betajs/betajs-media-components

View on GitHub
src/dynamics/_common/audio_visualization.js

Summary

Maintainability
D
1 day
Test Coverage
Scoped.define("module:AudioVisualization", [
    "base:Class",
    "base:Maths",
    "browser:Dom",
    "browser:Info"
], function(Class, Maths, Dom, Info, scoped) {
    return Class.extend({
        scoped: scoped
    }, function(inherited) {
        return {
            /**
             * @param stream  // Audio stream
             * @param {object} options // additional options like height and active element
             */
            constructor: function(stream, options) {
                inherited.constructor.call(this);
                this.stream = stream;
                this.recorder = null;
                this.theme = options.theme;
                if (options.recorder) {
                    this.recorder = options.recorder;
                } else {
                    // JSLint not allow for shortcuts new(window.AudioContext || window.webkitAudioContext)
                    var AudioContext = window.AudioContext || window.webkitAudioContext;
                    this.audioContext = new AudioContext();
                }
                this.createVisualizationCanvas(options.height, options.element);
                this.frameID = null;
            },

            createVisualizationCanvas: function(height, element) {
                var _height, _containerElement;
                _height = height || 120;
                _containerElement = (element.firstElementChild || element.firstChild);
                _containerElement.style.minHeight = _height + 'px';
                this.canvas = _containerElement.querySelector('canvas');
                this.canvas.style.display = 'block';
                this.canvas.width = parseFloat(window.getComputedStyle(_containerElement).width);
                this.canvas.height = _height;
                this.canvasContext = this.canvas.getContext("2d");
            },

            _clearCanvas: function() {
                this.canvasContext.clearRect(0, 0, this.canvas.width, this.canvas.height);
            },

            initializeVisualEffect: function() {
                try {
                    var _source;
                    if (this.recorder) {
                        this._analyser = this.recorder._analyser;
                        this.analyser = this._analyser._analyserNode;
                        this.audioContext = this._analyser._audioContext;
                        this.analyser.fftSize = 256;
                        //_source = this.audioContext.createMediaStreamSource(this.stream)
                    }

                    if (this.audioContext || this.stream) {
                        if (this.audioContext.state === 'suspended') {
                            Dom.userInteraction(function() {
                                this.audioContext.resume();
                            }, this);
                        }

                        if (this.stream instanceof HTMLElement) {
                            _source = this.audioContext.createMediaElementSource(this.stream);
                            this.analyser = this.audioContext.createAnalyser();
                            _source.connect(this.analyser);
                            this.analyser.fftSize = 256;
                            this.analyser.connect(this.audioContext.destination);
                        }

                        this.bufferLength = this.analyser.frequencyBinCount;
                        // this.dataArray = new Uint8Array(this.analyser.fftSize);
                        this.dataArray = new Uint8Array(this.analyser.frequencyBinCount);
                        // this.dataArray = new Float32Array(this.analyser.fftSize);
                        this.canvasWidth = this.canvas.width;
                        this.canvasHeight = this.canvas.height;
                        this.barWidth = (this.canvasWidth / this.bufferLength) * 2.5;
                        this.barHeight = 0;
                        this.x = 0;
                        //this.renderFrame = this._renderFrame;
                        // If requestAnimationFrame is missing
                        if (!window.requestAnimationFrame) {
                            window.requestAnimationFrame = (function() {
                                return window.webkitRequestAnimationFrame ||
                                    window.mozRequestAnimationFrame ||
                                    window.oRequestAnimationFrame ||
                                    window.msRequestAnimationFrame ||
                                    function( /* function FrameRequestCallback */ callback, /* DOMElement Element */ element) {
                                        window.setTimeout(callback, 1000 / 60);
                                    };
                            })();
                            window.cancelAnimationFrame = window.cancelAnimationFrame ||
                                window.mozCancelAnimationFrame ||
                                function(requestID) {
                                    clearTimeout(requestID);
                                }; //fall back
                        }
                    } else {
                        console.warn('Seems there is limitation by browser to create AudioContext instance');
                    }
                } catch (e) {
                    //this.set('visualeffectsupported', false);
                    console.warn('Web Audio API not supported', e);
                }
            },

            start: function() {
                this._renderFrame();
            },

            pause: function() {
                this._cancelFrame();
            },

            stop: function() {
                this._cancelFrame();
                this._clearCanvas();
            },

            destroy: function() {
                if (this.canvas) this.canvas.remove();
                inherited.destroy.call(this);
            },

            _renderFrame: function() {
                this.frameID = requestAnimationFrame(function() {
                    this._renderFrame();
                }.bind(this));
                this.analyser.getByteFrequencyData(this.dataArray);
                // this.dataArray = new Float32Array( this.analyser.fftSize);
                // this.analyser.getFloatTimeDomainData(this.dataArray);
                // this._drawRedBars();
                switch (this.theme) {
                    case "red-bars":
                        this._drawRedBars();
                        break;
                    default:
                        this._drawBigBalloon();
                        break;
                }
            },

            _cancelFrame: function() {
                cancelAnimationFrame(this.frameID);
            },

            updateSourceStream: function() {
                this._cancelFrame();
                this.initializeVisualEffect();
                // this._analyser = new AudioAnalyser(this._recorder.stream());
                // this._analyser.destroy();
            },

            _drawRedBars: function() {
                this.x = 0;
                this.canvasContext.fillStyle = "#000";
                this.canvasContext.fillRect(0, 0, this.canvasWidth, this.canvasHeight);
                for (var i = 0; i < this.bufferLength; i++) {
                    this.barHeight = this.dataArray[i] / 2;
                    var r = this.barHeight + (25 * (i / this.bufferLength));
                    var g = 250 * (i / this.bufferLength);
                    var b = 50;
                    this.canvasContext.fillStyle = "rgb(" + r + "," + g + "," + b + ")";
                    this.canvasContext.fillRect(this.x, this.canvasHeight - this.barHeight, this.barWidth, this.barHeight);
                    this.x += this.barWidth + 1;
                }
            },

            _drawBigBalloon: function() {
                this.__canvasBackground(235);
                this.canvasContext.fillStyle = "#000";
                var s = this.__getRMS();
                this.canvasContext.fillStyle = this.__rgb(s * 2);
                this.__HfillEllipse(this.canvasWidth / 2, this.canvasHeight / 2, s * 5, s * 5);
            },

            __getRMS: function() {
                var rms = 0;
                for (var i = 0; i < this.bufferLength; i++) {
                    rms += this.dataArray[i] * this.dataArray[i];
                }
                rms /= this.dataArray.length;
                rms = Math.sqrt(rms);
                return rms;
            },

            __canvasBackground: function(r, g, b, a) {
                if (typeof g === 'undefined') {
                    this.canvasContext.fillStyle = this.__rgb(r, r, r);
                } else if (typeof b === 'undefined' && typeof a === 'undefined') {
                    this.canvasContext.fillStyle = rgba(r, r, r, g);
                } else if (typeof a === 'undefined') {
                    this.canvasContext.fillStyle = this.__rgb(r, g, b);
                } else {
                    this.canvasContext.fillStyle = this.__rgba(r, g, b, a);
                }
                this.canvasContext.fillRect(0, 0, this.canvasWidth, this.canvasHeight);
            },

            __HfillEllipse: function(x, y, width, height) {
                if (typeof height === 'undefined') height = width;
                this.__Hellipse(x, y, width, height);
                this.canvasContext.fill();
                this.canvasContext.beginPath();
            },

            __Hellipse: function(x, y, width, height) {
                'use strict';
                if (typeof height === 'undefined') height = width;
                this.canvasContext.beginPath();
                for (var i = 0; i < Math.PI * 2; i += Math.PI / 64) {
                    this.canvasContext.lineTo(x + (Math.cos(i) * width / 2), y + (Math.sin(i) * height / 2));
                }
                this.canvasContext.closePath();
            },

            __rgb: function(r, g, b) {

                if (typeof g === 'undefined') g = r;
                if (typeof b === 'undefined') b = r;
                return 'rgb(' + Maths.clamp(Math.round(r), 0, 255) + ', ' + Maths.clamp(Math.round(g), 0, 255) + ', ' + Maths.clamp(Math.round(b), 0, 255) + ')';

            },

            __rgba: function(r, g, b, a) {
                if (typeof g === 'undefined') {
                    return 'rgb(' + Maths.clamp(Math.round(r), 0, 255) + ', ' + Maths.clamp(Math.round(r), 0, 255) + ', ' + Maths.clamp(Math.round(r), 0, 255) + ')';
                } else if (typeof b === 'undefined') {
                    return 'rgba(' + Maths.clamp(Math.round(r), 0, 255) + ', ' + Maths.clamp(Math.round(r), 0, 255) + ', ' + Maths.clamp(Math.round(r), 0, 255) + ', ' + Maths.clamp(g, 0, 1) + ')';
                } else if (typeof a === 'undefined') {
                    return 'rgba(' + Maths.clamp(Math.round(r), 0, 255) + ', ' + Maths.clamp(Math.round(g), 0, 255) + ', ' + Maths.clamp(Math.round(b), 0, 255) + ', 1)';
                } else {
                    return 'rgba(' + Maths.clamp(Math.round(r), 0, 255) + ', ' + Maths.clamp(Math.round(g), 0, 255) + ', ' + Maths.clamp(Math.round(b), 0, 255) + ', ' + Maths.clamp(a, 0, 1) + ')';
                }
            }
        };
    }, {
        supported: function() {
            return !!(window.AudioContext || window.webkitAudioContext);
        }
    });
});