- Replaced per-frame `run_in_executor` calls with single background reader thread in `ThreadedAudioInput` - Reader thread continuously calls `_read()` and enqueues data via `call_soon_threadsafe` to asyncio.Queue - Reduces per-frame scheduling overhead and context-switch jitter while preserving async API - Added thread lifecycle management: lazy start on first `frames()` call, graceful stop in `aclose()` - Update
66 lines
1.7 KiB
Python
66 lines
1.7 KiB
Python
import os
|
|
import sounddevice as sd
|
|
|
|
|
|
def main() -> None:
|
|
in_device = 'hw:0,0' # Shure MVX2U input
|
|
out_device = 'hw:1,0' # USB Audio output
|
|
sample_rate = 48000
|
|
frame_size = 480
|
|
|
|
dinfo = sd.query_devices(in_device)
|
|
doutfo = sd.query_devices(out_device)
|
|
print(f"Input device {in_device} has no input channels: {dinfo}")
|
|
inputs = [
|
|
(d['index'], d['name'], d['max_input_channels'])
|
|
for d in sd.query_devices()
|
|
if d.get('max_input_channels', 0) > 0
|
|
]
|
|
print('Input-capable devices:', inputs)
|
|
print(f"Output device {out_device} has no output channels: {doutfo}")
|
|
outputs = [
|
|
(d['index'], d['name'], d['max_output_channels'])
|
|
for d in sd.query_devices()
|
|
if d.get('max_output_channels', 0) > 0
|
|
]
|
|
print('Output-capable devices:', outputs)
|
|
|
|
istream = sd.RawInputStream(
|
|
samplerate=sample_rate,
|
|
device=in_device,
|
|
channels=1,
|
|
dtype='int16',
|
|
blocksize=frame_size,
|
|
)
|
|
ostream = sd.RawOutputStream(
|
|
samplerate=sample_rate,
|
|
device=out_device,
|
|
channels=1,
|
|
dtype='int16',
|
|
blocksize=frame_size,
|
|
)
|
|
|
|
istream.start()
|
|
ostream.start()
|
|
try:
|
|
while True:
|
|
data, overflowed = istream.read(frame_size)
|
|
if overflowed:
|
|
print("an overflow happened")
|
|
ostream.write(data)
|
|
except KeyboardInterrupt:
|
|
pass
|
|
finally:
|
|
try:
|
|
istream.stop(); istream.close()
|
|
except Exception:
|
|
pass
|
|
try:
|
|
ostream.stop(); ostream.close()
|
|
except Exception:
|
|
pass
|
|
|
|
|
|
if __name__ == '__main__':
|
|
main()
|