79 lines
3.3 KiB
TypeScript
79 lines
3.3 KiB
TypeScript
// @vitest-environment jsdom
|
||
|
||
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
|
||
import { ChimeTone } from '../../../../src/card-controller/call/tones/chime';
|
||
import { useAudioMocks } from './test-utils';
|
||
|
||
const audio = useAudioMocks();
|
||
|
||
// Each strike emits a 3-layer bell stack in the order: sparkle (one octave
|
||
// above the fundamental), fundamental, hum (one octave below). The frequencies
|
||
// here are the per-layer values derived from each strike's fundamental.
|
||
describe('ChimeTone', () => {
|
||
it('should play DING (Eb5) then DOOOOONG (B4) 0.5s later', () => {
|
||
new ChimeTone(0).start();
|
||
|
||
// 2 strikes × 3 layers = 6 oscillators.
|
||
expect(audio.oscillators).toHaveLength(6);
|
||
|
||
// DING -- Eb5 (622.25Hz) at t=0.
|
||
expect(audio.oscillators[0].frequency.value).toBe(622.25 * 2);
|
||
expect(audio.oscillators[1].frequency.value).toBe(622.25);
|
||
expect(audio.oscillators[2].frequency.value).toBe(622.25 / 2);
|
||
expect(audio.oscillators[0].start).toBeCalledWith(0);
|
||
expect(audio.oscillators[1].start).toBeCalledWith(0);
|
||
expect(audio.oscillators[2].start).toBeCalledWith(0);
|
||
|
||
// DOOOOONG -- B4 (493.88Hz) at t=0.5.
|
||
expect(audio.oscillators[3].frequency.value).toBe(493.88 * 2);
|
||
expect(audio.oscillators[4].frequency.value).toBe(493.88);
|
||
expect(audio.oscillators[5].frequency.value).toBe(493.88 / 2);
|
||
expect(audio.oscillators[3].start).toBeCalledWith(0.5);
|
||
expect(audio.oscillators[4].start).toBeCalledWith(0.5);
|
||
expect(audio.oscillators[5].start).toBeCalledWith(0.5);
|
||
});
|
||
|
||
it('should give DING a brighter, shorter bell envelope', () => {
|
||
new ChimeTone(0).start();
|
||
|
||
// Sparkle / fundamental / hum peaks for DING.
|
||
expect(audio.gainParams[0].linearRampToValueAtTime).toBeCalledWith(0.1, 0.005);
|
||
expect(audio.gainParams[1].linearRampToValueAtTime).toBeCalledWith(0.22, 0.005);
|
||
expect(audio.gainParams[2].linearRampToValueAtTime).toBeCalledWith(0.08, 0.005);
|
||
// Decay constants (sparkle fades fastest, hum lingers).
|
||
expect(audio.gainParams[0].setTargetAtTime).toBeCalledWith(0, 0.005, 0.3);
|
||
expect(audio.gainParams[1].setTargetAtTime).toBeCalledWith(0, 0.005, 0.8);
|
||
expect(audio.gainParams[2].setTargetAtTime).toBeCalledWith(0, 0.005, 1.2);
|
||
});
|
||
|
||
it('should give DOOOOONG a fuller, longer bell envelope', () => {
|
||
new ChimeTone(0).start();
|
||
|
||
expect(audio.gainParams[3].linearRampToValueAtTime).toBeCalledWith(0.11, 0.505);
|
||
expect(audio.gainParams[4].linearRampToValueAtTime).toBeCalledWith(0.28, 0.505);
|
||
expect(audio.gainParams[5].linearRampToValueAtTime).toBeCalledWith(0.1, 0.505);
|
||
expect(audio.gainParams[3].setTargetAtTime).toBeCalledWith(0, 0.505, 0.5);
|
||
expect(audio.gainParams[4].setTargetAtTime).toBeCalledWith(0, 0.505, 1.3);
|
||
expect(audio.gainParams[5].setTargetAtTime).toBeCalledWith(0, 0.505, 1.8);
|
||
});
|
||
|
||
describe('with fake timers', () => {
|
||
beforeEach(() => {
|
||
vi.useFakeTimers();
|
||
});
|
||
afterEach(() => {
|
||
vi.useRealTimers();
|
||
});
|
||
|
||
it('should schedule the next iteration 5 seconds after a strike pair', () => {
|
||
new ChimeTone(0).start();
|
||
expect(audio.oscillators).toHaveLength(6);
|
||
|
||
vi.advanceTimersByTime(5_000);
|
||
|
||
// A second iteration ran, producing another 6 oscillators.
|
||
expect(audio.oscillators).toHaveLength(12);
|
||
});
|
||
});
|
||
});
|