📄 pa_linux_alsa.c
字号:
/* * $Id: pa_linux_alsa.c,v 1.2 2004/04/22 04:19:50 mbrubeck Exp $ * PortAudio Portable Real-Time Audio Library * Latest Version at: http://www.portaudio.com * ALSA implementation by Joshua Haberman * * Copyright (c) 2002 Joshua Haberman <joshua@haberman.com> * * Based on the Open Source API proposed by Ross Bencina * Copyright (c) 1999-2002 Ross Bencina, Phil Burk * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files * (the "Software"), to deal in the Software without restriction, * including without limitation the rights to use, copy, modify, merge, * publish, distribute, sublicense, and/or sell copies of the Software, * and to permit persons to whom the Software is furnished to do so, * subject to the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * Any person wishing to distribute modifications to the Software is * requested to send the modifications to the original developer so that * they can be incorporated into the canonical version. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */#define ALSA_PCM_NEW_HW_PARAMS_API#define ALSA_PCM_NEW_SW_PARAMS_API#include <alsa/asoundlib.h>#undef ALSA_PCM_NEW_HW_PARAMS_API#undef ALSA_PCM_NEW_SW_PARAMS_API#include <sys/poll.h>#include <string.h> /* strlen() */#include <limits.h>#include <math.h>#include <pthread.h>#include "portaudio.h"#include "pa_util.h"#include "pa_allocation.h"#include "pa_hostapi.h"#include "pa_stream.h"#include "pa_cpuload.h"#include "pa_process.h"#include "pa_linux_alsa.h"#define MIN(x,y) ( (x) < (y) ? (x) : (y) )#define MAX(x,y) ( (x) > (y) ? (x) : (y) )static pthread_mutex_t gmtx; /* Global mutex */static int aErr_; /* Used with ENSURE */static PaError paErr_; /* Used with PA_ENSURE */typedef enum{ streamIn, streamOut} StreamIO;#define STRINGIZE_HELPER(exp) #exp#define STRINGIZE(exp) STRINGIZE_HELPER(exp)/* Check return value of ALSA function, and map it to PaError */#define ENSURE(exp, code) \ if( (aErr_ = (exp)) < 0 ) \ { \ if( (code) == paUnanticipatedHostError ) \ { \ pthread_mutex_lock( &gmtx ); \ PaUtil_SetLastHostErrorInfo( paALSA, aErr_, snd_strerror( aErr_ ) ); \ pthread_mutex_unlock( &gmtx ); \ } \ PA_DEBUG(( "Expression '" #exp "' failed in '" __FILE__ "', line: " STRINGIZE( __LINE__ ) "\n" )); \ result = (code); \ goto error; \ }/* Check PaError */#define PA_ENSURE(exp) \ if( (paErr_ = (exp)) < paNoError ) \ { \ PA_DEBUG(( "Expression '" #exp "' failed in '" __FILE__ "', line: " STRINGIZE( __LINE__ ) "\n" )); \ result = paErr_; \ goto error; \ }#define UNLESS(exp, code) \ if( (exp) == 0 ) \ { \ PA_DEBUG(( "Expression '" #exp "' failed in '" __FILE__ "', line: " STRINGIZE( __LINE__ ) "\n" )); \ result = (code); \ goto error; \ }/* Implementation specific stream structure */typedef struct PaAlsaStream{ PaUtilStreamRepresentation streamRepresentation; PaUtilCpuLoadMeasurer cpuLoadMeasurer; PaUtilBufferProcessor bufferProcessor; snd_pcm_t *pcm_capture; snd_pcm_t *pcm_playback; snd_pcm_uframes_t frames_per_period; snd_pcm_uframes_t playbackBufferSize; snd_pcm_uframes_t captureBufferSize; snd_pcm_format_t playbackNativeFormat; int capture_channels; int playback_channels; int capture_interleaved; /* bool: is capture interleaved? */ int playback_interleaved; /* bool: is playback interleaved? */ int callback_mode; /* bool: are we running in callback mode? */ int callback_finished; /* bool: are we in the "callback finished" state? See if stream has been stopped in background */ pthread_t callback_thread; /* the callback thread uses these to poll the sound device(s), waiting * for data to be ready/available */ unsigned int capture_nfds; unsigned int playback_nfds; struct pollfd *pfds; int pollTimeout; /* these aren't really stream state, the callback uses them */ snd_pcm_uframes_t capture_offset; snd_pcm_uframes_t playback_offset; int pcmsSynced; /* Have we successfully synced pcms */ int callbackAbort; /* Drop frames? */ int isActive; /* Is stream in active state? (Between StartStream and StopStream || !paContinue) */ snd_pcm_uframes_t startThreshold; pthread_mutex_t stateMtx; /* Used to synchronize access to stream state */ pthread_mutex_t startMtx; /* Used to synchronize stream start in callback mode */ pthread_cond_t startCond; /* Wait untill audio is started in callback thread */ /* Used by callback thread for underflow/overflow handling */ snd_pcm_sframes_t playbackAvail; snd_pcm_sframes_t captureAvail; int neverDropInput; PaTime underrun; PaTime overrun;}PaAlsaStream;/* PaAlsaHostApiRepresentation - host api datastructure specific to this implementation */typedef struct{ PaUtilHostApiRepresentation commonHostApiRep; PaUtilStreamInterface callbackStreamInterface; PaUtilStreamInterface blockingStreamInterface; PaUtilAllocationGroup *allocations; PaHostApiIndex hostApiIndex;}PaAlsaHostApiRepresentation;typedef struct PaAlsaDeviceInfo{ PaDeviceInfo commonDeviceInfo; int deviceNumber;}PaAlsaDeviceInfo;/* prototypes for functions declared in this file */PaError PaAlsa_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex hostApiIndex );static void Terminate( struct PaUtilHostApiRepresentation *hostApi );static PaError IsFormatSupported( struct PaUtilHostApiRepresentation *hostApi, const PaStreamParameters *inputParameters, const PaStreamParameters *outputParameters, double sampleRate );static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi, PaStream** s, const PaStreamParameters *inputParameters, const PaStreamParameters *outputParameters, double sampleRate, unsigned long framesPerBuffer, PaStreamFlags streamFlags, PaStreamCallback *callback, void *userData );static PaError CloseStream( PaStream* stream );static PaError StartStream( PaStream *stream );static PaError StopStream( PaStream *stream );static PaError AbortStream( PaStream *stream );static PaError IsStreamStopped( PaStream *s );static PaError IsStreamActive( PaStream *stream );static PaTime GetStreamTime( PaStream *stream );static double GetStreamCpuLoad( PaStream* stream );static PaError BuildDeviceList( PaAlsaHostApiRepresentation *hostApi );void CleanUpStream( PaAlsaStream *stream );int SetApproximateSampleRate( snd_pcm_t *pcm, snd_pcm_hw_params_t *hwParams, double sampleRate );int GetExactSampleRate( snd_pcm_hw_params_t *hwParams, double *sampleRate );/* Callback prototypes */static void *CallbackThread( void *userData );/* Blocking prototypes */static signed long GetStreamReadAvailable( PaStream* s );static signed long GetStreamWriteAvailable( PaStream* s );static PaError ReadStream( PaStream* stream, void *buffer, unsigned long frames );static PaError WriteStream( PaStream* stream, const void *buffer, unsigned long frames );PaError PaAlsa_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex hostApiIndex ){ PaError result = paNoError; PaAlsaHostApiRepresentation *alsaHostApi = NULL; UNLESS( alsaHostApi = (PaAlsaHostApiRepresentation*) PaUtil_AllocateMemory( sizeof(PaAlsaHostApiRepresentation) ), paInsufficientMemory ); UNLESS( alsaHostApi->allocations = PaUtil_CreateAllocationGroup(), paInsufficientMemory ); alsaHostApi->hostApiIndex = hostApiIndex; *hostApi = (PaUtilHostApiRepresentation*)alsaHostApi; (*hostApi)->info.structVersion = 1; (*hostApi)->info.type = paALSA; (*hostApi)->info.name = "ALSA"; (*hostApi)->Terminate = Terminate; (*hostApi)->OpenStream = OpenStream; (*hostApi)->IsFormatSupported = IsFormatSupported; PaUtil_InitializeStreamInterface( &alsaHostApi->callbackStreamInterface, CloseStream, StartStream, StopStream, AbortStream, IsStreamStopped, IsStreamActive, GetStreamTime, GetStreamCpuLoad, PaUtil_DummyRead, PaUtil_DummyWrite, PaUtil_DummyGetReadAvailable, PaUtil_DummyGetWriteAvailable ); PaUtil_InitializeStreamInterface( &alsaHostApi->blockingStreamInterface, CloseStream, StartStream, StopStream, AbortStream, IsStreamStopped, IsStreamActive, GetStreamTime, PaUtil_DummyGetCpuLoad, ReadStream, WriteStream, GetStreamReadAvailable, GetStreamWriteAvailable ); BuildDeviceList( alsaHostApi ); UNLESS( !pthread_mutex_init( &gmtx, NULL ), paInternalError ); /* 0 == success */ return result;error: if( alsaHostApi ) { if( alsaHostApi->allocations ) { PaUtil_FreeAllAllocations( alsaHostApi->allocations ); PaUtil_DestroyAllocationGroup( alsaHostApi->allocations ); } PaUtil_FreeMemory( alsaHostApi ); } return result;}/*! \brief Determine max channels and default latencies This function provides functionality to grope an opened (might be opened for capture or playback) pcm device for traits like max channels, suitable default latencies and default sample rate. Upon error, max channels is set to zero, and a suitable result returned. The device is closed before returning. */static PaError GropeDevice( snd_pcm_t *pcm, int *channels, double *defaultLowLatency, double *defaultHighLatency, double *defaultSampleRate ){ PaError result = paNoError; snd_pcm_hw_params_t *hwParams; snd_pcm_uframes_t lowLatency = 1024, highLatency = 16384; unsigned int uchans; assert( pcm ); ENSURE( snd_pcm_nonblock( pcm, 0 ), paUnanticipatedHostError ); snd_pcm_hw_params_alloca( &hwParams ); snd_pcm_hw_params_any( pcm, hwParams ); if (*defaultSampleRate != 0.) { /* Could be that the device opened in one mode supports samplerates that the other mode wont have, * so try again .. */ if( SetApproximateSampleRate( pcm, hwParams, *defaultSampleRate ) < 0 ) { *defaultSampleRate = 0.; PA_DEBUG(( "Original default samplerate failed, trying again ..\n" )); } } if( *defaultSampleRate == 0. ) /* Default sample rate not set */ { unsigned int sampleRate = 44100; /* Will contain approximate rate returned by alsa-lib */ ENSURE( snd_pcm_hw_params_set_rate_near( pcm, hwParams, &sampleRate, NULL ), paUnanticipatedHostError ); ENSURE( GetExactSampleRate( hwParams, defaultSampleRate ), paUnanticipatedHostError ); } ENSURE( snd_pcm_hw_params_get_channels_max( hwParams, &uchans ), paUnanticipatedHostError ); assert( uchans <= INT_MAX ); *channels = (int) uchans; assert( *channels > 0 ); /* Weird linking issue could cause wrong version of ALSA symbols to be called, resulting in zeroed values */ /* TWEAKME: * * Giving values for default min and max latency is not * straightforward. Here are our objectives: * * * for low latency, we want to give the lowest value * that will work reliably. This varies based on the * sound card, kernel, CPU, etc. I think it is better * to give sub-optimal latency than to give a number * too low and cause dropouts. My conservative * estimate at this point is to base it on 4096-sample * latency at 44.1 kHz, which gives a latency of 23ms. * * for high latency we want to give a large enough * value that dropouts are basically impossible. This * doesn't really require as much tweaking, since * providing too large a number will just cause us to * select the nearest setting that will work at stream * config time. */ ENSURE( snd_pcm_hw_params_set_buffer_size_near( pcm, hwParams, &lowLatency ), paUnanticipatedHostError ); /* Have to reset hwParams, to set new buffer size */ ENSURE( snd_pcm_hw_params_any( pcm, hwParams ), paUnanticipatedHostError );
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -