freakimkaefig/Music-XML-Analyzer

View on GitHub
app/controllers/MelodyController.php

Summary

Maintainability
F
4 days
Test Coverage
<?php

/**
 * Controller to search melody patterns
 *
 * @package     Controllers
 */
class MelodyController {

    static $result;
    static $results;
    static $patternArray;
    static $xmlArray;
    static $xmlPositionArray;
    static $xmlCounterArray;
    static $once;
    static $once2 = true;
    static $noteCounter;
    static $counter;

    /**
     * Search function to compare melody pattern to xml files
     *
     * @param   object     $pattern     The user generated melody pattern
     *
     * @return  array     Containing objects of \stdClass with file_id, file_url as well as start and end positions where pattern matches any given xml file
     *
     */
    public function search($pattern) {

        $p = $pattern[0]->notes;
        self::$patternArray = array();
        self::$results = array();

        //get note intervals of pattern
        foreach ($p as $note) {
            if($note->type == "note"){
                $interval = PatternController::getInterval($note);
                $obj = new stdClass();
                $obj->interval = $interval;
                $obj->type = $note->pitch->type;
                if(isset($note->pitch->beam)){
                    if($note->pitch->beam != false){
                        $obj->beam = (string)$note->pitch->beam;
                    }
                }
                // else if dotted note
                if(isset($note->pitch->dot)){
                    if($note->pitch->dot == true){
                        $obj->dot = "1";
                    }
                }
                array_push(self::$patternArray, $obj);
            }else{
                $obj = new stdClass();
                $obj->duration = $note->duration;
                $obj->dot = $note->dot;

                array_push(self::$patternArray, $obj);
            }
        }

        //get user uploads & file_id's & file_url
        $user = User::find(Cookie::get('user_id'));
        $user->uploads->each(function($upload) {

            $xml = simplexml_load_string($upload->content);
            $file_id = $upload->id;
            $file_url = $upload->url;

            $doc = new DOMDocument();
            $doc->loadXML($upload->content);
            $xPath = new DOMXPath($doc);

            self::$once = true;
            self::$xmlArray = array();
            self::$xmlPositionArray = array();
            self::$xmlCounterArray = array();
            self::$result = new stdClass();
            self::$result->occurences = array();

            $parts = $xml->xpath("//part");

            foreach($parts as $part){

                self::$noteCounter = 0;
                self::$once = true;
                self::$once2 = true;

                foreach($part->measure as $measure){
                    self::$counter = 0;

                    if(self::$once2){
                        self::$once2 = false;
                        //get division for calculation of rest duration once
                        $partDivision = $measure->attributes->divisions;
                        //get beat-type for calculation of rest duration once
                        $partBeatType = $measure->attributes->time->{'beat-type'};
                    }

                    //get beat-type changes within measures
                    if(!self::$once2){ //no changes within first round
                        // if changes occure
                        if(isset($measure->attributes->time->{'beat-type'})){
                            // get changes
                            $partBeatType = $measure->attributes->time->{'beat-type'};
                        }
                    }
                    $countPartMeasureNote = count($measure->note);
                    for($j = 0; $j < $countPartMeasureNote; $j++){
                        self::$noteCounter++;
                        self::$counter++;
                        $n = $measure->note[$j];

                        if(self::$once){
                            self::$once = false;
                            $lastVoice = $measure->note[$j]->voice;
                        }

                        if((int)$n->voice == (int)$lastVoice){
                            $pitch = new stdClass();
                            $pitch->step = $n->pitch->step;
                            $pitch->alter = $n->pitch->alter;
                            $pitch->octave = $n->pitch->octave;


                            $note = new stdClass();
                            $note->pitch = $pitch;
                            $note->voice = $n->voice;
                            // $note->type = $n->type;
                            $note->position = self::$noteCounter;
                            $note->counter = self::$counter;

                            // if note
                            if(!$n->rest && !isset($n->chord)){

                                $obj = new stdClass();
                                $obj->interval = PatternController::getInterval($note);
                                $obj->type = (string)$n->type;

                                if(isset($n->{'time-modification'})){
                                    $obj->beam = (string)$n->beam[0];
                                }
                                // else if dotted note
                                elseif($n->dot){
                                    $obj->dot = "1";
                                }

                                $res = new stdClass();
                                $res->part = $part['id'];
                                $res->pos = self::$noteCounter;

                                array_push(self::$xmlArray, $obj);
                                array_push(self::$xmlPositionArray, $res);
                                array_push(self::$xmlCounterArray, $note->counter);

                            }
                            // else if rest
                            else if(isset($n->rest)){
                                // calculate rest duration
                                try{
                                    $restDurationFloat = (float)((int)$n->duration / (int)$partDivision / 4);
                                } catch (Exception $e) {
                                    Log::error('Exception abgefangen: ',  array('error' => $e->getMessage()));
                                }

                                // determine 'type'
                                if ($restDurationFloat == 1){
                                    $restDuration = "whole";
                                } elseif ($restDurationFloat == 1.5) {
                                    $restDuration = "whole";
                                } elseif ($restDurationFloat == 0.75) {
                                    $restDuration = "half";
                                } elseif ($restDurationFloat == 0.5) {
                                    $restDuration = "half";
                                } elseif ($restDurationFloat == 0.375) {
                                    $restDuration = "quarter";
                                } elseif ($restDurationFloat == 0.25) {
                                    $restDuration = "quarter";
                                } elseif ($restDurationFloat == 0.1875) {
                                    $restDuration = "eighth";
                                } elseif ($restDurationFloat == 0.125) {
                                    $restDuration = "eighth";
                                } elseif ($restDurationFloat == 0.09375) {
                                    $restDuration = "16th";
                                } elseif ($restDurationFloat == 0.0625) {
                                    $restDuration = "16th";
                                } elseif ($restDurationFloat == 0.046875) {
                                    $restDuration = "32nd";
                                } elseif ($restDurationFloat == 0.03125) {
                                    $restDuration = "32nd";
                                } elseif ($restDurationFloat == 0.0234375) {
                                    $restDuration = "64th";
                                } elseif ($restDurationFloat == 0.015625) {
                                    $restDuration = "64th";
                                } elseif ($restDurationFloat == 0.01171875) {
                                    $restDuration = "64th";
                                } else {
                                    // catch strange values (FALLBACK)
                                    $restDuration = "64th";    // set to lowest possible value
                                }
                                $res = new stdClass();
                                $res->part = $part['id'];
                                $res->pos = self::$noteCounter;

                                $obj = new stdClass();
                                $obj->duration = $restDuration;
                                if($n->dot){

                                    $obj->dot = true;
                                }else{

                                    $obj->dot = false;
                                }

                                array_push(self::$xmlArray, $obj);
                                array_push(self::$xmlPositionArray, $res);
                                array_push(self::$xmlCounterArray, $note->counter);

                            }

                            //check if Array-length equals Pattern-length already
                            if(count(self::$xmlArray) == count(self::$patternArray)){

                                // compare arrays
                                if(array_values(self::$xmlArray) == array_values(self::$patternArray)){
                                    // create result
                                    self::$result->file_id = $file_id;
                                    self::$result->file_url = $file_url;

                                    $docPart = $xPath->query('//part[@id="' . (string)reset(self::$xmlPositionArray)->part . '"]')->item(0);
                                    $startNote = $docPart->getElementsByTagName('note')->item(((string)reset(self::$xmlPositionArray)->pos - 1));
                                    $startMeasureNumber = $startNote->parentNode->getAttribute('number');

                                    $endNote = $docPart->getElementsByTagName('note')->item(((string)end(self::$xmlPositionArray)->pos - 1));
                                    $endMeasureNumber = $endNote->parentNode->getAttribute('number');

                                    //fill with occurences
                                    $occ = new stdClass();
                                    $occ->start = reset(self::$xmlCounterArray);
                                    $occ->startMeasure = $startMeasureNumber;
                                    $occ->end = end(self::$xmlCounterArray);
                                    $occ->endMeasure = $endMeasureNumber;
                                    $occ->voice = (int)$note->voice;
                                    $occ->part_id = (string)$part['id'];

                                    array_push(self::$result->occurences, $occ);

                                    //reset arrays
                                    self::$xmlArray = array();
                                    self::$xmlPositionArray = array();
                                    self::$xmlCounterArray = array();

                                }else{

                                    self::$xmlArray = array_splice(self::$xmlArray, 1);
                                    self::$xmlCounterArray = array_splice(self::$xmlCounterArray, 1);
                                    self::$xmlPositionArray = array_splice(self::$xmlPositionArray, 1);

                                    self::$xmlArray = array_values(self::$xmlArray);
                                    self::$xmlCounterArray = array_values(self::$xmlCounterArray);
                                    self::$xmlPositionArray = array_values(self::$xmlPositionArray);

                                }

                                } //if array lengths aren't equal yet, continue

                        }
                        else{ //different voice incoming next; unset array; begin from scratch
                                $lastVoice = $measure->note[$j]->voice;
                                $j--;
                                self::$noteCounter--;
                                self::$counter--;
                                self::$xmlArray = array();
                                self::$xmlPositionArray = array();
                                self::$xmlCounterArray = array();
                            }
                    }
                }

                // reset arrays
                self::$xmlArray = array();
                self::$xmlPositionArray = array();
                self::$xmlCounterArray = array();
            }//end of foreach(parts as part)

            // check if result->occ is empty
            if(!empty(self::$result->occurences)){
                //push result
                array_push(self::$results, self::$result);
            }

        });

        return self::$results;
    }
}