Skip to content

Commit

Permalink
ソング: USTファイルのインポート機能の追加 (VOICEVOX#1933)
Browse files Browse the repository at this point in the history
* VOICEVOX#1908 USTファイルを一応読める試行実装

* VOICEVOX#1908 USTインポート修正

* VOICEVOX#1908 調整[update snapshots]

* Update src/store/singing.ts

Co-authored-by: Hiroshiba <hihokaruta@gmail.com>

* Update src/store/singing.ts

Co-authored-by: Hiroshiba <hihokaruta@gmail.com>

* エラー内容を修正

---------

Co-authored-by: Romot <user@USERs-MacBook-Pro.local>
Co-authored-by: Hiroshiba <hihokaruta@gmail.com>
  • Loading branch information
3 people authored Mar 20, 2024
1 parent 125a5e8 commit 86d2e8e
Show file tree
Hide file tree
Showing 3 changed files with 138 additions and 0 deletions.
13 changes: 13 additions & 0 deletions src/components/Sing/MenuBar.vue
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,11 @@ const importMusicXMLFile = async () => {
await store.dispatch("IMPORT_MUSICXML_FILE", {});
};
const importUstFile = async () => {
if (uiLocked.value) return;
await store.dispatch("IMPORT_UST_FILE", {});
};
const exportWaveFile = async () => {
if (uiLocked.value) return;
await store.dispatch("EXPORT_WAVE_FILE", {});
Expand Down Expand Up @@ -54,5 +59,13 @@ const fileSubMenuData: MenuItemData[] = [
},
disableWhenUiLocked: true,
},
{
type: "button",
label: "UST読み込み",
onClick: () => {
importUstFile();
},
disableWhenUiLocked: true,
},
];
</script>
121 changes: 121 additions & 0 deletions src/store/singing.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1699,6 +1699,127 @@ export const singingStore = createPartialStore<SingingStoreTypes>({
),
},

IMPORT_UST_FILE: {
action: createUILockAction(
async ({ dispatch }, { filePath }: { filePath?: string }) => {
// USTファイルの読み込み
if (!filePath) {
filePath = await window.backend.showImportFileDialog({
title: "UST読み込み",
name: "UST",
extensions: ["ust"],
});
if (!filePath) return;
}
// ファイルの読み込み
const fileData = getValueOrThrow(
await window.backend.readFile({ filePath })
);

// ファイルフォーマットに応じてエンコーディングを変える
// UTF-8とShiftJISの2種類に対応
let ustData;
try {
ustData = new TextDecoder("utf-8").decode(fileData);
// ShiftJISの場合はShiftJISでデコードし直す
if (ustData.includes("\ufffd")) {
ustData = new TextDecoder("shift-jis").decode(fileData);
}
} catch (error) {
throw new Error("Failed to decode UST file.", { cause: error });
}
if (!ustData || typeof ustData !== "string") {
throw new Error("Failed to decode UST file.");
}

// 初期化
const tpqn = DEFAULT_TPQN;
const tempos: Tempo[] = [
{
position: 0,
bpm: DEFAULT_BPM,
},
];
const timeSignatures: TimeSignature[] = [
{
measureNumber: 1,
beats: DEFAULT_BEATS,
beatType: DEFAULT_BEAT_TYPE,
},
];
const notes: Note[] = [];

// USTファイルのセクションをパース
const parseSection = (section: string): { [key: string]: string } => {
const sectionNameMatch = section.match(/\[(.+)\]/);
if (!sectionNameMatch) {
throw new Error("UST section name not found");
}
const params = section.split(/[\r\n]+/).reduce((acc, line) => {
const [key, value] = line.split("=");
if (key && value) {
acc[key] = value;
}
return acc;
}, {} as { [key: string]: string });
return {
...params,
sectionName: sectionNameMatch[1],
};
};

// セクションを分割
const sections = ustData.split(/^(?=\[)/m);
// ポジション
let position = 0;
// セクションごとに処理
sections.forEach((section) => {
const params = parseSection(section);
// SETTINGセクション
if (params.sectionName === "#SETTING") {
const tempo = Number(params["Tempo"]);
if (tempo) tempos[0].bpm = tempo;
}
// ノートセクション
if (params.sectionName.match(/^#\d{4}/)) {
// テンポ変更があれば追加
const tempo = Number(params["Tempo"]);
if (tempo) tempos.push({ position, bpm: tempo });
const noteNumber = Number(params["NoteNum"]);
const duration = Number(params["Length"]);
// 歌詞の前に連続音が含まれている場合は除去
const lyric = params["Lyric"].includes(" ")
? params["Lyric"].split(" ")[1]
: params["Lyric"];
// 休符であればポジションを進めるのみ
if (lyric === "R") {
position += duration;
} else {
// それ以外の場合はノートを追加
notes.push({
id: uuidv4(),
position,
duration,
noteNumber,
lyric,
});
position += duration;
}
}
});

await dispatch("SET_SCORE", {
score: {
tpqn,
tempos,
timeSignatures,
notes,
},
});
}
),
},

SET_NOW_AUDIO_EXPORTING: {
mutation(state, { nowAudioExporting }) {
state.nowAudioExporting = nowAudioExporting;
Expand Down
4 changes: 4 additions & 0 deletions src/store/type.ts
Original file line number Diff line number Diff line change
Expand Up @@ -930,6 +930,10 @@ export type SingingStoreTypes = {
action(payload: { filePath?: string }): void;
};

IMPORT_UST_FILE: {
action(payload: { filePath?: string }): void;
};

EXPORT_WAVE_FILE: {
action(payload: { filePath?: string }): SaveResultObject;
};
Expand Down

0 comments on commit 86d2e8e

Please sign in to comment.