mirror of
https://github.com/MindWorkAI/AI-Studio.git
synced 2026-10-06 14:09:40 +00:00
Harden voice recording initialization (#698)
Build and Release / Read metadata (push) Has been cancelled
Build and Release / Build app (${{ matrix.dotnet_runtime }}) (-aarch64-apple-darwin, osx-arm64, macos-latest, aarch64-apple-darwin, dmg updater) (push) Has been cancelled
Build and Release / Build app (${{ matrix.dotnet_runtime }}) (-aarch64-pc-windows-msvc.exe, win-arm64, windows-latest, aarch64-pc-windows-msvc, nsis updater) (push) Has been cancelled
Build and Release / Build app (${{ matrix.dotnet_runtime }}) (-aarch64-unknown-linux-gnu, linux-arm64, ubuntu-22.04-arm, aarch64-unknown-linux-gnu, appimage deb updater) (push) Has been cancelled
Build and Release / Build app (${{ matrix.dotnet_runtime }}) (-x86_64-apple-darwin, osx-x64, macos-latest, x86_64-apple-darwin, dmg updater) (push) Has been cancelled
Build and Release / Build app (${{ matrix.dotnet_runtime }}) (-x86_64-pc-windows-msvc.exe, win-x64, windows-latest, x86_64-pc-windows-msvc, nsis updater) (push) Has been cancelled
Build and Release / Build app (${{ matrix.dotnet_runtime }}) (-x86_64-unknown-linux-gnu, linux-x64, ubuntu-22.04, x86_64-unknown-linux-gnu, appimage deb updater) (push) Has been cancelled
Build and Release / Prepare & create release (push) Has been cancelled
Build and Release / Publish release (push) Has been cancelled
Build and Release / Read metadata (push) Has been cancelled
Build and Release / Build app (${{ matrix.dotnet_runtime }}) (-aarch64-apple-darwin, osx-arm64, macos-latest, aarch64-apple-darwin, dmg updater) (push) Has been cancelled
Build and Release / Build app (${{ matrix.dotnet_runtime }}) (-aarch64-pc-windows-msvc.exe, win-arm64, windows-latest, aarch64-pc-windows-msvc, nsis updater) (push) Has been cancelled
Build and Release / Build app (${{ matrix.dotnet_runtime }}) (-aarch64-unknown-linux-gnu, linux-arm64, ubuntu-22.04-arm, aarch64-unknown-linux-gnu, appimage deb updater) (push) Has been cancelled
Build and Release / Build app (${{ matrix.dotnet_runtime }}) (-x86_64-apple-darwin, osx-x64, macos-latest, x86_64-apple-darwin, dmg updater) (push) Has been cancelled
Build and Release / Build app (${{ matrix.dotnet_runtime }}) (-x86_64-pc-windows-msvc.exe, win-x64, windows-latest, x86_64-pc-windows-msvc, nsis updater) (push) Has been cancelled
Build and Release / Build app (${{ matrix.dotnet_runtime }}) (-x86_64-unknown-linux-gnu, linux-x64, ubuntu-22.04, x86_64-unknown-linux-gnu, appimage deb updater) (push) Has been cancelled
Build and Release / Prepare & create release (push) Has been cancelled
Build and Release / Publish release (push) Has been cancelled
This commit is contained in:
1 parent
56da27372c
commit
dac0b74145
13 files changed
+242
-69
No files matched your search
@@ -21,59 +21,68 @@ const SOUND_EFFECT_PATHS = [
|
||||
'/sounds/transcription_done.ogg'
|
||||
];
|
||||
|
||||
function createSoundEffectsInitResult(success, failedPaths = [], errorMessage = null) {
|
||||
return {
|
||||
success: success,
|
||||
failedPaths: failedPaths,
|
||||
errorMessage: errorMessage
|
||||
};
|
||||
}
|
||||
|
||||
// Initialize the audio context with low-latency settings.
|
||||
// Should be called from a user interaction (click, keypress)
|
||||
// to satisfy browser autoplay policies:
|
||||
window.initSoundEffects = async function() {
|
||||
|
||||
if (soundEffectContext && soundEffectContext.state !== 'closed') {
|
||||
// Already initialized, just ensure it's running:
|
||||
if (soundEffectContext.state === 'suspended') {
|
||||
await soundEffectContext.resume();
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
// Create the context with the interactive latency hint for the lowest latency:
|
||||
soundEffectContext = new (window.AudioContext || window.webkitAudioContext)({
|
||||
latencyHint: 'interactive'
|
||||
});
|
||||
if (soundEffectContext && soundEffectContext.state !== 'closed') {
|
||||
// Already initialized, just ensure it's running:
|
||||
if (soundEffectContext.state === 'suspended') {
|
||||
await soundEffectContext.resume();
|
||||
}
|
||||
} else {
|
||||
// Create the context with the interactive latency hint for the lowest latency:
|
||||
soundEffectContext = new (window.AudioContext || window.webkitAudioContext)({
|
||||
latencyHint: 'interactive'
|
||||
});
|
||||
|
||||
// Resume immediately (needed for Safari/macOS):
|
||||
if (soundEffectContext.state === 'suspended') {
|
||||
await soundEffectContext.resume();
|
||||
// Resume immediately (needed for Safari/macOS):
|
||||
if (soundEffectContext.state === 'suspended') {
|
||||
await soundEffectContext.resume();
|
||||
}
|
||||
|
||||
// Reset the queue timing:
|
||||
nextAvailablePlayTime = 0;
|
||||
|
||||
//
|
||||
// Play a very short silent buffer to "warm up" the audio pipeline.
|
||||
// This helps prevent the first real sound from being cut off:
|
||||
//
|
||||
const silentBuffer = soundEffectContext.createBuffer(1, 1, soundEffectContext.sampleRate);
|
||||
const silentSource = soundEffectContext.createBufferSource();
|
||||
silentSource.buffer = silentBuffer;
|
||||
silentSource.connect(soundEffectContext.destination);
|
||||
silentSource.start(0);
|
||||
|
||||
console.log('Sound effects - AudioContext initialized with latency:', soundEffectContext.baseLatency);
|
||||
}
|
||||
|
||||
// Reset the queue timing:
|
||||
nextAvailablePlayTime = 0;
|
||||
|
||||
//
|
||||
// Play a very short silent buffer to "warm up" the audio pipeline.
|
||||
// This helps prevent the first real sound from being cut off:
|
||||
//
|
||||
const silentBuffer = soundEffectContext.createBuffer(1, 1, soundEffectContext.sampleRate);
|
||||
const silentSource = soundEffectContext.createBufferSource();
|
||||
silentSource.buffer = silentBuffer;
|
||||
silentSource.connect(soundEffectContext.destination);
|
||||
silentSource.start(0);
|
||||
|
||||
console.log('Sound effects - AudioContext initialized with latency:', soundEffectContext.baseLatency);
|
||||
|
||||
// Preload all sound effects in parallel:
|
||||
if (!soundEffectsPreloaded) {
|
||||
await window.preloadSoundEffects();
|
||||
return await window.preloadSoundEffects();
|
||||
}
|
||||
|
||||
return createSoundEffectsInitResult(true);
|
||||
} catch (error) {
|
||||
console.warn('Failed to initialize sound effects:', error);
|
||||
return createSoundEffectsInitResult(false, [], error?.message || String(error));
|
||||
}
|
||||
};
|
||||
|
||||
// Preload all sound effect files into the cache:
|
||||
window.preloadSoundEffects = async function() {
|
||||
if (soundEffectsPreloaded) {
|
||||
return;
|
||||
return createSoundEffectsInitResult(true);
|
||||
}
|
||||
|
||||
// Ensure that the context exists:
|
||||
@@ -84,10 +93,15 @@ window.preloadSoundEffects = async function() {
|
||||
}
|
||||
|
||||
console.log('Sound effects - preloading', SOUND_EFFECT_PATHS.length, 'sound files...');
|
||||
const failedPaths = [];
|
||||
|
||||
const preloadPromises = SOUND_EFFECT_PATHS.map(async (soundPath) => {
|
||||
try {
|
||||
const response = await fetch(soundPath);
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP ${response.status}`);
|
||||
}
|
||||
|
||||
const arrayBuffer = await response.arrayBuffer();
|
||||
const audioBuffer = await soundEffectContext.decodeAudioData(arrayBuffer);
|
||||
soundEffectCache.set(soundPath, audioBuffer);
|
||||
@@ -95,12 +109,20 @@ window.preloadSoundEffects = async function() {
|
||||
console.log('Sound effects - preloaded:', soundPath, 'duration:', audioBuffer.duration.toFixed(2), 's');
|
||||
} catch (error) {
|
||||
console.warn('Sound effects - failed to preload:', soundPath, error);
|
||||
failedPaths.push(soundPath);
|
||||
}
|
||||
});
|
||||
|
||||
await Promise.all(preloadPromises);
|
||||
soundEffectsPreloaded = true;
|
||||
console.log('Sound effects - all files preloaded');
|
||||
soundEffectsPreloaded = failedPaths.length === 0;
|
||||
|
||||
if (soundEffectsPreloaded) {
|
||||
console.log('Sound effects - all files preloaded');
|
||||
return createSoundEffectsInitResult(true);
|
||||
}
|
||||
|
||||
console.warn('Sound effects - preload finished with failures:', failedPaths);
|
||||
return createSoundEffectsInitResult(false, failedPaths, 'One or more sound effects could not be loaded.');
|
||||
};
|
||||
|
||||
window.playSound = async function(soundPath) {
|
||||
|
||||
@@ -16,4 +16,5 @@
|
||||
- Improved the validation of additional API parameters in the advanced provider settings to help catch formatting mistakes earlier.
|
||||
- Improved the app startup resilience by allowing AI Studio to continue without Qdrant if it fails to initialize.
|
||||
- Fixed an issue where assistants hidden via configuration plugins still appear in "Send to ..." menus. Thanks, Gunnar, for reporting this issue.
|
||||
- Fixed an issue with voice recording where AI Studio could log errors and keep the feature available even though required parts failed to initialize. Voice recording is now disabled automatically for the current session in that case.
|
||||
- Fixed an issue where the app could turn white or appear invisible in certain chats after HTML-like content was shown. Thanks, Inga, for reporting this issue and providing some context on how to reproduce it.
|
||||
Reference in new issue
Block a user