Skip to content

Commit

Permalink
convert negative radians into degrees
Browse files Browse the repository at this point in the history
  • Loading branch information
blechdom committed Nov 9, 2023
1 parent c4fb86b commit f86f2c3
Show file tree
Hide file tree
Showing 4 changed files with 391 additions and 83 deletions.
142 changes: 142 additions & 0 deletions src/components/ContourAudioControls.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
import ContourAudioEngine from "@/components/ContourAudioEngine";
import WebRenderer from "@elemaudio/web-renderer";
import dynamic from "next/dynamic";
import React, {useEffect, useState} from "react";
import styled from "styled-components";
import {AudioParamsType, KnobRow} from "@/components/FractalPlayer";

type ContourAudioControlsProps = {
rowIndex: number;
fractalRow: number[];
audioContext: AudioContext | null;
core: WebRenderer;
playing: boolean;
}

const ContourAudioControls: React.FC<ContourAudioControlsProps> = (
{
rowIndex,
fractalRow,
audioContext,
core,
playing,
}) => {

const [volume, setVolume] = useState<number>(0);
const [threshold, setThreshold] = useState<number>(0.09);
const [lowest, setLowest] = useState<number>(200);
const [highest, setHighest] = useState<number>(6000);
const [smoothing, setSmoothing] = useState<number>(0.02);
const [audioParams, setAudioParams] = useState<AudioParamsType>({
volume: 0,
threshold: 0,
highest: 0,
lowest: 0,
smoothing: 0,
});

useEffect(() => {
setAudioParams({volume, lowest, highest, threshold, smoothing});
}, [volume, lowest, highest, threshold, smoothing, setAudioParams]);

return (<>
<KnobRow>
<ContourAudioEngine
rowIndex={rowIndex}
fractalRow={fractalRow}
audioContext={audioContext}
core={core}
playing={playing}
audioParams={audioParams}
/>
<ControlKnob>
<Knob
id={`contour-volume`}
label={"volume"}
diameter={30}
labelWidth={30}
fontSize={11}
tooltip={"main volume of this sonified fractal"}
knobValue={volume}
step={0.01}
min={0}
max={1}
onKnobInput={setVolume}
/>
</ControlKnob>
</KnobRow>
<KnobRow>
<ControlKnob>
<Knob
id={`contour-lowest`}
label={"lowest"}
diameter={30}
labelWidth={30}
fontSize={11}
tooltip={"lowest frequency of the oscillator bank (hz)"}
knobValue={lowest}
step={0.01}
min={0}
max={500}
onKnobInput={setLowest}
/>
</ControlKnob>
<ControlKnob>
<Knob
id={`contour-highest`}
label={"highest"}
diameter={30}
labelWidth={30}
fontSize={11}
tooltip={"highest frequency of the oscillator bank (hz)"}
knobValue={highest}
step={0.01}
min={20}
max={20000}
onKnobInput={setHighest}
/>
</ControlKnob>
<ControlKnob>
<Knob
id={`contour-threshold`}
label={"thresh"}
diameter={30}
labelWidth={30}
fontSize={11}
tooltip={"values below this threshold will be ignored during playback"}
knobValue={threshold}
step={0.001}
min={0}
max={1}
onKnobInput={setThreshold}
/>
</ControlKnob>
<ControlKnob>
<Knob
id={`contour-smoothing`}
label={"smooth"}
diameter={30}
labelWidth={30}
fontSize={11}
tooltip={"smoothing of frequency when changing (0.02 = 20ms)"}
knobValue={smoothing}
step={0.01}
min={0}
max={0.5}
onKnobInput={setSmoothing}
/>
</ControlKnob>
</KnobRow>
</>
);
};

const Knob = dynamic(() => import("el-vis-audio").then((mod) => mod.KnobParamLabel),
{ssr: false}
)

const ControlKnob = styled.div`
margin: -0.5rem 0.4rem 0 0.4rem;
`;

export default ContourAudioControls;
139 changes: 139 additions & 0 deletions src/components/ContourAudioEngine.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
import {AudioParamsType} from "@/components/FractalPlayer";
import dynamic from "next/dynamic";
import React, {useEffect, useState} from "react";
import {el, NodeRepr_t} from "@elemaudio/core";
import WebRenderer from "@elemaudio/web-renderer";
import styled from "styled-components";

const OscilloscopeSpectrogram = dynamic(() => import('el-vis-audio').then((mod) => mod.OscilloscopeSpectrogram), {ssr: false});

require("events").EventEmitter.defaultMaxListeners = 0;

type AudioEngineProps = {
rowIndex: number;
fractalRow: number[];
audioContext: AudioContext | null;
core: WebRenderer;
playing: boolean;
audioParams: AudioParamsType;
}

const AudioEngine: React.FC<AudioEngineProps> = ({
rowIndex,
fractalRow,
audioContext,
core,
playing,
audioParams: {volume, lowest, highest, threshold, smoothing}
}) => {

const [audioVizData, setAudioVizData] = useState<any>();
const [fftVizData, setFftVizData] = useState<any>();
const [ampScale, setAmpScale] = useState<number>(0);

useEffect(() => {
return () => {
if (audioContext) {
audioContext.suspend();
}
}
}, [audioContext]);

useEffect(() => {
if (audioContext) {
const SignalSynth = (signal: NodeRepr_t) => {
if (playing && signal && core) {
let synth = el.mul(
signal,
el.sm(el.const({key: `main-amp`, value: volume}))
) as NodeRepr_t;
synth = el.scope({name: `scope-contour`}, synth);
synth = el.fft({name: `fft-contour`}, synth);
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;
}

if (playing) {
audioContext.resume();
if (fractalRow?.length > 0) SignalSynth(Resynth());
} else {
audioContext.suspend();
}
}
}, [playing, volume, lowest, highest, threshold, fractalRow, audioContext, core, smoothing]);

core?.on("scope", function (e) {
if (e.source === `scope-contour`) {
e.data.length && setAudioVizData(e.data[0]);
}
});

core?.on("fft", function (e) {
if (e.source === `fft-contour`) {
setFftVizData(e.data.real);
}
});

return (
<FlexColumn>
<StyledOscilloscopeSpectrogram>
<OscilloscopeSpectrogram
audioVizData={audioVizData}
fftVizData={fftVizData}
width={156}
height={38}
/>
<br/><br/><br/><br/><br/>
</StyledOscilloscopeSpectrogram>
</FlexColumn>
);
};

const FlexColumn = styled.div`
display: flex;
flex-direction: column;
align-items: center;
padding-left: 0.5rem;
`;

const StyledOscilloscopeSpectrogram = styled.div`
outline: 1px solid #000000;
font-size: 1.5rem;
width: 156px;
height: 38px;
cursor: pointer;
`;
export default AudioEngine;
Loading

0 comments on commit f86f2c3

Please sign in to comment.