Частотный анализ звука из микрофона
Здравсвуйте, хочу создать частотный анализатор звука, но совершенно не могу понять, как всё должно происходить, кроме того, что нужно в определённый момент использовать ДПФ. Взял библиотеку 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);
}
}