Skip to content

Commit 19cef37

Browse files
refactor: generatePhrases関数をリファクタリング (#2643)
1 parent e6f296a commit 19cef37

File tree

2 files changed

+111
-78
lines changed

2 files changed

+111
-78
lines changed

src/sing/songTrackRendering.ts

Lines changed: 106 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import {
1111
Tempo,
1212
Track,
1313
} from "@/store/type";
14-
import { calculateHash, linearInterpolation } from "@/sing/utility";
14+
import { calculateHash, getPrev, linearInterpolation } from "@/sing/utility";
1515
import {
1616
calculatePhraseKey,
1717
decibelToLinear,
@@ -20,7 +20,8 @@ import {
2020
tickToSecond,
2121
} from "@/sing/domain";
2222
import { FramePhoneme, Note as NoteForRequestToEngine } from "@/openapi";
23-
import { EngineId, TrackId } from "@/type/preload";
23+
import { EngineId, NoteId, TrackId } from "@/type/preload";
24+
import { getOrThrow } from "@/helpers/mapHelper";
2425

2526
/**
2627
* フレーズレンダリングに必要なデータのスナップショット
@@ -29,6 +30,7 @@ export type SnapshotForPhraseRender = Readonly<{
2930
tpqn: number;
3031
tempos: Tempo[];
3132
tracks: Map<TrackId, Track>;
33+
trackOverlappingNoteIds: Map<TrackId, Set<NoteId>>;
3234
engineFrameRates: Map<EngineId, number>;
3335
editorFrameRate: number;
3436
}>;
@@ -308,60 +310,114 @@ const calculatePhraseStartTime = (
308310
);
309311
};
310312

311-
export const generatePhrases = async (
312-
notes: Note[],
313-
tempos: Tempo[],
314-
tpqn: number,
315-
phraseFirstRestMinDurationSeconds: number,
316-
trackId: TrackId,
317-
) => {
318-
const generatedPhrases = new Map<PhraseKey, Phrase>();
319-
320-
let phraseNotes: Note[] = [];
321-
let prevPhraseLastNote: Note | undefined = undefined;
313+
/**
314+
* トラックのノーツからフレーズごとのノーツを抽出する。
315+
*/
316+
const extractPhraseNotes = (trackNotes: Note[]) => {
317+
const phraseNotes: Note[][] = [];
318+
let currentPhraseNotes: Note[] = [];
322319

323-
for (let i = 0; i < notes.length; i++) {
324-
const note = notes[i];
325-
const nextNote = notes.at(i + 1);
320+
for (let i = 0; i < trackNotes.length; i++) {
321+
const note = trackNotes[i];
322+
const nextNote = trackNotes.at(i + 1);
326323
const currentNoteEndPos = note.position + note.duration;
327324

328-
phraseNotes.push(note);
325+
currentPhraseNotes.push(note);
329326

330327
// ノートが途切れていたら別のフレーズにする
331328
if (nextNote == undefined || currentNoteEndPos !== nextNote.position) {
332-
const phraseFirstNote = phraseNotes[0];
333-
const phraseFirstRestDuration = calcPhraseFirstRestDuration(
334-
prevPhraseLastNote,
335-
phraseFirstNote,
336-
phraseFirstRestMinDurationSeconds,
337-
tempos,
338-
tpqn,
339-
);
340-
const phraseStartTime = calculatePhraseStartTime(
341-
phraseFirstRestDuration,
342-
phraseNotes,
343-
tempos,
344-
tpqn,
345-
);
346-
const phraseKey = await calculatePhraseKey({
347-
firstRestDuration: phraseFirstRestDuration,
348-
notes: phraseNotes,
349-
startTime: phraseStartTime,
350-
trackId,
351-
});
352-
generatedPhrases.set(phraseKey, {
353-
firstRestDuration: phraseFirstRestDuration,
354-
notes: phraseNotes,
355-
startTime: phraseStartTime,
356-
state: "WAITING_TO_BE_RENDERED",
357-
trackId,
358-
});
359-
360-
if (nextNote != undefined) {
361-
prevPhraseLastNote = phraseNotes.at(-1);
362-
phraseNotes = [];
363-
}
329+
phraseNotes.push([...currentPhraseNotes]);
330+
currentPhraseNotes = [];
364331
}
365332
}
366-
return generatedPhrases;
333+
334+
return phraseNotes;
335+
};
336+
337+
/**
338+
* フレーズごとのノーツからフレーズを生成する。
339+
*/
340+
const createPhrasesFromNotes = async (
341+
phraseNotesList: Note[][],
342+
trackId: TrackId,
343+
snapshot: SnapshotForPhraseRender,
344+
firstRestMinDurationSeconds: number,
345+
) => {
346+
const phrases = new Map<PhraseKey, Phrase>();
347+
348+
for (let i = 0; i < phraseNotesList.length; i++) {
349+
const phraseNotes = phraseNotesList[i];
350+
const phraseFirstNote = phraseNotes[0];
351+
const prevPhraseNotes = getPrev(phraseNotesList, i);
352+
const prevPhraseLastNote = prevPhraseNotes?.at(-1);
353+
354+
const phraseFirstRestDuration = calcPhraseFirstRestDuration(
355+
prevPhraseLastNote,
356+
phraseFirstNote,
357+
firstRestMinDurationSeconds,
358+
snapshot.tempos,
359+
snapshot.tpqn,
360+
);
361+
const phraseStartTime = calculatePhraseStartTime(
362+
phraseFirstRestDuration,
363+
phraseNotes,
364+
snapshot.tempos,
365+
snapshot.tpqn,
366+
);
367+
const phraseKey = await calculatePhraseKey({
368+
firstRestDuration: phraseFirstRestDuration,
369+
notes: phraseNotes,
370+
startTime: phraseStartTime,
371+
trackId,
372+
});
373+
phrases.set(phraseKey, {
374+
firstRestDuration: phraseFirstRestDuration,
375+
notes: phraseNotes,
376+
startTime: phraseStartTime,
377+
state: "WAITING_TO_BE_RENDERED",
378+
trackId,
379+
});
380+
}
381+
382+
return phrases;
383+
};
384+
385+
/**
386+
* 各トラックのノーツからフレーズを生成する。
387+
* 重なっているノートはフレーズには含まれない。
388+
*/
389+
export const generatePhrases = async (
390+
snapshot: SnapshotForPhraseRender,
391+
firstRestMinDurationSeconds: number,
392+
) => {
393+
const phrases = new Map<PhraseKey, Phrase>();
394+
395+
for (const [trackId, track] of snapshot.tracks) {
396+
// 重なっているノートを除く
397+
const overlappingNoteIds = getOrThrow(
398+
snapshot.trackOverlappingNoteIds,
399+
trackId,
400+
);
401+
const trackNotes = track.notes.filter(
402+
(value) => !overlappingNoteIds.has(value.id),
403+
);
404+
405+
// トラックのノーツからフレーズごとのノーツを抽出
406+
const phraseNotesList = extractPhraseNotes(trackNotes);
407+
408+
// フレーズごとのノーツからフレーズを生成
409+
const trackPhrases = await createPhrasesFromNotes(
410+
phraseNotesList,
411+
trackId,
412+
snapshot,
413+
firstRestMinDurationSeconds,
414+
);
415+
416+
// 結果をマージ
417+
for (const [key, phrase] of trackPhrases) {
418+
phrases.set(key, phrase);
419+
}
420+
}
421+
422+
return phrases;
367423
};

src/store/singing.ts

Lines changed: 5 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -2077,34 +2077,11 @@ export const singingStore = createPartialStore<SingingStoreTypes>({
20772077

20782078
const renderStartStageIds = new Map<PhraseKey, PhraseRenderStageId>();
20792079

2080-
// 重なっているノートを削除する
2081-
const filteredTrackNotes = new Map<TrackId, Note[]>();
2082-
for (const [trackId, track] of snapshot.tracks) {
2083-
const overlappingNoteIds = getOrThrow(
2084-
snapshot.trackOverlappingNoteIds,
2085-
trackId,
2086-
);
2087-
const filteredNotes = track.notes.filter(
2088-
(value) => !overlappingNoteIds.has(value.id),
2089-
);
2090-
filteredTrackNotes.set(trackId, filteredNotes);
2091-
}
2092-
2093-
// ノーツからフレーズを生成する
2094-
const generatedPhrases = new Map<PhraseKey, Phrase>();
2095-
for (const trackId of snapshot.tracks.keys()) {
2096-
const filteredNotes = getOrThrow(filteredTrackNotes, trackId);
2097-
const phrases = await generatePhrases(
2098-
filteredNotes,
2099-
snapshot.tempos,
2100-
snapshot.tpqn,
2101-
firstRestMinDurationSeconds,
2102-
trackId,
2103-
);
2104-
for (const [phraseKey, phrase] of phrases) {
2105-
generatedPhrases.set(phraseKey, phrase);
2106-
}
2107-
}
2080+
// 各トラックのノーツからフレーズを生成する
2081+
const generatedPhrases = await generatePhrases(
2082+
snapshot,
2083+
firstRestMinDurationSeconds,
2084+
);
21082085

21092086
const mergedPhrases = new Map<PhraseKey, Phrase>();
21102087
const newlyCreatedPhraseKeys = new Set<PhraseKey>();

0 commit comments

Comments
 (0)