diff options
Diffstat (limited to 'libjava/classpath/gnu/javax/sound')
7 files changed, 1299 insertions, 0 deletions
diff --git a/libjava/classpath/gnu/javax/sound/midi/file/ExtendedMidiFileFormat.java b/libjava/classpath/gnu/javax/sound/midi/file/ExtendedMidiFileFormat.java new file mode 100644 index 00000000000..cb5a8e82dfa --- /dev/null +++ b/libjava/classpath/gnu/javax/sound/midi/file/ExtendedMidiFileFormat.java @@ -0,0 +1,77 @@ +/* ExtendedMidiFileFormat.java -- extended with track count info. + Copyright (C) 2006 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package gnu.javax.sound.midi.file; + +/** + * ExtendedMidiFileFormat is a package private class that simply + * adds the number of MIDI tracks for the MidiFileFormat class. + * + * @author Anthony Green (green@redhat.com) + */ +class ExtendedMidiFileFormat + extends javax.sound.midi.MidiFileFormat +{ + private int ntracks; + + /** + * Get the number of tracks for this MIDI file. + * + * @return the number of tracks for this MIDI file + */ + public int getNumberTracks() + { + return ntracks; + } + + /** + * Create an ExtendedMidiFileFormat object from the given parameters. + * + * @param type the MIDI file type (0, 1, or 2) + * @param divisionType the MIDI file division type + * @param resolution the MIDI file timing resolution + * @param bytes the MIDI file size in bytes + * @param microseconds the MIDI file length in microseconds + * @param ntracks the number of tracks + */ + public ExtendedMidiFileFormat(int type, float divisionType, int resolution, + int bytes, long microseconds, int ntracks) + { + super(type, divisionType, resolution, bytes, microseconds); + this.ntracks = ntracks; + } +} diff --git a/libjava/classpath/gnu/javax/sound/midi/file/MidiDataInputStream.java b/libjava/classpath/gnu/javax/sound/midi/file/MidiDataInputStream.java new file mode 100644 index 00000000000..010d17093c1 --- /dev/null +++ b/libjava/classpath/gnu/javax/sound/midi/file/MidiDataInputStream.java @@ -0,0 +1,83 @@ +/* MidiDataInputStream.java -- adds variable length MIDI ints + Copyright (C) 2006 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package gnu.javax.sound.midi.file; + +import java.io.DataInputStream; +import java.io.IOException; +import java.io.InputStream; + +/** + * MidiDataInputStream is simply a DataInputStream with the addition + * of special variable length int reading as defined by the MIDI spec. + * + * @author Anthony Green (green@redhat.com) + */ +public class MidiDataInputStream + extends DataInputStream +{ + /** + * Create a MidiDataInputStream. + */ + public MidiDataInputStream(InputStream is) + { + super(is); + } + + /** + * Read an int encoded in the MIDI-style variable length + * encoding format. + * + * @return an int + */ + public int readVariableLengthInt() + throws IOException + { + int c, value = readByte(); + + if ((value & 0x80) != 0) + { + value &= 0x7F; + do + { + value = (value << 7) + ((c = readByte()) & 0x7F); + } while ((c & 0x80) != 0); + } + + return value; + } +} diff --git a/libjava/classpath/gnu/javax/sound/midi/file/MidiDataOutputStream.java b/libjava/classpath/gnu/javax/sound/midi/file/MidiDataOutputStream.java new file mode 100644 index 00000000000..f9a3dac8e87 --- /dev/null +++ b/libjava/classpath/gnu/javax/sound/midi/file/MidiDataOutputStream.java @@ -0,0 +1,114 @@ +/* MidiDataOutputStream.java -- adds variable length MIDI ints + Copyright (C) 2006 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package gnu.javax.sound.midi.file; + +import java.io.DataOutputStream; +import java.io.IOException; +import java.io.OutputStream; + +/** + * MidiDataOutputStream is simply a DataOutputStream with the addition + * of special variable length int writing as defined by the MIDI spec. + * + * @author Anthony Green (green@redhat.com) + */ +public class MidiDataOutputStream + extends DataOutputStream +{ + /** + * Create a MidiDataOutputStream. + */ + public MidiDataOutputStream(OutputStream os) + { + super(os); + } + + /** + * Return the length of a variable length encoded int without + * writing it out. + * + * @return the length of the encoding + */ + public int variableLengthIntLength (int value) + { + int length = 0; + int buffer = value & 0x7F; + + while ((value >>= 7) != 0) + { + buffer <<= 8; + buffer |= ((value & 0x7F) | 0x80); + } + + while (true) + { + length++; + if ((buffer & 0x80) != 0) + buffer >>>= 8; + else + break; + } + + return length; + } + + /** + * Write an int encoded in the MIDI-style variable length + * encoding format. + */ + public synchronized void writeVariableLengthInt (int value) + throws IOException + { + int buffer = value & 0x7F; + + while ((value >>= 7) != 0) + { + buffer <<= 8; + buffer |= ((value & 0x7F) | 0x80); + } + + while (true) + { + writeByte(buffer & 0xff); + if ((buffer & 0x80) != 0) + buffer >>>= 8; + else + break; + } + } +} diff --git a/libjava/classpath/gnu/javax/sound/midi/file/MidiFileReader.java b/libjava/classpath/gnu/javax/sound/midi/file/MidiFileReader.java new file mode 100644 index 00000000000..cb640e14a6a --- /dev/null +++ b/libjava/classpath/gnu/javax/sound/midi/file/MidiFileReader.java @@ -0,0 +1,378 @@ +/* MidiFileReader.java -- Read MIDI files. + Copyright (C) 2006 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package gnu.javax.sound.midi.file; + +import java.io.DataInputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.net.URL; + +import javax.sound.midi.InvalidMidiDataException; +import javax.sound.midi.MetaMessage; +import javax.sound.midi.MidiEvent; +import javax.sound.midi.MidiFileFormat; +import javax.sound.midi.MidiMessage; +import javax.sound.midi.Sequence; +import javax.sound.midi.ShortMessage; +import javax.sound.midi.SysexMessage; +import javax.sound.midi.Track; + +/** + * A MIDI file reader. + * + * This code reads MIDI file types 0 and 1. + * + * There are many decent documents on the web describing the MIDI file + * format. I didn't bother looking for the official document. If it + * exists, I'm not even sure if it is freely available. We should + * update this comment if we find out anything helpful here. + * + * @author Anthony Green (green@redhat.com) + * + */ +public class MidiFileReader extends javax.sound.midi.spi.MidiFileReader +{ + /* Get the MidiFileFormat for the given input stream. + * @see javax.sound.midi.spi.MidiFileReader#getMidiFileFormat(java.io.InputStream) + */ + public MidiFileFormat getMidiFileFormat(InputStream in) + throws InvalidMidiDataException, IOException + { + DataInputStream din; + if (in instanceof DataInputStream) + din = (DataInputStream) in; + else + din = new DataInputStream(in); + + int type, ntracks, division, resolution, bytes; + float divisionType; + + if (din.readInt() != 0x4d546864) // "MThd" + throw new InvalidMidiDataException("Invalid MIDI chunk header."); + + bytes = din.readInt(); + if (bytes < 6) + throw new + InvalidMidiDataException("Invalid MIDI chunk header length: " + bytes); + + type = din.readShort(); + if (type < 0 || type > 2) + throw new + InvalidMidiDataException("Invalid MIDI file type value: " + type); + + ntracks = din.readShort(); + if (ntracks <= 0) + throw new + InvalidMidiDataException("Invalid number of MIDI tracks: " + ntracks); + + division = din.readShort(); + if ((division & 0x8000) != 0) + { + division = -((division >>> 8) & 0xFF); + switch (division) + { + case 24: + divisionType = Sequence.SMPTE_24; + break; + + case 25: + divisionType = Sequence.SMPTE_25; + break; + + case 29: + divisionType = Sequence.SMPTE_30DROP; + break; + + case 30: + divisionType = Sequence.SMPTE_30; + break; + + default: + throw new + InvalidMidiDataException("Invalid MIDI frame division type: " + + division); + } + resolution = division & 0xff; + } + else + { + divisionType = Sequence.PPQ; + resolution = division & 0x7fff; + } + + // If we haven't read every byte in the header now, just skip the rest. + din.skip(bytes - 6); + + return new ExtendedMidiFileFormat(type, divisionType, resolution, + MidiFileFormat.UNKNOWN_LENGTH, + MidiFileFormat.UNKNOWN_LENGTH, ntracks); + } + + /* Get the MidiFileFormat from the given URL. + * @see javax.sound.midi.spi.MidiFileReader#getMidiFileFormat(java.net.URL) + */ + public MidiFileFormat getMidiFileFormat(URL url) + throws InvalidMidiDataException, IOException + { + InputStream is = url.openStream(); + try + { + return getMidiFileFormat(is); + } + finally + { + is.close(); + } + } + + /* Get the MidiFileFormat from the given file. + * @see javax.sound.midi.spi.MidiFileReader#getMidiFileFormat(java.io.File) + */ + public MidiFileFormat getMidiFileFormat(File file) + throws InvalidMidiDataException, IOException + { + InputStream is = new FileInputStream(file); + try + { + return getMidiFileFormat(is); + } + finally + { + is.close(); + } + } + + /* Get the MIDI Sequence found in this input stream. + * @see javax.sound.midi.spi.MidiFileReader#getSequence(java.io.InputStream) + */ + public Sequence getSequence(InputStream is) throws InvalidMidiDataException, + IOException + { + MidiDataInputStream din = new MidiDataInputStream(is); + ExtendedMidiFileFormat mff = (ExtendedMidiFileFormat) getMidiFileFormat(din); + + Sequence seq = new Sequence(mff.getDivisionType(), mff.getResolution()); + + int ntracks = mff.getNumberTracks(); + + while (ntracks-- > 0) + { + Track track = seq.createTrack(); + int Mtrk = din.readInt(); + if (Mtrk != 0x4d54726b) + throw new InvalidMidiDataException("Invalid MIDI track header."); + int length = din.readInt(); + + int runningStatus = -1; + int click = 0; + + // Set this to true when we've hit an End of Track meta event. + boolean done = false; + + // Read all events. + while (! done) + { + MidiMessage mm; + int dtime = din.readVariableLengthInt(); + click += dtime; + + int sbyte = din.readUnsignedByte(); + + if (sbyte < 0xf0) + { + ShortMessage sm; + switch (sbyte & 0xf0) + { + case ShortMessage.NOTE_OFF: + case ShortMessage.NOTE_ON: + case ShortMessage.POLY_PRESSURE: + case ShortMessage.CONTROL_CHANGE: + case ShortMessage.PITCH_BEND: + case ShortMessage.SONG_POSITION_POINTER: + sm = new ShortMessage(); + sm.setMessage(sbyte, din.readByte(), din.readByte()); + runningStatus = sbyte; + break; + + case ShortMessage.PROGRAM_CHANGE: + case ShortMessage.CHANNEL_PRESSURE: + case ShortMessage.SONG_SELECT: + case 0xF5: // FIXME: unofficial bus select. Not in spec?? + sm = new ShortMessage(); + sm.setMessage(sbyte, din.readByte(), 0); + runningStatus = sbyte; + break; + + case ShortMessage.TUNE_REQUEST: + case ShortMessage.END_OF_EXCLUSIVE: + case ShortMessage.TIMING_CLOCK: + case ShortMessage.START: + case ShortMessage.CONTINUE: + case ShortMessage.STOP: + case ShortMessage.ACTIVE_SENSING: + case ShortMessage.SYSTEM_RESET: + sm = new ShortMessage(); + sm.setMessage(sbyte, 0, 0); + runningStatus = sbyte; + break; + + default: + if (runningStatus != - 1) + { + switch (runningStatus & 0xf0) + { + case ShortMessage.NOTE_OFF: + case ShortMessage.NOTE_ON: + case ShortMessage.POLY_PRESSURE: + case ShortMessage.CONTROL_CHANGE: + case ShortMessage.PITCH_BEND: + case ShortMessage.SONG_POSITION_POINTER: + sm = new ShortMessage(); + sm.setMessage(runningStatus, sbyte, din.readByte()); + break; + + case ShortMessage.PROGRAM_CHANGE: + case ShortMessage.CHANNEL_PRESSURE: + case ShortMessage.SONG_SELECT: + case 0xF5: // FIXME: unofficial bus select. Not in + // spec?? + sm = new ShortMessage(); + sm.setMessage(runningStatus, sbyte, 0); + continue; + + case ShortMessage.TUNE_REQUEST: + case ShortMessage.END_OF_EXCLUSIVE: + case ShortMessage.TIMING_CLOCK: + case ShortMessage.START: + case ShortMessage.CONTINUE: + case ShortMessage.STOP: + case ShortMessage.ACTIVE_SENSING: + case ShortMessage.SYSTEM_RESET: + sm = new ShortMessage(); + sm.setMessage(runningStatus, 0, 0); + continue; + + default: + throw new + InvalidMidiDataException("Invalid Short MIDI Event: " + + sbyte); + } + } + else + throw new + InvalidMidiDataException("Invalid Short MIDI Event: " + + sbyte); + } + mm = sm; + } + else if (sbyte == 0xf0 || sbyte == 0xf7) + { + // System Exclusive event + int slen = din.readVariableLengthInt(); + byte sysex[] = new byte[slen]; + din.readFully(sysex); + SysexMessage sm = new SysexMessage(); + sm.setMessage(sbyte, sysex, slen); + mm = sm; + runningStatus = - 1; + } + else if (sbyte == 0xff) + { + // Meta Message + byte mtype = din.readByte(); + int mlen = din.readVariableLengthInt(); + byte meta[] = new byte[mlen]; + din.readFully(meta); + MetaMessage metam = new MetaMessage(); + metam.setMessage(mtype, meta, mlen); + mm = metam; + + if (mtype == 0x2f) // End of Track + done = true; + + runningStatus = - 1; + } + else + { + throw new InvalidMidiDataException("Invalid status byte: " + + sbyte); + } + + track.add(new MidiEvent(mm, click)); + } + } + + return seq; + } + + /* Get the MIDI Sequence found at the given URL. + * @see javax.sound.midi.spi.MidiFileReader#getSequence(java.net.URL) + */ + public Sequence getSequence(URL url) throws InvalidMidiDataException, + IOException + { + InputStream is = url.openStream(); + try + { + return getSequence(is); + } + finally + { + is.close(); + } + } + + /* Get the MIDI Sequence found in the given file. + * @see javax.sound.midi.spi.MidiFileReader#getSequence(java.io.File) + */ + public Sequence getSequence(File file) throws InvalidMidiDataException, + IOException + { + InputStream is = new FileInputStream(file); + try + { + return getSequence(is); + } + finally + { + is.close(); + } + } +} diff --git a/libjava/classpath/gnu/javax/sound/midi/file/MidiFileWriter.java b/libjava/classpath/gnu/javax/sound/midi/file/MidiFileWriter.java new file mode 100644 index 00000000000..71970d5ba6e --- /dev/null +++ b/libjava/classpath/gnu/javax/sound/midi/file/MidiFileWriter.java @@ -0,0 +1,199 @@ +/* MidiFileWriter.java -- Write MIDI files. + Copyright (C) 2006 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package gnu.javax.sound.midi.file; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +import javax.sound.midi.MetaMessage; +import javax.sound.midi.MidiEvent; +import javax.sound.midi.Sequence; +import javax.sound.midi.Track; + +/** + * A MIDI file writer. + * + * This code writes MIDI file types 0 and 1. + * + * There are many decent documents on the web describing the MIDI file + * format. I didn't bother looking for the official document. If it + * exists, I'm not even sure if it is freely available. We should + * update this comment if we find out anything helpful here. + * + * @author Anthony Green (green@redhat.com) + * + */ +public class MidiFileWriter + extends javax.sound.midi.spi.MidiFileWriter +{ + /* Return an array indicating which midi file types are supported. + * @see javax.sound.midi.spi.MidiFileWriter#getMidiFileTypes() + */ + public int[] getMidiFileTypes() + { + return new int[]{0, 1}; + } + + /* Return an array indicating which midi file types are supported + * for a given Sequence. + * @see javax.sound.midi.spi.MidiFileWriter#getMidiFileTypes(javax.sound.midi.Sequence) + */ + public int[] getMidiFileTypes(Sequence sequence) + { + if (sequence.getTracks().length == 1) + return new int[]{0}; + else + return new int[]{1}; + } + + /* Write a sequence to an output stream in standard midi format. + * @see javax.sound.midi.spi.MidiFileWriter#write(javax.sound.midi.Sequence, int, java.io.OutputStream) + */ + public int write(Sequence in, int fileType, OutputStream out) + throws IOException + { + MidiDataOutputStream dos = new MidiDataOutputStream (out); + Track[] tracks = in.getTracks(); + dos.writeInt(0x4d546864); // MThd + dos.writeInt(6); + dos.writeShort(fileType); + dos.writeShort(tracks.length); + float divisionType = in.getDivisionType(); + int resolution = in.getResolution(); + // FIXME: division computation is incomplete. + int division = 0; + if (divisionType == Sequence.PPQ) + division = resolution & 0x7fff; + dos.writeShort(division); + int length = 14; + for (int i = 0; i < tracks.length; i++) + length += writeTrack(tracks[i], dos); + return length; + } + + /** + * 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; + } + + /** + * 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; + } + + /* Write a Sequence to a file. + * @see javax.sound.midi.spi.MidiFileWriter#write(javax.sound.midi.Sequence, int, java.io.File) + */ + public int write(Sequence in, int fileType, File out) throws IOException + { + OutputStream os = new FileOutputStream(out); + try + { + return write(in, fileType, os); + } + finally + { + os.close(); + } + } + +} diff --git a/libjava/classpath/gnu/javax/sound/sampled/AU/AUReader.java b/libjava/classpath/gnu/javax/sound/sampled/AU/AUReader.java new file mode 100644 index 00000000000..c9035df7338 --- /dev/null +++ b/libjava/classpath/gnu/javax/sound/sampled/AU/AUReader.java @@ -0,0 +1,212 @@ +/* AUReader.java -- Read AU files. + Copyright (C) 2006 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package gnu.javax.sound.sampled.AU; + +import javax.sound.sampled.AudioFormat; +import javax.sound.sampled.AudioSystem; +import javax.sound.sampled.AudioFileFormat; +import javax.sound.sampled.AudioInputStream; +import javax.sound.sampled.UnsupportedAudioFileException; +import javax.sound.sampled.spi.AudioFileReader; +import java.io.File; +import java.io.IOException; +import java.io.BufferedInputStream; +import java.io.InputStream; +import java.io.DataInputStream; +import java.io.FileInputStream; +import java.net.URL; +import java.nio.ByteBuffer; + +public class AUReader extends AudioFileReader +{ + private static class AUHeader + { + // Magic number identifying the file. '.snd' + private static final int MAGIC = 0x2e736e64; + + public static final int SIZE = 24; // size of the header + + // Encoding types + public static final int ULAW = 1; // 8-bit u-law + public static final int PCM8 = 2; // 8-bit PCM + public static final int PCM16 = 3; // 16-bit PCM + public static final int PCM24 = 4; // 24-bit PCM + public static final int PCM32 = 5; // 32-bit PCM + public static final int IEEE32 = 6; // 32-bit IEEE f.p. + public static final int IEEE64 = 7; // 64-bit IEEE f.p. + public static final int G721 = 23; + public static final int G722 = 24; + public static final int G723 = 25; + public static final int G723_5BIT = 26; + public static final int ALAW = 27; // 8-bit a-law + + // Header data. + public int headerSize; + public int fileSize; // this value may not be set. + public int encoding; + public int sampleRate; + public int channels; + public int sampleSizeInBits; + + public AUHeader(InputStream stream) + throws IOException, UnsupportedAudioFileException + { + byte[] hdr = new byte[24]; + stream.read( hdr ); + ByteBuffer buf = ByteBuffer.wrap(hdr); + + if( buf.getInt() != MAGIC ) + throw new UnsupportedAudioFileException("Not an AU format audio file."); + headerSize = buf.getInt(); + fileSize = buf.getInt(); + encoding = buf.getInt(); + sampleRate = buf.getInt(); + channels = buf.getInt(); + + switch(encoding) + { + case ULAW: + case PCM8: + case ALAW: + sampleSizeInBits = 8; + break; + case PCM16: + sampleSizeInBits = 16; + break; + case PCM24: + sampleSizeInBits = 24; + break; + case PCM32: + sampleSizeInBits = 32; + break; + default: // other types exist but are not supported. Yet. + throw new UnsupportedAudioFileException("Unsupported encoding."); + } + } + + public AudioFormat getAudioFormat() + { + AudioFormat.Encoding encType = AudioFormat.Encoding.PCM_SIGNED; + if(encoding == 1) + encType = AudioFormat.Encoding.ULAW; + if(encoding == 27) + encType = AudioFormat.Encoding.ALAW; + + return new AudioFormat(encType, + (float)sampleRate, + sampleSizeInBits, + channels, + (sampleSizeInBits >> 3) * channels, + (float)sampleRate, + true); + } + + public AudioFileFormat getAudioFileFormat() + { + return new AudioFileFormat(new AUFormatType(), + getAudioFormat(), + AudioSystem.NOT_SPECIFIED); + } + } + + public static class AUFormatType extends AudioFileFormat.Type + { + public AUFormatType() + { + super("AU", ".au"); + } + } + + public AudioFileFormat getAudioFileFormat(File file) + throws IOException, UnsupportedAudioFileException + { + return getAudioFileFormat(new FileInputStream(file)); + } + + public AudioFileFormat getAudioFileFormat(InputStream stream) + throws IOException, UnsupportedAudioFileException + { + if(!stream.markSupported()) + throw new IOException("Stream must support marking."); + + stream.mark(25); + AUHeader header = new AUHeader(stream); + stream.reset(); + + return header.getAudioFileFormat(); + } + + public AudioFileFormat getAudioFileFormat(URL url) + throws IOException, UnsupportedAudioFileException + { + return getAudioFileFormat(new BufferedInputStream(url.openStream())); + } + + public AudioInputStream getAudioInputStream(File file) + throws IOException, UnsupportedAudioFileException + { + InputStream stream = new FileInputStream(file); + long length = file.length(); + + AUHeader header = new AUHeader( stream ); + if( header.headerSize > AUHeader.SIZE ) + stream.skip(header.headerSize - AUHeader.SIZE); + + length -= header.headerSize; + + return new AudioInputStream(stream, header.getAudioFormat(), length); + } + + public AudioInputStream getAudioInputStream(InputStream stream) + throws IOException, UnsupportedAudioFileException + { + AUHeader header = new AUHeader( stream ); + if( header.headerSize > AUHeader.SIZE ) + stream.skip(header.headerSize - AUHeader.SIZE); + + return new AudioInputStream(stream, header.getAudioFormat(), + AudioSystem.NOT_SPECIFIED); + } + + public AudioInputStream getAudioInputStream(URL url) + throws IOException, UnsupportedAudioFileException + { + return getAudioInputStream(new BufferedInputStream(url.openStream())); + } +} + diff --git a/libjava/classpath/gnu/javax/sound/sampled/WAV/WAVReader.java b/libjava/classpath/gnu/javax/sound/sampled/WAV/WAVReader.java new file mode 100644 index 00000000000..0cf3c137797 --- /dev/null +++ b/libjava/classpath/gnu/javax/sound/sampled/WAV/WAVReader.java @@ -0,0 +1,236 @@ +/* WAVReader.java -- Read WAV files. + Copyright (C) 2006 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package gnu.javax.sound.sampled.WAV; + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.io.DataInputStream; +import java.io.FileInputStream; +import java.net.URL; + +import javax.sound.sampled.AudioFormat; +import javax.sound.sampled.AudioFileFormat; +import javax.sound.sampled.AudioInputStream; +import javax.sound.sampled.UnsupportedAudioFileException; +import javax.sound.sampled.spi.AudioFileReader; + +/** + * A WAV file reader. + * + * This code reads WAV files. + * + * There are many decent documents on the web describing the WAV file + * format. I didn't bother looking for the official document. If it + * exists, I'm not even sure if it is freely available. We should + * update this comment if we find out anything helpful here. I used + * http://www.sonicspot.com/guide/wavefiles.html + * + * @author Anthony Green (green@redhat.com) + * + */ +public class WAVReader extends AudioFileReader +{ + private static long readUnsignedIntLE (DataInputStream is) + throws IOException + { + byte[] buf = new byte[4]; + is.readFully(buf); + return (buf[0] & 0xFF + | ((buf[1] & 0xFF) << 8) + | ((buf[2] & 0xFF) << 16) + | ((buf[3] & 0xFF) << 24)); + } + + private static short readUnsignedShortLE (DataInputStream is) + throws IOException + { + byte[] buf = new byte[2]; + is.readFully(buf); + return (short) (buf[0] & 0xFF + | ((buf[1] & 0xFF) << 8)); + } + + /* Get an AudioFileFormat from the given File. + * @see javax.sound.sampled.spi.AudioFileReader#getAudioFileFormat(java.io.File) + */ + public AudioFileFormat getAudioFileFormat(File file) + throws UnsupportedAudioFileException, IOException + { + InputStream is = new FileInputStream(file); + try + { + return getAudioFileFormat(is); + } + finally + { + is.close(); + } + } + + /* Get an AudioFileFormat from the given InputStream. + * @see javax.sound.sampled.spi.AudioFileReader#getAudioFileFormat(java.io.InputStream) + */ + public AudioFileFormat getAudioFileFormat(InputStream in) + throws UnsupportedAudioFileException, IOException + { + DataInputStream din; + + if (in instanceof DataInputStream) + din = (DataInputStream) in; + else + din = new DataInputStream(in); + + if (din.readInt() != 0x52494646) // "RIFF" + throw new UnsupportedAudioFileException("Invalid WAV chunk header."); + + // Read the length of this RIFF thing. + readUnsignedIntLE(din); + + if (din.readInt() != 0x57415645) // "WAVE" + throw new UnsupportedAudioFileException("Invalid WAV chunk header."); + + boolean foundFmt = false; + boolean foundData = false; + + short compressionCode = 0, numberChannels = 0, blockAlign = 0, bitsPerSample = 0; + long sampleRate = 0, bytesPerSecond = 0; + long chunkLength = 0; + + while (! foundData) + { + int chunkId = din.readInt(); + chunkLength = readUnsignedIntLE(din); + switch (chunkId) + { + case 0x666D7420: // "fmt " + foundFmt = true; + compressionCode = readUnsignedShortLE(din); + numberChannels = readUnsignedShortLE(din); + sampleRate = readUnsignedIntLE(din); + bytesPerSecond = readUnsignedIntLE(din); + blockAlign = readUnsignedShortLE(din); + bitsPerSample = readUnsignedShortLE(din); + din.skip(chunkLength - 16); + break; + case 0x66616374: // "fact" + // FIXME: hold compression format dependent data. + din.skip(chunkLength); + break; + case 0x64617461: // "data" + if (! foundFmt) + throw new UnsupportedAudioFileException("This implementation requires WAV fmt chunks precede data chunks."); + foundData = true; + break; + default: + // Unrecognized chunk. Skip it. + din.skip(chunkLength); + } + } + + AudioFormat.Encoding encoding; + + switch (compressionCode) + { + case 1: // PCM/uncompressed + if (bitsPerSample <= 8) + encoding = AudioFormat.Encoding.PCM_UNSIGNED; + else + encoding = AudioFormat.Encoding.PCM_SIGNED; + break; + + default: + throw new UnsupportedAudioFileException("Unrecognized WAV compression code: 0x" + + Integer.toHexString(compressionCode)); + } + + return new AudioFileFormat (AudioFileFormat.Type.WAVE, + new AudioFormat(encoding, + (float) sampleRate, + bitsPerSample, + numberChannels, + ((bitsPerSample + 7) / 8) * numberChannels, + (float) bytesPerSecond, false), + (int) chunkLength); + } + + /* Get an AudioFileFormat from the given URL. + * @see javax.sound.sampled.spi.AudioFileReader#getAudioFileFormat(java.net.URL) + */ + public AudioFileFormat getAudioFileFormat(URL url) + throws UnsupportedAudioFileException, IOException + { + InputStream is = url.openStream(); + try + { + return getAudioFileFormat(is); + } + finally + { + is.close(); + } + } + + /* Get an AudioInputStream from the given File. + * @see javax.sound.sampled.spi.AudioFileReader#getAudioInputStream(java.io.File) + */ + public AudioInputStream getAudioInputStream(File file) + throws UnsupportedAudioFileException, IOException + { + return getAudioInputStream(new FileInputStream(file)); + } + + /* Get an AudioInputStream from the given InputStream. + * @see javax.sound.sampled.spi.AudioFileReader#getAudioInputStream(java.io.InputStream) + */ + public AudioInputStream getAudioInputStream(InputStream stream) + throws UnsupportedAudioFileException, IOException + { + AudioFileFormat aff = getAudioFileFormat(stream); + return new AudioInputStream(stream, aff.getFormat(), (long) aff.getFrameLength()); + } + + /* Get an AudioInputStream from the given URL. + * @see javax.sound.sampled.spi.AudioFileReader#getAudioInputStream(java.net.URL) + */ + public AudioInputStream getAudioInputStream(URL url) + throws UnsupportedAudioFileException, IOException + { + return getAudioInputStream(url.openStream()); + } +} |