⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 audio_portaudio.c

📁 ppciaxclient softphone
💻 C
📖 第 1 页 / 共 2 页
字号:
{
    PaError err;
    if (single) {
        err = Pa_OpenStream(&iStream, 
	      selectedInput, (inMono ? 1 : 2), paInt16, NULL,
	      selectedOutput, (outMono ? 1 : 2), paInt16, NULL,
	      sample_rate, 
	      SAMPLES_PER_FRAME,
	      PA_NUMBUFFERS,
	      0,
	      pa_callback, 
	      NULL);
    
        if (err != paNoError) {
            return -1;
        }

        oStream = iStream;
        oneStream = 1;
    } else {
        err = Pa_OpenStream(&iStream, 
	      selectedInput, (inMono ? 1 : 2), paInt16, NULL,
	      paNoDevice, 0, paInt16, NULL,
	      sample_rate, 
	      SAMPLES_PER_FRAME,
	      PA_NUMBUFFERS,
	      0,
	      pa_callback, 
	      NULL);
        if (err != paNoError) {
	    return -1;
        }

        err = Pa_OpenStream(&oStream, 
	      paNoDevice, 0, paInt16, NULL,
	      selectedOutput, (outMono ? 1 : 2), paInt16, NULL,
	      sample_rate, 
	      SAMPLES_PER_FRAME,
	      PA_NUMBUFFERS,
	      0,
	      pa_callback, 
	      NULL);
        if (err != paNoError) {
	    Pa_CloseStream(iStream);
            iStream = NULL;
	    return -1;
        }

        oneStream = 0;
    }

    virtualMonoIn = (inMono ? 0 : 1);
    virtualMonoOut = (outMono ? 0 : 1);
    return 0;
}

/* some commentary here:
 * 1: MacOSX: MacOSX often needs "virtual mono" and a single stream.  
 * That doesn't work for some USB devices (a Platronics headset), so 
 * mono in, virtual mono out, and mono in/out are also tried.
 *
 * 2: Unix/OSS: most cards are OK with real mono, and a single stream.
 * Except some.  For those, a single open with real mono will succeed,
 * but execution will fail.  Maybe others will open OK with a single
 * stream, and real mono, but fail later? Two stream mono is tried first,
 * since it reportedly provides better sound quality with ALSA
 * and Sound Blaster Live.
 *
 * The failure mode I saw with a volunteer was that reads/writes would
 * return -enodev (down in the portaudio code).  Bummer.
 *
 * Win32 works fine, in all cases, with a single stream and real mono,
 * so far.
 *
 * We could probably do this more cleanly, because there are still cases
 * where we will fail (i.e. if the user has only mono in and out on a Mac).
 *
 * */
static int pa_openstreams (struct iaxc_audio_driver *d ) {
    int err;
#ifdef LINUX
    err = pa_open(0, 1, 1) && /* two stream mono */
        pa_open(1, 1, 1) &&   /* one stream mono */
        pa_open(0, 0, 0);     /* two stream stereo */
#else
#ifdef MACOSX
    err = pa_open(1, 0, 0) &&  /* one stream stereo */
        pa_open(1, 1, 0) &&    /* one stream mono in stereo out */
        pa_open(1, 1, 1) &&    /* one stream mono */
        pa_open(0, 0, 0);      /* two stream stereo */
#else
    err = pa_open(1, 1, 1) &&  /* one stream mono */
        pa_open(1, 0, 0) &&    /* one stream stereo */
        pa_open(1, 1, 0) &&    /* one stream mono in stereo out */
        pa_open(0, 0, 0);      /* two stream stereo */
#endif /*MACOSX */
#endif /* LINUX */

    if (err) {
	handle_paerror(err, "Unable to open streams");
	return -1;
    }
    return 0;
}

static int pa_openauxstream (struct iaxc_audio_driver *d ) {
    PaError err;

    err = Pa_OpenStream ( &aStream, 
	      paNoDevice, 0, paInt16, NULL,  /* input info */
	      selectedRing,  virtualMonoOut+1, paInt16, NULL,  /* output info */
	      sample_rate, 
	      SAMPLES_PER_FRAME,  /* frames per buffer -- 10ms */
	      PA_NUMBUFFERS,   /* numbuffers */  /* use default */
	      0,   /* flags */
	      pa_aux_callback, 
	      NULL /* userdata */
      );
    if( err != paNoError ) 
    {
	handle_paerror(err, "opening separate ring stream");
	return -1;
    }

    return 0;
}

