Collection of tools useful for audio production
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

266 lines
7.7KB

  1. /*
  2. * Simple JACK Audio Meter
  3. * Copyright (C) 2011-2015 Filipe Coelho <falktx@falktx.com>
  4. *
  5. * This program is free software; you can redistribute it and/or modify
  6. * it under the terms of the GNU General Public License as published by
  7. * the Free Software Foundation; either version 2 of the License, or
  8. * any later version.
  9. *
  10. * This program is distributed in the hope that it will be useful,
  11. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. * GNU General Public License for more details.
  14. *
  15. * For a full copy of the GNU General Public License see the COPYING file
  16. */
  17. #include <QtCore/Qt>
  18. #ifndef Q_COMPILER_LAMBDA
  19. # define nullptr (0)
  20. #endif
  21. #define VERSION "0.8.1"
  22. #include "../jack_utils.hpp"
  23. #include "../widgets/digitalpeakmeter.hpp"
  24. #include <cmath>
  25. #include <QtGui/QApplication>
  26. #include <QtGui/QIcon>
  27. #include <QtGui/QMessageBox>
  28. // -------------------------------
  29. volatile double x_portValue1 = 0.0;
  30. volatile double x_portValue2 = 0.0;
  31. volatile bool x_isOutput = true;
  32. volatile bool x_needReconnect = false;
  33. volatile bool x_quitNow = false;
  34. jack_client_t* jClient = nullptr;
  35. jack_port_t* jPort1 = nullptr;
  36. jack_port_t* jPort2 = nullptr;
  37. QString gClientName;
  38. // -------------------------------
  39. // JACK callbacks
  40. int process_callback(const jack_nframes_t nframes, void*)
  41. {
  42. float* const jOut1 = (float*)jackbridge_port_get_buffer(jPort1, nframes);
  43. float* const jOut2 = (float*)jackbridge_port_get_buffer(jPort2, nframes);
  44. for (jack_nframes_t i = 0; i < nframes; i++)
  45. {
  46. if (std::abs(jOut1[i]) > x_portValue1)
  47. x_portValue1 = std::abs(jOut1[i]);
  48. if (std::abs(jOut2[i]) > x_portValue2)
  49. x_portValue2 = std::abs(jOut2[i]);
  50. }
  51. return 0;
  52. }
  53. void port_callback(jack_port_id_t, jack_port_id_t, int, void*)
  54. {
  55. if (x_isOutput)
  56. x_needReconnect = true;
  57. }
  58. #ifdef HAVE_JACKSESSION
  59. void session_callback(jack_session_event_t* const event, void* const arg)
  60. {
  61. #ifdef Q_OS_LINUX
  62. QString filepath("cadence-jackmeter");
  63. Q_UNUSED(arg);
  64. #else
  65. QString filepath((char*)arg);
  66. #endif
  67. event->command_line = strdup(filepath.toUtf8().constData());
  68. jackbridge_session_reply(jClient, event);
  69. if (event->type == JackSessionSaveAndQuit)
  70. x_quitNow = true;
  71. jackbridge_session_event_free(event);
  72. }
  73. #endif
  74. // -------------------------------
  75. // helpers
  76. void reconnect_ports()
  77. {
  78. x_needReconnect = false;
  79. const QString nameIn1(gClientName+":in1");
  80. const QString nameIn2(gClientName+":in2");
  81. if (x_isOutput)
  82. {
  83. jack_port_t* const jPlayPort1 = jackbridge_port_by_name(jClient, x_isOutput ? "system:playback_1" : "system:capture_1");
  84. jack_port_t* const jPlayPort2 = jackbridge_port_by_name(jClient, x_isOutput ? "system:playback_2" : "system:capture_2");
  85. std::vector<char*> jPortList1(jackbridge_port_get_all_connections_as_vector(jClient, jPlayPort1));
  86. std::vector<char*> jPortList2(jackbridge_port_get_all_connections_as_vector(jClient, jPlayPort2));
  87. foreach (char* const& thisPortName, jPortList1)
  88. {
  89. jack_port_t* const thisPort = jackbridge_port_by_name(jClient, thisPortName);
  90. if (! (jackbridge_port_is_mine(jClient, thisPort) || jackbridge_port_connected_to(jPort1, thisPortName)))
  91. jackbridge_connect(jClient, thisPortName, nameIn1.toUtf8().constData());
  92. free(thisPortName);
  93. }
  94. foreach (char* const& thisPortName, jPortList2)
  95. {
  96. jack_port_t* const thisPort = jackbridge_port_by_name(jClient, thisPortName);
  97. if (! (jackbridge_port_is_mine(jClient, thisPort) || jackbridge_port_connected_to(jPort2, thisPortName)))
  98. jackbridge_connect(jClient, thisPortName, nameIn2.toUtf8().constData());
  99. free(thisPortName);
  100. }
  101. jPortList1.clear();
  102. jPortList2.clear();
  103. }
  104. else
  105. {
  106. if (jack_port_t* const jRecPort1 = jackbridge_port_by_name(jClient, "system:capture_1"))
  107. {
  108. if (! jackbridge_port_connected_to(jPort1, "system:capture_1"))
  109. jackbridge_connect(jClient, "system:capture_1", nameIn1.toUtf8().constData());
  110. }
  111. if (jack_port_t* const jRecPort2 = jackbridge_port_by_name(jClient, "system:capture_2"))
  112. {
  113. if (! jackbridge_port_connected_to(jPort2, "system:capture_2"))
  114. jackbridge_connect(jClient, "system:capture_2", nameIn2.toUtf8().constData());
  115. }
  116. }
  117. }
  118. // -------------------------------
  119. // Meter class
  120. class MeterW : public DigitalPeakMeter
  121. {
  122. public:
  123. MeterW() : DigitalPeakMeter(nullptr)
  124. {
  125. setWindowFlags(Qt::Tool | Qt::WindowStaysOnTopHint);
  126. setWindowTitle(gClientName);
  127. if (x_isOutput)
  128. setColor(Color::GREEN);
  129. else
  130. setColor(Color::BLUE);
  131. setChannels(2);
  132. setOrientation(VERTICAL);
  133. setSmoothRelease(1);
  134. displayMeter(1, 0.0f);
  135. displayMeter(2, 0.0f);
  136. int refresh = float(jackbridge_get_buffer_size(jClient)) / jackbridge_get_sample_rate(jClient) * 1000;
  137. m_peakTimerId = startTimer(refresh > 50 ? refresh : 50);
  138. }
  139. protected:
  140. void timerEvent(QTimerEvent* event)
  141. {
  142. if (x_quitNow)
  143. {
  144. close();
  145. x_quitNow = false;
  146. return;
  147. }
  148. if (event->timerId() == m_peakTimerId)
  149. {
  150. displayMeter(1, x_portValue1);
  151. displayMeter(2, x_portValue2);
  152. x_portValue1 = 0.0;
  153. x_portValue2 = 0.0;
  154. if (x_needReconnect)
  155. reconnect_ports();
  156. }
  157. QWidget::timerEvent(event);
  158. }
  159. private:
  160. int m_peakTimerId;
  161. };
  162. // -------------------------------
  163. int main(int argc, char* argv[])
  164. {
  165. QApplication app(argc, argv);
  166. app.setApplicationName("JackMeter");
  167. app.setApplicationVersion(VERSION);
  168. app.setOrganizationName("Cadence");
  169. app.setWindowIcon(QIcon(":/scalable/cadence.svg"));
  170. if (app.arguments().contains("-in"))
  171. x_isOutput = false;
  172. // JACK initialization
  173. jack_status_t jStatus;
  174. #ifdef HAVE_JACKSESSION
  175. jack_options_t jOptions = static_cast<jack_options_t>(JackNoStartServer|JackUseExactName|JackSessionID);
  176. #else
  177. jack_options_t jOptions = static_cast<jack_options_t>(JackNoStartServer|JackUseExactName);
  178. #endif
  179. jClient = jackbridge_client_open(x_isOutput ? "M" : "Mi", jOptions, &jStatus);
  180. if (! jClient)
  181. {
  182. std::string errorString(jackbridge_status_get_error_string(jStatus));
  183. QMessageBox::critical(nullptr, app.translate("MeterW", "Error"), app.translate("MeterW",
  184. "Could not connect to JACK, possible reasons:\n"
  185. "%1").arg(QString::fromStdString(errorString)));
  186. return 1;
  187. }
  188. gClientName = jackbridge_get_client_name(jClient);
  189. jPort1 = jackbridge_port_register(jClient, "in1", JACK_DEFAULT_AUDIO_TYPE, JackPortIsInput, 0);
  190. jPort2 = jackbridge_port_register(jClient, "in2", JACK_DEFAULT_AUDIO_TYPE, JackPortIsInput, 0);
  191. jackbridge_set_process_callback(jClient, process_callback, nullptr);
  192. jackbridge_set_port_connect_callback(jClient, port_callback, nullptr);
  193. #ifdef HAVE_JACKSESSION
  194. jackbridge_set_session_callback(jClient, session_callback, argv[0]);
  195. #endif
  196. jackbridge_activate(jClient);
  197. reconnect_ports();
  198. // Show GUI
  199. MeterW gui;
  200. gui.resize(70, 600);
  201. gui.show();
  202. gui.setAttribute(Qt::WA_QuitOnClose);
  203. // App-Loop
  204. int ret = app.exec();
  205. jackbridge_deactivate(jClient);
  206. jackbridge_client_close(jClient);
  207. return ret;
  208. }