if (typeof Object.create !== 'function') {
    Object.create = function (obj) {
        function F() { };
        F.prototype = obj;
        return new F();
    };
}


(function ($, window, document, undefined) {

    var FileManager = {

        target: null,

        handleFileData : null,

        handleDragOver: function (self) {
            //evt.stopPropagation();  // Do not allow the dragover event to bubble.
            //evt.preventDefault(); // Prevent default dragover event behavior.
        },

        handleFileSelection: function (self, files) {
            //evt.stopPropagation(); // Do not allow the drop event to bubble.
            //evt.preventDefault(); // Prevent default drop event behavior.

            //var files = evt.dataTransfer.files; // Grab the list of files dragged to the drop box.

            if (!files) {
                alert("<p>At least one selected file is invalid - do not select any folders.</p><p>Please reselect and try again.</p>");
                return;
            }

            // "files" is a FileList of file objects. Try to display the contents of first only file:
            var file = files[0];
            if (!file) {
                alert("Unable to access " + file.name);
                return;
            }
            if (file.size == 0) {
                alert("Skipping " + file.name.toUpperCase() + " because it is empty.");
                return;
            }

            // Create FileReader object and prepare the callbacks
            var reader = new FileReader();
            // Set up asynchronous handlers for file-read-success, file-read-abort, and file-read-errors:
            reader.onloadend = function (evt) {
                // "onloadend" fires when the file contents have been successfully loaded into memory.
                //self.OnFileLoadEnd.call(evt.target.result);
                //self.audioVisualizer.handleFileData(evt.target.result);
                self.handleFileData(evt.target.result);
            } 
            reader.abort = self.handleFileReadAbort; // "abort" files on abort.
            reader.onerror = self.handleFileReadError; // "onerror" fires if something goes awry.

            if (file) { // Safety first.
                reader.readAsArrayBuffer(file); // Asynchronously start a file read thread. Other supported read methods include readAsArrayBuffer() and readAsDataURL().
            }
        },

        handleFileReadAbort: function (evt) {
            alert("File read aborted.");
        },

        handleFileReadError: function (evt) {
            var message;
            switch (evt.target.error.name) {
                case "NotFoundError":
                    alert("The file could not be found at the time the read was processed.");
                    break;
                case "SecurityError":
                    message = "<p>A file security error occured. This can be due to:</p>";
                    message += "<ul><li>Accessing certain files deemed unsafe for Web applications.</li>";
                    message += "<li>Performing too many read calls on file resources.</li>";
                    message += "<li>The file has changed on disk since the user selected it.</li></ul>";
                    alert(message);
                    break;
                case "NotReadableError":
                    alert("The file cannot be read. This can occur if the file is open in another application.");
                    break;
                case "EncodingError":
                    alert("The length of the data URL for the file is too long.");
                    break;
                default:
                    alert("File error code " + evt.target.error.name);
            } // switch
        },

        init: function (target, _handleFileData) {
            var self = this;
            var message;
            if (!window.FileReader) {
                message = '<p>The ' +
                    '<a href="http://dev.w3.org/2006/webapi/FileAPI/" target="_blank">File API</a>s ' +
                    'are not fully supported by this browser.</p>' +
                    '<p>Upgrade your browser to the latest version.</p>';
                $('body').innerHTML = message;
                throw message;
            }

            self.target = target;
            //self.audioVisualizer = audioVis;
            self.handleFileData = _handleFileData;

            //self.options = $.extend({}, self.options, options);

            self.target.addEventListener('dragover', function (evt) {
                evt.stopPropagation(); // Do not allow the drop event to bubble.
                evt.preventDefault(); // Prevent default drop event behavior.

                self.handleDragOver(self);
            }, false);

            self.target.addEventListener('drop', function (evt) {
                evt.stopPropagation(); // Do not allow the drop event to bubble.
                evt.preventDefault(); // Prevent default drop event behavior.

                self.handleFileSelection(self, evt.dataTransfer.files);
            }, false);
        },

    };


    var AudioVisualizer = {
        options:null,
        source:null,
        channel:0,
        canvas:null,
        canvasCtx: null,
        CANVAS_HEIGHT:0,
        CANVAS_WIDTH:0,
        ZOOM_LEVEL:0,
        sliceSize:0,
        curPos: 0,   
        fileManager:null,
        audioCtx:null,
        analyser:null,
        elem:null,
        $elem:null,
        $rangeSeek: null,
        $spanValueSeek: null,
        $rangeZoom: null,
        $spanValueZoom: null,
        $playButton:null,
        $stopButton: null,
        audioBuffer: null,
        sampling: 0,
        $cbSampling: null,
        $ddlSamplingDown: null,
        $imgSpinner:null,
        sampledAudioBuffer: null,
        isPlaying: false,
        $info: null,
        $canvasEQ:null,
        dataArrayAlt: null,
        swipeStart:0,
        $cbShowSamples: null,
        showSamples:false,
        handleSuccess: null,



        init: function (_options, elem) {
            var self = this;
            self.elem = elem;
            self.$elem = $(elem);
            self.options = $.extend({}, $.fn.audioVisualizer.options, _options);
            //if (self.options.source == null) {
            //    throw ("[Error] Source not defined!");
            //}

            self.handleSucces = self.options.handleSuccess;

            self.audioCtx = new (window.AudioContext || window.webkitAudioContext)();
            self.analyser = self.audioCtx.createAnalyser();
            self.analyser.fftSize = 1024;
            if (/Mobi/.test(navigator.userAgent)) {
                // mobile!
                self.analyser.fftSize = 512;
            }
            self.dataArrayAlt = new Uint8Array(self.analyser.frequencyBinCount);


            self.ZOOM_LEVEL = self.options.zoomLevel;
            self.sliceSize = self.options.sliceSize;
            self.curPos = 0;

            self.canvas = elem;
            self.canvasCtx = self.canvas.getContext('2d');
            self.CANVAS_HEIGHT = self.canvas.height;
            self.CANVAS_WIDTH = self.canvas.width;

            self.fileManager = Object.create(FileManager);
            self.fileManager.init(elem, function (fileData) {
                self.handleFileData(fileData);
            });


            //$elem.addClass("fileDropBox");
            self.canvasCtx.clearRect(0, 0, self.CANVAS_WIDTH, self.CANVAS_HEIGHT);
            self.canvasCtx.font = "20px Comic Sans MS";
            self.canvasCtx.fillStyle = "MidnightBlue";
            self.canvasCtx.textAlign = "center";
            self.canvasCtx.fillText("Σύρετε εδώ ένα αρχείο μουσικής", self.canvas.width / 2, self.canvas.height/2); 
        },

        handleFileData: function (fileData) {
            //Decode audio data
            var self = this;
            // Check if GUI exists 
            if (self.$playButton != null){
                self.toggleGUI(false);
            }
            $("#imgSpinner").removeClass("hidden");
            self.audioCtx.decodeAudioData(fileData, function (buffer) {
                $("#imgSpinner").addClass("hidden");
                self.handleAudioData(buffer);
            },
            function (e) {
                alert("Error with decoding audio data" + e.error);
            });
        },

        handleAudioData: function (buffer) {
            var self = this;


            $(self.canvas).css("border", "0px solid white")

            self.initSource(buffer);

            self.createControls();

            self.toggleGUI(true);

            self.showInfo(self.audioBuffer);

            self.calculateSlice();

            self.$cbSampling.iCheck("uncheck");
            
            self.drawSlice(self.channel, self.sampling);

            var canvasEQCtx = self.$canvasEQ[0].getContext('2d');
            var WIDTH = self.$canvasEQ[0].width;
            var HEIGHT = self.$canvasEQ[0].height;
            var bufferLengthAlt = self.analyser.frequencyBinCount;
            console.log(bufferLengthAlt);
            canvasEQCtx.clearRect(0, 0, WIDTH, HEIGHT);

            var drawAlt = function () {

                drawVisual = requestAnimationFrame(drawAlt);

                self.analyser.getByteFrequencyData(self.dataArrayAlt);


                var barWidth = (WIDTH / bufferLengthAlt) * 2.5;
                var barHeight;
                var x = 0;
                canvasEQCtx.fillStyle = 'rgb(243, 243, 243)';
                canvasEQCtx.fillRect(0, 0, WIDTH, HEIGHT);
                for (var i = 0; i < bufferLengthAlt; i++) {
                    barHeight = self.dataArrayAlt[i];

                    canvasEQCtx.fillStyle = 'rgb(' + (barHeight + 100) + ',50,50)';
                    //canvasEQCtx.fillStyle = 'rgb(50,50,' + (barHeight + 100) + ')';
                    canvasEQCtx.fillRect(x, HEIGHT - barHeight / 2, barWidth, barHeight / 2);

                    x += barWidth + 1;
                }
            }

            drawAlt();
            
            //call success calback
            self.handleSucces();
        },

        toggleGUI: function (bEnabled) {
            var self = this;
            if (bEnabled) {
                self.$imgSpinner.addClass("hidden");
                self.$ddlSamplingDown.removeAttr("disabled");
                self.$playButton.removeAttr("disabled");
                self.$stopButton.removeAttr("disabled");
                self.$cbSampling.iCheck("enable");
                self.$cbShowSamples.iCheck("enable");
            } else {
                self.$imgSpinner.removeClass("hidden");
                self.$ddlSamplingDown.attr("disabled", "disabled")
                self.$playButton.attr("disabled", "disabled")
                self.$stopButton.attr("disabled", "disabled")
                self.$cbSampling.iCheck("disable");
                self.$cbShowSamples.iCheck("disable");
            }
        },

        msToTime: function (duration) {
            var milliseconds = parseInt((duration % 1000) / 100)
                , seconds = parseInt((duration / 1000) % 60)
                , minutes = parseInt((duration / (1000 * 60)) % 60)
                , hours = parseInt((duration / (1000 * 60 * 60)) % 24);

            hours = (hours < 10) ? "0" + hours : hours;
            minutes = (minutes < 10) ? "0" + minutes : minutes;
            seconds = (seconds < 10) ? "0" + seconds : seconds;

            return hours + ":" + minutes + ":" + seconds + "." + milliseconds;
        },

        showInfo: function (buffer) {
            var self = this;
            self.$info.html("");

            var totalSeconds = buffer.duration;
            var totalMS = Math.floor(totalSeconds * 1000) + 1;

            self.$info.append("Διάρκεια: <b>" + self.msToTime(totalMS) + "</b>");
            self.$info.append(" - Κανάλια: <b>" + self.audioBuffer.numberOfChannels + "</b>");
            if (self.$cbSampling[0].checked) {
                self.$info.append(" - Δειγματοληψία: <b style='color:red'>" + buffer.sampleRate + "</b> δείγματα/δευτερόλεπτο");
            } else {
                self.$info.append(" - Δειγματοληψία: <b>" + buffer.sampleRate + "</b> δείγματα/δευτερόλεπτο");
            }
        },

        createControls: function () {
            var self = this;


            $(".controls").remove();

            var $ul = $("<ul class='controls'></ul");
            //$ul.insertAfter($(self.canvas));
            $ul.insertAfter($(".canvasContainer"));

            if (self.$playButton == null) {
                self.$playButton = $("<button class='nav-button'>Play</button>");
            }
            self.$playButton.off("click");
            self.$playButton.on("click", function () {
                if (self.$cbSampling[0].checked) {
                    self.playSampledSound(self.sampledAudioBuffer);
                }
                else {
                    self.playSampledSound(self.audioBuffer);
                }
            })

            if (self.$stopButton == null) {
                self.$stopButton = $("<button class='nav-button'>Stop</button>");
            }
            self.$stopButton.off("click");
            self.$stopButton.on("click", function () {
                self.stopSound();
            })

            // Reduce sampling checkbox
            if (self.$cbSampling == null) {
                self.$cbSampling = $('<input type="checkbox" id="cbSampling">');
            }

            // ShowSamples checkbox
            if (self.$cbShowSamples == null) {
                self.$cbShowSamples = $('<input type="checkbox" id="cbShowSamples">');
            }

            // Reduce sampling dropdown list
            if (self.$ddlSamplingDown == null) {
                self.$ddlSamplingDown = $('<select id="ddlDownSampling" disabled></select>');
                self.$ddlSamplingDown.append('<option value="2">στο μισό</option>')
                    .append('<option value="4">στο ένα τέταρτο</option>')
                    .append('<option value="8">στο ένα όγδοο</option>')
                    .append('<option value="16">στο ένα δέκατοέκτο</option>');
                //.append('<option value="0">καθόλου</option>')

                self.$ddlSamplingDown.hide();
            }

            if (self.$imgSpinner == null) {
                self.$imgSpinner = $('<img src="images/spinner.gif" class="spinner hidden" />');
            }

            $ul.append($("<li></li>")
                .append(self.$playButton)
                .append(self.$stopButton)
                .append(self.$cbShowSamples)
                .append('<label for="cbShowSamples">Εμφάνιση δειγμάτων ήχου</label>'));

            $ul.append($("<li></li>")
                .append(self.$cbSampling)
                .append('<label for="cbSampling">Μείωση Δειγματοληψίας</label>')
                .append(self.$imgSpinner)
                .append(self.$ddlSamplingDown));

            self.$ddlSamplingDown.change(function () {
                self.stopSound();
                self.sampling = Math.floor(this.value);
                self.createSampledSource(self.audioBuffer, self.sampling);
            });

            self.$cbSampling.iCheck({
                checkboxClass: 'icheckbox_square-grey',
                radioClass: 'iradio_square-grey',
                increaseArea: '20%' // optional
            })
            .on('ifToggled', function (event) {
                self.stopSound();

                if (this.checked) {
                    self.$ddlSamplingDown.show();
                    self.$ddlSamplingDown.change();
                } else {
                    self.sampling = 0;
                    self.$ddlSamplingDown.hide();
                    self.showInfo(self.audioBuffer);
                    self.drawSlice(self.channel, self.sampling);
                }
                });

            self.$cbShowSamples.iCheck({
                checkboxClass: 'icheckbox_square-grey',
                radioClass: 'iradio_square-grey',
                increaseArea: '20%' // optional
            })
            .on('ifToggled', function (event) {
                self.showSamples = this.checked;
                self.drawSlice(self.channel, self.sampling);
            });


            if (self.$info == null) {
                self.$info = $("<div></div>");
            }
            $ul.append($("<li></li>").append(self.$info));

            if (self.$canvasEQ == null) {
                self.$canvasEQ = $('<canvas id="canvasEQ" class="canvasEQ" width="900" height="100"></canvas>');
            }
            $ul.append($("<li></li>").append(self.$canvasEQ));

            var mousePressed = false;
            var endSpeed = 30;   // End pressed speed 30ms

            // Vertical slider
            $(".scrollVertical").remove();
            var $zoomWrapper = $("<div class='scrollVertical container'></div>")
            $zoomWrapper.insertAfter($(self.canvas));

            var keepZooming = function (step, speed, stepFactor) {
                if (mousePressed) {
                    if (stepFactor > 1)
                        stepFactor--;
                    var newValue = Math.floor(self.$rangeZoom.val()) + (step / stepFactor);
                    console.log("Keep zooming at ", newValue);
                    //console.log("speed is ", speed);
                    self.$rangeZoom.val(newValue).change();
                    if (speed > endSpeed)
                        speed = speed / 2;
                    window.setTimeout(function () {
                        keepZooming(step, speed, stepFactor);
                    }, speed)
                }
            }
            //Remove old zoom buttons
            $(".buttonScroll").remove();
            //add button ZoomIn
            var $btnZoomIn = $("<button class='buttonScroll buttonZoomIn'>+</button>");
            $zoomWrapper.append($btnZoomIn);
            $btnZoomIn.mousedown(function (e) {
                var step = Math.floor(self.$rangeZoom.attr("step"));
                mousePressed = true;
                keepZooming(step, 200, 3);
            });
            // add zoom slider
            if (self.$rangeZoom == null) {
                self.$rangeZoom = $('<input class="zoom-control" type="range" data-orientation="vertical" min="0" max="20" step="1" value="0">');
                //HTML5 oninput event
                self.$rangeZoom[0].oninput = function () {
                    self.ZOOM_LEVEL = Math.floor(this.value);
                    self.calculateSlice();
                    self.$rangeSeek.attr("max", Math.floor(self.channel.length - self.sliceSize));
                    self.$rangeSeek.attr("step", Math.floor(self.sliceSize / 100) + 1);
                    $('.seek-control').rangeslider('update', true);
                    //self.$rangeSeek.val(self.curPos).change();
                    self.drawSlice(self.channel, self.sampling);
                };
            }
            $zoomWrapper.append(self.$rangeZoom);

            //add button ZoomOut
            var $btnZoomOut = $("<button class='buttonScroll buttonZoomOut'>-</button>");
            $zoomWrapper.append($btnZoomOut);
            $btnZoomOut.mousedown(function (e) {
                var step = Math.floor(self.$rangeZoom.attr("step"));
                mousePressed = true;
                keepZooming(-step, 200, 3);
            });

            var keepSeeking = function (step, speed, stepFactor) {
                if (mousePressed) {
                    if (stepFactor > 1)
                        stepFactor--;
                    var newValue = Math.floor(self.$rangeSeek.val()) + (step / stepFactor);
                    console.log("Keep seeking at ", newValue);
                    //console.log("speed is ", speed);
                    self.$rangeSeek.val(newValue).change();
                    if (speed > endSpeed)
                        speed = speed / 2;
                    window.setTimeout(function () {
                        keepSeeking(step, speed, stepFactor);
                    }, speed)
                }
            }

            // Horizontal slider
            $(".scrollHorizontal").remove();
            var $seekWrapper = $("<div class='scrollHorizontal container'></div>")
            $seekWrapper.insertAfter($zoomWrapper);

            var $btnSeekLeft = $("<button class='buttonScroll buttonSeekLeft'>&lt;</button>");
            $seekWrapper.append($btnSeekLeft);
            $btnSeekLeft.mousedown(function (e) {
                var step = Math.floor(self.$rangeSeek.attr("step"));
                mousePressed = true;
                keepSeeking(-step, 200, 3);
            });
            // Seek horizontal slider
            if (self.$rangeSeek == null) {
                self.$rangeSeek = $('<input class="seek-control" type="range" min="0" max="20" step="5" value="0">');
                //HTML5 oninput event
                self.$rangeSeek[0].oninput = function () {
                    self.curPos = Math.floor(this.value);
                    self.drawSlice(self.channel, self.sampling);
                };
            }
            $seekWrapper.append(self.$rangeSeek);

            //add button seekRight
            var $btnSeekRight = $("<button class='buttonScroll buttonSeekRight'>&gt;</button>");
            $seekWrapper.append($btnSeekRight);
            $btnSeekRight.mousedown(function (e) {
                var step = Math.floor(self.$rangeSeek.attr("step"));
                mousePressed = true;
                keepSeeking(step, 200, 3);
            });

            $(document).off("mouseup");
            $(document).mouseup(function (e) {
                mousePressed = false;
            });


            //Swipe right or left
            $(self.canvas).swipe({
                swipeStatus: function (event, phase, direction, distance, duration, fingers, fingerData, currentDirection) {
                    var newValue = 0;
                    var max = Math.floor(self.$rangeSeek.attr("max"));
                    //var str = "<h4>Swipe Phase : " + phase + "<br/>";
                    //str += "Current direction: " + currentDirection + "<br/>";
                    //str += "Direction from inital touch: " + direction + "<br/>";
                    //str += "Distance from inital touch: " + distance + "<br/>";
                    //str += "Duration of swipe: " + duration + "<br/>";
                    //str += "Fingers used: " + fingers + "<br/></h4>";
                    //Here we can check the:
                    //phase : 'start', 'move', 'end', 'cancel'
                    //direction : 'left', 'right', 'up', 'down'
                    //distance : Distance finger is from initial touch point in px
                    //duration : Length of swipe in MS
                    //fingerCount : the number of fingers used
                    if (phase == 'start') {
                        self.swipeStart = Math.floor(self.$rangeSeek.val());
                        $(this).addClass("cursorMove");
                    }
                    if (phase == 'end' || phase == 'cancel') {
                        self.swipeStart = 0;
                        $(this).removeClass("cursorMove");
                    }
                    if (phase == 'move') {
                        if (direction == 'left') {
                            newValue = self.swipeStart + distance;
                            if (newValue > max)
                                newValue = max;
                            //console.log("Swipe newValue:" + newValue);
                            self.$rangeSeek.val(newValue).change();
                        } else
                            if (direction == 'right') {
                                newValue = self.swipeStart - distance;
                                if (newValue < 0)
                                    newValue = 0;
                               //console.log("Swipe newValue:" + newValue);
                               self.$rangeSeek.val(newValue).change();
                            }
                    }
                    //$("#test").html(str);
                },
                threshold: 200,
                maxTimeThreshold: 5000,
                fingers: 'all'
            });

            // Convert input type=range to rangeSlider.js
            self.$rangeSeek.rangeslider({
                polyfill: false,
                //// Callback function
                //onSlide: function (position, value) {
                //    //lensSize = value;
                //    //changeLensSize(value);
                //},
            });

            self.$rangeZoom.rangeslider({
                polyfill: false,
                //// Callback function
                //onSlide: function (position, value) {
                //    //lensSize = value;
                //    //changeLensSize(value);
                //},
            });

            self.$rangeSeek.attr("max", Math.floor(self.channel.length - self.sliceSize));
            self.$rangeSeek.attr("step", Math.floor(self.sliceSize / 400) + 1);
            self.$rangeSeek.val(self.curPos).change();
            self.$rangeSeek.rangeslider('update', true);

            self.$rangeZoom.attr("max", self.MAXIMUM_ZOOM_LEVEL);
            self.$rangeZoom.attr("step", Math.floor(self.MAXIMUM_ZOOM_LEVEL / 50) + 1);
            self.$rangeZoom.val(self.ZOOM_LEVEL);

            //remove handle
            var $rangeV = $(".rangeslider--vertical");
            //$(".rangeslider__handle", $rangeV).remove();
            $rangeV.addClass("middleVertical");
            self.$rangeZoom.rangeslider('update', true);
            //remove handle
            var $rangeH = $(".rangeslider--horizontal");
            //$(".rangeslider__handle", $rangeH).remove();
            $rangeH.addClass("middleHorizontal");
            self.$rangeSeek.rangeslider('update', true);

        },

        createSampledSource: function (audioBuffer, sampling) {
            var self = this;
            if (window.Worker) {

                //self.$imgSpinner.removeClass("hidden");
                //self.$ddlSamplingDown.attr("disabled", "disabled")
                self.toggleGUI(false);

                var sourceChannel = audioBuffer.getChannelData(0);
                var sourceBuffer = new ArrayBuffer(Float32Array.BYTES_PER_ELEMENT*sourceChannel.length); //  32-bit float
                //var arr32 = new Float32Array(sourceBuffer, 0, sourceChannel.length);// Associate the buffer with the data
                var arr32 = new Float32Array(sourceBuffer);// Associate the buffer with the data
                arr32.set(sourceChannel);       // copy data from sourceChannel

                var sampledWorker = new Worker(URL.createObjectURL(new Blob(["("+worker_function.toString()+")()"], {type: 'text/javascript'})));
                //var sampledWorker = new Worker("js/SamplingWorkerFX.js");
                sampledWorker.postMessage({
                    sourceBuffer: sourceBuffer,
                    sampling: self.sampling,
                    sampleRate:self.source.buffer.sampleRate
                }, [sourceBuffer]);

                console.log('Message posted to worker');
                sampledWorker.onmessage = function (e) {
                    console.log('Message received from worker');
                    var targetBuffer = e.data;
                    //var targetChannelLength = Math.floor(targetBuffer.byteLength / Float32Array.BYTES_PER_ELEMENT);
                    //var targetChannel = new Float32Array(targetBuffer, 0, targetChannelLength);
                    var targetChannel = new Float32Array(targetBuffer);
                    var targetSampleRate = audioBuffer.sampleRate / self.sampling;
                    var targetChannelLength = targetChannel.length;
                    var myArrayBuffer;
                    try {
                        myArrayBuffer = self.audioCtx.createBuffer(1, targetChannelLength, targetSampleRate);
                        myArrayBuffer.copyToChannel(targetChannel, 0, 0);

                        self.sampledAudioBuffer = myArrayBuffer;

                        //self.$imgSpinner.addClass("hidden");
                        //self.$ddlSamplingDown.removeAttr("disabled");
                        self.toggleGUI(true);

                        self.showInfo(self.sampledAudioBuffer);


                        //self.playSampledSound(self.sampledAudioBuffer);

                    } catch (e) {
                        alert("Δεν είναι δυνατή η αναπαραγωγή του ήχου");
                        console.log(e.message);
                        self.toggleGUI(true);
                    }
                    sampledWorker.terminate();
                    sampledWorker = null;
                    self.drawSlice(self.channel, self.sampling);
                };
            }
        },

        initSource: function (buffer) {
            var self = this;
            // if (self.source != null) {
            //     try {
            //         self.source.stop();
            //     } catch (e) {
            //         console.log(e.message);
            //     }
            // }
            
            self.stopSound();

            self.sampledAudioBuffer = null;

            self.audioBuffer = buffer;

            self.source = self.audioCtx.createBufferSource();

            //var myBuffer = buffer;
            //var songLength = buffer.duration;

            self.source.buffer = buffer;
            //source.playbackRate.value = playbackControl.value;
            //source.connect(audioCtx.destination);
            //self.source.loop = true;
            var sourceChannel = self.source.buffer.getChannelData(0);
            var arr32 = new Float32Array(sourceChannel.length);
            arr32.set(sourceChannel);       // copy data from sourceChannel
            self.channel = arr32;

            self.isPlaying = false;



            self.MAXIMUM_ZOOM_LEVEL = Math.floor(self.channel.length / (self.CANVAS_WIDTH / 16));
            self.curPos = 0;
            self.ZOOM_LEVEL = 1;

            self.calculateSlice();

            // Show whole waveform
            //self.sliceSize = self.channel.length;

            self.source.connect(self.analyser);
            self.analyser.connect(self.audioCtx.destination);
        },

        calculateSlice: function () {
            var self = this;
            self.sliceSize = Math.floor(self.channel.length / self.ZOOM_LEVEL);
            if (self.sliceSize > self.channel.length)
                self.sliceSize = self.channel.length;
        },

        playSound: function () {
            var self = this;
            if (self.source != null) {

                //self.initSource(self.audioBuffer);
                //self.source.start(0);
                self.playSampledSound(self.source.buffer);
            }
        },

        playSampledSound: function (buffer) {
            var self = this;
            if (buffer != null && self.isPlaying == false) {

                // Get an AudioBufferSourceNode.
                // This is the AudioNode to use when we want to play an AudioBuffer
                self.source = self.audioCtx.createBufferSource();

                // set the buffer in the AudioBufferSourceNode
                self.source.buffer = buffer;

                // connect the AudioBufferSourceNode to the
                // destination so we can hear the sound
                //sampledSource.connect(self.audioCtx.destination);
                self.source.connect(self.analyser);
                self.analyser.connect(self.audioCtx.destination);

                self.source.onended = function() {
                    self.isPlaying = false;
                    console.log("Sound stopped");
                }
                // start the source playing
                self.source.start(0);
                self.isPlaying = true;
            }
        },

        stopSound: function () {
            var self = this;
            if (self.source != null) {
                if (self.isPlaying) {
                    console.log("Stopping sound");
                    self.source.stop();
                }
            }
            self.isPlaying = false;
            //console.log(self.audioCtx.currentTime)
        },

        drawSlice: function (floatData, sampling) {
            var dataLength;
            var self = this;

            var rightPos = self.curPos + self.sliceSize;
            if (rightPos > self.channel.length)
                rightPos = self.channel.length;

            dataLength = rightPos - self.curPos;

            //canvasCtx.fillStyle = 'rgb(200, 200, 200)';
            self.canvasCtx.fillStyle = 'rgb(83, 117, 179)';
            self.canvasCtx.fillRect(0, 0, self.CANVAS_WIDTH, self. CANVAS_HEIGHT);

            // Waveform
            self.canvasCtx.lineWidth = 2;
            self.canvasCtx.strokeStyle = self.options.strokeColor;
            self.canvasCtx.beginPath();
            self.canvasCtx.moveTo(0, self.CANVAS_HEIGHT / 2);
            // Maximum 8000
            var increment = Math.floor(dataLength / 8000) + 1;
            //var sliceWidth = CANVAS_WIDTH * ZOOM_LEVEL / dataLength;
            var sliceWidth = self.CANVAS_WIDTH / (dataLength / increment);
            var x = 0;
            for (var i = self.curPos; i < rightPos; i+=increment) {
                var v = floatData[i] * 200.0;
                var y = self.CANVAS_HEIGHT / 2 + v;
                if (i === self.curPos) {
                    self.canvasCtx.moveTo(x, y);
                } else {
                    //self.canvasCtx.putImageData(self.imageData, x, y );
                    self.canvasCtx.lineTo(x, y);
                    if (self.showSamples) {
                        self.canvasCtx.fillStyle = "rgba("+243+","+243+","+243+","+(1.0)+")";
                        self.canvasCtx.fillRect( x-3, y-3, 6, 6 );
                    }
                }
                x += sliceWidth;
            }
            //self.canvasCtx.lineTo(self.CANVAS_WIDTH, self.CANVAS_HEIGHT / 2);
            self.canvasCtx.stroke();

            //Samples
            if (sampling != 0) {
                x = 0;
                self.canvasCtx.lineWidth = 1;
                self.canvasCtx.strokeStyle = "rgb(255, 0, 0)";
                self.canvasCtx.beginPath();
                self.canvasCtx.moveTo(0, self.CANVAS_HEIGHT / 2);
                for (i = self.curPos; i < rightPos; i += increment) {
                    var v = floatData[i] * 200.0;
                    var y = self.CANVAS_HEIGHT / 2 + v;
                    if (i === self.curPos) {
                        self.canvasCtx.moveTo(x, y);
                    } else {
                        if (i % sampling == 0) {
                            self.canvasCtx.lineTo(x, y);
                            if (self.showSamples) {
                                self.canvasCtx.fillStyle = "rgba(" + 255 + "," + 0 + "," + 0 + "," + (255 / 255) + ")";
                                self.canvasCtx.fillRect(x - 2, y - 2, 4, 4);
                            }
                        }
                    }
                    x += sliceWidth;
                }
                //self.canvasCtx.lineTo(self.CANVAS_WIDTH, self.CANVAS_HEIGHT / 2);
                self.canvasCtx.stroke();
            }




            // Middle line
            self.canvasCtx.beginPath();
            self.canvasCtx.lineWidth = 1;
            self.canvasCtx.strokeStyle = "rgb(125, 0, 125)";
            self.canvasCtx.moveTo(0, self.CANVAS_HEIGHT / 2);
            self.canvasCtx.lineTo(self.CANVAS_WIDTH, self.CANVAS_HEIGHT / 2)
            self.canvasCtx.stroke();
        },
    };

    $.fn.audioVisualizer = function (options) {
        return this.each(function () {
            var audioVis = Object.create(AudioVisualizer);

            audioVis.init(options, this);

            $.data(this, 'AudioVisualizer', audioVis);

        });
    };

    $.fn.audioVisualizer.options = {
        // These are the defaults.
        strokeColor: "rgb(1, 1, 1)",
        sliceSize: 1000,
        zoomLevel: 1.0,
        handleSuccess:null
    };

})(jQuery, window, document);

