@@ -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" ;
1515import {
1616 calculatePhraseKey ,
1717 decibelToLinear ,
@@ -20,7 +20,8 @@ import {
2020 tickToSecond ,
2121} from "@/sing/domain" ;
2222import { 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} ;
0 commit comments