diff --git a/src/mod/endpoints/mod_portaudio/mod_portaudio.c b/src/mod/endpoints/mod_portaudio/mod_portaudio.c index 68e914ac70..3e726cfb18 100644 --- a/src/mod/endpoints/mod_portaudio/mod_portaudio.c +++ b/src/mod/endpoints/mod_portaudio/mod_portaudio.c @@ -1136,6 +1136,8 @@ static switch_call_cause_t channel_outgoing_channel(switch_core_session_t *sessi } if (outbound_profile->destination_number && !strncasecmp(outbound_profile->destination_number, "endpoint", sizeof("endpoint"))) { + int timer_ms = -1; + int samples_per_packet = -1; audio_endpoint_t *endpoint = NULL; char *endpoint_name = switch_core_strdup(outbound_profile->pool, outbound_profile->destination_number); endpoint_name = strchr(endpoint_name, '/'); @@ -1155,6 +1157,34 @@ static switch_call_cause_t channel_outgoing_channel(switch_core_session_t *sessi retcause = SWITCH_CAUSE_USER_BUSY; goto error; } + + timer_ms = endpoint->in_stream ? endpoint->in_stream->codec_ms : endpoint->out_stream->codec_ms; + samples_per_packet = endpoint->in_stream ? + STREAM_SAMPLES_PER_PACKET(endpoint->in_stream) : STREAM_SAMPLES_PER_PACKET(endpoint->out_stream); + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, + "Setting up timer for endpoint '%s' with %dms and %d samples per packet\n", endpoint->name, timer_ms, samples_per_packet); + + /* only setup read timer if we'll be reading */ + if (endpoint->in_stream && switch_core_timer_init(&endpoint->read_timer, + globals.timer_name, timer_ms, + samples_per_packet, module_pool) != SWITCH_STATUS_SUCCESS) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "failed to setup read timer for endpoint '%s'!\n", endpoint->name); + switch_mutex_unlock(endpoint->mutex); + goto error; + } + + /* The write timer must be setup regardless */ + if (switch_core_timer_init(&endpoint->write_timer, + globals.timer_name, timer_ms, + samples_per_packet, module_pool) != SWITCH_STATUS_SUCCESS) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "failed to setup read timer for endpoint '%s'!\n", endpoint->name); + if (endpoint->in_stream) { + switch_core_timer_destroy(&endpoint->read_timer); + } + switch_mutex_unlock(endpoint->mutex); + goto error; + } + /* try to acquire the stream */ if (take_stream_channel(endpoint->in_stream, endpoint->inchan, 1)) { switch_mutex_unlock(endpoint->mutex); @@ -1520,21 +1550,6 @@ static switch_status_t load_endpoints(switch_xml_t endpoints) "Incomatible input and output streams for endpoint '%s'\n", endpoint_name); continue; } - - if (switch_core_timer_init(&endpoint->read_timer, - globals.timer_name, endpoint->in_stream->codec_ms, - STREAM_SAMPLES_PER_PACKET(endpoint->in_stream), module_pool) != SWITCH_STATUS_SUCCESS) { - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "failed to setup read timer for endpoint '%s'!\n", endpoint_name); - continue; - } - - if (switch_core_timer_init(&endpoint->write_timer, - globals.timer_name, endpoint->out_stream->codec_ms, - STREAM_SAMPLES_PER_PACKET(endpoint->in_stream), module_pool) != SWITCH_STATUS_SUCCESS) { - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "failed to setup read timer for endpoint '%s'!\n", endpoint_name); - continue; - } - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_NOTICE, "Created endpoint '%s', instream = %s, outstream = %s\n", endpoint->name, endpoint->in_stream ? endpoint->in_stream->name : "(none)", @@ -1642,14 +1657,6 @@ static switch_status_t load_config(void) } } - if ((streams = switch_xml_child(cfg, "streams"))) { - load_streams(streams); - } - - if ((endpoints = switch_xml_child(cfg, "endpoints"))) { - load_endpoints(endpoints); - } - if (!globals.dialplan) { set_global_dialplan("XML"); } @@ -1701,6 +1708,16 @@ static switch_status_t load_config(void) } } + /* streams and endpoints must be last, some initialization depend on globals defaults */ + if ((streams = switch_xml_child(cfg, "streams"))) { + load_streams(streams); + } + + if ((endpoints = switch_xml_child(cfg, "endpoints"))) { + load_endpoints(endpoints); + } + + switch_xml_free(xml); return status; diff --git a/src/mod/endpoints/mod_portaudio/pablio.c b/src/mod/endpoints/mod_portaudio/pablio.c index 7070eaedac..6423dc762d 100644 --- a/src/mod/endpoints/mod_portaudio/pablio.c +++ b/src/mod/endpoints/mod_portaudio/pablio.c @@ -78,14 +78,23 @@ static PaError PABLIO_TermFIFO(PaUtilRingBuffer * rbuf); static int iblockingIOCallback(const void *inputBuffer, void *outputBuffer, unsigned long framesPerBuffer, const PaStreamCallbackTimeInfo * timeInfo, PaStreamCallbackFlags statusFlags, void *userData) { + int c = 0, i = 0, j = 0; PABLIO_Stream *data = (PABLIO_Stream *) userData; long numBytes = data->bytesPerFrame * framesPerBuffer; + const int16_t *inputSamples = inputBuffer; + int16_t *chanSamples = (int16_t*)data->iobuff; /* This may get called with NULL inputBuffer during initial setup. */ if (inputBuffer != NULL) { - if (PaUtil_WriteRingBuffer(&data->inFIFOs[0], inputBuffer, numBytes) != numBytes) { - PaUtil_FlushRingBuffer(&data->inFIFOs[0]); - PaUtil_WriteRingBuffer(&data->inFIFOs[0], inputBuffer, numBytes); + /* retrieve the data for each channel and put it in the ring buffer */ + for (c = 0; c < data->channelCount; c++) { + for (i = 0, j = c; i < framesPerBuffer; j += data->channelCount, i++) { + chanSamples[i] = inputSamples[j]; + } + if (PaUtil_WriteRingBuffer(&data->inFIFOs[c], chanSamples, numBytes) != numBytes) { + PaUtil_FlushRingBuffer(&data->inFIFOs[c]); + PaUtil_WriteRingBuffer(&data->inFIFOs[c], inputBuffer, numBytes); + } } } @@ -97,13 +106,21 @@ static int oblockingIOCallback(const void *inputBuffer, void *outputBuffer, { PABLIO_Stream *data = (PABLIO_Stream *) userData; long numBytes = data->bytesPerFrame * framesPerBuffer; + int16_t *outputSamples = outputBuffer; + int16_t *chanSamples = (short *)data->iobuff; + int c = 0, i = 0, j = 0; if (outputBuffer != NULL) { - int i; - int numRead = PaUtil_ReadRingBuffer(&data->outFIFOs[0], outputBuffer, numBytes); - /* Zero out remainder of buffer if we run out of data. */ - for (i = numRead; i < numBytes; i++) { - ((char *) outputBuffer)[i] = 0; + for (c = 0; c < data->channelCount; c++) { + int numRead = PaUtil_ReadRingBuffer(&data->outFIFOs[c], chanSamples, numBytes); + numRead = numRead / sizeof(int16_t); + for (i = 0, j = c; i < framesPerBuffer; j += data->channelCount, i++) { + if (i < numRead) { + outputSamples[j] = chanSamples[i]; + } else { + outputSamples[j] = 0; + } + } } } @@ -251,6 +268,7 @@ PaError OpenAudioStream(PABLIO_Stream ** rwblPtr, PABLIO_Stream *aStream; long numFrames; //long numBytes; + int c = 0; int channels = 1; if (!(inputParameters || outputParameters)) { @@ -270,21 +288,26 @@ PaError OpenAudioStream(PABLIO_Stream ** rwblPtr, numFrames = RoundUpToNextPowerOf2(samples_per_packet * 5); aStream->bytesPerFrame = bytesPerSample; + aStream->channelCount = channels; /* Initialize Ring Buffers */ if (inputParameters) { - err = PABLIO_InitFIFO(&aStream->inFIFOs[0], numFrames, aStream->bytesPerFrame); - if (err != paNoError) { - goto error; + for (c = 0; c < channels; c++) { + err = PABLIO_InitFIFO(&aStream->inFIFOs[c], numFrames, aStream->bytesPerFrame); + if (err != paNoError) { + goto error; + } } aStream->has_in = 1; } if (outputParameters) { - err = PABLIO_InitFIFO(&aStream->outFIFOs[0], numFrames, aStream->bytesPerFrame); - if (err != paNoError) { - goto error; + for (c = 0; c < channels; c++) { + err = PABLIO_InitFIFO(&aStream->outFIFOs[c], numFrames, aStream->bytesPerFrame); + if (err != paNoError) { + goto error; + } } aStream->has_out = 1; } @@ -353,17 +376,21 @@ PaError CloseAudioStream(PABLIO_Stream * aStream) { int bytesEmpty; int byteSize; + int c = 0; - byteSize = aStream->outFIFOs[0].bufferSize; - if (aStream->has_out) { - /* If we are writing data, make sure we play everything written. */ - if (byteSize > 0) { - bytesEmpty = PaUtil_GetRingBufferWriteAvailable(&aStream->outFIFOs[0]); - while (bytesEmpty < byteSize) { - Pa_Sleep(10); - bytesEmpty = PaUtil_GetRingBufferWriteAvailable(&aStream->outFIFOs[0]); + + for (c = 0; c < aStream->channelCount; c++) { + byteSize = aStream->outFIFOs[c].bufferSize; + + /* If we are writing data, make sure we play everything written. */ + if (byteSize > 0) { + bytesEmpty = PaUtil_GetRingBufferWriteAvailable(&aStream->outFIFOs[c]); + while (bytesEmpty < byteSize) { + Pa_Sleep(10); + bytesEmpty = PaUtil_GetRingBufferWriteAvailable(&aStream->outFIFOs[c]); + } } } } @@ -399,11 +426,15 @@ PaError CloseAudioStream(PABLIO_Stream * aStream) } if (aStream->has_in) { - PABLIO_TermFIFO(&aStream->inFIFOs[0]); + for (c = 0; c < aStream->channelCount; c++) { + PABLIO_TermFIFO(&aStream->inFIFOs[c]); + } } if (aStream->has_out) { - PABLIO_TermFIFO(&aStream->outFIFOs[0]); + for (c = 0; c < aStream->channelCount; c++) { + PABLIO_TermFIFO(&aStream->outFIFOs[c]); + } } free(aStream); diff --git a/src/mod/endpoints/mod_portaudio/pablio.h b/src/mod/endpoints/mod_portaudio/pablio.h index 859aabb513..435e4b0ca7 100644 --- a/src/mod/endpoints/mod_portaudio/pablio.h +++ b/src/mod/endpoints/mod_portaudio/pablio.h @@ -56,7 +56,17 @@ extern "C" { #include +/*! Maximum number of channels per stream */ #define MAX_IO_CHANNELS 2 + +/*! Maximum numer of milliseconds per packet */ +#define MAX_IO_MS 100 + +/*! Maximum sampling rate (48Khz) */ +#define MAX_SAMPLING_RATE 48000 + +/* Maximum size of a read */ +#define MAX_IO_BUFFER (((MAX_IO_MS * MAX_SAMPLING_RATE)/1000)*sizeof(int16_t)) typedef struct { PaStream *istream; PaStream *ostream; @@ -68,6 +78,7 @@ typedef struct { PaUtilRingBuffer inFIFOs[MAX_IO_CHANNELS]; PaUtilRingBuffer outFIFOs[MAX_IO_CHANNELS]; int channelCount; + char iobuff[MAX_IO_BUFFER]; } PABLIO_Stream; /* Values for flags for OpenAudioStream(). */