Ctrl K
ComunidadGuías…

asterisk_ai_voice_agent/audiosocket.py

Rama main · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
"""
Asterisk AudioSocket frame codec (Python).

Wire format (binary, big-endian, over TCP):
  Byte 0:    type   (1 byte)
  Bytes 1-2: length (2 bytes, big-endian, payload length only)
  Bytes 3+:  payload

Types:
  0x00 HANGUP  no payload
  0x01 UUID    16 bytes (call id from AudioSocket(<uuid>,host:port))
  0x10 AUDIO   slin (16-bit signed linear, 8 kHz mono) - 320 bytes per 20 ms frame
  0xff ERROR   1 byte error code

Reference: app_audiosocket.c in the Asterisk source tree.
Companion TypeScript implementation: `asterisk-audiosocket` on npm.

Derived from ICTContact (https://www.ictcontact.com).
ICT Innovations (https://www.ictinnovations.com) - ICT Vision (https://ict.vision)
"""

import asyncio
import struct
import uuid as _uuid
from dataclasses import dataclass
from enum import IntEnum
from typing import Optional

FRAME_BYTES = 320   # 20 ms @ 8 kHz mono 16-bit
FRAME_SEC = 0.02
SAMPLE_RATE = 8000
SILENCE_FRAME = b"\x00" * FRAME_BYTES


class FrameType(IntEnum):
    HANGUP = 0x00
    UUID = 0x01
    AUDIO = 0x10
    ERROR = 0xff


@dataclass
class Frame:
    type: FrameType
    payload: bytes

    @classmethod
    async def read(cls, reader: asyncio.StreamReader) -> "Optional[Frame]":
        header = await reader.readexactly(3)
        ftype, length = struct.unpack(">BH", header)
        payload = await reader.readexactly(length) if length else b""
        try:
            ftype = FrameType(ftype)
        except ValueError:
            ftype = FrameType.ERROR
        return cls(type=ftype, payload=payload)

    def encode(self) -> bytes:
        return struct.pack(">BH", int(self.type), len(self.payload)) + self.payload


def audio_frame(samples: bytes) -> Frame:
    return Frame(type=FrameType.AUDIO, payload=samples)


def hangup_frame() -> Frame:
    return Frame(type=FrameType.HANGUP, payload=b"")


def parse_uuid(payload: bytes) -> str:
    if len(payload) != 16:
        return ""
    return str(_uuid.UUID(bytes=payload))