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: extratxt = "" if not extra else f"({extra})" fp.write(f"{origin}: {extratxt} {msg}\n") def __del__(self): self.logger.warn("Destroyed recorder object")