Частотный анализ звука из микрофона

Здравсвуйте, хочу создать частотный анализатор звука, но совершенно не могу понять, как всё должно происходить, кроме того, что нужно в определённый момент использовать ДПФ. Взял библиотеку libsoundio и FFTW, переписав в более удобный вид пример из документации, с выводом звука из микрофона в наушники, начал пытаться читать, что делать дальше. Как я понял, нужно взять N сэмплов, применить к ним ДПФ, после можно выводить их на экран. Но не могу понять, откуда их доставать нужно и на каком этапе. Кажется,что нужно доставать из кольцевого буффера, но тогда вопрос в какой тип преобразовывать? Буду крайне благодарен за помощь.
main.cpp

#include "panic.h"
#include "callback.h"
#include "audio_format.h"

#include <ranges>
#include <thread>
#include <memory>
#include <fstream>
#include <iostream>
#include <algorithm>
#include <cstring>

using namespace std::chrono_literals;

int main(void)
{
    std::unique_ptr<SoundIo, std::add_pointer_t<decltype(soundio_destroy)>> context {soundio_create(), soundio_destroy};

    panic_if(!context, "failed to create context: out of memory\n");

    int err;
    auto microphone_latency {0.2};

    panic_if(err = soundio_connect(context.get()),
             "failed to connect backend: %s\n", soundio_strerror(err));

    std::cout << "context automatically connected to backend: "
              << soundio_backend_name(context->current_backend) << '\n';

    soundio_flush_events(context.get());

    int const default_input_device_index {soundio_default_input_device_index(context.get())};

    panic_if(default_input_device_index < 0, "no input device found\n");

    int const default_output_device_index {soundio_default_output_device_index(context.get())};

    panic_if(default_output_device_index < 0, "no output device found\n");

    std::unique_ptr<SoundIoDevice, std::add_pointer_t<decltype(soundio_device_unref)>> indevice {
        soundio_get_input_device(context.get(), default_input_device_index),
        soundio_device_unref
    };

    panic_if(!indevice, "failed to create input device: out of memory\n");
    panic_if(indevice->probe_error, "unable to probe device: %s\n", soundio_strerror(indevice->probe_error));

    std::cout << "\ninput device:\n"
              << "  * name: " << indevice->name << '\n'
              << "  * id: "   << indevice->id   << '\n';

    std::unique_ptr<SoundIoDevice, std::add_pointer_t<decltype(soundio_device_unref)>> outdevice {
        soundio_get_output_device(context.get(), default_output_device_index),
        soundio_device_unref
    };

    panic_if(!outdevice, "failed to create output device: out of memory\n");
    panic_if(outdevice->probe_error, "unable to probe device: %s\n", soundio_strerror(outdevice->probe_error));

    std::cout << "\noutput device:\n"
              << "  * name: " << outdevice->name << '\n'
              << "  * id: "   << outdevice->id   << '\n';

    soundio_device_sort_channel_layouts(outdevice.get());

    auto const layout {soundio_best_matching_channel_layout(outdevice->layouts, outdevice->layout_count,
                                                            indevice->layouts, indevice->layout_count)};

    panic_if(!layout,"channel layouts not compatible\n");

    std::array constexpr prioritized_sample_rates {
        48000,
        44100,
        96000,
        24000
    };

    auto sample_rate_it {
        std::ranges::find_if(prioritized_sample_rates,
        [raw_indevice = indevice.get(), raw_outdevice = outdevice.get()](auto sample_rate) {
            return soundio_device_supports_sample_rate(raw_indevice, sample_rate) &&
                   soundio_device_supports_sample_rate(raw_outdevice, sample_rate);
        })
    };

    panic_if(sample_rate_it == prioritized_sample_rates.end(), "incompatibale sample rates\n");

    int const sample_rate {*sample_rate_it};

    std::array constexpr prioritized_formats {
        SoundIoFormatFloat32NE,
        SoundIoFormatFloat32FE,
        SoundIoFormatS32NE,
        SoundIoFormatS32FE,
        SoundIoFormatS24NE,
        SoundIoFormatS24FE,
        SoundIoFormatS16NE,
        SoundIoFormatS16FE,
        SoundIoFormatFloat64NE,
        SoundIoFormatFloat64FE,
        SoundIoFormatU32NE,
        SoundIoFormatU32FE,
        SoundIoFormatU24NE,
        SoundIoFormatU24FE,
        SoundIoFormatU16NE,
        SoundIoFormatU16FE,
        SoundIoFormatS8,
        SoundIoFormatU8
    };

    auto format_it {
        std::ranges::find_if(prioritized_formats,
        [raw_indevice = indevice.get(), raw_outdevice = outdevice.get()](auto format) {
            return soundio_device_supports_format(raw_indevice, format) &&
                   soundio_device_supports_format(raw_outdevice, format);
        })
    };

    panic_if(format_it == prioritized_formats.end(), "incompatible sample formats");

    auto const format {*format_it};

    std::unique_ptr<SoundIoInStream, std::add_pointer_t<decltype(soundio_instream_destroy)>> instream {
        soundio_instream_create(indevice.get()),
        soundio_instream_destroy
    };

    panic_if(!instream, "failed to create instream: out of memory\n");

    instream->format            = format;
    instream->sample_rate       = sample_rate;
    instream->layout            = *layout;
    instream->read_callback     = callback::read;
    instream->overflow_callback = callback::overflow;
    instream->software_latency  = microphone_latency;

    panic_if(err = soundio_instream_open(instream.get()),
             "failed to open instream: %s\n", soundio_strerror(err));

    std::cout << "\ninstream settings:\n"
              << "  * sample rate: "      << instream->sample_rate << "Hz\n"
              << "  * format: "           << soundio_format_string(instream->format) << '\n'
              << "  * layout name: "      << instream->layout.name << '\n'
              << "  * bytes per sample: " << instream->bytes_per_sample << '\n'
              << "  * bytes per frame: "  << instream->bytes_per_frame << '\n'
              << "  * software latency: " << instream->software_latency << '\n';

    std::unique_ptr<SoundIoOutStream, std::add_pointer_t<decltype(soundio_outstream_destroy)>> outstream {
        soundio_outstream_create(outdevice.get()),
        soundio_outstream_destroy
    };

    panic_if(!outstream, "failed to create outstream: out of memory\n");

    outstream->format             = format;
    outstream->sample_rate        = sample_rate;
    outstream->layout             = *layout;
    outstream->write_callback     = callback::write;
    outstream->underflow_callback = callback::underflow;
    outstream->software_latency   = microphone_latency;

    panic_if(err = soundio_outstream_open(outstream.get()),
             "failed to open instream: %s\n", soundio_strerror(err));

    std::cout << "\noutstream settings:\n"
              << "  * sample rate: "      << outstream->sample_rate << "Hz\n"
              << "  * format: "           << soundio_format_string(outstream->format) << '\n'
              << "  * layout name: "      << outstream->layout.name << '\n'
              << "  * bytes per sample: " << outstream->bytes_per_sample << '\n'
              << "  * bytes per frame: "  << outstream->bytes_per_frame << '\n'
              << "  * software latency: " << outstream->software_latency << '\n';

    int const capacity(2 * microphone_latency * instream->sample_rate * instream->bytes_per_frame);

    std::unique_ptr<SoundIoRingBuffer, std::add_pointer_t<decltype(soundio_ring_buffer_destroy)>> ring_buffer {
        soundio_ring_buffer_create(context.get(), capacity),
        soundio_ring_buffer_destroy
    };

    panic_if(!ring_buffer, "failed to create ring buffer: out of memory\n");

    auto write_ptr {soundio_ring_buffer_write_ptr(ring_buffer.get())};
    int const fill_count(microphone_latency * outstream->sample_rate * outstream->bytes_per_frame);

    std::fill_n(write_ptr, fill_count, 0);

    soundio_ring_buffer_advance_write_ptr(ring_buffer.get(), fill_count);

    instream->userdata  = ring_buffer.get();
    outstream->userdata = ring_buffer.get();

    panic_if(err = soundio_instream_start(instream.get()), soundio_strerror(err));
    panic_if(err = soundio_outstream_start(outstream.get()), soundio_strerror(err));

    for (;;) {
        soundio_wait_events(context.get());
    }

    return EXIT_SUCCESS;
}

