update voice
This commit is contained in:
@@ -0,0 +1,132 @@
|
||||
import sounddevice as sd
|
||||
import numpy as np
|
||||
import time
|
||||
# import wave # No longer needed for dummy file saving in main for play_audio_file
|
||||
# import tempfile # No longer needed for dummy file saving in main
|
||||
# import os # No longer needed for dummy file saving in main
|
||||
# import soundfile as sf # No longer needed as play_audio_file is removed
|
||||
|
||||
DEFAULT_SAMPLE_RATE = 44100
|
||||
DEFAULT_CHANNELS = 1
|
||||
DEFAULT_CHUNK_SIZE_MS = 50 # Process audio in 50ms chunks for VAD
|
||||
DEFAULT_SILENCE_THRESHOLD_RMS = 0.01 # RMS value, needs tuning
|
||||
DEFAULT_MIN_SILENCE_DURATION_MS = 1000 # 1 second of silence to stop
|
||||
DEFAULT_MAX_RECORDING_DURATION_S = 15 # Safety cap for recording
|
||||
DEFAULT_PRE_ROLL_CHUNKS = 3 # Number of chunks to keep before speech starts
|
||||
|
||||
def record_audio(sample_rate = DEFAULT_SAMPLE_RATE,
|
||||
channels = DEFAULT_CHANNELS,
|
||||
chunk_size_ms = DEFAULT_CHUNK_SIZE_MS,
|
||||
silence_threshold_rms = DEFAULT_SILENCE_THRESHOLD_RMS,
|
||||
min_silence_duration_ms = DEFAULT_MIN_SILENCE_DURATION_MS,
|
||||
max_recording_duration_s = DEFAULT_MAX_RECORDING_DURATION_S,
|
||||
pre_roll_chunks_count = DEFAULT_PRE_ROLL_CHUNKS):
|
||||
"""
|
||||
Records audio from the microphone with silence-based VAD.
|
||||
Returns in-memory audio data (NumPy array of float32) and sample rate.
|
||||
Returns (None, sample_rate) if recording fails or max duration is met without speech.
|
||||
"""
|
||||
chunk_size_frames = int(sample_rate * chunk_size_ms / 1000)
|
||||
min_silence_chunks = int(min_silence_duration_ms / chunk_size_ms)
|
||||
max_chunks = int(max_recording_duration_s * 1000 / chunk_size_ms)
|
||||
|
||||
print(f"Listening... (max {max_recording_duration_s}s). Speak when ready.")
|
||||
print(f"(Silence threshold RMS: {silence_threshold_rms}, Min silence duration: {min_silence_duration_ms}ms)")
|
||||
|
||||
recorded_frames = []
|
||||
pre_roll_frames = []
|
||||
is_recording = False
|
||||
silence_counter = 0
|
||||
chunks_recorded = 0
|
||||
|
||||
stream = None
|
||||
try:
|
||||
stream = sd.InputStream(samplerate=sample_rate, channels=channels, dtype='float32')
|
||||
stream.start()
|
||||
|
||||
for i in range(max_chunks):
|
||||
audio_chunk, overflowed = stream.read(chunk_size_frames)
|
||||
if overflowed:
|
||||
print("Warning: Audio buffer overflowed!")
|
||||
|
||||
rms = np.sqrt(np.mean(audio_chunk**2))
|
||||
|
||||
if is_recording:
|
||||
recorded_frames.append(audio_chunk)
|
||||
chunks_recorded += 1
|
||||
if rms < silence_threshold_rms:
|
||||
silence_counter += 1
|
||||
if silence_counter >= min_silence_chunks:
|
||||
print("Silence detected, stopping recording.")
|
||||
break
|
||||
else:
|
||||
silence_counter = 0 # Reset silence counter on sound
|
||||
else:
|
||||
pre_roll_frames.append(audio_chunk)
|
||||
if len(pre_roll_frames) > pre_roll_chunks_count:
|
||||
pre_roll_frames.pop(0)
|
||||
|
||||
if rms > silence_threshold_rms:
|
||||
print("Speech detected, starting recording.")
|
||||
is_recording = True
|
||||
for frame_to_add in pre_roll_frames:
|
||||
recorded_frames.append(frame_to_add)
|
||||
chunks_recorded = len(recorded_frames)
|
||||
pre_roll_frames.clear()
|
||||
|
||||
if i == max_chunks - 1 and not is_recording:
|
||||
print("No speech detected within the maximum recording duration.")
|
||||
stream.stop()
|
||||
stream.close()
|
||||
return None, sample_rate
|
||||
|
||||
if not recorded_frames and is_recording:
|
||||
print("Recording started but captured no frames before stopping. This might be due to immediate silence.")
|
||||
|
||||
except Exception as e:
|
||||
print(f"Error during recording: {e}")
|
||||
return None, sample_rate
|
||||
finally:
|
||||
if stream and not stream.closed:
|
||||
stream.stop()
|
||||
stream.close()
|
||||
|
||||
if not recorded_frames:
|
||||
print("No audio was recorded.")
|
||||
return None, sample_rate
|
||||
|
||||
audio_data = np.concatenate(recorded_frames)
|
||||
print(f"Recording finished. Total duration: {len(audio_data)/sample_rate:.2f}s")
|
||||
return audio_data, sample_rate
|
||||
|
||||
def play_audio_data(audio_data, sample_rate):
|
||||
"""Plays in-memory audio data (NumPy array)."""
|
||||
try:
|
||||
print(f"Playing in-memory audio data (Sample rate: {sample_rate} Hz, Duration: {len(audio_data)/sample_rate:.2f}s)")
|
||||
sd.play(audio_data, sample_rate)
|
||||
sd.wait()
|
||||
print("Playback from memory finished.")
|
||||
except Exception as e:
|
||||
print(f"Error playing in-memory audio: {e}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
print("--- Testing audio_utils.py ---")
|
||||
|
||||
# Test 1: record_audio() and play_audio_data() (in-memory)
|
||||
print("\n--- Test: Record and Play In-Memory Audio ---")
|
||||
print("Please speak into the microphone. Recording will start on sound and stop on silence.")
|
||||
recorded_audio, rec_sr = record_audio(
|
||||
sample_rate=DEFAULT_SAMPLE_RATE,
|
||||
silence_threshold_rms=0.02,
|
||||
min_silence_duration_ms=1500,
|
||||
max_recording_duration_s=10
|
||||
)
|
||||
|
||||
if recorded_audio is not None and rec_sr is not None:
|
||||
print(f"Recorded audio data shape: {recorded_audio.shape}, Sample rate: {rec_sr} Hz")
|
||||
play_audio_data(recorded_audio, rec_sr)
|
||||
else:
|
||||
print("No audio recorded or recording failed.")
|
||||
|
||||
print("\n--- audio_utils.py tests finished. ---")
|
||||
@@ -0,0 +1,45 @@
|
||||
import os
|
||||
from openai import OpenAI
|
||||
|
||||
def call_llm(prompt, history=None):
|
||||
"""
|
||||
Calls the OpenAI API to get a response from an LLM.
|
||||
|
||||
Args:
|
||||
prompt: The user's current prompt.
|
||||
history: A list of previous messages in the conversation, where each message
|
||||
is a dict with "role" and "content" keys. E.g.,
|
||||
[{"role": "user", "content": "Hello"}, {"role": "assistant", "content": "Hi there!"}]
|
||||
|
||||
Returns:
|
||||
The LLM's response content as a string.
|
||||
"""
|
||||
client = OpenAI(api_key=os.environ.get("OPENAI_API_KEY", "your-api-key")) # Default if not set
|
||||
|
||||
messages = []
|
||||
if history:
|
||||
messages.extend(history)
|
||||
messages.append({"role": "user", "content": prompt})
|
||||
|
||||
r = client.chat.completions.create(
|
||||
model="gpt-4o",
|
||||
messages=messages
|
||||
)
|
||||
return r.choices[0].message.content
|
||||
|
||||
if __name__ == "__main__":
|
||||
# Ensure you have OPENAI_API_KEY set in your environment for this test to work
|
||||
print("Testing LLM call...")
|
||||
|
||||
# Test with a simple prompt
|
||||
response = call_llm("Tell me a short joke")
|
||||
print(f"LLM (Simple Joke): {response}")
|
||||
|
||||
# Test with history
|
||||
chat_history = [
|
||||
{"role": "user", "content": "What is the capital of France?"},
|
||||
{"role": "assistant", "content": "The capital of France is Paris."}
|
||||
]
|
||||
follow_up_prompt = "And what is a famous landmark there?"
|
||||
response_with_history = call_llm(follow_up_prompt, history=chat_history)
|
||||
print(f"LLM (Follow-up with History): {response_with_history}")
|
||||
Reference in New Issue
Block a user