okay, flattened device
Some checks are pending
/ build (push) Waiting to run

might join app + device + engine again?
This commit is contained in:
stop screaming 2026-02-21 00:49:10 +02:00
parent 37068784cb
commit 604a42a4bc
27 changed files with 5963 additions and 5944 deletions

614
device/.scratch.rs Normal file
View file

@ -0,0 +1,614 @@
//fn begin (browse: &mut Browse) => {
//unreachable!();
//}
//fn cancel (browse: &mut Browse) => {
//todo!()
////browse.mode = None;
////Ok(None)
//}
//fn confirm (browse: &mut Browse) => {
//todo!()
////Ok(match browse.mode {
////Some(PoolMode::Import(index, ref mut browse)) => {
////if browse.is_file() {
////let path = browse.path();
////browse.mode = None;
////let _undo = PoolClipCommand::import(browse, index, path)?;
////None
////} else if browse.is_dir() {
////browse.mode = Some(PoolMode::Import(index, browse.chdir()?));
////None
////} else {
////None
////}
////},
////Some(PoolMode::Export(index, ref mut browse)) => {
////todo!()
////},
////_ => unreachable!(),
////})
//}
//fn select (browse: &mut Browse, index: usize) => {
//todo!()
////Ok(match browse.mode {
////Some(PoolMode::Import(index, ref mut browse)) => {
////browse.index = index;
////None
////},
////Some(PoolMode::Export(index, ref mut browse)) => {
////browse.index = index;
////None
////},
////_ => unreachable!(),
////})
//}
//fn chdir (browse: &mut Browse, dir: PathBuf) => {
//todo!()
////Ok(match browse.mode {
////Some(PoolMode::Import(index, ref mut browse)) => {
////browse.mode = Some(PoolMode::Import(index, Browse::new(Some(dir))?));
////None
////},
////Some(PoolMode::Export(index, ref mut browse)) => {
////browse.mode = Some(PoolMode::Export(index, Browse::new(Some(dir))?));
////None
////},
////_ => unreachable!(),
////})
//}
//fn filter (browse: &mut Browse, filter: Arc<str>) => {
//todo!()
//}
//def_command!(ArrangementCommand: |arranger: Arrangement| {
//Home => { arranger.editor = None; Ok(None) },
//Edit => {
//let selection = arranger.selection().clone();
//arranger.editor = if arranger.editor.is_some() {
//None
//} else {
//match selection {
//Selection::TrackClip { track, scene } => {
//let clip = &mut arranger.scenes_mut()[scene].clips[track];
//if clip.is_none() {
////app.clip_auto_create();
//*clip = Some(Arc::new(RwLock::new(MidiClip::new(
//&format!("t{track:02}s{scene:02}"),
//false, 384, None, Some(ItemTheme::random())
//))));
//}
//clip.as_ref().map(|c|c.into())
//}
//_ => {
//None
//}
//}
//};
//if let Some(editor) = arranger.editor.as_mut() {
//if let Some(clip) = editor.clip() {
//let length = clip.read().unwrap().length.max(1);
//let width = arranger.size_inner.w().saturating_sub(20).max(1);
//editor.set_time_zoom(length / width);
//editor.redraw();
//}
//}
//Ok(None)
//},
////// Set the selection
//Select { selection: Selection } => { *arranger.selection_mut() = *selection; Ok(None) },
////// Launch the selected clip or scene
//Launch => {
//match *arranger.selection() {
//Selection::Track(t) => {
//arranger.tracks[t].sequencer.enqueue_next(None)
//},
//Selection::TrackClip { track, scene } => {
//arranger.tracks[track].sequencer.enqueue_next(arranger.scenes[scene].clips[track].as_ref())
//},
//Selection::Scene(s) => {
//for t in 0..arranger.tracks.len() {
//arranger.tracks[t].sequencer.enqueue_next(arranger.scenes[s].clips[t].as_ref())
//}
//},
//_ => {}
//};
//Ok(None)
//},
////// Set the color of the selected entity
//SetColor { palette: Option<ItemTheme> } => {
//let mut palette = palette.unwrap_or_else(||ItemTheme::random());
//let selection = *arranger.selection();
//Ok(Some(Self::SetColor { palette: Some(match selection {
//Selection::Mix => {
//std::mem::swap(&mut palette, &mut arranger.color);
//palette
//},
//Selection::Scene(s) => {
//std::mem::swap(&mut palette, &mut arranger.scenes[s].color);
//palette
//}
//Selection::Track(t) => {
//std::mem::swap(&mut palette, &mut arranger.tracks[t].color);
//palette
//}
//Selection::TrackClip { track, scene } => {
//if let Some(ref clip) = arranger.scenes[scene].clips[track] {
//let mut clip = clip.write().unwrap();
//std::mem::swap(&mut palette, &mut clip.color);
//palette
//} else {
//return Ok(None)
//}
//},
//_ => todo!()
//}) }))
//},
//Track { track: TrackCommand } => { todo!("delegate") },
//TrackAdd => {
//let index = arranger.track_add(None, None, &[], &[])?.0;
//*arranger.selection_mut() = match arranger.selection() {
//Selection::Track(_) => Selection::Track(index),
//Selection::TrackClip { track: _, scene } => Selection::TrackClip {
//track: index, scene: *scene
//},
//_ => *arranger.selection()
//};
//Ok(Some(Self::TrackDelete { index }))
//},
//TrackSwap { index: usize, other: usize } => {
//let index = *index;
//let other = *other;
//Ok(Some(Self::TrackSwap { index, other }))
//},
//TrackDelete { index: usize } => {
//let index = *index;
//let exists = arranger.tracks().get(index).is_some();
//if exists {
//let track = arranger.tracks_mut().remove(index);
//let Track { sequencer: Sequencer { midi_ins, midi_outs, .. }, .. } = track;
//for port in midi_ins.into_iter() {
//port.close()?;
//}
//for port in midi_outs.into_iter() {
//port.close()?;
//}
//for scene in arranger.scenes_mut().iter_mut() {
//scene.clips.remove(index);
//}
//}
//Ok(None)
////TODO:Ok(Some(Self::TrackAdd ( index, track: Some(deleted_track) })
//},
//MidiIn { input: MidiInputCommand } => {
//todo!("delegate"); Ok(None)
//},
//MidiInAdd => {
//arranger.midi_in_add()?;
//Ok(None)
//},
//MidiOut { output: MidiOutputCommand } => {
//todo!("delegate");
//Ok(None)
//},
//MidiOutAdd => {
//arranger.midi_out_add()?;
//Ok(None)
//},
//Device { command: DeviceCommand } => {
//todo!("delegate");
//Ok(None)
//},
//DeviceAdd { index: usize } => {
//todo!("delegate");
//Ok(None)
//},
//Scene { scene: SceneCommand } => {
//todo!("delegate");
//Ok(None)
//},
//OutputAdd => {
//arranger.midi_outs.push(MidiOutput::new(
//arranger.jack(),
//&format!("/M{}", arranger.midi_outs.len() + 1),
//&[]
//)?);
//Ok(None)
//},
//InputAdd => {
//arranger.midi_ins.push(MidiInput::new(
//arranger.jack(),
//&format!("M{}/", arranger.midi_ins.len() + 1),
//&[]
//)?);
//Ok(None)
//},
//SceneAdd => {
//let index = arranger.scene_add(None, None)?.0;
//*arranger.selection_mut() = match arranger.selection() {
//Selection::Scene(_) => Selection::Scene(index),
//Selection::TrackClip { track, scene } => Selection::TrackClip {
//track: *track,
//scene: index
//},
//_ => *arranger.selection()
//};
//Ok(None) // TODO
//},
//SceneSwap { index: usize, other: usize } => {
//let index = *index;
//let other = *other;
//Ok(Some(Self::SceneSwap { index, other }))
//},
//SceneDelete { index: usize } => {
//let index = *index;
//let scenes = arranger.scenes_mut();
//Ok(if scenes.get(index).is_some() {
//let _scene = scenes.remove(index);
//None
//} else {
//None
//})
//},
//SceneLaunch { index: usize } => {
//let index = *index;
//for track in 0..arranger.tracks.len() {
//let clip = arranger.scenes[index].clips[track].as_ref();
//arranger.tracks[track].sequencer.enqueue_next(clip);
//}
//Ok(None)
//},
//Clip { scene: ClipCommand } => {
//todo!("delegate")
//},
//ClipGet { a: usize, b: usize } => {
////(Get [a: usize, b: usize] cmd_todo!("\n\rtodo: clip: get: {a} {b}"))
////("get" [a: usize, b: usize] Some(Self::Get(a.unwrap(), b.unwrap())))
//todo!()
//},
//ClipPut { a: usize, b: usize } => {
////(Put [t: usize, s: usize, c: MaybeClip]
////Some(Self::Put(t, s, arranger.clip_put(t, s, c))))
////("put" [a: usize, b: usize, c: MaybeClip] Some(Self::Put(a.unwrap(), b.unwrap(), c.unwrap())))
//todo!()
//},
//ClipDel { a: usize, b: usize } => {
////("delete" [a: usize, b: usize] Some(Self::Put(a.unwrap(), b.unwrap(), None))))
//todo!()
//},
//ClipEnqueue { a: usize, b: usize } => {
////(Enqueue [t: usize, s: usize]
////cmd!(arranger.tracks[t].sequencer.enqueue_next(arranger.scenes[s].clips[t].as_ref())))
////("enqueue" [a: usize, b: usize] Some(Self::Enqueue(a.unwrap(), b.unwrap())))
//todo!()
//},
//ClipSwap { a: usize, b: usize }=> {
////(Edit [clip: MaybeClip] cmd_todo!("\n\rtodo: clip: edit: {clip:?}"))
////("edit" [a: MaybeClip] Some(Self::Edit(a.unwrap())))
//todo!()
//},
//});
// Update sequencer playhead indicator
//self.now().set(0.);
//if let Some((ref started_at, Some(ref playing))) = self.sequencer.play_clip {
//let clip = clip.read().unwrap();
//if *playing.read().unwrap() == *clip {
//let pulse = self.current().pulse.get();
//let start = started_at.pulse.get();
//let now = (pulse - start) % clip.length as f64;
//self.now().set(now);
//}
//}
//fn jack_from_lv2 (name: &str, plugin: &::livi::Plugin) -> Usually<Jack> {
//let counts = plugin.port_counts();
//let mut jack = Jack::new(name)?;
//for i in 0..counts.atom_sequence_inputs {
//jack = jack.midi_in(&format!("midi-in-{i}"))
//}
//for i in 0..counts.atom_sequence_outputs {
//jack = jack.midi_out(&format!("midi-out-{i}"));
//}
//for i in 0..counts.audio_inputs {
//jack = jack.audio_in(&format!("audio-in-{i}"));
//}
//for i in 0..counts.audio_outputs {
//jack = jack.audio_out(&format!("audio-out-{i}"));
//}
//Ok(jack)
//}
//handle!(TuiIn: |self:Plugin, from|{
//match from.event() {
//kpat!(KeyCode::Up) => {
//self.selected = self.selected.saturating_sub(1);
//Ok(Some(true))
//},
//kpat!(KeyCode::Down) => {
//self.selected = (self.selected + 1).min(match &self.plugin {
//Some(PluginKind::LV2(LV2Plugin { port_list, .. })) => port_list.len() - 1,
//_ => unimplemented!()
//});
//Ok(Some(true))
//},
//kpat!(KeyCode::PageUp) => {
//self.selected = self.selected.saturating_sub(8);
//Ok(Some(true))
//},
//kpat!(KeyCode::PageDown) => {
//self.selected = (self.selected + 10).min(match &self.plugin {
//Some(PluginKind::LV2(LV2Plugin { port_list, .. })) => port_list.len() - 1,
//_ => unimplemented!()
//});
//Ok(Some(true))
//},
//kpat!(KeyCode::Char(',')) => {
//match self.plugin.as_mut() {
//Some(PluginKind::LV2(LV2Plugin { port_list, ref mut instance, .. })) => {
//let index = port_list[self.selected].index;
//if let Some(value) = instance.control_input(index) {
//instance.set_control_input(index, value - 0.01);
//}
//},
//_ => {}
//}
//Ok(Some(true))
//},
//kpat!(KeyCode::Char('.')) => {
//match self.plugin.as_mut() {
//Some(PluginKind::LV2(LV2Plugin { port_list, ref mut instance, .. })) => {
//let index = port_list[self.selected].index;
//if let Some(value) = instance.control_input(index) {
//instance.set_control_input(index, value + 0.01);
//}
//},
//_ => {}
//}
//Ok(Some(true))
//},
//kpat!(KeyCode::Char('g')) => {
//match self.plugin {
////Some(PluginKind::LV2(ref mut plugin)) => {
////plugin.ui_thread = Some(run_lv2_ui(LV2PluginUI::new()?)?);
////},
//Some(_) => unreachable!(),
//None => {}
//}
//Ok(Some(true))
//},
//_ => Ok(None)
//}
//});
//from_atom!("plugin/lv2" => |jack: &Jack, args| -> Plugin {
//let mut name = String::new();
//let mut path = String::new();
//atom!(atom in args {
//Atom::Map(map) => {
//if let Some(Atom::Str(n)) = map.get(&Atom::Key(":name")) {
//name = String::from(*n);
//}
//if let Some(Atom::Str(p)) = map.get(&Atom::Key(":path")) {
//path = String::from(*p);
//}
//},
//_ => panic!("unexpected in lv2 '{name}'"),
//});
//Plugin::new_lv2(jack, &name, &path)
//});
//pub struct LV2PluginUI {
//write: (),
//controller: (),
//widget: (),
//features: (),
//transfer: (),
//}
//take!(BrowseCommand |state: Pool, iter|Ok(state.browse.as_ref()
//.map(|p|Take::take(p, iter))
//.transpose()?
//.flatten()));
//fn file_browser_filter (&self) -> Arc<str> {
//todo!()
//}
//fn file_browser_path (&self) -> PathBuf {
//todo!();
//}
///// Immutable reference to sample at cursor.
//fn sample_selected (&self) -> Option<Arc<RwLock<Sample>>> {
//for (i, sample) in self.mapped.iter().enumerate() {
//if i == self.cursor().0 {
//return sample.as_ref()
//}
//}
//for (i, sample) in self.unmapped.iter().enumerate() {
//if i + self.mapped.len() == self.cursor().0 {
//return Some(sample)
//}
//}
//None
//}
//fn sample_gain (&self) -> f32 {
//todo!()
//}
//fn sample_above () -> usize {
//self.note_pos().min(119) + 8
//}
//fn sample_below () -> usize {
//self.note_pos().max(8) - 8
//}
//fn sample_to_left () -> usize {
//self.note_pos().min(126) + 1
//}
//fn sample_to_right () -> usize {
//self.note_pos().max(1) - 1
//}
//fn selected_pitch () -> u7 {
//(self.note_pos() as u8).into() // TODO
//}
//select (&self, state: &mut Sampler, i: usize) -> Option<Self> {
//Self::Select(state.set_note_pos(i))
//}
///// Assign sample to slot
//set (&self, slot: u7, sample: Option<Arc<RwLock<Sample>>>) -> Option<Self> {
//let i = slot.as_int() as usize;
//let old = self.mapped[i].clone();
//self.mapped[i] = sample;
//Some(Self::Set(old))
//}
//set_start (&self, state: &mut Sampler, slot: u7, frame: usize) -> Option<Self> {
//todo!()
//}
//set_gain (&self, state: &mut Sampler, slot: u7, g: f32) -> Option<Self> {
//todo!()
//}
//note_on (&self, state: &mut Sampler, slot: u7, v: u7) -> Option<Self> {
//todo!()
//}
//note_off (&self, state: &mut Sampler, slot: u7) -> Option<Self> {
//todo!()
//}
//set_sample (&self, state: &mut Sampler, slot: u7, s: Option<Arc<RwLock<Sample>>>) -> Option<Self> {
//Some(Self::SetSample(p, state.set_sample(p, s)))
//}
//import (&self, state: &mut Sampler, c: FileBrowserCommand) -> Option<Self> {
//match c {
//FileBrowserCommand::Begin => {
////let voices = &state.state.voices;
////let sample = Arc::new(RwLock::new(Sample::new("", 0, 0, vec![])));
//state.mode = Some(SamplerMode::Import(0, FileBrowser::new(None)?));
//None
//},
//_ => {
//println!("\n\rtodo: import: filebrowser: {c:?}");
//None
//}
//}
//}
////(Select [i: usize] Some(Self::Select(state.set_note_pos(i))))
////(RecordBegin [p: u7] cmd!(state.begin_recording(p.as_int() as usize)))
////(RecordCancel [] cmd!(state.cancel_recording()))
////(RecordFinish [] cmd!(state.finish_recording()))
////(SetStart [p: u7, frame: usize] cmd_todo!("\n\rtodo: {self:?}"))
////(SetGain [p: u7, gain: f32] cmd_todo!("\n\rtodo: {self:?}"))
////(NoteOn [p: u7, velocity: u7] cmd_todo!("\n\rtodo: {self:?}"))
////(NoteOff [p: u7] cmd_todo!("\n\rtodo: {self:?}"))
////(SetSample [p: u7, s: Option<Arc<RwLock<Sample>>>] Some(Self::SetSample(p, state.set_sample(p, s))))
////(Import [c: FileBrowserCommand] match c {
////FileBrowserCommand::Begin => {
//////let voices = &state.state.voices;
//////let sample = Arc::new(RwLock::new(Sample::new("", 0, 0, vec![])));
////state.mode = Some(SamplerMode::Import(0, FileBrowser::new(None)?));
////None
////},
////_ => {
////println!("\n\rtodo: import: filebrowser: {c:?}");
////None
////}
////})));
////("import" [,..a]
////FileBrowserCommand::try_from_expr(state, a).map(Self::Import))
////("select" [i: usize]
////Some(Self::Select(i.expect("no index"))))
////("record/begin" [i: u7]
////Some(Self::RecordBegin(i.expect("no index"))))
////("record/cancel" []
////Some(Self::RecordCancel))
////("record/finish" []
////Some(Self::RecordFinish))
////("set/sample" [i: u7, s: Option<Arc<RwLock<Sample>>>]
////Some(Self::SetSample(i.expect("no index"), s.expect("no sampler"))))
////("set/start" [i: u7, s: usize]
////Some(Self::SetStart(i.expect("no index"), s.expect("no start"))))
////("set/gain" [i: u7, g: f32]
////Some(Self::SetGain(i.expect("no index"), g.expect("no gain"))))
////("note/on" [p: u7, v: u7]
////Some(Self::NoteOn(p.expect("no slot"), v.expect("no velocity"))))
////("note/off" [p: u7]
////Some(Self::NoteOff(p.expect("no slot"))))));
// TODO:
//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)
//}
//}
//_ =>{}
//},
//_ =>{}
//}
//}
//}
//scene_scroll: Fill::Y(Fixed::X(1, ScrollbarV {
//offset: arrangement.scene_scroll,
//length: h_scenes_area as usize,
//total: h_scenes as usize,
//})),
//take!(SceneCommand |state: Arrangement, iter|state.selected_scene().as_ref()
//.map(|t|Take::take(t, iter)).transpose().map(|x|x.flatten()));
//pub(crate) fn io_conns <'a, T: PortsSizes<'a>> (
//fg: Color, bg: Color, iter: &mut impl Iterator<Item = (usize, &'a Arc<str>, &'a [Connect], usize, usize)>
//) -> impl Content<TuiOut> + 'a {
//Fill::XY(Thunk::new(move|to: &mut TuiOut|for (_, _, connections, y, y2) in &mut *iter {
//to.place(&map_south(y as u16, (y2-y) as u16, Bsp::s(
//Fill::Y(Tui::bold(true, wrap(bg, fg, Fill::Y(Align::w(&"▞▞▞▞ ▞▞▞▞"))))),
//Thunk::new(|to: &mut TuiOut|for (index, _connection) in connections.iter().enumerate() {
//to.place(&map_south(index as u16, 1, Fill::Y(Align::w(Tui::bold(false,
//wrap(bg, fg, Fill::Y(&"")))))))
//})
//)))
//}))
//}
//track_scroll: Fill::Y(Fixed::Y(1, ScrollbarH {
//offset: arrangement.track_scroll,
//length: h_tracks_area as usize,
//total: h_scenes as usize,
//})),
//take!(TrackCommand |state: Arrangement, iter|state.selected_track().as_ref()
//.map(|t|Take::take(t, iter)).transpose().map(|x|x.flatten()));