app/controllers/ResultController.php
<?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;
}
}
}
}