Switch cSoundPA to the C API

pull/2/head
Stelios Bounanos 2008-01-02 20:40:57 +00:00
rodzic d329548782
commit d3849ae877
8 zmienionych plików z 261 dodań i 186 usunięć

Wyświetl plik

@ -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

Wyświetl plik

@ -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:

Wyświetl plik

@ -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])

Wyświetl plik

@ -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@

Wyświetl plik

@ -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;

Wyświetl plik

@ -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

Wyświetl plik

@ -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;

Wyświetl plik

@ -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) {