Browse Source

fmt

master^2
Andreas Linz 2 years ago
parent
commit
99d6308254
Signed by: alinz GPG Key ID: 9BF39809C9DA580C
15 changed files with 466 additions and 447 deletions
  1. +3
    -3
      benches/osc.rs
  2. +2
    -2
      src/dsp/dynamics.rs
  3. +9
    -3
      src/dsp/env_gen.rs
  4. +22
    -12
      src/dsp/filter.rs
  5. +10
    -5
      src/dsp/flow.rs
  6. +9
    -9
      src/dsp/mod.rs
  7. +37
    -29
      src/dsp/voice.rs
  8. +74
    -68
      src/dsp/wavetable.rs
  9. +1
    -1
      src/errors.rs
  10. +4
    -4
      src/event/mod.rs
  11. +59
    -80
      src/io/midi.rs
  12. +2
    -3
      src/io/mod.rs
  13. +98
    -98
      src/io/osc.rs
  14. +135
    -128
      src/main.rs
  15. +1
    -2
      src/types.rs

+ 3
- 3
benches/osc.rs View File

@ -1,9 +1,9 @@
#![feature(test)]
#![feature(test)]
extern crate test;
use test::Bencher;
extern crate ytterbium;
use ytterbium::osc::{Osc,Simple,PhaseAccu,RotMat, Lookup};
use ytterbium::osc::{Lookup, Osc, PhaseAccu, RotMat, Simple};
const FS: usize = 48000;
const F: f64 = 440.0;
@ -46,4 +46,4 @@ fn bench_lookup(b: &mut Bencher) {
osc.tick();
}
})
}
}

+ 2
- 2
src/dsp/dynamics.rs View File

