mirror of
https://codeberg.org/unspeaker/tek.git
synced 2025-12-06 11:46:41 +01:00
178 lines
5.5 KiB
Rust
178 lines
5.5 KiB
Rust
use crate::*;
|
|
|
|
/// The sampler device plays sounds in response to MIDI notes.
|
|
#[derive(Debug)]
|
|
pub struct Sampler {
|
|
/// Name of sampler.
|
|
pub name: String,
|
|
/// Device color.
|
|
pub color: ItemTheme,
|
|
/// Audio input ports. Samples get recorded here.
|
|
pub audio_ins: Vec<JackAudioIn>,
|
|
/// Audio input meters.
|
|
pub input_meters: Vec<f32>,
|
|
/// Sample currently being recorded.
|
|
pub recording: Option<(usize, Arc<RwLock<Sample>>)>,
|
|
/// Recording buffer.
|
|
pub buffer: Vec<Vec<f32>>,
|
|
/// Samples mapped to MIDI notes.
|
|
pub mapped: [Option<Arc<RwLock<Sample>>>;128],
|
|
/// Samples that are not mapped to MIDI notes.
|
|
pub unmapped: Vec<Arc<RwLock<Sample>>>,
|
|
/// Sample currently being edited.
|
|
pub editing: Option<Arc<RwLock<Sample>>>,
|
|
/// MIDI input port. Triggers sample playback.
|
|
pub midi_in: Option<JackMidiIn>,
|
|
/// Collection of currently playing instances of samples.
|
|
pub voices: Arc<RwLock<Vec<Voice>>>,
|
|
/// Audio output ports. Voices get played here.
|
|
pub audio_outs: Vec<JackAudioOut>,
|
|
/// Audio output meters.
|
|
pub output_meters: Vec<f32>,
|
|
/// How to mix the voices.
|
|
pub mixing_mode: MixingMode,
|
|
/// How to meter the inputs and outputs.
|
|
pub metering_mode: MeteringMode,
|
|
/// Fixed gain applied to all output.
|
|
pub output_gain: f32,
|
|
/// Currently active modal, if any.
|
|
pub mode: Option<SamplerMode>,
|
|
/// Size of rendered sampler.
|
|
pub size: Measure<TuiOut>,
|
|
/// Lowest note displayed.
|
|
pub note_lo: AtomicUsize,
|
|
/// Currently selected note.
|
|
pub note_pt: AtomicUsize,
|
|
/// Selected note as row/col.
|
|
pub cursor: (AtomicUsize, AtomicUsize),
|
|
}
|
|
|
|
impl Default for Sampler {
|
|
fn default () -> Self {
|
|
Self {
|
|
midi_in: None,
|
|
audio_ins: vec![],
|
|
input_meters: vec![0.0;2],
|
|
output_meters: vec![0.0;2],
|
|
audio_outs: vec![],
|
|
name: "tek_sampler".to_string(),
|
|
mapped: [const { None };128],
|
|
unmapped: vec![],
|
|
voices: Arc::new(RwLock::new(vec![])),
|
|
buffer: vec![vec![0.0;16384];2],
|
|
output_gain: 1.,
|
|
recording: None,
|
|
mode: None,
|
|
editing: None,
|
|
size: Default::default(),
|
|
note_lo: 0.into(),
|
|
note_pt: 0.into(),
|
|
cursor: (0.into(), 0.into()),
|
|
color: Default::default(),
|
|
mixing_mode: Default::default(),
|
|
metering_mode: Default::default(),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl Sampler {
|
|
pub fn new (
|
|
jack: &Jack,
|
|
name: impl AsRef<str>,
|
|
midi_from: &[PortConnect],
|
|
audio_from: &[&[PortConnect];2],
|
|
audio_to: &[&[PortConnect];2],
|
|
) -> Usually<Self> {
|
|
let name = name.as_ref();
|
|
Ok(Self {
|
|
name: name.into(),
|
|
midi_in: Some(JackMidiIn::new(jack, format!("M/{name}"), midi_from)?),
|
|
audio_ins: vec![
|
|
JackAudioIn::new(jack, &format!("L/{name}"), audio_from[0])?,
|
|
JackAudioIn::new(jack, &format!("R/{name}"), audio_from[1])?,
|
|
],
|
|
audio_outs: vec![
|
|
JackAudioOut::new(jack, &format!("{name}/L"), audio_to[0])?,
|
|
JackAudioOut::new(jack, &format!("{name}/R"), audio_to[1])?,
|
|
],
|
|
..Default::default()
|
|
})
|
|
}
|
|
/// Value of cursor
|
|
pub fn cursor (&self) -> (usize, usize) {
|
|
(self.cursor.0.load(Relaxed), self.cursor.1.load(Relaxed))
|
|
}
|
|
}
|
|
|
|
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) -> &AtomicUsize {
|
|
unreachable!();
|
|
}
|
|
fn get_note_len (&self) -> usize {
|
|
0
|
|
}
|
|
fn set_note_len (&self, x: usize) -> usize {
|
|
0 /*TODO?*/
|
|
}
|
|
fn note_pos (&self) -> &AtomicUsize {
|
|
&self.note_pt
|
|
}
|
|
fn get_note_pos (&self) -> usize {
|
|
self.note_pt.load(Relaxed)
|
|
}
|
|
fn set_note_pos (&self, x: usize) -> usize {
|
|
let old = self.note_pt.swap(x, Relaxed);
|
|
self.cursor.0.store(x % 8, Relaxed);
|
|
self.cursor.1.store(x / 8, Relaxed);
|
|
old
|
|
}
|
|
}
|
|
|
|
/// A sound sample.
|
|
#[derive(Default, Debug)]
|
|
pub struct Sample {
|
|
pub name: Arc<str>,
|
|
pub start: usize,
|
|
pub end: usize,
|
|
pub channels: Vec<Vec<f32>>,
|
|
pub rate: Option<usize>,
|
|
pub gain: f32,
|
|
}
|
|
|
|
impl Sample {
|
|
pub fn new (name: impl AsRef<str>, start: usize, end: usize, channels: Vec<Vec<f32>>) -> Self {
|
|
Self { name: name.as_ref().into(), start, end, channels, rate: None, gain: 1.0 }
|
|
}
|
|
pub fn play (sample: &Arc<RwLock<Self>>, after: usize, velocity: &u7) -> Voice {
|
|
Voice {
|
|
sample: sample.clone(),
|
|
after,
|
|
position: sample.read().unwrap().start,
|
|
velocity: velocity.as_int() as f32 / 127.0,
|
|
}
|
|
}
|
|
}
|
|
|
|
/// A currently playing instance of a sample.
|
|
#[derive(Default, Debug, Clone)]
|
|
pub struct Voice {
|
|
pub sample: Arc<RwLock<Sample>>,
|
|
pub after: usize,
|
|
pub position: usize,
|
|
pub velocity: f32,
|
|
}
|
|
|
|
#[derive(Debug)]
|
|
pub enum SamplerMode {
|
|
// Load sample from path
|
|
Import(usize, FileBrowser),
|
|
}
|