This commit is contained in:
i do not exist 2026-09-23 12:06:38 +03:00
parent 0a89fab28a
commit 9e3cc1139d
18 changed files with 450 additions and 414 deletions

View file

@ -4,8 +4,6 @@ impl_from!(AppCmd: |x: AxisCmd| AppCmd::Axis { command: x });
impl_from!(AppCmd: |x: BrowseCmd| AppCmd::Browse { command: x });
impl_from!(AppCmd: |x: MidiClipCmd| AppCmd::MidiClip { command: x });
impl_from!(AppCmd: |x: MidiEditCmd| AppCmd::MidiEdit { command: x });
impl_from!(AppCmd: |x: ModeCmd| AppCmd::Mode { command: x });
//impl_from!(AppCmd: |x: PoolCmd| AppCmd::Pool { command: x });
impl_from!(AppCmd: |x: ProjectCmd| AppCmd::Project { command: x });
impl_from!(AppCmd: |x: SceneCmd| AppCmd::Scene { command: x });
impl_from!(AppCmd: |x: ScenesCmd| AppCmd::Scenes { command: x });
@ -18,6 +16,25 @@ impl_from!(AppCmd: |x: TracksCmd| AppCmd::Tracks { command: x });
self.exit.exit();
Ok(None)
}
/// Switch to another set of contextual modes.
#[command(Mode = "mode")] pub fn cmd_mode (&mut self, modes: Vec<Arc<str>>) -> Perhaps<AppCmd> {
*self.modes_mut() = modes.into();
Ok(None)
}
/// Enter a contextual mode.
#[command(Enter = "enter")] pub fn cmd_mode_enter (&mut self, mode: Arc<str>) -> Perhaps<AppCmd> {
self.modes_mut().push(mode);
Ok(Some(AppCmd::Leave))
}
/// Leave a contextual mode.
#[command(Leave = "leave")] fn cmd_mode_leave (&mut self) -> Perhaps<AppCmd> {
let mode = self.modes_mut().pop();
Ok(mode.map(|mode|AppCmd::Enter { mode }))
}
/// Navigate along a contextual axis.
#[command(Axis = "axis")] pub fn cmd_axis (&mut self, command: AxisCmd) -> Perhaps<AppCmd> {
Ok(command.dispatch(self)?.map(Into::into))
}
/// Cancel current selection.
#[command(Cancel = "cancel")] pub fn cmd_cancel (&mut self) -> Perhaps<AppCmd> {
todo!("cancel")
@ -44,27 +61,22 @@ impl_from!(AppCmd: |x: TracksCmd| AppCmd::Tracks { command: x });
std::mem::swap(&mut self.dialog, &mut dialog);
Ok(Some(AppCmd::Dialog { dialog }))
}
#[command(Mode = "mode")] pub fn cmd_mode (&mut self, command: ModeCmd) -> Perhaps<AppCmd> {
Ok(command.dispatch(self)?.map(Into::into))
}
#[command(Project = "session")] pub fn cmd_session (&mut self, command: ProjectCmd) -> Perhaps<AppCmd> {
Ok(command.dispatch(self)?.map(Into::into))
}
#[command(Axis = "axis")] pub fn cmd_axis (&mut self, command: AxisCmd) -> Perhaps<AppCmd> {
Ok(command.dispatch(self)?.map(Into::into))
}
/// Command the file browser.
#[command(Browse = "browse")] pub fn cmd_browse (&mut self, command: BrowseCmd) -> Perhaps<AppCmd> {
Ok(command.dispatch(self)?.map(Into::into))
}
/// Commands that alter the current session.
#[command(Project = "session")] pub fn cmd_session (&mut self, command: ProjectCmd) -> Perhaps<AppCmd> {
Ok(command.dispatch(self)?.map(Into::into))
}
/// Commands pertaining to MIDI clips.
#[command(MidiClip = "clip")] pub fn cmd_midi_clip (&mut self, command: MidiClipCmd) -> Perhaps<AppCmd> {
Ok(command.dispatch(self)?.map(Into::into))
}
/// Commands pertaining to the MIDI clip editor.
#[command(MidiEdit = "edit")] pub fn cmd_midi_edit (&mut self, command: MidiEditCmd) -> Perhaps<AppCmd> {
Ok(command.dispatch(self)?.map(Into::into))
}
//#[command(Pool = "pool")] pub fn cmd_pool (&mut self, command: PoolCmd) -> Perhaps<AppCmd> {
//Ok(command.dispatch(self)?.map(Into::into))
//}
#[command(Scene = "scene")] pub fn cmd_scene (&mut self, command: SceneCmd) -> Perhaps<AppCmd> {
Ok(command.dispatch(self)?.map(Into::into))
}
@ -77,6 +89,9 @@ impl_from!(AppCmd: |x: TracksCmd| AppCmd::Tracks { command: x });
#[command(Tracks = "tracks")] pub fn cmd_tracks (&mut self, command: TracksCmd) -> Perhaps<AppCmd> {
Ok(command.dispatch(self)?.map(Into::into))
}
//#[command(Pool = "pool")] pub fn cmd_pool (&mut self, command: PoolCmd) -> Perhaps<AppCmd> {
//Ok(command.dispatch(self)?.map(Into::into))
//}
}
impl<T: HasProject> ProjectControl for T {}
@ -142,29 +157,17 @@ impl<T> ClockControl for T where T: HasClock
}
}
impl<T: HasTracks
impl<T> TracksControl for T where T: HasTracks
+ HasScenes
+ HasSelection
+ HasJack<'static>
+ Namespace<usize>
+ Namespace<Arc<[Connect]>>
+ Namespace<Option<usize>>
+ Namespace<Option<Arc<str>>>
+ Namespace<Option<ItemTheme>>
+ Namespace<Option<Arc<[Connect]>>>
+ Namespace<ClipPool>
> TracksControl for T {}
{}
#[tek_proc::commands(TracksCmd)] pub trait TracksControl: HasTracks
+ HasScenes
+ HasSelection
+ HasJack<'static>
+ Namespace<usize>
+ Namespace<Arc<[Connect]>>
+ Namespace<Option<usize>>
+ Namespace<Option<Arc<str>>>
+ Namespace<Option<ItemTheme>>
+ Namespace<Option<Arc<[Connect]>>>
+ Namespace<ClipPool>
{
/// Stop all playing clips
@ -190,7 +193,7 @@ impl<T: HasTracks
Ok(None)
}
#[command(Add = "add")] fn tracks_add (&mut self) -> Perhaps<TracksCmd>
where Self: HasScenes + HasJack<'static>
where Self: HasScenes + HasJack<'static> + Sized
{
let (_index, _) = self.tracks_add_one(None, None, [].into(), [].into())?;
Ok(None)
@ -212,6 +215,76 @@ impl<T: HasTracks
*self.selection_mut() = select;
Ok(None)
}
/// Run audio callbacks for every track and every device
fn tracks_jack_process (&mut self, client: &Client, scope: &ProcessScope) -> Control
where Self: HasJack<'static>
{
for track in self.tracks_mut().iter_mut() {
if Control::Quit == Audio::process(&mut track.sequencer, client, scope) {
return Control::Quit
}
for device in track.devices.iter_mut() {
if Control::Quit == DeviceAudio(device).process(client, scope) {
return Control::Quit
}
}
}
Control::Continue
}
/// Add multiple tracks
fn tracks_add_many (
&mut self,
count: usize,
width: Option<usize>,
mins: Arc<[Connect]>,
mouts: Arc<[Connect]>,
) -> Usually<()>
where Self: HasJack<'static> + HasScenes
{
let track_color_1 = ItemColor::random();
let track_color_2 = ItemColor::random();
for i in 0..count {
let color = track_color_1.mix(track_color_2, i as f32 / count as f32).into();
let track = self.tracks_add_one(None, Some(color), mins.clone(), mouts.clone())?.1;
if let Some(width) = width {
track.width = width;
}
}
Ok(())
}
/// Add a track
fn tracks_add_one (
&mut self,
name: Option<Arc<str>>,
color: Option<ItemTheme>,
mins: Arc<[Connect]>,
mouts: Arc<[Connect]>,
) -> Usually<(usize, &mut Track)>
where Self: HasJack<'static> + HasScenes
{
let name: Arc<str> = name.map_or_else(
||format!("trk{}", self.tracks_last()).into(),
|x|x.to_string().into()
);
*self.tracks_last_mut() += 1;
let track = Track::new_added(self.jack(), Some(self.clock()), &name, color, None, mins, mouts)?;
self.tracks_mut().push(track);
let len = self.tracks().len();
let index = len - 1;
for scene in self.scenes_mut().iter_mut() {
while scene.clips.len() < len {
scene.clips.append(None);
}
}
Ok((index, &mut self.tracks_mut()[index]))
}
fn track_longest_name (&self) -> usize {
self.tracks().iter().map(|s|s.name.len()).fold(0, usize::max)
}
}
impl<T: HasTrack
@ -283,9 +356,6 @@ impl<T: HasScenes
+ HasTrackScroll
+ HasSelection
+ HasJack<'static>
+ Namespace<usize>
+ Namespace<Option<ItemTheme>>
+ Namespace<Option<Arc<str>>>
> ScenesControl for T {}
#[tek_proc::commands(ScenesCmd)] pub trait ScenesControl: HasScenes
@ -293,11 +363,8 @@ impl<T: HasScenes
+ HasTrackScroll
+ HasSelection
+ HasJack<'static>
+ Namespace<usize>
+ Namespace<Option<ItemTheme>>
+ Namespace<Option<Arc<str>>>
{
#[command(Add = "add")] fn scenes_add (&mut self) -> Perhaps<ScenesCmd> {
#[command(Add = "add")] fn scenes_add (&mut self) -> Perhaps<ScenesCmd> where Self: Sized {
let (_index, _) = self.scenes_add_one(None, None)?;
Ok(None)
}
@ -318,6 +385,41 @@ impl<T: HasScenes
*self.selection_mut() = select;
Ok(None)
}
/// Generate the default name for a new scene
fn scenes_default_name (&self) -> Arc<str> {
format!("s{:3>}", self.scenes().len() + 1).into()
}
fn scenes_longest_name (&self) -> usize {
self.scenes().iter().map(|s|s.name.len()).fold(0, usize::max)
}
/// Add multiple scenes
fn scenes_add_many (&mut self, n: usize)
-> Usually<()> where Self: HasTracks
{
let scene_color_1 = ItemColor::random();
let scene_color_2 = ItemColor::random();
for i in 0..n {
let _ = self.scenes_add_one(None, Some(
scene_color_1.mix(scene_color_2, i as f32 / n as f32).into()
))?;
}
Ok(())
}
/// Add a scene
fn scenes_add_one (&mut self, name: Option<Arc<str>>, color: Option<ItemTheme>)
-> Usually<(usize, &mut Scene)>
where
Self: HasTracks
{
let scene = Scene {
name: name.map_or_else(||self.scenes_default_name(), |x|x.to_string().into()),
clips: Pool::from(vec![None;self.tracks().len()]),
color: color.unwrap_or_else(ItemTheme::random),
};
self.scenes_mut().push(scene);
let index = self.scenes().len() - 1;
Ok((index, &mut self.scenes_mut()[index]))
}
}
impl<T: HasScene
@ -721,36 +823,6 @@ impl<T: HasClip + HasEditor
////}
//}
impl<T> ModeControl for T where T: HasModeStack
+ Namespace<Vec<Arc<str>>> + Namespace<Arc<str>> + Namespace<usize>
{}
#[tek_proc::commands(ModeCmd)] pub trait ModeControl: HasModeStack
+ Namespace<Vec<Arc<str>>> + Namespace<Arc<str>> + Namespace<usize>
{
#[command(Reset = "reset")] fn mode_reset (&mut self, modes: Vec<Arc<str>>) -> Perhaps<ModeCmd> {
*self.modes_mut() = modes.into();
Ok(None)
}
#[command(Push = "push")] fn mode_push (&mut self, mode: Arc<str>) -> Perhaps<ModeCmd> {
self.modes_mut().push(mode);
Ok(Some(ModeCmd::Pop))
}
#[command(Pop = "pop")] fn mode_pop (&mut self) -> Perhaps<ModeCmd> {
let mode = self.modes_mut().pop();
Ok(mode.map(|mode|ModeCmd::Push { mode }))
}
}
pub trait HasClip {
fn clip (&self) -> Option<ArcRwLockReadGuard<RawRwLock, Option<Arc<RwLock<MidiClip>>>>> {
self.clip_slot().map(|slot|slot.read())
}
fn clip_mut (&self) -> Option<ArcRwLockWriteGuard<RawRwLock, Option<Arc<RwLock<MidiClip>>>>> {
self.clip_slot().map(|slot|slot.write())
}
fn clip_slot (&self) -> Option<Slot<MidiClip>>;
}
impl<T> MidiClipControl for T where T: HasClip
+ Namespace<Option<bool>> + Namespace<Option<ItemTheme>> {}
#[tek_proc::commands(MidiClipCmd)] pub trait MidiClipControl: HasClip