device_panel.py 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149
  1. """
  2. Device Panel — UI for selecting audio input/output devices.
  3. """
  4. from PyQt6.QtWidgets import (QWidget, QVBoxLayout, QHBoxLayout,
  5. QLabel, QComboBox, QPushButton, QGroupBox,
  6. QMessageBox)
  7. from PyQt6.QtCore import pyqtSignal
  8. from core.audio_device import DeviceManager
  9. class DevicePanel(QWidget):
  10. # Signal emitted when devices are successfully applied
  11. devices_changed = pyqtSignal()
  12. def __init__(self, device_manager: DeviceManager):
  13. super().__init__()
  14. self.device_manager = device_manager
  15. self._init_ui()
  16. self.refresh_device_lists()
  17. def _init_ui(self):
  18. layout = QVBoxLayout()
  19. layout.setSpacing(20)
  20. # --- Audio Hardware Settings Group ---
  21. hw_group = QGroupBox("Audio Hardware Setup")
  22. hw_layout = QVBoxLayout()
  23. # Info text
  24. info_lbl = QLabel(
  25. "Select the sound card inputs and outputs connected to your cassette deck.\n"
  26. "• Connect PC Output to Tape Deck REC IN\n"
  27. "• Connect Tape Deck PLAY OUT to PC Input"
  28. )
  29. info_lbl.setWordWrap(True)
  30. info_lbl.setStyleSheet("color: #a0a0b0; font-style: italic; margin-bottom: 10px;")
  31. hw_layout.addWidget(info_lbl)
  32. # Input Device Row
  33. in_row = QHBoxLayout()
  34. in_lbl = QLabel("Recording Input (From Tape):")
  35. in_lbl.setFixedWidth(180)
  36. self.in_combo = QComboBox()
  37. self.in_combo.setSizeAdjustPolicy(QComboBox.SizeAdjustPolicy.AdjustToContents)
  38. in_row.addWidget(in_lbl)
  39. in_row.addWidget(self.in_combo, 1)
  40. hw_layout.addLayout(in_row)
  41. # Output Device Row
  42. out_row = QHBoxLayout()
  43. out_lbl = QLabel("Playback Output (To Tape):")
  44. out_lbl.setFixedWidth(180)
  45. self.out_combo = QComboBox()
  46. self.out_combo.setSizeAdjustPolicy(QComboBox.SizeAdjustPolicy.AdjustToContents)
  47. out_row.addWidget(out_lbl)
  48. out_row.addWidget(self.out_combo, 1)
  49. hw_layout.addLayout(out_row)
  50. # Sample Rate Row
  51. sr_row = QHBoxLayout()
  52. sr_lbl = QLabel("Sample Rate:")
  53. sr_lbl.setFixedWidth(180)
  54. self.sr_combo = QComboBox()
  55. self.sr_combo.addItems(["44100 Hz", "48000 Hz", "96000 Hz"])
  56. self.sr_combo.setCurrentText("44100 Hz")
  57. sr_row.addWidget(sr_lbl)
  58. sr_row.addWidget(self.sr_combo)
  59. sr_row.addStretch()
  60. hw_layout.addLayout(sr_row)
  61. hw_group.setLayout(hw_layout)
  62. layout.addWidget(hw_group)
  63. # --- Actions Row ---
  64. action_row = QHBoxLayout()
  65. self.refresh_btn = QPushButton("Refresh Devices")
  66. self.refresh_btn.clicked.connect(self.refresh_device_lists)
  67. self.apply_btn = QPushButton("Apply Settings")
  68. self.apply_btn.setObjectName("primaryButton")
  69. self.apply_btn.clicked.connect(self.apply_settings)
  70. action_row.addWidget(self.refresh_btn)
  71. action_row.addStretch()
  72. action_row.addWidget(self.apply_btn)
  73. layout.addLayout(action_row)
  74. layout.addStretch()
  75. self.setLayout(layout)
  76. def refresh_device_lists(self):
  77. """Update the dropdowns with available devices."""
  78. self.device_manager.refresh_devices()
  79. self.in_combo.clear()
  80. for dev in self.device_manager.get_input_devices():
  81. self.in_combo.addItem(str(dev), dev.index)
  82. self.out_combo.clear()
  83. for dev in self.device_manager.get_output_devices():
  84. self.out_combo.addItem(str(dev), dev.index)
  85. # Try to select current if set
  86. if self.device_manager.input_device_id is not None:
  87. idx = self.in_combo.findData(self.device_manager.input_device_id)
  88. if idx >= 0:
  89. self.in_combo.setCurrentIndex(idx)
  90. if self.device_manager.output_device_id is not None:
  91. idx = self.out_combo.findData(self.device_manager.output_device_id)
  92. if idx >= 0:
  93. self.out_combo.setCurrentIndex(idx)
  94. def apply_settings(self):
  95. """Apply selected devices to the DeviceManager."""
  96. in_idx = self.in_combo.currentData()
  97. out_idx = self.out_combo.currentData()
  98. sr_str = self.sr_combo.currentText().split()[0]
  99. sr = int(sr_str)
  100. try:
  101. if in_idx is not None:
  102. self.device_manager.input_device_id = in_idx
  103. if out_idx is not None:
  104. self.device_manager.output_device_id = out_idx
  105. self.device_manager.sample_rate = sr
  106. self.devices_changed.emit()
  107. # Change button appearance briefly to show success
  108. original_text = self.apply_btn.text()
  109. self.apply_btn.setText("Applied ✓")
  110. self.apply_btn.setStyleSheet("background-color: #4CAF50; color: white;")
  111. # Reset after 2 seconds
  112. import PyQt6.QtCore as QtCore
  113. QtCore.QTimer.singleShot(2000, lambda: self._reset_apply_btn(original_text))
  114. except Exception as e:
  115. QMessageBox.critical(self, "Device Error", f"Failed to apply audio settings:\n{str(e)}")
  116. def _reset_apply_btn(self, text):
  117. self.apply_btn.setText(text)
  118. self.apply_btn.setStyleSheet("") # Revert to stylesheet default