UNPKG

@firstcoders/hls-web-audio

Version:

`@firstcoders/hls-web-audio` plays multiple streamed audio tracks in sync using the Web Audio API. It is designed for stem-like playback where each track can be independently loaded, buffered, scheduled, and mixed.

163 lines (140 loc) 4.2 kB
import { expect } from '@bundled-es-modules/chai'; import sinon from 'sinon'; import SegmentPlayer from '../../../src/io/SegmentPlayer.js'; describe('SegmentPlayer', () => { let player; let acMock; let sourceNodeMock; let audioBufferMock; let destinationMock; beforeEach(() => { player = new SegmentPlayer(); sourceNodeMock = { connect: sinon.spy(), disconnect: sinon.spy(), start: sinon.spy(), stop: sinon.spy(), buffer: null, onended: null, }; acMock = { createBufferSource: sinon.stub().returns(sourceNodeMock), }; audioBufferMock = {}; destinationMock = {}; }); describe('#connect', () => { it('creates a buffer source, connects, and starts it', async () => { const onEndedSpy = sinon.spy(); await player.connect({ ac: acMock, audioBuffer: audioBufferMock, destination: destinationMock, start: 10, offset: 2, stop: 15, onEnded: onEndedSpy, }); expect(acMock.createBufferSource.calledOnce).to.be.true; expect(sourceNodeMock.buffer).to.equal(audioBufferMock); expect(sourceNodeMock.connect.calledWith(destinationMock)).to.be.true; expect(sourceNodeMock.start.calledWith(10, 2)).to.be.true; expect(sourceNodeMock.stop.calledWith(15)).to.be.true; // Simulate onended expect(typeof sourceNodeMock.onended).to.equal('function'); sourceNodeMock.onended(); expect(onEndedSpy.calledOnce).to.be.true; }); it('throws if trying to connect when already connected', async () => { await player.connect({ ac: acMock, audioBuffer: audioBufferMock, destination: destinationMock, start: 0, offset: 0, stop: 5, }); try { await player.connect({ ac: acMock, audioBuffer: audioBufferMock, destination: destinationMock, start: 0, offset: 0, stop: 5, }); expect.fail('Should have thrown'); } catch (err) { expect(err.message).to.equal('Cannot connect a segment twice'); } }); it('works without onEnded callback', async () => { await player.connect({ ac: acMock, audioBuffer: audioBufferMock, destination: destinationMock, start: 10, offset: 2, stop: 15, }); // Should not throw sourceNodeMock.onended(); }); }); describe('#disconnect', () => { it('disconnects and stops the source node, and clears buffer', async () => { await player.connect({ ac: acMock, audioBuffer: audioBufferMock, destination: destinationMock, start: 0, offset: 0, stop: 5, }); player.disconnect(); expect(sourceNodeMock.disconnect.calledOnce).to.be.true; expect(sourceNodeMock.stop.calledTwice).to.be.true; expect(sourceNodeMock.buffer).to.be.null; expect(player.isReady).to.be.false; }); it('does nothing if not connected', () => { // Should not throw player.disconnect(); }); it('ignores errors when setting buffer to null', async () => { await player.connect({ ac: acMock, audioBuffer: audioBufferMock, destination: destinationMock, start: 0, offset: 0, stop: 5, }); // Simulate a browser environment that throws when clearing buffer Object.defineProperty(sourceNodeMock, 'buffer', { set() { throw new Error('Cannot set buffer'); }, }); // Should not throw player.disconnect(); expect(player.isReady).to.be.false; }); }); describe('#isReady', () => { it('returns true when connected and false when disconnected', async () => { expect(player.isReady).to.be.false; await player.connect({ ac: acMock, audioBuffer: audioBufferMock, destination: destinationMock, start: 0, offset: 0, stop: 5, }); expect(player.isReady).to.be.true; player.disconnect(); expect(player.isReady).to.be.false; }); }); });