Javascript  |  364行  |  10.49 KB

/*
 * Copyright (c) 2014 The Chromium OS Authors. All rights reserved.
 * Use of this source code is governed by a BSD-style license that can be
 * found in the LICENSE file.
 */

var FFT_SIZE = 2048;

var audioContext;
var tonegen;
var recorder;
var drawContext;
var audioPlay, audioBuffer;
var audioSourceType = "sweep";
var recordSourceType = "microphone";

/**
 * Switches Play/Record tab
 * @param {string} tab name
 */
function switchTab(tabName) {
  var canvas_detail = document.getElementsByClassName('canvas_detail');
  switch (tabName) {
    case 'play_tab':
      document.getElementById('record_tab').setAttribute('class', '');
      document.getElementById('record_div').style.display = 'none';
      document.getElementById('play_div').style.display = 'block';
      for (var i = 0; i < canvas_detail.length; i++) {
        canvas_detail[i].style.display = "none";
      }
      drawContext.drawBg();
      break;
    case 'record_tab':
      document.getElementById('play_tab').setAttribute('class', '');
      document.getElementById('play_div').style.display = 'none';
      document.getElementById('record_div').style.display = 'block';
      for (var i = 0; i < canvas_detail.length; i++) {
        canvas_detail[i].style.display = "block";
      }
      drawContext.drawCanvas();
      break;
  }
  document.getElementById(tabName).setAttribute('class', 'selected');
}

function __log(e, data) {
  log.innerHTML += "\n" + e + " " + (data || '');
}

function startUserMedia(stream) {
  var input = audioContext.createMediaStreamSource(stream);
  recorder = new Recorder(input);
}

window.onload = function init() {
  setupSourceLayer(audioSourceType);
  try {
    // webkit shim
    window.AudioContext = window.AudioContext || window.webkitAudioContext;
    navigator.getUserMedia = navigator.getUserMedia ||
        navigator.webkitGetUserMedia;
    window.URL = window.URL || window.webkitURL;

    audioContext = new AudioContext;
  } catch (e) {
    alert('No web audio support in this browser!');
  }

  navigator.getUserMedia({audio: true}, startUserMedia, function(e) {
    alert('No live audio input: ' + e);
  });

  /* Initialize global objects */
  tonegen = new ToneGen();
  audioPlay = new AudioPlay();

  var canvas = document.getElementById('fr_canvas');
  drawContext = new DrawCanvas(canvas, audioContext.sampleRate / 2);
  drawContext.drawBg();
};

/* For Play tab */

/**
 * Sets audio source layer
 * @param {string} audio source type
 */
function setupSourceLayer(value) {
  var sourceTone = document.getElementById('source_tone');
  var sourceFile = document.getElementById('source_file');
  var sweepTone = document.getElementsByClassName('sweep_tone');
  audioSourceType = value;
  switch (value) {
    case 'sine':
      for (var i = 0; i < sweepTone.length; i++) {
        sweepTone[i].style.display = "none";
      }
      document.getElementById('freq_start').value = 1000;
      document.getElementById('freq_end').value = 1000;
      sourceTone.style.display = "block";
      sourceFile.style.display = "none";
      document.getElementById('play_audio').disabled = false;
      break;
    case 'sweep':
      for (var i = 0; i < sweepTone.length; i++) {
        sweepTone[i].style.display = "block";
      }
      document.getElementById('freq_start').value = 20;
      document.getElementById('freq_end').value = 12000;
      sourceTone.style.display = "block";
      sourceFile.style.display = "none";
      document.getElementById('play_audio').disabled = false;
      break;
    case 'file':
      sourceTone.style.display = "none";
      sourceFile.style.display = "block";
      document.getElementById('play_audio').disabled = true;
      break;
  }
}

/**
 * Sets left/right gain
 */
function gainChanged() {
  var leftGain = document.getElementById('left_gain').value;
  var rightGain = document.getElementById('right_gain').value;
  var gainLabel = document.getElementById('gain_label');
  gainLabel.innerHTML = 'L(' + leftGain + ') / R(' + rightGain + ')';
}

/**
 * Checks sine tone generator parameters and sets audio buffer
 */
function toneValueCheckSet() {
  var passed = true;
  var freqStart = parseInt(document.getElementById('freq_start').value);
  var freqEnd = parseInt(document.getElementById('freq_end').value);
  var duration = parseFloat(document.getElementById('tone_sec').value);
  var leftGain = parseInt(document.getElementById('left_gain').value);
  var rightGain = parseInt(document.getElementById('right_gain').value);
  var sweepLog = document.getElementById('sweep_log').checked;

  function isNumber(value, msg) {
    if (isNaN(value) || value <= 0) {
      alert(msg);
      passed = false;
    }
  }

  if (audioSourceType == 'sine') {
    freqEnd = freqStart;
  }

  isNumber(freqStart, "Start frequency should be a positive number.");
  isNumber(freqEnd, "Stop frequency should be a positive number.");
  isNumber(duration, "Duration should be a positive number.");
  if (freqEnd > audioContext.sampleRate / 2) {
    alert('Stop frequency is too large.');
    passed = false;
  }
  if (freqStart < 20) {
    alert('Start frequency is too small.');
    passed = false;
  }
  if (passed) {
    /* Passed value check and generate tone buffer */
    tonegen.setFreq(freqStart, freqEnd, sweepLog);
    tonegen.setDuration(duration);
    tonegen.setGain(leftGain / 20, rightGain / 20);
    tonegen.setSampleRate(audioContext.sampleRate);
    tonegen.genBuffer();
    var buffer = tonegen.getBuffer();
    audioPlay.setBuffer(buffer, document.getElementById('append_tone').checked);
  }
  return passed;
}

