diff --git a/crates/voicevox_core_java_api/lib/src/main/java/jp/hiroshiba/voicevoxcore/Synthesizer.java b/crates/voicevox_core_java_api/lib/src/main/java/jp/hiroshiba/voicevoxcore/Synthesizer.java index d6437a7d9..1a752236e 100644 --- a/crates/voicevox_core_java_api/lib/src/main/java/jp/hiroshiba/voicevoxcore/Synthesizer.java +++ b/crates/voicevox_core_java_api/lib/src/main/java/jp/hiroshiba/voicevoxcore/Synthesizer.java @@ -25,6 +25,31 @@ protected void finalize() throws Throwable { super.finalize(); } + /** + * ハードウェアアクセラレーションがGPUモードかどうかを返す。 + * + * @return ハードウェアアクセラレーションがGPUモードかどうか。 + */ + public boolean isGpuMode() { + return rsIsGpuMode(); + } + + /** + * メタ情報を取得する。 + * + * @return メタ情報。 + */ + @Nonnull + public VoiceModel.SpeakerMeta[] getMetas() { + 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; + } + /** * モデルを読み込む。 * @@ -242,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); diff --git a/crates/voicevox_core_java_api/lib/src/main/java/jp/hiroshiba/voicevoxcore/VoicevoxCoreInfo.java b/crates/voicevox_core_java_api/lib/src/main/java/jp/hiroshiba/voicevoxcore/VoicevoxCoreInfo.java index e2ab2b93d..fbd0c3d42 100644 --- a/crates/voicevox_core_java_api/lib/src/main/java/jp/hiroshiba/voicevoxcore/VoicevoxCoreInfo.java +++ b/crates/voicevox_core_java_api/lib/src/main/java/jp/hiroshiba/voicevoxcore/VoicevoxCoreInfo.java @@ -16,7 +16,11 @@ public class VoicevoxCoreInfo extends Dll { */ @Nonnull public static String getVersion() { - return rsGetVersion(); + String version = rsGetVersion(); + if (version == null) { + throw new NullPointerException("version"); + } + return version; } /** @@ -28,7 +32,11 @@ public static String getVersion() { public static SupportedDevices getSupportedDevices() { Gson gson = new Gson(); String supportedDevicesJson = rsGetSupportedDevicesJson(); - return gson.fromJson(supportedDevicesJson, SupportedDevices.class); + SupportedDevices supportedDevices = gson.fromJson(supportedDevicesJson, SupportedDevices.class); + if (supportedDevices == null) { + throw new NullPointerException("supported_devices"); + } + return supportedDevices; } @Nonnull diff --git a/crates/voicevox_core_java_api/lib/src/test/java/jp/hiroshiba/voicevoxcore/InfoTest.java b/crates/voicevox_core_java_api/lib/src/test/java/jp/hiroshiba/voicevoxcore/InfoTest.java index 7fe03c468..8d8580e71 100644 --- a/crates/voicevox_core_java_api/lib/src/test/java/jp/hiroshiba/voicevoxcore/InfoTest.java +++ b/crates/voicevox_core_java_api/lib/src/test/java/jp/hiroshiba/voicevoxcore/InfoTest.java @@ -8,7 +8,7 @@ import org.junit.jupiter.api.Test; -class MetaTest { +class InfoTest { @Test void checkVersion() { assertNotNull(VoicevoxCoreInfo.getVersion()); 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 efa91eed8..514cf463f 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 @@ -8,6 +8,7 @@ import static org.junit.jupiter.api.Assertions.assertTrue; import java.util.List; + import jp.hiroshiba.voicevoxcore.exceptions.InferenceFailedException; import jp.hiroshiba.voicevoxcore.exceptions.InvalidModelDataException; import org.junit.jupiter.api.Test; @@ -18,6 +19,15 @@ 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 accentPhrases, List otherAccentPhrases, @@ -40,9 +50,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)); } @@ -63,28 +81,23 @@ void checkAccentPhrases() throws InferenceFailedException, InvalidModelDataExcep OpenJtalk openJtalk = loadOpenJtalk(); Synthesizer synthesizer = Synthesizer.builder(openJtalk).build(); synthesizer.loadVoiceModel(model); - List accentPhrases = - synthesizer.createAccentPhrases("こんにちは", model.metas[0].styles[0].id); - List accentPhrases2 = - synthesizer.replaceMoraPitch(accentPhrases, model.metas[1].styles[0].id); + List accentPhrases = synthesizer.createAccentPhrases("こんにちは", model.metas[0].styles[0].id); + List accentPhrases2 = synthesizer.replaceMoraPitch(accentPhrases, model.metas[1].styles[0].id); assertTrue( checkAllMoras( accentPhrases, accentPhrases2, (mora, otherMora) -> mora.pitch != otherMora.pitch)); - List accentPhrases3 = - synthesizer.replacePhonemeLength(accentPhrases, model.metas[1].styles[0].id); + List accentPhrases3 = synthesizer.replacePhonemeLength(accentPhrases, model.metas[1].styles[0].id); assertTrue( checkAllMoras( accentPhrases, accentPhrases3, (mora, otherMora) -> mora.vowelLength != otherMora.vowelLength)); - List accentPhrases4 = - synthesizer.replaceMoraData(accentPhrases, model.metas[1].styles[0].id); + List accentPhrases4 = synthesizer.replaceMoraData(accentPhrases, model.metas[1].styles[0].id); assertTrue( checkAllMoras( accentPhrases, accentPhrases4, - (mora, otherMora) -> - mora.pitch != otherMora.pitch && mora.vowelLength != otherMora.vowelLength)); + (mora, otherMora) -> mora.pitch != otherMora.pitch && mora.vowelLength != otherMora.vowelLength)); } @Test diff --git a/crates/voicevox_core_java_api/src/synthesizer.rs b/crates/voicevox_core_java_api/src/synthesizer.rs index 9307c11b7..35fb6b6d0 100644 --- a/crates/voicevox_core_java_api/src/synthesizer.rs +++ b/crates/voicevox_core_java_api/src/synthesizer.rs @@ -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>(&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>(&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>( diff --git a/crates/voicevox_core_python_api/python/voicevox_core/_rust.pyi b/crates/voicevox_core_python_api/python/voicevox_core/_rust.pyi index 62de7b0e6..034e66908 100644 --- a/crates/voicevox_core_python_api/python/voicevox_core/_rust.pyi +++ b/crates/voicevox_core_python_api/python/voicevox_core/_rust.pyi @@ -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: