]> git.seodisparate.com/gitweb - MeterForPulseAudio/commitdiff
Working Meter that connects to default sink
authorStephen Seo <seo.disparate@gmail.com>
Mon, 18 Jun 2018 06:47:28 +0000 (15:47 +0900)
committerStephen Seo <seo.disparate@gmail.com>
Mon, 18 Jun 2018 06:47:28 +0000 (15:47 +0900)
src/Main.cpp
src/MfPA/Meter.cpp
src/MfPA/Meter.hpp

index 5549c309f7fe82a961a05bd4c49b112252b72673..0f3b48021f838b7969e3673cd87e12d5ace00f0b 100644 (file)
@@ -1,5 +1,9 @@
+#include "MfPA/Meter.hpp"
 
 int main(int argc, char** argv)
 {
+    MfPA::Meter meter;
+    meter.startMainLoop();
+
     return 0;
 }
index 520e11086441a96798ba1ac026e346db4a8bb632..ba1eb4dce1f7dffc2baa2d4fe3f432d6910ce0aa 100644 (file)
@@ -3,6 +3,7 @@
 #include <cstdlib>
 #include <cstring>
 #include <iostream>
+#include <cmath>
 
 #include <GDT/GameLoop.hpp>
 
@@ -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<float> v(nbytes/sizeof(float));
     memcpy(v.data(), data, nbytes);
 
-    {
-        std::lock_guard<std::mutex> 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();
 }
index ce89a059bb566d1b3922542653fefd1157ee2289..f3eb4aa45c6005a1c4be48d15a777473fbd98d00 100644 (file)
@@ -1,12 +1,16 @@
 #ifndef METER_FOR_PULSEAUDIO_HPP
 #define METER_FOR_PULSEAUDIO_HPP
 
-#include <pulse/pulseaudio.h>
+#define METER_DECAY_RATE 5.0f
+#define METER_PREV_DECAY_RATE 1.0f
 
-#include <mutex>
 #include <queue>
 #include <vector>
 
+#include <pulse/pulseaudio.h>
+
+#include <SFML/Graphics.hpp>
+
 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<std::vector<float>> sampleQueue;
 
     bool runFlag;
 
+    unsigned char channels;
+    std::vector<float> levels;
+    std::vector<std::tuple<float, float>> prevLevels;
+    std::vector<bool> levelsChanged;
+
+    sf::RenderWindow window;
+    sf::RectangleShape bar;
+
+#ifndef NDEBUG
+    float levelsPrintTimer;
+#endif
+
     void update(float dt);
     void draw();