Skip to content

Commit 4d820ed

Browse files
authored
fix: overlapping metronome channel (#2651)
1 parent 93e3818 commit 4d820ed

7 files changed

Lines changed: 40 additions & 19 deletions

File tree

packages/alphatab/src/synth/AlphaSynth.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -325,7 +325,7 @@ export class AlphaSynthBase implements IAlphaSynth {
325325
if (this._countInVolume > 0) {
326326
Logger.debug('AlphaSynth', 'Starting countin');
327327
this.sequencer.startCountIn();
328-
this.synthesizer.setupMetronomeChannel(this._countInVolume);
328+
this.synthesizer.setupMetronomeChannel(this.sequencer.metronomeChannel, this._countInVolume);
329329
this.updateTimePosition(0, true);
330330
}
331331

@@ -340,7 +340,7 @@ export class AlphaSynthBase implements IAlphaSynth {
340340
}
341341

342342
Logger.debug('AlphaSynth', 'Starting playback');
343-
this.synthesizer.setupMetronomeChannel(this.metronomeVolume);
343+
this.synthesizer.setupMetronomeChannel(this.sequencer.metronomeChannel, this.metronomeVolume);
344344
this._synthStopping = false;
345345
this.state = PlayerState.Playing;
346346
(this.stateChanged as EventEmitterOfT<PlayerStateChangedEventArgs>).trigger(
@@ -442,7 +442,7 @@ export class AlphaSynthBase implements IAlphaSynth {
442442

443443
private _checkReadyForPlayback(): void {
444444
if (this.isReadyForPlayback) {
445-
this.synthesizer.setupMetronomeChannel(this.metronomeVolume);
445+
this.synthesizer.setupMetronomeChannel(this.sequencer.metronomeChannel, this.metronomeVolume);
446446
const programs = this.sequencer.instrumentPrograms;
447447
const percussionKeys = this.sequencer.percussionKeys;
448448
let append = false;
@@ -875,7 +875,7 @@ export class AlphaSynthAudioExporter implements IAlphaSynthAudioExporter {
875875
private _generatedAudioEndTime: number = 0;
876876

877877
public setup() {
878-
this._synth.setupMetronomeChannel(this._synth.metronomeVolume);
878+
this._synth.setupMetronomeChannel(this._sequencer.metronomeChannel, this._synth.metronomeVolume);
879879

880880
const syncPoints = this._sequencer.currentSyncPoints;
881881
const alphaTabEndTime = this._sequencer.currentEndTime;

packages/alphatab/src/synth/BackingTrackPlayer.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,7 @@ class BackingTrackAudioSynthesizer implements IAudioSampleSynthesizer {
8181
// not supported, ignore
8282
}
8383

84-
public setupMetronomeChannel(_metronomeVolume: number): void {
84+
public setupMetronomeChannel(_metronomeChannel: number, _metronomeVolume: number): void {
8585
// not supported, ignore
8686
}
8787

packages/alphatab/src/synth/IAudioSampleSynthesizer.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,9 +68,10 @@ export interface IAudioSampleSynthesizer {
6868

6969
/**
7070
* Configures the channel used to generate metronome sounds.
71+
* @param metronomeChannel The midi hannel to use for playing the metronome (to avoid overlaps with instruments).
7172
* @param metronomeVolume The volume for the channel.
7273
*/
73-
setupMetronomeChannel(metronomeVolume: number): void;
74+
setupMetronomeChannel(metronomeChannel: number, metronomeVolume: number): void;
7475

7576
/**
7677
* Synthesizes the given number of samples without producing an output (e.g. on seeking)

packages/alphatab/src/synth/MidiFileSequencer.ts

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ class MidiSequencerState {
5151
public endTime: number = 0;
5252
public currentTempo: number = 0;
5353
public syncPointTempo: number = 0;
54+
public metronomeChannel: number = SynthConstants.DefaultChannelCount - 1;
5455
}
5556

5657
/**
@@ -65,6 +66,10 @@ export class MidiFileSequencer {
6566
private _oneTimeState: MidiSequencerState | null = null;
6667
private _countInState: MidiSequencerState | null = null;
6768

69+
public get metronomeChannel() {
70+
return this._mainState.metronomeChannel;
71+
}
72+
6873
public get isPlayingMain(): boolean {
6974
return this._currentState === this._mainState;
7075
}
@@ -171,7 +176,7 @@ export class MidiFileSequencer {
171176
const metronomeVolume: number = this._synthesizer.metronomeVolume;
172177
this._synthesizer.noteOffAll(true);
173178
this._synthesizer.resetSoft();
174-
this._synthesizer.setupMetronomeChannel(metronomeVolume);
179+
this._synthesizer.setupMetronomeChannel(this.metronomeChannel, metronomeVolume);
175180
}
176181
this._mainSilentProcess(timePosition);
177182
}
@@ -239,6 +244,8 @@ export class MidiFileSequencer {
239244
let metronomeTick: number = midiFile.tickShift; // shift metronome to content
240245
let metronomeTime: number = 0.0;
241246

247+
let maxChannel = 0;
248+
242249
let previousTick: number = 0;
243250
for (const mEvent of midiFile.events) {
244251
const synthData: SynthEvent = new SynthEvent(state.synthData.length, mEvent);
@@ -287,6 +294,9 @@ export class MidiFileSequencer {
287294
if (!state.firstProgramEventPerChannel.has(channel)) {
288295
state.firstProgramEventPerChannel.set(channel, synthData);
289296
}
297+
if (channel > maxChannel) {
298+
maxChannel = channel;
299+
}
290300
const isPercussion = channel === SynthConstants.PercussionChannel;
291301
if (!isPercussion) {
292302
this.instrumentPrograms.add(programChange.program);
@@ -297,6 +307,9 @@ export class MidiFileSequencer {
297307
if (isPercussion) {
298308
this.percussionKeys.add(noteOn.noteKey);
299309
}
310+
if (noteOn.channel > maxChannel) {
311+
maxChannel = noteOn.channel;
312+
}
300313
}
301314
}
302315

@@ -314,6 +327,7 @@ export class MidiFileSequencer {
314327
});
315328
state.endTime = absTime;
316329
state.endTick = absTick;
330+
state.metronomeChannel = maxChannel + 1;
317331

318332
return state;
319333
}

packages/alphatab/src/synth/SynthConstants.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@
88
*/
99
export class SynthConstants {
1010
public static readonly DefaultChannelCount: number = 16 + 1;
11-
public static readonly MetronomeChannel: number = SynthConstants.DefaultChannelCount - 1;
1211
public static readonly MetronomeKey: number = 33;
1312
public static readonly AudioChannels: number = 2;
1413
public static readonly MinVolume: number = 0;

packages/alphatab/src/synth/synthesis/TinySoundFont.ts

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@ export class TinySoundFont implements IAudioSampleSynthesizer {
6363
public currentTempo: number = 0;
6464
public timeSignatureNumerator: number = 0;
6565
public timeSignatureDenominator: number = 0;
66+
private _metronomeChannel: number = SynthConstants.DefaultChannelCount - 1;
6667

6768
public constructor(sampleRate: number) {
6869
this.outSampleRate = sampleRate;
@@ -188,8 +189,8 @@ export class TinySoundFont implements IAudioSampleSynthesizer {
188189
while (!this._midiEventQueue.isEmpty) {
189190
const m: SynthEvent = this._midiEventQueue.dequeue()!;
190191
if (m.isMetronome && this.metronomeVolume > 0) {
191-
this.channelNoteOff(SynthConstants.MetronomeChannel, SynthConstants.MetronomeKey);
192-
this.channelNoteOn(SynthConstants.MetronomeChannel, SynthConstants.MetronomeKey, 95 / 127);
192+
this.channelNoteOff(this._metronomeChannel, SynthConstants.MetronomeKey);
193+
this.channelNoteOn(this._metronomeChannel, SynthConstants.MetronomeKey, 95 / 127);
193194
} else if (m.event) {
194195
this.processMidiMessage(m.event);
195196
}
@@ -204,7 +205,7 @@ export class TinySoundFont implements IAudioSampleSynthesizer {
204205
// exception. metronome is implicitly added in solo
205206
const isChannelMuted: boolean =
206207
this._mutedChannels.has(channel) ||
207-
(anySolo && channel !== SynthConstants.MetronomeChannel && !this._soloChannels.has(channel));
208+
(anySolo && channel !== this._metronomeChannel && !this._soloChannels.has(channel));
208209

209210
if (!buffer) {
210211
voice.kill();
@@ -261,18 +262,19 @@ export class TinySoundFont implements IAudioSampleSynthesizer {
261262
}
262263

263264
public get metronomeVolume(): number {
264-
return this.channelGetMixVolume(SynthConstants.MetronomeChannel);
265+
return this.channelGetMixVolume(this._metronomeChannel);
265266
}
266267

267268
public set metronomeVolume(value: number) {
268-
this.setupMetronomeChannel(value);
269+
this.setupMetronomeChannel(this._metronomeChannel, value);
269270
}
270271

271-
public setupMetronomeChannel(volume: number): void {
272-
this.channelSetMixVolume(SynthConstants.MetronomeChannel, volume);
272+
public setupMetronomeChannel(channel:number, volume: number): void {
273+
this._metronomeChannel = channel;
274+
this.channelSetMixVolume(channel, volume);
273275
if (volume > 0) {
274-
this.channelSetVolume(SynthConstants.MetronomeChannel, 1);
275-
this.channelSetPresetNumber(SynthConstants.MetronomeChannel, 0, true);
276+
this.channelSetVolume(channel, 1);
277+
this.channelSetPresetNumber(channel, 0, true);
276278
}
277279
}
278280

packages/alphatab/test/audio/SyncPoint.test.ts

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,9 @@
1-
import { type IEventEmitterOfT, type IEventEmitter, EventEmitterOfT, EventEmitter } from '@coderline/alphatab/EventEmitter';
1+
import {
2+
type IEventEmitterOfT,
3+
type IEventEmitter,
4+
EventEmitterOfT,
5+
EventEmitter
6+
} from '@coderline/alphatab/EventEmitter';
27
import { ScoreLoader } from '@coderline/alphatab/importer/ScoreLoader';
38
import { AlphaSynthMidiFileHandler } from '@coderline/alphatab/midi/AlphaSynthMidiFileHandler';
49
import { MidiFile } from '@coderline/alphatab/midi/MidiFile';
@@ -464,7 +469,7 @@ class EmptyAudioSynthesizer implements IAudioSampleSynthesizer {
464469
_percussionKeys: Set<number>,
465470
_append: boolean
466471
): void {}
467-
public setupMetronomeChannel(_metronomeVolume: number): void {}
472+
public setupMetronomeChannel(_metronomeChannel: number, _metronomeVolume: number): void {}
468473
public synthesizeSilent(_sampleCount: number): void {}
469474
public dispatchEvent(_synthEvent: SynthEvent): void {}
470475
public synthesize(_buffer: Float32Array, _bufferPos: number, _ampleCount: number): SynthEvent[] {

0 commit comments

Comments
 (0)