freakimkaefig/Music-XML-Analyzer

View on GitHub
public/js/models/PatternModel.js

Summary

Maintainability
F
1 wk
Test Coverage
/** @constructor */
MusicXMLAnalyzer.PatternModel = function(){

    var that = {},
    noteElements = [],
    noteElements4VexFlow = [],

    curMode = 2,
    curName = "c",
    curAccidential = "none",
    curDuration = "quarter",
    curRythSpec = "none",
    curOctave = "4",
    VEXFLOW_REST_SIGN = "r",
    first = true,

    tripletCurrentAmount = 0,
    tripletEndPositions = [],
    tupletArray = [],
    beamArray = [],

    tripletEnterMode = false,
    noteElementAccidential = 0,
    isDot = false,
    beamVal = false,

    /**
     * Init method of PatternModel
     * @function
     * @public
     */
    init = function() {

    },

    /**
     * Calls method to set the current Mode active
     * @function
     * @public
     *
     * @param {number}    the current mode as number 0 (sound sequence), number 1 (rhythm) and number 2 (melody)
     *
     */
    setCurrentMode = function(mode) {
        if (curMode != mode) {
            emptyNoteArrays();
            $(that).trigger('clearCanvas');
        }

        curMode = mode;

        switch(curMode) {
            case 0:
                setDefaultValsForSoundSequenceMode();
                break;
            case 1:
                setDefaultValsForRhythmMode();
                break;
            case 2:
                setDefaultValsForMelodyMode();
                break;
        }
        //update view
        $(that).trigger('changeViewToCurrentMode', curMode);

    },

    /**
     * This method empties the notes arrays and sets the val first to true.
     * @function
     * @public
     */
    emptyNoteArrays = function() {
        noteElements = [];
        noteElements4VexFlow = [];
        first = true;
    },

    /**
     * Returns the current mode
     * @function
     * @public
     *
     * @return {number}    The current mode
     *
     */
    getCurrentMode = function() {
        return curMode;
    },

    /**
     * Sets the current note name
     * @function
     * @public
     *
     * @param {string}    the current note name
     *
     */
    setCurrentNoteName = function(noteName) {
        if(getCurrentMode() === 1 && noteName !== 'break'){

            curOctave = 4;
            curName = 'b';
        }else{

            curName = noteName;
        }
    },

    /**
     * Returns the current note name
     * @function
     * @public
     *
     * @return {string}    The current note name
     *
     */
    getCurrentNoteName = function() {
        return curName;
    },

    /**
     * Sets the current accidential
     * @function
     * @public
     *
     * @param {string}    the current accidential
     *
     */
    setCurrentAccidential = function(accidential) {
        curAccidential = accidential;
    },

    /**
     * Returns the current accidential
     * @function
     * @public
     *
     * @return {string}    The current accidential
     *
     */
    getCurrentAccidential = function() {
        return curAccidential;
    },

    /**
     * Sets the current note duration
     * @function
     * @public
     *
     * @param {string}    the current note duration
     *
     */
    setCurrentNoteDuration = function(noteDuration) {
        curDuration = noteDuration;
    },

    /**
     * Returns the current note duration
     * @function
     * @public
     *
     * @return {string}    The current note duration
     *
     */
    getCurrentNoteDuration = function() {
        return curDuration;
    },

    /**
     * Sets the current rhythmic special
     * @function
     * @public
     *
     * @param {string}    the current rhythmic special
     *
     */
    setCurrentNoteRythSpecial = function(rythSpec) {
        curRythSpec = rythSpec;
    },

    /**
     * Returns the current rhythmic special which can be a dot or triplet
     * @function
     * @public
     *
     * @return {string}    The current rhythmic special
     *
     */
    getCurrentNoteRythSpecial = function() {
        return curRythSpec;
    },

    /**
     * Sets the current octave
     * @function
     * @public
     *
     * @param {string}    the current octave
     *
     */
    setCurrentOctave = function(octave) {
        curOctave = octave;
    },

    /**
     * Returns the current octave
     * @function
     * @public
     *
     * @return {string}    The current octave
     *
     */
    getCurrentOctave = function() {
        return curOctave;
    },

    /**
     * This method sets vals for accidentials, dots and beams for noteElements-Array
     * @function
     * @public
     *
     */
    setValuesForNoteElement = function() {

        //accidential
        if (curAccidential == "#") {
            noteElementAccidential = 1;
        } else if (curAccidential == "b") {
            noteElementAccidential = -1;
        } else {
            noteElementAccidential = 0;
        }

        //dot
        if(curRythSpec == "dotted") {
            isDot = true;
        } else {
            isDot = false;
        }

        //beam
        if(curRythSpec == "triplet") {
                tripletCurrentAmount++;
        }

        //triplet
        switch(tripletCurrentAmount) {
            case 1:
                beamVal = "begin";
                break;
            case 2:
                beamVal = "continue";
                break;
            case 3:
                beamVal = "end";
                break;
            default:
                beamVal = false;
        }
    },

    /**
     * This method adds notes and breaks to the noteElements Array and the noteElements4VexFlow Array
     * @function
     * @public
     *
     */
    addNoteElement = function() {

        setValuesForNoteElement();

        if (curMode == 2) {
            if(first){
                first = false;
                if (curName != "break") {
                    noteElements.push(
                    {
                    type: curMode,
                        notes:
                        [
                            {
                                type: "note",
                                pitch: {
                                    step: curName.toUpperCase(),
                                    alter: noteElementAccidential,
                                    type: curDuration,
                                    octave: curOctave,
                                    dot: isDot,
                                    beam: beamVal
                                }
                            }
                        ]
                    });
                } else {
                    //break
                    noteElements.push(
                    {
                        type: curMode,
                        notes:
                        [
                            {
                            type: "rest",
                            dot: isDot,
                            duration: curDuration
                            }
                        ]
                    });
                }
            } else {
                if (curName != "break") {
                    noteElements[0].notes.push(
                    {
                                type: "note",
                                pitch: {
                                    step: curName.toUpperCase(),
                                    alter: noteElementAccidential,
                                    type: curDuration,
                                    octave: curOctave,
                                    dot: isDot,
                                    beam: beamVal
                                }
                    });
                } else {
                    //break
                    noteElements[0].notes.push(
                    {
                        type: "rest",
                        dot: isDot,
                        duration: curDuration
                    });
                }
            }
        } else if (curMode == 1) {
        // rhythm mode
            if(first){
                first = false;
                if (curName != "break") {
                    noteElements.push(
                    {
                    type: curMode,
                        notes:
                        [
                            {
                                type: "note",
                                pitch: {
                                    type: curDuration,
                                    dot: isDot,
                                    beam: beamVal
                                }
                            }
                        ]
                    });
                } else {
                    //break
                    noteElements.push(
                    {
                        type: curMode,
                        notes:
                        [
                            {
                                type: "rest",
                                dot: isDot,
                                duration: curDuration
                            }
                        ]
                    });
                }
            } else {
                if (curName != "break") {
                    noteElements[0].notes.push(
                    {

                        type: "note",
                        pitch: {
                            type: curDuration,
                            dot: isDot,
                            beam: beamVal
                        }

                    });
                } else {
                    //break
                    noteElements[0].notes.push(
                    {
                        type: "rest",
                        dot: isDot,
                        duration: curDuration
                    });
                }
            }
        } else if (curMode == 0) {
            if(first){
                first = false;
                if (curName != "break") {
                    noteElements.push(
                    {
                    type: curMode,
                        notes:
                        [
                            {
                                type: "note",
                                pitch: {
                                    step: curName.toUpperCase(),
                                    alter: noteElementAccidential,
                                    octave: curOctave
                                }
                            }
                        ]
                    });
                }
            } else {
                if (curName != "break") {
                    noteElements[0].notes.push(
                    {
                        type: "note",
                        pitch: {
                            step: curName.toUpperCase(),
                            alter: noteElementAccidential,
                            octave: curOctave
                        }
                    });
                }
            }
        }

        //check if break or normal note or note with accidential
        //then adapt values for vexflow an put them into an array
        var note;
        if(getCurrentMode() === 1 && curName !== 'break'){
            curName = 'b';
        }
        var keyContent = getKeyContent4Vexflow(curName);
        var durationContent = getDuration4Vexflow(curDuration);

        //check if break or normal note or note with accidential
        //then adapt values for vexflow an put them into an array
        if (curName == "break") {
            note = new Vex.Flow.StaveNote({ keys: ["b/4"],
                                duration: durationContent + VEXFLOW_REST_SIGN,
                                auto_stem: true });
        } else {
            var keys = keyContent + "/" + curOctave;
            if (getCurrentMode() == 1) {
                if (durationContent === "w" || durationContent === "h" || durationContent === "wd" || durationContent === "hd") {
                    keys += '/d0';
                } else {
                    keys += '/d2';
                }
            }
            note = new Vex.Flow.StaveNote({ keys: [keys],
                                duration: durationContent,
                                auto_stem: true });
        }

        if (curAccidential == "#" || curAccidential == "b") {
            note.addAccidental(0, new Vex.Flow.Accidental(curAccidential));
        }

        if (curRythSpec == "dotted") {
            note.addDotToAll();
        }

        noteElements4VexFlow.push(note);

        //check if triplet
        if (curRythSpec == "triplet") {
                if (tripletCurrentAmount == 3) {
                    $(that).trigger('endTripletEnterMode');
                    tripletEnterMode = false;
                    tripletCurrentAmount = 0;
                    //store all end positions of the triplets
                    tripletEndPositions.push(noteElements4VexFlow.length);
                    //create tuplet and beam and push it into corresponding array
                    var tuplet = new Vex.Flow.Tuplet(noteElements4VexFlow.slice(noteElements4VexFlow.length-3, noteElements4VexFlow.length))
                    var beam = new Vex.Flow.Tuplet(noteElements4VexFlow.slice(noteElements4VexFlow.length-3, noteElements4VexFlow.length))
                    tupletArray.push(tuplet);
                    beamArray.push(beam);
                } else if (tripletCurrentAmount == 1) {
                     tripletEnterMode = true;
                     $(that).trigger('startTripletEnterMode');
                }
        }

        if(noteElements.length == 0) {
            first = true;
            noteElements = [];
        }

        $(that).trigger('patternChange', [noteElements]);
        // send vexflow note elements to controller and then back to view
        $(that).trigger('updateNotationView', [getAllVexFlowNoteElements()]);

    },

    /**
     * Returns the length of the noteElements Array
     * @function
     * @public
     *
     * @return {number}    length of noteElements Array
     */
    getPatternLength = function(){
        if(noteElements.length > 0){
            return noteElements[0].notes.length;
        } else {
            return 0;
        }
    },

    /**
     * Sets the default values when you change to sound sequence mode
     * @function
     * @public
     */
    setDefaultValsForSoundSequenceMode = function() {
        curName = "c";
        curOctave = "4";
        curAccidential = "none";

        tripletCurrentAmount = 0;
        tripletEndPositions = [],
        tupletArray = [],
        beamArray = [],
        beamVal = false;
        isDot = false;

        $(that).trigger('changeSelectedNoteName', curName);
        $(that).trigger('changeSelectedOctave', curOctave);
        $(that).trigger('changeSelectedAccidential', curAccidential);
    },

    /**
     * Sets the default values when you change to rhythm mode
     * @function
     * @public
     */
    setDefaultValsForRhythmMode = function() {
        curDuration = "quarter";
        curRythSpec = "none";
        curOctave = "5";

        tripletCurrentAmount = 0;
        tripletEndPositions = [],
        tupletArray = [],
        beamArray = [],
        beamVal = false;
        isDot = false;

        $(that).trigger('changeSelectedDuration', curDuration);
        $(that).trigger('changeSelectedSpecRyth', curRythSpec);
    },

    /**
     * Sets the default values when you change to melody mode
     * @function
     * @public
     */
    setDefaultValsForMelodyMode = function() {
        curMode = 2;
        curName = "c";
        curOctave = "4";
        curAccidential = "none";
        curDuration = "quarter";
        curRythSpec = "none";

        tripletCurrentAmount = 0;
        tripletEndPositions = [],
        tupletArray = [],
        beamArray = [],
        beamVal = false;
        isDot = false;

        $(that).trigger('changeSelectedNoteName', curName);
        $(that).trigger('changeSelectedOctave', curOctave);
        $(that).trigger('changeSelectedAccidential', curAccidential);
        $(that).trigger('changeSelectedDuration', curDuration);
        $(that).trigger('changeSelectedSpecRyth', curRythSpec);
    },

    /**
     * Returns an Array with triplet end positions
     * @function
     * @public
     *
     * @return {Array.<number>}    Array with trilet end positions
     */
    getTripletEndPositions = function() {
        return tripletEndPositions;
    },

    /**
     * Returns an Array with tuplet end positions
     * @function
     * @public
     *
     * @return {Array.<number>}    Array with tuplet end positions
     */
    getTupletArray = function() {
        return tupletArray;
    },

    /**
     * Returns an Array with beam end positions
     * @function
     * @public
     *
     * @return {Array.<number>}    Array with beam end positions
     */
    getBeamArray = function() {
        return beamArray;
    },

    /**
     * Returns the content of a vexflow key
     * @function
     * @public
     *
     * @return {string}    content of vexflow key
     */
    getKeyContent4Vexflow = function(noteName) {
        var keyContent = noteName;
        switch (curAccidential) {
            case "#":
                keyContent += "#";
                break;
            case "b":
                keyContent += "b";
                break;
            default:
                //...
        }
        return keyContent;
    },

    /**
     * Returns the duration label which is used by vexflow
     * @function
     * @public
     *
     * @return {string}    duration for vexflow
     */
    getDuration4Vexflow = function(duration) {
        var duration4Vexflow = null;

            if ( duration == "whole") {
                duration4Vexflow = "w";
            } else if ( duration == "half") {
                duration4Vexflow = "h";
            } else if ( duration == "quarter") {
                duration4Vexflow = "q";
            } else if ( duration == "eighth") {
                duration4Vexflow = "8";
            } else if ( duration == "16th") {
                duration4Vexflow = "16";
            } else if ( duration == "32nd") {
                duration4Vexflow = "32";
            } else if ( duration == "64th") {
                duration4Vexflow = "64";
            }

            if (curRythSpec == "dotted") {
                duration4Vexflow += "d";
            }

        return duration4Vexflow;
    },

    /**
     * This method gets called when your click on the canvas to add a note element. The paramter note looks like "c/4". It updates the model values curName and curOctave and calls addNoteElement.
     * @function
     * @public
     *
     * @param {string}    note name in vexflow style like "c/4"
     *
     */
    addNoteElementByCanvasClick = function(note) {
        //split string at "/" to get noteName and ovtave
        //and saves it into array noteContainer
        var noteContainer = note.split("/");

        //current vals are being updated after note adding with click
        curName = noteContainer[0];
        curOctave = noteContainer[1];

        // updates selected btns for note name and view in pattern view
        $(that).trigger('changeSelectedNoteName', [curName]);
        $(that).trigger('changeSelectedOctave', [curOctave]);

        addNoteElement();
    },

    /**
     * Removes last added note element from noteElementsArray and vexflowArray.
     * @function
     * @public
     *
     */
    removeLastNoteElement = function() {
        if(noteElements.length == 0) {
            first = true;
            noteElements = [];
        }else if(noteElements[0].notes.length != 0) {
            //check if element you want to delete is triplet
            //and check if there are triplets before
            if(noteElements[0].notes[noteElements4VexFlow.length-1].pitch && noteElements[0].notes[noteElements4VexFlow.length-1].pitch.beam != false) {
                noteElements[0].notes.pop();
                noteElements[0].notes.pop();
                noteElements[0].notes.pop();
                noteElements4VexFlow.pop();
                noteElements4VexFlow.pop();
                noteElements4VexFlow.pop();
                beamArray.pop();
                tupletArray.pop();
            } else {
                noteElements[0].notes.pop();
                noteElements4VexFlow.pop();
            }
        }

        $(that).trigger('patternChange', [noteElements]);
        // send vexflow note elements to controller and then back to view
        $(that).trigger('updateNotationView', [getAllVexFlowNoteElements()]);
    },

    /**
     * Returns an array with Note Elements
     * @function
     * @public
     *
     * @return {Array<Notes>}    array of Note Elements
     */
    getAllNoteElements = function() {
        return noteElements;
    },

    /**
     * Returns an array wtih vexflow notes elements
     * @function
     * @public
     *
     * @return {Array<vexflowNotes>}    array of vexflowNotes
     */
    getAllVexFlowNoteElements = function() {
        return noteElements4VexFlow;
    };

    that.init = init;
    that.getKeyContent4Vexflow = getKeyContent4Vexflow;
    that.getCurrentNoteName = getCurrentNoteName;
    that.getCurrentAccidential = getCurrentAccidential;
    that.getCurrentNoteDuration = getCurrentNoteDuration;
    that.getCurrentNoteRythSpecial = getCurrentNoteRythSpecial;
    that.getCurrentOctave = getCurrentOctave;
    that.setCurrentMode = setCurrentMode;
    that.setCurrentNoteName = setCurrentNoteName;
    that.setCurrentAccidential = setCurrentAccidential;
    that.setCurrentNoteDuration = setCurrentNoteDuration;
    that.setCurrentNoteRythSpecial = setCurrentNoteRythSpecial;
    that.setCurrentOctave = setCurrentOctave;
    that.addNoteElement = addNoteElement;
    that.addNoteElementByCanvasClick = addNoteElementByCanvasClick;
    that.removeLastNoteElement = removeLastNoteElement;
    that.getCurrentMode = getCurrentMode;
    that.getAllNoteElements = getAllNoteElements;
    that.getAllVexFlowNoteElements = getAllVexFlowNoteElements;
    that.getDuration4Vexflow = getDuration4Vexflow;
    that.getTupletArray = getTupletArray;
    that.getBeamArray = getBeamArray;
    that.getPatternLength = getPatternLength;

    return that;
}