diff --git a/csdr/csdr.py b/csdr/csdr.py index 6e25ac8..8ea941c 100644 --- a/csdr/csdr.py +++ b/csdr/csdr.py @@ -165,6 +165,7 @@ class dsp(object): def __init__(self, output): self.samp_rate = 250000 self.output_rate = 11025 + self.hd_output_rate = 44100 self.fft_size = 1024 self.fft_fps = 5 self.center_freq = 0 @@ -273,6 +274,13 @@ class dsp(object): ] else: chain += ["csdr convert_f_s16"] + elif which == "wfm": + chain += ["csdr fmdemod_quadri_cf", "csdr limit_ff"] + chain += last_decimation_block + chain += [ + "csdr deemphasis_wfm_ff {audio_rate} 50e-6", + "csdr convert_f_s16" + ] elif self.isDigitalVoice(which): chain += ["csdr fmdemod_quadri_cf", "dc_block "] chain += last_decimation_block @@ -305,7 +313,7 @@ class dsp(object): chain += ["csdr amdemod_cf", "csdr fastdcblock_ff"] chain += last_decimation_block chain += ["csdr agc_ff", "csdr limit_ff", "csdr convert_f_s16"] - elif which == "freedv": + elif self.isFreeDV(which): chain += ["csdr realpart_cf"] chain += last_decimation_block chain += [ @@ -585,13 +593,18 @@ class dsp(object): def get_output_rate(self): return self.output_rate + def get_hd_output_rate(self): + return self.hd_output_rate + def get_audio_rate(self): if self.isDigitalVoice() or self.isPacket() or self.isPocsag(): return 48000 elif self.isWsjtMode() or self.isJs8(): return 12000 - elif self.get_demodulator() == "freedv": + elif self.isFreeDV(): return 8000 + elif self.isHdAudio(): + return self.get_hd_output_rate() return self.get_output_rate() def isDigitalVoice(self, demodulator=None): @@ -619,6 +632,16 @@ class dsp(object): demodulator = self.get_secondary_demodulator() return demodulator == "pocsag" + def isFreeDV(self, demodulator=None): + if demodulator is None: + demodulator = self.get_demodulator() + return demodulator == "freedv" + + def isHdAudio(self, demodulator=None): + if demodulator is None: + demodulator = self.get_demodulator() + return demodulator == "wfm" + def set_output_rate(self, output_rate): if self.output_rate == output_rate: return @@ -626,6 +649,12 @@ class dsp(object): self.calculate_decimation() self.restart() + def set_hd_output_rate(self, hd_output_rate): + if self.hd_output_rate == hd_output_rate: + return + self.hd_output_rate = hd_output_rate + self.restart() + def set_demodulator(self, demodulator): if demodulator in ["usb", "lsb", "cw"]: demodulator = "ssb" @@ -685,7 +714,7 @@ class dsp(object): def set_squelch_level(self, squelch_level): self.squelch_level = squelch_level # no squelch required on digital voice modes - actual_squelch = -150 if self.isDigitalVoice() or self.isPacket() or self.isPocsag() or self.get_demodulator() == "freedv" else self.squelch_level + actual_squelch = -150 if self.isDigitalVoice() or self.isPacket() or self.isPocsag() or self.isFreeDV() else self.squelch_level if self.running: self.pipes["squelch_pipe"].write("%g\n" % (self.convertToLinear(actual_squelch))) @@ -818,9 +847,10 @@ class dsp(object): threading.Thread(target=watch_thread).start() - if self.output.supports_type("audio"): + audio_type = "hd_audio" if self.isHdAudio() else "audio" + if self.output.supports_type(audio_type): self.output.send_output( - "audio", + audio_type, partial( self.process.stdout.read, self.get_fft_bytes_to_read() if self.demodulator == "fft" else self.get_audio_bytes_to_read(), diff --git a/htdocs/lib/AudioEngine.js b/htdocs/lib/AudioEngine.js index b82b9de..a4cffae 100644 --- a/htdocs/lib/AudioEngine.js +++ b/htdocs/lib/AudioEngine.js @@ -238,6 +238,28 @@ AudioEngine.prototype.pushAudio = function(data) { } }; +AudioEngine.prototype.pushHdAudio = function(data) { + if (!this.audioNode) return; + this.audioBytes.add(data.byteLength); + var buffer; + if (this.compression === "adpcm") { + //resampling & ADPCM + buffer = this.audioCodec.decode(new Uint8Array(data)); + } else { + buffer = new Int16Array(data); + } + buffer = this.hdResampler.process(buffer); + if (this.audioNode.port) { + // AudioWorklets supported + this.audioNode.port.postMessage(buffer); + } else { + // silently drop excess samples + if (this.getBuffersize() + buffer.length <= this.maxBufferSize) { + this.audioBuffers.push(buffer); + } + } +} + AudioEngine.prototype.setCompression = function(compression) { this.compression = compression; }; diff --git a/htdocs/openwebrx.js b/htdocs/openwebrx.js index 9d17f3b..7c24a23 100644 --- a/htdocs/openwebrx.js +++ b/htdocs/openwebrx.js @@ -829,6 +829,10 @@ function on_ws_recv(evt) { secondary_demod_waterfall_add(waterfall_f32); } break; + case 4: + // hd audio data + audioEngine.pushHdAudio(data); + break; default: console.warn('unknown type of binary message: ' + type) } diff --git a/owrx/connection.py b/owrx/connection.py index dd8cc27..6d43579 100644 --- a/owrx/connection.py +++ b/owrx/connection.py @@ -299,6 +299,9 @@ class OpenWebRxReceiverClient(OpenWebRxClient): def write_dsp_data(self, data): self.send(bytes([0x02]) + data) + def write_hd_audio(self, data): + self.send(bytes([0x04]) + data) + def write_s_meter_level(self, level): self.send({"type": "smeter", "value": level}) diff --git a/owrx/dsp.py b/owrx/dsp.py index 1bbf1f8..4343564 100644 --- a/owrx/dsp.py +++ b/owrx/dsp.py @@ -95,6 +95,7 @@ class DspManager(csdr.output): self.props.wireProperty("digimodes_fft_size", self.dsp.set_secondary_fft_size), self.props.wireProperty("samp_rate", self.dsp.set_samp_rate), self.props.wireProperty("output_rate", self.dsp.set_output_rate), + self.props.wireProperty("hd_output_rate", self.dsp.set_hd_output_rate), self.props.wireProperty("offset_freq", self.dsp.set_offset_freq), self.props.wireProperty("center_freq", self.dsp.set_center_freq), self.props.wireProperty("squelch_level", self.dsp.set_squelch_level), @@ -147,6 +148,7 @@ class DspManager(csdr.output): logger.debug("adding new output of type %s", t) writers = { "audio": self.handler.write_dsp_data, + "hd_audio": self.handler.write_hd_audio, "smeter": self.handler.write_s_meter_level, "secondary_fft": self.handler.write_secondary_fft, "secondary_demod": self.handler.write_secondary_demod, diff --git a/owrx/modes.py b/owrx/modes.py index bd930b8..fca97cf 100644 --- a/owrx/modes.py +++ b/owrx/modes.py @@ -40,6 +40,7 @@ class DigitalMode(Mode): class Modes(object): mappings = [ AnalogMode("nfm", "FM", bandpass=Bandpass(-4000, 4000)), + AnalogMode("wfm", "WFM", bandpass=Bandpass(-16000, 16000)), AnalogMode("am", "AM", bandpass=Bandpass(-4000, 4000)), AnalogMode("lsb", "LSB", bandpass=Bandpass(-3000, -300)), AnalogMode("usb", "USB", bandpass=Bandpass(300, 3000)),