Decrease audio quality - python

I'm trying to build an 'old radio' with raspberry pi 4 and python 3. I have a button which records my sound as long as I'm holding the button. When I release it, it replays the recorded sound and also plays an other wav file as background noise. My problem is that my voice is too clear and doesn't sound like an 'old radio' with noises and crackling.
I have tried to change CHUNK and nothing happened. Changing the RATE only makes my sound deeper/higher.
I'm using an USB converter for the microphone and the headset aswell.
Basically, I want to add 'noise' or any effect to my voice that makes it sound like it's from an old radio.
import time
import pyaudio
import wave
import sched
from pygame import mixer
import audiosegment
import numpy as np
from gpiozero import Button
CHUNK = 8192
FORMAT = pyaudio.paInt16
CHANNELS = 2
RATE = 45000
RECORD_SECONDS = 5
WAVE_OUTPUT_FILENAME = "output.wav"
button = Button(2)
p = pyaudio.PyAudio()
frames = []
def callback(in_data, frame_count, time_info, status):
frames.append(in_data)
return in_data, pyaudio.paContinue
started = False
stream = None
playing = None;
def recorder():
global started, p, stream, frames, button, playing
if button.is_pressed and not started and not playing:
# Start the recording
try:
stream = p.open(format=FORMAT,
channels=CHANNELS,
rate=RATE,
input=True,
frames_per_buffer=CHUNK,
stream_callback=callback)
print("Stream active:", stream.is_active())
started = True
print("start Stream")
except:
raise
elif not button.is_pressed and started:
try:
started = False
print("Stop recording")
stream.stop_stream()
stream.close()
wf = wave.open(WAVE_OUTPUT_FILENAME, 'wb')
wf.setnchannels(CHANNELS)
wf.setsampwidth(p.get_sample_size(FORMAT))
wf.setframerate(RATE)
wf.writeframes(b''.join(frames))
mixer.init()
mixer.Channel(0).set_volume(0.1)
tada = mixer.Sound('old_radio.wav')
channel = tada.play()
s = audiosegment.from_file(WAVE_OUTPUT_FILENAME)
main = mixer.Sound(WAVE_OUTPUT_FILENAME)
channel1 = main.play()
if 'channel1' in locals() and channel1 is not None:
while channel1.get_busy():
playing = True
channel.unpause()
channel.pause()
channel.stop()
channel1.stop()
playing = False
frames = []
except:
raise
# Reschedule the recorder function in 100 ms.
task.enter(0.1, 1, recorder, ())
print("Press and hold the button to begin recording")
print("Release the button to end recording")
task = sched.scheduler(time.time, time.sleep)
task.enter(0.1, 1, recorder, ())
task.run()

Related

pyAudio - stop recording at will from command line

I'm not really that good with Python. Recently I've been toying around with pyAudio and using a simple script managed to record system sound. Thing is, the script I'm using is ran using a fixed timer. Instead, I want to end the recording whenever I want, from command line.
Does anyone know how to achieve this, if possible at all?
import pyaudio
import wave
import sys
DURATION = int(sys.argv[1])
FORMAT = pyaudio.paInt16
CHANNELS = 2
RATE = 44100
CHUNK = 1024
RECORD_SECONDS = DURATION
WAVE_OUTPUT_FILENAME = "file.wav"
print("hello")
p = pyaudio.PyAudio()
stream = p.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")
stream.stop_stream()
stream.close()
p.terminate()
waveFile = wave.open(WAVE_OUTPUT_FILENAME, 'wb')
waveFile.setnchannels(CHANNELS)
waveFile.setsampwidth(p.get_sample_size(FORMAT))
waveFile.setframerate(RATE)
waveFile.writeframes(b''.join(frames))
waveFile.close()

How to record 2 audio feeds simultaneously?

