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

Improve: Java APIを色々改善 #673

Merged
merged 16 commits into from
Nov 7, 2023
Merged
Show file tree
Hide file tree
Changes from 10 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
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package jp.hiroshiba.voicevoxcore;

/** テキスト解析機としてのOpen JTalk。 */
public class OpenJtalk extends Dll {
private long handle;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,10 +25,36 @@ protected void finalize() throws Throwable {
super.finalize();
}

/**
* ハードウェアアクセラレーションがGPUモードかどうかを返す。
*
* @return ハードウェアアクセラレーションがGPUモードかどうか。
*/
public boolean isGpuMode() {
return rsIsGpuMode();
}

/**
* メタ情報を取得する。
*
* @return メタ情報。
*/
@Nonnull
public VoiceModel.SpeakerMeta[] getMetas() {
Copy link
Member

@Hiroshiba Hiroshiba Nov 3, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

気になったので質問です!

これがJavaAPIのメソッドになる感じなんでしたっけ。
Rustは.metas、pythonもmetasなのですが、JavaはgetMetasになってるかも? 👀
(言語仕様に詳しくないので、その方が望ましいのかがわからず。。)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

JavaではgetMetasという名前は、「metasというメンバ変数が内部にあり、それに対するgetter」という意味であるのが一般的かと思います。

この命名をするのであれば、VoiceModelのようにSpeakerMeta[] metasを持って随時更新し、それに対するgetterという形の定義にした方がよいと思います。
(ちなみに私の感覚だとpublic finalというgetterを介さないメンバ変数の露出は、Javaの慣習に反するのではと思っています。)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

たしかにgetMetasはmetasプロパティの単なるgetterじゃないので直感的じゃないかもと思いました。
audio_query辺りと一緒だから命名規則もこれに合わせておくと直感的かも。

ということで変更お願いできると・・・!! @sevenc-nanashi

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

#metas()にしました(という解釈で合ってるのかな)

Gson gson = new Gson();
String metasJson = rsGetMetasJson();
VoiceModel.SpeakerMeta[] rawMetas = gson.fromJson(metasJson, VoiceModel.SpeakerMeta[].class);
if (rawMetas == null) {
throw new NullPointerException("metas");
}
return rawMetas;
}

/**
* モデルを読み込む。
*
* @param voiceModel 読み込むモデル。
* @throws InvalidModelDataException 無効なモデルデータの場合。
*/
public void loadVoiceModel(VoiceModel voiceModel) throws InvalidModelDataException {
rsLoadVoiceModel(voiceModel);
Expand Down Expand Up @@ -59,6 +85,7 @@ public boolean isLoadedVoiceModel(String voiceModelId) {
* @param kana AquesTalk風記法。
* @param styleId スタイルID。
* @return {@link AudioQuery}。
* @throws InferenceFailedException 推論に失敗した場合。
*/
@Nonnull
public AudioQuery createAudioQueryFromKana(String kana, int styleId)
Expand All @@ -82,6 +109,7 @@ public AudioQuery createAudioQueryFromKana(String kana, int styleId)
* @param text 日本語のテキスト。
* @param styleId スタイルID。
* @return {@link AudioQuery}。
* @throws InferenceFailedException 推論に失敗した場合。
*/
@Nonnull
public AudioQuery createAudioQuery(String text, int styleId) throws InferenceFailedException {
Expand All @@ -104,6 +132,7 @@ public AudioQuery createAudioQuery(String text, int styleId) throws InferenceFai
* @param kana AquesTalk風記法。
* @param styleId スタイルID。
* @return {@link AccentPhrase} のリスト。
* @throws InferenceFailedException 推論に失敗した場合。
*/
@Nonnull
public List<AccentPhrase> createAccentPhrasesFromKana(String kana, int styleId)
Expand All @@ -123,6 +152,7 @@ public List<AccentPhrase> createAccentPhrasesFromKana(String kana, int styleId)
* @param text 日本語のテキスト。
* @param styleId スタイルID。
* @return {@link AccentPhrase} のリスト。
* @throws InferenceFailedException 推論に失敗した場合。
*/
@Nonnull
public List<AccentPhrase> createAccentPhrases(String text, int styleId)
Expand All @@ -142,6 +172,7 @@ public List<AccentPhrase> createAccentPhrases(String text, int styleId)
* @param accentPhrases 変更元のアクセント句の配列。
* @param styleId スタイルID。
* @return 変更後のアクセント句の配列。
* @throws InferenceFailedException 推論に失敗した場合。
*/
@Nonnull
public List<AccentPhrase> replaceMoraData(List<AccentPhrase> accentPhrases, int styleId)
Expand All @@ -161,6 +192,7 @@ public List<AccentPhrase> replaceMoraData(List<AccentPhrase> accentPhrases, int
* @param accentPhrases 変更元のアクセント句の配列。
* @param styleId スタイルID。
* @return 変更後のアクセント句の配列。
* @throws InferenceFailedException 推論に失敗した場合。
*/
@Nonnull
public List<AccentPhrase> replacePhonemeLength(List<AccentPhrase> accentPhrases, int styleId)
Expand All @@ -180,6 +212,7 @@ public List<AccentPhrase> replacePhonemeLength(List<AccentPhrase> accentPhrases,
* @param accentPhrases 変更元のアクセント句の配列。
* @param styleId スタイルID。
* @return 変更後のアクセント句の配列。
* @throws InferenceFailedException 推論に失敗した場合。
*/
@Nonnull
public List<AccentPhrase> replaceMoraPitch(List<AccentPhrase> accentPhrases, int styleId)
Expand Down Expand Up @@ -234,6 +267,11 @@ public TtsConfigurator tts(String text, int styleId) {

private native void rsNew(OpenJtalk openJtalk, Builder builder);

private native boolean rsIsGpuMode();

@Nonnull
private native String rsGetMetasJson();

private native void rsLoadVoiceModel(VoiceModel voiceModel) throws InvalidModelDataException;

private native void rsUnloadVoiceModel(String voiceModelId);
Expand Down Expand Up @@ -382,6 +420,7 @@ public SynthesisConfigurator interrogativeUpspeak(boolean interrogativeUpspeak)
* {@link AudioQuery} から音声合成する。
*
* @return 音声データ。
* @throws InferenceFailedException 推論に失敗した場合。
*/
@Nonnull
public byte[] execute() throws InferenceFailedException {
Expand Down Expand Up @@ -426,6 +465,7 @@ public TtsFromKanaConfigurator interrogativeUpspeak(boolean interrogativeUpspeak
* {@link AudioQuery} から音声合成する。
*
* @return 音声データ。
* @throws InferenceFailedException 推論に失敗した場合。
*/
@Nonnull
public byte[] execute() throws InferenceFailedException {
Expand Down Expand Up @@ -468,6 +508,7 @@ public TtsConfigurator interrogativeUpspeak(boolean interrogativeUpspeak) {
* {@link AudioQuery} から音声合成する。
*
* @return 音声データ。
* @throws InferenceFailedException 推論に失敗した場合。
*/
@Nonnull
public byte[] execute() throws InferenceFailedException {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ public void importDict(UserDict dict) {
* ユーザー辞書を読み込む。
*
* @param path ユーザー辞書のパス。
* @throws LoadUserDictException ユーザー辞書を読み込めなかった場合。
*/
public void load(String path) throws LoadUserDictException {
rsLoad(path);
Expand All @@ -83,6 +84,7 @@ public void load(String path) throws LoadUserDictException {
* ユーザー辞書を保存する。
*
* @param path ユーザー辞書のパス。
* @throws SaveUserDictException ユーザー辞書を保存できなかった場合。
*/
public void save(String path) throws SaveUserDictException {
rsSave(path);
Expand Down
sevenc-nanashi marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
package jp.hiroshiba.voicevoxcore;

import com.google.gson.Gson;
import com.google.gson.annotations.Expose;
import com.google.gson.annotations.SerializedName;
import jakarta.annotation.Nonnull;

/** VOICEVOX CORE自体の情報。 */
public class VoicevoxCoreInfo extends Dll {
/**
* ライブラリのバージョン。
*
* @return ライブラリのバージョン。
*/
@Nonnull
public static String getVersion() {
String version = rsGetVersion();
if (version == null) {
throw new NullPointerException("version");
}
return version;
sevenc-nanashi marked this conversation as resolved.
Show resolved Hide resolved
}

/**
* このライブラリで利用可能なデバイスの情報を取得する。
*
* @return {@link SupportedDevices}。
*/
@Nonnull
public static SupportedDevices getSupportedDevices() {
Gson gson = new Gson();
String supportedDevicesJson = rsGetSupportedDevicesJson();
SupportedDevices supportedDevices = gson.fromJson(supportedDevicesJson, SupportedDevices.class);
if (supportedDevices == null) {
throw new NullPointerException("supported_devices");
}
return supportedDevices;
}

@Nonnull
private static native String rsGetVersion();

@Nonnull
private static native String rsGetSupportedDevicesJson();

/**
* このライブラリで利用可能なデバイスの情報。
*
* <p>あくまで本ライブラリが対応しているデバイスの情報であることに注意。GPUが使える環境ではなかったとしても {@link #cuda} や {@link #dml} は {@code
* true} を示しうる。
*/
public static class SupportedDevices {
/**
* CPUが利用可能。
*
* <p>常に <code>true</code> 。
*/
@SerializedName("cpu")
@Expose
@Nonnull
public final boolean cpu;

/**
* CUDAが利用可能。
*
* <p>ONNX Runtimeの <a href=
* "https://onnxruntime.ai/docs/execution-providers/CUDA-ExecutionProvider.html"
* target="_blank">CUDAExecutionProvider</a>に対応する。 必要な環境についてはそちらを参照。
*/
@SerializedName("cuda")
@Expose
@Nonnull
public final boolean cuda;

/**
* DirectMLが利用可能。
*
* <p>ONNX Runtimeの <a href=
* "https://onnxruntime.ai/docs/execution-providers/DirectML-ExecutionProvider.html"
* target="_blank">DmlExecutionProvider</a>に対応する。 必要な環境についてはそちらを参照。
*/
@SerializedName("dml")
@Expose
@Nonnull
public final boolean dml;

private SupportedDevices() {
this.cpu = false;
this.cuda = false;
this.dml = false;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
/*
* VoicevoxCoreInfoのテスト。
*/
package jp.hiroshiba.voicevoxcore;

import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertTrue;

import org.junit.jupiter.api.Test;

class InfoTest {
@Test
void checkVersion() {
assertNotNull(VoicevoxCoreInfo.getVersion());
}

@Test
void checkSupportedDevices() {
VoicevoxCoreInfo.SupportedDevices supportedDevices = VoicevoxCoreInfo.getSupportedDevices();

assertNotNull(supportedDevices);
assertTrue(supportedDevices.cpu);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,14 @@ interface MoraCheckCallback {
boolean check(Mora mora, Mora otherMora);
}

@Test
void checkIsGpuMode() {
OpenJtalk openJtalk = loadOpenJtalk();
Synthesizer synthesizer =
Synthesizer.builder(openJtalk).accelerationMode(Synthesizer.AccelerationMode.CPU).build();
assertFalse(synthesizer.isGpuMode());
}

boolean checkAllMoras(
List<AccentPhrase> accentPhrases,
List<AccentPhrase> otherAccentPhrases,
Expand All @@ -40,9 +48,17 @@ void checkModel() throws InvalidModelDataException {
VoiceModel model = loadModel();
OpenJtalk openJtalk = loadOpenJtalk();
Synthesizer synthesizer = Synthesizer.builder(openJtalk).build();

assertTrue(synthesizer.getMetas().length == 0);

synthesizer.loadVoiceModel(model);

assertTrue(synthesizer.getMetas().length >= 1);
assertTrue(synthesizer.isLoadedVoiceModel(model.id));

synthesizer.unloadVoiceModel(model.id);

assertTrue(synthesizer.getMetas().length == 0);
assertFalse(synthesizer.isLoadedVoiceModel(model.id));
}

Expand Down
22 changes: 22 additions & 0 deletions crates/voicevox_core_java_api/src/info.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
use crate::common::throw_if_err;
use jni::{sys::jobject, JNIEnv};
#[no_mangle]
extern "system" fn Java_jp_hiroshiba_voicevoxcore_VoicevoxCoreInfo_rsGetVersion(
env: JNIEnv<'_>,
) -> jobject {
throw_if_err(env, std::ptr::null_mut(), |env| {
let version = env.new_string(env!("CARGO_PKG_VERSION"))?;
Ok(version.into_raw())
})
}
#[no_mangle]
extern "system" fn Java_jp_hiroshiba_voicevoxcore_VoicevoxCoreInfo_rsGetSupportedDevicesJson(
env: JNIEnv<'_>,
) -> jobject {
throw_if_err(env, std::ptr::null_mut(), |env| {
let supported_devices = voicevox_core::SupportedDevices::create()?;
let json = serde_json::to_string(&supported_devices).expect("Should not fail");
let json = env.new_string(json)?;
Ok(json.into_raw())
})
}
1 change: 1 addition & 0 deletions crates/voicevox_core_java_api/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
mod common;
mod info;
mod open_jtalk;
mod synthesizer;
mod user_dict;
Expand Down
31 changes: 31 additions & 0 deletions crates/voicevox_core_java_api/src/synthesizer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,37 @@ unsafe extern "system" fn Java_jp_hiroshiba_voicevoxcore_Synthesizer_rsNew<'loca
Ok(())
})
}
#[no_mangle]
unsafe extern "system" fn Java_jp_hiroshiba_voicevoxcore_Synthesizer_rsIsGpuMode<'local>(
env: JNIEnv<'local>,
this: JObject<'local>,
) -> jboolean {
throw_if_err(env, false, |env| {
let internal = env
.get_rust_field::<_, _, Arc<voicevox_core::Synthesizer>>(&this, "handle")?
.clone();

Ok(internal.is_gpu_mode())
})
.into()
}
#[no_mangle]
unsafe extern "system" fn Java_jp_hiroshiba_voicevoxcore_Synthesizer_rsGetMetasJson<'local>(
env: JNIEnv<'local>,
this: JObject<'local>,
) -> jobject {
throw_if_err(env, std::ptr::null_mut(), |env| {
let internal = env
.get_rust_field::<_, _, Arc<voicevox_core::Synthesizer>>(&this, "handle")?
.clone();

let metas_json = serde_json::to_string(&internal.metas()).expect("should not fail");

let j_metas_json = env.new_string(metas_json)?;

Ok(j_metas_json.into_raw())
})
}

#[no_mangle]
unsafe extern "system" fn Java_jp_hiroshiba_voicevoxcore_Synthesizer_rsLoadVoiceModel<'local>(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ class Synthesizer:
"""ハードウェアアクセラレーションがGPUモードかどうか。"""
...
@property
def metas(self) -> SpeakerMeta:
def metas(self) -> List[SpeakerMeta]:
"""メタ情報。"""
...
async def load_voice_model(self, model: VoiceModel) -> None:
Expand Down
12 changes: 12 additions & 0 deletions example/java/.gitattributes
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
#
# https://help.github.com/articles/dealing-with-line-endings/
#
# Linux start script should use lf
./gradlew text eol=lf linguist-vendored linguist-generated

# These are Windows script files and should use crlf
*.bat text eol=crlf

./gradlew linguist-vendored linguist-generated
./gradlew.bat linguist-vendored linguist-generated
./gradle/wrapper/gradle-wrapper.jar linguist-vendored linguist-generated
6 changes: 6 additions & 0 deletions example/java/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
# Ignore Gradle project-specific cache directory
.gradle

# Ignore Gradle build output directory
build
output.wav
Loading
Loading