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