ASIO to JACK driver for WINE
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.

178 lines
6.6KB

  1. #!/usr/bin/env python3
  2. # -*- coding: utf-8 -*-
  3. # WineASIO Settings GUI
  4. # Copyright (C) 2020-2025 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.GUI file
  17. # ---------------------------------------------------------------------------------------------------------------------
  18. import os
  19. import sys
  20. try:
  21. from PyQt6.QtCore import pyqtSlot, QDir
  22. from PyQt6.QtWidgets import QApplication, QDialog, QDialogButtonBox
  23. QDialogButtonBox.RestoreDefaults = QDialogButtonBox.StandardButton.RestoreDefaults
  24. useQt6 = True
  25. except ImportError:
  26. from PyQt5.QtCore import pyqtSlot, QDir
  27. from PyQt5.QtWidgets import QApplication, QDialog, QDialogButtonBox
  28. useQt6 = False
  29. # ---------------------------------------------------------------------------------------------------------------------
  30. from ui_settings import Ui_WineASIOSettings
  31. # ---------------------------------------------------------------------------------------------------------------------
  32. BUFFER_SIZE_LIST = (16, 32, 64, 128, 256, 512, 1024, 2048, 4096, 8192)
  33. HOME = QDir.homePath()
  34. WINEPREFIX = os.getenv("WINEPREFIX")
  35. if not WINEPREFIX:
  36. WINEPREFIX = os.path.join(HOME, ".wine")
  37. WINEASIO_PREFIX = "HKEY_CURRENT_USER\\Software\\Wine\\WineASIO"
  38. # ---------------------------------------------------------------------------------------------------------------------
  39. def getWineASIOKeyValue(key: str, default: str):
  40. wineFile = os.path.join(WINEPREFIX, "user.reg")
  41. if not os.path.exists(wineFile):
  42. return default
  43. wineDumpF = open(wineFile, "r")
  44. wineDump = wineDumpF.read()
  45. wineDumpF.close()
  46. wineDumpSplit = wineDump.split("[Software\\\\Wine\\\\WineASIO]")
  47. if len(wineDumpSplit) <= 1:
  48. return default
  49. wineDumpSmall = wineDumpSplit[1].split("[")[0]
  50. keyDumpSplit = wineDumpSmall.split('"%s"' % key)
  51. if len(keyDumpSplit) <= 1:
  52. return default
  53. keyDumpSmall = keyDumpSplit[1].split(":")[1].split("\n")[0]
  54. return keyDumpSmall
  55. def smartHex(value: str, length: int):
  56. hexStr = hex(value).replace("0x","")
  57. if len(hexStr) < length:
  58. zeroCount = length - len(hexStr)
  59. hexStr = "%s%s" % ("0"*zeroCount, hexStr)
  60. return hexStr
  61. # ---------------------------------------------------------------------------------------------------------------------
  62. # Set-up GUI (Tweaks, WineASIO)
  63. # Force Restart Dialog
  64. class WineASIOSettingsDialog(QDialog, Ui_WineASIOSettings):
  65. def __init__(self):
  66. QDialog.__init__(self, None)
  67. self.setupUi(self)
  68. self.changed = False
  69. self.loadSettings()
  70. self.accepted.connect(self.slot_saveSettings)
  71. self.buttonBox.button(QDialogButtonBox.RestoreDefaults).clicked.connect(self.slot_restoreDefaults)
  72. self.sb_ports_in.valueChanged.connect(self.slot_flagChanged)
  73. self.sb_ports_out.valueChanged.connect(self.slot_flagChanged)
  74. self.cb_ports_connect_hw.clicked.connect(self.slot_flagChanged)
  75. self.cb_jack_autostart.clicked.connect(self.slot_flagChanged)
  76. self.cb_jack_fixed_bsize.clicked.connect(self.slot_flagChanged)
  77. self.cb_jack_buffer_size.currentIndexChanged[int].connect(self.slot_flagChanged)
  78. def loadSettings(self):
  79. ins = int(getWineASIOKeyValue("Number of inputs", "00000010"), 16)
  80. outs = int(getWineASIOKeyValue("Number of outputs", "00000010"), 16)
  81. hw = bool(int(getWineASIOKeyValue("Connect to hardware", "00000001"), 10))
  82. autostart = bool(int(getWineASIOKeyValue("Autostart server", "00000000"), 10))
  83. fixed_bsize = bool(int(getWineASIOKeyValue("Fixed buffersize", "00000001"), 10))
  84. prefer_bsize = int(getWineASIOKeyValue("Preferred buffersize", "00000400"), 16)
  85. for bsize in BUFFER_SIZE_LIST:
  86. self.cb_jack_buffer_size.addItem(str(bsize))
  87. if bsize == prefer_bsize:
  88. self.cb_jack_buffer_size.setCurrentIndex(self.cb_jack_buffer_size.count()-1)
  89. self.sb_ports_in.setValue(ins)
  90. self.sb_ports_out.setValue(outs)
  91. self.cb_ports_connect_hw.setChecked(hw)
  92. self.cb_jack_autostart.setChecked(autostart)
  93. self.cb_jack_fixed_bsize.setChecked(fixed_bsize)
  94. @pyqtSlot()
  95. def slot_flagChanged(self):
  96. self.changed = True
  97. @pyqtSlot()
  98. def slot_restoreDefaults(self):
  99. self.changed = True
  100. self.sb_ports_in.setValue(16)
  101. self.sb_ports_out.setValue(16)
  102. self.cb_ports_connect_hw.setChecked(True)
  103. self.cb_jack_autostart.setChecked(False)
  104. self.cb_jack_fixed_bsize.setChecked(True)
  105. self.cb_jack_buffer_size.setCurrentIndex(BUFFER_SIZE_LIST.index(1024))
  106. @pyqtSlot()
  107. def slot_saveSettings(self):
  108. REGFILE = 'REGEDIT4\n'
  109. REGFILE += '\n'
  110. REGFILE += '[HKEY_CURRENT_USER\\Software\\Wine\\WineASIO]\n'
  111. REGFILE += '"Autostart server"=dword:0000000%i\n' % int(1 if self.cb_jack_autostart.isChecked() else 0)
  112. REGFILE += '"Connect to hardware"=dword:0000000%i\n' % int(1 if self.cb_ports_connect_hw.isChecked() else 0)
  113. REGFILE += '"Fixed buffersize"=dword:0000000%i\n' % int(1 if self.cb_jack_fixed_bsize.isChecked() else 0)
  114. REGFILE += '"Number of inputs"=dword:000000%s\n' % smartHex(self.sb_ports_in.value(), 2)
  115. REGFILE += '"Number of outputs"=dword:000000%s\n' % smartHex(self.sb_ports_out.value(), 2)
  116. REGFILE += '"Preferred buffersize"=dword:0000%s\n' % smartHex(int(self.cb_jack_buffer_size.currentText()), 4)
  117. with open("/tmp/wineasio-settings.reg", "w") as fh:
  118. fh.write(REGFILE)
  119. os.system("regedit /tmp/wineasio-settings.reg")
  120. # ---------------------------------------------------------------------------------------------------------------------
  121. if __name__ == '__main__':
  122. # App initialization
  123. app = QApplication(sys.argv)
  124. app.setApplicationName("WineASIO Settings")
  125. app.setApplicationVersion("1.0.0")
  126. app.setOrganizationName("falkTX")
  127. # Show GUI
  128. gui = WineASIOSettingsDialog()
  129. gui.show()
  130. # Exit properly
  131. ret = app.exec() if useQt6 else app.exec_()
  132. sys.exit(ret)
  133. # ---------------------------------------------------------------------------------------------------------------------