I'm trying to record sound from mic. Firstly as used PyAudio then sounddevice but both failed.
Here is code for PyAudio:
import pyaudio
def _recording_loop(samples_queue, running, stream, chunk_size):
stream.start_stream()
while running.is_set():
samples_queue.put(stream.read(chunk_size))
stream.stop_stream()
class Recoder:
def __init__(self, frame_rate, period):
self.proc = None
self.running = Event()
self.samples_queue = Queue()
self.frame_rate = frame_rate
self.chunk_size = (frame_rate*period) / 1000
self.channels = 1
self._pa = pyaudio.PyAudio()
self._stream = None
def start(self):
if self.proc is None:
self._stream = self._pa.open(format=pyaudio.paInt8,
channels=self.channels,
rate=self.frame_rate,
input=True,
frames_per_buffer=self.chunk_size)
self.running.set()
self.proc = Process(target=_recording_loop, args=[self.samples_queue, self.running, self._stream,
self.chunk_size])
self.proc.start()
def stop(self):
if self.proc is not None:
self.running.clear()
self.proc.join()
self._stream.close()
self._pa.terminate()
def empty(self):
return self.samples_queue.empty()
def read(self):
res = []
while not self.samples_queue.empty():
res.append(self.samples_queue.get())
return res
It gives me a warning:
Python[21648:645093] 13:42:01.242 WARNING: 140: This application, or a library it uses, is using the deprecated Carbon Component Manager for hosting Audio Units. Support for this will be removed in a future release. Also, this makes the host incompatible with version 3 audio units. Please transition to the API's in AudioComponent.h.
and nothing is ever recorded.
As I understand it's something with El Capitan and not solved yet. But maybe I'm wrong?
So I decided to switch library to sounddevice:
from multiprocessing import Process, Queue, Event
import sounddevice as sd
def _recording_loop(samples_queue, running, frame_rate, chunk_size):
while running.is_set():
samples_queue.put(sd.rec(chunk_size, samplerate=frame_rate, channels=1,
dtype='int8', blocking=True))
class Recoder:
def __init__(self, frame_rate, period):
self.proc = None
self.running = Event()
self.samples_queue = Queue()
self.frame_rate = frame_rate
self.period = period
self.chunk_size = (frame_rate * period) / 1000
def start(self):
if self.proc is None:
self.running.set()
self.proc = Process(target=_recording_loop, args=[self.samples_queue, self.running, self.frame_rate,
self.chunk_size])
self.proc.start()
def stop(self):
if self.proc is not None:
self.running.clear()
self.proc.join()
def empty(self):
return self.samples_queue.empty()
def read(self):
res = []
while not self.samples_queue.empty():
res.append(self.samples_queue.get())
return res
And it says:
||PaMacCore (AUHAL)|| Warning on line 530: err=''who?'', msg=Audio Hardware: Unknown Property
||PaMacCore (AUHAL)|| Warning on line 534: err=''who?'', msg=Audio Hardware: Unknown Property
||PaMacCore (AUHAL)|| Warning on line 445: err=''who?'', msg=Audio Hardware: Unknown Property
And again nothing is recorded. What I'm doing wrong?
sounddevice.rec() is not meant to be used like that. You just call it with the number of frames you want to record and that's it (see the example from the docs):
import sounddevice as sd
fs = 44100
duration = 10 # seconds
myrecording = sd.rec(duration * fs, samplerate=fs, channels=2,
blocking=True)
That's it. You don't need half a page of code just to record some sound.
BTW, you can ignore the warning for now, see https://github.com/spatialaudio/python-sounddevice/issues/10.
Yesterday I ran into similar problem. It seems that it's caused by using multiprocessing with sounddevice. When I do import sounddevice at the top of the module I get ||PaMacCore (AUHAL)|| Warning on line 530: err=''who?'', msg=Audio Hardware: Unknown Property and then the app just hangs while creating sounddevice.RawInputStream. When I import sounddevice in my run() method (I am creating a new class based on multiprocessing.Process) it works fine. For me it seems that sounddevice does something to initialise itself right after being imported and this must happen in the same process that will use it.
Edit: instead of Multiprocessing use Threading.
Related
I am working on a script that reads a rtps stream from a camera. The problem is that sometimes the connection is not perfect and the frames take a while to arrive.
From what I have found the read function from cv2.VideoCapture does not have a timeout that we can modify without recompiling, and the default (30 seconds) is way too much for what I need.
I tried two approaches, one using threading and the other using multiprocessing.
The former didn't work as expected since I cannot kill the thread fast enough and the script dies. the latter means that I am creating and destroying processes at a rate of 1/fps when everything is working, which I don't think is a good idea.
The following is a minimum working example. When proc = True, it uses multiprocessing, and when proc = False, it uses threading. the delay of the read function can be mimicked using TIMESLEEP > 0
import cv2
import time
import queue
import psutil
import threading
import multiprocessing as mp
TIMESLEEP = 0
class FrameThread(threading.Thread):
def __init__(self, func, res):
super().__init__()
self.daemon = True
self.res = res
self.func = func
def run(self):
time.sleep(TIMESLEEP)
self.res.put(self.func)
def putframe(func, res):
time.sleep(TIMESLEEP)
res.put(func)
class Test(object):
def __init__(self, url, proc = True):
self.url = url
self.black = [1, 2, 3]
self.fps = 10
self.proc = proc
self._rq = mp.Queue() if self.proc else queue.Queue()
def _timeout_func(self, func, timeout = 10):
if self.proc:
_proc = mp.Process(target = putframe, args = (func, self._rq))
_proc.start()
else:
FrameThread(func, self._rq).start()
try:
t1 = time.time()
ret, frame = self._rq.get(block = True, timeout = timeout)
diff_fps = 1 / self.fps - (time.time() - t1)
time.sleep(diff_fps if diff_fps > 0 else 0)
if self.proc:
_proc.terminate()
frame = frame if ret else self.black.copy()
except queue.Empty:
diff_fps = 1 / self.fps - timeout
time.sleep(diff_fps if diff_fps > 0 else 0)
if self.proc:
_proc.terminate()
ret, frame = True, self.black.copy()
return ret, frame
def run(self):
cap = cv2.VideoCapture(self.url)
while True:
ret, frame = self._timeout_func(cap.read(), timeout = 0.1)
if not ret:
break
print(self.proc if self.proc else len(psutil.Process().threads()), end='\r')
proc = False
test = Test('./video.mp4', proc = proc)
test.run()
Do you guys have any other idea or approach to do this? or any improvement on the above code?
Thanks!
Not tried this sort of script but I saw a similar kind of question and I would suggest you to use the e VLC python bindings (you can install it with pip install python-vlc) and play the stream:
import vlc
player=vlc.MediaPlayer('rtsp://:8554/output.h264')
player.play()
Then take a snapshot every second or so:
while 1:
time.sleep(1)
player.video_take_snapshot(0, '.snapshot.tmp.png', 0, 0)
And then you can use SimpleCV or something for processing (just load the image file '.snapshot.tmp.png' into your processing library).
I want to stream mp3 file from web using python PyQt5.I have researched a lot and only found code for streaming wav file.
from PyQt5.QtWidgets import *
from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.QtMultimedia import *
import urllib.request
import threading
import time
class Streamer:
def __init__(self,url):
self.url = url
self.fancy = urllib.request.URLopener()
self.web = self.fancy.open(self.url)
self.content_len = int(self.web.headers["Content-Length"])
self.data = self.web.read(1024*1024)
self.buffer = QBuffer()
self.buffer.writeData(self.data[250:])
self.buffer.open(QBuffer.ReadWrite)
threading.Thread(target=self.stream).start()
self.format = QAudioFormat()
self.format.setSampleRate(48000)
self.format.setChannelCount(2)
self.format.setSampleSize(16)
self.format.setCodec("audio/pcm")
self.format.setByteOrder(QAudioFormat.LittleEndian)
self.format.setSampleType(QAudioFormat.SignedInt)
self.audio = QAudioOutput(self.format)
self.audio.start(self.buffer)
def stream(self):
# while True:
# self.sound_data = self.web.read(1024*1024)
# if not self.sound_data:
# break
# self.buffer.buffer().append(self.sound_data)
# time.sleep(2)
while len(self.data) < self.content_len:
self.sound_data = self.web.read(1024*1024)
self.buffer.buffer().append(self.sound_data)
self.data+=self.sound_data
time.sleep(2)
self.buffer.buffer().clear()
del self.data
if __name__ == "__main__":
app = QApplication([])
streamer = Streamer("https://raw.githubusercontent.com/PremKumarMishra/Stream-Songs/main/Audio.wav")
app.exec_()
I checked but cant add MPEG-3(mp3 codec) codec in QAudioFormat.So this current code does not work for mp3.
The basic behavior of QMediaPlayer should be enough to manage buffering of simple audio streams, as the backend will consider the 100% of buffer size as enough to guarantee playing.
In case you want more control over the buffer state, you need to implement a custom QIODevice subclass to act as a middle layer between QMediaPlayer and the download process.
In the following example I'm using QNetworkAccessManager to download the stream, the readyRead signal of the QNetworkReply is then connected to a function that reads the raw bytes, and emits a buffer status considering the current available read data and the minimum size set for the buffer.
The first time the received data has reached the minimum size, it begins to emit the readyRead signal, and if the player has not been started yet (no media set), it sets the media using the Buffer instance, and is then ready to play.
from PyQt5 import QtCore, QtWidgets, QtNetwork, QtMultimedia
url = 'https://url.to/stream'
Errors = {}
for k, v in QtMultimedia.QMediaPlayer.__dict__.items():
if isinstance(v, QtMultimedia.QMediaPlayer.Error):
Errors[v] = k
class Buffer(QtCore.QIODevice):
buffering = QtCore.pyqtSignal(object, object)
fullBufferEmitted = False
def __init__(self, reply, minBufferSize=250000):
super().__init__()
self.minBufferSize = max(200000, minBufferSize)
self.reply = reply
self.data = bytes()
# the network reply is on another thread, use a mutex to ensure that
# no simultaneous access is done in the meantime
self.mutex = QtCore.QMutex()
# this is important!
self.setOpenMode(self.ReadOnly|self.Unbuffered)
self.reply.readyRead.connect(self.dataReceived)
def dataReceived(self):
self.mutex.lock()
self.data += self.reply.readAll().data()
dataLen = len(self.data)
self.mutex.unlock()
self.buffering.emit(dataLen, self.minBufferSize)
if not self.fullBufferEmitted:
if dataLen < self.minBufferSize:
return
self.fullBufferEmitted = True
self.readyRead.emit()
def isSequential(self):
return True
def readData(self, size):
self.mutex.lock()
data = self.data[:size]
self.data = self.data[size:]
self.mutex.unlock()
return data
def bytesAvailable(self):
return len(self.data) + super().bytesAvailable()
class Player(QtWidgets.QWidget):
def __init__(self):
super().__init__()
layout = QtWidgets.QVBoxLayout(self)
self.playButton = QtWidgets.QPushButton('Play', enabled=False)
layout.addWidget(self.playButton)
self.volumeSlider = QtWidgets.QSlider(QtCore.Qt.Horizontal)
layout.addWidget(self.volumeSlider)
self.statusLabel = QtWidgets.QLabel('Waiting')
self.statusLabel.setFrameShape(
self.statusLabel.StyledPanel|self.statusLabel.Sunken)
layout.addWidget(self.statusLabel)
self.player = QtMultimedia.QMediaPlayer(volume=16)
self.volumeSlider.setValue(self.player.volume())
self.networkManager = QtNetwork.QNetworkAccessManager()
self.url = QtCore.QUrl(url)
self.media = QtMultimedia.QMediaContent(self.url)
reply = self.networkManager.get(QtNetwork.QNetworkRequest(self.url))
self.buffer = Buffer(reply)
self.playButton.clicked.connect(self.play)
self.volumeSlider.valueChanged.connect(self.player.setVolume)
self.player.error.connect(self.error)
self.buffer.buffering.connect(self.buffering)
def error(self, error):
errorStr = 'Error: {} ({})'.format(
Errors.get(error, 'Unknown error'), int(error))
self.statusLabel.setText(errorStr)
print(errorStr)
def buffering(self, loaded, minBufferSize):
self.statusLabel.setText('Buffer: {}%'.format(int(loaded / minBufferSize * 100)))
if self.player.media().isNull() and loaded >= minBufferSize:
self.player.setMedia(self.media, self.buffer)
self.playButton.setEnabled(True)
self.playButton.setFocus()
self.statusLabel.setText('Ready to play')
def play(self):
if self.player.state() == self.player.PlayingState:
self.player.pause()
self.playButton.setText('Play')
else:
self.player.play()
self.playButton.setText('Pause')
app = QtWidgets.QApplication([])
w = Player()
w.show()
app.exec_()
Note that:
as soon as QMediaPlayer begins to read the stream, the buffer length will obviously become smaller, as there's no way to know or control how the backend access the stream: when the player is reading (which doesn't mean it's playing), it will read the stream anyway;
due to the reason above, the shown buffer size is only "guessed" as soon as the media is set, based on the data read and the data received from the network reply;
you might want to control the media player status in case the buffer goes too low (but you must consider what explained above), and eventually pause it;
I'm developing a screen recorder with Tkinter and Pyaudio but i'm encountering some issues:I can not record more than 10 seconds.For example if i try to record 1 minute of video, i can only get 10 seconds.
I'll leave my code down bellow.I didn't saw any errors in the command line.I made comments in my code so that you can understand what is happening:
"""
recordFile.py records audio from the default microphone in a background
thread using pyaudio.
"""
import pyaudio
import wave
import threading
import time
import subprocess
from tkinter import messagebox
CHUNK = 1024
FORMAT = pyaudio.paInt16
CHANNELS = 2
RATE = 44100
RECORD_SECONDS = 5
# WAVE_OUTPUT_FILENAME = "tmp/tmp.wav"
class recorder:
def __init__(self):
self.going = False # is the process running?
self.process = None # stores a reference to the background thread
self.filename = "" # the name of the file to record to
self.p = pyaudio.PyAudio()
self.devices = [None]
self.error = False
def record(self, filename):
# end the process before starting a new one
if self.process and self.process.is_alive():
self.going = False
self.error = False
# start a recording thread
self.process = threading.Thread(target=self._record)
self.process.start()
self.filename = filename
def _record(self):
try:
# initialize pyaudio
streams = []
frames = [] # stores audio data
for i in range(len(self.devices)):
streams.append(self.p.open(format=FORMAT,
channels=CHANNELS,
rate=RATE,
input=True,
frames_per_buffer=CHUNK,
input_device_index=self.devices[i]))
frames.append([])
print("* recording")
self.going = True # let the system know that we are running
while self.going: # stream the audio into "frames"
for i in range(len(self.devices)):
data = streams[i].read(CHUNK)
frames[i].append(data)
print("* done recording")
# stop recording
for i in range(len(self.devices)):
streams[i].stop_stream()
streams[i].close()
# write the audio data to a file (tmp/tmp.wav)
for i in range(len(self.devices)):
wf = wave.open(
self.filename[:self.filename.find(".")] + "_" + str(i) + self.filename[self.filename.find("."):],
'wb')
wf.setnchannels(CHANNELS)
wf.setsampwidth(self.p.get_sample_size(FORMAT))
wf.setframerate(RATE)
wf.writeframes(b''.join(frames[i]))
wf.close()
except Exception as e:
self.error = True
messagebox.showerror("AUDIO ERROR", "ERROR ENCOUNTERED RECORDING AUDIO: " + str(e))
def getDeviceCount(self):
return self.p.get_device_count()
def getDeviceName(self, deviceID):
return self.p.get_device_info_by_index(deviceID)["name"]
def isInputDevice(self, deviceID):
return int(self.p.get_device_info_by_index(deviceID)["maxInputChannels"]) > 0
def getAPIName(self, deviceID):
return self.p.get_host_api_info_by_index(self.p.get_device_info_by_index(deviceID)["hostApi"])["name"]
def setToDefault(self):
self.devices = [None]
def setToDevices(self, devices):
self.devices = devices
def stop_recording(self):
self.going = False
def destroy(self):
self.p.terminate()
I'm using the picamera library to create a PiCamerIcrcularIO stream to write the recording data to. I can't seem to get a reliable method to always create a video file with a 6 second length to it. Depending on when I copy the video data to disk I can end up with anywhere between 3 to 6 seconds of video. Here is the code I am using to test this with:
"""Testing the video recording settings"""
import datetime as dt
import os
from random import randint
import subprocess
import sys
import picamera
class Camera(object):
"""Camera device"""
def __init__(self):
self.device = None # type: picamera.PiCamera
self.stream = None # type: picamera.PiCameraCircularIO
def initialize_camera(self):
"""Initializes the camera to 1280x720 and start recording video to a circular buffer"""
print "initializing camera"
self.device = picamera.PiCamera()
# set the camera's resolution (1280, 720)
self.device.resolution = (1280, 720)
# set the camera's framerate
self.device.framerate = 24
self.stream = picamera.PiCameraCircularIO(self.device, seconds=8)
self.device.start_recording(self.stream, format='h264')
return
def stop_camera(self):
"""Stop the recording and turn the camera off"""
if self.device is not None:
if self.device.recording:
self.device.stop_recording()
self.device.close()
if self.stream is not None:
self.stream = None
return
def capture_video(self):
"""Store the video from the circular buffer to disk"""
try:
if self.device is None:
# get the camera set up
self.initialize_camera()
video_name = dt.datetime.now().strftime(
'%m-%d-%Y_%H:%M:%S').replace(':', '-') + '.h264'
video_path = "/home/pi"
# copy the buffer to disk
print os.path.join(video_path, video_name)
self.stream.copy_to(os.path.join(video_path, video_name), seconds=6)
# add the MP4 wrapper to the h264 file using the MP4Box program
mp4_command = "MP4Box -add '{0}' '{1}.mp4' -fps 24".format(
os.path.join(video_path, video_name),
os.path.join(video_path, os.path.splitext(os.path.basename(video_name))[0]))
subprocess.call([mp4_command], shell=True)
# remove the original h264 file
os.remove(os.path.join(video_path, video_name))
except BaseException as err:
print 'error: {0} on line {1}\r'.format(err, sys.exc_info()[-1].tb_lineno)
return
def main():
"""Main program"""
camera = Camera()
camera.initialize_camera()
camera.device.wait_recording(timeout=randint(9, 20))
camera.capture_video()
camera.stop_camera()
return
if __name__ == '__main__':
main()
sys.exit(0)
I'm using the random integer to simulate different potential points that I could be attempting to capture the recorded data to disk. I have also tried extending/reducing the seconds parameter on the PiCameraCircularIO object and specifically adding the intra_period, quality, or bitrate parameters to the start_recording function without seeming to affect the overall result. Is there something silly I'm overlooking?
I'm having some problems and I cannot seem to get my head around the concept.
What I am trying to do is this:
Have the microphone "listen" for voiced (above a particular threshold) and then start recording to a .wav file until the person has stopped speaking / the signal is no longer there. For example:
begin:
listen() -> nothing is being said
listen() -> nothing is being said
listen() -> VOICED - _BEGIN RECORDING_
listen() -> VOICED - _BEGIN RECORDING_
listen() -> UNVOICED - _END RECORDING_
end
I want to do this also using "threading" so a thread would be created that "listens" to the file constantly, and, another thread will begin when there is voiced data.. But, I cannot for the life of me figure out how I should go about it.. Here is my code so far:
import wave
import sys
import threading
from array import array
from sys import byteorder
try:
import pyaudio
CHECK_PYLIB = True
except ImportError:
CHECK_PYLIB = False
class Audio:
_chunk = 0.0
_format = 0.0
_channels = 0.0
_rate = 0.0
record_for = 0.0
stream = None
p = None
sample_width = None
THRESHOLD = 500
# initial constructor to accept params
def __init__(self, chunk, format, channels, rate):
#### set data-types
self._chunk = chunk
self.format = pyaudio.paInt16,
self.channels = channels
self.rate = rate
self.p = pyaudio.PyAudio();
def open(self):
# print "opened"
self.stream = self.p.open(format=pyaudio.paInt16,
channels=2,
rate=44100,
input=True,
frames_per_buffer=1024);
return True
def record(self):
# create a new instance/thread to record the sound
threading.Thread(target=self.listen).start();
def is_silence(snd_data):
return max(snd_data) < THRESHOLD
def listen(self):
r = array('h')
while True:
snd_data = array('h', self.stream.read(self._chunk))
if byteorder == 'big':
snd_data.byteswap()
r.extend(snd_data)
return sample_width, r
I'm guessing that I could record "5" second blocks, and, then if the block is deemed as "voiced" then it the thread should be started until all the voice data has been captured. However, because at current it's at while True: i don't want to capture all of the audio up until there are voiced commands, so e.g. "no voice", "no voice", "voice", "voice", "no voice", "no voice" i just want the "voice" inside the wav file.. Anyone have any suggestions?
Thank you
EDIT:
import wave
import sys
import time
import threading
from array import array
from sys import byteorder
from Queue import Queue, Full
import pyaudio
CHUNK_SIZE = 1024
MIN_VOLUME = 500
BUF_MAX_SIZE = 1024 * 10
process_g = 0
def main():
stopped = threading.Event()
q = Queue(maxsize=int(round(BUF_MAX_SIZE / CHUNK_SIZE)))
listen_t = threading.Thread(target=listen, args=(stopped, q))
listen_t.start()
process_g = threading.Thread(target=process, args=(stopped, q))
process_g.start()
try:
while True:
listen_t.join(0.1)
process_g.join(0.1)
except KeyboardInterrupt:
stopped.set()
listen_t.join()
process_g.join()
def process(stopped, q):
while True:
if stopped.wait(timeout = 0):
break
print "I'm processing.."
time.sleep(300)
def listen(stopped, q):
stream = pyaudio.PyAudio().open(
format = pyaudio.paInt16,
channels = 2,
rate = 44100,
input = True,
frames_per_buffer = 1024
)
while True:
if stopped and stopped.wait(timeout=0):
break
try:
print process_g
for i in range(0, int(44100 / 1024 * 5)):
data_chunk = array('h', stream.read(CHUNK_SIZE))
vol = max(data_chunk)
if(vol >= MIN_VOLUME):
print "WORDS.."
else:
print "Nothing.."
except Full:
pass
if __name__ == '__main__':
main()
Now, after every 5 seconds, I need the "process" function to execute, and then process the data (time.delay(10) whilst it does this and then start the recording back up..
Having spent some time on it, I've come up with the following code that seems to be doing what you need, except writing to file:
import threading
from array import array
from Queue import Queue, Full
import pyaudio
CHUNK_SIZE = 1024
MIN_VOLUME = 500
# if the recording thread can't consume fast enough, the listener will start discarding
BUF_MAX_SIZE = CHUNK_SIZE * 10
def main():
stopped = threading.Event()
q = Queue(maxsize=int(round(BUF_MAX_SIZE / CHUNK_SIZE)))
listen_t = threading.Thread(target=listen, args=(stopped, q))
listen_t.start()
record_t = threading.Thread(target=record, args=(stopped, q))
record_t.start()
try:
while True:
listen_t.join(0.1)
record_t.join(0.1)
except KeyboardInterrupt:
stopped.set()
listen_t.join()
record_t.join()
def record(stopped, q):
while True:
if stopped.wait(timeout=0):
break
chunk = q.get()
vol = max(chunk)
if vol >= MIN_VOLUME:
# TODO: write to file
print "O",
else:
print "-",
def listen(stopped, q):
stream = pyaudio.PyAudio().open(
format=pyaudio.paInt16,
channels=2,
rate=44100,
input=True,
frames_per_buffer=1024,
)
while True:
if stopped.wait(timeout=0):
break
try:
q.put(array('h', stream.read(CHUNK_SIZE)))
except Full:
pass # discard
if __name__ == '__main__':
main()
Look here:
https://github.com/jeysonmc/python-google-speech-scripts/blob/master/stt_google.py
It even converts Wav to flac and sends it to the google Speech api , just delete the stt_google_wav function if you dont need it ;)