This commit is contained in:
Vasilii Milovidov
2023-09-22 15:28:08 +04:00
parent a693f89796
commit 747ea8e6a3
4 changed files with 312 additions and 128 deletions
+156 -121
View File
@@ -1,141 +1,176 @@
import { Invoke } from './utils.mjs';
import { duration, isNote, midiToFreq, noteToMidi, Pattern } from '@strudel.cycles/core';
import { getAudioContext, getEnvelope } from '@strudel.cycles/webaudio';
import {Invoke} from './utils.mjs';
import {duration, isNote, midiToFreq, noteToMidi, Pattern} from '@strudel.cycles/core';
import {getAudioContext, getEnvelope, getSound} 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,
attack = 0.001,
decay = 0.05,
sustain = 1,
release = 0.1,
lpattack = 0.0001,
lpdecay = 0.2,
lpsustain = 0.6,
lprelease = 0.2,
lpenv = 1,
hpattack = 0.0001,
hpdecay = 0.2,
hpsustain = 0.6,
hprelease = 0.2,
hpenv = 1,
bpattack = 0.0001,
bpdecay = 0.2,
bpsustain = 0.6,
bprelease = 0.2,
bpenv = 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,
attack = 0.001,
decay = 0.05,
sustain = 1,
release = 0.1,
lpattack = 0.0001,
lpdecay = 0.2,
lpsustain = 0.6,
lprelease = 0.2,
lpenv = 1,
hpattack = 0.0001,
hpdecay = 0.2,
hpsustain = 0.6,
hprelease = 0.2,
hpenv = 1,
bpattack = 0.0001,
bpdecay = 0.2,
bpsustain = 0.6,
bprelease = 0.2,
bpenv = 1,
n = 0,
} = value;
bank = getSound(s).data.samples;
value.duration = hapDuration;
if (isNote(note)) {
note = noteToMidi(note);
}
// SAMPLES
value.duration = hapDuration;
if (isNote(note)) {
note = noteToMidi(note);
}
const baseUrl = getSound(s).data.baseUrl;
let path;
if (baseUrl === './piano/') {
path = 'https://strudel.tidalcycles.org/';
} else if (baseUrl === './EmuSP12/') {
path = 'https://strudel.tidalcycles.org/';
}
if (delay !== 0) {
delay = Math.abs(delay);
delayfeedback = Math.abs(delayfeedback);
delaytime = Math.abs(delaytime);
}
let transpose = 0;
transpose = note - 36;
let sampleUrl;
let key;
if (Array.isArray(bank)) {
sampleUrl = path !== undefined ? path + bank[n % bank.length].replace('./', '') : bank[n % bank.length].replace('./', '');
} else {
const midiDiff = (noteA) => noteToMidi(noteA) - note;
// object format will expect keys as notes
const closest = Object.keys(bank)
.filter((k) => !k.startsWith('_'))
.reduce(
(closest, key, j) => (!closest || Math.abs(midiDiff(key)) < Math.abs(midiDiff(closest)) ? key : closest),
null,
);
transpose = -midiDiff(closest); // semitones to repitch
sampleUrl = path !== undefined ? path + bank[closest][n % bank[closest].length].replace('./', '') : bank[closest][n % bank[closest].length].replace('./', '');
}
let adsr_on = attack !== 0.001 || decay !== 0.05 || sustain !== 1 || release !== 0.01 ? 1 : 0;
const playbackRate = 1.0 * Math.pow(2, transpose / 12);
// const sound = getSoundData(s);
const packages = {
loop: [loop, loopBegin, loopEnd],
delay: [delay, delaytime, delayfeedback],
lpf: [cutoff, resonance],
hpf: [hcutoff, hresonance],
bpf: [bandf, bandq],
adsr: [attack, decay, sustain, release, adsr_on],
lpenv: [lpattack, lpdecay, lpsustain, lprelease, lpenv],
hpenv: [hpattack, hpdecay, hpsustain, hprelease, hpenv],
bpenv: [bpattack, bpdecay, bpsustain, bprelease, bpenv],
};
// SYNTHS
if (delay !== 0) {
delay = Math.abs(delay);
delayfeedback = Math.abs(delayfeedback);
delaytime = Math.abs(delaytime);
}
const offset = (t - getAudioContext().currentTime) * 1000;
const roundedOffset = Math.round(offset);
const messagesfromjs = [];
let adsr_on = attack !== 0.001 || decay !== 0.05 || sustain !== 1 || release !== 0.01 ? 1 : 0;
messagesfromjs.push({
note: midiToFreq(note),
offset: roundedOffset,
waveform: s,
bank: bank,
lpf: packages.lpf,
hpf: packages.hpf,
bpf: packages.bpf,
duration: hapDuration,
velocity: velocity,
delay: packages.delay,
orbit: orbit,
speed: speed,
begin: begin,
end: end,
looper: packages.loop,
adsr: packages.adsr,
lpenv: packages.lpenv,
hpenv: packages.hpenv,
bpenv: packages.bpenv,
});
const packages = {
loop: [loop, loopBegin, loopEnd],
delay: [delay, delaytime, delayfeedback],
lpf: [cutoff, resonance],
hpf: [hcutoff, hresonance],
bpf: [bandf, bandq],
adsr: [attack, decay, sustain, release, adsr_on],
lpenv: [lpattack, lpdecay, lpsustain, lprelease, lpenv],
hpenv: [hpattack, hpdecay, hpsustain, hprelease, hpenv],
bpenv: [bpattack, bpdecay, bpsustain, bprelease, bpenv],
};
if (messagesfromjs.length) {
setTimeout(() => {
Invoke('sendwebaudio', { 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,
lpf: packages.lpf,
hpf: packages.hpf,
bpf: packages.bpf,
duration: hapDuration,
velocity: velocity,
delay: packages.delay,
orbit: orbit,
speed: speed,
begin: begin,
end: end,
looper: packages.loop,
adsr: packages.adsr,
lpenv: packages.lpenv,
hpenv: packages.hpenv,
bpenv: packages.bpenv,
n: n,
sampleurl: sampleUrl,
});
}
if (messagesfromjs.length) {
setTimeout(() => {
Invoke('sendwebaudio', {messagesfromjs});
});
}
};
const hap2value = (hap) => {
hap.ensureObjectValue();
return { ...hap.value, velocity: hap.context.velocity };
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);
desktopAudio(hap2value(hap), t - ct, hap.duration / cps, cps);
export const webaudioDesktopOutput = (hap, deadline, hapDuration) =>
desktopAudio(hap2value(hap), deadline, hapDuration);
desktopAudio(hap2value(hap), deadline, hapDuration);
Pattern.prototype.webaudio = function () {
return this.onTrigger(webaudioDesktopOutputTrigger);
return this.onTrigger(webaudioDesktopOutputTrigger);
};
+129 -1
View File
@@ -98,6 +98,7 @@ name = "app"
version = "0.1.0"
dependencies = [
"midir",
"mini-moka",
"rosc",
"serde",
"serde_json",
@@ -260,6 +261,12 @@ version = "1.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c3ac9f8b63eca6fd385229b3675f6cc0dc5c8a5c8a54a59d4f52ffd670d87b0c"
[[package]]
name = "bytecount"
version = "0.6.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2c676a478f63e9fa2dd5368a42f28bba0d6c560b775f38583c8bbaa7fcd67c9c"
[[package]]
name = "bytemuck"
version = "1.13.1"
@@ -308,6 +315,37 @@ dependencies = [
"system-deps 6.1.1",
]
[[package]]
name = "camino"
version = "1.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c59e92b5a388f549b863a7bea62612c09f24c8393560709a54558a9abdfb3b9c"
dependencies = [
"serde",
]
[[package]]
name = "cargo-platform"
version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2cfa25e60aea747ec7e1124f238816749faa93759c6ff5b31f1ccdda137f4479"
dependencies = [
"serde",
]
[[package]]
name = "cargo_metadata"
version = "0.14.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4acbb09d9ee8e23699b9634375c72795d095bf268439da88562cf9b501f181fa"
dependencies = [
"camino",
"cargo-platform",
"semver",
"serde",
"serde_json",
]
[[package]]
name = "cargo_toml"
version = "0.15.3"
@@ -726,6 +764,19 @@ dependencies = [
"syn 2.0.18",
]
[[package]]
name = "dashmap"
version = "5.5.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "978747c1d849a7d2ee5e8adc0159961c48fb7e5db2f06af6723b80123bb53856"
dependencies = [
"cfg-if",
"hashbrown 0.14.0",
"lock_api",
"once_cell",
"parking_lot_core",
]
[[package]]
name = "dasp_sample"
version = "0.11.0"
@@ -852,6 +903,15 @@ dependencies = [
"libc",
]
[[package]]
name = "error-chain"
version = "0.12.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2d2f06b9cac1506ece98fe3231e3cc9c4410ec3d5b1f24ae1c8946f0742cdefc"
dependencies = [
"version_check",
]
[[package]]
name = "fastrand"
version = "1.9.0"
@@ -1323,6 +1383,12 @@ version = "0.12.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888"
[[package]]
name = "hashbrown"
version = "0.14.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2c6201b9ff9fd90a5a3bac2e56a830d0caa509576f0e503818ee82c181b3437a"
[[package]]
name = "heck"
version = "0.3.3"
@@ -1493,7 +1559,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99"
dependencies = [
"autocfg",
"hashbrown",
"hashbrown 0.12.3",
"serde",
]
@@ -1801,6 +1867,21 @@ dependencies = [
"windows 0.43.0",
]
[[package]]
name = "mini-moka"
version = "0.10.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "23e0b72e7c9042467008b10279fc732326bd605459ae03bda88825909dd19b56"
dependencies = [
"crossbeam-channel",
"crossbeam-utils",
"dashmap",
"skeptic",
"smallvec",
"tagptr",
"triomphe",
]
[[package]]
name = "minimal-lexical"
version = "0.2.1"
@@ -2354,6 +2435,17 @@ dependencies = [
"unicode-ident",
]
[[package]]
name = "pulldown-cmark"
version = "0.9.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "77a1a2f1f0a7ecff9c31abbe177637be0e97a0aef46cf8738ece09327985d998"
dependencies = [
"bitflags",
"memchr",
"unicase",
]
[[package]]
name = "quick-xml"
version = "0.28.2"
@@ -2858,6 +2950,21 @@ version = "0.3.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7bd3e3206899af3f8b12af284fafc038cc1dc2b41d1b89dd17297221c5d225de"
[[package]]
name = "skeptic"
version = "0.13.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "16d23b015676c90a0f01c197bfdc786c20342c73a0afdda9025adb0bc42940a8"
dependencies = [
"bytecount",
"cargo_metadata",
"error-chain",
"glob",
"pulldown-cmark",
"tempfile",
"walkdir",
]
[[package]]
name = "slab"
version = "0.4.8"
@@ -3195,6 +3302,12 @@ dependencies = [
"version-compare 0.1.1",
]
[[package]]
name = "tagptr"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7b2093cf4c8eb1e67749a6762251bc9cd836b6fc171623bd0a9d324d37af2417"
[[package]]
name = "tao"
version = "0.16.2"
@@ -3708,12 +3821,27 @@ dependencies = [
"serde_json",
]
[[package]]
name = "triomphe"
version = "0.1.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0eee8098afad3fb0c54a9007aab6804558410503ad676d4633f9c2559a00ac0f"
[[package]]
name = "typenum"
version = "1.16.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "497961ef93d974e23eb6f433eb5fe1b7930b659f06d12dec6fc44a8f554c0bba"
[[package]]
name = "unicase"
version = "2.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f7d2d4dafb69621809a81864c9c1b864479e1235c0dd4e199924b9742439ed89"
dependencies = [
"version_check",
]
[[package]]
name = "unicode-bidi"
version = "0.3.13"
+1
View File
@@ -22,6 +22,7 @@ midir = "0.9.1"
tokio = { version = "1.29.0", features = ["full"] }
rosc = "0.10.1"
web-audio-api = { git = "https://github.com/orottier/web-audio-api-rs.git", branch = "main" }
mini-moka = "0.10.2"
[features]
# this feature is used for production builds or when `devPath` points to the filesystem and the built-in dev server is disabled.
+26 -6
View File
@@ -3,6 +3,7 @@ use std::{
time::Duration,
thread::sleep
};
use mini_moka::sync::Cache;
use tokio::{
sync::{mpsc, Mutex},
@@ -38,6 +39,8 @@ pub struct WebAudioMessage {
pub lpenv: FilterADSR,
pub hpenv: FilterADSR,
pub bpenv: FilterADSR,
pub n: usize,
pub sampleurl: String,
}
pub struct AsyncInputTransmitWebAudio {
@@ -70,21 +73,24 @@ pub fn init(
});
let message_queue_clone = Arc::clone(&message_queue);
tauri::async_runtime::spawn(async move {
/* ...........................................................
Prepare audio context
............................................................*/
// Create audio context
let latency_hint = match std::env::var("WEB_AUDIO_LATENCY").as_deref() {
Ok("playback") => AudioContextLatencyCategory::Playback,
_ => AudioContextLatencyCategory::default(),
};
let mut audio_context = AudioContext::new(AudioContextOptions {
latency_hint,
..AudioContextOptions::default()
});
// Create audio context
tauri::async_runtime::spawn(async move {
/* ...........................................................
Prepare audio context
............................................................*/
// let cache = Cache::new(10_000);
// let cache_clone_1 = cache.clone();
/* ...........................................................
Process queued messages
............................................................*/
@@ -97,7 +103,17 @@ pub fn init(
return true;
};
superdough(message.clone(), &mut audio_context);
let file_path = message.sampleurl.clone();
// superdough(message.clone(), &mut audio_context);
tokio::spawn(async move {
});
return false;
});
@@ -138,6 +154,8 @@ pub struct MessageFromJS {
lpenv: (f64, f64, f64, f64, f64),
hpenv: (f64, f64, f64, f64, f64),
bpenv: (f64, f64, f64, f64, f64),
n: usize,
sampleurl: String,
}
// Called from JS
@@ -212,6 +230,8 @@ pub async fn sendwebaudio(
release: m.bpenv.3,
env: m.bpenv.4,
},
n: m.n,
sampleurl: m.sampleurl,
};
messages_to_process.push(message_to_process);
}