Writing WAV files, and make a copy for the wav file - python

I have a question about writing WAV file.
Here are some information:
Writes a WAV file named filename with two channels, two bytes sample width,
frame rate as given by framerate , with len(sound) samples, compression type 'NONE' , and compression
name 'not compressed'
Here is my code:
import wave
import struct
def loadstereowav(filename):
w = wave.open(filename, 'r')
nchannels, sampwidth, framerate, nframes, comptype, compname = w.getparams()
assert nchannels == 2 and sampwidth == 2 and comptype == 'NONE'
frames = w.readframes(nframes * nchannels)
sound = struct.unpack_from((str(nframes)+'h') * nchannels, frames)
w.close()
return framerate, sound
def savestereowav(filename, framerate, sound):
w1 = wave.open(filename, "w")
for i in range(0, len(sound)):
w1.write(struct.pack('>h', framerate))
Here are some test case :
>>> rate, sound = loadstereowav('love.wav')
>>> savestereowav('love_copy.wav', rate, sound)
I need to write a savestereowav(filename, framerate, sound) function to Loading and saving a file should produce a copy.
For example, load a "love.wav" and make a copy with "love_copy.wav"
But, i have not idea about how to deal with the WAV file.
Anyone help me with this question?

If you need to copy the file without modification (same nchannels, same sampwidth, etc), you can copy the file without having to analyse/understand its contents: shutils.copyfile() should do the trick
On the other hand, if you need to transform the data (ex. change sample rate, number of channels, etc), you will need two things:
Understand the wav file format. Search for "wav file format" on the net (ex https://en.wikipedia.org/wiki/WAV). The wavemodule only provides the readframes() method, but you will have to manually manage the data.
Depending on what transformation you want, you will need some kind of signal processing method (for example https://en.wikipedia.org/wiki/Sampling_%28signal_processing%29). This can be a broad question.
Since you included a framerate parameter in the savestereowav function, I assumed you want to resample. But in your test case, you use the same rate.

as I can see you already have it... it is just a minor tweak.
When you load a WAV file, first you open it and retrieve all the parameters available. You can decide whether you use them or not. In practice, you only need the first four, being the number of channels (1 for mono, 2 for stereo), sample rate, the sample width, and the number of frames. Because the track in this code must be stereo, otherwise you fall in the assert case, you have to read the frames for each channel (left and right channels for stereo).
The sound is comprised after you unpack into bytes and then you close it.
Now you have all the information you need to clone or copy your WAV file.
Then, the rest is the inverse procedure, you create a fresh WAV file, set the same parameters from the original track, and you have to write frame by frame via the pack function.
import wave, struct
def loadstereowav(filename):
w = wave.open(filename, 'r')
nchannels, sample_width, sample_rate, nframes, comptype, compname = w.getparams()
assert nchannels == 2 and sample_width == 2 and comptype == 'NONE'
frames = w.readframes(nframes * nchannels)
sound = struct.unpack_from((str(nframes)+'h') * nchannels, frames)
w.close()
return sample_rate, nchannels, sample_width, sound
def savestereowav(filename, sample_rate, n_channels, sample_width, sound):
# Add file parameters
w1 = wave.open(filename, "w")
w1.setnchannels(n_channels)
w1.setsampwidth(sample_width)
w1.setframerate(sample_rate)
for i in range(0, len(sound)):
w1.writeframesraw(struct.pack('<h', sound[i])) ## Copy each frame
srate, channels, swidth, sound = loadstereowav('Hit.wav')
savestereowav('Hit_copy.wav', srate, channels, swidth, sound)

Related

How to save with .mp3 instead of .wav?

I'm trying to make a voice recorder, in this portion of code the file is saved with the .wavformat but i prefere the .mp3 format, how to save the file with the .mp3 extension? I think that SoundFile can't use mp3 because if i write .mp3 instead of .wav the program gives me an error, thanks
with sf.SoundFile("output.wav", mode='w', samplerate=44100,channels=2) as file:
#Create an input stream to record audio without a preset time
with sd.InputStream(samplerate=44100, channels=2, callback=callback):
while recording == True:
#Set the variable to True to allow playing the audio later
file_exists =True
#write into file
file.write(q.get())
Soundfile, or, more precisely, libsndfile, does not support mp3. Consider using a different library (e.g. pydub). Here's an example where I am writing a sine wave to mp3:
import numpy as np
from pydub import AudioSegment
sample_rate = 48000
frequency = 440 # Hz
length = 3 # in seconds
t = np.linspace(0, length, sample_rate * length)
y = np.sin(frequency * 2 * np.pi * t).astype("float32") # Has frequency of 440Hz
audio_segment = AudioSegment(
y.astype("float32").tobytes(),
frame_rate=sample_rate,
sample_width=y.dtype.itemsize,
channels=1
)
audio_segment.export("test.mp3", format="mp3", bitrate="128k")

How can i extract the frequency from WAV file - python

I'm creating WAV file and write list of specific notes frequency separate by silence note with const duration in 44100 sample_rate for example 440 Hz, silence, 351 Hz, silence etc.
now i want to read from the WAV file and get the exact frequency list.
how can i do that?
thanks!
This is my note to WAV code:
# !/usr/bin/python
# based on : www.daniweb.com/code/snippet263775.html
import math
import wave
import struct
import txtToNote
# Audio will contain a long list of samples (i.e. floating point numbers describing the
# waveform). If you were working with a very long sound you'd want to stream this to
# disk instead of buffering it all in memory list this. But most sounds will fit in
# memory.
import wavToNote
audio = []
sample_rate = 44100.0
def append_silence(duration_milliseconds=500):
"""
Adding silence is easy - we add zeros to the end of our array
"""
num_samples = duration_milliseconds * (sample_rate / 1000.0)
for x in range(int(num_samples)):
audio.append(0.0)
return
def append_sinewave(freq=440.0, duration_milliseconds=1000, volume=1.0):
"""
The sine wave generated here is the standard beep. If you want something
more aggressive you could try a square or saw tooth waveform. Though there
are some rather complicated issues with making high quality square and
sawtooth waves... which we won't address here :)
"""
global audio # using global variables isn't cool.
num_samples = duration_milliseconds * (sample_rate / 1000.0)
print("audio:")
for x in range(int(num_samples)):
audio.append(volume * math.sin(2 * math.pi * freq * (x / sample_rate)))
return
def revers_audio():
print("hi")
def save_wav(file_name):
# Open up a wav file
wav_file = wave.open(file_name, "w")
# wav params
nchannels = 1
sampwidth = 2
# 44100 is the industry standard sample rate - CD quality. If you need to
# save on file size you can adjust it downwards. The stanard for low quality
# is 8000 or 8kHz.
nframes = len(audio)
comptype = "NONE"
compname = "not compressed"
wav_file.setparams((nchannels, sampwidth, sample_rate, nframes, comptype, compname))
# WAV files here are using short, 16 bit, signed integers for the
# sample size. So we multiply the floating point data we have by 32767, the
# maximum value for a short integer. NOTE: It is theortically possible to
# use the floating point -1.0 to 1.0 data directly in a WAV file but not
# obvious how to do that using the wave module in python.
for sample in audio:
wav_file.writeframes(struct.pack('h', int(sample * 32767.0)))
wav_file.close()
return
# txt_to_note is my function that return frequency (float) for each latter in the string
# (the letters are note chords!)
# for simplicity ['a' = 220, 'b' = 467, 'c' = 351, 'd' = 367]
for i in txtToNote.txt_to_note("abcda"):
append_sinewave(freq=i)
append_silence()
save_wav("output.wav")
if __name__ == '__main__':
pass
You'd read the signal from the file, window it into suitably long chunks depending on your desired temporal resolution, run FFT on each chunk to get frequency data and find the peaks.
You can find windowing and FFT in the Scipy library.

How can PyAudio use my computer's microphone?

In order to record a 2 second wav file I used PyAudio (with Pyzo) and the following classical code to record a sound and save it :
import pyaudio
import wave
chunk = 1024
FORMAT = pyaudio.paInt16
CHANNELS = 1
RATE = 44100
RECORD_SECONDS = 2
WAVE_OUTPUT_FILENAME = "my_path//a_test_2.wav"
p = pyaudio.PyAudio()
# Création et initialisation de l'objet stream...
s = p.open(format = FORMAT,
channels = CHANNELS,
rate = RATE,
input = True,
frames_per_buffer = chunk)
print("---recording---")
d = []
print((RATE / chunk) * RECORD_SECONDS)
for i in range(0, (RATE // chunk * RECORD_SECONDS)):
data = s.read(chunk)
d.append(data)
#s.write(data, chunk)
print("---done recording---")
s.close()
p.terminate()
wf = wave.open(WAVE_OUTPUT_FILENAME, 'wb')
wf.setnchannels(CHANNELS)
wf.setsampwidth(p.get_sample_size(FORMAT))
wf.setframerate(RATE)
wf.writeframes(b''.join(d))
wf.close()
Then I used it, saying "aaa". Everything's fine, no error.
And when I read the wav file, no "aaa" could be heard. I visualized the file in Audacity and I could see everything was just silence (0). So it seems Pyzo doesn't know where my microphone is, because it didn't use it. What can I do ? Any idea ?
Or maybe it didn't write all the data recorded, but I don't know why.
I have already checked that my microphone is 16 bits and has a 44100 rate.
You'll need to do get this working step-by-step. To make sure that you're recording from the mic, I would suggest printing out the max of each chunk as you read it. Then you should see, in real time, a difference between background noise and your speech. For example:
import audioop
# all the setup stuff, then in your main data loop:
for i in range(0, (RATE // chunk * RECORD_SECONDS)):
data = s.read(chunk)
mx = audioop.max(data, 2)
print mx
Usually this difference between background noise and speech is more than 10x, so you can easily see the size change in the numbers as they fly by.
Also, at the start, list all of your microphones to make sure that you're using the right one (get_device_info_by_index). For example, you could be reading from the "line in" rather than the microphone.

Read the data of a single channel from a stereo wave file in Python

I have to read the data from just one channel in a stereo wave file in Python.
For this I tried it with scipy.io:
import scipy.io.wavfile as wf
import numpy
def read(path):
data = wf.read(path)
for frame in data[1]:
data = numpy.append(data, frame[0])
return data
But this code is very slow, especially if I have to work with longer files.
So does anybody know a faster way to do this? I thought about the standard wave module by using wave.readframes(), but how are the frames stored there?
scipy.io.wavfile.read returns the tuple (rate, data). If the file is stereo, data is a numpy array with shape (nsamples, 2). To get a specific channel, use a slice of data. For example,
rate, data = wavfile.read(path)
# data0 is the data from channel 0.
data0 = data[:, 0]
The wave module returns the frames as a string of bytes, which can be converted to numbers with the struct module. For instance:
def oneChannel(fname, chanIdx):
""" list with specified channel's data from multichannel wave with 16-bit data """
f = wave.open(fname, 'rb')
chans = f.getnchannels()
samps = f.getnframes()
sampwidth = f.getsampwidth()
assert sampwidth == 2
s = f.readframes(samps) #read the all the samples from the file into a byte string
f.close()
unpstr = '<{0}h'.format(samps*chans) #little-endian 16-bit samples
x = list(struct.unpack(unpstr, s)) #convert the byte string into a list of ints
return x[chanIdx::chans] #return the desired channel
If your WAV file has some other sample size, you can use the (uglier) function in another answer I wrote here.
I've never used scipy's wavfile function so I can't compare speed, but the wave and struct approach I use here has always worked for me.
rate, audio = wavfile.read(path)
audio = np.mean(audio, axis=1)

Play a part of a .wav file in python

Is it possible to play a certain part of a .wav file in Python?
I'd like to have a function play(file, start, length) that plays the audiofile file from start seconds and stops playing after length seconds. Is this possible, and if so, what library do I need?
this is possible and can be easy in python.
Pyaudio is a nice library and you can use to play your audio!
First do you need decode the audio file (wav, mp3, etc) this step convert audio data in numbers(short int or float32).
Do you need convert the seconds in equivalent position point to cut the signal in the position of interest, to do this multiply your frame rate by what seconds do you want !
Here one simple example for wav files:
import pyaudio
import sys
import numpy as np
import wave
import struct
File='ederwander.wav'
start = 12
length=7
chunk = 1024
spf = wave.open(File, 'rb')
signal = spf.readframes(-1)
signal = np.fromstring(signal, 'Int16')
p = pyaudio.PyAudio()
stream = p.open(format =
p.get_format_from_width(spf.getsampwidth()),
channels = spf.getnchannels(),
rate = spf.getframerate(),
output = True)
pos=spf.getframerate()*length
signal =signal[start*spf.getframerate():(start*spf.getframerate()) + pos]
sig=signal[1:chunk]
inc = 0;
data=0;
#play
while data != '':
data = struct.pack("%dh"%(len(sig)), *list(sig))
stream.write(data)
inc=inc+chunk
sig=signal[inc:inc+chunk]
stream.close()
p.terminate()
I know that this is a rather old question, but I just needed the exact same thing and for me ederwander's example seems a little bit too complicated.
Here is my shorter (and commented) solution:
import pyaudio
import wave
# set desired values
start = 7
length = 3
# open wave file
wave_file = wave.open('myWaveFile.wav', 'rb')
# initialize audio
py_audio = pyaudio.PyAudio()
stream = py_audio.open(format=py_audio.get_format_from_width(wave_file.getsampwidth()),
channels=wave_file.getnchannels(),
rate=wave_file.getframerate(),
output=True)
# skip unwanted frames
n_frames = int(start * wave_file.getframerate())
wave_file.setpos(n_frames)
# write desired frames to audio buffer
n_frames = int(length * wave_file.getframerate())
frames = wave_file.readframes(n_frames)
stream.write(frames)
# close and terminate everything properly
stream.close()
py_audio.terminate()
wave_file.close()

Categories