#include #include #include #include AudioGrabber::AudioGrabber() = default; AudioGrabber::~AudioGrabber() = default; bool AudioGrabber::read(pcm_stereo_sample *buffer, uint32_t buffer_size) { auto buffer_size_bytes = static_cast(sizeof(pcm_stereo_sample) * buffer_size); if (m_pulseaudio_simple == nullptr) { open_pulseaudio_source(static_cast(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(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(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(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(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 lck(m_mtx); return (float) loudness; } bool AudioGrabber::doWork() { std::unique_lock 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; } }