commit:     06c2a1810f75a013607a1c71083922144c7f7ed5
Author:     Adrian Schollmeyer <nex+b-g-o <AT> nexadn <DOT> de>
AuthorDate: Wed Jan  4 21:30:13 2023 +0000
Commit:     Adrian Schollmeyer <nex+b-g-o <AT> nexadn <DOT> de>
CommitDate: Wed Jan  4 21:30:13 2023 +0000
URL:        https://gitweb.gentoo.org/repo/proj/guru.git/commit/?id=06c2a181

media-sound/opensoundmeter: add 1.2.2, 1.2.2_p20220104

Signed-off-by: Adrian Schollmeyer <nex+b-g-o <AT> nexadn.de>

 media-sound/opensoundmeter/Manifest                |   1 +
 .../files/opensoundmeter-deadlock-fix.patch        |  64 ++
 .../files/opensoundmeter-jack-support.patch        | 668 +++++++++++++++++++++
 .../opensoundmeter/opensoundmeter-1.2.2.ebuild     |  60 ++
 .../opensoundmeter-1.2.2_p20220104.ebuild          |  76 +++
 5 files changed, 869 insertions(+)

diff --git a/media-sound/opensoundmeter/Manifest 
b/media-sound/opensoundmeter/Manifest
index 3d1338830..1cbf64c3a 100644
--- a/media-sound/opensoundmeter/Manifest
+++ b/media-sound/opensoundmeter/Manifest
@@ -1 +1,2 @@
 DIST opensoundmeter-1.2.1.gh.tar.gz 41006647 BLAKE2B 
a3ab132f8a90497132dae3144dd91d162111754e79056bb95ae2f638f65dc6074d2dbc1739c07897f6b2771edfe82c284aea1a48cc9af9454a91698c6915fb5b
 SHA512 
41701377b5df85e08664b68fe102cb6da5d57e70c0366bb5aafc681c926ba7da0622cceb218998bd677e3313f2727b7ecfcb4fcfb5f80fa1fd87334e2a27c377
+DIST opensoundmeter-1.2.2.gh.tar.gz 41041122 BLAKE2B 
f04441b5d672bb3f2dfcafcd0a69a272f2196972eff5ec99f3990c822edb35513bc9c27e07adeb58711a5355c1de280433f39baf0002ce3a9424c507988f758e
 SHA512 
3cc848133b51a0409401347bdfccfe12b82b7fb07b8925593ee5b14053ac51ccb84c71960fc0d6e888f7a31baa78dba970588150a41a5f5d13fa54792806edc9

diff --git a/media-sound/opensoundmeter/files/opensoundmeter-deadlock-fix.patch 
b/media-sound/opensoundmeter/files/opensoundmeter-deadlock-fix.patch
new file mode 100644
index 000000000..4719fd788
--- /dev/null
+++ b/media-sound/opensoundmeter/files/opensoundmeter-deadlock-fix.patch
@@ -0,0 +1,64 @@
+diff --git a/src/audio/plugins/alsa.cpp b/src/audio/plugins/alsa.cpp
+index 9987ba3..61537f2 100644
+--- a/src/audio/plugins/alsa.cpp
++++ b/src/audio/plugins/alsa.cpp
+@@ -16,6 +16,7 @@
+  *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
+  */
+ #include "alsa.h"
++#include <cassert>
+ #include <cstring>
+ #include <QCoreApplication>
+ #define ALSA_BUFFER_SIZE 1024
+@@ -175,23 +176,26 @@ Format AlsaPlugin::deviceFormat(const DeviceInfo::Id 
&id, const Plugin::Directio
+ Stream *AlsaPlugin::open(const DeviceInfo::Id &id, const Plugin::Direction 
&mode, const Format &format,
+                          QIODevice *endpoint)
+ {
+-    std::lock_guard<std::mutex> lock(m_deviceListMutex);
++    std::unique_lock<std::mutex> lock(m_deviceListMutex, std::defer_lock);
+     if (id.isNull()) {
+         return nullptr;
+     }
+ 
++    assert(!lock.owns_lock());
++    lock.lock();
+     AlsaPCMDevice *device = m_devices[ {mode, id}];
+     if (!device) {
+         device = new AlsaPCMDevice(id, mode, format, m_deviceListMutex);
+         connect (device, &AlsaPCMDevice::closed, this, [this, mode, id, 
device]() {
+-            //mutex is locked here by device
++            std::lock_guard<std::mutex> lock{m_deviceListMutex};
+             m_devices[ {mode, id}] = nullptr;
+             device->deleteLater();
+-            m_deviceListMutex.unlock();
+         });
+ 
+         m_devices[ {mode, id}] = device;
+     }
++    assert(lock.owns_lock());
++    lock.unlock();
+     if (!device->start()) {
+         return nullptr;
+     }
+@@ -326,14 +330,18 @@ bool AlsaPCMDevice::start()
+             }
+             QCoreApplication::processEvents();
+             if (m_callbacks.empty()) {
+-                m_mutex.lock();
++                std::unique_lock<std::mutex> lock{m_mutex};
+                 if (m_keepAlive) {
++                    lock.unlock();
+                     QCoreApplication::processEvents();
++                    lock.lock();
+                 } else {
+-                    m_threadActive = false;
++                    assert(lock.owns_lock());
++                    lock.unlock();
+                     break;
+                 }
+-                m_mutex.unlock();
++                assert(lock.owns_lock());
++                lock.unlock();
+             }
+             m_threadActive = true;
+             m_keepAlive = false;

diff --git a/media-sound/opensoundmeter/files/opensoundmeter-jack-support.patch 
b/media-sound/opensoundmeter/files/opensoundmeter-jack-support.patch
new file mode 100644
index 000000000..b5c7ad9c9
--- /dev/null
+++ b/media-sound/opensoundmeter/files/opensoundmeter-jack-support.patch
@@ -0,0 +1,668 @@
+diff --git a/.travis.yml b/.travis.yml
+index ce9e997..2b6fbe4 100644
+--- a/.travis.yml
++++ b/.travis.yml
+@@ -10,11 +10,12 @@ before_install:
+ install:
+   - sudo apt-get install build-essential libgl1-mesa-dev
+   - sudo apt-get install qt514base qt514multimedia qt514quickcontrols2
++  - sudo apt-get install libjack-dev
+   - source /opt/qt514/bin/qt514-env.sh
+ 
+ 
+ script:
+-  - /opt/qt514/bin/qmake PREFIX=/usr
++  - /opt/qt514/bin/qmake PREFIX=/usr CONFIG+=jack
+   - make
+   - # Generate AppImage
+ #  - sudo apt-get -y install checkinstall
+diff --git a/OpenSoundMeter.pro b/OpenSoundMeter.pro
+index a52b502..f9cef4a 100644
+--- a/OpenSoundMeter.pro
++++ b/OpenSoundMeter.pro
+@@ -300,6 +300,18 @@ unix:!macx:!ios {
+         src/audio/plugins/alsa.cpp
+ 
+     LIBS += -lasound
++
++    CONFIG(jack) {
++        HEADERS += \
++            src/audio/plugins/jack.h
++
++        SOURCES += \
++            src/audio/plugins/jack.cpp
++
++        LIBS += -ljack
++
++        DEFINES += USE_JACK
++    }
+ }
+ 
+ GRAPH = $$(GRAPH_BACKEND)
+diff --git a/src/audio/client.cpp b/src/audio/client.cpp
+index d319c72..db7c377 100644
+--- a/src/audio/client.cpp
++++ b/src/audio/client.cpp
+@@ -35,6 +35,10 @@
+ #include "plugins/alsa.h"
+ #endif
+ 
++#ifdef USE_JACK
++#include "plugins/jack.h"
++#endif
++
+ #ifdef USE_ASIO
+ #include "plugins/asioplugin.h"
+ #endif
+@@ -89,6 +93,10 @@ void Client::initPlugins()
+     m_plugins.push_back(QSharedPointer<Plugin>(new AlsaPlugin()));
+ #endif
+ 
++#ifdef USE_JACK
++    m_plugins.push_back(QSharedPointer<Plugin>(new JackPlugin()));
++#endif
++
+     for (auto &&plugin : m_plugins) {
+         connect(plugin.data(), &Plugin::deviceListChanged, this, [this]() {
+             refreshDeviceList();
+diff --git a/src/audio/plugins/jack.cpp b/src/audio/plugins/jack.cpp
+new file mode 100644
+index 0000000..3a87edf
+--- /dev/null
++++ b/src/audio/plugins/jack.cpp
+@@ -0,0 +1,462 @@
++/**
++ *  OSM
++ *  Copyright (C) 2022  Adrian Schollmeyer
++
++ *  This program is free software: you can redistribute it and/or modify
++ *  it under the terms of the GNU General Public License as published by
++ *  the Free Software Foundation, either version 3 of the License, or
++ *  (at your option) any later version.
++
++ *  This program is distributed in the hope that it will be useful,
++ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
++ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
++ *  GNU General Public License for more details.
++
++ *  You should have received a copy of the GNU General Public License
++ *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
++ */
++#include "jack.h"
++#include <algorithm>
++#include <cassert>
++
++namespace {
++constexpr unsigned int DEFAULT_SAMPLE_RATE{48000};
++constexpr unsigned int DEFAULT_CHANNEL_COUNT_INPUT{2};
++constexpr unsigned int DEFAULT_CHANNEL_COUNT_OUTPUT{1};
++
++constexpr const char* PLUGIN_NAME{"JACK"};
++constexpr const char* JACK_CLIENT_NAME{"OpenSoundMeter"};
++constexpr const char* DEFAULT_MEASUREMENT_CHANNEL{"measurement"};
++constexpr const char* DEFAULT_REFERENCE_CHANNEL{"reference"};
++constexpr const char* DEFAULT_OUTPUT_CHANNEL{"out"};
++}
++
++namespace audio {
++JackClient::JackClient(const Plugin::Direction &direction, QIODevice& 
endpoint)
++    : m_client(nullptr, jackClientDeleter()), m_direction(direction), 
m_endpoint(endpoint)
++{
++    initJackClient();
++    initPorts();
++    initSampleRate();
++    initBufferSize();
++    initProcessing();
++}
++
++JackClient::~JackClient()
++{
++    // Explicitly stop processing first, to prevent us still running the JACK
++    // processor while the rest of the object is being cleaned up
++    if (m_client)
++        close();
++}
++
++jack_nframes_t JackClient::currentSampleRate() const
++{
++    return m_sampleRate;
++}
++
++std::size_t JackClient::currentChannelCount() const
++{
++    return m_ports.size();
++}
++
++Plugin::Direction JackClient::direction() const
++{
++    return m_direction;
++}
++
++void JackClient::close()
++{
++    deactivate();
++    m_client.reset(nullptr);
++}
++
++void JackClient::initJackClient()
++{
++    jack_status_t jackStatus;
++    jack_client_t* jackClient = jack_client_open(
++        JACK_CLIENT_NAME,
++        JackOptions::JackNoStartServer,
++        &jackStatus
++    );
++
++    if (!jackClient)
++        throw JackPluginException{"Failed to open JACK client"};
++
++    m_client.reset(jackClient);
++}
++
++void JackClient::initSampleRate()
++{
++    assert(m_client);
++    m_sampleRate = jack_get_sample_rate(m_client.get());
++    qDebug() << "[JACK] Sample rate set: " << m_sampleRate;
++
++    int res = jack_set_sample_rate_callback(
++        m_client.get(),
++        jackSampleRateCallback,
++        this
++    );
++    if (res != 0)
++        throw JackPluginException{"Failed to register sample rate callback"};
++}
++
++void JackClient::initBufferSize()
++{
++    assert(m_client);
++    assert(!m_ports.empty());
++    m_bufferSize = jack_get_buffer_size(m_client.get());
++    m_sampleBuffer.resize(m_bufferSize * m_ports.size());
++    qDebug() << "[JACK] Buffer size set: " << m_bufferSize;
++
++    int res = jack_set_buffer_size_callback(
++        m_client.get(),
++        jackBufferSizeCallback,
++        this
++    );
++    if (res != 0)
++        throw JackPluginException{"Failed to register buffer size callback"};
++}
++
++void JackClient::initPorts()
++{
++    assert(m_client);
++    switch (m_direction) {
++    case Plugin::Direction::Input:
++        registerInputs();
++        break;
++
++    case Plugin::Direction::Output:
++        registerOutputs();
++        break;
++
++    default:
++        throw JackPluginException{"Unsupported signal direction"};
++    }
++}
++
++void JackClient::initProcessing()
++{
++    assert(m_client);
++    int res = jack_set_process_callback(
++        m_client.get(),
++        jackProcessCallback,
++        this
++    );
++    if (res != 0)
++        throw JackPluginException{"Failed to register process callback"};
++}
++
++void JackClient::activate()
++{
++    assert(m_client);
++    if (m_active)
++        return;
++
++    qDebug() << "[JACK] Activating";
++    int res = jack_activate(m_client.get());
++    if (res != 0)
++        throw JackPluginException{"Failed to activate client"};
++
++    m_active = true;
++}
++
++void JackClient::deactivate()
++{
++    if (!m_active)
++        return;
++
++    m_active = false;
++
++    assert(m_client);
++    int res = jack_deactivate(m_client.get());
++    if (res != 0)
++        throw JackPluginException{"Failed to deactivate client"};
++}
++
++void JackClient::registerInputs()
++{
++    qDebug() << "[JACK] Registering Inputs";
++    jack_port_t *measurementPort = jack_port_register(
++        m_client.get(),
++        DEFAULT_MEASUREMENT_CHANNEL,
++        JACK_DEFAULT_AUDIO_TYPE,
++        JackPortFlags::JackPortIsInput,
++        0
++    );
++    if (!measurementPort)
++        throw JackPluginException{"Failed to register measurement port"};
++    m_ports.emplace_back(measurementPort, jackPortDeleter(m_client.get()));
++    m_portBuffers.push_back(nullptr);
++
++    jack_port_t *referencePort = jack_port_register(
++        m_client.get(),
++        DEFAULT_REFERENCE_CHANNEL,
++        JACK_DEFAULT_AUDIO_TYPE,
++        JackPortFlags::JackPortIsInput,
++        0
++    );
++    if (!referencePort)
++        throw JackPluginException{"Failed to register reference port"};
++    m_ports.emplace_back(referencePort, jackPortDeleter(m_client.get()));
++    m_portBuffers.push_back(nullptr);
++}
++
++void JackClient::registerOutputs()
++{
++    qDebug() << "[JACK] Registering Outputs";
++    jack_port_t *outputPort = jack_port_register(
++        m_client.get(),
++        DEFAULT_OUTPUT_CHANNEL,
++        JACK_DEFAULT_AUDIO_TYPE,
++        JackPortFlags::JackPortIsOutput,
++        0
++    );
++    if (!outputPort)
++        throw JackPluginException{"Failed to register output port"};
++    m_ports.emplace_back(outputPort, jackPortDeleter(m_client.get()));
++    m_portBuffers.push_back(nullptr);
++}
++
++std::function<void(jack_client_t *)> JackClient::jackClientDeleter()
++{
++    return [this](jack_client_t *client) -> void {
++        m_ports.resize(0);
++        jack_client_close(client);
++    };
++}
++
++std::function<void(jack_port_t *)> JackClient::jackPortDeleter(jack_client_t 
*client)
++{
++    // We can't use the object's client member here as it might already have
++    // changed if this deleter is called in the client object's deleter,
++    // resulting in an assertion failure or segfault.
++    assert(client);
++    return [client](jack_port_t *port) -> void {
++        jack_port_unregister(client, port);
++    };
++}
++
++int JackClient::jackSampleRateCallback(jack_nframes_t newSampleRate, void* 
obj)
++{
++    assert(obj);
++    return 
reinterpret_cast<JackClient*>(obj)->jackSampleRateCallbackInt(newSampleRate);
++}
++
++int JackClient::jackSampleRateCallbackInt(jack_nframes_t newSampleRate)
++{
++    m_sampleRate = newSampleRate;
++    qInfo() << "[JACK] Sample rate updated: " << m_sampleRate;
++    emit sampleRateChanged(m_sampleRate);
++    return 0;
++}
++
++int JackClient::jackBufferSizeCallback(jack_nframes_t newBufferSize, void* 
obj)
++{
++    assert(obj);
++    return 
reinterpret_cast<JackClient*>(obj)->jackBufferSizeCallbackInt(newBufferSize);
++}
++
++int JackClient::jackBufferSizeCallbackInt(jack_nframes_t newBufferSize)
++{
++    m_bufferSize = newBufferSize;
++    qInfo() << "[JACK] Buffer size updated: " << m_bufferSize;
++    if (newBufferSize > 0)
++        m_sampleBuffer.resize(newBufferSize * m_ports.size());
++
++    return 0;
++}
++
++int JackClient::jackProcessCallback(jack_nframes_t nframes, void* obj)
++{
++    assert(obj);
++    return 
reinterpret_cast<JackClient*>(obj)->jackProcessCallbackInt(nframes);
++}
++
++int JackClient::jackProcessCallbackInt(jack_nframes_t nframes)
++{
++    // Yes, this can actually happen
++    if (m_bufferSize == 0)
++        return 1;
++
++    if (!m_active)
++        return 0;
++
++    assert(m_ports.size() == m_portBuffers.size());
++    assert(nframes == m_bufferSize);
++    assert(m_endpoint.isOpen());
++
++    if (m_direction == Plugin::Direction::Input) {
++        processInputPorts(nframes);
++    } else {
++        processOutputPorts(nframes);
++    }
++    return 0;
++}
++
++void JackClient::processInputPorts(jack_nframes_t nframes)
++{
++    if (!m_endpoint.isWritable())
++        return;
++
++    for (std::size_t i = 0; i < m_ports.size(); ++i) {
++        m_portBuffers[i] = reinterpret_cast<float*>(
++            jack_port_get_buffer(m_ports[i].get(), nframes));
++    }
++
++    std::size_t sampleBufferPos{0};
++    for (jack_nframes_t i = 0; i < nframes; ++i) {
++        for (const float* portBuf : m_portBuffers) {
++            assert(sampleBufferPos < m_sampleBuffer.size());
++            m_sampleBuffer[sampleBufferPos] = portBuf[i];
++            sampleBufferPos++;
++        }
++    }
++    assert(sampleBufferPos == nframes * m_portBuffers.size());
++    m_endpoint.write(
++        reinterpret_cast<char*>(m_sampleBuffer.data()),
++        sampleBufferPos * sizeof(float)
++    );
++}
++
++void JackClient::processOutputPorts(jack_nframes_t nframes)
++{
++    if (!m_endpoint.isReadable())
++        return;
++
++    for (std::size_t i = 0; i < m_ports.size(); ++i) {
++        m_portBuffers[i] = reinterpret_cast<float*>(
++            jack_port_get_buffer(m_ports[i].get(), nframes));
++    }
++
++    const std::size_t samplesToWrite = std::max(
++        static_cast<std::size_t>(nframes),
++        static_cast<std::size_t>(m_endpoint.bytesAvailable() / sizeof(float))
++    );
++    assert(samplesToWrite % m_ports.size() == 0);
++
++    std::size_t portIdx{0};
++    std::size_t portBufferPos{0};
++    for (std::size_t i = 0; i < samplesToWrite; ++i) {
++        assert(portIdx < m_portBuffers.size());
++        assert(portBufferPos < m_bufferSize);
++        const std::size_t bytesRead = m_endpoint.read(
++            reinterpret_cast<char*>(m_portBuffers[portIdx] + portBufferPos),
++            sizeof(float)
++        );
++        assert(bytesRead == sizeof(float));
++
++        portIdx = (portIdx + 1) % m_portBuffers.size();
++        if (portIdx == 0)
++            portBufferPos++;
++    }
++}
++
++JackPlugin::JackPlugin()
++{
++    DeviceInfo m_defaultDeviceInfo{PLUGIN_NAME, PLUGIN_NAME};
++    m_defaultDeviceInfo.setName(PLUGIN_NAME);
++    m_defaultDeviceInfo.setInputChannels({
++        DEFAULT_MEASUREMENT_CHANNEL,
++        DEFAULT_REFERENCE_CHANNEL
++    });
++    m_defaultDeviceInfo.setOutputChannels({DEFAULT_OUTPUT_CHANNEL});
++    m_defaultDeviceInfo.setDefaultSampleRate(DEFAULT_SAMPLE_RATE);
++    m_list.push_back(m_defaultDeviceInfo);
++}
++
++QString JackPlugin::name() const
++{
++    return PLUGIN_NAME;
++}
++
++DeviceInfo::List JackPlugin::getDeviceInfoList() const
++{
++    return m_list;
++}
++
++DeviceInfo::Id JackPlugin::defaultDeviceId(
++    [[maybe_unused]] const Plugin::Direction &mode) const
++{
++    return DeviceInfo::Id();
++}
++
++Format JackPlugin::deviceFormat(
++    [[maybe_unused]] const DeviceInfo::Id &id,
++    const Plugin::Direction &mode) const
++{
++    const auto client_it = std::find_if(
++        m_clients.begin(),
++        m_clients.end(),
++        [mode](const auto& client) {
++
++        return client.second->direction() == mode;
++    });
++
++    if (client_it == m_clients.end()) {
++        if (mode == Plugin::Direction::Input)
++            return {DEFAULT_SAMPLE_RATE, DEFAULT_CHANNEL_COUNT_INPUT};
++        else
++            return {DEFAULT_SAMPLE_RATE, DEFAULT_CHANNEL_COUNT_OUTPUT};
++    } else {
++        return {
++            static_cast<unsigned int>(client_it->second->currentSampleRate()),
++            static_cast<unsigned 
int>(client_it->second->currentChannelCount())
++        };
++    }
++}
++
++Stream *JackPlugin::open(
++    [[maybe_unused]] const DeviceInfo::Id &id,
++    const Plugin::Direction &mode,
++    [[maybe_unused]] const Format &format,
++    QIODevice *endpoint)
++{
++    // The sample rate is set by JACK, so we don't mess with it.
++    // The channel count is fixed to the minimum amount of channels necessary
++    // for OSM to work as we can freely patch the cannels in JACK.
++    //
++    // The ID can be ignored as we create individual JACK clients
++    // for each requested Stream.
++    try {
++        std::unique_ptr<JackClient> jack = std::make_unique<JackClient>(mode, 
*endpoint);
++
++        Format f{
++            jack->currentSampleRate(),
++            static_cast<unsigned int>(jack->currentChannelCount())
++        };
++        Stream *stream = new Stream(f);
++
++        m_clients.emplace(stream, std::move(jack));
++        JackClient& jackClient = *m_clients.at(stream).get();
++
++        connect(stream, &Stream::closeMe, this, [this, stream, endpoint]() {
++            // Order is important here to prevent JACK still writing data to
++            // the endpoint after it has been closed.
++            m_clients.erase(stream);
++
++            if (endpoint->isOpen())
++                endpoint->close();
++
++            stream->deleteLater();
++        });
++
++        connect(
++            &jackClient,
++            &JackClient::sampleRateChanged,
++            this,
++            [stream](jack_nframes_t newRate) {
++                assert(stream);
++                stream->setSampleRate(static_cast<unsigned int>(newRate));
++        });
++
++        endpoint->open(mode == Input ? QIODevice::WriteOnly : 
QIODevice::ReadOnly);
++        jackClient.activate();
++
++        return stream;
++    } catch (JackPluginException& e) {
++        return nullptr;
++    }
++}
++
++}
+diff --git a/src/audio/plugins/jack.h b/src/audio/plugins/jack.h
+new file mode 100644
+index 0000000..9685f65
+--- /dev/null
++++ b/src/audio/plugins/jack.h
+@@ -0,0 +1,127 @@
++/**
++ *  OSM
++ *  Copyright (C) 2022  Adrian Schollmeyer
++
++ *  This program is free software: you can redistribute it and/or modify
++ *  it under the terms of the GNU General Public License as published by
++ *  the Free Software Foundation, either version 3 of the License, or
++ *  (at your option) any later version.
++
++ *  This program is distributed in the hope that it will be useful,
++ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
++ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
++ *  GNU General Public License for more details.
++
++ *  You should have received a copy of the GNU General Public License
++ *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
++ */
++#ifndef AUDIO_JACKPLUGIN_H
++#define AUDIO_JACKPLUGIN_H
++
++#include "../plugin.h"
++#include <QObject>
++#include <functional>
++#include <jack/jack.h>
++#include <jack/types.h>
++#include <map>
++#include <memory>
++#include <stdexcept>
++#include <vector>
++
++namespace audio {
++class JackClient : public QObject
++{
++    Q_OBJECT
++
++public:
++    constexpr static std::size_t BUFFER_SIZE_MULTIPLIER{4};
++
++    JackClient(const Plugin::Direction &direction, QIODevice& endpoint);
++    JackClient(JackClient&&) = default;
++    JackClient(const JackClient &) = delete;
++    ~JackClient();
++
++    void activate();
++    void deactivate();
++
++    jack_nframes_t currentSampleRate() const;
++    std::size_t currentChannelCount() const;
++
++    Plugin::Direction direction() const;
++
++signals:
++    void sampleRateChanged(jack_nframes_t newSampleRate);
++
++public slots:
++    void close();
++
++private:
++    void initJackClient();
++    void initPorts();
++    void initSampleRate();
++    void initBufferSize();
++    void initProcessing();
++
++    void registerInputs();
++    void registerOutputs();
++
++    std::function<void(jack_client_t *)> jackClientDeleter();
++    static std::function<void(jack_port_t *)> jackPortDeleter(jack_client_t 
*client);
++
++    static int jackSampleRateCallback(jack_nframes_t newSampleRate, void* 
obj);
++    int jackSampleRateCallbackInt(jack_nframes_t newSampleRate);
++
++    static int jackBufferSizeCallback(jack_nframes_t newBufferSize, void* 
obj);
++    int jackBufferSizeCallbackInt(jack_nframes_t newBufferSize);
++
++    static int jackProcessCallback(jack_nframes_t nframes, void* obj);
++    int jackProcessCallbackInt(jack_nframes_t nframes);
++
++    void processInputPorts(jack_nframes_t nframes);
++    void processOutputPorts(jack_nframes_t nframes);
++
++    std::unique_ptr<jack_client_t, std::function<void(jack_client_t *)>> 
m_client;
++    std::vector<std::unique_ptr<jack_port_t, std::function<void(jack_port_t 
*)>>> m_ports;
++    std::vector<float*> m_portBuffers;
++    jack_nframes_t m_sampleRate;
++    jack_nframes_t m_bufferSize;
++    const Plugin::Direction m_direction;
++    QIODevice& m_endpoint;
++    std::vector<float> m_sampleBuffer;
++    bool m_active{false};
++};
++
++class JackPlugin : public Plugin
++{
++    Q_OBJECT
++
++public:
++    JackPlugin();
++    JackPlugin(const JackPlugin &) = delete;
++    virtual ~JackPlugin() = default;
++
++    QString name() const override;
++    DeviceInfo::List getDeviceInfoList() const override;
++    DeviceInfo::Id defaultDeviceId(const Direction &mode) const override;
++
++    Format deviceFormat(const DeviceInfo::Id &id, const Direction &mode) 
const override;
++    Stream *open(const DeviceInfo::Id &id, const Direction &mode, const 
Format &format, QIODevice *endpoint) override;
++
++private:
++    DeviceInfo::List m_list{};
++    DeviceInfo m_defaultDeviceInfoInput;
++    DeviceInfo m_defaultDeviceInfoOutput;
++    std::map<Stream *, std::unique_ptr<JackClient>> m_clients{};
++};
++
++class JackPluginException : public std::runtime_error
++{
++public:
++    template<typename T>
++    JackPluginException(const T& arg)
++        : std::runtime_error(arg)
++    {}
++};
++}
++
++#endif // AUDIO_JACKPLUGIN_H

diff --git a/media-sound/opensoundmeter/opensoundmeter-1.2.2.ebuild 
b/media-sound/opensoundmeter/opensoundmeter-1.2.2.ebuild
new file mode 100644
index 000000000..372bb74c4
--- /dev/null
+++ b/media-sound/opensoundmeter/opensoundmeter-1.2.2.ebuild
@@ -0,0 +1,60 @@
+# Copyright 2022 Gentoo Authors
+# Distributed under the terms of the GNU General Public License v2
+
+EAPI=8
+
+inherit qmake-utils desktop
+
+DESCRIPTION="FFT based application for tuning sound systems"
+HOMEPAGE="https://opensoundmeter.com/en/ https://github.com/psmokotnin/osm";
+SRC_URI="
+       https://github.com/psmokotnin/osm/archive/refs/tags/v${PV}.tar.gz -> 
${P}.gh.tar.gz
+"
+
+# GPL-3 for the codebase
+# N-Noise-EULA for the M-Noise noise generator
+LICENSE="GPL-3 M-Noise-EULA"
+SLOT="0"
+KEYWORDS="~amd64"
+
+DEPEND="
+       dev-qt/qtcore:5=
+       dev-qt/qtnetwork:5=
+       dev-qt/qtopengl:5=
+       dev-qt/qtquickcontrols2:5=
+       dev-qt/qtwidgets:5=
+       media-libs/alsa-lib
+"
+RDEPEND="${DEPEND}"
+# qtcore for qmake5
+BDEPEND="
+       dev-qt/qtcore:5
+"
+
+S="${WORKDIR}/osm-${PV}"
+DOCS=( "README.md" )
+
+src_prepare() {
+       default
+       mkdir -p build || die
+}
+
+src_configure() {
+       cd build || die
+       eqmake5 ../OpenSoundMeter.pro
+}
+
+src_compile() {
+       cd build || die
+       emake
+}
+
+src_install() {
+       # The default OpenSoundMeter doesn't respect standard dirs, so we 
install
+       # manually
+       dobin build/OpenSoundMeter
+
+       sed "s/Icon=white/Icon=${PN}/g" "OpenSoundMeter.desktop" || die
+       domenu "OpenSoundMeter.desktop"
+       newicon icons/white.png "${PN}.png"
+}

diff --git a/media-sound/opensoundmeter/opensoundmeter-1.2.2_p20220104.ebuild 
b/media-sound/opensoundmeter/opensoundmeter-1.2.2_p20220104.ebuild
new file mode 100644
index 000000000..a35275cec
--- /dev/null
+++ b/media-sound/opensoundmeter/opensoundmeter-1.2.2_p20220104.ebuild
@@ -0,0 +1,76 @@
+# Copyright 2022 Gentoo Authors
+# Distributed under the terms of the GNU General Public License v2
+
+EAPI=8
+
+inherit qmake-utils desktop
+
+MY_PV="$(ver_cut 1-3)"
+
+DESCRIPTION="FFT based application for tuning sound systems"
+HOMEPAGE="https://opensoundmeter.com/en/ https://github.com/psmokotnin/osm";
+SRC_URI="
+       https://github.com/psmokotnin/osm/archive/refs/tags/v${MY_PV}.tar.gz -> 
opensoundmeter-${MY_PV}.gh.tar.gz
+"
+
+# GPL-3 for the codebase
+# N-Noise-EULA for the M-Noise noise generator
+LICENSE="GPL-3 M-Noise-EULA"
+SLOT="0"
+KEYWORDS="~amd64"
+IUSE="jack"
+
+DEPEND="
+       dev-qt/qtcore:5=
+       dev-qt/qtnetwork:5=
+       dev-qt/qtopengl:5=
+       dev-qt/qtquickcontrols2:5=
+       dev-qt/qtwidgets:5=
+       media-libs/alsa-lib
+
+       jack? (
+               virtual/jack
+       )
+"
+RDEPEND="${DEPEND}"
+# qtcore for qmake5
+BDEPEND="
+       dev-qt/qtcore:5
+"
+
+PATCHES=(
+       # Two patches that just take far too long upstream
+       "${FILESDIR}/${PN}-jack-support.patch"
+       "${FILESDIR}/${PN}-deadlock-fix.patch"
+)
+
+S="${WORKDIR}/osm-${MY_PV}"
+DOCS=( "README.md" )
+
+src_prepare() {
+       default
+       mkdir -p build || die
+}
+
+src_configure() {
+       cd build || die
+       local myeqmakeargs=()
+       use jack && myeqmakeargs+=( "CONFIG+=jack" )
+
+       eqmake5 "${myeqmakeargs[@]}" ../OpenSoundMeter.pro
+}
+
+src_compile() {
+       cd build || die
+       emake
+}
+
+src_install() {
+       # The default OpenSoundMeter doesn't respect standard dirs, so we 
install
+       # manually
+       dobin build/OpenSoundMeter
+
+       sed "s/Icon=white/Icon=${PN}/g" "OpenSoundMeter.desktop" || die
+       domenu "OpenSoundMeter.desktop"
+       newicon icons/white.png "${PN}.png"
+}

Reply via email to