Chromesthesia.java
1    /* 
2      this program interprets melodic lines given in ABC notation as a chromesthete might. 
3     
4      a Pitch class will be defined, and will take cneter stage in the processing. 
5     
6      interpreting a melody in ABC notation will amount to flashing colored rectangles for prescribed durations, 
7      while sounding the pitch. the color of the rectangle will correspond to pitch class. the duration will 
8      correspond to the duration of the note. 
9     
10     the duration will be held constant at one beat for the first program. 
11    
12     ABC music represents notes in a manner consistent with these examples: 
13     C, D, E C D E c d e 
14    
15     testing with: 
16     C D E F G A B c c B A G F E D C 
17     C D E C C D E C E F G E F G G A G F E C G A G F E C C G, C C G, C 
18     C C G G A A G F F E E D D C G G F F E E D G G F F E E D C C G G A A G F F E E D D C 
19    
20     C2 C1 C C1/2 E E1 E1/2 E1/2 G2 G1 G G1/2 G 1/2 
21     D,2 D,1 D, D,1/2 D,1/2 F,2 F,1 F, F,1/2 F,1/2 F,1/2 A,2 A,1 A, A,1/2 A,1/2 
22     b2 b1 b b1/2 b1/2 b1/2 b1/2 b b1 b2 
23    
24     C,1/3 C,1/3 C,1/3 C, C,3 C1/3 C1/3 C1/3 C C3 c1/3 c1/3 c c3 
25     C,2/3 C,1/3 D,2/3 D,1/3 C,2/3 C,1/3 D,2/3 D,1/3 C,2/3 C,1/3 D,1 D,1 D,1 C,3 
26    */
27   
28   package chromesthesia2;
29   
30   import java.util.Scanner;
31   import javax.swing.JOptionPane;
32   import javax.swing.SwingUtilities;
33   import painter.SPainter;
34   
35   public class Chromesthesia {
36       // infrastructure for the program - launching a "graphics" thread
37       public static void main(String[] args){
38           SwingUtilities.invokeLater(new ThreadForGUI());
39       }
40   
41       private static class ThreadForGUI implements Runnable {
42           @Override
43           public void run() {
44               new Chromesthesia();
45           }
46       }
47   
48       public Chromesthesia() {
49           interpreter();
50       }
51   
52       // featured variables
53       private static SPainter miro;
54       private static Pitch[] pitches;
55       private static String last;
56   
57       // the interpreter
58       public static void interpreter() {
59           initialization(); // miro and pitches
60           while(true){
61               String input = getInput();
62               if (input.equalsIgnoreCase("EXIT")) {
63                   break;
64               } else if (input.equalsIgnoreCase("AGAIN")) {
65                   try {
66                       playMelody(last,pitches);
67                   } catch (Exception ex) {
68                       showErrorMessage(ex.toString());
69                   }
70               } else {
71                   try {
72                       playMelody(input,pitches);
73                       last = input;
74                   } catch (Exception ex) {
75                       showErrorMessage(ex.toString());
76                   }
77               }
78           }
79           cleanup(); // miro has to go
80       }
81   
82       //methods pertaining to the chromesthetic pitches
83   
84       private static Pitch[] establishPitches(SPainter painter) {
85           Pitch[] pitches = new Pitch[21];
86           Pitch pitchMiddleC = new Pitch("C",painter);
87           pitches[0] = pitchMiddleC;
88           Pitch pitchLowC = new Pitch("C,",painter);
89           pitches[1] = pitchLowC;
90           Pitch pitchHighC = new Pitch("c",painter);
91           pitches[2] = pitchHighC;
92           Pitch pitchmiddleD = new Pitch("D",painter);
93           pitches[3] = pitchmiddleD;
94           Pitch pitchLowD = new Pitch("D,",painter);
95           pitches[4] = pitchLowD;
96           Pitch pitchHighD = new Pitch("d",painter);
97           pitches[5] = pitchHighD;
98           Pitch pitchMiddleE = new Pitch("E",painter);
99           pitches[6] = pitchMiddleE;
100          Pitch pitchLowE = new Pitch("E,",painter);
101          pitches[7] = pitchLowE;
102          Pitch pitchHighE = new Pitch("e",painter);
103          pitches[8] = pitchHighE;
104          Pitch pitchMiddleF = new Pitch("F",painter);
105          pitches[9] = pitchMiddleF;
106          Pitch pitchLowF = new Pitch("F,",painter);
107          pitches[10] = pitchLowF;
108          Pitch pitchHighF = new Pitch("f",painter);
109          pitches[11] = pitchHighF;
110          Pitch pitchMiddleG = new Pitch("G",painter);
111          pitches[12] = pitchMiddleG;
112          Pitch pitchLowG = new Pitch("G,",painter);
113          pitches[13] = pitchLowG;
114          Pitch pitchHighG = new Pitch("g",painter);
115          pitches[14] = pitchHighG;
116          Pitch pitchMiddleA = new Pitch("A",painter);
117          pitches[15] = pitchMiddleA;
118          Pitch pitchLowA = new Pitch("A,",painter);
119          pitches[16] = pitchLowA;
120          Pitch pitchHighA = new Pitch("a",painter);
121          pitches[17] = pitchHighA;
122          Pitch pitchMiddleB = new Pitch("B",painter);
123          pitches[18] = pitchMiddleB;
124          Pitch pitchLowB = new Pitch("B,",painter);
125          pitches[19] = pitchLowB;
126          Pitch pitchHighB = new Pitch("B",painter);
127          pitches[20] = pitchHighB;
128          return pitches;
129      }
130  
131      private static Pitch find(String token, Pitch[] pitches) throws Exception {
132          for (int i = 0; i < pitches.length; i = i + 1) {
133              Pitch pitch = pitches[i];
134              if (pitch.abcName().equals(token)) {
135                  return pitch;
136              }
137          }
138          throw new Exception("### PITCH " + token + " NOT FOUND");
139      }
140  
141      private static void display(Pitch[] pitches) {
142          for (int i = 0; i < pitches.length; i = i + 1) {
143              System.out.println(pitches[i].toString());
144          }
145      }
146  
147      private static void playMelody(String input, Pitch[] pitches) throws Exception {
148          Scanner scanner = new Scanner(input);
149          while (scanner.hasNext()) {
150              String token = scanner.next();
151              String pitchName;
152              String duration = "";
153              if (token.indexOf(",") < 0) {
154                  pitchName = token.substring(0,1);
155                  duration = token.substring(1);
156              } else {
157                  pitchName = token.substring(0,1);
158                  duration = token.substring(2);
159              }
160              if (duration.length() == 0) {duration = "1";}
161              Pitch pitch = find(pitchName,pitches);
162              pitch.play(duration);
163          }
164      }
165  
166      // initialization, cleanup, getting input, error messaging
167      static private void showErrorMessage(String message) {
168          miro.setVisible(false);
169          JOptionPane.showMessageDialog(null,message);
170      }
171  
172      private static void initialization() {
173          // establish the painter and give it a substantial brush width
174          miro = new SPainter("Chromesthesia", 500,500);
175          miro.setVisible(false);
176          miro.setBrushWidth(7);
177          // establish the chromestitic pitch class objects
178          pitches = establishPitches(miro);
179          display(pitches);
180      }
181  
182      private static String getInput() {
183          miro.setVisible(false);
184          String label = "Please enter a melody in ABC notation, or EXIT, or AGAIN (to replay last melody)";
185          String input = JOptionPane.showInputDialog(null,label);
186          miro.setVisible(true);
187          if (input == null) { input = "";}
188          return input;
189      }
190  
191      static private void cleanup() {
192          System.exit(0);
193      }
194  }
195