<template>
  <div>
    <button @click="startAudio">Start</button>
    <button @click="stopAudio">Stop</button>
    <div>{{ level }}</div>
    Masking Gain : <input id="gain-control" v-model="maxGain1000" type="range" min="0" max="1" step="0.01">
    Harmonic Gain : <input id="gain-control" v-model="harmonicGain" type="range" min="0" max="1" step="0.01">
  </div>
</template>

<script>
export default {
  data() {
    return {
      audioContext: null,
      analyserNode: null,
      bufferSize: 4096,
      snapshotInterval: 125,
      snapshotDuration: 5512.5,
      level: 0,
      snapshots: [],
      intervalId: null,
      bandPass1:null,
      Gain1000:null,
      SoundNode:null,
      source100:null,
      maxGain1000:0.5,
      lastGainValue:0,
      maskingGain1000:0,
      harmonicGain:1,
      maskGainControl:0,
    };
  },
  mounted() {
    this.audioContext = new (window.AudioContext || window.webkitAudioContext)();
    this.source100=this.audioContext.createBufferSource()
    this.maskGainControl=this.audioContext.createGain();
    fetch('/masking/MaskingNoise_1000.aac')
        .then(response => response.arrayBuffer())
        .then(arrayBuffer => this.audioContext.decodeAudioData(arrayBuffer))
        .then(audioBuffer => {
          // Set buffer on AudioBufferSourceNode
          this.source100.buffer = audioBuffer;
          this.source100.loop = true;
          // Connect nodes
          // this.source100.connect(compressorNode);
          this.source100.connect(this.maskGainControl);
          // this.Gain1000.connect(this.compressorNode);
          this.maskGainControl.connect(this.audioContext.destination);
          // this.Gain1000.gain.setValueAtTime(1,this.audioCtx.currentTime);
          // Play source node

        });
    this.SoundNode=this.audioContext.createBufferSource();
    this.harmonicGainNode=this.audioContext.createGain();
    fetch('/sounds/Forest.aac')
        .then(response => response.arrayBuffer())
        .then(arrayBuffer => this.audioContext.decodeAudioData(arrayBuffer))
        .then(audioBuffer => {
          // Set buffer on AudioBufferSourceNode
          this.SoundNode.buffer = audioBuffer;
          this.SoundNode.loop = true;
          // compressorNode.attack.value = .25;
          // this.SoundNode.connect(compressorNode);
          // this.harmonicGainNode.gain.value=this.harmonicGain;
          this.currentDestination = this.audioContext.destination;
          this.SoundNode.connect(this.harmonicGainNode);
          this.harmonicGainNode.connect(this.currentDestination);
          // Play source node

          //1102500

        });
    },
  methods: {
    startAudio() {
      // Create the AudioContext


      // Create the AnalyserNode
      this.analyserNode = this.audioContext.createAnalyser();
      this.analyserNode.fftSize = this.bufferSize;
      this.analyserNode.minDecibels = -100;
      this.analyserNode.maxDecibels = -1;
      this.analyserNode.smoothingTimeConstant = 0.95;
      this.bandPass1=this.audioContext.createBiquadFilter();
      this.bandPass1.type='bandpass'
      this.bandPass1.frequency.value=63;
      this.bandPass1.Q.value=1.41;

      // Connect the microphone input to the AnalyserNode
      navigator.mediaDevices.getUserMedia({ audio: true })
          .then((stream) => {
            this.Gain1000=this.audioContext.createGain();
            this.Gain1000.gain.value = Math.pow(10, (-39 / 20));
            const source = this.audioContext.createMediaStreamSource(stream);
            source.connect(this.Gain1000);
            this.bandPass1.connect(this.bandPass1);
            source.connect(this.analyserNode)
            this.source100.start();
            this.SoundNode.start();
          })
          .catch((error) => {
            console.error('Error accessing microphone:', error);
          });

      // Start capturing snapshots at the specified interval
      this.intervalId = setInterval(() => {
        this.captureSnapshot();
      }, this.snapshotInterval);
    },
    stopAudio() {
      // Stop capturing snapshots and clear the interval
      clearInterval(this.intervalId);

      // Disconnect the audio nodes
      this.analyserNode.disconnect();
      this.audioContext.close();

      // Reset the data
      this.snapshots = [];
      this.level = 0;
    },
    captureSnapshot() {
      const bufferLength = this.analyserNode.frequencyBinCount;
      const frequencyData = new Uint8Array(bufferLength);
      this.analyserNode.getByteFrequencyData(frequencyData);

      const snapshot = {
        data: Array.from(frequencyData),
        timestamp: this.audioContext.currentTime,
      };

      this.snapshots.push(snapshot);

      // Remove old snapshots that are older than the snapshot duration
      const oldestTimestamp = snapshot.timestamp - this.snapshotDuration / 1000;
      while (this.snapshots.length > 0 && this.snapshots[0].timestamp < oldestTimestamp) {
        this.snapshots.shift();
        this.calculateLevel();
      }
      this.harmonicGainNode.gain.setValueAtTime(this.harmonicGain,this.audioContext.currentTime);
      // Calculate the integrated level value for the bandpass filter

    },
    calculateLevel() {
      // console.log(this.snapshots);
      const filteredSnapshots = this.snapshots.filter((snapshot) => {
        return (
            snapshot.data[63] >= 0 &&
            snapshot.data[63] <= 255
        );
      });
      // console.log(this.snapshots);

      const levelValues = filteredSnapshots.map((snapshot) => {
        return snapshot.data[63];
      });

      // Convert level values to RMS
      const squaredValues = levelValues.map((value) => {
        return value ** 2;
      });

      console.log(levelValues);
      const sumOfSquares = squaredValues.reduce((acc, value) => acc + value, 0);
      const meanOfSquares = sumOfSquares / squaredValues.length;
      const rms = Math.sqrt(meanOfSquares);

      const dbfsValues = levelValues.map((value) => {
        const rmsValue = value / 255; // Normalize to 0-1 range
        const dbfsValue = 20 * Math.log10(rmsValue);
        return dbfsValue;
      });
      console.log(dbfsValues);

      // Sort the RMS values in ascending order
      const sortedData = dbfsValues.sort((a, b) => a - b);
      // console.log(sortedData);

      // Calculate the 10th percentile value
      const percentile10 = this.calculatePercentile(sortedData, 10);

      // Calculate the 90th percentile value
      const percentile90 = this.calculatePercentile(sortedData, 90);
      const dbfs = 20 * Math.log10(rms / 255);
      console.log(rms);
      this.level = rms;
      this.percentile10 = percentile10;
      this.percentile90 = percentile90;
      console.log('!0th',this.percentile10);
      console.log('90th',this.percentile90);
      console.log("Percentile:",this.percentile90-this.percentile10);
      const percentile=this.percentile90-this.percentile10;
      if (!isFinite(percentile)){
        console.log('ok Inf');
        return;
      }
      const rampTime = 2;
      const startTime = this.audioContext.currentTime;
      // Map the difference to the desired range of GainValues
      const minDifference = 0; // Minimum difference value
      const maxDifference = 10; // Maximum difference value
      const minGainValue = 0.1; // Minimum GainValue
      const maxGainValue = this.maxGain1000; // Maximum GainValue

// Map the difference to the GainValue range
      const mappedGainValue = ((percentile - minDifference) / (maxDifference - minDifference)) *
          (maxGainValue - minGainValue) + minGainValue;
      console.log('Maped Gain',mappedGainValue);

      this.maskGainControl.gain.setTargetAtTime(mappedGainValue,this.audioContext.currentTime,6);


    },


    calculatePercentile(sortedData, percentile) {
      const index = Math.ceil((percentile / 100) * sortedData.length);
      return sortedData[index - 1];
    }
  },
};
</script>
