Here is a function I use to find a suitable loop in a sample. It uses a moving window to find two sample points that have similar surrounding values.
You’ll have to download gervill.jar to get the AudioFloatConverter class or provide a byte-to-float converter yourself.
You can also modify some of the constants in the code (larger -> better loops perhaps + slower): windowSize, endSteps, startSteps
static long[] calculateLoopStartAndLength(AudioFormat format,
byte[] sampleData, long initialLoopStart, long initialLoopLength) {
long[] result = new long[2];
AudioFloatConverter converter = AudioFloatConverter
.getConverter(format);
float[] floatData = new float[sampleData.length
/ (format.getFrameSize())];
converter.toFloatArray(sampleData, floatData);
int windowSize = 21;
int endSteps = 2000;
int startSteps = 2000;
int bestEndIndex = (int) (initialLoopStart + initialLoopLength);
int bestStartIndex = (int) initialLoopStart;
double smallestValue = Double.POSITIVE_INFINITY;
for (int i = -endSteps / 2; i < endSteps / 2; i++) {
int endIndex = (int) (initialLoopStart + initialLoopLength + i);
if (endIndex - windowSize / 2 < 0
|| endIndex + windowSize / 2 >= floatData.length) {
continue;
}
for (int j = -startSteps / 2; j < startSteps / 2; j++) {
int startIndex = (int) (initialLoopStart + j);
// Calculate the weighted difference sum
if (startIndex - windowSize / 2 < 0
|| startIndex + windowSize / 2 >= floatData.length) {
continue;
}
double sum = 0.0;
for (int k = -windowSize / 2; k <= windowSize / 2; k++) {
double endValue = floatData[endIndex + k];
double weight = 1.0;
if (k != 0) {
weight = Math.abs(1.0 / (double) (2.0 * k));
}
double startValue = floatData[startIndex + k];
sum += weight * Math.abs(endValue - startValue);
}
if (sum < smallestValue) {
smallestValue = sum;
bestEndIndex = endIndex;
bestStartIndex = startIndex;
}
}
}
result[0] = bestStartIndex;
result[1] = bestEndIndex - bestStartIndex;
return result;
}