PageSourceSearch

https://otaku.bg/applications/voicemsg/interface/recorder.min.js?v=af80b623d61790627979

js otaku.bg collected 2026-09-29 20:07:52 UTC 7,183 bytes, 178 lines download raw bytes

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.