Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,5 @@ test-results/
playwright-report/
.last-run.json
logs/
*.log
reports/
10 changes: 9 additions & 1 deletion api/sound.js
Original file line number Diff line number Diff line change
Expand Up @@ -232,7 +232,9 @@ export const flockSound = {
let offsetTime = 0;
for (let i = 0; i < notes.length; i++) {
const note = notes[i];
const duration = Number(durations[i]);
// Use default duration of 0.5 if missing or invalid (NaN)
const rawDuration = Number(durations[i]);
const duration = isNaN(rawDuration) ? 0.5 : rawDuration;

if (note !== null) {
flock.playMidiNote(
Expand Down Expand Up @@ -275,6 +277,12 @@ export const flockSound = {
) {
if (!context || context.state === "closed") return;

// Validate numeric parameters to prevent Web Audio API errors
if (!isFinite(duration) || !isFinite(playTime) || !isFinite(bpm)) {
console.warn('playMidiNote: Invalid parameters', { duration, playTime, bpm });
return;
}

// Create a new oscillator for each note
const osc = context.createOscillator();
const panner = mesh.metadata.panner;
Expand Down
4 changes: 4 additions & 0 deletions scripts/run-api-tests.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,10 @@ const AVAILABLE_SUITES = [
{ id: '@new', name: '🆕 Run Tests tagged @new', pattern: '@new' },
{ id: 'babylon', name: 'Basic Babylon Tests (3 tests)', pattern: 'Flock API Tests' },
{ id: 'sound', name: 'Sound Tests (1 test)', pattern: '@sound' },
{ id: 'sound-integration', name: 'Sound Integration Tests', pattern: '@sound-integration' },
{ id: 'sound-verification', name: 'Sound Verification Tests', pattern: '@sound-verification' },
{ id: 'sound-investigation', name: 'Sound API Investigation', pattern: '@investigation' },
{ id: 'sound-diagnostic', name: 'Sound Replacement Diagnostic', pattern: '@diagnostic' },
{ id: 'physics', name: 'Physics Tests (6 tests)', pattern: '@physics' },
{ id: 'materials', name: 'Materials Tests (22 tests)', pattern: '@materials' },
{ id: 'effects', name: 'Effects Tests (3 tests)', pattern: 'Effects API' },
Expand Down
102 changes: 102 additions & 0 deletions tests/sound-api-investigation.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
/**
* Sound API Investigation Test
* This test explores what methods and properties are available on BabylonJS Sound objects
* @tags @sound @slow @investigation
*/

export function runSoundAPIInvestigation(flock) {
describe("Sound API Investigation @sound @slow @investigation", function () {
this.timeout(10000);

afterEach(function () {
flock.stopAllSounds();
});

it("should inspect Sound object methods and properties", async function () {
flock.createBox('investigateBox', { x: 0, y: 0, z: 0 });

await flock.playSound('investigateBox', {
soundName: 'test.mp3',
loop: true,
volume: 0.5
});

// Wait for sound to attach
const mesh = flock.scene.getMeshByName('investigateBox');
let attempts = 0;
while (!mesh.metadata?.currentSound && attempts < 10) {
await new Promise(r => setTimeout(r, 50));
attempts++;
}

const sound = mesh.metadata.currentSound;

console.log("\n=== Sound Object Properties ===");
console.log("Object keys:", Object.keys(sound));
console.log("\n=== Methods ===");
console.log("getVolume:", typeof sound.getVolume);
console.log("setVolume:", typeof sound.setVolume);
console.log("getPlaybackRate:", typeof sound.getPlaybackRate);
console.log("setPlaybackRate:", typeof sound.setPlaybackRate);
console.log("play:", typeof sound.play);
console.log("pause:", typeof sound.pause);
console.log("stop:", typeof sound.stop);
console.log("isReady:", typeof sound.isReady);

console.log("\n=== Properties ===");
console.log("name:", sound.name);
console.log("loop:", sound.loop);
console.log("playbackRate:", sound.playbackRate);
console.log("_spatial:", sound._spatial);
console.log("_state:", sound._state);
console.log("_audioContext:", typeof sound._audioContext);
console.log("_buffer:", typeof sound._buffer);
console.log("_attachedMesh:", sound._attachedMesh?.name);

console.log("\n=== Attempting Method Calls ===");
if (typeof sound.getVolume === 'function') {
try {
const vol = sound.getVolume();
console.log("getVolume() returned:", vol);
} catch (e) {
console.log("getVolume() error:", e.message);
}
}

if (typeof sound.setVolume === 'function') {
try {
sound.setVolume(0.7);
console.log("setVolume(0.7) succeeded");
if (typeof sound.getVolume === 'function') {
console.log("New volume:", sound.getVolume());
}
} catch (e) {
console.log("setVolume() error:", e.message);
}
}

chai.expect(sound).to.not.be.undefined;
});

it("should inspect global sound properties", async function () {
await flock.playSound('__everywhere__', {
soundName: 'test.mp3',
loop: true,
volume: 0.5
});

await new Promise(r => setTimeout(r, 200));

const sound = flock.globalSounds[flock.globalSounds.length - 1];

console.log("\n=== Global Sound Properties ===");
console.log("_spatial:", sound._spatial);
console.log("name:", sound.name);
console.log("loop:", sound.loop);
console.log("playbackRate:", sound.playbackRate);
console.log("All properties:", Object.keys(sound).slice(0, 20));

chai.expect(sound).to.not.be.undefined;
});
});
}
Loading