Page MenuHomePhabricator

No OneTemporary

diff --git a/va-recorder.py b/va-recorder.py
index 18981d9..2ad9e9d 100755
--- a/va-recorder.py
+++ b/va-recorder.py
@@ -1,355 +1,363 @@
#!/usr/bin/env python3
import pyaudio
import wave
from ctypes import *
from optparse import OptionParser
from pathlib import Path
import math
+import platform
import struct
import wave
import time
import os
import sys
from datetime import datetime
import functools
print = functools.partial(print, flush=True)
# remove useless alsa logging
# From alsa-lib Git 3fd4ab9be0db7c7430ebd258f2717a976381715d
# $ grep -rn snd_lib_error_handler_t
# include/error.h:59:typedef void (*snd_lib_error_handler_t)(const char *file, int line, const char *function, int err, const char *fmt, ...) /* __attribute__ ((format (printf, 5, 6))) */;
# Define our error handler type
ERROR_HANDLER_FUNC = CFUNCTYPE(None, c_char_p, c_int, c_char_p, c_int, c_char_p)
def py_error_handler(filename, line, function, err, fmt):
pass
#print('messages are yummy')
-c_error_handler = ERROR_HANDLER_FUNC(py_error_handler)
-asound = cdll.LoadLibrary('libasound.so')
-# Set error handler
-asound.snd_lib_error_set_handler(c_error_handler)
-# import sounddevice as sd
+try:
+ asound = cdll.LoadLibrary('libasound.so')
+ # Set error handler
+ c_error_handler = ERROR_HANDLER_FUNC(py_error_handler)
+ asound.snd_lib_error_set_handler(c_error_handler)
+except OSError:
+ pass
+
+#import sounddevice as sd
#devices = sd.query_devices()
#print(devices)
f_name_directory = '%s/.sipclient/spool/playback' % Path.home()
lock_file = '%s/.sipclient/spool/playback/playback.lock' % Path.home()
class Recorder:
chunk = 1024
channels = 1
@staticmethod
def rms(frame):
SHORT_NORMALIZE = (1.0/32768.0)
count = len(frame) / 2
format = "%dh" % (count)
shorts = struct.unpack(format, frame)
sum_squares = 0.0
for sample in shorts:
n = sample * SHORT_NORMALIZE
sum_squares += n * n
rms = math.pow(sum_squares / count, 0.5)
return rms * 1000
def __init__(self, target, options):
self.p = pyaudio.PyAudio()
info = self.p.get_host_api_info_by_index(0)
numdevices = info.get('deviceCount')
self.target = target
self.timeout_length = options.timeout
self.rate = options.rate
self.device = options.device
self.min_rec_time = options.min_rec_time
self.max_rec_time = options.max_rec_time
self.external_lock_file = options.external_lock_file
self.external_trigger_file = options.external_trigger_file
self.level_lock_file = options.level_lock_file
self.level_enable_file = options.level_enable_file
self.quiet = options.quiet
self.started_by_file = False
self.started_by_level = False
devices = {}
devices_text = []
for i in range(0, numdevices):
if (self.p.get_device_info_by_host_api_device_index(0, i).get('maxInputChannels')) > 0:
devices[i] = self.p.get_device_info_by_host_api_device_index(0, i).get('name')
if devices[i] == str(self.device):
self.device = i
elif str(i) == str(self.device):
self.device = i
devices_text.append("%s) %s" % (i, devices[i]))
print('Available devices: %s' % ", ".join(devices_text))
try:
print('Using audio device %s) %s at sample rate %d' % (self.device, devices[self.device], self.rate))
except KeyError:
print('Non existent audio device')
self.threshold = 0 if target == 'test' else options.threshold
self.stream = self.p.open(format=pyaudio.paInt16,
channels=self.channels,
rate=self.rate,
input=True,
output=True,
input_device_index=self.device,
frames_per_buffer=self.chunk)
def listen(self):
wait_print = False
i = 0
lock_print = False
external_lock_print = False
level_lock_print = False
level_enable_print = False
while True:
i = i + 1
input = self.stream.read(self.chunk, exception_on_overflow = False)
rms_val = self.rms(input)
now = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
if os.path.exists(lock_file):
if not lock_print:
print("%s - lock file %s present, listen paused" % (now, lock_file))
lock_print = True
continue
else:
if lock_print:
print("%s - lock file %s absent, listen resumed" % (now, lock_file))
lock_print = False
if self.external_lock_file:
if os.path.exists(self.external_lock_file):
if not external_lock_print:
print("%s - external lock file %s present, listen paused" % (now, self.external_lock_file))
external_lock_print = True
continue
else:
if external_lock_print:
print("%s - external lock file %s absent, listen resumed" % (now, self.external_lock_file))
external_lock_print = False
if self.level_lock_file:
if os.path.exists(self.level_lock_file):
if not level_lock_print:
print("%s - level lock file %s present, listen paused" % (now, self.level_lock_file))
level_lock_print = True
continue
else:
if level_lock_print:
print("%s - level lock file %s absent, listen resumed" % (now, self.level_lock_file))
level_lock_print = False
wait_print = False
if self.external_trigger_file:
if os.path.exists(self.external_trigger_file):
if not self.started_by_file:
print('%s - recording by file %s' % (now, self.external_trigger_file))
self.started_by_file = True
if self.threshold and not self.started_by_file:
if self.level_enable_file:
if not os.path.exists(self.level_enable_file):
if not level_enable_print:
print("%s - level enable file %s missing, listen paused" % (now, self.level_enable_file))
level_enable_print = True
print("%s - not listening, level %3d" % (now, rms_val), end='\r')
continue
else:
if level_enable_print:
print("%s - level enable file %s present, listen resumed" % (now, self.level_enable_file))
level_enable_print = False
if rms_val >= self.threshold:
if not self.started_by_level:
print('%s - recording by level %3d > %d' % (now, rms_val, self.threshold))
self.started_by_level = True
if self.started_by_level or self.started_by_file:
self.record()
else:
if not self.quiet:
if self.external_trigger_file:
print("%s - listening, level %3d" % (now, rms_val), end='\r')
else:
print("%s - listening, level %3d < %d" % (now, rms_val, self.threshold), end='\r')
def record(self):
rec = []
current = time.time()
end = time.time() + self.timeout_length
start_time = current
recording = False
i = 0
now = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
print('%s - now recording...' % now)
if self.started_by_level:
while current <= end:
i = i + 1
data = self.stream.read(self.chunk)
rms_val = self.rms(data)
now = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
if rms_val >= self.threshold: end = time.time() + self.timeout_length
#if not recording:
diff = time.time() - start_time
if not self.quiet:
print('%s - recording at level %3d for %.1f seconds' % (now, rms_val, diff), end='\r')
recording = True
current = time.time()
rec.append(data)
rec_time = time.time() - start_time
if rec_time > self.max_rec_time:
#print("%s - maximum recording time of %d seconds reached" % (now, rec_time))
break
else:
rec_time = time.time() - start_time - self.timeout_length
if os.path.exists(lock_file):
print("%s - lock file %s detected" % (now, lock_file))
break
if rec_time > self.min_rec_time:
self.write(b''.join(rec), rec_time)
else:
if rec_time > 0:
print("%s - skip too short recording of %.1f seconds" % (now, rec_time))
else:
print()
if self.started_by_file:
while os.path.exists(self.external_trigger_file):
i = i + 1
data = self.stream.read(self.chunk)
rms_val = self.rms(data)
now = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
if not self.quiet:
print('%s - recording by file at level %3d' % (now, rms_val), end='\r')
recording = True
current = time.time()
rec.append(data)
rec_time = time.time() - start_time
if rec_time > self.max_rec_time:
break
if os.path.exists(lock_file):
print("%s - lock file %s detected" % (now, lock_file))
break
print("%s - recorded %.1f seconds" % (now, rec_time))
self.write(b''.join(rec), rec_time)
self.started_by_file = False
self.started_by_level = False
def play(self, file):
p = pyaudio.PyAudio()
wf = wave.open(file, 'rb')
stream = p.open(
format = p.get_format_from_width(wf.getsampwidth()),
channels = wf.getnchannels(),
rate = wf.getframerate(),
output = True
)
data = wf.readframes(1024)
while data != b'':
stream.write(data)
data = wf.readframes(1024)
stream.close()
p.terminate()
def write(self, recording, duration):
now = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
if os.path.exists(lock_file):
print("%s - lock file %s present, skip file" % (now, lock_file))
return
if self.external_lock_file and os.path.exists(self.external_lock_file):
print("%s - external lock file %s present, skip file" % (now, self.external_lock_file))
return
if self.level_lock_file and os.path.exists(self.level_lock_file):
print("%s - level lock file %s present, skip file" % (now, self.level_lock_file))
return
n_files = len(os.listdir(f_name_directory))
tmp_filename = os.path.join(f_name_directory, '%s.tmp' % self.target)
wf = wave.open(tmp_filename, 'wb')
wf.setnchannels(self.channels)
wf.setsampwidth(self.p.get_sample_size(pyaudio.paInt16))
wf.setframerate(self.rate)
wf.writeframes(recording)
wf.close()
filename = os.path.join(f_name_directory, '%s.wav' % self.target)
os.rename(tmp_filename, filename)
print('%s - saved %d seconds audio to %s' % (now, duration, filename))
if self.target == 'test':
self.play(filename)
def end(self):
# Reset to default error handler
self.p.terminate()
asound.snd_lib_error_set_handler(None)
if __name__ == '__main__':
description = 'This script is a voice activate recorder that saves individual recording in the folder %s that is polled by sip-session to initiate an outgoing call and playback the file. The filename is in the format user@domain.wav. Use test argument to test audio level.' % f_name_directory
usage = '%prog [options] [user@domain]'
parser = OptionParser(usage=usage, description=description)
parser.print_usage = parser.print_help
+ os_type = platform.system()
+ default_device = 'pulse' if os_type == 'Linux' else '1'
+
parser.add_option('-r', '--sample_rate', type='int', default='16000', dest='rate', help='Audio sample rate')
- parser.add_option('-d', '--device', type='string', default='pulse', dest='device', help='Use selected input audio device')
+ parser.add_option('-d', '--device', type='string', default=default_device, dest='device', help='Use selected input audio device')
parser.add_option('-T', '--timeout', type='int', default=2, dest='timeout', help='Silence timeout to stop recording')
parser.add_option('-m', '--min_rec_time', type='int', default=1, dest='min_rec_time', help='Minimum recording time to save recording')
parser.add_option('-M', '--max_rec_time', type='int', default=5, dest='max_rec_time', help='Maximum recording time for each file')
parser.add_option('-t', '--threshold', type='int', default=10, dest='threshold', help='Minimum signal level to start recording')
parser.add_option('-l', '--level_lock_file', type='string', dest='level_lock_file', help='Skip level recording if file exists')
parser.add_option('-L', '--level_enable_file', type='string', dest='level_enable_file', help='Enable level recording only if file exists')
parser.add_option('-e', '--external_lock_file', type='string', dest='external_lock_file', help='Skip recording if file exists')
parser.add_option('-E', '--external_trigger_file', type='string', dest='external_trigger_file', help='Start recording if file exists, regardless of level')
parser.add_option('-q', '--quiet', action='store_true', dest='quiet', default=False, help='Minimize logging.')
options, args = parser.parse_args()
try:
target = args[0]
except IndexError:
parser.print_help()
sys.exit(1)
try:
a = Recorder(args[0], options)
a.listen()
except KeyboardInterrupt:
a.end()
print()
sys.exit(0)
except OSError as e:
print("Error: %s" % str(e))
sys.exit(0)

File Metadata

Mime Type
text/x-diff
Expires
Sat, Nov 23, 10:20 AM (1 d, 3 h)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
3408711
Default Alt Text
(14 KB)

Event Timeline