Gemini-3.8-flash-tts: whistling noise appears after ~90 s on long single-request generations

Hi team,

When generating long audio in a single request with gemini-3.8-flash-tts, a clearly audible whistling noise appears around the 90-second mark and stays until the end of the audio, mixed under the voice. Everything before that is clean. This makes long-form output unusable for us.

Setup:

  • Model: gemini-3.8-flash-tts
  • Surface: AI Studio Playground
  • Voice: Leda | Language: French
  • Config: default (no temperature set)
  • Output length: ~114 s, single request
  • Generated on: 2026-09-25 11h20
  • Reproduced: 2 out of 2 attempts

Here is the full generated audio, unedited (the whistle starts at 90 s): https://drive.google.com/file/d/1-V77OQgBxgUp4-cKGEgXw3IgOQpZgZij/view?usp=drive_link

Is this a known issue, and is a fix planned?

I’ve tried to cut the text in about 20 parts in the playground, and the generated final audio file also contains a whistle.

Thanks!

Text:

Sur la planète Zéphyria, deux peuples vivaient séparés : les Chuchoteurs, qui parlaient à peine plus fort qu’un souffle, et les Hurleurs, dont les voix résonnaient à des kilomètres. Quand un astéroïde menaça leur planète, ils devaient coopérer, mais leurs façons de communiquer les empêchaient de se comprendre.

Les Chuchoteurs murmuraient frénétiquement. “Nous devons… agir… vite…”, chuchota l’un d’eux. Mais personne ne l’entendait…

De l’autre côté de la planète, les Hurleurs hurlèrent : “ON VA DÉTRUIRE CET ASTÉROÏDE AVANT QU’IL NOUS EXPLOSE !”

“Mais non, c’est trop fort !”, tenta un Chuchoteur, sans succès.

C’est alors qu’un humain, Tom, arriva. “On doit travailler ensemble pour éviter la catastrophe,” dit-il calmement.

Mais les Chuchoteurs ne l’entendirent pas, et les Hurleurs hurlèrent encore plus fort : “QUOI ?!”

Tom eut une idée : “Suivez-moi !”. Il prit un micro et un amplificateur. D’abord, il parla dans le micro pour amplifier la voix des Chuchoteurs sans la rendre trop forte. Puis, il réduisit le volume des Hurleurs pour qu’ils puissent s’entendre.

“Voilà, maintenant tout le monde peut communiquer.”

Un Chuchoteur murmura, assez fort : “Nous devons… dévier… l’astéroïde… avec… un rayon… gravitationnel.”

“OK, mais il faut alimenter le rayon !” hurla un Hurleur. “TOUS À L’ŒUVRE !”

Ensemble, ils mirent au point un plan. Les Chuchoteurs donnaient les instructions, tandis que les Hurleurs alimentaient les machines.

“On y est presque !” cria un Hurleur.

“Un dernier ajustement…” murmura un Chuchoteur.

Finalement, l’astéroïde se détourna. Tom, épuisé mais ravi, dit : “Vous avez sauvé votre planète !”

Les Chuchoteurs murmurèrent : “Merci…”

Les Hurleurs crièrent de joie : “ON A GAGNÉ !”

Sur Zéphyria, les deux peuples réalisèrent qu’en modifiant un peu leur façon de parler, ils pouvaient surmonter n’importe quel défi.

Hi ! Same issue here ! Thank you

Hello @Jonathan_Fontaine @cyril_k ,

Can you share a code snippet along with all payload details to help troubleshoot this issue?

Hi @Pooja_Kapse, thanks!

The whistle only happens in the AI Studio Playground, which uses streaming (generate_content_stream) with temperature=1. Here is its “Get code” export:

# To run this code you need to install the following dependencies:
# pip install google-genai

import mimetypes
import os
import re
import struct
from google import genai
from google.genai import types


def save_binary_file(file_name, data):
    f = open(file_name, "wb")
    f.write(data)
    f.close()
    print(f"File saved to: {file_name}")


