Last active
October 19, 2018 10:51
-
-
Save WJWang/588eaa15dd32ffff86fe6a193eb4a1b0 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<template lang="html"> | |
<div v-if="processing"> | |
<button disabled>play</button> | |
<input type="range" v-model="volume" /> | |
</div> | |
<div v-else> | |
playing at : {{ playTime }} / {{ songDuration }} secs<br> | |
Position: <input type="text" v-model="position" /> % | |
<br> | |
<button @click="play" v-if="!isPlaying">play</button> | |
<button @click="pause" v-else>pause</button> | |
<button @click="replay">replay</button> | |
Volume: <input type="range" v-model="volume" /> | |
</div> | |
</template> | |
<script> | |
const fetchMediaData = uri => new Promise((resolve, reject) => { | |
const request = new XMLHttpRequest(); | |
request.open('GET', uri, true); | |
request.responseType = 'arraybuffer'; | |
function onLoad() { | |
resolve(request.response); | |
} | |
function onError(e) { | |
reject(e); | |
} | |
request.onload = onLoad; | |
request.onerror = onError; | |
try { | |
request.send(); | |
} catch (e) { | |
reject(e); | |
} | |
}); | |
const AudioCtx = window.AudioContext || window.webkitAudioContext; | |
export default { | |
props: { | |
url: { type: String, required: true, default: '' }, | |
vol: { type: Number, default: 50 }, | |
pos: { type: Number, default: 0 }, | |
}, | |
data() { | |
return { | |
startOffset: 0, | |
startTime: 0, | |
playTime: 0, | |
timer: null, | |
isPlaying: false, | |
position: this.pos, | |
volume: this.vol, | |
songCtx: null, | |
songGainNode: null, | |
songBuffer: null, | |
song: null, | |
processing: false, | |
errorText: '', | |
}; | |
}, | |
methods: { | |
volumeAdjust() { | |
if (this.vol <= 100 && this.volume >= 0) { | |
this.songGainNode.gain.value = this.volume / 100; | |
} | |
if (this.volume > 100) { | |
this.volume = 100; | |
this.songGainNode.gain.value = 1; | |
} | |
if (this.volume < 0) { | |
this.volume = 0; | |
this.songGainNode.gain.value = 0; | |
} | |
}, | |
startPlayAtPos() { | |
this.stop(); | |
this.startOffset = ((this.position) / 100) * this.songDuration; | |
this.isPlaying = false; | |
this.play(); | |
}, | |
replay() { | |
this.stop(); | |
this.startOffset = 0; | |
this.isPlaying = false; | |
this.play(); | |
}, | |
stop() { | |
this.song.stop(); | |
clearInterval(this.timer); | |
}, | |
play() { | |
this.songCtx = new AudioCtx(); | |
this.startTime = this.songCtx.currentTime; | |
this.song = this.songCtx.createBufferSource(); | |
this.song.loop = false; | |
this.song.buffer = this.songBuffer; | |
this.songGainNode = this.songCtx.createGain(); | |
this.song.connect(this.songGainNode); | |
this.songGainNode.connect(this.songCtx.destination); | |
this.volumeAdjust(); | |
this.song.start(0, this.startOffset % this.songDuration); | |
this.startTimer(); | |
this.isPlaying = true; | |
}, | |
pause() { | |
this.stop(); | |
this.startOffset += this.songCtx.currentTime - this.startTime; | |
this.isPlaying = false; | |
}, | |
async fetchSong() { | |
this.processing = true; | |
try { | |
const audioData = await fetchMediaData(this.url); | |
const setting = { | |
channels: 2, | |
frameCounts: 44100 * (60 * 5), // 5 mins as default | |
sampleRate: 44100, | |
}; | |
const audioCtx = new AudioCtx(); | |
audioCtx.decodeAudioData(audioData, async (buffer) => { | |
const frameCounts = (buffer.duration) ? (buffer.duration * 44100) : setting.frameCounts; | |
const offlineCtx = new OfflineAudioContext(setting.channels, frameCounts, setting.sampleRate); | |
const source = offlineCtx.createBufferSource(); | |
source.buffer = buffer; | |
source.connect(offlineCtx.destination); | |
source.start(); | |
try { | |
const renderedBuffer = await offlineCtx.startRendering(); | |
this.songBuffer = renderedBuffer; | |
this.processing = false; | |
console.log('Rendering completed successfully'); | |
} catch (e) { | |
console.error(e); | |
this.processing = false; | |
this.errorText = 'Rendering failed'; | |
} | |
}); | |
} catch (e) { | |
this.processing = false; | |
this.errorText = 'Media file error occurs.'; | |
} | |
}, | |
startTimer() { | |
this.timer = setInterval(() => { | |
this.playTime = (this.startOffset + (this.songCtx.currentTime - this.startTime)) % this.songDuration; | |
}, 0); | |
} | |
}, | |
computed: { | |
songDuration() { | |
if (this.songBuffer) { | |
return this.songBuffer.duration; | |
} | |
return 0; | |
}, | |
}, | |
created() { | |
this.fetchSong(); | |
}, | |
watch: { | |
position() { | |
this.startPlayAtPos(); | |
}, | |
volume() { | |
this.volumeAdjust(); | |
} | |
}, | |
} | |
</script> | |
<style lang="sass" scoped> | |
</style> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment