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.util.Arrays;
30 
31 import javax.sound.midi.MidiChannel;
32 import javax.sound.midi.VoiceStatus;
33 
34 /**
35  * Abstract resampler class.
36  *
37  * @author Karl Helgason
38  */
39 public abstract class SoftAbstractResampler implements SoftResampler {
40 
41     private class ModelAbstractResamplerStream implements SoftResamplerStreamer {
42 
43         AudioFloatInputStream stream;
44         boolean stream_eof = false;
45         int loopmode;
46         boolean loopdirection = true; // true = forward
47         float loopstart;
48         float looplen;
49         float target_pitch;
50         float[] current_pitch = new float[1];
51         boolean started;
52         boolean eof;
53         int sector_pos = 0;
54         int sector_size = 400;
55         int sector_loopstart = -1;
56         boolean markset = false;
57         int marklimit = 0;
58         int streampos = 0;
59         int nrofchannels = 2;
60         boolean noteOff_flag = false;
61         float[][] ibuffer;
62         boolean ibuffer_order = true;
63         float[] sbuffer;
64         int pad;
65         int pad2;
66         float[] ix = new float[1];
67         int[] ox = new int[1];
68         float samplerateconv = 1;
69         float pitchcorrection = 0;
70 
ModelAbstractResamplerStream()71         ModelAbstractResamplerStream() {
72             pad = getPadding();
73             pad2 = getPadding() * 2;
74             ibuffer = new float[2][sector_size + pad2];
75             ibuffer_order = true;
76         }
77 
78         @Override
noteOn(MidiChannel channel, VoiceStatus voice, int noteNumber, int velocity)79         public void noteOn(MidiChannel channel, VoiceStatus voice,
80                            int noteNumber, int velocity) {
81         }
82 
83         @Override
noteOff(int velocity)84         public void noteOff(int velocity) {
85             noteOff_flag = true;
86         }
87 
88         @Override
open(ModelWavetable osc, float outputsamplerate)89         public void open(ModelWavetable osc, float outputsamplerate)
90                 throws IOException {
91 
92             eof = false;
93             nrofchannels = osc.getChannels();
94             if (ibuffer.length < nrofchannels) {
95                 ibuffer = new float[nrofchannels][sector_size + pad2];
96             }
97 
98             stream = osc.openStream();
99             streampos = 0;
100             stream_eof = false;
101             pitchcorrection = osc.getPitchcorrection();
102             samplerateconv
103                     = stream.getFormat().getSampleRate() / outputsamplerate;
104             looplen = osc.getLoopLength();
105             loopstart = osc.getLoopStart();
106             sector_loopstart = (int) (loopstart / sector_size);
107             sector_loopstart = sector_loopstart - 1;
108 
109             sector_pos = 0;
110 
111             if (sector_loopstart < 0)
112                 sector_loopstart = 0;
113             started = false;
114             loopmode = osc.getLoopType();
115 
116             if (loopmode != 0) {
117                 markset = false;
118                 marklimit = nrofchannels * (int) (looplen + pad2 + 1);
119             } else
120                 markset = true;
121             // loopmode = 0;
122 
123             target_pitch = samplerateconv;
124             current_pitch[0] = samplerateconv;
125 
126             ibuffer_order = true;
127             loopdirection = true;
128             noteOff_flag = false;
129 
130             for (int i = 0; i < nrofchannels; i++)
131                 Arrays.fill(ibuffer[i], sector_size, sector_size + pad2, 0);
132             ix[0] = pad;
133             eof = false;
134 
135             ix[0] = sector_size + pad;
136             sector_pos = -1;
137             streampos = -sector_size;
138 
139             nextBuffer();
140         }
141 
142         @Override
setPitch(float pitch)143         public void setPitch(float pitch) {
144             /*
145             this.pitch = (float) Math.pow(2f,
146             (pitchcorrection + pitch) / 1200.0f)
147              * samplerateconv;
148              */
149             this.target_pitch = (float)Math.exp(
150                     (pitchcorrection + pitch) * (Math.log(2.0) / 1200.0))
151                 * samplerateconv;
152 
153             if (!started)
154                 current_pitch[0] = this.target_pitch;
155         }
156 
nextBuffer()157         public void nextBuffer() throws IOException {
158             if (ix[0] < pad) {
159                 if (markset) {
160                     // reset to target sector
161                     stream.reset();
162                     ix[0] += streampos - (sector_loopstart * sector_size);
163                     sector_pos = sector_loopstart;
164                     streampos = sector_pos * sector_size;
165 
166                     // and go one sector backward
167                     ix[0] += sector_size;
168                     sector_pos -= 1;
169                     streampos -= sector_size;
170                     stream_eof = false;
171                 }
172             }
173 
174             if (ix[0] >= sector_size + pad) {
175                 if (stream_eof) {
176                     eof = true;
177                     return;
178                 }
179             }
180 
181             if (ix[0] >= sector_size * 4 + pad) {
182                 int skips = (int)((ix[0] - sector_size * 4 + pad) / sector_size);
183                 ix[0] -= sector_size * skips;
184                 sector_pos += skips;
185                 streampos += sector_size * skips;
186                 stream.skip(sector_size * skips);
187             }
188 
189             while (ix[0] >= sector_size + pad) {
190                 if (!markset) {
191                     if (sector_pos + 1 == sector_loopstart) {
192                         stream.mark(marklimit);
193                         markset = true;
194                     }
195                 }
196                 ix[0] -= sector_size;
197                 sector_pos++;
198                 streampos += sector_size;
199 
200                 for (int c = 0; c < nrofchannels; c++) {
201                     float[] cbuffer = ibuffer[c];
202                     for (int i = 0; i < pad2; i++)
203                         cbuffer[i] = cbuffer[i + sector_size];
204                 }
205 
206                 int ret;
207                 if (nrofchannels == 1)
208                     ret = stream.read(ibuffer[0], pad2, sector_size);
209                 else {
210                     int slen = sector_size * nrofchannels;
211                     if (sbuffer == null || sbuffer.length < slen)
212                         sbuffer = new float[slen];
213                     int sret = stream.read(sbuffer, 0, slen);
214                     if (sret == -1)
215                         ret = -1;
216                     else {
217                         ret = sret / nrofchannels;
218                         for (int i = 0; i < nrofchannels; i++) {
219                             float[] buff = ibuffer[i];
220                             int ix = i;
221                             int ix_step = nrofchannels;
222                             int ox = pad2;
223                             for (int j = 0; j < ret; j++, ix += ix_step, ox++)
224                                 buff[ox] = sbuffer[ix];
225                         }
226                     }
227 
228                 }
229 
230                 if (ret == -1) {
231                     ret = 0;
232                     stream_eof = true;
233                     for (int i = 0; i < nrofchannels; i++)
234                         Arrays.fill(ibuffer[i], pad2, pad2 + sector_size, 0f);
235                     return;
236                 }
237                 if (ret != sector_size) {
238                     for (int i = 0; i < nrofchannels; i++)
239                         Arrays.fill(ibuffer[i], pad2 + ret, pad2 + sector_size, 0f);
240                 }
241 
242                 ibuffer_order = true;
243 
244             }
245 
246         }
247 
reverseBuffers()248         public void reverseBuffers() {
249             ibuffer_order = !ibuffer_order;
250             for (int c = 0; c < nrofchannels; c++) {
251                 float[] cbuff = ibuffer[c];
252                 int len = cbuff.length - 1;
253                 int len2 = cbuff.length / 2;
254                 for (int i = 0; i < len2; i++) {
255                     float x = cbuff[i];
256                     cbuff[i] = cbuff[len - i];
257                     cbuff[len - i] = x;
258                 }
259             }
260         }
261 
262         @Override
read(float[][] buffer, int offset, int len)263         public int read(float[][] buffer, int offset, int len)
264                 throws IOException {
265 
266             if (eof)
267                 return -1;
268 
269             if (noteOff_flag)
270                 if ((loopmode & 2) != 0)
271                     if (loopdirection)
272                         loopmode = 0;
273 
274 
275             float pitchstep = (target_pitch - current_pitch[0]) / len;
276             float[] current_pitch = this.current_pitch;
277             started = true;
278 
279             int[] ox = this.ox;
280             ox[0] = offset;
281             int ox_end = len + offset;
282 
283             float ixend = sector_size + pad;
284             if (!loopdirection)
285                 ixend = pad;
286             while (ox[0] != ox_end) {
287                 nextBuffer();
288                 if (!loopdirection) {
289                     // If we are in backward playing part of pingpong
290                     // or reverse loop
291 
292                     if (streampos < (loopstart + pad)) {
293                         ixend = loopstart - streampos + pad2;
294                         if (ix[0] <= ixend) {
295                             if ((loopmode & 4) != 0) {
296                                 // Ping pong loop, change loopdirection
297                                 loopdirection = true;
298                                 ixend = sector_size + pad;
299                                 continue;
300                             }
301 
302                             ix[0] += looplen;
303                             ixend = pad;
304                             continue;
305                         }
306                     }
307 
308                     if (ibuffer_order != loopdirection)
309                         reverseBuffers();
310 
311                     ix[0] = (sector_size + pad2) - ix[0];
312                     ixend = (sector_size + pad2) - ixend;
313                     ixend++;
314 
315                     float bak_ix = ix[0];
316                     int bak_ox = ox[0];
317                     float bak_pitch = current_pitch[0];
318                     for (int i = 0; i < nrofchannels; i++) {
319                         if (buffer[i] != null) {
320                             ix[0] = bak_ix;
321                             ox[0] = bak_ox;
322                             current_pitch[0] = bak_pitch;
323                             interpolate(ibuffer[i], ix, ixend, current_pitch,
324                                     pitchstep, buffer[i], ox, ox_end);
325                         }
326                     }
327 
328                     ix[0] = (sector_size + pad2) - ix[0];
329                     ixend--;
330                     ixend = (sector_size + pad2) - ixend;
331 
332                     if (eof) {
333                         current_pitch[0] = this.target_pitch;
334                         return ox[0] - offset;
335                     }
336 
337                     continue;
338                 }
339                 if (loopmode != 0) {
340                     if (streampos + sector_size > (looplen + loopstart + pad)) {
341                         ixend = loopstart + looplen - streampos + pad2;
342                         if (ix[0] >= ixend) {
343                             if ((loopmode & 4) != 0 || (loopmode & 8) != 0) {
344                                 // Ping pong or revese loop, change loopdirection
345                                 loopdirection = false;
346                                 ixend = pad;
347                                 continue;
348                             }
349                             ixend = sector_size + pad;
350                             ix[0] -= looplen;
351                             continue;
352                         }
353                     }
354                 }
355 
356                 if (ibuffer_order != loopdirection)
357                     reverseBuffers();
358 
359                 float bak_ix = ix[0];
360                 int bak_ox = ox[0];
361                 float bak_pitch = current_pitch[0];
362                 for (int i = 0; i < nrofchannels; i++) {
363                     if (buffer[i] != null) {
364                         ix[0] = bak_ix;
365                         ox[0] = bak_ox;
366                         current_pitch[0] = bak_pitch;
367                         interpolate(ibuffer[i], ix, ixend, current_pitch,
368                                 pitchstep, buffer[i], ox, ox_end);
369                     }
370                 }
371 
372                 if (eof) {
373                     current_pitch[0] = this.target_pitch;
374                     return ox[0] - offset;
375                 }
376             }
377 
378             current_pitch[0] = this.target_pitch;
379             return len;
380         }
381 
382         @Override
close()383         public void close() throws IOException {
384             stream.close();
385         }
386     }
387 
getPadding()388     public abstract int getPadding();
389 
interpolate(float[] in, float[] in_offset, float in_end, float[] pitch, float pitchstep, float[] out, int[] out_offset, int out_end)390     public abstract void interpolate(float[] in, float[] in_offset,
391             float in_end, float[] pitch, float pitchstep, float[] out,
392             int[] out_offset, int out_end);
393 
394     @Override
openStreamer()395     public final SoftResamplerStreamer openStreamer() {
396         return new ModelAbstractResamplerStream();
397     }
398 }
399