Fix stop button - track and stop all active audio nodes
- Track all oscillators/sources in APU activeNodes Set - Add stopAll() method that immediately stops all tracked nodes - Auto-remove nodes from tracking when they end naturally - GameBoyPlayer.stop() now calls apu.stopAll()
This commit is contained in:
+42
-3
@@ -70,6 +70,9 @@ export class GameBoyAPU {
|
|||||||
// Note scheduling stats (no limit - Web Audio handles scheduling)
|
// Note scheduling stats (no limit - Web Audio handles scheduling)
|
||||||
private scheduledNoteCount = 0;
|
private scheduledNoteCount = 0;
|
||||||
|
|
||||||
|
// Track active audio nodes for stop functionality
|
||||||
|
private activeNodes: Set<OscillatorNode | AudioBufferSourceNode> = new Set();
|
||||||
|
|
||||||
constructor(audioContext?: AudioContext, config?: Partial<V2Config>) {
|
constructor(audioContext?: AudioContext, config?: Partial<V2Config>) {
|
||||||
this.audioContext = audioContext || new AudioContext();
|
this.audioContext = audioContext || new AudioContext();
|
||||||
this.config = { ...DEFAULT_V2_CONFIG, ...config };
|
this.config = { ...DEFAULT_V2_CONFIG, ...config };
|
||||||
@@ -197,7 +200,8 @@ export class GameBoyAPU {
|
|||||||
const channel = this.pulseChannels.get(channelId);
|
const channel = this.pulseChannels.get(channelId);
|
||||||
if (!channel) return;
|
if (!channel) return;
|
||||||
|
|
||||||
channel.playNote(midiNote, duration, velocity, startTime);
|
const result = channel.playNote(midiNote, duration, velocity, startTime);
|
||||||
|
this.trackNode(result.oscillator, result.stopTime);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -213,7 +217,8 @@ export class GameBoyAPU {
|
|||||||
const channel = this.waveChannels.get(channelId);
|
const channel = this.waveChannels.get(channelId);
|
||||||
if (!channel) return;
|
if (!channel) return;
|
||||||
|
|
||||||
channel.playNote(midiNote, duration, velocity, startTime);
|
const result = channel.playNote(midiNote, duration, velocity, startTime);
|
||||||
|
this.trackNode(result.oscillator, result.stopTime);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -229,7 +234,21 @@ export class GameBoyAPU {
|
|||||||
const channel = this.noiseChannels.get(channelId);
|
const channel = this.noiseChannels.get(channelId);
|
||||||
if (!channel) return;
|
if (!channel) return;
|
||||||
|
|
||||||
channel.playNote(midiNote, duration, velocity, startTime);
|
const result = channel.playNote(midiNote, duration, velocity, startTime);
|
||||||
|
this.trackNode(result.source, result.stopTime);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Track an audio node for stop functionality.
|
||||||
|
*/
|
||||||
|
private trackNode(node: OscillatorNode | AudioBufferSourceNode, stopTime: number): void {
|
||||||
|
this.activeNodes.add(node);
|
||||||
|
|
||||||
|
// Auto-remove when the node ends naturally
|
||||||
|
const cleanup = () => {
|
||||||
|
this.activeNodes.delete(node);
|
||||||
|
};
|
||||||
|
node.onended = cleanup;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -392,6 +411,26 @@ export class GameBoyAPU {
|
|||||||
this.scheduledNoteCount = 0;
|
this.scheduledNoteCount = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stop all currently playing and scheduled sounds immediately.
|
||||||
|
*/
|
||||||
|
stopAll(): void {
|
||||||
|
const now = this.audioContext.currentTime;
|
||||||
|
|
||||||
|
// Stop all tracked nodes
|
||||||
|
for (const node of this.activeNodes) {
|
||||||
|
try {
|
||||||
|
node.stop(now);
|
||||||
|
} catch {
|
||||||
|
// Node may have already stopped
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.activeNodes.clear();
|
||||||
|
|
||||||
|
// Reset channel states
|
||||||
|
this.reset();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get scheduled note count.
|
* Get scheduled note count.
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -230,7 +230,7 @@ export class GameBoyPlayer {
|
|||||||
*/
|
*/
|
||||||
stop(): void {
|
stop(): void {
|
||||||
this.isPlaying = false;
|
this.isPlaying = false;
|
||||||
this.apu.reset();
|
this.apu.stopAll();
|
||||||
console.log('Playback stopped');
|
console.log('Playback stopped');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user