function loadAudioFile() {
  document.getElementById('audio_file').click();
}

/**
 * Loads audio file from local drive
 */
function changeAudioFile() {
  function loadAudioDone(filename, buffer) {
    audioBuffer = buffer;
    document.getElementById('play_audio').disabled = false;
  }
  var input = document.getElementById('audio_file');
  document.getElementById('play_audio').disabled = true;
  audioPlay.loadFile(input.files[0], loadAudioDone);
  input.value = '';
}

/**
 * Play audio according source type
 */
function playAudioFile() {
  /**
   * Callback function to draw frequency response of current buffer
   */
  function getInstantBuffer(leftData, rightData, sampleRate) {
    drawContext.drawInstantCurve(leftData, rightData, sampleRate);
  }

  var btn = document.getElementById('play_audio');
  var append = document.getElementById('append_tone').checked;
  if (btn.className == 'btn-off') {
    switch (audioSourceType) {
      case 'sine':
      case 'sweep':
        if (toneValueCheckSet()) {
          audioPlay.play(playAudioFile, getInstantBuffer);
          btn.className = 'btn-on';
        }
        break;
      case 'file':
        audioPlay.setBuffer(audioBuffer, append);
        audioPlay.play(playAudioFile, getInstantBuffer);
        btn.className = 'btn-on';
        break;
    }
  } else {
    audioPlay.stop();
    btn.className = 'btn-off';
    drawContext.drawBg();
  }
}

/* For Record tab */

/**
 * Sets record source type
 * @param {string} record source type
 */
function setupRecordSource(value) {
  recordSourceType = value;
  var autoStop = document.getElementById('auto_stop');
  if (value == 'audio') {
    autoStop.disabled = true;
    autoStop.checked = false;
  } else {
    autoStop.disabled = false;
    autoStop.checked = true;
  }
}

function loadButtonClicked() {
  document.getElementById('sample_file').click();
}

/**
 * Loads sample file to draw frequency response curve into canvas
 */
function loadSampleFile() {
  /**
   * Callback function when file loaded
   * @param {string} file name
   * @param {AudioBuffer} file buffer
   */
  function addFileToCanvas(filename, buffer) {
    var newBuffer = [];
    for (var i = 0; i < buffer.numberOfChannels; i++) {
      newBuffer.push(buffer.getChannelData(i));
    }
    drawContext.add(new AudioCurve(newBuffer, filename, buffer.sampleRate));
  }
  var input = document.getElementById('sample_file');
  audioPlay.loadFile(input.files[0], addFileToCanvas);
  input.value = '';
}

/**
 * Starts/Stops record function
 */
function recordButtonClicked() {
  /**
   * Callback function to draw frequency response of current recorded buffer
   */
  function getInstantBuffer(leftData, rightData, sampleRate, stop) {
    drawContext.drawInstantCurve(leftData, rightData, sampleRate);
    if (stop)
      recordButtonClicked();
  }

  var btn = document.getElementById('record_btn');
  if (btn.className == 'btn-off') {
    var detect = document.getElementById('detect_tone').checked;
    var autoStop = document.getElementById('auto_stop').checked;
    var append = document.getElementById('append_tone').checked;
    if (recordSourceType == 'audio') {
      switch(audioSourceType) {
        case 'sine':
        case 'sweep':
          if (toneValueCheckSet()) {
            audioPlay.play(recordButtonClicked);
            btn.className = 'btn-on';
          }
          break;
        case 'file':
          audioPlay.setBuffer(audioBuffer, append);
          audioPlay.play(recordButtonClicked);
          btn.className = 'btn-on';
          break;
      }
    } else {
      btn.className = 'btn-on';
    }
    recorder.record(getInstantBuffer, detect, autoStop);
  } else {
    recorder.stop();
    if (recordSourceType == 'audio') {
      audioPlay.stop();
    }
    // create WAV download link using audio data blob
    var filename = new Date().toISOString() + '.wav';
    buffer = recorder.getBuffer();
    drawContext.add(new AudioCurve(buffer, filename, audioContext.sampleRate));
    createDownloadLink(filename);
    recorder.clear();
    btn.className = 'btn-off';
  }
}

/**
 * Creates download link of recorded file
 * @param {string} file name
 */
function createDownloadLink(filename) {
  var blob = recorder.exportWAV();
  var url = URL.createObjectURL(blob);
  var table = document.getElementById('record_list');
  var au = document.createElement('audio');
  au.controls = true;
  au.src = url;

  var hf = document.createElement('a');
  hf.href = url;
  hf.download = filename;
  hf.innerHTML = hf.download;

  var tr = table.insertRow(table.rows.length);
  var td_au = tr.insertCell(0);
  var td_hf = tr.insertCell(1);
  td_hf.style = "white-space: nowrap";
  td_au.appendChild(au);
  td_hf.appendChild(hf);
}

/**
 * Exports frequency response CVS file of curves on the canvas
 */
function exportCSV() {
  var hf = document.getElementById('export_csv');
  var noctaves = document.getElementById('noctaves').value;
  content = drawContext.exportCurve(noctaves);
  var blob = new Blob([content], {type: 'application/octet-stream'});
  var url = URL.createObjectURL(blob);
  hf.href = url;
  hf.download = 'audio.csv';
}