@@ -64,6 +64,7 @@ class PlayThroughContext {
6464 public currentTempo : number = 0 ;
6565 public automationToSyncPoint : Map < Automation , BackingTrackSyncPoint > = new Map < Automation , BackingTrackSyncPoint > ( ) ;
6666 public syncPoints ! : BackingTrackSyncPoint [ ] ;
67+ public createNewSyncPoints : boolean = false ;
6768}
6869
6970/**
@@ -133,6 +134,7 @@ export class MidiFileGenerator {
133134 MidiFileGenerator . playThroughSong (
134135 this . _score ,
135136 this . syncPoints ,
137+ false ,
136138 ( bar , previousMasterBar , currentTick , currentTempo , occurence ) => {
137139 this . generateMasterBar ( bar , previousMasterBar , currentTick , currentTempo , occurence ) ;
138140 } ,
@@ -219,14 +221,16 @@ export class MidiFileGenerator {
219221 * It correctly handles repeats and places sync points accoridng to their absolute midi tick when they
220222 * need to be considered for synchronization.
221223 * @param score The song for which to regenerate the sync points.
224+ * @param createNew Whether a new set of sync points should be generated for the sync (start, stop and tempo changes).
222225 * @returns The generated sync points for usage in the backing track playback.
223226 */
224- public static generateSyncPoints ( score : Score ) : BackingTrackSyncPoint [ ] {
227+ public static generateSyncPoints ( score : Score , createNew : boolean = false ) : BackingTrackSyncPoint [ ] {
225228 const syncPoints : BackingTrackSyncPoint [ ] = [ ] ;
226229
227230 MidiFileGenerator . playThroughSong (
228231 score ,
229232 syncPoints ,
233+ createNew ,
230234 ( _masterBar , _previousMasterBar , _currentTick , _currentTempo , _barOccurence ) => {
231235 // no generation
232236 } ,
@@ -250,6 +254,7 @@ export class MidiFileGenerator {
250254 const context = MidiFileGenerator . playThroughSong (
251255 score ,
252256 syncPoints ,
257+ false ,
253258 ( _masterBar , _previousMasterBar , _currentTick , _currentTempo , _barOccurence ) => {
254259 // no generation
255260 } ,
@@ -267,6 +272,7 @@ export class MidiFileGenerator {
267272 private static playThroughSong (
268273 score : Score ,
269274 syncPoints : BackingTrackSyncPoint [ ] ,
275+ createNewSyncPoints : boolean ,
270276 generateMasterBar : (
271277 masterBar : MasterBar ,
272278 previousMasterBar : MasterBar | null ,
@@ -282,6 +288,7 @@ export class MidiFileGenerator {
282288 const playContext = new PlayThroughContext ( ) ;
283289 playContext . currentTempo = score . tempo ;
284290 playContext . syncPoints = syncPoints ;
291+ playContext . createNewSyncPoints = createNewSyncPoints ;
285292 let previousMasterBar : MasterBar | null = null ;
286293
287294 // store the previous played bar for repeats
@@ -328,16 +335,24 @@ export class MidiFileGenerator {
328335 // we need to assume some BPM for the last interpolated point.
329336 // if we have more than just a start point, we keep the BPM before the last manual sync point
330337 // otherwise we have no customized sync BPM known and keep the synthesizer one.
331- backingTrackSyncPoint . syncBpm =
332- syncPoints . length > 1 ? syncPoints [ syncPoints . length - 2 ] . syncBpm : lastSyncPoint . synthBpm ;
338+ if ( playContext . createNewSyncPoints ) {
339+ backingTrackSyncPoint . syncBpm = lastSyncPoint . synthBpm ;
340+ backingTrackSyncPoint . synthBpm = lastSyncPoint . synthBpm ;
341+ } else if ( syncPoints . length === 1 ) {
342+ backingTrackSyncPoint . syncBpm = lastSyncPoint . synthBpm ;
343+ } else {
344+ backingTrackSyncPoint . syncBpm = syncPoints [ syncPoints . length - 2 ] . syncBpm ;
345+ }
333346
334347 backingTrackSyncPoint . synthTime =
335348 lastSyncPoint . synthTime + MidiUtils . ticksToMillis ( remainingTicks , lastSyncPoint . synthBpm ) ;
336349 backingTrackSyncPoint . syncTime =
337350 lastSyncPoint . syncTime + MidiUtils . ticksToMillis ( remainingTicks , backingTrackSyncPoint . syncBpm ) ;
338351
339352 // update the previous sync point according to the new time
340- lastSyncPoint . updateSyncBpm ( backingTrackSyncPoint . synthTime , backingTrackSyncPoint . syncTime ) ;
353+ if ( ! playContext . createNewSyncPoints ) {
354+ lastSyncPoint . updateSyncBpm ( backingTrackSyncPoint . synthTime , backingTrackSyncPoint . syncTime ) ;
355+ }
341356
342357 syncPoints . push ( backingTrackSyncPoint ) ;
343358 }
@@ -352,7 +367,9 @@ export class MidiFileGenerator {
352367 const duration = bar . calculateDuration ( ) ;
353368 const barSyncPoints = bar . syncPoints ;
354369 const barStartTick = context . synthTick ;
355- if ( barSyncPoints ) {
370+ if ( context . createNewSyncPoints ) {
371+ MidiFileGenerator . processBarTimeWithNewSyncPoints ( bar , occurence , context ) ;
372+ } else if ( barSyncPoints ) {
356373 MidiFileGenerator . processBarTimeWithSyncPoints ( bar , occurence , context ) ;
357374 } else {
358375 MidiFileGenerator . processBarTimeNoSyncPoints ( bar , context ) ;
@@ -367,6 +384,51 @@ export class MidiFileGenerator {
367384 }
368385 }
369386
387+ private static processBarTimeWithNewSyncPoints ( bar : MasterBar , occurence : number , context : PlayThroughContext ) {
388+ // start marker
389+ const barStartTick = context . synthTick ;
390+ if ( bar . index === 0 && occurence === 0 ) {
391+ context . currentTempo = bar . score . tempo ;
392+
393+ const backingTrackSyncPoint = new BackingTrackSyncPoint ( ) ;
394+ backingTrackSyncPoint . masterBarIndex = bar . index ;
395+ backingTrackSyncPoint . masterBarOccurence = occurence ;
396+ backingTrackSyncPoint . synthTick = barStartTick ;
397+ backingTrackSyncPoint . synthBpm = context . currentTempo ;
398+ backingTrackSyncPoint . synthTime = context . synthTime ;
399+ backingTrackSyncPoint . syncBpm = context . currentTempo ;
400+ backingTrackSyncPoint . syncTime = context . synthTime ;
401+
402+ context . syncPoints . push ( backingTrackSyncPoint ) ;
403+ }
404+
405+ // walk tempo changes and create points
406+ const duration = bar . calculateDuration ( ) ;
407+ for ( const change of bar . tempoAutomations ) {
408+ const absoluteTick = barStartTick + change . ratioPosition * duration ;
409+ const tickOffset = absoluteTick - context . synthTick ;
410+ if ( tickOffset > 0 ) {
411+ context . synthTick = absoluteTick ;
412+ context . synthTime += MidiUtils . ticksToMillis ( tickOffset , context . currentTempo ) ;
413+ }
414+
415+ if ( change . value !== context . currentTempo ) {
416+ context . currentTempo = change . value ;
417+
418+ const backingTrackSyncPoint = new BackingTrackSyncPoint ( ) ;
419+ backingTrackSyncPoint . masterBarIndex = bar . index ;
420+ backingTrackSyncPoint . masterBarOccurence = occurence ;
421+ backingTrackSyncPoint . synthTick = absoluteTick ;
422+ backingTrackSyncPoint . synthBpm = context . currentTempo ;
423+ backingTrackSyncPoint . synthTime = context . synthTime ;
424+ backingTrackSyncPoint . syncBpm = context . currentTempo ;
425+ backingTrackSyncPoint . syncTime = context . synthTime ;
426+
427+ context . syncPoints . push ( backingTrackSyncPoint ) ;
428+ }
429+ }
430+ }
431+
370432 private static processBarTimeWithSyncPoints ( bar : MasterBar , occurence : number , context : PlayThroughContext ) {
371433 const barStartTick = context . synthTick ;
372434 const duration = bar . calculateDuration ( ) ;
0 commit comments