简体   繁体   中英

How do you get the regular <audio> features when using AudioContext?

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):

非功能性HTML音频元素

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:

  1. including the <source> tag in the <audio> , ie changing it to this:

     <div class="audio"> <audio controls loop> <source src="#{url}"></source> </audio> </div> 
  2. adding source.connect(pitchShift) before pitchShift.connect(audio_context.destination) .

  3. 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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM