Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions src/synth/MidiFileSequencer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -480,6 +480,10 @@ export class MidiFileSequencer {
if (tempoChangeIndex !== state.tempoChangeIndex) {
state.tempoChangeIndex = tempoChangeIndex;
state.currentTempo = state.tempoChanges[state.tempoChangeIndex].bpm;

if (state.syncPoints.length === 0) {
state.syncPointTempo = state.currentTempo;
}
}
}

Expand Down
117 changes: 103 additions & 14 deletions test/audio/SyncPoint.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,27 +11,76 @@ import {
type IExternalMediaHandler,
type IExternalMediaSynthOutput
} from '@src/synth/ExternalMediaPlayer';
import type { IAudioSampleSynthesizer } from '@src/synth/IAudioSampleSynthesizer';
import type { ISynthOutputDevice } from '@src/synth/ISynthOutput';
import { MidiFileSequencer } from '@src/synth/MidiFileSequencer';
import type { PositionChangedEventArgs } from '@src/synth/PositionChangedEventArgs';
import type { Hydra } from '@src/synth/soundfont/Hydra';
import type { SynthEvent } from '@src/synth/synthesis/SynthEvent';
import { FlatMidiEventGenerator } from '@test/audio/FlatMidiEventGenerator';
import { TestPlatform } from '@test/TestPlatform';
import { expect } from 'chai';

describe('SyncPointTests', () => {
it('sync-point-update', () => {
// MidiFileSequencer
// sync points and tempo changes -> expect interpolation
it('sync-point-update', async () => {
const score = await syncPointTestScore();

const midi = new MidiFile();
const handler = new AlphaSynthMidiFileHandler(midi);
const generator = new MidiFileGenerator(score, new Settings(), handler);
generator.generate();

const sequencer = new MidiFileSequencer(new EmptyAudioSynthesizer());
sequencer.loadMidi(midi);
sequencer.mainUpdateSyncPoints(generator.syncPoints);

expect(
sequencer.currentSyncPoints.map(
p =>
`${p.masterBarIndex},${p.masterBarOccurence},${p.synthBpm},${p.syncBpm},${p.synthTime},${p.syncTime}`
)
).toMatchSnapshot();
});

it('backing-track-time-mapping', () => {
// MidiFileSequencer
// do a variety of lookups along the time axis.
// - sequentially (playback)
// - jumps (seeks back and forth)
// check
// - updated syncPointIndex
// - interpolated time
// - reverse lookup with mainTimePositionToBackingTrack
/**
* See #2158
*/
it('no-syncpoints-modified-tempo-', async () => {
const score = ScoreLoader.loadAlphaTex(`
.
\\tempo 90
C4 * 4 |
\\tempo 120
C4 * 4
`);

const midi = new MidiFile();
const handler = new AlphaSynthMidiFileHandler(midi);
const generator = new MidiFileGenerator(score, new Settings(), handler);
generator.generate();

const sequencer = new MidiFileSequencer(new EmptyAudioSynthesizer());
sequencer.loadMidi(midi);

sequencer.currentUpdateCurrentTempo(0);
expect(sequencer.currentTempo).to.equal(90);
expect(sequencer.modifiedTempo).to.equal(90);

sequencer.currentUpdateCurrentTempo(1000);
expect(sequencer.currentTempo).to.equal(90);
expect(sequencer.modifiedTempo).to.equal(90);

sequencer.currentUpdateCurrentTempo(2000);
expect(sequencer.currentTempo).to.equal(90);
expect(sequencer.modifiedTempo).to.equal(90);

sequencer.currentUpdateCurrentTempo(3000);
expect(sequencer.currentTempo).to.equal(120);
expect(sequencer.modifiedTempo).to.equal(120);

sequencer.currentUpdateCurrentTempo(4000);
expect(sequencer.currentTempo).to.equal(120);
expect(sequencer.modifiedTempo).to.equal(120);
});

async function syncPointTestScore() {
Expand All @@ -55,7 +104,10 @@ describe('SyncPointTests', () => {
generator.generate();

expect(
generator.syncPoints.map(p => `${p.masterBarIndex},${p.masterBarOccurence},${p.synthBpm},${p.syncBpm},${p.synthTime},${p.syncTime}`)
generator.syncPoints.map(
p =>
`${p.masterBarIndex},${p.masterBarOccurence},${p.synthBpm},${p.syncBpm},${p.synthTime},${p.syncTime}`
)
).toMatchSnapshot();

const update = MidiFileGenerator.generateSyncPoints(score);
Expand Down Expand Up @@ -327,7 +379,7 @@ class TestBackingTrackOutput implements IBackingTrackSynthOutput {
public sampleRequest: IEventEmitter = new EventEmitter();

public async enumerateOutputDevices(): Promise<ISynthOutputDevice[]> {
return ([] as ISynthOutputDevice[]);
return [] as ISynthOutputDevice[];
}
public async setOutputDevice(device: ISynthOutputDevice | null): Promise<void> {}
public async getOutputDevice(): Promise<ISynthOutputDevice | null> {
Expand Down Expand Up @@ -365,3 +417,40 @@ class TestExternalMediaHandler implements IExternalMediaHandler {
play(): void {}
pause(): void {}
}

class EmptyAudioSynthesizer implements IAudioSampleSynthesizer {
public masterVolume: number = 0;
public metronomeVolume: number = 0;
public outSampleRate: number = 44100;
public currentTempo: number = 120;
public timeSignatureNumerator: number = 4;
public timeSignatureDenominator: number = 4;
public activeVoiceCount: number = 0;
public noteOffAll(immediate: boolean): void {}
public resetSoft(): void {}
public resetPresets(): void {}
public loadPresets(
hydra: Hydra,
instrumentPrograms: Set<number>,
percussionKeys: Set<number>,
append: boolean
): void {}
public setupMetronomeChannel(metronomeVolume: number): void {}
public synthesizeSilent(sampleCount: number): void {}
public dispatchEvent(synthEvent: SynthEvent): void {}
public synthesize(buffer: Float32Array, bufferPos: number, sampleCount: number): SynthEvent[] {
return [];
}
public applyTranspositionPitches(transpositionPitches: Map<number, number>): void {}
public setChannelTranspositionPitch(channel: number, semitones: number): void {}
public channelSetMute(channel: number, mute: boolean): void {}
public channelSetSolo(channel: number, solo: boolean): void {}
public resetChannelStates(): void {}
public channelSetMixVolume(channel: number, volume: number): void {}
public hasSamplesForProgram(program: number): boolean {
return true;
}
public hasSamplesForPercussion(key: number): boolean {
return true;
}
}
15 changes: 15 additions & 0 deletions test/audio/__snapshots__/SyncPoint.test.ts.snap
Original file line number Diff line number Diff line change
Expand Up @@ -402,3 +402,18 @@ Array [
"11,0,80,80.01088583480758,48000,42004.666666666664",
]
`;

exports[`SyncPointTests sync-point-update 1`] = `
Array [
"0,0,120,120,0,0",
"2,0,120,240,4000,4000",
"3,0,120,240,6000,5000",
"4,0,60,59.96124953261497,8000,6000",
"5,0,60,59.961249532614985,12000,10002.585034013606",
"8,0,80,120.04899959166994,24000,22010.34013605442",
"9,0,80,120.04899959167003,27000,24009.52380952381",
"8,1,80,80.01088583480758,36000,30007.074829931975",
"9,1,80,80.01778172927317,39000,33006.666666666664",
"11,0,80,80.01088583480758,48000,42004.666666666664",
]
`;