/* * 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;