Check the audio input first: WAV, raw PCM and sample rate

Does a recording work as a file path but behave differently after conversion to an array or bytes? Before changing the model, hotwords or VAD, check the input format, sample rate, channels and numeric range. A small read-only inspector can expose empty files, truncated payloads and incorrect duration calculations without loading a model.

A stereo file with 32000 sample frames at 16 kHz lasts two seconds, not four. Each frame contains one sample per channel. Flattening the two channels into a mono sequence changes their temporal relationship. Similarly, labelling 8 kHz samples as 16 kHz does not generate new samples and is not resampling.

This inspector supports local, uncompressed, signed 16-bit PCM WAV only. It uses the Python standard library, makes no network requests, loads no FunASR package or model, and leaves files unchanged. It is not a general audio decoder and does not support MP3, floating-point WAV or arbitrary raw PCM. The binary test fixtures verify input handling, not speech recognition quality or a production service.

1. Separate containers, bytes and arrays

WAV is a container. Its bytes include format information as well as audio. Treating the complete file as samples also treats header bytes as sound. The example uses wave to parse the container before reading audio frames.

Raw PCM needs external format information. Confirm the sample rate, channel count, bit depth, signedness and byte order at the source. A filename extension or byte count cannot reliably establish those properties. Adding a guessed WAV header does not prove that the input has been repaired.

Arrays have both shape and amplitude conventions. Signed PCM16 spans -32768 through 32767. Changing its dtype to floating point does not scale the amplitude. Division by 32768 maps the negative endpoint to -1 and the positive endpoint to slightly less than 1. For two-dimensional arrays, identify the channel axis rather than simply reshaping. Check the input implementation in the package version you actually use.

2. Run a read-only PCM16 WAV inspector

Run the following as a separate inspect_wav.py script with a local WAV path. It reads in bounded chunks, reports frames, duration and the absolute peak across channels, and rejects empty audio, non-PCM16 input and truncated data.

import json
import struct
import sys
import wave


def inspect_pcm16_wav(path):
    with wave.open(str(path), "rb") as audio:
        channels = audio.getnchannels()
        rate = audio.getframerate()
        declared = audio.getnframes()
        if audio.getcomptype() != "NONE" or audio.getsampwidth() != 2:
            raise ValueError("Expected uncompressed PCM16 WAV")
        if channels < 1 or rate < 1 or declared < 1:
            raise ValueError("Empty or invalid audio")
        frame_bytes = channels * 2
        chunk_frames = max(1, 65536 // frame_bytes)
        frames = 0
        peak = 0
        while True:
            raw = audio.readframes(chunk_frames)
            if not raw:
                break
            if len(raw) % frame_bytes:
                raise ValueError("Truncated sample or channel frame")
            frames += len(raw) // frame_bytes
            peak = max(peak, max(abs(value[0])
                                 for value in struct.iter_unpack("<h", raw)))
        if frames != declared:
            raise ValueError("Audio data is shorter than its WAV header")
        return {
            "channels": channels,
            "sample_rate": rate,
            "frames": frames,
            "duration_seconds": frames / rate,
            "peak_abs": peak / 32768.0,
        }


if __name__ == "__main__":
    print(json.dumps(inspect_pcm16_wav(sys.argv[1]), indent=2))
python inspect_wav.py /path/to/input.wav

duration_seconds is frames divided by sample rate; channel count is not another multiplier or divisor. peak_abs is the maximum absolute sample value across all channels, not loudness, signal-to-noise ratio or speech probability. A nonempty all-zero file reports zero, whereas a file with no data frames is rejected. A peak of 1 deserves inspection but one extreme sample does not establish clipping throughout a recording.

3. Act on the result without hiding the problem

4. Know the version and verification boundary

Input-fix integration #3899 preserves its contributors' history and addresses NumPy PCM16 scaling and channel handling for two-dimensional floating-point input. Inspect the loader at the pinned source commit. At publication, these changes are merged into main; the accepted root package is still FunASR 1.4.26. Installing 1.4.26 must not be described as installing this newer main fix. This inspector does not depend on that fix or change your installation.

The article tests execute the actual code from both language sources and their built pages. Cases cover mono and stereo, 8 and 16 kHz, positive and negative PCM16 endpoints, nonempty silence, empty input, 8-bit input, truncated samples and whole frames, non-WAV input, a peak beyond the first chunk, and the command-line entry point. This is a bounded input-contract test, not a security audit of arbitrary damaged files or coverage of every WAV encoding extension.

After checking the file, evaluate text, timestamps, omitted speech and speaker results on recordings you are authorized to process. For an issue report, provide redacted format information, versions and a shareable minimal reproduction. Do not expose private audio, transcripts or absolute paths.

Continue with the meeting-transcript acceptance checklist to connect input checks with actual transcription results.