The following code works as a way to load a sound from a remote url and loop it with some pitch shifting. Even though I attach the AudioContext to the <audio>
node and it's playing sounds, I have none of the UI features like play/pause, seeking, volume, download, etc.
Screenshot (the sound is playing, but none of the HTML5 <audio>
features work):
I have this almost-empty HTML:
<div id="audios"></div>
Then I have a function to add an <audio>
element:
$ -> Mixer.$audios = $("#audios")
Mixer.add_audio = (url) ->
$audio_box = $ """
<div class='audio'>
<audio controls loop></audio>
</div>
"""
Mixer.$audios.append($audio_box)
audio = $audio_box.find("audio")[0]
Mixer.source_with_pitch_shift(audio, url).then (args) ->
[audio_context, source, buffer_src] = args
Mixer.start_playing(buffer_src)
Mixer.start_playing = (buffer_src) ->
buffer_src.loop = true
buffer_src.start()
The Mixer.source_with_pitch_shift
creates an AudioContext
from the buffer source (the audio file requested over HTTP). It also creates a PitchShift node (a GainNode
under the hood) attached to that context. Finally, I'm creating a MediaElementAudioSourceNode
connecting the audio context and the DOM <audio>
, but it seems to have no effect and I don't know what I'm supposed to do with it.
Mixer.source_with_pitch_shift = (audio, url) ->
Mixer.build_audio_context(url).then (args) ->
[audio_context, buffer_src] = args
# Not sure what to do with this:
source = audio_context.createMediaElementSource(audio)
pitchShift = PitchShift(audio_context)
pitchShift.transpose = 12
pitchShift.connect(audio_context.destination)
Promise.resolve([audio_context, source, buffer_src])
Mixer.build_audio_context = (url) ->
audioSrc = url
audio_context = new AudioContext()
Mixer.bufferSound(audio_context, audioSrc).then (buffer) ->
g = audio_context.createGain()
g.gain.value = 5
g.connect(audio_context.destination)
bq = audio_context.createBiquadFilter()
bq.detune.value = 0
setInterval ->
bq.detune.value += 200
, 1000
bq.connect(g)
buffer_src = audio_context.createBufferSource()
buffer_src.buffer = buffer
buffer_src.connect(bq)
Promise.resolve([audio_context, buffer_src])
Mixer.bufferSound = (ctx, url) ->
new Promise (resolve, reject) ->
req = new XMLHttpRequest()
req.open('GET', url, true)
req.responseType = 'arraybuffer'
req.onload = ->
ctx.decodeAudioData(req.response, resolve, reject)
req.send()
Calling it (this is a public url and the page ends up looping this 1-second sample and raising the pitch every loop):
Mixer.add_audio "https://mixer-music.s3.amazonaws.com/tPEE9ZwTmy0.mp3"
Can I get the standard UI functionality? Or is there a way I can re-implement it with a AudioBufferSourceNode
?
It turned out there were 3 things I needed to fix:
including the <source>
tag in the <audio>
, ie changing it to this:
<div class="audio"> <audio controls loop> <source src="#{url}"></source> </audio> </div>
adding source.connect(pitchShift)
before pitchShift.connect(audio_context.destination)
.
Running audio.crossOrigin = "anonymous"
on the audio element
Also, since I added the <source>
tag it's not necessary to load the song over HTTP. So I removed that.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.