On Tuesday, 8 July 2025 at 19:39:37 UTC, Dennis wrote:

The magnitude of each element (computed with `std.complex.abs`) corresponds to the amplitude of each frequency component, the angle in the complex plane represents the phase (computed with `std.complex.arg` in radians).

This is what I picked up from wikipedia.

The frequencies are all relative to the FFT window. `res[0]` is 0 Hz, `res[1]` corresponds to a sine wave that fits 1 cycle inside your window, res[2] is 2 cycles etc. The frequency in Hz depends on your sample rate. If it's 44100, 44100 / 4096 = ~10 so your window fits 10 times in 1 second. That means res[1] is around 10 hz, res[2] 20 hz etc. up to res[4095] which is 40950 hz. Although everything from res[2048] onwards is just a mirrored copy since 44100 samples/s can only capture frequencies up to 22 Khz (for more info search 'Nyquist frequency' and 'aliasing').

This was the key bit of information I didn't know.

The closest bucket to 1209Hz is 1209 * (4096 / 44100) = 112.3, which is not an exact match so it will leak frequencies in all other bins, but it will still mostly contribute to bins 112 and 113 so it's probably good enough to just check those.

Checking the closest bins to each frequency seems to work well, at least with clean sound from a wav file. It remains to be seen how it fares against noisy real life signals. I'll likely need a window function or interpolation but this is a good start.

```D
void decode_sound(float[] samples)
{
        import std.numeric;
        import std.math;
        import std.complex;
        import std.algorithm;

        size_t fft_size = 4096;
        auto f = new Fft(fft_size);

        foreach(ch; samples.chunks(fft_size))
        {
                auto res = f.fft(ch);
                double[] magnitudes = res.map!(x => x.abs).array;

auto high = [magnitudes[112], magnitudes[124], magnitudes[137], magnitudes[151]].maxIndex; auto low = [magnitudes[65], magnitudes[72], magnitudes[78], magnitudes[87]].maxIndex;

// high and low are 0,1,2,3 depending on which DTMF tone pair was detected
        }
}
```

Thanks,
Matthew

Reply via email to