Browse Source

Initial soundchip and music_cli stuff

master
Erin 8 years ago
parent
commit
ab041cf4ca
15 changed files with 304 additions and 0 deletions
  1. +4
    -0
      music_cli/Cargo.lock
  2. +11
    -0
      music_cli/Cargo.toml
  3. +46
    -0
      music_cli/src/main.rs
  4. +0
    -0
      music_cli/target/debug/.cargo-lock
  5. +4
    -0
      soundchip/Cargo.lock
  6. +6
    -0
      soundchip/Cargo.toml
  7. +94
    -0
      soundchip/src/audio.rs
  8. +50
    -0
      soundchip/src/channels.rs
  9. +62
    -0
      soundchip/src/lib.rs
  10. +24
    -0
      soundchip/src/waveform.rs
  11. +0
    -0
      soundchip/target/debug/.cargo-lock
  12. BIN
      soundchip/target/debug/.fingerprint/soundchip-678087a299b215b4/dep-lib-soundchip-678087a299b215b4
  13. +1
    -0
      soundchip/target/debug/.fingerprint/soundchip-678087a299b215b4/lib-soundchip-678087a299b215b4
  14. +1
    -0
      soundchip/target/debug/.fingerprint/soundchip-678087a299b215b4/lib-soundchip-678087a299b215b4.json
  15. +1
    -0
      soundchip/target/debug/libsoundchip.d

+ 4
- 0
music_cli/Cargo.lock View File

@ -0,0 +1,4 @@
[root]
name = "music_cli"
version = "0.1.0"

+ 11
- 0
music_cli/Cargo.toml View File

@ -0,0 +1,11 @@
[package]
name = "music_cli"
version = "0.1.0"
authors = ["Erin <erin@hashbang.sh>"]
[dependencies]
log = "0.3"
env_logger = "0.4"
portaudio = "0.7"
soundchip = {path = "../soundchip"}

+ 46
- 0
music_cli/src/main.rs View File

@ -0,0 +1,46 @@
#[macro_use] extern crate log;
extern crate env_logger;
extern crate portaudio;
extern crate soundchip;
use std::error::Error;
use std::sync::mpsc;
use portaudio as pa;
// TODO: replace with config struct, loaded via Serde
const CHANNELS: u32 = 2;
const SAMPLE_RATE: f64 = 44_100.0; // 44.1kHz
const FRAMES_PER_BUFFER: u32 = 64;
fn run() -> Result<(), Box<Error>> {
env_logger::init().unwrap();
info!("Portaudio launching!");
let pa = pa::PortAudio::new()?;
info!("PortAudio:");
info!("version: {}, {}", pa.version(), pa.version_text().unwrap());
let (ctx, crx) = mpsc::channel();
let mut chip = soundchip::Chip::new(crx);
let pa_settings = pa.default_output_stream_settings(CHANNELS as i32, SAMPLE_RATE, FRAMES_PER_BUFFER)?;
let pa_callback = move |pa::OutputStreamCallbackArgs {mut buffer, frames, ..}| {
chip.render_frames(&mut buffer, frames, SAMPLE_RATE, CHANNELS);
pa::Continue
};
let mut pa_stream = pa.open_non_blocking_stream(pa_settings, pa_callback)?;
pa_stream.start()?;
loop {
pa.sleep(1_000);
}
Ok(())
}
fn main() {
if let Err(err) = run() {
error!("{:?}", err);
}
}

+ 0
- 0
music_cli/target/debug/.cargo-lock View File


+ 4
- 0
soundchip/Cargo.lock View File

@ -0,0 +1,4 @@
[root]
name = "soundchip"
version = "0.1.0"

+ 6
- 0
soundchip/Cargo.toml View File

@ -0,0 +1,6 @@
[package]
name = "soundchip"
version = "0.1.0"
authors = ["Erin <erin@hashbang.sh>"]
[dependencies]

+ 94
- 0
soundchip/src/audio.rs View File

@ -0,0 +1,94 @@
use std::{u8, i8, f32};
use std::ops::{Add, Mul};
use std::iter::{Iterator, Sum};
pub type Sample = f32;
#[derive(Debug)]
pub struct Frame {
pub l: Sample,
pub r: Sample,
}
fn mono_pan_amplitudes(angle: f32) -> (f32, f32) {
// L_amp = √2/2 (cosθ - sinθ)
// R_amp = √2/2 (cosθ + sinθ)
let l = (2.0_f32).sqrt()/2.0 * (angle.cos() - angle.sin());
let r = (2.0_f32).sqrt()/2.0 * (angle.cos() + angle.sin());
(l, r)
}
pub fn from_bitpan(angle: i8) -> f32 {
const MAX_ANGLE: f32 = 0.7853982; // +/- 45 deg
(angle as f32) / (i8::max_value() as f32) * MAX_ANGLE
}
pub fn from_bitamp(amp: u8) -> f32 {
(amp as f32) / (u8::max_value() as f32)
}
impl Frame {
pub fn mono_to_stereo(x: Sample, panning: f32) -> Frame {
let (l_amp, r_amp) = mono_pan_amplitudes(panning);
Frame {l: l_amp * x, r: r_amp * x}
}
pub fn zero() -> Frame {
Frame {l: 0., r: 0.,}
}
}
impl Add for Frame {
type Output = Self;
fn add(self, rhs: Frame) -> Frame {
Frame {
l: self.l + rhs.l,
r: self.r + rhs.r,
}
}
}
impl Mul<f32> for Frame {
type Output = Self;
fn mul(self, rhs: f32) -> Frame {
Frame {
l: self.l * rhs,
r: self.r * rhs,
}
}
}
impl Sum for Frame {
fn sum<I: Iterator<Item=Frame>>(iter: I) -> Frame {
iter.fold(Frame::zero(), Add::add)
}
}
pub trait Renderable {
fn render_sample(&mut self, sample_rate: f64) -> Frame;
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn pan_amp() {
let pan_right = mono_pan_amplitudes(0.2);
assert!(pan_right.0 < pan_right.1);
let pan_left = mono_pan_amplitudes(-0.2);
assert!(pan_left.0 > pan_left.1);
let pan_center = mono_pan_amplitudes(0.);
assert!(pan_center.0 == pan_center.1);
}
#[test]
fn bitpan() {
assert_eq!(0., from_bitpan(0));
assert_eq!((45.0_f32).to_radians(), from_bitpan(127));
assert_eq!((-45.0_f32).to_radians(), from_bitpan(-127));
}
}

