diff --git a/.eslintrc.json b/.eslintrc.json deleted file mode 100644 index a801900..0000000 --- a/.eslintrc.json +++ /dev/null @@ -1,33 +0,0 @@ -{ - "env": { - "browser": true, - "es6": true - }, - "globals": { - "ENV": true - }, - "extends": "eslint:recommended", - "parserOptions": { - "sourceType": "module" - }, - "rules": { - "indent": [ - "error", - 4, - { "SwitchCase": 1 } - ], - "no-unused-vars": "off", - "linebreak-style": [ - "error", - "unix" - ], - "quotes": [ - "error", - "single" - ], - "semi": [ - "error", - "always" - ] - } -} \ No newline at end of file diff --git a/example/16bit-8000.raw b/example/16bit-8000.raw new file mode 100644 index 0000000..a922271 Binary files /dev/null and b/example/16bit-8000.raw differ diff --git a/example/32bit (float)-8000.raw b/example/32bit (float)-8000.raw new file mode 100644 index 0000000..f9404fd Binary files /dev/null and b/example/32bit (float)-8000.raw differ diff --git a/src/pcm-player.js b/src/pcm-player.js index 47bdd72..105683d 100644 --- a/src/pcm-player.js +++ b/src/pcm-player.js @@ -1,71 +1,122 @@ -function PCMPlayer(channels, sampleRate) { +function PCMPlayer(option) { + this.init(option); +} - this.samples = new Float32Array(); - this.flushingTime = 200; - this.channels = channels; - this.sampleRate = sampleRate; - - this.createContext = function() { - this.audioCtx = new (window.AudioContext || window.webkitAudioContext)(); - this.gainNode = this.audioCtx.createGain(); - this.gainNode.gain.value = 1; - this.gainNode.connect(this.audioCtx.destination); - this.startTime = this.audioCtx.currentTime; +PCMPlayer.prototype.init = function(option) { + var default = { + encoding: '16bitInt', + channels: 1, + sampleRate: 8000, + flushingTime: 200 }; + this.option = Object.assign({}, default, option); + this.samples = new Float32Array(); + this.flush = this.flush.bind(this); + this.interval = setInterval(this.flush, this.flushingTime); + this.maxValue = this.getMaxValue(); + this.typedArray = this.getTypedArray(); + this.createContext(); +}; - this.stopFlushing = function() { - if (this.interval) { - clearInterval(this.interval); - } - }; +PCMPlayer.prototype.getMaxValue = function () { + var encodings = { + '8bitInt': 128, + '16bitInt': 32768, + '32bitInt': 2147483648, + '32bitFloat': 1 + } - this.feed = function(data) { - let tmp = new Float32Array(this.samples.length + data.length); - tmp.set(this.samples, 0); - tmp.set(data, this.samples.length); - this.samples = tmp; - }; + return encodings[this.option.encoding] ? encodings[this.option.encoding] : encodings['16bitInt']; +}; + +PCMPlayer.prototype.getTypedArray = function () { + var typedArrays = { + '8bitInt': 'Int8Array', + '16bitInt': 'Int16Array', + '32bitInt': 'Int32Array', + '32bitFloat': 'Float32Array' + } + + return typedArrays[this.option.encoding] ? typedArrays[this.option.encoding] : typedArrays['16bitInt']; +}; + +PCMPlayer.prototype.createContext = function() { + this.audioCtx = new (window.AudioContext || window.webkitAudioContext)(); + this.gainNode = this.audioCtx.createGain(); + this.gainNode.gain.value = 1; + this.gainNode.connect(this.audioCtx.destination); + this.startTime = this.audioCtx.currentTime; +}; + +PCMPlayer.prototype.isTypedArray = function(data) { + return (data.byteLength && data.buffer && data.buffer.constructor == ArrayBuffer); +}; + +PCMPlayer.prototype.feed = function(data) { + if (!this.isTypedArray(isTypedArray)) return; + data = this.getFormatedValue(data); + var tmp = new Float32Array(this.samples.length + data.length); + tmp.set(this.samples, 0); + tmp.set(data, this.samples.length); + this.samples = tmp; +}; - this.flush = function() { - if (!this.channels || !this.sampleRate || !this.samples.length) return; - let bufferSource = this.audioCtx.createBufferSource(), - length = this.samples.length, - audioBuffer = this.audioCtx.createBuffer(this.channels, length, this.sampleRate), - audioData, - channel, - offset, - i, - decrement = 50; +PCMPlayer.prototype.getFormatedValue = function(data) { + var data = new this.typedArray(data.buffer), + float32 = new Float32Array(data.length), + i; - for (channel = 0; channel < this.channels; channel++) { - audioData = audioBuffer.getChannelData(channel); - offset = channel; - for (i = 0; i < length; i++) { - audioData[i] = this.samples[offset]; - /* fadein */ - if (i < 50) { - audioData[i] = (audioData[i] * i) / 50; - } - /* fadeout*/ - if (i >= (length - 51)) { - audioData[i] = (audioData[i] * decrement--) / 50; - } - offset += this.channels; + for (i = 0; i < data.length; i++) { + float32[i] = data[i] / this.maxValue; + } + return float32; +}; + +PCMPlayer.prototype.volume = function(volume) { + this.gainNode.gain.value = volume; +}; + +PCMPlayer.prototype.destroy = function() { + if (this.interval) { + clearInterval(this.interval); + } + this.samples = null; +}; + +PCMPlayer.prototype.flush = function() { + if (!this.samples.length) return; + var bufferSource = this.audioCtx.createBufferSource(), + length = this.samples.length, + audioBuffer = this.audioCtx.createBuffer(this.option.channels, length, this.option.sampleRate), + audioData, + channel, + offset, + i, + decrement = 50; + + for (channel = 0; channel < this.option.channels; channel++) { + audioData = audioBuffer.getChannelData(channel); + offset = channel; + for (i = 0; i < length; i++) { + audioData[i] = this.samples[offset]; + /* fadein */ + if (i < 50) { + audioData[i] = (audioData[i] * i) / 50; } + /* fadeout*/ + if (i >= (length - 51)) { + audioData[i] = (audioData[i] * decrement--) / 50; + } + offset += this.option.channels; } - - if (this.startTime < this.audioCtx.currentTime) { - this.startTime = this.audioCtx.currentTime; - } - bufferSource.buffer = audioBuffer; - bufferSource.connect(this.gainNode); - bufferSource.start(this.startTime); - this.startTime += audioBuffer.duration; - this.samples = new Float32Array(); - }; - - /* initiate start flushing */ - this.flush = this.flush.bind(this); - this.createContext(); - this.interval = setInterval(this.flush, this.flushingTime); -} \ No newline at end of file + } + + if (this.startTime < this.audioCtx.currentTime) { + this.startTime = this.audioCtx.currentTime; + } + bufferSource.buffer = audioBuffer; + bufferSource.connect(this.gainNode); + bufferSource.start(this.startTime); + this.startTime += audioBuffer.duration; + this.samples = new Float32Array(); +}; \ No newline at end of file