mirror of
https://github.com/eliasstepanik/strudel-docker.git
synced 2026-01-11 13:48:34 +00:00
settings panel
This commit is contained in:
parent
6f9bcc53dc
commit
d8e4055c6e
@ -14,9 +14,10 @@ import { map } from 'nanostores';
|
|||||||
import { logger } from './logger.mjs';
|
import { logger } from './logger.mjs';
|
||||||
import { loadBuffer } from './sampler.mjs';
|
import { loadBuffer } from './sampler.mjs';
|
||||||
|
|
||||||
let maxPolyphony = 128;
|
export const DEFAULT_MAX_POLYPHONY = 128;
|
||||||
|
let maxPolyphony = DEFAULT_MAX_POLYPHONY;
|
||||||
export function setMaxPolyphony(polyphony) {
|
export function setMaxPolyphony(polyphony) {
|
||||||
maxPolyphony = polyphony;
|
maxPolyphony = parseInt(polyphony) ?? DEFAULT_MAX_POLYPHONY;
|
||||||
}
|
}
|
||||||
export const soundMap = map();
|
export const soundMap = map();
|
||||||
|
|
||||||
@ -166,8 +167,8 @@ function loadWorklets() {
|
|||||||
|
|
||||||
// this function should be called on first user interaction (to avoid console warning)
|
// this function should be called on first user interaction (to avoid console warning)
|
||||||
export async function initAudio(options = {}) {
|
export async function initAudio(options = {}) {
|
||||||
const { disableWorklets = false, polyphony = maxPolyphony } = options;
|
const { disableWorklets = false, maxPolyphony } = options;
|
||||||
setMaxPolyphony(polyphony);
|
setMaxPolyphony(maxPolyphony);
|
||||||
if (typeof window === 'undefined') {
|
if (typeof window === 'undefined') {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -487,7 +488,7 @@ export const superdough = async (value, t, hapDuration) => {
|
|||||||
const ch = activeSoundSources.entries().next();
|
const ch = activeSoundSources.entries().next();
|
||||||
const source = ch.value[1];
|
const source = ch.value[1];
|
||||||
const chainID = ch.value[0];
|
const chainID = ch.value[0];
|
||||||
const endTime = t + .25;
|
const endTime = t + 0.25;
|
||||||
source?.node?.gain?.linearRampToValueAtTime(0, endTime);
|
source?.node?.gain?.linearRampToValueAtTime(0, endTime);
|
||||||
source?.stop?.(endTime);
|
source?.stop?.(endTime);
|
||||||
activeSoundSources.delete(chainID);
|
activeSoundSources.delete(chainID);
|
||||||
|
|||||||
@ -25,6 +25,10 @@ const getFrequencyFromValue = (value) => {
|
|||||||
|
|
||||||
return Number(freq);
|
return Number(freq);
|
||||||
};
|
};
|
||||||
|
function destroyAudioWorkletNode(node) {
|
||||||
|
node.disconnect();
|
||||||
|
node.parameters.get('end')?.setValueAtTime(0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
const waveforms = ['triangle', 'square', 'sawtooth', 'sine'];
|
const waveforms = ['triangle', 'square', 'sawtooth', 'sine'];
|
||||||
const noises = ['pink', 'white', 'brown', 'crackle'];
|
const noises = ['pink', 'white', 'brown', 'crackle'];
|
||||||
@ -117,7 +121,7 @@ export function registerSynthSounds() {
|
|||||||
let timeoutNode = webAudioTimeout(
|
let timeoutNode = webAudioTimeout(
|
||||||
ac,
|
ac,
|
||||||
() => {
|
() => {
|
||||||
o.disconnect();
|
destroyAudioWorkletNode(o);
|
||||||
envGain.disconnect();
|
envGain.disconnect();
|
||||||
onended();
|
onended();
|
||||||
fm?.stop();
|
fm?.stop();
|
||||||
@ -173,13 +177,12 @@ export function registerSynthSounds() {
|
|||||||
let envGain = gainNode(1);
|
let envGain = gainNode(1);
|
||||||
envGain = o.connect(envGain);
|
envGain = o.connect(envGain);
|
||||||
|
|
||||||
|
|
||||||
getParamADSR(envGain.gain, attack, decay, sustain, release, 0, 1, begin, holdend, 'linear');
|
getParamADSR(envGain.gain, attack, decay, sustain, release, 0, 1, begin, holdend, 'linear');
|
||||||
|
|
||||||
let timeoutNode = webAudioTimeout(
|
let timeoutNode = webAudioTimeout(
|
||||||
ac,
|
ac,
|
||||||
() => {
|
() => {
|
||||||
o.disconnect();
|
destroyAudioWorkletNode(o);
|
||||||
envGain.disconnect();
|
envGain.disconnect();
|
||||||
onended();
|
onended();
|
||||||
fm?.stop();
|
fm?.stop();
|
||||||
@ -237,7 +240,7 @@ export function registerSynthSounds() {
|
|||||||
return {
|
return {
|
||||||
node,
|
node,
|
||||||
stop: (endTime) => {
|
stop: (endTime) => {
|
||||||
stop(endTime)
|
stop(endTime);
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
@ -308,7 +311,6 @@ export function getOscillator(s, t, value) {
|
|||||||
return {
|
return {
|
||||||
node: noiseMix?.node || o,
|
node: noiseMix?.node || o,
|
||||||
stop: (time) => {
|
stop: (time) => {
|
||||||
// console.info(time)
|
|
||||||
fmModulator.stop(time);
|
fmModulator.stop(time);
|
||||||
vibratoOscillator?.stop(time);
|
vibratoOscillator?.stop(time);
|
||||||
noiseMix?.stop(time);
|
noiseMix?.stop(time);
|
||||||
|
|||||||
@ -663,9 +663,6 @@ registerProcessor('phase-vocoder-processor', PhaseVocoderProcessor);
|
|||||||
class PulseOscillatorProcessor extends AudioWorkletProcessor {
|
class PulseOscillatorProcessor extends AudioWorkletProcessor {
|
||||||
constructor() {
|
constructor() {
|
||||||
super();
|
super();
|
||||||
// this.port.onmessage = (event) => {
|
|
||||||
// console.info(event)
|
|
||||||
// };
|
|
||||||
this.pi = _PI;
|
this.pi = _PI;
|
||||||
this.phi = -this.pi; // phase
|
this.phi = -this.pi; // phase
|
||||||
this.Y0 = 0; // feedback memories
|
this.Y0 = 0; // feedback memories
|
||||||
@ -713,6 +710,9 @@ class PulseOscillatorProcessor extends AudioWorkletProcessor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
process(inputs, outputs, params) {
|
process(inputs, outputs, params) {
|
||||||
|
if (this.disconnected) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
if (currentTime <= params.begin[0]) {
|
if (currentTime <= params.begin[0]) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,10 +1,12 @@
|
|||||||
import { defaultSettings, settingsMap, useSettings } from '../../../settings.mjs';
|
import { defaultSettings, settingsMap, useSettings } from '../../../settings.mjs';
|
||||||
import { themes } from '@strudel/codemirror';
|
import { themes } from '@strudel/codemirror';
|
||||||
|
import { Textbox } from '../textbox/Textbox.jsx';
|
||||||
import { isUdels } from '../../util.mjs';
|
import { isUdels } from '../../util.mjs';
|
||||||
import { ButtonGroup } from './Forms.jsx';
|
import { ButtonGroup } from './Forms.jsx';
|
||||||
import { AudioDeviceSelector } from './AudioDeviceSelector.jsx';
|
import { AudioDeviceSelector } from './AudioDeviceSelector.jsx';
|
||||||
import { AudioEngineTargetSelector } from './AudioEngineTargetSelector.jsx';
|
import { AudioEngineTargetSelector } from './AudioEngineTargetSelector.jsx';
|
||||||
import { confirmDialog } from '../../util.mjs';
|
import { confirmDialog } from '../../util.mjs';
|
||||||
|
import { DEFAULT_MAX_POLYPHONY, setMaxPolyphony } from '@strudel/webaudio';
|
||||||
|
|
||||||
function Checkbox({ label, value, onChange, disabled = false }) {
|
function Checkbox({ label, value, onChange, disabled = false }) {
|
||||||
return (
|
return (
|
||||||
@ -53,7 +55,7 @@ function NumberSlider({ value, onChange, step = 1, ...rest }) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function FormItem({ label, children }) {
|
function FormItem({ label, children, sublabel }) {
|
||||||
return (
|
return (
|
||||||
<div className="grid gap-2">
|
<div className="grid gap-2">
|
||||||
<label>{label}</label>
|
<label>{label}</label>
|
||||||
@ -105,6 +107,7 @@ export function SettingsTab({ started }) {
|
|||||||
audioDeviceName,
|
audioDeviceName,
|
||||||
audioEngineTarget,
|
audioEngineTarget,
|
||||||
togglePanelTrigger,
|
togglePanelTrigger,
|
||||||
|
maxPolyphony,
|
||||||
} = useSettings();
|
} = useSettings();
|
||||||
const shouldAlwaysSync = isUdels();
|
const shouldAlwaysSync = isUdels();
|
||||||
const canChangeAudioDevice = AudioContext.prototype.setSinkId != null;
|
const canChangeAudioDevice = AudioContext.prototype.setSinkId != null;
|
||||||
@ -139,6 +142,26 @@ export function SettingsTab({ started }) {
|
|||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</FormItem>
|
</FormItem>
|
||||||
|
|
||||||
|
<FormItem label="Maximum Polyphony">
|
||||||
|
<Textbox
|
||||||
|
min={1}
|
||||||
|
max={Infinity}
|
||||||
|
onBlur={(e) => {
|
||||||
|
let v = parseInt(e.target.value);
|
||||||
|
v = isNaN(v) ? DEFAULT_MAX_POLYPHONY : v;
|
||||||
|
setMaxPolyphony(v);
|
||||||
|
settingsMap.setKey('maxPolyphony', v);
|
||||||
|
}}
|
||||||
|
onChange={(v) => {
|
||||||
|
v = Math.max(1, parseInt(v));
|
||||||
|
settingsMap.setKey('maxPolyphony', isNaN(v) ? undefined : v);
|
||||||
|
}}
|
||||||
|
type="number"
|
||||||
|
placeholder=""
|
||||||
|
value={maxPolyphony ?? ''}
|
||||||
|
/>
|
||||||
|
</FormItem>
|
||||||
<FormItem label="Theme">
|
<FormItem label="Theme">
|
||||||
<SelectInput options={themeOptions} value={theme} onChange={(theme) => settingsMap.setKey('theme', theme)} />
|
<SelectInput options={themeOptions} value={theme} onChange={(theme) => settingsMap.setKey('theme', theme)} />
|
||||||
</FormItem>
|
</FormItem>
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user