@ -46,6 +46,8 @@ ASTERISK_REGISTER_FILE()
# define AST_AUDIOHOOK_SYNC_TOLERANCE 100 /*!< Tolerance in milliseconds for audiohooks synchronization */
# define AST_AUDIOHOOK_SMALL_QUEUE_TOLERANCE 100 /*!< When small queue is enabled, this is the maximum amount of audio that can remain queued at a time. */
# define DEFAULT_INTERNAL_SAMPLE_RATE 8000
struct ast_audiohook_translate {
struct ast_trans_pvt * trans_pvt ;
struct ast_format * format ;
@ -117,7 +119,7 @@ int ast_audiohook_init(struct ast_audiohook *audiohook, enum ast_audiohook_type
audiohook - > init_flags = init_flags ;
/* initialize internal rate at 8khz, this will adjust if necessary */
audiohook_set_internal_rate ( audiohook , 8000 , 0 ) ;
audiohook_set_internal_rate ( audiohook , DEFAULT_INTERNAL_SAMPLE_RATE , 0 ) ;
/* Since we are just starting out... this audiohook is new */
ast_audiohook_update_status ( audiohook , AST_AUDIOHOOK_STATUS_NEW ) ;
@ -361,7 +363,19 @@ static struct ast_frame *audiohook_read_frame_helper(struct ast_audiohook *audio
struct ast_frame * read_frame = NULL , * final_frame = NULL ;
struct ast_format * slin ;
audiohook_set_internal_rate ( audiohook , ast_format_get_sample_rate ( format ) , 1 ) ;
/*
* Update the rate if compatibility mode is turned off or if it is
* turned on and the format rate is higher than the current rate .
*
* This makes it so any unnecessary rate switching / resetting does
* not take place and also any associated audiohook_list ' s internal
* sample rate maintains the highest sample rate between hooks .
*/
if ( ! ast_test_flag ( audiohook , AST_AUDIOHOOK_COMPATIBLE ) | |
( ast_test_flag ( audiohook , AST_AUDIOHOOK_COMPATIBLE ) & &
ast_format_get_sample_rate ( format ) > audiohook - > hook_internal_samp_rate ) ) {
audiohook_set_internal_rate ( audiohook , ast_format_get_sample_rate ( format ) , 1 ) ;
}
if ( ! ( read_frame = ( direction = = AST_AUDIOHOOK_DIRECTION_BOTH ?
audiohook_read_frame_both ( audiohook , samples , read_reference , write_reference ) :
@ -425,6 +439,22 @@ struct ast_frame *ast_audiohook_read_frame_all(struct ast_audiohook *audiohook,
static void audiohook_list_set_samplerate_compatibility ( struct ast_audiohook_list * audiohook_list )
{
struct ast_audiohook * ah = NULL ;
/*
* Anytime the samplerate compatibility is set ( attach / remove an audiohook ) the
* list ' s internal sample rate needs to be reset so that the next time processing
* through write_list , if needed , it will get updated to the correct rate .
*
* A list ' s internal rate always chooses the higher between its own rate and a
* given rate . If the current rate is being driven by an audiohook that wanted a
* higher rate then when this audiohook is removed the list ' s rate would remain
* at that level when it should be lower , and with no way to lower it since any
* rate compared against it would be lower .
*
* By setting it back to the lowest rate it can recalulate the new highest rate .
*/
audiohook_list - > list_internal_samp_rate = DEFAULT_INTERNAL_SAMPLE_RATE ;
audiohook_list - > native_slin_compatible = 1 ;
AST_LIST_TRAVERSE ( & audiohook_list - > manipulate_list , ah , list ) {
if ( ! ( ah - > init_flags & AST_AUDIOHOOK_MANIPULATE_ALL_RATES ) ) {
@ -455,7 +485,7 @@ int ast_audiohook_attach(struct ast_channel *chan, struct ast_audiohook *audioho
AST_LIST_HEAD_INIT_NOLOCK ( & ast_channel_audiohooks ( chan ) - > whisper_list ) ;
AST_LIST_HEAD_INIT_NOLOCK ( & ast_channel_audiohooks ( chan ) - > manipulate_list ) ;
/* This sample rate will adjust as necessary when writing to the list. */
ast_channel_audiohooks ( chan ) - > list_internal_samp_rate = 8000 ;
ast_channel_audiohooks ( chan ) - > list_internal_samp_rate = DEFAULT_INTERNAL_SAMPLE_RATE ;
}
/* Drop into respective list */
@ -467,8 +497,11 @@ int ast_audiohook_attach(struct ast_channel *chan, struct ast_audiohook *audioho
AST_LIST_INSERT_TAIL ( & ast_channel_audiohooks ( chan ) - > manipulate_list , audiohook , list ) ;
}
audiohook_set_internal_rate ( audiohook , ast_channel_audiohooks ( chan ) - > list_internal_samp_rate , 1 ) ;
/*
* Initialize the audiohook ' s rate to the default . If it needs to be ,
* it will get updated later .
*/
audiohook_set_internal_rate ( audiohook , DEFAULT_INTERNAL_SAMPLE_RATE , 1 ) ;
audiohook_list_set_samplerate_compatibility ( ast_channel_audiohooks ( chan ) ) ;
/* Change status over to running since it is now attached */
@ -766,14 +799,14 @@ static struct ast_frame *audiohook_list_translate_to_slin(struct ast_audiohook_l
struct ast_frame * new_frame = frame ;
struct ast_format * slin ;
/* If we are capable of maintaining doing samplerates other that 8khz, update
* the internal audiohook_list ' s rate and higher samplerate audio arrives . By
* updating the list ' s rate , all the audiohooks in the list will be updated as well
* as the are written and read from . */
if ( audiohook_list - > native_slin_compatible ) {
audiohook_list - > list_internal_samp_rate =
MAX ( ast_format_get_sample_rate ( frame - > subclass . format ) , audiohook_list - > list_internal_samp_rate ) ;
}
/*
* If we are capable of sample rates other that 8 khz , update the internal
* audiohook_list' s rate and higher sample rate audio arrives . If native
* slin compatibility is turned on all audiohooks in the list will be
* updated as well during read / write processing .
*/
audiohook_list - > list_internal_samp_rate =
MAX ( ast_format_get_sample_rate ( frame - > subclass . format ) , audiohook_list - > list_internal_samp_rate ) ;
slin = ast_format_cache_get_slin_by_rate ( audiohook_list - > list_internal_samp_rate ) ;
if ( ast_format_cmp ( frame - > subclass . format , slin ) = = AST_FORMAT_CMP_EQUAL ) {
@ -821,6 +854,36 @@ static struct ast_frame *audiohook_list_translate_to_native(struct ast_audiohook
return outframe ;
}
/*!
* \ brief Set the audiohook ' s internal sample rate to the audiohook_list ' s rate ,
* but only when native slin compatibility is turned on .
*
* \ param audiohook_list audiohook_list data object
* \ param audiohook the audiohook to update
* \ param rate the current max internal sample rate
*/
static void audiohook_list_set_hook_rate ( struct ast_audiohook_list * audiohook_list ,
struct ast_audiohook * audiohook , int * rate )
{
/* The rate should always be the max between itself and the hook */
if ( audiohook - > hook_internal_samp_rate > * rate ) {
* rate = audiohook - > hook_internal_samp_rate ;
}
/*
* If native slin compatibility is turned on then update the audiohook
* with the audiohook_list ' s current rate . Note , the audiohook ' s rate is
* set to the audiohook_list ' s rate and not the given rate . If there is
* a change in rate the hook ' s rate is changed on its next check .
*/
if ( audiohook_list - > native_slin_compatible ) {
ast_set_flag ( audiohook , AST_AUDIOHOOK_COMPATIBLE ) ;
audiohook_set_internal_rate ( audiohook , audiohook_list - > list_internal_samp_rate , 1 ) ;
} else {
ast_clear_flag ( audiohook , AST_AUDIOHOOK_COMPATIBLE ) ;
}
}
/*!
* \ brief Pass an AUDIO frame off to be handled by the audiohook core
*
@ -851,6 +914,7 @@ static struct ast_frame *audio_audiohook_write_list(struct ast_channel *chan, st
int samples ;
int middle_frame_manipulated = 0 ;
int removed = 0 ;
int internal_sample_rate ;
/* ---Part_1. translate start_frame to SLINEAR if necessary. */
if ( ! ( middle_frame = audiohook_list_translate_to_slin ( audiohook_list , direction , start_frame ) ) ) {
@ -858,6 +922,19 @@ static struct ast_frame *audio_audiohook_write_list(struct ast_channel *chan, st
}
samples = middle_frame - > samples ;
/*
* While processing each audiohook check to see if the internal sample rate needs
* to be adjusted ( it should be the highest rate specified between formats and
* hooks ) . The given audiohook_list ' s internal sample rate is then set to the
* updated value before returning .
*
* If slin compatibility mode is turned on then an audiohook ' s internal sample
* rate is set to its audiohook_list ' s rate . If an audiohook_list ' s rate is
* adjusted during this pass then the change is picked up by the audiohooks
* on the next pass .
*/
internal_sample_rate = audiohook_list - > list_internal_samp_rate ;
/* ---Part_2: Send middle_frame to spy and manipulator lists. middle_frame is guaranteed to be SLINEAR here.*/
/* Queue up signed linear frame to each spy */
AST_LIST_TRAVERSE_SAFE_BEGIN ( & audiohook_list - > spy_list , audiohook , list ) {
@ -872,7 +949,7 @@ static struct ast_frame *audio_audiohook_write_list(struct ast_channel *chan, st
}
continue ;
}
audiohook_ set_internal_rate( audiohook , audiohook_list - > list_internal_samp_rate , 1 ) ;
audiohook_ list_set_hook_rate( audiohook_list , audiohook , & internal_sample_rate ) ;
ast_audiohook_write_frame ( audiohook , direction , middle_frame ) ;
ast_audiohook_unlock ( audiohook ) ;
}
@ -896,7 +973,7 @@ static struct ast_frame *audio_audiohook_write_list(struct ast_channel *chan, st
}
continue ;
}
audiohook_ set_internal_rate( audiohook , audiohook_list - > list_internal_samp_rate , 1 ) ;
audiohook_ list_set_hook_rate( audiohook_list , audiohook , & internal_sample_rate ) ;
if ( ast_slinfactory_available ( factory ) > = samples & & ast_slinfactory_read ( factory , read_buf , samples ) ) {
/* Take audio from this whisper source and combine it into our main buffer */
for ( i = 0 , data1 = combine_buf , data2 = read_buf ; i < samples ; i + + , data1 + + , data2 + + ) {
@ -929,14 +1006,16 @@ static struct ast_frame *audio_audiohook_write_list(struct ast_channel *chan, st
}
continue ;
}
audiohook_set_internal_rate ( audiohook , audiohook_list - > list_internal_samp_rate , 1 ) ;
/* Feed in frame to manipulation. */
if ( ! audiohook - > manipulate_callback ( audiohook , chan , middle_frame , direction ) ) {
/* If the manipulation fails then the frame will be returned in its original state.
* Since there are potentially more manipulator callbacks in the list , no action should
* be taken here to exit early . */
middle_frame_manipulated = 1 ;
}
audiohook_list_set_hook_rate ( audiohook_list , audiohook , & internal_sample_rate ) ;
/*
* Feed in frame to manipulation .
*
* XXX FAILURES ARE IGNORED XXX
* If the manipulation fails then the frame will be returned in its original state .
* Since there are potentially more manipulator callbacks in the list , no action should
* be taken here to exit early .
*/
audiohook - > manipulate_callback ( audiohook , chan , middle_frame , direction ) ;
ast_audiohook_unlock ( audiohook ) ;
}
AST_LIST_TRAVERSE_SAFE_END ;
@ -960,6 +1039,12 @@ static struct ast_frame *audio_audiohook_write_list(struct ast_channel *chan, st
/* Before returning, if an audiohook got removed, reset samplerate compatibility */
if ( removed ) {
audiohook_list_set_samplerate_compatibility ( audiohook_list ) ;
} else {
/*
* Set the audiohook_list ' s rate to the updated rate . Note that if a hook
* was removed then the list ' s internal rate is reset to the default .
*/
audiohook_list - > list_internal_samp_rate = internal_sample_rate ;
}
return end_frame ;