refactor audio startup so it will autostart on firefox, if allowed
This commit is contained in:
		@@ -10,9 +10,16 @@ function AudioEngine(maxBufferLength, audioReporter) {
 | 
				
			|||||||
    if (!ctx) {
 | 
					    if (!ctx) {
 | 
				
			||||||
        return;
 | 
					        return;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    this.audioContext = new ctx();
 | 
					
 | 
				
			||||||
    this.allowed = this.audioContext.state === 'running';
 | 
					    this.onStartCallbacks = [];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    this.started = false;
 | 
					    this.started = false;
 | 
				
			||||||
 | 
					    this.audioContext = new ctx();
 | 
				
			||||||
 | 
					    var me = this;
 | 
				
			||||||
 | 
					    this.audioContext.onstatechange = function() {
 | 
				
			||||||
 | 
					        if (me.audioContext.state !== 'running') return;
 | 
				
			||||||
 | 
					        me._start();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    this.audioCodec = new ImaAdpcmCodec();
 | 
					    this.audioCodec = new ImaAdpcmCodec();
 | 
				
			||||||
    this.compression = 'none';
 | 
					    this.compression = 'none';
 | 
				
			||||||
@@ -24,112 +31,130 @@ function AudioEngine(maxBufferLength, audioReporter) {
 | 
				
			|||||||
    this.maxBufferSize = maxBufferLength * this.getSampleRate();
 | 
					    this.maxBufferSize = maxBufferLength * this.getSampleRate();
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
AudioEngine.prototype.start = function(callback) {
 | 
					AudioEngine.prototype.resume = function(){
 | 
				
			||||||
 | 
					    this.audioContext.resume();
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					AudioEngine.prototype._start = function() {
 | 
				
			||||||
    var me = this;
 | 
					    var me = this;
 | 
				
			||||||
    if (me.resamplingFactor === 0) return; //if failed to find a valid resampling factor...
 | 
					
 | 
				
			||||||
 | 
					    // if failed to find a valid resampling factor...
 | 
				
			||||||
 | 
					    if (me.resamplingFactor === 0) {
 | 
				
			||||||
 | 
					         return;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // been started before?
 | 
				
			||||||
    if (me.started) {
 | 
					    if (me.started) {
 | 
				
			||||||
        if (callback) callback(false);
 | 
					 | 
				
			||||||
        return;
 | 
					        return;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    me.audioContext.resume().then(function(){
 | 
					    // are we allowed to play audio?
 | 
				
			||||||
        me.allowed = me.audioContext.state === 'running';
 | 
					    if (!me.isAllowed()) {
 | 
				
			||||||
        if (!me.allowed) {
 | 
					        return;
 | 
				
			||||||
            if (callback) callback(false);
 | 
					    }
 | 
				
			||||||
            return;
 | 
					    me.started = true;
 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        me.started = true;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
        me.gainNode = me.audioContext.createGain();
 | 
					    me.gainNode = me.audioContext.createGain();
 | 
				
			||||||
        me.gainNode.connect(me.audioContext.destination);
 | 
					    me.gainNode.connect(me.audioContext.destination);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if (useAudioWorklets && me.audioContext.audioWorklet) {
 | 
					    if (useAudioWorklets && me.audioContext.audioWorklet) {
 | 
				
			||||||
            me.audioContext.audioWorklet.addModule('static/lib/AudioProcessor.js').then(function(){
 | 
					        me.audioContext.audioWorklet.addModule('static/lib/AudioProcessor.js').then(function(){
 | 
				
			||||||
                me.audioNode = new AudioWorkletNode(me.audioContext, 'openwebrx-audio-processor', {
 | 
					            me.audioNode = new AudioWorkletNode(me.audioContext, 'openwebrx-audio-processor', {
 | 
				
			||||||
                    numberOfInputs: 0,
 | 
					                numberOfInputs: 0,
 | 
				
			||||||
                    numberOfOutputs: 1,
 | 
					                numberOfOutputs: 1,
 | 
				
			||||||
                    outputChannelCount: [1],
 | 
					                outputChannelCount: [1],
 | 
				
			||||||
                    processorOptions: {
 | 
					                processorOptions: {
 | 
				
			||||||
                        maxBufferSize: me.maxBufferSize
 | 
					                    maxBufferSize: me.maxBufferSize
 | 
				
			||||||
                    }
 | 
					                }
 | 
				
			||||||
                });
 | 
					 | 
				
			||||||
                me.audioNode.connect(me.gainNode);
 | 
					 | 
				
			||||||
                me.audioNode.port.addEventListener('message', function(m){
 | 
					 | 
				
			||||||
                    var json = JSON.parse(m.data);
 | 
					 | 
				
			||||||
                    if (typeof(json.buffersize) !== 'undefined') {
 | 
					 | 
				
			||||||
                        me.audioReporter({
 | 
					 | 
				
			||||||
                            buffersize: json.buffersize
 | 
					 | 
				
			||||||
                        });
 | 
					 | 
				
			||||||
                    }
 | 
					 | 
				
			||||||
                    if (typeof(json.samplesProcessed) !== 'undefined') {
 | 
					 | 
				
			||||||
                        me.audioSamples.add(json.samplesProcessed);
 | 
					 | 
				
			||||||
                    }
 | 
					 | 
				
			||||||
                });
 | 
					 | 
				
			||||||
                me.audioNode.port.start();
 | 
					 | 
				
			||||||
                if (callback) callback(true, 'AudioWorklet');
 | 
					 | 
				
			||||||
            });
 | 
					            });
 | 
				
			||||||
        } else {
 | 
					 | 
				
			||||||
            me.audioBuffers = [];
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            if (!AudioBuffer.prototype.copyToChannel) { //Chrome 36 does not have it, Firefox does
 | 
					 | 
				
			||||||
                AudioBuffer.prototype.copyToChannel = function (input, channel) //input is Float32Array
 | 
					 | 
				
			||||||
                {
 | 
					 | 
				
			||||||
                    var cd = this.getChannelData(channel);
 | 
					 | 
				
			||||||
                    for (var i = 0; i < input.length; i++) cd[i] = input[i];
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            var bufferSize;
 | 
					 | 
				
			||||||
            if (me.audioContext.sampleRate < 44100 * 2)
 | 
					 | 
				
			||||||
                bufferSize = 4096;
 | 
					 | 
				
			||||||
            else if (me.audioContext.sampleRate >= 44100 * 2 && me.audioContext.sampleRate < 44100 * 4)
 | 
					 | 
				
			||||||
                bufferSize = 4096 * 2;
 | 
					 | 
				
			||||||
            else if (me.audioContext.sampleRate > 44100 * 4)
 | 
					 | 
				
			||||||
                bufferSize = 4096 * 4;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            function audio_onprocess(e) {
 | 
					 | 
				
			||||||
                var total = 0;
 | 
					 | 
				
			||||||
                var out = new Float32Array(bufferSize);
 | 
					 | 
				
			||||||
                while (me.audioBuffers.length) {
 | 
					 | 
				
			||||||
                    var b = me.audioBuffers.shift();
 | 
					 | 
				
			||||||
                    // not enough space to fit all data, so splice and put back in the queue
 | 
					 | 
				
			||||||
                    if (total + b.length > bufferSize) {
 | 
					 | 
				
			||||||
                        var spaceLeft  = bufferSize - total;
 | 
					 | 
				
			||||||
                        var tokeep = b.subarray(0, spaceLeft);
 | 
					 | 
				
			||||||
                        out.set(tokeep, total);
 | 
					 | 
				
			||||||
                        var tobuffer = b.subarray(spaceLeft, b.length);
 | 
					 | 
				
			||||||
                        me.audioBuffers.unshift(tobuffer);
 | 
					 | 
				
			||||||
                        total += spaceLeft;
 | 
					 | 
				
			||||||
                        break;
 | 
					 | 
				
			||||||
                    } else {
 | 
					 | 
				
			||||||
                        out.set(b, total);
 | 
					 | 
				
			||||||
                        total += b.length;
 | 
					 | 
				
			||||||
                    }
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                e.outputBuffer.copyToChannel(out, 0);
 | 
					 | 
				
			||||||
                me.audioSamples.add(total);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            //on Chrome v36, createJavaScriptNode has been replaced by createScriptProcessor
 | 
					 | 
				
			||||||
            var method = 'createScriptProcessor';
 | 
					 | 
				
			||||||
            if (me.audioContext.createJavaScriptNode) {
 | 
					 | 
				
			||||||
                method = 'createJavaScriptNode';
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            me.audioNode = me.audioContext[method](bufferSize, 0, 1);
 | 
					 | 
				
			||||||
            me.audioNode.onaudioprocess = audio_onprocess;
 | 
					 | 
				
			||||||
            me.audioNode.connect(me.gainNode);
 | 
					            me.audioNode.connect(me.gainNode);
 | 
				
			||||||
            if (callback) callback(true, 'ScriptProcessorNode');
 | 
					            me.audioNode.port.addEventListener('message', function(m){
 | 
				
			||||||
 | 
					                var json = JSON.parse(m.data);
 | 
				
			||||||
 | 
					                if (typeof(json.buffersize) !== 'undefined') {
 | 
				
			||||||
 | 
					                    me.audioReporter({
 | 
				
			||||||
 | 
					                        buffersize: json.buffersize
 | 
				
			||||||
 | 
					                    });
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                if (typeof(json.samplesProcessed) !== 'undefined') {
 | 
				
			||||||
 | 
					                    me.audioSamples.add(json.samplesProcessed);
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            });
 | 
				
			||||||
 | 
					            me.audioNode.port.start();
 | 
				
			||||||
 | 
					            me.workletType = 'AudioWorklet';
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					        me.audioBuffers = [];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (!AudioBuffer.prototype.copyToChannel) { //Chrome 36 does not have it, Firefox does
 | 
				
			||||||
 | 
					            AudioBuffer.prototype.copyToChannel = function (input, channel) //input is Float32Array
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                var cd = this.getChannelData(channel);
 | 
				
			||||||
 | 
					                for (var i = 0; i < input.length; i++) cd[i] = input[i];
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        setInterval(me.reportStats.bind(me), 1000);
 | 
					        var bufferSize;
 | 
				
			||||||
    });
 | 
					        if (me.audioContext.sampleRate < 44100 * 2)
 | 
				
			||||||
 | 
					            bufferSize = 4096;
 | 
				
			||||||
 | 
					        else if (me.audioContext.sampleRate >= 44100 * 2 && me.audioContext.sampleRate < 44100 * 4)
 | 
				
			||||||
 | 
					            bufferSize = 4096 * 2;
 | 
				
			||||||
 | 
					        else if (me.audioContext.sampleRate > 44100 * 4)
 | 
				
			||||||
 | 
					            bufferSize = 4096 * 4;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        function audio_onprocess(e) {
 | 
				
			||||||
 | 
					            var total = 0;
 | 
				
			||||||
 | 
					            var out = new Float32Array(bufferSize);
 | 
				
			||||||
 | 
					            while (me.audioBuffers.length) {
 | 
				
			||||||
 | 
					                var b = me.audioBuffers.shift();
 | 
				
			||||||
 | 
					                // not enough space to fit all data, so splice and put back in the queue
 | 
				
			||||||
 | 
					                if (total + b.length > bufferSize) {
 | 
				
			||||||
 | 
					                    var spaceLeft  = bufferSize - total;
 | 
				
			||||||
 | 
					                    var tokeep = b.subarray(0, spaceLeft);
 | 
				
			||||||
 | 
					                    out.set(tokeep, total);
 | 
				
			||||||
 | 
					                    var tobuffer = b.subarray(spaceLeft, b.length);
 | 
				
			||||||
 | 
					                    me.audioBuffers.unshift(tobuffer);
 | 
				
			||||||
 | 
					                    total += spaceLeft;
 | 
				
			||||||
 | 
					                    break;
 | 
				
			||||||
 | 
					                } else {
 | 
				
			||||||
 | 
					                    out.set(b, total);
 | 
				
			||||||
 | 
					                    total += b.length;
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            e.outputBuffer.copyToChannel(out, 0);
 | 
				
			||||||
 | 
					            me.audioSamples.add(total);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        //on Chrome v36, createJavaScriptNode has been replaced by createScriptProcessor
 | 
				
			||||||
 | 
					        var method = 'createScriptProcessor';
 | 
				
			||||||
 | 
					        if (me.audioContext.createJavaScriptNode) {
 | 
				
			||||||
 | 
					            method = 'createJavaScriptNode';
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        me.audioNode = me.audioContext[method](bufferSize, 0, 1);
 | 
				
			||||||
 | 
					        me.audioNode.onaudioprocess = audio_onprocess;
 | 
				
			||||||
 | 
					        me.audioNode.connect(me.gainNode);
 | 
				
			||||||
 | 
					        me.workletType = 'ScriptProcessorNode';
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    setInterval(me.reportStats.bind(me), 1000);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    var callbacks = this.onStartCallbacks;
 | 
				
			||||||
 | 
					    this.onStartCallbacks = false;
 | 
				
			||||||
 | 
					    callbacks.forEach(function(c) { c(me.workletType); });
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					AudioEngine.prototype.onStart = function(callback) {
 | 
				
			||||||
 | 
					    if (this.onStartCallbacks) {
 | 
				
			||||||
 | 
					        this.onStartCallbacks.push(callback);
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					        callback();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
AudioEngine.prototype.isAllowed = function() {
 | 
					AudioEngine.prototype.isAllowed = function() {
 | 
				
			||||||
    return this.allowed;
 | 
					    return this.audioContext.state === 'running';
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
AudioEngine.prototype.reportStats = function() {
 | 
					AudioEngine.prototype.reportStats = function() {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1099,9 +1099,11 @@ var mute = false;
 | 
				
			|||||||
// Optimalise these if audio lags or is choppy:
 | 
					// Optimalise these if audio lags or is choppy:
 | 
				
			||||||
var audio_buffer_maximal_length_sec = 1; //actual number of samples are calculated from sample rate
 | 
					var audio_buffer_maximal_length_sec = 1; //actual number of samples are calculated from sample rate
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function onAudioStart(success, apiType){
 | 
					function onAudioStart(apiType){
 | 
				
			||||||
    divlog('Web Audio API succesfully initialized, using ' + apiType  + ' API, sample rate: ' + audioEngine.getSampleRate() + " Hz");
 | 
					    divlog('Web Audio API succesfully initialized, using ' + apiType  + ' API, sample rate: ' + audioEngine.getSampleRate() + " Hz");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    hideOverlay();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // canvas_container is set after waterfall_init() has been called. we cannot initialize before.
 | 
					    // canvas_container is set after waterfall_init() has been called. we cannot initialize before.
 | 
				
			||||||
    //if (canvas_container) synchronize_demodulator_init();
 | 
					    //if (canvas_container) synchronize_demodulator_init();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -1320,11 +1322,12 @@ var audioEngine;
 | 
				
			|||||||
function openwebrx_init() {
 | 
					function openwebrx_init() {
 | 
				
			||||||
    audioEngine = new AudioEngine(audio_buffer_maximal_length_sec, audioReporter);
 | 
					    audioEngine = new AudioEngine(audio_buffer_maximal_length_sec, audioReporter);
 | 
				
			||||||
    $overlay = $('#openwebrx-autoplay-overlay');
 | 
					    $overlay = $('#openwebrx-autoplay-overlay');
 | 
				
			||||||
    $overlay.on('click', playButtonClick);
 | 
					    $overlay.on('click', function(){
 | 
				
			||||||
 | 
					        audioEngine.resume();
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					    audioEngine.onStart(onAudioStart);
 | 
				
			||||||
    if (!audioEngine.isAllowed()) {
 | 
					    if (!audioEngine.isAllowed()) {
 | 
				
			||||||
        $overlay.show();
 | 
					        $overlay.show();
 | 
				
			||||||
    } else {
 | 
					 | 
				
			||||||
        audioEngine.start(onAudioStart);
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    fft_codec = new ImaAdpcmCodec();
 | 
					    fft_codec = new ImaAdpcmCodec();
 | 
				
			||||||
    initProgressBars();
 | 
					    initProgressBars();
 | 
				
			||||||
@@ -1370,9 +1373,7 @@ function update_dmr_timeslot_filtering() {
 | 
				
			|||||||
    $('#openwebrx-panel-receiver').demodulatorPanel().getDemodulator().setDmrFilter(filter);
 | 
					    $('#openwebrx-panel-receiver').demodulatorPanel().getDemodulator().setDmrFilter(filter);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function playButtonClick() {
 | 
					function hideOverlay() {
 | 
				
			||||||
    //On iOS, we can only start audio from a click or touch event.
 | 
					 | 
				
			||||||
    audioEngine.start(onAudioStart);
 | 
					 | 
				
			||||||
    var $overlay = $('#openwebrx-autoplay-overlay');
 | 
					    var $overlay = $('#openwebrx-autoplay-overlay');
 | 
				
			||||||
    $overlay.css('opacity', 0);
 | 
					    $overlay.css('opacity', 0);
 | 
				
			||||||
    $overlay.on('transitionend', function() {
 | 
					    $overlay.on('transitionend', function() {
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user