|
- #!/usr/bin/env python3
- # -*- coding: utf-8 -*-
-
- # JACK-Capture frontend, with freewheel and transport support
- # Copyright (C) 2010-2018 Filipe Coelho <falktx@falktx.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 time import sleep
-
- if True:
- from PyQt5.QtCore import pyqtSlot, QProcess, QTime, QTimer, QSettings
- from PyQt5.QtWidgets import QDialog
- else:
- from PyQt4.QtCore import pyqtSlot, QProcess, QTime, QTimer, QSettings
- from PyQt4.QtGui import QDialog
-
- # ------------------------------------------------------------------------------------------------------------
- # Imports (Custom Stuff)
-
- import ui_render
- from shared import *
- from jacklib_helpers import *
-
- # ------------------------------------------------------------------------------------------------------------
- # Global variables
-
- global gJackCapturePath, gJackClient
- gJackCapturePath = ""
- gJackClient = None
-
- # ------------------------------------------------------------------------------------------------------------
- # Find 'jack_capture'
-
- # Check for cxfreeze
- if sys.path[0].rsplit(os.sep, 1)[-1] in ("catia", "claudia", "cadence-render", "render"):
- name = sys.path[0].rsplit(os.sep, 1)[-1]
- cwd = sys.path[0].rsplit(name, 1)[0]
- if os.path.exists(os.path.join(cwd, "jack_capture")):
- gJackCapturePath = os.path.join(cwd, "jack_capture")
- del cwd, name
-
- # Check in PATH
- if not gJackCapturePath:
- for iPATH in PATH:
- if os.path.exists(os.path.join(iPATH, "jack_capture")):
- gJackCapturePath = os.path.join(iPATH, "jack_capture")
- break
-
- def canRender():
- global gJackCapturePath
- return bool(gJackCapturePath)
-
- # ------------------------------------------------------------------------------------------------------------
- # Render Window
-
- class RenderW(QDialog):
- def __init__(self, parent):
- QDialog.__init__(self, parent)
- self.ui = ui_render.Ui_RenderW()
- self.ui.setupUi(self)
-
- self.fFreewheel = False
- self.fLastTime = -1
- self.fMaxTime = 180
-
- self.fTimer = QTimer(self)
- self.fProcess = QProcess(self)
-
- # -------------------------------------------------------------
- # Get JACK client and base information
-
- global gJackClient
-
- if gJackClient:
- self.fJackClient = gJackClient
- else:
- self.fJackClient = jacklib.client_open("Render-Dialog", jacklib.JackNoStartServer, None)
-
- self.fBufferSize = int(jacklib.get_buffer_size(self.fJackClient))
- self.fSampleRate = int(jacklib.get_sample_rate(self.fJackClient))
-
- for i in range(self.ui.cb_buffer_size.count()):
- if int(self.ui.cb_buffer_size.itemText(i)) == self.fBufferSize:
- self.ui.cb_buffer_size.setCurrentIndex(i)
- break
- else:
- self.ui.cb_buffer_size.addItem(str(self.fBufferSize))
- self.ui.cb_buffer_size.setCurrentIndex(self.ui.cb_buffer_size.count() - 1)
-
- # -------------------------------------------------------------
- # Set-up GUI stuff
-
- # Get List of formats
- self.fProcess.start(gJackCapturePath, ["-pf"])
- self.fProcess.waitForFinished()
-
- formats = str(self.fProcess.readAllStandardOutput(), encoding="utf-8").split(" ")
- formatsList = []
-
- for i in range(len(formats) - 1):
- iFormat = formats[i].strip()
- if iFormat:
- formatsList.append(iFormat)
-
- formatsList.sort()
-
- # Put all formats in combo-box, select 'wav' option
- for i in range(len(formatsList)):
- self.ui.cb_format.addItem(formatsList[i])
- if formatsList[i] == "wav":
- self.ui.cb_format.setCurrentIndex(i)
-
- self.ui.cb_depth.setCurrentIndex(4) #Float
- self.ui.rb_stereo.setChecked(True)
-
- self.ui.te_end.setTime(QTime(0, 3, 0))
- self.ui.progressBar.setFormat("")
- self.ui.progressBar.setMinimum(0)
- self.ui.progressBar.setMaximum(1)
- self.ui.progressBar.setValue(0)
-
- self.ui.b_render.setIcon(getIcon("media-record"))
- self.ui.b_stop.setIcon(getIcon("media-playback-stop"))
- self.ui.b_close.setIcon(getIcon("window-close"))
- self.ui.b_open.setIcon(getIcon("document-open"))
- self.ui.b_stop.setVisible(False)
- self.ui.le_folder.setText(HOME)
-
- # -------------------------------------------------------------
- # Set-up connections
-
- self.ui.b_render.clicked.connect(self.slot_renderStart)
- self.ui.b_stop.clicked.connect(self.slot_renderStop)
- self.ui.b_open.clicked.connect(self.slot_getAndSetPath)
- self.ui.b_now_start.clicked.connect(self.slot_setStartNow)
- self.ui.b_now_end.clicked.connect(self.slot_setEndNow)
- self.ui.te_start.timeChanged.connect(self.slot_updateStartTime)
- self.ui.te_end.timeChanged.connect(self.slot_updateEndTime)
- self.ui.group_time.clicked.connect(self.slot_transportChecked)
- self.fTimer.timeout.connect(self.slot_updateProgressbar)
-
- # -------------------------------------------------------------
-
- self.loadSettings()
-
- @pyqtSlot()
- def slot_renderStart(self):
- if not os.path.exists(self.ui.le_folder.text()):
- QMessageBox.warning(self, self.tr("Warning"), self.tr("The selected directory does not exist. Please choose a valid one."))
- return
-
- timeStart = self.ui.te_start.time()
- timeEnd = self.ui.te_end.time()
- minTime = (timeStart.hour() * 3600) + (timeStart.minute() * 60) + (timeStart.second())
- maxTime = (timeEnd.hour() * 3600) + (timeEnd.minute() * 60) + (timeEnd.second())
-
- newBufferSize = int(self.ui.cb_buffer_size.currentText())
- useTransport = self.ui.group_time.isChecked()
-
- self.fFreewheel = bool(self.ui.cb_render_mode.currentIndex() == 1)
- self.fLastTime = -1
- self.fMaxTime = maxTime
-
- if self.fFreewheel:
- self.fTimer.setInterval(100)
- else:
- self.fTimer.setInterval(500)
-
- self.ui.group_render.setEnabled(False)
- self.ui.group_time.setEnabled(False)
- self.ui.group_encoding.setEnabled(False)
- self.ui.b_render.setVisible(False)
- self.ui.b_stop.setVisible(True)
- self.ui.b_close.setEnabled(False)
-
- if useTransport:
- self.ui.progressBar.setFormat("%p%")
- self.ui.progressBar.setMinimum(minTime)
- self.ui.progressBar.setMaximum(maxTime)
- self.ui.progressBar.setValue(minTime)
- else:
- self.ui.progressBar.setFormat("")
- self.ui.progressBar.setMinimum(0)
- self.ui.progressBar.setMaximum(0)
- self.ui.progressBar.setValue(0)
-
- self.ui.progressBar.update()
-
- arguments = []
-
- # Filename prefix
- arguments.append("-fp")
- arguments.append(self.ui.le_prefix.text())
-
- # Format
- arguments.append("-f")
- arguments.append(self.ui.cb_format.currentText())
-
- # Bit depth
- arguments.append("-b")
- arguments.append(self.ui.cb_depth.currentText())
-
- # Channels
- arguments.append("-c")
- if self.ui.rb_mono.isChecked():
- arguments.append("1")
- elif self.ui.rb_stereo.isChecked():
- arguments.append("2")
- else:
- arguments.append(str(self.ui.sb_channels.value()))
-
- # Controlled only by freewheel
- if self.fFreewheel:
- arguments.append("-jf")
-
- # Controlled by transport
- elif useTransport:
- arguments.append("-jt")
-
- # Silent mode
- arguments.append("-dc")
- arguments.append("-s")
-
- # Change current directory
- os.chdir(self.ui.le_folder.text())
-
- if newBufferSize != int(jacklib.get_buffer_size(self.fJackClient)):
- print("NOTICE: buffer size changed before render")
- jacklib.set_buffer_size(self.fJackClient, newBufferSize)
-
- if useTransport:
- if jacklib.transport_query(self.fJackClient, None) > jacklib.JackTransportStopped: # rolling or starting
- jacklib.transport_stop(self.fJackClient)
-
- jacklib.transport_locate(self.fJackClient, minTime * self.fSampleRate)
-
- self.fProcess.start(gJackCapturePath, arguments)
- self.fProcess.waitForStarted()
-
- if self.fFreewheel:
- print("NOTICE: rendering in freewheel mode")
- sleep(1)
- jacklib.set_freewheel(self.fJackClient, 1)
-
- if useTransport:
- self.fTimer.start()
- jacklib.transport_start(self.fJackClient)
-
- @pyqtSlot()
- def slot_renderStop(self):
- useTransport = self.ui.group_time.isChecked()
-
- if useTransport:
- jacklib.transport_stop(self.fJackClient)
-
- if self.fFreewheel:
- jacklib.set_freewheel(self.fJackClient, 0)
- sleep(1)
-
- self.fProcess.terminate()
- #self.fProcess.waitForFinished(5000)
-
- if useTransport:
- self.fTimer.stop()
-
- self.ui.group_render.setEnabled(True)
- self.ui.group_time.setEnabled(True)
- self.ui.group_encoding.setEnabled(True)
- self.ui.b_render.setVisible(True)
- self.ui.b_stop.setVisible(False)
- self.ui.b_close.setEnabled(True)
-
- self.ui.progressBar.setFormat("")
- self.ui.progressBar.setMinimum(0)
- self.ui.progressBar.setMaximum(1)
- self.ui.progressBar.setValue(0)
- self.ui.progressBar.update()
-
- # Restore buffer size
- newBufferSize = int(jacklib.get_buffer_size(self.fJackClient))
-
- if newBufferSize != self.fBufferSize:
- jacklib.set_buffer_size(self.fJackClient, newBufferSize)
-
- @pyqtSlot()
- def slot_getAndSetPath(self):
- getAndSetPath(self, self.ui.le_folder.text(), self.ui.le_folder)
-
- @pyqtSlot()
- def slot_setStartNow(self):
- time = int(jacklib.get_current_transport_frame(self.fJackClient) / self.fSampleRate)
- secs = time % 60
- mins = int(time / 60) % 60
- hrs = int(time / 3600) % 60
- self.ui.te_start.setTime(QTime(hrs, mins, secs))
-
- @pyqtSlot()
- def slot_setEndNow(self):
- time = int(jacklib.get_current_transport_frame(self.fJackClient) / self.fSampleRate)
- secs = time % 60
- mins = int(time / 60) % 60
- hrs = int(time / 3600) % 60
- self.ui.te_end.setTime(QTime(hrs, mins, secs))
-
- @pyqtSlot(QTime)
- def slot_updateStartTime(self, time):
- if time >= self.ui.te_end.time():
- self.ui.te_end.setTime(time)
- renderEnabled = False
- else:
- renderEnabled = True
-
- if self.ui.group_time.isChecked():
- self.ui.b_render.setEnabled(renderEnabled)
-
- @pyqtSlot(QTime)
- def slot_updateEndTime(self, time):
- if time <= self.ui.te_start.time():
- self.ui.te_start.setTime(time)
- renderEnabled = False
- else:
- renderEnabled = True
-
- if self.ui.group_time.isChecked():
- self.ui.b_render.setEnabled(renderEnabled)
-
- @pyqtSlot(bool)
- def slot_transportChecked(self, yesNo):
- if yesNo:
- renderEnabled = bool(self.ui.te_end.time() > self.ui.te_start.time())
- else:
- renderEnabled = True
-
- self.ui.b_render.setEnabled(renderEnabled)
-
- @pyqtSlot()
- def slot_updateProgressbar(self):
- time = int(jacklib.get_current_transport_frame(self.fJackClient)) / self.fSampleRate
- self.ui.progressBar.setValue(time)
-
- if time > self.fMaxTime or (self.fLastTime > time and not self.fFreewheel):
- self.slot_renderStop()
-
- self.fLastTime = time
-
- def saveSettings(self):
- settings = QSettings("Cadence", "Cadence-Render")
-
- if self.ui.rb_mono.isChecked():
- channels = 1
- elif self.ui.rb_stereo.isChecked():
- channels = 2
- else:
- channels = self.ui.sb_channels.value()
-
- settings.setValue("Geometry", self.saveGeometry())
- settings.setValue("OutputFolder", self.ui.le_folder.text())
- settings.setValue("FilenamePrefix", self.ui.le_prefix.text())
- settings.setValue("EncodingFormat", self.ui.cb_format.currentText())
- settings.setValue("EncodingDepth", self.ui.cb_depth.currentText())
- settings.setValue("EncodingChannels", channels)
- settings.setValue("UseTransport", self.ui.group_time.isChecked())
- settings.setValue("StartTime", self.ui.te_start.time())
- settings.setValue("EndTime", self.ui.te_end.time())
-
- def loadSettings(self):
- settings = QSettings("Cadence", "Cadence-Render")
-
- self.restoreGeometry(settings.value("Geometry", b""))
-
- outputFolder = settings.value("OutputFolder", HOME)
-
- if os.path.exists(outputFolder):
- self.ui.le_folder.setText(outputFolder)
-
- self.ui.le_prefix.setText(settings.value("FilenamePrefix", "jack_capture_"))
-
- encFormat = settings.value("EncodingFormat", "Wav", type=str)
-
- for i in range(self.ui.cb_format.count()):
- if self.ui.cb_format.itemText(i) == encFormat:
- self.ui.cb_format.setCurrentIndex(i)
- break
-
- encDepth = settings.value("EncodingDepth", "Float", type=str)
-
- for i in range(self.ui.cb_depth.count()):
- if self.ui.cb_depth.itemText(i) == encDepth:
- self.ui.cb_depth.setCurrentIndex(i)
- break
-
- encChannels = settings.value("EncodingChannels", 2, type=int)
-
- if encChannels == 1:
- self.ui.rb_mono.setChecked(True)
- elif encChannels == 2:
- self.ui.rb_stereo.setChecked(True)
- else:
- self.ui.rb_outro.setChecked(True)
- self.ui.sb_channels.setValue(encChannels)
-
- self.ui.group_time.setChecked(settings.value("UseTransport", False, type=bool))
- self.ui.te_start.setTime(settings.value("StartTime", self.ui.te_start.time(), type=QTime))
- self.ui.te_end.setTime(settings.value("EndTime", self.ui.te_end.time(), type=QTime))
-
- def closeEvent(self, event):
- self.saveSettings()
-
- if self.fJackClient:
- jacklib.client_close(self.fJackClient)
-
- QDialog.closeEvent(self, event)
-
- def done(self, r):
- QDialog.done(self, r)
- self.close()
-
- # ------------------------------------------------------------------------------------------------------------
- # Allow to use this as a standalone app
-
- if __name__ == '__main__':
- # Additional imports
- from PyQt5.QtWidgets import QApplication
-
- # App initialization
- app = QApplication(sys.argv)
- app.setApplicationName("Cadence-Render")
- app.setApplicationVersion(VERSION)
- app.setOrganizationName("Cadence")
- app.setWindowIcon(QIcon(":/scalable/cadence.svg"))
-
- if jacklib is None:
- QMessageBox.critical(None, app.translate("RenderW", "Error"), app.translate("RenderW",
- "JACK is not available in this system, cannot use this application."))
- sys.exit(1)
-
- if not gJackCapturePath:
- QMessageBox.critical(None, app.translate("RenderW", "Error"), app.translate("RenderW",
- "The 'jack_capture' application is not available.\n"
- "Is not possible to render without it!"))
- sys.exit(2)
-
- jackStatus = jacklib.jack_status_t(0x0)
- gJackClient = jacklib.client_open("Render", jacklib.JackNoStartServer, jacklib.pointer(jackStatus))
-
- if not gJackClient:
- errorString = get_jack_status_error_string(jackStatus)
- QMessageBox.critical(None, app.translate("RenderW", "Error"), app.translate("RenderW",
- "Could not connect to JACK, possible reasons:\n"
- "%s" % errorString))
- sys.exit(1)
-
- # Show GUI
- gui = RenderW(None)
- gui.setWindowIcon(getIcon("media-record", 48))
- gui.show()
-
- # App-Loop
- sys.exit(app.exec_())
|