Add microphone picker and device management features
- Implement `list_input_devices` to retrieve and filter input-capable audio devices. - Create `MicPickerDialog` for selecting and saving microphone settings. - Add `set_env_value` function to update .env file with selected microphone index. - Update UI components to integrate microphone selection functionality. - Enhance tests for microphone device handling and environment variable management.
This commit is contained in:
@@ -0,0 +1,62 @@
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
sys.path.insert(0, str(Path(__file__).resolve().parent.parent))
|
||||
|
||||
from bolt_pet import config
|
||||
|
||||
|
||||
def test_set_env_value_uncomments_existing_line(tmp_path, monkeypatch):
|
||||
(tmp_path / ".env").write_text("#MIC_DEVICE=\nOTHER=1\n", encoding="utf-8")
|
||||
monkeypatch.setattr(config, "HERE", tmp_path)
|
||||
|
||||
config.set_env_value("MIC_DEVICE", "USB Mic")
|
||||
|
||||
lines = (tmp_path / ".env").read_text(encoding="utf-8").splitlines()
|
||||
assert lines == ["MIC_DEVICE=USB Mic", "OTHER=1"]
|
||||
assert config.os.environ["MIC_DEVICE"] == "USB Mic"
|
||||
|
||||
|
||||
def test_set_env_value_overwrites_existing_uncommented_line(tmp_path, monkeypatch):
|
||||
(tmp_path / ".env").write_text("MIC_DEVICE=Old Mic\n", encoding="utf-8")
|
||||
monkeypatch.setattr(config, "HERE", tmp_path)
|
||||
|
||||
config.set_env_value("MIC_DEVICE", "New Mic")
|
||||
|
||||
lines = (tmp_path / ".env").read_text(encoding="utf-8").splitlines()
|
||||
assert lines == ["MIC_DEVICE=New Mic"]
|
||||
|
||||
|
||||
def test_set_env_value_appends_when_key_is_missing(tmp_path, monkeypatch):
|
||||
(tmp_path / ".env").write_text("OTHER=1\n", encoding="utf-8")
|
||||
monkeypatch.setattr(config, "HERE", tmp_path)
|
||||
|
||||
config.set_env_value("MIC_DEVICE", "New Mic")
|
||||
|
||||
lines = (tmp_path / ".env").read_text(encoding="utf-8").splitlines()
|
||||
assert lines == ["OTHER=1", "MIC_DEVICE=New Mic"]
|
||||
|
||||
|
||||
def test_set_env_value_creates_file_if_missing(tmp_path, monkeypatch):
|
||||
monkeypatch.setattr(config, "HERE", tmp_path)
|
||||
|
||||
config.set_env_value("MIC_DEVICE", "New Mic")
|
||||
|
||||
assert (tmp_path / ".env").read_text(encoding="utf-8") == "MIC_DEVICE=New Mic\n"
|
||||
|
||||
|
||||
def test_mic_device_parses_a_numeric_index_as_int():
|
||||
# sounddevice only takes an int index as-is; a string (even "1") goes
|
||||
# through substring matching against every device name instead, which is
|
||||
# not what a saved index means — see the tray's mic picker.
|
||||
assert config._mic_device("1") == 1
|
||||
assert isinstance(config._mic_device("1"), int)
|
||||
|
||||
|
||||
def test_mic_device_keeps_a_name_as_a_string():
|
||||
assert config._mic_device("USB Mic") == "USB Mic"
|
||||
|
||||
|
||||
def test_mic_device_blank_is_none():
|
||||
assert config._mic_device("") is None
|
||||
assert config._mic_device(" ") is None
|
||||
@@ -100,6 +100,32 @@ def test_returns_none_when_should_continue_stops_before_speech():
|
||||
assert result is None
|
||||
|
||||
|
||||
def test_list_input_devices_filters_to_input_capable(monkeypatch):
|
||||
fake_devices = [
|
||||
{"name": "Speakers", "max_input_channels": 0, "hostapi": 0},
|
||||
{"name": "USB Mic", "max_input_channels": 2, "hostapi": 0},
|
||||
{"name": "Line In", "max_input_channels": 1, "hostapi": 1},
|
||||
]
|
||||
fake_hostapis = [{"name": "MME"}, {"name": "WASAPI"}]
|
||||
|
||||
class _FakeSoundDevice:
|
||||
@staticmethod
|
||||
def query_devices():
|
||||
return fake_devices
|
||||
|
||||
@staticmethod
|
||||
def query_hostapis(index):
|
||||
return fake_hostapis[index]
|
||||
|
||||
monkeypatch.setitem(sys.modules, "sounddevice", _FakeSoundDevice())
|
||||
|
||||
devices = mic.list_input_devices()
|
||||
assert devices == [
|
||||
{"index": 1, "name": "USB Mic", "hostapi": "MME"},
|
||||
{"index": 2, "name": "Line In", "hostapi": "WASAPI"},
|
||||
]
|
||||
|
||||
|
||||
def test_pcm_to_wav_bytes_round_trips_via_wave_module():
|
||||
import wave
|
||||
import io
|
||||
|
||||
Reference in New Issue
Block a user