Für eine Anwendung, die man in der Lage macht diejenigen Tinnitusfrequenz zu bestimmen, erscheint das folgende Szenario:Web Audio API: Klicks auf das Volumenänderung mit Schieber
Ein Benutzer ‚mousedown-‘ ein html5 Schieber -> ein Oszillator in einem bestimmten Frequenz wird gestartet. Durch Verschieben des Griffs kann der Benutzer die Lautstärke der Note ändern.
Hier ist der (CoffeeScript-) Code, der für die gesamte Audio-Verarbeitung verantwortlich ist:
# 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(), um die 'mouseDown-' Schieber Rückruf und Tuner.changeVolume(), um die verbundenen Schieberegler 'Eingang' Rückruf.
Das Problem ist wie folgt:
Jedes Mal, wenn der Schieber bewegt wird, tritt ein Klicken. Ich vermute, der Grund dafür ist, dass jedes Mal, wenn der Schieberegler die "Eingabe" auslöst, die Rampe, die derzeit von der Tuner.changeVolume-Methode gesteuert wird, von einer anderen Rampe überlagert wird.
Ich habe viel experimentiert (keine Zeitplanung, cancelScheduledValues, ...) und die obige Version lässt nur einen letzten Klick zurück, wenn der Slider anfänglich für verschiedene Browser auf dem Mac verschoben wird. Aber für Browser auf einem Windows-Rechner erhöht sich das Klicken deutlich.
(Btw. Es scheint nicht möglich zu sein, das @ gainNode.gain.value auszulesen, wenn eine Rampe gefahren wird.)
Jede Idee, wie dieses Problem umgehen? Ich wäre dankbar für jeden Hinweis ...