KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > cocoon > serialization > XMidiSerializer


1 /*
2  * Copyright 1999-2004 The Apache Software Foundation.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */

16 package org.apache.cocoon.serialization;
17
18 import java.io.IOException JavaDoc;
19
20 import org.apache.cocoon.ProcessingException;
21 import org.apache.cocoon.components.midi.xmidi.Constants;
22 import org.apache.cocoon.components.midi.xmidi.Utils;
23 import org.xml.sax.Attributes JavaDoc;
24 import org.xml.sax.SAXException JavaDoc;
25
26 /**
27  * Takes SAX Events and serializes them as a standard MIDI file.
28  *
29  * The MIDI file generation parts of this class are based on code from the XMidi project, written
30  * by Peter Arthur Loeb (http://www.palserv.com/XMidi/) and used with permission.
31  * The warranty disclaimer of the MIT license (http://www.opensource.org/licenses/mit-license.html)
32  * applies to Peter Arthur Loeb's code.
33  *
34  * @author <a HREF="mailto:mark.leicester@energyintellect.com">Mark Leicester</a>
35  * @author <a HREF="mailto:peter@palserv.com">Peter Loeb</a>
36  *
37  * @version CVS $Id: XMidiSerializer.java 123810 2004-12-31 17:07:31Z antonio $
38  */

39
40 public class XMidiSerializer extends AbstractSerializer {
41     private final static String JavaDoc mimeType = "audio/x-midi";
42
43     private final static int OUTSIDE_XMIDI = 0;
44     private final static int INSIDE_XMIDI = 1;
45     private final static int INSIDE_CHUNK = 2;
46     private final static int INSIDE_MTHD = 3;
47     private final static int INSIDE_MTRK = 4;
48     private final static int INSIDE_DELTA = 5;
49     private final static int INSIDE_STATUS = 6;
50     private final static int INSIDE_DELTA_CHANNEL = 7;
51     private final static int INSIDE_STATUS_CHANNEL = 8;
52
53     private int expectedBytes;
54     private boolean preventDataWrite;
55     private int state;
56     private StringBuffer JavaDoc buffer;
57     private boolean buffering = false;
58
59     /* (non-Javadoc)
60     * @see org.apache.avalon.excalibur.pool.Recyclable#recycle()
61     */

62     public void recycle() {
63         preventDataWrite = false;
64         state = OUTSIDE_XMIDI;
65         super.recycle();
66     }
67
68     /* (non-Javadoc)
69      * @see org.apache.cocoon.sitemap.SitemapOutputComponent#getMimeType()
70      */

71     public String JavaDoc getMimeType() {
72         return (mimeType);
73     }
74
75     /* (non-Javadoc)
76      * @see org.xml.sax.ContentHandler#startElement(java.lang.String, java.lang.String, java.lang.String, org.xml.sax.Attributes)
77      */

78     public void startElement(
79         String JavaDoc namespaceURI,
80         String JavaDoc localName,
81         String JavaDoc qName,
82         Attributes JavaDoc atts)
83         throws SAXException JavaDoc {
84         try {
85             if (localName.equals("XMidi")) {
86                 if (state != OUTSIDE_XMIDI) {
87                     throw new SAXException JavaDoc("XMidi element not expected here");
88                 }
89                 state = INSIDE_XMIDI;
90                 String JavaDoc version = atts.getValue("VERSION");
91                 if (version == null) {
92                     throw new SAXException JavaDoc("XMidi element has no version attribute");
93                 } else if (!version.equals(Constants.VERSION)) {
94                     throw new SAXException JavaDoc(
95                         "XMidi element has wrong version: expecting "
96                             + Constants.VERSION
97                             + ", got "
98                             + version);
99                 }
100                 this.getLogger().debug(
101                     "Found XMidi element, version " + version);
102             } else if (localName.equals("CHUNK")) {
103                 if (state != INSIDE_XMIDI) {
104                     throw new SAXException JavaDoc(
105                         localName
106                             + " element not expected here, state = "
107                             + state);
108                 }
109                 state = INSIDE_CHUNK;
110                 writeString(atts.getValue("TYPE"), 4);
111                 Integer JavaDoc iLen = new Integer JavaDoc(atts.getValue("LENGTH"));
112                 writeFullWord(iLen.intValue());
113                 this.getLogger().debug(
114                     "chunk type is: "
115                         + atts.getValue("TYPE")
116                         + " with length "
117                         + atts.getValue("LENGTH"));
118             } else if (localName.equals("MThd")) {
119                 if (state != INSIDE_XMIDI) {
120                     throw new SAXException JavaDoc(
121                         localName
122                             + " element not expected here, state = "
123                             + state);
124                 }
125                 state = INSIDE_MTHD;
126                 writeString(atts.getValue("TYPE"), 4);
127                 writeFullWord(Utils.stringToInt(atts.getValue("LENGTH")));
128                 this.getLogger().debug(
129                     "we have MThd chunk; len = " + atts.getValue("LENGTH"));
130             } else if (localName.equals("MTrk")) {
131                 if (state != INSIDE_XMIDI) {
132                     throw new SAXException JavaDoc(
133                         localName
134                             + " element not expected here, state = "
135                             + state);
136                 }
137                 state = INSIDE_MTRK;
138                 writeString(atts.getValue("TYPE"), 4);
139                 writeFullWord(Utils.stringToInt(atts.getValue("LENGTH")));
140                 this.getLogger().debug(
141                     "we have MTrk chunk; len = " + atts.getValue("LENGTH"));
142             } else if (localName.equals("DELTA")) {
143                 if (state != INSIDE_MTRK) {
144                     throw new SAXException JavaDoc(
145                         localName
146                             + " element not expected here, state = "
147                             + state);
148                 }
149                 state = INSIDE_DELTA;
150                 String JavaDoc dtime = atts.getValue("DTIME");
151                 byte[] hdt = Utils.hexToBa(dtime, 4);
152                 byte[] dt = Utils.intToDelta(hdt);
153                 this.getLogger().debug(
154                     "Delta: "
155                         + dtime
156                         + ", out = "
157                         + Utils.baToHex(dt, 0, dt.length - 1));
158                 this.output.write(dt);
159
160             } else if (localName.equals("STATUS")) {
161                 if (state != INSIDE_DELTA) {
162                     throw new SAXException JavaDoc(
163                         localName
164                             + " element not expected here, state = "
165                             + state);
166                 }
167                 state = INSIDE_STATUS;
168                 String JavaDoc sval = atts.getValue("SVAL");
169                 writeHex(sval, 1);
170                 String JavaDoc sl = atts.getValue("SLEN");
171                 expectedBytes = Utils.stringToInt(sl);
172                 this.getLogger().debug(
173                     "Status: " + sval + ", len = " + expectedBytes);
174                 if (sval.equals("FF")) {
175                     String JavaDoc nmd = atts.getValue("SNMT");
176                     writeHex(nmd, 1);
177                     byte[] hdt = Utils.intToBa(expectedBytes, 4);
178                     byte[] xdt = Utils.intToDelta(hdt);
179                     this.output.write(xdt);
180                     if (expectedBytes == 0) {
181                         preventDataWrite = true;
182                     }
183                     this.getLogger().debug("Non-midi: " + nmd);
184                 } else if (sval.equals("F0")) {
185                     byte[] hdt = Utils.intToBa(Utils.stringToInt(sl), 4);
186                     this.output.write(Utils.intToDelta(hdt));
187                     this.getLogger().debug("Sysex");
188                 } else if (
189                     sval.equals("F6")
190                         | sval.equals("F8")
191                         | sval.equals("FA")
192                         | sval.equals("FB")
193                         | sval.equals("FC")
194                         | sval.equals("FE")) {
195                     preventDataWrite = true;
196                     this.getLogger().debug("no data");
197                 }
198             } else if (localName.equals("CHANNEL")) {
199                 if ((state != INSIDE_DELTA) && (state != INSIDE_STATUS)) {
200                     throw new SAXException JavaDoc(
201                         localName
202                             + " element not expected here, state = "
203                             + state);
204                 }
205                 if (state == INSIDE_DELTA) {
206                     state = INSIDE_DELTA_CHANNEL;
207                 } else if (state == INSIDE_STATUS) {
208                     state = INSIDE_STATUS_CHANNEL;
209                 }
210                 this.getLogger().debug("Channel");
211             } else if (localName.equals("NOTE_OFF")) {
212                 if ((state != INSIDE_DELTA_CHANNEL)
213                     && (state != INSIDE_STATUS_CHANNEL)) {
214                     throw new SAXException JavaDoc(
215                         localName
216                             + " element not expected here, state = "
217                             + state);
218                 }
219                 String JavaDoc pitch = atts.getValue("PITCH");
220                 this.output.write(Utils.stringToInt(pitch));
221                 String JavaDoc vel = atts.getValue("VELOCITY");
222                 this.output.write(Utils.stringToInt(vel));
223                 this.getLogger().debug("Note off - " + pitch + ", " + vel);
224             } else if (localName.equals("NOTE_ON")) {
225                 if ((state != INSIDE_DELTA_CHANNEL)
226                     && (state != INSIDE_STATUS_CHANNEL)) {
227                     throw new SAXException JavaDoc(
228                         localName
229                             + " element not expected here, state = "
230                             + state);
231                 }
232                 String JavaDoc pitch = atts.getValue("PITCH");
233                 this.output.write(Utils.stringToInt(pitch));
234                 String JavaDoc vel = atts.getValue("VELOCITY");
235                 this.output.write(Utils.stringToInt(vel));
236                 this.getLogger().debug("Note on - " + pitch + ", " + vel);
237             } else if (localName.equals("AFTER")) {
238                 if ((state != INSIDE_DELTA_CHANNEL)
239                     && (state != INSIDE_STATUS_CHANNEL)) {
240                     throw new SAXException JavaDoc(
241                         localName
242                             + " element not expected here, state = "
243                             + state);
244                 }
245                 String JavaDoc pitch = atts.getValue("PITCH");
246                 this.output.write(Utils.stringToInt(pitch));
247                 String JavaDoc pres = atts.getValue("PRESSURE");
248                 this.output.write(Utils.stringToInt(pres));
249                 this.getLogger().debug("AFTER - " + pitch + ", " + pres);
250             } else if (localName.equals("CONTROL")) {
251                 if ((state != INSIDE_DELTA_CHANNEL)
252                     && (state != INSIDE_STATUS_CHANNEL)) {
253                     throw new SAXException JavaDoc(
254                         localName
255                             + " element not expected here, state = "
256                             + state);
257                 }
258                 String JavaDoc cnum = atts.getValue("NUMBER");
259                 this.output.write(Utils.stringToInt(cnum));
260                 String JavaDoc val = atts.getValue("VALUE");
261                 this.output.write(Utils.stringToInt(val));
262                 this.getLogger().debug("CONTROL - " + cnum + ", " + val);
263             } else if (localName.equals("PROGRAM")) {
264                 if ((state != INSIDE_DELTA_CHANNEL)
265                     && (state != INSIDE_STATUS_CHANNEL)) {
266                     throw new SAXException JavaDoc(
267                         localName
268                             + " element not expected here, state = "
269                             + state);
270                 }
271                 String JavaDoc patch = atts.getValue("NUMBER");
272                 this.output.write(Utils.stringToInt(patch));
273                 this.getLogger().debug("PATCH - " + patch);
274             } else if (localName.equals("PRESSURE")) {
275                 if ((state != INSIDE_DELTA_CHANNEL)
276                     && (state != INSIDE_STATUS_CHANNEL)) {
277                     throw new SAXException JavaDoc(
278                         localName
279                             + " element not expected here, state = "
280                             + state);
281                 }
282
283                 String JavaDoc amt = atts.getValue("AMOUNT");
284                 this.output.write(Utils.stringToInt(amt));
285                 this.getLogger().debug("PRESSURE - " + amt);
286             } else if (localName.equals("WHEEL")) {
287                 if ((state != INSIDE_DELTA_CHANNEL)
288                     && (state != INSIDE_STATUS_CHANNEL)) {
289                     throw new SAXException JavaDoc(
290                         localName
291                             + " element not expected here, state = "
292                             + state);
293                 }
294
295                 String JavaDoc amt = atts.getValue("AMOUNT");
296                 int a = Utils.stringToInt(amt);
297                 int b = a;
298                 int c = a;
299                 b &= 127;
300                 c >>= 7;
301                 this.output.write(c);
302                 this.output.write(b);
303                 this.getLogger().debug(
304                     "Wheel - " + a + ": (" + c + "," + a + ")");
305             } else if (localName.equals("EDATA")) {
306                 if ((state != INSIDE_DELTA) && (state != INSIDE_STATUS)) {
307                     throw new SAXException JavaDoc(
308                         localName
309                             + " element not expected here, state = "
310                             + state);
311                 }
312                 buffer = new StringBuffer JavaDoc();
313                 buffering = true;
314                 this.getLogger().debug("EDATA (element, not text)");
315             } else if (
316                 localName.equals("FORMAT")
317                     || localName.equals("TRACKS")
318                     || localName.equals("PPNQ")) {
319                 if (state != INSIDE_MTHD) {
320                     throw new SAXException JavaDoc(
321                         localName
322                             + " element not expected here, state = "
323                             + state);
324                 }
325                 buffer = new StringBuffer JavaDoc();
326                 buffering = true;
327                 this.getLogger().debug(localName + " element");
328             } else {
329                 this.getLogger().debug(
330                     "Found " + localName + ", in state " + state);
331             }
332
333         } catch (ProcessingException e) {
334             throw new SAXException JavaDoc(e);
335         } catch (IOException JavaDoc e) {
336             throw new SAXException JavaDoc(e);
337         }
338     }
339
340     /* (non-Javadoc)
341        * @see org.xml.sax.ContentHandler#endElement(java.lang.String, java.lang.String, java.lang.String)
342        */

343     public void endElement(String JavaDoc namespaceURI, String JavaDoc localName, String JavaDoc qName)
344         throws SAXException JavaDoc {
345         try {
346             if (localName.equals("CHUNK")) {
347                 state = INSIDE_XMIDI;
348             } else if (localName.equals("MThd") || localName.equals("MTrk")) {
349                 state = INSIDE_XMIDI;
350             } else if (localName.equals("DELTA")) {
351                 state = INSIDE_MTRK;
352             } else if (localName.equals("STATUS")) {
353                 state = INSIDE_DELTA;
354             } else if (localName.equals("CHANNEL")) {
355                 if (state == INSIDE_STATUS_CHANNEL) {
356                     state = INSIDE_STATUS;
357                 } else if (state == INSIDE_DELTA_CHANNEL) {
358                     state = INSIDE_DELTA;
359                 }
360             } else if (localName.equals("EDATA")) {
361                 if (!preventDataWrite) {
362                     writeHex(buffer.toString(), expectedBytes);
363                     this.getLogger().debug("EDATA: " + buffer.toString());
364                 } else {
365                     preventDataWrite = false;
366                 }
367                 buffering = false;
368             } else if (localName.equals("FORMAT")) {
369                 String JavaDoc typ = buffer.toString();
370                 for (int i = 0; i < typ.length(); i++) {
371                     if (typ.substring(i, i + 1).compareTo("0")
372                         < 0 || typ.substring(i, i + 1).compareTo("9")
373                         > 0) {
374                         throw new ProcessingException(
375                             "Invalid numeric midi format: " + typ);
376                     }
377                 }
378                 int midiFormat = Utils.stringToInt(typ);
379                 writeHalfWord(midiFormat);
380                 this.getLogger().debug("Format is " + midiFormat);
381                 buffering = false;
382             } else if (localName.equals("TRACKS")) {
383                 String JavaDoc sNum = buffer.toString();
384                 Integer JavaDoc iNum = new Integer JavaDoc(sNum);
385                 writeHalfWord(iNum.intValue());
386                 this.getLogger().debug(iNum + " tracks");
387                 buffering = false;
388             } else if (localName.equals("PPNQ")) {
389                 String JavaDoc sPNQ = buffer.toString();
390                 writeHex(sPNQ, 2);
391                 this.getLogger().debug("PPNQ is " + sPNQ);
392                 buffering = false;
393             } else if (localName.equals("HEXDATA")) {
394                 writeHex(buffer.toString(), buffer.length() / 2);
395                 buffering = false;
396             }
397         } catch (ProcessingException e) {
398             throw new SAXException JavaDoc(e);
399         } catch (IOException JavaDoc e) {
400             throw new SAXException JavaDoc(e);
401         }
402     }
403
404     void writeString(String JavaDoc s, int len)
405         throws IOException JavaDoc, ProcessingException {
406         int l = s.length();
407         if (l != len) {
408             throw new ProcessingException(
409                 "writeString; string length ("
410                     + l
411                     + ") != expected length ("
412                     + len
413                     + ")");
414         }
415         this.output.write(s.getBytes());
416     }
417
418     void writeHex(String JavaDoc h, int len) throws ProcessingException, IOException JavaDoc {
419         int l = h.length();
420         int cnt = 0;
421         int bs = 0;
422         int bc = 0;
423         for (int i = 0; i < l; i++) {
424             String JavaDoc s = h.substring(i, i + 1);
425             int x = " \n\t\r".indexOf(s);
426             if (x != -1) {
427                 continue;
428             }
429             int tmp = "0123456789ABCDEF".indexOf(s.toUpperCase());
430             if (bc == 0) {
431                 bs = tmp;
432             } else if (bc == 1) {
433                 bs <<= 4;
434                 bs |= tmp;
435             } else {
436                 throw new ProcessingException("writeHex; internal error");
437             }
438             bc++;
439             if (bc >= 2) {
440                 this.output.write(bs);
441                 cnt++;
442                 bc = 0;
443             }
444         }
445         if (bc != 0) {
446             throw new ProcessingException("un-even number of hex digits");
447         }
448         if (cnt != len) {
449             throw new ProcessingException(
450                 "writeHex count (" + cnt + ") != length (" + len + ")");
451         }
452     }
453
454     void writeFullWord(int f) throws ProcessingException, IOException JavaDoc {
455         byte[] b = Utils.intToBa(f, 4);
456         this.output.write(b);
457     }
458
459     void writeHalfWord(int h) throws ProcessingException, IOException JavaDoc {
460         byte[] b = Utils.intToBa(h, 2);
461         this.output.write(b);
462     }
463
464     /* (non-Javadoc)
465      * @see org.xml.sax.ContentHandler#characters(char[], int, int)
466      */

467     public void characters(char[] str, int arg1, int arg2)
468         throws SAXException JavaDoc {
469         if (buffering) {
470             buffer.append(str, arg1, arg2);
471         }
472     }
473
474 }
475
Popular Tags