freakimkaefig/Music-XML-Analyzer

View on GitHub
app/controllers/ResultController.php

Summary

Maintainability
F
2 wks
Test Coverage
<?php

/**
 * Controller to handle result requests
 * Generating result extracts for search results
 * Retrieves detail information about results
 *
 * @package     Controllers
 */
class ResultController extends BaseController {

    /**
     * Function handling get route 'searchResults' ('/results')
     *
     * @return     \Illuminate\Http\RedirectResponse|\Illuminate\View\View     Redirect to pattern input or results list
     *
     */
    public function getSearchResults() {
        if (!Cache::has('pattern')) {
            Log::error('No pattern in cache!');
            return Redirect::route('pattern');
        }

        if (!Cache::has('results')) {
            Log::error('No results in cache!');
            return Redirect::route('pattern');
        }

        Debugbar::info(Cache::get('results'));
        return View::make('results.list');
    }


    /**
     * Function handling get route 'resultDetail' ('/results/detail/{file}')
     *
     * @param     int     $id     The upload id
     * @param     int     $page     The requested page
     *
     * @return     \Illuminate\View\View|\Illuminate\Http\RedirectResponse     A laravel view, when successful, or a redirect if fails
     *
     */
    public function getResultDetail($id, $page) {
        if (Cache::has('results') && Cache::has('pattern')) {
            $results = Cache::get('results');
            foreach ($results as $item) {
                if ($item->file_id == $id) {
                    $result = $item;
                }
            }

            $pattern = Cache::get('pattern')[0];

            $itemsPerPage = 10;
            $numResults = count($result->occurences);
            $numPages = intval(ceil(count($result->occurences) / $itemsPerPage));
            $startItem = $page * $itemsPerPage;
            $endItem = $startItem + $itemsPerPage - 1;
            $result->occurences = array_slice($result->occurences, $startItem, $itemsPerPage);

            return View::make('results.detail', array('result' => $result, 'itemsPerPage' => $itemsPerPage, 'numPages' => $numPages, 'page' => $page, 'numResults' => $numResults, 'startItem' => $startItem, 'endItem' => $endItem));
        } else {
            return Redirect::route('pattern');
        }
    }


    /**
     * Function handling post route 'resultExtract' ('/result/extract')
     *
     * @return     string     JSON string with the generated extract
     */
    public function postResultExtract() {
        $file_id = Input::get('file_id');
        $file_url = Input::get('file_url');
        $part_id = Input::get('part_id');
        $voice = Input::get('voice');
        $startMeasure = Input::get('startMeasure');
        $start = Input::get('start') - 1;
        $endMeasure = Input::get('endMeasure');
        $end = Input::get('end') - 1;

        $resultExtract = $this->generateResultExtract($file_id, $part_id, $voice, $startMeasure, $start, $endMeasure, $end);

        return json_encode($resultExtract);
    }


    /**
     * Function to generate result extracts
     *
     * @param     int     $upload_id         The uploads id
     * @param     string     $part_id         The part id
     * @param     int     $voice             The voice the finding is in
     * @param     int     $startMeasure     The number of the starting measure
     * @param     int     $start             The note inside the starting measure
     * @param     int     $endMeasure     The number of the ending measure
     * @param     int     $end             The note inside the ending measure
     *
     * @return     \stdClass     A \stdClass object containing information to render staves with vexflow
     *
     */
    private function generateResultExtract($upload_id, $part_id, $voice, $startMeasure, $start, $endMeasure, $end) {
        set_time_limit(300);
        $upload = Upload::find($upload_id);

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

        $part = $xPath->query('//part[@id="' . $part_id . '"]')->item(0);

        $resultObject = new stdClass();
        $resultObject->type = 2;
        $resultObject->part_id = $part_id;
        $partMeasures = $part->getElementsByTagName('measure');
        $numMeasures = $partMeasures->length - 1;
        $firstMeasureNumber = $partMeasures->item(0)->getAttribute('number');
        $start_extract = $startMeasure;
        if ($startMeasure > $firstMeasureNumber) {
            $start_extract--;
        }
        $end_extract = $endMeasure;
        if ($numMeasures > $end_extract) {
            $end_extract++;
        }
        if ($end_extract - $start_extract < 3){
            if ($numMeasures > $end_extract + 1) {
                $end_extract++;
            } elseif ($startMeasure > $firstMeasureNumber) {
                $start_extract--;
            }
            if ($end_extract - $start_extract < 3) {
                if ($startMeasure > $firstMeasureNumber) {
                    $start_extract--;
                } elseif ($numMeasures > $end_extract + 1) {
                    $end_extract++;
                }
            }
        }

        $resultObject->start_extract = $start_extract;
        $resultObject->end_extract = $end_extract;
        $resultObject->measures = array();

        // calculate beat type
        $partBeats = $part->getElementsByTagName('beats')->item(0)->nodeValue;
        $curBeats = $partBeats;
        $partBeatType = $part->getElementsByTagName('beat-type')->item(0)->nodeValue;
        $curBeatType = $partBeatType;

        $measureCounter = 0;

        $part_measures = $part->getElementsByTagName('measure');

        for ($j = $start_extract; $j <= $end_extract; $j++) {
            $noteCounter = 0;
            $measure = $xPath->query('//part[@id="' . $part_id . '"]/measure[@number="' . $j . '"]')->item(0);
            $measureNotes = $measure->getElementsByTagName('note');

            $measureObject = new stdClass();
            $time_signature = false;    // no change in time signature

            // set time signature on first measure
            if ($measureCounter == 0) {
                $time_signature = $curBeats . "/" . $curBeatType;
            }

            // decide if time signature changes
            $beats = $measure->getElementsByTagName('beats');
            $beat_type = $measure->getElementsByTagName('beat-type');
            if (($beats->length && $beat_type->length)) {
                $curBeats = $beats->item(0)->nodeValue;
                $curBeatType = $beat_type->item(0)->nodeValue;
                $time_signature = $curBeats . "/" . $curBeatType;
            }

            // set time signature in note object
            $measureObject->time_signature = $time_signature;

            // append measure object to results
            $resultObject->measures[$measureCounter] = $measureObject;

            // loop over each note in measure
            foreach ($measureNotes as $note) {

                $noteVoice = $note->getElementsByTagName('voice')->item(0)->nodeValue;
                if ($noteVoice == $voice) {
                    // create note object
                    $noteObject = new stdClass();

                    // set color
                    $currentColor = "#000000";
                    if ($startMeasure == $endMeasure) {
                        if (($noteCounter >= $start && $noteCounter <= $end) && ($j == $startMeasure || $j == $endMeasure)) {
                            // set color to red if note is between start and end of result
                            $currentColor = "#b71c1c";
                        }
                    } else {
                        if (($j == $startMeasure && $noteCounter >= $start) || ($j > $startMeasure && $j < $endMeasure) || ($j == $endMeasure && $noteCounter <= $end)) {
                            // set color to red if note is between start and end of result
                            $currentColor = "#b71c1c";
                        }
                    }
                    $noteObject->color = $currentColor;

                    // decide if current element is a note or a rest (only notes have a pitch child)
                    $pitch = $note->getElementsByTagName('pitch');
                    if ($pitch->length) {
                        // it's a note
                        $noteObject->type = "note";
                        $noteObject->pitch = new stdClass();

                        // determine step
                        $step = $pitch->item(0)->getElementsByTagName('step');
                        if ($step->length) {
                            $noteObject->pitch->step = $step->item(0)->nodeValue;
                        }

                        // determine alter value
                        $alter = $pitch->item(0)->getElementsByTagName('alter');
                        if ($alter->length) {
                            $noteObject->pitch->alter = intval($alter->item(0)->nodeValue);
                        } else {
                            $noteObject->pitch->alter = 0;
                        }

                        // determine octave
                        $octave = $pitch->item(0)->getElementsByTagName('octave');
                        if ($octave->length) {
                            $noteObject->pitch->octave = $octave->item(0)->nodeValue;
                        }

                        // determine type / length
                        $type = $note->getElementsByTagName('type');
                        if ($type->length) {
                            $noteObject->pitch->type = $type->item(0)->nodeValue;
                        }

                        // determine dot
                        $dot = $note->getElementsByTagName('dot');
                        if ($dot->length) {
                            $noteObject->pitch->dot = true;
                        } else {
                            $noteObject->pitch->dot = false;
                        }

                        // determine ties
                        $ties = $note->getElementsByTagName('tie');
                        if ($ties->length) {
                            foreach ($ties as $tie) {
                                $noteObject->pitch->ties[] = $tie->getAttribute('type');
                            }
                        } else {
                            $noteObject->pitch->ties[] = false;
                        }

                        // determine chords
                        $chord = $note->getElementsByTagName('chord');
                        if ($chord->length) {
                            $noteObject->pitch->chord = true;
                        } else {
                            $noteObject->pitch->chord = false;
                        }
                        $noteObject->counter = $noteCounter;

                        $timeModification = $note->getElementsByTagName('time-modification');
                        if ($timeModification->length) {
                            $actualNotes = $timeModification->item(0)->getElementsByTagName('actual-notes');
                            if ($actualNotes->length) {
                                $noteObject->pitch->tuplet = $actualNotes->item(0)->nodeValue;
                            }
                        } else {
                            $noteObject->pitch->tuplet = false;
                        }

                    } else {
                        $rest = $note->getElementsByTagName('rest');
                        $unpitched = $note->getElementsByTagName('unpitched');
                        if ($rest->length) {
                            // it's a rest
                            $noteObject->type = "rest";
                            $curDuration = $note->getElementsByTagName('duration')->item(0)->nodeValue;
                            $partDivision = $part->getElementsByTagName('divisions')->item(0)->nodeValue;

                            // determine dot
                            $dot = $note->getElementsByTagName('dot');
                            if ($dot->length) {
                                $noteObject->dot = true;
                                $curDuration = $curDuration - ($curDuration / 4);
                            } else {
                                $noteObject->dot = false;
                            }

                            $restDurationFloat = (float)((int)$curDuration / (int)$partDivision / 4);//(int)$curBeatType);
                            $restDuration = $this->getDurationType($restDurationFloat);
                            $noteObject->duration = $restDuration;

                        } elseif ($unpitched->length) {
                            $noteObject->type = "unpitched";
                            $curDuration = $note->getElementsByTagName('duration')->item(0)->nodeValue;
                            $partDivision = $part->getElementsByTagName('divisions')->item(0)->nodeValue;
                            $noteDurationFloat = (float)((int)$curDuration / (int)$partDivision / (int)$curBeatType);
                            $noteDuration = $this->getDurationType($noteDurationFloat);
                            $noteObject->pitch = new stdClass();
                            $noteObject->pitch->type = $noteDuration;
                            $noteObject->pitch->step = $unpitched->item(0)->getElementsByTagName('display-step')->item(0)->nodeValue;
                            $noteObject->pitch->octave = $unpitched->item(0)->getElementsByTagName('display-octave')->item(0)->nodeValue;
                            $noteObject->pitch->alter = 0;

                            // determine dot
                            $dot = $note->getElementsByTagName('dot');
                            if ($dot->length) {
                                $noteObject->pitch->dot = true;
                            } else {
                                $noteObject->pitch->dot = false;
                            }

                            // determine ties
                            $ties = $note->getElementsByTagName('tie');
                            if ($ties->length) {
                                foreach ($ties as $tie) {
                                    $noteObject->pitch->ties[] = $tie->getAttribute('type');
                                }
                            } else {
                                $noteObject->pitch->ties[] = false;
                            }

                            // determine chords
                            $chord = $note->getElementsByTagName('chord');
                            if ($chord->length) {
                                $noteObject->pitch->chord = true;
                            } else {
                                $noteObject->pitch->chord = false;
                            }

                            $noteObject->counter = $noteCounter;
                        }
                    } // END: if ($pitch->length)

                    // set color in note object
                    $noteObject->color = $currentColor;

                    // append note to results
                    $resultObject->measures[$measureCounter]->notes[] = $noteObject;
                } // END if ($noteVoice == $voice) {
                $noteCounter++;
            } // END: foreach ($measureNotes as $note)
            $measureCounter++;
        }

        unset($doc);
        return $resultObject;
    }