Callback.cpp

#include "panic.h"
#include "callback.h"

#include <algorithm>
#include <cstring>

namespace callback
{

    void overflow(SoundIoInStream *)
    {
        static int count {};
        std::fprintf(stderr, "overflow %d\n", ++count);
    }

    void underflow(SoundIoOutStream *) {
        static int count {};
        std::fprintf(stderr, "underflow %d\n", ++count);
    }

    void read(SoundIoInStream *instream, int frame_count_min, int frame_count_max)
    {
        auto ring_buffer {static_cast<SoundIoRingBuffer*>(instream->userdata)};

        SoundIoChannelArea *areas;

        auto       write_ptr  {soundio_ring_buffer_write_ptr(ring_buffer)};
        int const  free_count {soundio_ring_buffer_free_count(ring_buffer) / instream->bytes_per_frame};

        panic_if(free_count < frame_count_min,
                 "%s failed: ring buffer overflow\n", __PRETTY_FUNCTION__);

        int const write_frames {std::min(free_count, frame_count_max)};
        int       frames_left  {write_frames};

        int       err;
        int const channel_count {instream->layout.channel_count};

        while (true) {
            int frame_count {frames_left};

            panic_if(err = soundio_instream_begin_read(instream, &areas, &frame_count),
                     "%s failed: %s\n", __PRETTY_FUNCTION__, soundio_strerror(err));

            if (!frame_count) {
                break;
            }

            if (areas) {
                for (int frame {}; frame < frame_count; ++frame) {
                    for (int channel {}; channel < channel_count; ++channel) {
                        write_ptr = std::copy_n(areas[channel].ptr,
                                                instream->bytes_per_sample, write_ptr);
                        areas[channel].ptr += areas[channel].step;
                    }
                }
            } else {
                std::fill_n(write_ptr, frame_count * instream->bytes_per_frame, 0);
            }

            panic_if(err = soundio_instream_end_read(instream),
                     "%s failed: %s\n", __PRETTY_FUNCTION__, soundio_strerror(err));

            frames_left -= frame_count;

            if (frames_left <= 0) {
                break;
            }
        }

        soundio_ring_buffer_advance_write_ptr(ring_buffer, write_frames * instream->bytes_per_frame);
    }

