Eric Z commited on
Commit
51a68c2
·
1 Parent(s): 41c120f

update streaming audio generation

Browse files
Files changed (1) hide show
  1. stream_app.py +57 -37
stream_app.py CHANGED
@@ -63,25 +63,40 @@ def run_gradio(config:dict):
63
  return input_text
64
 
65
  # reset transcribed text
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
@@ -102,12 +117,12 @@ def run_gradio(config:dict):
102
  )
103
 
104
  partial_response = ""
105
- for stream_response in response:
106
- logger.warning(f"Prompt response: {stream_response.to_dict()}")
107
- token = stream_response.choices[0].delta.content
108
- if token is None:
109
  break
110
- partial_response += token
111
  yield partial_response, False
112
  yield partial_response, True
113
 
@@ -161,41 +176,47 @@ def run_gradio(config:dict):
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
@@ -237,7 +258,6 @@ def parse_args() -> dict:
237
  if __name__ == "__main__":
238
  os.environ['GRADIO_ANALYTICS_ENABLED'] = 'False'
239
  api_key = os.environ.get("OPENAI_API_KEY")
240
- print(api_key)
241
  if not api_key:
242
  raise ValueError("OPENAI_API_KEY environment variable not set as environment variable or as a setting in `.env`. (see https://platform.openai.com/docs/quickstart/step-2-set-up-your-api-key)")
243
 
 
63
  return input_text
64
 
65
  # reset transcribed text
66
+ def audio_reset(input_text, path_prior):
67
+ if path_prior is not None:
68
+ Path(path_prior).unlink()
69
  # audio = whisper.clear?
70
+ return "", None # return empty, clear prior file
71
 
72
  # speak input text
73
+ def audio_speak(input_text, speaker_name, input_done=True, offset_prior=0, path_prior=None, auto_speak=None):
74
+ # alternate on-device? - https://github.com/suno-ai/bark?tab=readme-ov-file
75
+ # print(f"Speak: {input_text}, {offset_prior} of {len(input_text)}")
76
+ if not input_text: # empty string on conclusion (when streaming)
77
+ return None, None, 0
78
+ elif auto_speak is not None:
79
+ if "manual" in auto_speak.lower(): # don't proceed if manual
80
+ return None, None, 0
81
+ elif (not input_done) and ("stream" not in auto_speak.lower()): # stream, not done
82
+ return None, None, 0
83
+
84
+ if (path_prior is None) or (offset_prior > len(input_text)):
85
+ temp_file = tempfile.NamedTemporaryFile(delete=False)
86
+ path_prior = temp_file.name
87
+ offset_prior = 0
88
  response = client.audio.speech.create(
89
  model="tts-1",
90
  voice=speaker_name,
91
+ input=input_text[offset_prior:]
92
  )
93
+ offset_prior += len(input_text)
94
+ # append to existing file
95
+ # example: https://community.openai.com/t/streaming-from-text-to-speech-api/493784/5
96
+ with open(path_prior, 'ab') as file_append:
97
+ for chunk in response.iter_bytes(chunk_size=4096):
98
+ file_append.write(chunk)
99
+ return path_prior, path_prior, offset_prior
100
 
101
 
102
  # Define Gradio interface
 
117
  )
118
 
119
  partial_response = ""
120
+ response_dicts = [stream_response.to_dict() for stream_response in response]
121
+ logger.warning(f"Prompt response: {response_dicts}")
122
+ for stream_response in response_dicts:
123
+ if 'content' not in stream_response['choices'][0]['delta']:
124
  break
125
+ partial_response += stream_response['choices'][0]['delta']['content']
126
  yield partial_response, False
127
  yield partial_response, True
128
 
 
176
  choices=["alloy", "echo", "fable", "onyx", "nova", "shimmer"],
177
  show_label=False, value="nova", interactive=True,
178
  )
179
+ with gr.Row():
180
  combo_autospeak = gr.Radio(
181
+ choices=["Auto-speak", "Auto-speak (stream)", "Manual"], show_label=False,
182
  value="Manual", interactive=True,
183
  )
184
  with gr.Row():
185
  speak_button = gr.Button("Speak!", variant='secondary', interactive=True)
186
  with gr.Row():
187
  audio_playback = gr.Audio(
188
+ label="Speech", autoplay=True, streaming=False,
 
189
  type="filepath", sources=None,
190
  )
191
 
192
  with gr.Row():
193
+ submit_button = gr.Button("Generate Response", variant='primary')
194
+ generate_done = gr.State(False) # is last genai content chunked?
195
+ path_prior = gr.State(None) # retain prior file for audio playback
196
+ offset_prior = gr.State(0) # track textual offset in genrated content
197
+
198
 
199
+ audio_input.stream(audio_transcribe, # started streaming to transcribe
200
  inputs=[audio_input_model, audio_input, audio_threshold, input_text],
201
  outputs=input_text)
202
+ audio_input.clear(audio_reset, # cleared audio
203
+ inputs=[input_text, path_prior],
204
+ outputs=[input_text, path_prior])
205
+ audio_input.start_recording(audio_reset, # started a new speech->text
206
+ inputs=[input_text, path_prior],
207
+ outputs=[input_text, path_prior])
208
+ audio_input.stop_recording(get_ai_response, # stopped recording, start response
209
  inputs=[input_text],
210
+ outputs=[output_text, generate_done])
211
+ submit_button.click(get_ai_response, # clicked 'generate'
212
  inputs=[input_text],
213
+ outputs=[output_text, generate_done])
214
+ output_text.change(audio_speak, # streaming response from generate
215
+ inputs=[output_text, combo_speaker, generate_done, offset_prior, path_prior, combo_autospeak],
216
+ outputs=[audio_playback, path_prior, offset_prior])
217
+ speak_button.click(audio_speak, # click for speak trigger
218
+ inputs=[output_text, combo_speaker],
219
+ outputs=[audio_playback, path_prior, offset_prior])
220
 
221
 
222
  # demo.set_api_mode(enabled=False) # Disable API exposure
 
258
  if __name__ == "__main__":
259
  os.environ['GRADIO_ANALYTICS_ENABLED'] = 'False'
260
  api_key = os.environ.get("OPENAI_API_KEY")
 
261
  if not api_key:
262
  raise ValueError("OPENAI_API_KEY environment variable not set as environment variable or as a setting in `.env`. (see https://platform.openai.com/docs/quickstart/step-2-set-up-your-api-key)")
263