2021-04-11 12:40:28 +00:00
|
|
|
from owrx.modes import Modes, AudioChopperMode
|
2021-09-27 22:27:01 +00:00
|
|
|
from owrx.audio import AudioChopperProfile
|
2021-04-11 12:40:28 +00:00
|
|
|
from itertools import groupby
|
2021-04-11 16:46:21 +00:00
|
|
|
from owrx.audio import ProfileSourceSubscriber
|
2021-04-11 12:40:28 +00:00
|
|
|
from owrx.audio.wav import AudioWriter
|
2021-08-31 20:46:11 +00:00
|
|
|
from owrx.audio.queue import QueueJob
|
2021-09-06 18:00:14 +00:00
|
|
|
from csdr.module import ThreadModule
|
2021-09-06 13:05:33 +00:00
|
|
|
from pycsdr.types import Format
|
2021-09-27 22:27:01 +00:00
|
|
|
from abc import ABC, abstractmethod
|
2021-08-31 14:54:37 +00:00
|
|
|
import pickle
|
2021-04-11 12:40:28 +00:00
|
|
|
|
|
|
|
import logging
|
|
|
|
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
logger.setLevel(logging.INFO)
|
|
|
|
|
|
|
|
|
2021-09-27 22:27:01 +00:00
|
|
|
class AudioChopperParser(ABC):
|
|
|
|
@abstractmethod
|
|
|
|
def parse(self, profile: AudioChopperProfile, frequency: int, line: bytes):
|
|
|
|
pass
|
|
|
|
|
|
|
|
|
2021-09-06 18:00:14 +00:00
|
|
|
class AudioChopper(ThreadModule, ProfileSourceSubscriber):
|
2021-09-27 22:27:01 +00:00
|
|
|
def __init__(self, mode_str: str, parser: AudioChopperParser):
|
2021-08-31 14:54:37 +00:00
|
|
|
self.parser = parser
|
2021-08-31 20:46:11 +00:00
|
|
|
self.dialFrequency = None
|
2021-04-11 16:46:21 +00:00
|
|
|
self.doRun = True
|
|
|
|
self.writers = []
|
2021-04-11 12:40:28 +00:00
|
|
|
mode = Modes.findByModulation(mode_str)
|
|
|
|
if mode is None or not isinstance(mode, AudioChopperMode):
|
|
|
|
raise ValueError("Mode {} is not an audio chopper mode".format(mode_str))
|
2021-04-11 16:46:21 +00:00
|
|
|
self.profile_source = mode.get_profile_source()
|
2021-04-11 12:40:28 +00:00
|
|
|
super().__init__()
|
|
|
|
|
2021-09-06 13:05:33 +00:00
|
|
|
def getInputFormat(self) -> Format:
|
|
|
|
return Format.SHORT
|
|
|
|
|
|
|
|
def getOutputFormat(self) -> Format:
|
|
|
|
return Format.CHAR
|
|
|
|
|
2021-04-11 16:46:21 +00:00
|
|
|
def stop_writers(self):
|
|
|
|
while self.writers:
|
|
|
|
self.writers.pop().stop()
|
|
|
|
|
|
|
|
def setup_writers(self):
|
|
|
|
self.stop_writers()
|
|
|
|
sorted_profiles = sorted(self.profile_source.getProfiles(), key=lambda p: p.getInterval())
|
|
|
|
groups = {interval: list(group) for interval, group in groupby(sorted_profiles, key=lambda p: p.getInterval())}
|
2021-05-01 14:51:02 +00:00
|
|
|
writers = [
|
2021-08-31 14:54:37 +00:00
|
|
|
AudioWriter(self, interval, profiles) for interval, profiles in groups.items()
|
2021-05-01 14:51:02 +00:00
|
|
|
]
|
2021-04-11 18:10:49 +00:00
|
|
|
for w in writers:
|
2021-04-11 16:46:21 +00:00
|
|
|
w.start()
|
2021-04-11 18:10:49 +00:00
|
|
|
self.writers = writers
|
2021-04-11 16:46:21 +00:00
|
|
|
|
2021-04-11 12:40:28 +00:00
|
|
|
def run(self) -> None:
|
|
|
|
logger.debug("Audio chopper starting up")
|
2021-04-11 16:46:21 +00:00
|
|
|
self.setup_writers()
|
|
|
|
self.profile_source.subscribe(self)
|
2021-04-11 12:40:28 +00:00
|
|
|
while self.doRun:
|
|
|
|
data = None
|
|
|
|
try:
|
2021-08-31 14:54:37 +00:00
|
|
|
data = self.reader.read()
|
2021-04-11 12:40:28 +00:00
|
|
|
except ValueError:
|
|
|
|
pass
|
2021-08-31 14:54:37 +00:00
|
|
|
if data is None:
|
2021-04-11 12:40:28 +00:00
|
|
|
self.doRun = False
|
|
|
|
else:
|
|
|
|
for w in self.writers:
|
2021-08-31 14:54:37 +00:00
|
|
|
w.write(data.tobytes())
|
2021-04-11 12:40:28 +00:00
|
|
|
|
|
|
|
logger.debug("Audio chopper shutting down")
|
2021-04-11 16:46:21 +00:00
|
|
|
self.profile_source.unsubscribe(self)
|
|
|
|
self.stop_writers()
|
|
|
|
|
|
|
|
def onProfilesChanged(self):
|
|
|
|
logger.debug("profile change received, resetting writers...")
|
|
|
|
self.setup_writers()
|
2021-04-11 12:40:28 +00:00
|
|
|
|
2021-08-31 20:46:11 +00:00
|
|
|
def setDialFrequency(self, frequency: int) -> None:
|
|
|
|
self.dialFrequency = frequency
|
|
|
|
|
|
|
|
def createJob(self, profile, filename):
|
|
|
|
return QueueJob(profile, self.dialFrequency, self, filename)
|
|
|
|
|
|
|
|
def sendResult(self, result):
|
|
|
|
for line in result.lines:
|
|
|
|
data = self.parser.parse(result.profile, result.frequency, line)
|
|
|
|
if data is not None and self.writer is not None:
|
|
|
|
self.writer.write(pickle.dumps(data))
|