3v4l.org

run code in 150+ php & hhvm versions
Bugs & Features
<?php $mt = new music_theory(); var_dump($mt->get_scale_by_name("C")); /************************************************************* * This script is developed by Arturs Sosins aka ar2rsawseen, http://webcodingeasy.com * Fee free to distribute and modify code, but keep reference to its creator * * This class implements music theory for generating scales and chords * based on interval patterns between notes. User can add custom scale and chord pattern. * This class can generate scale notes based on provided scale name and type, * generate chord notes based on provided chord name and type, * transpose scales, transpose chords, * generate all chords that include provided notes, * generate all scales that include provided notes, * * For more information, examples and online documentation visit: * http://webcodingeasy.com/PHP-classes/Implement-music-theory-to-generate-scale-and-chord-notes **************************************************************/ class music_theory { //use sharps or flats private $alt = "sharp"; //array with sharp and flat type notes private $note = array( "sharp" => array("C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B"), "flat" => array("C", "Db", "D", "Eb", "E", "F", "Gb", "G", "Ab", "A", "Bb", "B") ); //array with scale patterns private $scales = array( "major" => array(2,2,1,2,2,2,1), "ionian" => array(2,2,1,2,2,2,1), "minor" => array(2,1,2,2,1,2,2), "aeolian" => array(2,1,2,2,1,2,2), "dorian" => array(2,1,2,2,2,1,2), "phrygian" => array(1,2,2,2,1,2,2), "lydian" => array(2,2,2,1,2,2,1), "mixolydian" => array(2,2,1,2,2,1,2), "locrian" => array(1,2,2,1,2,2,2), "melodic minor asc" => array(2,1,2,2,2,2,1), "melodic minor desc" => array(2,1,2,2,1,2,2), "chromatic" => array(1,1,1,1,1,1,1,1,1,1,1,1), "pentatonic major" => array(2,2,3,2,3), "whole tone" => array(2,2,2,2,2,2), "pentatonic minor" => array(3,2,2,3,2), "pentatonic blues" => array(3,2,1,1,3), "pentatonic neutral" => array(2,3,2,3,2), "lydian augmented" => array(2,2,2,2,1,2,1), "lydian minor" => array(2,2,2,1,1,2,2), "lydian diminished" => array(2,1,3,1,2,2,1), "major blues" => array(2,1,1,3,2,3), "dominant pentatonic" => array(2,2,3,3,2), "blues" => array(3,2,1,1,3,2) ); private $chords = array( "major" => array(4,3), "minor" => array(3,4), "5" => array(7), "aug" => array(4,4), "dim" => array(3,3), "7" => array(4,3,3), "sus4" => array(5,2), "sus2" => array(2,5), "7sus4" => array(5,2,3), "6" => array(4,3,2), "maj7" => array(4,3,4), "9" => array(4,3,3,4), "add9" => array(4,3,7), "m6" => array(3,4,2), "m7" => array(3,4,3), "mmaj7" => array(3,4,4), "m9" => array(3,4,3,4), "11" => array(4,3,3,4,3), "13" => array(4,3,3,4,3,4), "6add9" => array(4,3,2,5), "-5" => array(4,2), "7-5" => array(4,2,4), "7maj5" => array(4,4,2), "maj9" => array(4,3,4,3) ); private $errors = array(); //return array with all notes public function get_notes(){ return $this->note[$this->alt]; } //use flat notation public function set_flat(){ $this->alt = "flat"; } //use sharp notation public function set_sharp(){ $this->alt = "sharp"; } //return array with all defined scale types public function get_scale_types(){ return array_keys($this->scales); } //return array with all defined chord types public function get_chord_types(){ return array_keys($this->chords); } //return all errors and empty error array public function get_errors(){ $arr = $this->errors; $this->errors = array(); return $arr; } //check if all notes are in array private function is_in($notes, $arr) { $is_in = true; foreach($notes as $note) { if(!in_array(ucfirst(strtolower($note)), $arr)) { $is_in = false; } } return $is_in; } /** * add new scale type with interval pattern * Example input: * $type = "ionian"; //new scale type name * $pattern_array = array(2,2,1,2,2,2,1); //interval pattern **/ public function add_scale_type($type, $pattern_array){ $type = strtolower($type); if(!in_array($type, array_keys($this->scales))) { $this->scales[$type] = $pattern_array; } else { $this->errors[] = "Scale type name already used"; return false; } } /** * add new chord type with interval pattern * Example input: * $type = "major"; //new chord type name * $pattern_array = array(4,3); //interval pattern **/ public function add_chord_type($type, $pattern_array){ $type = strtolower($type); if(!in_array($type, array_keys($this->chords))) { $this->chords[$type] = $pattern_array; } else { $this->errors[] = "Chord type name already used"; return false; } } /** * Get scale notes by name and type (optional transpose) * Example input: * $name = "C"; //scale name * $type = "ionian"; //scale type * $transpose = -1 //intervals to transpose to (default value 0 doesn't transpose), negative number tranposes down, positive - up * Example output: * Array * ( * [0] => B * [1] => C# * [2] => D# * [3] => E * [4] => F# * [5] => G# * [6] => A# * [7] => B * ) **/ public function get_scale_by_name($name, $type, $transpose = 0){ $name = strtolower($name); $name = ucfirst($name); $type = strtolower($type); if(!in_array($name, $this->note['sharp'])) { if(!in_array($name, $this->note['flat'])) { $this->errors[] = "Invalid scale name"; return array(); } else { $notes = $this->note["flat"]; } } else { $notes = $this->note["sharp"]; } if(!in_array($type, array_keys($this->scales))) { $this->errors[] = "Invalid scale type"; return array(); } $scale = array(); $start = array_keys($notes, $name); $note_sum = sizeof($notes)-1; if($start[0] + $transpose < 0) { $current = ($start[0] + $transpose) + $note_sum + 1; } else if($start[0] + $transpose > $note_sum) { $current = (($start[0] + $transpose) - $note_sum) -1; } else { $current = $start[0] + $transpose; } $scale[] = $this->note[$this->alt][$current]; foreach($this->scales[$type] as $num) { $current += $num; if($current > $note_sum) { $current -= ($note_sum + 1); } $scale[] = $this->note[$this->alt][$current]; } return $scale; } /** * Get chord notes by name and type (optional transpose) * Example input: * $name = "C"; //chord name * $type = "major"; //chord type * $transpose = 2 //intervals to transpose to (default value 0 doesn't transpose), negative number tranposes down, positive - up * Example output: * Array * ( * [0] => D * [3] => F# * [5] => A * ) */ public function get_chord_by_name($name, $type, $transpose = 0){ $name = strtolower($name); $name = ucfirst($name); $type = strtolower($type); if(!in_array($name, $this->note['sharp'])) { if(!in_array($name, $this->note['flat'])) { $this->errors[] = "Invalid chord name"; return array(); } else { $notes = $this->note["flat"]; } } else { $notes = $this->note["sharp"]; } if(!in_array($type, array_keys($this->chords))) { $this->errors[] = "Invalid chord type"; return array(); } $chord = array(); $start = array_keys($notes, $name); $note_sum = sizeof($notes)-1; if($start[0] + $transpose < 0) { $current = ($start[0] + $transpose) + $note_sum + 1; } else if($start[0] + $transpose > $note_sum) { $current = (($start[0] + $transpose) - $note_sum) -1; } else { $current = $start[0] + $transpose; } $chord[] = $this->note[$this->alt][$current]; foreach($this->chords[$type] as $num) { $current += $num; if($current > $note_sum) { $current -= ($note_sum + 1); } $chord[] = $this->note[$this->alt][$current]; } return $chord; } /** * Get chord names and types by notes * Example input: * $notes = array("A","F","C", "E"); //notes * Example output: * Array * ( * [0] => Array * ( * [name] => C * [type] => 13 * ) * * [1] => Array * ( * [name] => D * [type] => m9 * ) * ... * ) */ public function get_chords_by_notes($notes){ if(!is_array($notes)) { $notes = array($notes); } $possible = array(); $chords = $this->get_chord_types(); for($i = 0; $i < 12; $i++) { foreach($chords as $chord) { $temp = $this->get_chord_by_name("C", $chord, $i); if($this->is_in($notes, $temp)) { $count = sizeof($possible); $possible[$count]["name"] = current($temp); $possible[$count]["type"] = $chord; } } } return $possible; } /** * Get scale names and types by notes * Example input: * $notes = array("C","D","E","G#","C#"); //notes * Example output: * Array * ( * [0] => Array * ( * [name] => C * [type] => chromatic * ) * * [1] => Array * ( * [name] => C# * [type] => chromatic * ) * ... * ) */ public function get_scales_by_notes($notes){ if(!is_array($notes)) { $notes = array($notes); } $possible = array(); $scales = $this->get_scale_types(); for($i = 0; $i < 12; $i++) { foreach($scales as $scale) { $temp = $this->get_scale_by_name("C", $scale, $i); if($this->is_in($notes, $temp)) { $count = sizeof($possible); $possible[$count]["name"] = current($temp); $possible[$count]["type"] = $scale; } } } return $possible; } /** * Get scale names and types by chord names/types * Provide different chord names with types, * and receive array with all scale names/types which suits provided chords * * Example input: * $chords = array(array("name" => "C", "type" => "major"), array("name" => "A", "type" => "minor")); //chord array * Example output: * Array * ( * [0] => Array * ( * [name] => C * [type] => major * ) * * [1] => Array * ( * [name] => C * [type] => ionian * ) * ... * ) */ public function get_scales_by_chords($chords){ $notes = array(); foreach($chords as $chord) { $chord_notes = $this->get_chord_by_name(ucfirst(strtolower($chord["name"])), $chord["type"]); $notes = array_merge($notes, $chord_notes); } $notes = array_unique($notes); $possible = array(); $scales = $this->get_scale_types(); for($i = 0; $i < 12; $i++) { foreach($scales as $scale) { $temp = $this->get_scale_by_name("C", $scale, $i); if($this->is_in($notes, $temp)) { $count = sizeof($possible); $possible[$count]["name"] = current($temp); $possible[$count]["type"] = $scale; } } } return $possible; } /** * Get chord names and types by scale * Provide scale name and type * and receive all chord names/types which suits provided scale * * Example input: * $scale_name = "C"; //scale name * $scale_type = "ionian" //scale type * Example output: * Array * ( * [0] => Array * ( * [name] => C * [type] => minor * ) * * [1] => Array * ( * [name] => C * [type] => 5 * ) * ... * ) */ public function get_chords_by_scale($scale_name, $scale_type){ $scale_notes = $this->get_scale_by_name(ucfirst(strtolower($scale_name)), strtolower($scale_type)); if(!empty($scale_notes)) { $possible = array(); $chords = $this->get_chord_types(); for($i = 0; $i < 12; $i++) { foreach($chords as $chord) { $temp = $this->get_chord_by_name("C", $chord, $i); if($this->is_in($temp, $scale_notes)) { $count = sizeof($possible); $possible[$count]["name"] = current($temp); $possible[$count]["type"] = $chord; } } } return $possible; } } } ?>
Output for 7.1.0
Fatal error: Uncaught ArgumentCountError: Too few arguments to function music_theory::get_scale_by_name(), 1 passed in /in/C7j53 on line 7 and at least 2 expected in /in/C7j53:192 Stack trace: #0 /in/C7j53(7): music_theory->get_scale_by_name('C') #1 {main} thrown in /in/C7j53 on line 192
Process exited with code 255.
Output for 5.4.0 - 5.6.28, 7.0.0 - 7.0.14
Warning: Missing argument 2 for music_theory::get_scale_by_name(), called in /in/C7j53 on line 7 and defined in /in/C7j53 on line 192 Notice: Undefined variable: type in /in/C7j53 on line 195 array(0) { }
Output for hhvm-3.12.0 - 3.13.2
Warning: get_scale_by_name() expects at least 2 parameters, 1 given in /in/C7j53 on line 192 Notice: Undefined variable: type in /in/C7j53 on line 195 Warning: strtolower() expects parameter 1 to be string, null given in /in/C7j53 on line 195 array(0) { }
Output for hhvm-3.10.0
Warning: get_scale_by_name() expects at least 2 parameters, 1 given in /in/C7j53 on line 192 Notice: Undefined variable: type in /in/C7j53 on line 195 array(0) { }