fork download
  1. #!/usr/bin/perl
  2.  
  3. # this is my first ever perl script
  4. use strict;
  5.  
  6. # semitone offsets of major scale from C
  7. my @Semitone = (0, 2, 4, 5, 7, 9, 11);
  8. # mode strings to indices
  9. my %Mode = ("" => 0, "D" => 1, "Y" => 2, "L" => 3, "M" => 4, "b" => 5, "C" => 6);
  10. # chord inversion maps
  11. my %Inv = (
  12. "" => [0, 2, 4], "6" => [2, 4, 0], "64" => [4, 0, 2],
  13. "7" => [0, 2, 4, 6], "65" => [2, 4, 6, 0], "43" => [4, 6, 0, 2], "42" => [6, 0, 2, 4],
  14. );
  15.  
  16. # read a line
  17. while (<>) {
  18. # remove trailing newline
  19. # trends page uses ".", hooktheory api uses ","
  20. for (split /[.,;]/) {
  21. # match entire string, trim whitespace for convenience
  22. # see somewhere at the doc for original grammar
  23. !(/\s*^([DYLMbC])?([1-7])(7|65|43|42|64|6)?(\/([1-7]))?\s*$/) and next;
  24. # chord cannot be both applied and borrowed
  25. (my $applied = !!$5) and $1 and next;
  26. # apply inversion
  27. my @chord = @{$Inv{$3}};
  28. # map degree indices to semitone values
  29. for (@chord) {
  30. # root degree + chord factor + mode shift
  31. my $degree = $2 + $_ + $Mode{$1};
  32. # convert to semitones
  33. $_ = $Semitone[($degree - 1) % 7];
  34. # transpose scale so that tonic becomes C
  35. $_ -= $Semitone[$Mode{$1}];
  36. # transpose applied chord relative to destination
  37. $applied and ($_ += $Semitone[$5 - 1]);
  38. # limit within 0 - 11
  39. $_ %= 12;
  40. }
  41. # display semitones
  42. print $_, " -> ", join(",", @chord), "\n";
  43. }
  44. print "\n";
  45. }
  46.  
Success #stdin #stdout 0s 6044KB
stdin
D47.L27.b7.565/2
1,364,6,164,4,16,265,57,1
stdout
D47 -> 5,9,0,3
L27 -> 2,6,9,0
b7 -> 10,2,5
565/2 -> 1,4,7,9

1 -> 0,4,7
364 -> 11,4,7
6 -> 9,0,4
164 -> 7,0,4
4 -> 5,9,0
16 -> 4,7,0
265 -> 5,9,0,2
57 -> 7,11,2,5
1 -> 0,4,7