autotune paramters, autotune in ui. UI organize

This commit is contained in:
michalcourson
2025-11-01 13:33:14 -04:00
parent 55e80b4c74
commit 3468c1f389
18 changed files with 406 additions and 712 deletions

View File

@ -26,419 +26,15 @@ import "@fontsource/roboto/400.css";
import "@fontsource/roboto/500.css";
import "@fontsource/roboto/700.css";
import Box from "@mui/material/Container";
// import Checkbox from "@mui/material/Checkbox";
import Typography from "@mui/material/Typography";
import Container from "@mui/material/Container";
import Slider from "@mui/material/Slider";
// import Button from "@mui/material/Button";
// import CardActions from "@mui/material/CardActions";
// import Snackbar from "@mui/material/Snackbar";
// import IconButton from "@mui/material/IconButton";
// import CloseIcon from "@mui/icons-material/Close";
// import InputLabel from "@mui/material/InputLabel";
// import MenuItem from "@mui/material/MenuItem";
// import FormControl from "@mui/material/FormControl";
// import Select from "@mui/material/Select";
// import FormGroup from "@mui/material/FormGroup";
// import FormControlLabel from "@mui/material/FormControlLabel";
import PianoKeyboard from "./Components/PianoKeyboard";
import { React, useState, useEffect, useRef } from "react";
import PropTypes from "prop-types";
import * as Juce from "juce-framework-frontend";
import JuceSlider from "./Components/JuceSlider.js";
import MidiNoteInfo from "./Components/MidiNoteInfo.js";
import { controlParameterIndexAnnotation } from "./types/JuceTypes.js";
import { React } from "react";
import "./App.css";
// import { KnobPercentage } from "./Components/KnobPercentage";
// Custom attributes in React must be in all lower case
const controlParameterIndexAnnotation = "controlparameterindex";
function JuceSlider({ identifier, title }) {
JuceSlider.propTypes = {
identifier: PropTypes.string,
title: PropTypes.string,
};
const sliderState = Juce.getSliderState(identifier);
const [value, setValue] = useState(sliderState.getNormalisedValue());
const [properties, setProperties] = useState(sliderState.properties);
const handleChange = (event, newValue) => {
sliderState.setNormalisedValue(newValue);
setValue(newValue);
};
const mouseDown = () => {
sliderState.sliderDragStarted();
};
const changeCommitted = (event, newValue) => {
sliderState.setNormalisedValue(newValue);
sliderState.sliderDragEnded();
};
useEffect(() => {
const valueListenerId = sliderState.valueChangedEvent.addListener(() => {
setValue(sliderState.getNormalisedValue());
});
const propertiesListenerId = sliderState.propertiesChangedEvent.addListener(
() => setProperties(sliderState.properties)
);
return function cleanup() {
sliderState.valueChangedEvent.removeListener(valueListenerId);
sliderState.propertiesChangedEvent.removeListener(propertiesListenerId);
};
});
function calculateValue() {
return sliderState.getScaledValue();
}
return (
<Box
{...{
[controlParameterIndexAnnotation]:
sliderState.properties.parameterIndex,
}}
>
<Typography sx={{ mt: 1.5 }}>
{properties.name}: {sliderState.getScaledValue()} {properties.label}
</Typography>
<Slider
aria-label={title}
value={value}
scale={calculateValue}
onChange={handleChange}
min={0}
max={1}
step={1 / (properties.numSteps - 1)}
onChangeCommitted={changeCommitted}
onMouseDown={mouseDown}
/>
</Box>
);
}
// function JuceCheckbox({ identifier }) {
// JuceCheckbox.propTypes = {
// identifier: PropTypes.string,
// };
// const checkboxState = Juce.getToggleState(identifier);
// const [value, setValue] = useState(checkboxState.getValue());
// const [properties, setProperties] = useState(checkboxState.properties);
// const handleChange = (event) => {
// checkboxState.setValue(event.target.checked);
// setValue(event.target.checked);
// };
// useEffect(() => {
// const valueListenerId = checkboxState.valueChangedEvent.addListener(() => {
// setValue(checkboxState.getValue());
// });
// const propertiesListenerId =
// checkboxState.propertiesChangedEvent.addListener(() =>
// setProperties(checkboxState.properties)
// );
// return function cleanup() {
// checkboxState.valueChangedEvent.removeListener(valueListenerId);
// checkboxState.propertiesChangedEvent.removeListener(propertiesListenerId);
// };
// });
// const cb = <Checkbox checked={value} onChange={handleChange} />;
// return (
// <Box
// {...{
// [controlParameterIndexAnnotation]:
// checkboxState.properties.parameterIndex,
// }}
// >
// <FormGroup>
// <FormControlLabel control={cb} label={properties.name} />
// </FormGroup>
// </Box>
// );
// }
// function JuceComboBox({ identifier }) {
// JuceComboBox.propTypes = {
// identifier: PropTypes.string,
// };
// const comboBoxState = Juce.getComboBoxState(identifier);
// const [value, setValue] = useState(comboBoxState.getChoiceIndex());
// const [properties, setProperties] = useState(comboBoxState.properties);
// const handleChange = (event) => {
// comboBoxState.setChoiceIndex(event.target.value);
// setValue(event.target.value);
// };
// useEffect(() => {
// const valueListenerId = comboBoxState.valueChangedEvent.addListener(() => {
// setValue(comboBoxState.getChoiceIndex());
// });
// const propertiesListenerId =
// comboBoxState.propertiesChangedEvent.addListener(() => {
// setProperties(comboBoxState.properties);
// });
// return function cleanup() {
// comboBoxState.valueChangedEvent.removeListener(valueListenerId);
// comboBoxState.propertiesChangedEvent.removeListener(propertiesListenerId);
// };
// });
// return (
// <Box
// {...{
// [controlParameterIndexAnnotation]:
// comboBoxState.properties.parameterIndex,
// }}
// >
// <FormControl fullWidth>
// <InputLabel id={identifier}>{properties.name}</InputLabel>
// <Select
// labelId={identifier}
// value={value}
// label={properties.name}
// onChange={handleChange}
// >
// {properties.choices.map((choice, i) => (
// <MenuItem value={i} key={i}>
// {choice}
// </MenuItem>
// ))}
// </Select>
// </FormControl>
// </Box>
// );
// }
// const sayHello = Juce.getNativeFunction("sayHello");
// const SpectrumDataReceiver_eventId = "spectrumData";
// function interpolate(a, b, s) {
// let result = new Array(a.length).fill(0);
// for (const [i, val] of a.entries()) result[i] += (1 - s) * val;
// for (const [i, val] of b.entries()) result[i] += s * val;
// return result;
// }
// function mod(dividend, divisor) {
// const quotient = Math.floor(dividend / divisor);
// return dividend - divisor * quotient;
// }
// class SpectrumDataReceiver {
// constructor(bufferLength) {
// this.bufferLength = bufferLength;
// this.buffer = new Array(this.bufferLength);
// this.readIndex = 0;
// this.writeIndex = 0;
// this.lastTimeStampMs = 0;
// this.timeResolutionMs = 0;
// let self = this;
// this.spectrumDataRegistrationId = window.__JUCE__.backend.addEventListener(
// SpectrumDataReceiver_eventId,
// () => {
// fetch(Juce.getBackendResourceAddress("spectrumData.json"))
// .then((response) => response.text())
// .then((text) => {
// const data = JSON.parse(text);
// if (self.timeResolutionMs == 0) {
// self.timeResolutionMs = data.timeResolutionMs;
// // We want to stay behind the write index by a full batch plus one
// // so that we can keep reading buffered frames until we receive the
// // new batch
// self.readIndex = -data.frames.length - 1;
// self.buffer.fill(new Array(data.frames[0].length).fill(0));
// }
// for (const f of data.frames)
// self.buffer[mod(self.writeIndex++, self.bufferLength)] = f;
// });
// }
// );
// }
// getBufferItem(index) {
// return this.buffer[mod(index, this.buffer.length)];
// }
// getLevels(timeStampMs) {
// if (this.timeResolutionMs == 0) return null;
// const previousTimeStampMs = this.lastTimeStampMs;
// this.lastTimeStampMs = timeStampMs;
// if (previousTimeStampMs == 0) return this.buffer[0];
// const timeAdvance =
// (timeStampMs - previousTimeStampMs) / this.timeResolutionMs;
// this.readIndex += timeAdvance;
// const integralPart = Math.floor(this.readIndex);
// const fractionalPart = this.readIndex - integralPart;
// return interpolate(
// this.getBufferItem(integralPart),
// this.getBufferItem(integralPart + 1),
// fractionalPart
// );
// }
// unregister() {
// window.__JUCE__.backend.removeEventListener(
// this.spectrumDataRegistrationId
// );
// }
// }
const MidNoteDataReceiver_eventId = "midNoteData";
class MidNoteDataReceiver {
constructor() {
this.notes = [];
let self = this;
this.midNoteDataRegistrationId = window.__JUCE__.backend.addEventListener(
MidNoteDataReceiver_eventId,
() => {
fetch(Juce.getBackendResourceAddress("midNoteData.json"))
.then((response) => response.text())
.then((text) => {
const data = JSON.parse(text);
self.notes = data.notes;
});
}
);
}
getNotes() {
return this.notes;
}
unregister() {
window.__JUCE__.backend.removeEventListener(this.midNoteDataRegistrationId);
}
}
function MidiNoteInfo() {
const [notes, setNotes] = useState([]);
const dataReceiverRef = useRef(null);
const isActiveRef = useRef(true);
useEffect(() => {
dataReceiverRef.current = new MidNoteDataReceiver();
isActiveRef.current = true;
function render() {
if (dataReceiverRef.current) {
setNotes(dataReceiverRef.current.getNotes());
}
if (isActiveRef.current) {
window.requestAnimationFrame(render);
}
}
window.requestAnimationFrame(render);
return function cleanup() {
isActiveRef.current = false;
if (dataReceiverRef.current) {
dataReceiverRef.current.unregister();
}
};
}, []);
return (
<div>
<PianoKeyboard heldNotes={notes} />
</div>
);
}
// function FreqBandInfo() {
// const canvasRef = useRef(null);
// let dataReceiver = null;
// let isActive = true;
// // eslint-disable-next-line no-unused-vars
// const render = (timeStampMs) => {
// const canvas = canvasRef.current;
// const ctx = canvas.getContext("2d");
// ctx.clearRect(0, 0, canvas.width, canvas.height);
// var grd = ctx.createLinearGradient(0, 0, 0, canvas.height);
// grd.addColorStop(0, "#1976d2");
// grd.addColorStop(1, "#dae9f8");
// ctx.fillStyle = grd;
// if (dataReceiver != null) {
// const levels = dataReceiver.getLevels(timeStampMs);
// if (levels != null) {
// const numBars = levels.length;
// const barWidth = canvas.width / numBars;
// const barHeight = canvas.height;
// for (const [i, l] of levels.entries()) {
// ctx.fillRect(
// i * barWidth,
// barHeight - l * barHeight,
// barWidth,
// l * barHeight
// );
// }
// }
// }
// if (isActive) window.requestAnimationFrame(render);
// };
// useEffect(() => {
// dataReceiver = new SpectrumDataReceiver(10);
// isActive = true;
// window.requestAnimationFrame(render);
// return function cleanup() {
// isActive = false;
// dataReceiver.unregister();
// };
// });
// const canvasStyle = {
// marginLeft: "0",
// marginRight: "0",
// marginTop: "1em",
// display: "block",
// width: "94%",
// bottom: "0",
// position: "absolute",
// };
// return (
// <Box>
// <canvas height={90} style={canvasStyle} ref={canvasRef}></canvas>
// </Box>
// );
// }
function App() {
const controlParameterIndexUpdater = new Juce.ControlParameterIndexUpdater(
@ -449,83 +45,15 @@ function App() {
controlParameterIndexUpdater.handleMouseMove(event);
});
// const [open, setOpen] = useState(false);
// const [snackbarMessage, setMessage] = useState("No message received yet");
// const openSnackbar = () => {
// setOpen(true);
// };
// const handleClose = (event, reason) => {
// if (reason === "clickaway") {
// return;
// }
// setOpen(false);
// };
// const action = (
// <>
// <IconButton
// size="small"
// aria-label="close"
// color="inherit"
// onClick={handleClose}
// >
// <CloseIcon fontSize="small" />
// </IconButton>
// </>
// );
return (
<div>
<Container>
<JuceSlider identifier="formantSlider" title="Formant" />
<JuceSlider identifier="autoTuneSpeedSlider" title="Auto Tune Speed" />
<JuceSlider identifier="autoTuneDepthSlider" title="Auto Tune Depth" />
<JuceSlider identifier="portTimeSlider" title="Portamento Speed" />
</Container>
<MidiNoteInfo />
{/* <CardActions style={{ justifyContent: "center" }}>
<Button
variant="contained"
sx={{ marginTop: 2 }}
onClick={() => {
sayHello("JUCE").then((result) => {
setMessage(result);
openSnackbar();
});
}}
>
Call backend function
</Button>
</CardActions>
<CardActions style={{ justifyContent: "center" }}>
<Button
variant="contained"
sx={{ marginTop: 2 }}
onClick={() => {
fetch(Juce.getBackendResourceAddress("data.txt"))
.then((response) => response.text())
.then((text) => {
setMessage("Data fetched: " + text);
openSnackbar();
});
}}
>
Fetch data from backend
</Button>
</CardActions>
<JuceCheckbox identifier="muteToggle" />
<br></br>
<JuceComboBox identifier="filterTypeCombo" /> */}
{/* <Snackbar
open={open}
autoHideDuration={6000}
onClose={handleClose}
message={snackbarMessage}
action={action}
/> */}
</div>
);
}