
ITSNOTAILABS
408 posts

ITSNOTAILABS
@ItsnotAILabs
Sovereign AI, Recursive Systems, AI Clouds. Agentic Systems, Hybrid Math-LLM, AI × Blockchain NeuroAI.MicroAI.WEB3. Backend & Doctrine Driven . @LoomMultiAI






/** * Audio helper for converting between Float32Array PCM and raw 16-bit integer PCM. * Useful for Gemini Live API and browser voice streaming. */ export function floatTo16BitPCM(input: Float32Array): ArrayBuffer { const buffer = new ArrayBuffer(input.length * 2); const view = new DataView(buffer); let offset = 0; for (let i = 0; i < input.length; i++, offset += 2) { let s = Math.max(-1, Math.min(1, input[i])); view.setInt16(offset, s < 0 ? s * 0x8000 : s * 0x7FFF, true); } return buffer; } export function base64ToArrayBuffer(base64: string): ArrayBuffer { const binaryString = window.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.buffer; } export function arrayBufferToBase64(buffer: ArrayBuffer): string { let binary = ''; const bytes = new Uint8Array(buffer); const len = bytes.byteLength; for (let i = 0; i < len; i++) { binary += String.fromCharCode(bytes[i]); } return window.btoa(binary); } /** * Playback PCM raw chunks (24kHz format from Gemini Live API) * Gapless scheduling to avoid audio glitches during streaming. */ export class PCMPlayer { private audioCtx: AudioContext | null = null; private nextStartTime: number = 0; constructor(private sampleRate: number = 24000) {} public init() { if (!this.audioCtx) { this.audioCtx = new (window.AudioContext || (window as any).webkitAudioContext)({ sampleRate: this.sampleRate, }); this.nextStartTime = this.audioCtx.currentTime; } if (this.audioCtx.state === 'suspended') { this.audioCtx.resume(); } } public playChunk(base64Data: string) { this.init(); if (!this.audioCtx) return; try { const arrayBuf = base64ToArrayBuffer(base64Data); const int16Array = new Int16Array(arrayBuf); const float32Array = new Float32Array(int16Array.length); // Convert 16-bit signed PCM to Float32 for (let i = 0; i < int16Array.length; i++) { float32Array[i] = int16Array[i] / 32768.0; } const audioBuffer = this.audioCtx.createBuffer(1, float32Array.length, this.sampleRate); audioBuffer.getChannelData(0).set(float32Array); const source = this.audioCtx.createBufferSource(); source.buffer = audioBuffer; source.connect(this.audioCtx.destination); // Gapless scheduling const currentTime = this.audioCtx.currentTime; if (this.nextStartTime < currentTime) { this.nextStartTime = currentTime + 0.05; // small safety buffer } source.start(this.nextStartTime); this.nextStartTime += audioBuffer.duration; } catch (err) { console.error('Failed to play raw PCM chunk:', err); } } public stop() { if (this.audioCtx) { this.nextStartTime = this.audioCtx.currentTime; } } public close() { if (this.audioCtx) { this.audioCtx.close(); this.audioCtx = null; } } } /** * Text-to-Speech browser fallback. * Ensures rich audio response even under iframe permission limits or offline/mock mode. */ export function speakTextFallback(text: string, langCode: string = 'en-US') { if (!('speechSynthesis' in window)) return; window.speechSynthesis.cancel(); const cleanText = text.replace(/[*#_`]/g, ''); // strip markdown const utterance = new SpeechSynthesisUtterance(cleanText); utterance.rate = 0.95; // slightly slower for clarity / language learners // Choose a good voice const voices = window.speechSynthesis.getVoices(); let selectedVoice: SpeechSynthesisVoice | null = null; if (langCode.toLowerCase().includes('ja')) { selectedVoice = voices.find(v => v . lang.startsWith('ja') || v . name . includes('Google 日本語') || v. name . includes('Kyoko') ) || null; } else { selectedVoice = voices.find(v => v . lang.startsWith('en') && (v. name .includes('Natural') || ncludes('Google') || h.includes('Samantha')) ) || null; } if (selectedVoice) { utterance.voice = selectedVoice; } window.speechSynthesis.speak(utterance); }










