I recently got the camera module for the Raspberry Pi. Working through their circular buffer example found here (code shown below.)
My goal is to save the 20 second buffer built in "stream = picamera.PiCameraCircularIO(camera, seconds=20)" but also to continue to record for 30 seconds. Below the main thing I have added to their example is "time.sleep(30)" following the GPIO input on Pin 17. When I run this it sometimes produces a file but that file is never playable. I'd appreciate any advice or suggestions you have to offer.
Code:
import io
import time
import picamera
import RPi.GPIO as GPIO
GPIO.setmode(GPIO.BCM)
GPIO.setup(17, GPIO.IN, GPIO.PUD_UP)
with picamera.PiCamera() as camera:
stream = picamera.PiCameraCircularIO(camera, seconds=20)
camera.start_recording(stream, format='h264')
GPIO.wait_for_edge(17, GPIO.FALLING)
time.sleep(30)
camera.stop_recording()
for frame in stream.frames:
if frame.header:
stream.seek(frame.position)
break
with io.open('/home/pi/Desktop/video.h264', 'wb') as output:
while True:
data = stream.read1()
if not data:
break
output.write(data)
I think that a better method is to replace:
time.sleep(30)
by
camera.wait_recording(30)
You are starting a Circular buffer correctly which will hold 20 seconds of video.
The last 20 seconds of video. Waiting 30 seconds however is futile however as it will only ever hold 20 seconds of video.
If you read the picamera docs (picamera.readthedocs.org) in the advanced recipes they show you how to use split_recording to preserve the contents of the circular buffer which you can then write while a second IO (a file or stream) records what is currently happening.
NOW you camera.wait_recording(30) and then split_recording back to another IO (in the advanced recipe it is the original, truncated, CircularIO).
At the end of this you will have two files. One containing the buffer i.e. 20 seconds before and another containing the 30 seconds after. Then you concat these two together and voila, you now have a 50 second video. Mp4Box does this well enough.
Now I have been trying very hard to use io.open to concatenate these two streams on the fly but I suspect that there are some header details/info that get messed with when you io.open('blah.h264', 'ab') which mean that you only get the last appended video. I don't suppose anyone reading this is an h264 encoding boffin?
Related
I have an Arducam 2MP plus camera wired to a raspberry pi Pico 2040 running circuitpython, I'm trying to take and store an image onto the Pico (just one), however from what I can tell the camera takes an image but doesn't return any data back to the Pico.
The code I have is modified from the Arducam GitHub example project where mine tries to save the data not sent it over usb cdc (link for the original GitHub post: https://github.com/ArduCAM/PICO_SPI_CAM).
This is the code I have at the moment:
import time as utime
import busio
import board
from Arducam import *
from board import *
once_number=128
mode = 0
start_capture = 1
stop_flag=0
data_in=0
value_command=0
flag_command=0
buffer=bytearray(once_number)
i2c = busio.I2C(board.GP27, board.GP26)
mycam = ArducamClass(OV2640, i2c)
mycam.Camera_Detection()
mycam.Spi_Test()
mycam.Camera_Init()
utime.sleep(1)
mycam.clear_fifo_flag()
mycam.OV2640_set_JPEG_size(OV2640_800x600)
mycam.set_format(JPEG)
mycam.OV2640_set_Light_Mode(Auto)
mycam.OV2640_set_Color_Saturation(Saturation2)
mycam.OV2640_set_Brightness(Brightness2)
mycam.OV2640_set_Contrast(Contrast2)
mycam.OV2640_set_Special_effects(Normal)
def read_fifo_burst():
count=0
lenght=mycam.read_fifo_length()
mycam.SPI_CS_LOW()
mycam.set_fifo_burst()
file = open("file.txt", 'a') # I know this is a txt not jpeg, its so I can see what's being outputed
while True:
mycam.spi.readinto(buffer,start=0,end=once_number)
file.write(buffer)
utime.sleep(0.00015)
count+=once_number
if count+once_number>lenght:
count=lenght-count
mycam.spi.readinto(buffer,start=0,end=count)
file.write(buffer)
mycam.SPI_CS_HIGH()
mycam.clear_fifo_flag()
break
while True:
if start_capture==1:
mycam.flush_fifo();
mycam.clear_fifo_flag();
mycam.start_capture();
start_capture=0
print("here")
if mycam.get_bit(ARDUCHIP_TRIG,CAP_DONE_MASK)!=0: # this line here
read_fifo_burst()
print("done")`
The line labeled 'this line here' doesn't seem to turn to a value that's not 0, if I remove this line I get (this is a printed output, not from the file, that wouldn't open properly):
bytearray(b'\x80\x1b\x04tf\x02\x04\x00\x19\x00\x04\x07\xa0\x10\x18\xc1\r#\x00\x04$\x00\x80\x84\x00\x04(7T\x91\x10X\teB\x00\x00\x01\x81\x01\x18\x00\x16\x00\x00\x10\x08\x00b\x00[*\x80\x80\x00\x00D\x82\x04\x00\x87\xc2\x10\x98\x05\x00BJ\x04\x18\x90\x00\x04 #\x00\x00\t\x10\x04\x00\x0cF#\x03X"\x02f\xa0\x12\x80\x00\x00\x81#*#\x02\x80\x12\x00#\x002F\x11\t\t \x04\x00q\x06\x10\x00\x00\x01\x04\x10\x04\x08 \xc0')
which when I try to decode I get:
UnicodeDecodeError: 'utf-8' codec can't decode byte 0x80 in position 0: invalid start byte
Does anyone know why the camera isn't sending any image data back (unless the return above is an image?) or why the get_bit() doesnt change from 0?
Also, if anyone has a better way for this to be done please share.
Thanks!
I suggest you to try the followings. I hope they help you:
Make sure the hardware/code requirements are satisfied.
Make sure the SPI is activated whenever you take a photo.
Check the correct pinout and Clock configurations (for instance using STM32CubeIDE. The CAM may have a problem with high-frequency SPI lines and work fine with lower frequencies.
Try to monitor SPI-related pins (MISO, MOSI, CK, NSS) by oscilloscope. It is important to have correct physical signals on the mentioned lines according to the CAM catalog.
Either Pull-up (for MISO, MOSI, and NSS) or pull-down (for CK) resistors must be used with the correct values (kOhms).
The Question
I want to load an audio file of any type (mp3, m4a, flac, etc) and write it to an output stream.
I tried using pydub, but it loads the entire file at once which takes forever and runs out of memory easily.
I also tried using python-vlc, but it's been unreliable and too much of a black box.
So, how can I open large audio files chunk-by-chunk for streaming?
Edit #1
I found half of a solution here, but I'll need to do more research for the other half.
TL;DR: Use subprocess and ffmpeg to convert the file to wav data, and pipe that data into np.frombuffer. The problem is, the subprocess still has to finish before frombuffer is used.
...unless it's possible to have the pipe written to on 1 thread while np reads it from another thread, which I haven't tested yet. For now, this problem is not solved.
I think the python package https://github.com/irmen/pyminiaudio can be of helpful. You can stream an audio file like this
import miniaudio
audio_path = "my_audio_file.mp3"
target_sampling_rate = 44100 #the input audio will be resampled a this sampling rate
n_channels = 1 #either 1 or 2
waveform_duration = 30 #in seconds
offset = 15 #this means that we read only in the interval [15s, duration of file]
waveform_generator = miniaudio.stream_file(
filename = audio_path,
sample_rate = target_sampling_rate,
seek_frame = int(offset * target_sampling_rate),
frames_to_read = int(waveform_duration * target_sampling_rate),
output_format = miniaudio.SampleFormat.FLOAT32,
nchannels = n_channels)
for waveform in waveform_generator:
#do something with the waveform....
I know for sure that this works on mp3, ogg, wav, flac but for some reason it does not on mp4/acc and I am actually looking for a way to read mp4/acc
I am trying to find a way in python to play a section of an audio file given a start and end time.
For example, say I have an audio file that is 1 min in duration. I want to play the section from 0:30 to 0:45 seconds.
I do not want to process or splice the file, only playback of the given section.
Any suggestions would be greatly appreciated!
Update:
I found a great solution using pydub:
https://github.com/jiaaro/pydub
from pydub import AudioSegment
from pydub.playback import play
audiofile = #path to audiofile
start_ms = #start of clip in milliseconds
end_ms = #end of clip in milliseconds
sound = AudioSegment.from_file(audiofile, format="wav")
splice = sound[start_ms:end_ms]
play(splice)
step one is to get your python to play entire audio file ... several libraries are available for this ... see if the library has a time specific api call ... you can always roll up your sleeves and implement this yourself after you read the audio file into a buffer or possibly stream the file and stop streaming at end of chosen time section
Another alternative is to leverage command line tools like ffmpeg which is the Swiss Army Knife of audio processing ... ffmpeg has command line input parms to do time specific start and stop ... also look at its sibling ffplay
Similar to ffplay/ffmpeg is another command line audio tool called sox
Use PyMedia and Player. Look at the functions SeekTo() and SeekEndTime(). I think you will be able to find a right solution after playing around with these functions.
I always have trouble installing external libraries and if you are running your code on a server and you don't have sudo privileges then it becomes even more cumbersome. Don't even get me started on ffmpeg installation.
So, here's an alternative solution with scipy and native IPython that avoids the hassle of installing some other library.
from scipy.io import wavfile # to read and write audio files
import IPython #to play them in jupyter notebook without the hassle of some other library
def PlayAudioSegment(filepath, start, end, channel='none'):
# get sample rate and audio data
sample_rate, audio_data = wavfile.read(filepath) # where filepath = 'directory/audio.wav'
#get length in minutes of audio file
print('duration: ', audio_data.shape[0] / sample_rate / 60,'min')
## splice the audio with prefered start and end times
spliced_audio = audio_data[start * sample_rate : end * sample_rate, :]
## choose left or right channel if preferred (0 or 1 for left and right, respectively; or leave as a string to keep as stereo)
spliced_audio = spliced_audio[:,channel] if type(channel)==int else spliced_audio
## playback natively with IPython; shape needs to be (nChannel,nSamples)
return IPython.display.Audio(spliced_audio.T, rate=sample_rate)
Use like this:
filepath = 'directory_with_file/audio.wav'
start = 30 # in seconds
end = 45 # in seconds
channel = 0 # left channel
PlayAudioSegment(filepath,start,end,channel)
What shall be evaluated and achieved:
I try to record audio data with a minimum of influence by hard- and especially software. After using Adobe Audition for some time I stumbled across PyAudio and was driven by curiosity as well as the possibility to refresh my Python knowledge.
As the fact displayed in the headline above may have given away I compared the sample values of two wave files (indeed sections of them) and had to find out that both programmes produce different output.
As I am definitely at my wit`s end, I do hope to find someone who could help me.
What has been done so far:
An M-Audio “M-Track Two-Channel USB Interface” has been used to record Audio Data with Audition CS6 and PyAudio simultaneously as the following steps are executed in the given order…
Audition is prepared for recording by opening “Prefrences/ Audio Hardware” and selecting the audio interface, a sample rate of 48 kHz and a latency of 250 ms (this value has been examined thoughout the last years as to be the second lowest I can get without getting the warning for lost samples – if I understood the purpose correctly I just have to worry about loosing samples cause monitoring is not an issue).
A new file with one channel, a sample rate of 48 kHz and a bit depth of 24 bit is opened.
The Python code (displayed below) is started and leads to a countdown being used to change over to Audition and start the recording 10 s before Python starts its.)
Wait until Python prints the “end of programme” message.
Stop and save the data recorded by Audition.
Now data has to be examined:
Both files (one recorded by Audition and Python respectively) are opened in Audition (Multitrack session). As Audition was started and terminated manually the two files have completely different beginning and ending times. Then they are aligned visually so that small extracts (which visually – by waveform shape – contain the same data) can be cut out and saved.
A Python programme has been written opening, reading and displaying the sample values using the default wave module and matplotlib.pyplot respectively (graphs are shown below).
Differences in both waveforms and a big question mark are revealed…
Does anybody have an idea why Audition is showing different sample values and specifically where precisely the mistake (is there any?) hides??
some (interesting) observations
a) When calling the pyaudio.PyAudio().get_default_input_device_info() method the default sample rate is listed as 44,1 kHz even though the default M-Track sample rate is said to be 48 kHz by its specifications (indeed Audition recognizes the 48 kHz by resampling incoming data if another rate was selected). Any ideas why and how to change this?
b) Aligning both files using the beginning of the sequence covered by PyAudio and checking whether they are still “in phase” at the end reveals no – PyAudio is shorter and seems to have lost samples (even though no exception was raised and the “exception on overflow” argument is “True”)
c) Using the “frames_per_buffer” keyword in the stream open method I was unable to align both files, having no idea where Python got its data from.
d) Using the “.get_default_input_device_info()” method and trying different sample rates (22,05 k, 44,1 k, 48 k, 192 k) I always receive True as an output.
Official Specifications M-Track:
bit depth = 24 bit
sample rate = 48 kHz
input via XLR
output via USB
Specifications Computer and Software:
Windows 8.1
I5-3230M # 2,6 GHz
8 GB RAM
Python 3.4.2 with PyAudio 0.2.11 – 32 bit
Audition CS6 Version 5.0.2
Python Code
import pyaudio
import wave
import time
formate = pyaudio.paInt24
channels = 1
framerate = 48000
fileName = 'test ' + '.wav'
chunk = 6144
# output of stream.get_read_available() at different positions
p = pyaudio.PyAudio()
stream = p.open(format=formate,
channels=channels,
rate=framerate,
input=True)
#frames_per_buffer=chunk) # observation c
# COUNTDOWN
for n in range(0, 30):
print(n)
time.sleep(1)
# get data
sampleList = []
for i in range(0, 79):
data = stream.read(chunk, exception_on_overflow = True)
sampleList.append(data)
print('end -', time.strftime('%d.%m.%Y %H:%M:%S', time.gmtime(time.time())))
stream.stop_stream()
stream.close()
p.terminate()
# produce file
file = wave.open(fileName, 'w')
file.setnchannels(channels)
file.setframerate(framerate)
file.setsampwidth(p.get_sample_size(formate))
file.writeframes(b''.join(sampleList))
file.close()
Figure 1: first comparison Audition – PyAudio
image 1
Figure 2: second comparison Audition - Pyaudio
image 2
I am currently working on an easy-to-use audio capturing device for digitizing old casette tapes (i.e. low fidelity). This device is based on a raspberry pi with an usb sound card, which does nothinge else than starting the listed python script on bootup.
import alsaaudio
import wave
import os.path
import RPi.GPIO as GPIO
import key
import usbstick
import time
try:
# Define storage
path = '/mnt/usb/'
prefix = 'Titel '
extension = '.wav'
# Configure GPIOs
GPIO.setmode(GPIO.BOARD)
button_shutdown = key.key(7)
button_record = key.key(11)
GPIO.setup(12, GPIO.OUT)
GPIO.setup(15, GPIO.OUT)
GPIO.output(12, GPIO.HIGH)
# Start thread to detect external memory
usb = usbstick.usbstick(path, 13)
# Configure volume
m = alsaaudio.Mixer('Mic', 0, 1)
m.setvolume(100, 0, 'capture')
# Only run until shutdown button gets pressed
while not (button_shutdown.pressed()):
# Only record if record button is pressed and memory is mounted
if (button_record.pressed() and usb.ismounted()):
# Create object to read input
inp = alsaaudio.PCM(alsaaudio.PCM_CAPTURE, alsaaudio.PCM_NORMAL, 'sysdefault:CARD=Device')
inp.setchannels(1)
inp.setrate(44100)
inp.setformat(alsaaudio.PCM_FORMAT_S16_LE)
inp.setperiodsize(1024)
# Find next file name
i = 0
filename = ''
while (True):
i += 1
filename = path + prefix + str(i) + extension
if not (os.path.exists(filename)):
break
print 'Neue Aufnahme wird gespeichert unter ' + filename
# Create wave file
wavfile = wave.open(filename, 'w')
wavfile.setnchannels(1)
wavfile.setsampwidth(2)
wavfile.setframerate(44100)
# Record sound
while (button_record.pressed()):
l, data = inp.read()
wavfile.writeframes(data)
GPIO.output(15, GPIO.HIGH)
# Stop record an save
print 'Aufnahme beendet\n'
inp.close()
wavfile.close()
GPIO.output(15, GPIO.LOW)
# Record has been started but no memory is mounted
elif (button_record.pressed() and not usb.ismounted()):
print 'Massenspeichergeraet nicht gefunden'
print 'Warte auf Massenspeichergeraet'
# Restart after timeout
timestamp = time.time()
while not (usb.ismounted()):
if ((time.time() - timestamp) > 120):
time.sleep(5)
print 'Timeout.'
#reboot()
#myexit()
print 'Massenspeichergeraet gefunden'
myexit()
except KeyboardInterrupt:
myexit()
According to the documentation pyaudio, the routine inp.read() or alsaaudio.PCM.read() respectively should usually wait until a full period of 1024 samples has been captured. It then should return the number of captured samples as well as the samples itself. Most of the time it returns exactly one period of 1024 samples. I don't think that I have a performance problem, since I would expect it to return several periods then.
THE VERY MYSTERIOUS BEHAVIOR: After 01:30 of recording, inp.read() takes some milliseconds longer than normal to process (this is a useful information in my ignorant opinion) and then returns -32 and faulty data. Then the stream continues. After half a minute at 02:00 it takes about a second (i.e. longer than the first time) to process and returns -32 and faulty data again. This procedere repeats then every minute (02:30-03:00, 03:30-04:00, 04:30-05:00). This timing specification was roughly taken by hand.
-32 seems to result from the following code line in /pyalsaaudio-0.7/alsaaudio.c
return -EPIPE;
Some words about how this expresses: If the data stream is directly written into the wave file, i.e. including the faulty period, the file contains sections of white noise. These sections last 30 seconds. This is because the samples usually consist of 2 bytes. When the faulty period (1 byte) is written, the byte order gets inverted. With the next faulty period it gets inverted again and therefore is correct. If faulty data is refused and only correct data is written into the wave file, the file 'jumps' every 30 seconds.
I think the problem can either be found in
1. the sound card (but I tested 2 different)
2. the computing performance of the raspberry pi
3. the lib pyaudio
Further note: I am pretty new to the linux and python topic. If you need any logs or something, please describe how I can find them.
To cut a long story short: Could someone please help me? How can I solve this error?
EDIT: I already did this usb firmware updating stuff, which is needed, since the usb can be overwhelmed. BTW: What exactly is this EPIPE failure?
Upon further inverstigation, I found out, that this error is not python/pyaudio specific. When I try to record a wave file with arecord, I get the following output. The overrun messages are sent according to the timing described above.
pi#raspberrypi ~ $ sudo arecord -D sysdefault:CARD=Device -B buffersize=192000 -f cd -t wav /mnt/usb/test.wav
Recording WAVE '/mnt/usb/test.wav' : Signed 16 bit Little Endian, Rate 44100 Hz, Stereo
overrun!!! (at least -1871413807.430 ms long)
overrun!!! (at least -1871413807.433 ms long)
overrun!!! (at least -1871413807.341 ms long)
overrun!!! (at least -1871413807.442 ms long)
Referring to this thread at raspberrypi.org, the problem seems to be the (partly) limited write speed to the SD card or the usb storage device with a raspberry pi. Recording to RAM (with tmpfs) or compressing the audio data (e.g. to mp3 with lame) before writing it somewhere else could be a good solution in some cases.
I can't say why the write speed is to low. In my opinion, the data stream is 192 kByte/s for 48 kSamples/s, 16 Bit, stereo. Any SD card or usb mass storage should be able to handle this. As seen above, buffering the audio stream doesn't help.