mirror of
https://codeberg.org/unspeaker/tek.git
synced 2025-12-06 19:56:42 +01:00
fix reexport mess + document modules
This commit is contained in:
parent
7d6bdcca99
commit
317547c6b2
14 changed files with 383 additions and 350 deletions
357
.scratch.rs
Normal file
357
.scratch.rs
Normal file
|
|
@ -0,0 +1,357 @@
|
||||||
|
/// Just a scratchpad
|
||||||
|
|
||||||
|
/// From sequencer:
|
||||||
|
|
||||||
|
//pub fn footer (
|
||||||
|
//buf: &mut Buffer,
|
||||||
|
//area: Rect,
|
||||||
|
//note0: usize,
|
||||||
|
//note: usize,
|
||||||
|
//time0: usize,
|
||||||
|
//time: usize,
|
||||||
|
//time_z: usize,
|
||||||
|
//) {
|
||||||
|
//let Rect { mut x, y, width, height } = area;
|
||||||
|
//buf.set_string(x, y + height, format!("├{}┤", "-".repeat((width - 2).into())),
|
||||||
|
//Style::default().dim());
|
||||||
|
//buf.set_string(x, y + height + 2, format!("├{}┤", "-".repeat((width - 2).into())),
|
||||||
|
//Style::default().dim());
|
||||||
|
//x = x + 2;
|
||||||
|
//{
|
||||||
|
//for (_, [letter, title, value]) in [
|
||||||
|
//["S", &format!("ync"), &format!("<4/4>")],
|
||||||
|
//["Q", &format!("uant"), &format!("<1/{}>", 4 * time_z)],
|
||||||
|
//["N", &format!("ote"), &format!("{} ({}-{})", note0 + note, note0, "X")],
|
||||||
|
//["T", &format!("ime"), &format!("{} ({}-{})", time0 + time, time0 + 1, "X")],
|
||||||
|
//].iter().enumerate() {
|
||||||
|
//buf.set_string(x, y + height + 1, letter, Style::default().bold().yellow().dim());
|
||||||
|
//x = x + 1;
|
||||||
|
//buf.set_string(x, y + height + 1, &title, Style::default().bold().dim());
|
||||||
|
//x = x + title.len() as u16 + 1;
|
||||||
|
//buf.set_string(x, y + height + 1, &value, Style::default().not_dim());
|
||||||
|
//x = x + value.len() as u16;
|
||||||
|
//buf.set_string(x, y + height + 1, " ", Style::default().dim());
|
||||||
|
//x = x + 2;
|
||||||
|
//}
|
||||||
|
//}
|
||||||
|
//}
|
||||||
|
|
||||||
|
//mod vertical {
|
||||||
|
//use super::*;
|
||||||
|
|
||||||
|
//pub fn draw (
|
||||||
|
//s: &Sequencer,
|
||||||
|
//buf: &mut Buffer,
|
||||||
|
//mut area: Rect,
|
||||||
|
//) -> Usually<Rect> {
|
||||||
|
//area.x = area.x + 13;
|
||||||
|
//keys(s, buf, area, 0);
|
||||||
|
//steps(s, buf, area, 0);
|
||||||
|
//playhead(s, buf, area.x, area.y);
|
||||||
|
//Ok(area)
|
||||||
|
//}
|
||||||
|
|
||||||
|
//pub fn steps (s: &Sequencer, buf: &mut Buffer, area: Rect, beat: usize) {
|
||||||
|
//if s.sequence.is_none() {
|
||||||
|
//return
|
||||||
|
//}
|
||||||
|
//let ppq = s.timebase.ppq() as usize;
|
||||||
|
//let bg = Style::default();
|
||||||
|
//let bw = bg.dim();
|
||||||
|
//let wh = bg.white();
|
||||||
|
//let Rect { x, y, .. } = area;
|
||||||
|
//for step in s.time_start..s.time_start+area.height as usize {
|
||||||
|
//let y = y - (s.time_start + step / 2) as u16;
|
||||||
|
//let step = step as usize;
|
||||||
|
////buf.set_string(x + 5, y, &" ".repeat(32.max(note1-s.note_start)as usize), bg);
|
||||||
|
//if step % s.time_zoom == 0 {
|
||||||
|
//buf.set_string(x + 2, y, &format!("{:2} ", step + 1), Style::default());
|
||||||
|
//}
|
||||||
|
//for k in s.note_start..s.note_start+area.width as usize {
|
||||||
|
//let key = ::midly::num::u7::from_int_lossy(k as u8);
|
||||||
|
//if step % 2 == 0 {
|
||||||
|
//let (a, b, c) = (
|
||||||
|
//(step + 0) as usize * ppq / s.time_zoom as usize,
|
||||||
|
//(step + 1) as usize * ppq / s.time_zoom as usize,
|
||||||
|
//(step + 2) as usize * ppq / s.time_zoom as usize,
|
||||||
|
//);
|
||||||
|
//let phrase = &s.phrases[s.sequence.unwrap()];
|
||||||
|
//let (character, style) = match (
|
||||||
|
//phrase.contains_note_on(key, a, b),
|
||||||
|
//phrase.contains_note_on(key, b, c),
|
||||||
|
//) {
|
||||||
|
//(true, true) => ("█", wh),
|
||||||
|
//(true, false) => ("▀", wh),
|
||||||
|
//(false, true) => ("▄", wh),
|
||||||
|
//(false, false) => ("·", bw),
|
||||||
|
//};
|
||||||
|
//character.blit(buf, x + (5 + k - s.note_start) as u16, y, Some(style));
|
||||||
|
//}
|
||||||
|
//}
|
||||||
|
//if beat == step as usize {
|
||||||
|
//buf.set_string(x + 4, y, if beat % 2 == 0 { "▀" } else { "▄" }, Style::default().yellow());
|
||||||
|
//for key in s.note_start..s.note_start+area.width as usize {
|
||||||
|
//let _color = if s.notes_on[key as usize] {
|
||||||
|
//Style::default().red()
|
||||||
|
//} else {
|
||||||
|
//KEY_STYLE[key as usize % 12]
|
||||||
|
//};
|
||||||
|
//}
|
||||||
|
//}
|
||||||
|
//}
|
||||||
|
//}
|
||||||
|
|
||||||
|
//pub fn playhead (s: &Sequencer, buf: &mut Buffer, x: u16, y: u16) {
|
||||||
|
//let x = x + 5 + s.note_cursor as u16;
|
||||||
|
//let y = y + s.time_cursor as u16 / 2;
|
||||||
|
//let c = if s.time_cursor % 2 == 0 { "▀" } else { "▄" };
|
||||||
|
//buf.set_string(x, y, c, Style::default());
|
||||||
|
//}
|
||||||
|
|
||||||
|
//pub const KEY_WHITE: Style = Style {
|
||||||
|
//fg: Some(Color::Gray),
|
||||||
|
//bg: None,
|
||||||
|
//underline_color: None,
|
||||||
|
//add_modifier: ::ratatui::style::Modifier::empty(),
|
||||||
|
//sub_modifier: ::ratatui::style::Modifier::empty(),
|
||||||
|
//};
|
||||||
|
|
||||||
|
//pub const KEY_BLACK: Style = Style {
|
||||||
|
//fg: Some(Color::Black),
|
||||||
|
//bg: None,
|
||||||
|
//underline_color: None,
|
||||||
|
//add_modifier: ::ratatui::style::Modifier::empty(),
|
||||||
|
//sub_modifier: ::ratatui::style::Modifier::empty(),
|
||||||
|
//};
|
||||||
|
|
||||||
|
//pub const KEY_STYLE: [Style;12] = [
|
||||||
|
//KEY_WHITE, KEY_BLACK, KEY_WHITE, KEY_BLACK, KEY_WHITE,
|
||||||
|
//KEY_WHITE, KEY_BLACK, KEY_WHITE, KEY_BLACK, KEY_WHITE, KEY_BLACK, KEY_WHITE,
|
||||||
|
//];
|
||||||
|
|
||||||
|
//pub fn keys (s: &Sequencer, buf: &mut Buffer, area: Rect, beat: usize) {
|
||||||
|
//let ppq = s.timebase.ppq() as usize;
|
||||||
|
//let Rect { x, y, .. } = area;
|
||||||
|
//for key in s.note_start..s.note_start+area.width as usize {
|
||||||
|
//let x = x + (5 + key - s.note_start) as u16;
|
||||||
|
//if key % 12 == 0 {
|
||||||
|
//let octave = format!("C{}", (key / 12) as i8 - 4);
|
||||||
|
//buf.set_string(x, y, &octave, Style::default());
|
||||||
|
//}
|
||||||
|
//let mut color = KEY_STYLE[key as usize % 12];
|
||||||
|
//let mut is_on = s.notes_on[key as usize];
|
||||||
|
//let step = beat;
|
||||||
|
//let (a, b, c) = (
|
||||||
|
//(step + 0) as usize * ppq / s.time_zoom as usize,
|
||||||
|
//(step + 1) as usize * ppq / s.time_zoom as usize,
|
||||||
|
//(step + 2) as usize * ppq / s.time_zoom as usize,
|
||||||
|
//);
|
||||||
|
//let key = ::midly::num::u7::from(key as u8);
|
||||||
|
//let phrase = &s.phrases[s.sequence.unwrap()];
|
||||||
|
//is_on = is_on || phrase.contains_note_on(key, a, b);
|
||||||
|
//is_on = is_on || phrase.contains_note_on(key, b, c);
|
||||||
|
//if is_on {
|
||||||
|
//color = Style::default().red();
|
||||||
|
//}
|
||||||
|
//buf.set_string(x, y - 1, &format!("▄"), color);
|
||||||
|
//}
|
||||||
|
//}
|
||||||
|
|
||||||
|
////pub fn render (s: &Sequencer, buf: &mut Buffer, area: Rect) -> Usually<Rect> {
|
||||||
|
////let Rect { x, y, width, height } = area;
|
||||||
|
////let header = draw_header(s, buf, area)?;
|
||||||
|
////let piano = match s.view {
|
||||||
|
////SequencerMode::Tiny => Rect { x, y, width, height: 0 },
|
||||||
|
////SequencerMode::Compact => Rect { x, y, width, height: 0 },
|
||||||
|
////SequencerMode::Vertical => self::vertical::draw(s, buf, Rect {
|
||||||
|
////x, y: y + header.height, width, height,
|
||||||
|
////})?,
|
||||||
|
////SequencerMode::Horizontal => self::horizontal::draw(
|
||||||
|
////buf,
|
||||||
|
////Rect { x, y: y + header.height, width, height, },
|
||||||
|
////s.phrase(),
|
||||||
|
////s.timebase.ppq() as usize,
|
||||||
|
////s.time_cursor,
|
||||||
|
////s.time_start,
|
||||||
|
////s.time_zoom,
|
||||||
|
////s.note_cursor,
|
||||||
|
////s.note_start,
|
||||||
|
////None
|
||||||
|
////)?,
|
||||||
|
////};
|
||||||
|
////Ok(draw_box(buf, Rect {
|
||||||
|
////x, y,
|
||||||
|
////width: header.width.max(piano.width),
|
||||||
|
////height: header.height + piano.height
|
||||||
|
////}))
|
||||||
|
////}
|
||||||
|
|
||||||
|
////pub fn draw_header (s: &Sequencer, buf: &mut Buffer, area: Rect) -> Usually<Rect> {
|
||||||
|
////let Rect { x, y, width, .. } = area;
|
||||||
|
////let style = Style::default().gray();
|
||||||
|
////crate::view::TransportView {
|
||||||
|
////timebase: &s.timebase,
|
||||||
|
////playing: s.playing,
|
||||||
|
////record: s.recording,
|
||||||
|
////overdub: s.overdub,
|
||||||
|
////monitor: s.monitoring,
|
||||||
|
////frame: 0
|
||||||
|
////}.render(buf, area)?;
|
||||||
|
////let separator = format!("├{}┤", "-".repeat((width - 2).into()));
|
||||||
|
////separator.blit(buf, x, y + 2, Some(style.dim()));
|
||||||
|
////let _ = draw_clips(s, buf, area)?;
|
||||||
|
////Ok(Rect { x, y, width, height: 3 })
|
||||||
|
////}
|
||||||
|
|
||||||
|
////pub fn draw_clips (s: &Sequencer, buf: &mut Buffer, area: Rect) -> Usually<Rect> {
|
||||||
|
////let Rect { x, y, .. } = area;
|
||||||
|
////let style = Style::default().gray();
|
||||||
|
////for (i, sequence) in s.phrases.iter().enumerate() {
|
||||||
|
////let label = format!("▶ {}", &sequence.name);
|
||||||
|
////label.blit(buf, x + 2, y + 3 + (i as u16)*2, Some(if Some(i) == s.sequence {
|
||||||
|
////match s.playing {
|
||||||
|
////TransportState::Rolling => style.white().bold(),
|
||||||
|
////_ => style.not_dim().bold()
|
||||||
|
////}
|
||||||
|
////} else {
|
||||||
|
////style.dim()
|
||||||
|
////}));
|
||||||
|
////}
|
||||||
|
////Ok(Rect { x, y, width: 14, height: 14 })
|
||||||
|
////}
|
||||||
|
//}
|
||||||
|
|
||||||
|
/// From arranger:
|
||||||
|
|
||||||
|
//use crate::core::*;
|
||||||
|
//use crate::view::*;
|
||||||
|
//use crate::model::*;
|
||||||
|
|
||||||
|
//pub fn render (state: &Mixer, buf: &mut Buffer, mut area: Rect)
|
||||||
|
//-> Usually<Rect>
|
||||||
|
//{
|
||||||
|
//if area.height < 2 {
|
||||||
|
//return Ok(area)
|
||||||
|
//}
|
||||||
|
//area.x = area.width.saturating_sub(80) / 2;
|
||||||
|
//area.width = area.width.min(80);
|
||||||
|
//area.height = state.tracks.len() as u16 + 2;
|
||||||
|
//draw_box(buf, area);
|
||||||
|
//let x = area.x + 1;
|
||||||
|
//let y = area.y + 1;
|
||||||
|
//let _h = area.height - 2;
|
||||||
|
//for (i, track) in state.tracks.iter().enumerate() {
|
||||||
|
////buf.set_string(
|
||||||
|
////x, y + index as u16,
|
||||||
|
////&track.name, Style::default().bold().not_dim()
|
||||||
|
////);
|
||||||
|
//for (j, (column, field)) in [
|
||||||
|
//(0, format!(" {:10} ", track.name)),
|
||||||
|
//(12, format!(" {:.1}dB ", track.gain)),
|
||||||
|
//(22, format!(" [ ] ")),
|
||||||
|
//(30, format!(" C ")),
|
||||||
|
//(35, format!(" {:.1}dB ", track.level)),
|
||||||
|
//(45, format!(" [ ] ")),
|
||||||
|
//(51, format!(" {:7} ", track.route)),
|
||||||
|
//].into_iter().enumerate() {
|
||||||
|
//buf.set_string(
|
||||||
|
//x + column as u16,
|
||||||
|
//y + i as u16,
|
||||||
|
//field,
|
||||||
|
//if state.selected_track == i && state.selected_column == j {
|
||||||
|
//Style::default().white().bold().not_dim()
|
||||||
|
//} else {
|
||||||
|
//Style::default().not_dim()
|
||||||
|
//}
|
||||||
|
//);
|
||||||
|
////stdout.queue(move_to(column, row))?;
|
||||||
|
////if state.selected_track == i && state.selected_column == j {
|
||||||
|
////stdout.queue(PrintStyledContent(field.to_string().bold().reverse()))?;
|
||||||
|
////} else {
|
||||||
|
////stdout.queue(PrintStyledContent(field.to_string().bold()))?;
|
||||||
|
////}
|
||||||
|
////fn render_meters (
|
||||||
|
////state: &mut Mixer,
|
||||||
|
////stdout: &mut Stdout,
|
||||||
|
////offset: Rect
|
||||||
|
////) -> Result<(), Box<dyn Error>> {
|
||||||
|
////let move_to = |col, row| crossterm::cursor::MoveTo(offset.0 + col, offset.1 + row);
|
||||||
|
////for (i, track) in state.tracks.iter().enumerate() {
|
||||||
|
////let row = (i + 1) as u16;
|
||||||
|
////stdout
|
||||||
|
////.queue(move_to(10, row))?.queue(PrintStyledContent("▁".green()))?
|
||||||
|
////.queue(move_to(20, row))?.queue(PrintStyledContent("▁".green()))?
|
||||||
|
////.queue(move_to(28, row))?.queue(PrintStyledContent("▁".green()))?
|
||||||
|
////.queue(move_to(43, row))?.queue(PrintStyledContent("▁".green()))?;
|
||||||
|
////}
|
||||||
|
////Ok(())
|
||||||
|
////}
|
||||||
|
//}
|
||||||
|
//}
|
||||||
|
//Ok(area)
|
||||||
|
//}
|
||||||
|
|
||||||
|
|
||||||
|
/// From transport:
|
||||||
|
|
||||||
|
// Record button/indicator
|
||||||
|
//&|buf: &mut Buffer, Rect { x, y, .. }: Rect|{
|
||||||
|
//"⏺ REC".blit(buf, x, y, Some(if self.record { red } else { dim }))
|
||||||
|
//},
|
||||||
|
|
||||||
|
//// Overdub button/indicator
|
||||||
|
//&|buf: &mut Buffer, Rect { x, y, .. }: Rect|{
|
||||||
|
//"⏺ DUB".blit(buf, x, y, Some(if self.overdub { yellow } else { dim }))
|
||||||
|
//},
|
||||||
|
|
||||||
|
//// Monitor button/indicator
|
||||||
|
//&|buf: &mut Buffer, Rect { x, y, .. }: Rect|{
|
||||||
|
//"⏺ MON".blit(buf, x, y, Some(if self.monitor { green } else { dim }))
|
||||||
|
//},
|
||||||
|
//
|
||||||
|
|
||||||
|
// From sampler:
|
||||||
|
//fn render_table (
|
||||||
|
//self: &mut Sampler,
|
||||||
|
//stdout: &mut Stdout,
|
||||||
|
//offset: (u16, u16),
|
||||||
|
//) -> Result<(), Box<dyn Error>> {
|
||||||
|
//let move_to = |col, row| crossterm::cursor::MoveTo(offset.0 + col, offset.1 + row);
|
||||||
|
//stdout.queue(move_to(0, 3))?.queue(
|
||||||
|
//Print(" Name Rate Trigger Route")
|
||||||
|
//)?;
|
||||||
|
//for (i, sample) in self.samples.lock().unwrap().iter().enumerate() {
|
||||||
|
//let row = 4 + i as u16;
|
||||||
|
//for (j, (column, field)) in [
|
||||||
|
//(0, format!(" {:7} ", sample.name)),
|
||||||
|
//(9, format!(" {:.1}Hz ", sample.rate)),
|
||||||
|
//(18, format!(" MIDI C{} {} ", sample.trigger.0, sample.trigger.1)),
|
||||||
|
//(33, format!(" {:.1}dB -> Output ", sample.gain)),
|
||||||
|
//(50, format!(" {} ", sample.playing.unwrap_or(0))),
|
||||||
|
//].into_iter().enumerate() {
|
||||||
|
//stdout.queue(move_to(column, row))?;
|
||||||
|
//if self.selected_sample == i && self.selected_column == j {
|
||||||
|
//stdout.queue(PrintStyledContent(field.to_string().bold().reverse()))?;
|
||||||
|
//} else {
|
||||||
|
//stdout.queue(PrintStyledContent(field.to_string().bold()))?;
|
||||||
|
//}
|
||||||
|
//}
|
||||||
|
//}
|
||||||
|
//Ok(())
|
||||||
|
//}
|
||||||
|
|
||||||
|
//fn render_meters (
|
||||||
|
//self: &mut Sampler,
|
||||||
|
//stdout: &mut Stdout,
|
||||||
|
//offset: (u16, u16),
|
||||||
|
//) -> Result<(), Box<dyn Error>> {
|
||||||
|
//let move_to = |col, row| crossterm::cursor::MoveTo(offset.0 + col, offset.1 + row);
|
||||||
|
//for (i, sample) in self.samples.lock().iter().enumerate() {
|
||||||
|
//let row = 4 + i as u16;
|
||||||
|
//stdout.queue(move_to(32, row))?.queue(
|
||||||
|
//PrintStyledContent("▁".green())
|
||||||
|
//)?;
|
||||||
|
//}
|
||||||
|
//Ok(())
|
||||||
|
//}
|
||||||
|
|
||||||
|
|
@ -1,3 +1,5 @@
|
||||||
|
//! Command line option parser.
|
||||||
|
|
||||||
use clap::{Parser, Subcommand};
|
use clap::{Parser, Subcommand};
|
||||||
|
|
||||||
#[derive(Debug, Parser)]
|
#[derive(Debug, Parser)]
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,5 @@
|
||||||
|
//! Global settings.
|
||||||
|
|
||||||
use crate::core::*;
|
use crate::core::*;
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
use std::fs::{File, create_dir_all};
|
use std::fs::{File, create_dir_all};
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,5 @@
|
||||||
|
//! Handling of input events.
|
||||||
|
|
||||||
use crate::{core::*, handle, App, AppFocus};
|
use crate::{core::*, handle, App, AppFocus};
|
||||||
|
|
||||||
pubmod!{ arranger chain focus mixer plugin sampler sequencer transport }
|
pubmod!{ arranger chain focus mixer plugin sampler sequencer transport }
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,5 @@
|
||||||
|
//! Prelude.
|
||||||
|
|
||||||
// Stdlib dependencies:
|
// Stdlib dependencies:
|
||||||
pub(crate) use std::error::Error;
|
pub(crate) use std::error::Error;
|
||||||
pub(crate) use std::io::{stdout};
|
pub(crate) use std::io::{stdout};
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,5 @@
|
||||||
|
//! Project file format.
|
||||||
|
|
||||||
use crate::{core::*, model::*, App};
|
use crate::{core::*, model::*, App};
|
||||||
|
|
||||||
use clojure_reader::{edn::{read, Edn}, error::Error as EdnError};
|
use clojure_reader::{edn::{read, Edn}, error::Error as EdnError};
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,5 @@
|
||||||
|
//! Audio engine.
|
||||||
|
|
||||||
use crate::core::*;
|
use crate::core::*;
|
||||||
|
|
||||||
submod!( device event factory ports );
|
submod!( device event factory ports );
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,7 @@ extern crate clap;
|
||||||
extern crate jack as _jack;
|
extern crate jack as _jack;
|
||||||
extern crate crossterm;
|
extern crate crossterm;
|
||||||
|
|
||||||
mod core; crate::core::submod! { cli config control model view jack edn }
|
mod core; crate::core::pubmod! { cli config control model view jack edn }
|
||||||
|
|
||||||
use crate::{core::*, model::*};
|
use crate::{core::*, model::*};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,5 @@
|
||||||
|
//! Application state.
|
||||||
|
|
||||||
pub mod looper;
|
pub mod looper;
|
||||||
pub mod mixer;
|
pub mod mixer;
|
||||||
pub mod phrase;
|
pub mod phrase;
|
||||||
|
|
|
||||||
|
|
@ -83,6 +83,7 @@ impl Sampler {
|
||||||
Control::Continue
|
Control::Continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Create [Voice]s from [Sample]s in response to MIDI input.
|
||||||
fn process_midi_in (&mut self, scope: &ProcessScope) {
|
fn process_midi_in (&mut self, scope: &ProcessScope) {
|
||||||
for RawMidi { time, bytes } in self.ports.midi_ins.get("midi").unwrap().iter(scope) {
|
for RawMidi { time, bytes } in self.ports.midi_ins.get("midi").unwrap().iter(scope) {
|
||||||
if let LiveEvent::Midi { message, .. } = LiveEvent::parse(bytes).unwrap() {
|
if let LiveEvent::Midi { message, .. } = LiveEvent::parse(bytes).unwrap() {
|
||||||
|
|
@ -95,22 +96,26 @@ impl Sampler {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Zero the output buffer.
|
||||||
fn clear_output_buffer (&mut self) {
|
fn clear_output_buffer (&mut self) {
|
||||||
for buffer in self.buffer.iter_mut() {
|
for buffer in self.buffer.iter_mut() {
|
||||||
buffer.fill(0.0);
|
buffer.fill(0.0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Mix all currently playing samples into the output.
|
||||||
fn process_audio_out (&mut self, scope: &ProcessScope) {
|
fn process_audio_out (&mut self, scope: &ProcessScope) {
|
||||||
let channel_count = self.buffer.len();
|
let channel_count = self.buffer.len();
|
||||||
self.voices.retain_mut(|voice|{
|
self.voices.retain_mut(|voice|{
|
||||||
for index in 0..scope.n_frames() as usize {
|
for index in 0..scope.n_frames() as usize {
|
||||||
if let Some(frame) = voice.next() {
|
if let Some(frame) = voice.next() {
|
||||||
for (channel, sample) in frame.iter().enumerate() {
|
for (channel, sample) in frame.iter().enumerate() {
|
||||||
|
// Averaging mixer:
|
||||||
//self.buffer[channel % channel_count][index] = (
|
//self.buffer[channel % channel_count][index] = (
|
||||||
//(self.buffer[channel % channel_count][index] + sample * self.output_gain) / 2.0
|
//(self.buffer[channel % channel_count][index] + sample * self.output_gain) / 2.0
|
||||||
//);
|
//);
|
||||||
self.buffer[channel % channel_count][index] += sample * self.output_gain;
|
self.buffer[channel % channel_count][index] +=
|
||||||
|
sample * self.output_gain;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
return false
|
return false
|
||||||
|
|
@ -142,6 +147,7 @@ impl Sampler {
|
||||||
}};
|
}};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Read WAV from file
|
||||||
pub fn read_sample_data (src: &str) -> Usually<(usize, Vec<Vec<f32>>)> {
|
pub fn read_sample_data (src: &str) -> Usually<(usize, Vec<Vec<f32>>)> {
|
||||||
let mut channels: Vec<wavers::Samples<f32>> = vec![];
|
let mut channels: Vec<wavers::Samples<f32>> = vec![];
|
||||||
for channel in wavers::Wav::from_path(src)?.channels() {
|
for channel in wavers::Wav::from_path(src)?.channels() {
|
||||||
|
|
@ -157,6 +163,7 @@ pub fn read_sample_data (src: &str) -> Usually<(usize, Vec<Vec<f32>>)> {
|
||||||
Ok((end, data))
|
Ok((end, data))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A sound sample.
|
||||||
pub struct Sample {
|
pub struct Sample {
|
||||||
pub name: String,
|
pub name: String,
|
||||||
pub start: usize,
|
pub start: usize,
|
||||||
|
|
@ -178,6 +185,7 @@ impl Sample {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A currently playing instance of a sample.
|
||||||
pub struct Voice {
|
pub struct Voice {
|
||||||
pub sample: Arc<Sample>,
|
pub sample: Arc<Sample>,
|
||||||
pub after: usize,
|
pub after: usize,
|
||||||
|
|
@ -203,48 +211,3 @@ impl Iterator for Voice {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//fn render_table (
|
|
||||||
//self: &mut Sampler,
|
|
||||||
//stdout: &mut Stdout,
|
|
||||||
//offset: (u16, u16),
|
|
||||||
//) -> Result<(), Box<dyn Error>> {
|
|
||||||
//let move_to = |col, row| crossterm::cursor::MoveTo(offset.0 + col, offset.1 + row);
|
|
||||||
//stdout.queue(move_to(0, 3))?.queue(
|
|
||||||
//Print(" Name Rate Trigger Route")
|
|
||||||
//)?;
|
|
||||||
//for (i, sample) in self.samples.lock().unwrap().iter().enumerate() {
|
|
||||||
//let row = 4 + i as u16;
|
|
||||||
//for (j, (column, field)) in [
|
|
||||||
//(0, format!(" {:7} ", sample.name)),
|
|
||||||
//(9, format!(" {:.1}Hz ", sample.rate)),
|
|
||||||
//(18, format!(" MIDI C{} {} ", sample.trigger.0, sample.trigger.1)),
|
|
||||||
//(33, format!(" {:.1}dB -> Output ", sample.gain)),
|
|
||||||
//(50, format!(" {} ", sample.playing.unwrap_or(0))),
|
|
||||||
//].into_iter().enumerate() {
|
|
||||||
//stdout.queue(move_to(column, row))?;
|
|
||||||
//if self.selected_sample == i && self.selected_column == j {
|
|
||||||
//stdout.queue(PrintStyledContent(field.to_string().bold().reverse()))?;
|
|
||||||
//} else {
|
|
||||||
//stdout.queue(PrintStyledContent(field.to_string().bold()))?;
|
|
||||||
//}
|
|
||||||
//}
|
|
||||||
//}
|
|
||||||
//Ok(())
|
|
||||||
//}
|
|
||||||
|
|
||||||
//fn render_meters (
|
|
||||||
//self: &mut Sampler,
|
|
||||||
//stdout: &mut Stdout,
|
|
||||||
//offset: (u16, u16),
|
|
||||||
//) -> Result<(), Box<dyn Error>> {
|
|
||||||
//let move_to = |col, row| crossterm::cursor::MoveTo(offset.0 + col, offset.1 + row);
|
|
||||||
//for (i, sample) in self.samples.lock().iter().enumerate() {
|
|
||||||
//let row = 4 + i as u16;
|
|
||||||
//stdout.queue(move_to(32, row))?.queue(
|
|
||||||
//PrintStyledContent("▁".green())
|
|
||||||
//)?;
|
|
||||||
//}
|
|
||||||
//Ok(())
|
|
||||||
//}
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,5 @@
|
||||||
|
//! Rendering of application to display.
|
||||||
|
|
||||||
pub mod arranger;
|
pub mod arranger;
|
||||||
pub mod border;
|
pub mod border;
|
||||||
pub mod chain;
|
pub mod chain;
|
||||||
|
|
|
||||||
|
|
@ -305,72 +305,3 @@ impl<'a> ArrangerView<'a> {
|
||||||
]).render(buf, area)
|
]).render(buf, area)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//use crate::core::*;
|
|
||||||
//use crate::view::*;
|
|
||||||
//use crate::model::*;
|
|
||||||
|
|
||||||
//pub fn render (state: &Mixer, buf: &mut Buffer, mut area: Rect)
|
|
||||||
//-> Usually<Rect>
|
|
||||||
//{
|
|
||||||
//if area.height < 2 {
|
|
||||||
//return Ok(area)
|
|
||||||
//}
|
|
||||||
//area.x = area.width.saturating_sub(80) / 2;
|
|
||||||
//area.width = area.width.min(80);
|
|
||||||
//area.height = state.tracks.len() as u16 + 2;
|
|
||||||
//draw_box(buf, area);
|
|
||||||
//let x = area.x + 1;
|
|
||||||
//let y = area.y + 1;
|
|
||||||
//let _h = area.height - 2;
|
|
||||||
//for (i, track) in state.tracks.iter().enumerate() {
|
|
||||||
////buf.set_string(
|
|
||||||
////x, y + index as u16,
|
|
||||||
////&track.name, Style::default().bold().not_dim()
|
|
||||||
////);
|
|
||||||
//for (j, (column, field)) in [
|
|
||||||
//(0, format!(" {:10} ", track.name)),
|
|
||||||
//(12, format!(" {:.1}dB ", track.gain)),
|
|
||||||
//(22, format!(" [ ] ")),
|
|
||||||
//(30, format!(" C ")),
|
|
||||||
//(35, format!(" {:.1}dB ", track.level)),
|
|
||||||
//(45, format!(" [ ] ")),
|
|
||||||
//(51, format!(" {:7} ", track.route)),
|
|
||||||
//].into_iter().enumerate() {
|
|
||||||
//buf.set_string(
|
|
||||||
//x + column as u16,
|
|
||||||
//y + i as u16,
|
|
||||||
//field,
|
|
||||||
//if state.selected_track == i && state.selected_column == j {
|
|
||||||
//Style::default().white().bold().not_dim()
|
|
||||||
//} else {
|
|
||||||
//Style::default().not_dim()
|
|
||||||
//}
|
|
||||||
//);
|
|
||||||
////stdout.queue(move_to(column, row))?;
|
|
||||||
////if state.selected_track == i && state.selected_column == j {
|
|
||||||
////stdout.queue(PrintStyledContent(field.to_string().bold().reverse()))?;
|
|
||||||
////} else {
|
|
||||||
////stdout.queue(PrintStyledContent(field.to_string().bold()))?;
|
|
||||||
////}
|
|
||||||
////fn render_meters (
|
|
||||||
////state: &mut Mixer,
|
|
||||||
////stdout: &mut Stdout,
|
|
||||||
////offset: Rect
|
|
||||||
////) -> Result<(), Box<dyn Error>> {
|
|
||||||
////let move_to = |col, row| crossterm::cursor::MoveTo(offset.0 + col, offset.1 + row);
|
|
||||||
////for (i, track) in state.tracks.iter().enumerate() {
|
|
||||||
////let row = (i + 1) as u16;
|
|
||||||
////stdout
|
|
||||||
////.queue(move_to(10, row))?.queue(PrintStyledContent("▁".green()))?
|
|
||||||
////.queue(move_to(20, row))?.queue(PrintStyledContent("▁".green()))?
|
|
||||||
////.queue(move_to(28, row))?.queue(PrintStyledContent("▁".green()))?
|
|
||||||
////.queue(move_to(43, row))?.queue(PrintStyledContent("▁".green()))?;
|
|
||||||
////}
|
|
||||||
////Ok(())
|
|
||||||
////}
|
|
||||||
//}
|
|
||||||
//}
|
|
||||||
//Ok(area)
|
|
||||||
//}
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -328,222 +328,3 @@ fn half_block (lower: bool, upper: bool) -> Option<char> {
|
||||||
_ => None
|
_ => None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//pub fn footer (
|
|
||||||
//buf: &mut Buffer,
|
|
||||||
//area: Rect,
|
|
||||||
//note0: usize,
|
|
||||||
//note: usize,
|
|
||||||
//time0: usize,
|
|
||||||
//time: usize,
|
|
||||||
//time_z: usize,
|
|
||||||
//) {
|
|
||||||
//let Rect { mut x, y, width, height } = area;
|
|
||||||
//buf.set_string(x, y + height, format!("├{}┤", "-".repeat((width - 2).into())),
|
|
||||||
//Style::default().dim());
|
|
||||||
//buf.set_string(x, y + height + 2, format!("├{}┤", "-".repeat((width - 2).into())),
|
|
||||||
//Style::default().dim());
|
|
||||||
//x = x + 2;
|
|
||||||
//{
|
|
||||||
//for (_, [letter, title, value]) in [
|
|
||||||
//["S", &format!("ync"), &format!("<4/4>")],
|
|
||||||
//["Q", &format!("uant"), &format!("<1/{}>", 4 * time_z)],
|
|
||||||
//["N", &format!("ote"), &format!("{} ({}-{})", note0 + note, note0, "X")],
|
|
||||||
//["T", &format!("ime"), &format!("{} ({}-{})", time0 + time, time0 + 1, "X")],
|
|
||||||
//].iter().enumerate() {
|
|
||||||
//buf.set_string(x, y + height + 1, letter, Style::default().bold().yellow().dim());
|
|
||||||
//x = x + 1;
|
|
||||||
//buf.set_string(x, y + height + 1, &title, Style::default().bold().dim());
|
|
||||||
//x = x + title.len() as u16 + 1;
|
|
||||||
//buf.set_string(x, y + height + 1, &value, Style::default().not_dim());
|
|
||||||
//x = x + value.len() as u16;
|
|
||||||
//buf.set_string(x, y + height + 1, " ", Style::default().dim());
|
|
||||||
//x = x + 2;
|
|
||||||
//}
|
|
||||||
//}
|
|
||||||
//}
|
|
||||||
|
|
||||||
//mod vertical {
|
|
||||||
//use super::*;
|
|
||||||
|
|
||||||
//pub fn draw (
|
|
||||||
//s: &Sequencer,
|
|
||||||
//buf: &mut Buffer,
|
|
||||||
//mut area: Rect,
|
|
||||||
//) -> Usually<Rect> {
|
|
||||||
//area.x = area.x + 13;
|
|
||||||
//keys(s, buf, area, 0);
|
|
||||||
//steps(s, buf, area, 0);
|
|
||||||
//playhead(s, buf, area.x, area.y);
|
|
||||||
//Ok(area)
|
|
||||||
//}
|
|
||||||
|
|
||||||
//pub fn steps (s: &Sequencer, buf: &mut Buffer, area: Rect, beat: usize) {
|
|
||||||
//if s.sequence.is_none() {
|
|
||||||
//return
|
|
||||||
//}
|
|
||||||
//let ppq = s.timebase.ppq() as usize;
|
|
||||||
//let bg = Style::default();
|
|
||||||
//let bw = bg.dim();
|
|
||||||
//let wh = bg.white();
|
|
||||||
//let Rect { x, y, .. } = area;
|
|
||||||
//for step in s.time_start..s.time_start+area.height as usize {
|
|
||||||
//let y = y - (s.time_start + step / 2) as u16;
|
|
||||||
//let step = step as usize;
|
|
||||||
////buf.set_string(x + 5, y, &" ".repeat(32.max(note1-s.note_start)as usize), bg);
|
|
||||||
//if step % s.time_zoom == 0 {
|
|
||||||
//buf.set_string(x + 2, y, &format!("{:2} ", step + 1), Style::default());
|
|
||||||
//}
|
|
||||||
//for k in s.note_start..s.note_start+area.width as usize {
|
|
||||||
//let key = ::midly::num::u7::from_int_lossy(k as u8);
|
|
||||||
//if step % 2 == 0 {
|
|
||||||
//let (a, b, c) = (
|
|
||||||
//(step + 0) as usize * ppq / s.time_zoom as usize,
|
|
||||||
//(step + 1) as usize * ppq / s.time_zoom as usize,
|
|
||||||
//(step + 2) as usize * ppq / s.time_zoom as usize,
|
|
||||||
//);
|
|
||||||
//let phrase = &s.phrases[s.sequence.unwrap()];
|
|
||||||
//let (character, style) = match (
|
|
||||||
//phrase.contains_note_on(key, a, b),
|
|
||||||
//phrase.contains_note_on(key, b, c),
|
|
||||||
//) {
|
|
||||||
//(true, true) => ("█", wh),
|
|
||||||
//(true, false) => ("▀", wh),
|
|
||||||
//(false, true) => ("▄", wh),
|
|
||||||
//(false, false) => ("·", bw),
|
|
||||||
//};
|
|
||||||
//character.blit(buf, x + (5 + k - s.note_start) as u16, y, Some(style));
|
|
||||||
//}
|
|
||||||
//}
|
|
||||||
//if beat == step as usize {
|
|
||||||
//buf.set_string(x + 4, y, if beat % 2 == 0 { "▀" } else { "▄" }, Style::default().yellow());
|
|
||||||
//for key in s.note_start..s.note_start+area.width as usize {
|
|
||||||
//let _color = if s.notes_on[key as usize] {
|
|
||||||
//Style::default().red()
|
|
||||||
//} else {
|
|
||||||
//KEY_STYLE[key as usize % 12]
|
|
||||||
//};
|
|
||||||
//}
|
|
||||||
//}
|
|
||||||
//}
|
|
||||||
//}
|
|
||||||
|
|
||||||
//pub fn playhead (s: &Sequencer, buf: &mut Buffer, x: u16, y: u16) {
|
|
||||||
//let x = x + 5 + s.note_cursor as u16;
|
|
||||||
//let y = y + s.time_cursor as u16 / 2;
|
|
||||||
//let c = if s.time_cursor % 2 == 0 { "▀" } else { "▄" };
|
|
||||||
//buf.set_string(x, y, c, Style::default());
|
|
||||||
//}
|
|
||||||
|
|
||||||
//pub const KEY_WHITE: Style = Style {
|
|
||||||
//fg: Some(Color::Gray),
|
|
||||||
//bg: None,
|
|
||||||
//underline_color: None,
|
|
||||||
//add_modifier: ::ratatui::style::Modifier::empty(),
|
|
||||||
//sub_modifier: ::ratatui::style::Modifier::empty(),
|
|
||||||
//};
|
|
||||||
|
|
||||||
//pub const KEY_BLACK: Style = Style {
|
|
||||||
//fg: Some(Color::Black),
|
|
||||||
//bg: None,
|
|
||||||
//underline_color: None,
|
|
||||||
//add_modifier: ::ratatui::style::Modifier::empty(),
|
|
||||||
//sub_modifier: ::ratatui::style::Modifier::empty(),
|
|
||||||
//};
|
|
||||||
|
|
||||||
//pub const KEY_STYLE: [Style;12] = [
|
|
||||||
//KEY_WHITE, KEY_BLACK, KEY_WHITE, KEY_BLACK, KEY_WHITE,
|
|
||||||
//KEY_WHITE, KEY_BLACK, KEY_WHITE, KEY_BLACK, KEY_WHITE, KEY_BLACK, KEY_WHITE,
|
|
||||||
//];
|
|
||||||
|
|
||||||
//pub fn keys (s: &Sequencer, buf: &mut Buffer, area: Rect, beat: usize) {
|
|
||||||
//let ppq = s.timebase.ppq() as usize;
|
|
||||||
//let Rect { x, y, .. } = area;
|
|
||||||
//for key in s.note_start..s.note_start+area.width as usize {
|
|
||||||
//let x = x + (5 + key - s.note_start) as u16;
|
|
||||||
//if key % 12 == 0 {
|
|
||||||
//let octave = format!("C{}", (key / 12) as i8 - 4);
|
|
||||||
//buf.set_string(x, y, &octave, Style::default());
|
|
||||||
//}
|
|
||||||
//let mut color = KEY_STYLE[key as usize % 12];
|
|
||||||
//let mut is_on = s.notes_on[key as usize];
|
|
||||||
//let step = beat;
|
|
||||||
//let (a, b, c) = (
|
|
||||||
//(step + 0) as usize * ppq / s.time_zoom as usize,
|
|
||||||
//(step + 1) as usize * ppq / s.time_zoom as usize,
|
|
||||||
//(step + 2) as usize * ppq / s.time_zoom as usize,
|
|
||||||
//);
|
|
||||||
//let key = ::midly::num::u7::from(key as u8);
|
|
||||||
//let phrase = &s.phrases[s.sequence.unwrap()];
|
|
||||||
//is_on = is_on || phrase.contains_note_on(key, a, b);
|
|
||||||
//is_on = is_on || phrase.contains_note_on(key, b, c);
|
|
||||||
//if is_on {
|
|
||||||
//color = Style::default().red();
|
|
||||||
//}
|
|
||||||
//buf.set_string(x, y - 1, &format!("▄"), color);
|
|
||||||
//}
|
|
||||||
//}
|
|
||||||
|
|
||||||
////pub fn render (s: &Sequencer, buf: &mut Buffer, area: Rect) -> Usually<Rect> {
|
|
||||||
////let Rect { x, y, width, height } = area;
|
|
||||||
////let header = draw_header(s, buf, area)?;
|
|
||||||
////let piano = match s.view {
|
|
||||||
////SequencerMode::Tiny => Rect { x, y, width, height: 0 },
|
|
||||||
////SequencerMode::Compact => Rect { x, y, width, height: 0 },
|
|
||||||
////SequencerMode::Vertical => self::vertical::draw(s, buf, Rect {
|
|
||||||
////x, y: y + header.height, width, height,
|
|
||||||
////})?,
|
|
||||||
////SequencerMode::Horizontal => self::horizontal::draw(
|
|
||||||
////buf,
|
|
||||||
////Rect { x, y: y + header.height, width, height, },
|
|
||||||
////s.phrase(),
|
|
||||||
////s.timebase.ppq() as usize,
|
|
||||||
////s.time_cursor,
|
|
||||||
////s.time_start,
|
|
||||||
////s.time_zoom,
|
|
||||||
////s.note_cursor,
|
|
||||||
////s.note_start,
|
|
||||||
////None
|
|
||||||
////)?,
|
|
||||||
////};
|
|
||||||
////Ok(draw_box(buf, Rect {
|
|
||||||
////x, y,
|
|
||||||
////width: header.width.max(piano.width),
|
|
||||||
////height: header.height + piano.height
|
|
||||||
////}))
|
|
||||||
////}
|
|
||||||
|
|
||||||
////pub fn draw_header (s: &Sequencer, buf: &mut Buffer, area: Rect) -> Usually<Rect> {
|
|
||||||
////let Rect { x, y, width, .. } = area;
|
|
||||||
////let style = Style::default().gray();
|
|
||||||
////crate::view::TransportView {
|
|
||||||
////timebase: &s.timebase,
|
|
||||||
////playing: s.playing,
|
|
||||||
////record: s.recording,
|
|
||||||
////overdub: s.overdub,
|
|
||||||
////monitor: s.monitoring,
|
|
||||||
////frame: 0
|
|
||||||
////}.render(buf, area)?;
|
|
||||||
////let separator = format!("├{}┤", "-".repeat((width - 2).into()));
|
|
||||||
////separator.blit(buf, x, y + 2, Some(style.dim()));
|
|
||||||
////let _ = draw_clips(s, buf, area)?;
|
|
||||||
////Ok(Rect { x, y, width, height: 3 })
|
|
||||||
////}
|
|
||||||
|
|
||||||
////pub fn draw_clips (s: &Sequencer, buf: &mut Buffer, area: Rect) -> Usually<Rect> {
|
|
||||||
////let Rect { x, y, .. } = area;
|
|
||||||
////let style = Style::default().gray();
|
|
||||||
////for (i, sequence) in s.phrases.iter().enumerate() {
|
|
||||||
////let label = format!("▶ {}", &sequence.name);
|
|
||||||
////label.blit(buf, x + 2, y + 3 + (i as u16)*2, Some(if Some(i) == s.sequence {
|
|
||||||
////match s.playing {
|
|
||||||
////TransportState::Rolling => style.white().bold(),
|
|
||||||
////_ => style.not_dim().bold()
|
|
||||||
////}
|
|
||||||
////} else {
|
|
||||||
////style.dim()
|
|
||||||
////}));
|
|
||||||
////}
|
|
||||||
////Ok(Rect { x, y, width: 14, height: 14 })
|
|
||||||
////}
|
|
||||||
//}
|
|
||||||
|
|
|
||||||
|
|
@ -79,18 +79,3 @@ render!(TransportToolbar |self, buf, area| {
|
||||||
|
|
||||||
]).render(buf, area)
|
]).render(buf, area)
|
||||||
});
|
});
|
||||||
|
|
||||||
// Record button/indicator
|
|
||||||
//&|buf: &mut Buffer, Rect { x, y, .. }: Rect|{
|
|
||||||
//"⏺ REC".blit(buf, x, y, Some(if self.record { red } else { dim }))
|
|
||||||
//},
|
|
||||||
|
|
||||||
//// Overdub button/indicator
|
|
||||||
//&|buf: &mut Buffer, Rect { x, y, .. }: Rect|{
|
|
||||||
//"⏺ DUB".blit(buf, x, y, Some(if self.overdub { yellow } else { dim }))
|
|
||||||
//},
|
|
||||||
|
|
||||||
//// Monitor button/indicator
|
|
||||||
//&|buf: &mut Buffer, Rect { x, y, .. }: Rect|{
|
|
||||||
//"⏺ MON".blit(buf, x, y, Some(if self.monitor { green } else { dim }))
|
|
||||||
//},
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue