377 lines · 16.0 KB · Java
RawDownload
/* * Copyright (c) 1999, 2011, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation.  Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle in the LICENSE file that accompanied this code. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ package com.sun.media.sound; import java.io.File;import java.io.InputStream;import java.io.OutputStream;import java.io.IOException; import java.io.BufferedOutputStream;import java.io.DataOutputStream;import java.io.FileOutputStream;import java.io.ByteArrayInputStream;import java.io.ByteArrayOutputStream;import java.io.RandomAccessFile;import java.io.SequenceInputStream; import javax.sound.sampled.AudioFileFormat;import javax.sound.sampled.AudioInputStream;import javax.sound.sampled.AudioFormat;import javax.sound.sampled.AudioSystem; //$$fb this class is buggy. Should be replaced in future. /** * WAVE file writer. * * @author Jan Borgersen */public class WaveFileWriter extends SunFileWriter {     // magic numbers    static  final int RIFF_MAGIC = 1380533830;    static  final int WAVE_MAGIC = 1463899717;    static  final int FMT_MAGIC  = 0x666d7420; // "fmt "    static  final int DATA_MAGIC = 0x64617461; // "data"     // encodings    static final int WAVE_FORMAT_UNKNOWN   = 0x0000;    static final int WAVE_FORMAT_PCM       = 0x0001;    static final int WAVE_FORMAT_ADPCM     = 0x0002;    static final int WAVE_FORMAT_ALAW      = 0x0006;    static final int WAVE_FORMAT_MULAW     = 0x0007;    static final int WAVE_FORMAT_OKI_ADPCM = 0x0010;    static final int WAVE_FORMAT_DIGISTD   = 0x0015;    static final int WAVE_FORMAT_DIGIFIX   = 0x0016;    static final int WAVE_IBM_FORMAT_MULAW = 0x0101;    static final int WAVE_IBM_FORMAT_ALAW  = 0x0102;    static final int WAVE_IBM_FORMAT_ADPCM = 0x0103;    static final int WAVE_FORMAT_DVI_ADPCM = 0x0011;    static final int WAVE_FORMAT_SX7383    = 0x1C07;     /**     * WAVE type     */    private static final AudioFileFormat.Type waveTypes[] = {        AudioFileFormat.Type.WAVE    };      /**     * Constructs a new WaveFileWriter object.     */    public WaveFileWriter() {        super(waveTypes);    }      // METHODS TO IMPLEMENT AudioFileWriter      public AudioFileFormat.Type[] getAudioFileTypes(AudioInputStream stream) {         AudioFileFormat.Type[] filetypes = new AudioFileFormat.Type[types.length];        System.arraycopy(types, 0, filetypes, 0, types.length);         // make sure we can write this stream        AudioFormat format = stream.getFormat();        AudioFormat.Encoding encoding = format.getEncoding();         if( AudioFormat.Encoding.ALAW.equals(encoding) ||            AudioFormat.Encoding.ULAW.equals(encoding) ||            AudioFormat.Encoding.PCM_SIGNED.equals(encoding) ||            AudioFormat.Encoding.PCM_UNSIGNED.equals(encoding) ) {             return filetypes;        }         return new AudioFileFormat.Type[0];    }      public int write(AudioInputStream stream, AudioFileFormat.Type fileType, OutputStream out) throws IOException {         //$$fb the following check must come first ! Otherwise        // the next frame length check may throw an IOException and        // interrupt iterating File Writers. (see bug 4351296)         // throws IllegalArgumentException if not supported        WaveFileFormat waveFileFormat = (WaveFileFormat)getAudioFileFormat(fileType, stream);         //$$fb when we got this far, we are committed to write this file         // we must know the total data length to calculate the file length        if( stream.getFrameLength() == AudioSystem.NOT_SPECIFIED ) {            throw new IOException("stream length not specified");        }         int bytesWritten = writeWaveFile(stream, waveFileFormat, out);        return bytesWritten;    }      public int write(AudioInputStream stream, AudioFileFormat.Type fileType, File out) throws IOException {         // throws IllegalArgumentException if not supported        WaveFileFormat waveFileFormat = (WaveFileFormat)getAudioFileFormat(fileType, stream);         // first write the file without worrying about length fields        FileOutputStream fos = new FileOutputStream( out );     // throws IOException        BufferedOutputStream bos = new BufferedOutputStream( fos, bisBufferSize );        int bytesWritten = writeWaveFile(stream, waveFileFormat, bos );        bos.close();         // now, if length fields were not specified, calculate them,        // open as a random access file, write the appropriate fields,        // close again....        if( waveFileFormat.getByteLength()== AudioSystem.NOT_SPECIFIED ) {             int dataLength=bytesWritten-waveFileFormat.getHeaderSize();            int riffLength=dataLength + waveFileFormat.getHeaderSize() - 8;             RandomAccessFile raf=new RandomAccessFile(out, "rw");            // skip RIFF magic            raf.skipBytes(4);            raf.writeInt(big2little( riffLength ));            // skip WAVE magic, fmt_ magic, fmt_ length, fmt_ chunk, data magic            raf.skipBytes(4+4+4+WaveFileFormat.getFmtChunkSize(waveFileFormat.getWaveType())+4);            raf.writeInt(big2little( dataLength ));            // that's all            raf.close();        }         return bytesWritten;    }     //--------------------------------------------------------------------     /**     * Returns the AudioFileFormat describing the file that will be written from this AudioInputStream.     * Throws IllegalArgumentException if not supported.     */    private AudioFileFormat getAudioFileFormat(AudioFileFormat.Type type, AudioInputStream stream) {        AudioFormat format = null;        WaveFileFormat fileFormat = null;        AudioFormat.Encoding encoding = AudioFormat.Encoding.PCM_SIGNED;         AudioFormat streamFormat = stream.getFormat();        AudioFormat.Encoding streamEncoding = streamFormat.getEncoding();         float sampleRate;        int sampleSizeInBits;        int channels;        int frameSize;        float frameRate;        int fileSize;         if (!types[0].equals(type)) {            throw new IllegalArgumentException("File type " + type + " not supported.");        }        int waveType = WaveFileFormat.WAVE_FORMAT_PCM;         if( AudioFormat.Encoding.ALAW.equals(streamEncoding) ||            AudioFormat.Encoding.ULAW.equals(streamEncoding) ) {             encoding = streamEncoding;            sampleSizeInBits = streamFormat.getSampleSizeInBits();            if (streamEncoding.equals(AudioFormat.Encoding.ALAW)) {                waveType = WAVE_FORMAT_ALAW;            } else {                waveType = WAVE_FORMAT_MULAW;            }        } else if ( streamFormat.getSampleSizeInBits()==8 ) {            encoding = AudioFormat.Encoding.PCM_UNSIGNED;            sampleSizeInBits=8;        } else {            encoding = AudioFormat.Encoding.PCM_SIGNED;            sampleSizeInBits=streamFormat.getSampleSizeInBits();        }          format = new AudioFormat( encoding,                                  streamFormat.getSampleRate(),                                  sampleSizeInBits,                                  streamFormat.getChannels(),                                  streamFormat.getFrameSize(),                                  streamFormat.getFrameRate(),                                  false);       // WAVE is little endian         if( stream.getFrameLength()!=AudioSystem.NOT_SPECIFIED ) {            fileSize = (int)stream.getFrameLength()*streamFormat.getFrameSize()                + WaveFileFormat.getHeaderSize(waveType);        } else {            fileSize = AudioSystem.NOT_SPECIFIED;        }         fileFormat = new WaveFileFormat( AudioFileFormat.Type.WAVE,                                         fileSize,                                         format,                                         (int)stream.getFrameLength() );         return fileFormat;    }      private int writeWaveFile(InputStream in, WaveFileFormat waveFileFormat, OutputStream out) throws IOException {         int bytesRead = 0;        int bytesWritten = 0;        InputStream fileStream = getFileStream(waveFileFormat, in);        byte buffer[] = new byte[bisBufferSize];        int maxLength = waveFileFormat.getByteLength();         while( (bytesRead = fileStream.read( buffer )) >= 0 ) {             if (maxLength>0) {                if( bytesRead < maxLength ) {                    out.write( buffer, 0, (int)bytesRead );                    bytesWritten += bytesRead;                    maxLength -= bytesRead;                } else {                    out.write( buffer, 0, (int)maxLength );                    bytesWritten += maxLength;                    maxLength = 0;                    break;                }            } else {                out.write( buffer, 0, (int)bytesRead );                bytesWritten += bytesRead;            }        }         return bytesWritten;    }     private InputStream getFileStream(WaveFileFormat waveFileFormat, InputStream audioStream) throws IOException {        // private method ... assumes audioFileFormat is a supported file type         // WAVE header fields        AudioFormat audioFormat = waveFileFormat.getFormat();        int headerLength       = waveFileFormat.getHeaderSize();        int riffMagic          = WaveFileFormat.RIFF_MAGIC;        int waveMagic          = WaveFileFormat.WAVE_MAGIC;        int fmtMagic           = WaveFileFormat.FMT_MAGIC;        int fmtLength          = WaveFileFormat.getFmtChunkSize(waveFileFormat.getWaveType());        short wav_type         = (short) waveFileFormat.getWaveType();        short channels         = (short) audioFormat.getChannels();        short sampleSizeInBits = (short) audioFormat.getSampleSizeInBits();        int sampleRate         = (int) audioFormat.getSampleRate();        int frameSizeInBytes   = (int) audioFormat.getFrameSize();        int frameRate              = (int) audioFormat.getFrameRate();        int avgBytesPerSec     = channels * sampleSizeInBits * sampleRate / 8;;        short blockAlign       = (short) ((sampleSizeInBits / 8) * channels);        int dataMagic              = WaveFileFormat.DATA_MAGIC;        int dataLength             = waveFileFormat.getFrameLength() * frameSizeInBytes;        int length                         = waveFileFormat.getByteLength();        int riffLength = dataLength + headerLength - 8;         byte header[] = null;        ByteArrayInputStream headerStream = null;        ByteArrayOutputStream baos = null;        DataOutputStream dos = null;        SequenceInputStream waveStream = null;         AudioFormat audioStreamFormat = null;        AudioFormat.Encoding encoding = null;        InputStream codedAudioStream = audioStream;         // if audioStream is an AudioInputStream and we need to convert, do it here...        if(audioStream instanceof AudioInputStream) {            audioStreamFormat = ((AudioInputStream)audioStream).getFormat();             encoding = audioStreamFormat.getEncoding();             if(AudioFormat.Encoding.PCM_SIGNED.equals(encoding)) {                if( sampleSizeInBits==8 ) {                    wav_type = WaveFileFormat.WAVE_FORMAT_PCM;                    // plug in the transcoder to convert from PCM_SIGNED to PCM_UNSIGNED                    codedAudioStream = AudioSystem.getAudioInputStream( new AudioFormat(                                                                                        AudioFormat.Encoding.PCM_UNSIGNED,                                                                                        audioStreamFormat.getSampleRate(),                                                                                        audioStreamFormat.getSampleSizeInBits(),                                                                                        audioStreamFormat.getChannels(),                                                                                        audioStreamFormat.getFrameSize(),                                                                                        audioStreamFormat.getFrameRate(),                                                                                        false),                                                                        (AudioInputStream)audioStream);                }            }            if( (AudioFormat.Encoding.PCM_SIGNED.equals(encoding) && audioStreamFormat.isBigEndian()) ||                (AudioFormat.Encoding.PCM_UNSIGNED.equals(encoding) && !audioStreamFormat.isBigEndian()) ||                (AudioFormat.Encoding.PCM_UNSIGNED.equals(encoding) && audioStreamFormat.isBigEndian()) ) {                if( sampleSizeInBits!=8) {                    wav_type = WaveFileFormat.WAVE_FORMAT_PCM;                    // plug in the transcoder to convert to PCM_SIGNED_LITTLE_ENDIAN                    codedAudioStream = AudioSystem.getAudioInputStream( new AudioFormat(                                                                                        AudioFormat.Encoding.PCM_SIGNED,                                                                                        audioStreamFormat.getSampleRate(),                                                                                        audioStreamFormat.getSampleSizeInBits(),                                                                                        audioStreamFormat.getChannels(),                                                                                        audioStreamFormat.getFrameSize(),                                                                                        audioStreamFormat.getFrameRate(),                                                                                        false),                                                                        (AudioInputStream)audioStream);                }            }        }          // Now push the header into a stream, concat, and return the new SequenceInputStream         baos = new ByteArrayOutputStream();        dos = new DataOutputStream(baos);         // we write in littleendian...        dos.writeInt(riffMagic);        dos.writeInt(big2little( riffLength ));        dos.writeInt(waveMagic);        dos.writeInt(fmtMagic);        dos.writeInt(big2little(fmtLength));        dos.writeShort(big2littleShort(wav_type));        dos.writeShort(big2littleShort(channels));        dos.writeInt(big2little(sampleRate));        dos.writeInt(big2little(avgBytesPerSec));        dos.writeShort(big2littleShort(blockAlign));        dos.writeShort(big2littleShort(sampleSizeInBits));        //$$fb 2002-04-16: Fix for 4636355: RIFF audio headers could be _more_ spec compliant        if (wav_type != WaveFileFormat.WAVE_FORMAT_PCM) {            // add length 0 for "codec specific data length"            dos.writeShort(0);        }         dos.writeInt(dataMagic);        dos.writeInt(big2little(dataLength));         dos.close();        header = baos.toByteArray();        headerStream = new ByteArrayInputStream( header );        waveStream = new SequenceInputStream(headerStream,                            new NoCloseInputStream(codedAudioStream));         return (InputStream)waveStream;    }}