    void write(SoundIoOutStream *outstream, int frame_count_min, int frame_count_max)
    {
        auto ring_buffer {static_cast<SoundIoRingBuffer *>(outstream->userdata)};

        SoundIoChannelArea *areas;

        int err;

        int frames_left;
        int frame_count;

        auto read_ptr {soundio_ring_buffer_read_ptr(ring_buffer)};

        int fill_bytes {soundio_ring_buffer_fill_count(ring_buffer)};
        int fill_count {fill_bytes / outstream->bytes_per_frame};

        int const channel_count {outstream->layout.channel_count};

        if (frame_count_min > fill_count) {
            frames_left = frame_count_min;

            for (;;) {
                frame_count = frames_left;

                if (frame_count <= 0) {
                    return;
                }

                panic_if(err = soundio_outstream_begin_write(outstream, &areas, &frame_count),
                         "begin write error: %s\n", soundio_strerror(err));

                if (frame_count <= 0) {
                    return;
                }

                for (int frame {}; frame < frame_count; ++frame) {
                    for (int channel {}; channel < channel_count; ++channel) {
                        std::fill_n(areas[channel].ptr, outstream->bytes_per_sample, 0);
                        areas[channel].ptr += areas[channel].step;
                    }
                }

                panic_if(err = soundio_outstream_end_write(outstream),
                         "end write error: %s\n", soundio_strerror(err));

                frames_left -= frame_count;
            }
        }

        int const read_count {std::min(frame_count_max, fill_count)};

        frames_left = read_count;

        while (frames_left > 0) {
            frame_count = frames_left;

            panic_if(err = soundio_outstream_begin_write(outstream, &areas, &frame_count),
                     "begin write error: %s\n", soundio_strerror(err));

            if (frame_count <= 0) {
                break;
            }

            for (int frame {}; frame < frame_count; ++frame) {
                for (int channel {}; channel < channel_count; ++channel) {
                    std::copy_n(read_ptr, outstream->bytes_per_sample, areas[channel].ptr);
                    areas[channel].ptr += areas[channel].step;
                    read_ptr += outstream->bytes_per_sample;
                }
            }

            panic_if(err = soundio_outstream_end_write(outstream),
                     "end write error: %s\n", soundio_strerror(err));

            frames_left -= frame_count;
        }

        soundio_ring_buffer_advance_read_ptr(ring_buffer, read_count * outstream->bytes_per_frame);
    }

}

Ответы (0 шт):