mirror of
https://codeberg.org/unspeaker/tek.git
synced 2026-04-03 21:00:44 +02:00
Compare commits
No commits in common. "2d7ca155e02f7beaf91eac5cf28fc63dc9982391" and "86869a1110d4af5edcd01c2fe58201482456dc60" have entirely different histories.
2d7ca155e0
...
86869a1110
13 changed files with 552 additions and 694 deletions
1098
Cargo.lock
generated
1098
Cargo.lock
generated
File diff suppressed because it is too large
Load diff
2
deps/dizzle
vendored
2
deps/dizzle
vendored
|
|
@ -1 +1 @@
|
|||
Subproject commit 5af7c8b09b1469f95073092fb3dc23e636018e64
|
||||
Subproject commit 361874ff72b19d2386f3cb6e8604e673c02203c9
|
||||
2
deps/tengri
vendored
2
deps/tengri
vendored
|
|
@ -1 +1 @@
|
|||
Subproject commit 090546b2d6fba3beb69b8ff65b30e5492d8a7b66
|
||||
Subproject commit d5976cae8e858f717ff051602f7d8570d6eab30d
|
||||
|
|
@ -44,35 +44,30 @@ impl HasJack<'static> for Arrangement {
|
|||
&self.jack
|
||||
}
|
||||
}
|
||||
|
||||
has!(Jack<'static>: |self: Arrangement|self.jack);
|
||||
has!(Measure<TuiOut>: |self: Arrangement|self.size);
|
||||
|
||||
has!(Jack<'static>: |self: Arrangement|self.jack);
|
||||
has!(Measure<TuiOut>: |self: Arrangement|self.size);
|
||||
#[cfg(feature = "editor")] has!(Option<MidiEditor>: |self: Arrangement|self.editor);
|
||||
#[cfg(feature = "port")] has!(Vec<MidiInput>: |self: Arrangement|self.midi_ins);
|
||||
#[cfg(feature = "port")] has!(Vec<MidiOutput>: |self: Arrangement|self.midi_outs);
|
||||
#[cfg(feature = "clock")] has!(Clock: |self: Arrangement|self.clock);
|
||||
#[cfg(feature = "select")] has!(Selection: |self: Arrangement|self.selection);
|
||||
|
||||
#[cfg(feature = "port")] has!(Vec<MidiInput>: |self: Arrangement|self.midi_ins);
|
||||
#[cfg(feature = "port")] has!(Vec<MidiOutput>: |self: Arrangement|self.midi_outs);
|
||||
#[cfg(feature = "clock")] has!(Clock: |self: Arrangement|self.clock);
|
||||
#[cfg(feature = "select")] has!(Selection: |self: Arrangement|self.selection);
|
||||
#[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 = "clip")] fn selected_clip (&self) -> Option<MidiClip> { todo!() }
|
||||
#[cfg(feature = "scene")] fn selected_scene (&self) -> Option<Scene> { todo!() }
|
||||
#[cfg(feature = "track")] fn selected_track (&self) -> Option<Track> { todo!() }
|
||||
#[cfg(feature = "port")] fn selected_midi_in (&self) -> Option<MidiInput> { todo!() }
|
||||
#[cfg(feature = "port")] fn selected_midi_out (&self) -> Option<MidiOutput> { todo!() }
|
||||
fn selected_device (&self) -> Option<Device> {
|
||||
todo!()
|
||||
}
|
||||
#[cfg(feature = "select")]
|
||||
impl Arrangement {
|
||||
#[cfg(feature = "clip")] fn selected_clip (&self) -> Option<MidiClip> { todo!() }
|
||||
#[cfg(feature = "scene")] fn selected_scene (&self) -> Option<Scene> { todo!() }
|
||||
#[cfg(feature = "track")] fn selected_track (&self) -> Option<Track> { todo!() }
|
||||
#[cfg(feature = "port")] fn selected_midi_in (&self) -> Option<MidiInput> { todo!() }
|
||||
#[cfg(feature = "port")] fn selected_midi_out (&self) -> Option<MidiOutput> { todo!() }
|
||||
fn selected_device (&self) -> Option<Device> { todo!() }
|
||||
fn unselect (&self) -> Selection {
|
||||
Selection::Nothing
|
||||
}
|
||||
|
|
@ -314,13 +309,13 @@ impl Arrangement {
|
|||
#[cfg(feature = "scene")]
|
||||
impl ScenesView for Arrangement {
|
||||
fn h_scenes (&self) -> u16 {
|
||||
(self.measure_height() as u16).saturating_sub(20)
|
||||
(self.height() as u16).saturating_sub(20)
|
||||
}
|
||||
fn w_side (&self) -> u16 {
|
||||
(self.measure_width() as u16 * 2 / 10).max(20)
|
||||
(self.width() as u16 * 2 / 10).max(20)
|
||||
}
|
||||
fn w_mid (&self) -> u16 {
|
||||
(self.measure_width() as u16).saturating_sub(2 * self.w_side()).max(40)
|
||||
(self.width() as u16).saturating_sub(2 * self.w_side()).max(40)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -371,8 +366,8 @@ impl Arrangement {
|
|||
}
|
||||
|
||||
pub(crate) fn wrap (bg: Color, fg: Color, content: impl Content<TuiOut>) -> impl Content<TuiOut> {
|
||||
let left = Tui::fg_bg(bg, Reset, Fixed::X(1, Repeat::Y("▐")));
|
||||
let right = Tui::fg_bg(bg, Reset, Fixed::X(1, Repeat::Y("▌")));
|
||||
let left = Tui::fg_bg(bg, Reset, Fixed::X(1, RepeatV("▐")));
|
||||
let right = Tui::fg_bg(bg, Reset, Fixed::X(1, RepeatV("▌")));
|
||||
Bsp::e(left, Bsp::w(right, Tui::fg_bg(fg, bg, content)))
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -53,7 +53,15 @@ impl Browse {
|
|||
files.push((name, format!("📄 {decoded}")));
|
||||
}
|
||||
}
|
||||
Ok(Self { cwd, dirs, files, ..Default::default() })
|
||||
Ok(Self {
|
||||
cwd,
|
||||
dirs,
|
||||
files,
|
||||
filter: "".to_string(),
|
||||
index: 0,
|
||||
scroll: 0,
|
||||
size: Measure::new(),
|
||||
})
|
||||
}
|
||||
|
||||
pub fn len (&self) -> usize {
|
||||
|
|
|
|||
|
|
@ -186,16 +186,16 @@ pub trait ClipsView:
|
|||
let w = if self.selection().track() == Some(track_index)
|
||||
&& let Some(editor) = self.editor ()
|
||||
{
|
||||
(editor.measure_width() as usize).max(24).max(track.width)
|
||||
editor.width().max(24).max(track.width)
|
||||
} else {
|
||||
track.width
|
||||
} as u16;
|
||||
let y = if self.selection().scene() == Some(scene_index)
|
||||
&& let Some(editor) = self.editor ()
|
||||
{
|
||||
(editor.measure_height() as usize).max(12)
|
||||
editor.height().max(12)
|
||||
} else {
|
||||
Self::H_SCENE as usize
|
||||
Self::H_SCENE
|
||||
} as u16;
|
||||
|
||||
to.place(&Fixed::XY(w, y, Bsp::b(
|
||||
|
|
|
|||
|
|
@ -1,23 +1,5 @@
|
|||
use crate::*;
|
||||
|
||||
impl<T: Has<Option<MidiEditor>>> HasEditor for T {}
|
||||
pub trait HasEditor: Has<Option<MidiEditor>> {
|
||||
fn editor (&self) -> Option<&MidiEditor> {
|
||||
self.get().as_ref()
|
||||
}
|
||||
fn editor_mut (&mut self) -> Option<&mut MidiEditor> {
|
||||
self.get_mut().as_mut()
|
||||
}
|
||||
fn is_editing (&self) -> bool {
|
||||
self.editor().is_some()
|
||||
}
|
||||
fn editor_w (&self) -> usize {
|
||||
self.editor().map(|e|e.size.w()).unwrap_or(0) as usize
|
||||
}
|
||||
fn editor_h (&self) -> usize {
|
||||
self.editor().map(|e|e.size.h()).unwrap_or(0) as usize
|
||||
}
|
||||
}
|
||||
#[macro_export] macro_rules! has_editor {
|
||||
(|$self:ident: $Struct:ident|{
|
||||
editor = $e0:expr;
|
||||
|
|
@ -34,6 +16,14 @@ pub trait HasEditor: Has<Option<MidiEditor>> {
|
|||
}
|
||||
};
|
||||
}
|
||||
impl<T: Has<Option<MidiEditor>>> HasEditor for T {}
|
||||
pub trait HasEditor: Has<Option<MidiEditor>> {
|
||||
fn editor (&self) -> Option<&MidiEditor> { self.get().as_ref() }
|
||||
fn editor_mut (&mut self) -> Option<&mut MidiEditor> { self.get_mut().as_mut() }
|
||||
fn is_editing (&self) -> bool { self.editor().is_some() }
|
||||
fn editor_w (&self) -> usize { self.editor().map(|e|e.size.w()).unwrap_or(0) }
|
||||
fn editor_h (&self) -> usize { self.editor().map(|e|e.size.h()).unwrap_or(0) }
|
||||
}
|
||||
|
||||
/// Contains state for viewing and editing a clip
|
||||
pub struct MidiEditor {
|
||||
|
|
@ -44,14 +34,7 @@ pub struct MidiEditor {
|
|||
}
|
||||
|
||||
has!(Measure<TuiOut>: |self: MidiEditor|self.size);
|
||||
impl Default for MidiEditor {
|
||||
fn default () -> Self {
|
||||
Self {
|
||||
size: Measure::new(0, 0),
|
||||
mode: PianoHorizontal::new(None),
|
||||
}
|
||||
}
|
||||
}
|
||||
impl Default for MidiEditor { fn default () -> Self { Self { size: Measure::new(), mode: PianoHorizontal::new(None), } } }
|
||||
impl std::fmt::Debug for MidiEditor {
|
||||
fn fmt (&self, f: &mut Formatter<'_>) -> std::result::Result<(), std::fmt::Error> {
|
||||
f.debug_struct("MidiEditor").field("mode", &self.mode).finish()
|
||||
|
|
@ -187,12 +170,8 @@ impl MidiEditor {
|
|||
}
|
||||
}
|
||||
|
||||
impl Draw<TuiOut> for MidiEditor {
|
||||
fn draw (&self, to: &mut TuiOut) { self.content().draw(to) }
|
||||
}
|
||||
impl Layout<TuiOut> for MidiEditor {
|
||||
fn layout (&self, to: XYWH<u16>) -> XYWH<u16> { self.content().layout(to) }
|
||||
}
|
||||
impl Draw<TuiOut> for MidiEditor { fn draw (&self, to: &mut TuiOut) { self.content().draw(to) } }
|
||||
impl Layout<TuiOut> for MidiEditor { fn layout (&self, to: [u16;4]) -> [u16;4] { self.content().layout(to) } }
|
||||
impl HasContent<TuiOut> for MidiEditor {
|
||||
fn content (&self) -> impl Content<TuiOut> { self.autoscroll(); /*self.autozoom();*/ self.size.of(&self.mode) }
|
||||
}
|
||||
|
|
@ -264,7 +243,7 @@ has!(Measure<TuiOut>:|self:PianoHorizontal|self.size);
|
|||
|
||||
impl PianoHorizontal {
|
||||
pub fn new (clip: Option<&Arc<RwLock<MidiClip>>>) -> Self {
|
||||
let size = Measure::new(0, 0);
|
||||
let size = Measure::new();
|
||||
let mut range = MidiRangeModel::from((12, true));
|
||||
range.time_axis = size.x.clone();
|
||||
range.note_axis = size.y.clone();
|
||||
|
|
@ -288,12 +267,8 @@ pub(crate) fn note_y_iter (note_lo: usize, note_hi: usize, y0: u16)
|
|||
(note_lo..=note_hi).rev().enumerate().map(move|(y, n)|(y, y0 + y as u16, n))
|
||||
}
|
||||
|
||||
impl Draw<TuiOut> for PianoHorizontal {
|
||||
fn draw (&self, to: &mut TuiOut) { self.content().draw(to) }
|
||||
}
|
||||
impl Layout<TuiOut> for PianoHorizontal {
|
||||
fn layout (&self, to: XYWH<u16>) -> XYWH<u16> { self.content().layout(to) }
|
||||
}
|
||||
impl Draw<TuiOut> for PianoHorizontal { fn draw (&self, to: &mut TuiOut) { self.content().draw(to) } }
|
||||
impl Layout<TuiOut> for PianoHorizontal { fn layout (&self, to: [u16;4]) -> [u16;4] { self.content().layout(to) } }
|
||||
impl HasContent<TuiOut> for PianoHorizontal {
|
||||
fn content (&self) -> impl Content<TuiOut> {
|
||||
Bsp::s(
|
||||
|
|
@ -382,7 +357,7 @@ impl PianoHorizontal {
|
|||
let buffer = self.buffer.clone();
|
||||
Thunk::new(move|to: &mut TuiOut|{
|
||||
let source = buffer.read().unwrap();
|
||||
let XYWH(x0, y0, w, _h) = to.area();
|
||||
let [x0, y0, w, _h] = to.area().xywh();
|
||||
//if h as usize != note_axis {
|
||||
//panic!("area height mismatch: {h} <> {note_axis}");
|
||||
//}
|
||||
|
|
@ -416,7 +391,7 @@ impl PianoHorizontal {
|
|||
let time_zoom = self.get_time_zoom();
|
||||
let style = Some(Style::default().fg(self.color.lightest.rgb));
|
||||
Thunk::new(move|to: &mut TuiOut|{
|
||||
let XYWH(x0, y0, w, _) = to.area();
|
||||
let [x0, y0, w, _] = to.area().xywh();
|
||||
for (_area_y, screen_y, note) in note_y_iter(note_lo, note_hi, y0) {
|
||||
if note == note_pos {
|
||||
for x in 0..w {
|
||||
|
|
@ -447,7 +422,7 @@ impl PianoHorizontal {
|
|||
let off_style = Some(Style::default().fg(Tui::g(255)));
|
||||
let on_style = Some(Style::default().fg(Rgb(255,0,0)).bg(color.base.rgb).bold());
|
||||
Fill::Y(Fixed::X(self.keys_width, Thunk::new(move|to: &mut TuiOut|{
|
||||
let XYWH(x, y0, _w, _h) = to.area();
|
||||
let [x, y0, _w, _h] = to.area().xywh();
|
||||
for (_area_y, screen_y, note) in note_y_iter(note_lo, note_hi, y0) {
|
||||
to.blit(&to_key(note), x, screen_y, key_style);
|
||||
if note > 127 {
|
||||
|
|
@ -463,7 +438,7 @@ impl PianoHorizontal {
|
|||
}
|
||||
fn timeline (&self) -> impl Content<TuiOut> + '_ {
|
||||
Fill::X(Fixed::Y(1, Thunk::new(move|to: &mut TuiOut|{
|
||||
let XYWH(x, y, w, _h) = to.area();
|
||||
let [x, y, w, _h] = to.area();
|
||||
let style = Some(Style::default().dim());
|
||||
let length = self.clip.as_ref().map(|p|p.read().unwrap().length).unwrap_or(1);
|
||||
for (area_x, screen_x) in (0..w).map(|d|(d, d+x)) {
|
||||
|
|
|
|||
|
|
@ -135,7 +135,7 @@ audio!(|self: Lv2, _client, scope|{
|
|||
impl Draw<TuiOut> for Lv2 {
|
||||
fn draw (&self, to: &mut TuiOut) {
|
||||
let area = to.area();
|
||||
let XYWH(x, y, _, height) = area;
|
||||
let [x, y, _, height] = area;
|
||||
let mut width = 20u16;
|
||||
let start = self.selected.saturating_sub((height as usize / 2).saturating_sub(1));
|
||||
let end = start + height as usize - 2;
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ pub struct Log10Meter(pub f32);
|
|||
impl Layout<TuiOut> for Log10Meter {}
|
||||
impl Draw<TuiOut> for Log10Meter {
|
||||
fn draw (&self, to: &mut TuiOut) {
|
||||
let XYWH(x, y, w, h) = to.area();
|
||||
let [x, y, w, h] = to.area();
|
||||
let signal = 100.0 - f32::max(0.0, f32::min(100.0, self.0.abs()));
|
||||
let v = (signal * h as f32 / 100.0).ceil() as u16;
|
||||
let y2 = y + h;
|
||||
|
|
@ -36,7 +36,7 @@ pub struct RmsMeter(pub f32);
|
|||
impl Layout<TuiOut> for RmsMeter {}
|
||||
impl Draw<TuiOut> for RmsMeter {
|
||||
fn draw (&self, to: &mut TuiOut) {
|
||||
let XYWH(x, y, w, h) = to.area();
|
||||
let [x, y, w, h] = to.area();
|
||||
let signal = f32::max(0.0, f32::min(100.0, self.0.abs()));
|
||||
let v = (signal * h as f32).ceil() as u16;
|
||||
let y2 = y + h;
|
||||
|
|
|
|||
|
|
@ -760,9 +760,9 @@ impl Sampler {
|
|||
let lo_fg = Color::Rgb(64, 64, 64);
|
||||
let lo_bg = if y == 7 { Color::Reset } else { tx_bg };
|
||||
Fixed::XY(w, h, Bsp::s(
|
||||
Fixed::Y(1, Tui::fg_bg(hi_fg, hi_bg, Repeat::X(Phat::<()>::LO))),
|
||||
Fixed::Y(1, Tui::fg_bg(hi_fg, hi_bg, RepeatH(Phat::<()>::LO))),
|
||||
Bsp::n(
|
||||
Fixed::Y(1, Tui::fg_bg(lo_fg, lo_bg, Repeat::X(Phat::<()>::HI))),
|
||||
Fixed::Y(1, Tui::fg_bg(lo_fg, lo_bg, RepeatH(Phat::<()>::HI))),
|
||||
Fill::X(Fixed::Y(1, Tui::fg_bg(tx_fg, tx_bg, name))),
|
||||
),
|
||||
))
|
||||
|
|
@ -883,7 +883,7 @@ fn draw_list_item (sample: &Option<Arc<RwLock<Sample>>>) -> String {
|
|||
fn draw_viewer (sample: Option<&Arc<RwLock<Sample>>>) -> impl Content<TuiOut> + use<'_> {
|
||||
let min_db = -64.0;
|
||||
Thunk::new(move|to: &mut TuiOut|{
|
||||
let XYWH(x, y, width, height) = to.area();
|
||||
let [x, y, width, height] = to.area();
|
||||
let area = Rect { x, y, width, height };
|
||||
if let Some(sample) = &sample {
|
||||
let sample = sample.read().unwrap();
|
||||
|
|
|
|||
|
|
@ -108,7 +108,7 @@ pub trait ScenesView:
|
|||
} else {
|
||||
Self::H_SCENE
|
||||
};
|
||||
if y + height <= self.clips_size().h() as usize {
|
||||
if y + height <= self.clips_size().h() {
|
||||
let data = (s, scene, y, y + height);
|
||||
y += height;
|
||||
Some(data)
|
||||
|
|
|
|||
|
|
@ -388,7 +388,7 @@ pub trait MidiRecord: MidiMonitor + HasClock + HasPlayClip {
|
|||
}
|
||||
}
|
||||
|
||||
pub trait MidiViewer: Measured<TuiOut> + MidiRange + MidiPoint + Debug + Send + Sync {
|
||||
pub trait MidiViewer: HasSize<TuiOut> + MidiRange + MidiPoint + Debug + Send + Sync {
|
||||
fn buffer_size (&self, clip: &MidiClip) -> (usize, usize);
|
||||
fn redraw (&self);
|
||||
fn clip (&self) -> &Option<Arc<RwLock<MidiClip>>>;
|
||||
|
|
|
|||
|
|
@ -188,23 +188,23 @@ pub trait TracksView:
|
|||
ScenesView +
|
||||
HasMidiIns +
|
||||
HasMidiOuts +
|
||||
Measured<TuiOut> +
|
||||
HasSize<TuiOut> +
|
||||
HasTrackScroll +
|
||||
HasSelection +
|
||||
HasEditor +
|
||||
HasClipsSize
|
||||
{
|
||||
fn tracks_width_available (&self) -> u16 {
|
||||
(self.measure_width() as u16).saturating_sub(40)
|
||||
(self.width() as u16).saturating_sub(40)
|
||||
}
|
||||
/// Iterate over tracks with their corresponding sizes.
|
||||
fn tracks_with_sizes (&self) -> impl TracksSizes<'_> {
|
||||
let _editor_width = self.editor().map(|e|e.measure_width());
|
||||
let _editor_width = self.editor().map(|e|e.width());
|
||||
let _active_track = self.selection().track();
|
||||
let mut x = 0;
|
||||
self.tracks().iter().enumerate().map_while(move |(index, track)|{
|
||||
let width = track.width.max(8);
|
||||
if x + width < self.clips_size().w() as usize {
|
||||
if x + width < self.clips_size().w() {
|
||||
let data = (index, track, x, x + width);
|
||||
x += width + Self::TRACK_SPACING;
|
||||
Some(data)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue