Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
PojavLauncherTeam
GitHub Repository: PojavLauncherTeam/openjdk-multiarch-jdk8u
Path: blob/aarch64-shenandoah-jdk8u272-b10/jdk/src/share/classes/com/sun/media/sound/AiffFileWriter.java
38924 views
1
/*
2
* Copyright (c) 1999, 2013, Oracle and/or its affiliates. All rights reserved.
3
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4
*
5
* This code is free software; you can redistribute it and/or modify it
6
* under the terms of the GNU General Public License version 2 only, as
7
* published by the Free Software Foundation. Oracle designates this
8
* particular file as subject to the "Classpath" exception as provided
9
* by Oracle in the LICENSE file that accompanied this code.
10
*
11
* This code is distributed in the hope that it will be useful, but WITHOUT
12
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14
* version 2 for more details (a copy is included in the LICENSE file that
15
* accompanied this code).
16
*
17
* You should have received a copy of the GNU General Public License version
18
* 2 along with this work; if not, write to the Free Software Foundation,
19
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20
*
21
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22
* or visit www.oracle.com if you need additional information or have any
23
* questions.
24
*/
25
26
package com.sun.media.sound;
27
28
import java.io.File;
29
import java.io.InputStream;
30
import java.io.OutputStream;
31
import java.io.IOException;
32
33
import java.io.BufferedOutputStream;
34
import java.io.DataOutputStream;
35
import java.io.FileOutputStream;
36
import java.io.ByteArrayInputStream;
37
import java.io.ByteArrayOutputStream;
38
import java.io.RandomAccessFile;
39
import java.io.SequenceInputStream;
40
41
import javax.sound.sampled.AudioFileFormat;
42
import javax.sound.sampled.AudioInputStream;
43
import javax.sound.sampled.AudioFormat;
44
import javax.sound.sampled.AudioSystem;
45
46
//$$fb this class is buggy. Should be replaced in future.
47
48
/**
49
* AIFF file writer.
50
*
51
* @author Jan Borgersen
52
*/
53
public final class AiffFileWriter extends SunFileWriter {
54
55
/**
56
* Constructs a new AiffFileWriter object.
57
*/
58
public AiffFileWriter() {
59
super(new AudioFileFormat.Type[]{AudioFileFormat.Type.AIFF});
60
}
61
62
63
// METHODS TO IMPLEMENT AudioFileWriter
64
65
public AudioFileFormat.Type[] getAudioFileTypes(AudioInputStream stream) {
66
67
AudioFileFormat.Type[] filetypes = new AudioFileFormat.Type[types.length];
68
System.arraycopy(types, 0, filetypes, 0, types.length);
69
70
// make sure we can write this stream
71
AudioFormat format = stream.getFormat();
72
AudioFormat.Encoding encoding = format.getEncoding();
73
74
if( (AudioFormat.Encoding.ALAW.equals(encoding)) ||
75
(AudioFormat.Encoding.ULAW.equals(encoding)) ||
76
(AudioFormat.Encoding.PCM_SIGNED.equals(encoding)) ||
77
(AudioFormat.Encoding.PCM_UNSIGNED.equals(encoding)) ) {
78
79
return filetypes;
80
}
81
82
return new AudioFileFormat.Type[0];
83
}
84
85
86
public int write(AudioInputStream stream, AudioFileFormat.Type fileType, OutputStream out) throws IOException {
87
88
//$$fb the following check must come first ! Otherwise
89
// the next frame length check may throw an IOException and
90
// interrupt iterating File Writers. (see bug 4351296)
91
92
// throws IllegalArgumentException if not supported
93
AiffFileFormat aiffFileFormat = (AiffFileFormat)getAudioFileFormat(fileType, stream);
94
95
// we must know the total data length to calculate the file length
96
if( stream.getFrameLength() == AudioSystem.NOT_SPECIFIED ) {
97
throw new IOException("stream length not specified");
98
}
99
100
int bytesWritten = writeAiffFile(stream, aiffFileFormat, out);
101
return bytesWritten;
102
}
103
104
105
public int write(AudioInputStream stream, AudioFileFormat.Type fileType, File out) throws IOException {
106
107
// throws IllegalArgumentException if not supported
108
AiffFileFormat aiffFileFormat = (AiffFileFormat)getAudioFileFormat(fileType, stream);
109
110
// first write the file without worrying about length fields
111
FileOutputStream fos = new FileOutputStream( out ); // throws IOException
112
BufferedOutputStream bos = new BufferedOutputStream( fos, bisBufferSize );
113
int bytesWritten = writeAiffFile(stream, aiffFileFormat, bos );
114
bos.close();
115
116
// now, if length fields were not specified, calculate them,
117
// open as a random access file, write the appropriate fields,
118
// close again....
119
if( aiffFileFormat.getByteLength()== AudioSystem.NOT_SPECIFIED ) {
120
121
// $$kk: 10.22.99: jan: please either implement this or throw an exception!
122
// $$fb: 2001-07-13: done. Fixes Bug 4479981
123
int ssndBlockSize = (aiffFileFormat.getFormat().getChannels() * aiffFileFormat.getFormat().getSampleSizeInBits());
124
125
int aiffLength=bytesWritten;
126
int ssndChunkSize=aiffLength-aiffFileFormat.getHeaderSize()+16;
127
long dataSize=ssndChunkSize-16;
128
int numFrames=(int) (dataSize*8/ssndBlockSize);
129
130
RandomAccessFile raf=new RandomAccessFile(out, "rw");
131
// skip FORM magic
132
raf.skipBytes(4);
133
raf.writeInt(aiffLength-8);
134
// skip aiff2 magic, fver chunk, comm magic, comm size, channel count,
135
raf.skipBytes(4+aiffFileFormat.getFverChunkSize()+4+4+2);
136
// write frame count
137
raf.writeInt(numFrames);
138
// skip sample size, samplerate, SSND magic
139
raf.skipBytes(2+10+4);
140
raf.writeInt(ssndChunkSize-8);
141
// that's all
142
raf.close();
143
}
144
145
return bytesWritten;
146
}
147
148
149
// -----------------------------------------------------------------------
150
151
/**
152
* Returns the AudioFileFormat describing the file that will be written from this AudioInputStream.
153
* Throws IllegalArgumentException if not supported.
154
*/
155
private AudioFileFormat getAudioFileFormat(AudioFileFormat.Type type, AudioInputStream stream) {
156
157
AudioFormat format = null;
158
AiffFileFormat fileFormat = null;
159
AudioFormat.Encoding encoding = AudioFormat.Encoding.PCM_SIGNED;
160
161
AudioFormat streamFormat = stream.getFormat();
162
AudioFormat.Encoding streamEncoding = streamFormat.getEncoding();
163
164
165
float sampleRate;
166
int sampleSizeInBits;
167
int channels;
168
int frameSize;
169
float frameRate;
170
int fileSize;
171
boolean convert8to16 = false;
172
173
if( !types[0].equals(type) ) {
174
throw new IllegalArgumentException("File type " + type + " not supported.");
175
}
176
177
if( (AudioFormat.Encoding.ALAW.equals(streamEncoding)) ||
178
(AudioFormat.Encoding.ULAW.equals(streamEncoding)) ) {
179
180
if( streamFormat.getSampleSizeInBits()==8 ) {
181
182
encoding = AudioFormat.Encoding.PCM_SIGNED;
183
sampleSizeInBits=16;
184
convert8to16 = true;
185
186
} else {
187
188
// can't convert non-8-bit ALAW,ULAW
189
throw new IllegalArgumentException("Encoding " + streamEncoding + " supported only for 8-bit data.");
190
}
191
} else if ( streamFormat.getSampleSizeInBits()==8 ) {
192
193
encoding = AudioFormat.Encoding.PCM_UNSIGNED;
194
sampleSizeInBits=8;
195
196
} else {
197
198
encoding = AudioFormat.Encoding.PCM_SIGNED;
199
sampleSizeInBits=streamFormat.getSampleSizeInBits();
200
}
201
202
203
format = new AudioFormat( encoding,
204
streamFormat.getSampleRate(),
205
sampleSizeInBits,
206
streamFormat.getChannels(),
207
streamFormat.getFrameSize(),
208
streamFormat.getFrameRate(),
209
true); // AIFF is big endian
210
211
212
if( stream.getFrameLength()!=AudioSystem.NOT_SPECIFIED ) {
213
if( convert8to16 ) {
214
fileSize = (int)stream.getFrameLength()*streamFormat.getFrameSize()*2 + AiffFileFormat.AIFF_HEADERSIZE;
215
} else {
216
fileSize = (int)stream.getFrameLength()*streamFormat.getFrameSize() + AiffFileFormat.AIFF_HEADERSIZE;
217
}
218
} else {
219
fileSize = AudioSystem.NOT_SPECIFIED;
220
}
221
222
fileFormat = new AiffFileFormat( AudioFileFormat.Type.AIFF,
223
fileSize,
224
format,
225
(int)stream.getFrameLength() );
226
227
return fileFormat;
228
}
229
230
231
private int writeAiffFile(InputStream in, AiffFileFormat aiffFileFormat, OutputStream out) throws IOException {
232
233
int bytesRead = 0;
234
int bytesWritten = 0;
235
InputStream fileStream = getFileStream(aiffFileFormat, in);
236
byte buffer[] = new byte[bisBufferSize];
237
int maxLength = aiffFileFormat.getByteLength();
238
239
while( (bytesRead = fileStream.read( buffer )) >= 0 ) {
240
if (maxLength>0) {
241
if( bytesRead < maxLength ) {
242
out.write( buffer, 0, (int)bytesRead );
243
bytesWritten += bytesRead;
244
maxLength -= bytesRead;
245
} else {
246
out.write( buffer, 0, (int)maxLength );
247
bytesWritten += maxLength;
248
maxLength = 0;
249
break;
250
}
251
252
} else {
253
out.write( buffer, 0, (int)bytesRead );
254
bytesWritten += bytesRead;
255
}
256
}
257
258
return bytesWritten;
259
}
260
261
private InputStream getFileStream(AiffFileFormat aiffFileFormat, InputStream audioStream) throws IOException {
262
263
// private method ... assumes aiffFileFormat is a supported file format
264
265
AudioFormat format = aiffFileFormat.getFormat();
266
AudioFormat streamFormat = null;
267
AudioFormat.Encoding encoding = null;
268
269
//$$fb a little bit nicer handling of constants
270
271
//int headerSize = 54;
272
int headerSize = aiffFileFormat.getHeaderSize();
273
274
//int fverChunkSize = 0;
275
int fverChunkSize = aiffFileFormat.getFverChunkSize();
276
//int commChunkSize = 26;
277
int commChunkSize = aiffFileFormat.getCommChunkSize();
278
int aiffLength = -1;
279
int ssndChunkSize = -1;
280
//int ssndOffset = headerSize - 16;
281
int ssndOffset = aiffFileFormat.getSsndChunkOffset();
282
short channels = (short) format.getChannels();
283
short sampleSize = (short) format.getSampleSizeInBits();
284
int ssndBlockSize = (channels * sampleSize);
285
int numFrames = aiffFileFormat.getFrameLength();
286
long dataSize = -1;
287
if( numFrames != AudioSystem.NOT_SPECIFIED) {
288
dataSize = (long) numFrames * ssndBlockSize / 8;
289
ssndChunkSize = (int)dataSize + 16;
290
aiffLength = (int)dataSize+headerSize;
291
}
292
float sampleFramesPerSecond = format.getSampleRate();
293
int compCode = AiffFileFormat.AIFC_PCM;
294
295
byte header[] = null;
296
ByteArrayInputStream headerStream = null;
297
ByteArrayOutputStream baos = null;
298
DataOutputStream dos = null;
299
SequenceInputStream aiffStream = null;
300
InputStream codedAudioStream = audioStream;
301
302
// if we need to do any format conversion, do it here....
303
304
if( audioStream instanceof AudioInputStream ) {
305
306
streamFormat = ((AudioInputStream)audioStream).getFormat();
307
encoding = streamFormat.getEncoding();
308
309
310
// $$jb: Note that AIFF samples are ALWAYS signed
311
if( (AudioFormat.Encoding.PCM_UNSIGNED.equals(encoding)) ||
312
( (AudioFormat.Encoding.PCM_SIGNED.equals(encoding)) && !streamFormat.isBigEndian() ) ) {
313
314
// plug in the transcoder to convert to PCM_SIGNED. big endian
315
codedAudioStream = AudioSystem.getAudioInputStream( new AudioFormat (
316
AudioFormat.Encoding.PCM_SIGNED,
317
streamFormat.getSampleRate(),
318
streamFormat.getSampleSizeInBits(),
319
streamFormat.getChannels(),
320
streamFormat.getFrameSize(),
321
streamFormat.getFrameRate(),
322
true ),
323
(AudioInputStream)audioStream );
324
325
} else if( (AudioFormat.Encoding.ULAW.equals(encoding)) ||
326
(AudioFormat.Encoding.ALAW.equals(encoding)) ) {
327
328
if( streamFormat.getSampleSizeInBits() != 8 ) {
329
throw new IllegalArgumentException("unsupported encoding");
330
}
331
332
//$$fb 2001-07-13: this is probably not what we want:
333
// writing PCM when ULAW/ALAW is requested. AIFC is able to write ULAW !
334
335
// plug in the transcoder to convert to PCM_SIGNED_BIG_ENDIAN
336
codedAudioStream = AudioSystem.getAudioInputStream( new AudioFormat (
337
AudioFormat.Encoding.PCM_SIGNED,
338
streamFormat.getSampleRate(),
339
streamFormat.getSampleSizeInBits() * 2,
340
streamFormat.getChannels(),
341
streamFormat.getFrameSize() * 2,
342
streamFormat.getFrameRate(),
343
true ),
344
(AudioInputStream)audioStream );
345
}
346
}
347
348
349
// Now create an AIFF stream header...
350
baos = new ByteArrayOutputStream();
351
dos = new DataOutputStream(baos);
352
353
// Write the outer FORM chunk
354
dos.writeInt(AiffFileFormat.AIFF_MAGIC);
355
dos.writeInt( (aiffLength-8) );
356
dos.writeInt(AiffFileFormat.AIFF_MAGIC2);
357
358
// Write a FVER chunk - only for AIFC
359
//dos.writeInt(FVER_MAGIC);
360
//dos.writeInt( (fverChunkSize-8) );
361
//dos.writeInt(FVER_TIMESTAMP);
362
363
// Write a COMM chunk
364
dos.writeInt(AiffFileFormat.COMM_MAGIC);
365
dos.writeInt( (commChunkSize-8) );
366
dos.writeShort(channels);
367
dos.writeInt(numFrames);
368
dos.writeShort(sampleSize);
369
write_ieee_extended(dos, sampleFramesPerSecond); // 10 bytes
370
371
//Only for AIFC
372
//dos.writeInt(compCode);
373
//dos.writeInt(compCode);
374
//dos.writeShort(0);
375
376
// Write the SSND chunk header
377
dos.writeInt(AiffFileFormat.SSND_MAGIC);
378
dos.writeInt( (ssndChunkSize-8) );
379
// ssndOffset and ssndBlockSize set to 0 upon
380
// recommendation in "Sound Manager" chapter in
381
// "Inside Macintosh Sound", pp 2-87 (from Babu)
382
dos.writeInt(0); // ssndOffset
383
dos.writeInt(0); // ssndBlockSize
384
385
// Concat this with the audioStream and return it
386
387
dos.close();
388
header = baos.toByteArray();
389
headerStream = new ByteArrayInputStream( header );
390
391
aiffStream = new SequenceInputStream(headerStream,
392
new NoCloseInputStream(codedAudioStream));
393
394
return aiffStream;
395
396
}
397
398
399
400
401
// HELPER METHODS
402
403
private static final int DOUBLE_MANTISSA_LENGTH = 52;
404
private static final int DOUBLE_EXPONENT_LENGTH = 11;
405
private static final long DOUBLE_SIGN_MASK = 0x8000000000000000L;
406
private static final long DOUBLE_EXPONENT_MASK = 0x7FF0000000000000L;
407
private static final long DOUBLE_MANTISSA_MASK = 0x000FFFFFFFFFFFFFL;
408
private static final int DOUBLE_EXPONENT_OFFSET = 1023;
409
410
private static final int EXTENDED_EXPONENT_OFFSET = 16383;
411
private static final int EXTENDED_MANTISSA_LENGTH = 63;
412
private static final int EXTENDED_EXPONENT_LENGTH = 15;
413
private static final long EXTENDED_INTEGER_MASK = 0x8000000000000000L;
414
415
/**
416
* Extended precision IEEE floating-point conversion routine.
417
* @argument DataOutputStream
418
* @argument double
419
* @exception IOException
420
*/
421
private void write_ieee_extended(DataOutputStream dos, float f) throws IOException {
422
/* The special cases NaN, Infinity and Zero are ignored, since
423
they do not represent useful sample rates anyway.
424
Denormalized number aren't handled, too. Below, there is a cast
425
from float to double. We hope that in this conversion,
426
numbers are normalized. Numbers that cannot be normalized are
427
ignored, too, as they, too, do not represent useful sample rates. */
428
long doubleBits = Double.doubleToLongBits((double) f);
429
430
long sign = (doubleBits & DOUBLE_SIGN_MASK)
431
>> (DOUBLE_EXPONENT_LENGTH + DOUBLE_MANTISSA_LENGTH);
432
long doubleExponent = (doubleBits & DOUBLE_EXPONENT_MASK)
433
>> DOUBLE_MANTISSA_LENGTH;
434
long doubleMantissa = doubleBits & DOUBLE_MANTISSA_MASK;
435
436
long extendedExponent = doubleExponent - DOUBLE_EXPONENT_OFFSET
437
+ EXTENDED_EXPONENT_OFFSET;
438
long extendedMantissa = doubleMantissa
439
<< (EXTENDED_MANTISSA_LENGTH - DOUBLE_MANTISSA_LENGTH);
440
long extendedSign = sign << EXTENDED_EXPONENT_LENGTH;
441
short extendedBits79To64 = (short) (extendedSign | extendedExponent);
442
long extendedBits63To0 = EXTENDED_INTEGER_MASK | extendedMantissa;
443
444
dos.writeShort(extendedBits79To64);
445
dos.writeLong(extendedBits63To0);
446
}
447
448
449
}
450
451