@@ -57,6 +57,51 @@ class AudioManager {
5757 this . cachedMicDeviceId = null ;
5858 this . persistentAudioContext = null ;
5959 this . workletModuleLoaded = false ;
60+ this . workletBlobUrl = null ;
61+ }
62+
63+ getWorkletBlobUrl ( ) {
64+ if ( this . workletBlobUrl ) return this . workletBlobUrl ;
65+ const code = `
66+ const BUFFER_SIZE = 800;
67+ class PCMStreamingProcessor extends AudioWorkletProcessor {
68+ constructor() {
69+ super();
70+ this._buffer = new Int16Array(BUFFER_SIZE);
71+ this._offset = 0;
72+ this._stopped = false;
73+ this.port.onmessage = (event) => {
74+ if (event.data === "stop") {
75+ if (this._offset > 0) {
76+ const partial = this._buffer.slice(0, this._offset);
77+ this.port.postMessage(partial.buffer, [partial.buffer]);
78+ this._buffer = new Int16Array(BUFFER_SIZE);
79+ this._offset = 0;
80+ }
81+ this._stopped = true;
82+ }
83+ };
84+ }
85+ process(inputs) {
86+ if (this._stopped) return false;
87+ const input = inputs[0]?.[0];
88+ if (!input) return true;
89+ for (let i = 0; i < input.length; i++) {
90+ const s = Math.max(-1, Math.min(1, input[i]));
91+ this._buffer[this._offset++] = s < 0 ? s * 0x8000 : s * 0x7fff;
92+ if (this._offset >= BUFFER_SIZE) {
93+ this.port.postMessage(this._buffer.buffer, [this._buffer.buffer]);
94+ this._buffer = new Int16Array(BUFFER_SIZE);
95+ this._offset = 0;
96+ }
97+ }
98+ return true;
99+ }
100+ }
101+ registerProcessor("pcm-streaming-processor", PCMStreamingProcessor);
102+ ` ;
103+ this . workletBlobUrl = URL . createObjectURL ( new Blob ( [ code ] , { type : "application/javascript" } ) ) ;
104+ return this . workletBlobUrl ;
60105 }
61106
62107 getCustomDictionaryPrompt ( ) {
@@ -1643,8 +1688,7 @@ class AudioManager {
16431688 try {
16441689 const audioContext = await this . getOrCreateAudioContext ( ) ;
16451690 if ( ! this . workletModuleLoaded ) {
1646- const workletUrl = new URL ( "./pcm-streaming-processor.js" , document . baseURI ) . href ;
1647- await audioContext . audioWorklet . addModule ( workletUrl ) ;
1691+ await audioContext . audioWorklet . addModule ( this . getWorkletBlobUrl ( ) ) ;
16481692 this . workletModuleLoaded = true ;
16491693 logger . debug ( "AudioWorklet module pre-loaded during warmup" , { } , "streaming" ) ;
16501694 }
@@ -1770,8 +1814,7 @@ class AudioManager {
17701814 this . streamingStream = stream ;
17711815
17721816 if ( ! this . workletModuleLoaded ) {
1773- const workletUrl = new URL ( "./pcm-streaming-processor.js" , document . baseURI ) . href ;
1774- await audioContext . audioWorklet . addModule ( workletUrl ) ;
1817+ await audioContext . audioWorklet . addModule ( this . getWorkletBlobUrl ( ) ) ;
17751818 this . workletModuleLoaded = true ;
17761819 }
17771820
@@ -2100,6 +2143,10 @@ class AudioManager {
21002143 this . persistentAudioContext . close ( ) . catch ( ( ) => { } ) ;
21012144 this . persistentAudioContext = null ;
21022145 this . workletModuleLoaded = false ;
2146+ if ( this . workletBlobUrl ) {
2147+ URL . revokeObjectURL ( this . workletBlobUrl ) ;
2148+ this . workletBlobUrl = null ;
2149+ }
21032150 }
21042151 try {
21052152 window . electronAPI ?. assemblyAiStreamingStop ?. ( ) ;
0 commit comments