Replace audioop for python 3.13+
This commit is contained in:
@@ -38,6 +38,7 @@ Website = "https://pipecat.ai"
|
|||||||
[project.optional-dependencies]
|
[project.optional-dependencies]
|
||||||
anthropic = [ "anthropic~=0.40.0" ]
|
anthropic = [ "anthropic~=0.40.0" ]
|
||||||
assemblyai = [ "assemblyai~=0.34.0" ]
|
assemblyai = [ "assemblyai~=0.34.0" ]
|
||||||
|
audio = [ "soundfile~=0.12.1" ]
|
||||||
aws = [ "boto3~=1.35.27" ]
|
aws = [ "boto3~=1.35.27" ]
|
||||||
azure = [ "azure-cognitiveservices-speech~=1.41.1", "openai~=1.57.2" ]
|
azure = [ "azure-cognitiveservices-speech~=1.41.1", "openai~=1.57.2" ]
|
||||||
canonical = [ "aiofiles~=24.1.0" ]
|
canonical = [ "aiofiles~=24.1.0" ]
|
||||||
|
|||||||
@@ -4,7 +4,8 @@
|
|||||||
# SPDX-License-Identifier: BSD 2-Clause License
|
# SPDX-License-Identifier: BSD 2-Clause License
|
||||||
#
|
#
|
||||||
|
|
||||||
import audioop
|
import sys
|
||||||
|
|
||||||
import numpy as np
|
import numpy as np
|
||||||
import pyloudnorm as pyln
|
import pyloudnorm as pyln
|
||||||
import resampy
|
import resampy
|
||||||
@@ -74,21 +75,50 @@ def exp_smoothing(value: float, prev_value: float, factor: float) -> float:
|
|||||||
return prev_value + factor * (value - prev_value)
|
return prev_value + factor * (value - prev_value)
|
||||||
|
|
||||||
|
|
||||||
def ulaw_to_pcm(ulaw_bytes: bytes, in_sample_rate: int, out_sample_rate: int):
|
if sys.version_info >= (3, 13):
|
||||||
# Convert μ-law to PCM
|
try:
|
||||||
in_pcm_bytes = audioop.ulaw2lin(ulaw_bytes, 2)
|
import io
|
||||||
|
|
||||||
# Resample
|
import soundfile as sf
|
||||||
out_pcm_bytes = audioop.ratecv(in_pcm_bytes, 2, 1, in_sample_rate, out_sample_rate, None)[0]
|
|
||||||
|
|
||||||
return out_pcm_bytes
|
def ulaw_to_pcm(ulaw_bytes: bytes, in_sample_rate: int, out_sample_rate: int) -> bytes:
|
||||||
|
with io.BytesIO(ulaw_bytes) as buf:
|
||||||
|
data, _ = sf.read(
|
||||||
|
buf, channels=1, samplerate=in_sample_rate, format="RAW", subtype="ULAW"
|
||||||
|
)
|
||||||
|
if in_sample_rate != out_sample_rate:
|
||||||
|
data = resampy.resample(data, in_sample_rate, out_sample_rate)
|
||||||
|
return (data * 32767).astype(np.int16).tobytes()
|
||||||
|
|
||||||
|
def pcm_to_ulaw(pcm_bytes: bytes, in_sample_rate: int, out_sample_rate: int) -> bytes:
|
||||||
|
data = np.frombuffer(pcm_bytes, dtype=np.int16).astype(np.float32) / 32767.0
|
||||||
|
if in_sample_rate != out_sample_rate:
|
||||||
|
data = resampy.resample(data, in_sample_rate, out_sample_rate)
|
||||||
|
with io.BytesIO() as buf:
|
||||||
|
sf.write(buf, data, out_sample_rate, format="RAW", subtype="ULAW")
|
||||||
|
return buf.getvalue()
|
||||||
|
except ImportError:
|
||||||
|
raise ImportError(
|
||||||
|
"For Python 3.13+, please install soundfile: pip install pipecat-ai[audio]"
|
||||||
|
)
|
||||||
|
|
||||||
def pcm_to_ulaw(pcm_bytes: bytes, in_sample_rate: int, out_sample_rate: int):
|
else:
|
||||||
# Resample
|
import audioop
|
||||||
in_pcm_bytes = audioop.ratecv(pcm_bytes, 2, 1, in_sample_rate, out_sample_rate, None)[0]
|
|
||||||
|
|
||||||
# Convert PCM to μ-law
|
def ulaw_to_pcm(ulaw_bytes: bytes, in_sample_rate: int, out_sample_rate: int):
|
||||||
ulaw_bytes = audioop.lin2ulaw(in_pcm_bytes, 2)
|
# Convert μ-law to PCM
|
||||||
|
in_pcm_bytes = audioop.ulaw2lin(ulaw_bytes, 2)
|
||||||
|
|
||||||
return ulaw_bytes
|
# Resample
|
||||||
|
out_pcm_bytes = audioop.ratecv(in_pcm_bytes, 2, 1, in_sample_rate, out_sample_rate, None)[0]
|
||||||
|
|
||||||
|
return out_pcm_bytes
|
||||||
|
|
||||||
|
def pcm_to_ulaw(pcm_bytes: bytes, in_sample_rate: int, out_sample_rate: int):
|
||||||
|
# Resample
|
||||||
|
in_pcm_bytes = audioop.ratecv(pcm_bytes, 2, 1, in_sample_rate, out_sample_rate, None)[0]
|
||||||
|
|
||||||
|
# Convert PCM to μ-law
|
||||||
|
ulaw_bytes = audioop.lin2ulaw(in_pcm_bytes, 2)
|
||||||
|
|
||||||
|
return ulaw_bytes
|
||||||
|
|||||||
@@ -6,13 +6,24 @@
|
|||||||
|
|
||||||
import base64
|
import base64
|
||||||
import json
|
import json
|
||||||
|
import sys
|
||||||
|
|
||||||
from pydantic import BaseModel
|
from pydantic import BaseModel
|
||||||
|
|
||||||
from pipecat.audio.utils import ulaw_to_pcm, pcm_to_ulaw
|
|
||||||
from pipecat.frames.frames import AudioRawFrame, Frame, InputAudioRawFrame, StartInterruptionFrame
|
from pipecat.frames.frames import AudioRawFrame, Frame, InputAudioRawFrame, StartInterruptionFrame
|
||||||
from pipecat.serializers.base_serializer import FrameSerializer, FrameSerializerType
|
from pipecat.serializers.base_serializer import FrameSerializer, FrameSerializerType
|
||||||
|
|
||||||
|
if sys.version_info >= (3, 13):
|
||||||
|
try:
|
||||||
|
from pipecat.audio.utils import pcm_to_ulaw, ulaw_to_pcm
|
||||||
|
except ImportError:
|
||||||
|
raise ImportError(
|
||||||
|
"Audio processing support required for TwilioFrameSerializer. "
|
||||||
|
"Please install with: pip install pipecat-ai[audio]"
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
from pipecat.audio.utils import pcm_to_ulaw, ulaw_to_pcm
|
||||||
|
|
||||||
|
|
||||||
class TwilioFrameSerializer(FrameSerializer):
|
class TwilioFrameSerializer(FrameSerializer):
|
||||||
class InputParams(BaseModel):
|
class InputParams(BaseModel):
|
||||||
|
|||||||
Reference in New Issue
Block a user