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::*;
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 {

View file

@ -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)
}

View file

@ -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 (

View file

@ -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
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 xdg = microxdg::XdgApp::new("tek")?;
crate::config::create_dirs(&xdg)?;
//run(Sampler::new("Sampler#000")?)
run(Launcher::new_with_controller(
"Launcher#0",
".*nanoKEY.*",