Eric Z commited on
Commit
b1c8b7b
·
1 Parent(s): 26d3d03

updates for voice production and sync

Browse files
Files changed (1) hide show
  1. stream_app.py +89 -34
stream_app.py CHANGED
@@ -3,8 +3,10 @@ import argparse
3
  import logging
4
  import gradio as gr
5
  from openai import OpenAI
6
- import whisper
7
  import io
 
 
8
 
9
  import dotenv
10
  dotenv.load_dotenv()
@@ -29,18 +31,18 @@ def run_gradio(config:dict):
29
  client = OpenAI(api_key=os.environ.get("OPENAI_API_KEY"))
30
 
31
  # transcription of audio
32
- def audio_transcribe(input_audio_model:str, input_audio:str, input_text:str):
33
  global whisper_model
34
  global logger
35
 
36
- if "offline" in input_audio_model.lower():
37
  if whisper_model is None:
38
  whisper_model = whisper.load_model("base")
39
- audio = whisper.load_audio(input_audio)
40
  result = whisper_model.transcribe(audio)
41
 
42
- elif "online" in input_audio_model.lower():
43
- with open(input_audio, 'rb') as file_audio:
44
  result = client.audio.transcriptions.create(
45
  model="whisper-1", file=file_audio, response_format="verbose_json",
46
  )
@@ -56,7 +58,7 @@ def run_gradio(config:dict):
56
  if len(prob_scores) > 0: # average the probs
57
  result["no_speech_prob"] = sum(prob_scores)/len(prob_scores)
58
 
59
- if result["no_speech_prob"] < (1 - config['speech_threshold']): # threshold to avoid bad output
60
  return input_text + " " + prompt
61
  return input_text
62
 
@@ -64,12 +66,29 @@ def run_gradio(config:dict):
64
  def audio_reset(input_text):
65
  # audio = whisper.clear?
66
  return "" # return empty
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
67
 
68
  # Define Gradio interface
69
- def get_ai_response(input_text, input_audio):
70
  prompt = input_text.strip()
71
  if not prompt:
72
- return "Please enter a prompt for interaction."
73
 
74
  logger.warning(f"Prompt: {prompt}")
