Web Audio API:使用滑块单击音量更改

时间:2016-06-29 09:43:59

标签: javascript html5 audio coffeescript web-audio-api

对于能够确定耳鸣频率的应用程序,会出现以下情况:

用户“mousedown”html5滑块 - >启动一定频率的振荡器。通过移动手柄,用户可以更改音符的音量。

以下是负责整个音频处理的(CoffeeScript-)代码:

# CoffeScript for Tuner element
# Enables playing notes via Web Audio API

class @Tuner
  constructor: () ->
    @playing = false
    @whobbling = false
    @stopping = false
    @atVolumeChange = false
    @atFadeIn = false
    @activeBtn = undefined

    if typeof AudioContext isnt "undefined"
      @audioCtx ?= new AudioContext()
    else if typeof webkitAudioContext isnt "undefined"
      @audioCtx = new webkitAudioContext()
    else
      console.log "Browser hat keine WebAudioAPI"

    # make sure listener is set correct:
    listener = @audioCtx.listener
    listener.setOrientation(0,0,-1,0,1,0)
    listener.setPosition(0,0,0)

    # Create PannerNode and set initially to L and R
    @pannerNode = @audioCtx.createPanner()
    @pannerNode.setOrientation(0,0,0)
    @pannerNode.setPosition(0,0,1)
    @pannerNode.connect( @audioCtx.destination )

    # Create GainNode and set to 0.0
    @gainNode = @audioCtx.createGain()
    @gainNode.gain.value = 0.0
    @gainNode.connect @pannerNode
    @actualFreq = 500
    @currentVolume = 0.0

  start: (volume, elem_id) ->
    if @playing or @stopping
      return

    @playing = true
    @currentVolume = volume
    @oscillator = @audioCtx.createOscillator()
    @oscillator.type = "sine"
    @oscillator.frequency.value = @actualFreq
    @oscillator.connect @gainNode

    # if whobbling is active, create LFO & LFOGain for effect
    if @whobbling
      lfo = @audioCtx.createOscillator()
      lfo.type = "sine"
      lfo.frequency.value = 5

      whobGain = @actualFreq * 5.0 / 100.0
      lfo_gain = @audioCtx.createGain()
      lfo_gain.gain.value = whobGain

      lfo.connect(lfo_gain)
      lfo_gain.connect(@oscillator.frequency)
      lfo.start(0)

    # starting process
    @activeBtn = elem_id
    matchingGUI.highlightPlay( @activeBtn ) if @activeBtn
    now = @audioCtx.currentTime
    @atFadeIn = true
    @gainNode.gain.setValueAtTime 0.0, now + 0.01
    @oscillator.start(now + 0.02)
    @gainNode.gain.setValueAtTime 0.0, now + 0.03
    @gainNode.gain.linearRampToValueAtTime volume, now + 0.2
    that = @
    setTimeout ->
      that.atFadeIn = false
    , 200

  stop: () ->
    if @playing and not @stopping
      @stopping = true
      now = @audioCtx.currentTime
      now += 0.3 if @atVolumeChange
      now += 0.3 if @atFadeIn
      @gainNode.gain.setValueAtTime @currentVolume, now + 0.01
      @gainNode.gain.linearRampToValueAtTime 0.0, now + 0.45
      @oscillator.stop( now + 0.5 )
      matchingGUI.highlightStop(@activeBtn) if @activeBtn
      that = @
      setTimeout ->
        that.stopping = false
        that.playing = false
        that.whobbling = false
      , 750

  setFrequency: (newFreq) ->
    @actualFreq = newFreq
    @oscillator.frequency.value = newFreq if @playing and not @stopping

  changeVolume: (newVolume) ->
    newVolumeDb = @linearToDb newVolume
    if not @stopping
      @atVolumeChange = true
      now = @audioCtx.currentTime
      @currentVolume = newVolumeDb
      @gainNode.gain.exponentialRampToValueAtTime @currentVolume, now + 0.333
      that = @
      setTimeout ->
        that.atVolumeChange = false
      , 300

  setWhobbling: () ->
    @whobbling = true

  setPanToLR: () ->
    @pannerNode.setPosition(0,0,1) unless @playing

  setPanToL: () ->
    @pannerNode.setPosition(-3,0,0) unless @playing

  setPanToR: () ->
    @pannerNode.setPosition(3,0,0) unless @playing

  play: (frequency, volume,  whob, elem_id) ->
    whob ?= false
    @setFrequency frequency
    @setWhobbling() if whob
    volumeDb = @linearToDb( volume )
    @start( volumeDb, elem_id )

  linearToDb: (s) ->
    dbStart = 90
    s = s * dbStart - dbStart
    return Math.pow 10, s/20

# === End Class Tuner ===
#
# export Tuner
root = exports ? window
root.Tuner = Tuner

Tuner.play()连接到滑块'mousedown'回调和Tuner.changeVolume()到滑块'输入'回调。

问题如下:

每次移动滑块时,都会发生单击。我想这是因为每次滑块触发'输入'时,当前由Tuner.changeVolume方法驱动的斜坡被另一个斜坡覆盖。

我实验了很多(没有调度,cancelScheduledValues,...),上面的版本只留下最后一次点击,最初为Mac上的各种浏览器移动滑块。但是对于Windows机器上的浏览器,点击会显着增加。

(顺便说一句。当驱动斜坡时,似乎无法读出@ gainNode.gain.value。)

知道如何处理这个问题吗? 我会感激任何提示...

1 个答案:

答案 0 :(得分:0)

可能是您的振荡器正在重启。不太熟悉CoffeeScript(需要更多咖啡...)所以不能直接解决问题,但如果它是一个点击并拖动滑动事件,也许浏览器不正确地发送带有mousemove事件的多个mousedowns。

相反,为什么不让振荡器始终运行但增益节点为0,实际上没有输出。然后,当用户越过增益斜坡并跟踪用户输入时。一般来说,使用斜坡是一种好习惯,但是如果你用鼠标移动触发音量变化,大多数用户都不会足够快地移动滑动器以便你进行“拉伸”。效果我想你想避免。