public void play() throws MidiUnavailableException { if (play) return; play = true; if (playTasks == null) init(); synchronized (playTasks) { for (PlayTask th : playTasks) th.timer.schedule(th.newTask(), 10L); } }
public void readFrom(FeatureInputStream in, long startOffset) throws IOException, InvalidMidiDataException { PlayTask th = new PlayTask(); float tempo = 160f; char ch = in.readChar(); TreeSet<MidimsgHolder> lst = new TreeSet<MidimsgHolder>(); while (ch != 0xFFFF) { long dstoff = 0L; int channel; synchronized (unusedChannels) { channel = unusedChannels.remove(0); // channel for new notes unusedChannels.add(channel); } int vol = 64; // base volume for new notes (0..127) long pushOffset = 0; TreeSet<MidimsgHolder> memslot = null; long memOnly = -1; while (ch != '\n' && ch != 0xFFFF) { boolean incOffset = true; switch (ch) { case '\\': if (in.peekChar() == '\r') in.readChar(); if (in.peekChar() == '\n') in.readChar(); break; case '/': if (in.peekChar() == '*') { /* checks and reads for this kind of comment block */ in.readChar(); for (; ; ) { while ((ch = in.readChar()) != '*' && ch != 0xFFFF) ; if ((ch = in.readChar()) == '/' || ch == 0xFFFF) break; } } break; case 'x': synchronized (unusedChannels) { unusedChannels.remove(channel); unusedChannels.add(0, channel); } channel = in.readInt(1, 16); break; case 't': MidimsgHolder t = MidimsgHolder.tempo(dstoff, in.readFloat()); if (tempo == 160f) tempo = t.newTempo; if (memOnly == -1) lst.add(t); if (memslot != null) memslot.add(t); break; case 'V': vol = Math.min(in.readInt(2, 16), 127); break; case 'R': if (memOnly == -1) { memOnly = dstoff; th.seqmem.put(in.readInt(), memslot = new TreeSet<MidimsgHolder>()); } else { memslot.add(MidimsgHolder.text(dstoff, "REPEAT")); memslot = null; dstoff = memOnly; memOnly = -1; } break; case 'M': if (memslot == null) th.seqmem.put(in.readInt(), memslot = new TreeSet<MidimsgHolder>()); else { memslot.add(MidimsgHolder.text(dstoff, "REPEAT")); memslot = null; } break; case 'p': MidimsgHolder pc = new MidimsgHolder(dstoff, PROGRAM_CHANGE, channel, in.readInt(), 0); if (memOnly == -1) lst.add(pc); if (memslot != null) memslot.add(pc); break; case '!': // generic midi event int tmp = in.peekChar(); if (tmp == '*') // fade in.readChar(); Integer a = null, b = 1, step = 1, delta = 128; int eventType = in.readInt(1, 16) << 4; int param1 = in.readInt(2, 16); if (tmp == '*') // fade { in.readChar(); // = a = in.readInt(2, 16); in.readChar(); // > b = in.readInt(2, 16); try { step = in.readInt(); } catch (IOException e) { } ; try { delta = in.readInt(); } catch (IOException e) { } ; } else { a = in.readInt(2, 16); b = a; } int d = 0; for (int x = a; step < 0 ? x >= b : x <= b; x += step, d += delta) { MidimsgHolder hh = new MidimsgHolder(dstoff + d, eventType, channel, param1, x); if (memOnly == -1) lst.add(hh); if (memslot != null) memslot.add(hh); } break; case '.': // stop if (memslot != null && memslot.size() == 0) memslot.add(MidimsgHolder.text(dstoff, ".")); // record correct start moment int length = readLength(in); if (in.peekChar() == '*') { in.readChar(); int qty2 = in.readInt(); for (int i = 0; i < qty2; i++) dstoff += length; } else dstoff += length; break; case '[': pushOffset = dstoff; break; case ']': dstoff = pushOffset; break; case '<': incOffset = false; ch = in.readChar(); if (ch < 'c' || ch > 'h') break; case 'c': case 'd': case 'e': case 'f': case 'g': case 'a': case 'b': case 'h': int add = (ch == 'a' || ch == 'b') ? 'c' - 8 : 'c'; int base = baseNote[ch - add]; int dur = 0; int velo = vol; int qty = 1; boolean ok = true; while (ok) { int mdf = in.peekChar(); switch (mdf) { case '#': base++; in.readChar(); break; case '-': base--; in.readChar(); break; case 'v': in.readChar(); if (in.peekChar() == '+') { in.readChar(); velo = in.readInt(2, 16); vol = Math.min(vol + velo, 127); velo = vol; break; } else if (in.peekChar() == '-') { in.readChar(); velo = in.readInt(2, 16); vol = Math.max(vol - velo, 0); velo = vol; break; } velo = Math.min(in.readInt(2, 16), 127); break; case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': base += (mdf - '5') * 12; in.readChar(); break; case 'I': case 'H': case 'G': case 'F': case 'E': case 'D': case 'C': case 'B': case 'A': dur += readLength(in); break; case '*': in.readChar(); qty = in.readInt(); break; default: ok = false; break; } } if (dur == 0) dur = 32; // 32 ticks for 4/4 for (int i = 0; i < qty; i++) { if (memOnly == -1) { lst.add(new MidimsgHolder(dstoff, NOTE_ON, channel, base, velo)); lst.add(new MidimsgHolder(dstoff + dur, NOTE_OFF, channel, base, 0)); } if (memslot != null) { memslot.add(new MidimsgHolder(dstoff, NOTE_ON, channel, base, velo)); memslot.add(new MidimsgHolder(dstoff + dur, NOTE_OFF, channel, base, 0)); } dstoff += dur; } if (!incOffset) dstoff -= dur * qty; break; case 'N': TreeSet<MidimsgHolder> stored = th.seqmem.get(in.readInt()); int n = 1; if (in.peekChar() == '*') { in.readChar(); n = in.readInt(); } if (stored != null) { for (int i = 0; i < n; i++) { long diff = dstoff - stored.first().offset; for (MidimsgHolder h : stored) { MidimsgHolder h2 = new MidimsgHolder(); h2.offset = h.offset + diff; h2.msg = h.msg; h2.newTempo = h.newTempo; if (memOnly == -1) lst.add(h2); if (memslot != null) memslot.add(h2); } dstoff += stored.last().offset - stored.first().offset; } } break; } ch = in.readChar(); } ch = in.readChar(); } PlayTask target = th; synchronized (playTasks) { for (PlayTask test : playTasks) if (test.tempo == tempo) { target = test; target.seqmem.putAll(th.seqmem); } } if (target == th) // must start a new timer { synchronized (playTasks) { playTasks.add(target); } target.timer.schedule(target.newTask(), 10L); } long offset = target.playOffset + startOffset; for (MidimsgHolder d : lst) d.offset = offset + d.offset + 32L; if (target.playQueue.isEmpty()) { synchronized (target.playQueue) { target.playQueue.addAll(lst); } } else { synchronized (target.playQueue) { lst.addAll(target.playQueue); target.playQueue.clear(); target.playQueue.addAll(lst); } } }