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

FrameAudioQueryの変更を反映できるようにする #1961

Merged
merged 15 commits into from
Apr 12, 2024

Conversation

sigprogramming
Copy link
Contributor

内容

FrameAudioQueryの変更を反映できるようにします。

関連 Issue

ref #1941

その他

@sigprogramming sigprogramming marked this pull request as ready for review March 31, 2024 04:14
@sigprogramming sigprogramming requested a review from a team as a code owner March 31, 2024 04:14
@sigprogramming sigprogramming requested review from y-chan and removed request for a team March 31, 2024 04:14
Comment on lines -844 to -852
// TODO: 助詞や拗音の扱いはあとで考える
const lyric = note.lyric
.replace("じょ", "ジョ")
.replace("うぉ", "ウォ")
.replace("は", "ハ")
.replace("へ", "ヘ");
Copy link
Contributor Author

Choose a reason for hiding this comment

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

この処理は無くても問題ないと思うので削除しました。

Comment on lines +1135 to +1137
if (startRenderingRequested() || stopRenderingRequested()) {
return;
}
Copy link
Contributor Author

Choose a reason for hiding this comment

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

再レンダリング・レンダリング停止フラグのチェックは、歌い方の推論の直前(音声合成の直後)のみ行うようにしました。(時間がかかるのは音声合成なので、これで十分かなと思います)

