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

既存キャラクターの音域調整量をマジックナンバーとして埋め込み、自動入力する #2028

Merged
21 changes: 18 additions & 3 deletions public/howtouse.md
Original file line number Diff line number Diff line change
Expand Up @@ -301,10 +301,18 @@ VOICEVOX では、歌声合成機能がプロトタイプ版として提供さ

### 音域調整

デフォルトの設定だと、声が低いキャラクターがうまく歌えないことがあります
そのような場合は「音域調整」を`-12`や`-24`などにすることで、音域を低めに合わせることができます
「音域調整」の値が大きいほど高い音域で、小さいほど低い音域でうまく歌えるようになります
デフォルトではキャラクターに合う音域が設定されています

将来的にこの値は自動設定される予定です。
「音域調整」の値と中央のキーの関係はおおよそ以下の通りです。

| 音域調整の値 | 中央のキー |
| --- | --- |
| 0 | G4(ソ4) |
| -7 | C4(ド4) |
| -12 | G3(ソ3) |
| -19 | C3(ド3) |
| -24 | G2(ソ2) |

### 声量調整

Expand All @@ -313,11 +321,18 @@ VOICEVOX では、歌声合成機能がプロトタイプ版として提供さ

将来的にこの値は自動設定される予定です。

### ピッチ編集

「設定」の「実験的機能」から「ソング:ピッチ編集機能」をONにすることで、歌の音程を細かく制御することができます。

### ソング機能のよくある質問

Q. 赤くなって声が再生されない
A. なにかしらのエラー状態を示しています。現在のバージョンでは、1つのノート(音符)につき日本語1文字分のみ入力できます。またノートが重なっていてもエラーとなります。

Q. 思った高さの音が出ない
A. 音域がずれている可能性があります。「音域調整」で調整してみてください。

## オプション

「設定」の「オプション」でいろいろな設定を変更することができます。
Expand Down
5 changes: 4 additions & 1 deletion src/components/Sing/CharacterMenuButton/MenuButton.vue
Original file line number Diff line number Diff line change
Expand Up @@ -186,7 +186,10 @@ const changeStyleId = (speakerUuid: SpeakerId, styleId: StyleId) => {
`No engineId for target character style (speakerUuid == ${speakerUuid}, styleId == ${styleId})`,
);

store.dispatch("COMMAND_SET_SINGER", { singer: { engineId, styleId } });
store.dispatch("COMMAND_SET_SINGER", {
singer: { engineId, styleId },
withRelated: true,
});
};

