public void buildTrackAndStart() { int[] trackList = null; sequence.deleteTrack(track); track = sequence.createTrack(); for (int i = 0; i < 16; i++) { trackList = new int[16]; int key = instruments[i]; for (int j = 0; j < 16; j++) { JCheckBox jc = (JCheckBox) checkboxList.get(j + (16 * i)); if (jc.isSelected()) { trackList[j] = key; } else { trackList[j] = 0; } } // close inner loop makeTracks(trackList); track.add(makeEvent(176, 1, 127, 0, 16)); } // close outer track.add(makeEvent(192, 9, 1, 0, 15)); try { sequencer.setSequence(sequence); sequencer.setLoopCount(sequencer.LOOP_CONTINUOUSLY); sequencer.start(); sequencer.setTempoInBPM(120); } catch (Exception e) { e.printStackTrace(); } } // close buildTrackAndStart method
public void los() { guiErstellen(); try { Sequencer sequencer = MidiSystem.getSequencer(); sequencer.open(); sequencer.addControllerEventListener(ml, new int[] {127}); Sequence seq = new Sequence(Sequence.PPQ, 4); Track track = seq.createTrack(); int r = 0; for (int i = 0; i < 60; i += 4) { r = (int) ((Math.random() * 50) + 1); track.add(eventErzeugen(144, 1, r, 100, i)); track.add(eventErzeugen(176, 1, 127, 0, i)); track.add(eventErzeugen(128, 1, r, 100, i + 2)); } sequencer.setSequence(seq); sequencer.setTempoInBPM(120); sequencer.start(); Thread.sleep(5000); sequencer.close(); } catch (Exception ex) { ex.printStackTrace(); } }
/** * Binary search for the event indexes of the track * * @param tick tick number of index to be found in array * @return index in track which is on or after "tick". if no entries are found that follow after * tick, track.size() is returned */ public static int tick2index(Track track, long tick) { int ret = 0; if (tick > 0) { int low = 0; int high = track.size() - 1; while (low < high) { // take the middle event as estimate ret = (low + high) >> 1; // tick of estimate long t = track.get(ret).getTick(); if (t == tick) { break; } else if (t < tick) { // estimate too low if (low == high - 1) { // "or after tick" ret++; break; } low = ret; } else { // if (t>tick) // estimate too high high = ret; } } } return ret; }
/** * Write a track to an output stream. * * @param track the track to write * @param dos a MidiDataOutputStream to write to * @return the number of bytes written */ private int writeTrack(Track track, MidiDataOutputStream dos) throws IOException { int i = 0, elength = track.size(), trackLength; MidiEvent pme = null; dos.writeInt(0x4d54726b); // "MTrk" trackLength = computeTrackLength(track, dos); dos.writeInt(trackLength); while (i < elength) { MidiEvent me = track.get(i); int dtime = 0; if (pme != null) dtime = (int) (me.getTick() - pme.getTick()); dos.writeVariableLengthInt(dtime); // FIXME: use running status byte byte msg[] = me.getMessage().getMessage(); dos.write(msg); pme = me; i++; } // We're done if the last event was an End of Track meta message. if (pme != null && (pme.getMessage() instanceof MetaMessage)) { MetaMessage mm = (MetaMessage) pme.getMessage(); if (mm.getType() == 0x2f) // End of Track message return trackLength + 8; } // Write End of Track meta message dos.writeVariableLengthInt(0); // Delta time of 0 dos.writeByte(0xff); // Meta Message dos.writeByte(0x2f); // End of Track message dos.writeVariableLengthInt(0); // Length of 0 return trackLength + 8 + 4; }
public void go() { setUpGui(); try { Sequencer sequencer = MidiSystem.getSequencer(); sequencer.open(); // make a sequencer and open sequencer.addControllerEventListener(m1, new int[] {127}); Sequence seq = new Sequence(Sequence.PPQ, 4); Track track = seq.createTrack(); int r = 0; for (int i = 0; i < 300; i += 4) { r = (int) ((Math.random() * 50) + 1); track.add(makeEvent(144, 1, r, 100, i)); track.add(makeEvent(176, 1, 127, 0, i)); track.add(makeEvent(128, 1, r, 100, i + 2)); } // end loop sequencer.setSequence(seq); sequencer.start(); sequencer.setTempoInBPM(120); } catch (Exception ex) { ex.printStackTrace(); } } // close method
public void play(int instrument, int note) { try { Sequencer player = MidiSystem.getSequencer(); player.open(); Sequence seq = new Sequence(Sequence.PPQ, 4); Track track = seq.createTrack(); MidiEvent event = null; ShortMessage first = new ShortMessage(); first.setMessage(192, 1, instrument, 0); MidiEvent changeInstrument = new MidiEvent(first, 1); track.add(changeInstrument); ShortMessage a = new ShortMessage(); a.setMessage(144, 1, note, 100); MidiEvent noteOn = new MidiEvent(a, 1); track.add(noteOn); ShortMessage b = new ShortMessage(); b.setMessage(128, 1, note, 100); MidiEvent noteOff = new MidiEvent(b, 16); track.add(noteOff); player.setSequence(seq); player.start(); } catch (Exception ex) { ex.printStackTrace(); } } // close play
public static Sequence process( final Sequence inputSequence, int track, long startTick, long endTick, int noteOverlap, int minimumRest, Handler loggingHandler) throws InvalidMidiDataException { Sequence outputSequence = new Sequence(inputSequence.getDivisionType(), inputSequence.getResolution()); Track[] inputTracks = inputSequence.getTracks(); SlurBinderA.noteOverlap = noteOverlap; SlurBinderA.minimumRest = minimumRest; slurEndNotes = new HashSet<>(); for (int trackI = 0; trackI < inputTracks.length; trackI++) { Track inputTrack = inputTracks[trackI]; Track outputTrack = outputSequence.createTrack(); if (trackI == track) { handleTrack(outputTrack, inputTrack, startTick, endTick); } else { for (int eventI = 0; eventI < inputTrack.size(); eventI++) { MidiEvent midEvent = inputTrack.get(eventI); outputTrack.add(midEvent); } } } return outputSequence; }
public void makeTracks(int[] list) { for (int i = 0; i < 16; i++) { int key = list[i]; if (key != 0) { track.add(makeEvent(144, 9, key, 100, i)); track.add(makeEvent(128, 9, key, 100, i + 1)); } } }
/** * Compute the length of a track as it will be written to the output stream. * * @param track the track to measure * @param dos a MidiDataOutputStream used for helper method * @return the length of the track */ private int computeTrackLength(Track track, MidiDataOutputStream dos) { int count = 0, length = 0, i = 0, eventCount = track.size(); long ptick = 0; while (i < eventCount) { MidiEvent me = track.get(i); long tick = me.getTick(); length += dos.variableLengthIntLength((int) (tick - ptick)); ptick = tick; length += me.getMessage().getLength(); i++; } return length; }
private void loadNotes() { int program = 0; HashMap<Integer, Float> lastTimeNote = new HashMap<Integer, Float>(); HashMap<Float, Integer> secondsNotes = new HashMap<Float, Integer>(); this.notas = new ArrayList<MIDINote>(); for (Track track : sequencia.getTracks()) { for (int c = 0; c < track.size(); ++c) { MidiEvent event = track.get(c); MidiMessage msg = event.getMessage(); if (msg instanceof ShortMessage) { ShortMessage shortmsg = (ShortMessage) msg; if (shortmsg.getCommand() == ShortMessage.PROGRAM_CHANGE) { program = shortmsg.getData1(); } else { // }else if(program>=25 && program <= 40){ // else if(program== 30){ if (shortmsg.getCommand() == ShortMessage.NOTE_ON) { MIDINote midiNote = new MIDINote(event, sequencia, tempoProcessor, program); // tocador.start(); int noteChord = midiNote.getChord(); float noteSecond = midiNote.getSecond(); if (!lastTimeNote.containsKey(noteChord)) { lastTimeNote.put(noteChord, 0.0f); } if (noteSecond - lastTimeNote.get(noteChord).floatValue() <= this.interval) { continue; } lastTimeNote.put(noteChord, noteSecond); // System.out.println("Play chord "+noteChord+" in "+noteSecond+" seconds"); notas.add(midiNote); if (!secondsNotes.containsKey(noteSecond)) { secondsNotes.put(noteSecond, 1); } secondsNotes.put(noteSecond, secondsNotes.get(noteSecond).intValue() + 1); } } } } } // System.out.println("tamanho da pista "+notas.size()+" e track "+maxNote); for (float second : secondsNotes.keySet()) { int repeated = secondsNotes.get(second).intValue(); if (repeated > maxNote) { this.maxNote = repeated; } } this.notesLength = secondsNotes.size(); // GameEngine.getInstance().setFramesPerSecond((int)(((tocador.getMicrosecondLength()/1000000)/(notas.size()*1.0))*4000)); // System.out.println("(int)(("+sequencia.getMicrosecondLength()+"/1000000)/"+notas.size()+"="+(int)((sequencia.getMicrosecondLength()/1000000)/notas.size())) }
/** * Send entry MIDI Sequence into Receiver using time stamps. * * @return The total length of the sequence. */ private double send(final Sequence seq, final Receiver recv) { assert seq.getDivisionType() == Sequence.PPQ; final float divtype = seq.getDivisionType(); final Track[] tracks = seq.getTracks(); tune(recv); final int[] trackspos = new int[tracks.length]; int mpq = 500000; final int seqres = seq.getResolution(); long lasttick = 0; long curtime = 0; while (true) { MidiEvent selevent = null; int seltrack = -1; for (int i = 0; i < tracks.length; i++) { final int trackpos = trackspos[i]; final Track track = tracks[i]; if (trackpos < track.size()) { final MidiEvent event = track.get(trackpos); if (selevent == null || event.getTick() < selevent.getTick()) { selevent = event; seltrack = i; } } } if (seltrack == -1) { break; } trackspos[seltrack]++; final long tick = selevent.getTick(); if (divtype == Sequence.PPQ) { curtime += (tick - lasttick) * mpq / seqres; } else { curtime = (long) (tick * 1000000.0 * divtype / seqres); } lasttick = tick; final MidiMessage msg = selevent.getMessage(); if (msg instanceof MetaMessage) { if (divtype == Sequence.PPQ && ((MetaMessage) msg).getType() == 0x51) { final byte[] data = ((MetaMessage) msg).getData(); mpq = (data[0] & 0xff) << 16 | (data[1] & 0xff) << 8 | data[2] & 0xff; } } else if (recv != null) { recv.send(msg, curtime); } } return curtime / 1000000.0; }
public void go() { try { sq.open(); Sequence seq = new Sequence(Sequence.PPQ, 4); Track track = seq.createTrack(); track.add(addNote(144, 9, 56, 100, 1)); track.add(addNote(128, 9, 56, 100, 4)); sq.setSequence(seq); sq.setTempoInBPM(tempo); sq.setLoopCount(Sequencer.LOOP_CONTINUOUSLY); sq.start(); } catch (Exception ex) { ex.printStackTrace(); } }
public boolean createSequence() { try { // Create a sequence with microsecond timing resolution // (Although this doesn't seem to have any effect on tick resolution - ticks in MidiEvents // always microsecond fSequence = new Sequence(Sequence.SMPTE_25, 40000); // Create track and add program change to bell sound Track track = fSequence.createTrack(); // track.add(createBankChange(1, 0)); track.add(createProgramChange(14, 0)); } catch (InvalidMidiDataException e) { System.out.println("Error creating sequence: " + e); return false; } return true; }
private void readTrack(DataInputStream dataInputStream, Track track) throws InvalidMidiDataException, IOException { // search for a "MTrk" chunk while (true) { int nMagic = dataInputStream.readInt(); if (nMagic == MidiConstants.TRACK_MAGIC) { break; } int nChunkLength = dataInputStream.readInt(); if (nChunkLength % 2 != 0) { nChunkLength++; } dataInputStream.skip(nChunkLength); } int nTrackChunkLength = dataInputStream.readInt(); long lTicks = 0; long[] alRemainingBytes = new long[1]; alRemainingBytes[0] = nTrackChunkLength; int[] anRunningStatusByte = new int[1]; // indicates no running status in effect anRunningStatusByte[0] = -1; while (alRemainingBytes[0] > 0) { long lDeltaTicks = readVariableLengthQuantity(dataInputStream, alRemainingBytes); // TDebug.out("delta ticks: " + lDeltaTicks); lTicks += lDeltaTicks; MidiEvent event = readEvent(dataInputStream, alRemainingBytes, anRunningStatusByte, lTicks); track.add(event); } }
public static double send(Sequence seq, Receiver recv) { float divtype = seq.getDivisionType(); assert (seq.getDivisionType() == Sequence.PPQ); Track[] tracks = seq.getTracks(); int[] trackspos = new int[tracks.length]; int mpq = 60000000 / 100; int seqres = seq.getResolution(); long lasttick = 0; long curtime = 0; while (true) { MidiEvent selevent = null; int seltrack = -1; for (int i = 0; i < tracks.length; i++) { int trackpos = trackspos[i]; Track track = tracks[i]; if (trackpos < track.size()) { MidiEvent event = track.get(trackpos); if (selevent == null || event.getTick() < selevent.getTick()) { selevent = event; seltrack = i; } } } if (seltrack == -1) break; trackspos[seltrack]++; long tick = selevent.getTick(); if (divtype == Sequence.PPQ) curtime += ((tick - lasttick) * mpq) / seqres; else curtime = (long) ((tick * 1000000.0 * divtype) / seqres); lasttick = tick; MidiMessage msg = selevent.getMessage(); if (msg instanceof MetaMessage) { if (divtype == Sequence.PPQ) if (((MetaMessage) msg).getType() == 0x51) { byte[] data = ((MetaMessage) msg).getData(); mpq = ((data[0] & 0xff) << 16) | ((data[1] & 0xff) << 8) | (data[2] & 0xff); } } else { if (recv != null) recv.send(msg, curtime); } } return curtime / 1000000.0; }
public boolean createEvent(int type, int num, long tick) { ShortMessage message = new ShortMessage(); try { message.setMessage(type + cc.num, num, cc.velocity); MidiEvent event = new MidiEvent(message, tick); track.add(event); return true; } catch (Exception ex) { ex.printStackTrace(); return false; } }
private static void handleTrack( Track outputTrack, Track inputTrack, long startTick, long endTick) { // handle all other events for (int eventI = 0; eventI < inputTrack.size(); eventI++) { MidiEvent midEvent = inputTrack.get(eventI); if (!Note.isNoteOnEvent(midEvent)) { if (!Note.isNoteOffEvent(midEvent)) { outputTrack.add(midEvent); } } } // handle all note events NoteTrack noteTrack = new NoteTrack(inputTrack); for (int i = 0; i < noteTrack.size(); i++) { Note thisNote = noteTrack.get(i); List<Note> followingNotes = findFollowingNotes(noteTrack, i); Note newNote = handleNote(thisNote, followingNotes, startTick, endTick); outputTrack.add(newNote.getNoteOnEvent()); outputTrack.add(newNote.getNoteOffEvent()); } }
public void parseSequence(boolean[][] grid) { ShortMessage on; this.grid = grid; ShortMessage off; int tickLength = 16; try { ShortMessage sm = new ShortMessage(); sm.setMessage(ShortMessage.PROGRAM_CHANGE, 0, instrument, 0); track.add(new MidiEvent(sm, 0)); for (boolean[] aGrid : grid) { for (int col = 0; col < aGrid.length; col++) { if (aGrid[col]) { if (other == 1) { other = 0; } else { other = 1; } off = new ShortMessage(); off.setMessage( ShortMessage.NOTE_OFF, 0, scale[(grid[0].length - col - 1 + other) % scale.length], velocity); on = new ShortMessage(); on.setMessage( ShortMessage.NOTE_ON, 0, scale[(grid[0].length - col - 1 + other) % scale.length], velocity); track.add(new MidiEvent(on, ticks)); track.add(new MidiEvent(off, ticks + tickLength)); } } ticks += tickLength; } } catch (InvalidMidiDataException e) { e.printStackTrace(); } }
public synchronized void refresh(Sequence seq) { ArrayList<MidiEvent> list = new ArrayList<>(); Track[] tracks = seq.getTracks(); if (tracks.length > 0) { // tempo events only occur in track 0 Track track = tracks[0]; int c = track.size(); for (int i = 0; i < c; i++) { MidiEvent ev = track.get(i); MidiMessage msg = ev.getMessage(); if (isMetaTempo(msg)) { // found a tempo event. Add it to the list list.add(ev); } } } int size = list.size() + 1; firstTempoIsFake = true; if ((size > 1) && (list.get(0).getTick() == 0)) { // do not need to add an initial tempo event at the beginning size--; firstTempoIsFake = false; } ticks = new long[size]; tempos = new int[size]; int e = 0; if (firstTempoIsFake) { // add tempo 120 at beginning ticks[0] = 0; tempos[0] = DEFAULT_TEMPO_MPQ; e++; } for (int i = 0; i < list.size(); i++, e++) { MidiEvent evt = list.get(i); ticks[e] = evt.getTick(); tempos[e] = getTempoMPQ(evt.getMessage()); } snapshotIndex = 0; snapshotMicro = 0; }
/** * Adds this Note at the specified time on the specified Track and channel in the specified * Sequence, then returns the time that a sequential Note should be added. * * @param seq the Sequence to which to add this Note * @param track the Track in the Sequence to which to add this Note * @param time the time at which to start this Note * @param offTime the time at which to end this Note * @param ch the channel on which to put this Note * @param transposition amount by which to transpose this note in semitones * @throws javax.sound.midi.InvalidMidiDataException */ public void render(Sequence seq, Track track, long time, long offTime, int ch, int transposition) throws InvalidMidiDataException { // To trace rendering info: // Trace.log(2, "\nchannel = " + ch // + " track = " + track // + " beat = " + time/480.0 // + " pitch = " + pitch // + " rhythmValue = " + rhythmValue); if (pitch == REST) { // if this Note is a rest, do nothing return; } int actualPitch = pitch + transposition; // Prevent exceptions in synth if (actualPitch > MAX_PITCH) { actualPitch = MAX_PITCH; } else if (actualPitch < MIN_PITCH) { actualPitch = MIN_PITCH; } // create a note on event at the current time MidiEvent evt = MidiSynth.createNoteOnEvent(ch, actualPitch, volume, time); track.add(evt); // Trace.log(0, " channel = " + ch + " time = " + time + " note on " + " pitch = " + actualPitch // + " velocity = " + volume); // advance the time and call the note off event evt = MidiSynth.createNoteOffEvent(ch, actualPitch, volume, offTime); track.add(evt); // Trace.log(0, " channel = " + ch + " offTime = " + time + " note off " + " pitch = " + // actualPitch + " velocity = " + volume); }
public void actionPerformed(ActionEvent ev) { try { // make (and open) a sequencer, make a sequence and track Sequencer sequencer = MidiSystem.getSequencer(); sequencer.open(); sequencer.addControllerEventListener(myPanel, new int[] {127}); Sequence seq = new Sequence(Sequence.PPQ, 4); Track track = seq.createTrack(); // now make two midi events (containing a midi message) for (int i = 0; i < 100; i += 4) { int rNum = (int) ((Math.random() * 50) + 1); if (rNum < 38) { // so now only do it if num <38 (75% of the time) track.add(makeEvent(144, 1, rNum, 100, i)); track.add(makeEvent(176, 1, 127, 0, i)); track.add(makeEvent(128, 1, rNum, 100, i + 2)); } } // end loop // add the events to the track // add the sequence to the sequencer, set timing, and start sequencer.setSequence(seq); sequencer.start(); sequencer.setTempoInBPM(220); } catch (Exception ex) { ex.printStackTrace(); } } // close actionperformed
public static final void addNotesToTrack(Track From, Track To) throws InvalidMidiDataException { for (int i = 0; i < From.size(); i++) { MidiEvent Me = From.get(i); MidiMessage Mm = Me.getMessage(); if (Mm instanceof ShortMessage) { ShortMessage Sm = (ShortMessage) Mm; int Command = Sm.getCommand(); int Com = -1; if (Command == ShortMessage.NOTE_ON) { Com = MetaEventOffset; } else if (Command == ShortMessage.NOTE_OFF) { Com = MetaEventOffset + 1; } if (Com > 0) { byte[] b = Sm.getMessage(); int l = (b == null ? 0 : b.length); MetaMessage MetaMessage = new MetaMessage(Com, b, l); MidiEvent Me2 = new MidiEvent(MetaMessage, Me.getTick()); To.add(Me2); } } } }
public void los() { guiErstellen(); try { // einen Sequencer erzeugen (und öffnen), // eine Sequence und einen Track erzeugen Sequencer sequencer = MidiSystem.getSequencer(); sequencer.open(); sequencer.addControllerEventListener(ml, new int[] {127}); Sequence seq = new Sequence(Sequence.PPQ, 4); Track track = seq.createTrack(); // jetzt werden MidiEvents (die eine // MidiMessage enthalten) erzeugt int r = 0; for (int i = 0; i < 60; i += 4) { r = (int) ((Math.random() * 50) + 1); track.add(eventErzeugen(144, 1, r, 100, i)); track.add(eventErzeugen(176, 1, 127, 0, i)); track.add(eventErzeugen(128, 1, r, 100, i + 2)); } // Ende der for-Schleife // Hinzufügen der Events zum Track und der Sequence // zum Sequencer, Setzen der Zeiten und Starten sequencer.setSequence(seq); sequencer.start(); sequencer.setTempoInBPM(120); } catch (Exception ex) { ex.printStackTrace(); } } // Methode los schließen
/** * Converts the given tune to a midi sequence. * * @param tune The tune to be converted. * @return The midi sequence of the tune. */ public Sequence toMidiSequence(Tune tune) { Sequence sequence = null; try { if (instrument == null) { Synthesizer synth = MidiSystem.getSynthesizer(); synth.open(); try { setInstrument(synth.getAvailableInstruments()[0]); } finally { synth.close(); } } // Sequence in ticks per quarter note : PPQ = Pulse Per Quarter Note // Resolution is expressed in ticks per beat. // Last parameter "1" is the number of tracks. sequence = new Sequence(Sequence.PPQ, SEQUENCE_RESOLUTION, 1); // Set the instrument on channel 0 ShortMessage sm = new ShortMessage(); sm.setMessage(ShortMessage.PROGRAM_CHANGE, 0, instrument.getPatch().getProgram(), 0); Track track = sequence.createTrack(); track.add(new MidiEvent(sm, 0)); // long trackLengthInTicks = track.ticks(); int lastRepeatOpen = -1; int repeatNumber = 1; boolean inWrongEnding = false; KeySignature tuneKey = null; KeySignature currentKey = null; Hashtable partsKey = new Hashtable(); long elapsedTime = 0; NoteAbstract[] graceNotes = null; Music staff = tune.getMusicForAudioRendition(); Iterator it = staff.getVoices().iterator(); while (it.hasNext()) { Voice voice = (Voice) it.next(); int i = 0; // StaffItem iterator while (i < voice.size()) { if (!inWrongEnding) { // ==================================================================== TEMPO if (voice.elementAt(i) instanceof abc.notation.Tempo) { addTempoEventsFor( track, elapsedTime, getMidiMessagesFor((Tempo) voice.elementAt(i))); // , trackLengthInTicks)); } else /*if (voice.elementAt(i) instanceof abc.notation.PartLabel) { //Imagine... part A in Gmaj, B in Amin //in tune you have K:G, P:A, ... P:B, K:Am //if you have part order ABA, when you return to A //you stay in Amin. This stores the tuneKey when a //new part appear, and restitute it when part is played again abc.notation.PartLabel pl = (abc.notation.PartLabel) voice.elementAt(i); if (partsKey.get(pl.getLabel()+"") == null) { partsKey.put(pl.getLabel()+"", tuneKey); } else { tuneKey = (KeySignature) partsKey.get(pl.getLabel()+""); } } else*/ // ==================================================================== KEY SIGNATURE if (voice.elementAt(i) instanceof abc.notation.KeySignature) { tuneKey = (KeySignature) (voice.elementAt(i)); currentKey = new KeySignature(tuneKey.getAccidentals()); } else // ==================================================================== NOTE // Notes ending ties should be ignored. Already taken into // account in getNoteLengthInTicks(Note) if (voice.elementAt(i) instanceof abc.notation.Note && !((abc.notation.Note) voice.elementAt(i)).isEndingTie()) { Note note = (Note) voice.elementAt(i); long noteDuration; boolean fermata = false; Vector decorationNotes = new Vector(); if (note.hasGeneralGracing() || note.hasDecorations()) { Decoration[] d = note.getDecorations(); for (int j = 0; j < d.length; j++) { switch (d[j].getType()) { case Decoration.FERMATA: case Decoration.FERMATA_INVERTED: fermata = true; break; case Decoration.LOWERMORDENT: case Decoration.UPPERMORDENT: case Decoration.DOUBLE_LOWER_MORDANT: case Decoration.DOUBLE_UPPER_MORDANT: case Decoration.TRILL: case Decoration.TURN: // GRUPETTO_UP case Decoration.TURN_INVERTED: // GRUPETTO_DOWN case Decoration.TURNX: case Decoration.TURNX_INVERTED: Note n = new Note(note.getHeight()); n.setAccidental(note.getAccidental(currentKey)); Note o = new Interval(Interval.SECOND, Interval.MAJOR, Interval.UPWARD) .calculateSecondNote(n); Note m = new Interval(Interval.SECOND, Interval.MAJOR, Interval.DOWNWARD) .calculateSecondNote(n); // TODO ornament templates: regular, musette, balkan... // n.setStrictDuration(Note.SIXTEENTH); // o.setDuration((short)(Note.EIGHTH+Note.SIXTEENTH)); o.setAccidental(Accidental.NONE); m.setAccidental(Accidental.NONE); n.setStrictDuration(Note.THIRTY_SECOND); m.setStrictDuration(Note.THIRTY_SECOND); o.setStrictDuration(Note.THIRTY_SECOND); switch (d[j].getType()) { case Decoration.DOUBLE_LOWER_MORDANT: decorationNotes.add(n); decorationNotes.add(m); case Decoration.LOWERMORDENT: decorationNotes.add(n); decorationNotes.add(m); break; case Decoration.DOUBLE_UPPER_MORDANT: case Decoration.TRILL: decorationNotes.add(n); decorationNotes.add(o); case Decoration.UPPERMORDENT: decorationNotes.add(n); decorationNotes.add(o); break; case Decoration.TURNX_INVERTED: case Decoration.TURN: decorationNotes.add(o); decorationNotes.add(n); decorationNotes.add(m); break; case Decoration.TURNX: case Decoration.TURN_INVERTED: decorationNotes.add(m); decorationNotes.add(n); decorationNotes.add(o); } break; } } // currently not used // future use: playing rolls, slides, etc. } long graceNotesDuration = 0; if (note.hasGracingNotes() || (decorationNotes.size() > 0)) { graceNotes = note.getGracingNotes(); // gracing are eighth note for graphical rendition // and because that's it in the parser // adapt duration to note length int divisor = 1; if (note.getStrictDuration() >= Note.HALF) divisor = 1; // grace is an eighth else if (note.getStrictDuration() >= Note.QUARTER) divisor = 2; // 16th else if (note.getStrictDuration() >= Note.EIGHTH) divisor = 4; // 32nd else divisor = 8; // 64th if (note.hasGracingNotes()) { for (int j = 0; j < graceNotes.length; j++) { noteDuration = getNoteLengthInTicks(graceNotes[j], staff) / divisor; graceNotesDuration += noteDuration; if (graceNotes[j] instanceof Note) playNote( (Note) graceNotes[j], i, currentKey, elapsedTime, noteDuration, track); else playMultiNote( (MultiNote) graceNotes[j], i, currentKey, /*elapsedTime,*/ noteDuration, track, staff); elapsedTime += noteDuration; } } for (int j = 0; j < decorationNotes.size(); j++) { noteDuration = getNoteLengthInTicks((Note) decorationNotes.elementAt(j), staff); graceNotesDuration += noteDuration; playNote( (Note) decorationNotes.elementAt(j), i, currentKey, elapsedTime, noteDuration, track); elapsedTime += noteDuration; } } // The note duration if the note isn't part of a tuplet. noteDuration = getNoteLengthInTicks(note, staff) - graceNotesDuration; if (noteDuration <= 0) // in case of too much grace notes noteDuration = getNoteLengthInTicks(note, staff); if (fermata) noteDuration *= 2; playNote(note, i, currentKey, elapsedTime, noteDuration, track); elapsedTime += noteDuration; } else // ==================================================================== MULTI NOTE if ((voice.elementAt(i) instanceof abc.notation.MultiNote)) { MultiNote multiNote = (MultiNote) voice.elementAt(i); playMultiNote(multiNote, i, currentKey, elapsedTime, track, staff); elapsedTime += getNoteLengthInTicks(multiNote, staff); } } // endif (!inWrongEnding) // ====================================================================== REPEAT BAR LINE if (voice.elementAt(i) instanceof abc.notation.RepeatBarLine) { RepeatBarLine bar = (RepeatBarLine) voice.elementAt(i); if (repeatNumber < bar.getRepeatNumbers()[0] && lastRepeatOpen != -1) { repeatNumber++; i = lastRepeatOpen; } else if (repeatNumber > bar.getRepeatNumbers()[0]) inWrongEnding = true; else inWrongEnding = false; } else // ====================================================================== BAR LINE OPEN / // CLOSE if (voice.elementAt(i) instanceof abc.notation.BarLine) { // currentKey = new KeySignature(tuneKey.getAccidentals()); switch (((BarLine) (voice.elementAt(i))).getType()) { case BarLine.SIMPLE: break; case BarLine.REPEAT_OPEN: lastRepeatOpen = i; repeatNumber = 1; break; case BarLine.REPEAT_CLOSE: if (repeatNumber < 2 && lastRepeatOpen != -1) { repeatNumber++; i = lastRepeatOpen; } else { repeatNumber = 1; lastRepeatOpen = -1; } break; // TODO case BarLine.BEGIN_AND_END_REPEAT } } // Whatever kind of bar line it is if (voice.elementAt(i) instanceof abc.notation.BarLine) { currentKey = new KeySignature(tuneKey.getAccidentals()); } i++; } // end while each element in voice } // end while each voice in music } catch (InvalidMidiDataException e) { e.printStackTrace(); } catch (Exception e) { e.printStackTrace(); } return sequence; }
protected static long addEventsToTrack(Track track, MidiEvent event) { track.add(event); return track.ticks(); }
/** @return The length of the track in ticks, once events have been added to it. */ protected static long addEventsToTrack(Track track, MidiEvent[] events) { if (events != null) for (int i = 0; i < events.length; i++) track.add(events[i]); return track.ticks(); }
private static Sequence convert( List<FileAndData> filesData, boolean useLotroInstruments, Map<Integer, LotroInstrument> instrumentOverrideMap, AbcInfo abcInfo, final boolean enableLotroErrors, final boolean stereo) throws ParseException { if (abcInfo == null) abcInfo = new AbcInfo(); else abcInfo.reset(); TuneInfo info = new TuneInfo(); Sequence seq = null; Track track = null; int channel = 0; int trackNumber = 0; int noteDivisorChangeLine = 0; long chordStartTick = 0; long chordEndTick = 0; long PPQN = 0; Map<Integer, Integer> tiedNotes = new HashMap<Integer, Integer>(); // noteId => (line << 16) | column Map<Integer, Integer> accidentals = new HashMap<Integer, Integer>(); // noteId => deltaNoteId List<MidiEvent> noteOffEvents = new ArrayList<MidiEvent>(); for (FileAndData fileAndData : filesData) { String fileName = fileAndData.file.getName(); int lineNumber = 0; int partStartLine = 0; for (String line : fileAndData.lines) { lineNumber++; // Handle extended info Matcher xInfoMatcher = XINFO_PATTERN.matcher(line); if (xInfoMatcher.matches()) { AbcField field = AbcField.fromString( xInfoMatcher.group(XINFO_FIELD) + xInfoMatcher.group(XINFO_COLON)); if (field == AbcField.TEMPO) { try { info.addTempoEvent(chordStartTick, xInfoMatcher.group(XINFO_VALUE).trim()); } catch (IllegalArgumentException e) { // Apparently that wasn't actually a tempo change } } else if (field != null) { String value = xInfoMatcher.group(XINFO_VALUE).trim(); abcInfo.setExtendedMetadata(field, value); if (field == AbcField.PART_NAME) { info.setTitle(value, true); abcInfo.setPartName(trackNumber, value, true); } } continue; } int comment = line.indexOf('%'); if (comment >= 0) line = line.substring(0, comment); if (line.trim().length() == 0) continue; int chordSize = 0; Matcher infoMatcher = INFO_PATTERN.matcher(line); if (infoMatcher.matches()) { char type = Character.toUpperCase(infoMatcher.group(INFO_TYPE).charAt(0)); String value = infoMatcher.group(INFO_VALUE).trim(); abcInfo.setMetadata(type, value); try { switch (type) { case 'X': for (int lineAndColumn : tiedNotes.values()) { throw new ParseException( "Tied note does not connect to another note", fileName, lineAndColumn >>> 16, lineAndColumn & 0xFFFF); } accidentals.clear(); noteOffEvents.clear(); info.newPart(Integer.parseInt(value)); trackNumber++; partStartLine = lineNumber; chordStartTick = 0; abcInfo.setPartNumber(trackNumber, info.getPartNumber()); track = null; // Will create a new track after the header is done if (instrumentOverrideMap != null && instrumentOverrideMap.containsKey(trackNumber)) { info.setInstrument(instrumentOverrideMap.get(trackNumber)); } break; case 'T': if (track != null) throw new ParseException( "Can't specify the title in the middle of a part", fileName, lineNumber, 0); info.setTitle(value, false); abcInfo.setPartName(trackNumber, value, false); if (instrumentOverrideMap == null || !instrumentOverrideMap.containsKey(trackNumber)) { info.setInstrument(TuneInfo.findInstrumentName(value, info.getInstrument())); } break; case 'K': info.setKey(value); break; case 'L': info.setNoteDivisor(value); noteDivisorChangeLine = lineNumber; break; case 'M': info.setMeter(value); noteDivisorChangeLine = lineNumber; break; case 'Q': { int tempo = info.getPrimaryTempoBPM(); info.setPrimaryTempoBPM(value); if (seq != null && (info.getPrimaryTempoBPM() != tempo)) { throw new ParseException( "The tempo must be the same for all parts of the song", fileName, lineNumber); } break; } } } catch (IllegalArgumentException e) { throw new ParseException( e.getMessage(), fileName, lineNumber, infoMatcher.start(INFO_VALUE)); } } else { // The line contains notes if (trackNumber == 0) { // This ABC file doesn't have an "X:" line before notes. Tsk tsk. trackNumber = 1; if (instrumentOverrideMap != null && instrumentOverrideMap.containsKey(trackNumber)) { info.setInstrument(instrumentOverrideMap.get(trackNumber)); } } if (seq == null) { try { PPQN = info.getPpqn(); seq = new Sequence(Sequence.PPQ, (int) PPQN); abcInfo.setPrimaryTempoBPM(info.getPrimaryTempoBPM()); // Create track 0, which will later be filled with the // tempo events and song metadata (title, etc.) seq.createTrack(); abcInfo.setPartNumber(0, 0); abcInfo.setPartName(0, info.getTitle(), false); track = null; } catch (InvalidMidiDataException mde) { throw new ParseException("Midi Error: " + mde.getMessage(), fileName); } } if (track == null) { channel = getTrackChannel(seq.getTracks().length); if (channel > MidiConstants.CHANNEL_COUNT - 1) throw new ParseException( "Too many parts (max = " + (MidiConstants.CHANNEL_COUNT - 1) + ")", fileName, partStartLine); track = seq.createTrack(); track.add( MidiFactory.createProgramChangeEvent( info.getInstrument().midiProgramId, channel, 0)); if (useLotroInstruments) track.add(MidiFactory.createChannelVolumeEvent(MidiConstants.MAX_VOLUME, channel, 1)); abcInfo.setPartInstrument(trackNumber, info.getInstrument()); } Matcher m = NOTE_PATTERN.matcher(line); int i = 0; boolean inChord = false; Tuplet tuplet = null; int brokenRhythmNumerator = 1; // The numerator of the note after the broken rhythm sign int brokenRhythmDenominator = 1; // The denominator of the note after the broken rhythm sign while (true) { boolean found = m.find(i); int parseEnd = found ? m.start() : line.length(); // Parse anything that's not a note for (; i < parseEnd; i++) { char ch = line.charAt(i); if (Character.isWhitespace(ch)) { if (inChord) throw new ParseException( "Unexpected whitespace inside a chord", fileName, lineNumber, i); continue; } switch (ch) { case '[': // Chord start if (inChord) { throw new ParseException( "Unexpected '" + ch + "' inside a chord", fileName, lineNumber, i); } if (brokenRhythmDenominator != 1 || brokenRhythmNumerator != 1) { throw new ParseException( "Can't have broken rhythm (< or >) within a chord", fileName, lineNumber, i); } chordSize = 0; inChord = true; break; case ']': // Chord end if (!inChord) { throw new ParseException("Unexpected '" + ch + "'", fileName, lineNumber, i); } inChord = false; chordStartTick = chordEndTick; break; case '|': // Bar line if (inChord) { throw new ParseException( "Unexpected '" + ch + "' inside a chord", fileName, lineNumber, i); } if (trackNumber == 1) abcInfo.addBar(chordStartTick); accidentals.clear(); if (i + 1 < line.length() && line.charAt(i + 1) == ']') { i++; // Skip |] } else if (trackNumber == 1) { abcInfo.addBar(chordStartTick); } break; case '+': { int j = line.indexOf('+', i + 1); if (j < 0) { throw new ParseException("There is no matching '+'", fileName, lineNumber, i); } try { info.setDynamics(line.substring(i + 1, j)); } catch (IllegalArgumentException iae) { throw new ParseException("Unsupported +decoration+", fileName, lineNumber, i); } if (enableLotroErrors && inChord) { throw new LotroParseException( "Can't include a +decoration+ inside a chord", fileName, lineNumber, i); } i = j; break; } case '(': // Tuplet or slur start if (i + 1 < line.length() && Character.isDigit(line.charAt(i + 1))) { // If it has a digit following it, it's a tuplet if (tuplet != null) throw new ParseException( "Unexpected '" + ch + "' before end of tuplet", fileName, lineNumber, i); try { for (int j = i + 1; j < line.length(); j++) { if (line.charAt(i) != ':' && !Character.isDigit(line.charAt(i))) { tuplet = new Tuplet(line.substring(i + 1, j + 1), info.isCompoundMeter()); i = j; break; } } } catch (IllegalArgumentException e) { throw new ParseException("Invalid tuplet", fileName, lineNumber, i); } } else { // Otherwise it's a slur, which LotRO conveniently ignores if (inChord) { throw new ParseException( "Unexpected '" + ch + "' inside a chord", fileName, lineNumber, i); } } break; case ')': // End of a slur, ignore if (inChord) { throw new ParseException( "Unexpected '" + ch + "' inside a chord", fileName, lineNumber, i); } break; case '\\': // Ignore backslashes break; default: throw new ParseException( "Unknown/unexpected character '" + ch + "'", fileName, lineNumber, i); } } if (i >= line.length()) break; // The matcher might find +f+, +ff+, or +fff+ and think it's a note if (i > m.start()) continue; if (inChord) chordSize++; if (enableLotroErrors && inChord && chordSize > AbcConstants.MAX_CHORD_NOTES) { throw new LotroParseException( "Too many notes in a chord", fileName, lineNumber, m.start()); } // Parse the note int numerator; int denominator; numerator = (m.group(NOTE_LEN_NUMER) == null) ? 1 : Integer.parseInt(m.group(NOTE_LEN_NUMER)); String denom = m.group(NOTE_LEN_DENOM); if (denom == null) denominator = 1; else if (denom.equals("/")) denominator = 2; else if (denom.equals("//")) denominator = 4; else denominator = Integer.parseInt(denom.substring(1)); String brokenRhythm = m.group(NOTE_BROKEN_RHYTHM); if (brokenRhythm != null) { if (brokenRhythmDenominator != 1 || brokenRhythmNumerator != 1) { throw new ParseException( "Invalid broken rhythm: " + brokenRhythm, fileName, lineNumber, m.start(NOTE_BROKEN_RHYTHM)); } if (inChord) { throw new ParseException( "Can't have broken rhythm (< or >) within a chord", fileName, lineNumber, m.start(NOTE_BROKEN_RHYTHM)); } if (m.group(NOTE_TIE) != null) { throw new ParseException( "Tied notes can't have broken rhythms (< or >)", fileName, lineNumber, m.start(NOTE_BROKEN_RHYTHM)); } int factor = 1 << brokenRhythm.length(); if (brokenRhythm.charAt(0) == '>') { numerator *= 2 * factor - 1; denominator *= factor; brokenRhythmDenominator = factor; } else { brokenRhythmNumerator = 2 * factor - 1; brokenRhythmDenominator = factor; denominator *= factor; } } else { numerator *= brokenRhythmNumerator; denominator *= brokenRhythmDenominator; brokenRhythmNumerator = 1; brokenRhythmDenominator = 1; } if (tuplet != null) { if (!inChord || chordSize == 1) tuplet.r--; numerator *= tuplet.q; denominator *= tuplet.p; if (tuplet.r == 0) tuplet = null; } // Convert back to the original tempo int curTempoBPM = info.getCurrentTempoBPM(chordStartTick); int primaryTempoBPM = info.getPrimaryTempoBPM(); numerator *= curTempoBPM; denominator *= primaryTempoBPM; // Try to guess if this note is using triplet timing if ((denominator % 3 == 0) && (numerator % 3 != 0)) { abcInfo.setHasTriplets(true); } long noteEndTick = chordStartTick + DEFAULT_NOTE_TICKS * numerator / denominator; // A chord is as long as its shortest note if (chordEndTick == chordStartTick || noteEndTick < chordEndTick) chordEndTick = noteEndTick; char noteLetter = m.group(NOTE_LETTER).charAt(0); String octaveStr = m.group(NOTE_OCTAVE); if (octaveStr == null) octaveStr = ""; if (noteLetter == 'z' || noteLetter == 'x') { if (m.group(NOTE_ACCIDENTAL) != null && m.group(NOTE_ACCIDENTAL).length() > 0) { throw new ParseException( "Unexpected accidental on a rest", fileName, lineNumber, m.start(NOTE_ACCIDENTAL)); } if (octaveStr.length() > 0) { throw new ParseException( "Unexpected octave indicator on a rest", fileName, lineNumber, m.start(NOTE_OCTAVE)); } } else { int octave = Character.isUpperCase(noteLetter) ? 3 : 4; if (octaveStr.indexOf('\'') >= 0) octave += octaveStr.length(); else if (octaveStr.indexOf(',') >= 0) octave -= octaveStr.length(); int noteId; int lotroNoteId; lotroNoteId = noteId = (octave + 1) * 12 + CHR_NOTE_DELTA[Character.toLowerCase(noteLetter) - 'a']; if (!useLotroInstruments) noteId += 12 * info.getInstrument().octaveDelta; if (m.group(NOTE_ACCIDENTAL) != null) { if (m.group(NOTE_ACCIDENTAL).startsWith("_")) accidentals.put(noteId, -m.group(NOTE_ACCIDENTAL).length()); else if (m.group(NOTE_ACCIDENTAL).startsWith("^")) accidentals.put(noteId, m.group(NOTE_ACCIDENTAL).length()); else if (m.group(NOTE_ACCIDENTAL).equals("=")) accidentals.put(noteId, 0); } int noteDelta; if (accidentals.containsKey(noteId)) { noteDelta = accidentals.get(noteId); } else { // Use the key signature to determine the accidental noteDelta = info.getKey().getDefaultAccidental(noteId).deltaNoteId; } lotroNoteId += noteDelta; noteId += noteDelta; if (enableLotroErrors && lotroNoteId < Note.MIN_PLAYABLE.id) throw new LotroParseException("Note is too low", fileName, lineNumber, m.start()); else if (enableLotroErrors && lotroNoteId > Note.MAX_PLAYABLE.id) throw new LotroParseException("Note is too high", fileName, lineNumber, m.start()); if (info.getInstrument() == LotroInstrument.COWBELL || info.getInstrument() == LotroInstrument.MOOR_COWBELL) { if (useLotroInstruments) { // Randomize the noteId unless it's part of a note tie if (m.group(NOTE_TIE) == null && !tiedNotes.containsKey(noteId)) { int min = info.getInstrument().lowestPlayable.id; int max = info.getInstrument().highestPlayable.id; lotroNoteId = noteId = min + (int) (Math.random() * (max - min)); } } else { noteId = (info.getInstrument() == LotroInstrument.COWBELL) ? 76 : 71; lotroNoteId = 71; } } // Check for overlapping notes, and remove extra note off events Iterator<MidiEvent> noteOffIter = noteOffEvents.iterator(); while (noteOffIter.hasNext()) { MidiEvent evt = noteOffIter.next(); if (evt.getTick() <= chordStartTick) { noteOffIter.remove(); continue; } int noteOffId = ((ShortMessage) evt.getMessage()).getData1(); if (noteOffId == noteId) { track.remove(evt); evt.setTick(chordStartTick); track.add(evt); noteOffIter.remove(); break; } } if (!tiedNotes.containsKey(noteId)) { if (info.getPpqn() != PPQN) { throw new ParseException( "The default note length must be the same for all parts of the song", fileName, noteDivisorChangeLine); } track.add( MidiFactory.createNoteOnEventEx( noteId, channel, info.getDynamics().getVol(useLotroInstruments), chordStartTick)); } if (m.group(NOTE_TIE) != null) { int lineAndColumn = (lineNumber << 16) | m.start(); tiedNotes.put(noteId, lineAndColumn); } else { double MPQN = MidiUtils.convertTempo(curTempoBPM); double lengthMicros = (noteEndTick - chordStartTick) * MPQN / PPQN; if (enableLotroErrors && lengthMicros < AbcConstants.SHORTEST_NOTE_MICROS) { throw new LotroParseException( "Note's duration is too short", fileName, lineNumber, m.start()); } else if (enableLotroErrors && lengthMicros > AbcConstants.LONGEST_NOTE_MICROS) { throw new LotroParseException( "Note's duration is too long", fileName, lineNumber, m.start()); } // Stringed instruments, drums, and woodwind breath sounds always play the // sound sample in its entirety. Since Gervill doesn't support the SoundFont // extension that specifies this, we have to increase the note length. // One second should do the trick. long noteEndTickTmp = noteEndTick; if (useLotroInstruments && !info.getInstrument().isSustainable(lotroNoteId)) { noteEndTickTmp = Math.max( noteEndTick, chordStartTick + Math.round(AbcConstants.ONE_SECOND_MICROS * PPQN / MPQN)); } MidiEvent noteOff = MidiFactory.createNoteOffEventEx( noteId, channel, info.getDynamics().getVol(useLotroInstruments), noteEndTickTmp); track.add(noteOff); noteOffEvents.add(noteOff); tiedNotes.remove(noteId); } } if (!inChord) chordStartTick = noteEndTick; i = m.end(); } if (tuplet != null) throw new ParseException("Tuplet not finished by end of line", fileName, lineNumber, i); if (inChord) throw new ParseException("Chord not closed at end of line", fileName, lineNumber, i); if (brokenRhythmDenominator != 1 || brokenRhythmNumerator != 1) throw new ParseException( "Broken rhythm unfinished at end of line", fileName, lineNumber, i); } } if (seq == null) throw new ParseException("The file contains no notes", fileName, lineNumber); for (int lineAndColumn : tiedNotes.values()) { throw new ParseException( "Tied note does not connect to another note", fileName, lineAndColumn >>> 16, lineAndColumn & 0xFFFF); } } PanGenerator pan = null; if (stereo && trackNumber > 1) pan = new PanGenerator(); Track[] tracks = seq.getTracks(); // Add tempo events for (Map.Entry<Long, Integer> tempoEvent : info.getAllPartsTempoMap().entrySet()) { long tick = tempoEvent.getKey(); int mpq = (int) MidiUtils.convertTempo(tempoEvent.getValue()); tracks[0].add(MidiFactory.createTempoEvent(mpq, tick)); } // Add name and pan events tracks[0].add(MidiFactory.createTrackNameEvent(abcInfo.getTitle())); for (int i = 1; i <= trackNumber; i++) { tracks[i].add(MidiFactory.createTrackNameEvent(abcInfo.getPartName(i))); int panAmount = PanGenerator.CENTER; if (pan != null) panAmount = pan.get(abcInfo.getPartInstrument(i), abcInfo.getPartName(i)); tracks[i].add(MidiFactory.createPanEvent(panAmount, getTrackChannel(i))); } return seq; }
public void _testGPWithPlayer(MidiSongDefinition sd, int usq) throws Exception { MidiSongDefinition testFile = SongArchive.testFileSongDefinition(); try { GPInputStream gpis = new GPInputStream(sd.getGpFileName()); GPSong gpsong = (GPSong) gpis.readObject(); gpis.close(); int tempoGPSong = gpsong.getTempo(); // OLD assertEquals((int)(60*1000*1000/usq),tempoGPSong); assertEquals((60 * 1000 * 1000 / usq), tempoGPSong); Song song = GPAdaptor.makeSong(gpsong); Tempo tempoSong = song.getTempo(); assertEquals(usq, (int) tempoSong.getUSQ()); MasterPlayer player = new MasterPlayer(); player.setSoundPlayer(new MidiFiler(testFile.getMidiFileName())); Performance performance = player.arrange(song, null); Tempo tempoPerformance = performance.getTempo(); assertEquals(usq, (int) tempoPerformance.getUSQ()); // a performance is really a sequence. So make sure there // is a tempo event (meta 0x51) on track 0. // make sure as well there is exactly ONE tempo event at timestamp 0 Sequence sequence = (Sequence) performance; Track[] midiTracks = sequence.getTracks(); Track tempoMap = midiTracks[0]; Tempo tempoInMIDI = new Tempo(); for (int i = 0; i < tempoMap.size(); i++) { MidiEvent me = tempoMap.get(i); long tick = me.getTick(); if (tick > 0) break; MidiMessage mm = me.getMessage(); if (mm.getStatus() == MetaMessage.META) { MetaMessage meta = (MetaMessage) mm; if (meta.getType() == 0x51) { byte[] data = meta.getData(); tempoInMIDI.setUSQ( ((data[0] & 0x00FF) << 16) | ((data[1] & 0x00FF) << 8) | ((data[2] & 0x00FF))); break; } } } assertEquals(usq, (int) tempoInMIDI.getUSQ()); MidiOutputStream mos = new MidiOutputStream(new FileOutputStream(testFile.getMidiFileName())); mos.write(performance); mos.close(); compareMIDIFiles( sd.getMidiFileName(), testFile.getMidiFileName(), sd.getChannels(), sd.getEventRemap()); } catch (FileNotFoundException e) { fail("file not found exception"); } catch (GPFormatException e) { fail("gp format exception"); } catch (IOException e) { fail("ioexception"); } catch (CodecFormatException e) { fail("codec format exception"); } catch (InvalidMidiDataException e) { fail("invalid midi data exception"); } }
/** * Create a note and add it to the track. * * @param note The MIDI value for the note to be played * @param startTime When this note should be played * @param duration How long the note should last */ private void createNote(Note note, int startTime, int duration, Track track) { int endTime = startTime + duration; int midiNumber = note.getMidiNumber(); track.add(createNoteEvent(ShortMessage.NOTE_ON, midiNumber, startTime, VELOCITY)); track.add(createNoteEvent(ShortMessage.NOTE_OFF, midiNumber, endTime, 0)); }
/** We assume that we are holding the "this" monitor */ private void addStopEvent(int note) throws InvalidMidiDataException { ShortMessage message = new ShortMessage(); message.setMessage(ShortMessage.NOTE_OFF, 0, note, 0); track.add(new MidiEvent(message, pos)); }