musikr: remove old cpp module

This commit is contained in:
Alexander Capehart 2025-02-19 12:23:03 -07:00
parent 97bac886d0
commit 78a06a0430
No known key found for this signature in database
GPG key ID: 37DBE3621FE9AD47
18 changed files with 0 additions and 1389 deletions

View file

@ -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)

View file

@ -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;
}

View file

@ -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

View file

@ -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;
}

View file

@ -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

View file

@ -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");
}

View file

@ -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

View file

@ -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);
}

View file

@ -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

View file

@ -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;
}

View file

@ -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

View file

@ -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;
}

View file

@ -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

View file

@ -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)));
}

View file

@ -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

View file

@ -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)

View file

@ -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;
}
}

View file

@ -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