I am using Adafruit I2S MEMS Microphone Breakout for recording. ref. https://learn.adafruit.com/adafruit-i2s-mems-microphone-breakout?view=all
When i wire the Mic to RPI in Mono configuration as per below image I am able record audio using arecord command and below python code
arecord -D dmic_sv -c2 -r 48000 -f S32_LE -t wav -V mono -v recording.wav
Python Code snippet:
channels=1, rate=48000, frames_per_buffer=2400
def start_recording(self):
try:
self.logger.info("start_recording()> enter")
# Use a stream with a callback in non-blocking mode
self._stream = self._pa.open(format=pyaudio.paInt32,
channels=self.channels,
rate=self.rate,
input=True,
frames_per_buffer=self.frames_per_buffer,
stream_callback=self.get_callback())
self._stream.start_stream()
self.logger.info("start_recording()> exit")
return self
except Exception, e:
self.logger.error("start_recording()>", exc_info = True)
But If I connect channel selection pin to logic high vltage i am able to record audio using arecord command but uanble to record using python code.
Any changes required in python code to record right channel mono audio?
I did something similar but using python-sounddevice. Here's my repo
EDIT:
here is the specific audio recording class for clarification
import threading
import queue
import numpy
import sounddevice as sd
import soundfile as sf
class AudioRecorder():
def __init__(self):
self.open = True
self.file_name = 'name_of_file.wav'
self.channels = 1
self.q = queue.Queue()
# Get samplerate
device_info = sd.query_devices(2, 'input')
self.samplerate = int(device_info['default_samplerate'])
def callback(self, indata, frames, time, status):
# This is called (from a separate thread) for each audio block.
if status:
print(status, file=sys.stderr)
self.q.put(indata.copy())
def record(self):
with sf.SoundFile(self.file_name, mode='x', samplerate=self.samplerate, channels=self.channels) as file:
with sd.InputStream(samplerate=self.samplerate, channels=self.channels, callback=self.callback):
while(self.open == True):
file.write(self.q.get())
EDIT 2: The code is a Python Class that creates an audio file using an I2S microphone similar to the image shown in the question. While the value self.open is true, sounddevice will write the audio data into a queue (def callback) and then write the data into a file. All you have to do is toggle self.open to start and stop recording.
Related
i bought an USB microphone and a Camera module for the raspberry pi4. I managed to record Audio and Video but when i merge them with ffmpeg the are not synchronized, i'm trying to solve this for 2 days now.
Here my code, when the program starts it record video with the instruction "camera.start_recording("Video.h264")", this instruction return immediatly so when the program executes the instruction after camera.start_recording("Video.h264") the video is still recording.
For recording Audio i use a while loop that stops when i press a button. When i press the button the video recording also stops.
After everything is written i use ffmpeg to merge the video (After converting it to mp4) and the Audio.
# coding=utf-8
from picamera import PiCamera
import pyaudio
import wave
import os
import time
import RPi.GPIO as GPIO
GPIO.setwarnings(False)
GPIO.setmode(GPIO.BOARD)
GPIO.setup(10, GPIO.IN, pull_up_down=GPIO.PUD_DOWN)
print("Ready")
go = True
camera = PiCamera()
camera.resolution = (1920, 1080)
audio = pyaudio.PyAudio()
stream = audio.open(format=pyaudio.paInt16, channels=1, rate=44100, input=True)
frames = []
camera.start_recording("Video.h264")
while go:
print("Recording")
if GPIO.input(10) == GPIO.HIGH:
camera.stop_recording()
go = False
print("Recording has been stopped")
data = stream.read(1024, exception_on_overflow = False)
frames.append(data)
stream.stop_stream()
stream.close()
audio.terminate()
sound_file = wave.open("Audio.wav", "wb")
sound_file.setnchannels(1)
sound_file.setsampwidth(audio.get_sample_size(pyaudio.paInt16))
sound_file.setframerate(44100)
sound_file.writeframes(b''.join(frames))
sound_file.close()
print("Converting in mp4")
os.system("ffmpeg -i Video.h264 Video.mp4")
print("Merging audio and video")
os.system("ffmpeg -i Video.mp4 -i Audio.wav -c:v copy -c:a aac vea.mp4")
I have written a Python script that runs on a Raspberry Pi and utilizes the PiCamera library to capture video, the Python Image Library (PIL) to extract individual frames, and then does some image processing on it using DIPLib and OpenCV. The goal of the program is to continuously capture frames of 3D printer filament and return the diameter value. The actual image processing portion works just fine- it's the frame capture that is causing me issues.
I am following the PiCamera Rapid Capture and processing tutorial from PiCamera and using the Python Threading library as they have done to ideally utilize more of th Pi's processor for processing and not getting bottlenecked and falling behind.
The implementation of this code is built to "Drop Frames" when there are not any threads available for processing. As I understand it, this should prevent the Pi from storing any extra frames in the buffer for processing, thus preventing a memory overflow (not sure if that's the correct terminology) from happening. Unfortunately this is exactly what is happening.
I am running the PiCamera at about 3 frames-per-second which gives ~10 threads the ability to process all the incoming images, that is until the memory starts to overflow. However, if I leave the script running for 5-10 minutes, the memory (as shown using htop) slowly compounds until it reaches maximum capacity- at which point the script basically drops all incoming frames.
UPDATE: here is the error it shows:
Exception has occurred: MemoryError
exception: no description
File "/home/pi/Desktop/FilamentPuller_01/pi_camera_threading_03.py", line 45, in run
img = np.array(Image.open(self.stream)
My theory is that the video recording functionality of PiCamera is holding a buffer of some sort, but I am not sure how to see it or how to stop it from doing that. I've been using VSCode on the Pi to debug, and each thread doesn't seem to holding any more data at a time than they should- essentially there should be no reason for them to compound more data from one cycle to the next as all the variables are reused.
I have included my code below, please let me know what other information I can provide to help with solving this issue. Thank you for any insight you might have
import io
import sys
import time
import threading
import cv2
import numpy as np
import os
import picamera
from PIL import Image
import csv
from diameter_detection import diameter_detection
from moving_average import MovingAverageFilter
from serial_write import serial_write
##### CAMERA SETTINGS ######
focalValue = 40 # focus
cameraResolution = (1080, 1000)
cameraZoom = (0.3,0,0.3,0.8)
cameraFrameRate = 1
# create an array for storing filtered diameter values
filtered_dia_meas = []
# create moving average filter
ma5_filter = MovingAverageFilter(2)
class ImageProcessor(threading.Thread):
def __init__(self, owner):
super(ImageProcessor, self).__init__()
self.stream = io.BytesIO()
self.event = threading.Event()
self.terminated = False
self.owner = owner
self.start()
def run(self):
# This method runs in a separate thread
while not self.terminated:
# Wait for an image to be written to the stream
if self.event.wait(1):
try:
self.stream.seek(0)
# Read the image and do some processing on it
img = np.array(Image.open(self.stream))
try:
diameter = diameter_detection(img)
except:
serial_write(0)
print('Could not read diameter, pausing and retrying...')
time.sleep(0.1)
# add the diameter to the filter
ma5_filter.step(diameter)
#filtered_dia_meas.append(ma5_filter.current_state())
# display the current filtered diameter to the Terminal
print(ma5_filter.current_state())
try:
# attempt to send the diameter to the connected serial device
serial_write(ma5_filter.current_state())
except:
print('Serial write failed!')
# Set done to True if you want the script to terminate
# at some point
#self.owner.done=True
finally:
# Reset the stream and event
self.stream.seek(0)
self.stream.truncate()
self.event.clear()
# Return ourselves to the available pool
with self.owner.lock:
self.owner.pool.append(self)
class ProcessOutput(object):
def __init__(self):
self.done = False
# Construct a pool of 10 image processors along with a lock
# to control access between threads
self.lock = threading.Lock()
self.pool = [ImageProcessor(self) for i in range(10)]
print('Threaded processes created')
self.processor = None
def write(self, buf):
if buf.startswith(b'\xff\xd8'):
# New frame; set the current processor going and grab
# a spare one
if self.processor:
self.processor.event.set()
with self.lock:
if self.pool:
self.processor = self.pool.pop()
else:
# No processor's available, we'll have to skip this frame
print('Frame Skipped!')
self.processor = None
if self.processor:
self.processor.stream.write(buf)
def flush(self):
# When told to flush (end of recording), shut
# down in an orderly fashion. First, add the current processor
# back to the pool
if self.processor:
with self.lock:
self.pool.append(self.processor)
self.processor = None
# Now, empty the pool, joining each thread as we go
while True:
with self.lock:
try:
proc = self.pool.pop()
except IndexError:
pass # pool is empty
proc.terminated = True
proc.join()
with picamera.PiCamera(resolution=cameraResolution) as camera:
print('Succesfully created camera object')
camera.framerate = cameraFrameRate
camera.zoom = (0.3,0,0.3,0.8)
# set focus motor
os.system("i2cset -y 0 0x0c %d %d" % (focalValue,0))
print('Camera focus set')
time.sleep(2)
print('Starting recording...')
output = ProcessOutput()
camera.start_recording(output, format='mjpeg')
while not output.done:
camera.wait_recording(1)
camera.stop_recording()
I'll be very detailed so there is no lack of information about what is happening
I have a class named Stream, that has the main function of reading data output from any selected audio device connected to the PC and return the data to another class (this part is not implemented in the code yet, but it's just for context). Here is the Stream class:
Stream.py
import pyaudio
import struct
from threading import Thread
from LEDController.BaseClass import BaseClass
from LEDController.Device import Device
class Stream(BaseClass):
def __init__(self, device: Device):
super().__init__(__name__)
self.__p = pyaudio.PyAudio()
self.stream = None
# Stream Settings
self.defaultFrames = 1024 * 8
self.format = pyaudio.paInt16
self.channels = device.channels
self.rate = device.defaultSampleRate
self.input = True
self.frames_per_buffer = self.defaultFrames
self.input_device_index = device.index
self.as_loopback = True
self.__open()
self.stop()
def __open(self):
try:
self.logger.info('Trying to Open Stream')
# Open Stream
self.stream = self.__p.open(
format=self.format,
channels=self.channels,
rate=self.rate,
input=self.input,
frames_per_buffer=self.defaultFrames,
input_device_index=self.input_device_index,
as_loopback=self.as_loopback,
# wasapi_fill_silence=True
)
except Exception as e:
self.logger.error(e)
else:
self.logger.info('Stream Opened')
def start(self):
try:
self.stream.start_stream()
self.read_thread = Thread(target=self.read)
self.read_thread.start()
self.logger.info("Stream Started")
return True
except Exception as e:
return self.logger.error(e)
def stop(self):
try:
self.stream.stop_stream()
self.logger.info("Stream Stopped")
return False
except Exception as e:
return self.logger.error(e)
def is_stopped(self):
return self.stream.is_stopped()
def read(self):
try:
while not self.is_stopped():
# Process Data
data = self.stream.read(self.defaultFrames) # This line doesn't return anything
data_int = struct.unpack(str(2 * self.defaultFrames) + 'h', data)
print(data_int)
# TODO return data
except Exception as e:
self.logger.error(e)
What it's doing:
At first, it receives a Device Object (code is not that important. However, I can edit and add it if needed).
The Device object has a few pieces of information about the current selected device (such as device default sample rate, channels, etc, as you can see in Stream.__init__(). So the Stream class uses those pieces of information to open a PyAudio Stream with PyAudio.open(...) and right after that, it stops the stream until the user (there is a GUI) opens it.
The Problem
The Stream.read() method it's called by a Thread, so it keeps running, processing, and returning the data in real-time. However when I call Stream.start() and it starts the read method, the same keeps stuck in the following line:
data = self.stream.read(self.defaultFrames)
I've added a print statement in this method to see the data flowing. However, this statement only gets called after stopping the stream, and no error is shown.
Output
2020-06-03 09:15:21,718 — LEDController.Stream.Stream - 57 — INFO — Stream Started
2020-06-03 09:15:24,371 — LEDController.Stream.Stream - 65 — INFO — Stream Stopped
(output of the print statement, it's quite long so I'll not put it here)
As you can see, no data is printed while the stream is open.
Looking at this output and after a few tests, I've figured out that self.stream.read(...) it's not returning any data, while the stream is opened and only returns when the stream stops.
After that conclusion, I've started looking for solutions and doing more tests, but I couldn't find anything, and only for the recorded, this problem was not happening before (it started like yesterday)
and I don't remember changing anything in my code.
Things that I've also tried, but haven't worked
Removing the Thread and calling Stream.read() normally
Reinstalling PyAudio
If you need any additional information, just tell me in the comments. I've tried to be as specific as possible and sorry if I didn't make my self clear enough. Just let me know and I'll update the question and fix the problem. Thanks in advance.
How do I stop the audio playing through playaudio module in Python code?
I have played music but I can't stop that music. How can I stop it?
playsound.playsound("name_of_file")
You can use the multiprocessing module to play the sound as a background process, then terminate it anytime you want:
import multiprocessing
from playsound import playsound
p = multiprocessing.Process(target=playsound, args=("file.mp3",))
p.start()
input("press ENTER to stop playback")
p.terminate()
Playsound is a single function module that plays sounds, and nothing else. It would seem that means it does not stop playing sounds either. From their own documentation:
The playsound module contains only one thing - the function (also named) playsound.
Personally, I like to use pyaudio. The following code is adapted from the example here. The code plays audio and has the space bar set as a pause/play button.
import pyaudio
import wave
import time
from pynput import keyboard
paused = False # global to track if the audio is paused
def on_press(key):
global paused
print (key)
if key == keyboard.Key.space:
if stream.is_stopped(): # time to play audio
print ('play pressed')
stream.start_stream()
paused = False
return False
elif stream.is_active(): # time to pause audio
print ('pause pressed')
stream.stop_stream()
paused = True
return False
return False
# you audio here
wf = wave.open('audio\\songs\\And_Your_Bird_Can_Sing_mp3_2_wav.wav', 'rb')
# instantiate PyAudio
p = pyaudio.PyAudio()
# define callback
def callback(in_data, frame_count, time_info, status):
data = wf.readframes(frame_count)
return (data, pyaudio.paContinue)
# open stream using callback
stream = p.open(format=p.get_format_from_width(wf.getsampwidth()),
channels=wf.getnchannels(),
rate=wf.getframerate(),
output=True,
stream_callback=callback)
# start the stream
stream.start_stream()
while stream.is_active() or paused==True:
with keyboard.Listener(on_press=on_press) as listener:
listener.join()
time.sleep(0.1)
# stop stream
stream.stop_stream()
stream.close()
wf.close()
# close PyAudio
p.terminate()
On windows try:
import winsound
winsound.PlaySound(r'C:\sound.wav', winsound.SND_ASYNC)
Stop Playback:
winsound.PlaySound(None, winsound.SND_PURGE)
#pip install pygame
from pygame import mixer
import time
mixer.init() #Initialzing pyamge mixer
mixer.music.load('lovingly-618.mp3') #Loading Music File
mixer.music.play() #Playing Music with Pygame
time.sleep(5)
mixer.music.stop()
Here is a much easier way:
wmp = win32com.client.dynamic.Dispatch("WMPlayer.OCX")
wmp.settings.autoStart = True
wmp.settings.volume = 100
wmp.URL = file
while globals()["allowSound"]:
PumpWaitingMessages()
You can change globals()["allowSound"] from another thread and set it to false when the audio has ended (you can get the length of the audio with wmp.durationString)
Here is some more info about this: Windows Media Player COM Automation works from VBS but not from Python
Although this does not use the playsound module it is a good alternative.
After successful execution of the program, audio file will start playing. Now Click on terminal, once you see the cursor, type Ctrl+C, it will bring you out of the terminal and audio will also stop playing.
Programming Instructions used:
from playsound import playsound
playsound('//path//to//a//sound//file//you//want//to//play.mp3')
EASY......
on your terminal tab, look for kill(delete) option on the right hand side.
click the delete option and it will stop playing
I am trying understand how stream.is_active() works. say the wave file reader example from the Documentation:
"""PyAudio Example: Play a wave file (callback version)."""
import pyaudio
import wave
import time
import sys
if len(sys.argv) < 2:
print("Plays a wave file.\n\nUsage: %s filename.wav" % sys.argv[0])
sys.exit(-1)
wf = wave.open(sys.argv[1], 'rb')
# instantiate PyAudio (1)
p = pyaudio.PyAudio()
# define callback (2)
def callback(in_data, frame_count, time_info, status):
data = wf.readframes(frame_count)
return (data, pyaudio.paContinue)
# open stream using callback (3)
stream = p.open(format=p.get_format_from_width(wf.getsampwidth()),
channels=wf.getnchannels(),
rate=wf.getframerate(),
output=True,
stream_callback=callback)
# start the stream (4)
stream.start_stream()
# wait for stream to finish (5)
while stream.is_active():
time.sleep(0.1)
# stop stream (6)
stream.stop_stream()
stream.close()
wf.close()
# close PyAudio (7)
p.terminate()
So I am not sure what is the condition to make is_active() returns False. If wf.readframes ran out will it return 0 or error to data? If 0 is the indicator what if I actually what 0 to be in my data. In the pyaudio.py, is_active() is defined:
def is_active(self):
"""
Returns whether the stream is active.
:rtype: bool
"""
return pa.is_stream_active(self._stream)
But I am not able to go deeper to portaudio (pa)'s is_stream_active().
The portaudio documentation says:
A stream is active after a successful call to Pa_StartStream(), until it becomes inactive either as a result of a call to Pa_StopStream() or Pa_AbortStream(), or as a result of a return value other than paContinue from the stream callback. In the latter case, the stream is considered inactive after the last buffer has finished playing.
http://portaudio.com/docs/v19-doxydocs/portaudio_8h.html#a1f8709c4971932643681a6f374c4bb5a