Skip to main content

Crate voicevox_core

Crate voicevox_core 

Source
Expand description

無料で使える中品質なテキスト読み上げソフトウェア、VOICEVOXのコア。

§Feature flags

  • buildtime-download-onnxruntime: ビルド時に後述する環境変数VVCORE_BUILD_DOWNLOAD_AND_COPY_ORT1なら、ONNX Runtimeのバイナリをダウンロードしてtarget directory内の複数箇所に配置する。VVCORE_BUILD_DOWNLOAD_AND_COPY_ORT1ではないなら警告を出して何もしない。後述のlink-onnxruntimeフィーチャと合わせると、システムにONNX Runtimeが無くてもビルドが可能になる。
  • load-onnxruntime: ONNX Runtimeをdlopen/LoadLibraryExWで開く。CUDADirectMLが利用可能。
  • link-onnxruntime: ONNX Runtimeをロード時動的リンクする。そのためビルドするためにはシステムにONNX Runtimeがインストールされているか、buildtime-download-onnxruntimeによるダウンロードを行う必要がある。iOSのようなdlopenの利用が困難な環境でのみこちらを利用するべきである。Note: 動的リンク対象のライブラリ名onnxruntimeで固定。変更はpatchelf(1)install_name_tool(1)で行うこと。また、ONNX RuntimeのGPU機能を使うことは不可。

このクレートの利用にあたっては上記のload-onnxruntimelink-onnxruntimeのうちどちらかを有効にしなければならない。両方の有効化はコンパイルエラーとなる。Onnxruntimeの初期化方法はこれらのフィーチャによって決まる。

§Build time environment variables

  • VVCORE_BUILD_DOWNLOAD_AND_COPY_ORT: buildtime-download-onnxruntimeフィーチャが有効化されているときのみ機能する。1のとき、ONNX Runtimeのバイナリをダウンロードしてtarget directory内の複数箇所に配置する。buildtime-download-onnxruntimeフィーチャが無効化されているときは値が1であっても、警告のみを出しダウンロードは行わない。

§Examples

//! トーク

use std::{io::Write as _, panic};

use anyhow::Context as _;
use const_format::concatcp;

use voicevox_core::{
    blocking::{Onnxruntime, OpenJtalk, Synthesizer, VoiceModelFile},
    CharacterMeta, StyleMeta,
};

// ダウンローダーにて`onnxruntime`としてダウンロードできるもの
const VVORT: &str = concatcp!(
    "./voicevox_core/onnxruntime/lib/",
    Onnxruntime::LIB_VERSIONED_FILENAME,
);

// ダウンローダーにて`dict`としてダウンロードできるもの
const OJT_DIC: &str = "./voicevox_core/dict/open_jtalk_dic_utf_8-1.11";

// ダウンローダーにて`models`としてダウンロードできるもの
const VVM: &str = "./voicevox_core/models/vvms/0.vvm";

const TARGET_CHARACTER_NAME: &str = "ずんだもん";
const TARGET_STYLE_NAME: &str = "ノーマル";
const TEXT: &str = "こんにちは";

let synth = {
    let ort = Onnxruntime::load_once().filename(VVORT).perform()?;
    let ojt = OpenJtalk::new(OJT_DIC)?;
    Synthesizer::builder(ort).text_analyzer(ojt).build()?
};

dbg!(synth.is_gpu_mode());

synth
    .load_voice_model(&VoiceModelFile::open(VVM)?)
    .perform()?;

let StyleMeta { id: style_id, .. } = synth
    .metas()
    .into_iter()
    .filter(|CharacterMeta { name, .. }| name == TARGET_CHARACTER_NAME)
    .flat_map(|CharacterMeta { styles, .. }| styles)
    .find(|StyleMeta { name, .. }| name == TARGET_STYLE_NAME)
    .with_context(|| {
        format!("could not find \"{TARGET_CHARACTER_NAME} ({TARGET_STYLE_NAME})\"")
    })?;

eprintln!("Synthesizing");
let wav = &synth.tts(TEXT, style_id).perform()?;

eprintln!("Playing the WAV");
play(wav)?;

fn play(wav: &[u8]) -> anyhow::Result<()> {
    let tempfile = tempfile::Builder::new().suffix(".wav").tempfile()?;
    (&tempfile).write_all(wav)?;
    let tempfile = &tempfile.into_temp_path();
    open::that_in_background(tempfile)
        .join()
        .unwrap_or_else(|e| panic::resume_unwind(e))?;
    Ok(())
}
//! ソング

use std::{io::Write as _, panic};

use anyhow::Context as _;
use const_format::concatcp;

use voicevox_core::{
    blocking::{Onnxruntime, Synthesizer, VoiceModelFile},
    CharacterMeta, Score, StyleMeta, StyleType,
};

// ダウンローダーにて`onnxruntime`としてダウンロードできるもの
const VVORT: &str = concatcp!(
    "./voicevox_core/onnxruntime/lib/",
    Onnxruntime::LIB_VERSIONED_FILENAME,
);

// ダウンローダーにて`models`としてダウンロードできるもの
const VVM: &str = "./voicevox_core/models/vvms/s0.vvm";

const SINGING_TEACHER_CHARACTER_NAME: &str = "波音リツ";
const SINGING_TEACHER_STYLE_NAME: &str = "ノーマル";
const SINGER_CHARACTER_NAME: &str = "ずんだもん";
const SINGER_STYLE_NAME: &str = "ノーマル";

let synth = {
    let ort = Onnxruntime::load_once().filename(VVORT).perform()?;
    Synthesizer::builder(ort).build()?
};

dbg!(synth.is_gpu_mode());

synth
    .load_voice_model(&VoiceModelFile::open(VVM)?)
    .perform()?;

let metas = &synth.metas();
let find_style = |character_name, style_name, style_types: &[_]| {
    metas
        .iter()
        .filter(|CharacterMeta { name, .. }| name == character_name)
        .flat_map(|CharacterMeta { styles, .. }| styles)
        .find(|StyleMeta { name, r#type, .. }| {
            name == style_name && style_types.contains(r#type)
        })
        .map(|&StyleMeta { id, .. }| id)
        .with_context(|| format!("could not find \"{character_name} ({style_name})\""))
};

let singing_teacher = find_style(
    SINGING_TEACHER_CHARACTER_NAME,
    SINGING_TEACHER_STYLE_NAME,
    &[StyleType::SingingTeacher, StyleType::Sing],
)?;

let singer = find_style(
    SINGER_CHARACTER_NAME,
    SINGER_STYLE_NAME,
    &[StyleType::FrameDecode],
)?;

let score = &serde_json::from_str::<Score>(
    r#"
{
  "notes": [
    { "key": null, "frame_length": 15, "lyric": "" },
    { "key": 60, "frame_length": 45, "lyric": "ド" },
    { "key": 62, "frame_length": 45, "lyric": "レ" },
    { "key": 64, "frame_length": 45, "lyric": "ミ" },
    { "key": null, "frame_length": 15, "lyric": "" }
  ]
}
    "#,
)
.unwrap();

let frame_audio_query = &synth.create_sing_frame_audio_query(score, singing_teacher)?;

eprintln!("Synthesizing");
let wav = &synth
    .frame_synthesis(&frame_audio_query, singer)
    .perform()?;

eprintln!("Playing the WAV");
play(wav)?;

fn play(wav: &[u8]) -> anyhow::Result<()> {
    let tempfile = tempfile::Builder::new().suffix(".wav").tempfile()?;
    (&tempfile).write_all(wav)?;
    let tempfile = &tempfile.into_temp_path();
    open::that_in_background(tempfile)
        .join()
        .unwrap_or_else(|e| panic::resume_unwind(e))?;
    Ok(())
}

§音声の調整

ユーザーガイドのテキスト音声合成の流れを参照。

以下のwav1からwav4はすべて同一となる。

use std::collections::HashSet;

use voicevox_core::{
    blocking::{Synthesizer, TextAnalyzer},
    AudioQuery, StyleId,
};

fn f(synth: &Synthesizer<impl TextAnalyzer>) -> anyhow::Result<()> {
    const TEXT: &str = _;
    const STYLE_ID: StyleId = _;

    let wav1 = synth.tts(TEXT, STYLE_ID).perform()?;

    let wav2 = {
        let query = synth.create_audio_query(TEXT, STYLE_ID)?;
        synth.synthesis(&query, STYLE_ID).perform()?
    };

    let wav3 = {
        let phrases = synth.create_accent_phrases(TEXT, STYLE_ID)?;
        let query = AudioQuery::from(phrases);
        synth.synthesis(&query, STYLE_ID).perform()?
    };

    let wav4 = {
        let phrases = synth.text_analyzer().analyze(TEXT)?;
        let phrases = synth.replace_mora_data(&phrases, STYLE_ID)?;
        let query = AudioQuery::from(phrases);
        synth.synthesis(&query, STYLE_ID).perform()?
    };

    let wav5 = {
        let phrases = synth.text_analyzer().analyze(TEXT)?;
        let phrases = synth.replace_phoneme_length(&phrases, STYLE_ID)?;
        let phrases = synth.replace_mora_pitch(&phrases, STYLE_ID)?;
        let query = AudioQuery::from(phrases);
        synth.synthesis(&query, STYLE_ID).perform()?
    };

    assert_eq!(1, HashSet::from([wav1, wav2, wav3, wav4, wav5]).len());
    Ok(())
}

Modules§

__docdoc
blocking
ブロッキング版API。
nonblocking
非同期版API。

Macros§

key
定数からKeyをコンストラクトする。

Structs§

AccentPhrase
AccentPhrase (アクセント句ごとの情報)。
AudioQuery
AudioQuery (音声合成用のクエリ)。
CharacterMeta
キャラクターのメタ情報。
CharacterVersion
キャラクターのバージョン。
Error
VOICEVOX COREのエラー。
FrameAudioQuery
フレームごとの音声合成用のクエリ。
FramePhoneme
音素の情報。
Key
音階。
Mora
モーラ(子音+母音)ごとの情報。
Note
音符または休符。
NoteId
音符のID。
OptionalLyric
音符の歌詞、または休符を表わす無音
SamplingRate
サンプリングレート(Hz)。
Score
楽譜情報。
Sil
sil (silent)。
StyleId
スタイルID。
StyleMeta
スタイルのメタ情報。
SupportedDevices
利用可能なデバイスの情報。
UserDictWord
ユーザー辞書の単語。
UserDictWordBuilder
UserDictWordのビルダー。
VoiceModelId
音声モデルID。

Enums§

AccelerationMode
ハードウェアアクセラレーションモードを設定する設定値。
ErrorKind
エラーの種類。
OnExistingVoiceModelId
Synthesizer::load_voice_modelの実行時に、同じidVoiceModelFileが既に読み込まれていたときのふるまい。
Phoneme
音素。
StyleType
スタイルに対応するモデルの種類。
UserDictWordType
ユーザー辞書の単語の種類。

Constants§

VERSION
本クレートのpackage.version

Functions§

ensure_compatible
与えられた楽譜歌唱音声合成用のクエリの組み合わせが、基本周波数と音量の生成に利用できるかどうかを確認する。

Type Aliases§

Result
VoiceModelMeta
音声モデルのメタ情報。