mirror of
https://codeberg.org/uzu/strudel
synced 2026-07-22 05:05:26 -04:00
webaudio for tauri WIP
This commit is contained in:
@@ -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';
|
||||
export * from './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);
|
||||
};
|
||||
return this.onTrigger(webaudioDesktopOutputTrigger);
|
||||
};
|
||||
|
||||
Generated
-4
@@ -1,9 +1,5 @@
|
||||
lockfileVersion: '6.0'
|
||||
|
||||
settings:
|
||||
autoInstallPeers: true
|
||||
excludeLinksFromLockfile: false
|
||||
|
||||
importers:
|
||||
|
||||
.:
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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<f64>,
|
||||
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),
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
|
||||
|
||||
Reference in New Issue
Block a user