diff --git a/src/main/scala/dev/capslock/voicevoxcore4s/Main.scala b/src/main/scala/dev/capslock/voicevoxcore4s/Main.scala index 3f5dc0c..0470f1d 100644 --- a/src/main/scala/dev/capslock/voicevoxcore4s/Main.scala +++ b/src/main/scala/dev/capslock/voicevoxcore4s/Main.scala @@ -4,51 +4,76 @@ import com.sun.jna.ptr.{PointerByReference, IntByReference, LongByReference} import java.io.FileOutputStream object Hello extends App { - /* Extract dictionary files from JAR into real file system */ + // Extract dictionary files from JAR into real file system. + // Files will go to temporary directory in OS. val dictionaryDirectory = Util.extractDictFiles() - Util.extractModels() - Util.extractAndLoadLibraries() + // Load required library + val searchPath = (os.pwd / "lib").toString + com.sun.jna.NativeLibrary.addSearchPath("voicevox_core", searchPath) + com.sun.jna.NativeLibrary.addSearchPath("onnxruntime", searchPath) + System.load((os.pwd / "lib" / "libonnxruntime.so.1.13.1").toString) + + // vocevox_core will be loaded automatically. val core = Core() println(core.voicevox_get_version()) + val initializeOptions = core.voicevox_make_default_initialize_options() initializeOptions.open_jtalk_dict_dir = dictionaryDirectory initializeOptions.acceleration_mode = Core.VoicevoxAccelerationMode.VOICEVOX_ACCELERATION_MODE_CPU.code println(initializeOptions) + val initialized = core.voicevox_initialize(initializeOptions) println(s"Hello, voicevoxcore4s! initialized? -> (${initialized})") + if (initialized == Core.VoicevoxResultCode.VOICEVOX_RESULT_OK.code) { - val loadResult = core.voicevox_load_model(2) // metan + val model_shikoku_metan = 2 + val loadResult = core.voicevox_load_model(model_shikoku_metan) println(s"model loaded: $loadResult") - val wl = new IntByReference() - val wav = new PointerByReference() + + // Generating voice. + // First, we should have two pointers: result length, result wav buffer. + val bytesLength = new IntByReference() + val wavBuffer = new PointerByReference() + + // Second, prepare TTS(talk to speech) options val ttsOpts = core.voicevox_make_default_tts_options() ttsOpts.kana = false + + // Run TTS. + // VOICEVOX will rewrite memory beyond pointer. val tts = core.voicevox_tts( "こんにちは、世界", - 2, + model_shikoku_metan, ttsOpts, - wl, - wav + bytesLength, // this will be modified + wavBuffer // this will be modified ) if (tts == Core.VoicevoxResultCode.VOICEVOX_RESULT_OK.code) { - println(s"length: ${wl.getValue()}") - val resultPtr = wav.getValue() - println("got pointer") - val resultArray = resultPtr.getByteArray(0, wl.getValue()) - println("got array") + // You can acquire data from pointer using getValue() + val length = bytesLength.getValue() + val resultPtr = wavBuffer.getValue() + val resultArray = resultPtr.getByteArray(0, bytesLength.getValue()) + println(s"length: $length bytes") + + // Write out buffer. val fs = new FileOutputStream("./result.wav") fs.write(resultArray) println("wrote array into file") fs.close() - println("closed file") + + // Release allocated memory. core.voicevox_wav_free(resultPtr) println("freed array") } else { println(s"tts failed: $tts") } + // When program exit, bury VOICEVOX instance. core.voicevox_finalize() + + // Delete dictionary directory extracted into temporary directory. + os.remove.all(os.Path(dictionaryDirectory)) } } diff --git a/src/main/scala/dev/capslock/voicevoxcore4s/Util.scala b/src/main/scala/dev/capslock/voicevoxcore4s/Util.scala index 0c1235b..77c2999 100644 --- a/src/main/scala/dev/capslock/voicevoxcore4s/Util.scala +++ b/src/main/scala/dev/capslock/voicevoxcore4s/Util.scala @@ -5,8 +5,6 @@ import java.lang.invoke.MethodHandles import Logger.logger object Util { - val jarPath = os.pwd - val libDir = jarPath / "voicevoxcore4s-libs" /** Extract dictionary files for VOICEVOX into temporary directory. * @@ -16,8 +14,9 @@ object Util { * Extracted dictionary directory */ def extractDictFiles(): String = { - val tmpdir = os.temp.dir(prefix = "voicevoxcore4s", deleteOnExit = true) - logger.debug(s"extracting dictionary files into $tmpdir...") + val temporalDirectory = + os.temp.dir(prefix = "voicevoxcore4s", deleteOnExit = true) + logger.debug(s"extracting dictionary files into $temporalDirectory...") val files = Seq( "char.bin", // "COPYING", // sbt-assemblyが自動的に名前を変更して不定になるので省略している。COPYING自体はJARに同梱される @@ -31,79 +30,13 @@ object Util { ) for { p <- files } { val stream = getClass.getResourceAsStream(s"/$p") - val tmpPath = tmpdir / p - if (!os.exists(tmpPath)) { - logger.debug(s"copying $p") - os.write(tmpPath, stream, createFolders = true) - } - } - logger.debug("finished extracting") - tmpdir.toString() - } - - /** Extract library files for VOICEVOX into current working directory. - * - * @return - * Library directory path - */ - def extractAndLoadLibraries(): Unit = { - import scala.sys.process._ - import scala.collection.JavaConverters._ - - System.setProperty("jna.tmpdir", libDir.toString()) - logger.debug(s"extracting libraries into $libDir...") - - logger.debug("extracting libonnx...") - val libonnx = - com.sun.jna.Native.extractFromResourcePath(s"/${BuildInfo.libonnxFile}") - logger.debug(s"extracted onnx runtime: $libonnx") - - val targetLibonnx = new File(libonnx.getParentFile(), BuildInfo.libonnxFile) - libonnx.renameTo(targetLibonnx) - logger.debug(s"renamed onnx runtime: -> $targetLibonnx") - - logger.debug("loading libonnx...") - System.load(targetLibonnx.getAbsolutePath()) - logger.debug("loaded libonnx.") - - logger.debug("extracting libcore...") - val libcore = - com.sun.jna.Native.extractFromResourcePath(s"/${BuildInfo.libcoreFile}") - logger.debug(s"extracted libcore: $libcore") - - val targetLibcore = new File(libcore.getParentFile(), BuildInfo.libcoreFile) - libcore.renameTo(targetLibcore) - logger.debug(s"renamed libcore: -> $targetLibcore") - - logger.debug("loading libcore...") - System.load(targetLibcore.getAbsolutePath()) - logger.debug("loaded libcore.") - - // val jarFile = new File(this.getClass.getProtectionDomain().getCodeSource().getLocation().getPath()) - // val jar = new JarFile(jarFile) - // for {e <- jar.entries().asIterator().asScala} { - // println(e.getName()) - // } - // println(jarFile) - } - - /** Extract model files included in VOICEVOX Core into current working - * directory. - */ - def extractModels(): Unit = { - // TODO: 自動化したい。modelディレクトリごとresourcesに格納できないだろうか - logger.debug(s"extracting core models into $libDir...") - val binaries = - (0 to 11) flatMap (n => Seq(s"d${n}.bin", s"pd${n}.bin", s"pi${n}.bin")) - val files = binaries ++ Seq("metas.json") - for { p <- files } { - val stream = getClass.getResourceAsStream(s"/$p") - val tmpPath = libDir / "model" / p + val tmpPath = temporalDirectory / p if (!os.exists(tmpPath)) { logger.debug(s"copying $p") os.write(tmpPath, stream, createFolders = true) } } logger.debug("finished extracting") + temporalDirectory.toString() } } diff --git a/src/test/scala/dev/capslock/voicevoxcore4s/CoreSpec.scala b/src/test/scala/dev/capslock/voicevoxcore4s/CoreSpec.scala index 86ac0b6..075ab4e 100644 --- a/src/test/scala/dev/capslock/voicevoxcore4s/CoreSpec.scala +++ b/src/test/scala/dev/capslock/voicevoxcore4s/CoreSpec.scala @@ -5,14 +5,6 @@ import org.scalatest.matchers.should.Matchers import com.sun.jna.ptr.{PointerByReference, IntByReference} -/** 確実に一度だけライブラリをロードするためにObjectを使っている - */ -object TestRunner { - val dictionaryDirectory = Util.extractDictFiles() - Util.extractModels() - Util.extractAndLoadLibraries() -} - class CoreSpec extends AnyFlatSpec with Matchers { // "Core" should "work at voicevox_tts" in { // val core = Core()