I am attempting to create a sort of “soundboard” like application, but one of the primary features I require is monitoring the desktop audio and feeding it into my VB-Cable so I can play it through my microphone. I have been attempting some of my own solutions but all of them seem to lead to dead ends, and I am not sure how else to do it. I have set up a dropdown which shows my active output devices and I can receive the indexes of the output devices to use with libraries such as pyaudio
or sounddevice
.
I am not sure how to monitor an output device such as a speaker, especially in a way that allows me to feed or play it into another output device.
Here is what my program contains (Two buttons and a dropdown that allows me to select a device):
enter image description here
And here is my current attempt which I began heavily using ChatGPT to see if it could find solutions but it was no help at all:
import pyaudio
import threading
import sounddevice as sd
class AudioRedirector:
def __init__(self, vb_cable_input_index):
self.vb_cable_input_index = vb_cable_input_index
self.p = pyaudio.PyAudio()
self.streams_open = False
self.running = False
self.thread = None
def start_audio_redirection(self, speaker_output_index):
if self.streams_open:
print("Audio redirection is already running.")
return
self.streams_open = True
self.running = True
FORMAT = pyaudio.paInt16
RATE = 44100
CHUNK = 1024
# Get device info to determine number of channels
speaker_device_info = self.p.get_device_info_by_index(speaker_output_index)
speaker_channels = speaker_device_info['maxInputChannels']
if speaker_channels < 1:
raise ValueError(f"Speaker output device at index {speaker_output_index} does not support input channels.")
vb_cable_device_info = self.p.get_device_info_by_index(self.vb_cable_input_index)
vb_cable_channels = vb_cable_device_info['maxOutputChannels']
if vb_cable_channels < 1:
raise ValueError(f"VB-Cable input device at index {self.vb_cable_input_index} does not support output channels.")
# Open speaker output stream for reading
self.speaker_stream = self.p.open(format=FORMAT,
channels=speaker_channels,
rate=RATE,
input=True,
input_device_index=speaker_output_index,
frames_per_buffer=CHUNK)
# Open VB-Cable input stream for writing
self.vb_cable_stream = self.p.open(format=FORMAT,
channels=speaker_channels,
rate=RATE,
output=True,
output_device_index=self.vb_cable_input_index,
frames_per_buffer=CHUNK)
def redirect_audio():
print("Redirecting audio from speaker output to VB-Cable...")
try:
while self.running:
# Read audio data from the speaker output
data = self.speaker_stream.read(CHUNK)
# Write audio data to the VB-Cable input
self.vb_cable_stream.write(data)
except Exception as e:
print(f"Error: {e}")
finally:
self.stop_audio_redirection()
self.thread = threading.Thread(target=redirect_audio)
self.thread.start()
def stop_audio_redirection(self):
if not self.streams_open:
print("Audio redirection is not running.")
return
self.running = False
self.thread.join()
# Stop and close the streams
self.speaker_stream.stop_stream()
self.speaker_stream.close()
self.vb_cable_stream.stop_stream()
self.vb_cable_stream.close()
self.streams_open = False
# Terminate PyAudio
self.p.terminate()
print("Audio redirection stopped.")
def get_device_index(name):
devices = sd.query_devices()
for index, device in enumerate(devices):
if name in device['name']:
return index
return None
audio_redirector = AudioRedirector(get_device_index("CABLE Input (VB-Audio Virtual Cable)"))
def start_monitor(output_name):
audio_redirector.start_audio_redirection(get_device_index(output_name))
def stop_monitor():
audio_redirector.stop_audio_redirection()
The VB-Cable is hardcoded as the main output device while the output device I am attempting to monitor is fed into the function start_monitor
and using the function get_device_index
turns the name of the devices into the corresponding indexes.
The start_monitor
and stop_monitor
functions are called by the UI already so I just need to figure out how exactly to monitor the given and input the data into the hardcoded VB-Cable.
The solution is very messy and I feel like there has to be some library or method to solve this issue