I want to record a audio track and save it 2 diffrent .wav files. The audio tracks should be saved with a delay of ca. 6 seconds and each .wav should be 12 seconds long.
I tried to do it with multiprocessing and pyaudio, but i cant manage to get it working
Please note that i am a beginner in python and that this is my first post on stackoverflow!
def func1():
#Record and save a 12 seconds long .wav
def func2():
#Record and save a 12 seconds long .wav
if __name__ == '__main__':
p1 = Process(target=func1)
p1.start()
p2 = Process(target=func2)
p2.start()
p1.join()
p2.join()
#start func2 6 seconds after func1
I would expect a data structure like this:
|---1.wav---|---1.wav---|---1.wav---|
|---2.wav---|---2.wav---|---2.wav---|
6sec 12sec 18sec 24sec 30sec 36sec 42sec
EDIT:
I came up with a bit of code that seems to work kind of well. It has a delay of .144 seconds. I am happy about improvement od this code. This code uses threading instead of multiprocessing.
import pyaudio
import wave
from threading import Thread
import time
from datetime import datetime
FORMAT = pyaudio.paInt16
CHANNELS = 2
RATE = 44100
CHUNK = 1024
CHUNK1 = 1024
RECORD_SECONDS = 12
WAVE_OUTPUT_FILENAME1 = name = "outputs/output_1"+datetime.now().strftime("%m:%d:%Y-")
WAVE_OUTPUT_FILENAME2 = name = "outputs/output_2"+datetime.now().strftime("%m:%d:%Y-")
def func1():
while 1==1:
global FORMAT
global CHANNELS
global RATE
global CHUNK
global RECORD_SECONDS
global WAVE_OUTPUT_FILENAME1
WAVE_OUTPUT_FILENAME1 = name = "outputs/output1_"#+datetime.now().strftime("%m:%d:%Y-")
audio = pyaudio.PyAudio()
stream = audio.open(format=FORMAT, channels=CHANNELS,
rate=RATE, input=True,
frames_per_buffer=CHUNK)
print("recording...")
frames = []
WAVE_OUTPUT_FILENAME1 = WAVE_OUTPUT_FILENAME1+datetime.now().strftime("%H;%M;%S.%f--")
for i in range(0, int(RATE / CHUNK * RECORD_SECONDS)):
data = stream.read(CHUNK)
frames.append(data)
WAVE_OUTPUT_FILENAME1 = WAVE_OUTPUT_FILENAME1 + datetime.now().strftime("%H;%M;%S.%f")+".wav"
print("finished recording")
# stop Recording
stream.stop_stream()
stream.close()
audio.terminate()
waveFile = wave.open(WAVE_OUTPUT_FILENAME1, 'wb')
waveFile.setnchannels(CHANNELS)
waveFile.setsampwidth(audio.get_sample_size(FORMAT))
waveFile.setframerate(RATE)
waveFile.writeframes(b''.join(frames))
waveFile.close()
def func2():
time.sleep(6)
while 1==1:
global FORMAT
global CHANNELS
global RATE
global CHUNK1
global RECORD_SECONDS
global WAVE_OUTPUT_FILENAME2
WAVE_OUTPUT_FILENAME2 = name = "outputs/output2_"#+datetime.now().strftime("%m:%d:%Y-")
audio = pyaudio.PyAudio()
stream = audio.open(format=FORMAT, channels=CHANNELS,
rate=RATE, input=True,
frames_per_buffer=CHUNK1)
print("recording...")
frames = []
WAVE_OUTPUT_FILENAME2 = WAVE_OUTPUT_FILENAME2+datetime.now().strftime("%H;%M;%S.%f--")
for i in range(0, int(RATE / CHUNK1 * RECORD_SECONDS)):
data = stream.read(CHUNK1)
frames.append(data)
WAVE_OUTPUT_FILENAME2 = WAVE_OUTPUT_FILENAME2 + datetime.now().strftime("%H;%M;%S.%f")+".wav"
print("finished recording")
# stop Recording
stream.stop_stream()
stream.close()
audio.terminate()
waveFile = wave.open(WAVE_OUTPUT_FILENAME2, 'wb')
waveFile.setnchannels(CHANNELS)
waveFile.setsampwidth(audio.get_sample_size(FORMAT))
waveFile.setframerate(RATE)
waveFile.writeframes(b''.join(frames))
waveFile.close()
if __name__ == '__main__':
Thread(target = func1).start()
Thread(target = func2).start()
why do you think you need multiprocessing? I think it just complicates things
how about just recording in 6 second (or smaller) chunks/frames and write the correct frames to each file.
I've got a bit carried away, and written a nice class to do this:
import pyaudio
import wave
import time
class OverlappedRecorder:
def __init__(
self, secs_per_file, secs_between_file, *,
num_channels=2, sample_rate=48000,
sample_format=pyaudio.paInt16,
):
# various constants needed later
self.num_channels = num_channels
self.sample_width = pyaudio.get_sample_size(sample_format)
self.sample_rate = sample_rate
self.frames_between_start = int(secs_between_file * sample_rate)
self.frames_per_file = int(secs_per_file * sample_rate)
# mutable state needed to keep everything going
self.files = []
self.frames_till_next_file = 0
self.pa = pyaudio.PyAudio()
self.stream = self.pa.open(
format=sample_format, channels=num_channels,
rate=sample_rate, frames_per_buffer=1024,
input=True, start=False,
stream_callback=self._callback,
)
def sleep_while_active(self):
while self.stream.is_active():
time.sleep(0.2)
def begin_wave_file(self):
"internal function to start a new WAV file"
path = time.strftime(
'recording-%Y-%m-%d-%H.%M.%S.wav',
time.localtime()
)
file = wave.open(path, 'wb')
file.setnchannels(self.num_channels)
file.setsampwidth(self.sample_width)
file.setframerate(self.sample_rate)
self.files.append(file)
# context manager stuff, recording starts when entered using "with"
def __enter__(self):
self.stream.start_stream()
return self
# exiting shuts everything down
def __exit__(self, exc_type, exc_val, exc_tb):
self.stream.stop_stream()
self.stream.close()
self.pa.terminate()
for file in self.files:
file.close()
# called by pyaudio when a new set of frames are ready
def _callback(self, data, frame_count, time_info, status):
self.frames_till_next_file -= frame_count
# see if we need to start a new file
if self.frames_till_next_file < 0:
self.frames_till_next_file += self.frames_between_start
self.begin_wave_file()
# can't remove from lists while iterating
# keep a list of files to close and remove later
done = []
for file in self.files:
remain = self.frames_per_file - file.getnframes()
# add appropriate amount of data to all open files
if frame_count < remain:
file.writeframesraw(data)
else:
remain *= self.sample_width * self.num_channels
file.writeframesraw(data[:remain])
done.append(file)
# close anything that finished
for file in done:
file.close()
self.files.remove(file)
# tell pyaudio to keep going
return (None, pyaudio.paContinue)
basic usage is: create an object, enter it using with and it'll start recording, and when you exit it'll stop and clean up.
rec = OverlappedRecorder(12, 6)
with rec:
time.sleep(30)
will let it run for 30 seconds, or you could do:
with OverlappedRecorder(12, 6) as rec:
rec.sleep_while_active()
to let it run until you hit Ctrl+C to kill the program, or you could put a call to input() in there to make it stop when you press enter, or whatever else you like.
a few comments on the code you posted:
you only need to declare global variables if you're going to modify them
why do you have seperate functions? why not just have a single function, and just delay start()ing the second Thread
why are you setting WAVE_OUTPUT_FILENAME1 so many times? just save the start_time and end_time, then format the string in one go
you don't have to read() in chunks, if you know it's going to fit in memory just read everything all in one go
you shouldn't need to keep starting and stopping recording, just open it once in each thread and if you're lucky samples will accumulate in the buffer while you're writing the wav file to disk
something like:
import pyaudio
import wave
import time
from datetime import datetime
from threading import Thread
FORMAT = pyaudio.paInt16
CHANNELS = 2
RATE = 44100
RECORD_SECONDS = 12
def recorder(prefix):
audio = pyaudio.PyAudio()
stream = audio.open(
format=FORMAT, channels=CHANNELS,
rate=RATE, input=True,
)
try:
while True:
start_time = datetime.now()
print("recording started", start_time)
data = stream.read(RATE * RECORD_SECONDS, False)
end_time = datetime.now()
print("finished", end_time)
name = f'{prefix}{start_time:%Y-%m-%d-%H-%M-%S.%f}-{end_time:%H-%M-%S.%f}.wav'
print("writing", name)
with wave.open(name, 'wb') as waveFile:
waveFile.setnchannels(CHANNELS)
waveFile.setsampwidth(audio.get_sample_size(FORMAT))
waveFile.setframerate(RATE)
waveFile.writeframes(data)
finally:
stream.stop_stream()
stream.close()
audio.terminate()
if __name__ == '__main__':
Thread(target=recorder, args=('outputs/output_1-',)).start()
time.sleep(6)
Thread(target=recorder, args=('outputs/output_2-',)).start()
a few differences:
the version using threading is much less code!
my version allows an arbitrary number of files without using up multiple OS threads for each file (there's the Python thread and pyaudio has an internal thread looking after audio buffers)
my version saves partial files
hope all the helps / makes sense!

I want change the sound value during the processing before play it back

Im recording a signal in real time, and play it back immediately. However,
I want change the sound value during the processing before play it back.
and here is my code:
import pyaudio
import time
import numpy
import sys
import sounddevice as sd
WIDTH = 2
CHANNELS = 2
RATE = 44100
chunk=1024
p = pyaudio.PyAudio()
def callback(in_data, frame_count, time_info, status):
return (in_data, pyaudio.paContinue)
stream = p.open(format=p.get_format_from_width(WIDTH),
channels=CHANNELS,
rate=RATE,
frames_per_buffer=chunk,
input=True,
output=True,
stream_callback=callback)
stream.start_stream()
while stream.is_active():
time.sleep(0.1)
stream.stop_stream()
stream.close()
p.terminate()
I found this code, but I have a problem by connecting my code by that code,can some one show me how can I solve this problem
# This function changes the volume of a sound
# A factor > 1 increases the volume
# A factor < 1 decreases the volume
def changeVolume(sound, factor):
newSound = duplicateSound(sound)
for sample in getSamples(newSound):
value = getSampleValue(sample)
setSampleValue(sample, value * factor)
return newSound

voice recording using pyaudio

i am trying to record voice using python.
i tried to use the pyaudio module it saved a wav file on my computer but recorded a static voice.
any suggestions?
import pyaudio
import wave
CHUNK = 1024
FORMAT = pyaudio.paInt16
CHANNELS = 2
RATE = 44100
RECORD_SECONDS = 5
WAVE_OUTPUT_FILENAME = "voice.wav"
p = pyaudio.PyAudio()
stream = p.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("* done recording")
stream.stop_stream()
stream.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(frames))
wf.close()
First, make sure your microphone is actually connected, on and not muted.
You are not providing a device index when opening the stream. This means that you will get the device that PyAudio considers the default. Which might not be your microphone.
Use the get_device_count and get_device_info_by_index methods of the PyAudio object in an interactive Python session. Print the dictionaries that get_device_info_by_index returns to determine which device index represents your microphone, and provide that index number as the input_device_index parameter when opening the stream.
below code will list indexes of the available recording devices,then user can give a specific index as the input,code will start recording via given recording device index.
import pyaudio
import wave
FORMAT = pyaudio.paInt16
CHANNELS = 1
RATE = 44100
CHUNK = 512
RECORD_SECONDS = 5
WAVE_OUTPUT_FILENAME = "recordedFile.wav"
device_index = 2
audio = pyaudio.PyAudio()
print("----------------------record device list---------------------")
info = audio.get_host_api_info_by_index(0)
numdevices = info.get('deviceCount')
for i in range(0, numdevices):
if (audio.get_device_info_by_host_api_device_index(0, i).get('maxInputChannels')) > 0:
print("Input Device id ", i, " - ", audio.get_device_info_by_host_api_device_index(0, i).get('name'))
print("-------------------------------------------------------------")
index = int(input())
print("recording via index "+str(index))
stream = audio.open(format=FORMAT, channels=CHANNELS,
rate=RATE, input=True,input_device_index = index,
frames_per_buffer=CHUNK)
print ("recording started")
Recordframes = []
for i in range(0, int(RATE / CHUNK * RECORD_SECONDS)):
data = stream.read(CHUNK)
Recordframes.append(data)
print ("recording stopped")
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(Recordframes))
waveFile.close()
Your code works in my environment: Win7 and Python3.4 - I get my voice recorded using my Laptops's microphone.
Maybe your microphone's recording level is set too low. Or is it muted or disabled?
Make sure your Microphone is connected to the computer. It can be identified using below code.
import speech_recognition as sr
for index, name in enumerate(sr.Microphone.list_microphone_names()):
print("Microphone with name \"{1}\" found for microphone(device_index{0})".format(index, name))

