-
-
Notifications
You must be signed in to change notification settings - Fork 16
Description
Description
This issue documents two sound-related bugs discovered during sound test integration, along with important behavioural findings about the playSound() API that may need addressing.
Note: the tests that found this issue will be submitted in a PR together with the proposed fix(es).
Issue 1: Web Audio linearRampToValueAtTime Error
Problem
When running sound tests, the following error appeared in browser console:
[browser:pageerror] Error [TypeError]: Failed to execute 'linearRampToValueAtTime'
on 'AudioParam': The provided double value is non-finite.
at Object.playMidiNote (http://localhost:5173/api/sound.js:302:19)
Root Cause
The error was triggered when playNotes() received mismatched array lengths (e.g., 3 notes but only 2 durations):
flock.playNotes('box', {
notes: [60, 62, 64], // 3 notes
durations: [0.5, 0.5] // Only 2 durations!
});Bug chain:
durations[2]returnsundefinedNumber(undefined)converts toNaNplayTime + NaN - gap - NaN=NaN- Web Audio API's
linearRampToValueAtTime(0, NaN)throws error
Expected Behavior
The API should handle mismatched array lengths gracefully without throwing Web Audio API errors. Missing durations should either:
- Default to a reasonable value (e.g., 0.5 beats)
- Be validated and rejected with a clear error message
- Be documented as requiring matching array lengths
Issue 2: Sound Replacement Test Failures and loop Parameter Behavior
Problem
Tests attempting to verify sound replacement on a mesh were failing with:
AssertionError: expected undefined not to be undefined
Investigation Findings
The playSound() API behaves fundamentally differently based on the loop parameter:
With loop: true
- Promise resolves immediately after sound is created and attached to mesh
- Returns the Sound object
- Sound continues playing indefinitely until stopped
With loop: false
- Promise resolves only when the sound finishes playing
- When sound ends, it automatically removes itself from
mesh.metadata.currentSound(sound.js:104-106) - If you await the promise, by the time it resolves,
currentSoundis alreadyundefined
Sound Replacement Timing
When playSound() is called on a mesh that already has a sound:
- Synchronous deletion (immediate): Old sound deleted from
mesh.metadata.currentSound - Async creation (~10-50ms): New sound created via
CreateSoundAsync() - Async attachment (~10-50ms): New sound attached to
mesh.metadata.currentSound
This means there's a brief window where currentSound is undefined between deletion and attachment.
Diagnostic Test Results
Created tests/sound-replacement-diagnostic.test.js to investigate:
Test: Playing second sound (loop=false)
Before second playSound: currentSound = "test.mp3"
Immediately after playSound call: currentSound = undefined ← Old sound deleted
After 50ms (promise not awaited): currentSound = "test2.mp3" ← New sound attached
After awaiting promise: currentSound = undefined ← Sound ended, auto-removed!
Impact
This behavior makes it difficult to:
- Test sound replacement - Cannot await the promise with
loop: falseand checkcurrentSound - Chain sound operations - Promise timing differs based on
loopparameter - Understand API contract - Not documented that promise resolution depends on
loop
Questions
- Is this the intended behavior, or should promises always resolve when the sound is attached?
- Should there be a separate method/option to wait for sound completion vs. sound attachment?
- Should the auto-cleanup of
currentSoundbe optional or configurable?
Reproduction
Issue 1 - Web Audio Error
npm run test:api soundLook for console error:
[browser:pageerror] Error [TypeError]: Failed to execute 'linearRampToValueAtTime'
on 'AudioParam': The provided double value is non-finite.
Issue 2 - Sound Replacement
Created diagnostic test suite to investigate the behavior:
tests/sound-replacement-diagnostic.test.js(3 diagnostic tests)- Shows timing of deletion, attachment, and auto-cleanup
- Demonstrates promise resolution differences between
loop: trueandloop: false
Files Involved
Source Code
api/sound.js:233-250-playNotes()loop that accessesdurations[i]api/sound.js:267-324-playMidiNote()withlinearRampToValueAtTimecallapi/sound.js:52-117-playSound()promise resolution logic
Tests
tests/sound.test.js:212-220- Test that triggers Issue 1tests/sound-integration.test.js- Integration tests affected by Issue 2tests/sound-replacement-diagnostic.test.js- Diagnostic tests revealing behavior
API Documentation Impact
The investigation revealed important API behavior that should be documented:
playSound()promise resolution depends onloopparameterloop: true→ resolves when sound is attached to meshloop: false→ resolves when sound finishes playing
- Sound auto-cleanup -
loop: falsesounds remove themselves frommesh.metadata.currentSoundwhen they end - Replacement timing - synchronous deletion + async creation pattern (brief
undefinedwindow) playNotes()array handling - behavior whennotes.length > durations.lengthis undefined
Related Issues
None - These were discovered during sound test integration from https://github.com/commercetest/babylonjs-sound-testing
Labels
bug- Actual bugs to fixdocumentation- Needs API documentation updatessound- Sound systemquestion- Needs decision on intended behavior (Issue 2)