WAV files carry audio samples along with the sample rate, channel layout, and numeric dtype that downstream signal code needs. scipy.io.wavfile fits small Python audio workflows where samples should move into a NumPy array, be adjusted, and return to an uncompressed WAV file.

wavfile.read() returns the sample rate and an array. A mono WAV loads as a one-dimensional array, while stereo and other multichannel files load as rows of samples with one column per channel.

wavfile.write() chooses the WAV sample format from the array dtype, so dtype conversion is part of the export decision. A short generated stereo tone keeps the round trip easy to inspect because it remains int16 data, reduces amplitude in integer space, writes a second file, and reloads the result.

Steps to read and write WAV files with SciPy:

  1. Create a script named wav_file_read_write.py.
    wav_file_read_write.py
    import numpy as np
    from scipy.io import wavfile
     
     
    sample_rate = 8000
    seconds = 0.02
    time = np.arange(int(sample_rate * seconds)) / sample_rate
     
    left = 0.4 * np.sin(2 * np.pi * 440 * time)
    right = 0.4 * np.sin(2 * np.pi * 660 * time)
    stereo = np.column_stack((left, right))
    pcm = np.round(stereo * np.iinfo(np.int16).max).astype(np.int16)
     
    wavfile.write("source.wav", sample_rate, pcm)
     
    rate, data = wavfile.read("source.wav")
    quiet = (data.astype(np.int32) // 2).astype(np.int16)
    wavfile.write("quiet.wav", rate, quiet)
     
    check_rate, check = wavfile.read("quiet.wav")
     
    print(f"source rate: {rate}")
    print(f"source dtype: {data.dtype}")
    print(f"source shape: {data.shape}")
    print(f"source sample row 1: {data[1].tolist()}")
    print(f"written rate: {check_rate}")
    print(f"written dtype: {check.dtype}")
    print(f"written shape: {check.shape}")
    print(f"written sample row 1: {check[1].tolist()}")
    print(f"amplitude reduced: {np.max(np.abs(check)) < np.max(np.abs(data))}")
    print(f"round trip exact: {np.array_equal(check, quiet)}")

    The 2-D array shape is (samples, channels). The int16 dtype makes wavfile.write() create a 16-bit PCM WAV file.

  2. Run the script.
    $ python3 wav_file_read_write.py
    source rate: 8000
    source dtype: int16
    source shape: (160, 2)
    source sample row 1: [4440, 6494]
    written rate: 8000
    written dtype: int16
    written shape: (160, 2)
    written sample row 1: [2220, 3247]
    amplitude reduced: True
    round trip exact: True

    The reloaded file keeps the same sample rate, dtype, and stereo shape. The sample row is half the original amplitude, and the final equality check confirms that the written WAV reloaded as the same array that was passed to wavfile.write().

  3. Read an existing WAV file from the project.
    rate, data = wavfile.read("recording.wav")
    print(rate, data.dtype, data.shape)

    Inspect data.dtype before scaling values. wavfile.read() returns 8-bit PCM as uint8, 16-bit PCM as int16, and 24-bit PCM in a left-justified int32 array.

  4. Convert floating-point processing output before writing 16-bit PCM.
    processed = np.clip(processed_float, -1.0, 1.0)
    processed_pcm = np.round(processed * np.iinfo(np.int16).max).astype(np.int16)

    wavfile.write() uses the dtype it receives. Passing float32 writes floating-point WAV data, while passing int16 writes 16-bit PCM.

  5. Preserve the sample rate when writing processed audio.
    wavfile.write("processed.wav", rate, processed_pcm)

    The rate argument is samples per second. Reusing the input rate keeps playback speed unchanged unless the audio has been resampled separately.
    Related: Resample a signal with SciPy

  6. Remove the demo files after adapting the pattern.
    $ rm wav_file_read_write.py source.wav quiet.wav