126 lines
3.9 KiB
Python
126 lines
3.9 KiB
Python
import pyaudio
|
|
import logging
|
|
import audioop
|
|
import wave
|
|
import time
|
|
import os
|
|
import chunk
|
|
from _struct import pack
|
|
from array import array
|
|
|
|
mainLogger = logging.getLogger("hugvey")
|
|
logger = mainLogger.getChild("recorder")
|
|
|
|
class Recorder:
|
|
"""
|
|
Record the streamed audio
|
|
"""
|
|
|
|
def __init__(self, hv_id, src_rate, out_folder, record_voice = True):
|
|
self.logger = mainLogger.getChild(f"{hv_id}").getChild('recorder')
|
|
|
|
if not os.path.exists(out_folder):
|
|
raise Exception(f"Invalid output folder for recordings: {out_folder}")
|
|
|
|
self.hv_id = hv_id
|
|
self.src_rate = src_rate
|
|
self.main_folder = out_folder # unfortunately not every device plays 16kHz audio streams
|
|
self.running = False
|
|
self.record_voice = record_voice
|
|
self.data = array('h')
|
|
self.currentLog = []
|
|
|
|
def start(self):
|
|
self.subsequentMutedFrames = 0
|
|
self.fragmentNr = 0
|
|
|
|
self.data = array('h')
|
|
self.currentTranscription = ""
|
|
self.currentLog = []
|
|
|
|
day = time.strftime("%Y%m%d")
|
|
t = time.strftime("%H:%M:%S")
|
|
|
|
self.out_folder = os.path.join(self.main_folder, day, f"{self.hv_id}", t)
|
|
if not os.path.exists(self.out_folder):
|
|
self.logger.debug(f"Create directory {self.out_folder}")
|
|
self.target_folder = os.makedirs(self.out_folder, exist_ok=True)
|
|
|
|
self.running = True
|
|
|
|
|
|
def writeData(self):
|
|
if self.record_voice:
|
|
if len(self.data) < 1:
|
|
self.logger.info("Skip empty wave creation")
|
|
else:
|
|
self.fragmentNr += 1
|
|
|
|
fn = os.path.join(self.out_folder, f"{self.fragmentNr}.wav")
|
|
|
|
self.logger.info(f"Write wave: {fn}")
|
|
|
|
self.wf = wave.open(fn, 'wb')
|
|
self.wf.setnchannels(1)
|
|
self.wf.setsampwidth(2)
|
|
self.wf.setframerate(self.src_rate)
|
|
# adapted from https://stackoverflow.com/questions/892199/detect-record-audio-in-python#6743593
|
|
self.wf.writeframes(pack('<' + ('h'*len(self.data)), *self.data))
|
|
self.wf.close()
|
|
|
|
with open(os.path.join(self.out_folder, "transcriptions.txt"), "a") as fp:
|
|
fp.write(f"{self.fragmentNr}\t{self.currentTranscription}\n")
|
|
|
|
self.log('-',self.currentTranscription)
|
|
|
|
self.data = array('h')
|
|
self.currentTranscription = ""
|
|
|
|
def receive(self, chunk):
|
|
if not self.running:
|
|
return
|
|
|
|
# self.logger.debug('receive {}'.format(len(chunk)))
|
|
if audioop.max(chunk, 2) == 0:
|
|
# mic is muted on client side.
|
|
|
|
self.subsequentMutedFrames += 1
|
|
#self.logger.debug(f"Empty frame {self.subsequentMutedFrames}")
|
|
if self.subsequentMutedFrames == 4:
|
|
# self.createWave()
|
|
self.writeData()
|
|
if self.subsequentMutedFrames > 4:
|
|
# Don't write more muted frames to the audio file.
|
|
return
|
|
else:
|
|
self.subsequentMutedFrames = 0
|
|
|
|
|
|
if not self.record_voice:
|
|
# we only check this here, as the writeData above is used to write
|
|
# transcriptions to the log.
|
|
return
|
|
|
|
d = array('h', chunk)
|
|
self.data.extend(d)
|
|
# self.wf.writeframes(chunk)
|
|
|
|
def shutdown(self):
|
|
self.writeData()
|
|
self.running = False
|
|
# self.wf.close()
|
|
|
|
def triggerStart(self):
|
|
self.start()
|
|
|
|
def updateTranscription(self, text):
|
|
self.currentTranscription = text
|
|
|
|
def log(self, origin, msg, extra=None):
|
|
self.currentLog.append({'time':time.time(), 'origin': origin, 'msg': msg, 'extra': extra})
|
|
with open(os.path.join(self.out_folder, "log.txt"), "a") as fp:
|
|
fp.write(f"{origin}: {msg}\n")
|
|
|
|
|
|
def __del__(self):
|
|
self.logger.warn("Destroyed recorder object")
|