0001: /*
0002: * Licensed to the Apache Software Foundation (ASF) under one or more
0003: * contributor license agreements. See the NOTICE file distributed with
0004: * this work for additional information regarding copyright ownership.
0005: * The ASF licenses this file to You under the Apache License, Version 2.0
0006: * (the "License"); you may not use this file except in compliance with
0007: * the License. You may obtain a copy of the License at
0008: *
0009: * http://www.apache.org/licenses/LICENSE-2.0
0010: *
0011: * Unless required by applicable law or agreed to in writing, software
0012: * distributed under the License is distributed on an "AS IS" BASIS,
0013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
0014: * See the License for the specific language governing permissions and
0015: * limitations under the License.
0016: */
0017: package org.apache.cocoon.generation;
0018:
0019: import java.io.IOException;
0020: import java.io.InputStream;
0021: import java.util.Hashtable;
0022: import java.util.Map;
0023:
0024: import org.apache.avalon.framework.parameters.ParameterException;
0025: import org.apache.avalon.framework.parameters.Parameterizable;
0026: import org.apache.avalon.framework.parameters.Parameters;
0027: import org.apache.cocoon.ProcessingException;
0028: import org.apache.cocoon.components.midi.xmidi.ByteLen;
0029: import org.apache.cocoon.components.midi.xmidi.Utils;
0030: import org.apache.cocoon.components.midi.xmidi.Constants;
0031: import org.apache.cocoon.components.source.SourceUtil;
0032: import org.apache.cocoon.environment.SourceResolver;
0033: import org.apache.excalibur.source.Source;
0034: import org.apache.excalibur.source.SourceException;
0035: import org.apache.excalibur.source.SourceNotFoundException;
0036: import org.apache.excalibur.source.SourceValidity;
0037: import org.xml.sax.SAXException;
0038: import org.xml.sax.helpers.AttributesImpl;
0039:
0040: /**
0041: * Reads a standard MIDI file and generates SAX Events.
0042: *
0043: * The MIDI file parsing parts of this class are based on code from the XMidi project, written
0044: * by Peter Arthur Loeb (http://www.palserv.com/XMidi/) and used with permission.
0045: * The warranty disclaimer of the MIT license (http://www.opensource.org/licenses/mit-license.html)
0046: * applies to Peter Arthur Loeb's code.
0047: *
0048: * @author <a href="mailto:mark.leicester@energyintellect.com">Mark Leicester</a>
0049: * @author <a href="mailto:peter@palserv.com">Peter Loeb</a>
0050: */
0051:
0052: public class XMidiGenerator extends AbstractGenerator implements
0053: Parameterizable {
0054:
0055: /** The input source */
0056: protected Source inputSource;
0057: private boolean global_verbose;
0058: private boolean local_verbose;
0059:
0060: private static final boolean VERBOSE_DEFAULT = true;
0061:
0062: private boolean validateVerbosity(String verbose) {
0063: if (verbose.equalsIgnoreCase("TRUE")) {
0064: return true;
0065: } else if (verbose.equalsIgnoreCase("FALSE")) {
0066: return false;
0067: }
0068: return VERBOSE_DEFAULT;
0069: }
0070:
0071: public void parameterize(Parameters parameters)
0072: throws ParameterException {
0073: global_verbose = validateVerbosity(parameters.getParameter(
0074: "verbose", String.valueOf(VERBOSE_DEFAULT)));
0075: }
0076:
0077: /**
0078: * Recycle this component.
0079: * All instance variables are set to <code>null</code>.
0080: */
0081: public void recycle() {
0082: if (null != this .inputSource) {
0083: super .resolver.release(this .inputSource);
0084: this .inputSource = null;
0085: }
0086: // Reinitialize variables
0087: initializeVariables();
0088: super .recycle();
0089: }
0090:
0091: /**
0092: * Setup the MIDI file generator.
0093: * Try to get the last modification date of the source for caching.
0094: */
0095: public void setup(SourceResolver resolver, Map objectModel,
0096: String src, Parameters par) throws ProcessingException,
0097: SAXException, IOException {
0098: super .setup(resolver, objectModel, src, par);
0099: // Initialize lookup tables
0100: initializeLookupTables();
0101: try {
0102: this .inputSource = resolver.resolveURI(src);
0103: } catch (SourceException se) {
0104: throw SourceUtil.handle("Error resolving '" + src + "'.",
0105: se);
0106: }
0107: local_verbose = validateVerbosity(parameters.getParameter(
0108: "verbose", String.valueOf(global_verbose)));
0109: }
0110:
0111: /**
0112: * Generate the unique key.
0113: * This key must be unique inside the space of this component.
0114: *
0115: * @return The generated key hashes the src
0116: */
0117: public java.io.Serializable getKey() {
0118: return this .inputSource.getURI();
0119: }
0120:
0121: /**
0122: * Generate the validity object.
0123: *
0124: * @return The generated validity object or <code>null</code> if the
0125: * component is currently not cacheable.
0126: */
0127: public SourceValidity getValidity() {
0128: return this .inputSource.getValidity();
0129: }
0130:
0131: /**
0132: * Generate XML data.
0133: */
0134: public void generate() throws IOException, SAXException,
0135: ProcessingException {
0136: try {
0137: if (getLogger().isDebugEnabled()) {
0138: getLogger().debug(
0139: "Source " + super .source + " resolved to "
0140: + this .inputSource.getURI());
0141: }
0142:
0143: parseMIDI(this .inputSource);
0144: } catch (SAXException e) {
0145: SourceUtil.handleSAXException(this .inputSource.getURI(), e);
0146: }
0147: }
0148:
0149: private int midiFormat;
0150: private Hashtable ffHash;
0151: private String[] chanArray;
0152: private String[] fArray;
0153: private int numTracks;
0154: private int trkCount;
0155: private String[] notes;
0156: private int[] register;
0157: private Hashtable contHash;
0158:
0159: /**
0160: * @param source
0161: */
0162: private void parseMIDI(Source source) throws SAXException,
0163: SourceNotFoundException, IOException, ProcessingException {
0164: InputStream inputStream = source.getInputStream();
0165:
0166: AttributesImpl attr = new AttributesImpl();
0167: String text = "";
0168: this .contentHandler.startDocument();
0169: attr.addAttribute("", "VERSION", "VERSION", "CDATA",
0170: Constants.VERSION);
0171: this .contentHandler.startElement("", "XMidi", "XMidi", attr);
0172:
0173: boolean chunkFlag = true;
0174: while (chunkFlag) { // for each chunk
0175: byte[] hdr = new byte[8];
0176:
0177: int r;
0178:
0179: r = inputStream.read(hdr);
0180: if (r == -1) {
0181: chunkFlag = false;
0182: continue;
0183: }
0184: if (r < 8) {
0185: this .getLogger().debug("Getting header");
0186: }
0187:
0188: // get chunk id
0189: String cid = Utils.baToString(hdr, 0, 3);
0190:
0191: // get chunk length
0192: int len = Utils.baToInt(hdr, 4, 7);
0193:
0194: // get rest of chunk
0195: byte[] dta = new byte[len];
0196: r = inputStream.read(dta);
0197: if (r < len) {
0198: throw new ProcessingException("Getting data");
0199: }
0200:
0201: if (cid.equals("MThd")) {
0202: attr.clear();
0203: attr.addAttribute("", "LENGTH", "LENGTH", "CDATA", ""
0204: + len);
0205: attr.addAttribute("", "TYPE", "TYPE", "CDATA", cid);
0206: this .contentHandler.startElement("", "MThd", "MThd",
0207: attr);
0208:
0209: midiFormat = Utils.baToInt(dta, 0, 1);
0210: numTracks = Utils.baToInt(dta, 2, 3);
0211: String pnq = Utils.baToHex(dta, 4, 5);
0212:
0213: attr.clear();
0214:
0215: this .contentHandler.startElement("", "FORMAT",
0216: "FORMAT", attr);
0217: text = "" + midiFormat;
0218: this .contentHandler.characters(text.toCharArray(), 0,
0219: text.length());
0220: this .contentHandler.endElement("", "FORMAT", "FORMAT");
0221:
0222: this .contentHandler.startElement("", "TRACKS",
0223: "TRACKS", attr);
0224: text = "" + numTracks;
0225: this .contentHandler.characters(text.toCharArray(), 0,
0226: text.length());
0227: this .contentHandler.endElement("", "TRACKS", "TRACKS");
0228:
0229: this .contentHandler.startElement("", "PPNQ", "PPNQ",
0230: attr);
0231: text = pnq;
0232: this .contentHandler.characters(text.toCharArray(), 0,
0233: text.length());
0234: this .contentHandler.endElement("", "PPNQ", "PPNQ");
0235:
0236: this .contentHandler.endElement("", "MThd", "MThd");
0237:
0238: } else if (cid.equals("MTrk")) {
0239: trkCount++;
0240: if (trkCount > numTracks) {
0241: throw new ProcessingException("too many tracks");
0242: }
0243: attr.clear();
0244: attr.addAttribute("", "LENGTH", "LENGTH", "CDATA", ""
0245: + len);
0246: attr.addAttribute("", "TYPE", "TYPE", "CDATA", cid);
0247: this .contentHandler.startElement("", "MTrk", "MTrk",
0248: attr);
0249: doTrack(dta, len);
0250: this .contentHandler.endElement("", "MTrk", "MTrk");
0251: } else {
0252: attr.clear();
0253: attr.addAttribute("", "LENGTH", "LENGTH", "CDATA", ""
0254: + len);
0255: attr.addAttribute("", "TYPE", "TYPE", "CDATA", cid);
0256: this .contentHandler.startElement("", "CHUNK", "CHUNK",
0257: attr);
0258: doHexData(dta, len);
0259: this .contentHandler.endElement("", "CHUNK", "CHUNK");
0260: }
0261:
0262: }
0263:
0264: this .contentHandler.endElement("", "XMidi", "XMidi");
0265: this .contentHandler.endDocument();
0266: }
0267:
0268: void initializeVariables() {
0269: numTracks = 0;
0270: trkCount = 0;
0271: }
0272:
0273: void initializeLookupTables() {
0274: ffHash = new Hashtable();
0275: ffHash.put("00", "Sequence Number");
0276: ffHash.put("01", "Text");
0277: ffHash.put("02", "Copyright");
0278: ffHash.put("03", "Sequence/Track Name");
0279: ffHash.put("04", "Instrument");
0280: ffHash.put("05", "Lyric");
0281: ffHash.put("06", "Marker");
0282: ffHash.put("07", "Cue Point");
0283: ffHash.put("20", "MIDI Channel");
0284: ffHash.put("21", "MIDI Port");
0285: ffHash.put("2F", "End of Track");
0286: ffHash.put("51", "Tempo");
0287: ffHash.put("54", "SMPTE Offset");
0288: ffHash.put("58", "Time Signature");
0289: ffHash.put("59", "Key Signature");
0290: ffHash.put("7F", "Proprietary Event");
0291:
0292: chanArray = new String[7];
0293: chanArray[0] = "Note Off";
0294: chanArray[1] = "Note On";
0295: chanArray[2] = "After Touch";
0296: chanArray[3] = "Control Change";
0297: chanArray[4] = "Program Change";
0298: chanArray[5] = "Channel Pressure";
0299: chanArray[6] = "Pitch Wheel";
0300:
0301: fArray = new String[16];
0302: fArray[0] = "SYSEX";
0303: fArray[1] = "MTC Quarter Frame Message";
0304: fArray[2] = "Song Position Pointer";
0305: fArray[3] = "Song Select";
0306: fArray[4] = "Undefined";
0307: fArray[5] = "Undefined";
0308: fArray[6] = "Tune Request";
0309: fArray[7] = "Unsupported";
0310: fArray[8] = "MIDI Clock";
0311: fArray[9] = "Undefined";
0312: fArray[10] = "MIDI Start";
0313: fArray[11] = "MIDI Continue";
0314: fArray[12] = "MIDI Stop";
0315: fArray[13] = "Undefined";
0316: fArray[14] = "Active Sense";
0317: fArray[15] = "NotUnderstood";
0318:
0319: contHash = new Hashtable();
0320: contHash.put("0", "Bank Select");
0321: contHash.put("1", "Modulation Wheel (coarse)");
0322: contHash.put("2", "Breath controller (coarse)");
0323: contHash.put("4", "Foot Pedal (coarse)");
0324: contHash.put("5", "Portamento Time (coarse)");
0325: contHash.put("6", "Data Entry (coarse)");
0326: contHash.put("7", "Volume (coarse)");
0327: contHash.put("8", "Balance (coarse)");
0328: contHash.put("10", "Pan position (coarse)");
0329: contHash.put("11", "Expression (coarse)");
0330: contHash.put("12", "Effect Control 1 (coarse)");
0331: contHash.put("13", "Effect Control 2 (coarse)");
0332: contHash.put("16", "General Purpose Slider 1");
0333: contHash.put("17", "General Purpose Slider 2");
0334: contHash.put("18", "General Purpose Slider 3");
0335: contHash.put("19", "General Purpose Slider 4");
0336: contHash.put("32", "Bank Select (fine)");
0337: contHash.put("33", "Modulation Wheel (fine)");
0338: contHash.put("34", "Breath controller (fine)");
0339: contHash.put("36", "Foot Pedal (fine)");
0340: contHash.put("37", "Portamento Time (fine)");
0341: contHash.put("38", "Data Entry (fine)");
0342: contHash.put("39", "Volume (fine)");
0343: contHash.put("40", "Balance (fine)");
0344: contHash.put("42", "Pan position (fine)");
0345: contHash.put("43", "Expression (fine)");
0346: contHash.put("44", "Effect Control 1 (fine)");
0347: contHash.put("45", "Effect Control 2 (fine)");
0348: contHash.put("64", "Hold Pedal (on/off)");
0349: contHash.put("65", "Portamento (on/off)");
0350: contHash.put("66", "Sustenuto Pedal (on/off)");
0351: contHash.put("67", "Soft Pedal (on/off)");
0352: contHash.put("68", "Legato Pedal (on/off)");
0353: contHash.put("69", "Hold 2 Pedal (on/off)");
0354: contHash.put("70", "Sound Variation");
0355: contHash.put("71", "Sound Timbre");
0356: contHash.put("72", "Sound Release Time");
0357: contHash.put("73", "Sound Attack Time");
0358: contHash.put("74", "Sound Brightness");
0359: contHash.put("75", "Sound Control 6");
0360: contHash.put("76", "Sound Control 7");
0361: contHash.put("77", "Sound Control 8");
0362: contHash.put("78", "Sound Control 9");
0363: contHash.put("79", "Sound Control 10");
0364: contHash.put("80", "General Purpose Button 1 (on/off)");
0365: contHash.put("81", "General Purpose Button 2 (on/off)");
0366: contHash.put("82", "General Purpose Button 3 (on/off)");
0367: contHash.put("83", "General Purpose Button 4 (on/off)");
0368: contHash.put("91", "Effects Level");
0369: contHash.put("92", "Tremulo Level");
0370: contHash.put("93", "Chorus Level");
0371: contHash.put("94", "Celeste Level");
0372: contHash.put("95", "Phaser Level");
0373: contHash.put("96", "Data Button increment");
0374: contHash.put("97", "Data Button decrement");
0375: contHash.put("98", "Non-registered Parameter (fine)");
0376: contHash.put("99", "Non-registered Parameter (coarse)");
0377: contHash.put("100", "Registered Parameter (fine)");
0378: contHash.put("101", "Registered Parameter (coarse)");
0379: contHash.put("120", "All Sound Off");
0380: contHash.put("121", "All Controllers Off");
0381: contHash.put("122", "Local Keyboard (on/off)");
0382: contHash.put("123", "All Notes Off");
0383: contHash.put("124", "Omni Mode Off");
0384: contHash.put("125", "Omni Mode On");
0385: contHash.put("126", "Mono Operation");
0386: contHash.put("127", "Poly Operation");
0387:
0388: notes = new String[128];
0389: register = new int[128];
0390: notes[0] = "C";
0391: register[0] = -5;
0392: notes[1] = "C#";
0393: register[1] = -5;
0394: notes[2] = "D";
0395: register[2] = -5;
0396: notes[3] = "Eb";
0397: register[3] = -5;
0398: notes[4] = "E";
0399: register[4] = -5;
0400: notes[5] = "F";
0401: register[5] = -5;
0402: notes[6] = "F#";
0403: register[6] = -5;
0404: notes[7] = "G";
0405: register[7] = -5;
0406: notes[8] = "Ab";
0407: register[8] = -5;
0408: notes[9] = "A";
0409: register[9] = -5;
0410: notes[10] = "Bb";
0411: register[10] = -5;
0412: notes[11] = "B";
0413: register[11] = -5;
0414: notes[12] = "C";
0415: register[12] = -4;
0416: notes[13] = "C#";
0417: register[13] = -4;
0418: notes[14] = "D";
0419: register[14] = -4;
0420: notes[15] = "Eb";
0421: register[15] = -4;
0422: notes[16] = "E";
0423: register[16] = -4;
0424: notes[17] = "F";
0425: register[17] = -4;
0426: notes[18] = "F#";
0427: register[18] = -4;
0428: notes[19] = "G";
0429: register[19] = -4;
0430: notes[20] = "Ab";
0431: register[20] = -4;
0432: notes[21] = "A";
0433: register[21] = -4;
0434: notes[22] = "Bb";
0435: register[22] = -4;
0436: notes[23] = "B";
0437: register[23] = -4;
0438: notes[24] = "C";
0439: register[24] = -3;
0440: notes[25] = "C#";
0441: register[25] = -3;
0442: notes[26] = "D";
0443: register[26] = -3;
0444: notes[27] = "Eb";
0445: register[27] = -3;
0446: notes[28] = "E";
0447: register[28] = -3;
0448: notes[29] = "F";
0449: register[29] = -3;
0450: notes[30] = "F#";
0451: register[30] = -3;
0452: notes[31] = "G";
0453: register[31] = -3;
0454: notes[32] = "Ab";
0455: register[32] = -3;
0456: notes[33] = "A";
0457: register[33] = -3;
0458: notes[34] = "Bb";
0459: register[34] = -3;
0460: notes[35] = "B";
0461: register[35] = -3;
0462: notes[36] = "C";
0463: register[36] = -2;
0464: notes[37] = "C#";
0465: register[37] = -2;
0466: notes[38] = "D";
0467: register[38] = -2;
0468: notes[39] = "Eb";
0469: register[39] = -2;
0470: notes[40] = "E";
0471: register[40] = -2;
0472: notes[41] = "F";
0473: register[41] = -2;
0474: notes[42] = "F#";
0475: register[42] = -2;
0476: notes[43] = "G";
0477: register[43] = -2;
0478: notes[44] = "Ab";
0479: register[44] = -2;
0480: notes[45] = "A";
0481: register[45] = -2;
0482: notes[46] = "Bb";
0483: register[46] = -2;
0484: notes[47] = "B";
0485: register[47] = -2;
0486: notes[48] = "C";
0487: register[48] = -1;
0488: notes[49] = "C#";
0489: register[49] = -1;
0490: notes[50] = "D";
0491: register[50] = -1;
0492: notes[51] = "Eb";
0493: register[51] = -1;
0494: notes[52] = "E";
0495: register[52] = -1;
0496: notes[53] = "F";
0497: register[53] = -1;
0498: notes[54] = "F#";
0499: register[54] = -1;
0500: notes[55] = "G";
0501: register[55] = -1;
0502: notes[56] = "Ab";
0503: register[56] = -1;
0504: notes[57] = "A";
0505: register[57] = -1;
0506: notes[58] = "Bb";
0507: register[58] = -1;
0508: notes[59] = "B";
0509: register[59] = -1;
0510: notes[60] = "C";
0511: register[60] = 0;
0512: notes[61] = "C#";
0513: register[61] = 0;
0514: notes[62] = "D";
0515: register[62] = 0;
0516: notes[63] = "Eb";
0517: register[63] = 0;
0518: notes[64] = "E";
0519: register[64] = 0;
0520: notes[65] = "F";
0521: register[65] = 0;
0522: notes[66] = "F#";
0523: register[66] = 0;
0524: notes[67] = "G";
0525: register[67] = 0;
0526: notes[68] = "Ab";
0527: register[68] = 0;
0528: notes[69] = "A";
0529: register[69] = 0;
0530: notes[70] = "Bb";
0531: register[70] = 0;
0532: notes[71] = "B";
0533: register[71] = 0;
0534: notes[72] = "C";
0535: register[72] = 1;
0536: notes[73] = "C#";
0537: register[73] = 1;
0538: notes[74] = "D";
0539: register[74] = 1;
0540: notes[75] = "Eb";
0541: register[75] = 1;
0542: notes[76] = "E";
0543: register[76] = 1;
0544: notes[77] = "F";
0545: register[77] = 1;
0546: notes[78] = "F#";
0547: register[78] = 1;
0548: notes[79] = "G";
0549: register[79] = 1;
0550: notes[80] = "Ab";
0551: register[80] = 1;
0552: notes[81] = "A";
0553: register[81] = 1;
0554: notes[82] = "Bb";
0555: register[82] = 1;
0556: notes[83] = "B";
0557: register[83] = 1;
0558: notes[84] = "C";
0559: register[84] = 2;
0560: notes[85] = "C#";
0561: register[85] = 2;
0562: notes[86] = "D";
0563: register[86] = 2;
0564: notes[87] = "Eb";
0565: register[87] = 2;
0566: notes[88] = "E";
0567: register[88] = 2;
0568: notes[89] = "F";
0569: register[89] = 2;
0570: notes[90] = "F#";
0571: register[90] = 2;
0572: notes[91] = "G";
0573: register[91] = 2;
0574: notes[92] = "Ab";
0575: register[92] = 2;
0576: notes[93] = "A";
0577: register[93] = 2;
0578: notes[94] = "Bb";
0579: register[94] = 2;
0580: notes[95] = "B";
0581: register[95] = 2;
0582: notes[96] = "C";
0583: register[96] = 3;
0584: notes[97] = "C#";
0585: register[97] = 3;
0586: notes[98] = "D";
0587: register[98] = 3;
0588: notes[99] = "Eb";
0589: register[99] = 3;
0590: notes[100] = "E";
0591: register[100] = 3;
0592: notes[101] = "F";
0593: register[101] = 3;
0594: notes[102] = "F#";
0595: register[102] = 3;
0596: notes[103] = "G";
0597: register[103] = 3;
0598: notes[104] = "Ab";
0599: register[104] = 3;
0600: notes[105] = "A";
0601: register[105] = 3;
0602: notes[106] = "Bb";
0603: register[106] = 3;
0604: notes[107] = "B";
0605: register[107] = 3;
0606: notes[108] = "C";
0607: register[108] = 4;
0608: notes[109] = "C#";
0609: register[109] = 4;
0610: notes[110] = "D";
0611: register[110] = 4;
0612: notes[111] = "Eb";
0613: register[111] = 4;
0614: notes[112] = "E";
0615: register[112] = 4;
0616: notes[113] = "F";
0617: register[113] = 4;
0618: notes[114] = "F#";
0619: register[114] = 4;
0620: notes[115] = "G";
0621: register[115] = 4;
0622: notes[116] = "Ab";
0623: register[116] = 4;
0624: notes[117] = "A";
0625: register[117] = 4;
0626: notes[118] = "Bb";
0627: register[118] = 4;
0628: notes[119] = "B";
0629: register[119] = 4;
0630: notes[120] = "C";
0631: register[120] = 5;
0632: notes[121] = "C#";
0633: register[121] = 5;
0634: notes[122] = "D";
0635: register[122] = 5;
0636: notes[123] = "Eb";
0637: register[123] = 5;
0638: notes[124] = "E";
0639: register[124] = 5;
0640: notes[125] = "F";
0641: register[125] = 5;
0642: notes[126] = "F#";
0643: register[126] = 5;
0644: notes[127] = "G";
0645: register[127] = 5;
0646: }
0647:
0648: /**
0649: * Add track data to DOM structure
0650: */
0651: void doTrack(byte[] dta, int len) throws SAXException,
0652: ProcessingException {
0653: AttributesImpl attr = new AttributesImpl();
0654: String text = "";
0655:
0656: boolean tFlag = true;
0657: int offset = 0;
0658:
0659: // initialize variables
0660: String edata = null;
0661: String snam = null;
0662: String nmData = "";
0663: String ctag = null;
0664: String ctagAttr[] = new String[4];
0665: String ctagAttrVal[] = new String[4];
0666: int ctagnum = 0;
0667: String cnum = null;
0668: String ctyp = null;
0669: int slen = 0;
0670: int chanType = 0;
0671: int hiName = 0;
0672: int status = 0;
0673: int noff = 0;
0674: String sval = null;
0675: boolean ecFlag = true; // assume edata
0676: boolean nFlag = true; // use slen for noff
0677: boolean firstTime = true;
0678:
0679: while (tFlag) {
0680: // do delta
0681: ByteLen bl = Utils.deltaToInt(dta, offset);
0682: offset += bl.len;
0683: String deltaLen = Utils.baToHex(bl.ba, 0, 3);
0684:
0685: nFlag = true; // assume simple (slen) offset
0686: // may or may not be status
0687: boolean statFlag = false;
0688: int first = Utils.baToInt(dta, offset, offset);
0689: this .getLogger().debug(
0690: "doTrack: in loop; deltaLen=" + deltaLen + ", len="
0691: + bl.len + ", first=" + first);
0692:
0693: if ((first & 128) == 128) {
0694: // it is a status byte
0695: statFlag = true;
0696: sval = Utils.baToHex(dta, offset, offset);
0697: status = first;
0698: this .getLogger().debug("doTrack: have status: " + sval);
0699: if (status < 240 && status > 127) {
0700: ecFlag = false;
0701: chanType = (status - 128) / 16;
0702: snam = chanArray[chanType];
0703: ctyp = sval.substring(0, 1);
0704: cnum = sval.substring(1, 2);
0705: } else {
0706: ecFlag = true;
0707: if (status > 239 && status < 256) {
0708: hiName = status - 240;
0709: snam = fArray[hiName];
0710: } else {
0711: throw new ProcessingException(
0712: "Invalid status: " + status);
0713: }
0714: }
0715: offset++;
0716: } else {
0717: this .getLogger().debug("doTrack: running status");
0718: }
0719:
0720: nmData = "";
0721: if (firstTime) {
0722: firstTime = false;
0723: if (!statFlag) {
0724: throw new ProcessingException(
0725: "first time, but no status; first = "
0726: + first);
0727: }
0728: }
0729:
0730: // offset points to the byte after the status
0731: // or first byte of "running status"
0732:
0733: attr.clear();
0734: attr.addAttribute("", "DTIME", "DTIME", "CDATA", ""
0735: + deltaLen);
0736: this .contentHandler
0737: .startElement("", "DELTA", "DELTA", attr);
0738:
0739: if (status > 127 && status < 144) {
0740: // note off
0741: slen = 2;
0742: // set up tag
0743: int pitch = Utils.baToInt(dta, offset, offset);
0744: int vel = Utils.baToInt(dta, offset + 1, offset + 1);
0745: ctag = "NOTE_OFF";
0746: ctagAttr[0] = "PITCH";
0747: ctagAttrVal[0] = "" + pitch;
0748: ctagAttr[1] = "VELOCITY";
0749: ctagAttrVal[1] = "" + vel;
0750: ctagnum = 2;
0751: if (local_verbose) {
0752: ctagAttr[2] = "NAME";
0753: ctagAttrVal[2] = notes[pitch];
0754: ctagAttr[3] = "REGISTER";
0755: ctagAttrVal[3] = "" + register[pitch];
0756: ctagnum = 4;
0757: }
0758: } else if (status > 143 && status < 160) {
0759: // note on
0760: slen = 2;
0761: int pitch = Utils.baToInt(dta, offset, offset);
0762: int vel = Utils.baToInt(dta, offset + 1, offset + 1);
0763: ctag = "NOTE_ON";
0764: ctagAttr[0] = "PITCH";
0765: ctagAttrVal[0] = "" + pitch;
0766: ctagAttr[1] = "VELOCITY";
0767: ctagAttrVal[1] = "" + vel;
0768: ctagnum = 2;
0769: if (local_verbose) {
0770: ctagAttr[2] = "NAME";
0771: ctagAttrVal[2] = notes[pitch];
0772: ctagAttr[3] = "REGISTER";
0773: ctagAttrVal[3] = "" + register[pitch];
0774: ctagnum = 4;
0775: }
0776: } else if (status > 159 && status < 176) {
0777: // after touch
0778: slen = 2;
0779: int pitch = Utils.baToInt(dta, offset, offset);
0780: int pres = Utils.baToInt(dta, offset + 1, offset + 1);
0781: ctag = "AFTER";
0782: ctagAttr[0] = "PITCH";
0783: ctagAttrVal[0] = "" + pitch;
0784: ctagAttr[1] = "PRESSURE";
0785: ctagAttrVal[1] = "" + pres;
0786: ctagnum = 2;
0787: if (local_verbose) {
0788: ctagAttr[2] = "NAME";
0789: ctagAttrVal[2] = notes[pitch];
0790: ctagAttr[3] = "REGISTER";
0791: ctagAttrVal[3] = "" + register[pitch];
0792: ctagnum = 4;
0793: }
0794: } else if (status > 175 && status < 192) {
0795: // control change
0796: slen = 2;
0797: int contnum = Utils.baToInt(dta, offset, offset);
0798: int contval = Utils
0799: .baToInt(dta, offset + 1, offset + 1);
0800: ctag = "CONTROL";
0801: ctagAttr[0] = "NUMBER";
0802: ctagAttrVal[0] = "" + contnum;
0803: ctagAttr[1] = "VALUE";
0804: ctagAttrVal[1] = "" + contval;
0805: ctagnum = 2;
0806: if (local_verbose) {
0807: ctagAttr[2] = "NAME";
0808: ctagAttrVal[2] = (String) contHash
0809: .get("" + contnum);
0810: ctagnum = 3;
0811: }
0812: } else if (status > 191 && status < 208) {
0813: // program (patch) change
0814: slen = 1;
0815: int patch = Utils.baToInt(dta, offset, offset);
0816: ctag = "PROGRAM";
0817: ctagAttr[0] = "NUMBER";
0818: ctagAttrVal[0] = "" + patch;
0819: ctagnum = 1;
0820: } else if (status > 207 && status < 224) {
0821: // channel pressure
0822: slen = 1;
0823: int pamt = Utils.baToInt(dta, offset, offset);
0824: ctag = "PRESSURE";
0825: ctagAttr[0] = "AMOUNT";
0826: ctagAttrVal[0] = "" + pamt;
0827: ctagnum = 1;
0828: } else if (status > 223 && status < 240) {
0829: // pitch wheel
0830: slen = 2;
0831: int pwamt = Utils.getPW(dta, offset);
0832: ctag = "WHEEL";
0833: ctagAttr[0] = "AMOUNT";
0834: ctagAttrVal[0] = "" + pwamt;
0835: ctagnum = 1;
0836: } else if (status == 240) {
0837: // sysex
0838: bl = Utils.deltaToInt(dta, offset);
0839: slen = Utils.baToInt(bl.ba, 0, 3);
0840: noff = bl.len;
0841: nFlag = false;
0842: edata = Utils.baToHex(dta, offset + noff, offset + noff
0843: + slen - 1);
0844: noff += slen;
0845: } else if (status == 255) {
0846: // non midi (reset only in "live" midi")
0847: nmData = Utils.baToHex(dta, offset, offset);
0848: // nmData = "SNMT=\""+nmDta+"\" ";
0849: snam = "non-MIDI";
0850: String nmNam = (String) ffHash.get(nmData);
0851: if (nmNam != null) {
0852: snam += " - " + nmNam;
0853: } else {
0854: snam += " - Unknown";
0855: }
0856: // int nmt = baToInt(dta,offset+1,offset+1);
0857: bl = Utils.deltaToInt(dta, offset + 1);
0858: slen = Utils.baToInt(bl.ba, 0, 3);
0859: noff = bl.len + 1;
0860: nFlag = false;
0861: if (slen == 0) {
0862: edata = "No data";
0863: } else {
0864: edata = Utils.baToHex(dta, offset + noff, offset
0865: + noff + slen - 1);
0866: noff += slen;
0867: }
0868: this .getLogger().debug(
0869: "doTrack: status FF" + nmData + ", edata = "
0870: + edata);
0871: } else if (status == 241 || status == 243) {
0872: int tcv = dta[offset];
0873: Integer tc = new Integer(tcv);
0874: edata = tc.toString();
0875: slen = 1;
0876: } else if (status == 242) {
0877: int tcv = Utils.getPW(dta, offset);
0878: Integer tc = new Integer(tcv);
0879: edata = tc.toString();
0880: slen = 2;
0881: } else if (status == 246 || status == 248 || status == 250
0882: || status == 251 || status == 252 || status == 254) {
0883: edata = "No data for " + snam;
0884: slen = 0;
0885: } else { // really unknown
0886: int ol = Utils.getNextHiOrd(dta, offset);
0887: edata = Utils.baToHex(dta, offset + 1, ol);
0888: ol -= offset + 1;
0889: slen = ol;
0890: }
0891:
0892: if (ecFlag) {
0893: if (statFlag) {
0894: attr.clear();
0895: attr.addAttribute("", "SLEN", "SLEN", "CDATA", ""
0896: + slen);
0897: attr
0898: .addAttribute("", "SNAM", "SNAM", "CDATA",
0899: snam);
0900: if (!nmData.equals("")) {
0901: attr.addAttribute("", "SNMT", "SNMT", "CDATA",
0902: nmData);
0903: }
0904: attr
0905: .addAttribute("", "SVAL", "SVAL", "CDATA",
0906: sval);
0907: this .contentHandler.startElement("", "STATUS",
0908: "STATUS", attr);
0909:
0910: attr.clear();
0911: this .contentHandler.startElement("", "EDATA",
0912: "EDATA", attr);
0913: text = edata;
0914: this .contentHandler.characters(text.toCharArray(),
0915: 0, text.length());
0916: this .contentHandler
0917: .endElement("", "EDATA", "EDATA");
0918:
0919: this .contentHandler.endElement("", "STATUS",
0920: "STATUS");
0921: } else {
0922: attr.clear();
0923: this .contentHandler.startElement("", "EDATA",
0924: "EDATA", attr);
0925: text = edata;
0926: this .contentHandler.characters(text.toCharArray(),
0927: 0, text.length());
0928: this .contentHandler
0929: .endElement("", "EDATA", "EDATA");
0930: }
0931: } else {
0932:
0933: if (statFlag) {
0934: attr.clear();
0935: attr.addAttribute("", "SLEN", "SLEN", "CDATA", ""
0936: + slen);
0937: attr
0938: .addAttribute("", "SNAM", "SNAM", "CDATA",
0939: snam);
0940: if (!nmData.equals("")) {
0941: attr.addAttribute("", "SNMT", "SNMT", "CDATA",
0942: nmData);
0943: }
0944: attr
0945: .addAttribute("", "SVAL", "SVAL", "CDATA",
0946: sval);
0947: this .contentHandler.startElement("", "STATUS",
0948: "STATUS", attr);
0949:
0950: attr.clear();
0951: attr.addAttribute("", "NUMBER", "NUMBER", "CDATA",
0952: cnum);
0953: attr
0954: .addAttribute("", "TYPE", "TYPE", "CDATA",
0955: ctyp);
0956: this .contentHandler.startElement("", "CHANNEL",
0957: "CHANNEL", attr);
0958:
0959: attr.clear();
0960: for (int c = 0; c < ctagnum; c++) {
0961: attr.addAttribute("", ctagAttr[c], ctagAttr[c],
0962: "CDATA", ctagAttrVal[c]);
0963: }
0964: this .contentHandler.startElement("", ctag, ctag,
0965: attr);
0966: this .contentHandler.endElement("", ctag, ctag);
0967:
0968: this .contentHandler.endElement("", "CHANNEL",
0969: "CHANNEL");
0970: this .contentHandler.endElement("", "STATUS",
0971: "STATUS");
0972: } else {
0973: attr.clear();
0974: attr.addAttribute("", "NUMBER", "NUMBER", "CDATA",
0975: cnum);
0976: attr
0977: .addAttribute("", "TYPE", "TYPE", "CDATA",
0978: ctyp);
0979: this .contentHandler.startElement("", "CHANNEL",
0980: "CHANNEL", attr);
0981:
0982: attr.clear();
0983: for (int c = 0; c < ctagnum; c++) {
0984: attr.addAttribute("", ctagAttr[c], ctagAttr[c],
0985: "CDATA", ctagAttrVal[c]);
0986: }
0987: this .contentHandler.startElement("", ctag, ctag,
0988: attr);
0989: this .contentHandler.endElement("", ctag, ctag);
0990:
0991: this .contentHandler.endElement("", "CHANNEL",
0992: "CHANNEL");
0993: }
0994:
0995: }
0996:
0997: this .contentHandler.endElement("", "DELTA", "DELTA");
0998:
0999: if (nFlag) {
1000: offset += slen;
1001: } else {
1002: offset += noff;
1003: }
1004: if (offset >= len) {
1005: tFlag = false;
1006: }
1007: }
1008: }
1009:
1010: /**
1011: * Write formatted hex data to file
1012: */
1013: void doHexData(byte[] dta, int len) throws ProcessingException,
1014: SAXException {
1015: AttributesImpl attr = new AttributesImpl();
1016: String text = "";
1017:
1018: this .contentHandler
1019: .startElement("", "HEXDATA", "HEXDATA", attr);
1020:
1021: StringBuffer bth = new StringBuffer("");
1022: int bpl = 32;
1023: int r = len % bpl;
1024: int n = len / bpl;
1025: for (int i = 0; i < n; i++) {
1026: int strt = i * bpl;
1027: bth.append(Utils.baToHex(dta, strt, strt + bpl - 1));
1028: }
1029: if (r > 0) {
1030: int strt = n * bpl;
1031: bth.append(Utils.baToHex(dta, strt, strt + r - 1));
1032: }
1033:
1034: text = bth.toString();
1035: this .contentHandler.characters(text.toCharArray(), 0, text
1036: .length());
1037:
1038: this .contentHandler.endElement("", "HEXDATA", "HEXDATA");
1039: }
1040: }
|