/** * @param piece a loaded GP4 file * @return a representation of the piece that supports the Song interface * @throws GP4AdaptorException */ public static Song makeSong(GPSong piece) throws GPFormatException { String methodName = "makeSong"; logger.entering(className, methodName); int bpm = piece.getTempo(); logger.finer("Tempo: " + bpm + "BPM"); Tempo tempo = new Tempo(); tempo.setBPM(bpm); Song song = new SongImpl(PPQ_HIGH_RESOLUTION / PPQ_SCALE_FACTOR, tempo); List<GPMeasure> measures = piece.getMeasures(); logger.finer("Number of measures: " + measures.size()); List<GPTrack> tracks = piece.getTracks(); logger.finer("Number of tracks: " + tracks.size()); // List fretStates = new LinkedList(); int index = 1; for (Iterator<GPTrack> it = tracks.iterator(); it.hasNext(); index++) { GPTrack track = it.next(); int port = track.getPort(); int channel = track.getChannel(); int channelEffects = track.getChannelEffects(); logger.fine( "Track " + index + " port " + port + " channels " + channel + "," + channelEffects); int channelIndex = (port - 1) * 16 + (channel - 1); GPMIDIChannel mc = piece.getChannels(channelIndex); int strings = track.getNumberOfStrings(); SongTrack st = new SongTrackImpl(index, strings); // channel is // 1-based st.setPrimaryDevice(new SongDeviceImpl(port, channel)); if (channel != channelEffects) { st.setSecondaryDevice(new SongDeviceImpl(port, channelEffects)); } st.setBendSensitivity(2); int instrument = mc.getInstrument(); logger.fine("Instrument " + instrument); // Only set valid instruments. The drum track usually doesn't have // one. st.setProgram(instrument); // TODO Verify the volume setting is correct st.setVolume(8 * mc.getVolume()); // TODO Verify the pan setting is correct st.setPan(8 * mc.getBalance()); // TODO Verify the chorus setting is correct st.setChorus(8 * mc.getChorus()); // TODO verify the reverb setting is correct st.setReverb(8 * mc.getReverb()); // TODO Verify the tremolo setting is correct st.setTremolo(8 * mc.getTremolo()); // TODO Verify the phaser setting is correct st.setPhaser(8 * mc.getPhaser()); song.addTrack(st); } List<GPMeasureTrackPair> measureTrackPairs = piece.getMeasuresTracksPairs(); logger.finer("Measure/Track pairs: " + measureTrackPairs.size()); ListIterator<GPMeasureTrackPair> itMTP = measureTrackPairs.listIterator(); ListIterator<GPMeasure> itM = measures.listIterator(); while (itM.hasNext()) { SongPhrase sp = phraseFactory(song, itM, itMTP, tracks); song.addPhrase(sp); } logger.exiting(className, methodName, song); return song; }
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 and return a regular SongMeasure from the current position of the iterators. * * @param itM an iterator over the measures collection. It must be pointing so that itM.next() * returns the current measure * @param itMTP an iterator over the measure-track pairs collection. It must be pointing so that * itMTP.next() returns the first track of the current measure * @param tracks The tracks definitions * @return a new filled-out SongMeasure * @throws GP4AdaptorException */ private static SongMeasure makeSongMeasure( Song song, ListIterator<GPMeasure> itM, ListIterator<GPMeasureTrackPair> itMTP, List<GPTrack> tracks) { GPMeasure measure = itM.next(); int ending = measure.getNumberOfAlternateEnding(); if (ending > 0) { logger.warning("Measure " + measure.getNumber() + " is alternate ending " + ending); } // TODO getNumber // TODO getNumberOfAlternateEnding // TODO getNumberOfRepetitions // TODO getTonality int numerator = measure.getNumerator(); int denominator = measure.getDenominator(); int measureLength = PPQ_HIGH_RESOLUTION * 4 * numerator / denominator; // OLD MGG logger.finer("Measure "+measure.getNumber()+", // "+numerator+"/"+denominator+", length " + measureLength); TimeSignature ts = new TimeSignature(numerator, denominator); SongMeasure sm = new SongMeasureImpl(measure.getNumber(), measureLength / PPQ_SCALE_FACTOR, ts); for (int trackIndex = 1; trackIndex <= tracks.size(); trackIndex++) { GPTrack track = tracks.get(trackIndex - 1); // TODO is12StringedGuitarTrack // TODO isBanjoTrack // TODO getter/setter for isDrumsTrack // TODO getChannelEffects // TODO getColor // TODO getName // TODO getNumberOfFrets // TODO getPort int capo = track.getCapo(); int strings = track.getNumberOfStrings(); int stringTuning[] = new int[strings]; for (int i = 0; i < strings; i++) stringTuning[i] = track.getStringsTuning(i); // OLD MGG logger.finer("Measure " + measure.getNumber() + " Track "+ // trackIndex); GPMeasureTrackPair mtp = itMTP.next(); SongMeasureTrack smt = new SongMeasureTrackImpl(song.getTrack(trackIndex)); sm.addTrack(smt); List<GPBeat> beats = mtp.getBeats(); int beatOffset = 0; for (Iterator<GPBeat> itBeat = beats.iterator(); itBeat.hasNext(); ) { GPBeat beat = itBeat.next(); // TODO effects GPDuration duration = beat.getDuration(); // TODO add getter and setter for GP4Beat.dottedNotes boolean dotted = beat.dottedNotes; int ntuplet = beat.getNTuplet(); int beatDuration = calculateDuration(duration, dotted, ntuplet); if (beat.isNoteBeat()) { List<GPNote> notes = beat.getNotes(); List<Integer> stringsPlayed = new LinkedList<Integer>(); // TODO suggest an easier way of returning the played // string list for (int i = 6; i >= 0; i--) { if (beat.isStringPlayed(i)) { stringsPlayed.add(new Integer(i)); } } Iterator<GPNote> itNotes = notes.iterator(); Iterator<Integer> itStrings = stringsPlayed.iterator(); while (itNotes.hasNext()) { // OLD MGG Integer stringObject = (Integer) itStrings.next(); // see TODO.TXT or NEWFEATURES.TXT // this lines were added to fix the GTP playing error - BEGIN Integer stringObject; if (!itStrings.hasNext()) { // this error appears to happen when its *.GTP file // TODO show a friendly error break; } /*ELSE*/ // END stringObject = itStrings.next(); // Note that the tuning array has the highest string // at index 0 // but the track has the highest string at index 6. // TODO find out if this is correct. What about a // 4-string bass? int stringID = 6 - stringObject.intValue(); GPNote note = itNotes.next(); int noteDuration = beatDuration; // see if this note has time independent settings on // it // TODO getter/setter for GP4Note.duration GPDuration tiDuration = note.duration; if (tiDuration != null) { // TODO getter/setter for GP4Note.isDotted boolean tiDotted = note.isDotted; int tiTriplet = note.getNTuplet(); noteDuration = calculateDuration(tiDuration, tiDotted, tiTriplet); } // TODO isDeadNote // TODO isGhostNote // TODO isTieNote // TODO effects // TODO Note effects are a little bit difficult. // Remember these are guitar effects // so we effectively need a 'virtual track' for // every string, so when we playback we // can assign a unique MIDI channel to that track // and so we can apply a channel-wide // controller to that effect (such as pitch bend). // Even the simple effect (let ring) // can't be done easily right now because we can't // tell when to really kill the note. // Of course if we don't need an effect, we should // try to use as few MIDI channels as // we can! int virtualTrackID = stringID; SongVirtualTrack svt = smt.getVirtualTrack(virtualTrackID); int noteStart = beatOffset / PPQ_SCALE_FACTOR; int noteEnd = (beatOffset + noteDuration) / PPQ_SCALE_FACTOR; // FIXME cover the tie note case by grabbing the // last fret if (note.isTieNote()) { svt.addEvent(new SongEventImpl(noteStart, new SongTieMessage(noteEnd - noteStart))); } else { int baseFret = note.getFretNumber(); // OLD // .getNumberOfFret(); int fret = baseFret + capo; GPDynamic dynamic = note.getDynamic(); int velocity = DynamicsMap.velocityOf(dynamic); int pitch = stringTuning[stringID] + fret; svt.addEvent( new SongEventImpl( noteStart, new SongNoteOnMessage(pitch, velocity, noteEnd - noteStart, baseFret))); } } } beatOffset += beatDuration; } if (beatOffset != measureLength) { // an underfull one isn't fatal, but an overfull one is String reason = "Measure: " + measure.getNumber() + " track: " + trackIndex + " length mismatch, expected " + measureLength + ", got " + beatOffset; if (beatOffset > measureLength) { logger.fine(reason); System.err.println(reason); } else { logger.fine(reason); } } } return sm; }