diff --git a/modules.conf b/modules.conf index 64846313a4..5ef5554040 100644 --- a/modules.conf +++ b/modules.conf @@ -15,3 +15,4 @@ mod_iaxchan mod_event_test mod_portaudio mod_xmpp_event +mod_sndfile diff --git a/src/include/switch_core.h b/src/include/switch_core.h index 5de4c944b7..fdf3e19795 100644 --- a/src/include/switch_core.h +++ b/src/include/switch_core.h @@ -124,6 +124,11 @@ SWITCH_DECLARE(void) pbx_core_session_signal_state_change(switch_core_session *s SWITCH_DECLARE(char *) switch_core_strdup(switch_memory_pool *pool, char *todup); SWITCH_DECLARE(switch_core_db *) switch_core_db_open_file(char *filename); SWITCH_DECLARE(char *) switch_core_session_get_uuid(switch_core_session *session); +SWITCH_DECLARE(switch_status) switch_core_file_open(switch_file_handle *fh, char *file_path, unsigned int flags, switch_memory_pool *pool); +SWITCH_DECLARE(switch_status) switch_core_file_read(switch_file_handle *fh, void *data, size_t *len); +SWITCH_DECLARE(switch_status) switch_core_file_write(switch_file_handle *fh, void *data, size_t *len); +SWITCH_DECLARE(switch_status) switch_core_file_seek(switch_file_handle *fh, unsigned int *cur_pos, unsigned int samples, int whence); +SWITCH_DECLARE(switch_status) switch_core_file_close(switch_file_handle *fh); #define SWITCH_CORE_DB "core" #define switch_core_db_handle() switch_core_db_open_file(SWITCH_CORE_DB) diff --git a/src/include/switch_loadable_module.h b/src/include/switch_loadable_module.h index ba30fcae50..b35c6d3c5a 100644 --- a/src/include/switch_loadable_module.h +++ b/src/include/switch_loadable_module.h @@ -60,7 +60,7 @@ SWITCH_DECLARE(switch_api_interface *) loadable_module_get_api_interface(char *n SWITCH_DECLARE(int) loadable_module_get_codecs(switch_memory_pool *pool, switch_codec_interface **array, int arraylen); SWITCH_DECLARE(int) loadable_module_get_codecs_sorted(switch_memory_pool *pool, switch_codec_interface **array, int arraylen, char **prefs, int preflen); SWITCH_DECLARE(switch_status) switch_api_execute(char *cmd, char *arg, char *retbuf, size_t len); -SWITCH_DECLARE(switch_api_interface *) loadable_module_get_file_interface(char *name); +SWITCH_DECLARE(switch_file_interface *) loadable_module_get_file_interface(char *name); SWITCH_DECLARE(void) loadable_module_shutdown(void); #ifdef __cplusplus diff --git a/src/include/switch_module_interfaces.h b/src/include/switch_module_interfaces.h index c038105830..1102d99152 100644 --- a/src/include/switch_module_interfaces.h +++ b/src/include/switch_module_interfaces.h @@ -159,19 +159,25 @@ struct switch_dialplan_interface { struct switch_file_interface { const char *interface_name; - switch_status (*file_open)(switch_file_handle *); + switch_status (*file_open)(switch_file_handle *, char *file_path); switch_status (*file_close)(switch_file_handle *); - switch_status (*file_read)(switch_file_handle *, void *data, size_t len); - switch_status (*file_write)(switch_file_handle *, void *data, size_t len); - switch_status (*file_seek)(switch_file_handle *, unsigned int samples, int whence); + switch_status (*file_read)(switch_file_handle *, void *data, size_t *len); + switch_status (*file_write)(switch_file_handle *, void *data, size_t *len); + switch_status (*file_seek)(switch_file_handle *, unsigned int *cur_pos, unsigned int samples, int whence); + char **extens; const struct switch_file_interface *next; - const char *extens[]; }; struct switch_file_handle { const struct switch_file_interface *file_interface; unsigned int flags; switch_file_t *fd; + unsigned int samples; + unsigned int samplerate; + unsigned int channels; + unsigned int format; + unsigned int sections; + int seekable; unsigned int sample_count; switch_memory_pool *memory_pool; void *private; diff --git a/src/include/switch_types.h b/src/include/switch_types.h index 265bf3b039..9e76d1ec46 100644 --- a/src/include/switch_types.h +++ b/src/include/switch_types.h @@ -117,6 +117,17 @@ typedef enum { SWITCH_TIMER_FLAG_FREE_POOL = (1 << 0), } switch_timer_flag; +typedef enum { + SWITCH_FILE_FLAG_READ = (1 << 0), + SWITCH_FILE_FLAG_WRITE = (1 << 1), + SWITCH_FILE_FLAG_FREE_POOL = (1 << 2), + SWITCH_FILE_DATA_SHORT = (1 << 3), + SWITCH_FILE_DATA_INT = (1 << 4), + SWITCH_FILE_DATA_FLOAT = (1 << 5), + SWITCH_FILE_DATA_DOUBLE = (1 << 6), + SWITCH_FILE_DATA_RAW = (1 << 7), +} switch_file_flag; + typedef enum { SWITCH_CODEC_TYPE_AUDIO, SWITCH_CODEC_TYPE_VIDEO, diff --git a/src/mod/mod_playback/mod_playback.c b/src/mod/mod_playback/mod_playback.c index 6a1bbabf90..15454fb272 100644 --- a/src/mod/mod_playback/mod_playback.c +++ b/src/mod/mod_playback/mod_playback.c @@ -41,10 +41,8 @@ static const char modname[] = "mod_playback"; void playback_function(switch_core_session *session, char *data) { switch_channel *channel; - switch_file_t *fd; char buf[960]; char dtmf[128]; - char *ext; int interval = 0, samples = 0; size_t len = 0, ilen = 0; switch_frame write_frame; @@ -52,30 +50,26 @@ void playback_function(switch_core_session *session, char *data) switch_core_thread_session thread_session; switch_codec codec; switch_memory_pool *pool = switch_core_session_get_pool(session); + switch_file_handle fh; char *codec_name; int x; channel = switch_core_session_get_channel(session); assert(channel != NULL); - - if (!(ext = strrchr(data, '.'))) { - switch_console_printf(SWITCH_CHANNEL_CONSOLE, "Invalid Format\n"); + if (switch_core_file_open(&fh, + data, + SWITCH_FILE_FLAG_READ | SWITCH_FILE_DATA_RAW, + switch_core_session_get_pool(session)) != SWITCH_STATUS_SUCCESS) { + switch_channel_hangup(channel); return; - } - - ext++; + } switch_channel_answer(channel); write_frame.data = buf; write_frame.buflen = sizeof(buf); - if (switch_file_open(&fd, data, SWITCH_FOPEN_READ, SWITCH_FPROT_UREAD, pool) != SWITCH_STATUS_SUCCESS) { - switch_console_printf(SWITCH_CHANNEL_CONSOLE, "OPEN FILE FAILED\n"); - switch_channel_hangup(channel); - return; - } switch_console_printf(SWITCH_CHANNEL_CONSOLE, "OPEN FILE %s\n", data); @@ -84,14 +78,6 @@ void playback_function(switch_core_session *session, char *data) samples = 160; codec_name = "L16"; - -#if 0 - interval = 20; - len = 33; - samples = 160; - codec_name = "gsm"; -#endif - write_frame.samples = samples; /* You can use zap instead of soft if you have it loaded */ @@ -134,7 +120,10 @@ void playback_function(switch_core_session *session, char *data) if (done) { break; } - if (switch_file_read(fd, buf, &ilen) != SWITCH_STATUS_SUCCESS) { + + switch_core_file_read(&fh, buf, &ilen); + + if (ilen <= 0) { break; } @@ -153,7 +142,7 @@ void playback_function(switch_core_session *session, char *data) } switch_console_printf(SWITCH_CHANNEL_CONSOLE, "done playing file\n"); - switch_file_close(fd); + switch_core_file_close(&fh); /* End the audio absorbing thread */ switch_core_thread_session_end(&thread_session); diff --git a/src/mod/mod_rawaudio/mod_rawaudio.c b/src/mod/mod_rawaudio/mod_rawaudio.c index 89e4b43d06..ca67c03b57 100644 --- a/src/mod/mod_rawaudio/mod_rawaudio.c +++ b/src/mod/mod_rawaudio/mod_rawaudio.c @@ -76,12 +76,14 @@ static switch_status switch_raw_decode(switch_codec *codec, return SWITCH_STATUS_NOOP; } + static switch_status switch_raw_destroy(switch_codec *codec) { return SWITCH_STATUS_SUCCESS; } -switch_status raw_file_open(switch_file_handle *handle) +#if 0 +switch_status raw_file_open(switch_file_handle *handle, char *path) { return SWITCH_STATUS_SUCCESS; } @@ -91,7 +93,7 @@ switch_status raw_file_close(switch_file_handle *handle) return SWITCH_STATUS_SUCCESS; } -switch_status raw_file_seek(switch_file_handle *handle, unsigned int samples, int whence) +switch_status raw_file_seek(switch_file_handle *handle, unsigned int *cur_sample, unsigned int samples, int whence) { return SWITCH_STATUS_NOTIMPL; } @@ -99,6 +101,9 @@ switch_status raw_file_seek(switch_file_handle *handle, unsigned int samples, in /* Registration */ + +static char *supported_formats[] = {"raw", "r8k", NULL}; + static const switch_file_interface raw_file_interface = { /*.interface_name*/ "raw", /*.file_open*/ raw_file_open, @@ -106,10 +111,10 @@ static const switch_file_interface raw_file_interface = { /*.file_read*/ NULL, /*.file_write*/ NULL, /*.file_seek*/ raw_file_seek, + /*.extens*/ supported_formats, /*.next*/ NULL, - /*.extens*/ {"raw", "r8k"} }; - +#endif static const switch_codec_implementation raw_32k_implementation = { /*.samples_per_second = */ 32000, @@ -196,7 +201,7 @@ static switch_loadable_module_interface raw_module_interface = { /*.codec_interface*/ &raw_codec_interface, /*.application_interface*/ NULL, /*.api_interface*/ NULL, - /*.file_interface*/ &raw_file_interface + ///*.file_interface*/ &raw_file_interface }; diff --git a/src/mod/mod_sndfile/Makefile b/src/mod/mod_sndfile/Makefile new file mode 100644 index 0000000000..ae41dd8b6a --- /dev/null +++ b/src/mod/mod_sndfile/Makefile @@ -0,0 +1,15 @@ +LDFLAGS += -lsndfile -L/usr/local/lib + +all: depends $(MOD).so + +depends: + $(BASE)/buildlib.sh $(BASE) install libsndfile-1.0.12.tar.gz + +$(MOD).so: $(MOD).c + $(CC) $(CFLAGS) -fPIC -c $(MOD).c -o $(MOD).o + $(CC) $(SOLINK) -o $(MOD).so $(MOD).o $(LDFLAGS) + + +clean: + rm -fr *.so *.o *~ + diff --git a/src/mod/mod_sndfile/mod_sndfile.c b/src/mod/mod_sndfile/mod_sndfile.c new file mode 100644 index 0000000000..1f35709483 --- /dev/null +++ b/src/mod/mod_sndfile/mod_sndfile.c @@ -0,0 +1,288 @@ +/* + * FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application + * Copyright (C) 2005/2006, Anthony Minessale II + * + * 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 + * Portions created by the Initial Developer are Copyright (C) + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Anthony Minessale II + * + * + * mod_sndfile.c -- Framework Demo Module + * + */ +#include +#include + +static const char modname[] = "mod_sndfile"; + +struct sndfile_context { + SF_INFO sfinfo; + SNDFILE* handle; +}; + +typedef struct sndfile_context sndfile_context; + +switch_status sndfile_file_open(switch_file_handle *handle, char *path) +{ + sndfile_context *context; + int mode = 0; + char *ext; + + if (!(ext = strrchr(path, '.'))) { + switch_console_printf(SWITCH_CHANNEL_CONSOLE, "Invalid Format\n"); + return SWITCH_STATUS_GENERR; + } + ext++; + + + if (switch_test_flag(handle, SWITCH_FILE_FLAG_READ)) { + mode += SFM_READ; + } + + if (switch_test_flag(handle, SWITCH_FILE_FLAG_WRITE)) { + mode += SFM_WRITE; + } + + if (!mode) { + switch_console_printf(SWITCH_CHANNEL_CONSOLE, "Invalid Mode!\n"); + return SWITCH_STATUS_GENERR; + } + + + if (!(context = switch_core_alloc(handle->memory_pool, sizeof(*context)))) { + return SWITCH_STATUS_MEMERR; + } + + if (!strcmp(ext, "r8") || !strcmp(ext, "raw")) { + context->sfinfo.format = SF_FORMAT_RAW | SF_FORMAT_PCM_16; + context->sfinfo.channels = 1; + context->sfinfo.samplerate = 8000; + } + + if (!strcmp(ext, "r16")) { + context->sfinfo.format = SF_FORMAT_RAW | SF_FORMAT_PCM_16; + context->sfinfo.channels = 1; + context->sfinfo.samplerate = 16000; + } + + if (!strcmp(ext, "r24")) { + context->sfinfo.format = SF_FORMAT_RAW | SF_FORMAT_PCM_16; + context->sfinfo.channels = 1; + context->sfinfo.samplerate = 24000; + } + + if (!strcmp(ext, "r32")) { + context->sfinfo.format = SF_FORMAT_RAW | SF_FORMAT_PCM_16; + context->sfinfo.channels = 1; + context->sfinfo.samplerate = 32000; + } + + if (!(context->handle = sf_open(path, mode, &context->sfinfo))) { + switch_console_printf(SWITCH_CHANNEL_CONSOLE, "Error Opening File [%s] [%s]\n", path, sf_strerror(context->handle)); + return SWITCH_STATUS_GENERR; + } + + handle->samples = context->sfinfo.frames; + handle->samplerate = context->sfinfo.samplerate; + handle->channels = context->sfinfo.channels; + handle->format = context->sfinfo.format; + handle->sections = context->sfinfo.sections; + handle->seekable = context->sfinfo.seekable; + + handle->private = context; + + return SWITCH_STATUS_SUCCESS; +} + +switch_status sndfile_file_close(switch_file_handle *handle) +{ + sndfile_context *context = handle->private; + + sf_close(context->handle); + + return SWITCH_STATUS_SUCCESS; +} + +switch_status sndfile_file_seek(switch_file_handle *handle, unsigned int *cur_sample, unsigned int samples, int whence) +{ + sndfile_context *context = handle->private; + + if (!handle->seekable) { + return SWITCH_STATUS_NOTIMPL; + } + + *cur_sample = sf_seek(context->handle, samples, whence); + + return SWITCH_STATUS_SUCCESS; + +} + +switch_status sndfile_file_read (switch_file_handle *handle, void *data, size_t *len) +{ + unsigned int inlen = *len; + sndfile_context *context = handle->private; + + if (switch_test_flag(handle, SWITCH_FILE_DATA_RAW)) { + *len = sf_read_raw (context->handle, data, inlen); + } else if (switch_test_flag(handle, SWITCH_FILE_DATA_INT)) { + *len = sf_readf_int(context->handle, (int *) data, inlen); + } else if (switch_test_flag(handle, SWITCH_FILE_DATA_SHORT)) { + *len = sf_readf_short(context->handle, (short *) data, inlen); + } else if (switch_test_flag(handle, SWITCH_FILE_DATA_FLOAT)) { + *len = sf_readf_float(context->handle, (float *) data, inlen); + } else if (switch_test_flag(handle, SWITCH_FILE_DATA_DOUBLE)) { + *len = sf_readf_double(context->handle, (double *) data, inlen); + } else { + *len = sf_readf_int(context->handle, (int *) data, inlen); + } + + return SWITCH_STATUS_SUCCESS; +} + +switch_status sndfile_file_write (switch_file_handle *handle, void *data, size_t *len) +{ + unsigned int inlen = *len; + sndfile_context *context = handle->private; + + if (switch_test_flag(handle, SWITCH_FILE_DATA_RAW)) { + *len = sf_write_raw (context->handle, data, inlen); + } else if (switch_test_flag(handle, SWITCH_FILE_DATA_INT)) { + *len = sf_writef_int(context->handle, (int *) data, inlen); + } else if (switch_test_flag(handle, SWITCH_FILE_DATA_SHORT)) { + *len = sf_writef_short(context->handle, (short *) data, inlen); + } else if (switch_test_flag(handle, SWITCH_FILE_DATA_FLOAT)) { + *len = sf_writef_float(context->handle, (float *) data, inlen); + } else if (switch_test_flag(handle, SWITCH_FILE_DATA_DOUBLE)) { + *len = sf_writef_double(context->handle, (double *) data, inlen); + } else { + *len = sf_writef_int(context->handle, (int *) data, inlen); + } + + return SWITCH_STATUS_SUCCESS; +} + +/* Registration */ + +static char *supported_formats[0]; + +static const switch_file_interface sndfile_file_interface = { + /*.interface_name*/ modname, + /*.file_open*/ sndfile_file_open, + /*.file_close*/ sndfile_file_close, + /*.file_read*/ sndfile_file_read, + /*.file_write*/ sndfile_file_write, + /*.file_seek*/ sndfile_file_seek, + /*.extens*/ supported_formats, + /*.next*/ NULL, +}; + +static switch_loadable_module_interface sndfile_module_interface = { + /*.module_name*/ modname, + /*.endpoint_interface*/ NULL, + /*.timer_interface*/ NULL, + /*.dialplan_interface*/ NULL, + /*.codec_interface*/ NULL, + /*.application_interface*/ NULL, + /*.api_interface*/ NULL, + /*.file_interface*/ &sndfile_file_interface +}; + +static switch_status setup_formats(void) +{ + SF_FORMAT_INFO info ; + SF_INFO sfinfo ; + char buffer [128] ; + int format, major_count, subtype_count, m, s ; + int len,x,skip; + + buffer [0] = 0 ; + sf_command (NULL, SFC_GET_LIB_VERSION, buffer, sizeof (buffer)) ; + if (strlen (buffer) < 1) { + switch_console_printf(SWITCH_CHANNEL_CONSOLE_CLEAN, "Line %d: could not retrieve lib version.\n", __LINE__) ; + return SWITCH_STATUS_FALSE; + } + + + switch_console_printf(SWITCH_CHANNEL_CONSOLE, "\nLibSndFile Version : %s Supported Formats\n", buffer) ; + switch_console_printf(SWITCH_CHANNEL_CONSOLE_CLEAN, "================================================================================\n"); + sf_command (NULL, SFC_GET_FORMAT_MAJOR_COUNT, &major_count, sizeof (int)) ; + sf_command (NULL, SFC_GET_FORMAT_SUBTYPE_COUNT, &subtype_count, sizeof (int)) ; + + sfinfo.channels = 1 ; + len = (major_count + 5) * sizeof(char *); + *supported_formats = switch_core_permenant_alloc(len); + memset(supported_formats, 0, len); + + len = 0; + for (m = 0 ; m < major_count ; m++) { + skip = 0; + info.format = m ; + sf_command (NULL, SFC_GET_FORMAT_MAJOR, &info, sizeof (info)) ; + switch_console_printf(SWITCH_CHANNEL_CONSOLE_CLEAN, "%s (extension \"%s\")\n", info.name, info.extension) ; + for (x = 0 ; x < len ; x++) { + if (supported_formats[x] == info.extension) { + skip++; + break; + } + } + if (!skip) { + supported_formats[len++] = (char *) info.extension; + } + format = info.format; + + for (s = 0 ; s < subtype_count ; s++) { + info.format = s ; + sf_command (NULL, SFC_GET_FORMAT_SUBTYPE, &info, sizeof (info)) ; + format = (format & SF_FORMAT_TYPEMASK) | info.format ; + sfinfo.format = format ; + if (sf_format_check (&sfinfo)) { + switch_console_printf(SWITCH_CHANNEL_CONSOLE_CLEAN, " %s\n", info.name) ; + } + } + + } + + supported_formats[len++] = "r8"; + supported_formats[len++] = "r16"; + supported_formats[len++] = "r24"; + supported_formats[len++] = "r32"; + + + + switch_console_printf(SWITCH_CHANNEL_CONSOLE_CLEAN, "================================================================================\n"); + return SWITCH_STATUS_SUCCESS; +} + +SWITCH_MOD_DECLARE(switch_status) switch_module_load(switch_loadable_module_interface **interface, char *filename) { + + + if (setup_formats() != SWITCH_STATUS_SUCCESS) { + return SWITCH_STATUS_FALSE; + } + + /* connect my internal structure to the blank pointer passed to me */ + *interface = &sndfile_module_interface; + + /* indicate that the module should continue to be loaded */ + return SWITCH_STATUS_SUCCESS; +} + diff --git a/src/switch_core.c b/src/switch_core.c index 254134f1da..4ee4afdd81 100644 --- a/src/switch_core.c +++ b/src/switch_core.c @@ -315,6 +315,62 @@ SWITCH_DECLARE(switch_status) switch_core_codec_destroy(switch_codec *codec) return SWITCH_STATUS_SUCCESS; } +SWITCH_DECLARE(switch_status) switch_core_file_open(switch_file_handle *fh, char *file_path, unsigned int flags, switch_memory_pool *pool) +{ + char *ext; + switch_status status; + + memset(fh, 0, sizeof(*fh)); + + if (!(ext = strrchr(file_path, '.'))) { + switch_console_printf(SWITCH_CHANNEL_CONSOLE, "Invalid Format\n"); + return SWITCH_STATUS_FALSE; + } + ext++; + + if (!(fh->file_interface = loadable_module_get_file_interface(ext))) { + switch_console_printf(SWITCH_CHANNEL_CONSOLE, "invalid file format [%s]!\n", ext); + return SWITCH_STATUS_GENERR; + } + + fh->flags = flags; + if (pool) { + fh->memory_pool = pool; + } else { + if ((status = switch_core_new_memory_pool(&fh->memory_pool)) != SWITCH_STATUS_SUCCESS) { + return status; + } + switch_set_flag(fh, SWITCH_TIMER_FLAG_FREE_POOL); + } + + return fh->file_interface->file_open(fh, file_path); +} + +SWITCH_DECLARE(switch_status) switch_core_file_read(switch_file_handle *fh, void *data, size_t *len) +{ + assert(fh != NULL); + + return fh->file_interface->file_read(fh, data, len); +} + +SWITCH_DECLARE(switch_status) switch_core_file_write(switch_file_handle *fh, void *data, size_t *len) +{ + assert(fh != NULL); + + return fh->file_interface->file_write(fh, data, len); +} + +SWITCH_DECLARE(switch_status) switch_core_file_seek(switch_file_handle *fh, unsigned int *cur_pos, unsigned int samples, int whence) +{ + return fh->file_interface->file_seek(fh, cur_pos, samples, whence); +} + +SWITCH_DECLARE(switch_status) switch_core_file_close(switch_file_handle *fh) +{ + return fh->file_interface->file_close(fh); +} + + SWITCH_DECLARE(switch_status) switch_core_timer_init(switch_timer *timer, char *timer_name, int interval, int samples, switch_memory_pool *pool) { switch_timer_interface *timer_interface; diff --git a/src/switch_loadable_module.c b/src/switch_loadable_module.c index ef756fa658..f074f06ec2 100644 --- a/src/switch_loadable_module.c +++ b/src/switch_loadable_module.c @@ -326,7 +326,7 @@ SWITCH_DECLARE(switch_status) switch_loadable_module_init() int i; for (i = 0 ; ptr->extens[i]; i++) { switch_console_printf(SWITCH_CHANNEL_CONSOLE, "Adding File Format '%s'\n", ptr->extens[i]); - switch_core_hash_insert(loadable_modules.api_hash, + switch_core_hash_insert(loadable_modules.file_hash, (char *) ptr->extens[i], (void *) ptr); } @@ -391,7 +391,7 @@ SWITCH_DECLARE(switch_api_interface *) loadable_module_get_api_interface(char *n return switch_core_hash_find(loadable_modules.api_hash, name); } -SWITCH_DECLARE(switch_api_interface *) loadable_module_get_file_interface(char *name) +SWITCH_DECLARE(switch_file_interface *) loadable_module_get_file_interface(char *name) { return switch_core_hash_find(loadable_modules.file_hash, name); }