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.

305 lines
10KB

  1. #!/usr/bin/env python3
  2. # -*- coding: utf-8 -*-
  3. # JACK-Capture frontend, with freewheel and transport support
  4. # Copyright (C) 2012 Filipe Coelho <falktx@gmail.com>
  5. #
  6. # This program is free software; you can redistribute it and/or modify
  7. # it under the terms of the GNU General Public License as published by
  8. # the Free Software Foundation; either version 2 of the License, or
  9. # any later version.
  10. #
  11. # This program is distributed in the hope that it will be useful,
  12. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14. # GNU General Public License for more details.
  15. #
  16. # For a full copy of the GNU General Public License see the COPYING file
  17. # Imports (Global)
  18. from PyQt4.QtCore import pyqtSlot, QProcess, QTime, QTimer
  19. from PyQt4.QtGui import QDialog
  20. from time import sleep
  21. # Imports (Custom Stuff)
  22. import ui_render
  23. from shared import *
  24. from jacklib_helpers import *
  25. global jack_client
  26. jack_client = None
  27. # Render Window
  28. class RenderW(QDialog, ui_render.Ui_RenderW):
  29. def __init__(self, parent):
  30. QDialog.__init__(self, parent)
  31. self.setupUi(self)
  32. # -------------------------------------------------------------
  33. # Get JACK client and base information
  34. global jack_client
  35. if (jack_client):
  36. self.m_jack_client = jack_client
  37. self.m_closeClient = False
  38. else:
  39. self.m_jack_client = jacklib.client_open("Render-Dialog", jacklib.JackNoStartServer, None)
  40. self.m_closeClient = True
  41. self.m_buffer_size = jacklib.get_buffer_size(self.m_jack_client)
  42. for i in range(self.cb_buffer_size.count()):
  43. if (int(self.cb_buffer_size.itemText(i)) == self.m_buffer_size):
  44. self.cb_buffer_size.setCurrentIndex(i)
  45. self.m_sample_rate = jacklib.get_sample_rate(self.m_jack_client)
  46. # -------------------------------------------------------------
  47. # Internal stuff
  48. self.m_max_time = 180
  49. self.m_last_time = 0
  50. self.m_freewheel = False
  51. self.m_timer = QTimer(self)
  52. self.m_process = QProcess(self)
  53. # -------------------------------------------------------------
  54. # Set-up GUI stuff
  55. # Get List of formats
  56. self.m_process.start("jack_capture", ["-pf"])
  57. self.m_process.waitForFinished()
  58. formats = str(self.m_process.readAllStandardOutput(), encoding="ascii").split(" ")
  59. for i in range(len(formats)-1):
  60. self.cb_format.addItem(formats[i])
  61. if (formats[i] == "wav"):
  62. self.cb_format.setCurrentIndex(i)
  63. self.cb_depth.setCurrentIndex(4) #Float
  64. self.rb_stereo.setChecked(True)
  65. self.te_end.setTime(QTime(0, 3, 0))
  66. self.progressBar.setMinimum(0)
  67. self.progressBar.setMaximum(0)
  68. self.progressBar.setValue(0)
  69. self.b_render.setIcon(getIcon("media-record"))
  70. self.b_stop.setIcon(getIcon("media-playback-stop"))
  71. self.b_close.setIcon(getIcon("window-close"))
  72. self.b_open.setIcon(getIcon("document-open"))
  73. self.b_stop.setVisible(False)
  74. self.le_folder.setText(HOME)
  75. # -------------------------------------------------------------
  76. # Set-up connections
  77. self.connect(self.b_render, SIGNAL("clicked()"), SLOT("slot_renderStart()"))
  78. self.connect(self.b_stop, SIGNAL("clicked()"), SLOT("slot_renderStop()"))
  79. self.connect(self.b_open, SIGNAL("clicked()"), SLOT("slot_getAndSetPath()"))
  80. self.connect(self.b_now_start, SIGNAL("clicked()"), SLOT("slot_setStartNow()"))
  81. self.connect(self.b_now_end, SIGNAL("clicked()"), SLOT("slot_setEndNow()"))
  82. self.connect(self.te_start, SIGNAL("timeChanged(const QTime)"), SLOT("slot_updateStartTime(const QTime)"))
  83. self.connect(self.te_end, SIGNAL("timeChanged(const QTime)"), SLOT("slot_updateEndTime(const QTime)"))
  84. self.connect(self.m_timer, SIGNAL("timeout()"), SLOT("slot_updateProgressbar()"))
  85. @pyqtSlot()
  86. def slot_renderStart(self):
  87. if (os.path.exists(self.le_folder.text()) == False):
  88. QMessageBox.warning(self, self.tr("Warning"), self.tr("The selected directory does not exist. Please choose a valid one."))
  89. return
  90. self.group_render.setEnabled(False)
  91. self.group_time.setEnabled(False)
  92. self.group_encoding.setEnabled(False)
  93. self.b_render.setVisible(False)
  94. self.b_stop.setVisible(True)
  95. self.b_close.setEnabled(False)
  96. self.m_freewheel = (self.cb_render_mode.currentIndex() == 1)
  97. new_buffer_size = int(self.cb_buffer_size.currentText())
  98. time_start = self.te_start.time()
  99. time_end = self.te_end.time()
  100. min_time = (time_start.hour()*3600)+(time_start.minute()*60)+(time_start.second())
  101. max_time = (time_end.hour()*3600)+(time_end.minute()*60)+(time_end.second())
  102. self.m_max_time = max_time
  103. self.progressBar.setMinimum(min_time)
  104. self.progressBar.setMaximum(max_time)
  105. self.progressBar.setValue(min_time)
  106. self.progressBar.update()
  107. if (self.m_freewheel):
  108. self.m_timer.setInterval(100)
  109. else:
  110. self.m_timer.setInterval(500)
  111. arguments = []
  112. # Bit depth
  113. arguments.append("-b")
  114. arguments.append(self.cb_depth.currentText())
  115. # Channels
  116. arguments.append("-c")
  117. if (self.rb_mono.isChecked()):
  118. arguments.append("1")
  119. elif (self.rb_stereo.isChecked()):
  120. arguments.append("2")
  121. else:
  122. arguments.append(str(self.sb_channels.value()))
  123. # Format
  124. arguments.append("-f")
  125. arguments.append(self.cb_format.currentText())
  126. # Controlled by transport
  127. arguments.append("-jt")
  128. # Silent mode
  129. arguments.append("-dc")
  130. arguments.append("-s")
  131. # Change current directory
  132. os.chdir(self.le_folder.text())
  133. if (new_buffer_size != jacklib.get_buffer_size(self.m_jack_client)):
  134. print("NOTICE: buffer size changed before render")
  135. jacklib.set_buffer_size(self.m_jack_client, new_buffer_size)
  136. if (jacklib.transport_query(self.m_jack_client, None) > jacklib.JackTransportStopped): # >TransportStopped is rolling/starting
  137. jacklib.transport_stop(self.m_jack_client)
  138. jacklib.transport_locate(self.m_jack_client, min_time*self.m_sample_rate)
  139. self.m_last_time = -1
  140. self.m_process.start("jack_capture", arguments)
  141. self.m_process.waitForStarted()
  142. if (self.m_freewheel):
  143. sleep(1)
  144. print("NOTICE: rendering in freewheel mode")
  145. jacklib.set_freewheel(jack_client, 1)
  146. self.m_timer.start()
  147. jacklib.transport_start(self.m_jack_client)
  148. @pyqtSlot()
  149. def slot_renderStop(self):
  150. jacklib.transport_stop(self.m_jack_client)
  151. if (self.m_freewheel):
  152. jacklib.set_freewheel(self.m_jack_client, 0)
  153. sleep(1)
  154. self.m_process.close()
  155. self.m_timer.stop()
  156. self.group_render.setEnabled(True)
  157. self.group_time.setEnabled(True)
  158. self.group_encoding.setEnabled(True)
  159. self.b_render.setVisible(True)
  160. self.b_stop.setVisible(False)
  161. self.b_close.setEnabled(True)
  162. self.progressBar.setMinimum(0)
  163. self.progressBar.setMaximum(0)
  164. self.progressBar.setValue(0)
  165. self.progressBar.update()
  166. # Restore buffer size
  167. new_buffer_size = jacklib.get_buffer_size(self.m_jack_client)
  168. if (new_buffer_size != self.m_buffer_size):
  169. jacklib.set_buffer_size(self.m_jack_client, new_buffer_size)
  170. @pyqtSlot()
  171. def slot_getAndSetPath(self):
  172. getAndSetPath(self, self.le_folder.text(), self.le_folder)
  173. @pyqtSlot()
  174. def slot_setStartNow(self):
  175. time = jacklib.get_current_transport_frame(self.m_jack_client)/self.m_sample_rate
  176. secs = time % 60
  177. mins = (time / 60) % 60
  178. hrs = (time / 3600) % 60
  179. self.te_start.setTime(QTime(hrs, mins, secs))
  180. @pyqtSlot()
  181. def slot_setEndNow(self):
  182. time = jacklib.get_current_transport_frame(self.m_jack_client)/self.m_sample_rate
  183. secs = time % 60
  184. mins = (time / 60) % 60
  185. hrs = (time / 3600) % 60
  186. self.te_end.setTime(QTime(hrs, mins, secs))
  187. @pyqtSlot(QTime)
  188. def slot_updateStartTime(self, time):
  189. if (time >= self.te_end.time()):
  190. self.te_end.setTime(time)
  191. self.b_render.setEnabled(False)
  192. else:
  193. self.b_render.setEnabled(True)
  194. @pyqtSlot(QTime)
  195. def slot_updateEndTime(self, time):
  196. if (time <= self.te_start.time()):
  197. time = self.te_start.setTime(time)
  198. self.b_render.setEnabled(False)
  199. else:
  200. self.b_render.setEnabled(True)
  201. @pyqtSlot()
  202. def slot_updateProgressbar(self):
  203. time = jacklib.get_current_transport_frame(self.m_jack_client)/self.m_sample_rate
  204. self.progressBar.setValue(time)
  205. if (time > self.m_max_time or (self.m_last_time > time and self.m_freewheel == False)):
  206. self.slot_renderStop()
  207. self.m_last_time = time
  208. def closeEvent(self, event):
  209. if (self.m_closeClient):
  210. jacklib.client_close(self.m_jack_client)
  211. QDialog.closeEvent(self, event)
  212. # -------------------------------------------------------------
  213. # Allow to use this as a standalone app
  214. if __name__ == '__main__':
  215. # Additional imports
  216. from PyQt4.QtGui import QApplication
  217. # App initialization
  218. app = QApplication(sys.argv)
  219. for iPATH in PATH:
  220. if os.path.exists(os.path.join(iPATH, "jack_capture")):
  221. break
  222. else:
  223. QMessageBox.critical(None, app.translate("RenderW", "Error"), app.translate("RenderW", "The 'jack_capture' application is not available.\nIs not possible to render without it!"))
  224. sys.exit(1)
  225. jack_status = jacklib.jack_status_t(0)
  226. jack_client = jacklib.client_open("Render", jacklib.JackNoStartServer, jacklib.pointer(jack_status))
  227. if not jack_client:
  228. QMessageBox.critical(None, app.translate("RenderW", "Error"), app.translate("RenderW", "Could not connect to JACK, possible errors:\n%s" % (get_jack_status_error_string(jack_status))))
  229. sys.exit(1)
  230. # Show GUI
  231. gui = RenderW(None)
  232. gui.setWindowIcon(getIcon("media-record", 48))
  233. gui.show()
  234. # App-Loop
  235. ret = app.exec_()
  236. if (jack_client):
  237. jacklib.client_close(jack_client)
  238. # Exit properly
  239. sys.exit(ret)