Files
Genarrative/src/components/common/creativeAudioSilenceTrim.test.ts
T
JenkenB 27b30f974b Update spacetime-client bindings and frontend
Large update across server and web clients: regenerated/added many spacetime-client module bindings and input types (including new delete/work_delete input types and numerous procedure/reducer files), updates to server-rs API modules (bark_battle, jump_hop, wooden_fish, auth, module-runtime and shared contracts), and fixes in module-runtime behavior and domain logic. Frontend changes include new/updated components and tests (creative audio helpers, bark-battle/jump-hop/wooden-fish clients and views, unified generation pages, RPG entry views, and runtime shells), plus CSS and service updates. Documentation and operational notes updated (.hermes pitfalls and multiple PRD/docs) to cover daily-task refresh, banner asset fallback, recommend-key bug, and other platform behaviors. Tests and verification steps added/updated alongside these changes.
2026-06-04 22:44:19 +08:00

78 lines
2.3 KiB
TypeScript

import { expect, test } from 'vitest';
import {
buildLeadingSilenceTrimmedWavBlob,
findFirstAudibleFrame,
} from './creativeAudioSilenceTrim';
function createAudioBufferStub(
channels: number[][],
sampleRate = 1000,
): AudioBuffer {
return {
length: channels[0]?.length ?? 0,
numberOfChannels: channels.length,
sampleRate,
duration: (channels[0]?.length ?? 0) / sampleRate,
getChannelData: (channel: number) =>
new Float32Array(channels[channel] ?? []),
} as AudioBuffer;
}
test('findFirstAudibleFrame skips leading frames that are silent across all channels', () => {
const buffer = createAudioBufferStub([
[0, 0.003, -0.006, 0.012, 0.02],
[0, -0.004, 0.009, 0, 0],
]);
expect(findFirstAudibleFrame(buffer, 0.01)).toBe(3);
});
test('buildLeadingSilenceTrimmedWavBlob writes a wav that starts at the first audible frame', async () => {
const buffer = createAudioBufferStub(
[
[0, 0, 0, 0.25, -0.5],
[0, 0, 0, -0.25, 0.5],
],
1000,
);
const blob = buildLeadingSilenceTrimmedWavBlob(buffer, {
silenceThreshold: 0.01,
minimumTrimDurationMs: 1,
});
expect(blob).not.toBeNull();
expect(blob?.type).toBe('audio/wav');
const bytes = await blob!.arrayBuffer();
const view = new DataView(bytes);
expect(String.fromCharCode(...new Uint8Array(bytes, 0, 4))).toBe('RIFF');
expect(String.fromCharCode(...new Uint8Array(bytes, 8, 4))).toBe('WAVE');
expect(String.fromCharCode(...new Uint8Array(bytes, 36, 4))).toBe('data');
expect(view.getUint32(40, true)).toBe(8);
expect(view.getInt16(44, true)).toBeCloseTo(8191, -1);
expect(view.getInt16(46, true)).toBeCloseTo(-8192, -1);
expect(view.getInt16(48, true)).toBeCloseTo(-16384, -1);
expect(view.getInt16(50, true)).toBeCloseTo(16383, -1);
});
test('buildLeadingSilenceTrimmedWavBlob keeps the original recording when no leading silence is removable', () => {
const startsImmediately = createAudioBufferStub([[0.2, 0.1, 0.05]], 1000);
const allSilent = createAudioBufferStub([[0, 0.001, -0.001]], 1000);
expect(
buildLeadingSilenceTrimmedWavBlob(startsImmediately, {
silenceThreshold: 0.01,
minimumTrimDurationMs: 1,
}),
).toBeNull();
expect(
buildLeadingSilenceTrimmedWavBlob(allSilent, {
silenceThreshold: 0.01,
minimumTrimDurationMs: 1,
}),
).toBeNull();
});