Home | History | Annotate | Download | only in frontend
      1 /*
      2  * Copyright (c) 2014 The Chromium OS Authors. All rights reserved.
      3  * Use of this source code is governed by a BSD-style license that can be
      4  * found in the LICENSE file.
      5  */
      6 
      7 var FFT_SIZE = 2048;
      8 
      9 var audioContext;
     10 var tonegen;
     11 var recorder;
     12 var drawContext;
     13 var audioPlay, audioBuffer;
     14 var audioSourceType = "sweep";
     15 var recordSourceType = "microphone";
     16 
     17 /**
     18  * Switches Play/Record tab
     19  * @param {string} tab name
     20  */
     21 function switchTab(tabName) {
     22   var canvas_detail = document.getElementsByClassName('canvas_detail');
     23   switch (tabName) {
     24     case 'play_tab':
     25       document.getElementById('record_tab').setAttribute('class', '');
     26       document.getElementById('record_div').style.display = 'none';
     27       document.getElementById('play_div').style.display = 'block';
     28       for (var i = 0; i < canvas_detail.length; i++) {
     29         canvas_detail[i].style.display = "none";
     30       }
     31       drawContext.drawBg();
     32       break;
     33     case 'record_tab':
     34       document.getElementById('play_tab').setAttribute('class', '');
     35       document.getElementById('play_div').style.display = 'none';
     36       document.getElementById('record_div').style.display = 'block';
     37       for (var i = 0; i < canvas_detail.length; i++) {
     38         canvas_detail[i].style.display = "block";
     39       }
     40       drawContext.drawCanvas();
     41       break;
     42   }
     43   document.getElementById(tabName).setAttribute('class', 'selected');
     44 }
     45 
     46 function __log(e, data) {
     47   log.innerHTML += "\n" + e + " " + (data || '');
     48 }
     49 
     50 function startUserMedia(stream) {
     51   var input = audioContext.createMediaStreamSource(stream);
     52   recorder = new Recorder(input);
     53 }
     54 
     55 window.onload = function init() {
     56   setupSourceLayer(audioSourceType);
     57   try {
     58     // webkit shim
     59     window.AudioContext = window.AudioContext || window.webkitAudioContext;
     60     navigator.getUserMedia = navigator.getUserMedia ||
     61         navigator.webkitGetUserMedia;
     62     window.URL = window.URL || window.webkitURL;
     63 
     64     audioContext = new AudioContext;
     65   } catch (e) {
     66     alert('No web audio support in this browser!');
     67   }
     68 
     69   navigator.getUserMedia({audio: true}, startUserMedia, function(e) {
     70     alert('No live audio input: ' + e);
     71   });
     72 
     73   /* Initialize global objects */
     74   tonegen = new ToneGen();
     75   audioPlay = new AudioPlay();
     76 
     77   var canvas = document.getElementById('fr_canvas');
     78   drawContext = new DrawCanvas(canvas, audioContext.sampleRate / 2);
     79   drawContext.drawBg();
     80 };
     81 
     82 /* For Play tab */
     83 
     84 /**
     85  * Sets audio source layer
     86  * @param {string} audio source type
     87  */
     88 function setupSourceLayer(value) {
     89   var sourceTone = document.getElementById('source_tone');
     90   var sourceFile = document.getElementById('source_file');
     91   var sweepTone = document.getElementsByClassName('sweep_tone');
     92   audioSourceType = value;
     93   switch (value) {
     94     case 'sine':
     95       for (var i = 0; i < sweepTone.length; i++) {
     96         sweepTone[i].style.display = "none";
     97       }
     98       document.getElementById('freq_start').value = 1000;
     99       document.getElementById('freq_end').value = 1000;
    100       sourceTone.style.display = "block";
    101       sourceFile.style.display = "none";
    102       document.getElementById('play_audio').disabled = false;
    103       break;
    104     case 'sweep':
    105       for (var i = 0; i < sweepTone.length; i++) {
    106         sweepTone[i].style.display = "block";
    107       }
    108       document.getElementById('freq_start').value = 20;
    109       document.getElementById('freq_end').value = 12000;
    110       sourceTone.style.display = "block";
    111       sourceFile.style.display = "none";
    112       document.getElementById('play_audio').disabled = false;
    113       break;
    114     case 'file':
    115       sourceTone.style.display = "none";
    116       sourceFile.style.display = "block";
    117       document.getElementById('play_audio').disabled = true;
    118       break;
    119   }
    120 }
    121 
    122 /**
    123  * Sets left/right gain
    124  */
    125 function gainChanged() {
    126   var leftGain = document.getElementById('left_gain').value;
    127   var rightGain = document.getElementById('right_gain').value;
    128   var gainLabel = document.getElementById('gain_label');
    129   gainLabel.innerHTML = 'L(' + leftGain + ') / R(' + rightGain + ')';
    130 }
    131 
    132 /**
    133  * Checks sine tone generator parameters and sets audio buffer
    134  */
    135 function toneValueCheckSet() {
    136   var passed = true;
    137   var freqStart = parseInt(document.getElementById('freq_start').value);
    138   var freqEnd = parseInt(document.getElementById('freq_end').value);
    139   var duration = parseFloat(document.getElementById('tone_sec').value);
    140   var leftGain = parseInt(document.getElementById('left_gain').value);
    141   var rightGain = parseInt(document.getElementById('right_gain').value);
    142   var sweepLog = document.getElementById('sweep_log').checked;
    143 
    144   function isNumber(value, msg) {
    145     if (isNaN(value) || value <= 0) {
    146       alert(msg);
    147       passed = false;
    148     }
    149   }
    150 
    151   if (audioSourceType == 'sine') {
    152     freqEnd = freqStart;
    153   }
    154 
    155   isNumber(freqStart, "Start frequency should be a positive number.");
    156   isNumber(freqEnd, "Stop frequency should be a positive number.");
    157   isNumber(duration, "Duration should be a positive number.");
    158   if (freqEnd > audioContext.sampleRate / 2) {
    159     alert('Stop frequency is too large.');
    160     passed = false;
    161   }
    162   if (freqStart < 20) {
    163     alert('Start frequency is too small.');
    164     passed = false;
    165   }
    166   if (passed) {
    167     /* Passed value check and generate tone buffer */
    168     tonegen.setFreq(freqStart, freqEnd, sweepLog);
    169     tonegen.setDuration(duration);
    170     tonegen.setGain(leftGain / 20, rightGain / 20);
    171     tonegen.setSampleRate(audioContext.sampleRate);
    172     tonegen.genBuffer();
    173     var buffer = tonegen.getBuffer();
    174     audioPlay.setBuffer(buffer, document.getElementById('append_tone').checked);
    175   }
    176   return passed;
    177 }
    178 
    179 function loadAudioFile() {
    180   document.getElementById('audio_file').click();
    181 }
    182 
    183 /**
    184  * Loads audio file from local drive
    185  */
    186 function changeAudioFile() {
    187   function loadAudioDone(filename, buffer) {
    188     audioBuffer = buffer;
    189     document.getElementById('play_audio').disabled = false;
    190   }
    191   var input = document.getElementById('audio_file');
    192   document.getElementById('play_audio').disabled = true;
    193   audioPlay.loadFile(input.files[0], loadAudioDone);
    194   input.value = '';
    195 }
    196 
    197 /**
    198  * Play audio according source type
    199  */
    200 function playAudioFile() {
    201   /**
    202    * Callback function to draw frequency response of current buffer
    203    */
    204   function getInstantBuffer(leftData, rightData, sampleRate) {
    205     drawContext.drawInstantCurve(leftData, rightData, sampleRate);
    206   }
    207 
    208   var btn = document.getElementById('play_audio');
    209   var append = document.getElementById('append_tone').checked;
    210   if (btn.className == 'btn-off') {
    211     switch (audioSourceType) {
    212       case 'sine':
    213       case 'sweep':
    214         if (toneValueCheckSet()) {
    215           audioPlay.play(playAudioFile, getInstantBuffer);
    216           btn.className = 'btn-on';
    217         }
    218         break;
    219       case 'file':
    220         audioPlay.setBuffer(audioBuffer, append);
    221         audioPlay.play(playAudioFile, getInstantBuffer);
    222         btn.className = 'btn-on';
    223         break;
    224     }
    225   } else {
    226     audioPlay.stop();
    227     btn.className = 'btn-off';
    228     drawContext.drawBg();
    229   }
    230 }
    231 
    232 /* For Record tab */
    233 
    234 /**
    235  * Sets record source type
    236  * @param {string} record source type
    237  */
    238 function setupRecordSource(value) {
    239   recordSourceType = value;
    240   var autoStop = document.getElementById('auto_stop');
    241   if (value == 'audio') {
    242     autoStop.disabled = true;
    243     autoStop.checked = false;
    244   } else {
    245     autoStop.disabled = false;
    246     autoStop.checked = true;
    247   }
    248 }
    249 
    250 function loadButtonClicked() {
    251   document.getElementById('sample_file').click();
    252 }
    253 
    254 /**
    255  * Loads sample file to draw frequency response curve into canvas
    256  */
    257 function loadSampleFile() {
    258   /**
    259    * Callback function when file loaded
    260    * @param {string} file name
    261    * @param {AudioBuffer} file buffer
    262    */
    263   function addFileToCanvas(filename, buffer) {
    264     var newBuffer = [];
    265     for (var i = 0; i < buffer.numberOfChannels; i++) {
    266       newBuffer.push(buffer.getChannelData(i));
    267     }
    268     drawContext.add(new AudioCurve(newBuffer, filename, buffer.sampleRate));
    269   }
    270   var input = document.getElementById('sample_file');
    271   audioPlay.loadFile(input.files[0], addFileToCanvas);
    272   input.value = '';
    273 }
    274 
    275 /**
    276  * Starts/Stops record function
    277  */
    278 function recordButtonClicked() {
    279   /**
    280    * Callback function to draw frequency response of current recorded buffer
    281    */
    282   function getInstantBuffer(leftData, rightData, sampleRate, stop) {
    283     drawContext.drawInstantCurve(leftData, rightData, sampleRate);
    284     if (stop)
    285       recordButtonClicked();
    286   }
    287 
    288   var btn = document.getElementById('record_btn');
    289   if (btn.className == 'btn-off') {
    290     var detect = document.getElementById('detect_tone').checked;
    291     var autoStop = document.getElementById('auto_stop').checked;
    292     var append = document.getElementById('append_tone').checked;
    293     if (recordSourceType == 'audio') {
    294       switch(audioSourceType) {
    295         case 'sine':
    296         case 'sweep':
    297           if (toneValueCheckSet()) {
    298             audioPlay.play(recordButtonClicked);
    299             btn.className = 'btn-on';
    300           }
    301           break;
    302         case 'file':
    303           audioPlay.setBuffer(audioBuffer, append);
    304           audioPlay.play(recordButtonClicked);
    305           btn.className = 'btn-on';
    306           break;
    307       }
    308     } else {
    309       btn.className = 'btn-on';
    310     }
    311     recorder.record(getInstantBuffer, detect, autoStop);
    312   } else {
    313     recorder.stop();
    314     if (recordSourceType == 'audio') {
    315       audioPlay.stop();
    316     }
    317     // create WAV download link using audio data blob
    318     var filename = new Date().toISOString() + '.wav';
    319     buffer = recorder.getBuffer();
    320     drawContext.add(new AudioCurve(buffer, filename, audioContext.sampleRate));
    321     createDownloadLink(filename);
    322     recorder.clear();
    323     btn.className = 'btn-off';
    324   }
    325 }
    326 
    327 /**
    328  * Creates download link of recorded file
    329  * @param {string} file name
    330  */
    331 function createDownloadLink(filename) {
    332   var blob = recorder.exportWAV();
    333   var url = URL.createObjectURL(blob);
    334   var table = document.getElementById('record_list');
    335   var au = document.createElement('audio');
    336   au.controls = true;
    337   au.src = url;
    338 
    339   var hf = document.createElement('a');
    340   hf.href = url;
    341   hf.download = filename;
    342   hf.innerHTML = hf.download;
    343 
    344   var tr = table.insertRow(table.rows.length);
    345   var td_au = tr.insertCell(0);
    346   var td_hf = tr.insertCell(1);
    347   td_hf.style = "white-space: nowrap";
    348   td_au.appendChild(au);
    349   td_hf.appendChild(hf);
    350 }
    351 
    352 /**
    353  * Exports frequency response CVS file of curves on the canvas
    354  */
    355 function exportCSV() {
    356   var hf = document.getElementById('export_csv');
    357   var noctaves = document.getElementById('noctaves').value;
    358   content = drawContext.exportCurve(noctaves);
    359   var blob = new Blob([content], {type: 'application/octet-stream'});
    360   var url = URL.createObjectURL(blob);
    361   hf.href = url;
    362   hf.download = 'audio.csv';
    363 }
    364