/** The Bitcrusher's samples (#322), as its worklet makes them. */ import { describe, expect, it } from 'vitest'; import { Crusher, crushSteps } from '../../src/core/soundfx/crusher'; const RATE = 48000; const one = (value: number) => new Float32Array([value]); function crush(input: number[], bits: number | number[], rate: number, mix = 1, crusher = new Crusher(RATE)) { const out = new Float32Array(input.length); const bitsAt = typeof bits === 'number' ? one(bits) : new Float32Array(bits); crusher.render([new Float32Array(input)], [out], bitsAt, one(rate), one(mix)); return [...out]; } describe('the Bitcrusher (#322)', () => { it('rounds to 2^(bits-1) steps either side of 0, between whole bits too', () => { expect(crush([0.3, 0.6, -0.4, -0.8, 1], 1, RATE)).toEqual([0, 1, 0, -1, 1]); expect(crush([0.3, 0.6, -0.4], 2, RATE)).toEqual([0.5, 0.5, -0.5]); expect(crushSteps(3.5)).toBeCloseTo(2 ** 2.5); expect(crushSteps(16)).toBe(32768); expect([crushSteps(0), crushSteps(99), crushSteps(Number.NaN)]).toEqual([1, 32768, 32768]); }); it('holds a sample for as long as its rate says', () => { expect(crush([0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8], 16, RATE / 4).map((v) => +v.toFixed(3))).toEqual([ 0.1, 0.1, 0.1, 0.1, 0.5, 0.5, 0.5, 0.5, ]); // A third of the rate: every third sample, wherever it falls. expect( crush( [1, 2, 3, 4, 5, 6, 7].map((v) => v / 10), 16, RATE / 3, ).map((v) => +v.toFixed(3)), ).toEqual([0.1, 0.1, 0.1, 0.4, 0.4, 0.4, 0.7]); }); it('blends with the sound by its mix, and bits can change from sample to sample', () => { expect(crush([0.3], 1, RATE, 0)).toEqual([expect.closeTo(0.3)]); expect(crush([0.3], 1, RATE, 0.5)).toEqual([expect.closeTo(0.15)]); expect(crush([0.3, 0.3], [1, 2], RATE)).toEqual([0, 0.5]); }); it('lets go of what it holds once nothing comes in, so no DC is left', () => { const crusher = new Crusher(RATE); crush([0.9, 0.9], 16, 100, 1, crusher); const out = new Float32Array(4).fill(7); crusher.render([], [out], one(16), one(100), one(1)); expect([...out]).toEqual([0, 0, 0, 0]); expect(crush([0.25], 16, 100, 1, crusher)).toEqual([0.25]); }); });