Skip to content
This repository has been archived by the owner on May 21, 2019. It is now read-only.

Commit

Permalink
Play back audio recorded from microphone #12
Browse files Browse the repository at this point in the history
  • Loading branch information
brianchirls committed Mar 8, 2017
1 parent cd5447d commit 8c3d221
Show file tree
Hide file tree
Showing 4 changed files with 116 additions and 41 deletions.
82 changes: 64 additions & 18 deletions src/js/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -274,6 +274,8 @@ const puppetShow = new PuppetShow({
audioContext
});

const recordedSounds = new Map();
const currentSounds = new Set();
const sfx = new Map();
[
'sounds/bark.wav',
Expand Down Expand Up @@ -449,6 +451,34 @@ function stopEditing() {
updateEditingState();
}

function playSoundEvent(event) {
if (!puppetShow.playing) {
// don't play sounds while we're paused
return true;
}

// todo: add to list of currently active audio tracks so we can resume

const name = event.params.name;
const time = event.time;
const duration = event.duration;
const currentTime = puppetShow.currentTime;

const timeLeft = Math.max(0, time + duration - currentTime);
if (timeLeft > 0) {
const effect = sfx.get(name) || recordedSounds.get(name);
if (!effect) {
console.warn('Unknown sound effect', name, event);
return false;
}

effect.play(Math.max(0, currentTime - time));
return true;
}

return false;
}

puppetShow
.on('load', () => {
console.log('loaded puppet show', puppetShow.id);
Expand All @@ -467,10 +497,37 @@ puppetShow
console.log('error loading puppet show', id);
// todo: clear stage, force redraw and report error
})
.on('play', updateButtons)
.on('pause', updateButtons)
.on('ready', updateButtons)
.on('unready', updateButtons)
.on('play', () => {
currentSounds.forEach(soundEvent => {
const isCurrent = playSoundEvent(soundEvent);
if (!isCurrent) {
currentSounds.delete(soundEvent);
}
});
updateButtons();
})
.on('pause', () => {
recordedSounds.forEach(e => e.stop());
sfx.forEach(e => e.stop());
updateButtons();
})
.on('ready', () => {
puppetShow.audioAssets.forEach(asset => {
const effect = new SoundEffect({
src: asset.buffer,
context: audioContext,
name: asset.id
});
recordedSounds.set(asset.id, effect);
});
updateButtons();
})
.on('unready', () => {
currentSounds.clear();
recordedSounds.forEach(s => s.stop());
recordedSounds.clear();
updateButtons();
})
.on('event', event => {
if (puppetShowRecorder && puppetShowRecorder.recording) {
return;
Expand All @@ -494,20 +551,9 @@ puppetShow
}

if (event.type === 'sound') {
const name = event.params.name;
const time = event.time;
const duration = event.duration;
const currentTime = puppetShow.currentTime;

const timeLeft = Math.max(0, time + duration - currentTime);
if (timeLeft > 0) {
const effect = sfx.get(name);
if (!effect) {
console.warn('Unknown sound effect', name, event);
return;
}

effect.play(Math.max(0, currentTime - time));
const isCurrent = playSoundEvent(event);
if (isCurrent) {
currentSounds.add(event);
}
return;
}
Expand Down
4 changes: 3 additions & 1 deletion src/js/puppet-show-recorder.js
Original file line number Diff line number Diff line change
Expand Up @@ -188,10 +188,12 @@ function PuppetShowRecorder(options) {
endTime = now();
audioRecorder.stop();

const duration = (endTime - startTime) / 1000;

// todo: save audio asset to puppetShow if not being cleared?
audioRecorder.exportWAV(blob => {
// todo: add time when we allow appending
puppetShow.addAudio(blob);
puppetShow.addAudio(blob, duration);
});

this.emit('stop');
Expand Down
27 changes: 21 additions & 6 deletions src/js/puppet-show.js
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,7 @@ function PuppetShow(options) {
// playback state
let playStartTime = 0;
let playEndTime = 0;
let playing = 0;
let playing = false;
let lastUpdateTime = 0;
let playEventIndex = 0;
const currentEventsByType = new Map();
Expand Down Expand Up @@ -336,6 +336,11 @@ function PuppetShow(options) {
duration = 0;
assetsToLoad = 0;

if (ready) {
ready = false;
this.emit('unready');
}

// erasing all media assets and events from server
showRef.child('duration').set(0);
showRef.child('events').remove();
Expand Down Expand Up @@ -373,15 +378,20 @@ function PuppetShow(options) {
event.duration = dur;
}

const isLastEvent = !events.length || events[events.length - 1].time <= event.time;
events.push(event);
showRef.child('events').push(event);

if (!isLastEvent) {
events.sort(sortEvents);
}

duration = Math.max(duration, time + (dur || 0));
showRef.child('duration').set(duration);
showRef.child('modifyTime').set(ServerValue.TIMESTAMP);
};

this.addAudio = (encodedBlob, time) => {
this.addAudio = (encodedBlob, dur, time) => {
if (!loaded) {
// todo: either wait to finish loading or throw error
return;
Expand Down Expand Up @@ -424,7 +434,6 @@ function PuppetShow(options) {
audioContext.decodeAudioData(fileReader.result).then(decodedBuffer => {

audioObject.buffer = decodedBuffer;
// todo: set up audio source or whatever
console.log('decoded audio');

duration = Math.max(duration, audioObject.time + decodedBuffer.duration);
Expand All @@ -450,8 +459,11 @@ function PuppetShow(options) {
console.log('saved audio file', id, snapshot);
});

// showRef.child('modifyTime').set(ServerValue.TIMESTAMP);
// todo: add to list of events
// add to list of events
this.addEvent('sound', {
name: id
}, null, dur, time);
showRef.child('modifyTime').set(ServerValue.TIMESTAMP);

if (wasReady) {
this.emit('unready');
Expand Down Expand Up @@ -503,7 +515,7 @@ function PuppetShow(options) {
// find any current events of each type/index and fire an event
// we assume the event handler will take care of ending the event
const currentTime = this.currentTime;
let index = currentTime >= lastUpdateTime ? playEventIndex : 0;
let index = currentTime > lastUpdateTime ? playEventIndex : 0;

currentEventsByType.forEach(map => map.clear());

Expand Down Expand Up @@ -567,6 +579,9 @@ function PuppetShow(options) {
events: {
value: events
},
audioAssets: {
value: audioAssets
},

playing: {
get: () => playing
Expand Down
44 changes: 28 additions & 16 deletions src/js/sound-effect.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
'use strict';

import eventEmitter from 'event-emitter';

function SoundEffect(options) {
const src = options.src;
const context = options.context;
Expand All @@ -8,23 +10,33 @@ function SoundEffect(options) {

const sources = [];

eventEmitter(this);

// load audio file
// todo: load appropriate format based on browser support
const xhr = new XMLHttpRequest();
xhr.responseType = 'arraybuffer';
xhr.onload = () => {
context.decodeAudioData(xhr.response, decodedBuffer => {
buffer = decodedBuffer;
this.duration = buffer.duration;
console.log('loaded buffer', src, buffer);
});
};
xhr.onerror = e => {
// keep trying
console.warn('Error loading audio', src, e);
};
xhr.open('GET', src, true);
xhr.send();
if (options.src instanceof window.AudioBuffer) {
buffer = options.src;
this.duration = buffer.duration;
setTimeout(() => this.emit('load'), 0);
} else if (src && typeof src === 'string') {
// todo: load appropriate format based on browser support
const xhr = new XMLHttpRequest();
xhr.responseType = 'arraybuffer';
xhr.onload = () => {
context.decodeAudioData(xhr.response, decodedBuffer => {
buffer = decodedBuffer;
this.duration = buffer.duration;
console.log('loaded buffer', src, buffer);
});
};
xhr.onerror = e => {
// keep trying
console.warn('Error loading audio', src, e);
};
xhr.open('GET', src, true);
xhr.send();
} else {
throw new Error('SoundEffect: missing src option');
}

let stopSource;
function stopEvent(evt) {
Expand Down

0 comments on commit 8c3d221

Please sign in to comment.