From 55b190ef4980dc3c9aec1a5bbc579c1d21b8eba3 Mon Sep 17 00:00:00 2001 From: jpwichern Date: Thu, 5 Jun 2014 19:00:39 +0200 Subject: [PATCH] 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);