big ass refactor (rip client)

This commit is contained in:
🪞👃🪞 2024-07-03 14:51:48 +03:00
parent 94c1f83ef2
commit 8c3cf53c67
56 changed files with 2232 additions and 1891 deletions

46
src/model/chain.rs Normal file
View file

@ -0,0 +1,46 @@
use crate::{core::*, view::*};
pub struct Chain {
pub name: String,
pub focused: bool,
pub focus: usize,
pub items: Vec<Box<dyn Device>>,
pub view: ChainViewMode,
pub adding: bool,
}
render!(Chain = crate::view::chain::render);
handle!(Chain = crate::control::chain::handle);
process!(Chain);
impl Chain {
pub fn new (name: &str, items: Option<Vec<Box<dyn Device>>>) -> Usually<Self> {
Ok(Self {
name: name.into(),
focused: false,
focus: 0,
items: items.unwrap_or_else(||vec![]),
view: ChainViewMode::Column,
adding: false
})
}
}
impl PortList for Chain {
fn midi_ins (&self) -> Usually<Vec<String>> {
if let Some(device) = self.items.get(0) {
device.midi_ins()
} else {
Ok(vec![])
}
}
fn audio_outs (&self) -> Usually<Vec<String>> {
if let Some(device) = self.items.get(self.items.len().saturating_sub(1)) {
device.audio_outs()
} else {
Ok(vec![])
}
}
}
pub fn process (_: &mut Chain, _: &Client, _: &ProcessScope) -> Control {
Control::Continue
}

184
src/model/launcher.rs Normal file
View file

