|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
package com.sun.media.sound; |
|
|
|
import java.io.IOException; |
|
import java.util.Arrays; |
|
|
|
import javax.sound.midi.MidiChannel; |
|
import javax.sound.midi.VoiceStatus; |
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public abstract class SoftAbstractResampler implements SoftResampler { |
|
|
|
private class ModelAbstractResamplerStream implements SoftResamplerStreamer { |
|
|
|
AudioFloatInputStream stream; |
|
boolean stream_eof = false; |
|
int loopmode; |
|
boolean loopdirection = true; |
|
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.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) { |
|
|
|
stream.reset(); |
|
ix[0] += streampos - (sector_loopstart * sector_size); |
|
sector_pos = sector_loopstart; |
|
streampos = sector_pos * sector_size; |
|
|
|
|
|
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) { |
|
|
|
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) { |
|
|
|
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(); |
|
} |
|
} |