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 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(); } }
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 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 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)); } } }
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; }
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 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 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
/** * 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 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
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; }
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); } } } }
protected static long addEventsToTrack(Track track, MidiEvent event) { track.add(event); 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; }
/** @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(); }
/** * 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; }
/** * 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)); }