You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

228 lines
6.1 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 ToneGen = function() {
/**
* Initializes tone generator.
*/
this.init = function() {
this.audioContext = new AudioContext();
}
/**
* Sets sample rate
* @param {int} sample rate
*/
this.setSampleRate = function(sampleRate) {
this.sampleRate = sampleRate;
}
/**
* Sets start/end frequencies and logarithmic sweep
* @param {int} start frequency
* @param {int} end frequency
* @param {boolean} logarithmic sweep or not
*/
this.setFreq = function(freqStart, freqEnd, sweepLog) {
this.freqStart = freqStart;
this.freqEnd = freqEnd;
this.sweepLog = sweepLog;
}
/**
* Sets tone duration
* @param {float} duration in seconds
*/
this.setDuration = function(duration) {
this.duration = parseFloat(duration);
}
/**
* Sets left and right gain value
* @param {float} left gain between 0 and 1
* @param {float} right gain between 0 and 1
*/
this.setGain = function(leftGain, rightGain) {
this.leftGain = parseFloat(leftGain);
this.rightGain = parseFloat(rightGain);
}
/**
* Generates sine tone buffer
*/
this.genBuffer = function() {
this.buffer = this.audioContext.createBuffer(2,
this.sampleRate * this.duration, this.sampleRate);
var leftChannel = this.buffer.getChannelData(0);
var rightChannel = this.buffer.getChannelData(1);
var phi;
var k = this.freqEnd / this.freqStart;
var beta = this.duration / Math.log(k);
for (var i = 0; i < leftChannel.length; i++) {
if (this.sweepLog) {
phi = 2 * Math.PI * this.freqStart * beta *
(Math.pow(k, i / leftChannel.length) - 1.0);
} else {
var f = this.freqStart + (this.freqEnd - this.freqStart) *
i / leftChannel.length / 2;
phi = f * 2 * Math.PI * i / this.sampleRate;
}
leftChannel[i] = this.leftGain * Math.sin(phi);
rightChannel[i] = this.rightGain * Math.sin(phi);
}
}
/**
* Returns generated sine tone buffer
* @return {AudioBuffer} audio buffer
*/
this.getBuffer = function() {
return this.buffer;
}
/**
* Returns append buffer
* @return {AudioBuffer} append audio buffer
*/
this.getAppendTone = function(sampleRate) {
var tone_freq = 1000, duration = 0.5;
this.setFreq(tone_freq, tone_freq, false);
this.setDuration(duration);
this.setGain(1, 1);
this.setSampleRate(sampleRate);
this.genBuffer();
return this.getBuffer();
}
this.init();
}
window.ToneGen = ToneGen;
var AudioPlay = function() {
var playCallback = null;
var sampleRate;
var playing = false;
/**
* Initializes audio play object
*/
this.init = function() {
this.audioContext = new AudioContext();
this.genChannel();
this.buffer = null;
sampleRate = this.audioContext.sampleRate;
}
/**
* Loads audio file
* @param {blob} audio file
* @param {function} callback function when file loaded
*/
this.loadFile = function(file_blob, done_cb) {
if (file_blob) {
var audioContext = this.audioContext;
reader = new FileReader();
reader.onloadend = function(e) {
audioContext.decodeAudioData(e.target.result,
function(buffer) {
done_cb(file_blob.name, buffer);
});
};
reader.readAsArrayBuffer(file_blob);
}
}
/**
* Sets audio path
*/
this.genChannel = function() {
this.node = (this.audioContext.createScriptProcessor ||
this.audioContext.createJavaScriptNode).call(
this.audioContext, 4096, 2, 2);
this.splitter = this.audioContext.createChannelSplitter(2);
this.merger = this.audioContext.createChannelMerger(2);
this.node.connect(this.splitter);
this.splitter.connect(this.merger, 0, 0);
this.splitter.connect(this.merger, 1, 1);
this.merger.connect(this.audioContext.destination);
this.node.onaudioprocess = function(e) {
for (var i = 0; i < e.inputBuffer.numberOfChannels; i++) {
e.outputBuffer.getChannelData(i).set(
e.inputBuffer.getChannelData(i), 0);
}
if (!playing) return;
if (playCallback) {
var tmpLeft = e.inputBuffer.getChannelData(0).subarray(
-FFT_SIZE-1, -1);
var tmpRight = e.inputBuffer.getChannelData(1).subarray(
-FFT_SIZE-1, -1);
playCallback(tmpLeft, tmpRight, sampleRate);
}
}
}
/**
* Plays audio
* @param {function} callback function when audio end
* @param {function} callback function to get current buffer
*/
this.play = function(done_cb, play_cb) {
playCallback = play_cb;
this.source = this.audioContext.createBufferSource();
this.source.buffer = this.buffer;
this.source.onended = function(e) {
playing = false;
this.disconnect();
if (done_cb) {
done_cb();
}
}
this.source.connect(this.node);
this.source.start(0);
playing = true;
}
/**
* Stops audio
*/
this.stop = function() {
playing = false;
this.source.stop();
this.source.disconnect();
}
/**
* Sets audio buffer
* @param {AudioBuffer} audio buffer
* @param {boolean} append tone or not
*/
this.setBuffer = function(buffer, append) {
if (append) {
function copyBuffer(src, dest, offset) {
for (var i = 0; i < dest.numberOfChannels; i++) {
dest.getChannelData(i).set(src.getChannelData(i), offset);
}
}
var appendTone = tonegen.getAppendTone(buffer.sampleRate);
var bufferLength = appendTone.length * 2 + buffer.length;
var newBuffer = this.audioContext.createBuffer(buffer.numberOfChannels,
bufferLength, buffer.sampleRate);
copyBuffer(appendTone, newBuffer, 0);
copyBuffer(buffer, newBuffer, appendTone.length);
copyBuffer(appendTone, newBuffer, appendTone.length + buffer.length);
this.buffer = newBuffer;
} else {
this.buffer = buffer;
}
}
this.init();
}
window.AudioPlay = AudioPlay;