[Python] Not able to send stream from Asterisk audio socket to python audio socket server

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

  1. Stack

    Stack Membro Participativo

    I added this in asterisk conf

    same => n(newIvr),Log(NOTICE, Start New IVR POC flow)
    same => n,AudioSocket(${UUID()},172.25.25.150:1579)
    same => n,Playback(goodbye)
    same => n,Hangup()


    and my python audio socket server

    import sys
    import threading
    import time
    import json
    import os
    import audioop
    import wave
    import logging
    import signal
    from google.cloud import speech
    from google.protobuf.json_format import MessageToDict
    from audiosocket import Audiosocket

    logging.basicConfig(
    stream=sys.stdout,
    format='%(asctime)s [%(levelname)s] %(message)s',
    level=logging.INFO
    )
    log = logging.getLogger()

    # Configuration
    port = 1579
    audiosocket = Audiosocket(("0.0.0.0", port))
    speech_client = speech.SpeechClient()

    def graceful_shutdown(signum, frame):
    log.info(" Shutting down service due to signal...")
    sys.exit(0)

    signal.signal(signal.SIGTERM, graceful_shutdown)
    signal.signal(signal.SIGINT, graceful_shutdown)

    def save_raw_to_wav(call_uuid, raw_filename, wav_filename, sample_rate=8000):
    try:
    file_size = os.path.getsize(raw_filename)
    if file_size == 0:
    log.warning(f"[{call_uuid}] ⚠️ Raw file is empty, skipping WAV conversion")
    return

    with open(raw_filename, 'rb') as raw_file:
    raw_data = raw_file.read()

    duration_seconds = len(raw_data) / (sample_rate * 2)

    if duration_seconds < 0.1:
    log.warning(f"[{call_uuid}] ⚠️ Duration < 0.1s ({duration_seconds:.2f}s), skipping WAV conversion")
    return

    with wave.open(wav_filename, 'wb') as wav_file:
    wav_file.setnchannels(1)
    wav_file.setsampwidth(2)
    wav_file.setframerate(sample_rate)
    wav_file.writeframes(raw_data)

    log.info(f"[{call_uuid}] ✅ WAV file saved: {wav_filename} ({duration_seconds:.2f} sec)")
    except Exception as e:
    log.error(f"[{call_uuid}] ❌ Error converting raw to WAV: {e}")

    def handle_call(conn):
    call_uuid = conn.uuid
    log.info(f"[{call_uuid}] Incoming connection with UUID: {call_uuid}")

    audio_filename = f"{call_uuid}.raw"
    try:
    audio_file = open(audio_filename, "wb")
    log.info(f"[{call_uuid}] Opened local raw audio file for writing: {audio_filename}")
    except Exception as e:
    log.error(f"[{call_uuid}] ❌ Failed to open file {audio_filename}: {e}")
    audio_file = None

    def audio_generator():
    total_bytes = 0
    last_data_time = time.time()
    start_time = time.time()

    while conn.connected:
    data = conn.read()
    now = time.time()

    if not data:
    if now - last_data_time > 2:
    log.info(f"[{call_uuid}] Detected silence > 2 seconds, stopping stream")
    break
    time.sleep(0.05)
    continue

    last_data_time = now
    total_bytes += len(data)

    # Decode G.711 μ-law (8-bit) to 16-bit PCM
    # decoded_data = audioop.ulaw2lin(data, 2)
    decoded_data = data

    if audio_file:
    try:
    audio_file.write(decoded_data)
    except Exception as file_write_err:
    log.error(f"[{call_uuid}] ❌ Error writing audio data: {file_write_err}")

    log.info(f"[{call_uuid}] Received {len(data)} bytes (total: {total_bytes})")
    yield speech.StreamingRecognizeRequest(audio_content=decoded_data)

    if now - start_time > 8:
    log.info(f"[{call_uuid}] ⏰ Stream duration > 8s, stopping stream")
    break

    log.info(f"[{call_uuid}] Finished streaming audio. Total bytes: {total_bytes}")

    config = speech.RecognitionConfig(
    encoding=speech.RecognitionConfig.AudioEncoding.LINEAR16,
    sample_rate_hertz=8000,
    language_code="en-US",
    )

    streaming_config = speech.StreamingRecognitionConfig(
    config=config,
    interim_results=False # Only final results
    )

    try:
    log.info(f"[{call_uuid}] Starting Google Speech API streaming")

    responses = speech_client.streaming_recognize(
    config=streaming_config,
    requests=audio_generator()
    )

    responses_dict = [MessageToDict(responses) for response in responses]
    log.info(f"[{call_uuid}] ️ Full responses object:\n{json.dumps(responses_dict, indent=2)}")

    # ✅ Process responses here directly without exhausting the generator
    for response in responses:
    log.info(f"[{call_uuid}] ️ Got response from Google")
    for result in response.results:
    if result.is_final:
    transcript = result.alternatives[0].transcript
    log.info(f"[{call_uuid}] Final transcript: {transcript}")
    print(f"[{call_uuid}] ️ Final transcript: {transcript}")

    except Exception as e:
    log.error(f"[{call_uuid}] ❌ Google Speech API error: {e}")

    finally:
    if audio_file:
    try:
    audio_file.close()
    log.info(f"[{call_uuid}] Closed raw audio file: {audio_filename}")
    save_raw_to_wav(call_uuid, audio_filename, f"{call_uuid}.wav")
    except Exception as e:
    log.error(f"[{call_uuid}] ❌ Error closing file: {e}")

    if conn:
    try:
    conn.hangup()
    conn.close()
    log.info(f"[{call_uuid}] ✅ Connection closed")
    except Exception as close_err:
    log.error(f"[{call_uuid}] ❌ Error closing connection: {close_err}")

    log.info(" Server started and listening for incoming connections")
    while True:
    try:
    conn = audiosocket.listen()
    time.sleep(0.3)
    log.info(" Received connection from audiosocket")
    # print("conn.uuid:", getattr(conn, "uuid", "no uuid"))

    threading.Thread(target=handle_call, args=(conn,), daemon=True).start()
    except Exception as listener_err:
    log.error(f"❌ Exception in main listener loop: {listener_err}")
    import traceback
    traceback.print_exc()


    and the logs

    2025-06-06 12:53:53,404 [INFO] Server started and listening for incoming connections
    2025-06-06 12:53:58,886 [INFO] Received connection from audiosocket
    2025-06-06 12:53:58,887 [INFO] [40325ec25efd4bd3805f53576e581d13] Incoming connection with UUID: 40325ec25efd4bd3805f53576e581d13
    2025-06-06 12:53:58,951 [INFO] [40325ec25efd4bd3805f53576e581d13] Opened local raw audio file for writing: 40325ec25efd4bd3805f53576e581d13.raw
    2025-06-06 12:53:58,952 [INFO] [40325ec25efd4bd3805f53576e581d13] Starting Google Speech API streaming
    2025-06-06 12:53:59,323 [INFO] [40325ec25efd4bd3805f53576e581d13] Received 320 bytes (total: 320)
    2025-06-06 12:53:59,527 [INFO] [40325ec25efd4bd3805f53576e581d13] Received 320 bytes (total: 640)
    2025-06-06 12:53:59,729 [INFO] [40325ec25efd4bd3805f53576e581d13] Received 320 bytes (total: 960)
    2025-06-06 12:53:59,931 [INFO] [40325ec25efd4bd3805f53576e581d13] Received 320 bytes (total: 1280)
    2025-06-06 12:54:00,132 [INFO] [40325ec25efd4bd3805f53576e581d13] Received 320 bytes (total: 1600)
    2025-06-06 12:54:00,334 [INFO] [40325ec25efd4bd3805f53576e581d13] Received 320 bytes (total: 1920)
    2025-06-06 12:54:00,536 [INFO] [40325ec25efd4bd3805f53576e581d13] Received 320 bytes (total: 2240)
    2025-06-06 12:54:00,737 [INFO] [40325ec25efd4bd3805f53576e581d13] Received 320 bytes (total: 2560)
    2025-06-06 12:54:00,939 [INFO] [40325ec25efd4bd3805f53576e581d13] Received 320 bytes (total: 2880)
    2025-06-06 12:54:01,141 [INFO] [40325ec25efd4bd3805f53576e581d13] Received 320 bytes (total: 3200)
    2025-06-06 12:54:01,348 [INFO] [40325ec25efd4bd3805f53576e581d13] Received 320 bytes (total: 3520)
    2025-06-06 12:54:01,550 [INFO] [40325ec25efd4bd3805f53576e581d13] Received 320 bytes (total: 3840)
    2025-06-06 12:54:01,753 [INFO] [40325ec25efd4bd3805f53576e581d13] Received 320 bytes (total: 4160)
    2025-06-06 12:54:01,959 [INFO] [40325ec25efd4bd3805f53576e581d13] Received 320 bytes (total: 4480)
    2025-06-06 12:54:02,164 [INFO] [40325ec25efd4bd3805f53576e581d13] Received 320 bytes (total: 4800)
    2025-06-06 12:54:02,369 [INFO] [40325ec25efd4bd3805f53576e581d13] Received 320 bytes (total: 5120)
    2025-06-06 12:54:02,572 [INFO] [40325ec25efd4bd3805f53576e581d13] Received 320 bytes (total: 5440)
    2025-06-06 12:54:02,778 [INFO] [40325ec25efd4bd3805f53576e581d13] Received 320 bytes (total: 5760)
    2025-06-06 12:54:02,985 [INFO] [40325ec25efd4bd3805f53576e581d13] Received 320 bytes (total: 6080)
    2025-06-06 12:54:03,194 [INFO] [40325ec25efd4bd3805f53576e581d13] Received 320 bytes (total: 6400)
    2025-06-06 12:54:03,404 [INFO] [40325ec25efd4bd3805f53576e581d13] Received 320 bytes (total: 6720)
    2025-06-06 12:54:03,606 [INFO] [40325ec25efd4bd3805f53576e581d13] Received 320 bytes (total: 7040)
    2025-06-06 12:54:03,808 [INFO] [40325ec25efd4bd3805f53576e581d13] Received 320 bytes (total: 7360)
    2025-06-06 12:54:04,011 [INFO] [40325ec25efd4bd3805f53576e581d13] Received 320 bytes (total: 7680)
    2025-06-06 12:54:04,213 [INFO] [40325ec25efd4bd3805f53576e581d13] Received 320 bytes (total: 8000)
    2025-06-06 12:54:04,416 [INFO] [40325ec25efd4bd3805f53576e581d13] Received 320 bytes (total: 8320)
    2025-06-06 12:54:04,619 [INFO] [40325ec25efd4bd3805f53576e581d13] Received 320 bytes (total: 8640)
    2025-06-06 12:54:04,821 [INFO] [40325ec25efd4bd3805f53576e581d13] Received 320 bytes (total: 8960)
    2025-06-06 12:54:05,044 [INFO] [40325ec25efd4bd3805f53576e581d13] Received 320 bytes (total: 9280)
    2025-06-06 12:54:05,246 [INFO] [40325ec25efd4bd3805f53576e581d13] Received 320 bytes (total: 9600)
    2025-06-06 12:54:05,447 [INFO] [40325ec25efd4bd3805f53576e581d13] Received 320 bytes (total: 9920)
    2025-06-06 12:54:05,649 [INFO] [40325ec25efd4bd3805f53576e581d13] Received 320 bytes (total: 10240)
    2025-06-06 12:54:05,851 [INFO] [40325ec25efd4bd3805f53576e581d13] Received 320 bytes (total: 10560)
    2025-06-06 12:54:06,053 [INFO] [40325ec25efd4bd3805f53576e581d13] Received 320 bytes (total: 10880)
    2025-06-06 12:54:06,255 [INFO] [40325ec25efd4bd3805f53576e581d13] Received 320 bytes (total: 11200)
    2025-06-06 12:54:06,458 [INFO] [40325ec25efd4bd3805f53576e581d13] Received 320 bytes (total: 11520)
    2025-06-06 12:54:06,659 [INFO] [40325ec25efd4bd3805f53576e581d13] Received 320 bytes (total: 11840)
    2025-06-06 12:54:06,861 [INFO] [40325ec25efd4bd3805f53576e581d13] Received 320 bytes (total: 12160)
    2025-06-06 12:54:07,063 [INFO] [40325ec25efd4bd3805f53576e581d13] Received 320 bytes (total: 12480)
    2025-06-06 12:54:07,265 [INFO] [40325ec25efd4bd3805f53576e581d13] Received 320 bytes (total: 12800)
    2025-06-06 12:54:07,265 [INFO] [40325ec25efd4bd3805f53576e581d13] ⏰ Stream duration > 8s, stopping stream
    2025-06-06 12:54:07,266 [INFO] [40325ec25efd4bd3805f53576e581d13] Finished streaming audio. Total bytes: 12800
    2025-06-06 12:54:07,483 [INFO] [40325ec25efd4bd3805f53576e581d13] ️ Full responses object:
    []
    2025-06-06 12:54:07,490 [INFO] [40325ec25efd4bd3805f53576e581d13] Closed raw audio file: 40325ec25efd4bd3805f53576e581d13.raw
    2025-06-06 12:54:07,539 [INFO] [40325ec25efd4bd3805f53576e581d13] ✅ WAV file saved: 40325ec25efd4bd3805f53576e581d13.wav (0.80 sec)
    2025-06-06 12:54:25,390 [ERROR] [40325ec25efd4bd3805f53576e581d13] ❌ Error closing connection: [Errno 9] Bad file descriptor


    the problem is i am not recieving any text response after streaming to google speech stream apis.

    Additionally i tried to create files, it creats raw and wav file. both are 13kb every time, and 0 sec in wav. I tried online by converting raw to wav , but during conversion it shows invalid raw file.

    Even in the pjsip.conf in asterisk i tried allow= slin,ulaw, alaw but not working

    in my python code i tried with

    decoded_data = audioop.ulaw2lin(data, 2)


    still same issue

    i am using this audio repo https://github.com/silentindark/audiosocket_server/blob/master/README.md

    any help on this?

    Continue reading...

Compartilhe esta Página