int set_talk_volume(struct ast_conf_member *member, struct ast_frame *f, int is_talk) { int ret=0; signed char gain_adjust; int volume = member->talk_volume; gain_adjust = gain_map[volume + 5]; if (is_talk) { /* * attempt to make the adjustment in the channel driver first */ if ( !member->talk_volume_adjust) { ret=ast_channel_setoption(member->chan, AST_OPTION_RXGAIN, &gain_adjust, sizeof(gain_adjust), 0); if (ret) member->talk_volume_adjust=1; } if (member->talk_volume_adjust && f) { ret=ast_frame_adjust_volume(f, gain_adjust); } } else { // Listen volume ret=ast_frame_adjust_volume(f, gain_adjust); } return ret; }
static int volume_callback(struct ast_audiohook *audiohook, struct ast_channel *chan, struct ast_frame *frame, enum ast_audiohook_direction direction) { struct ast_datastore *datastore = NULL; struct volume_information *vi = NULL; int *gain = NULL; /* If the audiohook is stopping it means the channel is shutting down.... but we let the datastore destroy take care of it */ if (audiohook->status == AST_AUDIOHOOK_STATUS_DONE) return 0; /* Grab datastore which contains our gain information */ if (!(datastore = ast_channel_datastore_find(chan, &volume_datastore, NULL))) return 0; vi = datastore->data; /* If this is DTMF then allow them to increase/decrease the gains */ if (ast_test_flag(vi, VOLUMEFLAG_CHANGE)) { if (frame->frametype == AST_FRAME_DTMF) { /* Only use DTMF coming from the source... not going to it */ if (direction != AST_AUDIOHOOK_DIRECTION_READ) return 0; if (frame->subclass.integer == '*') { vi->tx_gain += 1; vi->rx_gain += 1; } else if (frame->subclass.integer == '#') { vi->tx_gain -= 1; vi->rx_gain -= 1; } } } if (frame->frametype == AST_FRAME_VOICE) { /* Based on direction of frame grab the gain, and confirm it is applicable */ if (!(gain = (direction == AST_AUDIOHOOK_DIRECTION_READ) ? &vi->rx_gain : &vi->tx_gain) || !*gain) return 0; /* Apply gain to frame... easy as pi */ ast_frame_adjust_volume(frame, *gain); } return 0; }
conf_frame* mix_single_speaker( conf_frame* frames_in, int volume ) { #ifdef APP_KONFERENCE_DEBUG //DEBUG("returning single spoken frame\n") ; // // check input // if ( frames_in == NULL ) { DEBUG("unable to mix single spoken frame with null frame\n") ; return NULL ; } if ( frames_in->fr == NULL ) { DEBUG("unable to mix single spoken frame with null data\n") ; return NULL ; } if ( frames_in->member == NULL ) { DEBUG("unable to mix single spoken frame with null member\n") ; return NULL ; } #endif // APP_KONFERENCE_DEBUG // // 'mix' the frame // // copy orignal frame to converted array so listeners don't need to re-encode it frames_in->converted[ frames_in->member->read_format_index ] = ast_frdup( frames_in->fr ) ; // convert frame to slinear, if we have a path if (!(frames_in->fr = convert_frame_to_slinear( frames_in->member->to_slinear, frames_in->fr))) { ast_log( LOG_WARNING, "mix_single_speaker: unable to convert frame to slinear\n" ) ; return frames_in ; } if ( (frames_in->member->talk_volume != 0) || (volume != 0) ) { ast_frame_adjust_volume(frames_in->fr, frames_in->member->talk_volume + volume); } if (!frames_in->member->spy_partner) { // speaker is neither a spyee nor a spyer // set the frame's member to null ( i.e. all listeners ) frames_in->member = NULL ; } else { // speaker is either a spyee or a spyer if ( frames_in->member->spyee_channel_name == NULL ) { conf_frame *spy_frame = copy_conf_frame(frames_in); if ( spy_frame != 0 ) { frames_in->next = spy_frame; spy_frame->prev = frames_in; spy_frame->member = frames_in->member->spy_partner; } frames_in->member = NULL ; } else frames_in->member = frames_in->member->spy_partner ; } return frames_in ; }
conf_frame* mix_multiple_speakers( conf_frame* frames_in, int speakers, int listeners, int volume ) { #ifdef APP_KONFERENCE_DEBUG // // check input // // no frames to mix if ( ( frames_in == NULL ) || ( frames_in->fr == NULL ) ) { DEBUG("passed spoken frame list was NULL\n") ; return NULL ; } // if less than two speakers, then no frames to mix if ( speakers < 2 ) { DEBUG("mix_multiple_speakers() called with less than two speakers\n") ; return NULL ; } #endif // APP_KONFERENCE_DEBUG // // at this point we know that there is more than one frame, // and that the frames need to be converted to pcm to be mixed // // now, if there are only two frames and two members, // we can swap them. ( but we'll get to that later. ) // // // loop through the spoken frames, making a list of spoken members, // and converting gsm frames to slinear frames so we can mix them. // // pointer to the spoken frames list conf_frame* cf_spoken = frames_in ; // pointer to the new list of mixed frames conf_frame* cf_sendFrames = NULL ; while ( cf_spoken != NULL ) { // // while we're looping through the spoken frames, we'll // convert the frame to a format suitable for mixing // // if the frame fails to convert, drop it and treat // the speaking member like a listener by not adding // them to the cf_sendFrames list // if ( cf_spoken->member == NULL ) { ast_log( LOG_WARNING, "unable to determine frame member\n" ) ; } else { //DEBUG("converting frame to slinear, channel => %s\n", cf_spoken->member->channel_name) ; if ( !(cf_spoken->fr = convert_frame_to_slinear( cf_spoken->member->to_slinear, cf_spoken->fr)) ) { ast_log( LOG_WARNING, "mix_multiple_speakers: unable to convert frame to slinear\n" ) ; continue; } if (( cf_spoken->member->talk_volume != 0 ) || (volume != 0)) { ast_frame_adjust_volume(cf_spoken->fr, cf_spoken->member->talk_volume + volume); } if ( cf_spoken->member->spyee_channel_name == NULL ) { // create new conf frame with last frame as 'next' cf_sendFrames = create_conf_frame( cf_spoken->member, cf_sendFrames, NULL ) ; } else if ( cf_spoken->member->spy_partner->local_speaking_state == 0 ) { cf_sendFrames = create_conf_frame( cf_spoken->member->spy_partner, cf_sendFrames, NULL ) ; } } // point to the next spoken frame cf_spoken = cf_spoken->next ; } // if necessary, add a frame with a null member pointer. // this frame will hold the audio mixed for all listeners if ( listeners > 0 ) { cf_sendFrames = create_conf_frame( NULL, cf_sendFrames, NULL ) ; } // // mix the audio // // convenience pointer that skips over the friendly offset char* cp_listenerData ; // pointer to the send frames list conf_frame* cf_send = NULL ; for ( cf_send = cf_sendFrames ; cf_send != NULL ; cf_send = cf_send->next ) { // allocate a mix buffer which fill large enough memory to // hold a frame, and reset it's memory so we don't get noise char* cp_listenerBuffer = malloc( AST_CONF_BUFFER_SIZE ) ; memset( cp_listenerBuffer, 0x0, AST_CONF_BUFFER_SIZE ) ; // point past the friendly offset right to the data cp_listenerData = cp_listenerBuffer + AST_FRIENDLY_OFFSET ; // reset the spoken list pointer cf_spoken = frames_in ; // really mix the audio for ( ; cf_spoken != NULL ; cf_spoken = cf_spoken->next ) { // // if the members are equal, and they // are not null, do not mix them. // if ( ( cf_spoken->member == cf_send->member ) || ( cf_spoken->member->spyee_channel_name != NULL && cf_spoken->member->spy_partner != cf_send->member) ) { // don't mix this frame } else if ( cf_spoken->fr == NULL ) { ast_log( LOG_WARNING, "unable to mix conf_frame with null ast_frame\n" ) ; } else { // mix the new frame in with the existing buffer mix_slinear_frames( cp_listenerData, CASTDATA2PTR(cf_spoken->fr->data, char), AST_CONF_BLOCK_SAMPLES);//XXX NAS cf_spoken->fr->samples ) ; } } // copy a pointer to the frame data to the conf_frame cf_send->mixed_buffer = cp_listenerData ; } // // copy the mixed buffer to a new frame // // reset the send list pointer cf_send = cf_sendFrames ; while ( cf_send != NULL ) { cf_send->fr = create_slinear_frame( cf_send->mixed_buffer ) ; cf_send = cf_send->next ; } // // clean up the spoken frames we were passed // ( caller will only be responsible for free'ing returns frames ) // // reset the spoken list pointer cf_spoken = frames_in ; while ( cf_spoken != NULL ) { struct ast_conf_member *spy_partner = cf_spoken->member->spy_partner ; if ( spy_partner == NULL || cf_spoken->member->spyee_channel_name != NULL ) { // delete the frame cf_spoken = delete_conf_frame( cf_spoken ) ; } else { // move the unmixed frame to sendFrames // and indicate who it's for conf_frame *spy_frame = cf_spoken ; cf_spoken = cf_spoken->next; if ( cf_spoken != NULL ) cf_spoken->prev = NULL; spy_frame->next = cf_sendFrames; cf_sendFrames->prev = spy_frame; spy_frame->prev = NULL; spy_frame->member = spy_partner; cf_sendFrames = spy_frame; } } // return the list of frames for sending return cf_sendFrames ; }