static int pa_start (struct iaxc_audio_driver *d ) {
    PaError err;
    static int errcnt=0;

    if(running) return 0;
#ifndef _POCKETPC_
    /* re-open mixers if necessary */
    if(iMixer)
    { 
	Px_CloseMixer(iMixer);
	iMixer = NULL; 
    }

    if(oMixer) 
    {
	Px_CloseMixer(oMixer);
	oMixer = NULL;
    }
#endif
    //fprintf(stderr, "starting pa\n");

    if(errcnt > 5) {
	iaxc_usermsg(IAXC_TEXT_TYPE_FATALERROR,
		"iaxclient audio: Can't open Audio Device.  Perhaps you do not have an input or output device?");
	/* OK, we'll give the application the option to abort or not here, but we will throw a fatal error
	 * anyway */
	iaxc_millisleep(1000);
	//return -1; // Give Up.  Too many errors.
    }

    /* flush the ringbuffers */
    RingBuffer_Init(&inRing, INRBSZ, inRingBuf);
    RingBuffer_Init(&outRing, OUTRBSZ, outRingBuf);

    if(pa_openstreams(d))  {
	errcnt++;
        return -1;
    }

    errcnt = 0; // only count consecutive errors.

    err = Pa_StartStream(iStream); 
    if(err != paNoError)
	return -1;
#ifndef _POCKETPC_
    iMixer = Px_OpenMixer(iStream, 0);
#endif
    if(!oneStream){ 
	err = Pa_StartStream(oStream);
#ifndef _POCKETPC_
	oMixer = Px_OpenMixer(oStream, 0);
#endif
	if(err != paNoError) {
	    Pa_StopStream(iStream);
	    return -1;
	}
    }
    
    if(selectedRing != selectedOutput) {
        auxStream = 1;
    } else {
        auxStream = 0;
    }

    if(auxStream){ 
        pa_openauxstream(d); 
	err = Pa_StartStream(aStream);
	if(err != paNoError) {
	    auxStream = 0;
	}
    }
#ifndef _POCKETPC_
	/* select the microphone as the input source */
	if ( iMixer != NULL )
	{
		/* try the new method, reverting to the old if it fails */
		if ( Px_SetCurrentInputSourceByName( iMixer, "microphone" ) != 0 )
		{
			int n = Px_GetNumInputSources( iMixer ) - 1 ;
			for ( ; n > 0 ; --n )
			{
				if ( strcasecmp( "microphone", Px_GetInputSourceName( iMixer, n ) ) == 0 )
				{
					Px_SetCurrentInputSource( iMixer, n ) ;
				}
			}
		}
		
		/* try to set the microphone boost */
		Px_SetMicrophoneBoost( iMixer, 0 ) ;
	}
#endif
    running = 1;
    return 0;
}

static int pa_stop (struct iaxc_audio_driver *d ) {
    PaError err;

    if(!running) return 0;
    if(sounds) return 0;

//	if(iMixer)
//		Px_CloseMixer(iMixer);
    err = Pa_AbortStream(iStream); 
    err = Pa_CloseStream(iStream); 


    if(!oneStream){ 
	err = Pa_AbortStream(oStream);
	err = Pa_CloseStream(oStream);
    }

    if(auxStream){ 
	err = Pa_AbortStream(aStream);
	err = Pa_CloseStream(aStream);
    }

    running = 0;
    return 0;
}

static void pa_shutdown() {
    CloseAudioStream( iStream );
    if(!oneStream) CloseAudioStream( oStream );
    if(auxStream) CloseAudioStream( aStream );
#ifndef _POCKETPC_
	if(iMixer)
		Px_CloseMixer(iMixer);
#endif

}

static void handle_paerror(PaError err, char * where) {
	fprintf(stderr, "PortAudio error at %s: %s\n", where, Pa_GetErrorText(err));
}

static int pa_input(struct iaxc_audio_driver *d, void *samples, int *nSamples) {
	int bytestoread;

	bytestoread = *nSamples * sizeof(SAMPLE);

	/* we don't return partial buffers */
	if(RingBuffer_GetReadAvailable(&inRing) < bytestoread) {
	    *nSamples = 0;
	    return 0;	
	}

	RingBuffer_Read(&inRing, samples, bytestoread);

	return 0;
}

static int pa_output(struct iaxc_audio_driver *d, void *samples, int nSamples) {
	int bytestowrite = nSamples * sizeof(SAMPLE);
	int outRingLen;

	outRingLen = RingBuffer_GetReadAvailable(&outRing);
	outRingLenAvg = (outRingLenAvg * 9 + outRingLen ) / 10;

	/* if we've got a big output buffer, drop this */
	if(outRingLen > (RBOUTTARGET_BYTES) && outRingLenAvg > RBOUTTARGET_BYTES)  {
	  //fprintf(stderr, "*O*");
	  return outRingLen/2;
	}


	//if(RingBuffer_GetWriteAvailable(&outRing) < bytestowrite)  fprintf(stderr, "O");

	RingBuffer_Write(&outRing, samples, bytestowrite);

	return (outRingLen + bytestowrite)/2;

}

static int pa_select_devices (struct iaxc_audio_driver *d, int input, int output, int ring) {
    selectedInput = input;
    selectedOutput = output;
    selectedRing = ring;
    if(running) {
      pa_stop(d);
      pa_start(d);
    }
    return 0;
}

