So the following code doesn't appear to work until there's a runtime error or sometimes after CRA does an auto reload. I'm not sure why.
So it works... Sometimes. And I don't know why it just doesn't work everytime.
I know it's not a permission issue.
I just wish it would work every time! Any help would be really appreciated.
EDIT: So it appears that the Component needs to be re-renderer for it to work. Still trying to understand why.
useEffect(() => {
window.AudioContext = window.AudioContext || window.webkitAudioContext;
const appHeight = stageRef.current.app.renderer.height;
let audioContext = null;
let analyser = null;
let theBuffer = null;
let mediaStreamSource = null;
window.onload = function () {
audioContext = new AudioContext();
fetch("whistling3.ogg")
.then((response) => {
if (!response.ok) {
throw new Error(`HTTP error, status = ${response.status}`);
}
return response.arrayBuffer();
})
.then((buffer) => audioContext.decodeAudioData(buffer))
.then((decodedData) => {
theBuffer = decodedData;
});
};
function startPitchDetect() {
// grab an audio context
audioContext = new AudioContext();
// Attempt to get audio input
navigator.mediaDevices
.getUserMedia({
audio: {
mandatory: {
googEchoCancellation: "false",
googAutoGainControl: "false",
googNoiseSuppression: "false",
googHighpassFilter: "false",
},
optional: [],
},
})
.then((stream) => {
// Create an AudioNode from the stream.
mediaStreamSource = audioContext.createMediaStreamSource(stream);
// Connect it to the destination.
analyser = audioContext.createAnalyser();
analyser.fftSize = 2048;
mediaStreamSource.connect(analyser);
updatePitch();
})
.catch((err) => {
// always check for errors at the end.
console.error(`${err.name}: ${err.message}`);
alert("Stream generation failed.");
});
}
var rafID = null;
var tracks = null;
var buflen = 2048;
var buf = new Float32Array(buflen);
var noteStrings = [
"C",
"C#",
"D",
"D#",
"E",
"F",
"F#",
"G",
"G#",
"A",
"A#",
"B",
];
function noteFromPitch(frequency) {
var noteNum = 12 * (Math.log(frequency / 440) / Math.log(2));
return Math.round(noteNum) + 69;
}
function frequencyFromNoteNumber(note) {
return 440 * Math.pow(2, (note - 69) / 12);
}
function centsOffFromPitch(frequency, note) {
return Math.floor(
(1200 * Math.log(frequency / frequencyFromNoteNumber(note))) /
Math.log(2)
);
}
function autoCorrelate(buf, sampleRate) {
// Implements the ACF2+ algorithm
var SIZE = buf.length;
var rms = 0;
for (var i = 0; i < SIZE; i++) {
var val = buf[i];
rms += val * val;
}
rms = Math.sqrt(rms / SIZE);
if (rms < 0.01)
// not enough signal
return -1;
var r1 = 0,
r2 = SIZE - 1,
thres = 0.2;
for (var i = 0; i < SIZE / 2; i++)
if (Math.abs(buf[i]) < thres) {
r1 = i;
break;
}
for (var i = 1; i < SIZE / 2; i++)
if (Math.abs(buf[SIZE - i]) < thres) {
r2 = SIZE - i;
break;
}
buf = buf.slice(r1, r2);
SIZE = buf.length;
var c = new Array(SIZE).fill(0);
for (var i = 0; i < SIZE; i++)
for (var j = 0; j < SIZE - i; j++) c[i] = c[i] + buf[j] * buf[j + i];
var d = 0;
while (c[d] > c[d + 1]) d++;
var maxval = -1,
maxpos = -1;
for (var i = d; i < SIZE; i++) {
if (c[i] > maxval) {
maxval = c[i];
maxpos = i;
}
}
var T0 = maxpos;
var x1 = c[T0 - 1],
x2 = c[T0],
x3 = c[T0 + 1];
let a = (x1 + x3 - 2 * x2) / 2;
let b = (x3 - x1) / 2;
if (a) T0 = T0 - b / (2 * a);
return sampleRate / T0;
}
function updatePitch(time) {
var cycles = new Array();
analyser.getFloatTimeDomainData(buf);
var ac = autoCorrelate(buf, audioContext.sampleRate);
if (ac == -1) {
if (currentNoteRef.current) {
currentNoteRef.current.y = appHeight / 2 - 50;
}
} else {
let pitch = ac;
var note = noteFromPitch(pitch);
const noteAsString = noteStrings[note % 12];
var detune = centsOffFromPitch(pitch, note);
console.log(
`pitch is ${pitch}, note is ${note}, detune is ${detune}, note as string is ${noteAsString}`
);
if (currentNoteRef.current) {
currentNoteRef.current.y =
appHeight / 2 - ((pitch / 500) * appHeight) / 2;
}
if (detune == 0) {
} else {
if (detune < 0) {
// flat
} else {
// sharp
}
// Math.abs(detune); is the detune amount
}
}
if (!window.requestAnimationFrame)
window.requestAnimationFrame = window.webkitRequestAnimationFrame;
rafID = window.requestAnimationFrame(updatePitch);
}
startPitchDetect();
});