merge in newer portaudio

git-svn-id: http://svn.freeswitch.org/svn/freeswitch/trunk@8835 d0543943-73ff-0310-b7d9-9358b9ac24b2
This commit is contained in:
Anthony Minessale committed 2008-06-24 22:15:50 +00:00
1 parent 50e9411c82
commit 625c524db7
62 files changed
+23721 -8516

No files matched your search

+103 -51
View File
@@ -1,11 +1,11 @@
/*
* $Id: pa_linux_alsa.c 1161 2006-12-17 16:16:13Z aknudsen $
* $Id: pa_linux_alsa.c 1278 2007-09-12 17:39:48Z aknudsen $
* PortAudio Portable Real-Time Audio Library
* Latest Version at: http://www.portaudio.com
* ALSA implementation by Joshua Haberman and Arve Knudsen
*
* Copyright (c) 2002 Joshua Haberman <joshua@haberman.com>
* Copyright (c) 2005-2006 Arve Knudsen <aknuds-1@broadpark.no>
* Copyright (c) 2005-2007 Arve Knudsen <aknuds-1@broadpark.no>
*
* Based on the Open Source API proposed by Ross Bencina
* Copyright (c) 1999-2002 Ross Bencina, Phil Burk
@@ -70,6 +70,8 @@
#include "pa_stream.h"
#include "pa_cpuload.h"
#include "pa_process.h"
#include "pa_endianness.h"
#include "pa_debugprint.h"
#include "pa_linux_alsa.h"
@@ -96,6 +98,13 @@
assert( success == aErr_ );
static int aErr_; /* Used with ENSURE_ */
static int numPeriods_ = 4;
int PaAlsa_SetNumPeriods( int numPeriods )
{
numPeriods_ = numPeriods;
return paNoError;
}
typedef enum
{
@@ -520,14 +529,19 @@ static int IgnorePlugin( const char *pluginId )
return 0;
}
/* Wrapper around snd_pcm_open which repeatedly retries opening a device for up to a second
* if it is busy. This is because dmix may temporarily hold on to a device after it (dmix)
* has been opened and closed. */
static int OpenPcm( snd_pcm_t **pcmp, const char *name, snd_pcm_stream_t stream, int mode )
/** Open PCM device.
*
* Wrapper around snd_pcm_open which may repeatedly retry opening a device if it is busy, for
* a certain time. This is because dmix may temporarily hold on to a device after it (dmix)
* has been opened and closed.
* @param mode: Open mode (e.g., SND_PCM_BLOCKING).
* @param waitOnBusy: Retry opening busy device for up to one second?
**/
static int OpenPcm( snd_pcm_t **pcmp, const char *name, snd_pcm_stream_t stream, int mode, int waitOnBusy )
{
int tries = 0;
int tries = 0, maxTries = waitOnBusy ? 100 : 0;
int ret = snd_pcm_open( pcmp, name, stream, mode );
for( tries = 0; tries < 100 && -EBUSY == ret; ++tries )
for( tries = 0; tries < maxTries && -EBUSY == ret; ++tries )
{
Pa_Sleep( 10 );
ret = snd_pcm_open( pcmp, name, stream, mode );
@@ -539,8 +553,8 @@ static int OpenPcm( snd_pcm_t **pcmp, const char *name, snd_pcm_stream_t stream,
}
if( -EBUSY == ret )
{
PA_DEBUG(( "%s: Failed to open busy device\n",
__FUNCTION__ ));
PA_DEBUG(( "%s: Failed to open busy device '%s'\n",
__FUNCTION__, name ));
}
return ret;
@@ -563,7 +577,7 @@ static PaError FillInDevInfo( PaAlsaHostApiRepresentation *alsaApi, HwDevInfo* d
/* Query capture */
if( deviceName->hasCapture &&
OpenPcm( &pcm, deviceName->alsaName, SND_PCM_STREAM_CAPTURE, blocking )
OpenPcm( &pcm, deviceName->alsaName, SND_PCM_STREAM_CAPTURE, blocking, 0 )
>= 0 )
{
if( GropeDevice( pcm, deviceName->isPlug, StreamDirection_In, blocking, devInfo,
@@ -577,7 +591,7 @@ static PaError FillInDevInfo( PaAlsaHostApiRepresentation *alsaApi, HwDevInfo* d
/* Query playback */
if( deviceName->hasPlayback &&
OpenPcm( &pcm, deviceName->alsaName, SND_PCM_STREAM_PLAYBACK, blocking )
OpenPcm( &pcm, deviceName->alsaName, SND_PCM_STREAM_PLAYBACK, blocking, 0 )
>= 0 )
{
if( GropeDevice( pcm, deviceName->isPlug, StreamDirection_Out, blocking, devInfo,
@@ -607,8 +621,12 @@ 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 && 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 )
{
@@ -639,6 +657,10 @@ static PaError BuildDeviceList( PaAlsaHostApiRepresentation *alsaApi )
int res;
int blocking = SND_PCM_NONBLOCK;
char alsaCardName[50];
#ifdef PA_ENABLE_DEBUG_OUTPUT
PaTime startTime = PaUtil_GetTime();
#endif
if( getenv( "PA_ALSA_INITIALIZE_BLOCK" ) && atoi( getenv( "PA_ALSA_INITIALIZE_BLOCK" ) ) )
blocking = 0;
@@ -820,6 +842,11 @@ static PaError BuildDeviceList( PaAlsaHostApiRepresentation *alsaApi )
/* Loop over list of cards, filling in info. If a device is deemed unavailable (can't get name),
* it's ignored.
*
* Note that we do this in two stages. This is a workaround owing to the fact that the 'dmix'
* plugin may cause the underlying hardware device to be busy for a short while even after it
* (dmix) is closed. The 'default' plugin may also point to the dmix plugin, so the same goes
* for this.
*/
for( i = 0, devIdx = 0; i < numDeviceNames; ++i )
@@ -834,6 +861,7 @@ static PaError BuildDeviceList( PaAlsaHostApiRepresentation *alsaApi )
PA_ENSURE( FillInDevInfo( alsaApi, hwInfo, blocking, devInfo, &devIdx ) );
}
assert( devIdx < numDeviceNames );
/* Now inspect 'dmix' and 'default' plugins */
for( i = 0; i < numDeviceNames; ++i )
{
PaAlsaDeviceInfo* devInfo = &deviceInfoArray[i];
@@ -850,6 +878,10 @@ static PaError BuildDeviceList( PaAlsaHostApiRepresentation *alsaApi )
baseApi->info.deviceCount = devIdx; /* Number of successfully queried devices */
#ifdef PA_ENABLE_DEBUG_OUTPUT
PA_DEBUG(( "%s: Building device list took %f seconds\n", __FUNCTION__, PaUtil_GetTime() - startTime ));
#endif
end:
return result;
@@ -910,8 +942,13 @@ static PaSampleFormat GetAvailableFormats( snd_pcm_t *pcm )
if( snd_pcm_hw_params_test_format( pcm, hwParams, SND_PCM_FORMAT_S32 ) >= 0)
available |= paInt32;
if( snd_pcm_hw_params_test_format( pcm, hwParams, SND_PCM_FORMAT_S24 ) >= 0)
#ifdef PA_LITTLE_ENDIAN
if( snd_pcm_hw_params_test_format( pcm, hwParams, SND_PCM_FORMAT_S24_3LE ) >= 0)
available |= paInt24;
#elif defined PA_BIG_ENDIAN
if( snd_pcm_hw_params_test_format( pcm, hwParams, SND_PCM_FORMAT_S24_3BE ) >= 0)
available |= paInt24;
#endif
if( snd_pcm_hw_params_test_format( pcm, hwParams, SND_PCM_FORMAT_S16 ) >= 0)
available |= paInt16;
@@ -936,7 +973,11 @@ static snd_pcm_format_t Pa2AlsaFormat( PaSampleFormat paFormat )
return SND_PCM_FORMAT_S16;
case paInt24:
return SND_PCM_FORMAT_S24;
#ifdef PA_LITTLE_ENDIAN
return SND_PCM_FORMAT_S24_3LE;
#elif defined PA_BIG_ENDIAN
return SND_PCM_FORMAT_S24_3BE;
#endif
case paInt32:
return SND_PCM_FORMAT_S32;
@@ -986,14 +1027,10 @@ static PaError AlsaOpen( const PaUtilHostApiRepresentation *hostApi, const PaStr
PA_DEBUG(( "%s: Opening device %s\n", __FUNCTION__, deviceName ));
if( (ret = OpenPcm( pcm, deviceName, streamDir == StreamDirection_In ? SND_PCM_STREAM_CAPTURE : SND_PCM_STREAM_PLAYBACK,
SND_PCM_NONBLOCK )) < 0 )
SND_PCM_NONBLOCK, 1 )) < 0 )
{
/* Not to be closed */
*pcm = NULL;
if( -EBUSY == ret )
{
PA_DEBUG(( "%s: Device is busy\n", __FUNCTION__ ));
}
ENSURE_( ret, -EBUSY == ret ? paDeviceUnavailable : paBadIODeviceCombination );
}
ENSURE_( snd_pcm_nonblock( *pcm, 0 ), paUnanticipatedHostError );
@@ -1287,14 +1324,25 @@ static PaError PaAlsaStreamComponent_FinishConfigure( PaAlsaStreamComponent *sel
snd_pcm_sw_params_alloca( &swParams );
bufSz = (params->suggestedLatency * sampleRate) + self->framesPerBuffer; /* One period does not count as latency */
bufSz = params->suggestedLatency * sampleRate;
ENSURE_( snd_pcm_hw_params_set_buffer_size_near( self->pcm, hwParams, &bufSz ), paUnanticipatedHostError );
/* Set the parameters! */
ENSURE_( snd_pcm_hw_params( self->pcm, hwParams ), paUnanticipatedHostError );
{
int r = snd_pcm_hw_params( self->pcm, hwParams );
#ifdef PA_ENABLE_DEBUG_OUTPUT
if( r < 0 )
{
snd_output_t *output = NULL;
snd_output_stdio_attach( &output, stderr, 0 );
snd_pcm_hw_params_dump( hwParams, output );
}
#endif
ENSURE_(r, paUnanticipatedHostError );
}
ENSURE_( snd_pcm_hw_params_get_buffer_size( hwParams, &self->bufferSize ), paUnanticipatedHostError );
/* Latency in seconds, one period is not counted as latency */
*latency = (self->bufferSize - self->framesPerBuffer) / sampleRate;
/* Latency in seconds */
*latency = self->bufferSize / sampleRate;
/* Now software parameters... */
ENSURE_( snd_pcm_sw_params_current( self->pcm, swParams ), paUnanticipatedHostError );
@@ -1473,20 +1521,19 @@ static PaError PaAlsaStreamComponent_DetermineFramesPerBuffer( PaAlsaStreamCompo
}
}
/* Using 5 as a base number of periods, we try to approximate the suggested latency (+1 period),
/* Using the base number of periods, we try to approximate the suggested latency (+1 period),
finding a combination of period/buffer size which best fits these constraints */
{
unsigned numPeriods = 4, maxPeriods = 0;
unsigned numPeriods = numPeriods_, maxPeriods = 0;
/* It may be that the device only supports 2 periods for instance */
dir = 0;
ENSURE_( snd_pcm_hw_params_get_periods_max( hwParams, &maxPeriods, &dir ), paUnanticipatedHostError );
assert( maxPeriods > 1 );
/* One period is not counted as latency */
maxPeriods -= 1;
numPeriods = PA_MIN( maxPeriods, numPeriods );
if( framesPerUserBuffer != paFramesPerBufferUnspecified )
{
/* Try to get a power-of-two of the user buffer size. */
framesPerHostBuffer = framesPerUserBuffer;
if( framesPerHostBuffer < bufferSize )
{
@@ -1494,6 +1541,11 @@ static PaError PaAlsaStreamComponent_DetermineFramesPerBuffer( PaAlsaStreamCompo
{
framesPerHostBuffer *= 2;
}
/* One extra period is preferrable to one less (should be more robust) */
if( bufferSize / framesPerHostBuffer < numPeriods )
{
framesPerHostBuffer /= 2;
}
}
else
{
@@ -1614,15 +1666,15 @@ static PaError PaAlsaStream_DetermineFramesPerBuffer( PaAlsaStream* self, double
unsigned long framesPerHostBuffer = 0;
int dir = 0;
int accurate = 1;
unsigned numPeriods = numPeriods_;
if( self->capture.pcm && self->playback.pcm )
{
if( framesPerUserBuffer == paFramesPerBufferUnspecified )
{
snd_pcm_uframes_t desiredLatency, e, minPeriodSize, maxPeriodSize, optimalPeriodSize, periodSize,
minCapture, minPlayback, maxCapture, maxPlayback;
/* Come up with a common desired latency */
snd_pcm_uframes_t desiredBufSz, e, minPeriodSize, maxPeriodSize, optimalPeriodSize, periodSize,
minCapture, minPlayback, maxCapture, maxPlayback;
dir = 0;
ENSURE_( snd_pcm_hw_params_get_period_size_min( hwParamsCapture, &minCapture, &dir ), paUnanticipatedHostError );
@@ -1636,9 +1688,9 @@ static PaError PaAlsaStream_DetermineFramesPerBuffer( PaAlsaStream* self, double
maxPeriodSize = PA_MIN( maxPlayback, maxCapture );
PA_UNLESS( minPeriodSize <= maxPeriodSize, paBadIODeviceCombination );
desiredLatency = (snd_pcm_uframes_t)(PA_MIN( outputParameters->suggestedLatency, inputParameters->suggestedLatency )
desiredBufSz = (snd_pcm_uframes_t)(PA_MIN( outputParameters->suggestedLatency, inputParameters->suggestedLatency )
* sampleRate);
/* Clamp desiredLatency */
/* Clamp desiredBufSz */
{
snd_pcm_uframes_t maxBufferSize;
snd_pcm_uframes_t maxBufferSizeCapture, maxBufferSizePlayback;
@@ -1646,7 +1698,7 @@ static PaError PaAlsaStream_DetermineFramesPerBuffer( PaAlsaStream* self, double
ENSURE_( snd_pcm_hw_params_get_buffer_size_max( hwParamsPlayback, &maxBufferSizePlayback ), paUnanticipatedHostError );
maxBufferSize = PA_MIN( maxBufferSizeCapture, maxBufferSizePlayback );
desiredLatency = PA_MIN( desiredLatency, maxBufferSize );
desiredBufSz = PA_MIN( desiredBufSz, maxBufferSize );
}
/* Find the closest power of 2 */
@@ -1659,13 +1711,15 @@ static PaError PaAlsaStream_DetermineFramesPerBuffer( PaAlsaStream* self, double
{
if( snd_pcm_hw_params_test_period_size( self->playback.pcm, hwParamsPlayback, periodSize, 0 ) >= 0 &&
snd_pcm_hw_params_test_period_size( self->capture.pcm, hwParamsCapture, periodSize, 0 ) >= 0 )
break; /* Ok! */
{
/* OK! */
break;
}
periodSize *= 2;
}
/* 4 periods considered optimal */
optimalPeriodSize = PA_MAX( desiredLatency / 4, minPeriodSize );
optimalPeriodSize = PA_MAX( desiredBufSz / numPeriods, minPeriodSize );
optimalPeriodSize = PA_MIN( optimalPeriodSize, maxPeriodSize );
/* Find the closest power of 2 */
@@ -1676,12 +1730,15 @@ static PaError PaAlsaStream_DetermineFramesPerBuffer( PaAlsaStream* self, double
while( optimalPeriodSize >= periodSize )
{
if( snd_pcm_hw_params_test_period_size( self->capture.pcm, hwParamsCapture, optimalPeriodSize, 0 ) < 0 )
continue;
if( snd_pcm_hw_params_test_period_size( self->playback.pcm, hwParamsPlayback, optimalPeriodSize, 0 ) >= 0 )
if( snd_pcm_hw_params_test_period_size( self->capture.pcm, hwParamsCapture, optimalPeriodSize, 0 )
>= 0 && snd_pcm_hw_params_test_period_size( self->playback.pcm, hwParamsPlayback,
optimalPeriodSize, 0 ) >= 0 )
{
break;
}
optimalPeriodSize /= 2;
}
if( optimalPeriodSize > periodSize )
periodSize = optimalPeriodSize;
@@ -1698,7 +1755,7 @@ static PaError PaAlsaStream_DetermineFramesPerBuffer( PaAlsaStream* self, double
else
{
/* Unable to find a common period size, oh well */
optimalPeriodSize = PA_MAX( desiredLatency / 4, minPeriodSize );
optimalPeriodSize = PA_MAX( desiredBufSz / numPeriods, minPeriodSize );
optimalPeriodSize = PA_MIN( optimalPeriodSize, maxPeriodSize );
self->capture.framesPerBuffer = optimalPeriodSize;
@@ -1726,7 +1783,7 @@ static PaError PaAlsaStream_DetermineFramesPerBuffer( PaAlsaStream* self, double
dir = 0;
ENSURE_( snd_pcm_hw_params_get_periods_max( hwParamsPlayback, &maxPeriods, &dir ), paUnanticipatedHostError );
if( maxPeriods < 4 )
if( maxPeriods < numPeriods )
{
/* The playback component is trickier to get right, try that first */
first = &self->playback;
@@ -2102,7 +2159,9 @@ error:
static PaError AlsaStop( PaAlsaStream *stream, int abort )
{
PaError result = paNoError;
/* XXX: Seems that draining the dmix device may trigger a race condition in ALSA */
/* XXX: snd_pcm_drain tends to lock up, avoid it until we find out more */
abort = 1;
/*
if( stream->capture.pcm && !strcmp( Pa_GetDeviceInfo( stream->capture.device )->name,
"dmix" ) )
{
@@ -2113,6 +2172,7 @@ static PaError AlsaStop( PaAlsaStream *stream, int abort )
{
abort = 1;
}
*/
if( abort )
{
@@ -3448,7 +3508,6 @@ error:
/* Extensions */
/** Initialize host API specific structure, call this before setting relevant attributes. */
void PaAlsa_InitializeStreamInfo( PaAlsaStreamInfo *info )
{
info->size = sizeof (PaAlsaStreamInfo);
@@ -3457,11 +3516,6 @@ void PaAlsa_InitializeStreamInfo( PaAlsaStreamInfo *info )
info->deviceString = NULL;
}
/** Instruct whether to enable real-time priority when starting the audio thread.
*
* If this is turned on by the stream is started, the audio callback thread will be created
* with the FIFO scheduling policy, which is suitable for realtime operation.
**/
void PaAlsa_EnableRealtimeScheduling( PaStream *s, int enable )
{
PaAlsaStream *stream = (PaAlsaStream *) s;
@@ -3476,7 +3530,6 @@ void PaAlsa_EnableWatchdog( PaStream *s, int enable )
}
#endif
/** Get the ALSA-lib card index of this stream's input device. */
PaError PaAlsa_GetStreamInputCard(PaStream* s, int* card) {
PaAlsaStream *stream = (PaAlsaStream *) s;
snd_pcm_info_t* pcmInfo;
@@ -3493,7 +3546,6 @@ error:
return result;
}
/** Get the ALSA-lib card index of this stream's output device. */
PaError PaAlsa_GetStreamOutputCard(PaStream* s, int* card) {
PaAlsaStream *stream = (PaAlsaStream *) s;
snd_pcm_info_t* pcmInfo;
@@ -1,10 +1,10 @@
/*
* PortAudio Portable Real-Time Audio Library
* Latest Version at: http://www.portaudio.com
*
*
* PortAudio v18 version of AudioScience HPI driver by Fred Gleason <fredg@salemradiolabs.com>
* PortAudio v19 version of AudioScience HPI driver by Ludwig Schwardt <schwardt@sun.ac.za>
*
*
* Copyright (c) 2003 Fred Gleason
* Copyright (c) 2005,2006 Ludwig Schwardt
*
@@ -29,13 +29,13 @@
*/
/*
* The text above constitutes the entire PortAudio license; however,
* The text above constitutes the entire PortAudio license; however,
* the PortAudio community also makes the following non-binding requests:
*
* 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. It is also
* requested that these non-binding requests be included along with the
* they can be incorporated into the canonical version. It is also
* requested that these non-binding requests be included along with the
* license above.
*/
@@ -47,45 +47,45 @@
/** @file
@ingroup hostapi_src
@brief Host API implementation supporting AudioScience cards
@brief Host API implementation supporting AudioScience cards
via the Linux HPI interface.
<h3>Overview</h3>
This is a PortAudio implementation for the AudioScience HPI Audio API
on the Linux platform. AudioScience makes a range of audio adapters customised
for the broadcasting industry, with support for both Windows and Linux.
More information on their products can be found on their website:
http://www.audioscience.com
Documentation for the HPI API can be found at:
http://www.audioscience.com/internet/download/sdk/spchpi.pdf
The Linux HPI driver itself (a kernel module + library) can be downloaded from:
http://www.audioscience.com/internet/download/linux_drivers.htm
<h3>Implementation strategy</h3>
*Note* Ideally, AudioScience cards should be handled by the PortAudio ALSA
*Note* Ideally, AudioScience cards should be handled by the PortAudio ALSA
implementation on Linux, as ALSA is the preferred Linux soundcard API. The existence
of this host API implementation might therefore seem a bit flawed. Unfortunately, at
the time of the creation of this implementation (June 2006), the PA ALSA implementation
could not make use of the existing AudioScience ALSA driver. PA ALSA uses the
"memory-mapped" (mmap) ALSA access mode to interact with the ALSA library, while the
of this host API implementation might therefore seem a bit flawed. Unfortunately, at
the time of the creation of this implementation (June 2006), the PA ALSA implementation
could not make use of the existing AudioScience ALSA driver. PA ALSA uses the
"memory-mapped" (mmap) ALSA access mode to interact with the ALSA library, while the
AudioScience ALSA driver only supports the "read-write" access mode. The appropriate
solution to this problem is to add "read-write" support to PortAudio ALSA, thereby
extending the range of soundcards it supports (AudioScience cards are not the only
ones with this problem). Given the author's limited knowledge of ALSA and the
ones with this problem). Given the author's limited knowledge of ALSA and the
simplicity of the HPI API, the second-best solution was born...
The following mapping between HPI and PA was followed:
HPI subsystem => PortAudio host API
HPI adapter => nothing specific
HPI stream => PortAudio device
Each HPI stream is either input or output (not both), and can support
different channel counts, sampling rates and sample formats. It is therefore
a more natural fit to a PA device. A PA stream can therefore combine two
@@ -93,19 +93,19 @@
HPI streams can even be on different physical adapters. The two streams ought to be
sample-synchronised when they reside on the same adapter, as most AudioScience adapters
derive their ADC and DAC clocks from one master clock. When combining two adapters
into one full-duplex stream, however, the use of a word clock connection between the
into one full-duplex stream, however, the use of a word clock connection between the
adapters is strongly recommended.
The HPI interface is inherently blocking, making use of read and write calls to
transfer data between user buffers and driver buffers. The callback interface therefore
requires a helper thread ("callback engine") which periodically transfers data (one thread
per PA stream, in fact). The current implementation explicitly sleeps via Pa_Sleep() until
enough samples can be transferred (select() or poll() would be better, but currently seems
per PA stream, in fact). The current implementation explicitly sleeps via Pa_Sleep() until
enough samples can be transferred (select() or poll() would be better, but currently seems
impossible...). The thread implementation makes use of the Unix thread helper functions
and some pthread calls here and there. If a unified PA thread exists, this host API
implementation might also compile on Windows, as this is the only real Linux-specific
and some pthread calls here and there. If a unified PA thread exists, this host API
implementation might also compile on Windows, as this is the only real Linux-specific
part of the code.
There is no inherent fixed buffer size in the HPI interface, as in some other host APIs.
The PortAudio implementation contains a buffer that is allocated during OpenStream and
used to transfer data between the callback and the HPI driver buffer. The size of this
@@ -113,28 +113,28 @@
requested callback buffer size as far as possible. It can become quite huge, as the
AudioScience cards are typically geared towards higher-latency applications and contain
large hardware buffers.
The HPI interface natively supports most common sample formats and sample rates (some
conversion is done on the adapter itself).
Stream time is measured based on the number of processed frames, which is adjusted by the
number of frames currently buffered by the HPI driver.
There is basic support for detecting overflow and underflow. The HPI interface does not
explicitly indicate this, so thresholds on buffer levels are used in combination with
stream state. Recovery from overflow and underflow is left to the PA client.
Blocking streams are also implemented. It makes use of the same polling routines that
the callback interface uses, in order to prevent the allocation of variable-sized
buffers during reading and writing. The framesPerBuffer parameter is therefore still
buffers during reading and writing. The framesPerBuffer parameter is therefore still
relevant, and this can be increased in the blocking case to improve efficiency.
The implementation contains extensive reporting macros (slightly modified PA_ENSURE and
PA_UNLESS versions) and a useful stream dump routine to provide debugging feedback.
Output buffer priming via the user callback (i.e. paPrimeOutputBuffersUsingStreamCallback
and friends) is not implemented yet. All output is primed with silence.
Please send bug reports etc. to Ludwig Schwardt <schwardt@sun.ac.za>
*/
@@ -157,6 +157,7 @@
#include "pa_cpuload.h" /* CPU load measurer */
#include "pa_process.h" /* Buffer processor */
#include "pa_converters.h" /* PaUtilZeroer */
#include "pa_debugprint.h"
/* -------------------------------------------------------------------------- */
@@ -305,9 +306,9 @@ PaAsiHpiDeviceInfo;
during the Active state of PortAudio (due to underruns) and also during CallBackFinished in
the case of an output stream. Similarly, HPI_STATE_STOPPED mostly coincides with the Stopped
PortAudio state, by may also occur in the CallbackFinished state when recording is finished.
Here is a rough match-up:
PortAudio state => HPI state
--------------- ---------
Active => HPI_STATE_RECORDING, HPI_STATE_PLAYING, (HPI_STATE_DRAINED)
@@ -328,7 +329,7 @@ typedef struct PaAsiHpiStreamComponent
/** Device information (HPI handles, etc) */
PaAsiHpiDeviceInfo *hpiDevice;
/** Stream handle, as passed to HPI interface.
HACK: we assume types HPI_HISTREAM and HPI_HOSTREAM are the same...
HACK: we assume types HPI_HISTREAM and HPI_HOSTREAM are the same...
(both are HW32 up to version 3.00 of ASIHPI, and hopefully they stay that way) */
HPI_HISTREAM hpiStream;
/** Stream format, as passed to HPI interface */
@@ -377,7 +378,7 @@ typedef struct PaAsiHpiStream
int neverDropInput;
/** Contains copy of user buffers, used by blocking interface to transfer non-interleaved data.
It went here instead of to each stream component, as the stream component buffer setup in
PaAsiHpi_SetupBuffers doesn't know the stream details such as callbackMode.
PaAsiHpi_SetupBuffers doesn't know the stream details such as callbackMode.
(Maybe a problem later if ReadStream and WriteStream happens concurrently on same stream.) */
void **blockingUserBufferCopy;
@@ -408,7 +409,7 @@ typedef struct PaAsiHpiStreamInfo
/** Number of frames played/recorded since last stream reset */
HW32 frameCounter;
/** Amount of data (in bytes) in hardware (on-card) buffer.
This differs from dataSize if bus mastering (BBM) is used, which introduces another
This differs from dataSize if bus mastering (BBM) is used, which introduces another
driver-level buffer to which dataSize/bufferSize then refers. */
HW32 auxDataSize;
/** Total number of data frames currently buffered by HPI driver (host + hw buffers) */
@@ -517,9 +518,9 @@ static PaError PaAsiHpi_EndProcessing( PaAsiHpiStream *stream, unsigned long num
This compiles a list of all HPI adapters, and registers a PA device for each input and
output stream it finds. Most errors are ignored, as missing or erroneous devices are
simply skipped.
@param hpiHostApi Pointer to HPI host API struct
@return PortAudio error code (only paInsufficientMemory in practice)
*/
static PaError PaAsiHpi_BuildDeviceList( PaAsiHpiHostApiRepresentation *hpiHostApi )
@@ -726,11 +727,11 @@ error:
This is the only function exported beyond this file. It is called by PortAudio to initialize
the host API. It stores API info, finds and registers all devices, and sets up callback and
blocking interfaces.
@param hostApi Pointer to host API struct
@param hostApiIndex Index of current (HPI) host API
@return PortAudio error code
*/
PaError PaAsiHpi_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex hostApiIndex )
@@ -807,7 +808,7 @@ error:
/** Terminate host API implementation.
This closes all HPI adapters and frees the HPI subsystem. It also frees the host API struct
memory. It should be called once for every PaAsiHpi_Initialize call.
@param Pointer to host API struct
*/
static void Terminate( struct PaUtilHostApiRepresentation *hostApi )
@@ -853,9 +854,9 @@ error:
/** Converts PortAudio sample format to equivalent HPI format.
@param paFormat PortAudio sample format
@return HPI sample format
*/
static HW16 PaAsiHpi_PaToHpiFormat( PaSampleFormat paFormat )
@@ -887,9 +888,9 @@ static HW16 PaAsiHpi_PaToHpiFormat( PaSampleFormat paFormat )
/** Converts HPI sample format to equivalent PortAudio format.
@param paFormat HPI sample format
@return PortAudio sample format
*/
static PaSampleFormat PaAsiHpi_HpiToPaFormat( HW16 hpiFormat )
@@ -919,20 +920,20 @@ static PaSampleFormat PaAsiHpi_HpiToPaFormat( HW16 hpiFormat )
/** Creates HPI format struct based on PortAudio parameters.
This also does some checks to see whether the desired format is valid, and whether
This also does some checks to see whether the desired format is valid, and whether
the device allows it. This only checks the format of one half (input or output) of the
PortAudio stream.
@param hostApi Pointer to host API struct
@param parameters Pointer to stream parameter struct
@param sampleRate Desired sample rate
@param hpiDevice Pointer to HPI device struct
@param hpiFormat Resulting HPI format returned here
@return PortAudio error code (typically indicating a problem with stream format)
*/
static PaError PaAsiHpi_CreateFormat( struct PaUtilHostApiRepresentation *hostApi,
@@ -1005,13 +1006,13 @@ static PaError PaAsiHpi_CreateFormat( struct PaUtilHostApiRepresentation *hostAp
/** Open HPI input stream with given format.
This attempts to open HPI input stream with desired format. If the format is not supported
or the device is unavailable, the stream is closed and a PortAudio error code is returned.
@param hostApi Pointer to host API struct
@param hpiDevice Pointer to HPI device struct
@param hpiFormat Pointer to HPI format struct
@return PortAudio error code (typically indicating a problem with stream format or device)
*/
static PaError PaAsiHpi_OpenInput( struct PaUtilHostApiRepresentation *hostApi,
@@ -1060,13 +1061,13 @@ error:
/** Open HPI output stream with given format.
This attempts to open HPI output stream with desired format. If the format is not supported
or the device is unavailable, the stream is closed and a PortAudio error code is returned.
@param hostApi Pointer to host API struct
@param hpiDevice Pointer to HPI device struct
@param hpiFormat Pointer to HPI format struct
@return PortAudio error code (typically indicating a problem with stream format or device)
*/
static PaError PaAsiHpi_OpenOutput( struct PaUtilHostApiRepresentation *hostApi,
@@ -1115,15 +1116,15 @@ error:
/** Checks whether the desired stream formats and devices are supported
(for both input and output).
This is done by actually opening the appropriate HPI streams and closing them again.
@param hostApi Pointer to host API struct
@param inputParameters Pointer to stream parameter struct for input side of stream
@param outputParameters Pointer to stream parameter struct for output side of stream
@param sampleRate Desired sample rate
@return PortAudio error code (paFormatIsSupported on success)
*/
static PaError IsFormatSupported( struct PaUtilHostApiRepresentation *hostApi,
@@ -1177,11 +1178,11 @@ error:
/** Obtain HPI stream information.
This obtains info such as stream state and available data/space in buffers. It also
estimates whether an underflow or overflow occurred.
@param streamComp Pointer to stream component (input or output) to query
@param info Pointer to stream info struct that will contain result
@return PortAudio error code (either paNoError, paDeviceUnavailable or paUnanticipatedHostError)
*/
static PaError PaAsiHpi_GetStreamInfo( PaAsiHpiStreamComponent *streamComp, PaAsiHpiStreamInfo *info )
@@ -1262,9 +1263,9 @@ error:
/** Display stream component information for debugging purposes.
@param streamComp Pointer to stream component (input or output) to query
@param stream Pointer to stream struct which contains the component above
*/
static void PaAsiHpi_StreamComponentDump( PaAsiHpiStreamComponent *streamComp,
@@ -1391,7 +1392,7 @@ static void PaAsiHpi_StreamComponentDump( PaAsiHpiStreamComponent *streamComp,
/** Display stream information for debugging purposes.
@param stream Pointer to stream to query
*/
static void PaAsiHpi_StreamDump( PaAsiHpiStream *stream )
@@ -1457,25 +1458,25 @@ static void PaAsiHpi_StreamDump( PaAsiHpiStream *stream )
/** Determine buffer sizes and allocate appropriate stream buffers.
This attempts to allocate a BBM (host) buffer for the HPI stream component (either input
This attempts to allocate a BBM (host) buffer for the HPI stream component (either input
or output, as both have similar buffer needs). Not all AudioScience adapters support BBM,
in which case the hardware buffer has to suffice. The size of the HPI host buffer is chosen
as a multiple of framesPerPaHostBuffer, and also influenced by the suggested latency and the
estimated minimum polling interval. The HPI host and hardware buffer sizes are stored, and an
appropriate cap for the hardware buffer is also calculated. Finally, the temporary stream
in which case the hardware buffer has to suffice. The size of the HPI host buffer is chosen
as a multiple of framesPerPaHostBuffer, and also influenced by the suggested latency and the
estimated minimum polling interval. The HPI host and hardware buffer sizes are stored, and an
appropriate cap for the hardware buffer is also calculated. Finally, the temporary stream
buffer which serves as the PortAudio host buffer for this implementation is allocated.
This buffer contains an integer number of user buffers, to simplify buffer adaption in the
buffer processor. The function returns paBufferTooBig if the HPI interface cannot allocate
an HPI host buffer of the desired size.
@param streamComp Pointer to stream component struct
@param pollingInterval Polling interval for stream, in milliseconds
@param framesPerPaHostBuffer Size of PortAudio host buffer, in frames
@param suggestedLatency Suggested latency for stream component, in seconds
@return PortAudio error code (possibly paBufferTooBig or paInsufficientMemory)
*/
static PaError PaAsiHpi_SetupBuffers( PaAsiHpiStreamComponent *streamComp, HW32 pollingInterval,
@@ -1532,7 +1533,7 @@ static PaError PaAsiHpi_SetupBuffers( PaAsiHpiStreamComponent *streamComp, HW32
}
}
/* Choose closest memory block boundary (HPI API document states that
"a buffer size of Nx4096 - 20 makes the best use of memory"
"a buffer size of Nx4096 - 20 makes the best use of memory"
(under the entry for HPI_StreamEstimateBufferSize)) */
bbmBufferSize = ((HW32)ceil((bbmBufferSize + 20)/4096.0))*4096 - 20;
streamComp->hostBufferSize = bbmBufferSize;
@@ -1646,28 +1647,28 @@ error:
based on the suggested latency. It then opens each requested stream direction with the
appropriate stream format, and allocates the required stream buffers. It sets up the
various PortAudio structures dealing with streams, and estimates the stream latency.
See pa_hostapi.h for a list of validity guarantees made about OpenStream parameters.
@param hostApi Pointer to host API struct
@param s List of open streams, where successfully opened stream will go
@param inputParameters Pointer to stream parameter struct for input side of stream
@param outputParameters Pointer to stream parameter struct for output side of stream
@param sampleRate Desired sample rate
@param framesPerBuffer Desired number of frames per buffer passed to user callback
@param framesPerBuffer Desired number of frames per buffer passed to user callback
(or chunk size for blocking stream)
@param streamFlags Stream flags
@param streamCallback Pointer to user callback function (zero for blocking interface)
@param userData Pointer to user data that will be passed to callback function along with data
@return PortAudio error code
*/
static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
@@ -1860,12 +1861,12 @@ error:
/** Close PortAudio stream.
When CloseStream() is called, the multi-api layer ensures that the stream has already
When CloseStream() is called, the multi-api layer ensures that the stream has already
been stopped or aborted. This closes the underlying HPI streams and deallocates stream
buffers and structs.
@param s Pointer to PortAudio stream
@return PortAudio error code
*/
static PaError CloseStream( PaStream *s )
@@ -1919,9 +1920,9 @@ error:
This resets the output stream and uses PortAudio helper routines to fill the
temp buffer with silence. It then writes two host buffers to the stream. This is supposed
to be called before the stream is started. It has no effect on input-only streams.
@param stream Pointer to stream struct
@return PortAudio error code
*/
static PaError PaAsiHpi_PrimeOutputWithSilence( PaAsiHpiStream *stream )
@@ -1962,14 +1963,14 @@ error:
/** Start HPI streams (both input + output).
This starts all HPI streams in the PortAudio stream. Output streams are first primed with
silence, if required. After this call the PA stream is in the Active state.
@todo Implement priming via the user callback
@param stream Pointer to stream struct
@param outputPrimed True if output is already primed (if false, silence will be loaded before starting)
@return PortAudio error code
@return PortAudio error code
*/
static PaError PaAsiHpi_StartStream( PaAsiHpiStream *stream, int outputPrimed )
{
@@ -2006,10 +2007,10 @@ error:
The thread will then take care of starting the HPI streams, and this function will block
until the streams actually start. In the case of a blocking interface, the HPI streams
are simply started.
@param s Pointer to PortAudio stream
@return PortAudio error code
@return PortAudio error code
*/
static PaError StartStream( PaStream *s )
{
@@ -2025,7 +2026,7 @@ static PaError StartStream( PaStream *s )
{
/* Create and start callback engine thread */
/* Also waits 1 second for stream to be started by engine thread (otherwise aborts) */
PA_ENSURE_( PaUnixThread_New( &stream->thread, &CallbackThreadFunc, stream, 1. ) );
PA_ENSURE_( PaUnixThread_New( &stream->thread, &CallbackThreadFunc, stream, 1., 0 ) );
}
else
{
@@ -2040,15 +2041,15 @@ error:
/** Stop HPI streams (input + output), either softly or abruptly.
If abort is false, the function blocks until the output stream is drained, otherwise it
stops immediately and discards data in the stream hardware buffers.
This function is safe to call from the callback engine thread as well as the main thread.
@param stream Pointer to stream struct
@param abort True if samples in output buffer should be discarded (otherwise blocks until stream is done)
@return PortAudio error code
@return PortAudio error code
*/
static PaError PaAsiHpi_StopStream( PaAsiHpiStream *stream, int abort )
{
@@ -2098,20 +2099,20 @@ error:
/** Stop or abort PortAudio stream.
This function is used to explicitly stop the PortAudio stream (via StopStream/AbortStream),
as opposed to the situation when the callback finishes with a result other than paContinue.
If a stream is in callback mode we will have to inspect whether the background thread has
finished, or we will have to take it out. In either case we join the thread before returning.
In blocking mode, we simply tell HPI to stop abruptly (abort) or finish buffers (drain).
as opposed to the situation when the callback finishes with a result other than paContinue.
If a stream is in callback mode we will have to inspect whether the background thread has
finished, or we will have to take it out. In either case we join the thread before returning.
In blocking mode, we simply tell HPI to stop abruptly (abort) or finish buffers (drain).
The PortAudio stream will be in the Stopped state after a call to this function.
Don't call this from the callback engine thread!
Don't call this from the callback engine thread!
@param stream Pointer to stream struct
@param abort True if samples in output buffer should be discarded (otherwise blocks until stream is done)
@return PortAudio error code
*/
static PaError PaAsiHpi_ExplicitStop( PaAsiHpiStream *stream, int abort )
@@ -2151,10 +2152,10 @@ error:
/** Stop PortAudio stream.
This blocks until the output buffers are drained.
@param s Pointer to PortAudio stream
@return PortAudio error code
@return PortAudio error code
*/
static PaError StopStream( PaStream *s )
{
@@ -2164,10 +2165,10 @@ static PaError StopStream( PaStream *s )
/** Abort PortAudio stream.
This discards any existing data in output buffers and stops the stream immediately.
@param s Pointer to PortAudio stream
@return PortAudio error code
@return PortAudio error code
*/
static PaError AbortStream( PaStream *s )
{
@@ -2176,14 +2177,14 @@ static PaError AbortStream( PaStream *s )
/** Determine whether the stream is stopped.
A stream is considered to be stopped prior to a successful call to StartStream and after
a successful call to StopStream or AbortStream. If a stream callback returns a value other
A stream is considered to be stopped prior to a successful call to StartStream and after
a successful call to StopStream or AbortStream. If a stream callback returns a value other
than paContinue the stream is NOT considered to be stopped (it is in CallbackFinished state).
@param s Pointer to PortAudio stream
@return Returns one (1) when the stream is stopped, zero (0) when the stream is running, or
a PaErrorCode (which are always negative) if PortAudio is not initialized or an
a PaErrorCode (which are always negative) if PortAudio is not initialized or an
error is encountered.
*/
static PaError IsStreamStopped( PaStream *s )
@@ -2196,14 +2197,14 @@ static PaError IsStreamStopped( PaStream *s )
/** Determine whether the stream is active.
A stream is active after a successful call to StartStream(), until it becomes inactive either
as a result of a call to StopStream() or AbortStream(), or as a result of a return value
other than paContinue from the stream callback. In the latter case, the stream is considered
A stream is active after a successful call to StartStream(), until it becomes inactive either
as a result of a call to StopStream() or AbortStream(), or as a result of a return value
other than paContinue from the stream callback. In the latter case, the stream is considered
inactive after the last buffer has finished playing.
@param s Pointer to PortAudio stream
@return Returns one (1) when the stream is active (i.e. playing or recording audio),
@return Returns one (1) when the stream is active (i.e. playing or recording audio),
zero (0) when not playing, or a PaErrorCode (which are always negative)
if PortAudio is not initialized or an error is encountered.
*/
@@ -2217,12 +2218,12 @@ static PaError IsStreamActive( PaStream *s )
/** Returns current stream time.
This corresponds to the system clock. The clock should run continuously while the stream
This corresponds to the system clock. The clock should run continuously while the stream
is open, i.e. between calls to OpenStream() and CloseStream(), therefore a frame counter
is not good enough.
@param s Pointer to PortAudio stream
@return Stream time, in seconds
*/
static PaTime GetStreamTime( PaStream *s )
@@ -2232,9 +2233,9 @@ static PaTime GetStreamTime( PaStream *s )
/** Returns CPU load.
@param s Pointer to PortAudio stream
@return CPU load (0.0 if blocking interface is used)
*/
static double GetStreamCpuLoad( PaStream *s )
@@ -2251,8 +2252,8 @@ static double GetStreamCpuLoad( PaStream *s )
abruptly). It also calls the user-supplied StreamFinished callback, and sets the
stream state to CallbackFinished if it was reached via a non-paContinue return from
the user callback function.
@param userData A pointer to an open stream previously created with Pa_OpenStream
@param userData A pointer to an open stream previously created with Pa_OpenStream
*/
static void PaAsiHpi_OnThreadExit( void *userData )
{
@@ -2273,7 +2274,7 @@ static void PaAsiHpi_OnThreadExit( void *userData )
}
/* Unfortunately both explicit calls to Stop/AbortStream (leading to Stopped state)
and implicit stops via paComplete/paAbort (leading to CallbackFinished state)
and implicit stops via paComplete/paAbort (leading to CallbackFinished state)
end up here - need another flag to remind us which is the case */
if( stream->callbackFinished )
stream->state = paAsiHpiCallbackFinishedState;
@@ -2281,20 +2282,20 @@ static void PaAsiHpi_OnThreadExit( void *userData )
/** Wait until there is enough frames to fill a host buffer.
The routine attempts to sleep until at least a full host buffer can be retrieved from the
The routine attempts to sleep until at least a full host buffer can be retrieved from the
input HPI stream and passed to the output HPI stream. It will first sleep until enough
output space is available, as this is usually easily achievable. If it is an output-only
stream, it will also sleep if the hardware buffer is too full, thereby throttling the
filling of the output buffer and reducing output latency. The routine then blocks until
enough input samples are available, unless this will cause an output underflow. In the
process, input overflows and output underflows are indicated.
@param stream Pointer to stream struct
@param framesAvail Returns the number of available frames
@param cbFlags Overflows and underflows indicated in here
@return PortAudio error code (only paUnanticipatedHostError expected)
*/
static PaError PaAsiHpi_WaitForFrames( PaAsiHpiStream *stream, unsigned long *framesAvail,
@@ -2369,9 +2370,9 @@ static PaError PaAsiHpi_WaitForFrames( PaAsiHpiStream *stream, unsigned long *fr
/** @todo The paInputOverflow flag should be set in the callback containing the
first input sample following the overflow. That means the block currently sitting
at the fore-front of recording, i.e. typically the one containing the newest (last)
sample in the HPI buffer system. This is most likely not the same as the current
block of data being passed to the callback. The current overflow should ideally
be noted in an overflow list of sorts, with an indication of when it should be
sample in the HPI buffer system. This is most likely not the same as the current
block of data being passed to the callback. The current overflow should ideally
be noted in an overflow list of sorts, with an indication of when it should be
reported. The trouble starts if there are several separate overflow incidents,
given a big input buffer. Oh well, something to try out later... */
if( info.overflow )
@@ -2402,18 +2403,18 @@ error:
/** Obtain recording, current and playback timestamps of stream.
The current time is determined by the system clock. This "now" timestamp occurs at the
forefront of recording (and playback in the full-duplex case), which happens later than the
forefront of recording (and playback in the full-duplex case), which happens later than the
input timestamp by an amount equal to the total number of recorded frames in the input buffer.
The output timestamp indicates when the next generated sample will actually be played. This
The output timestamp indicates when the next generated sample will actually be played. This
happens after all the samples currently in the output buffer are played. The output timestamp
therefore follows the current timestamp by an amount equal to the number of frames yet to be
played back in the output buffer.
If the current timestamp is the present, the input timestamp is in the past and the output
timestamp is in the future.
@param stream Pointer to stream struct
@param timeInfo Pointer to timeInfo struct that will contain timestamps
*/
static void PaAsiHpi_CalculateTimeInfo( PaAsiHpiStream *stream, PaStreamCallbackTimeInfo *timeInfo )
@@ -2448,16 +2449,16 @@ static void PaAsiHpi_CalculateTimeInfo( PaAsiHpiStream *stream, PaStreamCallback
/** Read from HPI input stream and register buffers.
This reads data from the HPI input stream (if it exists) and registers the temp stream
buffers of both input and output streams with the buffer processor. In the process it also
buffers of both input and output streams with the buffer processor. In the process it also
handles input underflows in the full-duplex case.
@param stream Pointer to stream struct
@param numFrames On entrance the number of available frames, on exit the number of
@param numFrames On entrance the number of available frames, on exit the number of
received frames
@param cbFlags Indicates overflows and underflows
@return PortAudio error code
*/
static PaError PaAsiHpi_BeginProcessing( PaAsiHpiStream *stream, unsigned long *numFrames,
@@ -2531,11 +2532,11 @@ error:
This completes the processing cycle by writing the temp buffer to the HPI interface.
Additional output underflows are caught before data is written to the stream, as this
action typically remedies the underflow and hides it in the process.
@param stream Pointer to stream struct
@param numFrames The number of frames to write to the output stream
@param cbFlags Indicates overflows and underflows
*/
static PaError PaAsiHpi_EndProcessing( PaAsiHpiStream *stream, unsigned long numFrames,
@@ -2573,11 +2574,11 @@ error:
/** Main callback engine.
This function runs in a separate thread and does all the work of fetching audio data from
the AudioScience card via the HPI interface, feeding it to the user callback via the buffer
the AudioScience card via the HPI interface, feeding it to the user callback via the buffer
processor, and delivering the resulting output data back to the card via HPI calls.
It is started and terminated when the PortAudio stream is started and stopped, and starts
the HPI streams on startup.
@param userData A pointer to an open stream previously created with Pa_OpenStream.
*/
static void *CallbackThreadFunc( void *userData )
@@ -2720,16 +2721,16 @@ error:
functions can be guaranteed to only be called for blocking streams. */
/** Read data from input stream.
This reads the indicated number of frames into the supplied buffer from an input stream,
This reads the indicated number of frames into the supplied buffer from an input stream,
and blocks until this is done.
@param s Pointer to PortAudio stream
@param buffer Pointer to buffer that will receive interleaved data (or an array of pointers
to a buffer for each non-interleaved channel)
@param frames Number of frames to read from stream
@return PortAudio error code (also indicates overflow via paInputOverflowed)
*/
static PaError ReadStream( PaStream *s,
@@ -2787,16 +2788,16 @@ error:
/** Write data to output stream.
This writes the indicated number of frames from the supplied buffer to an output stream,
This writes the indicated number of frames from the supplied buffer to an output stream,
and blocks until this is done.
@param s Pointer to PortAudio stream
@param buffer Pointer to buffer that provides interleaved data (or an array of pointers
to a buffer for each non-interleaved channel)
@param frames Number of frames to write to stream
@return PortAudio error code (also indicates underflow via paOutputUnderflowed)
*/
static PaError WriteStream( PaStream *s,
@@ -2854,9 +2855,9 @@ error:
/** Number of frames that can be read from input stream without blocking.
@param s Pointer to PortAudio stream
@return Number of frames, or PortAudio error code
*/
static signed long GetStreamReadAvailable( PaStream *s )
@@ -2882,9 +2883,9 @@ error:
/** Number of frames that can be written to output stream without blocking.
@param s Pointer to PortAudio stream
@return Number of frames, or PortAudio error code
*/
static signed long GetStreamWriteAvailable( PaStream *s )
+30 -3
View File
@@ -1,5 +1,5 @@
/*
* $Id: pa_asio.cpp 1146 2006-11-23 04:39:43Z rossb $
* $Id: pa_asio.cpp 1230 2007-06-15 16:16:33Z rossb $
* Portable Audio I/O Library for ASIO Drivers
*
* Author: Stephane Letz
@@ -132,7 +132,7 @@
#include "pa_stream.h"
#include "pa_cpuload.h"
#include "pa_process.h"
#include "pa_debugprint.h"
/* This version of pa_asio.cpp is currently only targetted at Win32,
It would require a few tweaks to work with pre-OS X Macintosh.
@@ -1021,6 +1021,7 @@ static ASIOSampleRate defaultSampleRateSearchOrder_[]
/* we look up IsDebuggerPresent at runtime incase it isn't present (on Win95 for example) */
typedef BOOL (WINAPI *IsDebuggerPresentPtr)(VOID);
IsDebuggerPresentPtr IsDebuggerPresent_ = 0;
//FARPROC IsDebuggerPresent_ = 0; // this is the current way to do it apparently according to davidv
PaError PaAsio_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex hostApiIndex )
{
@@ -1677,6 +1678,7 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
PaAsioDriverInfo *driverInfo;
int *inputChannelSelectors = 0;
int *outputChannelSelectors = 0;
bool isExternal = false;
/* unless we move to using lower level ASIO calls, we can only have
one device open at a time */
@@ -1792,6 +1794,30 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
}
/* davidv: listing ASIO Clock sources, there is an ongoing investigation by
me about whether or not call ASIOSetSampleRate if an external Clock is
used. A few drivers expected different things here */
{
ASIOClockSource clocks[32];
long numSources=32;
asioError = ASIOGetClockSources(clocks, &numSources);
if( asioError != ASE_OK ){
PA_DEBUG(("ERROR: ASIOGetClockSources: %s\n", PaAsio_GetAsioErrorText(asioError) ));
}else{
PA_DEBUG(("INFO ASIOGetClockSources listing %d clocks\n", numSources ));
for (int i=0;i<numSources;++i){
PA_DEBUG(("ASIOClockSource%d %s current:%d\n", i,clocks[i].name, clocks[i].isCurrentSource ));
/*
If you have problems with some drivers when externally clocked,
uncomment the next two lines
*/
//if (clocks[i].isCurrentSource)
// isExternal = true;
}
}
}
// check that the device supports the requested sample rate
asioError = ASIOCanSampleRate( sampleRate );
@@ -1821,7 +1847,8 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
if (oldRate != sampleRate){
PA_DEBUG(("before ASIOSetSampleRate(%f)\n",sampleRate));
asioError = ASIOSetSampleRate( sampleRate );
asioError = ASIOSetSampleRate( isExternal?0:sampleRate );
/* Set sample rate */
if( asioError != ASE_OK )
{
+14 -8
View File
@@ -73,17 +73,24 @@ the stream with it. See below for creating a channel map.
Known issues:
- Latency: Latency settings are ignored in most cases. Exceptions are when
doing I/O between different devices and as a hint for selecting a realtively
low or relatively high latency in conjunction with
paHostFramesPerBufferUnspecified. Latency settings are always automatically
bound to "safe" values, however, so setting extreme values here should not be
- Buffering: No buffering beyond that provided by core audio is provided
except where absolutely needed for the implementation to work. This may cause
issues with large framesPerBuffer settings and it also means that no additional
latency will be provided even if a large latency setting is selected.
- Latency: Latency settings are generally ignored. They may be used as a
hint for buffer size in paHostFramesPerBufferUnspecified, or the value may
be used in cases where additional buffering is needed, such as doing input and
output on seperate devices. Latency settings are always automatically bound
to "safe" values, however, so setting extreme values here should not be
an issue.
- Buffer Size: paHostFramesPerBufferUnspecified and specific host buffer sizes
are supported. paHostFramesPerBufferUnspecified works best in "pro" mode,
where the buffer size and sample rate of the audio device is most likely
to match the expected values.
to match the expected values. In the case of paHostFramesPerBuffer, an
appropriate framesPerBuffer value will be used that guarantees minimum
requested latency if that's possible.
- Timing info. It reports on stream time, but I'm probably doing something
wrong since patest_sine_time often reports negative latency numbers. Also,
@@ -111,8 +118,7 @@ render quyality property is used to set the sample rate conversion quality
as "documented" here:
http://lists.apple.com/archives/coreaudio-api/2004/Jan/msg00141.html
- x86/Universal Binary: to build a universal binary, be sure to use
the darwin makefile and not the usual configure && make combo.
- x86/Universal Binary: Universal binaries can be build.
@@ -65,6 +65,7 @@
#include "pa_mac_core_internal.h"
#include <string.h> /* strlen(), memcmp() etc. */
#include <libkern/OSAtomic.h>
#include "pa_mac_core.h"
#include "pa_mac_core_utilities.h"
@@ -98,7 +99,7 @@ void PaMacCore_SetupStreamInfo( PaMacCoreStreamInfo *data, const unsigned long
/*
* Function declared in pa_mac_core.h. Adds channel mapping to a PaMacCoreStreamInfoStruct
*/
void PaMacCore_SetupChannelMap( PaMacCoreStreamInfo *data, const long * const channelMap, const unsigned long channelMapSize )
void PaMacCore_SetupChannelMap( PaMacCoreStreamInfo *data, const SInt32 * const channelMap, const unsigned long channelMapSize )
{
data->channelMap = channelMap;
data->channelMapSize = channelMapSize;
@@ -261,11 +262,12 @@ static PaError GetChannelInfo( PaMacAUHAL *auhalHostApi,
int isInput);
static PaError OpenAndSetupOneAudioUnit(
const PaMacCoreStream *stream,
const PaStreamParameters *inStreamParams,
const PaStreamParameters *outStreamParams,
const unsigned long requestedFramesPerBuffer,
unsigned long *actualInputFramesPerBuffer,
unsigned long *actualOutputFramesPerBuffer,
const UInt32 requestedFramesPerBuffer,
UInt32 *actualInputFramesPerBuffer,
UInt32 *actualOutputFramesPerBuffer,
const PaMacAUHAL *auhalHostApi,
AudioUnit *audioUnit,
AudioConverterRef *srConverter,
@@ -277,6 +279,55 @@ static PaError OpenAndSetupOneAudioUnit(
#define PA_AUHAL_SET_LAST_HOST_ERROR( errorCode, errorText ) \
PaUtil_SetLastHostErrorInfo( paInDevelopment, errorCode, errorText )
/*
* Callback for setting over/underrun flags.
*
*/
static OSStatus xrunCallback(
AudioDeviceID inDevice,
UInt32 inChannel,
Boolean isInput,
AudioDevicePropertyID inPropertyID,
void* inClientData)
{
PaMacCoreStream *stream = (PaMacCoreStream *) inClientData;
if( stream->state != ACTIVE )
return 0; //if the stream isn't active, we don't care if the device is dropping
if( isInput )
OSAtomicOr32( paInputUnderflow, (uint32_t *)&(stream->xrunFlags) );
else
OSAtomicOr32( paOutputOverflow, (uint32_t *)&(stream->xrunFlags) );
return 0;
}
/*
* Callback called when starting or stopping a stream.
*/
static void startStopCallback(
void * inRefCon,
AudioUnit ci,
AudioUnitPropertyID inID,
AudioUnitScope inScope,
AudioUnitElement inElement )
{
PaMacCoreStream *stream = (PaMacCoreStream *) inRefCon;
UInt32 isRunning;
UInt32 size = sizeof( isRunning );
assert( !AudioUnitGetProperty( ci, kAudioOutputUnitProperty_IsRunning, inScope, inElement, &isRunning, &size ) );
if( isRunning )
return; //We are only interested in when we are stopping
// -- if we are using 2 I/O units, we only need one notification!
if( stream->inputUnit && stream->outputUnit && stream->inputUnit != stream->outputUnit && ci == stream->inputUnit )
return;
PaStreamFinishedCallback *sfc = stream->streamRepresentation.streamFinishedCallback;
if( stream->state == STOPPING )
stream->state = STOPPED ;
if( sfc )
sfc( stream->streamRepresentation.userData );
}
/*currently, this is only used in initialization, but it might be modified
to be used when the list of devices changes.*/
static PaError gatherDeviceInfo(PaMacAUHAL *auhalHostApi)
@@ -737,11 +788,12 @@ static PaError IsFormatSupported( struct PaUtilHostApiRepresentation *hostApi,
}
static PaError OpenAndSetupOneAudioUnit(
const PaMacCoreStream *stream,
const PaStreamParameters *inStreamParams,
const PaStreamParameters *outStreamParams,
const unsigned long requestedFramesPerBuffer,
unsigned long *actualInputFramesPerBuffer,
unsigned long *actualOutputFramesPerBuffer,
const UInt32 requestedFramesPerBuffer,
UInt32 *actualInputFramesPerBuffer,
UInt32 *actualOutputFramesPerBuffer,
const PaMacAUHAL *auhalHostApi,
AudioUnit *audioUnit,
AudioConverterRef *srConverter,
@@ -753,7 +805,7 @@ static PaError OpenAndSetupOneAudioUnit(
Component comp;
/*An Apple TN suggests using CAStreamBasicDescription, but that is C++*/
AudioStreamBasicDescription desiredFormat;
OSErr result = noErr;
OSStatus result = noErr;
PaError paResult = paNoError;
int line = 0;
UInt32 callbackKey;
@@ -881,7 +933,7 @@ static PaError OpenAndSetupOneAudioUnit(
audioDevice,
sizeof(AudioDeviceID) ) );
}
if( outStreamParams )
if( outStreamParams && outStreamParams != inStreamParams )
{
*audioDevice = auhalHostApi->devIds[outStreamParams->device] ;
ERR_WRAP( AudioUnitSetProperty( *audioUnit,
@@ -891,6 +943,19 @@ static PaError OpenAndSetupOneAudioUnit(
audioDevice,
sizeof(AudioDeviceID) ) );
}
/* -- add listener for dropouts -- */
ERR_WRAP( AudioDeviceAddPropertyListener( *audioDevice,
0,
outStreamParams ? false : true,
kAudioDeviceProcessorOverload,
xrunCallback,
(void *)stream) );
/* -- listen for stream start and stop -- */
ERR_WRAP( AudioUnitAddPropertyListener( *audioUnit,
kAudioOutputUnitProperty_IsRunning,
startStopCallback,
(void *)stream ) );
/* -- set format -- */
bzero( &desiredFormat, sizeof(desiredFormat) );
@@ -1010,7 +1075,7 @@ static PaError OpenAndSetupOneAudioUnit(
kAudioUnitScope_Input,
OUTPUT_ELEMENT,
actualOutputFramesPerBuffer,
sizeof(unsigned long) ) );
sizeof(*actualOutputFramesPerBuffer) ) );
ERR_WRAP( AudioUnitGetProperty( *audioUnit,
kAudioUnitProperty_MaximumFramesPerSlice,
kAudioUnitScope_Global,
@@ -1025,7 +1090,7 @@ static PaError OpenAndSetupOneAudioUnit(
kAudioUnitScope_Output,
INPUT_ELEMENT,
actualInputFramesPerBuffer,
sizeof(unsigned long) ) );
sizeof(*actualInputFramesPerBuffer) ) );
/* Don't know why this causes problems
ERR_WRAP( AudioUnitGetProperty( *audioUnit,
kAudioUnitProperty_MaximumFramesPerSlice,
@@ -1305,7 +1370,7 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
/*requested a realtively low latency. make sure this is in range of devices */
/*try to get the device's min natural buffer size and use that (but no smaller than 64).*/
AudioValueRange audioRange;
size_t size = sizeof( audioRange );
UInt32 size = sizeof( audioRange );
if( inputParameters ) {
WARNING( result = AudioDeviceGetProperty( auhalHostApi->devIds[inputParameters->device],
0,
@@ -1315,6 +1380,7 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
if( result )
requested = MAX( requested, audioRange.mMinimum );
}
size = sizeof( audioRange );
if( outputParameters ) {
WARNING( result = AudioDeviceGetProperty( auhalHostApi->devIds[outputParameters->device],
0,
@@ -1328,7 +1394,7 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
/* requested a realtively high latency. make sure this is in range of devices */
/*try to get the device's max natural buffer size and use that (but no larger than 1024).*/
AudioValueRange audioRange;
size_t size = sizeof( audioRange );
UInt32 size = sizeof( audioRange );
requested = MIN( requested, 1024 );
if( inputParameters ) {
WARNING( result = AudioDeviceGetProperty( auhalHostApi->devIds[inputParameters->device],
@@ -1339,6 +1405,7 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
if( result )
requested = MIN( requested, audioRange.mMaximum );
}
size = sizeof( audioRange );
if( outputParameters ) {
WARNING( result = AudioDeviceGetProperty( auhalHostApi->devIds[outputParameters->device],
0,
@@ -1359,17 +1426,22 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
/* -- Now we actually open and setup streams. -- */
if( inputParameters && outputParameters && outputParameters->device == inputParameters->device )
{ /* full duplex. One device. */
result = OpenAndSetupOneAudioUnit( inputParameters,
UInt32 inputFramesPerBuffer = (UInt32) stream->inputFramesPerBuffer;
UInt32 outputFramesPerBuffer = (UInt32) stream->outputFramesPerBuffer;
result = OpenAndSetupOneAudioUnit( stream,
inputParameters,
outputParameters,
framesPerBuffer,
&(stream->inputFramesPerBuffer),
&(stream->outputFramesPerBuffer),
&inputFramesPerBuffer,
&outputFramesPerBuffer,
auhalHostApi,
&(stream->inputUnit),
&(stream->inputSRConverter),
&(stream->inputDevice),
sampleRate,
stream );
stream->inputFramesPerBuffer = inputFramesPerBuffer;
stream->outputFramesPerBuffer = outputFramesPerBuffer;
stream->outputUnit = stream->inputUnit;
stream->outputDevice = stream->inputDevice;
if( result != paNoError )
@@ -1377,11 +1449,14 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
}
else
{ /* full duplex, different devices OR simplex */
result = OpenAndSetupOneAudioUnit( NULL,
UInt32 outputFramesPerBuffer = (UInt32) stream->outputFramesPerBuffer;
UInt32 inputFramesPerBuffer = (UInt32) stream->inputFramesPerBuffer;
result = OpenAndSetupOneAudioUnit( stream,
NULL,
outputParameters,
framesPerBuffer,
NULL,
&(stream->outputFramesPerBuffer),
&outputFramesPerBuffer,
auhalHostApi,
&(stream->outputUnit),
NULL,
@@ -1390,10 +1465,11 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
stream );
if( result != paNoError )
goto error;
result = OpenAndSetupOneAudioUnit( inputParameters,
result = OpenAndSetupOneAudioUnit( stream,
inputParameters,
NULL,
framesPerBuffer,
&(stream->inputFramesPerBuffer),
&inputFramesPerBuffer,
NULL,
auhalHostApi,
&(stream->inputUnit),
@@ -1403,6 +1479,8 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
stream );
if( result != paNoError )
goto error;
stream->inputFramesPerBuffer = inputFramesPerBuffer;
stream->outputFramesPerBuffer = outputFramesPerBuffer;
}
if( stream->inputUnit ) {
@@ -1727,14 +1805,14 @@ static OSStatus AudioIOProc( void *inRefCon,
* we do not use the input SR converter or the input ring buffer.
*
*/
OSErr err = 0;
OSStatus err = 0;
unsigned long frames;
/* -- start processing -- */
PaUtil_BeginBufferProcessing( &(stream->bufferProcessor),
&timeInfo,
stream->xrunFlags );
stream->xrunFlags = 0;
stream->xrunFlags = 0; //FIXME: this flag also gets set outside by a callback, which calls the xrunCallback function. It should be in the same thread as the main audio callback, but the apple docs just use the word "usually" so it may be possible to loose an xrun notification, if that callback happens here.
/* -- compute frames. do some checks -- */
assert( ioData->mNumberBuffers == 1 );
@@ -1868,7 +1946,7 @@ static OSStatus AudioIOProc( void *inRefCon,
PaUtil_AdvanceRingBufferReadIndex(&stream->inputRingBuffer, size1 );
} else if( ( size1 + size2 ) / ( flsz * inChan ) < frames ) {
/*we underflowed. take what data we can, zero the rest.*/
float data[frames*inChan];
unsigned char data[frames*inChan*flsz];
if( size1 )
memcpy( data, data1, size1 );
if( size2 )
@@ -1922,7 +2000,7 @@ static OSStatus AudioIOProc( void *inRefCon,
* if this is an input-only stream, we need to process it more,
* otherwise, we let the output case deal with it.
*/
OSErr err = 0;
OSStatus err = 0;
int chan = stream->inputAudioBufferList.mBuffers[0].mNumberChannels ;
/* FIXME: looping here may not actually be necessary, but it was something I tried in testing. */
do {
@@ -2054,6 +2132,18 @@ static PaError CloseStream( PaStream* s )
VDBUG( ( "Closing stream.\n" ) );
if( stream ) {
if( stream->outputUnit )
AudioDeviceRemovePropertyListener( stream->outputDevice,
0,
false,
kAudioDeviceProcessorOverload,
xrunCallback );
if( stream->inputUnit && stream->outputUnit != stream->inputUnit )
AudioDeviceRemovePropertyListener( stream->inputDevice,
0,
true,
kAudioDeviceProcessorOverload,
xrunCallback );
if( stream->outputUnit && stream->outputUnit != stream->inputUnit ) {
AudioUnitUninitialize( stream->outputUnit );
CloseComponent( stream->outputUnit );
@@ -2089,11 +2179,10 @@ static PaError CloseStream( PaStream* s )
return result;
}
static PaError StartStream( PaStream *s )
{
PaMacCoreStream *stream = (PaMacCoreStream*)s;
OSErr result = noErr;
OSStatus result = noErr;
VVDBUG(("StartStream()\n"));
VDBUG( ( "Starting stream.\n" ) );
@@ -2120,11 +2209,25 @@ static PaError StartStream( PaStream *s )
#undef ERR_WRAP
}
// it's not clear from appl's docs that this really waits
// until all data is flushed.
static ComponentResult BlockWhileAudioUnitIsRunning( AudioUnit audioUnit, AudioUnitElement element )
{
Boolean isRunning = 1;
while( isRunning ) {
UInt32 s = sizeof( isRunning );
ComponentResult err = AudioUnitGetProperty( audioUnit, kAudioOutputUnitProperty_IsRunning, kAudioUnitScope_Global, element, &isRunning, &s );
if( err )
return err;
Pa_Sleep( 100 );
}
return noErr;
}
static PaError StopStream( PaStream *s )
{
PaMacCoreStream *stream = (PaMacCoreStream*)s;
OSErr result = noErr;
OSStatus result = noErr;
PaError paErr;
VVDBUG(("StopStream()\n"));
@@ -2140,6 +2243,8 @@ static PaError StopStream( PaStream *s )
if( stream->inputUnit == stream->outputUnit && stream->inputUnit )
{
ERR_WRAP( AudioOutputUnitStop(stream->inputUnit) );
ERR_WRAP( BlockWhileAudioUnitIsRunning(stream->inputUnit,0) );
ERR_WRAP( BlockWhileAudioUnitIsRunning(stream->inputUnit,1) );
ERR_WRAP( AudioUnitReset(stream->inputUnit, kAudioUnitScope_Global, 1) );
ERR_WRAP( AudioUnitReset(stream->inputUnit, kAudioUnitScope_Global, 0) );
}
@@ -2148,11 +2253,13 @@ static PaError StopStream( PaStream *s )
if( stream->inputUnit )
{
ERR_WRAP(AudioOutputUnitStop(stream->inputUnit) );
ERR_WRAP( BlockWhileAudioUnitIsRunning(stream->inputUnit,1) );
ERR_WRAP(AudioUnitReset(stream->inputUnit,kAudioUnitScope_Global,1));
}
if( stream->outputUnit )
{
ERR_WRAP(AudioOutputUnitStop(stream->outputUnit));
ERR_WRAP( BlockWhileAudioUnitIsRunning(stream->outputUnit,0) );
ERR_WRAP(AudioUnitReset(stream->outputUnit,kAudioUnitScope_Global,0));
}
}
@@ -85,6 +85,22 @@ static size_t computeSampleSizeFromFormat( PaSampleFormat format )
default: return 0;
}
}
/*
* Same as computeSampleSizeFromFormat, except that if
* the size is not a power of two, it returns the next power of two up
*/
static size_t computeSampleSizeFromFormatPow2( PaSampleFormat format )
{
switch( format ) {
case paFloat32: return 4;
case paInt32: return 4;
case paInt24: return 4;
case paInt16: return 2;
case paInt8: case paUInt8: return 1;
default: return 0;
}
}
/*
@@ -114,10 +130,14 @@ PaError initializeBlioRingBuffers(
blio->outputRingBuffer.buffer = NULL;
/* initialize simple data */
blio->ringBufferFrames = ringBufferSize;
blio->inputSampleFormat = inputSampleFormat;
blio->inputSampleSize = computeSampleSizeFromFormat(inputSampleFormat);
blio->inputSampleSizeActual = computeSampleSizeFromFormat(inputSampleFormat);
blio->inputSampleSizePow2 = computeSampleSizeFromFormatPow2(inputSampleFormat);
blio->outputSampleFormat = outputSampleFormat;
blio->outputSampleSize = computeSampleSizeFromFormat(outputSampleFormat);
blio->outputSampleSizeActual = computeSampleSizeFromFormat(outputSampleFormat);
blio->outputSampleSizePow2 = computeSampleSizeFromFormatPow2(outputSampleFormat);
blio->framesPerBuffer = framesPerBuffer;
blio->inChan = inChan;
blio->outChan = outChan;
@@ -142,7 +162,7 @@ PaError initializeBlioRingBuffers(
result = UNIX_ERR( pthread_cond_init( &(blio->outputCond), NULL ) );
#endif
if( inChan ) {
data = calloc( ringBufferSize, blio->inputSampleSize );
data = calloc( ringBufferSize, blio->inputSampleSizePow2*inChan );
if( !data )
{
result = paInsufficientMemory;
@@ -151,11 +171,11 @@ PaError initializeBlioRingBuffers(
assert( 0 == PaUtil_InitializeRingBuffer(
&blio->inputRingBuffer,
ringBufferSize*blio->inputSampleSize,
ringBufferSize*blio->inputSampleSizePow2*inChan,
data ) );
}
if( outChan ) {
data = calloc( ringBufferSize, blio->outputSampleSize );
data = calloc( ringBufferSize, blio->outputSampleSizePow2*outChan );
if( !data )
{
result = paInsufficientMemory;
@@ -164,7 +184,7 @@ PaError initializeBlioRingBuffers(
assert( 0 == PaUtil_InitializeRingBuffer(
&blio->outputRingBuffer,
ringBufferSize*blio->outputSampleSize,
ringBufferSize*blio->outputSampleSizePow2*outChan,
data ) );
}
@@ -247,7 +267,8 @@ PaError resetBlioRingBuffers( PaMacBlio *blio )
bzero( blio->outputRingBuffer.buffer,
blio->outputRingBuffer.bufferSize );
/* Advance buffer */
PaUtil_AdvanceRingBufferWriteIndex( &blio->outputRingBuffer, blio->outputRingBuffer.bufferSize );
PaUtil_AdvanceRingBufferWriteIndex( &blio->outputRingBuffer, blio->ringBufferFrames*blio->outputSampleSizeActual*blio->outChan );
//PaUtil_AdvanceRingBufferWriteIndex( &blio->outputRingBuffer, blio->outputRingBuffer.bufferSize );
/* Update isOutputFull. */
#ifdef PA_MAC__BLIO_MUTEX
@@ -332,10 +353,10 @@ int BlioCallback( const void *input, void *output, unsigned long frameCount,
avail = PaUtil_GetRingBufferWriteAvailable( &blio->inputRingBuffer );
/* check for underflow */
if( avail < frameCount * blio->inputSampleSize * blio->inChan )
if( avail < frameCount * blio->inputSampleSizeActual * blio->inChan )
OSAtomicOr32( paInputOverflow, &blio->statusFlags );
toRead = MIN( avail, frameCount * blio->inputSampleSize * blio->inChan );
toRead = MIN( avail, frameCount * blio->inputSampleSizeActual * blio->inChan );
/* copy the data */
/*printf( "reading %d\n", toRead );*/
@@ -352,14 +373,14 @@ int BlioCallback( const void *input, void *output, unsigned long frameCount,
avail = PaUtil_GetRingBufferReadAvailable( &blio->outputRingBuffer );
/* check for underflow */
if( avail < frameCount * blio->outputSampleSize * blio->outChan )
if( avail < frameCount * blio->outputSampleSizeActual * blio->outChan )
OSAtomicOr32( paOutputUnderflow, &blio->statusFlags );
toWrite = MIN( avail, frameCount * blio->outputSampleSize * blio->outChan );
toWrite = MIN( avail, frameCount * blio->outputSampleSizeActual * blio->outChan );
if( toWrite != frameCount * blio->outputSampleSize * blio->outChan )
if( toWrite != frameCount * blio->outputSampleSizeActual * blio->outChan )
bzero( ((char *)output)+toWrite,
frameCount * blio->outputSampleSize * blio->outChan - toWrite );
frameCount * blio->outputSampleSizeActual * blio->outChan - toWrite );
/* copy the data */
/*printf( "writing %d\n", toWrite );*/
assert( toWrite == PaUtil_ReadRingBuffer( &blio->outputRingBuffer, output, toWrite ) );
@@ -413,11 +434,11 @@ PaError ReadStream( PaStream* stream,
#endif
}
} while( avail == 0 );
toRead = MIN( avail, frames * blio->inputSampleSize * blio->inChan );
toRead -= toRead % blio->inputSampleSize * blio->inChan ;
toRead = MIN( avail, frames * blio->inputSampleSizeActual * blio->inChan );
toRead -= toRead % blio->inputSampleSizeActual * blio->inChan ;
PaUtil_ReadRingBuffer( &blio->inputRingBuffer, (void *)cbuf, toRead );
cbuf += toRead;
frames -= toRead / ( blio->inputSampleSize * blio->inChan );
frames -= toRead / ( blio->inputSampleSizeActual * blio->inChan );
if( toRead == avail ) {
#ifdef PA_MAC_BLIO_MUTEX
@@ -443,7 +464,7 @@ PaError ReadStream( PaStream* stream,
/* report underflow only once: */
if( ret ) {
OSAtomicAnd32( ~paInputOverflow, &blio->statusFlags );
OSAtomicAnd32( (uint32_t)(~paInputOverflow), &blio->statusFlags );
ret = paInputOverflowed;
}
@@ -491,11 +512,11 @@ PaError WriteStream( PaStream* stream,
}
} while( avail == 0 );
toWrite = MIN( avail, frames * blio->outputSampleSize * blio->outChan );
toWrite -= toWrite % blio->outputSampleSize * blio->outChan ;
toWrite = MIN( avail, frames * blio->outputSampleSizeActual * blio->outChan );
toWrite -= toWrite % blio->outputSampleSizeActual * blio->outChan ;
PaUtil_WriteRingBuffer( &blio->outputRingBuffer, (void *)cbuf, toWrite );
cbuf += toWrite;
frames -= toWrite / ( blio->outputSampleSize * blio->outChan );
frames -= toWrite / ( blio->outputSampleSizeActual * blio->outChan );
#ifdef PA_MAC_BLIO_MUTEX
if( toWrite == avail ) {
@@ -520,7 +541,7 @@ PaError WriteStream( PaStream* stream,
/* report underflow only once: */
if( ret ) {
OSAtomicAnd32( ~paOutputUnderflow, &blio->statusFlags );
OSAtomicAnd32( (uint32_t)(~paOutputUnderflow), &blio->statusFlags );
ret = paOutputUnderflowed;
}
@@ -549,7 +570,7 @@ signed long GetStreamReadAvailable( PaStream* stream )
VVDBUG(("GetStreamReadAvailable()\n"));
return PaUtil_GetRingBufferReadAvailable( &blio->inputRingBuffer )
/ ( blio->outputSampleSize * blio->outChan );
/ ( blio->outputSampleSizeActual * blio->outChan );
}
@@ -559,6 +580,6 @@ signed long GetStreamWriteAvailable( PaStream* stream )
VVDBUG(("GetStreamWriteAvailable()\n"));
return PaUtil_GetRingBufferWriteAvailable( &blio->outputRingBuffer )
/ ( blio->outputSampleSize * blio->outChan );
/ ( blio->outputSampleSizeActual * blio->outChan );
}
@@ -79,10 +79,13 @@
typedef struct {
PaUtilRingBuffer inputRingBuffer;
PaUtilRingBuffer outputRingBuffer;
size_t ringBufferFrames;
PaSampleFormat inputSampleFormat;
size_t inputSampleSize;
size_t inputSampleSizeActual;
size_t inputSampleSizePow2;
PaSampleFormat outputSampleFormat;
size_t outputSampleSize;
size_t outputSampleSizeActual;
size_t outputSampleSizePow2;
size_t framesPerBuffer;
@@ -139,6 +139,7 @@ typedef struct PaMacCoreStream
/* We need to preallocate an inputBuffer for reading data. */
AudioBufferList inputAudioBufferList;
AudioTimeStamp startTime;
/* FIXME: instead of volatile, these should be properly memory barriered */
volatile PaStreamCallbackFlags xrunFlags;
volatile bool isTimeSet;
volatile enum {
@@ -146,7 +147,8 @@ typedef struct PaMacCoreStream
and the user has called StopStream(). */
CALLBACK_STOPPED = 1, /* callback has requested stop,
but user has not yet called StopStream(). */
STOPPING = 2, /* The stream is in the process of closing.
STOPPING = 2, /* The stream is in the process of closing
because the user has called StopStream.
This state is just used internally;
externally it is indistinguishable from
ACTIVE.*/
@@ -199,10 +199,19 @@ PaError PaMacCore_SetError(OSStatus error, int line, int isError)
else
errorType = "Warning";
if ((int)error < -99999 || (int)error > 99999)
DBUG(("%s on line %d: err='%4s', msg='%s'\n", errorType, line, (const char *)&error, errorText));
else
DBUG(("%s on line %d: err=%d, 0x%x, msg='%s'\n", errorType, line, (int)error, (unsigned)error, errorText));
char str[20];
// see if it appears to be a 4-char-code
*(UInt32 *)(str + 1) = CFSwapInt32HostToBig(error);
if (isprint(str[1]) && isprint(str[2]) && isprint(str[3]) && isprint(str[4]))
{
str[0] = str[5] = '\'';
str[6] = '\0';
} else {
// no, format it as an integer
sprintf(str, "%d", (int)error);
}
DBUG(("%s on line %d: err='%s', msg=%s\n", errorType, line, str, errorText));
PaUtil_SetLastHostErrorInfo( paCoreAudio, error, errorText );
@@ -520,9 +529,9 @@ PaError setBestSampleRateForDevice( const AudioDeviceID device,
not usually catastrophic.
*/
PaError setBestFramesPerBuffer( const AudioDeviceID device,
const bool isOutput,
unsigned long requestedFramesPerBuffer,
unsigned long *actualFramesPerBuffer )
const bool isOutput,
UInt32 requestedFramesPerBuffer,
UInt32 *actualFramesPerBuffer )
{
UInt32 afpb;
const bool isInput = !isOutput;
@@ -199,7 +199,7 @@ PaError setBestSampleRateForDevice( const AudioDeviceID device,
not usually catastrophic.
*/
PaError setBestFramesPerBuffer( const AudioDeviceID device,
const bool isOutput,
unsigned long requestedFramesPerBuffer,
unsigned long *actualFramesPerBuffer );
const bool isOutput,
UInt32 requestedFramesPerBuffer,
UInt32 *actualFramesPerBuffer );
#endif /* PA_MAC_CORE_UTILITIES_H__*/
+431 -117
View File
@@ -1,10 +1,10 @@
/*
* $Id: pa_win_ds.c 1116 2006-09-06 15:47:03Z rossb $
* $Id: pa_win_ds.c 1286 2007-09-26 21:34:23Z rossb $
* Portable Audio I/O Library DirectSound implementation
*
* Authors: Phil Burk, Robert Marsanyi & Ross Bencina
* Based on the Open Source API proposed by Ross Bencina
* Copyright (c) 1999-2006 Ross Bencina, Phil Burk, Robert Marsanyi
* Copyright (c) 1999-2007 Ross Bencina, Phil Burk, Robert Marsanyi
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files
@@ -55,20 +55,34 @@
@todo implement IsFormatSupported
@todo check that CoInitialize() CoUninitialize() are always correctly
paired, even in error cases.
@todo call PaUtil_SetLastHostErrorInfo with a specific error string (currently just "DSound error").
@todo make sure all buffers have been played before stopping the stream
when the stream callback returns paComplete
@todo retrieve default devices using the DRVM_MAPPER_PREFERRED_GET functions used in the wmme api
these wave device ids can be aligned with the directsound devices either by retrieving
the system interface device name using DRV_QUERYDEVICEINTERFACE or by using the wave device
id retrieved in KsPropertySetEnumerateCallback.
old TODOs from phil, need to work out if these have been done:
O- fix "patest_stop.c"
*/
#include <stdio.h>
#include <string.h> /* strlen() */
#include <windows.h>
#include <objbase.h>
/*
We are only using DX3 in here, no need to polute the namespace - davidv
*/
#define DIRECTSOUND_VERSION 0x0300
#include <dsound.h>
#ifdef PAWIN_USE_WDMKS_DEVICE_INFO
#include <dsconf.h>
#endif /* PAWIN_USE_WDMKS_DEVICE_INFO */
#include "pa_util.h"
#include "pa_allocation.h"
@@ -76,8 +90,13 @@
#include "pa_stream.h"
#include "pa_cpuload.h"
#include "pa_process.h"
#include "pa_debugprint.h"
#include "pa_win_ds.h"
#include "pa_win_ds_dynlink.h"
#include "pa_win_waveformat.h"
#include "pa_win_wdmks_utils.h"
#if (defined(WIN32) && (defined(_MSC_VER) && (_MSC_VER >= 1200))) /* MSC version 6 and above */
#pragma comment( lib, "dsound.lib" )
@@ -168,9 +187,12 @@ static BOOL CALLBACK CollectGUIDsProc(LPGUID lpGUID,
typedef struct PaWinDsDeviceInfo
{
GUID guid;
GUID *lpGUID;
double sampleRates[3];
PaDeviceInfo inheritedDeviceInfo;
GUID guid;
GUID *lpGUID;
double sampleRates[3];
char deviceInputChannelCountIsKnown; /**<< if the system returns 0xFFFF then we don't really know the number of supported channels (1=>known, 0=>unknown)*/
char deviceOutputChannelCountIsKnown; /**<< if the system returns 0xFFFF then we don't really know the number of supported channels (1=>known, 0=>unknown)*/
} PaWinDsDeviceInfo;
typedef struct
@@ -182,7 +204,8 @@ typedef struct
PaUtilAllocationGroup *allocations;
/* implementation specific data goes here */
PaWinDsDeviceInfo *winDsDeviceInfos;
char comWasInitialized;
} PaWinDsHostApiRepresentation;
@@ -269,6 +292,7 @@ typedef struct DSDeviceNameAndGUID{
char *name; // allocated from parent's allocations, never deleted by this structure
GUID guid;
LPGUID lpGUID;
void *pnpInterface; // wchar_t* interface path, allocated using the DS host api's allocation group
} DSDeviceNameAndGUID;
typedef struct DSDeviceNameAndGUIDVector{
@@ -280,6 +304,12 @@ typedef struct DSDeviceNameAndGUIDVector{
DSDeviceNameAndGUID *items; // Allocated using LocalAlloc()
} DSDeviceNameAndGUIDVector;
typedef struct DSDeviceNamesAndGUIDs{
PaWinDsHostApiRepresentation *winDsHostApi;
DSDeviceNameAndGUIDVector inputNamesAndGUIDs;
DSDeviceNameAndGUIDVector outputNamesAndGUIDs;
} DSDeviceNamesAndGUIDs;
static PaError InitializeDSDeviceNameAndGUIDVector(
DSDeviceNameAndGUIDVector *guidVector, PaUtilAllocationGroup *allocations )
{
@@ -326,6 +356,7 @@ static PaError ExpandDSDeviceNameAndGUIDVector( DSDeviceNameAndGUIDVector *guidV
newItems[i].lpGUID = &newItems[i].guid;
memcpy( &newItems[i].guid, guidVector->items[i].lpGUID, sizeof(GUID) );;
}
newItems[i].pnpInterface = guidVector->items[i].pnpInterface;
}
LocalFree( guidVector->items );
@@ -397,12 +428,153 @@ static BOOL CALLBACK CollectGUIDsProc(LPGUID lpGUID,
return FALSE;
}
namesAndGUIDs->items[namesAndGUIDs->count].pnpInterface = 0;
++namesAndGUIDs->count;
--namesAndGUIDs->free;
return TRUE;
}
#ifdef PAWIN_USE_WDMKS_DEVICE_INFO
static void *DuplicateWCharString( PaUtilAllocationGroup *allocations, wchar_t *source )
{
size_t len;
wchar_t *result;
len = wcslen( source );
result = (wchar_t*)PaUtil_GroupAllocateMemory( allocations, (long) ((len+1) * sizeof(wchar_t)) );
wcscpy( result, source );
return result;
}
static BOOL CALLBACK KsPropertySetEnumerateCallback( PDSPROPERTY_DIRECTSOUNDDEVICE_DESCRIPTION_W_DATA data, LPVOID context )
{
int i;
DSDeviceNamesAndGUIDs *deviceNamesAndGUIDs = (DSDeviceNamesAndGUIDs*)context;
if( data->DataFlow == DIRECTSOUNDDEVICE_DATAFLOW_RENDER )
{
for( i=0; i < deviceNamesAndGUIDs->outputNamesAndGUIDs.count; ++i )
{
if( deviceNamesAndGUIDs->outputNamesAndGUIDs.items[i].lpGUID
&& memcmp( &data->DeviceId, deviceNamesAndGUIDs->outputNamesAndGUIDs.items[i].lpGUID, sizeof(GUID) ) == 0 )
{
deviceNamesAndGUIDs->outputNamesAndGUIDs.items[i].pnpInterface =
(char*)DuplicateWCharString( deviceNamesAndGUIDs->winDsHostApi->allocations, data->Interface );
break;
}
}
}
else if( data->DataFlow == DIRECTSOUNDDEVICE_DATAFLOW_CAPTURE )
{
for( i=0; i < deviceNamesAndGUIDs->inputNamesAndGUIDs.count; ++i )
{
if( deviceNamesAndGUIDs->inputNamesAndGUIDs.items[i].lpGUID
&& memcmp( &data->DeviceId, deviceNamesAndGUIDs->inputNamesAndGUIDs.items[i].lpGUID, sizeof(GUID) ) == 0 )
{
deviceNamesAndGUIDs->inputNamesAndGUIDs.items[i].pnpInterface =
(char*)DuplicateWCharString( deviceNamesAndGUIDs->winDsHostApi->allocations, data->Interface );
break;
}
}
}
return TRUE;
}
static GUID pawin_CLSID_DirectSoundPrivate =
{ 0x11ab3ec0, 0x25ec, 0x11d1, 0xa4, 0xd8, 0x00, 0xc0, 0x4f, 0xc2, 0x8a, 0xca };
static GUID pawin_DSPROPSETID_DirectSoundDevice =
{ 0x84624f82, 0x25ec, 0x11d1, 0xa4, 0xd8, 0x00, 0xc0, 0x4f, 0xc2, 0x8a, 0xca };
static GUID pawin_IID_IKsPropertySet =
{ 0x31efac30, 0x515c, 0x11d0, 0xa9, 0xaa, 0x00, 0xaa, 0x00, 0x61, 0xbe, 0x93 };
/*
FindDevicePnpInterfaces fills in the pnpInterface fields in deviceNamesAndGUIDs
with UNICODE file paths to the devices. The DS documentation mentions
at least two techniques by which these Interface paths can be found using IKsPropertySet on
the DirectSound class object. One is using the DSPROPERTY_DIRECTSOUNDDEVICE_DESCRIPTION
property, and the other is using DSPROPERTY_DIRECTSOUNDDEVICE_ENUMERATE.
I tried both methods and only the second worked. I found two postings on the
net from people who had the same problem with the first method, so I think the method used here is
more common/likely to work. The probem is that IKsPropertySet_Get returns S_OK
but the fields of the device description are not filled in.
The mechanism we use works by registering an enumeration callback which is called for
every DSound device. Our callback searches for a device in our deviceNamesAndGUIDs list
with the matching GUID and copies the pointer to the Interface path.
Note that we could have used this enumeration callback to perform the original
device enumeration, however we choose not to so we can disable this step easily.
Apparently the IKsPropertySet mechanism was added in DirectSound 9c 2004
http://www.tech-archive.net/Archive/Development/microsoft.public.win32.programmer.mmedia/2004-12/0099.html
-- rossb
*/
static void FindDevicePnpInterfaces( DSDeviceNamesAndGUIDs *deviceNamesAndGUIDs )
{
IClassFactory *pClassFactory;
if( paWinDsDSoundEntryPoints.DllGetClassObject(&pawin_CLSID_DirectSoundPrivate, &IID_IClassFactory, (PVOID *) &pClassFactory) == S_OK ){
IKsPropertySet *pPropertySet;
if( pClassFactory->lpVtbl->CreateInstance( pClassFactory, NULL, &pawin_IID_IKsPropertySet, (PVOID *) &pPropertySet) == S_OK ){
DSPROPERTY_DIRECTSOUNDDEVICE_ENUMERATE_W_DATA data;
ULONG bytesReturned;
data.Callback = KsPropertySetEnumerateCallback;
data.Context = deviceNamesAndGUIDs;
IKsPropertySet_Get( pPropertySet,
&pawin_DSPROPSETID_DirectSoundDevice,
DSPROPERTY_DIRECTSOUNDDEVICE_ENUMERATE_W,
NULL,
0,
&data,
sizeof(data),
&bytesReturned
);
IKsPropertySet_Release( pPropertySet );
}
pClassFactory->lpVtbl->Release( pClassFactory );
}
/*
The following code fragment, which I chose not to use, queries for the
device interface for a device with a specific GUID:
ULONG BytesReturned;
DSPROPERTY_DIRECTSOUNDDEVICE_DESCRIPTION_W_DATA Property;
memset (&Property, 0, sizeof(Property));
Property.DataFlow = DIRECTSOUNDDEVICE_DATAFLOW_RENDER;
Property.DeviceId = *lpGUID;
hr = IKsPropertySet_Get( pPropertySet,
&pawin_DSPROPSETID_DirectSoundDevice,
DSPROPERTY_DIRECTSOUNDDEVICE_DESCRIPTION_W,
NULL,
0,
&Property,
sizeof(Property),
&BytesReturned
);
if( hr == S_OK )
{
//pnpInterface = Property.Interface;
}
*/
}
#endif /* PAWIN_USE_WDMKS_DEVICE_INFO */
/*
GUIDs for emulated devices which we blacklist below.
@@ -418,7 +590,6 @@ static double defaultSampleRateSearchOrder_[] =
{ 44100.0, 48000.0, 32000.0, 24000.0, 22050.0, 88200.0, 96000.0, 192000.0,
16000.0, 12000.0, 11025.0, 9600.0, 8000.0 };
/************************************************************************************
** Extract capabilities from an output device, and add it to the device info list
** if successful. This function assumes that there is enough room in the
@@ -427,18 +598,18 @@ static double defaultSampleRateSearchOrder_[] =
** The device will not be added to the device list if any errors are encountered.
*/
static PaError AddOutputDeviceInfoFromDirectSound(
PaWinDsHostApiRepresentation *winDsHostApi, char *name, LPGUID lpGUID )
PaWinDsHostApiRepresentation *winDsHostApi, char *name, LPGUID lpGUID, char *pnpInterface )
{
PaUtilHostApiRepresentation *hostApi = &winDsHostApi->inheritedHostApiRep;
PaDeviceInfo *deviceInfo = hostApi->deviceInfos[hostApi->info.deviceCount];
PaWinDsDeviceInfo *winDsDeviceInfo = &winDsHostApi->winDsDeviceInfos[hostApi->info.deviceCount];
PaWinDsDeviceInfo *winDsDeviceInfo = (PaWinDsDeviceInfo*) hostApi->deviceInfos[hostApi->info.deviceCount];
PaDeviceInfo *deviceInfo = &winDsDeviceInfo->inheritedDeviceInfo;
HRESULT hr;
LPDIRECTSOUND lpDirectSound;
DSCAPS caps;
int deviceOK = TRUE;
PaError result = paNoError;
int i;
/* Copy GUID to the device info structure. Set pointer. */
if( lpGUID == NULL )
{
@@ -449,7 +620,6 @@ static PaError AddOutputDeviceInfoFromDirectSound(
memcpy( &winDsDeviceInfo->guid, lpGUID, sizeof(GUID) );
winDsDeviceInfo->lpGUID = &winDsDeviceInfo->guid;
}
if( lpGUID )
{
@@ -529,8 +699,39 @@ static PaError AddOutputDeviceInfoFromDirectSound(
if( deviceOK )
{
deviceInfo->maxInputChannels = 0;
/* Mono or stereo device? */
deviceInfo->maxOutputChannels = ( caps.dwFlags & DSCAPS_PRIMARYSTEREO ) ? 2 : 1;
winDsDeviceInfo->deviceInputChannelCountIsKnown = 1;
/* DS output capabilities only indicate supported number of channels
using two flags which indicate mono and/or stereo.
We assume that stereo devices may support more than 2 channels
(as is the case with 5.1 devices for example) and so
set deviceOutputChannelCountIsKnown to 0 (unknown).
In this case OpenStream will try to open the device
when the user requests more than 2 channels, rather than
returning an error.
*/
if( caps.dwFlags & DSCAPS_PRIMARYSTEREO )
{
deviceInfo->maxOutputChannels = 2;
winDsDeviceInfo->deviceOutputChannelCountIsKnown = 0;
}
else
{
deviceInfo->maxOutputChannels = 1;
winDsDeviceInfo->deviceOutputChannelCountIsKnown = 1;
}
#ifdef PAWIN_USE_WDMKS_DEVICE_INFO
if( pnpInterface )
{
int count = PaWin_WDMKS_QueryFilterMaximumChannelCount( pnpInterface, /* isInput= */ 0 );
if( count > 0 )
{
deviceInfo->maxOutputChannels = count;
winDsDeviceInfo->deviceOutputChannelCountIsKnown = 1;
}
}
#endif /* PAWIN_USE_WDMKS_DEVICE_INFO */
deviceInfo->defaultLowInputLatency = 0.; /** @todo IMPLEMENT ME */
deviceInfo->defaultLowOutputLatency = 0.; /** @todo IMPLEMENT ME */
@@ -547,8 +748,8 @@ static PaError AddOutputDeviceInfoFromDirectSound(
for( i = 0; i < PA_DEFAULTSAMPLERATESEARCHORDER_COUNT_; ++i )
{
if( defaultSampleRateSearchOrder_[i] >= caps.dwMinSecondarySampleRate
&& defaultSampleRateSearchOrder_[i] <= caps.dwMaxSecondarySampleRate ){
&& defaultSampleRateSearchOrder_[i] <= caps.dwMaxSecondarySampleRate )
{
deviceInfo->defaultSampleRate = defaultSampleRateSearchOrder_[i];
break;
}
@@ -614,11 +815,11 @@ static PaError AddOutputDeviceInfoFromDirectSound(
** The device will not be added to the device list if any errors are encountered.
*/
static PaError AddInputDeviceInfoFromDirectSoundCapture(
PaWinDsHostApiRepresentation *winDsHostApi, char *name, LPGUID lpGUID )
PaWinDsHostApiRepresentation *winDsHostApi, char *name, LPGUID lpGUID, char *pnpInterface )
{
PaUtilHostApiRepresentation *hostApi = &winDsHostApi->inheritedHostApiRep;
PaDeviceInfo *deviceInfo = hostApi->deviceInfos[hostApi->info.deviceCount];
PaWinDsDeviceInfo *winDsDeviceInfo = &winDsHostApi->winDsDeviceInfos[hostApi->info.deviceCount];
PaWinDsDeviceInfo *winDsDeviceInfo = (PaWinDsDeviceInfo*) hostApi->deviceInfos[hostApi->info.deviceCount];
PaDeviceInfo *deviceInfo = &winDsDeviceInfo->inheritedDeviceInfo;
HRESULT hr;
LPDIRECTSOUNDCAPTURE lpDirectSoundCapture;
DSCCAPS caps;
@@ -636,7 +837,6 @@ static PaError AddInputDeviceInfoFromDirectSoundCapture(
memcpy( &winDsDeviceInfo->guid, lpGUID, sizeof(GUID) );
}
hr = paWinDsDSoundEntryPoints.DirectSoundCaptureCreate( lpGUID, &lpDirectSoundCapture, NULL );
/** try using CoCreateInstance because DirectSoundCreate was hanging under
@@ -678,7 +878,22 @@ static PaError AddInputDeviceInfoFromDirectSoundCapture(
if( deviceOK )
{
deviceInfo->maxInputChannels = caps.dwChannels;
winDsDeviceInfo->deviceInputChannelCountIsKnown = 1;
deviceInfo->maxOutputChannels = 0;
winDsDeviceInfo->deviceOutputChannelCountIsKnown = 1;
#ifdef PAWIN_USE_WDMKS_DEVICE_INFO
if( pnpInterface )
{
int count = PaWin_WDMKS_QueryFilterMaximumChannelCount( pnpInterface, /* isInput= */ 1 );
if( count > 0 )
{
deviceInfo->maxInputChannels = count;
winDsDeviceInfo->deviceInputChannelCountIsKnown = 1;
}
}
#endif /* PAWIN_USE_WDMKS_DEVICE_INFO */
deviceInfo->defaultLowInputLatency = 0.; /** @todo IMPLEMENT ME */
deviceInfo->defaultLowOutputLatency = 0.; /** @todo IMPLEMENT ME */
@@ -770,17 +985,30 @@ PaError PaWinDs_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiInde
PaError result = paNoError;
int i, deviceCount;
PaWinDsHostApiRepresentation *winDsHostApi;
DSDeviceNameAndGUIDVector inputNamesAndGUIDs, outputNamesAndGUIDs;
PaDeviceInfo *deviceInfoArray;
DSDeviceNamesAndGUIDs deviceNamesAndGUIDs;
HRESULT hr = CoInitialize(NULL); /** @todo: should uninitialize too */
if( FAILED(hr) ){
PaWinDsDeviceInfo *deviceInfoArray;
char comWasInitialized = 0;
/*
If COM is already initialized CoInitialize will either return
FALSE, or RPC_E_CHANGED_MODE if it was initialised in a different
threading mode. In either case we shouldn't consider it an error
but we need to be careful to not call CoUninitialize() if
RPC_E_CHANGED_MODE was returned.
*/
HRESULT hr = CoInitialize(NULL);
if( FAILED(hr) && hr != RPC_E_CHANGED_MODE )
return paUnanticipatedHostError;
}
if( hr != RPC_E_CHANGED_MODE )
comWasInitialized = 1;
/* initialise guid vectors so they can be safely deleted on error */
inputNamesAndGUIDs.items = NULL;
outputNamesAndGUIDs.items = NULL;
deviceNamesAndGUIDs.winDsHostApi = NULL;
deviceNamesAndGUIDs.inputNamesAndGUIDs.items = NULL;
deviceNamesAndGUIDs.outputNamesAndGUIDs.items = NULL;
PaWinDs_InitializeDSoundEntryPoints();
@@ -791,6 +1019,8 @@ PaError PaWinDs_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiInde
goto error;
}
winDsHostApi->comWasInitialized = comWasInitialized;
winDsHostApi->allocations = PaUtil_CreateAllocationGroup();
if( !winDsHostApi->allocations )
{
@@ -810,32 +1040,39 @@ PaError PaWinDs_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiInde
/* DSound - enumerate devices to count them and to gather their GUIDs */
result = InitializeDSDeviceNameAndGUIDVector( &inputNamesAndGUIDs, winDsHostApi->allocations );
result = InitializeDSDeviceNameAndGUIDVector( &deviceNamesAndGUIDs.inputNamesAndGUIDs, winDsHostApi->allocations );
if( result != paNoError )
goto error;
result = InitializeDSDeviceNameAndGUIDVector( &outputNamesAndGUIDs, winDsHostApi->allocations );
result = InitializeDSDeviceNameAndGUIDVector( &deviceNamesAndGUIDs.outputNamesAndGUIDs, winDsHostApi->allocations );
if( result != paNoError )
goto error;
paWinDsDSoundEntryPoints.DirectSoundCaptureEnumerate( (LPDSENUMCALLBACK)CollectGUIDsProc, (void *)&inputNamesAndGUIDs );
paWinDsDSoundEntryPoints.DirectSoundCaptureEnumerate( (LPDSENUMCALLBACK)CollectGUIDsProc, (void *)&deviceNamesAndGUIDs.inputNamesAndGUIDs );
paWinDsDSoundEntryPoints.DirectSoundEnumerate( (LPDSENUMCALLBACK)CollectGUIDsProc, (void *)&outputNamesAndGUIDs );
paWinDsDSoundEntryPoints.DirectSoundEnumerate( (LPDSENUMCALLBACK)CollectGUIDsProc, (void *)&deviceNamesAndGUIDs.outputNamesAndGUIDs );
if( inputNamesAndGUIDs.enumerationError != paNoError )
if( deviceNamesAndGUIDs.inputNamesAndGUIDs.enumerationError != paNoError )
{
result = inputNamesAndGUIDs.enumerationError;
result = deviceNamesAndGUIDs.inputNamesAndGUIDs.enumerationError;
goto error;
}
if( outputNamesAndGUIDs.enumerationError != paNoError )
if( deviceNamesAndGUIDs.outputNamesAndGUIDs.enumerationError != paNoError )
{
result = outputNamesAndGUIDs.enumerationError;
result = deviceNamesAndGUIDs.outputNamesAndGUIDs.enumerationError;
goto error;
}
deviceCount = inputNamesAndGUIDs.count + outputNamesAndGUIDs.count;
deviceCount = deviceNamesAndGUIDs.inputNamesAndGUIDs.count + deviceNamesAndGUIDs.outputNamesAndGUIDs.count;
#ifdef PAWIN_USE_WDMKS_DEVICE_INFO
if( deviceCount > 0 )
{
deviceNamesAndGUIDs.winDsHostApi = winDsHostApi;
FindDevicePnpInterfaces( &deviceNamesAndGUIDs );
}
#endif /* PAWIN_USE_WDMKS_DEVICE_INFO */
if( deviceCount > 0 )
{
@@ -849,18 +1086,9 @@ PaError PaWinDs_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiInde
}
/* allocate all PaDeviceInfo structs in a contiguous block */
deviceInfoArray = (PaDeviceInfo*)PaUtil_GroupAllocateMemory(
winDsHostApi->allocations, sizeof(PaDeviceInfo) * deviceCount );
if( !deviceInfoArray )
{
result = paInsufficientMemory;
goto error;
}
/* allocate all DSound specific info structs in a contiguous block */
winDsHostApi->winDsDeviceInfos = (PaWinDsDeviceInfo*)PaUtil_GroupAllocateMemory(
deviceInfoArray = (PaWinDsDeviceInfo*)PaUtil_GroupAllocateMemory(
winDsHostApi->allocations, sizeof(PaWinDsDeviceInfo) * deviceCount );
if( !winDsHostApi->winDsDeviceInfos )
if( !deviceInfoArray )
{
result = paInsufficientMemory;
goto error;
@@ -868,37 +1096,39 @@ PaError PaWinDs_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiInde
for( i=0; i < deviceCount; ++i )
{
PaDeviceInfo *deviceInfo = &deviceInfoArray[i];
PaDeviceInfo *deviceInfo = &deviceInfoArray[i].inheritedDeviceInfo;
deviceInfo->structVersion = 2;
deviceInfo->hostApi = hostApiIndex;
deviceInfo->name = 0;
(*hostApi)->deviceInfos[i] = deviceInfo;
}
for( i=0; i< inputNamesAndGUIDs.count; ++i )
for( i=0; i < deviceNamesAndGUIDs.inputNamesAndGUIDs.count; ++i )
{
result = AddInputDeviceInfoFromDirectSoundCapture( winDsHostApi,
inputNamesAndGUIDs.items[i].name,
inputNamesAndGUIDs.items[i].lpGUID );
deviceNamesAndGUIDs.inputNamesAndGUIDs.items[i].name,
deviceNamesAndGUIDs.inputNamesAndGUIDs.items[i].lpGUID,
deviceNamesAndGUIDs.inputNamesAndGUIDs.items[i].pnpInterface );
if( result != paNoError )
goto error;
}
for( i=0; i< outputNamesAndGUIDs.count; ++i )
for( i=0; i < deviceNamesAndGUIDs.outputNamesAndGUIDs.count; ++i )
{
result = AddOutputDeviceInfoFromDirectSound( winDsHostApi,
outputNamesAndGUIDs.items[i].name,
outputNamesAndGUIDs.items[i].lpGUID );
deviceNamesAndGUIDs.outputNamesAndGUIDs.items[i].name,
deviceNamesAndGUIDs.outputNamesAndGUIDs.items[i].lpGUID,
deviceNamesAndGUIDs.outputNamesAndGUIDs.items[i].pnpInterface );
if( result != paNoError )
goto error;
}
}
result = TerminateDSDeviceNameAndGUIDVector( &inputNamesAndGUIDs );
result = TerminateDSDeviceNameAndGUIDVector( &deviceNamesAndGUIDs.inputNamesAndGUIDs );
if( result != paNoError )
goto error;
result = TerminateDSDeviceNameAndGUIDVector( &outputNamesAndGUIDs );
result = TerminateDSDeviceNameAndGUIDVector( &deviceNamesAndGUIDs.outputNamesAndGUIDs );
if( result != paNoError )
goto error;
@@ -932,8 +1162,11 @@ error:
PaUtil_FreeMemory( winDsHostApi );
}
TerminateDSDeviceNameAndGUIDVector( &inputNamesAndGUIDs );
TerminateDSDeviceNameAndGUIDVector( &outputNamesAndGUIDs );
TerminateDSDeviceNameAndGUIDVector( &deviceNamesAndGUIDs.inputNamesAndGUIDs );
TerminateDSDeviceNameAndGUIDVector( &deviceNamesAndGUIDs.outputNamesAndGUIDs );
if( comWasInitialized )
CoUninitialize();
return result;
}
@@ -943,6 +1176,7 @@ error:
static void Terminate( struct PaUtilHostApiRepresentation *hostApi )
{
PaWinDsHostApiRepresentation *winDsHostApi = (PaWinDsHostApiRepresentation*)hostApi;
char comWasInitialized = winDsHostApi->comWasInitialized;
/*
IMPLEMENT ME:
@@ -959,7 +1193,8 @@ static void Terminate( struct PaUtilHostApiRepresentation *hostApi )
PaWinDs_TerminateDSoundEntryPoints();
CoUninitialize();
if( comWasInitialized )
CoUninitialize();
}
@@ -994,17 +1229,40 @@ static int PaWinDS_GetMinSystemLatency( void )
return minLatencyMsec;
}
static PaError ValidateWinDirectSoundSpecificStreamInfo(
const PaStreamParameters *streamParameters,
const PaWinDirectSoundStreamInfo *streamInfo )
{
if( streamInfo )
{
if( streamInfo->size != sizeof( PaWinDirectSoundStreamInfo )
|| streamInfo->version != 1 )
{
return paIncompatibleHostApiSpecificStreamInfo;
}
}
return paNoError;
}
/***********************************************************************************/
static PaError IsFormatSupported( struct PaUtilHostApiRepresentation *hostApi,
const PaStreamParameters *inputParameters,
const PaStreamParameters *outputParameters,
double sampleRate )
{
PaError result;
PaWinDsDeviceInfo *inputWinDsDeviceInfo, *outputWinDsDeviceInfo;
PaDeviceInfo *inputDeviceInfo, *outputDeviceInfo;
int inputChannelCount, outputChannelCount;
PaSampleFormat inputSampleFormat, outputSampleFormat;
PaWinDirectSoundStreamInfo *inputStreamInfo, *outputStreamInfo;
if( inputParameters )
{
inputWinDsDeviceInfo = (PaWinDsDeviceInfo*) hostApi->deviceInfos[ inputParameters->device ];
inputDeviceInfo = &inputWinDsDeviceInfo->inheritedDeviceInfo;
inputChannelCount = inputParameters->channelCount;
inputSampleFormat = inputParameters->sampleFormat;
@@ -1015,12 +1273,14 @@ static PaError IsFormatSupported( struct PaUtilHostApiRepresentation *hostApi,
return paInvalidDevice;
/* check that input device can support inputChannelCount */
if( inputChannelCount > hostApi->deviceInfos[ inputParameters->device ]->maxInputChannels )
if( inputWinDsDeviceInfo->deviceInputChannelCountIsKnown
&& inputChannelCount > inputDeviceInfo->maxInputChannels )
return paInvalidChannelCount;
/* validate inputStreamInfo */
if( inputParameters->hostApiSpecificStreamInfo )
return paIncompatibleHostApiSpecificStreamInfo; /* this implementation doesn't use custom stream info */
inputStreamInfo = (PaWinDirectSoundStreamInfo*)inputParameters->hostApiSpecificStreamInfo;
result = ValidateWinDirectSoundSpecificStreamInfo( inputParameters, inputStreamInfo );
if( result != paNoError ) return result;
}
else
{
@@ -1029,6 +1289,9 @@ static PaError IsFormatSupported( struct PaUtilHostApiRepresentation *hostApi,
if( outputParameters )
{
outputWinDsDeviceInfo = (PaWinDsDeviceInfo*) hostApi->deviceInfos[ outputParameters->device ];
outputDeviceInfo = &outputWinDsDeviceInfo->inheritedDeviceInfo;
outputChannelCount = outputParameters->channelCount;
outputSampleFormat = outputParameters->sampleFormat;
@@ -1039,12 +1302,14 @@ static PaError IsFormatSupported( struct PaUtilHostApiRepresentation *hostApi,
return paInvalidDevice;
/* check that output device can support inputChannelCount */
if( outputChannelCount > hostApi->deviceInfos[ outputParameters->device ]->maxOutputChannels )
if( outputWinDsDeviceInfo->deviceOutputChannelCountIsKnown
&& outputChannelCount > outputDeviceInfo->maxOutputChannels )
return paInvalidChannelCount;
/* validate outputStreamInfo */
if( outputParameters->hostApiSpecificStreamInfo )
return paIncompatibleHostApiSpecificStreamInfo; /* this implementation doesn't use custom stream info */
outputStreamInfo = (PaWinDirectSoundStreamInfo*)outputParameters->hostApiSpecificStreamInfo;
result = ValidateWinDirectSoundSpecificStreamInfo( outputParameters, outputStreamInfo );
if( result != paNoError ) return result;
}
else
{
@@ -1115,22 +1380,14 @@ static int PaWinDs_GetMinLatencyFrames( double sampleRate )
}
static HRESULT InitInputBuffer( PaWinDsStream *stream, unsigned long nFrameRate, WORD nChannels, int bytesPerBuffer )
static HRESULT InitInputBuffer( PaWinDsStream *stream, PaSampleFormat sampleFormat, unsigned long nFrameRate, WORD nChannels, int bytesPerBuffer, PaWinWaveFormatChannelMask channelMask )
{
DSCBUFFERDESC captureDesc;
WAVEFORMATEX wfFormat;
PaWinWaveFormat waveFormat;
HRESULT result;
stream->bytesPerInputFrame = nChannels * sizeof(short);
stream->bytesPerInputFrame = nChannels * Pa_GetSampleSize(sampleFormat);
// Define the buffer format
wfFormat.wFormatTag = WAVE_FORMAT_PCM;
wfFormat.nChannels = nChannels;
wfFormat.nSamplesPerSec = nFrameRate;
wfFormat.wBitsPerSample = 8 * sizeof(short);
wfFormat.nBlockAlign = (WORD)(wfFormat.nChannels * (wfFormat.wBitsPerSample / 8));
wfFormat.nAvgBytesPerSec = wfFormat.nSamplesPerSec * wfFormat.nBlockAlign;
wfFormat.cbSize = 0; /* No extended format info. */
stream->inputSize = bytesPerBuffer;
// ----------------------------------------------------------------------
// Setup the secondary buffer description
@@ -1138,34 +1395,50 @@ static HRESULT InitInputBuffer( PaWinDsStream *stream, unsigned long nFrameRate,
captureDesc.dwSize = sizeof(DSCBUFFERDESC);
captureDesc.dwFlags = 0;
captureDesc.dwBufferBytes = bytesPerBuffer;
captureDesc.lpwfxFormat = &wfFormat;
captureDesc.lpwfxFormat = (WAVEFORMATEX*)&waveFormat;
// Create the capture buffer
if ((result = IDirectSoundCapture_CreateCaptureBuffer( stream->pDirectSoundCapture,
&captureDesc, &stream->pDirectSoundInputBuffer, NULL)) != DS_OK) return result;
// first try WAVEFORMATEXTENSIBLE. if this fails, fall back to WAVEFORMATEX
PaWin_InitializeWaveFormatExtensible( &waveFormat, nChannels,
sampleFormat, nFrameRate, channelMask );
if( IDirectSoundCapture_CreateCaptureBuffer( stream->pDirectSoundCapture,
&captureDesc, &stream->pDirectSoundInputBuffer, NULL) != DS_OK )
{
PaWin_InitializeWaveFormatEx( &waveFormat, nChannels, sampleFormat, nFrameRate );
if ((result = IDirectSoundCapture_CreateCaptureBuffer( stream->pDirectSoundCapture,
&captureDesc, &stream->pDirectSoundInputBuffer, NULL)) != DS_OK) return result;
}
stream->readOffset = 0; // reset last read position to start of buffer
return DS_OK;
}
static HRESULT InitOutputBuffer( PaWinDsStream *stream, unsigned long nFrameRate, WORD nChannels, int bytesPerBuffer )
static HRESULT InitOutputBuffer( PaWinDsStream *stream, PaSampleFormat sampleFormat, unsigned long nFrameRate, WORD nChannels, int bytesPerBuffer, PaWinWaveFormatChannelMask channelMask )
{
/** @todo FIXME: if InitOutputBuffer returns an error I'm not sure it frees all resources cleanly */
DWORD dwDataLen;
DWORD playCursor;
HRESULT result;
LPDIRECTSOUNDBUFFER pPrimaryBuffer;
HWND hWnd;
HRESULT hr;
WAVEFORMATEX wfFormat;
PaWinWaveFormat waveFormat;
DSBUFFERDESC primaryDesc;
DSBUFFERDESC secondaryDesc;
unsigned char* pDSBuffData;
LARGE_INTEGER counterFrequency;
int bytesPerSample = Pa_GetSampleSize(sampleFormat);
stream->outputBufferSizeBytes = bytesPerBuffer;
stream->outputIsRunning = FALSE;
stream->outputUnderflowCount = 0;
stream->dsw_framesWritten = 0;
stream->bytesPerOutputFrame = nChannels * sizeof(short);
stream->bytesPerOutputFrame = nChannels * bytesPerSample;
// We were using getForegroundWindow() but sometimes the ForegroundWindow may not be the
// applications's window. Also if that window is closed before the Buffer is closed
@@ -1199,16 +1472,19 @@ static HRESULT InitOutputBuffer( PaWinDsStream *stream, unsigned long nFrameRate
// Create the buffer
if ((result = IDirectSound_CreateSoundBuffer( stream->pDirectSound,
&primaryDesc, &pPrimaryBuffer, NULL)) != DS_OK) return result;
// Define the buffer format
wfFormat.wFormatTag = WAVE_FORMAT_PCM;
wfFormat.nChannels = nChannels;
wfFormat.nSamplesPerSec = nFrameRate;
wfFormat.wBitsPerSample = 8 * sizeof(short);
wfFormat.nBlockAlign = (WORD)(wfFormat.nChannels * (wfFormat.wBitsPerSample / 8));
wfFormat.nAvgBytesPerSec = wfFormat.nSamplesPerSec * wfFormat.nBlockAlign;
wfFormat.cbSize = 0; /* No extended format info. */
// Set the primary buffer's format
if((result = IDirectSoundBuffer_SetFormat( pPrimaryBuffer, &wfFormat)) != DS_OK) return result;
// first try WAVEFORMATEXTENSIBLE. if this fails, fall back to WAVEFORMATEX
PaWin_InitializeWaveFormatExtensible( &waveFormat, nChannels,
sampleFormat, nFrameRate, channelMask );
if( IDirectSoundBuffer_SetFormat( pPrimaryBuffer, (WAVEFORMATEX*)&waveFormat) != DS_OK )
{
PaWin_InitializeWaveFormatEx( &waveFormat, nChannels, sampleFormat, nFrameRate );
if((result = IDirectSoundBuffer_SetFormat( pPrimaryBuffer, (WAVEFORMATEX*)&waveFormat)) != DS_OK) return result;
}
// ----------------------------------------------------------------------
// Setup the secondary buffer description
@@ -1216,7 +1492,7 @@ static HRESULT InitOutputBuffer( PaWinDsStream *stream, unsigned long nFrameRate
secondaryDesc.dwSize = sizeof(DSBUFFERDESC);
secondaryDesc.dwFlags = DSBCAPS_GLOBALFOCUS | DSBCAPS_GETCURRENTPOSITION2;
secondaryDesc.dwBufferBytes = bytesPerBuffer;
secondaryDesc.lpwfxFormat = &wfFormat;
secondaryDesc.lpwfxFormat = (WAVEFORMATEX*)&waveFormat;
// Create the secondary buffer
if ((result = IDirectSound_CreateSoundBuffer( stream->pDirectSound,
&secondaryDesc, &stream->pDirectSoundOutputBuffer, NULL)) != DS_OK) return result;
@@ -1229,7 +1505,7 @@ static HRESULT InitOutputBuffer( PaWinDsStream *stream, unsigned long nFrameRate
if ((result = IDirectSoundBuffer_Unlock( stream->pDirectSoundOutputBuffer, pDSBuffData, dwDataLen, NULL, 0)) != DS_OK) return result;
if( QueryPerformanceFrequency( &counterFrequency ) )
{
int framesInBuffer = bytesPerBuffer / (nChannels * sizeof(short));
int framesInBuffer = bytesPerBuffer / (nChannels * bytesPerSample);
stream->perfCounterTicksPerBuffer.QuadPart = (counterFrequency.QuadPart * framesInBuffer) / nFrameRate;
}
else
@@ -1249,7 +1525,6 @@ static HRESULT InitOutputBuffer( PaWinDsStream *stream, unsigned long nFrameRate
return DS_OK;
}
/***********************************************************************************/
/* see pa_hostapi.h for a list of validity guarantees made about OpenStream parameters */
@@ -1266,13 +1541,20 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
PaError result = paNoError;
PaWinDsHostApiRepresentation *winDsHostApi = (PaWinDsHostApiRepresentation*)hostApi;
PaWinDsStream *stream = 0;
PaWinDsDeviceInfo *inputWinDsDeviceInfo, *outputWinDsDeviceInfo;
PaDeviceInfo *inputDeviceInfo, *outputDeviceInfo;
int inputChannelCount, outputChannelCount;
PaSampleFormat inputSampleFormat, outputSampleFormat;
PaSampleFormat hostInputSampleFormat, hostOutputSampleFormat;
unsigned long suggestedInputLatencyFrames, suggestedOutputLatencyFrames;
PaWinDirectSoundStreamInfo *inputStreamInfo, *outputStreamInfo;
PaWinWaveFormatChannelMask inputChannelMask, outputChannelMask;
if( inputParameters )
{
inputWinDsDeviceInfo = (PaWinDsDeviceInfo*) hostApi->deviceInfos[ inputParameters->device ];
inputDeviceInfo = &inputWinDsDeviceInfo->inheritedDeviceInfo;
inputChannelCount = inputParameters->channelCount;
inputSampleFormat = inputParameters->sampleFormat;
suggestedInputLatencyFrames = (unsigned long)(inputParameters->suggestedLatency * sampleRate);
@@ -1286,12 +1568,19 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
return paInvalidDevice;
/* check that input device can support inputChannelCount */
if( inputChannelCount > hostApi->deviceInfos[ inputParameters->device ]->maxInputChannels )
if( inputWinDsDeviceInfo->deviceInputChannelCountIsKnown
&& inputChannelCount > inputDeviceInfo->maxInputChannels )
return paInvalidChannelCount;
/* validate hostApiSpecificStreamInfo */
if( inputParameters->hostApiSpecificStreamInfo )
return paIncompatibleHostApiSpecificStreamInfo; /* this implementation doesn't use custom stream info */
inputStreamInfo = (PaWinDirectSoundStreamInfo*)inputParameters->hostApiSpecificStreamInfo;
result = ValidateWinDirectSoundSpecificStreamInfo( inputParameters, inputStreamInfo );
if( result != paNoError ) return result;
if( inputStreamInfo && inputStreamInfo->flags & paWinDirectSoundUseChannelMask )
inputChannelMask = inputStreamInfo->channelMask;
else
inputChannelMask = PaWin_DefaultChannelMask( inputChannelCount );
}
else
{
@@ -1303,6 +1592,9 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
if( outputParameters )
{
outputWinDsDeviceInfo = (PaWinDsDeviceInfo*) hostApi->deviceInfos[ outputParameters->device ];
outputDeviceInfo = &outputWinDsDeviceInfo->inheritedDeviceInfo;
outputChannelCount = outputParameters->channelCount;
outputSampleFormat = outputParameters->sampleFormat;
suggestedOutputLatencyFrames = (unsigned long)(outputParameters->suggestedLatency * sampleRate);
@@ -1312,13 +1604,20 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
if( outputParameters->device == paUseHostApiSpecificDeviceSpecification )
return paInvalidDevice;
/* check that output device can support inputChannelCount */
if( outputChannelCount > hostApi->deviceInfos[ outputParameters->device ]->maxOutputChannels )
/* check that output device can support outputChannelCount */
if( outputWinDsDeviceInfo->deviceOutputChannelCountIsKnown
&& outputChannelCount > outputDeviceInfo->maxOutputChannels )
return paInvalidChannelCount;
/* validate hostApiSpecificStreamInfo */
if( outputParameters->hostApiSpecificStreamInfo )
return paIncompatibleHostApiSpecificStreamInfo; /* this implementation doesn't use custom stream info */
outputStreamInfo = (PaWinDirectSoundStreamInfo*)outputParameters->hostApiSpecificStreamInfo;
result = ValidateWinDirectSoundSpecificStreamInfo( outputParameters, outputStreamInfo );
if( result != paNoError ) return result;
if( outputStreamInfo && outputStreamInfo->flags & paWinDirectSoundUseChannelMask )
outputChannelMask = outputStreamInfo->channelMask;
else
outputChannelMask = PaWin_DefaultChannelMask( outputChannelCount );
}
else
{
@@ -1384,8 +1683,11 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
if( inputParameters )
{
/* IMPLEMENT ME - establish which host formats are available */
PaSampleFormat nativeInputFormats = paInt16;
//PaSampleFormat nativeFormats = paUInt8 | paInt16 | paInt24 | paInt32 | paFloat32;
hostInputSampleFormat =
PaUtil_SelectClosestAvailableFormat( paInt16 /* native formats */, inputParameters->sampleFormat );
PaUtil_SelectClosestAvailableFormat( nativeInputFormats, inputParameters->sampleFormat );
}
else
{
@@ -1395,8 +1697,11 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
if( outputParameters )
{
/* IMPLEMENT ME - establish which host formats are available */
PaSampleFormat nativeOutputFormats = paInt16;
//PaSampleFormat nativeOutputFormats = paUInt8 | paInt16 | paInt24 | paInt32 | paFloat32;
hostOutputSampleFormat =
PaUtil_SelectClosestAvailableFormat( paInt16 /* native formats */, outputParameters->sampleFormat );
PaUtil_SelectClosestAvailableFormat( nativeOutputFormats, outputParameters->sampleFormat );
}
else
{
@@ -1474,7 +1779,8 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
DBUG(("PaHost_OpenStream: deviceID = 0x%x\n", outputParameters->device));
*/
bytesPerDirectSoundBuffer = stream->framesPerDSBuffer * outputParameters->channelCount * sizeof(short);
int bytesPerSample = Pa_GetSampleSize(hostOutputSampleFormat);
bytesPerDirectSoundBuffer = stream->framesPerDSBuffer * outputParameters->channelCount * bytesPerSample;
if( bytesPerDirectSoundBuffer < DSBSIZE_MIN )
{
result = paBufferTooSmall;
@@ -1486,9 +1792,10 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
goto error;
}
hr = paWinDsDSoundEntryPoints.DirectSoundCreate(
((PaWinDsDeviceInfo*)hostApi->deviceInfos[outputParameters->device])->lpGUID,
&stream->pDirectSound, NULL );
hr = paWinDsDSoundEntryPoints.DirectSoundCreate( winDsHostApi->winDsDeviceInfos[outputParameters->device].lpGUID,
&stream->pDirectSound, NULL );
if( hr != DS_OK )
{
ERR_RPT(("PortAudio: DirectSoundCreate() failed!\n"));
@@ -1497,8 +1804,10 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
goto error;
}
hr = InitOutputBuffer( stream,
hostOutputSampleFormat,
(unsigned long) (sampleRate + 0.5),
(WORD)outputParameters->channelCount, bytesPerDirectSoundBuffer );
(WORD)outputParameters->channelCount, bytesPerDirectSoundBuffer,
outputChannelMask );
DBUG(("InitOutputBuffer() returns %x\n", hr));
if( hr != DS_OK )
{
@@ -1520,7 +1829,8 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
DBUG(("PaHost_OpenStream: deviceID = 0x%x\n", inputParameters->device));
*/
bytesPerDirectSoundBuffer = stream->framesPerDSBuffer * inputParameters->channelCount * sizeof(short);
int bytesPerSample = Pa_GetSampleSize(hostInputSampleFormat);
bytesPerDirectSoundBuffer = stream->framesPerDSBuffer * inputParameters->channelCount * bytesPerSample;
if( bytesPerDirectSoundBuffer < DSBSIZE_MIN )
{
result = paBufferTooSmall;
@@ -1532,8 +1842,9 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
goto error;
}
hr = paWinDsDSoundEntryPoints.DirectSoundCaptureCreate( winDsHostApi->winDsDeviceInfos[inputParameters->device].lpGUID,
&stream->pDirectSoundCapture, NULL );
hr = paWinDsDSoundEntryPoints.DirectSoundCaptureCreate(
((PaWinDsDeviceInfo*)hostApi->deviceInfos[inputParameters->device])->lpGUID,
&stream->pDirectSoundCapture, NULL );
if( hr != DS_OK )
{
ERR_RPT(("PortAudio: DirectSoundCaptureCreate() failed!\n"));
@@ -1542,8 +1853,10 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
goto error;
}
hr = InitInputBuffer( stream,
hostInputSampleFormat,
(unsigned long) (sampleRate + 0.5),
(WORD)inputParameters->channelCount, bytesPerDirectSoundBuffer );
(WORD)inputParameters->channelCount, bytesPerDirectSoundBuffer,
inputChannelMask );
DBUG(("InitInputBuffer() returns %x\n", hr));
if( hr != DS_OK )
{
@@ -1885,7 +2198,8 @@ static void CALLBACK Pa_TimerCallback(UINT uID, UINT uMsg, DWORD_PTR dwUser, DWO
}
}
if( !stream->isActive ){
if( !stream->isActive )
{
if( stream->streamRepresentation.streamFinishedCallback != 0 )
stream->streamRepresentation.streamFinishedCallback( stream->streamRepresentation.userData );
}
@@ -49,6 +49,14 @@
PaWinDsDSoundEntryPoints paWinDsDSoundEntryPoints = { 0, 0, 0, 0, 0, 0, 0 };
static HRESULT WINAPI DummyDllGetClassObject(REFCLSID rclsid, REFIID riid, LPVOID *ppv)
{
(void)rclsid; /* unused parameter */
(void)riid; /* unused parameter */
(void)ppv; /* unused parameter */
return CLASS_E_CLASSNOTAVAILABLE;
}
static HRESULT WINAPI DummyDirectSoundCreate(LPGUID lpcGuidDevice, LPDIRECTSOUND *ppDS, LPUNKNOWN pUnkOuter)
{
(void)lpcGuidDevice; /* unused parameter */
@@ -99,6 +107,12 @@ void PaWinDs_InitializeDSoundEntryPoints(void)
paWinDsDSoundEntryPoints.hInstance_ = LoadLibrary("dsound.dll");
if( paWinDsDSoundEntryPoints.hInstance_ != NULL )
{
paWinDsDSoundEntryPoints.DllGetClassObject =
(HRESULT (WINAPI *)(REFCLSID, REFIID , LPVOID *))
GetProcAddress( paWinDsDSoundEntryPoints.hInstance_, "DllGetClassObject" );
if( paWinDsDSoundEntryPoints.DllGetClassObject == NULL )
paWinDsDSoundEntryPoints.DllGetClassObject = DummyDllGetClassObject;
paWinDsDSoundEntryPoints.DirectSoundCreate =
(HRESULT (WINAPI *)(LPGUID, LPDIRECTSOUND *, LPUNKNOWN))
GetProcAddress( paWinDsDSoundEntryPoints.hInstance_, "DirectSoundCreate" );
@@ -48,7 +48,7 @@
#define INCLUDED_PA_DSOUND_DYNLINK_H
/* on Borland compilers, WIN32 doesn't seem to be defined by default, which
breaks DSound.h. Adding the define here fixes the problem. - rossb. */
breaks dsound.h. Adding the define here fixes the problem. - rossb. */
#ifdef __BORLANDC__
#if !defined(WIN32)
#define WIN32
@@ -59,8 +59,7 @@
We are only using DX3 in here, no need to polute the namespace - davidv
*/
#define DIRECTSOUND_VERSION 0x0300
#include <DSound.h>
#include <dsound.h>
#ifdef __cplusplus
extern "C"
@@ -72,6 +71,8 @@ typedef struct
{
HINSTANCE hInstance_;
HRESULT (WINAPI *DllGetClassObject)(REFCLSID , REFIID , LPVOID *);
HRESULT (WINAPI *DirectSoundCreate)(LPGUID, LPDIRECTSOUND *, LPUNKNOWN);
HRESULT (WINAPI *DirectSoundEnumerateW)(LPDSENUMCALLBACKW, LPVOID);
HRESULT (WINAPI *DirectSoundEnumerateA)(LPDSENUMCALLBACKA, LPVOID);
+77 -51
View File
@@ -1,5 +1,5 @@
/*
* $Id: pa_jack.c 1151 2006-11-29 02:11:16Z leland_lucius $
* $Id: pa_jack.c 1306 2007-11-27 19:33:53Z aknudsen $
* PortAudio Portable Real-Time Audio Library
* Latest Version at: http://www.portaudio.com
* JACK Implementation by Joshua Haberman
@@ -32,19 +32,19 @@
*/
/*
* The text above constitutes the entire PortAudio license; however,
* The text above constitutes the entire PortAudio license; however,
* the PortAudio community also makes the following non-binding requests:
*
* 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. It is also
* requested that these non-binding requests be included along with the
* they can be incorporated into the canonical version. It is also
* requested that these non-binding requests be included along with the
* license above.
*/
/**
@file
@ingroup hostaip_src
@ingroup hostapi_src
*/
#include <string.h>
@@ -69,11 +69,13 @@
#include "pa_allocation.h"
#include "pa_cpuload.h"
#include "pa_ringbuffer.h"
#include "pa_debugprint.h"
static int aErr_;
static PaError paErr_; /* For use with ENSURE_PA */
static pthread_t mainThread_;
static char *jackErr_ = NULL;
static const char* clientName_ = "PortAudio";
#define STRINGIZE_HELPER(expr) #expr
#define STRINGIZE(expr) STRINGIZE_HELPER(expr)
@@ -381,9 +383,9 @@ static PaError BlockingWriteStream( PaStream* s, const void *data, unsigned long
bytesWritten = PaUtil_WriteRingBuffer( &stream->outFIFO, p, numBytes );
numBytes -= bytesWritten;
p += bytesWritten;
if( numBytes > 0 )
if( numBytes > 0 )
{
/* we use the following algorithm:
/* we use the following algorithm:
* (1) write data
* (2) if some data didn't fit into the ringbuffer, set data_available to 0
* to indicate to the audio that if space becomes available, we want to know
@@ -703,8 +705,7 @@ PaError PaJack_Initialize( PaUtilHostApiRepresentation **hostApi,
PaError result = paNoError;
PaJackHostApiRepresentation *jackHostApi;
int activated = 0;
char *clientName;
int written;
jack_status_t jackStatus = 0;
*hostApi = NULL; /* Initialize to NULL */
UNLESS( jackHostApi = (PaJackHostApiRepresentation*)
@@ -716,19 +717,21 @@ PaError PaJack_Initialize( PaUtilHostApiRepresentation **hostApi,
ASSERT_CALL( pthread_cond_init( &jackHostApi->cond, NULL ), 0 );
/* Try to become a client of the JACK server. If we cannot do
* this, then this API cannot be used. */
* this, then this API cannot be used.
*
* Without the JackNoStartServer option, the jackd server is started
* automatically which we do not want.
*/
clientName = PaUtil_GroupAllocateMemory( jackHostApi->deviceInfoMemory, jack_client_name_size() );
written = snprintf( clientName, jack_client_name_size(), "PortAudio-%d", getpid() );
assert( written < jack_client_name_size() );
jackHostApi->jack_client = jack_client_new( clientName );
if( jackHostApi->jack_client == NULL )
jackHostApi->jack_client = jack_client_open( clientName_, JackNoStartServer, &jackStatus );
if( !jackHostApi->jack_client )
{
/* the V19 development docs say that if an implementation
* detects that it cannot be used, it should return a NULL
* interface and paNoError */
result = paNoError;
goto error;
/* the V19 development docs say that if an implementation
* detects that it cannot be used, it should return a NULL
* interface and paNoError */
PA_DEBUG(( "%s: Couldn't connect to JACK, status: %d\n", __FUNCTION__, jackStatus ));
result = paNoError;
goto error;
}
jackHostApi->hostApiIndex = hostApiIndex;
@@ -739,7 +742,6 @@ PaError PaJack_Initialize( PaUtilHostApiRepresentation **hostApi,
(*hostApi)->info.name = "JACK Audio Connection Kit";
/* Build a device list by querying the JACK server */
ENSURE_PA( BuildDeviceList( jackHostApi ) );
/* Register functions */
@@ -772,7 +774,8 @@ PaError PaJack_Initialize( PaUtilHostApiRepresentation **hostApi,
jack_on_shutdown( jackHostApi->jack_client, JackOnShutdown, jackHostApi );
jack_set_error_function( JackErrorCallback );
jackHostApi->jack_buffer_size = jack_get_buffer_size ( jackHostApi->jack_client );
UNLESS( !jack_set_sample_rate_callback( jackHostApi->jack_client, JackSrCb, jackHostApi ), paUnanticipatedHostError );
/* Don't check for error, may not be supported (deprecated in at least jackdmp) */
jack_set_sample_rate_callback( jackHostApi->jack_client, JackSrCb, jackHostApi );
UNLESS( !jack_set_xrun_callback( jackHostApi->jack_client, JackXRunCb, jackHostApi ), paUnanticipatedHostError );
UNLESS( !jack_set_process_callback( jackHostApi->jack_client, JackCallback, jackHostApi ), paUnanticipatedHostError );
UNLESS( !jack_activate( jackHostApi->jack_client ), paUnanticipatedHostError );
@@ -883,7 +886,7 @@ static PaError IsFormatSupported( struct PaUtilHostApiRepresentation *hostApi,
/*
The following check is not necessary for JACK.
- if a full duplex stream is requested, check that the combination
of input and output parameters is supported
@@ -891,7 +894,7 @@ static PaError IsFormatSupported( struct PaUtilHostApiRepresentation *hostApi,
Because the buffer adapter handles conversion between all standard
sample formats, the following checks are only required if paCustomFormat
is implemented, or under some other unusual conditions.
- check that input device can support inputSampleFormat, or that
we have the capability to convert from outputSampleFormat to
a native format
@@ -902,7 +905,7 @@ static PaError IsFormatSupported( struct PaUtilHostApiRepresentation *hostApi,
*/
/* check that the device supports sampleRate */
#define ABS(x) ( (x) > 0 ? (x) : -(x) )
if( ABS(sampleRate - jack_get_sample_rate(((PaJackHostApiRepresentation *) hostApi)->jack_client )) > 1 )
return paInvalidSampleRate;
@@ -1052,7 +1055,7 @@ error:
return result;
}
/* Add stream to processing queue */
/* Add stream to JACK callback processing queue */
static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
PaStream** s,
const PaStreamParameters *inputParameters,
@@ -1224,15 +1227,15 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
if( inputChannelCount > 0 )
{
int err = 0;
/* ... remote output ports (that we input from) */
/* Get output ports of our capture device */
snprintf( regex_pattern, regexSz, "%s:.*", hostApi->deviceInfos[ inputParameters->device ]->name );
UNLESS( jack_ports = jack_get_ports( jackHostApi->jack_client, regex_pattern,
NULL, JackPortIsOutput ), paUnanticipatedHostError );
for( i = 0; i < inputChannelCount && jack_ports[i]; i++ )
{
if( (stream->remote_output_ports[i] = jack_port_by_name(
jackHostApi->jack_client, jack_ports[i] )) == NULL )
jackHostApi->jack_client, jack_ports[i] )) == NULL )
{
err = 1;
break;
@@ -1249,7 +1252,7 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
{
int err = 0;
/* ... remote input ports (that we output to) */
/* Get input ports of our playback device */
snprintf( regex_pattern, regexSz, "%s:.*", hostApi->deviceInfos[ outputParameters->device ]->name );
UNLESS( jack_ports = jack_get_ports( jackHostApi->jack_client, regex_pattern,
NULL, JackPortIsInput ), paUnanticipatedHostError );
@@ -1298,8 +1301,9 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
stream->streamRepresentation.streamInfo.sampleRate = jackSr;
stream->t0 = jack_frame_time( jackHostApi->jack_client ); /* A: Time should run from Pa_OpenStream */
ENSURE_PA( AddStream( stream ) ); /* Add to queue over opened streams */
/* Add to queue of opened streams */
ENSURE_PA( AddStream( stream ) );
*s = (PaStream*)stream;
return result;
@@ -1407,7 +1411,7 @@ end:
return result;
}
/* Alter the processing queue if necessary */
/* Update the JACK callback's stream processing queue. */
static PaError UpdateQueue( PaJackHostApiRepresentation *hostApi )
{
PaError result = paNoError;
@@ -1433,7 +1437,10 @@ static PaError UpdateQueue( PaJackHostApiRepresentation *hostApi )
node->next = hostApi->toAdd;
}
else
{
/* The only queue entry. */
hostApi->processQueue = (PaJackStream *)hostApi->toAdd;
}
/* If necessary, update stream state */
if( hostApi->toAdd->streamRepresentation.streamInfo.sampleRate != jackSr )
@@ -1482,6 +1489,7 @@ error:
return result;
}
/* Audio processing callback invoked periodically from JACK. */
static int JackCallback( jack_nframes_t frames, void *userData )
{
PaError result = paNoError;
@@ -1583,24 +1591,27 @@ static PaError StartStream( PaStream *s )
/* Ready the processor */
PaUtil_ResetBufferProcessor( &stream->bufferProcessor );
/* connect the ports */
/* Connect the ports. Note that the ports may already have been connected by someone else in
* the meantime, in which case JACK returns EEXIST. */
/* NOTE: I would rather use jack_port_connect which uses jack_port_t's
* instead of port names, but it is not implemented yet. */
if( stream->num_incoming_connections > 0 )
{
for( i = 0; i < stream->num_incoming_connections; i++ )
UNLESS( jack_connect( stream->jack_client,
jack_port_name( stream->remote_output_ports[i] ),
jack_port_name( stream->local_input_ports[i] ) ) == 0, paUnanticipatedHostError );
{
int r = jack_connect( stream->jack_client, jack_port_name( stream->remote_output_ports[i] ),
jack_port_name( stream->local_input_ports[i] ) );
UNLESS( 0 == r || EEXIST == r, paUnanticipatedHostError );
}
}
if( stream->num_outgoing_connections > 0 )
{
for( i = 0; i < stream->num_outgoing_connections; i++ )
UNLESS( jack_connect( stream->jack_client,
jack_port_name( stream->local_output_ports[i] ),
jack_port_name( stream->remote_input_ports[i] ) ) == 0, paUnanticipatedHostError );
{
int r = jack_connect( stream->jack_client, jack_port_name( stream->local_output_ports[i] ),
jack_port_name( stream->remote_input_ports[i] ) );
UNLESS( 0 == r || EEXIST == r, paUnanticipatedHostError );
}
}
stream->xrun = FALSE;
@@ -1654,7 +1665,7 @@ static PaError RealStop( PaJackStream *stream, int abort )
ENSURE_PA( result );
UNLESS( !stream->is_active, paInternalError );
PA_DEBUG(( "%s: Stream stopped\n", __FUNCTION__ ));
error:
@@ -1666,27 +1677,19 @@ error:
{
for( i = 0; i < stream->num_incoming_connections; i++ )
{
UNLESS( !jack_port_lock( stream->jack_client, stream->local_input_ports[i] ),
paUnanticipatedHostError );
if( jack_port_connected( stream->local_input_ports[i] ) )
{
UNLESS( !jack_port_disconnect( stream->jack_client, stream->local_input_ports[i] ),
paUnanticipatedHostError );
}
UNLESS( !jack_port_unlock( stream->jack_client, stream->local_input_ports[i] ),
paUnanticipatedHostError );
}
for( i = 0; i < stream->num_outgoing_connections; i++ )
{
UNLESS( !jack_port_lock( stream->jack_client, stream->local_output_ports[i] ),
paUnanticipatedHostError );
if( jack_port_connected( stream->local_output_ports[i] ) )
{
UNLESS( !jack_port_disconnect( stream->jack_client, stream->local_output_ports[i] ),
paUnanticipatedHostError );
}
UNLESS( !jack_port_unlock( stream->jack_client, stream->local_output_ports[i] ),
paUnanticipatedHostError );
}
}
@@ -1733,3 +1736,26 @@ static double GetStreamCpuLoad( PaStream* s )
PaJackStream *stream = (PaJackStream*)s;
return PaUtil_GetCpuLoad( &stream->cpuLoadMeasurer );
}
PaError PaJack_SetClientName( const char* name )
{
if( strlen( name ) > jack_client_name_size() )
{
/* OK, I don't know any better error code */
return paInvalidFlag;
}
clientName_ = name;
return paNoError;
}
PaError PaJack_GetClientName(const char** clientName)
{
PaError result = paNoError;
PaJackHostApiRepresentation* jackHostApi = NULL;
PaJackHostApiRepresentation** ref = &jackHostApi;
ENSURE_PA( PaUtil_GetHostApiRepresentation( (PaUtilHostApiRepresentation**)ref, paJACK ) );
*clientName = jack_get_client_name( jackHostApi->jack_client );
error:
return result;
}
+49 -38
View File
@@ -1,5 +1,5 @@
/*
* $Id: pa_unix_oss.c 1103 2006-08-29 20:56:04Z aknudsen $
* $Id: pa_unix_oss.c 1296 2007-10-28 22:43:50Z aknudsen $
* PortAudio Portable Real-Time Audio Library
* Latest Version at: http://www.portaudio.com
* OSS implementation by:
@@ -32,19 +32,19 @@
*/
/*
* The text above constitutes the entire PortAudio license; however,
* The text above constitutes the entire PortAudio license; however,
* the PortAudio community also makes the following non-binding requests:
*
* 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. It is also
* requested that these non-binding requests be included along with the
* they can be incorporated into the canonical version. It is also
* requested that these non-binding requests be included along with the
* license above.
*/
/**
@file
@ingroup hostaip_src
@ingroup hostapi_src
*/
#include <stdio.h>
@@ -84,6 +84,7 @@
#include "pa_cpuload.h"
#include "pa_process.h"
#include "pa_unix_util.h"
#include "pa_debugprint.h"
static int sysErr_;
static pthread_t mainThread_;
@@ -113,7 +114,7 @@ static pthread_t mainThread_;
*/
static int Get_AFMT_S16_NE( void )
{
long testData = 1;
long testData = 1;
char *ptr = (char *) &testData;
int isLittle = ( *ptr == 1 ); /* Does address point to least significant byte? */
return isLittle ? AFMT_S16_LE : AFMT_S16_BE;
@@ -278,7 +279,7 @@ error:
PaUtil_FreeAllAllocations( ossHostApi->allocations );
PaUtil_DestroyAllocationGroup( ossHostApi->allocations );
}
PaUtil_FreeMemory( ossHostApi );
}
return result;
@@ -289,7 +290,7 @@ PaError PaUtil_InitializeDeviceInfo( PaDeviceInfo *deviceInfo, const char *name,
PaTime defaultHighOutputLatency, double defaultSampleRate, PaUtilAllocationGroup *allocations )
{
PaError result = paNoError;
deviceInfo->structVersion = 2;
if( allocations )
{
@@ -384,7 +385,7 @@ static PaError QueryDirection( const char *deviceName, StreamMode mode, double *
{
maxNumChannels = (stereo) ? 2 : 1;
}
PA_DEBUG(( "%s: use SNDCTL_DSP_STEREO, maxNumChannels = %d\n", __FUNCTION__, maxNumChannels ))
PA_DEBUG(( "%s: use SNDCTL_DSP_STEREO, maxNumChannels = %d\n", __FUNCTION__, maxNumChannels ));
}
/* During channel negotiation, the last ioctl() may have failed. This can
@@ -440,7 +441,7 @@ static PaError QueryDevice( char *deviceName, PaOSSHostApiRepresentation *ossApi
/* douglas:
we have to do this querying in a slightly different order. apparently
some sound cards will give you different info based on their settins.
some sound cards will give you different info based on their settins.
e.g. a card might give you stereo at 22kHz but only mono at 44kHz.
the correct order for OSS is: format, channels, sample rate
*/
@@ -507,9 +508,9 @@ static PaError BuildDeviceList( PaOSSHostApiRepresentation *ossApi )
/* Find devices by calling QueryDevice on each
* potential device names. When we find a valid one,
* add it to a linked list.
* A: Can there only be 10 devices? */
* A: Set an arbitrary of 100 devices, should probably be a smarter way. */
for( i = 0; i < 10; i++ )
for( i = 0; i < 100; i++ )
{
char deviceName[32];
PaDeviceInfo *deviceInfo;
@@ -596,7 +597,7 @@ static PaError IsFormatSupported( struct PaUtilHostApiRepresentation *hostApi,
int tempDevHandle = -1;
int flags;
PaSampleFormat inputSampleFormat, outputSampleFormat;
if( inputParameters )
{
inputChannelCount = inputParameters->channelCount;
@@ -625,7 +626,7 @@ static PaError IsFormatSupported( struct PaUtilHostApiRepresentation *hostApi,
{
outputChannelCount = outputParameters->channelCount;
outputSampleFormat = outputParameters->sampleFormat;
/* unless alternate device specification is supported, reject the use of
paUseHostApiSpecificDeviceSpecification */
@@ -661,7 +662,7 @@ static PaError IsFormatSupported( struct PaUtilHostApiRepresentation *hostApi,
return paInvalidChannelCount;
/* open the device so we can do more tests */
if( inputChannelCount > 0 )
{
result = PaUtil_DeviceIndexToHostApiDeviceIndex(&device, inputParameters->device, hostApi);
@@ -677,7 +678,7 @@ static PaError IsFormatSupported( struct PaUtilHostApiRepresentation *hostApi,
deviceInfo = hostApi->deviceInfos[device];
deviceName = (char *)deviceInfo->name;
flags = O_NONBLOCK;
if (inputChannelCount > 0 && outputChannelCount > 0)
flags |= O_RDWR;
@@ -695,7 +696,7 @@ static PaError IsFormatSupported( struct PaUtilHostApiRepresentation *hostApi,
error:
if( tempDevHandle >= 0 )
close( tempDevHandle );
close( tempDevHandle );
return result;
}
@@ -882,7 +883,7 @@ static PaError PaOssStream_Initialize( PaOssStream *stream, const PaStreamParame
{
PaUtil_InitializeStreamRepresentation( &stream->streamRepresentation,
&ossApi->blockingStreamInterface, callback, userData );
}
}
ENSURE_( sem_init( &stream->semaphore, 0, 0 ), paInternalError );
@@ -948,7 +949,7 @@ static PaError GetAvailableFormats( PaOssStreamComponent *component, PaSampleFor
frmts |= paInt16;
else
result = paSampleFormatNotSupported;
*availableFormats = frmts;
error:
@@ -975,8 +976,8 @@ static int CalcHigherLogTwo( int n )
return log2;
}
static PaError PaOssStreamComponent_Configure( PaOssStreamComponent *component, double sampleRate, unsigned long framesPerBuffer,
StreamMode streamMode, PaOssStreamComponent *master )
static PaError PaOssStreamComponent_Configure( PaOssStreamComponent *component, double sampleRate, unsigned long
framesPerBuffer, StreamMode streamMode, PaOssStreamComponent *master )
{
PaError result = paNoError;
int temp, nativeFormat;
@@ -986,7 +987,7 @@ static PaError PaOssStreamComponent_Configure( PaOssStreamComponent *component,
int frgmt;
int numBufs;
int bytesPerBuf;
double bufSz;
unsigned long bufSz;
unsigned long fragSz;
audio_buf_info bufInfo;
@@ -998,21 +999,21 @@ static PaError PaOssStreamComponent_Configure( PaOssStreamComponent *component,
* The hardware need not respect the requested fragment size, so we may have to adapt.
*/
if( framesPerBuffer == paFramesPerBufferUnspecified )
{
bufSz = component->latency * sampleRate;
{
bufSz = (unsigned long)(component->latency * sampleRate);
fragSz = bufSz / 4;
}
else
{
fragSz = framesPerBuffer;
bufSz = component->latency * sampleRate + fragSz; /* Latency + 1 buffer */
bufSz = (unsigned long)(component->latency * sampleRate) + fragSz; /* Latency + 1 buffer */
}
PA_ENSURE( GetAvailableFormats( component, &availableFormats ) );
hostFormat = PaUtil_SelectClosestAvailableFormat( availableFormats, component->userFormat );
/* OSS demands at least 2 buffers, and 16 bytes per buffer */
numBufs = PA_MAX( bufSz / fragSz, 2 );
numBufs = (int)PA_MAX( bufSz / fragSz, 2 );
bytesPerBuf = PA_MAX( fragSz * Pa_GetSampleSize( hostFormat ) * chans, 16 );
/* The fragment parameters are encoded like this:
@@ -1042,7 +1043,7 @@ static PaError PaOssStreamComponent_Configure( PaOssStreamComponent *component,
/* reject if there's no sample rate within 1% of the one requested */
if( (fabs( sampleRate - sr ) / sampleRate) > 0.01 )
{
PA_DEBUG(("%s: Wanted %f, closest sample rate was %d\n", __FUNCTION__, sampleRate, sr ));
PA_DEBUG(("%s: Wanted %f, closest sample rate was %d\n", __FUNCTION__, sampleRate, sr ));
PA_ENSURE( paInvalidSampleRate );
}
@@ -1119,7 +1120,8 @@ static PaError PaOssStream_Configure( PaOssStream *stream, double sampleRate, un
if( stream->capture )
{
PaOssStreamComponent *component = stream->capture;
PaOssStreamComponent_Configure( component, sampleRate, framesPerBuffer, StreamMode_In, NULL );
PA_ENSURE( PaOssStreamComponent_Configure( component, sampleRate, framesPerBuffer, StreamMode_In,
NULL ) );
assert( component->hostChannelCount > 0 );
assert( component->hostFrames > 0 );
@@ -1187,6 +1189,7 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
const PaDeviceInfo *inputDeviceInfo = 0, *outputDeviceInfo = 0;
int bpInitialized = 0;
double inLatency = 0., outLatency = 0.;
int i = 0;
/* validate platform specific flags */
if( (streamFlags & paPlatformSpecificFlags) != 0 )
@@ -1222,7 +1225,15 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
return paInvalidChannelCount;
}
}
/* Round framesPerBuffer to the next power-of-two to make OSS happy. */
if( framesPerBuffer != paFramesPerBufferUnspecified )
{
framesPerBuffer &= INT_MAX;
for (i = 1; framesPerBuffer > i; i <<= 1) ;
framesPerBuffer = i;
}
/* allocate and do basic initialization of the stream structure */
PA_UNLESS( stream = (PaOssStream*)PaUtil_AllocateMemory( sizeof(PaOssStream) ), paInsufficientMemory );
PA_ENSURE( PaOssStream_Initialize( stream, inputParameters, outputParameters, streamCallback, userData, streamFlags, ossHostApi ) );
@@ -1230,7 +1241,7 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
PA_ENSURE( PaOssStream_Configure( stream, sampleRate, framesPerBuffer, &inLatency, &outLatency ) );
PaUtil_InitializeCpuLoadMeasurer( &stream->cpuLoadMeasurer, sampleRate );
if( inputParameters )
{
inputHostFormat = stream->capture->hostFormat;
@@ -1352,7 +1363,7 @@ static PaError PaOssStream_WaitForFrames( PaOssStream *stream, unsigned long *fr
pollCapture = 0;
}
*/
else if( stream->playback ) /* Timed out, go on with playback? */
else if( stream->playback ) /* Timed out, go on with playback? */
{
/*PA_DEBUG(( "%s: Trying to poll again for capture frames, pollTimeout: %d\n",
__FUNCTION__, stream->pollTimeout ));*/
@@ -1463,7 +1474,7 @@ static PaError PaOssStream_Prepare( PaOssStream *stream )
/* Ok, we have triggered the stream */
stream->triggered = 1;
error:
return result;
}
@@ -1502,7 +1513,7 @@ static void OnExit( void *data )
PaUtil_ResetCpuLoadMeasurer( &stream->cpuLoadMeasurer );
PaOssStream_Stop( stream, stream->callbackAbort );
PA_DEBUG(( "OnExit: Stoppage\n" ));
/* Eventually notify user all buffers have played */
@@ -1550,13 +1561,13 @@ static void *PaOSS_AudioThreadProc( void *userData )
int initiateProcessing = triggered; /* Already triggered? */
PaStreamCallbackFlags cbFlags = 0; /* We might want to keep state across iterations */
PaStreamCallbackTimeInfo timeInfo = {0,0,0}; /* TODO: IMPLEMENT ME */
/*
#if ( SOUND_VERSION > 0x030904 )
audio_errinfo errinfo;
#endif
*/
assert( stream );
pthread_cleanup_push( &OnExit, stream ); /* Execute OnExit when exiting */
@@ -1594,7 +1605,8 @@ static void *PaOSS_AudioThreadProc( void *userData )
*/
if( !initiateProcessing )
{
PA_ENSURE( PaOssStream_WaitForFrames( stream, &framesAvail ) ); /* Wait on available frames */
/* Wait on available frames */
PA_ENSURE( PaOssStream_WaitForFrames( stream, &framesAvail ) );
assert( framesAvail % stream->framesPerHostBuffer == 0 );
}
else
@@ -1937,8 +1949,7 @@ static signed long GetStreamWriteAvailable( PaStream* s )
if( ioctl( stream->playback->fd, SNDCTL_DSP_GETODELAY, &delay ) < 0 )
return paUnanticipatedHostError;
return (PaOssStreamComponent_BufferSize( stream->playback ) - delay) / PaOssStreamComponent_FrameSize( stream->playback );
}
@@ -1,6 +1,6 @@
/*
* Portable Audio I/O Library WASAPI implementation
* Copyright (c) 2006 David Viens
* Copyright (c) 2006-2007 David Viens
*
* Based on the Open Source API proposed by Ross Bencina
* Copyright (c) 1999-2002 Ross Bencina, Phil Burk
@@ -40,14 +40,9 @@
@ingroup hostaip_src
@brief WASAPI implementation of support for a host API.
@note This file is provided as a starting point for implementing support for
a new host API. IMPLEMENT ME comments are used to indicate functionality
which much be customised for each implementation.
@note pa_wasapi currently requires VC 2005, and the latest Vista SDK
*/
//these headers are only in Windows SDK CTP Feb 2006 and only work in VC 2005!
#if _MSC_VER >= 1400
#include <windows.h>
#include <MMReg.h> //must be before other Wasapi headers
@@ -55,18 +50,28 @@
#include <mmdeviceapi.h>
#include <Avrt.h>
#include <audioclient.h>
#include <Endpointvolume.h>
#include <KsMedia.h>
#include <functiondiscoverykeys.h> // PKEY_Device_FriendlyName
#endif
#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_debugprint.h"
/*
davidv : work in progress. try using with 48000 , then 44100
and shared mode FIRST.
*/
#define PORTAUDIO_SHAREMODE AUDCLNT_SHAREMODE_SHARED
//#define PORTAUDIO_SHAREMODE AUDCLNT_SHAREMODE_EXCLUSIVE
@@ -217,17 +222,18 @@ typedef struct PaWinWasapiStream
//input
PaWinWasapiSubStream in;
IAudioCaptureClient *cclient;
IAudioEndpointVolume *inVol;
//output
PaWinWasapiSubStream out;
IAudioRenderClient *rclient;
IAudioEndpointVolume *outVol;
bool running;
bool closeRequest;
DWORD dwThreadId;
HANDLE hThread;
HANDLE hNotificationEvent;
GUID session;
@@ -315,6 +321,8 @@ FAvRtWaitOnThreadOrderingGroup pAvRtWaitOnThreadOrderingGroup=0;
FAvSetMmThreadCharacteristics pAvSetMmThreadCharacteristics=0;
FAvSetMmThreadPriority pAvSetMmThreadPriority=0;
#define setupPTR(fun, type, name) { \
fun = (type) GetProcAddress(hDInputDLL,name); \
if(fun == NULL) { \
@@ -379,11 +387,6 @@ PaError PaWinWasapi_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApi
IMMDeviceCollection* spEndpoints=0;
paWasapi->enumerator = 0;
if (!setupAVRT()){
PRINT(("Windows WASAPI : No AVRT! (not VISTA?)"));
goto error;
}
hResult = CoCreateInstance(
__uuidof(MMDeviceEnumerator), NULL,CLSCTX_INPROC_SERVER,
__uuidof(IMMDeviceEnumerator),
@@ -452,6 +455,7 @@ PaError PaWinWasapi_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApi
for( UINT i=0; i < paWasapi->deviceCount; ++i ){
PA_DEBUG(("i:%d\n",i));
PaDeviceInfo *deviceInfo = &deviceInfoArray[i];
deviceInfo->structVersion = 2;
deviceInfo->hostApi = hostApiIndex;
@@ -585,7 +589,14 @@ PaError PaWinWasapi_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApi
hResult = myClient->GetMixFormat(&paWasapi->devInfo[i].MixFormat);
IF_FAILED_JUMP(hResult, error);
if (hResult != S_OK){
/*davidv: this happened with my hardware, previously for that same device in DirectSound:
Digital Output (Realtek AC'97 Audio)'s GUID: {0x38f2cf50,0x7b4c,0x4740,0x86,0xeb,0xd4,0x38,0x66,0xd8,0xc8, 0x9f}
so something must be _really_ wrong with this device, TODO handle this better. We kind of need GetMixFormat*/
logAUDCLNT_E(hResult);
goto error;
}
myClient->Release();
}
@@ -690,37 +701,37 @@ static void Terminate( struct PaUtilHostApiRepresentation *hostApi )
}
static void
LogWAVEFORMATEXTENSIBLE(const WAVEFORMATEXTENSIBLE &in){
LogWAVEFORMATEXTENSIBLE(const WAVEFORMATEXTENSIBLE *in){
const WAVEFORMATEX *old = (WAVEFORMATEX *)&in;
const WAVEFORMATEX *old = (WAVEFORMATEX *)in;
switch (old->wFormatTag){
case WAVE_FORMAT_EXTENSIBLE:{
PRINT(("wFormatTag=WAVE_FORMAT_EXTENSIBLE\n"));
if (in.SubFormat == KSDATAFORMAT_SUBTYPE_IEEE_FLOAT){
if (in->SubFormat == KSDATAFORMAT_SUBTYPE_IEEE_FLOAT){
PRINT(("SubFormat=KSDATAFORMAT_SUBTYPE_IEEE_FLOAT\n"));
}
else if (in.SubFormat == KSDATAFORMAT_SUBTYPE_PCM){
else if (in->SubFormat == KSDATAFORMAT_SUBTYPE_PCM){
PRINT(("SubFormat=KSDATAFORMAT_SUBTYPE_PCM\n"));
}
else{
PRINT(("SubFormat=CUSTOM GUID{%d:%d:%d:%d%d%d%d%d%d%d%d}\n",
in.SubFormat.Data1,
in.SubFormat.Data2,
in.SubFormat.Data3,
(int)in.SubFormat.Data4[0],
(int)in.SubFormat.Data4[1],
(int)in.SubFormat.Data4[2],
(int)in.SubFormat.Data4[3],
(int)in.SubFormat.Data4[4],
(int)in.SubFormat.Data4[5],
(int)in.SubFormat.Data4[6],
(int)in.SubFormat.Data4[7]));
in->SubFormat.Data1,
in->SubFormat.Data2,
in->SubFormat.Data3,
(int)in->SubFormat.Data4[0],
(int)in->SubFormat.Data4[1],
(int)in->SubFormat.Data4[2],
(int)in->SubFormat.Data4[3],
(int)in->SubFormat.Data4[4],
(int)in->SubFormat.Data4[5],
(int)in->SubFormat.Data4[6],
(int)in->SubFormat.Data4[7]));
}
PRINT(("Samples.wValidBitsPerSample=%d\n", in.Samples.wValidBitsPerSample));
PRINT(("dwChannelMask=0x%X\n",in.dwChannelMask));
PRINT(("Samples.wValidBitsPerSample=%d\n", in->Samples.wValidBitsPerSample));
PRINT(("dwChannelMask=0x%X\n",in->dwChannelMask));
}break;
case WAVE_FORMAT_PCM: PRINT(("wFormatTag=WAVE_FORMAT_PCM\n")); break;
@@ -742,21 +753,21 @@ LogWAVEFORMATEXTENSIBLE(const WAVEFORMATEXTENSIBLE &in){
WAVEFORMATXXX is always interleaved
*/
static PaSampleFormat
waveformatToPaFormat(const WAVEFORMATEXTENSIBLE &in){
waveformatToPaFormat(const WAVEFORMATEXTENSIBLE *in){
const WAVEFORMATEX *old = (WAVEFORMATEX *)&in;
const WAVEFORMATEX *old = (WAVEFORMATEX*)in;
switch (old->wFormatTag){
case WAVE_FORMAT_EXTENSIBLE:
{
if (in.SubFormat == KSDATAFORMAT_SUBTYPE_IEEE_FLOAT){
if (in.Samples.wValidBitsPerSample == 32)
if (in->SubFormat == KSDATAFORMAT_SUBTYPE_IEEE_FLOAT){
if (in->Samples.wValidBitsPerSample == 32)
return paFloat32;
else
return paCustomFormat;
}
else if (in.SubFormat == KSDATAFORMAT_SUBTYPE_PCM){
else if (in->SubFormat == KSDATAFORMAT_SUBTYPE_PCM){
switch (old->wBitsPerSample){
case 32: return paInt32; break;
case 24: return paInt24;break;
@@ -797,7 +808,7 @@ waveformatToPaFormat(const WAVEFORMATEXTENSIBLE &in){
static PaError
waveformatFromParams(WAVEFORMATEXTENSIBLE &wav,
waveformatFromParams(WAVEFORMATEXTENSIBLE*wavex,
const PaStreamParameters * params,
double sampleRate){
@@ -813,13 +824,13 @@ waveformatFromParams(WAVEFORMATEXTENSIBLE &wav,
default: return paSampleFormatNotSupported;break;
}
memset(&wav,0,sizeof(WAVEFORMATEXTENSIBLE));
memset(wavex,0,sizeof(WAVEFORMATEXTENSIBLE));
WAVEFORMATEX *old = (WAVEFORMATEX *)&wav;
WAVEFORMATEX *old = (WAVEFORMATEX *)wavex;
old->nChannels = (WORD)params->channelCount;
old->nSamplesPerSec = (DWORD)sampleRate;
old->wBitsPerSample = bytesPerSample*8;
old->nAvgBytesPerSec = old->nSamplesPerSec * old->nChannels * bytesPerSample;
old->wBitsPerSample = (WORD)(bytesPerSample*8);
old->nAvgBytesPerSec = (DWORD)(old->nSamplesPerSec * old->nChannels * bytesPerSample);
old->nBlockAlign = (WORD)(old->nChannels * bytesPerSample);
//WAVEFORMATEX
@@ -834,19 +845,19 @@ waveformatFromParams(WAVEFORMATEXTENSIBLE &wav,
old->cbSize = sizeof (WAVEFORMATEXTENSIBLE) - sizeof (WAVEFORMATEX);
if ((params->sampleFormat & ~paNonInterleaved) == paFloat32)
wav.SubFormat = KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
wavex->SubFormat = KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
else
wav.SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
wavex->SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
wav.Samples.wValidBitsPerSample = old->wBitsPerSample; //no extra padding!
wavex->Samples.wValidBitsPerSample = old->wBitsPerSample; //no extra padding!
switch(params->channelCount){
case 1: wav.dwChannelMask = SPEAKER_FRONT_CENTER; break;
case 2: wav.dwChannelMask = 0x1 | 0x2; break;
case 4: wav.dwChannelMask = 0x1 | 0x2 | 0x10 | 0x20; break;
case 6: wav.dwChannelMask = 0x1 | 0x2 | 0x4 | 0x8 | 0x10 | 0x20; break;
case 8: wav.dwChannelMask = 0x1 | 0x2 | 0x4 | 0x8 | 0x10 | 0x20 | 0x40 | 0x80; break;
default: wav.dwChannelMask = 0; break;
case 1: wavex->dwChannelMask = SPEAKER_FRONT_CENTER; break;
case 2: wavex->dwChannelMask = 0x1 | 0x2; break;
case 4: wavex->dwChannelMask = 0x1 | 0x2 | 0x10 | 0x20; break;
case 6: wavex->dwChannelMask = 0x1 | 0x2 | 0x4 | 0x8 | 0x10 | 0x20; break;
case 8: wavex->dwChannelMask = 0x1 | 0x2 | 0x4 | 0x8 | 0x10 | 0x20 | 0x40 | 0x80; break;
default: wavex->dwChannelMask = 0; break;
}
}
@@ -854,36 +865,160 @@ waveformatFromParams(WAVEFORMATEXTENSIBLE &wav,
}
enum PaWasapiFormatAnswer {PWFA_OK,PWFA_NO,PWFA_SUGGESTED};
static PaWasapiFormatAnswer
IsFormatSupportedInternal(IAudioClient * myClient, WAVEFORMATEXTENSIBLE &wavex){
PaWasapiFormatAnswer answer = PWFA_OK;
WAVEFORMATEX *closestMatch=0;
HRESULT hResult = myClient->IsFormatSupported(
//AUDCLNT_SHAREMODE_EXCLUSIVE,
AUDCLNT_SHAREMODE_SHARED,
(WAVEFORMATEX*)&wavex,&closestMatch);
/*
#define paFloat32 ((PaSampleFormat) 0x00000001)
#define paInt32 ((PaSampleFormat) 0x00000002)
#define paInt24 ((PaSampleFormat) 0x00000004)
#define paInt16 ((PaSampleFormat) 0x00000008)
*/
//lifted from pa_wdmks
static void wasapiFillWFEXT( WAVEFORMATEXTENSIBLE* pwfext, PaSampleFormat sampleFormat, double sampleRate, int channelCount)
{
PA_DEBUG(( "sampleFormat = %lx\n" , sampleFormat ));
PA_DEBUG(( "sampleRate = %f\n" , sampleRate ));
PA_DEBUG(( "chanelCount = %d\n", channelCount ));
pwfext->Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
pwfext->Format.nChannels = channelCount;
pwfext->Format.nSamplesPerSec = (int)sampleRate;
if(channelCount == 1)
pwfext->dwChannelMask = KSAUDIO_SPEAKER_DIRECTOUT;
else
pwfext->dwChannelMask = KSAUDIO_SPEAKER_STEREO;
if(sampleFormat == paFloat32)
{
pwfext->Format.nBlockAlign = channelCount * 4;
pwfext->Format.wBitsPerSample = 32;
pwfext->Format.cbSize = sizeof(WAVEFORMATEXTENSIBLE)-sizeof(WAVEFORMATEX);
pwfext->Samples.wValidBitsPerSample = 32;
pwfext->SubFormat = KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
}
else if(sampleFormat == paInt32)
{
pwfext->Format.nBlockAlign = channelCount * 4;
pwfext->Format.wBitsPerSample = 32;
pwfext->Format.cbSize = sizeof(WAVEFORMATEXTENSIBLE)-sizeof(WAVEFORMATEX);
pwfext->Samples.wValidBitsPerSample = 32;
pwfext->SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
}
else if(sampleFormat == paInt24)
{
pwfext->Format.nBlockAlign = channelCount * 3;
pwfext->Format.wBitsPerSample = 24;
pwfext->Format.cbSize = sizeof(WAVEFORMATEXTENSIBLE)-sizeof(WAVEFORMATEX);
pwfext->Samples.wValidBitsPerSample = 24;
pwfext->SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
}
else if(sampleFormat == paInt16)
{
pwfext->Format.nBlockAlign = channelCount * 2;
pwfext->Format.wBitsPerSample = 16;
pwfext->Format.cbSize = sizeof(WAVEFORMATEXTENSIBLE)-sizeof(WAVEFORMATEX);
pwfext->Samples.wValidBitsPerSample = 16;
pwfext->SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
}
pwfext->Format.nAvgBytesPerSec = pwfext->Format.nSamplesPerSec * pwfext->Format.nBlockAlign;
}
/*
#define FORMATTESTS 4
const int BestToWorst[FORMATTESTS]={paFloat32,paInt32,paInt24,paInt16};
*/
#define FORMATTESTS 3
const int BestToWorst[FORMATTESTS]={paFloat32,paInt24,paInt16};
static PaError
GetClosestFormat(IAudioClient * myClient, double sampleRate,const PaStreamParameters * params,
AUDCLNT_SHAREMODE *shareMode, WAVEFORMATEXTENSIBLE *outWavex)
{
//TODO we should try exclusive first and shared after
*shareMode = PORTAUDIO_SHAREMODE;
PaError answer = paInvalidSampleRate;
waveformatFromParams(outWavex,params,sampleRate);
WAVEFORMATEX *sharedClosestMatch=0;
HRESULT hResult=!S_OK;
if (*shareMode == AUDCLNT_SHAREMODE_EXCLUSIVE)
hResult = myClient->IsFormatSupported(AUDCLNT_SHAREMODE_EXCLUSIVE,(WAVEFORMATEX*)outWavex,NULL);
else
hResult = myClient->IsFormatSupported(AUDCLNT_SHAREMODE_SHARED, (WAVEFORMATEX*)&outWavex,&sharedClosestMatch);
if (hResult == S_OK)
answer = PWFA_OK;
else if (closestMatch){
WAVEFORMATEXTENSIBLE* ext = (WAVEFORMATEXTENSIBLE*)closestMatch;
answer = paFormatIsSupported;
else if (sharedClosestMatch){
WAVEFORMATEXTENSIBLE* ext = (WAVEFORMATEXTENSIBLE*)sharedClosestMatch;
if (closestMatch->wFormatTag == WAVE_FORMAT_EXTENSIBLE)
memcpy(&wavex,closestMatch,sizeof(WAVEFORMATEXTENSIBLE));
else
memcpy(&wavex,closestMatch,sizeof(WAVEFORMATEX));
int closestMatchSR = (int)sharedClosestMatch->nSamplesPerSec;
CoTaskMemFree(closestMatch);
answer = PWFA_SUGGESTED;
if (sharedClosestMatch->wFormatTag == WAVE_FORMAT_EXTENSIBLE)
memcpy(outWavex,sharedClosestMatch,sizeof(WAVEFORMATEXTENSIBLE));
else
memcpy(outWavex,sharedClosestMatch,sizeof(WAVEFORMATEX));
CoTaskMemFree(sharedClosestMatch);
if ((int)sampleRate == closestMatchSR)
answer = paFormatIsSupported;
else
answer = paInvalidSampleRate;
}else if (hResult != S_OK){
}else {
//it doesnt suggest anything?? ok lets show it the MENU!
//ok fun time as with pa_win_mme, we know only a refusal of the user-requested
//sampleRate+num Channel is disastrous, as the portaudio buffer processor converts between anything
//so lets only use the number
for (int i=0;i<FORMATTESTS;++i){
WAVEFORMATEXTENSIBLE ext;
wasapiFillWFEXT(&ext,BestToWorst[i],sampleRate,params->channelCount);
if (*shareMode == AUDCLNT_SHAREMODE_EXCLUSIVE)
hResult = myClient->IsFormatSupported(AUDCLNT_SHAREMODE_EXCLUSIVE,(WAVEFORMATEX*)&ext,NULL);
else
hResult = myClient->IsFormatSupported(AUDCLNT_SHAREMODE_SHARED, (WAVEFORMATEX*)&ext,&sharedClosestMatch);
if (hResult == S_OK){
memcpy(outWavex,&ext,sizeof(WAVEFORMATEXTENSIBLE));
answer = paFormatIsSupported;
break;
}
}
if (answer!=paFormatIsSupported) {
//try MIX format?
//why did it HAVE to come to this ....
WAVEFORMATEX pcm16WaveFormat;
memset(&pcm16WaveFormat,0,sizeof(WAVEFORMATEX));
pcm16WaveFormat.wFormatTag = WAVE_FORMAT_PCM;
pcm16WaveFormat.nChannels = 2;
pcm16WaveFormat.nSamplesPerSec = (DWORD)sampleRate;
pcm16WaveFormat.nBlockAlign = 4;
pcm16WaveFormat.nAvgBytesPerSec = pcm16WaveFormat.nSamplesPerSec*pcm16WaveFormat.nBlockAlign;
pcm16WaveFormat.wBitsPerSample = 16;
pcm16WaveFormat.cbSize = 0;
if (*shareMode == AUDCLNT_SHAREMODE_EXCLUSIVE)
hResult = myClient->IsFormatSupported(AUDCLNT_SHAREMODE_EXCLUSIVE,(WAVEFORMATEX*)&pcm16WaveFormat,NULL);
else
hResult = myClient->IsFormatSupported(AUDCLNT_SHAREMODE_SHARED, (WAVEFORMATEX*)&pcm16WaveFormat,&sharedClosestMatch);
if (hResult == S_OK){
memcpy(outWavex,&pcm16WaveFormat,sizeof(WAVEFORMATEX));
answer = paFormatIsSupported;
}
}
logAUDCLNT_E(hResult);
answer = PWFA_NO;
}
return answer;
@@ -891,10 +1026,11 @@ IsFormatSupportedInternal(IAudioClient * myClient, WAVEFORMATEXTENSIBLE &wavex){
static PaError IsFormatSupported( struct PaUtilHostApiRepresentation *hostApi,
const PaStreamParameters *inputParameters,
const PaStreamParameters *outputParameters,
const PaStreamParameters *inputParameters,
const PaStreamParameters *outputParameters,
double sampleRate )
{
int inputChannelCount, outputChannelCount;
PaSampleFormat inputSampleFormat, outputSampleFormat;
@@ -925,9 +1061,7 @@ static PaError IsFormatSupported( struct PaUtilHostApiRepresentation *hostApi,
PaWinWasapiHostApiRepresentation *paWasapi = (PaWinWasapiHostApiRepresentation*)hostApi;
WAVEFORMATEXTENSIBLE wavex;
waveformatFromParams(wavex,inputParameters,sampleRate);
IAudioClient *myClient=0;
HRESULT hResult = paWasapi->devInfo[inputParameters->device].device->Activate(
__uuidof(IAudioClient), CLSCTX_INPROC_SERVER, NULL, (void**)&myClient);
@@ -936,26 +1070,13 @@ static PaError IsFormatSupported( struct PaUtilHostApiRepresentation *hostApi,
return paInvalidDevice;
}
PaWasapiFormatAnswer answer = IsFormatSupportedInternal(myClient,wavex);
WAVEFORMATEXTENSIBLE wavex;
AUDCLNT_SHAREMODE shareMode;
PaError answer = GetClosestFormat(myClient,sampleRate,inputParameters,&shareMode,&wavex);
myClient->Release();
switch (answer){
case PWFA_OK: break;
case PWFA_NO: return paSampleFormatNotSupported;
case PWFA_SUGGESTED:
{
PRINT(("Suggested format:"));
LogWAVEFORMATEXTENSIBLE(wavex);
if (wavex.Format.nSamplesPerSec == (DWORD)sampleRate){
//no problem its a format issue only
}
else{
return paInvalidSampleRate;
}
}
}
if (answer !=paFormatIsSupported)
return answer;
}
else
{
@@ -989,9 +1110,6 @@ static PaError IsFormatSupported( struct PaUtilHostApiRepresentation *hostApi,
PaWinWasapiHostApiRepresentation *paWasapi = (PaWinWasapiHostApiRepresentation*)hostApi;
WAVEFORMATEXTENSIBLE wavex;
waveformatFromParams(wavex,outputParameters,sampleRate);
IAudioClient *myClient=0;
HRESULT hResult = paWasapi->devInfo[outputParameters->device].device->Activate(
__uuidof(IAudioClient), CLSCTX_INPROC_SERVER, NULL, (void**)&myClient);
@@ -1000,26 +1118,13 @@ static PaError IsFormatSupported( struct PaUtilHostApiRepresentation *hostApi,
return paInvalidDevice;
}
PaWasapiFormatAnswer answer = IsFormatSupportedInternal(myClient,wavex);
WAVEFORMATEXTENSIBLE wavex;
AUDCLNT_SHAREMODE shareMode;
PaError answer = GetClosestFormat(myClient,sampleRate,outputParameters,&shareMode,&wavex);
myClient->Release();
switch (answer){
case PWFA_OK: break;
case PWFA_NO: return paSampleFormatNotSupported;
case PWFA_SUGGESTED:
{
PRINT(("Suggested format:"));
LogWAVEFORMATEXTENSIBLE(wavex);
if (wavex.Format.nSamplesPerSec == (DWORD)sampleRate){
//no problem its a format issue only
}
else{
return paInvalidSampleRate;
}
}
}
if (answer !=paFormatIsSupported)
return answer;
}
else
{
@@ -1087,35 +1192,26 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
if (hResult != S_OK)
return paInvalidDevice;
waveformatFromParams(stream->in.wavex,outputParameters,sampleRate);
PaWasapiFormatAnswer answer = IsFormatSupportedInternal(stream->in.client,
stream->in.wavex);
hResult = info.device->Activate(
__uuidof(IAudioEndpointVolume), CLSCTX_INPROC_SERVER, NULL,
(void**)&stream->inVol);
switch (answer){
case PWFA_OK: break;
case PWFA_NO: return paSampleFormatNotSupported;
case PWFA_SUGGESTED:
{
PRINT(("Suggested format:"));
LogWAVEFORMATEXTENSIBLE(stream->in.wavex);
if (stream->in.wavex.Format.nSamplesPerSec == (DWORD)sampleRate){
//no problem its a format issue only
}
else{
return paInvalidSampleRate;
}
}
}
if (hResult != S_OK)
return paInvalidDevice;
AUDCLNT_SHAREMODE shareMode;
PaError answer = GetClosestFormat(stream->in.client,sampleRate,inputParameters,&shareMode,&stream->in.wavex);
if (answer !=paFormatIsSupported)
return answer;
//stream->out.period = info.DefaultDevicePeriod;
stream->in.period = info.MinimumDevicePeriod;
hResult = stream->in.client->Initialize(
AUDCLNT_SHAREMODE_SHARED,
//AUDCLNT_SHAREMODE_EXCLUSIVE,
shareMode,
0, //no flags
stream->in.period*3, //tripple buffer
stream->in.period,
0,//stream->out.period,
(WAVEFORMATEX*)&stream->in.wavex,
&stream->session
@@ -1142,7 +1238,7 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
/* IMPLEMENT ME - establish which host formats are available */
hostInputSampleFormat =
PaUtil_SelectClosestAvailableFormat( waveformatToPaFormat(stream->in.wavex), inputSampleFormat );
PaUtil_SelectClosestAvailableFormat( waveformatToPaFormat(&stream->in.wavex), inputSampleFormat );
}
else
{
@@ -1179,45 +1275,53 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
if (hResult != S_OK)
return paInvalidDevice;
waveformatFromParams(stream->out.wavex,outputParameters,sampleRate);
AUDCLNT_SHAREMODE shareMode;
PaError answer = GetClosestFormat(stream->out.client,sampleRate,outputParameters,&shareMode,&stream->out.wavex);
PaWasapiFormatAnswer answer = IsFormatSupportedInternal(stream->out.client,
stream->out.wavex);
if (answer !=paFormatIsSupported)
return answer;
LogWAVEFORMATEXTENSIBLE(&stream->out.wavex);
switch (answer){
case PWFA_OK: break;
case PWFA_NO: return paSampleFormatNotSupported;
case PWFA_SUGGESTED:
{
PRINT(("Suggested format:"));
LogWAVEFORMATEXTENSIBLE(stream->out.wavex);
if (stream->out.wavex.Format.nSamplesPerSec == (DWORD)sampleRate){
//no problem its a format issue only
}
else{
return paInvalidSampleRate;
}
}
}
//stream->out.period = info.DefaultDevicePeriod;
// stream->out.period = info.DefaultDevicePeriod;
stream->out.period = info.MinimumDevicePeriod;
hResult = stream->out.client->Initialize(
AUDCLNT_SHAREMODE_SHARED,
//AUDCLNT_SHAREMODE_EXCLUSIVE,
0, //no flags
stream->out.period*3, //tripple buffer
0,//stream->out.period,
(WAVEFORMATEX*)&stream->out.wavex,
&stream->session
);
/*For an exclusive-mode stream that uses event-driven buffering,
the caller must specify nonzero values for hnsPeriodicity and hnsBufferDuration,
and the values of these two parameters must be equal */
if (shareMode == AUDCLNT_SHAREMODE_EXCLUSIVE){
hResult = stream->out.client->Initialize(
shareMode,
AUDCLNT_STREAMFLAGS_EVENTCALLBACK,
stream->out.period,
stream->out.period,
(WAVEFORMATEX*)&stream->out.wavex,
&stream->session
);
}
else{
hResult = stream->out.client->Initialize(
shareMode,
AUDCLNT_STREAMFLAGS_EVENTCALLBACK,
0,
0,
(WAVEFORMATEX*)&stream->out.wavex,
&stream->session
);
}
if (hResult != S_OK){
logAUDCLNT_E(hResult);
return paInvalidDevice;
}
hResult = info.device->Activate(
__uuidof(IAudioEndpointVolume), CLSCTX_INPROC_SERVER, NULL,
(void**)&stream->outVol);
if (hResult != S_OK)
return paInvalidDevice;
hResult = stream->out.client->GetBufferSize(&stream->out.bufferSize);
if (hResult != S_OK)
return paInvalidDevice;
@@ -1225,16 +1329,15 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
hResult = stream->out.client->GetStreamLatency(&stream->out.latency);
if (hResult != S_OK)
return paInvalidDevice;
double periodsPerSecond = 1.0/nano100ToSeconds(stream->out.period);
double samplesPerPeriod = (double)(stream->out.wavex.Format.nSamplesPerSec)/periodsPerSecond;
//this is the number of samples that are required at each period
stream->out.framesPerHostCallback = (unsigned long)samplesPerPeriod;//unrelated to channels
stream->out.framesPerHostCallback = stream->out.bufferSize; //(unsigned long)samplesPerPeriod;//unrelated to channels
/* IMPLEMENT ME - establish which host formats are available */
hostOutputSampleFormat =
PaUtil_SelectClosestAvailableFormat( waveformatToPaFormat(stream->out.wavex), outputSampleFormat );
hostOutputSampleFormat = PaUtil_SelectClosestAvailableFormat( waveformatToPaFormat(&stream->out.wavex), outputSampleFormat );
}
else
{
@@ -1376,7 +1479,10 @@ static PaError CloseStream( PaStream* s )
SAFE_RELEASE(stream->in.client);
SAFE_RELEASE(stream->cclient);
SAFE_RELEASE(stream->rclient);
SAFE_RELEASE(stream->inVol);
SAFE_RELEASE(stream->outVol);
CloseHandle(stream->hThread);
CloseHandle(stream->hNotificationEvent);
PaUtil_TerminateBufferProcessor( &stream->bufferProcessor );
PaUtil_TerminateStreamRepresentation( &stream->streamRepresentation );
@@ -1385,7 +1491,7 @@ static PaError CloseStream( PaStream* s )
return result;
}
VOID ProcThread(void *client);
DWORD WINAPI ProcThread(void *client);
static PaError StartStream( PaStream *s )
{
@@ -1414,7 +1520,7 @@ static PaError StartStream( PaStream *s )
stream->hThread = CreateThread(
NULL, // no security attribute
0, // default stack size
(LPTHREAD_START_ROUTINE) ProcThread,
ProcThread,
(LPVOID) stream, // thread parameter
0, // not suspended
&stream->dwThreadId); // returns thread ID
@@ -1667,11 +1773,8 @@ static void WaspiHostProcessingLoop( void *inputBuffer, long inputFrames,
}
VOID
ProcThread(void *param){
HRESULT hResult;
void
MMCSS_activate(){
DWORD stuff=0;
HANDLE thCarac = pAvSetMmThreadCharacteristics("Pro Audio",&stuff);
@@ -1684,31 +1787,6 @@ ProcThread(void *param){
PRINT(("AvSetMmThreadPriority failed!\n"));
}
PaWinWasapiStream *stream = (PaWinWasapiStream*)param;
HANDLE context;
GUID threadOrderGUID;
memset(&threadOrderGUID,0,sizeof(GUID));
LARGE_INTEGER large;
large.QuadPart = stream->out.period;
BOOL ok = pAvRtCreateThreadOrderingGroup(&context,
&large,
&threadOrderGUID,
#ifdef _DEBUG
0 //THREAD_ORDER_GROUP_INFINITE_TIMEOUT
#else
0 //default is 5 times the 2nd param
#endif
//TEXT("Audio")
);
if (!ok){
PRINT(("AvRtCreateThreadOrderingGroup failed!\n"));
}
//debug
{
HANDLE hh = GetCurrentThread();
@@ -1716,9 +1794,23 @@ ProcThread(void *param){
DWORD currclass = GetPriorityClass(GetCurrentProcess());
PRINT(("currprio 0x%X currclass 0x%X\n",currprio,currclass));
}
}
//fill up initial buffer latency??
DWORD WINAPI
ProcThread(void* param){
HRESULT hResult;
MMCSS_activate();
PaWinWasapiStream *stream = (PaWinWasapiStream*)param;
stream->hNotificationEvent = CreateEvent(NULL,
FALSE, //bManualReset are we sure??
FALSE,
"PAWASA");
hResult = stream->out.client->SetEventHandle(stream->hNotificationEvent);
if (hResult != S_OK)
logAUDCLNT_E(hResult);
if (stream->out.client){
hResult = stream->out.client->Start();
@@ -1726,67 +1818,94 @@ ProcThread(void *param){
logAUDCLNT_E(hResult);
}
stream->running = true;
stream->running = true;
bool bOne = false;
while( !stream->closeRequest )
{
//lets wait but have a 1 second timeout
DWORD dwResult = WaitForSingleObject(stream->hNotificationEvent, 1000);
switch( dwResult ) {
case WAIT_OBJECT_0: {
unsigned long usingBS = stream->out.framesPerHostCallback;
BYTE* indata = 0;
BYTE* outdata = 0;
hResult = stream->rclient->GetBuffer(usingBS, &outdata);
if (hResult != S_OK || !outdata) {
//logAUDCLNT_E(hResult);
//most probably shared mode and hResult=AUDCLNT_E_BUFFER_TOO_LARGE
UINT32 padding = 0;
hResult = stream->out.client->GetCurrentPadding(&padding);
if (padding == 0)
break;
usingBS = usingBS-padding;
if (usingBS == 0)
break;//huh?
hResult = stream->rclient->GetBuffer(usingBS, &outdata);
if (hResult != S_OK)//what can we do NOW??
break;
//logAUDCLNT_E(hResult);
}
WaspiHostProcessingLoop(indata, usingBS ,outdata, usingBS, stream);
hResult = stream->rclient->ReleaseBuffer(usingBS, 0);
if (hResult != S_OK)
logAUDCLNT_E(hResult);
/* This was suggested, but in my tests it doesnt seem to improve the
locking behaviour some drivers have running in exclusive mode.
if(!ResetEvent(stream->hNotificationEvent)){
logAUDCLNT_E(hResult);
}
*/
}
break;
while(!stream->closeRequest){
BOOL answer = pAvRtWaitOnThreadOrderingGroup(context);
if (!answer){
PRINT(("AvRtWaitOnThreadOrderingGroup failed\n"));
}
unsigned long usingBS = stream->out.framesPerHostCallback;
UINT32 padding=0;
hResult = stream->out.client->GetCurrentPadding(&padding);
logAUDCLNT_E(hResult);
//buffer full dont pursue
if (padding == stream->out.bufferSize)
continue;
//if something is already inside
if (padding > 0){
usingBS = stream->out.bufferSize-padding;
if (usingBS > stream->out.framesPerHostCallback){
//PRINT(("underflow! %d\n",usingBS));
}
else if (usingBS < stream->out.framesPerHostCallback){
//PRINT(("overflow! %d\n",usingBS));
}
}
else
usingBS = stream->out.framesPerHostCallback;
BYTE*indata =0;
BYTE*outdata=0;
hResult = stream->rclient->GetBuffer(usingBS,&outdata);
if (hResult != S_OK || !outdata) {
logAUDCLNT_E(hResult);
continue;
}
WaspiHostProcessingLoop(indata, usingBS
,outdata,usingBS,stream);
hResult = stream->rclient->ReleaseBuffer(usingBS,0);
if (hResult != S_OK)
logAUDCLNT_E(hResult);
}
BOOL bRes = pAvRtDeleteThreadOrderingGroup(context);
if (!bRes){
PRINT(("AvRtDeleteThreadOrderingGroup failure\n"));
}
stream->out.client->Stop();
stream->closeRequest = false;
return 0;
}
#endif //VC 2005
#endif //VC 2005
#if 0
if(bFirst) {
float masteur;
hResult = stream->outVol->GetMasterVolumeLevelScalar(&masteur);
if (hResult != S_OK)
logAUDCLNT_E(hResult);
float chan1, chan2;
hResult = stream->outVol->GetChannelVolumeLevelScalar(0, &chan1);
if (hResult != S_OK)
logAUDCLNT_E(hResult);
hResult = stream->outVol->GetChannelVolumeLevelScalar(1, &chan2);
if (hResult != S_OK)
logAUDCLNT_E(hResult);
BOOL bMute;
hResult = stream->outVol->GetMute(&bMute);
if (hResult != S_OK)
logAUDCLNT_E(hResult);
stream->outVol->SetMasterVolumeLevelScalar(0.5, NULL);
stream->outVol->SetChannelVolumeLevelScalar(0, 0.5, NULL);
stream->outVol->SetChannelVolumeLevelScalar(1, 0.5, NULL);
stream->outVol->SetMute(FALSE, NULL);
bFirst = false;
}
#endif
@@ -1,5 +1,5 @@
/*
* $Id: pa_win_wdmks.c 1097 2006-08-26 08:27:53Z rossb $
* $Id: pa_win_wdmks.c 1263 2007-08-27 22:59:09Z rossb $
* PortAudio Windows WDM-KS interface
*
* Author: Andrew Baldwin
@@ -73,6 +73,7 @@
#include "pa_cpuload.h"
#include "pa_process.h"
#include "portaudio.h"
#include "pa_debugprint.h"
#include <windows.h>
#include <winioctl.h>
@@ -1558,6 +1559,11 @@ static PaError FilterCanCreateCapturePin(PaWinWdmFilter* filter,
/**
* Build the list of available filters
* Use the SetupDi API to enumerate all devices in the KSCATEGORY_AUDIO which
* have a KSCATEGORY_RENDER or KSCATEGORY_CAPTURE alias. For each of these
* devices initialise a PaWinWdmFilter structure by calling our NewFilter()
* function. We enumerate devices twice, once to count how many there are,
* and once to initialize the PaWinWdmFilter structures.
*/
static PaError BuildFilterList(PaWinWdmHostApiRepresentation* wdmHostApi)
{
+365 -106
View File
@@ -1,5 +1,5 @@
/*
* $Id: pa_win_wmme.c 1097 2006-08-26 08:27:53Z rossb $
* $Id: pa_win_wmme.c 1286 2007-09-26 21:34:23Z rossb $
* pa_win_wmme.c
* Implementation of PortAudio for Windows MultiMedia Extensions (WMME)
*
@@ -88,6 +88,10 @@ Non-critical stuff for the future:
@todo define UNICODE and _UNICODE in the project settings and see what breaks
@todo refactor conversion of MMSYSTEM errors into PA arrors into a single function.
@todo cleanup WAVEFORMATEXTENSIBLE retry in InitializeWaveHandles to not use a for loop
*/
/*
@@ -112,7 +116,9 @@ Non-critical stuff for the future:
#include <math.h>
#include <windows.h>
#include <mmsystem.h>
#ifndef UNDER_CE
#include <process.h>
#endif
#include <assert.h>
/* PLB20010422 - "memory.h" doesn't work on CodeWarrior for PC. Thanks Mike Berry for the mod. */
#ifndef __MWERKS__
@@ -128,10 +134,24 @@ Non-critical stuff for the future:
#include "pa_stream.h"
#include "pa_cpuload.h"
#include "pa_process.h"
#include "pa_debugprint.h"
#include "pa_win_wmme.h"
#include "pa_win_waveformat.h"
#if (defined(WIN32) && (defined(_MSC_VER) && (_MSC_VER >= 1200))) /* MSC version 6 and above */
#ifdef PAWIN_USE_WDMKS_DEVICE_INFO
#include "pa_win_wdmks_utils.h"
#ifndef DRV_QUERYDEVICEINTERFACE
#define DRV_QUERYDEVICEINTERFACE (DRV_RESERVED + 12)
#endif
#ifndef DRV_QUERYDEVICEINTERFACESIZE
#define DRV_QUERYDEVICEINTERFACESIZE (DRV_RESERVED + 13)
#endif
#endif /* PAWIN_USE_WDMKS_DEVICE_INFO */
#if (defined(UNDER_CE))
#pragma comment(lib, "Coredll.lib")
#elif (defined(WIN32) && (defined(_MSC_VER) && (_MSC_VER >= 1200))) /* MSC version 6 and above */
#pragma comment(lib, "winmm.lib")
#endif
@@ -369,6 +389,8 @@ typedef struct
{
PaDeviceInfo inheritedDeviceInfo;
DWORD dwFormats; /**<< standard formats bitmask from the WAVEINCAPS and WAVEOUTCAPS structures */
char deviceInputChannelCountIsKnown; /**<< if the system returns 0xFFFF then we don't really know the number of supported channels (1=>known, 0=>unknown)*/
char deviceOutputChannelCountIsKnown; /**<< if the system returns 0xFFFF then we don't really know the number of supported channels (1=>known, 0=>unknown)*/
}
PaWinMmeDeviceInfo;
@@ -497,7 +519,7 @@ static PaError QueryFormatSupported( PaDeviceInfo *deviceInfo,
int winMmeDeviceId, int channels, double sampleRate )
{
PaWinMmeDeviceInfo *winMmeDeviceInfo = (PaWinMmeDeviceInfo*)deviceInfo;
WAVEFORMATEX waveFormatEx;
PaWinWaveFormat waveFormat;
if( sampleRate == 11025.0
&& ( (channels == 1 && (winMmeDeviceInfo->dwFormats & WAVE_FORMAT_1M16))
@@ -520,15 +542,19 @@ static PaError QueryFormatSupported( PaDeviceInfo *deviceInfo,
return paNoError;
}
waveFormatEx.wFormatTag = WAVE_FORMAT_PCM;
waveFormatEx.nChannels = (WORD)channels;
waveFormatEx.nSamplesPerSec = (DWORD)sampleRate;
waveFormatEx.nAvgBytesPerSec = waveFormatEx.nSamplesPerSec * channels * sizeof(short);
waveFormatEx.nBlockAlign = (WORD)(channels * sizeof(short));
waveFormatEx.wBitsPerSample = 16;
waveFormatEx.cbSize = 0;
/* first, attempt to query the device using WAVEFORMATEXTENSIBLE,
if this fails we fall back to WAVEFORMATEX */
return waveFormatExQueryFunction( winMmeDeviceId, &waveFormatEx );
/* @todo at the moment we only query with 16 bit sample format and directout speaker config*/
PaWin_InitializeWaveFormatExtensible( &waveFormat, channels,
paInt16, sampleRate, PAWIN_SPEAKER_DIRECTOUT );
if( waveFormatExQueryFunction( winMmeDeviceId, (WAVEFORMATEX*)&waveFormat ) == paNoError )
return paNoError;
PaWin_InitializeWaveFormatEx( &waveFormat, channels, paInt16, sampleRate );
return waveFormatExQueryFunction( winMmeDeviceId, (WAVEFORMATEX*)&waveFormat );
}
@@ -558,6 +584,40 @@ static void DetectDefaultSampleRate( PaWinMmeDeviceInfo *winMmeDeviceInfo, int w
}
#ifdef PAWIN_USE_WDMKS_DEVICE_INFO
static int QueryWaveInKSFilterMaxChannels( int waveInDeviceId, int *maxChannels )
{
void *devicePath;
DWORD devicePathSize;
int result = 0;
if( waveInMessage((HWAVEIN)waveInDeviceId, DRV_QUERYDEVICEINTERFACESIZE,
(DWORD_PTR)&devicePathSize, 0 ) != MMSYSERR_NOERROR )
return 0;
devicePath = PaUtil_AllocateMemory( devicePathSize );
if( !devicePath )
return 0;
/* apparently DRV_QUERYDEVICEINTERFACE returns a unicode interface path, although this is undocumented */
if( waveInMessage((HWAVEIN)waveInDeviceId, DRV_QUERYDEVICEINTERFACE,
(DWORD_PTR)devicePath, devicePathSize ) == MMSYSERR_NOERROR )
{
int count = PaWin_WDMKS_QueryFilterMaximumChannelCount( devicePath, /* isInput= */ 1 );
if( count > 0 )
{
*maxChannels = count;
result = 1;
}
}
PaUtil_FreeMemory( devicePath );
return result;
}
#endif /* PAWIN_USE_WDMKS_DEVICE_INFO */
static PaError InitializeInputDeviceInfo( PaWinMmeHostApiRepresentation *winMmeHostApi,
PaWinMmeDeviceInfo *winMmeDeviceInfo, UINT winMmeInputDeviceId, int *success )
{
@@ -611,17 +671,30 @@ static PaError InitializeInputDeviceInfo( PaWinMmeHostApiRepresentation *winMmeH
}
deviceInfo->name = deviceName;
deviceInfo->maxInputChannels = wic.wChannels;
/* Sometimes a device can return a rediculously large number of channels.
* This happened with an SBLive card on a Windows ME box.
* If that happens, then force it to 2 channels. PLB20010413
if( wic.wChannels == 0xFFFF || wic.wChannels < 1 || wic.wChannels > 255 ){
/* For Windows versions using WDM (possibly Windows 98 ME and later)
* the kernel mixer sits between the application and the driver. As a result,
* wave*GetDevCaps often kernel mixer channel counts, which are unlimited.
* When this happens we assume the device is stereo and set a flag
* so that other channel counts can be tried with OpenStream -- i.e. when
* device*ChannelCountIsKnown is false, OpenStream will try whatever
* channel count you supply.
* see also InitializeOutputDeviceInfo() below.
*/
if( (deviceInfo->maxInputChannels < 1) || (deviceInfo->maxInputChannels > 256) )
{
PA_DEBUG(("Pa_GetDeviceInfo: Num input channels reported as %d! Changed to 2.\n", deviceInfo->maxInputChannels ));
PA_DEBUG(("Pa_GetDeviceInfo: Num input channels reported as %d! Changed to 2.\n", wic.wChannels ));
deviceInfo->maxInputChannels = 2;
winMmeDeviceInfo->deviceInputChannelCountIsKnown = 0;
}else{
deviceInfo->maxInputChannels = wic.wChannels;
winMmeDeviceInfo->deviceInputChannelCountIsKnown = 1;
}
#ifdef PAWIN_USE_WDMKS_DEVICE_INFO
winMmeDeviceInfo->deviceInputChannelCountIsKnown =
QueryWaveInKSFilterMaxChannels( winMmeInputDeviceId, &deviceInfo->maxInputChannels );
#endif /* PAWIN_USE_WDMKS_DEVICE_INFO */
winMmeDeviceInfo->dwFormats = wic.dwFormats;
DetectDefaultSampleRate( winMmeDeviceInfo, winMmeInputDeviceId,
@@ -634,6 +707,40 @@ error:
}
#ifdef PAWIN_USE_WDMKS_DEVICE_INFO
static int QueryWaveOutKSFilterMaxChannels( int waveOutDeviceId, int *maxChannels )
{
void *devicePath;
DWORD devicePathSize;
int result = 0;
if( waveOutMessage((HWAVEOUT)waveOutDeviceId, DRV_QUERYDEVICEINTERFACESIZE,
(DWORD_PTR)&devicePathSize, 0 ) != MMSYSERR_NOERROR )
return 0;
devicePath = PaUtil_AllocateMemory( devicePathSize );
if( !devicePath )
return 0;
/* apparently DRV_QUERYDEVICEINTERFACE returns a unicode interface path, although this is undocumented */
if( waveOutMessage((HWAVEOUT)waveOutDeviceId, DRV_QUERYDEVICEINTERFACE,
(DWORD_PTR)devicePath, devicePathSize ) == MMSYSERR_NOERROR )
{
int count = PaWin_WDMKS_QueryFilterMaximumChannelCount( devicePath, /* isInput= */ 0 );
if( count > 0 )
{
*maxChannels = count;
result = 1;
}
}
PaUtil_FreeMemory( devicePath );
return result;
}
#endif /* PAWIN_USE_WDMKS_DEVICE_INFO */
static PaError InitializeOutputDeviceInfo( PaWinMmeHostApiRepresentation *winMmeHostApi,
PaWinMmeDeviceInfo *winMmeDeviceInfo, UINT winMmeOutputDeviceId, int *success )
{
@@ -642,7 +749,7 @@ static PaError InitializeOutputDeviceInfo( PaWinMmeHostApiRepresentation *winMme
MMRESULT mmresult;
WAVEOUTCAPS woc;
PaDeviceInfo *deviceInfo = &winMmeDeviceInfo->inheritedDeviceInfo;
*success = 0;
mmresult = waveOutGetDevCaps( winMmeOutputDeviceId, &woc, sizeof( WAVEOUTCAPS ) );
@@ -687,17 +794,30 @@ static PaError InitializeOutputDeviceInfo( PaWinMmeHostApiRepresentation *winMme
}
deviceInfo->name = deviceName;
deviceInfo->maxOutputChannels = woc.wChannels;
/* Sometimes a device can return a rediculously large number of channels.
* This happened with an SBLive card on a Windows ME box.
* It also happens on Win XP!
if( woc.wChannels == 0xFFFF || woc.wChannels < 1 || woc.wChannels > 255 ){
/* For Windows versions using WDM (possibly Windows 98 ME and later)
* the kernel mixer sits between the application and the driver. As a result,
* wave*GetDevCaps often kernel mixer channel counts, which are unlimited.
* When this happens we assume the device is stereo and set a flag
* so that other channel counts can be tried with OpenStream -- i.e. when
* device*ChannelCountIsKnown is false, OpenStream will try whatever
* channel count you supply.
* see also InitializeInputDeviceInfo() above.
*/
if( (deviceInfo->maxOutputChannels < 1) || (deviceInfo->maxOutputChannels > 256) )
{
PA_DEBUG(("Pa_GetDeviceInfo: Num output channels reported as %d! Changed to 2.\n", deviceInfo->maxOutputChannels ));
PA_DEBUG(("Pa_GetDeviceInfo: Num output channels reported as %d! Changed to 2.\n", woc.wChannels ));
deviceInfo->maxOutputChannels = 2;
winMmeDeviceInfo->deviceOutputChannelCountIsKnown = 0;
}else{
deviceInfo->maxOutputChannels = woc.wChannels;
winMmeDeviceInfo->deviceOutputChannelCountIsKnown = 1;
}
#ifdef PAWIN_USE_WDMKS_DEVICE_INFO
winMmeDeviceInfo->deviceOutputChannelCountIsKnown =
QueryWaveOutKSFilterMaxChannels( winMmeOutputDeviceId, &deviceInfo->maxOutputChannels );
#endif /* PAWIN_USE_WDMKS_DEVICE_INFO */
winMmeDeviceInfo->dwFormats = woc.dwFormats;
DetectDefaultSampleRate( winMmeDeviceInfo, winMmeOutputDeviceId,
@@ -743,6 +863,8 @@ PaError PaWinMme_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiInd
PaWinMmeDeviceInfo *deviceInfoArray;
int deviceInfoInitializationSucceeded;
PaTime defaultLowLatency, defaultHighLatency;
DWORD waveInPreferredDevice, waveOutPreferredDevice;
DWORD preferredDeviceStatusFlags;
winMmeHostApi = (PaWinMmeHostApiRepresentation*)PaUtil_AllocateMemory( sizeof(PaWinMmeHostApiRepresentation) );
if( !winMmeHostApi )
@@ -774,6 +896,19 @@ PaError PaWinMme_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiInd
winMmeHostApi->inputDeviceCount = 0;
winMmeHostApi->outputDeviceCount = 0;
#if !defined(DRVM_MAPPER_PREFERRED_GET)
/* DRVM_MAPPER_PREFERRED_GET is defined in mmddk.h but we avoid a dependency on the DDK by defining it here */
#define DRVM_MAPPER_PREFERRED_GET (0x2000+21)
#endif
/* the following calls assume that if wave*Message fails the preferred device parameter won't be modified */
preferredDeviceStatusFlags = 0;
waveInPreferredDevice = -1;
waveInMessage( (HWAVEIN)WAVE_MAPPER, DRVM_MAPPER_PREFERRED_GET, (DWORD)&waveInPreferredDevice, (DWORD)&preferredDeviceStatusFlags );
preferredDeviceStatusFlags = 0;
waveOutPreferredDevice = -1;
waveOutMessage( (HWAVEOUT)WAVE_MAPPER, DRVM_MAPPER_PREFERRED_GET, (DWORD)&waveOutPreferredDevice, (DWORD)&preferredDeviceStatusFlags );
maximumPossibleDeviceCount = 0;
@@ -825,7 +960,9 @@ PaError PaWinMme_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiInd
deviceInfo->hostApi = hostApiIndex;
deviceInfo->maxInputChannels = 0;
wmmeDeviceInfo->deviceInputChannelCountIsKnown = 1;
deviceInfo->maxOutputChannels = 0;
wmmeDeviceInfo->deviceOutputChannelCountIsKnown = 1;
deviceInfo->defaultLowInputLatency = defaultLowLatency;
deviceInfo->defaultLowOutputLatency = defaultLowLatency;
@@ -838,8 +975,14 @@ PaError PaWinMme_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiInd
goto error;
if( deviceInfoInitializationSucceeded ){
if( (*hostApi)->info.defaultInputDevice == paNoDevice )
if( (*hostApi)->info.defaultInputDevice == paNoDevice ){
/* if there is currently no default device, use the first one available */
(*hostApi)->info.defaultInputDevice = (*hostApi)->info.deviceCount;
}else if( winMmeDeviceId == waveInPreferredDevice ){
/* set the default device to the system preferred device */
(*hostApi)->info.defaultInputDevice = (*hostApi)->info.deviceCount;
}
winMmeHostApi->winMmeDeviceIds[ (*hostApi)->info.deviceCount ] = winMmeDeviceId;
(*hostApi)->deviceInfos[ (*hostApi)->info.deviceCount ] = deviceInfo;
@@ -860,7 +1003,9 @@ PaError PaWinMme_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiInd
deviceInfo->hostApi = hostApiIndex;
deviceInfo->maxInputChannels = 0;
wmmeDeviceInfo->deviceInputChannelCountIsKnown = 1;
deviceInfo->maxOutputChannels = 0;
wmmeDeviceInfo->deviceOutputChannelCountIsKnown = 1;
deviceInfo->defaultLowInputLatency = defaultLowLatency;
deviceInfo->defaultLowOutputLatency = defaultLowLatency;
@@ -873,9 +1018,15 @@ PaError PaWinMme_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiInd
goto error;
if( deviceInfoInitializationSucceeded ){
if( (*hostApi)->info.defaultOutputDevice == paNoDevice )
if( (*hostApi)->info.defaultOutputDevice == paNoDevice ){
/* if there is currently no default device, use the first one available */
(*hostApi)->info.defaultOutputDevice = (*hostApi)->info.deviceCount;
}else if( winMmeDeviceId == waveOutPreferredDevice ){
/* set the default device to the system preferred device */
(*hostApi)->info.defaultOutputDevice = (*hostApi)->info.deviceCount;
}
winMmeHostApi->winMmeDeviceIds[ (*hostApi)->info.deviceCount ] = winMmeDeviceId;
(*hostApi)->deviceInfos[ (*hostApi)->info.deviceCount ] = deviceInfo;
@@ -886,7 +1037,6 @@ PaError PaWinMme_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiInd
}
}
InitializeDefaultDeviceIdsFromEnv( winMmeHostApi );
(*hostApi)->Terminate = Terminate;
@@ -936,6 +1086,34 @@ static void Terminate( struct PaUtilHostApiRepresentation *hostApi )
}
static PaError IsInputChannelCountSupported( PaWinMmeDeviceInfo* deviceInfo, int channelCount )
{
PaError result = paNoError;
if( channelCount > 0
&& deviceInfo->deviceInputChannelCountIsKnown
&& channelCount > deviceInfo->inheritedDeviceInfo.maxInputChannels ){
result = paInvalidChannelCount;
}
return result;
}
static PaError IsOutputChannelCountSupported( PaWinMmeDeviceInfo* deviceInfo, int channelCount )
{
PaError result = paNoError;
if( channelCount > 0
&& deviceInfo->deviceOutputChannelCountIsKnown
&& channelCount > deviceInfo->inheritedDeviceInfo.maxOutputChannels ){
result = paInvalidChannelCount;
}
return result;
}
static PaError IsFormatSupported( struct PaUtilHostApiRepresentation *hostApi,
const PaStreamParameters *inputParameters,
const PaStreamParameters *outputParameters,
@@ -980,10 +1158,14 @@ static PaError IsFormatSupported( struct PaUtilHostApiRepresentation *hostApi,
inputDeviceInfo = hostApi->deviceInfos[ inputStreamInfo->devices[i].device ];
/* check that input device can support inputChannelCount */
if( inputStreamInfo->devices[i].channelCount <= 0
|| inputStreamInfo->devices[i].channelCount > inputDeviceInfo->maxInputChannels )
if( inputStreamInfo->devices[i].channelCount < 1 )
return paInvalidChannelCount;
paerror = IsInputChannelCountSupported( (PaWinMmeDeviceInfo*)inputDeviceInfo,
inputStreamInfo->devices[i].channelCount );
if( paerror != paNoError )
return paerror;
/* test for valid sample rate, see comment above */
winMmeInputDeviceId = LocalDeviceIndexToWinMmeDeviceId( winMmeHostApi, inputStreamInfo->devices[i].device );
paerror = QueryFormatSupported( inputDeviceInfo, QueryInputWaveFormatEx, winMmeInputDeviceId, inputStreamInfo->devices[i].channelCount, sampleRate );
@@ -1002,8 +1184,9 @@ static PaError IsFormatSupported( struct PaUtilHostApiRepresentation *hostApi,
inputDeviceInfo = hostApi->deviceInfos[ inputParameters->device ];
/* check that input device can support inputChannelCount */
if( inputChannelCount > inputDeviceInfo->maxInputChannels )
return paInvalidChannelCount;
paerror = IsInputChannelCountSupported( (PaWinMmeDeviceInfo*)inputDeviceInfo, inputChannelCount );
if( paerror != paNoError )
return paerror;
/* test for valid sample rate, see comment above */
winMmeInputDeviceId = LocalDeviceIndexToWinMmeDeviceId( winMmeHostApi, inputParameters->device );
@@ -1035,10 +1218,14 @@ static PaError IsFormatSupported( struct PaUtilHostApiRepresentation *hostApi,
outputDeviceInfo = hostApi->deviceInfos[ outputStreamInfo->devices[i].device ];
/* check that output device can support outputChannelCount */
if( outputStreamInfo->devices[i].channelCount <= 0
|| outputStreamInfo->devices[i].channelCount > outputDeviceInfo->maxOutputChannels )
if( outputStreamInfo->devices[i].channelCount < 1 )
return paInvalidChannelCount;
paerror = IsOutputChannelCountSupported( (PaWinMmeDeviceInfo*)outputDeviceInfo,
outputStreamInfo->devices[i].channelCount );
if( paerror != paNoError )
return paerror;
/* test for valid sample rate, see comment above */
winMmeOutputDeviceId = LocalDeviceIndexToWinMmeDeviceId( winMmeHostApi, outputStreamInfo->devices[i].device );
paerror = QueryFormatSupported( outputDeviceInfo, QueryOutputWaveFormatEx, winMmeOutputDeviceId, outputStreamInfo->devices[i].channelCount, sampleRate );
@@ -1057,8 +1244,9 @@ static PaError IsFormatSupported( struct PaUtilHostApiRepresentation *hostApi,
outputDeviceInfo = hostApi->deviceInfos[ outputParameters->device ];
/* check that output device can support outputChannelCount */
if( outputChannelCount > outputDeviceInfo->maxOutputChannels )
return paInvalidChannelCount;
paerror = IsOutputChannelCountSupported( (PaWinMmeDeviceInfo*)outputDeviceInfo, outputChannelCount );
if( paerror != paNoError )
return paerror;
/* test for valid sample rate, see comment above */
winMmeOutputDeviceId = LocalDeviceIndexToWinMmeDeviceId( winMmeHostApi, outputParameters->device );
@@ -1488,7 +1676,7 @@ static PaError InitializeWaveHandles( PaWinMmeHostApiRepresentation *winMmeHostA
PaWinMmeSingleDirectionHandlesAndBuffers *handlesAndBuffers,
unsigned long bytesPerHostSample,
double sampleRate, PaWinMmeDeviceAndChannelCount *devices,
unsigned int deviceCount, int isInput );
unsigned int deviceCount, PaWinWaveFormatChannelMask channelMask, int isInput );
static PaError TerminateWaveHandles( PaWinMmeSingleDirectionHandlesAndBuffers *handlesAndBuffers, int isInput, int currentlyProcessingAnError );
static PaError InitializeWaveHeaders( PaWinMmeSingleDirectionHandlesAndBuffers *handlesAndBuffers,
unsigned long hostBufferCount,
@@ -1512,13 +1700,11 @@ static PaError InitializeWaveHandles( PaWinMmeHostApiRepresentation *winMmeHostA
PaWinMmeSingleDirectionHandlesAndBuffers *handlesAndBuffers,
unsigned long bytesPerHostSample,
double sampleRate, PaWinMmeDeviceAndChannelCount *devices,
unsigned int deviceCount, int isInput )
unsigned int deviceCount, PaWinWaveFormatChannelMask channelMask, int isInput )
{
PaError result;
MMRESULT mmresult;
unsigned long bytesPerFrame;
WAVEFORMATEX wfx;
signed int i;
signed int i, j;
/* for error cleanup we expect that InitializeSingleDirectionHandlesAndBuffers()
has already been called to zero some fields */
@@ -1546,64 +1732,94 @@ static PaError InitializeWaveHandles( PaWinMmeHostApiRepresentation *winMmeHostA
((HWAVEOUT*)handlesAndBuffers->waveHandles)[i] = 0;
}
wfx.wFormatTag = WAVE_FORMAT_PCM;
wfx.nSamplesPerSec = (DWORD) sampleRate;
wfx.cbSize = 0;
for( i = 0; i < (signed int)deviceCount; ++i )
{
UINT winMmeDeviceId;
PaWinWaveFormat waveFormat;
UINT winMmeDeviceId = LocalDeviceIndexToWinMmeDeviceId( winMmeHostApi, devices[i].device );
/* @todo: consider providing a flag or #define to not try waveformat extensible
this could just initialize j to 1 the first time round. */
winMmeDeviceId = LocalDeviceIndexToWinMmeDeviceId( winMmeHostApi, devices[i].device );
wfx.nChannels = (WORD)devices[i].channelCount;
bytesPerFrame = wfx.nChannels * bytesPerHostSample;
wfx.nAvgBytesPerSec = (DWORD)(bytesPerFrame * sampleRate);
wfx.nBlockAlign = (WORD)bytesPerFrame;
wfx.wBitsPerSample = (WORD)((bytesPerFrame/wfx.nChannels) * 8);
/* REVIEW: consider not firing an event for input when a full duplex
stream is being used. this would probably depend on the
neverDropInput flag. */
if( isInput )
mmresult = waveInOpen( &((HWAVEIN*)handlesAndBuffers->waveHandles)[i], winMmeDeviceId, &wfx,
(DWORD_PTR)handlesAndBuffers->bufferEvent, (DWORD_PTR)0, CALLBACK_EVENT );
else
mmresult = waveOutOpen( &((HWAVEOUT*)handlesAndBuffers->waveHandles)[i], winMmeDeviceId, &wfx,
(DWORD_PTR)handlesAndBuffers->bufferEvent, (DWORD_PTR)0, CALLBACK_EVENT );
if( mmresult != MMSYSERR_NOERROR )
for( j = 0; j < 2; ++j )
{
switch( mmresult )
{
case MMSYSERR_ALLOCATED: /* Specified resource is already allocated. */
result = paDeviceUnavailable;
break;
case MMSYSERR_NODRIVER: /* No device driver is present. */
result = paDeviceUnavailable;
break;
case MMSYSERR_NOMEM: /* Unable to allocate or lock memory. */
result = paInsufficientMemory;
break;
if( j == 0 )
{
/* first, attempt to open the device using WAVEFORMATEXTENSIBLE,
if this fails we fall back to WAVEFORMATEX */
/* @todo at the moment we only use 16 bit sample format */
PaWin_InitializeWaveFormatExtensible( &waveFormat, devices[i].channelCount,
paInt16, sampleRate, channelMask );
case MMSYSERR_BADDEVICEID: /* Specified device identifier is out of range. */
/* falls through */
case WAVERR_BADFORMAT: /* Attempted to open with an unsupported waveform-audio format. */
/* falls through */
default:
result = paUnanticipatedHostError;
if( isInput )
{
PA_MME_SET_LAST_WAVEIN_ERROR( mmresult );
}
else
{
PA_MME_SET_LAST_WAVEOUT_ERROR( mmresult );
}
}
goto error;
else
{
/* retry with WAVEFORMATEX */
PaWin_InitializeWaveFormatEx( &waveFormat, devices[i].channelCount, paInt16, sampleRate );
}
/* REVIEW: consider not firing an event for input when a full duplex
stream is being used. this would probably depend on the
neverDropInput flag. */
if( isInput )
{
mmresult = waveInOpen( &((HWAVEIN*)handlesAndBuffers->waveHandles)[i], winMmeDeviceId,
(WAVEFORMATEX*)&waveFormat,
(DWORD_PTR)handlesAndBuffers->bufferEvent, (DWORD_PTR)0, CALLBACK_EVENT );
}
else
{
mmresult = waveOutOpen( &((HWAVEOUT*)handlesAndBuffers->waveHandles)[i], winMmeDeviceId,
(WAVEFORMATEX*)&waveFormat,
(DWORD_PTR)handlesAndBuffers->bufferEvent, (DWORD_PTR)0, CALLBACK_EVENT );
}
if( mmresult == MMSYSERR_NOERROR )
{
break; /* success */
}
else if( j == 0 )
{
continue; /* try again with WAVEFORMATEX */
}
else
{
switch( mmresult )
{
case MMSYSERR_ALLOCATED: /* Specified resource is already allocated. */
result = paDeviceUnavailable;
break;
case MMSYSERR_NODRIVER: /* No device driver is present. */
result = paDeviceUnavailable;
break;
case MMSYSERR_NOMEM: /* Unable to allocate or lock memory. */
result = paInsufficientMemory;
break;
case MMSYSERR_BADDEVICEID: /* Specified device identifier is out of range. */
/* falls through */
case WAVERR_BADFORMAT: /* Attempted to open with an unsupported waveform-audio format. */
/* This can also occur if we try to open the device with an unsupported
* number of channels. This is attempted when device*ChannelCountIsKnown is
* set to 0.
*/
/* falls through */
default:
result = paUnanticipatedHostError;
if( isInput )
{
PA_MME_SET_LAST_WAVEIN_ERROR( mmresult );
}
else
{
PA_MME_SET_LAST_WAVEOUT_ERROR( mmresult );
}
}
goto error;
}
}
}
@@ -1921,12 +2137,20 @@ static PaError ValidateInputChannelCounts(
unsigned long deviceCount )
{
unsigned int i;
PaWinMmeDeviceInfo *inputDeviceInfo;
PaError paerror;
for( i=0; i < deviceCount; ++i )
{
if( devices[i].channelCount < 1 || devices[i].channelCount
> hostApi->deviceInfos[ devices[i].device ]->maxInputChannels )
if( devices[i].channelCount < 1 )
return paInvalidChannelCount;
inputDeviceInfo =
(PaWinMmeDeviceInfo*)hostApi->deviceInfos[ devices[i].device ];
paerror = IsInputChannelCountSupported( inputDeviceInfo, devices[i].channelCount );
if( paerror != paNoError )
return paerror;
}
return paNoError;
@@ -1938,12 +2162,20 @@ static PaError ValidateOutputChannelCounts(
unsigned long deviceCount )
{
unsigned int i;
PaWinMmeDeviceInfo *outputDeviceInfo;
PaError paerror;
for( i=0; i < deviceCount; ++i )
{
if( devices[i].channelCount < 1 || devices[i].channelCount
> hostApi->deviceInfos[ devices[i].device ]->maxOutputChannels )
if( devices[i].channelCount < 1 )
return paInvalidChannelCount;
outputDeviceInfo =
(PaWinMmeDeviceInfo*)hostApi->deviceInfos[ devices[i].device ];
paerror = IsOutputChannelCountSupported( outputDeviceInfo, devices[i].channelCount );
if( paerror != paNoError )
return paerror;
}
return paNoError;
@@ -1976,6 +2208,7 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
PaSampleFormat inputSampleFormat, outputSampleFormat;
double suggestedInputLatency, suggestedOutputLatency;
PaWinMmeStreamInfo *inputStreamInfo, *outputStreamInfo;
PaWinWaveFormatChannelMask inputChannelMask, outputChannelMask;
unsigned long framesPerHostInputBuffer;
unsigned long hostInputBufferCount;
unsigned long framesPerHostOutputBuffer;
@@ -2014,6 +2247,18 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
hostInputSampleFormat =
PaUtil_SelectClosestAvailableFormat( paInt16 /* native formats */, inputSampleFormat );
if( inputDeviceCount != 1 ){
/* always use direct speakers when using multi-device multichannel mode */
inputChannelMask = PAWIN_SPEAKER_DIRECTOUT;
}
else
{
if( inputStreamInfo && inputStreamInfo->flags & paWinMmeUseChannelMask )
inputChannelMask = inputStreamInfo->channelMask;
else
inputChannelMask = PaWin_DefaultChannelMask( inputDevices[0].channelCount );
}
}
else
{
@@ -2051,6 +2296,18 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
hostOutputSampleFormat =
PaUtil_SelectClosestAvailableFormat( paInt16 /* native formats */, outputSampleFormat );
if( outputDeviceCount != 1 ){
/* always use direct speakers when using multi-device multichannel mode */
outputChannelMask = PAWIN_SPEAKER_DIRECTOUT;
}
else
{
if( outputStreamInfo && outputStreamInfo->flags & paWinMmeUseChannelMask )
outputChannelMask = outputStreamInfo->channelMask;
else
outputChannelMask = PaWin_DefaultChannelMask( outputDevices[0].channelCount );
}
}
else
{
@@ -2172,7 +2429,7 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
{
result = InitializeWaveHandles( winMmeHostApi, &stream->input,
stream->bufferProcessor.bytesPerHostInputSample, sampleRate,
inputDevices, inputDeviceCount, 1 /* isInput */ );
inputDevices, inputDeviceCount, inputChannelMask, 1 /* isInput */ );
if( result != paNoError ) goto error;
}
@@ -2180,7 +2437,7 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
{
result = InitializeWaveHandles( winMmeHostApi, &stream->output,
stream->bufferProcessor.bytesPerHostOutputSample, sampleRate,
outputDevices, outputDeviceCount, 0 /* isInput */ );
outputDevices, outputDeviceCount, outputChannelMask, 0 /* isInput */ );
if( result != paNoError ) goto error;
}
@@ -2335,6 +2592,7 @@ static PaError AdvanceToNextInputBuffer( PaWinMmeStream *stream )
for( i=0; i < stream->input.deviceCount; ++i )
{
stream->input.waveHeaders[i][ stream->input.currentBufferIndex ].dwFlags &= ~WHDR_DONE;
mmresult = waveInAddBuffer( ((HWAVEIN*)stream->input.waveHandles)[i],
&stream->input.waveHeaders[i][ stream->input.currentBufferIndex ],
sizeof(WAVEHDR) );
@@ -2637,7 +2895,7 @@ static DWORD WINAPI ProcessingThreadProc( void *pArg )
for( i=0; i<stream->input.deviceCount; ++i )
{
/* we have stored the number of channels in the buffer in dwUser */
int channelCount = stream->input.waveHeaders[i][ hostInputBufferIndex ].dwUser;
int channelCount = (int)stream->input.waveHeaders[i][ hostInputBufferIndex ].dwUser;
PaUtil_SetInterleavedInputChannels( &stream->bufferProcessor, channel,
stream->input.waveHeaders[i][ hostInputBufferIndex ].lpData +
@@ -2658,7 +2916,7 @@ static DWORD WINAPI ProcessingThreadProc( void *pArg )
for( i=0; i<stream->output.deviceCount; ++i )
{
/* we have stored the number of channels in the buffer in dwUser */
int channelCount = stream->output.waveHeaders[i][ hostOutputBufferIndex ].dwUser;
int channelCount = (int)stream->output.waveHeaders[i][ hostOutputBufferIndex ].dwUser;
PaUtil_SetInterleavedOutputChannels( &stream->bufferProcessor, channel,
stream->output.waveHeaders[i][ hostOutputBufferIndex ].lpData +
@@ -2861,6 +3119,7 @@ static PaError StartStream( PaStream *s )
{
for( j=0; j<stream->input.deviceCount; ++j )
{
stream->input.waveHeaders[j][i].dwFlags &= ~WHDR_DONE;
mmresult = waveInAddBuffer( ((HWAVEIN*)stream->input.waveHandles)[j], &stream->input.waveHeaders[j][i], sizeof(WAVEHDR) );
if( mmresult != MMSYSERR_NOERROR )
{
@@ -2907,7 +3166,7 @@ static PaError StartStream( PaStream *s )
for( j=0; j<stream->output.deviceCount; ++j )
{
/* we have stored the number of channels in the buffer in dwUser */
int channelCount = stream->output.waveHeaders[j][i].dwUser;
int channelCount = (int)stream->output.waveHeaders[j][i].dwUser;
PaUtil_SetInterleavedOutputChannels( &stream->bufferProcessor, channel,
stream->output.waveHeaders[j][i].lpData +
@@ -3115,7 +3374,7 @@ static PaError StopStream( PaStream *s )
for( i=0; i<stream->output.deviceCount; ++i )
{
/* we have stored the number of channels in the buffer in dwUser */
int channelCount = stream->output.waveHeaders[i][ hostOutputBufferIndex ].dwUser;
int channelCount = (int)stream->output.waveHeaders[i][ hostOutputBufferIndex ].dwUser;
PaUtil_SetInterleavedOutputChannels( &stream->bufferProcessor, channel,
stream->output.waveHeaders[i][ hostOutputBufferIndex ].lpData +
@@ -3367,7 +3626,7 @@ static PaError ReadStream( PaStream* s,
for( i=0; i<stream->input.deviceCount; ++i )
{
/* we have stored the number of channels in the buffer in dwUser */
int channelCount = stream->input.waveHeaders[i][ hostInputBufferIndex ].dwUser;
int channelCount = (int)stream->input.waveHeaders[i][ hostInputBufferIndex ].dwUser;
PaUtil_SetInterleavedInputChannels( &stream->bufferProcessor, channel,
stream->input.waveHeaders[i][ hostInputBufferIndex ].lpData +
@@ -3472,7 +3731,7 @@ static PaError WriteStream( PaStream* s,
for( i=0; i<stream->output.deviceCount; ++i )
{
/* we have stored the number of channels in the buffer in dwUser */
int channelCount = stream->output.waveHeaders[i][ hostOutputBufferIndex ].dwUser;
int channelCount = (int)stream->output.waveHeaders[i][ hostOutputBufferIndex ].dwUser;
PaUtil_SetInterleavedOutputChannels( &stream->bufferProcessor, channel,
stream->output.waveHeaders[i][ hostOutputBufferIndex ].lpData +