[Python] how to send audio files media to twilio stream phone call

Discussão em 'Python' iniciado por Stack, Junho 3, 2025.

  1. Stack

    Stack Membro Participativo

    I'm basically building a simple GPT assistant through phone calls using twilio, using the google cloud speech Im transcripting the user's input. for now I have a sample tts_output4 a audio file which I want to be played after the transcription is done. I looked into various sources but can't find anything to play the audio.

    Basically after my call is accepted and open A.I responds to it, I have a audio file which needs to be played called tts_output4.wav which will played using send_static_audio function.

    below is my server.py code:


    import base64
    import json
    import threading
    from io import BytesIO
    import time
    from pydub import AudioSegment
    current_sentence = ""
    last_timestamp = 0
    sentence_timeout = 1.5

    from flask import Flask, render_template
    from flask_sockets import Sockets
    from google.cloud.speech import RecognitionConfig, StreamingRecognitionConfig
    from gtts import gTTS
    import os
    import openai # GPT-4 integration

    from SpeechClientBridge import SpeechClientBridge

    # GPT-4 API setup
    openai.api_key = os.getenviron("OpenAI_API_key")

    HTTP_SERVER_PORT = 8080

    config = RecognitionConfig(
    encoding=RecognitionConfig.AudioEncoding.MULAW,
    sample_rate_hertz=8000,
    language_code="en-US",
    )
    streaming_config = StreamingRecognitionConfig(config=config, interim_results=True)

    app = Flask(__name__)
    sockets = Sockets(app)


    @app.route("/twiml", methods=["POST"])
    def return_twiml():
    print("POST TwiML")
    return render_template("streams.xml")


    def on_transcription_response(response, ws):
    global current_sentence, last_timestamp

    if not response.results:
    return

    result = response.results[0]
    if not result.alternatives:
    return

    transcription = result.alternatives[0].transcript
    stability = result.stability
    is_final = result.is_final

    current_time = time.time()

    if is_final or (current_time - last_timestamp > sentence_timeout and stability > 0.8):
    # Append only the new part of the transcription
    if len(transcription) > len(current_sentence):
    current_sentence += transcription[len(current_sentence):]

    print("Complete Sentence:", current_sentence)

    gpt_response = get_gpt_response(current_sentence)
    print("GPT Response:", gpt_response)
    send_static_audio(ws)

    # send_gpt_response_as_audio(gpt_response, ws)



    current_sentence = ""
    last_timestamp = current_time
    else:

    if len(transcription) > len(current_sentence):
    current_sentence = transcription
    last_timestamp = current_time



    def get_gpt_response(prompt):
    """Get response from GPT-4 using the v1/chat/completions endpoint."""
    response = openai.ChatCompletion.create(
    model="gpt-4o",
    messages=[
    {"role": "system", "content": "You are a helpful assistant."},
    {"role": "user", "content": prompt}
    ],
    max_tokens=150
    )
    return response['choices'][0]['message']['content'].strip()

    def send_gpt_response_as_audio(text, ws):
    try:
    # Convert the GPT response to speech (TTS)
    tts = gTTS(text, lang='en')
    audio_fp = BytesIO()
    tts.write_to_fp(audio_fp)
    audio_fp.seek(0)

    # Convert the TTS audio to the required format for Twilio
    audio_segment = AudioSegment.from_mp3(audio_fp)
    audio_segment = audio_segment.set_frame_rate(8000).set_channels(1).set_sample_width(1)
    audio_data = BytesIO()
    audio_segment.export(audio_data, format="wav", codec="pcm_mulaw")
    audio_data = audio_data.getvalue()

    # Split and send the audio data in chunks
    CHUNK_SIZE = 1024 # Adjust chunk size if necessary
    for i in range(0, len(audio_data), CHUNK_SIZE):
    chunk = audio_data[i:i + CHUNK_SIZE]
    audio_b64 = base64.b64encode(chunk).decode('utf-8')
    message = json.dumps({
    "event": "media",
    "media": {
    "payload": audio_b64
    }
    })
    ws.send(message)
    # sleep(0.1) # Small delay to prevent overwhelming the WebSocket connection

    print("Audio sent successfully")

    except Exception as e:
    print(f"Error sending audio: {e}")

    def send_static_audio(ws):
    try:
    # Load a static audio file (in PCM mu-law format)
    with open("tts_output4.wav", "rb") as f:
    audio_data = f.read()

    audio_b64 = base64.b64encode(audio_data).decode('utf-8')
    message = json.dumps({
    "event": "media",
    "media": {
    "payload": audio_b64
    }
    })
    ws.send(message)
    # sleep(0.1) # Small delay between chunks

    print("Static audio sent successfully")
    except Exception as e:
    print(f"Error sending static audio: {e}")


    def convert_text_to_speech(text, save_locally=True):
    print(f"Converting text to speech: {text}")
    try:
    tts = gTTS(text, lang='en')
    audio_fp = BytesIO()
    tts.write_to_fp(audio_fp)
    audio_fp.seek(0)
    audio_data = audio_fp.read()
    print(f"TTS conversion complete, audio data length: {len(audio_data)}")

    if save_locally:
    with open("tts_output.mp3", "wb") as f:
    f.write(audio_data)
    print("TTS output saved locally as tts_output.mp3")

    return audio_data
    except Exception as e:
    print(f"Error in TTS conversion: {e}")
    return None


    def convert_audio_for_twilio(audio_data):
    """Convert audio to 8-bit mu-law audio at 8kHz."""
    audio = AudioSegment.from_mp3(BytesIO(audio_data))
    audio = audio.set_frame_rate(8000).set_channels(1)
    buffer = BytesIO()
    audio.export(buffer, format="wav", codec="pcm_mulaw")
    return buffer.getvalue()

    def send_audio_to_twilio(audio_data, ws):
    """Send the audio data to Twilio via WebSocket."""
    if ws.closed:
    print("WebSocket is closed. Cannot send audio.")
    return
    audio_b64 = base64.b64encode(audio_data).decode('utf-8')
    try:
    message = json.dumps({
    "event": "media",
    "media": {
    "payload": audio_b64
    }
    })
    print(f"Sending audio message of length: {len(message)}")
    ws.send(message)
    print("Audio sent successfully")
    except Exception as e:
    print(f"Error sending audio: {e}")


    @sockets.route("/")
    def transcript(ws):
    print("WS connection opened")
    bridge = SpeechClientBridge(streaming_config, lambda response: on_transcription_response(response, ws))
    t = threading.Thread(target=bridge.start)
    t.start()

    while not ws.closed:
    message = ws.receive()
    if message is None:
    bridge.add_request(None)
    bridge.terminate()
    break

    data = json.loads(message)
    if data["event"] in ("connected", "start"):
    print(f"Media WS: Received event '{data['event']}': {message}")
    continue
    if data["event"] == "media":
    media = data["media"]
    chunk = base64.b64decode(media["payload"])
    bridge.add_request(chunk)
    if data["event"] == "stop":
    print(f"Media WS: Received event 'stop': {message}")
    print("Stopping...")
    break

    bridge.terminate()
    print("WS connection closed")


    if __name__ == "__main__":
    from gevent import pywsgi
    from geventwebsocket.handler import WebSocketHandler

    server = pywsgi.WSGIServer(
    ("", HTTP_SERVER_PORT), app, handler_class=WebSocketHandler
    )
    print("Server listening on: http://localhost:" + str(HTTP_SERVER_PORT))
    server.serve_forever()


    and just for a reference this is my streams.xml file:

    <?xml version="1.0" encoding="UTF-8"?>
    <Response>
    <Connect>
    <Stream url="wss://my-grok-app-link.ngrok-free.app/"></Stream>
    </Connect>
    <Pause length="3600"/>
    </Response>


    Any code help is appreciated :)

    Continue reading...

Compartilhe esta Página