const getDefaultStyle = (speakerUuid: string) => {
Expand Down
2 changes: 1 addition & 1 deletion src/components/Sing/SingEditor.vue
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ onetimeWatch(
// CI上のe2eテストのNemoエンジンには歌手がいないためエラーになるのでワークアラウンド
// FIXME: 歌手をいると見せかけるmock APIを作り、ここのtry catchを削除する
try {
await store.dispatch("SET_SINGER", {});
await store.dispatch("SET_SINGER", { withRelated: true });
} catch (e) {
window.backend.logError(e);
}
Expand Down
211 changes: 211 additions & 0 deletions src/sing/workaroundKeyRangeAdjustment.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,211 @@
/**
* 音域調整量の自動入力のためのワークアラウンド。
* 本来エンジンから得るべき音域調整量を、マジックナンバーとして直接データ化している。
*
* FIXME: スタイルの音域をエンジンから取得可能にし、音域調整量を計算するように修正する。
*/

import { createLogger } from "@/domain/frontend/log";
import { Singer } from "@/store/type";
import { CharacterInfo, EngineId } from "@/type/preload";

const logger = createLogger("sing/workaroundKeyRangeAdjustment");

const workaroundKeyRangeAdjustmentValues: {
[key: string]: { [key: string]: number };
} = {
四国めたん: {
ノーマル: -4,
あまあま: -4,
ツンツン: -5,
セクシー: -4,
ヒソヒソ: -9,
},
ずんだもん: {
ノーマル: -2,
あまあま: 0,
ツンツン: -3,
セクシー: 0,
ヒソヒソ: -7,
ヘロヘロ: -3,
なみだめ: 6,
},
春日部つむぎ: {
ノーマル: -2,
},
雨晴はう: {
ノーマル: 0,
},
波音リツ: {
ノーマル: -8,
クイーン: -5,
},
玄野武宏: {
ノーマル: -17,
喜び: -9,
ツンギレ: -14,
悲しみ: -18,
},
白上虎太郎: {
ふつう: -14,
わーい: -8,
びくびく: -7,
おこ: -9,
びえーん: -3,
},
青山龍星: {
ノーマル: -22,
熱血: -18,
不機嫌: -23,
喜び: -21,
しっとり: -27,
かなしみ: -22,
},
冥鳴ひまり: {
ノーマル: -7,
},
九州そら: {
ノーマル: -7,
あまあま: -2,
ツンツン: -6,
セクシー: -4,
},
もち子さん: {
ノーマル: -5,
"セクシー/あん子": -7,
泣き: -2,
怒り: -3,
喜び: -2,
のんびり: -5,
},
剣崎雌雄: {
ノーマル: -18,
},
WhiteCUL: {
ノーマル: -6,
たのしい: -3,
かなしい: -7,
びえーん: 0,
},
後鬼: {
"人間ver.": -7,
"ぬいぐるみver.": -2,
},
"No.7": {
ノーマル: -8,
アナウンス: -10,
読み聞かせ: -9,
},
ちび式じい: {
ノーマル: -18,
},
櫻歌ミコ: {
ノーマル: -6,
第二形態: -12,
ロリ: -7,
},
"小夜/SAYO": {
ノーマル: -4,
},
ナースロボ_タイプT: {
ノーマル: -6,
楽々: -3,
恐怖: -4,
},
"†聖騎士 紅桜†": {
ノーマル: -15,
},
雀松朱司: {
ノーマル: -21,
},
麒ヶ島宗麟: {
ノーマル: -17,
},
春歌ナナ: {
ノーマル: -2,
},
猫使アル: {
ノーマル: -8,
おちつき: -9,
うきうき: -7,
},
猫使ビィ: {
ノーマル: -1,
おちつき: -3,
},
中国うさぎ: {
ノーマル: -8,
おどろき: -4,
こわがり: -2,
へろへろ: -4,
},
栗田まろん: {
ノーマル: -14,
},
あいえるたん: {
ノーマル: -2,
},
満別花丸: {
ノーマル: -4,
元気: 2,
ささやき: -33,
ぶりっ子: 0,
ボーイ: -10,
},
琴詠ニア: {
ノーマル: -4,
},
};

/**
* 指定した歌手の音域調整量を取得するワークアラウンド。
* ハミングの場合はマジックナンバーを使う。
* 歌手の場合は0を返す。
*/
export function getWorkaroundKeyRangeAdjustment(
engineCharacterInfos: Record<EngineId, CharacterInfo[]>,
singer: Singer,
): number {
const defaultKeyRangeAdjustment = 0;

const characterInfos = engineCharacterInfos[singer.engineId];
if (characterInfos == undefined) {
logger.warn("characterInfos not found.", singer);
return defaultKeyRangeAdjustment;
}
const characterInfo = characterInfos.find((c) =>
c.metas.styles.find((s) => s.styleId === singer.styleId),
);
if (characterInfo == undefined) {
logger.warn("characterInfo not found.", singer);
return defaultKeyRangeAdjustment;
}
const styleInfo = characterInfo.metas.styles.find(
(s) => s.styleId === singer.styleId,
);
if (styleInfo == undefined) {
logger.warn("styleInfo not found.", singer);
return defaultKeyRangeAdjustment;
}

if (styleInfo.styleType == "frame_decode") {
// ハミングの場合はマジックナンバーを使う
const singerName = characterInfo.metas.speakerName;
const styleName = styleInfo.styleName;
if (styleName == undefined) {
logger.warn("styleName not found.", singer);
return defaultKeyRangeAdjustment;
}
const keyRangeAdjustment =
workaroundKeyRangeAdjustmentValues[singerName]?.[styleName];
if (keyRangeAdjustment == undefined) {
// 新しいキャラなどの場合はここに来る
logger.warn("keyRangeAdjustment not found.", singer);
return defaultKeyRangeAdjustment;
}
return keyRangeAdjustment;
} else {
// 歌手の場合はそのまま
return 0;
}
}
2 changes: 1 addition & 1 deletion src/store/project.ts
Original file line number Diff line number Diff line change
Expand Up @@ -173,7 +173,7 @@ export const projectStore = createPartialStore<ProjectStoreTypes>({
notes: tracks[0].notes,
},
});
await context.dispatch("SET_SINGER", {});
await context.dispatch("SET_SINGER", { withRelated: true });
await context.dispatch("CLEAR_PITCH_EDIT_DATA");

context.commit("SET_PROJECT_FILEPATH", { filePath: undefined });
Expand Down
38 changes: 32 additions & 6 deletions src/store/singing.ts
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@ import {
createPromiseThatResolvesWhen,
round,
} from "@/sing/utility";
import { getWorkaroundKeyRangeAdjustment } from "@/sing/workaroundKeyRangeAdjustment";
import { createLogger } from "@/domain/frontend/log";
import { noteSchema } from "@/domain/project/schema";

Expand Down Expand Up @@ -218,12 +219,27 @@ export const singingStore = createPartialStore<SingingStoreTypes>({
},

SET_SINGER: {
mutation(state, { singer }: { singer?: Singer }) {
// 歌手をセットする。
// withRelatedがtrueの場合、関連する情報もセットする。
mutation(
state,
{ singer, withRelated }: { singer?: Singer; withRelated?: boolean },
) {
Comment on lines 221 to +227
Copy link
Member Author

@Hiroshiba Hiroshiba Apr 26, 2024

Choose a reason for hiding this comment

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

set singerを少し改造して、セットする時に関連データも一緒にセットするかどうかを選べるようにしました。
デフォルトは普通のセッターです。ちなみに普通にsingerだけsetしてるのはプロジェクトファイル読み込みの部分だけです。

今は音域調整料だけですが、将来的には声量調整量とか、まあその他もろもろとかを突っ込んでいければいいのかなと思っています。

state.tracks[selectedTrackIndex].singer = singer;

if (withRelated == true && singer != undefined) {
// 音域調整量マジックナンバーを設定するワークアラウンド
const keyRangeAdjustment = getWorkaroundKeyRangeAdjustment(
state.characterInfos,
singer,
);
state.tracks[selectedTrackIndex].keyRangeAdjustment =
keyRangeAdjustment;
}
},
async action(
{ state, getters, dispatch, commit },
{ singer }: { singer?: Singer },
{ singer, withRelated }: { singer?: Singer; withRelated?: boolean },
) {
if (state.defaultStyleIds == undefined)
throw new Error("state.defaultStyleIds == undefined");
Expand All @@ -242,7 +258,7 @@ export const singingStore = createPartialStore<SingingStoreTypes>({
const styleId = singer?.styleId ?? defaultStyleId;

dispatch("SETUP_SINGER", { singer: { engineId, styleId } });
commit("SET_SINGER", { singer: { engineId, styleId } });
commit("SET_SINGER", { singer: { engineId, styleId }, withRelated });

dispatch("RENDER");
},
Expand Down Expand Up @@ -2529,12 +2545,22 @@ export const singingCommandStoreState: SingingCommandStoreState = {};
export const singingCommandStore = transformCommandStore(
createPartialStore<SingingCommandStoreTypes>({
COMMAND_SET_SINGER: {
mutation(draft, { singer }) {
mutation(draft, { singer, withRelated }) {
singingStore.mutations.SET_SINGER(draft, { singer });
if (withRelated == true && singer != undefined) {
// 音域調整量マジックナンバーを設定するワークアラウンド
const keyRangeAdjustment = getWorkaroundKeyRangeAdjustment(
draft.characterInfos,
singer,
);
singingStore.mutations.SET_KEY_RANGE_ADJUSTMENT(draft, {
keyRangeAdjustment,
});
}
},
async action({ dispatch, commit }, { singer }) {
async action({ dispatch, commit }, { singer, withRelated }) {
dispatch("SETUP_SINGER", { singer });
commit("COMMAND_SET_SINGER", { singer });
commit("COMMAND_SET_SINGER", { singer, withRelated });

dispatch("RENDER");
},
Expand Down
8 changes: 4 additions & 4 deletions src/store/type.ts
Original file line number Diff line number Diff line change
Expand Up @@ -840,8 +840,8 @@ export type SingingStoreTypes = {
};

SET_SINGER: {
mutation: { singer?: Singer };
action(payload: { singer?: Singer }): void;
mutation: { singer?: Singer; withRelated?: boolean };
action(payload: { singer?: Singer; withRelated?: boolean }): void;
};

SET_KEY_RANGE_ADJUSTMENT: {
Expand Down Expand Up @@ -1116,8 +1116,8 @@ export type SingingCommandStoreState = {

export type SingingCommandStoreTypes = {
COMMAND_SET_SINGER: {
mutation: { singer: Singer };
action(payload: { singer: Singer }): void;
mutation: { singer: Singer; withRelated?: boolean };
action(payload: { singer: Singer; withRelated?: boolean }): void;
};

COMMAND_SET_KEY_RANGE_ADJUSTMENT: {
Expand Down
Loading