Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Remove output audio thread #46

Merged
merged 9 commits into from
Dec 16, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ Intel Mac hardware is no longer supported by airspy-fmradion, although the autho

* 20231215-0: Fix the following known bugs and refactor the code to streamline the functioning:
- Bug: a hung process during the startup period before valid audio signals are coming out
- Bug: aisplaying `-nan` in the output level meter in broadcast FM and NBFM
- Bug: displaying `-nan` in the output level meter in broadcast FM and NBFM
- The NaN is presumably generated by volk_32fc_s32f_atan2_32f() in PhaseDiscriminator::process()
- This NaN issue was presumably the root cause of the multipath filter anomaly first fixed in 20231213-1
- Enhancement: streamlining processing flow in the main for loop of `main()`
Expand Down
113 changes: 2 additions & 111 deletions main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -51,54 +51,12 @@
// define this for enabling coefficient monitor functions
// #undef COEFF_MONITOR

// define this for monitoring DataBuffer queue status
// #undef DATABUFFER_QUEUE_MONITOR

#define AIRSPY_FMRADION_VERSION "20231215-0"
#define AIRSPY_FMRADION_VERSION "20231215-direct-out"

// Flag to set graceful termination
// in process_signals()
static std::atomic_bool stop_flag(false);

// Get data from output buffer and write to output stream.
// This code runs in a separate thread.
static void write_output_data(AudioOutput *output, DataBuffer<Sample> *buf) {

#ifdef DATABUFFER_QUEUE_MONITOR
unsigned int max_queue_length = 0;
#endif // DATABUFFER_QUEUE_MONITOR

while (!stop_flag.load()) {

if (buf->pull_end_reached()) {
// Reached end of stream.
break;
}

// Get samples from buffer and write to output.
SampleVector samples = buf->pull();
// The output device might be closed BEFORE
// buf->pull() is executed, so you need to
// check the flag here too
if (!stop_flag.load()) {
output->write(samples);
if (!(*output)) {
fprintf(stderr, "ERROR: AudioOutput: %s\n", output->error().c_str());
// Setting stop_flag to true, suggested by GitHub @montgomeryb
stop_flag.store(true);
}
}

#ifdef DATABUFFER_QUEUE_MONITOR
unsigned int queue_size = (unsigned int)buf->queue_size();
if (queue_size > max_queue_length) {
max_queue_length = queue_size;
fprintf(stderr, "Max queue length: %u\n", max_queue_length);
}
#endif // DATABUFFER_QUEUE_MONITOR
}
}

