summaryrefslogtreecommitdiff
path: root/songdbj/org/tritonus/file/WaveAudioOutputStream.java
diff options
context:
space:
mode:
Diffstat (limited to 'songdbj/org/tritonus/file/WaveAudioOutputStream.java')
-rw-r--r--songdbj/org/tritonus/file/WaveAudioOutputStream.java201
1 files changed, 201 insertions, 0 deletions
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 ***/