mirror of
https://github.com/gentoo-mirror/guru.git
synced 2026-07-08 14:43:27 -04:00
media-sound/opensoundmeter: treeclean
Closes: https://bugs.gentoo.org/932799 (pkgremoved) Signed-off-by: David Roman <stkw0@disroot.org>
This commit is contained in:
@@ -1,2 +0,0 @@
|
||||
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
|
||||
@@ -1,64 +0,0 @@
|
||||
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;
|
||||
@@ -1,668 +0,0 @@
|
||||
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
|
||||
@@ -1,12 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE pkgmetadata SYSTEM "https://www.gentoo.org/dtd/metadata.dtd">
|
||||
<pkgmetadata>
|
||||
<maintainer type="person">
|
||||
<email>nex+b-g-o@nexadn.de</email>
|
||||
</maintainer>
|
||||
<upstream>
|
||||
<remote-id type="github">psmokotnin/osm</remote-id>
|
||||
<bugs-to>https://github.com/psmokotnin/osm/issues</bugs-to>
|
||||
<changelog>https://github.com/psmokotnin/osm/releases</changelog>
|
||||
</upstream>
|
||||
</pkgmetadata>
|
||||
@@ -1,61 +0,0 @@
|
||||
# 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
|
||||
"
|
||||
|
||||
S="${WORKDIR}/osm-${PV}"
|
||||
|
||||
# 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
|
||||
"
|
||||
|
||||
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"
|
||||
}
|
||||
@@ -1,61 +0,0 @@
|
||||
# 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
|
||||
"
|
||||
|
||||
S="${WORKDIR}/osm-${PV}"
|
||||
|
||||
# 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
|
||||
"
|
||||
|
||||
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"
|
||||
}
|
||||
@@ -1,77 +0,0 @@
|
||||
# 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
|
||||
"
|
||||
|
||||
S="${WORKDIR}/osm-${MY_PV}"
|
||||
|
||||
# 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"
|
||||
)
|
||||
|
||||
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"
|
||||
}
|
||||
@@ -42,11 +42,6 @@ mpv-plugin/kde-do-not-disturb
|
||||
mpv-plugin/kde-night-color
|
||||
x11-misc/aerial-sddm-theme
|
||||
|
||||
# David Roman <stkw0@disroot.org> (2026-05-21)
|
||||
# Not updated since 2023, depends on masked Qt5
|
||||
# Removal after 2026-06-21
|
||||
media-sound/opensoundmeter
|
||||
|
||||
# David Roman <stkw0@disroot.org> (2026-05-21)
|
||||
# Unmaintained upstream, depends on masked Qt5
|
||||
# Removal after 2026-06-21
|
||||
|
||||
Reference in New Issue
Block a user