/* * 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"; static switch_memory_pool_t *module_pool = NULL; static struct { switch_hash_t *format_hash; } globals; struct format_map { char *ext; char *uext; uint32_t format; }; struct sndfile_context { SF_INFO sfinfo; SNDFILE *handle; }; typedef struct sndfile_context sndfile_context; static switch_status_t sndfile_file_open(switch_file_handle_t *handle, char *path) { sndfile_context *context; int mode = 0; char *ext; int ready = 0; struct format_map *map = NULL; if ((ext = strrchr(path, '.')) == 0) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "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_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Invalid Mode!\n"); return SWITCH_STATUS_GENERR; } if ((context = switch_core_alloc(handle->memory_pool, sizeof(*context))) == 0) { return SWITCH_STATUS_MEMERR; } map = switch_core_hash_find(globals.format_hash, ext); if (mode & SFM_WRITE) { sf_count_t frames = 0 ; context->sfinfo.channels = handle->channels; context->sfinfo.samplerate = handle->samplerate; if (handle->samplerate == 8000 || handle->samplerate == 16000) { context->sfinfo.format |= SF_FORMAT_PCM_16; } else if (handle->samplerate == 24000) { context->sfinfo.format |= SF_FORMAT_PCM_24; } else if (handle->samplerate == 32000) { context->sfinfo.format |= SF_FORMAT_PCM_32; } sf_command (context->handle, SFC_FILE_TRUNCATE, &frames, sizeof (frames)); if (map) { context->sfinfo.format |= map->format; ready = 1; } } else if (map) { ready = 1; } if (!ready) { ready = 1; 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; } else if (!strcmp(ext, "r16")) { context->sfinfo.format = SF_FORMAT_RAW | SF_FORMAT_PCM_16; context->sfinfo.channels = 1; context->sfinfo.samplerate = 16000; } else if (!strcmp(ext, "r24")) { context->sfinfo.format = SF_FORMAT_RAW | SF_FORMAT_PCM_24; context->sfinfo.channels = 1; context->sfinfo.samplerate = 24000; } else if (!strcmp(ext, "r32")) { context->sfinfo.format = SF_FORMAT_RAW | SF_FORMAT_PCM_32; context->sfinfo.channels = 1; context->sfinfo.samplerate = 32000; } else if (!strcmp(ext, "gsm")) { context->sfinfo.format = SF_FORMAT_RAW | SF_FORMAT_GSM610; context->sfinfo.channels = 1; context->sfinfo.samplerate = 8000; } else if (!strcmp(ext, "ul")) { context->sfinfo.format = SF_FORMAT_RAW | SF_FORMAT_ULAW; context->sfinfo.channels = 1; context->sfinfo.samplerate = 8000; } else if (!strcmp(ext, "al")) { context->sfinfo.format = SF_FORMAT_RAW | SF_FORMAT_ALAW; context->sfinfo.channels = 1; context->sfinfo.samplerate = 8000; } else { ready = 0; } } if (!ready) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Error Opening File [%s] [%s]\n", path); return SWITCH_STATUS_GENERR; } if ((mode & SFM_WRITE) && sf_format_check (&context->sfinfo) == 0) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Error : file format is invalid (0x%08X).\n", context->sfinfo.format); return SWITCH_STATUS_GENERR; }; if ((context->handle = sf_open(path, mode, &context->sfinfo)) == 0) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Error Opening File [%s] [%s]\n", path, sf_strerror(context->handle)); return SWITCH_STATUS_GENERR; } switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Opening File [%s] %dhz\n", path, context->sfinfo.samplerate); handle->samples = (unsigned int) context->sfinfo.frames; handle->samplerate = context->sfinfo.samplerate; handle->channels = (uint8_t)context->sfinfo.channels; handle->format = context->sfinfo.format; handle->sections = context->sfinfo.sections; handle->seekable = context->sfinfo.seekable; handle->speed = 0; handle->private_info = context; return SWITCH_STATUS_SUCCESS; } static switch_status_t sndfile_file_close(switch_file_handle_t *handle) { sndfile_context *context = handle->private_info; sf_close(context->handle); return SWITCH_STATUS_SUCCESS; } static switch_status_t sndfile_file_seek(switch_file_handle_t *handle, unsigned int *cur_sample, int64_t samples, int whence) { sndfile_context *context = handle->private_info; if (!handle->seekable) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "File is not seekable\n"); return SWITCH_STATUS_NOTIMPL; } *cur_sample = (unsigned int) sf_seek(context->handle, samples, whence); handle->pos = *cur_sample; return SWITCH_STATUS_SUCCESS; } static switch_status_t sndfile_file_read(switch_file_handle_t *handle, void *data, size_t *len) { size_t inlen = *len; sndfile_context *context = handle->private_info; if (switch_test_flag(handle, SWITCH_FILE_DATA_RAW)) { *len = (size_t) sf_read_raw(context->handle, data, inlen); } else if (switch_test_flag(handle, SWITCH_FILE_DATA_INT)) { *len = (size_t) sf_readf_int(context->handle, (int *) data, inlen); } else if (switch_test_flag(handle, SWITCH_FILE_DATA_SHORT)) { *len = (size_t) sf_readf_short(context->handle, (short *) data, inlen); } else if (switch_test_flag(handle, SWITCH_FILE_DATA_FLOAT)) { *len = (size_t) sf_readf_float(context->handle, (float *) data, inlen); } else if (switch_test_flag(handle, SWITCH_FILE_DATA_DOUBLE)) { *len = (size_t) sf_readf_double(context->handle, (double *) data, inlen); } else { *len = (size_t) sf_readf_int(context->handle, (int *) data, inlen); } return SWITCH_STATUS_SUCCESS; } static switch_status_t sndfile_file_write(switch_file_handle_t *handle, void *data, size_t *len) { size_t inlen = *len; sndfile_context *context = handle->private_info; if (switch_test_flag(handle, SWITCH_FILE_DATA_RAW)) { *len = (size_t) sf_write_raw(context->handle, data, inlen); } else if (switch_test_flag(handle, SWITCH_FILE_DATA_INT)) { *len = (size_t) sf_writef_int(context->handle, (int *) data, inlen); } else if (switch_test_flag(handle, SWITCH_FILE_DATA_SHORT)) { *len = (size_t) sf_writef_short(context->handle, (short *) data, inlen); } else if (switch_test_flag(handle, SWITCH_FILE_DATA_FLOAT)) { *len = (size_t) sf_writef_float(context->handle, (float *) data, inlen); } else if (switch_test_flag(handle, SWITCH_FILE_DATA_DOUBLE)) { *len = (size_t) sf_writef_double(context->handle, (double *) data, inlen); } else { *len = (size_t) sf_writef_int(context->handle, (int *) data, inlen); } return SWITCH_STATUS_SUCCESS; } static switch_status_t sndfile_file_set_string(switch_file_handle_t *handle, switch_audio_col_t col, const char *string) { sndfile_context *context = handle->private_info; return sf_set_string(context->handle, (int)col, string) ? SWITCH_STATUS_FALSE : SWITCH_STATUS_SUCCESS; } static switch_status_t sndfile_file_get_string(switch_file_handle_t *handle, switch_audio_col_t col, const char **string) { sndfile_context *context = handle->private_info; const char *s; if ((s = sf_get_string(context->handle, (int)col))) { *string = s; return SWITCH_STATUS_SUCCESS; } return SWITCH_STATUS_FALSE; } /* Registration */ static char **supported_formats; static switch_file_interface_t 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, /*.file_set_string */ sndfile_file_set_string, /*.file_get_string */ sndfile_file_get_string, /*.extens */ NULL, /*.next */ NULL, }; static switch_loadable_module_interface_t 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_t 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; char *extras[] = { "r8", "r16", "r24", "r32", "gsm", "ul", "al", NULL }; int exlen = (sizeof(extras) / sizeof(extras[0])); buffer[0] = 0; sf_command(NULL, SFC_GET_LIB_VERSION, buffer, sizeof(buffer)); if (strlen(buffer) < 1) { switch_log_printf(SWITCH_CHANNEL_LOG_CLEAN, SWITCH_LOG_ERROR, "Line %d: could not retrieve lib version.\n", __LINE__); return SWITCH_STATUS_FALSE; } switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "\nLibSndFile Version : %s Supported Formats\n", buffer); switch_log_printf(SWITCH_CHANNEL_LOG_CLEAN, SWITCH_LOG_INFO, "================================================================================\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 + (exlen + 2)) * sizeof(char *)); supported_formats = switch_core_permanent_alloc(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_log_printf(SWITCH_CHANNEL_LOG_CLEAN, SWITCH_LOG_INFO, "%s (extension \"%s\")\n", info.name, info.extension); for (x = 0; x < len; x++) { if (supported_formats[x] == info.extension) { skip++; break; } } if (!skip) { char *p; struct format_map *map = switch_core_permanent_alloc(sizeof(*map)); if (!map) { abort(); } map->ext = switch_core_permanent_strdup(info.extension); map->uext = switch_core_permanent_strdup(info.extension); map->format = info.format; for(p = map->ext; *p; p++) { *p = tolower(*p); } for(p = map->uext; *p; p++) { *p = toupper(*p); } switch_core_hash_insert(globals.format_hash, map->ext, map); switch_core_hash_insert(globals.format_hash, map->uext, map); 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_log_printf(SWITCH_CHANNEL_LOG_CLEAN, SWITCH_LOG_DEBUG, " %s\n", info.name); } */ } } for (m = 0; m < exlen; m++) { supported_formats[len++] = extras[m]; } switch_log_printf(SWITCH_CHANNEL_LOG_CLEAN, SWITCH_LOG_NOTICE, "================================================================================\n"); return SWITCH_STATUS_SUCCESS; } SWITCH_MOD_DECLARE(switch_status_t) switch_module_load(const switch_loadable_module_interface_t **module_interface, char *filename) { if (switch_core_new_memory_pool(&module_pool) != SWITCH_STATUS_SUCCESS) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "OH OH no pool\n"); return SWITCH_STATUS_TERM; } switch_core_hash_init(&globals.format_hash, module_pool); if (setup_formats() != SWITCH_STATUS_SUCCESS) { return SWITCH_STATUS_FALSE; } /* connect my internal structure to the blank pointer passed to me */ sndfile_file_interface.extens = supported_formats; *module_interface = &sndfile_module_interface; /* indicate that the module should continue to be loaded */ 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 expandtab: */