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