UNPKG

woolball-client

Version:

Client-side library for Woolball enabling secure browser resource sharing for distributed AI task processing

146 lines (145 loc) 5.64 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.audioConversion = void 0; let audioEncoder = null; let audioDecoder = null; async function audioConversion(data) { const { input, outputCodec = 'opus', outputBitrate = 128000, outputSampleRate = 48000, outputChannels = 2, ...options } = data; try { if (!('AudioEncoder' in globalThis) || !('AudioDecoder' in globalThis)) { throw new Error('WebCodecs API is not available in this browser.'); } const inputData = typeof input === 'string' ? JSON.parse(input) : input; const validCodecs = ['opus', 'aac', 'mp3']; if (!validCodecs.includes(outputCodec)) { throw new Error(`Invalid output codec. Supported codecs: ${validCodecs.join(', ')}`); } let inputBuffer; let inputCodec; let inputSampleRate; let inputChannels; if (inputData.base64) { const base64String = inputData.base64.split(',')[1] || inputData.base64; const binaryString = atob(base64String); const bytes = new Uint8Array(binaryString.length); for (let i = 0; i < binaryString.length; i++) { bytes[i] = binaryString.charCodeAt(i); } inputBuffer = bytes.buffer; inputCodec = inputData.codec || 'opus'; inputSampleRate = inputData.sampleRate || 48000; inputChannels = inputData.numberOfChannels || 2; } else if (inputData.buffer) { inputBuffer = inputData.buffer; inputCodec = inputData.codec || 'opus'; inputSampleRate = inputData.sampleRate || 48000; inputChannels = inputData.numberOfChannels || 2; } else { throw new Error('Invalid input format. Provide audio as base64 or ArrayBuffer.'); } const decoderConfig = { codec: inputCodec, sampleRate: inputSampleRate, numberOfChannels: inputChannels }; const encoderConfig = { codec: outputCodec, sampleRate: outputSampleRate, numberOfChannels: outputChannels, bitrate: outputBitrate }; const encodedChunks = []; if (!audioEncoder) { audioEncoder = new AudioEncoder({ output: (chunk, metadata) => { encodedChunks.push(chunk); }, error: (error) => { throw new Error(`Encoder error: ${error.message}`); } }); } audioEncoder.configure(encoderConfig); if (inputData.frames) { for (const frame of inputData.frames) { const audioData = new AudioData({ format: frame.format || 'f32', sampleRate: frame.sampleRate || inputSampleRate, numberOfChannels: frame.numberOfChannels || inputChannels, numberOfFrames: frame.numberOfFrames, timestamp: frame.timestamp, data: frame.data }); audioEncoder.encode(audioData); audioData.close(); } } else if (inputData.encodedChunks) { if (!audioDecoder) { audioDecoder = new AudioDecoder({ output: (frame) => { audioEncoder?.encode(frame); frame.close(); }, error: (error) => { throw new Error(`Decoder error: ${error.message}`); } }); } audioDecoder.configure(decoderConfig); for (const chunk of inputData.encodedChunks) { const encodedChunk = new EncodedAudioChunk({ type: chunk.type || 'key', timestamp: chunk.timestamp || 0, duration: chunk.duration, data: chunk.data }); audioDecoder.decode(encodedChunk); } await audioDecoder.flush(); } await audioEncoder.flush(); const chunks = encodedChunks.map(chunk => { const buffer = new ArrayBuffer(chunk.byteLength); chunk.copyTo(buffer); return buffer; }); let mimeType; switch (outputCodec) { case 'opus': mimeType = 'audio/webm; codecs=opus'; break; case 'aac': mimeType = 'audio/mp4; codecs=mp4a.40.2'; break; case 'mp3': mimeType = 'audio/mpeg'; break; default: mimeType = `audio/webm; codecs=${outputCodec}`; } const blob = new Blob(chunks, { type: mimeType }); const reader = new FileReader(); const base64Promise = new Promise((resolve, reject) => { reader.onload = () => resolve(reader.result); reader.onerror = reject; }); reader.readAsDataURL(blob); const base64 = await base64Promise; return { audio: base64, codec: outputCodec, sampleRate: outputSampleRate, numberOfChannels: outputChannels, originalCodec: inputCodec, originalSampleRate: inputSampleRate, originalNumberOfChannels: inputChannels }; } catch (error) { throw error; } } exports.audioConversion = audioConversion;