@@ -906,7 +899,7 @@ export const singingStore = createPartialStore<SingingStoreTypes>({
frameAudioQuery: FrameAudioQuery
) => {
frameAudioQuery.volume = frameAudioQuery.volume.map((value) => {
return value * Math.pow(10, volumeRangeAdjustment / 20);
return value * decibelToLinear(volumeRangeAdjustment);
Copy link
Contributor Author

Choose a reason for hiding this comment

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

decibelToLinearを使用するようにしました。

Comment on lines 7 to 24
const createPhrase = (
start: number,
end: number,
state: PhraseState
): Phrase => {
return {
notes: [],
startTicks: start * DEFAULT_TPQN,
endTicks: end * DEFAULT_TPQN,
keyRangeAdjustment: 0,
volumeRangeAdjustment: 0,
notes: [
{
id: uuidv4(),
position: start * DEFAULT_TPQN,
duration: (end - start) * DEFAULT_TPQN,
noteNumber: 60,
lyric: "ド",
},
],
state,
tempos,
tpqn: DEFAULT_TPQN,
singer: {
engineId: EngineId("00000000-0000-0000-0000-000000000000"),
styleId: StyleId(0),
},
};
};
Copy link
Contributor Author

Choose a reason for hiding this comment

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

phrase.notesからstartTicksendTicksを計算する形に変更したので、ここの処理も変更しています。

Copy link
Member

@Hiroshiba Hiroshiba left a comment

Choose a reason for hiding this comment

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

ほぼLGTMです!!
細かいとこをいっぱいコメントしてしまいました 🙇

途中で気づいたのですが、singing style=歌い方、singing voice=歌声ですかね!
styleがエンジン側のスタイルと被ってしまうので、guideとかどうでしょう。

あと将来的に簡単なキャッシュ機構を持つクラスを定義してもいいかもと思いました!

src/store/type.ts Outdated Show resolved Hide resolved
src/store/singing.ts Outdated Show resolved Hide resolved
src/store/singing.ts Outdated Show resolved Hide resolved
Comment on lines +1026 to +1027
for (const [notesHash, foundPhrase] of foundPhrases) {
const phraseKey = notesHash;
Copy link
Member

Choose a reason for hiding this comment

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

keyでありhashであることを示す便利ワードか規則があるとこの辺りすっと書けそうですね!

Copy link
Contributor Author

Choose a reason for hiding this comment

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

良いワード・規則が思いつきませんでした…

src/store/singing.ts Outdated Show resolved Hide resolved
src/store/singing.ts Outdated Show resolved Hide resolved
src/store/singing.ts Outdated Show resolved Hide resolved
src/sing/domain.ts Outdated Show resolved Hide resolved
src/store/singing.ts Outdated Show resolved Hide resolved
src/sing/domain.ts Outdated Show resolved Hide resolved
Copy link
Member

@Hiroshiba Hiroshiba left a comment

Choose a reason for hiding this comment

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

実際に動かしてみたところ、キャラを変えて即時再生すると、レンダリング中の時に前のキャッシュが再生されるようになってるかもです!
(以前は確かダミーの音が流れてたはず?)

src/store/type.ts Show resolved Hide resolved
src/store/singing.ts Outdated Show resolved Hide resolved
Copy link
Member

@Hiroshiba Hiroshiba left a comment

Choose a reason for hiding this comment

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

LGTM!!!
(レビュー可能状態かわからないのですが、動かしてみた感じ問題なさそうだったので)

@sigprogramming さん的にレビューOKな状態であってたらマージで良さそう!
設計から実装まで大変だったと思います、おつです!!!!!

あ、@romot-co さん、 @sevenc-nanashi さん、 @y-chan さん、もしお手すきでしたら実行チェックをお願いできると心強いです! 🙏


もし可能ならもう少し小さめのPRだと嬉しいかもと思ったのですが、今回の変更を速度落とさずやるのは無理な気もしました。src/sing/domain.ts関数分けを別PRにするくらい・・・?
まだまだ硬さよりも速度優先で良いフェーズだと思うので、もしいけそうだったらくらいの気持ちです 🙏

@sigprogramming
Copy link
Contributor Author

レビューOKです、レビューありがとうございます!

今回データ構造を一度に大きく変更したので、差分も大きくなってしまいました。
次回からなるべく分割して、小さくなるようにします…!

@sigprogramming
Copy link
Contributor Author

sigprogramming commented Apr 8, 2024

用語、仕様、処理の流れを整理してみました。

用語、仕様、処理の流れ

用語

フレーズ(Phrase

  • 歌い方(のソースのハッシュ)を保持する
  • 歌声(のソースのハッシュ)を保持する

歌い方のソース(SingingGuideSource

  • 歌い方を生成するために必要なデータ

歌い方(SingingGuide

  • 生成された歌い方
  • 歌い方のソースのハッシュと紐づけてstate.singingGuidesで保持される
  • キャッシュはsingingGuideCacheで保持される

歌声のソース(SingingVoiceSource

  • 歌声を合成するために必要なデータ

歌声(SingingVoice

  • 合成された音声(歌声)
  • 歌声のソースのハッシュと紐づけてsingingVoicesで保持される
  • キャッシュはsingingVoiceCacheで保持される

シーケンス(Sequence

  • 実際に再生されるもの
    • ノートシーケンスの場合は、シンセの音でノートが再生される
    • オーディオシーケンスの場合は、音声が再生される
  • フレーズのキー(phraseKey)と紐づけてsequencesで保持される

仕様

  • 以下のデータが変更されたときに歌い方を再生成し音声(歌声)を再合成
    • tpqn
    • tempos
    • phrase.notes
    • track.singer.engineId
    • track.keyRangeAdjustment
    • track.volumeRangeAdjustment
    • engineManifest.frameRate
    • restDurationSeconds(前後の休符の長さ)
  • 以下のデータが変更されたときに音声(歌声)を再合成
    • phrase.singingGuide.frameAudioQuery
    • track.singer
  • シンガーが未設定の場合は、プレビュー音(シンセの音)で再生
  • レンダリング未完了のフレーズは、プレビュー音(シンセの音)で再生
  • レンダリングできなかったフレーズは、次回レンダリング時に歌い方の生成からやり直す

render関数の処理の流れ

1. レンダリング中に変更される可能性のあるデータをコピーする

  • 重なっているノートの削除も行う

2. state.phrasesを更新する

  1. フレーズを調べる
  2. 無くなったフレーズを見つけた場合、以下を行い、state.phrasesから削除する
    1. フレーズに紐づいている歌い方と歌声を削除する
    2. フレーズに紐づいているシーケンスの接続を解除して削除する
  3. 新しいフレーズを見つけた場合、state.phrasesに追加する
  4. state.phrasesにすでに存在するフレーズの場合、以下を行う
    1. シンガーが未設定、またはstateCOULD_NOT_RENDERの場合、歌い方と歌声を削除する
    2. シンガーが設定されていてstateCOULD_NOT_RENDERではない場合、以下を行う
      1. 歌い方が存在する場合、以下を行う
        1. 歌い方のソースのハッシュを計算する
        2. 計算したハッシュとフレーズが保持しているハッシュが一致しない場合、歌い方と歌声を削除する
      2. 歌声が存在する場合、以下を行う
        1. 歌声のソースのハッシュを計算する
        2. 計算したハッシュとフレーズが保持しているハッシュが一致しない場合、歌声を削除する
    3. 歌い方が存在しない、または歌声が存在しない場合、stateWAITING_TO_BE_RENDEREDに設定する

3. state.phrasesに存在する、stateWAITING_TO_BE_RENDEREDのフレーズに対して、以下を行う

  1. シーケンスが存在する場合、シーケンスの接続を解除して削除する
  2. プレビュー音を再生するためのノートシーケンスを作成して接続する

4. state.phrasesに存在する、stateWAITING_TO_BE_RENDEREDのフレーズに対して、優先度が高い順に以下を行う

  1. レンダリングの停止・やり直しが必要な場合は処理を終了
  2. シンガーが未設定の場合、statePLAYABLEに設定し、このフレーズの処理を終了
  3. stateNOW_RENDERINGに設定する
  4. 歌い方が存在しない場合、以下を行う
    1. 歌い方のソースのハッシュを計算する
    2. 歌い方のキャッシュが存在する場合、取得する
    3. 歌い方のキャッシュが存在しない場合、以下を行う
      1. FrameAudioQueryのフェッチを行う
      2. フェッチしたFrameAudioQueryに対して、以下を行う
        1. f0のシフトを行う
        2. volumeのスケーリングを行う
      3. フレーズの開始時刻を計算する
      4. 歌い方を生成する
      5. 歌い方をキャッシュする
    4. 歌い方をフレーズに設定する
      • 実際には、フレーズは歌い方のソースのハッシュを保持し、
        歌い方は、歌い方のソースのハッシュと紐づけてstate.singingGuidesで保持する
  5. 以下を行う
    1. 歌声のソースのハッシュを計算する
    2. 歌声のキャッシュが存在する場合、取得する
    3. 歌声のキャッシュが存在しない場合、以下を行う
      1. 音声合成を行う
      2. 歌声を生成する
      3. 歌声をキャッシュする
    4. 歌声をフレーズに設定する
      • 実際には、フレーズは歌声のソースのハッシュを保持し、
        歌声は、歌声のソースのハッシュと紐づけてsingingVoicesで保持する
  6. ノートシーケンスの接続を解除して削除する
  7. 歌声を再生するためのオーディオシーケンスを作成して接続する
  8. statePLAYABLEに設定し、このフレーズの処理を終了

@Hiroshiba
Copy link
Member

マージします!
ロガー周りの変更とコンフリクトが発生していたのでこちらで変えさせていただきました!

@Hiroshiba Hiroshiba merged commit e6ee7e5 into VOICEVOX:main Apr 12, 2024
9 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

2 participants