diff --git a/crates/voicevox_core_java_api/README.md b/crates/voicevox_core_java_api/README.md index d1bc31938..45d249a87 100644 --- a/crates/voicevox_core_java_api/README.md +++ b/crates/voicevox_core_java_api/README.md @@ -40,28 +40,32 @@ VOICEVOX CORE の Java バインディング。 バインディングは `cargo build` でビルドできます。 Java プロジェクトを動かすには、 -- `LD_LIBRARY_PATH`などの環境変数に `[プロジェクトルート]/target/debug`(または`/release`) を追加するか、 -- `lib/src/main/resources/dll/[target]/libvoicevox_core_java_api.so` を作成する(`libvoicevox_core_java_api.so`はプラットフォームによって異なります、詳細は後述)。 +- `LD_LIBRARY_PATH`などの環境変数に `[プロジェクトルート]/target/debug`(または`/release`) や onnxruntime の DLL があるディレクトリを追加するか、 +- `lib/src/main/resources/dll/[target]/`内に onnxruntime と voicevox_core_java_api の DLL をコピーする 必要があります。 ```console ❯ cargo build -❯ LD_LIBRARY_PATH=$(realpath ../../target/debug) ./gradlew build +❯ export LD_LIBRARY_PATH="$(realpath ../../target/debug):$LD_LIBRARY_PATH" +❯ export LD_LIBRARY_PATH="/path/to/onnxruntime/lib:$LD_LIBRARY_PATH" +❯ ./gradlew build # または ❯ cp ../../target/debug/libvoicevox_core_java_api.so lib/src/main/resources/dll/[target]/libvoicevox_core_java_api.so +❯ cp /path/to/onnxruntime/lib/libonnxruntime.so.1.14.0 lib/src/main/resources/dll/[target]/libonnxruntime.so.1.14.0 ❯ ./gradlew build ``` ## ビルド(リリース) `cargo build --release` で Rust 側を、`./gradlew build` で Java 側をビルドできます。 -パッケージ化する時は lib/src/main/resources/dll 内に dll をコピーしてください。 +パッケージ化する時は lib/src/main/resources/dll 内に DLL をコピーしてください。 ```console ❯ cargo build --release ❯ cp ../../target/release/libvoicevox_core_java_api.so lib/src/main/resources/dll/[target]/libvoicevox_core_java_api.so +❯ cp /path/to/onnxruntime/lib/libonnxruntime.so.1.14.0 lib/src/main/resources/dll/[target]/libonnxruntime.so.1.14.0 ❯ ./gradlew build ``` @@ -85,12 +89,5 @@ Java プロジェクトを動かすには、 Android では、jniLibs から System.loadLibrary で読み込みます。 -Android 以外では、src/main/resources/dll 内の適切な DLL を一時ディレクトリにコピーし、System.load で読み込みます。 -DLL の名前は、 - -- Windows:voicevox_core_java_api.dll -- Linux:libvoicevox_core_java_api.so -- macOS:libvoicevox_core_java_api.dylib - -になります。 +Android 以外では、src/main/resources/dll 内の DLL を一時ディレクトリにコピーし、System.load で読み込みます。 見付からなかった場合は、`System.loadLibrary` で読み込みます。これはデバッグ用です。 diff --git a/crates/voicevox_core_java_api/lib/build.gradle b/crates/voicevox_core_java_api/lib/build.gradle index 3e87cceff..140b56c50 100644 --- a/crates/voicevox_core_java_api/lib/build.gradle +++ b/crates/voicevox_core_java_api/lib/build.gradle @@ -13,6 +13,8 @@ plugins { version = '0.0.0' +def String buildTarget = hasProperty('buildTarget') ? buildTarget : "cpu" + repositories { // Use Maven Central for resolving dependencies. mavenCentral() @@ -36,7 +38,9 @@ dependencies { // https://mvnrepository.com/artifact/jakarta.validation/jakarta.validation-api implementation group: 'jakarta.validation', name: 'jakarta.validation-api', version: '3.0.2' - implementation group: 'com.microsoft.onnxruntime', name: 'onnxruntime', version: '1.14.0' + if (buildTarget == "android") { + implementation group: 'com.microsoft.onnxruntime', name: 'onnxruntime-android', version: '1.14.0' + } } // Apply a specific Java toolchain to ease working on different environments. diff --git a/crates/voicevox_core_java_api/lib/src/main/java/jp/hiroshiba/voicevoxcore/Dll.java b/crates/voicevox_core_java_api/lib/src/main/java/jp/hiroshiba/voicevoxcore/Dll.java index e4e674205..385833b46 100644 --- a/crates/voicevox_core_java_api/lib/src/main/java/jp/hiroshiba/voicevoxcore/Dll.java +++ b/crates/voicevox_core_java_api/lib/src/main/java/jp/hiroshiba/voicevoxcore/Dll.java @@ -1,6 +1,5 @@ package jp.hiroshiba.voicevoxcore; -import ai.onnxruntime.OrtEnvironment; import java.io.InputStream; import java.nio.file.Files; import java.nio.file.Path; @@ -16,16 +15,25 @@ abstract class Dll { } else { String rawOsName = System.getProperty("os.name"); String rawOsArch = System.getProperty("os.arch"); - String osName, osArch, dllName; + String osName, osArch, vvDllName, ortDllName; + String[] ortOptionalDllNames; if (rawOsName.startsWith("Win")) { osName = "windows"; - dllName = "voicevox_core_java_api.dll"; + vvDllName = "voicevox_core_java_api.dll"; + ortDllName = "onnxruntime.dll"; + ortOptionalDllNames = + new String[] {"onnxruntime_providers_shared.dll", "onnxruntime_providers_cuda.dll"}; } else if (rawOsName.startsWith("Mac")) { osName = "macos"; - dllName = "libvoicevox_core_java_api.dylib"; + vvDllName = "libvoicevox_core_java_api.dylib"; + ortDllName = "libonnxruntime.1.14.0.dylib"; + ortOptionalDllNames = new String[] {}; } else if (rawOsName.startsWith("Linux")) { osName = "linux"; - dllName = "libvoicevox_core_java_api.so"; + vvDllName = "libvoicevox_core_java_api.so"; + ortDllName = "libonnxruntime.so.1.14.0"; + ortOptionalDllNames = + new String[] {"libonnxruntime_providers_shared.so", "libonnxruntime_providers_cuda.so"}; } else { throw new RuntimeException("Unsupported OS: " + rawOsName); } @@ -42,27 +50,40 @@ abstract class Dll { } String target = osName + "-" + osArch; - // ONNX Runtime の DLL を読み込む。 - OrtEnvironment.getEnvironment(); - try (InputStream in = Dll.class.getResourceAsStream("/dll/" + target + "/" + dllName)) { - if (in == null) { - try { - // フォールバック。開発用。 - System.loadLibrary("voicevox_core_java_api"); - } catch (UnsatisfiedLinkError e) { - throw new RuntimeException("Failed to load Voicevox Core DLL for " + target, e); - } - } else { - Path tempDir = Paths.get(System.getProperty("java.io.tmpdir")); - Path dllPath = tempDir.resolve(dllName); - dllPath.toFile().deleteOnExit(); - Files.copy(in, dllPath); + loadDll(target, ortDllName, "onnxruntime"); + for (String dllName : ortOptionalDllNames) { + loadDll(target, dllName); + } + loadDll(target, vvDllName, "voicevox_core_java_api"); + } + } + + private static void loadDll(String target, String dllName) { + loadDll(target, dllName, null); + } - System.load(dllPath.toAbsolutePath().toString()); + private static void loadDll(String target, String dllName, String fallbackDllName) { + String resourceRoot = "/dll/" + target + "/"; + try (InputStream in = Dll.class.getResourceAsStream(resourceRoot + dllName)) { + if (in == null) { + if (fallbackDllName == null) { + return; + } + try { + System.loadLibrary(fallbackDllName); + } catch (UnsatisfiedLinkError e) { + throw new RuntimeException("Failed to load " + dllName + " for " + target, e); } - } catch (Exception e) { - throw new RuntimeException("Failed to load Voicevox Core DLL for " + target, e); + } else { + Path tempDir = Paths.get(System.getProperty("java.io.tmpdir")); + Path dllPath = tempDir.resolve(dllName); + dllPath.toFile().deleteOnExit(); + Files.copy(in, dllPath); + + System.load(dllPath.toAbsolutePath().toString()); } + } catch (Exception e) { + throw new RuntimeException("Failed to load " + dllName + " for " + target, e); } } } diff --git a/crates/voicevox_core_java_api/lib/src/test/java/jp/hiroshiba/voicevoxcore/SynthesizerTest.java b/crates/voicevox_core_java_api/lib/src/test/java/jp/hiroshiba/voicevoxcore/SynthesizerTest.java index fe9b8220b..4172d310c 100644 --- a/crates/voicevox_core_java_api/lib/src/test/java/jp/hiroshiba/voicevoxcore/SynthesizerTest.java +++ b/crates/voicevox_core_java_api/lib/src/test/java/jp/hiroshiba/voicevoxcore/SynthesizerTest.java @@ -4,7 +4,6 @@ */ package jp.hiroshiba.voicevoxcore; -import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; import java.util.List; @@ -37,19 +36,19 @@ boolean checkAllMoras( void checkModel() { VoiceModel model = loadModel(); OpenJtalk openJtalk = loadOpenJtalk(); - Synthesizer synthesizer = Synthesizer.builder(openJtalk).build(); + Synthesizer synthesizer = + Synthesizer.builder(openJtalk).accelerationMode(Synthesizer.AccelerationMode.CPU).build(); + synthesizer.loadVoiceModel(model); + assertTrue(synthesizer.isLoadedVoiceModel(model.id)); - synthesizer.unloadVoiceModel(model.id); - assertFalse(synthesizer.isLoadedVoiceModel(model.id)); } @Test void checkAudioQuery() { VoiceModel model = loadModel(); - OpenJtalk openJtalk = loadOpenJtalk(); - Synthesizer synthesizer = Synthesizer.builder(openJtalk).build(); - synthesizer.loadVoiceModel(model); + Synthesizer synthesizer = createSynthesizer(); + AudioQuery query = synthesizer.createAudioQuery("こんにちは", model.metas[0].styles[0].id).execute(); synthesizer.synthesis(query, model.metas[0].styles[0].id).execute(); @@ -58,9 +57,8 @@ void checkAudioQuery() { @Test void checkAccentPhrases() { VoiceModel model = loadModel(); - OpenJtalk openJtalk = loadOpenJtalk(); - Synthesizer synthesizer = Synthesizer.builder(openJtalk).build(); - synthesizer.loadVoiceModel(model); + Synthesizer synthesizer = createSynthesizer(); + List accentPhrases = synthesizer.createAccentPhrases("こんにちは", model.metas[0].styles[0].id).execute(); List accentPhrases2 = @@ -88,9 +86,7 @@ void checkAccentPhrases() { @Test void checkTts() { VoiceModel model = loadModel(); - OpenJtalk openJtalk = loadOpenJtalk(); - Synthesizer synthesizer = Synthesizer.builder(openJtalk).build(); - synthesizer.loadVoiceModel(model); + Synthesizer synthesizer = createSynthesizer(); synthesizer.tts("こんにちは", model.metas[0].styles[0].id).execute(); } } diff --git a/crates/voicevox_core_java_api/lib/src/test/java/jp/hiroshiba/voicevoxcore/TestUtils.java b/crates/voicevox_core_java_api/lib/src/test/java/jp/hiroshiba/voicevoxcore/TestUtils.java index 670eddbdb..c4d861804 100644 --- a/crates/voicevox_core_java_api/lib/src/test/java/jp/hiroshiba/voicevoxcore/TestUtils.java +++ b/crates/voicevox_core_java_api/lib/src/test/java/jp/hiroshiba/voicevoxcore/TestUtils.java @@ -3,7 +3,14 @@ import java.io.File; class TestUtils { + VoiceModel voiceModel = null; + OpenJtalk openJtalk = null; + Synthesizer synthesizer = null; + VoiceModel loadModel() { + if (voiceModel != null) { + return voiceModel; + } // cwdはvoicevox_core/crates/voicevox_core_java_api/lib String cwd = System.getProperty("user.dir"); File path = new File(cwd + "/../../../model/sample.vvm"); @@ -16,6 +23,9 @@ VoiceModel loadModel() { } OpenJtalk loadOpenJtalk() { + if (openJtalk != null) { + return openJtalk; + } String cwd = System.getProperty("user.dir"); File path = new File(cwd + "/../../test_util/data/open_jtalk_dic_utf_8-1.11"); @@ -25,4 +35,15 @@ OpenJtalk loadOpenJtalk() { throw new RuntimeException(e); } } + + Synthesizer createSynthesizer() { + if (synthesizer != null) { + return synthesizer; + } + OpenJtalk openJtalk = loadOpenJtalk(); + Synthesizer synthesizer = + Synthesizer.builder(openJtalk).accelerationMode(Synthesizer.AccelerationMode.CPU).build(); + synthesizer.loadVoiceModel(loadModel()); + return synthesizer; + } }