|
|
|
|
|
|
|
|
|
import {Blob} from '@google/genai'; |
|
|
|
function encode(bytes) { |
|
let binary = ''; |
|
const len = bytes.byteLength; |
|
for (let i = 0; i < len; i++) { |
|
binary += String.fromCharCode(bytes[i]); |
|
} |
|
return btoa(binary); |
|
} |
|
|
|
function decode(base64) { |
|
const binaryString = atob(base64); |
|
const len = binaryString.length; |
|
const bytes = new Uint8Array(len); |
|
for (let i = 0; i < len; i++) { |
|
bytes[i] = binaryString.charCodeAt(i); |
|
} |
|
return bytes; |
|
} |
|
|
|
function createBlob(data: Float32Array): Blob { |
|
const l = data.length; |
|
const int16 = new Int16Array(l); |
|
for (let i = 0; i < l; i++) { |
|
|
|
int16[i] = data[i] * 32768; |
|
} |
|
|
|
return { |
|
data: encode(new Uint8Array(int16.buffer)), |
|
mimeType: 'audio/pcm;rate=16000', |
|
}; |
|
} |
|
|
|
async function decodeAudioData( |
|
data: Uint8Array, |
|
ctx: AudioContext, |
|
sampleRate: number, |
|
numChannels: number, |
|
): Promise<AudioBuffer> { |
|
const buffer = ctx.createBuffer( |
|
numChannels, |
|
data.length / 2 / numChannels, |
|
sampleRate, |
|
); |
|
|
|
const dataInt16 = new Int16Array(data.buffer); |
|
const l = dataInt16.length; |
|
const dataFloat32 = new Float32Array(l); |
|
for (let i = 0; i < l; i++) { |
|
dataFloat32[i] = dataInt16[i] / 32768.0; |
|
} |
|
|
|
if (numChannels === 0) { |
|
buffer.copyToChannel(dataFloat32, 0); |
|
} else { |
|
for (let i = 0; i < numChannels; i++) { |
|
const channel = dataFloat32.filter( |
|
(_, index) => index % numChannels === i, |
|
); |
|
buffer.copyToChannel(channel, i); |
|
} |
|
} |
|
|
|
return buffer; |
|
} |
|
|
|
export {createBlob, decode, decodeAudioData, encode}; |
|
|