mirror of
https://codeberg.org/unspeaker/tek.git
synced 2025-12-07 12:16:42 +01:00
wip: AudioEngine pt.2 (rewrite activation sanely)
This commit is contained in:
parent
2303b258f6
commit
746e29aeb3
8 changed files with 104 additions and 106 deletions
|
|
@ -1,22 +1,5 @@
|
||||||
use crate::*;
|
use crate::*;
|
||||||
use jack::*;
|
use jack::*;
|
||||||
/// Just run thing with JACK. Returns the activated client.
|
|
||||||
pub fn jack_run<T, E: Engine>(name: &str, app: &Arc<RwLock<T>>) -> Usually<DynamicAsyncClient>
|
|
||||||
where
|
|
||||||
T: Handle<E> + Audio + Send + Sync + 'static,
|
|
||||||
{
|
|
||||||
let options = ClientOptions::NO_START_SERVER;
|
|
||||||
let (client, _status) = Client::new(name, options)?;
|
|
||||||
let on_event = Notifications(Box::new({
|
|
||||||
let _app = app.clone();
|
|
||||||
move|_event|{/* FIXME: this deadlocks: app.lock().unwrap().handle(&event).unwrap(); */}
|
|
||||||
}) as Box<dyn Fn(JackEvent) + Send + Sync>);
|
|
||||||
let on_chunk = contrib::ClosureProcessHandler::new(Box::new({
|
|
||||||
let app = app.clone();
|
|
||||||
move|c: &Client, s: &ProcessScope|app.write().unwrap().process(c, s)
|
|
||||||
}) as BoxedAudioHandler);
|
|
||||||
Ok(client.activate_async(on_event, on_chunk)?)
|
|
||||||
}
|
|
||||||
/// Trait for things that wrap a JACK client.
|
/// Trait for things that wrap a JACK client.
|
||||||
pub trait AudioEngine {
|
pub trait AudioEngine {
|
||||||
fn activate(
|
fn activate(
|
||||||
|
|
@ -36,10 +19,46 @@ pub trait AudioEngine {
|
||||||
}
|
}
|
||||||
/// Wraps [Client] or [DynamicAsyncClient] in place.
|
/// Wraps [Client] or [DynamicAsyncClient] in place.
|
||||||
pub enum JackClient {
|
pub enum JackClient {
|
||||||
|
/// Before activation.
|
||||||
Inactive(Client),
|
Inactive(Client),
|
||||||
|
/// During activation.
|
||||||
Activating,
|
Activating,
|
||||||
|
/// After activation. Must not be dropped for JACK thread to persist.
|
||||||
Active(DynamicAsyncClient),
|
Active(DynamicAsyncClient),
|
||||||
}
|
}
|
||||||
|
pub type DynamicAsyncClient = AsyncClient<DynamicNotifications, DynamicAudioHandler>;
|
||||||
|
pub type DynamicAudioHandler = contrib::ClosureProcessHandler<(), BoxedAudioHandler>;
|
||||||
|
pub type BoxedAudioHandler = Box<dyn FnMut(&Client, &ProcessScope) -> Control + Send>;
|
||||||
|
impl JackClient {
|
||||||
|
pub fn new (name: &str) -> Usually<Self> {
|
||||||
|
let (client, _) = Client::new(name, ClientOptions::NO_START_SERVER)?;
|
||||||
|
Ok(Self::Inactive(client))
|
||||||
|
}
|
||||||
|
pub fn activate_with <T: Audio + 'static> (
|
||||||
|
self,
|
||||||
|
init: impl FnOnce(&Arc<RwLock<JackClient>>)->T
|
||||||
|
)
|
||||||
|
-> Usually<Arc<RwLock<T>>>
|
||||||
|
{
|
||||||
|
let client = Arc::new(RwLock::new(self));
|
||||||
|
let target = Arc::new(RwLock::new(init(&client)));
|
||||||
|
let event = Box::new(move|_|{/*TODO*/}) as Box<dyn Fn(JackEvent) + Send + Sync>;
|
||||||
|
let events = Notifications(event);
|
||||||
|
let frame = Box::new({
|
||||||
|
let target = target.clone();
|
||||||
|
move|c: &_, s: &_|if let Ok(mut target) = target.write() {
|
||||||
|
target.process(c, s)
|
||||||
|
} else {
|
||||||
|
Control::Quit
|
||||||
|
}
|
||||||
|
});
|
||||||
|
let frames = contrib::ClosureProcessHandler::new(frame as BoxedAudioHandler);
|
||||||
|
let mut buffer = Self::Activating;
|
||||||
|
std::mem::swap(&mut*client.write().unwrap(), &mut buffer);
|
||||||
|
*client.write().unwrap() = Self::Active(Client::from(buffer).activate_async(events, frames)?);
|
||||||
|
Ok(target)
|
||||||
|
}
|
||||||
|
}
|
||||||
impl From<JackClient> for Client {
|
impl From<JackClient> for Client {
|
||||||
fn from (jack: JackClient) -> Client {
|
fn from (jack: JackClient) -> Client {
|
||||||
match jack {
|
match jack {
|
||||||
|
|
@ -59,23 +78,23 @@ impl AudioEngine for JackClient {
|
||||||
}
|
}
|
||||||
fn activate(
|
fn activate(
|
||||||
self,
|
self,
|
||||||
mut process: impl FnMut(&Arc<RwLock<Self>>, &Client, &ProcessScope) -> Control + Send + 'static,
|
mut cb: impl FnMut(&Arc<RwLock<Self>>, &Client, &ProcessScope) -> Control + Send + 'static,
|
||||||
) -> Usually<Arc<RwLock<Self>>>
|
) -> Usually<Arc<RwLock<Self>>>
|
||||||
where
|
where
|
||||||
Self: Send + Sync + 'static
|
Self: Send + Sync + 'static
|
||||||
{
|
{
|
||||||
let client = Client::from(self);
|
let client = Client::from(self);
|
||||||
let state = Arc::new(RwLock::new(Self::Activating));
|
let state = Arc::new(RwLock::new(Self::Activating));
|
||||||
let event = Box::new(move |_| { /*TODO*/ }) as Box<dyn Fn(JackEvent) + Send + Sync>;
|
let event = Box::new(move|_|{/*TODO*/}) as Box<dyn Fn(JackEvent) + Send + Sync>;
|
||||||
let events = Notifications(event);
|
let events = Notifications(event);
|
||||||
let frame = Box::new({let state = state.clone(); move|c: &_, s: &_|process(&state, c, s)});
|
let frame = Box::new({let state = state.clone(); move|c: &_, s: &_|cb(&state, c, s)});
|
||||||
let frames = contrib::ClosureProcessHandler::new(frame as BoxedAudioHandler);
|
let frames = contrib::ClosureProcessHandler::new(frame as BoxedAudioHandler);
|
||||||
*state.write().unwrap() = Self::Active(client.activate_async(events, frames)?);
|
*state.write().unwrap() = Self::Active(client.activate_async(events, frames)?);
|
||||||
Ok(state)
|
Ok(state)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/// Trait for things that have a JACK process callback.
|
/// Trait for things that have a JACK process callback.
|
||||||
pub trait Audio {
|
pub trait Audio: Send + Sync {
|
||||||
fn process(&mut self, _: &Client, _: &ProcessScope) -> Control {
|
fn process(&mut self, _: &Client, _: &ProcessScope) -> Control {
|
||||||
Control::Continue
|
Control::Continue
|
||||||
}
|
}
|
||||||
|
|
@ -120,12 +139,6 @@ pub trait AudioComponent<E: Engine>: Component<E> + Audio {
|
||||||
/// All things that implement the required traits can be treated as `AudioComponent`.
|
/// All things that implement the required traits can be treated as `AudioComponent`.
|
||||||
impl<E: Engine, W: Component<E> + Audio> AudioComponent<E> for W {}
|
impl<E: Engine, W: Component<E> + Audio> AudioComponent<E> for W {}
|
||||||
|
|
||||||
pub type DynamicAsyncClient = AsyncClient<DynamicNotifications, DynamicAudioHandler>;
|
|
||||||
|
|
||||||
pub type DynamicAudioHandler = contrib::ClosureProcessHandler<(), BoxedAudioHandler>;
|
|
||||||
|
|
||||||
pub type BoxedAudioHandler = Box<dyn FnMut(&Client, &ProcessScope) -> Control + Send>;
|
|
||||||
|
|
||||||
/// `JackDevice` factory. Creates JACK `Client`s, performs port registration
|
/// `JackDevice` factory. Creates JACK `Client`s, performs port registration
|
||||||
/// and activation, and encapsulates a `AudioComponent` into a `JackDevice`.
|
/// and activation, and encapsulates a `AudioComponent` into a `JackDevice`.
|
||||||
pub struct Jack {
|
pub struct Jack {
|
||||||
|
|
|
||||||
|
|
@ -42,7 +42,7 @@ impl Plugin<Tui> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<E> Audio for Plugin<E> {
|
impl<E: Send + Sync> Audio for Plugin<E> {
|
||||||
fn process (&mut self, _: &Client, scope: &ProcessScope) -> Control {
|
fn process (&mut self, _: &Client, scope: &ProcessScope) -> Control {
|
||||||
match self.plugin.as_mut() {
|
match self.plugin.as_mut() {
|
||||||
Some(PluginKind::LV2(LV2Plugin {
|
Some(PluginKind::LV2(LV2Plugin {
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@ use crate::*;
|
||||||
/// Root level object for standalone `tek_arranger`
|
/// Root level object for standalone `tek_arranger`
|
||||||
pub struct Arranger<E: Engine> {
|
pub struct Arranger<E: Engine> {
|
||||||
/// JACK client handle (needs to not be dropped for standalone mode to work).
|
/// JACK client handle (needs to not be dropped for standalone mode to work).
|
||||||
pub jack: Option<Arc<JackClient>>,
|
pub jack: Arc<RwLock<JackClient>>,
|
||||||
/// Which view is focused
|
/// Which view is focused
|
||||||
pub focus_cursor: (usize, usize),
|
pub focus_cursor: (usize, usize),
|
||||||
/// Controls the JACK transport.
|
/// Controls the JACK transport.
|
||||||
|
|
@ -51,7 +51,7 @@ pub enum ArrangerStatusBar {
|
||||||
/// Represents the tracks and scenes of the composition.
|
/// Represents the tracks and scenes of the composition.
|
||||||
pub struct Arrangement<E: Engine> {
|
pub struct Arrangement<E: Engine> {
|
||||||
/// Global JACK client
|
/// Global JACK client
|
||||||
pub jack: Arc<JackClient>,
|
pub jack: Arc<RwLock<JackClient>>,
|
||||||
/// Global timebase
|
/// Global timebase
|
||||||
pub clock: Arc<TransportTime>,
|
pub clock: Arc<TransportTime>,
|
||||||
/// Name of arranger
|
/// Name of arranger
|
||||||
|
|
@ -120,26 +120,27 @@ pub struct HorizontalArranger<'a, E: Engine>(pub &'a Arrangement<E>);
|
||||||
/// General methods for arranger
|
/// General methods for arranger
|
||||||
impl<E: Engine> Arranger<E> {
|
impl<E: Engine> Arranger<E> {
|
||||||
pub fn new (
|
pub fn new (
|
||||||
|
jack: &Arc<RwLock<JackClient>>,
|
||||||
transport: Option<Arc<RwLock<TransportToolbar<E>>>>,
|
transport: Option<Arc<RwLock<TransportToolbar<E>>>>,
|
||||||
arrangement: Arrangement<E>,
|
arrangement: Arrangement<E>,
|
||||||
phrases: Arc<RwLock<PhrasePool<E>>>,
|
phrases: Arc<RwLock<PhrasePool<E>>>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let mut app = Self {
|
let mut app = Self {
|
||||||
jack: None,
|
jack: jack.clone(),
|
||||||
focus_cursor: (0, 1),
|
focus_cursor: (0, 1),
|
||||||
phrases_split: 20,
|
phrases_split: 20,
|
||||||
arrangement_split: 21,
|
arrangement_split: 21,
|
||||||
editor: PhraseEditor::new(),
|
editor: PhraseEditor::new(),
|
||||||
status: ArrangerStatusBar::ArrangementClip,
|
status: ArrangerStatusBar::ArrangementClip,
|
||||||
|
transport: transport.clone(),
|
||||||
|
arrangement,
|
||||||
|
phrases,
|
||||||
|
size: Measure::new(),
|
||||||
clock: if let Some(ref transport) = transport {
|
clock: if let Some(ref transport) = transport {
|
||||||
transport.read().unwrap().clock.clone()
|
transport.read().unwrap().clock.clone()
|
||||||
} else {
|
} else {
|
||||||
Arc::new(TransportTime::default())
|
Arc::new(TransportTime::default())
|
||||||
},
|
},
|
||||||
transport,
|
|
||||||
arrangement,
|
|
||||||
phrases,
|
|
||||||
size: Measure::new(),
|
|
||||||
};
|
};
|
||||||
app.update_focus();
|
app.update_focus();
|
||||||
app
|
app
|
||||||
|
|
@ -226,7 +227,7 @@ impl<E: Engine> FocusGrid<ArrangerFocus> for Arranger<E> {
|
||||||
/// General methods for arrangement
|
/// General methods for arrangement
|
||||||
impl<E: Engine> Arrangement<E> {
|
impl<E: Engine> Arrangement<E> {
|
||||||
pub fn new (
|
pub fn new (
|
||||||
jack: &Arc<JackClient>,
|
jack: &Arc<RwLock<JackClient>>,
|
||||||
clock: &Arc<TransportTime>,
|
clock: &Arc<TransportTime>,
|
||||||
name: &str,
|
name: &str,
|
||||||
phrases: &Arc<RwLock<PhrasePool<E>>>
|
phrases: &Arc<RwLock<PhrasePool<E>>>
|
||||||
|
|
@ -537,16 +538,16 @@ impl<E: Engine> Arrangement<E> {
|
||||||
}
|
}
|
||||||
impl ArrangementTrack {
|
impl ArrangementTrack {
|
||||||
pub fn new (
|
pub fn new (
|
||||||
jack: &Arc<JackClient>,
|
jack: &Arc<RwLock<JackClient>>,
|
||||||
clock: &Arc<TransportTime>,
|
clock: &Arc<TransportTime>,
|
||||||
name: &str,
|
name: &str,
|
||||||
color: Option<ItemColor>
|
color: Option<ItemColor>
|
||||||
) -> Self {
|
) -> Self {
|
||||||
Self {
|
Self {
|
||||||
name: Arc::new(RwLock::new(name.into())),
|
name: Arc::new(RwLock::new(name.into())),
|
||||||
width: name.len() + 2,
|
width: name.len() + 2,
|
||||||
color: color.unwrap_or_else(ItemColor::random),
|
color: color.unwrap_or_else(ItemColor::random),
|
||||||
player: PhrasePlayer::new(jack, clock),
|
player: PhrasePlayer::new(&jack, clock),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub fn longest_name (tracks: &[Self]) -> usize {
|
pub fn longest_name (tracks: &[Self]) -> usize {
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,7 @@ pub type PhraseChunk = [Vec<PhraseMessage>];
|
||||||
/// Root level object for standalone `tek_sequencer`
|
/// Root level object for standalone `tek_sequencer`
|
||||||
pub struct Sequencer<E: Engine> {
|
pub struct Sequencer<E: Engine> {
|
||||||
/// JACK client handle (needs to not be dropped for standalone mode to work).
|
/// JACK client handle (needs to not be dropped for standalone mode to work).
|
||||||
pub jack: Option<Arc<JackClient>>,
|
pub jack: Arc<RwLock<JackClient>>,
|
||||||
/// Which view is focused
|
/// Which view is focused
|
||||||
pub focus_cursor: (usize, usize),
|
pub focus_cursor: (usize, usize),
|
||||||
/// Controls the JACK transport.
|
/// Controls the JACK transport.
|
||||||
|
|
@ -392,7 +392,7 @@ impl PartialEq for Phrase { fn eq (&self, other: &Self) -> bool { self.uuid == o
|
||||||
impl Eq for Phrase {}
|
impl Eq for Phrase {}
|
||||||
impl PhrasePlayer {
|
impl PhrasePlayer {
|
||||||
pub fn new (
|
pub fn new (
|
||||||
jack: &Arc<JackClient>,
|
jack: &Arc<RwLock<JackClient>>,
|
||||||
clock: &Arc<TransportTime>
|
clock: &Arc<TransportTime>
|
||||||
) -> Self {
|
) -> Self {
|
||||||
Self {
|
Self {
|
||||||
|
|
|
||||||
|
|
@ -18,39 +18,31 @@ pub struct SequencerCli {
|
||||||
|
|
||||||
impl SequencerCli {
|
impl SequencerCli {
|
||||||
fn run (&self) -> Usually<()> {
|
fn run (&self) -> Usually<()> {
|
||||||
let jack = Client::new("tek_arranger", ClientOptions::NO_START_SERVER)?.0;
|
let name = self.name.map(|name|name.as_str()).unwrap_or("tek_arranger");
|
||||||
let jack = JackClient::Inactive(jack);
|
Tui::run(JackClient::new(name)?.activate_with(|jack|{
|
||||||
let transport = Arc::new(RwLock::new(TransportToolbar::new(None, Some(jack.transport()))));
|
let mut transport = TransportToolbar::new(jack, None);
|
||||||
let clock = transport.read().unwrap().clock.clone();
|
let sequencer = Sequencer {
|
||||||
let sequencer = Sequencer {
|
jack: jack.clone(),
|
||||||
jack: None,
|
focus_cursor: (1, 1),
|
||||||
player: None,
|
phrases: Arc::new(RwLock::new(PhrasePool::new())),
|
||||||
focus_cursor: (1, 1),
|
editor: PhraseEditor::new(),
|
||||||
phrases: Arc::new(RwLock::new(PhrasePool::new())),
|
clock: transport.clock.clone(),
|
||||||
editor: PhraseEditor::new(),
|
player: Some(PhrasePlayer::new(jack, &transport.clock)),
|
||||||
transport: self.transport.then_some(transport),
|
transport: self.transport.then_some(Arc::new(RwLock::new(transport))),
|
||||||
clock,
|
};
|
||||||
};
|
if let Some(_) = self.name.as_ref() {
|
||||||
if let Some(_) = self.name.as_ref() {
|
// TODO: sequencer.name = Arc::new(RwLock::new(name.clone()));
|
||||||
// TODO: sequencer.name = Arc::new(RwLock::new(name.clone()));
|
}
|
||||||
}
|
if let Some(_) = self.ppq {
|
||||||
if let Some(_) = self.ppq {
|
// TODO: sequencer.ppq = ppq;
|
||||||
// TODO: sequencer.ppq = ppq;
|
}
|
||||||
}
|
if let Some(_) = self.length {
|
||||||
if let Some(_) = self.length {
|
// TODO: if let Some(phrase) = sequencer.phrase.as_mut() {
|
||||||
// TODO: if let Some(phrase) = sequencer.phrase.as_mut() {
|
//phrase.write().unwrap().length = length;
|
||||||
//phrase.write().unwrap().length = length;
|
//}
|
||||||
//}
|
}
|
||||||
}
|
sequencer
|
||||||
let sequencer = Arc::new(RwLock::new(sequencer));
|
}))?;
|
||||||
let jack = jack.activate(&sequencer.clone(), Sequencer::callback)?;
|
Ok(())
|
||||||
let jack = Arc::new(jack);
|
|
||||||
let player = PhrasePlayer::new(&jack, &clock);
|
|
||||||
if let Some(ref transport) = sequencer.read().unwrap().transport {
|
|
||||||
transport.write().unwrap().jack = Some(jack.clone());
|
|
||||||
}
|
|
||||||
sequencer.write().unwrap().jack = Some(jack.clone());
|
|
||||||
sequencer.write().unwrap().player = player;
|
|
||||||
Tui::run(sequencer).map(|_|())
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -26,14 +26,14 @@ impl TransportTime {
|
||||||
/// Stores and displays time-related state.
|
/// Stores and displays time-related state.
|
||||||
pub struct TransportToolbar<E: Engine> {
|
pub struct TransportToolbar<E: Engine> {
|
||||||
_engine: PhantomData<E>,
|
_engine: PhantomData<E>,
|
||||||
|
/// JACK client handle (needs to not be dropped for standalone mode to work).
|
||||||
|
pub jack: Arc<RwLock<JackClient>>,
|
||||||
|
/// JACK transport handle.
|
||||||
|
pub transport: Transport,
|
||||||
/// Current sample rate, tempo, and PPQ.
|
/// Current sample rate, tempo, and PPQ.
|
||||||
pub clock: Arc<TransportTime>,
|
pub clock: Arc<TransportTime>,
|
||||||
/// Enable metronome?
|
/// Enable metronome?
|
||||||
pub metronome: bool,
|
pub metronome: bool,
|
||||||
/// JACK client handle (needs to not be dropped for standalone mode to work).
|
|
||||||
pub jack: Option<Arc<JackClient>>,
|
|
||||||
/// JACK transport handle.
|
|
||||||
pub transport: Option<Transport>,
|
|
||||||
/// Whether the toolbar is focused
|
/// Whether the toolbar is focused
|
||||||
pub focused: bool,
|
pub focused: bool,
|
||||||
/// Which part of the toolbar is focused
|
/// Which part of the toolbar is focused
|
||||||
|
|
@ -43,17 +43,14 @@ pub struct TransportToolbar<E: Engine> {
|
||||||
#[derive(Clone, Copy, PartialEq)]
|
#[derive(Clone, Copy, PartialEq)]
|
||||||
pub enum TransportToolbarFocus { Bpm, Sync, PlayPause, Clock, Quant, }
|
pub enum TransportToolbarFocus { Bpm, Sync, PlayPause, Clock, Quant, }
|
||||||
impl<E: Engine> TransportToolbar<E> {
|
impl<E: Engine> TransportToolbar<E> {
|
||||||
pub fn new (
|
pub fn new (jack: &Arc<RwLock<JackClient>>, clock: Option<&Arc<TransportTime>>) -> Self {
|
||||||
clock: Option<&Arc<TransportTime>>,
|
|
||||||
transport: Option<Transport>,
|
|
||||||
) -> Self {
|
|
||||||
Self {
|
Self {
|
||||||
_engine: Default::default(),
|
_engine: Default::default(),
|
||||||
focused: false,
|
focused: false,
|
||||||
focus: TransportToolbarFocus::PlayPause,
|
focus: TransportToolbarFocus::PlayPause,
|
||||||
metronome: false,
|
metronome: false,
|
||||||
jack: None,
|
transport: jack.read().unwrap().transport(),
|
||||||
transport,
|
jack: jack.clone(),
|
||||||
clock: match clock {
|
clock: match clock {
|
||||||
Some(clock) => clock.clone(),
|
Some(clock) => clock.clone(),
|
||||||
None => {
|
None => {
|
||||||
|
|
@ -70,16 +67,15 @@ impl<E: Engine> TransportToolbar<E> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub fn toggle_play (&mut self) -> Usually<()> {
|
pub fn toggle_play (&mut self) -> Usually<()> {
|
||||||
let transport = self.transport.as_ref().unwrap();
|
|
||||||
let playing = self.clock.playing.read().unwrap().expect("1st sample has not been processed yet");
|
let playing = self.clock.playing.read().unwrap().expect("1st sample has not been processed yet");
|
||||||
let playing = match playing {
|
let playing = match playing {
|
||||||
TransportState::Stopped => {
|
TransportState::Stopped => {
|
||||||
transport.start()?;
|
self.transport.start()?;
|
||||||
Some(TransportState::Starting)
|
Some(TransportState::Starting)
|
||||||
},
|
},
|
||||||
_ => {
|
_ => {
|
||||||
transport.stop()?;
|
self.transport.stop()?;
|
||||||
transport.locate(0)?;
|
self.transport.locate(0)?;
|
||||||
Some(TransportState::Stopped)
|
Some(TransportState::Stopped)
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -1,14 +1,10 @@
|
||||||
include!("lib.rs");
|
include!("lib.rs");
|
||||||
/// Application entrypoint.
|
/// Application entrypoint.
|
||||||
pub fn main () -> Usually<()> {
|
pub fn main () -> Usually<()> {
|
||||||
let jack = JackClient::Inactive(
|
Tui::run(JackClient::new("tek_transport")?.activate_with(|jack|{
|
||||||
Client::new("tek_transport", ClientOptions::NO_START_SERVER)?.0
|
let mut transport = TransportToolbar::new(jack, None);
|
||||||
);
|
transport.focused = true;
|
||||||
let mut transport = TransportToolbar::new(None, Some(jack.transport()));
|
transport
|
||||||
transport.focused = true;
|
}))?;
|
||||||
let transport = Arc::new(RwLock::new(transport));
|
|
||||||
let jack = jack.activate(&transport.clone(), TransportToolbar::callback)?;
|
|
||||||
transport.write().unwrap().jack = Some(jack.into());
|
|
||||||
Tui::run(transport)?;
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,10 @@
|
||||||
use crate::*;
|
use crate::*;
|
||||||
impl<E: Engine> Audio for TransportToolbar<E> {
|
impl<E: Engine> Audio for TransportToolbar<E> {
|
||||||
fn process (&mut self, _: &Client, scope: &ProcessScope) -> Control {
|
fn process (&mut self, _: &Client, scope: &ProcessScope) -> Control {
|
||||||
let times = scope.cycle_times().unwrap();
|
let times = scope.cycle_times().unwrap();
|
||||||
let CycleTimes { current_frames, current_usecs, next_usecs: _, period_usecs: _ } = times;
|
let CycleTimes { current_frames, current_usecs, next_usecs: _, period_usecs: _ } = times;
|
||||||
let _chunk_size = scope.n_frames() as usize;
|
let _chunk_size = scope.n_frames() as usize;
|
||||||
let transport = self.transport.as_ref().unwrap().query().unwrap();
|
let transport = self.transport.query().unwrap();
|
||||||
self.clock.current.sample.set(transport.pos.frame() as f64);
|
self.clock.current.sample.set(transport.pos.frame() as f64);
|
||||||
let mut playing = self.clock.playing.write().unwrap();
|
let mut playing = self.clock.playing.write().unwrap();
|
||||||
let mut started = self.clock.started.write().unwrap();
|
let mut started = self.clock.started.write().unwrap();
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue