391 lines
13 KiB
Java
391 lines
13 KiB
Java
/*
|
|
* Copyright (c) 2007, 2013, Oracle and/or its affiliates. All rights reserved.
|
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
|
*
|
|
* This code is free software; you can redistribute it and/or modify it
|
|
* under the terms of the GNU General Public License version 2 only, as
|
|
* published by the Free Software Foundation. Oracle designates this
|
|
* particular file as subject to the "Classpath" exception as provided
|
|
* by Oracle in the LICENSE file that accompanied this code.
|
|
*
|
|
* This code is distributed in the hope that it will be useful, but WITHOUT
|
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
|
* version 2 for more details (a copy is included in the LICENSE file that
|
|
* accompanied this code).
|
|
*
|
|
* You should have received a copy of the GNU General Public License version
|
|
* 2 along with this work; if not, write to the Free Software Foundation,
|
|
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
*
|
|
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
|
* or visit www.oracle.com if you need additional information or have any
|
|
* questions.
|
|
*/
|
|
package com.sun.media.sound;
|
|
|
|
import java.io.IOException;
|
|
import java.util.Arrays;
|
|
|
|
import javax.sound.midi.MidiChannel;
|
|
import javax.sound.midi.VoiceStatus;
|
|
|
|
/**
|
|
* Abstract resampler class.
|
|
*
|
|
* @author Karl Helgason
|
|
*/
|
|
public abstract class SoftAbstractResampler implements SoftResampler {
|
|
|
|
private class ModelAbstractResamplerStream implements SoftResamplerStreamer {
|
|
|
|
AudioFloatInputStream stream;
|
|
boolean stream_eof = false;
|
|
int loopmode;
|
|
boolean loopdirection = true; // true = forward
|
|
float loopstart;
|
|
float looplen;
|
|
float target_pitch;
|
|
float[] current_pitch = new float[1];
|
|
boolean started;
|
|
boolean eof;
|
|
int sector_pos = 0;
|
|
int sector_size = 400;
|
|
int sector_loopstart = -1;
|
|
boolean markset = false;
|
|
int marklimit = 0;
|
|
int streampos = 0;
|
|
int nrofchannels = 2;
|
|
boolean noteOff_flag = false;
|
|
float[][] ibuffer;
|
|
boolean ibuffer_order = true;
|
|
float[] sbuffer;
|
|
int pad;
|
|
int pad2;
|
|
float[] ix = new float[1];
|
|
int[] ox = new int[1];
|
|
float samplerateconv = 1;
|
|
float pitchcorrection = 0;
|
|
|
|
ModelAbstractResamplerStream() {
|
|
pad = getPadding();
|
|
pad2 = getPadding() * 2;
|
|
ibuffer = new float[2][sector_size + pad2];
|
|
ibuffer_order = true;
|
|
}
|
|
|
|
public void noteOn(MidiChannel channel, VoiceStatus voice,
|
|
int noteNumber, int velocity) {
|
|
}
|
|
|
|
public void noteOff(int velocity) {
|
|
noteOff_flag = true;
|
|
}
|
|
|
|
public void open(ModelWavetable osc, float outputsamplerate)
|
|
throws IOException {
|
|
|
|
eof = false;
|
|
nrofchannels = osc.getChannels();
|
|
if (ibuffer.length < nrofchannels) {
|
|
ibuffer = new float[nrofchannels][sector_size + pad2];
|
|
}
|
|
|
|
stream = osc.openStream();
|
|
streampos = 0;
|
|
stream_eof = false;
|
|
pitchcorrection = osc.getPitchcorrection();
|
|
samplerateconv
|
|
= stream.getFormat().getSampleRate() / outputsamplerate;
|
|
looplen = osc.getLoopLength();
|
|
loopstart = osc.getLoopStart();
|
|
sector_loopstart = (int) (loopstart / sector_size);
|
|
sector_loopstart = sector_loopstart - 1;
|
|
|
|
sector_pos = 0;
|
|
|
|
if (sector_loopstart < 0)
|
|
sector_loopstart = 0;
|
|
started = false;
|
|
loopmode = osc.getLoopType();
|
|
|
|
if (loopmode != 0) {
|
|
markset = false;
|
|
marklimit = nrofchannels * (int) (looplen + pad2 + 1);
|
|
} else
|
|
markset = true;
|
|
// loopmode = 0;
|
|
|
|
target_pitch = samplerateconv;
|
|
current_pitch[0] = samplerateconv;
|
|
|
|
ibuffer_order = true;
|
|
loopdirection = true;
|
|
noteOff_flag = false;
|
|
|
|
for (int i = 0; i < nrofchannels; i++)
|
|
Arrays.fill(ibuffer[i], sector_size, sector_size + pad2, 0);
|
|
ix[0] = pad;
|
|
eof = false;
|
|
|
|
ix[0] = sector_size + pad;
|
|
sector_pos = -1;
|
|
streampos = -sector_size;
|
|
|
|
nextBuffer();
|
|
}
|
|
|
|
public void setPitch(float pitch) {
|
|
/*
|
|
this.pitch = (float) Math.pow(2f,
|
|
(pitchcorrection + pitch) / 1200.0f)
|
|
* samplerateconv;
|
|
*/
|
|
this.target_pitch = (float)Math.exp(
|
|
(pitchcorrection + pitch) * (Math.log(2.0) / 1200.0))
|
|
* samplerateconv;
|
|
|
|
if (!started)
|
|
current_pitch[0] = this.target_pitch;
|
|
}
|
|
|
|
public void nextBuffer() throws IOException {
|
|
if (ix[0] < pad) {
|
|
if (markset) {
|
|
// reset to target sector
|
|
stream.reset();
|
|
ix[0] += streampos - (sector_loopstart * sector_size);
|
|
sector_pos = sector_loopstart;
|
|
streampos = sector_pos * sector_size;
|
|
|
|
// and go one sector backward
|
|
ix[0] += sector_size;
|
|
sector_pos -= 1;
|
|
streampos -= sector_size;
|
|
stream_eof = false;
|
|
}
|
|
}
|
|
|
|
if (ix[0] >= sector_size + pad) {
|
|
if (stream_eof) {
|
|
eof = true;
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (ix[0] >= sector_size * 4 + pad) {
|
|
int skips = (int)((ix[0] - sector_size * 4 + pad) / sector_size);
|
|
ix[0] -= sector_size * skips;
|
|
sector_pos += skips;
|
|
streampos += sector_size * skips;
|
|
stream.skip(sector_size * skips);
|
|
}
|
|
|
|
while (ix[0] >= sector_size + pad) {
|
|
if (!markset) {
|
|
if (sector_pos + 1 == sector_loopstart) {
|
|
stream.mark(marklimit);
|
|
markset = true;
|
|
}
|
|
}
|
|
ix[0] -= sector_size;
|
|
sector_pos++;
|
|
streampos += sector_size;
|
|
|
|
for (int c = 0; c < nrofchannels; c++) {
|
|
float[] cbuffer = ibuffer[c];
|
|
for (int i = 0; i < pad2; i++)
|
|
cbuffer[i] = cbuffer[i + sector_size];
|
|
}
|
|
|
|
int ret;
|
|
if (nrofchannels == 1)
|
|
ret = stream.read(ibuffer[0], pad2, sector_size);
|
|
else {
|
|
int slen = sector_size * nrofchannels;
|
|
if (sbuffer == null || sbuffer.length < slen)
|
|
sbuffer = new float[slen];
|
|
int sret = stream.read(sbuffer, 0, slen);
|
|
if (sret == -1)
|
|
ret = -1;
|
|
else {
|
|
ret = sret / nrofchannels;
|
|
for (int i = 0; i < nrofchannels; i++) {
|
|
float[] buff = ibuffer[i];
|
|
int ix = i;
|
|
int ix_step = nrofchannels;
|
|
int ox = pad2;
|
|
for (int j = 0; j < ret; j++, ix += ix_step, ox++)
|
|
buff[ox] = sbuffer[ix];
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
if (ret == -1) {
|
|
ret = 0;
|
|
stream_eof = true;
|
|
for (int i = 0; i < nrofchannels; i++)
|
|
Arrays.fill(ibuffer[i], pad2, pad2 + sector_size, 0f);
|
|
return;
|
|
}
|
|
if (ret != sector_size) {
|
|
for (int i = 0; i < nrofchannels; i++)
|
|
Arrays.fill(ibuffer[i], pad2 + ret, pad2 + sector_size, 0f);
|
|
}
|
|
|
|
ibuffer_order = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
public void reverseBuffers() {
|
|
ibuffer_order = !ibuffer_order;
|
|
for (int c = 0; c < nrofchannels; c++) {
|
|
float[] cbuff = ibuffer[c];
|
|
int len = cbuff.length - 1;
|
|
int len2 = cbuff.length / 2;
|
|
for (int i = 0; i < len2; i++) {
|
|
float x = cbuff[i];
|
|
cbuff[i] = cbuff[len - i];
|
|
cbuff[len - i] = x;
|
|
}
|
|
}
|
|
}
|
|
|
|
public int read(float[][] buffer, int offset, int len)
|
|
throws IOException {
|
|
|
|
if (eof)
|
|
return -1;
|
|
|
|
if (noteOff_flag)
|
|
if ((loopmode & 2) != 0)
|
|
if (loopdirection)
|
|
loopmode = 0;
|
|
|
|
|
|
float pitchstep = (target_pitch - current_pitch[0]) / len;
|
|
float[] current_pitch = this.current_pitch;
|
|
started = true;
|
|
|
|
int[] ox = this.ox;
|
|
ox[0] = offset;
|
|
int ox_end = len + offset;
|
|
|
|
float ixend = sector_size + pad;
|
|
if (!loopdirection)
|
|
ixend = pad;
|
|
while (ox[0] != ox_end) {
|
|
nextBuffer();
|
|
if (!loopdirection) {
|
|
// If we are in backward playing part of pingpong
|
|
// or reverse loop
|
|
|
|
if (streampos < (loopstart + pad)) {
|
|
ixend = loopstart - streampos + pad2;
|
|
if (ix[0] <= ixend) {
|
|
if ((loopmode & 4) != 0) {
|
|
// Ping pong loop, change loopdirection
|
|
loopdirection = true;
|
|
ixend = sector_size + pad;
|
|
continue;
|
|
}
|
|
|
|
ix[0] += looplen;
|
|
ixend = pad;
|
|
continue;
|
|
}
|
|
}
|
|
|
|
if (ibuffer_order != loopdirection)
|
|
reverseBuffers();
|
|
|
|
ix[0] = (sector_size + pad2) - ix[0];
|
|
ixend = (sector_size + pad2) - ixend;
|
|
ixend++;
|
|
|
|
float bak_ix = ix[0];
|
|
int bak_ox = ox[0];
|
|
float bak_pitch = current_pitch[0];
|
|
for (int i = 0; i < nrofchannels; i++) {
|
|
if (buffer[i] != null) {
|
|
ix[0] = bak_ix;
|
|
ox[0] = bak_ox;
|
|
current_pitch[0] = bak_pitch;
|
|
interpolate(ibuffer[i], ix, ixend, current_pitch,
|
|
pitchstep, buffer[i], ox, ox_end);
|
|
}
|
|
}
|
|
|
|
ix[0] = (sector_size + pad2) - ix[0];
|
|
ixend--;
|
|
ixend = (sector_size + pad2) - ixend;
|
|
|
|
if (eof) {
|
|
current_pitch[0] = this.target_pitch;
|
|
return ox[0] - offset;
|
|
}
|
|
|
|
continue;
|
|
}
|
|
if (loopmode != 0) {
|
|
if (streampos + sector_size > (looplen + loopstart + pad)) {
|
|
ixend = loopstart + looplen - streampos + pad2;
|
|
if (ix[0] >= ixend) {
|
|
if ((loopmode & 4) != 0 || (loopmode & 8) != 0) {
|
|
// Ping pong or revese loop, change loopdirection
|
|
loopdirection = false;
|
|
ixend = pad;
|
|
continue;
|
|
}
|
|
ixend = sector_size + pad;
|
|
ix[0] -= looplen;
|
|
continue;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (ibuffer_order != loopdirection)
|
|
reverseBuffers();
|
|
|
|
float bak_ix = ix[0];
|
|
int bak_ox = ox[0];
|
|
float bak_pitch = current_pitch[0];
|
|
for (int i = 0; i < nrofchannels; i++) {
|
|
if (buffer[i] != null) {
|
|
ix[0] = bak_ix;
|
|
ox[0] = bak_ox;
|
|
current_pitch[0] = bak_pitch;
|
|
interpolate(ibuffer[i], ix, ixend, current_pitch,
|
|
pitchstep, buffer[i], ox, ox_end);
|
|
}
|
|
}
|
|
|
|
if (eof) {
|
|
current_pitch[0] = this.target_pitch;
|
|
return ox[0] - offset;
|
|
}
|
|
}
|
|
|
|
current_pitch[0] = this.target_pitch;
|
|
return len;
|
|
}
|
|
|
|
public void close() throws IOException {
|
|
stream.close();
|
|
}
|
|
}
|
|
|
|
public abstract int getPadding();
|
|
|
|
public abstract void interpolate(float[] in, float[] in_offset,
|
|
float in_end, float[] pitch, float pitchstep, float[] out,
|
|
int[] out_offset, int out_end);
|
|
|
|
public final SoftResamplerStreamer openStreamer() {
|
|
return new ModelAbstractResamplerStream();
|
|
}
|
|
}
|