VulcanoLE/src/VulcanoLE/Audio/AudioGrabber.cpp

195 lines
6.6 KiB
C++

#include <cstring>
#include <cmath>
#include <VulcanoLE/Audio/AudioGrabber.h>
#include <VUtils/Logging.h>
AudioGrabber::AudioGrabber() = default;
AudioGrabber::~AudioGrabber() = default;
bool AudioGrabber::read(pcm_stereo_sample *buffer, uint32_t buffer_size) {
auto buffer_size_bytes = static_cast<size_t>(sizeof(pcm_stereo_sample) * buffer_size);
if (m_pulseaudio_simple == nullptr) {
open_pulseaudio_source(static_cast<uint32_t>(buffer_size_bytes));
}
if (m_pulseaudio_simple != nullptr) {
memset(buffer, 0, buffer_size_bytes);
int32_t error_code;
auto return_code = pa_simple_read(m_pulseaudio_simple, buffer,
buffer_size_bytes, &error_code);
if (return_code < 0) {
WARN("Could not finish reading pulse Audio stream buffer\n bytes read: %d buffer\n size: %d", return_code,
buffer_size_bytes)
// zero out buffer
memset(buffer, 0, buffer_size_bytes);
pa_simple_free(m_pulseaudio_simple);
m_pulseaudio_simple = nullptr;
return false;
}
// Success fully read entire buffer
return true;
}
return false;
}
void AudioGrabber::populate_default_source_name() {
pa_mainloop_api *mainloop_api;
pa_context *pulseaudio_context;
m_pulseaudio_mainloop = pa_mainloop_new();
mainloop_api = pa_mainloop_get_api(m_pulseaudio_mainloop);
pulseaudio_context = pa_context_new(mainloop_api, "VulcanoLE device list");
pa_context_connect(pulseaudio_context, nullptr, PA_CONTEXT_NOFLAGS,
nullptr);
pa_context_set_state_callback(pulseaudio_context,
pulseaudio_context_state_callback,
reinterpret_cast<void *>(this));
int ret;
if (pa_mainloop_run(m_pulseaudio_mainloop, &ret) < 0) {
ERR("Could not open pulseaudio mainloop to find default device name: %d", ret)
}
}
bool AudioGrabber::open_pulseaudio_source(uint32_t max_buffer_size) {
int32_t error_code = 0;
static const pa_sample_spec sample_spec = { PA_SAMPLE_FLOAT32NE, k_sample_rate,
k_channels };
static const pa_buffer_attr buffer_attr = { max_buffer_size, 0, 0, 0,
(max_buffer_size / 2) };
populate_default_source_name();
if (!m_pulseaudio_default_source_name.empty()) {
m_pulseaudio_simple =
pa_simple_new(nullptr, k_record_stream_name, PA_STREAM_RECORD,
m_pulseaudio_default_source_name.c_str(),
k_record_stream_description, &sample_spec,
nullptr, &buffer_attr, &error_code);
}
if (m_pulseaudio_simple == nullptr) {
m_pulseaudio_simple =
pa_simple_new(nullptr, k_record_stream_name, PA_STREAM_RECORD,
nullptr, k_record_stream_description,
&sample_spec, nullptr, &buffer_attr, &error_code);
}
if (m_pulseaudio_simple == nullptr) {
m_pulseaudio_simple =
pa_simple_new(nullptr, k_record_stream_name, PA_STREAM_RECORD,
"0", k_record_stream_description, &sample_spec,
nullptr, &buffer_attr, &error_code);
}
if (m_pulseaudio_simple != nullptr) {
return true;
}
ERR("Could not open pulseaudio source: %s", pa_strerror(error_code))
return false;
}
void AudioGrabber::pulseaudio_context_state_callback(pa_context *c, void *userdata) {
switch (pa_context_get_state(c)) {
case PA_CONTEXT_UNCONNECTED:
case PA_CONTEXT_CONNECTING:
case PA_CONTEXT_AUTHORIZING:
case PA_CONTEXT_SETTING_NAME:
break;
case PA_CONTEXT_READY: {
pa_operation_unref(pa_context_get_server_info(
c, pulseaudio_server_info_callback, userdata));
break;
}
case PA_CONTEXT_FAILED:
case PA_CONTEXT_TERMINATED:
auto *src =
reinterpret_cast<AudioGrabber *>(userdata);
pa_mainloop_quit(src->m_pulseaudio_mainloop, 0);
break;
}
}
void AudioGrabber::pulseaudio_server_info_callback(pa_context *context, const pa_server_info *i, void *userdata) {
if (i != nullptr) {
auto *src = reinterpret_cast<AudioGrabber *>(userdata);
std::string name = i->default_sink_name;
name.append(k_default_monitor_postfix);
src->m_pulseaudio_default_source_name = name;
// stop mainloop after finding default name
pa_mainloop_quit(src->m_pulseaudio_mainloop, 0);
}
}
AudioGrabber *AudioGrabber::createAudioGrabber() {
auto *grabber = new AudioGrabber();
return grabber;
}
void AudioGrabber::init() {
m_pcm_buffer = static_cast<pcm_stereo_sample *>(calloc(BUFFER_SIZE, sizeof(pcm_stereo_sample)));
if (env != nullptr)
m_scale = env->getAsDouble("audio_scale", 1.0);
DBG("SET Audio Scale: %.3f", m_scale)
loudness = 0.0;
}
void AudioGrabber::calculateRMS(pcm_stereo_sample *pFrame) {
float square = 0, mean;
for (int i = 0; i < BUFFER_SIZE; i++) {
float left = pFrame[0].l;
square += std::pow(left, 2);
}
mean = (square / (float) (BUFFER_SIZE));
loudness = std::sqrt(mean);
}
void AudioGrabber::calculatePEAK(pcm_stereo_sample *pFrame) {
float max = 0;
for (int i = 0; i < BUFFER_SIZE; i++) {
float left = std::abs(pFrame[0].l);
if (left > max) {
max = left;
}
}
loudness = max;
}
float AudioGrabber::getLoudness() {
std::unique_lock<std::mutex> lck(m_mtx);
return (float) loudness;
}
bool AudioGrabber::doWork() {
std::unique_lock<std::mutex> lck(m_mtx);
if (this->read(m_pcm_buffer, BUFFER_SIZE)) {
for (int i = 0; i < BUFFER_SIZE; ++i) {
// my system is fucking quite
m_pcm_buffer[i].l *= m_scale;
m_pcm_buffer[i].r *= m_scale;
}
switch (requestMode) {
case ReqMode::FFT:
fft.process(m_pcm_buffer);
break;
case ReqMode::RMS:
calculateRMS(m_pcm_buffer);
break;
case ReqMode::PEAK:
calculatePEAK(m_pcm_buffer);
break;
default:
fft.process(m_pcm_buffer);
calculateRMS(m_pcm_buffer);
calculatePEAK(m_pcm_buffer);
}
return true;
} else {
DBG("Wait for Data")
return false;
}
}