    /**
     * Helper function to calculate the duration from float to type
     *
     * @param     float     $durationFloat           The duration as float
     *
     * @return     string     The duration as string type
     *
     */
    private function getDurationType($durationFloat) {
        if ($durationFloat == 1){
            return "whole";
        } elseif ($durationFloat == 0.75) {
            return "whole";
        } elseif ($durationFloat == 0.5) {
            return "half";
        } elseif ($durationFloat == 0.375) {
            return "half";
        } elseif ($durationFloat == 0.25) {
            return "quarter";
        } elseif ($durationFloat == 0.1875) {
            return "quarter";
        } elseif ($durationFloat == 0.125) {
            return "eighth";
        } elseif ($durationFloat == 0.09375) {
            return "eighth";
        } elseif ($durationFloat == 0.0625) {
            return "16th";
        } elseif ($durationFloat == 0.046875) {
            return "16th";
        } elseif ($durationFloat == 0.03125) {
            return "32nd";
        } elseif ($durationFloat == 0.0234375) {
            return "32nd";
        } elseif ($durationFloat == 0.015625) {
            return "64th";
        } elseif ($durationFloat == 0.01171875) {
            return "64th";
        } else {
            // catch strange values (FALLBACK)
            return "64th";    // set to lowest possible value
        }
    }


