From cd3b90e807b02844465740c920503ffe2a7a33d5 Mon Sep 17 00:00:00 2001 From: kgalvin Date: Thu, 9 Nov 2023 13:41:49 -0800 Subject: [PATCH] add audio for contour --- src/components/ContourAudioControls.tsx | 51 +++++++-------- src/components/ContourAudioEngine.tsx | 86 ++++++++++++------------- src/components/FractalPlayer.tsx | 10 ++- 3 files changed, 71 insertions(+), 76 deletions(-) diff --git a/src/components/ContourAudioControls.tsx b/src/components/ContourAudioControls.tsx index b335744..c51c668 100644 --- a/src/components/ContourAudioControls.tsx +++ b/src/components/ContourAudioControls.tsx @@ -6,8 +6,7 @@ import styled from "styled-components"; import {AudioParamsType, KnobRow} from "@/components/FractalPlayer"; type ContourAudioControlsProps = { - rowIndex: number; - fractalRow: number[]; + contour: { angle: number; duration: number }[]; audioContext: AudioContext | null; core: WebRenderer; playing: boolean; @@ -15,8 +14,7 @@ type ContourAudioControlsProps = { const ContourAudioControls: React.FC = ( { - rowIndex, - fractalRow, + contour, audioContext, core, playing, @@ -24,26 +22,25 @@ const ContourAudioControls: React.FC = ( const [volume, setVolume] = useState(0); const [threshold, setThreshold] = useState(0.09); - const [lowest, setLowest] = useState(200); + const [freqScaling, setFreqScaling] = useState(1); const [highest, setHighest] = useState(6000); - const [smoothing, setSmoothing] = useState(0.02); + const [duration, setDuration] = useState(5); const [audioParams, setAudioParams] = useState({ volume: 0, threshold: 0, highest: 0, - lowest: 0, - smoothing: 0, + freqScaling: 0, + duration: 0, }); useEffect(() => { - setAudioParams({volume, lowest, highest, threshold, smoothing}); - }, [volume, lowest, highest, threshold, smoothing, setAudioParams]); + setAudioParams({volume, freqScaling, highest, threshold, duration}); + }, [volume, freqScaling, highest, threshold, duration, setAudioParams]); return (<> = ( - + {/* = ( max={1} onKnobInput={setThreshold} /> - + */} diff --git a/src/components/ContourAudioEngine.tsx b/src/components/ContourAudioEngine.tsx index 1386c9d..52f6d9d 100644 --- a/src/components/ContourAudioEngine.tsx +++ b/src/components/ContourAudioEngine.tsx @@ -1,6 +1,6 @@ import {AudioParamsType} from "@/components/FractalPlayer"; import dynamic from "next/dynamic"; -import React, {useEffect, useState} from "react"; +import React, {useCallback, useEffect, useState} from "react"; import {el, NodeRepr_t} from "@elemaudio/core"; import WebRenderer from "@elemaudio/web-renderer"; import styled from "styled-components"; @@ -10,8 +10,7 @@ const OscilloscopeSpectrogram = dynamic(() => import('el-vis-audio').then((mod) require("events").EventEmitter.defaultMaxListeners = 0; type AudioEngineProps = { - rowIndex: number; - fractalRow: number[]; + contour: { angle: number; duration: number }[]; audioContext: AudioContext | null; core: WebRenderer; playing: boolean; @@ -19,17 +18,18 @@ type AudioEngineProps = { } const AudioEngine: React.FC = ({ - rowIndex, - fractalRow, + contour, audioContext, core, playing, - audioParams: {volume, lowest, highest, threshold, smoothing} + audioParams: {volume, freqScaling, highest, threshold, duration} }) => { const [audioVizData, setAudioVizData] = useState(); const [fftVizData, setFftVizData] = useState(); - const [ampScale, setAmpScale] = useState(0); + const [frequency, setFrequency] = useState(220); + const [contourStarted, setContourStarted] = useState(false); + const [sequencerId, setSequencerId] = useState(); useEffect(() => { return () => { @@ -40,6 +40,7 @@ const AudioEngine: React.FC = ({ }, [audioContext]); useEffect(() => { + if (!contourStarted) startContour(); if (audioContext) { const SignalSynth = (signal: NodeRepr_t) => { if (playing && signal && core) { @@ -52,48 +53,47 @@ const AudioEngine: React.FC = ({ core.render(synth, synth); } }; - - const Resynth = () => { - let accum = 0; - const linearRange = Math.log10(highest) - Math.log10(lowest); - const linearInterval = linearRange / fractalRow.length; - - const allVoices = [...Array(fractalRow.length)].map((_, i) => { - const amplitude = fractalRow[i] > threshold ? fractalRow[i] : 0; - const key = `osc-freq-${i}`; - const linearFreq = Math.log10(lowest) + (i * linearInterval); - const freq = 10 ** linearFreq; - - if (freq < audioContext.sampleRate / 2) { - accum += amplitude; - const freqSignal = el.const({key, value: freq}); - const ampSignal = el.const({key: `osc-amp-${i}`, value: amplitude}); - const smoothFreqSignal = el.smooth(el.tau2pole(smoothing), freqSignal); - const smoothAmpSignal = el.smooth(el.tau2pole(smoothing), ampSignal); - return el.mul(el.cycle(smoothFreqSignal), smoothAmpSignal); - } else { - return el.const({key, value: 0}); - } - }); - - const addMany = (ins: NodeRepr_t[]): NodeRepr_t => { - return el.add(...ins) as NodeRepr_t; - }; - const rowMult = accum !== 0 ? 1 / accum : 0; - setAmpScale(rowMult); - const rowAmp = el.const({key: "row-gain", value: rowMult}); - const smoothRowAmp = el.smooth(el.tau2pole(smoothing), rowAmp); - return el.mul(addMany(allVoices as NodeRepr_t[]), smoothRowAmp) as NodeRepr_t; - } - + const ContourSynth = el.cycle(el.sm(el.const({key: 'ex2:mix', value: frequency}))); if (playing) { audioContext.resume(); - if (fractalRow?.length > 0) SignalSynth(Resynth()); + SignalSynth(ContourSynth); } else { + setContourStarted(false); + setFrequency(200); audioContext.suspend(); } } - }, [playing, volume, lowest, highest, threshold, fractalRow, audioContext, core, smoothing]); + }, [playing, volume, freqScaling, highest, threshold, contour, audioContext, core, frequency]); + + const startContour = useCallback(() => { + if (!playing) { + clearInterval(sequencerId) + setContourStarted(false); + } else { + setContourStarted(true); + let elapsedTime = 0; + let freq = frequency; + contour.forEach((segment, i) => { + elapsedTime += (segment.duration * duration); + setTimeout(() => { + freq += ((segment.angle - 180) * freqScaling); + setFrequency(freq); + }, elapsedTime); + }); + + // start setIntervals + //let freq = frequency; + //const i = 0; + //const intervalId = setInterval(() => { + + //setFrequency(freq); + //freq += (contour[i].angle - 180); + //i++; + //}, contour[i].duration * 500); + //setSequencerId(intervalId); + } + }, [playing, contour]); + core?.on("scope", function (e) { if (e.source === `scope-contour`) { diff --git a/src/components/FractalPlayer.tsx b/src/components/FractalPlayer.tsx index b1baa70..6ed3b82 100644 --- a/src/components/FractalPlayer.tsx +++ b/src/components/FractalPlayer.tsx @@ -43,8 +43,8 @@ export type AudioParamsType = { volume: number; threshold: number; highest: number; - lowest: number; - smoothing: number; + freqScaling: number; + duration: number; } const FractalPlayer: React.FC = ({ @@ -110,7 +110,6 @@ const FractalPlayer: React.FC = ({ setContour(newContour.soundControlList); } } - console.log("marching squares"); } }, [tolerance, rawFractalData, showContour]); @@ -362,7 +361,7 @@ const FractalPlayer: React.FC = ({ diameter={30} labelWidth={30} fontSize={11} - tooltip={"tolerance of contour smoothing"} + tooltip={"tolerance of contour duration"} knobValue={tolerance} step={0.1} min={0} @@ -370,8 +369,7 @@ const FractalPlayer: React.FC = ({ onKnobInput={setTolerance} />