Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

StyleMeta::r#typeを追加し、トークという区分を実装に導入する #761

Merged
merged 53 commits into from
May 6, 2024
Merged
Show file tree
Hide file tree
Changes from 45 commits
Commits
Show all changes
53 commits
Select commit Hold shift + click to select a range
ca6ce4a
VVMが持つトーク用モデルをオプショナルにする
qryxip Mar 7, 2024
599cd13
`OptionFuture`を使う
qryxip Mar 8, 2024
6cca5d0
"talk"という名前を実装に導入する
qryxip Mar 8, 2024
cb2637a
`voicevox_core_macros`のdocを更新
qryxip Mar 9, 2024
d0cc720
`InferenceDomainSet`の導入
qryxip Mar 9, 2024
4c80605
アイテムを移動
qryxip Mar 9, 2024
60d2517
Minor refactor
qryxip Mar 9, 2024
098b139
`Optional<A>`を導入
qryxip Mar 10, 2024
704b5f5
`StyleMeta::r#type`を追加
qryxip Mar 10, 2024
cd9ad03
`ByInferenceDomain` → `InferenceDomainMap(Impl)`
qryxip Mar 10, 2024
dcc5ee1
fixup! `StyleMeta::r#type`を追加
qryxip Mar 10, 2024
e4d3eec
`InferenceDomain`の不在に対してのエラーを作成
qryxip Mar 10, 2024
9251c25
`S: InferenceDomainGroup` → `G: InferenceDomainGroup`
qryxip Mar 10, 2024
0e24fb2
docとパニックメッセージを更新
qryxip Mar 10, 2024
1e12019
`model_inner_ids`を`InferenceDomain`ごとに持つ
qryxip Mar 10, 2024
051d181
voicevox_core.hをアップデート
qryxip Mar 10, 2024
fd2905b
`MissingModelData`に対するPythonとJavaの定義をする
qryxip Mar 10, 2024
ad47904
`ids_for`で`type`をチェックする
qryxip Mar 10, 2024
c272d3d
Minor refactor
qryxip Mar 10, 2024
e46cbd4
Minor refactor
qryxip Mar 10, 2024
0403753
Minor refactor
qryxip Mar 10, 2024
1894acd
Minor refactor
qryxip Mar 11, 2024
ca4be74
`ensure_acceptable`での`StyleType`の列挙を`.unique()`
qryxip Mar 11, 2024
7ccb2a3
`BTreeMap`に直接`InferenceDomainAssociation`を`impl`しない
qryxip Mar 14, 2024
2521744
`StyleIdToModelInnerId`をnewtype化する
qryxip Mar 14, 2024
26f254c
`InferenceDomainAssociationTargetFunction::try_ref_map` → `apply`
qryxip Mar 16, 2024
5df5a07
残りの`StyleType`を追加
qryxip Mar 16, 2024
6543aa2
Merge branch 'main' into make-talk-model-set-optional
qryxip Mar 30, 2024
22d53f9
"Association" → "MapValueProjection"
qryxip Mar 30, 2024
2a2f273
`model_bytes_or_sessions`を`bool`のmapに変える
qryxip Mar 30, 2024
2bb8b82
`ForAllInferenceDomain`をempty typeに
qryxip Mar 30, 2024
5eeb975
コメントをdocstringとして書く
qryxip Mar 30, 2024
28382c8
Merge branch 'main' into make-talk-model-set-optional
qryxip Apr 9, 2024
1bb6f45
status.rsをinfer/下から引っ張り出す
qryxip Apr 13, 2024
1a51947
`Status`がDomainの区分を知るようにする
qryxip Apr 13, 2024
e6493d2
`InferenceDomainGroup`や`InferenceDomainMapValueProjection`を解体
qryxip Apr 13, 2024
ba81714
dead code削除
qryxip Apr 13, 2024
de9a919
文字列リテラルのインデントを修正
qryxip Apr 14, 2024
b30abc7
`by_domain` → `session_sets_with_inner_ids`
qryxip Apr 14, 2024
bcdc1a8
Domainの区分に触れる部分をできるだけ隔離
qryxip Apr 14, 2024
de63365
`ModelBytesByDomain` → `ModelBytesWithInnerIdsByDomain`
qryxip Apr 14, 2024
430a548
余計なwhere clauseを削除
qryxip Apr 15, 2024
66a9924
Merge branch 'main' into make-talk-model-set-optional
qryxip Apr 16, 2024
07126c6
Merge branch 'main' into make-talk-model-set-optional
qryxip Apr 30, 2024
4270a1c
Merge branch 'main' into make-talk-model-set-optional
qryxip May 3, 2024
734564f
`SessionSet`と`SessionCell`に接頭辞"Inference"を付ける
qryxip May 5, 2024
43fa77d
PythonとJavaの`StyleType`がSCREAMING_SNAKE_CASEになっていたので修正
qryxip May 5, 2024
678004b
`style_type`のチェックは`VoiceModelHeader`の構築時に行う
qryxip May 5, 2024
ba3d230
`check_acceptable`にdocを書く
qryxip May 5, 2024
7311323
Fix up `accepts`
qryxip May 5, 2024
32e20bc
スタイル判定にテスト
qryxip May 5, 2024
7fd730d
`ensure_acceptable`にFIXME
qryxip May 5, 2024
275d3d4
docを書くようTODOを残す
qryxip May 6, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion crates/voicevox_core/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,10 @@ open_jtalk.workspace = true
ouroboros.workspace = true
rayon.workspace = true
regex.workspace = true
serde = { workspace = true, features = ["derive"] }
serde = { workspace = true, features = ["derive", "rc"] }
serde_json = { workspace = true, features = ["preserve_order"] }
smallvec.workspace = true
strum = { workspace = true, features = ["derive"] }
tempfile.workspace = true
thiserror.workspace = true
tokio = { workspace = true, features = ["rt"] } # FIXME: feature-gateする
Expand Down
20 changes: 15 additions & 5 deletions crates/voicevox_core/src/error.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
use crate::{
engine::{FullContextLabelError, KanaParseError},
user_dict::InvalidWordError,
StyleId, VoiceModelId,
StyleId, StyleType, VoiceModelId,
};
//use engine::
use duplicate::duplicate_item;
use std::path::PathBuf;
use itertools::Itertools as _;
use std::{collections::BTreeSet, path::PathBuf};
use thiserror::Error;
use uuid::Uuid;

