Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Java API:onnxruntimeを自前で用意するように #610

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 9 additions & 12 deletions crates/voicevox_core_java_api/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
```

Expand All @@ -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` で読み込みます。これはデバッグ用です。
6 changes: 5 additions & 1 deletion crates/voicevox_core_java_api/lib/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ plugins {

version = '0.0.0'

def String buildTarget = hasProperty('buildTarget') ? buildTarget : "cpu"

repositories {
// Use Maven Central for resolving dependencies.
mavenCentral()
Expand All @@ -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.
Expand Down
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -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);
}
Expand All @@ -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);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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();
Expand All @@ -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<AccentPhrase> accentPhrases =
synthesizer.createAccentPhrases("こんにちは", model.metas[0].styles[0].id).execute();
List<AccentPhrase> accentPhrases2 =
Expand Down Expand Up @@ -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();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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");
Expand All @@ -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");

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