Skip to content

Commit

Permalink
THE BIG RENAME: name change to ember-stereo, rename hifi references t…
Browse files Browse the repository at this point in the history
…o stereo, rename some helpers to make more sense
  • Loading branch information
jkeen committed Jun 2, 2021
1 parent 9d0de9c commit d8a10b3
Show file tree
Hide file tree
Showing 160 changed files with 50,445 additions and 745 deletions.
14 changes: 10 additions & 4 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,11 +1,17 @@
# ember-hifi Changelog
# ember-stereo Changelog

### 2.0

- [IMPROVEMENT] Added helpers and modifiers to operate hifi only from templates
- [CHANGE] audio events now fire an object with an object as the first object, with the `sound`, matching the `play` and `load` functions. All event listeners should change from `this.hifi.on('event-name', (sound) => { // handler })` to `this.hifi.on('event-name', ({sound}) => { // handler })`
- [IMPROVEMENT] Added helpers and modifiers to operate hifi only from templates, removing a huge hurdle for newcomers having to deal with audio ids
- [CHANGE] audio events now fire an object as the first argument, with the `sound`, matching the `play` and `load` functions. All event listeners should change from `this.stereo.on('event-name', (sound) => { // handler })` to `this.stereo.on('event-name', ({sound}) => { // handler })`
- [CHANGE] Default volume is now 100
- [CHANGE] Audio errors no longer throw errors that need to be caught, instead they are surfaced on the hifi service, or through the `{{is-errored}}` and `{{hifi-error-details}}`
- [CHANGE] Audio errors no longer throw errors that need to be caught, instead they are surfaced on the hifi service, or through the `{{sound-is-errored}}` and `{{sound-error-details}}`

- [CHORE] Renamed anything that was `hifi` to `stereo`
- [CHORE] Upgrade to Ember 3.24. This addon is octane-only now
- [CHORE] Complex promise/event waiting loops that were prone to error and hard to test have been replaced with ember concurrency tasks

# FORKED from ember-hifi

### 1.18 (September 29, 2020)

Expand Down
18 changes: 9 additions & 9 deletions CUSTOM_CONNECTIONS.md
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@

## Writing Your Own Hifi Connection
## Writing Your Own Stereo Connection

Do you need to support a funky audio format that requires a special library, or do you really want to buck this whole HTML5-only strategy and play sounds using Flash? You can make your own hifi connection.
Do you need to support a funky audio format that requires a special library, or do you really want to buck this whole HTML5-only strategy and play sounds using Flash? You can make your own stereo connection.

```sh
$ ember generate hifi-connection flash-connection
$ ember generate stereo-connection flash-connection
```

This creates `app/hifi-connections/flash-connection.js` and a unit test at `tests/unit/hifi-connections/flash-connection.js`, which you should now customize.
This creates `app/stereo-connections/flash-connection.js` and a unit test at `tests/unit/stereo-connections/flash-connection.js`, which you should now customize.

The files created by the blueprint should walk you through what you need to implement, but to be thorough:

