diff --git a/packages/desktopbridge/index.mjs b/packages/desktopbridge/index.mjs index ba3051ece..30b54846e 100644 --- a/packages/desktopbridge/index.mjs +++ b/packages/desktopbridge/index.mjs @@ -7,4 +7,4 @@ This program is free software: you can redistribute it and/or modify it under th export * from './midibridge.mjs'; export * from './utils.mjs'; export * from './oscbridge.mjs'; -export * from './webaudiobridge.mjs'; \ No newline at end of file +export * from './webaudiobridge.mjs'; diff --git a/packages/desktopbridge/webaudiobridge.mjs b/packages/desktopbridge/webaudiobridge.mjs index 0ef179533..3bf2961e2 100644 --- a/packages/desktopbridge/webaudiobridge.mjs +++ b/packages/desktopbridge/webaudiobridge.mjs @@ -1,109 +1,110 @@ import { Invoke } from './utils.mjs'; -import {duration, isNote, midiToFreq, noteToMidi, Pattern} from '@strudel.cycles/core'; -import {getAudioContext} from "@strudel.cycles/webaudio"; +import { duration, isNote, midiToFreq, noteToMidi, Pattern } from '@strudel.cycles/core'; +import { getAudioContext } from '@strudel.cycles/webaudio'; export const desktopAudio = async (value, deadline, hapDuration) => { - const ac = getAudioContext(); - if (typeof value !== 'object') { - throw new Error( - `expected hap.value to be an object, but got "${value}". Hint: append .note() or .s() to the end`, - 'error', - ); - } + const ac = getAudioContext(); + if (typeof value !== 'object') { + throw new Error( + `expected hap.value to be an object, but got "${value}". Hint: append .note() or .s() to the end`, + 'error', + ); + } - let t = ac.currentTime + deadline; + let t = ac.currentTime + deadline; - let { - note = "C3", - s = 'triangle', - bank = '', - source, - gain = 0.8, - // low pass - cutoff = 8000, - resonance = 1, - // high pass - hcutoff = 0, - hresonance = 1, - // band pass - bandf = 0, - bandq = 1, - // - coarse, - crush, - shape, - pan, - vowel, - delay = 0, - delayfeedback = 0.5, - delaytime = 0.25, - orbit = 1, - room, - size = 2, - velocity = 1, - analyze, // analyser wet - fft = 8, // fftSize 0 - 10 - speed = 1, // sample playback speed - begin = 0, - end = 1, - loop = 0, - loopBegin = 0, - loopEnd = 1, - } = value; + let { + note = 'C3', + s = 'triangle', + bank = '', + source, + gain = 0.8, + // low pass + cutoff = 8000, + resonance = 1, + // high pass + hcutoff = 0, + hresonance = 1, + // band pass + bandf = 0, + bandq = 1, + // + coarse, + crush, + shape, + pan, + vowel, + delay = 0, + delayfeedback = 0.5, + delaytime = 0.25, + orbit = 1, + room, + size = 2, + velocity = 1, + analyze, // analyser wet + fft = 8, // fftSize 0 - 10 + speed = 1, // sample playback speed + begin = 0, + end = 1, + loop = 0, + loopBegin = 0, + loopEnd = 1, + } = value; - console.log(value); - value.duration = hapDuration; - if (isNote(note)) { - note = noteToMidi(note); - } + console.log(value); + value.duration = hapDuration; + if (isNote(note)) { + note = noteToMidi(note); + } - if (delay !== 0) { - delay = Math.abs(delay); - delayfeedback = Math.abs(delayfeedback); - delaytime = Math.abs(delaytime); - } + if (delay !== 0) { + delay = Math.abs(delay); + delayfeedback = Math.abs(delayfeedback); + delaytime = Math.abs(delaytime); + } - const loop_packaged = [loop, loopBegin, loopEnd]; - const delay_packaged = [delay, delayfeedback, delaytime]; + const loop_packaged = [loop, loopBegin, loopEnd]; + const delay_packaged = [delay, delayfeedback, delaytime]; - const offset = (t - getAudioContext().currentTime) * 1000; - const roundedOffset = Math.round(offset); - const messagesfromjs = []; + const offset = (t - getAudioContext().currentTime) * 1000; + const roundedOffset = Math.round(offset); + const messagesfromjs = []; - messagesfromjs.push({ - note: midiToFreq(note), - offset: roundedOffset, - waveform: s, - bank: bank, - cutoff: cutoff, - resonance: resonance, - hcutoff: hcutoff, - hresonance: hresonance, - bandf: bandf, - bandq: bandq, - duration: hapDuration, - velocity: velocity, - delay: delay_packaged, - speed: speed, - begin: begin, - end: end, - loop_packaged: loop_packaged, + messagesfromjs.push({ + note: midiToFreq(note), + offset: roundedOffset, + waveform: s, + bank: bank, + cutoff: cutoff, + resonance: resonance, + hcutoff: hcutoff, + hresonance: hresonance, + bandf: bandf, + bandq: bandq, + duration: hapDuration, + velocity: velocity, + delay: delay_packaged, + speed: speed, + begin: begin, + end: end, + loop_packaged: loop_packaged, + }); + if (messagesfromjs.length) { + setTimeout(() => { + Invoke('sendwebaudio', { messagesfromjs }); }); - - if (messagesfromjs.length) { - setTimeout(() => { - Invoke('sendwebaudio', { messagesfromjs }); - }); - } -} -const hap2value = (hap) => { - hap.ensureObjectValue(); - return { ...hap.value, velocity: hap.context.velocity }; + } }; -export const webaudioDesktopOutputTrigger = (t, hap, ct, cps) => desktopAudio(hap2value(hap), t - ct, hap.duration / cps, cps); -export const webaudioDesktopOutput = (hap, deadline, hapDuration) => desktopAudio(hap2value(hap), deadline, hapDuration); +const hap2value = (hap) => { + hap.ensureObjectValue(); + return { ...hap.value, velocity: hap.context.velocity }; +}; +export const webaudioDesktopOutputTrigger = (t, hap, ct, cps) => + desktopAudio(hap2value(hap), t - ct, hap.duration / cps, cps); +export const webaudioDesktopOutput = (hap, deadline, hapDuration) => + desktopAudio(hap2value(hap), deadline, hapDuration); Pattern.prototype.webaudio = function () { - return this.onTrigger(webaudioDesktopOutputTrigger); -}; \ No newline at end of file + return this.onTrigger(webaudioDesktopOutputTrigger); +}; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 437058f98..ec8ccfb0a 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1,9 +1,5 @@ lockfileVersion: '6.0' -settings: - autoInstallPeers: true - excludeLinksFromLockfile: false - importers: .: diff --git a/src-tauri/src/main.rs b/src-tauri/src/main.rs index acc39824f..d173910e5 100644 --- a/src-tauri/src/main.rs +++ b/src-tauri/src/main.rs @@ -10,8 +10,11 @@ use std::sync::Arc; use loggerbridge::Logger; use tauri::Manager; -use tokio::sync::mpsc; -use tokio::sync::Mutex; +use tokio::{ + sync::mpsc, + sync::Mutex +}; + // the payload type must implement `Serialize` and `Clone`. #[derive(Clone, serde::Serialize)] struct Payload { diff --git a/src-tauri/src/webaudiobridge.rs b/src-tauri/src/webaudiobridge.rs index 95eb993a3..1f0c5e002 100644 --- a/src-tauri/src/webaudiobridge.rs +++ b/src-tauri/src/webaudiobridge.rs @@ -16,6 +16,13 @@ pub struct Delay { pub feedback: f64, } +#[derive(Clone, Copy, Debug)] +pub struct Loop { + pub is_loop: u8, + pub loop_start: f64, + pub loop_end: f64, +} + #[derive(Debug)] pub struct WebAudioMessage { pub note: f32, @@ -35,7 +42,7 @@ pub struct WebAudioMessage { pub speed: f32, pub begin: f64, pub end: f64, - pub loop_packaged: (u8, f64, f64), + pub loop_packaged: Loop, } pub struct AsyncInputTransmitWebAudio { @@ -125,7 +132,7 @@ pub struct MessageFromJS { bandq: f32, duration: f64, velocity: f32, - delay: Vec, + delay: (f64, f64, f64), speed: f32, begin: f64, end: f64, @@ -156,14 +163,18 @@ pub async fn sendwebaudio( duration: m.duration, velocity: m.velocity, delay: Delay { - wet: m.delay[0], - delay_time: m.delay[1], - feedback: m.delay[2], + wet: m.delay.0, + delay_time: m.delay.1, + feedback: m.delay.2, }, speed: m.speed, begin: m.begin, end: m.end, - loop_packaged: m.loop_packaged, + loop_packaged: Loop { + is_loop: m.loop_packaged.0, + loop_start: m.loop_packaged.1, + loop_end: m.loop_packaged.2, + }, }; messages_to_process.push(message_to_process); } @@ -200,15 +211,15 @@ fn superdough(message: &WebAudioMessage, context: &mut AudioContext) { src.connect(&lpf); connect_filters_to_envelope(&env, message, &hpf, &bandpass, now); src.playback_rate().set_value(message.speed); - if message.loop_packaged.0 == 1 { + if message.loop_packaged.is_loop > 0 { src.set_loop(true); - src.set_loop_start(message.loop_packaged.1); - src.set_loop_end(message.loop_packaged.2); + src.set_loop_start(message.loop_packaged.loop_start); + src.set_loop_end(message.loop_packaged.loop_end); src.start_at_with_offset_and_duration(now, src.loop_start(), audio_buffer_duration / message.speed as f64); - }; - // println!("{}", message.is_loop); - // src.start_at_with_offset_and_duration(now, message.begin * audio_buffer_duration, audio_buffer_duration / message.speed as f64); - src.stop_at(now + 2.0); + } else { + src.start_at_with_offset_and_duration(now, message.begin * audio_buffer_duration, audio_buffer_duration / message.speed as f64); + } + // src.stop_at(now + 2.0); }, Err(e) => eprintln!("Failed to open file: {:?}", e), } diff --git a/website/src/repl/Repl.jsx b/website/src/repl/Repl.jsx index 99385a7ec..0ddd44d2b 100644 --- a/website/src/repl/Repl.jsx +++ b/website/src/repl/Repl.jsx @@ -7,7 +7,7 @@ This program is free software: you can redistribute it and/or modify it under th import { cleanupDraw, cleanupUi, controls, evalScope, getDrawContext, logger } from '@strudel.cycles/core'; import { CodeMirror, cx, flash, useHighlighting, useStrudel, useKeydown } from '@strudel.cycles/react'; import { getAudioContext, initAudioOnFirstClick, resetLoadedSounds, webaudioOutput } from '@strudel.cycles/webaudio'; -import { webaudioDesktopOutput } from "@strudel/desktopbridge"; +import { webaudioDesktopOutput } from '@strudel/desktopbridge'; import { createClient } from '@supabase/supabase-js'; import { nanoid } from 'nanoid'; import React, { createContext, useCallback, useEffect, useState, useMemo } from 'react'; @@ -23,7 +23,6 @@ import Loader from './Loader'; import { settingPatterns } from '../settings.mjs'; import { code2hash, hash2code } from './helpers.mjs'; import { isTauri } from '../tauri.mjs'; -import {webaudioDesktopOutput} from "@strudel/desktopbridge"; const { latestCode } = settingsMap.get();