cleaning up

This commit is contained in:
Jade Rowland
2023-10-01 00:46:40 -04:00
parent d5c6cb85ea
commit 1d85d60b11
4 changed files with 142 additions and 113 deletions
+19 -7
View File
@@ -6,20 +6,31 @@ import { listen } from '@tauri-apps/api/event';
export class CyclistBridge extends Cyclist {
constructor(params) {
super(params);
this.start_timer;
this.abeLinkListener = listen('abelink-event', async (e) => {
const payload = e?.payload;
if (payload == null) {
return;
}
const { started, cps, timestamp } = payload;
// (if bpm !== prev_bpm) {
//update the clock
const { started, cps, phase, timestamp } = payload;
// TODO: I'm not sure how to hook this up this cps adjustment in Strudel
// (if cps !== clock_cps) {
// updateClock(cps)
// }
// TODO: I'm not sure how to hook this up this phase adjustment in Strudel
// a phase adjustment message is sent every 30 seconds from backend to keep clocks in sync
// if (Math.abs(phase - this.clock.getPhase()) > someDelta) {
// setCyclistPhase(phase)
// }
if (this.started !== started && started != null) {
if (started) {
// when start message comes from abelink, delay starting cyclist clock until the start of the next abelink phase
this.start_timer = window.setTimeout(() => {
// TODO: evaluate the code so if another source triggers the play there will not be an error
logger('[cyclist] start');
this.clock.start();
this.setStarted(true);
@@ -28,8 +39,6 @@ export class CyclistBridge extends Cyclist {
this.stop();
}
}
const { message, message_type } = e.payload;
});
}
@@ -37,11 +46,12 @@ export class CyclistBridge extends Cyclist {
if (!this.pattern) {
throw new Error('Scheduler: no pattern set! call .setPattern first.');
}
const linkmsg = {
// TODO: change this to value of "main" clock cps
cps: 0.5,
started: true,
timestamp: Date.now(),
phase: this.clock.getPhase(),
};
Invoke('sendabelinkmsg', { linkmsg });
}
@@ -52,9 +62,11 @@ export class CyclistBridge extends Cyclist {
this.lastEnd = 0;
this.setStarted(false);
const linkmsg = {
// TODO: change this to value of "main" clock cps
cps: 0.5,
started: false,
timestamp: Date.now(),
phase: this.clock.getPhase(),
};
Invoke('sendabelinkmsg', { linkmsg });
}
+1 -22
View File
@@ -3,7 +3,7 @@ import { repl } from '@strudel.cycles/core';
import { transpiler } from '@strudel.cycles/transpiler';
import usePatternFrame from './usePatternFrame';
import usePostMessage from './usePostMessage.mjs';
import { listen } from '@tauri-apps/api/event';
function useStrudel({
defaultOutput,
interval,
@@ -67,7 +67,6 @@ function useStrudel({
}),
[defaultOutput, interval, getTime],
);
const broadcast = usePostMessage(({ data: { from, type } }) => {
if (type === 'start' && from !== id) {
// console.log('message', from, type);
@@ -127,26 +126,6 @@ function useStrudel({
await activateCode();
}
};
// listen('abelink-event', async (e) => {
// const payload = e?.payload;
// if (payload == null) {
// return;
// }
// const { play, bpm, timestamp } = payload;
// if (started !== play && play != null) {
// if (play) {
// // activateCode();
// start();
// } else {
// stop();
// }
// }
// const { message, message_type } = e.payload;
// });
const error = schedulerError || evalError;
usePatternFrame({
+116 -77
View File
@@ -9,53 +9,6 @@ use crate::loggerbridge::Logger;
use tauri::Window;
#[derive(Deserialize, Clone, serde::Serialize)]
pub struct LinkMsg {
pub started: bool,
pub cps: f64,
pub timestamp: u64,
}
#[derive(Clone)]
pub struct AbeLinkToJs {
pub window: Arc<Window>,
}
impl AbeLinkToJs {
pub fn send(&self, payload: LinkMsg) {
let _ = self.window.emit("abelink-event", payload);
}
}
pub struct AsyncInputTransmit {
pub abelink: Arc<Mutex<AbeLinkState>>,
}
pub struct AbeLinkState {
pub link: AblLink,
pub session_state: SessionState,
pub running: bool,
pub quantum: f64,
}
impl AbeLinkState {
pub fn new() -> Self {
Self {
link: AblLink::new(120.0),
session_state: SessionState::new(),
running: true,
quantum: 4.0,
}
}
pub fn capture_app_state(&mut self) {
self.link.capture_app_session_state(&mut self.session_state);
}
pub fn commit_app_state(&mut self) {
self.link.commit_app_session_state(&self.session_state);
}
}
fn bpm_to_cps(bpm: f64) -> f64 {
let cpm = bpm / 4.0;
return cpm / 60.0;
@@ -66,15 +19,111 @@ fn cps_to_bpm(cps: f64) -> f64 {
return cpm * 4.0;
}
pub fn init(_logger: Logger, abelink_to_js: AbeLinkToJs, abelink: Arc<Mutex<AbeLinkState>>) {
fn current_unix_time() -> Duration {
let current_unix_time = SystemTime::now().duration_since(UNIX_EPOCH).unwrap();
return current_unix_time;
}
#[derive(Deserialize, Clone, serde::Serialize)]
pub struct LinkMsg {
pub started: bool,
pub cps: f64,
pub timestamp: u64,
pub phase: f64,
}
pub struct AbeLinkStateContainer {
pub abelink: Arc<Mutex<AbeLinkState>>,
}
pub struct AbeLinkState {
pub link: AblLink,
pub session_state: SessionState,
pub running: bool,
pub quantum: f64,
pub window: Arc<Window>,
}
impl AbeLinkState {
pub fn new(window: Arc<Window>) -> Self {
Self {
link: AblLink::new(120.0),
session_state: SessionState::new(),
running: true,
quantum: 4.0,
window,
}
}
pub fn unix_time_at_next_phase(&self) -> u64 {
let link_time_stamp = self.link.clock_micros();
let quantum = self.quantum;
let beat = self.session_state.beat_at_time(link_time_stamp, quantum);
let phase = self.session_state.phase_at_time(link_time_stamp, quantum);
let internal_time_at_next_phase = self.session_state.time_at_beat(beat + (quantum - phase), quantum);
let time_offset = Duration::from_micros((internal_time_at_next_phase - link_time_stamp) as u64);
let current_unix_time = current_unix_time();
let unix_time_at_next_phase = (current_unix_time + time_offset).as_millis() - 140;
return unix_time_at_next_phase as u64;
}
pub fn cps(&self) -> f64 {
let bpm = self.session_state.tempo();
let cps = bpm_to_cps(bpm);
return cps;
}
pub fn capture_app_state(&mut self) {
self.link.capture_app_session_state(&mut self.session_state);
}
pub fn commit_app_state(&mut self) {
self.link.commit_app_session_state(&self.session_state);
}
pub fn send(&self, payload: LinkMsg) {
let _ = self.window.emit("abelink-event", payload);
}
pub fn send_started(&self) {
let cps = self.cps();
let started = self.session_state.is_playing();
let payload = LinkMsg {
cps,
started,
timestamp: self.unix_time_at_next_phase(),
phase: 0.0,
};
self.send(payload);
}
pub fn send_cps(&self) {
let cps = self.cps();
let started = self.session_state.is_playing();
let phase = self.session_state.phase_at_time(self.link.clock_micros(), self.quantum);
let payload = LinkMsg {
cps,
started,
timestamp: current_unix_time().as_millis() as u64,
phase,
};
self.send(payload);
}
pub fn send_phase(&self) {
self.send_started();
}
}
pub fn init(_logger: Logger, abelink: Arc<Mutex<AbeLinkState>>) {
tauri::async_runtime::spawn(async move {
/* ...........................................................
Initialize Ableton link
............................................................*/
let mut prev_is_playing = false;
let mut prev_bpm = 120.0;
let mut prev_is_started = false;
let mut prev_cps = 0.5;
let mut time_since_last_phase_send = 0;
let sleep_time = 10;
/* .......................................................................
Evaluate Abelink State and send messages back to JS side when needed.
........................................................................*/
loop {
let mut state = abelink.lock().await;
if state.link.is_enabled() == false {
@@ -82,42 +131,32 @@ pub fn init(_logger: Logger, abelink_to_js: AbeLinkToJs, abelink: Arc<Mutex<AbeL
state.link.enable_start_stop_sync(true);
}
let link_time_stamp = state.link.clock_micros();
let bpm = state.session_state.tempo();
let cps = bpm_to_cps(bpm);
let started = state.session_state.is_playing();
let quantum = state.quantum;
let beat = state.session_state.beat_at_time(link_time_stamp, quantum);
let phase = state.session_state.phase_at_time(link_time_stamp, quantum);
let time_at_next_cycle = state.session_state.time_at_beat(beat + (quantum - phase), quantum);
let time_offset = Duration::from_micros((time_at_next_cycle - link_time_stamp) as u64);
let current_unix_time = SystemTime::now().duration_since(UNIX_EPOCH).unwrap();
let message_timestamp = (current_unix_time + time_offset).as_millis() - 140;
state.capture_app_state();
if bpm != prev_bpm || started != prev_is_playing {
let payload = LinkMsg {
cps,
started,
timestamp: message_timestamp as u64,
};
abelink_to_js.send(payload);
prev_is_playing = started;
prev_bpm = bpm;
if started != prev_is_started {
state.send_started();
prev_is_started = started;
} else if state.cps() != prev_cps {
state.send_cps();
prev_cps = state.cps();
// a phase sync message needs to be sent to strudel every 30 seconds to keep clock drift at bay
} else if time_since_last_phase_send > 30000 {
state.send_phase();
time_since_last_phase_send = 0;
}
drop(state);
sleep(Duration::from_millis(10));
sleep(Duration::from_millis(sleep_time));
time_since_last_phase_send = time_since_last_phase_send + sleep_time;
}
});
}
// Called from JS
#[tauri::command]
pub async fn sendabelinkmsg(linkmsg: LinkMsg, state: tauri::State<'_, AsyncInputTransmit>) -> Result<(), String> {
pub async fn sendabelinkmsg(linkmsg: LinkMsg, state: tauri::State<'_, AbeLinkStateContainer>) -> Result<(), String> {
let mut abelink = state.abelink.lock().await;
let started = abelink.session_state.is_playing();
let time_stamp = abelink.link.clock_micros();
+6 -7
View File
@@ -8,7 +8,6 @@ mod ablelinkbridge;
use std::sync::Arc;
use ablelinkbridge::AbeLinkState;
use ablelinkbridge::AbeLinkToJs;
use loggerbridge::Logger;
use tauri::Manager;
use tokio::sync::mpsc;
@@ -38,11 +37,6 @@ fn main() {
let window = Arc::new(app.get_window("main").unwrap());
let logger = Logger { window: window.clone() };
let abelink = Arc::new(Mutex::new(AbeLinkState::new()));
app.manage(ablelinkbridge::AsyncInputTransmit {
abelink: abelink.clone(),
});
midibridge::init(
logger.clone(),
async_input_receiver_midi,
@@ -56,7 +50,12 @@ fn main() {
async_output_transmitter_osc
);
ablelinkbridge::init(logger.clone(), AbeLinkToJs { window }, abelink);
// This state must be declared in the setup so it can be shared between invoked commands and the initialized function
let abelink = Arc::new(Mutex::new(AbeLinkState::new(window)));
app.manage(ablelinkbridge::AbeLinkStateContainer {
abelink: abelink.clone(),
});
ablelinkbridge::init(logger.clone(), abelink);
Ok(())
})