Tkinter timer while recording

Same as in topic, i want to create timer while the recording function would run. I have tried a lot of ideas but all of them resulted as an error or just broke up program, maybe you have some ideas?
class rec(object):
def __init__(self):
FORMAT = pyaudio.paInt16
CHANNELS = 2
RATE = 44100
CHUNK = 1024
WAVE_OUTPUT_FILENAME = str(e2.get() + '.wav')
try:
RECORD_SECONDS = int(e1.get())
except ValueError:
tkMessageBox.showinfo("Error", "Please Enter number of seconds")
i = 0
while os.path.exists(WAVE_OUTPUT_FILENAME):
WAVE_OUTPUT_FILENAME = str(e2.get() + '%d.wav'%i)
i += 1
audio = pyaudio.PyAudio()
stream = audio.open(format=FORMAT, channels=CHANNELS,
rate=RATE, input=True,
frames_per_buffer=CHUNK)
frames = []
print "recording...\n\n"
for i in range(0, int(RATE / CHUNK * RECORD_SECONDS)):
sys.stdout.write('\r%i' % i)
data = stream.read(CHUNK)
frames.append(data)
print "\nfinished recording\n"
# 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.Wave_read.getnframes()
waveFile.close()
tkMessageBox.showinfo("Recorded", "Track:%s"%WAVE_OUTPUT_FILENAME)
I tried to make other function with timer and initiate that function, when i press button, but if i pressed button firstly program ran rec function, and after gone timer function, also i tried to initiate those function by pipe but it does the same, do i have to make that with threads?

Categories

Resources