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 }