1 /*
   2  * Copyright (c) 2007, 2013, 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.IOException;
  29 import java.io.InputStream;
  30 
  31 import javax.sound.sampled.AudioFormat;
  32 import javax.sound.sampled.AudioFormat.Encoding;
  33 import javax.sound.sampled.AudioInputStream;
  34 import javax.sound.sampled.AudioSystem;
  35 
  36 /**
  37  * Wavetable oscillator for pre-loaded data.
  38  *
  39  * @author Karl Helgason
  40  */
  41 public final class ModelByteBufferWavetable implements ModelWavetable {
  42 
  43     private class Buffer8PlusInputStream extends InputStream {
  44 
  45         private final boolean bigendian;
  46         private final int framesize_pc;
  47         int pos = 0;
  48         int pos2 = 0;
  49         int markpos = 0;
  50         int markpos2 = 0;
  51 
  52         Buffer8PlusInputStream() {
  53             framesize_pc = format.getFrameSize() / format.getChannels();
  54             bigendian = format.isBigEndian();
  55         }
  56 
  57         @Override
  58         public int read(byte[] b, int off, int len) throws IOException {
  59             int avail = available();
  60             if (avail <= 0)
  61                 return -1;
  62             if (len > avail)
  63                 len = avail;
  64             byte[] buff1 = buffer.array();
  65             byte[] buff2 = buffer8.array();
  66             pos += buffer.arrayOffset();
  67             pos2 += buffer8.arrayOffset();
  68             if (bigendian) {
  69                 for (int i = 0; i < len; i += (framesize_pc + 1)) {
  70                     System.arraycopy(buff1, pos, b, i, framesize_pc);
  71                     System.arraycopy(buff2, pos2, b, i + framesize_pc, 1);
  72                     pos += framesize_pc;
  73                     pos2 += 1;
  74                 }
  75             } else {
  76                 for (int i = 0; i < len; i += (framesize_pc + 1)) {
  77                     System.arraycopy(buff2, pos2, b, i, 1);
  78                     System.arraycopy(buff1, pos, b, i + 1, framesize_pc);
  79                     pos += framesize_pc;
  80                     pos2 += 1;
  81                 }
  82             }
  83             pos -= buffer.arrayOffset();
  84             pos2 -= buffer8.arrayOffset();
  85             return len;
  86         }
  87 
  88         @Override
  89         public long skip(long n) throws IOException {
  90             int avail = available();
  91             if (avail <= 0)
  92                 return -1;
  93             if (n > avail)
  94                 n = avail;
  95             pos += (n / (framesize_pc + 1)) * (framesize_pc);
  96             pos2 += n / (framesize_pc + 1);
  97             return super.skip(n);
  98         }
  99 
 100         @Override
 101         public int read(byte[] b) throws IOException {
 102             return read(b, 0, b.length);
 103         }
 104 
 105         @Override
 106         public int read() throws IOException {
 107             byte[] b = new byte[1];
 108             int ret = read(b, 0, 1);
 109             if (ret == -1)
 110                 return -1;
 111             return 0 & 0xFF;
 112         }
 113 
 114         @Override
 115         public boolean markSupported() {
 116             return true;
 117         }
 118 
 119         @Override
 120         public int available() throws IOException {
 121             return (int)buffer.capacity() + (int)buffer8.capacity() - pos - pos2;
 122         }
 123 
 124         @Override
 125         public synchronized void mark(int readlimit) {
 126             markpos = pos;
 127             markpos2 = pos2;
 128         }
 129 
 130         @Override
 131         public synchronized void reset() throws IOException {
 132             pos = markpos;
 133             pos2 = markpos2;
 134 
 135         }
 136     }
 137 
 138     private float loopStart = -1;
 139     private float loopLength = -1;
 140     private final ModelByteBuffer buffer;
 141     private ModelByteBuffer buffer8 = null;
 142     private AudioFormat format = null;
 143     private float pitchcorrection = 0;
 144     private float attenuation = 0;
 145     private int loopType = LOOP_TYPE_OFF;
 146 
 147     public ModelByteBufferWavetable(ModelByteBuffer buffer) {
 148         this.buffer = buffer;
 149     }
 150 
 151     public ModelByteBufferWavetable(ModelByteBuffer buffer,
 152             float pitchcorrection) {
 153         this.buffer = buffer;
 154         this.pitchcorrection = pitchcorrection;
 155     }
 156 
 157     public ModelByteBufferWavetable(ModelByteBuffer buffer, AudioFormat format) {
 158         this.format = format;
 159         this.buffer = buffer;
 160     }
 161 
 162     public ModelByteBufferWavetable(ModelByteBuffer buffer, AudioFormat format,
 163             float pitchcorrection) {
 164         this.format = format;
 165         this.buffer = buffer;
 166         this.pitchcorrection = pitchcorrection;
 167     }
 168 
 169     public void set8BitExtensionBuffer(ModelByteBuffer buffer) {
 170         buffer8 = buffer;
 171     }
 172 
 173     public ModelByteBuffer get8BitExtensionBuffer() {
 174         return buffer8;
 175     }
 176 
 177     public ModelByteBuffer getBuffer() {
 178         return buffer;
 179     }
 180 
 181     public AudioFormat getFormat() {
 182         if (format == null) {
 183             if (buffer == null)
 184                 return null;
 185             InputStream is = buffer.getInputStream();
 186             AudioFormat format = null;
 187             try {
 188                 format = AudioSystem.getAudioFileFormat(is).getFormat();
 189             } catch (Exception e) {
 190                 //e.printStackTrace();
 191             }
 192             try {
 193                 is.close();
 194             } catch (IOException e) {
 195                 //e.printStackTrace();
 196             }
 197             return format;
 198         }
 199         return format;
 200     }
 201 
 202     @Override
 203     public AudioFloatInputStream openStream() {
 204         if (buffer == null)
 205             return null;
 206         if (format == null) {
 207             InputStream is = buffer.getInputStream();
 208             AudioInputStream ais = null;
 209             try {
 210                 ais = AudioSystem.getAudioInputStream(is);
 211             } catch (Exception e) {
 212                 //e.printStackTrace();
 213                 return null;
 214             }
 215             return AudioFloatInputStream.getInputStream(ais);
 216         }
 217         if (buffer.array() == null) {
 218             return AudioFloatInputStream.getInputStream(new AudioInputStream(
 219                     buffer.getInputStream(), format,
 220                     buffer.capacity() / format.getFrameSize()));
 221         }
 222         if (buffer8 != null) {
 223             if (format.getEncoding().equals(Encoding.PCM_SIGNED)
 224                     || format.getEncoding().equals(Encoding.PCM_UNSIGNED)) {
 225                 InputStream is = new Buffer8PlusInputStream();
 226                 AudioFormat format2 = new AudioFormat(
 227                         format.getEncoding(),
 228                         format.getSampleRate(),
 229                         format.getSampleSizeInBits() + 8,
 230                         format.getChannels(),
 231                         format.getFrameSize() + (1 * format.getChannels()),
 232                         format.getFrameRate(),
 233                         format.isBigEndian());
 234 
 235                 AudioInputStream ais = new AudioInputStream(is, format2,
 236                         buffer.capacity() / format.getFrameSize());
 237                 return AudioFloatInputStream.getInputStream(ais);
 238             }
 239         }
 240         return AudioFloatInputStream.getInputStream(format, buffer.array(),
 241                 (int)buffer.arrayOffset(), (int)buffer.capacity());
 242     }
 243 
 244     @Override
 245     public int getChannels() {
 246         return getFormat().getChannels();
 247     }
 248 
 249     @Override
 250     public ModelOscillatorStream open(float samplerate) {
 251         // ModelWavetableOscillator doesn't support ModelOscillatorStream
 252         return null;
 253     }
 254 
 255     // attenuation is in cB
 256     @Override
 257     public float getAttenuation() {
 258         return attenuation;
 259     }
 260     // attenuation is in cB
 261     public void setAttenuation(float attenuation) {
 262         this.attenuation = attenuation;
 263     }
 264 
 265     @Override
 266     public float getLoopLength() {
 267         return loopLength;
 268     }
 269 
 270     public void setLoopLength(float loopLength) {
 271         this.loopLength = loopLength;
 272     }
 273 
 274     @Override
 275     public float getLoopStart() {
 276         return loopStart;
 277     }
 278 
 279     public void setLoopStart(float loopStart) {
 280         this.loopStart = loopStart;
 281     }
 282 
 283     public void setLoopType(int loopType) {
 284         this.loopType = loopType;
 285     }
 286 
 287     @Override
 288     public int getLoopType() {
 289         return loopType;
 290     }
 291 
 292     @Override
 293     public float getPitchcorrection() {
 294         return pitchcorrection;
 295     }
 296 
 297     public void setPitchcorrection(float pitchcorrection) {
 298         this.pitchcorrection = pitchcorrection;
 299     }
 300 }