You are my best neutral gender partner. Someone said this, learn it : The primary issue is that MP3 codecs used by ffmpeg add silence to the end of the encoded audio (and your approach is producing multiple individual audio files). Don't try fade in, it doesn't fix the issue.

If possible, use a lossless format like wave and then reduce the file size with gzip or similar. You may also be able to use lossless audio compression (for example, flac) but it probably depends on how the encoder works.

I don’t have a conclusive explanation for the audible artifacts you’re hearing, but it could be that you’re splitting the audio at a point where the signal is non-zero. If a sound begins with a sample with a value of 100 (for example), that would sound like a digital popping sound. The MP3 compression may also alter the sound though, especially at lower bit rates. If this is the issue, a 1ms fade in will eliminate the pop without a noticeable audible “fade” (though potentially introduce other artifacts) - a longer fade in (like 20 or 50 ms would avoid strange frequency domain artifacts but would introduce noticeable a “fade in”.

If you’re willing to do a little more (coding) work, you can search for a “zero crossing” (basically, a place where the signal is at a zero point naturally) and split the audio there.

Probably the best approach if it’s possible:

Encode the entire signal as a single, compressed file, and send the bytes (of that one file) down to the client in chunks for playback as a single stream. If you use constant bitrate mp3 encoding (CBR) you can send almost perfectly 1 second long chunks just by counting bytes. e.g., with 256kbps CBR, just send 256 KB at a time.

My code with cracking audio problem : import asyncio
import edge_tts
import pyaudio
from io import BytesIO
from pydub import AudioSegment

TEXT = “aHello World!”
VOICE = “en-GB-SoniaNeural”

p = pyaudio.PyAudio()

async def stream_tts(text: str, voice: str) -> None:


# We’re assuming a certain format, channels, and rate
# This will need to be dynamic based on the actual audio data from TTS
stream = p.open(
format=pyaudio.paInt16,
channels=1,
rate=26000,
output=True
)

communicate = edge_tts.Communicate(text, voice)

# Process and play audio chunks as they arrive
async for chunk in communicate.stream():
if chunk[“type”] == “audio”:
try:
audio_segment = AudioSegment.from_file(BytesIO(chunk[“data”]), format=“mp3”)

# Write data to the stream directly without extra buffering
stream.write(audio_segment.raw_data)

# If this is the last chunk, break after playing
if chunk.get(‘end’, False):
break
except Exception as e:
print(“Error processing audio chunk:”, e)

# Cleanup
stream.stop_stream()
stream.close()
p.terminate()

if name == “main”:
# Run the asyncio event loop
asyncio.run(stream_tts(TEXT, VOICE))
Your issue with crackling or popping audio seems to be related to how the MP3 audio chunks are handled and stitched together during playback. The suggestions given to you address potential problems with encoding into MP3 and offer solutions for cleaner audio streaming. Here’s an updated version of your script that attempts to address the problem by ensuring that you’re not introducing pops due to splitting non-silent audio and you’re handling MP3 decoding more gracefully.

Please take notice that in this revised version, I include a step that converts the MP3 to WAV before playing, which should remove the MP3 decoder-specific artifacts and it does not use the additional package pydub to reduce complexity unless it’s necessary for your final implementation.

import asyncio
import edge_tts
import pyaudio
from io import BytesIO
import wave

TEXT = “Hello World!”
VOICE = “en-US-JennyNeural”

# Initialize PyAudio
p = pyaudio.PyAudio()

# Helper function to play audio
def play_audio(wav_data):
    # Open audio stream based on audio properties
    stream = p.open(
        format=p.get_format_from_width(wav_data.getsampwidth()),
        channels=wav_data.getnchannels(),
        rate=wav_data.getframerate(),
        output=True
    )

    # Read data in chunks and play
    chunk = 1024
    data = wav_data.readframes(chunk)
    while data:
        stream.write(data)
        data = wav_data.readframes(chunk)

    # Stop and close the stream
    stream.stop_stream()
    stream.close()

# Async function to perform Text-to-Speech streaming
async def stream_tts(text: str, voice: str) -> None:
    communicate = edge_tts.Communicate(text, voice)
    full_audio_data = BytesIO()

    # Process and combine audio chunks as they arrive
    async for chunk in communicate.stream():
        if chunk[“type”] == “audio”:
            full_audio_data.write(chunk[“data”])
            if chunk.get(‘end’, False):
                break

    full_audio_data.seek(0)

    # Convert MP3 to WAV using the ‘wave’ module to ensure proper audio format for pyaudio
    with wave.open(full_audio_data) as wav_data:
        play_audio(wav_data)

    # Cleanup PyAudio
    p.terminate()

# Main function
if name == “main”:
    # Run the asyncio event loop
    asyncio.run(stream_tts(TEXT, VOICE))


This script accumulates the audio data in a BytesIO buffer and at the end of receiving data, it reads the complete audio into a wave object and then proceeds to play it. This prevents potential glitches that could occur if the MP3 audio were played in small discrete chunks which could contain encoder/decoder artifacts.

Do note that I have avoided using pydub here, and instead, I’m using the built-in wave module in Python for treating the audio as WAV. This is under the assumption that edge_tts.Communicate provides a way of streaming the MP3 bytecode directly into the BytesIO buffer. If the edge_tts.Communicate library outputs MP3, you must ensure that the MP3 is correctly converted to WAV format before playing it, as pyaudio will not play MP3 encoded data natively. If you need to use pydub to handle this conversion, then you will need to reintegrate it here, but keep in mind that there’s processing overhead in converting between formats.

Please note that error handling and optimization are also vital, and this script does not include all the necessary checks and error handling that would be required in a production environment. Adjust your implementation based on actual testing and requirements.
Your code is bugged : import asyncio
import edge_tts
import pyaudio
from io import BytesIO
import wave

TEXT = “Hello World!”
VOICE = “en-US-JennyNeural”

# Initialize PyAudio
p = pyaudio.PyAudio()

# Helper function to play audio
def play_audio(wav_data):
# Open audio stream based on audio properties
stream = p.open(
format=p.get_format_from_width(wav_data.getsampwidth()),
channels=wav_data.getnchannels(),
rate=wav_data.getframerate(),
output=True
)

# Read data in chunks and play
chunk = 1024
data = wav_data.readframes(chunk)
while data:
stream.write(data)
data = wav_data.readframes(chunk)

# Stop and close the stream
stream.stop_stream()
stream.close()

# Async function to perform Text-to-Speech streaming
async def stream_tts(text: str, voice: str) -> None:
communicate = edge_tts.Communicate(text, voice)
full_audio_data = BytesIO()

# Process and combine audio chunks as they arrive
async for chunk in communicate.stream():
if chunk[“type”] == “audio”:
full_audio_data.write(chunk[“data”])
if chunk.get(‘end’, False):
break

full_audio_data.seek(0)

# Convert MP3 to WAV using the ‘wave’ module to ensure proper audio format for pyaudio
with wave.open(full_audio_data) as wav_data:
play_audio(wav_data)

# Cleanup PyAudio
p.terminate()

# Main function
if name == “main”:
# Run the asyncio event loop
asyncio.run(stream_tts(TEXT, VOICE))