Expand All @@ -30,9 +30,9 @@ The files created by the blueprint should walk you through what you need to impl
});
```

`canPlayMimeType` and `canUseConnection` are called when `hifi` is looking for connections to try with a url. Give your best guess here. For instance, our built-in HLS.js library won't work on mobile, so `canUseConnection` returns false on a mobile device and true on a desktop browser. Similary, HLS only plays `application/vnd.apple.mpegurl` files, so we just check for that extension in `canPlayMimeType`.
`canPlayMimeType` and `canUseConnection` are called when `stereo` is looking for connections to try with a url. Give your best guess here. For instance, our built-in HLS.js library won't work on mobile, so `canUseConnection` returns false on a mobile device and true on a desktop browser. Similary, HLS only plays `application/vnd.apple.mpegurl` files, so we just check for that extension in `canPlayMimeType`.

##### Implement methods to bridge communication between hifi and your third party sound.
##### Implement methods to bridge communication between stereo and your third party sound.

- `setup()`
Wire up your library to trigger the following methods when things happen on your sound:
Expand Down Expand Up @@ -63,7 +63,7 @@ let FlashConnection = BaseSound.extend({
sound.trigger("audio-ready")
},
onloaderror: function(error) {
// Couldn't load this sound. Tell hifi to move on and try another url/connection
// Couldn't load this sound. Tell stereo to move on and try another url/connection
sound.trigger('audio-load-error', {error});
},
onpause: function() {
Expand Down Expand Up @@ -96,7 +96,7 @@ let FlashConnection = BaseSound.extend({
}
```
### Other required methods to let hifi control your sound
### Other required methods to let stereo control your sound
```javascript
_setVolume(volume) {
Expand Down Expand Up @@ -144,7 +144,7 @@ Once you have implemented your new connection, you can add it to your app's conf
```js
module.exports = function(environment) {
var ENV = {
emberHifi:
emberStereo:
debug: true, // get ready for some deep console messages to help you find your way
connections: [
{
Expand Down
69 changes: 35 additions & 34 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,34 +1,34 @@
# ember-hifi
# ember-stereo

## The easy way to play audio in your ember app

![Download count all time](https://img.shields.io/npm/dt/ember-hifi.svg) [![npm version](https://img.shields.io/npm/v/ember-hifi.svg?style=flat-square)](https://www.npmjs.com/package/ember-hifi) [![CircleCI](https://img.shields.io/circleci/project/github/nypublicradio/ember-hifi/master.svg?style=flat-square)](https://circleci.com/gh/nypublicradio/ember-hifi/tree/master) [![Ember Observer Score](http://emberobserver.com/badges/ember-hifi.svg)](http://emberobserver.com/addons/ember-hifi)
[![Maintainability](https://api.codeclimate.com/v1/badges/24a53d2c7a91e15d7200/maintainability)](https://codeclimate.com/github/nypublicradio/ember-hifi/maintainability)
[![Test Coverage](https://api.codeclimate.com/v1/badges/24a53d2c7a91e15d7200/test_coverage)](https://codeclimate.com/github/nypublicradio/ember-hifi/test_coverage)
![Download count all time](https://img.shields.io/npm/dt/ember-stereo.svg) [![npm version](https://img.shields.io/npm/v/ember-stereo.svg?style=flat-square)](https://www.npmjs.com/package/ember-stereo) [![CircleCI](https://img.shields.io/circleci/project/github/nypublicradio/ember-stereo/master.svg?style=flat-square)](https://circleci.com/gh/nypublicradio/ember-stereo/tree/master) [![Ember Observer Score](http://emberobserver.com/badges/ember-stereo.svg)](http://emberobserver.com/addons/ember-stereo)
[![Maintainability](https://api.codeclimate.com/v1/badges/24a53d2c7a91e15d7200/maintainability)](https://codeclimate.com/github/nypublicradio/ember-stereo/maintainability)
[![Test Coverage](https://api.codeclimate.com/v1/badges/24a53d2c7a91e15d7200/test_coverage)](https://codeclimate.com/github/nypublicradio/ember-stereo/test_coverage)

This addon exposes a `hifi` service which produces `Sound` objects which represent a playable piece of audio.
This addon exposes a `stereo` service which produces `Sound` objects which represent a playable piece of audio.

The `hifi` service makes it easy to play audio in the unfriendly landscape that is the current state of audio on the web. Forget worrying about formats and browsers and just give `hifi` a list of URLs to try and it'll play the first one that works.
The `stereo` service makes it easy to play audio in the unfriendly landscape that is the current state of audio on the web. Forget worrying about formats and browsers and just give `stereo` a list of URLs to try and it'll play the first one that works.

* Ember.js v3.16 or above
* Node.js v10 or above

## Installing The Addon

```shell
npm install ember-hifi
npm install ember-stereo
```

### Upgrading from < 1.11.0
`ember-hifi` no longer adds bower dependencies. If you are upgrading, you should edit your app's `bower.json` to remove the `hls.js` and `howler.js` entries added by previous versions of `ember-hifi`. NPM's dependency graph will take care of installing these libraries, and they will be added to your app's vendor tree at build time. Thanks to [@gmurphey](https://github.com/gmurphey) for [#41](https://github.com/nypublicradio/ember-hifi/pull/41).
`ember-stereo` no longer adds bower dependencies. If you are upgrading, you should edit your app's `bower.json` to remove the `hls.js` and `howler.js` entries added by previous versions of `ember-stereo`. NPM's dependency graph will take care of installing these libraries, and they will be added to your app's vendor tree at build time. Thanks to [@gmurphey](https://github.com/gmurphey) for [#41](https://github.com/nypublicradio/ember-stereo/pull/41).

## Usage


### API

#### Service API
`hifi` plays one sound at a time. Multiple sounds can be loaded and ready to go, but only one sound plays at a time. The currently playing sound is set to `currentSound` on the service, and most methods and properties on the service simply proxy to that sound.
`stereo` plays one sound at a time. Multiple sounds can be loaded and ready to go, but only one sound plays at a time. The currently playing sound is set to `currentSound` on the service, and most methods and properties on the service simply proxy to that sound.

###### Methods

Expand All @@ -42,13 +42,13 @@ If the audio URLs are not known at the time of a play event, give `play` the pro

```javascript
export default Ember.Route.extend({
hifi: service(),
stereo: service(),
...
actions: {
play(id) {
let urlPromise = this.store.findRecord('story', id).then(story => story.getProperties('aacUrl', 'hlsUrl'))

this.hifi.play(urlPromise).then(({sound}) => {
this.stereo.play(urlPromise).then(({sound}) => {
// sound object

}).catch(error => {
Expand All @@ -62,11 +62,11 @@ If you already know the URLs, just pass them in.

```javascript
export default Ember.Route.extend({
hifi: service(),
stereo: service(),
...
actions: {
play(urls) {
this.hifi.play(urls).then(({sound}) => {
this.stereo.play(urls).then(({sound}) => {
// sound object

}).catch(error => {
Expand All @@ -90,7 +90,7 @@ Moves the playhead of the current sound forwards by duration (in ms)
Moves the playhead of the current sound backwards by duration (in ms)

- `load(urlsOrPromise, options)`
Tries each hifi connection with each url and returns the ready `sound` from the first combination that works. The sound is cached internally so on subsequent load requests with the same url the already prepared sound will be returned. Calling `play` on the returned sound will start playback immediately.
Tries each stereo connection with each url and returns the ready `sound` from the first combination that works. The sound is cached internally so on subsequent load requests with the same url the already prepared sound will be returned. Calling `play` on the returned sound will start playback immediately.

###### Gettable/Settable Properties
- `volume` (integer, 0-100)
Expand All @@ -101,11 +101,11 @@ System volume. Bind a range element to this property for a simple volume control
//component.js
import { inject as service } from "@ember/service";
export default Component.extend({
hifi: service(),
stereo: service(),
})

//template.hbs
{{input type="range" value=hifi.volume}}
{{input type="range" value=stereo.volume}}
```

- `position` (integer, in ms)
Expand All @@ -114,11 +114,11 @@ Here's a silly way to make a position control, too.
```javascript
//component.js
export default Component.extend({
hifi: service(),
stereo: service(),
})

//template.hbs
{{input type="range" value=hifi.position min=0 max=hifi.duration step=1000}}
{{input type="range" value=stereo.position min=0 max=stereo.duration step=1000}}
```

###### Read Only Properties
Expand Down Expand Up @@ -162,9 +162,9 @@ Moves the playhead of the sound backwards by duration (in ms)
- `url` the url of the sound

### Events
The `hifi` service and the `sound` objects are extended with [Ember.Evented](https://www.emberjs.com/api/classes/Ember.Evented.html). You can subscribe to the following events in your application.
The `stereo` service and the `sound` objects are extended with [Ember.Evented](https://www.emberjs.com/api/classes/Ember.Evented.html). You can subscribe to the following events in your application.

###### Triggered on both the sound and relayed through the hifi service
###### Triggered on both the sound and relayed through the stereo service

- `audio-played` ({ sound }) - the sound started playing
- `audio-paused` ({ sound }) - the sound was paused
Expand All @@ -175,7 +175,8 @@ The `hifi` service and the `sound` objects are extended with [Ember.Evented](htt
- `audio-will-fast-forward` ({sound, currentPosition, newPosition}) - fired before fast-forwarding a sound
- `audio-position-will-change` ({sound, currentPosition, newPosition}) - fired before audio position change
- `audio-position-changed` ({sound})
###### Hifi service events

###### Stereo service events
- `current-sound-changed` ({sound, previousSound}) - triggered when the current sound changes. On initial play, previousSound will be undefined.
- `current-sound-interrupted` ({sound, previousSound}) - triggered when a sound has been playing and a new one takes its place by being played, pausing the first one
- `new-load-request` ({loadPromise, urlsOrPromise, options}) - triggered whenever `.load` or `.play` is called.
Expand All @@ -189,50 +190,50 @@ The `hifi` service and the `sound` objects are extended with [Ember.Evented](htt
1. `HLS` - Uses HLS.js for playing HLS streams on the desktop.
1. `Howler` - Uses [howler](http://howlerjs.com) to play audio

`hifi` 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.
`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.

## Test Helpers
#### Acceptance Tests

Import this helper into acceptance tests to stub out hifi.
Import this helper into acceptance tests to stub out stereo.

```javascript
import '[your-app-name]/tests/helpers/hifi-acceptance-helper';
import '[your-app-name]/tests/helpers/stereo-acceptance-helper';
```

#### Unit Tests + Integration Tests

If you have a unit test that interacts with ember-hifi, you might get some errors if hifi's needs aren't met. Hifi uses some internal services that we'd hate for you to have to know about or type out, so just use our helper instead.
If you have a unit test that interacts with ember-stereo, you might get some errors if stereo's needs aren't met. Hifi uses some internal services that we'd hate for you to have to know about or type out, so just use our helper instead.

```javascript
import { hifiNeeds, dummyHifi } from 'overhaul/tests/helpers/hifi-integration-helpers';
import { stereoNeeds, dummyStereo } from 'overhaul/tests/helpers/stereo-integration-helpers';

moduleFor('[your module]', 'Unit | [type] | [your module]', {
needs: [...hifiNeeds]
needs: [...stereoNeeds]

...
});
```

If you need to fake out the hifiService to test how your app handles hifi events, you can use the dummyHifi service
If you need to fake out the stereoService to test how your app handles stereo events, you can use the dummyStereo service

```javascript
import { hifiNeeds, dummyHifi } from 'overhaul/tests/helpers/hifi-integration-helpers';
import { stereoNeeds, dummyStereo } from 'overhaul/tests/helpers/stereo-integration-helpers';

moduleFor('[your module]', 'Integration | [type] | [your module]', {
needs: [...hifiNeeds],
needs: [...stereoNeeds],

beforeEach() {
this.register('service:hifi', dummyHifi);
this.inject.service('hifi');
this.register('service:stereo', dummyStereo);
this.inject.service('stereo');
}
...
});
```

After stubbing out the service with the dummyHifi service you can pass it some special urls in the format `/:status/:length/:name` to mimic responses, where `status` can be `good` or `bad`, and `length` can be an integer representing the duration in ms, or `stream`.
After stubbing out the service with the dummyStereo service you can pass it some special urls in the format `/:status/:length/:name` to mimic responses, where `status` can be `good` or `bad`, and `length` can be an integer representing the duration in ms, or `stream`.

A 10 second audio clip: `/good/10000/test`

Expand All @@ -243,4 +244,4 @@ A url that will fail: `/bad/stream/test`

## [Writing Your Own Hifi Connection](CUSTOM_CONNECTIONS.md)

Do you need to support a funky audio format that hifi's built-in connections can't handle? Read more about how to write your own custom connection [here](CUSTOM_CONNECTIONS.md).
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](CUSTOM_CONNECTIONS.md).
10 changes: 5 additions & 5 deletions addon/-private/helpers/is-helper.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import { inject as service } from '@ember/service';
import Helper from '@ember/component/helper';
import { dedupeTracked } from 'tracked-toolbox';
import hasEqualUrls from 'ember-hifi/-private/utils/has-equal-urls';
import hasEqualUrls from 'ember-stereo/-private/utils/has-equal-urls';

const UNINITIALIZED = Object.freeze({});
export default class HifiBaseIsHelper extends Helper {
@service hifi;
export default class StereoBaseIsHelper extends Helper {
@service stereo;

identifier = UNINITIALIZED;
@dedupeTracked sound = UNINITIALIZED
Expand All @@ -25,12 +25,12 @@ export default class HifiBaseIsHelper extends Helper {
this.sound = UNINITIALIZED; // if identifier changes, reinitialize sound
this.identifier = identifier || 'system';
if (this.identifier !== 'system') {
let sound = this.hifi.findLoaded(this.identifier)
let sound = this.stereo.findLoaded(this.identifier)
if (sound) {
this.sound = sound;
}
else {
this.hifi.on('new-load-request', async ({loadPromise, urlsOrPromise, /* options */}) => {
this.stereo.on('new-load-request', async ({loadPromise, urlsOrPromise, /* options */}) => {
let isEqual = await hasEqualUrls(this.identifier, urlsOrPromise);
if (isEqual) {
loadPromise.then(({sound, /* failures */}) => {
Expand Down
8 changes: 4 additions & 4 deletions addon/-private/utils/error-cache.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { A as emberArray, makeArray } from '@ember/array';
import debug from 'debug';
import { tracked } from '@glimmer/tracking';
import urlToIdentifier from 'ember-hifi/-private/utils/url-to-identifier';
import urlToIdentifier from 'ember-stereo/-private/utils/url-to-identifier';

/**
* This class caches sound objects based on urls. You shouldn't have to interact with this class.
Expand All @@ -16,8 +16,8 @@ export default class ErrorCache {
@tracked _cache = {};
name = 'error-cache'

constructor(hifi) {
this.hifi = hifi;
constructor(stereo) {
this.stereo = stereo;
}

reset() {
Expand Down Expand Up @@ -51,7 +51,7 @@ export default class ErrorCache {
*
* @param {Sound} sound
*/
cache({url, error, connectionKey}) {
cache({url, error, connectionKey}) {
let identifier = urlToIdentifier(url)
if (!this._cache[identifier]) {
this._cache[identifier] = {}
Expand Down
4 changes: 2 additions & 2 deletions addon/-private/utils/has-equal-urls.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import urlToIdentifier from 'ember-hifi/-private/utils/url-to-identifier';
import resolveUrls from 'ember-hifi/-private/utils/resolve-urls';
import urlToIdentifier from 'ember-stereo/-private/utils/url-to-identifier';
import resolveUrls from 'ember-stereo/-private/utils/resolve-urls';
import { makeArray } from '@ember/array';

export default async function hasEqualUrls(urlOrPromise1, urlOrPromise2) {
Expand Down
2 changes: 1 addition & 1 deletion addon/-private/utils/resolve-urls.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,6 @@ function resolveFunction(urlsOrPromise) {

export default async function resolveUrls(urlsOrPromise) {
let urls = prepare(await Promise.resolve(resolveFunction(urlsOrPromise)));
debug('ember-hifi')(`given urls: ${urls.join(', ')}`);
debug('ember-stereo')(`given urls: ${urls.join(', ')}`);
return urls;
}
Loading

0 comments on commit d8a10b3

Please sign in to comment.