Simulator Sound
I’m not sure I would have taken the trouble of implementing sound in the simulator version of Quinti-Maze if I’d not already done it in 2020. I used it then to emulate the hardware for a Simon-style game I was implementing to experiment with a system for escape room props using MQTT. That experiment might make for an interesting future post.
To start, I defined the notes to play as an array of tuples, with elements for frequency, duration and delay.
pub const NOTES: &[(u32, u64, u64)] = &[
(1000, 256, 0),
(1000, 128, 10),
(1000, 128, 10),
(1333, 169, 10),
(1000, 169, 10),
(1333, 169, 10),
(1667, 653, 10),
];
In order to bring some structure to the bits of Quinti-Maze that are different between the simulator
and actual hardware I’ve introduced a
PlatformSpecific
trait. I’ll talk about that design more in a future post, but below is a simplified version of it and the simulator implementation.
pub trait PlatformSpecific: Default {
fn play_victory_notes(&mut self);
}
struct SimPlatform {
stream: OutputStream,
stream_handle: OutputStreamHandle,
}
impl Default for SimPlatform {
fn default() -> Self {
let (stream, stream_handle) = OutputStream::try_default().expect("default sound output");
Self {
stream,
stream_handle,
}
}
}
impl PlatformSpecific for SimPlatform {
fn play_victory_notes(&mut self) {
let sink = Sink::try_new(&self.stream_handle).expect("new sink");
for (freq, duration, delay) in NOTES {
let source = SineWave::new(*freq)
.take_duration(Duration::from_millis(*duration))
.amplify(0.20)
.delay(Duration::from_millis(*delay));
sink.append(source);
}
sink.sleep_until_end();
}
}
Simulator playback depends on the rodio audio playback library.
Clicking the Apple II speaker at a particular frequency produces a square wave, which has that traditional early game machine sound. Rodio doesn’t have a square wave generator, so I’ve used the sin wave source as the next closest thing.
The rest is something that Rodio handles well; adding a source of frequency and duration to a sink. The delay was needed due to an interest wrinkle; there’s no inherent delay between sounds sent to a Rodio sink so the first three notes sounded like one long note. I guess it was the time it took to go back to the Applesoft interpreter and evaluate the next expression that provided the audible gap in 1982.