From 34678b579a2b3f6ab784bc4924bd5de38d4ed914 Mon Sep 17 00:00:00 2001 From: Sig Date: Sun, 28 Apr 2024 13:14:09 +0900 Subject: [PATCH 1/4] =?UTF-8?q?=E3=82=BD=E3=83=B3=E3=82=B0=EF=BC=9AwebGLVe?= =?UTF-8?q?rsion=E3=81=8C2=E6=9C=AA=E6=BA=80=E3=81=AE=E5=A0=B4=E5=90=88?= =?UTF-8?q?=E3=81=AB=E3=82=A8=E3=83=A9=E3=83=BC=E3=81=A8=E3=81=97=E3=81=A6?= =?UTF-8?q?=E3=83=AD=E3=82=AE=E3=83=B3=E3=82=B0=E3=81=99=E3=82=8B=E3=82=88?= =?UTF-8?q?=E3=81=86=E3=81=AB=E3=81=99=E3=82=8B=20(#2033)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit webGLVersionが2未満の場合にエラーとしてロギングするようにした --- src/components/Sing/SequencerPitch.vue | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/components/Sing/SequencerPitch.vue b/src/components/Sing/SequencerPitch.vue index 3d350a5f5b..269a4a2822 100644 --- a/src/components/Sing/SequencerPitch.vue +++ b/src/components/Sing/SequencerPitch.vue @@ -47,7 +47,7 @@ const props = defineProps<{ | { type: "erase"; startFrame: number; frameLength: number }; }>(); -const { warn } = createLogger("SequencerPitch"); +const { warn, error } = createLogger("SequencerPitch"); const store = useStore(); const singingGuides = computed(() => [...store.state.singingGuides.values()]); const pitchEditData = computed(() => { @@ -425,6 +425,13 @@ onMountedOrActivated(() => { }); stage = new PIXI.Container(); + // webGLVersionをチェックする + // 2未満の場合、ピッチの表示ができないのでエラーとしてロギングする + const webGLVersion = renderer.context.webGLVersion; + if (webGLVersion < 2) { + error(`webGLVersion is less than 2. webGLVersion: ${webGLVersion}`); + } + const callback = () => { if (renderInNextFrame) { render(); From 060e5e2fcbeb254b22e5a14add0a012e1b7f0172 Mon Sep 17 00:00:00 2001 From: Sig Date: Sun, 28 Apr 2024 13:59:39 +0900 Subject: [PATCH 2/4] =?UTF-8?q?=E3=82=BD=E3=83=B3=E3=82=B0=EF=BC=9AstartFr?= =?UTF-8?q?ame=E3=81=8C=E3=83=9E=E3=82=A4=E3=83=8A=E3=82=B9=E5=80=A4?= =?UTF-8?q?=E3=81=AB=E3=81=AA=E3=82=8Af0=E3=81=ABundefined=E3=81=8C?= =?UTF-8?q?=E5=85=A5=E3=82=8B=E3=83=90=E3=82=B0=E3=82=92=E4=BF=AE=E6=AD=A3?= =?UTF-8?q?=20(#2032)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * startFrameがマイナス値になりf0にundefinedが入るバグを修正 * 修正 --- src/components/Sing/SequencerPitch.vue | 27 ++++++++++++++++---------- src/sing/domain.ts | 14 +++++++++---- 2 files changed, 27 insertions(+), 14 deletions(-) diff --git a/src/components/Sing/SequencerPitch.vue b/src/components/Sing/SequencerPitch.vue index 269a4a2822..9e30597baa 100644 --- a/src/components/Sing/SequencerPitch.vue +++ b/src/components/Sing/SequencerPitch.vue @@ -274,9 +274,6 @@ const updateOriginalPitchDataSectionMap = async () => { throw new Error("phonemes.length is 0."); } const f0 = singingGuide.query.f0; - const startTime = singingGuide.startTime; - const startFrame = Math.round(startTime * frameRate); - const endFrame = startFrame + f0.length; // 各フレームの音素の配列を生成する const framePhonemes = convertToFramePhonemes(phonemes); @@ -284,19 +281,29 @@ const updateOriginalPitchDataSectionMap = async () => { throw new Error("f0.length and framePhonemes.length do not match."); } + // 歌い方の開始フレームと終了フレームを計算する + const singingGuideFrameLength = f0.length; + const singingGuideStartFrame = Math.round( + singingGuide.startTime * frameRate, + ); + const singingGuideEndFrame = + singingGuideStartFrame + singingGuideFrameLength; + // 無声子音区間以外のf0をtempDataにコピーする // NOTE: 無声子音区間は音程が無く、f0の値が大きく上下するので表示しない - if (tempData.length < endFrame) { - const valuesToPush = new Array(endFrame - tempData.length).fill( - VALUE_INDICATING_NO_DATA, - ); + if (tempData.length < singingGuideEndFrame) { + const valuesToPush = new Array( + singingGuideEndFrame - tempData.length, + ).fill(VALUE_INDICATING_NO_DATA); tempData.push(...valuesToPush); } - for (let i = 0; i < f0.length; i++) { - const phoneme = framePhonemes[i]; + const startFrame = Math.max(0, singingGuideStartFrame); + const endFrame = singingGuideEndFrame; + for (let i = startFrame; i < endFrame; i++) { + const phoneme = framePhonemes[i - singingGuideStartFrame]; const unvoiced = unvoicedPhonemes.includes(phoneme); if (!unvoiced) { - tempData[startFrame + i] = f0[i]; + tempData[i] = f0[i - singingGuideStartFrame]; } } } diff --git a/src/sing/domain.ts b/src/sing/domain.ts index d17b61395c..e7d8e24066 100644 --- a/src/sing/domain.ts +++ b/src/sing/domain.ts @@ -443,15 +443,21 @@ export function applyPitchEdit( throw new Error("f0.length and framePhonemes.length do not match."); } - const startFrame = Math.round( + // 歌い方の開始フレームと終了フレームを計算する + const singingGuideFrameLength = f0.length; + const singingGuideStartFrame = Math.round( singingGuide.startTime * singingGuide.frameRate, ); - const endFrame = Math.min(startFrame + f0.length, pitchEditData.length); + const singingGuideEndFrame = singingGuideStartFrame + singingGuideFrameLength; + + // ピッチ編集をf0に適用する + const startFrame = Math.max(0, singingGuideStartFrame); + const endFrame = Math.min(pitchEditData.length, singingGuideEndFrame); for (let i = startFrame; i < endFrame; i++) { - const phoneme = framePhonemes[i - startFrame]; + const phoneme = framePhonemes[i - singingGuideStartFrame]; const voiced = !unvoicedPhonemes.includes(phoneme); if (voiced && pitchEditData[i] !== VALUE_INDICATING_NO_DATA) { - f0[i - startFrame] = pitchEditData[i]; + f0[i - singingGuideStartFrame] = pitchEditData[i]; } } } From be4b8965772111fd7c4c6dbd66166deb4e43731c Mon Sep 17 00:00:00 2001 From: Sig Date: Sun, 28 Apr 2024 14:11:11 +0900 Subject: [PATCH 3/4] =?UTF-8?q?=E3=82=BD=E3=83=B3=E3=82=B0=EF=BC=9A?= =?UTF-8?q?=E9=9F=B3=E9=87=8F=E3=81=8C=E6=AD=A3=E3=81=97=E3=81=8F=E7=94=9F?= =?UTF-8?q?=E6=88=90=E3=81=95=E3=82=8C=E3=81=A6=E3=81=84=E3=81=AA=E3=81=84?= =?UTF-8?q?=E3=81=AE=E3=82=92=E4=BF=AE=E6=AD=A3=20(#2030)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * 音量生成時に音域調整の処理(noteのkeyのシフト)が行われて正しく音量が生成されていないのを修正 * 音量生成用にクエリをコピーして、もう一度f0シフトして音量生成する形に修正 * コメントを修正 --- src/store/singing.ts | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/src/store/singing.ts b/src/store/singing.ts index e3e4eaacc4..ec529cce28 100644 --- a/src/store/singing.ts +++ b/src/store/singing.ts @@ -1338,8 +1338,8 @@ export const singingStore = createPartialStore({ `Fetched frame audio query. Phonemes are "${phonemes}".`, ); + // 音域調整を適用する shiftGuidePitch(keyRangeAdjustment, query); - scaleGuideVolume(volumeRangeAdjustment, query); const startTime = calcStartTime( phrase.notes, @@ -1363,9 +1363,12 @@ export const singingStore = createPartialStore({ }); } - // 歌い方をコピーして、ピッチ編集を適用する + // ピッチ編集を適用する前に、歌い方をコピーする singingGuide = structuredClone(toRaw(singingGuide)); + + // ピッチ編集を適用する + applyPitchEdit(singingGuide, pitchEditData, editFrameRate); // 歌声のキャッシュがあれば取得し、なければ音声合成を行う @@ -1385,24 +1388,31 @@ export const singingStore = createPartialStore({ logger.info(`Loaded singing voice from cache.`); } else { - // ピッチ編集を適用したクエリから音量を作る + // 音量生成用のクエリを作る + // ピッチ編集を適用したクエリをコピーし、 + // f0をもう一度シフトして、f0生成時の(シフトする前の)高さに戻す + const queryForVolume = structuredClone(singingGuide.query); + shiftGuidePitch(-keyRangeAdjustment, queryForVolume); + + // 音量生成用のクエリから音量を作る // 音量値はAPIを叩く毎に変わるので、calc hashしたあとに音量を取得している const notesForRequestToEngine = createNotesForRequestToEngine( phrase.notes, tempos, tpqn, - keyRangeAdjustment, + keyRangeAdjustment, // f0を生成するときと同様に、noteのkeyのシフトを行う singingGuide.frameRate, restDurationSeconds, ); - const volumes = await dispatch("FETCH_SING_FRAME_VOLUME", { notes: notesForRequestToEngine, - frameAudioQuery: singingGuide.query, + frameAudioQuery: queryForVolume, styleId: singingTeacherStyleId, engineId: singerAndFrameRate.singer.engineId, }); singingGuide.query.volume = volumes; + + // 声量調整を適用する scaleGuideVolume(volumeRangeAdjustment, singingGuide.query); const blob = await synthesize( From 2106181fadc6c60602f340538288394e2b8471c6 Mon Sep 17 00:00:00 2001 From: Hiroshiba Date: Sun, 28 Apr 2024 14:11:19 +0900 Subject: [PATCH 4/4] =?UTF-8?q?Docs:=20=E3=83=94=E3=83=83=E3=83=81?= =?UTF-8?q?=E7=B7=A8=E9=9B=86=E6=A9=9F=E8=83=BD=E3=81=AE=E5=A0=B4=E6=89=80?= =?UTF-8?q?=E3=82=92=E3=82=88=E3=82=8A=E6=AD=A3=E7=A2=BA=E3=81=AB=E6=A1=88?= =?UTF-8?q?=E5=86=85=20(#2035)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Docs: ピッチ編集機能の場所をより正確に案内 * 修正:mac環境でCmdとなる部分をCtrlとして案内していたのを修正 --- public/howtouse.md | 2 +- src/components/Sing/ToolBar/EditTargetSwicher.vue | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/public/howtouse.md b/public/howtouse.md index 4f5f1fa6f4..e316c3a0ab 100644 --- a/public/howtouse.md +++ b/public/howtouse.md @@ -323,7 +323,7 @@ VOICEVOX では、歌声合成機能がプロトタイプ版として提供さ ### ピッチ編集 -「設定」の「実験的機能」から「ソング:ピッチ編集機能」をONにすることで、歌の音程を細かく制御することができます。 +「設定」→「オプション」→「実験的機能」から「ソング:ピッチ編集機能」をONにすることで、歌の音程を細かく制御することができます。 ### ソング機能のよくある質問 diff --git a/src/components/Sing/ToolBar/EditTargetSwicher.vue b/src/components/Sing/ToolBar/EditTargetSwicher.vue index a93a4569ce..04e0d7412d 100644 --- a/src/components/Sing/ToolBar/EditTargetSwicher.vue +++ b/src/components/Sing/ToolBar/EditTargetSwicher.vue @@ -22,13 +22,14 @@ class="margin-right" @click="editTarget !== 'PITCH' && changeEditTarget('PITCH')" >ピッチ編集
Ctrl+クリックで消去
ピッチ編集
{{ !isMac ? "Ctrl" : "Cmd" }}+クリックで消去