summaryrefslogtreecommitdiff
path: root/songdbj/org/tritonus/file
diff options
context:
space:
mode:
Diffstat (limited to 'songdbj/org/tritonus/file')
-rw-r--r--songdbj/org/tritonus/file/AiffAudioFileReader.java244
-rw-r--r--songdbj/org/tritonus/file/AiffAudioFileWriter.java104
-rw-r--r--songdbj/org/tritonus/file/AiffAudioOutputStream.java205
-rw-r--r--songdbj/org/tritonus/file/AiffTool.java82
-rw-r--r--songdbj/org/tritonus/file/AuAudioFileReader.java185
-rw-r--r--songdbj/org/tritonus/file/AuAudioFileWriter.java104
-rw-r--r--songdbj/org/tritonus/file/AuAudioOutputStream.java121
-rw-r--r--songdbj/org/tritonus/file/AuTool.java95
-rw-r--r--songdbj/org/tritonus/file/WaveAudioFileReader.java300
-rw-r--r--songdbj/org/tritonus/file/WaveAudioFileWriter.java103
-rw-r--r--songdbj/org/tritonus/file/WaveAudioOutputStream.java201
-rw-r--r--songdbj/org/tritonus/file/WaveTool.java115
-rw-r--r--songdbj/org/tritonus/file/gsm/GSMAudioFileReader.java142
-rw-r--r--songdbj/org/tritonus/file/gsm/GSMAudioFileWriter.java77
-rw-r--r--songdbj/org/tritonus/file/gsm/package.html10
-rw-r--r--songdbj/org/tritonus/file/jorbis/JorbisAudioFileReader.java231
-rw-r--r--songdbj/org/tritonus/file/jorbis/package.html10
-rw-r--r--songdbj/org/tritonus/file/mpeg/MpegAudioFileWriter.java75
-rw-r--r--songdbj/org/tritonus/file/mpeg/package.html10
-rw-r--r--songdbj/org/tritonus/file/package.html10
-rw-r--r--songdbj/org/tritonus/file/pvorbis/VorbisAudioFileReader.java302
-rw-r--r--songdbj/org/tritonus/file/pvorbis/VorbisAudioFileWriter.java75
-rw-r--r--songdbj/org/tritonus/file/pvorbis/package.html12
-rw-r--r--songdbj/org/tritonus/file/vorbis/VorbisAudioFileReader.java302
-rw-r--r--songdbj/org/tritonus/file/vorbis/VorbisAudioFileWriter.java75
-rw-r--r--songdbj/org/tritonus/file/vorbis/package.html12
26 files changed, 3202 insertions, 0 deletions
diff --git a/songdbj/org/tritonus/file/AiffAudioFileReader.java b/songdbj/org/tritonus/file/AiffAudioFileReader.java
new file mode 100644
index 0000000000..139ba05425
--- /dev/null
+++ b/songdbj/org/tritonus/file/AiffAudioFileReader.java
@@ -0,0 +1,244 @@
1/*
2 * AiffAudioFileReader.java
3 *
4 * This file is part of Tritonus: http://www.tritonus.org/
5 */
6
7/*
8 * Copyright (c) 2000 by Florian Bomers <http://www.bomers.de>
9 * Copyright (c) 1999 by Matthias Pfisterer
10 *
11 *
12 * This program is free software; you can redistribute it and/or modify
13 * it under the terms of the GNU Library General Public License as published
14 * by the Free Software Foundation; either version 2 of the License, or
15 * (at your option) any later version.
16 *
17 * This program is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 * GNU Library General Public License for more details.
21 *
22 * You should have received a copy of the GNU Library General Public
23 * License along with this program; if not, write to the Free Software
24 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
25 *
26 */
27
28/*
29|<--- this code is formatted to fit into 80 columns --->|
30*/
31
32package org.tritonus.sampled.file;
33
34import java.io.DataInputStream;
35import java.io.File;
36import java.io.InputStream;
37import java.io.IOException;
38
39import javax.sound.sampled.AudioFormat;
40import javax.sound.sampled.AudioFileFormat;
41import javax.sound.sampled.AudioInputStream;
42import javax.sound.sampled.AudioSystem;
43import javax.sound.sampled.UnsupportedAudioFileException;
44
45import org.tritonus.share.sampled.file.TAudioFileFormat;
46import org.tritonus.share.sampled.file.TAudioFileReader;
47import org.tritonus.share.TDebug;
48
49
50/** Class for reading AIFF and AIFF-C files.
51 *
52 * @author Florian Bomers
53 * @author Matthias Pfisterer
54 */
55public class AiffAudioFileReader extends TAudioFileReader
56{
57 private static final int READ_LIMIT = 1000;
58
59
60
61 public AiffAudioFileReader()
62 {
63 super(READ_LIMIT);
64 }
65
66
67
68 private void skipChunk(DataInputStream dataInputStream, int chunkLength, int chunkRead)
69 throws IOException {
70 chunkLength-=chunkRead;
71 if (chunkLength>0) {
72 dataInputStream.skip(chunkLength + (chunkLength % 2));
73 }
74 }
75
76 private AudioFormat readCommChunk(DataInputStream dataInputStream, int chunkLength)
77 throws IOException, UnsupportedAudioFileException {
78
79 int nNumChannels = dataInputStream.readShort();
80 if (nNumChannels <= 0) {
81 throw new UnsupportedAudioFileException(
82 "not an AIFF file: number of channels must be positive");
83 }
84 if (TDebug.TraceAudioFileReader) {
85 TDebug.out("Found "+nNumChannels+" channels.");
86 }
87 // ignored: frame count
88 dataInputStream.readInt();
89 int nSampleSize = dataInputStream.readShort();
90 float fSampleRate = (float) readIeeeExtended(dataInputStream);
91 if (fSampleRate <= 0.0) {
92 throw new UnsupportedAudioFileException(
93 "not an AIFF file: sample rate must be positive");
94 }
95 if (TDebug.TraceAudioFileReader) {
96 TDebug.out("Found framerate "+fSampleRate);
97 }
98 AudioFormat.Encoding encoding = AudioFormat.Encoding.PCM_SIGNED;
99 int nRead=18;
100 if (chunkLength>nRead) {
101 int nEncoding=dataInputStream.readInt();
102 nRead+=4;
103 if (nEncoding==AiffTool.AIFF_COMM_PCM) {
104 // PCM - nothing to do
105 }
106 else if (nEncoding==AiffTool.AIFF_COMM_ULAW) {
107 // ULAW
108 encoding=AudioFormat.Encoding.ULAW;
109 nSampleSize=8;
110 }
111 else if (nEncoding==AiffTool.AIFF_COMM_IMA_ADPCM) {
112 encoding = new AudioFormat.Encoding("IMA_ADPCM");
113 nSampleSize=4;
114 }
115 else {
116 throw new UnsupportedAudioFileException(
117 "Encoding 0x"+Integer.toHexString(nEncoding)
118 +" of AIFF file not supported");
119 }
120 }
121 /* In case of IMA ADPCM, frame size is 0.5 bytes (since it is
122 always mono). A value of 1 as frame size would be wrong.
123 Handling of frame size 0 in defined nowhere. So the best
124 solution is to set the frame size to unspecified (-1).
125 */
126 int nFrameSize = (nSampleSize == 4) ?
127 AudioSystem.NOT_SPECIFIED :
128 calculateFrameSize(nSampleSize, nNumChannels);
129 if (TDebug.TraceAudioFileReader) { TDebug.out("calculated frame size: " + nFrameSize); }
130 skipChunk(dataInputStream, chunkLength, nRead);
131 AudioFormat format = new AudioFormat(encoding,
132 fSampleRate,
133 nSampleSize,
134 nNumChannels,
135 nFrameSize,
136 fSampleRate,
137 true);
138 return format;
139 }
140
141 private void readVerChunk(DataInputStream dataInputStream, int chunkLength)
142 throws IOException, UnsupportedAudioFileException {
143 if (chunkLength<4) {
144 throw new UnsupportedAudioFileException(
145 "Corrput AIFF file: FVER chunk too small.");
146 }
147 int nVer=dataInputStream.readInt();
148 if (nVer!=AiffTool.AIFF_FVER_TIME_STAMP) {
149 throw new UnsupportedAudioFileException(
150 "Unsupported AIFF file: version not known.");
151 }
152 skipChunk(dataInputStream, chunkLength, 4);
153 }
154
155
156
157 protected AudioFileFormat getAudioFileFormat(InputStream inputStream, long lFileSizeInBytes)
158 throws UnsupportedAudioFileException, IOException
159 {
160 if (TDebug.TraceAudioFileReader) {TDebug.out("AiffAudioFileReader.getAudioFileFormat(InputStream, long): begin"); }
161 DataInputStream dataInputStream = new DataInputStream(inputStream);
162 int nMagic = dataInputStream.readInt();
163 if (nMagic != AiffTool.AIFF_FORM_MAGIC) {
164 throw new UnsupportedAudioFileException(
165 "not an AIFF file: header magic is not FORM");
166 }
167 int nTotalLength = dataInputStream.readInt();
168 nMagic = dataInputStream.readInt();
169 boolean bIsAifc;
170 if (nMagic == AiffTool.AIFF_AIFF_MAGIC) {
171 bIsAifc = false;
172 } else if (nMagic == AiffTool.AIFF_AIFC_MAGIC) {
173 bIsAifc = true;
174 } else {
175 throw new UnsupportedAudioFileException(
176 "unsupported IFF file: header magic neither AIFF nor AIFC");
177 }
178 boolean bFVerFound=!bIsAifc;
179 boolean bCommFound=false;
180 boolean bSSndFound=false;
181 AudioFormat format=null;
182 int nDataChunkLength=0;
183
184 // walk through the chunks
185 // chunks may be in any order. However, in this implementation, SSND must be last
186 while (!bFVerFound || !bCommFound || !bSSndFound) {
187 nMagic = dataInputStream.readInt();
188 int nChunkLength = dataInputStream.readInt();
189 switch (nMagic) {
190 case AiffTool.AIFF_COMM_MAGIC:
191 format=readCommChunk(dataInputStream, nChunkLength);
192 if (TDebug.TraceAudioFileReader) {
193 TDebug.out("Read COMM chunk with length "+nChunkLength);
194 }
195 bCommFound=true;
196 break;
197 case AiffTool.AIFF_FVER_MAGIC:
198 if (!bFVerFound) {
199 readVerChunk(dataInputStream, nChunkLength);
200 if (TDebug.TraceAudioFileReader) {
201 TDebug.out("Read FVER chunk with length "+nChunkLength);
202 }
203 bFVerFound=true;
204 } else {
205 skipChunk(dataInputStream, nChunkLength, 0);
206 }
207 break;
208 case AiffTool.AIFF_SSND_MAGIC:
209 if (!bCommFound || !bFVerFound) {
210 throw new UnsupportedAudioFileException(
211 "cannot handle AIFF file: SSND not last chunk");
212 }
213 bSSndFound=true;
214 nDataChunkLength=nChunkLength-8;
215 // 8 information bytes of no interest
216 dataInputStream.skip(8);
217 if (TDebug.TraceAudioFileReader) {
218 TDebug.out("Found SSND chunk with length "+nChunkLength);
219 }
220 break;
221 default:
222 if (TDebug.TraceAudioFileReader) {
223 TDebug.out("Skipping unknown chunk: "
224 +Integer.toHexString(nMagic));
225 }
226 skipChunk(dataInputStream, nChunkLength, 0);
227 break;
228 }
229 }
230
231 // TODO: length argument has to be in frames
232 AudioFileFormat audioFileFormat = new TAudioFileFormat(
233 bIsAifc ? AudioFileFormat.Type.AIFC : AudioFileFormat.Type.AIFF,
234 format,
235 nDataChunkLength / format.getFrameSize(),
236 nTotalLength + 8);
237 if (TDebug.TraceAudioFileReader) {TDebug.out("AiffAudioFileReader.getAudioFileFormat(InputStream, long): end"); }
238 return audioFileFormat;
239 }
240}
241
242
243
244/*** AiffAudioFileReader.java ***/
diff --git a/songdbj/org/tritonus/file/AiffAudioFileWriter.java b/songdbj/org/tritonus/file/AiffAudioFileWriter.java
new file mode 100644
index 0000000000..cfd6996ae9
--- /dev/null
+++ b/songdbj/org/tritonus/file/AiffAudioFileWriter.java
@@ -0,0 +1,104 @@
1/*
2 * AiffAudioFileWriter.java
3 *
4 * This file is part of Tritonus: http://www.tritonus.org/
5 */
6
7/*
8 * Copyright (c) 2000 by Florian Bomers <http://www.bomers.de>
9 *
10 *
11 * This program is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU Library General Public License as published
13 * by the Free Software Foundation; either version 2 of the License, or
14 * (at your option) any later version.
15 *
16 * This program is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU Library General Public License for more details.
20 *
21 * You should have received a copy of the GNU Library General Public
22 * License along with this program; if not, write to the Free Software
23 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
24 *
25 */
26
27/*
28|<--- this code is formatted to fit into 80 columns --->|
29*/
30
31package org.tritonus.sampled.file;
32
33import java.io.IOException;
34import java.util.Arrays;
35
36import javax.sound.sampled.AudioFileFormat;
37import javax.sound.sampled.AudioFormat;
38import javax.sound.sampled.AudioInputStream;
39import javax.sound.sampled.AudioSystem;
40
41import org.tritonus.share.TDebug;
42import org.tritonus.share.sampled.file.AudioOutputStream;
43import org.tritonus.share.sampled.file.TAudioFileWriter;
44import org.tritonus.share.sampled.file.TDataOutputStream;
45
46
47/**
48 * Class for writing AIFF and AIFF-C files.
49 *
50 * @author Florian Bomers
51 */
52
53public class AiffAudioFileWriter extends TAudioFileWriter {
54
55 private static final AudioFileFormat.Type[] FILE_TYPES =
56 {
57 AudioFileFormat.Type.AIFF,
58 AudioFileFormat.Type.AIFC
59 };
60
61 private static final int ALL=AudioSystem.NOT_SPECIFIED;
62 private static final AudioFormat.Encoding PCM_SIGNED = AudioFormat.Encoding.PCM_SIGNED;
63 private static final AudioFormat.Encoding ULAW = AudioFormat.Encoding.ULAW;
64 private static final AudioFormat.Encoding IMA_ADPCM = new AudioFormat.Encoding("IMA_ADPCM");
65
66 // IMPORTANT: this array depends on the AudioFormat.match() algorithm which takes
67 // AudioSystem.NOT_SPECIFIED into account !
68 private static final AudioFormat[] AUDIO_FORMATS =
69 {
70 new AudioFormat(PCM_SIGNED, ALL, 8, ALL, ALL, ALL, true),
71 new AudioFormat(PCM_SIGNED, ALL, 8, ALL, ALL, ALL, false),
72 new AudioFormat(ULAW, ALL, 8, ALL, ALL, ALL, false),
73 new AudioFormat(ULAW, ALL, 8, ALL, ALL, ALL, true),
74 new AudioFormat(PCM_SIGNED, ALL, 16, ALL, ALL, ALL, true),
75 new AudioFormat(PCM_SIGNED, ALL, 24, ALL, ALL, ALL, true),
76 new AudioFormat(PCM_SIGNED, ALL, 32, ALL, ALL, ALL, true),
77 new AudioFormat(IMA_ADPCM, ALL, 4, ALL, ALL, ALL, true),
78 new AudioFormat(IMA_ADPCM, ALL, 4, ALL, ALL, ALL, false),
79 };
80
81 public AiffAudioFileWriter() {
82 super(Arrays.asList(FILE_TYPES),
83 Arrays.asList(AUDIO_FORMATS));
84 }
85
86
87 protected boolean isAudioFormatSupportedImpl(AudioFormat format,
88 AudioFileFormat.Type fileType) {
89 return AiffTool.getFormatCode(format)!=AiffTool.AIFF_COMM_UNSPECIFIED;
90 }
91
92
93 protected AudioOutputStream getAudioOutputStream(AudioFormat audioFormat,
94 long lLengthInBytes,
95 AudioFileFormat.Type fileType,
96 TDataOutputStream dataOutputStream) throws IOException {
97 return new AiffAudioOutputStream(audioFormat, fileType,
98 lLengthInBytes,
99 dataOutputStream);
100 }
101
102}
103
104/*** AiffAudioFileWriter.java ***/
diff --git a/songdbj/org/tritonus/file/AiffAudioOutputStream.java b/songdbj/org/tritonus/file/AiffAudioOutputStream.java
new file mode 100644
index 0000000000..d0006ebfe0
--- /dev/null
+++ b/songdbj/org/tritonus/file/AiffAudioOutputStream.java
@@ -0,0 +1,205 @@
1/*
2 * AiffAudioOutputStream.java
3 *
4 * This file is part of Tritonus: http://www.tritonus.org/
5 */
6
7/*
8 * Copyright (c) 2000 by Florian Bomers <http://www.bomers.de>
9 *
10 *
11 * This program is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU Library General Public License as published
13 * by the Free Software Foundation; either version 2 of the License, or
14 * (at your option) any later version.
15 *
16 * This program is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU Library General Public License for more details.
20 *
21 * You should have received a copy of the GNU Library General Public
22 * License along with this program; if not, write to the Free Software
23 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
24 *
25 */
26
27/*
28|<--- this code is formatted to fit into 80 columns --->|
29*/
30
31package org.tritonus.sampled.file;
32
33import java.io.IOException;
34import javax.sound.sampled.AudioFormat;
35import javax.sound.sampled.AudioFileFormat;
36import javax.sound.sampled.AudioSystem;
37import org.tritonus.share.TDebug;
38import org.tritonus.share.sampled.file.TAudioOutputStream;
39import org.tritonus.share.sampled.file.TDataOutputStream;
40
41
42/**
43 * AudioOutputStream for AIFF and AIFF-C files.
44 *
45 * @author Florian Bomers
46 */
47public class AiffAudioOutputStream extends TAudioOutputStream {
48
49 // this constant is used for chunk lengths when the length is not known yet
50 private static final int LENGTH_NOT_KNOWN=-1;
51
52 private AudioFileFormat.Type m_FileType;
53
54 public AiffAudioOutputStream(AudioFormat audioFormat,
55 AudioFileFormat.Type fileType,
56 long lLength,
57 TDataOutputStream dataOutputStream) {
58 super(audioFormat,
59 lLength,
60 dataOutputStream,
61 lLength == AudioSystem.NOT_SPECIFIED
62 && dataOutputStream.supportsSeek());
63 // AIFF files cannot exceed 2GB
64 if (lLength != AudioSystem.NOT_SPECIFIED && lLength>0x7FFFFFFFl) {
65 throw new IllegalArgumentException(
66 "AIFF files cannot be larger than 2GB.");
67 }
68 // IDEA: write AIFF file instead of AIFC when encoding=PCM ?
69 m_FileType=fileType;
70 if (!audioFormat.getEncoding().equals(AudioFormat.Encoding.PCM_SIGNED)
71 && !audioFormat.getEncoding().equals(AudioFormat.Encoding.PCM_UNSIGNED)) {
72 // only AIFC files can handle non-pcm data
73 m_FileType=AudioFileFormat.Type.AIFC;
74 }
75 }
76
77 protected void writeHeader()
78 throws IOException {
79 if (TDebug.TraceAudioOutputStream) {
80 TDebug.out("AiffAudioOutputStream.writeHeader(): called.");
81 }
82 AudioFormat format = getFormat();
83 boolean bIsAifc = m_FileType.equals(AudioFileFormat.Type.AIFC);
84 long lLength = getLength();
85 TDataOutputStream dos = getDataOutputStream();
86 int nCommChunkSize=18;
87 int nFormatCode=AiffTool.getFormatCode(format);
88 if (bIsAifc) {
89 // encoding takes 4 bytes
90 // encoding name takes at minimum 2 bytes
91 nCommChunkSize+=6;
92 }
93 int nHeaderSize=4 // magic
94 +8+nCommChunkSize // COMM chunk
95 +8; // header of SSND chunk
96 if (bIsAifc) {
97 // add length for FVER chunk
98 nHeaderSize+=12;
99 }
100 // if patching the header, and the length has not been known at first
101 // writing of the header, just truncate the size fields, don't throw an exception
102 if (lLength != AudioSystem.NOT_SPECIFIED && lLength+nHeaderSize>0x7FFFFFFFl) {
103 lLength=0x7FFFFFFFl-nHeaderSize;
104 }
105 // chunks must be on word-boundaries
106 long lSSndChunkSize=(lLength!=AudioSystem.NOT_SPECIFIED)?
107 (lLength+(lLength%2)+8):AudioSystem.NOT_SPECIFIED;
108
109 // write IFF container chunk
110 dos.writeInt(AiffTool.AIFF_FORM_MAGIC);
111 dos.writeInt((lLength!=AudioSystem.NOT_SPECIFIED)?
112 ((int) (lSSndChunkSize+nHeaderSize)):LENGTH_NOT_KNOWN);
113 if (bIsAifc) {
114 dos.writeInt(AiffTool.AIFF_AIFC_MAGIC);
115 // write FVER chunk
116 dos.writeInt(AiffTool.AIFF_FVER_MAGIC);
117 dos.writeInt(4);
118 dos.writeInt(AiffTool.AIFF_FVER_TIME_STAMP);
119 } else {
120 dos.writeInt(AiffTool.AIFF_AIFF_MAGIC);
121 }
122
123 // write COMM chunk
124 dos.writeInt(AiffTool.AIFF_COMM_MAGIC);
125 dos.writeInt(nCommChunkSize);
126 dos.writeShort((short) format.getChannels());
127 dos.writeInt((lLength!=AudioSystem.NOT_SPECIFIED)?
128 ((int) (lLength / format.getFrameSize())):LENGTH_NOT_KNOWN);
129 if (nFormatCode==AiffTool.AIFF_COMM_ULAW) {
130 // AIFF ulaw states 16 bits for ulaw data
131 dos.writeShort(16);
132 } else {
133 dos.writeShort((short) format.getSampleSizeInBits());
134 }
135 writeIeeeExtended(dos, format.getSampleRate());
136 if (bIsAifc) {
137 dos.writeInt(nFormatCode);
138 dos.writeShort(0); // no encoding name
139 // TODO: write encoding.toString() ??
140 }
141
142 // write header of SSND chunk
143
144
145
146 dos.writeInt(AiffTool.AIFF_SSND_MAGIC);
147 // don't use lSSndChunkSize here !
148 dos.writeInt((lLength!=AudioSystem.NOT_SPECIFIED)
149 ?((int) (lLength+8)):LENGTH_NOT_KNOWN);
150 // 8 information bytes of no interest
151 dos.writeInt(0); // offset
152 dos.writeInt(0); // blocksize
153 }
154
155
156
157
158 protected void patchHeader()
159 throws IOException {
160 TDataOutputStream tdos = getDataOutputStream();
161 tdos.seek(0);
162 setLengthFromCalculatedLength();
163 writeHeader();
164 }
165
166 public void close() throws IOException {
167 long nBytesWritten=getCalculatedLength();
168
169 if ((nBytesWritten % 2)==1) {
170 if (TDebug.TraceAudioOutputStream) {
171 TDebug.out("AiffOutputStream.close(): adding padding byte");
172 }
173 // extra byte for to align on word boundaries
174 TDataOutputStream tdos = getDataOutputStream();
175 tdos.writeByte(0);
176 // DON'T adjust calculated length !
177 }
178
179
180
181 super.close();
182 }
183
184 public void writeIeeeExtended(TDataOutputStream dos, float sampleRate) throws IOException {
185 // currently, only integer sample rates are written
186 // TODO: real conversion
187 // I don't know exactly how much I have to shift left the mantisse for normalisation
188 // now I do it so that there are any bits set in the first 5 bits
189 int nSampleRate=(int) sampleRate;
190 short ieeeExponent=0;
191 while ((nSampleRate!=0) && (nSampleRate & 0x80000000)==0) {
192 ieeeExponent++;
193 nSampleRate<<=1;
194 }
195 dos.writeShort(16414-ieeeExponent); // exponent
196 dos.writeInt(nSampleRate); // mantisse high double word
197 dos.writeInt(0); // mantisse low double word
198 }
199
200
201
202
203}
204
205/*** AiffAudioOutputStream.java ***/
diff --git a/songdbj/org/tritonus/file/AiffTool.java b/songdbj/org/tritonus/file/AiffTool.java
new file mode 100644
index 0000000000..39cdbf0878
--- /dev/null
+++ b/songdbj/org/tritonus/file/AiffTool.java
@@ -0,0 +1,82 @@
1/*
2 * AiffTool.java
3 *
4 * This file is part of Tritonus: http://www.tritonus.org/
5 */
6
7/*
8 * Copyright (c) 2000 by Florian Bomers <http://www.bomers.de>
9 *
10 *
11 * This program is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU Library General Public License as published
13 * by the Free Software Foundation; either version 2 of the License, or
14 * (at your option) any later version.
15 *
16 * This program is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU Library General Public License for more details.
20 *
21 * You should have received a copy of the GNU Library General Public
22 * License along with this program; if not, write to the Free Software
23 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
24 *
25 */
26
27/*
28|<--- this code is formatted to fit into 80 columns --->|
29*/
30
31package org.tritonus.sampled.file;
32
33import javax.sound.sampled.AudioFormat;
34import javax.sound.sampled.AudioFileFormat;
35import javax.sound.sampled.AudioSystem;
36
37
38/**
39 * Common constants and methods for handling aiff and aiff-c files.
40 *
41 * @author Florian Bomers
42 */
43
44public class AiffTool {
45
46 public static final int AIFF_FORM_MAGIC = 0x464F524D;
47 public static final int AIFF_AIFF_MAGIC = 0x41494646;
48 public static final int AIFF_AIFC_MAGIC = 0x41494643;
49 public static final int AIFF_COMM_MAGIC = 0x434F4D4D;
50 public static final int AIFF_SSND_MAGIC = 0x53534E44;
51 public static final int AIFF_FVER_MAGIC = 0x46564552;
52 public static final int AIFF_COMM_UNSPECIFIED = 0x00000000; // "0000"
53 public static final int AIFF_COMM_PCM = 0x4E4F4E45; // "NONE"
54 public static final int AIFF_COMM_ULAW = 0x756C6177; // "ulaw"
55 public static final int AIFF_COMM_IMA_ADPCM = 0x696D6134; // "ima4"
56 public static final int AIFF_FVER_TIME_STAMP = 0xA2805140; // May 23, 1990, 2:40pm
57
58 public static int getFormatCode(AudioFormat format) {
59 AudioFormat.Encoding encoding = format.getEncoding();
60 int nSampleSize = format.getSampleSizeInBits();
61 boolean bigEndian = format.isBigEndian();
62 // $$fb 2000-08-16: check the frame size, too.
63 boolean frameSizeOK=format.getFrameSize()==AudioSystem.NOT_SPECIFIED
64 || format.getChannels()!=AudioSystem.NOT_SPECIFIED
65 || format.getFrameSize()==nSampleSize/8*format.getChannels();
66
67 if ((encoding.equals(AudioFormat.Encoding.PCM_SIGNED))
68 && ((bigEndian && nSampleSize>=16 && nSampleSize<=32) || (nSampleSize==8))
69 && frameSizeOK) {
70 return AIFF_COMM_PCM;
71 } else if (encoding.equals(AudioFormat.Encoding.ULAW) && nSampleSize == 8 && frameSizeOK) {
72 return AIFF_COMM_ULAW;
73 } else if (encoding.equals(new AudioFormat.Encoding("IMA_ADPCM")) && nSampleSize == 4) {
74 return AIFF_COMM_IMA_ADPCM;
75 } else {
76 return AIFF_COMM_UNSPECIFIED;
77 }
78 }
79
80}
81
82/*** AiffTool.java ***/
diff --git a/songdbj/org/tritonus/file/AuAudioFileReader.java b/songdbj/org/tritonus/file/AuAudioFileReader.java
new file mode 100644
index 0000000000..b527920118
--- /dev/null
+++ b/songdbj/org/tritonus/file/AuAudioFileReader.java
@@ -0,0 +1,185 @@
1/*
2 * AuAudioFileReader.java
3 *
4 * This file is part of Tritonus: http://www.tritonus.org/
5 */
6
7/*
8 * Copyright (c) 1999,2000,2001 by Florian Bomers <http://www.bomers.de>
9 * Copyright (c) 1999 by Matthias Pfisterer
10 *
11 *
12 * This program is free software; you can redistribute it and/or modify
13 * it under the terms of the GNU Library General Public License as published
14 * by the Free Software Foundation; either version 2 of the License, or
15 * (at your option) any later version.
16 *
17 * This program is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 * GNU Library General Public License for more details.
21 *
22 * You should have received a copy of the GNU Library General Public
23 * License along with this program; if not, write to the Free Software
24 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
25 *
26 */
27
28/*
29|<--- this code is formatted to fit into 80 columns --->|
30*/
31
32package org.tritonus.sampled.file;
33
34import java.io.DataInputStream;
35import java.io.File;
36import java.io.InputStream;
37import java.io.IOException;
38
39import javax.sound.sampled.AudioFormat;
40import javax.sound.sampled.AudioFileFormat;
41import javax.sound.sampled.AudioInputStream;
42import javax.sound.sampled.AudioSystem;
43import javax.sound.sampled.UnsupportedAudioFileException;
44import javax.sound.sampled.spi.AudioFileReader;
45
46import org.tritonus.share.TDebug;
47import org.tritonus.share.sampled.file.TAudioFileFormat;
48import org.tritonus.share.sampled.file.TAudioFileReader;
49
50
51/** Class for reading Sun/Next AU files.
52 *
53 * @author Florian Bomers
54 * @author Matthias Pfisterer
55 */
56public class AuAudioFileReader extends TAudioFileReader
57{
58 private static final int READ_LIMIT = 1000;
59
60
61
62 public AuAudioFileReader()
63 {
64 super(READ_LIMIT);
65 }
66
67
68
69 private static String readDescription(DataInputStream dis, int len) throws IOException {
70 byte c=-1;
71 String ret="";
72 while (len>0 && (c=dis.readByte())!=0) {
73 ret=ret+(char) c;
74 len--;
75 }
76 if (len>1 && c==0) {
77 dis.skip(len-1);
78 }
79 return ret;
80 }
81
82
83
84 protected AudioFileFormat getAudioFileFormat(InputStream inputStream, long lFileSizeInBytes)
85 throws UnsupportedAudioFileException, IOException
86 {
87 if (TDebug.TraceAudioFileReader) {TDebug.out("AuAudioFileReader.getAudioFileFormat(InputStream, long): begin"); }
88 DataInputStream dataInputStream = new DataInputStream(inputStream);
89 int nMagic = dataInputStream.readInt();
90 if (nMagic != AuTool.AU_HEADER_MAGIC) {
91 throw new UnsupportedAudioFileException(
92 "not an AU file: wrong header magic");
93 }
94 int nDataOffset = dataInputStream.readInt();
95 if (TDebug.TraceAudioFileReader) {
96 TDebug.out("AuAudioFileReader.getAudioFileFormat(): data offset: " + nDataOffset);
97 }
98 if (nDataOffset < AuTool.DATA_OFFSET) {
99 throw new UnsupportedAudioFileException(
100 "not an AU file: data offset must be 24 or greater");
101 }
102 int nDataLength = dataInputStream.readInt();
103 if (TDebug.TraceAudioFileReader) {
104 TDebug.out("AuAudioFileReader.getAudioFileFormat(): data length: " + nDataLength);
105 }
106 if (nDataLength < 0 && nDataLength!=AuTool.AUDIO_UNKNOWN_SIZE) {
107 throw new UnsupportedAudioFileException(
108 "not an AU file: data length must be positive, 0 or -1 for unknown");
109 }
110 AudioFormat.Encoding encoding = null;
111 int nSampleSize = 0;
112 int nEncoding = dataInputStream.readInt();
113 switch (nEncoding) {
114 case AuTool.SND_FORMAT_MULAW_8: // 8-bit uLaw G.711
115 encoding = AudioFormat.Encoding.ULAW;
116 nSampleSize = 8;
117 break;
118
119 case AuTool.SND_FORMAT_LINEAR_8:
120 encoding = AudioFormat.Encoding.PCM_SIGNED;
121 nSampleSize = 8;
122 break;
123
124 case AuTool.SND_FORMAT_LINEAR_16:
125 encoding = AudioFormat.Encoding.PCM_SIGNED;
126 nSampleSize = 16;
127 break;
128
129 case AuTool.SND_FORMAT_LINEAR_24:
130 encoding = AudioFormat.Encoding.PCM_SIGNED;
131 nSampleSize = 24;
132 break;
133
134 case AuTool.SND_FORMAT_LINEAR_32:
135 encoding = AudioFormat.Encoding.PCM_SIGNED;
136 nSampleSize = 32;
137 break;
138
139 case AuTool.SND_FORMAT_ALAW_8: // 8-bit aLaw G.711
140 encoding = AudioFormat.Encoding.ALAW;
141 nSampleSize = 8;
142 break;
143 }
144 if (nSampleSize == 0) {
145 throw new UnsupportedAudioFileException(
146 "unsupported AU file: unknown encoding " + nEncoding);
147 }
148 int nSampleRate = dataInputStream.readInt();
149 if (nSampleRate <= 0) {
150 throw new UnsupportedAudioFileException(
151 "corrupt AU file: sample rate must be positive");
152 }
153 int nNumChannels = dataInputStream.readInt();
154 if (nNumChannels <= 0) {
155 throw new UnsupportedAudioFileException(
156 "corrupt AU file: number of channels must be positive");
157 }
158 // skip header information field
159 inputStream.skip(nDataOffset - AuTool.DATA_OFFSET);
160 // read header info field
161 //String desc=readDescription(dataInputStream, nDataOffset - AuTool.DATA_OFFSET);
162
163 AudioFormat format = new AudioFormat(encoding,
164 (float) nSampleRate,
165 nSampleSize,
166 nNumChannels,
167 calculateFrameSize(nSampleSize, nNumChannels),
168 (float) nSampleRate,
169 true);
170 AudioFileFormat audioFileFormat = new TAudioFileFormat(
171 AudioFileFormat.Type.AU,
172 format,
173 (nDataLength==AuTool.AUDIO_UNKNOWN_SIZE)?
174 AudioSystem.NOT_SPECIFIED:(nDataLength / format.getFrameSize()),
175 (nDataLength==AuTool.AUDIO_UNKNOWN_SIZE)?
176 AudioSystem.NOT_SPECIFIED:(nDataLength + nDataOffset));
177 if (TDebug.TraceAudioFileReader) { TDebug.out("AuAudioFileReader.getAudioFileFormat(InputStream, long): begin"); }
178 return audioFileFormat;
179 }
180}
181
182
183
184/*** AuAudioFileReader.java ***/
185
diff --git a/songdbj/org/tritonus/file/AuAudioFileWriter.java b/songdbj/org/tritonus/file/AuAudioFileWriter.java
new file mode 100644
index 0000000000..5ac80a8c8a
--- /dev/null
+++ b/songdbj/org/tritonus/file/AuAudioFileWriter.java
@@ -0,0 +1,104 @@
1/*
2 * AuAudioFileWriter.java
3 *
4 * This file is part of Tritonus: http://www.tritonus.org/
5 */
6
7/*
8 * Copyright (c) 1999,2000,2001 by Florian Bomers <http://www.bomers.de>
9 * Copyright (c) 1999 by Matthias Pfisterer
10 *
11 *
12 * This program is free software; you can redistribute it and/or modify
13 * it under the terms of the GNU Library General Public License as published
14 * by the Free Software Foundation; either version 2 of the License, or
15 * (at your option) any later version.
16 *
17 * This program is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 * GNU Library General Public License for more details.
21 *
22 * You should have received a copy of the GNU Library General Public
23 * License along with this program; if not, write to the Free Software
24 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
25 *
26 */
27
28/*
29|<--- this code is formatted to fit into 80 columns --->|
30*/
31
32package org.tritonus.sampled.file;
33
34import java.io.IOException;
35import java.util.Arrays;
36
37import javax.sound.sampled.AudioFileFormat;
38import javax.sound.sampled.AudioFormat;
39import javax.sound.sampled.AudioInputStream;
40import javax.sound.sampled.AudioSystem;
41
42import org.tritonus.share.TDebug;
43import org.tritonus.share.sampled.file.AudioOutputStream;
44import org.tritonus.share.sampled.file.TAudioFileWriter;
45import org.tritonus.share.sampled.file.TDataOutputStream;
46
47
48/**
49 * AudioFileWriter for Sun/Next AU files.
50 *
51 * @author Florian Bomers
52 * @author Matthias Pfisterer
53 */
54public class AuAudioFileWriter extends TAudioFileWriter {
55
56 private static final AudioFileFormat.Type[] FILE_TYPES =
57 {
58 AudioFileFormat.Type.AU
59 };
60
61 private static final int ALL=AudioSystem.NOT_SPECIFIED;
62
63 // IMPORTANT: this array depends on the AudioFormat.match() algorithm which takes
64 // AudioSystem.NOT_SPECIFIED into account !
65 private static final AudioFormat[] AUDIO_FORMATS =
66 {
67 new AudioFormat(AudioFormat.Encoding.PCM_SIGNED, ALL, 8, ALL, ALL, ALL, true),
68 new AudioFormat(AudioFormat.Encoding.PCM_SIGNED, ALL, 8, ALL, ALL, ALL, false),
69
70 new AudioFormat(AudioFormat.Encoding.ULAW, ALL, 8, ALL, ALL, ALL, false),
71 new AudioFormat(AudioFormat.Encoding.ULAW, ALL, 8, ALL, ALL, ALL, true),
72
73 new AudioFormat(AudioFormat.Encoding.ALAW, ALL, 8, ALL, ALL, ALL, false),
74 new AudioFormat(AudioFormat.Encoding.ALAW, ALL, 8, ALL, ALL, ALL, true),
75
76 new AudioFormat(AudioFormat.Encoding.PCM_SIGNED, ALL, 16, ALL, ALL, ALL, true),
77
78 new AudioFormat(AudioFormat.Encoding.PCM_SIGNED, ALL, 24, ALL, ALL, ALL, true),
79
80 new AudioFormat(AudioFormat.Encoding.PCM_SIGNED, ALL, 32, ALL, ALL, ALL, true),
81 };
82
83 public AuAudioFileWriter() {
84 super(Arrays.asList(FILE_TYPES),
85 Arrays.asList(AUDIO_FORMATS));
86 }
87
88
89 protected boolean isAudioFormatSupportedImpl(AudioFormat format,
90 AudioFileFormat.Type fileType) {
91 return AuTool.getFormatCode(format)!=AuTool.SND_FORMAT_UNSPECIFIED;
92 }
93 protected AudioOutputStream getAudioOutputStream(AudioFormat audioFormat,
94 long lLengthInBytes,
95 AudioFileFormat.Type fileType,
96 TDataOutputStream dataOutputStream) throws IOException {
97 return new AuAudioOutputStream(audioFormat,
98 lLengthInBytes,
99 dataOutputStream);
100 }
101
102}
103
104/*** AuAudioFileWriter.java ***/
diff --git a/songdbj/org/tritonus/file/AuAudioOutputStream.java b/songdbj/org/tritonus/file/AuAudioOutputStream.java
new file mode 100644
index 0000000000..9f33e7a2aa
--- /dev/null
+++ b/songdbj/org/tritonus/file/AuAudioOutputStream.java
@@ -0,0 +1,121 @@
1/*
2 * AuAudioOutputStream.java
3 *
4 * This file is part of Tritonus: http://www.tritonus.org/
5 */
6
7/*
8 * Copyright (c) 2000,2001 by Florian Bomers <http://www.bomers.de>
9 * Copyright (c) 1999 by Matthias Pfisterer
10 *
11 *
12 * This program is free software; you can redistribute it and/or modify
13 * it under the terms of the GNU Library General Public License as published
14 * by the Free Software Foundation; either version 2 of the License, or
15 * (at your option) any later version.
16 *
17 * This program is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 * GNU Library General Public License for more details.
21 *
22 * You should have received a copy of the GNU Library General Public
23 * License along with this program; if not, write to the Free Software
24 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
25 *
26 */
27
28/*
29|<--- this code is formatted to fit into 80 columns --->|
30*/
31
32package org.tritonus.sampled.file;
33
34import java.io.IOException;
35import javax.sound.sampled.AudioFormat;
36import javax.sound.sampled.AudioFileFormat;
37import javax.sound.sampled.AudioSystem;
38import org.tritonus.share.TDebug;
39import org.tritonus.share.sampled.file.TAudioOutputStream;
40import org.tritonus.share.sampled.file.TDataOutputStream;
41
42
43
44/**
45 * AudioOutputStream for AU files.
46 *
47 * @author Florian Bomers
48 * @author Matthias Pfisterer
49 */
50
51public class AuAudioOutputStream extends TAudioOutputStream {
52
53 private static String description="Created by Tritonus";
54
55 /**
56 * Writes a null-terminated ascii string s to f.
57 * The total number of bytes written is aligned on a 2byte boundary.
58 * @exception IOException Write error.
59 */
60 protected static void writeText(TDataOutputStream dos, String s) throws IOException {
61 if (s.length()>0) {
62 dos.writeBytes(s);
63 dos.writeByte(0); // pour terminer le texte
64 if ((s.length() % 2)==0) {
65 // ajout d'un zero pour faire la longeur pair
66 dos.writeByte(0);
67 }
68 }
69 }
70
71 /**
72 * Returns number of bytes that have to written for string s (with alignment)
73 */
74 protected static int getTextLength(String s) {
75 if (s.length()==0) {
76 return 0;
77 } else {
78 return (s.length()+2) & 0xFFFFFFFE;
79 }
80 }
81
82 public AuAudioOutputStream(AudioFormat audioFormat,
83 long lLength,
84 TDataOutputStream dataOutputStream) {
85 // if length exceeds 2GB, set the length field to NOT_SPECIFIED
86 super(audioFormat,
87 lLength>0x7FFFFFFFl?AudioSystem.NOT_SPECIFIED:lLength,
88 dataOutputStream,
89 lLength == AudioSystem.NOT_SPECIFIED && dataOutputStream.supportsSeek());
90 }
91
92 protected void writeHeader() throws IOException {
93 if (TDebug.TraceAudioOutputStream) {
94 TDebug.out("AuAudioOutputStream.writeHeader(): called.");
95 }
96 AudioFormat format = getFormat();
97 long lLength = getLength();
98 TDataOutputStream dos = getDataOutputStream();
99 if (TDebug.TraceAudioOutputStream) {
100 TDebug.out("AuAudioOutputStream.writeHeader(): AudioFormat: " + format);
101 TDebug.out("AuAudioOutputStream.writeHeader(): length: " + lLength);
102 }
103
104 dos.writeInt(AuTool.AU_HEADER_MAGIC);
105 dos.writeInt(AuTool.DATA_OFFSET+getTextLength(description));
106 dos.writeInt((lLength!=AudioSystem.NOT_SPECIFIED)?((int) lLength):AuTool.AUDIO_UNKNOWN_SIZE);
107 dos.writeInt(AuTool.getFormatCode(format));
108 dos.writeInt((int) format.getSampleRate());
109 dos.writeInt(format.getChannels());
110 writeText(dos, description);
111 }
112
113 protected void patchHeader() throws IOException {
114 TDataOutputStream tdos = getDataOutputStream();
115 tdos.seek(0);
116 setLengthFromCalculatedLength();
117 writeHeader();
118 }
119}
120
121/*** AuAudioOutputStream.java ***/
diff --git a/songdbj/org/tritonus/file/AuTool.java b/songdbj/org/tritonus/file/AuTool.java
new file mode 100644
index 0000000000..bcdc62f86c
--- /dev/null
+++ b/songdbj/org/tritonus/file/AuTool.java
@@ -0,0 +1,95 @@
1/*
2 * AuTool.java
3 *
4 * This file is part of Tritonus: http://www.tritonus.org/
5 */
6
7/*
8 * Copyright (c) 2000,2001 by Florian Bomers <http://www.bomers.de>
9 *
10 *
11 * This program is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU Library General Public License as published
13 * by the Free Software Foundation; either version 2 of the License, or
14 * (at your option) any later version.
15 *
16 * This program is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU Library General Public License for more details.
20 *
21 * You should have received a copy of the GNU Library General Public
22 * License along with this program; if not, write to the Free Software
23 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
24 *
25 */
26
27/*
28|<--- this code is formatted to fit into 80 columns --->|
29*/
30
31package org.tritonus.sampled.file;
32
33import javax.sound.sampled.AudioFormat;
34import javax.sound.sampled.AudioFileFormat;
35import javax.sound.sampled.AudioSystem;
36
37
38/** Common constants and methods for handling au files.
39 *
40 * @author Florian Bomers
41 */
42
43public class AuTool {
44
45 public static final int AU_HEADER_MAGIC = 0x2e736e64;
46 public static final int AUDIO_UNKNOWN_SIZE = -1;
47
48 // length of header in bytes
49 public static final int DATA_OFFSET = 24;
50
51 public static final int SND_FORMAT_UNSPECIFIED = 0;
52 public static final int SND_FORMAT_MULAW_8 = 1;
53 public static final int SND_FORMAT_LINEAR_8 = 2;
54 public static final int SND_FORMAT_LINEAR_16 = 3;
55 public static final int SND_FORMAT_LINEAR_24 = 4;
56 public static final int SND_FORMAT_LINEAR_32 = 5;
57 public static final int SND_FORMAT_FLOAT = 6;
58 public static final int SND_FORMAT_DOUBLE = 7;
59 public static final int SND_FORMAT_ADPCM_G721 = 23;
60 public static final int SND_FORMAT_ADPCM_G722 = 24;
61 public static final int SND_FORMAT_ADPCM_G723_3 = 25;
62 public static final int SND_FORMAT_ADPCM_G723_5 = 26;
63 public static final int SND_FORMAT_ALAW_8 = 27;
64
65 public static int getFormatCode(AudioFormat format) {
66 AudioFormat.Encoding encoding = format.getEncoding();
67 int nSampleSize = format.getSampleSizeInBits();
68 // must be big endian for >8 bit formats
69 boolean bigEndian = format.isBigEndian();
70 // $$fb 2000-08-16: check the frame size, too.
71 boolean frameSizeOK=(
72 format.getFrameSize()==AudioSystem.NOT_SPECIFIED
73 || format.getChannels()!=AudioSystem.NOT_SPECIFIED
74 || format.getFrameSize()==nSampleSize/8*format.getChannels());
75
76 if (encoding.equals(AudioFormat.Encoding.ULAW) && nSampleSize == 8 && frameSizeOK) {
77 return SND_FORMAT_MULAW_8;
78 } else if (encoding.equals(AudioFormat.Encoding.PCM_SIGNED) && frameSizeOK) {
79 if (nSampleSize == 8) {
80 return SND_FORMAT_LINEAR_8;
81 } else if (nSampleSize == 16 && bigEndian) {
82 return SND_FORMAT_LINEAR_16;
83 } else if (nSampleSize == 24 && bigEndian) {
84 return SND_FORMAT_LINEAR_24;
85 } else if (nSampleSize == 32 && bigEndian) {
86 return SND_FORMAT_LINEAR_32;
87 }
88 } else if (encoding.equals(AudioFormat.Encoding.ALAW) && nSampleSize == 8 && frameSizeOK) {
89 return SND_FORMAT_ALAW_8;
90 }
91 return SND_FORMAT_UNSPECIFIED;
92 }
93}
94
95/*** AuTool.java ***/
diff --git a/songdbj/org/tritonus/file/WaveAudioFileReader.java b/songdbj/org/tritonus/file/WaveAudioFileReader.java
new file mode 100644
index 0000000000..62d3f1a9ea
--- /dev/null
+++ b/songdbj/org/tritonus/file/WaveAudioFileReader.java
@@ -0,0 +1,300 @@
1/*
2 * WaveAudioFileReader.java
3 *
4 * This file is part of Tritonus: http://www.tritonus.org/
5 */
6
7/*
8 * Copyright (c) 1999,2000 by Florian Bomers <http://www.bomers.de>
9 * Copyright (c) 1999 by Matthias Pfisterer
10 *
11 *
12 * This program is free software; you can redistribute it and/or modify
13 * it under the terms of the GNU Library General Public License as published
14 * by the Free Software Foundation; either version 2 of the License, or
15 * (at your option) any later version.
16 *
17 * This program is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 * GNU Library General Public License for more details.
21 *
22 * You should have received a copy of the GNU Library General Public
23 * License along with this program; if not, write to the Free Software
24 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
25 *
26 */
27
28/*
29|<--- this code is formatted to fit into 80 columns --->|
30*/
31
32package org.tritonus.sampled.file;
33
34import java.io.DataInputStream;
35import java.io.File;
36import java.io.InputStream;
37import java.io.IOException;
38
39import javax.sound.sampled.AudioSystem;
40import javax.sound.sampled.AudioFormat;
41import javax.sound.sampled.AudioFileFormat;
42import javax.sound.sampled.AudioInputStream;
43import javax.sound.sampled.UnsupportedAudioFileException;
44import javax.sound.sampled.spi.AudioFileReader;
45
46import org.tritonus.share.TDebug;
47import org.tritonus.share.sampled.file.TAudioFileFormat;
48import org.tritonus.share.sampled.file.TAudioFileReader;
49
50
51/**
52 * Class for reading wave files.
53 *
54 * @author Florian Bomers
55 * @author Matthias Pfisterer
56 */
57
58public class WaveAudioFileReader extends TAudioFileReader
59{
60 private static final int READ_LIMIT = 1000;
61
62
63
64 public WaveAudioFileReader()
65 {
66 super(READ_LIMIT);
67 }
68
69
70
71 protected void advanceChunk(DataInputStream dis, long prevLength, long prevRead)
72 throws IOException {
73 if (prevLength>0) {
74 dis.skip(((prevLength+1) & 0xFFFFFFFE)-prevRead);
75 }
76 }
77
78
79 protected long findChunk(DataInputStream dis, int key)
80 throws UnsupportedAudioFileException, IOException {
81 // $$fb 1999-12-18: we should take care that we don't exceed
82 // the mark of this stream. When we exceeded the mark and
83 // we notice that we don't support this wave file,
84 // other potential wave file readers have no chance.
85 int thisKey;
86 long chunkLength=0;
87 do {
88 advanceChunk(dis, chunkLength, 0);
89 try {
90 thisKey = dis.readInt();
91 } catch (IOException e)
92 {
93 if (TDebug.TraceAllExceptions)
94 {
95 TDebug.out(e);
96 }
97 // $$fb: when we come here, we skipped past the end of the wave file
98 // without finding the chunk.
99 // IMHO, this is not an IOException, as there are incarnations
100 // of WAVE files which store data in different chunks.
101 // maybe we can find a nice description of the "required chunk" ?
102 throw new UnsupportedAudioFileException(
103 "unsupported WAVE file: required chunk not found.");
104 }
105 chunkLength = readLittleEndianInt(dis) & 0xFFFFFFFF; // unsigned
106 }
107 while (thisKey != key);
108 return chunkLength;
109 }
110
111 protected AudioFormat readFormatChunk(DataInputStream dis,
112 long chunkLength) throws UnsupportedAudioFileException, IOException {
113 String debugAdd="";
114
115 int read=WaveTool.MIN_FMT_CHUNK_LENGTH;
116
117 if (chunkLength<WaveTool.MIN_FMT_CHUNK_LENGTH) {
118 throw new UnsupportedAudioFileException(
119 "corrupt WAVE file: format chunk is too small");
120 }
121
122 short formatCode=readLittleEndianShort(dis);
123 short channelCount = readLittleEndianShort(dis);
124 if (channelCount <= 0) {
125 throw new UnsupportedAudioFileException(
126 "corrupt WAVE file: number of channels must be positive");
127 }
128
129 int sampleRate = readLittleEndianInt(dis);
130 if (sampleRate <= 0) {
131 throw new UnsupportedAudioFileException(
132 "corrupt WAVE file: sample rate must be positive");
133 }
134
135 int avgBytesPerSecond=readLittleEndianInt(dis);
136 int blockAlign=readLittleEndianShort(dis);
137
138 AudioFormat.Encoding encoding;
139 int sampleSizeInBits;
140 int frameSize=0;
141 float frameRate=(float) sampleRate;
142
143 int cbSize = 0;
144 switch (formatCode) {
145 case WaveTool.WAVE_FORMAT_PCM:
146 if (chunkLength<WaveTool.MIN_FMT_CHUNK_LENGTH+2) {
147 throw new UnsupportedAudioFileException(
148 "corrupt WAVE file: format chunk is too small");
149 }
150 sampleSizeInBits = readLittleEndianShort(dis);
151 if (sampleSizeInBits <= 0) {
152 throw new UnsupportedAudioFileException(
153 "corrupt WAVE file: sample size must be positive");
154 }
155 encoding = (sampleSizeInBits <= 8) ?
156 AudioFormat.Encoding.PCM_UNSIGNED : AudioFormat.Encoding.PCM_SIGNED;
157 if (TDebug.TraceAudioFileReader) {
158 debugAdd+=", wBitsPerSample="+sampleSizeInBits;
159 }
160 read+=2;
161 break;
162 case WaveTool.WAVE_FORMAT_ALAW:
163 sampleSizeInBits = 8;
164 encoding = AudioFormat.Encoding.ALAW;
165 break;
166 case WaveTool.WAVE_FORMAT_ULAW:
167 sampleSizeInBits = 8;
168 encoding = AudioFormat.Encoding.ULAW;
169 break;
170 case WaveTool.WAVE_FORMAT_GSM610:
171 if (chunkLength<WaveTool.MIN_FMT_CHUNK_LENGTH+6) {
172 throw new UnsupportedAudioFileException(
173 "corrupt WAVE file: extra GSM bytes are missing");
174 }
175 sampleSizeInBits = readLittleEndianShort(dis); // sample Size (is 0 for GSM)
176 cbSize=readLittleEndianShort(dis);
177 if (cbSize < 2) {
178 throw new UnsupportedAudioFileException(
179 "corrupt WAVE file: extra GSM bytes are corrupt");
180 }
181 int decodedSamplesPerBlock=readLittleEndianShort(dis) & 0xFFFF; // unsigned
182 if (TDebug.TraceAudioFileReader) {
183 debugAdd+=", wBitsPerSample="+sampleSizeInBits
184 +", cbSize="+cbSize
185 +", wSamplesPerBlock="+decodedSamplesPerBlock;
186 }
187 sampleSizeInBits = AudioSystem.NOT_SPECIFIED;
188 encoding = WaveTool.GSM0610;
189 frameSize=blockAlign;
190 frameRate=((float) sampleRate)/((float) decodedSamplesPerBlock);
191 read+=6;
192 break;
193
194 case WaveTool.WAVE_FORMAT_IMA_ADPCM:
195 if (chunkLength < WaveTool.MIN_FMT_CHUNK_LENGTH + 2)
196 {
197 throw new UnsupportedAudioFileException(
198 "corrupt WAVE file: extra GSM bytes are missing");
199 }
200 sampleSizeInBits = readLittleEndianShort(dis);
201 cbSize = readLittleEndianShort(dis);
202 if (cbSize < 2)
203 {
204 throw new UnsupportedAudioFileException(
205 "corrupt WAVE file: extra IMA ADPCM bytes are corrupt");
206 }
207 int samplesPerBlock = readLittleEndianShort(dis) & 0xFFFF; // unsigned
208 if (TDebug.TraceAudioFileReader) {
209 debugAdd+=", wBitsPerSample="+sampleSizeInBits
210 +", cbSize="+cbSize
211 +", wSamplesPerBlock=" + samplesPerBlock;
212 }
213 sampleSizeInBits = AudioSystem.NOT_SPECIFIED;
214 encoding = WaveTool.GSM0610;
215 frameSize = blockAlign;
216 frameRate = ((float) sampleRate)/((float) samplesPerBlock);
217 read += 6;
218 break;
219
220 default:
221 throw new UnsupportedAudioFileException(
222 "unsupported WAVE file: unknown format code "+formatCode);
223 }
224 // if frameSize isn't set, calculate it (the default)
225 if (frameSize==0) {
226 frameSize = calculateFrameSize(sampleSizeInBits, channelCount);
227 }
228
229 if (TDebug.TraceAudioFileReader) {
230 TDebug.out("WaveAudioFileReader.readFormatChunk():");
231 TDebug.out(" read values: wFormatTag="+formatCode
232 +", nChannels="+channelCount
233 +", nSamplesPerSec="+sampleRate
234 +", nAvgBytesPerSec="+avgBytesPerSecond
235 +", nBlockAlign=="+blockAlign
236 +debugAdd);
237 TDebug.out(" constructed values: "
238 +"encoding="+encoding
239 +", sampleRate="+((float) sampleRate)
240 +", sampleSizeInBits="+sampleSizeInBits
241 +", channels="+channelCount
242 +", frameSize="+frameSize
243 +", frameRate="+frameRate);
244 }
245
246 // go to next chunk
247 advanceChunk(dis, chunkLength, read);
248 return new AudioFormat(
249 encoding,
250 (float) sampleRate,
251 sampleSizeInBits,
252 channelCount,
253 frameSize,
254 frameRate,
255 false);
256 }
257
258
259
260 protected AudioFileFormat getAudioFileFormat(InputStream inputStream, long lFileLengthInBytes)
261 throws UnsupportedAudioFileException, IOException {
262 DataInputStream dataInputStream = new DataInputStream(inputStream);
263 int magic = dataInputStream.readInt();
264 if (magic != WaveTool.WAVE_RIFF_MAGIC) {
265 throw new UnsupportedAudioFileException(
266 "not a WAVE file: wrong header magic");
267 }
268 long totalLength = readLittleEndianInt(dataInputStream) & 0xFFFFFFFF; // unsigned
269 magic = dataInputStream.readInt();
270 if (magic != WaveTool.WAVE_WAVE_MAGIC) {
271 throw new UnsupportedAudioFileException("not a WAVE file: wrong header magic");
272 }
273 // search for "fmt " chunk
274 long chunkLength = findChunk(dataInputStream, WaveTool.WAVE_FMT_MAGIC);
275 AudioFormat format = readFormatChunk(dataInputStream, chunkLength);
276
277 // search for "data" chunk
278 long dataChunkLength = findChunk(dataInputStream, WaveTool.WAVE_DATA_MAGIC);
279
280 long frameLength = dataChunkLength / format.getFrameSize();
281 if (format.getEncoding().equals(WaveTool.GSM0610)) {
282 // TODO: should not be necessary
283 frameLength = dataChunkLength;
284 }
285
286 if (TDebug.TraceAudioFileReader) {
287 TDebug.out("WaveAudioFileReader.getAudioFileFormat(): total length: "
288 +totalLength+", frame length = "+frameLength);
289 }
290 return new TAudioFileFormat(AudioFileFormat.Type.WAVE,
291 format,
292 (int) frameLength,
293 (int) (totalLength + WaveTool.CHUNK_HEADER_SIZE));
294 }
295}
296
297
298
299/*** WaveAudioFileReader.java ***/
300
diff --git a/songdbj/org/tritonus/file/WaveAudioFileWriter.java b/songdbj/org/tritonus/file/WaveAudioFileWriter.java
new file mode 100644
index 0000000000..d501b5b2b1
--- /dev/null
+++ b/songdbj/org/tritonus/file/WaveAudioFileWriter.java
@@ -0,0 +1,103 @@
1/*
2 * WaveAudioFileWriter.java
3 *
4 * This file is part of Tritonus: http://www.tritonus.org/
5 */
6
7/*
8 * Copyright (c) 1999,2000 by Florian Bomers <http://www.bomers.de>
9 *
10 *
11 * This program is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU Library General Public License as published
13 * by the Free Software Foundation; either version 2 of the License, or
14 * (at your option) any later version.
15 *
16 * This program is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU Library General Public License for more details.
20 *
21 * You should have received a copy of the GNU Library General Public
22 * License along with this program; if not, write to the Free Software
23 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
24 *
25 */
26
27/*
28|<--- this code is formatted to fit into 80 columns --->|
29*/
30
31package org.tritonus.sampled.file;
32
33import java.io.IOException;
34import java.util.Arrays;
35
36import javax.sound.sampled.AudioFileFormat;
37import javax.sound.sampled.AudioFormat;
38import javax.sound.sampled.AudioInputStream;
39import javax.sound.sampled.AudioSystem;
40
41import org.tritonus.share.TDebug;
42import org.tritonus.share.sampled.file.AudioOutputStream;
43import org.tritonus.share.sampled.file.TAudioFileWriter;
44import org.tritonus.share.sampled.file.TDataOutputStream;
45
46
47/**
48 * Class for writing Microsoft(tm) WAVE files
49 *
50 * @author Florian Bomers
51 */
52public class WaveAudioFileWriter
53extends TAudioFileWriter {
54
55 private static final AudioFileFormat.Type[] FILE_TYPES =
56 {
57 AudioFileFormat.Type.WAVE
58 };
59
60 private static final int ALL=AudioSystem.NOT_SPECIFIED;
61
62 // IMPORTANT: this array depends on the AudioFormat.match() algorithm which takes
63 // AudioSystem.NOT_SPECIFIED into account !
64 private static final AudioFormat[] AUDIO_FORMATS =
65 {
66 // Encoding, SampleRate, sampleSizeInBits, channels, frameSize, frameRate, bigEndian
67 new AudioFormat(AudioFormat.Encoding.PCM_UNSIGNED, ALL, 8, ALL, ALL, ALL, true),
68 new AudioFormat(AudioFormat.Encoding.PCM_UNSIGNED, ALL, 8, ALL, ALL, ALL, false),
69 new AudioFormat(AudioFormat.Encoding.ULAW, ALL, 8, ALL, ALL, ALL, false),
70 new AudioFormat(AudioFormat.Encoding.ULAW, ALL, 8, ALL, ALL, ALL, true),
71 new AudioFormat(AudioFormat.Encoding.ALAW, ALL, 8, ALL, ALL, ALL, false),
72 new AudioFormat(AudioFormat.Encoding.ALAW, ALL, 8, ALL, ALL, ALL, true),
73 new AudioFormat(AudioFormat.Encoding.PCM_SIGNED, ALL, 16, ALL, ALL, ALL, false),
74 new AudioFormat(AudioFormat.Encoding.PCM_SIGNED, ALL, 24, ALL, ALL, ALL, false),
75 new AudioFormat(AudioFormat.Encoding.PCM_SIGNED, ALL, 32, ALL, ALL, ALL, false),
76 new AudioFormat(WaveTool.GSM0610, ALL, ALL, ALL, ALL, ALL, false),
77 new AudioFormat(WaveTool.GSM0610, ALL, ALL, ALL, ALL, ALL, true),
78 };
79
80 public WaveAudioFileWriter() {
81 super(Arrays.asList(FILE_TYPES),
82 Arrays.asList(AUDIO_FORMATS));
83 }
84
85 // overwritten for quicker and more accurate check
86 protected boolean isAudioFormatSupportedImpl(AudioFormat format,
87 AudioFileFormat.Type fileType) {
88 return WaveTool.getFormatCode(format) != WaveTool.WAVE_FORMAT_UNSPECIFIED;
89 }
90
91
92 protected AudioOutputStream getAudioOutputStream(AudioFormat audioFormat,
93 long lLengthInBytes,
94 AudioFileFormat.Type fileType,
95 TDataOutputStream dataOutputStream) throws IOException {
96 return new WaveAudioOutputStream(audioFormat,
97 lLengthInBytes,
98 dataOutputStream);
99 }
100
101}
102
103/*** WaveAudioFileWriter.java ***/
diff --git a/songdbj/org/tritonus/file/WaveAudioOutputStream.java b/songdbj/org/tritonus/file/WaveAudioOutputStream.java
new file mode 100644
index 0000000000..9054c3c4e1
--- /dev/null
+++ b/songdbj/org/tritonus/file/WaveAudioOutputStream.java
@@ -0,0 +1,201 @@
1/*
2 * WaveAudioOutputStream.java
3 *
4 * This file is part of Tritonus: http://www.tritonus.org/
5 */
6
7/*
8 * Copyright (c) 2000 by Florian Bomers <http://www.bomers.de>
9 *
10 *
11 * This program is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU Library General Public License as published
13 * by the Free Software Foundation; either version 2 of the License, or
14 * (at your option) any later version.
15 *
16 * This program is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU Library General Public License for more details.
20 *
21 * You should have received a copy of the GNU Library General Public
22 * License along with this program; if not, write to the Free Software
23 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
24 *
25 */
26
27/*
28|<--- this code is formatted to fit into 80 columns --->|
29*/
30
31package org.tritonus.sampled.file;
32
33import java.io.IOException;
34
35import javax.sound.sampled.AudioFormat;
36import javax.sound.sampled.AudioFileFormat;
37import javax.sound.sampled.AudioSystem;
38
39import org.tritonus.share.TDebug;
40import org.tritonus.share.sampled.file.TAudioOutputStream;
41import org.tritonus.share.sampled.file.TDataOutputStream;
42
43
44/**
45 * AudioOutputStream for Wave files.
46 *
47 * @author Florian Bomers
48 */
49
50public class WaveAudioOutputStream extends TAudioOutputStream {
51
52 // this constant is used for chunk lengths when the length is not known yet
53 private static final int LENGTH_NOT_KNOWN=-1;
54 private int formatCode;
55
56 public WaveAudioOutputStream(AudioFormat audioFormat,
57 long lLength,
58 TDataOutputStream dataOutputStream) {
59 super(audioFormat,
60 lLength,
61 dataOutputStream,
62 lLength == AudioSystem.NOT_SPECIFIED && dataOutputStream.supportsSeek());
63 // wave cannot store more than 4GB
64 if (lLength != AudioSystem.NOT_SPECIFIED
65 && (lLength+WaveTool.DATA_OFFSET)>0xFFFFFFFFl) {
66 if (TDebug.TraceAudioOutputStream) {
67 TDebug.out("WaveAudioOutputStream: Length exceeds 4GB: "
68 +lLength+"=0x"+Long.toHexString(lLength)
69 +" with header="+(lLength+WaveTool.DATA_OFFSET)
70 +"=0x"+Long.toHexString(lLength+WaveTool.DATA_OFFSET));
71 }
72 throw new IllegalArgumentException("Wave files cannot be larger than 4GB.");
73 }
74 formatCode = WaveTool.getFormatCode(getFormat());
75 if (formatCode == WaveTool.WAVE_FORMAT_UNSPECIFIED) {
76 throw new IllegalArgumentException("Unknown encoding/format for this wave file.");
77 }
78
79 }
80
81 protected void writeHeader()
82 throws IOException {
83 if (TDebug.TraceAudioOutputStream) {
84 TDebug.out("WaveAudioOutputStream.writeHeader()");
85 }
86 AudioFormat format = getFormat();
87 long lLength = getLength();
88 int formatChunkAdd=0;
89 if (formatCode==WaveTool.WAVE_FORMAT_GSM610) {
90 // space for extra fields
91 formatChunkAdd+=2;
92 }
93 int dataOffset=WaveTool.DATA_OFFSET+formatChunkAdd;
94 if (formatCode!=WaveTool.WAVE_FORMAT_PCM) {
95 // space for fact chunk
96 dataOffset+=4+WaveTool.CHUNK_HEADER_SIZE;
97 }
98
99 // if patching the header, and the length has not been known at first
100 // writing of the header, just truncate the size fields, don't throw an exception
101 if (lLength != AudioSystem.NOT_SPECIFIED
102 && lLength+dataOffset>0xFFFFFFFFl) {
103 lLength=0xFFFFFFFFl-dataOffset;
104 }
105
106 // chunks must be on word-boundaries
107 long lDataChunkSize=lLength+(lLength%2);
108 TDataOutputStream dos = getDataOutputStream();
109
110 // write RIFF container chunk
111 dos.writeInt(WaveTool.WAVE_RIFF_MAGIC);
112 dos.writeLittleEndian32((int) ((lDataChunkSize+dataOffset-WaveTool.CHUNK_HEADER_SIZE)
113 & 0xFFFFFFFF));
114 dos.writeInt(WaveTool.WAVE_WAVE_MAGIC);
115
116 // write fmt_ chunk
117 int formatChunkSize=WaveTool.FMT_CHUNK_SIZE+formatChunkAdd;
118 short sampleSizeInBits=(short) format.getSampleSizeInBits();
119 int decodedSamplesPerBlock=1;
120
121 if (formatCode==WaveTool.WAVE_FORMAT_GSM610) {
122 if (format.getFrameSize()==33) {
123 decodedSamplesPerBlock=320;
124 } else if (format.getFrameSize()==65) {
125 decodedSamplesPerBlock=320;
126 } else {
127 // how to retrieve this value here ?
128 decodedSamplesPerBlock=(int) (format.getFrameSize()*(320.0f/65.0f));
129 }
130 sampleSizeInBits=0; // MS standard
131 }
132
133
134 int avgBytesPerSec=((int) format.getSampleRate())/decodedSamplesPerBlock*format.getFrameSize();
135 dos.writeInt(WaveTool.WAVE_FMT_MAGIC);
136 dos.writeLittleEndian32(formatChunkSize);
137 dos.writeLittleEndian16((short) formatCode); // wFormatTag
138 dos.writeLittleEndian16((short) format.getChannels()); // nChannels
139 dos.writeLittleEndian32((int) format.getSampleRate()); // nSamplesPerSec
140 dos.writeLittleEndian32(avgBytesPerSec); // nAvgBytesPerSec
141 dos.writeLittleEndian16((short) format.getFrameSize()); // nBlockalign
142 dos.writeLittleEndian16(sampleSizeInBits); // wBitsPerSample
143 dos.writeLittleEndian16((short) formatChunkAdd); // cbSize
144
145 if (formatCode==WaveTool.WAVE_FORMAT_GSM610) {
146 dos.writeLittleEndian16((short) decodedSamplesPerBlock); // wSamplesPerBlock
147 }
148
149 // write fact chunk
150
151
152 if (formatCode!=WaveTool.WAVE_FORMAT_PCM) {
153 // write "fact" chunk: number of samples
154 // todo: add this as an attribute or property
155 // in AudioOutputStream or AudioInputStream
156 long samples=0;
157 if (lLength!=AudioSystem.NOT_SPECIFIED) {
158 samples=lLength/format.getFrameSize()*decodedSamplesPerBlock;
159 }
160 // saturate sample count
161 if (samples>0xFFFFFFFFl) {
162 samples=(0xFFFFFFFFl/decodedSamplesPerBlock)*decodedSamplesPerBlock;
163 }
164 dos.writeInt(WaveTool.WAVE_FACT_MAGIC);
165 dos.writeLittleEndian32(4);
166 dos.writeLittleEndian32((int) (samples & 0xFFFFFFFF));
167 }
168
169 // write header of data chunk
170 dos.writeInt(WaveTool.WAVE_DATA_MAGIC);
171 dos.writeLittleEndian32((lLength!=AudioSystem.NOT_SPECIFIED)?((int) lLength):LENGTH_NOT_KNOWN);
172 }
173
174 protected void patchHeader()
175 throws IOException {
176 TDataOutputStream tdos = getDataOutputStream();
177 tdos.seek(0);
178 setLengthFromCalculatedLength();
179 writeHeader();
180 }
181
182 public void close() throws IOException {
183 long nBytesWritten=getCalculatedLength();
184
185 if ((nBytesWritten % 2)==1) {
186 if (TDebug.TraceAudioOutputStream) {
187 TDebug.out("WaveOutputStream.close(): adding padding byte");
188 }
189 // extra byte for to align on word boundaries
190 TDataOutputStream tdos = getDataOutputStream();
191 tdos.writeByte(0);
192 // DON'T adjust calculated length !
193 }
194
195
196 super.close();
197 }
198
199}
200
201/*** WaveAudioOutputStream.java ***/
diff --git a/songdbj/org/tritonus/file/WaveTool.java b/songdbj/org/tritonus/file/WaveTool.java
new file mode 100644
index 0000000000..3487557088
--- /dev/null
+++ b/songdbj/org/tritonus/file/WaveTool.java
@@ -0,0 +1,115 @@
1/*
2 * WaveTool.java
3 *
4 * This file is part of Tritonus: http://www.tritonus.org/
5 */
6
7/*
8 * Copyright (c) 2000 by Florian Bomers <http://www.bomers.de>
9 *
10 *
11 * This program is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU Library General Public License as published
13 * by the Free Software Foundation; either version 2 of the License, or
14 * (at your option) any later version.
15 *
16 * This program is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU Library General Public License for more details.
20 *
21 * You should have received a copy of the GNU Library General Public
22 * License along with this program; if not, write to the Free Software
23 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
24 *
25 */
26
27/*
28|<--- this code is formatted to fit into 80 columns --->|
29*/
30
31package org.tritonus.sampled.file;
32
33import javax.sound.sampled.AudioSystem;
34import javax.sound.sampled.AudioFormat;
35import javax.sound.sampled.AudioFileFormat;
36
37
38/**
39 * Common constants and methods for handling wave files.
40 *
41 * @author Florian Bomers
42 */
43
44public class WaveTool {
45
46 public static final int WAVE_RIFF_MAGIC = 0x52494646; // "RIFF"
47 public static final int WAVE_WAVE_MAGIC = 0x57415645; // "WAVE"
48 public static final int WAVE_FMT_MAGIC = 0x666D7420; // "fmt "
49 public static final int WAVE_DATA_MAGIC = 0x64617461; // "DATA"
50 public static final int WAVE_FACT_MAGIC = 0x66616374; // "fact"
51
52 public static final short WAVE_FORMAT_UNSPECIFIED = 0;
53 public static final short WAVE_FORMAT_PCM = 1;
54 public static final short WAVE_FORMAT_MS_ADPCM = 2;
55 public static final short WAVE_FORMAT_ALAW = 6;
56 public static final short WAVE_FORMAT_ULAW = 7;
57 public static final short WAVE_FORMAT_IMA_ADPCM = 17; // same as DVI_ADPCM
58 public static final short WAVE_FORMAT_G723_ADPCM = 20;
59 public static final short WAVE_FORMAT_GSM610 = 49;
60 public static final short WAVE_FORMAT_G721_ADPCM = 64;
61 public static final short WAVE_FORMAT_MPEG = 80;
62
63 public static final int MIN_FMT_CHUNK_LENGTH=14;
64 public static final int MIN_DATA_OFFSET=12+8+MIN_FMT_CHUNK_LENGTH+8;
65 public static final int MIN_FACT_CHUNK_LENGTH = 4;
66
67 // we always write the sample size in bits and the length of extra bytes.
68 // There are programs (CoolEdit) that rely on the
69 // additional entry for sample size in bits.
70 public static final int FMT_CHUNK_SIZE=18;
71 public static final int RIFF_CONTAINER_CHUNK_SIZE=12;
72 public static final int CHUNK_HEADER_SIZE=8;
73 public static final int DATA_OFFSET=RIFF_CONTAINER_CHUNK_SIZE
74 +CHUNK_HEADER_SIZE+FMT_CHUNK_SIZE+CHUNK_HEADER_SIZE;
75
76 public static AudioFormat.Encoding GSM0610 = new AudioFormat.Encoding("GSM0610");
77 public static AudioFormat.Encoding IMA_ADPCM = new AudioFormat.Encoding("IMA_ADPCM");
78
79 public static short getFormatCode(AudioFormat format) {
80 AudioFormat.Encoding encoding = format.getEncoding();
81 int nSampleSize = format.getSampleSizeInBits();
82 boolean littleEndian = !format.isBigEndian();
83 boolean frameSizeOK=format.getFrameSize()==AudioSystem.NOT_SPECIFIED
84 || format.getChannels()!=AudioSystem.NOT_SPECIFIED
85 || format.getFrameSize()==nSampleSize/8*format.getChannels();
86
87 if (nSampleSize==8 && frameSizeOK
88 && (encoding.equals(AudioFormat.Encoding.PCM_SIGNED)
89 || encoding.equals(AudioFormat.Encoding.PCM_UNSIGNED))) {
90 return WAVE_FORMAT_PCM;
91 } else if (nSampleSize>8 && frameSizeOK && littleEndian
92 && encoding.equals(AudioFormat.Encoding.PCM_SIGNED)) {
93 return WAVE_FORMAT_PCM;
94 } else if (encoding.equals(AudioFormat.Encoding.ULAW)
95 && (nSampleSize==AudioSystem.NOT_SPECIFIED || nSampleSize == 8)
96 && frameSizeOK) {
97 return WAVE_FORMAT_ULAW;
98 } else if (encoding.equals(AudioFormat.Encoding.ALAW)
99 && (nSampleSize==AudioSystem.NOT_SPECIFIED || nSampleSize == 8)
100 && frameSizeOK) {
101 return WAVE_FORMAT_ALAW;
102 } else if (encoding.equals(new AudioFormat.Encoding("IMA_ADPCM"))
103 && nSampleSize == 4)
104 {
105 return WAVE_FORMAT_IMA_ADPCM;
106 }
107 else if (encoding.equals(GSM0610)) {
108 return WAVE_FORMAT_GSM610;
109 }
110 return WAVE_FORMAT_UNSPECIFIED;
111 }
112
113}
114
115/*** WaveTool.java ***/
diff --git a/songdbj/org/tritonus/file/gsm/GSMAudioFileReader.java b/songdbj/org/tritonus/file/gsm/GSMAudioFileReader.java
new file mode 100644
index 0000000000..26859ddf24
--- /dev/null
+++ b/songdbj/org/tritonus/file/gsm/GSMAudioFileReader.java
@@ -0,0 +1,142 @@
1/*
2 * GSMAudioFileReader.java
3 *
4 * This file is part of Tritonus: http://www.tritonus.org/
5 */
6
7/*
8 * Copyright (c) 1999 - 2004 by Matthias Pfisterer
9 * Copyright (c) 2001 by Florian Bomers <http://www.bomers.de>
10 *
11 * This program is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU Library General Public License as published
13 * by the Free Software Foundation; either version 2 of the License, or
14 * (at your option) any later version.
15 *
16 * This program is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU Library General Public License for more details.
20 *
21 * You should have received a copy of the GNU Library General Public
22 * License along with this program; if not, write to the Free Software
23 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
24 */
25
26/*
27|<--- this code is formatted to fit into 80 columns --->|
28*/
29
30package org.tritonus.sampled.file.gsm;
31
32import java.io.InputStream;
33import java.io.IOException;
34import java.io.EOFException;
35
36import java.util.HashMap;
37import java.util.Map;
38
39import javax.sound.sampled.AudioSystem;
40import javax.sound.sampled.AudioFormat;
41import javax.sound.sampled.AudioFileFormat;
42import javax.sound.sampled.AudioInputStream;
43import javax.sound.sampled.UnsupportedAudioFileException;
44import javax.sound.sampled.spi.AudioFileReader;
45
46import org.tritonus.share.TDebug;
47import org.tritonus.share.sampled.file.TAudioFileFormat;
48import org.tritonus.share.sampled.file.TAudioFileReader;
49
50
51
52/** AudioFileReader class for GSM 06.10 data.
53 @author Matthias Pfisterer
54 */
55public class GSMAudioFileReader
56extends TAudioFileReader
57{
58 private static final int GSM_MAGIC = 0xD0;
59 private static final int GSM_MAGIC_MASK = 0xF0;
60
61 private static final int MARK_LIMIT = 1;
62
63
64
65 public GSMAudioFileReader()
66 {
67 super(MARK_LIMIT, true);
68 }
69
70
71
72 protected AudioFileFormat getAudioFileFormat(InputStream inputStream, long lFileSizeInBytes)
73 throws UnsupportedAudioFileException, IOException
74 {
75 if (TDebug.TraceAudioFileReader) { TDebug.out("GSMAudioFileReader.getAudioFileFormat(): begin"); }
76 int b0 = inputStream.read();
77 if (b0 < 0)
78 {
79 throw new EOFException();
80 }
81
82 /*
83 * Check for magic number.
84 */
85 if ((b0 & GSM_MAGIC_MASK) != GSM_MAGIC)
86 {
87 throw new UnsupportedAudioFileException("not a GSM stream: wrong magic number");
88 }
89
90
91 /*
92 If the file size is known, we derive the number of frames
93 ('frame size') from it.
94 If the values don't fit into integers, we leave them at
95 NOT_SPECIFIED. 'Unknown' is considered less incorrect than
96 a wrong value.
97 */
98 // [fb] not specifying it causes Sun's Wave file writer to write rubbish
99 int nByteSize = AudioSystem.NOT_SPECIFIED;
100 int nFrameSize = AudioSystem.NOT_SPECIFIED;
101 Map<String, Object> properties = new HashMap<String, Object>();
102 if (lFileSizeInBytes != AudioSystem.NOT_SPECIFIED)
103 {
104 // the number of GSM frames
105 long lFrameSize = lFileSizeInBytes / 33;
106 // duration in microseconds
107 long lDuration = lFrameSize * 20000;
108 properties.put("duration", lDuration);
109 if (lFileSizeInBytes <= Integer.MAX_VALUE)
110 {
111 nByteSize = (int) lFileSizeInBytes;
112 nFrameSize = (int) (lFileSizeInBytes / 33);
113 }
114 }
115
116 Map<String, Object> afProperties = new HashMap<String, Object>();
117 afProperties.put("bitrate", 13200L);
118 AudioFormat format = new AudioFormat(
119 new AudioFormat.Encoding("GSM0610"),
120 8000.0F,
121 AudioSystem.NOT_SPECIFIED /* ??? [sample size in bits] */,
122 1,
123 33,
124 50.0F,
125 true, // this value is chosen arbitrarily
126 afProperties);
127 AudioFileFormat audioFileFormat =
128 new TAudioFileFormat(
129 new AudioFileFormat.Type("GSM","gsm"),
130 format,
131 nFrameSize,
132 nByteSize,
133 properties);
134 if (TDebug.TraceAudioFileReader) { TDebug.out("GSMAudioFileReader.getAudioFileFormat(): end"); }
135 return audioFileFormat;
136 }
137}
138
139
140
141/*** GSMAudioFileReader.java ***/
142
diff --git a/songdbj/org/tritonus/file/gsm/GSMAudioFileWriter.java b/songdbj/org/tritonus/file/gsm/GSMAudioFileWriter.java
new file mode 100644
index 0000000000..eb3d1f3f16
--- /dev/null
+++ b/songdbj/org/tritonus/file/gsm/GSMAudioFileWriter.java
@@ -0,0 +1,77 @@
1/*
2 * GSMAudioFileWriter.java
3 *
4 * This file is part of Tritonus: http://www.tritonus.org/
5 */
6
7/*
8 * Copyright (c) 2000 by Florian Bomers <http://www.bomers.de>
9 * Copyright (c) 2000 by Matthias Pfisterer
10 *
11 *
12 * This program is free software; you can redistribute it and/or modify
13 * it under the terms of the GNU Library General Public License as published
14 * by the Free Software Foundation; either version 2 of the License, or
15 * (at your option) any later version.
16 *
17 * This program is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 * GNU Library General Public License for more details.
21 *
22 * You should have received a copy of the GNU Library General Public
23 * License along with this program; if not, write to the Free Software
24 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
25 *
26 */
27
28/*
29|<--- this code is formatted to fit into 80 columns --->|
30*/
31
32package org.tritonus.sampled.file.gsm;
33
34import java.util.Arrays;
35
36import javax.sound.sampled.AudioFileFormat;
37import javax.sound.sampled.AudioFormat;
38import javax.sound.sampled.AudioSystem;
39
40import org.tritonus.share.TDebug;
41import org.tritonus.share.sampled.file.THeaderlessAudioFileWriter;
42
43
44
45/** Class for writing GSM streams
46 *
47 * @author Florian Bomers
48 * @author Matthias Pfisterer
49 */
50public class GSMAudioFileWriter
51extends THeaderlessAudioFileWriter
52{
53 private static final AudioFileFormat.Type[] FILE_TYPES =
54 {
55 new AudioFileFormat.Type("GSM", "gsm")
56 };
57
58 private static final AudioFormat[] AUDIO_FORMATS =
59 {
60 new AudioFormat(new AudioFormat.Encoding("GSM0610"), 8000.0F, ALL, 1, 33, 50.0F, false),
61 new AudioFormat(new AudioFormat.Encoding("GSM0610"), 8000.0F, ALL, 1, 33, 50.0F, true),
62 };
63
64
65
66 public GSMAudioFileWriter()
67 {
68 super(Arrays.asList(FILE_TYPES),
69 Arrays.asList(AUDIO_FORMATS));
70 if (TDebug.TraceAudioFileWriter) { TDebug.out("GSMAudioFileWriter.<init>(): begin"); }
71 if (TDebug.TraceAudioFileWriter) { TDebug.out("GSMAudioFileWriter.<init>(): end"); }
72 }
73}
74
75
76
77/*** GSMAudioFileWriter.java ***/
diff --git a/songdbj/org/tritonus/file/gsm/package.html b/songdbj/org/tritonus/file/gsm/package.html
new file mode 100644
index 0000000000..763851dda0
--- /dev/null
+++ b/songdbj/org/tritonus/file/gsm/package.html
@@ -0,0 +1,10 @@
1<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
2<html>
3 <head>
4 </head>
5
6 <body>
7 <p>GSM 06.10 audio file reader and writer.
8 The classes provided here .</p>
9 </body>
10</html>
diff --git a/songdbj/org/tritonus/file/jorbis/JorbisAudioFileReader.java b/songdbj/org/tritonus/file/jorbis/JorbisAudioFileReader.java
new file mode 100644
index 0000000000..5b33534b21
--- /dev/null
+++ b/songdbj/org/tritonus/file/jorbis/JorbisAudioFileReader.java
@@ -0,0 +1,231 @@
1/*
2 * JorbisAudioFileReader.java
3 *
4 * This file is part of Tritonus: http://www.tritonus.org/
5 */
6
7/*
8 * Copyright (c) 2001 by Matthias Pfisterer
9 *
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU Library General Public License as published
12 * by the Free Software Foundation; either version 2 of the License, or
13 * (at your option) any later version.
14 *
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU Library General Public License for more details.
19 *
20 * You should have received a copy of the GNU Library General Public
21 * License along with this program; if not, write to the Free Software
22 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
23 */
24
25/*
26|<--- this code is formatted to fit into 80 columns --->|
27*/
28
29package org.tritonus.sampled.file.jorbis;
30
31import java.io.InputStream;
32import java.io.IOException;
33
34import javax.sound.sampled.AudioSystem;
35import javax.sound.sampled.AudioFormat;
36import javax.sound.sampled.AudioFileFormat;
37import javax.sound.sampled.UnsupportedAudioFileException;
38
39import org.tritonus.share.TDebug;
40import org.tritonus.share.sampled.file.TAudioFileFormat;
41import org.tritonus.share.sampled.file.TAudioFileReader;
42
43import com.jcraft.jogg.Buffer;
44import com.jcraft.jogg.SyncState;
45import com.jcraft.jogg.StreamState;
46import com.jcraft.jogg.Page;
47import com.jcraft.jogg.Packet;
48
49
50
51/**
52 * @author Matthias Pfisterer
53 */
54public class JorbisAudioFileReader
55extends TAudioFileReader
56{
57 private static final int INITAL_READ_LENGTH = 4096;
58 private static final int MARK_LIMIT = INITAL_READ_LENGTH + 1;
59
60
61
62 public JorbisAudioFileReader()
63 {
64 super(MARK_LIMIT, true);
65 }
66
67
68
69 protected AudioFileFormat getAudioFileFormat(InputStream inputStream, long lFileSizeInBytes)
70 throws UnsupportedAudioFileException, IOException
71 {
72 // sync and verify incoming physical bitstream
73 SyncState oggSyncState = new SyncState();
74
75 // take physical pages, weld into a logical stream of packets
76 StreamState oggStreamState = new StreamState();
77
78 // one Ogg bitstream page. Vorbis packets are inside
79 Page oggPage = new Page();
80
81 // one raw packet of data for decode
82 Packet oggPacket = new Packet();
83
84 int bytes = 0;
85
86 // Decode setup
87
88 oggSyncState.init(); // Now we can read pages
89
90 // grab some data at the head of the stream. We want the first page
91 // (which is guaranteed to be small and only contain the Vorbis
92 // stream initial header) We need the first page to get the stream
93 // serialno.
94
95 // submit a 4k block to libvorbis' Ogg layer
96 int index = oggSyncState.buffer(INITAL_READ_LENGTH);
97 bytes = inputStream.read(oggSyncState.data, index, INITAL_READ_LENGTH);
98 oggSyncState.wrote(bytes);
99
100 // Get the first page.
101 if (oggSyncState.pageout(oggPage) != 1)
102 {
103 // have we simply run out of data? If so, we're done.
104 if (bytes < 4096)
105 {
106 // IDEA: throw EOFException?
107 throw new UnsupportedAudioFileException("not a Vorbis stream: ended prematurely");
108 }
109 throw new UnsupportedAudioFileException("not a Vorbis stream: not in Ogg bitstream format");
110 }
111
112 // Get the serial number and set up the rest of decode.
113 // serialno first; use it to set up a logical stream
114 oggStreamState.init(oggPage.serialno());
115
116 // extract the initial header from the first page and verify that the
117 // Ogg bitstream is in fact Vorbis data
118
119 // I handle the initial header first instead of just having the code
120 // read all three Vorbis headers at once because reading the initial
121 // header is an easy way to identify a Vorbis bitstream and it's
122 // useful to see that functionality seperated out.
123
124 if (oggStreamState.pagein(oggPage) < 0)
125 {
126 // error; stream version mismatch perhaps
127 throw new UnsupportedAudioFileException("not a Vorbis stream: can't read first page of Ogg bitstream data");
128 }
129
130 if (oggStreamState.packetout(oggPacket) != 1)
131 {
132 // no page? must not be vorbis
133 throw new UnsupportedAudioFileException("not a Vorbis stream: can't read initial header packet");
134 }
135
136 Buffer oggPacketBuffer = new Buffer();
137 oggPacketBuffer.readinit(oggPacket.packet_base, oggPacket.packet, oggPacket.bytes);
138
139 int nPacketType = oggPacketBuffer.read(8);
140 byte[] buf = new byte[6];
141 oggPacketBuffer.read(buf, 6);
142 if(buf[0]!='v' || buf[1]!='o' || buf[2]!='r' ||
143 buf[3]!='b' || buf[4]!='i' || buf[5]!='s')
144 {
145 throw new UnsupportedAudioFileException("not a Vorbis stream: not a vorbis header packet");
146 }
147 if (nPacketType != 1)
148 {
149 throw new UnsupportedAudioFileException("not a Vorbis stream: first packet is not the identification header");
150 }
151 if(oggPacket.b_o_s == 0)
152 {
153 throw new UnsupportedAudioFileException("not a Vorbis stream: initial packet not marked as beginning of stream");
154 }
155 int nVersion = oggPacketBuffer.read(32);
156 if (nVersion != 0)
157 {
158 throw new UnsupportedAudioFileException("not a Vorbis stream: wrong vorbis version");
159 }
160 int nChannels = oggPacketBuffer.read(8);
161 float fSampleRate = oggPacketBuffer.read(32);
162
163 // These are only used for error checking.
164 int bitrate_upper = oggPacketBuffer.read(32);
165 int bitrate_nominal = oggPacketBuffer.read(32);
166 int bitrate_lower = oggPacketBuffer.read(32);
167
168 int[] blocksizes = new int[2];
169 blocksizes[0] = 1 << oggPacketBuffer.read(4);
170 blocksizes[1] = 1 << oggPacketBuffer.read(4);
171
172 if (fSampleRate < 1.0F ||
173 nChannels < 1 ||
174 blocksizes[0] < 8||
175 blocksizes[1] < blocksizes[0] ||
176 oggPacketBuffer.read(1) != 1)
177 {
178 throw new UnsupportedAudioFileException("not a Vorbis stream: illegal values in initial header");
179 }
180
181
182 if (TDebug.TraceAudioFileReader) { TDebug.out("JorbisAudioFileReader.getAudioFileFormat(): channels: " + nChannels); }
183 if (TDebug.TraceAudioFileReader) { TDebug.out("JorbisAudioFileReader.getAudioFileFormat(): rate: " + fSampleRate); }
184
185 /*
186 If the file size is known, we derive the number of frames
187 ('frame size') from it.
188 If the values don't fit into integers, we leave them at
189 NOT_SPECIFIED. 'Unknown' is considered less incorrect than
190 a wrong value.
191 */
192 // [fb] not specifying it causes Sun's Wave file writer to write rubbish
193 int nByteSize = AudioSystem.NOT_SPECIFIED;
194 if (lFileSizeInBytes != AudioSystem.NOT_SPECIFIED
195 && lFileSizeInBytes <= Integer.MAX_VALUE)
196 {
197 nByteSize = (int) lFileSizeInBytes;
198 }
199 int nFrameSize = AudioSystem.NOT_SPECIFIED;
200 /* Can we calculate a useful size?
201 Peeking into ogginfo gives the insight that the only
202 way seems to be reading through the file. This is
203 something we do not want, at least not by default.
204 */
205 // nFrameSize = (int) (lFileSizeInBytes / ...;
206
207 AudioFormat format = new AudioFormat(
208 new AudioFormat.Encoding("VORBIS"),
209 fSampleRate,
210 AudioSystem.NOT_SPECIFIED,
211 nChannels,
212 AudioSystem.NOT_SPECIFIED,
213 AudioSystem.NOT_SPECIFIED,
214 true); // this value is chosen arbitrarily
215 if (TDebug.TraceAudioFileReader) { TDebug.out("JorbisAudioFileReader.getAudioFileFormat(): AudioFormat: " + format); }
216 AudioFileFormat.Type type = new AudioFileFormat.Type("Ogg","ogg");
217 AudioFileFormat audioFileFormat =
218 new TAudioFileFormat(
219 type,
220 format,
221 nFrameSize,
222 nByteSize);
223 if (TDebug.TraceAudioFileReader) { TDebug.out("JorbisAudioFileReader.getAudioFileFormat(): AudioFileFormat: " + audioFileFormat); }
224 return audioFileFormat;
225 }
226}
227
228
229
230/*** JorbisAudioFileReader.java ***/
231
diff --git a/songdbj/org/tritonus/file/jorbis/package.html b/songdbj/org/tritonus/file/jorbis/package.html
new file mode 100644
index 0000000000..e5b5599d8c
--- /dev/null
+++ b/songdbj/org/tritonus/file/jorbis/package.html
@@ -0,0 +1,10 @@
1<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
2<html>
3 <head>
4 </head>
5
6 <body>
7 <p>Ogg vorbis audio file reader based on the jorbis library.
8 The classes provided here .</p>
9 </body>
10</html>
diff --git a/songdbj/org/tritonus/file/mpeg/MpegAudioFileWriter.java b/songdbj/org/tritonus/file/mpeg/MpegAudioFileWriter.java
new file mode 100644
index 0000000000..d958fecf0d
--- /dev/null
+++ b/songdbj/org/tritonus/file/mpeg/MpegAudioFileWriter.java
@@ -0,0 +1,75 @@
1/*
2 * MpegAudioFileWriter.java
3 *
4 * This file is part of Tritonus: http://www.tritonus.org/
5 */
6
7/*
8 * Copyright (c) 2000 by Florian Bomers <http://www.bomers.de>
9 *
10 *
11 * This program is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU Library General Public License as published
13 * by the Free Software Foundation; either version 2 of the License, or
14 * (at your option) any later version.
15 *
16 * This program is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU Library General Public License for more details.
20 *
21 * You should have received a copy of the GNU Library General Public
22 * License along with this program; if not, write to the Free Software
23 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
24 *
25 */
26
27/*
28|<--- this code is formatted to fit into 80 columns --->|
29*/
30
31package org.tritonus.sampled.file.mpeg;
32
33import java.util.Arrays;
34
35import javax.sound.sampled.AudioFileFormat;
36import javax.sound.sampled.AudioFormat;
37
38import org.tritonus.share.TDebug;
39import org.tritonus.share.sampled.file.THeaderlessAudioFileWriter;
40
41
42
43/** Class for writing mpeg files
44 *
45 * @author Florian Bomers
46 */
47public class MpegAudioFileWriter extends THeaderlessAudioFileWriter {
48
49 private static final AudioFileFormat.Type[] FILE_TYPES = {
50 //new AudioFileFormat.Type("MPEG", "mpeg"),
51 // workaround for the fixed extension problem in AudioFileFormat.Type
52 // see org.tritonus.share.sampled.AudioFileTypes.java
53 new AudioFileFormat.Type("MP3", "mp3")
54 };
55
56 public static AudioFormat.Encoding MPEG1L3=new AudioFormat.Encoding("MPEG1L3");
57
58 private static final AudioFormat[] AUDIO_FORMATS = {
59 new AudioFormat(MPEG1L3, ALL, ALL, 1, ALL, ALL, false),
60 new AudioFormat(MPEG1L3, ALL, ALL, 1, ALL, ALL, true),
61 new AudioFormat(MPEG1L3, ALL, ALL, 2, ALL, ALL, false),
62 new AudioFormat(MPEG1L3, ALL, ALL, 2, ALL, ALL, true),
63 };
64
65 public MpegAudioFileWriter()
66 {
67 super(Arrays.asList(FILE_TYPES),
68 Arrays.asList(AUDIO_FORMATS));
69 if (TDebug.TraceAudioFileWriter) { TDebug.out("MpegAudioFileWriter.<init>(): begin"); }
70 if (TDebug.TraceAudioFileWriter) { TDebug.out("MpegAudioFileWriter.<init>(): end"); }
71 }
72}
73
74
75/*** MpegAudioFileWriter.java ***/
diff --git a/songdbj/org/tritonus/file/mpeg/package.html b/songdbj/org/tritonus/file/mpeg/package.html
new file mode 100644
index 0000000000..f24ded00ec
--- /dev/null
+++ b/songdbj/org/tritonus/file/mpeg/package.html
@@ -0,0 +1,10 @@
1<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
2<html>
3 <head>
4 </head>
5
6 <body>
7 <p>Mp3 audio file reader and writer.
8 The classes provided here .</p>
9 </body>
10</html>
diff --git a/songdbj/org/tritonus/file/package.html b/songdbj/org/tritonus/file/package.html
new file mode 100644
index 0000000000..a3ba1632a8
--- /dev/null
+++ b/songdbj/org/tritonus/file/package.html
@@ -0,0 +1,10 @@
1<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
2<html>
3 <head>
4 </head>
5
6 <body>
7 <p>Standard audio file readers and writers (.aiff, .au, .wav).
8 The classes provided here .</p>
9 </body>
10</html>
diff --git a/songdbj/org/tritonus/file/pvorbis/VorbisAudioFileReader.java b/songdbj/org/tritonus/file/pvorbis/VorbisAudioFileReader.java
new file mode 100644
index 0000000000..a882f11c3a
--- /dev/null
+++ b/songdbj/org/tritonus/file/pvorbis/VorbisAudioFileReader.java
@@ -0,0 +1,302 @@
1/*
2 * VorbisAudioFileReader.java
3 *
4 * This file is part of Tritonus: http://www.tritonus.org/
5 */
6
7/*
8 * Copyright (c) 2001 - 2004 by Matthias Pfisterer
9 *
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU Library General Public License as published
12 * by the Free Software Foundation; either version 2 of the License, or
13 * (at your option) any later version.
14 *
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU Library General Public License for more details.
19 *
20 * You should have received a copy of the GNU Library General Public
21 * License along with this program; if not, write to the Free Software
22 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
23 */
24
25/*
26|<--- this code is formatted to fit into 80 columns --->|
27*/
28
29package org.tritonus.sampled.file.pvorbis;
30
31import java.io.InputStream;
32import java.io.IOException;
33
34import javax.sound.sampled.AudioSystem;
35import javax.sound.sampled.AudioFormat;
36import javax.sound.sampled.AudioFileFormat;
37import javax.sound.sampled.UnsupportedAudioFileException;
38
39import org.tritonus.share.TDebug;
40import org.tritonus.share.sampled.file.TAudioFileFormat;
41import org.tritonus.share.sampled.file.TAudioFileReader;
42
43import org.tritonus.lowlevel.pogg.Buffer;
44import org.tritonus.lowlevel.pogg.Page;
45import org.tritonus.lowlevel.pogg.Packet;
46import org.tritonus.lowlevel.pogg.SyncState;
47import org.tritonus.lowlevel.pogg.StreamState;
48
49
50
51/**
52 * @author Matthias Pfisterer
53 */
54public class VorbisAudioFileReader
55extends TAudioFileReader
56{
57 private static final int INITAL_READ_LENGTH = 4096;
58 private static final int MARK_LIMIT = INITAL_READ_LENGTH + 1;
59
60
61
62 public VorbisAudioFileReader()
63 {
64 super(MARK_LIMIT, true);
65 }
66
67
68
69 protected AudioFileFormat getAudioFileFormat(InputStream inputStream,
70 long lFileSizeInBytes)
71 throws UnsupportedAudioFileException, IOException
72 {
73 if (TDebug.TraceAudioFileReader) { TDebug.out(">VorbisAudioFileReader.getAudioFileFormat(): begin"); }
74 SyncState oggSyncState = new SyncState();
75 StreamState oggStreamState = new StreamState();
76 Page oggPage = new Page();
77 Packet oggPacket = new Packet();
78
79 int bytes = 0;
80
81 // Decode setup
82
83 oggSyncState.init(); // Now we can read pages
84
85 // grab some data at the head of the stream. We want the first page
86 // (which is guaranteed to be small and only contain the Vorbis
87 // stream initial header) We need the first page to get the stream
88 // serialno.
89
90 // submit a 4k block to libvorbis' Ogg layer
91 byte[] abBuffer = new byte[INITAL_READ_LENGTH];
92 bytes = inputStream.read(abBuffer);
93 if (TDebug.TraceAudioFileReader) { TDebug.out("read bytes from input stream: " + bytes); }
94 int nResult = oggSyncState.write(abBuffer, bytes);
95 if (TDebug.TraceAudioFileReader) { TDebug.out("SyncState.write() returned " + nResult); }
96
97 // Get the first page.
98 if (oggSyncState.pageOut(oggPage) != 1)
99 {
100 // have we simply run out of data? If so, we're done.
101 if (bytes < INITAL_READ_LENGTH)
102 {
103 if (TDebug.TraceAudioFileReader) { TDebug.out("stream ended prematurely"); }
104 if (TDebug.TraceAudioFileReader) { TDebug.out("<VorbisAudioFileReader.getAudioFileFormat(): throwing exception"); }
105 // IDEA: throw EOFException?
106 oggSyncState.free();
107 oggStreamState.free();
108 oggPage.free();
109 oggPacket.free();
110 throw new UnsupportedAudioFileException("not a Vorbis stream: ended prematurely");
111 }
112 if (TDebug.TraceAudioFileReader) { TDebug.out("not in Ogg bitstream format"); }
113 if (TDebug.TraceAudioFileReader) { TDebug.out("<VorbisAudioFileReader.getAudioFileFormat(): throwing exception"); }
114 oggSyncState.free();
115 oggStreamState.free();
116 oggPage.free();
117 oggPacket.free();
118 throw new UnsupportedAudioFileException("not a Vorbis stream: not in Ogg bitstream format");
119 }
120
121 // Get the serial number and set up the rest of decode.
122 // serialno first; use it to set up a logical stream
123 int nSerialNo = oggPage.getSerialNo();
124 if (TDebug.TraceAudioFileReader) TDebug.out("serial no.: " + nSerialNo);
125 oggStreamState.init(nSerialNo);
126
127 // extract the initial header from the first page and verify that the
128 // Ogg bitstream is in fact Vorbis data
129
130 // I handle the initial header first instead of just having the code
131 // read all three Vorbis headers at once because reading the initial
132 // header is an easy way to identify a Vorbis bitstream and it's
133 // useful to see that functionality seperated out.
134
135 if (oggStreamState.pageIn(oggPage) < 0)
136 {
137 if (TDebug.TraceAudioFileReader) { TDebug.out("can't read first page of Ogg bitstream data"); }
138 if (TDebug.TraceAudioFileReader) { TDebug.out("<VorbisAudioFileReader.getAudioFileFormat(): throwing exception"); }
139 // error; stream version mismatch perhaps
140 oggSyncState.free();
141 oggStreamState.free();
142 oggPage.free();
143 oggPacket.free();
144 throw new UnsupportedAudioFileException("not a Vorbis stream: can't read first page of Ogg bitstream data");
145 }
146
147 if (oggStreamState.packetOut(oggPacket) != 1)
148 {
149 if (TDebug.TraceAudioFileReader) { TDebug.out("can't read initial header packet"); }
150 if (TDebug.TraceAudioFileReader) { TDebug.out("<VorbisAudioFileReader.getAudioFileFormat(): throwing exception"); }
151 // no page? must not be vorbis
152 oggSyncState.free();
153 oggStreamState.free();
154 oggPage.free();
155 oggPacket.free();
156 throw new UnsupportedAudioFileException("not a Vorbis stream: can't read initial header packet");
157 }
158
159 byte[] abData = oggPacket.getData();
160 if (TDebug.TraceAudioFileReader)
161 {
162 String strData = "";
163 for (int i = 0; i < abData.length; i++)
164 {
165 strData += " " + abData[i];
166 }
167 TDebug.out("packet data: " + strData);
168 }
169
170 int nPacketType = abData[0];
171 if (TDebug.TraceAudioFileReader) { TDebug.out("packet type: " + nPacketType); }
172 if (nPacketType != 1)
173 {
174 if (TDebug.TraceAudioFileReader) { TDebug.out("first packet is not the identification header"); }
175 if (TDebug.TraceAudioFileReader) { TDebug.out("<VorbisAudioFileReader.getAudioFileFormat(): throwing exception"); }
176 oggSyncState.free();
177 oggStreamState.free();
178 oggPage.free();
179 oggPacket.free();
180 throw new UnsupportedAudioFileException("not a Vorbis stream: first packet is not the identification header");
181 }
182 if(abData[1] != 'v' ||
183 abData[2] != 'o' ||
184 abData[3] != 'r' ||
185 abData[4] != 'b' ||
186 abData[5] != 'i' ||
187 abData[6] != 's')
188 {
189 if (TDebug.TraceAudioFileReader) { TDebug.out("not a vorbis header packet"); }
190 if (TDebug.TraceAudioFileReader) { TDebug.out("<VorbisAudioFileReader.getAudioFileFormat(): throwing exception"); }
191 oggSyncState.free();
192 oggStreamState.free();
193 oggPage.free();
194 oggPacket.free();
195 throw new UnsupportedAudioFileException("not a Vorbis stream: not a vorbis header packet");
196 }
197 if (! oggPacket.isBos())
198 {
199 if (TDebug.TraceAudioFileReader) { TDebug.out("initial packet not marked as beginning of stream"); }
200 if (TDebug.TraceAudioFileReader) { TDebug.out("<VorbisAudioFileReader.getAudioFileFormat(): throwing exception"); }
201 oggSyncState.free();
202 oggStreamState.free();
203 oggPage.free();
204 oggPacket.free();
205 throw new UnsupportedAudioFileException("not a Vorbis stream: initial packet not marked as beginning of stream");
206 }
207 int nVersion = (abData[7] & 0xFF) + 256 * (abData[8] & 0xFF) + 65536 * (abData[9] & 0xFF) + 16777216 * (abData[10] & 0xFF);
208 if (TDebug.TraceAudioFileReader) { TDebug.out("version: " + nVersion); }
209 if (nVersion != 0)
210 {
211 if (TDebug.TraceAudioFileReader) { TDebug.out("wrong vorbis version"); }
212 if (TDebug.TraceAudioFileReader) { TDebug.out("<VorbisAudioFileReader.getAudioFileFormat(): throwing exception"); }
213 oggSyncState.free();
214 oggStreamState.free();
215 oggPage.free();
216 oggPacket.free();
217 throw new UnsupportedAudioFileException("not a Vorbis stream: wrong vorbis version");
218 }
219 int nChannels = (abData[11] & 0xFF);
220 float fSampleRate = (abData[12] & 0xFF) + 256 * (abData[13] & 0xFF) + 65536 * (abData[14] & 0xFF) + 16777216 * (abData[15] & 0xFF);
221 if (TDebug.TraceAudioFileReader) { TDebug.out("channels: " + nChannels); }
222 if (TDebug.TraceAudioFileReader) { TDebug.out("rate: " + fSampleRate); }
223
224 // These are only used for error checking.
225 int bitrate_upper = abData[16] + 256 * abData[17] + 65536 * abData[18] + 16777216 * abData[19];
226 int bitrate_nominal = abData[20] + 256 * abData[21] + 65536 * abData[22] + 16777216 * abData[23];
227 int bitrate_lower = abData[24] + 256 * abData[25] + 65536 * abData[26] + 16777216 * abData[27];
228
229 int[] blocksizes = new int[2];
230 blocksizes[0] = 1 << (abData[28] & 0xF);
231 blocksizes[1] = 1 << ((abData[28] >>> 4) & 0xF);
232 if (TDebug.TraceAudioFileReader) { TDebug.out("blocksizes[0]: " + blocksizes[0]); }
233 if (TDebug.TraceAudioFileReader) { TDebug.out("blocksizes[1]: " + blocksizes[1]); }
234
235 if (fSampleRate < 1.0F ||
236 nChannels < 1 ||
237 blocksizes[0] < 8||
238 blocksizes[1] < blocksizes[0] ||
239 (abData[29] & 0x1) != 1)
240 {
241 if (TDebug.TraceAudioFileReader) { TDebug.out("illegal values in initial header"); }
242 if (TDebug.TraceAudioFileReader) { TDebug.out("<VorbisAudioFileReader.getAudioFileFormat(): throwing exception"); }
243 oggSyncState.free();
244 oggStreamState.free();
245 oggPage.free();
246 oggPacket.free();
247 throw new UnsupportedAudioFileException("not a Vorbis stream: illegal values in initial header");
248 }
249
250 oggSyncState.free();
251 oggStreamState.free();
252 oggPage.free();
253 oggPacket.free();
254
255 /*
256 If the file size is known, we derive the number of frames
257 ('frame size') from it.
258 If the values don't fit into integers, we leave them at
259 NOT_SPECIFIED. 'Unknown' is considered less incorrect than
260 a wrong value.
261 */
262 // [fb] not specifying it causes Sun's Wave file writer to write rubbish
263 int nByteSize = AudioSystem.NOT_SPECIFIED;
264 if (lFileSizeInBytes != AudioSystem.NOT_SPECIFIED
265 && lFileSizeInBytes <= Integer.MAX_VALUE)
266 {
267 nByteSize = (int) lFileSizeInBytes;
268 }
269 int nFrameSize = AudioSystem.NOT_SPECIFIED;
270 /* Can we calculate a useful size?
271 Peeking into ogginfo gives the insight that the only
272 way seems to be reading through the file. This is
273 something we do not want, at least not by default.
274 */
275 // nFrameSize = (int) (lFileSizeInBytes / ...;
276
277 AudioFormat format = new AudioFormat(
278 new AudioFormat.Encoding("VORBIS"),
279 fSampleRate,
280 AudioSystem.NOT_SPECIFIED,
281 nChannels,
282 AudioSystem.NOT_SPECIFIED,
283 AudioSystem.NOT_SPECIFIED,
284 true); // this value is chosen arbitrarily
285 if (TDebug.TraceAudioFileReader) { TDebug.out("AudioFormat: " + format); }
286 AudioFileFormat.Type type = new AudioFileFormat.Type("Ogg","ogg");
287 AudioFileFormat audioFileFormat =
288 new TAudioFileFormat(
289 type,
290 format,
291 nFrameSize,
292 nByteSize);
293 if (TDebug.TraceAudioFileReader) { TDebug.out("AudioFileFormat: " + audioFileFormat); }
294 if (TDebug.TraceAudioFileReader) { TDebug.out("<VorbisAudioFileReader.getAudioFileFormat(): end"); }
295 return audioFileFormat;
296 }
297}
298
299
300
301/*** VorbisAudioFileReader.java ***/
302
diff --git a/songdbj/org/tritonus/file/pvorbis/VorbisAudioFileWriter.java b/songdbj/org/tritonus/file/pvorbis/VorbisAudioFileWriter.java
new file mode 100644
index 0000000000..5fc9c0663d
--- /dev/null
+++ b/songdbj/org/tritonus/file/pvorbis/VorbisAudioFileWriter.java
@@ -0,0 +1,75 @@
1/*
2 * VorbisAudioFileWriter.java
3 *
4 * This file is part of Tritonus: http://www.tritonus.org/
5 */
6
7/*
8 * Copyright (c) 2000 by Florian Bomers <http://www.bomers.de>
9 * Copyright (c) 2000 -2004 by Matthias Pfisterer
10 *
11 * This program is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU Library General Public License as published
13 * by the Free Software Foundation; either version 2 of the License, or
14 * (at your option) any later version.
15 *
16 * This program is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU Library General Public License for more details.
20 *
21 * You should have received a copy of the GNU Library General Public
22 * License along with this program; if not, write to the Free Software
23 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
24 */
25
26/*
27|<--- this code is formatted to fit into 80 columns --->|
28*/
29
30package org.tritonus.sampled.file.pvorbis;
31
32import java.util.Arrays;
33
34import javax.sound.sampled.AudioFileFormat;
35import javax.sound.sampled.AudioFormat;
36import javax.sound.sampled.AudioSystem;
37
38import org.tritonus.share.TDebug;
39import org.tritonus.share.sampled.file.THeaderlessAudioFileWriter;
40
41
42
43/** Class for writing Vorbis streams
44 *
45 * @author Florian Bomers
46 * @author Matthias Pfisterer
47 */
48public class VorbisAudioFileWriter
49extends THeaderlessAudioFileWriter
50{
51 private static final AudioFileFormat.Type[] FILE_TYPES =
52 {
53 new AudioFileFormat.Type("Vorbis", "ogg")
54 };
55
56 private static final AudioFormat[] AUDIO_FORMATS =
57 {
58 new AudioFormat(new AudioFormat.Encoding("VORBIS"), ALL, ALL, ALL, ALL, ALL, false),
59 new AudioFormat(new AudioFormat.Encoding("VORBIS"), ALL, ALL, ALL, ALL, ALL, true),
60 };
61
62
63
64 public VorbisAudioFileWriter()
65 {
66 super(Arrays.asList(FILE_TYPES),
67 Arrays.asList(AUDIO_FORMATS));
68 if (TDebug.TraceAudioFileWriter) { TDebug.out("VorbisAudioFileWriter.<init>(): begin"); }
69 if (TDebug.TraceAudioFileWriter) { TDebug.out("VorbisAudioFileWriter.<init>(): end"); }
70 }
71}
72
73
74
75/*** VorbisAudioFileWriter.java ***/
diff --git a/songdbj/org/tritonus/file/pvorbis/package.html b/songdbj/org/tritonus/file/pvorbis/package.html
new file mode 100644
index 0000000000..a5238aaca0
--- /dev/null
+++ b/songdbj/org/tritonus/file/pvorbis/package.html
@@ -0,0 +1,12 @@
1<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
2<html>
3 <head>
4 </head>
5
6 <body>
7 <p>Ogg vorbis audio file reader and writer based on the pure java libraries.
8 The classes provided here .</p>
9
10 @see org.tritonus.lowlevel.ogg
11 </body>
12</html>
diff --git a/songdbj/org/tritonus/file/vorbis/VorbisAudioFileReader.java b/songdbj/org/tritonus/file/vorbis/VorbisAudioFileReader.java
new file mode 100644
index 0000000000..f8b34d5411
--- /dev/null
+++ b/songdbj/org/tritonus/file/vorbis/VorbisAudioFileReader.java
@@ -0,0 +1,302 @@
1/*
2 * VorbisAudioFileReader.java
3 *
4 * This file is part of Tritonus: http://www.tritonus.org/
5 */
6
7/*
8 * Copyright (c) 2001 by Matthias Pfisterer
9 *
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU Library General Public License as published
12 * by the Free Software Foundation; either version 2 of the License, or
13 * (at your option) any later version.
14 *
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU Library General Public License for more details.
19 *
20 * You should have received a copy of the GNU Library General Public
21 * License along with this program; if not, write to the Free Software
22 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
23 */
24
25/*
26|<--- this code is formatted to fit into 80 columns --->|
27*/
28
29package org.tritonus.sampled.file.vorbis;
30
31import java.io.InputStream;
32import java.io.IOException;
33
34import javax.sound.sampled.AudioSystem;
35import javax.sound.sampled.AudioFormat;
36import javax.sound.sampled.AudioFileFormat;
37import javax.sound.sampled.UnsupportedAudioFileException;
38
39import org.tritonus.share.TDebug;
40import org.tritonus.share.sampled.file.TAudioFileFormat;
41import org.tritonus.share.sampled.file.TAudioFileReader;
42
43import org.tritonus.lowlevel.ogg.Buffer;
44import org.tritonus.lowlevel.ogg.Page;
45import org.tritonus.lowlevel.ogg.Packet;
46import org.tritonus.lowlevel.ogg.SyncState;
47import org.tritonus.lowlevel.ogg.StreamState;
48
49
50
51/**
52 * @author Matthias Pfisterer
53 */
54public class VorbisAudioFileReader
55extends TAudioFileReader
56{
57 private static final int INITAL_READ_LENGTH = 4096;
58 private static final int MARK_LIMIT = INITAL_READ_LENGTH + 1;
59
60
61
62 public VorbisAudioFileReader()
63 {
64 super(MARK_LIMIT, true);
65 }
66
67
68
69 protected AudioFileFormat getAudioFileFormat(InputStream inputStream,
70 long lFileSizeInBytes)
71 throws UnsupportedAudioFileException, IOException
72 {
73 if (TDebug.TraceAudioFileReader) { TDebug.out(">VorbisAudioFileReader.getAudioFileFormat(): begin"); }
74 SyncState oggSyncState = new SyncState();
75 StreamState oggStreamState = new StreamState();
76 Page oggPage = new Page();
77 Packet oggPacket = new Packet();
78
79 int bytes = 0;
80
81 // Decode setup
82
83 oggSyncState.init(); // Now we can read pages
84
85 // grab some data at the head of the stream. We want the first page
86 // (which is guaranteed to be small and only contain the Vorbis
87 // stream initial header) We need the first page to get the stream
88 // serialno.
89
90 // submit a 4k block to libvorbis' Ogg layer
91 byte[] abBuffer = new byte[INITAL_READ_LENGTH];
92 bytes = inputStream.read(abBuffer);
93 if (TDebug.TraceAudioFileReader) { TDebug.out("read bytes from input stream: " + bytes); }
94 int nResult = oggSyncState.write(abBuffer, bytes);
95 if (TDebug.TraceAudioFileReader) { TDebug.out("SyncState.write() returned " + nResult); }
96
97 // Get the first page.
98 if (oggSyncState.pageOut(oggPage) != 1)
99 {
100 // have we simply run out of data? If so, we're done.
101 if (bytes < INITAL_READ_LENGTH)
102 {
103 if (TDebug.TraceAudioFileReader) { TDebug.out("stream ended prematurely"); }
104 if (TDebug.TraceAudioFileReader) { TDebug.out("<VorbisAudioFileReader.getAudioFileFormat(): throwing exception"); }
105 // IDEA: throw EOFException?
106 oggSyncState.free();
107 oggStreamState.free();
108 oggPage.free();
109 oggPacket.free();
110 throw new UnsupportedAudioFileException("not a Vorbis stream: ended prematurely");
111 }
112 if (TDebug.TraceAudioFileReader) { TDebug.out("not in Ogg bitstream format"); }
113 if (TDebug.TraceAudioFileReader) { TDebug.out("<VorbisAudioFileReader.getAudioFileFormat(): throwing exception"); }
114 oggSyncState.free();
115 oggStreamState.free();
116 oggPage.free();
117 oggPacket.free();
118 throw new UnsupportedAudioFileException("not a Vorbis stream: not in Ogg bitstream format");
119 }
120
121 // Get the serial number and set up the rest of decode.
122 // serialno first; use it to set up a logical stream
123 int nSerialNo = oggPage.getSerialNo();
124 TDebug.out("serial no.: " + nSerialNo);
125 oggStreamState.init(nSerialNo);
126
127 // extract the initial header from the first page and verify that the
128 // Ogg bitstream is in fact Vorbis data
129
130 // I handle the initial header first instead of just having the code
131 // read all three Vorbis headers at once because reading the initial
132 // header is an easy way to identify a Vorbis bitstream and it's
133 // useful to see that functionality seperated out.
134
135 if (oggStreamState.pageIn(oggPage) < 0)
136 {
137 if (TDebug.TraceAudioFileReader) { TDebug.out("can't read first page of Ogg bitstream data"); }
138 if (TDebug.TraceAudioFileReader) { TDebug.out("<VorbisAudioFileReader.getAudioFileFormat(): throwing exception"); }
139 // error; stream version mismatch perhaps
140 oggSyncState.free();
141 oggStreamState.free();
142 oggPage.free();
143 oggPacket.free();
144 throw new UnsupportedAudioFileException("not a Vorbis stream: can't read first page of Ogg bitstream data");
145 }
146
147 if (oggStreamState.packetOut(oggPacket) != 1)
148 {
149 if (TDebug.TraceAudioFileReader) { TDebug.out("can't read initial header packet"); }
150 if (TDebug.TraceAudioFileReader) { TDebug.out("<VorbisAudioFileReader.getAudioFileFormat(): throwing exception"); }
151 // no page? must not be vorbis
152 oggSyncState.free();
153 oggStreamState.free();
154 oggPage.free();
155 oggPacket.free();
156 throw new UnsupportedAudioFileException("not a Vorbis stream: can't read initial header packet");
157 }
158
159 byte[] abData = oggPacket.getData();
160 if (TDebug.TraceAudioFileReader)
161 {
162 String strData = "";
163 for (int i = 0; i < abData.length; i++)
164 {
165 strData += " " + abData[i];
166 }
167 TDebug.out("packet data: " + strData);
168 }
169
170 int nPacketType = abData[0];
171 if (TDebug.TraceAudioFileReader) { TDebug.out("packet type: " + nPacketType); }
172 if (nPacketType != 1)
173 {
174 if (TDebug.TraceAudioFileReader) { TDebug.out("first packet is not the identification header"); }
175 if (TDebug.TraceAudioFileReader) { TDebug.out("<VorbisAudioFileReader.getAudioFileFormat(): throwing exception"); }
176 oggSyncState.free();
177 oggStreamState.free();
178 oggPage.free();
179 oggPacket.free();
180 throw new UnsupportedAudioFileException("not a Vorbis stream: first packet is not the identification header");
181 }
182 if(abData[1] != 'v' ||
183 abData[2] != 'o' ||
184 abData[3] != 'r' ||
185 abData[4] != 'b' ||
186 abData[5] != 'i' ||
187 abData[6] != 's')
188 {
189 if (TDebug.TraceAudioFileReader) { TDebug.out("not a vorbis header packet"); }
190 if (TDebug.TraceAudioFileReader) { TDebug.out("<VorbisAudioFileReader.getAudioFileFormat(): throwing exception"); }
191 oggSyncState.free();
192 oggStreamState.free();
193 oggPage.free();
194 oggPacket.free();
195 throw new UnsupportedAudioFileException("not a Vorbis stream: not a vorbis header packet");
196 }
197 if (! oggPacket.isBos())
198 {
199 if (TDebug.TraceAudioFileReader) { TDebug.out("initial packet not marked as beginning of stream"); }
200 if (TDebug.TraceAudioFileReader) { TDebug.out("<VorbisAudioFileReader.getAudioFileFormat(): throwing exception"); }
201 oggSyncState.free();
202 oggStreamState.free();
203 oggPage.free();
204 oggPacket.free();
205 throw new UnsupportedAudioFileException("not a Vorbis stream: initial packet not marked as beginning of stream");
206 }
207 int nVersion = (abData[7] & 0xFF) + 256 * (abData[8] & 0xFF) + 65536 * (abData[9] & 0xFF) + 16777216 * (abData[10] & 0xFF);
208 if (TDebug.TraceAudioFileReader) { TDebug.out("version: " + nVersion); }
209 if (nVersion != 0)
210 {
211 if (TDebug.TraceAudioFileReader) { TDebug.out("wrong vorbis version"); }
212 if (TDebug.TraceAudioFileReader) { TDebug.out("<VorbisAudioFileReader.getAudioFileFormat(): throwing exception"); }
213 oggSyncState.free();
214 oggStreamState.free();
215 oggPage.free();
216 oggPacket.free();
217 throw new UnsupportedAudioFileException("not a Vorbis stream: wrong vorbis version");
218 }
219 int nChannels = (abData[11] & 0xFF);
220 float fSampleRate = (abData[12] & 0xFF) + 256 * (abData[13] & 0xFF) + 65536 * (abData[14] & 0xFF) + 16777216 * (abData[15] & 0xFF);
221 if (TDebug.TraceAudioFileReader) { TDebug.out("channels: " + nChannels); }
222 if (TDebug.TraceAudioFileReader) { TDebug.out("rate: " + fSampleRate); }
223
224 // These are only used for error checking.
225 int bitrate_upper = abData[16] + 256 * abData[17] + 65536 * abData[18] + 16777216 * abData[19];
226 int bitrate_nominal = abData[20] + 256 * abData[21] + 65536 * abData[22] + 16777216 * abData[23];
227 int bitrate_lower = abData[24] + 256 * abData[25] + 65536 * abData[26] + 16777216 * abData[27];
228
229 int[] blocksizes = new int[2];
230 blocksizes[0] = 1 << (abData[28] & 0xF);
231 blocksizes[1] = 1 << ((abData[28] >>> 4) & 0xF);
232 if (TDebug.TraceAudioFileReader) { TDebug.out("blocksizes[0]: " + blocksizes[0]); }
233 if (TDebug.TraceAudioFileReader) { TDebug.out("blocksizes[1]: " + blocksizes[1]); }
234
235 if (fSampleRate < 1.0F ||
236 nChannels < 1 ||
237 blocksizes[0] < 8||
238 blocksizes[1] < blocksizes[0] ||
239 (abData[29] & 0x1) != 1)
240 {
241 if (TDebug.TraceAudioFileReader) { TDebug.out("illegal values in initial header"); }
242 if (TDebug.TraceAudioFileReader) { TDebug.out("<VorbisAudioFileReader.getAudioFileFormat(): throwing exception"); }
243 oggSyncState.free();
244 oggStreamState.free();
245 oggPage.free();
246 oggPacket.free();
247 throw new UnsupportedAudioFileException("not a Vorbis stream: illegal values in initial header");
248 }
249
250 oggSyncState.free();
251 oggStreamState.free();
252 oggPage.free();
253 oggPacket.free();
254
255 /*
256 If the file size is known, we derive the number of frames
257 ('frame size') from it.
258 If the values don't fit into integers, we leave them at
259 NOT_SPECIFIED. 'Unknown' is considered less incorrect than
260 a wrong value.
261 */
262 // [fb] not specifying it causes Sun's Wave file writer to write rubbish
263 int nByteSize = AudioSystem.NOT_SPECIFIED;
264 if (lFileSizeInBytes != AudioSystem.NOT_SPECIFIED
265 && lFileSizeInBytes <= Integer.MAX_VALUE)
266 {
267 nByteSize = (int) lFileSizeInBytes;
268 }
269 int nFrameSize = AudioSystem.NOT_SPECIFIED;
270 /* Can we calculate a useful size?
271 Peeking into ogginfo gives the insight that the only
272 way seems to be reading through the file. This is
273 something we do not want, at least not by default.
274 */
275 // nFrameSize = (int) (lFileSizeInBytes / ...;
276
277 AudioFormat format = new AudioFormat(
278 new AudioFormat.Encoding("VORBIS"),
279 fSampleRate,
280 AudioSystem.NOT_SPECIFIED,
281 nChannels,
282 AudioSystem.NOT_SPECIFIED,
283 AudioSystem.NOT_SPECIFIED,
284 true); // this value is chosen arbitrarily
285 if (TDebug.TraceAudioFileReader) { TDebug.out("AudioFormat: " + format); }
286 AudioFileFormat.Type type = new AudioFileFormat.Type("Ogg","ogg");
287 AudioFileFormat audioFileFormat =
288 new TAudioFileFormat(
289 type,
290 format,
291 nFrameSize,
292 nByteSize);
293 if (TDebug.TraceAudioFileReader) { TDebug.out("AudioFileFormat: " + audioFileFormat); }
294 if (TDebug.TraceAudioFileReader) { TDebug.out("<VorbisAudioFileReader.getAudioFileFormat(): end"); }
295 return audioFileFormat;
296 }
297}
298
299
300
301/*** VorbisAudioFileReader.java ***/
302
diff --git a/songdbj/org/tritonus/file/vorbis/VorbisAudioFileWriter.java b/songdbj/org/tritonus/file/vorbis/VorbisAudioFileWriter.java
new file mode 100644
index 0000000000..ee7e310e48
--- /dev/null
+++ b/songdbj/org/tritonus/file/vorbis/VorbisAudioFileWriter.java
@@ -0,0 +1,75 @@
1/*
2 * VorbisAudioFileWriter.java
3 *
4 * This file is part of Tritonus: http://www.tritonus.org/
5 */
6
7/*
8 * Copyright (c) 2000 by Florian Bomers <http://www.bomers.de>
9 * Copyright (c) 2000 by Matthias Pfisterer
10 *
11 * This program is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU Library General Public License as published
13 * by the Free Software Foundation; either version 2 of the License, or
14 * (at your option) any later version.
15 *
16 * This program is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU Library General Public License for more details.
20 *
21 * You should have received a copy of the GNU Library General Public
22 * License along with this program; if not, write to the Free Software
23 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
24 */
25
26/*
27|<--- this code is formatted to fit into 80 columns --->|
28*/
29
30package org.tritonus.sampled.file.vorbis;
31
32import java.util.Arrays;
33
34import javax.sound.sampled.AudioFileFormat;
35import javax.sound.sampled.AudioFormat;
36import javax.sound.sampled.AudioSystem;
37
38import org.tritonus.share.TDebug;
39import org.tritonus.share.sampled.file.THeaderlessAudioFileWriter;
40
41
42
43/** Class for writing Vorbis streams
44 *
45 * @author Florian Bomers
46 * @author Matthias Pfisterer
47 */
48public class VorbisAudioFileWriter
49extends THeaderlessAudioFileWriter
50{
51 private static final AudioFileFormat.Type[] FILE_TYPES =
52 {
53 new AudioFileFormat.Type("Vorbis", "ogg")
54 };
55
56 private static final AudioFormat[] AUDIO_FORMATS =
57 {
58 new AudioFormat(new AudioFormat.Encoding("VORBIS"), ALL, ALL, ALL, ALL, ALL, false),
59 new AudioFormat(new AudioFormat.Encoding("VORBIS"), ALL, ALL, ALL, ALL, ALL, true),
60 };
61
62
63
64 public VorbisAudioFileWriter()
65 {
66 super(Arrays.asList(FILE_TYPES),
67 Arrays.asList(AUDIO_FORMATS));
68 if (TDebug.TraceAudioFileWriter) { TDebug.out("VorbisAudioFileWriter.<init>(): begin"); }
69 if (TDebug.TraceAudioFileWriter) { TDebug.out("VorbisAudioFileWriter.<init>(): end"); }
70 }
71}
72
73
74
75/*** VorbisAudioFileWriter.java ***/
diff --git a/songdbj/org/tritonus/file/vorbis/package.html b/songdbj/org/tritonus/file/vorbis/package.html
new file mode 100644
index 0000000000..5d6c328b7d
--- /dev/null
+++ b/songdbj/org/tritonus/file/vorbis/package.html
@@ -0,0 +1,12 @@
1<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
2<html>
3 <head>
4 </head>
5
6 <body>
7 <p>Ogg vorbis audio file reader and writer based on native libraries.
8 The classes provided here .</p>
9
10 @see org.tritonus.lowlevel.ogg
11 </body>
12</html>