kopia lustrzana https://github.com/jamescoxon/dl-fldigi
Upstream version 2.10K
rodzic
ac64df5690
commit
28718230c5
|
@ -16,6 +16,8 @@ Change Log:
|
|||
12) Fixes to mfsk picture mode decoding
|
||||
13) Changed frequency2 data file format, downward compatible
|
||||
14) Added FELD_SLOWHELL, FELD_HELLX5 and FELD_HELLX9 modems
|
||||
15 Added support to the PortAudio backend for separate capture and playback
|
||||
devices and sample rates
|
||||
|
||||
2.09 1) Modified src/Makefile.am for FreeBSD name space clash
|
||||
2) Added psk multi-channel viewer with regex search capability
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
AC_COPYRIGHT([Copyright (C) 2007, 2008 Stelios Bounanos, M0GLD (m0gld AT enotty DOT net)])
|
||||
|
||||
AC_PREREQ(2.61)
|
||||
AC_INIT([fldigi], [2.10J], [w1hkj AT w1hkj DOT com])
|
||||
AC_INIT([fldigi], [2.10K], [w1hkj AT w1hkj DOT com])
|
||||
AC_CONFIG_AUX_DIR([build-aux])
|
||||
|
||||
# define build, build_cpu, build_vendor, build_os
|
||||
|
@ -121,7 +121,7 @@ AC_FLDIGI_PKG_CHECK([sndfile], [sndfile >= 1.0.10], [with], [SNDFILE],
|
|||
# Set ac_cv_portaudio to yes/no
|
||||
# Define USE_PORTAUDIO in config.h
|
||||
# Substitute PORTAUDIO_CFLAGS and PORTAUDIO_LIBS in Makefile
|
||||
AC_FLDIGI_PKG_CHECK([portaudio], [portaudio-2.0 >= 19], [without], [PORTAUDIO],
|
||||
AC_FLDIGI_PKG_CHECK([portaudio], [portaudio-2.0 >= 19], [with], [PORTAUDIO],
|
||||
[enable support for PortAudio @<:@autodetect@:>@] )
|
||||
|
||||
### pulseaudio
|
||||
|
@ -134,7 +134,7 @@ AC_FLDIGI_PKG_CHECK([pulseaudio], [libpulse-simple >= 0.9.7], [with], [PULSEAUDI
|
|||
if test "x$ac_cv_oss" = "xno" && \
|
||||
test "x$ac_cv_portaudio" = "xno" && \
|
||||
test "x$ac_cv_pulseaudio" = "xno"; then
|
||||
AC_MSG_FAILURE([$PACKAGE requires OSS, PortAudio, or PulseAudio])
|
||||
AC_MSG_WARN([*** $PACKAGE will be compiled without audio device support ***])
|
||||
fi
|
||||
|
||||
### hamlib
|
||||
|
|
|
@ -556,6 +556,74 @@ Fl_Tabs *tabsSoundCard=(Fl_Tabs *)0;
|
|||
|
||||
Fl_Group *tabAudio=(Fl_Group *)0;
|
||||
|
||||
Fl_Group *AudioOSS=(Fl_Group *)0;
|
||||
|
||||
static void cb_btnAudioIO(Fl_Round_Button*, void*) {
|
||||
update_sound_config(SND_IDX_OSS);
|
||||
progdefaults.changed = true;
|
||||
resetSoundCard();
|
||||
}
|
||||
|
||||
Fl_Input_Choice *menuOSSDev=(Fl_Input_Choice *)0;
|
||||
|
||||
static void cb_menuOSSDev(Fl_Input_Choice* o, void*) {
|
||||
scDevice[0] = scDevice[1] = progdefaults.OSSdevice = o->value();
|
||||
resetSoundCard();
|
||||
progdefaults.changed = true;
|
||||
}
|
||||
|
||||
Fl_Group *AudioPort=(Fl_Group *)0;
|
||||
|
||||
static void cb_btnAudioIO1(Fl_Round_Button*, void*) {
|
||||
update_sound_config(SND_IDX_PORT);
|
||||
progdefaults.changed = true;
|
||||
resetSoundCard();
|
||||
}
|
||||
|
||||
Fl_Input_Choice *menuPortInDev=(Fl_Input_Choice *)0;
|
||||
|
||||
static void cb_menuPortInDev(Fl_Input_Choice* o, void*) {
|
||||
scDevice[0] = progdefaults.PortInDevice = o->value();
|
||||
resetSoundCard();
|
||||
progdefaults.changed = true;
|
||||
}
|
||||
|
||||
Fl_Input_Choice *menuPortOutDev=(Fl_Input_Choice *)0;
|
||||
|
||||
static void cb_menuPortOutDev(Fl_Input_Choice* o, void*) {
|
||||
scDevice[1] = progdefaults.PortOutDevice = o->value();
|
||||
resetSoundCard();
|
||||
progdefaults.changed = true;
|
||||
}
|
||||
|
||||
Fl_Group *AudioPulse=(Fl_Group *)0;
|
||||
|
||||
static void cb_btnAudioIO2(Fl_Round_Button*, void*) {
|
||||
update_sound_config(SND_IDX_PULSE);
|
||||
progdefaults.changed = true;
|
||||
resetSoundCard();
|
||||
}
|
||||
|
||||
Fl_Input *inpPulseServer=(Fl_Input *)0;
|
||||
|
||||
static void cb_inpPulseServer(Fl_Input* o, void*) {
|
||||
scDevice[0] = scDevice[1] = progdefaults.PulseServer = o->value();
|
||||
resetSoundCard();
|
||||
progdefaults.changed = true;
|
||||
}
|
||||
|
||||
Fl_Group *AudioNull=(Fl_Group *)0;
|
||||
|
||||
Fl_Round_Button *btnAudioIO[4]={(Fl_Round_Button *)0};
|
||||
|
||||
static void cb_btnAudioIO3(Fl_Round_Button*, void*) {
|
||||
update_sound_config(SND_IDX_NULL);
|
||||
progdefaults.changed = true;
|
||||
resetSoundCard();
|
||||
}
|
||||
|
||||
Fl_Group *tabAudioOpt=(Fl_Group *)0;
|
||||
|
||||
Fl_Spinner *cntRxRateCorr=(Fl_Spinner *)0;
|
||||
|
||||
static void cb_cntRxRateCorr(Fl_Spinner* o, void*) {
|
||||
|
@ -577,69 +645,18 @@ static void cb_cntTxOffset(Fl_Spinner* o, void*) {
|
|||
progdefaults.changed = true;
|
||||
}
|
||||
|
||||
Fl_Group *AudioIO=(Fl_Group *)0;
|
||||
Fl_Choice *menuOutSampleRate=(Fl_Choice *)0;
|
||||
|
||||
static void cb_btnAudioIO(Fl_Round_Button* o, void*) {
|
||||
btnAudioIO[1]->value(0);
|
||||
btnAudioIO[2]->value(0);
|
||||
o->value(1);
|
||||
menuOSSDev->activate();
|
||||
menuPADev->deactivate();
|
||||
menuSampleRate->deactivate();
|
||||
scDevice = menuOSSDev->value();
|
||||
progdefaults.btnAudioIOis = 0;
|
||||
progdefaults.changed = true;
|
||||
resetSoundCard();
|
||||
}
|
||||
|
||||
static void cb_btnAudioIO1(Fl_Round_Button* o, void*) {
|
||||
btnAudioIO[0]->value(0);
|
||||
btnAudioIO[2]->value(0);
|
||||
o->value(1);
|
||||
menuPADev->activate();
|
||||
menuOSSDev->deactivate();
|
||||
menuSampleRate->activate();
|
||||
scDevice = menuPADev->value();
|
||||
progdefaults.btnAudioIOis = 1;
|
||||
progdefaults.changed = true;
|
||||
resetSoundCard();
|
||||
}
|
||||
|
||||
Fl_Round_Button *btnAudioIO[3]={(Fl_Round_Button *)0};
|
||||
|
||||
static void cb_btnAudioIO2(Fl_Round_Button* o, void*) {
|
||||
btnAudioIO[0]->value(0);
|
||||
btnAudioIO[1]->value(0);
|
||||
o->value(1);
|
||||
menuPADev->deactivate();
|
||||
menuOSSDev->deactivate();
|
||||
menuSampleRate->deactivate();
|
||||
scDevice = "localhost";
|
||||
progdefaults.btnAudioIOis = 2;
|
||||
progdefaults.changed = true;
|
||||
resetSoundCard();
|
||||
}
|
||||
|
||||
Fl_Input_Choice *menuOSSDev=(Fl_Input_Choice *)0;
|
||||
|
||||
static void cb_menuOSSDev(Fl_Input_Choice* o, void*) {
|
||||
scDevice = progdefaults.OSSdevice = o->value();
|
||||
static void cb_menuOutSampleRate(Fl_Choice* o, void*) {
|
||||
progdefaults.out_sample_rate = o->value() > 1 ? strtol(o->mvalue()->text, 0, 10) : o->value();
|
||||
resetSoundCard();
|
||||
progdefaults.changed = true;
|
||||
}
|
||||
|
||||
Fl_Input_Choice *menuPADev=(Fl_Input_Choice *)0;
|
||||
Fl_Choice *menuInSampleRate=(Fl_Choice *)0;
|
||||
|
||||
static void cb_menuPADev(Fl_Input_Choice* o, void*) {
|
||||
scDevice = progdefaults.PAdevice = o->value();
|
||||
resetSoundCard();
|
||||
progdefaults.changed = true;
|
||||
}
|
||||
|
||||
Fl_Choice *menuSampleRate=(Fl_Choice *)0;
|
||||
|
||||
static void cb_menuSampleRate(Fl_Choice* o, void*) {
|
||||
progdefaults.sample_rate = o->value() > 1 ? strtol(o->mvalue()->text, 0, 10) : o->value();
|
||||
static void cb_menuInSampleRate(Fl_Choice* o, void*) {
|
||||
progdefaults.in_sample_rate = o->value() > 1 ? strtol(o->mvalue()->text, 0, 10) : o->value();
|
||||
resetSoundCard();
|
||||
progdefaults.changed = true;
|
||||
}
|
||||
|
@ -887,8 +904,7 @@ progdefaults.changed = true;
|
|||
Fl_Value_Slider *sldrHellBW=(Fl_Value_Slider *)0;
|
||||
|
||||
static void cb_sldrHellBW(Fl_Value_Slider*, void*) {
|
||||
if (active_modem->get_mode() == MODE_FELDHELL)
|
||||
active_modem->set_bandwidth(sldrHellBW->value());
|
||||
active_modem->set_bandwidth(sldrHellBW->value());
|
||||
progdefaults.changed = true;
|
||||
}
|
||||
|
||||
|
@ -1151,7 +1167,7 @@ static const char szStopBits[] = "1|1.5|2";
|
|||
static const char szOliviaTones[] = "2|4|8|16|32|64|128|256";
|
||||
static const char szOliviaBandwidth[] = "125|250|500|1000|2000";
|
||||
static const char szBaudRates[] = "300|600|1200|2400|4800|9600|19200|38400|57600|115200|230400|460800";
|
||||
{ Fl_Double_Window* o = new Fl_Double_Window(400, 255, "fldigi - config");
|
||||
{ Fl_Double_Window* o = new Fl_Double_Window(400, 254, "fldigi - config");
|
||||
w = o;
|
||||
o->color(FL_DARK2);
|
||||
o->selection_color((Fl_Color)51);
|
||||
|
@ -1539,6 +1555,7 @@ static const char szBaudRates[] = "300|600|1200|2400|4800|9600|19200|38400|57600
|
|||
{ Fl_Group* o = tabQRZ = new Fl_Group(0, 25, 400, 195, "qrz");
|
||||
o->color((Fl_Color)51);
|
||||
o->selection_color((Fl_Color)51);
|
||||
o->hide();
|
||||
{ Fl_Check_Button* o = btnQRZnotavailable = new Fl_Check_Button(31, 45, 200, 20, "Not available");
|
||||
o->down_box(FL_DOWN_BOX);
|
||||
o->value(1);
|
||||
|
@ -1581,63 +1598,103 @@ static const char szBaudRates[] = "300|600|1200|2400|4800|9600|19200|38400|57600
|
|||
o->hide();
|
||||
{ Fl_Tabs* o = tabsSoundCard = new Fl_Tabs(0, 25, 400, 195);
|
||||
o->selection_color((Fl_Color)10);
|
||||
{ Fl_Group* o = tabAudio = new Fl_Group(0, 50, 400, 170, "Audio");
|
||||
{ Fl_Group* o = tabAudio = new Fl_Group(0, 50, 400, 170, "Audio devices");
|
||||
o->color((Fl_Color)51);
|
||||
o->selection_color((Fl_Color)51);
|
||||
{ Fl_Spinner* o = cntRxRateCorr = new Fl_Spinner(300, 160, 75, 24, "RX ppm:");
|
||||
{ Fl_Group* o = AudioOSS = new Fl_Group(5, 58, 391, 35);
|
||||
o->box(FL_ENGRAVED_FRAME);
|
||||
{ Fl_Round_Button* o = btnAudioIO[0] = new Fl_Round_Button(5, 63, 100, 25, "OSS");
|
||||
o->down_box(FL_DIAMOND_DOWN_BOX);
|
||||
o->selection_color((Fl_Color)1);
|
||||
o->callback((Fl_Callback*)cb_btnAudioIO);
|
||||
}
|
||||
{ Fl_Input_Choice* o = menuOSSDev = new Fl_Input_Choice(280, 63, 110, 25, "Device");
|
||||
o->callback((Fl_Callback*)cb_menuOSSDev);
|
||||
o->value(progdefaults.OSSdevice.c_str());
|
||||
}
|
||||
o->end();
|
||||
}
|
||||
{ Fl_Group* o = AudioPort = new Fl_Group(5, 95, 390, 61);
|
||||
o->box(FL_ENGRAVED_FRAME);
|
||||
{ Fl_Round_Button* o = btnAudioIO[1] = new Fl_Round_Button(5, 115, 95, 25, "PortAudio");
|
||||
o->down_box(FL_DIAMOND_DOWN_BOX);
|
||||
o->selection_color((Fl_Color)1);
|
||||
o->callback((Fl_Callback*)cb_btnAudioIO1);
|
||||
}
|
||||
{ Fl_Input_Choice* o = menuPortInDev = new Fl_Input_Choice(165, 99, 225, 25, "Capture");
|
||||
o->callback((Fl_Callback*)cb_menuPortInDev);
|
||||
o->value(progdefaults.PortInDevice.c_str());
|
||||
}
|
||||
{ Fl_Input_Choice* o = menuPortOutDev = new Fl_Input_Choice(165, 127, 225, 25, "Playback");
|
||||
o->callback((Fl_Callback*)cb_menuPortOutDev);
|
||||
o->value(progdefaults.PortOutDevice.c_str());
|
||||
}
|
||||
o->end();
|
||||
}
|
||||
{ Fl_Group* o = AudioPulse = new Fl_Group(5, 158, 390, 32);
|
||||
o->box(FL_ENGRAVED_FRAME);
|
||||
{ Fl_Round_Button* o = btnAudioIO[2] = new Fl_Round_Button(5, 159, 100, 30, "PulseAudio");
|
||||
o->down_box(FL_DIAMOND_DOWN_BOX);
|
||||
o->selection_color((Fl_Color)1);
|
||||
o->callback((Fl_Callback*)cb_btnAudioIO2);
|
||||
}
|
||||
{ Fl_Input* o = inpPulseServer = new Fl_Input(165, 161, 225, 25, "Server");
|
||||
o->tooltip("Leave this blank or refer to\nhttp://www.pulseaudio.org/wiki/ServerStrings");
|
||||
o->callback((Fl_Callback*)cb_inpPulseServer);
|
||||
o->value(progdefaults.PulseServer.c_str());
|
||||
}
|
||||
o->end();
|
||||
}
|
||||
{ Fl_Group* o = AudioNull = new Fl_Group(5, 192, 390, 25);
|
||||
o->box(FL_ENGRAVED_FRAME);
|
||||
{ Fl_Round_Button* o = btnAudioIO[3] = new Fl_Round_Button(5, 192, 100, 25, "File I/O only");
|
||||
o->down_box(FL_DIAMOND_DOWN_BOX);
|
||||
o->selection_color((Fl_Color)1);
|
||||
o->callback((Fl_Callback*)cb_btnAudioIO3);
|
||||
}
|
||||
o->end();
|
||||
}
|
||||
o->end();
|
||||
}
|
||||
{ Fl_Group* o = tabAudioOpt = new Fl_Group(0, 50, 400, 170, "Audio settings");
|
||||
o->color((Fl_Color)51);
|
||||
o->selection_color((Fl_Color)51);
|
||||
o->hide();
|
||||
{ Fl_Spinner* o = cntRxRateCorr = new Fl_Spinner(5, 160, 85, 25, "RX ppm");
|
||||
o->callback((Fl_Callback*)cb_cntRxRateCorr);
|
||||
o->align(FL_ALIGN_RIGHT);
|
||||
o->step(1);
|
||||
o->minimum(-50000);
|
||||
o->maximum(50000);
|
||||
}
|
||||
{ Fl_Spinner* o = cntTxRateCorr = new Fl_Spinner(300, 130, 75, 24, "TX ppm:");
|
||||
{ Fl_Spinner* o = cntTxRateCorr = new Fl_Spinner(5, 130, 85, 25, "TX ppm");
|
||||
o->callback((Fl_Callback*)cb_cntTxRateCorr);
|
||||
o->align(FL_ALIGN_RIGHT);
|
||||
o->step(1);
|
||||
o->minimum(-50000);
|
||||
o->maximum(50000);
|
||||
}
|
||||
{ Fl_Spinner* o = cntTxOffset = new Fl_Spinner(330, 190, 45, 24, "Tx offset:");
|
||||
{ Fl_Spinner* o = cntTxOffset = new Fl_Spinner(5, 190, 85, 25, "TX offset");
|
||||
o->callback((Fl_Callback*)cb_cntTxOffset);
|
||||
o->align(FL_ALIGN_RIGHT);
|
||||
o->value(progdefaults.TxOffset);
|
||||
o->step(1);
|
||||
o->minimum(-50);
|
||||
o->maximum(50);
|
||||
}
|
||||
{ Fl_Group* o = AudioIO = new Fl_Group(0, 55, 140, 70, "I/O");
|
||||
o->box(FL_ENGRAVED_FRAME);
|
||||
o->align(FL_ALIGN_TOP_LEFT|FL_ALIGN_INSIDE);
|
||||
{ Fl_Round_Button* o = btnAudioIO[0] = new Fl_Round_Button(35, 60, 100, 25, "OSS");
|
||||
o->down_box(FL_DIAMOND_DOWN_BOX);
|
||||
o->selection_color((Fl_Color)1);
|
||||
o->callback((Fl_Callback*)cb_btnAudioIO);
|
||||
}
|
||||
{ Fl_Round_Button* o = btnAudioIO[1] = new Fl_Round_Button(35, 80, 100, 25, "PortAudio");
|
||||
o->down_box(FL_DIAMOND_DOWN_BOX);
|
||||
o->selection_color((Fl_Color)1);
|
||||
o->callback((Fl_Callback*)cb_btnAudioIO1);
|
||||
}
|
||||
{ Fl_Round_Button* o = btnAudioIO[2] = new Fl_Round_Button(35, 100, 100, 25, "PulseAudio");
|
||||
o->down_box(FL_DIAMOND_DOWN_BOX);
|
||||
o->selection_color((Fl_Color)1);
|
||||
o->callback((Fl_Callback*)cb_btnAudioIO2);
|
||||
}
|
||||
o->end();
|
||||
}
|
||||
{ Fl_Input_Choice* o = menuOSSDev = new Fl_Input_Choice(155, 60, 110, 25, "OSS device");
|
||||
o->callback((Fl_Callback*)cb_menuOSSDev);
|
||||
o->align(FL_ALIGN_RIGHT);
|
||||
o->value(progdefaults.OSSdevice.c_str());
|
||||
}
|
||||
{ Fl_Input_Choice* o = menuPADev = new Fl_Input_Choice(155, 90, 110, 25, "PortAudio device");
|
||||
o->callback((Fl_Callback*)cb_menuPADev);
|
||||
o->align(FL_ALIGN_RIGHT);
|
||||
o->value(progdefaults.PAdevice.c_str());
|
||||
}
|
||||
{ Fl_Choice* o = menuSampleRate = new Fl_Choice(5, 190, 85, 25, "Sample rate");
|
||||
{ Fl_Choice* o = menuOutSampleRate = new Fl_Choice(5, 90, 85, 25, "Playback sample rate");
|
||||
o->tooltip("Force a specific sample rate. Select \"Native\" if \"Auto\" does not work wel\
|
||||
l with your sound hardware.");
|
||||
o->down_box(FL_BORDER_BOX);
|
||||
o->callback((Fl_Callback*)cb_menuSampleRate);
|
||||
o->callback((Fl_Callback*)cb_menuOutSampleRate);
|
||||
o->align(FL_ALIGN_RIGHT);
|
||||
o->menu(sample_rate_menu);
|
||||
}
|
||||
{ Fl_Choice* o = menuInSampleRate = new Fl_Choice(5, 60, 85, 25, "Capture sample rate");
|
||||
o->tooltip("Force a specific sample rate. Select \"Native\" if \"Auto\" does not work wel\
|
||||
l with your sound hardware.");
|
||||
o->down_box(FL_BORDER_BOX);
|
||||
o->callback((Fl_Callback*)cb_menuInSampleRate);
|
||||
o->align(FL_ALIGN_RIGHT);
|
||||
o->menu(sample_rate_menu);
|
||||
}
|
||||
|
@ -1731,7 +1788,6 @@ fect after a restart.");
|
|||
{ Fl_Group* o = tabModems = new Fl_Group(0, 25, 401, 195, "Modem");
|
||||
o->color((Fl_Color)51);
|
||||
o->selection_color((Fl_Color)51);
|
||||
o->hide();
|
||||
{ Fl_Tabs* o = tabsModems = new Fl_Tabs(0, 25, 401, 195);
|
||||
o->color((Fl_Color)51);
|
||||
o->selection_color((Fl_Color)10);
|
||||
|
@ -1911,7 +1967,6 @@ fect after a restart.");
|
|||
{ Fl_Group* o = tabFeld = new Fl_Group(0, 50, 400, 170, "Feld");
|
||||
o->color((Fl_Color)51);
|
||||
o->selection_color((Fl_Color)51);
|
||||
o->hide();
|
||||
{ Fl_Choice* o = selHellFont = new Fl_Choice(175, 62, 122, 20, "Feld Hell Font:");
|
||||
o->down_box(FL_BORDER_BOX);
|
||||
o->labelfont(4);
|
||||
|
@ -1923,10 +1978,10 @@ fect after a restart.");
|
|||
{ Fl_Value_Slider* o = sldrHellBW = new Fl_Value_Slider(30, 190, 345, 20, "Filter BW");
|
||||
o->type(1);
|
||||
o->color((Fl_Color)215);
|
||||
o->minimum(50);
|
||||
o->maximum(600);
|
||||
o->minimum(10);
|
||||
o->maximum(2400);
|
||||
o->step(5);
|
||||
o->value(245);
|
||||
o->value(400);
|
||||
o->textsize(14);
|
||||
o->callback((Fl_Callback*)cb_sldrHellBW);
|
||||
o->align(FL_ALIGN_TOP_LEFT);
|
||||
|
@ -2042,6 +2097,7 @@ fect after a restart.");
|
|||
o->end();
|
||||
}
|
||||
{ Fl_Group* o = new Fl_Group(0, 50, 400, 170, "PskViewer");
|
||||
o->hide();
|
||||
{ Fl_Group* o = new Fl_Group(5, 60, 390, 155);
|
||||
o->box(FL_ENGRAVED_FRAME);
|
||||
o->align(FL_ALIGN_TOP_LEFT|FL_ALIGN_INSIDE);
|
||||
|
|
|
@ -35,14 +35,14 @@ static const char szBaudRates[] = "300|600|1200|2400|4800|9600|19200|38400|57600
|
|||
code {} {}
|
||||
Fl_Window {} {
|
||||
label {fldigi - config} open
|
||||
xywh {663 306 400 255} type Double color 45 selection_color 51 align 80 visible
|
||||
xywh {868 365 400 254} type Double color 45 selection_color 51 align 80 visible
|
||||
} {
|
||||
Fl_Tabs tabsConfigure {open
|
||||
xywh {0 0 405 220} color 47 selection_color 9
|
||||
} {
|
||||
Fl_Group tabOperator {
|
||||
label Oper
|
||||
callback {progdefaults.changed = true;} open
|
||||
callback {progdefaults.changed = true;}
|
||||
xywh {0 25 400 195} color 51 selection_color 51 when 1 hide
|
||||
} {
|
||||
Fl_Input inpMyCallsign {
|
||||
|
@ -89,7 +89,7 @@ progdefaults.changed = true;}
|
|||
}
|
||||
}
|
||||
Fl_Group tabWaterfall {
|
||||
label {W-fall} open
|
||||
label {W-fall}
|
||||
xywh {0 25 405 195} color 51 selection_color 51 hide
|
||||
} {
|
||||
Fl_Tabs {} {open
|
||||
|
@ -306,7 +306,7 @@ progdefaults.changed = true;
|
|||
}
|
||||
}
|
||||
Fl_Group tabVideo {
|
||||
label Video open
|
||||
label Video
|
||||
xywh {0 25 400 195} color 51 selection_color 51 hide
|
||||
} {
|
||||
Fl_Group {} {
|
||||
|
@ -369,7 +369,7 @@ progdefaults.changed = true;}
|
|||
}
|
||||
}
|
||||
Fl_Group tabRig {
|
||||
label Rig open
|
||||
label Rig
|
||||
xywh {0 25 400 195} hide
|
||||
} {
|
||||
Fl_Group {} {
|
||||
|
@ -578,7 +578,7 @@ progdefaults.changed = true;}
|
|||
}
|
||||
Fl_Group tabQRZ {
|
||||
label qrz open
|
||||
xywh {0 25 400 195} color 51 selection_color 51
|
||||
xywh {0 25 400 195} color 51 selection_color 51 hide
|
||||
} {
|
||||
Fl_Check_Button btnQRZnotavailable {
|
||||
label {Not available}
|
||||
|
@ -600,7 +600,7 @@ btnQRZnotavailable->value(0);
|
|||
btnHAMCALLsocket->value(0);
|
||||
progdefaults.QRZ = 1;
|
||||
}
|
||||
progdefaults.changed = true;} selected
|
||||
progdefaults.changed = true;}
|
||||
tooltip {You need a QRZ on-line subscription to access QRZ.com} xywh {32 74 205 20} down_box DOWN_BOX
|
||||
code0 {if (progdefaults.QRZ == 1) o->value(1); else o->value(0);}
|
||||
}
|
||||
|
@ -658,112 +658,140 @@ o->label((inpQRZuserpassword->type() & FL_SECRET_INPUT) ? "Show" : "Hide");}
|
|||
xywh {0 25 400 195} selection_color 10
|
||||
} {
|
||||
Fl_Group tabAudio {
|
||||
label Audio open
|
||||
label {Audio devices} open
|
||||
xywh {0 50 400 170} color 51 selection_color 51
|
||||
} {
|
||||
Fl_Group AudioOSS {open
|
||||
xywh {5 58 391 35} box ENGRAVED_FRAME
|
||||
} {
|
||||
Fl_Round_Button {btnAudioIO[0]} {
|
||||
label OSS
|
||||
callback {update_sound_config(SND_IDX_OSS);
|
||||
progdefaults.changed = true;
|
||||
resetSoundCard();} selected
|
||||
xywh {5 63 100 25} down_box DIAMOND_DOWN_BOX selection_color 1
|
||||
}
|
||||
Fl_Input_Choice menuOSSDev {
|
||||
label Device
|
||||
callback {scDevice[0] = scDevice[1] = progdefaults.OSSdevice = o->value();
|
||||
resetSoundCard();
|
||||
progdefaults.changed = true;} open
|
||||
xywh {280 63 110 25}
|
||||
code0 {o->value(progdefaults.OSSdevice.c_str());}
|
||||
} {}
|
||||
}
|
||||
Fl_Group AudioPort {open
|
||||
xywh {5 95 390 61} box ENGRAVED_FRAME
|
||||
} {
|
||||
Fl_Round_Button {btnAudioIO[1]} {
|
||||
label PortAudio
|
||||
callback {update_sound_config(SND_IDX_PORT);
|
||||
progdefaults.changed = true;
|
||||
resetSoundCard();}
|
||||
xywh {5 115 95 25} down_box DIAMOND_DOWN_BOX selection_color 1
|
||||
}
|
||||
Fl_Input_Choice menuPortInDev {
|
||||
label Capture
|
||||
callback {scDevice[0] = progdefaults.PortInDevice = o->value();
|
||||
resetSoundCard();
|
||||
progdefaults.changed = true;} open
|
||||
xywh {165 99 225 25}
|
||||
code0 {o->value(progdefaults.PortInDevice.c_str());}
|
||||
} {}
|
||||
Fl_Input_Choice menuPortOutDev {
|
||||
label Playback
|
||||
callback {scDevice[1] = progdefaults.PortOutDevice = o->value();
|
||||
resetSoundCard();
|
||||
progdefaults.changed = true;} open
|
||||
xywh {165 127 225 25}
|
||||
code0 {o->value(progdefaults.PortOutDevice.c_str());}
|
||||
} {}
|
||||
}
|
||||
Fl_Group AudioPulse {open
|
||||
xywh {5 158 390 32} box ENGRAVED_FRAME
|
||||
} {
|
||||
Fl_Round_Button {btnAudioIO[2]} {
|
||||
label PulseAudio
|
||||
callback {update_sound_config(SND_IDX_PULSE);
|
||||
progdefaults.changed = true;
|
||||
resetSoundCard();}
|
||||
xywh {5 159 100 30} down_box DIAMOND_DOWN_BOX selection_color 1
|
||||
}
|
||||
Fl_Input inpPulseServer {
|
||||
label Server
|
||||
callback {scDevice[0] = scDevice[1] = progdefaults.PulseServer = o->value();
|
||||
resetSoundCard();
|
||||
progdefaults.changed = true;}
|
||||
tooltip {Leave this blank or refer to
|
||||
http://www.pulseaudio.org/wiki/ServerStrings} xywh {165 161 225 25}
|
||||
code0 {o->value(progdefaults.PulseServer.c_str());}
|
||||
}
|
||||
}
|
||||
Fl_Group AudioNull {open
|
||||
xywh {5 192 390 25} box ENGRAVED_FRAME
|
||||
} {
|
||||
Fl_Round_Button {btnAudioIO[3]} {
|
||||
label {File I/O only}
|
||||
callback {update_sound_config(SND_IDX_NULL);
|
||||
progdefaults.changed = true;
|
||||
resetSoundCard();}
|
||||
xywh {5 192 100 25} down_box DIAMOND_DOWN_BOX selection_color 1
|
||||
}
|
||||
}
|
||||
}
|
||||
Fl_Group tabAudioOpt {
|
||||
label {Audio settings} open
|
||||
xywh {0 50 400 170} color 51 selection_color 51 hide
|
||||
} {
|
||||
Fl_Spinner cntRxRateCorr {
|
||||
label {RX ppm:}
|
||||
label {RX ppm}
|
||||
callback {progdefaults.RX_corr = (int)o->value();
|
||||
progdefaults.changed = true;}
|
||||
xywh {300 160 75 24}
|
||||
xywh {5 160 85 25} align 8
|
||||
code0 {o->step(1);}
|
||||
code1 {o->minimum(-50000);}
|
||||
code2 {o->maximum(50000);}
|
||||
}
|
||||
Fl_Spinner cntTxRateCorr {
|
||||
label {TX ppm:}
|
||||
label {TX ppm}
|
||||
callback {progdefaults.TX_corr = (int)o->value();
|
||||
progdefaults.changed = true;}
|
||||
xywh {300 130 75 24}
|
||||
xywh {5 130 85 25} align 8
|
||||
code0 {o->step(1);}
|
||||
code1 {o->minimum(-50000);}
|
||||
code2 {o->maximum(50000);}
|
||||
}
|
||||
Fl_Spinner cntTxOffset {
|
||||
label {Tx offset:}
|
||||
label {TX offset}
|
||||
callback {progdefaults.TxOffset = (int)o->value();
|
||||
progdefaults.changed = true;}
|
||||
xywh {330 190 45 24}
|
||||
xywh {5 190 85 25} align 8
|
||||
code0 {o->value(progdefaults.TxOffset);}
|
||||
code1 {o->step(1);}
|
||||
code2 {o->minimum(-50);}
|
||||
code3 {o->maximum(50);}
|
||||
}
|
||||
Fl_Group AudioIO {
|
||||
label {I/O} open
|
||||
xywh {0 55 140 70} box ENGRAVED_FRAME align 21
|
||||
} {
|
||||
Fl_Round_Button {btnAudioIO[0]} {
|
||||
label OSS
|
||||
callback {btnAudioIO[1]->value(0);
|
||||
btnAudioIO[2]->value(0);
|
||||
o->value(1);
|
||||
menuOSSDev->activate();
|
||||
menuPADev->deactivate();
|
||||
menuSampleRate->deactivate();
|
||||
scDevice = menuOSSDev->value();
|
||||
progdefaults.btnAudioIOis = 0;
|
||||
progdefaults.changed = true;
|
||||
resetSoundCard();}
|
||||
xywh {35 60 100 25} down_box DIAMOND_DOWN_BOX selection_color 1
|
||||
}
|
||||
Fl_Round_Button {btnAudioIO[1]} {
|
||||
label PortAudio
|
||||
callback {btnAudioIO[0]->value(0);
|
||||
btnAudioIO[2]->value(0);
|
||||
o->value(1);
|
||||
menuPADev->activate();
|
||||
menuOSSDev->deactivate();
|
||||
menuSampleRate->activate();
|
||||
scDevice = menuPADev->value();
|
||||
progdefaults.btnAudioIOis = 1;
|
||||
progdefaults.changed = true;
|
||||
resetSoundCard();}
|
||||
xywh {35 80 100 25} down_box DIAMOND_DOWN_BOX selection_color 1
|
||||
}
|
||||
Fl_Round_Button {btnAudioIO[2]} {
|
||||
label PulseAudio
|
||||
callback {btnAudioIO[0]->value(0);
|
||||
btnAudioIO[1]->value(0);
|
||||
o->value(1);
|
||||
menuPADev->deactivate();
|
||||
menuOSSDev->deactivate();
|
||||
menuSampleRate->deactivate();
|
||||
scDevice = "localhost";
|
||||
progdefaults.btnAudioIOis = 2;
|
||||
progdefaults.changed = true;
|
||||
resetSoundCard();}
|
||||
xywh {35 100 100 25} down_box DIAMOND_DOWN_BOX selection_color 1
|
||||
}
|
||||
}
|
||||
Fl_Input_Choice menuOSSDev {
|
||||
label {OSS device}
|
||||
callback {scDevice = progdefaults.OSSdevice = o->value();
|
||||
Fl_Choice menuOutSampleRate {
|
||||
label {Playback sample rate}
|
||||
callback {progdefaults.out_sample_rate = o->value() > 1 ? strtol(o->mvalue()->text, 0, 10) : o->value();
|
||||
resetSoundCard();
|
||||
progdefaults.changed = true;} open
|
||||
xywh {155 60 110 25} align 8
|
||||
code0 {o->value(progdefaults.OSSdevice.c_str());}
|
||||
tooltip {Force a specific sample rate. Select "Native" if "Auto" does not work well with your sound hardware.} xywh {5 90 85 25} down_box BORDER_BOX align 8
|
||||
code0 {extern Fl_Menu_Item sample_rate_menu[];}
|
||||
code1 {o->menu(sample_rate_menu);}
|
||||
} {}
|
||||
Fl_Input_Choice menuPADev {
|
||||
label {PortAudio device}
|
||||
callback {scDevice = progdefaults.PAdevice = o->value();
|
||||
Fl_Choice menuInSampleRate {
|
||||
label {Capture sample rate}
|
||||
callback {progdefaults.in_sample_rate = o->value() > 1 ? strtol(o->mvalue()->text, 0, 10) : o->value();
|
||||
resetSoundCard();
|
||||
progdefaults.changed = true;} open
|
||||
xywh {155 90 110 25} align 8
|
||||
code0 {o->value(progdefaults.PAdevice.c_str());}
|
||||
} {}
|
||||
Fl_Choice menuSampleRate {
|
||||
label {Sample rate}
|
||||
callback {progdefaults.sample_rate = o->value() > 1 ? strtol(o->mvalue()->text, 0, 10) : o->value();
|
||||
resetSoundCard();
|
||||
progdefaults.changed = true;} open
|
||||
tooltip {Force a specific sample rate. Select "Native" if "Auto" does not work well with your sound hardware.} xywh {5 190 85 25} down_box BORDER_BOX align 8
|
||||
tooltip {Force a specific sample rate. Select "Native" if "Auto" does not work well with your sound hardware.} xywh {5 60 85 25} down_box BORDER_BOX align 8
|
||||
code0 {extern Fl_Menu_Item sample_rate_menu[];}
|
||||
code1 {o->menu(sample_rate_menu);}
|
||||
} {}
|
||||
}
|
||||
Fl_Group tabMixer {
|
||||
label Mixer open
|
||||
label Mixer
|
||||
xywh {0 50 400 170} color 51 selection_color 51 hide
|
||||
} {
|
||||
Fl_Light_Button btnLineIn {
|
||||
|
@ -869,7 +897,7 @@ This option takes effect after a restart.} xywh {50 155 180 20} down_box DOWN_BO
|
|||
}
|
||||
Fl_Group tabModems {
|
||||
label Modem open
|
||||
xywh {0 25 401 195} color 51 selection_color 51 hide
|
||||
xywh {0 25 401 195} color 51 selection_color 51
|
||||
} {
|
||||
Fl_Tabs tabsModems {open
|
||||
xywh {0 25 401 195} color 51 selection_color 10 align 9
|
||||
|
@ -1024,7 +1052,7 @@ progdefaults.changed = true;}
|
|||
}
|
||||
Fl_Group tabFeld {
|
||||
label Feld open
|
||||
xywh {0 50 400 170} color 51 selection_color 51 hide
|
||||
xywh {0 50 400 170} color 51 selection_color 51
|
||||
} {
|
||||
Fl_Choice selHellFont {
|
||||
label {Feld Hell Font:}
|
||||
|
@ -1037,10 +1065,9 @@ progdefaults.changed = true;} open
|
|||
} {}
|
||||
Fl_Value_Slider sldrHellBW {
|
||||
label {Filter BW}
|
||||
callback {if (active_modem->get_mode() == MODE_FELDHELL)
|
||||
active_modem->set_bandwidth(sldrHellBW->value());
|
||||
progdefaults.changed = true;}
|
||||
xywh {30 190 345 20} type Horizontal color 215 align 5 minimum 50 maximum 600 step 5 value 245 textsize 14
|
||||
callback {active_modem->set_bandwidth(sldrHellBW->value());
|
||||
progdefaults.changed = true;} selected
|
||||
xywh {30 190 345 20} type Horizontal color 215 align 5 minimum 10 maximum 2400 step 5 value 400 textsize 14
|
||||
}
|
||||
Fl_Check_Button btnHellXmtWidth {
|
||||
label {2x Xmt Width}
|
||||
|
@ -1161,7 +1188,7 @@ progdefaults.changed = true;}
|
|||
}
|
||||
Fl_Group {} {
|
||||
label PskViewer open
|
||||
xywh {0 50 400 170}
|
||||
xywh {0 50 400 170} hide
|
||||
} {
|
||||
Fl_Group {} {open
|
||||
xywh {5 60 390 155} box ENGRAVED_FRAME align 21
|
||||
|
|
|
@ -226,6 +226,7 @@ void startup_modem(modem *m)
|
|||
m == feld_80modem ) {
|
||||
ReceiveText->Hide();
|
||||
FHdisp->show();
|
||||
sldrHellBW->value(m->get_bandwidth());
|
||||
} else {
|
||||
ReceiveText->Show();
|
||||
FHdisp->hide();
|
||||
|
@ -1694,13 +1695,13 @@ void put_echo_char(unsigned int data)
|
|||
|
||||
void resetRTTY() {
|
||||
if (active_modem->get_mode() != MODE_RTTY) return;
|
||||
trx_reset(scDevice.c_str());
|
||||
trx_reset();
|
||||
active_modem->restart();
|
||||
}
|
||||
|
||||
void resetOLIVIA() {
|
||||
if (active_modem->get_mode() != MODE_OLIVIA) return;
|
||||
trx_reset(scDevice.c_str());
|
||||
trx_reset();
|
||||
active_modem->restart();
|
||||
}
|
||||
|
||||
|
@ -1713,7 +1714,7 @@ void resetDOMEX() {
|
|||
md == MODE_DOMINOEX16 ||
|
||||
md == MODE_DOMINOEX22 ) {
|
||||
|
||||
trx_reset(scDevice.c_str());
|
||||
trx_reset();
|
||||
active_modem->restart();
|
||||
}
|
||||
}
|
||||
|
@ -1823,12 +1824,53 @@ void resetSoundCard()
|
|||
{
|
||||
bool mixer_enabled = progdefaults.EnableMixer;
|
||||
enableMixer(false);
|
||||
trx_reset(scDevice.c_str());
|
||||
progdefaults.SCdevice = scDevice;
|
||||
trx_reset();
|
||||
if (mixer_enabled)
|
||||
enableMixer(true);
|
||||
}
|
||||
|
||||
void update_sound_config(unsigned idx)
|
||||
{
|
||||
// radio button
|
||||
for (size_t i = 0; i < sizeof(btnAudioIO)/sizeof(*btnAudioIO); i++)
|
||||
btnAudioIO[i]->value(i == idx);
|
||||
|
||||
// devices
|
||||
menuOSSDev->deactivate();
|
||||
menuPortInDev->deactivate();
|
||||
menuPortOutDev->deactivate();
|
||||
inpPulseServer->deactivate();
|
||||
|
||||
// settings
|
||||
menuInSampleRate->deactivate();
|
||||
menuOutSampleRate->deactivate();
|
||||
|
||||
progdefaults.btnAudioIOis = idx;
|
||||
switch (idx) {
|
||||
case SND_IDX_OSS:
|
||||
menuOSSDev->activate();
|
||||
scDevice[0] = scDevice[1] = menuOSSDev->value();
|
||||
break;
|
||||
case SND_IDX_PORT:
|
||||
menuPortInDev->activate();
|
||||
menuPortOutDev->activate();
|
||||
menuInSampleRate->activate();
|
||||
menuOutSampleRate->activate();
|
||||
scDevice[0] = menuPortInDev->value();
|
||||
scDevice[1] = menuPortOutDev->value();
|
||||
break;
|
||||
case SND_IDX_PULSE:
|
||||
inpPulseServer->activate();
|
||||
menuInSampleRate->activate();
|
||||
menuOutSampleRate->activate();
|
||||
scDevice[0] = scDevice[1] = inpPulseServer->value();
|
||||
break;
|
||||
case SND_IDX_NULL:
|
||||
scDevice[0] = scDevice[1] = "";
|
||||
break;
|
||||
};
|
||||
}
|
||||
|
||||
void setReverse(int rev) {
|
||||
active_modem->set_reverse(rev);
|
||||
}
|
||||
|
|
|
@ -39,6 +39,9 @@
|
|||
#include "confdialog.h"
|
||||
#include "qrunner.h"
|
||||
|
||||
#include <FL/Fl.H>
|
||||
#include <FL/Fl_Value_Slider.H>
|
||||
|
||||
char feldmsg[80];
|
||||
|
||||
void feld::tx_init(SoundBase *sc)
|
||||
|
@ -85,59 +88,50 @@ feld::feld(trx_mode m)
|
|||
double lp;
|
||||
mode = m;
|
||||
samplerate = FeldSampleRate;
|
||||
FL_LOCK_D();
|
||||
bandwidth = sldrHellBW->value();
|
||||
FL_UNLOCK_D();
|
||||
|
||||
switch (mode) {
|
||||
case MODE_FSKHELL:
|
||||
bandwidth = 122.5;
|
||||
feldcolumnrate = 17.5;
|
||||
break;
|
||||
switch (mode) {
|
||||
// Amplitude modulation modes
|
||||
case MODE_FELDHELL:
|
||||
feldcolumnrate = 17.5;
|
||||
break;
|
||||
case MODE_SLOWHELL:
|
||||
bandwidth = 122.5;
|
||||
// bandwidth = 50;
|
||||
feldcolumnrate = 2.1875;
|
||||
break;
|
||||
case MODE_HELLX5:
|
||||
bandwidth = 1000;
|
||||
feldcolumnrate = 87.5;
|
||||
break;
|
||||
case MODE_HELLX9:
|
||||
bandwidth = 2000;
|
||||
feldcolumnrate = 157.5;
|
||||
break;
|
||||
// Frequency modulation modes
|
||||
case MODE_FSKHELL:
|
||||
feldcolumnrate = 17.5;
|
||||
break;
|
||||
case MODE_FSKH105:
|
||||
bandwidth = 55;
|
||||
feldcolumnrate = 17.5;
|
||||
break;
|
||||
case MODE_HELL80:
|
||||
bandwidth = 300;
|
||||
feldcolumnrate = 35;
|
||||
break;
|
||||
case MODE_FELDHELL:
|
||||
default :
|
||||
bandwidth = 122.5;
|
||||
feldcolumnrate = 17.5;
|
||||
break;
|
||||
}
|
||||
hell_bandwidth = bandwidth;
|
||||
|
||||
rxpixrate = (RxColumnLen * feldcolumnrate);
|
||||
txpixrate = (TxColumnLen * feldcolumnrate);
|
||||
downsampleinc = (double)(rxpixrate/samplerate);
|
||||
upsampleinc = (double)(txpixrate/samplerate);
|
||||
phi2freq = samplerate / txpixrate / 2.0 / M_PI;
|
||||
|
||||
hell_bandwidth = txpixrate;
|
||||
|
||||
hell_bandwidth = bandwidth;
|
||||
set_bandwidth(hell_bandwidth);
|
||||
|
||||
hilbert = new C_FIR_filter();
|
||||
hilbert->init_hilbert(37, 1);
|
||||
|
||||
if (mode == MODE_FELDHELL || mode == MODE_SLOWHELL ||
|
||||
mode == MODE_HELLX5 || mode == MODE_HELLX9 )
|
||||
lp = 1.5 * bandwidth / 2.0 / samplerate;
|
||||
else
|
||||
lp = 1.5 * txpixrate / samplerate;
|
||||
lp = 1.5 * bandwidth / samplerate;
|
||||
|
||||
bpfilt = new fftfilt(0, lp, 1024);
|
||||
|
||||
|
@ -181,7 +175,7 @@ void feld::FSKHELL_rx(complex z)
|
|||
double f;
|
||||
int vid;
|
||||
|
||||
f = (prev % z).arg() * samplerate / M_PI / bandwidth / 2.0;
|
||||
f = (prev % z).arg() * phi2freq;
|
||||
prev = z;
|
||||
|
||||
f = bbfilt->run(f);
|
||||
|
@ -275,20 +269,20 @@ int feld::rx_process(const double *buf, int len)
|
|||
// squelchon = progdefaults.sqlonoff;
|
||||
FL_UNLOCK_D();
|
||||
|
||||
switch (mode) {
|
||||
default:
|
||||
// switch (mode) {
|
||||
// default:
|
||||
if (bandwidth != hell_bandwidth) {
|
||||
double lp;
|
||||
hell_bandwidth = bandwidth;
|
||||
lp = 1.5 * bandwidth / 2.0 / samplerate;
|
||||
bpfilt->create_filter(0, lp);
|
||||
}
|
||||
break;
|
||||
case MODE_FSKHELL:
|
||||
case MODE_FSKH105:
|
||||
case MODE_HELL80:
|
||||
break;
|
||||
}
|
||||
// break;
|
||||
// case MODE_FSKHELL:
|
||||
// case MODE_FSKH105:
|
||||
// case MODE_HELL80:
|
||||
// break;
|
||||
// }
|
||||
|
||||
while (len-- > 0) {
|
||||
/* create analytic signal... */
|
||||
|
@ -394,10 +388,10 @@ void feld::send_symbol(int currsymb, int nextsymb)
|
|||
switch (mode) {
|
||||
case MODE_FSKHELL :
|
||||
case MODE_FSKH105 :
|
||||
tone = midtone + (reverse ? -1 : 1) * (currsymb ? -1 : 1) * bandwidth / 2.0;
|
||||
tone = midtone + (reverse ? -1 : 1) * (currsymb ? -1 : 1) * txpixrate / 2.0;
|
||||
break;
|
||||
case MODE_HELL80:
|
||||
tone = midtone - (reverse ? -1 : 1) * (currsymb ? -1 : 1) * bandwidth / 2.0;
|
||||
tone = midtone - (reverse ? -1 : 1) * (currsymb ? -1 : 1) * txpixrate / 2.0;
|
||||
break;
|
||||
case MODE_HELLX5 : case MODE_HELLX9 :
|
||||
Amp = currsymb;
|
||||
|
|
|
@ -81,17 +81,24 @@ extern Fl_Button *btnQRZpasswordShow;
|
|||
extern Fl_Group *tabSoundCard;
|
||||
extern Fl_Tabs *tabsSoundCard;
|
||||
extern Fl_Group *tabAudio;
|
||||
extern Fl_Group *AudioOSS;
|
||||
#include <FL/Fl_Input_Choice.H>
|
||||
extern Fl_Input_Choice *menuOSSDev;
|
||||
extern Fl_Group *AudioPort;
|
||||
extern Fl_Input_Choice *menuPortInDev;
|
||||
extern Fl_Input_Choice *menuPortOutDev;
|
||||
extern Fl_Group *AudioPulse;
|
||||
extern Fl_Input *inpPulseServer;
|
||||
extern Fl_Group *AudioNull;
|
||||
extern Fl_Round_Button *btnAudioIO[4];
|
||||
extern Fl_Group *tabAudioOpt;
|
||||
#include <FL/Fl_Spinner.H>
|
||||
extern Fl_Spinner *cntRxRateCorr;
|
||||
extern Fl_Spinner *cntTxRateCorr;
|
||||
extern Fl_Spinner *cntTxOffset;
|
||||
extern Fl_Group *AudioIO;
|
||||
extern Fl_Round_Button *btnAudioIO[3];
|
||||
#include <FL/Fl_Input_Choice.H>
|
||||
extern Fl_Input_Choice *menuOSSDev;
|
||||
extern Fl_Input_Choice *menuPADev;
|
||||
extern Fl_Menu_Item sample_rate_menu[];
|
||||
extern Fl_Choice *menuSampleRate;
|
||||
extern Fl_Choice *menuOutSampleRate;
|
||||
extern Fl_Choice *menuInSampleRate;
|
||||
extern Fl_Group *tabMixer;
|
||||
#include <FL/Fl_Light_Button.H>
|
||||
extern void setMixerInput(int);
|
||||
|
|
|
@ -130,10 +130,14 @@ struct configuration {
|
|||
string secText;
|
||||
// Sound card
|
||||
int btnAudioIOis;
|
||||
string SCdevice;
|
||||
string OSSdevice;
|
||||
string PAdevice;
|
||||
string PortInDevice;
|
||||
string PortOutDevice;
|
||||
string PulseServer;
|
||||
int sample_rate;
|
||||
int in_sample_rate;
|
||||
int out_sample_rate;
|
||||
string sample_converter;
|
||||
int RX_corr;
|
||||
int TX_corr;
|
||||
|
|
|
@ -59,14 +59,12 @@ protected:
|
|||
double txpixrate;
|
||||
double downsampleinc;
|
||||
double upsampleinc;
|
||||
double phi2freq;
|
||||
|
||||
C_FIR_filter *hilbert;
|
||||
fftfilt *bpfilt;
|
||||
Cmovavg *bbfilt;
|
||||
Cmovavg *minmaxfilt;
|
||||
// double bbfilter[MaxSymLen];
|
||||
// unsigned int filterptr;
|
||||
|
||||
//tx
|
||||
FELD_STATE tx_state;
|
||||
double txphacc;
|
||||
|
|
|
@ -129,6 +129,7 @@ extern void resetRTTY();
|
|||
extern void resetOLIVIA();
|
||||
extern void resetDOMEX();
|
||||
extern void resetSoundCard();
|
||||
extern void update_sound_config(unsigned idx);
|
||||
extern void restoreFocus();
|
||||
extern void setReverse(int);
|
||||
extern void clearQSO();
|
||||
|
|
|
@ -24,7 +24,7 @@
|
|||
extern string HomeDir;
|
||||
extern string xmlfname;
|
||||
|
||||
extern std::string scDevice;
|
||||
extern std::string scDevice[2];
|
||||
extern PTT *push2talk;
|
||||
#if USE_HAMLIB
|
||||
extern Rig *xcvr;
|
||||
|
|
|
@ -28,6 +28,8 @@
|
|||
#ifndef _SOUND_H
|
||||
#define _SOUND_H
|
||||
|
||||
enum { SND_IDX_UNKNOWN = -1, SND_IDX_OSS, SND_IDX_PORT, SND_IDX_PULSE, SND_IDX_NULL, SND_IDX_END };
|
||||
|
||||
#include <cstring>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
@ -55,15 +57,20 @@
|
|||
class SndException : public std::exception
|
||||
{
|
||||
public:
|
||||
SndException(int err_ = 0) : err(err_), msg(std::string("Sound error: ") + err_to_str()) { }
|
||||
SndException(const char* msg_) : err(1), msg(msg_) { }
|
||||
SndException(int err_ = 0)
|
||||
: err(err_), msg(std::string("Sound error: ") + err_to_str(err_))
|
||||
{ }
|
||||
SndException(const char* msg_)
|
||||
: err(1), msg(msg_)
|
||||
{ }
|
||||
SndException(int err_, const std::string& msg_) : err(err_), msg(msg_) { }
|
||||
virtual ~SndException() throw() { }
|
||||
|
||||
const char* what(void) const throw() { return msg.c_str(); }
|
||||
int error(void) const { return err; }
|
||||
|
||||
protected:
|
||||
virtual const char* err_to_str(void) { return strerror(err); }
|
||||
const char* err_to_str(int e) { return strerror(e); }
|
||||
|
||||
int err;
|
||||
std::string msg;
|
||||
|
@ -73,10 +80,12 @@ protected:
|
|||
class SndPortException : public SndException
|
||||
{
|
||||
public:
|
||||
SndPortException(int err_ = 0) : SndException(err_) { }
|
||||
SndPortException(int err_ = 0)
|
||||
: SndException(err_, std::string("PortAudio error: ") + err_to_str(err_))
|
||||
{ }
|
||||
SndPortException(const char* msg_) : SndException(msg_) { }
|
||||
protected:
|
||||
const char* err_to_str(void) { return Pa_GetErrorText(err); }
|
||||
const char* err_to_str(int e) { return Pa_GetErrorText(e); }
|
||||
};
|
||||
#endif
|
||||
|
||||
|
@ -84,10 +93,12 @@ protected:
|
|||
class SndPulseException : public SndException
|
||||
{
|
||||
public:
|
||||
SndPulseException(int err_ = 0) : SndException(err_) { }
|
||||
SndPulseException(int err_ = 0)
|
||||
: SndException(err_, std::string("PulseAudio error: ") + err_to_str(err_))
|
||||
{ }
|
||||
SndPulseException(const char* msg_) : SndException(msg_) { }
|
||||
protected:
|
||||
const char* err_to_str(void) { return pa_strerror(err); }
|
||||
const char* err_to_str(int e) { return pa_strerror(e); }
|
||||
};
|
||||
#endif
|
||||
|
||||
|
@ -203,7 +214,7 @@ public:
|
|||
static const std::vector<const PaDeviceInfo*>& devices(void);
|
||||
|
||||
public:
|
||||
SoundPort(const char *dev);
|
||||
SoundPort(const char *in_dev, const char *out_dev);
|
||||
~SoundPort();
|
||||
int Open(int mode, int freq = 8000);
|
||||
void Close();
|
||||
|
@ -215,31 +226,27 @@ public:
|
|||
private:
|
||||
void src_data_reset(int mode);
|
||||
void resample(int mode, float *buf, size_t count, size_t max = 0);
|
||||
void init_stream(void);
|
||||
void start_stream(void);
|
||||
bool stream_active(void);
|
||||
void init_stream(unsigned dir);
|
||||
void start_stream(unsigned dir);
|
||||
bool stream_active(unsigned dir);
|
||||
bool full_duplex_device(const PaDeviceInfo* dev);
|
||||
bool adjust_stream(void);
|
||||
double find_srate(void);
|
||||
double find_srate(unsigned dir);
|
||||
void pa_perror(int err, const char* str = 0);
|
||||
|
||||
private:
|
||||
std::string device;
|
||||
enum { STREAM_IN, STREAM_OUT };
|
||||
std::string device[2];
|
||||
|
||||
static bool pa_init;
|
||||
PaStream* stream;
|
||||
PaStream* stream[2];
|
||||
|
||||
device_iterator idev;
|
||||
device_iterator idev[2];
|
||||
static std::vector<const PaDeviceInfo*> devs;
|
||||
PaStreamParameters in_params;
|
||||
PaStreamParameters out_params;
|
||||
enum { STREAM_IN, STREAM_OUT };
|
||||
PaStreamParameters* stream_params[2];
|
||||
PaStreamParameters stream_params[2];
|
||||
|
||||
unsigned frames_per_buffer;
|
||||
unsigned max_frames_per_buffer;
|
||||
unsigned frames_per_buffer[2];
|
||||
double req_sample_rate;
|
||||
double dev_sample_rate;
|
||||
double dev_sample_rate[2];
|
||||
float *fbuf;
|
||||
};
|
||||
|
||||
|
@ -265,9 +272,8 @@ private:
|
|||
void resample(int mode, float *buf, size_t count, size_t max = 0);
|
||||
|
||||
private:
|
||||
double dev_sample_rate;
|
||||
pa_simple* in_stream;
|
||||
pa_simple* out_stream;
|
||||
double dev_sample_rate[2];
|
||||
pa_simple* stream[2];
|
||||
pa_sample_spec stream_params;
|
||||
|
||||
float* fbuf;
|
||||
|
@ -275,4 +281,16 @@ private:
|
|||
|
||||
#endif // USE_PULSEAUDIO
|
||||
|
||||
|
||||
class SoundNull : public SoundBase
|
||||
{
|
||||
public:
|
||||
int Open(int mode, int freq = 8000) { sample_frequency = freq; return 0; }
|
||||
void Close(void) { }
|
||||
size_t Write(double* buf, size_t count);
|
||||
size_t Write_stereo(double* bufleft, double* bufright, size_t count);
|
||||
size_t Read(double *buf, size_t count);
|
||||
bool full_duplex(void) { return true; }
|
||||
};
|
||||
|
||||
#endif // SOUND_H
|
||||
|
|
|
@ -37,9 +37,9 @@
|
|||
// ----------------------------------------------------------------------------
|
||||
|
||||
extern void trx_start_modem(modem *);
|
||||
extern void trx_start(const char *scdev);
|
||||
extern void trx_start(void);
|
||||
extern void trx_close();
|
||||
extern void trx_reset(const char *scdev);
|
||||
extern void trx_reset(void);
|
||||
extern void trx_start_macro_timer();
|
||||
|
||||
extern void wait_modem_ready_prep(void);
|
||||
|
|
75
src/main.cxx
75
src/main.cxx
|
@ -68,7 +68,7 @@
|
|||
|
||||
using namespace std;
|
||||
|
||||
string scDevice = "/dev/dsp1";
|
||||
string scDevice[2];
|
||||
bool pa_allow_full_duplex = false;
|
||||
int pa_frames_per_buffer = 0;
|
||||
|
||||
|
@ -196,7 +196,7 @@ int main(int argc, char ** argv)
|
|||
progdefaults.openDefaults();
|
||||
|
||||
sound_init();
|
||||
trx_start(scDevice.c_str());
|
||||
trx_start();
|
||||
|
||||
progdefaults.initInterface();
|
||||
fl_digi_main->show(argc, argv);
|
||||
|
@ -221,7 +221,7 @@ void sound_init(void)
|
|||
glob("/dev/dsp*", 0, NULL, &gbuf);
|
||||
for (size_t i = 0; i < gbuf.gl_pathc; i++)
|
||||
menuOSSDev->add(gbuf.gl_pathv[i]);
|
||||
if (progdefaults.OSSdevice == "")
|
||||
if (progdefaults.OSSdevice.length() == 0)
|
||||
progdefaults.OSSdevice = gbuf.gl_pathv[0];
|
||||
menuOSSDev->value(progdefaults.OSSdevice.c_str());
|
||||
globfree(&gbuf);
|
||||
|
@ -243,21 +243,33 @@ void sound_init(void)
|
|||
s.insert(i, 1, '\\');
|
||||
i += 2;
|
||||
}
|
||||
menuPADev->add(s.c_str());
|
||||
if ((*idev)->maxInputChannels > 0)
|
||||
menuPortInDev->add(s.c_str());
|
||||
if ((*idev)->maxOutputChannels > 0)
|
||||
menuPortOutDev->add(s.c_str());
|
||||
}
|
||||
// set the initial value in the configuration structure
|
||||
if (progdefaults.PAdevice == "")
|
||||
progdefaults.PAdevice = (*(SoundPort::devices().begin() + Pa_GetDefaultOutputDevice()))->name;
|
||||
menuPADev->value(progdefaults.PAdevice.c_str());
|
||||
|
||||
btnAudioIO[1]->activate();
|
||||
if (progdefaults.PortInDevice.length() == 0) {
|
||||
if (progdefaults.PAdevice.length() == 0)
|
||||
progdefaults.PortInDevice = (*(SoundPort::devices().begin() + Pa_GetDefaultInputDevice()))->name;
|
||||
else
|
||||
progdefaults.PortInDevice = progdefaults.PAdevice;
|
||||
}
|
||||
menuPortInDev->value(progdefaults.PortInDevice.c_str());
|
||||
if (progdefaults.PortOutDevice.length() == 0) {
|
||||
if (progdefaults.PAdevice.length() == 0)
|
||||
progdefaults.PortOutDevice = (*(SoundPort::devices().begin() + Pa_GetDefaultOutputDevice()))->name;
|
||||
else
|
||||
progdefaults.PortOutDevice = progdefaults.PAdevice;
|
||||
}
|
||||
menuPortOutDev->value(progdefaults.PortOutDevice.c_str());
|
||||
#endif
|
||||
|
||||
#if USE_OSS
|
||||
glob("/dev/mixer*", 0, NULL, &gbuf);
|
||||
for (size_t i = 0; i < gbuf.gl_pathc; i++)
|
||||
for (size_t i = 0; i < gbuf.gl_pathc; i++)
|
||||
menuMix->add(gbuf.gl_pathv[i]);
|
||||
if (progdefaults.MXdevice == "")
|
||||
if (progdefaults.MXdevice.length() == 0)
|
||||
progdefaults.MXdevice = gbuf.gl_pathv[0];
|
||||
globfree(&gbuf);
|
||||
menuMix->value(progdefaults.MXdevice.c_str());
|
||||
|
@ -268,15 +280,19 @@ void sound_init(void)
|
|||
|
||||
// set the Sound Card configuration tab to the correct initial values
|
||||
#if !USE_OSS
|
||||
btnAudioIO[0]->deactivate();
|
||||
AudioOSS->deactivate();
|
||||
btnAudioIO[SND_IDX_OSS]->deactivate();
|
||||
#endif
|
||||
#if !USE_PORTAUDIO
|
||||
btnAudioIO[1]->deactivate();
|
||||
AudioPort->deactivate();
|
||||
btnAudioIO[SND_IDX_PORT]->deactivate();
|
||||
#endif
|
||||
#if !USE_PULSEAUDIO
|
||||
btnAudioIO[2]->deactivate();
|
||||
AudioPulse->deactivate();
|
||||
btnAudioIO[SND_IDX_PULSE]->deactivate();
|
||||
#endif
|
||||
if (progdefaults.btnAudioIOis == -1 || !btnAudioIO[progdefaults.btnAudioIOis]->active()) {
|
||||
if (progdefaults.btnAudioIOis == SND_IDX_UNKNOWN ||
|
||||
!btnAudioIO[progdefaults.btnAudioIOis]->active()) { // or saved sound api now disabled
|
||||
for (size_t i = 0; i < sizeof(btnAudioIO)/sizeof(*btnAudioIO); i++) {
|
||||
if (btnAudioIO[i]->active()) {
|
||||
progdefaults.btnAudioIOis = i;
|
||||
|
@ -284,33 +300,8 @@ void sound_init(void)
|
|||
}
|
||||
}
|
||||
}
|
||||
switch (progdefaults.btnAudioIOis) {
|
||||
case 0:
|
||||
scDevice = progdefaults.OSSdevice;
|
||||
btnAudioIO[0]->value(1);
|
||||
btnAudioIO[1]->value(0);
|
||||
btnAudioIO[2]->value(0);
|
||||
menuOSSDev->activate();
|
||||
menuPADev->deactivate();
|
||||
menuSampleRate->deactivate();
|
||||
break;
|
||||
case 1:
|
||||
scDevice = progdefaults.PAdevice;
|
||||
btnAudioIO[0]->value(0);
|
||||
btnAudioIO[1]->value(1);
|
||||
btnAudioIO[2]->value(0);
|
||||
menuOSSDev->deactivate();
|
||||
menuPADev->activate();
|
||||
break;
|
||||
case 2:
|
||||
scDevice = "localhost";
|
||||
btnAudioIO[0]->value(0);
|
||||
btnAudioIO[1]->value(0);
|
||||
btnAudioIO[2]->value(1);
|
||||
menuOSSDev->deactivate();
|
||||
menuPADev->deactivate();
|
||||
break;
|
||||
}
|
||||
update_sound_config(progdefaults.btnAudioIOis);
|
||||
|
||||
resetMixerControls();
|
||||
}
|
||||
|
||||
|
|
|
@ -134,10 +134,14 @@ configuration progdefaults = {
|
|||
"fldigi ", // secondary text
|
||||
// Sound card
|
||||
-1, // int btnAudioIOis
|
||||
"", // string SCdevice;
|
||||
"", // string OSSdevice;
|
||||
"", // string PAdevice;
|
||||
"", // string PortIndevice;
|
||||
"", // string PortOutDevice;
|
||||
"", // string PulseServer
|
||||
0, // int sample_rate;
|
||||
-1, // int in_sample_rate;
|
||||
-1, // int out_sample_rate;
|
||||
"src-sinc-fastest", // string sample_converter;
|
||||
0, // int RX_corr;
|
||||
0, // int TX_corr;
|
||||
|
@ -226,8 +230,8 @@ enum TAG { \
|
|||
HAMRIGNAME, HAMRIGDEVICE, HAMRIGBAUDRATE,
|
||||
PTTDEV,
|
||||
SECONDARYTEXT,
|
||||
AUDIOIO, SCDEVICE, OSSDEVICE, PADEVICE,
|
||||
SAMPLERATE, RXCORR, TXCORR, TXOFFSET,
|
||||
AUDIOIO, OSSDEVICE, PADEVICE, PORTINDEVICE, PORTOUTDEVICE, PULSESERVER,
|
||||
SAMPLERATE, INSAMPLERATE, OUTSAMPLERATE, RXCORR, TXCORR, TXOFFSET,
|
||||
USELEADINGZEROS, CONTESTSTART, CONTESTDIGITS,
|
||||
USETIMER, MACRONUMBER, TIMEOUT,
|
||||
MXDEVICE, RCVMIXER, XMTMIXER, PCMVOLUME,
|
||||
|
@ -392,11 +396,14 @@ void configuration::writeDefaultsXML()
|
|||
|
||||
writeXMLstr(f, "PTTDEV", PTTdev);
|
||||
writeXMLstr(f, "SECONDARYTEXT", secText);
|
||||
writeXMLint(f, "AUDIOIO", btnAudioIOis);
|
||||
writeXMLstr(f, "SCDEVICE", SCdevice);
|
||||
writeXMLint(f, "AUDIOIO", btnAudioIOis);
|
||||
writeXMLstr(f, "OSSDEVICE", OSSdevice);
|
||||
writeXMLstr(f, "PADEVICE", PAdevice);
|
||||
writeXMLstr(f, "PORTINDEVICE", PortInDevice);
|
||||
writeXMLstr(f, "PORTOUTDEVICE", PortOutDevice);
|
||||
writeXMLint(f, "SAMPLERATE", sample_rate);
|
||||
writeXMLint(f, "INSAMPLERATE", in_sample_rate);
|
||||
writeXMLint(f, "OUTSAMPLERATE", out_sample_rate);
|
||||
writeXMLint(f, "RXCORR", RX_corr);
|
||||
writeXMLint(f, "TXCORR", TX_corr);
|
||||
writeXMLint(f, "TXOFFSET", TxOffset);
|
||||
|
@ -738,18 +745,30 @@ bool configuration::readDefaultsXML()
|
|||
case AUDIOIO :
|
||||
btnAudioIOis = atoi(xml->getNodeData());
|
||||
break;
|
||||
case SCDEVICE :
|
||||
SCdevice = xml->getNodeData();
|
||||
break;
|
||||
case OSSDEVICE :
|
||||
OSSdevice = xml->getNodeData();
|
||||
break;
|
||||
case PADEVICE :
|
||||
PAdevice = xml->getNodeData();
|
||||
break;
|
||||
case PORTINDEVICE :
|
||||
PortInDevice = xml->getNodeData();
|
||||
break;
|
||||
case PORTOUTDEVICE :
|
||||
PortOutDevice = xml->getNodeData();
|
||||
break;
|
||||
case PULSESERVER :
|
||||
PulseServer = xml->getNodeData();
|
||||
break;
|
||||
case SAMPLERATE :
|
||||
sample_rate = atoi(xml->getNodeData());
|
||||
break;
|
||||
case INSAMPLERATE :
|
||||
in_sample_rate = atoi(xml->getNodeData());
|
||||
break;
|
||||
case OUTSAMPLERATE :
|
||||
out_sample_rate = atoi(xml->getNodeData());
|
||||
break;
|
||||
case RXCORR :
|
||||
RX_corr = atoi(xml->getNodeData());
|
||||
break;
|
||||
|
@ -960,10 +979,13 @@ bool configuration::readDefaultsXML()
|
|||
else if (!strcmp("PTTDEV", nodeName)) tag = PTTDEV;
|
||||
else if (!strcmp("SECONDARYTEXT", nodeName)) tag = SECONDARYTEXT;
|
||||
else if (!strcmp("AUDIOIO", nodeName)) tag = AUDIOIO;
|
||||
else if (!strcmp("SCDEVICE", nodeName)) tag = SCDEVICE;
|
||||
else if (!strcmp("OSSDEVICE", nodeName)) tag = OSSDEVICE;
|
||||
else if (!strcmp("PADEVICE", nodeName)) tag = PADEVICE;
|
||||
else if (!strcmp("PORTINDEVICE", nodeName)) tag = PORTINDEVICE;
|
||||
else if (!strcmp("PORTOUTDEVICE", nodeName)) tag = PORTOUTDEVICE;
|
||||
else if (!strcmp("SAMPLERATE", nodeName)) tag = SAMPLERATE;
|
||||
else if (!strcmp("INSAMPLERATE", nodeName)) tag = INSAMPLERATE;
|
||||
else if (!strcmp("OUTSAMPLERATE", nodeName)) tag = OUTSAMPLERATE;
|
||||
else if (!strcmp("RXCORR", nodeName)) tag = RXCORR;
|
||||
else if (!strcmp("TXCORR", nodeName)) tag = TXCORR;
|
||||
else if (!strcmp("TXOFFSET", nodeName)) tag = TXOFFSET;
|
||||
|
@ -1280,26 +1302,32 @@ int configuration::openDefaults() {
|
|||
btnMicIn->value(MicIn);
|
||||
btnLineIn->value(LineIn);
|
||||
|
||||
btnAudioIO[0]->value(0);
|
||||
btnAudioIO[0]->value(0);
|
||||
btnAudioIO[btnAudioIOis]->value(1);
|
||||
|
||||
menuOSSDev->value(OSSdevice.c_str());
|
||||
menuPADev->value(PAdevice.c_str());
|
||||
if (btnAudioIOis == 1)
|
||||
menuPADev->activate();
|
||||
menuPortInDev->value(PortInDevice.c_str());
|
||||
menuPortOutDev->value(PortOutDevice.c_str());
|
||||
inpPulseServer->value(PulseServer.c_str());
|
||||
|
||||
btnMixer->value(EnableMixer);
|
||||
resetMixerControls();
|
||||
menuMix->value(MXdevice.c_str());
|
||||
|
||||
if (sample_rate > 1) {
|
||||
char s[6+1];
|
||||
snprintf(s, sizeof(s), "%d", sample_rate);
|
||||
menuSampleRate->value(menuSampleRate->find_item(s));
|
||||
char sr[6+1];
|
||||
if (in_sample_rate == -1)
|
||||
in_sample_rate = sample_rate;
|
||||
if (in_sample_rate > 1) {
|
||||
snprintf(sr, sizeof(sr), "%d", in_sample_rate);
|
||||
menuInSampleRate->value(menuInSampleRate->find_item(sr));
|
||||
}
|
||||
else
|
||||
menuSampleRate->value(sample_rate);
|
||||
menuInSampleRate->value(in_sample_rate);
|
||||
if (out_sample_rate == -1)
|
||||
out_sample_rate = sample_rate;
|
||||
if (out_sample_rate > 1) {
|
||||
snprintf(sr, sizeof(sr), "%d", out_sample_rate);
|
||||
menuOutSampleRate->value(menuOutSampleRate->find_item(sr));
|
||||
}
|
||||
else
|
||||
menuOutSampleRate->value(out_sample_rate);
|
||||
|
||||
cntRxRateCorr->value(RX_corr);
|
||||
cntTxRateCorr->value(TX_corr);
|
||||
|
|
|
@ -4,6 +4,8 @@
|
|||
// Copyright (C) 2006
|
||||
// Dave Freese, W1HKJ
|
||||
// Leigh Klotz, WA5ZNU
|
||||
// Copyright (C) 2008
|
||||
// Stelios Bounanos, M0GLD
|
||||
//
|
||||
// This file is part of fldigi.
|
||||
//
|
||||
|
@ -60,11 +62,7 @@ using namespace std;
|
|||
|
||||
int rotoroffset = 0;
|
||||
|
||||
string htmlpage = "";
|
||||
string xmlpage = "";
|
||||
string sessionpage = "";
|
||||
string host = "online.qrz.com";
|
||||
string detail;
|
||||
string qrzSessionKey;
|
||||
string qrzalert;
|
||||
string qrzerror;
|
||||
|
@ -83,17 +81,7 @@ string qrzlatd;
|
|||
string qrzlond;
|
||||
string qrznotes;
|
||||
|
||||
const char *error[] = {
|
||||
"OK", // err 0
|
||||
"Host not found", // err 1
|
||||
"Not an IP host!", // err 2
|
||||
"No http service", // err 3
|
||||
"Cannot open socket", // err 4
|
||||
"Cannot Connect to server", // err 5
|
||||
"Socket write error", // err 6
|
||||
"Socket timeout", // err 7
|
||||
"Socket select error" // err 8
|
||||
};
|
||||
const char* error_string;
|
||||
|
||||
enum QUERYTYPE { NONE, QRZCD, QRZNET, HAMCALLNET };
|
||||
QUERYTYPE DB_query = NONE;
|
||||
|
@ -104,16 +92,6 @@ enum TAG { \
|
|||
ZIP, COUNTRY,LATD, LOND, GRID, \
|
||||
DOB };
|
||||
|
||||
char rbuffer[32768];
|
||||
|
||||
fd_set readfds, testfds;
|
||||
struct timeval timeout;
|
||||
int sockfd = -1;
|
||||
int result;
|
||||
struct sockaddr_in address;
|
||||
struct hostent *hostinfo;
|
||||
struct servent *servinfo;
|
||||
|
||||
int qrzdummy;
|
||||
Fl_Thread QRZ_thread;
|
||||
bool QRZ_exit = false;
|
||||
|
@ -123,8 +101,12 @@ static void *CALLSIGNloop(void *args);
|
|||
|
||||
bool parseSessionKey();
|
||||
bool parse_xml();
|
||||
int getSessionKey();
|
||||
int QRZGetXML();
|
||||
|
||||
int connect_to_server(const char* node, const char* service, int* fd);
|
||||
ssize_t read_from_server(int fd, const string& request, string& reply, struct timeval* timeout);
|
||||
|
||||
bool getSessionKey(string& sessionpage);
|
||||
bool QRZGetXML(string& xmlpage);
|
||||
int bearing(const char *, const char *);
|
||||
void qra(const char *, double &, double &);
|
||||
void QRZ_disp_result();
|
||||
|
@ -135,16 +117,16 @@ void QRZinit(void);
|
|||
void QRZclose(void);
|
||||
void qthappend(string &qth, string &datum);
|
||||
void QRZAlert();
|
||||
int QRZLogin();
|
||||
bool QRZLogin(string& sessionpage);
|
||||
void QRZquery();
|
||||
void parse_html();
|
||||
int HAMCALLget();
|
||||
void parse_html(const string& htmlpage);
|
||||
bool HAMCALLget(string& htmlpage);
|
||||
void HAMCALLquery();
|
||||
|
||||
|
||||
QRZ *qCall;
|
||||
|
||||
bool parseSessionKey()
|
||||
bool parseSessionKey(const string& sessionpage)
|
||||
{
|
||||
IrrXMLReader* xml = createIrrXMLReader(new IIrrXMLStringReader(sessionpage));
|
||||
TAG tag=IGNORE;
|
||||
|
@ -190,11 +172,11 @@ bool parseSessionKey()
|
|||
}
|
||||
}
|
||||
delete xml;
|
||||
return 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool parse_xml()
|
||||
bool parse_xml(const string& xmlpage)
|
||||
{
|
||||
IrrXMLReader* xml = createIrrXMLReader(new IIrrXMLStringReader(xmlpage));
|
||||
|
||||
|
@ -328,158 +310,158 @@ bool parse_xml()
|
|||
|
||||
// delete the xml parser after usage
|
||||
delete xml;
|
||||
return 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
int getSessionKey()
|
||||
// Open a stream socket, connect it to "service" running on "node", and copy it
|
||||
// to *fd. Return 0 if successful, < 0 if getaddrinfo failed, or errno if there
|
||||
// was some other error.
|
||||
int connect_to_server(const char* node, const char* service, int* fd)
|
||||
{
|
||||
hostinfo = gethostbyname(host.c_str());
|
||||
if(!hostinfo)
|
||||
return 1;
|
||||
if(hostinfo->h_addrtype != AF_INET)
|
||||
return 2;
|
||||
servinfo = getservbyname("http", "tcp");
|
||||
if(!servinfo)
|
||||
return 3;
|
||||
struct addrinfo hints, *ai, *aip;
|
||||
|
||||
sockfd = socket(AF_INET, SOCK_STREAM, 0);
|
||||
|
||||
if (sockfd == -1)
|
||||
return 4;
|
||||
address.sin_family = AF_INET;
|
||||
address.sin_port = servinfo->s_port;
|
||||
address.sin_addr = *(struct in_addr *)*hostinfo->h_addr_list;
|
||||
memset(&hints, 0, sizeof(hints));
|
||||
#ifdef AI_ADDRCONFIG
|
||||
hints.ai_flags = AI_ADDRCONFIG;
|
||||
#endif
|
||||
hints.ai_family = AF_UNSPEC;
|
||||
hints.ai_socktype = SOCK_STREAM;
|
||||
|
||||
result = connect(sockfd, (struct sockaddr *)&address, sizeof(address));
|
||||
if(result == -1) {
|
||||
close(sockfd);
|
||||
return 5;
|
||||
}
|
||||
|
||||
{
|
||||
detail = "GET /bin/xml?username=";
|
||||
detail += progdefaults.QRZusername;
|
||||
detail += ";password=";
|
||||
detail += progdefaults.QRZuserpassword;
|
||||
detail += ";version=";
|
||||
detail += PACKAGE_NAME;
|
||||
detail += "/";
|
||||
detail += PACKAGE_VERSION;
|
||||
detail += " HTTP/1.0\n";
|
||||
detail += "Host: ";
|
||||
detail += host;
|
||||
detail += "\n";
|
||||
detail += "Connection: close\n";
|
||||
detail += "\n";
|
||||
}
|
||||
|
||||
|
||||
result = write(sockfd, detail.c_str() , detail.length());
|
||||
if (result != (int)detail.length()) {
|
||||
close(sockfd);
|
||||
return 6;
|
||||
}
|
||||
|
||||
FD_ZERO(&readfds);
|
||||
FD_SET(sockfd, &readfds);
|
||||
|
||||
while (1) {
|
||||
testfds = readfds;
|
||||
timeout.tv_sec = 5; // timeout = 5 seconds
|
||||
timeout.tv_usec = 0;
|
||||
result = select(FD_SETSIZE, &testfds, (fd_set *)0, (fd_set *)0, &timeout);
|
||||
if (result == 0) {
|
||||
int res;
|
||||
if ((res = getaddrinfo(node, service, &hints, &ai)) < 0)
|
||||
return res;
|
||||
for (aip = ai; aip; aip = ai->ai_next) { // use the first one that works
|
||||
if ((*fd = socket(aip->ai_family, aip->ai_socktype, aip->ai_protocol)) == -1)
|
||||
continue;
|
||||
if (connect(*fd, aip->ai_addr, aip->ai_addrlen) == 0)
|
||||
break;
|
||||
}
|
||||
if (result == -1) {
|
||||
close(sockfd);
|
||||
return 8;
|
||||
}
|
||||
if (FD_ISSET(sockfd, &testfds)) {
|
||||
memset(rbuffer, 0, 32768);
|
||||
result = read(sockfd, rbuffer, sizeof(rbuffer));
|
||||
if (result <= 0) break;
|
||||
sessionpage += rbuffer;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
close(sockfd);
|
||||
return 0;
|
||||
if (!aip) { // no usable address found
|
||||
res = errno;
|
||||
if (*fd >= 0)
|
||||
close(*fd);
|
||||
}
|
||||
|
||||
freeaddrinfo(ai);
|
||||
return res;
|
||||
}
|
||||
|
||||
int QRZGetXML()
|
||||
// Write "request" to socket fd, append reply to "reply". Wait a maximum of
|
||||
// "timeout" for each operation. Return the number of bytes read, or -ETIMEDOUT
|
||||
// if a read or write timed out, or -errno if there was some other error.
|
||||
ssize_t read_from_server(int fd, const string& request, string& reply, struct timeval* timeout)
|
||||
{
|
||||
hostinfo = gethostbyname(host.c_str());
|
||||
if(!hostinfo)
|
||||
return 1;
|
||||
if(hostinfo->h_addrtype != AF_INET)
|
||||
return 2;
|
||||
servinfo = getservbyname("http", "tcp");
|
||||
if(!servinfo)
|
||||
return 3;
|
||||
char rbuffer[32768];
|
||||
fd_set rwset;
|
||||
ssize_t res, n;
|
||||
|
||||
sockfd = socket(AF_INET, SOCK_STREAM, 0);
|
||||
|
||||
if (sockfd == -1)
|
||||
return 4;
|
||||
address.sin_family = AF_INET;
|
||||
address.sin_port = servinfo->s_port;
|
||||
address.sin_addr = *(struct in_addr *)*hostinfo->h_addr_list;
|
||||
// write request
|
||||
n = 0;
|
||||
for (;;) {
|
||||
FD_ZERO(&rwset);
|
||||
FD_SET(fd, &rwset);
|
||||
res = select(fd + 1, 0, &rwset, 0, timeout);
|
||||
if (res == -1)
|
||||
return -errno;
|
||||
else if (res == 0)
|
||||
return -ETIMEDOUT;
|
||||
|
||||
result = connect(sockfd, (struct sockaddr *)&address, sizeof(address));
|
||||
if(result == -1) {
|
||||
close(sockfd);
|
||||
return 5;
|
||||
}
|
||||
|
||||
{
|
||||
detail = "GET /bin/xml?s=";
|
||||
detail += qrzSessionKey;
|
||||
detail += ";callsign=";
|
||||
detail += callsign;
|
||||
detail += " HTTP/1.0\n";
|
||||
detail += "Host: ";
|
||||
detail += host;
|
||||
detail += "\n";
|
||||
detail += "Connection: close\n";
|
||||
detail += "\n";
|
||||
}
|
||||
|
||||
result = write(sockfd, detail.c_str() , detail.length());
|
||||
if (result != (int)detail.length()) {
|
||||
close(sockfd);
|
||||
return 6;
|
||||
}
|
||||
|
||||
FD_ZERO(&readfds);
|
||||
FD_SET(sockfd, &readfds);
|
||||
|
||||
while (1) {
|
||||
testfds = readfds;
|
||||
timeout.tv_sec = 5; // timeout = 5 seconds
|
||||
timeout.tv_usec = 0;
|
||||
result = select(FD_SETSIZE, &testfds, (fd_set *)0, (fd_set *)0, &timeout);
|
||||
if (result == 0) {
|
||||
res = write(fd, request.c_str() + n, request.length() - n);
|
||||
if (res > 0)
|
||||
n += res;
|
||||
else
|
||||
return -errno;
|
||||
if ((size_t)n == request.length())
|
||||
break;
|
||||
}
|
||||
|
||||
// read reply
|
||||
n = 0;
|
||||
for (;;) {
|
||||
FD_ZERO(&rwset);
|
||||
FD_SET(fd, &rwset);
|
||||
res = select(fd + 1, &rwset, 0, 0, timeout);
|
||||
if (res == -1)
|
||||
return -errno;
|
||||
else if (res == 0)
|
||||
return -ETIMEDOUT;
|
||||
|
||||
res = read(fd, rbuffer, sizeof(rbuffer));
|
||||
if (res > 0) {
|
||||
reply.append(rbuffer, (unsigned)res);
|
||||
n += res;
|
||||
}
|
||||
if (result == -1) {
|
||||
close(sockfd);
|
||||
return 8;
|
||||
}
|
||||
if (FD_ISSET(sockfd, &testfds)) {
|
||||
memset(rbuffer, 0, 32768);
|
||||
result = read(sockfd, rbuffer, sizeof(rbuffer));
|
||||
if (result <= 0) break;
|
||||
xmlpage += rbuffer;
|
||||
} else {
|
||||
else {
|
||||
if (res < 0)
|
||||
return -errno;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
bool getSessionKey(string& sessionpage)
|
||||
{
|
||||
int r, sockfd;
|
||||
if ((r = connect_to_server(host.c_str(), "http", &sockfd)) != 0) {
|
||||
error_string = r > 0 ? strerror(r) : gai_strerror(r);
|
||||
return false;
|
||||
}
|
||||
|
||||
string detail;
|
||||
detail = "GET /bin/xml?username=";
|
||||
detail += progdefaults.QRZusername;
|
||||
detail += ";password=";
|
||||
detail += progdefaults.QRZuserpassword;
|
||||
detail += ";version=";
|
||||
detail += PACKAGE_NAME;
|
||||
detail += "/";
|
||||
detail += PACKAGE_VERSION;
|
||||
detail += " HTTP/1.0\n";
|
||||
detail += "Host: ";
|
||||
detail += host;
|
||||
detail += "\n";
|
||||
detail += "Connection: close\n";
|
||||
detail += "\n";
|
||||
|
||||
struct timeval timeout = { 5, 0 }; // timeout = 5 seconds
|
||||
ssize_t nread = read_from_server(sockfd, detail, sessionpage, &timeout);
|
||||
close(sockfd);
|
||||
return 0;
|
||||
if (nread < 0 && nread != -ETIMEDOUT)
|
||||
error_string = strerror(-nread);
|
||||
|
||||
// for some strange reason we return successfully even if we time out
|
||||
return nread == -ETIMEDOUT || nread > 0;
|
||||
}
|
||||
|
||||
bool QRZGetXML(string& xmlpage)
|
||||
{
|
||||
int r, sockfd;
|
||||
if ((r = connect_to_server(host.c_str(), "http", &sockfd)) != 0) {
|
||||
error_string = r > 0 ? strerror(r) : gai_strerror(r);
|
||||
return false;
|
||||
}
|
||||
|
||||
string detail;
|
||||
detail = "GET /bin/xml?s=";
|
||||
detail += qrzSessionKey;
|
||||
detail += ";callsign=";
|
||||
detail += callsign;
|
||||
detail += " HTTP/1.0\n";
|
||||
detail += "Host: ";
|
||||
detail += host;
|
||||
detail += "\n";
|
||||
detail += "Connection: close\n";
|
||||
detail += "\n";
|
||||
|
||||
struct timeval timeout = { 5, 0 }; // timeout = 5 seconds
|
||||
ssize_t nread = read_from_server(sockfd, detail, xmlpage, &timeout);
|
||||
close(sockfd);
|
||||
if (nread < 0)
|
||||
error_string = strerror(-nread);
|
||||
|
||||
return nread > 0;
|
||||
}
|
||||
|
||||
int bearing(const char *myqra, const char *dxqra) {
|
||||
|
@ -528,14 +510,14 @@ void QRZ_disp_result()
|
|||
FL_LOCK();
|
||||
{
|
||||
if (qrzfname.length() > 0) {
|
||||
int spacePos = qrzfname.find(" ");
|
||||
string::size_type spacePos = qrzfname.find(" ");
|
||||
// if fname is "ABC" then display "ABC"
|
||||
// or if fname is "X Y" then display "X Y"
|
||||
if (spacePos ==-1 || (spacePos == 1)) {
|
||||
if (spacePos == string::npos || (spacePos == 1)) {
|
||||
inpName->value(qrzfname.c_str());
|
||||
}
|
||||
// if fname is "ABC Y" then display "ABC"
|
||||
else if (spacePos == ((int)qrzfname.length())-2) {
|
||||
else if (spacePos == qrzfname.length() - 2) {
|
||||
string fname="";
|
||||
fname.assign(qrzfname, 0, spacePos);
|
||||
inpName->value(fname.c_str());
|
||||
|
@ -577,7 +559,6 @@ void QRZ_COM_query()
|
|||
return;
|
||||
}
|
||||
|
||||
xmlpage = "";
|
||||
DB_query = QRZNET;
|
||||
FL_LOCK();
|
||||
inpNotes->value(" *** Request sent to qrz.com ***");
|
||||
|
@ -591,10 +572,9 @@ void HAMCALL_COM_query()
|
|||
if (!QRZ_enabled)
|
||||
return;
|
||||
}
|
||||
htmlpage = "";
|
||||
DB_query = HAMCALLNET;
|
||||
FL_LOCK();
|
||||
inpNotes->value(" *** Request sent to www.hamcomm.net ***");
|
||||
inpNotes->value(" *** Request sent to www.hamcall.net ***");
|
||||
FL_UNLOCK();
|
||||
}
|
||||
|
||||
|
@ -673,33 +653,36 @@ void QRZAlert()
|
|||
}
|
||||
}
|
||||
|
||||
int QRZLogin() {
|
||||
int err=0;
|
||||
bool QRZLogin(string& sessionpage) {
|
||||
bool ok = true;
|
||||
if (qrzSessionKey.empty()) {
|
||||
err = getSessionKey();
|
||||
if (!err) err = parseSessionKey();
|
||||
ok = getSessionKey(sessionpage);
|
||||
if (ok) ok = parseSessionKey(sessionpage);
|
||||
}
|
||||
if (! err) {
|
||||
if (!ok) {
|
||||
QRZAlert();
|
||||
}
|
||||
return err;
|
||||
return ok;
|
||||
}
|
||||
|
||||
void QRZquery()
|
||||
{
|
||||
int err=0;
|
||||
bool ok = true;
|
||||
|
||||
string qrzpage;
|
||||
|
||||
if (qrzSessionKey.empty())
|
||||
err = QRZLogin();
|
||||
if (!err)
|
||||
err = QRZGetXML();
|
||||
if (!err) {
|
||||
ok = QRZLogin(qrzpage);
|
||||
if (ok)
|
||||
ok = QRZGetXML(qrzpage);
|
||||
if (ok) {
|
||||
if (qrzSessionKey.empty())
|
||||
err = QRZLogin();
|
||||
if (!err)
|
||||
err = QRZGetXML();
|
||||
ok = QRZLogin(qrzpage);
|
||||
if (ok)
|
||||
ok = QRZGetXML(qrzpage);
|
||||
}
|
||||
if (!err) {
|
||||
parse_xml();
|
||||
if (ok) {
|
||||
parse_xml(qrzpage);
|
||||
if (!qrzalert.empty()) {
|
||||
FL_LOCK();
|
||||
inpNotes->value(qrzalert.c_str());
|
||||
|
@ -719,9 +702,9 @@ void QRZquery()
|
|||
QRZ_disp_result();
|
||||
}
|
||||
}
|
||||
if (err) {
|
||||
if (!ok) {
|
||||
FL_LOCK();
|
||||
inpNotes->value(error[err]);
|
||||
inpNotes->value(error_string);
|
||||
FL_UNLOCK();
|
||||
}
|
||||
}
|
||||
|
@ -736,7 +719,7 @@ void QRZquery()
|
|||
#define HAMCALL_GRID 202
|
||||
#define HAMCALL_DOB 194
|
||||
|
||||
void parse_html()
|
||||
void parse_html(const string& htmlpage)
|
||||
{
|
||||
size_t p;
|
||||
qrzborn="";
|
||||
|
@ -776,85 +759,44 @@ void parse_html()
|
|||
}
|
||||
}
|
||||
|
||||
int HAMCALLget()
|
||||
bool HAMCALLget(string& htmlpage)
|
||||
{
|
||||
int r, sockfd;
|
||||
if ((r = connect_to_server(HAMCALL_HOST, "http", &sockfd)) != 0) {
|
||||
error_string = r > 0 ? strerror(r) : gai_strerror(r);
|
||||
return false;
|
||||
}
|
||||
|
||||
string url_detail;
|
||||
int len;
|
||||
|
||||
url_detail = "GET /call?username=";
|
||||
url_detail = "GET /call?username=";
|
||||
url_detail += progdefaults.QRZusername;
|
||||
url_detail += "&password=";
|
||||
url_detail += progdefaults.QRZuserpassword;
|
||||
url_detail += "&rawlookup=1&callsign=";
|
||||
url_detail += callsign;
|
||||
url_detail += "&program=WA5ZNU/Testing/0.0+\r\n";
|
||||
url_detail += "&program=fldigi-";
|
||||
url_detail += VERSION;
|
||||
url_detail += "\r\n";
|
||||
|
||||
hostinfo = gethostbyname(HAMCALL_HOST);
|
||||
if(!hostinfo)
|
||||
return 1;
|
||||
if(hostinfo->h_addrtype != AF_INET)
|
||||
return 2;
|
||||
servinfo = getservbyname("http", "tcp");
|
||||
if(!servinfo)
|
||||
return 3;
|
||||
struct timeval timeout = { 10, 0 }; // timeout = 10 seconds
|
||||
ssize_t nread = read_from_server(sockfd, url_detail, htmlpage, &timeout);
|
||||
close(sockfd);
|
||||
if (nread < 0)
|
||||
error_string = strerror(-nread);
|
||||
|
||||
sockfd = socket(AF_INET, SOCK_STREAM, 0);
|
||||
if (sockfd == -1)
|
||||
return 4;
|
||||
|
||||
address.sin_family = AF_INET;
|
||||
address.sin_port = servinfo->s_port;
|
||||
address.sin_addr = *(struct in_addr *)*hostinfo->h_addr_list;
|
||||
|
||||
result = connect(sockfd, (struct sockaddr *)&address, sizeof(address));
|
||||
if(result == -1) {
|
||||
close(sockfd);
|
||||
return 5;
|
||||
}
|
||||
|
||||
len = url_detail.length();
|
||||
result = write(sockfd, url_detail.c_str() , len);
|
||||
if (result != len) {
|
||||
close(sockfd);
|
||||
return 6;
|
||||
}
|
||||
|
||||
FD_ZERO(&readfds);
|
||||
FD_SET(sockfd, &readfds);
|
||||
|
||||
testfds = readfds;
|
||||
timeout.tv_sec = 15; // timeout = 10 seconds
|
||||
timeout.tv_usec = 0;
|
||||
result = select(FD_SETSIZE, &testfds, (fd_set *)0, (fd_set *)0, &timeout);
|
||||
|
||||
if (result == 0) {
|
||||
close(sockfd);
|
||||
return 7;
|
||||
}
|
||||
if (result == -1) {
|
||||
close(sockfd);
|
||||
return 8;
|
||||
}
|
||||
if (FD_ISSET(sockfd, &testfds)) {
|
||||
memset(rbuffer, 0, 32768);
|
||||
result = read(sockfd, rbuffer, sizeof(rbuffer));
|
||||
if (result && result < 32768)
|
||||
htmlpage += rbuffer;
|
||||
}
|
||||
|
||||
close(sockfd);
|
||||
return 0;
|
||||
return nread > 0;
|
||||
}
|
||||
|
||||
void HAMCALLquery()
|
||||
{
|
||||
int err;
|
||||
if ((err = HAMCALLget()) == 0) {
|
||||
parse_html();
|
||||
string htmlpage;
|
||||
|
||||
if (HAMCALLget(htmlpage)) {
|
||||
parse_html(htmlpage);
|
||||
QRZ_disp_result();
|
||||
} else {
|
||||
FL_LOCK();
|
||||
inpNotes->value(error[err]);
|
||||
inpNotes->value(error_string);
|
||||
FL_UNLOCK();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -678,7 +678,7 @@ void SoundPort::initialize(void)
|
|||
throw SndPortException(err);
|
||||
pa_init = true;
|
||||
|
||||
PaDeviceIndex ndev = Pa_GetDeviceCount();
|
||||
PaDeviceIndex ndev;
|
||||
if ((ndev = Pa_GetDeviceCount()) < 0)
|
||||
throw SndPortException(ndev);
|
||||
if (ndev == 0)
|
||||
|
@ -700,10 +700,15 @@ const std::vector<const PaDeviceInfo*>& SoundPort::devices(void)
|
|||
return devs;
|
||||
}
|
||||
|
||||
SoundPort::SoundPort(const char *dev)
|
||||
: device(dev), stream(0), frames_per_buffer(paFramesPerBufferUnspecified),
|
||||
req_sample_rate(0), dev_sample_rate(0), fbuf(0)
|
||||
SoundPort::SoundPort(const char *in_dev, const char *out_dev)
|
||||
: req_sample_rate(0), fbuf(0)
|
||||
{
|
||||
device[STREAM_IN] = in_dev;
|
||||
device[STREAM_OUT] = out_dev;
|
||||
stream[STREAM_IN] = stream[STREAM_OUT] = 0;
|
||||
frames_per_buffer[STREAM_IN] = frames_per_buffer[STREAM_OUT] = paFramesPerBufferUnspecified;
|
||||
dev_sample_rate[STREAM_IN] = dev_sample_rate[STREAM_OUT] = 0;
|
||||
|
||||
try {
|
||||
rx_src_data = new SRC_DATA;
|
||||
tx_src_data = new SRC_DATA;
|
||||
|
@ -741,50 +746,61 @@ int SoundPort::Open(int mode, int freq)
|
|||
|
||||
// Try to keep the stream open if we are using jack, or if we
|
||||
// are in full duplex mode and the sample rate has not changed.
|
||||
if (stream_active()) {
|
||||
if (Pa_GetHostApiInfo((*idev)->hostApi)->type == paJACK) {
|
||||
if (stream_active(STREAM_IN) && stream_active(STREAM_OUT)) {
|
||||
if (Pa_GetHostApiInfo((*idev[STREAM_IN])->hostApi)->type == paJACK ||
|
||||
Pa_GetHostApiInfo((*idev[STREAM_OUT])->hostApi)->type == paJACK) {
|
||||
// If we have a new sample rate, we must reset the src data.
|
||||
if (old_sample_rate != freq)
|
||||
src_data_reset(1 << O_RDONLY | 1 << O_WRONLY);
|
||||
return 0;
|
||||
}
|
||||
else if (full_duplex_device(*idev) && old_sample_rate == freq)
|
||||
else if ( (idev[STREAM_IN] != idev[STREAM_OUT] ||
|
||||
full_duplex_device(*idev[STREAM_IN])) &&
|
||||
old_sample_rate == freq)
|
||||
return 0;
|
||||
}
|
||||
|
||||
Close();
|
||||
init_stream();
|
||||
|
||||
init_stream(STREAM_IN);
|
||||
#ifndef NDEBUG
|
||||
if (dev_sample_rate != req_sample_rate)
|
||||
cerr << "PA_debug: resampling " << dev_sample_rate
|
||||
if (dev_sample_rate[STREAM_IN] != req_sample_rate)
|
||||
cerr << "PA_debug: input: resampling " << dev_sample_rate[STREAM_IN]
|
||||
<< " <-> " << req_sample_rate << endl;
|
||||
#endif
|
||||
|
||||
init_stream(STREAM_OUT);
|
||||
#ifndef NDEBUG
|
||||
if (dev_sample_rate[STREAM_OUT] != req_sample_rate)
|
||||
cerr << "PA_debug: output: resampling " << dev_sample_rate[STREAM_OUT]
|
||||
<< " <-> " << req_sample_rate << endl;
|
||||
#endif
|
||||
|
||||
mode = full_duplex() ? 1 << O_RDONLY | 1 << O_WRONLY : 1 << mode;
|
||||
if (!(mode & 1 << O_WRONLY))
|
||||
stream_params[STREAM_OUT] = NULL;
|
||||
if (!(mode & 1 << O_RDONLY))
|
||||
stream_params[STREAM_IN] = NULL;
|
||||
src_data_reset(mode);
|
||||
|
||||
start_stream();
|
||||
if (mode & 1 << O_RDONLY)
|
||||
start_stream(STREAM_IN);
|
||||
if (mode & 1 << O_WRONLY)
|
||||
start_stream(STREAM_OUT);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void SoundPort::Close(void)
|
||||
{
|
||||
if (!stream_active())
|
||||
return;
|
||||
int err;
|
||||
for (int i = 0; i < 2; i++) {
|
||||
if (!stream_active(i))
|
||||
return;
|
||||
|
||||
int err;
|
||||
if ((err = Pa_StopStream(stream[i])) != paNoError)
|
||||
pa_perror(err, "Pa_StopStream");
|
||||
if ((err = Pa_CloseStream(stream[i])) != paNoError)
|
||||
pa_perror(err, "Pa_CloseStream");
|
||||
|
||||
if ((err = Pa_StopStream(stream)) != paNoError)
|
||||
pa_perror(err, "Pa_StopStream");
|
||||
if ((err = Pa_CloseStream(stream)) != paNoError)
|
||||
pa_perror(err, "Pa_CloseStream");
|
||||
|
||||
stream = 0;
|
||||
stream[i] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
size_t SoundPort::Read(double *buf, size_t count)
|
||||
|
@ -793,13 +809,13 @@ size_t SoundPort::Read(double *buf, size_t count)
|
|||
|
||||
int err;
|
||||
static int retries = 0;
|
||||
if ((err = Pa_ReadStream(stream, fbuf, ncount)) != paNoError) {
|
||||
if ((err = Pa_ReadStream(stream[STREAM_IN], fbuf, ncount)) != paNoError) {
|
||||
pa_perror(err, "Pa_ReadStream");
|
||||
switch (err) {
|
||||
case paInputOverflowed:
|
||||
return adjust_stream() ? Read(buf, count) : 0;
|
||||
return 0;
|
||||
case paUnanticipatedHostError:
|
||||
if (Pa_GetHostApiInfo((*idev)->hostApi)->type == paOSS && retries++ < 8) {
|
||||
if (Pa_GetHostApiInfo((*idev[STREAM_IN])->hostApi)->type == paOSS && retries++ < 8) {
|
||||
cerr << "Retrying read\n";
|
||||
return Read(buf, count);
|
||||
}
|
||||
|
@ -825,7 +841,7 @@ size_t SoundPort::Read(double *buf, size_t count)
|
|||
}
|
||||
|
||||
float *rbuf = fbuf;
|
||||
if (req_sample_rate != dev_sample_rate || progdefaults.RX_corr != 0) {
|
||||
if (req_sample_rate != dev_sample_rate[STREAM_IN] || progdefaults.RX_corr != 0) {
|
||||
resample(1 << O_RDONLY, rbuf, ncount, count);
|
||||
rbuf = rx_src_data->data_out;
|
||||
count = rx_src_data->output_frames_gen;
|
||||
|
@ -846,21 +862,21 @@ size_t SoundPort::Write(double *buf, size_t count)
|
|||
fbuf[2*i] = fbuf[2*i + 1] = buf[i];
|
||||
|
||||
float *wbuf = fbuf;
|
||||
if (req_sample_rate != dev_sample_rate || progdefaults.TX_corr != 0) {
|
||||
if (req_sample_rate != dev_sample_rate[STREAM_OUT] || progdefaults.TX_corr != 0) {
|
||||
resample(1 << O_WRONLY, wbuf, count);
|
||||
wbuf = tx_src_data->data_out;
|
||||
count = tx_src_data->output_frames_gen;
|
||||
}
|
||||
|
||||
int err;
|
||||
static int retries = 0;
|
||||
if ((err = Pa_WriteStream(stream, wbuf, count)) != paNoError) {
|
||||
static unsigned retries = 0;
|
||||
if ((err = Pa_WriteStream(stream[STREAM_OUT], wbuf, count)) != paNoError) {
|
||||
pa_perror(err, "Pa_WriteStream");
|
||||
switch (err) {
|
||||
case paOutputUnderflowed:
|
||||
return adjust_stream() ? Write(buf, count) : 0;
|
||||
return 0;
|
||||
case paUnanticipatedHostError:
|
||||
if (Pa_GetHostApiInfo((*idev)->hostApi)->type == paOSS && retries++ < 8) {
|
||||
if (Pa_GetHostApiInfo((*idev[STREAM_OUT])->hostApi)->type == paOSS && retries++ < 8) {
|
||||
cerr << "Retrying write\n";
|
||||
return Write(buf, count);
|
||||
}
|
||||
|
@ -887,26 +903,28 @@ size_t SoundPort::Write_stereo(double *bufleft, double *bufright, size_t count)
|
|||
}
|
||||
|
||||
float *wbuf = fbuf;
|
||||
if (req_sample_rate != dev_sample_rate || progdefaults.TX_corr != 0) {
|
||||
if (req_sample_rate != dev_sample_rate[STREAM_OUT] || progdefaults.TX_corr != 0) {
|
||||
resample(1 << O_WRONLY, wbuf, count);
|
||||
wbuf = tx_src_data->data_out;
|
||||
count = tx_src_data->output_frames_gen;
|
||||
}
|
||||
|
||||
int err;
|
||||
static int retries = 0;
|
||||
static unsigned retries = 0;
|
||||
if ((err = Pa_WriteStream(stream, wbuf, count)) != paNoError) {
|
||||
pa_perror(err, "Pa_WriteStream");
|
||||
switch (err) {
|
||||
case paOutputUnderflowed:
|
||||
return adjust_stream() ? Write_stereo(bufleft, bufright, count) : 0;
|
||||
return 0;
|
||||
case paUnanticipatedHostError:
|
||||
if (Pa_GetHostApiInfo((*idev)->hostApi)->type == paOSS && retries++ < 8) {
|
||||
if (Pa_GetHostApiInfo((*idev[STREAM_OUT])->hostApi)->type == paOSS && retries++ < 8) {
|
||||
cerr << "Retrying write\n";
|
||||
return Write_stereo(bufleft, bufright, count);
|
||||
}
|
||||
else
|
||||
else {
|
||||
cerr << "Giving up\n";
|
||||
retries = 0;
|
||||
}
|
||||
// fall through
|
||||
default:
|
||||
throw SndPortException(err);
|
||||
|
@ -922,8 +940,10 @@ size_t SoundPort::Write_stereo(double *bufleft, double *bufright, size_t count)
|
|||
bool SoundPort::full_duplex(void)
|
||||
{
|
||||
extern bool pa_allow_full_duplex;
|
||||
return (pa_allow_full_duplex && full_duplex_device(*idev)) ||
|
||||
Pa_GetHostApiInfo((*idev)->hostApi)->type == paJACK;
|
||||
return (pa_allow_full_duplex && full_duplex_device(*idev[STREAM_IN])) ||
|
||||
idev[STREAM_IN] != idev[STREAM_OUT] ||
|
||||
(Pa_GetHostApiInfo((*idev[STREAM_IN])->hostApi)->type == paJACK ||
|
||||
Pa_GetHostApiInfo((*idev[STREAM_OUT])->hostApi)->type == paJACK);
|
||||
}
|
||||
|
||||
void SoundPort::src_data_reset(int mode)
|
||||
|
@ -935,7 +955,7 @@ void SoundPort::src_data_reset(int mode)
|
|||
rx_src_state = src_new(sample_converter, 2, &err);
|
||||
if (!rx_src_state)
|
||||
throw SndException(src_strerror(err));
|
||||
rx_src_data->src_ratio = req_sample_rate / (dev_sample_rate * (1.0 + rxppm / 1e6));
|
||||
rx_src_data->src_ratio = req_sample_rate / (dev_sample_rate[STREAM_IN] * (1.0 + rxppm / 1e6));
|
||||
}
|
||||
if (mode & 1 << O_WRONLY) {
|
||||
if (tx_src_state)
|
||||
|
@ -943,7 +963,7 @@ void SoundPort::src_data_reset(int mode)
|
|||
tx_src_state = src_new(sample_converter, 2, &err);
|
||||
if (!tx_src_state)
|
||||
throw SndException(src_strerror(err));
|
||||
tx_src_data->src_ratio = dev_sample_rate * (1.0 + txppm / 1e6) / req_sample_rate;
|
||||
tx_src_data->src_ratio = dev_sample_rate[STREAM_OUT] * (1.0 + txppm / 1e6) / req_sample_rate;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -955,7 +975,7 @@ void SoundPort::resample(int mode, float *buf, size_t count, size_t max)
|
|||
if (rxppm != progdefaults.RX_corr) {
|
||||
rxppm = progdefaults.RX_corr;
|
||||
rx_src_data->src_ratio = req_sample_rate
|
||||
/ dev_sample_rate
|
||||
/ dev_sample_rate[STREAM_IN]
|
||||
* (1.0 + rxppm / 1e6);
|
||||
src_set_ratio(rx_src_state, rx_src_data->src_ratio);
|
||||
}
|
||||
|
@ -972,7 +992,7 @@ void SoundPort::resample(int mode, float *buf, size_t count, size_t max)
|
|||
else if (mode & 1 << O_WRONLY) {
|
||||
if (txppm != progdefaults.TX_corr) {
|
||||
txppm = progdefaults.TX_corr;
|
||||
tx_src_data->src_ratio = dev_sample_rate
|
||||
tx_src_data->src_ratio = dev_sample_rate[STREAM_OUT]
|
||||
* (1.0 + txppm / 1e6)
|
||||
/ req_sample_rate;
|
||||
src_set_ratio(tx_src_state, tx_src_data->src_ratio);
|
||||
|
@ -989,97 +1009,98 @@ void SoundPort::resample(int mode, float *buf, size_t count, size_t max)
|
|||
}
|
||||
}
|
||||
|
||||
void SoundPort::init_stream(void)
|
||||
void SoundPort::init_stream(unsigned dir)
|
||||
{
|
||||
#ifndef NDEBUG
|
||||
cerr << "PA_debug: looking for \"" << device << "\"\n";
|
||||
cerr << "PA_debug: looking for \"" << device[dir] << "\"\n";
|
||||
#endif
|
||||
for (idev = devs.begin(); idev != devs.end(); ++idev)
|
||||
if (device == (*idev)->name)
|
||||
for (idev[dir] = devs.begin(); idev[dir] != devs.end(); ++idev[dir])
|
||||
if (device[dir] == (*idev[dir])->name)
|
||||
break;
|
||||
if (idev == devs.end()) {
|
||||
cerr << "PA_debug: could not find device \"" << device << "\"\n";
|
||||
idev = devs.begin() + Pa_GetDefaultOutputDevice();
|
||||
if (idev[dir] == devs.end()) {
|
||||
cerr << "PA_debug: could not find device \"" << device[dir] << "\"\n";
|
||||
idev[dir] = devs.begin() + (dir == STREAM_IN ? Pa_GetDefaultInputDevice() : Pa_GetDefaultOutputDevice());
|
||||
}
|
||||
PaDeviceIndex idx = idev - devs.begin();
|
||||
PaDeviceIndex idx = idev[dir] - devs.begin();
|
||||
|
||||
#ifndef NDEBUG
|
||||
cerr << "PA_debug: using device:"
|
||||
cerr << "PA_debug: using " << (dir == STREAM_IN ? "input" : "output") << " device:"
|
||||
<< "\n index: " << idx
|
||||
<< "\n name: " << (*idev)->name
|
||||
<< "\n hostAPI: " << Pa_GetHostApiInfo((*idev)->hostApi)->name
|
||||
<< "\n maxInputChannels: " << (*idev)->maxInputChannels
|
||||
<< "\n maxOutputChannels: " << (*idev)->maxOutputChannels
|
||||
<< "\n defaultLowInputLatency: " << (*idev)->defaultLowInputLatency
|
||||
<< "\n defaultHighInputLatency: " << (*idev)->defaultHighInputLatency
|
||||
<< "\n defaultLowOutputLatency: " << (*idev)->defaultLowOutputLatency
|
||||
<< "\n defaultHighOutputLatency: " << (*idev)->defaultHighOutputLatency
|
||||
<< "\n defaultSampleRate: " << (*idev)->defaultSampleRate
|
||||
<< "\n name: " << (*idev[dir])->name
|
||||
<< "\n hostAPI: " << Pa_GetHostApiInfo((*idev[dir])->hostApi)->name
|
||||
<< "\n maxInputChannels: " << (*idev[dir])->maxInputChannels
|
||||
<< "\n maxOutputChannels: " << (*idev[dir])->maxOutputChannels
|
||||
<< "\n defaultLowInputLatency: " << (*idev[dir])->defaultLowInputLatency
|
||||
<< "\n defaultHighInputLatency: " << (*idev[dir])->defaultHighInputLatency
|
||||
<< "\n defaultLowOutputLatency: " << (*idev[dir])->defaultLowOutputLatency
|
||||
<< "\n defaultHighOutputLatency: " << (*idev[dir])->defaultHighOutputLatency
|
||||
<< "\n defaultSampleRate: " << (*idev[dir])->defaultSampleRate
|
||||
<< boolalpha
|
||||
<< "\n isInputOnlyDevice: " << ((*idev)->maxOutputChannels == 0)
|
||||
<< "\n isOutputOnlyDevice: " << ((*idev)->maxInputChannels == 0)
|
||||
<< "\n isFullDuplexDevice: " << full_duplex_device(*idev)
|
||||
<< "\n isInputOnlyDevice: " << ((*idev[dir])->maxOutputChannels == 0)
|
||||
<< "\n isOutputOnlyDevice: " << ((*idev[dir])->maxInputChannels == 0)
|
||||
<< "\n isFullDuplexDevice: " << full_duplex_device(*idev[dir])
|
||||
<< "\n isSystemDefaultInputDevice: " << (idx == Pa_GetDefaultInputDevice())
|
||||
<< "\n isSystemDefaultOutputDevice: " << (idx == Pa_GetDefaultOutputDevice())
|
||||
<< "\n isHostApiDefaultInputDevice: " << (idx == Pa_GetHostApiInfo((*idev)->hostApi)->defaultInputDevice)
|
||||
<< "\n isHostApiDefaultOutputDevice: " << (idx == Pa_GetHostApiInfo((*idev)->hostApi)->defaultOutputDevice)
|
||||
<< "\n isHostApiDefaultInputDevice: " << (idx == Pa_GetHostApiInfo((*idev[dir])->hostApi)->defaultInputDevice)
|
||||
<< "\n isHostApiDefaultOutputDevice: " << (idx == Pa_GetHostApiInfo((*idev[dir])->hostApi)->defaultOutputDevice)
|
||||
<< "\n\n";
|
||||
#endif
|
||||
|
||||
// we are unlikely to have an output-only device
|
||||
if ((*idev)->maxInputChannels == 0)
|
||||
throw SndPortException(EBUSY);
|
||||
if ((dir == STREAM_IN && (*idev[dir])->maxInputChannels == 0) ||
|
||||
(dir == STREAM_OUT && (*idev[dir])->maxOutputChannels == 0))
|
||||
throw SndException(EBUSY);
|
||||
|
||||
in_params.device = idx;
|
||||
in_params.channelCount = 2;
|
||||
in_params.sampleFormat = paFloat32;
|
||||
in_params.suggestedLatency = (*idev)->defaultHighInputLatency;
|
||||
in_params.hostApiSpecificStreamInfo = NULL;
|
||||
stream_params[STREAM_IN] = &in_params;
|
||||
if (dir == STREAM_IN) {
|
||||
stream_params[STREAM_IN].device = idx;
|
||||
stream_params[STREAM_IN].channelCount = 2;
|
||||
stream_params[STREAM_IN].sampleFormat = paFloat32;
|
||||
stream_params[STREAM_IN].suggestedLatency = (*idev[dir])->defaultHighInputLatency;
|
||||
stream_params[STREAM_IN].hostApiSpecificStreamInfo = NULL;
|
||||
}
|
||||
else {
|
||||
stream_params[STREAM_OUT].device = idx;
|
||||
stream_params[STREAM_OUT].channelCount = 2;
|
||||
stream_params[STREAM_OUT].sampleFormat = paFloat32;
|
||||
stream_params[STREAM_OUT].suggestedLatency = (*idev[dir])->defaultHighOutputLatency;
|
||||
stream_params[STREAM_OUT].hostApiSpecificStreamInfo = NULL;
|
||||
}
|
||||
|
||||
out_params.device = idx;
|
||||
out_params.channelCount = 2;
|
||||
out_params.sampleFormat = paFloat32;
|
||||
out_params.suggestedLatency = (*idev)->defaultHighOutputLatency;
|
||||
out_params.hostApiSpecificStreamInfo = NULL;
|
||||
stream_params[STREAM_OUT] = &out_params;
|
||||
dev_sample_rate[dir] = find_srate(dir);
|
||||
|
||||
dev_sample_rate = find_srate();
|
||||
|
||||
max_frames_per_buffer = ceil2(MIN(SND_BUF_LEN, (unsigned)(SCBLOCKSIZE *
|
||||
dev_sample_rate / req_sample_rate)));
|
||||
#ifndef NDEBUG
|
||||
cerr << "PA_debug: max_frames_per_buffer = " << max_frames_per_buffer << endl;
|
||||
#endif
|
||||
extern int pa_frames_per_buffer;
|
||||
if (pa_frames_per_buffer)
|
||||
frames_per_buffer = pa_frames_per_buffer;
|
||||
frames_per_buffer[dir] = pa_frames_per_buffer;
|
||||
}
|
||||
|
||||
void SoundPort::start_stream(void)
|
||||
void SoundPort::start_stream(unsigned dir)
|
||||
{
|
||||
int err;
|
||||
|
||||
err = Pa_OpenStream(&stream, stream_params[STREAM_IN], stream_params[STREAM_OUT],
|
||||
dev_sample_rate, frames_per_buffer, paNoFlag, NULL, NULL);
|
||||
if (err != paNoError)
|
||||
throw SndPortException(err);
|
||||
if ((err = Pa_StartStream(stream)) != paNoError) {
|
||||
Close();
|
||||
throw SndPortException(err);
|
||||
}
|
||||
if (dir == STREAM_IN)
|
||||
err = Pa_OpenStream(&stream[STREAM_IN], &stream_params[STREAM_IN], NULL,
|
||||
dev_sample_rate[STREAM_IN], frames_per_buffer[STREAM_IN],
|
||||
paNoFlag, NULL, NULL);
|
||||
else
|
||||
err = Pa_OpenStream(&stream[STREAM_OUT], NULL, &stream_params[STREAM_OUT],
|
||||
dev_sample_rate[STREAM_OUT], frames_per_buffer[STREAM_OUT],
|
||||
paNoFlag, NULL, NULL);
|
||||
if (err != paNoError)
|
||||
throw SndPortException(err);
|
||||
|
||||
if ((err = Pa_StartStream(stream[dir])) != paNoError) {
|
||||
Close();
|
||||
throw SndPortException(err);
|
||||
}
|
||||
}
|
||||
|
||||
bool SoundPort::stream_active(void)
|
||||
bool SoundPort::stream_active(unsigned dir)
|
||||
{
|
||||
if (!stream)
|
||||
if (!stream[dir])
|
||||
return false;
|
||||
|
||||
int err;
|
||||
|
||||
if ((err = Pa_IsStreamActive(stream)) < 0)
|
||||
if ((err = Pa_IsStreamActive(stream[dir])) < 0)
|
||||
throw SndPortException(err);
|
||||
|
||||
return err == 1;
|
||||
}
|
||||
|
||||
|
@ -1088,50 +1109,30 @@ bool SoundPort::full_duplex_device(const PaDeviceInfo* dev)
|
|||
return dev->maxInputChannels > 0 && dev->maxOutputChannels > 0;
|
||||
}
|
||||
|
||||
bool SoundPort::adjust_stream(void)
|
||||
{
|
||||
if (frames_per_buffer == max_frames_per_buffer)
|
||||
return false;
|
||||
|
||||
if (frames_per_buffer != paFramesPerBufferUnspecified)
|
||||
frames_per_buffer *= 2;
|
||||
else
|
||||
frames_per_buffer = SCBLOCKSIZE;
|
||||
|
||||
if (!powerof2(frames_per_buffer))
|
||||
frames_per_buffer = ceil2(frames_per_buffer);
|
||||
|
||||
frames_per_buffer = MIN(max_frames_per_buffer, frames_per_buffer);
|
||||
|
||||
cerr << "PA_debug: adjusting frames_per_buffer to "
|
||||
<< frames_per_buffer << endl;
|
||||
Close();
|
||||
start_stream();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Determine the sample rate that we will use. We try the modem's rate
|
||||
// first and fall back to the device's default rate. If there is a user
|
||||
// setting we just return that without making any checks.
|
||||
double SoundPort::find_srate(void)
|
||||
double SoundPort::find_srate(unsigned dir)
|
||||
{
|
||||
switch (progdefaults.sample_rate) {
|
||||
case 0:
|
||||
int sr = (dir == STREAM_IN ? progdefaults.in_sample_rate : progdefaults.out_sample_rate);
|
||||
switch (sr) {
|
||||
case -1: case 0:
|
||||
break;
|
||||
case 1:
|
||||
return (*idev)->defaultSampleRate;
|
||||
return (*idev[dir])->defaultSampleRate;
|
||||
default:
|
||||
return progdefaults.sample_rate;
|
||||
return sr;
|
||||
}
|
||||
|
||||
double srates[] = { req_sample_rate, (*idev)->defaultSampleRate };
|
||||
double srates[] = { req_sample_rate, (*idev[dir])->defaultSampleRate };
|
||||
int err;
|
||||
for (size_t i = 0; i < sizeof(srates)/sizeof(srates[0]); i++) {
|
||||
#ifndef NDEBUG
|
||||
cerr << "PA_debug: trying " << srates[i] << " Hz" << endl;
|
||||
#endif
|
||||
if ((err = Pa_IsFormatSupported(&in_params, &out_params, srates[i])) == paFormatIsSupported)
|
||||
if ((err = Pa_IsFormatSupported((dir == STREAM_IN ? &stream_params[STREAM_IN] : NULL),
|
||||
(dir == STREAM_OUT ? &stream_params[STREAM_OUT] : NULL),
|
||||
srates[i])) == paFormatIsSupported)
|
||||
return srates[i];
|
||||
#ifndef NDEBUG
|
||||
else
|
||||
|
@ -1153,7 +1154,8 @@ void SoundPort::pa_perror(int err, const char* str)
|
|||
|
||||
if (i < 0) { // PA failed without setting its "last host error" info. Sigh...
|
||||
cerr << "Host API error info not available\n";
|
||||
if (Pa_GetHostApiInfo((*idev)->hostApi)->type == paOSS && errno)
|
||||
if ((Pa_GetHostApiInfo((*idev[STREAM_IN])->hostApi)->type == paOSS ||
|
||||
Pa_GetHostApiInfo((*idev[STREAM_OUT])->hostApi)->type == paOSS) && errno)
|
||||
cerr << "Possible OSS error " << errno << ": "
|
||||
<< strerror(errno) << '\n';
|
||||
}
|
||||
|
@ -1169,8 +1171,10 @@ void SoundPort::pa_perror(int err, const char* str)
|
|||
#if USE_PULSEAUDIO
|
||||
|
||||
SoundPulse::SoundPulse(const char *dev)
|
||||
: dev_sample_rate(48000), in_stream(0), out_stream(0), fbuf(0)
|
||||
: fbuf(0)
|
||||
{
|
||||
stream[0] = stream[1] = 0;
|
||||
|
||||
try {
|
||||
rx_src_data = new SRC_DATA;
|
||||
tx_src_data = new SRC_DATA;
|
||||
|
@ -1200,9 +1204,14 @@ SoundPulse::~SoundPulse()
|
|||
int SoundPulse::Open(int mode, int freq)
|
||||
{
|
||||
int old_sample_rate = sample_frequency;
|
||||
sample_frequency = freq;
|
||||
|
||||
if (in_stream && out_stream) {
|
||||
dev_sample_rate[0] = (progdefaults.in_sample_rate > 1 ?
|
||||
progdefaults.in_sample_rate : 48000);
|
||||
dev_sample_rate[1] = (progdefaults.out_sample_rate > 1 ?
|
||||
progdefaults.out_sample_rate : 48000);
|
||||
|
||||
sample_frequency = freq;
|
||||
if (stream[0] && stream[1]) {
|
||||
if (sample_frequency != old_sample_rate) {
|
||||
src_data_reset(1 << O_RDONLY | 1 << O_WRONLY);
|
||||
return 0;
|
||||
|
@ -1210,25 +1219,30 @@ int SoundPulse::Open(int mode, int freq)
|
|||
}
|
||||
else
|
||||
Close();
|
||||
stream_params.format = PA_SAMPLE_FLOAT32LE;
|
||||
stream_params.channels = 2;
|
||||
stream_params.rate = dev_sample_rate;
|
||||
|
||||
const char* server = (progdefaults.PulseServer.length() ?
|
||||
progdefaults.PulseServer.c_str() : NULL);
|
||||
char sname[32];
|
||||
int err;
|
||||
|
||||
stream_params.format = PA_SAMPLE_FLOAT32LE;
|
||||
stream_params.channels = 2;
|
||||
|
||||
stream_params.rate = dev_sample_rate[0];
|
||||
snprintf(sname, sizeof(sname), "capture (%u)", getpid());
|
||||
if (!in_stream) {
|
||||
in_stream = pa_simple_new(NULL, PACKAGE_NAME, PA_STREAM_RECORD, NULL,
|
||||
if (!stream[0]) {
|
||||
stream[0] = pa_simple_new(server, PACKAGE_NAME, PA_STREAM_RECORD, NULL,
|
||||
sname, &stream_params, NULL, NULL, &err);
|
||||
if (!in_stream)
|
||||
if (!stream[0])
|
||||
throw SndPulseException(err);
|
||||
}
|
||||
|
||||
stream_params.rate = dev_sample_rate[1];
|
||||
snprintf(sname, sizeof(sname), "playback (%u)", getpid());
|
||||
if (!out_stream) {
|
||||
out_stream = pa_simple_new(NULL, PACKAGE_NAME, PA_STREAM_PLAYBACK, NULL,
|
||||
if (!stream[1]) {
|
||||
stream[1] = pa_simple_new(server, PACKAGE_NAME, PA_STREAM_PLAYBACK, NULL,
|
||||
sname, &stream_params, NULL, NULL, &err);
|
||||
if (!out_stream)
|
||||
if (!stream[1])
|
||||
throw SndPulseException(err);
|
||||
}
|
||||
|
||||
|
@ -1240,19 +1254,19 @@ int SoundPulse::Open(int mode, int freq)
|
|||
void SoundPulse::Close(void)
|
||||
{
|
||||
int err = PA_OK;
|
||||
if (in_stream) {
|
||||
pa_simple_drain(in_stream, &err);
|
||||
if (stream[0]) {
|
||||
pa_simple_drain(stream[0], &err);
|
||||
if (err != PA_OK)
|
||||
cerr << pa_strerror(err) << '\n';
|
||||
pa_simple_free(in_stream);
|
||||
in_stream = 0;
|
||||
pa_simple_free(stream[0]);
|
||||
stream[0] = 0;
|
||||
}
|
||||
if (out_stream) {
|
||||
pa_simple_drain(out_stream, &err);
|
||||
if (stream[1]) {
|
||||
pa_simple_drain(stream[1], &err);
|
||||
if (err != PA_OK)
|
||||
cerr << pa_strerror(err) << '\n';
|
||||
pa_simple_free(out_stream);
|
||||
out_stream = 0;
|
||||
pa_simple_free(stream[1]);
|
||||
stream[1] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1265,14 +1279,14 @@ size_t SoundPulse::Write(double* buf, size_t count)
|
|||
fbuf[2*i] = fbuf[2*i + 1] = buf[i];
|
||||
|
||||
float *wbuf = fbuf;
|
||||
if (sample_frequency != dev_sample_rate || progdefaults.TX_corr != 0) {
|
||||
if (sample_frequency != dev_sample_rate[1] || progdefaults.TX_corr != 0) {
|
||||
resample(1 << O_WRONLY, wbuf, count);
|
||||
wbuf = tx_src_data->data_out;
|
||||
count = tx_src_data->output_frames_gen;
|
||||
}
|
||||
|
||||
int err;
|
||||
if (pa_simple_write(out_stream, wbuf, count * sizeof(double), &err) == -1)
|
||||
if (pa_simple_write(stream[1], wbuf, count * sizeof(double), &err) == -1)
|
||||
throw SndPulseException(err);
|
||||
|
||||
return count;
|
||||
|
@ -1289,14 +1303,14 @@ size_t SoundPulse::Write_stereo(double* bufleft, double* bufright, size_t count)
|
|||
}
|
||||
|
||||
float *wbuf = fbuf;
|
||||
if (sample_frequency != dev_sample_rate || progdefaults.TX_corr != 0) {
|
||||
if (sample_frequency != dev_sample_rate[1] || progdefaults.TX_corr != 0) {
|
||||
resample(1 << O_WRONLY, wbuf, count);
|
||||
wbuf = tx_src_data->data_out;
|
||||
count = tx_src_data->output_frames_gen;
|
||||
}
|
||||
|
||||
int err;
|
||||
if (pa_simple_write(out_stream, wbuf, count * sizeof(double), &err) == -1)
|
||||
if (pa_simple_write(stream[1], wbuf, count * sizeof(double), &err) == -1)
|
||||
throw SndPulseException(err);
|
||||
|
||||
return count;
|
||||
|
@ -1307,7 +1321,7 @@ size_t SoundPulse::Read(double *buf, size_t count)
|
|||
size_t ncount = (int)MIN(SND_BUF_LEN, floor(count / rx_src_data->src_ratio));
|
||||
|
||||
int err;
|
||||
if (pa_simple_read(in_stream, fbuf, sizeof(double) * ncount, &err) == -1)
|
||||
if (pa_simple_read(stream[0], fbuf, sizeof(double) * ncount, &err) == -1)
|
||||
throw SndPulseException(err);
|
||||
|
||||
if (capture)
|
||||
|
@ -1323,7 +1337,7 @@ size_t SoundPulse::Read(double *buf, size_t count)
|
|||
}
|
||||
|
||||
float *rbuf = fbuf;
|
||||
if (sample_frequency != dev_sample_rate || progdefaults.RX_corr != 0) {
|
||||
if (sample_frequency != dev_sample_rate[0] || progdefaults.RX_corr != 0) {
|
||||
resample(1 << O_RDONLY, rbuf, ncount, count);
|
||||
rbuf = rx_src_data->data_out;
|
||||
count = rx_src_data->output_frames_gen;
|
||||
|
@ -1344,7 +1358,7 @@ void SoundPulse::src_data_reset(int mode)
|
|||
rx_src_state = src_new(sample_converter, stream_params.channels, &err);
|
||||
if (!rx_src_state)
|
||||
throw SndException(src_strerror(err));
|
||||
rx_src_data->src_ratio = sample_frequency / (dev_sample_rate * (1.0 + rxppm / 1e6));
|
||||
rx_src_data->src_ratio = sample_frequency / (dev_sample_rate[0] * (1.0 + rxppm / 1e6));
|
||||
}
|
||||
if (mode & 1 << O_WRONLY) {
|
||||
if (tx_src_state)
|
||||
|
@ -1352,7 +1366,7 @@ void SoundPulse::src_data_reset(int mode)
|
|||
tx_src_state = src_new(sample_converter, stream_params.channels, &err);
|
||||
if (!tx_src_state)
|
||||
throw SndException(src_strerror(err));
|
||||
tx_src_data->src_ratio = dev_sample_rate * (1.0 + txppm / 1e6) / sample_frequency;
|
||||
tx_src_data->src_ratio = dev_sample_rate[1] * (1.0 + txppm / 1e6) / sample_frequency;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1364,7 +1378,7 @@ void SoundPulse::resample(int mode, float *buf, size_t count, size_t max)
|
|||
if (rxppm != progdefaults.RX_corr) {
|
||||
rxppm = progdefaults.RX_corr;
|
||||
rx_src_data->src_ratio = sample_frequency
|
||||
/ dev_sample_rate
|
||||
/ dev_sample_rate[0]
|
||||
* (1.0 + rxppm / 1e6);
|
||||
src_set_ratio(rx_src_state, rx_src_data->src_ratio);
|
||||
}
|
||||
|
@ -1381,7 +1395,7 @@ void SoundPulse::resample(int mode, float *buf, size_t count, size_t max)
|
|||
else if (mode & 1 << O_WRONLY) {
|
||||
if (txppm != progdefaults.TX_corr) {
|
||||
txppm = progdefaults.TX_corr;
|
||||
tx_src_data->src_ratio = dev_sample_rate
|
||||
tx_src_data->src_ratio = dev_sample_rate[1]
|
||||
* (1.0 + txppm / 1e6)
|
||||
/ sample_frequency;
|
||||
src_set_ratio(tx_src_state, tx_src_data->src_ratio);
|
||||
|
@ -1398,5 +1412,45 @@ void SoundPulse::resample(int mode, float *buf, size_t count, size_t max)
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
#endif // USE_PULSEAUDIO
|
||||
|
||||
|
||||
size_t SoundNull::Write(double* buf, size_t count)
|
||||
{
|
||||
if (generate)
|
||||
writeGenerate(buf, count);
|
||||
|
||||
usleep((useconds_t)ceil((1e6 * count) / sample_frequency));
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
size_t SoundNull::Write_stereo(double* bufleft, double* bufright, size_t count)
|
||||
{
|
||||
if (generate)
|
||||
writeGenerate(bufleft, count);
|
||||
|
||||
usleep((useconds_t)ceil((1e6 * count) / sample_frequency));
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
size_t SoundNull::Read(double *buf, size_t count)
|
||||
{
|
||||
memset(buf, 0, count * sizeof(*buf));
|
||||
|
||||
if (capture)
|
||||
writeCapture(buf, count);
|
||||
if (playback) {
|
||||
readPlayback(buf, count);
|
||||
if (progdefaults.EnableMixer) {
|
||||
double vol = valRcvMixer->value();
|
||||
for (size_t i = 0; i < count; i++)
|
||||
buf[i] *= vol;
|
||||
}
|
||||
}
|
||||
|
||||
usleep((useconds_t)ceil((1e6 * count) / sample_frequency));
|
||||
|
||||
return count;
|
||||
}
|
||||
|
|
|
@ -294,8 +294,6 @@ void trx_start_modem(modem *m)
|
|||
trx_state = STATE_NEW_MODEM;
|
||||
}
|
||||
|
||||
string trx_scdev;
|
||||
|
||||
void trx_reset_loop()
|
||||
{
|
||||
if (scard) {
|
||||
|
@ -305,20 +303,23 @@ void trx_reset_loop()
|
|||
|
||||
switch (progdefaults.btnAudioIOis) {
|
||||
#if USE_OSS
|
||||
case 0:
|
||||
scard = new SoundOSS(trx_scdev.c_str());
|
||||
case SND_IDX_OSS:
|
||||
scard = new SoundOSS(scDevice[0].c_str());
|
||||
break;
|
||||
#endif
|
||||
#if USE_PORTAUDIO
|
||||
case 1:
|
||||
scard = new SoundPort(trx_scdev.c_str());
|
||||
case SND_IDX_PORT:
|
||||
scard = new SoundPort(scDevice[0].c_str(), scDevice[1].c_str());
|
||||
break;
|
||||
#endif
|
||||
#if USE_PULSEAUDIO
|
||||
case 2:
|
||||
scard = new SoundPulse(trx_scdev.c_str());
|
||||
case SND_IDX_PULSE:
|
||||
scard = new SoundPulse(scDevice[0].c_str());
|
||||
break;
|
||||
#endif
|
||||
case SND_IDX_NULL:
|
||||
scard = new SoundNull;
|
||||
break;
|
||||
default:
|
||||
abort();
|
||||
}
|
||||
|
@ -326,9 +327,8 @@ void trx_reset_loop()
|
|||
trx_state = STATE_RX;
|
||||
}
|
||||
|
||||
void trx_reset(const char *scdev)
|
||||
void trx_reset(void)
|
||||
{
|
||||
trx_scdev = scdev;
|
||||
trx_state = STATE_RESTART;
|
||||
}
|
||||
|
||||
|
@ -365,7 +365,7 @@ void trx_start_macro_timer()
|
|||
FL_UNLOCK();
|
||||
}
|
||||
|
||||
void trx_start(const char *scdev)
|
||||
void trx_start(void)
|
||||
{
|
||||
if (trxrunning) {
|
||||
std::cout<< "trx already running!\n"; fflush(stdout);
|
||||
|
@ -376,20 +376,23 @@ void trx_start(const char *scdev)
|
|||
|
||||
switch (progdefaults.btnAudioIOis) {
|
||||
#if USE_OSS
|
||||
case 0:
|
||||
scard = new SoundOSS(scdev);
|
||||
case SND_IDX_OSS:
|
||||
scard = new SoundOSS(scDevice[0].c_str());
|
||||
break;
|
||||
#endif
|
||||
#if USE_PORTAUDIO
|
||||
case 1:
|
||||
scard = new SoundPort(scdev);
|
||||
case SND_IDX_PORT:
|
||||
scard = new SoundPort(scDevice[0].c_str(), scDevice[1].c_str());
|
||||
break;
|
||||
#endif
|
||||
#if USE_PULSEAUDIO
|
||||
case 2:
|
||||
scard = new SoundPulse(scdev);
|
||||
case SND_IDX_PULSE:
|
||||
scard = new SoundPulse(scDevice[0].c_str());
|
||||
break;
|
||||
#endif
|
||||
case SND_IDX_NULL:
|
||||
scard = new SoundNull;
|
||||
break;
|
||||
default:
|
||||
abort();
|
||||
}
|
||||
|
|
|
@ -291,7 +291,7 @@ void FTextBase::adjust_colours(void)
|
|||
|
||||
|
||||
Fl_Menu_Item FTextView::view_menu[] = {
|
||||
{ "@-4>> &QRZ this call", 0, 0 },
|
||||
{ "@-4>> &Look up call", 0, 0 },
|
||||
{ "@-9-> &Call", 0, 0 },
|
||||
{ "@-9-> &Name", 0, 0 },
|
||||
{ "@-9-> QT&H", 0, 0 },
|
||||
|
@ -459,8 +459,8 @@ void FTextView::menu_cb(int val)
|
|||
char *s;
|
||||
case RX_MENU_QRZ_THIS:
|
||||
menu_cb(RX_MENU_CALL);
|
||||
extern void QRZquery();
|
||||
QRZquery();
|
||||
extern void CALLSIGNquery();
|
||||
CALLSIGNquery();
|
||||
break;
|
||||
case RX_MENU_CALL:
|
||||
s = get_word(popx, popy);
|
||||
|
|
Ładowanie…
Reference in New Issue