Home | History | Annotate | Download | only in plugins
      1 /*
      2  * Copyright (c) 2009-2010 jMonkeyEngine
      3  * All rights reserved.
      4  *
      5  * Redistribution and use in source and binary forms, with or without
      6  * modification, are permitted provided that the following conditions are
      7  * met:
      8  *
      9  * * Redistributions of source code must retain the above copyright
     10  *   notice, this list of conditions and the following disclaimer.
     11  *
     12  * * Redistributions in binary form must reproduce the above copyright
     13  *   notice, this list of conditions and the following disclaimer in the
     14  *   documentation and/or other materials provided with the distribution.
     15  *
     16  * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
     17  *   may be used to endorse or promote products derived from this software
     18  *   without specific prior written permission.
     19  *
     20  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
     21  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
     22  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
     23  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
     24  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
     25  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
     26  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
     27  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
     28  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
     29  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
     30  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     31  */
     32 
     33 package com.jme3.audio.plugins;
     34 
     35 import com.jme3.asset.AssetInfo;
     36 import com.jme3.asset.AssetLoader;
     37 import com.jme3.audio.AudioBuffer;
     38 import com.jme3.audio.AudioData;
     39 import com.jme3.audio.AudioKey;
     40 import com.jme3.audio.AudioStream;
     41 import com.jme3.util.BufferUtils;
     42 import com.jme3.util.LittleEndien;
     43 import java.io.IOException;
     44 import java.io.InputStream;
     45 import java.nio.ByteBuffer;
     46 import java.util.logging.Level;
     47 import java.util.logging.Logger;
     48 
     49 public class WAVLoader implements AssetLoader {
     50 
     51     private static final Logger logger = Logger.getLogger(WAVLoader.class.getName());
     52 
     53     // all these are in big endian
     54     private static final int i_RIFF = 0x46464952;
     55     private static final int i_WAVE = 0x45564157;
     56     private static final int i_fmt  = 0x20746D66;
     57     private static final int i_data = 0x61746164;
     58 
     59     private boolean readStream = false;
     60 
     61     private AudioBuffer audioBuffer;
     62     private AudioStream audioStream;
     63     private AudioData audioData;
     64     private int bytesPerSec;
     65     private float duration;
     66 
     67     private LittleEndien in;
     68 
     69     private void readFormatChunk(int size) throws IOException{
     70         // if other compressions are supported, size doesn't have to be 16
     71 //        if (size != 16)
     72 //            logger.warning("Expected size of format chunk to be 16");
     73 
     74         int compression = in.readShort();
     75         if (compression != 1){
     76             throw new IOException("WAV Loader only supports PCM wave files");
     77         }
     78 
     79         int channels = in.readShort();
     80         int sampleRate = in.readInt();
     81 
     82         bytesPerSec = in.readInt(); // used to calculate duration
     83 
     84         int bytesPerSample = in.readShort();
     85         int bitsPerSample = in.readShort();
     86 
     87         int expectedBytesPerSec = (bitsPerSample * channels * sampleRate) / 8;
     88         if (expectedBytesPerSec != bytesPerSec){
     89             logger.log(Level.WARNING, "Expected {0} bytes per second, got {1}",
     90                     new Object[]{expectedBytesPerSec, bytesPerSec});
     91         }
     92 
     93         if (bitsPerSample != 8 && bitsPerSample != 16)
     94             throw new IOException("Only 8 and 16 bits per sample are supported!");
     95 
     96         if ( (bitsPerSample / 8) * channels != bytesPerSample)
     97             throw new IOException("Invalid bytes per sample value");
     98 
     99         if (bytesPerSample * sampleRate != bytesPerSec)
    100             throw new IOException("Invalid bytes per second value");
    101 
    102         audioData.setupFormat(channels, bitsPerSample, sampleRate);
    103 
    104         int remaining = size - 16;
    105         if (remaining > 0){
    106             in.skipBytes(remaining);
    107         }
    108     }
    109 
    110     private void readDataChunkForBuffer(int len) throws IOException {
    111         ByteBuffer data = BufferUtils.createByteBuffer(len);
    112         byte[] buf = new byte[512];
    113         int read = 0;
    114         while ( (read = in.read(buf)) > 0){
    115             data.put(buf, 0, Math.min(read, data.remaining()) );
    116         }
    117         data.flip();
    118         audioBuffer.updateData(data);
    119         in.close();
    120     }
    121 
    122     private void readDataChunkForStream(int len) throws IOException {
    123         audioStream.updateData(in, duration);
    124     }
    125 
    126     private AudioData load(InputStream inputStream, boolean stream) throws IOException{
    127         this.in = new LittleEndien(inputStream);
    128 
    129         int sig = in.readInt();
    130         if (sig != i_RIFF)
    131             throw new IOException("File is not a WAVE file");
    132 
    133         // skip size
    134         in.readInt();
    135         if (in.readInt() != i_WAVE)
    136             throw new IOException("WAVE File does not contain audio");
    137 
    138         readStream = stream;
    139         if (readStream){
    140             audioStream = new AudioStream();
    141             audioData = audioStream;
    142         }else{
    143             audioBuffer = new AudioBuffer();
    144             audioData = audioBuffer;
    145         }
    146 
    147         while (true) {
    148             int type = in.readInt();
    149             int len = in.readInt();
    150 
    151             switch (type) {
    152                 case i_fmt:
    153                     readFormatChunk(len);
    154                     break;
    155                 case i_data:
    156                     // Compute duration based on data chunk size
    157                     duration = len / bytesPerSec;
    158 
    159                     if (readStream) {
    160                         readDataChunkForStream(len);
    161                     } else {
    162                         readDataChunkForBuffer(len);
    163                     }
    164                     return audioData;
    165                 default:
    166                     int skipped = in.skipBytes(len);
    167                     if (skipped <= 0) {
    168                         return null;
    169                     }
    170                     break;
    171             }
    172         }
    173     }
    174 
    175     public Object load(AssetInfo info) throws IOException {
    176         AudioData data;
    177         InputStream inputStream = null;
    178         try {
    179             inputStream = info.openStream();
    180             data = load(inputStream, ((AudioKey)info.getKey()).isStream());
    181             if (data instanceof AudioStream){
    182                 inputStream = null;
    183             }
    184             return data;
    185         } finally {
    186             if (inputStream != null){
    187                 inputStream.close();
    188             }
    189         }
    190     }
    191 }
    192