From 87df72d54a000fb69d2bfa0bf0509610d18bc520 Mon Sep 17 00:00:00 2001 From: Stephen Seo Date: Mon, 18 Jun 2018 15:47:28 +0900 Subject: [PATCH] Working Meter that connects to default sink --- src/Main.cpp | 4 + src/MfPA/Meter.cpp | 220 +++++++++++++++++++++++++++++++++++++++++++-- src/MfPA/Meter.hpp | 24 ++++- 3 files changed, 238 insertions(+), 10 deletions(-) diff --git a/src/Main.cpp b/src/Main.cpp index 5549c30..0f3b480 100644 --- a/src/Main.cpp +++ b/src/Main.cpp @@ -1,5 +1,9 @@ +#include "MfPA/Meter.hpp" int main(int argc, char** argv) { + MfPA::Meter meter; + meter.startMainLoop(); + return 0; } diff --git a/src/MfPA/Meter.cpp b/src/MfPA/Meter.cpp index 520e110..ba1eb4d 100644 --- a/src/MfPA/Meter.cpp +++ b/src/MfPA/Meter.cpp @@ -3,6 +3,7 @@ #include #include #include +#include #include @@ -10,21 +11,32 @@ MfPA::Meter::Meter(const char* sinkOrSourceName, bool isSink) : currentState(WAITING), isMonitoringSink(isSink), sinkOrSourceName(sinkOrSourceName), +gotSinkInfo(false), +gotSourceInfo(false), mainLoop(nullptr), context(nullptr), stream(nullptr), -runFlag(true) +runFlag(true), +channels(1), +window(sf::VideoMode(100,400), "Meter for PulseAudio") { + window.setView(sf::View(sf::FloatRect(0.0f, 0.0f, 1.0f, 1.0f))); + bar.setFillColor(sf::Color::Green); + setenv("PULSE_PROP_application.name", "Meter for PulseAudio", 1); setenv("PULSE_PROP_application.icon_name", "multimedia-volume-control", 1); mainLoop = pa_mainloop_new(); - pa_context_new(pa_mainloop_get_api(mainLoop), "Meter for PulseAudio"); + context = pa_context_new(pa_mainloop_get_api(mainLoop), "Meter for PulseAudio"); pa_context_set_state_callback( context, MfPA::Meter::get_context_callback, this); pa_context_connect(context, nullptr, PA_CONTEXT_NOFLAGS, nullptr); + +#ifndef NDEBUG + std::cout << "End of Meter constructor" << std::endl; +#endif } MfPA::Meter::~Meter() @@ -45,10 +57,16 @@ MfPA::Meter::~Meter() { pa_mainloop_free(mainLoop); } +#ifndef NDEBUG + std::cout << "End of Meter deconstructor" << std::endl; +#endif } void MfPA::Meter::get_context_callback(pa_context* c, void* userdata) { +#ifndef NDEBUG + std::cout << "Begin get_context_callback" << std::endl; +#endif MfPA::Meter* meter = (MfPA::Meter*) userdata; switch(pa_context_get_state(c)) { @@ -59,9 +77,20 @@ void MfPA::Meter::get_context_callback(pa_context* c, void* userdata) meter->currentState = MfPA::Meter::WAITING; break; case PA_CONTEXT_READY: + if(meter->currentState != MfPA::Meter::WAITING) + { +#ifndef NDEBUG + std::cout << "WARNING: Got READY while state is not WAITING" + << std::endl; +#endif + break; + } meter->currentState = MfPA::Meter::PROCESSING; if(meter->sinkOrSourceName[0] == '\0') { +#ifndef NDEBUG + std::cout << "Attempting to get default" << std::endl; +#endif // sinkOrSourceName not provided, get default pa_operation_unref(pa_context_get_server_info( c, @@ -70,6 +99,9 @@ void MfPA::Meter::get_context_callback(pa_context* c, void* userdata) } else if(meter->isMonitoringSink) { +#ifndef NDEBUG + std::cout << "Attempting to get provided sink" << std::endl; +#endif // sink provided, getting info on sink pa_operation_unref(pa_context_get_sink_info_by_name( c, @@ -79,6 +111,9 @@ void MfPA::Meter::get_context_callback(pa_context* c, void* userdata) } else { +#ifndef NDEBUG + std::cout << "Attempting to get provided source" << std::endl; +#endif // source provided, getting info on source pa_operation_unref(pa_context_get_source_info_by_name( c, @@ -89,11 +124,16 @@ void MfPA::Meter::get_context_callback(pa_context* c, void* userdata) break; case PA_CONTEXT_FAILED: meter->currentState = MfPA::Meter::FAILED; + std::cerr << pa_strerror(pa_context_errno(meter->context)) + << std::endl; break; case PA_CONTEXT_TERMINATED: meter->currentState = MfPA::Meter::TERMINATED; break; } +#ifndef NDEBUG + std::cout << "End get_context_callback" << std::endl; +#endif } void MfPA::Meter::get_defaults_callback( @@ -101,6 +141,9 @@ void MfPA::Meter::get_defaults_callback( const pa_server_info* i, void* userdata) { +#ifndef NDEBUG + std::cout << "Begin get_defaults_callback" << std::endl; +#endif MfPA::Meter* meter = (MfPA::Meter*) userdata; if(meter->isMonitoringSink) { @@ -122,6 +165,9 @@ void MfPA::Meter::get_defaults_callback( MfPA::Meter::get_source_info_callback, userdata)); } +#ifndef NDEBUG + std::cout << "End get_defaults_callback" << std::endl; +#endif } void MfPA::Meter::get_sink_info_callback( @@ -130,7 +176,18 @@ void MfPA::Meter::get_sink_info_callback( int eol, void* userdata) { +#ifndef NDEBUG + std::cout << "Begin get_sink_info_callback" << std::endl; +#endif MfPA::Meter* meter = (MfPA::Meter*) userdata; + if(meter->gotSinkInfo) + { +#ifndef NDEBUG + std::cout << "Already got sink info, end get_sink_info_callback" + << std::endl; +#endif + return; + } if(eol != PA_OK) { meter->currentState = MfPA::Meter::FAILED; @@ -143,6 +200,10 @@ void MfPA::Meter::get_sink_info_callback( i->monitor_source_name, MfPA::Meter::get_source_info_callback, userdata)); + meter->gotSinkInfo = true; +#ifndef NDEBUG + std::cout << "End get_sink_info_callback" << std::endl; +#endif } void MfPA::Meter::get_source_info_callback( @@ -151,7 +212,18 @@ void MfPA::Meter::get_source_info_callback( int eol, void* userdata) { +#ifndef NDEBUG + std::cout << "Begin get_source_info_callback" << std::endl; +#endif MfPA::Meter* meter = (MfPA::Meter*) userdata; + if(meter->gotSourceInfo) + { +#ifndef NDEBUG + std::cout << "Already got source info, end get_source_info_callback" + << std::endl; + return; +#endif + } if(eol != PA_OK) { meter->currentState = MfPA::Meter::FAILED; @@ -159,6 +231,7 @@ void MfPA::Meter::get_source_info_callback( return; } + meter->channels = i->sample_spec.channels; pa_sample_spec sampleSpec; sampleSpec.format = PA_SAMPLE_FLOAT32LE; sampleSpec.rate = i->sample_spec.rate; @@ -180,11 +253,19 @@ void MfPA::Meter::get_source_info_callback( meter->stream, i->name, nullptr, +// PA_STREAM_NOFLAGS); PA_STREAM_PEAK_DETECT); + meter->gotSourceInfo = true; +#ifndef NDEBUG + std::cout << "End get_source_info_callback" << std::endl; +#endif } void MfPA::Meter::get_stream_state_callback(pa_stream* s, void* userdata) { +#ifndef NDEBUG + std::cout << "Begin get_stream_state_callback" << std::endl; +#endif MfPA::Meter* meter = (MfPA::Meter*) userdata; switch(pa_stream_get_state(s)) { @@ -196,12 +277,17 @@ void MfPA::Meter::get_stream_state_callback(pa_stream* s, void* userdata) break; case PA_STREAM_FAILED: meter->currentState = MfPA::Meter::FAILED; - std::cerr << "ERROR: Failed to get stream"; + std::cerr << "ERROR: Failed to get stream, "; + std::cerr << pa_strerror(pa_context_errno(meter->context)) + << std::endl; break; case PA_STREAM_TERMINATED: meter->currentState = MfPA::Meter::TERMINATED; break; } +#ifndef NDEBUG + std::cout << "End get_stream_state_callback" << std::endl; +#endif } void MfPA::Meter::get_stream_data_callback( @@ -209,6 +295,9 @@ void MfPA::Meter::get_stream_data_callback( size_t nbytes, void* userdata) { +#ifndef NDEBUG +// std::cout << "Begin get_stream_data_callback" << std::endl; +#endif MfPA::Meter* meter = (MfPA::Meter*) userdata; const void* data; @@ -222,16 +311,31 @@ void MfPA::Meter::get_stream_data_callback( std::vector v(nbytes/sizeof(float)); memcpy(v.data(), data, nbytes); - { - std::lock_guard lock(meter->queueMutex); - meter->sampleQueue.push(std::move(v)); - } + meter->sampleQueue.push(std::move(v)); pa_stream_drop(s); +#ifndef NDEBUG +// std::cout << "End get_stream_data_callback" << std::endl; +#endif } void MfPA::Meter::startMainLoop() { + // initialize levels + levels.resize(channels); + prevLevels.resize(channels); + levelsChanged.resize(channels); + for(unsigned int i = 0; i < channels; ++i) + { + levels[i] = 0.0f; + prevLevels[i] = std::make_tuple(0.0f, 0.0f); + levelsChanged[i] = false; + } + +#ifndef NDEBUG + levelsPrintTimer = 1.0f; +#endif + GDT::IntervalBasedGameLoop( &runFlag, [this] (float dt) { @@ -250,9 +354,111 @@ void MfPA::Meter::update(float dt) if(currentState == TERMINATED || currentState == FAILED) { runFlag = false; + return; + } + + { + sf::Event event; + while(window.pollEvent(event)) + { + if(event.type == sf::Event::Closed) + { + runFlag = false; + return; + } + } + } + + for(unsigned int i = 0; i < channels; ++i) + { + levelsChanged[i] = false; } + + while(!sampleQueue.empty()) + { + { + unsigned char currentChannel = 0; + const auto& front = sampleQueue.front(); + for(auto iter = front.begin(); iter != front.end(); ++iter) + { + float fabs = std::abs(*iter); + if(levels[currentChannel] < fabs) + { + levels[currentChannel] = fabs; + levelsChanged[currentChannel] = true; + } + if(std::get<0>(prevLevels[currentChannel]) < fabs) + { + std::get<0>(prevLevels[currentChannel]) = fabs; + std::get<1>(prevLevels[currentChannel]) = 1.0f; + } + currentChannel = (currentChannel + 1) % channels; + } + } + sampleQueue.pop(); + } + + for(unsigned int i = 0; i < channels; ++i) + { + if(!levelsChanged[i]) + { + levels[i] -= METER_DECAY_RATE * dt; + if(levels[i] < 0.0f) + { + levels[i] = 0.0f; + } + } + if(std::get<1>(prevLevels[i]) > 0.0f) + { + std::get<1>(prevLevels[i]) -= METER_PREV_DECAY_RATE * dt; + if(std::get<1>(prevLevels[i]) <= 0.0f) + { + std::get<0>(prevLevels[i]) = 0.0f; + std::get<1>(prevLevels[i]) = 0.0f; + } + } + } + +#ifndef NDEBUG + levelsPrintTimer -= dt; + if(levelsPrintTimer <= 0.0f) + { + for(unsigned int i = 0; i < channels; ++i) + { + std::cout << "[" << i << "] " << levels[i] << " "; + } + std::cout << std::endl; + levelsPrintTimer = 1.0f; + } +#endif } void MfPA::Meter::draw() { + window.clear(); + + for(unsigned int i = 0; i < channels; ++i) + { + // prev levels + bar.setFillColor(sf::Color( + 0, 255, 0, 255 * std::get<1>(prevLevels[i]))); + bar.setSize(sf::Vector2f( + 1.0f / (float)channels, + std::get<0>(prevLevels[i]))); + bar.setPosition(sf::Vector2f( + (float)i * 1.0f / (float)channels, + 1.0f - std::get<0>(prevLevels[i]))); + window.draw(bar); + // levels + bar.setFillColor(sf::Color::Green); + bar.setSize(sf::Vector2f( + 1.0f / (float)channels, + levels[i])); + bar.setPosition(sf::Vector2f( + (float)i * 1.0f / (float)channels, + 1.0f - levels[i])); + window.draw(bar); + } + + window.display(); } diff --git a/src/MfPA/Meter.hpp b/src/MfPA/Meter.hpp index ce89a05..f3eb4aa 100644 --- a/src/MfPA/Meter.hpp +++ b/src/MfPA/Meter.hpp @@ -1,12 +1,16 @@ #ifndef METER_FOR_PULSEAUDIO_HPP #define METER_FOR_PULSEAUDIO_HPP -#include +#define METER_DECAY_RATE 5.0f +#define METER_PREV_DECAY_RATE 1.0f -#include #include #include +#include + +#include + namespace MfPA { @@ -60,15 +64,29 @@ private: bool isMonitoringSink; const char* sinkOrSourceName; + bool gotSinkInfo; + bool gotSourceInfo; + pa_mainloop* mainLoop; pa_context* context; pa_stream* stream; - std::mutex queueMutex; std::queue> sampleQueue; bool runFlag; + unsigned char channels; + std::vector levels; + std::vector> prevLevels; + std::vector levelsChanged; + + sf::RenderWindow window; + sf::RectangleShape bar; + +#ifndef NDEBUG + float levelsPrintTimer; +#endif + void update(float dt); void draw(); -- 2.49.0