From 7cc349d554602c563111f0b1d938e8ec5a3dcd21 Mon Sep 17 00:00:00 2001 From: "google-labs-jules[bot]" <161369871+google-labs-jules[bot]@users.noreply.github.com> Date: Sat, 24 May 2025 22:46:59 +0000 Subject: [PATCH] Fix: Resolve AudioContext initialization error and sync volume control This commit addresses the "Cannot read properties of null (reading 'value')" error related to AudioContext initialization and resolves inconsistencies in volume control. Key changes: 1. **Error Handling in `useTTS.js`**: * Added defensive checks in `generateSpeech` and `generateMultiSpeech` after `initAudio()` to ensure `audioContext` and `audioContext.value` are valid. This prevents errors if `AudioContext` fails to initialize and provides clearer error feedback to you. * Added logging to aid in diagnosing `AudioContext` issues. 2. **Volume Control Synchronization**: * Refactored `useTTS.js` to use the `setVolumeAndApply` action from `ttsStore.js`. This ensures that changes to the volume are reflected in both the Pinia store and the Web Audio API's `gainNode`. * The `reset()` function in `useTTS.js` now correctly uses the synchronized volume setting. * This fixes the issue where UI volume could be out of sync with the actual audio volume. 3. **Initial Volume in `useAudioContext.js`**: * Modified `initAudio()` in `useAudioContext.js` to set the initial gain value of the `gainNode` based on the volume stored in `ttsStore.js`. This ensures volume consistency from the moment audio is initialized. These changes improve the robustness of audio feature initialization and provide a more consistent state management for volume control. --- frontend/src/composables/useAudioContext.js | 7 +++++ frontend/src/composables/useTTS.js | 33 +++++++++++++++++---- 2 files changed, 35 insertions(+), 5 deletions(-) diff --git a/frontend/src/composables/useAudioContext.js b/frontend/src/composables/useAudioContext.js index 401d0f6..9278200 100644 --- a/frontend/src/composables/useAudioContext.js +++ b/frontend/src/composables/useAudioContext.js @@ -1,4 +1,5 @@ import { ref } from 'vue' +import { useTTSStore } from '../stores/ttsStore'; export function useAudioContext() { const audioContext = ref(null) @@ -9,6 +10,12 @@ export function useAudioContext() { audioContext.value = new (window.AudioContext || window.webkitAudioContext)() gainNode.value = audioContext.value.createGain() gainNode.value.connect(audioContext.value.destination) + + // Initialize gainNode's volume from the store's current volume + const ttsStore = useTTSStore(); + // The store volume is 0-100, gainNode.gain.value expects 0-1 + const initialVolume = ttsStore.volume / 100; + gainNode.value.gain.value = initialVolume; } } diff --git a/frontend/src/composables/useTTS.js b/frontend/src/composables/useTTS.js index 1ea4242..54f803b 100644 --- a/frontend/src/composables/useTTS.js +++ b/frontend/src/composables/useTTS.js @@ -17,7 +17,8 @@ export function useTTS() { unifiedBuffer, audioDuration, isDownloadComplete, - downloadProgress + downloadProgress, + volume: storeVolume, // Add this } = storeToRefs(ttsStore) const { updateUnifiedBuffer, @@ -26,12 +27,13 @@ export function useTTS() { setPlaybackProgress, setCurrentTime, setIsDownloadComplete, - setDownloadProgress + setDownloadProgress, + setVolumeAndApply, // Add this } = ttsStore const audioQueue = [] // For streaming chunks // Audio context - const { audioContext, gainNode, initAudio, setVolume, closeAudio } = useAudioContext() + const { audioContext, gainNode, initAudio, setVolume: applyVolumeToAudioContext, closeAudio } = useAudioContext() // Playback control const { @@ -71,6 +73,14 @@ export function useTTS() { currentAbortController = new AbortController() initAudio() + console.log('audioContext after initAudio():', audioContext); + console.log('audioContext.value after initAudio():', audioContext ? audioContext.value : 'audioContext is null'); + if (!audioContext || !audioContext.value) { + console.error('AudioContext is not initialized. audioContext or audioContext.value is null/undefined after initAudio().'); + progressMessage.value = 'Error: Audio context could not be initialized.'; + isGenerating.value = false; + return; + } if (audioContext.value && audioContext.value.state === 'suspended') { await audioContext.value.resume() } @@ -226,6 +236,12 @@ export function useTTS() { try { initAudio() + if (!audioContext || !audioContext.value) { + console.error('AudioContext is not initialized in generateMultiSpeech. audioContext or audioContext.value is null/undefined after initAudio().'); + progressMessage.value = 'Error: Audio context could not be initialized for multi-speech.'; + isGenerating.value = false; + return; + } if (audioContext.value && audioContext.value.state === 'suspended') { await audioContext.value.resume() } @@ -298,6 +314,12 @@ export function useTTS() { } } + function setSyncedVolume(newVolumeValue) { + // newVolumeValue is expected to be 0-100 from UI controls + // setVolumeAndApply from the store will handle calling applyVolumeToAudioContext + setVolumeAndApply(newVolumeValue, applyVolumeToAudioContext); + } + // When the user clicks play/pause we switch out of streaming mode. async function togglePlaybackHandler() { streamingMode = false @@ -339,7 +361,7 @@ export function useTTS() { closeAudio() } - setVolume(1) + setSyncedVolume(80) progressMessage.value = '' setIsPlaying(false) // Reset our chunk state @@ -386,7 +408,8 @@ export function useTTS() { togglePlayback: togglePlaybackHandler, reset, setTotalDuration, - setVolume, + setVolume: setSyncedVolume, + volume: storeVolume, // When the user seeks, we also set streamingMode to false seekToPosition: (pos) => { streamingMode = false;