Files
Anthony 0775104b69 feat: Initialize project with basic structure and dependencies
Sets up the foundational elements for the NewsCaster AI application. This includes:
- Initializing the project with Vite and React.
- Defining core types for articles and player state.
- Configuring build tools and TypeScript.
- Adding essential dependencies like React, Vite, and Google's Gemini API client.
- Providing initial README instructions for running locally.
- Setting up basic styling and structure in index.html.
- Defining available voices and playback constants.
- Implementing utility functions for audio handling.
2025-11-19 19:33:34 +08:00

59 lines
1.9 KiB
TypeScript

/**
* Converts a Base64 string (Raw PCM) to a Uint8Array.
*/
export const base64ToUint8Array = (base64: string): Uint8Array => {
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;
};
/**
* Wraps raw PCM data in a WAV container so it can be played by standard HTML5 Audio elements.
* This allows us to use `playbackRate` with automatic pitch preservation.
*/
export const createWavBlob = (pcmData: Uint8Array, sampleRate: number = 24000): Blob => {
const numChannels = 1;
const bitsPerSample = 16;
const byteRate = (sampleRate * numChannels * bitsPerSample) / 8;
const blockAlign = (numChannels * bitsPerSample) / 8;
const dataSize = pcmData.length;
const chunkSize = 36 + dataSize;
const buffer = new ArrayBuffer(44 + dataSize);
const view = new DataView(buffer);
// RIFF chunk descriptor
writeString(view, 0, 'RIFF');
view.setUint32(4, chunkSize, true);
writeString(view, 8, 'WAVE');
// fmt sub-chunk
writeString(view, 12, 'fmt ');
view.setUint32(16, 16, true); // Subchunk1Size (16 for PCM)
view.setUint16(20, 1, true); // AudioFormat (1 for PCM)
view.setUint16(22, numChannels, true); // NumChannels
view.setUint32(24, sampleRate, true); // SampleRate
view.setUint32(28, byteRate, true); // ByteRate
view.setUint16(32, blockAlign, true); // BlockAlign
view.setUint16(34, bitsPerSample, true); // BitsPerSample
// data sub-chunk
writeString(view, 36, 'data');
view.setUint32(40, dataSize, true);
// Write PCM data
const dataView = new Uint8Array(buffer, 44);
dataView.set(pcmData);
return new Blob([buffer], { type: 'audio/wav' });
};
const writeString = (view: DataView, offset: number, string: string) => {
for (let i = 0; i < string.length; i++) {
view.setUint8(offset + i, string.charCodeAt(i));
}
};