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.

235 lines
6.2KB

  1. /*
  2. * Simple JACK Audio Meter
  3. * Copyright (C) 2011-2012 Filipe Coelho <falktx@gmail.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. #include "../jack_utils.h"
  22. #include "../widgets/digitalpeakmeter.h"
  23. #include <QtGui/QApplication>
  24. #include <QtGui/QMessageBox>
  25. // -------------------------------
  26. volatile double x_portValue1 = 0.0;
  27. volatile double x_portValue2 = 0.0;
  28. volatile bool x_needReconnect = false;
  29. volatile bool x_quitNow = false;
  30. jack_client_t* jClient = nullptr;
  31. jack_port_t* jPort1 = nullptr;
  32. jack_port_t* jPort2 = nullptr;
  33. // -------------------------------
  34. float abs_f(const float value)
  35. {
  36. if (value < 1.0f)
  37. return -value;
  38. return value;
  39. }
  40. // -------------------------------
  41. // JACK callbacks
  42. int process_callback(jack_nframes_t nframes, void*)
  43. {
  44. float* jOut1 = (float*)jack_port_get_buffer(jPort1, nframes);
  45. float* jOut2 = (float*)jack_port_get_buffer(jPort2, nframes);
  46. for (jack_nframes_t i = 0; i < nframes; i++)
  47. {
  48. if (abs_f(jOut1[i]) > x_portValue1)
  49. x_portValue1 = abs_f(jOut1[i]);
  50. if (abs_f(jOut2[i]) > x_portValue2)
  51. x_portValue2 = abs_f(jOut2[i]);
  52. }
  53. return 0;
  54. }
  55. void port_callback(jack_port_id_t, jack_port_id_t, int, void*)
  56. {
  57. x_needReconnect = true;
  58. }
  59. #ifdef HAVE_JACKSESSION
  60. void session_callback(jack_session_event_t* event, void* arg)
  61. {
  62. #ifdef Q_OS_LINUX
  63. QString filepath("cadence_jackmeter");
  64. Q_UNUSED(arg);
  65. #else
  66. QString filepath((char*)arg);
  67. #endif
  68. event->command_line = strdup(filepath.toUtf8().constData());
  69. jack_session_reply(jClient, event);
  70. if (event->type == JackSessionSaveAndQuit)
  71. x_quitNow = true;
  72. jack_session_event_free(event);
  73. }
  74. #endif
  75. // -------------------------------
  76. // helpers
  77. void reconnect_inputs()
  78. {
  79. x_needReconnect = false;
  80. jack_port_t* const jPlayPort1 = jack_port_by_name(jClient, "system:playback_1");
  81. jack_port_t* const jPlayPort2 = jack_port_by_name(jClient, "system:playback_2");
  82. std::vector<char*> jPortList1(jack_port_get_all_connections_as_vector(jClient, jPlayPort1));
  83. std::vector<char*> jPortList2(jack_port_get_all_connections_as_vector(jClient, jPlayPort2));
  84. foreach (char* const& thisPortName, jPortList1)
  85. {
  86. jack_port_t* thisPort = jack_port_by_name(jClient, thisPortName);
  87. if (! (jack_port_is_mine(jClient, thisPort) || jack_port_connected_to(jPort1, thisPortName)))
  88. jack_connect(jClient, thisPortName, "M:in1");
  89. free(thisPortName);
  90. }
  91. foreach (char* const& thisPortName, jPortList2)
  92. {
  93. jack_port_t* thisPort = jack_port_by_name(jClient, thisPortName);
  94. if (! (jack_port_is_mine(jClient, thisPort) || jack_port_connected_to(jPort2, thisPortName)))
  95. jack_connect(jClient, thisPortName, "M:in2");
  96. free(thisPortName);
  97. }
  98. jPortList1.clear();
  99. jPortList2.clear();
  100. }
  101. // -------------------------------
  102. // Meter class
  103. class MeterW : public DigitalPeakMeter
  104. {
  105. public:
  106. MeterW() : DigitalPeakMeter(nullptr)
  107. {
  108. setWindowFlags(Qt::Tool | Qt::WindowStaysOnTopHint);
  109. setWindowTitle("M");
  110. setChannels(2);
  111. setOrientation(VERTICAL);
  112. setSmoothRelease(1);
  113. displayMeter(1, 0.0);
  114. displayMeter(2, 0.0);
  115. int refresh = float(jack_get_buffer_size(jClient)) / jack_get_sample_rate(jClient) * 1000;
  116. setRefreshRate(refresh > 25 ? refresh : 25);
  117. m_peakTimerId = startTimer(refresh > 50 ? refresh : 50);
  118. }
  119. protected:
  120. void timerEvent(QTimerEvent* event)
  121. {
  122. if (x_quitNow)
  123. {
  124. close();
  125. x_quitNow = false;
  126. return;
  127. }
  128. if (event->timerId() == m_peakTimerId)
  129. {
  130. displayMeter(1, x_portValue1);
  131. displayMeter(2, x_portValue2);
  132. x_portValue1 = 0.0;
  133. x_portValue2 = 0.0;
  134. if (x_needReconnect)
  135. reconnect_inputs();
  136. }
  137. QWidget::timerEvent(event);
  138. }
  139. private:
  140. int m_peakTimerId;
  141. };
  142. // -------------------------------
  143. int main(int argc, char* argv[])
  144. {
  145. QApplication app(argc, argv);
  146. // JACK initialization
  147. jack_status_t jStatus;
  148. #ifdef HAVE_JACKSESSION
  149. jack_options_t jOptions = static_cast<JackOptions>(JackNoStartServer|JackUseExactName|JackSessionID);
  150. #else
  151. jack_options_t jOptions = static_cast<JackOptions>(JackNoStartServer|JackUseExactName);
  152. #endif
  153. jClient = jack_client_open("M", jOptions, &jStatus);
  154. if (! jClient)
  155. {
  156. std::string errorString(jack_status_get_error_string(jStatus));
  157. QMessageBox::critical(nullptr, app.translate("MeterW", "Error"), app.translate("MeterW",
  158. "Could not connect to JACK, possible reasons:\n"
  159. "%1").arg(QString::fromStdString(errorString)));
  160. return 1;
  161. }
  162. jPort1 = jack_port_register(jClient, "in1", JACK_DEFAULT_AUDIO_TYPE, JackPortIsInput, 0);
  163. jPort2 = jack_port_register(jClient, "in2", JACK_DEFAULT_AUDIO_TYPE, JackPortIsInput, 0);
  164. jack_set_process_callback(jClient, process_callback, nullptr);
  165. jack_set_port_connect_callback(jClient, port_callback, nullptr);
  166. #ifdef HAVE_JACKSESSION
  167. jack_set_session_callback(jClient, session_callback, argv[0]);
  168. #endif
  169. jack_activate(jClient);
  170. reconnect_inputs();
  171. // Show GUI
  172. MeterW gui;
  173. gui.resize(70, 600);
  174. gui.show();
  175. gui.setAttribute(Qt::WA_QuitOnClose);
  176. // App-Loop
  177. int ret = app.exec();
  178. jack_deactivate(jClient);
  179. jack_client_close(jClient);
  180. return ret;
  181. }