mirror of
https://github.com/samirkumardas/pcm-player.git
synced 2025-04-11 04:41:23 +02:00
unit test updated
This commit is contained in:
parent
f65222fc73
commit
aaa272eda5
13 changed files with 333 additions and 87 deletions
|
@ -1,8 +1 @@
|
||||||
git clone git@gitlab.ipvisionsoft.com:ipvision-web/liveplayer.git
|
Coming soon
|
||||||
|
|
||||||
|
|
||||||
cd liveplayer
|
|
||||||
|
|
||||||
npm run build
|
|
||||||
|
|
||||||
npm run start
|
|
|
@ -5,42 +5,30 @@
|
||||||
<title>Opus to PCM</title>
|
<title>Opus to PCM</title>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div id="container" style="width: 400px; margin: 0 auto; font-size: 30px;">
|
<div id="container" style="width: 400px; margin: 0 auto;">
|
||||||
It should play audio if everying went well!
|
<h2>It should play audio if everying went well!</h2>
|
||||||
|
<p>Yea! recoreded audio is not in good qualtity though! </p>
|
||||||
</div>
|
</div>
|
||||||
<script>
|
<script>
|
||||||
window.onload = function() {
|
window.onload = function() {
|
||||||
var orgSampleRate = 8000,
|
var channels = 1,
|
||||||
channels = 1,
|
socketURL = 'ws://localhost:8080';
|
||||||
configFlag = false,
|
|
||||||
socketURL = 'ws://localhost:8080';
|
|
||||||
|
|
||||||
var decoder = new Decoder.OpusToPCM({
|
var decoder = new Decoder.OpusToPCM({
|
||||||
channels: channels
|
channels: channels
|
||||||
});
|
});
|
||||||
|
|
||||||
var player = new PCMPlayer();
|
decoder.on('decode', function(pcmData) {
|
||||||
|
player.feed(pcmData);
|
||||||
|
});
|
||||||
|
|
||||||
|
var player = new PCMPlayer(channels, decoder.getSampleRate());
|
||||||
|
|
||||||
var ws = new WebSocket(socketURL);
|
var ws = new WebSocket(socketURL);
|
||||||
ws.binaryType = 'arraybuffer';
|
ws.binaryType = 'arraybuffer';
|
||||||
ws.addEventListener('message',function(event) {
|
ws.addEventListener('message',function(event) {
|
||||||
var data = new Uint8Array(event.data);
|
var data = new Uint8Array(event.data);
|
||||||
decoder.decode(data).then(function(pcmData) {
|
decoder.decode(data);
|
||||||
|
|
||||||
/*
|
|
||||||
Sample rate in opus is not fixed.
|
|
||||||
It can vary depending on hardware rate. But
|
|
||||||
we need sample rate for playing PCM data using audio context,
|
|
||||||
so lets set sampelRate here returned by decoder.
|
|
||||||
*/
|
|
||||||
if (!configFlag) {
|
|
||||||
var samppleRate = decoder.getSampleRate();
|
|
||||||
player.setConfig(samppleRate, channels);
|
|
||||||
configFlag = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
player.feed(pcmData);
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
58
example/player/pcm-player.js
Normal file
58
example/player/pcm-player.js
Normal file
|
@ -0,0 +1,58 @@
|
||||||
|
function PCMPlayer() {
|
||||||
|
|
||||||
|
this.samples = [];
|
||||||
|
this.flushingTime = 200;
|
||||||
|
this.createContext();
|
||||||
|
this.startFlushing();
|
||||||
|
this.flush = this.flush.bind(this);
|
||||||
|
this.interval = setInterval(this.flush, this.flushingTime);
|
||||||
|
|
||||||
|
this.setConfig = function(sampleRate, channels) {
|
||||||
|
this.sampleRate = sampleRate;
|
||||||
|
this.channels = channels;
|
||||||
|
}
|
||||||
|
|
||||||
|
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.stopFlushing = function() {
|
||||||
|
if (this.interval) {
|
||||||
|
clearInterval(this.interval);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.flush = function() {
|
||||||
|
let bufferSource = this.audioCtx.createBufferSource(),
|
||||||
|
length = this.samples.length,
|
||||||
|
audioBuffer = this.audioCtx.createBuffer(this.channels, length, this.sampleRate),
|
||||||
|
audioData,
|
||||||
|
channel,
|
||||||
|
offset,
|
||||||
|
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];
|
||||||
|
offset += this.channels;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bufferSource.buffer = audioBuffer;
|
||||||
|
bufferSource.connect(this.gainNode);
|
||||||
|
bufferSource.start();
|
||||||
|
this.samples = [];
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,12 +1,9 @@
|
||||||
function PCMPlayer() {
|
function PCMPlayer(channels, sampleRate) {
|
||||||
|
|
||||||
this.samples = new Float32Array();
|
this.samples = new Float32Array();
|
||||||
this.flushingTime = 200;
|
this.flushingTime = 200;
|
||||||
|
this.channels = channels;
|
||||||
this.setConfig = function(sampleRate, channels) {
|
this.sampleRate = sampleRate;
|
||||||
this.sampleRate = sampleRate;
|
|
||||||
this.channels = channels;
|
|
||||||
};
|
|
||||||
|
|
||||||
this.createContext = function() {
|
this.createContext = function() {
|
||||||
this.audioCtx = new (window.AudioContext || window.webkitAudioContext)();
|
this.audioCtx = new (window.AudioContext || window.webkitAudioContext)();
|
||||||
|
|
77
karma.conf.js
Normal file
77
karma.conf.js
Normal file
|
@ -0,0 +1,77 @@
|
||||||
|
// Karma configuration
|
||||||
|
// Generated on Fri Nov 03 2017 16:10:03 GMT+0600 (+06)
|
||||||
|
|
||||||
|
module.exports = function(config) {
|
||||||
|
config.set({
|
||||||
|
|
||||||
|
// base path that will be used to resolve all patterns (eg. files, exclude)
|
||||||
|
basePath: '',
|
||||||
|
|
||||||
|
|
||||||
|
// frameworks to use
|
||||||
|
// available frameworks: https://npmjs.org/browse/keyword/karma-adapter
|
||||||
|
frameworks: ['mocha'],
|
||||||
|
|
||||||
|
// list of files / patterns to load in the browser
|
||||||
|
files: [
|
||||||
|
'test/*.js'
|
||||||
|
],
|
||||||
|
|
||||||
|
// list of files to exclude
|
||||||
|
exclude: [
|
||||||
|
],
|
||||||
|
|
||||||
|
// preprocess matching files before serving them to the browser
|
||||||
|
// available preprocessors: https://npmjs.org/browse/keyword/karma-preprocessor
|
||||||
|
preprocessors: {
|
||||||
|
'test/*.js': ['rollup']
|
||||||
|
},
|
||||||
|
|
||||||
|
rollupPreprocessor: {
|
||||||
|
plugins: [
|
||||||
|
require('rollup-plugin-node-globals')(),
|
||||||
|
require('rollup-plugin-node-builtins')(),
|
||||||
|
require('rollup-plugin-babel')()
|
||||||
|
],
|
||||||
|
format: 'iife', // Helps prevent naming collisions.
|
||||||
|
name: 'Decoder', // Required for 'iife' format.
|
||||||
|
sourcemap: 'inline' // Sensible for testing.
|
||||||
|
},
|
||||||
|
|
||||||
|
// test results reporter to use
|
||||||
|
// possible values: 'dots', 'progress'
|
||||||
|
// available reporters: https://npmjs.org/browse/keyword/karma-reporter
|
||||||
|
reporters: ['progress'],
|
||||||
|
|
||||||
|
|
||||||
|
// web server port
|
||||||
|
port: 9876,
|
||||||
|
|
||||||
|
|
||||||
|
// enable / disable colors in the output (reporters and logs)
|
||||||
|
colors: true,
|
||||||
|
|
||||||
|
|
||||||
|
// level of logging
|
||||||
|
// possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG
|
||||||
|
logLevel: config.LOG_INFO,
|
||||||
|
|
||||||
|
|
||||||
|
// enable / disable watching file and executing tests whenever any file changes
|
||||||
|
autoWatch: false,
|
||||||
|
|
||||||
|
|
||||||
|
// start these browsers
|
||||||
|
// available browser launchers: https://npmjs.org/browse/keyword/karma-launcher
|
||||||
|
browsers: ['Chrome'],
|
||||||
|
|
||||||
|
|
||||||
|
// Continuous Integration mode
|
||||||
|
// if true, Karma captures browsers, runs the tests and exits
|
||||||
|
singleRun: true,
|
||||||
|
|
||||||
|
// Concurrency level
|
||||||
|
// how many browser should be started simultaneous
|
||||||
|
concurrency: Infinity
|
||||||
|
})
|
||||||
|
}
|
19
package.json
19
package.json
|
@ -1,12 +1,12 @@
|
||||||
{
|
{
|
||||||
"name": "opsu-to-pcm",
|
"name": "opus-to-pcm",
|
||||||
"version": "0.1.0",
|
"version": "0.1.0",
|
||||||
"description": "Decode raw opus packet to pcm without using any external library",
|
"description": "Decode raw opus packet to pcm without using any external library",
|
||||||
"main": "dist/opus_to_pcm.js",
|
"main": "dist/opus_to_pcm.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "rollup -c",
|
"build": "rollup -c",
|
||||||
"pro": "NODE_ENV=production rollup -c",
|
"pro": "NODE_ENV=production rollup -c",
|
||||||
"test": "echo \"Error: no test specified\" && exit 1"
|
"test": "karma start karma.conf.js"
|
||||||
},
|
},
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
|
@ -16,13 +16,22 @@
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"babel-plugin-external-helpers": "^6.22.0",
|
"babel-plugin-external-helpers": "^6.22.0",
|
||||||
"babel-preset-es2015": "^6.24.1",
|
"babel-preset-es2015": "^6.24.1",
|
||||||
|
"babel-register": "^6.26.0",
|
||||||
|
"karma": "^1.7.1",
|
||||||
|
"karma-chrome-launcher": "^2.2.0",
|
||||||
|
"karma-mocha": "^1.3.0",
|
||||||
|
"karma-rollup-preprocessor": "^5.0.1",
|
||||||
|
"mocha": "^4.0.1",
|
||||||
"path": "^0.12.7",
|
"path": "^0.12.7",
|
||||||
"rollup": "^0.39.0",
|
"rollup": "^0.50.0",
|
||||||
"rollup-plugin-babel": "^2.7.1",
|
"rollup-plugin-babel": "^2.7.1",
|
||||||
|
"rollup-plugin-buble": "^0.16.0",
|
||||||
"rollup-plugin-eslint": "^3.0.0",
|
"rollup-plugin-eslint": "^3.0.0",
|
||||||
|
"rollup-plugin-node-builtins": "^2.1.2",
|
||||||
|
"rollup-plugin-node-globals": "^1.1.0",
|
||||||
|
"rollup-plugin-node-resolve": "^3.0.0",
|
||||||
"rollup-plugin-replace": "^1.1.1",
|
"rollup-plugin-replace": "^1.1.1",
|
||||||
"rollup-plugin-uglify": "^1.0.1"
|
"rollup-plugin-uglify": "^1.0.1"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,13 +6,14 @@ import eslint from 'rollup-plugin-eslint';
|
||||||
import replace from 'rollup-plugin-replace';
|
import replace from 'rollup-plugin-replace';
|
||||||
import uglify from 'rollup-plugin-uglify';
|
import uglify from 'rollup-plugin-uglify';
|
||||||
|
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
entry: 'src/opus-to-pcm.js',
|
input: 'src/opus-to-pcm.js',
|
||||||
dest: 'dist/opus_to_pcm.js',
|
output: {
|
||||||
format: 'iife',
|
file: 'dist/opus_to_pcm.js',
|
||||||
moduleName: 'Decoder',
|
format: 'iife',
|
||||||
sourceMap: false, //inline
|
name: 'Decoder',
|
||||||
|
sourcemap: false, //inline
|
||||||
|
},
|
||||||
plugins: [
|
plugins: [
|
||||||
eslint(),
|
eslint(),
|
||||||
babel({
|
babel({
|
||||||
|
|
|
@ -1,9 +1,11 @@
|
||||||
import { appendByteArray } from './utils/utils.js';
|
import { appendByteArray } from './utils/utils.js';
|
||||||
|
import Event from './utils/event.js';
|
||||||
import Ogg from './utils/ogg.js';
|
import Ogg from './utils/ogg.js';
|
||||||
import OpusWorker from './utils/opus-worker.js';
|
import OpusWorker from './utils/opus-worker.js';
|
||||||
export class OpusToPCM {
|
export class OpusToPCM extends Event {
|
||||||
|
|
||||||
constructor(options) {
|
constructor(options) {
|
||||||
|
super('decoder');
|
||||||
window.MediaSource = window.MediaSource || window.WebKitMediaSource;
|
window.MediaSource = window.MediaSource || window.WebKitMediaSource;
|
||||||
let nativeSupport = !!(window.MediaSource && window.MediaSource.isTypeSupported('audio/webm; codecs=opus'));
|
let nativeSupport = !!(window.MediaSource && window.MediaSource.isTypeSupported('audio/webm; codecs=opus'));
|
||||||
let defaults = {
|
let defaults = {
|
||||||
|
@ -19,20 +21,29 @@ export class OpusToPCM {
|
||||||
} else {
|
} else {
|
||||||
this.decoder = null;
|
this.decoder = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (this.decoder) {
|
||||||
|
this.decoder.on('data', this.onData.bind(this));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
getSampleRate() {
|
getSampleRate() {
|
||||||
return this.decoder.getSampleRate();
|
return this.decoder.getSampleRate();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
onData(data) {
|
||||||
|
this.dispatch('decode', data);
|
||||||
|
}
|
||||||
|
|
||||||
decode(packet) {
|
decode(packet) {
|
||||||
if (!this.decoder) {
|
if (!this.decoder) {
|
||||||
throw ('opps! no decoder is found to decode');
|
throw ('opps! no decoder is found to decode');
|
||||||
}
|
}
|
||||||
return this.decoder.decode(packet);
|
this.decoder.decode(packet);
|
||||||
}
|
}
|
||||||
|
|
||||||
destroy() {
|
destroy() {
|
||||||
this.decoder.destroy();
|
this.decoder.destroy();
|
||||||
|
this.offAll();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
39
src/utils/event.js
Normal file
39
src/utils/event.js
Normal file
|
@ -0,0 +1,39 @@
|
||||||
|
export default class Event {
|
||||||
|
constructor(type) {
|
||||||
|
this.listener = {};
|
||||||
|
this.type = type | '';
|
||||||
|
}
|
||||||
|
|
||||||
|
on(event, fn) {
|
||||||
|
if (!this.listener[event]) {
|
||||||
|
this.listener[event] = [];
|
||||||
|
}
|
||||||
|
this.listener[event].push(fn);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
off(event, fn) {
|
||||||
|
if (this.listener[event]) {
|
||||||
|
var index = this.listener[event].indexOf(fn);
|
||||||
|
if (index > -1) {
|
||||||
|
this.listener[event].splice(index, 1);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
offAll() {
|
||||||
|
this.listener = {};
|
||||||
|
}
|
||||||
|
|
||||||
|
dispatch(event, data) {
|
||||||
|
if (this.listener[event]) {
|
||||||
|
this.listener[event].map((each) => {
|
||||||
|
each.apply(null, [data]);
|
||||||
|
});
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,15 +1,17 @@
|
||||||
|
import Event from './event.js';
|
||||||
import { appendByteArray } from './utils.js';
|
import { appendByteArray } from './utils.js';
|
||||||
export default class Ogg {
|
export default class Ogg extends Event {
|
||||||
constructor(channel) {
|
constructor(channel) {
|
||||||
this.outSampleRate = 0;
|
super('ogg');
|
||||||
this.channel = channel;
|
this.channel = channel;
|
||||||
this.audioCtx = new (window.AudioContext || window.webkitAudioContext)();
|
this.audioCtx = new (window.AudioContext || window.webkitAudioContext)();
|
||||||
this.resolver = null;
|
this.queue = [];
|
||||||
|
this.flushLimit = 20; /* the larger flush limit, the lesser clicking noise */
|
||||||
this.init();
|
this.init();
|
||||||
}
|
}
|
||||||
|
|
||||||
getSampleRate() {
|
getSampleRate() {
|
||||||
return this.outSampleRate;
|
return this.audioCtx.sampleRate;
|
||||||
}
|
}
|
||||||
|
|
||||||
init() {
|
init() {
|
||||||
|
@ -42,19 +44,18 @@ export default class Ogg {
|
||||||
dv.setUint16( 10, 0, true ); // pre-skip, don't need to skip any value
|
dv.setUint16( 10, 0, true ); // pre-skip, don't need to skip any value
|
||||||
dv.setUint32( 12, 8000, true ); // original sample rate, any valid sample e.g 8000
|
dv.setUint32( 12, 8000, true ); // original sample rate, any valid sample e.g 8000
|
||||||
dv.setUint16( 16, 0, true ); // output gain
|
dv.setUint16( 16, 0, true ); // output gain
|
||||||
dv.setUint8( 18, 0, true ); // channel map 0 = mono or stereo
|
dv.setUint8( 18, 0, true ); // channel map 0 = one stream: mono or stereo
|
||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
getCommentHeader() {
|
getCommentHeader() {
|
||||||
let data = new Uint8Array(24),
|
let data = new Uint8Array(20),
|
||||||
dv = new DataView(data.buffer);
|
dv = new DataView(data.buffer);
|
||||||
dv.setUint32( 0, 1937076303, true ); // Magic Signature 'Opus'
|
dv.setUint32( 0, 1937076303, true ); // Magic Signature 'Opus'
|
||||||
dv.setUint32( 4, 1936154964, true ); // Magic Signature 'Tags'
|
dv.setUint32( 4, 1936154964, true ); // Magic Signature 'Tags'
|
||||||
dv.setUint32( 8, 8, true ); // Vendor Length
|
dv.setUint32( 8, 4, true ); // Vendor Length
|
||||||
dv.setUint32( 12, 1919512167, true ); // Vendor name 'ring'
|
dv.setUint32( 12, 1633837924, true ); // Vendor name 'abcd'
|
||||||
dv.setUint32( 16, 1818850917, true ); // Vendor name 'live'
|
dv.setUint32( 16, 0, true ); // User Comment List Length
|
||||||
dv.setUint32( 20, 0, true ); // User Comment List Length
|
|
||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -84,12 +85,19 @@ export default class Ogg {
|
||||||
return page;
|
return page;
|
||||||
}
|
}
|
||||||
|
|
||||||
getOGG(packet) {
|
getOGG() {
|
||||||
let oggData = this.oggHeader,
|
let oggData = this.oggHeader,
|
||||||
segmentData;
|
packet,
|
||||||
|
segmentData,
|
||||||
|
headerType;
|
||||||
|
|
||||||
|
while (this.queue.length) {
|
||||||
|
packet = this.queue.shift();
|
||||||
|
headerType = this.queue.length == 0 ? 4 : 0; // for last packet, header type should be end of stream
|
||||||
|
segmentData = this.getPage(packet, headerType);
|
||||||
|
oggData = appendByteArray(oggData, segmentData);
|
||||||
|
}
|
||||||
|
|
||||||
segmentData = this.getPage(packet, 4); /* headerType - end of stream i.e 4 */
|
|
||||||
oggData = appendByteArray(oggData, segmentData);
|
|
||||||
this.pageIndex = 2; /* reseting pageIndex to 2 so we can re-use same header */
|
this.pageIndex = 2; /* reseting pageIndex to 2 so we can re-use same header */
|
||||||
return oggData;
|
return oggData;
|
||||||
}
|
}
|
||||||
|
@ -114,27 +122,28 @@ export default class Ogg {
|
||||||
}
|
}
|
||||||
|
|
||||||
decode(packet) {
|
decode(packet) {
|
||||||
let ogg = this.getOGG(packet);
|
this.queue.push(packet);
|
||||||
return new Promise((resolve) => {
|
if (this.queue.length >= this.flushLimit) {
|
||||||
this.audioCtx.decodeAudioData(ogg.buffer, (audioBuffer) => {
|
this.process();
|
||||||
let pcmFloat;
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (!this.outSampleRate) {
|
process() {
|
||||||
this.outSampleRate = audioBuffer.sampleRate;
|
let ogg = this.getOGG();
|
||||||
}
|
this.audioCtx.decodeAudioData(ogg.buffer, (audioBuffer) => {
|
||||||
if (this.channel == 1) {
|
let pcmFloat;
|
||||||
pcmFloat = audioBuffer.getChannelData(0);
|
if (this.channel == 1) {
|
||||||
} else {
|
pcmFloat = audioBuffer.getChannelData(0);
|
||||||
pcmFloat = this.getMergedPCMData(audioBuffer);
|
} else {
|
||||||
}
|
pcmFloat = this.getMergedPCMData(audioBuffer);
|
||||||
resolve(pcmFloat);
|
}
|
||||||
});
|
this.dispatch('data', pcmFloat);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
getMergedPCMData(audioBuffer) {
|
getMergedPCMData(audioBuffer) {
|
||||||
let audioData,
|
let audioData,
|
||||||
result,
|
result = [],
|
||||||
length,
|
length,
|
||||||
pcmFloat,
|
pcmFloat,
|
||||||
offset = 0,
|
offset = 0,
|
||||||
|
@ -148,6 +157,7 @@ export default class Ogg {
|
||||||
|
|
||||||
length = result[0].length;
|
length = result[0].length;
|
||||||
pcmFloat = new Float32Array(this.channel * length);
|
pcmFloat = new Float32Array(this.channel * length);
|
||||||
|
i = 0;
|
||||||
while(length > i) {
|
while(length > i) {
|
||||||
for(j=0; j<this.channel; j++) {
|
for(j=0; j<this.channel; j++) {
|
||||||
pcmFloat[offset++] = result[j][i];
|
pcmFloat[offset++] = result[j][i];
|
||||||
|
@ -161,5 +171,6 @@ export default class Ogg {
|
||||||
this.oggHeader = null;
|
this.oggHeader = null;
|
||||||
this.audioCtx = null;
|
this.audioCtx = null;
|
||||||
this.checksumTable = null;
|
this.checksumTable = null;
|
||||||
|
this.offAll();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
export default class OpusWorker {
|
import Event from './event.js';
|
||||||
|
export default class OpusWorker extends Event {
|
||||||
constructor(channels) {
|
constructor(channels) {
|
||||||
|
super('worker');
|
||||||
this.worker = new Worker('libopus/opus.min.js');
|
this.worker = new Worker('libopus/opus.min.js');
|
||||||
this.resolver = null;
|
|
||||||
this.worker.addEventListener('message', this.onMessage.bind(this));
|
this.worker.addEventListener('message', this.onMessage.bind(this));
|
||||||
this.worker.postMessage({
|
this.worker.postMessage({
|
||||||
type: 'init',
|
type: 'init',
|
||||||
|
@ -22,18 +23,14 @@ export default class OpusWorker {
|
||||||
buffer: packet
|
buffer: packet
|
||||||
};
|
};
|
||||||
this.worker.postMessage(workerData);
|
this.worker.postMessage(workerData);
|
||||||
return new Promise((resolve) => {
|
|
||||||
this.resolver = resolve;
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
onMessage(event) {
|
onMessage(event) {
|
||||||
let data = event.data;
|
let data = event.data;
|
||||||
if (this.resolver) {
|
this.dispatch('data', data.buffer);
|
||||||
this.resolver(data.buffer);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
destroy() {
|
destroy() {
|
||||||
this.worker = null;
|
this.worker = null;
|
||||||
|
this.offAll();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
31
test/event.js
Normal file
31
test/event.js
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
import {equal, deepEqual} from 'assert';
|
||||||
|
import Event from '../src/utils/event.js';
|
||||||
|
|
||||||
|
var event = new Event('XYZ');
|
||||||
|
|
||||||
|
var callback = function() {
|
||||||
|
};
|
||||||
|
|
||||||
|
describe('event tests --', function() {
|
||||||
|
|
||||||
|
beforeEach(function(){
|
||||||
|
event.offAll();
|
||||||
|
event.on('topic1',callback);
|
||||||
|
event.on('topic1',callback);
|
||||||
|
event.on('topic2',callback);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('listener should be 2 for the topic1', function() {
|
||||||
|
equal(event.listener.topic1.length, 2);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('event dispatcher should be return true for a successful dispatcher', function() {
|
||||||
|
equal(event.dispatch('topic1', true), true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('listener should be zero after removing all listeners', function() {
|
||||||
|
event.offAll();
|
||||||
|
deepEqual(event.listener, {});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
34
test/ogg.js
Normal file
34
test/ogg.js
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
import {equal} from 'assert';
|
||||||
|
import Ogg from '../src/utils/ogg.js';
|
||||||
|
var channel = 1,
|
||||||
|
decoder = new Ogg(channel),
|
||||||
|
audioCtx = new (window.AudioContext || window.webkitAudioContext)(),
|
||||||
|
sampleRate = audioCtx.sampleRate;
|
||||||
|
|
||||||
|
|
||||||
|
describe('Ogg tests -- ', function() {
|
||||||
|
it('sample rate should be same system sample rate', function() {
|
||||||
|
equal(decoder.getSampleRate(), sampleRate);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('magic Signature of ID header should be Opus Head', function() {
|
||||||
|
var idHeader = decoder.getIDHeader();
|
||||||
|
var dv = new DataView(idHeader.buffer);
|
||||||
|
equal(dv.getUint32(0, true), 1937076303);
|
||||||
|
equal(dv.getUint32(4, true), 1684104520);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('magic Signature of comment header should be Opus Tags', function() {
|
||||||
|
var commonHeader = decoder.getCommentHeader();
|
||||||
|
var dv = new DataView(commonHeader.buffer);
|
||||||
|
equal(dv.getUint32(0, true), 1937076303);
|
||||||
|
equal(dv.getUint32(4, true), 1936154964);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('page header should be started with OggS', function() {
|
||||||
|
var segmentData = new Uint8Array(20);
|
||||||
|
var page = decoder.getPage(segmentData, 4);
|
||||||
|
var dv = new DataView(page.buffer);
|
||||||
|
equal(dv.getUint32(0, true), 1399285583);
|
||||||
|
});
|
||||||
|
});
|
Loading…
Add table
Reference in a new issue