Chromesthesia.java
1    
2    /* 
3     * This program interprets melodic lines given in ABC notation as a chromesthete might. 
4     * 
5     * A Pitch class will be defined, and will take center stage in the processing. 
6     * 
7     * Interpreting a melody in ABC notation will amount to flashing colored rectangles for prescribed durations, while sounding the pitch! The color of the rectangle will correspond to pitch class. 
8     * The duration will correspond to the duration of the note. 
9     * 
10    * For this first version of the program, the duration will be held constant at 1 beat. 
11    * 
12    * Three sorts of images will appear on the screen, the chromesthetic output box, a text input box, and an error message box. Simplicity of design is rendered by permitting only one box to be on 
13    * the screen at a time. 
14    * 
15    * ABC represents notes in a manner consistent with these examples: 
16    * C, D, E, C D E c d e 
17    * 
18    * Google ABC music representation if you would like to know more about it. 
19    */
20   
21   package chromesthesia2;
22   
23   import java.util.Scanner;
24   import javax.swing.JOptionPane;
25   import javax.swing.SwingUtilities;
26   import painter.SPainter;
27   
28   public class Chromesthesia {
29       // INFRASTRUCTURE FOR THE PROGRAM -- LAUNCHING A "GRAPHICS" THREAD
30   
31       public static void main(String[] args) {
32           SwingUtilities.invokeLater(new ThreadForGUI());
33       }
34   
35       private static class ThreadForGUI implements Runnable {
36           @Override
37           public void run() {
38               new Chromesthesia();
39           }
40       }
41   
42       public Chromesthesia() {
43           interpreter();
44       }
45   
46       // FEATURED VARIABLES
47   
48       private static SPainter miro;
49       private static Pitch[] pitches;
50   
51       // THE INTERPRETER
52   
53       public static void interpreter() {
54           initialization(); // miro and pitches
55           String inputAgain = "";
56           while ( true ) {
57               String input = getInput();
58               if ( input.equalsIgnoreCase("EXIT") ) {
59                   break;
60               } else if (input.equalsIgnoreCase("AGAIN")) {
61                   try{
62                       playMelody(inputAgain,pitches);
63                   }catch (Exception ex) {
64                       showErrorMessage(ex.toString());
65                   }
66               } else {
67                   try {
68                       inputAgain = input;
69                       playMelody(input,pitches);
70                   } catch (Exception ex) {
71                       showErrorMessage(ex.toString());
72                   }
73               }
74           }
75   
76           cleanup(); // miro has to go
77   
78       }
79   
80       // METHODS PERTAINING TO THE CHROMESTHETIC PITCHES
81       // IMPORTANT LINES OF TEXT
82       // (a) C D E F G A B c c B A G F E D C
83       // (b) 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
84       // (c) 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
85       // (aa) C2 C1 C C1/2 C1/2 E2 E1 E E1/2 E1/2 G2 G1 G G1/2 G1/2
86       // (bb) D,2 D,1 D, D,1/2 D,1/2 F,2 F,1 F, F,1/2 F,1/2 A,2 A,1 A, A,1/2 A,1/2
87       // (cc) b2 b1 b b1/2 b1/2 b1/2 b1/2 b b1 b2
88       // (aaa) C,1/3 C,1/3 C,1/3 C, C,3 C1/3 C1/3 C1/3 C C3 c1/3 c1/3 c1/3 c c3
89       // (bbb) 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
90       // ADDED ENCODED SEQUENCES
91       // (A) C,2/3 G,2/3 G,2/3 G,2/3 F,1/3 E,1/3 D1 E,1/3 F,1/3 G1 A1 G1 F,2/3 E,2/3 D,2/3 C2
92       // (B) A,2 B,1 E, C,1/2 D,1/2 G,2 B,1 A, F1/2 E1/2 B2 A,1 C, D,1/2 A,1/2 G, C3
93   
94       private static Pitch[] establishPitches(SPainter painter) {
95           Pitch[] pitches = new Pitch[21];
96           Pitch pitchMiddleC = new Pitch("C",painter);
97           pitches[0] = pitchMiddleC;
98           Pitch pitchLowC = new Pitch("C,",painter);
99           pitches[1] = pitchLowC;
100          Pitch pitchHighC = new Pitch("c",painter);
101          pitches[2] = pitchHighC;
102          Pitch pitchMiddleD = new Pitch("D",painter);
103          pitches[3] = pitchMiddleD;
104          Pitch pitchLowD = new Pitch("D,",painter);
105          pitches[4] = pitchLowD;
106          Pitch pitchHighD = new Pitch("d",painter);
107          pitches[5] = pitchHighD;
108          Pitch pitchMiddleE = new Pitch("E",painter);
109          pitches[6] = pitchMiddleE;
110          Pitch pitchLowE = new Pitch("E,",painter);
111          pitches[7] = pitchLowE;
112          Pitch pitchHighE = new Pitch("e",painter);
113          pitches[8] = pitchHighE;
114          Pitch pitchMiddleF = new Pitch("F",painter);
115          pitches[9] = pitchMiddleF;
116          Pitch pitchLowF = new Pitch("F,",painter);
117          pitches[10] = pitchLowF;
118          Pitch pitchHighF = new Pitch("f",painter);
119          pitches[11] = pitchHighF;
120          Pitch pitchMiddleG = new Pitch("G",painter);
121          pitches[12] = pitchMiddleG;
122          Pitch pitchLowG = new Pitch("G,",painter);
123          pitches[13] = pitchLowG;
124          Pitch pitchHighG = new Pitch("g",painter);
125          pitches[14] = pitchHighG;
126          Pitch pitchMiddleA = new Pitch("A",painter);
127          pitches[15] = pitchMiddleA;
128          Pitch pitchLowA = new Pitch("A,",painter);
129          pitches[16] = pitchLowA;
130          Pitch pitchHighA = new Pitch("a",painter);
131          pitches[17] = pitchHighA;
132          Pitch pitchMiddleB = new Pitch("B",painter);
133          pitches[18] = pitchMiddleB;
134          Pitch pitchLowB = new Pitch("B,",painter);
135          pitches[19] = pitchLowB;
136          Pitch pitchHighB = new Pitch("b",painter);
137          pitches[20] = pitchHighB;
138          return pitches;
139      }
140  
141      private static Pitch find(String token, Pitch[] pitches) throws Exception {
142          for ( int i = 0; i < pitches.length; i = i + 1 ) {
143              Pitch pitch = pitches[i];
144              if ( pitch.abcName().equals(token) ) {
145                  return pitch;
146              }
147          }
148          throw new Exception("### PITCH " + token + " NOT FOUND");
149      }
150  
151      private static void display(Pitch[] pitches) {
152          for ( int i = 0; i < pitches.length; i = i + 1 ) {
153              System.out.println(pitches[i].toString());
154          }
155      }
156  
157      private static void playMelody(String input, Pitch[] pitches) throws Exception {
158          Scanner scanner = new Scanner(input);
159          while ( scanner.hasNext() ) {
160              String token = scanner.next();
161              String pitchName;
162              String duration = "";
163              if ( token.indexOf(",") < 0 ) {
164                  pitchName = token.substring(0,1);
165                  duration = token.substring(1);
166              } else {
167                  pitchName = token.substring(0,2);
168                  duration = token.substring(2);
169              }
170              if ( duration.length() == 0 ) { duration = "1"; }
171              Pitch pitch = find(pitchName,pitches);
172              pitch.play(duration);
173          }
174      }
175  
176      // INITIALIZATION, CLEANUP, GETTING INPUT, ERROR MESSAGING
177  
178      static private void showErrorMessage(String message) {
179          miro.setVisible(false);
180          JOptionPane.showMessageDialog(null,message);
181      }
182  
183      private static void initialization() {
184          // ESTABLISH THE PAINTER AND GIVE IT A SUBSTANTIAL BRUSH WIDTH
185          miro = new SPainter("Chromesthesia",500,500);
186          miro.setVisible(false);
187          miro.setBrushWidth(7);
188          // ESTABLISH THE CHROMESTHETIC PITCH CLASS OBJECTS
189          pitches = establishPitches(miro);
190          display(pitches);
191      }
192  
193      private static String getInput() {
194          miro.setVisible(false);
195          String label = "Please enter a melody in ABC notation, or EXIT, or AGAIN ...    ";
196          String input = JOptionPane.showInputDialog(null,label);
197          miro.setVisible(true);
198          if ( input == null ) { input = ""; }
199          return input;
200      }
201  
202      static private void cleanup() {
203          System.exit(0);
204      }
205  }