Index: modules/sound/src/main/java/javax/sound/midi/Track.java =================================================================== --- modules/sound/src/main/java/javax/sound/midi/Track.java (revision 429235) +++ modules/sound/src/main/java/javax/sound/midi/Track.java (working copy) @@ -16,29 +16,148 @@ package javax.sound.midi; +import java.util.ArrayList; + public class Track { + private ArrayList events; //vector of events contain in the Track + + private MidiEvent badEvent; //variable to save event which I try to add + //to empty Track; see description below + + private long tick; + + Track() { + /* + * create an empty Track; new Track must contain only meta-event End of Track. + * MetaMessage with MetaMessage.data contains -1, 47 and 0 is meta-event + */ + events = new ArrayList(); + events.add(new MidiEvent(new MetaMessage(new byte[] {-1, 47, 0}), 0)); + } public boolean add(MidiEvent event) { - //TODO - return false; + //FIXME + /* + * Some words about badEvent. + * When I write tests, I find following situation in the RI: + * if I want to add to empty Track new event that is not meta-event End of Track, + * I catch exception ArrayIndexOutOfBoundsException with meaning -1, and my + * event doesn't add to Track, but meta-event adds. If I try to add the same + * event after it, method Track.add(MidiEvent) return 'false', and my event + * doesn't add again. So, I want to delete this event and use method + * Track.remove(MidiEvent) for it, but it return 'false' too! And only after + * this "shamanism" I can add this event to Track normally. + * And only for this situation I use variable badEvent. + * + * See test org.apache.harmony.sound.tests.javax.sound.midi.TrackTest + * for more details + */ + + /* + * if event equals null or badEvent, this method return 'false' + */ + if (event == null || event == badEvent) { + return false; + } + /* + * If event equals meta-event End of Track and Track in this moment + * doesn't contain some events, i.e. Track.size() return 0, this + * event accrue to Track; + * if Track is not empty, but it doesn't contain meta-event, + * this event accrue to the end of Track; + * in any case addition of this meta-event is successful, this method + * return 'true' even if meta-event End of Track already contains in the Track + */ + if (event.getMessage().getMessage()[0] == -1 && + event.getMessage().getMessage()[1] == 47 && + event.getMessage().getMessage()[2] == 0 ) { + if (events.size() == 0) { + return events.add(event); + } else { + byte[] bt = events.get(events.size() - 1).getMessage().getMessage(); + if ((bt[0] != -1) && (bt[1] != 47) && (bt[2] != 0)) { + return events.add(event); + } + } + return true; + } + /* + * after use method Track.add(MidiEvent) Track must contain meta-event + * End of Track; so, at first I add this event to Track if it doesn't + * contain meta-event and parameter 'event' is not meta-event + */ + if (events.size() == 0) { + events.add(new MidiEvent(new MetaMessage(new byte[] {-1, 47, 0}), 0)); + badEvent = event; + throw new ArrayIndexOutOfBoundsException("-1"); + } else { + byte[] bt = events.get(events.size() - 1).getMessage().getMessage(); + if ((bt[0] != -1) && (bt[1] != 47) && (bt[2] != 0)) { + events.add(new MidiEvent(new MetaMessage(new byte[] {-1, 47, 0}), 0)); + } + } + + if (events.contains(event)) { + return false; + } + + /* + * events in the Track must take up position in ascending ticks + */ + if (events.size() == 1) { + events.add(0, event); + } + for (int i = 0; i < events.size() - 1; i++ ) { + if (events.get(i).getTick() <= event.getTick()) { + continue; + } else { + events.add(i, event); + break; + } + } + /* + * method Track.ticks() return the biggest value of tick of all events + * and save it even I remove event with the biggest values of tick + */ + if (tick < event.getTick()) { + tick = event.getTick(); + } + return true; } public MidiEvent get(int index) throws ArrayIndexOutOfBoundsException { - //TODO - return null; + if (index < 0 || index >= events.size()) { + throw new ArrayIndexOutOfBoundsException("Index: " + index + ", Size: " + + events.size()); + } + return events.get(index); } public boolean remove(MidiEvent event) { - //TODO + /* + * if I remove event that equals badEvent, I "delete" badEvent + */ + if (event == badEvent) { + badEvent = null; + return false; + } + /* + * method Track.ticks() always return the biggest value that ever has been + * in the Track; so only when Track is empty Track.ticks() return 0 + */ + if (events.remove(event)) { + if (events.size() == 0) { + tick = 0; + } + return true; + } return false; } public int size() { - //TODO - return 1; + return events.size(); } public long ticks() { - //TODO - return 1L; + return tick; } } Index: modules/sound/src/main/java/javax/sound/midi/Sequence.java =================================================================== --- modules/sound/src/main/java/javax/sound/midi/Sequence.java (revision 429235) +++ modules/sound/src/main/java/javax/sound/midi/Sequence.java (working copy) @@ -34,54 +34,106 @@ protected int resolution; protected Vector tracks; + + private Vector patches; public Sequence(float divisionType, int resolution) throws InvalidMidiDataException { - //TODO + if (divisionType != Sequence.PPQ && + divisionType != Sequence.SMPTE_24 && + divisionType != Sequence.SMPTE_25 && + divisionType != Sequence.SMPTE_30 && + divisionType != Sequence.SMPTE_30DROP ) { + throw new InvalidMidiDataException("Unsupported division type: " + divisionType); + } + this.divisionType = divisionType; + this.resolution = resolution; + this.tracks = new Vector(); + this.patches = new Vector(); + } public Sequence(float divisionType, int resolution, int numTracks) throws InvalidMidiDataException { - //TODO + if (divisionType != Sequence.PPQ && + divisionType != Sequence.SMPTE_24 && + divisionType != Sequence.SMPTE_25 && + divisionType != Sequence.SMPTE_30 && + divisionType != Sequence.SMPTE_30DROP ) { + throw new InvalidMidiDataException("Unsupported division type: " + divisionType); + } + this.divisionType = divisionType; + this.resolution = resolution; + this.patches = new Vector(); + this.tracks = new Vector(); + if (numTracks > 0) { + for (int i = 0; i < numTracks; i++) { + tracks.add(new Track()); + } + } } public Track createTrack() { - //TODO - return null; + /* + * new Tracks accrue to the end of vector + */ + Track tr = new Track(); + tracks.add(tr); + return tr; } public boolean deleteTrack(Track track) { - //TODO - return false; + return tracks.remove(track); } public float getDivisionType() { - //TODO - return 1.0f; + return divisionType; } public long getMicrosecondLength() { - //TODO - return 1L; + float divisionType; + if (this.divisionType == 0.0f) { + divisionType = 2; + } else { + divisionType = this.divisionType; + } + return (long) (1000000.0 * getTickLength() / + (divisionType * this.resolution * 1.0f)); } public Patch[] getPatchList() { - //TODO - return null; + //FIXME + /* + * I don't understand how to works this method, and so + * I simply return an empty array. 'patches' initializes + * in the constructor as empty vector + */ + Patch[] patch = new Patch[patches.size()]; + patches.toArray(patch); + return patch; } public int getResolution() { - //TODO - return 1; + return resolution; } public long getTickLength() { - //TODO - return 1L; + /* + * this method return the biggest value of tick of + * all tracks contain in the Sequence + */ + long maxTick = 0; + for (int i = 0; i < tracks.size(); i++) { + if (maxTick < tracks.get(i).ticks()) { + maxTick = tracks.get(i).ticks(); + } + } + return maxTick; } public Track[] getTracks() { - //TODO - return null; + Track[] track = new Track[tracks.size()]; + tracks.toArray(track); + return track; } }