All files / src/tasks/volume volume.js

39.44% Statements 43/109
100% Branches 2/2
28.57% Functions 2/7
39.44% Lines 43/109

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 1101x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x                               1x 1x 1x 1x 1x 1x                           1x 1x                         1x 1x                               1x 1x                       1x 1x 1x  
import AudioCalibrator from '../audioCalibrator';
 
import {sleep} from '../../utils';
 
/**
 *
 */
class Volume extends AudioCalibrator {
  /**
   *
   */
  constructor(numCalibrationRounds = 1, numCalibrationNodes = 1) {
    super(numCalibrationRounds, numCalibrationNodes);
  }
 
  /** @private */
  #CALIBRATION_TONE_FREQUENCY = 1000; // Hz
 
  /** @private */
  #CALIBRATION_TONE_TYPE = 'sine';
 
  /** @private */
  #CALIBRATION_TONE_DURATION = 5; // seconds
 
  /** @private */
  soundGainDBSPL = null;
 
  #getTruncatedSignal = (left = 3.5, right = 4.5) => {
    const start = Math.floor(left * this.sourceSamplingRate);
    const end = Math.floor(right * this.sourceSamplingRate);
    const result = Array.from(this.getLastRecordedSignal().slice(start, end));
    const checkResult = list => {
      const setItem = new Set(list);
      if (setItem.size === 1 && setItem.has(0)) {
        console.warn('The last capture failed, all recorded signal is zero');
      }
      if (setItem.size === 0) {
        console.warn('The last capture failed, no recorded signal to truncate');
      }
    };
    checkResult(result);
    return result;
  };
 
  /**
   * Construct a Calibration Node with the calibration parameters.
   * @private
   */
  #createCalibrationNode = () => {
    const audioContext = this.makeNewSourceAudioContext();
    const oscilator = audioContext.createOscillator();
    const gainNode = audioContext.createGain();

    oscilator.frequency.value = this.#CALIBRATION_TONE_FREQUENCY;
    oscilator.type = this.#CALIBRATION_TONE_TYPE;
    gainNode.gain.value = 0.04;

    oscilator.connect(gainNode);
    gainNode.connect(audioContext.destination);

    this.addCalibrationNode(oscilator);
  };
 
  #playCalibrationAudio = async () => {
    const actualDuration = this.#CALIBRATION_TONE_DURATION * this.numCalibrationNodes;
    const totalDuration = actualDuration * 1.2;
    for (let i = 0; i < this.calibrationNodes.length; i += 1) {
      this.calibrationNodes[i].start(i * this.#CALIBRATION_TONE_DURATION);
      this.calibrationNodes[i].stop(
        i * this.#CALIBRATION_TONE_DURATION + this.#CALIBRATION_TONE_DURATION
      );
    }
    console.log(`Playing a buffer of ${actualDuration} seconds of audio`);
    console.log(`Waiting a total of ${totalDuration} seconds`);
    await sleep(totalDuration);
  };
 
  #sendToServerForProcessing = () => {
    console.log('Sending data to server');
    this.pyServer
      .getVolumeCalibration({
        sampleRate: this.sourceSamplingRate,
        payload: this.#getTruncatedSignal(),
      })
      .then(res => {
        if (this.soundGainDBSPL === null) {
          this.soundGainDBSPL = res;
        }
      })
      .catch(err => {
        console.warn(err);
      });
  };
 
  startCalibration = async stream => {
    do {
      await this.calibrationSteps(
        stream,
        this.#playCalibrationAudio,
        this.#createCalibrationNode,
        this.#sendToServerForProcessing
      );
    } while (this.soundGainDBSPL === null);

    return this.soundGainDBSPL;
  };
}
 
export default Volume;