364 lines
10 KiB
364 lines
10 KiB
/*
|
|
* 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';
|
|
}
|