#!/usr/bin/env python3 # -*- coding: utf-8 -*- # Common/Shared code related to JACK # Copyright (C) 2012 Filipe Coelho # # 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 pyqtSlot, QTimer from PyQt4.QtGui import QCursor, QFontMetrics, QMenu # Imports (Custom Stuff) import jacksettings, logs, render from shared import * from jacklib_helpers import * # Have JACK2 ? try: JACK2 = True version_str = str(jacklib.get_version_string(), encoding="ascii") print("Using JACK2, version %s" % (version_str)) except: JACK2 = False # Can Render ? for iPATH in PATH: if (os.path.exists(os.path.join(iPATH, "jack_capture"))): canRender = True break else: canRender = False # Variables TRANSPORT_VIEW_HMS = 0 TRANSPORT_VIEW_BBT = 1 TRANSPORT_VIEW_FRAMES = 2 buffer_sizes = (16, 32, 64, 128, 256, 512, 1024, 2048, 4096, 8192) sample_rates = (22050, 32000, 44100, 48000, 88200, 96000, 192000) # DBus object class DBusObject(object): __slots__ = [ 'loop', 'bus', 'a2j', 'jack', 'ladish_control', 'ladish_studio', 'ladish_room', 'ladish_graph', 'ladish_app_iface', 'ladish_app_daemon', 'patchbay' ] DBus = DBusObject() # Jack object class JackObject(object): __slots__ = [ 'client' ] jack = JackObject() # Init objects DBus.loop = None DBus.bus = None DBus.a2j = None DBus.jack = None DBus.ladish_control = None DBus.ladish_studio = None DBus.ladish_room = None DBus.ladish_graph = None DBus.ladish_app_iface = None DBus.ladish_app_daemon = None DBus.patchbay = None jack.client = None # ------------------------------------------------------------- # Property change calls def jack_buffer_size(self, buffer_size): if (self.m_buffer_size != buffer_size): if (jack.client): failed = bool(jacklib.set_buffer_size(jack.client, buffer_size) != 0) else: failed = bool(jacksettings.setBufferSize(buffer_size)) if (failed): print("Failed to change buffer-size as %i, reset to %i" % (buffer_size, self.m_buffer_size)) setBufferSize(self, self.m_buffer_size, True) def jack_sample_rate(self, sample_rate): if (jack.client): setSampleRate(self, sample_rate, True) else: if (jacksettings.setSampleRate(sample_rate)): setSampleRate(self, sample_rate) @pyqtSlot(str) def slot_jackBufferSize_ComboBox(self, text): if (not text or text.isdigit() == False): return jack_buffer_size(self, int(text)) @pyqtSlot(int) def slot_jackBufferSize_Menu(self, buffer_size): jack_buffer_size(self, buffer_size) @pyqtSlot(str) def slot_jackSampleRate_ComboBox(self, text): if (not text or text.isdigit() == False): return jack_sample_rate(self, int(text)) # ------------------------------------------------------------- # Transport calls def setTransportView(self, view): if (view == TRANSPORT_VIEW_HMS): self.m_selected_transport_view = TRANSPORT_VIEW_HMS self.label_time.setMinimumWidth(QFontMetrics(self.label_time.font()).width("00:00:00")+3) elif (view == TRANSPORT_VIEW_BBT): self.m_selected_transport_view = TRANSPORT_VIEW_BBT self.label_time.setMinimumWidth(QFontMetrics(self.label_time.font()).width("000|00|0000")+3) elif (view == TRANSPORT_VIEW_FRAMES): self.m_selected_transport_view = TRANSPORT_VIEW_FRAMES self.label_time.setMinimumWidth(QFontMetrics(self.label_time.font()).width("000'000'000")+3) else: self.m_selected_transport_view = None @pyqtSlot(bool) def slot_transportPlayPause(self, play): if (not jack.client): return if (play): jacklib.transport_start(jack.client) else: jacklib.transport_stop(jack.client) refreshTransport(self) @pyqtSlot() def slot_transportStop(self): if (not jack.client): return jacklib.transport_stop(jack.client) jacklib.transport_locate(jack.client, 0) refreshTransport(self) @pyqtSlot() def slot_transportBackwards(self): if (not jack.client): return new_frame = jacklib.get_current_transport_frame(jack.client)-100000 if (new_frame < 0): new_frame = 0 jacklib.transport_locate(jack.client, new_frame) @pyqtSlot() def slot_transportForwards(self): if (not jack.client): return new_frame = jacklib.get_current_transport_frame(jack.client)+100000 jacklib.transport_locate(jack.client, new_frame) @pyqtSlot() def slot_transportViewMenu(self): menu = QMenu(self) act_t_hms = menu.addAction("Hours:Minutes:Seconds") act_t_bbt = menu.addAction("Beat:Bar:Tick") act_t_fr = menu.addAction("Frames") act_t_hms.setCheckable(True) act_t_bbt.setCheckable(True) act_t_fr.setCheckable(True) if (self.m_selected_transport_view == TRANSPORT_VIEW_HMS): act_t_hms.setChecked(True) elif (self.m_selected_transport_view == TRANSPORT_VIEW_BBT): act_t_bbt.setChecked(True) elif (self.m_selected_transport_view == TRANSPORT_VIEW_FRAMES): act_t_fr.setChecked(True) act_selected = menu.exec_(QCursor().pos()) if (act_selected == act_t_hms): setTransportView(self, TRANSPORT_VIEW_HMS) elif (act_selected == act_t_bbt): setTransportView(self, TRANSPORT_VIEW_BBT) elif (act_selected == act_t_fr): setTransportView(self, TRANSPORT_VIEW_FRAMES) # ------------------------------------------------------------- # Refresh GUI stuff def refreshDSPLoad(self): if (not jack.client): return setDSPLoad(self, int(jacklib.cpu_load(jack.client))) def refreshTransport(self): if (not jack.client): return pos = jacklib.jack_position_t() pos.valid = 0 state = jacklib.transport_query(jack.client, jacklib.pointer(pos)) if (self.m_selected_transport_view == TRANSPORT_VIEW_HMS): frame = pos.frame time = frame / self.m_sample_rate secs = time % 60 mins = (time / 60) % 60 hrs = (time / 3600) % 60 self.label_time.setText("%02i:%02i:%02i" % (hrs, mins, secs)) elif (self.m_selected_transport_view == TRANSPORT_VIEW_BBT): if (pos.valid & jacklib.JackPositionBBT): bar = pos.bar beat = pos.beat tick = pos.tick if (bar == 0): beat = 0 tick = 0 self.label_time.setText("%03i|%02i|%04i" % (bar, beat, tick)) else: self.label_time.setText("000|00|0000") elif (self.m_selected_transport_view == TRANSPORT_VIEW_FRAMES): frame = pos.frame frame1 = pos.frame % 1000 frame2 = (pos.frame / 1000) % 1000 frame3 = (pos.frame / 1000000) % 1000 self.label_time.setText("%03i'%03i'%03i" % (frame3, frame2, frame1)) if (pos.valid & jacklib.JackPositionBBT): if (self.m_last_bpm != pos.beats_per_minute): self.sb_bpm.setValue(pos.beats_per_minute) self.sb_bpm.setStyleSheet("") else: pos.beats_per_minute = -1 if (self.m_last_bpm != pos.beats_per_minute): self.sb_bpm.setStyleSheet("QDoubleSpinBox { color: palette(mid); }") self.m_last_bpm = pos.beats_per_minute if (state != self.m_last_transport_state): if (state == jacklib.JackTransportStopped): icon = getIcon("media-playback-start") self.act_transport_play.setChecked(False) self.act_transport_play.setIcon(icon) self.act_transport_play.setText(self.tr("&Play")) self.b_transport_play.setChecked(False) self.b_transport_play.setIcon(icon) else: icon = getIcon("media-playback-pause") self.act_transport_play.setChecked(True) self.act_transport_play.setIcon(icon) self.act_transport_play.setText(self.tr("&Pause")) self.b_transport_play.setChecked(True) self.b_transport_play.setIcon(icon) self.m_last_transport_state = state # ------------------------------------------------------------- # Set GUI stuff def setBufferSize(self, buffer_size, forced=False): if (self.m_buffer_size == buffer_size and not forced): return self.m_buffer_size = buffer_size if (buffer_size): if (buffer_size == 16): self.cb_buffer_size.setCurrentIndex(0) elif (buffer_size == 32): self.cb_buffer_size.setCurrentIndex(1) elif (buffer_size == 64): self.cb_buffer_size.setCurrentIndex(2) elif (buffer_size == 128): self.cb_buffer_size.setCurrentIndex(3) elif (buffer_size == 256): self.cb_buffer_size.setCurrentIndex(4) elif (buffer_size == 512): self.cb_buffer_size.setCurrentIndex(5) elif (buffer_size == 1024): self.cb_buffer_size.setCurrentIndex(6) elif (buffer_size == 2048): self.cb_buffer_size.setCurrentIndex(7) elif (buffer_size == 4096): self.cb_buffer_size.setCurrentIndex(8) elif (buffer_size == 8192): self.cb_buffer_size.setCurrentIndex(9) else: QMessageBox.warning(self, self.tr("Warning"), self.tr("Invalid JACK buffer-size requested")) #if ("act_jack_bf_list" in dir(self)): if (buffer_size): for act_bf in self.act_jack_bf_list: act_bf.setEnabled(True) if (act_bf.text().replace("&","") == str(buffer_size)): if (act_bf.isChecked() == False): act_bf.setChecked(True) else: if (act_bf.isChecked()): act_bf.setChecked(False) #else: #for i in range(len(self.act_jack_bf_list)): #self.act_jack_bf_list[i].setEnabled(False) #if (self.act_jack_bf_list[i].isChecked()): #self.act_jack_bf_list[i].setChecked(False) def setSampleRate(self, sample_rate, future=False): if (self.m_sample_rate == sample_rate): return if (future): pass if (self.sender() == self.cb_sample_rate): # Changed using GUI ask = QMessageBox.question(self, self.tr("Change Sample Rate"), self.tr("It's not possible to change Sample Rate while JACK is running.\n" "Do you want to change as soon as JACK stops?"), QMessageBox.Ok|QMessageBox.Cancel) if (ask == QMessageBox.Ok): self.m_next_sample_rate = sample_rate else: self.m_next_sample_rate = 0 # not future else: self.m_sample_rate = sample_rate self.m_next_sample_rate = 0 for i in range(len(sample_rates)): sample_rate = sample_rates[i] sample_rate_str = str(sample_rate) self.cb_sample_rate.setItemText(i, sample_rate_str) if (self.m_sample_rate == sample_rate): self.cb_sample_rate.setCurrentIndex(i) def setRealTime(self, realtime): self.label_realtime.setText(" RT " if realtime else " RT ") self.label_realtime.setEnabled(realtime) def setDSPLoad(self, dsp_load): self.pb_dsp_load.setValue(dsp_load) def setXruns(self, xruns): self.b_xruns.setText("%s Xrun%s" % (str(xruns) if (xruns >= 0) else "--", "" if (xruns == 1) else "s")) # ------------------------------------------------------------- # External Dialogs @pyqtSlot() def slot_showJackSettings(self): jacksettings.JackSettingsW(self).exec_() @pyqtSlot() def slot_showLogs(self): logs.LogsW(self).show() @pyqtSlot() def slot_showRender(self): render.RenderW(self).exec_() # ------------------------------------------------------------- # Shared Connections def setJackConnections(self, modes): pass if ("jack" in modes): self.connect(self.act_jack_clear_xruns, SIGNAL("triggered()"), SLOT("slot_JackClearXruns()")) self.connect(self.act_jack_render, SIGNAL("triggered()"), lambda: slot_showRender(self)) self.connect(self.act_jack_configure, SIGNAL("triggered()"), lambda: slot_showJackSettings(self)) self.connect(self.b_jack_clear_xruns, SIGNAL("clicked()"), SLOT("slot_JackClearXruns()")) self.connect(self.b_jack_configure, SIGNAL("clicked()"), lambda: slot_showJackSettings(self)) self.connect(self.b_jack_render, SIGNAL("clicked()"), lambda: slot_showRender(self)) self.connect(self.cb_buffer_size, SIGNAL("currentIndexChanged(QString)"), lambda: slot_jackBufferSize_ComboBox(self, self.cb_buffer_size.currentText())) self.connect(self.cb_sample_rate, SIGNAL("currentIndexChanged(QString)"), lambda: slot_jackSampleRate_ComboBox(self, self.cb_sample_rate.currentText())) self.connect(self.b_xruns, SIGNAL("clicked()"), SLOT("slot_JackClearXruns()")) if ("buffer-size" in modes): self.connect(self.act_jack_bf_16, SIGNAL("triggered(bool)"), lambda: slot_jackBufferSize_Menu(self, 16)) self.connect(self.act_jack_bf_32, SIGNAL("triggered(bool)"), lambda: slot_jackBufferSize_Menu(self, 32)) self.connect(self.act_jack_bf_64, SIGNAL("triggered(bool)"), lambda: slot_jackBufferSize_Menu(self, 64)) self.connect(self.act_jack_bf_128, SIGNAL("triggered(bool)"), lambda: slot_jackBufferSize_Menu(self, 128)) self.connect(self.act_jack_bf_256, SIGNAL("triggered(bool)"), lambda: slot_jackBufferSize_Menu(self, 256)) self.connect(self.act_jack_bf_512, SIGNAL("triggered(bool)"), lambda: slot_jackBufferSize_Menu(self, 512)) self.connect(self.act_jack_bf_1024, SIGNAL("triggered(bool)"), lambda: slot_jackBufferSize_Menu(self, 1024)) self.connect(self.act_jack_bf_2048, SIGNAL("triggered(bool)"), lambda: slot_jackBufferSize_Menu(self, 2048)) self.connect(self.act_jack_bf_4096, SIGNAL("triggered(bool)"), lambda: slot_jackBufferSize_Menu(self, 4096)) self.connect(self.act_jack_bf_8192, SIGNAL("triggered(bool)"), lambda: slot_jackBufferSize_Menu(self, 8192)) if ("transport" in modes): self.connect(self.act_transport_play, SIGNAL("triggered(bool)"), lambda: slot_transportPlayPause(self, self.act_transport_play.isChecked())) self.connect(self.act_transport_stop, SIGNAL("triggered()"), lambda: slot_transportStop(self)) self.connect(self.act_transport_backwards, SIGNAL("triggered()"), lambda: slot_transportBackwards(self)) self.connect(self.act_transport_forwards, SIGNAL("triggered()"), lambda: slot_transportForwards(self)) self.connect(self.b_transport_play, SIGNAL("clicked(bool)"), lambda: slot_transportPlayPause(self, self.b_transport_play.isChecked())) self.connect(self.b_transport_stop, SIGNAL("clicked()"), lambda: slot_transportStop(self)) self.connect(self.b_transport_backwards, SIGNAL("clicked()"), lambda: slot_transportBackwards(self)) self.connect(self.b_transport_forwards, SIGNAL("clicked()"), lambda: slot_transportForwards(self)) self.connect(self.label_time, SIGNAL("customContextMenuRequested(QPoint)"), lambda: slot_transportViewMenu(self)) if ("misc" in modes): if (LINUX): self.connect(self.act_show_logs, SIGNAL("triggered()"), lambda: slot_showLogs(self)) else: self.act_show_logs.setEnabled(False)