static int pa_selected_devices (struct iaxc_audio_driver *d, int *input, int *output, int *ring) {
    *input = selectedInput;
    *output = selectedOutput;
    *ring = selectedRing;
    return 0;
}

static int pa_destroy (struct iaxc_audio_driver *d ) {
    //implementme
	free(d->devices);
#ifndef _POCKETPC_
	if(iMixer)
	{
		Px_CloseMixer(iMixer);
		iMixer = NULL;
	}
#endif
    return 0;
}

static double pa_input_level_get(struct iaxc_audio_driver *d)
{
	/* iMixer should be non-null if we using either one or two streams */
#ifndef _POCKETPC_
    if(!iMixer) return -1;

	/* make sure this device supports input volume controls */
	if ( Px_GetNumInputSources( iMixer ) == 0 )
		return -1 ;

    return Px_GetInputVolume(iMixer);
#else
	return 0;
#endif
}

static double pa_output_level_get(struct iaxc_audio_driver *d){
#ifndef _POCKETPC_
    PxMixer *mix;

	/* oMixer may be null if we're using one stream,
	   in which case, iMixer should not be null,
	   if it is, return an error */
	   
    if(oMixer)
      mix = oMixer;
    else if (iMixer)
      mix = iMixer;
    else
      return -1;

	/* prefer the pcm output, but default to the master output */
	if ( Px_SupportsPCMOutputVolume( mix ) )
		return Px_GetPCMOutputVolume( mix );
	else
		return Px_GetMasterVolume( mix );
#endif
}

static int pa_input_level_set(struct iaxc_audio_driver *d, double level){
#ifndef _POCKETPC_
    if(!iMixer) return -1;
     
	/* make sure this device supports input volume controls */
	if ( Px_GetNumInputSources( iMixer ) == 0 )
		return -1 ;

    //fprintf(stderr, "setting input level to %f\n", level);
    Px_SetInputVolume(iMixer, level);
#endif
    return 0;
}

static int pa_output_level_set(struct iaxc_audio_driver *d, double level){
#ifndef _POCKETPC_
    PxMixer *mix;

    if(oMixer)
      mix = oMixer;
    else if (iMixer)
      mix = iMixer;
    else
      return -1;

	/* prefer the pcm output, but default to the master output */
	if ( Px_SupportsPCMOutputVolume( mix ) ) 
		Px_SetPCMOutputVolume( mix, level );
	else 
		Px_SetMasterVolume( mix, level );
#endif
    return 0;
}

static int pa_mic_boost_get( struct iaxc_audio_driver* d )
{
	int enable = -1 ;
#ifndef _POCKETPC_
	if ( iMixer != NULL )
		enable = Px_GetMicrophoneBoost( iMixer ) ;
#endif
	return enable ;
}

int pa_mic_boost_set( struct iaxc_audio_driver* d, int enable )
{
#ifndef _POCKETPC_
	int err = -1 ;
	if ( iMixer != NULL )
		err = Px_SetMicrophoneBoost( iMixer, enable ) ;
	return err ;
#else
	return 0;
#endif
	
}

/* initialize audio driver */
int pa_initialize (struct iaxc_audio_driver *d, int sr) {
    PaError  err;

    sample_rate = sr;

    /* initialize portaudio */
    if(paNoError != (err = Pa_Initialize()))
	return err;

    /* scan devices */
    scan_devices(d);

    /* setup methods */
    d->initialize = pa_initialize;
    d->destroy = pa_destroy;
    d->select_devices = pa_select_devices;
    d->selected_devices = pa_selected_devices;
    d->start = pa_start;
    d->stop = pa_stop;
    d->output = pa_output;
    d->input = pa_input;
    d->input_level_get = pa_input_level_get;
    d->input_level_set = pa_input_level_set;
    d->output_level_get = pa_output_level_get;
    d->output_level_set = pa_output_level_set;
    d->play_sound = pa_play_sound;
    d->stop_sound = pa_stop_sound;
    d->mic_boost_get = pa_mic_boost_get;
    d->mic_boost_set = pa_mic_boost_set;

    /* setup private data stuff */
    selectedInput  = Pa_GetDefaultInputDeviceID();
    selectedOutput = Pa_GetDefaultOutputDeviceID();
    selectedRing   = Pa_GetDefaultOutputDeviceID();
    sounds	   = NULL;
    MUTEXINIT(&sound_lock);

    

    RingBuffer_Init(&inRing, INRBSZ, inRingBuf);
    RingBuffer_Init(&outRing, OUTRBSZ, outRingBuf);

    running = 0;

    /* start, then stop streams, in order to test devices, and get mixers */
    pa_start(d);
    /* if input level is very low, raise it a bit;  helps AAGC work properly */
    {
      double level;
      level = pa_input_level_get(d);
      if(level < 0.5)
	pa_input_level_set(d,0.6);
    }
    pa_stop(d);

    return 0;
}

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -