Writing WAV file using Python, Numpy array and WAVE module
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))
Related videos on Youtube
Comments
-
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 over 7 yearsYou need to convert your float data to integer and pack it in Little Endian.
-
mir0soft over 7 yearsHow 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 over 7 yearsYes, 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 forstruct.pack
. -
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 over 7 yearsAs 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 astruct
format string. -
mir0soft over 7 years@PM2Ring Yes, I will have that in mind using
struct
. Thanks again!
-
-
mir0soft over 7 yearsYes, I tried with scipy and all is working fine. But I am still puzzled why is this happening when I am using WAVE?
-
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/…