Expand Down Expand Up @@ -38,6 +39,7 @@ impl Error {
LoadModelErrorKind::ReadZipEntry { .. } => ErrorKind::ReadZipEntry,
LoadModelErrorKind::ModelAlreadyLoaded { .. } => ErrorKind::ModelAlreadyLoaded,
LoadModelErrorKind::StyleAlreadyLoaded { .. } => ErrorKind::StyleAlreadyLoaded,
LoadModelErrorKind::MissingModelData { .. } => ErrorKind::MissingModelData,
LoadModelErrorKind::InvalidModelData => ErrorKind::InvalidModelData,
},
ErrorRepr::GetSupportedDevices(_) => ErrorKind::GetSupportedDevices,
Expand Down Expand Up @@ -70,10 +72,14 @@ pub(crate) enum ErrorRepr {
GetSupportedDevices(#[source] anyhow::Error),

#[error(
"`{style_id}`に対するスタイルが見つかりませんでした。音声モデルが読み込まれていないか、読\
み込みが解除されています"
"`{style_id}` ([{style_types}])に対するスタイルが見つかりませんでした。音声モデルが\
読み込まれていないか、読み込みが解除されています",
style_types = style_types.iter().format(", ")
)]
StyleNotFound { style_id: StyleId },
StyleNotFound {
style_id: StyleId,
style_types: &'static BTreeSet<StyleType>,
},

#[error(
"`{model_id}`に対する音声モデルが見つかりませんでした。読み込まれていないか、読み込みが既\
Expand Down Expand Up @@ -121,6 +127,8 @@ pub enum ErrorKind {
ModelAlreadyLoaded,
/// すでに読み込まれているスタイルを読み込もうとした。
StyleAlreadyLoaded,
/// モデルデータが見つからなかった。
MissingModelData,
/// 無効なモデルデータ。
InvalidModelData,
/// サポートされているデバイス情報取得に失敗した。
Expand Down Expand Up @@ -169,6 +177,8 @@ pub(crate) enum LoadModelErrorKind {
ModelAlreadyLoaded { id: VoiceModelId },
#[display(fmt = "スタイル`{id}`は既に読み込まれています")]
StyleAlreadyLoaded { id: StyleId },
#[display(fmt = "`{style_type}`に対応するモデルデータがありませんでした")]
MissingModelData { style_type: StyleType },
#[display(fmt = "モデルデータを読むことができませんでした")]
InvalidModelData,
}
20 changes: 14 additions & 6 deletions crates/voicevox_core/src/infer.rs
Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@
pub(crate) mod domain;
pub(crate) mod domains;
mod model_file;
pub(crate) mod runtimes;
pub(crate) mod status;
pub(crate) mod session_set;

use std::{borrow::Cow, fmt::Debug};
use std::{borrow::Cow, collections::BTreeSet, fmt::Debug};

use derive_new::new;
use duplicate::duplicate_item;
use enum_map::{Enum, EnumMap};
use ndarray::{Array, ArrayD, Dimension, ShapeError};
use thiserror::Error;

