Automatic sample loop finder

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;
	}


So it’s used for finding a good start and end place to make a repeating loop?

Sounds pretty useful, thanks for the code.

If you get time, it’d be cool to see a simple use-case.

Yes that is correct.

It tries to find similar surroundings for two places in a sample. The similarity at the two loop points is valued the most with the weight variable that decreases with distance.
There are a lot of possibilities for exploring other weights and also trying to get away from that nasty cubic-like complexity.

Some kind of use-case might pop up some day :slight_smile: