mirror of
https://codeberg.org/unspeaker/tek.git
synced 2025-12-07 12:16:42 +01:00
struct Track; load Sampler in Launcher
This commit is contained in:
parent
1038e24ceb
commit
685c49cfd9
6 changed files with 150 additions and 122 deletions
|
|
@ -1,25 +1,27 @@
|
|||
use crate::prelude::*;
|
||||
|
||||
mod transport;
|
||||
mod chain;
|
||||
mod sequencer;
|
||||
mod sampler;
|
||||
mod mixer;
|
||||
mod looper;
|
||||
mod plugin;
|
||||
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::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::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 ::jack::{AudioIn, AudioOut, MidiIn, MidiOut, Port, PortSpec, Client};
|
||||
use ::jack::{Port, PortSpec, Client};
|
||||
|
||||
pub trait Device: Render + Handle + PortList + Send + Sync {
|
||||
fn boxed (self) -> Box<dyn Device> where Self: Sized + 'static {
|
||||
|
|
|
|||
|
|
@ -9,8 +9,7 @@ pub struct Launcher {
|
|||
overdub: bool,
|
||||
position: usize,
|
||||
cursor: (usize, usize),
|
||||
pub tracks: Vec<DynamicDevice<Sequencer>>,
|
||||
pub chains: Vec<DynamicDevice<Chain>>,
|
||||
pub tracks: Vec<Track>,
|
||||
scenes: Vec<Scene>,
|
||||
show_help: bool,
|
||||
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_in in chain.midi_ins()?.iter() {
|
||||
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 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]),
|
||||
],
|
||||
tracks: vec![
|
||||
Sequencer::new("Drum", &timebase)?,
|
||||
Sequencer::new("Bass", &timebase)?,
|
||||
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("Samples", &timebase, vec![
|
||||
Sampler::new("Samples")?.boxed(),
|
||||
])?,
|
||||
|
||||
Chain::new("Chain#0000", vec![
|
||||
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("Kick", &timebase, vec![
|
||||
//Plugin::lv2("Kick/ChowKick", "file:///home/user/.lv2/ChowKick.lv2", &[1, 1, 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,
|
||||
show_help: true
|
||||
|
|
@ -196,6 +170,20 @@ impl Launcher {
|
|||
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 {}
|
||||
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_box(buf, Rect { x, y: y + 1, width, height: height - 1 });
|
||||
let style = Some(Style::default().green().dim());
|
||||
let chain = &*state.chains[0].state();
|
||||
let (_, plugins) = crate::device::chain::draw_as_row(chain, buf, chain_area, style)?;
|
||||
let chain = state.active_chain();
|
||||
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 {
|
||||
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 {
|
||||
draw_box_styled(buf, Rect { height: 18, ..chain_area }, style);
|
||||
}
|
||||
draw_highlight(state, buf, &Some(plugins[chain.focus]), match state.view {
|
||||
LauncherView::Chains => Style::default().green().not_dim(),
|
||||
_ => Style::default().green().dim()
|
||||
});
|
||||
if let Some(chain) = &chain {
|
||||
if let Some(plugin) = plugins.get(chain.focus) {
|
||||
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 {
|
||||
draw_box_styled(buf, seq_area, style);
|
||||
|
|
@ -303,7 +302,7 @@ fn draw_tracks (
|
|||
let mut w = 15;
|
||||
let mut highlight = None;
|
||||
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);
|
||||
let width = draw_track(state, buf, x + w, y, i as u16, &track);
|
||||
if i + 1 == state.col() {
|
||||
|
|
@ -333,7 +332,7 @@ fn draw_track (
|
|||
} else {
|
||||
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(sequence) = track.sequences.get(*sequence_index) {
|
||||
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 (
|
||||
state: &Launcher, buf: &mut Buffer, x: u16, y: u16, width: u16, height: u16
|
||||
) -> 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(
|
||||
//&sequencer.state(), buf, 0, y, width, 0
|
||||
//);
|
||||
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(
|
||||
&sequencer.state(), buf, x, y + 1, width,
|
||||
&track.sequencer.state(), buf, x, y + 1, width,
|
||||
);
|
||||
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(),
|
||||
_ => Style::default().green().dim(),
|
||||
}
|
||||
|
|
@ -410,8 +409,8 @@ pub fn handle (state: &mut Launcher, event: &AppEvent) -> Usually<bool> {
|
|||
},
|
||||
LauncherView::Sequencer => {
|
||||
let i = state.col().saturating_sub(1);
|
||||
if let Some(sequencer) = state.tracks.get_mut(i) {
|
||||
crate::device::sequencer::handle(&mut *sequencer.state(), event)?
|
||||
if let Some(track) = state.tracks.get_mut(i) {
|
||||
crate::device::sequencer::handle(&mut *track.sequencer.state(), event)?
|
||||
} else {
|
||||
true
|
||||
}
|
||||
|
|
@ -486,7 +485,7 @@ fn clip_next (state: &mut Launcher) -> Usually<bool> {
|
|||
let scene = &mut state.scenes[scene_id];
|
||||
scene.clips[clip_id] = match scene.clips[clip_id] {
|
||||
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
|
||||
} else {
|
||||
Some(i + 1)
|
||||
|
|
@ -501,7 +500,7 @@ fn clip_prev (state: &mut Launcher) -> Usually<bool> {
|
|||
let clip_id = state.cursor.0 - 1;
|
||||
let scene = &mut state.scenes[scene_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 {
|
||||
None
|
||||
} else {
|
||||
|
|
@ -530,22 +529,22 @@ fn play_start (_: &mut Launcher) -> Usually<bool> {
|
|||
}
|
||||
fn record_toggle (s: &mut Launcher) -> Usually<bool> {
|
||||
s.recording = !s.recording;
|
||||
for sequencer in s.tracks.iter() {
|
||||
sequencer.state().recording = s.recording;
|
||||
for track in s.tracks.iter() {
|
||||
track.sequencer.state().recording = s.recording;
|
||||
}
|
||||
Ok(true)
|
||||
}
|
||||
fn overdub_toggle (s: &mut Launcher) -> Usually<bool> {
|
||||
s.overdub = !s.overdub;
|
||||
for sequencer in s.tracks.iter() {
|
||||
sequencer.state().overdub = s.overdub;
|
||||
for track in s.tracks.iter() {
|
||||
track.sequencer.state().overdub = s.overdub;
|
||||
}
|
||||
Ok(true)
|
||||
}
|
||||
fn monitor_toggle (s: &mut Launcher) -> Usually<bool> {
|
||||
s.monitoring = !s.monitoring;
|
||||
for sequencer in s.tracks.iter() {
|
||||
sequencer.state().monitoring = s.monitoring;
|
||||
for track in s.tracks.iter() {
|
||||
track.sequencer.state().monitoring = s.monitoring;
|
||||
}
|
||||
Ok(true)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -36,7 +36,7 @@ impl Sampler {
|
|||
];
|
||||
let samples = Arc::new(Mutex::new(samples));
|
||||
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(),
|
||||
input,
|
||||
selected_sample: 0,
|
||||
|
|
@ -45,7 +45,7 @@ impl Sampler {
|
|||
midi_ins: vec![],
|
||||
audio_ins: vec![],
|
||||
audio_outs: vec![],
|
||||
}))
|
||||
}).activate(client)
|
||||
}
|
||||
|
||||
pub fn process (&mut self, _: &Client, scope: &ProcessScope) -> Control {
|
||||
|
|
@ -80,7 +80,7 @@ impl Sampler {
|
|||
Control::Continue
|
||||
}
|
||||
|
||||
fn load_sample (&mut self, path: &str) {}
|
||||
fn load_sample (&mut self, _path: &str) {}
|
||||
}
|
||||
|
||||
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>
|
||||
{
|
||||
let width = 40;
|
||||
let style = Style::default().gray();
|
||||
draw_box(buf, Rect { x, y: y, width: 40, height: 12 });
|
||||
buf.set_string(x + 1, y + 1, &format!(" {} │", state.name), style.white().bold());
|
||||
buf.set_string(x + 0, y + 2, &format!("├--------------------------------------┤"), style.dim());
|
||||
buf.set_string(x + 2, y + 3, &format!("C4 Sample#000"), style.bold());
|
||||
buf.set_string(x + 2, y + 4, &format!(" {:.03}s",
|
||||
100000.0/44100.0), style);
|
||||
buf.set_string(x + 2, y + 5, &format!("C#4 Sample#001"), style.bold());
|
||||
buf.set_string(x + 2, y + 6, &format!(" {:.03}-{:.03}s",
|
||||
50000.0/44100.0, 100000.0/44100.0), style);
|
||||
buf.set_string(x + 2, y + 7, &format!("D4 Sample#002"), style.bold());
|
||||
buf.set_string(x + 2, y + 8, &format!(" {:.03}-{:.03}/{:.03}s",
|
||||
0.0, 50000.0/44100.0, 100000.0/44100.0), style);
|
||||
buf.set_string(x + 2, y + 9, &format!("D#4 Sample#003"), style.bold());
|
||||
buf.set_string(x + 2, y + 10, &format!(" {:.03}-[{:.03}-{:.03}]/{:.03}s ",
|
||||
10000.0/44100.0, 25000.0/44100.0, 50000.0/44100.0, 100000.0/44100.0), style);
|
||||
//buf.set_string(x + 1, y + 7 + 6, &format!(" Inputs... │ Outputs... "), style.dim());
|
||||
//render_table(state, stdout, offset)?;
|
||||
//render_meters(state, stdout, offset)?;
|
||||
Ok(Rect { x, y, width: 40, height: 11 })
|
||||
let separator = format!("├{}┤", "-".repeat((width - 2).into()));
|
||||
separator.blit(buf, x, y + 2, Some(style.dim()));
|
||||
format!(" {} │", state.name).blit(buf, x+1, y+1, Some(style.white().bold()));
|
||||
for (i, (note, name, cut)) in [
|
||||
("C4", "Sample#000", format!("{:.03}s",
|
||||
100000.0/44100.0)),
|
||||
("C#4", "Sample#001", format!("{:.03}-{:.03}s",
|
||||
50000.0/44100.0, 100000.0/44100.0)),
|
||||
("D4", "Sample#002", format!("{:.03}-{:.03}/{:.03}s",
|
||||
0.0, 50000.0/44100.0, 100000.0/44100.0)),
|
||||
("D#4", "Sample#003", format!("{:.03}-[{:.03}-{:.03}]/{:.03}s ",
|
||||
10000.0/44100.0, 25000.0/44100.0, 50000.0/44100.0, 100000.0/44100.0)),
|
||||
].iter().enumerate() {
|
||||
let i = i as u16;
|
||||
format!("{note:3} {name}")
|
||||
.blit(buf, x+2, y+3+i*2, Some(style.bold()));
|
||||
format!(" {cut}")
|
||||
.blit(buf, x+2, y+4+i*2, Some(style));
|
||||
}
|
||||
Ok(Rect { x, y, width, height: 11 })
|
||||
}
|
||||
|
||||
//fn render_table (
|
||||
|
|
|
|||
|
|
@ -73,29 +73,23 @@ enum SequencerView {
|
|||
}
|
||||
|
||||
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 transport = client.transport();
|
||||
let state = transport.query_state()?;
|
||||
DynamicDevice::new(render, handle, Self::process, Self {
|
||||
name: name.into(),
|
||||
midi_in: client.register_port("in", MidiIn::default())?,
|
||||
midi_out: client.register_port("out", MidiOut::default())?,
|
||||
midi_in: client.register_port("in", MidiIn::default())?,
|
||||
midi_out: client.register_port("out", MidiOut::default())?,
|
||||
|
||||
timebase: timebase.clone(),
|
||||
steps: 64,
|
||||
resolution: 4,
|
||||
sequence: 0,
|
||||
sequences: vec![
|
||||
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"),
|
||||
],
|
||||
sequences: sequences.unwrap_or(vec![Sequence::new("")]),
|
||||
notes_on: vec![false;128],
|
||||
|
||||
playing: TransportState::Starting,
|
||||
|
|
@ -115,14 +109,18 @@ impl Sequencer {
|
|||
pub fn process (&mut self, _: &Client, scope: &ProcessScope) -> Control {
|
||||
|
||||
// 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();
|
||||
self.playing = transport.state;
|
||||
let pos = &transport.pos;
|
||||
let usecs = self.timebase.frame_to_usec(pos.frame() as usize);
|
||||
let steps = usecs / self.timebase.usec_per_step(self.resolution as usize);
|
||||
let step = steps % self.steps;
|
||||
let tick = step * self.timebase.ppq() / self.resolution;
|
||||
self.playing = transport.state;
|
||||
let pos = &transport.pos;
|
||||
let usecs = self.timebase.frame_to_usec(pos.frame() as usize);
|
||||
let steps = usecs / self.timebase.usec_per_step(self.resolution as usize);
|
||||
let step = steps % self.steps;
|
||||
let tick = step * self.timebase.ppq() / self.resolution;
|
||||
|
||||
// Prepare output buffer
|
||||
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 (time0, time1) = s.time_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_dub(buf, x + 20, y + 1, s.overdub);
|
||||
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 })
|
||||
}
|
||||
pub fn draw_timer (
|
||||
|
|
@ -401,15 +399,13 @@ fn nop (_: &mut Sequencer) -> Usually<bool> {
|
|||
Ok(false)
|
||||
}
|
||||
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 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 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_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) {
|
||||
sequence.get_mut(&start).unwrap().push(note_on.clone());
|
||||
} else {
|
||||
|
|
|
|||
27
src/device/track.rs
Normal file
27
src/device/track.rs
Normal 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()
|
||||
}
|
||||
}
|
||||
|
|
@ -19,6 +19,7 @@ fn main () -> Result<(), Box<dyn Error>> {
|
|||
let _cli = cli::Cli::parse();
|
||||
let xdg = microxdg::XdgApp::new("tek")?;
|
||||
crate::config::create_dirs(&xdg)?;
|
||||
//run(Sampler::new("Sampler#000")?)
|
||||
run(Launcher::new_with_controller(
|
||||
"Launcher#0",
|
||||
".*nanoKEY.*",
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue