From 55b190ef4980dc3c9aec1a5bbc579c1d21b8eba3 Mon Sep 17 00:00:00 2001 From: jpwichern Date: Thu, 5 Jun 2014 19:00:39 +0200 Subject: [PATCH 1/3] Update dsd.h, dsd_audio.c, dsd_main.c and dsd_symbol.c Adding portaudio support. Implementing it as a new audio_..._type in dsd.h. Update CMakeLists.txt and create FindLibPortAudio.cmake CMake portaudio check --- CMakeLists.txt | 7 ++ cmake/FindLibPortAudio.cmake | 108 +++++++++++++++++++++ dsd.h | 19 +++- dsd_audio.c | 175 +++++++++++++++++++++++++++++++++-- dsd_main.c | 68 +++++++++++++- dsd_symbol.c | 18 +++- 6 files changed, 381 insertions(+), 14 deletions(-) create mode 100644 cmake/FindLibPortAudio.cmake diff --git a/CMakeLists.txt b/CMakeLists.txt index 300ee00..72833c6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -9,6 +9,7 @@ git_describe(GIT_TAG) find_package(LibSndFile REQUIRED) find_package(LibMbe REQUIRED) find_package(ITPP REQUIRED) +find_package(LibPortAudio) FILE(GLOB SRCS *.c *.cpp) @@ -24,6 +25,12 @@ INCLUDE_DIRECTORIES( SET(LIBS ${LIBS} ${LIBMBE_LIBRARY} ${LIBSNDFILE_LIBRARY} ${ITPP_LIBRARY}) +if(PORTAUDIO_FOUND) +SET(INCLUDE_DIRECTORIES ${INCLUDE_DIRECTORIES} "${PORTAUDIO_INCLUDE_DIRS}") +SET(LIBS ${LIBS} ${PORTAUDIO_LIBRARIES}) +add_definitions(-DUSE_PORTAUDIO) +endif(PORTAUDIO_FOUND) + ADD_EXECUTABLE(dsd ${SRCS}) TARGET_LINK_LIBRARIES(dsd ${LIBS}) diff --git a/cmake/FindLibPortAudio.cmake b/cmake/FindLibPortAudio.cmake new file mode 100644 index 0000000..11e4055 --- /dev/null +++ b/cmake/FindLibPortAudio.cmake @@ -0,0 +1,108 @@ +# - Try to find Portaudio +# Once done this will define +# +# PORTAUDIO_FOUND - system has Portaudio +# PORTAUDIO_INCLUDE_DIRS - the Portaudio include directory +# PORTAUDIO_LIBRARIES - Link these to use Portaudio +# PORTAUDIO_DEFINITIONS - Compiler switches required for using Portaudio +# PORTAUDIO_VERSION - Portaudio version +# +# Copyright (c) 2006 Andreas Schneider +# +# Redistribution and use is allowed according to the terms of the New BSD license. +# For details see the accompanying COPYING-CMAKE-SCRIPTS file. +# + + +if (PORTAUDIO_LIBRARIES AND PORTAUDIO_INCLUDE_DIRS) + # in cache already + set(PORTAUDIO_FOUND TRUE) +else (PORTAUDIO_LIBRARIES AND PORTAUDIO_INCLUDE_DIRS) + if (NOT WIN32) + include(FindPkgConfig) + pkg_check_modules(PORTAUDIO2 portaudio-2.0) + endif (NOT WIN32) + + if (PORTAUDIO2_FOUND) + set(PORTAUDIO_INCLUDE_DIRS + ${PORTAUDIO2_INCLUDE_DIRS} + ) + if (${CMAKE_SYSTEM_NAME} MATCHES "Darwin") + set(PORTAUDIO_LIBRARIES "${PORTAUDIO2_LIBRARY_DIRS}/lib${PORTAUDIO2_LIBRARIES}.dylib") + else (${CMAKE_SYSTEM_NAME} MATCHES "Darwin") + set(PORTAUDIO_LIBRARIES + ${PORTAUDIO2_LIBRARIES} + ) + endif (${CMAKE_SYSTEM_NAME} MATCHES "Darwin") + set(PORTAUDIO_VERSION + 19 + ) + set(PORTAUDIO_FOUND TRUE) + else (PORTAUDIO2_FOUND) + find_path(PORTAUDIO_INCLUDE_DIR + NAMES + portaudio.h + PATHS + /usr/include + /usr/local/include + /opt/local/include + /sw/include + ) + + find_library(PORTAUDIO_LIBRARY + NAMES + portaudio + PATHS + /usr/lib + /usr/local/lib + /opt/local/lib + /sw/lib + ) + + find_path(PORTAUDIO_LIBRARY_DIR + NAMES + portaudio + PATHS + /usr/lib + /usr/local/lib + /opt/local/lib + /sw/lib + ) + + set(PORTAUDIO_INCLUDE_DIRS + ${PORTAUDIO_INCLUDE_DIR} + ) + set(PORTAUDIO_LIBRARIES + ${PORTAUDIO_LIBRARY} + ) + + set(PORTAUDIO_LIBRARY_DIRS + ${PORTAUDIO_LIBRARY_DIR} + ) + + set(PORTAUDIO_VERSION + 18 + ) + + if (PORTAUDIO_INCLUDE_DIRS AND PORTAUDIO_LIBRARIES) + set(PORTAUDIO_FOUND TRUE) + endif (PORTAUDIO_INCLUDE_DIRS AND PORTAUDIO_LIBRARIES) + + if (PORTAUDIO_FOUND) + if (NOT Portaudio_FIND_QUIETLY) + message(STATUS "Found Portaudio: ${PORTAUDIO_LIBRARIES}") + endif (NOT Portaudio_FIND_QUIETLY) + else (PORTAUDIO_FOUND) + if (Portaudio_FIND_REQUIRED) + message(FATAL_ERROR "Could not find Portaudio") + else (Portaudio_FIND_REQUIRED) + message(STATUS "Could NOT find Portaudio") + endif (Portaudio_FIND_REQUIRED) + endif (PORTAUDIO_FOUND) + endif (PORTAUDIO2_FOUND) + + + # show the PORTAUDIO_INCLUDE_DIRS and PORTAUDIO_LIBRARIES variables only in the advanced view + mark_as_advanced(PORTAUDIO_INCLUDE_DIRS PORTAUDIO_LIBRARIES) + +endif (PORTAUDIO_LIBRARIES AND PORTAUDIO_INCLUDE_DIRS) diff --git a/dsd.h b/dsd.h index ebe0ac4..831bc71 100644 --- a/dsd.h +++ b/dsd.h @@ -42,6 +42,15 @@ #include "p25p1_heuristics.h" + +#define SAMPLE_RATE_IN 48000 +#define SAMPLE_RATE_OUT 8000 +#define PA_FRAMES_PER_BUFFER 64 + +#ifdef USE_PORTAUDIO +#include "portaudio.h" +#endif + /* * global variables */ @@ -66,12 +75,18 @@ typedef struct int audio_in_fd; SNDFILE *audio_in_file; SF_INFO *audio_in_file_info; - int audio_in_type; // 0 for device, 1 for file +#ifdef USE_PORTAUDIO + PaStream* audio_in_pa_stream; +#endif + int audio_in_type; // 0 for device, 1 for file, 2 for portaudio char audio_out_dev[1024]; int audio_out_fd; SNDFILE *audio_out_file; SF_INFO *audio_out_file_info; - int audio_out_type; // 0 for device, 1 for file +#ifdef USE_PORTAUDIO + PaStream* audio_out_pa_stream; +#endif + int audio_out_type; // 0 for device, 1 for file, 2 for portaudio int split; int playoffset; char mbe_out_dir[1024]; diff --git a/dsd_audio.c b/dsd_audio.c index 3a1e335..17544aa 100644 --- a/dsd_audio.c +++ b/dsd_audio.c @@ -17,6 +17,7 @@ #include "dsd.h" + void processAudio (dsd_opts * opts, dsd_state * state) { @@ -213,16 +214,65 @@ writeSynthesizedVoice (dsd_opts * opts, dsd_state * state) */ } + void playSynthesizedVoice (dsd_opts * opts, dsd_state * state) { - ssize_t result; if (state->audio_out_idx > opts->delay) { // output synthesized speech to sound card - result = write (opts->audio_out_fd, (state->audio_out_buf_p - state->audio_out_idx), (state->audio_out_idx * 2)); + if(opts->audio_out_type == 2) + { +#ifdef USE_PORTAUDIO + PaError err = paNoError; + do + { + long available = Pa_GetStreamWriteAvailable( opts->audio_out_pa_stream ); + if(available < 0) + err = available; + //printf("Frames available: %d\n", available); + if( err != paNoError ) + break; + if(available > SAMPLE_RATE_OUT / 2) + { + printf("\nSyncing voice output stream\n"); + err = Pa_StopStream( opts->audio_out_pa_stream ); + if( err != paNoError ) + break; + } + + err = Pa_IsStreamActive( opts->audio_out_pa_stream ); + if(err == 0) + { + printf("Start voice output stream\n"); + err = Pa_StartStream( opts->audio_out_pa_stream ); + } + else if(err == 1) + { + err = paNoError; + } + if( err != paNoError ) + break; + + //printf("write stream %d\n", state->audio_out_idx); + err = Pa_WriteStream( opts->audio_out_pa_stream, (state->audio_out_buf_p - state->audio_out_idx), state->audio_out_idx ); + if( err != paNoError ) + break; + } while(0); + + if( err != paNoError ) + { + fprintf( stderr, "An error occured while using the portaudio output stream\n" ); + fprintf( stderr, "Error number: %d\n", err ); + fprintf( stderr, "Error message: %s\n", Pa_GetErrorText( err ) ); + } + +#endif + } + else + result = write (opts->audio_out_fd, (state->audio_out_buf_p - state->audio_out_idx), (state->audio_out_idx * 2)); state->audio_out_idx = 0; } @@ -236,11 +286,99 @@ playSynthesizedVoice (dsd_opts * opts, dsd_state * state) } } +#ifdef USE_PORTAUDIO +int getPADevice(char* dev, int input, PaStream** stream) +{ + int devnum = atoi(dev + 3); + printf("Using portaudio device %d.\n", devnum); + + PaError err; + + int numDevices = Pa_GetDeviceCount(); + if( numDevices < 0 ) + { + printf( "ERROR: Pa_GetDeviceCount returned 0x%x\n", numDevices ); + err = numDevices; + goto error; + } + if( devnum >= numDevices) + { + printf( "ERROR: Requested device %d is larger than number of devices.\n", devnum ); + return(1); + } + + const PaDeviceInfo *deviceInfo = Pa_GetDeviceInfo( devnum ); + + /* print device name */ +#ifdef WIN32 + { /* Use wide char on windows, so we can show UTF-8 encoded device names */ + wchar_t wideName[MAX_PATH]; + MultiByteToWideChar(CP_UTF8, 0, deviceInfo->name, -1, wideName, MAX_PATH-1); + wprintf( L"Name = %s\n", wideName ); + } +#else + printf( "Name = %s\n", deviceInfo->name ); +#endif + if((input == 1) && (deviceInfo->maxInputChannels == 0)) + { + printf( "ERROR: Requested device %d is not an input device.\n", devnum ); + return(1); + } + if((input == 0) && (deviceInfo->maxOutputChannels == 0)) + { + printf( "ERROR: Requested device %d is not an output device.\n", devnum ); + return(1); + } + + //Create stream parameters + PaStreamParameters parameters; + parameters.device = devnum; + parameters.channelCount = 1; /* mono */ + parameters.sampleFormat = paInt16; //Shorts + parameters.suggestedLatency = 0.500; //0.050; // Pa_GetDeviceInfo( outputParameters.device )->defaultLowOutputLatency; + parameters.hostApiSpecificStreamInfo = NULL; + + //Open stream + err = Pa_OpenStream( + stream, + (input == 1) ? ¶meters : NULL, + (input == 0) ? ¶meters : NULL, + (input == 1) ? SAMPLE_RATE_IN : SAMPLE_RATE_OUT, + PA_FRAMES_PER_BUFFER, + paClipOff, + NULL /*callback*/, + NULL ); + if( err != paNoError ) goto error; + + return 0; + +error: + fprintf( stderr, "An error occured while initializing a portaudio stream\n" ); + fprintf( stderr, "Error number: %d\n", err ); + fprintf( stderr, "Error message: %s\n", Pa_GetErrorText( err ) ); + return err; +} +#endif + void openAudioOutDevice (dsd_opts * opts, int speed) { // get info of device/file - struct stat stat_buf; + if(strncmp(opts->audio_in_dev, "pa:", 3) == 0) + { + opts->audio_out_type = 2; +#ifdef USE_PORTAUDIO + int err = getPADevice(opts->audio_out_dev, 0, &opts->audio_out_pa_stream); + if(err != 0) + exit(err); +#else + printf("Error, Portaudio support not compiled.\n"); + exit(1); +#endif + } + else + { + struct stat stat_buf; if(stat(opts->audio_out_dev, &stat_buf) != 0) { printf("Error, couldn't open %s\n", opts->audio_out_dev); @@ -316,6 +454,7 @@ openAudioOutDevice (dsd_opts * opts, int speed) } #endif + } printf ("Audio Out Device: %s\n", opts->audio_out_dev); } @@ -323,6 +462,28 @@ void openAudioInDevice (dsd_opts * opts) { // get info of device/file + if(strncmp(opts->audio_in_dev, "pa:", 2) == 0) + { + opts->audio_in_type = 2; +#ifdef USE_PORTAUDIO + int err = getPADevice(opts->audio_in_dev, 1, &opts->audio_in_pa_stream); + if(err != 0) + exit(err); + + if (opts->split == 0) + { + int err = getPADevice(opts->audio_in_dev, 0, &opts->audio_out_pa_stream); + if(err != 0) + exit(err); + } + +#else + printf("Error, Portaudio support not compiled.\n"); + exit(1); +#endif + } + else + { struct stat stat_buf; if (stat(opts->audio_in_dev, &stat_buf) != 0) { @@ -370,8 +531,8 @@ openAudioInDevice (dsd_opts * opts) // get current ioctl (opts->audio_in_fd, AUDIO_GETINFO, &aset); - aset.record.sample_rate = 48000; - aset.play.sample_rate = 48000; + aset.record.sample_rate = SAMPLE_RATE_IN; + aset.play.sample_rate = SAMPLE_RATE_IN; aset.record.channels = 1; aset.play.channels = 1; aset.record.precision = 16; @@ -411,7 +572,7 @@ openAudioInDevice (dsd_opts * opts) { printf ("ioctl reset error \n"); } - fmt = 48000; + fmt = SAMPLE_RATE_IN; if (ioctl (opts->audio_in_fd, SNDCTL_DSP_SPEED, &fmt) < 0) { printf ("ioctl speed error \n"); @@ -428,6 +589,8 @@ openAudioInDevice (dsd_opts * opts) } #endif } + } + if (opts->split == 1) { printf ("Audio In Device: %s\n", opts->audio_in_dev); diff --git a/dsd_main.c b/dsd_main.c index 730f34f..9cad25f 100644 --- a/dsd_main.c +++ b/dsd_main.c @@ -27,6 +27,7 @@ #include "git_ver.h" #include "p25p1_heuristics.h" + int comp (const void *a, const void *b) { @@ -305,7 +306,20 @@ usage () void liveScanner (dsd_opts * opts, dsd_state * state) { - while (1) +#ifdef USE_PORTAUDIO + if(opts->audio_in_type == 2) + { + PaError err = Pa_StartStream( opts->audio_in_pa_stream ); + if( err != paNoError ) + { + fprintf( stderr, "An error occured while starting the portaudio input stream\n" ); + fprintf( stderr, "Error number: %d\n", err ); + fprintf( stderr, "Error message: %s\n", Pa_GetErrorText( err ) ); + return; + } + } +#endif + while (1) { noCarrier (opts, state); state->synctype = getFrameSync (opts, state); @@ -344,6 +358,37 @@ cleanupAndExit (dsd_opts * opts, dsd_state * state) closeWavOutFile (opts, state); } +#ifdef USE_PORTAUDIO + if((opts->audio_in_type == 2) || (opts->audio_out_type == 2)) + { + printf("Terminating portaudio.\n"); + PaError err = paNoError; + if(opts->audio_in_pa_stream != NULL) + err = Pa_CloseStream( opts->audio_in_pa_stream ); + if( err != paNoError ) + { + fprintf( stderr, "An error occured while closing the portaudio input stream\n" ); + fprintf( stderr, "Error number: %d\n", err ); + fprintf( stderr, "Error message: %s\n", Pa_GetErrorText( err ) ); + } + if(opts->audio_out_pa_stream != NULL) + err = Pa_CloseStream( opts->audio_out_pa_stream ); + if( err != paNoError ) + { + fprintf( stderr, "An error occured while closing the portaudio output stream\n" ); + fprintf( stderr, "Error number: %d\n", err ); + fprintf( stderr, "Error message: %s\n", Pa_GetErrorText( err ) ); + } + err = Pa_Terminate(); + if( err != paNoError ) + { + fprintf( stderr, "An error occured while terminating portaudio\n" ); + fprintf( stderr, "Error number: %d\n", err ); + fprintf( stderr, "Error message: %s\n", Pa_GetErrorText( err ) ); + } + } +#endif + printf("\n"); printf("Total audio errors: %i\n", state->debug_audio_errors); printf("Total header errors: %i\n", state->debug_header_errors); @@ -735,6 +780,23 @@ main (int argc, char **argv) openSerial (&opts, &state); } + +#ifdef USE_PORTAUDIO + if((strncmp(opts.audio_in_dev, "pa:", 3) == 0) + || (strncmp(opts.audio_out_dev, "pa:", 3) == 0)) + { + printf("Initializing portaudio.\n"); + PaError err = Pa_Initialize(); + if( err != paNoError ) + { + fprintf( stderr, "An error occured while initializing portaudio\n" ); + fprintf( stderr, "Error number: %d\n", err ); + fprintf( stderr, "Error message: %s\n", Pa_GetErrorText( err ) ); + exit(err); + } + } +#endif + if (opts.playfiles == 1) { opts.split = 1; @@ -746,7 +808,7 @@ main (int argc, char **argv) } else { - openAudioOutDevice (&opts, 8000); + openAudioOutDevice (&opts, SAMPLE_RATE_OUT); } } else if (strcmp (opts.audio_in_dev, opts.audio_out_dev) != 0) @@ -760,7 +822,7 @@ main (int argc, char **argv) } else { - openAudioOutDevice (&opts, 8000); + openAudioOutDevice (&opts, SAMPLE_RATE_OUT); } openAudioInDevice (&opts); } diff --git a/dsd_symbol.c b/dsd_symbol.c index be830bb..b7fac9f 100644 --- a/dsd_symbol.c +++ b/dsd_symbol.c @@ -82,12 +82,24 @@ getSymbol (dsd_opts * opts, dsd_state * state, int have_sync) if(opts->audio_in_type == 0) { result = read (opts->audio_in_fd, &sample, 2); } - else { + else if (opts->audio_in_type == 1) { result = sf_read_short(opts->audio_in_file, &sample, 1); if(result == 0) { cleanupAndExit (opts, state); } } + else + { +#ifdef USE_PORTAUDIO + PaError err = Pa_ReadStream( opts->audio_in_pa_stream, &sample, 1 ); + if( err != paNoError ) + { + fprintf( stderr, "An error occured while using the portaudio input stream\n" ); + fprintf( stderr, "Error number: %d\n", err ); + fprintf( stderr, "Error message: %s\n", Pa_GetErrorText( err ) ); + } +#endif + } #ifdef TRACE_DSD state->debug_sample_index++; @@ -267,8 +279,8 @@ getSymbol (dsd_opts * opts, dsd_state * state, int have_sync) if (state->debug_label_file == NULL) { state->debug_label_file = fopen ("pp_label.txt", "w"); } - left = state->debug_sample_left_edge / 48000.0; - right = state->debug_sample_right_edge / 48000.0; + left = state->debug_sample_left_edge / SAMPLE_RATE_IN; + right = state->debug_sample_right_edge / SAMPLE_RATE_IN; if (state->debug_prefix != '\0') { if (state->debug_prefix == 'I') { fprintf(state->debug_label_file, "%f\t%f\t%c%c %i\n", left, right, state->debug_prefix, state->debug_prefix_2, symbol); From 5dd236ec9607627f3195fc3207f82dcc8a710e40 Mon Sep 17 00:00:00 2001 From: jpwichern Date: Fri, 6 Jun 2014 17:26:50 +0200 Subject: [PATCH 2/3] Update dsd_audio.c Small oops --- dsd_audio.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dsd_audio.c b/dsd_audio.c index 17544aa..d46fee0 100644 --- a/dsd_audio.c +++ b/dsd_audio.c @@ -364,7 +364,7 @@ void openAudioOutDevice (dsd_opts * opts, int speed) { // get info of device/file - if(strncmp(opts->audio_in_dev, "pa:", 3) == 0) + if(strncmp(opts->audio_out_dev, "pa:", 3) == 0) { opts->audio_out_type = 2; #ifdef USE_PORTAUDIO From 74fa117850038fe5d63556e99a99b3fb901b025a Mon Sep 17 00:00:00 2001 From: Jiri Wichern Date: Sat, 7 Jun 2014 20:49:35 +0200 Subject: [PATCH 3/3] Tweaking the delay a bit... 0,050 s for output seems too little; it results in choppy audio and ultimately, a heap of errors. 0,100 seems ok for the output buffer. But the input buffer needs to be much larger else it overflows. Also, an extra stop stream before terminating to make sure all audio from the audio buffer is played. --- dsd.h | 9 ++++++++- dsd_audio.c | 7 +++++-- dsd_main.c | 39 +++++++++++++++++++++++++++++---------- 3 files changed, 42 insertions(+), 13 deletions(-) diff --git a/dsd.h b/dsd.h index 831bc71..22ef3e9 100644 --- a/dsd.h +++ b/dsd.h @@ -45,10 +45,17 @@ #define SAMPLE_RATE_IN 48000 #define SAMPLE_RATE_OUT 8000 -#define PA_FRAMES_PER_BUFFER 64 #ifdef USE_PORTAUDIO #include "portaudio.h" +#define PA_FRAMES_PER_BUFFER 64 +//Buffer needs to be large enough to prevent input buffer overruns while DSD is doing other struff (like outputting voice) +//else you get skipped samples which result in incomplete/erronous decodes and a mountain of error messages. +#define PA_LATENCY_IN 0.500 +//Buffer needs to be large enough to prevent output buffer underruns while DSD is doing other stuff (like decoding input) +//else you get choppy audio and in 'extreme' cases errors. +//Buffer also needs to be as small as possible so we don't have a lot of audio delay. +#define PA_LATENCY_OUT 0.100 #endif /* diff --git a/dsd_audio.c b/dsd_audio.c index d46fee0..2497919 100644 --- a/dsd_audio.c +++ b/dsd_audio.c @@ -235,8 +235,11 @@ playSynthesizedVoice (dsd_opts * opts, dsd_state * state) //printf("Frames available: %d\n", available); if( err != paNoError ) break; - if(available > SAMPLE_RATE_OUT / 2) + if(available > SAMPLE_RATE_OUT * PA_LATENCY_OUT) { + //It looks like this might not be needed for very small latencies. However, it's definitely needed for a bit larger ones. + //When PA_LATENCY_OUT == 0.500 I get output buffer underruns if I don't use this. With PA_LATENCY_OUT <= 0.100 I don't see those happen. + //But with PA_LATENCY_OUT < 0.100 I run the risk of choppy audio and stream errors. printf("\nSyncing voice output stream\n"); err = Pa_StopStream( opts->audio_out_pa_stream ); if( err != paNoError ) @@ -335,7 +338,7 @@ int getPADevice(char* dev, int input, PaStream** stream) parameters.device = devnum; parameters.channelCount = 1; /* mono */ parameters.sampleFormat = paInt16; //Shorts - parameters.suggestedLatency = 0.500; //0.050; // Pa_GetDeviceInfo( outputParameters.device )->defaultLowOutputLatency; + parameters.suggestedLatency = (input == 1) ? PA_LATENCY_IN : PA_LATENCY_OUT; parameters.hostApiSpecificStreamInfo = NULL; //Open stream diff --git a/dsd_main.c b/dsd_main.c index 9cad25f..fe45a78 100644 --- a/dsd_main.c +++ b/dsd_main.c @@ -98,8 +98,14 @@ initOpts (dsd_opts * opts) opts->scoperate = 15; sprintf (opts->audio_in_dev, "/dev/audio"); opts->audio_in_fd = -1; +#ifdef USE_PORTAUDIO + opts->audio_in_pa_stream = NULL; +#endif sprintf (opts->audio_out_dev, "/dev/audio"); opts->audio_out_fd = -1; +#ifdef USE_PORTAUDIO + opts->audio_out_pa_stream = NULL; +#endif opts->split = 0; opts->playoffset = 0; opts->mbe_out_dir[0] = 0; @@ -364,20 +370,33 @@ cleanupAndExit (dsd_opts * opts, dsd_state * state) printf("Terminating portaudio.\n"); PaError err = paNoError; if(opts->audio_in_pa_stream != NULL) - err = Pa_CloseStream( opts->audio_in_pa_stream ); - if( err != paNoError ) { - fprintf( stderr, "An error occured while closing the portaudio input stream\n" ); - fprintf( stderr, "Error number: %d\n", err ); - fprintf( stderr, "Error message: %s\n", Pa_GetErrorText( err ) ); + err = Pa_CloseStream( opts->audio_in_pa_stream ); + if( err != paNoError ) + { + fprintf( stderr, "An error occured while closing the portaudio input stream\n" ); + fprintf( stderr, "Error number: %d\n", err ); + fprintf( stderr, "Error message: %s\n", Pa_GetErrorText( err ) ); + } } if(opts->audio_out_pa_stream != NULL) - err = Pa_CloseStream( opts->audio_out_pa_stream ); - if( err != paNoError ) { - fprintf( stderr, "An error occured while closing the portaudio output stream\n" ); - fprintf( stderr, "Error number: %d\n", err ); - fprintf( stderr, "Error message: %s\n", Pa_GetErrorText( err ) ); + err = Pa_IsStreamActive( opts->audio_out_pa_stream ); + if(err == 1) + err = Pa_StopStream( opts->audio_out_pa_stream ); + if( err != paNoError ) + { + fprintf( stderr, "An error occured while closing the portaudio output stream\n" ); + fprintf( stderr, "Error number: %d\n", err ); + fprintf( stderr, "Error message: %s\n", Pa_GetErrorText( err ) ); + } + err = Pa_CloseStream( opts->audio_out_pa_stream ); + if( err != paNoError ) + { + fprintf( stderr, "An error occured while closing the portaudio output stream\n" ); + fprintf( stderr, "Error number: %d\n", err ); + fprintf( stderr, "Error message: %s\n", Pa_GetErrorText( err ) ); + } } err = Pa_Terminate(); if( err != paNoError )