From 3a074d64a2ee0adc0a2bf28db8fbac589959127b Mon Sep 17 00:00:00 2001 From: Henry Coles Date: Tue, 30 Sep 2025 07:36:13 +0100 Subject: [PATCH 1/4] provide byte arrays to runtime --- .../main/java/org/pitest/voices/Chorus.java | 27 +++- .../org/pitest/voices/ClassPathModel.java | 53 +++++++ .../java/org/pitest/voices/FileModel.java | 83 +++++++++++ .../main/java/org/pitest/voices/Model.java | 137 ++++++------------ .../main/java/org/pitest/voices/Models.java | 13 +- .../org/pitest/voices/NonEnglishModels.java | 8 +- .../main/java/org/pitest/voices/Pause.java | 9 ++ .../main/java/org/pitest/voices/UsModels.java | 6 +- .../java/org/pitest/g2p/util/Resource.java | 14 +- .../voices/openvoice/OpenVoiceSupplier.java | 5 +- 10 files changed, 232 insertions(+), 123 deletions(-) create mode 100644 chorus/src/main/java/org/pitest/voices/ClassPathModel.java create mode 100644 chorus/src/main/java/org/pitest/voices/FileModel.java diff --git a/chorus/src/main/java/org/pitest/voices/Chorus.java b/chorus/src/main/java/org/pitest/voices/Chorus.java index d17b2ef..f27a302 100644 --- a/chorus/src/main/java/org/pitest/voices/Chorus.java +++ b/chorus/src/main/java/org/pitest/voices/Chorus.java @@ -27,6 +27,9 @@ public class Chorus implements AutoCloseable { // lazily initialised private G2PModel g2p; + // lazily initialised, held only to allow closing + private OrtSession.SessionOptions opts; + public Chorus(Dictionary dictionary) { this(new ChorusConfig(dictionary)); } @@ -43,8 +46,8 @@ public Voice voice(Model model) { phonemizer, conf.trace(), session, - model.defaultPauses(), - model.defaultParams(), + Pause.defaultPauses(), + ModelParameters.defaultParams(), model.defaultGain()); } @@ -59,17 +62,17 @@ private G2PModel g2p() { private VoiceSession loadVoice(Model model) { try { Files.createDirectories(conf.base()); - return loadPiperModel(model, model.resolve(conf.base())); + return loadPiperModel(model); } catch (IOException e) { throw new RuntimeException(e); } } - private VoiceSession loadPiperModel(Model model, Path onnx) { + private VoiceSession loadPiperModel(Model model) { OrtEnvironment env = OrtEnvironment.getEnvironment(); try { var options = configureSession(); - var session = env.createSession(onnx.toString(), options); + var session = env.createSession(model.byteBuffer(conf.base()), options); return new VoiceSession(env, model.resolveConfig(conf.base()), session); } catch (IOException | OrtException e) { throw new RuntimeException(e); @@ -77,9 +80,9 @@ private VoiceSession loadPiperModel(Model model, Path onnx) { } private OrtSession.SessionOptions configureSession() { - OrtSession.SessionOptions options = new OrtSession.SessionOptions(); - conf.cudaOptions().accept(options); - return options; + OrtSession.SessionOptions options = new OrtSession.SessionOptions(); + conf.cudaOptions().accept(options); + return options; } @Override @@ -100,6 +103,14 @@ public void close() { } } + if (opts != null) { + try { + opts.close(); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + } } diff --git a/chorus/src/main/java/org/pitest/voices/ClassPathModel.java b/chorus/src/main/java/org/pitest/voices/ClassPathModel.java new file mode 100644 index 0000000..3da67c6 --- /dev/null +++ b/chorus/src/main/java/org/pitest/voices/ClassPathModel.java @@ -0,0 +1,53 @@ +package org.pitest.voices; + +import org.pitest.g2p.core.Language; +import org.pitest.g2p.util.Resource; + +import java.io.IOException; +import java.nio.file.Path; + +public class ClassPathModel implements Model { + + private final String resource; + private final Language lang; + private final float gain; + + public ClassPathModel(String resource, Language lang, float gain) { + this.resource = resource; + this.lang = lang; + this.gain = gain; + } + + @Override + public byte[] byteBuffer(Path cacheBase) { + return Resource.readAsBytes(resource); + } + + @Override + public ModelConfig resolveConfig(Path cacheBase) throws IOException { + try (var is = Resource.readAsStream(resource)) { + return ModelConfig.fromJson(is); + } + } + + @Override + public String id() { + return resource; + } + + @Override + public Language language() { + return lang; + } + + @Override + public Model withLanguage(Language lang) { + return new ClassPathModel(resource, lang, gain); + } + + @Override + public float defaultGain() { + return gain; + } + +} diff --git a/chorus/src/main/java/org/pitest/voices/FileModel.java b/chorus/src/main/java/org/pitest/voices/FileModel.java new file mode 100644 index 0000000..b4cc148 --- /dev/null +++ b/chorus/src/main/java/org/pitest/voices/FileModel.java @@ -0,0 +1,83 @@ +package org.pitest.voices; + +import org.pitest.g2p.core.Language; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.StandardOpenOption; + + +public class FileModel implements Model { + + private final String name; + private final String location; + private final Language lang; + + private final ModelFetcher resolver; + + private final float gain; + + public FileModel(String name, + String location, + Language lang, + ModelFetcher resolver, + float gain) { + this.name = name; + this.location = location; + this.lang = lang; + this.resolver = resolver; + this.gain = gain; + } + + @Override + public String id() { + return name; + } + + @Override + public Language language() { + return lang; + } + + @Override + public Model withLanguage(Language lang) { + return new FileModel(name, location, lang, resolver, gain); + } + + @Override + public byte[] byteBuffer(Path cacheBase) throws IOException { + return Files.readAllBytes(resolveFiles(cacheBase).resolve(onnx())); + } + + @Override + public ModelConfig resolveConfig(Path cacheBase) throws IOException { + Path json = resolveFiles(cacheBase).resolve(onnx() + ".json"); + try(var in = Files.newInputStream(json, StandardOpenOption.READ)) { + return ModelConfig.fromJson(in); + } + } + + @Override + public float defaultGain() { + return gain; + } + + String onnx() { + return name + ".onnx"; + } + + private Path resolveFiles(Path cacheBase) throws IOException { + Path location = cacheBase.resolve(this.location); + Path onnx = location.resolve(onnx()); + if (!Files.exists(onnx)) { + Path tempLocation = fetch(); + Files.move(tempLocation, location); + } + return location; + } + + private Path fetch() throws IOException { + return resolver.fetch().resolve(location); + } +} diff --git a/chorus/src/main/java/org/pitest/voices/Model.java b/chorus/src/main/java/org/pitest/voices/Model.java index 5e568bd..e798b5e 100644 --- a/chorus/src/main/java/org/pitest/voices/Model.java +++ b/chorus/src/main/java/org/pitest/voices/Model.java @@ -3,97 +3,54 @@ import org.pitest.g2p.core.Language; import java.io.IOException; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.StandardOpenOption; -import java.util.List; - -public class Model { - - private final String name; - private final String location; - private final Language lang; - - private final ModelFetcher resolver; - - private final List pauses; - private final float gain; - - private final ModelParameters params; - - public Model(String name, - String location, - Language lang, - ModelFetcher resolver, - List pauses, - float gain, - ModelParameters params) { - this.name = name; - this.location = location; - this.lang = lang; - this.resolver = resolver; - this.pauses = pauses; - this.gain = gain; - this.params = params; - } - - public String name() { - return name; - } - - public Language language() { - return lang; - } - - public Model withLanguage(Language lang) { - return new Model(name, location, lang, resolver, pauses, gain, params); - } - public Path resolve(Path cacheBase) throws IOException { - return resolveFiles(cacheBase).resolve(onnx()); - } - - public ModelConfig resolveConfig(Path cacheBase) throws IOException { - Path json = resolveFiles(cacheBase).resolve(onnx() + ".json"); - try(var in = Files.newInputStream(json, StandardOpenOption.READ)) { - return ModelConfig.fromJson(in); - } - } - - public Path fetch() throws IOException { - return resolver.fetch().resolve(location); - } - - public List defaultPauses() { - return pauses; - } - - String onnx() { - return name + ".onnx"; - } - - - String id() { - return name; - } - - public float defaultGain() { - return gain; - } - - public ModelParameters defaultParams() { - return params; - } - - private Path resolveFiles(Path cacheBase) throws IOException { - Path location = cacheBase.resolve(this.location); - Path onnx = location.resolve(onnx()); - if (!Files.exists(onnx)) { - Path tempLocation = fetch(); - Files.move(tempLocation, location); - } - return location; - } +import java.nio.file.Path; +/** + * Piper tts model + */ +public interface Model { + + /** + * Unique id + * @return an id + */ + String id(); + + /** + * Language model speaks + * @return A language instance + */ + Language language(); + + /** + * Creates a variation of the model with a different language + * @param lang Language to use + * @return a new model instance + */ + Model withLanguage(Language lang); + + /** + * The model in bytes + * @param cacheBase Directory currently used by Voices for any caches + * @return Model as bytes + * @throws IOException in event of error + */ + byte[] byteBuffer(Path cacheBase) throws IOException; + + /** + * The model config + * @param cacheBase Directory currently used by Voices for any caches + * @return Model config + * @throws IOException in event of error + */ + ModelConfig resolveConfig(Path cacheBase) throws IOException; + + /** + * Default gain to use with this model. Useful as some models + * appear much louder or quieter than others + * @return float representing the default gain + */ + float defaultGain(); } diff --git a/chorus/src/main/java/org/pitest/voices/Models.java b/chorus/src/main/java/org/pitest/voices/Models.java index 2c8ae28..e5b5e8e 100644 --- a/chorus/src/main/java/org/pitest/voices/Models.java +++ b/chorus/src/main/java/org/pitest/voices/Models.java @@ -3,7 +3,6 @@ import org.pitest.g2p.core.Language; import java.net.URL; -import java.util.List; public class Models { @@ -33,19 +32,11 @@ public static Model northernEnglishMale() { } private static Model sherpaModel(String name, float gain) { - return new Model(name, + return new FileModel(name, "vits-piper-" + name, Language.en_GB, new ModelDownloader(url("https://github.com/k2-fsa/sherpa-onnx/releases/download/tts-models/vits-piper-" + name + ".tar.bz2")), - defaultPauses(), gain, ModelParameters.defaultParams() - ); - } - - public static List defaultPauses() { - return List.of(new Pause("—", 3), - new Pause("–", 2), - new Pause(":", 2) - ); + gain); } static URL url(String url) { diff --git a/chorus/src/main/java/org/pitest/voices/NonEnglishModels.java b/chorus/src/main/java/org/pitest/voices/NonEnglishModels.java index 633ea84..f9697e8 100644 --- a/chorus/src/main/java/org/pitest/voices/NonEnglishModels.java +++ b/chorus/src/main/java/org/pitest/voices/NonEnglishModels.java @@ -2,7 +2,6 @@ import org.pitest.g2p.core.Language; -import static org.pitest.voices.Models.defaultPauses; import static org.pitest.voices.Models.url; public class NonEnglishModels { @@ -16,11 +15,10 @@ public static Model nlNLRonnie() { } private static Model sherpaModel(String name, Language lang, float gain) { - return new Model(name, + return new FileModel(name, "vits-piper-" + name, lang, - new ModelDownloader(url("https://github.com/k2-fsa/sherpa-onnx/releases/download/tts-models/vits-piper-" + name + ".tar.bz2")), - defaultPauses(), gain, ModelParameters.defaultParams() - ); + new ModelDownloader(url("https://github.com/k2-fsa/sherpa-onnx/releases/download/tts-models/vits-piper-" + name + ".tar.bz2")) + , gain ); } } diff --git a/chorus/src/main/java/org/pitest/voices/Pause.java b/chorus/src/main/java/org/pitest/voices/Pause.java index c950edb..7715976 100644 --- a/chorus/src/main/java/org/pitest/voices/Pause.java +++ b/chorus/src/main/java/org/pitest/voices/Pause.java @@ -1,9 +1,18 @@ package org.pitest.voices; +import java.util.List; + public record Pause(String symbol, int beats) { public boolean matches(String symbol) { return this.symbol.equals(symbol); } + public static List defaultPauses() { + return List.of(new Pause("—", 3), + new Pause("–", 2), + new Pause(":", 2) + ); + } + } diff --git a/chorus/src/main/java/org/pitest/voices/UsModels.java b/chorus/src/main/java/org/pitest/voices/UsModels.java index f00a308..d3c54da 100644 --- a/chorus/src/main/java/org/pitest/voices/UsModels.java +++ b/chorus/src/main/java/org/pitest/voices/UsModels.java @@ -2,7 +2,6 @@ import org.pitest.g2p.core.Language; -import static org.pitest.voices.Models.defaultPauses; import static org.pitest.voices.Models.url; public class UsModels { @@ -56,11 +55,10 @@ public static Model samMedium() { } private static Model sherpaModel(String name, float gain) { - return new Model(name, + return new FileModel(name, "vits-piper-" + name, Language.en_US, new ModelDownloader(url("https://github.com/k2-fsa/sherpa-onnx/releases/download/tts-models/vits-piper-" + name + ".tar.bz2")), - defaultPauses(), gain, ModelParameters.defaultParams() - ); + gain); } } diff --git a/g2p/src/main/java/org/pitest/g2p/util/Resource.java b/g2p/src/main/java/org/pitest/g2p/util/Resource.java index d0d18ad..207df79 100644 --- a/g2p/src/main/java/org/pitest/g2p/util/Resource.java +++ b/g2p/src/main/java/org/pitest/g2p/util/Resource.java @@ -1,7 +1,5 @@ package org.pitest.g2p.util; -import org.pitest.g2p.core.Dictionary; - import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; @@ -13,8 +11,16 @@ public class Resource { + public static byte[] readAsBytes(String resource) { + try (InputStream is = readAsStream(resource)) { + return is.readAllBytes(); + } catch (IOException e) { + throw new UncheckedIOException(e); + } + } + public static List read(String resource) { - try (InputStream is = Dictionary.class.getResourceAsStream(resource); + try (InputStream is = readAsStream(resource); InputStreamReader inputStreamReader = new InputStreamReader(is, StandardCharsets.UTF_8); BufferedReader bufferedReader = new BufferedReader(inputStreamReader)) { return bufferedReader.lines() @@ -27,7 +33,7 @@ public static List read(String resource) { public static InputStream readAsStream(String resource) { var stream = Resource.class.getResourceAsStream(resource); if (stream == null) { - throw new RuntimeException("Could not find resource " + resource); + throw new IllegalStateException("Could not find resource " + resource); } return stream; } diff --git a/openvoice/src/main/java/org/pitest/voices/openvoice/OpenVoiceSupplier.java b/openvoice/src/main/java/org/pitest/voices/openvoice/OpenVoiceSupplier.java index d63a68b..707db26 100644 --- a/openvoice/src/main/java/org/pitest/voices/openvoice/OpenVoiceSupplier.java +++ b/openvoice/src/main/java/org/pitest/voices/openvoice/OpenVoiceSupplier.java @@ -11,8 +11,10 @@ import java.util.function.Supplier; public class OpenVoiceSupplier implements G2PModelSupplier { + private final ONNXModel model; + public OpenVoiceSupplier() { this(new ONNXModel(URLModelFetcher.fromString("https://huggingface.co/OpenVoiceOS/g2p-mbyt5-12l-ipa-childes-espeak-onnx/resolve/main/fdemelo_g2p-mbyt5-12l-ipa-childes-espeak.onnx"), "fdemelo_g2p-mbyt5-12l-ipa-childes-espeak")); @@ -26,7 +28,8 @@ public OpenVoiceSupplier(ONNXModel model) { public G2PModel create(Supplier options, Dictionary dictionary, OrtEnvironment env, Path base) { try { Path onnx = model.resolve(base); - var session = env.createSession(onnx.toString(), options.get()); + var opts = options.get(); + var session = env.createSession(onnx.toString(), opts); return new OpenVoiceModel(dictionary, session, env); } catch (Exception e) { throw new RuntimeException(e); From 2784fc6e946ce2d71078b7dec61dff9e9d96f134 Mon Sep 17 00:00:00 2001 From: Henry Coles Date: Thu, 2 Oct 2025 17:57:07 +0100 Subject: [PATCH 2/4] load models from classpath --- alba/pom.xml | 67 +++++++++++++ .../java/org/pitest/voices/alba/Alba.java | 14 +++ .../java/org/pitest/voices/alba/AlbaTest.java | 31 ++++++ bryce/pom.xml | 67 +++++++++++++ .../java/org/pitest/voices/bryce/Bryce.java | 14 +++ .../org/pitest/voices/bryce/BryceTest.java | 30 ++++++ chorus/pom.xml | 28 ++---- .../main/java/org/pitest/voices/Chorus.java | 7 +- .../java/org/pitest/voices/ChorusConfig.java | 12 +-- .../org/pitest/voices/G2PModelSupplier.java | 4 +- .../java/org/pitest/voices/PiperVoice.java | 5 +- .../java/org/pitest/voices/ChorusTest.java | 17 ++-- .../org/pitest/voices/ModelConfigTest.java | 2 - .../java/org/pitest/voices/util/Fetch.java | 9 +- core/pom.xml | 66 +++++++++++++ .../org/pitest/voices/ClassPathModel.java | 5 +- .../main/java/org/pitest/voices/Model.java | 3 - .../java/org/pitest/voices/ModelConfig.java | 0 .../java/org/pitest/voices}/Resource.java | 2 +- cori/pom.xml | 67 +++++++++++++ .../java/org/pitest/voices/cori/Cori.java | 14 +++ .../java/org/pitest/voices/cori/CoriTest.java | 30 ++++++ en_uk/pom.xml | 18 +++- .../org/pitest/voices/uk/EnUkDictionary.java | 4 +- .../dictionary/DictionaryGenerator.java | 4 +- .../pitest/voices/dictionary/Homographs.java | 12 +-- .../org/pitest/voices/uk/TryItOutTest.java | 37 +++----- en_us/pom.xml | 13 +++ .../org/pitest/voices/us/EnUsDictionary.java | 5 +- .../java/org/pitest/voices/us/ConvertCMU.java | 2 +- .../org/pitest/voices/us/TryItOutTest.java | 18 ++-- .../java/org/pitest/g2p/core/NoModel.java | 19 ---- .../pitest/{g2p/core => voices}/Language.java | 2 +- .../{ => voices}/g2p/core/Dictionary.java | 5 +- .../{ => voices}/g2p/core/EnglishModel.java | 13 +-- .../{ => voices}/g2p/core/Expansion.java | 2 +- .../{ => voices}/g2p/core/G2PModel.java | 7 +- .../g2p/core/PiperPhonemizer.java | 13 +-- .../g2p/core/WordToSyllables.java | 2 +- .../g2p/core/expansions/NumberExpander.java | 4 +- .../{ => voices}/g2p/core/pos/POSToken.java | 2 +- .../pitest/{ => voices}/g2p/core/pos/Pos.java | 2 +- .../g2p/core/pos/SimplePOSTagger.java | 4 +- .../g2p/core/syllables/RulesSyllabiliser.java | 8 +- .../g2p/core/tracing/LoggingTrace.java | 4 +- .../{ => voices}/g2p/core/tracing/Trace.java | 4 +- .../pitest/{ => voices}/g2p/util/English.java | 2 +- .../org/pitest/voices/g2p/util/Resource.java | 32 +++++++ .../g2p/core/EnglishModelRulesTest.java | 9 +- .../g2p/core/PiperPhonemizerTest.java | 7 +- .../core/expansions/NumberExpanderTest.java | 2 +- .../g2p/core/pos/SimplePOSTaggerTest.java | 4 +- .../core/syllables/RulesSyllabiliserTest.java | 2 +- model_downloader/pom.xml | 94 ++++++++++++++++++ .../pitest/voices/download}/FileModel.java | 6 +- .../voices/download}/ModelDownloader.java | 2 +- .../pitest/voices/download}/ModelFetcher.java | 2 +- .../org/pitest/voices/download}/Models.java | 6 +- .../voices/download}/NonEnglishModels.java | 8 +- .../voices/download}/URLModelFetcher.java | 2 +- .../org/pitest/voices/download}/UsModels.java | 8 +- .../pitest/voices/download/ModelsTest.java | 61 ++++++++++++ .../voices/download/NonEnglishModelsTest.java | 37 ++++++++ .../pitest/voices/download/UsModelsTest.java | 95 +++++++++++++++++++ openvoice/pom.xml | 15 +-- .../pitest/voices/openvoice/ONNXModel.java | 28 ------ .../voices/openvoice/OpenVoiceModel.java | 10 +- .../voices/openvoice/OpenVoiceSupplier.java | 22 +---- .../voices/openvoice/OpenVoiceModelTest.java | 10 +- .../pitest/voices/openvoice/TryItOutTest.java | 35 ++----- pom.xml | 8 ++ 71 files changed, 927 insertions(+), 278 deletions(-) create mode 100644 alba/pom.xml create mode 100644 alba/src/main/java/org/pitest/voices/alba/Alba.java create mode 100644 alba/src/test/java/org/pitest/voices/alba/AlbaTest.java create mode 100644 bryce/pom.xml create mode 100644 bryce/src/main/java/org/pitest/voices/bryce/Bryce.java create mode 100644 bryce/src/test/java/org/pitest/voices/bryce/BryceTest.java create mode 100644 core/pom.xml rename {chorus => core}/src/main/java/org/pitest/voices/ClassPathModel.java (88%) rename {chorus => core}/src/main/java/org/pitest/voices/Model.java (96%) rename {chorus => core}/src/main/java/org/pitest/voices/ModelConfig.java (100%) rename {g2p/src/main/java/org/pitest/g2p/util => core/src/main/java/org/pitest/voices}/Resource.java (97%) create mode 100644 cori/pom.xml create mode 100644 cori/src/main/java/org/pitest/voices/cori/Cori.java create mode 100644 cori/src/test/java/org/pitest/voices/cori/CoriTest.java delete mode 100644 g2p/src/main/java/org/pitest/g2p/core/NoModel.java rename g2p/src/main/java/org/pitest/{g2p/core => voices}/Language.java (97%) rename g2p/src/main/java/org/pitest/{ => voices}/g2p/core/Dictionary.java (96%) rename g2p/src/main/java/org/pitest/{ => voices}/g2p/core/EnglishModel.java (99%) rename g2p/src/main/java/org/pitest/{ => voices}/g2p/core/Expansion.java (64%) rename g2p/src/main/java/org/pitest/{ => voices}/g2p/core/G2PModel.java (73%) rename g2p/src/main/java/org/pitest/{ => voices}/g2p/core/PiperPhonemizer.java (92%) rename g2p/src/main/java/org/pitest/{ => voices}/g2p/core/WordToSyllables.java (74%) rename g2p/src/main/java/org/pitest/{ => voices}/g2p/core/expansions/NumberExpander.java (99%) rename g2p/src/main/java/org/pitest/{ => voices}/g2p/core/pos/POSToken.java (55%) rename g2p/src/main/java/org/pitest/{ => voices}/g2p/core/pos/Pos.java (98%) rename g2p/src/main/java/org/pitest/{ => voices}/g2p/core/pos/SimplePOSTagger.java (96%) rename g2p/src/main/java/org/pitest/{ => voices}/g2p/core/syllables/RulesSyllabiliser.java (96%) rename g2p/src/main/java/org/pitest/{ => voices}/g2p/core/tracing/LoggingTrace.java (94%) rename g2p/src/main/java/org/pitest/{ => voices}/g2p/core/tracing/Trace.java (93%) rename g2p/src/main/java/org/pitest/{ => voices}/g2p/util/English.java (89%) create mode 100644 g2p/src/main/java/org/pitest/voices/g2p/util/Resource.java rename g2p/src/test/java/org/pitest/{ => voices}/g2p/core/EnglishModelRulesTest.java (95%) rename g2p/src/test/java/org/pitest/{ => voices}/g2p/core/PiperPhonemizerTest.java (95%) rename g2p/src/test/java/org/pitest/{ => voices}/g2p/core/expansions/NumberExpanderTest.java (99%) rename g2p/src/test/java/org/pitest/{ => voices}/g2p/core/pos/SimplePOSTaggerTest.java (95%) rename g2p/src/test/java/org/pitest/{ => voices}/g2p/core/syllables/RulesSyllabiliserTest.java (97%) create mode 100644 model_downloader/pom.xml rename {chorus/src/main/java/org/pitest/voices => model_downloader/src/main/java/org/pitest/voices/download}/FileModel.java (93%) rename {chorus/src/main/java/org/pitest/voices => model_downloader/src/main/java/org/pitest/voices/download}/ModelDownloader.java (98%) rename {chorus/src/main/java/org/pitest/voices => model_downloader/src/main/java/org/pitest/voices/download}/ModelFetcher.java (78%) rename {chorus/src/main/java/org/pitest/voices => model_downloader/src/main/java/org/pitest/voices/download}/Models.java (92%) rename {chorus/src/main/java/org/pitest/voices => model_downloader/src/main/java/org/pitest/voices/download}/NonEnglishModels.java (79%) rename {chorus/src/main/java/org/pitest/voices => model_downloader/src/main/java/org/pitest/voices/download}/URLModelFetcher.java (97%) rename {chorus/src/main/java/org/pitest/voices => model_downloader/src/main/java/org/pitest/voices/download}/UsModels.java (91%) create mode 100644 model_downloader/src/test/java/org/pitest/voices/download/ModelsTest.java create mode 100644 model_downloader/src/test/java/org/pitest/voices/download/NonEnglishModelsTest.java create mode 100644 model_downloader/src/test/java/org/pitest/voices/download/UsModelsTest.java delete mode 100644 openvoice/src/main/java/org/pitest/voices/openvoice/ONNXModel.java diff --git a/alba/pom.xml b/alba/pom.xml new file mode 100644 index 0000000..1e55ec7 --- /dev/null +++ b/alba/pom.xml @@ -0,0 +1,67 @@ + + + 4.0.0 + + + org.pitest.voices + voices-parent + ${revision} + + + alba + alba + + jar + + + + Apache 2 + https://www.apache.org/licenses/LICENSE-2.0.txt + repo + + + + + + + org.pitest.voices + core + ${project.parent.version} + + + org.pitest.voices + alba-model + ${models.version} + + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + + + org.apache.maven.plugins + maven-surefire-plugin + + + org.apache.maven.plugins + maven-jar-plugin + + + org.apache.maven.plugins + maven-source-plugin + + + org.apache.maven.plugins + maven-javadoc-plugin + + + + + + + diff --git a/alba/src/main/java/org/pitest/voices/alba/Alba.java b/alba/src/main/java/org/pitest/voices/alba/Alba.java new file mode 100644 index 0000000..3ce6bb4 --- /dev/null +++ b/alba/src/main/java/org/pitest/voices/alba/Alba.java @@ -0,0 +1,14 @@ +package org.pitest.voices.alba; + +import org.pitest.voices.ClassPathModel; +import org.pitest.voices.Language; +import org.pitest.voices.Model; + +public class Alba { + + public static Model albaMedium() { + return new ClassPathModel("/models/vits-piper-en_GB-alba-medium/en_GB-alba-medium.onnx", + Language.en_GB, + 2.0f); + } +} diff --git a/alba/src/test/java/org/pitest/voices/alba/AlbaTest.java b/alba/src/test/java/org/pitest/voices/alba/AlbaTest.java new file mode 100644 index 0000000..c452846 --- /dev/null +++ b/alba/src/test/java/org/pitest/voices/alba/AlbaTest.java @@ -0,0 +1,31 @@ +package org.pitest.voices.alba; + +import org.junit.jupiter.api.Test; +import org.pitest.voices.Language; +import org.pitest.voices.Model; + +import java.io.IOException; +import java.nio.file.Path; + +import static org.assertj.core.api.Assertions.assertThat; + +class AlbaTest { + + Model underTest = Alba.albaMedium(); + + @Test + void speaksProperEnglish() { + assertThat(underTest.language()).isEqualTo(Language.en_GB); + } + + + @Test + void loadsResources() throws IOException { + assertThat(underTest.byteBuffer(unused())).hasSizeGreaterThan(50000); + assertThat(underTest.resolveConfig(unused()).sampleRate()).isEqualTo(22050L); + } + + private Path unused() { + return null; + } +} \ No newline at end of file diff --git a/bryce/pom.xml b/bryce/pom.xml new file mode 100644 index 0000000..e476c09 --- /dev/null +++ b/bryce/pom.xml @@ -0,0 +1,67 @@ + + + 4.0.0 + + + org.pitest.voices + voices-parent + ${revision} + + + bryce + bryce + + jar + + + + Apache 2 + https://www.apache.org/licenses/LICENSE-2.0.txt + repo + + + + + + + org.pitest.voices + core + ${project.parent.version} + + + org.pitest.voices + bryce-model + ${models.version} + + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + + + org.apache.maven.plugins + maven-surefire-plugin + + + org.apache.maven.plugins + maven-jar-plugin + + + org.apache.maven.plugins + maven-source-plugin + + + org.apache.maven.plugins + maven-javadoc-plugin + + + + + + + diff --git a/bryce/src/main/java/org/pitest/voices/bryce/Bryce.java b/bryce/src/main/java/org/pitest/voices/bryce/Bryce.java new file mode 100644 index 0000000..b327b7b --- /dev/null +++ b/bryce/src/main/java/org/pitest/voices/bryce/Bryce.java @@ -0,0 +1,14 @@ +package org.pitest.voices.bryce; + +import org.pitest.voices.ClassPathModel; +import org.pitest.voices.Language; +import org.pitest.voices.Model; + +public class Bryce { + + public static Model bryceMedium() { + return new ClassPathModel("/models/vits-piper-en_US-bryce-medium/en_US-bryce-medium.onnx", + Language.en_US, + 1.0f); + } +} diff --git a/bryce/src/test/java/org/pitest/voices/bryce/BryceTest.java b/bryce/src/test/java/org/pitest/voices/bryce/BryceTest.java new file mode 100644 index 0000000..d92e0c5 --- /dev/null +++ b/bryce/src/test/java/org/pitest/voices/bryce/BryceTest.java @@ -0,0 +1,30 @@ +package org.pitest.voices.bryce; + +import org.junit.jupiter.api.Test; +import org.pitest.voices.Language; +import org.pitest.voices.Model; + +import java.io.IOException; +import java.nio.file.Path; + +import static org.assertj.core.api.Assertions.assertThat; + +class BryceTest { + + Model underTest = Bryce.bryceMedium(); + + @Test + void speaksUSEnglish() { + assertThat(underTest.language()).isEqualTo(Language.en_US); + } + + @Test + void loadsResources() throws IOException { + assertThat(underTest.byteBuffer(unused())).hasSizeGreaterThan(50000); + assertThat(underTest.resolveConfig(unused()).sampleRate()).isEqualTo(22050L); + } + + private Path unused() { + return null; + } +} \ No newline at end of file diff --git a/chorus/pom.xml b/chorus/pom.xml index f8a89fc..eb251b4 100644 --- a/chorus/pom.xml +++ b/chorus/pom.xml @@ -26,7 +26,7 @@ org.pitest.voices - g2p + core ${project.parent.version} @@ -36,12 +36,6 @@ 2.5.5 - - com.google.code.gson - gson - 2.13.2 - - com.microsoft.onnxruntime onnxruntime @@ -56,25 +50,19 @@ 2.0.16 - - - me.tongfei - progressbar - 0.10.1 - - - org.apache.commons - commons-compress - 1.28.0 - - - org.apache.commons commons-text 1.14.0 test + + org.pitest.voices + alba + ${project.parent.version} + test + + diff --git a/chorus/src/main/java/org/pitest/voices/Chorus.java b/chorus/src/main/java/org/pitest/voices/Chorus.java index f27a302..5a0008f 100644 --- a/chorus/src/main/java/org/pitest/voices/Chorus.java +++ b/chorus/src/main/java/org/pitest/voices/Chorus.java @@ -3,13 +3,12 @@ import ai.onnxruntime.OrtEnvironment; import ai.onnxruntime.OrtException; import ai.onnxruntime.OrtSession; -import org.pitest.g2p.core.Dictionary; -import org.pitest.g2p.core.G2PModel; -import org.pitest.g2p.core.PiperPhonemizer; +import org.pitest.voices.g2p.core.Dictionary; +import org.pitest.voices.g2p.core.G2PModel; +import org.pitest.voices.g2p.core.PiperPhonemizer; import java.io.IOException; import java.nio.file.Files; -import java.nio.file.Path; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; diff --git a/chorus/src/main/java/org/pitest/voices/ChorusConfig.java b/chorus/src/main/java/org/pitest/voices/ChorusConfig.java index 07a1db7..b74f33a 100644 --- a/chorus/src/main/java/org/pitest/voices/ChorusConfig.java +++ b/chorus/src/main/java/org/pitest/voices/ChorusConfig.java @@ -2,12 +2,12 @@ import ai.onnxruntime.OrtException; import ai.onnxruntime.OrtSession; -import org.pitest.g2p.core.EnglishModel; -import org.pitest.g2p.core.Dictionary; -import org.pitest.g2p.core.Expansion; -import org.pitest.g2p.core.expansions.NumberExpander; -import org.pitest.g2p.core.syllables.RulesSyllabiliser; -import org.pitest.g2p.core.tracing.Trace; +import org.pitest.voices.g2p.core.EnglishModel; +import org.pitest.voices.g2p.core.Dictionary; +import org.pitest.voices.g2p.core.Expansion; +import org.pitest.voices.g2p.core.expansions.NumberExpander; +import org.pitest.voices.g2p.core.syllables.RulesSyllabiliser; +import org.pitest.voices.g2p.core.tracing.Trace; import java.nio.file.Path; import java.util.List; diff --git a/chorus/src/main/java/org/pitest/voices/G2PModelSupplier.java b/chorus/src/main/java/org/pitest/voices/G2PModelSupplier.java index 5d9d978..be48afb 100644 --- a/chorus/src/main/java/org/pitest/voices/G2PModelSupplier.java +++ b/chorus/src/main/java/org/pitest/voices/G2PModelSupplier.java @@ -2,8 +2,8 @@ import ai.onnxruntime.OrtEnvironment; import ai.onnxruntime.OrtSession; -import org.pitest.g2p.core.Dictionary; -import org.pitest.g2p.core.G2PModel; +import org.pitest.voices.g2p.core.Dictionary; +import org.pitest.voices.g2p.core.G2PModel; import java.nio.file.Path; import java.util.function.Supplier; diff --git a/chorus/src/main/java/org/pitest/voices/PiperVoice.java b/chorus/src/main/java/org/pitest/voices/PiperVoice.java index 2b52103..79d0eb8 100644 --- a/chorus/src/main/java/org/pitest/voices/PiperVoice.java +++ b/chorus/src/main/java/org/pitest/voices/PiperVoice.java @@ -2,9 +2,8 @@ import opennlp.tools.sentdetect.SentenceDetectorME; import opennlp.tools.sentdetect.SentenceModel; -import org.pitest.g2p.core.PiperPhonemizer; -import org.pitest.g2p.core.tracing.Trace; -import org.pitest.g2p.util.Resource; +import org.pitest.voices.g2p.core.PiperPhonemizer; +import org.pitest.voices.g2p.core.tracing.Trace; import org.pitest.voices.audio.Audio; diff --git a/chorus/src/test/java/org/pitest/voices/ChorusTest.java b/chorus/src/test/java/org/pitest/voices/ChorusTest.java index ba595b9..242ab77 100644 --- a/chorus/src/test/java/org/pitest/voices/ChorusTest.java +++ b/chorus/src/test/java/org/pitest/voices/ChorusTest.java @@ -1,16 +1,11 @@ package org.pitest.voices; import org.junit.jupiter.api.Test; -import org.pitest.g2p.core.Dictionary; -import org.pitest.g2p.core.EnglishModel; -import org.pitest.g2p.core.PiperPhonemizer; -import org.pitest.g2p.core.expansions.NumberExpander; -import org.pitest.g2p.core.tracing.LoggingTrace; -import org.pitest.g2p.core.tracing.Trace; +import org.pitest.voices.alba.Alba; +import org.pitest.voices.g2p.core.Dictionary; +import org.pitest.voices.g2p.core.tracing.LoggingTrace; import org.pitest.voices.audio.Audio; -import java.util.List; - import static org.assertj.core.api.Assertions.assertThat; import static org.pitest.voices.ChorusConfig.chorusConfig; import static org.pitest.voices.util.Play.play; @@ -28,7 +23,7 @@ class ChorusTest { void someWords() { try (Chorus chorus = new Chorus(config)) { - Voice v1 = chorus.voice(Models.albaMedium()); + Voice v1 = chorus.voice(Alba.albaMedium()); Audio audio = v1.say("A rainbow is a meteorological phenomenon that is caused by reflection, " + "refraction and dispersion of light in water droplets resulting in a spectrum of light appearing in the sky."); @@ -41,7 +36,7 @@ void someWords() { @Test void numbers() { try (Chorus chorus = new Chorus(config)) { - Voice v1 = chorus.voice(Models.albaMedium()); + Voice v1 = chorus.voice(Alba.albaMedium()); Audio audio = v1.say("The year is 2025"); @@ -52,7 +47,7 @@ void numbers() { @Test void dashes() { try (Chorus chorus = new Chorus(config)) { - Voice v1 = chorus.voice(Models.albaMedium()); + Voice v1 = chorus.voice(Alba.albaMedium()); Audio audio = v1.say("It wasn't meant for visitors like this one---low and insignificant."); play(audio); diff --git a/chorus/src/test/java/org/pitest/voices/ModelConfigTest.java b/chorus/src/test/java/org/pitest/voices/ModelConfigTest.java index 31950ba..45b153e 100644 --- a/chorus/src/test/java/org/pitest/voices/ModelConfigTest.java +++ b/chorus/src/test/java/org/pitest/voices/ModelConfigTest.java @@ -1,12 +1,10 @@ package org.pitest.voices; import org.junit.jupiter.api.Test; -import org.pitest.g2p.util.Resource; import java.io.InputStream; import static org.assertj.core.api.Assertions.assertThat; -import static org.junit.jupiter.api.Assertions.*; class ModelConfigTest { diff --git a/chorus/src/test/java/org/pitest/voices/util/Fetch.java b/chorus/src/test/java/org/pitest/voices/util/Fetch.java index 458a740..88ae571 100644 --- a/chorus/src/test/java/org/pitest/voices/util/Fetch.java +++ b/chorus/src/test/java/org/pitest/voices/util/Fetch.java @@ -1,8 +1,5 @@ package org.pitest.voices.util; -import me.tongfei.progressbar.ProgressBar; -import me.tongfei.progressbar.ProgressBarBuilder; - import java.io.FileOutputStream; import java.io.IOException; import java.net.URL; @@ -29,12 +26,10 @@ public Path fetch(URL url, String name) throws IOException { URLConnection urlConn = url.openConnection(); long size = urlConn.getContentLengthLong(); - ProgressBarBuilder pbb = new ProgressBarBuilder(); - pbb.setTaskName("Fetching "); - pbb.setInitialMax(size); + ReadableByteChannel readableByteChannel = Channels - .newChannel(ProgressBar.wrap(urlConn.getInputStream(), pbb)); + .newChannel(urlConn.getInputStream()); try(FileOutputStream fileOutputStream = new FileOutputStream(archive.toFile())) { FileChannel fileChannel = fileOutputStream.getChannel(); diff --git a/core/pom.xml b/core/pom.xml new file mode 100644 index 0000000..69bd50c --- /dev/null +++ b/core/pom.xml @@ -0,0 +1,66 @@ + + + 4.0.0 + + + org.pitest.voices + voices-parent + ${revision} + + + core + core + + jar + + + + Apache 2 + https://www.apache.org/licenses/LICENSE-2.0.txt + repo + + + + + + org.pitest.voices + g2p + ${project.parent.version} + + + + com.google.code.gson + gson + 2.13.2 + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + + + org.apache.maven.plugins + maven-surefire-plugin + + + org.apache.maven.plugins + maven-jar-plugin + + + org.apache.maven.plugins + maven-source-plugin + + + org.apache.maven.plugins + maven-javadoc-plugin + + + + + + + diff --git a/chorus/src/main/java/org/pitest/voices/ClassPathModel.java b/core/src/main/java/org/pitest/voices/ClassPathModel.java similarity index 88% rename from chorus/src/main/java/org/pitest/voices/ClassPathModel.java rename to core/src/main/java/org/pitest/voices/ClassPathModel.java index 3da67c6..23bc79d 100644 --- a/chorus/src/main/java/org/pitest/voices/ClassPathModel.java +++ b/core/src/main/java/org/pitest/voices/ClassPathModel.java @@ -1,8 +1,5 @@ package org.pitest.voices; -import org.pitest.g2p.core.Language; -import org.pitest.g2p.util.Resource; - import java.io.IOException; import java.nio.file.Path; @@ -25,7 +22,7 @@ public byte[] byteBuffer(Path cacheBase) { @Override public ModelConfig resolveConfig(Path cacheBase) throws IOException { - try (var is = Resource.readAsStream(resource)) { + try (var is = Resource.readAsStream(resource + ".json")) { return ModelConfig.fromJson(is); } } diff --git a/chorus/src/main/java/org/pitest/voices/Model.java b/core/src/main/java/org/pitest/voices/Model.java similarity index 96% rename from chorus/src/main/java/org/pitest/voices/Model.java rename to core/src/main/java/org/pitest/voices/Model.java index e798b5e..a2b608c 100644 --- a/chorus/src/main/java/org/pitest/voices/Model.java +++ b/core/src/main/java/org/pitest/voices/Model.java @@ -1,9 +1,6 @@ package org.pitest.voices; -import org.pitest.g2p.core.Language; - import java.io.IOException; - import java.nio.file.Path; /** diff --git a/chorus/src/main/java/org/pitest/voices/ModelConfig.java b/core/src/main/java/org/pitest/voices/ModelConfig.java similarity index 100% rename from chorus/src/main/java/org/pitest/voices/ModelConfig.java rename to core/src/main/java/org/pitest/voices/ModelConfig.java diff --git a/g2p/src/main/java/org/pitest/g2p/util/Resource.java b/core/src/main/java/org/pitest/voices/Resource.java similarity index 97% rename from g2p/src/main/java/org/pitest/g2p/util/Resource.java rename to core/src/main/java/org/pitest/voices/Resource.java index 207df79..005b086 100644 --- a/g2p/src/main/java/org/pitest/g2p/util/Resource.java +++ b/core/src/main/java/org/pitest/voices/Resource.java @@ -1,4 +1,4 @@ -package org.pitest.g2p.util; +package org.pitest.voices; import java.io.BufferedReader; import java.io.IOException; diff --git a/cori/pom.xml b/cori/pom.xml new file mode 100644 index 0000000..60a36df --- /dev/null +++ b/cori/pom.xml @@ -0,0 +1,67 @@ + + + 4.0.0 + + + org.pitest.voices + voices-parent + ${revision} + + + cori + cori + + jar + + + + Apache 2 + https://www.apache.org/licenses/LICENSE-2.0.txt + repo + + + + + + + org.pitest.voices + core + ${project.parent.version} + + + org.pitest.voices + cori-model + ${models.version} + + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + + + org.apache.maven.plugins + maven-surefire-plugin + + + org.apache.maven.plugins + maven-jar-plugin + + + org.apache.maven.plugins + maven-source-plugin + + + org.apache.maven.plugins + maven-javadoc-plugin + + + + + + + diff --git a/cori/src/main/java/org/pitest/voices/cori/Cori.java b/cori/src/main/java/org/pitest/voices/cori/Cori.java new file mode 100644 index 0000000..038ac92 --- /dev/null +++ b/cori/src/main/java/org/pitest/voices/cori/Cori.java @@ -0,0 +1,14 @@ +package org.pitest.voices.cori; + +import org.pitest.voices.ClassPathModel; +import org.pitest.voices.Language; +import org.pitest.voices.Model; + +public class Cori { + + public static Model coriHigh() { + return new ClassPathModel("/models/vits-piper-en_GB-cori-high/en_GB-cori-high.onnx", + Language.en_GB, + 1.0f); + } +} diff --git a/cori/src/test/java/org/pitest/voices/cori/CoriTest.java b/cori/src/test/java/org/pitest/voices/cori/CoriTest.java new file mode 100644 index 0000000..1123501 --- /dev/null +++ b/cori/src/test/java/org/pitest/voices/cori/CoriTest.java @@ -0,0 +1,30 @@ +package org.pitest.voices.cori; + +import org.junit.jupiter.api.Test; +import org.pitest.voices.Language; +import org.pitest.voices.Model; + +import java.io.IOException; +import java.nio.file.Path; + +import static org.assertj.core.api.Assertions.assertThat; + +class CoriTest { + + Model underTest = Cori.coriHigh(); + + @Test + void speaksProperEnglish() { + assertThat(underTest.language()).isEqualTo(Language.en_GB); + } + + @Test + void loadsResources() throws IOException { + assertThat(underTest.byteBuffer(unused())).hasSizeGreaterThan(50000); + assertThat(underTest.resolveConfig(unused()).sampleRate()).isEqualTo(22050L); + } + + private Path unused() { + return null; + } +} \ No newline at end of file diff --git a/en_uk/pom.xml b/en_uk/pom.xml index 0dbc1e2..76a2ae2 100644 --- a/en_uk/pom.xml +++ b/en_uk/pom.xml @@ -29,7 +29,11 @@ g2p ${project.parent.version} - + + org.pitest.voices + core + ${project.parent.version} + com.microsoft.onnxruntime @@ -44,6 +48,18 @@ ${project.parent.version} test + + org.pitest.voices + alba + ${project.parent.version} + test + + + org.pitest.voices + cori + ${project.parent.version} + test + org.pitest.voices chorus diff --git a/en_uk/src/main/java/org/pitest/voices/uk/EnUkDictionary.java b/en_uk/src/main/java/org/pitest/voices/uk/EnUkDictionary.java index b72b6f7..f5926d5 100644 --- a/en_uk/src/main/java/org/pitest/voices/uk/EnUkDictionary.java +++ b/en_uk/src/main/java/org/pitest/voices/uk/EnUkDictionary.java @@ -1,8 +1,8 @@ package org.pitest.voices.uk; -import org.pitest.g2p.core.Dictionary; +import org.pitest.voices.g2p.core.Dictionary; -import static org.pitest.g2p.util.Resource.read; +import static org.pitest.voices.Resource.read; public class EnUkDictionary { public static Dictionary en_uk() { diff --git a/en_uk/src/test/java/org/pitest/voices/dictionary/DictionaryGenerator.java b/en_uk/src/test/java/org/pitest/voices/dictionary/DictionaryGenerator.java index 6b20a98..c27d5a5 100644 --- a/en_uk/src/test/java/org/pitest/voices/dictionary/DictionaryGenerator.java +++ b/en_uk/src/test/java/org/pitest/voices/dictionary/DictionaryGenerator.java @@ -1,7 +1,7 @@ package org.pitest.voices.dictionary; -import org.pitest.g2p.core.Dictionary; -import org.pitest.g2p.util.Resource; +import org.pitest.voices.g2p.core.Dictionary; +import org.pitest.voices.Resource; import org.pitest.voices.ChorusConfig; import org.pitest.voices.util.Fetch; diff --git a/en_uk/src/test/java/org/pitest/voices/dictionary/Homographs.java b/en_uk/src/test/java/org/pitest/voices/dictionary/Homographs.java index 163fa78..4189289 100644 --- a/en_uk/src/test/java/org/pitest/voices/dictionary/Homographs.java +++ b/en_uk/src/test/java/org/pitest/voices/dictionary/Homographs.java @@ -1,11 +1,11 @@ package org.pitest.voices.dictionary; -import org.pitest.g2p.core.Dictionary; -import org.pitest.g2p.core.EnglishModel; -import org.pitest.g2p.core.Language; -import org.pitest.g2p.core.PiperPhonemizer; -import org.pitest.g2p.core.pos.Pos; -import org.pitest.g2p.core.tracing.Trace; +import org.pitest.voices.Language; +import org.pitest.voices.g2p.core.Dictionary; +import org.pitest.voices.g2p.core.EnglishModel; +import org.pitest.voices.g2p.core.PiperPhonemizer; +import org.pitest.voices.g2p.core.pos.Pos; +import org.pitest.voices.g2p.core.tracing.Trace; import org.pitest.voices.ChorusConfig; import java.io.IOException; diff --git a/en_uk/src/test/java/org/pitest/voices/uk/TryItOutTest.java b/en_uk/src/test/java/org/pitest/voices/uk/TryItOutTest.java index 73a8a43..ae7862a 100644 --- a/en_uk/src/test/java/org/pitest/voices/uk/TryItOutTest.java +++ b/en_uk/src/test/java/org/pitest/voices/uk/TryItOutTest.java @@ -1,11 +1,12 @@ package org.pitest.voices.uk; import org.junit.jupiter.api.Test; -import org.pitest.g2p.core.tracing.LoggingTrace; -import org.pitest.g2p.util.Resource; +import org.pitest.voices.alba.Alba; +import org.pitest.voices.cori.Cori; +import org.pitest.voices.g2p.core.tracing.LoggingTrace; +import org.pitest.voices.Resource; import org.pitest.voices.Chorus; import org.pitest.voices.ChorusConfig; -import org.pitest.voices.Models; import org.pitest.voices.Pause; import org.pitest.voices.Voice; import org.pitest.voices.audio.Audio; @@ -23,7 +24,7 @@ public class TryItOutTest { @Test void orwell() { try (Chorus chorus = new Chorus(config)) { - Voice v1 = chorus.voice(Models.albaMedium()); + Voice v1 = chorus.voice(Alba.albaMedium()); var audio = v1.say("It was a bright cold day in April, and the clocks were striking thirteen."); play(audio); } @@ -32,7 +33,7 @@ void orwell() { @Test void mobyDick() { try (Chorus chorus = new Chorus(config)) { - Voice v1 = chorus.voice(Models.albaMedium()); + Voice v1 = chorus.voice(Alba.albaMedium()); String text = String.join("\n", Resource.read("/samples/moby_dick.md")); Audio audio = v1.say(text); @@ -40,22 +41,10 @@ void mobyDick() { } } - @Test - void aWord() { - try (Chorus chorus = new Chorus(config)) { - Voice v1 = chorus.voice(Models.albaMedium()); - Voice v2 = chorus.voice(Models.alanMedium()); - Voice v3 = chorus.voice(Models.northernEnglishMale()); - play(v1.say("abbreviated")); - play(v2.say("abbreviated")); - play(v3.say("abbreviated")); - } - } - @Test void wutheringHeights() { try (Chorus chorus = new Chorus(config)) { - Voice v1 = chorus.voice(Models.coriHigh()) + Voice v1 = chorus.voice(Cori.coriHigh()) .withPauses(List.of(new Pause(",", 6))); String text = String.join("\n", Resource.read("/samples/wuthering_heights.md")); Audio audio = v1.say(text); @@ -66,7 +55,7 @@ void wutheringHeights() { @Test void dashes() { try (Chorus chorus = new Chorus(config)) { - Voice v1 = chorus.voice(Models.albaMedium()); + Voice v1 = chorus.voice(Alba.albaMedium()); Audio audio = v1.say("It wasn't meant for visitors like this one---low and insignificant."); play(audio); @@ -78,7 +67,7 @@ void dashes() { void homographs() { try (Chorus chorus = new Chorus(config)) { - Voice v1 = chorus.voice(Models.albaMedium()); + Voice v1 = chorus.voice(Alba.albaMedium()); Audio audio = v1.say( "invalid response for an invalid. " + @@ -100,7 +89,7 @@ void homographs() { @Test void rainbows() { try (Chorus chorus = new Chorus(config)) { - Voice v1 = chorus.voice(Models.albaMedium()); + Voice v1 = chorus.voice(Alba.albaMedium()); Audio audio = v1.say("A rainbow is a meteorological phenomenon that is caused by reflection, " + "refraction and dispersion of light in water droplets resulting in a spectrum of light appearing in the sky."); @@ -112,7 +101,7 @@ void rainbows() { void hyphens() { try (Chorus chorus = new Chorus(config)) { - Voice v1 = chorus.voice(Models.albaMedium()); + Voice v1 = chorus.voice(Alba.albaMedium()); Audio audio = v1.say("anti-theft---a security term---device"); play(audio); } @@ -123,12 +112,12 @@ void hyphens() { void normalise() { String text = "This is a sentence"; try (Chorus chorus = new Chorus(config)) { - Voice v1 = chorus.voice(Models.albaMedium()); + Voice v1 = chorus.voice(Alba.albaMedium()); Audio reference = v1.say(text); float max = reference.max(); for (float gain = 0.7f; gain < 5.0f; gain += 0.1f) { - var v2 = chorus.voice(Models.coriHigh()) + var v2 = chorus.voice(Cori.coriHigh()) .withGain(gain) .say(text); System.out.println("Testing gain " + gain + " for max " + max + " got " + v2.max()); diff --git a/en_us/pom.xml b/en_us/pom.xml index a64f445..bc3d6ad 100644 --- a/en_us/pom.xml +++ b/en_us/pom.xml @@ -43,6 +43,19 @@ ${project.parent.version} test + + + org.pitest.voices + alba + ${project.parent.version} + test + + + org.pitest.voices + bryce + ${project.parent.version} + test + org.pitest.voices chorus diff --git a/en_us/src/main/java/org/pitest/voices/us/EnUsDictionary.java b/en_us/src/main/java/org/pitest/voices/us/EnUsDictionary.java index 074aa25..38ff0c5 100644 --- a/en_us/src/main/java/org/pitest/voices/us/EnUsDictionary.java +++ b/en_us/src/main/java/org/pitest/voices/us/EnUsDictionary.java @@ -1,8 +1,9 @@ package org.pitest.voices.us; -import org.pitest.g2p.core.Dictionary; +import org.pitest.voices.g2p.core.Dictionary; + +import static org.pitest.voices.g2p.util.Resource.read; -import static org.pitest.g2p.util.Resource.read; public class EnUsDictionary { public static Dictionary en_us() { diff --git a/en_us/src/test/java/org/pitest/voices/us/ConvertCMU.java b/en_us/src/test/java/org/pitest/voices/us/ConvertCMU.java index e174342..aeebe18 100644 --- a/en_us/src/test/java/org/pitest/voices/us/ConvertCMU.java +++ b/en_us/src/test/java/org/pitest/voices/us/ConvertCMU.java @@ -1,6 +1,6 @@ package org.pitest.voices.us; -import org.pitest.g2p.core.Dictionary; +import org.pitest.voices.g2p.core.Dictionary; import org.pitest.voices.ChorusConfig; import org.pitest.voices.util.Fetch; diff --git a/en_us/src/test/java/org/pitest/voices/us/TryItOutTest.java b/en_us/src/test/java/org/pitest/voices/us/TryItOutTest.java index 6e61719..74e93c1 100644 --- a/en_us/src/test/java/org/pitest/voices/us/TryItOutTest.java +++ b/en_us/src/test/java/org/pitest/voices/us/TryItOutTest.java @@ -2,12 +2,12 @@ import org.junit.jupiter.api.Test; -import org.pitest.g2p.core.tracing.LoggingTrace; -import org.pitest.g2p.util.Resource; +import org.pitest.voices.alba.Alba; +import org.pitest.voices.bryce.Bryce; +import org.pitest.voices.g2p.core.tracing.LoggingTrace; +import org.pitest.voices.Resource; import org.pitest.voices.Chorus; import org.pitest.voices.ChorusConfig; -import org.pitest.voices.Models; -import org.pitest.voices.UsModels; import org.pitest.voices.Voice; import org.pitest.voices.audio.Audio; @@ -22,7 +22,7 @@ public class TryItOutTest { @Test void gibson() { try (Chorus chorus = new Chorus(config)) { - Voice v1 = chorus.voice(UsModels.bryceMedium()).withLengthScale(0.7f); + Voice v1 = chorus.voice(Bryce.bryceMedium()).withLengthScale(0.7f); var audio = v1.say("The sky above the port was the color of television, tuned to a dead channel."); play(audio); } @@ -31,7 +31,7 @@ void gibson() { @Test void huckleberryFinn() { try (Chorus chorus = new Chorus(config)) { - Voice v1 = chorus.voice(Models.albaMedium()).withLengthScale(0.5f); + Voice v1 = chorus.voice(Alba.albaMedium()).withLengthScale(0.5f); String text = String.join("\n", Resource.read("/samples/huckleberry_finn.md")); Audio audio = v1.say(text); @@ -42,7 +42,7 @@ void huckleberryFinn() { @Test void aWord() { try (Chorus chorus = new Chorus(config)) { - Voice v1 = chorus.voice(Models.albaMedium()); + Voice v1 = chorus.voice(Alba.albaMedium()); Audio audio = v1.say("abbreviated"); play(audio); } @@ -51,7 +51,7 @@ void aWord() { @Test void someWords() { try (Chorus chorus = new Chorus(config)) { - Voice v1 = chorus.voice(Models.albaMedium()); + Voice v1 = chorus.voice(Alba.albaMedium()); Audio audio = v1.say("usurped\n" + "usurping\n" + "usurps\n" + @@ -98,7 +98,7 @@ void someWords() { void homographs() { try (Chorus chorus = new Chorus(config)) { - Voice v1 = chorus.voice(Models.albaMedium()); + Voice v1 = chorus.voice(Alba.albaMedium()); Audio audio = v1.say( "invalid response for an invalid. " + diff --git a/g2p/src/main/java/org/pitest/g2p/core/NoModel.java b/g2p/src/main/java/org/pitest/g2p/core/NoModel.java deleted file mode 100644 index 64ac4ce..0000000 --- a/g2p/src/main/java/org/pitest/g2p/core/NoModel.java +++ /dev/null @@ -1,19 +0,0 @@ -package org.pitest.g2p.core; - - -import org.pitest.g2p.core.pos.Pos; -import org.pitest.g2p.core.tracing.Trace; - -public class NoModel implements G2PModel { - - @Override - public String predict(Trace trace, Language lang, String word, Pos pos) { - return null; - } - - @Override - public void close() throws Exception { - // no op - } - -} diff --git a/g2p/src/main/java/org/pitest/g2p/core/Language.java b/g2p/src/main/java/org/pitest/voices/Language.java similarity index 97% rename from g2p/src/main/java/org/pitest/g2p/core/Language.java rename to g2p/src/main/java/org/pitest/voices/Language.java index e529a85..672e25b 100644 --- a/g2p/src/main/java/org/pitest/g2p/core/Language.java +++ b/g2p/src/main/java/org/pitest/voices/Language.java @@ -1,4 +1,4 @@ -package org.pitest.g2p.core; +package org.pitest.voices; public enum Language { ca_ES("Catalan"), diff --git a/g2p/src/main/java/org/pitest/g2p/core/Dictionary.java b/g2p/src/main/java/org/pitest/voices/g2p/core/Dictionary.java similarity index 96% rename from g2p/src/main/java/org/pitest/g2p/core/Dictionary.java rename to g2p/src/main/java/org/pitest/voices/g2p/core/Dictionary.java index 06aeee1..74a72c4 100644 --- a/g2p/src/main/java/org/pitest/g2p/core/Dictionary.java +++ b/g2p/src/main/java/org/pitest/voices/g2p/core/Dictionary.java @@ -1,6 +1,6 @@ -package org.pitest.g2p.core; +package org.pitest.voices.g2p.core; -import org.pitest.g2p.core.pos.Pos; +import org.pitest.voices.g2p.core.pos.Pos; import java.io.IOException; import java.nio.charset.StandardCharsets; @@ -14,7 +14,6 @@ import java.util.Set; import static java.util.Collections.emptyMap; -import static org.pitest.g2p.util.Resource.read; public class Dictionary { diff --git a/g2p/src/main/java/org/pitest/g2p/core/EnglishModel.java b/g2p/src/main/java/org/pitest/voices/g2p/core/EnglishModel.java similarity index 99% rename from g2p/src/main/java/org/pitest/g2p/core/EnglishModel.java rename to g2p/src/main/java/org/pitest/voices/g2p/core/EnglishModel.java index 7c63175..38ec6d2 100644 --- a/g2p/src/main/java/org/pitest/g2p/core/EnglishModel.java +++ b/g2p/src/main/java/org/pitest/voices/g2p/core/EnglishModel.java @@ -1,8 +1,9 @@ -package org.pitest.g2p.core; +package org.pitest.voices.g2p.core; -import org.pitest.g2p.core.pos.Pos; -import org.pitest.g2p.core.tracing.Trace; -import org.pitest.g2p.core.syllables.RulesSyllabiliser; +import org.pitest.voices.Language; +import org.pitest.voices.g2p.core.pos.Pos; +import org.pitest.voices.g2p.core.tracing.Trace; +import org.pitest.voices.g2p.core.syllables.RulesSyllabiliser; import java.util.ArrayList; import java.util.Arrays; @@ -13,8 +14,8 @@ import java.util.regex.Matcher; import java.util.regex.Pattern; -import static org.pitest.g2p.util.English.CONSONANTS; -import static org.pitest.g2p.util.English.VOWELS; +import static org.pitest.voices.g2p.util.English.CONSONANTS; +import static org.pitest.voices.g2p.util.English.VOWELS; /** * Phonemization model for English. Largely uses a dictionary, but falls back to a rules diff --git a/g2p/src/main/java/org/pitest/g2p/core/Expansion.java b/g2p/src/main/java/org/pitest/voices/g2p/core/Expansion.java similarity index 64% rename from g2p/src/main/java/org/pitest/g2p/core/Expansion.java rename to g2p/src/main/java/org/pitest/voices/g2p/core/Expansion.java index 4c6b747..d432655 100644 --- a/g2p/src/main/java/org/pitest/g2p/core/Expansion.java +++ b/g2p/src/main/java/org/pitest/voices/g2p/core/Expansion.java @@ -1,4 +1,4 @@ -package org.pitest.g2p.core; +package org.pitest.voices.g2p.core; public interface Expansion { String expand(String text); diff --git a/g2p/src/main/java/org/pitest/g2p/core/G2PModel.java b/g2p/src/main/java/org/pitest/voices/g2p/core/G2PModel.java similarity index 73% rename from g2p/src/main/java/org/pitest/g2p/core/G2PModel.java rename to g2p/src/main/java/org/pitest/voices/g2p/core/G2PModel.java index 7b6490c..e077f33 100644 --- a/g2p/src/main/java/org/pitest/g2p/core/G2PModel.java +++ b/g2p/src/main/java/org/pitest/voices/g2p/core/G2PModel.java @@ -1,7 +1,8 @@ -package org.pitest.g2p.core; +package org.pitest.voices.g2p.core; -import org.pitest.g2p.core.pos.Pos; -import org.pitest.g2p.core.tracing.Trace; +import org.pitest.voices.Language; +import org.pitest.voices.g2p.core.pos.Pos; +import org.pitest.voices.g2p.core.tracing.Trace; public interface G2PModel extends AutoCloseable { diff --git a/g2p/src/main/java/org/pitest/g2p/core/PiperPhonemizer.java b/g2p/src/main/java/org/pitest/voices/g2p/core/PiperPhonemizer.java similarity index 92% rename from g2p/src/main/java/org/pitest/g2p/core/PiperPhonemizer.java rename to g2p/src/main/java/org/pitest/voices/g2p/core/PiperPhonemizer.java index 2bce564..dc8f087 100644 --- a/g2p/src/main/java/org/pitest/g2p/core/PiperPhonemizer.java +++ b/g2p/src/main/java/org/pitest/voices/g2p/core/PiperPhonemizer.java @@ -1,10 +1,11 @@ -package org.pitest.g2p.core; +package org.pitest.voices.g2p.core; import opennlp.tools.tokenize.SimpleTokenizer; -import org.pitest.g2p.core.pos.POSToken; -import org.pitest.g2p.core.pos.Pos; -import org.pitest.g2p.core.pos.SimplePOSTagger; -import org.pitest.g2p.core.tracing.Trace; +import org.pitest.voices.Language; +import org.pitest.voices.g2p.core.pos.POSToken; +import org.pitest.voices.g2p.core.pos.Pos; +import org.pitest.voices.g2p.core.pos.SimplePOSTagger; +import org.pitest.voices.g2p.core.tracing.Trace; import java.io.IOException; import java.util.ArrayList; @@ -12,7 +13,7 @@ import java.util.stream.Collectors; import java.util.stream.Stream; -import static org.pitest.g2p.core.pos.SimplePOSTagger.makeTagger; +import static org.pitest.voices.g2p.core.pos.SimplePOSTagger.makeTagger; public class PiperPhonemizer { diff --git a/g2p/src/main/java/org/pitest/g2p/core/WordToSyllables.java b/g2p/src/main/java/org/pitest/voices/g2p/core/WordToSyllables.java similarity index 74% rename from g2p/src/main/java/org/pitest/g2p/core/WordToSyllables.java rename to g2p/src/main/java/org/pitest/voices/g2p/core/WordToSyllables.java index 05d064d..242f0a1 100644 --- a/g2p/src/main/java/org/pitest/g2p/core/WordToSyllables.java +++ b/g2p/src/main/java/org/pitest/voices/g2p/core/WordToSyllables.java @@ -1,4 +1,4 @@ -package org.pitest.g2p.core; +package org.pitest.voices.g2p.core; import java.util.List; diff --git a/g2p/src/main/java/org/pitest/g2p/core/expansions/NumberExpander.java b/g2p/src/main/java/org/pitest/voices/g2p/core/expansions/NumberExpander.java similarity index 99% rename from g2p/src/main/java/org/pitest/g2p/core/expansions/NumberExpander.java rename to g2p/src/main/java/org/pitest/voices/g2p/core/expansions/NumberExpander.java index b2e40b7..0581a2b 100644 --- a/g2p/src/main/java/org/pitest/g2p/core/expansions/NumberExpander.java +++ b/g2p/src/main/java/org/pitest/voices/g2p/core/expansions/NumberExpander.java @@ -1,6 +1,6 @@ -package org.pitest.g2p.core.expansions; +package org.pitest.voices.g2p.core.expansions; -import org.pitest.g2p.core.Expansion; +import org.pitest.voices.g2p.core.Expansion; import java.util.HashMap; import java.util.Map; diff --git a/g2p/src/main/java/org/pitest/g2p/core/pos/POSToken.java b/g2p/src/main/java/org/pitest/voices/g2p/core/pos/POSToken.java similarity index 55% rename from g2p/src/main/java/org/pitest/g2p/core/pos/POSToken.java rename to g2p/src/main/java/org/pitest/voices/g2p/core/pos/POSToken.java index c907a9c..04998d2 100644 --- a/g2p/src/main/java/org/pitest/g2p/core/pos/POSToken.java +++ b/g2p/src/main/java/org/pitest/voices/g2p/core/pos/POSToken.java @@ -1,4 +1,4 @@ -package org.pitest.g2p.core.pos; +package org.pitest.voices.g2p.core.pos; public record POSToken(String word, Pos pos) { diff --git a/g2p/src/main/java/org/pitest/g2p/core/pos/Pos.java b/g2p/src/main/java/org/pitest/voices/g2p/core/pos/Pos.java similarity index 98% rename from g2p/src/main/java/org/pitest/g2p/core/pos/Pos.java rename to g2p/src/main/java/org/pitest/voices/g2p/core/pos/Pos.java index 57629c1..254c41a 100644 --- a/g2p/src/main/java/org/pitest/g2p/core/pos/Pos.java +++ b/g2p/src/main/java/org/pitest/voices/g2p/core/pos/Pos.java @@ -1,4 +1,4 @@ -package org.pitest.g2p.core.pos; +package org.pitest.voices.g2p.core.pos; // Reduced set of part of speech tags, mapped to diff --git a/g2p/src/main/java/org/pitest/g2p/core/pos/SimplePOSTagger.java b/g2p/src/main/java/org/pitest/voices/g2p/core/pos/SimplePOSTagger.java similarity index 96% rename from g2p/src/main/java/org/pitest/g2p/core/pos/SimplePOSTagger.java rename to g2p/src/main/java/org/pitest/voices/g2p/core/pos/SimplePOSTagger.java index 9eb52e1..67fc187 100644 --- a/g2p/src/main/java/org/pitest/g2p/core/pos/SimplePOSTagger.java +++ b/g2p/src/main/java/org/pitest/voices/g2p/core/pos/SimplePOSTagger.java @@ -1,9 +1,9 @@ -package org.pitest.g2p.core.pos; +package org.pitest.voices.g2p.core.pos; import opennlp.tools.postag.POSModel; import opennlp.tools.postag.POSTagFormat; import opennlp.tools.postag.POSTaggerME; -import org.pitest.g2p.util.Resource; +import org.pitest.voices.g2p.util.Resource; import java.io.IOException; import java.io.UncheckedIOException; diff --git a/g2p/src/main/java/org/pitest/g2p/core/syllables/RulesSyllabiliser.java b/g2p/src/main/java/org/pitest/voices/g2p/core/syllables/RulesSyllabiliser.java similarity index 96% rename from g2p/src/main/java/org/pitest/g2p/core/syllables/RulesSyllabiliser.java rename to g2p/src/main/java/org/pitest/voices/g2p/core/syllables/RulesSyllabiliser.java index 11fd476..29b74d8 100644 --- a/g2p/src/main/java/org/pitest/g2p/core/syllables/RulesSyllabiliser.java +++ b/g2p/src/main/java/org/pitest/voices/g2p/core/syllables/RulesSyllabiliser.java @@ -1,14 +1,14 @@ -package org.pitest.g2p.core.syllables; +package org.pitest.voices.g2p.core.syllables; -import org.pitest.g2p.core.WordToSyllables; +import org.pitest.voices.g2p.core.WordToSyllables; import java.util.ArrayList; import java.util.List; import java.util.Set; import java.util.stream.Collectors; -import static org.pitest.g2p.util.English.CONSONANTS; -import static org.pitest.g2p.util.English.VOWELS; +import static org.pitest.voices.g2p.util.English.CONSONANTS; +import static org.pitest.voices.g2p.util.English.VOWELS; /** * Converts words to syllables via a set of hard coded rules. diff --git a/g2p/src/main/java/org/pitest/g2p/core/tracing/LoggingTrace.java b/g2p/src/main/java/org/pitest/voices/g2p/core/tracing/LoggingTrace.java similarity index 94% rename from g2p/src/main/java/org/pitest/g2p/core/tracing/LoggingTrace.java rename to g2p/src/main/java/org/pitest/voices/g2p/core/tracing/LoggingTrace.java index 9b3fb2e..87d9e36 100644 --- a/g2p/src/main/java/org/pitest/g2p/core/tracing/LoggingTrace.java +++ b/g2p/src/main/java/org/pitest/voices/g2p/core/tracing/LoggingTrace.java @@ -1,6 +1,6 @@ -package org.pitest.g2p.core.tracing; +package org.pitest.voices.g2p.core.tracing; -import org.pitest.g2p.core.pos.Pos; +import org.pitest.voices.g2p.core.pos.Pos; import java.util.ArrayList; import java.util.List; diff --git a/g2p/src/main/java/org/pitest/g2p/core/tracing/Trace.java b/g2p/src/main/java/org/pitest/voices/g2p/core/tracing/Trace.java similarity index 93% rename from g2p/src/main/java/org/pitest/g2p/core/tracing/Trace.java rename to g2p/src/main/java/org/pitest/voices/g2p/core/tracing/Trace.java index a773a81..3a61d44 100644 --- a/g2p/src/main/java/org/pitest/g2p/core/tracing/Trace.java +++ b/g2p/src/main/java/org/pitest/voices/g2p/core/tracing/Trace.java @@ -1,6 +1,6 @@ -package org.pitest.g2p.core.tracing; +package org.pitest.voices.g2p.core.tracing; -import org.pitest.g2p.core.pos.Pos; +import org.pitest.voices.g2p.core.pos.Pos; import java.util.List; diff --git a/g2p/src/main/java/org/pitest/g2p/util/English.java b/g2p/src/main/java/org/pitest/voices/g2p/util/English.java similarity index 89% rename from g2p/src/main/java/org/pitest/g2p/util/English.java rename to g2p/src/main/java/org/pitest/voices/g2p/util/English.java index 2166195..7ea90e9 100644 --- a/g2p/src/main/java/org/pitest/g2p/util/English.java +++ b/g2p/src/main/java/org/pitest/voices/g2p/util/English.java @@ -1,4 +1,4 @@ -package org.pitest.g2p.util; +package org.pitest.voices.g2p.util; import java.util.Set; diff --git a/g2p/src/main/java/org/pitest/voices/g2p/util/Resource.java b/g2p/src/main/java/org/pitest/voices/g2p/util/Resource.java new file mode 100644 index 0000000..6ee8a4e --- /dev/null +++ b/g2p/src/main/java/org/pitest/voices/g2p/util/Resource.java @@ -0,0 +1,32 @@ +package org.pitest.voices.g2p.util; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.UncheckedIOException; +import java.nio.charset.StandardCharsets; +import java.util.List; +import java.util.stream.Collectors; + +public class Resource { + + public static List read(String resource) { + try (InputStream is = readAsStream(resource); + InputStreamReader inputStreamReader = new InputStreamReader(is, StandardCharsets.UTF_8); + BufferedReader bufferedReader = new BufferedReader(inputStreamReader)) { + return bufferedReader.lines() + .collect(Collectors.toList()); + } catch (IOException e) { + throw new UncheckedIOException(e); + } + } + + public static InputStream readAsStream(String resource) { + var stream = Resource.class.getResourceAsStream(resource); + if (stream == null) { + throw new IllegalStateException("Could not find resource " + resource); + } + return stream; + } +} diff --git a/g2p/src/test/java/org/pitest/g2p/core/EnglishModelRulesTest.java b/g2p/src/test/java/org/pitest/voices/g2p/core/EnglishModelRulesTest.java similarity index 95% rename from g2p/src/test/java/org/pitest/g2p/core/EnglishModelRulesTest.java rename to g2p/src/test/java/org/pitest/voices/g2p/core/EnglishModelRulesTest.java index f5a2549..48a447a 100644 --- a/g2p/src/test/java/org/pitest/g2p/core/EnglishModelRulesTest.java +++ b/g2p/src/test/java/org/pitest/voices/g2p/core/EnglishModelRulesTest.java @@ -1,11 +1,12 @@ -package org.pitest.g2p.core; +package org.pitest.voices.g2p.core; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.CsvSource; -import org.pitest.g2p.core.pos.Pos; -import org.pitest.g2p.core.tracing.Trace; -import org.pitest.g2p.core.syllables.RulesSyllabiliser; +import org.pitest.voices.Language; +import org.pitest.voices.g2p.core.pos.Pos; +import org.pitest.voices.g2p.core.tracing.Trace; +import org.pitest.voices.g2p.core.syllables.RulesSyllabiliser; import static org.assertj.core.api.Assertions.assertThat; diff --git a/g2p/src/test/java/org/pitest/g2p/core/PiperPhonemizerTest.java b/g2p/src/test/java/org/pitest/voices/g2p/core/PiperPhonemizerTest.java similarity index 95% rename from g2p/src/test/java/org/pitest/g2p/core/PiperPhonemizerTest.java rename to g2p/src/test/java/org/pitest/voices/g2p/core/PiperPhonemizerTest.java index 272321d..df73df8 100644 --- a/g2p/src/test/java/org/pitest/g2p/core/PiperPhonemizerTest.java +++ b/g2p/src/test/java/org/pitest/voices/g2p/core/PiperPhonemizerTest.java @@ -1,8 +1,9 @@ -package org.pitest.g2p.core; +package org.pitest.voices.g2p.core; import org.junit.jupiter.api.Test; -import org.pitest.g2p.core.expansions.NumberExpander; -import org.pitest.g2p.core.tracing.Trace; +import org.pitest.voices.Language; +import org.pitest.voices.g2p.core.expansions.NumberExpander; +import org.pitest.voices.g2p.core.tracing.Trace; import java.util.List; import java.util.Map; diff --git a/g2p/src/test/java/org/pitest/g2p/core/expansions/NumberExpanderTest.java b/g2p/src/test/java/org/pitest/voices/g2p/core/expansions/NumberExpanderTest.java similarity index 99% rename from g2p/src/test/java/org/pitest/g2p/core/expansions/NumberExpanderTest.java rename to g2p/src/test/java/org/pitest/voices/g2p/core/expansions/NumberExpanderTest.java index 349417c..68de2b7 100644 --- a/g2p/src/test/java/org/pitest/g2p/core/expansions/NumberExpanderTest.java +++ b/g2p/src/test/java/org/pitest/voices/g2p/core/expansions/NumberExpanderTest.java @@ -1,4 +1,4 @@ -package org.pitest.g2p.core.expansions; +package org.pitest.voices.g2p.core.expansions; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; diff --git a/g2p/src/test/java/org/pitest/g2p/core/pos/SimplePOSTaggerTest.java b/g2p/src/test/java/org/pitest/voices/g2p/core/pos/SimplePOSTaggerTest.java similarity index 95% rename from g2p/src/test/java/org/pitest/g2p/core/pos/SimplePOSTaggerTest.java rename to g2p/src/test/java/org/pitest/voices/g2p/core/pos/SimplePOSTaggerTest.java index d551b0d..b099827 100644 --- a/g2p/src/test/java/org/pitest/g2p/core/pos/SimplePOSTaggerTest.java +++ b/g2p/src/test/java/org/pitest/voices/g2p/core/pos/SimplePOSTaggerTest.java @@ -1,9 +1,9 @@ -package org.pitest.g2p.core.pos; +package org.pitest.voices.g2p.core.pos; import org.junit.jupiter.api.Test; import static org.assertj.core.api.Assertions.assertThat; -import static org.pitest.g2p.core.pos.SimplePOSTagger.makeTagger; +import static org.pitest.voices.g2p.core.pos.SimplePOSTagger.makeTagger; class SimplePOSTaggerTest { diff --git a/g2p/src/test/java/org/pitest/g2p/core/syllables/RulesSyllabiliserTest.java b/g2p/src/test/java/org/pitest/voices/g2p/core/syllables/RulesSyllabiliserTest.java similarity index 97% rename from g2p/src/test/java/org/pitest/g2p/core/syllables/RulesSyllabiliserTest.java rename to g2p/src/test/java/org/pitest/voices/g2p/core/syllables/RulesSyllabiliserTest.java index 2d1ff71..142dca0 100644 --- a/g2p/src/test/java/org/pitest/g2p/core/syllables/RulesSyllabiliserTest.java +++ b/g2p/src/test/java/org/pitest/voices/g2p/core/syllables/RulesSyllabiliserTest.java @@ -1,4 +1,4 @@ -package org.pitest.g2p.core.syllables; +package org.pitest.voices.g2p.core.syllables; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; diff --git a/model_downloader/pom.xml b/model_downloader/pom.xml new file mode 100644 index 0000000..5c5d6ce --- /dev/null +++ b/model_downloader/pom.xml @@ -0,0 +1,94 @@ + + + 4.0.0 + + + org.pitest.voices + voices-parent + ${revision} + + + model-downloader + model-downloader + + jar + + + + Apache 2 + https://www.apache.org/licenses/LICENSE-2.0.txt + repo + + + + + + + org.pitest.voices + core + ${project.parent.version} + + + + me.tongfei + progressbar + 0.10.1 + + + org.apache.commons + commons-compress + 1.28.0 + + + + + com.microsoft.onnxruntime + onnxruntime + 1.22.0 + test + + + + org.pitest.voices + chorus + ${project.parent.version} + test + + + org.pitest.voices + chorus + ${project.parent.version} + test-jar + test + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + + + org.apache.maven.plugins + maven-surefire-plugin + + + org.apache.maven.plugins + maven-jar-plugin + + + org.apache.maven.plugins + maven-source-plugin + + + org.apache.maven.plugins + maven-javadoc-plugin + + + + + + + diff --git a/chorus/src/main/java/org/pitest/voices/FileModel.java b/model_downloader/src/main/java/org/pitest/voices/download/FileModel.java similarity index 93% rename from chorus/src/main/java/org/pitest/voices/FileModel.java rename to model_downloader/src/main/java/org/pitest/voices/download/FileModel.java index b4cc148..228a0f6 100644 --- a/chorus/src/main/java/org/pitest/voices/FileModel.java +++ b/model_downloader/src/main/java/org/pitest/voices/download/FileModel.java @@ -1,6 +1,8 @@ -package org.pitest.voices; +package org.pitest.voices.download; -import org.pitest.g2p.core.Language; +import org.pitest.voices.Language; +import org.pitest.voices.Model; +import org.pitest.voices.ModelConfig; import java.io.IOException; import java.nio.file.Files; diff --git a/chorus/src/main/java/org/pitest/voices/ModelDownloader.java b/model_downloader/src/main/java/org/pitest/voices/download/ModelDownloader.java similarity index 98% rename from chorus/src/main/java/org/pitest/voices/ModelDownloader.java rename to model_downloader/src/main/java/org/pitest/voices/download/ModelDownloader.java index bdeb12c..7344b20 100644 --- a/chorus/src/main/java/org/pitest/voices/ModelDownloader.java +++ b/model_downloader/src/main/java/org/pitest/voices/download/ModelDownloader.java @@ -1,4 +1,4 @@ -package org.pitest.voices; +package org.pitest.voices.download; import org.apache.commons.compress.archivers.ArchiveEntry; import org.apache.commons.compress.archivers.tar.TarArchiveInputStream; diff --git a/chorus/src/main/java/org/pitest/voices/ModelFetcher.java b/model_downloader/src/main/java/org/pitest/voices/download/ModelFetcher.java similarity index 78% rename from chorus/src/main/java/org/pitest/voices/ModelFetcher.java rename to model_downloader/src/main/java/org/pitest/voices/download/ModelFetcher.java index ff2b98b..8cc2bfa 100644 --- a/chorus/src/main/java/org/pitest/voices/ModelFetcher.java +++ b/model_downloader/src/main/java/org/pitest/voices/download/ModelFetcher.java @@ -1,4 +1,4 @@ -package org.pitest.voices; +package org.pitest.voices.download; import java.io.IOException; import java.nio.file.Path; diff --git a/chorus/src/main/java/org/pitest/voices/Models.java b/model_downloader/src/main/java/org/pitest/voices/download/Models.java similarity index 92% rename from chorus/src/main/java/org/pitest/voices/Models.java rename to model_downloader/src/main/java/org/pitest/voices/download/Models.java index e5b5e8e..32fe7b5 100644 --- a/chorus/src/main/java/org/pitest/voices/Models.java +++ b/model_downloader/src/main/java/org/pitest/voices/download/Models.java @@ -1,12 +1,12 @@ -package org.pitest.voices; +package org.pitest.voices.download; -import org.pitest.g2p.core.Language; +import org.pitest.voices.Language; +import org.pitest.voices.Model; import java.net.URL; public class Models { - public static Model coriHigh() { return sherpaModel("en_GB-cori-high", 2.0f); } diff --git a/chorus/src/main/java/org/pitest/voices/NonEnglishModels.java b/model_downloader/src/main/java/org/pitest/voices/download/NonEnglishModels.java similarity index 79% rename from chorus/src/main/java/org/pitest/voices/NonEnglishModels.java rename to model_downloader/src/main/java/org/pitest/voices/download/NonEnglishModels.java index f9697e8..6a58e74 100644 --- a/chorus/src/main/java/org/pitest/voices/NonEnglishModels.java +++ b/model_downloader/src/main/java/org/pitest/voices/download/NonEnglishModels.java @@ -1,8 +1,10 @@ -package org.pitest.voices; +package org.pitest.voices.download; -import org.pitest.g2p.core.Language; +import org.pitest.voices.Language; +import org.pitest.voices.Model; + +import static org.pitest.voices.download.Models.url; -import static org.pitest.voices.Models.url; public class NonEnglishModels { diff --git a/chorus/src/main/java/org/pitest/voices/URLModelFetcher.java b/model_downloader/src/main/java/org/pitest/voices/download/URLModelFetcher.java similarity index 97% rename from chorus/src/main/java/org/pitest/voices/URLModelFetcher.java rename to model_downloader/src/main/java/org/pitest/voices/download/URLModelFetcher.java index cc02ec5..f82e865 100644 --- a/chorus/src/main/java/org/pitest/voices/URLModelFetcher.java +++ b/model_downloader/src/main/java/org/pitest/voices/download/URLModelFetcher.java @@ -1,4 +1,4 @@ -package org.pitest.voices; +package org.pitest.voices.download; import me.tongfei.progressbar.ProgressBar; import me.tongfei.progressbar.ProgressBarBuilder; diff --git a/chorus/src/main/java/org/pitest/voices/UsModels.java b/model_downloader/src/main/java/org/pitest/voices/download/UsModels.java similarity index 91% rename from chorus/src/main/java/org/pitest/voices/UsModels.java rename to model_downloader/src/main/java/org/pitest/voices/download/UsModels.java index d3c54da..80580c9 100644 --- a/chorus/src/main/java/org/pitest/voices/UsModels.java +++ b/model_downloader/src/main/java/org/pitest/voices/download/UsModels.java @@ -1,8 +1,10 @@ -package org.pitest.voices; +package org.pitest.voices.download; -import org.pitest.g2p.core.Language; -import static org.pitest.voices.Models.url; +import org.pitest.voices.Language; +import org.pitest.voices.Model; + +import static org.pitest.voices.download.Models.url; public class UsModels { diff --git a/model_downloader/src/test/java/org/pitest/voices/download/ModelsTest.java b/model_downloader/src/test/java/org/pitest/voices/download/ModelsTest.java new file mode 100644 index 0000000..e951d36 --- /dev/null +++ b/model_downloader/src/test/java/org/pitest/voices/download/ModelsTest.java @@ -0,0 +1,61 @@ +package org.pitest.voices.download; + +import org.junit.jupiter.api.Test; +import org.pitest.voices.ChorusConfig; +import org.pitest.voices.Language; +import org.pitest.voices.Model; +import org.pitest.voices.g2p.core.Dictionary; + +import java.io.IOException; +import java.nio.file.Path; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.*; + +class ModelsTest { + + static final int ARBITRARY_SIZE = 1000; + Path cache = ChorusConfig.chorusConfig(Dictionary.empty()).base(); + + @Test + void coriHigh() throws IOException { + var model = Models.coriHigh(); + checkModel(model); + } + + @Test + void albaMedium() throws IOException { + var model = Models.albaMedium(); + checkModel(model); + } + + @Test + void alanMedium() throws IOException { + var model = Models.alanMedium(); + checkModel(model); + } + + @Test + void jennyDiocoMedium() throws IOException { + var model = Models.jennyDiocoMedium(); + checkModel(model); + } + + @Test + void sweetbbakAmy() throws IOException { + var model = Models.sweetbbakAmy(); + checkModel(model); + } + + @Test + void northernEnglishMale() throws IOException { + var model = Models.northernEnglishMale(); + checkModel(model); + } + + private void checkModel(Model model) throws IOException { + assertThat(model.byteBuffer(cache)).hasSizeGreaterThan(ARBITRARY_SIZE); + assertThat(model.resolveConfig(cache).sampleRate()).isEqualTo(22050L); + assertThat(model.language()).isEqualTo(Language.en_GB); + } +} \ No newline at end of file diff --git a/model_downloader/src/test/java/org/pitest/voices/download/NonEnglishModelsTest.java b/model_downloader/src/test/java/org/pitest/voices/download/NonEnglishModelsTest.java new file mode 100644 index 0000000..9064ab1 --- /dev/null +++ b/model_downloader/src/test/java/org/pitest/voices/download/NonEnglishModelsTest.java @@ -0,0 +1,37 @@ +package org.pitest.voices.download; + +import org.junit.jupiter.api.Test; +import org.pitest.voices.ChorusConfig; +import org.pitest.voices.Language; +import org.pitest.voices.Model; +import org.pitest.voices.g2p.core.Dictionary; + +import java.io.IOException; +import java.nio.file.Path; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.pitest.voices.download.ModelsTest.ARBITRARY_SIZE; + +class NonEnglishModelsTest { + + Path cache = ChorusConfig.chorusConfig(Dictionary.empty()).base(); + + @Test + void frFRSiwis() throws IOException { + var model = NonEnglishModels.frFRSiwis(); + checkModel(model); + assertThat(model.language()).isEqualTo(Language.fr_FR); + } + + @Test + void nlNLRonnie() throws IOException { + var model = NonEnglishModels.nlNLRonnie(); + checkModel(model); + assertThat(model.language()).isEqualTo(Language.nl_NL); + } + + private void checkModel(Model model) throws IOException { + assertThat(model.byteBuffer(cache)).hasSizeGreaterThan(ARBITRARY_SIZE); + assertThat(model.resolveConfig(cache).sampleRate()).isEqualTo(22050L); + } +} \ No newline at end of file diff --git a/model_downloader/src/test/java/org/pitest/voices/download/UsModelsTest.java b/model_downloader/src/test/java/org/pitest/voices/download/UsModelsTest.java new file mode 100644 index 0000000..6826003 --- /dev/null +++ b/model_downloader/src/test/java/org/pitest/voices/download/UsModelsTest.java @@ -0,0 +1,95 @@ +package org.pitest.voices.download; + +import org.junit.jupiter.api.Test; +import org.pitest.voices.ChorusConfig; +import org.pitest.voices.Language; +import org.pitest.voices.Model; +import org.pitest.voices.g2p.core.Dictionary; + +import java.io.IOException; +import java.nio.file.Path; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.pitest.voices.download.ModelsTest.ARBITRARY_SIZE; + +class UsModelsTest { + Path cache = ChorusConfig.chorusConfig(Dictionary.empty()).base(); + + @Test + void amyMedium() throws IOException { + var model = UsModels.amyMedium(); + checkModel(model); + } + + @Test + void bryceMedium() throws IOException { + var model = UsModels.bryceMedium(); + checkModel(model); + } + + @Test + void hfcFemaleMedium() throws IOException { + var model = UsModels.hfcFemaleMedium(); + checkModel(model); + } + + @Test + void hfcMaleMedium() throws IOException { + var model = UsModels.hfcMaleMedium(); + checkModel(model); + } + + @Test + void joeMedium() throws IOException { + var model = UsModels.joeMedium(); + checkModel(model); + } + + @Test + void johnMedium() throws IOException { + var model = UsModels.johnMedium(); + checkModel(model); + } + + @Test + void kristinMedium() throws IOException { + var model = UsModels.kristinMedium(); + checkModel(model); + } + + @Test + void kusalMedium() throws IOException { + var model = UsModels.kusalMedium(); + checkModel(model); + } + + @Test + void lessacHigh() throws IOException { + var model = UsModels.lessacHigh(); + checkModel(model); + } + + @Test + void normanMedium() throws IOException { + var model = UsModels.normanMedium(); + checkModel(model); + } + + @Test + void ryanHigh() throws IOException { + var model = UsModels.ryanHigh(); + checkModel(model); + } + + @Test + void samMedium() throws IOException { + var model = UsModels.samMedium(); + checkModel(model); + } + + private void checkModel(Model model) throws IOException { + assertThat(model.byteBuffer(cache)).hasSizeGreaterThan(ARBITRARY_SIZE); + assertThat(model.resolveConfig(cache).sampleRate()).isEqualTo(22050L); + assertThat(model.language()).isEqualTo(Language.en_US); + } +} \ No newline at end of file diff --git a/openvoice/pom.xml b/openvoice/pom.xml index df362fe..c6db385 100644 --- a/openvoice/pom.xml +++ b/openvoice/pom.xml @@ -26,17 +26,15 @@ org.pitest.voices - g2p + chorus ${project.parent.version} org.pitest.voices - chorus - ${project.parent.version} + openvoice-model + ${models.version} - - com.microsoft.onnxruntime onnxruntime @@ -49,7 +47,12 @@ slf4j-simple 2.0.16 - + + org.pitest.voices + alba + ${project.parent.version} + test + org.pitest.voices chorus diff --git a/openvoice/src/main/java/org/pitest/voices/openvoice/ONNXModel.java b/openvoice/src/main/java/org/pitest/voices/openvoice/ONNXModel.java deleted file mode 100644 index cd25aa3..0000000 --- a/openvoice/src/main/java/org/pitest/voices/openvoice/ONNXModel.java +++ /dev/null @@ -1,28 +0,0 @@ -package org.pitest.voices.openvoice; - -import org.pitest.voices.ModelFetcher; - -import java.io.IOException; -import java.nio.file.Files; -import java.nio.file.Path; - -public class ONNXModel { - - private final ModelFetcher remote; - private final String local; - - public ONNXModel(ModelFetcher remote, String local) { - this.remote = remote; - this.local = local; - } - - public Path resolve(Path cacheBase) throws IOException { - Path location = cacheBase.resolve(this.local); - Path onnx = location.resolve("model.onnx"); - if (!Files.exists(onnx)) { - Path tempLocation = remote.fetch(); - Files.move(tempLocation.getParent(), location); - } - return onnx; - } -} diff --git a/openvoice/src/main/java/org/pitest/voices/openvoice/OpenVoiceModel.java b/openvoice/src/main/java/org/pitest/voices/openvoice/OpenVoiceModel.java index c5cde0f..6bef4bc 100644 --- a/openvoice/src/main/java/org/pitest/voices/openvoice/OpenVoiceModel.java +++ b/openvoice/src/main/java/org/pitest/voices/openvoice/OpenVoiceModel.java @@ -3,11 +3,11 @@ import ai.onnxruntime.OrtEnvironment; import ai.onnxruntime.OrtException; import ai.onnxruntime.OrtSession; -import org.pitest.g2p.core.Dictionary; -import org.pitest.g2p.core.G2PModel; -import org.pitest.g2p.core.pos.Pos; -import org.pitest.g2p.core.tracing.Trace; -import org.pitest.g2p.core.Language; +import org.pitest.voices.Language; +import org.pitest.voices.g2p.core.Dictionary; +import org.pitest.voices.g2p.core.G2PModel; +import org.pitest.voices.g2p.core.pos.Pos; +import org.pitest.voices.g2p.core.tracing.Trace; public class OpenVoiceModel implements G2PModel { diff --git a/openvoice/src/main/java/org/pitest/voices/openvoice/OpenVoiceSupplier.java b/openvoice/src/main/java/org/pitest/voices/openvoice/OpenVoiceSupplier.java index 707db26..bbfe7e3 100644 --- a/openvoice/src/main/java/org/pitest/voices/openvoice/OpenVoiceSupplier.java +++ b/openvoice/src/main/java/org/pitest/voices/openvoice/OpenVoiceSupplier.java @@ -2,34 +2,22 @@ import ai.onnxruntime.OrtEnvironment; import ai.onnxruntime.OrtSession; -import org.pitest.g2p.core.Dictionary; -import org.pitest.g2p.core.G2PModel; +import org.pitest.voices.g2p.core.Dictionary; +import org.pitest.voices.g2p.core.G2PModel; +import org.pitest.voices.Resource; import org.pitest.voices.G2PModelSupplier; -import org.pitest.voices.URLModelFetcher; import java.nio.file.Path; import java.util.function.Supplier; public class OpenVoiceSupplier implements G2PModelSupplier { - private final ONNXModel model; - - - public OpenVoiceSupplier() { - this(new ONNXModel(URLModelFetcher.fromString("https://huggingface.co/OpenVoiceOS/g2p-mbyt5-12l-ipa-childes-espeak-onnx/resolve/main/fdemelo_g2p-mbyt5-12l-ipa-childes-espeak.onnx"), - "fdemelo_g2p-mbyt5-12l-ipa-childes-espeak")); - } - - public OpenVoiceSupplier(ONNXModel model) { - this.model = model; - } - @Override public G2PModel create(Supplier options, Dictionary dictionary, OrtEnvironment env, Path base) { try { - Path onnx = model.resolve(base); + var model = Resource.readAsBytes("/models/fdemelo_g2p-mbyt5-12l-ipa-childes-espeak.onnx"); var opts = options.get(); - var session = env.createSession(onnx.toString(), opts); + var session = env.createSession(model, opts); return new OpenVoiceModel(dictionary, session, env); } catch (Exception e) { throw new RuntimeException(e); diff --git a/openvoice/src/test/java/org/pitest/voices/openvoice/OpenVoiceModelTest.java b/openvoice/src/test/java/org/pitest/voices/openvoice/OpenVoiceModelTest.java index d14d064..e6e1c4f 100644 --- a/openvoice/src/test/java/org/pitest/voices/openvoice/OpenVoiceModelTest.java +++ b/openvoice/src/test/java/org/pitest/voices/openvoice/OpenVoiceModelTest.java @@ -3,11 +3,11 @@ import ai.onnxruntime.OrtEnvironment; import ai.onnxruntime.OrtSession; import org.junit.jupiter.api.Test; -import org.pitest.g2p.core.Dictionary; -import org.pitest.g2p.core.G2PModel; -import org.pitest.g2p.core.Language; -import org.pitest.g2p.core.pos.Pos; -import org.pitest.g2p.core.tracing.Trace; +import org.pitest.voices.Language; +import org.pitest.voices.g2p.core.Dictionary; +import org.pitest.voices.g2p.core.G2PModel; +import org.pitest.voices.g2p.core.pos.Pos; +import org.pitest.voices.g2p.core.tracing.Trace; import java.nio.file.Path; diff --git a/openvoice/src/test/java/org/pitest/voices/openvoice/TryItOutTest.java b/openvoice/src/test/java/org/pitest/voices/openvoice/TryItOutTest.java index 97ee4aa..db4fabb 100644 --- a/openvoice/src/test/java/org/pitest/voices/openvoice/TryItOutTest.java +++ b/openvoice/src/test/java/org/pitest/voices/openvoice/TryItOutTest.java @@ -1,13 +1,12 @@ package org.pitest.voices.openvoice; import org.junit.jupiter.api.Test; -import org.pitest.g2p.core.Dictionary; -import org.pitest.g2p.core.tracing.LoggingTrace; -import org.pitest.g2p.util.Resource; +import org.pitest.voices.alba.Alba; +import org.pitest.voices.g2p.core.Dictionary; +import org.pitest.voices.g2p.core.tracing.LoggingTrace; +import org.pitest.voices.Resource; import org.pitest.voices.Chorus; import org.pitest.voices.ChorusConfig; -import org.pitest.voices.Models; -import org.pitest.voices.NonEnglishModels; import org.pitest.voices.Voice; import org.pitest.voices.audio.Audio; @@ -23,7 +22,7 @@ class TryItOutTest { @Test void orwell() { try (Chorus chorus = new Chorus(config)) { - Voice v1 = chorus.voice(Models.albaMedium()); + Voice v1 = chorus.voice(Alba.albaMedium()); var audio = v1.say("It was a bright cold day in April, and the clocks were striking thirteen."); play(audio); } @@ -32,26 +31,8 @@ void orwell() { @Test void interruptedSpeech() { try (Chorus chorus = new Chorus(config)) { - Voice v1 = chorus.voice(Models.albaMedium()); - var audio = v1.say("\"That's not what I---\""); - play(audio); - } - } - - @Test - void frFR() { - try (Chorus chorus = new Chorus(config)) { - Voice v1 = chorus.voice(NonEnglishModels.frFRSiwis()); - var audio = v1.say("Je parle français"); - play(audio); - } - } - - @Test - void nlNL() { - try (Chorus chorus = new Chorus(config)) { - Voice v1 = chorus.voice(NonEnglishModels.nlNLRonnie()); - var audio = v1.say("Hallo daar"); + Voice v1 = chorus.voice(Alba.albaMedium()); + var audio = v1.say("That's not what I---"); play(audio); } } @@ -59,7 +40,7 @@ void nlNL() { @Test void mobyDick() { try (Chorus chorus = new Chorus(config)) { - Voice v1 = chorus.voice(Models.albaMedium()); + Voice v1 = chorus.voice(Alba.albaMedium()); String text = String.join("\n", Resource.read("/samples/moby_dick.md")); Audio audio = v1.say(text); diff --git a/pom.xml b/pom.xml index 8c88f35..9cd205e 100644 --- a/pom.xml +++ b/pom.xml @@ -17,6 +17,8 @@ dev-SNAPSHOT + + dev-SNAPSHOT UTF-8 UTF-8 hcoles/voices @@ -272,10 +274,16 @@ g2p + core + alba + bryce + cori chorus + model_downloader en_uk en_us openvoice + From 20ef28fe932c3adc75cdbc52736a62b98972b276 Mon Sep 17 00:00:00 2001 From: Henry Coles Date: Thu, 2 Oct 2025 18:42:12 +0100 Subject: [PATCH 3/4] update readme --- README.md | 52 ++++++++++++++++++++++++++++++++++++++-------------- pom.xml | 2 +- 2 files changed, 39 insertions(+), 15 deletions(-) diff --git a/README.md b/README.md index c64f9ce..b77ebfd 100644 --- a/README.md +++ b/README.md @@ -12,12 +12,14 @@ An easy-to-use local text to speech library for Java. It can produce reasonable quality audio using low-specced hardware. -It provides four main components +It provides several components -* Code to retrieve and run the voice models from the [piper](https://github.com/rhasspy/piper) project +* Code to run the voice models from the [piper](https://github.com/rhasspy/piper) project * A piper-compatible pure Java phonemizer for English partially ported from [phonemize](https://github.com/hans00/phonemize) * Compatible phoneme dictionaries for uk and us English * A multi-lingual phonemizer using the [onnx model](https://huggingface.co/OpenVoiceOS/g2p-mbyt5-12l-ipa-childes-espeak-onnx) from OpenVoiceOs +* A small number of piper models available as dependencies on maven central +* Code to download other models not uploaded to central The models are run using the onnxruntime library, so can utilise both CPU and GPU. @@ -25,22 +27,28 @@ The models are run using the onnxruntime library, so can utilise both CPU and GP See [Releases](https://github.com/hcoles/voices/releases) -## English-Only Usage +## English-Only Usage With Rules Based Phonemizer -Using Voices requires three dependencies +Using Voices requires three code dependencies and one or more models. ```xml org.pitest.voices chorus - 0.0.4 + 0.0.5 + + + + org.pitest.voices + alba + 0.0.5 org.pitest.voices en_uk - 0.0.4 + 0.0.5 @@ -50,7 +58,7 @@ Using Voices requires three dependencies ``` -Technically, Voices can be used without a dictionary, but the quality of the speech would be poor. +Technically, the rules based phonemizer can be used without a dictionary, but the quality of the speech would be poor. The `Chorus` class acts as a manager for voice models, handling loading and freeing of resources. Loading is an expensive operation, so it is recommended to keep a single instance of `Chorus` for the lifetime of your application. @@ -58,17 +66,33 @@ operation, so it is recommended to keep a single instance of `Chorus` for the li ```java ChorusConfig config = chorusConfig(EnUkDictionary.en_uk()); try (Chorus chorus = new Chorus(config)) { - Voice alba = chorus.voice(Models.albaMedium()); - Voice jenny = chorus.voice(Models.jennyDiocoMedium()); - - Audio audio = alba.say("Hello there, I'm vaguely Scottish"); - audio = audio.append(jenny.say("I'm not.")); - audio = audio.append(alba.withGain(0.5f).say("I am much quieter")); - + Voice alba = chorus.voice(Alba.albaMedium()); + + Audio audio = alba.say("Hello there, I'm vaguely Scottish."); audio.save(some path); } ``` +The example above uses a model retrieved at build time as a normal maven dependency. + +A wider range of models can be retrieved at runtime by adding the model downloader dependency. + +```xml + + org.pitest.voices + model-downloader + 0.0.5 + +``` + +Models can be retrieved using the factory methods on the + +* org.pitest.voices.download.Models +* org.pitest.voices.download.UsModels +* org.pitest.voices.download.NonEnglishModels + +Classes. + By default, voice models are downloaded to `~/.cache/voices/`, but this can be configured in ChorusConfig. ## Multi-lingual Usage diff --git a/pom.xml b/pom.xml index 9cd205e..b46ba6a 100644 --- a/pom.xml +++ b/pom.xml @@ -18,7 +18,7 @@ dev-SNAPSHOT - dev-SNAPSHOT + 0.0.1 UTF-8 UTF-8 hcoles/voices From 46af63fed7b26c923b87a676931e759b7099d143 Mon Sep 17 00:00:00 2001 From: Henry Coles Date: Thu, 2 Oct 2025 18:43:31 +0100 Subject: [PATCH 4/4] rename method --- alba/src/test/java/org/pitest/voices/alba/AlbaTest.java | 2 +- bryce/src/test/java/org/pitest/voices/bryce/BryceTest.java | 2 +- chorus/src/main/java/org/pitest/voices/Chorus.java | 2 +- core/src/main/java/org/pitest/voices/ClassPathModel.java | 2 +- core/src/main/java/org/pitest/voices/Model.java | 2 +- cori/src/test/java/org/pitest/voices/cori/CoriTest.java | 2 +- .../src/main/java/org/pitest/voices/download/FileModel.java | 2 +- .../src/test/java/org/pitest/voices/download/ModelsTest.java | 2 +- .../java/org/pitest/voices/download/NonEnglishModelsTest.java | 2 +- .../src/test/java/org/pitest/voices/download/UsModelsTest.java | 2 +- 10 files changed, 10 insertions(+), 10 deletions(-) diff --git a/alba/src/test/java/org/pitest/voices/alba/AlbaTest.java b/alba/src/test/java/org/pitest/voices/alba/AlbaTest.java index c452846..1d45663 100644 --- a/alba/src/test/java/org/pitest/voices/alba/AlbaTest.java +++ b/alba/src/test/java/org/pitest/voices/alba/AlbaTest.java @@ -21,7 +21,7 @@ void speaksProperEnglish() { @Test void loadsResources() throws IOException { - assertThat(underTest.byteBuffer(unused())).hasSizeGreaterThan(50000); + assertThat(underTest.asBytes(unused())).hasSizeGreaterThan(50000); assertThat(underTest.resolveConfig(unused()).sampleRate()).isEqualTo(22050L); } diff --git a/bryce/src/test/java/org/pitest/voices/bryce/BryceTest.java b/bryce/src/test/java/org/pitest/voices/bryce/BryceTest.java index d92e0c5..ea7b9c2 100644 --- a/bryce/src/test/java/org/pitest/voices/bryce/BryceTest.java +++ b/bryce/src/test/java/org/pitest/voices/bryce/BryceTest.java @@ -20,7 +20,7 @@ void speaksUSEnglish() { @Test void loadsResources() throws IOException { - assertThat(underTest.byteBuffer(unused())).hasSizeGreaterThan(50000); + assertThat(underTest.asBytes(unused())).hasSizeGreaterThan(50000); assertThat(underTest.resolveConfig(unused()).sampleRate()).isEqualTo(22050L); } diff --git a/chorus/src/main/java/org/pitest/voices/Chorus.java b/chorus/src/main/java/org/pitest/voices/Chorus.java index 5a0008f..8310d83 100644 --- a/chorus/src/main/java/org/pitest/voices/Chorus.java +++ b/chorus/src/main/java/org/pitest/voices/Chorus.java @@ -71,7 +71,7 @@ private VoiceSession loadPiperModel(Model model) { OrtEnvironment env = OrtEnvironment.getEnvironment(); try { var options = configureSession(); - var session = env.createSession(model.byteBuffer(conf.base()), options); + var session = env.createSession(model.asBytes(conf.base()), options); return new VoiceSession(env, model.resolveConfig(conf.base()), session); } catch (IOException | OrtException e) { throw new RuntimeException(e); diff --git a/core/src/main/java/org/pitest/voices/ClassPathModel.java b/core/src/main/java/org/pitest/voices/ClassPathModel.java index b109df2..21513a7 100644 --- a/core/src/main/java/org/pitest/voices/ClassPathModel.java +++ b/core/src/main/java/org/pitest/voices/ClassPathModel.java @@ -23,7 +23,7 @@ public ClassPathModel(String resource, Language lang, int sid, float gain) { } @Override - public byte[] byteBuffer(Path cacheBase) { + public byte[] asBytes(Path cacheBase) { return Resource.readAsBytes(resource); } diff --git a/core/src/main/java/org/pitest/voices/Model.java b/core/src/main/java/org/pitest/voices/Model.java index b69a0bf..3ccd03a 100644 --- a/core/src/main/java/org/pitest/voices/Model.java +++ b/core/src/main/java/org/pitest/voices/Model.java @@ -39,7 +39,7 @@ public interface Model { * @return Model as bytes * @throws IOException in event of error */ - byte[] byteBuffer(Path cacheBase) throws IOException; + byte[] asBytes(Path cacheBase) throws IOException; /** * The model config diff --git a/cori/src/test/java/org/pitest/voices/cori/CoriTest.java b/cori/src/test/java/org/pitest/voices/cori/CoriTest.java index 1123501..d12232c 100644 --- a/cori/src/test/java/org/pitest/voices/cori/CoriTest.java +++ b/cori/src/test/java/org/pitest/voices/cori/CoriTest.java @@ -20,7 +20,7 @@ void speaksProperEnglish() { @Test void loadsResources() throws IOException { - assertThat(underTest.byteBuffer(unused())).hasSizeGreaterThan(50000); + assertThat(underTest.asBytes(unused())).hasSizeGreaterThan(50000); assertThat(underTest.resolveConfig(unused()).sampleRate()).isEqualTo(22050L); } diff --git a/model_downloader/src/main/java/org/pitest/voices/download/FileModel.java b/model_downloader/src/main/java/org/pitest/voices/download/FileModel.java index 8835ae9..4c460d1 100644 --- a/model_downloader/src/main/java/org/pitest/voices/download/FileModel.java +++ b/model_downloader/src/main/java/org/pitest/voices/download/FileModel.java @@ -65,7 +65,7 @@ public Model withLanguage(Language lang) { } @Override - public byte[] byteBuffer(Path cacheBase) throws IOException { + public byte[] asBytes(Path cacheBase) throws IOException { return Files.readAllBytes(resolveFiles(cacheBase).resolve(onnx())); } diff --git a/model_downloader/src/test/java/org/pitest/voices/download/ModelsTest.java b/model_downloader/src/test/java/org/pitest/voices/download/ModelsTest.java index b4dc2a1..b286e4d 100644 --- a/model_downloader/src/test/java/org/pitest/voices/download/ModelsTest.java +++ b/model_downloader/src/test/java/org/pitest/voices/download/ModelsTest.java @@ -59,7 +59,7 @@ void northernEnglishMale() throws IOException { } private void checkModel(Model model) throws IOException { - assertThat(model.byteBuffer(cache)).hasSizeGreaterThan(ARBITRARY_SIZE); + assertThat(model.asBytes(cache)).hasSizeGreaterThan(ARBITRARY_SIZE); assertThat(model.resolveConfig(cache).sampleRate()).isEqualTo(22050L); assertThat(model.language()).isEqualTo(Language.en_GB); } diff --git a/model_downloader/src/test/java/org/pitest/voices/download/NonEnglishModelsTest.java b/model_downloader/src/test/java/org/pitest/voices/download/NonEnglishModelsTest.java index 9064ab1..59da5c6 100644 --- a/model_downloader/src/test/java/org/pitest/voices/download/NonEnglishModelsTest.java +++ b/model_downloader/src/test/java/org/pitest/voices/download/NonEnglishModelsTest.java @@ -31,7 +31,7 @@ void nlNLRonnie() throws IOException { } private void checkModel(Model model) throws IOException { - assertThat(model.byteBuffer(cache)).hasSizeGreaterThan(ARBITRARY_SIZE); + assertThat(model.asBytes(cache)).hasSizeGreaterThan(ARBITRARY_SIZE); assertThat(model.resolveConfig(cache).sampleRate()).isEqualTo(22050L); } } \ No newline at end of file diff --git a/model_downloader/src/test/java/org/pitest/voices/download/UsModelsTest.java b/model_downloader/src/test/java/org/pitest/voices/download/UsModelsTest.java index 6826003..5b5f5a0 100644 --- a/model_downloader/src/test/java/org/pitest/voices/download/UsModelsTest.java +++ b/model_downloader/src/test/java/org/pitest/voices/download/UsModelsTest.java @@ -88,7 +88,7 @@ void samMedium() throws IOException { } private void checkModel(Model model) throws IOException { - assertThat(model.byteBuffer(cache)).hasSizeGreaterThan(ARBITRARY_SIZE); + assertThat(model.asBytes(cache)).hasSizeGreaterThan(ARBITRARY_SIZE); assertThat(model.resolveConfig(cache).sampleRate()).isEqualTo(22050L); assertThat(model.language()).isEqualTo(Language.en_US); }