1 /*
   2  * Copyright (c) 2008, 2016, 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.IOException;
  30 import java.io.OutputStream;
  31 import java.util.Objects;
  32 
  33 import javax.sound.sampled.AudioFileFormat;
  34 import javax.sound.sampled.AudioFileFormat.Type;
  35 import javax.sound.sampled.AudioFormat;
  36 import javax.sound.sampled.AudioFormat.Encoding;
  37 import javax.sound.sampled.AudioInputStream;
  38 import javax.sound.sampled.AudioSystem;
  39 import javax.sound.sampled.spi.AudioFileWriter;
  40 
  41 /**
  42  * Floating-point encoded (format 3) WAVE file writer.
  43  *
  44  * @author Karl Helgason
  45  */
  46 public final class WaveFloatFileWriter extends AudioFileWriter {
  47 
  48     @Override
  49     public Type[] getAudioFileTypes() {
  50         return new Type[]{Type.WAVE};
  51     }
  52 
  53     @Override
  54     public Type[] getAudioFileTypes(AudioInputStream stream) {
  55 
  56         if (!stream.getFormat().getEncoding().equals(Encoding.PCM_FLOAT))
  57             return new Type[0];
  58         return new Type[] { Type.WAVE };
  59     }
  60 
  61     private void checkFormat(AudioFileFormat.Type type, AudioInputStream stream) {
  62         if (!Type.WAVE.equals(type))
  63             throw new IllegalArgumentException("File type " + type
  64                     + " not supported.");
  65         if (!stream.getFormat().getEncoding().equals(Encoding.PCM_FLOAT))
  66             throw new IllegalArgumentException("File format "
  67                     + stream.getFormat() + " not supported.");
  68     }
  69 
  70     public void write(AudioInputStream stream, RIFFWriter writer)
  71             throws IOException {
  72 
  73         RIFFWriter fmt_chunk = writer.writeChunk("fmt ");
  74 
  75         AudioFormat format = stream.getFormat();
  76         fmt_chunk.writeUnsignedShort(3); // WAVE_FORMAT_IEEE_FLOAT
  77         fmt_chunk.writeUnsignedShort(format.getChannels());
  78         fmt_chunk.writeUnsignedInt((int) format.getSampleRate());
  79         fmt_chunk.writeUnsignedInt(((int) format.getFrameRate())
  80                 * format.getFrameSize());
  81         fmt_chunk.writeUnsignedShort(format.getFrameSize());
  82         fmt_chunk.writeUnsignedShort(format.getSampleSizeInBits());
  83         fmt_chunk.close();
  84         RIFFWriter data_chunk = writer.writeChunk("data");
  85         byte[] buff = new byte[1024];
  86         int len;
  87         while ((len = stream.read(buff, 0, buff.length)) != -1)
  88             data_chunk.write(buff, 0, len);
  89         data_chunk.close();
  90     }
  91 
  92     private static final class NoCloseOutputStream extends OutputStream {
  93         final OutputStream out;
  94 
  95         NoCloseOutputStream(OutputStream out) {
  96             this.out = out;
  97         }
  98 
  99         @Override
 100         public void write(int b) throws IOException {
 101             out.write(b);
 102         }
 103 
 104         @Override
 105         public void flush() throws IOException {
 106             out.flush();
 107         }
 108 
 109         @Override
 110         public void write(byte[] b, int off, int len) throws IOException {
 111             out.write(b, off, len);
 112         }
 113 
 114         @Override
 115         public void write(byte[] b) throws IOException {
 116             out.write(b);
 117         }
 118     }
 119 
 120     private AudioInputStream toLittleEndian(AudioInputStream ais) {
 121         AudioFormat format = ais.getFormat();
 122         AudioFormat targetFormat = new AudioFormat(format.getEncoding(), format
 123                 .getSampleRate(), format.getSampleSizeInBits(), format
 124                 .getChannels(), format.getFrameSize(), format.getFrameRate(),
 125                 false);
 126         return AudioSystem.getAudioInputStream(targetFormat, ais);
 127     }
 128 
 129     @Override
 130     public int write(AudioInputStream stream, Type fileType, OutputStream out)
 131             throws IOException {
 132         Objects.requireNonNull(stream);
 133         Objects.requireNonNull(fileType);
 134         Objects.requireNonNull(out);
 135 
 136         checkFormat(fileType, stream);
 137         if (stream.getFormat().isBigEndian())
 138             stream = toLittleEndian(stream);
 139         RIFFWriter writer = new RIFFWriter(new NoCloseOutputStream(out), "WAVE");
 140         write(stream, writer);
 141         int fpointer = (int) writer.getFilePointer();
 142         writer.close();
 143         return fpointer;
 144     }
 145 
 146     @Override
 147     public int write(AudioInputStream stream, Type fileType, File out)
 148             throws IOException {
 149         Objects.requireNonNull(stream);
 150         Objects.requireNonNull(fileType);
 151         Objects.requireNonNull(out);
 152 
 153         checkFormat(fileType, stream);
 154         if (stream.getFormat().isBigEndian())
 155             stream = toLittleEndian(stream);
 156         RIFFWriter writer = new RIFFWriter(out, "WAVE");
 157         write(stream, writer);
 158         int fpointer = (int) writer.getFilePointer();
 159         writer.close();
 160         return fpointer;
 161     }
 162 }