diff --git a/dist/encoderWorker.min.js b/dist/encoderWorker.min.js index 7f4d241c..d77dbbf2 100644 --- a/dist/encoderWorker.min.js +++ b/dist/encoderWorker.min.js @@ -11,7 +11,7 @@ var ja=[ci,zh,Ah,Bh,Ch,Dh,ci,ci];var ka=[di,Kg];return{___muldsi3:Yh,_sbrk:_h,_s // EMSCRIPTEN_END_ASM -(Module.asmGlobalArg,Module.asmLibraryArg,buffer);var _speex_resampler_destroy=Module["_speex_resampler_destroy"]=asm["_speex_resampler_destroy"];var setThrew=Module["setThrew"]=asm["setThrew"];var _bitshift64Lshr=Module["_bitshift64Lshr"]=asm["_bitshift64Lshr"];var _bitshift64Shl=Module["_bitshift64Shl"]=asm["_bitshift64Shl"];var _bitshift64Ashr=Module["_bitshift64Ashr"]=asm["_bitshift64Ashr"];var _memset=Module["_memset"]=asm["_memset"];var _sbrk=Module["_sbrk"]=asm["_sbrk"];var _i64Add=Module["_i64Add"]=asm["_i64Add"];var _memcpy=Module["_memcpy"]=asm["_memcpy"];var stackAlloc=Module["stackAlloc"]=asm["stackAlloc"];var ___muldi3=Module["___muldi3"]=asm["___muldi3"];var getTempRet0=Module["getTempRet0"]=asm["getTempRet0"];var setTempRet0=Module["setTempRet0"]=asm["setTempRet0"];var _speex_resampler_init=Module["_speex_resampler_init"]=asm["_speex_resampler_init"];var _emscripten_get_global_libc=Module["_emscripten_get_global_libc"]=asm["_emscripten_get_global_libc"];var _opus_encoder_ctl=Module["_opus_encoder_ctl"]=asm["_opus_encoder_ctl"];var stackSave=Module["stackSave"]=asm["stackSave"];var ___muldsi3=Module["___muldsi3"]=asm["___muldsi3"];var _free=Module["_free"]=asm["_free"];var runPostSets=Module["runPostSets"]=asm["runPostSets"];var _opus_encode_float=Module["_opus_encode_float"]=asm["_opus_encode_float"];var establishStackSpace=Module["establishStackSpace"]=asm["establishStackSpace"];var _memmove=Module["_memmove"]=asm["_memmove"];var stackRestore=Module["stackRestore"]=asm["stackRestore"];var _speex_resampler_process_interleaved_float=Module["_speex_resampler_process_interleaved_float"]=asm["_speex_resampler_process_interleaved_float"];var _malloc=Module["_malloc"]=asm["_malloc"];var _opus_encoder_create=Module["_opus_encoder_create"]=asm["_opus_encoder_create"];var dynCall_iiiiiii=Module["dynCall_iiiiiii"]=asm["dynCall_iiiiiii"];var dynCall_viiiiiii=Module["dynCall_viiiiiii"]=asm["dynCall_viiiiiii"];Runtime.stackAlloc=Module["stackAlloc"];Runtime.stackSave=Module["stackSave"];Runtime.stackRestore=Module["stackRestore"];Runtime.establishStackSpace=Module["establishStackSpace"];Runtime.setTempRet0=Module["setTempRet0"];Runtime.getTempRet0=Module["getTempRet0"];Module["asm"]=asm;function ExitStatus(status){this.name="ExitStatus";this.message="Program terminated with exit("+status+")";this.status=status}ExitStatus.prototype=new Error;ExitStatus.prototype.constructor=ExitStatus;var initialStackTop;var preloadStartTime=null;var calledMain=false;dependenciesFulfilled=function runCaller(){if(!Module["calledRun"])run();if(!Module["calledRun"])dependenciesFulfilled=runCaller};Module["callMain"]=Module.callMain=function callMain(args){args=args||[];ensureInitRuntime();var argc=args.length+1;function pad(){for(var i=0;i<4-1;i++){argv.push(0)}}var argv=[allocate(intArrayFromString(Module["thisProgram"]),"i8",ALLOC_NORMAL)];pad();for(var i=0;i0){return}preRun();if(runDependencies>0)return;if(Module["calledRun"])return;function doRun(){if(Module["calledRun"])return;Module["calledRun"]=true;if(ABORT)return;ensureInitRuntime();preMain();if(Module["onRuntimeInitialized"])Module["onRuntimeInitialized"]();if(Module["_main"]&&shouldRunNow)Module["callMain"](args);postRun()}if(Module["setStatus"]){Module["setStatus"]("Running...");setTimeout((function(){setTimeout((function(){Module["setStatus"]("")}),1);doRun()}),1)}else{doRun()}}Module["run"]=Module.run=run;function exit(status,implicit){if(implicit&&Module["noExitRuntime"]){return}if(Module["noExitRuntime"]){}else{ABORT=true;EXITSTATUS=status;STACKTOP=initialStackTop;exitRuntime();if(Module["onExit"])Module["onExit"](status)}if(ENVIRONMENT_IS_NODE){process["exit"](status)}Module["quit"](status,new ExitStatus(status))}Module["exit"]=Module.exit=exit;var abortDecorators=[];function abort(what){if(what!==undefined){Module.print(what);Module.printErr(what);what=JSON.stringify(what)}else{what=""}ABORT=true;EXITSTATUS=1;var extra="\nIf this abort() is unexpected, build with -s ASSERTIONS=1 which can give more information.";var output="abort("+what+") at "+stackTrace()+extra;if(abortDecorators){abortDecorators.forEach((function(decorator){output=decorator(output,what)}))}throw output}Module["abort"]=Module.abort=abort;if(Module["preInit"]){if(typeof Module["preInit"]=="function")Module["preInit"]=[Module["preInit"]];while(Module["preInit"].length>0){Module["preInit"].pop()()}}var shouldRunNow=true;if(Module["noInitialRun"]){shouldRunNow=false}run();"use strict";var encoder;self["onmessage"]=(function(e){switch(e["data"]["command"]){case"encode":if(encoder){encoder.encode(e["data"]["buffers"])}break;case"done":if(encoder){encoder.encodeFinalFrame()}break;case"init":encoder=new OggOpusEncoder(e["data"]);break;default:}});var OggOpusEncoder=(function(config){this.numberOfChannels=config["numberOfChannels"]||1;this.originalSampleRate=config["originalSampleRate"];this.encoderSampleRate=config["encoderSampleRate"]||48e3;this.maxBuffersPerPage=config["maxBuffersPerPage"]||40;this.encoderApplication=config["encoderApplication"]||2049;this.encoderFrameSize=config["encoderFrameSize"]||20;this.encoderComplexity=config["encoderComplexity"];this.bufferLength=config["bufferLength"]||4096;this.resampleQuality=config["resampleQuality"]||3;this.bitRate=config["bitRate"];this.pageIndex=0;this.granulePosition=0;this.segmentData=new Uint8Array(65025);this.segmentDataIndex=0;this.segmentTable=new Uint8Array(255);this.segmentTableIndex=0;this.buffersInPage=0;this.serial=Math.floor(Math.random()*Math.pow(2,32));this.initChecksumTable();this.initCodec();this.initResampler();this.generateIdPage();this.generateCommentPage();if(this.numberOfChannels===1){this.interleave=(function(buffers){return buffers[0]})}else{this.interleavedBuffers=new Float32Array(this.bufferLength*this.numberOfChannels)}});OggOpusEncoder.prototype.encode=(function(buffers){var samples=this.interleave(buffers);var sampleIndex=0;while(sampleIndex=this.maxBuffersPerPage){this.generatePage()}});OggOpusEncoder.prototype.encodeFinalFrame=(function(){var finalFrameBuffers=[];for(var i=0;i>>24&255^data[i]]}return checksum>>>0});OggOpusEncoder.prototype.generateCommentPage=(function(){var segmentDataView=new DataView(this.segmentData.buffer);segmentDataView.setUint32(0,1937076303,true);segmentDataView.setUint32(4,1936154964,true);segmentDataView.setUint32(8,8,true);segmentDataView.setUint32(12,1868784978,true);segmentDataView.setUint32(16,1919247474,true);segmentDataView.setUint32(20,0,true);this.segmentTableIndex=1;this.segmentDataIndex=this.segmentTable[0]=24;this.headerType=0;this.generatePage()});OggOpusEncoder.prototype.generateIdPage=(function(){var segmentDataView=new DataView(this.segmentData.buffer);segmentDataView.setUint32(0,1937076303,true);segmentDataView.setUint32(4,1684104520,true);segmentDataView.setUint8(8,1,true);segmentDataView.setUint8(9,this.numberOfChannels,true);segmentDataView.setUint16(10,3840,true);segmentDataView.setUint32(12,this.originalSampleRate,true);segmentDataView.setUint16(16,0,true);segmentDataView.setUint8(18,0,true);this.segmentTableIndex=1;this.segmentDataIndex=this.segmentTable[0]=19;this.headerType=2;this.generatePage()});OggOpusEncoder.prototype.generatePage=(function(){var granulePosition=this.lastPositiveGranulePosition===this.granulePosition?-1:this.granulePosition;var pageBuffer=new ArrayBuffer(27+this.segmentTableIndex+this.segmentDataIndex);var pageBufferView=new DataView(pageBuffer);var page=new Uint8Array(pageBuffer);pageBufferView.setUint32(0,1399285583,true);pageBufferView.setUint8(4,0,true);pageBufferView.setUint8(5,this.headerType,true);pageBufferView.setUint32(6,granulePosition,true);if(granulePosition>4294967296||granulePosition<0){pageBufferView.setUint32(10,Math.floor(granulePosition/4294967296),true)}pageBufferView.setUint32(14,this.serial,true);pageBufferView.setUint32(18,this.pageIndex++,true);pageBufferView.setUint8(26,this.segmentTableIndex,true);page.set(this.segmentTable.subarray(0,this.segmentTableIndex),27);page.set(this.segmentData.subarray(0,this.segmentDataIndex),27+this.segmentTableIndex);pageBufferView.setUint32(22,this.getChecksum(page),true);self["postMessage"](page,[page.buffer]);this.segmentTableIndex=0;this.segmentDataIndex=0;this.buffersInPage=0;if(granulePosition>0){this.lastPositiveGranulePosition=granulePosition}});OggOpusEncoder.prototype.initChecksumTable=(function(){this.checksumTable=[];for(var i=0;i<256;i++){var r=i<<24;for(var j=0;j<8;j++){r=(r&2147483648)!=0?r<<1^79764919:r<<1}this.checksumTable[i]=r&4294967295}});OggOpusEncoder.prototype.initCodec=(function(){var errLocation=_malloc(4);this.encoder=_opus_encoder_create(this.encoderSampleRate,this.numberOfChannels,this.encoderApplication,errLocation);_free(errLocation);if(this.bitRate){var bitRateLocation=_malloc(4);HEAP32[bitRateLocation>>2]=this.bitRate;_opus_encoder_ctl(this.encoder,4002,bitRateLocation);_free(bitRateLocation)}if(this.encoderComplexity){var encoderComplexityLocation=_malloc(4);_opus_encoder_ctl(this.encoder,4010,encoderComplexityLocation);_free(encoderComplexityLocation)}this.encoderSamplesPerChannel=this.encoderSampleRate*this.encoderFrameSize/1e3;this.encoderSamplesPerChannelPointer=_malloc(4);HEAP32[this.encoderSamplesPerChannelPointer>>2]=this.encoderSamplesPerChannel;this.encoderBufferLength=this.encoderSamplesPerChannel*this.numberOfChannels;this.encoderBufferPointer=_malloc(this.encoderBufferLength*4);this.encoderBuffer=HEAPF32.subarray(this.encoderBufferPointer>>2,(this.encoderBufferPointer>>2)+this.encoderBufferLength);this.encoderOutputMaxLength=4e3;this.encoderOutputPointer=_malloc(this.encoderOutputMaxLength);this.encoderOutputBuffer=HEAPU8.subarray(this.encoderOutputPointer,this.encoderOutputPointer+this.encoderOutputMaxLength)});OggOpusEncoder.prototype.initResampler=(function(){var errLocation=_malloc(4);this.resampler=_speex_resampler_init(this.numberOfChannels,this.originalSampleRate,this.encoderSampleRate,this.resampleQuality,errLocation);_free(errLocation);this.resampleBufferIndex=0;this.resampleSamplesPerChannel=this.originalSampleRate*this.encoderFrameSize/1e3;this.resampleSamplesPerChannelPointer=_malloc(4);HEAP32[this.resampleSamplesPerChannelPointer>>2]=this.resampleSamplesPerChannel;this.resampleBufferLength=this.resampleSamplesPerChannel*this.numberOfChannels;this.resampleBufferPointer=_malloc(this.resampleBufferLength*4);this.resampleBuffer=HEAPF32.subarray(this.resampleBufferPointer>>2,(this.resampleBufferPointer>>2)+this.resampleBufferLength)});OggOpusEncoder.prototype.interleave=(function(buffers){for(var i=0;i=0){if(this.segmentTableIndex===255){this.generatePage();this.headerType=1}var segmentLength=Math.min(packetLength,255);this.segmentTable[this.segmentTableIndex++]=segmentLength;this.segmentData.set(this.encoderOutputBuffer.subarray(packetIndex,packetIndex+segmentLength),this.segmentDataIndex);this.segmentDataIndex+=segmentLength;packetIndex+=segmentLength;packetLength-=255}this.granulePosition+=48*this.encoderFrameSize;if(this.segmentTableIndex===255){this.generatePage();this.headerType=0}}) +(Module.asmGlobalArg,Module.asmLibraryArg,buffer);var _speex_resampler_destroy=Module["_speex_resampler_destroy"]=asm["_speex_resampler_destroy"];var setThrew=Module["setThrew"]=asm["setThrew"];var _bitshift64Lshr=Module["_bitshift64Lshr"]=asm["_bitshift64Lshr"];var _bitshift64Shl=Module["_bitshift64Shl"]=asm["_bitshift64Shl"];var _bitshift64Ashr=Module["_bitshift64Ashr"]=asm["_bitshift64Ashr"];var _memset=Module["_memset"]=asm["_memset"];var _sbrk=Module["_sbrk"]=asm["_sbrk"];var _i64Add=Module["_i64Add"]=asm["_i64Add"];var _memcpy=Module["_memcpy"]=asm["_memcpy"];var stackAlloc=Module["stackAlloc"]=asm["stackAlloc"];var ___muldi3=Module["___muldi3"]=asm["___muldi3"];var getTempRet0=Module["getTempRet0"]=asm["getTempRet0"];var setTempRet0=Module["setTempRet0"]=asm["setTempRet0"];var _speex_resampler_init=Module["_speex_resampler_init"]=asm["_speex_resampler_init"];var _emscripten_get_global_libc=Module["_emscripten_get_global_libc"]=asm["_emscripten_get_global_libc"];var _opus_encoder_ctl=Module["_opus_encoder_ctl"]=asm["_opus_encoder_ctl"];var stackSave=Module["stackSave"]=asm["stackSave"];var ___muldsi3=Module["___muldsi3"]=asm["___muldsi3"];var _free=Module["_free"]=asm["_free"];var runPostSets=Module["runPostSets"]=asm["runPostSets"];var _opus_encode_float=Module["_opus_encode_float"]=asm["_opus_encode_float"];var establishStackSpace=Module["establishStackSpace"]=asm["establishStackSpace"];var _memmove=Module["_memmove"]=asm["_memmove"];var stackRestore=Module["stackRestore"]=asm["stackRestore"];var _speex_resampler_process_interleaved_float=Module["_speex_resampler_process_interleaved_float"]=asm["_speex_resampler_process_interleaved_float"];var _malloc=Module["_malloc"]=asm["_malloc"];var _opus_encoder_create=Module["_opus_encoder_create"]=asm["_opus_encoder_create"];var dynCall_iiiiiii=Module["dynCall_iiiiiii"]=asm["dynCall_iiiiiii"];var dynCall_viiiiiii=Module["dynCall_viiiiiii"]=asm["dynCall_viiiiiii"];Runtime.stackAlloc=Module["stackAlloc"];Runtime.stackSave=Module["stackSave"];Runtime.stackRestore=Module["stackRestore"];Runtime.establishStackSpace=Module["establishStackSpace"];Runtime.setTempRet0=Module["setTempRet0"];Runtime.getTempRet0=Module["getTempRet0"];Module["asm"]=asm;function ExitStatus(status){this.name="ExitStatus";this.message="Program terminated with exit("+status+")";this.status=status}ExitStatus.prototype=new Error;ExitStatus.prototype.constructor=ExitStatus;var initialStackTop;var preloadStartTime=null;var calledMain=false;dependenciesFulfilled=function runCaller(){if(!Module["calledRun"])run();if(!Module["calledRun"])dependenciesFulfilled=runCaller};Module["callMain"]=Module.callMain=function callMain(args){args=args||[];ensureInitRuntime();var argc=args.length+1;function pad(){for(var i=0;i<4-1;i++){argv.push(0)}}var argv=[allocate(intArrayFromString(Module["thisProgram"]),"i8",ALLOC_NORMAL)];pad();for(var i=0;i0){return}preRun();if(runDependencies>0)return;if(Module["calledRun"])return;function doRun(){if(Module["calledRun"])return;Module["calledRun"]=true;if(ABORT)return;ensureInitRuntime();preMain();if(Module["onRuntimeInitialized"])Module["onRuntimeInitialized"]();if(Module["_main"]&&shouldRunNow)Module["callMain"](args);postRun()}if(Module["setStatus"]){Module["setStatus"]("Running...");setTimeout((function(){setTimeout((function(){Module["setStatus"]("")}),1);doRun()}),1)}else{doRun()}}Module["run"]=Module.run=run;function exit(status,implicit){if(implicit&&Module["noExitRuntime"]){return}if(Module["noExitRuntime"]){}else{ABORT=true;EXITSTATUS=status;STACKTOP=initialStackTop;exitRuntime();if(Module["onExit"])Module["onExit"](status)}if(ENVIRONMENT_IS_NODE){process["exit"](status)}Module["quit"](status,new ExitStatus(status))}Module["exit"]=Module.exit=exit;var abortDecorators=[];function abort(what){if(what!==undefined){Module.print(what);Module.printErr(what);what=JSON.stringify(what)}else{what=""}ABORT=true;EXITSTATUS=1;var extra="\nIf this abort() is unexpected, build with -s ASSERTIONS=1 which can give more information.";var output="abort("+what+") at "+stackTrace()+extra;if(abortDecorators){abortDecorators.forEach((function(decorator){output=decorator(output,what)}))}throw output}Module["abort"]=Module.abort=abort;if(Module["preInit"]){if(typeof Module["preInit"]=="function")Module["preInit"]=[Module["preInit"]];while(Module["preInit"].length>0){Module["preInit"].pop()()}}var shouldRunNow=true;if(Module["noInitialRun"]){shouldRunNow=false}run();"use strict";var root=typeof self==="object"&&self.self===self&&self||typeof global==="object"&&global.global===global&&global||this;((function(global){var encoder;global["onmessage"]=(function(e){switch(e["data"]["command"]){case"encode":if(encoder){encoder.encode(e["data"]["buffers"])}break;case"done":if(encoder){encoder.encodeFinalFrame()}break;case"init":encoder=new OggOpusEncoder(e["data"]);break;default:}});var OggOpusEncoder=(function(config){this.numberOfChannels=config["numberOfChannels"]||1;this.originalSampleRate=config["originalSampleRate"];this.encoderSampleRate=config["encoderSampleRate"]||48e3;this.maxBuffersPerPage=config["maxBuffersPerPage"]||40;this.encoderApplication=config["encoderApplication"]||2049;this.encoderFrameSize=config["encoderFrameSize"]||20;this.encoderComplexity=config["encoderComplexity"];this.bufferLength=config["bufferLength"]||4096;this.resampleQuality=config["resampleQuality"]||3;this.bitRate=config["bitRate"];this.pageIndex=0;this.granulePosition=0;this.segmentData=new Uint8Array(65025);this.segmentDataIndex=0;this.segmentTable=new Uint8Array(255);this.segmentTableIndex=0;this.buffersInPage=0;this.serial=Math.floor(Math.random()*Math.pow(2,32));this.initChecksumTable();this.initCodec();this.initResampler();this.generateIdPage();this.generateCommentPage();if(this.numberOfChannels===1){this.interleave=(function(buffers){return buffers[0]})}else{this.interleavedBuffers=new Float32Array(this.bufferLength*this.numberOfChannels)}});OggOpusEncoder._opus_encoder_create=_opus_encoder_create;OggOpusEncoder._opus_encoder_ctl=_opus_encoder_ctl;OggOpusEncoder._speex_resampler_process_interleaved_float=_speex_resampler_process_interleaved_float;OggOpusEncoder._speex_resampler_init=_speex_resampler_init;OggOpusEncoder._opus_encode_float=_opus_encode_float;OggOpusEncoder._free=_free;OggOpusEncoder._malloc=_malloc;OggOpusEncoder.prototype.encode=(function(buffers){var samples=this.interleave(buffers);var sampleIndex=0;while(sampleIndex=this.maxBuffersPerPage){this.generatePage()}});OggOpusEncoder.prototype.encodeFinalFrame=(function(){var finalFrameBuffers=[];for(var i=0;i>>24&255^data[i]]}return checksum>>>0});OggOpusEncoder.prototype.generateCommentPage=(function(){var segmentDataView=new DataView(this.segmentData.buffer);segmentDataView.setUint32(0,1937076303,true);segmentDataView.setUint32(4,1936154964,true);segmentDataView.setUint32(8,8,true);segmentDataView.setUint32(12,1868784978,true);segmentDataView.setUint32(16,1919247474,true);segmentDataView.setUint32(20,0,true);this.segmentTableIndex=1;this.segmentDataIndex=this.segmentTable[0]=24;this.headerType=0;this.generatePage()});OggOpusEncoder.prototype.generateIdPage=(function(){var segmentDataView=new DataView(this.segmentData.buffer);segmentDataView.setUint32(0,1937076303,true);segmentDataView.setUint32(4,1684104520,true);segmentDataView.setUint8(8,1,true);segmentDataView.setUint8(9,this.numberOfChannels,true);segmentDataView.setUint16(10,3840,true);segmentDataView.setUint32(12,this.originalSampleRate,true);segmentDataView.setUint16(16,0,true);segmentDataView.setUint8(18,0,true);this.segmentTableIndex=1;this.segmentDataIndex=this.segmentTable[0]=19;this.headerType=2;this.generatePage()});OggOpusEncoder.prototype.generatePage=(function(){var granulePosition=this.lastPositiveGranulePosition===this.granulePosition?-1:this.granulePosition;var pageBuffer=new ArrayBuffer(27+this.segmentTableIndex+this.segmentDataIndex);var pageBufferView=new DataView(pageBuffer);var page=new Uint8Array(pageBuffer);pageBufferView.setUint32(0,1399285583,true);pageBufferView.setUint8(4,0,true);pageBufferView.setUint8(5,this.headerType,true);pageBufferView.setUint32(6,granulePosition,true);if(granulePosition>4294967296||granulePosition<0){pageBufferView.setUint32(10,Math.floor(granulePosition/4294967296),true)}pageBufferView.setUint32(14,this.serial,true);pageBufferView.setUint32(18,this.pageIndex++,true);pageBufferView.setUint8(26,this.segmentTableIndex,true);page.set(this.segmentTable.subarray(0,this.segmentTableIndex),27);page.set(this.segmentData.subarray(0,this.segmentDataIndex),27+this.segmentTableIndex);pageBufferView.setUint32(22,this.getChecksum(page),true);global["postMessage"](page,[page.buffer]);this.segmentTableIndex=0;this.segmentDataIndex=0;this.buffersInPage=0;if(granulePosition>0){this.lastPositiveGranulePosition=granulePosition}});OggOpusEncoder.prototype.initChecksumTable=(function(){this.checksumTable=[];for(var i=0;i<256;i++){var r=i<<24;for(var j=0;j<8;j++){r=(r&2147483648)!=0?r<<1^79764919:r<<1}this.checksumTable[i]=r&4294967295}});OggOpusEncoder.prototype.initCodec=(function(){var errLocation=OggOpusEncoder._malloc(4);this.encoder=OggOpusEncoder._opus_encoder_create(this.encoderSampleRate,this.numberOfChannels,this.encoderApplication,errLocation);OggOpusEncoder._free(errLocation);if(this.bitRate){var bitRateLocation=OggOpusEncoder._malloc(4);HEAP32[bitRateLocation>>2]=this.bitRate;OggOpusEncoder._opus_encoder_ctl(this.encoder,4002,bitRateLocation);OggOpusEncoder._free(bitRateLocation)}if(this.encoderComplexity){var encoderComplexityLocation=OggOpusEncoder._malloc(4);HEAP32[encoderComplexityLocation>>2]=this.encoderComplexity;OggOpusEncoder._opus_encoder_ctl(this.encoder,4010,encoderComplexityLocation);OggOpusEncoder._free(encoderComplexityLocation)}this.encoderSamplesPerChannel=this.encoderSampleRate*this.encoderFrameSize/1e3;this.encoderSamplesPerChannelPointer=OggOpusEncoder._malloc(4);HEAP32[this.encoderSamplesPerChannelPointer>>2]=this.encoderSamplesPerChannel;this.encoderBufferLength=this.encoderSamplesPerChannel*this.numberOfChannels;this.encoderBufferPointer=OggOpusEncoder._malloc(this.encoderBufferLength*4);this.encoderBuffer=HEAPF32.subarray(this.encoderBufferPointer>>2,(this.encoderBufferPointer>>2)+this.encoderBufferLength);this.encoderOutputMaxLength=4e3;this.encoderOutputPointer=OggOpusEncoder._malloc(this.encoderOutputMaxLength);this.encoderOutputBuffer=HEAPU8.subarray(this.encoderOutputPointer,this.encoderOutputPointer+this.encoderOutputMaxLength)});OggOpusEncoder.prototype.initResampler=(function(){var errLocation=OggOpusEncoder._malloc(4);this.resampler=OggOpusEncoder._speex_resampler_init(this.numberOfChannels,this.originalSampleRate,this.encoderSampleRate,this.resampleQuality,errLocation);OggOpusEncoder._free(errLocation);this.resampleBufferIndex=0;this.resampleSamplesPerChannel=this.originalSampleRate*this.encoderFrameSize/1e3;this.resampleSamplesPerChannelPointer=OggOpusEncoder._malloc(4);HEAP32[this.resampleSamplesPerChannelPointer>>2]=this.resampleSamplesPerChannel;this.resampleBufferLength=this.resampleSamplesPerChannel*this.numberOfChannels;this.resampleBufferPointer=OggOpusEncoder._malloc(this.resampleBufferLength*4);this.resampleBuffer=HEAPF32.subarray(this.resampleBufferPointer>>2,(this.resampleBufferPointer>>2)+this.resampleBufferLength)});OggOpusEncoder.prototype.interleave=(function(buffers){for(var i=0;i=0){if(this.segmentTableIndex===255){this.generatePage();this.headerType=1}var segmentLength=Math.min(packetLength,255);this.segmentTable[this.segmentTableIndex++]=segmentLength;this.segmentData.set(this.encoderOutputBuffer.subarray(packetIndex,packetIndex+segmentLength),this.segmentDataIndex);this.segmentDataIndex+=segmentLength;packetIndex+=segmentLength;packetLength-=255}this.granulePosition+=48*this.encoderFrameSize;if(this.segmentTableIndex===255){this.generatePage();this.headerType=0}});global.OggOpusEncoder=OggOpusEncoder;if(typeof module=="object"&&module.exports){module.exports=OggOpusEncoder}}))(root) diff --git a/package.json b/package.json index c6c1c9f1..696e4c5e 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "opus-recorder", - "version": "0.6.0", + "version": "0.6.1", "description": "A library for recording opus encoded audio", "homepage": "https://github.com/chris-rudmin/Recorderjs", "author": "Chris Rudmin", diff --git a/src/encoderWorker.js b/src/encoderWorker.js index 0bb9883a..ad57664e 100644 --- a/src/encoderWorker.js +++ b/src/encoderWorker.js @@ -1,260 +1,282 @@ "use strict"; -var encoder; -self['onmessage'] = function( e ){ - switch( e['data']['command'] ){ +var root = (typeof self === 'object' && self.self === self && self) || (typeof global === 'object' && global.global === global && global) || this; - case 'encode': - if (encoder){ - encoder.encode( e['data']['buffers'] ); - } - break; - - case 'done': - if (encoder) { - encoder.encodeFinalFrame(); - } - break; +(function( global ) { - case 'init': - encoder = new OggOpusEncoder( e['data'] ); - break; + var encoder; + global['onmessage'] = function( e ){ + switch( e['data']['command'] ){ - default: - // Ignore any unknown commands and continue recieving commands - } -}; - -var OggOpusEncoder = function( config ){ - this.numberOfChannels = config['numberOfChannels'] || 1; - this.originalSampleRate = config['originalSampleRate']; - this.encoderSampleRate = config['encoderSampleRate'] || 48000; - this.maxBuffersPerPage = config['maxBuffersPerPage'] || 40; // Limit latency for streaming - this.encoderApplication = config['encoderApplication'] || 2049; // 2048 = Voice, 2049 = Full Band Audio, 2051 = Restricted Low Delay - this.encoderFrameSize = config['encoderFrameSize'] || 20; // 20ms frame - this.encoderComplexity = config['encoderComplexity']; // Value between 0 and 10 inclusive. 10 being highest quality. - this.bufferLength = config['bufferLength'] || 4096; - this.resampleQuality = config['resampleQuality'] || 3; // Value between 0 and 10 inclusive. 10 being highest quality. - this.bitRate = config['bitRate']; - - this.pageIndex = 0; - this.granulePosition = 0; - this.segmentData = new Uint8Array( 65025 ); - this.segmentDataIndex = 0; - this.segmentTable = new Uint8Array( 255 ); - this.segmentTableIndex = 0; - this.buffersInPage = 0; - this.serial = Math.floor( Math.random() * Math.pow(2,32) ); - - this.initChecksumTable(); - this.initCodec(); - this.initResampler(); - this.generateIdPage(); - this.generateCommentPage(); - - if ( this.numberOfChannels === 1 ) { - this.interleave = function( buffers ) { return buffers[0]; }; - } - else { - this.interleavedBuffers = new Float32Array( this.bufferLength * this.numberOfChannels ); - } -}; + case 'encode': + if (encoder){ + encoder.encode( e['data']['buffers'] ); + } + break; -OggOpusEncoder.prototype.encode = function( buffers ) { - var samples = this.interleave( buffers ); - var sampleIndex = 0; + case 'done': + if (encoder) { + encoder.encodeFinalFrame(); + } + break; - while ( sampleIndex < samples.length ) { + case 'init': + encoder = new OggOpusEncoder( e['data'] ); + break; - var lengthToCopy = Math.min( this.resampleBufferLength - this.resampleBufferIndex, samples.length - sampleIndex ); - this.resampleBuffer.set( samples.subarray( sampleIndex, sampleIndex+lengthToCopy ), this.resampleBufferIndex ); - sampleIndex += lengthToCopy; - this.resampleBufferIndex += lengthToCopy; + default: + // Ignore any unknown commands and continue recieving commands + } + }; + + var OggOpusEncoder = function( config ){ + this.numberOfChannels = config['numberOfChannels'] || 1; + this.originalSampleRate = config['originalSampleRate']; + this.encoderSampleRate = config['encoderSampleRate'] || 48000; + this.maxBuffersPerPage = config['maxBuffersPerPage'] || 40; // Limit latency for streaming + this.encoderApplication = config['encoderApplication'] || 2049; // 2048 = Voice, 2049 = Full Band Audio, 2051 = Restricted Low Delay + this.encoderFrameSize = config['encoderFrameSize'] || 20; // 20ms frame + this.encoderComplexity = config['encoderComplexity']; // Value between 0 and 10 inclusive. 10 being highest quality. + this.bufferLength = config['bufferLength'] || 4096; + this.resampleQuality = config['resampleQuality'] || 3; // Value between 0 and 10 inclusive. 10 being highest quality. + this.bitRate = config['bitRate']; + + this.pageIndex = 0; + this.granulePosition = 0; + this.segmentData = new Uint8Array( 65025 ); + this.segmentDataIndex = 0; + this.segmentTable = new Uint8Array( 255 ); + this.segmentTableIndex = 0; + this.buffersInPage = 0; + this.serial = Math.floor( Math.random() * Math.pow(2,32) ); + + this.initChecksumTable(); + this.initCodec(); + this.initResampler(); + this.generateIdPage(); + this.generateCommentPage(); + + if ( this.numberOfChannels === 1 ) { + this.interleave = function( buffers ) { return buffers[0]; }; + } + else { + this.interleavedBuffers = new Float32Array( this.bufferLength * this.numberOfChannels ); + } + }; + + OggOpusEncoder._opus_encoder_create = _opus_encoder_create; + OggOpusEncoder._opus_encoder_ctl = _opus_encoder_ctl; + OggOpusEncoder._speex_resampler_process_interleaved_float = _speex_resampler_process_interleaved_float; + OggOpusEncoder._speex_resampler_init = _speex_resampler_init; + OggOpusEncoder._opus_encode_float = _opus_encode_float; + OggOpusEncoder._free = _free; + OggOpusEncoder._malloc = _malloc; + + OggOpusEncoder.prototype.encode = function( buffers ) { + var samples = this.interleave( buffers ); + var sampleIndex = 0; + + while ( sampleIndex < samples.length ) { + + var lengthToCopy = Math.min( this.resampleBufferLength - this.resampleBufferIndex, samples.length - sampleIndex ); + this.resampleBuffer.set( samples.subarray( sampleIndex, sampleIndex+lengthToCopy ), this.resampleBufferIndex ); + sampleIndex += lengthToCopy; + this.resampleBufferIndex += lengthToCopy; + + if ( this.resampleBufferIndex === this.resampleBufferLength ) { + OggOpusEncoder._speex_resampler_process_interleaved_float( this.resampler, this.resampleBufferPointer, this.resampleSamplesPerChannelPointer, this.encoderBufferPointer, this.encoderSamplesPerChannelPointer ); + var packetLength = OggOpusEncoder._opus_encode_float( this.encoder, this.encoderBufferPointer, this.encoderSamplesPerChannel, this.encoderOutputPointer, this.encoderOutputMaxLength ); + this.segmentPacket( packetLength ); + this.resampleBufferIndex = 0; + } + } - if ( this.resampleBufferIndex === this.resampleBufferLength ) { - _speex_resampler_process_interleaved_float( this.resampler, this.resampleBufferPointer, this.resampleSamplesPerChannelPointer, this.encoderBufferPointer, this.encoderSamplesPerChannelPointer ); - var packetLength = _opus_encode_float( this.encoder, this.encoderBufferPointer, this.encoderSamplesPerChannel, this.encoderOutputPointer, this.encoderOutputMaxLength ); - this.segmentPacket( packetLength ); - this.resampleBufferIndex = 0; + this.buffersInPage++; + if ( this.buffersInPage >= this.maxBuffersPerPage ) { + this.generatePage(); } - } + }; - this.buffersInPage++; - if ( this.buffersInPage >= this.maxBuffersPerPage ) { + OggOpusEncoder.prototype.encodeFinalFrame = function() { + var finalFrameBuffers = []; + for ( var i = 0; i < this.numberOfChannels; ++i ) { + finalFrameBuffers.push( new Float32Array( this.bufferLength - (this.resampleBufferIndex / this.numberOfChannels) )); + } + this.encode( finalFrameBuffers ); + this.headerType += 4; this.generatePage(); - } -}; - -OggOpusEncoder.prototype.encodeFinalFrame = function() { - var finalFrameBuffers = []; - for ( var i = 0; i < this.numberOfChannels; ++i ) { - finalFrameBuffers.push( new Float32Array( this.bufferLength - (this.resampleBufferIndex / this.numberOfChannels) )); - } - this.encode( finalFrameBuffers ); - this.headerType += 4; - this.generatePage(); - self['postMessage'](null); - self['close'](); -}; - -OggOpusEncoder.prototype.getChecksum = function( data ){ - var checksum = 0; - for ( var i = 0; i < data.length; i++ ) { - checksum = (checksum << 8) ^ this.checksumTable[ ((checksum>>>24) & 0xff) ^ data[i] ]; - } - return checksum >>> 0; -}; - -OggOpusEncoder.prototype.generateCommentPage = function(){ - var segmentDataView = new DataView( this.segmentData.buffer ); - segmentDataView.setUint32( 0, 1937076303, true ) // Magic Signature 'Opus' - segmentDataView.setUint32( 4, 1936154964, true ) // Magic Signature 'Tags' - segmentDataView.setUint32( 8, 8, true ); // Vendor Length - segmentDataView.setUint32( 12, 1868784978, true ); // Vendor name 'Reco' - segmentDataView.setUint32( 16, 1919247474, true ); // Vendor name 'rder' - segmentDataView.setUint32( 20, 0, true ); // User Comment List Length - this.segmentTableIndex = 1; - this.segmentDataIndex = this.segmentTable[0] = 24; - this.headerType = 0; - this.generatePage(); -}; - -OggOpusEncoder.prototype.generateIdPage = function(){ - var segmentDataView = new DataView( this.segmentData.buffer ); - segmentDataView.setUint32( 0, 1937076303, true ) // Magic Signature 'Opus' - segmentDataView.setUint32( 4, 1684104520, true ) // Magic Signature 'Head' - segmentDataView.setUint8( 8, 1, true ); // Version - segmentDataView.setUint8( 9, this.numberOfChannels, true ); // Channel count - segmentDataView.setUint16( 10, 3840, true ); // pre-skip (80ms) - segmentDataView.setUint32( 12, this.originalSampleRate, true ); // original sample rate - segmentDataView.setUint16( 16, 0, true ); // output gain - segmentDataView.setUint8( 18, 0, true ); // channel map 0 = mono or stereo - this.segmentTableIndex = 1; - this.segmentDataIndex = this.segmentTable[0] = 19; - this.headerType = 2; - this.generatePage(); -}; - -OggOpusEncoder.prototype.generatePage = function(){ - var granulePosition = ( this.lastPositiveGranulePosition === this.granulePosition) ? -1 : this.granulePosition; - var pageBuffer = new ArrayBuffer( 27 + this.segmentTableIndex + this.segmentDataIndex ); - var pageBufferView = new DataView( pageBuffer ); - var page = new Uint8Array( pageBuffer ); - - pageBufferView.setUint32( 0, 1399285583, true); // Capture Pattern starts all page headers 'OggS' - pageBufferView.setUint8( 4, 0, true ); // Version - pageBufferView.setUint8( 5, this.headerType, true ); // 1 = continuation, 2 = beginning of stream, 4 = end of stream - - // Number of samples upto and including this page at 48000Hz, into 64 bits - pageBufferView.setUint32( 6, granulePosition, true ); - if ( granulePosition > 4294967296 || granulePosition < 0 ) { - pageBufferView.setUint32( 10, Math.floor( granulePosition/4294967296 ), true ); - } + global['postMessage'](null); + global['close'](); + }; + + OggOpusEncoder.prototype.getChecksum = function( data ){ + var checksum = 0; + for ( var i = 0; i < data.length; i++ ) { + checksum = (checksum << 8) ^ this.checksumTable[ ((checksum>>>24) & 0xff) ^ data[i] ]; + } + return checksum >>> 0; + }; + + OggOpusEncoder.prototype.generateCommentPage = function(){ + var segmentDataView = new DataView( this.segmentData.buffer ); + segmentDataView.setUint32( 0, 1937076303, true ) // Magic Signature 'Opus' + segmentDataView.setUint32( 4, 1936154964, true ) // Magic Signature 'Tags' + segmentDataView.setUint32( 8, 8, true ); // Vendor Length + segmentDataView.setUint32( 12, 1868784978, true ); // Vendor name 'Reco' + segmentDataView.setUint32( 16, 1919247474, true ); // Vendor name 'rder' + segmentDataView.setUint32( 20, 0, true ); // User Comment List Length + this.segmentTableIndex = 1; + this.segmentDataIndex = this.segmentTable[0] = 24; + this.headerType = 0; + this.generatePage(); + }; + + OggOpusEncoder.prototype.generateIdPage = function(){ + var segmentDataView = new DataView( this.segmentData.buffer ); + segmentDataView.setUint32( 0, 1937076303, true ) // Magic Signature 'Opus' + segmentDataView.setUint32( 4, 1684104520, true ) // Magic Signature 'Head' + segmentDataView.setUint8( 8, 1, true ); // Version + segmentDataView.setUint8( 9, this.numberOfChannels, true ); // Channel count + segmentDataView.setUint16( 10, 3840, true ); // pre-skip (80ms) + segmentDataView.setUint32( 12, this.originalSampleRate, true ); // original sample rate + segmentDataView.setUint16( 16, 0, true ); // output gain + segmentDataView.setUint8( 18, 0, true ); // channel map 0 = mono or stereo + this.segmentTableIndex = 1; + this.segmentDataIndex = this.segmentTable[0] = 19; + this.headerType = 2; + this.generatePage(); + }; + + OggOpusEncoder.prototype.generatePage = function(){ + var granulePosition = ( this.lastPositiveGranulePosition === this.granulePosition) ? -1 : this.granulePosition; + var pageBuffer = new ArrayBuffer( 27 + this.segmentTableIndex + this.segmentDataIndex ); + var pageBufferView = new DataView( pageBuffer ); + var page = new Uint8Array( pageBuffer ); + + pageBufferView.setUint32( 0, 1399285583, true); // Capture Pattern starts all page headers 'OggS' + pageBufferView.setUint8( 4, 0, true ); // Version + pageBufferView.setUint8( 5, this.headerType, true ); // 1 = continuation, 2 = beginning of stream, 4 = end of stream + + // Number of samples upto and including this page at 48000Hz, into 64 bits + pageBufferView.setUint32( 6, granulePosition, true ); + if ( granulePosition > 4294967296 || granulePosition < 0 ) { + pageBufferView.setUint32( 10, Math.floor( granulePosition/4294967296 ), true ); + } - pageBufferView.setUint32( 14, this.serial, true ); // Bitstream serial number - pageBufferView.setUint32( 18, this.pageIndex++, true ); // Page sequence number - pageBufferView.setUint8( 26, this.segmentTableIndex, true ); // Number of segments in page. - page.set( this.segmentTable.subarray(0, this.segmentTableIndex), 27 ); // Segment Table - page.set( this.segmentData.subarray(0, this.segmentDataIndex), 27 + this.segmentTableIndex ); // Segment Data - pageBufferView.setUint32( 22, this.getChecksum( page ), true ); // Checksum - - self['postMessage']( page, [page.buffer] ); - this.segmentTableIndex = 0; - this.segmentDataIndex = 0; - this.buffersInPage = 0; - if ( granulePosition > 0 ) { - this.lastPositiveGranulePosition = granulePosition; - } -}; - -OggOpusEncoder.prototype.initChecksumTable = function(){ - this.checksumTable = []; - for ( var i = 0; i < 256; i++ ) { - var r = i << 24; - for ( var j = 0; j < 8; j++ ) { - r = ((r & 0x80000000) != 0) ? ((r << 1) ^ 0x04c11db7) : (r << 1); + pageBufferView.setUint32( 14, this.serial, true ); // Bitstream serial number + pageBufferView.setUint32( 18, this.pageIndex++, true ); // Page sequence number + pageBufferView.setUint8( 26, this.segmentTableIndex, true ); // Number of segments in page. + page.set( this.segmentTable.subarray(0, this.segmentTableIndex), 27 ); // Segment Table + page.set( this.segmentData.subarray(0, this.segmentDataIndex), 27 + this.segmentTableIndex ); // Segment Data + pageBufferView.setUint32( 22, this.getChecksum( page ), true ); // Checksum + + global['postMessage']( page, [page.buffer] ); + this.segmentTableIndex = 0; + this.segmentDataIndex = 0; + this.buffersInPage = 0; + if ( granulePosition > 0 ) { + this.lastPositiveGranulePosition = granulePosition; + } + }; + + OggOpusEncoder.prototype.initChecksumTable = function(){ + this.checksumTable = []; + for ( var i = 0; i < 256; i++ ) { + var r = i << 24; + for ( var j = 0; j < 8; j++ ) { + r = ((r & 0x80000000) != 0) ? ((r << 1) ^ 0x04c11db7) : (r << 1); + } + this.checksumTable[i] = (r & 0xffffffff); + } + }; + + OggOpusEncoder.prototype.initCodec = function() { + var errLocation = OggOpusEncoder._malloc( 4 ); + this.encoder = OggOpusEncoder._opus_encoder_create( this.encoderSampleRate, this.numberOfChannels, this.encoderApplication, errLocation ); + OggOpusEncoder._free( errLocation ); + + if ( this.bitRate ) { + var bitRateLocation = OggOpusEncoder._malloc( 4 ); + HEAP32[ bitRateLocation >> 2 ] = this.bitRate; + OggOpusEncoder._opus_encoder_ctl( this.encoder, 4002, bitRateLocation ); + OggOpusEncoder._free( bitRateLocation ); } - this.checksumTable[i] = (r & 0xffffffff); - } -}; - -OggOpusEncoder.prototype.initCodec = function() { - var errLocation = _malloc( 4 ); - this.encoder = _opus_encoder_create( this.encoderSampleRate, this.numberOfChannels, this.encoderApplication, errLocation ); - _free( errLocation ); - - if ( this.bitRate ) { - var bitRateLocation = _malloc( 4 ); - HEAP32[ bitRateLocation >> 2 ] = this.bitRate; - _opus_encoder_ctl( this.encoder, 4002, bitRateLocation ); - _free( bitRateLocation ); - } - if ( this.encoderComplexity ) { - var encoderComplexityLocation = _malloc( 4 ); - _opus_encoder_ctl( this.encoder, 4010, encoderComplexityLocation ); - _free( encoderComplexityLocation ); - } + if ( this.encoderComplexity ) { + var encoderComplexityLocation = OggOpusEncoder._malloc( 4 ); + HEAP32[ encoderComplexityLocation >> 2 ] = this.encoderComplexity; + OggOpusEncoder._opus_encoder_ctl( this.encoder, 4010, encoderComplexityLocation ); + OggOpusEncoder._free( encoderComplexityLocation ); + } - this.encoderSamplesPerChannel = this.encoderSampleRate * this.encoderFrameSize / 1000; - this.encoderSamplesPerChannelPointer = _malloc( 4 ); - HEAP32[ this.encoderSamplesPerChannelPointer >> 2 ] = this.encoderSamplesPerChannel; - - this.encoderBufferLength = this.encoderSamplesPerChannel * this.numberOfChannels; - this.encoderBufferPointer = _malloc( this.encoderBufferLength * 4 ); // 4 bytes per sample - this.encoderBuffer = HEAPF32.subarray( this.encoderBufferPointer >> 2, (this.encoderBufferPointer >> 2) + this.encoderBufferLength ); - - this.encoderOutputMaxLength = 4000; - this.encoderOutputPointer = _malloc( this.encoderOutputMaxLength ); - this.encoderOutputBuffer = HEAPU8.subarray( this.encoderOutputPointer, this.encoderOutputPointer + this.encoderOutputMaxLength ); -}; - -OggOpusEncoder.prototype.initResampler = function() { - var errLocation = _malloc( 4 ); - this.resampler = _speex_resampler_init( this.numberOfChannels, this.originalSampleRate, this.encoderSampleRate, this.resampleQuality, errLocation ); - _free( errLocation ); - - this.resampleBufferIndex = 0; - this.resampleSamplesPerChannel = this.originalSampleRate * this.encoderFrameSize / 1000; - this.resampleSamplesPerChannelPointer = _malloc( 4 ); - HEAP32[ this.resampleSamplesPerChannelPointer >> 2 ] = this.resampleSamplesPerChannel; - - this.resampleBufferLength = this.resampleSamplesPerChannel * this.numberOfChannels; - this.resampleBufferPointer = _malloc( this.resampleBufferLength * 4 ); // 4 bytes per sample - this.resampleBuffer = HEAPF32.subarray( this.resampleBufferPointer >> 2, (this.resampleBufferPointer >> 2) + this.resampleBufferLength ); -}; - -OggOpusEncoder.prototype.interleave = function( buffers ) { - for ( var i = 0; i < this.bufferLength; i++ ) { - for ( var channel = 0; channel < this.numberOfChannels; channel++ ) { - this.interleavedBuffers[ i * this.numberOfChannels + channel ] = buffers[ channel ][ i ]; + this.encoderSamplesPerChannel = this.encoderSampleRate * this.encoderFrameSize / 1000; + this.encoderSamplesPerChannelPointer = OggOpusEncoder._malloc( 4 ); + HEAP32[ this.encoderSamplesPerChannelPointer >> 2 ] = this.encoderSamplesPerChannel; + + this.encoderBufferLength = this.encoderSamplesPerChannel * this.numberOfChannels; + this.encoderBufferPointer = OggOpusEncoder._malloc( this.encoderBufferLength * 4 ); // 4 bytes per sample + this.encoderBuffer = HEAPF32.subarray( this.encoderBufferPointer >> 2, (this.encoderBufferPointer >> 2) + this.encoderBufferLength ); + + this.encoderOutputMaxLength = 4000; + this.encoderOutputPointer = OggOpusEncoder._malloc( this.encoderOutputMaxLength ); + this.encoderOutputBuffer = HEAPU8.subarray( this.encoderOutputPointer, this.encoderOutputPointer + this.encoderOutputMaxLength ); + }; + + OggOpusEncoder.prototype.initResampler = function() { + var errLocation = OggOpusEncoder._malloc( 4 ); + this.resampler = OggOpusEncoder._speex_resampler_init( this.numberOfChannels, this.originalSampleRate, this.encoderSampleRate, this.resampleQuality, errLocation ); + OggOpusEncoder._free( errLocation ); + + this.resampleBufferIndex = 0; + this.resampleSamplesPerChannel = this.originalSampleRate * this.encoderFrameSize / 1000; + this.resampleSamplesPerChannelPointer = OggOpusEncoder._malloc( 4 ); + HEAP32[ this.resampleSamplesPerChannelPointer >> 2 ] = this.resampleSamplesPerChannel; + + this.resampleBufferLength = this.resampleSamplesPerChannel * this.numberOfChannels; + this.resampleBufferPointer = OggOpusEncoder._malloc( this.resampleBufferLength * 4 ); // 4 bytes per sample + this.resampleBuffer = HEAPF32.subarray( this.resampleBufferPointer >> 2, (this.resampleBufferPointer >> 2) + this.resampleBufferLength ); + }; + + OggOpusEncoder.prototype.interleave = function( buffers ) { + for ( var i = 0; i < this.bufferLength; i++ ) { + for ( var channel = 0; channel < this.numberOfChannels; channel++ ) { + this.interleavedBuffers[ i * this.numberOfChannels + channel ] = buffers[ channel ][ i ]; + } } - } - return this.interleavedBuffers; -}; + return this.interleavedBuffers; + }; + + OggOpusEncoder.prototype.segmentPacket = function( packetLength ) { + var packetIndex = 0; -OggOpusEncoder.prototype.segmentPacket = function( packetLength ) { - var packetIndex = 0; + while ( packetLength >= 0 ) { + + if ( this.segmentTableIndex === 255 ) { + this.generatePage(); + this.headerType = 1; + } - while ( packetLength >= 0 ) { + var segmentLength = Math.min( packetLength, 255 ); + this.segmentTable[ this.segmentTableIndex++ ] = segmentLength; + this.segmentData.set( this.encoderOutputBuffer.subarray( packetIndex, packetIndex + segmentLength ), this.segmentDataIndex ); + this.segmentDataIndex += segmentLength; + packetIndex += segmentLength; + packetLength -= 255; + } + this.granulePosition += ( 48 * this.encoderFrameSize ); if ( this.segmentTableIndex === 255 ) { this.generatePage(); - this.headerType = 1; + this.headerType = 0; } + }; - var segmentLength = Math.min( packetLength, 255 ); - this.segmentTable[ this.segmentTableIndex++ ] = segmentLength; - this.segmentData.set( this.encoderOutputBuffer.subarray( packetIndex, packetIndex + segmentLength ), this.segmentDataIndex ); - this.segmentDataIndex += segmentLength; - packetIndex += segmentLength; - packetLength -= 255; - } + // Exports for unit testing + global.OggOpusEncoder = OggOpusEncoder; - this.granulePosition += ( 48 * this.encoderFrameSize ); - if ( this.segmentTableIndex === 255 ) { - this.generatePage(); - this.headerType = 0; + if ( typeof module == 'object' && module.exports ) { + module.exports = OggOpusEncoder; } -}; + +})(root); diff --git a/test/encoderWorker.js b/test/encoderWorker.js index a1556a76..829677cb 100644 --- a/test/encoderWorker.js +++ b/test/encoderWorker.js @@ -1,7 +1,6 @@ var chai = require('chai'); var sinon = require('sinon'); var sinonChai = require("sinon-chai"); -var requireUncached = require('require-uncached'); chai.use(sinonChai); var should = chai.should(); @@ -10,4 +9,67 @@ var expect = chai.expect; describe('encoderWorker', function() { + var OggOpusEncoder = require('../dist/encoderWorker.min'); + var sandbox = sinon.sandbox.create(); + var _opus_encoder_create_spy; + var _opus_encoder_ctl_spy; + var _speex_resampler_process_interleaved_float_spy; + var _speex_resampler_init_spy; + var _opus_encode_float_spy; + + beforeEach(function(){ + global.postMessage = sandbox.stub(); + global.close = sandbox.stub(); + _opus_encoder_create_spy = sandbox.spy(OggOpusEncoder, '_opus_encoder_create'); + _opus_encoder_ctl_spy = sandbox.spy(OggOpusEncoder, '_opus_encoder_ctl'); + _speex_resampler_process_interleaved_float_spy = sandbox.spy(OggOpusEncoder, '_speex_resampler_process_interleaved_float'); + _speex_resampler_init_spy = sandbox.spy(OggOpusEncoder, '_speex_resampler_init'); + _opus_encode_float_spy = sandbox.spy(OggOpusEncoder, '_opus_encode_float'); + }); + + afterEach(function () { + sandbox.restore(); + }); + + it('should initialize config', function () { + var encoder = new OggOpusEncoder({ + originalSampleRate: 44100 + }); + + expect(encoder).to.have.property('numberOfChannels', 1); + expect(encoder).to.have.property('encoderSampleRate', 48000); + expect(encoder).to.have.property('maxBuffersPerPage', 40); + expect(encoder).to.have.property('encoderApplication', 2049); + expect(encoder).to.have.property('encoderFrameSize', 20); + expect(encoder).to.have.property('bufferLength', 4096); + expect(encoder).to.have.property('resampleQuality', 3); + expect(encoder).to.have.property('originalSampleRate', 44100); + }); + + it('should initialize encoder', function () { + var encoder = new OggOpusEncoder({ + originalSampleRate: 44100 + }); + + expect(_opus_encoder_create_spy).to.have.been.calledOnce; + }); + + it('should configure bitRate', function () { + var encoder = new OggOpusEncoder({ + originalSampleRate: 44100, + bitRate: 16000 + }); + + expect(_opus_encoder_ctl_spy).to.have.been.calledWith(encoder.encoder, 4002, sinon.match.any); + }); + + it('should configure compexity', function () { + var encoder = new OggOpusEncoder({ + originalSampleRate: 44100, + encoderComplexity: 10 + }); + + expect(_opus_encoder_ctl_spy).to.have.been.calledWith(encoder.encoder, 4010, sinon.match.any); + }); + }); \ No newline at end of file