kopia lustrzana https://github.com/jamescoxon/dl-fldigi
Switch cSoundPA to the C API
rodzic
d329548782
commit
d3849ae877
|
@ -1,5 +1,7 @@
|
|||
Change Log:
|
||||
|
||||
2.08 1) Use PortAudio's C API; the C++ bindings are no longer required
|
||||
|
||||
2.07 1) bug fix for mode changes via SysV interface (pskmail / flarq)
|
||||
2) bug fix for modem configuration post quick change from status
|
||||
bar using mouse wheel.
|
||||
|
@ -13,7 +15,6 @@ Change Log:
|
|||
display, the mouse wheel (no key modifier) alters the PSK AFC search
|
||||
range, FeldHell filter BW, or CW filter BW
|
||||
6) The Audio menu is now a submenu of Files
|
||||
|
||||
2.05 1) Converted entire make system over to the GNU autoconf / automake
|
||||
format. See the INSTALL file for additional information
|
||||
2) Corrected various minor bugs
|
||||
|
|
5
INSTALL
5
INSTALL
|
@ -9,9 +9,8 @@ To compile fldigi you will need:
|
|||
* the development files for the Boost C++ library.
|
||||
|
||||
You should also install the libraries and headers for PortAudio, the
|
||||
Portable audio I/O library, together with its C++ bindings. It is
|
||||
possible (but not recommended) to compile fldigi without PortAudio;
|
||||
see below.
|
||||
Portable audio I/O library. It is possible, but not recommended, to
|
||||
compile fldigi without PortAudio; see below.
|
||||
|
||||
Additional features are enabled if the corresponding libraries are
|
||||
present on your system:
|
||||
|
|
10
configure.ac
10
configure.ac
|
@ -214,19 +214,19 @@ AC_ARG_WITH([portaudio],
|
|||
esac],
|
||||
[ac_cv_want_portaudio=yes])
|
||||
if test "x$ac_cv_want_portaudio" = "xyes"; then
|
||||
PKG_CHECK_EXISTS(portaudiocpp >= 12, ac_cv_portaudio=yes, ac_cv_portaudio=no)
|
||||
PKG_CHECK_EXISTS(portaudio-2.0 >= 19, ac_cv_portaudio=yes, ac_cv_portaudio=no)
|
||||
if test "x$ac_cv_portaudio" = "xyes"; then
|
||||
AC_DEFINE(USE_PORTAUDIO, 1, [Set to 1 if we are using PortAudio, 0 otherwise])
|
||||
else
|
||||
AC_MSG_NOTICE([portaudio test failed (use --without-portaudio to disable)])
|
||||
fi
|
||||
PKG_CHECK_MODULES(PORTAUDIOCPP, portaudiocpp >= 12)
|
||||
PKG_CHECK_MODULES(PORTAUDIO, portaudio-2.0 >= 19)
|
||||
else
|
||||
AC_DEFINE(USE_PORTAUDIO, 0, [Set to 1 if we are using PortAudio, 0 otherwise])
|
||||
ac_cv_portaudio=no
|
||||
fi
|
||||
AC_SUBST([PORTAUDIOCPP_CFLAGS])
|
||||
AC_SUBST([PORTAUDIOCPP_LIBS])
|
||||
AC_SUBST([PORTAUDIO_CFLAGS])
|
||||
AC_SUBST([PORTAUDIO_LIBS])
|
||||
|
||||
|
||||
###########################
|
||||
|
@ -258,7 +258,7 @@ else
|
|||
else
|
||||
AC_DEFINE(USE_HAMLIB, 1, [Set to 1 if we are using hamlib, 0 otherwise])
|
||||
fi
|
||||
PKG_CHECK_MODULES(HAMLIB, hamlib >= 1.2.4) # for the error message
|
||||
PKG_CHECK_MODULES(HAMLIB, hamlib >= 1.2.0) # for the error message
|
||||
fi
|
||||
fi
|
||||
AC_SUBST([HAMLIB_CFLAGS])
|
||||
|
|
|
@ -5,12 +5,12 @@ bin_PROGRAMS = fldigi
|
|||
|
||||
AM_CPPFLAGS = -I$(srcdir) -I$(srcdir)/include -I$(srcdir)/irrxml @BOOST_CPPFLAGS@
|
||||
|
||||
AM_CXXFLAGS = @FLTK_CFLAGS@ @PORTAUDIOCPP_CFLAGS@ @SNDFILE_CFLAGS@ \
|
||||
AM_CXXFLAGS = @FLTK_CFLAGS@ @PORTAUDIO_CFLAGS@ @SNDFILE_CFLAGS@ \
|
||||
@SAMPLERATE_CFLAGS@ @HAMLIB_CFLAGS@ \
|
||||
-pipe -Wall -O2 -ffast-math -fno-rtti -fexceptions -finline-functions
|
||||
AM_CFLAGS = $(AM_CXXFLAGS)
|
||||
|
||||
LDADD = @BOOST_LDFLAGS@ @FLTK_LIBS@ @PORTAUDIOCPP_LIBS@ @SNDFILE_LIBS@ \
|
||||
LDADD = @BOOST_LDFLAGS@ @FLTK_LIBS@ @PORTAUDIO_LIBS@ @SNDFILE_LIBS@ \
|
||||
@SAMPLERATE_LIBS@ @HAMLIB_LIBS@ @RTLIB@
|
||||
|
||||
|
||||
|
|
|
@ -44,13 +44,14 @@
|
|||
#include <math.h>
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#if USE_SNDFILE
|
||||
#include <sndfile.hh>
|
||||
#endif
|
||||
#include <iostream>
|
||||
|
||||
#if USE_PORTAUDIO
|
||||
#include <portaudiocpp/PortAudioCpp.hxx>
|
||||
#include <portaudio.h>
|
||||
#endif
|
||||
|
||||
#include <samplerate.h>
|
||||
|
@ -63,27 +64,26 @@
|
|||
#define SND_BUF_LEN 65536
|
||||
//#define SRC_BUF_LEN (8*SND_BUF_LEN)
|
||||
|
||||
#define powerof2(n) ((((n) - 1) & (n)) == 0)
|
||||
|
||||
#define msgprefix std::string("Sound error: ")
|
||||
class SndException : public std::exception
|
||||
{
|
||||
public:
|
||||
SndException() { *szError = 0; error = 0; }
|
||||
SndException(int e) {
|
||||
snprintf(szError, sizeof(szError), "Error: %d, %s", e, strerror(e));
|
||||
error = e;
|
||||
}
|
||||
SndException(const char *s) {
|
||||
strncpy(szError, s, sizeof(szError));
|
||||
szError[sizeof(szError) - 1] = '\0';
|
||||
error = 1;
|
||||
}
|
||||
const char *what(void) const throw() { return szError; }
|
||||
|
||||
SndException() : err(0) { }
|
||||
#if USE_PORTAUDIO
|
||||
SndException(int e) : err(e), msg(msgprefix + (e >= 0 ? strerror(e) : Pa_GetErrorText(e))) { }
|
||||
#else
|
||||
SndException(int e) : err(e), msg(msgprefix + strerror(e)) { }
|
||||
#endif
|
||||
SndException(const char *s) : err(1), msg(msgprefix + s) { }
|
||||
~SndException() throw() { }
|
||||
const char *what(void) const throw() { return msg.c_str(); }
|
||||
int error(void) const { return err; }
|
||||
private:
|
||||
char szError[80];
|
||||
int error;
|
||||
int err;
|
||||
std::string msg;
|
||||
};
|
||||
#undef msgprefix
|
||||
|
||||
class cSound {
|
||||
protected:
|
||||
|
@ -182,6 +182,12 @@ private:
|
|||
|
||||
class cSoundPA : public cSound
|
||||
{
|
||||
public:
|
||||
typedef std::vector<const PaDeviceInfo*>::const_iterator device_iterator;
|
||||
static void initialize(void);
|
||||
static void terminate(void);
|
||||
static const std::vector<const PaDeviceInfo*>& devices(void);
|
||||
|
||||
public:
|
||||
cSoundPA(const char *dev);
|
||||
~cSoundPA();
|
||||
|
@ -196,21 +202,31 @@ private:
|
|||
void src_data_reset(int mode);
|
||||
void resample(int mode, float *buf, int count, int max = 0);
|
||||
void init_stream(void);
|
||||
void start_stream(void);
|
||||
bool stream_active(void);
|
||||
bool full_duplex_device(const PaDeviceInfo* dev);
|
||||
void adjust_stream(void);
|
||||
double find_srate(void);
|
||||
static unsigned ceil2(unsigned n);
|
||||
static unsigned floor2(unsigned n);
|
||||
static uint32_t ceil2(uint32_t n);
|
||||
static uint32_t floor2(uint32_t n);
|
||||
|
||||
private:
|
||||
std::string device;
|
||||
|
||||
portaudio::System &sys;
|
||||
portaudio::BlockingStream stream;
|
||||
// portaudio::System &sys;
|
||||
// portaudio::BlockingStream stream;
|
||||
static bool pa_init;
|
||||
PaStream* stream;
|
||||
|
||||
portaudio::System::DeviceIterator idev;
|
||||
portaudio::DirectionSpecificStreamParameters in_params;
|
||||
portaudio::DirectionSpecificStreamParameters out_params;
|
||||
portaudio::StreamParameters stream_params;
|
||||
device_iterator idev;
|
||||
// portaudio::DirectionSpecificStreamParameters in_params;
|
||||
// portaudio::DirectionSpecificStreamParameters out_params;
|
||||
// portaudio::StreamParameters stream_params;
|
||||
static std::vector<const PaDeviceInfo*> devs;
|
||||
PaStreamParameters in_params;
|
||||
PaStreamParameters out_params;
|
||||
enum { STREAM_IN, STREAM_OUT };
|
||||
PaStreamParameters* stream_params[2];
|
||||
|
||||
unsigned frames_per_buffer;
|
||||
unsigned max_frames_per_buffer;
|
||||
|
|
25
src/main.cxx
25
src/main.cxx
|
@ -39,9 +39,7 @@
|
|||
#include <locale.h>
|
||||
|
||||
#include <FL/Fl_Shared_Image.H>
|
||||
#if USE_PORTAUDIO
|
||||
#include <portaudiocpp/PortAudioCpp.hxx>
|
||||
#endif
|
||||
|
||||
#include "main.h"
|
||||
#include "waterfall.h"
|
||||
#include "fft.h"
|
||||
|
@ -201,12 +199,13 @@ int main(int argc, char ** argv)
|
|||
globfree(&gbuf);
|
||||
|
||||
#if USE_PORTAUDIO
|
||||
portaudio::AutoSystem autoSys;
|
||||
portaudio::System &sys = portaudio::System::instance();
|
||||
for (portaudio::System::DeviceIterator idev = sys.devicesBegin();
|
||||
idev != sys.devicesEnd(); ++idev) {
|
||||
cSoundPA::initialize();
|
||||
|
||||
for (cSoundPA::device_iterator idev = cSoundPA::devices().begin();
|
||||
idev != cSoundPA::devices().end(); ++idev) {
|
||||
string s;
|
||||
s.append(idev->hostApi().name()).append("/").append(idev->name());
|
||||
s.append(Pa_GetHostApiInfo((*idev)->hostApi)->name).append("/").append((*idev)->name);
|
||||
|
||||
string::size_type i = s.find('/') + 1;
|
||||
// backslash-escape any slashes in the device name
|
||||
while ((i = s.find('/', i)) != string::npos) {
|
||||
|
@ -215,11 +214,11 @@ int main(int argc, char ** argv)
|
|||
}
|
||||
menuPADev->add(s.c_str());
|
||||
// set the initial value in the configuration structure
|
||||
if (progdefaults.PAdevice == "" && idev == sys.devicesBegin())
|
||||
progdefaults.PAdevice = idev->name();
|
||||
if (progdefaults.PAdevice == "" && idev == cSoundPA::devices().begin())
|
||||
progdefaults.PAdevice = (*idev)->name;
|
||||
}
|
||||
menuPADev->value(progdefaults.PAdevice.c_str());
|
||||
|
||||
|
||||
btnAudioIO[1]->activate();
|
||||
#endif
|
||||
|
||||
|
@ -265,6 +264,7 @@ int main(int argc, char ** argv)
|
|||
for (int i = 0; i < NUM_QRUNNER_THREADS; i++)
|
||||
cbq[i]->detach();
|
||||
|
||||
cSoundPA::terminate();
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -597,8 +597,7 @@ void print_versions(std::ostream& s)
|
|||
#endif
|
||||
|
||||
#if USE_PORTAUDIO
|
||||
s << ' ' << portaudio::System::versionText() << ' '
|
||||
<< portaudio::System::version() << '\n';
|
||||
s << ' ' << Pa_GetVersionText() << ' ' << Pa_GetVersion() << '\n';
|
||||
#endif
|
||||
|
||||
#if USE_SNDFILE
|
||||
|
|
|
@ -41,6 +41,11 @@
|
|||
#endif
|
||||
#define MAX(a,b) (((a) > (b)) ? (a) : (b))
|
||||
|
||||
#ifdef powerof2
|
||||
# undef powerof2
|
||||
#endif
|
||||
#define powerof2(n) ((((n) - 1) & (n)) == 0)
|
||||
|
||||
|
||||
cSound::cSound()
|
||||
: sample_frequency(0), txppm(progdefaults.TX_corr), rxppm(progdefaults.RX_corr),
|
||||
|
@ -649,10 +654,44 @@ int cSoundOSS::write_stereo(double *bufleft, double *bufright, int count)
|
|||
|
||||
#if USE_PORTAUDIO
|
||||
|
||||
bool cSoundPA::pa_init = false;
|
||||
std::vector<const PaDeviceInfo*> cSoundPA::devs;
|
||||
void cSoundPA::initialize(void)
|
||||
{
|
||||
if (pa_init)
|
||||
return;
|
||||
|
||||
int err;
|
||||
|
||||
if ((err = Pa_Initialize()) != paNoError)
|
||||
throw SndException(err);
|
||||
pa_init = true;
|
||||
|
||||
PaDeviceIndex ndev = Pa_GetDeviceCount();
|
||||
if ((ndev = Pa_GetDeviceCount()) < 0)
|
||||
throw SndException(ndev);
|
||||
if (ndev == 0)
|
||||
throw SndException("No available audio devices");
|
||||
|
||||
devs.reserve(ndev);
|
||||
for (PaDeviceIndex i = 0; i < ndev; i++)
|
||||
devs.push_back(Pa_GetDeviceInfo(i));
|
||||
|
||||
}
|
||||
void cSoundPA::terminate(void)
|
||||
{
|
||||
static_cast<void>(Pa_Terminate());
|
||||
pa_init = false;
|
||||
devs.clear();
|
||||
}
|
||||
const std::vector<const PaDeviceInfo*>& cSoundPA::devices(void)
|
||||
{
|
||||
return devs;
|
||||
}
|
||||
|
||||
cSoundPA::cSoundPA(const char *dev)
|
||||
: device(dev), sys(portaudio::System::instance()),
|
||||
frames_per_buffer(paFramesPerBufferUnspecified), req_sample_rate(0),
|
||||
dev_sample_rate(0), fbuf(0)
|
||||
: device(dev), stream(0), frames_per_buffer(paFramesPerBufferUnspecified),
|
||||
req_sample_rate(0), dev_sample_rate(0), fbuf(0)
|
||||
{
|
||||
try {
|
||||
rx_src_data = new SRC_DATA;
|
||||
|
@ -691,95 +730,76 @@ int cSoundPA::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.isOpen()) {
|
||||
if (idev->hostApi().typeId() == paJACK) {
|
||||
if (stream_active()) {
|
||||
if (Pa_GetHostApiInfo((*idev)->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 (idev->isFullDuplexDevice() && old_sample_rate == freq)
|
||||
else if (full_duplex_device(*idev) && old_sample_rate == freq)
|
||||
return 0;
|
||||
}
|
||||
|
||||
Close();
|
||||
try {
|
||||
init_stream();
|
||||
}
|
||||
catch (const exception &e) {
|
||||
cerr << e.what() << endl;
|
||||
// make sure the stream is closed
|
||||
try {
|
||||
stream.close();
|
||||
}
|
||||
catch (...) { }
|
||||
throw SndException(e.what());
|
||||
}
|
||||
init_stream();
|
||||
#ifndef NDEBUG
|
||||
if (dev_sample_rate != req_sample_rate)
|
||||
cerr << "PA_debug: resampling " << dev_sample_rate
|
||||
<< " <-> " << req_sample_rate << endl;
|
||||
#endif
|
||||
|
||||
mode = full_duplex() ? 1 << O_RDONLY | 1 << O_WRONLY : 1 << mode;
|
||||
if (!(mode & 1 << O_WRONLY))
|
||||
stream_params.setOutputParameters(portaudio::DirectionSpecificStreamParameters::null());
|
||||
stream_params[STREAM_OUT] = NULL;
|
||||
if (!(mode & 1 << O_RDONLY))
|
||||
stream_params.setInputParameters(portaudio::DirectionSpecificStreamParameters::null());
|
||||
stream_params[STREAM_IN] = NULL;
|
||||
src_data_reset(mode);
|
||||
|
||||
#ifndef NDEBUG
|
||||
if (dev_sample_rate != req_sample_rate)
|
||||
cerr << "PA_debug: resampling " << dev_sample_rate
|
||||
<< " <-> " << req_sample_rate << endl;
|
||||
#endif
|
||||
start_stream();
|
||||
|
||||
try {
|
||||
stream.open(stream_params);
|
||||
stream.start();
|
||||
}
|
||||
catch (const exception& e) {
|
||||
cerr << e.what() << endl;
|
||||
try {
|
||||
stream.close();
|
||||
}
|
||||
catch (...) { }
|
||||
throw SndException(e.what());
|
||||
}
|
||||
|
||||
return 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void cSoundPA::Close(void)
|
||||
{
|
||||
if (stream.isOpen()) {
|
||||
stream.stop();
|
||||
stream.close();
|
||||
}
|
||||
if (!stream_active())
|
||||
return;
|
||||
|
||||
int err;
|
||||
|
||||
if ((err = Pa_StopStream(stream)) != paNoError)
|
||||
cerr << "Pa_StopStream error: " << Pa_GetErrorText(err) << '\n';
|
||||
if ((err = Pa_CloseStream(stream)) != paNoError)
|
||||
cerr << "Pa_CloseStream error: " << Pa_GetErrorText(err) << '\n';
|
||||
|
||||
stream = 0;
|
||||
}
|
||||
|
||||
int cSoundPA::Read(double *buf, int count)
|
||||
{
|
||||
int ncount = (int)floor(MIN(count, SND_BUF_LEN) / rx_src_data->src_ratio);
|
||||
int err;
|
||||
|
||||
try {
|
||||
stream.read(fbuf, ncount);
|
||||
}
|
||||
catch (const portaudio::PaException &e) {
|
||||
cerr << e.what() << endl;
|
||||
if (strstr(e.what(), "rflow"))
|
||||
if ((err = Pa_ReadStream(stream, fbuf, ncount)) != paNoError) {
|
||||
cerr << "Pa_ReadStream: " << Pa_GetErrorText(err) << '\n';
|
||||
if (err == paInputOverflowed)
|
||||
adjust_stream();
|
||||
else
|
||||
throw SndException(e.what());
|
||||
throw SndException(err);
|
||||
}
|
||||
|
||||
if (capture) writeCapture( buf, count);
|
||||
|
||||
if (capture)
|
||||
writeCapture(buf, count);
|
||||
if (playback) {
|
||||
readPlayback( buf, count);
|
||||
double vol = valRcvMixer->value();
|
||||
for (int i = 0; i < count; i++)
|
||||
buf[i] *= vol;
|
||||
readPlayback(buf, count);
|
||||
double vol = valRcvMixer->value();
|
||||
for (int i = 0; i < count; i++)
|
||||
buf[i] *= vol;
|
||||
return count;
|
||||
}
|
||||
|
||||
float *rbuf = fbuf;
|
||||
|
||||
if (req_sample_rate != dev_sample_rate || progdefaults.RX_corr != 0) {
|
||||
resample(1 << O_RDONLY, rbuf, ncount, count);
|
||||
rbuf = rx_src_data->data_out;
|
||||
|
@ -794,7 +814,8 @@ int cSoundPA::Read(double *buf, int count)
|
|||
|
||||
int cSoundPA::write_samples(double *buf, int count)
|
||||
{
|
||||
if (generate) writeGenerate( buf, count );
|
||||
if (generate)
|
||||
writeGenerate(buf, count);
|
||||
|
||||
for (int i = 0; i < count; i++)
|
||||
fbuf[2*i] = fbuf[2*i + 1] = buf[i];
|
||||
|
@ -805,15 +826,14 @@ int cSoundPA::write_samples(double *buf, int count)
|
|||
wbuf = tx_src_data->data_out;
|
||||
count = tx_src_data->output_frames_gen;
|
||||
}
|
||||
try {
|
||||
stream.write(wbuf, count);
|
||||
}
|
||||
catch (const portaudio::PaException &e) {
|
||||
cerr << e.what() << endl;
|
||||
if (strstr(e.what(), "rflow"))
|
||||
|
||||
int err;
|
||||
if ((err = Pa_WriteStream(stream, wbuf, count)) != paNoError) {
|
||||
cerr << "Pa_WriteStream: " << Pa_GetErrorText(err) << '\n';
|
||||
if (err == paOutputUnderflowed)
|
||||
adjust_stream();
|
||||
else
|
||||
throw SndException(e.what());
|
||||
throw SndException(err);
|
||||
}
|
||||
|
||||
return count;
|
||||
|
@ -821,7 +841,8 @@ int cSoundPA::write_samples(double *buf, int count)
|
|||
|
||||
int cSoundPA::write_stereo(double *bufleft, double *bufright, int count)
|
||||
{
|
||||
if (generate) writeGenerate( bufleft, count );
|
||||
if (generate)
|
||||
writeGenerate(bufleft, count);
|
||||
|
||||
for (int i = 0; i < count; i++) {
|
||||
fbuf[2*i] = bufleft[i];
|
||||
|
@ -834,15 +855,14 @@ int cSoundPA::write_stereo(double *bufleft, double *bufright, int count)
|
|||
wbuf = tx_src_data->data_out;
|
||||
count = tx_src_data->output_frames_gen;
|
||||
}
|
||||
try {
|
||||
stream.write(wbuf, count);
|
||||
}
|
||||
catch (const portaudio::PaException &e) {
|
||||
cerr << e.what() << endl;
|
||||
if (strstr(e.what(), "rflow"))
|
||||
|
||||
int err;
|
||||
if ((err = Pa_WriteStream(stream, wbuf, count)) != paNoError) {
|
||||
cerr << "Pa_WriteStream: " << Pa_GetErrorText(err) << '\n';
|
||||
if (err == paOutputUnderflowed)
|
||||
adjust_stream();
|
||||
else
|
||||
throw SndException(e.what());
|
||||
throw SndException(err);
|
||||
}
|
||||
|
||||
return count;
|
||||
|
@ -851,8 +871,8 @@ int cSoundPA::write_stereo(double *bufleft, double *bufright, int count)
|
|||
bool cSoundPA::full_duplex(void)
|
||||
{
|
||||
extern bool pa_allow_full_duplex;
|
||||
return (pa_allow_full_duplex && idev->isFullDuplexDevice()) ||
|
||||
idev->hostApi().typeId() == paJACK;
|
||||
return (pa_allow_full_duplex && full_duplex_device(*idev)) ||
|
||||
Pa_GetHostApiInfo((*idev)->hostApi)->type == paJACK;
|
||||
}
|
||||
|
||||
void cSoundPA::src_data_reset(int mode)
|
||||
|
@ -882,8 +902,8 @@ void cSoundPA::resample(int mode, float *buf, int count, int max)
|
|||
if (rxppm != progdefaults.RX_corr) {
|
||||
rxppm = progdefaults.RX_corr;
|
||||
rx_src_data->src_ratio = req_sample_rate
|
||||
/ dev_sample_rate
|
||||
* (1.0 + rxppm / 1e6);
|
||||
/ dev_sample_rate
|
||||
* (1.0 + rxppm / 1e6);
|
||||
src_set_ratio(rx_src_state, rx_src_data->src_ratio);
|
||||
}
|
||||
|
||||
|
@ -899,8 +919,8 @@ void cSoundPA::resample(int mode, float *buf, int count, int max)
|
|||
if (txppm != progdefaults.TX_corr) {
|
||||
txppm = progdefaults.TX_corr;
|
||||
tx_src_data->src_ratio = dev_sample_rate
|
||||
* (1.0 + txppm / 1e6)
|
||||
/ req_sample_rate;
|
||||
* (1.0 + txppm / 1e6)
|
||||
/ req_sample_rate;
|
||||
src_set_ratio(tx_src_state, tx_src_data->src_ratio);
|
||||
}
|
||||
|
||||
|
@ -919,65 +939,98 @@ void cSoundPA::init_stream(void)
|
|||
#ifndef NDEBUG
|
||||
cerr << "PA_debug: looking for \"" << device << "\"\n";
|
||||
#endif
|
||||
for (idev = sys.devicesBegin(); idev != sys.devicesEnd(); ++idev)
|
||||
if (device == idev->name())
|
||||
for (idev = devs.begin(); idev != devs.end(); ++idev)
|
||||
if (device == (*idev)->name)
|
||||
break;
|
||||
if (idev == sys.devicesEnd()) {
|
||||
idev = sys.devicesBegin();
|
||||
if (idev == devs.end()) {
|
||||
cerr << "PA_debug: could not find device \"" << device << "\"\n";
|
||||
idev = devs.begin();
|
||||
}
|
||||
PaDeviceIndex idx = idev - devs.begin();
|
||||
|
||||
#ifndef NDEBUG
|
||||
cerr << "PA_debug: using device:"
|
||||
<< "\n index: " << idev->index()
|
||||
<< "\n name: " << idev->name()
|
||||
<< "\n hostAPI: " << 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 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
|
||||
<< boolalpha
|
||||
<< "\n isInputOnlyDevice: " << idev->isInputOnlyDevice()
|
||||
<< "\n isOutputOnlyDevice: " << idev->isOutputOnlyDevice()
|
||||
<< "\n isFullDuplexDevice: " << idev->isFullDuplexDevice()
|
||||
<< "\n isSystemDefaultInputDevice: " << idev->isSystemDefaultInputDevice()
|
||||
<< "\n isSystemDefaultOutputDevice: " << idev->isSystemDefaultOutputDevice()
|
||||
<< "\n isHostApiDefaultInputDevice: " << idev->isHostApiDefaultInputDevice()
|
||||
<< "\n isHostApiDefaultOutputDevice: " << idev->isHostApiDefaultOutputDevice()
|
||||
<< "\n isInputOnlyDevice: " << ((*idev)->maxOutputChannels == 0)
|
||||
<< "\n isOutputOnlyDevice: " << ((*idev)->maxInputChannels == 0)
|
||||
<< "\n isFullDuplexDevice: " << full_duplex_device(*idev)
|
||||
<< "\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\n";
|
||||
#endif
|
||||
|
||||
in_params.setDevice(*idev);
|
||||
in_params.setNumChannels(2);
|
||||
in_params.setSampleFormat(portaudio::FLOAT32, true);
|
||||
in_params.setSuggestedLatency(idev->defaultHighInputLatency());
|
||||
in_params.setHostApiSpecificStreamInfo(NULL);
|
||||
// we are unlikely to have an output-only device
|
||||
if ((*idev)->maxInputChannels == 0)
|
||||
throw SndException(EBUSY);
|
||||
|
||||
out_params.setDevice(*idev);
|
||||
out_params.setNumChannels(2);
|
||||
out_params.setSampleFormat(portaudio::FLOAT32, true);
|
||||
out_params.setSuggestedLatency(idev->defaultHighOutputLatency());
|
||||
out_params.setHostApiSpecificStreamInfo(NULL);
|
||||
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;
|
||||
|
||||
stream_params.clearFlags();
|
||||
stream_params.setInputParameters(in_params);
|
||||
stream_params.setOutputParameters(out_params);
|
||||
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 = find_srate();
|
||||
stream_params.setSampleRate(dev_sample_rate);
|
||||
|
||||
max_frames_per_buffer = ceil2(MIN(SND_BUF_LEN, (unsigned)(SCBLOCKSIZE *
|
||||
dev_sample_rate / req_sample_rate)));
|
||||
extern int pa_frames_per_buffer;
|
||||
if (pa_frames_per_buffer)
|
||||
frames_per_buffer = pa_frames_per_buffer;
|
||||
|
||||
stream_params.setFramesPerBuffer(frames_per_buffer);
|
||||
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;
|
||||
}
|
||||
|
||||
void cSoundPA::start_stream(void)
|
||||
{
|
||||
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 SndException(err);
|
||||
if ((err = Pa_StartStream(stream)) != paNoError) {
|
||||
Close();
|
||||
throw SndException(err);
|
||||
}
|
||||
}
|
||||
|
||||
bool cSoundPA::stream_active(void)
|
||||
{
|
||||
if (!stream)
|
||||
return false;
|
||||
|
||||
int err;
|
||||
|
||||
if ((err = Pa_IsStreamActive(stream)) < 0)
|
||||
throw SndException(err);
|
||||
|
||||
return err == 1;
|
||||
}
|
||||
|
||||
bool cSoundPA::full_duplex_device(const PaDeviceInfo* dev)
|
||||
{
|
||||
return dev->maxInputChannels > 0 && dev->maxOutputChannels > 0;
|
||||
}
|
||||
|
||||
void cSoundPA::adjust_stream(void)
|
||||
|
@ -985,23 +1038,20 @@ void cSoundPA::adjust_stream(void)
|
|||
if (frames_per_buffer == max_frames_per_buffer)
|
||||
return;
|
||||
|
||||
frames_per_buffer = stream_params.framesPerBuffer();
|
||||
if (frames_per_buffer)
|
||||
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 = 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;
|
||||
stream_params.setFramesPerBuffer(frames_per_buffer);
|
||||
Close();
|
||||
stream.open(stream_params);
|
||||
stream.start();
|
||||
start_stream();
|
||||
}
|
||||
|
||||
// Determine the sample rate that we will use. We try the modem's rate
|
||||
|
@ -1012,23 +1062,26 @@ double cSoundPA::find_srate(void)
|
|||
if (progdefaults.sample_rate)
|
||||
return progdefaults.sample_rate;
|
||||
|
||||
double srates[] = { req_sample_rate, idev->defaultSampleRate() };
|
||||
for (unsigned i = 0; i < sizeof(srates)/sizeof(srates[0]); i++) {
|
||||
portaudio::StreamParameters sp(in_params, out_params,
|
||||
srates[i], 0, paNoFlag);
|
||||
double srates[] = { req_sample_rate, (*idev)->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 (sp.isSupported())
|
||||
return sp.sampleRate();
|
||||
if ((err = Pa_IsFormatSupported(&in_params, &out_params, srates[i])) == paFormatIsSupported)
|
||||
return srates[i];
|
||||
#ifndef NDEBUG
|
||||
else
|
||||
cerr << "Pa_IsFormatSupported: " << Pa_GetErrorText(err) << '\n';
|
||||
#endif
|
||||
}
|
||||
|
||||
throw SndException("No supported sample rate found. Sound device busy?");
|
||||
throw SndException(err);
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Return smallest power of 2 greater than n
|
||||
unsigned cSoundPA::ceil2(unsigned n)
|
||||
uint32_t cSoundPA::ceil2(uint32_t n)
|
||||
{
|
||||
--n;
|
||||
n |= n >> 1;
|
||||
|
@ -1040,8 +1093,8 @@ unsigned cSoundPA::ceil2(unsigned n)
|
|||
return n + 1;
|
||||
}
|
||||
|
||||
// Return the biggest power of 2 less than n
|
||||
unsigned cSoundPA::floor2(unsigned n)
|
||||
// Return biggest power of 2 less than n
|
||||
uint32_t cSoundPA::floor2(uint32_t n)
|
||||
{
|
||||
n |= n >> 1;
|
||||
n |= n >> 2;
|
||||
|
|
|
@ -92,10 +92,17 @@ void trx_trx_receive_loop()
|
|||
scard->Open(O_RDONLY, active_modem->get_samplerate());
|
||||
}
|
||||
catch (const SndException& e) {
|
||||
put_status(e.what(), 1);
|
||||
MilliSleep(10);
|
||||
put_status(e.what());
|
||||
#if USE_PORTAUDIO
|
||||
if (e.error() == EBUSY) {
|
||||
cSoundPA::terminate();
|
||||
cSoundPA::initialize();
|
||||
}
|
||||
#endif
|
||||
MilliSleep(1000);
|
||||
return;
|
||||
}
|
||||
put_status("");
|
||||
active_modem->rx_init();
|
||||
|
||||
while (1) {
|
||||
|
|
Ładowanie…
Reference in New Issue