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 
Buffer8PlusInputStream()52         Buffer8PlusInputStream() {
53             framesize_pc = format.getFrameSize() / format.getChannels();
54             bigendian = format.isBigEndian();
55         }
56 
57         @Override
read(byte[] b, int off, int len)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
skip(long n)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
read(byte[] b)101         public int read(byte[] b) throws IOException {
102             return read(b, 0, b.length);
103         }
104 
105         @Override
read()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
markSupported()115         public boolean markSupported() {
116             return true;
117         }
118 
119         @Override
available()120         public int available() throws IOException {
121             return (int)buffer.capacity() + (int)buffer8.capacity() - pos - pos2;
122         }
123 
124         @Override
mark(int readlimit)125         public synchronized void mark(int readlimit) {
126             markpos = pos;
127             markpos2 = pos2;
128         }
129 
130         @Override
reset()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 
ModelByteBufferWavetable(ModelByteBuffer buffer)147     public ModelByteBufferWavetable(ModelByteBuffer buffer) {
148         this.buffer = buffer;
149     }
150 
ModelByteBufferWavetable(ModelByteBuffer buffer, float pitchcorrection)151     public ModelByteBufferWavetable(ModelByteBuffer buffer,
152             float pitchcorrection) {
153         this.buffer = buffer;
154         this.pitchcorrection = pitchcorrection;
155     }
156 
ModelByteBufferWavetable(ModelByteBuffer buffer, AudioFormat format)157     public ModelByteBufferWavetable(ModelByteBuffer buffer, AudioFormat format) {
158         this.format = format;
159         this.buffer = buffer;
160     }
161 
ModelByteBufferWavetable(ModelByteBuffer buffer, AudioFormat format, float pitchcorrection)162     public ModelByteBufferWavetable(ModelByteBuffer buffer, AudioFormat format,
163             float pitchcorrection) {
164         this.format = format;
165         this.buffer = buffer;
166         this.pitchcorrection = pitchcorrection;
167     }
168 
set8BitExtensionBuffer(ModelByteBuffer buffer)169     public void set8BitExtensionBuffer(ModelByteBuffer buffer) {
170         buffer8 = buffer;
171     }
172 
get8BitExtensionBuffer()173     public ModelByteBuffer get8BitExtensionBuffer() {
174         return buffer8;
175     }
176 
getBuffer()177     public ModelByteBuffer getBuffer() {
178         return buffer;
179     }
180 
getFormat()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
openStream()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
getChannels()245     public int getChannels() {
246         return getFormat().getChannels();
247     }
248 
249     @Override
open(float samplerate)250     public ModelOscillatorStream open(float samplerate) {
251         // ModelWavetableOscillator doesn't support ModelOscillatorStream
252         return null;
253     }
254 
255     // attenuation is in cB
256     @Override
getAttenuation()257     public float getAttenuation() {
258         return attenuation;
259     }
260     // attenuation is in cB
setAttenuation(float attenuation)261     public void setAttenuation(float attenuation) {
262         this.attenuation = attenuation;
263     }
264 
265     @Override
getLoopLength()266     public float getLoopLength() {
267         return loopLength;
268     }
269 
setLoopLength(float loopLength)270     public void setLoopLength(float loopLength) {
271         this.loopLength = loopLength;
272     }
273 
274     @Override
getLoopStart()275     public float getLoopStart() {
276         return loopStart;
277     }
278 
setLoopStart(float loopStart)279     public void setLoopStart(float loopStart) {
280         this.loopStart = loopStart;
281     }
282 
setLoopType(int loopType)283     public void setLoopType(int loopType) {
284         this.loopType = loopType;
285     }
286 
287     @Override
getLoopType()288     public int getLoopType() {
289         return loopType;
290     }
291 
292     @Override
getPitchcorrection()293     public float getPitchcorrection() {
294         return pitchcorrection;
295     }
296 
setPitchcorrection(float pitchcorrection)297     public void setPitchcorrection(float pitchcorrection) {
298         this.pitchcorrection = pitchcorrection;
299     }
300 }
301