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