/* * Simple JACK Audio Meter * Copyright (C) 2011-2015 Filipe Coelho * * 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 #ifndef Q_COMPILER_LAMBDA # define nullptr (0) #endif #define VERSION "0.8.1" #include "../jack_utils.hpp" #include "../widgets/digitalpeakmeter.hpp" #include #include #include #include // ------------------------------- 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 jPortList1(jackbridge_port_get_all_connections_as_vector(jClient, jPlayPort1)); std::vector 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 (jack_port_t* const jRecPort1 = jackbridge_port_by_name(jClient, "system:capture_1")) { if (! jackbridge_port_connected_to(jPort1, "system:capture_1")) jackbridge_connect(jClient, "system:capture_1", nameIn1.toUtf8().constData()); } if (jack_port_t* const jRecPort2 = jackbridge_port_by_name(jClient, "system:capture_2")) { 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(JackNoStartServer|JackUseExactName|JackSessionID); #else jack_options_t jOptions = static_cast(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; }