77 function detect_key($chord_list) |
77 function detect_key($chord_list) |
78 { |
78 { |
79 $majors = array(); |
79 $majors = array(); |
80 $minors = array(); |
80 $minors = array(); |
81 $sharp_or_flat = ACC_SHARP; |
81 $sharp_or_flat = ACC_SHARP; |
|
82 // sus4 chords are also a great indicator since they are almost always |
|
83 // used exclusively on the fifth |
|
84 $have_sus4 = false; |
82 // index which chords are used in the song |
85 // index which chords are used in the song |
83 foreach ( $chord_list as $chord ) |
86 foreach ( $chord_list as $chord ) |
84 { |
87 { |
85 // discard bass note |
88 // discard bass note |
86 list($chord) = explode('/', $chord); |
89 list($chord) = explode('/', $chord); |
87 $match = array(); |
90 $match = array(); |
88 preg_match('/((?:m?7?|2|add9|sus4|[Mm]aj[79])?)$/', $chord, $match); |
91 preg_match('/((?:[Mm]?7?|2|5|6|add9|sus4|[Mm]aj[79]|dim|aug)?)$/', $chord, $match); |
89 if ( !empty($match[1]) ) |
92 if ( !empty($match[1]) ) |
|
93 { |
90 $chord = str_replace_once($match[1], '', $chord); |
94 $chord = str_replace_once($match[1], '', $chord); |
|
95 if ( $match[1] === 'sus4' ) |
|
96 $have_sus4 = $chord; |
|
97 } |
91 $sharp_or_flat = get_sharp($chord) == $chord ? ACC_SHARP : ACC_FLAT; |
98 $sharp_or_flat = get_sharp($chord) == $chord ? ACC_SHARP : ACC_FLAT; |
92 $chord = get_sharp($chord); |
99 $chord = get_sharp($chord); |
93 if ( $match[1] == 'm' || $match[1] == 'm7' ) |
100 if ( $match[1] == 'm' || $match[1] == 'm7' ) |
94 { |
101 { |
95 // minor chord |
102 // minor chord |
103 if ( !isset($majors[$chord]) ) |
110 if ( !isset($majors[$chord]) ) |
104 $majors[$chord] = 0; |
111 $majors[$chord] = 0; |
105 $majors[$chord]++; |
112 $majors[$chord]++; |
106 } |
113 } |
107 } |
114 } |
|
115 /* |
|
116 // remove very low scorers |
|
117 foreach ( $majors as $key => $count ) |
|
118 { |
|
119 if ( $count < 1 ) |
|
120 unset($majors[$key]); |
|
121 } |
|
122 */ |
108 // now we go through each of the detected major chords, calculate its consonants, and determine how many of its consonants are present in the song. |
123 // now we go through each of the detected major chords, calculate its consonants, and determine how many of its consonants are present in the song. |
109 $scores = array(); |
124 $scores = array(); |
110 foreach ( $majors as $key => $count ) |
125 foreach ( $majors as $key => $count ) |
111 { |
126 { |
112 $scores[$key] = 0; |
127 $scores[$key] = 0; |
113 $consonants = get_consonants(name_to_key($key)); |
128 $consonants = get_consonants(name_to_key($key)); |
114 if ( isset($majors[key_to_name($consonants['fourth'])]) ) |
129 if ( isset($majors[key_to_name($consonants['fourth'])]) ) |
115 $scores[$key] += 2; |
130 $scores[$key] += 2; |
116 if ( isset($majors[key_to_name($consonants['fifth'])]) ) |
131 if ( isset($majors[key_to_name($consonants['fifth'])]) ) |
117 $scores[$key] += 2; |
132 $scores[$key] += $have_sus4 === key_to_name($consonants['fifth']) ? 4 : 2; |
118 if ( isset($majors[key_to_name($consonants['minors'][0])]) ) |
133 if ( isset($majors[key_to_name($consonants['minors'][0])]) ) |
119 $scores[$key] += 1; |
134 $scores[$key] += 1; |
120 if ( isset($majors[key_to_name($consonants['minors'][1])]) ) |
135 if ( isset($majors[key_to_name($consonants['minors'][1])]) ) |
121 $scores[$key] += 2; |
136 $scores[$key] += 2; |
122 if ( isset($majors[key_to_name($consonants['minors'][2])]) ) |
137 if ( isset($majors[key_to_name($consonants['minors'][2])]) ) |
219 { |
234 { |
220 list($upper, $lower) = explode('/', $chord); |
235 list($upper, $lower) = explode('/', $chord); |
221 return transpose_chord($upper, $increment, $accidental) . '/' . transpose_chord($lower, $increment, $accidental); |
236 return transpose_chord($upper, $increment, $accidental) . '/' . transpose_chord($lower, $increment, $accidental); |
222 } |
237 } |
223 // shave off any wacky things we're doing to the chord (minor, seventh, etc.) |
238 // shave off any wacky things we're doing to the chord (minor, seventh, etc.) |
224 preg_match('/((?:m?7?|2|add9|sus4|[Mm]aj[79])?)$/', $chord, $match); |
239 preg_match('/((?:[Mm]?7?|2|5|6|add9|sus4|[Mm]aj[79]|dim|aug)?)$/', $chord, $match); |
225 // find base chord |
240 // find base chord |
226 if ( !empty($match[1]) ) |
241 if ( !empty($match[1]) ) |
227 $chord = str_replace($match[1], '', $chord); |
242 $chord = str_replace($match[1], '', $chord); |
228 // what's our accidental? allow it to be specified, and autodetect if it isn't |
243 // what's our accidental? allow it to be specified, and autodetect if it isn't |
229 if ( !$accidental ) |
244 if ( !$accidental ) |
266 } |
281 } |
267 span.halftone-chord { |
282 span.halftone-chord { |
268 position: absolute; |
283 position: absolute; |
269 top: 0pt; |
284 top: 0pt; |
270 color: rgb(27, 104, 184); |
285 color: rgb(27, 104, 184); |
|
286 } |
|
287 span.halftone-line.labeled span.halftone-chord { |
|
288 position: static; |
271 } |
289 } |
272 span.halftone-chord.sequential { |
290 span.halftone-chord.sequential { |
273 padding-left: 20pt; |
291 padding-left: 20pt; |
274 } |
292 } |
275 div.halftone-key-select { |
293 div.halftone-key-select { |
342 $transpose = isset($_GET['transpose']) ? intval($_GET['transpose']) : $transpose; |
360 $transpose = isset($_GET['transpose']) ? intval($_GET['transpose']) : $transpose; |
343 $transpose_accidental = $inkey ? $accidentals[$inkey] : false; |
361 $transpose_accidental = $inkey ? $accidentals[$inkey] : false; |
344 foreach ( explode("\n", $inner) as $line ) |
362 foreach ( explode("\n", $inner) as $line ) |
345 { |
363 { |
346 $chordline = false; |
364 $chordline = false; |
347 $chords_regex = '/(\((?:[A-G][#b]?(?:m?7?|2|add9|sus4|[Mm]aj[79])?(?:\/[A-G][#b]?)?)\))/'; |
365 $chords_regex = '/(\((?:[A-G][#b]?(?:[Mm]?7?|2|5|6|add9|sus4|[Mm]aj[79]|dim|aug)?(?:\/[A-G][#b]?)?)\))/'; |
348 $line_split = preg_split($chords_regex, $line, -1, PREG_SPLIT_DELIM_CAPTURE); |
366 $line_split = preg_split($chords_regex, $line, -1, PREG_SPLIT_DELIM_CAPTURE); |
|
367 $line_pattern = ''; |
349 if ( preg_match_all($chords_regex, $line, $chords) ) |
368 if ( preg_match_all($chords_regex, $line, $chords) ) |
350 { |
369 { |
351 // this is a line with lyrics + chords |
370 // this is a line with lyrics + chords |
352 // echo out the line, adding spans around chords. here is where we also do transposition |
371 // echo out the line, adding spans around chords. here is where we also do transposition |
353 // (if requested) and |
372 // (if requested) and |
368 else |
387 else |
369 { |
388 { |
370 $line_final[] = '<span class="halftone-chord">' . prettify_accidentals($chord_list[] = transpose_chord(trim($entry, '()'), $transpose, $transpose_accidental)) . '</span>'; |
389 $line_final[] = '<span class="halftone-chord">' . prettify_accidentals($chord_list[] = transpose_chord(trim($entry, '()'), $transpose, $transpose_accidental)) . '</span>'; |
371 } |
390 } |
372 $last_was_chord = true; |
391 $last_was_chord = true; |
|
392 $line_pattern .= 'c'; |
373 } |
393 } |
374 else |
394 else |
375 { |
395 { |
376 if ( trim($entry) != "" ) |
396 if ( trim($entry) != "" ) |
377 { |
397 { |
378 $last_was_chord = false; |
398 $last_was_chord = false; |
379 $line_final[] = $entry; |
399 $line_final[] = $entry; |
|
400 $line_pattern .= 'w'; |
380 } |
401 } |
381 } |
402 } |
382 } |
403 } |
383 $song .= '<span class="halftone-line">' . implode("", $line_final) . "</span>\n"; |
404 $class_append = preg_match('/^w?c+$/', $line_pattern) ? ' labeled' : ''; |
|
405 $song .= '<span class="halftone-line' . $class_append . '">' . implode("", $line_final) . "</span>\n"; |
384 } |
406 } |
385 else if ( preg_match('/^=\s*(.+?)\s*=$/', $line, $match) ) |
407 else if ( preg_match('/^=\s*(.+?)\s*=$/', $line, $match) ) |
386 { |
408 { |
387 $song .= "</div>\n<div class=\"section\">\n== {$match[1]} ==\n\n"; |
409 $song .= "</div>\n<div class=\"section\">\n== {$match[1]} ==\n\n"; |
388 } |
410 } |