Microphone input not processed until after a reload occurs (create-react-app)

32 Views Asked by At

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();
  });
0

There are 0 best solutions below