From: dmitrykos Date: Thu, 16 Feb 2012 16:59:03 +0000 (+0000) Subject: alsa: X-Git-Tag: pa_stable_v19_20140130_r1919~84 X-Git-Url: https://andrewgundersen.net/repos?a=commitdiff_plain;h=d9980664525f4551fc2b3f38e5a8a7229139ac5b;p=portaudio alsa: - support for sub-devices; - fixed 24-bit format support for some devices which on little-endian machine operate only in big-endian format, example of such device is Audio4DJ which supports only SND_PCM_FORMAT_S24_3BE (for this patch implemented support only for 24-bit audio samples although other formats may be added in case of necessity and if such devices exist); ! special thanks to Owen Williams from Mixxx project who tested these changes/fixes on real Audio4DJ device. --- diff --git a/src/hostapi/alsa/pa_linux_alsa.c b/src/hostapi/alsa/pa_linux_alsa.c index 3bec0d3..76a7785 100644 --- a/src/hostapi/alsa/pa_linux_alsa.c +++ b/src/hostapi/alsa/pa_linux_alsa.c @@ -7,6 +7,7 @@ * Copyright (c) 2002 Joshua Haberman * Copyright (c) 2005-2009 Arve Knudsen * Copyright (c) 2008 Kevin Kofler + * Copyright (c) 2011 Dmitry Kostjuchenko * * Based on the Open Source API proposed by Ross Bencina * Copyright (c) 1999-2002 Ross Bencina, Phil Burk @@ -79,10 +80,17 @@ #include "pa_linux_alsa.h" +/* Add missing define (for compatibility with older ALSA versions). */ #ifndef SND_PCM_TSTAMP_ENABLE -#define SND_PCM_TSTAMP_ENABLE SND_PCM_TSTAMP_MMAP + #define SND_PCM_TSTAMP_ENABLE SND_PCM_TSTAMP_MMAP #endif +/* Specifies that hardware audio sample needs byte-swapping into platfom native value representation. */ +#define paSwapEndian ((PaSampleFormat) 0x40000000) /**< @see PaSampleFormat */ + +/* Remove paSwapEndian and paNonInterleaved flags to get pure format value. */ +#define PA_ALSA_TO_FORMAT(X) ((X) & ~(paSwapEndian|paNonInterleaved)) + /* Defines Alsa function types and pointers to these functions. */ #define _PA_DEFINE_FUNC(x) typedef typeof(x) x##_ft; static x##_ft *alsa_##x = 0 @@ -186,6 +194,9 @@ _PA_DEFINE_FUNC(snd_pcm_info_set_subdevice); _PA_DEFINE_FUNC(snd_pcm_info_set_stream); _PA_DEFINE_FUNC(snd_pcm_info_get_name); _PA_DEFINE_FUNC(snd_pcm_info_get_card); +_PA_DEFINE_FUNC(snd_pcm_info_get_subdevices_count); +_PA_DEFINE_FUNC(snd_pcm_info_get_subdevice_name); +_PA_DEFINE_FUNC(snd_pcm_info_get_subdevices_avail); #define alsa_snd_pcm_info_alloca(ptr) __alsa_snd_alloca(ptr, snd_pcm_info) _PA_DEFINE_FUNC(snd_ctl_pcm_next_device); @@ -244,96 +255,96 @@ static void *g_AlsaLib = NULL; int _PA_LOCAL_IMPL(snd_pcm_hw_params_set_rate_near) (snd_pcm_t *pcm, snd_pcm_hw_params_t *params, unsigned int *val, int *dir) { - int ret; + int ret; - if ((ret = alsa_snd_pcm_hw_params_set_rate(pcm, params, (*val), (*dir))) < 0) - return ret; + if ((ret = alsa_snd_pcm_hw_params_set_rate(pcm, params, (*val), (*dir))) < 0) + return ret; - return 0; + return 0; } int _PA_LOCAL_IMPL(snd_pcm_hw_params_set_buffer_size_near) (snd_pcm_t *pcm, snd_pcm_hw_params_t *params, snd_pcm_uframes_t *val) { - int ret; + int ret; - if ((ret = alsa_snd_pcm_hw_params_set_buffer_size(pcm, params, (*val))) < 0) - return ret; + if ((ret = alsa_snd_pcm_hw_params_set_buffer_size(pcm, params, (*val))) < 0) + return ret; - return 0; + return 0; } int _PA_LOCAL_IMPL(snd_pcm_hw_params_set_period_size_near) (snd_pcm_t *pcm, snd_pcm_hw_params_t *params, snd_pcm_uframes_t *val, int *dir) { - int ret; + int ret; - if ((ret = alsa_snd_pcm_hw_params_set_period_size(pcm, params, (*val), (*dir))) < 0) - return ret; + if ((ret = alsa_snd_pcm_hw_params_set_period_size(pcm, params, (*val), (*dir))) < 0) + return ret; - return 0; + return 0; } int _PA_LOCAL_IMPL(snd_pcm_hw_params_get_channels_min) (const snd_pcm_hw_params_t *params, unsigned int *val) { - (*val) = 1; - return 0; + (*val) = 1; + return 0; } int _PA_LOCAL_IMPL(snd_pcm_hw_params_get_channels_max) (const snd_pcm_hw_params_t *params, unsigned int *val) { - (*val) = 2; - return 0; + (*val) = 2; + return 0; } int _PA_LOCAL_IMPL(snd_pcm_hw_params_get_periods_min) (const snd_pcm_hw_params_t *params, unsigned int *val, int *dir) { - (*val) = 2; - return 0; + (*val) = 2; + return 0; } int _PA_LOCAL_IMPL(snd_pcm_hw_params_get_periods_max) (const snd_pcm_hw_params_t *params, unsigned int *val, int *dir) { - (*val) = 8; - return 0; + (*val) = 8; + return 0; } int _PA_LOCAL_IMPL(snd_pcm_hw_params_get_period_size_min) (const snd_pcm_hw_params_t *params, snd_pcm_uframes_t *frames, int *dir) { - (*frames) = 64; - return 0; + (*frames) = 64; + return 0; } int _PA_LOCAL_IMPL(snd_pcm_hw_params_get_period_size_max) (const snd_pcm_hw_params_t *params, snd_pcm_uframes_t *frames, int *dir) { - (*frames) = 512; - return 0; + (*frames) = 512; + return 0; } int _PA_LOCAL_IMPL(snd_pcm_hw_params_get_buffer_size_max) (const snd_pcm_hw_params_t *params, snd_pcm_uframes_t *val) { - int ret; - int dir = 0; - snd_pcm_uframes_t pmax = 0; - unsigned int pcnt = 0; - - if ((ret = _PA_LOCAL_IMPL(snd_pcm_hw_params_get_period_size_max)(params, &pmax, &dir)) < 0) - return ret; - if ((ret = _PA_LOCAL_IMPL(snd_pcm_hw_params_get_periods_max)(params, &pcnt, &dir)) < 0) - return ret; - - (*val) = pmax * pcnt; - return 0; + int ret; + int dir = 0; + snd_pcm_uframes_t pmax = 0; + unsigned int pcnt = 0; + + if ((ret = _PA_LOCAL_IMPL(snd_pcm_hw_params_get_period_size_max)(params, &pmax, &dir)) < 0) + return ret; + if ((ret = _PA_LOCAL_IMPL(snd_pcm_hw_params_get_periods_max)(params, &pcnt, &dir)) < 0) + return ret; + + (*val) = pmax * pcnt; + return 0; } int _PA_LOCAL_IMPL(snd_pcm_hw_params_get_rate_min) (const snd_pcm_hw_params_t *params, unsigned int *val, int *dir) { - (*val) = 44100; - return 0; + (*val) = 44100; + return 0; } int _PA_LOCAL_IMPL(snd_pcm_hw_params_get_rate_max) (const snd_pcm_hw_params_t *params, unsigned int *val, int *dir) { - (*val) = 44100; - return 0; + (*val) = 44100; + return 0; } #endif // PA_ALSA_DYNAMIC @@ -345,22 +356,22 @@ static int PaAlsa_LoadLibrary() { #ifdef PA_ALSA_DYNAMIC - PA_DEBUG(( "%s: loading ALSA library file - %s\n", __FUNCTION__, g_AlsaLibName )); + PA_DEBUG(( "%s: loading ALSA library file - %s\n", __FUNCTION__, g_AlsaLibName )); - dlerror(); + dlerror(); g_AlsaLib = dlopen(g_AlsaLibName, (RTLD_NOW|RTLD_GLOBAL)); if (g_AlsaLib == NULL) { - PA_DEBUG(( "%s: failed dlopen() ALSA library file - %s, error: %s\n", __FUNCTION__, g_AlsaLibName, dlerror() )); - return 0; + PA_DEBUG(( "%s: failed dlopen() ALSA library file - %s, error: %s\n", __FUNCTION__, g_AlsaLibName, dlerror() )); + return 0; } PA_DEBUG(( "%s: loading ALSA API\n", __FUNCTION__ )); #define _PA_LOAD_FUNC(x) do { \ - alsa_##x = dlsym(g_AlsaLib, #x); \ - if (alsa_##x == NULL) { \ - PA_DEBUG(( "%s: symbol [%s] not found in - %s, error: %s\n", __FUNCTION__, #x, g_AlsaLibName, dlerror() )); }\ + alsa_##x = dlsym(g_AlsaLib, #x); \ + if (alsa_##x == NULL) { \ + PA_DEBUG(( "%s: symbol [%s] not found in - %s, error: %s\n", __FUNCTION__, #x, g_AlsaLibName, dlerror() )); }\ } while(0) #else @@ -464,6 +475,9 @@ _PA_LOAD_FUNC(snd_pcm_info_set_subdevice); _PA_LOAD_FUNC(snd_pcm_info_set_stream); _PA_LOAD_FUNC(snd_pcm_info_get_name); _PA_LOAD_FUNC(snd_pcm_info_get_card); +_PA_LOAD_FUNC(snd_pcm_info_get_subdevices_count); +_PA_LOAD_FUNC(snd_pcm_info_get_subdevice_name); +_PA_LOAD_FUNC(snd_pcm_info_get_subdevices_avail); _PA_LOAD_FUNC(snd_ctl_pcm_next_device); _PA_LOAD_FUNC(snd_ctl_pcm_info); @@ -499,29 +513,29 @@ _PA_LOAD_FUNC(snd_output_stdio_attach); #undef _PA_LOAD_FUNC #ifdef PA_ALSA_DYNAMIC - PA_DEBUG(( "%s: loaded ALSA API - ok\n", __FUNCTION__ )); + PA_DEBUG(( "%s: loaded ALSA API - ok\n", __FUNCTION__ )); #define _PA_VALIDATE_LOAD_REPLACEMENT(x)\ - do {\ - if (alsa_##x == NULL)\ - {\ - alsa_##x = &_PA_LOCAL_IMPL(x);\ - PA_DEBUG(( "%s: replacing [%s] with local implementation\n", __FUNCTION__, #x ));\ - }\ - } while (0) - - _PA_VALIDATE_LOAD_REPLACEMENT(snd_pcm_hw_params_set_rate_near); - _PA_VALIDATE_LOAD_REPLACEMENT(snd_pcm_hw_params_set_buffer_size_near); - _PA_VALIDATE_LOAD_REPLACEMENT(snd_pcm_hw_params_set_period_size_near); - _PA_VALIDATE_LOAD_REPLACEMENT(snd_pcm_hw_params_get_channels_min); - _PA_VALIDATE_LOAD_REPLACEMENT(snd_pcm_hw_params_get_channels_max); - _PA_VALIDATE_LOAD_REPLACEMENT(snd_pcm_hw_params_get_periods_min); - _PA_VALIDATE_LOAD_REPLACEMENT(snd_pcm_hw_params_get_periods_max); - _PA_VALIDATE_LOAD_REPLACEMENT(snd_pcm_hw_params_get_period_size_min); - _PA_VALIDATE_LOAD_REPLACEMENT(snd_pcm_hw_params_get_period_size_max); - _PA_VALIDATE_LOAD_REPLACEMENT(snd_pcm_hw_params_get_buffer_size_max); - _PA_VALIDATE_LOAD_REPLACEMENT(snd_pcm_hw_params_get_rate_min); - _PA_VALIDATE_LOAD_REPLACEMENT(snd_pcm_hw_params_get_rate_max); + do {\ + if (alsa_##x == NULL)\ + {\ + alsa_##x = &_PA_LOCAL_IMPL(x);\ + PA_DEBUG(( "%s: replacing [%s] with local implementation\n", __FUNCTION__, #x ));\ + }\ + } while (0) + + _PA_VALIDATE_LOAD_REPLACEMENT(snd_pcm_hw_params_set_rate_near); + _PA_VALIDATE_LOAD_REPLACEMENT(snd_pcm_hw_params_set_buffer_size_near); + _PA_VALIDATE_LOAD_REPLACEMENT(snd_pcm_hw_params_set_period_size_near); + _PA_VALIDATE_LOAD_REPLACEMENT(snd_pcm_hw_params_get_channels_min); + _PA_VALIDATE_LOAD_REPLACEMENT(snd_pcm_hw_params_get_channels_max); + _PA_VALIDATE_LOAD_REPLACEMENT(snd_pcm_hw_params_get_periods_min); + _PA_VALIDATE_LOAD_REPLACEMENT(snd_pcm_hw_params_get_periods_max); + _PA_VALIDATE_LOAD_REPLACEMENT(snd_pcm_hw_params_get_period_size_min); + _PA_VALIDATE_LOAD_REPLACEMENT(snd_pcm_hw_params_get_period_size_max); + _PA_VALIDATE_LOAD_REPLACEMENT(snd_pcm_hw_params_get_buffer_size_max); + _PA_VALIDATE_LOAD_REPLACEMENT(snd_pcm_hw_params_get_rate_min); + _PA_VALIDATE_LOAD_REPLACEMENT(snd_pcm_hw_params_get_rate_max); #undef _PA_LOCAL_IMPL #undef _PA_VALIDATE_LOAD_REPLACEMENT @@ -545,14 +559,14 @@ static void PaAlsa_CloseLibrary() { #ifdef PA_ALSA_DYNAMIC dlclose(g_AlsaLib); - g_AlsaLib = NULL; + g_AlsaLib = NULL; #endif } /* Check return value of ALSA function, and map it to PaError */ #define ENSURE_(expr, code) \ do { \ - int __pa_unsure_error_id;\ + int __pa_unsure_error_id;\ if( UNLIKELY( (__pa_unsure_error_id = (expr)) < 0 ) ) \ { \ /* PaUtil_SetLastHostErrorInfo should only be used in the main thread */ \ @@ -570,10 +584,10 @@ static void PaAlsa_CloseLibrary() #define ASSERT_CALL_(expr, success) \ do {\ - int __pa_assert_error_id;\ - __pa_assert_error_id = (expr);\ - assert( success == __pa_assert_error_id );\ - } while (0) + int __pa_assert_error_id;\ + __pa_assert_error_id = (expr);\ + assert( success == __pa_assert_error_id );\ + } while (0) static int numPeriods_ = 4; static int busyRetries_ = 100; @@ -715,6 +729,9 @@ static const PaAlsaDeviceInfo *GetDeviceInfo( const PaUtilHostApiRepresentation return (const PaAlsaDeviceInfo *)hostApi->deviceInfos[device]; } +static void CheckAndReplaceConverterForSwapEndian(PaAlsaStream *stream, PaSampleFormat hostInputSampleFormat, + PaSampleFormat hostOutputSampleFormat); + /** Uncommented because AlsaErrorHandler is unused for anything good yet. If AlsaErrorHandler is to be used, do not forget to register this callback in PaAlsa_Initialize, and unregister in Terminate. */ @@ -982,11 +999,11 @@ HwDevInfo predefinedNames[] = { { "AndroidPlayback_ExtraDockSpeaker_normal", NULL, 0, 1, 0 }, { "AndroidPlayback_TvOut_normal", NULL, 0, 1, 0 }, - { "AndroidRecord_Microphone", NULL, 0, 0, 1 }, + { "AndroidRecord_Microphone", NULL, 0, 0, 1 }, { "AndroidRecord_Earpiece_normal", NULL, 0, 0, 1 }, - { "AndroidRecord_Speaker_normal", NULL, 0, 0, 1 }, + { "AndroidRecord_Speaker_normal", NULL, 0, 0, 1 }, { "AndroidRecord_Headset_normal", NULL, 0, 0, 1 }, - { "AndroidRecord_Bluetooth_normal", NULL, 0, 0, 1 }, + { "AndroidRecord_Bluetooth_normal", NULL, 0, 0, 1 }, { "AndroidRecord_Speaker_Headset_normal", NULL, 0, 0, 1 }, { NULL, NULL, 0, 1, 0 } @@ -1061,8 +1078,7 @@ static int OpenPcm( snd_pcm_t **pcmp, const char *name, snd_pcm_stream_t stream, ret = alsa_snd_pcm_open( pcmp, name, stream, mode ); if( -EBUSY != ret ) { - PA_DEBUG(( "%s: Successfully opened initially busy device after %d tries\n", - __FUNCTION__, tries )); + PA_DEBUG(( "%s: Successfully opened initially busy device after %d tries\n", __FUNCTION__, tries )); } } if( -EBUSY == ret ) @@ -1071,8 +1087,8 @@ static int OpenPcm( snd_pcm_t **pcmp, const char *name, snd_pcm_stream_t stream, } else { - if (ret < 0) - PA_DEBUG(( "%s: Opened device '%s' ptr[%p] - result: [%d:%s]\n", __FUNCTION__, name, *pcmp, ret, alsa_snd_strerror(ret) )); + if (ret < 0) + PA_DEBUG(( "%s: Opened device '%s' ptr[%p] - result: [%d:%s]\n", __FUNCTION__, name, *pcmp, ret, alsa_snd_strerror(ret) )); } return ret; @@ -1086,7 +1102,7 @@ static PaError FillInDevInfo( PaAlsaHostApiRepresentation *alsaApi, HwDevInfo* d snd_pcm_t *pcm = NULL; PaUtilHostApiRepresentation *baseApi = &alsaApi->baseHostApiRep; - PA_DEBUG(( "%s: filling device info for: %s\n", __FUNCTION__, deviceName->name )); + PA_DEBUG(( "%s: Filling device info for: %s\n", __FUNCTION__, deviceName->name )); /* Zero fields */ InitializeDeviceInfo( baseDeviceInfo ); @@ -1096,8 +1112,7 @@ static PaError FillInDevInfo( PaAlsaHostApiRepresentation *alsaApi, HwDevInfo* d /* Query capture */ if( deviceName->hasCapture && - OpenPcm( &pcm, deviceName->alsaName, SND_PCM_STREAM_CAPTURE, blocking, 0 ) - >= 0 ) + OpenPcm( &pcm, deviceName->alsaName, SND_PCM_STREAM_CAPTURE, blocking, 0 ) >= 0 ) { if( GropeDevice( pcm, deviceName->isPlug, StreamDirection_In, blocking, devInfo ) != paNoError ) { @@ -1109,8 +1124,7 @@ static PaError FillInDevInfo( PaAlsaHostApiRepresentation *alsaApi, HwDevInfo* d /* Query playback */ if( deviceName->hasPlayback && - OpenPcm( &pcm, deviceName->alsaName, SND_PCM_STREAM_PLAYBACK, blocking, 0 ) - >= 0 ) + OpenPcm( &pcm, deviceName->alsaName, SND_PCM_STREAM_PLAYBACK, blocking, 0 ) >= 0 ) { if( GropeDevice( pcm, deviceName->isPlug, StreamDirection_Out, blocking, devInfo ) != paNoError ) { @@ -1121,10 +1135,10 @@ static PaError FillInDevInfo( PaAlsaHostApiRepresentation *alsaApi, HwDevInfo* d } baseDeviceInfo->structVersion = 2; - baseDeviceInfo->hostApi = alsaApi->hostApiIndex; - baseDeviceInfo->name = deviceName->name; - devInfo->alsaName = deviceName->alsaName; - devInfo->isPlug = deviceName->isPlug; + baseDeviceInfo->hostApi = alsaApi->hostApiIndex; + baseDeviceInfo->name = deviceName->name; + devInfo->alsaName = deviceName->alsaName; + devInfo->isPlug = deviceName->isPlug; /* A: Storing pointer to PaAlsaDeviceInfo object as pointer to PaDeviceInfo object. * Should now be safe to add device info, unless the device supports neither capture nor playback @@ -1132,14 +1146,14 @@ static PaError FillInDevInfo( PaAlsaHostApiRepresentation *alsaApi, HwDevInfo* d if( baseDeviceInfo->maxInputChannels > 0 || baseDeviceInfo->maxOutputChannels > 0 ) { /* Make device default if there isn't already one or it is the ALSA "default" device */ - if( (baseApi->info.defaultInputDevice == paNoDevice || !strcmp(deviceName->alsaName, - "default" )) && baseDeviceInfo->maxInputChannels > 0 ) + if( (baseApi->info.defaultInputDevice == paNoDevice || + !strcmp(deviceName->alsaName, "default" )) && baseDeviceInfo->maxInputChannels > 0 ) { baseApi->info.defaultInputDevice = *devIdx; PA_DEBUG(("Default input device: %s\n", deviceName->name)); } - if( (baseApi->info.defaultOutputDevice == paNoDevice || !strcmp(deviceName->alsaName, - "default" )) && baseDeviceInfo->maxOutputChannels > 0 ) + if( (baseApi->info.defaultOutputDevice == paNoDevice || + !strcmp(deviceName->alsaName, "default" )) && baseDeviceInfo->maxOutputChannels > 0 ) { baseApi->info.defaultOutputDevice = *devIdx; PA_DEBUG(("Default output device: %s\n", deviceName->name)); @@ -1150,7 +1164,7 @@ static PaError FillInDevInfo( PaAlsaHostApiRepresentation *alsaApi, HwDevInfo* d } else { - PA_DEBUG(( "%s: skipped device: %s, all channels - 0\n", __FUNCTION__, deviceName->name )); + PA_DEBUG(( "%s: Skipped device: %s, all channels == 0\n", __FUNCTION__, deviceName->name )); } end: @@ -1171,7 +1185,6 @@ static PaError BuildDeviceList( PaAlsaHostApiRepresentation *alsaApi ) snd_pcm_info_t *pcmInfo; int res; int blocking = SND_PCM_NONBLOCK; - char alsaCardName[50]; #ifdef PA_ENABLE_DEBUG_OUTPUT PaTime startTime = PaUtil_GetTime(); #endif @@ -1196,87 +1209,130 @@ static PaError BuildDeviceList( PaAlsaHostApiRepresentation *alsaApi ) alsa_snd_pcm_info_alloca( &pcmInfo ); while( alsa_snd_card_next( &cardIdx ) == 0 && cardIdx >= 0 ) { + char alsaCardNameId[64] = { 0 }; char *cardName; int devIdx = -1; snd_ctl_t *ctl; - char buf[50]; - snprintf( alsaCardName, sizeof (alsaCardName), "hw:%d", cardIdx ); + /* Make card name */ + snprintf( alsaCardNameId, sizeof (alsaCardNameId)-1, "hw:%d", cardIdx ); - /* Acquire name of card */ - if( alsa_snd_ctl_open( &ctl, alsaCardName, 0 ) < 0 ) + /* Try opening card */ + if( alsa_snd_ctl_open( &ctl, alsaCardNameId, 0 ) < 0 ) { /* Unable to open card :( */ - PA_DEBUG(( "%s: Unable to open device %s\n", __FUNCTION__, alsaCardName )); + PA_DEBUG(( "%s: Unable to open device %s\n", __FUNCTION__, alsaCardNameId )); continue; } alsa_snd_ctl_card_info( ctl, cardInfo ); - + PA_ENSURE( PaAlsa_StrDup( alsaApi, &cardName, alsa_snd_ctl_card_info_get_name( cardInfo )) ); + + PA_DEBUG(( "%s: Open card: id[%s] name[%s]\n", __FUNCTION__, alsaCardNameId, cardName )); + /* Iterate devices */ while( alsa_snd_ctl_pcm_next_device( ctl, &devIdx ) == 0 && devIdx >= 0 ) { char *alsaDeviceName, *deviceName; size_t len; int hasPlayback = 0, hasCapture = 0; - snprintf( buf, sizeof (buf), "hw:%d,%d", cardIdx, devIdx ); + int subDevIdx, subDevCount = 1; + + PA_DEBUG(( "%s: - idx = %d:\n", __FUNCTION__, devIdx )); - /* Obtain info about this particular device */ + /* Make this device current */ alsa_snd_pcm_info_set_device( pcmInfo, devIdx ); - alsa_snd_pcm_info_set_subdevice( pcmInfo, 0 ); - alsa_snd_pcm_info_set_stream( pcmInfo, SND_PCM_STREAM_CAPTURE ); - if( alsa_snd_ctl_pcm_info( ctl, pcmInfo ) >= 0 ) - { - hasCapture = 1; - } - alsa_snd_pcm_info_set_stream( pcmInfo, SND_PCM_STREAM_PLAYBACK ); - if( alsa_snd_ctl_pcm_info( ctl, pcmInfo ) >= 0 ) + /* Iterate sub-devices */ + for( subDevIdx = 0; subDevIdx < subDevCount; ++subDevIdx ) { - hasPlayback = 1; - } + char buf[64] = { 0 }; - if( !hasPlayback && !hasCapture ) - { - /* Error */ - continue; - } + /* Make this sub-device current */ + alsa_snd_pcm_info_set_subdevice( pcmInfo, subDevIdx ); - /* The length of the string written by snprintf plus terminating 0 */ - len = snprintf( NULL, 0, "%s: %s (%s)", cardName, alsa_snd_pcm_info_get_name( pcmInfo ), buf ) + 1; - PA_UNLESS( deviceName = (char *)PaUtil_GroupAllocateMemory( alsaApi->allocations, len ), - paInsufficientMemory ); - snprintf( deviceName, len, "%s: %s (%s)", cardName, - alsa_snd_pcm_info_get_name( pcmInfo ), buf ); + /* Test for Capture capability */ + alsa_snd_pcm_info_set_stream( pcmInfo, SND_PCM_STREAM_CAPTURE ); + if( alsa_snd_ctl_pcm_info( ctl, pcmInfo ) >= 0 ) + { + hasCapture = 1; + } - ++numDeviceNames; - if( !hwDevInfos || numDeviceNames > maxDeviceNames ) - { - maxDeviceNames *= 2; - PA_UNLESS( hwDevInfos = (HwDevInfo *) realloc( hwDevInfos, maxDeviceNames * sizeof (HwDevInfo) ), - paInsufficientMemory ); - } + /* Test for Playback capability */ + alsa_snd_pcm_info_set_stream( pcmInfo, SND_PCM_STREAM_PLAYBACK ); + if( alsa_snd_ctl_pcm_info( ctl, pcmInfo ) >= 0 ) + { + hasPlayback = 1; + } - PA_ENSURE( PaAlsa_StrDup( alsaApi, &alsaDeviceName, buf ) ); + /* If neither supported, such device is stub or failed */ + if( !hasPlayback && !hasCapture ) + { + continue; + } - hwDevInfos[ numDeviceNames - 1 ].alsaName = alsaDeviceName; - hwDevInfos[ numDeviceNames - 1 ].name = deviceName; - hwDevInfos[ numDeviceNames - 1 ].isPlug = 0; - hwDevInfos[ numDeviceNames - 1 ].hasPlayback = hasPlayback; - hwDevInfos[ numDeviceNames - 1 ].hasCapture = hasCapture; + /* Get sub-device count */ + if( subDevIdx == 0 ) + { + subDevCount = alsa_snd_pcm_info_get_subdevices_count( pcmInfo ); + PA_DEBUG(( "%s: sub-devices: %d/%d\n", __FUNCTION__, alsa_snd_pcm_info_get_subdevices_avail( pcmInfo ), subDevCount)); + } + + PA_DEBUG(( "%s: - sub: %d\n", __FUNCTION__, subDevIdx )); + if( hasCapture ) PA_DEBUG(( "%s: - cap: CAPTURE\n", __FUNCTION__ )); + if( hasPlayback ) PA_DEBUG(( "%s: - cap: PLAYBACK\n", __FUNCTION__ )); + + /* Make name Id */ + snprintf( buf, sizeof(buf)-1, ( subDevCount > 1 ? "hw:%d,%d,%d" : "hw:%d,%d" ), cardIdx, devIdx, subDevIdx ); + + /* Make name */ + if( subDevCount <= 1 ) + { + const char *snd_deviceName = alsa_snd_pcm_info_get_name( pcmInfo ); + + len = snprintf( NULL, 0, "%s: %s (%s)", cardName, snd_deviceName, buf ) + 1; + PA_UNLESS( deviceName = (char *)PaUtil_GroupAllocateMemory( alsaApi->allocations, len ), paInsufficientMemory ); + snprintf( deviceName, len, "%s: %s (%s)", cardName, snd_deviceName, buf ); + } + else + { + const char *snd_deviceName = alsa_snd_pcm_info_get_name( pcmInfo ); + const char *snd_subDeviceName = alsa_snd_pcm_info_get_subdevice_name( pcmInfo ); + + len = snprintf( NULL, 0, "%s: %s (%s) {%s}", cardName, snd_deviceName, buf, snd_subDeviceName ) + 1; + PA_UNLESS( deviceName = (char *)PaUtil_GroupAllocateMemory( alsaApi->allocations, len ), paInsufficientMemory ); + snprintf( deviceName, len, "%s: %s (%s) {%s}", cardName, snd_deviceName, buf, snd_subDeviceName ); + } + + ++numDeviceNames; + if( !hwDevInfos || numDeviceNames > maxDeviceNames ) + { + maxDeviceNames *= 2; + PA_UNLESS( hwDevInfos = (HwDevInfo *) realloc( hwDevInfos, maxDeviceNames * sizeof (HwDevInfo) ), paInsufficientMemory ); + } + + PA_ENSURE( PaAlsa_StrDup( alsaApi, &alsaDeviceName, buf ) ); + + hwDevInfos[ numDeviceNames - 1 ].alsaName = alsaDeviceName; + hwDevInfos[ numDeviceNames - 1 ].name = deviceName; + hwDevInfos[ numDeviceNames - 1 ].isPlug = 0; + hwDevInfos[ numDeviceNames - 1 ].hasPlayback = hasPlayback; + hwDevInfos[ numDeviceNames - 1 ].hasCapture = hasCapture; + + PA_DEBUG(( "%s: Registered device: id[%s] name[%s]\n", __FUNCTION__, alsaDeviceName, deviceName )); + } } alsa_snd_ctl_close( ctl ); } /* Iterate over plugin devices */ - if( NULL == (*alsa_snd_config) ) { /* alsa_snd_config_update is called implicitly by some functions, if this hasn't happened snd_config will be NULL (bleh) */ ENSURE_( alsa_snd_config_update(), paUnanticipatedHostError ); PA_DEBUG(( "Updating snd_config\n" )); } - assert( *alsa_snd_config ); + assert( *alsa_snd_config ); if( (res = alsa_snd_config_search( *alsa_snd_config, "pcm", &topNode )) >= 0 ) { snd_config_iterator_t i, next; @@ -1309,36 +1365,34 @@ static PaError BuildDeviceList( PaAlsaHostApiRepresentation *alsaApi ) } PA_DEBUG(( "%s: Found plugin [%s] of type [%s]\n", __FUNCTION__, idStr, tpStr )); - PA_UNLESS( alsaDeviceName = (char*)PaUtil_GroupAllocateMemory( alsaApi->allocations, - strlen(idStr) + 6 ), paInsufficientMemory ); + PA_UNLESS( alsaDeviceName = (char*)PaUtil_GroupAllocateMemory( alsaApi->allocations, strlen(idStr) + 6 ), paInsufficientMemory ); strcpy( alsaDeviceName, idStr ); - PA_UNLESS( deviceName = (char*)PaUtil_GroupAllocateMemory( alsaApi->allocations, - strlen(idStr) + 1 ), paInsufficientMemory ); + + PA_UNLESS( deviceName = (char*)PaUtil_GroupAllocateMemory( alsaApi->allocations, strlen(idStr) + 1 ), paInsufficientMemory ); strcpy( deviceName, idStr ); ++numDeviceNames; if( !hwDevInfos || numDeviceNames > maxDeviceNames ) { maxDeviceNames *= 2; - PA_UNLESS( hwDevInfos = (HwDevInfo *) realloc( hwDevInfos, maxDeviceNames * sizeof (HwDevInfo) ), - paInsufficientMemory ); + PA_UNLESS( hwDevInfos = (HwDevInfo *) realloc( hwDevInfos, maxDeviceNames * sizeof (HwDevInfo) ), paInsufficientMemory ); } predefined = FindDeviceName( alsaDeviceName ); hwDevInfos[numDeviceNames - 1].alsaName = alsaDeviceName; - hwDevInfos[numDeviceNames - 1].name = deviceName; - hwDevInfos[numDeviceNames - 1].isPlug = 1; + hwDevInfos[numDeviceNames - 1].name = deviceName; + hwDevInfos[numDeviceNames - 1].isPlug = 1; if( predefined ) { hwDevInfos[numDeviceNames - 1].hasPlayback = predefined->hasPlayback; - hwDevInfos[numDeviceNames - 1].hasCapture = predefined->hasCapture; + hwDevInfos[numDeviceNames - 1].hasCapture = predefined->hasCapture; } else { hwDevInfos[numDeviceNames - 1].hasPlayback = 1; - hwDevInfos[numDeviceNames - 1].hasCapture = 1; + hwDevInfos[numDeviceNames - 1].hasCapture = 1; } } } @@ -1346,12 +1400,10 @@ static PaError BuildDeviceList( PaAlsaHostApiRepresentation *alsaApi ) PA_DEBUG(( "%s: Iterating over ALSA plugins failed: %s\n", __FUNCTION__, alsa_snd_strerror( res ) )); /* allocate deviceInfo memory based on the number of devices */ - PA_UNLESS( baseApi->deviceInfos = (PaDeviceInfo**)PaUtil_GroupAllocateMemory( - alsaApi->allocations, sizeof(PaDeviceInfo*) * (numDeviceNames) ), paInsufficientMemory ); + PA_UNLESS( baseApi->deviceInfos = (PaDeviceInfo**)PaUtil_GroupAllocateMemory( alsaApi->allocations, sizeof(PaDeviceInfo*) * (numDeviceNames) ), paInsufficientMemory ); /* allocate all device info structs in a contiguous block */ - PA_UNLESS( deviceInfoArray = (PaAlsaDeviceInfo*)PaUtil_GroupAllocateMemory( - alsaApi->allocations, sizeof(PaAlsaDeviceInfo) * numDeviceNames ), paInsufficientMemory ); + PA_UNLESS( deviceInfoArray = (PaAlsaDeviceInfo*)PaUtil_GroupAllocateMemory( alsaApi->allocations, sizeof(PaAlsaDeviceInfo) * numDeviceNames ), paInsufficientMemory ); /* Loop over list of cards, filling in info. If a device is deemed unavailable (can't get name), * it's ignored. @@ -1361,7 +1413,7 @@ static PaError BuildDeviceList( PaAlsaHostApiRepresentation *alsaApi ) * (dmix) is closed. The 'default' plugin may also point to the dmix plugin, so the same goes * for this. */ - PA_DEBUG(( "%s: filling device info for %d devices\n", __FUNCTION__, numDeviceNames )); + PA_DEBUG(( "%s: Filling device info for %d devices\n", __FUNCTION__, numDeviceNames )); for( i = 0, devIdx = 0; i < numDeviceNames; ++i ) { PaAlsaDeviceInfo* devInfo = &deviceInfoArray[i]; @@ -1384,8 +1436,7 @@ static PaError BuildDeviceList( PaAlsaHostApiRepresentation *alsaApi ) continue; } - PA_ENSURE( FillInDevInfo( alsaApi, hwInfo, blocking, devInfo, - &devIdx ) ); + PA_ENSURE( FillInDevInfo( alsaApi, hwInfo, blocking, devInfo, &devIdx ) ); } free( hwDevInfos ); @@ -1458,9 +1509,13 @@ static PaSampleFormat GetAvailableFormats( snd_pcm_t *pcm ) #ifdef PA_LITTLE_ENDIAN if( alsa_snd_pcm_hw_params_test_format( pcm, hwParams, SND_PCM_FORMAT_S24_3LE ) >= 0) available |= paInt24; + if( alsa_snd_pcm_hw_params_test_format( pcm, hwParams, SND_PCM_FORMAT_S24_3BE ) >= 0) + available |= (paInt24|paSwapEndian); #elif defined PA_BIG_ENDIAN if( alsa_snd_pcm_hw_params_test_format( pcm, hwParams, SND_PCM_FORMAT_S24_3BE ) >= 0) available |= paInt24; + if( alsa_snd_pcm_hw_params_test_format( pcm, hwParams, SND_PCM_FORMAT_S24_3LE ) >= 0) + available |= (paInt24|paSwapEndian); #endif if( alsa_snd_pcm_hw_params_test_format( pcm, hwParams, SND_PCM_FORMAT_S16 ) >= 0) @@ -1484,7 +1539,7 @@ static void LogAllAvailableFormats( snd_pcm_t *pcm ) alsa_snd_pcm_hw_params_any( pcm, hwParams ); - PA_DEBUG(( " --- Supported Formats ---\n" )); + PA_DEBUG(( " --- Supported Formats ---\n" )); if( alsa_snd_pcm_hw_params_test_format( pcm, hwParams, SND_PCM_FORMAT_S8 ) >= 0) PA_DEBUG(( "SND_PCM_FORMAT_S8\n" )); @@ -1599,7 +1654,7 @@ static void LogAllAvailableFormats( snd_pcm_t *pcm ) static snd_pcm_format_t Pa2AlsaFormat( PaSampleFormat paFormat ) { - switch( paFormat ) + switch( PA_ALSA_TO_FORMAT(paFormat) ) { case paFloat32: return SND_PCM_FORMAT_FLOAT; @@ -1609,11 +1664,10 @@ static snd_pcm_format_t Pa2AlsaFormat( PaSampleFormat paFormat ) case paInt24: #ifdef PA_LITTLE_ENDIAN - return SND_PCM_FORMAT_S24_3LE; + return ( paFormat & paSwapEndian ? SND_PCM_FORMAT_S24_3BE : SND_PCM_FORMAT_S24_3LE ); #elif defined PA_BIG_ENDIAN - return SND_PCM_FORMAT_S24_3BE; + return ( paFormat & paSwapEndian ? SND_PCM_FORMAT_S24_3LE : SND_PCM_FORMAT_S24_3BE ); #endif - case paInt32: return SND_PCM_FORMAT_S32; @@ -1716,7 +1770,13 @@ static PaError TestParameters( const PaUtilHostApiRepresentation *hostApi, const /* See if we can find a best possible match */ availableFormats = GetAvailableFormats( pcm ); - PA_ENSURE( hostFormat = PaUtil_SelectClosestAvailableFormat( availableFormats, parameters->sampleFormat ) ); + PA_ENSURE( hostFormat = PaUtil_SelectClosestAvailableFormat( PA_ALSA_TO_FORMAT(availableFormats), parameters->sampleFormat ) ); + + /* Append endiannes conversion flag */ + if ( availableFormats & paSwapEndian ) + { + hostFormat |= paSwapEndian; + } /* Some specific hardware (reported: Audio8 DJ) can fail with assertion during this step. */ ENSURE_( alsa_snd_pcm_hw_params_set_format( pcm, hwParams, Pa2AlsaFormat( hostFormat ) ), paUnanticipatedHostError ); @@ -1804,6 +1864,7 @@ error: static PaError PaAlsaStreamComponent_Initialize( PaAlsaStreamComponent *self, PaAlsaHostApiRepresentation *alsaApi, const PaStreamParameters *params, StreamDirection streamDir, int callbackMode ) { + PaSampleFormat availableFormats; PaError result = paNoError; PaSampleFormat userSampleFormat = params->sampleFormat, hostSampleFormat = paNoError; assert( params->channelCount > 0 ); @@ -1828,8 +1889,16 @@ static PaError PaAlsaStreamComponent_Initialize( PaAlsaStreamComponent *self, Pa PA_ENSURE( AlsaOpen( &alsaApi->baseHostApiRep, params, streamDir, &self->pcm ) ); self->nfds = alsa_snd_pcm_poll_descriptors_count( self->pcm ); - PA_ENSURE( hostSampleFormat = PaUtil_SelectClosestAvailableFormat( GetAvailableFormats( self->pcm ), userSampleFormat ) ); + /* Get host sample format */ + availableFormats = GetAvailableFormats( self->pcm ); + PA_ENSURE( hostSampleFormat = PaUtil_SelectClosestAvailableFormat( PA_ALSA_TO_FORMAT(availableFormats), userSampleFormat ) ); + /* Append endiannes conversion flag */ + if ( availableFormats & paSwapEndian ) + { + hostSampleFormat |= paSwapEndian; + } + self->hostSampleFormat = hostSampleFormat; self->nativeFormat = Pa2AlsaFormat( hostSampleFormat ); self->hostInterleaved = self->userInterleaved = !(userSampleFormat & paNonInterleaved); @@ -1850,7 +1919,7 @@ error: /* Log all available formats. */ if ( hostSampleFormat == paSampleFormatNotSupported ) - { + { LogAllAvailableFormats( self->pcm ); PA_DEBUG(( "%s: Please provide the log output to PortAudio developers, your hardware does not have any sample format implemented yet.\n", __FUNCTION__ )); } @@ -2014,12 +2083,12 @@ static PaError PaAlsaStreamComponent_FinishConfigure( PaAlsaStreamComponent *sel } if (alsa_snd_pcm_hw_params_get_buffer_size != NULL) { - ENSURE_( alsa_snd_pcm_hw_params_get_buffer_size( hwParams, &self->bufferSize ), paUnanticipatedHostError ); - } - else - { - self->bufferSize = bufSz; - } + ENSURE_( alsa_snd_pcm_hw_params_get_buffer_size( hwParams, &self->bufferSize ), paUnanticipatedHostError ); + } + else + { + self->bufferSize = bufSz; + } /* Latency in seconds */ *latency = self->bufferSize / sampleRate; @@ -2145,7 +2214,7 @@ static int CalculatePollTimeout( const PaAlsaStream *stream, unsigned long frame */ static unsigned long PaAlsa_AlignBackward(unsigned long v, unsigned long align) { - return ((v - (align ? v % align : 0))); + return ((v - (align ? v % align : 0))); } /** Align value in forward direction. @@ -2155,8 +2224,8 @@ static unsigned long PaAlsa_AlignBackward(unsigned long v, unsigned long align) */ static unsigned long PaAlsa_AlignForward(unsigned long v, unsigned long align) { - unsigned long remainder = (align ? (v % align) : 0); - return (remainder != 0 ? v + (align - remainder) : v); + unsigned long remainder = (align ? (v % align) : 0); + return (remainder != 0 ? v + (align - remainder) : v); } /** Get size of host buffer maintained from the number of user frames, sample rate and suggested latency. Minimum double buffering @@ -2168,8 +2237,8 @@ static unsigned long PaAlsa_AlignForward(unsigned long v, unsigned long align) */ static unsigned long PaAlsa_GetFramesPerHostBuffer(unsigned long userFramesPerBuffer, PaTime suggestedLatency, double sampleRate) { - unsigned long frames = userFramesPerBuffer + PA_MAX( userFramesPerBuffer, (unsigned long)(suggestedLatency * sampleRate) ); - return frames; + unsigned long frames = userFramesPerBuffer + PA_MAX( userFramesPerBuffer, (unsigned long)(suggestedLatency * sampleRate) ); + return frames; } /** Determine size per host buffer. @@ -2239,7 +2308,7 @@ static PaError PaAlsaStreamComponent_DetermineFramesPerBuffer( PaAlsaStreamCompo #endif - { + { unsigned numPeriods = numPeriods_, maxPeriods = 0, minPeriods = numPeriods_; /* It may be that the device only supports 2 periods for instance */ @@ -2356,7 +2425,7 @@ static PaError PaAlsaStreamComponent_DetermineFramesPerBuffer( PaAlsaStreamCompo framesPerHostBuffer = 2048; #endif PA_DEBUG(( "%s: suggested host buffer period = %lu \n", __FUNCTION__, framesPerHostBuffer )); - } + } { /* Get min/max period sizes and adjust our chosen */ @@ -2377,10 +2446,10 @@ static PaError PaAlsaStreamComponent_DetermineFramesPerBuffer( PaAlsaStreamCompo framesPerHostBuffer = ((minmax_diff == 2) ? max - 1 : max); } - PA_DEBUG(( "%s: device period minimum = %lu\n", __FUNCTION__, min )); - PA_DEBUG(( "%s: device period maximum = %lu\n", __FUNCTION__, max )); - PA_DEBUG(( "%s: host buffer period = %lu\n", __FUNCTION__, framesPerHostBuffer )); - PA_DEBUG(( "%s: host buffer period latency = %f\n", __FUNCTION__, (double)(framesPerHostBuffer / sampleRate) )); + PA_DEBUG(( "%s: device period minimum = %lu\n", __FUNCTION__, min )); + PA_DEBUG(( "%s: device period maximum = %lu\n", __FUNCTION__, max )); + PA_DEBUG(( "%s: host buffer period = %lu\n", __FUNCTION__, framesPerHostBuffer )); + PA_DEBUG(( "%s: host buffer period latency = %f\n", __FUNCTION__, (double)(framesPerHostBuffer / sampleRate) )); /* Try setting period size */ dir = 0; @@ -2764,11 +2833,16 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi, hostOutputSampleFormat = stream->playback.hostSampleFormat | (!stream->playback.hostInterleaved ? paNonInterleaved : 0); PA_ENSURE( PaUtil_InitializeBufferProcessor( &stream->bufferProcessor, - numInputChannels, inputSampleFormat, hostInputSampleFormat, - numOutputChannels, outputSampleFormat, hostOutputSampleFormat, + numInputChannels, inputSampleFormat, (hostInputSampleFormat & ~paSwapEndian), + numOutputChannels, outputSampleFormat, (hostOutputSampleFormat & ~paSwapEndian), sampleRate, streamFlags, framesPerBuffer, stream->maxFramesPerHostBuffer, hostBufferSizeMode, callback, userData ) ); + /* Some drivers may work only in Big-Endian format (like Audio4DJ), check it and replace + original converter with our specific with swapping capability. + */ + CheckAndReplaceConverterForSwapEndian(stream, hostInputSampleFormat, hostOutputSampleFormat); + /* Ok, buffer processor is initialized, now we can deduce it's latency */ if( numInputChannels > 0 ) stream->streamRepresentation.streamInfo.inputLatency = inputLatency + (PaTime)( @@ -3769,8 +3843,8 @@ static PaError PaAlsaStream_WaitForFrames( PaAlsaStream *self, unsigned long *fr /* not else ! */ if (timeouts >= 2048) /* audio device not working, shall return error to notify waiters */ { - *framesAvail = 0; /* no frames available for processing */ - xrun = 1; /* try recovering device */ + *framesAvail = 0; /* no frames available for processing */ + xrun = 1; /* try recovering device */ PA_DEBUG(( "%s: poll timed out\n", __FUNCTION__, timeouts )); goto end;/*PA_ENSURE( paTimedOut );*/ @@ -4148,7 +4222,7 @@ static void *CallbackThreadFunc( void *userData ) int xrun = 0; #ifdef PTHREAD_CANCELED - pthread_testcancel(); + pthread_testcancel(); #endif /* @concern StreamStop if the main thread has requested a stop and the stream has not been effectively @@ -4550,3 +4624,648 @@ PaError PaAlsa_SetRetriesBusy( int retries ) busyRetries_ = retries; return paNoError; } + +/* -------------------------------------------------------------------------- */ +/* -------------------------------------------------------------------------- */ +/* -------------------------------------------------------------------------- */ +/* CONVERTERS (Swapping SRC/DST) */ +/* -------------------------------------------------------------------------- */ + +#define PA_CLIP_( val, min, max )\ + { val = ((val) < (min)) ? (min) : (((val) > (max)) ? (max) : (val)); } + +static const double const_1_div_2147483648_ = 1.0 / 2147483648.0; /* 32 bit multiplier */ + +/* -------------------------------------------------------------------------- */ + +static void Copy_24_To_24_Swap( + void *destinationBuffer, signed int destinationStride, + void *sourceBuffer, signed int sourceStride, + unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator ) +{ + unsigned char *src = (unsigned char*)sourceBuffer; + unsigned char *dest = (unsigned char*)destinationBuffer; + + (void) ditherGenerator; /* unused parameter */ + + while( count-- ) + { + dest[2] = src[0]; + dest[1] = src[1]; + dest[0] = src[2]; + + src += sourceStride * 3; + dest += destinationStride * 3; + } +} + +/* -------------------------------------------------------------------------- */ + +static void UInt8_To_Int24_Swap( + void *destinationBuffer, signed int destinationStride, + void *sourceBuffer, signed int sourceStride, + unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator ) +{ + unsigned char *src = (unsigned char*)sourceBuffer; + unsigned char *dest = (unsigned char*)destinationBuffer; + (void) ditherGenerator; /* unused parameters */ + + while( count-- ) + { + +#if defined(PA_LITTLE_ENDIAN) + dest[2] = 0; + dest[1] = 0; + dest[0] = (unsigned char)(*src - 128); +#elif defined(PA_BIG_ENDIAN) + dest[2] = (unsigned char)(*src - 128); + dest[1] = 0; + dest[0] = 0; +#endif + + src += sourceStride; + dest += destinationStride * 3; + } +} + +/* -------------------------------------------------------------------------- */ + +static void Int8_To_Int24_Swap( + void *destinationBuffer, signed int destinationStride, + void *sourceBuffer, signed int sourceStride, + unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator ) +{ + signed char *src = (signed char*)sourceBuffer; + unsigned char *dest = (unsigned char*)destinationBuffer; + (void)ditherGenerator; /* unused parameter */ + + while( count-- ) + { + +#if defined(PA_LITTLE_ENDIAN) + dest[2] = 0; + dest[1] = 0; + dest[0] = (*src); +#elif defined(PA_BIG_ENDIAN) + dest[2] = (*src); + dest[1] = 0; + dest[0] = 0; +#endif + + src += sourceStride; + dest += destinationStride * 3; + } +} + +/* -------------------------------------------------------------------------- */ + +static void Int16_To_Int24_Swap( + void *destinationBuffer, signed int destinationStride, + void *sourceBuffer, signed int sourceStride, + unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator ) +{ + PaInt16 *src = (PaInt16*) sourceBuffer; + unsigned char *dest = (unsigned char*)destinationBuffer; + PaInt16 temp; + + (void) ditherGenerator; /* unused parameter */ + + while( count-- ) + { + temp = *src; + +#if defined(PA_LITTLE_ENDIAN) + dest[2] = 0; + dest[1] = (unsigned char)(temp); + dest[0] = (unsigned char)(temp >> 8); +#elif defined(PA_BIG_ENDIAN) + dest[2] = (unsigned char)(temp >> 8); + dest[1] = (unsigned char)(temp); + dest[0] = 0; +#endif + + src += sourceStride; + dest += destinationStride * 3; + } +} + +/* -------------------------------------------------------------------------- */ + +static void Int32_To_Int24_Swap( + void *destinationBuffer, signed int destinationStride, + void *sourceBuffer, signed int sourceStride, + unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator ) +{ + PaInt32 *src = (PaInt32*)sourceBuffer; + unsigned char *dest = (unsigned char*)destinationBuffer; + (void) ditherGenerator; /* unused parameter */ + + while( count-- ) + { + /* REVIEW */ +#if defined(PA_LITTLE_ENDIAN) + dest[2] = (unsigned char)(*src >> 8); + dest[1] = (unsigned char)(*src >> 16); + dest[0] = (unsigned char)(*src >> 24); +#elif defined(PA_BIG_ENDIAN) + dest[2] = (unsigned char)(*src >> 24); + dest[1] = (unsigned char)(*src >> 16); + dest[0] = (unsigned char)(*src >> 8); +#endif + src += sourceStride; + dest += destinationStride * 3; + } +} + +/* -------------------------------------------------------------------------- */ + +static void Float32_To_Int24_Swap( + void *destinationBuffer, signed int destinationStride, + void *sourceBuffer, signed int sourceStride, + unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator ) +{ + float *src = (float*)sourceBuffer; + unsigned char *dest = (unsigned char*)destinationBuffer; + PaInt32 temp; + + (void) ditherGenerator; /* unused parameter */ + + while( count-- ) + { + /* convert to 32 bit and drop the low 8 bits */ + double scaled = (double)(*src) * 2147483647.0; + temp = (PaInt32) scaled; + +#if defined(PA_LITTLE_ENDIAN) + dest[2] = (unsigned char)(temp >> 8); + dest[1] = (unsigned char)(temp >> 16); + dest[0] = (unsigned char)(temp >> 24); +#elif defined(PA_BIG_ENDIAN) + dest[2] = (unsigned char)(temp >> 24); + dest[1] = (unsigned char)(temp >> 16); + dest[0] = (unsigned char)(temp >> 8); +#endif + + src += sourceStride; + dest += destinationStride * 3; + } +} + +/* -------------------------------------------------------------------------- */ + +static void Float32_To_Int24_Dither_Swap( + void *destinationBuffer, signed int destinationStride, + void *sourceBuffer, signed int sourceStride, + unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator ) +{ + float *src = (float*)sourceBuffer; + unsigned char *dest = (unsigned char*)destinationBuffer; + PaInt32 temp; + + while( count-- ) + { + /* convert to 32 bit and drop the low 8 bits */ + + double dither = PaUtil_GenerateFloatTriangularDither( ditherGenerator ); + /* use smaller scaler to prevent overflow when we add the dither */ + double dithered = ((double)*src * (2147483646.0)) + dither; + + temp = (PaInt32) dithered; + +#if defined(PA_LITTLE_ENDIAN) + dest[2] = (unsigned char)(temp >> 8); + dest[1] = (unsigned char)(temp >> 16); + dest[0] = (unsigned char)(temp >> 24); +#elif defined(PA_BIG_ENDIAN) + dest[2] = (unsigned char)(temp >> 24); + dest[1] = (unsigned char)(temp >> 16); + dest[0] = (unsigned char)(temp >> 8); +#endif + + src += sourceStride; + dest += destinationStride * 3; + } +} + +/* -------------------------------------------------------------------------- */ + +static void Float32_To_Int24_Clip_Swap( + void *destinationBuffer, signed int destinationStride, + void *sourceBuffer, signed int sourceStride, + unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator ) +{ + float *src = (float*)sourceBuffer; + unsigned char *dest = (unsigned char*)destinationBuffer; + PaInt32 temp; + + (void) ditherGenerator; /* unused parameter */ + + while( count-- ) + { + /* convert to 32 bit and drop the low 8 bits */ + double scaled = *src * 0x7FFFFFFF; + PA_CLIP_( scaled, -2147483648., 2147483647. ); + temp = (PaInt32) scaled; + +#if defined(PA_LITTLE_ENDIAN) + dest[2] = (unsigned char)(temp >> 8); + dest[1] = (unsigned char)(temp >> 16); + dest[0] = (unsigned char)(temp >> 24); +#elif defined(PA_BIG_ENDIAN) + dest[2] = (unsigned char)(temp >> 24); + dest[1] = (unsigned char)(temp >> 16); + dest[0] = (unsigned char)(temp >> 8); +#endif + + src += sourceStride; + dest += destinationStride * 3; + } +} + +/* -------------------------------------------------------------------------- */ + +static void Float32_To_Int24_DitherClip_Swap( + void *destinationBuffer, signed int destinationStride, + void *sourceBuffer, signed int sourceStride, + unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator ) +{ + float *src = (float*)sourceBuffer; + unsigned char *dest = (unsigned char*)destinationBuffer; + PaInt32 temp; + + while( count-- ) + { + /* convert to 32 bit and drop the low 8 bits */ + + double dither = PaUtil_GenerateFloatTriangularDither( ditherGenerator ); + /* use smaller scaler to prevent overflow when we add the dither */ + double dithered = ((double)*src * (2147483646.0)) + dither; + PA_CLIP_( dithered, -2147483648., 2147483647. ); + + temp = (PaInt32) dithered; + +#if defined(PA_LITTLE_ENDIAN) + dest[2] = (unsigned char)(temp >> 8); + dest[1] = (unsigned char)(temp >> 16); + dest[0] = (unsigned char)(temp >> 24); +#elif defined(PA_BIG_ENDIAN) + dest[2] = (unsigned char)(temp >> 24); + dest[1] = (unsigned char)(temp >> 16); + dest[0] = (unsigned char)(temp >> 8); +#endif + + src += sourceStride; + dest += destinationStride * 3; + } +} + +/* -------------------------------------------------------------------------- */ + +static void Int24_To_Float32_Swap( + void *destinationBuffer, signed int destinationStride, + void *sourceBuffer, signed int sourceStride, + unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator ) +{ + unsigned char *src = (unsigned char*)sourceBuffer; + float *dest = (float*)destinationBuffer; + PaInt32 temp; + + (void) ditherGenerator; /* unused parameter */ + + while( count-- ) + { + +#if defined(PA_LITTLE_ENDIAN) + temp = (((PaInt32)src[2]) << 8); + temp = temp | (((PaInt32)src[1]) << 16); + temp = temp | (((PaInt32)src[0]) << 24); +#elif defined(PA_BIG_ENDIAN) + temp = (((PaInt32)src[2]) << 24); + temp = temp | (((PaInt32)src[1]) << 16); + temp = temp | (((PaInt32)src[0]) << 8); +#endif + + *dest = (float) ((double)temp * const_1_div_2147483648_); + + src += sourceStride * 3; + dest += destinationStride; + } +} + +/* -------------------------------------------------------------------------- */ + +static void Int24_To_Int32_Swap( + void *destinationBuffer, signed int destinationStride, + void *sourceBuffer, signed int sourceStride, + unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator ) +{ + unsigned char *src = (unsigned char*)sourceBuffer; + PaInt32 *dest = (PaInt32*) destinationBuffer; + PaInt32 temp; + + (void) ditherGenerator; /* unused parameter */ + + while( count-- ) + { + +#if defined(PA_LITTLE_ENDIAN) + temp = (((PaInt32)src[2]) << 8); + temp = temp | (((PaInt32)src[1]) << 16); + temp = temp | (((PaInt32)src[0]) << 24); +#elif defined(PA_BIG_ENDIAN) + temp = (((PaInt32)src[2]) << 24); + temp = temp | (((PaInt32)src[1]) << 16); + temp = temp | (((PaInt32)src[0]) << 8); +#endif + + *dest = temp; + + src += sourceStride * 3; + dest += destinationStride; + } +} + +/* -------------------------------------------------------------------------- */ + +static void Int24_To_Int16_Swap( + void *destinationBuffer, signed int destinationStride, + void *sourceBuffer, signed int sourceStride, + unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator ) +{ + unsigned char *src = (unsigned char*)sourceBuffer; + PaInt16 *dest = (PaInt16*)destinationBuffer; + + PaInt16 temp; + + (void) ditherGenerator; /* unused parameter */ + + while( count-- ) + { + +#if defined(PA_LITTLE_ENDIAN) + /* src[2] is discarded */ + temp = (((PaInt16)src[1])); + temp = temp | (PaInt16)(((PaInt16)src[0]) << 8); +#elif defined(PA_BIG_ENDIAN) + /* src[0] is discarded */ + temp = (PaInt16)(((PaInt16)src[2]) << 8); + temp = temp | (((PaInt16)src[1])); +#endif + + *dest = temp; + + src += sourceStride * 3; + dest += destinationStride; + } +} + +/* -------------------------------------------------------------------------- */ + +static void Int24_To_Int16_Dither_Swap( + void *destinationBuffer, signed int destinationStride, + void *sourceBuffer, signed int sourceStride, + unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator ) +{ + unsigned char *src = (unsigned char*)sourceBuffer; + PaInt16 *dest = (PaInt16*)destinationBuffer; + + PaInt32 temp, dither; + + while( count-- ) + { + +#if defined(PA_LITTLE_ENDIAN) + temp = (((PaInt32)src[2]) << 8); + temp = temp | (((PaInt32)src[1]) << 16); + temp = temp | (((PaInt32)src[0]) << 24); +#elif defined(PA_BIG_ENDIAN) + temp = (((PaInt32)src[2]) << 24); + temp = temp | (((PaInt32)src[1]) << 16); + temp = temp | (((PaInt32)src[0]) << 8); +#endif + + /* REVIEW */ + dither = PaUtil_Generate16BitTriangularDither( ditherGenerator ); + *dest = (PaInt16) (((temp >> 1) + dither) >> 15); + + src += sourceStride * 3; + dest += destinationStride; + } +} + +/* -------------------------------------------------------------------------- */ + +static void Int24_To_Int8_Swap( + void *destinationBuffer, signed int destinationStride, + void *sourceBuffer, signed int sourceStride, + unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator ) +{ + unsigned char *src = (unsigned char*)sourceBuffer; + signed char *dest = (signed char*)destinationBuffer; + + (void) ditherGenerator; /* unused parameter */ + + while( count-- ) + { + +#if defined(PA_LITTLE_ENDIAN) + /* src[0] is discarded */ + /* src[1] is discarded */ + *dest = src[0]; +#elif defined(PA_BIG_ENDIAN) + /* src[2] is discarded */ + /* src[1] is discarded */ + *dest = src[2]; +#endif + + src += sourceStride * 3; + dest += destinationStride; + } +} + +/* -------------------------------------------------------------------------- */ + +static void Int24_To_Int8_Dither_Swap( + void *destinationBuffer, signed int destinationStride, + void *sourceBuffer, signed int sourceStride, + unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator ) +{ + unsigned char *src = (unsigned char*)sourceBuffer; + signed char *dest = (signed char*)destinationBuffer; + + PaInt32 temp, dither; + + while( count-- ) + { + +#if defined(PA_LITTLE_ENDIAN) + temp = (((PaInt32)src[2]) << 8); + temp = temp | (((PaInt32)src[1]) << 16); + temp = temp | (((PaInt32)src[0]) << 24); +#elif defined(PA_BIG_ENDIAN) + temp = (((PaInt32)src[2]) << 24); + temp = temp | (((PaInt32)src[1]) << 16); + temp = temp | (((PaInt32)src[0]) << 8); +#endif + + /* REVIEW */ + dither = PaUtil_Generate16BitTriangularDither( ditherGenerator ); + *dest = (signed char) (((temp >> 1) + dither) >> 23); + + src += sourceStride * 3; + dest += destinationStride; + } +} + +/* -------------------------------------------------------------------------- */ + +static void Int24_To_UInt8_Swap( + void *destinationBuffer, signed int destinationStride, + void *sourceBuffer, signed int sourceStride, + unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator ) +{ + unsigned char *src = (unsigned char*)sourceBuffer; + unsigned char *dest = (unsigned char*)destinationBuffer; + + (void) ditherGenerator; /* unused parameter */ + + while( count-- ) + { + +#if defined(PA_LITTLE_ENDIAN) + /* src[0] is discarded */ + /* src[1] is discarded */ + *dest = (unsigned char)(src[0] + 128); +#elif defined(PA_BIG_ENDIAN) + *dest = (unsigned char)(src[2] + 128); + /* src[1] is discarded */ + /* src[2] is discarded */ +#endif + + src += sourceStride * 3; + dest += destinationStride; + } +} + +/* -------------------------------------------------------------------------- */ + +static void X_To_X_Stub( + void *destinationBuffer, signed int destinationStride, + void *sourceBuffer, signed int sourceStride, + unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator ) +{ + (void) destinationBuffer; /* unused parameters */ + (void) destinationStride; /* unused parameters */ + (void) sourceBuffer; /* unused parameters */ + (void) sourceStride; /* unused parameters */ + (void) count; /* unused parameters */ + (void) ditherGenerator; /* unused parameters */ + /* IMPLEMENT ME */ + + assert(0 && "input/output format needs additional swapping converter"); +} + +/* -------------------------------------------------------------------------- */ + +static PaUtilConverterTable paAlsaSwapConverters = { + X_To_X_Stub, /* PaUtilConverter *Float32_To_Int32; */ + X_To_X_Stub, /* PaUtilConverter *Float32_To_Int32_Dither; */ + X_To_X_Stub, /* PaUtilConverter *Float32_To_Int32_Clip; */ + X_To_X_Stub, /* PaUtilConverter *Float32_To_Int32_DitherClip; */ + + Float32_To_Int24_Swap, /* PaUtilConverter *Float32_To_Int24; */ + Float32_To_Int24_Dither_Swap, /* PaUtilConverter *Float32_To_Int24_Dither; */ + Float32_To_Int24_Clip_Swap, /* PaUtilConverter *Float32_To_Int24_Clip; */ + Float32_To_Int24_DitherClip_Swap, /* PaUtilConverter *Float32_To_Int24_DitherClip; */ + + X_To_X_Stub, /* PaUtilConverter *Float32_To_Int16; */ + X_To_X_Stub, /* PaUtilConverter *Float32_To_Int16_Dither; */ + X_To_X_Stub, /* PaUtilConverter *Float32_To_Int16_Clip; */ + X_To_X_Stub, /* PaUtilConverter *Float32_To_Int16_DitherClip; */ + + X_To_X_Stub, /* PaUtilConverter *Float32_To_Int8; */ + X_To_X_Stub, /* PaUtilConverter *Float32_To_Int8_Dither; */ + X_To_X_Stub, /* PaUtilConverter *Float32_To_Int8_Clip; */ + X_To_X_Stub, /* PaUtilConverter *Float32_To_Int8_DitherClip; */ + + X_To_X_Stub, /* PaUtilConverter *Float32_To_UInt8; */ + X_To_X_Stub, /* PaUtilConverter *Float32_To_UInt8_Dither; */ + X_To_X_Stub, /* PaUtilConverter *Float32_To_UInt8_Clip; */ + X_To_X_Stub, /* PaUtilConverter *Float32_To_UInt8_DitherClip; */ + + X_To_X_Stub, /* PaUtilConverter *Int32_To_Float32; */ + Int32_To_Int24_Swap, /* PaUtilConverter *Int32_To_Int24; */ + X_To_X_Stub,/*TO-DO*//* PaUtilConverter *Int32_To_Int24_Dither; */ + X_To_X_Stub, /* PaUtilConverter *Int32_To_Int16; */ + X_To_X_Stub, /* PaUtilConverter *Int32_To_Int16_Dither; */ + X_To_X_Stub, /* PaUtilConverter *Int32_To_Int8; */ + X_To_X_Stub, /* PaUtilConverter *Int32_To_Int8_Dither; */ + X_To_X_Stub, /* PaUtilConverter *Int32_To_UInt8; */ + X_To_X_Stub, /* PaUtilConverter *Int32_To_UInt8_Dither; */ + + Int24_To_Float32_Swap, /* PaUtilConverter *Int24_To_Float32; */ + Int24_To_Int32_Swap, /* PaUtilConverter *Int24_To_Int32; */ + Int24_To_Int16_Swap, /* PaUtilConverter *Int24_To_Int16; */ + Int24_To_Int16_Dither_Swap, /* PaUtilConverter *Int24_To_Int16_Dither; */ + Int24_To_Int8_Swap, /* PaUtilConverter *Int24_To_Int8; */ + Int24_To_Int8_Dither_Swap, /* PaUtilConverter *Int24_To_Int8_Dither; */ + Int24_To_UInt8_Swap, /* PaUtilConverter *Int24_To_UInt8; */ + X_To_X_Stub, /* PaUtilConverter *Int24_To_UInt8_Dither; */ + + X_To_X_Stub, /* PaUtilConverter *Int16_To_Float32; */ + X_To_X_Stub, /* PaUtilConverter *Int16_To_Int32; */ + Int16_To_Int24_Swap, /* PaUtilConverter *Int16_To_Int24; */ + X_To_X_Stub, /* PaUtilConverter *Int16_To_Int8; */ + X_To_X_Stub, /* PaUtilConverter *Int16_To_Int8_Dither; */ + X_To_X_Stub, /* PaUtilConverter *Int16_To_UInt8; */ + X_To_X_Stub, /* PaUtilConverter *Int16_To_UInt8_Dither; */ + + X_To_X_Stub, /* PaUtilConverter *Int8_To_Float32; */ + X_To_X_Stub, /* PaUtilConverter *Int8_To_Int32; */ + Int8_To_Int24_Swap, /* PaUtilConverter *Int8_To_Int24 */ + X_To_X_Stub, /* PaUtilConverter *Int8_To_Int16; */ + X_To_X_Stub, /* PaUtilConverter *Int8_To_UInt8; */ + + X_To_X_Stub, /* PaUtilConverter *UInt8_To_Float32; */ + X_To_X_Stub, /* PaUtilConverter *UInt8_To_Int32; */ + UInt8_To_Int24_Swap, /* PaUtilConverter *UInt8_To_Int24; */ + X_To_X_Stub, /* PaUtilConverter *UInt8_To_Int16; */ + X_To_X_Stub, /* PaUtilConverter *UInt8_To_Int8; */ + + X_To_X_Stub, /* PaUtilConverter *Copy_8_To_8; */ + X_To_X_Stub, /* PaUtilConverter *Copy_16_To_16; */ + Copy_24_To_24_Swap, /* PaUtilConverter *Copy_24_To_24; */ + X_To_X_Stub /* PaUtilConverter *Copy_32_To_32; */ +}; +void CheckAndReplaceConverterForSwapEndian(PaAlsaStream *stream, PaSampleFormat hostInputSampleFormat, + PaSampleFormat hostOutputSampleFormat) +{ + PaUtilConverter **cv_org = (PaUtilConverter **)&paConverters, + **cv_swp = (PaUtilConverter **)&paAlsaSwapConverters; + + int i, i_max = sizeof(PaUtilConverterTable)/sizeof(PaUtilConverter *); + + if (hostInputSampleFormat & paSwapEndian) + { + for (i = 0; i < i_max; ++i) + { + if (cv_org[i] == stream->bufferProcessor.inputConverter) + { + stream->bufferProcessor.inputConverter = cv_swp[i]; + break; + } + } + } + + if (hostOutputSampleFormat & paSwapEndian) + { + for (i = 0; i < i_max; ++i) + { + if (cv_org[i] == stream->bufferProcessor.outputConverter) + { + stream->bufferProcessor.outputConverter = cv_swp[i]; + break; + } + } + } +}