75
  response = client.chat.completions.create(model=config['model'],
@@ -89,7 +108,8 @@ def run_gradio(config:dict):
89
  if token is None:
90
  break
91
  partial_response += token
92
- yield partial_response
 
93
 
94
  with gr.Blocks(css="footer{display:none !important}") as demo:
95
  gr.Markdown("""
@@ -101,47 +121,82 @@ def run_gradio(config:dict):
101
  with gr.Group():
102
  input_text = gr.Textbox(
103
  label="Text Input",
104
- placeholder="Enter your prompt here",
105
  lines=5,
106
- max_lines=10,
107
  )
108
  online_text_model = f"openai-{config['model']} (online)"
109
- input_audio_model = gr.Radio(
110
- label="Textual Model",
111
  choices=[online_text_model],
112
  value=online_text_model,
113
  )
114
 
115
  with gr.Group():
116
- input_audio = gr.Audio(
117
  label="Speech Input",
118
  streaming=True,
119
  type="filepath",
120
  )
121
- input_audio_model = gr.Radio(
122
- label="Audio Model",
 
 
 
 
123
  choices=["whisper (offline)", "openai-whisper (online)"],
124
  value="openai-whisper (online)",
125
  )
126
 
127
  with gr.Column():
128
- output_text = gr.Textbox(
129
- label="Output",
130
- interactive=False,
131
- lines=10,
132
- )
133
- submit_button = gr.Button("Submit", variant='primary')
134
- input_audio.stream(audio_transcribe,
135
- inputs=[input_audio_model, input_audio, input_text],
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
136
  outputs=input_text)
137
- input_audio.clear(audio_reset, inputs=input_text, outputs=input_text)
138
- input_audio.start_recording(audio_reset, inputs=input_text, outputs=input_text)
139
- input_audio.stop_recording(get_ai_response,
140
- inputs=[input_text, input_audio],
141
- outputs=output_text)
142
  submit_button.click(get_ai_response,
143
- inputs=[input_text, input_audio],
144
- outputs=output_text)
 
 
 
 
 
 
 
145
 
146
  # demo.set_api_mode(enabled=False) # Disable API exposure
147
  # demo.set_footer(enabled=False) # Disable Gradio footers
@@ -152,7 +207,7 @@ def run_gradio(config:dict):
152
 
153
  def parse_args() -> dict:
154
  parser = argparse.ArgumentParser()
155
- opt_group = parser.add_argument_group("Model Configuration")
156
  opt_group.add_argument("--model", type=str, default="gpt-4o",
157
  help="Model to use for chat completion.")
158
  opt_group.add_argument("--temperature", type=float, default=1.0,
@@ -161,7 +216,7 @@ def parse_args() -> dict:
161
  help="Maximum number of tokens to generate in chat completion.")
162
 
163
  opt_group = parser.add_argument_group("Speech Processing")
164
- opt_group.add_argument("--speech_threshold", type=float, default=0.10,
165
  help="Speech threshold (probability) for recognition to add text to a prompt. ")
166
 
167
  opt_group = parser.add_argument_group("App Settings")
 
3
  import logging
4
  import gradio as gr
5
  from openai import OpenAI
6
+ import whisper # just for local models
7
  import io
8
+ from pathlib import Path
9
+ import tempfile
10
 
11
  import dotenv
12
  dotenv.load_dotenv()
 
31
  client = OpenAI(api_key=os.environ.get("OPENAI_API_KEY"))
32
 
33
  # transcription of audio
34
+ def audio_transcribe(audio_input_model:str, audio_input:str, audio_threshold:float, input_text:str):
35
  global whisper_model
36
  global logger
37
 
38
+ if "offline" in audio_input_model.lower():
39
  if whisper_model is None:
40
  whisper_model = whisper.load_model("base")
41
+ audio = whisper.load_audio(audio_input)
42
  result = whisper_model.transcribe(audio)
43
 
44
+ elif "online" in audio_input_model.lower():
45
+ with open(audio_input, 'rb') as file_audio:
46
  result = client.audio.transcriptions.create(
47
  model="whisper-1", file=file_audio, response_format="verbose_json",
48
  )
 
58
  if len(prob_scores) > 0: # average the probs
59
  result["no_speech_prob"] = sum(prob_scores)/len(prob_scores)
60
 
61
+ if result["no_speech_prob"] < (1 - audio_threshold): # threshold to avoid bad output
62
  return input_text + " " + prompt
63
  return input_text
64
 
 
66
  def audio_reset(input_text):
67
  # audio = whisper.clear?
68
  return "" # return empty
69
+
70
+ # speak input text
71
+ def audio_speak(input_text, speaker_name, output_complete, auto_speak=None):
72
+ print(f"OUTPUT: {output_complete}")
73
+ if not output_complete:
74
+ return None
75
+ if auto_speak is not None and auto_speak.lower() == "manual": # abort if manual
76
+ return None
77
+ temp_file = tempfile.NamedTemporaryFile(delete=False)
78
+ response = client.audio.speech.create(
79
+ model="tts-1",
80
+ voice=speaker_name,
81
+ input=input_text
82
+ )
83
+ response.write_to_file(temp_file.name)
84
+ return temp_file.name
85
+
86
 
87
  # Define Gradio interface
88
+ def get_ai_response(input_text):
89
  prompt = input_text.strip()
90
  if not prompt:
91
+ return "Please enter a prompt for interaction.", False
92
 
93
  logger.warning(f"Prompt: {prompt}")
94
  response = client.chat.completions.create(model=config['model'],
 
108
  if token is None:
109
  break
110
  partial_response += token
111
+ yield partial_response, False
112
+ yield partial_response, True
113
 
114
  with gr.Blocks(css="footer{display:none !important}") as demo:
115
  gr.Markdown("""
 
121
  with gr.Group():
122
  input_text = gr.Textbox(
123
  label="Text Input",
124
+ placeholder="Enter your prompt here or use speech recognition to genreate it.",
125
  lines=5,
126
+ max_lines=5,
127
  )
128
  online_text_model = f"openai-{config['model']} (online)"
129
+ audio_input_model = gr.Radio(
130
+ label="Textual Model", show_label=False,
131
  choices=[online_text_model],
132
  value=online_text_model,
133
  )
134
 
135
  with gr.Group():
136
+ audio_input = gr.Audio(
137
  label="Speech Input",
138
  streaming=True,
139
  type="filepath",
140
  )
141
+ audio_threshold = gr.Slider(
142
+ label="Speech Threshold", minimum=0.0, maximum=1.0, step=0.01,
143
+ value=config['speech_threshold'],
144
+ )
145
+ audio_input_model = gr.Radio(
146
+ label="Audio Model", show_label=False,
147
  choices=["whisper (offline)", "openai-whisper (online)"],
148
  value="openai-whisper (online)",
149
  )
150
 
151
  with gr.Column():
152
+ with gr.Group():
153
+ with gr.Row():
154
+ output_text = gr.Textbox(
155
+ label="Output",
156
+ interactive=False,
157
+ lines=10, max_lines=15,
158
+ )
159
+ with gr.Row():
160
+ combo_speaker = gr.Dropdown(
161
+ choices=["alloy", "echo", "fable", "onyx", "nova", "shimmer"],
162
+ show_label=False, value="nova", interactive=True,
163
+ )
164
+ combo_autospeak = gr.Radio(
165
+ choices=["Auto-speak", "Manual"], show_label=False,
166
+ value="Manual", interactive=True,
167
+ )
168
+ with gr.Row():
169
+ speak_button = gr.Button("Speak!", variant='secondary', interactive=True)
170
+ with gr.Row():
171
+ audio_playback = gr.Audio(
172
+ label="Speech", autoplay=True,
173
+ streaming=False,
174
+ type="filepath", sources=None,
175
+ )
176
+
177
+ with gr.Row():
178
+ submit_button = gr.Button("Submit", variant='primary')
179
+ output_complete = gr.State(False)
180
+
181
+
182
+ audio_input.stream(audio_transcribe,
183
+ inputs=[audio_input_model, audio_input, audio_threshold, input_text],
184
  outputs=input_text)
185
+ audio_input.clear(audio_reset, inputs=input_text, outputs=input_text)
186
+ audio_input.start_recording(audio_reset, inputs=input_text, outputs=input_text)
187
+ audio_input.stop_recording(get_ai_response,
188
+ inputs=[input_text],
189
+ outputs=[output_text, output_complete])
190
  submit_button.click(get_ai_response,
191
+ inputs=[input_text],
192
+ outputs=[output_text, output_complete])
193
+ output_text.change(audio_speak,
194
+ inputs=[output_text, combo_speaker, output_complete, combo_autospeak],
195
+ outputs=audio_playback)
196
+ speak_button.click(audio_speak,
197
+ inputs=[output_text, combo_speaker, output_complete],
198
+ outputs=audio_playback)
199
+
200
 
201
  # demo.set_api_mode(enabled=False) # Disable API exposure
202
  # demo.set_footer(enabled=False) # Disable Gradio footers
 
207
 
208
  def parse_args() -> dict:
209
  parser = argparse.ArgumentParser()
210
+ opt_group = parser.add_argument_group("Model Configuration")
211
  opt_group.add_argument("--model", type=str, default="gpt-4o",
212
  help="Model to use for chat completion.")
213
  opt_group.add_argument("--temperature", type=float, default=1.0,
 
216
  help="Maximum number of tokens to generate in chat completion.")
217
 
218
  opt_group = parser.add_argument_group("Speech Processing")
219
+ opt_group.add_argument("--speech_threshold", type=float, default=0.15,
220
  help="Speech threshold (probability) for recognition to add text to a prompt. ")
221
 
222
  opt_group = parser.add_argument_group("App Settings")