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 
  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
  79         public void noteOn(MidiChannel channel, VoiceStatus voice,
  80                            int noteNumber, int velocity) {
  81         }
  82 
  83         @Override
  84         public void noteOff(int velocity) {
  85             noteOff_flag = true;
  86         }
  87 
  88         @Override
  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
 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 
 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 
 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
 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
 383         public void close() throws IOException {
 384             stream.close();
 385         }
 386     }
 387 
 388     public abstract int getPadding();
 389 
 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
 395     public final SoftResamplerStreamer openStreamer() {
 396         return new ModelAbstractResamplerStream();
 397     }
 398 }