@ -0,0 +1,184 @@
use crate::{core::*, model::*};
pub enum LauncherMode {
Tracks,
Sequencer,
Chains
}
impl LauncherMode {
pub fn is_chains (&self) -> bool {
match self { Self::Chains => true, _ => false }
}
pub fn is_tracks (&self) -> bool {
match self { Self::Tracks => true, _ => false }
}
pub fn is_sequencer (&self) -> bool {
match self { Self::Sequencer => true, _ => false }
}
}
pub struct Launcher {
pub name: String,
pub timebase: Arc<Timebase>,
pub transport: ::jack::Transport,
pub playing: TransportState,
pub monitoring: bool,
pub recording: bool,
pub overdub: bool,
pub current_frame: usize,
pub cursor: (usize, usize),
pub tracks: Vec<Track>,
pub scenes: Vec<Scene>,
pub show_help: bool,
pub view: LauncherMode,
}
render!(Launcher = crate::view::launcher::render);
handle!(Launcher = crate::control::launcher::handle);
process!(Launcher |self, _client, _scope| {
let transport = self.transport.query().unwrap();
self.playing = transport.state;
self.current_frame = transport.pos.frame() as usize;
Control::Continue
});
impl PortList for Launcher {}
impl Launcher {
pub fn new (
name: &str,
timebase: &Arc<Timebase>,
tracks: Option<Vec<Track>>,
scenes: Option<Vec<Scene>>
) -> Result<Self, Box<dyn Error>> {
let (client, _) = Client::new(name, ClientOptions::NO_START_SERVER)?;
let transport = client.transport();
let ppq = timebase.ppq() as usize;
Ok(Self {
name: name.into(),
view: LauncherMode::Chains,
playing: transport.query_state()?,
transport,
timebase: timebase.clone(),
monitoring: true,
recording: false,
overdub: true,
cursor: (2, 2),
current_frame: 0,
scenes: scenes.unwrap_or_else(||vec![Scene::new(&"Scene 1", &[None])]),
tracks: if let Some(tracks) = tracks { tracks } else { vec![
Track::new("Track 1", &timebase, None, Some(vec![
Phrase::new("MIDI Clip 1", ppq * 4, Some(BTreeMap::from([
( ppq * 0, vec![MidiMessage::NoteOn { key: 36.into(), vel: 100.into() }] ),
( ppq * 1, vec![MidiMessage::NoteOn { key: 36.into(), vel: 100.into() }] ),
( ppq * 2, vec![MidiMessage::NoteOn { key: 36.into(), vel: 100.into() }] ),
( ppq * 3, vec![MidiMessage::NoteOn { key: 36.into(), vel: 100.into() }] ),
])))
]))?,
] },
show_help: true,
})
}
pub fn connect (&self, midi_in: &str, audio_outs: &[&str]) -> Usually<()> {
let (client, _status) = Client::new(
&format!("{}-init", &self.name), ClientOptions::NO_START_SERVER
)?;
let midi_ins = client.ports(Some(midi_in), None, PortFlags::IS_OUTPUT);
let audio_outs: Vec<Vec<String>> = audio_outs.iter()
.map(|pattern|client.ports(Some(pattern), None, PortFlags::IS_INPUT))
.collect();
for (i, sequencer) in self.tracks.iter().enumerate() {
for sequencer_midi_in in sequencer.midi_ins()?.iter() {
for midi_in in midi_ins.iter() {
client.connect_ports_by_name(&midi_in, &sequencer_midi_in)?;
}
}
let chain: &Chain = &self.tracks[i].chain;
for port in sequencer.midi_outs()?.iter() {
for midi_in in chain.midi_ins()?.iter() {
client.connect_ports_by_name(&port, &midi_in)?;
}
}
for (j, port) in chain.audio_outs()?.iter().enumerate() {
for audio_out in audio_outs[j % audio_outs.len()].iter() {
client.connect_ports_by_name(&port, &audio_out)?;
}
}
}
Ok(())
}
pub fn cols (&self) -> usize {
(self.tracks.len() + 2) as usize
}
pub fn col (&self) -> usize {
self.cursor.0 as usize
}
pub fn dec_col (&mut self) {
self.cursor.0 = if self.cursor.0 > 0 {
self.cursor.0 - 1
} else {
(self.cols() - 1) as usize
}
}
pub fn inc_col (&mut self) {
self.cursor.0 = if self.cursor.0 >= self.cols() - 1 {
0
} else {
self.cursor.0 + 1
}
}
pub fn rows (&self) -> usize {
(self.scenes.len() + 2) as usize
}
pub fn row (&self) -> usize {
self.cursor.1 as usize
}
pub fn dec_row (&mut self) {
self.cursor.1 = if self.cursor.1 > 0 {
self.cursor.1 - 1
} else {
self.rows() - 1
}
}
pub fn inc_row (&mut self) {
self.cursor.1 = if self.cursor.1 >= self.rows() - 1 {
0
} else {
self.cursor.1 + 1
}
}
pub fn track (&self) -> Option<(usize, &Track)> {
match self.col() { 0 => None, _ => {
let id = self.col() as usize - 1;
self.tracks.get(id).map(|t|(id, t))
} }
}
pub fn track_mut (&mut self) -> Option<(usize, &mut Track)> {
match self.col() { 0 => None, _ => {
let id = self.col() as usize - 1;
self.tracks.get_mut(id).map(|t|(id, t))
} }
}
pub fn scene (&self) -> Option<(usize, &Scene)> {
match self.row() { 0 => None, _ => {
let id = self.row() as usize - 1;
self.scenes.get(id).map(|t|(id, t))
} }
}
pub fn scene_mut (&mut self) -> Option<(usize, &mut Scene)> {
match self.row() { 0 => None, _ => {
let id = self.row() as usize - 1;
self.scenes.get_mut(id).map(|t|(id, t))
} }
}
pub fn sequencer (&self) -> Option<&Sequencer> {
Some(&self.track()?.1.sequencer)
}
pub fn sequencer_mut (&mut self) -> Option<&mut Sequencer> {
Some(&mut self.track_mut()?.1.sequencer)
}
pub fn chain (&self) -> Option<&Chain> {
Some(&self.track()?.1.chain)
}
pub fn phrase_id (&self) -> Option<usize> {
let (track_id, _) = self.track()?;
let (_, scene) = self.scene()?;
*scene.clips.get(track_id)?
}
}

29
src/model/looper.rs Normal file
View file

@ -0,0 +1,29 @@
use crate::core::*;
pub struct Looper {
name: String
}
impl Looper {
pub fn new (name: &str) -> Result<DynamicDevice<Self>, Box<dyn Error>> {
Ok(DynamicDevice::new(render, handle, process, Self {
name: name.into(),
}))
}
}
pub fn process (_: &mut Looper, _: &Client, _: &ProcessScope) -> Control {
Control::Continue
}
pub fn render (_: &Looper, _: &mut Buffer, _: Rect) -> Usually<Rect> {
Ok(Rect::default())
}
pub fn handle (_: &mut Looper, _: &AppEvent) -> Usually<bool> {
Ok(false)
}
pub const ACTIONS: [(&'static str, &'static str);1] = [
("Ins/Del", "Add/remove loop"),
];

132
src/model/mixer.rs Normal file
View file

@ -0,0 +1,132 @@
use crate::core::*;
pub struct Mixer {
pub name: String,
pub tracks: Vec<MixerTrack>,
pub selected_track: usize,
pub selected_column: usize,
}
impl Mixer {
pub fn new (name: &str) -> Result<DynamicDevice<Self>, Box<dyn Error>> {
let (client, _status) = Client::new(name, ClientOptions::NO_START_SERVER)?;
Ok(DynamicDevice::new(
crate::view::mixer::render,
crate::control::mixer::handle,
process,
Self {
name: name.into(),
selected_column: 0,
selected_track: 1,
tracks: vec![
MixerTrack::new(&client, 1, "Mono 1")?,
MixerTrack::new(&client, 1, "Mono 2")?,
MixerTrack::new(&client, 2, "Stereo 1")?,
MixerTrack::new(&client, 2, "Stereo 2")?,
MixerTrack::new(&client, 2, "Stereo 3")?,
MixerTrack::new(&client, 2, "Bus 1")?,
MixerTrack::new(&client, 2, "Bus 2")?,
MixerTrack::new(&client, 2, "Mix")?,
],
}
))
}
}
pub fn process (
_: &mut Mixer,
_: &Client,
_: &ProcessScope
) -> Control {
Control::Continue
}
pub struct MixerTrack {
pub name: String,
pub channels: u8,
pub input_ports: Vec<Port<AudioIn>>,
pub pre_gain_meter: f64,
pub gain: f64,
pub insert_ports: Vec<Port<AudioOut>>,
pub return_ports: Vec<Port<AudioIn>>,
pub post_gain_meter: f64,
pub post_insert_meter: f64,
pub level: f64,
pub pan: f64,
pub output_ports: Vec<Port<AudioOut>>,
pub post_fader_meter: f64,
pub route: String,
}
impl MixerTrack {
pub fn new (jack: &Client, channels: u8, name: &str) -> Result<Self, Box<dyn Error>> {
let mut input_ports = vec![];
let mut insert_ports = vec![];
let mut return_ports = vec![];
let mut output_ports = vec![];
for channel in 1..=channels {
input_ports.push(jack.register_port(&format!("{name} [input {channel}]"), AudioIn::default())?);
output_ports.push(jack.register_port(&format!("{name} [out {channel}]"), AudioOut::default())?);
let insert_port = jack.register_port(&format!("{name} [pre {channel}]"), AudioOut::default())?;
let return_port = jack.register_port(&format!("{name} [insert {channel}]"), AudioIn::default())?;
jack.connect_ports(&insert_port, &return_port)?;
insert_ports.push(insert_port);
return_ports.push(return_port);
}
Ok(Self {
name: name.into(),
channels,
input_ports,
pre_gain_meter: 0.0,
gain: 0.0,
post_gain_meter: 0.0,
insert_ports,
return_ports,
post_insert_meter: 0.0,
level: 0.0,
pan: 0.0,
post_fader_meter: 0.0,
route: "---".into(),
output_ports,
})
}
}
//impl<W: Write> Input<TUI<W>, bool> for Mixer {
//fn handle (&mut self, engine: &mut TUI<W>) -> Result<Option<bool>> {
//Ok(None)
//}
//
//impl<W: Write> Output<TUI<W>, [u16;2]> for Mixer {
//fn render (&self, envine: &mut TUI<W>) -> Result<Option<[u16;2]>> {
//let tracks_table = Columns::new()
//.add(titles)
//.add(input_meters)
//.add(gains)
//.add(gain_meters)
//.add(pres)
//.add(pre_meters)
//.add(levels)
//.add(pans)
//.add(pan_meters)
//.add(posts)
//.add(routes)
//Rows::new()
//.add(Columns::new()
//.add(Rows::new()
//.add("[Arrows]".bold())
//.add("Navigate"))
//.add(Rows::new()
//.add("[+/-]".bold())
//.add("Adjust"))
//.add(Rows::new()
//.add("[Ins/Del]".bold())
//.add("Add/remove track")))
//.add(tracks_table)
//.render(engine)
//}
//}

129
src/model/phrase.rs Normal file
View file

@ -0,0 +1,129 @@
use crate::core::*;
pub struct Phrase {
pub name: String,
pub length: usize,
pub notes: PhraseData,
}
impl Default for Phrase {
fn default () -> Self {
Self::new("", 0, None)
}
}
impl Phrase {
pub fn new (name: &str, length: usize, notes: Option<PhraseData>) -> Self {
Self { name: name.to_string(), length, notes: notes.unwrap_or(BTreeMap::new()) }
}
/// Check if a range `start..end` contains MIDI Note On `k`
pub fn contains_note_on (&self, k: u7, start: usize, end: usize) -> bool {
for (_, (_, events)) in self.notes.range(start..end).enumerate() {
for event in events.iter() {
match event {
MidiMessage::NoteOn {key,..} => {
if *key == k {
return true
}
}
_ => {}
}
}
}
return false
}
/// Write a chunk of MIDI events to an output port.
pub fn process_out (
&self,
output: &mut MIDIChunk,
notes_on: &mut Vec<bool>,
timebase: &Arc<Timebase>,
frame0: usize,
frames: usize,
) {
let start = frame0 as f64;
let end = start + frames as f64;
let repeat = timebase.pulses_frames(self.length as f64);
let ticks = timebase.frames_to_ticks(start, end, repeat);
//panic!("{start} {end} {repeat} {ticks:?}");
for (time, tick) in ticks.iter() {
let events = self.notes.get(&(*tick as usize));
if events.is_none() {
continue
}
for message in events.unwrap().iter() {
let mut buf = vec![];
let channel = 0.into();
let message = *message;
match message {
MidiMessage::NoteOn { key, .. } => notes_on[key.as_int() as usize] = true,
MidiMessage::NoteOff { key, .. } => notes_on[key.as_int() as usize] = false,
_ => {}
}
LiveEvent::Midi { channel, message }.write(&mut buf).unwrap();
let t = *time as usize;
if output[t].is_none() {
output[t] = Some(vec![]);
}
if let Some(Some(frame)) = output.get_mut(t) {
frame.push(buf);
}
}
}
}
/// Read a chunk of MIDI events from an input port.
pub fn process_in (
&mut self,
input: ::jack::MidiIter,
notes_on: &mut Vec<bool>,
mut monitor: Option<&mut MIDIChunk>,
record: bool,
timebase: &Arc<Timebase>,
frame0: usize,
) {
for RawMidi { time, bytes } in input {
let time = time as usize;
let pulse = timebase.frames_pulses((frame0 + time) as f64) as usize;
if let LiveEvent::Midi { message, .. } = LiveEvent::parse(bytes).unwrap() {
if let MidiMessage::NoteOn { key, vel: _ } = message {
notes_on[key.as_int() as usize] = true;
if let Some(ref mut monitor) = monitor {
if monitor[time].is_none() {
monitor[time] = Some(vec![]);
}
if let Some(Some(frame)) = monitor.get_mut(time) {
frame.push(bytes.into())
}
}
if record {
let contains = self.notes.contains_key(&pulse);
if contains {
self.notes.get_mut(&pulse).unwrap().push(message.clone());
} else {
self.notes.insert(pulse, vec![message.clone()]);
}
}
} else if let midly::MidiMessage::NoteOff { key, vel: _ } = message {
notes_on[key.as_int() as usize] = false;
if let Some(ref mut monitor) = monitor {
if monitor[time].is_none() {
monitor[time] = Some(vec![]);
}
if let Some(Some(frame)) = monitor.get_mut(time) {
frame.push(bytes.into())
}
}
if record {
let contains = self.notes.contains_key(&pulse);
if contains {
self.notes.get_mut(&pulse).unwrap().push(message.clone());
} else {
self.notes.insert(pulse, vec![message.clone()]);
}
}
}
}
}
}
}

176
src/model/plugin.rs Normal file
View file

@ -0,0 +1,176 @@
use crate::core::*;
pub mod lv2;
pub mod vst2;
pub mod vst3;
use self::lv2::*;
pub struct Plugin {
pub name: String,
pub path: Option<String>,
pub plugin: Option<PluginKind>,
pub selected: usize,
pub mapping: bool,
pub midi_ins: Vec<Port<MidiIn>>,
pub midi_outs: Vec<Port<MidiOut>>,
pub audio_ins: Vec<Port<AudioIn>>,
pub audio_outs: Vec<Port<AudioOut>>,
}
pub enum PluginKind {
LV2(LV2Plugin),
VST2 {
instance: ::vst::host::PluginInstance
},
VST3,
}
const HELM: &'static str = "file:///nix/store/ij3sz7nqg5l7v2dygdvzy3w6cj62bd6r-helm-0.9.0/lib/lv2/helm.lv2";
impl Plugin {
/// Load a LV2 plugin.
pub fn lv2 (name: &str, path: &str) -> Usually<DynamicDevice<Self>> {
let host = Self::new(name)?;
let plugin = LV2Plugin::new(path)?;
let mut state = host.state();
let client = host.client.as_ref().unwrap().as_client();
let (midi_ins, midi_outs, audio_ins, audio_outs) = (
plugin.plugin.port_counts().atom_sequence_inputs,
plugin.plugin.port_counts().atom_sequence_outputs,
plugin.plugin.port_counts().audio_inputs,
plugin.plugin.port_counts().audio_outputs,
);
state.midi_ins = {
let mut ports = vec![];
for i in 0..midi_ins {
ports.push(client.register_port(&format!("midi-in-{i}"), MidiIn::default())?)
}
ports
};
state.midi_outs = {
let mut ports = vec![];
for i in 0..midi_outs {
ports.push(client.register_port(&format!("midi-out-{i}"), MidiOut::default())?)
}
ports
};
state.audio_ins = {
let mut ports = vec![];
for i in 0..audio_ins {
ports.push(client.register_port(&format!("audio-in-{i}"), AudioIn::default())?)
}
ports
};
state.audio_outs = {
let mut ports = vec![];
for i in 0..audio_outs {
ports.push(client.register_port(&format!("audio-out-{i}"), AudioOut::default())?)
}
ports
};
state.plugin = Some(PluginKind::LV2(plugin));
state.path = Some(String::from(path));
std::mem::drop(state);
Ok(host)
}
pub fn new (name: &str) -> Usually<DynamicDevice<Self>> {
DynamicDevice::new(
crate::view::plugin::render,
crate::control::plugin::handle,
Self::process,
Self {
name: name.into(),
path: None,
plugin: None,
selected: 0,
mapping: false,
midi_ins: vec![],
midi_outs: vec![],
audio_ins: vec![],
audio_outs: vec![],
}
).activate(Client::new(name, ClientOptions::NO_START_SERVER)?.0)
}
pub fn process (&mut self, _: &Client, scope: &ProcessScope) -> Control {
match self.plugin.as_mut() {
Some(PluginKind::LV2(LV2Plugin { features, ref mut instance, .. })) => {
let urid = features.midi_urid();
let mut inputs = vec![];
for port in self.midi_ins.iter() {
let mut atom = ::livi::event::LV2AtomSequence::new(
&features,
scope.n_frames() as usize
);
for event in port.iter(scope) {
match event.bytes.len() {
3 => atom.push_midi_event::<3>(
event.time as i64,
urid,
&event.bytes[0..3]
).unwrap(),
_ => {}
}
}
inputs.push(atom);
}
let mut outputs = vec![];
for _ in self.midi_outs.iter() {
outputs.push(::livi::event::LV2AtomSequence::new(
&features,
scope.n_frames() as usize
));
}
let ports = ::livi::EmptyPortConnections::new()
.with_atom_sequence_inputs(
inputs.iter()
)
.with_atom_sequence_outputs(
outputs.iter_mut()
)
.with_audio_inputs(
self.audio_ins.iter().map(|o|o.as_slice(scope))
)
.with_audio_outputs(
self.audio_outs.iter_mut().map(|o|o.as_mut_slice(scope))
);
unsafe {
instance.run(scope.n_frames() as usize, ports).unwrap()
};
},
_ => {}
}
Control::Continue
}
}
impl PortList for Plugin {
fn audio_ins (&self) -> Usually<Vec<String>> {
let mut ports = vec![];
for port in self.audio_ins.iter() {
ports.push(port.name()?);
}
Ok(ports)
}
fn audio_outs (&self) -> Usually<Vec<String>> {
let mut ports = vec![];
for port in self.audio_outs.iter() {
ports.push(port.name()?);
}
Ok(ports)
}
fn midi_ins (&self) -> Usually<Vec<String>> {
let mut ports = vec![];
for port in self.midi_ins.iter() {
ports.push(port.name()?);
}
Ok(ports)
}
fn midi_outs (&self) -> Usually<Vec<String>> {
let mut ports = vec![];
for port in self.midi_outs.iter() {
ports.push(port.name()?);
}
Ok(ports)
}
}

42
src/model/plugin/lv2.rs Normal file
View file

@ -0,0 +1,42 @@
use crate::core::*;
pub struct LV2Plugin {
pub world: ::livi::World,
pub instance: ::livi::Instance,
pub plugin: ::livi::Plugin,
pub features: Arc<::livi::Features>,
pub port_list: Vec<::livi::Port>,
}
impl LV2Plugin {
pub fn new (uri: &str) -> Usually<Self> {
// Get 1st plugin at URI
let world = ::livi::World::with_load_bundle(&uri);
let features = ::livi::FeaturesBuilder { min_block_length: 1, max_block_length: 65536 };
let features = world.build_features(features);
let mut plugin = None;
for p in world.iter_plugins() {
plugin = Some(p);
break
}
let plugin = plugin.unwrap();
// Instantiate
Ok(Self {
world,
instance: unsafe {
plugin.instantiate(features.clone(), 48000.0).expect("boop")
},
port_list: {
let mut port_list = vec![];
for port in plugin.ports() {
port_list.push(port);
}
port_list
},
plugin,
features,
})
}
}

15
src/model/plugin/vst2.rs Normal file
View file

@ -0,0 +1,15 @@
use crate::core::*;
use super::*;
impl ::vst::host::Host for Plugin {}
fn set_vst_plugin (host: &Arc<Mutex<Plugin>>, _path: &str) -> Usually<PluginKind> {
let mut loader = ::vst::host::PluginLoader::load(
&std::path::Path::new("/nix/store/ij3sz7nqg5l7v2dygdvzy3w6cj62bd6r-helm-0.9.0/lib/lxvst/helm.so"),
host.clone()
)?;
Ok(PluginKind::VST2 {
instance: loader.instance()?
})
}

0
src/model/plugin/vst3.rs Normal file
View file

175
src/model/sampler.rs Normal file
View file

@ -0,0 +1,175 @@
use crate::core::*;
pub struct Voice {
pub sample: Arc<Sample>,
pub after: usize,
pub position: usize,
}
pub struct Sample {
pub name: String,
pub start: usize,
pub end: usize,
pub channels: Vec<Vec<f32>>,
}
pub struct Sampler {
pub name: String,
pub cursor: (usize, usize),
pub samples: BTreeMap<u7, Arc<Sample>>,
pub voices: Vec<Voice>,
pub midi_in: Port<MidiIn>,
pub audio_ins: Vec<Port<AudioIn>>,
pub audio_outs: Vec<Port<AudioOut>>,
}
impl Voice {
pub fn chunk (&mut self, mut frames: usize) -> Option<Vec<Vec<f32>>> {
// Create output buffer for each channel
let mut chunk = vec![vec![];self.sample.channels.len()];
// If it's not time to play yet, count down
if self.after >= frames {
self.after = self.after - frames;
return Some(chunk)
}
// If the voice will start playing within the current buffer,
// subtract the remaining number of wait frames.
if self.after > 0 && self.after < frames {
chunk = vec![vec![0.0;self.after];self.sample.channels.len()];
frames = frames - self.after;
self.after = 0;
}
if self.position < self.sample.end {
let start = self.position.min(self.sample.end);
let end = (self.position + frames).min(self.sample.end);
for (i, channel) in self.sample.channels.iter().enumerate() {
chunk[i].extend_from_slice(&channel[start..end]);
};
self.position = self.position + frames;
Some(chunk)
} else {
None
}
}
}
impl Sample {
pub fn new (name: &str, start: usize, end: usize, channels: Vec<Vec<f32>>) -> Arc<Self> {
Arc::new(Self { name: name.to_string(), start, end, channels })
}
pub fn play (self: &Arc<Self>, after: usize) -> Voice {
Voice { sample: self.clone(), after, position: self.start }
}
}
impl Sampler {
pub fn new (
name: &str,
samples: Option<BTreeMap<u7, Arc<Sample>>>,
) -> Result<DynamicDevice<Self>, Box<dyn Error>> {
let (client, _) = Client::new(name, ClientOptions::NO_START_SERVER)?;
DynamicDevice::new(
crate::view::sampler::render,
crate::control::sampler::handle,
Self::process,
Self {
name: name.into(),
cursor: (0, 0),
samples: samples.unwrap_or(BTreeMap::new()),
voices: vec![],
midi_in: client.register_port("midi", ::jack::MidiIn::default())?,
audio_ins: vec![
client.register_port("recL", ::jack::AudioIn::default())?,
client.register_port("recR", ::jack::AudioIn::default())?,
],
audio_outs: vec![
client.register_port("outL", ::jack::AudioOut::default())?,
client.register_port("outR", ::jack::AudioOut::default())?,
],
}).activate(client)
}
pub fn process (&mut self, _: &Client, scope: &ProcessScope) -> Control {
// Output buffer: this will be copied to the audio outs.
let channel_count = self.audio_outs.len();
let mut mixed = vec![vec![0.0;scope.n_frames() as usize];channel_count];
// Process MIDI input to add new voices.
for RawMidi { time, bytes } in self.midi_in.iter(scope) {
if let LiveEvent::Midi { message, .. } = LiveEvent::parse(bytes).unwrap() {
if let MidiMessage::NoteOn { ref key, .. } = message {
if let Some(sample) = self.samples.get(key) {
self.voices.push(sample.play(time as usize));
}
}
}
}
// Emit next chunk of each currently playing voice,
// dropping voices that have reached their ends.
let mut voices = vec![];
std::mem::swap(&mut voices, &mut self.voices);
loop {
if voices.len() < 1 {
break
}
let mut voice = voices.swap_remove(0);
if let Some(chunk) = voice.chunk(scope.n_frames() as usize) {
for (i, channel) in chunk.iter().enumerate() {
let buffer = &mut mixed[i % channel_count];
for (i, sample) in channel.iter().enumerate() {
buffer[i] += sample;
}
}
self.voices.push(voice);
}
}
// Write output buffer to output ports.
for (i, port) in self.audio_outs.iter_mut().enumerate() {
let buffer = &mixed[i];
for (i, value) in port.as_mut_slice(scope).iter_mut().enumerate() {
*value = *buffer.get(i).unwrap_or(&0.0);
}
}
Control::Continue
}
fn load_sample (&mut self, _path: &str) {}
}
impl PortList for Sampler {
fn midi_ins (&self) -> Usually<Vec<String>> {
Ok(vec![self.midi_in.name()?])
}
fn audio_ins (&self) -> Usually<Vec<String>> {
let mut ports = vec![];
for port in self.audio_ins.iter() {
ports.push(port.name()?);
}
Ok(ports)
}
fn audio_outs (&self) -> Usually<Vec<String>> {
let mut ports = vec![];
for port in self.audio_outs.iter() {
ports.push(port.name()?);
}
Ok(ports)
}
}
#[macro_export] macro_rules! sample {
($note:expr, $name:expr, $src:expr) => {
{
let mut channels: Vec<wavers::Samples<f32>> = vec![];
for channel in wavers::Wav::from_path($src)?.channels() {
channels.push(channel);
}
let mut end = 0;
let mut data: Vec<Vec<f32>> = vec![];
for samples in channels.iter() {
let channel = Vec::from(samples.as_ref());
end = end.max(channel.len());
data.push(channel);
}
(u7::from_int_lossy($note).into(), Sample::new($name, 0, end, data).into())
}
};
}

13
src/model/scene.rs Normal file
View file

@ -0,0 +1,13 @@
pub struct Scene {
pub name: String,
pub clips: Vec<Option<usize>>,
}
impl Scene {
pub fn new (name: impl AsRef<str>, clips: impl AsRef<[Option<usize>]>) -> Self {
Self {
name: name.as_ref().into(),
clips: clips.as_ref().iter().map(|x|x.clone()).collect()
}
}
}

141
src/model/sequencer.rs Normal file
View file

@ -0,0 +1,141 @@
use crate::core::*;
use crate::model::*;
#[derive(Debug, Clone)]
pub enum SequencerMode { Tiny, Compact, Horizontal, Vertical, }
pub struct Sequencer {
pub name: String,
/// JACK transport handle.
pub transport: ::jack::Transport,
/// JACK MIDI input port that will be created.
pub midi_in: Port<MidiIn>,
/// JACK MIDI output port that will be created.
pub midi_out: Port<MidiOut>,
/// Holds info about tempo
pub timebase: Arc<Timebase>,
/// Phrase selector
pub sequence: Option<usize>,
/// Map: tick -> MIDI events at tick
pub phrases: Vec<Phrase>,
/// Red keys on piano roll.
pub notes_on: Vec<bool>,
/// Play sequence to output.
pub playing: TransportState,
/// Play input through output.
pub monitoring: bool,
/// Write input to sequence.
pub recording: bool,
/// Don't delete when recording.
pub overdub: bool,
/// Display mode
pub view: SequencerMode,
/// Range of notes to display
pub note_start: usize,
/// Position of cursor within note range
pub note_cursor: usize,
/// PPM per display unit
pub time_zoom: usize,
/// Range of time steps to display
pub time_start: usize,
/// Position of cursor within time range
pub time_cursor: usize,
}
render!(Sequencer = crate::view::sequencer::render);
handle!(Sequencer = crate::control::sequencer::handle);
process!(Sequencer |self, _client, scope| {
if self.sequence.is_none() { return Control::Continue }
let phrase = self.phrases.get_mut(self.sequence.unwrap());
if phrase.is_none() { return Control::Continue }
let phrase = phrase.unwrap();
let frame = scope.last_frame_time() as usize;
let frames = scope.n_frames() as usize;
let mut output: Vec<Option<Vec<Vec<u8>>>> = vec![None;frames];
let transport = self.transport.query().unwrap();
if transport.state != self.playing {
all_notes_off(&mut output);
}
self.playing = transport.state;
// Play from phrase into output buffer
if self.playing == TransportState::Rolling {
phrase.process_out(
&mut output,
&mut self.notes_on,
&self.timebase,
frame,
frames
);
}
// Play from input to monitor, and record into phrase.
phrase.process_in(
self.midi_in.iter(scope),
&mut self.notes_on,
if self.monitoring { Some(&mut output) } else { None },
self.recording && self.playing == TransportState::Rolling,
&self.timebase,
frame,
);
write_output(&mut self.midi_out.writer(scope), &mut output, frames);
Control::Continue
});
impl Sequencer {
pub fn new (
name: &str,
timebase: &Arc<Timebase>,
phrases: Option<Vec<Phrase>>,
) -> Usually<Self> {
let (client, _status) = Client::new(name, ClientOptions::NO_START_SERVER)?;
let transport = client.transport();
Ok(Self {
name: name.into(),
timebase: timebase.clone(),
phrases: phrases.unwrap_or_else(||vec![Phrase::default()]),
sequence: Some(0),
transport,
midi_in: client.register_port("in", MidiIn::default())?,
monitoring: true,
recording: true,
midi_out: client.register_port("out", MidiOut::default())?,
playing: TransportState::Starting,
overdub: true,
view: SequencerMode::Horizontal,
notes_on: vec![false;128],
note_start: 12,
note_cursor: 0,
time_zoom: 24,
time_start: 0,
time_cursor: 0,
})
}
pub fn phrase <'a> (&'a self) -> Option<&'a Phrase> {
self.phrases.get(self.sequence?)
}
}
impl PortList for Sequencer {
fn midi_ins (&self) -> Usually<Vec<String>> { Ok(vec![self.midi_in.name()?]) }
fn midi_outs (&self) -> Usually<Vec<String>> { Ok(vec![self.midi_out.name()?]) }
}
/// Add "all notes off" to the start of a buffer.
pub fn all_notes_off (output: &mut MIDIChunk) {
output[0] = Some(vec![]);
if let Some(Some(frame)) = output.get_mut(0) {
let mut buf = vec![];
let msg = MidiMessage::Controller { controller: 123.into(), value: 0.into() };
let evt = LiveEvent::Midi { channel: 0.into(), message: msg };
evt.write(&mut buf).unwrap();
frame.push(buf);
}
}
/// Write to JACK port from output buffer (containing notes from sequence and/or monitor)
fn write_output (writer: &mut ::jack::MidiWriter, output: &mut MIDIChunk, frames: usize) {
for time in 0..frames {
if let Some(Some(frame)) = output.get_mut(time ) {
for event in frame.iter() {
writer.write(&::jack::RawMidi { time: time as u32, bytes: &event })
.expect(&format!("{event:?}"));
}
}
}
}

48
src/model/track.rs Normal file
View file

@ -0,0 +1,48 @@
use crate::core::*;
use crate::model::*;
pub struct Track {
pub name: String,
pub sequencer: Sequencer,
pub chain: Chain,
}
impl Track {
pub fn new (
name: &str,
tempo: &Arc<Timebase>,
devices: Option<Vec<Box<dyn Device>>>,
phrases: Option<Vec<Phrase>>,
) -> Usually<Self> {
let sequencer = Sequencer::new(&name, tempo, phrases)?;
let chain = Chain::new(&name, devices)?;
let (client, _status) = Client::new("init", ClientOptions::NO_START_SERVER)?;
{
if let (Some(output), Some(input)) = (
sequencer.midi_outs()?.get(0).clone(),
if let Some(item) = chain.items.get(0) {
if let Some(port) = item.midi_ins()?.get(0) {
Some(port.clone())
} else {
None
}
} else {
None
}
) {
client.connect_ports_by_name(&output, &input)?;
}
}
Ok(Self { name: name.to_string(), sequencer, chain })
}
}
impl PortList for Track {
fn midi_ins (&self) -> Usually<Vec<String>> {
self.sequencer.midi_ins()
}
fn audio_outs (&self) -> Usually<Vec<String>> {
self.chain.audio_outs()
}
}