Last active
July 27, 2023 07:31
-
-
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.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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