/**
   * @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;
  }
Exemple #2
0
  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;
  }