One stereo service plays one sound at a time, across whatever the browser offers (native audio, HLS, Howler, AirPlay, Chromecast), with template helpers that make player UI nearly declarative. You address sounds by identifier — usually just a URL string, but also a url object, a Sound, an array of any of those, or a promise resolving to any of those.
- Ember.js v4.12 or above
- Ember CLI v4.12 or above (with ember-auto-import v2 or Embroider)
- Node.js v18 or above
ember install ember-stereo
The full guide and API reference live at ember-stereo.com, with live demos of everything.
Upgrading to 6.0? Read the upgrade guide.
There's a helper or modifier for nearly every piece of player UI (playback, seeking, state, autoplay-blocking, metadata, timestamps) see the docs for the catalog. From javascript, the service does the same things: this.stereo.play(urlsOrPromise) resolves to an identity-stable Sound and findSound(identifier) returns one synchronously that reports isLoading/isPlaying/errors reactively. See Playing Sounds.
ember-stereo treats a remote device as just another connection that the sound can swap to. AirPlay and Chromecast are wired up automatically and included on demand.
The stereo service and every Sound are evented: audio-played, audio-paused, audio-ended, audio-blocked, current-sound-changed, the casting events, and more. See Monitoring Events.
NativeAudioUses the native<audio>element for playing and streaming audioHLSUses HLS.js for playing HLS streams on the desktop.HowlerUses howler to play audio
stereo will take a list of urls and find the first connection/url combo that works. For desktop browsers, we'll try each url on each connection in the order the urls were specified. For mobile browsers, we'll first try all the URLs on the NativeAudio using a technique to (hopefully) get around any autoplaying restrictions that sometimes require mobile users to click a play button twice.
If you need to test audio handling that involves ember-stereo in your app, you're gonna need this helper. It sets up and cleans up a few stereo-related items, but most importantly it stubs out the native browser audio and video elements replacing it with a FakeMediaElement that behaves sanely in the test environment.
You can control how the sound behaves by providing a url in one of these formats:
URLs that will successfully load:
good/10000/test-url.mp3: an mp3 that is 10 seconds longgood/stream/the-current.aac: an aac audio stream, duration = Infinity, will behave like a stream does
URLs that will fail:
bad/codec-error/the-current.aac: an aac sound that will fail with 'codec-error'bad/some%20custom%20string/the-current.aac: an aac sound that will fail with error message 'some custom string'
Here's an example test, testing an example player, making sure that fast forward and rewind buttons are disabled.
import { setupStereoTest } from 'ember-stereo/test-support/stereo-setup';
module('Integration | Component | player', function (hooks) {
setupStereoTest(hooks);
test('it does not display rewind and ff buttons when stream', async function (assert) {
let stereo = this.owner.lookup('service:stereo');
await stereo.play('/good/stream/test.mp3', {
metadata: {
show,
track,
},
});
await render(hbs`<Player/>`);
assert.dom('[data-test-element="fastforward-button"]').isDisabled();
assert.dom('[data-test-element="rewind-button"]').isDisabled();
assert.dom('[data-test-element="play-pause-button"]').exists();
});
});Do you need to support a funky audio format that stereo's built-in connections can't handle? Read more about how to write your own custom connection here.