262 lines
7.6 KiB
C++
262 lines
7.6 KiB
C++
/*
|
|
* Simple JACK Audio Meter
|
|
* Copyright (C) 2011-2015 Filipe Coelho <falktx@falktx.com>
|
|
*
|
|
* 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 2 of the License, or
|
|
* 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.
|
|
*
|
|
* For a full copy of the GNU General Public License see the COPYING file
|
|
*/
|
|
|
|
#include <QtCore/Qt>
|
|
|
|
#ifndef Q_COMPILER_LAMBDA
|
|
# define nullptr (0)
|
|
#endif
|
|
|
|
#define VERSION "0.8.1"
|
|
|
|
#include "../jack_utils.hpp"
|
|
#include "../widgets/digitalpeakmeter.hpp"
|
|
|
|
#include <cmath>
|
|
#include <QtGui/QIcon>
|
|
#include <QtWidgets/QApplication>
|
|
#include <QtWidgets/QMessageBox>
|
|
|
|
// -------------------------------
|
|
|
|
volatile double x_portValue1 = 0.0;
|
|
volatile double x_portValue2 = 0.0;
|
|
volatile bool x_isOutput = true;
|
|
volatile bool x_needReconnect = false;
|
|
volatile bool x_quitNow = false;
|
|
|
|
jack_client_t* jClient = nullptr;
|
|
jack_port_t* jPort1 = nullptr;
|
|
jack_port_t* jPort2 = nullptr;
|
|
|
|
QString gClientName;
|
|
|
|
// -------------------------------
|
|
// JACK callbacks
|
|
|
|
int process_callback(const jack_nframes_t nframes, void*)
|
|
{
|
|
float* const jOut1 = (float*)jackbridge_port_get_buffer(jPort1, nframes);
|
|
float* const jOut2 = (float*)jackbridge_port_get_buffer(jPort2, nframes);
|
|
|
|
for (jack_nframes_t i = 0; i < nframes; i++)
|
|
{
|
|
if (std::abs(jOut1[i]) > x_portValue1)
|
|
x_portValue1 = std::abs(jOut1[i]);
|
|
|
|
if (std::abs(jOut2[i]) > x_portValue2)
|
|
x_portValue2 = std::abs(jOut2[i]);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
void port_callback(jack_port_id_t, jack_port_id_t, int, void*)
|
|
{
|
|
if (x_isOutput)
|
|
x_needReconnect = true;
|
|
}
|
|
|
|
#ifdef HAVE_JACKSESSION
|
|
void session_callback(jack_session_event_t* const event, void* const arg)
|
|
{
|
|
#ifdef Q_OS_LINUX
|
|
QString filepath("cadence-jackmeter");
|
|
Q_UNUSED(arg);
|
|
#else
|
|
QString filepath((char*)arg);
|
|
#endif
|
|
|
|
event->command_line = strdup(filepath.toUtf8().constData());
|
|
|
|
jackbridge_session_reply(jClient, event);
|
|
|
|
if (event->type == JackSessionSaveAndQuit)
|
|
x_quitNow = true;
|
|
|
|
jackbridge_session_event_free(event);
|
|
}
|
|
#endif
|
|
|
|
// -------------------------------
|
|
// helpers
|
|
|
|
void reconnect_ports()
|
|
{
|
|
x_needReconnect = false;
|
|
|
|
const QString nameIn1(gClientName+":in1");
|
|
const QString nameIn2(gClientName+":in2");
|
|
|
|
if (x_isOutput)
|
|
{
|
|
jack_port_t* const jPlayPort1 = jackbridge_port_by_name(jClient, x_isOutput ? "system:playback_1" : "system:capture_1");
|
|
jack_port_t* const jPlayPort2 = jackbridge_port_by_name(jClient, x_isOutput ? "system:playback_2" : "system:capture_2");
|
|
std::vector<char*> jPortList1(jackbridge_port_get_all_connections_as_vector(jClient, jPlayPort1));
|
|
std::vector<char*> jPortList2(jackbridge_port_get_all_connections_as_vector(jClient, jPlayPort2));
|
|
|
|
foreach (char* const& thisPortName, jPortList1)
|
|
{
|
|
jack_port_t* const thisPort = jackbridge_port_by_name(jClient, thisPortName);
|
|
|
|
if (! (jackbridge_port_is_mine(jClient, thisPort) || jackbridge_port_connected_to(jPort1, thisPortName)))
|
|
jackbridge_connect(jClient, thisPortName, nameIn1.toUtf8().constData());
|
|
|
|
free(thisPortName);
|
|
}
|
|
|
|
foreach (char* const& thisPortName, jPortList2)
|
|
{
|
|
jack_port_t* const thisPort = jackbridge_port_by_name(jClient, thisPortName);
|
|
|
|
if (! (jackbridge_port_is_mine(jClient, thisPort) || jackbridge_port_connected_to(jPort2, thisPortName)))
|
|
jackbridge_connect(jClient, thisPortName, nameIn2.toUtf8().constData());
|
|
|
|
free(thisPortName);
|
|
}
|
|
|
|
jPortList1.clear();
|
|
jPortList2.clear();
|
|
}
|
|
else
|
|
{
|
|
if (jackbridge_port_by_name(jClient, "system:capture_1") != nullptr)
|
|
if (! jackbridge_port_connected_to(jPort1, "system:capture_1"))
|
|
jackbridge_connect(jClient, "system:capture_1", nameIn1.toUtf8().constData());
|
|
|
|
if (jackbridge_port_by_name(jClient, "system:capture_2") != nullptr)
|
|
if (! jackbridge_port_connected_to(jPort2, "system:capture_2"))
|
|
jackbridge_connect(jClient, "system:capture_2", nameIn2.toUtf8().constData());
|
|
}
|
|
}
|
|
|
|
// -------------------------------
|
|
// Meter class
|
|
|
|
class MeterW : public DigitalPeakMeter
|
|
{
|
|
public:
|
|
MeterW() : DigitalPeakMeter(nullptr)
|
|
{
|
|
setWindowFlags(Qt::Tool | Qt::WindowStaysOnTopHint);
|
|
setWindowTitle(gClientName);
|
|
|
|
if (x_isOutput)
|
|
setColor(Color::GREEN);
|
|
else
|
|
setColor(Color::BLUE);
|
|
|
|
setChannels(2);
|
|
setOrientation(VERTICAL);
|
|
setSmoothRelease(1);
|
|
|
|
displayMeter(1, 0.0f);
|
|
displayMeter(2, 0.0f);
|
|
|
|
int refresh = float(jackbridge_get_buffer_size(jClient)) / jackbridge_get_sample_rate(jClient) * 1000;
|
|
|
|
m_peakTimerId = startTimer(refresh > 50 ? refresh : 50);
|
|
}
|
|
|
|
protected:
|
|
void timerEvent(QTimerEvent* event)
|
|
{
|
|
if (x_quitNow)
|
|
{
|
|
close();
|
|
x_quitNow = false;
|
|
return;
|
|
}
|
|
|
|
if (event->timerId() == m_peakTimerId)
|
|
{
|
|
displayMeter(1, x_portValue1);
|
|
displayMeter(2, x_portValue2);
|
|
x_portValue1 = 0.0;
|
|
x_portValue2 = 0.0;
|
|
|
|
if (x_needReconnect)
|
|
reconnect_ports();
|
|
}
|
|
|
|
QWidget::timerEvent(event);
|
|
}
|
|
|
|
private:
|
|
int m_peakTimerId;
|
|
};
|
|
|
|
// -------------------------------
|
|
|
|
int main(int argc, char* argv[])
|
|
{
|
|
QApplication app(argc, argv);
|
|
app.setApplicationName("JackMeter");
|
|
app.setApplicationVersion(VERSION);
|
|
app.setOrganizationName("Cadence");
|
|
app.setWindowIcon(QIcon(":/scalable/cadence.svg"));
|
|
|
|
if (app.arguments().contains("-in"))
|
|
x_isOutput = false;
|
|
|
|
// JACK initialization
|
|
jack_status_t jStatus;
|
|
#ifdef HAVE_JACKSESSION
|
|
jack_options_t jOptions = static_cast<jack_options_t>(JackNoStartServer|JackUseExactName|JackSessionID);
|
|
#else
|
|
jack_options_t jOptions = static_cast<jack_options_t>(JackNoStartServer|JackUseExactName);
|
|
#endif
|
|
jClient = jackbridge_client_open(x_isOutput ? "M" : "Mi", jOptions, &jStatus);
|
|
|
|
if (! jClient)
|
|
{
|
|
std::string errorString(jackbridge_status_get_error_string(jStatus));
|
|
QMessageBox::critical(nullptr, app.translate("MeterW", "Error"), app.translate("MeterW",
|
|
"Could not connect to JACK, possible reasons:\n"
|
|
"%1").arg(QString::fromStdString(errorString)));
|
|
return 1;
|
|
}
|
|
|
|
gClientName = jackbridge_get_client_name(jClient);
|
|
|
|
jPort1 = jackbridge_port_register(jClient, "in1", JACK_DEFAULT_AUDIO_TYPE, JackPortIsInput, 0);
|
|
jPort2 = jackbridge_port_register(jClient, "in2", JACK_DEFAULT_AUDIO_TYPE, JackPortIsInput, 0);
|
|
|
|
jackbridge_set_process_callback(jClient, process_callback, nullptr);
|
|
jackbridge_set_port_connect_callback(jClient, port_callback, nullptr);
|
|
#ifdef HAVE_JACKSESSION
|
|
jackbridge_set_session_callback(jClient, session_callback, argv[0]);
|
|
#endif
|
|
jackbridge_activate(jClient);
|
|
|
|
reconnect_ports();
|
|
|
|
// Show GUI
|
|
MeterW gui;
|
|
gui.resize(70, 600);
|
|
gui.show();
|
|
gui.setAttribute(Qt::WA_QuitOnClose);
|
|
|
|
// App-Loop
|
|
int ret = app.exec();
|
|
|
|
jackbridge_deactivate(jClient);
|
|
jackbridge_client_close(jClient);
|
|
|
|
return ret;
|
|
}
|