@@ -11,27 +11,76 @@ import {
1111 type IExternalMediaHandler ,
1212 type IExternalMediaSynthOutput
1313} from '@src/synth/ExternalMediaPlayer' ;
14+ import type { IAudioSampleSynthesizer } from '@src/synth/IAudioSampleSynthesizer' ;
1415import type { ISynthOutputDevice } from '@src/synth/ISynthOutput' ;
16+ import { MidiFileSequencer } from '@src/synth/MidiFileSequencer' ;
1517import type { PositionChangedEventArgs } from '@src/synth/PositionChangedEventArgs' ;
18+ import type { Hydra } from '@src/synth/soundfont/Hydra' ;
19+ import type { SynthEvent } from '@src/synth/synthesis/SynthEvent' ;
1620import { FlatMidiEventGenerator } from '@test/audio/FlatMidiEventGenerator' ;
1721import { TestPlatform } from '@test/TestPlatform' ;
1822import { expect } from 'chai' ;
1923
2024describe ( 'SyncPointTests' , ( ) => {
21- it ( 'sync-point-update' , ( ) => {
22- // MidiFileSequencer
23- // sync points and tempo changes -> expect interpolation
25+ it ( 'sync-point-update' , async ( ) => {
26+ const score = await syncPointTestScore ( ) ;
27+
28+ const midi = new MidiFile ( ) ;
29+ const handler = new AlphaSynthMidiFileHandler ( midi ) ;
30+ const generator = new MidiFileGenerator ( score , new Settings ( ) , handler ) ;
31+ generator . generate ( ) ;
32+
33+ const sequencer = new MidiFileSequencer ( new EmptyAudioSynthesizer ( ) ) ;
34+ sequencer . loadMidi ( midi ) ;
35+ sequencer . mainUpdateSyncPoints ( generator . syncPoints ) ;
36+
37+ expect (
38+ sequencer . currentSyncPoints . map (
39+ p =>
40+ `${ p . masterBarIndex } ,${ p . masterBarOccurence } ,${ p . synthBpm } ,${ p . syncBpm } ,${ p . synthTime } ,${ p . syncTime } `
41+ )
42+ ) . toMatchSnapshot ( ) ;
2443 } ) ;
2544
26- it ( 'backing-track-time-mapping' , ( ) => {
27- // MidiFileSequencer
28- // do a variety of lookups along the time axis.
29- // - sequentially (playback)
30- // - jumps (seeks back and forth)
31- // check
32- // - updated syncPointIndex
33- // - interpolated time
34- // - reverse lookup with mainTimePositionToBackingTrack
45+ /**
46+ * See #2158
47+ */
48+ it ( 'no-syncpoints-modified-tempo-' , async ( ) => {
49+ const score = ScoreLoader . loadAlphaTex ( `
50+ .
51+ \\tempo 90
52+ C4 * 4 |
53+ \\tempo 120
54+ C4 * 4
55+ ` ) ;
56+
57+ const midi = new MidiFile ( ) ;
58+ const handler = new AlphaSynthMidiFileHandler ( midi ) ;
59+ const generator = new MidiFileGenerator ( score , new Settings ( ) , handler ) ;
60+ generator . generate ( ) ;
61+
62+ const sequencer = new MidiFileSequencer ( new EmptyAudioSynthesizer ( ) ) ;
63+ sequencer . loadMidi ( midi ) ;
64+
65+ sequencer . currentUpdateCurrentTempo ( 0 ) ;
66+ expect ( sequencer . currentTempo ) . to . equal ( 90 ) ;
67+ expect ( sequencer . modifiedTempo ) . to . equal ( 90 ) ;
68+
69+ sequencer . currentUpdateCurrentTempo ( 1000 ) ;
70+ expect ( sequencer . currentTempo ) . to . equal ( 90 ) ;
71+ expect ( sequencer . modifiedTempo ) . to . equal ( 90 ) ;
72+
73+ sequencer . currentUpdateCurrentTempo ( 2000 ) ;
74+ expect ( sequencer . currentTempo ) . to . equal ( 90 ) ;
75+ expect ( sequencer . modifiedTempo ) . to . equal ( 90 ) ;
76+
77+ sequencer . currentUpdateCurrentTempo ( 3000 ) ;
78+ expect ( sequencer . currentTempo ) . to . equal ( 120 ) ;
79+ expect ( sequencer . modifiedTempo ) . to . equal ( 120 ) ;
80+
81+ sequencer . currentUpdateCurrentTempo ( 4000 ) ;
82+ expect ( sequencer . currentTempo ) . to . equal ( 120 ) ;
83+ expect ( sequencer . modifiedTempo ) . to . equal ( 120 ) ;
3584 } ) ;
3685
3786 async function syncPointTestScore ( ) {
@@ -55,7 +104,10 @@ describe('SyncPointTests', () => {
55104 generator . generate ( ) ;
56105
57106 expect (
58- generator . syncPoints . map ( p => `${ p . masterBarIndex } ,${ p . masterBarOccurence } ,${ p . synthBpm } ,${ p . syncBpm } ,${ p . synthTime } ,${ p . syncTime } ` )
107+ generator . syncPoints . map (
108+ p =>
109+ `${ p . masterBarIndex } ,${ p . masterBarOccurence } ,${ p . synthBpm } ,${ p . syncBpm } ,${ p . synthTime } ,${ p . syncTime } `
110+ )
59111 ) . toMatchSnapshot ( ) ;
60112
61113 const update = MidiFileGenerator . generateSyncPoints ( score ) ;
@@ -327,7 +379,7 @@ class TestBackingTrackOutput implements IBackingTrackSynthOutput {
327379 public sampleRequest : IEventEmitter = new EventEmitter ( ) ;
328380
329381 public async enumerateOutputDevices ( ) : Promise < ISynthOutputDevice [ ] > {
330- return ( [ ] as ISynthOutputDevice [ ] ) ;
382+ return [ ] as ISynthOutputDevice [ ] ;
331383 }
332384 public async setOutputDevice ( device : ISynthOutputDevice | null ) : Promise < void > { }
333385 public async getOutputDevice ( ) : Promise < ISynthOutputDevice | null > {
@@ -365,3 +417,40 @@ class TestExternalMediaHandler implements IExternalMediaHandler {
365417 play ( ) : void { }
366418 pause ( ) : void { }
367419}
420+
421+ class EmptyAudioSynthesizer implements IAudioSampleSynthesizer {
422+ public masterVolume : number = 0 ;
423+ public metronomeVolume : number = 0 ;
424+ public outSampleRate : number = 44100 ;
425+ public currentTempo : number = 120 ;
426+ public timeSignatureNumerator : number = 4 ;
427+ public timeSignatureDenominator : number = 4 ;
428+ public activeVoiceCount : number = 0 ;
429+ public noteOffAll ( immediate : boolean ) : void { }
430+ public resetSoft ( ) : void { }
431+ public resetPresets ( ) : void { }
432+ public loadPresets (
433+ hydra : Hydra ,
434+ instrumentPrograms : Set < number > ,
435+ percussionKeys : Set < number > ,
436+ append : boolean
437+ ) : void { }
438+ public setupMetronomeChannel ( metronomeVolume : number ) : void { }
439+ public synthesizeSilent ( sampleCount : number ) : void { }
440+ public dispatchEvent ( synthEvent : SynthEvent ) : void { }
441+ public synthesize ( buffer : Float32Array , bufferPos : number , sampleCount : number ) : SynthEvent [ ] {
442+ return [ ] ;
443+ }
444+ public applyTranspositionPitches ( transpositionPitches : Map < number , number > ) : void { }
445+ public setChannelTranspositionPitch ( channel : number , semitones : number ) : void { }
446+ public channelSetMute ( channel : number , mute : boolean ) : void { }
447+ public channelSetSolo ( channel : number , solo : boolean ) : void { }
448+ public resetChannelStates ( ) : void { }
449+ public channelSetMixVolume ( channel : number , volume : number ) : void { }
450+ public hasSamplesForProgram ( program : number ) : boolean {
451+ return true ;
452+ }
453+ public hasSamplesForPercussion ( key : number ) : boolean {
454+ return true ;
455+ }
456+ }
0 commit comments