mirror of
https://codeberg.org/unspeaker/tek.git
synced 2025-12-08 04:36:45 +01:00
refactor sampler, flatten arranger
This commit is contained in:
parent
a9d22bd26f
commit
9f70441627
28 changed files with 1816 additions and 1836 deletions
192
crates/sampler/src/sampler_view.rs
Normal file
192
crates/sampler/src/sampler_view.rs
Normal file
|
|
@ -0,0 +1,192 @@
|
|||
use crate::*;
|
||||
|
||||
content!(TuiOut: |self: Sampler| {
|
||||
let keys_width = 5;
|
||||
let keys = move||"";//SamplerKeys(self);
|
||||
let fg = self.color.base.rgb;
|
||||
let bg = self.color.darkest.rgb;
|
||||
let border = Fill::xy(Outer(true, Style::default().fg(fg).bg(bg)));
|
||||
let with_border = |x|lay!(border, Fill::xy(x));
|
||||
let with_size = |x|lay!(self.size.clone(), x);
|
||||
Tui::bg(bg, Fill::xy(with_border(Bsp::s(
|
||||
Tui::fg(self.color.light.rgb, Tui::bold(true, &"Sampler")),
|
||||
with_size(Shrink::y(1, Bsp::e(
|
||||
Fixed::x(keys_width, keys()),
|
||||
Fill::xy(SamplesTui {
|
||||
color: self.color,
|
||||
note_hi: self.note_hi(),
|
||||
note_pt: self.note_pos(),
|
||||
height: self.size.h(),
|
||||
}),
|
||||
))),
|
||||
))))
|
||||
});
|
||||
|
||||
struct SamplesTui {
|
||||
color: ItemPalette,
|
||||
note_hi: usize,
|
||||
note_pt: usize,
|
||||
height: usize,
|
||||
}
|
||||
|
||||
render!(TuiOut: |self: SamplesTui, to| {
|
||||
let x = to.area.x();
|
||||
let bg_base = self.color.darkest.rgb;
|
||||
let bg_selected = self.color.darker.rgb;
|
||||
let style_empty = Style::default().fg(self.color.base.rgb);
|
||||
let style_full = Style::default().fg(self.color.lighter.rgb);
|
||||
for y in 0..self.height {
|
||||
let note = self.note_hi - y as usize;
|
||||
let bg = if note == self.note_pt { bg_selected } else { bg_base };
|
||||
let style = Some(style_empty.bg(bg));
|
||||
to.blit(&" (no sample) ", x, to.area.y() + y as u16, style);
|
||||
}
|
||||
});
|
||||
|
||||
impl NoteRange for Sampler {
|
||||
fn note_lo (&self) -> &AtomicUsize { &self.note_lo }
|
||||
fn note_axis (&self) -> &AtomicUsize { &self.size.y }
|
||||
}
|
||||
|
||||
impl NotePoint for Sampler {
|
||||
fn note_len (&self) -> usize {0/*TODO*/}
|
||||
fn set_note_len (&self, x: usize) {}
|
||||
fn note_pos (&self) -> usize { self.note_pt.load(Relaxed) }
|
||||
fn set_note_pos (&self, x: usize) { self.note_pt.store(x, Relaxed); }
|
||||
}
|
||||
|
||||
impl Sampler {
|
||||
const _EMPTY: &[(f64, f64)] = &[(0., 0.), (1., 1.), (2., 2.), (0., 2.), (2., 0.)];
|
||||
pub fn list <'a> (&'a self, compact: bool, editor: &MidiEditor) -> impl Content<TuiOut> + 'a {
|
||||
let note_lo = editor.note_lo().load(Relaxed);
|
||||
let note_pt = editor.note_pos();
|
||||
let note_hi = editor.note_hi();
|
||||
Outer(true, Style::default().fg(Tui::g(96))).enclose(Map::new(
|
||||
move||(note_lo..=note_hi).rev(),
|
||||
move|note, i| {
|
||||
let offset = |a|Push::y(i as u16, Align::n(Fixed::y(1, Fill::x(a))));
|
||||
let mut bg = if note == note_pt { Tui::g(64) } else { Color::Reset };
|
||||
let mut fg = Tui::g(160);
|
||||
let mapped: &Option<Arc<RwLock<Sample>>> = &self.mapped[note];
|
||||
if mapped.is_some() {
|
||||
fg = Tui::g(224);
|
||||
bg = Color::Rgb(0, if note == note_pt { 96 } else { 64 }, 0);
|
||||
}
|
||||
if let Some((index, _)) = self.recording {
|
||||
if note == index {
|
||||
bg = if note == note_pt { Color::Rgb(96,24,0) } else { Color::Rgb(64,16,0) };
|
||||
fg = Color::Rgb(224,64,32)
|
||||
}
|
||||
}
|
||||
offset(Tui::fg_bg(fg, bg, format!("{note:3} {}", self.list_item(note, compact))))
|
||||
}))
|
||||
}
|
||||
pub fn list_item (&self, note: usize, compact: bool) -> String {
|
||||
if compact {
|
||||
String::default()
|
||||
} else {
|
||||
draw_list_item(&self.mapped[note])
|
||||
}
|
||||
}
|
||||
pub fn viewer (&self, note_pt: usize) -> impl Content<TuiOut> + use<'_> {
|
||||
draw_viewer(if let Some((_, sample)) = &self.recording {
|
||||
Some(sample)
|
||||
} else if let Some(sample) = &self.mapped[note_pt] {
|
||||
Some(sample)
|
||||
} else {
|
||||
None
|
||||
})
|
||||
}
|
||||
pub fn status (&self, index: usize) -> impl Content<TuiOut> {
|
||||
draw_status(self.mapped[index].as_ref())
|
||||
}
|
||||
}
|
||||
|
||||
fn draw_list_item (sample: &Option<Arc<RwLock<Sample>>>) -> String {
|
||||
if let Some(sample) = sample {
|
||||
let sample = sample.read().unwrap();
|
||||
format!("{:8} {:3} {:6}-{:6}/{:6}",
|
||||
sample.name,
|
||||
sample.gain,
|
||||
sample.start,
|
||||
sample.end,
|
||||
sample.channels[0].len()
|
||||
)
|
||||
} else {
|
||||
String::from("(none)")
|
||||
}
|
||||
}
|
||||
|
||||
fn draw_viewer (sample: Option<&Arc<RwLock<Sample>>>) -> impl Content<TuiOut> + use<'_> {
|
||||
let min_db = -40.0;
|
||||
ThunkRender::new(move|to: &mut TuiOut|{
|
||||
let [x, y, width, height] = to.area();
|
||||
let area = Rect { x, y, width, height };
|
||||
let (x_bounds, y_bounds, lines): ([f64;2], [f64;2], Vec<Line>) =
|
||||
if let Some(sample) = &sample {
|
||||
let sample = sample.read().unwrap();
|
||||
let start = sample.start as f64;
|
||||
let end = sample.end as f64;
|
||||
let length = end - start;
|
||||
let step = length / width as f64;
|
||||
let mut t = start;
|
||||
let mut lines = vec![];
|
||||
while t < end {
|
||||
let chunk = &sample.channels[0][t as usize..((t + step) as usize).min(sample.end)];
|
||||
let total: f32 = chunk.iter().map(|x|x.abs()).sum();
|
||||
let count = chunk.len() as f32;
|
||||
let meter = 10. * (total / count).log10();
|
||||
let x = t as f64;
|
||||
let y = meter as f64;
|
||||
lines.push(Line::new(x, min_db, x, y, Color::Green));
|
||||
t += step / 2.;
|
||||
}
|
||||
(
|
||||
[sample.start as f64, sample.end as f64],
|
||||
[min_db, 0.],
|
||||
lines
|
||||
)
|
||||
} else {
|
||||
(
|
||||
[0.0, width as f64],
|
||||
[0.0, height as f64],
|
||||
vec![
|
||||
Line::new(0.0, 0.0, width as f64, height as f64, Color::Red),
|
||||
Line::new(width as f64, 0.0, 0.0, height as f64, Color::Red),
|
||||
]
|
||||
)
|
||||
};
|
||||
Canvas::default()
|
||||
.x_bounds(x_bounds)
|
||||
.y_bounds(y_bounds)
|
||||
.paint(|ctx| { for line in lines.iter() { ctx.draw(line) } })
|
||||
.render(area, &mut to.buffer);
|
||||
})
|
||||
}
|
||||
|
||||
fn draw_status (sample: Option<&Arc<RwLock<Sample>>>) -> impl Content<TuiOut> {
|
||||
Tui::bold(true, Tui::fg(Tui::g(224), sample
|
||||
.map(|sample|{
|
||||
let sample = sample.read().unwrap();
|
||||
format!("Sample {}-{}", sample.start, sample.end)
|
||||
})
|
||||
.unwrap_or_else(||"No sample".to_string())))
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue