| @@ -0,0 +1,231 @@ | |||||
| #!/usr/bin/env python | |||||
| # -*- coding: utf-8 -*- | |||||
| # Digital Peak Meter, a custom Qt4 widget | |||||
| # Copyright (C) 2012 Filipe Coelho <falktx@gmail.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 | |||||
| # Imports (Global) | |||||
| from PyQt4.QtCore import qCritical, Qt, QRectF, QTimer, QSize | |||||
| from PyQt4.QtGui import QColor, QLinearGradient, QPainter, QWidget | |||||
| # Widget Class | |||||
| class DigitalPeakMeter(QWidget): | |||||
| HORIZONTAL = 1 | |||||
| VERTICAL = 2 | |||||
| GREEN = 1 | |||||
| BLUE = 2 | |||||
| def __init__(self, parent): | |||||
| QWidget.__init__(self, parent) | |||||
| self.n_channels = 0 | |||||
| self.bg_color = QColor("#111111") | |||||
| self.base_color = QColor("#5DE73D") | |||||
| self.base_colorT = QColor(15, 110, 15, 100) | |||||
| self.orientation = self.VERTICAL | |||||
| self.meter_gradient = QLinearGradient(0, 0, 1, 1) | |||||
| self.smooth_multiplier = 1 | |||||
| self.setOrientation(self.VERTICAL) | |||||
| self.setChannels(2) | |||||
| self.paint_timer = QTimer() | |||||
| self.paint_timer.setInterval(60) | |||||
| self.paint_timer.timeout.connect(self.update) | |||||
| self.paint_timer.start() | |||||
| def minimumSizeHint(self): | |||||
| return QSize(30, 30) | |||||
| def sizeHint(self): | |||||
| return QSize(self.width_, self.height_) | |||||
| def setChannels(self, channels): | |||||
| self.n_channels = channels | |||||
| self.channels_data = [] | |||||
| self.last_max_data = [] | |||||
| if (channels > 0): | |||||
| for i in range(channels): | |||||
| self.channels_data.append(0.0) | |||||
| self.last_max_data.append(0.0) #self.height_ | |||||
| def setColor(self, color): | |||||
| if (color == self.GREEN): | |||||
| self.base_color = QColor("#5DE73D") | |||||
| self.base_colorT = QColor(15, 110, 15, 100) | |||||
| elif (color == self.BLUE): | |||||
| self.base_color = QColor("#52EEF8") | |||||
| self.base_colorT = QColor(15, 15, 110, 100) | |||||
| else: | |||||
| return | |||||
| self.setOrientation(self.orientation) | |||||
| def setOrientation(self, orientation): | |||||
| self.orientation = orientation | |||||
| if (self.orientation == self.HORIZONTAL): | |||||
| self.meter_gradient.setColorAt(0.0, self.base_color) | |||||
| self.meter_gradient.setColorAt(0.2, self.base_color) | |||||
| self.meter_gradient.setColorAt(0.4, self.base_color) | |||||
| self.meter_gradient.setColorAt(0.6, self.base_color) | |||||
| self.meter_gradient.setColorAt(0.8, Qt.yellow) | |||||
| self.meter_gradient.setColorAt(1.0, Qt.red) | |||||
| elif (self.orientation == self.VERTICAL): | |||||
| self.meter_gradient.setColorAt(0.0, Qt.red) | |||||
| self.meter_gradient.setColorAt(0.2, Qt.yellow) | |||||
| self.meter_gradient.setColorAt(0.4, self.base_color) | |||||
| self.meter_gradient.setColorAt(0.6, self.base_color) | |||||
| self.meter_gradient.setColorAt(0.8, self.base_color) | |||||
| self.meter_gradient.setColorAt(1.0, self.base_color) | |||||
| self.checkSizes() | |||||
| def setRefreshRate(self, rate): | |||||
| self.paint_timer.stop() | |||||
| self.paint_timer.setInterval(rate) | |||||
| self.paint_timer.start() | |||||
| def setSmoothRelease(self, value): | |||||
| if (value < 0): | |||||
| value = 0 | |||||
| elif (value > 5): | |||||
| value = 5 | |||||
| self.smooth_multiplier = value | |||||
| def displayMeter(self, meter_n, level): | |||||
| if (meter_n > self.n_channels): | |||||
| qCritical("DigitalPeakMeter::displayMeter(%i, %f) - Invalid meter number", meter_n, level) | |||||
| return | |||||
| if (level < 0.0): | |||||
| level = -level | |||||
| if (level > 1.0): | |||||
| level = 1.0 | |||||
| self.channels_data[meter_n-1] = level | |||||
| def checkSizes(self): | |||||
| self.width_ = self.width() | |||||
| self.height_ = self.height() | |||||
| self.meter_size = 0 | |||||
| if (self.orientation == self.HORIZONTAL): | |||||
| self.meter_gradient.setFinalStop(self.width_, 0) | |||||
| if (self.n_channels > 0): | |||||
| self.meter_size = self.height_/self.n_channels | |||||
| elif (self.orientation == self.VERTICAL): | |||||
| self.meter_gradient.setFinalStop(0, self.height_) | |||||
| if (self.n_channels > 0): | |||||
| self.meter_size = self.width_/self.n_channels | |||||
| def paintEvent(self, event): | |||||
| painter = QPainter(self) | |||||
| painter.setPen(Qt.black) | |||||
| painter.setBrush(Qt.black) | |||||
| painter.drawRect(0, 0, self.width_, self.height_) | |||||
| meter_x = 0 | |||||
| for i in range(self.n_channels): | |||||
| level = self.channels_data[i] | |||||
| if (level == self.last_max_data[i]): | |||||
| continue | |||||
| if (self.orientation == self.HORIZONTAL): | |||||
| value = self.width_*level | |||||
| elif (self.orientation == self.VERTICAL): | |||||
| value = self.height_-(self.height_*level) | |||||
| else: | |||||
| value = 0 | |||||
| # Don't bounce the meter so much | |||||
| if (self.smooth_multiplier > 0): | |||||
| value = (self.last_max_data[i]*self.smooth_multiplier + value)/(self.smooth_multiplier+1) | |||||
| if (value < 0): | |||||
| value = 0 | |||||
| painter.setPen(self.bg_color) | |||||
| painter.setBrush(self.meter_gradient) | |||||
| if (self.orientation == self.HORIZONTAL): | |||||
| painter.drawRect(0, meter_x, value, self.meter_size) | |||||
| elif (self.orientation == self.VERTICAL): | |||||
| painter.drawRect(meter_x, value, self.meter_size, self.height_) | |||||
| meter_x += self.meter_size | |||||
| self.last_max_data[i] = value | |||||
| if (self.orientation == self.HORIZONTAL): | |||||
| lsmall = self.width_ | |||||
| lfull = self.height_-1 | |||||
| elif (self.orientation == self.VERTICAL): | |||||
| lsmall = self.height_ | |||||
| lfull = self.width_-1 | |||||
| else: | |||||
| return | |||||
| painter.setBrush(QColor(0, 0, 0, 0)) | |||||
| if (self.orientation == self.HORIZONTAL): | |||||
| # Base | |||||
| painter.setPen(self.base_colorT) | |||||
| painter.drawLine(lsmall/4, 1, lsmall/4, lfull) | |||||
| painter.drawLine(lsmall/2, 1, lsmall/2, lfull) | |||||
| # Yellow | |||||
| painter.setPen(QColor(110, 110, 15, 100)) | |||||
| painter.drawLine(lsmall/1.4, 1, lsmall/1.4, lfull) | |||||
| painter.drawLine(lsmall/1.2, 1, lsmall/1.2, lfull) | |||||
| # Orange | |||||
| painter.setPen(QColor(180, 110, 15, 100)) | |||||
| painter.drawLine(lsmall/1.1, 1, lsmall/1.1, lfull) | |||||
| # Red | |||||
| painter.setPen(QColor(110, 15, 15, 100)) | |||||
| painter.drawLine(lsmall/1.04, 1, lsmall/1.04, lfull) | |||||
| elif (self.orientation == self.VERTICAL): | |||||
| # Base | |||||
| painter.setPen(self.base_colorT) | |||||
| painter.drawLine(1, lsmall-(lsmall/4), lfull, lsmall-(lsmall/4)) | |||||
| painter.drawLine(1, lsmall-(lsmall/2), lfull, lsmall-(lsmall/2)) | |||||
| # Yellow | |||||
| painter.setPen(QColor(110, 110, 15, 100)) | |||||
| painter.drawLine(1, lsmall-(lsmall/1.4), lfull, lsmall-(lsmall/1.4)) | |||||
| painter.drawLine(1, lsmall-(lsmall/1.2), lfull, lsmall-(lsmall/1.2)) | |||||
| # Orange | |||||
| painter.setPen(QColor(180, 110, 15, 100)) | |||||
| painter.drawLine(1, lsmall-(lsmall/1.1), lfull, lsmall-(lsmall/1.1)) | |||||
| # Red | |||||
| painter.setPen(QColor(110, 15, 15, 100)) | |||||
| painter.drawLine(1, lsmall-(lsmall/1.04), lfull, lsmall-(lsmall/1.04)) | |||||
| def resizeEvent(self, event): | |||||
| QTimer.singleShot(0, self.checkSizes) | |||||
| return QWidget.resizeEvent(self, event) | |||||
| @@ -53,3 +53,26 @@ def get_jack_status_error_string(c_status): | |||||
| error_string = error_string.strip().rsplit(";", 1)[0]+"." | error_string = error_string.strip().rsplit(";", 1)[0]+"." | ||||
| return error_string | return error_string | ||||
| # C char** -> Python list conversion | |||||
| def c_char_p_p_to_list(c_char_p_p): | |||||
| i = 0 | |||||
| final_list = [] | |||||
| if (not c_char_p_p): | |||||
| return final_list | |||||
| while (True): | |||||
| new_char_p = c_char_p_p[i] | |||||
| if (new_char_p): | |||||
| final_list.append(str(new_char_p, encoding="ascii")) | |||||
| else: | |||||
| break | |||||
| i += 1 | |||||
| jacklib.free(c_char_p_p) | |||||
| return final_list | |||||
| # C cast void* -> jack_default_audio_sample_t* | |||||
| def translate_audio_port_buffer(void_p): | |||||
| return jacklib.cast(void_p, jacklib.POINTER(jacklib.jack_default_audio_sample_t)) | |||||
| @@ -0,0 +1,136 @@ | |||||
| #!/usr/bin/env python | |||||
| # -*- coding: utf-8 -*- | |||||
| # Simple JACK Audio Meter | |||||
| # Copyright (C) 2012 Filipe Coelho <falktx@gmail.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 | |||||
| # Imports (Global) | |||||
| from PyQt4.QtCore import Qt | |||||
| from PyQt4.QtGui import QApplication, QWidget | |||||
| # Imports (Custom Stuff) | |||||
| from digitalpeakmeter import DigitalPeakMeter | |||||
| from jacklib_helpers import * | |||||
| from shared import * | |||||
| global x_port1, x_port2, need_reconnect | |||||
| x_port1 = 0.0 | |||||
| x_port2 = 0.0 | |||||
| need_reconnect = False | |||||
| client = None | |||||
| def process_callback(nframes, arg): | |||||
| global x_port1, x_port2 | |||||
| p_out1 = translate_audio_port_buffer(jacklib.port_get_buffer(port_1, nframes)) | |||||
| p_out2 = translate_audio_port_buffer(jacklib.port_get_buffer(port_2, nframes)) | |||||
| for i in range(nframes): | |||||
| if (abs(p_out1[i]) > x_port1): | |||||
| x_port1 = abs(p_out1[i]) | |||||
| if (abs(p_out2[i]) > x_port2): | |||||
| x_port2 = abs(p_out2[i]) | |||||
| return 0 | |||||
| def port_callback(port_a, port_b, connect_yesno, arg): | |||||
| global need_reconnect | |||||
| need_reconnect = True | |||||
| return 0 | |||||
| def reconnect_inputs(): | |||||
| play_port_1 = jacklib.port_by_name(client, "system:playback_1") | |||||
| play_port_2 = jacklib.port_by_name(client, "system:playback_2") | |||||
| list_port_1 = c_char_p_p_to_list(jacklib.port_get_all_connections(client, play_port_1)) | |||||
| list_port_2 = c_char_p_p_to_list(jacklib.port_get_all_connections(client, play_port_2)) | |||||
| client_name = str(jacklib.get_client_name(client), encoding="ascii") | |||||
| for port in list_port_1: | |||||
| this_port = jacklib.port_by_name(client, port) | |||||
| if not jacklib.port_is_mine(client, this_port): | |||||
| jacklib.connect(client, port, "%s:in1" % (client_name)) | |||||
| for port in list_port_2: | |||||
| this_port = jacklib.port_by_name(client, port) | |||||
| if not jacklib.port_is_mine(client, this_port): | |||||
| jacklib.connect(client, port, "%s:in2" % (client_name)) | |||||
| global need_reconnect | |||||
| need_reconnect = False | |||||
| class MeterW(DigitalPeakMeter): | |||||
| def __init__(self, parent): | |||||
| DigitalPeakMeter.__init__(self, parent) | |||||
| client_name = str(jacklib.get_client_name(client), encoding="ascii") | |||||
| self.setWindowFlags(self.windowFlags() | Qt.WindowStaysOnTopHint); | |||||
| self.setWindowTitle(client_name) | |||||
| self.setChannels(2) | |||||
| self.setOrientation(self.VERTICAL) | |||||
| self.setSmoothRelease(1) | |||||
| self.displayMeter(1, x_port1) | |||||
| self.displayMeter(2, x_port2) | |||||
| self.setRefreshRate(30) | |||||
| self.peakTimer = self.startTimer(60) | |||||
| def timerEvent(self, event): | |||||
| if (event.timerId() == self.peakTimer): | |||||
| global x_port1, x_port2, need_reconnect | |||||
| self.displayMeter(1, x_port1) | |||||
| self.displayMeter(2, x_port2) | |||||
| x_port1 = 0.0 | |||||
| x_port2 = 0.0 | |||||
| if (need_reconnect): | |||||
| reconnect_inputs() | |||||
| QWidget.timerEvent(self, event) | |||||
| #--------------- main ------------------ | |||||
| if __name__ == '__main__': | |||||
| # App initialization | |||||
| app = QApplication(sys.argv) | |||||
| # JACK initialization | |||||
| client = jacklib.client_open("M", jacklib.JackNullOption, None) | |||||
| port_1 = jacklib.port_register(client, "in1", jacklib.JACK_DEFAULT_AUDIO_TYPE, jacklib.JackPortIsInput, 0) | |||||
| port_2 = jacklib.port_register(client, "in2", jacklib.JACK_DEFAULT_AUDIO_TYPE, jacklib.JackPortIsInput, 0) | |||||
| jacklib.set_process_callback(client, process_callback, None) | |||||
| jacklib.set_port_connect_callback(client, port_callback, None) | |||||
| jacklib.activate(client) | |||||
| reconnect_inputs() | |||||
| # Show GUI | |||||
| gui = MeterW(None) | |||||
| gui.resize(70, 600) | |||||
| gui.show() | |||||
| set_up_signals(gui) | |||||
| # App-Loop | |||||
| ret = app.exec_() | |||||
| jacklib.deactivate(client) | |||||
| jacklib.client_close(client) | |||||
| sys.exit(ret) | |||||
| @@ -17,7 +17,7 @@ | |||||
| # For a full copy of the GNU General Public License see the COPYING file | # For a full copy of the GNU General Public License see the COPYING file | ||||
| # Imports (Global) | # Imports (Global) | ||||
| from PyQt4.QtCore import pyqtSlot, Qt, QFile, QIODevice, QTextStream, QThread, SIGNAL, SLOT | |||||
| from PyQt4.QtCore import pyqtSlot, Qt, QFile, QIODevice, QTextStream, QThread | |||||
| from PyQt4.QtGui import QDialog, QPalette, QSyntaxHighlighter | from PyQt4.QtGui import QDialog, QPalette, QSyntaxHighlighter | ||||
| # Imports (Custom Stuff) | # Imports (Custom Stuff) | ||||
| @@ -352,7 +352,6 @@ class LogsW(QDialog, ui_logs.Ui_LogsW): | |||||
| if __name__ == '__main__': | if __name__ == '__main__': | ||||
| # Additional imports | # Additional imports | ||||
| import sys | |||||
| from PyQt4.QtGui import QApplication | from PyQt4.QtGui import QApplication | ||||
| # App initialization | # App initialization | ||||
| @@ -362,5 +361,7 @@ if __name__ == '__main__': | |||||
| gui = LogsW(None, Qt.WindowFlags()) | gui = LogsW(None, Qt.WindowFlags()) | ||||
| gui.show() | gui.show() | ||||
| set_up_signals(gui) | |||||
| # App-Loop | # App-Loop | ||||
| sys.exit(app.exec_()) | sys.exit(app.exec_()) | ||||
| @@ -17,7 +17,7 @@ | |||||
| # For a full copy of the GNU General Public License see the COPYING file | # For a full copy of the GNU General Public License see the COPYING file | ||||
| # Imports (Global) | # Imports (Global) | ||||
| from PyQt4.QtCore import pyqtSlot, Qt, QProcess, QTime, QTimer, SIGNAL, SLOT | |||||
| from PyQt4.QtCore import pyqtSlot, Qt, QProcess, QTime, QTimer | |||||
| from PyQt4.QtGui import QDialog | from PyQt4.QtGui import QDialog | ||||
| from time import sleep | from time import sleep | ||||
| @@ -270,7 +270,6 @@ class RenderW(QDialog, ui_render.Ui_RenderW): | |||||
| if __name__ == '__main__': | if __name__ == '__main__': | ||||
| # Additional imports | # Additional imports | ||||
| import sys | |||||
| from PyQt4.QtGui import QApplication | from PyQt4.QtGui import QApplication | ||||
| # App initialization | # App initialization | ||||
| @@ -17,10 +17,24 @@ | |||||
| # For a full copy of the GNU General Public License see the COPYING file | # For a full copy of the GNU General Public License see the COPYING file | ||||
| # Imports (Global) | # Imports (Global) | ||||
| import os | |||||
| from PyQt4.QtCore import qDebug, qWarning | |||||
| import os, sys | |||||
| from PyQt4.QtCore import pyqtSlot, qWarning, SIGNAL, SLOT | |||||
| from PyQt4.QtGui import QIcon, QMessageBox, QFileDialog | from PyQt4.QtGui import QIcon, QMessageBox, QFileDialog | ||||
| # Set Platform | |||||
| if ("linux" in sys.platform): | |||||
| LINUX = True | |||||
| WINDOWS = False | |||||
| elif ("win" in sys.platform): | |||||
| LINUX = False | |||||
| WINDOWS = True | |||||
| else: | |||||
| LINUX = False | |||||
| WINDOWS = False | |||||
| if (WINDOWS == False): | |||||
| from signal import signal, SIGINT, SIGTERM, SIGUSR1, SIGUSR2 | |||||
| # Small integrity tests | # Small integrity tests | ||||
| HOME = os.getenv("HOME") | HOME = os.getenv("HOME") | ||||
| if (HOME == None): | if (HOME == None): | ||||
| @@ -48,3 +62,43 @@ def getAndSetPath(self, currentPath, lineEdit): | |||||
| if (newPath): | if (newPath): | ||||
| lineEdit.setText(newPath) | lineEdit.setText(newPath) | ||||
| return newPath | return newPath | ||||
| # Custom MessageBox | |||||
| def CustomMessageBox(self, icon, title, text, extra_text="", buttons=QMessageBox.Yes|QMessageBox.No, defButton=QMessageBox.No): | |||||
| msgBox = QMessageBox(self) | |||||
| msgBox.setIcon(icon) | |||||
| msgBox.setWindowTitle(title) | |||||
| msgBox.setText(text) | |||||
| msgBox.setInformativeText(extra_text) | |||||
| msgBox.setStandardButtons(buttons) | |||||
| msgBox.setDefaultButton(defButton) | |||||
| return msgBox.exec_() | |||||
| # signal handler for unix systems | |||||
| def set_up_signals(_gui): | |||||
| if (WINDOWS == False): | |||||
| from signal import signal, SIGINT, SIGTERM, SIGUSR1, SIGUSR2 | |||||
| global x_gui | |||||
| x_gui = _gui | |||||
| signal(SIGINT, signal_handler) | |||||
| signal(SIGTERM, signal_handler) | |||||
| signal(SIGUSR1, signal_handler) | |||||
| signal(SIGUSR2, signal_handler) | |||||
| x_gui.connect(x_gui, SIGNAL("SIGUSR2()"), lambda gui=x_gui: showWindow(gui)) | |||||
| x_gui.connect(x_gui, SIGNAL("SIGTERM()"), SLOT("close()")) | |||||
| def signal_handler(sig=0, frame=0): | |||||
| global x_gui | |||||
| if (sig in (SIGINT, SIGTERM)): | |||||
| x_gui.emit(SIGNAL("SIGTERM()")) | |||||
| elif (sig == SIGUSR1): | |||||
| x_gui.emit(SIGNAL("SIGUSR1()")) | |||||
| elif (sig == SIGUSR2): | |||||
| x_gui.emit(SIGNAL("SIGUSR2()")) | |||||
| def showWindow(self): | |||||
| if (self.isMaximized()): | |||||
| self.showMaximized() | |||||
| else: | |||||
| self.showNormal() | |||||