From 853ee5b024bc68deb740592e628263bb1414ea24 Mon Sep 17 00:00:00 2001 From: Jakob Ketterl Date: Wed, 10 Jul 2024 13:49:40 +0200 Subject: [PATCH] preliminary work to allow dynamic audio format switching --- csdr/chain/dablin.py | 12 +++++++----- csdr/chain/demodulator.py | 37 +++++++++++++++++++++++++++++++++++++ owrx/dab/dablin.py | 2 +- owrx/dsp.py | 14 ++++++++++++-- 4 files changed, 57 insertions(+), 8 deletions(-) diff --git a/csdr/chain/dablin.py b/csdr/chain/dablin.py index c5a32116..0da6bcf3 100644 --- a/csdr/chain/dablin.py +++ b/csdr/chain/dablin.py @@ -1,4 +1,4 @@ -from csdr.chain.demodulator import BaseDemodulatorChain, FixedIfSampleRateChain, FixedAudioRateChain, HdAudio, \ +from csdr.chain.demodulator import BaseDemodulatorChain, FixedIfSampleRateChain, DynamicAudioRateChain, HdAudio, \ MetaProvider, DabServiceSelector, DialFrequencyReceiver from csdr.module import PickleModule from csdreti.modules import EtiDecoder @@ -58,8 +58,10 @@ class MetaProcessor(PickleModule): self.shifter.setRate(0) -class Dablin(BaseDemodulatorChain, FixedIfSampleRateChain, FixedAudioRateChain, HdAudio, MetaProvider, DabServiceSelector, DialFrequencyReceiver): +class Dablin(BaseDemodulatorChain, FixedIfSampleRateChain, DynamicAudioRateChain, HdAudio, MetaProvider, DabServiceSelector, DialFrequencyReceiver): def __init__(self): + self.audioRate = 48000 + shift = Shift(0) self.decoder = EtiDecoder() @@ -77,7 +79,7 @@ class Dablin(BaseDemodulatorChain, FixedIfSampleRateChain, FixedAudioRateChain, shift, self.decoder, self.dablin, - Downmix(Format.FLOAT), + Downmix(Format.SHORT), ] super().__init__(workers) @@ -90,8 +92,8 @@ class Dablin(BaseDemodulatorChain, FixedIfSampleRateChain, FixedAudioRateChain, def getFixedIfSampleRate(self) -> int: return 2048000 - def getFixedAudioRate(self) -> int: - return 48000 + def getAudioRate(self) -> int: + return self.audioRate def stop(self): self.processor.stop() diff --git a/csdr/chain/demodulator.py b/csdr/chain/demodulator.py index 7b4506cc..49268f7f 100644 --- a/csdr/chain/demodulator.py +++ b/csdr/chain/demodulator.py @@ -2,6 +2,10 @@ from csdr.chain import Chain from abc import ABC, ABCMeta, abstractmethod from pycsdr.modules import Writer +import logging + +logger = logging.getLogger(__name__) + class FixedAudioRateChain(ABC): @abstractmethod @@ -9,6 +13,39 @@ class FixedAudioRateChain(ABC): pass +class DynamicAudioRateListener(ABC): + @abstractmethod + def onAudioRateChange(self, rate: int): + pass + + +class DynamicAudioRateChain(ABC): + def __init__(self): + self.listeners = [] + super().__init__() + + def addListener(self, listener: DynamicAudioRateListener): + if listener in self.listeners: + return + self.listeners.append(listener) + + def removeListener(self, listener: DynamicAudioRateListener): + if listener not in self.listeners: + return + self.listeners.remove(listener) + + def fireAudioRateChange(self, rate: int): + for listener in self.listeners: + try: + listener.onAudioRateChange(rate) + except: + logger.exception("error while sending audio rate change") + + @abstractmethod + def getAudioRate(self) -> int: + pass + + class FixedIfSampleRateChain(ABC): @abstractmethod def getFixedIfSampleRate(self) -> int: diff --git a/owrx/dab/dablin.py b/owrx/dab/dablin.py index ad7d2641..01c042f7 100644 --- a/owrx/dab/dablin.py +++ b/owrx/dab/dablin.py @@ -7,7 +7,7 @@ class DablinModule(ExecModule): self.serviceId = 0 super().__init__( Format.CHAR, - Format.FLOAT, + Format.SHORT, self._buildArgs() ) diff --git a/owrx/dsp.py b/owrx/dsp.py index 625254ea..765b2ca3 100644 --- a/owrx/dsp.py +++ b/owrx/dsp.py @@ -5,7 +5,7 @@ from owrx.modes import Modes, DigitalMode from csdr.chain import Chain from csdr.chain.demodulator import BaseDemodulatorChain, FixedIfSampleRateChain, FixedAudioRateChain, HdAudio, \ SecondaryDemodulator, DialFrequencyReceiver, MetaProvider, SlotFilterChain, SecondarySelectorChain, \ - DeemphasisTauChain, DemodulatorError, RdsChain, DabServiceSelector + DeemphasisTauChain, DemodulatorError, RdsChain, DabServiceSelector, DynamicAudioRateChain, DynamicAudioRateListener from csdr.chain.selector import Selector, SecondarySelector from csdr.chain.clientaudio import ClientAudioChain from csdr.chain.fft import FftChain @@ -35,7 +35,7 @@ class ClientDemodulatorSecondaryDspEventClient(ABC): pass -class ClientDemodulatorChain(Chain): +class ClientDemodulatorChain(Chain, DynamicAudioRateListener): def __init__(self, demod: BaseDemodulatorChain, sampleRate: int, outputRate: int, hdOutputRate: int, audioCompression: str, secondaryDspEventReceiver: ClientDemodulatorSecondaryDspEventClient): self.sampleRate = sampleRate self.outputRate = outputRate @@ -101,11 +101,16 @@ class ClientDemodulatorChain(Chain): if self.demodulator is not None: self.demodulator.stop() + if isinstance(self.demodulator, DynamicAudioRateChain): + self.demodulator.removeListener(self) self.demodulator = demodulator self.selector.setOutputRate(self._getSelectorOutputRate()) + if isinstance(self.demodulator, DynamicAudioRateChain): + self.demodulator.addListener(self) + clientRate = self._getClientAudioInputRate() self.demodulator.setSampleRate(clientRate) @@ -155,6 +160,8 @@ class ClientDemodulatorChain(Chain): def _getClientAudioInputRate(self): if isinstance(self.demodulator, FixedAudioRateChain): return self.demodulator.getFixedAudioRate() + elif isinstance(self.demodulator, DynamicAudioRateChain): + return self.demodulator.getAudioRate() elif isinstance(self.secondaryDemodulator, FixedAudioRateChain): return self.secondaryDemodulator.getFixedAudioRate() else: @@ -395,6 +402,9 @@ class ClientDemodulatorChain(Chain): if isinstance(self.demodulator, RdsChain): self.demodulator.setRdsRbds(self.rdsRbds) + def onAudioRateChange(self, rate: int): + self.clientAudioChain.setInputRate(self._getClientAudioInputRate()) + class ModulationValidator(OrValidator): """