Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
PojavLauncherTeam
GitHub Repository: PojavLauncherTeam/mobile
Path: blob/master/src/java.desktop/share/classes/com/sun/media/sound/AiffFileWriter.java
41161 views
1
/*
2
* Copyright (c) 1999, 2018, 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.BufferedOutputStream;
29
import java.io.ByteArrayInputStream;
30
import java.io.ByteArrayOutputStream;
31
import java.io.DataOutputStream;
32
import java.io.File;
33
import java.io.FileOutputStream;
34
import java.io.IOException;
35
import java.io.InputStream;
36
import java.io.OutputStream;
37
import java.io.RandomAccessFile;
38
import java.io.SequenceInputStream;
39
import java.util.Objects;
40
41
import javax.sound.sampled.AudioFileFormat;
42
import javax.sound.sampled.AudioFormat;
43
import javax.sound.sampled.AudioInputStream;
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
// METHODS TO IMPLEMENT AudioFileWriter
63
64
@Override
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
@Override
86
public int write(AudioInputStream stream, AudioFileFormat.Type fileType, OutputStream out) throws IOException {
87
Objects.requireNonNull(stream);
88
Objects.requireNonNull(fileType);
89
Objects.requireNonNull(out);
90
91
//$$fb the following check must come first ! Otherwise
92
// the next frame length check may throw an IOException and
93
// interrupt iterating File Writers. (see bug 4351296)
94
95
// throws IllegalArgumentException if not supported
96
AiffFileFormat aiffFileFormat = (AiffFileFormat)getAudioFileFormat(fileType, stream);
97
98
// we must know the total data length to calculate the file length
99
if( stream.getFrameLength() == AudioSystem.NOT_SPECIFIED ) {
100
throw new IOException("stream length not specified");
101
}
102
103
return writeAiffFile(stream, aiffFileFormat, out);
104
}
105
106
@Override
107
public int write(AudioInputStream stream, AudioFileFormat.Type fileType, File out) throws IOException {
108
Objects.requireNonNull(stream);
109
Objects.requireNonNull(fileType);
110
Objects.requireNonNull(out);
111
112
// throws IllegalArgumentException if not supported
113
AiffFileFormat aiffFileFormat = (AiffFileFormat)getAudioFileFormat(fileType, stream);
114
115
// first write the file without worrying about length fields
116
final int bytesWritten;
117
try (final FileOutputStream fos = new FileOutputStream(out);
118
final BufferedOutputStream bos = new BufferedOutputStream(fos)) {
119
bytesWritten = writeAiffFile(stream, aiffFileFormat, bos);
120
}
121
122
// now, if length fields were not specified, calculate them,
123
// open as a random access file, write the appropriate fields,
124
// close again....
125
if( aiffFileFormat.getByteLength()== AudioSystem.NOT_SPECIFIED ) {
126
127
// $$kk: 10.22.99: jan: please either implement this or throw an exception!
128
// $$fb: 2001-07-13: done. Fixes Bug 4479981
129
int channels = aiffFileFormat.getFormat().getChannels();
130
int sampleSize = aiffFileFormat.getFormat().getSampleSizeInBits();
131
int ssndBlockSize = channels * ((sampleSize + 7) / 8);
132
133
int aiffLength=bytesWritten;
134
int ssndChunkSize=aiffLength-aiffFileFormat.getHeaderSize()+16;
135
long dataSize=ssndChunkSize-16;
136
//TODO possibly incorrect round
137
int numFrames = (int) (dataSize / ssndBlockSize);
138
try (final RandomAccessFile raf = new RandomAccessFile(out, "rw")) {
139
// skip FORM magic
140
raf.skipBytes(4);
141
raf.writeInt(aiffLength - 8);
142
// skip aiff2 magic, fver chunk, comm magic, comm size, channel count,
143
raf.skipBytes(4 + aiffFileFormat.getFverChunkSize() + 4 + 4 + 2);
144
// write frame count
145
raf.writeInt(numFrames);
146
// skip sample size, samplerate, SSND magic
147
raf.skipBytes(2 + 10 + 4);
148
raf.writeInt(ssndChunkSize - 8);
149
// that's all
150
}
151
}
152
153
return bytesWritten;
154
}
155
156
157
// -----------------------------------------------------------------------
158
159
/**
160
* Returns the AudioFileFormat describing the file that will be written from this AudioInputStream.
161
* Throws IllegalArgumentException if not supported.
162
*/
163
private AudioFileFormat getAudioFileFormat(AudioFileFormat.Type type, AudioInputStream stream) {
164
if (!isFileTypeSupported(type, stream)) {
165
throw new IllegalArgumentException("File type " + type + " not supported.");
166
}
167
168
AudioFormat format = null;
169
AiffFileFormat fileFormat = null;
170
AudioFormat.Encoding encoding = AudioFormat.Encoding.PCM_SIGNED;
171
172
AudioFormat streamFormat = stream.getFormat();
173
AudioFormat.Encoding streamEncoding = streamFormat.getEncoding();
174
175
int sampleSizeInBits;
176
int fileSize;
177
boolean convert8to16 = false;
178
179
if( (AudioFormat.Encoding.ALAW.equals(streamEncoding)) ||
180
(AudioFormat.Encoding.ULAW.equals(streamEncoding)) ) {
181
182
if( streamFormat.getSampleSizeInBits()==8 ) {
183
184
encoding = AudioFormat.Encoding.PCM_SIGNED;
185
sampleSizeInBits=16;
186
convert8to16 = true;
187
188
} else {
189
190
// can't convert non-8-bit ALAW,ULAW
191
throw new IllegalArgumentException("Encoding " + streamEncoding + " supported only for 8-bit data.");
192
}
193
} else if ( streamFormat.getSampleSizeInBits()==8 ) {
194
195
encoding = AudioFormat.Encoding.PCM_UNSIGNED;
196
sampleSizeInBits=8;
197
198
} else {
199
200
encoding = AudioFormat.Encoding.PCM_SIGNED;
201
sampleSizeInBits=streamFormat.getSampleSizeInBits();
202
}
203
204
205
format = new AudioFormat( encoding,
206
streamFormat.getSampleRate(),
207
sampleSizeInBits,
208
streamFormat.getChannels(),
209
streamFormat.getFrameSize(),
210
streamFormat.getFrameRate(),
211
true); // AIFF is big endian
212
213
214
if( stream.getFrameLength()!=AudioSystem.NOT_SPECIFIED ) {
215
if( convert8to16 ) {
216
fileSize = (int)stream.getFrameLength()*streamFormat.getFrameSize()*2 + AiffFileFormat.AIFF_HEADERSIZE;
217
} else {
218
fileSize = (int)stream.getFrameLength()*streamFormat.getFrameSize() + AiffFileFormat.AIFF_HEADERSIZE;
219
}
220
} else {
221
fileSize = AudioSystem.NOT_SPECIFIED;
222
}
223
224
fileFormat = new AiffFileFormat( AudioFileFormat.Type.AIFF,
225
fileSize,
226
format,
227
(int)stream.getFrameLength() );
228
229
return fileFormat;
230
}
231
232
private int writeAiffFile(InputStream in, AiffFileFormat aiffFileFormat, OutputStream out) throws IOException {
233
234
int bytesRead = 0;
235
int bytesWritten = 0;
236
InputStream fileStream = getFileStream(aiffFileFormat, in);
237
byte[] buffer = new byte[bisBufferSize];
238
int maxLength = aiffFileFormat.getByteLength();
239
240
while( (bytesRead = fileStream.read( buffer )) >= 0 ) {
241
if (maxLength>0) {
242
if( bytesRead < maxLength ) {
243
out.write( buffer, 0, bytesRead );
244
bytesWritten += bytesRead;
245
maxLength -= bytesRead;
246
} else {
247
out.write( buffer, 0, maxLength );
248
bytesWritten += maxLength;
249
maxLength = 0;
250
break;
251
}
252
253
} else {
254
out.write( buffer, 0, bytesRead );
255
bytesWritten += bytesRead;
256
}
257
}
258
259
return bytesWritten;
260
}
261
262
private InputStream getFileStream(AiffFileFormat aiffFileFormat, InputStream audioStream) throws IOException {
263
264
// private method ... assumes aiffFileFormat is a supported file format
265
266
AudioFormat format = aiffFileFormat.getFormat();
267
AudioFormat streamFormat = null;
268
AudioFormat.Encoding encoding = null;
269
270
//$$fb a little bit nicer handling of constants
271
int headerSize = aiffFileFormat.getHeaderSize();
272
//int fverChunkSize = 0;
273
int fverChunkSize = aiffFileFormat.getFverChunkSize();
274
int commChunkSize = aiffFileFormat.getCommChunkSize();
275
int aiffLength = -1;
276
int ssndChunkSize = -1;
277
int ssndOffset = aiffFileFormat.getSsndChunkOffset();
278
short channels = (short) format.getChannels();
279
short sampleSize = (short) format.getSampleSizeInBits();
280
int ssndBlockSize = channels * ((sampleSize + 7) / 8);
281
int numFrames = aiffFileFormat.getFrameLength();
282
long dataSize = -1;
283
if( numFrames != AudioSystem.NOT_SPECIFIED) {
284
dataSize = (long) numFrames * ssndBlockSize;
285
ssndChunkSize = (int)dataSize + 16;
286
aiffLength = (int)dataSize+headerSize;
287
}
288
float sampleFramesPerSecond = format.getSampleRate();
289
int compCode = AiffFileFormat.AIFC_PCM;
290
291
byte[] header = null;
292
InputStream codedAudioStream = audioStream;
293
294
// if we need to do any format conversion, do it here....
295
296
if( audioStream instanceof AudioInputStream ) {
297
298
streamFormat = ((AudioInputStream)audioStream).getFormat();
299
encoding = streamFormat.getEncoding();
300
301
302
// $$jb: Note that AIFF samples are ALWAYS signed
303
if( (AudioFormat.Encoding.PCM_UNSIGNED.equals(encoding)) ||
304
( (AudioFormat.Encoding.PCM_SIGNED.equals(encoding)) && !streamFormat.isBigEndian() ) ) {
305
306
// plug in the transcoder to convert to PCM_SIGNED. big endian
307
codedAudioStream = AudioSystem.getAudioInputStream( new AudioFormat (
308
AudioFormat.Encoding.PCM_SIGNED,
309
streamFormat.getSampleRate(),
310
streamFormat.getSampleSizeInBits(),
311
streamFormat.getChannels(),
312
streamFormat.getFrameSize(),
313
streamFormat.getFrameRate(),
314
true ),
315
(AudioInputStream)audioStream );
316
317
} else if( (AudioFormat.Encoding.ULAW.equals(encoding)) ||
318
(AudioFormat.Encoding.ALAW.equals(encoding)) ) {
319
320
if( streamFormat.getSampleSizeInBits() != 8 ) {
321
throw new IllegalArgumentException("unsupported encoding");
322
}
323
324
//$$fb 2001-07-13: this is probably not what we want:
325
// writing PCM when ULAW/ALAW is requested. AIFC is able to write ULAW !
326
327
// plug in the transcoder to convert to PCM_SIGNED_BIG_ENDIAN
328
codedAudioStream = AudioSystem.getAudioInputStream( new AudioFormat (
329
AudioFormat.Encoding.PCM_SIGNED,
330
streamFormat.getSampleRate(),
331
streamFormat.getSampleSizeInBits() * 2,
332
streamFormat.getChannels(),
333
streamFormat.getFrameSize() * 2,
334
streamFormat.getFrameRate(),
335
true ),
336
(AudioInputStream)audioStream );
337
}
338
}
339
340
341
// Now create an AIFF stream header...
342
try (final ByteArrayOutputStream baos = new ByteArrayOutputStream();
343
final DataOutputStream dos = new DataOutputStream(baos)) {
344
// Write the outer FORM chunk
345
dos.writeInt(AiffFileFormat.AIFF_MAGIC);
346
dos.writeInt((aiffLength - 8));
347
dos.writeInt(AiffFileFormat.AIFF_MAGIC2);
348
// Write a FVER chunk - only for AIFC
349
//dos.writeInt(FVER_MAGIC);
350
//dos.writeInt( (fverChunkSize-8) );
351
//dos.writeInt(FVER_TIMESTAMP);
352
// Write a COMM chunk
353
dos.writeInt(AiffFileFormat.COMM_MAGIC);
354
dos.writeInt((commChunkSize - 8));
355
dos.writeShort(channels);
356
dos.writeInt(numFrames);
357
dos.writeShort(sampleSize);
358
write_ieee_extended(dos, sampleFramesPerSecond); // 10 bytes
359
//Only for AIFC
360
//dos.writeInt(compCode);
361
//dos.writeInt(compCode);
362
//dos.writeShort(0);
363
// Write the SSND chunk header
364
dos.writeInt(AiffFileFormat.SSND_MAGIC);
365
dos.writeInt((ssndChunkSize - 8));
366
// ssndOffset and ssndBlockSize set to 0 upon
367
// recommendation in "Sound Manager" chapter in
368
// "Inside Macintosh Sound", pp 2-87 (from Babu)
369
dos.writeInt(0); // ssndOffset
370
dos.writeInt(0); // ssndBlockSize
371
header = baos.toByteArray();
372
}
373
return new SequenceInputStream(new ByteArrayInputStream(header),
374
new NoCloseInputStream(codedAudioStream));
375
}
376
377
// HELPER METHODS
378
379
private static final int DOUBLE_MANTISSA_LENGTH = 52;
380
private static final int DOUBLE_EXPONENT_LENGTH = 11;
381
private static final long DOUBLE_SIGN_MASK = 0x8000000000000000L;
382
private static final long DOUBLE_EXPONENT_MASK = 0x7FF0000000000000L;
383
private static final long DOUBLE_MANTISSA_MASK = 0x000FFFFFFFFFFFFFL;
384
private static final int DOUBLE_EXPONENT_OFFSET = 1023;
385
386
private static final int EXTENDED_EXPONENT_OFFSET = 16383;
387
private static final int EXTENDED_MANTISSA_LENGTH = 63;
388
private static final int EXTENDED_EXPONENT_LENGTH = 15;
389
private static final long EXTENDED_INTEGER_MASK = 0x8000000000000000L;
390
391
/**
392
* Extended precision IEEE floating-point conversion routine.
393
* @argument DataOutputStream
394
* @argument double
395
* @exception IOException
396
*/
397
private void write_ieee_extended(DataOutputStream dos, float f) throws IOException {
398
/* The special cases NaN, Infinity and Zero are ignored, since
399
they do not represent useful sample rates anyway.
400
Denormalized number aren't handled, too. Below, there is a cast
401
from float to double. We hope that in this conversion,
402
numbers are normalized. Numbers that cannot be normalized are
403
ignored, too, as they, too, do not represent useful sample rates. */
404
long doubleBits = Double.doubleToLongBits((double) f);
405
406
long sign = (doubleBits & DOUBLE_SIGN_MASK)
407
>> (DOUBLE_EXPONENT_LENGTH + DOUBLE_MANTISSA_LENGTH);
408
long doubleExponent = (doubleBits & DOUBLE_EXPONENT_MASK)
409
>> DOUBLE_MANTISSA_LENGTH;
410
long doubleMantissa = doubleBits & DOUBLE_MANTISSA_MASK;
411
412
long extendedExponent = doubleExponent - DOUBLE_EXPONENT_OFFSET
413
+ EXTENDED_EXPONENT_OFFSET;
414
long extendedMantissa = doubleMantissa
415
<< (EXTENDED_MANTISSA_LENGTH - DOUBLE_MANTISSA_LENGTH);
416
long extendedSign = sign << EXTENDED_EXPONENT_LENGTH;
417
short extendedBits79To64 = (short) (extendedSign | extendedExponent);
418
long extendedBits63To0 = EXTENDED_INTEGER_MASK | extendedMantissa;
419
420
dos.writeShort(extendedBits79To64);
421
dos.writeLong(extendedBits63To0);
422
}
423
}
424
425