+ 50
- 0
soundchip/src/channels.rs View File

@ -0,0 +1,50 @@
use ::waveform::Waveform;
use ::audio::{Frame, Renderable, from_bitamp, from_bitpan};
#[derive(Default)]
pub struct Channel<W> where W: Waveform {
waveform: W,
phase: f64,
pub frequency: f64,
pan: f32,
amp: f32,
}
fn next_sample_phase(phase: f64, frequency: f64, sample_rate: f64) -> f64 {
phase + (frequency / sample_rate)
}
impl<W> Channel<W> where W: Waveform + Default {
pub fn new(wavf: W, f0: f64, a0: f32, p0: f32) -> Channel<W> {
Channel {
waveform: wavf,
phase: 0.,
frequency: f0,
pan: p0,
amp: a0,
}
}
pub fn set_pan(&mut self, angle: i8) {
self.pan = from_bitpan(angle);
}
pub fn set_amp(&mut self, amp: u8) {
self.amp = from_bitamp(amp);
}
pub fn note_on(&mut self, note: u8) {}
pub fn note_off(&mut self, note: u8) {}
}
impl<W> Renderable for Channel<W> where W: Waveform {
fn render_sample(&mut self, sample_rate: f64) -> Frame {
let x = Frame::mono_to_stereo(self.waveform.x(self.phase), self.pan);
self.phase = next_sample_phase(self.phase, self.frequency, sample_rate);
x * self.amp
}
}

+ 62
- 0
soundchip/src/lib.rs View File

@ -0,0 +1,62 @@
mod channels;
mod waveform;
mod audio;
use audio::{Frame, Renderable};
use channels::Channel;
use waveform::{Waveform, Saw, Square};
use std::sync::mpsc::Receiver;
pub struct Chip {
pub vol: f32,
rx: Receiver<Message>,
renderables: Vec<Box<Renderable>>,
}
pub enum Message {
NoteOn(usize, u8),
NoteOff(usize, u8),
}
impl Chip {
pub fn new(mpi_rx: Receiver<Message>) -> Chip {
Chip {
vol: 0.25,
rx: mpi_rx,
renderables: Vec::with_capacity(8),
}
}
fn process_messages(&mut self) {
match self.rx.try_recv() {
Ok(m) => match m {
Message::NoteOn(channel, note) => {
}
Message::NoteOff(channel, note) => {
}
},
Err(_) => {} // nop if there's nothing pending in the channel
}
}
pub fn render_sample(&mut self, sample_rate: f64) -> Frame {
self.renderables.iter_mut()
.map(|m| m.render_sample(sample_rate))
.sum()
}
pub fn render_frames(&mut self, buffer: &mut [f32], n_frames: usize,
sample_rate: f64, _channels: u32) {
let mut idx = 0;
for _ in 0..n_frames {
self.process_messages();
let sample = self.render_sample(sample_rate);
buffer[idx] = sample.l;
buffer[idx+1] = sample.r;
idx += 2;
}
}
}

+ 24
- 0
soundchip/src/waveform.rs View File

@ -0,0 +1,24 @@
pub trait Waveform {
fn x(&self, t: f64) -> f32;
}
#[derive(Default, Debug)]
pub struct Saw;
#[derive(Default, Debug)]
pub struct Square;
impl Waveform for Saw {
fn x(&self, t: f64) -> f32 {
((t % 1.0) * 2.0 - 1.0) as f32
}
}
impl Waveform for Square {
fn x(&self, t: f64) -> f32 {
if t % 1.0 < 0.5 {
-1.0
} else {
1.0
}
}
}

+ 0
- 0
soundchip/target/debug/.cargo-lock View File


BIN
soundchip/target/debug/.fingerprint/soundchip-678087a299b215b4/dep-lib-soundchip-678087a299b215b4 View File


+ 1
- 0
soundchip/target/debug/.fingerprint/soundchip-678087a299b215b4/lib-soundchip-678087a299b215b4 View File

@ -0,0 +1 @@
6a6ccedd67c95a31

+ 1
- 0
soundchip/target/debug/.fingerprint/soundchip-678087a299b215b4/lib-soundchip-678087a299b215b4.json View File

@ -0,0 +1 @@
{"rustc":2995933453150367971,"features":"[]","target":7514913036628055629,"profile":4671609428991786967,"deps":[],"local":{"MtimeBased":[[1498694003,977093121],"/home/liam/git/star/soundchip/target/debug/.fingerprint/soundchip-678087a299b215b4/dep-lib-soundchip-678087a299b215b4"]},"rustflags":[]}

+ 1
- 0
soundchip/target/debug/libsoundchip.d View File

@ -0,0 +1 @@
/home/liam/git/star/soundchip/target/debug/libsoundchip.rlib: /home/liam/git/star/soundchip/src/lib.rs

Loading…
Cancel
Save