Пример #1
0
int cw_linear_stream(struct cw_channel *chan, const char *filename, int fd, int allowoverride)
{
	struct linear_state *lin;
	char tmpf[256];
	int res = -1;
	int autoclose = 0;
	if (fd < 0) {
		if (cw_strlen_zero(filename))
			return -1;
		autoclose = 1;
		if (filename[0] == '/') 
			cw_copy_string(tmpf, filename, sizeof(tmpf));
		else
			snprintf(tmpf, sizeof(tmpf), "%s/%s/%s", (char *)cw_config_CW_VAR_DIR, "sounds", filename);
		fd = open(tmpf, O_RDONLY);
		if (fd < 0){
			cw_log(LOG_WARNING, "Unable to open file '%s': %s\n", tmpf, strerror(errno));
			return -1;
		}
	}
	lin = malloc(sizeof(struct linear_state));
	if (lin) {
		memset(lin, 0, sizeof(lin));
		lin->fd = fd;
		lin->allowoverride = allowoverride;
		lin->autoclose = autoclose;
		res = cw_generator_activate(chan, &linearstream, lin);
	}
	return res;
}
Пример #2
0
int cw_conf_member_genactivate( struct cw_conf_member *member ) {
    int res = 1;

    if ( !cw_generator_is_active(member->chan) )
	res = cw_generator_activate(member->chan,&membergen,member);

    if (res < 0)
    {
    	cw_log(LOG_WARNING,"Failed to activate generator on conference '%s'\n",member->chan->name);
    	res = 0;
    }
    else
        res = 1;

    return res ;
}
Пример #3
0
int conf_play_soundqueue( struct cw_conf_member *member ) 
{
    int res = 0;

    cw_stopstream(member->chan);
    queue_incoming_silent_frame(member,3);

    struct cw_conf_soundq *toplay, *delitem;

    cw_generator_deactivate(member->chan);
    cw_mutex_lock(&member->lock);

    toplay = member->soundq;
    while (  ( toplay != NULL) && ( res == 0 )  ) {

	manager_event(
		EVENT_FLAG_CALL, 
		APP_CONFERENCE_MANID"Sound",
		"Channel: %s\r\n"
		"Sound: %s\r\n",
		member->channel_name, 
		toplay->name
	);

	res = conf_play_soundfile( member, toplay->name );
	if (res) break;

	delitem = toplay;
	toplay = toplay->next;
	member->soundq = toplay;
	free(delitem);
    }
    cw_generator_activate(member->chan,&membergen,member);
    cw_mutex_unlock(&member->lock);

    if (res != 0)
        conference_stop_sounds( member );

    return res;
}
Пример #4
0
struct cw_conf_member *create_member( struct cw_channel *chan, int argc, char **argv ) {

    if ( chan == NULL )
    {
    	cw_log( LOG_ERROR, "unable to create member with null channel\n" ) ;
    	return NULL ;
    }
	
    if ( chan->name == NULL )
    {
    	cw_log( LOG_ERROR, "unable to create member with null channel name\n" ) ;
    	return NULL ;
    }
	
    struct cw_conf_member *member = calloc( 1, sizeof( struct cw_conf_member ) ) ;
	
    if ( member == NULL ) 
    {
    	cw_log( LOG_ERROR, "unable to malloc cw_conf_member\n" ) ;
    	return NULL ;
    }

    //
    // initialize member with passed data values
    //

    // initialize mutex
    cw_mutex_init( &member->lock ) ;
	
    char argstr[80];
    char *stringp, *token ;

    // copy the passed data
    strncpy( argstr, argv[0], sizeof(argstr) - 1 ) ;

    // point to the copied data
    stringp = argstr ;
	
    cw_log( CW_CONF_DEBUG, "attempting to parse passed params, stringp => %s\n", stringp ) ;
	
    // parse the id
    if ( ( token = strsep( &stringp, "/" ) ) != NULL )
    {
    	member->id = malloc( strlen( token ) + 1 ) ;
    	strcpy( member->id, token ) ;
    }
    else
    {
    	cw_log( LOG_ERROR, "unable to parse member id\n" ) ;
    	free( member ) ;
    	return NULL ;
    }

    // parse the flags
    if ( ( token = strsep( &stringp, "/" ) ) != NULL )
    {
	member->flags = malloc( strlen( token ) + 1 ) ;
	strcpy( member->flags, token ) ;
    }
    else
    {
	// make member->flags something 
	member->flags = malloc( sizeof( char ) ) ;
	memset( member->flags, 0x0, sizeof( char ) ) ;
    }

    // parse the pin
    if ( ( token = strsep( &stringp, "/" ) ) != NULL )
    {
	member->pin = malloc( strlen( token ) + 1 ) ;
	strcpy( member->pin, token ) ;
    }
    else
    {
	// make member->pin something 
	member->pin = malloc( sizeof( char ) ) ;
	memset( member->pin, 0x0, sizeof( char ) ) ;
    }
	
    // debugging
    cw_log( 
    	CW_CONF_DEBUG, 
	"parsed data params, id => %s, flags => %s, pin %s\n",
	member->id, member->flags, member->pin
    ) ;

    //
    // initialize member with default values
    //

    // keep pointer to member's channel
    member->chan = chan ;
    member->auto_destroy = 1 ;

    // copy the channel name
    member->channel_name = strdup(chan->name);
    // Copy the channel CallerID
    if (chan->cid.cid_dnid)
	    member->cid.cid_dnid = strdup(chan->cid.cid_dnid);
    else
	    member->cid.cid_dnid = NULL;
    if (chan->cid.cid_num)
	    member->cid.cid_num = strdup(chan->cid.cid_num);
    else
	    member->cid.cid_num = NULL;
    if (chan->cid.cid_name)
	    member->cid.cid_name = strdup(chan->cid.cid_name);
    else
	    member->cid.cid_name = NULL;
    if (chan->cid.cid_ani)
	    member->cid.cid_ani = strdup(chan->cid.cid_ani);
    else
	    member->cid.cid_ani = NULL;
    if (chan->cid.cid_rdnis)
	    member->cid.cid_rdnis = strdup(chan->cid.cid_rdnis);
    else
	    member->cid.cid_rdnis = NULL;
    member->cid.cid_pres = chan->cid.cid_pres;
    member->cid.cid_ani2 = chan->cid.cid_ani2;
    member->cid.cid_ton = chan->cid.cid_ton;
    member->cid.cid_tns = chan->cid.cid_tns;

    // ( default can be overridden by passed flags )
    member->type = MEMBERTYPE_LISTENER ;

    // linked-list pointer
    member->next = NULL ;
	
    // flags
    member->remove_flag = 0 ;
    member->force_remove_flag = 0 ;

    // record start time
    gettimeofday( &member->time_entered, NULL ) ;

    // Initialize member RTP data
    member->framelen   = 0;		// frame length in milliseconds
    member->samples    = 0;		// number of samples in framelen
    member->samplefreq = 0;		// calculated sample frequency
    member->enable_vad = 0;
    member->enable_vad_allowed = 0;
    member->silence_nr = 1;

    if  (!strncmp(chan->name,"Local",sizeof("Local")) )
	member->enable_vad_allowed = 0;

    // smoother defaults.
    member->smooth_size_in = -1;
    member->smooth_size_out = -1;
    member->inSmoother= NULL;

    // Audio data
    member->talk_volume = 0;		
    member->talk_volume_adjust = 0;		
    member->talk_mute   = 0;		
    member->skip_voice_detection = 10;

    member->quiet_mode = 0;
    member->beep_only_mode = 0;
    member->is_on_hold = 0;
    member->skip_moh_when_alone = 0;

    member->lostframecount = 0;

    //DTMF Data
    member->manage_dtmf = 1;
    member->dtmf_admin_mode=0;
    member->dtmf_long_insert=0;

    //Play conference sounds by default
    member->dont_play_any_sound=0;

    // Zeroing output frame buffer
    memset(member->framedata,0,sizeof(member->framedata));

    //
    // parse passed flags
    //
	
    // temp pointer to flags string
    char* flags = member->flags ;

