diff --git a/musikr/src/main/cpp/CMakeLists.txt b/musikr/src/main/cpp/CMakeLists.txt index dd0db4101..23bb28d13 100644 --- a/musikr/src/main/cpp/CMakeLists.txt +++ b/musikr/src/main/cpp/CMakeLists.txt @@ -45,9 +45,13 @@ set_target_properties( add_library(${CMAKE_PROJECT_NAME} SHARED # List C/C++ source files with relative paths to this CMakeLists.txt. taglib_jni.cpp - JVMInputStream.cpp - JVMTagMap.cpp - JVMMetadataBuilder.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 diff --git a/musikr/src/main/cpp/JByteArrayRef.cpp b/musikr/src/main/cpp/JByteArrayRef.cpp new file mode 100644 index 000000000..297d9d859 --- /dev/null +++ b/musikr/src/main/cpp/JByteArrayRef.cpp @@ -0,0 +1,47 @@ +/* + * 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 . + */ + +#include "JByteArrayRef.h" + +JByteArrayRef::JByteArrayRef(JNIEnv *env, TagLib::ByteVector &data) : env(env) { + auto size = static_cast(data.size()); + array = env->NewByteArray(size); + env->SetByteArrayRegion(array, 0, static_cast(size), + reinterpret_cast(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(data), length); + env->ReleaseByteArrayElements(array, data, JNI_ABORT); + return byteVector; +} + +jbyteArray& JByteArrayRef::operator*() { + return array; +} + diff --git a/musikr/src/main/cpp/JByteArrayRef.h b/musikr/src/main/cpp/JByteArrayRef.h new file mode 100644 index 000000000..02ce6e032 --- /dev/null +++ b/musikr/src/main/cpp/JByteArrayRef.h @@ -0,0 +1,45 @@ +/* + * 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 . + */ + +#ifndef AUXIO_JBYTEARRAYREF_H +#define AUXIO_JBYTEARRAYREF_H + +#include +#include + +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 diff --git a/musikr/src/main/cpp/JClassRef.cpp b/musikr/src/main/cpp/JClassRef.cpp new file mode 100644 index 000000000..8bf38a3b5 --- /dev/null +++ b/musikr/src/main/cpp/JClassRef.cpp @@ -0,0 +1,34 @@ +/* + * 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 . + */ + +#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; +} diff --git a/musikr/src/main/cpp/JClassRef.h b/musikr/src/main/cpp/JClassRef.h new file mode 100644 index 000000000..b9c5336b1 --- /dev/null +++ b/musikr/src/main/cpp/JClassRef.h @@ -0,0 +1,45 @@ +/* + * 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 . + */ + +#ifndef AUXIO_JCLASSREF_H +#define AUXIO_JCLASSREF_H + +#include + +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 diff --git a/musikr/src/main/cpp/JInputStream.cpp b/musikr/src/main/cpp/JInputStream.cpp new file mode 100644 index 000000000..14693d394 --- /dev/null +++ b/musikr/src/main/cpp/JInputStream.cpp @@ -0,0 +1,134 @@ +/* + * 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 . + */ + +#include "JInputStream.h" + +#include + +#include "JClassRef.h" +#include "JByteArrayRef.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("oStream is not an instance of TagLibOStream"); + } + jInputStreamReadBlockMethod = jInputStreamClass.method("readBlock", + "(J)[B"); + 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. + return ""; +} + +TagLib::ByteVector JInputStream::readBlock(size_t length) { + // Do manual memory management here since we don't to avoid the added abstraction + // overhead of a smart JByteArrayRef. + auto data = env->CallObjectMethod(jInputStream, jInputStreamReadBlockMethod, + static_cast(length)); + if (data == nullptr) { + throw std::runtime_error("Failed to read block, see logs"); + } + JByteArrayRef jByteArray = { env, reinterpret_cast(data) }; + return jByteArray.copy(); +} + +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(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(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(jlength); +} + +void JInputStream::truncate(TagLib::offset_t length) { + throw std::runtime_error("Not implemented"); +} + diff --git a/musikr/src/main/cpp/JVMInputStream.h b/musikr/src/main/cpp/JInputStream.h similarity index 82% rename from musikr/src/main/cpp/JVMInputStream.h rename to musikr/src/main/cpp/JInputStream.h index f89e39d3b..084f9dc31 100644 --- a/musikr/src/main/cpp/JVMInputStream.h +++ b/musikr/src/main/cpp/JInputStream.h @@ -1,6 +1,6 @@ /* * Copyright (c) 2024 Auxio Project - * JVMInputStream.h is part of Auxio. + * 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 @@ -16,21 +16,22 @@ * along with this program. If not, see . */ -#ifndef AUXIO_JVMINPUTSTREAM_H -#define AUXIO_JVMINPUTSTREAM_H +#ifndef AUXIO_JINPUTSTREAM_H +#define AUXIO_JINPUTSTREAM_H #include +#include "JObjectRef.h" #include "taglib/tiostream.h" -class JVMInputStream: public TagLib::IOStream { +class JInputStream: public TagLib::IOStream { public: - JVMInputStream(JNIEnv *env, jobject inputStream); + JInputStream(JNIEnv *env, jobject jInputStream); - ~JVMInputStream(); + ~JInputStream(); - JVMInputStream(const JVMInputStream&) = delete; - JVMInputStream& operator=(const JVMInputStream&) = delete; + JInputStream(const JInputStream&) = delete; + JInputStream& operator=(const JInputStream&) = delete; /*! * Returns the stream name in the local file system encoding. @@ -113,15 +114,14 @@ public: private: JNIEnv *env; - jobject inputStream; - jmethodID inputStreamReadBlockMethod; - jmethodID inputStreamIsOpenMethod; - jmethodID inputStreamSeekFromBeginningMethod; - jmethodID inputStreamSeekFromCurrentMethod; - jmethodID inputStreamSeekFromEndMethod; - jmethodID inputStreamTellMethod; - jmethodID inputStreamLengthMethod; - + jobject jInputStream; + jmethodID jInputStreamReadBlockMethod; + jmethodID jInputStreamIsOpenMethod; + jmethodID jInputStreamSeekFromBeginningMethod; + jmethodID jInputStreamSeekFromCurrentMethod; + jmethodID jInputStreamSeekFromEndMethod; + jmethodID jInputStreamTellMethod; + jmethodID jInputStreamLengthMethod; }; -#endif //AUXIO_JVMINPUTSTREAM_H +#endif //AUXIO_JINPUTSTREAM_H diff --git a/musikr/src/main/cpp/JVMMetadataBuilder.cpp b/musikr/src/main/cpp/JMetadataBuilder.cpp similarity index 74% rename from musikr/src/main/cpp/JVMMetadataBuilder.cpp rename to musikr/src/main/cpp/JMetadataBuilder.cpp index e0e4996eb..f7aa61457 100644 --- a/musikr/src/main/cpp/JVMMetadataBuilder.cpp +++ b/musikr/src/main/cpp/JMetadataBuilder.cpp @@ -1,6 +1,6 @@ /* * Copyright (c) 2024 Auxio Project - * JVMMetadataBuilder.cpp is part of Auxio. + * 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 @@ -16,7 +16,7 @@ * along with this program. If not, see . */ -#include "JVMMetadataBuilder.h" +#include "JMetadataBuilder.h" #include "util.h" @@ -26,15 +26,20 @@ #include -JVMMetadataBuilder::JVMMetadataBuilder(JNIEnv *env) : env(env), id3v2(env), xiph( +#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 JVMMetadataBuilder::setMimeType(const std::string_view type) { - this->mimeType = type; +void JMetadataBuilder::setMimeType(TagLib::String type) { + mimeType = type; } -void JVMMetadataBuilder::setId3v1(TagLib::ID3v1::Tag &tag) { +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()); @@ -46,7 +51,7 @@ void JVMMetadataBuilder::setId3v1(TagLib::ID3v1::Tag &tag) { } } -void JVMMetadataBuilder::setId3v2(TagLib::ID3v2::Tag &tag) { +void JMetadataBuilder::setId3v2(TagLib::ID3v2::Tag &tag) { // We want to ideally find the front cover, fall back to the first picture otherwise. std::optional firstPic; std::optional frontCoverPic; @@ -89,7 +94,7 @@ void JVMMetadataBuilder::setId3v2(TagLib::ID3v2::Tag &tag) { } } -void JVMMetadataBuilder::setXiph(TagLib::Ogg::XiphComment &tag) { +void JMetadataBuilder::setXiph(TagLib::Ogg::XiphComment &tag) { for (auto field : tag.fieldListMap()) { auto key = field.first.upper(); auto values = field.second; @@ -100,7 +105,7 @@ void JVMMetadataBuilder::setXiph(TagLib::Ogg::XiphComment &tag) { } template -void mp4AddImpl(JVMTagMap &map, TagLib::String &itemName, T itemValue) { +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(':'); @@ -112,7 +117,7 @@ void mp4AddImpl(JVMTagMap &map, TagLib::String &itemName, T itemValue) { } } -void JVMMetadataBuilder::setMp4(TagLib::MP4::Tag &tag) { +void JMetadataBuilder::setMp4(TagLib::MP4::Tag &tag) { auto map = tag.itemMap(); std::optional < TagLib::MP4::CoverArt > firstCover; for (auto item : map) { @@ -165,7 +170,7 @@ void JVMMetadataBuilder::setMp4(TagLib::MP4::Tag &tag) { } } -void JVMMetadataBuilder::setFlacPictures( +void JMetadataBuilder::setFlacPictures( TagLib::List &pics) { // Find the front cover image. If it doesn't exist, fall back to the first image. for (auto pic : pics) { @@ -179,44 +184,33 @@ void JVMMetadataBuilder::setFlacPictures( } } -void JVMMetadataBuilder::setProperties(TagLib::AudioProperties *properties) { +void JMetadataBuilder::setProperties(TagLib::AudioProperties *properties) { this->properties = properties; } -jobject JVMMetadataBuilder::build() { - jclass propertiesClass = env->FindClass( - "org/oxycblt/musikr/metadata/Properties"); - jmethodID propertiesInit = env->GetMethodID(propertiesClass, "", +jobject JMetadataBuilder::build() { + JClassRef jPropertiesClass { env, "org/oxycblt/musikr/metadata/Properties" }; + jmethodID jPropertiesInitMethod = jPropertiesClass.method("", "(Ljava/lang/String;JII)V"); - jstring jmimeType = env->NewStringUTF(mimeType.data()); - jobject propertiesObj = env->NewObject(propertiesClass, propertiesInit, - jmimeType, (jlong) properties->lengthInMilliseconds(), - properties->bitrate(), properties->sampleRate()); - env->DeleteLocalRef(jmimeType); - env->DeleteLocalRef(propertiesClass); + JStringRef jMimeType { env, this->mimeType }; - jclass metadataClass = env->FindClass( - "org/oxycblt/musikr/metadata/Metadata"); - jmethodID metadataInit = env->GetMethodID(metadataClass, "", + 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("", "(Ljava/util/Map;Ljava/util/Map;Ljava/util/Map;[BLorg/" "oxycblt/musikr/metadata/Properties;)V"); - jobject id3v2Map = id3v2.getObject(); - jobject xiphMap = xiph.getObject(); - jobject mp4Map = mp4.getObject(); - jbyteArray coverArray = nullptr; + auto jId3v2Map = id3v2.getObject(); + auto jXiphMap = xiph.getObject(); + auto jMp4Map = mp4.getObject(); if (cover.has_value()) { - auto coverSize = static_cast(cover->size()); - coverArray = env->NewByteArray(coverSize); - env->SetByteArrayRegion(coverArray, 0, coverSize, - reinterpret_cast(cover->data())); + JByteArrayRef jCoverArray { env, cover.value() }; + return env->NewObject(*jMetadataClass, jMetadataInitMethod, **jId3v2Map, + **jXiphMap, **jMp4Map, *jCoverArray, *jProperties); } - jobject metadataObj = env->NewObject(metadataClass, metadataInit, id3v2Map, - xiphMap, mp4Map, coverArray, propertiesObj); - env->DeleteLocalRef(propertiesObj); - env->DeleteLocalRef(metadataClass); - env->DeleteLocalRef(coverArray); - env->DeleteLocalRef(id3v2Map); - env->DeleteLocalRef(xiphMap); - env->DeleteLocalRef(mp4Map); - return metadataObj; + return env->NewObject(*jMetadataClass, jMetadataInitMethod, **jId3v2Map, + **jXiphMap, **jMp4Map, nullptr, *jProperties); } diff --git a/musikr/src/main/cpp/JVMMetadataBuilder.h b/musikr/src/main/cpp/JMetadataBuilder.h similarity index 78% rename from musikr/src/main/cpp/JVMMetadataBuilder.h rename to musikr/src/main/cpp/JMetadataBuilder.h index 1ee52db3a..7bca6cbbb 100644 --- a/musikr/src/main/cpp/JVMMetadataBuilder.h +++ b/musikr/src/main/cpp/JMetadataBuilder.h @@ -1,6 +1,6 @@ /* * Copyright (c) 2024 Auxio Project - * JVMMetadataBuilder.h is part of Auxio. + * 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 @@ -16,8 +16,8 @@ * along with this program. If not, see . */ -#ifndef AUXIO_JVMMETADATABUILDER_H -#define AUXIO_JVMMETADATABUILDER_H +#ifndef AUXIO_JMETADATABUILDER_H +#define AUXIO_JMETADATABUILDER_H #include #include @@ -29,13 +29,13 @@ #include "taglib/mp4tag.h" #include "taglib/audioproperties.h" -#include "JVMTagMap.h" +#include "JTagMap.h" -class JVMMetadataBuilder { +class JMetadataBuilder { public: - JVMMetadataBuilder(JNIEnv *env); + JMetadataBuilder(JNIEnv *env); - void setMimeType(const std::string_view type); + void setMimeType(TagLib::String type); void setId3v1(TagLib::ID3v1::Tag &tag); void setId3v2(TagLib::ID3v2::Tag &tag); void setXiph(TagLib::Ogg::XiphComment &tag); @@ -48,14 +48,14 @@ public: private: JNIEnv *env; - std::string_view mimeType; + TagLib::String mimeType; std::optional cover; TagLib::AudioProperties *properties; - JVMTagMap id3v2; - JVMTagMap xiph; - JVMTagMap mp4; + JTagMap id3v2; + JTagMap xiph; + JTagMap mp4; }; -#endif //AUXIO_JVMMETADATABUILDER_H +#endif //AUXIO_JMETADATABUILDER_H diff --git a/musikr/src/main/cpp/JObjectRef.cpp b/musikr/src/main/cpp/JObjectRef.cpp new file mode 100644 index 000000000..9d6914cbf --- /dev/null +++ b/musikr/src/main/cpp/JObjectRef.cpp @@ -0,0 +1,30 @@ +/* + * 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 . + */ + +#include "JObjectRef.h" + +JObjectRef::JObjectRef(JNIEnv *env, jobject object) : env(env), object(object) { +} + +JObjectRef::~JObjectRef() { + env->DeleteLocalRef(object); +} + +jobject& JObjectRef::operator*() { + return object; +} diff --git a/musikr/src/main/cpp/JObjectRef.h b/musikr/src/main/cpp/JObjectRef.h new file mode 100644 index 000000000..affc383e5 --- /dev/null +++ b/musikr/src/main/cpp/JObjectRef.h @@ -0,0 +1,44 @@ +/* + * 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 . + */ + +#ifndef AUXIO_JOBJECTREF_H +#define AUXIO_JOBJECTREF_H + +#include +#include +#include +#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 diff --git a/musikr/src/main/cpp/JStringRef.cpp b/musikr/src/main/cpp/JStringRef.cpp new file mode 100644 index 000000000..e6b1bf71d --- /dev/null +++ b/musikr/src/main/cpp/JStringRef.cpp @@ -0,0 +1,33 @@ +/* + * 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 . + */ + +#include "JStringRef.h" +#include "util.h" + +JStringRef::JStringRef(JNIEnv *env, const TagLib::String string) { + this->env = env; + this->string = env->NewStringUTF(string.toCString(true)); +} + +JStringRef::~JStringRef() { + env->DeleteLocalRef(string); +} + +jstring& JStringRef::operator*() { + return string; +} diff --git a/musikr/src/main/cpp/JStringRef.h b/musikr/src/main/cpp/JStringRef.h new file mode 100644 index 000000000..ffe4ab8ad --- /dev/null +++ b/musikr/src/main/cpp/JStringRef.h @@ -0,0 +1,42 @@ +/* + * 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 . + */ + +#ifndef AUXIO_JSTRINGREF_H +#define AUXIO_JSTRINGREF_H + +#include +#include + +class JStringRef { +public: + JStringRef(JNIEnv *env, TagLib::String string); + + ~JStringRef(); + + JStringRef(const JStringRef&) = delete; + + JStringRef& operator=(const JStringRef&) = delete; + + jstring& operator*(); + +private: + JNIEnv *env; + jstring string; +}; + +#endif //AUXIO_JSTRINGREF_H diff --git a/musikr/src/main/cpp/JTagMap.cpp b/musikr/src/main/cpp/JTagMap.cpp new file mode 100644 index 000000000..f229f3387 --- /dev/null +++ b/musikr/src/main/cpp/JTagMap.cpp @@ -0,0 +1,118 @@ +/* + * 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 . + */ + +#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("", "()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("", "()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 JTagMap::getObject() { + return std::move( + std::make_unique < JObjectRef + > (env, env->CallObjectMethod(**jTagMap, + jTagMapGetObjectMethod))); +} diff --git a/musikr/src/main/cpp/JVMTagMap.h b/musikr/src/main/cpp/JTagMap.h similarity index 62% rename from musikr/src/main/cpp/JVMTagMap.h rename to musikr/src/main/cpp/JTagMap.h index 2462ef2ff..95293db7b 100644 --- a/musikr/src/main/cpp/JVMTagMap.h +++ b/musikr/src/main/cpp/JTagMap.h @@ -1,6 +1,6 @@ /* * Copyright (c) 2024 Auxio Project - * JVMTagMap.h is part of Auxio. + * 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 @@ -16,21 +16,23 @@ * along with this program. If not, see . */ -#ifndef AUXIO_JVMTAGMAP_H -#define AUXIO_JVMTAGMAP_H +#ifndef AUXIO_JTAGMAP_H +#define AUXIO_JTAGMAP_H #include #include #include #include -class JVMTagMap { -public: - JVMTagMap(JNIEnv *env); - ~JVMTagMap(); +#include "JObjectRef.h" +#include "JClassRef.h" - JVMTagMap(const JVMTagMap&) = delete; - JVMTagMap& operator=(const JVMTagMap&) = delete; +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); @@ -43,21 +45,23 @@ public: void add_combined(TagLib::String id, TagLib::String description, TagLib::StringList values); - jobject getObject(); + std::unique_ptr getObject(); private: JNIEnv *env; - jobject tagMap; - jmethodID tagMapAddIdSingleMethod; - jmethodID tagMapAddIdListMethod; - jmethodID tagMapAddCustomSingleMethod; - jmethodID tagMapAddCustomListMethod; - jmethodID tagMapAddCombinedSingleMethod; - jmethodID tagMapAddCombinedListMethod; - jmethodID tagMapGetObjectMethod; - jclass arrayListClass; - jmethodID arrayListInitMethod; - jmethodID arrayListAddMethod; + + std::unique_ptr jTagMap; + jmethodID jTagMapAddIdSingleMethod; + jmethodID jTagMapAddIdListMethod; + jmethodID jTagMapAddCustomSingleMethod; + jmethodID jTagMapAddCustomListMethod; + jmethodID jTagMapAddCombinedSingleMethod; + jmethodID jTagMapAddCombinedListMethod; + jmethodID jTagMapGetObjectMethod; + + std::unique_ptr jArrayListClass; + jmethodID jArrayListInitMethod; + jmethodID jArrayListAddMethod; }; -#endif //AUXIO_JVMTAGMAP_H +#endif //AUXIO_JTAGMAP_H diff --git a/musikr/src/main/cpp/JVMInputStream.cpp b/musikr/src/main/cpp/JVMInputStream.cpp deleted file mode 100644 index 19e07d8a1..000000000 --- a/musikr/src/main/cpp/JVMInputStream.cpp +++ /dev/null @@ -1,137 +0,0 @@ -/* - * Copyright (c) 2024 Auxio Project - * JVMInputStream.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 . - */ - -#include "JVMInputStream.h" - -#include - -// TODO: Handle stream exceptions -JVMInputStream::JVMInputStream(JNIEnv *env, jobject inputStream) : env(env), inputStream( - inputStream) { - jclass inputStreamClass = env->FindClass( - "org/oxycblt/musikr/metadata/NativeInputStream"); - if (!env->IsInstanceOf(inputStream, inputStreamClass)) { - throw std::runtime_error("oStream is not an instance of TagLibOStream"); - } - inputStreamReadBlockMethod = env->GetMethodID(inputStreamClass, "readBlock", - "(J)[B"); - inputStreamIsOpenMethod = env->GetMethodID(inputStreamClass, "isOpen", - "()Z"); - inputStreamSeekFromBeginningMethod = env->GetMethodID(inputStreamClass, - "seekFromBeginning", "(J)Z"); - inputStreamSeekFromCurrentMethod = env->GetMethodID(inputStreamClass, - "seekFromCurrent", "(J)Z"); - inputStreamSeekFromEndMethod = env->GetMethodID(inputStreamClass, - "seekFromEnd", "(J)Z"); - inputStreamTellMethod = env->GetMethodID(inputStreamClass, "tell", "()J"); - inputStreamLengthMethod = env->GetMethodID(inputStreamClass, "length", - "()J"); - env->DeleteLocalRef(inputStreamClass); -} - -JVMInputStream::~JVMInputStream() { - // The implicit assumption is that inputStream is managed by the owner, - // so we don't need to delete any references here -} - -TagLib::FileName JVMInputStream::name() const { - // Not actually used except in FileRef, can safely ignore. - return ""; -} - -TagLib::ByteVector JVMInputStream::readBlock(size_t length) { - auto data = (jbyteArray) env->CallObjectMethod(inputStream, - inputStreamReadBlockMethod, static_cast(length)); - if (data == nullptr) { - throw std::runtime_error("Failed to read block, see logs"); - } - jsize dataLength = env->GetArrayLength(data); - auto dataBytes = env->GetByteArrayElements(data, nullptr); - TagLib::ByteVector byteVector(reinterpret_cast(dataBytes), - dataLength); - env->ReleaseByteArrayElements(data, dataBytes, JNI_ABORT); - env->DeleteLocalRef(data); - return byteVector; -} - -void JVMInputStream::writeBlock(const TagLib::ByteVector &data) { - throw std::runtime_error("Not implemented"); -} - -void JVMInputStream::insert(const TagLib::ByteVector &data, - TagLib::offset_t start, size_t replace) { - throw std::runtime_error("Not implemented"); -} - -void JVMInputStream::removeBlock(TagLib::offset_t start, size_t length) { - throw std::runtime_error("Not implemented"); -} - -bool JVMInputStream::readOnly() const { - return true; -} - -bool JVMInputStream::isOpen() const { - return env->CallBooleanMethod(inputStream, inputStreamIsOpenMethod); -} - -void JVMInputStream::seek(TagLib::offset_t offset, Position p) { - auto joffset = static_cast(std::llround(offset)); - jboolean result; - switch (p) { - case Beginning: - result = env->CallBooleanMethod(inputStream, - inputStreamSeekFromBeginningMethod, joffset); - break; - case Current: - result = env->CallBooleanMethod(inputStream, - inputStreamSeekFromCurrentMethod, joffset); - break; - case End: - result = env->CallBooleanMethod(inputStream, - inputStreamSeekFromEndMethod, joffset); - break; - } - if (!result) { - throw std::runtime_error("Failed to seek, see logs"); - } -} - -void JVMInputStream::clear() { - // Nothing to do -} - -TagLib::offset_t JVMInputStream::tell() const { - jlong jposition = env->CallLongMethod(inputStream, inputStreamTellMethod); - if (jposition == INT64_MIN) { - throw std::runtime_error("Failed to get position, see logs"); - } - return static_cast(jposition); -} - -TagLib::offset_t JVMInputStream::length() { - jlong jlength = env->CallLongMethod(inputStream, inputStreamLengthMethod); - if (jlength == INT64_MIN) { - throw std::runtime_error("Failed to get length, see logs"); - } - return static_cast(jlength); -} - -void JVMInputStream::truncate(TagLib::offset_t length) { - throw std::runtime_error("Not implemented"); -} diff --git a/musikr/src/main/cpp/JVMTagMap.cpp b/musikr/src/main/cpp/JVMTagMap.cpp deleted file mode 100644 index 526aaf643..000000000 --- a/musikr/src/main/cpp/JVMTagMap.cpp +++ /dev/null @@ -1,131 +0,0 @@ -/* - * Copyright (c) 2024 Auxio Project - * JVMTagMap.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 . - */ - -#include "JVMTagMap.h" - -#include "util.h" - -JVMTagMap::JVMTagMap(JNIEnv *env) : env(env) { - jclass tagMapClass = env->FindClass( - "org/oxycblt/musikr/metadata/NativeTagMap"); - jmethodID init = env->GetMethodID(tagMapClass, "", "()V"); - tagMap = env->NewObject(tagMapClass, init); - tagMapAddIdSingleMethod = env->GetMethodID(tagMapClass, "addID", - "(Ljava/lang/String;Ljava/lang/String;)V"); - tagMapAddIdListMethod = env->GetMethodID(tagMapClass, "addID", - "(Ljava/lang/String;Ljava/util/List;)V"); - tagMapAddCustomSingleMethod = env->GetMethodID(tagMapClass, "addCustom", - "(Ljava/lang/String;Ljava/lang/String;)V"); - tagMapAddCustomListMethod = env->GetMethodID(tagMapClass, "addCustom", - "(Ljava/lang/String;Ljava/util/List;)V"); - tagMapAddCombinedSingleMethod = env->GetMethodID(tagMapClass, "addCombined", - "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V"); - tagMapAddCombinedListMethod = env->GetMethodID(tagMapClass, "addCombined", - "(Ljava/lang/String;Ljava/lang/String;Ljava/util/List;)V"); - tagMapGetObjectMethod = env->GetMethodID(tagMapClass, "getObject", - "()Ljava/util/Map;"); - env->DeleteLocalRef(tagMapClass); - - arrayListClass = env->FindClass("java/util/ArrayList"); - arrayListInitMethod = env->GetMethodID(arrayListClass, "", "()V"); - arrayListAddMethod = env->GetMethodID(arrayListClass, "add", - "(Ljava/lang/Object;)Z"); -} - -JVMTagMap::~JVMTagMap() { - env->DeleteLocalRef(tagMap); - env->DeleteLocalRef(arrayListClass); -} - -void JVMTagMap::add_id(const TagLib::String id, const TagLib::String value) { - jstring jid = env->NewStringUTF(id.toCString(true)); - jstring jvalue = env->NewStringUTF(value.toCString(true)); - env->CallVoidMethod(tagMap, tagMapAddIdSingleMethod, jid, jvalue); - env->DeleteLocalRef(jid); - env->DeleteLocalRef(jvalue); -} - -void JVMTagMap::add_id(const TagLib::String id, - const TagLib::StringList values) { - jstring jid = env->NewStringUTF(id.toCString(true)); - jobject jvalues = env->NewObject(arrayListClass, arrayListInitMethod); - for (auto &item : values) { - jstring jvalue = env->NewStringUTF(item.toCString(true)); - env->CallBooleanMethod(jvalues, arrayListAddMethod, jvalue); - env->DeleteLocalRef(jvalue); - } - env->CallVoidMethod(tagMap, tagMapAddIdListMethod, jid, jvalues); - env->DeleteLocalRef(jid); -} - -void JVMTagMap::add_custom(const TagLib::String description, - const TagLib::String value) { - jstring jdescription = env->NewStringUTF(description.toCString(true)); - jstring jvalue = env->NewStringUTF(value.toCString(true)); - env->CallVoidMethod(tagMap, tagMapAddCustomSingleMethod, jdescription, - jvalue); - env->DeleteLocalRef(jdescription); - env->DeleteLocalRef(jvalue); -} - -void JVMTagMap::add_custom(const TagLib::String description, - const TagLib::StringList values) { - jstring jid = env->NewStringUTF(description.toCString(true)); - jobject jvalues = env->NewObject(arrayListClass, arrayListInitMethod); - for (auto &item : values) { - jstring jvalue = env->NewStringUTF(item.toCString(true)); - env->CallBooleanMethod(jvalues, arrayListAddMethod, jvalue); - env->DeleteLocalRef(jvalue); - } - env->CallVoidMethod(tagMap, tagMapAddCustomListMethod, jid, jvalues); - env->DeleteLocalRef(jid); - env->DeleteLocalRef(jvalues); -} - -void JVMTagMap::add_combined(const TagLib::String id, - const TagLib::String description, const TagLib::String value) { - jstring jid = env->NewStringUTF(id.toCString(true)); - jstring jdescription = env->NewStringUTF(description.toCString(true)); - jstring jvalue = env->NewStringUTF(value.toCString(true)); - env->CallVoidMethod(tagMap, tagMapAddCombinedSingleMethod, jid, - jdescription, jvalue); - env->DeleteLocalRef(jid); - env->DeleteLocalRef(jdescription); - env->DeleteLocalRef(jvalue); -} - -void JVMTagMap::add_combined(const TagLib::String id, - const TagLib::String description, const TagLib::StringList values) { - jstring jid = env->NewStringUTF(id.toCString(true)); - jstring jdescription = env->NewStringUTF(description.toCString(true)); - jobject jvalues = env->NewObject(arrayListClass, arrayListInitMethod); - for (auto &item : values) { - jstring jvalue = env->NewStringUTF(item.toCString(true)); - env->CallBooleanMethod(jvalues, arrayListAddMethod, jvalue); - env->DeleteLocalRef(jvalue); - } - env->CallVoidMethod(tagMap, tagMapAddCombinedListMethod, jid, jdescription, - jvalues); - env->DeleteLocalRef(jid); - env->DeleteLocalRef(jdescription); - env->DeleteLocalRef(jvalues); -} - -jobject JVMTagMap::getObject() { - return env->CallObjectMethod(tagMap, tagMapGetObjectMethod); -} diff --git a/musikr/src/main/cpp/taglib_jni.cpp b/musikr/src/main/cpp/taglib_jni.cpp index a0c393c2c..2bc040ea0 100644 --- a/musikr/src/main/cpp/taglib_jni.cpp +++ b/musikr/src/main/cpp/taglib_jni.cpp @@ -18,8 +18,8 @@ #include #include -#include "JVMInputStream.h" -#include "JVMMetadataBuilder.h" +#include "JInputStream.h" +#include "JMetadataBuilder.h" #include "util.h" #include "taglib/fileref.h" @@ -35,65 +35,65 @@ Java_org_oxycblt_musikr_metadata_TagLibJNI_openNative(JNIEnv *env, jobject /* this */, jobject inputStream) { try { - JVMInputStream stream {env, inputStream}; - TagLib::FileRef fileRef {&stream}; + JInputStream jStream {env, inputStream}; + TagLib::FileRef fileRef {&jStream}; if (fileRef.isNull()) { LOGE("Error opening file"); return nullptr; } TagLib::File *file = fileRef.file(); - JVMMetadataBuilder builder {env}; + JMetadataBuilder jBuilder {env}; if (auto *mpegFile = dynamic_cast(file)) { - builder.setMimeType("audio/mpeg"); + jBuilder.setMimeType("audio/mpeg"); auto id3v1Tag = mpegFile->ID3v1Tag(); if (id3v1Tag != nullptr) { - builder.setId3v1(*id3v1Tag); + jBuilder.setId3v1(*id3v1Tag); } auto id3v2Tag = mpegFile->ID3v2Tag(); if (id3v2Tag != nullptr) { - builder.setId3v2(*id3v2Tag); + jBuilder.setId3v2(*id3v2Tag); } } else if (auto *mp4File = dynamic_cast(file)) { - builder.setMimeType("audio/mp4"); + jBuilder.setMimeType("audio/mp4"); auto tag = mp4File->tag(); if (tag != nullptr) { - builder.setMp4(*tag); + jBuilder.setMp4(*tag); } } else if (auto *flacFile = dynamic_cast(file)) { - builder.setMimeType("audio/flac"); + jBuilder.setMimeType("audio/flac"); auto id3v1Tag = flacFile->ID3v1Tag(); if (id3v1Tag != nullptr) { - builder.setId3v1(*id3v1Tag); + jBuilder.setId3v1(*id3v1Tag); } auto id3v2Tag = flacFile->ID3v2Tag(); if (id3v2Tag != nullptr) { - builder.setId3v2(*id3v2Tag); + jBuilder.setId3v2(*id3v2Tag); } auto xiphComment = flacFile->xiphComment(); if (xiphComment != nullptr) { - builder.setXiph(*xiphComment); + jBuilder.setXiph(*xiphComment); } auto pics = flacFile->pictureList(); - builder.setFlacPictures(pics); + jBuilder.setFlacPictures(pics); } else if (auto *opusFile = dynamic_cast(file)) { - builder.setMimeType("audio/opus"); + jBuilder.setMimeType("audio/opus"); auto tag = opusFile->tag(); if (tag != nullptr) { - builder.setXiph(*tag); + jBuilder.setXiph(*tag); } } else if (auto *vorbisFile = dynamic_cast(file)) { - builder.setMimeType("audio/vorbis"); + jBuilder.setMimeType("audio/vorbis"); auto tag = vorbisFile->tag(); if (tag != nullptr) { - builder.setXiph(*tag); + jBuilder.setXiph(*tag); } } else if (auto *wavFile = dynamic_cast(file)) { - builder.setMimeType("audio/wav"); + jBuilder.setMimeType("audio/wav"); auto tag = wavFile->ID3v2Tag(); if (tag != nullptr) { - builder.setId3v2(*tag); + jBuilder.setId3v2(*tag); } } else { // While taglib supports other formats, ExoPlayer does not. Ignore them. @@ -101,8 +101,8 @@ Java_org_oxycblt_musikr_metadata_TagLibJNI_openNative(JNIEnv *env, return nullptr; } - builder.setProperties(file->audioProperties()); - return builder.build(); + jBuilder.setProperties(file->audioProperties()); + return jBuilder.build(); } catch (std::runtime_error e) { LOGD("Error opening file: %s", e.what()); return nullptr;