refactor: isolate playback and upload use cases
ci / module boundaries / boundaries (push) Successful in 6s
ci / api / api (push) Successful in 47s
ci / contract / contract (сгенерированный код совпадает с openapi.yaml) (push) Successful in 1m2s
ci / db / db (push) Successful in 1m4s
ci / docker / docker (образы собираются — их сервер собирает сам при обновлении) (push) Successful in 1m30s
ci / ts / ts (packages + web) (push) Successful in 2m54s
ci / mobile / mobile (Metro-бандл Android) (push) Successful in 1m14s
ci / module boundaries / boundaries (push) Successful in 6s
ci / api / api (push) Successful in 47s
ci / contract / contract (сгенерированный код совпадает с openapi.yaml) (push) Successful in 1m2s
ci / db / db (push) Successful in 1m4s
ci / docker / docker (образы собираются — их сервер собирает сам при обновлении) (push) Successful in 1m30s
ci / ts / ts (packages + web) (push) Successful in 2m54s
ci / mobile / mobile (Metro-бандл Android) (push) Successful in 1m14s
This commit is contained in:
1 parent
c84f90e2cc
commit
e158558da3
56 files changed
+3337
-2196
No files matched your search
@@ -1,48 +1,57 @@
|
||||
import type { AuthSession } from '@pipu/core';
|
||||
import { afterEach, describe, expect, it, vi } from 'vitest';
|
||||
|
||||
import { Player } from './player';
|
||||
import { AudioAnalysis } from './analysis';
|
||||
import { AudioEngine } from './engine';
|
||||
|
||||
afterEach(() => vi.restoreAllMocks());
|
||||
|
||||
function fixture() {
|
||||
const player = new Player({} as AuthSession, document.createElement('audio'));
|
||||
const sample = vi.fn((data: Uint8Array) => {
|
||||
data.fill(0);
|
||||
// 48 kHz / 1024: bins 1..4 cover the bass band.
|
||||
data.fill(255, 1, 5);
|
||||
});
|
||||
Object.assign(player, {
|
||||
state: { ...player.getState(), playing: true },
|
||||
analyser: { context: { sampleRate: 48000 }, fftSize: 1024, getByteFrequencyData: sample },
|
||||
});
|
||||
return { player, sample };
|
||||
const analyser = { context: { sampleRate: 48000 }, fftSize: 1024, getByteFrequencyData: sample } as unknown as AnalyserNode;
|
||||
const analysis = new AudioAnalysis(() => analyser);
|
||||
return { analysis, sample };
|
||||
}
|
||||
|
||||
describe('audio reactivity', () => {
|
||||
it('separates bass from the other frequency bands and shares the frame', () => {
|
||||
vi.spyOn(performance, 'now').mockReturnValue(100);
|
||||
const { player, sample } = fixture();
|
||||
expect(player.readAudio()).toEqual({ bass: 1, mid: 0, treble: 0 });
|
||||
player.readAudio();
|
||||
const { analysis, sample } = fixture();
|
||||
expect(analysis.read(true)).toEqual({ bass: 1, mid: 0, treble: 0 });
|
||||
analysis.read(true);
|
||||
expect(sample).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it('returns silence after pausing or muting', () => {
|
||||
const clock = vi.spyOn(performance, 'now').mockReturnValue(100);
|
||||
const { player, sample } = fixture();
|
||||
player.readAudio();
|
||||
Object.assign(player, { state: { ...player.getState(), playing: false } });
|
||||
const { analysis, sample } = fixture();
|
||||
analysis.read(true);
|
||||
clock.mockReturnValue(120);
|
||||
expect(player.readAudio()).toEqual({ bass: 0, mid: 0, treble: 0 });
|
||||
Object.assign(player, { state: { ...player.getState(), playing: true, muted: true } });
|
||||
expect(analysis.read(false)).toEqual({ bass: 0, mid: 0, treble: 0 });
|
||||
clock.mockReturnValue(140);
|
||||
expect(player.readAudio()).toEqual({ bass: 0, mid: 0, treble: 0 });
|
||||
expect(analysis.read(false)).toEqual({ bass: 0, mid: 0, treble: 0 });
|
||||
expect(sample).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it('works when Web Audio is unavailable', () => {
|
||||
const player = new Player({} as AuthSession, document.createElement('audio'));
|
||||
expect(player.readAudio()).toEqual({ bass: 0, mid: 0, treble: 0 });
|
||||
const analysis = new AudioAnalysis(() => null);
|
||||
expect(analysis.read(false)).toEqual({ bass: 0, mid: 0, treble: 0 });
|
||||
});
|
||||
});
|
||||
|
||||
describe('audio engine lifetime', () => {
|
||||
it('removes audio listeners on disposal', () => {
|
||||
const audio = document.createElement('audio');
|
||||
const engine = new AudioEngine(audio);
|
||||
const playing = vi.fn();
|
||||
engine.on('playing', playing);
|
||||
audio.dispatchEvent(new Event('playing'));
|
||||
expect(playing).toHaveBeenCalledTimes(1);
|
||||
engine.dispose();
|
||||
audio.dispatchEvent(new Event('playing'));
|
||||
expect(playing).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
});
|
||||
Reference in new issue
Block a user