mirror of
https://codeberg.org/unspeaker/tek.git
synced 2025-12-08 20:56:43 +01:00
switchable monitoring
This commit is contained in:
parent
e30dd94d23
commit
edadfde1a4
5 changed files with 94 additions and 107 deletions
|
|
@ -204,23 +204,14 @@ fn play_toggle (s: &mut Launcher) -> Usually<bool> {
|
||||||
//unimplemented!()
|
//unimplemented!()
|
||||||
//}
|
//}
|
||||||
fn record_toggle (s: &mut Launcher) -> Usually<bool> {
|
fn record_toggle (s: &mut Launcher) -> Usually<bool> {
|
||||||
s.recording = !s.recording;
|
s.sequencer().map(|mut s|s.recording = !s.recording);
|
||||||
for track in s.tracks.iter() {
|
|
||||||
track.sequencer.state().recording = s.recording;
|
|
||||||
}
|
|
||||||
Ok(true)
|
Ok(true)
|
||||||
}
|
}
|
||||||
fn overdub_toggle (s: &mut Launcher) -> Usually<bool> {
|
fn overdub_toggle (s: &mut Launcher) -> Usually<bool> {
|
||||||
s.overdub = !s.overdub;
|
s.sequencer().map(|mut s|s.overdub = !s.overdub);
|
||||||
for track in s.tracks.iter() {
|
|
||||||
track.sequencer.state().overdub = s.overdub;
|
|
||||||
}
|
|
||||||
Ok(true)
|
Ok(true)
|
||||||
}
|
}
|
||||||
fn monitor_toggle (s: &mut Launcher) -> Usually<bool> {
|
fn monitor_toggle (s: &mut Launcher) -> Usually<bool> {
|
||||||
s.monitoring = !s.monitoring;
|
s.sequencer().map(|mut s|s.monitoring = !s.monitoring);
|
||||||
for track in s.tracks.iter() {
|
|
||||||
track.sequencer.state().monitoring = s.monitoring;
|
|
||||||
}
|
|
||||||
Ok(true)
|
Ok(true)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -57,7 +57,7 @@ impl Launcher {
|
||||||
recording: false,
|
recording: false,
|
||||||
overdub: true,
|
overdub: true,
|
||||||
cursor: (2, 2),
|
cursor: (2, 2),
|
||||||
current_frame: 0,
|
current_frame: 0,
|
||||||
scenes: scenes.unwrap_or_else(||vec![Scene::new(&"Scene 1", &[None])]),
|
scenes: scenes.unwrap_or_else(||vec![Scene::new(&"Scene 1", &[None])]),
|
||||||
tracks: if let Some(tracks) = tracks { tracks } else { vec![
|
tracks: if let Some(tracks) = tracks { tracks } else { vec![
|
||||||
Track::new("Track 1", &timebase, None, Some(vec![
|
Track::new("Track 1", &timebase, None, Some(vec![
|
||||||
|
|
@ -172,8 +172,8 @@ impl DynamicDevice<Launcher> {
|
||||||
}
|
}
|
||||||
impl PortList for Launcher {}
|
impl PortList for Launcher {}
|
||||||
pub fn process (state: &mut Launcher, _: &Client, _: &ProcessScope) -> Control {
|
pub fn process (state: &mut Launcher, _: &Client, _: &ProcessScope) -> Control {
|
||||||
let transport = state.transport.query().unwrap();
|
let transport = state.transport.query().unwrap();
|
||||||
state.playing = transport.state;
|
state.playing = transport.state;
|
||||||
state.current_frame = transport.pos.frame() as usize;
|
state.current_frame = transport.pos.frame() as usize;
|
||||||
Control::Continue
|
Control::Continue
|
||||||
}
|
}
|
||||||
|
|
@ -184,9 +184,9 @@ pub fn render (state: &Launcher, buf: &mut Buffer, mut area: Rect) -> Usually<Re
|
||||||
{
|
{
|
||||||
use crate::device::transport::*;
|
use crate::device::transport::*;
|
||||||
draw_play_stop(buf, x + 1, y, &state.playing);
|
draw_play_stop(buf, x + 1, y, &state.playing);
|
||||||
draw_rec(buf, x + 12, y, state.recording);
|
draw_rec(buf, x + 12, y, state.sequencer().map(|s|s.recording).unwrap_or(false));
|
||||||
draw_mon(buf, x + 19, y, state.monitoring);
|
draw_mon(buf, x + 19, y, state.sequencer().map(|s|s.monitoring).unwrap_or(false));
|
||||||
draw_dub(buf, x + 26, y, state.overdub);
|
draw_dub(buf, x + 26, y, state.sequencer().map(|s|s.overdub).unwrap_or(false));
|
||||||
draw_bpm(buf, x + 33, y, state.timebase.bpm() as usize);
|
draw_bpm(buf, x + 33, y, state.timebase.bpm() as usize);
|
||||||
draw_timer(buf, x + width - 1, y, &state.timebase, state.current_frame);
|
draw_timer(buf, x + width - 1, y, &state.timebase, state.current_frame);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,9 @@
|
||||||
use crate::core::*;
|
use crate::core::*;
|
||||||
use crate::layout::*;
|
use crate::layout::*;
|
||||||
|
|
||||||
mod keys; use self::keys::*;
|
mod keys; use self::keys::*;
|
||||||
mod handle; pub use self::handle::*;
|
mod handle; pub use self::handle::*;
|
||||||
mod process; pub use self::process::*;
|
mod phrase; pub use self::phrase::*;
|
||||||
mod phrase; pub use self::phrase::*;
|
|
||||||
|
|
||||||
pub mod horizontal;
|
pub mod horizontal;
|
||||||
pub mod vertical;
|
pub mod vertical;
|
||||||
|
|
@ -58,21 +57,18 @@ impl Sequencer {
|
||||||
) -> Usually<DynamicDevice<Self>> {
|
) -> Usually<DynamicDevice<Self>> {
|
||||||
let (client, _status) = Client::new(name, ClientOptions::NO_START_SERVER)?;
|
let (client, _status) = Client::new(name, ClientOptions::NO_START_SERVER)?;
|
||||||
let transport = client.transport();
|
let transport = client.transport();
|
||||||
DynamicDevice::new(render, handle, process, Self {
|
DynamicDevice::new(render, handle, Self::process, Self {
|
||||||
name: name.into(),
|
name: name.into(),
|
||||||
midi_in: client.register_port("in", MidiIn::default())?,
|
timebase: timebase.clone(),
|
||||||
midi_out: client.register_port("out", MidiOut::default())?,
|
phrases: phrases.unwrap_or_else(||vec![Phrase::default()]),
|
||||||
|
sequence: Some(0),
|
||||||
timebase: timebase.clone(),
|
|
||||||
sequence: Some(0),
|
|
||||||
phrases: phrases.unwrap_or_else(||vec![
|
|
||||||
Phrase::new("Phrase0", 4 * timebase.ppq() as usize, None)
|
|
||||||
]),
|
|
||||||
|
|
||||||
transport,
|
transport,
|
||||||
playing: TransportState::Starting,
|
midi_in: client.register_port("in", MidiIn::default())?,
|
||||||
monitoring: true,
|
monitoring: true,
|
||||||
recording: true,
|
recording: true,
|
||||||
|
midi_out: client.register_port("out", MidiOut::default())?,
|
||||||
|
playing: TransportState::Starting,
|
||||||
overdub: true,
|
overdub: true,
|
||||||
|
|
||||||
view: SequencerView::Horizontal,
|
view: SequencerView::Horizontal,
|
||||||
|
|
@ -88,6 +84,61 @@ impl Sequencer {
|
||||||
pub fn phrase <'a> (&'a self) -> Option<&'a Phrase> {
|
pub fn phrase <'a> (&'a self) -> Option<&'a Phrase> {
|
||||||
self.phrases.get(self.sequence?)
|
self.phrases.get(self.sequence?)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn process (&mut self, _: &Client, scope: &ProcessScope) -> Control {
|
||||||
|
// Get currently playing phrase
|
||||||
|
if self.sequence.is_none() {
|
||||||
|
return Control::Continue
|
||||||
|
}
|
||||||
|
let phrase = self.phrases.get_mut(self.sequence.unwrap());
|
||||||
|
if phrase.is_none() {
|
||||||
|
return Control::Continue
|
||||||
|
}
|
||||||
|
let phrase = phrase.unwrap();
|
||||||
|
// Prepare output buffer and transport
|
||||||
|
let frame = scope.last_frame_time() as usize;//transport.pos.frame() as usize;
|
||||||
|
let frames = scope.n_frames() as usize;
|
||||||
|
let mut output: Vec<Option<Vec<Vec<u8>>>> = vec![None;frames];
|
||||||
|
// Check transport self. If starting or stopping, send "all notes off":
|
||||||
|
let transport = self.transport.query().unwrap();
|
||||||
|
if transport.state != self.playing {
|
||||||
|
all_notes_off(&mut output);
|
||||||
|
}
|
||||||
|
self.playing = transport.state;
|
||||||
|
// Play from phrase into output buffer
|
||||||
|
if self.playing == TransportState::Rolling {
|
||||||
|
phrase.process_out(
|
||||||
|
&mut output,
|
||||||
|
&mut self.notes_on,
|
||||||
|
&self.timebase,
|
||||||
|
frame,
|
||||||
|
frames
|
||||||
|
);
|
||||||
|
}
|
||||||
|
// Play from input to monitor, and record into phrase.
|
||||||
|
phrase.process_in(
|
||||||
|
self.midi_in.iter(scope),
|
||||||
|
&mut self.notes_on,
|
||||||
|
if self.monitoring { Some(&mut output) } else { None },
|
||||||
|
self.recording && self.playing == TransportState::Rolling,
|
||||||
|
&self.timebase,
|
||||||
|
frame,
|
||||||
|
);
|
||||||
|
// Write to port from output buffer
|
||||||
|
// (containing notes from sequence and/or monitor)
|
||||||
|
let mut writer = self.midi_out.writer(scope);
|
||||||
|
for time in 0..scope.n_frames() {
|
||||||
|
if let Some(Some(frame)) = output.get_mut(time as usize) {
|
||||||
|
for event in frame.iter() {
|
||||||
|
writer.write(&::jack::RawMidi { time, bytes: &event })
|
||||||
|
.expect(&format!("{event:?}"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Control::Continue
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
impl PortList for Sequencer {
|
impl PortList for Sequencer {
|
||||||
fn midi_ins (&self) -> Usually<Vec<String>> { Ok(vec![self.midi_in.name()?]) }
|
fn midi_ins (&self) -> Usually<Vec<String>> { Ok(vec![self.midi_in.name()?]) }
|
||||||
|
|
@ -168,3 +219,14 @@ pub fn contains_note_on (sequence: &Phrase, k: u7, start: usize, end: usize) ->
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
/// Add "all notes off" to the start of a buffer.
|
||||||
|
pub fn all_notes_off (output: &mut MIDIChunk) {
|
||||||
|
output[0] = Some(vec![]);
|
||||||
|
if let Some(Some(frame)) = output.get_mut(0) {
|
||||||
|
let mut buf = vec![];
|
||||||
|
let msg = MidiMessage::Controller { controller: 123.into(), value: 0.into() };
|
||||||
|
let evt = LiveEvent::Midi { channel: 0.into(), message: msg };
|
||||||
|
evt.write(&mut buf).unwrap();
|
||||||
|
frame.push(buf);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,7 @@ impl Phrase {
|
||||||
Self { name: name.to_string(), length, notes: notes.unwrap_or(BTreeMap::new()) }
|
Self { name: name.to_string(), length, notes: notes.unwrap_or(BTreeMap::new()) }
|
||||||
}
|
}
|
||||||
/** Write a chunk of MIDI events to an output port. */
|
/** Write a chunk of MIDI events to an output port. */
|
||||||
pub fn chunk_out (
|
pub fn process_out (
|
||||||
&self,
|
&self,
|
||||||
output: &mut MIDIChunk,
|
output: &mut MIDIChunk,
|
||||||
notes_on: &mut Vec<bool>,
|
notes_on: &mut Vec<bool>,
|
||||||
|
|
@ -56,7 +56,7 @@ impl Phrase {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/** Read a chunk of MIDI events from an input port. */
|
/** Read a chunk of MIDI events from an input port. */
|
||||||
pub fn chunk_in (
|
pub fn process_in (
|
||||||
&mut self,
|
&mut self,
|
||||||
input: ::jack::MidiIter,
|
input: ::jack::MidiIter,
|
||||||
notes_on: &mut Vec<bool>,
|
notes_on: &mut Vec<bool>,
|
||||||
|
|
@ -111,3 +111,9 @@ impl Phrase {
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Default for Phrase {
|
||||||
|
fn default () -> Self {
|
||||||
|
Self::new("", 0, None)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,72 +0,0 @@
|
||||||
use crate::core::*;
|
|
||||||
use super::*;
|
|
||||||
|
|
||||||
pub fn process (state: &mut Sequencer, _: &Client, scope: &ProcessScope) -> Control {
|
|
||||||
// Get currently playing phrase
|
|
||||||
if state.sequence.is_none() {
|
|
||||||
return Control::Continue
|
|
||||||
}
|
|
||||||
let phrase = state.phrases.get_mut(state.sequence.unwrap());
|
|
||||||
if phrase.is_none() {
|
|
||||||
return Control::Continue
|
|
||||||
}
|
|
||||||
let phrase = phrase.unwrap();
|
|
||||||
// Prepare output buffer and transport
|
|
||||||
let frame = scope.last_frame_time() as usize;//transport.pos.frame() as usize;
|
|
||||||
let frames = scope.n_frames() as usize;
|
|
||||||
let mut output: Vec<Option<Vec<Vec<u8>>>> = vec![None;frames];
|
|
||||||
// Check transport state. If starting or stopping, send "all notes off":
|
|
||||||
let transport = state.transport.query().unwrap();
|
|
||||||
if transport.state != state.playing {
|
|
||||||
all_notes_off(&mut output);
|
|
||||||
}
|
|
||||||
state.playing = transport.state;
|
|
||||||
// Play from phrase into output buffer
|
|
||||||
if state.playing == TransportState::Rolling {
|
|
||||||
phrase.chunk_out(
|
|
||||||
&mut output,
|
|
||||||
&mut state.notes_on,
|
|
||||||
&state.timebase,
|
|
||||||
frame,
|
|
||||||
frames
|
|
||||||
);
|
|
||||||
}
|
|
||||||
// Play from input to monitor, and record into phrase.
|
|
||||||
//let usecs = state.timebase.frames_usecs(frame);
|
|
||||||
//let steps = usecs / state.timebase.usec_per_step(state.time_zoom as usize);
|
|
||||||
//let step = steps % state.steps;
|
|
||||||
//let tick = (step * state.timebase.ppq() / state.time_zoom) as u32;
|
|
||||||
phrase.chunk_in(
|
|
||||||
state.midi_in.iter(scope),
|
|
||||||
&mut state.notes_on,
|
|
||||||
Some(&mut output),
|
|
||||||
state.recording && state.playing == TransportState::Rolling,
|
|
||||||
&state.timebase,
|
|
||||||
frame,
|
|
||||||
);
|
|
||||||
// Write to port from output buffer
|
|
||||||
// (containing notes from sequence and/or monitor)
|
|
||||||
let mut writer = state.midi_out.writer(scope);
|
|
||||||
for time in 0..scope.n_frames() {
|
|
||||||
if let Some(Some(frame)) = output.get_mut(time as usize) {
|
|
||||||
for event in frame.iter() {
|
|
||||||
writer.write(&::jack::RawMidi { time, bytes: &event })
|
|
||||||
.expect(&format!("{event:?}"));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Control::Continue
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/// Add "all notes off" to the start of a buffer.
|
|
||||||
pub fn all_notes_off (output: &mut MIDIChunk) {
|
|
||||||
output[0] = Some(vec![]);
|
|
||||||
if let Some(Some(frame)) = output.get_mut(0) {
|
|
||||||
let mut buf = vec![];
|
|
||||||
let msg = MidiMessage::Controller { controller: 123.into(), value: 0.into() };
|
|
||||||
let evt = LiveEvent::Midi { channel: 0.into(), message: msg };
|
|
||||||
evt.write(&mut buf).unwrap();
|
|
||||||
frame.push(buf);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue