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.

232 lines
6.3KB

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