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 center stage in the processing. 
5     * 
6     * 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. 
7     * The duration will correspond to the duration of the note. 
8     * 
9     * The duration will be held constant at 1 beat. 
10    * 
11    * These 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 
12    * to be on the screen at a time. 
13    * 
14    * ABC notes represent these examples: 
15    * C, D, E, C D E c d e 
16    * 
17    * Google ABC music representation if you need help. 
18    * 
19    * By Hunter Gersitz | SUNY Oswego Fall 2019 
20    * */
21   
22   package chromesthesia2;
23   
24   import painter.SPainter;
25   
26   import javax.swing.*;
27   import java.util.Scanner;
28   
29   public class Chromesthesia {
30   
31       // INFRASTRUCTURE FOR THE PROBLEM -- LAUNCHING A "GRAPHICS" THREAD
32   
33       public static void main(String[] args) {
34           SwingUtilities.invokeLater(new ThreadForGUI());
35       }
36   
37       private static class ThreadForGUI implements Runnable {
38           public void run() {
39               new Chromesthesia();
40           }
41       }
42   
43       private Chromesthesia() {
44           interpreter();
45       }
46   
47       // FEATURED VARIABLES
48   
49       private static SPainter miro;
50       private static Pitch[] pitches;
51   
52       // THE INTERPRETER
53   
54       private static void interpreter() {
55   
56           initialization(); //miro and pitches
57           String lastInput = "";
58   
59           while (true) {
60               String input = getInput();
61   
62   
63               if (input.equalsIgnoreCase("EXIT")) {
64                   break;
65               }
66               else if (input.equalsIgnoreCase("AGAIN")) {
67                   try {
68                       playMelody(lastInput, pitches);
69                   } catch (Exception ex) {
70                       showErrorMessage(ex.toString());
71                   }
72               }
73               else {
74                   try {
75                       playMelody(input, pitches);
76                       lastInput = input;
77                   } catch (Exception ex) {
78                       showErrorMessage(ex.toString());
79                   }
80               }
81           }
82           cleanup(); //miro has to go
83   
84       }
85   
86       // METHODS FEATURING TO THE CHROMESTHETIC PITCHES
87   
88       private static Pitch[] establishPitches(SPainter painter) {
89           Pitch[] pitches = new Pitch[21];
90           Pitch pitchMiddleC = new Pitch("C", painter);
91           pitches[0] = pitchMiddleC;
92           Pitch pitchLowC = new Pitch("C,", painter);
93           pitches[1] = pitchLowC;
94           Pitch pitchHighC = new Pitch("c", painter);
95           pitches[2] = pitchHighC;
96           Pitch pitchMiddleD = new Pitch("D", painter);
97           pitches[3] = pitchMiddleD;
98           Pitch pitchLowD = new Pitch("D,", painter);
99           pitches[4] = pitchLowD;
100          Pitch pitchHighD = new Pitch("d", painter);
101          pitches[5] = pitchHighD;
102          Pitch pitchMiddleE = new Pitch("E", painter);
103          pitches[6] = pitchMiddleE;
104          Pitch pitchLowE = new Pitch("E,", painter);
105          pitches[7] = pitchLowE;
106          Pitch pitchHighE = new Pitch("e", painter);
107          pitches[8] = pitchHighE;
108  
109          // notes F and f
110          Pitch pitchMiddleF = new Pitch("F", painter);
111          pitches[9] = pitchMiddleF;
112  
113          Pitch pitchLowF = new Pitch("F,", painter);
114          pitches[10] = pitchLowF;
115  
116          Pitch pitchHighF = new Pitch("f", painter);
117          pitches[11] = pitchHighF;
118  
119          // notes G and g
120          Pitch pitchMiddleG = new Pitch("G",painter);
121          pitches[12] = pitchMiddleG;
122  
123          Pitch pitchLowG = new Pitch("G,",painter);
124          pitches[13] = pitchLowG;
125  
126          Pitch pitchHighG = new Pitch("g",painter);
127          pitches[14] = pitchHighG;
128  
129          // NOTES A and a
130          Pitch pitchMiddleA = new Pitch("A",painter);
131          pitches[15] = pitchMiddleA;
132  
133          Pitch pitchLowA = new Pitch("A,",painter);
134          pitches[16] = pitchLowA;
135  
136          Pitch pitchHighA = new Pitch("a",painter);
137          pitches[17] = pitchHighA;
138  
139          // NOTES B and b
140          Pitch pitchMiddleB = new Pitch("B",painter);
141          pitches[18] = pitchMiddleB;
142  
143          Pitch pitchLowB = new Pitch("B,",painter);
144          pitches[19] = pitchLowB;
145  
146          Pitch pitchHighB = new Pitch("b",painter);
147          pitches[20] = pitchHighB;
148  
149  
150          return pitches;
151      }
152  
153      private static Pitch find(String token, Pitch[] pitches) throws Exception {
154          for (Pitch pitch : pitches) {
155              if (pitch.abcName().equals(token)) {
156                  return pitch;
157              }
158          }
159          throw new Exception("### PITCH " + token + " NOT FOUND");
160      }
161  
162      private static void display(Pitch[] pitches) {
163          for (Pitch pitch : pitches) {
164              System.out.println(pitch.toString());
165          }
166      }
167  
168      private static void playMelody(String input, Pitch[] pitches) throws Exception {
169          Scanner scanner = new Scanner(input);
170          while (scanner.hasNext()) {
171              String token = scanner.next();
172              String pitchName;
173              String duration = "";
174              if ( token.indexOf(",") < 0 ) {
175                  pitchName = token.substring(0, 1);
176                  duration = token.substring(1);
177              }
178              else {
179                  pitchName = token.substring(0, 2);
180                  duration = token.substring(2);
181              }
182              if ( duration.length() == 0 ) {
183                  duration = "1";
184              }
185              // REPEAT Again Command
186              if (input.equalsIgnoreCase("AGAIN")) {
187  
188  
189              }
190  
191              Pitch pitch = find(pitchName, pitches);
192              pitch.play(duration);
193  
194          }
195      }
196  
197      // INITIALIZATION, CLEANUP, GETTING INPUT, ERROR MESSAGING
198  
199      static private void showErrorMessage(String message) {
200          miro.setVisible(false);
201          JOptionPane.showMessageDialog(null, message);
202      }
203  
204      private static void initialization() {
205          // ESTABLISH THE PAINTER AND GIVE IT A SUBSTANTIAL BRUSH WIDTH
206          miro = new SPainter("Chromesthesia 2", 500, 500);
207          miro.setVisible(false);
208          miro.setBrushWidth(7);
209          // ESTABLISH PITCH CLASS OBJECTS
210          pitches = establishPitches(miro);
211          display(pitches);
212      }
213  
214      private static String getInput() {
215          miro.setVisible(false);
216          String label = "Please enter a melody in ABC notation, AGAIN or EXIT ....      ";
217          String input = JOptionPane.showInputDialog(null, label);
218          miro.setVisible(true);
219          if (input == null) {
220              input = "";
221          }
222  
223  
224          return input;
225      }
226  
227      static private void cleanup() {
228          int i = 0;
229          System.exit(i);
230      }
231  
232  }
233  // END PROGRAM chorme2
234