@ -1,6 +1,6 @@
use types::Stereo;
use dsp::{SignalLink, ControllableLink};
use dsp::{ControllableLink, SignalLink};
use event::ControlEvent;
use types::Stereo;
pub struct HardLimiter {}
impl SignalLink for HardLimiter {

+ 9
- 3
src/dsp/env_gen.rs View File

@ -1,9 +1,9 @@
use std::default::Default;
use types::*;
use event::{ControlEvent, Controllable};
use types::*;
#[derive(PartialEq,Debug,Copy,Clone)]
#[derive(PartialEq, Debug, Copy, Clone)]
pub enum ADSRState {
Attack,
Decay,
@ -111,7 +111,13 @@ impl Controllable for ADSR {
self.velocity = Float::from_db((1.0 - velocity) * -30.0);
}
ControlEvent::NoteOff { .. } => self.state_change(ADSRState::Release),
ControlEvent::ADSR { ref id, attack, decay, sustain, release } => {
ControlEvent::ADSR {
ref id,
attack,
decay,
sustain,
release,
} => {
// check path
// TODO: make sure that all values are non-zero!
if *id == self.id {

+ 22
- 12
src/dsp/filter.rs View File

@ -1,8 +1,8 @@
use types::{Float, Stereo, PI};
use dsp::ControllableLink;
use event::ControlEvent;
use types::{Float, Stereo, PI};
#[derive(Debug,Clone,Copy)]
#[derive(Debug, Clone, Copy)]
pub enum FilterType {
LP,
HP,
@ -108,7 +108,11 @@ impl ControllableLink for Filter {
}
fn handle(&mut self, msg: &ControlEvent) {
match *msg {
ControlEvent::Filter { filter_type, freq, q } => {
ControlEvent::Filter {
filter_type,
freq,
q,
} => {
if let Some(some_type) = filter_type {
self.set_filter_type(some_type)
}
@ -118,7 +122,6 @@ impl ControllableLink for Filter {
if let Some(some_q) = q {
self.set_q(some_q)
}
}
_ => {}
}
@ -130,10 +133,10 @@ mod tests {
extern crate hound;
extern crate rand;
use super::{Filter, FilterType};
use self::rand::distributions::{IndependentSample, Range};
use super::super::ControllableLink;
use super::{Filter, FilterType};
use types::{Float, Stereo, MINUS_THREE_DB};
use self::rand::distributions::{IndependentSample, Range};
#[test]
fn test_filter() {
@ -147,10 +150,17 @@ mod tests {
bits_per_sample: 32,
};
let scale = ::std::i32::MAX as Float;
for filter_type in &[FilterType::LP, FilterType::HP, FilterType::BP, FilterType::Notch] {
let filename = format!("ytterbium-{}-{:?}-filter.wav",
env!("CARGO_PKG_VERSION"),
filter_type);
for filter_type in &[
FilterType::LP,
FilterType::HP,
FilterType::BP,
FilterType::Notch,
] {
let filename = format!(
"ytterbium-{}-{:?}-filter.wav",
env!("CARGO_PKG_VERSION"),
filter_type
);
let mut writer = hound::WavWriter::create(filename, wave_spec).unwrap();
const MINUS_SIX_DB: Float = MINUS_THREE_DB * MINUS_THREE_DB;
@ -168,8 +178,8 @@ mod tests {
filter.set_q(q);
filter.set_filter_type(*filter_type);
let multiplier = 1.0 +
((end_freq as Float).ln() - (start_freq).ln()) / num_samples as Float;
let multiplier =
1.0 + ((end_freq as Float).ln() - (start_freq).ln()) / num_samples as Float;
for idx in 0..num_samples {
let r = range.ind_sample(&mut rng);

+ 10
- 5
src/dsp/flow.rs View File

@ -1,9 +1,11 @@
use types::{SharedMut, Stereo, Wrap};
use event::{Controllable, ControlEvent};
use rb::{RbProducer, Producer};
use dsp::{SignalSource, SignalLink, ControllableLink, SignalSink, SignalFlow, VoiceManager,
SoftLimiter, Filter};
use dsp::{
ControllableLink, Filter, SignalFlow, SignalLink, SignalSink, SignalSource, SoftLimiter,
VoiceManager,
};
use event::{ControlEvent, Controllable};
use rb::{Producer, RbProducer};
pub struct Flow {
source: VoiceManager,
@ -14,7 +16,10 @@ impl Flow {
pub fn new(source: VoiceManager, sink: BufferSink, sample_rate: usize) -> Self {
Flow {
source: source,
links: vec![SharedMut::wrap(Filter::new(sample_rate)), SharedMut::wrap(SoftLimiter {})],
links: vec![
SharedMut::wrap(Filter::new(sample_rate)),
SharedMut::wrap(SoftLimiter {}),
],
sink: sink,
}
}

+ 9
- 9
src/dsp/mod.rs View File

@ -1,19 +1,19 @@
use types::Stereo;
use event::ControlEvent;
use types::Stereo;
mod env_gen;
mod wavetable;
mod voice;
mod flow;
mod dynamics;
mod env_gen;
mod filter;
mod flow;
mod voice;
mod wavetable;
pub use self::env_gen::{ADSR, ADSRState};
pub use self::wavetable::{Wavetable, WavetableOsc, generate_wavetables, Waveform};
pub use self::voice::VoiceManager;
pub use self::flow::{Flow, BufferSink};
pub use self::dynamics::{HardLimiter, SoftLimiter};
pub use self::env_gen::{ADSRState, ADSR};
pub use self::filter::{Filter, FilterType};
pub use self::flow::{BufferSink, Flow};
pub use self::voice::VoiceManager;
pub use self::wavetable::{generate_wavetables, Waveform, Wavetable, WavetableOsc};
pub trait SignalSource {
fn tick(&mut self) -> Stereo;

+ 37
- 29
src/dsp/voice.rs View File

@ -1,15 +1,15 @@
extern crate itertools;
use std::collections::{VecDeque, HashMap};
use self::itertools::Zip;
use std::collections::{HashMap, VecDeque};
use std::rc::Rc;
use types::*;
use dsp;
use dsp::env_gen::*;
use dsp::wavetable::*;
use io::PitchConvert;
use event::{ControlEvent, Controllable};
use io::PitchConvert;
use std::rc::Rc;
use types::*;
use dsp::SignalSource;
@ -23,23 +23,22 @@ pub struct Voice {
oscillators: Vec<WavetableOsc>,
}
impl Voice {
fn new(sample_rate: usize,
wavetables: Rc<HashMap<Waveform, Vec<Wavetable>>>,
pitch_convert_handle: Rc<PitchConvert>)
-> Self {
fn new(
sample_rate: usize,
wavetables: Rc<HashMap<Waveform, Vec<Wavetable>>>,
pitch_convert_handle: Rc<PitchConvert>,
) -> Self {
let mut levels = Vec::with_capacity(OSC_CNT);
let mut oscillators = Vec::with_capacity(OSC_CNT);
let mut volume_envelopes = Vec::with_capacity(OSC_CNT);
for idx in 0..OSC_CNT {
levels.push(if idx == 0 {
MINUS_THREE_DB
} else {
0.0
});
oscillators.push(WavetableOsc::with_id(format!("OSC{}", idx + 1),
sample_rate,
wavetables.clone(),
pitch_convert_handle.clone()));
levels.push(if idx == 0 { MINUS_THREE_DB } else { 0.0 });
oscillators.push(WavetableOsc::with_id(
format!("OSC{}", idx + 1),
sample_rate,
wavetables.clone(),
pitch_convert_handle.clone(),
));
volume_envelopes.push(ADSR::with_id(sample_rate, format!("ADSR-OSC{}", idx + 1)));
}
Voice {
@ -52,20 +51,23 @@ impl Voice {
}
}
fn running(&self) -> bool {
self.volume_envelopes.iter().all(|envelope| envelope.state() != ADSRState::Off)
self.volume_envelopes
.iter()
.all(|envelope| envelope.state() != ADSRState::Off)
}
fn tick(&mut self) -> Stereo {
let mut samples = [0.0; OSC_CNT];
let mut frame = Stereo::default();
// tick each oscillator + apply env
for (idx, (sample, oscillator, envelope, level, pan)) in
Zip::new((&mut samples,
&mut self.oscillators,
&mut self.volume_envelopes,
&self.levels,
&self.pan))
.take(OSC_CNT)
.enumerate() {
for (idx, (sample, oscillator, envelope, level, pan)) in Zip::new((
&mut samples,
&mut self.oscillators,
&mut self.volume_envelopes,
&self.levels,
&self.pan,
)).take(OSC_CNT)
.enumerate()
{
*sample = oscillator.tick() * envelope.tick();
frame += Stereo(*sample, *sample) * *level * *pan;
}
@ -115,12 +117,14 @@ impl Controllable for Voice {
"OSC4" => 3,
_ => self.fm_mod.len(), // offset is larger than the length of the vector -> no modification
} * OSC_CNT;
for (idx, (old_level, new_level)) in self.fm_mod
for (idx, (old_level, new_level)) in self
.fm_mod
.iter_mut()
.skip(offset)
.take(OSC_CNT)
.zip(levels.iter())
.enumerate() {
.enumerate()
{
*old_level = if idx == offset {
*new_level * 0.1 // feedback modulation easily creates feedback
} else {
@ -150,7 +154,11 @@ impl VoiceManager {
let pitch_convert = Rc::new(PitchConvert::default());
let mut voices = Vec::with_capacity(max_voices);
for _ in 0..max_voices {
voices.push(Voice::new(sample_rate, wavetables.clone(), pitch_convert.clone()));
voices.push(Voice::new(
sample_rate,
wavetables.clone(),
pitch_convert.clone(),
));
}
VoiceManager {
voices: voices,

+ 74
- 68
src/dsp/wavetable.rs View File

@ -1,22 +1,22 @@
extern crate rustfft;
extern crate bincode;
extern crate num;
extern crate rand;
extern crate bincode;
extern crate rustfft;
use self::bincode::rustc_serialize::{decode_from, encode_into};
use self::bincode::SizeLimit;
use self::rustfft::algorithm::Radix4;
use self::rustfft::num_complex::Complex;
use self::rustfft::num_traits::Zero;
use self::rustfft::FFT;
use std::collections::HashMap;
use std::rc::Rc;
use std::fs::File;
use std::io::{BufReader, BufWriter};
use self::bincode::rustc_serialize::{decode_from, encode_into};
use self::bincode::SizeLimit;
use std::rc::Rc;
use types::*;
use io::PitchConvert;
use event::{ControlEvent, Controllable};
use io::PitchConvert;
use types::*;
const OVERSAMPLING: usize = 2;
const INVERSE: bool = true;
@ -39,19 +39,18 @@ impl Wavetable {
/// The phase is mapped to a table index.
fn sample(&self, phasor: Float) -> Float {
let table_len = self.table.len();
let idx = if phasor < 0.0 {
phasor + 1.0
} else {
phasor
} * table_len as Float;
let idx = if phasor < 0.0 { phasor + 1.0 } else { phasor } * table_len as Float;
// linear interpolation
let (i, j) = (idx.floor() as usize % table_len, idx.ceil() as usize % table_len);
let (i, j) = (
idx.floor() as usize % table_len,
idx.ceil() as usize % table_len,
);
self.table[i] + (self.table[j] - self.table[i]) * (idx - i as Float)
}
}
/// Implemented waveforms.
#[derive(PartialEq,Eq,Hash,Debug,Copy,Clone)]
#[derive(PartialEq, Eq, Hash, Debug, Copy, Clone)]
pub enum Waveform {
Sine,
Saw,
@ -65,7 +64,9 @@ pub enum Waveform {
macro_rules! scale {
($flag:expr, $signal:expr) => {
if $flag {
let scale = $signal.iter().fold(0.0, |acc: Float, val| acc.max(val.re.abs()));
let scale = $signal
.iter()
.fold(0.0, |acc: Float, val| acc.max(val.re.abs()));
for sample in &mut $signal {
sample.re *= scale.recip();
}
@ -74,19 +75,24 @@ macro_rules! scale {
}
/// Builds wavetables for each waveform and returns a `HashMap` containing them.
pub fn generate_wavetables(fundamental_freq: Float,
sample_rate: usize)
-> HashMap<Waveform, Vec<Wavetable>> {
pub fn generate_wavetables(
fundamental_freq: Float,
sample_rate: usize,
) -> HashMap<Waveform, Vec<Wavetable>> {
let mut tables: HashMap<Waveform, Vec<Wavetable>> = HashMap::new();
for waveform in &[Waveform::Saw,
Waveform::Sine,
Waveform::Square,
Waveform::Tri,
Waveform::SharpTri,
Waveform::Random] {
let filename = format!("ytterbium-{}-wavetable-{:?}.bin",
env!("CARGO_PKG_VERSION"),
waveform);
for waveform in &[
Waveform::Saw,
Waveform::Sine,
Waveform::Square,
Waveform::Tri,
Waveform::SharpTri,
Waveform::Random,
] {
let filename = format!(
"ytterbium-{}-wavetable-{:?}.bin",
env!("CARGO_PKG_VERSION"),
waveform
);
let band_limited_table = {
if let Ok(file) = File::open(&filename) {
let mut reader = BufReader::new(file);
@ -94,8 +100,10 @@ pub fn generate_wavetables(fundamental_freq: Float,
.expect(&format!("could not decode wavetable: {}", filename))
} else {
let band_limited_table = build_wavetables(*waveform, fundamental_freq, sample_rate);
let file = File::create(&filename)
.expect(&format!("could not create file for wavetable: {}", filename));
let file = File::create(&filename).expect(&format!(
"could not create file for wavetable: {}",
filename
));
let mut writer = BufWriter::new(file);
encode_into(&band_limited_table, &mut writer, SizeLimit::Infinite)
.expect(&format!("could not encode wavetable: {}", filename));
@ -109,10 +117,11 @@ pub fn generate_wavetables(fundamental_freq: Float,
/// Builds the band-limited wavetables for the given waveform, fundamental frequency and
/// sample rate.
fn build_wavetables(waveform: Waveform,
fundamental_freq: Float,
sample_rate: usize)
-> Vec<Wavetable> {
fn build_wavetables(
waveform: Waveform,
fundamental_freq: Float,
sample_rate: usize,
) -> Vec<Wavetable> {
let min_table_size = 64;
let mut phase_incr = fundamental_freq * 2.0 / sample_rate as Float;
let (mut harmonics, mut table_size) = match waveform {
@ -154,10 +163,7 @@ fn generate_spectrum(waveform: Waveform, harmonics: usize, spectrum: &mut Vec
let table_size = spectrum.len();
if harmonics == 1 {
// use a pure sine
spectrum[1] = Complex {
re: 1.0,
im: -1.0,
};
spectrum[1] = Complex { re: 1.0, im: -1.0 };
spectrum[table_size - 1] = -spectrum[1];
return;
}
@ -184,11 +190,7 @@ fn generate_spectrum(waveform: Waveform, harmonics: usize, spectrum: &mut Vec
}
Waveform::Tri => {
for i in (1..harmonics).filter(|i| i % 2 == 1) {
let sign = if i % 4 == 1 {
1.0
} else {
-1.0
};
let sign = if i % 4 == 1 { 1.0 } else { -1.0 };
let magnitude = ((i * i) as Float).recip();
spectrum[i] = Complex {
re: 1.0,
@ -199,11 +201,7 @@ fn generate_spectrum(waveform: Waveform, harmonics: usize, spectrum: &mut Vec
}
Waveform::SharpTri => {
for i in (1..harmonics).filter(|i| i % 2 == 1) {
let sign = if i % 4 == 1 {
1.0
} else {
-1.0
};
let sign = if i % 4 == 1 { 1.0 } else { -1.0 };
let magnitude = (i as Float).recip();
spectrum[i] = Complex {
re: 1.0,
@ -244,10 +242,11 @@ pub struct WavetableOsc {
}
impl WavetableOsc {
/// Constructs a wavetable oscillator for the given sample rate.
pub fn new(sample_rate: usize,
wavetables: Rc<HashMap<Waveform, Vec<Wavetable>>>,
pitch_convert: Rc<PitchConvert>)
-> Self {
pub fn new(
sample_rate: usize,
wavetables: Rc<HashMap<Waveform, Vec<Wavetable>>>,
pitch_convert: Rc<PitchConvert>,
) -> Self {
WavetableOsc {
phase_incr: 0.0,
sample_rate: sample_rate,
@ -265,11 +264,12 @@ impl WavetableOsc {
}
}
pub fn with_id<S: Into<String>>(id: S,
sample_rate: usize,
wavetables: Rc<HashMap<Waveform, Vec<Wavetable>>>,
pitch_convert: Rc<PitchConvert>)
-> Self {
pub fn with_id<S: Into<String>>(
id: S,
sample_rate: usize,
wavetables: Rc<HashMap<Waveform, Vec<Wavetable>>>,
pitch_convert: Rc<PitchConvert>,
) -> Self {
let mut osc = WavetableOsc::new(sample_rate, wavetables, pitch_convert);
osc.set_id(id);
osc
@ -358,9 +358,11 @@ impl Controllable for WavetableOsc {
}
ControlEvent::Detune { ref id, detune } => {
if *id == self.id {
let (low, current, high) = (self.pitch_convert.key_to_hz(self.key - 1),
self.pitch_convert.key_to_hz(self.key),
self.pitch_convert.key_to_hz(self.key + 1));
let (low, current, high) = (
self.pitch_convert.key_to_hz(self.key - 1),
self.pitch_convert.key_to_hz(self.key),
self.pitch_convert.key_to_hz(self.key + 1),
);
// linear approximation of cents
let cent = if detune < 0 {
(low - current)
@ -392,15 +394,19 @@ fn test_wavetable_sweep() {
sample_rate: SAMPLE_RATE as u32,
bits_per_sample: 32,
};
for waveform in &[Waveform::Sine,
Waveform::Saw,
Waveform::Square,
Waveform::Tri,
Waveform::SharpTri,
Waveform::Random] {
let filename = format!("ytterbium-{}-{:?}-sweep.wav",
env!("CARGO_PKG_VERSION"),
waveform);
for waveform in &[
Waveform::Sine,
Waveform::Saw,
Waveform::Square,
Waveform::Tri,
Waveform::SharpTri,
Waveform::Random,
] {
let filename = format!(
"ytterbium-{}-{:?}-sweep.wav",
env!("CARGO_PKG_VERSION"),
waveform
);
// An existing file will be overwritten.
osc.reset();
let mut writer = hound::WavWriter::create(filename, wave_spec).unwrap();

+ 1
- 1
src/errors.rs View File

@ -1,5 +1,5 @@
extern crate rosc;
extern crate portmidi;
extern crate rosc;
#[derive(Debug)]
pub enum RunError {

+ 4
- 4
src/event/mod.rs View File

@ -1,17 +1,17 @@
extern crate rosc;
extern crate portmidi;
extern crate rosc;
use types::*;
use dsp::{Waveform, FilterType};
use dsp::{FilterType, Waveform};
macro_rules! feq {
($lhs:expr, $rhs:expr) => {
($lhs - $rhs).abs() < 1.0E-7
}
};
}
#[derive(Debug,Clone)]
#[derive(Debug, Clone)]
pub enum ControlEvent {
Unsupported,
NoteOn {

+ 59
- 80
src/io/midi.rs View File

@ -1,10 +1,10 @@
extern crate portmidi;
use types::*;
use errors::RunError;
use std::sync::mpsc;
use std::time::Duration;
use std::thread;
use std::time::Duration;
use types::*;
use io::Receiver;
@ -23,16 +23,15 @@ impl MidiReceiver {
pub fn new() -> Result<Self, RunError> {
const BUF_LEN: usize = 1024;
let context = try!(portmidi::PortMidi::new().map_err(RunError::MidiError));
let in_devices = context.devices()
let in_devices = context
.devices()
.unwrap()
.into_iter()
.filter(|dev| dev.is_input())
.collect::<Vec<portmidi::DeviceInfo>>();
let in_ports = in_devices.into_iter()
.filter_map(|dev| {
context.input_port(dev, BUF_LEN)
.ok()
})
let in_ports = in_devices
.into_iter()
.filter_map(|dev| context.input_port(dev, BUF_LEN).ok())
.collect::<Vec<portmidi::InputPort>>();
if in_ports.is_empty() {
Err(RunError::NoMidiDeviceAvailable)
@ -46,26 +45,23 @@ impl MidiReceiver {
}
}
impl MidiReceiver {
fn receive(&self,
port: &portmidi::InputPort)
-> Result<Option<Vec<portmidi::MidiEvent>>, RunError> {
fn receive(
&self,
port: &portmidi::InputPort,
) -> Result<Option<Vec<portmidi::MidiEvent>>, RunError> {
port.read_n(self.buf_len).map_err(RunError::MidiError)
}
fn to_control_event(&self, event: MidiEvent) -> ControlEvent {
match event {
MidiEvent::NoteOn { key, velocity, .. } => {
ControlEvent::NoteOn {
key: key,
velocity: velocity,
}
}
MidiEvent::NoteOff { key, velocity, .. } => {
ControlEvent::NoteOff {
key: key,
velocity: velocity,
}
}
MidiEvent::NoteOn { key, velocity, .. } => ControlEvent::NoteOn {
key: key,
velocity: velocity,
},
MidiEvent::NoteOff { key, velocity, .. } => ControlEvent::NoteOff {
key: key,
velocity: velocity,
},
_ => ControlEvent::Unsupported,
}
}
@ -86,7 +82,8 @@ impl Receiver for MidiReceiver {
// event_buf.sort_by_key(|e| e.timestamp);
while let Some(event) = event_buf.pop() {
tx.send(self.to_control_event(MidiEvent::from(event))).unwrap();
tx.send(self.to_control_event(MidiEvent::from(event)))
.unwrap();
}
thread::sleep(timeout);
@ -101,12 +98,10 @@ impl From for MidiEvent {
let data2 = event.message.data2;
match status {
0xF0 => MidiEvent::SysEx,
0xF1 => {
MidiEvent::TimeCodeQuarterFrame {
msg_type: ((data1 & 0xF0) >> 4) as u8,
value: (data1 & 0x0F) as u8,
}
}
0xF1 => MidiEvent::TimeCodeQuarterFrame {
msg_type: ((data1 & 0xF0) >> 4) as u8,
value: (data1 & 0x0F) as u8,
},
0xF2 => MidiEvent::SongPosition(data1 as u16 + ((data2 as u16) << 8)),
0xF3 => MidiEvent::SongSelect(data1 as u8),
0xF6 => MidiEvent::TuneRequest,
@ -122,57 +117,41 @@ impl From for MidiEvent {
let channel = status & 0x0F;
// TODO: nested enum for ChannelMode messages?
match status & 0xF0 {
0x80 => {
MidiEvent::NoteOff {
key: data1 as u8,
velocity: data2 as Float / 127.0,
channel: channel,
}
}
0x90 => {
MidiEvent::NoteOn {
key: data1 as u8,
velocity: data2 as Float / 127.0,
channel: channel,
}
}
0xA0 => {
MidiEvent::PolyphonicKeyPressure {
key: data1 as u8,
velocity: data2 as Float / 127.0,
channel: channel,
}
}
0xB0 => {
match data1 {
120...127 => MidiEvent::Unsupported,
_ => {
MidiEvent::ControlChange {
controller: data1 as u8,
value: data2 as Float / 127.0,
channel: channel,
}
}
}
}
0xC0 => {
MidiEvent::ProgramChange {
program: data1 as u8,
channel: channel,
}
}
0xD0 => {
MidiEvent::ChannelPressure {
pressure: data1 as u8,
channel: channel,
}
}
0xE0 => {
MidiEvent::PitchBend {
pitchbend: data1 as u16 + ((data2 as u16) << 8),
0x80 => MidiEvent::NoteOff {
key: data1 as u8,
velocity: data2 as Float / 127.0,
channel: channel,
},
0x90 => MidiEvent::NoteOn {
key: data1 as u8,
velocity: data2 as Float / 127.0,
channel: channel,
},
0xA0 => MidiEvent::PolyphonicKeyPressure {
key: data1 as u8,
velocity: data2 as Float / 127.0,
channel: channel,
},
0xB0 => match data1 {
120...127 => MidiEvent::Unsupported,
_ => MidiEvent::ControlChange {
controller: data1 as u8,
value: data2 as Float / 127.0,
channel: channel,
}
}
},
},
0xC0 => MidiEvent::ProgramChange {
program: data1 as u8,
channel: channel,
},
0xD0 => MidiEvent::ChannelPressure {
pressure: data1 as u8,
channel: channel,
},
0xE0 => MidiEvent::PitchBend {
pitchbend: data1 as u16 + ((data2 as u16) << 8),
channel: channel,
},
_ => MidiEvent::Unknown,
}
}

+ 2
- 3
src/io/mod.rs View File

@ -1,8 +1,8 @@
mod midi;
mod osc;
use std::sync::mpsc;
use event::ControlEvent;
use std::sync::mpsc;
pub use self::midi::*;
pub use self::osc::*;
@ -27,8 +27,7 @@ impl PitchConvert {
let dist_concert_a = key as isize - 69;
let two: Float = 2.0;
two.powf(dist_concert_a as Float / 12.0) * tune_freq
})
.collect::<Vec<_>>(),
}).collect::<Vec<_>>(),
}
}

+ 98
- 98
src/io/osc.rs View File

@ -1,21 +1,21 @@
extern crate rosc;
use rosc::{OscPacket, OscMessage, OscType};
use rosc::{OscMessage, OscPacket, OscType};
use errors::RunError;
use std::net::{UdpSocket, SocketAddr};
use std::net::{SocketAddr, UdpSocket};
use std::sync::mpsc;
use io::Receiver;
use dsp::{FilterType, Waveform};
use event::ControlEvent;
use dsp::{Waveform, FilterType};
use types::*;
macro_rules! exp_scale {
($val:expr) => {
((($val as Float) * ::std::f64::consts::LN_10).exp() - 1.0)/10.0
}
((($val as Float) * ::std::f64::consts::LN_10).exp() - 1.0) / 10.0
};
}
const TRANSPOSE_RANGE: usize = 6; // Octaves
@ -40,16 +40,22 @@ impl OscReceiver {
}
impl OscReceiver {
fn receive(&mut self) -> Result<OscPacket, RunError> {
let (size, _) = try!(self.socket
.recv_from(&mut self.buf)
.map_err(RunError::IoError));
let (size, _) = try!(
self.socket
.recv_from(&mut self.buf)
.map_err(RunError::IoError)
);
rosc::decoder::decode(&self.buf[..size]).map_err(RunError::OscError)
}
fn as_control_event(&mut self, packet: OscPacket) -> Vec<ControlEvent> {
let mut events = Vec::new();
for msg in Self::unwrap_packet(packet) {
let parts = msg.addr.split('/').filter(|s| !s.is_empty()).collect::<Vec<&str>>();
let parts = msg
.addr
.split('/')
.filter(|s| !s.is_empty())
.collect::<Vec<&str>>();
if parts.is_empty() {
continue;
}
@ -114,118 +120,113 @@ impl OscReceiver {
}
}
fn handle_oscillators(&mut self,
msg: &OscMessage,
address: &[&str],
events: &mut Vec<ControlEvent>) {
fn handle_oscillators(
&mut self,
msg: &OscMessage,
address: &[&str],
events: &mut Vec<ControlEvent>,
) {
if address.len() < 3 {
return;
}
// TODO: refactor this mess
match address[0] {
"FILTER" => {
match (address[1], address[2]) {
("FILTERTYPE", "selection") => {
"FILTER" => match (address[1], address[2]) {
("FILTERTYPE", "selection") => {
let args = msg.args.as_ref().unwrap();
if let OscType::Float(selection) = args[0] {
let filter_type = match selection as usize {
0 => Some(FilterType::LP),
1 => Some(FilterType::HP),
2 => Some(FilterType::BP),
3 => Some(FilterType::Notch),
_ => None,
};
events.push(ControlEvent::Filter {
filter_type: filter_type,
freq: None,
q: None,
})
}
}
("FREQRES", _) => match address[2] {
"x" => {
let args = msg.args.as_ref().unwrap();
if let OscType::Float(selection) = args[0] {
let filter_type = match selection as usize {
0 => Some(FilterType::LP),
1 => Some(FilterType::HP),
2 => Some(FilterType::BP),
3 => Some(FilterType::Notch),
_ => None,
};
if let OscType::Float(freq) = args[0] {
events.push(ControlEvent::Filter {
filter_type: filter_type,
freq: None,
filter_type: None,
freq: Some(40. + 20_000.0 * freq as Float),
q: None,
})
});
}
}
("FREQRES", _) => {
match address[2] {
"x" => {
let args = msg.args.as_ref().unwrap();
if let OscType::Float(freq) = args[0] {
events.push(ControlEvent::Filter {
filter_type: None,
freq: Some(40. + 20_000.0 * freq as Float),
q: None,
});
}
}
"y" => {
let args = msg.args.as_ref().unwrap();
if let OscType::Float(q) = args[0] {
events.push(ControlEvent::Filter {
filter_type: None,
freq: None,
q: Some(4. * q as Float),
});
}
}
_ => {}
"y" => {
let args = msg.args.as_ref().unwrap();
if let OscType::Float(q) = args[0] {
events.push(ControlEvent::Filter {
filter_type: None,
freq: None,
q: Some(4. * q as Float),
});
}
}
_ => {}
},
_ => {}
},
"MIX" => match (address[1], address[2]) {
("PAN", "x") => {
let args = msg
.args
.as_ref()
.unwrap()
.iter()
.map(|arg| match *arg {
OscType::Float(val) => val as Float,
_ => 0.0,
}).collect::<Vec<_>>();
events.push(ControlEvent::Pan(args));
}
}
"MIX" => {
match (address[1], address[2]) {
("PAN", "x") => {
let args = msg.args
.as_ref()
.unwrap()
.iter()
.map(|arg| match *arg {
OscType::Float(val) => val as Float,
_ => 0.0,
})
.collect::<Vec<_>>();
events.push(ControlEvent::Pan(args));
}
("VOLUME", "x") => {
let args = msg.args
("VOLUME", "x") => {
let args = msg
.args
.as_ref()
.unwrap()
.iter()
.map(|arg| match *arg {
OscType::Float(val) => (1.0 - val as Float) * -61.0,
_ => 0.0,
}).collect::<Vec<_>>();
events.push(ControlEvent::Volume(args));
}
_ => {}
},
"FM" => match address[1] {
"OSC1" | "OSC2" | "OSC3" | "OSC4" => {
if let ("LEVEL", "x") = (address[2], address[3]) {
let args = msg
.args
.as_ref()
.unwrap()
.iter()
.map(|arg| match *arg {
OscType::Float(val) => (1.0 - val as Float) * -61.0,
OscType::Float(val) => val as Float,
_ => 0.0,
})
.collect::<Vec<_>>();
events.push(ControlEvent::Volume(args));
}
_ => {}
}
}
"FM" => {
match address[1] {
"OSC1" | "OSC2" | "OSC3" | "OSC4" => {
if let ("LEVEL", "x") = (address[2], address[3]) {
let args = msg.args
.as_ref()
.unwrap()
.iter()
.map(|arg| match *arg {
OscType::Float(val) => val as Float,
_ => 0.0,
})
.collect::<Vec<Float>>();
events.push(ControlEvent::FM {
id: address[1].to_owned(),
levels: args,
});
}
}).collect::<Vec<Float>>();
events.push(ControlEvent::FM {
id: address[1].to_owned(),
levels: args,
});
}
_ => {}
}
}
_ => {}
},
_ => {
match (address[1], address[2]) {
("ADSR", "x") => {
let args = msg.args
let args = msg
.args
.as_ref()
.unwrap()
.iter()
@ -235,8 +236,7 @@ impl OscReceiver {
OscType::Float(val) => exp_scale!(val),
_ => 1.0E-4,
}
})
.collect::<Vec<Float>>();
}).collect::<Vec<Float>>();
let id_prefix: String = "ADSR-".to_owned();
events.push(ControlEvent::ADSR {
id: id_prefix + address[0],

+ 135
- 128
src/main.rs View File

@ -2,36 +2,36 @@
extern crate approx;
extern crate rustc_serialize;
extern crate rb;
extern crate rosc;
extern crate rsoundio;
extern crate rb;
extern crate clap;
use std::cmp;
use std::collections::HashMap;
use std::thread;
use std::sync::mpsc;
use std::sync::{Arc, Barrier};
use std::sync::atomic::{AtomicBool, Ordering};
use std::str::FromStr;
use std::io::Write;
use std::net::{IpAddr, SocketAddr};
use std::process;
use std::io::Write;
use std::str::FromStr;
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::mpsc;
use std::sync::{Arc, Barrier};
use std::thread;
use rb::{RB, RbConsumer};
use rb::{RbConsumer, RB};
#[macro_use]
mod event;
mod io;
mod dsp;
mod types;
mod errors;
use io::{Receiver, OscReceiver, MidiReceiver};
mod io;
mod types;
use dsp::*;
use errors::RunError;
use event::Controllable;
use io::{MidiReceiver, OscReceiver, Receiver};
use types::*;
use errors::RunError;
const VERSION: &'static str = env!("CARGO_PKG_VERSION");
const BUF_SIZE: usize = 2048;
@ -86,7 +86,8 @@ fn get_args() -> Args {
.arg(sample_rate_arg)
.get_matches();
let sample_rate = args.value_of("sample-rate")
let sample_rate = args
.value_of("sample-rate")
.map_or(48_000, |str_val| str_val.parse::<usize>().unwrap());
let ip_addr = match IpAddr::from_str(args.value_of("address").unwrap()) {
Ok(val) => val,
@ -95,20 +96,20 @@ fn get_args() -> Args {
process::exit(1)
}
};
let ports = args.values_of("ports")
let ports = args
.values_of("ports")
.unwrap()
.map(|port| {
match port.parse::<u16>() {
Ok(val) => val,
Err(err) => {
printerr!("Bad port, must be in range [0, 65535]: {}", err);
process::exit(1)
}
.map(|port| match port.parse::<u16>() {
Ok(val) => val,
Err(err) => {
printerr!("Bad port, must be in range [0, 65535]: {}", err);
process::exit(1)
}
})
.collect::<Vec<u16>>();
let (socket_addr_in, socket_addr_out) = (SocketAddr::new(ip_addr, ports[0]),
SocketAddr::new(ip_addr, ports[1]));
}).collect::<Vec<u16>>();
let (socket_addr_in, socket_addr_out) = (
SocketAddr::new(ip_addr, ports[0]),
SocketAddr::new(ip_addr, ports[1]),
);
Args {
socket_addr_in: socket_addr_in,
@ -123,8 +124,7 @@ fn main() {
.map_err(|err| {
printerr!("{:?}", err);
process::exit(1)
})
.unwrap();
}).unwrap();
}
fn run(args: Args) -> Result<(), RunError> {
@ -135,113 +135,118 @@ fn run(args: Args) -> Result<(), RunError> {
let mut handles = HashMap::with_capacity(5);
let quit = Arc::new(AtomicBool::new(false));
handles.insert("osc",
thread::Builder::new()
.name("osc".to_owned())
.spawn({
let tx = tx_receiver.clone();
let socket_addr = args.socket_addr_in;
move || {
let mut osc_receiver = OscReceiver::new(socket_addr).unwrap();
osc_receiver.receive_and_send(tx)
}
})
.unwrap());
handles.insert(
"osc",
thread::Builder::new()
.name("osc".to_owned())
.spawn({
let tx = tx_receiver.clone();
let socket_addr = args.socket_addr_in;
move || {
let mut osc_receiver = OscReceiver::new(socket_addr).unwrap();
osc_receiver.receive_and_send(tx)
}
}).unwrap(),
);
handles.insert("midi",
thread::Builder::new()
.name("midi".to_owned())
.spawn({
let tx = tx_receiver.clone();
move || {
let mut midi_receiver = MidiReceiver::new().unwrap();
midi_receiver.receive_and_send(tx)
}
})
.unwrap());
handles.insert(
"midi",
thread::Builder::new()
.name("midi".to_owned())
.spawn({
let tx = tx_receiver.clone();
move || {
let mut midi_receiver = MidiReceiver::new().unwrap();
midi_receiver.receive_and_send(tx)
}
}).unwrap(),
);
handles.insert("dsp",
thread::Builder::new()
.name("dsp".to_owned())
.spawn({
let init = audio_init.clone();
let quit = quit.clone();
let sample_rate = args.sample_rate;
move || {
let source = VoiceManager::new(MAX_VOICES, sample_rate);
let sink = BufferSink::new(producer, 64);
let mut flow = Flow::new(source, sink, sample_rate);
init.wait();
loop {
if quit.load(Ordering::Relaxed) {
break;
handles.insert(
"dsp",
thread::Builder::new()
.name("dsp".to_owned())
.spawn({
let init = audio_init.clone();
let quit = quit.clone();
let sample_rate = args.sample_rate;
move || {
let source = VoiceManager::new(MAX_VOICES, sample_rate);
let sink = BufferSink::new(producer, 64);
let mut flow = Flow::new(source, sink, sample_rate);
init.wait();
loop {
if quit.load(Ordering::Relaxed) {
break;
}
if let Ok(msg) = rx_dsp.try_recv() {
flow.handle(&msg);
}
flow.tick();
}
if let Ok(msg) = rx_dsp.try_recv() {
flow.handle(&msg);
}
flow.tick();
}
}
})
.unwrap());
}).unwrap(),
);
handles.insert("output",
thread::Builder::new()
.name("output".to_owned())
.spawn({
let init = audio_init.clone();
let sample_rate = args.sample_rate as u32;
move || {
let sio = rsoundio::SoundIo::new("ytterbium");
// connect to default backend
sio.connect().unwrap();
sio.flush_events();
let dev = sio.default_output_device().unwrap();
let mut out_stream = dev.create_outstream().unwrap();
out_stream.set_name("ytterbium").ok();
out_stream.set_format(rsoundio::SioFormat::Float32LE).unwrap();
out_stream.set_sample_rate(sample_rate);
handles.insert(
"output",
thread::Builder::new()
.name("output".to_owned())
.spawn({
let init = audio_init.clone();
let sample_rate = args.sample_rate as u32;
move || {
let sio = rsoundio::SoundIo::new("ytterbium");
// connect to default backend
sio.connect().unwrap();
sio.flush_events();
let dev = sio.default_output_device().unwrap();
let mut out_stream = dev.create_outstream().unwrap();
out_stream.set_name("ytterbium").ok();
out_stream
.set_format(rsoundio::SioFormat::Float32LE)
.unwrap();
out_stream.set_sample_rate(sample_rate);
init.wait();
out_stream.register_write_callback(|out: rsoundio::OutStream,
min_frame_count: u32,
max_frame_count: u32| {
let len = cmp::min(BUF_SIZE, max_frame_count as usize);
let mut data: Vec<Stereo> = vec![Stereo::default(); BUF_SIZE];
consumer.read_blocking(&mut data[..len]).unwrap();
let mut frames = vec![Vec::with_capacity(len),
Vec::with_capacity(len)];
for &frame in &data[..len] {
frames[0].push(frame.0 as f32);
frames[1].push(frame.1 as f32);
}
match out.write_stream_f32(min_frame_count, &frames) {
Ok(_) => (),
Err(err) =>