    /**
     * Static helper function |  to retrieve the artist for a given upload id
     *
     * @param     int     $id     the uploads id
     *
     * @return     string     The artist for given upload id
     *
     */
    public static function _getArtist($id) {
        $xml = simplexml_load_string(Upload::find($id)->content);
        $artist = $xml->xpath("//credit[credit-type='composer']");
        if ($artist) {
            return $artist[0]->{'credit-words'}->{0};
        } else {
            return "Unknown Artist";
        }
    }


    /**
     * Static helper function to retrieve the title for a given upload id
     *
     * @param     int     $id     the uploads id
     *
     * @return     string     The title for given upload id
     *
     */
    public static function _getTitle($id) {
        $xml = simplexml_load_string(Upload::find($id)->content);
        $title = $xml->xpath("//credit[credit-type='title']");
        if ($title) {
            return $title[0]->{'credit-words'}->{0};
        } else {
            return "Unknown Title";
        }
    }


    /**
     * Static helper function to retrieve the filename for a given upload id
     *
     * @param     int     $id     the uploads id
     *
     * @return     string     The title for given upload id
     *
     */
    public static function _getFilename($id) {
        $upload =  Upload::find($id);
        return $upload->name();
    }

    /**
     * Static helper function to retrieve the part name for a given upload id and part_id
     *
     * @param     int     $id         the uploads id
     * @param     int     $part_id     the part id
     *
     * @return     string     The title for given upload id
     *
     */
    public static function _getInstrument($id, $part_id) {
        $xml = simplexml_load_string(Upload::find($id)->content);
        $part = $xml->xpath('//score-part[@id="' . $part_id . '"]');
        if ($part) {
            return $part[0]->{'part-name'}->{0};
        } else {
            return "Unknown Instrument";
        }
    }


    /**
     * Static helper function to retrieve the key for a given upload id
     *
     * @param     int     $id     the uploads id
     *
     * @return     string     The key for given upload id
     *
     */
    public static function _getKey($id){
        $xml = simplexml_load_string(Upload::find($id)->content);
        $keys = $xml->xpath("//key");
        $key = $keys[0];

        $fifths = $key->fifths;
        $mode = (string)$key->mode;

        if($fifths != null && $mode === "major"){
            switch($fifths) {
                case "0": return "C major"; break;
                case "1": return "G major"; break;
                case "2": return "D major"; break;
                case "3": return "A major"; break;
                case "4": return "E major"; break;
                case "5": return "H major"; break;
                case "6": return "F sharp major"; break;
                case "7": return "C sharp major"; break;
                case "-1": return "F major"; break;
                case "-2": return "B major"; break;
                case "-3": return "E flat major"; break;
                case "-4": return "A flat major"; break;
                case "-5": return "D flat major"; break;
                case "-6": return "G flat major"; break;
                case "-7": return "C flat major"; break;
            }
        } elseif($fifths != null && $mode === "minor") {
            switch($fifths) {
                case "0": return "A minor"; break;
                case "1": return "E minor"; break;
                case "2": return "H minor"; break;
                case "3": return "F sharp minor"; break;
                case "4": return "C sharp minor"; break;
                case "5": return "G sharp minor"; break;
                case "6": return "D sharp minor"; break;
                case "7": return "A sharp minor"; break;
                case "-1": return "D minor"; break;
                case "-2": return "G minor"; break;
                case "-3": return "C minor"; break;
                case "-4": return "F minor"; break;
                case "-5": return "B minor"; break;
                case "-6": return "E flat minor"; break;
                case "-7": return "A flat minor"; break;
            }
        }
    }
}