    int i;
    for ( i = 0 ; i < strlen( flags ) ; ++i )
    {
	// allowed flags are M, L, T, C, V, d
	switch ( flags[i] )
	{
	    // member types ( last flag wins )
		case 'M':
		    member->type = MEMBERTYPE_MASTER ;
		    break ;
		case 'S':
		    member->type = MEMBERTYPE_SPEAKER ;
		    break ;
		case 'L':
		    member->type = MEMBERTYPE_LISTENER ;
		    break ;
		case 'T':
		    member->type = MEMBERTYPE_TALKER ;
		    break ;
		case 'C':
		    member->type = MEMBERTYPE_CONSULTANT ;
		    break ;
		// speex preprocessing options
		case 'V':
#if ENABLE_VAD
		    if  ( strncmp(chan->name,"Local",sizeof("Local")-1) ) {
		        if (member->type != MEMBERTYPE_LISTENER) {
			    member->enable_vad_allowed = 1 ;
			    member->enable_vad = 1 ;
			}
		    } else { 
			member->enable_vad_allowed = 0;
			member->enable_vad = 0 ;
			cw_log( LOG_WARNING, "VAD Not supported on outgoing channels.\n"); 
		    }
#else
		    cw_log( LOG_WARNING, "VAD Support is not compiled in. Disabling.\n"); 
#endif	
		    break ;

		// additional features
		case 'd': // Send DTMF manager events..
		    member->manage_dtmf = 0;
		    break;
		case 'm': // don't play MOH when alone
		    member->skip_moh_when_alone = 1;
		    break;
		case 'x': // Don't destroy when empty
		    if ( member->type == MEMBERTYPE_MASTER )
			member->auto_destroy = 0;
		    break;
		case 'q': // Quiet mode
		    member->quiet_mode = 1;
		    break;
		case 'b': // Only beep to announce if a member join/left
		    member->beep_only_mode = 1;
		    break;

		default:
		    cw_log( LOG_WARNING, "received invalid flag, chan => %s, flag => %c\n", 
			    chan->name, flags[i] ) ;			
		    break ;
	}
    }

    // Circular buffer
    member->cbuf = calloc( 1, sizeof( struct member_cbuffer ) ) ;
	
    if ( member->cbuf == NULL ) 
    {
    	cw_log( LOG_ERROR, "unable to malloc member_cbuffer\n" ) ;
    	return NULL ;
    } else {
	// initialize it
	memset(member->cbuf, 0, sizeof(struct member_cbuffer) );
    }

    //
    // read, write, and translation options
    //

    cw_log( CW_CONF_DEBUG, "created member on channel %s, type => %d, readformat => %d, writeformat => %d\n", 	
		member->chan->name, member->type, chan->readformat, chan->writeformat ) ;

    // set member's audio formats, taking dsp preprocessing into account
    // ( chan->nativeformats, CW_FORMAT_SLINEAR, CW_FORMAT_ULAW, CW_FORMAT_GSM )
    member->read_format = CW_FORMAT_SLINEAR ;
    member->write_format = CW_FORMAT_SLINEAR ;

    //
    // finish up
    //
		
    cw_log( CW_CONF_DEBUG, "created member on channel %s, type => %d, readformat => %d, writeformat => %d\n", 	
		member->chan->name, member->type, chan->readformat, chan->writeformat ) ;

    if ( !cw_generator_is_active(chan) )
	cw_generator_activate(chan,&membergen,member);


