Creating Freddie Mercury's voice with javascript

      async function playFreddie() {
        let freddie = [[500, 1], [1000, 40], [1500, 5], [2000, 10], [2500, 10], [3000, 15], [3500, 10], [4000, 5]]
        // Play frequencies with their respective volumes, with vibrato depth of 2% and speed of 7 Hz
        await playFrequencies(500, 3, freddie, 2, 7, 0.2);
      }
  
      async function playFrequencies(baseFrequency, duration, frequencyVolumeArray, vibratoDepthPercent = 2, vibratoSpeed = 7, maxRandomness = 0.2) {
        // Create AudioContext
        let audioCtx = new (window.AudioContext || window.webkitAudioContext)();
  
        let pitchRatio = baseFrequency / frequencyVolumeArray[0][0];
        console.log('pitchRatio', pitchRatio)
  
        // Create a list of promises
        let promises = frequencyVolumeArray.map((freqVol) => {
          // Extract the frequency and volume
          let frequency = freqVol[0];
          let volume = freqVol[1] / 100; // Convert percentage to decimal
  
          // Create oscillator for the frequency
          let oscillator = audioCtx.createOscillator();
          oscillator.frequency.value = pitchRatio * frequency; // Set base frequency
  
          // Create a GainNode to control the volume
          let gainNode = audioCtx.createGain();
          gainNode.gain.value = volume;
  
          // Create vibrato oscillator and gain
          let vibrato = audioCtx.createOscillator();
          let vGain = audioCtx.createGain();
  
          // Connect the vibrato to the main oscillator frequency
          vibrato.connect(vGain);
          vGain.connect(oscillator.frequency);
  
          // Connect the oscillator to the gain node
          oscillator.connect(gainNode);
  
          // Connect the gain node to the output destination
          gainNode.connect(audioCtx.destination);
  
          // Function to update vibrato parameters
          const updateVibrato = () => {
            let randomDepthVariation = (Math.random() * 2 - 1) * maxRandomness; // -5% to +5%
            let randomSpeedVariation = (Math.random() * 2 - 1) * maxRandomness; // -5% to +5%
            let adjustedVibratoDepth = vibratoDepthPercent * (1 + randomDepthVariation);
            let adjustedVibratoSpeed = vibratoSpeed * (1 + randomSpeedVariation);
            vibrato.frequency.value = adjustedVibratoSpeed; // Vibrato frequency
            vGain.gain.value = frequency * (adjustedVibratoDepth / 100); // Vibrato depth
          };
  
          // Start the oscillator and vibrato
          oscillator.start();
          vibrato.start();
  
          // Update vibrato parameters every 0.5 seconds
          let vibratoInterval = setInterval(updateVibrato, 500);
  
          // Return a promise that resolves after the sound finishes
          return new Promise((resolve) => {
            setTimeout(function () {
              clearInterval(vibratoInterval);
              resolve();
            }, duration * 1000);
          });
        });
  
        // Wait for all sounds to finish
        await Promise.all(promises);
  
        // Close the AudioContext
        audioCtx.close();
      }