mirror of
https://codeberg.org/unspeaker/tek.git
synced 2026-03-13 11:50:44 +01:00
wip: removing Has trait
This commit is contained in:
parent
b6559fc904
commit
9ae35830c3
5 changed files with 520 additions and 540 deletions
6
Justfile
6
Justfile
|
|
@ -4,6 +4,9 @@ export RUST_BACKTRACE := "1"
|
||||||
default +ARGS="new":
|
default +ARGS="new":
|
||||||
target/debug/tek {{ARGS}}
|
target/debug/tek {{ARGS}}
|
||||||
|
|
||||||
|
doc +ARGS="":
|
||||||
|
cargo doc --open -j4 --document-private-items {{ARGS}}
|
||||||
|
|
||||||
cloc:
|
cloc:
|
||||||
for src in {cli,edn/src,input/src,jack/src,midi/src,output/src,plugin/src,sampler/src,tek/src,time/src,tui/src}; do echo; echo $src; cloc --quiet $src; done
|
for src in {cli,edn/src,input/src,jack/src,midi/src,output/src,plugin/src,sampler/src,tek/src,time/src,tui/src}; do echo; echo $src; cloc --quiet $src; done
|
||||||
|
|
||||||
|
|
@ -43,9 +46,6 @@ run-init:
|
||||||
prof:
|
prof:
|
||||||
CARGO_PROFILE_RELEASE_DEBUG=true cargo flamegraph --
|
CARGO_PROFILE_RELEASE_DEBUG=true cargo flamegraph --
|
||||||
|
|
||||||
doc:
|
|
||||||
cargo doc -j4 --workspace --document-private-items
|
|
||||||
|
|
||||||
release := "reset && cargo run --release --"
|
release := "reset && cargo run --release --"
|
||||||
release:
|
release:
|
||||||
{{release}}
|
{{release}}
|
||||||
|
|
|
||||||
87
app/tek.rs
87
app/tek.rs
|
|
@ -384,12 +384,12 @@ pub fn device_kinds () -> &'static [&'static str] {
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: Has<Vec<Device>>> HasDevices for T {
|
impl<T: AsRef<Vec<Device>> + AsMut<Vec<Device>>> HasDevices for T {
|
||||||
fn devices (&self) -> &Vec<Device> {
|
fn devices (&self) -> &Vec<Device> {
|
||||||
self.get()
|
self.as_ref()
|
||||||
}
|
}
|
||||||
fn devices_mut (&mut self) -> &mut Vec<Device> {
|
fn devices_mut (&mut self) -> &mut Vec<Device> {
|
||||||
self.get_mut()
|
self.as_mut()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1127,26 +1127,6 @@ mod view {
|
||||||
Fixed::XY(20, 1 + ins, frame.enclose(Fixed::XY(20, 1 + ins, field)))
|
Fixed::XY(20, 1 + ins, frame.enclose(Fixed::XY(20, 1 + ins, field)))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn per_track_top <'a, T: Content<TuiOut> + 'a, U: TracksSizes<'a>> (
|
|
||||||
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 fn per_track <'a, T: Content<TuiOut> + 'a, U: TracksSizes<'a>> (
|
|
||||||
tracks: impl Fn() -> U + Send + Sync + 'a,
|
|
||||||
callback: impl Fn(usize, &'a Track)->T + Send + Sync + 'a
|
|
||||||
) -> impl Content<TuiOut> + 'a {
|
|
||||||
per_track_top(tracks, move|index, track|Fill::Y(Align::y(callback(index, track))))
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn io_ports <'a, T: PortsSizes<'a>> (
|
pub fn io_ports <'a, T: PortsSizes<'a>> (
|
||||||
fg: Color, bg: Color, iter: impl Fn()->T + Send + Sync + 'a
|
fg: Color, bg: Color, iter: impl Fn()->T + Send + Sync + 'a
|
||||||
) -> impl Content<TuiOut> + 'a {
|
) -> impl Content<TuiOut> + 'a {
|
||||||
|
|
@ -1159,67 +1139,6 @@ mod view {
|
||||||
Fill::Y(Align::w(Tui::bold(false, Tui::fg_bg(fg, bg,
|
Fill::Y(Align::w(Tui::bold(false, Tui::fg_bg(fg, bg,
|
||||||
&connect.info)))))))))
|
&connect.info)))))))))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "lv2")] pub fn draw_header (state: &Lv2, to: &mut TuiOut, x: u16, y: u16, w: u16) {
|
|
||||||
let style = Style::default().gray();
|
|
||||||
let label1 = format!(" {}", state.name);
|
|
||||||
to.blit(&label1, x + 1, y, Some(style.white().bold()));
|
|
||||||
if let Some(ref path) = state.path {
|
|
||||||
let label2 = format!("{}…", &path[..((w as usize - 10).min(path.len()))]);
|
|
||||||
to.blit(&label2, x + 2 + label1.len() as u16, y, Some(style.not_dim()));
|
|
||||||
}
|
|
||||||
//Ok(Rect { x, y, width: w, height: 1 })
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn draw_sample (
|
|
||||||
to: &mut TuiOut, x: u16, y: u16, note: Option<&u7>, sample: &Sample, focus: bool
|
|
||||||
) -> Usually<usize> {
|
|
||||||
let style = if focus { Style::default().green() } else { Style::default() };
|
|
||||||
if focus {
|
|
||||||
to.blit(&"🬴", x+1, y, Some(style.bold()));
|
|
||||||
}
|
|
||||||
let label1 = format!("{:3} {:12}",
|
|
||||||
note.map(|n|n.to_string()).unwrap_or(String::default()),
|
|
||||||
sample.name);
|
|
||||||
let label2 = format!("{:>6} {:>6} +0.0",
|
|
||||||
sample.start,
|
|
||||||
sample.end);
|
|
||||||
to.blit(&label1, x+2, y, Some(style.bold()));
|
|
||||||
to.blit(&label2, x+3+label1.len()as u16, y, Some(style));
|
|
||||||
Ok(label1.len() + label2.len() + 4)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn sampler_jack_process (
|
|
||||||
state: &mut Sampler, _: &Client, scope: &ProcessScope
|
|
||||||
) -> Control {
|
|
||||||
if let Some(midi_in) = &state.midi_in {
|
|
||||||
for midi in midi_in.port().iter(scope) {
|
|
||||||
sampler_midi_in(&state.samples, &state.voices, midi)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
state.process_audio_out(scope);
|
|
||||||
state.process_audio_in(scope);
|
|
||||||
Control::Continue
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Create [Voice]s from [Sample]s in response to MIDI input.
|
|
||||||
pub(crate) fn sampler_midi_in (
|
|
||||||
samples: &SampleKit<128>, voices: &Arc<RwLock<Vec<Voice>>>, RawMidi { time, bytes }: RawMidi
|
|
||||||
) {
|
|
||||||
if let Ok(LiveEvent::Midi { message, .. }) = LiveEvent::parse(bytes) {
|
|
||||||
match message {
|
|
||||||
MidiMessage::NoteOn { ref key, ref vel } => {
|
|
||||||
if let Some(sample) = samples.get(key.as_int() as usize) {
|
|
||||||
voices.write().unwrap().push(Sample::play(sample, time as usize, vel));
|
|
||||||
}
|
|
||||||
},
|
|
||||||
MidiMessage::Controller { controller: _, value: _ } => {
|
|
||||||
// TODO
|
|
||||||
}
|
|
||||||
_ => {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)] mod test_view_meter {
|
#[cfg(test)] mod test_view_meter {
|
||||||
|
|
|
||||||
250
app/tek_impls.rs
250
app/tek_impls.rs
|
|
@ -2,7 +2,10 @@ use crate::*;
|
||||||
use std::fmt::Write;
|
use std::fmt::Write;
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
|
||||||
impl Draw<TuiOut> for App {
|
mod app {
|
||||||
|
use crate::*;
|
||||||
|
|
||||||
|
impl Draw<TuiOut> for App {
|
||||||
fn draw (&self, to: &mut TuiOut) {
|
fn draw (&self, to: &mut TuiOut) {
|
||||||
if let Some(e) = self.error.read().unwrap().as_ref() {
|
if let Some(e) = self.error.read().unwrap().as_ref() {
|
||||||
to.place_at(to.area(), e);
|
to.place_at(to.area(), e);
|
||||||
|
|
@ -14,16 +17,16 @@ impl Draw<TuiOut> for App {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
handle!(TuiIn: |self: App, input|{
|
handle!(TuiIn: |self: App, input|{
|
||||||
let commands = collect_commands(self, input)?;
|
let commands = collect_commands(self, input)?;
|
||||||
let history = execute_commands(self, commands)?;
|
let history = execute_commands(self, commands)?;
|
||||||
self.history.extend(history.into_iter());
|
self.history.extend(history.into_iter());
|
||||||
Ok(None)
|
Ok(None)
|
||||||
});
|
});
|
||||||
|
|
||||||
impl<'a> Namespace<'a, AppCommand> for App {
|
impl<'a> Namespace<'a, AppCommand> for App {
|
||||||
symbols!('a |app| -> AppCommand {
|
symbols!('a |app| -> AppCommand {
|
||||||
"x/inc" => AppCommand::Inc { axis: ControlAxis::X },
|
"x/inc" => AppCommand::Inc { axis: ControlAxis::X },
|
||||||
"x/dec" => AppCommand::Dec { axis: ControlAxis::X },
|
"x/dec" => AppCommand::Dec { axis: ControlAxis::X },
|
||||||
|
|
@ -32,27 +35,27 @@ impl<'a> Namespace<'a, AppCommand> for App {
|
||||||
"confirm" => AppCommand::Confirm,
|
"confirm" => AppCommand::Confirm,
|
||||||
"cancel" => AppCommand::Cancel,
|
"cancel" => AppCommand::Cancel,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Understand<TuiOut, ()> for App {
|
impl Understand<TuiOut, ()> for App {
|
||||||
fn understand_expr <'a> (&'a self, to: &mut TuiOut, lang: &'a impl Expression) -> Usually<()> {
|
fn understand_expr <'a> (&'a self, to: &mut TuiOut, lang: &'a impl Expression) -> Usually<()> {
|
||||||
app_understand_expr(self, to, lang)
|
app_understand_expr(self, to, lang)
|
||||||
}
|
}
|
||||||
fn understand_word <'a> (&'a self, to: &mut TuiOut, lang: &'a impl Expression) -> Usually<()> {
|
fn understand_word <'a> (&'a self, to: &mut TuiOut, lang: &'a impl Expression) -> Usually<()> {
|
||||||
app_understand_word(self, to, lang)
|
app_understand_word(self, to, lang)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn app_understand_expr (state: &App, to: &mut TuiOut, lang: &impl Expression) -> Usually<()> {
|
fn app_understand_expr (state: &App, to: &mut TuiOut, lang: &impl Expression) -> Usually<()> {
|
||||||
if evaluate_output_expression(state, to, lang)?
|
if evaluate_output_expression(state, to, lang)?
|
||||||
|| evaluate_output_expression_tui(state, to, lang)? {
|
|| evaluate_output_expression_tui(state, to, lang)? {
|
||||||
Ok(())
|
Ok(())
|
||||||
} else {
|
} else {
|
||||||
Err(format!("App::understand_expr: unexpected: {lang:?}").into())
|
Err(format!("App::understand_expr: unexpected: {lang:?}").into())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn app_understand_word (state: &App, to: &mut TuiOut, dsl: &impl Expression) -> Usually<()> {
|
fn app_understand_word (state: &App, to: &mut TuiOut, dsl: &impl Expression) -> Usually<()> {
|
||||||
let mut frags = dsl.src()?.unwrap().split("/");
|
let mut frags = dsl.src()?.unwrap().split("/");
|
||||||
match frags.next() {
|
match frags.next() {
|
||||||
Some(":logo") => to.place(&view_logo()),
|
Some(":logo") => to.place(&view_logo()),
|
||||||
|
|
@ -155,9 +158,9 @@ fn app_understand_word (state: &App, to: &mut TuiOut, dsl: &impl Expression) ->
|
||||||
_ => unreachable!()
|
_ => unreachable!()
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
impl App {
|
impl App {
|
||||||
/// Update memoized render of clock values.
|
/// Update memoized render of clock values.
|
||||||
/// ```
|
/// ```
|
||||||
/// tek::App::default().update_clock();
|
/// tek::App::default().update_clock();
|
||||||
|
|
@ -276,9 +279,13 @@ impl App {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Dialog {
|
mod dialog {
|
||||||
|
use crate::*;
|
||||||
|
|
||||||
|
impl Dialog {
|
||||||
/// ```
|
/// ```
|
||||||
/// let _ = tek::Dialog::welcome();
|
/// let _ = tek::Dialog::welcome();
|
||||||
/// ```
|
/// ```
|
||||||
|
|
@ -346,6 +353,7 @@ impl Dialog {
|
||||||
pub fn browser (&self) -> Option<&Arc<Browse>> { todo!() }
|
pub fn browser (&self) -> Option<&Arc<Browse>> { todo!() }
|
||||||
/// FIXME: implement
|
/// FIXME: implement
|
||||||
pub fn browser_target (&self) -> Option<&BrowseTarget> { todo!() }
|
pub fn browser_target (&self) -> Option<&BrowseTarget> { todo!() }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "vst2")] impl<E: Engine> ::vst::host::Host for Plugin<E> {}
|
#[cfg(feature = "vst2")] impl<E: Engine> ::vst::host::Host for Plugin<E> {}
|
||||||
|
|
@ -353,26 +361,14 @@ impl Dialog {
|
||||||
mod arrange {
|
mod arrange {
|
||||||
use crate::*;
|
use crate::*;
|
||||||
|
|
||||||
has!(Jack<'static>: |self: Arrangement|self.jack);
|
impl_has!(Jack<'static>: |self: Arrangement|self.jack);
|
||||||
has!(Measure<TuiOut>: |self: Arrangement|self.size);
|
impl_has!(Measure<TuiOut>: |self: Arrangement|self.size);
|
||||||
|
|
||||||
#[cfg(feature = "editor")] has!(Option<MidiEditor>: |self: Arrangement|self.editor);
|
#[cfg(feature = "editor")] impl_has!(Option<MidiEditor>: |self: Arrangement|self.editor);
|
||||||
#[cfg(feature = "port")] has!(Vec<MidiInput>: |self: Arrangement|self.midi_ins);
|
#[cfg(feature = "port")] impl_has!(Vec<MidiInput>: |self: Arrangement|self.midi_ins);
|
||||||
#[cfg(feature = "port")] has!(Vec<MidiOutput>: |self: Arrangement|self.midi_outs);
|
#[cfg(feature = "port")] impl_has!(Vec<MidiOutput>: |self: Arrangement|self.midi_outs);
|
||||||
#[cfg(feature = "clock")] has!(Clock: |self: Arrangement|self.clock);
|
#[cfg(feature = "clock")] impl_has!(Clock: |self: Arrangement|self.clock);
|
||||||
#[cfg(feature = "select")] has!(Selection: |self: Arrangement|self.selection);
|
#[cfg(feature = "select")] impl_has!(Selection: |self: Arrangement|self.selection);
|
||||||
|
|
||||||
#[cfg(feature = "track")] impl TracksView for Arrangement {} // -> to auto?
|
|
||||||
|
|
||||||
#[cfg(all(feature = "select", feature = "track"))] has!(Vec<Track>: |self: Arrangement|self.tracks);
|
|
||||||
#[cfg(all(feature = "select", feature = "track"))] maybe_has!(Track: |self: Arrangement|
|
|
||||||
{ Has::<Selection>::get(self).track().map(|index|Has::<Vec<Track>>::get(self).get(index)).flatten() };
|
|
||||||
{ Has::<Selection>::get(self).track().map(|index|Has::<Vec<Track>>::get_mut(self).get_mut(index)).flatten() });
|
|
||||||
|
|
||||||
#[cfg(all(feature = "select", feature = "scene"))] has!(Vec<Scene>: |self: Arrangement|self.scenes);
|
|
||||||
#[cfg(all(feature = "select", feature = "scene"))] maybe_has!(Scene: |self: Arrangement|
|
|
||||||
{ Has::<Selection>::get(self).track().map(|index|Has::<Vec<Scene>>::get(self).get(index)).flatten() };
|
|
||||||
{ Has::<Selection>::get(self).track().map(|index|Has::<Vec<Scene>>::get_mut(self).get_mut(index)).flatten() });
|
|
||||||
|
|
||||||
#[cfg(feature = "select")] impl Arrangement {
|
#[cfg(feature = "select")] impl Arrangement {
|
||||||
#[cfg(feature = "clip")] fn selected_clip (&self) -> Option<MidiClip> { todo!() }
|
#[cfg(feature = "clip")] fn selected_clip (&self) -> Option<MidiClip> { todo!() }
|
||||||
|
|
@ -748,10 +744,7 @@ mod browse {
|
||||||
//take!(ClipCommand |state: Arrangement, iter|state.selected_clip().as_ref()
|
//take!(ClipCommand |state: Arrangement, iter|state.selected_clip().as_ref()
|
||||||
//.map(|t|Take::take(t, iter)).transpose().map(|x|x.flatten()));
|
//.map(|t|Take::take(t, iter)).transpose().map(|x|x.flatten()));
|
||||||
|
|
||||||
impl<T: Has<Clock>> HasClock for T {
|
impl<T: AsRef<Clock> + AsMut<Clock>> HasClock for T {}
|
||||||
fn clock (&self) -> &Clock { self.get() }
|
|
||||||
fn clock_mut (&mut self) -> &mut Clock { self.get_mut() }
|
|
||||||
}
|
|
||||||
|
|
||||||
impl std::fmt::Debug for Clock {
|
impl std::fmt::Debug for Clock {
|
||||||
fn fmt (&self, f: &mut Formatter<'_>) -> std::result::Result<(), std::fmt::Error> {
|
fn fmt (&self, f: &mut Formatter<'_>) -> std::result::Result<(), std::fmt::Error> {
|
||||||
|
|
@ -1030,9 +1023,9 @@ impl ClockView {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
impl<T: Has<Option<MidiEditor>>> HasEditor for T {}
|
impl<T: AsRef<Option<MidiEditor>>> HasEditor for T {}
|
||||||
|
|
||||||
has!(Measure<TuiOut>: |self: MidiEditor|self.size);
|
impl_has!(Measure<TuiOut>: |self: MidiEditor|self.size);
|
||||||
|
|
||||||
|
|
||||||
impl std::fmt::Debug for MidiEditor {
|
impl std::fmt::Debug for MidiEditor {
|
||||||
|
|
@ -1200,7 +1193,7 @@ impl HasContent<TuiOut> for MidiEditor {
|
||||||
fn content (&self) -> impl Content<TuiOut> { self.autoscroll(); /*self.autozoom();*/ self.size.of(&self.mode) }
|
fn content (&self) -> impl Content<TuiOut> { self.autoscroll(); /*self.autozoom();*/ self.size.of(&self.mode) }
|
||||||
}
|
}
|
||||||
|
|
||||||
has!(Measure<TuiOut>:|self:PianoHorizontal|self.size);
|
impl_has!(Measure<TuiOut>:|self:PianoHorizontal|self.size);
|
||||||
|
|
||||||
impl PianoHorizontal {
|
impl PianoHorizontal {
|
||||||
pub fn new (clip: Option<&Arc<RwLock<MidiClip>>>) -> Self {
|
pub fn new (clip: Option<&Arc<RwLock<MidiClip>>>) -> Self {
|
||||||
|
|
@ -1634,7 +1627,7 @@ impl Selection {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
impl<T: Has<Selection>> HasSelection for T {}
|
impl<T: AsRef<Selection> + AsMut<Selection>> HasSelection for T {}
|
||||||
|
|
||||||
/// Default is always empty map regardless if `E` and `C` implement [Default].
|
/// Default is always empty map regardless if `E` and `C` implement [Default].
|
||||||
impl<E, C> Default for Bind<E, C> {
|
impl<E, C> Default for Bind<E, C> {
|
||||||
|
|
@ -1733,43 +1726,24 @@ impl<J: HasJack<'static>> RegisterPorts for J {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "scene")] impl<T: HasScenes + HasTracks> AddScene for T {}
|
#[cfg(feature = "clock")] impl_has!(Clock: |self: App|self.project.clock);
|
||||||
#[cfg(feature = "scene")] impl<T: Has<Option<Scene>> + Send + Sync> HasScene for T {}
|
#[cfg(feature = "clock")] impl_has!(Clock: |self: Track|self.sequencer.clock);
|
||||||
#[cfg(feature = "scene")] impl<T: Has<Vec<Scene>> + Send + Sync> HasScenes for T {}
|
#[cfg(feature = "clock")] impl_has!(Clock: |self: Sequencer|self.clock);
|
||||||
#[cfg(feature = "scene")] impl HasSceneScroll for App { fn scene_scroll (&self) -> usize { self.project.scene_scroll() } }
|
|
||||||
#[cfg(feature = "scene")] impl HasSceneScroll for Arrangement { fn scene_scroll (&self) -> usize { self.scene_scroll } }
|
|
||||||
#[cfg(feature = "scene")] has!(Vec<Scene>: |self: App|self.project.scenes);
|
|
||||||
#[cfg(feature = "scene")] maybe_has!(Scene: |self: App|
|
|
||||||
{ MaybeHas::<Scene>::get(&self.project) };
|
|
||||||
{ MaybeHas::<Scene>::get_mut(&mut self.project) });
|
|
||||||
|
|
||||||
#[cfg(feature = "track")] impl<T: Has<Vec<Track>> + Send + Sync> HasTracks for T {}
|
#[cfg(feature = "port")] impl_has!(Vec<MidiInput>: |self: App|self.project.midi_ins);
|
||||||
#[cfg(feature = "track")] impl HasTrackScroll for App { fn track_scroll (&self) -> usize { self.project.track_scroll() } }
|
#[cfg(feature = "port")] impl_has!(Vec<MidiOutput>: |self: App|self.project.midi_outs);
|
||||||
#[cfg(feature = "track")] impl HasTrackScroll for Arrangement { fn track_scroll (&self) -> usize { self.track_scroll } }
|
#[cfg(feature = "port")] impl_has!(Vec<MidiInput>: |self: Sequencer|self.midi_ins);
|
||||||
#[cfg(feature = "track")] has!(Vec<Track>: |self: App|self.project.tracks);
|
#[cfg(feature = "port")] impl_has!(Vec<MidiOutput>: |self: Sequencer|self.midi_outs);
|
||||||
#[cfg(feature = "track")] maybe_has!(Track: |self: App|
|
|
||||||
{ MaybeHas::<Track>::get(&self.project) };
|
|
||||||
{ MaybeHas::<Track>::get_mut(&mut self.project) });
|
|
||||||
|
|
||||||
#[cfg(feature = "clock")] has!(Clock: |self: App|self.project.clock);
|
|
||||||
#[cfg(feature = "clock")] has!(Clock: |self: Track|self.sequencer.clock);
|
|
||||||
#[cfg(feature = "clock")] has!(Clock: |self: Sequencer|self.clock);
|
|
||||||
|
|
||||||
#[cfg(feature = "port")] has!(Vec<MidiInput>: |self: App|self.project.midi_ins);
|
|
||||||
#[cfg(feature = "port")] has!(Vec<MidiOutput>: |self: App|self.project.midi_outs);
|
|
||||||
#[cfg(feature = "port")] has!(Vec<MidiInput>: |self: Sequencer|self.midi_ins);
|
|
||||||
#[cfg(feature = "port")] has!(Vec<MidiOutput>: |self: Sequencer|self.midi_outs);
|
|
||||||
|
|
||||||
audio!(App: tek_jack_process, tek_jack_event);
|
audio!(App: tek_jack_process, tek_jack_event);
|
||||||
audio!(Sampler: sampler_jack_process);
|
|
||||||
|
|
||||||
has!(Jack<'static>: |self: App|self.jack);
|
impl_has!(Jack<'static>: |self: App|self.jack);
|
||||||
has!(Dialog: |self: App|self.dialog);
|
impl_has!(Dialog: |self: App|self.dialog);
|
||||||
has!(Measure<TuiOut>: |self: App|self.size);
|
impl_has!(Measure<TuiOut>: |self: App|self.size);
|
||||||
has!(Option<MidiEditor>: |self: App|self.project.editor);
|
impl_has!(Option<MidiEditor>: |self: App|self.project.editor);
|
||||||
has!(Pool: |self: App|self.pool);
|
impl_has!(Pool: |self: App|self.pool);
|
||||||
has!(Selection: |self: App|self.project.selection);
|
impl_has!(Selection: |self: App|self.project.selection);
|
||||||
has!(Sequencer: |self: Track|self.sequencer);
|
impl_has!(Sequencer: |self: Track|self.sequencer);
|
||||||
has_clips!( |self: App|self.pool.clips);
|
has_clips!( |self: App|self.pool.clips);
|
||||||
|
|
||||||
impl_debug!(MenuItem |self, w| { write!(w, "{}", &self.0) });
|
impl_debug!(MenuItem |self, w| { write!(w, "{}", &self.0) });
|
||||||
|
|
@ -2433,11 +2407,11 @@ mod midi {
|
||||||
parse_midi_input(self.port().iter(scope))
|
parse_midi_input(self.port().iter(scope))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl<T: Has<Vec<MidiInput>>> HasMidiIns for T {
|
impl<T: AsRef<Vec<MidiInput>>> HasMidiIns for T {
|
||||||
fn midi_ins (&self) -> &Vec<MidiInput> { self.get() }
|
fn midi_ins (&self) -> &Vec<MidiInput> { self.get() }
|
||||||
fn midi_ins_mut (&mut self) -> &mut Vec<MidiInput> { self.get_mut() }
|
fn midi_ins_mut (&mut self) -> &mut Vec<MidiInput> { self.get_mut() }
|
||||||
}
|
}
|
||||||
impl<T: Has<Vec<MidiOutput>>> HasMidiOuts for T {
|
impl<T: AsRef<Vec<MidiOutput>>> HasMidiOuts for T {
|
||||||
fn midi_outs (&self) -> &Vec<MidiOutput> { self.get() }
|
fn midi_outs (&self) -> &Vec<MidiOutput> { self.get() }
|
||||||
fn midi_outs_mut (&mut self) -> &mut Vec<MidiOutput> { self.get_mut() }
|
fn midi_outs_mut (&mut self) -> &mut Vec<MidiOutput> { self.get_mut() }
|
||||||
}
|
}
|
||||||
|
|
@ -2653,13 +2627,24 @@ mod audio {
|
||||||
#[cfg(feature = "track")] mod track {
|
#[cfg(feature = "track")] mod track {
|
||||||
use crate::*;
|
use crate::*;
|
||||||
|
|
||||||
|
impl_as_ref!(Vec<Track>: |self: App| self.project.as_ref());
|
||||||
|
impl_as_mut!(Vec<Track>: |self: App| self.project.as_mut());
|
||||||
|
impl_as_ref!(Vec<Track>: |self: Arrangement| self.tracks);
|
||||||
|
impl_as_mut!(Vec<Track>: |self: Arrangement| self.tracks);
|
||||||
|
|
||||||
|
#[cfg(all(feature = "select"))] impl_as_ref!(Option<Track>: |self: App| self.project.as_ref());
|
||||||
|
#[cfg(all(feature = "select"))] impl_as_mut!(Option<Track>: |self: App| self.project.as_mut());
|
||||||
|
|
||||||
|
impl HasTrackScroll for App { fn track_scroll (&self) -> usize { self.project.track_scroll() } }
|
||||||
|
impl HasTrackScroll for Arrangement { fn track_scroll (&self) -> usize { self.track_scroll } }
|
||||||
|
|
||||||
impl HasWidth for Track {
|
impl HasWidth for Track {
|
||||||
const MIN_WIDTH: usize = 9;
|
const MIN_WIDTH: usize = 9;
|
||||||
fn width_inc (&mut self) { self.width += 1; }
|
fn width_inc (&mut self) { self.width += 1; }
|
||||||
fn width_dec (&mut self) { if self.width > Track::MIN_WIDTH { self.width -= 1; } }
|
fn width_dec (&mut self) { if self.width > Track::MIN_WIDTH { self.width -= 1; } }
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: MaybeHas<Track>> HasTrack for T {
|
impl<T: AsRef<Option<Track>>> HasTrack for T {
|
||||||
fn track (&self) -> Option<&Track> { self.get() }
|
fn track (&self) -> Option<&Track> { self.get() }
|
||||||
fn track_mut (&mut self) -> Option<&mut Track> { self.get_mut() }
|
fn track_mut (&mut self) -> Option<&mut Track> { self.get_mut() }
|
||||||
}
|
}
|
||||||
|
|
@ -2744,11 +2729,48 @@ mod audio {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn per_track <'a, T: Content<TuiOut> + 'a, U: TracksSizes<'a>> (
|
||||||
|
tracks: impl Fn() -> U + Send + Sync + 'a,
|
||||||
|
callback: impl Fn(usize, &'a Track)->T + Send + Sync + 'a
|
||||||
|
) -> impl Content<TuiOut> + 'a {
|
||||||
|
per_track_top(tracks, move|index, track|Fill::Y(Align::y(callback(index, track))))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn per_track_top <'a, T: Content<TuiOut> + 'a, U: TracksSizes<'a>> (
|
||||||
|
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))))})))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "scene")] mod scene {
|
#[cfg(feature = "scene")] mod scene {
|
||||||
use crate::*;
|
use crate::*;
|
||||||
|
|
||||||
|
impl<T: HasScenes + HasTracks> AddScene for T {}
|
||||||
|
impl<T: AsRef<Vec<Scene>> + AsMut<Vec<Scene>> + Send + Sync> HasScenes for T {}
|
||||||
|
impl<T: AsRef<Option<Scene>> + AsMut<Option<Scene>> + Send + Sync> HasScene for T {}
|
||||||
|
|
||||||
|
impl_as_ref!(Vec<Scene>: |self: App| self.project.as_ref());
|
||||||
|
impl_as_mut!(Vec<Scene>: |self: App| self.project.as_mut());
|
||||||
|
impl_as_ref!(Vec<Scene>: |self: Arrangement| self.scenes.as_ref());
|
||||||
|
impl_as_mut!(Vec<Scene>: |self: Arrangement| self.scenes.as_mut());
|
||||||
|
#[cfg(all(feature = "select"))] impl_as_ref!(Option<Scene>: |self: App| self.project.as_ref());
|
||||||
|
#[cfg(all(feature = "select"))] impl_as_mut!(Option<Scene>: |self: App| self.project.as_mut());
|
||||||
|
#[cfg(all(feature = "select"))] impl_as_ref!(Option<Scene>: |self: Arrangement| self.selected.scene());
|
||||||
|
#[cfg(all(feature = "select"))] impl_as_mut!(Option<Scene>: |self: Arrangement| self.selected.scene());
|
||||||
|
|
||||||
|
impl HasSceneScroll for App { fn scene_scroll (&self) -> usize { self.project.scene_scroll() } }
|
||||||
|
impl HasSceneScroll for Arrangement { fn scene_scroll (&self) -> usize { self.scene_scroll } }
|
||||||
|
|
||||||
|
|
||||||
impl ScenesView for App {
|
impl ScenesView for App {
|
||||||
fn w_mid (&self) -> u16 { (self.measure_width() as u16).saturating_sub(self.w_side()) }
|
fn w_mid (&self) -> u16 { (self.measure_width() as u16).saturating_sub(self.w_side()) }
|
||||||
fn w_side (&self) -> u16 { 20 }
|
fn w_side (&self) -> u16 { 20 }
|
||||||
|
|
@ -2792,10 +2814,9 @@ mod audio {
|
||||||
|
|
||||||
#[cfg(feature = "sequencer")] mod sequencer {
|
#[cfg(feature = "sequencer")] mod sequencer {
|
||||||
use crate::*;
|
use crate::*;
|
||||||
impl<T: Has<Sequencer>> HasSequencer for T {
|
|
||||||
fn sequencer (&self) -> &Sequencer { self.get() }
|
impl<T: AsRef<Sequencer> + AsMut<Sequencer>> HasSequencer for T {}
|
||||||
fn sequencer_mut (&mut self) -> &mut Sequencer { self.get_mut() }
|
|
||||||
}
|
|
||||||
impl Default for Sequencer {
|
impl Default for Sequencer {
|
||||||
fn default () -> Self {
|
fn default () -> Self {
|
||||||
Self {
|
Self {
|
||||||
|
|
@ -2816,10 +2837,12 @@ mod audio {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl HasMidiBuffers for Sequencer {
|
impl HasMidiBuffers for Sequencer {
|
||||||
fn note_buf_mut (&mut self) -> &mut Vec<u8> { &mut self.note_buf }
|
fn note_buf_mut (&mut self) -> &mut Vec<u8> { &mut self.note_buf }
|
||||||
fn midi_buf_mut (&mut self) -> &mut Vec<Vec<Vec<u8>>> { &mut self.midi_buf }
|
fn midi_buf_mut (&mut self) -> &mut Vec<Vec<Vec<u8>>> { &mut self.midi_buf }
|
||||||
}
|
}
|
||||||
|
|
||||||
impl std::fmt::Debug for Sequencer {
|
impl std::fmt::Debug for Sequencer {
|
||||||
fn fmt (&self, f: &mut Formatter<'_>) -> std::result::Result<(), std::fmt::Error> {
|
fn fmt (&self, f: &mut Formatter<'_>) -> std::result::Result<(), std::fmt::Error> {
|
||||||
f.debug_struct("Sequencer")
|
f.debug_struct("Sequencer")
|
||||||
|
|
@ -2829,6 +2852,7 @@ mod audio {
|
||||||
.finish()
|
.finish()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl MidiMonitor for Sequencer {
|
impl MidiMonitor for Sequencer {
|
||||||
fn notes_in (&self) -> &Arc<RwLock<[bool; 128]>> {
|
fn notes_in (&self) -> &Arc<RwLock<[bool; 128]>> {
|
||||||
&self.notes_in
|
&self.notes_in
|
||||||
|
|
@ -2840,6 +2864,7 @@ mod audio {
|
||||||
&mut self.monitoring
|
&mut self.monitoring
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl MidiRecord for Sequencer {
|
impl MidiRecord for Sequencer {
|
||||||
fn recording (&self) -> bool {
|
fn recording (&self) -> bool {
|
||||||
self.recording
|
self.recording
|
||||||
|
|
@ -2854,6 +2879,7 @@ mod audio {
|
||||||
&mut self.overdub
|
&mut self.overdub
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature="clip")] impl HasPlayClip for Sequencer {
|
#[cfg(feature="clip")] impl HasPlayClip for Sequencer {
|
||||||
fn reset (&self) -> bool {
|
fn reset (&self) -> bool {
|
||||||
self.reset
|
self.reset
|
||||||
|
|
@ -2874,6 +2900,7 @@ mod audio {
|
||||||
&mut self.next_clip
|
&mut self.next_clip
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// JACK process callback for a sequencer's clip sequencer/recorder.
|
/// JACK process callback for a sequencer's clip sequencer/recorder.
|
||||||
impl Audio for Sequencer {
|
impl Audio for Sequencer {
|
||||||
fn process (&mut self, _: &Client, scope: &ProcessScope) -> Control {
|
fn process (&mut self, _: &Client, scope: &ProcessScope) -> Control {
|
||||||
|
|
@ -2884,6 +2911,7 @@ mod audio {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Sequencer {
|
impl Sequencer {
|
||||||
pub fn new (
|
pub fn new (
|
||||||
name: impl AsRef<str>,
|
name: impl AsRef<str>,
|
||||||
|
|
@ -3536,6 +3564,55 @@ mod audio {
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
audio!(Sampler: sampler_jack_process);
|
||||||
|
pub(crate) fn sampler_jack_process (state: &mut Sampler, _: &Client, scope: &ProcessScope) -> Control {
|
||||||
|
if let Some(midi_in) = &state.midi_in {
|
||||||
|
for midi in midi_in.port().iter(scope) {
|
||||||
|
sampler_midi_in(&state.samples, &state.voices, midi)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
state.process_audio_out(scope);
|
||||||
|
state.process_audio_in(scope);
|
||||||
|
Control::Continue
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Create [Voice]s from [Sample]s in response to MIDI input.
|
||||||
|
fn sampler_midi_in (
|
||||||
|
samples: &SampleKit<128>, voices: &Arc<RwLock<Vec<Voice>>>, RawMidi { time, bytes }: RawMidi
|
||||||
|
) {
|
||||||
|
if let Ok(LiveEvent::Midi { message, .. }) = LiveEvent::parse(bytes) {
|
||||||
|
match message {
|
||||||
|
MidiMessage::NoteOn { ref key, ref vel } => {
|
||||||
|
if let Some(sample) = samples.get(key.as_int() as usize) {
|
||||||
|
voices.write().unwrap().push(Sample::play(sample, time as usize, vel));
|
||||||
|
}
|
||||||
|
},
|
||||||
|
MidiMessage::Controller { controller: _, value: _ } => {
|
||||||
|
// TODO
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn draw_sample (
|
||||||
|
to: &mut TuiOut, x: u16, y: u16, note: Option<&u7>, sample: &Sample, focus: bool
|
||||||
|
) -> Usually<usize> {
|
||||||
|
let style = if focus { Style::default().green() } else { Style::default() };
|
||||||
|
if focus {
|
||||||
|
to.blit(&"🬴", x+1, y, Some(style.bold()));
|
||||||
|
}
|
||||||
|
let label1 = format!("{:3} {:12}",
|
||||||
|
note.map(|n|n.to_string()).unwrap_or(String::default()),
|
||||||
|
sample.name);
|
||||||
|
let label2 = format!("{:>6} {:>6} +0.0",
|
||||||
|
sample.start,
|
||||||
|
sample.end);
|
||||||
|
to.blit(&label1, x+2, y, Some(style.bold()));
|
||||||
|
to.blit(&label2, x+3+label1.len()as u16, y, Some(style));
|
||||||
|
Ok(label1.len() + label2.len() + 4)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "lv2")] mod lv2 {
|
#[cfg(feature = "lv2")] mod lv2 {
|
||||||
|
|
@ -3680,6 +3757,17 @@ mod audio {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fn draw_header (state: &Lv2, to: &mut TuiOut, x: u16, y: u16, w: u16) {
|
||||||
|
let style = Style::default().gray();
|
||||||
|
let label1 = format!(" {}", state.name);
|
||||||
|
to.blit(&label1, x + 1, y, Some(style.white().bold()));
|
||||||
|
if let Some(ref path) = state.path {
|
||||||
|
let label2 = format!("{}…", &path[..((w as usize - 10).min(path.len()))]);
|
||||||
|
to.blit(&label2, x + 2 + label1.len() as u16, y, Some(style.not_dim()));
|
||||||
|
}
|
||||||
|
//Ok(Rect { x, y, width: w, height: 1 })
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
mod pool {
|
mod pool {
|
||||||
|
|
|
||||||
|
|
@ -115,10 +115,6 @@ pub trait JackPerfModel {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
//pub trait MaybeHas<T>: Send + Sync {
|
|
||||||
//fn get (&self) -> Option<&T>;
|
|
||||||
//}
|
|
||||||
|
|
||||||
pub trait HasN<T>: Send + Sync {
|
pub trait HasN<T>: Send + Sync {
|
||||||
fn get_nth (&self, key: usize) -> &T;
|
fn get_nth (&self, key: usize) -> &T;
|
||||||
fn get_nth_mut (&mut self, key: usize) -> &mut T;
|
fn get_nth_mut (&mut self, key: usize) -> &mut T;
|
||||||
|
|
@ -242,37 +238,44 @@ pub trait MidiRange: TimeRange + NoteRange {}
|
||||||
/// can be clocked in microseconds with f64 without losing precision.
|
/// can be clocked in microseconds with f64 without losing precision.
|
||||||
pub trait TimeUnit: InteriorMutable<f64> {}
|
pub trait TimeUnit: InteriorMutable<f64> {}
|
||||||
|
|
||||||
pub trait HasSceneScroll: HasScenes {
|
pub trait HasClipsSize { fn clips_size (&self) -> &Measure<TuiOut>; }
|
||||||
fn scene_scroll (&self) -> usize;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub trait HasTrackScroll: HasTracks {
|
|
||||||
fn track_scroll (&self) -> usize;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub trait HasMidiClip {
|
pub trait HasMidiClip {
|
||||||
fn clip (&self) -> Option<Arc<RwLock<MidiClip>>>;
|
fn clip (&self) -> Option<Arc<RwLock<MidiClip>>>;
|
||||||
}
|
}
|
||||||
|
pub trait HasClock: AsRef<Clock> + AsMut<Clock> {
|
||||||
pub trait HasClipsSize {
|
fn clock_mut (&mut self) -> &mut Clock { self.as_mut() }
|
||||||
fn clips_size (&self) -> &Measure<TuiOut>;
|
fn clock (&self) -> &Clock { self.as_ref() }
|
||||||
}
|
}
|
||||||
|
pub trait HasDevices: AsRef<Vec<Device>> + AsMut<Vec<Device>> {
|
||||||
pub trait HasClock: Send + Sync {
|
fn devices_mut (&mut self) -> &mut Vec<Device> { self.as_mut() }
|
||||||
fn clock (&self) -> &Clock;
|
fn devices (&self) -> &Vec<Device> { self.as_reF() }
|
||||||
fn clock_mut (&mut self) -> &mut Clock;
|
|
||||||
}
|
}
|
||||||
|
pub trait HasSelection: AsRef<Selection> + AsMut<Selection> {
|
||||||
pub trait HasDevices {
|
fn selection_mut (&mut self) -> &mut Selection { self.as_mut() }
|
||||||
fn devices (&self) -> &Vec<Device>;
|
fn selection (&self) -> &Selection { self.as_ref() }
|
||||||
fn devices_mut (&mut self) -> &mut Vec<Device>;
|
|
||||||
}
|
}
|
||||||
|
pub trait HasSequencer: AsRef<Sequencer> + AsMut<Sequencer> {
|
||||||
pub trait HasSelection: Has<Selection> {
|
fn sequencer_mut (&mut self) -> &mut Sequencer { self.as_mut() }
|
||||||
fn selection (&self) -> &Selection { self.get() }
|
fn sequencer (&self) -> &Sequencer { self.as_ref() }
|
||||||
fn selection_mut (&mut self) -> &mut Selection { self.get_mut() }
|
}
|
||||||
|
pub trait HasScene: AsRef<Option<Scene>> + AsMut<Option<Scene>> {
|
||||||
|
fn scene_mut (&mut self) -> &mut Option<Scene> { self.as_mut() }
|
||||||
|
fn scene (&self) -> Option<&Scene> { self.as_ref() }
|
||||||
|
}
|
||||||
|
pub trait HasScenes: AsRef<Vec<Scene>> + AsMut<Vec<Scene>> {
|
||||||
|
fn scenes (&self) -> &Vec<Scene> { self.as_reF() }
|
||||||
|
fn scenes_mut (&mut self) -> &mut Vec<Scene> { self.as_mut() }
|
||||||
|
/// Generate the default name for a new scene
|
||||||
|
fn scene_default_name (&self) -> Arc<str> { format!("s{:3>}", self.scenes().len() + 1).into() }
|
||||||
|
fn scene_longest_name (&self) -> usize { self.scenes().iter().map(|s|s.name.len()).fold(0, usize::max) }
|
||||||
|
}
|
||||||
|
pub trait HasSceneScroll: HasScenes {
|
||||||
|
fn scene_scroll (&self) -> usize;
|
||||||
|
}
|
||||||
|
pub trait HasTrackScroll: HasTracks {
|
||||||
|
fn track_scroll (&self) -> usize;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait HasWidth {
|
pub trait HasWidth {
|
||||||
const MIN_WIDTH: usize;
|
const MIN_WIDTH: usize;
|
||||||
/// Increment track width.
|
/// Increment track width.
|
||||||
|
|
@ -280,48 +283,17 @@ pub trait HasWidth {
|
||||||
/// Decrement track width, down to a hardcoded minimum of [Self::MIN_WIDTH].
|
/// Decrement track width, down to a hardcoded minimum of [Self::MIN_WIDTH].
|
||||||
fn width_dec (&mut self);
|
fn width_dec (&mut self);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait HasMidiBuffers {
|
pub trait HasMidiBuffers {
|
||||||
fn note_buf_mut (&mut self) -> &mut Vec<u8>;
|
fn note_buf_mut (&mut self) -> &mut Vec<u8>;
|
||||||
fn midi_buf_mut (&mut self) -> &mut Vec<Vec<Vec<u8>>>;
|
fn midi_buf_mut (&mut self) -> &mut Vec<Vec<Vec<u8>>>;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait HasSequencer {
|
|
||||||
fn sequencer (&self) -> &Sequencer;
|
|
||||||
fn sequencer_mut (&mut self) -> &mut Sequencer;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub trait HasScene: Has<Option<Scene>> + Send + Sync {
|
|
||||||
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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub trait HasScenes: Has<Vec<Scene>> + Send + Sync {
|
|
||||||
fn scenes (&self) -> &Vec<Scene> {
|
|
||||||
Has::<Vec<Scene>>::get(self)
|
|
||||||
}
|
|
||||||
fn scenes_mut (&mut self) -> &mut Vec<Scene> {
|
|
||||||
Has::<Vec<Scene>>::get_mut(self)
|
|
||||||
}
|
|
||||||
/// Generate the default name for a new scene
|
|
||||||
fn scene_default_name (&self) -> Arc<str> {
|
|
||||||
format!("s{:3>}", self.scenes().len() + 1).into()
|
|
||||||
}
|
|
||||||
fn scene_longest_name (&self) -> usize {
|
|
||||||
self.scenes().iter().map(|s|s.name.len()).fold(0, usize::max)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// ```
|
/// ```
|
||||||
/// use tek::{MidiEditor, HasEditor, tengri::Has};
|
/// use tek::{MidiEditor, HasEditor, tengri::Has};
|
||||||
///
|
///
|
||||||
/// let mut host = TestEditorHost(Some(MidiEditor::default()));
|
/// let mut host = TestEditorHost(Some(MidiEditor::default()));
|
||||||
/// struct TestEditorHost(Option<MidiEditor>);
|
/// struct TestEditorHost(Option<MidiEditor>);
|
||||||
/// impl Has<Option<MidiEditor>> for TestEditorHost {
|
/// impl AsRef<Option<MidiEditor>> for TestEditorHost {
|
||||||
/// fn get (&self) -> &Option<MidiEditor> { &self.0 }
|
/// fn get (&self) -> &Option<MidiEditor> { &self.0 }
|
||||||
/// fn get_mut (&mut self) -> &mut Option<MidiEditor> { &mut self.0 }
|
/// fn get_mut (&mut self) -> &mut Option<MidiEditor> { &mut self.0 }
|
||||||
/// }
|
/// }
|
||||||
|
|
@ -332,7 +304,7 @@ pub trait HasScenes: Has<Vec<Scene>> + Send + Sync {
|
||||||
/// let _ = host.editor_w();
|
/// let _ = host.editor_w();
|
||||||
/// let _ = host.editor_h();
|
/// let _ = host.editor_h();
|
||||||
/// ```
|
/// ```
|
||||||
pub trait HasEditor: Has<Option<MidiEditor>> {
|
pub trait HasEditor: AsRef<Option<MidiEditor>> {
|
||||||
fn editor (&self) -> Option<&MidiEditor> {
|
fn editor (&self) -> Option<&MidiEditor> {
|
||||||
self.get().as_ref()
|
self.get().as_ref()
|
||||||
}
|
}
|
||||||
|
|
@ -407,9 +379,10 @@ pub trait HasMidiOuts {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait HasTracks: Has<Vec<Track>> + Send + Sync {
|
impl<T: AsRef<Vec<Track>> + AsMut<Vec<Track>> + Send + Sync> HasTracks for T {}
|
||||||
fn tracks (&self) -> &Vec<Track> { Has::<Vec<Track>>::get(self) }
|
pub trait HasTracks: AsRef<Vec<Track>> + AsMut<Vec<Track>> + Send + Sync {
|
||||||
fn tracks_mut (&mut self) -> &mut Vec<Track> { Has::<Vec<Track>>::get_mut(self) }
|
fn tracks (&self) -> &Vec<Track> { self.as_ref() }
|
||||||
|
fn tracks_mut (&mut self) -> &mut Vec<Track> { self.as_mut() }
|
||||||
/// Run audio callbacks for every track and every device
|
/// Run audio callbacks for every track and every device
|
||||||
fn process_tracks (&mut self, client: &Client, scope: &ProcessScope) -> Control {
|
fn process_tracks (&mut self, client: &Client, scope: &ProcessScope) -> Control {
|
||||||
for track in self.tracks_mut().iter_mut() {
|
for track in self.tracks_mut().iter_mut() {
|
||||||
|
|
|
||||||
2
dizzle
2
dizzle
|
|
@ -1 +1 @@
|
||||||
Subproject commit 7d1fbe3fbe53699a3e12eb5a3d55db79653d72d8
|
Subproject commit 909b94cbd4beffb49be314724dc79db9374bcc99
|
||||||
Loading…
Add table
Add a link
Reference in a new issue