2016-06-29 13 views
0

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

Antwort

0

Es kann sein, dass Ihre Oszillatoren neu gestartet werden. Nicht gut mit CoffeeScript vertraut (brauche mehr Kaffee ...), kann also das Problem nicht direkt ausmerzen, aber wenn es ein Klick- und Drag-Schiebeereignis ist, sendet ein Browser möglicherweise mehrere Mausdrains mit dem mousemove-Ereignis.

Stattdessen warum nicht immer die Oszillatoren laufen aber der Gain-Knoten bei 0, effektiv keine Ausgabe. Dann, wenn der Benutzer die Verstärkungsrampen überschreitet und die Benutzereingabe verfolgt. Im Allgemeinen ist es eine gute Übung, Rampen zu verwenden, aber wenn Sie die Lautstärkeänderung mit Mousemove auslösen, bewegen die meisten Benutzer die Schieberegler nicht schnell genug, um den "Zipping" -Effekt zu erhalten, den Sie vermeiden möchten.