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.

216 lines
7.1KB

  1. #!/usr/bin/env python3
  2. # -*- coding: utf-8 -*-
  3. # Cadence ALSA-Loop daemon
  4. # Copyright (C) 2012-2013 Filipe Coelho <falktx@falktx.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. # ------------------------------------------------------------------------------------------------------------
  18. # Imports (Global)
  19. import os
  20. import sys
  21. from signal import signal, SIGINT, SIGTERM
  22. from time import sleep
  23. from PyQt4.QtCore import QProcess
  24. # ------------------------------------------------------------------------------------------------------------
  25. # Imports (Custom Stuff)
  26. import jacklib
  27. # --------------------------------------------------
  28. # Auto re-activate if on good kernel
  29. global reactivateCounter
  30. reactivateCounter = -1
  31. isKernelGood = os.uname()[2] >= "3.8"
  32. # --------------------------------------------------
  33. # Global loop check
  34. global doLoop, doRunNow, useZita, procIn, procOut
  35. doLoop = True
  36. doRunNow = True
  37. useZita = False
  38. procIn = QProcess()
  39. procOut = QProcess()
  40. checkFile = "/tmp/.cadence-aloop-daemon.x"
  41. # --------------------------------------------------
  42. # Global JACK variables
  43. global bufferSize, sampleRate, channels
  44. bufferSize = 1024
  45. sampleRate = 44100
  46. channels = 2
  47. # --------------------------------------------------
  48. # quit on SIGINT or SIGTERM
  49. def signal_handler(sig, frame=0):
  50. global doLoop
  51. doLoop = False
  52. # --------------------------------------------------
  53. # listen to jack buffer-size and sample-rate changes
  54. def buffer_size_callback(newBufferSize, arg):
  55. global doRunNow, bufferSize
  56. bufferSize = newBufferSize
  57. doRunNow = True
  58. return 0
  59. def sample_rate_callback(newSampleRate, arg):
  60. global doRunNow, sampleRate
  61. sampleRate = newSampleRate
  62. doRunNow = True
  63. return 0
  64. # --------------------------------------------------
  65. # listen to jack2 master switch
  66. def client_registration_callback(clientName, register, arg):
  67. global doLoop, doRunNow, reactivateCounter, useZita
  68. if clientName in (b"alsa2jack", b"jack2alsa") and not register:
  69. if doRunNow or not doLoop:
  70. return
  71. if isKernelGood:
  72. if reactivateCounter == -1:
  73. reactivateCounter = 0
  74. print("NOTICE: %s has been stopped, waiting 5 secs to reactivate" % ("zita-a2j/j2a" if useZita else "alsa_in/out"))
  75. elif doLoop:
  76. doLoop = False
  77. print("NOTICE: %s has been stopped, quitting now..." % ("zita-a2j/j2a" if useZita else "alsa_in/out"))
  78. # --------------------------------------------------
  79. # listen to jack shutdown
  80. def shutdown_callback(arg):
  81. global doLoop
  82. doLoop = False
  83. # --------------------------------------------------
  84. # run alsa_in and alsa_out
  85. def run_alsa_bridge():
  86. global reactivateCounter
  87. global bufferSize, sampleRate, channels
  88. global procIn, procOut
  89. global useZita
  90. if procIn.state() != QProcess.NotRunning:
  91. procIn.terminate()
  92. procIn.waitForFinished(1000)
  93. if procOut.state() != QProcess.NotRunning:
  94. procOut.terminate()
  95. procOut.waitForFinished(1000)
  96. reactivateCounter = -1
  97. if useZita:
  98. procIn.start("env", ["JACK_SAMPLE_RATE=%i" % sampleRate, "JACK_PERIOD_SIZE=%i" % bufferSize, "zita-a2j", "-d", "hw:Loopback,1,0", "-r", "%i" % sampleRate, "-p", "%i" % bufferSize, "-j", "alsa2jack", "-c", "%i" % channels])
  99. procOut.start("env", ["JACK_SAMPLE_RATE=%i" % sampleRate, "JACK_PERIOD_SIZE=%i" % bufferSize, "zita-j2a", "-d", "hw:Loopback,1,1", "-r", "%i" % sampleRate, "-p", "%i" % bufferSize, "-j", "jack2alsa", "-c", "%i" % channels])
  100. else:
  101. procIn.start("env", ["JACK_SAMPLE_RATE=%i" % sampleRate, "JACK_PERIOD_SIZE=%i" % bufferSize, "alsa_in", "-d", "cloop", "%i" % sampleRate, "-p", "%i" % bufferSize, "-j", "alsa2jack", "-q", "1", "-c", "%i" % channels])
  102. procOut.start("env", ["JACK_SAMPLE_RATE=%i" % sampleRate, "JACK_PERIOD_SIZE=%i" % bufferSize, "alsa_out", "-d", "ploop", "%i" % sampleRate, "-p", "%i" % bufferSize, "-j", "jack2alsa", "-q", "1", "-c", "%i" % channels])
  103. if procIn.waitForStarted():
  104. sleep(1)
  105. jacklib.connect(client, "alsa2jack:capture_1", "system:playback_1")
  106. jacklib.connect(client, "alsa2jack:capture_2", "system:playback_2")
  107. if procOut.waitForStarted():
  108. sleep(1)
  109. jacklib.connect(client, "system:capture_1", "jack2alsa:playback_1")
  110. jacklib.connect(client, "system:capture_2", "jack2alsa:playback_2")
  111. #--------------- main ------------------
  112. if __name__ == '__main__':
  113. for i in range(len(sys.argv)):
  114. if i == 0: continue
  115. argv = sys.argv[i]
  116. if argv == "--zita":
  117. useZita = True
  118. elif argv.startswith("--channels="):
  119. chStr = argv.replace("--channels=", "")
  120. if chStr.isdigit():
  121. channels = int(chStr)
  122. # Init JACK client
  123. client = jacklib.client_open("cadence-aloop-daemon", jacklib.JackUseExactName, None)
  124. if not client:
  125. print("cadence-aloop-daemon is already running, delete \"/tmp/.cadence-aloop-daemon.x\" to close it")
  126. quit()
  127. if jacklib.JACK2:
  128. jacklib.set_client_registration_callback(client, client_registration_callback, None)
  129. jacklib.set_buffer_size_callback(client, buffer_size_callback, None)
  130. jacklib.set_sample_rate_callback(client, sample_rate_callback, None)
  131. jacklib.on_shutdown(client, shutdown_callback, None)
  132. jacklib.activate(client)
  133. # Quit when requested
  134. signal(SIGINT, signal_handler)
  135. signal(SIGTERM, signal_handler)
  136. # Get initial values
  137. sampleRate = jacklib.get_sample_rate(client)
  138. bufferSize = jacklib.get_buffer_size(client)
  139. # Create check file
  140. if not os.path.exists(checkFile):
  141. os.mknod(checkFile)
  142. # Keep running until told otherwise
  143. firstStart = True
  144. while doLoop and os.path.exists(checkFile):
  145. if doRunNow:
  146. if firstStart:
  147. firstStart = False
  148. print("cadence-aloop-daemon started, using %s and %i channels" % ("zita-a2j/j2a" if useZita else "alsa_in/out", channels))
  149. run_alsa_bridge()
  150. doRunNow = False
  151. elif isKernelGood and reactivateCounter >= 0:
  152. if reactivateCounter == 5:
  153. reactivateCounter = -1
  154. doRunNow = True
  155. else:
  156. reactivateCounter += 1
  157. sleep(1)
  158. # Close JACK client
  159. jacklib.deactivate(client)
  160. jacklib.client_close(client)
  161. if os.path.exists(checkFile):
  162. os.remove(checkFile)
  163. if procIn.state() != QProcess.NotRunning:
  164. procIn.terminate()
  165. procIn.waitForFinished(1000)
  166. if procOut.state() != QProcess.NotRunning:
  167. procOut.terminate()
  168. procOut.waitForFinished(1000)