Skip to content

Commit b81fb82

Browse files
feat(facade): add a mutable audio connector
1 parent d7a9731 commit b81fb82

File tree

7 files changed

+148
-117
lines changed

7 files changed

+148
-117
lines changed

build/webpack.config.build.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,8 @@ module.exports = {
1313
utils: file('utils'),
1414
hls: file('hls'),
1515
filters: file('filters'),
16-
media: file('media')
16+
media: file('media'),
17+
connect: file('connect')
1718
},
1819
output: {
1920
path: path.resolve('./dist'),

example/audio-connect.js

Lines changed: 50 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,18 @@
1+
import { compose, path } from 'ramda'
12
import 'file-loader?name=index.html!./index.html'
23
import m4a from 'file-loader!./audio-files/example.m4a'
34
import mp3 from 'file-loader!./audio-files/example.mp3'
45
import ogg from 'file-loader!./audio-files/example.ogg'
56

67
import { connect } from '@podlove/html5-audio-driver'
7-
import { registerConnectActions } from './src/actions'
8+
import { loadButton, playButton, pauseButton, muteButton, unmuteButton, restartButton } from './src/actions'
9+
import { volumeInput, rateInput } from './src/inputs'
10+
import { log } from './src/console'
11+
812

913
const connector = connect.audio()
1014

11-
connector.load([{
15+
const load = () => connector.load([{
1216
url: m4a,
1317
mimeType: 'audio/mp4'
1418
}, {
@@ -19,4 +23,47 @@ connector.load([{
1923
mimeType: 'audio/pgg'
2024
}])
2125

22-
registerConnectActions(connector)
26+
// actions
27+
loadButton.addEventListener('click', () => load())
28+
playButton.addEventListener('click', () => connector.actions.play())
29+
pauseButton.addEventListener('click', () => connector.actions.pause())
30+
muteButton.addEventListener('click', () => connector.actions.mute())
31+
unmuteButton.addEventListener('click', () => connector.actions.unmute())
32+
restartButton.addEventListener('click', compose(() => connector.actions.play(), () => connector.actions.setPlaytime(0), () => connector.actions.pause()))
33+
34+
// inputs
35+
volumeInput.addEventListener('change', compose(connector.actions.setVolume, path(['target', 'value'])))
36+
rateInput.addEventListener('change', compose(connector.actions.setRate, path(['target', 'value'])))
37+
38+
// Props
39+
const renderProps = () => {
40+
const element = document.getElementById('props')
41+
const playerProperties = connector.state
42+
43+
while (element.firstChild) {
44+
element.removeChild(element.firstChild)
45+
}
46+
47+
Object.keys(playerProperties).map(key => {
48+
const propNode = document.createElement('tr')
49+
propNode.innerHTML = `<td>${key}</td><td>${playerProperties[key]}</td>`
50+
element.appendChild(propNode)
51+
})
52+
}
53+
54+
// Events
55+
const onEvent = (event) => compose(renderProps, log(event))
56+
connector.events.onLoaded(onEvent('loaded'))
57+
connector.events.onLoading(onEvent('loading'))
58+
connector.events.onBuffering(onEvent('buffering'))
59+
connector.events.onBufferChange(onEvent('buffer changed'))
60+
connector.events.onPause(onEvent('paused'))
61+
connector.events.onPlay(onEvent('playing'))
62+
connector.events.onPlaytimeUpdate(onEvent('playtime'))
63+
connector.events.onError(onEvent('error'))
64+
connector.events.onEnd(onEvent('end'))
65+
connector.events.onRateChange(onEvent('rate changed'))
66+
connector.events.onDurationChange(onEvent('duration changed'))
67+
connector.events.onVolumeChange(onEvent('volume changed'))
68+
connector.events.onFilterUpdate(onEvent('filter updated'))
69+

example/src/actions.js

Lines changed: 6 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,12 @@ import { compose } from 'ramda'
22
import { actions } from '@podlove/html5-audio-driver'
33

44
// actions
5-
const playButton = document.getElementById('play')
6-
const pauseButton = document.getElementById('pause')
7-
const loadButton = document.getElementById('load')
8-
const restartButton = document.getElementById('restart')
9-
const muteButton = document.getElementById('mute')
10-
const unmuteButton = document.getElementById('unmute')
5+
export const playButton = document.getElementById('play')
6+
export const pauseButton = document.getElementById('pause')
7+
export const loadButton = document.getElementById('load')
8+
export const restartButton = document.getElementById('restart')
9+
export const muteButton = document.getElementById('mute')
10+
export const unmuteButton = document.getElementById('unmute')
1111

1212
export const registerActions = node => {
1313
const mediaActions = actions(node)
@@ -21,12 +21,3 @@ export const registerActions = node => {
2121
unmuteButton.addEventListener('click', mediaActions.unmute)
2222
restartButton.addEventListener('click', compose(mediaActions.play, () => mediaActions.setPlaytime(0), mediaActions.pause))
2323
}
24-
25-
export const registerConnectActions = connector => {
26-
loadButton.addEventListener('click', connector.actions.load)
27-
playButton.addEventListener('click', connector.actions.play)
28-
pauseButton.addEventListener('click', connector.actions.pause)
29-
muteButton.addEventListener('click', connector.actions.mute)
30-
unmuteButton.addEventListener('click', connector.actions.unmute)
31-
restartButton.addEventListener('click', compose(connector.actions.play, () => connector.actions.setPlaytime(0), connector.actions.pause))
32-
}

example/src/inputs.js

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,11 @@
11
import { compose, path } from 'ramda'
22
import { actions } from '@podlove/html5-audio-driver'
33

4-
const volumeInput = document.getElementById('volume')
5-
const rateInput = document.getElementById('rate')
4+
export const volumeInput = document.getElementById('volume')
5+
export const rateInput = document.getElementById('rate')
66

77
export const registerInputs = node => {
88
const mediaActions = actions(node)
99
volumeInput.addEventListener('change', compose(mediaActions.setVolume, path(['target', 'value'])))
1010
rateInput.addEventListener('change', compose(mediaActions.setRate, path(['target', 'value'])))
1111
}
12-

src/actions.js

Lines changed: 2 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -88,21 +88,6 @@ const setPlaytime = media => (time = 0) => {
8888
return media
8989
}
9090

91-
// reset :: MediaElement -> MediaElement
92-
const reset = media => {
93-
if (!media.firstChild) {
94-
return media
95-
}
96-
97-
media.setAttribute('src', null)
98-
while (media.firstChild) {
99-
media.removeChild(media.firstChild)
100-
}
101-
media.removeAttribute('src')
102-
103-
return media
104-
}
105-
10691
const actions = collectProperties({
10792
play,
10893
pause,
@@ -111,8 +96,7 @@ const actions = collectProperties({
11196
mute,
11297
unmute,
11398
setVolume,
114-
setRate,
115-
reset
99+
setRate
116100
})
117101

118102
export {
@@ -124,6 +108,5 @@ export {
124108
unmute,
125109
setVolume,
126110
setRate,
127-
actions,
128-
reset
111+
actions
129112
}

src/connect.js

Lines changed: 85 additions & 76 deletions
Original file line numberDiff line numberDiff line change
@@ -1,90 +1,99 @@
1-
import { compose } from 'ramda'
1+
import { attatchStream } from "./hls";
2+
import { audio as createAudioElement } from "./audio";
23

3-
import { createSourceNodes, mediaNode } from './media'
4-
import { mountNode } from './utils'
5-
import { attatchStream } from './hls'
6-
import { audio as createAudioElement } from './audio'
4+
import { events as mediaEvents } from "./events";
5+
import { actions as mediaActions } from "./actions";
6+
import { props } from "./props"
77

8-
import { events as mediaEvents } from './events'
9-
import { actions as mediaActions } from './actions'
8+
const ACTIONS = [
9+
'play',
10+
'pause',
11+
'load',
12+
'setPlaytime',
13+
'mute',
14+
'unmute',
15+
'setVolume',
16+
'setRate'
17+
]
18+
19+
const EVENTS = [
20+
'onLoading',
21+
'onLoaded',
22+
'onPause',
23+
'onBufferChange',
24+
'onEnd',
25+
'onPlaytimeUpdate',
26+
'onVolumeChange',
27+
'onError',
28+
'onDurationChange',
29+
'onRateChange',
30+
'onPlay',
31+
'onBuffering',
32+
'onReady',
33+
'onFilterUpdate'
34+
]
1035

11-
// type -> MediaFacade
1236
export const audio = () => {
13-
let mediaElement = null
37+
const facade = {
38+
state: {},
39+
load,
40+
mediaElement: null,
41+
actions: ACTIONS.reduce(
42+
(result, action) => ({
43+
...result,
44+
[action]: () => {}
45+
}),
46+
{}
47+
),
48+
events: EVENTS.reduce(
49+
(result, event) => ({
50+
...result,
51+
[event]: handler => recievers[event].push(handler)
52+
}),
53+
{}
54+
)
55+
};
1456

15-
const actions = {
16-
play: () => {},
17-
pause: () => {},
18-
load: () => {},
19-
setPlaytime: () => {},
20-
mute: () => {},
21-
unmute: () => {},
22-
setVolume: () => {},
23-
setRate: () => {},
24-
reset: () => {}
25-
}
57+
const recievers = EVENTS.reduce(
58+
(result, event) => ({
59+
...result,
60+
[event]: [() => facade.state = props(facade.mediaElement)]
61+
}),
62+
{}
63+
);
2664

27-
const recievers = {
28-
onLoading: [],
29-
onLoaded: [],
30-
onPause: [],
31-
onBufferChange: [],
32-
onEnd: [],
33-
onPlaytimeUpdate: [],
34-
onVolumeChange: [],
35-
onError: [],
36-
onDurationChange: [],
37-
onRateChange: [],
38-
onPlay: [],
39-
onBuffering: [],
40-
onReady: [],
41-
onFilterUpdate: []
65+
function load(sources) {
66+
// remove media element
67+
facade.mediaElement && facade.mediaElement.parentNode.removeChild(facade.mediaElement);
68+
facade.actions.play = connect(sources, 'play');
69+
ACTIONS.forEach(action => {
70+
facade.actions[action] = connect(sources, action)
71+
});
4272
}
4373

44-
const registerEvent = eventType => handler => recievers[eventType].push(handler)
74+
function connect(sources, action) {
75+
return (params = []) => {
76+
// create a new media element
77+
facade.mediaElement = createAudioElement(sources);
78+
attatchStream(facade.mediaElement);
4579

46-
const connect = sources => () => {
47-
// create a new media element
48-
mediaElement = createAudioElement(sources)
49-
attatchStream(mediaElement)
80+
// connect the events to existing recievers
81+
const eventEmitters = mediaEvents(facade.mediaElement);
5082

51-
// connect the events to existing recievers
52-
const eventEmitters = mediaEvents(mediaElement)
53-
Object.keys(eventEmitters).forEach(name => recievers[name].forEach(receiver => eventEmitters[name](receiver)))
83+
EVENTS.forEach(name =>
84+
recievers[name].forEach(receiver => eventEmitters[name](receiver))
85+
);
5486

55-
// update actions to new media element
56-
const actionEmitters = mediaActions(mediaElement)
57-
Object.keys(actionEmitters).forEach(name => actions[name] = actionEmitters[name])
87+
// update actions to new media element
88+
const actionEmitters = mediaActions(facade.mediaElement);
89+
ACTIONS.forEach(name =>
90+
facade.actions[name] = actionEmitters[name]
91+
);
5892

59-
// call play
60-
actions.play()
93+
// run the action
94+
action && facade.actions[action].call(null, params);
95+
};
6196
}
6297

63-
const load = sources => {
64-
// remove media element
65-
mediaElement && mediaElement.parentNode.removeChild()
66-
actions.play = connect(sources)
67-
}
68-
69-
return {
70-
mediaElement,
71-
load,
72-
events: {
73-
onLoading: registerEvent('onLoading'),
74-
onLoaded: registerEvent('onLoaded'),
75-
onPause: registerEvent('onPause'),
76-
onBufferChange: registerEvent('onBufferChange'),
77-
onEnd: registerEvent('onEnd'),
78-
onPlaytimeUpdate: registerEvent('onPlaytimeUpdate'),
79-
onVolumeChange: registerEvent('onVolumeChange'),
80-
onError: registerEvent('onError'),
81-
onDurationChange: registerEvent('onDurationChange'),
82-
onRateChange: registerEvent('onRateChange'),
83-
onPlay: registerEvent('onPlay'),
84-
onBuffering: registerEvent('onBuffering'),
85-
onReady: registerEvent('onReady'),
86-
onFilterUpdate: registerEvent('onFilterUpdate')
87-
},
88-
actions
89-
}
90-
}
98+
return facade;
99+
};

src/props.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ const state = compose(state => {
5151

5252
// TODO: make functional
5353
const playing = media =>
54+
media &&
5455
media.currentTime > 0 &&
5556
!media.paused &&
5657
!media.ended &&

0 commit comments

Comments
 (0)