use crate::SupportedDevices;
use crate::{StyleType, SupportedDevices};

pub(crate) trait InferenceRuntime: 'static {
type Session: Sized + Send + 'static;
Expand All @@ -32,9 +32,17 @@ pub(crate) trait InferenceRuntime: 'static {
fn run(ctx: Self::RunContext<'_>) -> anyhow::Result<Vec<OutputTensor>>;
}

/// ある`VoiceModel`が提供する推論操作の集合を示す
pub(crate) trait InferenceDomain {
/// 共に扱われるべき推論操作の集合を示す
pub(crate) trait InferenceDomain: Sized {
type Operation: InferenceOperation;

/// 対応する`StyleType`。
///
/// 複数の`InferenceDomain`に対応する`StyleType`があってもよい。
///
/// また、どの`InferenceDomain`にも属さない`StyleType`があってもよい。そのような`StyleType`は
/// 音声モデルのロード時に単に拒否されるべきである。
fn style_types() -> &'static BTreeSet<StyleType>;
}

/// `InferenceDomain`の推論操作を表す列挙型。
Expand Down
22 changes: 22 additions & 0 deletions crates/voicevox_core/src/infer/domains.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
mod talk;

pub(crate) use self::talk::{
DecodeInput, DecodeOutput, PredictDurationInput, PredictDurationOutput, PredictIntonationInput,
PredictIntonationOutput, TalkDomain, TalkOperation,
};

pub(crate) struct InferenceDomainMap<V: InferenceDomainMapValues + ?Sized> {
pub(crate) talk: V::Talk,
}

pub(crate) trait InferenceDomainMapValues {
type Talk;
}

impl<T> InferenceDomainMapValues for (T,) {
type Talk = T;
}

impl<A> InferenceDomainMapValues for [A] {
type Talk = A;
}
Original file line number Diff line number Diff line change
@@ -1,22 +1,32 @@
use std::collections::BTreeSet;

use enum_map::Enum;
use macros::{InferenceInputSignature, InferenceOperation, InferenceOutputSignature};
use ndarray::{Array0, Array1, Array2};
use once_cell::sync::Lazy;

use crate::StyleType;

use super::{
use super::super::{
InferenceDomain, InferenceInputSignature as _, InferenceOutputSignature as _, OutputTensor,
};

pub(crate) enum InferenceDomainImpl {}
pub(crate) enum TalkDomain {}

impl InferenceDomain for TalkDomain {
type Operation = TalkOperation;

impl InferenceDomain for InferenceDomainImpl {
type Operation = InferenceOperationImpl;
fn style_types() -> &'static BTreeSet<StyleType> {
static STYLE_TYPES: Lazy<BTreeSet<StyleType>> = Lazy::new(|| [StyleType::Talk].into());
&STYLE_TYPES
}
Comment on lines +19 to +22
Copy link
Member Author

@qryxip qryxip Mar 11, 2024

Choose a reason for hiding this comment

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

status.rsの以下の2箇所で使われる。この2箇所が、VVM内のStyleTypeに対する検査となる。

style_typesの実装としてはStyleTypeが不足しててもよいし(上記2箇所を通らなくなって全部弾かれるようになるだけなので)、InferenceDomain間で重複していてもよい (e.g. 今後StyleType::SingSingingTeacherDomain::style_typesFrameDecodeDomain::style_typesに含まれる)。

(edit) StyleType::{SingingTeacher,FrameDecode,Sing}だけ追加しました。実際に"type": "singing_teacher"のようにmetas.jsonを書いた場合、ensure_acceptableで確実に弾かれるようになっています。

}

#[derive(Clone, Copy, Enum, InferenceOperation)]
#[inference_operation(
type Domain = InferenceDomainImpl;
type Domain = TalkDomain;
)]
pub(crate) enum InferenceOperationImpl {
pub(crate) enum TalkOperation {
#[inference_operation(
type Input = PredictDurationInput;
type Output = PredictDurationOutput;
Expand Down
102 changes: 102 additions & 0 deletions crates/voicevox_core/src/infer/session_set.rs
Copy link
Member Author

Choose a reason for hiding this comment

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

status.rsから分離。

Copy link
Member Author

Choose a reason for hiding this comment

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

このファイルレベルのコメントがoutdateになるやつ、未だによくわからない。何やってもoutdatedになるってこと…?

Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
use std::{collections::HashMap, fmt::Display, marker::PhantomData, sync::Arc};

use anyhow::bail;
use enum_map::{Enum as _, EnumMap};
use itertools::Itertools as _;

use crate::error::ErrorRepr;

use super::{
model_file, InferenceDomain, InferenceInputSignature, InferenceOperation, InferenceRuntime,
InferenceSessionOptions, InferenceSignature, ParamInfo,
};

pub(crate) struct SessionSet<R: InferenceRuntime, D: InferenceDomain>(
Copy link
Member

Choose a reason for hiding this comment

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

このSessionが何を指すか、初見だとわからないかもと思いました!
僕たちはonnxruntime.Sessionで見慣れてますが、なにか違うものだと勘違いするかも。

例えばInferenceSessionSetにするのはどうでしょう。SessionCellも同じく。
あとSessionとは何なのかの一言ドキュメントがあれば追いやすいのかなと思いました。まあ・・・説明難しいのですが・・・。

Copy link
Member Author

Choose a reason for hiding this comment

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

これまではstatus.rsの中だけで使ってたので名前を簡潔にしていたのですが、pub(crates)にするなら"Inference"は付けた方が確かにいいですね。
734564f (#761)

"session"については語感でなんとなく把握してもらえないかなと思っています。とはいってもTFLiteとかに手を出すときは整理をつける必要がありますし、infer/下の諸々と一緒にドキュメントは整備したい感はあります。

Copy link
Member

Choose a reason for hiding this comment

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

たしかにinfer以下にあれば察しはつくかもですね。

あーでも1回の推論ごとに1回セッションができると勘違いしてコード読み進めちゃう可能性はありそう。
モデル(Operation?)と対応する概念だという説明はまあなくても読んでればわかるけどあった方が良さそう。

Copy link
Member Author

Choose a reason for hiding this comment

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

まああった方はよいですね。TODOを残しました。
275d3d4 (#761)

EnumMap<D::Operation, Arc<std::sync::Mutex<R::Session>>>,
);

impl<R: InferenceRuntime, D: InferenceDomain> SessionSet<R, D> {
pub(crate) fn new(
model_bytes: &EnumMap<D::Operation, Vec<u8>>,
options: &EnumMap<D::Operation, InferenceSessionOptions>,
) -> anyhow::Result<Self> {
let mut sessions = model_bytes
.iter()
.map(|(op, model_bytes)| {
let (expected_input_param_infos, expected_output_param_infos) =
<D::Operation as InferenceOperation>::PARAM_INFOS[op];

let (sess, actual_input_param_infos, actual_output_param_infos) =
R::new_session(|| model_file::decrypt(model_bytes), options[op])?;

check_param_infos(expected_input_param_infos, &actual_input_param_infos)?;
check_param_infos(expected_output_param_infos, &actual_output_param_infos)?;

Ok((op.into_usize(), std::sync::Mutex::new(sess).into()))
})
.collect::<anyhow::Result<HashMap<_, _>>>()?;

return Ok(Self(EnumMap::<D::Operation, _>::from_fn(|k| {
sessions.remove(&k.into_usize()).expect("should exist")
})));

fn check_param_infos<D: PartialEq + Display>(
expected: &[ParamInfo<D>],
actual: &[ParamInfo<D>],
) -> anyhow::Result<()> {
if !(expected.len() == actual.len()
&& itertools::zip_eq(expected, actual)
.all(|(expected, actual)| expected.accepts(actual)))
{
let expected = display_param_infos(expected);
let actual = display_param_infos(actual);
bail!("expected {{{expected}}}, got {{{actual}}}")
}
Ok(())
}

fn display_param_infos(infos: &[ParamInfo<impl Display>]) -> impl Display {
infos
.iter()
.map(|ParamInfo { name, dt, ndim }| {
let brackets = match *ndim {
Some(ndim) => "[]".repeat(ndim),
None => "[]...".to_owned(),
};
format!("{name}: {dt}{brackets}")
})
.join(", ")
}
}
}

impl<R: InferenceRuntime, D: InferenceDomain> SessionSet<R, D> {
pub(crate) fn get<I>(&self) -> SessionCell<R, I>
where
I: InferenceInputSignature,
I::Signature: InferenceSignature<Domain = D>,
{
SessionCell {
inner: self.0[I::Signature::OPERATION].clone(),
marker: PhantomData,
}
}
}

pub(crate) struct SessionCell<R: InferenceRuntime, I> {
inner: Arc<std::sync::Mutex<R::Session>>,
marker: PhantomData<fn(I)>,
}

impl<R: InferenceRuntime, I: InferenceInputSignature> SessionCell<R, I> {
pub(crate) fn run(
self,
input: I,
) -> crate::Result<<I::Signature as InferenceSignature>::Output> {
let inner = &mut self.inner.lock().unwrap();
let ctx = input.make_run_context::<R>(inner);
R::run(ctx)
.and_then(TryInto::try_into)
.map_err(|e| ErrorRepr::InferenceFailed(e).into())
}
}
Loading
Loading