Skip to content

Instantly share code, notes, and snippets.

@44100hertz
Created June 7, 2018 20:49
Show Gist options
  • Save 44100hertz/e4be786d3f770c25bda7999e7bf72bf9 to your computer and use it in GitHub Desktop.
Save 44100hertz/e4be786d3f770c25bda7999e7bf72bf9 to your computer and use it in GitHub Desktop.
mml -> fm synth
const SRATE: u16 = 48000;
use std::f64::consts::PI;
use std::io::{Read, Write, stdin, stdout};
fn main() {
let sin_table: Vec<u8> = (0..256)
.map(|i| ((i as f64 / 512.0 / PI).sin() * 256.0) as u8)
.collect();
let get_sin = |phase: u16| -> i16 {
let pos = (phase >> 6) as u8 as usize;
let samp = sin_table[if phase & 1<<14 == 0 { pos } else { 0xff - pos }];
if phase & 1<<15 == 0 { samp as i16 } else { -(samp as i16) }
};
let notes = {
let mut notes = [0_u8; 256];
let note_vals: [(char, u8); 14] = [
('a',0), ('A',1), ('b',2), ('B',3), ('c',3), ('C',4), ('d',5),
('D',6), ('e',7), ('E',8), ('f',8), ('F',9), ('g',10),('G',11)];
for (c, i) in &note_vals { notes[*c as u8 as usize] = *i; }
notes
};
let mut octave = 0i8;
let (mut phase, mut phase_inc, mut env) = (0u16, 0u16, 0u16);
let mut delay = 0;
let mut chars = {
let mut file = vec![];
stdin().read_to_end(&mut file).unwrap();
file.into_iter().peekable()
};
let mut exit = false;
while !exit {
let c = chars.next().unwrap_or_else(|| { exit=true; b' '} );
match c {
b'O' => {
let next = chars.next().expect("no octave specified");
assert!(next <= b'9' && next >= b'0');
octave = (next - b'0') as i8 - 4;
}
b'<' => if octave > -4 { octave -= 1 },
b'>' => if octave < 5 { octave += 1 },
b'a'..=b'g' | b'A'..=b'G' => {
env = SRATE/4;
let note = notes[c as usize] as f64;
phase_inc = (2.0_f64.powf(note/12.0 + octave as f64) * 440.0 *
std::u16::MAX as f64 / SRATE as f64) as u16;
let delays = [1, 2, 3, 4, 6, 8, 12, 16, 24, 32];
delay = 8;
if let Some(&next) = chars.peek() {
if next <= b'9' && next >= b'0' {
chars.next().unwrap();
delay = delays[(next - b'0') as usize];
}
}
}
_ => {},
}
for _ in 0..delay {
let mut out = [0u8; SRATE as usize/8];
for i in 0..SRATE as usize/16 {
let phase2 = get_sin(phase).wrapping_mul(env as i16>>4) as u16;
let samp = get_sin(phase2) * 128;
phase = phase.wrapping_add(phase_inc);
if env > 0 { env -= 1 }
out[i*2] = samp as u8;
out[i*2+1] = (samp >> 8) as u8;
}
stdout().write_all(&out).unwrap();
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment