struct Track; load Sampler in Launcher

This commit is contained in:
🪞👃🪞 2024-06-28 00:09:53 +03:00
parent 1038e24ceb
commit 685c49cfd9
6 changed files with 150 additions and 122 deletions

View file

@ -1,25 +1,27 @@
use crate::prelude::*; use crate::prelude::*;
mod transport;
mod chain; mod chain;
mod sequencer;
mod sampler;
mod mixer;
mod looper;
mod plugin;
mod launcher; mod launcher;
mod looper;
mod mixer;
mod plugin;
mod sampler;
mod sequencer;
mod track;
mod transport;
pub use self::transport::Transport;
pub use self::chain::Chain; pub use self::chain::Chain;
pub use self::sequencer::Sequencer;
pub use self::sampler::Sampler;
pub use self::mixer::Mixer;
pub use self::looper::Looper;
pub use self::plugin::Plugin;
pub use self::launcher::Launcher; pub use self::launcher::Launcher;
pub use self::looper::Looper;
pub use self::mixer::Mixer;
pub use self::plugin::Plugin;
pub use self::sampler::Sampler;
pub use self::sequencer::Sequencer;
pub use self::track::Track;
pub use self::transport::Transport;
use crossterm::event; use crossterm::event;
use ::jack::{AudioIn, AudioOut, MidiIn, MidiOut, Port, PortSpec, Client}; use ::jack::{Port, PortSpec, Client};
pub trait Device: Render + Handle + PortList + Send + Sync { pub trait Device: Render + Handle + PortList + Send + Sync {
fn boxed (self) -> Box<dyn Device> where Self: Sized + 'static { fn boxed (self) -> Box<dyn Device> where Self: Sized + 'static {

View file

@ -9,8 +9,7 @@ pub struct Launcher {
overdub: bool, overdub: bool,
position: usize, position: usize,
cursor: (usize, usize), cursor: (usize, usize),
pub tracks: Vec<DynamicDevice<Sequencer>>, pub tracks: Vec<Track>,
pub chains: Vec<DynamicDevice<Chain>>,
scenes: Vec<Scene>, scenes: Vec<Scene>,
show_help: bool, show_help: bool,
view: LauncherView, view: LauncherView,
@ -70,7 +69,7 @@ impl Launcher {
)?; )?;
} }
} }
let chain: &DynamicDevice<Chain> = &state.chains[i]; let chain: &DynamicDevice<Chain> = &state.tracks[i].chain;
for midi_out in sequencer.midi_outs()?.iter() { for midi_out in sequencer.midi_outs()?.iter() {
for midi_in in chain.midi_ins()?.iter() { for midi_in in chain.midi_ins()?.iter() {
client.connect_ports_by_name(&midi_out, &midi_in)?; client.connect_ports_by_name(&midi_out, &midi_in)?;
@ -78,7 +77,7 @@ impl Launcher {
} }
for (j, port) in chain.audio_outs()?.iter().enumerate() { for (j, port) in chain.audio_outs()?.iter().enumerate() {
for audio_out in audio_outs[j % audio_outs.len()].iter() { for audio_out in audio_outs[j % audio_outs.len()].iter() {
client.connect_ports_by_name(&port, &audio_out); client.connect_ports_by_name(&port, &audio_out)?;
} }
} }
} }
@ -114,43 +113,18 @@ impl Launcher {
Scene::new(&"Scene#08", &[None, None, None, None]), Scene::new(&"Scene#08", &[None, None, None, None]),
], ],
tracks: vec![ tracks: vec![
Sequencer::new("Drum", &timebase)?, Track::new("Samples", &timebase, vec![
Sequencer::new("Bass", &timebase)?, Sampler::new("Samples")?.boxed(),
Sequencer::new("Pads", &timebase)?,
Sequencer::new("Lead", &timebase)?,
],
chains: vec![
Chain::new("Chain#0000", vec![
Plugin::lv2(
"Plugin#000",
"file:///home/user/.lv2/ChowKick.lv2",
&[1, 1, 0, 2]
)?.boxed(),
])?, ])?,
Track::new("Kick", &timebase, vec![
Chain::new("Chain#0000", vec![ //Plugin::lv2("Kick/ChowKick", "file:///home/user/.lv2/ChowKick.lv2", &[1, 1, 0, 2])?.boxed(),
Plugin::lv2(
"Plugin#001",
"file:///home/user/.lv2/Helm.lv2",
&[1, 0, 0, 2]
)?.boxed(),
])?,
Chain::new("Chain#0000", vec![
Plugin::lv2(
"Plugin#002",
"file:///home/user/.lv2/Helm.lv2",
&[1, 0, 0, 2]
)?.boxed(),
])?,
Chain::new("Chain#0000", vec![
Plugin::lv2(
"Plugin#003",
"file:///home/user/.lv2/Odin2.lv2",
&[1, 0, 0, 2]
)?.boxed(),
])?, ])?,
//Track::new("Bass", &timebase, vec![
//Plugin::lv2("Bass/Helm", "file:///home/user/.lv2/Helm.lv2", &[1, 0, 0, 2])?.boxed(),
//])?,
//Track::new("Pads", &timebase, vec![
//Plugin::lv2("Pads/Odin2", "file:///home/user/.lv2/Odin2.lv2", &[1, 0, 0, 2])?.boxed(),
//])?,
], ],
timebase, timebase,
show_help: true show_help: true
@ -196,6 +170,20 @@ impl Launcher {
self.cursor.1 + 1 self.cursor.1 + 1
} }
} }
fn active_track (&self) -> Option<&Track> {
if self.cursor.0 >= 1 {
self.tracks.get(self.cursor.0 as usize - 1 as usize)
} else {
None
}
}
fn active_sequencer <'a> (&'a self) -> Option<std::sync::MutexGuard<Sequencer>> {
self.active_track().map(|t|t.sequencer.state())
}
fn active_chain <'a> (&'a self) -> Option<std::sync::MutexGuard<Chain>> {
self.active_track().map(|t|t.chain.state())
}
} }
impl PortList for Launcher {} impl PortList for Launcher {}
pub fn process (state: &mut Launcher, _: &Client, _: &ProcessScope) -> Control { pub fn process (state: &mut Launcher, _: &Client, _: &ProcessScope) -> Control {
@ -228,8 +216,15 @@ pub fn render (state: &Launcher, buf: &mut Buffer, area: Rect) -> Usually<Rect>
draw_crossings(state, buf, x + w - 2, y + 1); draw_crossings(state, buf, x + w - 2, y + 1);
draw_box(buf, Rect { x, y: y + 1, width, height: height - 1 }); draw_box(buf, Rect { x, y: y + 1, width, height: height - 1 });
let style = Some(Style::default().green().dim()); let style = Some(Style::default().green().dim());
let chain = &*state.chains[0].state(); let chain = state.active_chain();
let (_, plugins) = crate::device::chain::draw_as_row(chain, buf, chain_area, style)?; let plugins = if let Some(chain) = &chain {
let (_, plugins) = crate::device::chain::draw_as_row(
&*chain, buf, chain_area, style
)?;
plugins
} else {
vec![]
};
if state.view == LauncherView::Tracks { if state.view == LauncherView::Tracks {
draw_box_styled(buf, track_area, style); draw_box_styled(buf, track_area, style);
@ -242,10 +237,14 @@ pub fn render (state: &Launcher, buf: &mut Buffer, area: Rect) -> Usually<Rect>
if state.view == LauncherView::Chains { if state.view == LauncherView::Chains {
draw_box_styled(buf, Rect { height: 18, ..chain_area }, style); draw_box_styled(buf, Rect { height: 18, ..chain_area }, style);
} }
draw_highlight(state, buf, &Some(plugins[chain.focus]), match state.view { if let Some(chain) = &chain {
LauncherView::Chains => Style::default().green().not_dim(), if let Some(plugin) = plugins.get(chain.focus) {
_ => Style::default().green().dim() draw_highlight(state, buf, &Some(*plugin), match state.view {
}); LauncherView::Chains => Style::default().green().not_dim(),
_ => Style::default().green().dim()
});
}
}
if state.view == LauncherView::Sequencer { if state.view == LauncherView::Sequencer {
draw_box_styled(buf, seq_area, style); draw_box_styled(buf, seq_area, style);
@ -303,7 +302,7 @@ fn draw_tracks (
let mut w = 15; let mut w = 15;
let mut highlight = None; let mut highlight = None;
for (i, track) in state.tracks.iter().enumerate() { for (i, track) in state.tracks.iter().enumerate() {
let track = track.state(); let track = track.sequencer.state();
draw_crossings(state, buf, x + w - 2, y); draw_crossings(state, buf, x + w - 2, y);
let width = draw_track(state, buf, x + w, y, i as u16, &track); let width = draw_track(state, buf, x + w, y, i as u16, &track);
if i + 1 == state.col() { if i + 1 == state.col() {
@ -333,7 +332,7 @@ fn draw_track (
} else { } else {
Style::default() Style::default()
}; };
for (scene_index, scene) in state.scenes.iter().enumerate() { for (_, scene) in state.scenes.iter().enumerate() {
if let Some(Some(sequence_index)) = scene.clips.get(i as usize) { if let Some(Some(sequence_index)) = scene.clips.get(i as usize) {
if let Some(sequence) = track.sequences.get(*sequence_index) { if let Some(sequence) = track.sequences.get(*sequence_index) {
width = width.max(sequence.name.len() as u16 + 5); width = width.max(sequence.name.len() as u16 + 5);
@ -376,18 +375,18 @@ fn draw_crossings (state: &Launcher, buf: &mut Buffer, x: u16, y: u16) {
fn draw_sequencer ( fn draw_sequencer (
state: &Launcher, buf: &mut Buffer, x: u16, y: u16, width: u16, height: u16 state: &Launcher, buf: &mut Buffer, x: u16, y: u16, width: u16, height: u16
) -> Usually<()> { ) -> Usually<()> {
if let Some(sequencer) = state.tracks.get(state.col().saturating_sub(1)) { if let Some(track) = state.tracks.get(state.col().saturating_sub(1)) {
//crate::device::sequencer::horizontal::footer( //crate::device::sequencer::horizontal::footer(
//&sequencer.state(), buf, 0, y, width, 0 //&sequencer.state(), buf, 0, y, width, 0
//); //);
crate::device::sequencer::horizontal::keys( crate::device::sequencer::horizontal::keys(
&sequencer.state(), buf, Rect { x, y: y + 1, width, height } &track.sequencer.state(), buf, Rect { x, y: y + 1, width, height }
)?; )?;
crate::device::sequencer::horizontal::lanes( crate::device::sequencer::horizontal::lanes(
&sequencer.state(), buf, x, y + 1, width, &track.sequencer.state(), buf, x, y + 1, width,
); );
crate::device::sequencer::horizontal::cursor( crate::device::sequencer::horizontal::cursor(
&sequencer.state(), buf, x, y + 1, match state.view { &track.sequencer.state(), buf, x, y + 1, match state.view {
LauncherView::Sequencer => Style::default().green().not_dim(), LauncherView::Sequencer => Style::default().green().not_dim(),
_ => Style::default().green().dim(), _ => Style::default().green().dim(),
} }
@ -410,8 +409,8 @@ pub fn handle (state: &mut Launcher, event: &AppEvent) -> Usually<bool> {
}, },
LauncherView::Sequencer => { LauncherView::Sequencer => {
let i = state.col().saturating_sub(1); let i = state.col().saturating_sub(1);
if let Some(sequencer) = state.tracks.get_mut(i) { if let Some(track) = state.tracks.get_mut(i) {
crate::device::sequencer::handle(&mut *sequencer.state(), event)? crate::device::sequencer::handle(&mut *track.sequencer.state(), event)?
} else { } else {
true true
} }
@ -486,7 +485,7 @@ fn clip_next (state: &mut Launcher) -> Usually<bool> {
let scene = &mut state.scenes[scene_id]; let scene = &mut state.scenes[scene_id];
scene.clips[clip_id] = match scene.clips[clip_id] { scene.clips[clip_id] = match scene.clips[clip_id] {
None => Some(0), None => Some(0),
Some(i) => if i >= state.tracks[clip_id].state().sequences.len().saturating_sub(1) { Some(i) => if i >= state.tracks[clip_id].sequencer.state().sequences.len().saturating_sub(1) {
None None
} else { } else {
Some(i + 1) Some(i + 1)
@ -501,7 +500,7 @@ fn clip_prev (state: &mut Launcher) -> Usually<bool> {
let clip_id = state.cursor.0 - 1; let clip_id = state.cursor.0 - 1;
let scene = &mut state.scenes[scene_id]; let scene = &mut state.scenes[scene_id];
scene.clips[clip_id] = match scene.clips[clip_id] { scene.clips[clip_id] = match scene.clips[clip_id] {
None => Some(state.tracks[clip_id].state().sequences.len().saturating_sub(1)), None => Some(state.tracks[clip_id].sequencer.state().sequences.len().saturating_sub(1)),
Some(i) => if i == 0 { Some(i) => if i == 0 {
None None
} else { } else {
@ -530,22 +529,22 @@ fn play_start (_: &mut Launcher) -> Usually<bool> {
} }
fn record_toggle (s: &mut Launcher) -> Usually<bool> { fn record_toggle (s: &mut Launcher) -> Usually<bool> {
s.recording = !s.recording; s.recording = !s.recording;
for sequencer in s.tracks.iter() { for track in s.tracks.iter() {
sequencer.state().recording = s.recording; track.sequencer.state().recording = s.recording;
} }
Ok(true) Ok(true)
} }
fn overdub_toggle (s: &mut Launcher) -> Usually<bool> { fn overdub_toggle (s: &mut Launcher) -> Usually<bool> {
s.overdub = !s.overdub; s.overdub = !s.overdub;
for sequencer in s.tracks.iter() { for track in s.tracks.iter() {
sequencer.state().overdub = s.overdub; track.sequencer.state().overdub = s.overdub;
} }
Ok(true) Ok(true)
} }
fn monitor_toggle (s: &mut Launcher) -> Usually<bool> { fn monitor_toggle (s: &mut Launcher) -> Usually<bool> {
s.monitoring = !s.monitoring; s.monitoring = !s.monitoring;
for sequencer in s.tracks.iter() { for track in s.tracks.iter() {
sequencer.state().monitoring = s.monitoring; track.sequencer.state().monitoring = s.monitoring;
} }
Ok(true) Ok(true)
} }

View file

@ -36,7 +36,7 @@ impl Sampler {
]; ];
let samples = Arc::new(Mutex::new(samples)); let samples = Arc::new(Mutex::new(samples));
let input = client.register_port("trigger", ::jack::MidiIn::default())?; let input = client.register_port("trigger", ::jack::MidiIn::default())?;
Ok(DynamicDevice::new(render, handle, Self::process, Self { DynamicDevice::new(render, handle, Self::process, Self {
name: name.into(), name: name.into(),
input, input,
selected_sample: 0, selected_sample: 0,
@ -45,7 +45,7 @@ impl Sampler {
midi_ins: vec![], midi_ins: vec![],
audio_ins: vec![], audio_ins: vec![],
audio_outs: vec![], audio_outs: vec![],
})) }).activate(client)
} }
pub fn process (&mut self, _: &Client, scope: &ProcessScope) -> Control { pub fn process (&mut self, _: &Client, scope: &ProcessScope) -> Control {
@ -80,7 +80,7 @@ impl Sampler {
Control::Continue Control::Continue
} }
fn load_sample (&mut self, path: &str) {} fn load_sample (&mut self, _path: &str) {}
} }
impl PortList for Sampler { impl PortList for Sampler {
@ -128,29 +128,32 @@ impl Sample {
} }
pub fn render (state: &Sampler, buf: &mut Buffer, Rect { x, y, .. }: Rect) pub fn render (state: &Sampler, buf: &mut Buffer, Rect { x, y, width, .. }: Rect)
-> Usually<Rect> -> Usually<Rect>
{ {
let width = 40;
let style = Style::default().gray(); let style = Style::default().gray();
draw_box(buf, Rect { x, y: y, width: 40, height: 12 }); draw_box(buf, Rect { x, y: y, width: 40, height: 12 });
buf.set_string(x + 1, y + 1, &format!(" {}", state.name), style.white().bold()); let separator = format!("{}", "-".repeat((width - 2).into()));
buf.set_string(x + 0, y + 2, &format!("├--------------------------------------┤"), style.dim()); separator.blit(buf, x, y + 2, Some(style.dim()));
buf.set_string(x + 2, y + 3, &format!("C4 Sample#000"), style.bold()); format!(" {}", state.name).blit(buf, x+1, y+1, Some(style.white().bold()));
buf.set_string(x + 2, y + 4, &format!(" {:.03}s", for (i, (note, name, cut)) in [
100000.0/44100.0), style); ("C4", "Sample#000", format!("{:.03}s",
buf.set_string(x + 2, y + 5, &format!("C#4 Sample#001"), style.bold()); 100000.0/44100.0)),
buf.set_string(x + 2, y + 6, &format!(" {:.03}-{:.03}s", ("C#4", "Sample#001", format!("{:.03}-{:.03}s",
50000.0/44100.0, 100000.0/44100.0), style); 50000.0/44100.0, 100000.0/44100.0)),
buf.set_string(x + 2, y + 7, &format!("D4 Sample#002"), style.bold()); ("D4", "Sample#002", format!("{:.03}-{:.03}/{:.03}s",
buf.set_string(x + 2, y + 8, &format!(" {:.03}-{:.03}/{:.03}s", 0.0, 50000.0/44100.0, 100000.0/44100.0)),
0.0, 50000.0/44100.0, 100000.0/44100.0), style); ("D#4", "Sample#003", format!("{:.03}-[{:.03}-{:.03}]/{:.03}s ",
buf.set_string(x + 2, y + 9, &format!("D#4 Sample#003"), style.bold()); 10000.0/44100.0, 25000.0/44100.0, 50000.0/44100.0, 100000.0/44100.0)),
buf.set_string(x + 2, y + 10, &format!(" {:.03}-[{:.03}-{:.03}]/{:.03}s ", ].iter().enumerate() {
10000.0/44100.0, 25000.0/44100.0, 50000.0/44100.0, 100000.0/44100.0), style); let i = i as u16;
//buf.set_string(x + 1, y + 7 + 6, &format!(" Inputs... │ Outputs... "), style.dim()); format!("{note:3} {name}")
//render_table(state, stdout, offset)?; .blit(buf, x+2, y+3+i*2, Some(style.bold()));
//render_meters(state, stdout, offset)?; format!(" {cut}")
Ok(Rect { x, y, width: 40, height: 11 }) .blit(buf, x+2, y+4+i*2, Some(style));
}
Ok(Rect { x, y, width, height: 11 })
} }
//fn render_table ( //fn render_table (

View file

@ -73,29 +73,23 @@ enum SequencerView {
} }
impl Sequencer { impl Sequencer {
pub fn new (name: &str, timebase: &Arc<Timebase>) -> Usually<DynamicDevice<Self>> { pub fn new (
name: &str,
timebase: &Arc<Timebase>,
sequences: Option<Vec<Sequence>>,
) -> Usually<DynamicDevice<Self>> {
let (client, _status) = Client::new(name, ClientOptions::NO_START_SERVER)?; let (client, _status) = Client::new(name, ClientOptions::NO_START_SERVER)?;
let transport = client.transport(); let transport = client.transport();
let state = transport.query_state()?;
DynamicDevice::new(render, handle, Self::process, Self { DynamicDevice::new(render, handle, Self::process, Self {
name: name.into(), name: name.into(),
midi_in: client.register_port("in", MidiIn::default())?, midi_in: client.register_port("in", MidiIn::default())?,
midi_out: client.register_port("out", MidiOut::default())?, midi_out: client.register_port("out", MidiOut::default())?,
timebase: timebase.clone(), timebase: timebase.clone(),
steps: 64, steps: 64,
resolution: 4, resolution: 4,
sequence: 0, sequence: 0,
sequences: vec![ sequences: sequences.unwrap_or(vec![Sequence::new("")]),
Sequence::new(&"Phrase#01"),
Sequence::new(&"Phrase#02"),
Sequence::new(&"Phrase#03"),
Sequence::new(&"Phrase#04"),
Sequence::new(&"Phrase#05"),
Sequence::new(&"Phrase#06"),
Sequence::new(&"Phrase#07"),
Sequence::new(&"Phrase#08"),
],
notes_on: vec![false;128], notes_on: vec![false;128],
playing: TransportState::Starting, playing: TransportState::Starting,
@ -115,14 +109,18 @@ impl Sequencer {
pub fn process (&mut self, _: &Client, scope: &ProcessScope) -> Control { pub fn process (&mut self, _: &Client, scope: &ProcessScope) -> Control {
// Update time // Update time
let mut sequence = &mut self.sequences[self.sequence].notes; let sequence = self.sequences.get_mut(self.sequence);
if sequence.is_none() {
return Control::Continue
}
let sequence = &mut sequence.unwrap().notes;
let transport = self.transport.query().unwrap(); let transport = self.transport.query().unwrap();
self.playing = transport.state; self.playing = transport.state;
let pos = &transport.pos; let pos = &transport.pos;
let usecs = self.timebase.frame_to_usec(pos.frame() as usize); let usecs = self.timebase.frame_to_usec(pos.frame() as usize);
let steps = usecs / self.timebase.usec_per_step(self.resolution as usize); let steps = usecs / self.timebase.usec_per_step(self.resolution as usize);
let step = steps % self.steps; let step = steps % self.steps;
let tick = step * self.timebase.ppq() / self.resolution; let tick = step * self.timebase.ppq() / self.resolution;
// Prepare output buffer // Prepare output buffer
let frames = scope.n_frames() as usize; let frames = scope.n_frames() as usize;
@ -231,7 +229,7 @@ impl PortList for Sequencer {
} }
} }
fn render (s: &Sequencer, buf: &mut Buffer, mut area: Rect) -> Usually<Rect> { fn render (s: &Sequencer, buf: &mut Buffer, area: Rect) -> Usually<Rect> {
let Rect { x, y, width, .. } = area; let Rect { x, y, width, .. } = area;
let (time0, time1) = s.time_axis; let (time0, time1) = s.time_axis;
let (note0, note1) = s.note_axis; let (note0, note1) = s.note_axis;
@ -281,7 +279,7 @@ pub fn draw_header (s: &Sequencer, buf: &mut Buffer, area: Rect, beat: usize) ->
draw_rec(buf, x + 13, y + 1, s.recording); draw_rec(buf, x + 13, y + 1, s.recording);
draw_dub(buf, x + 20, y + 1, s.overdub); draw_dub(buf, x + 20, y + 1, s.overdub);
draw_mon(buf, x + 27, y + 1, s.monitoring); draw_mon(buf, x + 27, y + 1, s.monitoring);
let clips = draw_clips(s, buf, area)?; let _ = draw_clips(s, buf, area)?;
Ok(Rect { x, y, width: area.width, height: 3 }) Ok(Rect { x, y, width: area.width, height: 3 })
} }
pub fn draw_timer ( pub fn draw_timer (
@ -401,15 +399,13 @@ fn nop (_: &mut Sequencer) -> Usually<bool> {
Ok(false) Ok(false)
} }
fn note_add (s: &mut Sequencer) -> Usually<bool> { fn note_add (s: &mut Sequencer) -> Usually<bool> {
let pos = s.transport.query().unwrap().pos;
let usecs = s.timebase.frame_to_usec(pos.frame() as usize);
let step = (s.time_axis.0 + s.time_cursor) as u32; let step = (s.time_axis.0 + s.time_cursor) as u32;
let start = (step as usize * s.timebase.ppq() / s.resolution) as u32; let start = (step as usize * s.timebase.ppq() / s.resolution) as u32;
let end = ((step + 1) as usize * s.timebase.ppq() / s.resolution) as u32; let end = ((step + 1) as usize * s.timebase.ppq() / s.resolution) as u32;
let key = ::midly::num::u7::from_int_lossy((s.note_cursor + s.note_axis.0) as u8); let key = ::midly::num::u7::from_int_lossy((s.note_cursor + s.note_axis.0) as u8);
let note_on = ::midly::MidiMessage::NoteOn { key, vel: 100.into() }; let note_on = ::midly::MidiMessage::NoteOn { key, vel: 100.into() };
let note_off = ::midly::MidiMessage::NoteOff { key, vel: 100.into() }; let note_off = ::midly::MidiMessage::NoteOff { key, vel: 100.into() };
let mut sequence = &mut s.sequences[s.sequence].notes; let sequence = &mut s.sequences[s.sequence].notes;
if sequence.contains_key(&start) { if sequence.contains_key(&start) {
sequence.get_mut(&start).unwrap().push(note_on.clone()); sequence.get_mut(&start).unwrap().push(note_on.clone());
} else { } else {

27
src/device/track.rs Normal file
View file

@ -0,0 +1,27 @@
use crate::prelude::*;
pub struct Track {
pub name: String,
pub sequencer: DynamicDevice<Sequencer>,
pub chain: DynamicDevice<Chain>,
}
impl Track {
pub fn new (
name: &str,
tempo: &Arc<Timebase>,
devices: Vec<Box<dyn Device>>
) -> Usually<Self> {
Ok(Self {
name: name.to_string(),
sequencer: Sequencer::new(&name, tempo, None)?,
chain: Chain::new(&name, devices)?,
})
}
}
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()
}
}

View file

@ -19,6 +19,7 @@ fn main () -> Result<(), Box<dyn Error>> {
let _cli = cli::Cli::parse(); let _cli = cli::Cli::parse();
let xdg = microxdg::XdgApp::new("tek")?; let xdg = microxdg::XdgApp::new("tek")?;
crate::config::create_dirs(&xdg)?; crate::config::create_dirs(&xdg)?;
//run(Sampler::new("Sampler#000")?)
run(Launcher::new_with_controller( run(Launcher::new_with_controller(
"Launcher#0", "Launcher#0",
".*nanoKEY.*", ".*nanoKEY.*",