javascript - 网络音频 API : Clicks on volume change with slider

标签 javascript html audio coffeescript web-audio-api

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

用户“mousedown”一个 html5 slider -> 启动某个频率的振荡器。通过移动 handle ,用户可以改变音符的音量。

这是负责整个音频处理的 (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() 连接到 slider “mousedown”回调,Tuner.changeVolume() 连接到 slider “输入”回调。

问题如下:

每次移动 slider 时,都会发生一次点击。我猜这是因为每次 slider 触发“输入”时,当前由 Tuner.changeVolume 方法驱动的斜坡被另一个斜坡覆盖。

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

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

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

最佳答案

可能是您的振荡器正在重新启动。对 CoffeeScript 不太熟悉(需要更多咖啡...),因此无法直接解决问题,但如果它是单击和拖动滑动事件,则浏览器可能不正确地使用 mousemove 事件发送多个鼠标按下。

相反,为什么不让振荡器始终运行,但增益节点为 0,实际上没有输出。然后当用户越过增益斜坡并跟踪用户输入时。通常使用斜坡是一种很好的做法,但是如果您使用 mousemove 触发音量变化,大多数用户不会快速移动 slider 以使您获得我认为您想要避免的“压缩”效果。

关于javascript - 网络音频 API : Clicks on volume change with slider,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/38095909/

相关文章:

javascript - 改变 slimscroll 的位置

html - 第一个动态添加的组件没有从类中获取样式

android - 设备播放的声音比代码中的声音大

javascript - 网络摄像头流媒体

javascript - 检测浏览器是否可以显示 pdf 内联

html - 第二个选择元素未显示在浏览器上

html - IE 11无法在音频标签中播放OGG文件

linux - WAV 文件缺少音频和录音

javascript - 仅隐藏数据表的这一部分(从 x 个总条目中过滤)

javascript - 在 JavaScript 应用程序中将输入的文本作为 JavaScript 运行?