1(function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.Recorder=f()}})(function(){ 2 "use strict"; 3 4// Recorder class using AudioWorkletNode (no ScriptProcessorNode) 5 function Recorder(source, cfg) { 6 var self = this; 7 this.config = { 8 numChannels: 2, 9 mimeType: 'audio/wav' 10 }; 11 this.recording = false; 12 this.callbacks = { 13 getBuffer: [], 14 exportWAV: [] 15 }; 16 Object.assign(this.config, cfg); 17 this.context = source.context; 18 this.audioBuffers = []; 19 this.node = null; 20 21 // Initialize AudioWorklet and node, inject processor from string 22 var processorCode = ` 23 class RecorderProcessor extends AudioWorkletProcessor { 24 constructor() { 25 super(); 26 } 27 process(inputs, outputs, parameters) { 28 const input = inputs[0]; 29 // Each input[channel] is a Float32Array 30 if (input && input.length > 0) { 31 // Send an array of channels (Float32Array for each channel) 32 this.port.postMessage(input.map(channel => Float32Array.from(channel))); 33 } 34 // Keep processor alive 35 return true; 36 } 37 } 38 registerProcessor('recorder-processor', RecorderProcessor); 39 `; 40 // Create blob and object URL for processor 41 var blob = new Blob([processorCode], {type: 'application/javascript'}); 42 var processorUrl = URL.createObjectURL(blob); 43 44 // Add module and create AudioWorkletNode synchronously 45 var _ready = this.context.audioWorklet.addModule(processorUrl).then(function(){ 46 self.node = new AudioWorkletNode(self.context, 'recorder-processor', { 47 numberOfInputs: 1, 48 numberOfOutputs: 1, 49 channelCount: self.config.numChannels 50 }); 51 // Receive audio data from processor 52 self.node.port.onmessage = function(event){ 53 if(self.recording){ 54 self.audioBuffers.push(event.data); 55 } 56 }; 57 // Connect the audio nodes 58 source.connect(self.node).connect(self.context.destination); 59 }); 60 61 // Small hack: queue all calls until ready 62 function ensureReady(fn){ 63 return function(){ 64 var args = arguments; 65 _ready.then(function(){fn.apply(self,args)}); 66 }; 67 } 68 this.record = ensureReady(function(){ 69 self.recording = true; 70 self.audioBuffers = []; 71 }); 72 this.stop = ensureReady(function(){ 73 self.recording = false; 74 }); 75 this.clear = ensureReady(function(){ 76 self.audioBuffers = []; 77 }); 78 this.getBuffer = ensureReady(function(cb){ 79 if (typeof cb !== 'function') throw new Error('Callback not set'); 80 var numChannels = self.config.numChannels; 81 var buffers = Array.from({length: numChannels}, () => []); 82 for (var i = 0; i < self.audioBuffers.length; i++) { 83 var frame = self.audioBuffers[i]; 84 for (var channel = 0; channel < numChannels; channel++) { 85 buffers[channel].push(frame[channel]); 86 } 87 } 88 var result = buffers.map(function(channelBuffers){ 89 var length = 0; 90 for(var i=0;i<channelBuffers.length;i++) length+=channelBuffers[i].length; 91 var res = new Float32Array(length); 92 var offset = 0; 93 for(var i=0;i<channelBuffers.length;i++){ 94 res.set(channelBuffers[i], offset); 95 offset+=channelBuffers[i].length; 96 } 97 return res; 98 }); 99 cb(result); 100 }); 101 this.exportWAV = ensureReady(function(cb, mimeType){ 102 mimeType = mimeType || self.config.mimeType; 103 if (typeof cb !== 'function') throw new Error('Callback not set'); 104 self.getBuffer(function(buffers){ 105 var wav = Recorder.encodeWAV(buffers, self.context.sampleRate, self.config.numChannels); 106 var audioBlob = new Blob([wav], { type: mimeType }); 107 cb(audioBlob); 108 }); 109 }); 110 } 111 112// Static helpers (unchanged) 113 Recorder.encodeWAV = function(buffers, sampleRate, numChannels){ 114 // Interleave channels if stereo 115 var interleaved; 116 if(numChannels === 2){ 117 interleaved = Recorder.interleave(buffers[0], buffers[1]); 118 }else{ 119 interleaved = buffers[0]; 120 } 121 var dataview = Recorder._encodeWAV(interleaved, sampleRate, numChannels); 122 return dataview; 123 }; 124 Recorder.interleave = function(inputL, inputR){ 125 var length = inputL.length + inputR.length; 126 var result = new Float32Array(length); 127 var index = 0, inputIndex = 0; 128 while(index < length){ 129 result[index++] = inputL[inputIndex]; 130 result[index++] = inputR[inputIndex]; 131 inputIndex++; 132 } 133 return result; 134 }; 135 Recorder._encodeWAV = function(samples, sampleRate, numChannels){ 136 var buffer = new ArrayBuffer(44 + samples.length * 2); 137 var view = new DataView(buffer); 138 139 Recorder._writeString(view, 0, 'RIFF'); 140 view.setUint32(4, 36 + samples.length * 2, true); 141 Recorder._writeString(view, 8, 'WAVE'); 142 Recorder._writeString(view, 12, 'fmt '); 143 view.setUint32(16, 16, true); 144 view.setUint16(20, 1, true); 145 view.setUint16(22, numChannels, true); 146 view.setUint32(24, sampleRate, true); 147 view.setUint32(28, sampleRate * numChannels * 2, true); 148 view.setUint16(32, numChannels * 2, true); 149 view.setUint16(34, 16, true); 150 Recorder._writeString(view, 36, 'data'); 151 view.setUint32(40, samples.length * 2, true); 152 Recorder._floatTo16BitPCM(view, 44, samples); 153 return view; 154 }; 155 Recorder._floatTo16BitPCM = function(output, offset, input){ 156 for(var i=0;i<input.length;i++,offset+=2){ 157 var s = Math.max(-1, Math.min(1, input[i])); 158 output.setInt16(offset, s<0?s*0x8000:s*0x7FFF, true); 159 } 160 }; 161 Recorder._writeString = function(view, offset, string){ 162 for(var i=0;i<string.length;i++){ 163 view.setUint8(offset+i, string.charCodeAt(i)); 164 } 165 }; 166 Recorder.forceDownload = function(blob, filename){ 167 var url = (window.URL || window.webkitURL).createObjectURL(blob); 168 var link = window.document.createElement('a'); 169 link.href = url; 170 link.download = filename || 'output.wav'; 171 var click = document.createEvent("Event"); 172 click.initEvent("click", true, true); 173 link.dispatchEvent(click); 174 }; 175 176// UMD export 177 return Recorder; 178});
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.