Browse Source

Added digitalpeakmeter; added jackmeter

tags/v0.9.0
falkTX 13 years ago
parent
commit
cbf5da2078
6 changed files with 450 additions and 6 deletions
  1. +231
    -0
      src/digitalpeakmeter.py
  2. +23
    -0
      src/jacklib_helpers.py
  3. +136
    -0
      src/jackmeter.py
  4. +3
    -2
      src/logs.py
  5. +1
    -2
      src/render.py
  6. +56
    -2
      src/shared.py

+ 231
- 0
src/digitalpeakmeter.py View File

@@ -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)

+ 23
- 0
src/jacklib_helpers.py View File

@@ -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))

+ 136
- 0
src/jackmeter.py View File

@@ -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)

+ 3
- 2
src/logs.py View File

@@ -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_())

+ 1
- 2
src/render.py View File

@@ -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


+ 56
- 2
src/shared.py View File

@@ -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()

Loading…
Cancel
Save