static void usage() {
fprintf(
stderr,
Expand Down Expand Up @@ -865,12 +823,6 @@ int main(int argc, char **argv) {
FineTuner fm_afc_finetuner((unsigned int)fm_target_rate / fm_afc_hz_step);
float fm_afc_offset_sum = 0.0;

// If buffering enabled, start background output thread.
DataBuffer<Sample> output_buffer;
std::thread output_thread;
// Always use output_thread for smooth output.
output_thread =
std::thread(write_output_data, audio_output.get(), &output_buffer);
float audio_level = 0;
bool got_stereo = false;

Expand All @@ -882,26 +834,11 @@ int main(int argc, char **argv) {

float if_level = 0;

#ifdef DATABUFFER_QUEUE_MONITOR
// unsigned int nchannel = stereo ? 2 : 1;
bool inbuf_length_warning = false;
unsigned int max_source_buffer_length = 0;
#endif // DATABUFFER_QUEUE_MONITOR

///////////////////////////////////////
// NOTE: main processing loop from here
///////////////////////////////////////
for (uint64_t block = 0; !stop_flag.load(); block++) {

#ifdef DATABUFFER_QUEUE_MONITOR
// Check for overflow of source buffer.
if (!inbuf_length_warning && source_buffer.queue_size() > 10 * ifrate) {
fprintf(stderr, "\nWARNING: source buffer queue sizes exceeds 10 (system "
"too slow)\n");
inbuf_length_warning = true;
}
#endif // DATABUFFER_QUEUE_MONITOR

// Pull next block from source buffer.
IQSampleVector iqsamples = source_buffer.pull();

Expand All @@ -920,15 +857,6 @@ int main(int argc, char **argv) {
continue;
}

#ifdef DATABUFFER_QUEUE_MONITOR
unsigned int source_buffer_length = source_buffer.queue_size();
if (source_buffer_length > max_source_buffer_length) {
max_source_buffer_length = source_buffer_length;
fprintf(stderr, "Max source buffer length: %u\n",
max_source_buffer_length);
}
#endif // DATABUFFER_QUEUE_MONITOR

double prev_block_time = block_time;
block_time = Utility::get_time();

Expand Down Expand Up @@ -1040,7 +968,7 @@ int main(int argc, char **argv) {
// set to zero volume if the squelch is closed.
Utility::adjust_gain(audiosamples, if_rms >= squelch_level ? 0.5 : 0.0);
// Write samples to output.
output_buffer.push(std::move(audiosamples));
audio_output->write(std::move(audiosamples));

// Show status messages for each block if not in quiet mode.
bool stereo_change = false;
Expand All @@ -1063,32 +991,13 @@ int main(int argc, char **argv) {
// Show per-block statistics.
// Add 1e-9 to log10() to prevent generating NaN
float audio_level_db = 20 * log10(audio_level + 1e-9) + 3.01;
#ifdef DATABUFFER_QUEUE_MONITOR
uint32_t quelen = (uint32_t)output_buffer.queue_size();
#endif // DATABUFFER_QUEUE_MONITOR

switch (modtype) {
case ModType::FM:
case ModType::NBFM:
#ifdef DATABUFFER_QUEUE_MONITOR
fprintf(stderr,
#ifdef COEFF_MONITOR
// DATABUFFER_QUEUE_MONITOR && COEFF_MONITOR
"blk=%11" PRIu64
":ppm=%+7.3f:IF=%+6.1fdB:AF=%+6.1fdB:qlen=%" PRIu32 "\n",
#else
// DATABUFFER_QUEUE_MONITOR && !(COEFF_MONITOR)
"\rblk=%11" PRIu64
":ppm=%+7.3f:IF=%+6.1fdB:AF=%+6.1fdB:qlen=%" PRIu32,
#endif // COEFF_MONITOR
block, ppm_average.average(), if_level_db, audio_level_db,
quelen);
#else
// !(DATABUFFER_QUEUE_MONITOR) && !(COEFF_MONITOR)
fprintf(stderr,
"\rblk=%11" PRIu64 ":ppm=%+7.3f:IF=%+6.1fdB:AF=%+6.1fdB",
block, ppm_average.average(), if_level_db, audio_level_db);
#endif // DATABUFFER_QUEUE_MONITOR
fflush(stderr);
break;
case ModType::AM:
Expand All @@ -1102,14 +1011,8 @@ int main(int argc, char **argv) {
double if_agc_gain_db =
20 * log10(am.get_if_agc_current_gain() + 1e-9);
fprintf(stderr,
#ifdef DATABUFFER_QUEUE_MONITOR
"\rblk=%11" PRIu64
":IF=%+6.1fdB:AGC=%+6.1fdB:AF=%+6.1fdB:qlen=%" PRIu32,
block, if_level_db, if_agc_gain_db, audio_level_db, quelen);
#else
"\rblk=%11" PRIu64 ":IF=%+6.1fdB:AGC=%+6.1fdB:AF=%+6.1fdB",
block, if_level_db, if_agc_gain_db, audio_level_db);
#endif // DATABUFFER_QUEUE_MONITOR
fflush(stderr);
break;
}
Expand Down Expand Up @@ -1170,20 +1073,8 @@ int main(int argc, char **argv) {
// Exit and cleanup
fprintf(stderr, "\n");

// Terminate background audio output thread first.
output_buffer.push_end();
// Close audio output.
audio_output->output_close();
// Stop output thread
if (output_thread.joinable()) {
// Detach output_thread if joinable
output_thread.detach();
} else {
// If output_thread is not joinable,
// the process will halt
fprintf(stderr, "output_thread is not joinable\n");
fprintf(stderr, "You may need to kill this program with SIGKILL\n");
}
// Terminate receiver thread.
up_srcsdr->stop();

Expand Down
2 changes: 1 addition & 1 deletion sfmbase/AirspySource.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -190,7 +190,7 @@ void AirspySource::get_device_names(std::vector<std::string> &devices) {
<< serials[i];
devices.push_back(devname_ostr.str());
}
#ifdef DEBUG_AIRSPYHFSOURCE
#ifdef DEBUG_AIRSPYSOURCE
std::cerr << "AirspySource::get_device_names: enumerated " << ndev
<< "device(s)" << std::endl;
#endif
Expand Down
8 changes: 4 additions & 4 deletions sfmbase/AmDecode.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -56,9 +56,9 @@ AmDecoder::AmDecoder(IQSampleCoeff &amfilter_coeff, const ModType mode)
// reference
((m_mode == ModType::USB) || (m_mode == ModType::LSB) ||
(m_mode == ModType::CW) || (m_mode == ModType::WSPR))
? 0.32
? 0.24
// default value
: 0.8,
: 0.6,
// rate
((m_mode == ModType::CW) || (m_mode == ModType::WSPR))
? 0.00125
Expand All @@ -72,9 +72,9 @@ AmDecoder::AmDecoder(IQSampleCoeff &amfilter_coeff, const ModType mode)
1000000.0, // max_gain
// rate
((m_mode == ModType::CW) || (m_mode == ModType::WSPR))
? 0.001
? 0.0006
// default value
: 0.0005)
: 0.0003)

// fine tuner for CW pitch shifting (shift up 500Hz)
// sampling rate: 12kHz
Expand Down
1 change: 0 additions & 1 deletion sfmbase/AudioOutput.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -229,7 +229,6 @@ bool PortAudioOutput::write(const SampleVector &samples) {
return true;
} else if (m_paerror == paOutputUnderflowed) {
// This error is benign
// fprintf(stderr, "paOutputUnderflowed\n");
return true;
} else
add_paerror("Pa_WriteStream()");
Expand Down
1 change: 0 additions & 1 deletion sfmbase/FmDecode.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -258,7 +258,6 @@ void FmDecoder::process(const IQSampleVector &samples_in, SampleVector &audio) {
if (!done_ok) {
// Reset the filter coefficients.
m_multipathfilter.initialize_coefficients();
// fprintf(stderr, "Reset Multipath Filter coefficients\n");
// Discard the invalid filter output, and
// use the no-filter input after resetting the filter.
m_samples_in_multipathfiltered = std::move(m_samples_in_after_agc);
Expand Down
5 changes: 3 additions & 2 deletions sfmbase/MultipathFilter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,9 @@ MultipathFilter::MultipathFilter(unsigned int stages)

// Initialize coefficient and state vectors with the size.
,
m_coeff(m_filter_order), m_state(m_filter_order) {
m_coeff(m_filter_order), m_state(m_filter_order),
// Initialize calculation error value.
m_error(0) {

assert(stages > 0);
for (unsigned int i = 0; i < m_filter_order; i++) {
Expand Down Expand Up @@ -116,7 +118,6 @@ inline void MultipathFilter::update_coeff(const IQSample result) {
m_filter_order);
volk_32f_accumulator_s32f(&state_mag_sq_sum, state_mag_sq.data(),
m_filter_order);
// fprintf(stderr, "state_mag_sq_sum = %.9g\n", state_mag_sq_sum);

// Obtain the step size (dymanically computed)
// Add offset to prevent division-by-zero error
Expand Down