diff options
Diffstat (limited to 'songdbj/org/tritonus/file/AiffAudioOutputStream.java')
-rw-r--r-- | songdbj/org/tritonus/file/AiffAudioOutputStream.java | 205 |
1 files changed, 205 insertions, 0 deletions
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 | |||
31 | package org.tritonus.sampled.file; | ||
32 | |||
33 | import java.io.IOException; | ||
34 | import javax.sound.sampled.AudioFormat; | ||
35 | import javax.sound.sampled.AudioFileFormat; | ||
36 | import javax.sound.sampled.AudioSystem; | ||
37 | import org.tritonus.share.TDebug; | ||
38 | import org.tritonus.share.sampled.file.TAudioOutputStream; | ||
39 | import org.tritonus.share.sampled.file.TDataOutputStream; | ||
40 | |||
41 | |||
42 | /** | ||
43 | * AudioOutputStream for AIFF and AIFF-C files. | ||
44 | * | ||
45 | * @author Florian Bomers | ||
46 | */ | ||
47 | public 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 ***/ | ||