Search code examples
pythonnumpywavwave

Preferred way to write audio data to a WAV file?


I am trying to write an audio file using python's wave and numpy. So far I have the following and it works well:

import wave
import numpy as np

# set up WAV file parameters
num_channels    = 1                             # mono audio
sample_width    = 1                             # 8 bits(1 byte)/sample
sample_rate     = 44.1e3                        # 44.1k samples/second
frequency       = 440                           # 440 Hz
duration        = 20                            # play for this many seconds
num_samples     = int(sample_rate * duration)   # samples/seconds * seconds

# open WAV file and write data
with wave.open('sine8bit_2.wav', 'w') as wavfile:
    wavfile.setnchannels(num_channels)
    wavfile.setsampwidth(sample_width)
    wavfile.setframerate(sample_rate)

    t = np.linspace(0, duration, num_samples)
    data = (127*np.sin(2*np.pi*frequency*t)).astype(np.int8)
    wavfile.writeframes(data) # or data.tobytes() ??

My issue is that since I am using a high sampling rate, the num_samples variable might quickly become too large (9261000 samples for a 3 minute 30 seconds track say). Would using a numpy array this large be advisable? Is there a better way of going about this? Also is use of writeframes(.tobytes()) needed in this case because my code runs fine without it and it seems like extra overhead (especially if the arrays get too large).


Solution

  • Assuming you are only going to write a sine wave, you could very well create only one period as your data array and write that several times to the .wav file.

    Using the parameters you provided, your data array is 8800 times smaller with that approach. Its size also no longer depends on the duration of your file!

    import wave
    import numpy as np
    
    # set up WAV file parameters
    num_channels    = 1                             # mono audio
    sample_width    = 1                             # 8 bits(1 byte)/sample
    sample_rate     = 44.1e3                        # 44.1k samples/second
    frequency       = 440                           # 440 Hz
    duration        = 20                            # play for this many seconds
    
    # Create a single period of sine wave.
    n = round(sample_rate/frequency)
    t = np.linspace(0, 1/frequency, n)
    data = (127*np.sin(2*np.pi*frequency*t)).astype(np.int8)
    periods = round(frequency*duration)
    
    # open WAV file and write data
    with wave.open('sine8bit_2.wav', 'w') as wavfile:
        wavfile.setnchannels(num_channels)
        wavfile.setsampwidth(sample_width)
        wavfile.setframerate(sample_rate)
    
        for _ in range(periods):
            wavfile.writeframes(data)