LASP 1.0
Library for Acoustic Signal Processing
Loading...
Searching...
No Matches
lasp_playback.py
Go to the documentation of this file.
1#!/usr/bin/env python3
2# -*- coding: utf-8 -*-
3"""
4
5"""
6import cv2 as cv
7import queue
8import sounddevice as sd
9import time
10from .lasp_atomic import Atomic
11from threading import Thread, Condition
12import h5py
13
14
16 """
17 Play back a single channel from a
18 """
19
20 def __init__(self, fn1, channel=0, video=False, verbose=True):
21 """
22 Initialize a Playback class for playing back audio
23
24 Args:
25 fn1: Filename of the measurement file
26 channel: channel index to play back
27 video: if True and video is available in the measurement file,
28 video will also be shown
29 verbose: print out status messages to stdout
30 """
31 ext = '.h5'
32 if fn1[-3:] != ext:
33 fn = fn1 + ext
34 else:
35 fn = fn1
36
37 print('Filename: ', fn)
38 self._fn = fn
39
40 self.channel = channel
41 self._video = video
43 self._running = Atomic(False)
44 self._running_cond = Condition()
45 if video:
46 self._video_queue = queue.Queue()
47
48 with h5py.File(fn, 'r') as f:
49 self.samplerate = f.attrs['samplerate']
50 self.nchannels = f.attrs['nchannels']
51 self.blocksize = f.attrs['blocksize']
52 self.nblocks = f['audio'].shape[0]
53 if verbose:
54 print('Sample rate: ', self.samplerate)
55 print('Number of audio frames: ', self.nblocks*self.blocksize)
56 print('Recording time: ', self.nblocks
57 * self.blocksize/self.samplerate)
58
59 if video:
60 try:
61 f['video']
62 self._video_frame_positions = f['video_frame_positions'][:]
63 except AttributeError:
64 print('No video available in measurement file.'
65 'Disabling video')
66 self._video = False
67
68 @property
69 def T(self):
70 """
71 Returns
72 the lenght of the measurement in seconds
73 """
74 return self._nblocks*self._blocksize/self._samplerate
75
76 def start(self):
77 """
78 Start the playback
79 """
80 with h5py.File(self._fn, 'r') as f:
81 self._ad = f['audio']
82 dtype = self._ad.dtype
83 dtype_str = str(dtype)
84 stream = sd.OutputStream(samplerate=self.samplerate,
85 blocksize=self.blocksize,
86 channels=1,
87 dtype=dtype_str,
88 callback=self.audio_callback)
89
90 self._running <<= True
91 if self._video:
92 self._vd = f['video']
93 videothread = Thread(target=self.video_thread_fcn)
94 videothread.start()
95
96 with stream:
97 try:
98 with self._running_cond:
99 while self._running:
100 self._running_cond.wait()
101 except KeyboardInterrupt:
102 print('Keyboard interrupt. Quit playback')
103
104 if self._video:
105 videothread.join()
106
107 def audio_callback(self, outdata, frames, time, status):
108 """
109
110 """
111 aframectr = self._aframectr()
112 if aframectr < self.nblocks:
113 outdata[:, 0] = self._ad[aframectr, :, self.channel]
114 self._aframectr += 1
115 else:
116 self._running <<= False
117 with self._running_cond:
118 self._running_cond.notify()
119
121 frame_ctr = 0
122 nframes = self._vd.shape[0]
123 video_frame_positions = self._video_frame_positions
124 assert video_frame_positions.shape[0] == nframes
125
126 while self._running and frame_ctr < nframes:
127 frame = self._vd[frame_ctr]
128
129 # Find corresponding audio frame
130 corsp_aframe = video_frame_positions[frame_ctr]
131
132 while self._aframectr() <= corsp_aframe:
133 print('Sleep video...')
134 time.sleep(self.blocksize/self.samplerate/2)
135
136 cv.imshow("Video output. Press 'q' to quit", frame)
137 if cv.waitKey(1) & 0xFF == ord('q'):
138 self._running <<= False
139
140 frame_ctr += 1
141 print('Ending video playback thread')
142 cv.destroyAllWindows()
Implementation of atomic operations on integers and booleans.
Play back a single channel from a.
__init__(self, fn1, channel=0, video=False, verbose=True)
Initialize a Playback class for playing back audio.
start(self)
Start the playback.
T(self)
Returns the lenght of the measurement in seconds.
audio_callback(self, outdata, frames, time, status)