Writing WAV file using Python, Numpy array and WAVE module

15,105

Solution 1

import scipy.io.wavfile
scipy.io.wavfile.write("karplus.wav", Fs, y)

Tada! AFAIK works with float64 and float32, and probably others. For stereo, shape must be (nb_samples, 2). See scipy.io.wavfile.write.

Solution 2

Here are code samples to write a (stereo) wave file using the wave standard library. I included two examples: one using numpy, and one that doesn't require any dependencies.

Using a numpy array

Note that if your data is in a numpy array, no need for the struct library.

import wave
import numpy as np

samplerate = 44100

# A note on the left channel for 1 second.
t = np.linspace(0, 1, samplerate)
left_channel = 0.5 * np.sin(2 * np.pi * 440.0 * t)

# Noise on the right channel.
right_channel = np.random.random(size=samplerate)

# Put the channels together with shape (2, 44100).
audio = np.array([left_channel, right_channel]).T

# Convert to (little-endian) 16 bit integers.
audio = (audio * (2 ** 15 - 1)).astype("<h")

with wave.open("sound1.wav", "w") as f:
    # 2 Channels.
    f.setnchannels(2)
    # 2 bytes per sample.
    f.setsampwidth(2)
    f.setframerate(samplerate)
    f.writeframes(audio.tobytes())

Using a list

This is (almost) the same code but without using numpy. No external dependencies are required.

import math
import random
import struct
import wave

samplerate = 44100

left_channel = [
    0.5 * math.sin(2 * math.pi * 440.0 * i / samplerate) for i in range(samplerate)
]
right_channel = [random.random() for _ in range(samplerate)]

with wave.open("sound2.wav", "w") as f:
    f.setnchannels(2)
    f.setsampwidth(2)
    f.setframerate(samplerate)
    for samples in zip(left_channel, right_channel):
        for sample in samples:
            sample = int(sample * (2 ** 15 - 1))
            f.writeframes(struct.pack("<h", sample))
Share:
15,105

Related videos on Youtube

mir0soft
Author by

mir0soft

Tumbleweed?

Updated on May 25, 2022

Comments

  • mir0soft
    mir0soft almost 2 years

    I am trying to implement the Karplus-Strong algorithm.

    All is looking fine when I play (through Jupyter Notebook using Audio(y, rate=Fs)) the collected numpy array (representing guitar accord).

    Unfortunately, writing the numpy array: y, into wav file using WAVE module is incorrect (using the next python code):

    noise_output = wave.open('k-s.wav', 'w')
    noise_output.setparams((1, 4, Fs, 0, 'NONE', 'not compressed'))
    
    for i in range(0, len(y)):
         value = y[i]
         packed_value = struct.pack('f', value)
         noise_output.writeframes(packed_value)
    
    noise_output.close()
    

    Each element of y is

    <type 'numpy.float64'>
    

    How should I amend the writing loop in order write the WAV file correctly?

    Some more information about the issue. Before writing to WAV, the first elements of the y array are:

     [ 0.33659756  0.33659756 -0.43915295 -0.87036152  1.40708988  0.32123558
    -0.6889402   1.9739982  -1.29587159 -0.12299964  2.18381762  0.82228042
     0.24593503 -1.28067426 -0.67568838 -0.01843234 -1.830472    1.2729578
    -0.56575346  0.55410736]
    

    After writing the elements to the WAV file, close the WAV file and read it again, I got this for the first 20 elements of the collected array:

    [ 1051481732  1051481732 -1092560728 -1084305405  1068768133  1050966269
     -1087349149  1073523705 -1079648481 -1107564740  1074512811  1062371576
      1048303204 -1079775966 -1087571478 -1130954901 -1075163928  1067642952
     -1089415880  1057872379]
    
    • PM 2Ring
      PM 2Ring over 7 years
      You need to convert your float data to integer and pack it in Little Endian.
    • mir0soft
      mir0soft over 7 years
      How to convert it to integer? By some scaling or? The float numbers are pretty close to 1, so I will need some scaling maybe? @PM2Ring
    • PM 2Ring
      PM 2Ring over 7 years
      Yes, you need to scale them. I suggest using signed 16 bit integers, so scale your floats so they'll fit in -2**15 < x < 2**15, and use '<h' as the format string for struct.pack.
    • mir0soft
      mir0soft over 7 years
      @PM2Ring It worked, thanks (even more clearly using signed 32 bit)! I am still puzzled though, why the previous solution failed?
    • PM 2Ring
      PM 2Ring over 7 years
      As far as I know (and from what I can see in the docs) the Python wave module only supports integer data formats for WAV files. So even though your packed float data is the right size, it sounds terrible because it gets interpreted as 32 bit integer data. BTW, you should always specify the correct endian type in a struct format string.
    • mir0soft
      mir0soft over 7 years
      @PM2Ring Yes, I will have that in mind using struct. Thanks again!
  • mir0soft
    mir0soft over 7 years
    Yes, I tried with scipy and all is working fine. But I am still puzzled why is this happening when I am using WAVE?
  • ERJAN
    ERJAN over 3 years
    @mir0soft hi sir can u help with how to pass edited wav to another function without saving it ? stackoverflow.com/questions/63467345/…