Since putting in the Echo, I’ve started to get dropouts. I’m concerned, because this represents the addition of only a few operations per frame. If something as simple as this causes problems, I worry that there is no “room” to add more DSP like putting in even a simple filter to deal with the aliasing problems.
Also, the needed latency is already kind of high compared to what I’ve heard can be done with audio. So I want to track down the culprits.
(Could be something like the GUI, too, taking up CPU. I redraw just the play-area display with every mouse event. Could try and limit the refresh further, but the mouse can move from end to end so fast, that trails are left behind. Idea, maybe try eliminating the keyboard overlay and see if that helps. I DO do a lot of precalculating to speed up the keyboard display, though, so all the rectangle are ready to go.)
To clearly hear the dropouts, try an echo of length 5000 msec (5 seconds). They are rarer with more normal echo lengths (e.g. 500 msec). Why should the size matter? Because more RAM is being used up to hold the echo data?
By the way, it is possible to get some rather interesting and radical sounds by setting the feedback to 99 and the echo length to 50 or shorter, and 100% Sawtooth.
import java.util.concurrent.LinkedBlockingQueue;
public class Echo {
private final LinkedBlockingQueue<Double> samples;
private final double feedback;
private final int PADDING = 44100;
public double getFeedback() { return feedback;}
public int getEchoLength()
{
return samples.size() - PADDING;
}
Echo(int echoLength, double feedback)
{
this.feedback = Math.min(1.0,
Math.max(0, feedback));
samples = new LinkedBlockingQueue<Double>(
echoLength + PADDING);
for (int i = 0; i < (echoLength - 1); i++)
{
samples.add(0.0);
}
}
public double tick(double audioVal){
double soundVal = samples.poll();
samples.add(soundVal * feedback + audioVal);
return soundVal;
}
}
Next is the Read() for the TargetDelayLine which provides the audio data. I’m wondering if I should structure it to work on more than a single frame at a go. Maybe getting blocks of 256 samples, for instance, would make more sense. The routine which reads the TDL is currently calling for 22050 bytes per read. Any less makes the dropout problem worse.
public int read(byte[] buffer, int idxBuf, int bytesToRead)
{
if (!running) return 0;
int frToIdx = bytesToRead/4;
int bufferIdx = idxBuf;
while (frToIdx-- > 0)
{
pitchVal = pitchCtrl.tick();
av1 = sineTable.get(pitchVal);
av2 = currentWT.get(pitchVal);
toneVal = toneCtrl.tick();
audioVal = av1 * (1 - toneVal) + av2 * toneVal;
double mouseVol = volumeCtrl.tick();
audioVal *= mouseVol;
audioVal += echo.tick(audioVal) * mouseVol;
audioVal *= 32767;
audioVal = Math.max(-32700, Math.min(32700, audioVal));
buffer[bufferIdx++] = (byte)((int)(audioVal) & 0xff);
buffer[bufferIdx++] = (byte)((int)audioVal >> 8);
buffer[bufferIdx++] = (byte)((int)(audioVal) & 0xff);
buffer[bufferIdx++] = (byte)((int)audioVal >> 8);
}
return (bufferIdx - idxBuf);
}