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")
Related
I am trying to implement a startle burst into a .mp4 file. I have been able to do this successfully but how I have altered the original audio results in an array. The trouble I am having is that the 'write_videofile' function seems to only take a specific type of structure in order to write the audio file. This is what I have so far any help would be great.
#Necessary imports
import os as os
import moviepy.editor as mp
from playsound import playsound
import librosa as librosa
import librosa.display
import sounddevice as sd
import numpy as np
from moviepy.audio.AudioClip import AudioArrayClip
#Set working directory
os.chdir('/Users/matthew/Desktop/Python_startle')
#Upload the video and indicate where in the clip the burst should be inserted
vid = mp.VideoFileClip("37vids_balanced/Baby_Babble.mp4")
audio, sr = librosa.load('37vids_balanced/Baby_Babble.mp4')
librosa.display.waveplot(audio, sr = sr)
sd.play(audio, sr)
audio_duration = librosa.get_duration(filename = '37vids_balanced/Baby_Babble.mp4')
start_point_sec = 6
start_point_samp = start_point_sec * sr
burst_samp = int(sr * .05) #how many samples are in the noise burst
burst_window = audio[start_point_samp : start_point_samp + burst_samp].astype(np.int32) #Isolating part of the audio clip to add burst to
#Creating startle burst
noise = np.random.normal(0,1,len(burst_window))
#Inserting the noise created into the portion of the audio that is wanted
audio[start_point_samp : start_point_samp + burst_samp] = audio[start_point_samp : start_point_samp + burst_samp].astype(np.int64) + noise #Puts the modified segment into the original audio
librosa.display.waveplot(audio, sr = sr)
sd.play(audio, sr)
new_vid = vid.set_audio(audio)
sd.play(new_vid.audio, sr)
#Trying to get the audio into the proper form to convert into a mp4
new_vid.audio = [[i] for i in new_vid.audio] #This makes the data into a list because the 'AudioArrayClip' could not take in an array because an array is not 'iterable' this might not be the best solution but it has worked so far
new_vid.audio = AudioArrayClip(new_vid.audio, fps = new_vid.fps)
new_vid.write_videofile('37vids_balanced_noise/Baby_Babble.mp4')
Maybe there is an easier way to take the array into an iterable form that would work.
I currently have the following code, which produces a sine wave of varying frequencies using the pyaudio module:
import pyaudio
import numpy as np
p = pyaudio.PyAudio()
volume = 0.5
fs = 44100
duration = 1
f = 440
samples = (np.sin(2 * np.pi * np.arange(fs * duration) * f /
fs)).astype(np.float32).tobytes()
stream = p.open(format = pyaudio.paFloat32,
channels = 1,
rate = fs,
output = True)
stream.write(samples)
However, instead of playing the sound, is there any way to make it so that the sound is written into an audio file?
Add this code at the top of your code.
from scipy.io.wavfile import write
Also, add this code at the bottom of your code.
This worked for me.
scaled = numpy.int16(s/numpy.max(numpy.abs(s)) * 32767)
write('test.wav', 44100, scaled)
Using scipy.io.wavfile.write as suggested by #h lee produced the desired results:
import numpy
from scipy.io.wavfile import write
volume = 1
sample_rate = 44100
duration = 10
frequency = 1000
samples = (numpy.sin(2 * numpy.pi * numpy.arange(sample_rate * duration)
* frequency / sample_rate)).astype(numpy.float32)
write('test.wav', sample_rate, samples)
Another example can be found on the documentation: https://docs.scipy.org/doc/scipy/reference/generated/scipy.io.wavfile.write.html
Handle your audio input as a numpy array like I did here in the second answer, but instead of just processing the frames and sending the data back to PyAudio, save each frame in a new output_array. Then when the processing is done you can use that output_array to write it to .wav or .mp3 file.
If you do that, however, the sound would still play. If you don't want to play the sound you have two options, either using the blocking mode or, if you wanna stick with the non blocking mode and the callbacks, do the following:
Erase output=True so that it is False as default.
Add a input=True parameter.
In your callback do not return ret_data, instead return None.
Keep count of the frames you've processed so that when you're done you return paComplete as second value of the returned tuple.
I am trying to achieve active noise reduction in python. My project is composed of two set of codes:
sound recording code
sound filtering code
What I aim for is that when you run the program, it will start recording through the microphone. After you've finished recording there will be a saved file called "file1.wav" When you play that file, it is the one that you recorded originally. After you're finished with that, you will now put "file1.wav" through a filter by calling "fltrd()". This will create a second wav file in the same folder and that second wav file is supposedly the one with less/reduced noise. Now my problem is that the second wav file is enhancing noise instead of reducing it. Can anyone please troubleshoot my code? :(
Here is my code below:
import pyaudio
import wave
import matplotlib.pyplot as plt
import numpy as np
import scipy.io.wavfile
import scipy.signal as sp
FORMAT = pyaudio.paInt16
CHANNELS = 2
RATE = 44100
CHUNK = 1024
RECORD_SECONDS = 5
WAVE_OUTPUT_FILENAME = "file1.wav"
audio = pyaudio.PyAudio()
# start Recording
stream = audio.open(format=FORMAT, channels=CHANNELS,
rate=RATE, input=True,
frames_per_buffer=CHUNK)
print ("recording...")
frames = []
for i in range(0, int(RATE / CHUNK * RECORD_SECONDS)):
data = stream.read(CHUNK)
frames.append(data)
print ("finished recording")
# stop Recording
stream.stop_stream()
stream.close()
audio.terminate()
waveFile = wave.open(WAVE_OUTPUT_FILENAME, 'wb')
waveFile.setnchannels(CHANNELS)
waveFile.setsampwidth(audio.get_sample_size(FORMAT))
waveFile.setframerate(RATE)
waveFile.writeframes(b''.join(frames))
waveFile.close()
x = scipy.io.wavfile.read('file1.wav')
n = x[1]
y = np.zeros(n.shape)
y = n.cumsum(axis=0)
def fltrd():
n,x = scipy.io.wavfile.read('file1.wav')
a2 = x.cumsum(axis=0)
a3 = np.asarray(a2, dtype = np.int16)
scipy.io.wavfile.write('file2.wav',n,a3)
Actual noise filtering is difficult and intense. However, an simple noise filter using high and low pass filter can be easily created using pydub library. See here for more details (install, requirements etc)
Also see here for more details on low and high pass filter using pydub.
Basic idea is to take a audio file and then pass it through both low and high pass filter such that audio above and below certain threahold will be highly attenuated (in effect demonstrating filtering).
Although, this will not affect any noise falling in pass-band for which you will need to look at other noise cancellation techniques.
from pydub import AudioSegment
from pydub import low_passfilter
from pydub import high_pass_filter
from pydub.playback import play
song = AudioSegment.from_wav('file1.wav')
#Freq in Hz ,Adjust as per your needs
new = song.low_pass_filter(5000).high_pass_filter(200)
play(new)
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)
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()