WAV processing depends on more than the sample values: the sample rate, channel layout, and numeric representation must survive the trip through Python. scipy.io.wavfile exposes those details as ordinary NumPy data and writes the processed array back as an uncompressed WAV file.

The wavfile.read() function returns a sample-rate integer and an array whose shape distinguishes mono from multichannel audio. wavfile.write() uses that array's dtype to choose the output sample format, so an int16 array produces 16-bit PCM while a float32 array produces 32-bit floating-point WAV data.

A generated stereo tone makes the round trip reproducible without an external recording. The final reload checks the written file rather than trusting the in-memory array, and the comparison can fail if the rate or sample values change during export.

Steps to read and write WAV files with SciPy:

  1. Create the input section in wav_file_read_write.py.
    wav_file_read_write.py
    import numpy as np
    from scipy.io import wavfile
     
     
    sample_rate = 8000
    duration = 0.02
    time = np.arange(int(sample_rate * duration)) / sample_rate
     
    channels = np.column_stack(
        (
            0.4 * np.sin(2 * np.pi * 440 * time),
            0.4 * np.sin(2 * np.pi * 660 * time),
        )
    )
    source_pcm = np.round(channels * np.iinfo(np.int16).max).astype(np.int16)

    The two columns represent the left and right channels. Converting the values to int16 establishes a 16-bit PCM source for the file round trip.

  2. Append the source-file round-trip section to wav_file_read_write.py.
    wavfile.write("source.wav", sample_rate, source_pcm)
     
    rate, samples = wavfile.read("source.wav")
    print(f"read: rate={rate}, dtype={samples.dtype}, shape={samples.shape}")

    A mono file loads as a one-dimensional array. Stereo and other multichannel files load with shape (samples, channels).

  3. Append the amplitude change and output-file write section to wav_file_read_write.py.
    quieter_pcm = (samples.astype(np.int32) // 2).astype(np.int16)
    wavfile.write("quieter.wav", rate, quieter_pcm)

    The wider intermediate dtype prevents 16-bit overflow during arithmetic. Reusing rate preserves playback speed, and converting back to int16 keeps the output as 16-bit PCM.

  4. Append the output-file reload checks to wav_file_read_write.py.
    check_rate, check_samples = wavfile.read("quieter.wav")
    print(
        f"written: rate={check_rate}, dtype={check_samples.dtype}, "
        f"shape={check_samples.shape}"
    )
    print(
        f"peak amplitude: {np.max(np.abs(samples))} -> "
        f"{np.max(np.abs(check_samples))}"
    )
    print(f"sample rate preserved: {check_rate == rate}")
    print(f"written samples match: {np.array_equal(check_samples, quieter_pcm)}")
  5. Run the completed WAV round-trip script to verify the reloaded file.
    $ python3 wav_file_read_write.py
    read: rate=8000, dtype=int16, shape=(160, 2)
    written: rate=8000, dtype=int16, shape=(160, 2)
    peak amplitude: 13107 -> 6554
    sample rate preserved: True
    written samples match: True

    The second peak is half the source amplitude after integer rounding. Both final boolean checks come from reloading quieter.wav, so either becomes False when the written file does not preserve the expected rate or sample array.