A Sound File Octave Splitter

Here’s a quick tool I made, I’m sharing it in case it might interest you.

You need to save it within a folder, then copy a sound file within the same folder, adjust the top variables so they fit your needs.

Main parameter is freq. Once started, it will divide it by two until it is just above 20HZ, then will play the sound file and split each octave according to the freq: you will get one sound file between 20HZ and freq, one sound file between freq and freq * 2, between freq * 2 and freq * 4, etc, up to frequencies between freq * 256 and 20KHZ (i.e. 10 bands, first and last not being an octave wide).

Notes:

  • This is real-time recording, so you need to wait the sound file duration before it’s done.
  • I’m using LPF + HPF to act as a bandpass. This will introduce phase shifting, different for each recording.
  • You will lose volume at the frequency breaking points (see bottom code). So you might want to split octaves somewhere spectrally not important (e.g. root tritone on tonal music).
  • Only stereo here, but this is easy to adapt.

The code:

(
var freq = 66.midicps;
var fileName = "file_name.wav";
var targetName = "file_name";
var format = "wav";
var sampleFormat = "int32";
var sampleRate = 48000;

var cwd = thisProcess.nowExecutingPath.dirname;
var fileBuffer;
var buffers = Array.newClear(10);
var onSynthFree = {
	buffers.do({ |buffer, index|
		buffer.write(
			cwd +/+ targetName ++ "_oct" ++ index ++ "." ++ format,
			format,
			sampleFormat
		);
		buffer.free;
	});
	fileBuffer.free;
	"Octave splitting done".postln;
};
var onFileLoaded = {
	10.do({ |i|
		buffers[i] = Buffer.alloc(
			s,
			fileBuffer.numFrames + sampleRate,
			2
		);
	});

	SynthDef(\octaveSplitter, { |in, freq,
		b1, b2, b3, b4, b5, b6, b7, b8, b9, b10|
		var snd = PlayBuf.ar(2, in);

		var oct1 = LPF.ar(HPF.ar(snd, 20), freq);
		var oct2 = LPF.ar(HPF.ar(snd, freq), freq * 2);
		var oct3 = LPF.ar(HPF.ar(snd, freq * 2), freq * 4);
		var oct4 = LPF.ar(HPF.ar(snd, freq * 4), freq * 8);
		var oct5 = LPF.ar(HPF.ar(snd, freq * 8), freq * 16);
		var oct6 = LPF.ar(HPF.ar(snd, freq * 16), freq * 32);
		var oct7 = LPF.ar(HPF.ar(snd, freq * 32), freq * 64);
		var oct8 = LPF.ar(HPF.ar(snd, freq * 64), freq * 128);
		var oct9 = LPF.ar(HPF.ar(snd, freq * 128), freq * 256);
		var oct10 = LPF.ar(HPF.ar(snd, freq * 256), 20000);

		RecordBuf.ar(oct1, b1);
		RecordBuf.ar(oct2, b2);
		RecordBuf.ar(oct3, b3);
		RecordBuf.ar(oct4, b4);
		RecordBuf.ar(oct5, b5);
		RecordBuf.ar(oct6, b6);
		RecordBuf.ar(oct7, b7);
		RecordBuf.ar(oct8, b8);
		RecordBuf.ar(oct9, b9);
		RecordBuf.ar(oct10, b10);

		EnvGen.kr(
			Env([0, 0], [BufDur.kr(in) + 1]),
			doneAction: Done.freeSelf;
		);
	})
	.play(
		args: [
			\in, fileBuffer,
			\freq, freq,
			\b1, buffers[0],
			\b2, buffers[1],
			\b3, buffers[2],
			\b4, buffers[3],
			\b5, buffers[4],
			\b6, buffers[5],
			\b7, buffers[6],
			\b8, buffers[7],
			\b9, buffers[8],
			\b10, buffers[9]
	])
	.onFree({ onSynthFree.value; });
};

while { (freq / 2) > 20 } {
	freq = freq / 2;
};

fileBuffer = Buffer.read(
	s,
	cwd +/+ fileName,
	action: { |buffer|
		fileBuffer = buffer;
		onFileLoaded.value;
	}
);
)

Use a spectrogram to see how frequencies are attenuated at freq multiples:

(
SynthDef(\osfr, { |freq = 24|
	var snd = PinkNoise.ar(0.1);

	var oct1 = LPF.ar(HPF.ar(snd, 20), freq);
	var oct2 = LPF.ar(HPF.ar(snd, freq), freq * 2);
	var oct3 = LPF.ar(HPF.ar(snd, freq * 2), freq * 4);
	var oct4 = LPF.ar(HPF.ar(snd, freq * 4), freq * 8);
	var oct5 = LPF.ar(HPF.ar(snd, freq * 8), freq * 16);
	var oct6 = LPF.ar(HPF.ar(snd, freq * 16), freq * 32);
	var oct7 = LPF.ar(HPF.ar(snd, freq * 32), freq * 64);
	var oct8 = LPF.ar(HPF.ar(snd, freq * 64), freq * 128);
	var oct9 = LPF.ar(HPF.ar(snd, freq * 128), freq * 256);
	var oct10 = LPF.ar(HPF.ar(snd, freq * 256), 20000);

	snd = oct1 + oct2 + oct3 + oct5 + oct6 + oct7 + oct8 + oct9 + oct10;

	Out.ar(0, snd);
}).play;
)

Have fun,
Simon

4 Likes