-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
convert negative radians into degrees
- Loading branch information
Showing
4 changed files
with
391 additions
and
83 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; |
Oops, something went wrong.