Skip to content

Instantly share code, notes, and snippets.

@sgeos
Last active July 27, 2023 07:31
Show Gist options
  • Save sgeos/d14cb2e737ce5739213f5d4c648c7b77 to your computer and use it in GitHub Desktop.
Save sgeos/d14cb2e737ce5739213f5d4c648c7b77 to your computer and use it in GitHub Desktop.
SDL2 based piano roll written in Rust. Consulted with ChatGPT4 and put solutions together.
extern crate sdl2;
use sdl2::audio::{ AudioCallback, AudioSpecDesired, };
#[derive(Copy, Clone)]
enum Note {
A = 0,
ASharp = 1,
B = 2,
C = 3,
CSharp = 4,
D = 5,
DSharp = 6,
E = 7,
F = 8,
FSharp = 9,
G = 10,
GSharp = 11,
}
struct OctaveNote {
note: Note,
octave: i32,
}
impl OctaveNote {
fn frequency(&self) -> f32 {
let base_frequency = 440.0; // Frequency of A4
let base_octave = 4; // Octave of A4
let note_distance = (self.note as i32) + (self.octave - base_octave) * 12;
base_frequency * (2f32).powf(note_distance as f32 / 12.0)
}
}
struct TriangleWave {
phase: f32,
phase_inc: f32,
amplitude: i16,
}
impl AudioCallback for TriangleWave {
type Channel = i16;
fn callback(&mut self, out: &mut [i16]) {
for x in out.iter_mut() {
*x = (self.amplitude as f32 * self.phase) as i16;
self.phase += self.phase_inc;
if self.phase >= 1.0 {
self.phase -= 2.0;
}
}
}
}
fn play_triangle(duration: f32, pitch: f32) {
let sdl_context = sdl2::init().unwrap();
let audio_subsystem = sdl_context.audio().unwrap();
let desired_spec = AudioSpecDesired {
freq: Some(44100),
channels: Some(1), // mono
samples: None // default sample size
};
let device = audio_subsystem.open_playback(None, &desired_spec, |spec| {
// initialize the audio callback
TriangleWave {
phase: 0.0,
phase_inc: pitch / spec.freq as f32, // 440 Hz
amplitude: i16::MAX / 4, // reduce amplitude to protect our ears
}
}).unwrap();
// Start playback
device.resume();
// Play for duration seconds
let nanos = (duration * 1_000_000_000.0) as u64;
std::thread::sleep(std::time::Duration::from_nanos(nanos));
}
fn play_piano_roll(piano_roll: Vec<(f32, OctaveNote)>) {
for (duration, note) in piano_roll {
play_triangle(duration, note.frequency());
}
}
fn main() {
let piano_roll = vec![
(0.5, OctaveNote { note: Note::C, octave: 4 }),
(0.5, OctaveNote { note: Note::E, octave: 4 }),
(1.0, OctaveNote { note: Note::F, octave: 4 }),
(0.5, OctaveNote { note: Note::E, octave: 4 }),
(0.5, OctaveNote { note: Note::C, octave: 4 }),
(1.0, OctaveNote { note: Note::D, octave: 4 }),
(0.5, OctaveNote { note: Note::D, octave: 4 }),
(0.5, OctaveNote { note: Note::E, octave: 4 }),
(0.5, OctaveNote { note: Note::F, octave: 4 }),
(0.5, OctaveNote { note: Note::G, octave: 4 }),
(1.0, OctaveNote { note: Note::G, octave: 4 }),
(0.5, OctaveNote { note: Note::E, octave: 4 }),
(0.5, OctaveNote { note: Note::C, octave: 4 }),
(1.0, OctaveNote { note: Note::C, octave: 4 }),
(0.5, OctaveNote { note: Note::G, octave: 4 }),
(0.5, OctaveNote { note: Note::E, octave: 4 }),
(1.0, OctaveNote { note: Note::C, octave: 4 }),
];
play_piano_roll(piano_roll);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment