1 16 package org.apache.cocoon.serialization; 17 18 import java.io.IOException ; 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 ; 24 import org.xml.sax.SAXException ; 25 26 39 40 public class XMidiSerializer extends AbstractSerializer { 41 private final static String 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 buffer; 57 private boolean buffering = false; 58 59 62 public void recycle() { 63 preventDataWrite = false; 64 state = OUTSIDE_XMIDI; 65 super.recycle(); 66 } 67 68 71 public String getMimeType() { 72 return (mimeType); 73 } 74 75 78 public void startElement( 79 String namespaceURI, 80 String localName, 81 String qName, 82 Attributes atts) 83 throws SAXException { 84 try { 85 if (localName.equals("XMidi")) { 86 if (state != OUTSIDE_XMIDI) { 87 throw new SAXException ("XMidi element not expected here"); 88 } 89 state = INSIDE_XMIDI; 90 String version = atts.getValue("VERSION"); 91 if (version == null) { 92 throw new SAXException ("XMidi element has no version attribute"); 93 } else if (!version.equals(Constants.VERSION)) { 94 throw new SAXException ( 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 ( 105 localName 106 + " element not expected here, state = " 107 + state); 108 } 109 state = INSIDE_CHUNK; 110 writeString(atts.getValue("TYPE"), 4); 111 Integer iLen = new Integer (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 ( 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 ( 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 ( 145 localName 146 + " element not expected here, state = " 147 + state); 148 } 149 state = INSIDE_DELTA; 150 String 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 ( 163 localName 164 + " element not expected here, state = " 165 + state); 166 } 167 state = INSIDE_STATUS; 168 String sval = atts.getValue("SVAL"); 169 writeHex(sval, 1); 170 String 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 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 ( 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 ( 215 localName 216 + " element not expected here, state = " 217 + state); 218 } 219 String pitch = atts.getValue("PITCH"); 220 this.output.write(Utils.stringToInt(pitch)); 221 String 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 ( 228 localName 229 + " element not expected here, state = " 230 + state); 231 } 232 String pitch = atts.getValue("PITCH"); 233 this.output.write(Utils.stringToInt(pitch)); 234 String 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 ( 241 localName 242 + " element not expected here, state = " 243 + state); 244 } 245 String pitch = atts.getValue("PITCH"); 246 this.output.write(Utils.stringToInt(pitch)); 247 String 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 ( 254 localName 255 + " element not expected here, state = " 256 + state); 257 } 258 String cnum = atts.getValue("NUMBER"); 259 this.output.write(Utils.stringToInt(cnum)); 260 String 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 ( 267 localName 268 + " element not expected here, state = " 269 + state); 270 } 271 String 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 ( 278 localName 279 + " element not expected here, state = " 280 + state); 281 } 282 283 String 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 ( 290 localName 291 + " element not expected here, state = " 292 + state); 293 } 294 295 String 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 ( 308 localName 309 + " element not expected here, state = " 310 + state); 311 } 312 buffer = new StringBuffer (); 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 ( 321 localName 322 + " element not expected here, state = " 323 + state); 324 } 325 buffer = new StringBuffer (); 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 (e); 335 } catch (IOException e) { 336 throw new SAXException (e); 337 } 338 } 339 340 343 public void endElement(String namespaceURI, String localName, String qName) 344 throws SAXException { 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 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 sNum = buffer.toString(); 384 Integer iNum = new Integer (sNum); 385 writeHalfWord(iNum.intValue()); 386 this.getLogger().debug(iNum + " tracks"); 387 buffering = false; 388 } else if (localName.equals("PPNQ")) { 389 String 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 (e); 399 } catch (IOException e) { 400 throw new SAXException (e); 401 } 402 } 403 404 void writeString(String s, int len) 405 throws IOException , 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 h, int len) throws ProcessingException, IOException { 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 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 { 455 byte[] b = Utils.intToBa(f, 4); 456 this.output.write(b); 457 } 458 459 void writeHalfWord(int h) throws ProcessingException, IOException { 460 byte[] b = Utils.intToBa(h, 2); 461 this.output.write(b); 462 } 463 464 467 public void characters(char[] str, int arg1, int arg2) 468 throws SAXException { 469 if (buffering) { 470 buffer.append(str, arg1, arg2); 471 } 472 } 473 474 } 475 | Popular Tags |