From 928a989de1d24e977d0fd92cff9862c3db65c09e Mon Sep 17 00:00:00 2001 From: Anthony Minessale Date: Thu, 12 Jun 2014 22:06:33 +0500 Subject: [PATCH] first pass of stereo support --- src/include/switch_core.h | 4 +- src/include/switch_module_interfaces.h | 7 +- src/include/switch_resample.h | 4 +- .../mod_conference/mod_conference.c | 157 ++++++++++---- src/mod/asr_tts/mod_cepstral/mod_cepstral.c | 3 +- src/mod/codecs/mod_opus/mod_opus.c | 80 +++++-- .../mod_local_stream/mod_local_stream.c | 12 +- .../mod_shell_stream/mod_shell_stream.c | 2 + src/mod/formats/mod_shout/mod_shout.c | 22 +- .../formats/mod_tone_stream/mod_tone_stream.c | 3 + src/mod/formats/mod_vlc/mod_vlc.c | 27 ++- src/mod/languages/mod_v8/src/fssession.cpp | 2 +- src/switch_core_codec.c | 7 +- src/switch_core_file.c | 58 ++--- src/switch_core_io.c | 43 +++- src/switch_core_speech.c | 24 +- src/switch_ivr_async.c | 12 +- src/switch_ivr_play_say.c | 71 +++--- src/switch_pcm.c | 205 +++++++++++++++++- src/switch_resample.c | 54 ++++- 20 files changed, 592 insertions(+), 205 deletions(-) diff --git a/src/include/switch_core.h b/src/include/switch_core.h index e38a8ae003..1414d518a2 100644 --- a/src/include/switch_core.h +++ b/src/include/switch_core.h @@ -1870,7 +1870,9 @@ SWITCH_DECLARE(switch_status_t) switch_core_speech_open(_In_ switch_speech_handl const char *module_name, const char *voice_name, _In_ unsigned int rate, - _In_ unsigned int interval, switch_speech_flag_t *flags, _In_opt_ switch_memory_pool_t *pool); + _In_ unsigned int interval, + _In_ unsigned int channels, + switch_speech_flag_t *flags, _In_opt_ switch_memory_pool_t *pool); /*! \brief Feed text to the TTS module \param sh the speech handle to feed diff --git a/src/include/switch_module_interfaces.h b/src/include/switch_module_interfaces.h index 62fea0b3f7..aff702962b 100644 --- a/src/include/switch_module_interfaces.h +++ b/src/include/switch_module_interfaces.h @@ -311,6 +311,7 @@ struct switch_file_handle { uint32_t native_rate; /*! the number of channels */ uint32_t channels; + uint32_t real_channels; /*! integer representation of the format */ unsigned int format; /*! integer representation of the sections */ @@ -435,7 +436,7 @@ struct switch_speech_interface { /*! the name of the interface */ const char *interface_name; /*! function to open the speech interface */ - switch_status_t (*speech_open) (switch_speech_handle_t *sh, const char *voice_name, int rate, switch_speech_flag_t *flags); + switch_status_t (*speech_open) (switch_speech_handle_t *sh, const char *voice_name, int rate, int channels, switch_speech_flag_t *flags); /*! function to close the speech interface */ switch_status_t (*speech_close) (switch_speech_handle_t *, switch_speech_flag_t *flags); /*! function to feed audio to the ASR */ @@ -466,6 +467,8 @@ struct switch_speech_handle { uint32_t rate; uint32_t speed; uint32_t samples; + uint32_t channels; + uint32_t real_channels; char voice[80]; char *engine; /*! module specific param */ @@ -599,6 +602,8 @@ struct switch_codec_fmtp { int bits_per_second; /*! number of microseconds of media in one packet (ptime * 1000) */ int microseconds_per_packet; + /*! stereo */ + int stereo; /*! private data for the codec module to store handle specific info */ void *private_info; diff --git a/src/include/switch_resample.h b/src/include/switch_resample.h index bc1677079f..355510f575 100644 --- a/src/include/switch_resample.h +++ b/src/include/switch_resample.h @@ -67,6 +67,8 @@ SWITCH_BEGIN_EXTERN_C uint32_t to_len; /*! the total size of the to buffer */ uint32_t to_size; + /*! the number of channels */ + int channels; } switch_audio_resampler_t; @@ -171,7 +173,7 @@ SWITCH_DECLARE(void) switch_change_sln_volume_granular(int16_t *data, uint32_t s SWITCH_DECLARE(uint32_t) switch_merge_sln(int16_t *data, uint32_t samples, int16_t *other_data, uint32_t other_samples); SWITCH_DECLARE(uint32_t) switch_unmerge_sln(int16_t *data, uint32_t samples, int16_t *other_data, uint32_t other_samples); -SWITCH_DECLARE(void) switch_mux_channels(int16_t *data, switch_size_t samples, uint32_t channels); +SWITCH_DECLARE(void) switch_mux_channels(int16_t *data, switch_size_t samples, uint32_t orig_channels, uint32_t channels); SWITCH_END_EXTERN_C #endif diff --git a/src/mod/applications/mod_conference/mod_conference.c b/src/mod/applications/mod_conference/mod_conference.c index 36ecbf7d43..f04581736d 100644 --- a/src/mod/applications/mod_conference/mod_conference.c +++ b/src/mod/applications/mod_conference/mod_conference.c @@ -359,6 +359,7 @@ typedef struct conference_obj { switch_mutex_t *flag_mutex; uint32_t rate; uint32_t interval; + uint32_t channels; switch_mutex_t *mutex; conference_member_t *members; conference_member_t *floor_holder; @@ -2532,7 +2533,7 @@ static void *SWITCH_THREAD_FUNC conference_thread_run(switch_thread_t *thread, v conference_obj_t *conference = (conference_obj_t *) obj; conference_member_t *imember, *omember; uint32_t samples = switch_samples_per_packet(conference->rate, conference->interval); - uint32_t bytes = samples * 2; + uint32_t bytes = samples * 2 * conference->channels; uint8_t ready = 0, total = 0; switch_timer_t timer = { 0 }; switch_event_t *event; @@ -2595,12 +2596,12 @@ static void *SWITCH_THREAD_FUNC conference_thread_run(switch_thread_t *thread, v while (globals.running && !switch_test_flag(conference, CFLAG_DESTRUCT)) { switch_size_t file_sample_len = samples; - switch_size_t file_data_len = samples * 2; + switch_size_t file_data_len = samples * 2 * conference->channels; int has_file_data = 0, members_with_video = 0; uint32_t conf_energy = 0; int nomoh = 0; - conference_member_t *floor_holder, *video_bridge_members[2] = { 0 }; - + conference_member_t *floor_holder, *video_bridge_members[2] = { 0 }; + /* Sync the conference to a single timing source */ if (switch_core_timer_next(&timer) != SWITCH_STATUS_SUCCESS) { switch_set_flag(conference, CFLAG_DESTRUCT); @@ -2744,11 +2745,13 @@ static void *SWITCH_THREAD_FUNC conference_thread_run(switch_thread_t *thread, v conference->fnode->leadin--; } else if (!conference->fnode->done) { file_sample_len = samples; + if (conference->fnode->type == NODE_TYPE_SPEECH) { switch_speech_flag_t flags = SWITCH_SPEECH_FLAG_BLOCKING; - + if (switch_core_speech_read_tts(conference->fnode->sh, file_frame, &file_data_len, &flags) == SWITCH_STATUS_SUCCESS) { - file_sample_len = file_data_len / 2; + file_sample_len = file_data_len / 2 / conference->fnode->sh->channels; + } else { file_sample_len = file_data_len = 0; } @@ -2780,8 +2783,7 @@ static void *SWITCH_THREAD_FUNC conference_thread_run(switch_thread_t *thread, v } else { if (has_file_data) { switch_size_t x; - - for (x = 0; x < file_sample_len; x++) { + for (x = 0; x < file_sample_len * conference->channels; x++) { int32_t z; int16_t *muxed; @@ -2792,27 +2794,29 @@ static void *SWITCH_THREAD_FUNC conference_thread_run(switch_thread_t *thread, v muxed[x] = (int16_t) z; } } else { - memcpy(file_frame, async_file_frame, file_sample_len * 2); + memcpy(file_frame, async_file_frame, file_sample_len * 2 * conference->channels); has_file_data = 1; } } } } - + if (ready || has_file_data) { /* Use more bits in the main_frame to preserve the exact sum of the audio samples. */ - int main_frame[SWITCH_RECOMMENDED_BUFFER_SIZE / 2] = { 0 }; - int16_t write_frame[SWITCH_RECOMMENDED_BUFFER_SIZE / 2] = { 0 }; + int main_frame[SWITCH_RECOMMENDED_BUFFER_SIZE] = { 0 }; + int16_t write_frame[SWITCH_RECOMMENDED_BUFFER_SIZE] = { 0 }; /* Init the main frame with file data if there is any. */ bptr = (int16_t *) file_frame; if (has_file_data && file_sample_len) { + for (x = 0; x < bytes / 2; x++) { - if (x <= file_sample_len) { + if (x <= file_sample_len * conference->channels) { main_frame[x] = (int32_t) bptr[x]; } else { memset(&main_frame[x], 255, sizeof(main_frame[x])); + //printf("FUCCCK %d <= %ld (%ld/%d)\n", x, file_sample_len * conference->channels, file_sample_len, conference->channels); } } } @@ -2876,8 +2880,10 @@ static void *SWITCH_THREAD_FUNC conference_thread_run(switch_thread_t *thread, v } bptr = (int16_t *) omember->frame; - for (x = 0; x < bytes / 2; x++) { + + for (x = 0; x < bytes / 2 ; x++) { z = main_frame[x]; + /* bptr[x] represents my own contribution to this audio sample */ if (switch_test_flag(omember, MFLAG_HAS_AUDIO) && x <= omember->read / 2) { z -= (int32_t) bptr[x]; @@ -3678,6 +3684,27 @@ static void check_agc_levels(conference_member_t *member) } +static void member_check_channels(switch_frame_t *frame, conference_member_t *member, switch_bool_t in) +{ + if (member->conference->channels != member->read_impl.number_of_channels) { + uint32_t rlen; + int from, to; + + if (in) { + to = member->conference->channels; + from = member->read_impl.number_of_channels; + } else { + from = member->conference->channels; + to = member->read_impl.number_of_channels; + } + + rlen = frame->datalen / 2 / from; + + switch_mux_channels((int16_t *) frame->data, rlen, from, to); + + frame->datalen = rlen * 2 * to; + } +} /* marshall frames from the call leg to the conference thread for muxing to other call legs */ @@ -3729,6 +3756,8 @@ static void *SWITCH_THREAD_FUNC conference_loop_input(switch_thread_t *thread, v break; } + member_check_channels(read_frame, member, SWITCH_TRUE); + if (switch_channel_test_flag(channel, CF_VIDEO) && !switch_test_flag(member, MFLAG_ACK_VIDEO)) { switch_set_flag_locked(member, MFLAG_ACK_VIDEO); switch_channel_clear_flag(channel, CF_VIDEO_ECHO); @@ -3827,7 +3856,7 @@ static void *SWITCH_THREAD_FUNC conference_loop_input(switch_thread_t *thread, v switch_change_sln_volume_granular(read_frame->data, read_frame->datalen / 2, member->agc_volume_in_level); } - if ((samples = read_frame->datalen / sizeof(*data))) { + if ((samples = read_frame->datalen / sizeof(*data) / member->read_impl.number_of_channels)) { for (i = 0; i < samples; i++) { energy += abs(data[j]); j += member->read_impl.number_of_channels; @@ -3980,9 +4009,9 @@ static void *SWITCH_THREAD_FUNC conference_loop_input(switch_thread_t *thread, v int16_t *bptr = (int16_t *) read_frame->data; int len = (int) read_frame->datalen; - switch_resample_process(read_resampler, bptr, len / 2); - memcpy(member->resample_out, read_resampler->to, read_resampler->to_len * 2); - len = read_resampler->to_len * 2; + switch_resample_process(read_resampler, bptr, len / 2 / member->conference->channels); + memcpy(member->resample_out, read_resampler->to, read_resampler->to_len * 2 * member->conference->channels); + len = read_resampler->to_len * 2 * member->conference->channels; datalen = len; data = member->resample_out; } else { @@ -4039,8 +4068,8 @@ static void *SWITCH_THREAD_FUNC conference_loop_input(switch_thread_t *thread, v static void member_add_file_data(conference_member_t *member, int16_t *data, switch_size_t file_data_len) { - switch_size_t file_sample_len = file_data_len / 2; - int16_t file_frame[SWITCH_RECOMMENDED_BUFFER_SIZE / 2] = { 0 }; + switch_size_t file_sample_len; + int16_t file_frame[SWITCH_RECOMMENDED_BUFFER_SIZE] = { 0 }; switch_mutex_lock(member->fnode_mutex); @@ -4049,6 +4078,8 @@ static void member_add_file_data(conference_member_t *member, int16_t *data, swi goto done; } + file_sample_len = file_data_len / 2 / member->conference->channels; + /* if we are done, clean it up */ if (member->fnode->done) { conference_file_node_t *fnode; @@ -4071,15 +4102,15 @@ static void member_add_file_data(conference_member_t *member, int16_t *data, swi } else { if (member->fnode->type == NODE_TYPE_SPEECH) { switch_speech_flag_t flags = SWITCH_SPEECH_FLAG_BLOCKING; - + if (switch_core_speech_read_tts(member->fnode->sh, file_frame, &file_data_len, &flags) == SWITCH_STATUS_SUCCESS) { - file_sample_len = file_data_len / 2; + file_sample_len = file_data_len / 2 / member->conference->channels; } else { file_sample_len = file_data_len = 0; } } else if (member->fnode->type == NODE_TYPE_FILE) { switch_core_file_read(&member->fnode->fh, file_frame, &file_sample_len); - file_data_len = file_sample_len * 2; + file_data_len = file_sample_len * 2 * member->fnode->fh.channels; } if (file_sample_len <= 0) { @@ -4092,7 +4123,7 @@ static void member_add_file_data(conference_member_t *member, int16_t *data, swi switch_change_sln_volume(file_frame, (uint32_t)file_sample_len, member->volume_out_level); } - for (i = 0; i < (int)file_sample_len; i++) { + for (i = 0; i < (int)file_sample_len * member->conference->channels; i++) { if (member->fnode->mux) { sample = data[i] + file_frame[i]; switch_normalize_to_16bit(sample); @@ -4157,7 +4188,7 @@ static void conference_loop_output(conference_member_t *member) //csamples = samples; tsamples = member->orig_read_impl.samples_per_packet; low_count = 0; - bytes = samples * 2; + bytes = samples * 2 * member->conference->channels; call_list = NULL; cp = NULL; @@ -4165,7 +4196,7 @@ static void conference_loop_output(conference_member_t *member) switch_assert(member->conference != NULL); - flush_len = switch_samples_per_packet(member->conference->rate, member->conference->interval) * 10; + flush_len = switch_samples_per_packet(member->conference->rate, member->conference->interval) * 10 * member->conference->channels; if (switch_core_timer_init(&timer, member->conference->timer_name, interval, tsamples, NULL) != SWITCH_STATUS_SUCCESS) { switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(member->session), SWITCH_LOG_ERROR, "Timer Setup Failed. Conference Cannot Start\n"); @@ -4331,7 +4362,7 @@ static void conference_loop_output(conference_member_t *member) mux_used = (uint32_t) switch_buffer_inuse(member->mux_buffer); use_timer = 1; - + if (mux_used) { if (mux_used < bytes) { if (++low_count >= 5) { @@ -4354,7 +4385,7 @@ static void conference_loop_output(conference_member_t *member) low_count = 0; if ((write_frame.datalen = (uint32_t) switch_buffer_read(use_buffer, write_frame.data, bytes))) { if (write_frame.datalen) { - write_frame.samples = write_frame.datalen / 2; + write_frame.samples = write_frame.datalen / 2 / member->conference->channels; if( !switch_test_flag(member, MFLAG_CAN_HEAR)) { memset(write_frame.data, 255, write_frame.datalen); @@ -4369,6 +4400,9 @@ static void conference_loop_output(conference_member_t *member) if (member->fnode) { member_add_file_data(member, write_frame.data, write_frame.datalen); } + + member_check_channels(&write_frame, member, SWITCH_FALSE); + if (switch_core_session_write_frame(member->session, &write_frame, SWITCH_IO_FLAG_NONE, 0) != SWITCH_STATUS_SUCCESS) { switch_mutex_unlock(member->audio_out_mutex); break; @@ -4383,6 +4417,7 @@ static void conference_loop_output(conference_member_t *member) memset(write_frame.data, 255, write_frame.datalen); write_frame.timestamp = timer.samplecount; member_add_file_data(member, write_frame.data, write_frame.datalen); + member_check_channels(&write_frame, member, SWITCH_FALSE); if (switch_core_session_write_frame(member->session, &write_frame, SWITCH_IO_FLAG_NONE, 0) != SWITCH_STATUS_SUCCESS) { switch_channel_hangup(channel, SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER); break; @@ -4399,6 +4434,8 @@ static void conference_loop_output(conference_member_t *member) write_frame.samples = samples; write_frame.timestamp = timer.samplecount; + member_check_channels(&write_frame, member, SWITCH_FALSE); + if (switch_core_session_write_frame(member->session, &write_frame, SWITCH_IO_FLAG_NONE, 0) != SWITCH_STATUS_SUCCESS) { switch_channel_hangup(channel, SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER); break; @@ -4521,7 +4558,7 @@ static void *SWITCH_THREAD_FUNC conference_record_thread_run(switch_thread_t *th return NULL; } - data_buf_len = samples * sizeof(int16_t); + data_buf_len = samples * sizeof(int16_t) * conference->channels; switch_zmalloc(data_buf, data_buf_len); switch_mutex_lock(globals.hash_mutex); @@ -4574,7 +4611,7 @@ static void *SWITCH_THREAD_FUNC conference_record_thread_run(switch_thread_t *th fh.pre_buffer_datalen = SWITCH_DEFAULT_FILE_BUFFER_LEN; if (switch_core_file_open(&fh, - rec->path, (uint8_t) 1, conference->rate, SWITCH_FILE_FLAG_WRITE | SWITCH_FILE_DATA_SHORT, + rec->path, (uint8_t) conference->channels, conference->rate, SWITCH_FILE_FLAG_WRITE | SWITCH_FILE_DATA_SHORT, rec->pool) != SWITCH_STATUS_SUCCESS) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Error Opening File [%s]\n", rec->path); @@ -4917,7 +4954,7 @@ static switch_status_t conference_play_file(conference_obj_t *conference, char * /* Open the file */ fnode->fh.pre_buffer_datalen = SWITCH_DEFAULT_FILE_BUFFER_LEN; - if (switch_core_file_open(&fnode->fh, file, (uint8_t) 1, conference->rate, SWITCH_FILE_FLAG_READ | SWITCH_FILE_DATA_SHORT, pool) != + if (switch_core_file_open(&fnode->fh, file, (uint8_t) conference->channels, conference->rate, SWITCH_FILE_FLAG_READ | SWITCH_FILE_DATA_SHORT, pool) != SWITCH_STATUS_SUCCESS) { switch_event_t *event; @@ -5043,7 +5080,7 @@ static switch_status_t conference_member_play_file(conference_member_t *member, /* Open the file */ fnode->fh.pre_buffer_datalen = SWITCH_DEFAULT_FILE_BUFFER_LEN; if (switch_core_file_open(&fnode->fh, - file, (uint8_t) 1, member->conference->rate, SWITCH_FILE_FLAG_READ | SWITCH_FILE_DATA_SHORT, + file, (uint8_t) member->conference->channels, member->conference->rate, SWITCH_FILE_FLAG_READ | SWITCH_FILE_DATA_SHORT, pool) != SWITCH_STATUS_SUCCESS) { switch_core_destroy_memory_pool(&pool); status = SWITCH_STATUS_NOTFOUND; @@ -5109,7 +5146,7 @@ static switch_status_t conference_member_say(conference_member_t *member, char * if (!member->sh) { memset(&member->lsh, 0, sizeof(member->lsh)); if (switch_core_speech_open(&member->lsh, conference->tts_engine, conference->tts_voice, - conference->rate, conference->interval, &flags, switch_core_session_get_pool(member->session)) != + conference->rate, conference->interval, conference->channels, &flags, switch_core_session_get_pool(member->session)) != SWITCH_STATUS_SUCCESS) { switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(member->session), SWITCH_LOG_ERROR, "Invalid TTS module [%s]!\n", conference->tts_engine); return SWITCH_STATUS_FALSE; @@ -5198,7 +5235,7 @@ static switch_status_t conference_say(conference_obj_t *conference, const char * if (!conference->sh) { memset(&conference->lsh, 0, sizeof(conference->lsh)); if (switch_core_speech_open(&conference->lsh, conference->tts_engine, conference->tts_voice, - conference->rate, conference->interval, &flags, NULL) != SWITCH_STATUS_SUCCESS) { + conference->rate, conference->interval, conference->channels, &flags, NULL) != SWITCH_STATUS_SUCCESS) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Invalid TTS module [%s]!\n", conference->tts_engine); return SWITCH_STATUS_FALSE; } @@ -8075,14 +8112,15 @@ static int setup_media(conference_member_t *member, conference_obj_t *conference if (switch_core_codec_init(&member->read_codec, "L16", NULL, read_impl.actual_samples_per_second, read_impl.microseconds_per_packet / 1000, - 1, SWITCH_CODEC_FLAG_ENCODE | SWITCH_CODEC_FLAG_DECODE, NULL, member->pool) == SWITCH_STATUS_SUCCESS) { + read_impl.number_of_channels, + SWITCH_CODEC_FLAG_ENCODE | SWITCH_CODEC_FLAG_DECODE, NULL, member->pool) == SWITCH_STATUS_SUCCESS) { switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(member->session), SWITCH_LOG_DEBUG, - "Raw Codec Activation Success L16@%uhz 1 channel %dms\n", - read_impl.actual_samples_per_second, read_impl.microseconds_per_packet / 1000); + "Raw Codec Activation Success L16@%uhz %d channel %dms\n", + read_impl.actual_samples_per_second, read_impl.number_of_channels, read_impl.microseconds_per_packet / 1000); } else { - switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(member->session), SWITCH_LOG_DEBUG, "Raw Codec Activation Failed L16@%uhz 1 channel %dms\n", - read_impl.actual_samples_per_second, read_impl.microseconds_per_packet / 1000); + switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(member->session), SWITCH_LOG_DEBUG, "Raw Codec Activation Failed L16@%uhz %d channel %dms\n", + read_impl.actual_samples_per_second, read_impl.number_of_channels, read_impl.microseconds_per_packet / 1000); goto done; } @@ -8096,7 +8134,7 @@ static int setup_media(conference_member_t *member, conference_obj_t *conference if (read_impl.actual_samples_per_second != conference->rate) { if (switch_resample_create(&member->read_resampler, read_impl.actual_samples_per_second, - conference->rate, member->frame_size, SWITCH_RESAMPLE_QUALITY, 1) != SWITCH_STATUS_SUCCESS) { + conference->rate, member->frame_size, SWITCH_RESAMPLE_QUALITY, conference->channels) != SWITCH_STATUS_SUCCESS) { switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(member->session), SWITCH_LOG_CRIT, "Unable to create resampler!\n"); goto done; } @@ -8120,12 +8158,14 @@ static int setup_media(conference_member_t *member, conference_obj_t *conference NULL, conference->rate, read_impl.microseconds_per_packet / 1000, - 1, SWITCH_CODEC_FLAG_ENCODE | SWITCH_CODEC_FLAG_DECODE, NULL, member->pool) == SWITCH_STATUS_SUCCESS) { + read_impl.number_of_channels, + SWITCH_CODEC_FLAG_ENCODE | SWITCH_CODEC_FLAG_DECODE, NULL, member->pool) == SWITCH_STATUS_SUCCESS) { switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(member->session), SWITCH_LOG_DEBUG, - "Raw Codec Activation Success L16@%uhz 1 channel %dms\n", conference->rate, read_impl.microseconds_per_packet / 1000); + "Raw Codec Activation Success L16@%uhz %d channel %dms\n", + conference->rate, conference->channels, read_impl.microseconds_per_packet / 1000); } else { - switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(member->session), SWITCH_LOG_DEBUG, "Raw Codec Activation Failed L16@%uhz 1 channel %dms\n", - conference->rate, read_impl.microseconds_per_packet / 1000); + switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(member->session), SWITCH_LOG_DEBUG, "Raw Codec Activation Failed L16@%uhz %d channel %dms\n", + conference->rate, conference->channels, read_impl.microseconds_per_packet / 1000); goto codec_done2; } @@ -9027,6 +9067,7 @@ static conference_obj_t *conference_new(char *name, conf_xml_cfg_t cfg, switch_c uint32_t announce_count = 0; char *maxmember_sound = NULL; uint32_t rate = 8000, interval = 20; + uint32_t channels = 1; int broadcast_chat_messages = 0; int comfort_noise_level = 0; int pin_retries = 3; @@ -9044,8 +9085,8 @@ static conference_obj_t *conference_new(char *name, conf_xml_cfg_t cfg, switch_c switch_uuid_t uuid; switch_codec_implementation_t read_impl = { 0 }; switch_channel_t *channel = NULL; - const char *force_rate = NULL, *force_interval = NULL, *presence_id = NULL; - uint32_t force_rate_i = 0, force_interval_i = 0; + const char *force_rate = NULL, *force_interval = NULL, *force_channels = NULL, *presence_id = NULL; + uint32_t force_rate_i = 0, force_interval_i = 0, force_channels_i = NULL; /* Validate the conference name */ if (zstr(name)) { @@ -9073,6 +9114,18 @@ static conference_obj_t *conference_new(char *name, conf_xml_cfg_t cfg, switch_c } } + if ((force_channels = switch_channel_get_variable(channel, "conference_force_channels"))) { + if (!strcasecmp(force_channels, "auto")) { + force_rate_i = read_impl.number_of_channels; + } else { + tmp = atoi(force_channels); + + if (tmp == 1 || tmp == 2) { + force_channels_i = channels = tmp; + } + } + } + if ((force_interval = switch_channel_get_variable(channel, "conference_force_interval"))) { if (!strcasecmp(force_interval, "auto")) { force_interval_i = read_impl.microseconds_per_packet / 1000; @@ -9117,6 +9170,17 @@ static conference_obj_t *conference_new(char *name, conf_xml_cfg_t cfg, switch_c rate = tmp; } } + } else if (!force_channels_i && !strcasecmp(var, "channels") && !zstr(val)) { + uint32_t tmp = atoi(val); + if (session && tmp == 0) { + if (!strcasecmp(val, "auto")) { + channels = read_impl.number_of_channels; + } + } else { + if (tmp == 1 || tmp == 2) { + channels = tmp; + } + } } else if (!strcasecmp(var, "domain") && !zstr(val)) { domain = val; } else if (!strcasecmp(var, "description") && !zstr(val)) { @@ -9507,6 +9571,7 @@ static conference_obj_t *conference_new(char *name, conf_xml_cfg_t cfg, switch_c conference->domain = "cluecon.com"; } + conference->channels = channels; conference->rate = rate; conference->interval = interval; conference->ivr_dtmf_timeout = ivr_dtmf_timeout; diff --git a/src/mod/asr_tts/mod_cepstral/mod_cepstral.c b/src/mod/asr_tts/mod_cepstral/mod_cepstral.c index b00b709354..c368c28b24 100644 --- a/src/mod/asr_tts/mod_cepstral/mod_cepstral.c +++ b/src/mod/asr_tts/mod_cepstral/mod_cepstral.c @@ -118,7 +118,7 @@ static swift_result_t write_audio(swift_event * event, swift_event_t type, void return rv; } -static switch_status_t cepstral_speech_open(switch_speech_handle_t *sh, const char *voice_name, int rate, switch_speech_flag_t *flags) +static switch_status_t cepstral_speech_open(switch_speech_handle_t *sh, const char *voice_name, int rate, int channels, switch_speech_flag_t *flags) { cepstral_t *cepstral = switch_core_alloc(sh->memory_pool, sizeof(*cepstral)); char srate[25]; @@ -290,7 +290,6 @@ static switch_status_t cepstral_speech_read_tts(switch_speech_handle_t *sh, void if (!used && cepstral->done_gen) { - status = SWITCH_STATUS_BREAK; break; } diff --git a/src/mod/codecs/mod_opus/mod_opus.c b/src/mod/codecs/mod_opus/mod_opus.c index cb9b558a48..af10ca3371 100644 --- a/src/mod/codecs/mod_opus/mod_opus.c +++ b/src/mod/codecs/mod_opus/mod_opus.c @@ -143,6 +143,11 @@ static switch_status_t switch_opus_fmtp_parse(const char *fmtp, switch_codec_fmt codec_settings->samplerate = atoi(arg); codec_fmtp->actual_samples_per_second = codec_settings->samplerate; } + + if (!strcasecmp(data, "stereo")) { + codec_settings->stereo = atoi(arg); + codec_fmtp->stereo = codec_settings->stereo; + } if (!strcasecmp(data, "maxaveragebitrate")) { codec_settings->maxaveragebitrate = atoi(arg); @@ -202,36 +207,40 @@ static char *gen_fmtp(opus_codec_settings_t *settings, switch_memory_pool_t *poo char buf[256] = ""; if (settings->useinbandfec) { - snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), "useinbandfec=1;"); + snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), "useinbandfec=1; "); } if (settings->usedtx) { - snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), "usedtx=1;"); + snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), "usedtx=1; "); } if (settings->maxaveragebitrate) { - snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), "maxaveragebitrate=%d;", settings->maxaveragebitrate); + snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), "maxaveragebitrate=%d; ", settings->maxaveragebitrate); } if (settings->ptime) { - snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), "ptime=%d;", settings->ptime); + snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), "ptime=%d; ", settings->ptime); } if (settings->minptime) { - snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), "minptime=%d;", settings->minptime); + snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), "minptime=%d; ", settings->minptime); } - + if (settings->maxptime) { - snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), "maxptime=%d;", settings->maxptime); + snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), "maxptime=%d; ", settings->maxptime); } if (settings->samplerate) { - snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), "samplerate=%d;", settings->samplerate); + snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), "samplerate=%d; ", settings->samplerate); } - - if (end_of(buf) == ';') { - end_of(buf) = '\0'; + + if (settings->stereo) { + snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), "stereo=%d; ", settings->stereo); + } + + if (end_of(buf) == ' ') { + *(end_of_p(buf) - 1) = '\0'; } return switch_core_strdup(pool, buf); @@ -255,9 +264,8 @@ static switch_status_t switch_opus_init(switch_codec_t *codec, switch_codec_flag memset(&codec_fmtp, '\0', sizeof(struct switch_codec_fmtp)); codec_fmtp.private_info = &opus_codec_settings; switch_opus_fmtp_parse(codec->fmtp_in, &codec_fmtp); - codec->fmtp_out = gen_fmtp(&opus_codec_settings, codec->memory_pool); - + if (encoding) { /* come up with a way to specify these */ int bitrate_bps = OPUS_AUTO; @@ -268,7 +276,7 @@ static switch_status_t switch_opus_init(switch_codec_t *codec, switch_codec_flag context->encoder_object = opus_encoder_create(samplerate, codec->implementation->number_of_channels, - OPUS_APPLICATION_VOIP, &err); + codec->implementation->number_of_channels == 1 ? OPUS_APPLICATION_VOIP : OPUS_APPLICATION_AUDIO, &err); if (err != OPUS_OK) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Cannot create encoder: %s\n", opus_strerror(err)); @@ -358,17 +366,19 @@ static switch_status_t switch_opus_encode(switch_codec_t *codec, if (!context) { return SWITCH_STATUS_FALSE; } - - if (len > 1275) len = 1275; - - bytes = opus_encode(context->encoder_object, (void *) decoded_data, decoded_data_len / 2, (unsigned char *) encoded_data, len); - + + if (len > 2880) len = 2880; + + bytes = opus_encode(context->encoder_object, (void *) decoded_data, + decoded_data_len / 2 / codec->implementation->number_of_channels, (unsigned char *) encoded_data, len); + if (bytes > 0) { *encoded_data_len = (uint32_t) bytes; return SWITCH_STATUS_SUCCESS; } - - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Encoder Error!\n"); + + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Encoder Error: %s Decoded Datalen %u Codec NumberChans %u Len %u DecodedDate %p EncodedData %p ContextEncoderObject %p!\n", opus_strerror(bytes),decoded_data_len,codec->implementation->number_of_channels,len,(void *) decoded_data,(void *) encoded_data,(void *) context->encoder_object); + return SWITCH_STATUS_GENERR; } @@ -387,12 +397,13 @@ static switch_status_t switch_opus_decode(switch_codec_t *codec, } samples = opus_decode(context->decoder_object, (*flag & SFF_PLC) ? NULL : encoded_data, encoded_data_len, decoded_data, *decoded_data_len, 0); - + if (samples < 0) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Decoder Error: %s!\n", opus_strerror(samples)); return SWITCH_STATUS_GENERR; } - - *decoded_data_len = samples * 2; + + *decoded_data_len = samples * 2 * codec->implementation->number_of_channels; return SWITCH_STATUS_SUCCESS; } @@ -481,6 +492,27 @@ SWITCH_MODULE_LOAD_FUNCTION(mod_opus_load) switch_opus_decode, /* function to decode encoded data into raw data */ switch_opus_destroy); /* deinitalize a codec handle using this implementation */ + settings.stereo = 1; + dft_fmtp = gen_fmtp(&settings, pool); + + switch_core_codec_add_implementation(pool, codec_interface, SWITCH_CODEC_TYPE_AUDIO, /* enumeration defining the type of the codec */ + 116, /* the IANA code number */ + "opus",/* the IANA code name */ + dft_fmtp, /* default fmtp to send (can be overridden by the init function) */ + rate, /* samples transferred per second */ + rate, /* actual samples transferred per second */ + bits, /* bits transferred per second */ + mss, /* number of microseconds per frame */ + samples, /* number of samples per frame */ + bytes * 2, /* number of bytes per frame decompressed */ + 0, /* number of bytes per frame compressed */ + 2,/* number of channels represented */ + 1, /* number of frames per network packet */ + switch_opus_init, /* function to initialize a codec handle using this implementation */ + switch_opus_encode, /* function to encode raw data into encoded data */ + switch_opus_decode, /* function to decode encoded data into raw data */ + switch_opus_destroy); /* deinitalize a codec handle using this implementation */ + bytes *= 2; samples *= 2; mss *= 2; diff --git a/src/mod/formats/mod_local_stream/mod_local_stream.c b/src/mod/formats/mod_local_stream/mod_local_stream.c index 35ea59d841..5f4a1e0645 100644 --- a/src/mod/formats/mod_local_stream/mod_local_stream.c +++ b/src/mod/formats/mod_local_stream/mod_local_stream.c @@ -298,7 +298,7 @@ static void *SWITCH_THREAD_FUNC read_stream_thread(switch_thread_t *thread, void } } - switch_buffer_write(audio_buffer, abuf, olen * 2); + switch_buffer_write(audio_buffer, abuf, olen * 2 * source->channels); } } @@ -308,8 +308,8 @@ static void *SWITCH_THREAD_FUNC read_stream_thread(switch_thread_t *thread, void break; } - if (!is_open || used >= source->prebuf || (source->total && used > source->samples * 2)) { - used = switch_buffer_read(audio_buffer, dist_buf, source->samples * 2); + if (!is_open || used >= source->prebuf || (source->total && used > source->samples * 2 * source->channels)) { + used = switch_buffer_read(audio_buffer, dist_buf, source->samples * 2 * source->channels); if (source->total) { uint32_t bused = 0; switch_mutex_lock(source->mutex); @@ -548,7 +548,7 @@ static switch_status_t local_stream_file_read(switch_file_handle_t *handle, void { local_stream_context_t *context = handle->private_info; switch_size_t bytes = 0; - size_t need = *len * 2; + size_t need = *len * 2 * handle->real_channels; if (!context->source->ready) { *len = 0; @@ -557,13 +557,13 @@ static switch_status_t local_stream_file_read(switch_file_handle_t *handle, void switch_mutex_lock(context->audio_mutex); if ((bytes = switch_buffer_read(context->audio_buffer, data, need))) { - *len = bytes / 2; + *len = bytes / 2 / handle->real_channels; } else { if (need > 2560) { need = 2560; } memset(data, 255, need); - *len = need / 2; + *len = need / 2 / handle->real_channels; } switch_mutex_unlock(context->audio_mutex); handle->sample_count += *len; diff --git a/src/mod/formats/mod_shell_stream/mod_shell_stream.c b/src/mod/formats/mod_shell_stream/mod_shell_stream.c index 4c41e2e63a..07e0e41ce7 100644 --- a/src/mod/formats/mod_shell_stream/mod_shell_stream.c +++ b/src/mod/formats/mod_shell_stream/mod_shell_stream.c @@ -99,6 +99,8 @@ static switch_status_t shell_stream_file_open(switch_file_handle_t *handle, cons return SWITCH_STATUS_FALSE; } + handle->channels = 1; + context = switch_core_alloc(handle->memory_pool, sizeof(*context)); context->fds[0] = -1; diff --git a/src/mod/formats/mod_shout/mod_shout.c b/src/mod/formats/mod_shout/mod_shout.c index 662de882c3..68ea6b77b0 100644 --- a/src/mod/formats/mod_shout/mod_shout.c +++ b/src/mod/formats/mod_shout/mod_shout.c @@ -625,6 +625,9 @@ static switch_status_t shout_file_open(switch_file_handle_t *handle, const char char *err = NULL; const char *mpg123err = NULL; int portno = 0; + long rate = 0; + int channels = 0; + int encoding = 0; if ((context = switch_core_alloc(handle->memory_pool, sizeof(*context))) == 0) { return SWITCH_STATUS_MEMERR; @@ -659,9 +662,6 @@ static switch_status_t shout_file_open(switch_file_handle_t *handle, const char } if (handle->handler) { - if (mpg123_param(context->mh, MPG123_FLAGS, MPG123_SEEKBUFFER | MPG123_MONO_MIX, 0) != MPG123_OK) { - MPGERROR(); - } if (mpg123_open_feed(context->mh) != MPG123_OK) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Error opening mpg feed\n"); mpg123err = mpg123_strerror(context->mh); @@ -672,9 +672,7 @@ static switch_status_t shout_file_open(switch_file_handle_t *handle, const char launch_read_stream_thread(context); } else { handle->seekable = 1; - if (mpg123_param(context->mh, MPG123_FLAGS, MPG123_MONO_MIX, 0) != MPG123_OK) { - MPGERROR(); - } + if (mpg123_open(context->mh, path) != MPG123_OK) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Error opening %s\n", path); mpg123err = mpg123_strerror(context->mh); @@ -682,6 +680,12 @@ static switch_status_t shout_file_open(switch_file_handle_t *handle, const char } } + + + mpg123_getformat(context->mh, &rate, &channels, &encoding); + handle->channels = channels; + handle->samplerate = rate; + } else if (switch_test_flag(handle, SWITCH_FILE_FLAG_WRITE)) { if (!(context->gfp = lame_init())) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Could not allocate lame\n"); @@ -897,7 +901,7 @@ static switch_status_t shout_file_seek(switch_file_handle_t *handle, unsigned in static switch_status_t shout_file_read(switch_file_handle_t *handle, void *data, size_t *len) { shout_context_t *context = handle->private_info; - size_t rb = 0, bytes = *len * sizeof(int16_t), newbytes = 0; + size_t rb = 0, bytes = *len * sizeof(int16_t) * handle->channels, newbytes = 0; *len = 0; @@ -919,7 +923,7 @@ static switch_status_t shout_file_read(switch_file_handle_t *handle, void *data, /* switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "rb: %d, bytes: %d\n", (int) rb, (int) bytes); */ if (rb) { - *len = rb / sizeof(int16_t); + *len = rb / sizeof(int16_t) / handle->channels; } else { /* no data, so insert 1 second of silence */ newbytes = 2 * handle->samplerate; @@ -929,7 +933,7 @@ static switch_status_t shout_file_read(switch_file_handle_t *handle, void *data, switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Padding mp3 stream with 1s of empty audio. (%s)\n", context->stream_url); memset(data, 255, bytes); - *len = bytes / sizeof(int16_t); + *len = bytes / sizeof(int16_t) / handle->channels; } handle->sample_count += *len; diff --git a/src/mod/formats/mod_tone_stream/mod_tone_stream.c b/src/mod/formats/mod_tone_stream/mod_tone_stream.c index 3c6e69ac8e..06a5de98c2 100644 --- a/src/mod/formats/mod_tone_stream/mod_tone_stream.c +++ b/src/mod/formats/mod_tone_stream/mod_tone_stream.c @@ -67,6 +67,7 @@ static switch_status_t silence_stream_file_open(switch_file_handle_t *handle, co } } + handle->channels = 1; handle->private_info = sh; return SWITCH_STATUS_SUCCESS; @@ -153,6 +154,8 @@ static switch_status_t tone_stream_file_open(switch_file_handle_t *handle, const handle->samplerate = 8000; } + handle->channels = 1; + teletone_init_session(&ts, 0, teletone_handler, audio_buffer); ts.rate = handle->samplerate; ts.channels = 1; diff --git a/src/mod/formats/mod_vlc/mod_vlc.c b/src/mod/formats/mod_vlc/mod_vlc.c index 24488f8899..51996686dc 100644 --- a/src/mod/formats/mod_vlc/mod_vlc.c +++ b/src/mod/formats/mod_vlc/mod_vlc.c @@ -73,6 +73,7 @@ struct vlc_file_context { int samples; int playing; int samplerate; + int channels; int err; int pts; libvlc_instance_t *inst_out; @@ -113,7 +114,7 @@ void vlc_auto_play_callback(void *data, const void *samples, unsigned count, int switch_mutex_lock(context->audio_mutex); if (context->audio_buffer) { - if (!switch_buffer_write(context->audio_buffer, samples, count * 2)) { + if (!switch_buffer_write(context->audio_buffer, samples, count * 2 * context->channels)) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Buffer error\n"); } } @@ -145,7 +146,7 @@ int vlc_imem_get_callback(void *data, const char *cookie, int64_t *dts, int64_t context->samples = 0; if ( samples ) { - bytes = samples * 2; + bytes = samples * 2 * context->channels; *output = malloc(bytes); bytes = switch_buffer_read(context->audio_buffer, *output, bytes); switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "VLC imem samples: %d\n", samples); @@ -210,12 +211,14 @@ static switch_status_t vlc_file_open(switch_file_handle_t *handle, const char *p context->mp = libvlc_media_player_new_from_media(context->m); - if ( !handle->samplerate) + if (!handle->samplerate) { handle->samplerate = 16000; + } context->samplerate = handle->samplerate; - - libvlc_audio_set_format(context->mp, "S16N", context->samplerate, 1); + context->channels = handle->channels; + + libvlc_audio_set_format(context->mp, "S16N", context->samplerate, handle->channels); m_event_manager = libvlc_media_event_manager(context->m); libvlc_event_attach(m_event_manager, libvlc_MediaStateChanged, vlc_media_state_callback, (void *) context); @@ -246,7 +249,7 @@ static switch_status_t vlc_file_open(switch_file_handle_t *handle, const char *p opts[6] = "--rawaud-fourcc=s16l"; opts[7] = switch_mprintf("--rawaud-samplerate=%d", context->samplerate); opts[8] = switch_mprintf("--imem-data=%ld", context); - opts[9] = "--rawaud-channels=1"; + //opts[9] = "--rawaud-channels=1"; /* Prepare to write to an output stream. */ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "VLC open %s for writing\n", path); @@ -274,7 +277,7 @@ static switch_status_t vlc_file_open(switch_file_handle_t *handle, const char *p static switch_status_t vlc_file_read(switch_file_handle_t *handle, void *data, size_t *len) { vlc_file_context_t *context = handle->private_info; - size_t bytes = *len * sizeof(int16_t), read; + size_t bytes = *len * sizeof(int16_t) * handle->channels, read; libvlc_state_t status; if (!context) { @@ -310,7 +313,8 @@ static switch_status_t vlc_file_read(switch_file_handle_t *handle, void *data, s read = switch_buffer_read(context->audio_buffer, data, bytes); switch_mutex_unlock(context->audio_mutex); - status = libvlc_media_get_state(context->m); + status = libvlc_media_get_state(context->m); + if (!read && (status == libvlc_Stopped || status == libvlc_Ended || status == libvlc_Error)) { return SWITCH_STATUS_FALSE; } else if (!read) { @@ -318,8 +322,9 @@ static switch_status_t vlc_file_read(switch_file_handle_t *handle, void *data, s memset(data, 0, read); } - if (read) - *len = read/2; + if (read) { + *len = read / 2 / handle->channels; + } return SWITCH_STATUS_SUCCESS; } @@ -327,7 +332,7 @@ static switch_status_t vlc_file_read(switch_file_handle_t *handle, void *data, s static switch_status_t vlc_file_write(switch_file_handle_t *handle, void *data, size_t *len) { vlc_file_context_t *context = handle->private_info; - size_t bytes = *len * sizeof(int16_t); + size_t bytes = *len * sizeof(int16_t) * handle->channels; switch_mutex_lock(context->audio_mutex); context->samples += *len; diff --git a/src/mod/languages/mod_v8/src/fssession.cpp b/src/mod/languages/mod_v8/src/fssession.cpp index eeaef7894c..dce66a053d 100644 --- a/src/mod/languages/mod_v8/src/fssession.cpp +++ b/src/mod/languages/mod_v8/src/fssession.cpp @@ -982,7 +982,7 @@ switch_status_t FSSession::InitSpeechEngine(const char *engine, const char *voic return SWITCH_STATUS_FALSE; } - if (switch_core_speech_open(&this->_speech->sh, engine, voice, rate, interval, + if (switch_core_speech_open(&this->_speech->sh, engine, voice, rate, interval, read_codec->implementation->number_of_channels, &flags, switch_core_session_get_pool(this->_session)) != SWITCH_STATUS_SUCCESS) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Invalid TTS module!\n"); switch_core_codec_destroy(&this->_speech->codec); diff --git a/src/switch_core_codec.c b/src/switch_core_codec.c index 4b47e27c0f..63e33ec2b6 100644 --- a/src/switch_core_codec.c +++ b/src/switch_core_codec.c @@ -700,8 +700,9 @@ SWITCH_DECLARE(switch_status_t) switch_core_codec_init_with_bitrate(switch_codec switch_set_flag(codec, SWITCH_CODEC_FLAG_READY); return SWITCH_STATUS_SUCCESS; } else { - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Codec %s Exists but not at the desired implementation. %dhz %dms\n", codec_name, rate, - ms); + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Codec %s Exists but not at the desired implementation. %dhz %dms %dch\n", + codec_name, rate, ms, channels); + } UNPROTECT_INTERFACE(codec_interface); @@ -765,7 +766,7 @@ SWITCH_DECLARE(switch_status_t) switch_core_codec_decode(switch_codec_t *codec, } if (codec->implementation->encoded_bytes_per_packet) { - uint32_t frames = encoded_data_len / codec->implementation->encoded_bytes_per_packet; + uint32_t frames = encoded_data_len / codec->implementation->encoded_bytes_per_packet / codec->implementation->number_of_channels; if (frames && codec->implementation->decoded_bytes_per_packet * frames > *decoded_data_len) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "Buffer size sanity check failed! edl:%u ebpp:%u fr:%u ddl:%u\n", diff --git a/src/switch_core_file.c b/src/switch_core_file.c index 3c6a2f8494..66e5df1d6e 100644 --- a/src/switch_core_file.c +++ b/src/switch_core_file.c @@ -188,6 +188,11 @@ SWITCH_DECLARE(switch_status_t) switch_core_perform_file_open(const char *file, switch_goto_status(status, fail); } + fh->real_channels = fh->channels; + + if (channels) { + fh->channels = channels; + } if ((flags & SWITCH_FILE_FLAG_WRITE) && !is_stream && (status = switch_file_exists(file_path, fh->memory_pool)) != SWITCH_STATUS_SUCCESS) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "File [%s] not created!\n", file_path); @@ -216,12 +221,13 @@ SWITCH_DECLARE(switch_status_t) switch_core_perform_file_open(const char *file, if (fh->pre_buffer_datalen) { //switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Prebuffering %d bytes\n", (int)fh->pre_buffer_datalen); - switch_buffer_create_dynamic(&fh->pre_buffer, fh->pre_buffer_datalen * fh->channels, fh->pre_buffer_datalen * fh->channels / 2, 0); + switch_buffer_create_dynamic(&fh->pre_buffer, fh->pre_buffer_datalen * fh->channels, fh->pre_buffer_datalen * fh->channels, 0); fh->pre_buffer_data = switch_core_alloc(fh->memory_pool, fh->pre_buffer_datalen * fh->channels); } - if (fh->channels > 1 && (flags & SWITCH_FILE_FLAG_READ) && !(fh->flags & SWITCH_FILE_NOMUX)) { - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "File has %d channels, muxing to mono will occur.\n", fh->channels); + + if (fh->real_channels != fh->channels && (flags & SWITCH_FILE_FLAG_READ) && !(fh->flags & SWITCH_FILE_NOMUX)) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "File has %d channels, muxing to %d channel%s will occur.\n", fh->real_channels, fh->channels, fh->channels == 1 ? "" : "s"); } switch_set_flag(fh, SWITCH_FILE_OPEN); @@ -264,8 +270,8 @@ SWITCH_DECLARE(switch_status_t) switch_core_file_read(switch_file_handle_t *fh, return SWITCH_STATUS_FALSE; } - if (fh->buffer && switch_buffer_inuse(fh->buffer) >= *len * 2) { - *len = switch_buffer_read(fh->buffer, data, orig_len * 2) / 2; + if (fh->buffer && switch_buffer_inuse(fh->buffer) >= *len * 2 * fh->channels) { + *len = switch_buffer_read(fh->buffer, data, orig_len * 2 * fh->channels) / 2 / fh->channels; return *len == 0 ? SWITCH_STATUS_FALSE : SWITCH_STATUS_SUCCESS; } @@ -284,9 +290,9 @@ SWITCH_DECLARE(switch_status_t) switch_core_file_read(switch_file_handle_t *fh, int asis = switch_test_flag(fh, SWITCH_FILE_NATIVE); if (!switch_test_flag(fh, SWITCH_FILE_BUFFER_DONE)) { - rlen = asis ? fh->pre_buffer_datalen : fh->pre_buffer_datalen / 2; + rlen = asis ? fh->pre_buffer_datalen : fh->pre_buffer_datalen / 2 / fh->real_channels; - if (switch_buffer_inuse(fh->pre_buffer) < rlen * 2) { + if (switch_buffer_inuse(fh->pre_buffer) < rlen * 2 * fh->channels) { if ((status = fh->file_interface->file_read(fh, fh->pre_buffer_data, &rlen)) == SWITCH_STATUS_BREAK) { return SWITCH_STATUS_BREAK; } @@ -296,16 +302,16 @@ SWITCH_DECLARE(switch_status_t) switch_core_file_read(switch_file_handle_t *fh, switch_set_flag(fh, SWITCH_FILE_BUFFER_DONE); } else { fh->samples_in += rlen; - if (fh->channels > 1 && !switch_test_flag(fh, SWITCH_FILE_NOMUX)) { - switch_mux_channels((int16_t *) fh->pre_buffer_data, rlen, fh->channels); + if (fh->real_channels != fh->channels && !switch_test_flag(fh, SWITCH_FILE_NOMUX)) { + switch_mux_channels((int16_t *) fh->pre_buffer_data, rlen, fh->real_channels, fh->channels); } - switch_buffer_write(fh->pre_buffer, fh->pre_buffer_data, asis ? rlen : rlen * 2); + switch_buffer_write(fh->pre_buffer, fh->pre_buffer_data, asis ? rlen : rlen * 2 * fh->channels); } } } - rlen = switch_buffer_read(fh->pre_buffer, data, asis ? *len : *len * 2); - *len = asis ? rlen : rlen / 2; + rlen = switch_buffer_read(fh->pre_buffer, data, asis ? *len : *len * 2 * fh->channels); + *len = asis ? rlen : rlen / 2 / fh->channels; if (*len == 0) { switch_set_flag(fh, SWITCH_FILE_DONE); @@ -327,23 +333,21 @@ SWITCH_DECLARE(switch_status_t) switch_core_file_read(switch_file_handle_t *fh, fh->samples_in += *len; - if (fh->channels > 1 && !switch_test_flag(fh, SWITCH_FILE_NOMUX)) { - switch_mux_channels((int16_t *) data, *len, fh->channels); + if (fh->real_channels != fh->channels && !switch_test_flag(fh, SWITCH_FILE_NOMUX)) { + switch_mux_channels((int16_t *) data, *len, fh->real_channels, fh->channels); } - } - if (!switch_test_flag(fh, SWITCH_FILE_NATIVE) && fh->native_rate != fh->samplerate) { if (!fh->resampler) { if (switch_resample_create(&fh->resampler, - fh->native_rate, fh->samplerate, (uint32_t) orig_len, SWITCH_RESAMPLE_QUALITY, 1) != SWITCH_STATUS_SUCCESS) { + fh->native_rate, fh->samplerate, (uint32_t) orig_len, SWITCH_RESAMPLE_QUALITY, fh->channels) != SWITCH_STATUS_SUCCESS) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "Unable to create resampler!\n"); return SWITCH_STATUS_GENERR; } } - switch_resample_process(fh->resampler, data, (uint32_t) * len); + switch_resample_process(fh->resampler, data, (uint32_t) *len); if (fh->resampler->to_len < want || fh->resampler->to_len > orig_len) { if (!fh->buffer) { @@ -351,24 +355,24 @@ SWITCH_DECLARE(switch_status_t) switch_core_file_read(switch_file_handle_t *fh, switch_buffer_create_dynamic(&fh->buffer, factor, factor, 0); switch_assert(fh->buffer); } - if (!fh->dbuf || fh->dbuflen < fh->resampler->to_len * 2) { + if (!fh->dbuf || fh->dbuflen < fh->resampler->to_len * 2 * fh->channels) { void *mem; - fh->dbuflen = fh->resampler->to_len * 2; + fh->dbuflen = fh->resampler->to_len * 2 * fh->channels; mem = realloc(fh->dbuf, fh->dbuflen); switch_assert(mem); fh->dbuf = mem; } - switch_assert(fh->resampler->to_len * 2 <= fh->dbuflen); - memcpy((int16_t *) fh->dbuf, fh->resampler->to, fh->resampler->to_len * 2); - switch_buffer_write(fh->buffer, fh->dbuf, fh->resampler->to_len * 2); + switch_assert(fh->resampler->to_len * 2 * fh->channels <= fh->dbuflen); + memcpy((int16_t *) fh->dbuf, fh->resampler->to, fh->resampler->to_len * 2 * fh->channels); + switch_buffer_write(fh->buffer, fh->dbuf, fh->resampler->to_len * 2 * fh->channels); - if (switch_buffer_inuse(fh->buffer) < want * 2) { + if (switch_buffer_inuse(fh->buffer) < want * 2 * fh->channels) { *len = want; goto more; } - *len = switch_buffer_read(fh->buffer, data, orig_len * 2) / 2; + *len = switch_buffer_read(fh->buffer, data, orig_len * 2 * fh->channels) / 2 / fh->channels; } else { - memcpy(data, fh->resampler->to, fh->resampler->to_len * 2); + memcpy(data, fh->resampler->to, fh->resampler->to_len * 2 * fh->channels); *len = fh->resampler->to_len; } @@ -415,7 +419,7 @@ SWITCH_DECLARE(switch_status_t) switch_core_file_write(switch_file_handle_t *fh, switch_assert(mem); fh->dbuf = mem; } - switch_assert(fh->resampler->to_len * 2 <= fh->dbuflen); + switch_assert(fh->resampler->to_len * 2 *fh->channels <= fh->dbuflen); memcpy(fh->dbuf, fh->resampler->to, fh->resampler->to_len * 2 * fh->channels); data = fh->dbuf; } else { diff --git a/src/switch_core_io.c b/src/switch_core_io.c index eb294a6773..430993df1f 100644 --- a/src/switch_core_io.c +++ b/src/switch_core_io.c @@ -596,6 +596,15 @@ SWITCH_DECLARE(switch_status_t) switch_core_session_read_frame(switch_core_sessi status = SWITCH_STATUS_RESAMPLE; } + /* mux or demux to match */ + if (session->read_impl.number_of_channels != read_frame->codec->implementation->number_of_channels) { + uint32_t rlen = session->raw_read_frame.datalen / 2 / read_frame->codec->implementation->number_of_channels; + switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_WARNING, "%s MUX READ\n", switch_channel_get_name(session->channel)); + switch_mux_channels((int16_t *) session->raw_read_frame.data, rlen, + read_frame->codec->implementation->number_of_channels, session->read_impl.number_of_channels); + session->raw_write_frame.datalen = rlen * 2 * session->read_impl.number_of_channels; + } + switch (status) { case SWITCH_STATUS_RESAMPLE: if (!session->read_resampler) { @@ -604,7 +613,8 @@ SWITCH_DECLARE(switch_status_t) switch_core_session_read_frame(switch_core_sessi status = switch_resample_create(&session->read_resampler, read_frame->codec->implementation->actual_samples_per_second, session->read_impl.actual_samples_per_second, - session->read_impl.decoded_bytes_per_packet, SWITCH_RESAMPLE_QUALITY, 1); + session->read_impl.decoded_bytes_per_packet, SWITCH_RESAMPLE_QUALITY, + session->read_impl.number_of_channels); switch_mutex_unlock(session->resample_mutex); @@ -807,10 +817,10 @@ SWITCH_DECLARE(switch_status_t) switch_core_session_read_frame(switch_core_sessi if (session->read_resampler) { short *data = read_frame->data; switch_mutex_lock(session->resample_mutex); - switch_resample_process(session->read_resampler, data, (int) read_frame->datalen / 2); - memcpy(data, session->read_resampler->to, session->read_resampler->to_len * 2); + switch_resample_process(session->read_resampler, data, (int) read_frame->datalen / 2 / session->read_resampler->channels); + memcpy(data, session->read_resampler->to, session->read_resampler->to_len * 2 * session->read_resampler->channels); read_frame->samples = session->read_resampler->to_len; - read_frame->datalen = session->read_resampler->to_len * 2; + read_frame->datalen = session->read_resampler->to_len * 2 * session->read_resampler->channels; read_frame->rate = session->read_resampler->to_rate; switch_mutex_unlock(session->resample_mutex); } @@ -1206,6 +1216,14 @@ SWITCH_DECLARE(switch_status_t) switch_core_session_write_frame(switch_core_sess status = SWITCH_STATUS_RESAMPLE; } + /* mux or demux to match */ + if (session->write_impl.number_of_channels != frame->codec->implementation->number_of_channels) { + uint32_t rlen = session->raw_write_frame.datalen / 2 / frame->codec->implementation->number_of_channels; + switch_mux_channels((int16_t *) session->raw_write_frame.data, rlen, + frame->codec->implementation->number_of_channels, session->write_impl.number_of_channels); + session->raw_write_frame.datalen = rlen * 2 * session->write_impl.number_of_channels; + } + switch (status) { case SWITCH_STATUS_RESAMPLE: resample++; @@ -1216,7 +1234,7 @@ SWITCH_DECLARE(switch_status_t) switch_core_session_write_frame(switch_core_sess status = switch_resample_create(&session->write_resampler, frame->codec->implementation->actual_samples_per_second, session->write_impl.actual_samples_per_second, - session->write_impl.decoded_bytes_per_packet, SWITCH_RESAMPLE_QUALITY, 1); + session->write_impl.decoded_bytes_per_packet, SWITCH_RESAMPLE_QUALITY, session->write_impl.number_of_channels); switch_mutex_unlock(session->resample_mutex); @@ -1294,13 +1312,13 @@ SWITCH_DECLARE(switch_status_t) switch_core_session_write_frame(switch_core_sess switch_mutex_lock(session->resample_mutex); if (session->write_resampler) { - switch_resample_process(session->write_resampler, data, write_frame->datalen / 2); + switch_resample_process(session->write_resampler, data, write_frame->datalen / 2 / session->write_resampler->channels); - memcpy(data, session->write_resampler->to, session->write_resampler->to_len * 2); + memcpy(data, session->write_resampler->to, session->write_resampler->to_len * 2 * session->write_resampler->channels); write_frame->samples = session->write_resampler->to_len; - write_frame->datalen = write_frame->samples * 2; + write_frame->datalen = write_frame->samples * 2 * session->write_resampler->channels; write_frame->rate = session->write_resampler->to_rate; @@ -1527,7 +1545,8 @@ SWITCH_DECLARE(switch_status_t) switch_core_session_write_frame(switch_core_sess status = switch_resample_create(&session->write_resampler, frame->codec->implementation->actual_samples_per_second, session->write_impl.actual_samples_per_second, - session->write_impl.decoded_bytes_per_packet, SWITCH_RESAMPLE_QUALITY, 1); + session->write_impl.decoded_bytes_per_packet, SWITCH_RESAMPLE_QUALITY, + session->write_impl.number_of_channels); } switch_mutex_unlock(session->resample_mutex); @@ -1598,10 +1617,10 @@ SWITCH_DECLARE(switch_status_t) switch_core_session_write_frame(switch_core_sess short *data = write_frame->data; switch_mutex_lock(session->resample_mutex); if (session->read_resampler) { - switch_resample_process(session->read_resampler, data, write_frame->datalen / 2); - memcpy(data, session->read_resampler->to, session->read_resampler->to_len * 2); + switch_resample_process(session->read_resampler, data, write_frame->datalen / 2 / session->read_resampler->channels); + memcpy(data, session->read_resampler->to, session->read_resampler->to_len * 2 * session->read_resampler->channels); write_frame->samples = session->read_resampler->to_len; - write_frame->datalen = session->read_resampler->to_len * 2; + write_frame->datalen = session->read_resampler->to_len * 2 * session->read_resampler->channels; write_frame->rate = session->read_resampler->to_rate; } switch_mutex_unlock(session->resample_mutex); diff --git a/src/switch_core_speech.c b/src/switch_core_speech.c index 82cc3c75c2..95403dbf42 100644 --- a/src/switch_core_speech.c +++ b/src/switch_core_speech.c @@ -39,7 +39,8 @@ SWITCH_DECLARE(switch_status_t) switch_core_speech_open(switch_speech_handle_t *sh, const char *module_name, const char *voice_name, - unsigned int rate, unsigned int interval, switch_speech_flag_t *flags, switch_memory_pool_t *pool) + unsigned int rate, unsigned int interval, unsigned int channels, + switch_speech_flag_t *flags, switch_memory_pool_t *pool) { switch_status_t status; char buf[256] = ""; @@ -83,8 +84,10 @@ SWITCH_DECLARE(switch_status_t) switch_core_speech_open(switch_speech_handle_t * sh->samples = switch_samples_per_packet(rate, interval); sh->samplerate = rate; sh->native_rate = rate; + sh->channels = channels; + sh->real_channels = 1; - if ((status = sh->speech_interface->speech_open(sh, voice_name, rate, flags)) == SWITCH_STATUS_SUCCESS) { + if ((status = sh->speech_interface->speech_open(sh, voice_name, rate, channels, flags)) == SWITCH_STATUS_SUCCESS) { switch_set_flag(sh, SWITCH_SPEECH_FLAG_OPEN); } else { UNPROTECT_INTERFACE(sh->speech_interface); @@ -205,7 +208,8 @@ SWITCH_DECLARE(switch_status_t) switch_core_speech_read_tts(switch_speech_handle if (sh->buffer && (switch_buffer_inuse(sh->buffer) >= orig_len || switch_test_flag(sh, SWITCH_SPEECH_FLAG_DONE))) { if ((*datalen = switch_buffer_read(sh->buffer, data, orig_len))) { - return SWITCH_STATUS_SUCCESS; + status = SWITCH_STATUS_SUCCESS; + goto done; } } @@ -217,16 +221,17 @@ SWITCH_DECLARE(switch_status_t) switch_core_speech_read_tts(switch_speech_handle more: + *datalen = orig_len / sh->channels; + if ((status = sh->speech_interface->speech_read_tts(sh, data, datalen, flags)) != SWITCH_STATUS_SUCCESS) { switch_set_flag(sh, SWITCH_SPEECH_FLAG_DONE); goto top; } - if (sh->native_rate && sh->samplerate && sh->native_rate != sh->samplerate) { if (!sh->resampler) { if (switch_resample_create(&sh->resampler, - sh->native_rate, sh->samplerate, (uint32_t) orig_len, SWITCH_RESAMPLE_QUALITY, 1) != SWITCH_STATUS_SUCCESS) { + sh->native_rate, sh->samplerate, (uint32_t) orig_len / sh->channels, SWITCH_RESAMPLE_QUALITY, 1) != SWITCH_STATUS_SUCCESS) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "Unable to create resampler!\n"); return SWITCH_STATUS_GENERR; } @@ -261,6 +266,15 @@ SWITCH_DECLARE(switch_status_t) switch_core_speech_read_tts(switch_speech_handle } } + + done: + + if (sh->channels != sh->real_channels) { + uint32_t rlen = *datalen / 2; + switch_mux_channels((int16_t *) data, rlen, 1, sh->channels); + *datalen = rlen * 2 * sh->channels; + } + return status; } diff --git a/src/switch_ivr_async.c b/src/switch_ivr_async.c index 9a69776380..00a933efcd 100644 --- a/src/switch_ivr_async.c +++ b/src/switch_ivr_async.c @@ -767,7 +767,7 @@ static switch_bool_t write_displace_callback(switch_media_bug_t *bug, void *user st = switch_core_file_read(&dh->fh, buf, &len); - for (x = 0; x < (uint32_t) len; x++) { + for (x = 0; x < (uint32_t) len * dh->fh.channels; x++) { int32_t mixed = fp[x] + buf[x]; switch_normalize_to_16bit(mixed); fp[x] = (int16_t) mixed; @@ -779,6 +779,9 @@ static switch_bool_t write_displace_callback(switch_media_bug_t *bug, void *user } } + rframe->datalen = rframe->samples * 2 * dh->fh.channels; + + if (st != SWITCH_STATUS_SUCCESS || len == 0) { if (dh->loop) { uint32_t pos = 0; @@ -848,17 +851,20 @@ static switch_bool_t read_displace_callback(switch_media_bug_t *bug, void *user_ st = switch_core_file_read(&dh->fh, buf, &len); - for (x = 0; x < (uint32_t) len; x++) { + for (x = 0; x < (uint32_t) len * dh->fh.channels; x++) { int32_t mixed = fp[x] + buf[x]; switch_normalize_to_16bit(mixed); fp[x] = (int16_t) mixed; } + } else { st = switch_core_file_read(&dh->fh, rframe->data, &len); rframe->samples = (uint32_t) len; - rframe->datalen = rframe->samples * 2; } + rframe->datalen = rframe->samples * 2 * dh->fh.channels; + + if (st != SWITCH_STATUS_SUCCESS || len == 0) { if (dh->loop) { uint32_t pos = 0; diff --git a/src/switch_ivr_play_say.c b/src/switch_ivr_play_say.c index b07aee0ef0..9ac573d5dd 100644 --- a/src/switch_ivr_play_say.c +++ b/src/switch_ivr_play_say.c @@ -737,13 +737,13 @@ SWITCH_DECLARE(switch_status_t) switch_ivr_record_file(switch_core_session_t *se if (!switch_test_flag(fh, SWITCH_FILE_PAUSE) && !switch_test_flag(read_frame, SFF_CNG)) { int16_t *data = read_frame->data; - len = (switch_size_t) asis ? read_frame->datalen : read_frame->datalen / 2; + len = (switch_size_t) asis ? read_frame->datalen : read_frame->datalen / 2 / fh->channels; if (switch_core_file_write(fh, data, &len) != SWITCH_STATUS_SUCCESS) { break; } } else if (switch_test_flag(read_frame, SFF_CNG) && fill_cng) { - len = write_frame.datalen / 2; + len = write_frame.datalen / 2 / fh->channels; if (switch_core_file_write(fh, write_frame.data, &len) != SWITCH_STATUS_SUCCESS) { break; } @@ -1301,14 +1301,17 @@ SWITCH_DECLARE(switch_status_t) switch_ivr_play_file(switch_core_session_t *sess codec_name, NULL, fh->samplerate, - interval, 1, SWITCH_CODEC_FLAG_ENCODE | SWITCH_CODEC_FLAG_DECODE, NULL, pool) == SWITCH_STATUS_SUCCESS) { + interval, read_impl.number_of_channels, + SWITCH_CODEC_FLAG_ENCODE | SWITCH_CODEC_FLAG_DECODE, NULL, pool) == SWITCH_STATUS_SUCCESS) { switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), - SWITCH_LOG_DEBUG, "Codec Activated %s@%uhz %u channels %dms\n", codec_name, fh->samplerate, fh->channels, interval); + SWITCH_LOG_DEBUG, "Codec Activated %s@%uhz %u channels %dms\n", + codec_name, fh->samplerate, read_impl.number_of_channels, interval); } else { switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, - "Raw Codec Activation Failed %s@%uhz %u channels %dms\n", codec_name, fh->samplerate, fh->channels, interval); + "Raw Codec Activation Failed %s@%uhz %u channels %dms\n", codec_name, + fh->samplerate, read_impl.number_of_channels, interval); switch_core_session_io_write_lock(session); switch_channel_set_private(channel, "__fh", NULL); switch_core_session_io_rwunlock(session); @@ -1339,7 +1342,7 @@ SWITCH_DECLARE(switch_status_t) switch_ivr_play_file(switch_core_session_t *sess uint32_t len; len = samples * 2; - if (switch_core_timer_init(&timer, timer_name, interval, samples, pool) != SWITCH_STATUS_SUCCESS) { + if (switch_core_timer_init(&timer, timer_name, interval, samples / codec.implementation->number_of_channels, pool) != SWITCH_STATUS_SUCCESS) { switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR, "Setup timer failed!\n"); switch_core_codec_destroy(&codec); switch_core_session_io_write_lock(session); @@ -1351,7 +1354,8 @@ SWITCH_DECLARE(switch_status_t) switch_ivr_play_file(switch_core_session_t *sess continue; } switch_core_timer_sync(&timer); // Sync timer - switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "Setup timer success %u bytes per %d ms!\n", len, interval); + switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, + "Setup timer success %u bytes per %d ms! %d ch\n", len, interval, codec.implementation->number_of_channels); } write_frame.rate = fh->samplerate; @@ -1445,7 +1449,7 @@ SWITCH_DECLARE(switch_status_t) switch_ivr_play_file(switch_core_session_t *sess } } - buflen = FILE_STARTSAMPLES * sizeof(*abuf) * fh->cur_channels; + buflen = FILE_STARTSAMPLES * sizeof(*abuf) * fh->cur_channels ? fh->cur_channels : fh->channels; if (buflen > write_frame.buflen) { abuf = realloc(abuf, buflen); @@ -1502,10 +1506,11 @@ SWITCH_DECLARE(switch_status_t) switch_ivr_play_file(switch_core_session_t *sess olen /= 2; } switch_set_flag(fh, SWITCH_FILE_BREAK_ON_CHANGE); + if ((rstatus = switch_core_file_read(fh, abuf, &olen)) == SWITCH_STATUS_BREAK) { continue; } - + if (rstatus != SWITCH_STATUS_SUCCESS) { eof++; continue; @@ -1528,7 +1533,7 @@ SWITCH_DECLARE(switch_status_t) switch_ivr_play_file(switch_core_session_t *sess last_native = test_native; - switch_buffer_write(fh->audio_buffer, abuf, switch_test_flag(fh, SWITCH_FILE_NATIVE) ? olen : olen * 2); + switch_buffer_write(fh->audio_buffer, abuf, switch_test_flag(fh, SWITCH_FILE_NATIVE) ? olen : olen * 2 * fh->channels); olen = switch_buffer_read(fh->audio_buffer, abuf, framelen); fh->offset_pos += (uint32_t)(olen / 2); @@ -2142,14 +2147,12 @@ SWITCH_DECLARE(switch_status_t) switch_ivr_speak_text_handle(switch_core_session switch_codec_t *codec, switch_timer_t *timer, char *text, switch_input_args_t *args) { switch_channel_t *channel = switch_core_session_get_channel(session); - short abuf[960]; + short abuf[SWITCH_RECOMMENDED_BUFFER_SIZE]; switch_dtmf_t dtmf = { 0 }; uint32_t len = 0; switch_size_t ilen = 0; switch_frame_t write_frame = { 0 }; - int x; int done = 0; - int lead_in_out = 10; switch_status_t status = SWITCH_STATUS_SUCCESS; switch_speech_flag_t flags = SWITCH_SPEECH_FLAG_NONE; switch_size_t extra = 0; @@ -2174,7 +2177,7 @@ SWITCH_DECLARE(switch_status_t) switch_ivr_speak_text_handle(switch_core_session write_frame.data = abuf; write_frame.buflen = sizeof(abuf); - len = sh->samples * 2; + len = sh->samples * 2 * sh->channels; flags = 0; @@ -2227,7 +2230,6 @@ SWITCH_DECLARE(switch_status_t) switch_ivr_speak_text_handle(switch_core_session text = NULL; write_frame.rate = sh->rate; - memset(write_frame.data, 0, len); write_frame.datalen = len; write_frame.samples = len / 2; @@ -2235,23 +2237,14 @@ SWITCH_DECLARE(switch_status_t) switch_ivr_speak_text_handle(switch_core_session switch_assert(codec->implementation != NULL); - for (x = 0; !done && x < lead_in_out; x++) { - switch_yield(codec->implementation->microseconds_per_packet); - if (timer) { - write_frame.timestamp = timer->samplecount; - } - if (switch_core_session_write_frame(session, &write_frame, SWITCH_IO_FLAG_NONE, 0) != SWITCH_STATUS_SUCCESS) { - done = 1; - break; - } - } - switch_channel_audio_sync(channel); - ilen = len; + for (;;) { switch_event_t *event; + ilen = len; + if (!switch_channel_ready(channel)) { status = SWITCH_STATUS_FALSE; break; @@ -2348,23 +2341,11 @@ SWITCH_DECLARE(switch_status_t) switch_ivr_speak_text_handle(switch_core_session continue; } + flags = SWITCH_SPEECH_FLAG_BLOCKING; status = switch_core_speech_read_tts(sh, abuf, &ilen, &flags); if (status != SWITCH_STATUS_SUCCESS) { - write_frame.datalen = (uint32_t) codec->implementation->decoded_bytes_per_packet; - write_frame.samples = (uint32_t) (write_frame.datalen / 2); - memset(write_frame.data, 0, write_frame.datalen); - for (x = 0; !done && x < lead_in_out; x++) { - switch_yield(codec->implementation->microseconds_per_packet); - if (timer) { - write_frame.timestamp = timer->samplecount; - } - if (switch_core_session_write_frame(session, &write_frame, SWITCH_IO_FLAG_NONE, 0) != SWITCH_STATUS_SUCCESS) { - done = 1; - break; - } - } if (status == SWITCH_STATUS_BREAK) { status = SWITCH_STATUS_SUCCESS; } @@ -2376,7 +2357,7 @@ SWITCH_DECLARE(switch_status_t) switch_ivr_speak_text_handle(switch_core_session } write_frame.datalen = (uint32_t) ilen; - write_frame.samples = (uint32_t) (ilen / 2); + write_frame.samples = (uint32_t) (ilen / 2 / sh->channels); if (timer) { write_frame.timestamp = timer->samplecount; } @@ -2466,6 +2447,7 @@ SWITCH_DECLARE(switch_status_t) switch_ivr_speak_text(switch_core_session_t *ses switch_channel_t *channel = switch_core_session_get_channel(session); uint32_t rate = 0; int interval = 0; + uint32_t channels; switch_frame_t write_frame = { 0 }; switch_timer_t ltimer, *timer; switch_codec_t lcodec, *codec; @@ -2519,10 +2501,11 @@ SWITCH_DECLARE(switch_status_t) switch_ivr_speak_text(switch_core_session_t *ses rate = read_impl.actual_samples_per_second; interval = read_impl.microseconds_per_packet / 1000; + channels = read_impl.number_of_channels; if (need_create) { memset(sh, 0, sizeof(*sh)); - if ((status = switch_core_speech_open(sh, tts_name, voice_name, (uint32_t) rate, interval, &flags, NULL)) != SWITCH_STATUS_SUCCESS) { + if ((status = switch_core_speech_open(sh, tts_name, voice_name, (uint32_t) rate, interval, read_impl.number_of_channels, &flags, NULL)) != SWITCH_STATUS_SUCCESS) { switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR, "Invalid TTS module!\n"); switch_core_session_reset(session, SWITCH_TRUE, SWITCH_TRUE); switch_ivr_clear_speech_cache(session); @@ -2543,11 +2526,11 @@ SWITCH_DECLARE(switch_status_t) switch_ivr_speak_text(switch_core_session_t *ses switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "OPEN TTS %s\n", tts_name); codec_name = "L16"; - + if (need_create) { if (switch_core_codec_init(codec, codec_name, - NULL, (int) rate, interval, 1, SWITCH_CODEC_FLAG_ENCODE | SWITCH_CODEC_FLAG_DECODE, NULL, + NULL, (int) rate, interval, channels, SWITCH_CODEC_FLAG_ENCODE | SWITCH_CODEC_FLAG_DECODE, NULL, pool) == SWITCH_STATUS_SUCCESS) { switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "Raw Codec Activated\n"); } else { diff --git a/src/switch_pcm.c b/src/switch_pcm.c index 36ee0e7796..d62e5f7337 100644 --- a/src/switch_pcm.c +++ b/src/switch_pcm.c @@ -364,6 +364,25 @@ SWITCH_MODULE_LOAD_FUNCTION(core_pcm_load) switch_proxy_decode, /* function to decode encoded data into raw data */ switch_proxy_destroy); /* deinitalize a codec handle using this implementation */ + SWITCH_ADD_CODEC(codec_interface, "PROXY PASS-THROUGH"); + switch_core_codec_add_implementation(pool, codec_interface, SWITCH_CODEC_TYPE_AUDIO, /* enumeration defining the type of the codec */ + 0, /* the IANA code number */ + "PROXY", /* the IANA code name */ + NULL, /* default fmtp to send (can be overridden by the init function) */ + 8000, /* samples transferred per second */ + 8000, /* actual samples transferred per second */ + 0, /* bits transferred per second */ + 20000, /* number of microseconds per frame */ + 160, /* number of samples per frame */ + 320 * 2, /* number of bytes per frame decompressed */ + 320 * 2, /* number of bytes per frame compressed */ + 2, /* number of channels represented */ + 1, /* number of frames per network packet */ + switch_proxy_init, /* function to initialize a codec handle using this implementation */ + switch_proxy_encode, /* function to encode raw data into encoded data */ + switch_proxy_decode, /* function to decode encoded data into raw data */ + switch_proxy_destroy); /* deinitalize a codec handle using this implementation */ + SWITCH_ADD_CODEC(codec_interface, "RAW Signed Linear (16 bit)"); for (counta = 1; counta <= 3; counta++) { @@ -377,6 +396,11 @@ SWITCH_MODULE_LOAD_FUNCTION(core_pcm_load) SWITCH_CODEC_TYPE_AUDIO, 70, "L16", NULL, rate, rate, bps, mpf * countb, spf * countb, bpf * countb, ebpf * countb, 1, spf * countb, switch_raw_init, switch_raw_encode, switch_raw_decode, switch_raw_destroy); + + switch_core_codec_add_implementation(pool, codec_interface, + SWITCH_CODEC_TYPE_AUDIO, 70, "L16", NULL, rate, rate, bps, + mpf * countb, spf * countb, bpf * countb * 2, ebpf * countb, 2, spf * countb, + switch_raw_init, switch_raw_encode, switch_raw_decode, switch_raw_destroy); } rate = rate * 2; bps = bps * 2; @@ -408,6 +432,24 @@ SWITCH_MODULE_LOAD_FUNCTION(core_pcm_load) switch_raw_decode, /* function to decode encoded data into raw data */ switch_raw_destroy); /* deinitalize a codec handle using this implementation */ + switch_core_codec_add_implementation(pool, codec_interface, SWITCH_CODEC_TYPE_AUDIO, /* enumeration defining the type of the codec */ + 70, /* the IANA code number */ + "L16", /* the IANA code name */ + NULL, /* default fmtp to send (can be overridden by the init function) */ + 12000, /* samples transferred per second */ + 12000, /* actual samples transferred per second */ + 192000 * 2, /* bits transferred per second */ + ms_per_frame, /* number of microseconds per frame */ + samples_per_frame, /* number of samples per frame */ + bytes_per_frame * 2, /* number of bytes per frame decompressed */ + bytes_per_frame * 2, /* number of bytes per frame compressed */ + 2, /* number of channels represented */ + 1, /* number of frames per network packet */ + switch_raw_init, /* function to initialize a codec handle using this implementation */ + switch_raw_encode, /* function to encode raw data into encoded data */ + switch_raw_decode, /* function to decode encoded data into raw data */ + switch_raw_destroy); /* deinitalize a codec handle using this implementation */ + samples_per_frame += 240; bytes_per_frame += 480; ms_per_frame += 20000; @@ -437,6 +479,24 @@ SWITCH_MODULE_LOAD_FUNCTION(core_pcm_load) switch_raw_decode, /* function to decode encoded data into raw data */ switch_raw_destroy); /* deinitalize a codec handle using this implementation */ + switch_core_codec_add_implementation(pool, codec_interface, SWITCH_CODEC_TYPE_AUDIO, /* enumeration defining the type of the codec */ + 70, /* the IANA code number */ + "L16", /* the IANA code name */ + NULL, /* default fmtp to send (can be overridden by the init function) */ + 24000, /* samples transferred per second */ + 24000, /* actual samples transferred per second */ + 384000 * 2, /* bits transferred per second */ + ms_per_frame, /* number of microseconds per frame */ + samples_per_frame, /* number of samples per frame */ + bytes_per_frame * 2, /* number of bytes per frame decompressed */ + bytes_per_frame * 2, /* number of bytes per frame compressed */ + 2, /* number of channels represented */ + 1, /* number of frames per network packet */ + switch_raw_init, /* function to initialize a codec handle using this implementation */ + switch_raw_encode, /* function to encode raw data into encoded data */ + switch_raw_decode, /* function to decode encoded data into raw data */ + switch_raw_destroy); /* deinitalize a codec handle using this implementation */ + samples_per_frame += 480; bytes_per_frame += 960; ms_per_frame += 20000; @@ -468,6 +528,24 @@ SWITCH_MODULE_LOAD_FUNCTION(core_pcm_load) switch_raw_decode, /* function to decode encoded data into raw data */ switch_raw_destroy); /* deinitalize a codec handle using this implementation */ + switch_core_codec_add_implementation(pool, codec_interface, SWITCH_CODEC_TYPE_AUDIO, /* enumeration defining the type of the codec */ + 70, /* the IANA code number */ + "L16", /* the IANA code name */ + NULL, /* default fmtp to send (can be overridden by the init function) */ + 48000, /* samples transferred per second */ + 48000, /* actual samples transferred per second */ + 768000 * 2, /* bits transferred per second */ + ms_per_frame, /* number of microseconds per frame */ + samples_per_frame * 2, /* number of samples per frame */ + bytes_per_frame * 2, /* number of bytes per frame decompressed */ + bytes_per_frame * 2, /* number of bytes per frame compressed */ + 2, /* number of channels represented */ + 1, /* number of frames per network packet */ + switch_raw_init, /* function to initialize a codec handle using this implementation */ + switch_raw_encode, /* function to encode raw data into encoded data */ + switch_raw_decode, /* function to decode encoded data into raw data */ + switch_raw_destroy); /* deinitalize a codec handle using this implementation */ + samples_per_frame += 96; bytes_per_frame += 192; ms_per_frame += 2000; @@ -498,6 +576,24 @@ SWITCH_MODULE_LOAD_FUNCTION(core_pcm_load) switch_raw_decode, /* function to decode encoded data into raw data */ switch_raw_destroy); /* deinitalize a codec handle using this implementation */ + switch_core_codec_add_implementation(pool, codec_interface, SWITCH_CODEC_TYPE_AUDIO, /* enumeration defining the type of the codec */ + 70, /* the IANA code number */ + "L16", /* the IANA code name */ + NULL, /* default fmtp to send (can be overridden by the init function) */ + 8000, /* samples transferred per second */ + 8000, /* actual samples transferred per second */ + 128000 * 2, /* bits transferred per second */ + ms_per_frame, /* number of microseconds per frame */ + samples_per_frame, /* number of samples per frame */ + bytes_per_frame * 2, /* number of bytes per frame decompressed */ + bytes_per_frame * 2, /* number of bytes per frame compressed */ + 2, /* number of channels represented */ + 1, /* number of frames per network packet */ + switch_raw_init, /* function to initialize a codec handle using this implementation */ + switch_raw_encode, /* function to encode raw data into encoded data */ + switch_raw_decode, /* function to decode encoded data into raw data */ + switch_raw_destroy); /* deinitalize a codec handle using this implementation */ + samples_per_frame += 16; bytes_per_frame += 32; ms_per_frame += 2000; @@ -527,6 +623,24 @@ SWITCH_MODULE_LOAD_FUNCTION(core_pcm_load) switch_raw_decode, /* function to decode encoded data into raw data */ switch_raw_destroy); /* deinitalize a codec handle using this implementation */ + switch_core_codec_add_implementation(pool, codec_interface, SWITCH_CODEC_TYPE_AUDIO, /* enumeration defining the type of the codec */ + 70, /* the IANA code number */ + "L16", /* the IANA code name */ + NULL, /* default fmtp to send (can be overridden by the init function) */ + 16000, /* samples transferred per second */ + 16000, /* actual samples transferred per second */ + 256000 * 2, /* bits transferred per second */ + ms_per_frame, /* number of microseconds per frame */ + samples_per_frame, /* number of samples per frame */ + bytes_per_frame * 2, /* number of bytes per frame decompressed */ + bytes_per_frame * 2, /* number of bytes per frame compressed */ + 2, /* number of channels represented */ + 1, /* number of frames per network packet */ + switch_raw_init, /* function to initialize a codec handle using this implementation */ + switch_raw_encode, /* function to encode raw data into encoded data */ + switch_raw_decode, /* function to decode encoded data into raw data */ + switch_raw_destroy); /* deinitalize a codec handle using this implementation */ + samples_per_frame += 32; bytes_per_frame += 64; ms_per_frame += 2000; @@ -557,6 +671,24 @@ SWITCH_MODULE_LOAD_FUNCTION(core_pcm_load) switch_raw_decode, /* function to decode encoded data into raw data */ switch_raw_destroy); /* deinitalize a codec handle using this implementation */ + switch_core_codec_add_implementation(pool, codec_interface, SWITCH_CODEC_TYPE_AUDIO, /* enumeration defining the type of the codec */ + 70, /* the IANA code number */ + "L16", /* the IANA code name */ + NULL, /* default fmtp to send (can be overridden by the init function) */ + 32000, /* samples transferred per second */ + 32000, /* actual samples transferred per second */ + 512000 * 2, /* bits transferred per second */ + ms_per_frame, /* number of microseconds per frame */ + samples_per_frame, /* number of samples per frame */ + bytes_per_frame * 2, /* number of bytes per frame decompressed */ + bytes_per_frame * 2, /* number of bytes per frame compressed */ + 2, /* number of channels represented */ + 1, /* number of frames per network packet */ + switch_raw_init, /* function to initialize a codec handle using this implementation */ + switch_raw_encode, /* function to encode raw data into encoded data */ + switch_raw_decode, /* function to decode encoded data into raw data */ + switch_raw_destroy); /* deinitalize a codec handle using this implementation */ + samples_per_frame += 64; bytes_per_frame += 128; ms_per_frame += 2000; @@ -585,6 +717,25 @@ SWITCH_MODULE_LOAD_FUNCTION(core_pcm_load) switch_raw_encode, /* function to encode raw data into encoded data */ switch_raw_decode, /* function to decode encoded data into raw data */ switch_raw_destroy); /* deinitalize a codec handle using this implementation */ + + switch_core_codec_add_implementation(pool, codec_interface, SWITCH_CODEC_TYPE_AUDIO, /* enumeration defining the type of the codec */ + 70, /* the IANA code number */ + "L16", /* the IANA code name */ + NULL, /* default fmtp to send (can be overridden by the init function) */ + 48000, /* samples transferred per second */ + 48000, /* actual samples transferred per second */ + 768000 * 2, /* bits transferred per second */ + ms_per_frame, /* number of microseconds per frame */ + samples_per_frame * 2, /* number of samples per frame */ + bytes_per_frame * 2, /* number of bytes per frame decompressed */ + bytes_per_frame * 2, /* number of bytes per frame compressed */ + 2, /* number of channels represented */ + 1, /* number of frames per network packet */ + switch_raw_init, /* function to initialize a codec handle using this implementation */ + switch_raw_encode, /* function to encode raw data into encoded data */ + switch_raw_decode, /* function to decode encoded data into raw data */ + switch_raw_destroy); /* deinitalize a codec handle using this implementation */ + samples_per_frame += 480; bytes_per_frame += 960; ms_per_frame += 10000; @@ -608,6 +759,24 @@ SWITCH_MODULE_LOAD_FUNCTION(core_pcm_load) switch_raw_decode, /* function to decode encoded data into raw data */ switch_raw_destroy); /* deinitalize a codec handle using this implementation */ + switch_core_codec_add_implementation(pool, codec_interface, SWITCH_CODEC_TYPE_AUDIO, /* enumeration defining the type of the codec */ + 70, /* the IANA code number */ + "L16", /* the IANA code name */ + NULL, /* default fmtp to send (can be overridden by the init function) */ + 22050, /* samples transferred per second */ + 22050, /* actual samples transferred per second */ + 352800 * 2, /* bits transferred per second */ + 20000, /* number of microseconds per frame */ + 441, /* number of samples per frame */ + 882 * 2, /* number of bytes per frame decompressed */ + 882 * 2, /* number of bytes per frame compressed */ + 2, /* number of channels represented */ + 1, /* number of frames per network packet */ + switch_raw_init, /* function to initialize a codec handle using this implementation */ + switch_raw_encode, /* function to encode raw data into encoded data */ + switch_raw_decode, /* function to decode encoded data into raw data */ + switch_raw_destroy); /* deinitalize a codec handle using this implementation */ + switch_core_codec_add_implementation(pool, codec_interface, SWITCH_CODEC_TYPE_AUDIO, /* enumeration defining the type of the codec */ 70, /* the IANA code number */ "L16", /* the IANA code name */ @@ -626,6 +795,24 @@ SWITCH_MODULE_LOAD_FUNCTION(core_pcm_load) switch_raw_decode, /* function to decode encoded data into raw data */ switch_raw_destroy); /* deinitalize a codec handle using this implementation */ + switch_core_codec_add_implementation(pool, codec_interface, SWITCH_CODEC_TYPE_AUDIO, /* enumeration defining the type of the codec */ + 70, /* the IANA code number */ + "L16", /* the IANA code name */ + NULL, /* default fmtp to send (can be overridden by the init function) */ + 11025, /* samples transferred per second */ + 11025, /* actual samples transferred per second */ + 176400 * 2, /* bits transferred per second */ + 40000, /* number of microseconds per frame */ + 441, /* number of samples per frame */ + 882 * 2, /* number of bytes per frame decompressed */ + 882 * 2, /* number of bytes per frame compressed */ + 2, /* number of channels represented */ + 1, /* number of frames per network packet */ + switch_raw_init, /* function to initialize a codec handle using this implementation */ + switch_raw_encode, /* function to encode raw data into encoded data */ + switch_raw_decode, /* function to decode encoded data into raw data */ + switch_raw_destroy); /* deinitalize a codec handle using this implementation */ + switch_core_codec_add_implementation(pool, codec_interface, SWITCH_CODEC_TYPE_AUDIO, /* enumeration defining the type of the codec */ 70, /* the IANA code number */ @@ -645,7 +832,23 @@ SWITCH_MODULE_LOAD_FUNCTION(core_pcm_load) switch_raw_decode, /* function to decode encoded data into raw data */ switch_raw_destroy); /* deinitalize a codec handle using this implementation */ - + switch_core_codec_add_implementation(pool, codec_interface, SWITCH_CODEC_TYPE_AUDIO, /* enumeration defining the type of the codec */ + 70, /* the IANA code number */ + "L16", /* the IANA code name */ + NULL, /* default fmtp to send (can be overridden by the init function) */ + 11025, /* samples transferred per second */ + 11025, /* actual samples transferred per second */ + 176400 * 2, /* bits transferred per second */ + 32000, /* number of microseconds per frame */ + 256, /* number of samples per frame */ + 512 * 2, /* number of bytes per frame decompressed */ + 512 * 2, /* number of bytes per frame compressed */ + 2, /* number of channels represented */ + 1, /* number of frames per network packet */ + switch_raw_init, /* function to initialize a codec handle using this implementation */ + switch_raw_encode, /* function to encode raw data into encoded data */ + switch_raw_decode, /* function to decode encoded data into raw data */ + switch_raw_destroy); /* deinitalize a codec handle using this implementation */ /* indicate that the module should continue to be loaded */ diff --git a/src/switch_resample.c b/src/switch_resample.c index 2363ace9c8..a0b7249763 100644 --- a/src/switch_resample.c +++ b/src/switch_resample.c @@ -78,7 +78,8 @@ SWITCH_DECLARE(switch_status_t) switch_resample_perform_create(switch_audio_resa resampler->factor = (lto_rate / lfrom_rate); resampler->rfactor = (lfrom_rate / lto_rate); resampler->to_size = resample_buffer(to_rate, from_rate, (uint32_t) to_size); - resampler->to = malloc(resampler->to_size * sizeof(int16_t)); + resampler->to = malloc(resampler->to_size * sizeof(int16_t) * channels); + resampler->channels = channels; return SWITCH_STATUS_SUCCESS; } @@ -271,18 +272,55 @@ SWITCH_DECLARE(uint32_t) switch_unmerge_sln(int16_t *data, uint32_t samples, int return x; } -SWITCH_DECLARE(void) switch_mux_channels(int16_t *data, switch_size_t samples, uint32_t channels) +SWITCH_DECLARE(void) switch_mux_channels(int16_t *data, switch_size_t samples, uint32_t orig_channels, uint32_t channels) { switch_size_t i = 0; uint32_t j = 0; - for (i = 0; i < samples; i++) { - int32_t z = 0; - for (j = 0; j < channels; j++) { - z += data[i * channels + j]; - switch_normalize_to_16bit(z); - data[i] = (int16_t) z; + switch_assert(channels < 11); + + if (orig_channels > channels) { + for (i = 0; i < samples; i++) { + int32_t z = 0; + for (j = 0; j < orig_channels; j++) { + z += data[i * orig_channels + j]; + switch_normalize_to_16bit(z); + data[i] = (int16_t) z; + } } + } else if (orig_channels < channels) { + + /* interesting problem... take a give buffer and double up every sample in the buffer without using any other buffer..... + This way beats the other i think bacause there is no malloc but I do have to copy the data twice */ +#if 1 + uint32_t k = 0, len = samples * orig_channels; + + for (i = 0; i < len; i++) { + data[i+len] = data[i]; + } + + for (i = 0; i < samples; i++) { + for (j = 0; j < channels; j++) { + data[k++] = data[i + samples]; + } + } + +#else + uint32_t k = 0, len = samples * 2 * orig_channels; + int16_t *orig = NULL; + + switch_zmalloc(orig, len); + memcpy(orig, data, len); + + for (i = 0; i < samples; i++) { + for (j = 0; j < channels; j++) { + data[k++] = orig[i]; + } + } + + free(orig); +#endif + } }