Spaces:
Running
Running
/** | |
* Copyright 2024 Google LLC | |
* | |
* Licensed under the Apache License, Version 2.0 (the "License"); | |
* you may not use this file except in compliance with the License. | |
* You may obtain a copy of the License at | |
* | |
* http://www.apache.org/licenses/LICENSE-2.0 | |
* | |
* Unless required by applicable law or agreed to in writing, software | |
* distributed under the License is distributed on an "AS IS" BASIS, | |
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
* See the License for the specific language governing permissions and | |
* limitations under the License. | |
*/ | |
const SafariAudioRecordingWorklet = ` | |
class AudioProcessingWorklet extends AudioWorkletProcessor { | |
// Safari seems to work better with smaller buffer sizes | |
// and more frequent updates | |
buffer = new Int16Array(1024); | |
bufferWriteIndex = 0; | |
lastProcessTime = 0; | |
sampleRate = 0; | |
constructor(options) { | |
super(); | |
console.log('Safari AudioProcessingWorklet constructed with options:', options); | |
this.sampleRate = options.processorOptions?.sampleRate || sampleRate; | |
console.log('Using sample rate:', this.sampleRate); | |
} | |
process(inputs) { | |
// Log processing details periodically | |
const now = currentTime; | |
if (now - this.lastProcessTime > 1) { | |
console.log('Safari AudioProcessingWorklet processing:', { | |
inputChannels: inputs[0]?.length, | |
inputSamples: inputs[0]?.[0]?.length, | |
bufferWriteIndex: this.bufferWriteIndex, | |
time: now | |
}); | |
this.lastProcessTime = now; | |
} | |
if (!inputs[0]?.length) { | |
console.warn('No input channels available'); | |
return true; | |
} | |
const channel0 = inputs[0][0]; | |
if (!channel0?.length) { | |
console.warn('Empty input channel'); | |
return true; | |
} | |
this.processChunk(channel0); | |
return true; | |
} | |
sendAndClearBuffer() { | |
if (this.bufferWriteIndex > 0) { | |
this.port.postMessage({ | |
event: "chunk", | |
data: { | |
int16arrayBuffer: this.buffer.slice(0, this.bufferWriteIndex).buffer, | |
}, | |
}); | |
this.bufferWriteIndex = 0; | |
} | |
} | |
processChunk(float32Array) { | |
// Safari can sometimes send empty arrays or undefined | |
if (!float32Array?.length) { | |
return; | |
} | |
const l = float32Array.length; | |
for (let i = 0; i < l; i++) { | |
// Convert float32 -1 to 1 to int16 -32768 to 32767 | |
// Add some additional gain for Safari which tends to be quieter | |
const int16Value = Math.max(-32768, Math.min(32767, float32Array[i] * 32768 * 1.5)); | |
this.buffer[this.bufferWriteIndex++] = int16Value; | |
if (this.bufferWriteIndex >= this.buffer.length) { | |
this.sendAndClearBuffer(); | |
} | |
} | |
// Make sure to send any remaining data | |
if (this.bufferWriteIndex > 0) { | |
this.sendAndClearBuffer(); | |
} | |
} | |
} | |
`; | |
export default SafariAudioRecordingWorklet; |