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 }