Added monitor panel. Only spectra fast and slow weighting implementation is not savy
This commit is contained in:
parent
60cf046fa6
commit
b3d60a2d69
1
.gitignore
vendored
1
.gitignore
vendored
@ -18,3 +18,4 @@ test/test_workers
|
|||||||
doc
|
doc
|
||||||
LASP.egg-info
|
LASP.egg-info
|
||||||
lasp_octave_fir.*
|
lasp_octave_fir.*
|
||||||
|
lasp/aps_ui.py
|
||||||
|
@ -12,7 +12,23 @@ include_directories(
|
|||||||
c
|
c
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# add the command to generate the source code
|
||||||
|
# add_custom_command (
|
||||||
|
# OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/Table.h
|
||||||
|
# COMMAND MakeTable ${CMAKE_CURRENT_BINARY_DIR}/Table.h
|
||||||
|
# DEPENDS MakeTable
|
||||||
|
# )
|
||||||
|
|
||||||
|
add_custom_command(
|
||||||
|
OUTPUT "aps_ui.py"
|
||||||
|
COMMAND pyside-uic ${CMAKE_CURRENT_SOURCE_DIR}/ui/aps_ui.ui -o ${CMAKE_CURRENT_SOURCE_DIR}/aps_ui.py
|
||||||
|
DEPENDS "ui/aps_ui.ui"
|
||||||
|
)
|
||||||
|
add_custom_target(ui ALL DEPENDS "aps_ui.py")
|
||||||
|
|
||||||
|
|
||||||
set_source_files_properties(wrappers.c PROPERTIES COMPILE_FLAGS "${CMAKE_C_FLAGS} ${CYTHON_EXTRA_C_FLAGS}")
|
set_source_files_properties(wrappers.c PROPERTIES COMPILE_FLAGS "${CMAKE_C_FLAGS} ${CYTHON_EXTRA_C_FLAGS}")
|
||||||
cython_add_module(wrappers wrappers.pyx)
|
cython_add_module(wrappers wrappers.pyx)
|
||||||
target_link_libraries(wrappers lasp_lib )
|
target_link_libraries(wrappers lasp_lib )
|
||||||
|
|
||||||
|
|
||||||
|
0
lasp/fir_design/__init__.py
Normal file
0
lasp/fir_design/__init__.py
Normal file
51
lasp/lasp_atomic.py
Normal file
51
lasp/lasp_atomic.py
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
"""
|
||||||
|
Provides a simple atomic variable:
|
||||||
|
|
||||||
|
>>> a = Atomic(0)
|
||||||
|
|
||||||
|
Retrieve the value
|
||||||
|
>>> b = a()
|
||||||
|
Set a new value:
|
||||||
|
>>> a <<= b
|
||||||
|
Get conversion to boolean:
|
||||||
|
>>> if a:
|
||||||
|
do something
|
||||||
|
|
||||||
|
Atomic increment:
|
||||||
|
>>> A += 1
|
||||||
|
|
||||||
|
@author: J.A. de Jong - ASCEE
|
||||||
|
"""
|
||||||
|
from threading import Lock
|
||||||
|
|
||||||
|
class Atomic:
|
||||||
|
def __init__(self, val):
|
||||||
|
self._val = val
|
||||||
|
self._lock = Lock()
|
||||||
|
|
||||||
|
def __iadd__(self, toadd):
|
||||||
|
with self._lock:
|
||||||
|
self._val += toadd
|
||||||
|
return self
|
||||||
|
|
||||||
|
def __isub__(self, toadd):
|
||||||
|
with self._lock:
|
||||||
|
self._val -= toadd
|
||||||
|
return self
|
||||||
|
|
||||||
|
|
||||||
|
def __bool__(self):
|
||||||
|
with self._lock:
|
||||||
|
return self._val
|
||||||
|
|
||||||
|
def __ilshift__(self, other):
|
||||||
|
with self._lock:
|
||||||
|
self._val = other
|
||||||
|
return self
|
||||||
|
|
||||||
|
def __call__(self):
|
||||||
|
with self._lock:
|
||||||
|
return self._val
|
||||||
|
|
186
lasp/lasp_avstream.py
Normal file
186
lasp/lasp_avstream.py
Normal file
@ -0,0 +1,186 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
"""
|
||||||
|
Created on Sat Mar 10 08:28:03 2018
|
||||||
|
|
||||||
|
@author: Read data from image stream and record sound at the same time
|
||||||
|
"""
|
||||||
|
import cv2 as cv
|
||||||
|
import sounddevice as sd
|
||||||
|
from .lasp_atomic import Atomic
|
||||||
|
from threading import Thread, Condition, Lock
|
||||||
|
import time
|
||||||
|
__all__ = ['AvType','AvStream']
|
||||||
|
|
||||||
|
# %%
|
||||||
|
blocksize = 2048
|
||||||
|
video_x,video_y = 640,480
|
||||||
|
dtype, sampwidth = 'int32',4
|
||||||
|
|
||||||
|
class AvType:
|
||||||
|
video=0
|
||||||
|
audio=1
|
||||||
|
|
||||||
|
class AvStream:
|
||||||
|
def __init__(self, audiodeviceno=None, video=None, nchannels = None, samplerate = None):
|
||||||
|
|
||||||
|
audiodevice,audiodeviceno = self._findDevice(audiodeviceno)
|
||||||
|
if nchannels is None:
|
||||||
|
self.nchannels = audiodevice['max_input_channels']
|
||||||
|
if self.nchannels == 0:
|
||||||
|
raise RuntimeError('Device has no input channels')
|
||||||
|
else:
|
||||||
|
self.nchannels = nchannels
|
||||||
|
|
||||||
|
self.audiodeviceno = audiodeviceno
|
||||||
|
if samplerate is None:
|
||||||
|
self.samplerate = audiodevice['default_samplerate']
|
||||||
|
else:
|
||||||
|
self.samplerate = samplerate
|
||||||
|
|
||||||
|
self.blocksize = blocksize
|
||||||
|
|
||||||
|
self.video_x, self.video_y = video_x,video_y
|
||||||
|
self.dtype, self.sampwidth = dtype, sampwidth
|
||||||
|
|
||||||
|
self._aframectr = Atomic(0)
|
||||||
|
self._vframectr = Atomic(0)
|
||||||
|
|
||||||
|
self._callbacklock = Lock()
|
||||||
|
|
||||||
|
self._running = Atomic(False)
|
||||||
|
self._running_cond = Condition()
|
||||||
|
|
||||||
|
self._video = video
|
||||||
|
self._video_started = Atomic(False)
|
||||||
|
self._callbacks = []
|
||||||
|
self._audiothread = None
|
||||||
|
self._videothread = None
|
||||||
|
|
||||||
|
def addCallback(self, cb):
|
||||||
|
"""
|
||||||
|
|
||||||
|
"""
|
||||||
|
with self._callbacklock:
|
||||||
|
if not cb in self._callbacks:
|
||||||
|
self._callbacks.append(cb)
|
||||||
|
|
||||||
|
def removeCallback(self, cb):
|
||||||
|
with self._callbacklock:
|
||||||
|
if cb in self._callbacks:
|
||||||
|
self._callbacks.remove(cb)
|
||||||
|
|
||||||
|
def _findDevice(self, deviceno):
|
||||||
|
|
||||||
|
if deviceno is None:
|
||||||
|
deviceno = 0
|
||||||
|
devices = sd.query_devices()
|
||||||
|
found = []
|
||||||
|
for device in devices:
|
||||||
|
name = device['name']
|
||||||
|
if 'Umik' in name:
|
||||||
|
found.append((device,deviceno))
|
||||||
|
elif 'nanoSHARC' in name:
|
||||||
|
found.append((device,deviceno))
|
||||||
|
deviceno+=1
|
||||||
|
|
||||||
|
if len(found) == 0:
|
||||||
|
print('Please choose one of the following:')
|
||||||
|
print(sd.query_devices())
|
||||||
|
raise RuntimeError('Could not find a proper device')
|
||||||
|
|
||||||
|
return found[0]
|
||||||
|
else:
|
||||||
|
return (sd.query_devices(deviceno,kind='input'),deviceno)
|
||||||
|
|
||||||
|
def start(self):
|
||||||
|
"""
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
if self._running:
|
||||||
|
raise RuntimeError('Stream already started')
|
||||||
|
|
||||||
|
assert self._audiothread == None
|
||||||
|
assert self._videothread == None
|
||||||
|
|
||||||
|
self._running <<= True
|
||||||
|
self._audiothread = Thread(target=self._audioThread)
|
||||||
|
if self._video is not None:
|
||||||
|
self._videothread = Thread(target=self._videoThread)
|
||||||
|
self._videothread.start()
|
||||||
|
else:
|
||||||
|
self._video_started <<= True
|
||||||
|
self._audiothread.start()
|
||||||
|
|
||||||
|
def _audioThread(self):
|
||||||
|
# Raw stream to allow for in24 packed data type
|
||||||
|
stream = sd.InputStream(
|
||||||
|
device=self.audiodeviceno,
|
||||||
|
dtype=self.dtype,
|
||||||
|
blocksize=blocksize,
|
||||||
|
channels=self.nchannels,
|
||||||
|
samplerate=self.samplerate,
|
||||||
|
callback=self._audioCallback)
|
||||||
|
|
||||||
|
with stream:
|
||||||
|
with self._running_cond:
|
||||||
|
while self._running:
|
||||||
|
self._running_cond.wait()
|
||||||
|
print('stopped audiothread')
|
||||||
|
|
||||||
|
def _videoThread(self):
|
||||||
|
cap = cv.VideoCapture(self._video)
|
||||||
|
if not cap.isOpened():
|
||||||
|
cap.open()
|
||||||
|
vframectr = 0
|
||||||
|
loopctr = 0
|
||||||
|
while self._running:
|
||||||
|
ret, frame = cap.read()
|
||||||
|
# print(frame.shape)
|
||||||
|
if ret==True:
|
||||||
|
if vframectr == 0:
|
||||||
|
self._video_started <<= True
|
||||||
|
with self._callbacklock:
|
||||||
|
for cb in self._callbacks:
|
||||||
|
cb(AvType.video,frame,self._aframectr(),vframectr)
|
||||||
|
vframectr += 1
|
||||||
|
self._vframectr += 1
|
||||||
|
else:
|
||||||
|
|
||||||
|
if loopctr == 10:
|
||||||
|
print('Error: no video capture!')
|
||||||
|
time.sleep(0.2)
|
||||||
|
loopctr +=1
|
||||||
|
|
||||||
|
cap.release()
|
||||||
|
print('stopped videothread')
|
||||||
|
|
||||||
|
def _audioCallback(self, indata, nframes, time, status):
|
||||||
|
"""This is called (from a separate thread) for each audio block."""
|
||||||
|
if not self._video_started:
|
||||||
|
return
|
||||||
|
|
||||||
|
with self._callbacklock:
|
||||||
|
for cb in self._callbacks:
|
||||||
|
cb(AvType.audio,indata,self._aframectr(),self._vframectr())
|
||||||
|
self._aframectr += 1
|
||||||
|
|
||||||
|
def stop(self):
|
||||||
|
self._running <<= False
|
||||||
|
with self._running_cond:
|
||||||
|
self._running_cond.notify()
|
||||||
|
self._audiothread.join()
|
||||||
|
self._audiothread = None
|
||||||
|
if self._video:
|
||||||
|
self._videothread.join()
|
||||||
|
self._videothread = None
|
||||||
|
self._aframectr <<= 0
|
||||||
|
self._vframectr <<= 0
|
||||||
|
self._video_started <<= False
|
||||||
|
|
||||||
|
def isStarted(self):
|
||||||
|
return self._running()
|
||||||
|
|
||||||
|
def hasVideo(self):
|
||||||
|
return True if self._video is not None else False
|
126
lasp/lasp_playback.py
Normal file
126
lasp/lasp_playback.py
Normal file
@ -0,0 +1,126 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
"""
|
||||||
|
Created on Sat Mar 10 08:28:03 2018
|
||||||
|
|
||||||
|
@author: Read data from image stream and record sound at the same time
|
||||||
|
"""
|
||||||
|
import cv2 as cv
|
||||||
|
import numpy as np
|
||||||
|
import queue
|
||||||
|
import sounddevice as sd
|
||||||
|
import time
|
||||||
|
from matplotlib.pyplot import plot, show
|
||||||
|
from .lasp_atomic import Atomic
|
||||||
|
from threading import Thread, Condition
|
||||||
|
import h5py
|
||||||
|
|
||||||
|
class Playback:
|
||||||
|
"""
|
||||||
|
Play back a single channel from a
|
||||||
|
"""
|
||||||
|
def __init__(self, fn1, channel = 0, video=True, verbose=True):
|
||||||
|
"""
|
||||||
|
|
||||||
|
"""
|
||||||
|
ext = '.h5'
|
||||||
|
if not ext in fn1:
|
||||||
|
fn = fn1 + ext
|
||||||
|
else:
|
||||||
|
fn = fn1
|
||||||
|
|
||||||
|
print('Filename: ', fn)
|
||||||
|
self._fn = fn
|
||||||
|
|
||||||
|
self.channel = channel
|
||||||
|
self._video = video
|
||||||
|
self._aframectr = Atomic(0)
|
||||||
|
self._running = Atomic(False)
|
||||||
|
self._running_cond = Condition()
|
||||||
|
if video:
|
||||||
|
self._video_queue = queue.Queue()
|
||||||
|
|
||||||
|
with h5py.File(fn,'r') as f:
|
||||||
|
self.samplerate = f.attrs['samplerate']
|
||||||
|
self.nchannels = f.attrs['nchannels']
|
||||||
|
self.blocksize = f.attrs['blocksize']
|
||||||
|
self.nblocks = f['audio'].shape[0]
|
||||||
|
if verbose:
|
||||||
|
print('Sample rate: ', self.samplerate)
|
||||||
|
print('Number of audio frames: ', self.nblocks*self.blocksize)
|
||||||
|
print('Recording time: ', self.nblocks*self.blocksize/self.samplerate)
|
||||||
|
|
||||||
|
|
||||||
|
if video:
|
||||||
|
try:
|
||||||
|
f['video']
|
||||||
|
self._video_frame_positions = f['video_frame_positions'][:]
|
||||||
|
except:
|
||||||
|
print('No video available in measurement file. Disabling video')
|
||||||
|
self._video = False
|
||||||
|
|
||||||
|
@property
|
||||||
|
def T(self):
|
||||||
|
return self._nblocks*self._blocksize/self._samplerate
|
||||||
|
|
||||||
|
def start(self):
|
||||||
|
with h5py.File(self._fn,'r') as f:
|
||||||
|
stream = sd.OutputStream(samplerate = self.samplerate,
|
||||||
|
blocksize = self.blocksize,
|
||||||
|
channels = 1,
|
||||||
|
dtype = 'int32',callback = self.audio_callback)
|
||||||
|
self._ad = f['audio']
|
||||||
|
self._running <<= True
|
||||||
|
if self._video:
|
||||||
|
self._vd = f['video']
|
||||||
|
videothread = Thread(target=self.video_thread_fcn)
|
||||||
|
videothread.start()
|
||||||
|
|
||||||
|
with stream:
|
||||||
|
try:
|
||||||
|
with self._running_cond:
|
||||||
|
while self._running:
|
||||||
|
self._running_cond.wait()
|
||||||
|
except KeyboardInterrupt:
|
||||||
|
print('Keyboard interrupt. Quit playback')
|
||||||
|
|
||||||
|
if self._video:
|
||||||
|
videothread.join()
|
||||||
|
|
||||||
|
def audio_callback(self, outdata, frames, time, status):
|
||||||
|
"""
|
||||||
|
|
||||||
|
"""
|
||||||
|
aframectr = self._aframectr()
|
||||||
|
if aframectr < self.nblocks:
|
||||||
|
outdata[:,0] = self._ad[aframectr,:,self.channel]
|
||||||
|
self._aframectr += 1
|
||||||
|
else:
|
||||||
|
self._running <<= False
|
||||||
|
with self._running_cond:
|
||||||
|
self._running_cond.notify()
|
||||||
|
|
||||||
|
def video_thread_fcn(self):
|
||||||
|
frame_ctr = 0
|
||||||
|
nframes = self._vd.shape[0]
|
||||||
|
video_frame_positions = self._video_frame_positions
|
||||||
|
assert video_frame_positions.shape[0] == nframes
|
||||||
|
|
||||||
|
while self._running and frame_ctr < nframes:
|
||||||
|
frame = self._vd[frame_ctr]
|
||||||
|
|
||||||
|
# Find corresponding audio frame
|
||||||
|
corsp_aframe = video_frame_positions[frame_ctr]
|
||||||
|
|
||||||
|
while self._aframectr() <= corsp_aframe:
|
||||||
|
print('Sleep video...')
|
||||||
|
time.sleep(self.blocksize/self.samplerate/2)
|
||||||
|
|
||||||
|
cv.imshow("Video output. Press 'q' to quit",frame)
|
||||||
|
if cv.waitKey(1) & 0xFF == ord('q'):
|
||||||
|
self._running <<= False
|
||||||
|
|
||||||
|
frame_ctr += 1
|
||||||
|
print('Ending video playback thread')
|
||||||
|
cv.destroyAllWindows()
|
||||||
|
|
124
lasp/lasp_record.py
Normal file
124
lasp/lasp_record.py
Normal file
@ -0,0 +1,124 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
"""
|
||||||
|
Created on Sat Mar 10 08:28:03 2018
|
||||||
|
|
||||||
|
@author: Read data from image stream and record sound at the same time
|
||||||
|
"""
|
||||||
|
import numpy as np
|
||||||
|
from .lasp_atomic import Atomic
|
||||||
|
from threading import Condition
|
||||||
|
from .lasp_avstream import AvType
|
||||||
|
import h5py, time
|
||||||
|
|
||||||
|
# %%
|
||||||
|
class Recording:
|
||||||
|
def __init__(self, fn, stream, rectime = None):
|
||||||
|
|
||||||
|
ext = '.h5'
|
||||||
|
if not ext in fn:
|
||||||
|
fn += ext
|
||||||
|
self._stream = stream
|
||||||
|
self.blocksize = stream.blocksize
|
||||||
|
self.samplerate = stream.samplerate
|
||||||
|
self._running = Atomic(False)
|
||||||
|
self._running_cond = Condition()
|
||||||
|
self.rectime = rectime
|
||||||
|
self._fn = fn
|
||||||
|
|
||||||
|
self._video_frame_positions = []
|
||||||
|
|
||||||
|
self._aframeno = Atomic(0)
|
||||||
|
self._vframeno = 0
|
||||||
|
|
||||||
|
def start(self):
|
||||||
|
stream = self._stream
|
||||||
|
|
||||||
|
with h5py.File(self._fn,'w') as f:
|
||||||
|
self._ad = f.create_dataset('audio',
|
||||||
|
(1,stream.blocksize,stream.nchannels),
|
||||||
|
dtype=stream.dtype,
|
||||||
|
maxshape=(None,stream.blocksize,
|
||||||
|
stream.nchannels),
|
||||||
|
compression='gzip'
|
||||||
|
)
|
||||||
|
if stream.hasVideo():
|
||||||
|
video_x,video_y = stream.video_x,stream.video_y
|
||||||
|
self._vd = f.create_dataset('video',
|
||||||
|
(1,video_y,video_x,3),
|
||||||
|
dtype='uint8',
|
||||||
|
maxshape=(None,video_y,video_x,3),
|
||||||
|
compression='gzip'
|
||||||
|
)
|
||||||
|
|
||||||
|
f.attrs['samplerate'] = stream.samplerate
|
||||||
|
f.attrs['nchannels'] = stream.nchannels
|
||||||
|
f.attrs['blocksize'] = stream.blocksize
|
||||||
|
f.attrs['time'] = time.time()
|
||||||
|
self._running <<= True
|
||||||
|
# Videothread is going to start
|
||||||
|
|
||||||
|
if not stream.isStarted():
|
||||||
|
stream.start()
|
||||||
|
|
||||||
|
stream.addCallback(self._callback)
|
||||||
|
with self._running_cond:
|
||||||
|
try:
|
||||||
|
print('Starting record....')
|
||||||
|
while self._running:
|
||||||
|
self._running_cond.wait()
|
||||||
|
except KeyboardInterrupt:
|
||||||
|
print("Keyboard interrupt on record")
|
||||||
|
self._running <<= False
|
||||||
|
|
||||||
|
stream.removeCallback(self._callback)
|
||||||
|
|
||||||
|
if stream.hasVideo():
|
||||||
|
f['video_frame_positions'] = self._video_frame_positions
|
||||||
|
|
||||||
|
|
||||||
|
print('\nEnding record')
|
||||||
|
|
||||||
|
def stop(self):
|
||||||
|
self._running <<= False
|
||||||
|
with self._running_cond:
|
||||||
|
self._running_cond.notify()
|
||||||
|
|
||||||
|
def _callback(self, _type, data, aframe, vframe):
|
||||||
|
if not self._stream.isStarted():
|
||||||
|
self._running <<= False
|
||||||
|
with self._running_cond:
|
||||||
|
self._running_cond.notify()
|
||||||
|
|
||||||
|
if _type == AvType.audio:
|
||||||
|
self._aCallback(data, aframe)
|
||||||
|
elif _type == AvType.video:
|
||||||
|
self._vCallback(data)
|
||||||
|
|
||||||
|
def _aCallback(self, frames, aframe):
|
||||||
|
# print(self._aframeno())
|
||||||
|
print('.',end='')
|
||||||
|
curT = self._aframeno()*self.blocksize/self.samplerate
|
||||||
|
if self.rectime is not None and curT > self.rectime:
|
||||||
|
# We are done!
|
||||||
|
self._running <<= False
|
||||||
|
with self._running_cond:
|
||||||
|
self._running_cond.notify()
|
||||||
|
return
|
||||||
|
|
||||||
|
self._ad.resize(self._aframeno()+1,axis=0)
|
||||||
|
self._ad[self._aframeno(),:,:] = frames
|
||||||
|
self._aframeno += 1
|
||||||
|
|
||||||
|
|
||||||
|
def _vCallback(self,frame):
|
||||||
|
self._video_frame_positions.append(self._aframeno())
|
||||||
|
vframeno = self._vframeno
|
||||||
|
self._vd.resize(vframeno+1,axis=0)
|
||||||
|
self._vd[vframeno,:,:] = frame
|
||||||
|
self._vframeno += 1
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
|
||||||
|
rec = Recording('test',5)
|
||||||
|
rec.start()
|
Loading…
Reference in New Issue
Block a user