<commit_before>/**
 * @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;
    }
});
<commit_msg>Replace sketchy AudioDataAPIDevice with magic version based on https://github.com/notmasteryet/audiodata/<commit_after>/**
 * @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.started = new Date().valueOf();
        this.autoLatency = true;
        this.bufferSize = this.sampleRate * 0.02;
        this.interval = this.tick.periodical(10, this);
    },

    tick: function() {
        var outputPosition = this.output.mozCurrentSampleOffset();
        // 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 samplesNeeded = outputPosition +
                            (this.bufferSize * this.numberOfChannels) -
                            this.writePosition;

        if (this.autoLatency) {
            var delta = (new Date().valueOf() - this.started) / 1000;
            this.bufferSize = this.sampleRate * delta;
            if (outputPosition) {
                this.autoLatency = false;
            }
        }

        if (samplesNeeded >= this.numberOfChannels) {
            // Samples needed per channel
            samplesNeeded = Math.floor(samplesNeeded / this.numberOfChannels);
            // Request some sound data from the callback function.
            AudioletNode.prototype.tick.apply(this, [samplesNeeded,
                                                     this.getWriteTime()]);
            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);
            }
        }
    },

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

    getWriteTime: function() {
        return this.writePosition / this.numberOfChannels;
    }
});

{'fraction_non_alphanumeric': 0.08982252806954003, 'fraction_numerical': 0.00398406374501992, 'mean_word_length': 1.8439752832131824, 'pattern_counts': {'":': 0, '<': 8, '<?xml version=': 0, '>': 6, 'https://': 1, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 4, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5501197', 'n_tokens_mistral': 1427, 'n_tokens_neox': 1341, 'n_words': 334}