134 lines
4.0 KiB
Python
134 lines
4.0 KiB
Python
import openai
|
|
from openai import OpenAI
|
|
import pyaudio
|
|
import wave
|
|
import threading
|
|
from dotenv import load_dotenv
|
|
import os
|
|
|
|
# Load environment variables from .env file
|
|
load_dotenv()
|
|
|
|
# Set up your OpenAI API key from the environment variable
|
|
client = OpenAI(api_key=os.getenv('OPENAI_API_KEY'))
|
|
|
|
def list_audio_devices():
|
|
p = pyaudio.PyAudio()
|
|
print("Available audio devices:")
|
|
info = p.get_host_api_info_by_index(0)
|
|
num_devices = info.get('deviceCount')
|
|
# List all available devices, and mark output devices
|
|
for i in range(0, num_devices):
|
|
if p.get_device_info_by_index(i).get('maxOutputChannels') > 0:
|
|
print(f"Device index {i}: {p.get_device_info_by_index(i).get('name')}")
|
|
p.terminate()
|
|
|
|
|
|
def play_saved_audio(file_path, device_index=None):
|
|
# Open the saved audio file
|
|
wf = wave.open(file_path, 'rb')
|
|
|
|
print(f"Playing audio to device {device_index}")
|
|
|
|
# Setup PyAudio
|
|
p = pyaudio.PyAudio()
|
|
|
|
try:
|
|
stream = p.open(format=p.get_format_from_width(wf.getsampwidth()),
|
|
channels=wf.getnchannels(),
|
|
rate=wf.getframerate(),
|
|
output=True,
|
|
output_device_index=device_index)
|
|
data = wf.readframes(1024)
|
|
while data:
|
|
stream.write(data)
|
|
data = wf.readframes(1024)
|
|
except Exception as e:
|
|
print(f"Error playing audio on device {device_index}: {e}")
|
|
finally:
|
|
stream.stop_stream()
|
|
stream.close()
|
|
wf.close()
|
|
p.terminate()
|
|
|
|
#Plays to multiple device indexes at the same time
|
|
def play_audio_multiplexed(file_paths, device_indices):
|
|
p = pyaudio.PyAudio()
|
|
streams = []
|
|
|
|
# Open all files and start all streams
|
|
for file_path, device_index in zip(file_paths, device_indices):
|
|
wf = wave.open(file_path, 'rb')
|
|
stream = p.open(format=p.get_format_from_width(wf.getsampwidth()),
|
|
channels=wf.getnchannels(),
|
|
rate=wf.getframerate(),
|
|
output=True,
|
|
output_device_index=device_index)
|
|
streams.append((stream, wf))
|
|
|
|
# Play interleaved
|
|
active_streams = len(streams)
|
|
while active_streams > 0:
|
|
for stream, wf in streams:
|
|
data = wf.readframes(1024)
|
|
if data:
|
|
stream.write(data)
|
|
else:
|
|
stream.stop_stream()
|
|
stream.close()
|
|
wf.close()
|
|
active_streams -= 1
|
|
|
|
p.terminate()
|
|
|
|
def stream_audio_to_virtual_mic(text, voice="fable", device_index=None, device_index_2=None):
|
|
response = client.audio.speech.create(
|
|
model="tts-1",
|
|
voice=voice,
|
|
input=text,
|
|
response_format='wav'
|
|
)
|
|
|
|
#This can either stream to one device index at a time, or, via multiplexing
|
|
#it can stream to two similtaneously to prevent lag playing in sequence
|
|
if device_index_2 is not None:
|
|
file_path_1 = "output1.wav"
|
|
file_path_2 = "output2.wav"
|
|
response.stream_to_file(file_path_1)
|
|
response.stream_to_file(file_path_2)
|
|
play_audio_multiplexed([file_path_1, file_path_2], [device_index, device_index_2])
|
|
else:
|
|
file_path_1 = "output1.wav"
|
|
response.stream_to_file(file_path_1)
|
|
play_saved_audio(file_path_1, device_index)
|
|
|
|
return "";
|
|
|
|
|
|
|
|
if __name__ == "__main__":
|
|
import sys
|
|
|
|
|
|
arglen = len(sys.argv)
|
|
|
|
if arglen < 2:
|
|
print("Usage: python script.py 'text to convert'")
|
|
sys.exit(1)
|
|
|
|
print(f"arg count {arglen}")
|
|
|
|
if arglen == 4:
|
|
device_index = int(sys.argv[2])
|
|
device_index_2 = int(sys.argv[3])
|
|
elif arglen == 3:
|
|
device_index = int(sys.argv[2])
|
|
device_index_2 = None
|
|
else:
|
|
list_audio_devices()
|
|
device_index = int(input("Enter the device index: "))
|
|
device_index_2 = None
|
|
|
|
|
|
stream_audio_to_virtual_mic(sys.argv[1], voice="fable", device_index=device_index,device_index_2=device_index_2)
|