// title: Spectrogram of a sound file // author: wondersluyter // description: // Generates an Image containing the spectrogram of a soundfile // code: // requires SignalBox quark // 1. pick an fft size (must be power of two) ~size = 2048; ~size = 4096; // 2. path to a sound file ~path = Platform.resourceDir +/+ "sounds/a11wlk01.wav"; ( // 3. analyze audio : this may take a little while for a long sound file var data = Signal.read(~path); var size = ~size; var sf = SoundFile.openRead(~path); var sampleRate = sf.sampleRate; var hops = 4; var window = Signal.hanningWindow(size); var numFrames = (data.size / size * hops); var fftMags = Array(numFrames); var imag = Signal.newClear(size); var cosTable = Signal.fftCosTable(size); sf.close; (data.size / size * hops).asInteger.do { |j| var i = j / hops; var r = data[(size * i).asInteger..(size * (i + 1) - 1).asInteger]; r = r.addAll(Array.fill(size - r.size, {0})); r = r * window; fftMags.add((fft(r, imag, cosTable).magnitude.log10)[0..(size * 20000/sampleRate).asInteger]); }; ~fftMags = fftMags; ) ( // 4. generate image : this may also take a little while var width = ~fftMags.size; var height = ~fftMags[0].size; var image = Image.new(width, height); var scaleFunc = { |pixel| pixel.lincurve(0, height, 1, height, curve: 3) }; // curve factor for Y scaleing (0 = linear, 3 =~ mel scale) image.setPixels(Int32Array.fill(width * height, { |i| var index = scaleFunc.(height - (i / width) - 1).asInteger; var frame = i % width; var mag = ~fftMags[frame][index]; Image.colorToPixel(Color.gray(0, mag.lincurve(-2, 1, 0, 1, curve: 1.5))); // input range to adjust threshold, curve factor for contrast: 0-2 }), Rect(0, 0, image.width, image.height)); ~image.free; ~image = image; ) // 5. display image ~image.plot;