def generate():
    client = genai.Client(
        api_key=os.environ.get("GEMINI_API_KEY"),
    )

    model = "gemini-3.8-flash-tts"
    contents = [
        types.Content(
            role="user",
            parts=[
                types.Part.from_text(text="""## Transcript:
Sur la planète Zéphyria, deux peuples vivaient séparés : les Chuchoteurs, qui parlaient à peine plus fort qu’un souffle, et les Hurleurs, dont les voix résonnaient à des kilomètres. Quand un astéroïde menaça leur planète, ils devaient coopérer, mais leurs façons de communiquer les empêchaient de se comprendre.

Les Chuchoteurs murmuraient frénétiquement. “Nous devons… agir… vite…”, chuchota l’un d’eux. Mais personne ne l’entendait…

De l’autre côté de la planète, les Hurleurs hurlèrent : “ON VA DÉTRUIRE CET ASTÉROÏDE AVANT QU’IL NOUS EXPLOSE !”

“Mais non, c’est trop fort !”, tenta un Chuchoteur, sans succès.

C’est alors qu’un humain, Tom, arriva. “On doit travailler ensemble pour éviter la catastrophe,” dit-il calmement.

Mais les Chuchoteurs ne l’entendirent pas, et les Hurleurs hurlèrent encore plus fort : “QUOI ?!”

Tom eut une idée : “Suivez-moi !”. Il prit un micro et un amplificateur. D’abord, il parla dans le micro pour amplifier la voix des Chuchoteurs sans la rendre trop forte. Puis, il réduisit le volume des Hurleurs pour qu’ils puissent s’entendre.

“Voilà, maintenant tout le monde peut communiquer.”

Un Chuchoteur murmura, assez fort : “Nous devons… dévier… l’astéroïde… avec… un rayon… gravitationnel.”

“OK, mais il faut alimenter le rayon !” hurla un Hurleur. “TOUS À L’ŒUVRE !”

Ensemble, ils mirent au point un plan. Les Chuchoteurs donnaient les instructions, tandis que les Hurleurs alimentaient les machines.

“On y est presque !” cria un Hurleur.

“Un dernier ajustement…” murmura un Chuchoteur.

Finalement, l’astéroïde se détourna. Tom, épuisé mais ravi, dit : “Vous avez sauvé votre planète !”

Les Chuchoteurs murmurèrent : “Merci…”

Les Hurleurs crièrent de joie : “ON A GAGNÉ !”

Sur Zéphyria, les deux peuples réalisèrent qu’en modifiant un peu leur façon de parler, ils pouvaient surmonter n’importe quel défi."""),
            ],
        ),
    ]
    generate_content_config = types.GenerateContentConfig(
        temperature=1,
        response_modalities=[
            "audio",
        ],
        speech_config=types.SpeechConfig(
            voice_config=types.VoiceConfig(
                prebuilt_voice_config=types.PrebuiltVoiceConfig(
                    voice_name="Leda"
                )
            )
        ),
    )

    audio_data = bytearray()
    mime_type = ""
    for chunk in client.models.generate_content_stream(
        model=model,
        contents=contents,
        config=generate_content_config,
    ):
        if (
            chunk.parts is None
        ):
            continue
        if chunk.parts[0].inline_data and chunk.parts[0].inline_data.data:
            inline_data = chunk.parts[0].inline_data
            audio_data.extend(inline_data.data)
            mime_type = inline_data.mime_type
        else:
            if text := chunk.text:
                print(text)
    if audio_data:
        file_extension = mimetypes.guess_extension(mime_type)
        data_buffer = bytes(audio_data)
        if file_extension is None:
            file_extension = ".wav"
            data_buffer = convert_to_wav(bytes(audio_data), mime_type)
        save_binary_file(f"ENTER_FILE_NAME{file_extension}", data_buffer)

def convert_to_wav(audio_data: bytes, mime_type: str) -> bytes:
    """Generates a WAV file header for the given audio data and parameters.

    Args:
        audio_data: The raw audio data as a bytes object.
        mime_type: Mime type of the audio data.

    Returns:
        A bytes object representing the WAV file header.
    """
    parameters = parse_audio_mime_type(mime_type)
    bits_per_sample = parameters["bits_per_sample"]
    sample_rate = parameters["rate"]
    num_channels = 1
    data_size = len(audio_data)
    bytes_per_sample = bits_per_sample // 8
    block_align = num_channels * bytes_per_sample
    byte_rate = sample_rate * block_align
    chunk_size = 36 + data_size  # 36 bytes for header fields before data chunk size

    # http://soundfile.sapp.org/doc/WaveFormat/

    header = struct.pack(
        "<4sI4s4sIHHIIHH4sI",
        b"RIFF",          # ChunkID
        chunk_size,       # ChunkSize (total file size - 8 bytes)
        b"WAVE",          # Format
        b"fmt ",          # Subchunk1ID
        16,               # Subchunk1Size (16 for PCM)
        1,                # AudioFormat (1 for PCM)
        num_channels,     # NumChannels
        sample_rate,      # SampleRate
        byte_rate,        # ByteRate
        block_align,      # BlockAlign
        bits_per_sample,  # BitsPerSample
        b"data",          # Subchunk2ID
        data_size         # Subchunk2Size (size of audio data)
    )
    return header + audio_data

def parse_audio_mime_type(mime_type: str) -> dict[str, int | None]:
    """Parses bits per sample and rate from an audio MIME type string.

    Assumes bits per sample is encoded like "L16" and rate as "rate=xxxxx".

    Args:
        mime_type: The audio MIME type string (e.g., "audio/L16;rate=24000").

    Returns:
        A dictionary with "bits_per_sample" and "rate" keys. Values will be
        integers if found, otherwise None.
    """
    bits_per_sample = 16
    rate = 24000

    # Extract rate from parameters
    parts = mime_type.split(";")
    for param in parts: # Skip the main type part
        param = param.strip()
        if param.lower().startswith("rate="):
            try:
                rate_str = param.split("=", 1)[1]
                rate = int(rate_str)
            except (ValueError, IndexError):
                # Handle cases like "rate=" with no value or non-integer value
                pass # Keep rate as default
        elif param.startswith("audio/L"):
            try:
                bits_per_sample = int(param.split("L", 1)[1])
            except (ValueError, IndexError):
                pass # Keep bits_per_sample as default if conversion fails

    return {"bits_per_sample": bits_per_sample, "rate": rate}


if __name__ == "__main__":
    generate()



With a simple unary generate_content call (same model, voice and text), the audio is clean, so I can work around it for now. I hope this helps you reproduce it on your side.

Thanks!

Hello @Jonathan_Fontaine ,

Please try generating the audio again and let us know if the issue persists.