jdk/src/java.desktop/share/classes/com/sun/media/sound/AuFileWriter.java
changeset 25859 3317bb8137f4
parent 22584 eed64ee05369
child 32865 f9cb6e427f9e
equal deleted inserted replaced
25858:836adbf7a2cd 25859:3317bb8137f4
       
     1 /*
       
     2  * Copyright (c) 1999, 2014, 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 
       
    47 /**
       
    48  * AU file writer.
       
    49  *
       
    50  * @author Jan Borgersen
       
    51  */
       
    52 public final class AuFileWriter extends SunFileWriter {
       
    53 
       
    54     //$$fb value for length field if length is not known
       
    55     public final static int UNKNOWN_SIZE=-1;
       
    56 
       
    57     /**
       
    58      * Constructs a new AuFileWriter object.
       
    59      */
       
    60     public AuFileWriter() {
       
    61         super(new AudioFileFormat.Type[]{AudioFileFormat.Type.AU});
       
    62     }
       
    63 
       
    64     public AudioFileFormat.Type[] getAudioFileTypes(AudioInputStream stream) {
       
    65 
       
    66         AudioFileFormat.Type[] filetypes = new AudioFileFormat.Type[types.length];
       
    67         System.arraycopy(types, 0, filetypes, 0, types.length);
       
    68 
       
    69         // make sure we can write this stream
       
    70         AudioFormat format = stream.getFormat();
       
    71         AudioFormat.Encoding encoding = format.getEncoding();
       
    72 
       
    73         if( (AudioFormat.Encoding.ALAW.equals(encoding)) ||
       
    74             (AudioFormat.Encoding.ULAW.equals(encoding)) ||
       
    75             (AudioFormat.Encoding.PCM_SIGNED.equals(encoding)) ||
       
    76             (AudioFormat.Encoding.PCM_UNSIGNED.equals(encoding)) ) {
       
    77 
       
    78             return filetypes;
       
    79         }
       
    80 
       
    81         return new AudioFileFormat.Type[0];
       
    82     }
       
    83 
       
    84 
       
    85     public int write(AudioInputStream stream, AudioFileFormat.Type fileType, OutputStream out) throws IOException {
       
    86 
       
    87         // we must know the total data length to calculate the file length
       
    88         //$$fb 2001-07-13: fix for bug 4351296: do not throw an exception
       
    89         //if( stream.getFrameLength() == AudioSystem.NOT_SPECIFIED ) {
       
    90         //      throw new IOException("stream length not specified");
       
    91         //}
       
    92 
       
    93         // throws IllegalArgumentException if not supported
       
    94         AuFileFormat auFileFormat = (AuFileFormat)getAudioFileFormat(fileType, stream);
       
    95 
       
    96         int bytesWritten = writeAuFile(stream, auFileFormat, out);
       
    97         return bytesWritten;
       
    98     }
       
    99 
       
   100 
       
   101 
       
   102     public int write(AudioInputStream stream, AudioFileFormat.Type fileType, File out) throws IOException {
       
   103 
       
   104         // throws IllegalArgumentException if not supported
       
   105         AuFileFormat auFileFormat = (AuFileFormat)getAudioFileFormat(fileType, stream);
       
   106 
       
   107         // first write the file without worrying about length fields
       
   108         FileOutputStream fos = new FileOutputStream( out );     // throws IOException
       
   109         BufferedOutputStream bos = new BufferedOutputStream( fos, bisBufferSize );
       
   110         int bytesWritten = writeAuFile(stream, auFileFormat, bos );
       
   111         bos.close();
       
   112 
       
   113         // now, if length fields were not specified, calculate them,
       
   114         // open as a random access file, write the appropriate fields,
       
   115         // close again....
       
   116         if( auFileFormat.getByteLength()== AudioSystem.NOT_SPECIFIED ) {
       
   117 
       
   118             // $$kk: 10.22.99: jan: please either implement this or throw an exception!
       
   119             // $$fb: 2001-07-13: done. Fixes Bug 4479981
       
   120             RandomAccessFile raf=new RandomAccessFile(out, "rw");
       
   121             if (raf.length()<=0x7FFFFFFFl) {
       
   122                 // skip AU magic and data offset field
       
   123                 raf.skipBytes(8);
       
   124                 raf.writeInt(bytesWritten-AuFileFormat.AU_HEADERSIZE);
       
   125                 // that's all
       
   126             }
       
   127             raf.close();
       
   128         }
       
   129 
       
   130         return bytesWritten;
       
   131     }
       
   132 
       
   133 
       
   134     // -------------------------------------------------------------
       
   135 
       
   136     /**
       
   137      * Returns the AudioFileFormat describing the file that will be written from this AudioInputStream.
       
   138      * Throws IllegalArgumentException if not supported.
       
   139      */
       
   140     private AudioFileFormat getAudioFileFormat(AudioFileFormat.Type type, AudioInputStream stream) {
       
   141 
       
   142         AudioFormat format = null;
       
   143         AuFileFormat fileFormat = null;
       
   144         AudioFormat.Encoding encoding = AudioFormat.Encoding.PCM_SIGNED;
       
   145 
       
   146         AudioFormat streamFormat = stream.getFormat();
       
   147         AudioFormat.Encoding streamEncoding = streamFormat.getEncoding();
       
   148 
       
   149 
       
   150         float sampleRate;
       
   151         int sampleSizeInBits;
       
   152         int channels;
       
   153         int frameSize;
       
   154         float frameRate;
       
   155         int fileSize;
       
   156 
       
   157         if( !types[0].equals(type) ) {
       
   158             throw new IllegalArgumentException("File type " + type + " not supported.");
       
   159         }
       
   160 
       
   161         if( (AudioFormat.Encoding.ALAW.equals(streamEncoding)) ||
       
   162             (AudioFormat.Encoding.ULAW.equals(streamEncoding)) ) {
       
   163 
       
   164             encoding = streamEncoding;
       
   165             sampleSizeInBits = streamFormat.getSampleSizeInBits();
       
   166 
       
   167         } else if ( streamFormat.getSampleSizeInBits()==8 ) {
       
   168 
       
   169             encoding = AudioFormat.Encoding.PCM_SIGNED;
       
   170             sampleSizeInBits=8;
       
   171 
       
   172         } else {
       
   173 
       
   174             encoding = AudioFormat.Encoding.PCM_SIGNED;
       
   175             sampleSizeInBits=streamFormat.getSampleSizeInBits();
       
   176         }
       
   177 
       
   178 
       
   179         format = new AudioFormat( encoding,
       
   180                                   streamFormat.getSampleRate(),
       
   181                                   sampleSizeInBits,
       
   182                                   streamFormat.getChannels(),
       
   183                                   streamFormat.getFrameSize(),
       
   184                                   streamFormat.getFrameRate(),
       
   185                                   true);        // AU is always big endian
       
   186 
       
   187 
       
   188         if( stream.getFrameLength()!=AudioSystem.NOT_SPECIFIED ) {
       
   189             fileSize = (int)stream.getFrameLength()*streamFormat.getFrameSize() + AuFileFormat.AU_HEADERSIZE;
       
   190         } else {
       
   191             fileSize = AudioSystem.NOT_SPECIFIED;
       
   192         }
       
   193 
       
   194         fileFormat = new AuFileFormat( AudioFileFormat.Type.AU,
       
   195                                        fileSize,
       
   196                                        format,
       
   197                                        (int)stream.getFrameLength() );
       
   198 
       
   199         return fileFormat;
       
   200     }
       
   201 
       
   202 
       
   203     private InputStream getFileStream(AuFileFormat auFileFormat, InputStream audioStream) throws IOException {
       
   204 
       
   205         // private method ... assumes auFileFormat is a supported file type
       
   206 
       
   207         AudioFormat format            = auFileFormat.getFormat();
       
   208 
       
   209         int magic          = AuFileFormat.AU_SUN_MAGIC;
       
   210         int headerSize     = AuFileFormat.AU_HEADERSIZE;
       
   211         long dataSize       = auFileFormat.getFrameLength();
       
   212         //$$fb fix for Bug 4351296
       
   213         //int dataSizeInBytes = dataSize * format.getFrameSize();
       
   214         long dataSizeInBytes = (dataSize==AudioSystem.NOT_SPECIFIED)?UNKNOWN_SIZE:dataSize * format.getFrameSize();
       
   215         if (dataSizeInBytes>0x7FFFFFFFl) {
       
   216             dataSizeInBytes=UNKNOWN_SIZE;
       
   217         }
       
   218         int encoding_local = auFileFormat.getAuType();
       
   219         int sampleRate     = (int)format.getSampleRate();
       
   220         int channels       = format.getChannels();
       
   221         //$$fb below is the fix for 4297100.
       
   222         //boolean bigendian      = format.isBigEndian();
       
   223         boolean bigendian      = true;                  // force bigendian
       
   224 
       
   225         byte header[] = null;
       
   226         ByteArrayInputStream headerStream = null;
       
   227         ByteArrayOutputStream baos = null;
       
   228         DataOutputStream dos = null;
       
   229         SequenceInputStream auStream = null;
       
   230 
       
   231         AudioFormat audioStreamFormat = null;
       
   232         AudioFormat.Encoding encoding = null;
       
   233         InputStream codedAudioStream = audioStream;
       
   234 
       
   235         // if we need to do any format conversion, do it here.
       
   236 
       
   237         codedAudioStream = audioStream;
       
   238 
       
   239         if( audioStream instanceof AudioInputStream ) {
       
   240 
       
   241 
       
   242             audioStreamFormat = ((AudioInputStream)audioStream).getFormat();
       
   243             encoding = audioStreamFormat.getEncoding();
       
   244 
       
   245             //$$ fb 2001-07-13: Bug 4391108
       
   246             if( (AudioFormat.Encoding.PCM_UNSIGNED.equals(encoding)) ||
       
   247                 (AudioFormat.Encoding.PCM_SIGNED.equals(encoding)
       
   248                  && bigendian != audioStreamFormat.isBigEndian()) ) {
       
   249 
       
   250                                 // plug in the transcoder to convert to PCM_SIGNED, bigendian
       
   251                                 // NOTE: little endian AU is not common, so we're always converting
       
   252                                 //       to big endian unless the passed in audioFileFormat is little.
       
   253                                 // $$fb this NOTE is superseded. We always write big endian au files, this is by far the standard.
       
   254                 codedAudioStream = AudioSystem.getAudioInputStream( new AudioFormat (
       
   255                                                                                      AudioFormat.Encoding.PCM_SIGNED,
       
   256                                                                                      audioStreamFormat.getSampleRate(),
       
   257                                                                                      audioStreamFormat.getSampleSizeInBits(),
       
   258                                                                                      audioStreamFormat.getChannels(),
       
   259                                                                                      audioStreamFormat.getFrameSize(),
       
   260                                                                                      audioStreamFormat.getFrameRate(),
       
   261                                                                                      bigendian),
       
   262                                                                     (AudioInputStream)audioStream );
       
   263 
       
   264 
       
   265             }
       
   266         }
       
   267 
       
   268         baos = new ByteArrayOutputStream();
       
   269         dos = new DataOutputStream(baos);
       
   270 
       
   271 
       
   272         if (bigendian) {
       
   273             dos.writeInt(AuFileFormat.AU_SUN_MAGIC);
       
   274             dos.writeInt(headerSize);
       
   275             dos.writeInt((int)dataSizeInBytes);
       
   276             dos.writeInt(encoding_local);
       
   277             dos.writeInt(sampleRate);
       
   278             dos.writeInt(channels);
       
   279         } else {
       
   280             dos.writeInt(AuFileFormat.AU_SUN_INV_MAGIC);
       
   281             dos.writeInt(big2little(headerSize));
       
   282             dos.writeInt(big2little((int)dataSizeInBytes));
       
   283             dos.writeInt(big2little(encoding_local));
       
   284             dos.writeInt(big2little(sampleRate));
       
   285             dos.writeInt(big2little(channels));
       
   286         }
       
   287 
       
   288         // Now create a new InputStream from headerStream and the InputStream
       
   289         // in audioStream
       
   290 
       
   291         dos.close();
       
   292         header = baos.toByteArray();
       
   293         headerStream = new ByteArrayInputStream( header );
       
   294         auStream = new SequenceInputStream(headerStream,
       
   295                         new NoCloseInputStream(codedAudioStream));
       
   296 
       
   297         return auStream;
       
   298     }
       
   299 
       
   300     private int writeAuFile(InputStream in, AuFileFormat auFileFormat, OutputStream out) throws IOException {
       
   301 
       
   302         int bytesRead = 0;
       
   303         int bytesWritten = 0;
       
   304         InputStream fileStream = getFileStream(auFileFormat, in);
       
   305         byte buffer[] = new byte[bisBufferSize];
       
   306         int maxLength = auFileFormat.getByteLength();
       
   307 
       
   308         while( (bytesRead = fileStream.read( buffer )) >= 0 ) {
       
   309             if (maxLength>0) {
       
   310                 if( bytesRead < maxLength ) {
       
   311                     out.write( buffer, 0, bytesRead );
       
   312                     bytesWritten += bytesRead;
       
   313                     maxLength -= bytesRead;
       
   314                 } else {
       
   315                     out.write( buffer, 0, maxLength );
       
   316                     bytesWritten += maxLength;
       
   317                     maxLength = 0;
       
   318                     break;
       
   319                 }
       
   320             } else {
       
   321                 out.write( buffer, 0, bytesRead );
       
   322                 bytesWritten += bytesRead;
       
   323             }
       
   324         }
       
   325 
       
   326         return bytesWritten;
       
   327     }
       
   328 
       
   329 
       
   330 }