summaryrefslogtreecommitdiff
path: root/songdbj/org/tritonus/share/sampled/file/TAudioFileWriter.java
diff options
context:
space:
mode:
Diffstat (limited to 'songdbj/org/tritonus/share/sampled/file/TAudioFileWriter.java')
-rw-r--r--songdbj/org/tritonus/share/sampled/file/TAudioFileWriter.java484
1 files changed, 0 insertions, 484 deletions
diff --git a/songdbj/org/tritonus/share/sampled/file/TAudioFileWriter.java b/songdbj/org/tritonus/share/sampled/file/TAudioFileWriter.java
deleted file mode 100644
index d9d6ee86ec..0000000000
--- a/songdbj/org/tritonus/share/sampled/file/TAudioFileWriter.java
+++ /dev/null
@@ -1,484 +0,0 @@
1/*
2 * TAudioFileWriter.java
3 *
4 * This file is part of Tritonus: http://www.tritonus.org/
5 */
6
7/*
8 * Copyright (c) 1999, 2000 by Matthias Pfisterer
9 * Copyright (c) 1999, 2000 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/*
28|<--- this code is formatted to fit into 80 columns --->|
29*/
30
31package org.tritonus.share.sampled.file;
32
33import java.io.File;
34import java.io.FileOutputStream;
35import java.io.InputStream;
36import java.io.IOException;
37import java.io.OutputStream;
38import java.util.Collection;
39import java.util.Iterator;
40
41import javax.sound.sampled.AudioFormat;
42import javax.sound.sampled.AudioFileFormat;
43import javax.sound.sampled.AudioInputStream;
44import javax.sound.sampled.AudioSystem;
45import javax.sound.sampled.spi.AudioFileWriter;
46
47import org.tritonus.share.TDebug;
48import org.tritonus.share.sampled.AudioFormats;
49import org.tritonus.share.sampled.AudioUtils;
50import org.tritonus.share.sampled.TConversionTool;
51import org.tritonus.share.ArraySet;
52
53/**
54 * Common base class for implementing classes of AudioFileWriter.
55 * <p>It provides often-used functionality and the new architecture using
56 * an AudioOutputStream.
57 * <p>There should be only one set of audio formats supported by any given
58 * class of TAudioFileWriter. This class assumes implicitely that all
59 * supported file types have a common set of audio formats they can handle.
60 *
61 * @author Matthias Pfisterer
62 * @author Florian Bomers
63 */
64
65public abstract class TAudioFileWriter
66extends AudioFileWriter
67{
68 protected static final int ALL = AudioSystem.NOT_SPECIFIED;
69
70 public static AudioFormat.Encoding PCM_SIGNED=new AudioFormat.Encoding("PCM_SIGNED");
71 public static AudioFormat.Encoding PCM_UNSIGNED=new AudioFormat.Encoding("PCM_UNSIGNED");
72
73 /** Buffer length for the loop in the write() method.
74 * This is in bytes. Perhaps it should be in frames to give an
75 * equal amount of latency.
76 */
77 private static final int BUFFER_LENGTH = 16384;
78
79 // only needed for Collection.toArray()
80 protected static final AudioFileFormat.Type[] NULL_TYPE_ARRAY = new AudioFileFormat.Type[0];
81
82
83 /** The audio file types (AudioFileFormat.Type) that can be
84 * handled by the AudioFileWriter.
85 */
86 private Collection<AudioFileFormat.Type> m_audioFileTypes;
87
88
89
90 /** The AudioFormats that can be handled by the
91 * AudioFileWriter.
92 */
93 // IDEA: implement a special collection that uses matches() to test whether an element is already in
94 private Collection<AudioFormat> m_audioFormats;
95
96
97 /**
98 * Inheriting classes should call this constructor
99 * in order to make use of the functionality of TAudioFileWriter.
100 */
101 protected TAudioFileWriter(Collection<AudioFileFormat.Type> fileTypes,
102 Collection<AudioFormat> audioFormats)
103 {
104 if (TDebug.TraceAudioFileWriter) { TDebug.out("TAudioFileWriter.<init>(): begin"); }
105 m_audioFileTypes = fileTypes;
106 m_audioFormats = audioFormats;
107 if (TDebug.TraceAudioFileWriter) { TDebug.out("TAudioFileWriter.<init>(): end"); }
108 }
109
110 // implementing the interface
111 public AudioFileFormat.Type[] getAudioFileTypes()
112 {
113 return m_audioFileTypes.toArray(NULL_TYPE_ARRAY);
114 }
115
116
117 // implementing the interface
118 public boolean isFileTypeSupported(AudioFileFormat.Type fileType)
119 {
120 return m_audioFileTypes.contains(fileType);
121 }
122
123
124
125 // implementing the interface
126 public AudioFileFormat.Type[] getAudioFileTypes(
127 AudioInputStream audioInputStream)
128 {
129 //$$fb 2000-08-16: rewrote this method. We need to check for *each*
130 // file type, whether the format is supported !
131 AudioFormat format = audioInputStream.getFormat();
132 ArraySet<AudioFileFormat.Type> res=new ArraySet<AudioFileFormat.Type>();
133 Iterator<AudioFileFormat.Type> it=m_audioFileTypes.iterator();
134 while (it.hasNext()) {
135 AudioFileFormat.Type thisType = it.next();
136 if (isAudioFormatSupportedImpl(format, thisType)) {
137 res.add(thisType);
138 }
139 }
140 return res.toArray(NULL_TYPE_ARRAY);
141 }
142
143
144
145 // implementing the interface
146 public boolean isFileTypeSupported(AudioFileFormat.Type fileType, AudioInputStream audioInputStream)
147 {
148 // $$fb 2000-08-16: finally this method works reliably !
149 return isFileTypeSupported(fileType)
150 && (isAudioFormatSupportedImpl(audioInputStream.getFormat(), fileType)
151 || findConvertableFormat(audioInputStream.getFormat(), fileType)!=null);
152 // we may soft it up by including the possibility of endian/sign
153 // changing for PCM formats.
154 // I prefer to return false if the format is not exactly supported
155 // but still exectute the write, if only sign/endian changing is necessary.
156 }
157
158
159
160 // implementing the interface
161 public int write(AudioInputStream audioInputStream,
162 AudioFileFormat.Type fileType,
163 File file)
164 throws IOException
165 {
166 if (TDebug.TraceAudioFileWriter)
167 {
168 TDebug.out(">TAudioFileWriter.write(.., File): called");
169 TDebug.out("class: "+getClass().getName());
170 }
171 //$$fb added this check
172 if (!isFileTypeSupported(fileType)) {
173 if (TDebug.TraceAudioFileWriter)
174 {
175 TDebug.out("< file type is not supported");
176 }
177 throw new IllegalArgumentException("file type is not supported.");
178 }
179
180 AudioFormat inputFormat = audioInputStream.getFormat();
181 if (TDebug.TraceAudioFileWriter) { TDebug.out("input format: " + inputFormat); }
182 AudioFormat outputFormat = null;
183 boolean bNeedsConversion = false;
184 if (isAudioFormatSupportedImpl(inputFormat, fileType))
185 {
186 if (TDebug.TraceAudioFileWriter) { TDebug.out("input format is supported directely"); }
187 outputFormat = inputFormat;
188 bNeedsConversion = false;
189 }
190 else
191 {
192 if (TDebug.TraceAudioFileWriter) { TDebug.out("input format is not supported directely; trying to find a convertable format"); }
193 outputFormat = findConvertableFormat(inputFormat, fileType);
194 if (outputFormat != null)
195 {
196 bNeedsConversion = true;
197 // $$fb 2000-08-16 made consistent with new conversion trials
198 // if 8 bit and only endianness changed, don't convert !
199 if (outputFormat.getSampleSizeInBits()==8
200 && outputFormat.getEncoding().equals(inputFormat.getEncoding())) {
201 bNeedsConversion = false;
202 }
203 }
204 else
205 {
206 if (TDebug.TraceAudioFileWriter) { TDebug.out("< input format is not supported and not convertable."); }
207 throw new IllegalArgumentException("format not supported and not convertable");
208 }
209 }
210 long lLengthInBytes = AudioUtils.getLengthInBytes(audioInputStream);
211 TDataOutputStream dataOutputStream = new TSeekableDataOutputStream(file);
212 AudioOutputStream audioOutputStream =
213 getAudioOutputStream(
214 outputFormat,
215 lLengthInBytes,
216 fileType,
217 dataOutputStream);
218 int written=writeImpl(audioInputStream,
219 audioOutputStream,
220 bNeedsConversion);
221 if (TDebug.TraceAudioFileWriter)
222 {
223 TDebug.out("< wrote "+written+" bytes.");
224 }
225 return written;
226 }
227
228
229
230 // implementing the interface
231 public int write(AudioInputStream audioInputStream,
232 AudioFileFormat.Type fileType,
233 OutputStream outputStream)
234 throws IOException
235 {
236 //$$fb added this check
237 if (!isFileTypeSupported(fileType)) {
238 throw new IllegalArgumentException("file type is not supported.");
239 }
240 if (TDebug.TraceAudioFileWriter)
241 {
242 TDebug.out(">TAudioFileWriter.write(.., OutputStream): called");
243 TDebug.out("class: "+getClass().getName());
244 }
245 AudioFormat inputFormat = audioInputStream.getFormat();
246 if (TDebug.TraceAudioFileWriter) { TDebug.out("input format: " + inputFormat); }
247 AudioFormat outputFormat = null;
248 boolean bNeedsConversion = false;
249 if (isAudioFormatSupportedImpl(inputFormat, fileType))
250 {
251 if (TDebug.TraceAudioFileWriter) { TDebug.out("input format is supported directely"); }
252 outputFormat = inputFormat;
253 bNeedsConversion = false;
254 }
255 else
256 {
257 if (TDebug.TraceAudioFileWriter) { TDebug.out("input format is not supported directely; trying to find a convertable format"); }
258 outputFormat = findConvertableFormat(inputFormat, fileType);
259 if (outputFormat != null)
260 {
261 bNeedsConversion = true;
262 // $$fb 2000-08-16 made consistent with new conversion trials
263 // if 8 bit and only endianness changed, don't convert !
264 if (outputFormat.getSampleSizeInBits()==8
265 && outputFormat.getEncoding().equals(inputFormat.getEncoding())) {
266 bNeedsConversion = false;
267 }
268 }
269 else
270 {
271 if (TDebug.TraceAudioFileWriter) { TDebug.out("< format is not supported"); }
272 throw new IllegalArgumentException("format not supported and not convertable");
273 }
274 }
275 long lLengthInBytes = AudioUtils.getLengthInBytes(audioInputStream);
276 TDataOutputStream dataOutputStream = new TNonSeekableDataOutputStream(outputStream);
277 AudioOutputStream audioOutputStream =
278 getAudioOutputStream(
279 outputFormat,
280 lLengthInBytes,
281 fileType,
282 dataOutputStream);
283 int written=writeImpl(audioInputStream,
284 audioOutputStream,
285 bNeedsConversion);
286 if (TDebug.TraceAudioFileWriter) { TDebug.out("< wrote "+written+" bytes."); }
287 return written;
288 }
289
290
291
292 protected int writeImpl(
293 AudioInputStream audioInputStream,
294 AudioOutputStream audioOutputStream,
295 boolean bNeedsConversion)
296 throws IOException
297 {
298 if (TDebug.TraceAudioFileWriter)
299 {
300 TDebug.out(">TAudioFileWriter.writeImpl(): called");
301 TDebug.out("class: "+getClass().getName());
302 }
303 int nTotalWritten = 0;
304 AudioFormat inputFormat = audioInputStream.getFormat();
305 AudioFormat outputFormat = audioOutputStream.getFormat();
306
307 // TODO: handle case when frame size is unknown ?
308 int nBytesPerSample = outputFormat.getFrameSize() / outputFormat.getChannels();
309
310 //$$fb 2000-07-18: BUFFER_LENGTH must be a multiple of frame size...
311 int nBufferSize=((int)BUFFER_LENGTH/outputFormat.getFrameSize())*outputFormat.getFrameSize();
312 byte[] abBuffer = new byte[nBufferSize];
313 while (true)
314 {
315 if (TDebug.TraceAudioFileWriter) { TDebug.out("trying to read (bytes): " + abBuffer.length); }
316 int nBytesRead = audioInputStream.read(abBuffer);
317 if (TDebug.TraceAudioFileWriter) { TDebug.out("read (bytes): " + nBytesRead); }
318 if (nBytesRead == -1)
319 {
320 break;
321 }
322 if (bNeedsConversion)
323 {
324 TConversionTool.changeOrderOrSign(abBuffer, 0,
325 nBytesRead, nBytesPerSample);
326 }
327 int nWritten = audioOutputStream.write(abBuffer, 0, nBytesRead);
328 nTotalWritten += nWritten;
329 }
330 if (TDebug.TraceAudioFileWriter) { TDebug.out("<TAudioFileWriter.writeImpl(): after main loop. Wrote "+nTotalWritten+" bytes"); }
331 audioOutputStream.close();
332 // TODO: get bytes written for header etc. from AudioOutputStrem and add to nTotalWrittenBytes
333 return nTotalWritten;
334 }
335
336
337 /** Returns the AudioFormat that can be handled for the given file type.
338 * In this simple implementation, all handled AudioFormats are
339 * returned (i.e. the fileType argument is ignored). If the
340 * handled AudioFormats depend on the file type, this method
341 * has to be overwritten by subclasses.
342 */
343 protected Iterator<AudioFormat> getSupportedAudioFormats(AudioFileFormat.Type fileType)
344 {
345 return m_audioFormats.iterator();
346 }
347
348
349 /** Checks whether the passed <b>AudioFormat</b> can be handled.
350 * In this simple implementation, it is only checked if the
351 * passed AudioFormat matches one of the generally handled
352 * formats (i.e. the fileType argument is ignored). If the
353 * handled AudioFormats depend on the file type, this method
354 * or getSupportedAudioFormats() (on which this method relies)
355 * has to be overwritten by subclasses.
356 * <p>
357 * This is the central method for checking if a FORMAT is supported.
358 * Inheriting classes can overwrite this for performance
359 * or to exclude/include special type/format combinations.
360 * <p>
361 * This method is only called when the <code>fileType</code>
362 * is in the list of supported file types ! Overriding
363 * classes <b>need not</b> check this.
364 */
365 //$$fb 2000-08-16 changed name, changed documentation. Semantics !
366 protected boolean isAudioFormatSupportedImpl(
367 AudioFormat audioFormat,
368 AudioFileFormat.Type fileType)
369 {
370 if (TDebug.TraceAudioFileWriter)
371 {
372 TDebug.out("> TAudioFileWriter.isAudioFormatSupportedImpl(): format to test: " + audioFormat);
373 TDebug.out("class: "+getClass().getName());
374 }
375 Iterator audioFormats = getSupportedAudioFormats(fileType);
376 while (audioFormats.hasNext())
377 {
378 AudioFormat handledFormat = (AudioFormat) audioFormats.next();
379 if (TDebug.TraceAudioFileWriter) { TDebug.out("matching against format : " + handledFormat); }
380 if (AudioFormats.matches(handledFormat, audioFormat))
381 {
382 if (TDebug.TraceAudioFileWriter) { TDebug.out("<...succeeded."); }
383 return true;
384 }
385 }
386 if (TDebug.TraceAudioFileWriter) { TDebug.out("< ... failed"); }
387 return false;
388 }
389
390
391
392 protected abstract AudioOutputStream getAudioOutputStream(
393 AudioFormat audioFormat,
394 long lLengthInBytes,
395 AudioFileFormat.Type fileType,
396 TDataOutputStream dataOutputStream)
397 throws IOException;
398
399 private AudioFormat findConvertableFormat(
400 AudioFormat inputFormat,
401 AudioFileFormat.Type fileType)
402 {
403 if (TDebug.TraceAudioFileWriter) { TDebug.out("TAudioFileWriter.findConvertableFormat(): input format: " + inputFormat); }
404 if (!isFileTypeSupported(fileType)) {
405 if (TDebug.TraceAudioFileWriter) { TDebug.out("< input file type is not supported."); }
406 return null;
407 }
408 AudioFormat.Encoding inputEncoding = inputFormat.getEncoding();
409 if ((inputEncoding.equals(PCM_SIGNED) || inputEncoding.equals(PCM_UNSIGNED))
410 && inputFormat.getSampleSizeInBits() == 8)
411 {
412 AudioFormat outputFormat = convertFormat(inputFormat, true, false);
413 if (TDebug.TraceAudioFileWriter) { TDebug.out("trying output format: " + outputFormat); }
414 if (isAudioFormatSupportedImpl(outputFormat, fileType))
415 {
416 if (TDebug.TraceAudioFileWriter) { TDebug.out("< ... succeeded"); }
417 return outputFormat;
418 }
419 //$$fb 2000-08-16: added trial of other endianness for 8bit. We try harder !
420 outputFormat = convertFormat(inputFormat, false, true);
421 if (TDebug.TraceAudioFileWriter) { TDebug.out("trying output format: " + outputFormat); }
422 if (isAudioFormatSupportedImpl(outputFormat, fileType))
423 {
424 if (TDebug.TraceAudioFileWriter) { TDebug.out("< ... succeeded"); }
425 return outputFormat;
426 }
427 outputFormat = convertFormat(inputFormat, true, true);
428 if (TDebug.TraceAudioFileWriter) { TDebug.out("trying output format: " + outputFormat); }
429 if (isAudioFormatSupportedImpl(outputFormat, fileType))
430 {
431 if (TDebug.TraceAudioFileWriter) { TDebug.out("< ... succeeded"); }
432 return outputFormat;
433 }
434 if (TDebug.TraceAudioFileWriter) { TDebug.out("< ... failed"); }
435 return null;
436 }
437 else if (inputEncoding.equals(PCM_SIGNED) &&
438 (inputFormat.getSampleSizeInBits() == 16 ||
439 inputFormat.getSampleSizeInBits() == 24 ||
440 inputFormat.getSampleSizeInBits() == 32) )
441 {
442 // TODO: possible to allow all sample sized > 8 bit?
443 // $$ fb: don't think that this is necessary. Well, let's talk about that in 5 years :)
444 AudioFormat outputFormat = convertFormat(inputFormat, false, true);
445 if (TDebug.TraceAudioFileWriter) { TDebug.out("trying output format: " + outputFormat); }
446 if (isAudioFormatSupportedImpl(outputFormat, fileType))
447 {
448 if (TDebug.TraceAudioFileWriter) { TDebug.out("< ... succeeded"); }
449 return outputFormat;
450 }
451 else
452 {
453 if (TDebug.TraceAudioFileWriter) { TDebug.out("< ... failed"); }
454 return null;
455 }
456 }
457 else
458 {
459 if (TDebug.TraceAudioFileWriter) { TDebug.out("< ... failed"); }
460 return null;
461 }
462 }
463
464 // $$fb 2000-08-16: added convenience method
465 private AudioFormat convertFormat(AudioFormat format, boolean changeSign, boolean changeEndian) {
466 AudioFormat.Encoding enc=PCM_SIGNED;
467 if (format.getEncoding().equals(PCM_UNSIGNED)!=changeSign) {
468 enc=PCM_UNSIGNED;
469 }
470 return new AudioFormat(
471 enc,
472 format.getSampleRate(),
473 format.getSampleSizeInBits(),
474 format.getChannels(),
475 format.getFrameSize(),
476 format.getFrameRate(),
477 format.isBigEndian() ^ changeEndian);
478 }
479
480}
481
482
483
484/*** TAudioFileWriter.java ***/