diff --git a/csdr/chain/__init__.py b/csdr/chain/__init__.py index 7b1e47f..1a39d3d 100644 --- a/csdr/chain/__init__.py +++ b/csdr/chain/__init__.py @@ -49,19 +49,31 @@ class Chain: self.workers[index].stop() self.workers[index] = newWorker + error = None + if index == 0: if self.reader is not None: newWorker.setReader(self.reader) else: - previousWorker = self.workers[index - 1] - self._connect(previousWorker, newWorker) + try: + previousWorker = self.workers[index - 1] + self._connect(previousWorker, newWorker) + except ValueError as e: + # store error for later raising, but still attempt the second connection + error = e if index == len(self.workers) - 1: if self.writer is not None: newWorker.setWriter(self.writer) else: - nextWorker = self.workers[index + 1] - self._connect(newWorker, nextWorker) + try: + nextWorker = self.workers[index + 1] + self._connect(newWorker, nextWorker) + except ValueError as e: + error = e + + if error is not None: + raise e def append(self, newWorker): previousWorker = None diff --git a/csdr/chain/clientaudio.py b/csdr/chain/clientaudio.py index 39ae8d5..aab887a 100644 --- a/csdr/chain/clientaudio.py +++ b/csdr/chain/clientaudio.py @@ -3,11 +3,9 @@ from pycsdr.modules import AudioResampler, Convert, AdpcmEncoder, Limit from pycsdr.types import Format -class ClientAudioChain(Chain): - def __init__(self, format: Format, inputRate: int, clientRate: int, compression: str): +class Converter(Chain): + def __init__(self, format: Format, inputRate: int, clientRate: int): workers = [] - self.inputRate = inputRate - self.clientRate = clientRate if inputRate != clientRate: # we only have an audio resampler for float ATM so if we need to resample, we need to convert if format != Format.FLOAT: @@ -15,20 +13,39 @@ class ClientAudioChain(Chain): workers += [AudioResampler(inputRate, clientRate), Limit(), Convert(Format.FLOAT, Format.SHORT)] elif format != Format.SHORT: workers += [Convert(format, Format.SHORT)] + super().__init__(workers) + + +class ClientAudioChain(Chain): + def __init__(self, format: Format, inputRate: int, clientRate: int, compression: str): + self.format = format + self.inputRate = inputRate + self.clientRate = clientRate + workers = [self._buildConverter()] if compression == "adpcm": workers += [AdpcmEncoder(sync=True)] super().__init__(workers) + def _buildConverter(self): + return Converter(self.format, self.inputRate, self.clientRate) + def setFormat(self, format: Format) -> None: - pass + if format == self.format: + return + self.format = format + self.replace(0, self._buildConverter()) def setInputRate(self, inputRate: int) -> None: if inputRate == self.inputRate: return + self.inputRate = inputRate + self.replace(0, self._buildConverter()) def setClientRate(self, clientRate: int) -> None: if clientRate == self.clientRate: return + self.clientRate = clientRate + self.replace(0, self._buildConverter()) def setAudioCompression(self, compression: str) -> None: index = self.indexOf(lambda x: isinstance(x, AdpcmEncoder)) diff --git a/owrx/dsp.py b/owrx/dsp.py index df0253c..325eaaa 100644 --- a/owrx/dsp.py +++ b/owrx/dsp.py @@ -14,7 +14,7 @@ from csdr.chain.demodulator import BaseDemodulatorChain from csdr.chain.selector import Selector from csdr.chain.clientaudio import ClientAudioChain from csdr.chain.analog import NFm, WFm, Am, Ssb -from csdr.chain.digiham import Dmr, Dstar, Nxdn, Ysf +from csdr.chain.digiham import DigihamChain, Dmr, Dstar, Nxdn, Ysf from pycsdr.modules import Buffer, Writer from pycsdr.types import Format from typing import Union @@ -34,9 +34,18 @@ class ClientDemodulatorChain(Chain): self.selector.setBandpass(-4000, 4000) self.demodulator = demod self.clientAudioChain = ClientAudioChain(Format.FLOAT, outputRate, outputRate, audioCompression) + self.metaWriter = None + self.squelchLevel = -150 super().__init__([self.selector, self.demodulator, self.clientAudioChain]) def setDemodulator(self, demodulator: BaseDemodulatorChain): + try: + self.clientAudioChain.setFormat(demodulator.getOutputFormat()) + except ValueError: + # this will happen if the new format does not match the current demodulator. + # it's expected and should be mended when swapping out the demodulator in the next step + pass + self.replace(1, demodulator) if self.demodulator is not None: @@ -58,8 +67,11 @@ class ClientDemodulatorChain(Chain): if not demodulator.supportsSquelch(): self.selector.setSquelchLevel(-150) + else: + self.selector.setSquelchLevel(self.squelchLevel) - self.clientAudioChain.setFormat(demodulator.getOutputFormat()) + if self.metaWriter is not None and isinstance(demodulator, DigihamChain): + demodulator.setMetaWriter(self.metaWriter) def setLowCut(self, lowCut): self.selector.setLowCut(lowCut) @@ -78,6 +90,9 @@ class ClientDemodulatorChain(Chain): self.clientAudioChain.setAudioCompression(compression) def setSquelchLevel(self, level: float) -> None: + if level == self.squelchLevel: + return + self.squelchLevel = level if not self.demodulator.supportsSquelch(): return self.selector.setSquelchLevel(level) @@ -95,6 +110,13 @@ class ClientDemodulatorChain(Chain): def setPowerWriter(self, writer: Writer) -> None: self.selector.setPowerWriter(writer) + def setMetaWriter(self, writer: Writer) -> None: + if writer is self.metaWriter: + return + self.metaWriter = writer + if isinstance(self.demodulator, DigihamChain): + self.demodulator.setMetaWriter(self.metaWriter) + def setSampleRate(self, sampleRate: int) -> None: if sampleRate == self.sampleRate: return @@ -182,6 +204,12 @@ class DspManager(Output, SdrSourceEventClient): reader = buffer.getReader() self.send_output("smeter", reader.read) + # wire meta output + buffer = Buffer(Format.CHAR) + self.chain.setMetaWriter(buffer) + reader = buffer.getReader() + self.send_output("meta", reader.read) + def set_dial_freq(changes): if ( "center_freq" not in self.props diff --git a/owrx/meta.py b/owrx/meta.py index 9525673..63eb727 100644 --- a/owrx/meta.py +++ b/owrx/meta.py @@ -171,7 +171,9 @@ class MetaParser(Parser): } self.currentMetaData = None - def parse(self, raw: str): + def parse(self, raw: memoryview): + raw = raw.tobytes().decode("utf-8").rstrip("\n") + for meta in raw.split("\n"): fields = meta.split(";") meta = {v[0]: ":".join(v[1:]) for v in map(lambda x: x.split(":"), fields) if v[0] != ""}