musikr: remove old cpp module
This commit is contained in:
parent
97bac886d0
commit
78a06a0430
18 changed files with 0 additions and 1389 deletions
|
@ -1,71 +0,0 @@
|
||||||
# For more information about using CMake with Android Studio, read the
|
|
||||||
# documentation: https://d.android.com/studio/projects/add-native-code.html.
|
|
||||||
# For more examples on how to use CMake, see https://github.com/android/ndk-samples.
|
|
||||||
|
|
||||||
# Sets the minimum CMake version required for this project.
|
|
||||||
cmake_minimum_required(VERSION 3.22.1)
|
|
||||||
|
|
||||||
# Declares the project name. The project name can be accessed via ${ PROJECT_NAME},
|
|
||||||
# Since this is the top level CMakeLists.txt, the project name is also accessible
|
|
||||||
# with ${CMAKE_PROJECT_NAME} (both CMake variables are in-sync within the top level
|
|
||||||
# build script scope).
|
|
||||||
project("tagJNI") # becomes "libtagJNI.so"
|
|
||||||
|
|
||||||
# Creates and names a library, sets it as either STATIC
|
|
||||||
# or SHARED, and provides the relative paths to its source code.
|
|
||||||
# You can define multiple libraries, and CMake builds them for you.
|
|
||||||
# Gradle automatically packages shared libraries with your APK.
|
|
||||||
#
|
|
||||||
# In this top level CMakeLists.txt, ${CMAKE_PROJECT_NAME} is used to define
|
|
||||||
# the target library name; in the sub-module's CMakeLists.txt, ${PROJECT_NAME}
|
|
||||||
# is preferred for the same purpose.
|
|
||||||
#
|
|
||||||
# In order to load a library into your app from Java/Kotlin, you must call
|
|
||||||
# System.loadLibrary() and pass the name of the library defined here;
|
|
||||||
# for GameActivity/NativeActivity derived applications, the same library name must be
|
|
||||||
# used in the AndroidManifest.xml file.
|
|
||||||
set(taglib_location "${CMAKE_CURRENT_SOURCE_DIR}/taglib")
|
|
||||||
set(taglib_pkg "${taglib_location}/pkg/${ANDROID_ABI}")
|
|
||||||
set(taglib_lib "${taglib_pkg}/lib")
|
|
||||||
set(taglib_include "${taglib_pkg}/include")
|
|
||||||
|
|
||||||
set(taglib_file_name libtag.a)
|
|
||||||
set(taglib_file_path ${taglib_lib}/${taglib_file_name})
|
|
||||||
set(taglib_lib_name, "taglib")
|
|
||||||
add_library(
|
|
||||||
"taglib"
|
|
||||||
STATIC
|
|
||||||
IMPORTED)
|
|
||||||
set_target_properties(
|
|
||||||
"taglib" PROPERTIES
|
|
||||||
IMPORTED_LOCATION
|
|
||||||
${taglib_file_path}
|
|
||||||
INTERFACE_INCLUDE_DIRECTORIES
|
|
||||||
${taglib_include})
|
|
||||||
add_library(${CMAKE_PROJECT_NAME} SHARED
|
|
||||||
# List C/C++ source files with relative paths to this CMakeLists.txt.
|
|
||||||
taglib_jni.cpp
|
|
||||||
JInputStream.cpp
|
|
||||||
JTagMap.cpp
|
|
||||||
JMetadataBuilder.cpp
|
|
||||||
JClassRef.cpp
|
|
||||||
JObjectRef.cpp
|
|
||||||
JStringRef.cpp
|
|
||||||
JByteArrayRef.cpp
|
|
||||||
)
|
|
||||||
target_link_options(${CMAKE_PROJECT_NAME}
|
|
||||||
# @Tolriq found that these flags can reduce the size of the linked
|
|
||||||
# taglib + jni shim shared library. Kudos to them.
|
|
||||||
# https://github.com/taglib/taglib/issues/1212#issuecomment-2326456903
|
|
||||||
# Additionally, enable 16kb page size. I believe taglib can support this fine,
|
|
||||||
# as a cursory glance indicates that it doesn't hardcode any page sizes.
|
|
||||||
PRIVATE "-Wl,--exclude-libs,ALL,-z,max-page-size=16384")
|
|
||||||
|
|
||||||
# Specifies libraries CMake should link to your target library. You
|
|
||||||
# can link libraries from various origins, such as libraries defined in this
|
|
||||||
# build script, prebuilt third-party libraries, or Android system libraries.
|
|
||||||
target_link_libraries(${CMAKE_PROJECT_NAME}
|
|
||||||
# List libraries link to the target library
|
|
||||||
PRIVATE android
|
|
||||||
PRIVATE log
|
|
||||||
PRIVATE taglib)
|
|
|
@ -1,47 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (c) 2025 Auxio Project
|
|
||||||
* JByteArrayRef.cpp is part of Auxio.
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "JByteArrayRef.h"
|
|
||||||
|
|
||||||
JByteArrayRef::JByteArrayRef(JNIEnv *env, TagLib::ByteVector &data) : env(env) {
|
|
||||||
auto size = static_cast<jsize>(data.size());
|
|
||||||
array = env->NewByteArray(size);
|
|
||||||
env->SetByteArrayRegion(array, 0, static_cast<jsize>(size),
|
|
||||||
reinterpret_cast<const jbyte*>(data.data()));
|
|
||||||
}
|
|
||||||
|
|
||||||
JByteArrayRef::JByteArrayRef(JNIEnv *env, jbyteArray array) : env(env), array(
|
|
||||||
array) {
|
|
||||||
}
|
|
||||||
|
|
||||||
JByteArrayRef::~JByteArrayRef() {
|
|
||||||
env->DeleteLocalRef(array);
|
|
||||||
}
|
|
||||||
|
|
||||||
TagLib::ByteVector JByteArrayRef::copy() {
|
|
||||||
jsize length = env->GetArrayLength(array);
|
|
||||||
auto data = env->GetByteArrayElements(array, nullptr);
|
|
||||||
TagLib::ByteVector byteVector(reinterpret_cast<const char*>(data), length);
|
|
||||||
env->ReleaseByteArrayElements(array, data, JNI_ABORT);
|
|
||||||
return byteVector;
|
|
||||||
}
|
|
||||||
|
|
||||||
jbyteArray& JByteArrayRef::operator*() {
|
|
||||||
return array;
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,45 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (c) 2025 Auxio Project
|
|
||||||
* JByteArrayRef.h is part of Auxio.
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef AUXIO_JBYTEARRAYREF_H
|
|
||||||
#define AUXIO_JBYTEARRAYREF_H
|
|
||||||
|
|
||||||
#include <jni.h>
|
|
||||||
#include <taglib/tbytevector.h>
|
|
||||||
|
|
||||||
class JByteArrayRef {
|
|
||||||
public:
|
|
||||||
JByteArrayRef(JNIEnv *env, TagLib::ByteVector &data);
|
|
||||||
JByteArrayRef(JNIEnv *env, jbyteArray array);
|
|
||||||
|
|
||||||
~JByteArrayRef();
|
|
||||||
|
|
||||||
JByteArrayRef(const JByteArrayRef&) = delete;
|
|
||||||
|
|
||||||
JByteArrayRef& operator=(const JByteArrayRef&) = delete;
|
|
||||||
|
|
||||||
TagLib::ByteVector copy();
|
|
||||||
|
|
||||||
jbyteArray& operator*();
|
|
||||||
|
|
||||||
private:
|
|
||||||
JNIEnv *env;
|
|
||||||
jbyteArray array;
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif //AUXIO_JBYTEARRAYREF_H
|
|
|
@ -1,34 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (c) 2025 Auxio Project
|
|
||||||
* JClassRef.cpp is part of Auxio.
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "JClassRef.h"
|
|
||||||
JClassRef::JClassRef(JNIEnv *env, const char *classpath) : env(env) {
|
|
||||||
clazz = env->FindClass(classpath);
|
|
||||||
}
|
|
||||||
|
|
||||||
JClassRef::~JClassRef() {
|
|
||||||
env->DeleteLocalRef(clazz);
|
|
||||||
}
|
|
||||||
|
|
||||||
jmethodID JClassRef::method(const char *name, const char *signature) {
|
|
||||||
return env->GetMethodID(clazz, name, signature);
|
|
||||||
}
|
|
||||||
|
|
||||||
jclass& JClassRef::operator*() {
|
|
||||||
return clazz;
|
|
||||||
}
|
|
|
@ -1,45 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (c) 2025 Auxio Project
|
|
||||||
* JClassRef.h is part of Auxio.
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef AUXIO_JCLASSREF_H
|
|
||||||
#define AUXIO_JCLASSREF_H
|
|
||||||
|
|
||||||
#include <jni.h>
|
|
||||||
|
|
||||||
class JClassRef {
|
|
||||||
public:
|
|
||||||
JClassRef(JNIEnv *env, const char *classpath);
|
|
||||||
|
|
||||||
~JClassRef();
|
|
||||||
|
|
||||||
JClassRef(const JClassRef&) = delete;
|
|
||||||
|
|
||||||
JClassRef& operator=(const JClassRef&) = delete;
|
|
||||||
|
|
||||||
// Only exists to work around a broken lint that doesn't
|
|
||||||
// realize that this class is a smart pointer to jclass.
|
|
||||||
jmethodID method(const char *name, const char *signature);
|
|
||||||
|
|
||||||
jclass& operator*();
|
|
||||||
|
|
||||||
private:
|
|
||||||
JNIEnv *env;
|
|
||||||
jclass clazz;
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif //AUXIO_JCLASSREF_H
|
|
|
@ -1,145 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (c) 2024 Auxio Project
|
|
||||||
* JInputStream.cpp is part of Auxio.
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "JInputStream.h"
|
|
||||||
|
|
||||||
#include <cmath>
|
|
||||||
|
|
||||||
#include "JClassRef.h"
|
|
||||||
#include "JByteArrayRef.h"
|
|
||||||
#include "JStringRef.h"
|
|
||||||
|
|
||||||
JInputStream::JInputStream(JNIEnv *env, jobject jInputStream) : env(env), jInputStream(
|
|
||||||
jInputStream) {
|
|
||||||
JClassRef jInputStreamClass = { env,
|
|
||||||
"org/oxycblt/musikr/metadata/NativeInputStream" };
|
|
||||||
if (!env->IsInstanceOf(jInputStream, *jInputStreamClass)) {
|
|
||||||
throw std::runtime_error("Object is not NativeInputStream");
|
|
||||||
}
|
|
||||||
jInputStreamNameMethod = jInputStreamClass.method("name",
|
|
||||||
"()Ljava/lang/String;");
|
|
||||||
jInputStreamReadBlockMethod = jInputStreamClass.method("readBlock",
|
|
||||||
"(Ljava/nio/ByteBuffer;)Z");
|
|
||||||
jInputStreamIsOpenMethod = jInputStreamClass.method("isOpen", "()Z");
|
|
||||||
jInputStreamSeekFromBeginningMethod = jInputStreamClass.method(
|
|
||||||
"seekFromBeginning", "(J)Z");
|
|
||||||
jInputStreamSeekFromCurrentMethod = jInputStreamClass.method(
|
|
||||||
"seekFromCurrent", "(J)Z");
|
|
||||||
jInputStreamSeekFromEndMethod = jInputStreamClass.method("seekFromEnd",
|
|
||||||
"(J)Z");
|
|
||||||
jInputStreamTellMethod = jInputStreamClass.method("tell", "()J");
|
|
||||||
jInputStreamLengthMethod = jInputStreamClass.method("length", "()J");
|
|
||||||
}
|
|
||||||
|
|
||||||
JInputStream::~JInputStream() {
|
|
||||||
// The implicit assumption is that inputStream is managed by the owner,
|
|
||||||
// so we don't need to delete any references here
|
|
||||||
}
|
|
||||||
|
|
||||||
TagLib::FileName JInputStream::name() const {
|
|
||||||
// Not actually used except in FileRef, can safely ignore.
|
|
||||||
JStringRef jName { env, reinterpret_cast<jstring>(env->CallObjectMethod(
|
|
||||||
jInputStream, jInputStreamNameMethod)) };
|
|
||||||
return jName.copy().toCString();
|
|
||||||
}
|
|
||||||
|
|
||||||
TagLib::ByteVector JInputStream::readBlock(size_t length) {
|
|
||||||
// We have to invert the buffer allocation here siits not a perfect system (vykeen instead of korvax0 but i warped all over the hub and i dont think its possible to find a "perfect" purple system like you would withnce the JVM ByteBuffer allocation system
|
|
||||||
// uses a bugged caching mechanism that leaks memory if used in multithreaded contexts.
|
|
||||||
TagLib::ByteVector buf { static_cast<unsigned int>(length), 0 };
|
|
||||||
jobject wrappedByteBuffer = env->NewDirectByteBuffer(buf.data(),
|
|
||||||
buf.size());
|
|
||||||
if (wrappedByteBuffer == nullptr) {
|
|
||||||
throw std::runtime_error("Failed to wrap ByteBuffer");
|
|
||||||
}
|
|
||||||
JObjectRef byteBuffer = { env, wrappedByteBuffer };
|
|
||||||
jboolean result = env->CallBooleanMethod(jInputStream,
|
|
||||||
jInputStreamReadBlockMethod, *byteBuffer);
|
|
||||||
if (!result) {
|
|
||||||
throw std::runtime_error("Failed to read block, see logs");
|
|
||||||
}
|
|
||||||
return buf;
|
|
||||||
}
|
|
||||||
|
|
||||||
void JInputStream::writeBlock(const TagLib::ByteVector &data) {
|
|
||||||
throw std::runtime_error("Not implemented");
|
|
||||||
}
|
|
||||||
|
|
||||||
void JInputStream::insert(const TagLib::ByteVector &data,
|
|
||||||
TagLib::offset_t start, size_t replace) {
|
|
||||||
throw std::runtime_error("Not implemented");
|
|
||||||
}
|
|
||||||
|
|
||||||
void JInputStream::removeBlock(TagLib::offset_t start, size_t length) {
|
|
||||||
throw std::runtime_error("Not implemented");
|
|
||||||
}
|
|
||||||
|
|
||||||
bool JInputStream::readOnly() const {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool JInputStream::isOpen() const {
|
|
||||||
return env->CallBooleanMethod(jInputStream, jInputStreamIsOpenMethod);
|
|
||||||
}
|
|
||||||
|
|
||||||
void JInputStream::seek(TagLib::offset_t offset, Position p) {
|
|
||||||
auto joffset = static_cast<jlong>(std::llround(offset));
|
|
||||||
jboolean result;
|
|
||||||
switch (p) {
|
|
||||||
case Beginning:
|
|
||||||
result = env->CallBooleanMethod(jInputStream,
|
|
||||||
jInputStreamSeekFromBeginningMethod, joffset);
|
|
||||||
break;
|
|
||||||
case Current:
|
|
||||||
result = env->CallBooleanMethod(jInputStream,
|
|
||||||
jInputStreamSeekFromCurrentMethod, joffset);
|
|
||||||
break;
|
|
||||||
case End:
|
|
||||||
result = env->CallBooleanMethod(jInputStream,
|
|
||||||
jInputStreamSeekFromEndMethod, joffset);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (!result) {
|
|
||||||
throw std::runtime_error("Failed to seek, see logs");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void JInputStream::clear() {
|
|
||||||
// Nothing to do
|
|
||||||
}
|
|
||||||
|
|
||||||
TagLib::offset_t JInputStream::tell() const {
|
|
||||||
jlong jposition = env->CallLongMethod(jInputStream, jInputStreamTellMethod);
|
|
||||||
if (jposition == INT64_MIN) {
|
|
||||||
throw std::runtime_error("Failed to get position, see logs");
|
|
||||||
}
|
|
||||||
return static_cast<TagLib::offset_t>(jposition);
|
|
||||||
}
|
|
||||||
|
|
||||||
TagLib::offset_t JInputStream::length() {
|
|
||||||
jlong jlength = env->CallLongMethod(jInputStream, jInputStreamLengthMethod);
|
|
||||||
if (jlength == INT64_MIN) {
|
|
||||||
throw std::runtime_error("Failed to get length, see logs");
|
|
||||||
}
|
|
||||||
return static_cast<TagLib::offset_t>(jlength);
|
|
||||||
}
|
|
||||||
|
|
||||||
void JInputStream::truncate(TagLib::offset_t length) {
|
|
||||||
throw std::runtime_error("Not implemented");
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,128 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (c) 2024 Auxio Project
|
|
||||||
* JInputStream.h is part of Auxio.
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef AUXIO_JINPUTSTREAM_H
|
|
||||||
#define AUXIO_JINPUTSTREAM_H
|
|
||||||
|
|
||||||
#include <jni.h>
|
|
||||||
#include "JObjectRef.h"
|
|
||||||
|
|
||||||
#include "taglib/tiostream.h"
|
|
||||||
|
|
||||||
class JInputStream: public TagLib::IOStream {
|
|
||||||
public:
|
|
||||||
JInputStream(JNIEnv *env, jobject jInputStream);
|
|
||||||
|
|
||||||
~JInputStream();
|
|
||||||
|
|
||||||
JInputStream(const JInputStream&) = delete;
|
|
||||||
JInputStream& operator=(const JInputStream&) = delete;
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Returns the stream name in the local file system encoding.
|
|
||||||
*/
|
|
||||||
TagLib::FileName name() const override;
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Reads a block of size \a length at the current get pointer.
|
|
||||||
*/
|
|
||||||
TagLib::ByteVector readBlock(size_t length) override;
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Attempts to write the block \a data at the current get pointer. If the
|
|
||||||
* file is currently only opened read only -- i.e. readOnly() returns \c true --
|
|
||||||
* this attempts to reopen the file in read/write mode.
|
|
||||||
*
|
|
||||||
* \note This should be used instead of using the streaming output operator
|
|
||||||
* for a ByteVector. And even this function is significantly slower than
|
|
||||||
* doing output with a char[].
|
|
||||||
*/
|
|
||||||
void writeBlock(const TagLib::ByteVector &data) override;
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Insert \a data at position \a start in the file overwriting \a replace
|
|
||||||
* bytes of the original content.
|
|
||||||
*
|
|
||||||
* \note This method is slow since it requires rewriting all of the file
|
|
||||||
* after the insertion point.
|
|
||||||
*/
|
|
||||||
void insert(const TagLib::ByteVector &data, TagLib::offset_t start = 0,
|
|
||||||
size_t replace = 0) override;
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Removes a block of the file starting a \a start and continuing for
|
|
||||||
* \a length bytes.
|
|
||||||
*
|
|
||||||
* \note This method is slow since it involves rewriting all of the file
|
|
||||||
* after the removed portion.
|
|
||||||
*/
|
|
||||||
void removeBlock(TagLib::offset_t start = 0, size_t length = 0) override;
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Returns \c true if the file is read only (or if the file can not be opened).
|
|
||||||
*/
|
|
||||||
bool readOnly() const override;
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Since the file can currently only be opened as an argument to the
|
|
||||||
* constructor (sort-of by design), this returns if that open succeeded.
|
|
||||||
*/
|
|
||||||
bool isOpen() const override;
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Move the I/O pointer to \a offset in the stream from position \a p. This
|
|
||||||
* defaults to seeking from the beginning of the stream.
|
|
||||||
*
|
|
||||||
* \see Position
|
|
||||||
*/
|
|
||||||
void seek(TagLib::offset_t offset, Position p = Beginning) override;
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Reset the end-of-stream and error flags on the stream.
|
|
||||||
*/
|
|
||||||
void clear() override;
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Returns the current offset within the stream.
|
|
||||||
*/
|
|
||||||
TagLib::offset_t tell() const override;
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Returns the length of the stream.
|
|
||||||
*/
|
|
||||||
TagLib::offset_t length() override;
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Truncates the stream to a \a length.
|
|
||||||
*/
|
|
||||||
void truncate(TagLib::offset_t length) override;
|
|
||||||
|
|
||||||
private:
|
|
||||||
JNIEnv *env;
|
|
||||||
jobject jInputStream;
|
|
||||||
jmethodID jInputStreamNameMethod;
|
|
||||||
jmethodID jInputStreamReadBlockMethod;
|
|
||||||
jmethodID jInputStreamIsOpenMethod;
|
|
||||||
jmethodID jInputStreamSeekFromBeginningMethod;
|
|
||||||
jmethodID jInputStreamSeekFromCurrentMethod;
|
|
||||||
jmethodID jInputStreamSeekFromEndMethod;
|
|
||||||
jmethodID jInputStreamTellMethod;
|
|
||||||
jmethodID jInputStreamLengthMethod;
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif //AUXIO_JINPUTSTREAM_H
|
|
|
@ -1,219 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (c) 2024 Auxio Project
|
|
||||||
* JMetadataBuilder.cpp is part of Auxio.
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "JMetadataBuilder.h"
|
|
||||||
|
|
||||||
#include "util.h"
|
|
||||||
|
|
||||||
#include <taglib/mp4tag.h>
|
|
||||||
#include <taglib/textidentificationframe.h>
|
|
||||||
#include <taglib/attachedpictureframe.h>
|
|
||||||
|
|
||||||
#include <taglib/tpropertymap.h>
|
|
||||||
|
|
||||||
#include "JObjectRef.h"
|
|
||||||
#include "JClassRef.h"
|
|
||||||
#include "JStringRef.h"
|
|
||||||
#include "JByteArrayRef.h"
|
|
||||||
|
|
||||||
JMetadataBuilder::JMetadataBuilder(JNIEnv *env) : env(env), id3v2(env), xiph(
|
|
||||||
env), mp4(env), cover(), properties(nullptr) {
|
|
||||||
}
|
|
||||||
|
|
||||||
void JMetadataBuilder::setMimeType(TagLib::String type) {
|
|
||||||
mimeType = type;
|
|
||||||
}
|
|
||||||
|
|
||||||
void JMetadataBuilder::setId3v1(TagLib::ID3v1::Tag &tag) {
|
|
||||||
id3v2.add_id("TIT2", tag.title());
|
|
||||||
id3v2.add_id("TPE1", tag.artist());
|
|
||||||
id3v2.add_id("TALB", tag.album());
|
|
||||||
id3v2.add_id("TRCK", std::to_string(tag.track()));
|
|
||||||
id3v2.add_id("TYER", std::to_string(tag.year()));
|
|
||||||
const int genreNumber = tag.genreNumber();
|
|
||||||
if (genreNumber != 255) {
|
|
||||||
id3v2.add_id("TCON", std::to_string(genreNumber));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void JMetadataBuilder::setId3v2(TagLib::ID3v2::Tag &tag) {
|
|
||||||
// We want to ideally find the front cover, fall back to the first picture otherwise.
|
|
||||||
std::optional<TagLib::ID3v2::AttachedPictureFrame*> firstPic;
|
|
||||||
std::optional<TagLib::ID3v2::AttachedPictureFrame*> frontCoverPic;
|
|
||||||
for (auto frame : tag.frameList()) {
|
|
||||||
if (auto txxxFrame =
|
|
||||||
dynamic_cast<TagLib::ID3v2::UserTextIdentificationFrame*>(frame)) {
|
|
||||||
TagLib::String id = frame->frameID();
|
|
||||||
TagLib::StringList frameText = txxxFrame->fieldList();
|
|
||||||
if (frameText.isEmpty())
|
|
||||||
continue;
|
|
||||||
auto begin = frameText.begin();
|
|
||||||
TagLib::String description = *begin;
|
|
||||||
frameText.erase(begin);
|
|
||||||
id3v2.add_combined(id, description, frameText);
|
|
||||||
} else if (auto textFrame =
|
|
||||||
dynamic_cast<TagLib::ID3v2::TextIdentificationFrame*>(frame)) {
|
|
||||||
TagLib::String key = frame->frameID();
|
|
||||||
TagLib::StringList frameText = textFrame->fieldList();
|
|
||||||
id3v2.add_id(key, frameText);
|
|
||||||
} else if (auto pictureFrame =
|
|
||||||
dynamic_cast<TagLib::ID3v2::AttachedPictureFrame*>(frame)) {
|
|
||||||
if (!firstPic) {
|
|
||||||
firstPic = pictureFrame;
|
|
||||||
}
|
|
||||||
if (!frontCoverPic
|
|
||||||
&& pictureFrame->type()
|
|
||||||
== TagLib::ID3v2::AttachedPictureFrame::FrontCover) {
|
|
||||||
frontCoverPic = pictureFrame;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (frontCoverPic) {
|
|
||||||
auto pic = *frontCoverPic;
|
|
||||||
cover = pic->picture();
|
|
||||||
} else if (firstPic) {
|
|
||||||
auto pic = *firstPic;
|
|
||||||
cover = pic->picture();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void JMetadataBuilder::setXiph(TagLib::Ogg::XiphComment &tag) {
|
|
||||||
for (auto field : tag.fieldListMap()) {
|
|
||||||
auto key = field.first.upper();
|
|
||||||
auto values = field.second;
|
|
||||||
xiph.add_custom(key, values);
|
|
||||||
}
|
|
||||||
auto pics = tag.pictureList();
|
|
||||||
setFlacPictures(pics);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename T>
|
|
||||||
void mp4AddImpl(JTagMap &map, TagLib::String &itemName, T itemValue) {
|
|
||||||
if (itemName.startsWith("----")) {
|
|
||||||
// Split this into it's atom name and description
|
|
||||||
auto split = itemName.find(':');
|
|
||||||
auto atomName = itemName.substr(0, split);
|
|
||||||
auto atomDescription = itemName.substr(split + 1);
|
|
||||||
map.add_combined(atomName, atomDescription, itemValue);
|
|
||||||
} else {
|
|
||||||
map.add_id(itemName, itemValue);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void JMetadataBuilder::setMp4(TagLib::MP4::Tag &tag) {
|
|
||||||
auto map = tag.itemMap();
|
|
||||||
std::optional < TagLib::MP4::CoverArt > firstCover;
|
|
||||||
for (auto item : map) {
|
|
||||||
auto itemName = item.first;
|
|
||||||
auto itemValue = item.second;
|
|
||||||
if (itemName == "covr") {
|
|
||||||
// Special cover case.
|
|
||||||
// MP4 has no types, so just prioritize easier to decode covers (PNG, JPEG)
|
|
||||||
auto pics = itemValue.toCoverArtList();
|
|
||||||
for (auto &pic : pics) {
|
|
||||||
auto format = pic.format();
|
|
||||||
if (format == TagLib::MP4::CoverArt::PNG
|
|
||||||
|| format == TagLib::MP4::CoverArt::JPEG) {
|
|
||||||
cover = pic.data();
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!pics.isEmpty()) {
|
|
||||||
cover = pics.front().data();
|
|
||||||
}
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
auto type = itemValue.type();
|
|
||||||
std::string serializedValue;
|
|
||||||
switch (type) {
|
|
||||||
// Normal expected MP4 items
|
|
||||||
case TagLib::MP4::Item::Type::StringList:
|
|
||||||
mp4AddImpl(mp4, itemName, itemValue.toStringList());
|
|
||||||
break;
|
|
||||||
// Weird MP4 items I'm 90% sure I'll encounter.
|
|
||||||
case TagLib::MP4::Item::Type::Int:
|
|
||||||
serializedValue = std::to_string(itemValue.toInt());
|
|
||||||
break;
|
|
||||||
case TagLib::MP4::Item::Type::UInt:
|
|
||||||
serializedValue = std::to_string(itemValue.toUInt());
|
|
||||||
break;
|
|
||||||
case TagLib::MP4::Item::Type::LongLong:
|
|
||||||
serializedValue = std::to_string(itemValue.toLongLong());
|
|
||||||
break;
|
|
||||||
case TagLib::MP4::Item::Type::IntPair:
|
|
||||||
// It's inefficient going from the integer representation back into
|
|
||||||
// a string, but I fully expect taggers to just write "NN/TT" strings
|
|
||||||
// anyway, and musikr doesn't have to do as much fiddly variant handling.
|
|
||||||
serializedValue = std::to_string(itemValue.toIntPair().first) + "/"
|
|
||||||
+ std::to_string(itemValue.toIntPair().second);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
// Don't care about the other types
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
mp4AddImpl(mp4, itemName, TagLib::String(serializedValue));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void JMetadataBuilder::setFlacPictures(
|
|
||||||
TagLib::List<TagLib::FLAC::Picture*> &pics) {
|
|
||||||
// Find the front cover image. If it doesn't exist, fall back to the first image.
|
|
||||||
for (auto pic : pics) {
|
|
||||||
if (pic->type() == TagLib::FLAC::Picture::FrontCover) {
|
|
||||||
cover = pic->data();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!pics.isEmpty()) {
|
|
||||||
cover = pics.front()->data();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void JMetadataBuilder::setProperties(TagLib::AudioProperties *properties) {
|
|
||||||
this->properties = properties;
|
|
||||||
}
|
|
||||||
|
|
||||||
jobject JMetadataBuilder::build() {
|
|
||||||
JClassRef jPropertiesClass { env, "org/oxycblt/musikr/metadata/Properties" };
|
|
||||||
jmethodID jPropertiesInitMethod = jPropertiesClass.method("<init>",
|
|
||||||
"(Ljava/lang/String;JII)V");
|
|
||||||
JStringRef jMimeType { env, this->mimeType };
|
|
||||||
|
|
||||||
JObjectRef jProperties { env, env->NewObject(*jPropertiesClass,
|
|
||||||
jPropertiesInitMethod, *jMimeType,
|
|
||||||
(jlong) properties->lengthInMilliseconds(), properties->bitrate(),
|
|
||||||
properties->sampleRate()) };
|
|
||||||
|
|
||||||
JClassRef jMetadataClass { env, "org/oxycblt/musikr/metadata/Metadata" };
|
|
||||||
jmethodID jMetadataInitMethod = jMetadataClass.method("<init>",
|
|
||||||
"(Ljava/util/Map;Ljava/util/Map;Ljava/util/Map;[BLorg/"
|
|
||||||
"oxycblt/musikr/metadata/Properties;)V");
|
|
||||||
auto jId3v2Map = id3v2.getObject();
|
|
||||||
auto jXiphMap = xiph.getObject();
|
|
||||||
auto jMp4Map = mp4.getObject();
|
|
||||||
if (cover.has_value()) {
|
|
||||||
JByteArrayRef jCoverArray { env, cover.value() };
|
|
||||||
jobject result = env->NewObject(*jMetadataClass, jMetadataInitMethod,
|
|
||||||
**jId3v2Map, **jXiphMap, **jMp4Map, *jCoverArray, *jProperties);
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
return env->NewObject(*jMetadataClass, jMetadataInitMethod, **jId3v2Map,
|
|
||||||
**jXiphMap, **jMp4Map, nullptr, *jProperties);
|
|
||||||
}
|
|
|
@ -1,61 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (c) 2024 Auxio Project
|
|
||||||
* JMetadataBuilder.h is part of Auxio.
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef AUXIO_JMETADATABUILDER_H
|
|
||||||
#define AUXIO_JMETADATABUILDER_H
|
|
||||||
|
|
||||||
#include <jni.h>
|
|
||||||
#include <string_view>
|
|
||||||
#include <optional>
|
|
||||||
|
|
||||||
#include "taglib/id3v1tag.h"
|
|
||||||
#include "taglib/id3v2tag.h"
|
|
||||||
#include "taglib/xiphcomment.h"
|
|
||||||
#include "taglib/mp4tag.h"
|
|
||||||
#include "taglib/audioproperties.h"
|
|
||||||
|
|
||||||
#include "JTagMap.h"
|
|
||||||
|
|
||||||
class JMetadataBuilder {
|
|
||||||
public:
|
|
||||||
JMetadataBuilder(JNIEnv *env);
|
|
||||||
|
|
||||||
void setMimeType(TagLib::String type);
|
|
||||||
void setId3v1(TagLib::ID3v1::Tag &tag);
|
|
||||||
void setId3v2(TagLib::ID3v2::Tag &tag);
|
|
||||||
void setXiph(TagLib::Ogg::XiphComment &tag);
|
|
||||||
void setMp4(TagLib::MP4::Tag &tag);
|
|
||||||
void setFlacPictures(TagLib::List<TagLib::FLAC::Picture*> &pics);
|
|
||||||
void setProperties(TagLib::AudioProperties *properties);
|
|
||||||
|
|
||||||
jobject build();
|
|
||||||
|
|
||||||
private:
|
|
||||||
JNIEnv *env;
|
|
||||||
|
|
||||||
TagLib::String mimeType;
|
|
||||||
|
|
||||||
std::optional<TagLib::ByteVector> cover;
|
|
||||||
TagLib::AudioProperties *properties;
|
|
||||||
|
|
||||||
JTagMap id3v2;
|
|
||||||
JTagMap xiph;
|
|
||||||
JTagMap mp4;
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif //AUXIO_JMETADATABUILDER_H
|
|
|
@ -1,30 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (c) 2025 Auxio Project
|
|
||||||
* JObjectRef.cpp is part of Auxio.
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "JObjectRef.h"
|
|
||||||
|
|
||||||
JObjectRef::JObjectRef(JNIEnv *env, jobject object) : env(env), object(object) {
|
|
||||||
}
|
|
||||||
|
|
||||||
JObjectRef::~JObjectRef() {
|
|
||||||
env->DeleteLocalRef(object);
|
|
||||||
}
|
|
||||||
|
|
||||||
jobject& JObjectRef::operator*() {
|
|
||||||
return object;
|
|
||||||
}
|
|
|
@ -1,44 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (c) 2025 Auxio Project
|
|
||||||
* JObjectRef.h is part of Auxio.
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef AUXIO_JOBJECTREF_H
|
|
||||||
#define AUXIO_JOBJECTREF_H
|
|
||||||
|
|
||||||
#include <jni.h>
|
|
||||||
#include <memory>
|
|
||||||
#include <taglib/tstring.h>
|
|
||||||
#include "JObjectRef.h"
|
|
||||||
|
|
||||||
class JObjectRef {
|
|
||||||
public:
|
|
||||||
JObjectRef(JNIEnv *env, jobject object);
|
|
||||||
|
|
||||||
~JObjectRef();
|
|
||||||
|
|
||||||
JObjectRef(const JObjectRef&) = delete;
|
|
||||||
|
|
||||||
JObjectRef& operator=(const JObjectRef&) = delete;
|
|
||||||
|
|
||||||
jobject& operator*();
|
|
||||||
|
|
||||||
private:
|
|
||||||
JNIEnv *env;
|
|
||||||
jobject object;
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif //AUXIO_JOBJECTREF_H
|
|
|
@ -1,43 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (c) 2025 Auxio Project
|
|
||||||
* JStringRef.cpp is part of Auxio.
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "JStringRef.h"
|
|
||||||
#include "util.h"
|
|
||||||
|
|
||||||
JStringRef::JStringRef(JNIEnv *env, jstring jString) : env(env), string(jString) {
|
|
||||||
}
|
|
||||||
|
|
||||||
JStringRef::JStringRef(JNIEnv *env, const TagLib::String string) {
|
|
||||||
this->env = env;
|
|
||||||
this->string = env->NewStringUTF(string.toCString(true));
|
|
||||||
}
|
|
||||||
|
|
||||||
JStringRef::~JStringRef() {
|
|
||||||
env->DeleteLocalRef(string);
|
|
||||||
}
|
|
||||||
|
|
||||||
TagLib::String JStringRef::copy() {
|
|
||||||
auto chars = env->GetStringUTFChars(string, nullptr);
|
|
||||||
TagLib::String result = chars;
|
|
||||||
env->ReleaseStringUTFChars(string, chars);
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
jstring& JStringRef::operator*() {
|
|
||||||
return string;
|
|
||||||
}
|
|
|
@ -1,46 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (c) 2025 Auxio Project
|
|
||||||
* JStringRef.h is part of Auxio.
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef AUXIO_JSTRINGREF_H
|
|
||||||
#define AUXIO_JSTRINGREF_H
|
|
||||||
|
|
||||||
#include <jni.h>
|
|
||||||
#include <taglib/tstring.h>
|
|
||||||
|
|
||||||
class JStringRef {
|
|
||||||
public:
|
|
||||||
JStringRef(JNIEnv *env, jstring jString);
|
|
||||||
|
|
||||||
JStringRef(JNIEnv *env, TagLib::String string);
|
|
||||||
|
|
||||||
~JStringRef();
|
|
||||||
|
|
||||||
JStringRef(const JStringRef&) = delete;
|
|
||||||
|
|
||||||
JStringRef& operator=(const JStringRef&) = delete;
|
|
||||||
|
|
||||||
TagLib::String copy();
|
|
||||||
|
|
||||||
jstring& operator*();
|
|
||||||
|
|
||||||
private:
|
|
||||||
JNIEnv *env;
|
|
||||||
jstring string;
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif //AUXIO_JSTRINGREF_H
|
|
|
@ -1,118 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (c) 2024 Auxio Project
|
|
||||||
* JTagMap.cpp is part of Auxio.
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "JTagMap.h"
|
|
||||||
|
|
||||||
#include "JStringRef.h"
|
|
||||||
|
|
||||||
JTagMap::JTagMap(JNIEnv *env) : env(env) {
|
|
||||||
auto jTagMapClass = std::make_unique < JClassRef
|
|
||||||
> (env, "org/oxycblt/musikr/metadata/NativeTagMap");
|
|
||||||
auto jTagMapInitMethod = jTagMapClass->method("<init>", "()V");
|
|
||||||
jTagMap = std::move(
|
|
||||||
std::make_unique < JObjectRef
|
|
||||||
> (env, env->NewObject(**jTagMapClass, jTagMapInitMethod)));
|
|
||||||
jTagMapAddIdSingleMethod = jTagMapClass->method("addID",
|
|
||||||
"(Ljava/lang/String;Ljava/lang/String;)V");
|
|
||||||
jTagMapAddIdListMethod = jTagMapClass->method("addID",
|
|
||||||
"(Ljava/lang/String;Ljava/util/List;)V");
|
|
||||||
jTagMapAddCustomSingleMethod = jTagMapClass->method("addCustom",
|
|
||||||
"(Ljava/lang/String;Ljava/lang/String;)V");
|
|
||||||
jTagMapAddCustomListMethod = jTagMapClass->method("addCustom",
|
|
||||||
"(Ljava/lang/String;Ljava/util/List;)V");
|
|
||||||
jTagMapAddCombinedSingleMethod = jTagMapClass->method("addCombined",
|
|
||||||
"(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V");
|
|
||||||
jTagMapAddCombinedListMethod = jTagMapClass->method("addCombined",
|
|
||||||
"(Ljava/lang/String;Ljava/lang/String;Ljava/util/List;)V");
|
|
||||||
jTagMapGetObjectMethod = jTagMapClass->method("getObject",
|
|
||||||
"()Ljava/util/Map;");
|
|
||||||
|
|
||||||
jArrayListClass = std::make_unique < JClassRef
|
|
||||||
> (env, "java/util/ArrayList");
|
|
||||||
jArrayListInitMethod = jArrayListClass->method("<init>", "()V");
|
|
||||||
jArrayListAddMethod = jArrayListClass->method("add",
|
|
||||||
"(Ljava/lang/Object;)Z");
|
|
||||||
}
|
|
||||||
|
|
||||||
void JTagMap::add_id(const TagLib::String id, const TagLib::String value) {
|
|
||||||
JStringRef jId { env, id };
|
|
||||||
JStringRef jValue { env, value };
|
|
||||||
env->CallVoidMethod(**jTagMap, jTagMapAddIdSingleMethod, *jId, *jValue);
|
|
||||||
}
|
|
||||||
|
|
||||||
void JTagMap::add_id(const TagLib::String id, const TagLib::StringList values) {
|
|
||||||
JStringRef jId { env, id };
|
|
||||||
JObjectRef jValues { env, env->NewObject(**jArrayListClass,
|
|
||||||
jArrayListInitMethod) };
|
|
||||||
for (auto &value : values) {
|
|
||||||
JStringRef jValue { env, value };
|
|
||||||
env->CallBooleanMethod(*jValues, jArrayListAddMethod, *jValue);
|
|
||||||
}
|
|
||||||
env->CallVoidMethod(**jTagMap, jTagMapAddIdListMethod, *jId, *jValues);
|
|
||||||
}
|
|
||||||
|
|
||||||
void JTagMap::add_custom(const TagLib::String description,
|
|
||||||
const TagLib::String value) {
|
|
||||||
JStringRef jDescription { env, description };
|
|
||||||
JStringRef jValue { env, value };
|
|
||||||
env->CallVoidMethod(**jTagMap, jTagMapAddCustomSingleMethod, *jDescription,
|
|
||||||
*jValue);
|
|
||||||
}
|
|
||||||
|
|
||||||
void JTagMap::add_custom(const TagLib::String description,
|
|
||||||
const TagLib::StringList values) {
|
|
||||||
JStringRef jDescription { env, description };
|
|
||||||
JObjectRef jValues { env, env->NewObject(**jArrayListClass,
|
|
||||||
jArrayListInitMethod) };
|
|
||||||
for (auto &value : values) {
|
|
||||||
JStringRef jValue { env, value };
|
|
||||||
env->CallBooleanMethod(*jValues, jArrayListAddMethod, *jValue);
|
|
||||||
}
|
|
||||||
env->CallVoidMethod(**jTagMap, jTagMapAddCustomListMethod, *jDescription,
|
|
||||||
*jValues);
|
|
||||||
}
|
|
||||||
|
|
||||||
void JTagMap::add_combined(const TagLib::String id,
|
|
||||||
const TagLib::String description, const TagLib::String value) {
|
|
||||||
JStringRef jId { env, id };
|
|
||||||
JStringRef jDescription { env, description };
|
|
||||||
JStringRef jValue { env, value };
|
|
||||||
env->CallVoidMethod(**jTagMap, jTagMapAddCombinedSingleMethod, *jId,
|
|
||||||
*jDescription, *jValue);
|
|
||||||
}
|
|
||||||
|
|
||||||
void JTagMap::add_combined(const TagLib::String id,
|
|
||||||
const TagLib::String description, const TagLib::StringList values) {
|
|
||||||
JStringRef jId { env, id };
|
|
||||||
JStringRef jDescription { env, description };
|
|
||||||
JObjectRef jValues { env, env->NewObject(**jArrayListClass,
|
|
||||||
jArrayListInitMethod) };
|
|
||||||
for (auto &value : values) {
|
|
||||||
JStringRef jValue { env, value };
|
|
||||||
env->CallBooleanMethod(*jValues, jArrayListAddMethod, *jValue);
|
|
||||||
}
|
|
||||||
env->CallVoidMethod(**jTagMap, jTagMapAddCombinedListMethod, *jId,
|
|
||||||
*jDescription, *jValues);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::unique_ptr<JObjectRef> JTagMap::getObject() {
|
|
||||||
return std::move(
|
|
||||||
std::make_unique < JObjectRef
|
|
||||||
> (env, env->CallObjectMethod(**jTagMap,
|
|
||||||
jTagMapGetObjectMethod)));
|
|
||||||
}
|
|
|
@ -1,67 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (c) 2024 Auxio Project
|
|
||||||
* JTagMap.h is part of Auxio.
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef AUXIO_JTAGMAP_H
|
|
||||||
#define AUXIO_JTAGMAP_H
|
|
||||||
|
|
||||||
#include <jni.h>
|
|
||||||
#include <string_view>
|
|
||||||
#include <taglib/tstring.h>
|
|
||||||
#include <taglib/tstringlist.h>
|
|
||||||
|
|
||||||
#include "JObjectRef.h"
|
|
||||||
#include "JClassRef.h"
|
|
||||||
|
|
||||||
class JTagMap {
|
|
||||||
public:
|
|
||||||
JTagMap(JNIEnv *env);
|
|
||||||
|
|
||||||
JTagMap(const JTagMap&) = delete;
|
|
||||||
JTagMap& operator=(const JTagMap&) = delete;
|
|
||||||
|
|
||||||
void add_id(TagLib::String id, TagLib::String value);
|
|
||||||
void add_id(TagLib::String id, TagLib::StringList values);
|
|
||||||
|
|
||||||
void add_custom(TagLib::String description, TagLib::String value);
|
|
||||||
void add_custom(TagLib::String description, TagLib::StringList values);
|
|
||||||
|
|
||||||
void add_combined(TagLib::String id, TagLib::String description,
|
|
||||||
TagLib::String value);
|
|
||||||
void add_combined(TagLib::String id, TagLib::String description,
|
|
||||||
TagLib::StringList values);
|
|
||||||
|
|
||||||
std::unique_ptr<JObjectRef> getObject();
|
|
||||||
|
|
||||||
private:
|
|
||||||
JNIEnv *env;
|
|
||||||
|
|
||||||
std::unique_ptr<JObjectRef> jTagMap;
|
|
||||||
jmethodID jTagMapAddIdSingleMethod;
|
|
||||||
jmethodID jTagMapAddIdListMethod;
|
|
||||||
jmethodID jTagMapAddCustomSingleMethod;
|
|
||||||
jmethodID jTagMapAddCustomListMethod;
|
|
||||||
jmethodID jTagMapAddCombinedSingleMethod;
|
|
||||||
jmethodID jTagMapAddCombinedListMethod;
|
|
||||||
jmethodID jTagMapGetObjectMethod;
|
|
||||||
|
|
||||||
std::unique_ptr<JClassRef> jArrayListClass;
|
|
||||||
jmethodID jArrayListInitMethod;
|
|
||||||
jmethodID jArrayListAddMethod;
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif //AUXIO_JTAGMAP_H
|
|
|
@ -1,15 +0,0 @@
|
||||||
# Define the minimum CMake version and project name
|
|
||||||
cmake_minimum_required(VERSION 3.22.1)
|
|
||||||
|
|
||||||
# Set the Android NDK path
|
|
||||||
option(ANDROID_NDK_PATH "Path to Android NDK Install. Should be same version specified in gradle." REQUIRED)
|
|
||||||
|
|
||||||
# Specify the target Android API level
|
|
||||||
set(ANDROID_PLATFORM android-24)
|
|
||||||
|
|
||||||
# Define the toolchain
|
|
||||||
set(CMAKE_SYSTEM_NAME Android)
|
|
||||||
set(CMAKE_ANDROID_ARCH_ABI ${ANDROID_ABI})
|
|
||||||
set(CMAKE_ANDROID_NDK ${ANDROID_NDK_PATH})
|
|
||||||
set(CMAKE_ANDROID_STL_TYPE c++_static)
|
|
||||||
set(CMAKE_ANDROID_NDK_TOOLCHAIN_VERSION clang)
|
|
|
@ -1,200 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (c) 2024 Auxio Project
|
|
||||||
* taglib_jni.cpp is part of Auxio.
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <jni.h>
|
|
||||||
#include <string>
|
|
||||||
#include "JInputStream.h"
|
|
||||||
#include "JMetadataBuilder.h"
|
|
||||||
#include "util.h"
|
|
||||||
|
|
||||||
#include "taglib/fileref.h"
|
|
||||||
#include "taglib/flacfile.h"
|
|
||||||
#include "taglib/mp4file.h"
|
|
||||||
#include "taglib/mpegfile.h"
|
|
||||||
#include "taglib/opusfile.h"
|
|
||||||
#include "taglib/vorbisfile.h"
|
|
||||||
#include "taglib/wavfile.h"
|
|
||||||
|
|
||||||
bool parseMpeg(const char *name, TagLib::File *file,
|
|
||||||
JMetadataBuilder &jBuilder) {
|
|
||||||
auto *mpegFile = dynamic_cast<TagLib::MPEG::File*>(file);
|
|
||||||
if (mpegFile == nullptr) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
auto id3v1Tag = mpegFile->ID3v1Tag();
|
|
||||||
if (id3v1Tag != nullptr) {
|
|
||||||
try {
|
|
||||||
jBuilder.setId3v1(*id3v1Tag);
|
|
||||||
} catch (std::exception &e) {
|
|
||||||
LOGE("Unable to parse ID3v1 tag in %s: %s", name, e.what());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
auto id3v2Tag = mpegFile->ID3v2Tag();
|
|
||||||
if (id3v2Tag != nullptr) {
|
|
||||||
try {
|
|
||||||
jBuilder.setId3v2(*id3v2Tag);
|
|
||||||
} catch (std::exception &e) {
|
|
||||||
LOGE("Unable to parse ID3v2 tag in %s: %s", name, e.what());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool parseMp4(const char *name, TagLib::File *file,
|
|
||||||
JMetadataBuilder &jBuilder) {
|
|
||||||
auto *mp4File = dynamic_cast<TagLib::MP4::File*>(file);
|
|
||||||
if (mp4File == nullptr) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
auto tag = mp4File->tag();
|
|
||||||
if (tag != nullptr) {
|
|
||||||
try {
|
|
||||||
jBuilder.setMp4(*tag);
|
|
||||||
} catch (std::exception &e) {
|
|
||||||
LOGE("Unable to parse MP4 tag in %s: %s", name, e.what());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool parseFlac(const char *name, TagLib::File *file,
|
|
||||||
JMetadataBuilder &jBuilder) {
|
|
||||||
auto *flacFile = dynamic_cast<TagLib::FLAC::File*>(file);
|
|
||||||
if (flacFile == nullptr) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
auto id3v1Tag = flacFile->ID3v1Tag();
|
|
||||||
if (id3v1Tag != nullptr) {
|
|
||||||
try {
|
|
||||||
jBuilder.setId3v1(*id3v1Tag);
|
|
||||||
} catch (std::exception &e) {
|
|
||||||
LOGE("Unable to parse ID3v1 tag in %s: %s", name, e.what());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
auto id3v2Tag = flacFile->ID3v2Tag();
|
|
||||||
if (id3v2Tag != nullptr) {
|
|
||||||
try {
|
|
||||||
jBuilder.setId3v2(*id3v2Tag);
|
|
||||||
} catch (std::exception &e) {
|
|
||||||
LOGE("Unable to parse ID3v2 tag in %s: %s", name, e.what());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
auto xiphComment = flacFile->xiphComment();
|
|
||||||
if (xiphComment != nullptr) {
|
|
||||||
try {
|
|
||||||
jBuilder.setXiph(*xiphComment);
|
|
||||||
} catch (std::exception &e) {
|
|
||||||
LOGE("Unable to parse Xiph comment in %s: %s", name, e.what());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
auto pics = flacFile->pictureList();
|
|
||||||
jBuilder.setFlacPictures(pics);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool parseOpus(const char *name, TagLib::File *file,
|
|
||||||
JMetadataBuilder &jBuilder) {
|
|
||||||
auto *opusFile = dynamic_cast<TagLib::Ogg::Opus::File*>(file);
|
|
||||||
if (opusFile == nullptr) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
auto tag = opusFile->tag();
|
|
||||||
if (tag != nullptr) {
|
|
||||||
try {
|
|
||||||
jBuilder.setXiph(*tag);
|
|
||||||
} catch (std::exception &e) {
|
|
||||||
LOGE("Unable to parse Xiph comment in %s: %s", name, e.what());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool parseVorbis(const char *name, TagLib::File *file,
|
|
||||||
JMetadataBuilder &jBuilder) {
|
|
||||||
auto *vorbisFile = dynamic_cast<TagLib::Ogg::Vorbis::File*>(file);
|
|
||||||
if (vorbisFile == nullptr) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
auto tag = vorbisFile->tag();
|
|
||||||
if (tag != nullptr) {
|
|
||||||
try {
|
|
||||||
jBuilder.setXiph(*tag);
|
|
||||||
} catch (std::exception &e) {
|
|
||||||
LOGE("Unable to parse Xiph comment %s: %s", name, e.what());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool parseWav(const char *name, TagLib::File *file,
|
|
||||||
JMetadataBuilder &jBuilder) {
|
|
||||||
auto *wavFile = dynamic_cast<TagLib::RIFF::WAV::File*>(file);
|
|
||||||
if (wavFile == nullptr) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
auto tag = wavFile->ID3v2Tag();
|
|
||||||
if (tag != nullptr) {
|
|
||||||
try {
|
|
||||||
jBuilder.setId3v2(*tag);
|
|
||||||
} catch (std::exception &e) {
|
|
||||||
LOGE("Unable to parse ID3v2 tag in %s: %s", name, e.what());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
extern "C" JNIEXPORT jobject JNICALL
|
|
||||||
Java_org_oxycblt_musikr_metadata_TagLibJNI_openNative(JNIEnv *env,
|
|
||||||
jobject /* this */,
|
|
||||||
jobject inputStream) {
|
|
||||||
const char *name = nullptr;
|
|
||||||
try {
|
|
||||||
JInputStream jStream {env, inputStream};
|
|
||||||
name = jStream.name();
|
|
||||||
TagLib::FileRef fileRef {&jStream};
|
|
||||||
if (fileRef.isNull()) {
|
|
||||||
throw std::runtime_error("Invalid file");
|
|
||||||
}
|
|
||||||
TagLib::File *file = fileRef.file();
|
|
||||||
JMetadataBuilder jBuilder {env};
|
|
||||||
jBuilder.setProperties(file->audioProperties());
|
|
||||||
|
|
||||||
// TODO: Make some type of composable logger so I don't
|
|
||||||
// have to shoehorn this into the native code.
|
|
||||||
if (parseMpeg(name, file, jBuilder)) {
|
|
||||||
jBuilder.setMimeType("audio/mpeg");
|
|
||||||
} else if (parseMp4(name, file, jBuilder)) {
|
|
||||||
jBuilder.setMimeType("audio/mp4");
|
|
||||||
} else if (parseFlac(name, file, jBuilder)) {
|
|
||||||
jBuilder.setMimeType("audio/flac");
|
|
||||||
} else if (parseOpus(name, file, jBuilder)) {
|
|
||||||
jBuilder.setMimeType("audio/opus");
|
|
||||||
} else if (parseVorbis(name, file, jBuilder)) {
|
|
||||||
jBuilder.setMimeType("audio/vorbis");
|
|
||||||
} else if (parseWav(name, file, jBuilder)) {
|
|
||||||
jBuilder.setMimeType("audio/wav");
|
|
||||||
} else {
|
|
||||||
LOGE("File format in %s is not supported", name);
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
return jBuilder.build();
|
|
||||||
} catch (std::exception &e) {
|
|
||||||
LOGE("Unable to parse metadata in %s: %s", name != nullptr ? name : "unknown file", e.what());
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,31 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (c) 2024 Auxio Project
|
|
||||||
* util.h is part of Auxio.
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef AUXIO_UTIL_H
|
|
||||||
#define AUXIO_UTIL_H
|
|
||||||
|
|
||||||
#include <jni.h>
|
|
||||||
#include <android/log.h>
|
|
||||||
|
|
||||||
#define LOG_TAG "taglib_jni"
|
|
||||||
#define LOGE(...) \
|
|
||||||
((void)__android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__))
|
|
||||||
#define LOGD(...) \
|
|
||||||
((void)__android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__))
|
|
||||||
|
|
||||||
#endif //AUXIO_UTIL_H
|
|
Loading…
Reference in a new issue