/**
 * @depends AbstractAudioletDevice.js
 */

var AudioDataAPIDevice = new Class({
    Extends: AbstractAudioletDevice,
    initialize: function(audiolet) {
        AbstractAudioletDevice.prototype.initialize.apply(this, [audiolet]);
        this.output = new Audio();
        this.overflow = null;
        this.writePosition = 0;

        this.output.mozSetup(this.numberOfChannels, this.sampleRate);
        
        this.tick.periodical(1000 * this.bufferSize / this.sampleRate, this);
    },

    tick: function() {
        // Check if some data was not written in previous attempts
        var numSamplesWritten;
        if (this.overflow) {
            numSamplesWritten = this.output.mozWriteAudio(this.overflow);
            this.writePosition += numSamplesWritten;
            if (numSamplesWritten < this.overflow.length) {
                // Not all the data was written, saving the tail for writing
                // the next time fillBuffer is called
                this.overflow = this.overflow.subarray(numSamplesWritten);
                return;
            }
            this.overflow = null;
        }

        var outputPosition = this.output.mozCurrentSampleOffset();
        if (outputPosition == 0) {
            // Until the output starts ticking we ignore any samples written
            this.writePosition = 0;
        }
        var samplesNeeded = outputPosition +
                            (this.bufferSize * this.numberOfChannels) -
                            this.writePosition;
        // Seems to help stability - lob some extra samples in if the internal
        // buffer will become almost completely empty before we tick again
        if (samplesNeeded <= 0 && samplesNeeded >= -128) {
            samplesNeeded = this.bufferSize;
        }
        if (samplesNeeded > 0) {
            // Request some sound data from the callback function.
            AudioletNode.prototype.tick.apply(this, [samplesNeeded /
                                                     this.numberOfChannels]);
            this.buffer.interleave();
            var buffer = this.buffer.data;

            // Writing the data.
            numSamplesWritten = this.output.mozWriteAudio(buffer);
            this.writePosition += numSamplesWritten;
            if (numSamplesWritten < buffer.length) {
                // Not all the data was written, saving the tail.
                this.overflow = buffer.subarray(numSamplesWritten);
            }
        }
    },

    getTime: function() {
        return this.output.mozCurrentSampleOffset() / this.numberOfChannels;
    }
});


@@ -422,30 +422,80 @@
 his.
-tick.periodical(1000 *
+started = new Date().valueOf();%0A        this.autoLatency = true;%0A       
  thi
@@ -507,17 +507,17 @@
 ferSize 
-/
+=
  this.sa
@@ -528,47 +528,170 @@
 Rate
-, this);%0A    %7D,%0A%0A    tick: function() %7B
+ * 0.02;%0A        this.interval = this.tick.periodical(10, this);%0A    %7D,%0A%0A    tick: function() %7B%0A        var outputPosition = this.output.mozCurrentSampleOffset();
 %0A   
@@ -1306,75 +1306,24 @@
 var 
-outputPosition = this.output.mozCurrentSampleOffset();%0A        if (
+samplesNeeded = 
 outp
@@ -1337,16 +1337,12 @@
 ion 
-== 0) %7B%0A
++%0A  
     
@@ -1353,183 +1353,217 @@
     
-// Until the output starts ticking we ignore any samples written%0A            this.writePosition = 0;%0A        %7D%0A        var samplesNeeded = outputPosition +%0A                   
+              (this.bufferSize * this.numberOfChannels) -%0A                            this.writePosition;%0A%0A        if (this.autoLatency) %7B%0A            var delta = (new Date().valueOf() - this.started) / 1000;%0A
     
@@ -1559,33 +1559,35 @@
  1000;%0A         
-(
+   
 this.bufferSize 
@@ -1590,81 +1590,65 @@
 ize 
-*
+=
  this.
-numberOfChannels) -%0A                            this.write
+sampleRate * delta;%0A            if (output
 Position
 ;%0A  
@@ -1643,17 +1643,19 @@
 Position
-;
+) %7B
 %0A       
@@ -1659,153 +1659,66 @@
     
-// Seems to help stability - lob some extra samples in if the internal%0A        // buffer will become almost completely empty before we tick again
+        this.autoLatency = false;%0A            %7D%0A        %7D%0A
 %0A   
@@ -1744,37 +1744,32 @@
 ded 
-%3C= 0 && samplesNeeded %3E= -128
+%3E= this.numberOfChannels
 ) %7B%0A
@@ -1784,40 +1784,37 @@
     
-s
+// S
 amples
-N
+ n
 eeded 
-= this.bufferSize;
+per channel
 %0A   
@@ -1822,42 +1822,78 @@
     
-%7D%0A
     
-    if (samplesNeeded %3E 0) %7B
+samplesNeeded = Math.floor(samplesNeeded / this.numberOfChannels);
 %0A   
@@ -2022,18 +2022,17 @@
 esNeeded
- /
+,
 %0A       
@@ -2082,32 +2082,30 @@
    this.
-numberOfChannels
+getWriteTime()
 %5D);%0A    
@@ -2561,16 +2561,24 @@
 %0A    get
+Playback
 Time: fu
@@ -2665,16 +2665,114 @@
 annels;%0A
+    %7D,%0A%0A    getWriteTime: function() %7B%0A        return this.writePosition / this.numberOfChannels;%0A
     %7D%0A%7D)

Replace sketchy AudioDataAPIDevice with magic version based on https://github.com/notmasteryet/audiodata/