    return member ;
}
Пример #5
0
static int process_incoming(struct cw_conf_member *member, struct cw_frame *f) 
{
    int res;

    // Play our sound queue, if not empty. 
    if (member->soundq) {
	// Free the frame.
	if ( f != NULL ) {
	    cw_fr_free( f ) ;
	}
	res = conf_play_soundqueue( member ); 
	if (res != 0) {
	    queue_incoming_silent_frame(member,2);
	    // send the DTMF event to the MGR interface..
	    manager_event(
		EVENT_FLAG_CALL,
		APP_CONFERENCE_MANID"DTMF",
		"Channel: %s\r\n"
		"Key: %c\r\n",
		member->channel_name,
		res
	    ) ;
	    parse_dtmf_option( member, res);
	}
	return res;
    }

    //
    // Moderator forces MOH management
    //

    if ( member->force_on_hold == 1 ) {
	cw_moh_start(member->chan,"");
	member->force_on_hold = 0 ;
    } 
    else if ( member->force_on_hold == -1 ) {
	cw_moh_stop(member->chan);
	cw_generator_activate(member->chan,&membergen,member);
	member->force_on_hold = 0 ;
    } 

    //
    // MOH When the user is alone in the conference
    //
    if ( member->conf->membercount == 1 && 
	 member->is_on_hold == 0 && 
	 member->skip_moh_when_alone == 0 
       ) {
	cw_moh_start(member->chan,"");
	member->is_on_hold = 1 ;
	return 0;
    }

    if ( member->conf->membercount > 1 && 
	 member->is_on_hold == 1 && 
	 member->skip_moh_when_alone == 0 
       ) {
	cw_moh_stop(member->chan);
	cw_generator_activate(member->chan,&membergen,member);
	member->is_on_hold = 0 ;
	return 0;
    }

    if ( member->force_remove_flag == 1 ) {
        return 0;
    }

    // If we don't have any frame to parse, then return
    if ( f == NULL ) {
	return 0;
    }

    // Actions based on the content of the frame
    if ( f->frametype == CW_FRAME_DTMF && member->manage_dtmf )
    {	
	queue_incoming_silent_frame(member,2);

	// send the DTMF event to the MGR interface..
	manager_event(
		EVENT_FLAG_CALL,
		APP_CONFERENCE_MANID"DTMF",
		"Channel: %s\r\n"
		"Key: %c\r\n",
		member->channel_name,
		f->subclass
	) ;

	parse_dtmf_option( member, f->subclass);

	cw_fr_free(f);
    }
    else if (  (member->type == MEMBERTYPE_LISTENER) || (member->talk_mute) )
    {
	// this is a listen-only user, or it's muted. 	
	// Ignore the frame
	cw_fr_free( f ) ;
    }
    else if ( f->frametype == CW_FRAME_VOICE ) 
    {			
	// ********************************************************************************** VOICE
	int old_speaking_state = member->is_speaking;

#if ENABLE_VAD
	if ( member->talk_mute == 1 ) member->is_speaking = 0;

	if ( member->enable_vad 
	     && f->subclass == CW_FORMAT_SLINEAR && f->samples > 0 
	   )
	{
	    // and if it's time to check what the member is doing
	    if ( member->skip_voice_detection <= 0 || member->is_speaking ) 
	    {
		int rees;
		rees = vad_is_talk( f->data, f->datalen, &member->silence_nr, 20);
		// send the frame to the preprocessor
		if ( rees != 0 )
		{
		    // voice detected, reset skip count
		    if ( member->framelen != 0 )
			member->skip_voice_detection = (CW_CONF_SKIP_MS_AFTER_VOICE_DETECTION / member->framelen);
		    else 
			// Let's suppose that 20ms as a framelen is not too different from the real situation
			member->skip_voice_detection = 20;
		    member->is_speaking=1;
		}
		else {
		    // member is silent
		    member->is_speaking=0;
		    if ( member->framelen != 0 )
			member->skip_voice_detection = ( CW_CONF_SKIP_MS_WHEN_SILENT / member->framelen );
		    else 
			member->skip_voice_detection = 5;
		}

	    }
	    --member->skip_voice_detection ;
	}

	if (old_speaking_state != member ->is_speaking)
	    send_state_change_notifications(member);
#endif

	// volume of the frame is modified after the VAD has been done
	if (member->talk_volume != 0) 
	    set_talk_volume(member, f, 1);


	if (  member->is_speaking && queue_incoming_frame( member, f ) != 0 )
	    cw_log( CW_CONF_DEBUG, "dropped incoming frame, channel => %s\n", member->channel_name ) ;

	// free the original frame
	cw_fr_free( f ) ;
    }
    else if ( f->frametype == CW_FRAME_CONTROL && f->subclass == CW_CONTROL_HANGUP ) 
    {
	// hangup received, queue silence && free the frame 
	queue_incoming_silent_frame(member,2);
	cw_fr_free( f ) ;
    }
    else
    {
	// Unmanaged frame
#if  ( APP_NCONFERENCE_DEBUG == 1 )
	cw_log(LOG_WARNING,"Freeing unknown frame: type %d  member %s \n", f->frametype, member->chan->name );
#endif
	cw_fr_free( f ) ;
    }

    return 0;
}
Пример #6
0
int cw_playtones_start(struct cw_channel *chan, int vol, const char *playlst, int interruptible)
{
	char *s;
	char *data = cw_strdupa(playlst);
	struct playtones_def d = { vol, -1, 0, 1, NULL};
	char *stringp = NULL;
	char *separator;

	if (vol >= 0)
		d.vol = -13;
	else
		d.vol = vol;

	d.interruptible = interruptible;
	
	stringp = data;
	/* the stringp/data is not null here */
	/* check if the data is separated with '|' or with ',' by default */
	if (strchr(stringp,'|'))
		separator = "|";
	else
		separator = ",";
	s = strsep(&stringp,separator);
	while (s  &&  *s)
    {
		int freq1, freq2, time, modulate=0, moddepth=90, midinote=0;

		if (s[0] == '!')
			s++;
		else if (d.reppos == -1)
			d.reppos = d.nitems;
		
        if (sscanf(s, "%d+%d/%d", &freq1, &freq2, &time) == 3)
        {
			/* f1+f2/time format */
		}
        else if (sscanf(s, "%d+%d", &freq1, &freq2) == 2)
        {
			/* f1+f2 format */
			time = 0;
		}
        else if (sscanf(s, "%d*%d/%d", &freq1, &freq2, &time) == 3)
        {
			/* f1*f2/time format */
			modulate = 1;
		}
        else if (sscanf(s, "%d*%d@%d/%d", &freq1, &freq2, &moddepth, &time) == 4)
        {
			/* f1*f2@md/time format */
			modulate = 1;
		}
        else if (sscanf(s, "%d*%d", &freq1, &freq2) == 2)
        {
			/* f1*f2 format */
			time = 0;
			modulate = 1;
		}
        else if (sscanf(s, "%d*%d@%d", &freq1, &freq2, &moddepth) == 3)
        {
			/* f1*f2@md format */
			time = 0;
			modulate = 1;
		}
        else if (sscanf(s, "%d/%d", &freq1, &time) == 2)
        {
			/* f1/time format */
			freq2 = 0;
		}
        else if (sscanf(s, "%d", &freq1) == 1)
        {
			/* f1 format */
			freq2 = 0;
			time = 0;
		}
        else if (sscanf(s, "M%d+M%d/%d", &freq1, &freq2, &time) == 3)
        {
			/* Mf1+Mf2/time format */
			midinote = 1;
		}
        else if (sscanf(s, "M%d+M%d", &freq1, &freq2) == 2)
        {
			/* Mf1+Mf2 format */
			time = 0;
			midinote = 1;
		}
        else if (sscanf(s, "M%d*M%d/%d", &freq1, &freq2, &time) == 3)
        {
			/* Mf1*Mf2/time format */
			modulate = 1;
			midinote = 1;
		}
        else if (sscanf(s, "M%d*M%d", &freq1, &freq2) == 2)
        {
			/* Mf1*Mf2 format */
			time = 0;
			modulate = 1;
			midinote = 1;
		}
        else if (sscanf(s, "M%d/%d", &freq1, &time) == 2)
        {
			/* Mf1/time format */
			freq2 = -1;
			midinote = 1;
		}
        else if (sscanf(s, "M%d", &freq1) == 1)
        {
			/* Mf1 format */
			freq2 = -1;
			time = 0;
			midinote = 1;
		}
        else
        {
			cw_log(LOG_WARNING,"%s: tone component '%s' of '%s' is no good\n",chan->name,s,playlst);
			return -1;
		}

		if (midinote)
        {
			/* midi notes must be between 0 and 127 */
			if ((freq1 >= 0) && (freq1 <= 127))
				freq1 = midi_to_hz[freq1];
			else
				freq1 = 0;

			if ((freq2 >= 0) && (freq2 <= 127))
				freq2 = midi_to_hz[freq2];
			else
				freq2 = 0;
		}

		if ((d.items = realloc(d.items,(d.nitems + 1)*sizeof(struct playtones_item))) == NULL)
		{
			cw_log(LOG_WARNING, "Realloc failed!\n");
			return -1;
		}
		d.items[d.nitems].freq1    = freq1;
		d.items[d.nitems].freq2    = freq2;
		d.items[d.nitems].duration = time;
		d.items[d.nitems].modulate = modulate;
		d.items[d.nitems].modulation_depth = moddepth;
		d.nitems++;

		s = strsep(&stringp, separator);
	}

	if (cw_generator_activate(chan, &playtones, &d))
		return -1;

	return 0;
}
Пример #7
0
static int fax_audio(struct cw_channel *chan, fax_state_t *fax, const char *file, int calling_party, int verbose)
{
    char *x;
    struct cw_frame *inf = NULL;
    struct cw_frame outf;
    int ready = 1;
    int samples = 0;
    int res = 0;
    int len = 0;
    int generator_mode = 0;
    uint64_t begin = 0;
    uint64_t received_frames = 0;
    uint8_t __buf[sizeof(uint16_t)*MAX_BLOCK_SIZE + 2*CW_FRIENDLY_OFFSET];
    uint8_t *buf = __buf + CW_FRIENDLY_OFFSET;
#if 0
    struct cw_frame *dspf = NULL;
    struct cw_dsp *dsp = NULL;
#endif
    uint64_t voice_frames;
    t30_state_t *t30;

    memset(fax, 0, sizeof(*fax));
    if (fax_init(fax, calling_party) == NULL)
    {
        cw_log(LOG_WARNING, "Unable to start FAX\n");
        return -1;
    }
    t30 = fax_get_t30_state(fax);
    fax_set_transmit_on_idle(fax, TRUE);
    span_log_set_message_handler(&fax->logging, span_message);
    span_log_set_message_handler(&t30->logging, span_message);
    if (verbose)
    {
        span_log_set_level(&fax->logging, SPAN_LOG_SHOW_SEVERITY | SPAN_LOG_SHOW_PROTOCOL | SPAN_LOG_FLOW);
        span_log_set_level(&t30->logging, SPAN_LOG_SHOW_SEVERITY | SPAN_LOG_SHOW_PROTOCOL | SPAN_LOG_FLOW);
    }

    fax_set_common(chan, t30, file, calling_party, verbose);
    fax_set_transmit_on_idle(fax, TRUE);

    if (calling_party)
    {
        voice_frames = 0;
    }
    else
    {
#if 0
        /* Initializing the DSP */
        if ((dsp = cw_dsp_new()) == NULL)
        {
            cw_log(LOG_WARNING, "Unable to allocate DSP!\n");
        }
        else
        {
            cw_dsp_set_threshold(dsp, 256); 
            cw_dsp_set_features(dsp, DSP_FEATURE_DTMF_DETECT | DSP_FEATURE_FAX_CNG_DETECT);
            cw_dsp_digitmode(dsp, DSP_DIGITMODE_DTMF | DSP_DIGITMODE_RELAXDTMF);
        }
#endif
        voice_frames = 1;
    }

    /* This is the main loop */
    begin = nowis();
    while (ready  &&  ready_to_talk(chan))
    {
        if (chan->t38_status == T38_NEGOTIATED)
            break;

        if ((res = cw_waitfor(chan, 20)) < 0)
        {
            ready = 0;
            break;
        }

        if (!t30_call_active(t30))
            break;

        if ((inf = cw_read(chan)) == NULL)
        {
            ready = 0;
            break;
        }

        /* We got a frame */
        if (inf->frametype == CW_FRAME_VOICE)
        {
#if 0
            if (dsp)
            {
                if ((dspf = cw_frdup(inf)))
                    dspf = cw_dsp_process(chan, dsp, dspf);

                if (dspf)
                {
                    if (dspf->frametype == CW_FRAME_DTMF)
                    {
                        if (dspf->subclass == 'f')
                        {
                            cw_log(LOG_DEBUG, "Fax detected in RxFax !!!\n");
                            cw_app_request_t38(chan);
                            /* Prevent any further attempts to negotiate T.38 */
                            cw_dsp_free(dsp);
                            dsp = NULL;
                    
                        }
                    }
                    cw_fr_free(dspf);
                    dspf = NULL;
                }
            }
#else
            if (voice_frames)
            {
                /* Wait a little before trying to switch to T.38, as some things don't seem
                   to like entirely missing the audio. */
                if (++voice_frames == 100)
                {
                    cw_log(LOG_DEBUG, "Requesting T.38 negotation in RxFax !!!\n");
                    cw_app_request_t38(chan);
                    voice_frames = 0;
                }
            }
#endif
            received_frames++;

            if (fax_rx(fax, inf->data, inf->samples))
                break;

            samples = (inf->samples <= MAX_BLOCK_SIZE)  ?  inf->samples  :  MAX_BLOCK_SIZE;
            if ((len = fax_tx(fax, (int16_t *) &buf[CW_FRIENDLY_OFFSET], samples)) > 0)
            {
                cw_fr_init_ex(&outf, CW_FRAME_VOICE, CW_FORMAT_SLINEAR, "FAX");
                outf.datalen = len*sizeof(int16_t);
                outf.samples = len;
                outf.data = &buf[CW_FRIENDLY_OFFSET];
                outf.offset = CW_FRIENDLY_OFFSET;

                if (cw_write(chan, &outf) < 0)
                {
                    cw_log(LOG_WARNING, "Unable to write frame to channel; %s\n", strerror(errno));
                    break;
                }
            }
        }
        else
        {
            if ((nowis() - begin) > 1000000)
            {
                if (received_frames < 20)
                {
                    /* Just to be sure we have had no frames ... */
                    cw_log(LOG_NOTICE, "Switching to generator mode\n");
                    generator_mode = 1;
                    break;
                }
            }
        }
        cw_fr_free(inf);
        inf = NULL;
    }

    if (inf)
    {
        cw_fr_free(inf);
        inf = NULL;
    }
    if (generator_mode)
    {
        /* This is activated when we don't receive any frame for X seconds (see above)... */
        cw_log(LOG_NOTICE, "Starting generator\n");
#if 0
        if (dsp)
            cw_dsp_reset(dsp);
#endif
        cw_generator_activate(chan, &faxgen, fax);

        while (ready  &&  ready_to_talk(chan))
        {
            if (chan->t38_status == T38_NEGOTIATED)
                break;

            if ((res = cw_waitfor(chan, 20)) < 0)
            {
                ready = 0;
                break;
            }

            if (!t30_call_active(t30))
                break;

            if ((inf = cw_read(chan)) == NULL)
            {
                ready = 0;
                break;
            }

            /* We got a frame */
            if (inf->frametype == CW_FRAME_VOICE)
            {
#if 0
                if (dsp)
                {
                    if ((dspf = cw_frdup(inf)))
                        dspf = cw_dsp_process(chan, dsp, dspf);

                    if (dspf)
                    {
                        if (dspf->frametype == CW_FRAME_DTMF)
                        {
                            if (dspf->subclass == 'f')
                            {
                                cw_log(LOG_DEBUG, "Fax detected in RxFax !!!\n");
                                cw_app_request_t38(chan);
                                /* Prevent any further attempts to negotiate T.38 */
                                cw_dsp_free(dsp);
                                dsp = NULL;
                            }
                        }
                        cw_fr_free(dspf);
                        dspf = NULL;
                    }
                }
#else
                if (voice_frames)
                {
                    if (++voice_frames == 100)
                    {
                        cw_log(LOG_DEBUG, "Requesting T.38 negotation in RxFax !!!\n");
                        cw_app_request_t38(chan);
                        voice_frames = 0;
                    }
                }
#endif
                if (fax_rx(fax, inf->data, inf->samples))
                {
                    ready = 0;
                    break;
                }
            }

            cw_fr_free(inf);
            inf = NULL;
        }

        if (inf)
        {
            cw_fr_free(inf);
            inf = NULL;
        }
        cw_log(LOG_NOTICE, "Stopping generator\n");
        cw_generator_deactivate(chan);
    }
#if 0
    if (dsp)
        cw_dsp_free(dsp);
#endif
    return ready;
}