def interpret(self, liveAudio: LiveAudio): self.staticChunk, self.liveChunk = self._getCurrentCorrespondingChunks( liveAudio) chunksCorr = self.measureNormalizedCrossCorelationFromAudio(liveAudio) hzDiff = self.baseHzAbsoluteDifference(liveAudio) pearsonPCMCorr = self.pearsonCorrelationCoefficient( self.staticChunk.rawData, self.liveChunk.rawData) pearsonSpectrumCorr = self.pearsonCorrelationCoefficient( self.staticChunk.chunkAS, self.liveChunk.chunkAS) diffsTuple = self.compareNHighestAmplitudes(liveAudio) Logger.interpretEngineLog("""[{chunkNr}] corr = {corr} HzDiff = {HzDiff} PerasonPCMCorr = {pearsonPCMCorr} pearsonSpectrumCorr = {pearsonSpectrumCorr} TopFreqsDiffs = {diffs} """.format(chunkNr=liveAudio.getLastChunksIndex() - 1, corr=chunksCorr, HzDiff=hzDiff, pearsonPCMCorr=pearsonPCMCorr, pearsonSpectrumCorr=pearsonSpectrumCorr, diffs=diffsTuple)) self.compareSpectrumCentroid() Logger.info("PCM Envelopes correlation = {}".format( self.compareEnvelopes( liveAudio))) # TODO: laterchange logger method.
def _calculateStaticAudioParameters(self): def calculateNMaxFreqsEnvelopes(): from src.Commons.Settings import TOP_FREQS_COUNT envelopes = [] for i in range(0, TOP_FREQS_COUNT): envelopes.append([]) for c in self.staticAudio.chunks: freqs = np.sort( ProcessingEngine.findNLoudestFreqsFromFFT( c.chunkAS, c.chunkFreqs)) for i in range(0, freqs.size): envelopes[i].append(freqs[i]) return envelopes # self.calculateFrequencyEnvelopeForAudio(self.staticAudio) self.staticAudio.absolutePCMEnvelope = ProcessingEngine.calculateAbsolutePCMEnvelope( self.staticAudio.rawData) self.staticAudio.envelope = self.envelopeObj.threeStepEnvelope( self.staticAudio.rawData, meanWindowSize=200) envelopes = calculateNMaxFreqsEnvelopes() self.staticAudio.nfrequencyEnvelopes = envelopes for ch in self.staticAudio.chunks: ch.baseFrequency = ProcessingEngine.estimateHzByAutocorrelationMethod( ch.rawData) Logger.info("Cunks[{nr}] base Hz = {fq}".format( nr=ch.chunkNr, fq=ch.baseFrequency))
def saveRecordedDataToFile(self, pcm: np.ndarray, fileName='Record'): fileName += strftime("_%Y-%m-%d_%H-%M-%S", gmtime()) waveFile = wave.open(fileName + ".wav", 'wb') waveFile.setnchannels(Cai.numberOfChannels) waveFile.setsampwidth(Cai.sampleWidthInBytes) waveFile.setframerate(Cai.frameRate) waveFile.writeframes(pcm.tostring()) Logger.info("Saving recorded data as: " + fileName) waveFile.close()
def okAction(self): index = self.comboBox.currentIndex() Idi.currentlyUsedDeviceIndex = index Logger.info("Changed microphone device to: " + str(index)) self.parent.ear.close() self.parent.ear.pyAudio = pyaudio.PyAudio() self.parent.ear.stream_start() self.close()
def _validateFilePath(self, filePath): if not os.path.exists(filePath): Logger.info("Path to file does not exist! [{filepath}]".format( filepath=filePath)) raise ValueError( "Path to file does not exist! [{filepath}]".format( filepath=filePath)) return True
def validateOutputDevices(pyAudio: pyaudio.PyAudio()): validDevicesIndexes = [] for deviceIndex in range(pyAudio.get_device_count()): devInfo = pyAudio.get_device_info_by_index(deviceIndex) if devInfo["maxOutputChannels"] > 0: validDevicesIndexes.append(deviceIndex) Logger.info("Found {listLen} valid output devices".format( listLen=len(validDevicesIndexes))) return validDevicesIndexes
def open(self): self._stream = self.pyAudio.open( format=Cai.sampleWidthPyAudio, input_device_index=Idi.currentlyUsedDeviceIndex, channels=Cai.numberOfChannels, rate=Cai.frameRate, input=True, frames_per_buffer=Cai.getChunkSize()) Logger.info("Opening stream based on device: " + str(Idi.currentlyUsedDeviceIndex)) self.keepListening = True return self
def _logAudioInfo(self): Logger.info('''[{dateTime}] You choose : {}\t - nrOfFrames: {} - framerate: {} - nchannels: {} - sampwidth {} (in bytes) - comptype {} - compname {} - these are converted audio file informations'''#audio data will be converted to input device audio format - to properly operate on this two files .format(self.filePath, self.numberOfFrames, self.frameRate, self.numberOfChannels, self.sampleWidth, self.compType, self.compName, dateTime=str(datetime.now())) )
def __init__(self): Observable.__init__(self) Observer.__init__(self) self.pyAudio = pyaudio.PyAudio() Idi.currentlyUsedDeviceIndex = self.getValidDeviceIndex() self.setCommonAudioInformations() self.datax = np.arange(Cai.getChunkSize()) / float(Cai.frameRate) Logger.info("Using: {name} (device {device}) at {hz} Hz".format( name=Idi.name, device=Idi.currentlyUsedDeviceIndex, hz=Cai.frameRate)) self.chunksRead = 0 self.stream = Stream(self.pyAudio)
def signalMixer(data1: np.ndarray, data2: np.ndarray, saveFilePath="./mixed.wav"): segment1 = pydub.AudioSegment(data1, sample_width=Cai.sampleWidthInBytes, frame_rate=Cai.frameRate, channels=Cai.numberOfChannels) segment2 = pydub.AudioSegment(data2, sample_width=Cai.sampleWidthInBytes, frame_rate=Cai.frameRate, channels=Cai.numberOfChannels) combined_sounds = segment1 + segment2 combined_sounds.export(saveFilePath, format="wav") Logger.info("saved mixed audio file at {}".format(saveFilePath))
def validInputDevice(self, deviceIndex, rate=44100): """given a device ID and a rate, return TRUE/False if it's valid.""" try: info = self.pyAudio.get_device_info_by_index(deviceIndex) if info["maxInputChannels"] == 0: return False stream = self.pyAudio.open( format=pyaudio.paInt16, channels=1, input_device_index=deviceIndex, # , frames_per_buffer=self.chunk rate=int(info["defaultSampleRate"]), input=True) stream.close() return True except ValueError as e: Logger.info( "ValueError Exception Occured: I/O error({0}): {1}".format( e.errno, e.strerror)) return False
def __init__(self): if not Settings.ENABLE_PLUGINS: Logger.info("Plugins DISABLED!") self.pluginClassesObjects = [] # TODO: plugins package paths external config import os print(os.listdir("./")) self.foundPluginFiles = findPluginFiles( "./src/Engine/PluginPackage/plugins") self.pluginResponseDict: {} = {} for pluginName in self.foundPluginFiles: pluginClassObj = PluginLoader.loadPlugin(pluginName) if type( pluginClassObj ).__name__ == "StaticCorrPlugin": # Little hack for now :'D MessageServer.registerForEvent(pluginClassObj, MsgTypes.RECORDING_STOP) self.pluginClassesObjects.append(pluginClassObj) self.pluginResponseDict[pluginName] = []
def loadPlugin(cls, name): """ :param name: module file name :return: loaded module class object. """ name = name[:-3] # TODO: workaround for now to cut file Extension :P try: # TODO: validate if fetched class implements PluginAbstractModel import importlib module = importlib.import_module(PLUGIN_PACKAGE_PATH + "." + name) moduleClass = getattr(module, name) if moduleClass in cls.loadedModulesClasses: Logger.warninig("Module already loaded!") return cls.loadedModulesClasses.append(moduleClass) Logger.info("Dynamically loaded plugin :" + name) return moduleClass(Cai.getChunkSize()) except ValueError as e: # Exception? print("No Model named: " + name) raise e
def adjustAudioFormat(self, wav): rawData = wav.readframes(wav.getnframes()) # Raw audio data (in bytes) if wav.getnchannels() != Cai.numberOfChannels: # Can cause errors if some day Cai.numberOfChannels will be more than 1 Logger.info( "Converting number of channels: {source} -> {target}".format( source=wav.getnchannels(), target=Cai.numberOfChannels)) rawData = audioop.tomono(rawData, wav.getsampwidth(), 0.5, 0.5) if wav.getframerate() != Cai.frameRate: Logger.info("Resampling: {source} -> {target}".format( source=wav.getframerate(), target=Cai.frameRate)) try: rawData = resampy.resample( np.fromstring(rawData, dtype=np.int16), wav.getframerate(), Cai.frameRate).tostring() # TODO: resample sould recieve np.ndarray :float, not np.int16 except ValueError | TypeError as e: Logger.info("Resampling exception! ValueError | TypeError\n") raise e if wav.getsampwidth() != Cai.sampleWidthInBytes: Logger.info("BitDepth change: {source} -> {target}".format( source=wav.getsampwidth(), target=Cai.sampleWidthInBytes)) rawData = audioop.lin2lin(rawData, wav.getsampwidth(), Cai.sampleWidthInBytes) wav_file = wave.open(self.convertedFileName, 'w') wav_file.setparams( (Cai.numberOfChannels, Cai.sampleWidthInBytes, Cai.frameRate, int(len(rawData) / Cai.sampleWidthInBytes), 'NONE', "not compressed")) wav_file.writeframes(rawData) wav_file.close() return wave.open(self.convertedFileName)
def setCommonAudioInformations(self): info = self.pyAudio.get_device_info_by_index( Idi.currentlyUsedDeviceIndex) Cai.frameRate = int(info["defaultSampleRate"]) Idi.name = Idi.foundDevices.get(Idi.currentlyUsedDeviceIndex) Logger.logCommonAudioInformations()
def startRecording(self): Logger.info("Starting recording") MessageServer.notifyEventClients(MsgTypes.NEW_RECORDING)
def stopRecording(self): MessageServer.notifyEventClients(MsgTypes.RECORDING_STOP) self._shouldRecord = False Logger.info("Stopping recording.")
def closeOutputStream(self): self._stream.close() self.pyAudio.terminate() self.currentChunkNr = 0 Logger.info("Output stream closed.")
def open(self): self._stream = self.pyAudio.open(format=Cai.sampleWidthPyAudio, channels=Cai.numberOfChannels, rate=Cai.frameRate, output=True) Logger.info("Opening output stream")
def close(self): """gently detach from things.""" Logger.info(" -- sending stream termination command...") self.stream.close() self.pyAudio.terminate()
def handleNewChunk(self, data): if not ENABLE_PLUGINS: return for p in self.pluginClassesObjects: Logger.pluginLog("x ", p.process(data))
def compareSpectrumCentroid(self): diff = abs(self.staticChunk.spectralCentroid - self.liveChunk.spectralCentroid) Logger.centroidLog("StatCentroid={}\tliveCentroid={}\tdiff={}".format( self.staticChunk.spectralCentroid, self.liveChunk.spectralCentroid, diff))
def calculateFrequencyEnvelopeForAudio(self, audio: Audio): for c in audio.chunks: freq = ProcessingEngine.findLoudestFreqFromFFT( c.chunkAS, c.chunkFreqs) audio.nfrequencyEnvelopes.append(freq) Logger.info("{0}. ChunksFreq = {1}".format(c.chunkNr, freq))