Skip to content

bugbakery/html5-audio-driver

 
 

Repository files navigation

Pure HTML5 Audio Driver

npm version Standard Style Commitizen friendly FOSSA Status

Opinionated low level functional bindings to control html5 audio

Constraints (or what you won't find here)

  • Full functional audio player with controls and all the fuzz, instead you should use mediaelement
  • Support for multiple sounds and ambient control, instead you should use howler.js
  • No WebAudio, instead you should use pizzicato

Features (or what you will find here)

  • Full control over the audio element
  • Functional bindings to all necessary events
  • Composability for all audio actions
  • Helper functions to get relevant audio element properties
  • Written in vanilla es6 with only one dependency to ramda

Installation

npm install html5-audio-driver or yarn add html5-audio-driver

Usage

Creating an AudioElement

If you have already an audio element defined, good for you, skip this and use the dom reference. Otherwise you can use this helper, the helper will create an audio element without controls, preloading and loops:

import { audio } from '@podlove/html5-audio-driver'

const myAudioElement = audio([{
  url: 'audio-files/example.m4a',
  mimeType: 'audio/mp4'
}, {
  url: 'audio-files/example.mp3',
  mimeType: 'audio/mp3'
}, {
  url: 'audio-files/example.ogg',
  mimeType: 'audio/ogg'
}])

mimeType is needed so the browser can decide what source is appropriated to use.

Interacting with the audio element

All audio element actions are curried and accept as their first parameter the audio element. Also each action returns the audio element:

import { compose } from 'ramda'
import { play, setPlaytime } from '@podlove/html5-audio-driver/actions'

const setPlaytimeAndPlay = compose(play, setAudioPlaytime)(myAudioElement)
// Sets the playtime to 50 seconds and plays the audio
setPlaytimeAndPlay(50)

For convenience also a action composer is available:

import { actions } from '@podlove/html5-audio-driver'

const audioActions = actions(myAudioElement)

audioActions.load()
audioActions.play()
audioActions.pause()
audioActions.setPlaytime(50)
audioActions.setRate(1.5)
audioActions.mute()
audioActions.unmute()

Available Actions:

Function Action parameters
play Safeplays the audio, initiates load if not already loaded void
pause pauses the audio void
load loads the audio void
mute mutes the audio void
unmute unmutes the audio void
setRate sets the play rate number: [0.5 ... 4]
setPlaytime sets the current play time number: [0 ... duration]

Reacting to audio events

All audio events are curried and accept as their first parameter the audio element. The second parameter is always the callback function. Each event returns a different set of audio properties, depending on the event scope:

import { onPlay } from '@podlove/html5-audio-driver/events'

const playEvent = onPlay(myAudioElement)

playEvent(console.log) // similar to onPlay(myAudioElement, console.log)
/**
* Will log audio properties on audio play:
* {
*  duration,
*  buffered,
*  volume,
*  state,
*  playtime,
*  ended,
*  rate,
*  muted,
*  src,
*  paused,
*  playing
* }
*/

For convenience also a events composer is available:

import { events } from '@podlove/html5-audio-driver'

const audioEvents = events(myAudioElement)

audioEvents.onLoading(console.log)
audioEvents.onLoaded(console.log)
audioEvents.onReady(console.log)
audioEvents.onPlay(console.log)
audioEvents.onPause(console.log)
audioEvents.onBufferChange(console.log)
audioEvents.onBuffering(console.log)
audioEvents.onPlaytimeUpdate(console.log)
audioEvents.onVolumeChange(console.log)
audioEvents.onError(console.log)
audioEvents.onDurationChange(console.log)
audioEvents.onRateChange(console.log)
audioEvents.onEnd(console.log)

Available Events:

Function Event Original Callback Payload Once
onLoading When browser starts audio loading progress All props true
onLoaded When browser loaded the entire file canplaythrough All props true
onReady When browser has enough data to play canplay All props false
onPlay When browser starts playing audio play All props false
onPause When browser pauses audio pause All props false
onEnd When browser reaches end of audio ended All props false
onBufferChange When browser buffered a new audio segment progress buffered segments false
onBuffering When browser waits for audio segments to play waiting All props false
onPlaytimeUpdate When currentTime of audio changes timeupdate playtime false
onVolumeChange When volume of audio changes volumechange volume false
onError When an error occurred while playing the audio error NETWORK_NO_SOURCE, NETWORK_EMPTY, NETWORK_LOADING, MEDIA_ERROR false
onDurationChange When browser has new information on audio duration durationchange duration false
onRateChange When browser detects a change in audio playback rate ratechange rate false
onFilterUpdate When a filter has been changed filterUpdated All props false

Audio Element Properties

Multiple different functions are provided to give you easy access to audio element properties. Initially most of them are undefined:

import { volume } from '@podlove/html5-audio-driver/props'

isPlaying(myAudioElement) // Will return false

For convenience also a composed version is available giving you all available properties:

import { props } from '@podlove/html5-audio-driver/props'

props(myAudioElement)
/**
* {
*  duration,
*  buffered,
*  volume,
*  state,
*  playtime,
*  ended,
*  rate,
*  muted,
*  src,
*  paused,
*  playing
* }
*/

Available Properties:

Function Description Return Value Initial Value
duration Duration of audio in seconds number undefined
buffered Buffered audio segments start and end in seconds [[number, number], ...] []
volume Audio volume number: [0...1] undefined
state Network State HAVE_NOTHING, HAVE_METADATA, HAVE_CURRENT_DATA, HAVE_FUTURE_DATA, HAVE_ENOUGH_DATA undefined
playtime Current audio playtime position in sconds number undefined
ended Indicates if audio has ended boolean undefined
rate Audio playback rate number: [0.5 ... 4] undefined
muted Indicates if audio is muted boolean undefined
src Used audio source string undefined
paused Indicates if audio is paused boolean undefined
channels Available audio channels number undefined
playing Indicates if audio is playing boolean false

Handled HTML5 Quirks and Limitations (the nasty part :/)

HTML5 audio was a needed addition to get rid of the flash hell. Although it is already multiple years implemented in all the different browsers each implementation has it's flaws. If you want to dive deeper into the topic I recommend you the following article.

Play Action

Using the play action will give you a safe function that surpresses most of the errors. One source is that older browsers doesn't implement audio.play() as a promise. Also there is a race condition between play and pause that needs to be .catched.

Playtime (CurrentTime)

In Safari and mobile Safari it isn't possible to set the currentTime before loading the audio. You can set it but it won't mutate the audio currentTime value. html5-audio-driver therefore uses a custom playtime attribute that is synced wit the currentTime.

Mobile Environments

To play audio on mobile devices you have to trigger use a direct user interaction to trigger the audio. Also volume is not available on mobile devices.

Legacy Browser Support (IE11)

In case you need IE11 support you have to provide some polyfills in your application. Have a look at the test polyfills to see a working example.

Publishing

Run npm publish:prepare move to the dist/ folder and run npm publish --public

License

FOSSA Status

About

Pure HTML5 Audio Driver

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages

  • TypeScript 86.1%
  • JavaScript 7.0%
  • HTML 6.8%
  • Shell 0.1%