mirror of
https://codeberg.org/unspeaker/tek.git
synced 2025-12-06 19:56:42 +01:00
wip: down to 13 errors
This commit is contained in:
parent
6ce83fb27a
commit
ebdb8881e9
19 changed files with 793 additions and 691 deletions
|
|
@ -6,6 +6,7 @@ impl Arrangement {
|
|||
fn _todo_arc_str_stub_ (&self) -> Arc<str> { todo!() }
|
||||
fn _todo_item_theme_stub (&self) -> ItemTheme { todo!() }
|
||||
fn _todo_opt_item_theme_stub (&self) -> Option<ItemTheme> { todo!() }
|
||||
fn _todo_opt_selection_ (&self) -> Option<Selection> { todo!() }
|
||||
}
|
||||
|
||||
#[tengri_proc::command(Arrangement)]
|
||||
|
|
@ -200,117 +201,20 @@ impl ArrangementCommand {
|
|||
}
|
||||
}
|
||||
|
||||
impl<'state> Context<'state, TrackCommand> for Arrangement {
|
||||
fn get <'source> (&'state self, iter: &mut TokenIter<'source>) -> Option<TrackCommand> {
|
||||
impl<'state> Context<'state, MidiInputCommand> for Arrangement {
|
||||
fn get <'source> (&'state self, iter: &mut TokenIter<'source>) -> Option<MidiInputCommand> {
|
||||
Context::get(&self, iter)
|
||||
}
|
||||
}
|
||||
|
||||
#[tengri_proc::expose]
|
||||
impl Track {
|
||||
fn _todo_opt_bool_stub_ (&self) -> Option<bool> { todo!() }
|
||||
fn _todo_usize_stub_ (&self) -> usize { todo!() }
|
||||
fn _todo_arc_str_stub_ (&self) -> Arc<str> { todo!() }
|
||||
fn _todo_item_theme_stub (&self) -> ItemTheme { todo!() }
|
||||
}
|
||||
|
||||
#[tengri_proc::command(Track)]
|
||||
impl TrackCommand {
|
||||
fn set_name (track: &mut Track, mut name: Arc<str>) -> Perhaps<Self> {
|
||||
std::mem::swap(&mut name, &mut track.name);
|
||||
Ok(Some(Self::SetName { name }))
|
||||
}
|
||||
fn set_color (track: &mut Track, mut color: ItemTheme) -> Perhaps<Self> {
|
||||
std::mem::swap(&mut color, &mut track.color);
|
||||
Ok(Some(Self::SetColor { color }))
|
||||
}
|
||||
fn set_mute (track: &mut Track, value: Option<bool>) -> Perhaps<Self> {
|
||||
todo!()
|
||||
}
|
||||
fn set_solo (track: &mut Track, value: Option<bool>) -> Perhaps<Self> {
|
||||
todo!()
|
||||
}
|
||||
fn set_rec (track: &mut Track, value: Option<bool>) -> Perhaps<Self> {
|
||||
let current = track.sequencer.recording;
|
||||
let value = value.unwrap_or(!current);
|
||||
Ok((value != current).then_some(Self::SetRec { value: Some(current) }))
|
||||
}
|
||||
fn set_mon (track: &mut Track, value: Option<bool>) -> Perhaps<Self> {
|
||||
let current = track.sequencer.monitoring;
|
||||
let value = value.unwrap_or(!current);
|
||||
Ok((value != current).then_some(Self::SetRec { value: Some(current) }))
|
||||
}
|
||||
fn set_size (track: &mut Track, size: usize) -> Perhaps<Self> {
|
||||
todo!()
|
||||
}
|
||||
fn set_zoom (track: &mut Track, zoom: usize) -> Perhaps<Self> {
|
||||
todo!()
|
||||
}
|
||||
fn stop (track: &mut Track) -> Perhaps<Self> {
|
||||
track.sequencer.enqueue_next(None);
|
||||
Ok(None)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'state> Context<'state, SceneCommand> for Arrangement {
|
||||
fn get <'source> (&'state self, iter: &mut TokenIter<'source>) -> Option<SceneCommand> {
|
||||
impl<'state> Context<'state, MidiOutputCommand> for Arrangement {
|
||||
fn get <'source> (&'state self, iter: &mut TokenIter<'source>) -> Option<MidiOutputCommand> {
|
||||
Context::get(&self, iter)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'state> Context<'state, ClipCommand> for Arrangement {
|
||||
fn get <'source> (&'state self, iter: &mut TokenIter<'source>) -> Option<ClipCommand> {
|
||||
impl<'state> Context<'state, DeviceCommand> for Arrangement {
|
||||
fn get <'source> (&'state self, iter: &mut TokenIter<'source>) -> Option<DeviceCommand> {
|
||||
Context::get(&self, iter)
|
||||
}
|
||||
}
|
||||
|
||||
#[tengri_proc::expose]
|
||||
impl Scene {
|
||||
fn _todo_opt_bool_stub_ (&self) -> Option<bool> { todo!() }
|
||||
fn _todo_usize_stub_ (&self) -> usize { todo!() }
|
||||
fn _todo_arc_str_stub_ (&self) -> Arc<str> { todo!() }
|
||||
fn _todo_item_theme_stub (&self) -> ItemTheme { todo!() }
|
||||
}
|
||||
|
||||
#[tengri_proc::command(Scene)]
|
||||
impl SceneCommand {
|
||||
fn set_name (scene: &mut Scene, mut name: Arc<str>) -> Perhaps<Self> {
|
||||
std::mem::swap(&mut scene.name, &mut name);
|
||||
Ok(Some(Self::SetName { name }))
|
||||
}
|
||||
fn set_color (scene: &mut Scene, mut color: ItemTheme) -> Perhaps<Self> {
|
||||
std::mem::swap(&mut scene.color, &mut color);
|
||||
Ok(Some(Self::SetColor { color }))
|
||||
}
|
||||
fn set_size (scene: &mut Scene, size: usize) -> Perhaps<Self> {
|
||||
todo!()
|
||||
}
|
||||
fn set_zoom (scene: &mut Scene, zoom: usize) -> Perhaps<Self> {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
#[tengri_proc::expose]
|
||||
impl MidiClip {
|
||||
fn _todo_opt_bool_stub_ (&self) -> Option<bool> { todo!() }
|
||||
fn _todo_bool_stub_ (&self) -> bool { todo!() }
|
||||
fn _todo_usize_stub_ (&self) -> usize { todo!() }
|
||||
fn _todo_arc_str_stub_ (&self) -> Arc<str> { todo!() }
|
||||
fn _todo_item_theme_stub (&self) -> ItemTheme { todo!() }
|
||||
fn _todo_opt_item_theme_stub (&self) -> Option<ItemTheme> { todo!() }
|
||||
}
|
||||
|
||||
#[tengri_proc::command(MidiClip)]
|
||||
impl ClipCommand {
|
||||
fn set_color (clip: &mut MidiClip, color: Option<ItemTheme>) -> Perhaps<Self> {
|
||||
//(SetColor [t: usize, s: usize, c: ItemTheme]
|
||||
//clip.clip_set_color(t, s, c).map(|o|Self::SetColor(t, s, o)))));
|
||||
//("color" [a: usize, b: usize] Some(Self::SetColor(a.unwrap(), b.unwrap(), ItemTheme::random())))
|
||||
todo!()
|
||||
}
|
||||
fn set_loop (clip: &mut MidiClip, looping: Option<bool>) -> Perhaps<Self> {
|
||||
//(SetLoop [t: usize, s: usize, l: bool] cmd_todo!("\n\rtodo: {self:?}"))
|
||||
//("loop" [a: usize, b: usize, c: bool] Some(Self::SetLoop(a.unwrap(), b.unwrap(), c.unwrap())))
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,36 @@
|
|||
use crate::*;
|
||||
|
||||
impl<'state> Context<'state, ClipCommand> for Arrangement {
|
||||
fn get <'source> (&'state self, iter: &mut TokenIter<'source>) -> Option<ClipCommand> {
|
||||
Context::get(&self, iter)
|
||||
}
|
||||
}
|
||||
|
||||
#[tengri_proc::expose]
|
||||
impl MidiClip {
|
||||
fn _todo_opt_bool_stub_ (&self) -> Option<bool> { todo!() }
|
||||
fn _todo_bool_stub_ (&self) -> bool { todo!() }
|
||||
fn _todo_usize_stub_ (&self) -> usize { todo!() }
|
||||
fn _todo_arc_str_stub_ (&self) -> Arc<str> { todo!() }
|
||||
fn _todo_item_theme_stub (&self) -> ItemTheme { todo!() }
|
||||
fn _todo_opt_item_theme_stub (&self) -> Option<ItemTheme> { todo!() }
|
||||
}
|
||||
|
||||
#[tengri_proc::command(MidiClip)]
|
||||
impl ClipCommand {
|
||||
fn set_color (clip: &mut MidiClip, color: Option<ItemTheme>) -> Perhaps<Self> {
|
||||
//(SetColor [t: usize, s: usize, c: ItemTheme]
|
||||
//clip.clip_set_color(t, s, c).map(|o|Self::SetColor(t, s, o)))));
|
||||
//("color" [a: usize, b: usize] Some(Self::SetColor(a.unwrap(), b.unwrap(), ItemTheme::random())))
|
||||
todo!()
|
||||
}
|
||||
fn set_loop (clip: &mut MidiClip, looping: Option<bool>) -> Perhaps<Self> {
|
||||
//(SetLoop [t: usize, s: usize, l: bool] cmd_todo!("\n\rtodo: {self:?}"))
|
||||
//("loop" [a: usize, b: usize, c: bool] Some(Self::SetLoop(a.unwrap(), b.unwrap(), c.unwrap())))
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
impl Arrangement {
|
||||
|
||||
/// Put a clip in a slot
|
||||
|
|
@ -26,7 +57,7 @@ impl Arrangement {
|
|||
|
||||
/// Get the active clip
|
||||
pub(crate) fn clip (&self) -> Option<Arc<RwLock<MidiClip>>> {
|
||||
self.scene()?.clips.get(self.selected().track()?)?.clone()
|
||||
self.scene()?.clips.get(self.selected.track()?)?.clone()
|
||||
}
|
||||
|
||||
/// Toggle looping for the active clip
|
||||
|
|
@ -37,64 +68,3 @@ impl Arrangement {
|
|||
}
|
||||
|
||||
}
|
||||
|
||||
#[cfg(all(feature = "pool", feature = "editor"))]
|
||||
pub trait AutoCreate:
|
||||
Has<Vec<Scene>> +
|
||||
Has<Vec<Track>> +
|
||||
Has<Option<MidiEditor>> +
|
||||
Has<Option<Selection>> +
|
||||
Has<Option<Pool>> +
|
||||
Send + Sync
|
||||
{
|
||||
// Create new clip in pool when entering empty cell
|
||||
fn clip_auto_create (&mut self) -> Option<Arc<RwLock<MidiClip>>> {
|
||||
if let Some(pool) = Has::<Option<Pool>>::get(self)
|
||||
&& let Some(Selection::TrackClip { track, scene }) = self.get()
|
||||
&& let Some(scene) = Has::<Vec<Scene>>::get_mut(self).get_mut(scene)
|
||||
&& let Some(slot) = scene.clips.get_mut(track)
|
||||
&& slot.is_none()
|
||||
&& let Some(track) = Has::<Vec<Track>>::get_mut(self).get_mut(track)
|
||||
{
|
||||
let (index, mut clip) = pool.add_new_clip();
|
||||
// autocolor: new clip colors from scene and track color
|
||||
let color = track.color.base.mix(scene.color.base, 0.5);
|
||||
clip.write().unwrap().color = ItemColor::random_near(color, 0.2).into();
|
||||
if let Some(ref mut editor) = Has::<Option<MidiEditor>>::get_mut(self) {
|
||||
editor.set_clip(Some(&clip));
|
||||
}
|
||||
*slot = Some(clip.clone());
|
||||
Some(clip)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(all(feature = "pool", feature = "editor"))]
|
||||
pub trait AutoRemove:
|
||||
Has<Vec<Scene>> +
|
||||
Has<Vec<Track>> +
|
||||
Has<Option<MidiEditor>> +
|
||||
Has<Option<Selection>> +
|
||||
Has<Option<Pool>> +
|
||||
Send + Sync
|
||||
{
|
||||
// Remove clip from arrangement when exiting empty clip editor
|
||||
fn clip_auto_remove (&mut self) {
|
||||
if let Some(ref mut pool) = Has::<Option<Pool>>::get(self)
|
||||
&& let Some(Selection::TrackClip { track, scene }) = self.get()
|
||||
&& let Some(scene) = Has::<Vec<Scene>>::get_mut(self).get_mut(scene)
|
||||
&& let Some(slot) = scene.clips.get_mut(track)
|
||||
&& let Some(clip) = slot.as_mut()
|
||||
{
|
||||
let mut swapped = None;
|
||||
if clip.read().unwrap().count_midi_messages() == 0 {
|
||||
std::mem::swap(&mut swapped, slot);
|
||||
}
|
||||
if let Some(clip) = swapped {
|
||||
pool.delete_clip(&clip.read().unwrap());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,6 +6,10 @@ pub struct Arrangement {
|
|||
pub name: Arc<str>,
|
||||
/// Base color.
|
||||
pub color: ItemTheme,
|
||||
/// Jack client handle
|
||||
pub jack: Jack,
|
||||
/// Source of time
|
||||
pub clock: Clock,
|
||||
/// List of global midi inputs
|
||||
pub midi_ins: Vec<JackMidiIn>,
|
||||
/// List of global midi outputs
|
||||
|
|
@ -35,8 +39,8 @@ pub struct Arrangement {
|
|||
pub arranger: Arc<RwLock<Buffer>>,
|
||||
/// Display size
|
||||
pub size: Measure<TuiOut>,
|
||||
/// Jack client handle
|
||||
pub jack: Jack,
|
||||
/// Contains all clips in arrangement
|
||||
pub pool: Pool,
|
||||
}
|
||||
|
||||
has!(Option<Selection>: |self: Arrangement|self.selected);
|
||||
|
|
@ -45,6 +49,7 @@ has!(Vec<JackMidiOut>: |self: Arrangement|self.midi_outs);
|
|||
has!(Vec<Scene>: |self: Arrangement|self.scenes);
|
||||
has!(Vec<Track>: |self: Arrangement|self.tracks);
|
||||
has!(Jack: |self: Arrangement|self.jack);
|
||||
has!(Clock: |self: Arrangement|self.clock);
|
||||
|
||||
impl Arrangement {
|
||||
/// Width of display
|
||||
|
|
@ -57,7 +62,8 @@ impl Arrangement {
|
|||
}
|
||||
/// Width taken by all tracks.
|
||||
pub(crate) fn w_tracks (&self) -> u16 {
|
||||
self.tracks_with_sizes().last().map(|(_, _, _, x)|x as u16).unwrap_or(0)
|
||||
self.tracks_with_sizes(&self.selected, None).last()
|
||||
.map(|(_, _, _, x)|x as u16).unwrap_or(0)
|
||||
}
|
||||
/// Width available to display tracks.
|
||||
pub(crate) fn w_tracks_area (&self, is_editing: bool) -> u16 {
|
||||
|
|
@ -114,3 +120,15 @@ impl Arrangement {
|
|||
//1 + self.devices_with_sizes().last().map(|(_, _, _, _, y)|y as u16).unwrap_or(0)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "sampler")]
|
||||
impl Arrangement {
|
||||
/// Get the first sampler of the active track
|
||||
pub fn sampler (&self) -> Option<&Sampler> {
|
||||
self.track().map(|t|t.sampler(0)).flatten()
|
||||
}
|
||||
/// Get the first sampler of the active track
|
||||
pub fn sampler_mut (&mut self) -> Option<&mut Sampler> {
|
||||
self.track_mut().map(|t|t.sampler_mut(0)).flatten()
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,10 +1,42 @@
|
|||
use crate::*;
|
||||
|
||||
impl<'state> Context<'state, SceneCommand> for Arrangement {
|
||||
fn get <'source> (&'state self, iter: &mut TokenIter<'source>) -> Option<SceneCommand> {
|
||||
Context::get(&self, iter)
|
||||
}
|
||||
}
|
||||
|
||||
#[tengri_proc::expose]
|
||||
impl Scene {
|
||||
fn _todo_opt_bool_stub_ (&self) -> Option<bool> { todo!() }
|
||||
fn _todo_usize_stub_ (&self) -> usize { todo!() }
|
||||
fn _todo_arc_str_stub_ (&self) -> Arc<str> { todo!() }
|
||||
fn _todo_item_theme_stub (&self) -> ItemTheme { todo!() }
|
||||
}
|
||||
|
||||
#[tengri_proc::command(Scene)]
|
||||
impl SceneCommand {
|
||||
fn set_name (scene: &mut Scene, mut name: Arc<str>) -> Perhaps<Self> {
|
||||
std::mem::swap(&mut scene.name, &mut name);
|
||||
Ok(Some(Self::SetName { name }))
|
||||
}
|
||||
fn set_color (scene: &mut Scene, mut color: ItemTheme) -> Perhaps<Self> {
|
||||
std::mem::swap(&mut scene.color, &mut color);
|
||||
Ok(Some(Self::SetColor { color }))
|
||||
}
|
||||
fn set_size (scene: &mut Scene, size: usize) -> Perhaps<Self> {
|
||||
todo!()
|
||||
}
|
||||
fn set_zoom (scene: &mut Scene, zoom: usize) -> Perhaps<Self> {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Has<Option<Scene>> + Send + Sync> HasScene for T {}
|
||||
|
||||
pub trait HasScene: Has<Option<Scene>> + Send + Sync {
|
||||
fn scene (&self) -> &Option<Scene> {
|
||||
Has::<Option<Scene>>::get(self)
|
||||
fn scene (&self) -> Option<&Scene> {
|
||||
Has::<Option<Scene>>::get(self).as_ref()
|
||||
}
|
||||
fn scene_mut (&mut self) -> &mut Option<Scene> {
|
||||
Has::<Option<Scene>>::get_mut(self)
|
||||
|
|
|
|||
|
|
@ -13,9 +13,9 @@ pub trait HasSelection: Has<Selection> {
|
|||
|
||||
impl Has<Option<Track>> for Arrangement {
|
||||
fn get (&self) -> &Option<Track> {
|
||||
Has::<Option<Selection>>::get(self)
|
||||
.and_then(|selection|selection.track())
|
||||
.and_then(|index|Has::<Vec<Track>>::get(self).get(index))
|
||||
let selection: Selection = Has::<Option<Track>>::get(self)?;
|
||||
let index: Option<usize> = selection.track()?;
|
||||
let track: Track = Has::<Vec<Track>>::get(self).get(index)?;
|
||||
}
|
||||
fn get_mut (&mut self) -> &mut Option<Track> {
|
||||
Has::<Option<Selection>>::get(self)
|
||||
|
|
@ -33,7 +33,7 @@ impl Has<Option<Scene>> for Arrangement {
|
|||
fn get_mut (&mut self) -> &mut Option<Scene> {
|
||||
Has::<Option<Selection>>::get(self)
|
||||
.and_then(|selection|selection.track())
|
||||
.and_then(|index|Has::<Vec<Scene>>::get_mut(self).get_mut(index))
|
||||
.and_then(|index|Has::<Vec<Scene>>::get_mut(self).get_mut(index).as_deref())
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,57 @@
|
|||
use crate::*;
|
||||
|
||||
impl<'state> Context<'state, TrackCommand> for Arrangement {
|
||||
fn get <'source> (&'state self, iter: &mut TokenIter<'source>) -> Option<TrackCommand> {
|
||||
Context::get(&self, iter)
|
||||
}
|
||||
}
|
||||
|
||||
#[tengri_proc::expose]
|
||||
impl Track {
|
||||
fn _todo_opt_bool_stub_ (&self) -> Option<bool> { todo!() }
|
||||
fn _todo_usize_stub_ (&self) -> usize { todo!() }
|
||||
fn _todo_arc_str_stub_ (&self) -> Arc<str> { todo!() }
|
||||
fn _todo_item_theme_stub (&self) -> ItemTheme { todo!() }
|
||||
}
|
||||
|
||||
#[tengri_proc::command(Track)]
|
||||
impl TrackCommand {
|
||||
fn set_name (track: &mut Track, mut name: Arc<str>) -> Perhaps<Self> {
|
||||
std::mem::swap(&mut name, &mut track.name);
|
||||
Ok(Some(Self::SetName { name }))
|
||||
}
|
||||
fn set_color (track: &mut Track, mut color: ItemTheme) -> Perhaps<Self> {
|
||||
std::mem::swap(&mut color, &mut track.color);
|
||||
Ok(Some(Self::SetColor { color }))
|
||||
}
|
||||
fn set_mute (track: &mut Track, value: Option<bool>) -> Perhaps<Self> {
|
||||
todo!()
|
||||
}
|
||||
fn set_solo (track: &mut Track, value: Option<bool>) -> Perhaps<Self> {
|
||||
todo!()
|
||||
}
|
||||
fn set_rec (track: &mut Track, value: Option<bool>) -> Perhaps<Self> {
|
||||
let current = track.sequencer.recording;
|
||||
let value = value.unwrap_or(!current);
|
||||
Ok((value != current).then_some(Self::SetRec { value: Some(current) }))
|
||||
}
|
||||
fn set_mon (track: &mut Track, value: Option<bool>) -> Perhaps<Self> {
|
||||
let current = track.sequencer.monitoring;
|
||||
let value = value.unwrap_or(!current);
|
||||
Ok((value != current).then_some(Self::SetRec { value: Some(current) }))
|
||||
}
|
||||
fn set_size (track: &mut Track, size: usize) -> Perhaps<Self> {
|
||||
todo!()
|
||||
}
|
||||
fn set_zoom (track: &mut Track, zoom: usize) -> Perhaps<Self> {
|
||||
todo!()
|
||||
}
|
||||
fn stop (track: &mut Track) -> Perhaps<Self> {
|
||||
track.sequencer.enqueue_next(None);
|
||||
Ok(None)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
pub struct Track {
|
||||
/// Name of track
|
||||
|
|
@ -97,77 +149,6 @@ impl Track {
|
|||
}
|
||||
}
|
||||
|
||||
impl<T: Has<Vec<Track>> + Send + Sync> HasTracks for T {}
|
||||
|
||||
pub trait HasTracks: Has<Vec<Track>> + Send + Sync {
|
||||
fn tracks (&self) -> &Vec<Track> {
|
||||
Has::<Vec<Track>>::get(self)
|
||||
}
|
||||
fn tracks_mut (&mut self) -> &mut Vec<Track> {
|
||||
Has::<Vec<Track>>::get_mut(self)
|
||||
}
|
||||
/// Run audio callbacks for every track and every device
|
||||
fn tracks_render (&mut self, client: &Client, scope: &ProcessScope) -> Control {
|
||||
for track in self.tracks_mut().iter_mut() {
|
||||
if Control::Quit == PlayerAudio(
|
||||
track.sequencer_mut(), &mut self.note_buf, &mut self.midi_buf
|
||||
).process(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
|
||||
}
|
||||
fn track_longest_name (&self) -> usize {
|
||||
self.tracks().iter().map(|s|s.name.len()).fold(0, usize::max)
|
||||
}
|
||||
/// Stop all playing clips
|
||||
fn tracks_stop_all (&mut self) {
|
||||
for track in self.tracks_mut().iter_mut() {
|
||||
track.sequencer.enqueue_next(None);
|
||||
}
|
||||
}
|
||||
/// Stop all playing clips
|
||||
fn tracks_launch (&mut self, clips: Option<Vec<Option<Arc<RwLock<MidiClip>>>>>) {
|
||||
if let Some(clips) = clips {
|
||||
for (clip, track) in clips.iter().zip(self.tracks_mut()) {
|
||||
track.sequencer.enqueue_next(clip);
|
||||
}
|
||||
} else {
|
||||
for track in self.tracks_mut().iter_mut() {
|
||||
track.sequencer.enqueue_next(None);
|
||||
}
|
||||
}
|
||||
}
|
||||
/// Iterate over tracks with their corresponding sizes.
|
||||
fn tracks_with_sizes (
|
||||
&self,
|
||||
selection: &Option<Selection>,
|
||||
editor_width: Option<usize>
|
||||
) -> impl TracksSizes<'_> {
|
||||
let mut x = 0;
|
||||
let active_track = if let Some(width) = editor_width && let Some(selection) = selection {
|
||||
selection.track()
|
||||
} else {
|
||||
None
|
||||
};
|
||||
self.tracks().iter().enumerate().map(move |(index, track)|{
|
||||
let width = active_track
|
||||
.and_then(|_|editor_width)
|
||||
.unwrap_or(track.width.max(8));
|
||||
let data = (index, track, x, x + width);
|
||||
x += width + Self::TRACK_SPACING;
|
||||
data
|
||||
})
|
||||
}
|
||||
/// Spacing between tracks.
|
||||
const TRACK_SPACING: usize = 0;
|
||||
}
|
||||
|
||||
impl<T: Has<Option<Track>> + Send + Sync> HasTrack for T {}
|
||||
|
||||
pub trait HasTrack: Has<Option<Track>> + Send + Sync {
|
||||
|
|
@ -178,116 +159,3 @@ pub trait HasTrack: Has<Option<Track>> + Send + Sync {
|
|||
Has::<Option<Track>>::get_mut(self).as_mut()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> ArrangerView<'a> {
|
||||
pub(crate) fn tracks_with_sizes_scrolled (&'a self)
|
||||
-> impl TracksSizes<'a>
|
||||
{
|
||||
self.arrangement
|
||||
.tracks_with_sizes(&self.arrangement.selected, self.is_editing.then_some(20/*FIXME*/))
|
||||
.map_while(move|(t, track, x1, x2)|{
|
||||
(self.width_mid > x2 as u16).then_some((t, track, x1, x2))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn per_track_top <'a, T: Content<TuiOut> + 'a, U: TracksSizes<'a>> (
|
||||
width: u16,
|
||||
tracks: impl Fn() -> U + Send + Sync + 'a,
|
||||
callback: impl Fn(usize, &'a Track)->T + Send + Sync + 'a
|
||||
) -> impl Content<TuiOut> + 'a {
|
||||
Align::x(Tui::bg(Reset, Map::new(tracks,
|
||||
move|(index, track, x1, x2): (usize, &'a Track, usize, usize), _|{
|
||||
let width = (x2 - x1) as u16;
|
||||
map_east(x1 as u16, width, Fixed::x(width, Tui::fg_bg(
|
||||
track.color.lightest.rgb,
|
||||
track.color.base.rgb,
|
||||
callback(index, track))))})))
|
||||
}
|
||||
|
||||
pub(crate) fn per_track <'a, T: Content<TuiOut> + 'a, U: TracksSizes<'a>> (
|
||||
width: u16,
|
||||
tracks: impl Fn() -> U + Send + Sync + 'a,
|
||||
callback: impl Fn(usize, &'a Track)->T + Send + Sync + 'a
|
||||
) -> impl Content<TuiOut> + 'a {
|
||||
per_track_top(
|
||||
width,
|
||||
tracks,
|
||||
move|index, track|Fill::y(Align::y(callback(index, track)))
|
||||
)
|
||||
}
|
||||
|
||||
impl<T: HasTracks + HasScenes + HasClock + HasJack> AddTrack for T {}
|
||||
|
||||
pub trait AddTrack: HasTracks + HasScenes + HasClock + HasJack {
|
||||
/// Add multiple tracks
|
||||
fn tracks_add (
|
||||
&mut self,
|
||||
count: usize,
|
||||
width: Option<usize>,
|
||||
mins: &[PortConnect],
|
||||
mouts: &[PortConnect],
|
||||
) -> Usually<()> {
|
||||
let jack = self.jack().clone();
|
||||
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 mut track = self.track_add(None, Some(color), mins, mouts)?.1;
|
||||
if let Some(width) = width {
|
||||
track.width = width;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Add a track
|
||||
fn track_add (
|
||||
&mut self,
|
||||
name: Option<&str>,
|
||||
color: Option<ItemTheme>,
|
||||
mins: &[PortConnect],
|
||||
mouts: &[PortConnect],
|
||||
) -> Usually<(usize, &mut Track)> {
|
||||
self.track_last += 1;
|
||||
let name: Arc<str> = name.map_or_else(
|
||||
||format!("Track{:02}", self.track_last).into(),
|
||||
|x|x.to_string().into()
|
||||
);
|
||||
let mut track = Track {
|
||||
width: (name.len() + 2).max(12),
|
||||
color: color.unwrap_or_else(ItemTheme::random),
|
||||
sequencer: Sequencer::new(
|
||||
&format!("{name}"),
|
||||
self.jack(),
|
||||
Some(self.clock()),
|
||||
None,
|
||||
mins,
|
||||
mouts
|
||||
)?,
|
||||
name,
|
||||
..Default::default()
|
||||
};
|
||||
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.push(None);
|
||||
}
|
||||
}
|
||||
Ok((index, &mut self.tracks_mut()[index]))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "sampler")]
|
||||
impl Arrangement {
|
||||
/// Get the first sampler of the active track
|
||||
pub fn sampler (&self) -> Option<&Sampler> {
|
||||
self.track().map(|t|t.sampler(0)).flatten()
|
||||
}
|
||||
/// Get the first sampler of the active track
|
||||
pub fn sampler_mut (&mut self) -> Option<&mut Sampler> {
|
||||
self.track_mut().map(|t|t.sampler_mut(0)).flatten()
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,145 @@
|
|||
use crate::*;
|
||||
|
||||
impl<T: Has<Vec<Track>> + Send + Sync> HasTracks for T {}
|
||||
|
||||
pub trait HasTracks: Has<Vec<Track>> + Send + Sync {
|
||||
fn tracks (&self) -> &Vec<Track> {
|
||||
Has::<Vec<Track>>::get(self)
|
||||
}
|
||||
fn tracks_mut (&mut self) -> &mut Vec<Track> {
|
||||
Has::<Vec<Track>>::get_mut(self)
|
||||
}
|
||||
/// Run audio callbacks for every track and every device
|
||||
fn process_tracks (&mut self, client: &Client, scope: &ProcessScope) -> Control {
|
||||
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
|
||||
}
|
||||
fn track_longest_name (&self) -> usize {
|
||||
self.tracks().iter().map(|s|s.name.len()).fold(0, usize::max)
|
||||
}
|
||||
/// Stop all playing clips
|
||||
fn tracks_stop_all (&mut self) {
|
||||
for track in self.tracks_mut().iter_mut() {
|
||||
track.sequencer.enqueue_next(None);
|
||||
}
|
||||
}
|
||||
/// Stop all playing clips
|
||||
fn tracks_launch (&mut self, clips: Option<Vec<Option<Arc<RwLock<MidiClip>>>>>) {
|
||||
if let Some(clips) = clips {
|
||||
for (clip, track) in clips.iter().zip(self.tracks_mut()) {
|
||||
track.sequencer.enqueue_next(clip.as_ref());
|
||||
}
|
||||
} else {
|
||||
for track in self.tracks_mut().iter_mut() {
|
||||
track.sequencer.enqueue_next(None);
|
||||
}
|
||||
}
|
||||
}
|
||||
/// Iterate over tracks with their corresponding sizes.
|
||||
fn tracks_with_sizes (
|
||||
&self,
|
||||
selection: &Option<Selection>,
|
||||
editor_width: Option<usize>
|
||||
) -> impl TracksSizes<'_> {
|
||||
let mut x = 0;
|
||||
let active_track = if let Some(width) = editor_width && let Some(selection) = selection {
|
||||
selection.track()
|
||||
} else {
|
||||
None
|
||||
};
|
||||
self.tracks().iter().enumerate().map(move |(index, track)|{
|
||||
let width = active_track
|
||||
.and_then(|_|editor_width)
|
||||
.unwrap_or(track.width.max(8));
|
||||
let data = (index, track, x, x + width);
|
||||
x += width + Self::TRACK_SPACING;
|
||||
data
|
||||
})
|
||||
}
|
||||
/// Spacing between tracks.
|
||||
const TRACK_SPACING: usize = 0;
|
||||
}
|
||||
|
||||
impl<'a> ArrangerView<'a> {
|
||||
pub(crate) fn tracks_with_sizes_scrolled (&'a self)
|
||||
-> impl TracksSizes<'a>
|
||||
{
|
||||
self.arrangement
|
||||
.tracks_with_sizes(&self.arrangement.selected, self.is_editing.then_some(20/*FIXME*/))
|
||||
.map_while(move|(t, track, x1, x2)|{
|
||||
(self.width_mid > x2 as u16).then_some((t, track, x1, x2))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: HasTracks + HasScenes + HasClock + HasJack> AddTrack for T {}
|
||||
|
||||
pub trait AddTrack: HasTracks + HasScenes + HasClock + HasJack {
|
||||
/// Add multiple tracks
|
||||
fn tracks_add (
|
||||
&mut self,
|
||||
count: usize,
|
||||
width: Option<usize>,
|
||||
mins: &[PortConnect],
|
||||
mouts: &[PortConnect],
|
||||
) -> Usually<()> {
|
||||
let jack = self.jack().clone();
|
||||
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 mut track = self.track_add(None, Some(color), mins, mouts)?.1;
|
||||
if let Some(width) = width {
|
||||
track.width = width;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Add a track
|
||||
fn track_add (
|
||||
&mut self,
|
||||
name: Option<&str>,
|
||||
color: Option<ItemTheme>,
|
||||
mins: &[PortConnect],
|
||||
mouts: &[PortConnect],
|
||||
) -> Usually<(usize, &mut Track)> {
|
||||
self.track_last += 1;
|
||||
let name: Arc<str> = name.map_or_else(
|
||||
||format!("Track{:02}", self.track_last).into(),
|
||||
|x|x.to_string().into()
|
||||
);
|
||||
let mut track = Track {
|
||||
width: (name.len() + 2).max(12),
|
||||
color: color.unwrap_or_else(ItemTheme::random),
|
||||
sequencer: Sequencer::new(
|
||||
&format!("{name}"),
|
||||
self.jack(),
|
||||
Some(self.clock()),
|
||||
None,
|
||||
mins,
|
||||
mouts
|
||||
)?,
|
||||
name,
|
||||
..Default::default()
|
||||
};
|
||||
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.push(None);
|
||||
}
|
||||
}
|
||||
Ok((index, &mut self.tracks_mut()[index]))
|
||||
}
|
||||
}
|
||||
|
|
@ -283,3 +283,29 @@ impl<'a> ArrangerView<'a> {
|
|||
}
|
||||
|
||||
}
|
||||
|
||||
pub(crate) fn per_track_top <'a, T: Content<TuiOut> + 'a, U: TracksSizes<'a>> (
|
||||
width: u16,
|
||||
tracks: impl Fn() -> U + Send + Sync + 'a,
|
||||
callback: impl Fn(usize, &'a Track)->T + Send + Sync + 'a
|
||||
) -> impl Content<TuiOut> + 'a {
|
||||
Align::x(Tui::bg(Reset, Map::new(tracks,
|
||||
move|(index, track, x1, x2): (usize, &'a Track, usize, usize), _|{
|
||||
let width = (x2 - x1) as u16;
|
||||
map_east(x1 as u16, width, Fixed::x(width, Tui::fg_bg(
|
||||
track.color.lightest.rgb,
|
||||
track.color.base.rgb,
|
||||
callback(index, track))))})))
|
||||
}
|
||||
|
||||
pub(crate) fn per_track <'a, T: Content<TuiOut> + 'a, U: TracksSizes<'a>> (
|
||||
width: u16,
|
||||
tracks: impl Fn() -> U + Send + Sync + 'a,
|
||||
callback: impl Fn(usize, &'a Track)->T + Send + Sync + 'a
|
||||
) -> impl Content<TuiOut> + 'a {
|
||||
per_track_top(
|
||||
width,
|
||||
tracks,
|
||||
move|index, track|Fill::y(Align::y(callback(index, track)))
|
||||
)
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue