bool LocalRxBase::initialize(void) { if (!Rx::initialize()) { return false; } bool deemphasis = false; cfg.getValue(name(), "DEEMPHASIS", deemphasis); int delay_line_len = 0; bool mute_1750 = false; if (cfg.getValue(name(), "1750_MUTING", mute_1750)) { delay_line_len = max(delay_line_len, TONE_1750_MUTING_PRE); } cfg.getValue(name(), "SQL_TAIL_ELIM", sql_tail_elim); if (sql_tail_elim > 0) { delay_line_len = max(delay_line_len, sql_tail_elim); } cfg.getValue(name(), "PREAMP", preamp_gain); bool peak_meter = false; cfg.getValue(name(), "PEAK_METER", peak_meter); // Get the audio source object AudioSource *prev_src = audioSource(); assert(prev_src != 0); // Create a fifo buffer to handle large audio blocks input_fifo = new AudioFifo(1024); // input_fifo->setOverwrite(true); prev_src->registerSink(input_fifo); prev_src = input_fifo; SvxLink::SepPair<string, uint16_t> raw_audio_fwd_dest; if (cfg.getValue(name(), "RAW_AUDIO_UDP_DEST", raw_audio_fwd_dest)) { AudioSplitter *raw_audio_splitter = new AudioSplitter; prev_src->registerSink(raw_audio_splitter, true); AudioPassthrough *pass = new AudioPassthrough; raw_audio_splitter->addSink(pass, true); prev_src = pass; AudioUdpSink *udp = new AudioUdpSink(IpAddress(raw_audio_fwd_dest.first), raw_audio_fwd_dest.second); if (!udp->initOk()) { cerr << "*** ERROR: Could not open UDP socket for raw audio output\n"; return false; } raw_audio_splitter->addSink(udp, true); } // If a preamp was configured, create it if (preamp_gain != 0) { AudioAmp *preamp = new AudioAmp; preamp->setGain(preamp_gain); prev_src->registerSink(preamp, true); prev_src = preamp; } // If a peak meter was configured, create it if (peak_meter) { PeakMeter *peak_meter = new PeakMeter(name()); prev_src->registerSink(peak_meter, true); prev_src = peak_meter; } // If the sound card sample rate is higher than 16kHz (48kHz assumed), // decimate it down to 16kHz if (audioSampleRate() > 16000) { AudioDecimator *d1 = new AudioDecimator(3, coeff_48_16_wide, coeff_48_16_wide_taps); prev_src->registerSink(d1, true); prev_src = d1; } AudioSplitter *siglevdet_splitter = 0; siglevdet_splitter = new AudioSplitter; prev_src->registerSink(siglevdet_splitter, true); // Create the signal level detector siglevdet = SigLevDetFactoryBase::createNamedSigLevDet(cfg, name()); if ((siglevdet == 0) || (!siglevdet->initialize(cfg, name(), INTERNAL_SAMPLE_RATE))) { cout << "*** ERROR: Could not initialize the signal level detector for " << "receiver " << name() << endl; delete siglevdet; siglevdet = 0; return false; } siglevdet->setIntegrationTime(0); siglevdet->signalLevelUpdated.connect( mem_fun(*this, &LocalRxBase::onSignalLevelUpdated)); siglevdet_splitter->addSink(siglevdet, true); // Create a mute valve mute_valve = new AudioValve; mute_valve->setOpen(true); siglevdet_splitter->addSink(mute_valve, true); prev_src = mute_valve; #if (INTERNAL_SAMPLE_RATE != 16000) // If the sound card sample rate is higher than 8kHz (16 or 48kHz assumed) // decimate it down to 8kHz. // 16kHz audio to other consumers. if (audioSampleRate() > 8000) { AudioDecimator *d2 = new AudioDecimator(2, coeff_16_8, coeff_16_8_taps); prev_src->registerSink(d2, true); prev_src = d2; } #endif // If a deemphasis filter was configured, create it if (deemphasis) { //AudioFilter *deemph_filt = new AudioFilter("LpBu1/300"); //AudioFilter *deemph_filt = new AudioFilter("HsBq1/0.01/-18/3500"); //AudioFilter *deemph_filt = new AudioFilter("HsBq1/0.05/-36/3500"); //deemph_filt->setOutputGain(9.0f); //AudioFilter *deemph_filt = new AudioFilter("HpBu1/50 x LpBu1/150"); //deemph_filt->setOutputGain(7.0f); DeemphasisFilter *deemph_filt = new DeemphasisFilter; prev_src->registerSink(deemph_filt, true); prev_src = deemph_filt; } // Create a splitter to distribute full bandwidth audio to all consumers AudioSplitter *fullband_splitter = new AudioSplitter; prev_src->registerSink(fullband_splitter, true); prev_src = fullband_splitter; // Create the configured squelch detector and initialize it string sql_det_str; if (!cfg.getValue(name(), "SQL_DET", sql_det_str)) { cerr << "*** ERROR: Config variable " << name() << "/SQL_DET not set\n"; return false; } if (sql_det_str == "OPEN") { squelch_det = new SquelchOpen; } else if (sql_det_str == "VOX") { squelch_det = new SquelchVox; } else if (sql_det_str == "CTCSS") { SquelchCtcss *squelch_ctcss = new SquelchCtcss; squelch_ctcss->snrUpdated.connect(ctcssSnrUpdated.make_slot()); squelch_det = squelch_ctcss; } else if (sql_det_str == "SERIAL") { squelch_det = new SquelchSerial; } else if (sql_det_str == "SIGLEV") { squelch_det = new SquelchSigLev(siglevdet); } else if (sql_det_str == "EVDEV") { squelch_det = new SquelchEvDev; } else if (sql_det_str == "GPIO") { squelch_det = new SquelchGpio; } else if (sql_det_str == "PTY") { squelch_det = new SquelchPty; } #ifdef HAS_HIDRAW_SUPPORT else if (sql_det_str == "HIDRAW") { squelch_det = new SquelchHidraw; } #endif else { cerr << "*** ERROR: Unknown squelch type specified in config variable " << name() << "/SQL_DET. Legal values are: OPEN, VOX, CTCSS, SIGLEV, " << "EVDEV, GPIO, PTY and SERIAL\n"; // FIXME: Cleanup return false; } if (!squelch_det->initialize(cfg, name())) { cerr << "*** ERROR: Squelch detector initialization failed for RX \"" << name() << "\"\n"; delete squelch_det; squelch_det = 0; // FIXME: Cleanup return false; } readyStateChanged.connect(mem_fun(*this, &LocalRxBase::rxReadyStateChanged)); if (cfg.getValue(name(), "SQL_HANGTIME", sql_hangtime)) { squelch_det->setHangtime(sql_hangtime); } cfg.getValue(name(), "SQL_EXTENDED_HANGTIME", sql_extended_hangtime); cfg.getValue(name(), "SQL_EXTENDED_HANGTIME_THRESH", sql_extended_hangtime_thresh); squelch_det->squelchOpen.connect(mem_fun(*this, &LocalRxBase::onSquelchOpen)); fullband_splitter->addSink(squelch_det, true); // Create a new audio splitter to handle tone detectors tone_dets = new AudioSplitter; prev_src->registerSink(tone_dets, true); prev_src = tone_dets; // Filter out the voice band, removing high- and subaudible frequencies, // for example CTCSS. #if (INTERNAL_SAMPLE_RATE == 16000) AudioFilter *voiceband_filter = new AudioFilter("BpCh10/-0.1/300-5000"); #else AudioFilter *voiceband_filter = new AudioFilter("BpCh10/-0.1/300-3500"); #endif prev_src->registerSink(voiceband_filter, true); prev_src = voiceband_filter; // Create an audio splitter to distribute the voiceband audio to all // other consumers AudioSplitter *voiceband_splitter = new AudioSplitter; prev_src->registerSink(voiceband_splitter, true); prev_src = voiceband_splitter; // Create the configured type of DTMF decoder and add it to the splitter string dtmf_dec_type("NONE"); cfg.getValue(name(), "DTMF_DEC_TYPE", dtmf_dec_type); if (dtmf_dec_type != "NONE") { DtmfDecoder *dtmf_dec = DtmfDecoder::create(cfg, name()); if ((dtmf_dec == 0) || !dtmf_dec->initialize()) { // FIXME: Cleanup? delete dtmf_dec; return false; } dtmf_dec->digitActivated.connect( mem_fun(*this, &LocalRxBase::dtmfDigitActivated)); dtmf_dec->digitDeactivated.connect( mem_fun(*this, &LocalRxBase::dtmfDigitDeactivated)); voiceband_splitter->addSink(dtmf_dec, true); bool dtmf_muting = false; cfg.getValue(name(), "DTMF_MUTING", dtmf_muting); if (dtmf_muting) { dtmf_muting_pre = dtmf_dec->detectionTime(); delay_line_len = max(delay_line_len, dtmf_muting_pre); } } // Create a selective multiple tone detector object string sel5_dec_type("NONE"); cfg.getValue(name(), "SEL5_DEC_TYPE", sel5_dec_type); if (sel5_dec_type != "NONE") { Sel5Decoder *sel5_dec = Sel5Decoder::create(cfg, name()); if (sel5_dec == 0 || !sel5_dec->initialize()) { cerr << "*** ERROR: Sel5 decoder initialization failed for RX \"" << name() << "\"\n"; return false; } sel5_dec->sequenceDetected.connect( mem_fun(*this, &LocalRxBase::sel5Detected)); voiceband_splitter->addSink(sel5_dec, true); } // Create an audio valve to use as squelch and connect it to the splitter sql_valve = new AudioValve; sql_valve->setOpen(false); prev_src->registerSink(sql_valve, true); prev_src = sql_valve; // Create the state detector AudioStreamStateDetector *state_det = new AudioStreamStateDetector; state_det->sigStreamStateChanged.connect( mem_fun(*this, &LocalRxBase::audioStreamStateChange)); prev_src->registerSink(state_det, true); prev_src = state_det; // If we need a delay line (e.g. for DTMF muting and/or squelch tail // elimination), create it if (delay_line_len > 0) { delay = new AudioDelayLine(delay_line_len); prev_src->registerSink(delay, true); prev_src = delay; } // Add a limiter to smoothly limiting the audio before hard clipping it AudioCompressor *limit = new AudioCompressor; limit->setThreshold(-1); limit->setRatio(0.1); limit->setAttack(2); limit->setDecay(20); limit->setOutputGain(1); prev_src->registerSink(limit, true); prev_src = limit; // Clip audio to limit its amplitude AudioClipper *clipper = new AudioClipper; clipper->setClipLevel(0.98); prev_src->registerSink(clipper, true); prev_src = clipper; // Remove high frequencies generated by the previous clipping #if (INTERNAL_SAMPLE_RATE == 16000) AudioFilter *splatter_filter = new AudioFilter("LpCh9/-0.05/5000"); #else AudioFilter *splatter_filter = new AudioFilter("LpCh9/-0.05/3500"); #endif prev_src->registerSink(splatter_filter, true); prev_src = splatter_filter; // Set the previous audio pipe object to handle audio distribution for // the LocalRxBase class setHandler(prev_src); // Open the audio device for reading if (!audioOpen()) { // FIXME: Cleanup? return false; } if (mute_1750) { ToneDetector *calldet = new ToneDetector(1750, 50, 100); assert(calldet != 0); calldet->setPeakThresh(13); calldet->activated.connect(mem_fun(*this, &LocalRxBase::tone1750detected)); voiceband_splitter->addSink(calldet, true); //cout << "### Enabling 1750Hz muting\n"; } return true; } /* LocalRxBase:initialize */
bool LocalRx::initialize(void) { if (!Rx::initialize()) { return false; } string value; string audio_dev; if (!cfg.getValue(name(), "AUDIO_DEV", audio_dev)) { cerr << "*** ERROR: Config variable " << name() << "/AUDIO_DEV not set\n"; return false; } if (!cfg.getValue(name(), "AUDIO_CHANNEL", value)) { cerr << "*** ERROR: Config variable " << name() << "/AUDIO_CHANNEL not set\n"; return false; } int audio_channel = atoi(value.c_str()); bool deemphasis = false; if (cfg.getValue(name(), "DEEMPHASIS", value)) { deemphasis = (atoi(value.c_str()) != 0); } int delay_line_len = 0; if (cfg.getValue(name(), "MUTE_DTMF", value)) { cerr << "*** ERROR: The MUTE_DTMF configuration variable has been\n" << " renamed to DTMF_MUTING. Change this in configuration\n" << " section \"" << name() << "\".\n"; return false; } if (cfg.getValue(name(), "DTMF_MUTING", value)) { mute_dtmf = (atoi(value.c_str()) != 0); delay_line_len = DTMF_MUTING_PRE; } if (cfg.getValue(name(), "SQL_TAIL_ELIM", value)) { sql_tail_elim = atoi(value.c_str()); delay_line_len = max(delay_line_len, sql_tail_elim); } if (cfg.getValue(name(), "PREAMP", value)) { preamp_gain = atoi(value.c_str()); } bool peak_meter = false; if (cfg.getValue(name(), "PEAK_METER", value)) { peak_meter = (atoi(value.c_str()) != 0); } int dtmf_hangtime = 100; if (cfg.getValue(name(), "DTMF_HANGTIME", value)) { dtmf_hangtime = atoi(value.c_str()); } // Create the audio IO object audio_io = new AudioIO(audio_dev, audio_channel); //FIXME: Check that the audio device is correctly initialized // before continuing. AudioSource *prev_src = audio_io; // Create a fifo buffer to handle large audio blocks AudioFifo *input_fifo = new AudioFifo(1024); // input_fifo->setOverwrite(true); prev_src->registerSink(input_fifo, true); prev_src = input_fifo; // If a preamp was configured, create it if (preamp_gain != 0) { AudioAmp *preamp = new AudioAmp; preamp->setGain(preamp_gain); prev_src->registerSink(preamp, true); prev_src = preamp; } // If a peak meter was configured, create it if (peak_meter) { PeakMeter *peak_meter = new PeakMeter(name()); prev_src->registerSink(peak_meter, true); prev_src = peak_meter; } // If the sound card sample rate is higher than 16kHz (48kHz assumed), // decimate it down to 16kHz if (audio_io->sampleRate() > 16000) { AudioDecimator *d1 = new AudioDecimator(3, coeff_48_16_wide, coeff_48_16_wide_taps); prev_src->registerSink(d1, true); prev_src = d1; } // If the sound card sample rate is higher than 8kHz (16 or 48kHz assumed) // decimate it down to 8kHz. Also create a splitter to distribute the // 16kHz audio to other consumers. #if (INTERNAL_SAMPLE_RATE != 16000) AudioSplitter *rate_16k_splitter = 0; if (audio_io->sampleRate() > 8000) { rate_16k_splitter = new AudioSplitter; prev_src->registerSink(rate_16k_splitter, true); AudioDecimator *d2 = new AudioDecimator(2, coeff_16_8, coeff_16_8_taps); //prev_src->registerSink(d2, true); rate_16k_splitter->addSink(d2, true); prev_src = d2; } #endif // If a deemphasis filter was configured, create it if (deemphasis) { //AudioFilter *deemph_filt = new AudioFilter("LpBu1/300"); //AudioFilter *deemph_filt = new AudioFilter("HsBq1/0.01/-18/3500"); AudioFilter *deemph_filt = new AudioFilter("HsBq1/0.05/-36/3500"); deemph_filt->setOutputGain(2.88); prev_src->registerSink(deemph_filt, true); prev_src = deemph_filt; } // Create an audio splitter to distribute the 8kHz audio to all consumers AudioSplitter *splitter = new AudioSplitter; prev_src->registerSink(splitter, true); prev_src = 0; // Create the signal level detector. Connect it to the 16 or 8kHz splitter // depending on how the sound card sample rate is setup. #if (INTERNAL_SAMPLE_RATE != 16000) if (rate_16k_splitter != 0) { siglevdet = createSigLevDet(name(), 16000); if (siglevdet == 0) { return false; } rate_16k_splitter->addSink(siglevdet, true); } else { siglevdet = createSigLevDet(name(), 8000); if (siglevdet == 0) { return false; } splitter->addSink(siglevdet, true); } #else siglevdet = createSigLevDet(name(), 16000); if (siglevdet == 0) { return false; } splitter->addSink(siglevdet, true); #endif // Create the configured squech detector and initialize it. Then connect // it to the 8kHz audio splitter string sql_det_str; if (!cfg.getValue(name(), "SQL_DET", sql_det_str)) { cerr << "*** ERROR: Config variable " << name() << "/SQL_DET not set\n"; return false; } if (sql_det_str == "VOX") { squelch_det = new SquelchVox; } else if (sql_det_str == "CTCSS") { squelch_det = new SquelchCtcss; } else if (sql_det_str == "SERIAL") { squelch_det = new SquelchSerial; } else if (sql_det_str == "SIGLEV") { squelch_det = new SquelchSigLev(siglevdet); } else if (sql_det_str == "EVDEV") { squelch_det = new SquelchEvDev; } else { cerr << "*** ERROR: Unknown squelch type specified in config variable " << name() << "/SQL_DET. Legal values are: VOX, CTCSS, SIGLEV, " << "EVDEV and SERIAL\n"; // FIXME: Cleanup return false; } if (!squelch_det->initialize(cfg, name())) { cerr << "*** ERROR: Squelch detector initialization failed for RX \"" << name() << "\"\n"; delete squelch_det; squelch_det = 0; // FIXME: Cleanup return false; } squelch_det->squelchOpen.connect(slot(*this, &LocalRx::onSquelchOpen)); splitter->addSink(squelch_det, true); // Create the configured type of DTMF decoder and add it to the splitter DtmfDecoder *dtmf_dec = DtmfDecoder::create(cfg, name()); if ((dtmf_dec == 0) || !dtmf_dec->initialize()) { // FIXME: Cleanup? return false; } dtmf_dec->digitActivated.connect(slot(*this, &LocalRx::dtmfDigitActivated)); dtmf_dec->digitDeactivated.connect( slot(*this, &LocalRx::dtmfDigitDeactivated)); splitter->addSink(dtmf_dec, true); // creates a selective multiple tone detector object string sel5_det_str; if (cfg.getValue(name(), "SEL5_DEC_TYPE", sel5_det_str)) { Sel5Decoder *sel5_dec = Sel5Decoder::create(cfg, name()); if (sel5_dec == 0 || !sel5_dec->initialize()) { cerr << "*** ERROR: Sel5 decoder initialization failed for RX \"" << name() << "\"\n"; return false; } sel5_dec->sequenceDetected.connect(slot(*this, &LocalRx::sel5Detected)); splitter->addSink(sel5_dec, true); } // Create a new audio splitter to handle tone detectors then add it to // the splitter tone_dets = new AudioSplitter; splitter->addSink(tone_dets, true); // Create an audio valve to use as squelch and connect it to the splitter sql_valve = new AudioValve; sql_valve->setOpen(false); splitter->addSink(sql_valve, true); prev_src = sql_valve; // Create the state detector AudioStreamStateDetector *state_det = new AudioStreamStateDetector; state_det->sigStreamStateChanged.connect( slot(*this, &LocalRx::audioStreamStateChange)); prev_src->registerSink(state_det, true); prev_src = state_det; // Create the highpass CTCSS filter that cuts off audio below 300Hz AudioFilter *ctcss_filt = new AudioFilter("HpBu20/300"); prev_src->registerSink(ctcss_filt, true); prev_src = ctcss_filt; // If we need a delay line (e.g. for DTMF muting and/or squelch tail // elimination), create it if (delay_line_len > 0) { delay = new AudioDelayLine(delay_line_len); prev_src->registerSink(delay, true); prev_src = delay; } // Add a limiter to smoothly limiting the audio before hard clipping it AudioCompressor *limit = new AudioCompressor; limit->setThreshold(-1); limit->setRatio(0.1); limit->setAttack(2); limit->setDecay(20); limit->setOutputGain(1); prev_src->registerSink(limit, true); prev_src = limit; // Clip audio to limit its amplitude AudioClipper *clipper = new AudioClipper; clipper->setClipLevel(0.98); prev_src->registerSink(clipper, true); prev_src = clipper; // Remove high frequencies generated by the previous clipping #if (INTERNAL_SAMPLE_RATE == 16000) AudioFilter *splatter_filter = new AudioFilter("LpBu20/5500"); #else AudioFilter *splatter_filter = new AudioFilter("LpBu20/3500"); #endif prev_src->registerSink(splatter_filter, true); prev_src = splatter_filter; // Set the previous audio pipe object to handle audio distribution for // the LocalRx class setHandler(prev_src); return true; } /* LocalRx:initialize */