]> git.ipfire.org Git - thirdparty/freeswitch.git/commitdiff
dos2unix
authorMichael Jerris <mike@jerris.com>
Wed, 27 May 2009 23:17:45 +0000 (23:17 +0000)
committerMichael Jerris <mike@jerris.com>
Wed, 27 May 2009 23:17:45 +0000 (23:17 +0000)
git-svn-id: http://svn.freeswitch.org/svn/freeswitch/trunk@13474 d0543943-73ff-0310-b7d9-9358b9ac24b2

src/mod/formats/mod_portaudio_stream/mod_portaudio_stream.c

index f5b990106eebcff80413d40a4cfe8bb730323f7c..0e6eec9e46e1a7d59b7d0eff7c0228dd8b6d406f 100644 (file)
-/*\r
- * FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application\r
- * Copyright (C) 2005-2009, Anthony Minessale II <anthm@freeswitch.org>\r
- *\r
- * Version: MPL 1.1\r
- *\r
- * The contents of this file are subject to the Mozilla Public License Version\r
- * 1.1 (the "License"); you may not use this file except in compliance with\r
- * the License. You may obtain a copy of the License at\r
- * http://www.mozilla.org/MPL/\r
- *\r
- * Software distributed under the License is distributed on an "AS IS" basis,\r
- * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License\r
- * for the specific language governing rights and limitations under the\r
- * License.\r
- *\r
- * The Original Code is FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application\r
- *\r
- * The Initial Developer of the Original Code is\r
- * Anthony Minessale II <anthm@freeswitch.org>\r
- * Portions created by the Initial Developer are Copyright (C)\r
- * the Initial Developer. All Rights Reserved.\r
- *\r
- * Contributor(s):\r
- *\r
- * mod_portaudio_stream.c -- Portaudio Streaming interface Audio\r
- *\r
- */\r
-#include "switch.h"\r
-#include <stdio.h>\r
-#include <stdlib.h>\r
-#include <math.h>\r
-#include "pablio.h"\r
-#include <string.h>\r
-\r
-#define DEFAULT_PREBUFFER_SIZE 1024 * 64\r
-#define SAMPLE_TYPE  paInt16\r
-#define PREFERRED_RATE  8000\r
-\r
-SWITCH_MODULE_LOAD_FUNCTION(mod_portaudio_stream_load);\r
-SWITCH_MODULE_SHUTDOWN_FUNCTION(mod_portaudio_stream_shutdown);\r
-SWITCH_MODULE_DEFINITION(mod_portaudio_stream, mod_portaudio_stream_load, mod_portaudio_stream_shutdown, NULL);\r
-static switch_memory_pool_t *module_pool = NULL;\r
-\r
-struct portaudio_stream_source;\r
-\r
-static struct {\r
-       int running;\r
-       int threads;\r
-       switch_mutex_t *mutex;\r
-       switch_hash_t *source_hash;\r
-\r
-} globals;\r
-\r
-\r
-struct portaudio_stream_context {\r
-       struct portaudio_stream_source *source;\r
-       switch_mutex_t *audio_mutex;\r
-       switch_buffer_t *audio_buffer;\r
-       int err;\r
-       const char *func;\r
-       const char *file;\r
-       int line;\r
-       switch_file_handle_t *handle;\r
-       struct portaudio_stream_context *next;\r
-};\r
-\r
-typedef struct portaudio_stream_context portaudio_stream_context_t;\r
-\r
-struct portaudio_stream_source {\r
-       char *sourcename;\r
-       int sourcedev;\r
-       int rate;\r
-       int interval;\r
-       char *timer_name;\r
-       int total;\r
-       int ready;\r
-       int stopped;\r
-       uint8_t channels;\r
-       switch_size_t samples;\r
-       uint32_t prebuf;\r
-       portaudio_stream_context_t *context_list;\r
-       switch_mutex_t *mutex;\r
-       switch_memory_pool_t *pool;\r
-       switch_thread_rwlock_t *rwlock;\r
-       PABLIO_Stream *audio_stream;\r
-       switch_frame_t read_frame;\r
-       switch_timer_t timer;\r
-       switch_codec_t read_codec;\r
-       switch_codec_t write_codec;\r
-       switch_mutex_t *device_lock;\r
-       unsigned char databuf[SWITCH_RECOMMENDED_BUFFER_SIZE];\r
-};\r
-\r
-typedef struct portaudio_stream_source portaudio_stream_source_t;\r
-\r
-\r
-\r
-static int get_dev_by_number(char *numstr, int in)\r
-{\r
-       int numDevices = Pa_GetDeviceCount();\r
-       const PaDeviceInfo *pdi;\r
-       char *end_ptr;\r
-       int number;\r
-\r
-       number = (int) strtol(numstr, &end_ptr, 10);\r
-\r
-       if (end_ptr == numstr || number < 0) {\r
-               return -1;\r
-       }\r
-\r
-       if (number > -1 && number < numDevices && (pdi = Pa_GetDeviceInfo(number))) {\r
-               if (in && pdi->maxInputChannels) {\r
-                       return number;\r
-               } else if (!in && pdi->maxOutputChannels) {\r
-                       return number;\r
-               }\r
-       }\r
-\r
-       return -1;\r
-}\r
-\r
-static int get_dev_by_name(char *name, int in)\r
-{\r
-       int i;\r
-       int numDevices;\r
-       const PaDeviceInfo *pdi;\r
-       numDevices = Pa_GetDeviceCount();\r
-\r
-       if (numDevices < 0) {\r
-               switch_log_printf(SWITCH_CHANNEL_LOG_CLEAN, SWITCH_LOG_ERROR, "ERROR: Pa_CountDevices returned 0x%x\n", numDevices);\r
-               return -2;\r
-       }\r
-\r
-       for (i = 0; i < numDevices; i++) {\r
-               int match = 0;\r
-               pdi = Pa_GetDeviceInfo(i);\r
-\r
-               if (switch_strlen_zero(name)) {\r
-                       match = 1;\r
-               } else if (pdi && pdi->name && strstr(pdi->name, name)) {\r
-                       match = 1;\r
-               }\r
-\r
-               if (match) {\r
-                       if (in && pdi->maxInputChannels) {\r
-                               return i;\r
-                       } else if (!in && pdi->maxOutputChannels) {\r
-                               return i;\r
-                       }\r
-               }\r
-       }\r
-\r
-       return -1;\r
-}\r
-\r
-static switch_status_t engage_device(portaudio_stream_source_t *source,int restart)\r
-{\r
-       PaStreamParameters inputParameters,outputParameters;\r
-       PaError err;\r
-       int sample_rate = source->rate;\r
-       int codec_ms = source->interval;\r
-\r
-       switch_mutex_init(&source->device_lock, SWITCH_MUTEX_NESTED, module_pool);\r
-\r
-       if (source->timer.timer_interface) {\r
-               switch_core_timer_sync(&source->timer);\r
-       }\r
-\r
-       if (source->audio_stream) {\r
-               return SWITCH_STATUS_SUCCESS;\r
-       }\r
-\r
-       if (!switch_core_codec_ready(&source->read_codec)) {\r
-               if (switch_core_codec_init(&source->read_codec,\r
-                                                                  "L16",\r
-                                                                  NULL, sample_rate, codec_ms, 1, SWITCH_CODEC_FLAG_ENCODE | SWITCH_CODEC_FLAG_DECODE, NULL,\r
-                                                                  NULL) != SWITCH_STATUS_SUCCESS) {\r
-                       switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Can't load codec?\n");\r
-                       return SWITCH_STATUS_FALSE;\r
-               }\r
-       }\r
-\r
-       switch_assert(source->read_codec.implementation);\r
-\r
-       if (!switch_core_codec_ready(&source->write_codec) {\r
-               if (switch_core_codec_init(&source->write_codec,\r
-                                                                  "L16",\r
-                                                                  NULL,\r
-                                                                  sample_rate, codec_ms, 1, SWITCH_CODEC_FLAG_ENCODE | SWITCH_CODEC_FLAG_DECODE, NULL,\r
-                                                                  NULL) != SWITCH_STATUS_SUCCESS) {\r
-                       switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Can't load codec?\n");\r
-                       switch_core_codec_destroy(&source->read_codec);\r
-                       return SWITCH_STATUS_FALSE;\r
-               }\r
-       }\r
-\r
-\r
-       if (!source->timer.timer_interface) {\r
-               if (switch_core_timer_init(&source->timer,\r
-                                                                       source->timer_name, codec_ms, source->read_codec.implementation->samples_per_packet,\r
-                                                                  module_pool) != SWITCH_STATUS_SUCCESS) {\r
-                       switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "setup timer failed!\n");\r
-                       switch_core_codec_destroy(&source->read_codec);\r
-                       switch_core_codec_destroy(&source->write_codec);\r
-                       return SWITCH_STATUS_FALSE;\r
-               }\r
-       }\r
-\r
-       source->read_frame.rate = sample_rate;\r
-       source->read_frame.codec = &source->read_codec;\r
-\r
-       switch_mutex_lock(source->device_lock);\r
-       /* LOCKED ************************************************************************************************** */\r
-       inputParameters.device = source->sourcedev;\r
-       inputParameters.channelCount = 1;\r
-       inputParameters.sampleFormat = SAMPLE_TYPE;\r
-       inputParameters.suggestedLatency = Pa_GetDeviceInfo(inputParameters.device)->defaultLowInputLatency;\r
-       inputParameters.hostApiSpecificStreamInfo = NULL;\r
-       outputParameters.device = source->sourcedev;\r
-       outputParameters.channelCount = 1;\r
-       outputParameters.sampleFormat = SAMPLE_TYPE;\r
-       outputParameters.suggestedLatency = Pa_GetDeviceInfo(outputParameters.device)->defaultLowOutputLatency;\r
-       outputParameters.hostApiSpecificStreamInfo = NULL;\r
-\r
-\r
-       err = OpenAudioStream(&source->audio_stream, &inputParameters, NULL, sample_rate, paClipOff,\r
-                       source->read_codec.implementation->samples_per_packet, 0);\r
-       /* UNLOCKED ************************************************************************************************* */\r
-       if (err != paNoError) {\r
-               switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Error opening audio device retrying\n");\r
-               switch_yield(1000000);\r
-               err = OpenAudioStream(&source->audio_stream, &inputParameters, &outputParameters, sample_rate, paClipOff,\r
-                                       source->read_codec.implementation->samples_per_packet, 0);\r
-       }\r
-\r
-       switch_mutex_unlock(source->device_lock);\r
-       if (err != paNoError) {\r
-               switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Can't open audio device\n");\r
-               switch_core_codec_destroy(&source->read_codec);\r
-               switch_core_timer_destroy(&source->timer);\r
-               return SWITCH_STATUS_FALSE;\r
-       }\r
-\r
-\r
-       return SWITCH_STATUS_SUCCESS;\r
-}\r
-\r
-static void *SWITCH_THREAD_FUNC read_stream_thread(switch_thread_t *thread, void *obj)\r
-{\r
-       portaudio_stream_source_t *source = obj;\r
-       portaudio_stream_context_t *cp;\r
-       int samples = 0;\r
-       int  bused, bytesToWrite;\r
-\r
-\r
-       switch_mutex_lock(globals.mutex);\r
-       globals.threads++;\r
-       switch_mutex_unlock(globals.mutex);\r
-\r
-       if (!source->prebuf) {\r
-               source->prebuf = DEFAULT_PREBUFFER_SIZE;\r
-       }\r
-\r
-\r
-\r
-       switch_mutex_lock(globals.mutex);\r
-       switch_core_hash_insert(globals.source_hash, source->sourcename, source);\r
-       switch_mutex_unlock(globals.mutex);\r
-\r
-\r
-       switch_thread_rwlock_create(&source->rwlock, source->pool);\r
-\r
-       if (engage_device(source,0)!=SWITCH_STATUS_SUCCESS){\r
-               switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, " Dev %d cant be engaged !\n",(int) source->sourcedev);\r
-       } else {\r
-               switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, " Dev %d engaged at %d rate!\n",(int) source->sourcedev, (int) source->rate);\r
-               if (globals.running && !source->stopped) {\r
-                       source->ready = 1;\r
-\r
-                       if (!source->audio_stream){\r
-                               switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "No Audio Stream wops!\n");\r
-                               source->stopped = 0;\r
-                               source->ready = 0;\r
-                       } else {\r
-                               while (globals.running && !source->stopped) {\r
-                                       samples = 0;\r
-                                       switch_mutex_lock(source->device_lock);\r
-                                       samples = ReadAudioStream(source->audio_stream, source->databuf,\r
-                                                                       source->read_codec.implementation->samples_per_packet  , &source->timer);\r
-                                       switch_mutex_unlock(source->device_lock);\r
-\r
-\r
-                                       if (samples) {\r
-                                               bytesToWrite = source->samples;\r
-                                               if (samples < bytesToWrite) {\r
-                                                       bytesToWrite = samples;\r
-                                               }\r
-                                               bytesToWrite *= source->audio_stream->bytesPerFrame;\r
-\r
-                                               if (source->total) {\r
-\r
-                                                       switch_mutex_lock(source->mutex);\r
-                                                       for (cp = source->context_list; cp; cp = cp->next) {\r
-\r
-                                                               switch_mutex_lock(cp->audio_mutex);\r
-\r
-                                                               bused = switch_buffer_inuse(cp->audio_buffer);\r
-                                                               if (bused > source->samples * 768 ) {\r
-                                                                       switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "Leaking stream handle! [%s() %s:%d] %d used %d max\n", cp->func, cp->file,\r
-                                                                                                         cp->line,(int) bused, (int) ( source->samples * 768));\r
-                                                                       switch_buffer_zero(cp->audio_buffer);\r
-                                                               } else {\r
-                                                                       switch_buffer_write(cp->audio_buffer, source->databuf, bytesToWrite);\r
-                                                               }\r
-\r
-                                                               switch_mutex_unlock(cp->audio_mutex);\r
-                                                       }\r
-                                                       switch_mutex_unlock(source->mutex);\r
-                                               }\r
-\r
-                                       }\r
-\r
-                               }\r
-                       }\r
-\r
-               }\r
-       }\r
-\r
-\r
-       source->ready = 0;\r
-\r
-       switch_mutex_lock(globals.mutex);\r
-       switch_core_hash_delete(globals.source_hash, source->sourcename);\r
-       switch_mutex_unlock(globals.mutex);\r
-\r
-       switch_thread_rwlock_wrlock(source->rwlock);\r
-       switch_thread_rwlock_unlock(source->rwlock);\r
-\r
-\r
-       switch_mutex_lock(source->device_lock);\r
-       CloseAudioStream(source->audio_stream);\r
-       if (switch_core_codec_ready(&source->read_codec)) {\r
-               switch_core_codec_destroy(&source->read_codec);\r
-               switch_core_codec_destroy(&source->write_codec);\r
-       }\r
-       if (switch_core_codec_ready(&source->write_codec) {\r
-               switch_core_codec_destroy(&source->write_codec);\r
-       }\r
-       switch_mutex_unlock(source->device_lock);\r
-\r
-\r
-       switch_core_destroy_memory_pool(&source->pool);\r
-\r
-       switch_mutex_lock(globals.mutex);\r
-       globals.threads--;\r
-       switch_mutex_unlock(globals.mutex);\r
-\r
-       switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, " thread ending succesfully !\n");\r
-       switch_thread_exit(thread,SWITCH_STATUS_SUCCESS);\r
-\r
-       return NULL;\r
-}\r
-\r
-\r
-static switch_status_t portaudio_stream_file_open(switch_file_handle_t *handle, const char *path)\r
-{\r
-       portaudio_stream_context_t *context ;\r
-       portaudio_stream_source_t *source;\r
-       switch_memory_pool_t *pool;\r
-       switch_status_t status = SWITCH_STATUS_FALSE;\r
-       switch_thread_t *thread;\r
-       switch_threadattr_t *thd_attr = NULL;\r
-       uint32_t rate = PREFERRED_RATE;\r
-       char *npath ;\r
-       int devNumber;\r
-\r
-\r
-\r
-\r
-\r
-       handle->pre_buffer_datalen = 0;\r
-\r
-       if (switch_test_flag(handle, SWITCH_FILE_FLAG_WRITE)) {\r
-               switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "This format does not support writing! (yet)\n");\r
-               return status;\r
-       }\r
-\r
-       npath =  switch_core_strdup(module_pool, path);\r
-\r
-       int tmp = handle->samplerate;\r
-       if (tmp == 8000 || tmp == 16000 || tmp == 32000 || tmp == 48000) {\r
-               rate = tmp;\r
-       }\r
-\r
-       if (*path == '#') {\r
-               devNumber = get_dev_by_number(npath  + 1, 1);\r
-       } else {\r
-               devNumber = get_dev_by_name(npath , 1);\r
-       }\r
-    npath = switch_mprintf("device-%d at %d",devNumber,rate);\r
-\r
-\r
-\r
-       switch_mutex_lock(globals.mutex);\r
-       source = switch_core_hash_find(globals.source_hash, npath);\r
-\r
-       /* dev isnt there, try to start thread */\r
-       if (!source) {\r
-               switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, " source isnt Created, create and start thread!\n");\r
-\r
-               if (switch_core_new_memory_pool(&pool) != SWITCH_STATUS_SUCCESS) {\r
-                       switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, " :S no pool\n");\r
-               } else {\r
-                       source = switch_core_alloc(pool, sizeof(*source));\r
-                       if (source!=NULL){\r
-                               source->pool = pool;\r
-                               source->sourcedev = devNumber;\r
-                               source->sourcename = switch_core_strdup(source->pool, npath);\r
-                               source->rate = rate;\r
-                               source->interval = 20;\r
-                               source->channels = 1;\r
-                               source->timer_name = "soft";\r
-                               source->prebuf = DEFAULT_PREBUFFER_SIZE;\r
-                               source->stopped = 0;\r
-                               source->ready = 0;\r
-                               source->samples = switch_samples_per_packet(source->rate, source->interval);\r
-\r
-\r
-\r
-                               switch_mutex_init(&source->mutex, SWITCH_MUTEX_NESTED, source->pool);\r
-\r
-\r
-\r
-                               switch_threadattr_create(&thd_attr, source->pool);\r
-                               switch_threadattr_detach_set(thd_attr, 1);\r
-\r
-                               switch_threadattr_stacksize_set(thd_attr, SWITCH_THREAD_STACKSIZE);\r
-                               switch_thread_create(&thread, thd_attr, read_stream_thread, source, source->pool);\r
-\r
-                       }\r
-\r
-               }\r
-\r
-       }\r
-       switch_mutex_unlock(globals.mutex);\r
-       switch_yield(1000000);\r
-       /* dev already engaged */\r
-       if (source) {\r
-\r
-               /*wait for source to be ready*/\r
-\r
-               while(source->ready==0){switch_yield(100000);}\r
-\r
-\r
-               if (switch_thread_rwlock_tryrdlock(source->rwlock) != SWITCH_STATUS_SUCCESS) {\r
-                       switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, " error rwlock !\n");\r
-                       source = NULL;\r
-               }\r
-\r
-       }\r
-\r
-\r
-\r
-       if (source) {\r
-               status = SWITCH_STATUS_SUCCESS;\r
-\r
-               if ((context = switch_core_alloc(handle->memory_pool, sizeof(*context))) == 0) {\r
-                       switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, " error allocating context!\n");\r
-                       status = SWITCH_STATUS_MEMERR;\r
-               } else {\r
-                       /* everything goes fine at this point */\r
-                       handle->samples = 0;\r
-                       handle->samplerate = source->rate;\r
-                       handle->channels = 1;\r
-                       handle->format = 0;\r
-                       handle->sections = 0;\r
-                       handle->seekable = 0;\r
-                       handle->speed = 0;\r
-                       handle->private_info = context;\r
-                       handle->interval = source->interval;\r
-\r
-\r
-                       switch_mutex_init(&context->audio_mutex, SWITCH_MUTEX_NESTED, handle->memory_pool);\r
-                       if (switch_buffer_create_dynamic(&context->audio_buffer, 512, 1024, 0) != SWITCH_STATUS_SUCCESS) {\r
-                               switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Memory Error!\n");\r
-                               status = SWITCH_STATUS_MEMERR;\r
-                       } else {\r
-                               /* context created... then continue */\r
-                               context->source = source;\r
-                               context->file = handle->file;\r
-                               context->func = handle->func;\r
-                               context->line = handle->line;\r
-                               context->handle = handle;\r
-                               switch_mutex_lock(source->mutex);\r
-                               context->next = source->context_list;\r
-                               source->context_list = context;\r
-                               source->total++;\r
-                               switch_mutex_unlock(source->mutex);\r
-\r
-                       }\r
-               }\r
-       } else {\r
-               switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Unknown source %s\n", path);\r
-               status = SWITCH_STATUS_FALSE;\r
-       }\r
-\r
-\r
-\r
-       return status;\r
-}\r
-\r
-\r
-static switch_status_t portaudio_stream_file_close(switch_file_handle_t *handle)\r
-{\r
-       portaudio_stream_context_t *cp, *last = NULL, *context = handle->private_info;\r
-\r
-\r
-       switch_mutex_lock(context->source->mutex);\r
-       for (cp = context->source->context_list; cp; cp = cp->next) {\r
-               if (cp == context) {\r
-                       if (last) {\r
-                               last->next = cp->next;\r
-                       } else {\r
-                               context->source->context_list = cp->next;\r
-                       }\r
-                       break;\r
-               }\r
-               last = cp;\r
-       }\r
-       context->source->total--;\r
-       switch_mutex_unlock(context->source->mutex);\r
-       switch_buffer_destroy(&context->audio_buffer);\r
-       switch_thread_rwlock_unlock(context->source->rwlock);\r
-\r
-       return SWITCH_STATUS_SUCCESS;\r
-}\r
-\r
-\r
-static switch_status_t portaudio_stream_file_read(switch_file_handle_t *handle, void *data, size_t *len)\r
-{\r
-       portaudio_stream_context_t *context = handle->private_info;\r
-       switch_size_t bytes = 0;\r
-       int bytesPerSample = context->source->audio_stream->bytesPerFrame;\r
-       size_t need = *len * bytesPerSample;\r
-\r
-\r
-\r
-       if (!context->source->ready) {\r
-               *len = 0;\r
-               return SWITCH_STATUS_FALSE;\r
-       }\r
-\r
-       switch_mutex_lock(context->audio_mutex);\r
-       if ((bytes = switch_buffer_read(context->audio_buffer, data, need))) {\r
-               *len = bytes / bytesPerSample;\r
-       } else {\r
-               if (need > 2560) {\r
-                       need = 2560;\r
-               }\r
-               memset(data, 255, need);\r
-               *len = need / bytesPerSample;\r
-       }\r
-       switch_mutex_unlock(context->audio_mutex);\r
-\r
-\r
-\r
-       handle->sample_count += *len;\r
-       return SWITCH_STATUS_SUCCESS;\r
-\r
-\r
-}\r
-\r
-/* Registration */\r
-\r
-static char *supported_formats[SWITCH_MAX_CODECS] = { 0 };\r
-\r
-static void shutdown_event_handler(switch_event_t *event)\r
-{\r
-       globals.running = 0;\r
-}\r
-\r
-SWITCH_MODULE_LOAD_FUNCTION(mod_portaudio_stream_load)\r
-{\r
-       switch_file_interface_t *file_interface;\r
-       supported_formats[0] = "portaudio_stream";\r
-\r
-       module_pool = pool;\r
-\r
-       Pa_Initialize();\r
-\r
-       *module_interface = switch_loadable_module_create_module_interface(pool, modname);\r
-       file_interface = switch_loadable_module_create_interface(*module_interface, SWITCH_FILE_INTERFACE);\r
-       file_interface->interface_name = modname;\r
-       file_interface->extens = supported_formats;\r
-       file_interface->file_open = portaudio_stream_file_open;\r
-       file_interface->file_close = portaudio_stream_file_close;\r
-       file_interface->file_read = portaudio_stream_file_read;\r
-\r
-       if (switch_event_bind(modname, SWITCH_EVENT_SHUTDOWN, SWITCH_EVENT_SUBCLASS_ANY, shutdown_event_handler, NULL) != SWITCH_STATUS_SUCCESS) {\r
-               switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Couldn't bind shutdown event handler!\n");\r
-       }\r
-\r
-       memset(&globals, 0, sizeof(globals));\r
-       globals.running = 1;\r
-       globals.threads = 0;\r
-       switch_mutex_init(&globals.mutex, SWITCH_MUTEX_NESTED, module_pool);\r
-       switch_core_hash_init(&globals.source_hash, module_pool);\r
-\r
-\r
-       /* indicate that the module should continue to be loaded */\r
-       return SWITCH_STATUS_SUCCESS;\r
-}\r
-\r
-SWITCH_MODULE_SHUTDOWN_FUNCTION(mod_portaudio_stream_shutdown)\r
-{\r
-       globals.running = 0;\r
-       switch_event_unbind_callback(shutdown_event_handler);\r
-\r
-       while(globals.threads > 0) {\r
-               switch_yield(100000);\r
-       }\r
-\r
-       Pa_Terminate();\r
-\r
-       switch_core_hash_destroy(&globals.source_hash);\r
-       return SWITCH_STATUS_SUCCESS;\r
-}\r
-\r
-/* For Emacs:\r
- * Local Variables:\r
- * mode:c\r
- * indent-tabs-mode:t\r
- * tab-width:4\r
- * c-basic-offset:4\r
- * End:\r
- * For VIM:\r
- * vim:set softtabstop=4 shiftwidth=4 tabstop=4:\r
- */\r
+/*
+ * FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
+ * Copyright (C) 2005-2009, Anthony Minessale II <anthm@freeswitch.org>
+ *
+ * Version: MPL 1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
+ *
+ * The Initial Developer of the Original Code is
+ * Anthony Minessale II <anthm@freeswitch.org>
+ * Portions created by the Initial Developer are Copyright (C)
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * mod_portaudio_stream.c -- Portaudio Streaming interface Audio
+ *
+ */
+#include "switch.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <math.h>
+#include "pablio.h"
+#include <string.h>
+
+#define DEFAULT_PREBUFFER_SIZE 1024 * 64
+#define SAMPLE_TYPE  paInt16
+#define PREFERRED_RATE  8000
+
+SWITCH_MODULE_LOAD_FUNCTION(mod_portaudio_stream_load);
+SWITCH_MODULE_SHUTDOWN_FUNCTION(mod_portaudio_stream_shutdown);
+SWITCH_MODULE_DEFINITION(mod_portaudio_stream, mod_portaudio_stream_load, mod_portaudio_stream_shutdown, NULL);
+static switch_memory_pool_t *module_pool = NULL;
+
+struct portaudio_stream_source;
+
+static struct {
+       int running;
+       int threads;
+       switch_mutex_t *mutex;
+       switch_hash_t *source_hash;
+
+} globals;
+
+
+struct portaudio_stream_context {
+       struct portaudio_stream_source *source;
+       switch_mutex_t *audio_mutex;
+       switch_buffer_t *audio_buffer;
+       int err;
+       const char *func;
+       const char *file;
+       int line;
+       switch_file_handle_t *handle;
+       struct portaudio_stream_context *next;
+};
+
+typedef struct portaudio_stream_context portaudio_stream_context_t;
+
+struct portaudio_stream_source {
+       char *sourcename;
+       int sourcedev;
+       int rate;
+       int interval;
+       char *timer_name;
+       int total;
+       int ready;
+       int stopped;
+       uint8_t channels;
+       switch_size_t samples;
+       uint32_t prebuf;
+       portaudio_stream_context_t *context_list;
+       switch_mutex_t *mutex;
+       switch_memory_pool_t *pool;
+       switch_thread_rwlock_t *rwlock;
+       PABLIO_Stream *audio_stream;
+       switch_frame_t read_frame;
+       switch_timer_t timer;
+       switch_codec_t read_codec;
+       switch_codec_t write_codec;
+       switch_mutex_t *device_lock;
+       unsigned char databuf[SWITCH_RECOMMENDED_BUFFER_SIZE];
+};
+
+typedef struct portaudio_stream_source portaudio_stream_source_t;
+
+
+
+static int get_dev_by_number(char *numstr, int in)
+{
+       int numDevices = Pa_GetDeviceCount();
+       const PaDeviceInfo *pdi;
+       char *end_ptr;
+       int number;
+
+       number = (int) strtol(numstr, &end_ptr, 10);
+
+       if (end_ptr == numstr || number < 0) {
+               return -1;
+       }
+
+       if (number > -1 && number < numDevices && (pdi = Pa_GetDeviceInfo(number))) {
+               if (in && pdi->maxInputChannels) {
+                       return number;
+               } else if (!in && pdi->maxOutputChannels) {
+                       return number;
+               }
+       }
+
+       return -1;
+}
+
+static int get_dev_by_name(char *name, int in)
+{
+       int i;
+       int numDevices;
+       const PaDeviceInfo *pdi;
+       numDevices = Pa_GetDeviceCount();
+
+       if (numDevices < 0) {
+               switch_log_printf(SWITCH_CHANNEL_LOG_CLEAN, SWITCH_LOG_ERROR, "ERROR: Pa_CountDevices returned 0x%x\n", numDevices);
+               return -2;
+       }
+
+       for (i = 0; i < numDevices; i++) {
+               int match = 0;
+               pdi = Pa_GetDeviceInfo(i);
+
+               if (switch_strlen_zero(name)) {
+                       match = 1;
+               } else if (pdi && pdi->name && strstr(pdi->name, name)) {
+                       match = 1;
+               }
+
+               if (match) {
+                       if (in && pdi->maxInputChannels) {
+                               return i;
+                       } else if (!in && pdi->maxOutputChannels) {
+                               return i;
+                       }
+               }
+       }
+
+       return -1;
+}
+
+static switch_status_t engage_device(portaudio_stream_source_t *source,int restart)
+{
+       PaStreamParameters inputParameters,outputParameters;
+       PaError err;
+       int sample_rate = source->rate;
+       int codec_ms = source->interval;
+
+       switch_mutex_init(&source->device_lock, SWITCH_MUTEX_NESTED, module_pool);
+
+       if (source->timer.timer_interface) {
+               switch_core_timer_sync(&source->timer);
+       }
+
+       if (source->audio_stream) {
+               return SWITCH_STATUS_SUCCESS;
+       }
+
+       if (!switch_core_codec_ready(&source->read_codec)) {
+               if (switch_core_codec_init(&source->read_codec,
+                                                                  "L16",
+                                                                  NULL, sample_rate, codec_ms, 1, SWITCH_CODEC_FLAG_ENCODE | SWITCH_CODEC_FLAG_DECODE, NULL,
+                                                                  NULL) != SWITCH_STATUS_SUCCESS) {
+                       switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Can't load codec?\n");
+                       return SWITCH_STATUS_FALSE;
+               }
+       }
+
+       switch_assert(source->read_codec.implementation);
+
+       if (!switch_core_codec_ready(&source->write_codec) {
+               if (switch_core_codec_init(&source->write_codec,
+                                                                  "L16",
+                                                                  NULL,
+                                                                  sample_rate, codec_ms, 1, SWITCH_CODEC_FLAG_ENCODE | SWITCH_CODEC_FLAG_DECODE, NULL,
+                                                                  NULL) != SWITCH_STATUS_SUCCESS) {
+                       switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Can't load codec?\n");
+                       switch_core_codec_destroy(&source->read_codec);
+                       return SWITCH_STATUS_FALSE;
+               }
+       }
+
+
+       if (!source->timer.timer_interface) {
+               if (switch_core_timer_init(&source->timer,
+                                                                       source->timer_name, codec_ms, source->read_codec.implementation->samples_per_packet,
+                                                                  module_pool) != SWITCH_STATUS_SUCCESS) {
+                       switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "setup timer failed!\n");
+                       switch_core_codec_destroy(&source->read_codec);
+                       switch_core_codec_destroy(&source->write_codec);
+                       return SWITCH_STATUS_FALSE;
+               }
+       }
+
+       source->read_frame.rate = sample_rate;
+       source->read_frame.codec = &source->read_codec;
+
+       switch_mutex_lock(source->device_lock);
+       /* LOCKED ************************************************************************************************** */
+       inputParameters.device = source->sourcedev;
+       inputParameters.channelCount = 1;
+       inputParameters.sampleFormat = SAMPLE_TYPE;
+       inputParameters.suggestedLatency = Pa_GetDeviceInfo(inputParameters.device)->defaultLowInputLatency;
+       inputParameters.hostApiSpecificStreamInfo = NULL;
+       outputParameters.device = source->sourcedev;
+       outputParameters.channelCount = 1;
+       outputParameters.sampleFormat = SAMPLE_TYPE;
+       outputParameters.suggestedLatency = Pa_GetDeviceInfo(outputParameters.device)->defaultLowOutputLatency;
+       outputParameters.hostApiSpecificStreamInfo = NULL;
+
+
+       err = OpenAudioStream(&source->audio_stream, &inputParameters, NULL, sample_rate, paClipOff,
+                       source->read_codec.implementation->samples_per_packet, 0);
+       /* UNLOCKED ************************************************************************************************* */
+       if (err != paNoError) {
+               switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Error opening audio device retrying\n");
+               switch_yield(1000000);
+               err = OpenAudioStream(&source->audio_stream, &inputParameters, &outputParameters, sample_rate, paClipOff,
+                                       source->read_codec.implementation->samples_per_packet, 0);
+       }
+
+       switch_mutex_unlock(source->device_lock);
+       if (err != paNoError) {
+               switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Can't open audio device\n");
+               switch_core_codec_destroy(&source->read_codec);
+               switch_core_timer_destroy(&source->timer);
+               return SWITCH_STATUS_FALSE;
+       }
+
+
+       return SWITCH_STATUS_SUCCESS;
+}
+
+static void *SWITCH_THREAD_FUNC read_stream_thread(switch_thread_t *thread, void *obj)
+{
+       portaudio_stream_source_t *source = obj;
+       portaudio_stream_context_t *cp;
+       int samples = 0;
+       int  bused, bytesToWrite;
+
+
+       switch_mutex_lock(globals.mutex);
+       globals.threads++;
+       switch_mutex_unlock(globals.mutex);
+
+       if (!source->prebuf) {
+               source->prebuf = DEFAULT_PREBUFFER_SIZE;
+       }
+
+
+
+       switch_mutex_lock(globals.mutex);
+       switch_core_hash_insert(globals.source_hash, source->sourcename, source);
+       switch_mutex_unlock(globals.mutex);
+
+
+       switch_thread_rwlock_create(&source->rwlock, source->pool);
+
+       if (engage_device(source,0)!=SWITCH_STATUS_SUCCESS){
+               switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, " Dev %d cant be engaged !\n",(int) source->sourcedev);
+       } else {
+               switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, " Dev %d engaged at %d rate!\n",(int) source->sourcedev, (int) source->rate);
+               if (globals.running && !source->stopped) {
+                       source->ready = 1;
+
+                       if (!source->audio_stream){
+                               switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "No Audio Stream wops!\n");
+                               source->stopped = 0;
+                               source->ready = 0;
+                       } else {
+                               while (globals.running && !source->stopped) {
+                                       samples = 0;
+                                       switch_mutex_lock(source->device_lock);
+                                       samples = ReadAudioStream(source->audio_stream, source->databuf,
+                                                                       source->read_codec.implementation->samples_per_packet  , &source->timer);
+                                       switch_mutex_unlock(source->device_lock);
+
+
+                                       if (samples) {
+                                               bytesToWrite = source->samples;
+                                               if (samples < bytesToWrite) {
+                                                       bytesToWrite = samples;
+                                               }
+                                               bytesToWrite *= source->audio_stream->bytesPerFrame;
+
+                                               if (source->total) {
+
+                                                       switch_mutex_lock(source->mutex);
+                                                       for (cp = source->context_list; cp; cp = cp->next) {
+
+                                                               switch_mutex_lock(cp->audio_mutex);
+
+                                                               bused = switch_buffer_inuse(cp->audio_buffer);
+                                                               if (bused > source->samples * 768 ) {
+                                                                       switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "Leaking stream handle! [%s() %s:%d] %d used %d max\n", cp->func, cp->file,
+                                                                                                         cp->line,(int) bused, (int) ( source->samples * 768));
+                                                                       switch_buffer_zero(cp->audio_buffer);
+                                                               } else {
+                                                                       switch_buffer_write(cp->audio_buffer, source->databuf, bytesToWrite);
+                                                               }
+
+                                                               switch_mutex_unlock(cp->audio_mutex);
+                                                       }
+                                                       switch_mutex_unlock(source->mutex);
+                                               }
+
+                                       }
+
+                               }
+                       }
+
+               }
+       }
+
+
+       source->ready = 0;
+
+       switch_mutex_lock(globals.mutex);
+       switch_core_hash_delete(globals.source_hash, source->sourcename);
+       switch_mutex_unlock(globals.mutex);
+
+       switch_thread_rwlock_wrlock(source->rwlock);
+       switch_thread_rwlock_unlock(source->rwlock);
+
+
+       switch_mutex_lock(source->device_lock);
+       CloseAudioStream(source->audio_stream);
+       if (switch_core_codec_ready(&source->read_codec)) {
+               switch_core_codec_destroy(&source->read_codec);
+               switch_core_codec_destroy(&source->write_codec);
+       }
+       if (switch_core_codec_ready(&source->write_codec) {
+               switch_core_codec_destroy(&source->write_codec);
+       }
+       switch_mutex_unlock(source->device_lock);
+
+
+       switch_core_destroy_memory_pool(&source->pool);
+
+       switch_mutex_lock(globals.mutex);
+       globals.threads--;
+       switch_mutex_unlock(globals.mutex);
+
+       switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, " thread ending succesfully !\n");
+       switch_thread_exit(thread,SWITCH_STATUS_SUCCESS);
+
+       return NULL;
+}
+
+
+static switch_status_t portaudio_stream_file_open(switch_file_handle_t *handle, const char *path)
+{
+       portaudio_stream_context_t *context ;
+       portaudio_stream_source_t *source;
+       switch_memory_pool_t *pool;
+       switch_status_t status = SWITCH_STATUS_FALSE;
+       switch_thread_t *thread;
+       switch_threadattr_t *thd_attr = NULL;
+       uint32_t rate = PREFERRED_RATE;
+       char *npath ;
+       int devNumber;
+
+
+
+
+
+       handle->pre_buffer_datalen = 0;
+
+       if (switch_test_flag(handle, SWITCH_FILE_FLAG_WRITE)) {
+               switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "This format does not support writing! (yet)\n");
+               return status;
+       }
+
+       npath =  switch_core_strdup(module_pool, path);
+
+       int tmp = handle->samplerate;
+       if (tmp == 8000 || tmp == 16000 || tmp == 32000 || tmp == 48000) {
+               rate = tmp;
+       }
+
+       if (*path == '#') {
+               devNumber = get_dev_by_number(npath  + 1, 1);
+       } else {
+               devNumber = get_dev_by_name(npath , 1);
+       }
+    npath = switch_mprintf("device-%d at %d",devNumber,rate);
+
+
+
+       switch_mutex_lock(globals.mutex);
+       source = switch_core_hash_find(globals.source_hash, npath);
+
+       /* dev isnt there, try to start thread */
+       if (!source) {
+               switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, " source isnt Created, create and start thread!\n");
+
+               if (switch_core_new_memory_pool(&pool) != SWITCH_STATUS_SUCCESS) {
+                       switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, " :S no pool\n");
+               } else {
+                       source = switch_core_alloc(pool, sizeof(*source));
+                       if (source!=NULL){
+                               source->pool = pool;
+                               source->sourcedev = devNumber;
+                               source->sourcename = switch_core_strdup(source->pool, npath);
+                               source->rate = rate;
+                               source->interval = 20;
+                               source->channels = 1;
+                               source->timer_name = "soft";
+                               source->prebuf = DEFAULT_PREBUFFER_SIZE;
+                               source->stopped = 0;
+                               source->ready = 0;
+                               source->samples = switch_samples_per_packet(source->rate, source->interval);
+
+
+
+                               switch_mutex_init(&source->mutex, SWITCH_MUTEX_NESTED, source->pool);
+
+
+
+                               switch_threadattr_create(&thd_attr, source->pool);
+                               switch_threadattr_detach_set(thd_attr, 1);
+
+                               switch_threadattr_stacksize_set(thd_attr, SWITCH_THREAD_STACKSIZE);
+                               switch_thread_create(&thread, thd_attr, read_stream_thread, source, source->pool);
+
+                       }
+
+               }
+
+       }
+       switch_mutex_unlock(globals.mutex);
+       switch_yield(1000000);
+       /* dev already engaged */
+       if (source) {
+
+               /*wait for source to be ready*/
+
+               while(source->ready==0){switch_yield(100000);}
+
+
+               if (switch_thread_rwlock_tryrdlock(source->rwlock) != SWITCH_STATUS_SUCCESS) {
+                       switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, " error rwlock !\n");
+                       source = NULL;
+               }
+
+       }
+
+
+
+       if (source) {
+               status = SWITCH_STATUS_SUCCESS;
+
+               if ((context = switch_core_alloc(handle->memory_pool, sizeof(*context))) == 0) {
+                       switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, " error allocating context!\n");
+                       status = SWITCH_STATUS_MEMERR;
+               } else {
+                       /* everything goes fine at this point */
+                       handle->samples = 0;
+                       handle->samplerate = source->rate;
+                       handle->channels = 1;
+                       handle->format = 0;
+                       handle->sections = 0;
+                       handle->seekable = 0;
+                       handle->speed = 0;
+                       handle->private_info = context;
+                       handle->interval = source->interval;
+
+
+                       switch_mutex_init(&context->audio_mutex, SWITCH_MUTEX_NESTED, handle->memory_pool);
+                       if (switch_buffer_create_dynamic(&context->audio_buffer, 512, 1024, 0) != SWITCH_STATUS_SUCCESS) {
+                               switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Memory Error!\n");
+                               status = SWITCH_STATUS_MEMERR;
+                       } else {
+                               /* context created... then continue */
+                               context->source = source;
+                               context->file = handle->file;
+                               context->func = handle->func;
+                               context->line = handle->line;
+                               context->handle = handle;
+                               switch_mutex_lock(source->mutex);
+                               context->next = source->context_list;
+                               source->context_list = context;
+                               source->total++;
+                               switch_mutex_unlock(source->mutex);
+
+                       }
+               }
+       } else {
+               switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Unknown source %s\n", path);
+               status = SWITCH_STATUS_FALSE;
+       }
+
+
+
+       return status;
+}
+
+
+static switch_status_t portaudio_stream_file_close(switch_file_handle_t *handle)
+{
+       portaudio_stream_context_t *cp, *last = NULL, *context = handle->private_info;
+
+
+       switch_mutex_lock(context->source->mutex);
+       for (cp = context->source->context_list; cp; cp = cp->next) {
+               if (cp == context) {
+                       if (last) {
+                               last->next = cp->next;
+                       } else {
+                               context->source->context_list = cp->next;
+                       }
+                       break;
+               }
+               last = cp;
+       }
+       context->source->total--;
+       switch_mutex_unlock(context->source->mutex);
+       switch_buffer_destroy(&context->audio_buffer);
+       switch_thread_rwlock_unlock(context->source->rwlock);
+
+       return SWITCH_STATUS_SUCCESS;
+}
+
+
+static switch_status_t portaudio_stream_file_read(switch_file_handle_t *handle, void *data, size_t *len)
+{
+       portaudio_stream_context_t *context = handle->private_info;
+       switch_size_t bytes = 0;
+       int bytesPerSample = context->source->audio_stream->bytesPerFrame;
+       size_t need = *len * bytesPerSample;
+
+
+
+       if (!context->source->ready) {
+               *len = 0;
+               return SWITCH_STATUS_FALSE;
+       }
+
+       switch_mutex_lock(context->audio_mutex);
+       if ((bytes = switch_buffer_read(context->audio_buffer, data, need))) {
+               *len = bytes / bytesPerSample;
+       } else {
+               if (need > 2560) {
+                       need = 2560;
+               }
+               memset(data, 255, need);
+               *len = need / bytesPerSample;
+       }
+       switch_mutex_unlock(context->audio_mutex);
+
+
+
+       handle->sample_count += *len;
+       return SWITCH_STATUS_SUCCESS;
+
+
+}
+
+/* Registration */
+
+static char *supported_formats[SWITCH_MAX_CODECS] = { 0 };
+
+static void shutdown_event_handler(switch_event_t *event)
+{
+       globals.running = 0;
+}
+
+SWITCH_MODULE_LOAD_FUNCTION(mod_portaudio_stream_load)
+{
+       switch_file_interface_t *file_interface;
+       supported_formats[0] = "portaudio_stream";
+
+       module_pool = pool;
+
+       Pa_Initialize();
+
+       *module_interface = switch_loadable_module_create_module_interface(pool, modname);
+       file_interface = switch_loadable_module_create_interface(*module_interface, SWITCH_FILE_INTERFACE);
+       file_interface->interface_name = modname;
+       file_interface->extens = supported_formats;
+       file_interface->file_open = portaudio_stream_file_open;
+       file_interface->file_close = portaudio_stream_file_close;
+       file_interface->file_read = portaudio_stream_file_read;
+
+       if (switch_event_bind(modname, SWITCH_EVENT_SHUTDOWN, SWITCH_EVENT_SUBCLASS_ANY, shutdown_event_handler, NULL) != SWITCH_STATUS_SUCCESS) {
+               switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Couldn't bind shutdown event handler!\n");
+       }
+
+       memset(&globals, 0, sizeof(globals));
+       globals.running = 1;
+       globals.threads = 0;
+       switch_mutex_init(&globals.mutex, SWITCH_MUTEX_NESTED, module_pool);
+       switch_core_hash_init(&globals.source_hash, module_pool);
+
+
+       /* indicate that the module should continue to be loaded */
+       return SWITCH_STATUS_SUCCESS;
+}
+
+SWITCH_MODULE_SHUTDOWN_FUNCTION(mod_portaudio_stream_shutdown)
+{
+       globals.running = 0;
+       switch_event_unbind_callback(shutdown_event_handler);
+
+       while(globals.threads > 0) {
+               switch_yield(100000);
+       }
+
+       Pa_Terminate();
+
+       switch_core_hash_destroy(&globals.source_hash);
+       return SWITCH_STATUS_SUCCESS;
+}
+
+/* For Emacs:
+ * Local Variables:
+ * mode:c
+ * indent-tabs-mode:t
+ * tab-width:4
+ * c-basic-offset:4
+ * End:
+ * For VIM:
+ * vim:set softtabstop=4 shiftwidth=4 tabstop=4:
+ */