more sensible port creation

This commit is contained in:
🪞👃🪞 2025-01-22 00:10:31 +01:00
parent 4028b3bb29
commit bbe49ad463
7 changed files with 133 additions and 125 deletions

View file

@ -62,20 +62,28 @@ impl Default for MidiPlayer {
}
impl MidiPlayer {
pub fn new (
jack: &Jack,
name: impl AsRef<str>,
clip: Option<&Arc<RwLock<MidiClip>>>,
name: impl AsRef<str>,
jack: &Jack,
clock: Option<&Clock>,
clip: Option<&Arc<RwLock<MidiClip>>>,
midi_from: &[PortConnect],
midi_to: &[PortConnect],
) -> Usually<Self> {
let name = name.as_ref();
let clock = Clock::from(jack);
let clock = clock.cloned().unwrap_or_default();
Ok(Self {
midi_ins: vec![JackMidiIn::new(jack, format!("M/{name}"), midi_from)?,],
midi_outs: vec![JackMidiOut::new(jack, format!("{name}/M"), midi_to)?, ],
play_clip: Some((Moment::zero(&clock.timebase), clip.cloned())),
midi_ins: vec![JackMidiIn::new(jack, format!("M/{name}"), midi_from)?,],
midi_outs: vec![JackMidiOut::new(jack, format!("{name}/M"), midi_to)?, ],
play_clip: clip.map(|clip|(Moment::zero(&clock.timebase), Some(clip.clone()))),
clock,
..Default::default()
note_buf: vec![0;8],
reset: true,
recording: false,
monitoring: false,
overdub: false,
next_clip: None,
notes_in: RwLock::new([false;128]).into(),
notes_out: RwLock::new([false;128]).into(),
})
}
}
@ -88,26 +96,6 @@ impl std::fmt::Debug for MidiPlayer {
.finish()
}
}
from!(|clock: &Clock| MidiPlayer = Self {
clock: clock.clone(),
midi_ins: vec![],
midi_outs: vec![],
note_buf: vec![0;8],
reset: true,
recording: false,
monitoring: false,
overdub: false,
play_clip: None,
next_clip: None,
notes_in: RwLock::new([false;128]).into(),
notes_out: RwLock::new([false;128]).into(),
});
from!(|state: (&Clock, &Arc<RwLock<MidiClip>>)|MidiPlayer = {
let (clock, clip) = state;
let mut model = Self::from(clock);
model.play_clip = Some((Moment::zero(&clock.timebase), Some(clip.clone())));
model
});
has_clock!(|self: MidiPlayer|self.clock);
impl HasMidiIns for MidiPlayer {
fn midi_ins (&self) -> &Vec<JackMidiIn> { &self.midi_ins }

View file

@ -1,7 +1,6 @@
use crate::*;
impl HasJack for Tek { fn jack (&self) -> &Jack { &self.jack } }
audio!(
|self: Tek, client, scope|{
// Start profiling cycle
let t0 = self.perf.get_t0();
@ -14,20 +13,20 @@ audio!(
.collect::<Vec<_>>())
.collect::<Vec<_>>();
// Update standalone MIDI sequencer
if let Some(player) = self.player.as_mut() {
if Control::Quit == PlayerAudio(
player,
&mut self.note_buf,
&mut self.midi_buf,
).process(client, scope) {
return Control::Quit
}
}
//if let Some(player) = self.player.as_mut() {
//if Control::Quit == PlayerAudio(
//player,
//&mut self.note_buf,
//&mut self.midi_buf,
//).process(client, scope) {
//return Control::Quit
//}
//}
// Update standalone sampler
if let Some(sampler) = self.sampler.as_mut() {
if Control::Quit == SamplerAudio(sampler).process(client, scope) {
return Control::Quit
}
//if let Some(sampler) = self.sampler.as_mut() {
//if Control::Quit == SamplerAudio(sampler).process(client, scope) {
//return Control::Quit
//}
//for port in midi_in.iter() {
//for message in port.iter() {
//match message {
@ -35,30 +34,30 @@ audio!(
//}
//}
//}
}
//}
// TODO move these to editor and sampler?:
for port in midi_in.iter() {
for event in port.iter() {
match event {
(time, Ok(LiveEvent::Midi {message, ..})) => match message {
MidiMessage::NoteOn {ref key, ..} if let Some(editor) = self.editor.as_ref() => {
editor.set_note_pos(key.as_int() as usize);
},
MidiMessage::Controller {controller, value} if let (Some(editor), Some(sampler)) = (
self.editor.as_ref(),
self.sampler.as_ref(),
) => {
// TODO: give sampler its own cursor
if let Some(sample) = &sampler.mapped[editor.note_pos()] {
sample.write().unwrap().handle_cc(*controller, *value)
}
}
_ =>{}
},
_ =>{}
}
}
}
//for port in midi_in.iter() {
//for event in port.iter() {
//match event {
//(time, Ok(LiveEvent::Midi {message, ..})) => match message {
//MidiMessage::NoteOn {ref key, ..} if let Some(editor) = self.editor.as_ref() => {
//editor.set_note_pos(key.as_int() as usize);
//},
//MidiMessage::Controller {controller, value} if let (Some(editor), Some(sampler)) = (
//self.editor.as_ref(),
//self.sampler.as_ref(),
//) => {
//// TODO: give sampler its own cursor
//if let Some(sample) = &sampler.mapped[editor.note_pos()] {
//sample.write().unwrap().handle_cc(*controller, *value)
//}
//}
//_ =>{}
//},
//_ =>{}
//}
//}
//}
// Update track sequencers
for track in self.tracks.iter_mut() {
if PlayerAudio(
@ -71,20 +70,19 @@ audio!(
self.perf.update(t0, scope);
Control::Continue
};
|self, event|{
use JackEvent::*;
match event {
SampleRate(sr) =>
{ self.clock.timebase.sr.set(sr as f64); },
PortRegistration(id, true) =>
{},
{ println!("\rport add: {id}") },
PortRegistration(id, false) =>
{},
{ println!("\rport del: {id}") },
PortsConnected(a, b, true) =>
{},
{ println!("\rport conn: {a} {b}") },
PortsConnected(a, b, false) =>
{},
{ println!("\rport disc: {a} {b}") },
ClientRegistration(id, true) =>
{},
ClientRegistration(id, false) =>
@ -93,6 +91,8 @@ audio!(
{},
XRun =>
{},
GraphReorder =>
{},
_ => { panic!("{event:?}"); }
}
}

View file

@ -103,9 +103,9 @@ impl Tek {
view: SourceIter(include_str!("./view_transport.edn")),
jack: jack.clone(),
color: ItemPalette::random(),
clock: Clock::new(jack, bpm),
midi_ins: vec![JackMidiIn::new(jack, "GlobalI", midi_froms)?],
midi_outs: vec![JackMidiOut::new(jack, "GlobalO", midi_tos)?],
clock: Clock::new(jack, bpm)?,
midi_ins: vec![],
midi_outs: vec![],
keys: SourceIter(KEYS_APP),
keys_clip: SourceIter(KEYS_CLIP),
keys_track: SourceIter(KEYS_TRACK),
@ -129,14 +129,16 @@ impl Tek {
) -> Usually<Self> {
let clip = MidiClip::new("Clip", true, 384usize, None, Some(ItemColor::random().into()));
let clip = Arc::new(RwLock::new(clip));
let this = Self::new_clock(jack, bpm, sync_lead, sync_follow, midi_froms, midi_tos)?;
Ok(Self {
view: SourceIter(include_str!("./view_sequencer.edn")),
pool: Some((&clip).into()),
editor: Some((&clip).into()),
editing: false.into(),
midi_buf: vec![vec![];65536],
player: Some(MidiPlayer::new(&jack, "sequencer", Some(&clip), &midi_froms, &midi_tos)?),
..Self::new_clock(jack, bpm, sync_lead, sync_follow, midi_froms, midi_tos)?
tracks: vec![Track::default()],
//player: Some(MidiPlayer::new("sequencer", &jack, Some(&this.clock), Some(&clip), &midi_froms, &midi_tos)?),
..this
})
}
pub fn new_groovebox (
@ -147,12 +149,15 @@ impl Tek {
) -> Usually<Self> {
let app = Self {
view: SourceIter(include_str!("./view_groovebox.edn")),
sampler: Some(Sampler::new(jack, &"sampler", midi_froms, audio_froms, audio_tos)?),
tracks: vec![Track {
devices: vec![Sampler::new(jack, &"sampler", midi_froms, audio_froms, audio_tos)?.boxed()],
..Track::default()
}],
..Self::new_sequencer(jack, bpm, sync_lead, sync_follow, midi_froms, midi_tos)?
};
if let Some(sampler) = app.sampler.as_ref().unwrap().midi_in.as_ref() {
app.player.as_ref().unwrap().midi_outs[0].connect_to(sampler.port())?;
}
//if let Some(sampler) = app.sampler.as_ref().unwrap().midi_in.as_ref() {
//app.player.as_ref().unwrap().midi_outs[0].connect_to(sampler.port())?;
//}
Ok(app)
}
pub fn new_arranger (
@ -164,10 +169,11 @@ impl Tek {
) -> Usually<Self> {
let mut arranger = Self {
view: SourceIter(include_str!("./view_arranger.edn")),
..Self::new_groovebox(
jack, bpm, sync_lead, sync_follow,
midi_froms, midi_tos, audio_froms, audio_tos,
)?
pool: Some(Default::default()),
editor: Some(Default::default()),
editing: false.into(),
midi_buf: vec![vec![];65536],
..Self::new_clock(jack, bpm, sync_lead, sync_follow, midi_froms, midi_tos)?
};
arranger.scenes_add(scenes);
arranger.tracks_add(tracks, track_width, midi_froms, midi_tos);

View file

@ -69,8 +69,8 @@ atom_command!(TekCommand: |app: Tek| {
MidiEditCommand::try_from_expr(app.editor.as_ref().expect("no editor"), a).expect("invalid command")))
("pool" [,..a] Self::Pool(
PoolCommand::try_from_expr(app.pool.as_ref().expect("no pool"), a).expect("invalid command")))
("sampler" [,..a] Self::Sampler(
SamplerCommand::try_from_expr(app.sampler.as_ref().expect("no sampler"), a).expect("invalid command")))
//("sampler" [,..a] Self::Sampler(
//SamplerCommand::try_from_expr(app.sampler().as_ref().expect("no sampler"), a).expect("invalid command")))
("scene" [,..a] Self::Scene(
SceneCommand::try_from_expr(app, a).expect("invalid command")))
("track" [,..a] Self::Track(
@ -133,10 +133,10 @@ command!(|self: TekCommand, app: Tek|match self {
Self::Clip(cmd) => cmd.delegate(app, Self::Clip)?,
Self::Editor(cmd) => app.editor.as_mut()
.map(|editor|cmd.delegate(editor, Self::Editor)).transpose()?.flatten(),
Self::Sampler(cmd) => app.sampler.as_mut()
.map(|sampler|cmd.delegate(sampler, Self::Sampler)).transpose()?.flatten(),
Self::Enqueue(clip) => app.player.as_mut()
.map(|player|{player.enqueue_next(clip.as_ref());None}).flatten(),
//Self::Sampler(cmd) => app.sampler.as_mut()
//.map(|sampler|cmd.delegate(sampler, Self::Sampler)).transpose()?.flatten(),
//Self::Enqueue(clip) => app.player.as_mut()
//.map(|player|{player.enqueue_next(clip.as_ref());None}).flatten(),
Self::Color(palette) => {
use Selection::*;
Some(Self::Color(match app.selected {
@ -190,6 +190,7 @@ command!(|self: TekCommand, app: Tek|match self {
} else {
None
},
_ => todo!()
});
#[derive(Clone, Debug)] pub enum TrackCommand {
Add,

View file

@ -8,8 +8,6 @@ use crate::*;
pub color: ItemPalette,
pub pool: Option<MidiPool>,
pub editor: Option<MidiEditor>,
pub player: Option<MidiPlayer>,
pub sampler: Option<Sampler>,
pub midi_buf: Vec<Vec<Vec<u8>>>,
pub midi_ins: Vec<JackMidiIn>,
pub midi_outs: Vec<JackMidiOut>,
@ -39,9 +37,9 @@ use crate::*;
has_size!(<TuiOut>|self: Tek|&self.size);
has_clock!(|self: Tek|self.clock);
has_clips!(|self: Tek|self.pool.as_ref().expect("no clip pool").clips);
has_sampler!(|self: Tek|{
sampler = self.sampler;
index = self.editor.as_ref().map(|e|e.note_pos()).unwrap_or(0); });
//has_sampler!(|self: Tek|{
//sampler = self.sampler;
//index = self.editor.as_ref().map(|e|e.note_pos()).unwrap_or(0); });
has_editor!(|self: Tek|{
editor = self.editor;
editor_w = {
@ -108,24 +106,33 @@ impl Tek {
}
pub fn track_add (
&mut self, name: Option<&str>, color: Option<ItemPalette>,
midi_from: &[PortConnect], midi_to: &[PortConnect],
midi_froms: &[PortConnect],
midi_tos: &[PortConnect],
) -> Usually<(usize, &mut Track)> {
let name = name.map_or_else(||self.track_next_name(), |x|x.to_string().into());
let mut track = Track {
width: (name.len() + 2).max(9),
color: color.unwrap_or_else(ItemPalette::random),
player: MidiPlayer::from(self.clock()),
player: MidiPlayer::new(
&format!("{name}"),
self.jack(),
Some(self.clock()),
None,
midi_froms,
midi_tos
)?,
name,
..Default::default()
};
let midi_in = JackMidiIn::new(&self.jack, &format!("{}I", &track.name), midi_from)?;
midi_in.connect_to_matching()?;
track.player.midi_ins.push(midi_in);
//let midi_in = JackMidiIn::new(&self.jack, &format!("{}I", &track.name), midi_from)?;
//midi_in.connect_to_matching()?;
//track.player.midi_ins.push(midi_in);
//let midi_out = JackMidiOut::new(&self.jack, &format!("{}O", &track.name), midi_to)?;
//midi_out.connect_to_matching()?;
//track.player.midi_outs.push(midi_out);
let midi_out = JackMidiOut::new(&self.jack, &format!("{}O", &track.name), midi_to)?;
midi_out.connect_to_matching()?;
track.player.midi_outs.push(midi_out);
self.tracks_mut().push(track);
let len = self.tracks().len();
let index = len - 1;
@ -293,7 +300,9 @@ pub trait HasTracks: HasSelection + HasClock + HasJack + HasEditor + Send + Sync
self.selected().track().and_then(|s|self.tracks_mut().get_mut(s))
}
}
pub trait Device: Send + Sync + std::fmt::Debug {}
pub trait Device: Send + Sync + std::fmt::Debug {
fn boxed <'a> (self) -> Box<dyn Device + 'a> where Self: Sized + 'a { Box::new(self) }
}
impl Device for Sampler {}
impl Device for Plugin {}
#[derive(Debug, Default)] pub struct Scene {

View file

@ -53,8 +53,8 @@ impl Default for ViewCache {
view!(TuiOut: |self: Tek| self.size.of(View(self, self.view)); {
":editor" => (&self.editor).boxed(),
":pool" => self.view_pool().boxed(),
":sample" => self.view_sample(self.is_editing()).boxed(),
":sampler" => self.view_sampler(self.is_editing(), &self.editor).boxed(),
//":sample" => self.view_sample(self.is_editing()).boxed(),
//":sampler" => self.view_sampler(self.is_editing(), &self.editor).boxed(),
":status" => self.view_editor().boxed(),
":toolbar" => self.view_clock().boxed(),
":tracks" => self.view_tracks().boxed(),

View file

@ -80,23 +80,12 @@ pub struct Clock {
pub sync: Arc<LaunchSync>,
/// Size of buffer in samples
pub chunk: Arc<AtomicUsize>,
}
impl From<&Jack> for Clock {
fn from (jack: &Jack) -> Self {
let (chunk, transport) = jack.with_client(|c|(c.buffer_size(), c.transport()));
let timebase = Arc::new(Timebase::default());
Self {
quant: Arc::new(24.into()),
sync: Arc::new(384.into()),
transport: Arc::new(Some(transport)),
chunk: Arc::new((chunk as usize).into()),
global: Arc::new(Moment::zero(&timebase)),
playhead: Arc::new(Moment::zero(&timebase)),
offset: Arc::new(Moment::zero(&timebase)),
started: RwLock::new(None).into(),
timebase,
}
}
/// For syncing the clock to an external source
pub midi_in: Arc<RwLock<Option<JackMidiIn>>>,
/// For syncing other devices to this clock
pub midi_out: Arc<RwLock<Option<JackMidiOut>>>,
/// For emitting a metronome
pub click_out: Arc<RwLock<Option<JackAudioOut>>>,
}
impl std::fmt::Debug for Clock {
fn fmt (&self, f: &mut std::fmt::Formatter<'_>) -> std::result::Result<(), std::fmt::Error> {
@ -112,12 +101,27 @@ impl std::fmt::Debug for Clock {
}
}
impl Clock {
pub fn new (jack: &Jack, bpm: Option<f64>) -> Self {
let clock = Self::from(jack);
pub fn new (jack: &Jack, bpm: Option<f64>) -> Usually<Self> {
let (chunk, transport) = jack.with_client(|c|(c.buffer_size(), c.transport()));
let timebase = Arc::new(Timebase::default());
let clock = Self {
quant: Arc::new(24.into()),
sync: Arc::new(384.into()),
transport: Arc::new(Some(transport)),
chunk: Arc::new((chunk as usize).into()),
global: Arc::new(Moment::zero(&timebase)),
playhead: Arc::new(Moment::zero(&timebase)),
offset: Arc::new(Moment::zero(&timebase)),
started: RwLock::new(None).into(),
timebase,
midi_in: Arc::new(RwLock::new(Some(JackMidiIn::new(jack, "M/clock", &[])?))),
midi_out: Arc::new(RwLock::new(Some(JackMidiOut::new(jack, "clock/M", &[])?))),
click_out: Arc::new(RwLock::new(Some(JackAudioOut::new(jack, "click", &[])?))),
};
if let Some(bpm) = bpm {
clock.timebase.bpm.set(bpm);
}
clock
Ok(clock)
}
pub fn timebase (&self) -> &Arc<Timebase> {
&self.timebase