1 /*
2  * $Id: pa_win_wmme.c,v 1.1.1.1 2007/09/28 02:50:56 sumomo Exp $
3  * pa_win_wmme.c
4  * Implementation of PortAudio for Windows MultiMedia Extensions (WMME)
5  *
6  * PortAudio Portable Real-Time Audio Library
7  * Latest Version at: http://www.portaudio.com
8  *
9  * Authors: Ross Bencina and Phil Burk
10  * Copyright (c) 1999-2000 Ross Bencina and Phil Burk
11  *
12  * Permission is hereby granted, free of charge, to any person obtaining
13  * a copy of this software and associated documentation files
14  * (the "Software"), to deal in the Software without restriction,
15  * including without limitation the rights to use, copy, modify, merge,
16  * publish, distribute, sublicense, and/or sell copies of the Software,
17  * and to permit persons to whom the Software is furnished to do so,
18  * subject to the following conditions:
19  *
20  * The above copyright notice and this permission notice shall be
21  * included in all copies or substantial portions of the Software.
22  *
23  * Any person wishing to distribute modifications to the Software is
24  * requested to send the modifications to the original developer so that
25  * they can be incorporated into the canonical version.
26  *
27  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
28  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
29  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
30  * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
31  * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
32  * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
33  * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
34  *
35  */
36 /*
37   All memory allocations and frees are marked with MEM for quick review.
38 */
39 
40 /* Modification History:
41  PLB = Phil Burk
42  JM = Julien Maillard
43  RDB = Ross Bencina
44  PLB20010402 - sDevicePtrs now allocates based on sizeof(pointer)
45  PLB20010413 - check for excessive numbers of channels
46  PLB20010422 - apply Mike Berry's changes for CodeWarrior on PC
47                including condition including of memory.h,
48                and explicit typecasting on memory allocation
49  PLB20010802 - use GlobalAlloc for sDevicesPtr instead of PaHost_AllocFastMemory
50  PLB20010816 - pass process instead of thread to SetPriorityClass()
51  PLB20010927 - use number of frames instead of real-time for CPULoad calculation.
52  JM20020118 - prevent hung thread when buffers underflow.
53  PLB20020321 - detect Win XP versus NT, 9x; fix DBUG typo; removed init of CurrentCount
54  RDB20020411 - various renaming cleanups, factored streamData alloc and cpu usage init
55  RDB20020417 - stopped counting WAVE_MAPPER when there were no real devices
56                refactoring, renaming and fixed a few edge case bugs
57  PLB20020612 - added 8000.0 Hz to custom sampling rates array
58 */
59 #pragma warning (disable: 4115)
60 #include <stdio.h>
61 #include <stdlib.h>
62 #include <math.h>
63 #include <windows.h>
64 #include <mmsystem.h>
65 #include <process.h>
66 /* PLB20010422 - "memory.h" doesn't work on CodeWarrior for PC. Thanks Mike Berry for the mod. */
67 #ifndef __MWERKS__
68 #include <malloc.h>
69 #include <memory.h>
70 #endif /* __MWERKS__ */
71 #include "portaudio.h"
72 #include "pa_host.h"
73 #include "pa_trace.h"
74 
75 /************************************************* Constants ********/
76 #define PA_TRACK_MEMORY          (0)
77 
78 #define PA_USE_TIMER_CALLBACK    (0)  /* Select between two options for background task. 0=thread, 1=timer */
79 /* Switches for debugging. */
80 #define PA_SIMULATE_UNDERFLOW    (0)  /* Set to one to force an underflow of the output buffer. */
81 
82 /* To trace program, enable TRACE_REALTIME_EVENTS in pa_trace.h */
83 #define PA_TRACE_RUN             (0)
84 #define PA_TRACE_START_STOP      (1)
85 
86 #define PA_USE_HIGH_LATENCY      (0)  /* For debugging glitches. */
87 
88 #if PA_USE_HIGH_LATENCY
89  #define PA_MIN_MSEC_PER_HOST_BUFFER  (100)
90  #define PA_MAX_MSEC_PER_HOST_BUFFER  (300) /* Do not exceed unless user buffer exceeds */
91  #define PA_MIN_NUM_HOST_BUFFERS      (4)
92  #define PA_MAX_NUM_HOST_BUFFERS      (16)  /* OK to exceed if necessary */
93  #define PA_WIN_9X_LATENCY            (400)
94 #else
95  #define PA_MIN_MSEC_PER_HOST_BUFFER  (10)
96  #define PA_MAX_MSEC_PER_HOST_BUFFER  (100) /* Do not exceed unless user buffer exceeds */
97  #define PA_MIN_NUM_HOST_BUFFERS      (3)
98  #define PA_MAX_NUM_HOST_BUFFERS      (16)  /* OK to exceed if necessary */
99  #define PA_WIN_9X_LATENCY            (200)
100 #endif
101 #define MIN_TIMEOUT_MSEC                 (1000)
102 
103 /*
104 ** Use higher latency for NT because it is even worse at real-time
105 ** operation than Win9x.
106 */
107 #define PA_WIN_NT_LATENCY        (PA_WIN_9X_LATENCY * 2)
108 #define PA_WIN_WDM_LATENCY       (PA_WIN_9X_LATENCY)
109 
110 #if PA_SIMULATE_UNDERFLOW
111 static  gUnderCallbackCounter = 0;
112 #define UNDER_SLEEP_AT       (40)
113 #define UNDER_SLEEP_FOR      (500)
114 #endif
115 
116 #define PRINT(x) { printf x; fflush(stdout); }
117 #define ERR_RPT(x) PRINT(x)
118 #define DBUG(x)  /* PRINT(x) */
119 #define DBUGX(x) /* PRINT(x) */
120 /************************************************* Definitions ********/
121 /**************************************************************
122  * Structure for internal host specific stream data.
123  * This is allocated on a per stream basis.
124  */
125 typedef struct PaWMMEStreamData
126 {
127     /* Input -------------- */
128     HWAVEIN            hWaveIn;
129     WAVEHDR           *inputBuffers;
130     int                currentInputBuffer;
131     int                bytesPerHostInputBuffer;
132     int                bytesPerUserInputBuffer;    /* native buffer size in bytes */
133     /* Output -------------- */
134     HWAVEOUT           hWaveOut;
135     WAVEHDR           *outputBuffers;
136     int                currentOutputBuffer;
137     int                bytesPerHostOutputBuffer;
138     int                bytesPerUserOutputBuffer;    /* native buffer size in bytes */
139     /* Run Time -------------- */
140     PaTimestamp        framesPlayed;
141     long               lastPosition;                /* used to track frames played. */
142     /* For measuring CPU utilization. */
143     LARGE_INTEGER      entryCount;
144     double             inverseTicksPerHostBuffer;
145     /* Init Time -------------- */
146     int                numHostBuffers;
147     int                framesPerHostBuffer;
148     int                userBuffersPerHostBuffer;
149     CRITICAL_SECTION   streamLock;                  /* Mutext to prevent threads from colliding. */
150     INT                streamLockInited;
151 #if PA_USE_TIMER_CALLBACK
152     BOOL               ifInsideCallback;            /* Test for reentrancy. */
153     MMRESULT           timerID;
154 #else
155     HANDLE             abortEvent;
156     int                abortEventInited;
157     HANDLE             bufferEvent;
158     int                bufferEventInited;
159     HANDLE             engineThread;
160     DWORD              engineThreadID;
161 #endif
162 }
163 PaWMMEStreamData;
164 /************************************************* Shared Data ********/
165 /* FIXME - put Mutex around this shared data. */
166 static int sNumInputDevices = 0;
167 static int sNumOutputDevices = 0;
168 static int sNumDevices = 0;
169 static PaDeviceInfo **sDevicePtrs = NULL;
170 static int sDefaultInputDeviceID = paNoDevice;
171 static int sDefaultOutputDeviceID = paNoDevice;
172 static int sPaHostError = 0;
173 static const char sMapperSuffixInput[] = " - Input";
174 static const char sMapperSuffixOutput[] = " - Output";
175 
176 #if PA_TRACK_MEMORY
177 static int sNumAllocations = 0;
178 #endif
179 
180 /************************************************* Macros ********/
181 /* Convert external PA ID to an internal ID that includes WAVE_MAPPER */
182 #define PaDeviceIdToWinId(id) (((id) < sNumInputDevices) ? (id - 1) : (id - sNumInputDevices - 1))
183 /************************************************* Prototypes **********/
184 
185 void Pa_InitializeNumDevices( void );
186 PaError Pa_AllocateDevicePtrs( void );
187 
188 static void CALLBACK Pa_TimerCallback(UINT uID, UINT uMsg,
189                                       DWORD dwUser, DWORD dw1, DWORD dw2);
190 PaError PaHost_GetTotalBufferFrames( internalPortAudioStream   *past );
191 static PaError PaHost_UpdateStreamTime( PaWMMEStreamData *wmmeStreamData );
192 static PaError PaHost_BackgroundManager( internalPortAudioStream   *past );
193 
194 static void *PaHost_AllocateTrackedMemory( long numBytes );
195 static void PaHost_FreeTrackedMemory( void *addr );
196 
197 /*******************************************************************/
PaHost_AllocateWMMEStreamData(internalPortAudioStream * stream)198 static PaError PaHost_AllocateWMMEStreamData( internalPortAudioStream *stream )
199 {
200     PaError             result = paNoError;
201     PaWMMEStreamData *wmmeStreamData;
202 
203     wmmeStreamData = (PaWMMEStreamData *) PaHost_AllocateFastMemory(sizeof(PaWMMEStreamData)); /* MEM */
204     if( wmmeStreamData == NULL )
205     {
206         result = paInsufficientMemory;
207         goto error;
208     }
209     memset( wmmeStreamData, 0, sizeof(PaWMMEStreamData) );
210     stream->past_DeviceData = (void *) wmmeStreamData;
211 
212     return result;
213 
214 error:
215     return result;
216 }
217 
218 /*******************************************************************/
PaHost_FreeWMMEStreamData(internalPortAudioStream * internalStream)219 static void PaHost_FreeWMMEStreamData( internalPortAudioStream *internalStream )
220 {
221     PaWMMEStreamData *wmmeStreamData = (PaWMMEStreamData *) internalStream->past_DeviceData;
222 
223     PaHost_FreeFastMemory( wmmeStreamData, sizeof(PaWMMEStreamData) ); /* MEM */
224     internalStream->past_DeviceData = NULL;
225 }
226 /*************************************************************************/
PaHost_GetWMMEStreamData(internalPortAudioStream * internalStream)227 static PaWMMEStreamData* PaHost_GetWMMEStreamData( internalPortAudioStream* internalStream )
228 {
229     PaWMMEStreamData *result = NULL;
230 
231     if( internalStream != NULL )
232     {
233         result = (PaWMMEStreamData *) internalStream->past_DeviceData;
234     }
235     return result;
236 }
237 /********************************* BEGIN CPU UTILIZATION MEASUREMENT ****/
238 /* FIXME: the cpu usage code should be factored out into a common module */
Pa_InitializeCpuUsageScalar(internalPortAudioStream * stream)239 static void Pa_InitializeCpuUsageScalar( internalPortAudioStream   *stream )
240 {
241     PaWMMEStreamData *wmmeStreamData = (PaWMMEStreamData *) stream->past_DeviceData;
242 
243     LARGE_INTEGER frequency;
244     if( QueryPerformanceFrequency( &frequency ) == 0 )
245     {
246         wmmeStreamData->inverseTicksPerHostBuffer = 0.0;
247     }
248     else
249     {
250         wmmeStreamData->inverseTicksPerHostBuffer = stream->past_SampleRate /
251                 ( (double)frequency.QuadPart * stream->past_FramesPerUserBuffer * wmmeStreamData->userBuffersPerHostBuffer );
252         DBUG(("inverseTicksPerHostBuffer = %g\n", wmmeStreamData->inverseTicksPerHostBuffer ));
253     }
254 }
Pa_StartUsageCalculation(internalPortAudioStream * stream)255 static void Pa_StartUsageCalculation( internalPortAudioStream   *stream )
256 {
257     PaWMMEStreamData *wmmeStreamData = (PaWMMEStreamData *) stream->past_DeviceData;
258 
259     if( wmmeStreamData == NULL ) return;
260     /* Query system timer for usage analysis and to prevent overuse of CPU. */
261     QueryPerformanceCounter( &wmmeStreamData->entryCount );
262 }
Pa_EndUsageCalculation(internalPortAudioStream * stream)263 static void Pa_EndUsageCalculation( internalPortAudioStream   *stream )
264 {
265     LARGE_INTEGER CurrentCount;
266     PaWMMEStreamData *wmmeStreamData = (PaWMMEStreamData *) stream->past_DeviceData;
267 
268     if( wmmeStreamData == NULL ) return;
269     /*
270      * Measure CPU utilization during this callback. Note that this calculation
271      * assumes that we had the processor the whole time.
272      */
273 #define LOWPASS_COEFFICIENT_0   (0.9)
274 #define LOWPASS_COEFFICIENT_1   (0.99999 - LOWPASS_COEFFICIENT_0)
275     if( QueryPerformanceCounter( &CurrentCount ) )
276     {
277         LONGLONG InsideCount = CurrentCount.QuadPart - wmmeStreamData->entryCount.QuadPart;
278         double newUsage = InsideCount * wmmeStreamData->inverseTicksPerHostBuffer;
279         stream->past_Usage = (LOWPASS_COEFFICIENT_0 * stream->past_Usage) +
280                              (LOWPASS_COEFFICIENT_1 * newUsage);
281     }
282 }
283 /****************************************** END CPU UTILIZATION *******/
284 
Pa_InitializeNumDevices(void)285 static void Pa_InitializeNumDevices( void )
286 {
287     sNumInputDevices = waveInGetNumDevs();
288     if( sNumInputDevices > 0 )
289     {
290         sNumInputDevices += 1; /* add one extra for the WAVE_MAPPER */
291         sDefaultInputDeviceID = 0;
292     }
293     else
294     {
295         sDefaultInputDeviceID = paNoDevice;
296     }
297 
298     sNumOutputDevices = waveOutGetNumDevs();
299     if( sNumOutputDevices > 0 )
300     {
301         sNumOutputDevices += 1; /* add one extra for the WAVE_MAPPER */
302         sDefaultOutputDeviceID = sNumInputDevices;
303     }
304     else
305     {
306         sDefaultOutputDeviceID = paNoDevice;
307     }
308 
309     sNumDevices = sNumInputDevices + sNumOutputDevices;
310 }
311 
Pa_AllocateDevicePtrs(void)312 static PaError Pa_AllocateDevicePtrs( void )
313 {
314     int numBytes;
315     int i;
316 
317     /* Allocate structures to hold device info. */
318     /* PLB20010402 - was allocating too much memory. */
319     /* numBytes = sNumDevices * sizeof(PaDeviceInfo);  // PLB20010402 */
320 
321     if( sNumDevices > 0 )
322     {
323         numBytes = sNumDevices * sizeof(PaDeviceInfo *); /* PLB20010402 */
324         sDevicePtrs = (PaDeviceInfo **) PaHost_AllocateTrackedMemory( numBytes ); /* MEM */
325         if( sDevicePtrs == NULL ) return paInsufficientMemory;
326 
327         for( i = 0; i < sNumDevices; i++ )
328             sDevicePtrs[i] = NULL;  /* RDB20020417 explicitly set each ptr to NULL */
329     }
330     else
331     {
332         sDevicePtrs = NULL;
333     }
334 
335     return paNoError;
336 }
337 /*************************************************************************/
Pa_GetHostError()338 long Pa_GetHostError()
339 {
340     return sPaHostError;
341 }
342 /*************************************************************************/
Pa_CountDevices()343 int Pa_CountDevices()
344 {
345     if( PaHost_IsInitialized() )
346         return sNumDevices;
347     else
348         return 0;
349 }
350 /*************************************************************************
351  * If a PaDeviceInfo structure has not already been created,
352  * then allocate one and fill it in for the selected device.
353  *
354  * We create one extra input and one extra output device for the WAVE_MAPPER.
355  * [Does anyone know how to query the default device and get its name?]
356  */
Pa_GetDeviceInfo(PaDeviceID id)357 const PaDeviceInfo* Pa_GetDeviceInfo( PaDeviceID id )
358 {
359 #define NUM_STANDARDSAMPLINGRATES   3   /* 11025, 22050, 44100 */
360     static DWORD customSamplingRates[] = { 8000, 32000, 48000, 64000, 88200, 96000 };
361 #define NUM_CUSTOMSAMPLINGRATES     (sizeof(customSamplingRates)/sizeof(DWORD))
362 #define MAX_NUMSAMPLINGRATES        (NUM_STANDARDSAMPLINGRATES+NUM_CUSTOMSAMPLINGRATES)
363 
364     PaDeviceInfo *deviceInfo;
365     double *sampleRates; /* non-const ptr */
366     int i;
367     char *s;
368 
369     DBUG(( "Pa_GetDeviceInfo( %d )\n", id ));
370     if( id < 0 || id >= sNumDevices )
371         return NULL;
372     if( sDevicePtrs[ id ] != NULL )
373     {
374         return sDevicePtrs[ id ];
375     }
376     deviceInfo = (PaDeviceInfo *)PaHost_AllocateTrackedMemory( sizeof(PaDeviceInfo) ); /* MEM */
377     if( deviceInfo == NULL ) return NULL;
378     deviceInfo->structVersion = 1;
379     deviceInfo->maxInputChannels = 0;
380     deviceInfo->maxOutputChannels = 0;
381     deviceInfo->numSampleRates = 0;
382     sampleRates = (double*)PaHost_AllocateTrackedMemory( MAX_NUMSAMPLINGRATES * sizeof(double) ); /* MEM */
383     deviceInfo->sampleRates = sampleRates;
384     deviceInfo->nativeSampleFormats = paInt16;       /* should query for higher bit depths below */
385     if( id < sNumInputDevices )
386     {
387         /* input device */
388         int inputMmID = PaDeviceIdToWinId(id);
389         WAVEINCAPS wic;
390         if( waveInGetDevCaps( inputMmID, &wic, sizeof( WAVEINCAPS ) ) != MMSYSERR_NOERROR )
391             goto error;
392 
393         /* Append I/O suffix to WAVE_MAPPER device. */
394         if( inputMmID == WAVE_MAPPER )
395         {
396             s = (char *) PaHost_AllocateTrackedMemory( strlen( wic.szPname ) + 1 + sizeof(sMapperSuffixInput) ); /* MEM */
397             strcpy( s, wic.szPname );
398             strcat( s, sMapperSuffixInput );
399         }
400         else
401         {
402             s = (char *) PaHost_AllocateTrackedMemory( strlen( wic.szPname ) + 1 ); /* MEM */
403             strcpy( s, wic.szPname );
404         }
405         deviceInfo->name = s;
406         deviceInfo->maxInputChannels = wic.wChannels;
407         DBUG(( "Pa_GetDeviceInfo: input %s, maxChannels = %d\n", deviceInfo->name, deviceInfo->maxInputChannels ));
408         /* Sometimes a device can return a rediculously large number of channels.
409          * This happened with an SBLive card on a Windows ME box.
410          * If that happens, then force it to 2 channels.  PLB20010413
411          */
412         if( (deviceInfo->maxInputChannels < 1) || (deviceInfo->maxInputChannels > 256) )
413         {
414             ERR_RPT(("Pa_GetDeviceInfo: Num input channels reported as %d! Changed to 2.\n", deviceInfo->maxOutputChannels ));
415             deviceInfo->maxInputChannels = 2;
416         }
417         /* Add a sample rate to the list if we can do stereo 16 bit at that rate
418          * based on the format flags. */
419         if( wic.dwFormats & WAVE_FORMAT_1M16 ||wic.dwFormats & WAVE_FORMAT_1S16 )
420             sampleRates[ deviceInfo->numSampleRates++ ] = 11025.;
421         if( wic.dwFormats & WAVE_FORMAT_2M16 ||wic.dwFormats & WAVE_FORMAT_2S16 )
422             sampleRates[ deviceInfo->numSampleRates++ ] = 22050.;
423         if( wic.dwFormats & WAVE_FORMAT_4M16 ||wic.dwFormats & WAVE_FORMAT_4S16 )
424             sampleRates[ deviceInfo->numSampleRates++ ] = 44100.;
425         /* Add a sample rate to the list if we can do stereo 16 bit at that rate
426          * based on opening the device successfully. */
427         for( i=0; i < NUM_CUSTOMSAMPLINGRATES; i++ )
428         {
429             WAVEFORMATEX wfx;
430             wfx.wFormatTag = WAVE_FORMAT_PCM;
431             wfx.nSamplesPerSec = customSamplingRates[i];
432             wfx.wBitsPerSample = 16;
433             wfx.cbSize = 0; /* ignored */
434             wfx.nChannels = (WORD)deviceInfo->maxInputChannels;
435             wfx.nAvgBytesPerSec = wfx.nChannels * wfx.nSamplesPerSec * sizeof(short);
436             wfx.nBlockAlign = (WORD)(wfx.nChannels * sizeof(short));
437             if( waveInOpen( NULL, inputMmID, &wfx, 0, 0, WAVE_FORMAT_QUERY ) == MMSYSERR_NOERROR )
438             {
439                 sampleRates[ deviceInfo->numSampleRates++ ] = customSamplingRates[i];
440             }
441         }
442 
443     }
444     else if( id - sNumInputDevices < sNumOutputDevices )
445     {
446         /* output device */
447         int outputMmID = PaDeviceIdToWinId(id);
448         WAVEOUTCAPS woc;
449         if( waveOutGetDevCaps( outputMmID, &woc, sizeof( WAVEOUTCAPS ) ) != MMSYSERR_NOERROR )
450             goto error;
451         /* Append I/O suffix to WAVE_MAPPER device. */
452         if( outputMmID == WAVE_MAPPER )
453         {
454             s = (char *) PaHost_AllocateTrackedMemory( strlen( woc.szPname ) + 1 + sizeof(sMapperSuffixOutput) );  /* MEM */
455             strcpy( s, woc.szPname );
456             strcat( s, sMapperSuffixOutput );
457         }
458         else
459         {
460             s = (char *) PaHost_AllocateTrackedMemory( strlen( woc.szPname ) + 1 );  /* MEM */
461             strcpy( s, woc.szPname );
462         }
463         deviceInfo->name = s;
464         deviceInfo->maxOutputChannels = woc.wChannels;
465         DBUG(( "Pa_GetDeviceInfo: output %s, maxChannels = %d\n", deviceInfo->name, deviceInfo->maxOutputChannels ));
466         /* Sometimes a device can return a rediculously large number of channels.
467          * This happened with an SBLive card on a Windows ME box.
468          * It also happens on Win XP!
469          */
470         if( (deviceInfo->maxOutputChannels < 1) || (deviceInfo->maxOutputChannels > 256) )
471         {
472 #if 1
473             deviceInfo->maxOutputChannels = 2;
474 #else
475         /* If channel max is goofy, then query for max channels. PLB20020228
476          * This doesn't seem to help. Disable code for now. Remove it later.
477          */
478             ERR_RPT(("Pa_GetDeviceInfo: Num output channels reported as %d!", deviceInfo->maxOutputChannels ));
479             deviceInfo->maxOutputChannels = 0;
480 			/* Attempt to find the correct maximum by querying the device. */
481 			for( i=2; i<16; i += 2 )
482 			{
483 				WAVEFORMATEX wfx;
484 				wfx.wFormatTag = WAVE_FORMAT_PCM;
485 				wfx.nSamplesPerSec = 44100;
486 				wfx.wBitsPerSample = 16;
487 				wfx.cbSize = 0; /* ignored */
488 				wfx.nChannels = (WORD) i;
489 				wfx.nAvgBytesPerSec = wfx.nChannels * wfx.nSamplesPerSec * sizeof(short);
490 				wfx.nBlockAlign = (WORD)(wfx.nChannels * sizeof(short));
491 				if( waveOutOpen( NULL, outputMmID, &wfx, 0, 0, WAVE_FORMAT_QUERY ) == MMSYSERR_NOERROR )
492 				{
493 					deviceInfo->maxOutputChannels = i;
494 				}
495 				else
496 				{
497 					break;
498 				}
499 			}
500 #endif
501             ERR_RPT((" Changed to %d.\n", deviceInfo->maxOutputChannels ));
502         }
503 
504         /* Add a sample rate to the list if we can do stereo 16 bit at that rate
505          * based on the format flags. */
506         if( woc.dwFormats & WAVE_FORMAT_1M16 ||woc.dwFormats & WAVE_FORMAT_1S16 )
507             sampleRates[ deviceInfo->numSampleRates++ ] = 11025.;
508         if( woc.dwFormats & WAVE_FORMAT_2M16 ||woc.dwFormats & WAVE_FORMAT_2S16 )
509             sampleRates[ deviceInfo->numSampleRates++ ] = 22050.;
510         if( woc.dwFormats & WAVE_FORMAT_4M16 ||woc.dwFormats & WAVE_FORMAT_4S16 )
511             sampleRates[ deviceInfo->numSampleRates++ ] = 44100.;
512 
513         /* Add a sample rate to the list if we can do stereo 16 bit at that rate
514          * based on opening the device successfully. */
515         for( i=0; i < NUM_CUSTOMSAMPLINGRATES; i++ )
516         {
517             WAVEFORMATEX wfx;
518             wfx.wFormatTag = WAVE_FORMAT_PCM;
519             wfx.nSamplesPerSec = customSamplingRates[i];
520             wfx.wBitsPerSample = 16;
521             wfx.cbSize = 0; /* ignored */
522             wfx.nChannels = (WORD)deviceInfo->maxOutputChannels;
523             wfx.nAvgBytesPerSec = wfx.nChannels * wfx.nSamplesPerSec * sizeof(short);
524             wfx.nBlockAlign = (WORD)(wfx.nChannels * sizeof(short));
525             DBUG(( "Pa_GetDeviceInfo: waveOutOpen( ... WAVE_FORMAT_QUERY at SR = %d\n", customSamplingRates[i] ));
526             if( waveOutOpen( NULL, outputMmID, &wfx, 0, 0, WAVE_FORMAT_QUERY ) == MMSYSERR_NOERROR )
527             {
528                 sampleRates[ deviceInfo->numSampleRates++ ] = customSamplingRates[i];
529             }
530         }
531     }
532     DBUG(( "Pa_GetDeviceInfo: done.\n" ));
533     sDevicePtrs[ id ] = deviceInfo;
534     return deviceInfo;
535 
536 error:
537     PaHost_FreeTrackedMemory( sampleRates ); /* MEM */
538     PaHost_FreeTrackedMemory( deviceInfo ); /* MEM */
539 
540     return NULL;
541 }
542 /*************************************************************************
543  * Returns recommended device ID.
544  * On the PC, the recommended device can be specified by the user by
545  * setting an environment variable. For example, to use device #1.
546  *
547  *    set PA_RECOMMENDED_OUTPUT_DEVICE=1
548  *
549  * The user should first determine the available device ID by using
550  * the supplied application "pa_devs".
551  */
552 #define PA_ENV_BUF_SIZE  (32)
553 #define PA_REC_IN_DEV_ENV_NAME  ("PA_RECOMMENDED_INPUT_DEVICE")
554 #define PA_REC_OUT_DEV_ENV_NAME  ("PA_RECOMMENDED_OUTPUT_DEVICE")
PaHost_GetEnvDefaultDeviceID(char * envName)555 static PaDeviceID PaHost_GetEnvDefaultDeviceID( char *envName )
556 {
557     DWORD   hresult;
558     char    envbuf[PA_ENV_BUF_SIZE];
559     PaDeviceID recommendedID = paNoDevice;
560 
561     /* Let user determine default device by setting environment variable. */
562     hresult = GetEnvironmentVariable( envName, envbuf, PA_ENV_BUF_SIZE );
563     if( (hresult > 0) && (hresult < PA_ENV_BUF_SIZE) )
564     {
565         recommendedID = atoi( envbuf );
566     }
567     return recommendedID;
568 }
569 /**********************************************************************
570  * Check for environment variable, else query devices and use result.
571  */
Pa_GetDefaultInputDeviceID(void)572 PaDeviceID Pa_GetDefaultInputDeviceID( void )
573 {
574     PaDeviceID result;
575 
576     result = PaHost_GetEnvDefaultDeviceID( PA_REC_IN_DEV_ENV_NAME );
577     if( result == paNoDevice || result < 0 || result >= sNumInputDevices )
578     {
579         result = sDefaultInputDeviceID;
580     }
581     return result;
582 }
Pa_GetDefaultOutputDeviceID(void)583 PaDeviceID Pa_GetDefaultOutputDeviceID( void )
584 {
585     PaDeviceID result;
586 
587     result = PaHost_GetEnvDefaultDeviceID( PA_REC_OUT_DEV_ENV_NAME );
588     if( result == paNoDevice || result < sNumInputDevices || result >= sNumDevices )
589     {
590         result = sDefaultOutputDeviceID;
591     }
592     return result;
593 }
594 /**********************************************************************
595  * Initialize Host dependant part of API.
596  */
PaHost_Init(void)597 PaError PaHost_Init( void )
598 {
599 
600 #if PA_TRACK_MEMORY
601     PRINT(("PaHost_Init: sNumAllocations = %d\n", sNumAllocations ));
602 #endif
603 
604 #if PA_SIMULATE_UNDERFLOW
605     PRINT(("WARNING - Underflow Simulation Enabled - Expect a Big Glitch!!!\n"));
606 #endif
607 
608 
609     Pa_InitializeNumDevices();
610 
611     return Pa_AllocateDevicePtrs();
612 }
613 
614 /**********************************************************************
615  * Check WAVE buffers to see if they are done.
616  * Fill any available output buffers and use any available
617  * input buffers by calling user callback.
618  *
619  * This routine will loop until:
620  *    user callback returns !=0 OR
621  *    all output buffers are filled OR
622  *    past->past_StopSoon is set OR
623  *    an error occurs when calling WMME.
624  *
625  * Returns >0 when user requests a stop, <0 on error.
626  *
627  */
Pa_TimeSlice(internalPortAudioStream * stream)628 static PaError Pa_TimeSlice( internalPortAudioStream *stream )
629 {
630     PaError           result = paNoError;
631     MMRESULT          mmresult;
632     char             *inBufPtr;
633     char             *outBufPtr;
634     int               gotInput = 0;
635     int               gotOutput = 0;
636     int               i;
637     int               buffersProcessed = 0;
638     int               done = 0;
639     PaWMMEStreamData *wmmeStreamData = (PaWMMEStreamData *) stream->past_DeviceData;
640 
641     if( wmmeStreamData == NULL ) return paInternalError;
642 
643     stream->past_NumCallbacks += 1;
644 #if PA_TRACE_RUN
645     AddTraceMessage("Pa_TimeSlice: past_NumCallbacks ", stream->past_NumCallbacks );
646 #endif
647 
648     /* JM20020118 - prevent hung thread when buffers underflow. */
649     /* while( !done ) /* BAD */
650     while( !done && !stream->past_StopSoon ) /* GOOD */
651     {
652 #if PA_SIMULATE_UNDERFLOW
653         if(gUnderCallbackCounter++ == UNDER_SLEEP_AT)
654         {
655             Sleep(UNDER_SLEEP_FOR);
656         }
657 #endif
658 
659         /* If we are using output, then we need an empty output buffer. */
660         gotOutput = 0;
661         outBufPtr = NULL;
662         if( stream->past_NumOutputChannels > 0 )
663         {
664             if((wmmeStreamData->outputBuffers[ wmmeStreamData->currentOutputBuffer ].dwFlags & WHDR_DONE) == 0)
665             {
666                 break;  /* If none empty then bail and try again later. */
667             }
668             else
669             {
670                 outBufPtr = wmmeStreamData->outputBuffers[ wmmeStreamData->currentOutputBuffer ].lpData;
671                 gotOutput = 1;
672             }
673         }
674         /* Use an input buffer if one is available. */
675         gotInput = 0;
676         inBufPtr = NULL;
677         if( ( stream->past_NumInputChannels > 0 ) &&
678                 (wmmeStreamData->inputBuffers[ wmmeStreamData->currentInputBuffer ].dwFlags & WHDR_DONE) )
679         {
680             inBufPtr = wmmeStreamData->inputBuffers[ wmmeStreamData->currentInputBuffer ].lpData;
681             gotInput = 1;
682 #if PA_TRACE_RUN
683             AddTraceMessage("Pa_TimeSlice: got input buffer at ", (int)inBufPtr );
684             AddTraceMessage("Pa_TimeSlice: got input buffer # ", wmmeStreamData->currentInputBuffer );
685 #endif
686 
687         }
688         /* If we can't do anything then bail out. */
689         if( !gotInput && !gotOutput ) break;
690         buffersProcessed += 1;
691         /* Each Wave buffer contains multiple user buffers so do them all now. */
692         /* Base Usage on time it took to process one host buffer. */
693         Pa_StartUsageCalculation( stream );
694         for( i=0; i<wmmeStreamData->userBuffersPerHostBuffer; i++ )
695         {
696             if( done )
697             {
698                 if( gotOutput )
699                 {
700                     /* Clear remainder of wave buffer if we are waiting for stop. */
701                     AddTraceMessage("Pa_TimeSlice: zero rest of wave buffer ", i );
702                     memset( outBufPtr, 0, wmmeStreamData->bytesPerUserOutputBuffer );
703                 }
704             }
705             else
706             {
707                 /* Convert 16 bit native data to user data and call user routine. */
708                 result = Pa_CallConvertInt16( stream, (short *) inBufPtr, (short *) outBufPtr );
709                 if( result != 0) done = 1;
710             }
711             if( gotInput ) inBufPtr += wmmeStreamData->bytesPerUserInputBuffer;
712             if( gotOutput) outBufPtr += wmmeStreamData->bytesPerUserOutputBuffer;
713         }
714         Pa_EndUsageCalculation( stream );
715         /* Send WAVE buffer to Wave Device to be refilled. */
716         if( gotInput )
717         {
718             mmresult = waveInAddBuffer( wmmeStreamData->hWaveIn,
719                                        &wmmeStreamData->inputBuffers[ wmmeStreamData->currentInputBuffer ],
720                                        sizeof(WAVEHDR) );
721             if( mmresult != MMSYSERR_NOERROR )
722             {
723                 sPaHostError = mmresult;
724                 result = paHostError;
725                 break;
726             }
727             wmmeStreamData->currentInputBuffer = (wmmeStreamData->currentInputBuffer+1 >= wmmeStreamData->numHostBuffers) ?
728                                               0 : wmmeStreamData->currentInputBuffer+1;
729         }
730         /* Write WAVE buffer to Wave Device. */
731         if( gotOutput )
732         {
733 #if PA_TRACE_START_STOP
734             AddTraceMessage( "Pa_TimeSlice: writing buffer ", wmmeStreamData->currentOutputBuffer );
735 #endif
736             mmresult = waveOutWrite( wmmeStreamData->hWaveOut,
737                                     &wmmeStreamData->outputBuffers[ wmmeStreamData->currentOutputBuffer ],
738                                     sizeof(WAVEHDR) );
739             if( mmresult != MMSYSERR_NOERROR )
740             {
741                 sPaHostError = mmresult;
742                 result = paHostError;
743                 break;
744             }
745             wmmeStreamData->currentOutputBuffer = (wmmeStreamData->currentOutputBuffer+1 >= wmmeStreamData->numHostBuffers) ?
746                                                0 : wmmeStreamData->currentOutputBuffer+1;
747         }
748 
749     }
750 
751 #if PA_TRACE_RUN
752     AddTraceMessage("Pa_TimeSlice: buffersProcessed ", buffersProcessed );
753 #endif
754     return (result != 0) ? result : done;
755 }
756 
757 /*******************************************************************/
PaHost_BackgroundManager(internalPortAudioStream * stream)758 static PaError PaHost_BackgroundManager( internalPortAudioStream *stream )
759 {
760     PaError      result = paNoError;
761     int          i;
762     int          numQueuedoutputBuffers = 0;
763     PaWMMEStreamData *wmmeStreamData = (PaWMMEStreamData *) stream->past_DeviceData;
764 
765     /* Has someone asked us to abort by calling Pa_AbortStream()? */
766     if( stream->past_StopNow )
767     {
768         stream->past_IsActive = 0; /* Will cause thread to return. */
769     }
770     /* Has someone asked us to stop by calling Pa_StopStream()
771      * OR has a user callback returned '1' to indicate finished.
772      */
773     else if( stream->past_StopSoon )
774     {
775         /* Poll buffer and when all have played then exit thread. */
776         /* Count how many output buffers are queued. */
777         numQueuedoutputBuffers = 0;
778         if( stream->past_NumOutputChannels > 0 )
779         {
780             for( i=0; i<wmmeStreamData->numHostBuffers; i++ )
781             {
782                 if( !( wmmeStreamData->outputBuffers[ i ].dwFlags & WHDR_DONE) )
783                 {
784 #if PA_TRACE_START_STOP
785                     AddTraceMessage( "PaHost_BackgroundManager: waiting for buffer ", i );
786 #endif
787                     numQueuedoutputBuffers++;
788                 }
789             }
790         }
791 #if PA_TRACE_START_STOP
792         AddTraceMessage( "PaHost_BackgroundManager: numQueuedoutputBuffers ", numQueuedoutputBuffers );
793 #endif
794         if( numQueuedoutputBuffers == 0 )
795         {
796             stream->past_IsActive = 0; /* Will cause thread to return. */
797         }
798     }
799     else
800     {
801         /* Process full input buffer and fill up empty output buffers. */
802         if( (result = Pa_TimeSlice( stream )) != 0)
803         {
804             /* User callback has asked us to stop. */
805 #if PA_TRACE_START_STOP
806             AddTraceMessage( "PaHost_BackgroundManager: TimeSlice() returned ", result );
807 #endif
808             stream->past_StopSoon = 1; /* Request that audio play out then stop. */
809             result = paNoError;
810         }
811     }
812 
813     PaHost_UpdateStreamTime( wmmeStreamData );
814     return result;
815 }
816 
817 #if PA_USE_TIMER_CALLBACK
818 /*******************************************************************/
Pa_TimerCallback(UINT uID,UINT uMsg,DWORD dwUser,DWORD dw1,DWORD dw2)819 static void CALLBACK Pa_TimerCallback(UINT uID, UINT uMsg, DWORD dwUser, DWORD dw1, DWORD dw2)
820 {
821     internalPortAudioStream   *stream;
822     PaWMMEStreamData        *wmmeStreamData;
823     PaError                    result;
824 
825     stream = (internalPortAudioStream *) dwUser;
826     if( stream == NULL ) return;
827     wmmeStreamData = (PaWMMEStreamData *) stream->past_DeviceData;
828     if( wmmeStreamData == NULL ) return;
829     if( wmmeStreamData->ifInsideCallback )
830     {
831         if( wmmeStreamData->timerID != 0 )
832         {
833             timeKillEvent(wmmeStreamData->timerID);  /* Stop callback timer. */
834             wmmeStreamData->timerID = 0;
835         }
836         return;
837     }
838     wmmeStreamData->ifInsideCallback = 1;
839     /* Manage flags and audio processing. */
840     result = PaHost_BackgroundManager( stream );
841     if( result != paNoError )
842     {
843         stream->past_IsActive = 0;
844     }
845     wmmeStreamData->ifInsideCallback = 0;
846 }
847 #else /* PA_USE_TIMER_CALLBACK */
848 /*******************************************************************/
WinMMPa_OutputThreadProc(void * pArg)849 static DWORD WINAPI WinMMPa_OutputThreadProc( void *pArg )
850 {
851     internalPortAudioStream *stream;
852     PaWMMEStreamData      *wmmeStreamData;
853     HANDLE       events[2];
854     int          numEvents = 0;
855     DWORD        result = 0;
856     DWORD        waitResult;
857     DWORD        numTimeouts = 0;
858     DWORD        timeOut;
859     stream = (internalPortAudioStream *) pArg;
860     wmmeStreamData = (PaWMMEStreamData *) stream->past_DeviceData;
861 #if PA_TRACE_START_STOP
862     AddTraceMessage( "WinMMPa_OutputThreadProc: timeoutPeriod", timeoutPeriod );
863     AddTraceMessage( "WinMMPa_OutputThreadProc: past_NumUserBuffers", stream->past_NumUserBuffers );
864 #endif
865     /* Calculate timeOut as half the time it would take to play all buffers. */
866     timeOut = (DWORD) (500.0 * PaHost_GetTotalBufferFrames( stream ) / stream->past_SampleRate);
867     /* Get event(s) ready for wait. */
868     events[numEvents++] = wmmeStreamData->bufferEvent;
869     if( wmmeStreamData->abortEventInited ) events[numEvents++] = wmmeStreamData->abortEvent;
870     /* Stay in this thread as long as we are "active". */
871     while( stream->past_IsActive )
872     {
873         /*******************************************************************/
874         /******** WAIT here for an event from WMME or PA *******************/
875         /*******************************************************************/
876         waitResult = WaitForMultipleObjects( numEvents, events, FALSE, timeOut );
877         /* Error? */
878         if( waitResult == WAIT_FAILED )
879         {
880             sPaHostError = GetLastError();
881             result = paHostError;
882             stream->past_IsActive = 0;
883         }
884         /* Timeout? Don't stop. Just keep polling for DONE.*/
885         else if( waitResult == WAIT_TIMEOUT )
886         {
887 #if PA_TRACE_START_STOP
888             AddTraceMessage( "WinMMPa_OutputThreadProc: timed out ", numQueuedoutputBuffers );
889 #endif
890             numTimeouts += 1;
891         }
892         /* Manage flags and audio processing. */
893         result = PaHost_BackgroundManager( stream );
894         if( result != paNoError )
895         {
896             stream->past_IsActive = 0;
897         }
898     }
899     return result;
900 }
901 #endif
902 
903 /*******************************************************************/
PaHost_OpenInputStream(internalPortAudioStream * stream)904 PaError PaHost_OpenInputStream( internalPortAudioStream   *stream )
905 {
906     PaError          result = paNoError;
907     MMRESULT         mmresult;
908     PaWMMEStreamData *wmmeStreamData;
909     int              i;
910     int              inputMmId;
911     int              bytesPerInputFrame;
912     WAVEFORMATEX     wfx;
913     const PaDeviceInfo  *deviceInfo;
914 
915     wmmeStreamData = (PaWMMEStreamData *) stream->past_DeviceData;
916     DBUG(("PaHost_OpenStream: deviceID = 0x%x\n", stream->past_InputDeviceID));
917     deviceInfo = Pa_GetDeviceInfo( stream->past_InputDeviceID );
918     if( deviceInfo == NULL ) return paInternalError;
919 
920     switch( deviceInfo->nativeSampleFormats  )
921     {
922     case paInt32:
923     case paFloat32:
924         bytesPerInputFrame = sizeof(float) * stream->past_NumInputChannels;
925         break;
926     default:
927         bytesPerInputFrame = sizeof(short) * stream->past_NumInputChannels;
928         break;
929     }
930     wfx.wFormatTag = WAVE_FORMAT_PCM;
931     wfx.nChannels = (WORD) stream->past_NumInputChannels;
932     wfx.nSamplesPerSec = (DWORD) stream->past_SampleRate;
933     wfx.nAvgBytesPerSec = (DWORD)(bytesPerInputFrame * stream->past_SampleRate);
934     wfx.nBlockAlign = (WORD)bytesPerInputFrame;
935     wfx.wBitsPerSample = (WORD)((bytesPerInputFrame/stream->past_NumInputChannels) * 8);
936     wfx.cbSize = 0;
937     inputMmId = PaDeviceIdToWinId( stream->past_InputDeviceID );
938 #if PA_USE_TIMER_CALLBACK
939     mmresult = waveInOpen( &wmmeStreamData->hWaveIn, inputMmId, &wfx,
940                      0, 0, CALLBACK_NULL );
941 #else
942     mmresult = waveInOpen( &wmmeStreamData->hWaveIn, inputMmId, &wfx,
943                      (DWORD)wmmeStreamData->bufferEvent, (DWORD) stream, CALLBACK_EVENT );
944 #endif
945     if( mmresult != MMSYSERR_NOERROR )
946     {
947         ERR_RPT(("PortAudio: PaHost_OpenInputStream() failed!\n"));
948         result = paHostError;
949         sPaHostError = mmresult;
950         goto error;
951     }
952     /* Allocate an array to hold the buffer pointers. */
953     wmmeStreamData->inputBuffers = (WAVEHDR *) PaHost_AllocateTrackedMemory( sizeof(WAVEHDR)*wmmeStreamData->numHostBuffers ); /* MEM */
954     if( wmmeStreamData->inputBuffers == NULL )
955     {
956         result = paInsufficientMemory;
957         goto error;
958     }
959     /* Allocate each buffer. */
960     for( i=0; i<wmmeStreamData->numHostBuffers; i++ )
961     {
962         wmmeStreamData->inputBuffers[i].lpData = (char *)PaHost_AllocateTrackedMemory( wmmeStreamData->bytesPerHostInputBuffer ); /* MEM */
963         if( wmmeStreamData->inputBuffers[i].lpData == NULL )
964         {
965             result = paInsufficientMemory;
966             goto error;
967         }
968         wmmeStreamData->inputBuffers[i].dwBufferLength = wmmeStreamData->bytesPerHostInputBuffer;
969         wmmeStreamData->inputBuffers[i].dwUser = i;
970         if( ( mmresult = waveInPrepareHeader( wmmeStreamData->hWaveIn, &wmmeStreamData->inputBuffers[i], sizeof(WAVEHDR) )) != MMSYSERR_NOERROR )
971         {
972             result = paHostError;
973             sPaHostError = mmresult;
974             goto error;
975         }
976     }
977     return result;
978 
979 error:
980     return result;
981 }
982 /*******************************************************************/
PaHost_OpenOutputStream(internalPortAudioStream * stream)983 PaError PaHost_OpenOutputStream( internalPortAudioStream *stream )
984 {
985     PaError          result = paNoError;
986     MMRESULT         mmresult;
987     PaWMMEStreamData *wmmeStreamData;
988     int              i;
989     int              outputMmID;
990     int              bytesPerOutputFrame;
991     WAVEFORMATEX     wfx;
992     const PaDeviceInfo *deviceInfo;
993 
994     wmmeStreamData = (PaWMMEStreamData *) stream->past_DeviceData;
995     DBUG(("PaHost_OpenStream: deviceID = 0x%x\n", stream->past_OutputDeviceID));
996 
997     deviceInfo = Pa_GetDeviceInfo( stream->past_OutputDeviceID );
998     if( deviceInfo == NULL ) return paInternalError;
999 
1000     switch( deviceInfo->nativeSampleFormats  )
1001     {
1002     case paInt32:
1003     case paFloat32:
1004         bytesPerOutputFrame = sizeof(float) * stream->past_NumOutputChannels;
1005         break;
1006     default:
1007         bytesPerOutputFrame = sizeof(short) * stream->past_NumOutputChannels;
1008         break;
1009     }
1010     wfx.wFormatTag = WAVE_FORMAT_PCM;
1011     wfx.nChannels = (WORD) stream->past_NumOutputChannels;
1012     wfx.nSamplesPerSec = (DWORD) stream->past_SampleRate;
1013     wfx.nAvgBytesPerSec = (DWORD)(bytesPerOutputFrame * stream->past_SampleRate);
1014     wfx.nBlockAlign = (WORD)bytesPerOutputFrame;
1015     wfx.wBitsPerSample = (WORD)((bytesPerOutputFrame/stream->past_NumOutputChannels) * 8);
1016     wfx.cbSize = 0;
1017     outputMmID = PaDeviceIdToWinId( stream->past_OutputDeviceID );
1018 #if PA_USE_TIMER_CALLBACK
1019     mmresult = waveOutOpen( &wmmeStreamData->hWaveOut, outputMmID, &wfx,
1020                       0, 0, CALLBACK_NULL );
1021 #else
1022 
1023     wmmeStreamData->abortEvent = CreateEvent( NULL, TRUE, FALSE, NULL );
1024     if( wmmeStreamData->abortEvent == NULL )
1025     {
1026         result = paHostError;
1027         sPaHostError = GetLastError();
1028         goto error;
1029     }
1030     wmmeStreamData->abortEventInited = 1;
1031     mmresult = waveOutOpen( &wmmeStreamData->hWaveOut, outputMmID, &wfx,
1032                       (DWORD)wmmeStreamData->bufferEvent, (DWORD) stream, CALLBACK_EVENT );
1033 #endif
1034     if( mmresult != MMSYSERR_NOERROR )
1035     {
1036         ERR_RPT(("PortAudio: PaHost_OpenOutputStream() failed!\n"));
1037         result = paHostError;
1038         sPaHostError = mmresult;
1039         goto error;
1040     }
1041     /* Allocate an array to hold the buffer pointers. */
1042     wmmeStreamData->outputBuffers = (WAVEHDR *) PaHost_AllocateTrackedMemory( sizeof(WAVEHDR)*wmmeStreamData->numHostBuffers ); /* MEM */
1043     if( wmmeStreamData->outputBuffers == NULL )
1044     {
1045         result = paInsufficientMemory;
1046         goto error;
1047     }
1048     /* Allocate each buffer. */
1049     for( i=0; i<wmmeStreamData->numHostBuffers; i++ )
1050     {
1051         wmmeStreamData->outputBuffers[i].lpData = (char *) PaHost_AllocateTrackedMemory( wmmeStreamData->bytesPerHostOutputBuffer ); /* MEM */
1052         if( wmmeStreamData->outputBuffers[i].lpData == NULL )
1053         {
1054             result = paInsufficientMemory;
1055             goto error;
1056         }
1057         wmmeStreamData->outputBuffers[i].dwBufferLength = wmmeStreamData->bytesPerHostOutputBuffer;
1058         wmmeStreamData->outputBuffers[i].dwUser = i;
1059         if( (mmresult = waveOutPrepareHeader( wmmeStreamData->hWaveOut, &wmmeStreamData->outputBuffers[i], sizeof(WAVEHDR) )) != MMSYSERR_NOERROR )
1060         {
1061             result = paHostError;
1062             sPaHostError = mmresult;
1063             goto error;
1064         }
1065     }
1066     return result;
1067 
1068 error:
1069     return result;
1070 }
1071 /*******************************************************************/
PaHost_GetTotalBufferFrames(internalPortAudioStream * stream)1072 PaError PaHost_GetTotalBufferFrames( internalPortAudioStream *stream )
1073 {
1074     PaWMMEStreamData *wmmeStreamData = (PaWMMEStreamData *) stream->past_DeviceData;
1075     return wmmeStreamData->numHostBuffers * wmmeStreamData->framesPerHostBuffer;
1076 }
1077 /*******************************************************************
1078  * Determine number of WAVE Buffers
1079  * and how many User Buffers we can put into each WAVE buffer.
1080  */
PaHost_CalcNumHostBuffers(internalPortAudioStream * stream)1081 static void PaHost_CalcNumHostBuffers( internalPortAudioStream *stream )
1082 {
1083     PaWMMEStreamData *wmmeStreamData = (PaWMMEStreamData *) stream->past_DeviceData;
1084     unsigned int  minNumBuffers;
1085     int           minframesPerHostBuffer;
1086     int           maxframesPerHostBuffer;
1087     int           minTotalFrames;
1088     int           userBuffersPerHostBuffer;
1089     int           framesPerHostBuffer;
1090     int           numHostBuffers;
1091 
1092     /* Calculate minimum and maximum sizes based on timing and sample rate. */
1093     minframesPerHostBuffer = (int) (PA_MIN_MSEC_PER_HOST_BUFFER * stream->past_SampleRate * 0.001);
1094     minframesPerHostBuffer = (minframesPerHostBuffer + 7) & ~7;
1095     DBUG(("PaHost_CalcNumHostBuffers: minframesPerHostBuffer = %d\n", minframesPerHostBuffer ));
1096     maxframesPerHostBuffer = (int) (PA_MAX_MSEC_PER_HOST_BUFFER * stream->past_SampleRate * 0.001);
1097     maxframesPerHostBuffer = (maxframesPerHostBuffer + 7) & ~7;
1098     DBUG(("PaHost_CalcNumHostBuffers: maxframesPerHostBuffer = %d\n", maxframesPerHostBuffer ));
1099     /* Determine number of user buffers based on minimum latency. */
1100     minNumBuffers = Pa_GetMinNumBuffers( stream->past_FramesPerUserBuffer, stream->past_SampleRate );
1101     stream->past_NumUserBuffers = ( minNumBuffers > stream->past_NumUserBuffers ) ? minNumBuffers : stream->past_NumUserBuffers;
1102     DBUG(("PaHost_CalcNumHostBuffers: min past_NumUserBuffers = %d\n", stream->past_NumUserBuffers ));
1103     minTotalFrames = stream->past_NumUserBuffers * stream->past_FramesPerUserBuffer;
1104     /* We cannot make the WAVE buffers too small because they may not get serviced quickly enough. */
1105     if( (int) stream->past_FramesPerUserBuffer < minframesPerHostBuffer )
1106     {
1107         userBuffersPerHostBuffer =
1108             (minframesPerHostBuffer + stream->past_FramesPerUserBuffer - 1) /
1109             stream->past_FramesPerUserBuffer;
1110     }
1111     else
1112     {
1113         userBuffersPerHostBuffer = 1;
1114     }
1115     framesPerHostBuffer = stream->past_FramesPerUserBuffer * userBuffersPerHostBuffer;
1116     /* Calculate number of WAVE buffers needed. Round up to cover minTotalFrames. */
1117     numHostBuffers = (minTotalFrames + framesPerHostBuffer - 1) / framesPerHostBuffer;
1118     /* Make sure we have anough WAVE buffers. */
1119     if( numHostBuffers < PA_MIN_NUM_HOST_BUFFERS)
1120     {
1121         numHostBuffers = PA_MIN_NUM_HOST_BUFFERS;
1122     }
1123     else if( (numHostBuffers > PA_MAX_NUM_HOST_BUFFERS) &&
1124              ((int) stream->past_FramesPerUserBuffer < (maxframesPerHostBuffer/2) ) )
1125     {
1126         /* If we have too many WAVE buffers, try to put more user buffers in a wave buffer. */
1127         while(numHostBuffers > PA_MAX_NUM_HOST_BUFFERS)
1128         {
1129             userBuffersPerHostBuffer += 1;
1130             framesPerHostBuffer = stream->past_FramesPerUserBuffer * userBuffersPerHostBuffer;
1131             numHostBuffers = (minTotalFrames + framesPerHostBuffer - 1) / framesPerHostBuffer;
1132             /* If we have gone too far, back up one. */
1133             if( (framesPerHostBuffer > maxframesPerHostBuffer) ||
1134                     (numHostBuffers < PA_MAX_NUM_HOST_BUFFERS) )
1135             {
1136                 userBuffersPerHostBuffer -= 1;
1137                 framesPerHostBuffer = stream->past_FramesPerUserBuffer * userBuffersPerHostBuffer;
1138                 numHostBuffers = (minTotalFrames + framesPerHostBuffer - 1) / framesPerHostBuffer;
1139                 break;
1140             }
1141         }
1142     }
1143 
1144     wmmeStreamData->userBuffersPerHostBuffer = userBuffersPerHostBuffer;
1145     wmmeStreamData->framesPerHostBuffer = framesPerHostBuffer;
1146     wmmeStreamData->numHostBuffers = numHostBuffers;
1147     DBUG(("PaHost_CalcNumHostBuffers: userBuffersPerHostBuffer = %d\n", wmmeStreamData->userBuffersPerHostBuffer ));
1148     DBUG(("PaHost_CalcNumHostBuffers: numHostBuffers = %d\n", wmmeStreamData->numHostBuffers ));
1149     DBUG(("PaHost_CalcNumHostBuffers: framesPerHostBuffer = %d\n", wmmeStreamData->framesPerHostBuffer ));
1150     DBUG(("PaHost_CalcNumHostBuffers: past_NumUserBuffers = %d\n", stream->past_NumUserBuffers ));
1151 }
1152 /*******************************************************************/
PaHost_OpenStream(internalPortAudioStream * stream)1153 PaError PaHost_OpenStream( internalPortAudioStream *stream )
1154 {
1155     PaError             result = paNoError;
1156     PaWMMEStreamData *wmmeStreamData;
1157 
1158     result = PaHost_AllocateWMMEStreamData( stream );
1159     if( result != paNoError ) return result;
1160 
1161     wmmeStreamData = PaHost_GetWMMEStreamData( stream );
1162 
1163     /* Figure out how user buffers fit into WAVE buffers. */
1164     PaHost_CalcNumHostBuffers( stream );
1165     {
1166         int msecLatency = (int) ((PaHost_GetTotalBufferFrames(stream) * 1000) / stream->past_SampleRate);
1167         DBUG(("PortAudio on WMME - Latency = %d frames, %d msec\n", PaHost_GetTotalBufferFrames(stream), msecLatency ));
1168     }
1169     InitializeCriticalSection( &wmmeStreamData->streamLock );
1170     wmmeStreamData->streamLockInited = 1;
1171 
1172 #if (PA_USE_TIMER_CALLBACK == 0)
1173     wmmeStreamData->bufferEventInited = 0;
1174     wmmeStreamData->bufferEvent = CreateEvent( NULL, FALSE, FALSE, NULL );
1175     if( wmmeStreamData->bufferEvent == NULL )
1176     {
1177         result = paHostError;
1178         sPaHostError = GetLastError();
1179         goto error;
1180     }
1181     wmmeStreamData->bufferEventInited = 1;
1182 #endif /* (PA_USE_TIMER_CALLBACK == 0) */
1183     /* ------------------ OUTPUT */
1184     wmmeStreamData->bytesPerUserOutputBuffer = stream->past_FramesPerUserBuffer * stream->past_NumOutputChannels * sizeof(short);
1185     wmmeStreamData->bytesPerHostOutputBuffer = wmmeStreamData->userBuffersPerHostBuffer * wmmeStreamData->bytesPerUserOutputBuffer;
1186     if( (stream->past_OutputDeviceID != paNoDevice) && (stream->past_NumOutputChannels > 0) )
1187     {
1188         result = PaHost_OpenOutputStream( stream );
1189         if( result < 0 ) goto error;
1190     }
1191     /* ------------------ INPUT */
1192     wmmeStreamData->bytesPerUserInputBuffer = stream->past_FramesPerUserBuffer * stream->past_NumInputChannels * sizeof(short);
1193     wmmeStreamData->bytesPerHostInputBuffer = wmmeStreamData->userBuffersPerHostBuffer * wmmeStreamData->bytesPerUserInputBuffer;
1194     if( (stream->past_InputDeviceID != paNoDevice) && (stream->past_NumInputChannels > 0) )
1195     {
1196         result = PaHost_OpenInputStream( stream );
1197         if( result < 0 ) goto error;
1198     }
1199 
1200     Pa_InitializeCpuUsageScalar( stream );
1201 
1202     return result;
1203 
1204 error:
1205     PaHost_CloseStream( stream );
1206     return result;
1207 }
1208 /*************************************************************************/
PaHost_StartOutput(internalPortAudioStream * stream)1209 PaError PaHost_StartOutput( internalPortAudioStream *stream )
1210 {
1211     PaError          result = paNoError;
1212     MMRESULT         mmresult;
1213     int              i;
1214     PaWMMEStreamData *wmmeStreamData = PaHost_GetWMMEStreamData( stream );
1215 
1216     if( wmmeStreamData == NULL ) return paInternalError;
1217 
1218     if( stream->past_OutputDeviceID != paNoDevice )
1219     {
1220         if( (mmresult = waveOutPause( wmmeStreamData->hWaveOut )) != MMSYSERR_NOERROR )
1221         {
1222             result = paHostError;
1223             sPaHostError = mmresult;
1224             goto error;
1225         }
1226         for( i=0; i<wmmeStreamData->numHostBuffers; i++ )
1227         {
1228             ZeroMemory( wmmeStreamData->outputBuffers[i].lpData, wmmeStreamData->outputBuffers[i].dwBufferLength );
1229             mmresult = waveOutWrite( wmmeStreamData->hWaveOut, &wmmeStreamData->outputBuffers[i], sizeof(WAVEHDR) );
1230             if( mmresult != MMSYSERR_NOERROR )
1231             {
1232                 result = paHostError;
1233                 sPaHostError = mmresult;
1234                 goto error;
1235             }
1236             stream->past_FrameCount += wmmeStreamData->framesPerHostBuffer;
1237         }
1238         wmmeStreamData->currentOutputBuffer = 0;
1239         if( (mmresult = waveOutRestart( wmmeStreamData->hWaveOut )) != MMSYSERR_NOERROR )
1240         {
1241             result = paHostError;
1242             sPaHostError = mmresult;
1243             goto error;
1244         }
1245     }
1246 
1247 error:
1248     DBUG(("PaHost_StartOutput: wave returned mmresult = 0x%X.\n", mmresult));
1249     return result;
1250 }
1251 /*************************************************************************/
PaHost_StartInput(internalPortAudioStream * internalStream)1252 PaError PaHost_StartInput( internalPortAudioStream *internalStream )
1253 {
1254     PaError          result = paNoError;
1255     MMRESULT         mmresult;
1256     int              i;
1257     PaWMMEStreamData *wmmeStreamData = PaHost_GetWMMEStreamData( internalStream );
1258 
1259     if( wmmeStreamData == NULL ) return paInternalError;
1260 
1261     if( internalStream->past_InputDeviceID != paNoDevice )
1262     {
1263         for( i=0; i<wmmeStreamData->numHostBuffers; i++ )
1264         {
1265             mmresult = waveInAddBuffer( wmmeStreamData->hWaveIn, &wmmeStreamData->inputBuffers[i], sizeof(WAVEHDR) );
1266             if( mmresult != MMSYSERR_NOERROR )
1267             {
1268                 result = paHostError;
1269                 sPaHostError = mmresult;
1270                 goto error;
1271             }
1272         }
1273         wmmeStreamData->currentInputBuffer = 0;
1274         mmresult = waveInStart( wmmeStreamData->hWaveIn );
1275         DBUG(("Pa_StartStream: waveInStart returned = 0x%X.\n", mmresult));
1276         if( mmresult != MMSYSERR_NOERROR )
1277         {
1278             result = paHostError;
1279             sPaHostError = mmresult;
1280             goto error;
1281         }
1282     }
1283 
1284 error:
1285     return result;
1286 }
1287 /*************************************************************************/
PaHost_StartEngine(internalPortAudioStream * stream)1288 PaError PaHost_StartEngine( internalPortAudioStream *stream )
1289 {
1290     PaError             result = paNoError;
1291     PaWMMEStreamData *wmmeStreamData = PaHost_GetWMMEStreamData( stream );
1292 #if PA_USE_TIMER_CALLBACK
1293     int                 resolution;
1294     int                 bufsPerTimerCallback;
1295     int                 msecPerBuffer;
1296 #endif /* PA_USE_TIMER_CALLBACK */
1297 
1298     if( wmmeStreamData == NULL ) return paInternalError;
1299 
1300     stream->past_StopSoon = 0;
1301     stream->past_StopNow = 0;
1302     stream->past_IsActive = 1;
1303     wmmeStreamData->framesPlayed = 0.0;
1304     wmmeStreamData->lastPosition = 0;
1305 #if PA_TRACE_START_STOP
1306     AddTraceMessage( "PaHost_StartEngine: TimeSlice() returned ", result );
1307 #endif
1308 #if PA_USE_TIMER_CALLBACK
1309     /* Create timer that will wake us up so we can fill the DSound buffer. */
1310     bufsPerTimerCallback = wmmeStreamData->numHostBuffers/4;
1311     if( bufsPerTimerCallback < 1 ) bufsPerTimerCallback = 1;
1312     if( bufsPerTimerCallback < 1 ) bufsPerTimerCallback = 1;
1313     msecPerBuffer = (1000 * bufsPerTimerCallback *
1314                      wmmeStreamData->userBuffersPerHostBuffer *
1315                      internalStream->past_FramesPerUserBuffer ) / (int) internalStream->past_SampleRate;
1316     if( msecPerBuffer < 10 ) msecPerBuffer = 10;
1317     else if( msecPerBuffer > 100 ) msecPerBuffer = 100;
1318     resolution = msecPerBuffer/4;
1319     wmmeStreamData->timerID = timeSetEvent( msecPerBuffer, resolution,
1320                                          (LPTIMECALLBACK) Pa_TimerCallback,
1321                                          (DWORD) stream, TIME_PERIODIC );
1322     if( wmmeStreamData->timerID == 0 )
1323     {
1324         result = paHostError;
1325         sPaHostError = GetLastError();;
1326         goto error;
1327     }
1328 #else /* PA_USE_TIMER_CALLBACK */
1329     ResetEvent( wmmeStreamData->abortEvent );
1330     /* Create thread that waits for audio buffers to be ready for processing. */
1331     wmmeStreamData->engineThread = CreateThread( 0, 0, WinMMPa_OutputThreadProc, stream, 0, &wmmeStreamData->engineThreadID );
1332     if( wmmeStreamData->engineThread == NULL )
1333     {
1334         result = paHostError;
1335         sPaHostError = GetLastError();;
1336         goto error;
1337     }
1338 #if PA_TRACE_START_STOP
1339     AddTraceMessage( "PaHost_StartEngine: thread ", (int) wmmeStreamData->engineThread );
1340 #endif
1341     /* I used to pass the thread which was failing. I now pass GetCurrentProcess().
1342      * This fix could improve latency for some applications. It could also result in CPU
1343      * starvation if the callback did too much processing.
1344      * I also added result checks, so we might see more failures at initialization.
1345      * Thanks to Alberto di Bene for spotting this.
1346      */
1347     if( !SetPriorityClass( GetCurrentProcess(), HIGH_PRIORITY_CLASS ) ) /* PLB20010816 */
1348     {
1349         result = paHostError;
1350         sPaHostError = GetLastError();;
1351         goto error;
1352     }
1353     if( !SetThreadPriority( wmmeStreamData->engineThread, THREAD_PRIORITY_HIGHEST ) )
1354     {
1355         result = paHostError;
1356         sPaHostError = GetLastError();;
1357         goto error;
1358     }
1359 #endif
1360 
1361 error:
1362     return result;
1363 }
1364 /*************************************************************************/
PaHost_StopEngine(internalPortAudioStream * internalStream,int abort)1365 PaError PaHost_StopEngine( internalPortAudioStream *internalStream, int abort )
1366 {
1367     int timeOut;
1368     PaWMMEStreamData *wmmeStreamData = PaHost_GetWMMEStreamData( internalStream );
1369 
1370     if( wmmeStreamData == NULL ) return paNoError;
1371 
1372     /* Tell background thread to stop generating more data and to let current data play out. */
1373     internalStream->past_StopSoon = 1;
1374     /* If aborting, tell background thread to stop NOW! */
1375     if( abort ) internalStream->past_StopNow = 1;
1376 
1377     /* Calculate timeOut longer than longest time it could take to play all buffers. */
1378     timeOut = (DWORD) (1500.0 * PaHost_GetTotalBufferFrames( internalStream ) / internalStream->past_SampleRate);
1379     if( timeOut < MIN_TIMEOUT_MSEC ) timeOut = MIN_TIMEOUT_MSEC;
1380 
1381 #if PA_USE_TIMER_CALLBACK
1382     if( (internalStream->past_OutputDeviceID != paNoDevice) &&
1383             internalStream->past_IsActive &&
1384             (wmmeStreamData->timerID != 0) )
1385     {
1386         /* Wait for IsActive to drop. */
1387         while( (internalStream->past_IsActive) && (timeOut > 0) )
1388         {
1389             Sleep(10);
1390             timeOut -= 10;
1391         }
1392         timeKillEvent( wmmeStreamData->timerID );  /* Stop callback timer. */
1393         wmmeStreamData->timerID = 0;
1394     }
1395 #else /* PA_USE_TIMER_CALLBACK */
1396 #if PA_TRACE_START_STOP
1397     AddTraceMessage( "PaHost_StopEngine: thread ", (int) wmmeStreamData->engineThread );
1398 #endif
1399     if( (internalStream->past_OutputDeviceID != paNoDevice) &&
1400             (internalStream->past_IsActive) &&
1401             (wmmeStreamData->engineThread != NULL) )
1402     {
1403         DWORD got;
1404         /* Tell background thread to stop generating more data and to let current data play out. */
1405         DBUG(("PaHost_StopEngine: waiting for background thread.\n"));
1406         got = WaitForSingleObject( wmmeStreamData->engineThread, timeOut );
1407         if( got == WAIT_TIMEOUT )
1408         {
1409             ERR_RPT(("PaHost_StopEngine: timed out while waiting for background thread to finish.\n"));
1410             return paTimedOut;
1411         }
1412         CloseHandle( wmmeStreamData->engineThread );
1413         wmmeStreamData->engineThread = NULL;
1414     }
1415 #endif /* PA_USE_TIMER_CALLBACK */
1416 
1417     internalStream->past_IsActive = 0;
1418     return paNoError;
1419 }
1420 /*************************************************************************/
PaHost_StopInput(internalPortAudioStream * stream,int abort)1421 PaError PaHost_StopInput( internalPortAudioStream *stream, int abort )
1422 {
1423     MMRESULT mmresult;
1424     PaWMMEStreamData *wmmeStreamData = PaHost_GetWMMEStreamData( stream );
1425 
1426     if( wmmeStreamData == NULL ) return paNoError; /* FIXME: why return paNoError? */
1427     (void) abort; /* unused parameter */
1428 
1429     if( wmmeStreamData->hWaveIn != NULL )
1430     {
1431         mmresult = waveInReset( wmmeStreamData->hWaveIn );
1432         if( mmresult != MMSYSERR_NOERROR )
1433         {
1434             sPaHostError = mmresult;
1435             return paHostError;
1436         }
1437     }
1438     return paNoError;
1439 }
1440 /*************************************************************************/
PaHost_StopOutput(internalPortAudioStream * internalStream,int abort)1441 PaError PaHost_StopOutput( internalPortAudioStream *internalStream, int abort )
1442 {
1443     MMRESULT mmresult;
1444     PaWMMEStreamData *wmmeStreamData = PaHost_GetWMMEStreamData( internalStream );
1445 
1446     if( wmmeStreamData == NULL ) return paNoError;    /* FIXME: why return paNoError? */
1447     (void) abort;   /* unused parameter */
1448 
1449 #if PA_TRACE_START_STOP
1450     AddTraceMessage( "PaHost_StopOutput: hWaveOut ", (int) wmmeStreamData->hWaveOut );
1451 #endif
1452     if( wmmeStreamData->hWaveOut != NULL )
1453     {
1454         mmresult = waveOutReset( wmmeStreamData->hWaveOut );
1455         if( mmresult != MMSYSERR_NOERROR )
1456         {
1457             sPaHostError = mmresult;
1458             return paHostError;
1459         }
1460     }
1461     return paNoError;
1462 }
1463 /*******************************************************************/
PaHost_CloseStream(internalPortAudioStream * stream)1464 PaError PaHost_CloseStream( internalPortAudioStream *stream )
1465 {
1466     int i;
1467     PaWMMEStreamData *wmmeStreamData = PaHost_GetWMMEStreamData( stream );
1468 
1469     if( stream == NULL ) return paBadStreamPtr;
1470     if( wmmeStreamData == NULL ) return paNoError;   /* FIXME: why return no error? */
1471 
1472 #if PA_TRACE_START_STOP
1473     AddTraceMessage( "PaHost_CloseStream: hWaveOut ", (int) wmmeStreamData->hWaveOut );
1474 #endif
1475     /* Free data and device for output. */
1476     if( wmmeStreamData->hWaveOut )
1477     {
1478         if( wmmeStreamData->outputBuffers )
1479         {
1480             for( i=0; i<wmmeStreamData->numHostBuffers; i++ )
1481             {
1482                 waveOutUnprepareHeader( wmmeStreamData->hWaveOut, &wmmeStreamData->outputBuffers[i], sizeof(WAVEHDR) );
1483                 PaHost_FreeTrackedMemory( wmmeStreamData->outputBuffers[i].lpData ); /* MEM */
1484             }
1485             PaHost_FreeTrackedMemory( wmmeStreamData->outputBuffers ); /* MEM */
1486         }
1487         waveOutClose( wmmeStreamData->hWaveOut );
1488     }
1489     /* Free data and device for input. */
1490     if( wmmeStreamData->hWaveIn )
1491     {
1492         if( wmmeStreamData->inputBuffers )
1493         {
1494             for( i=0; i<wmmeStreamData->numHostBuffers; i++ )
1495             {
1496                 waveInUnprepareHeader( wmmeStreamData->hWaveIn, &wmmeStreamData->inputBuffers[i], sizeof(WAVEHDR) );
1497                 PaHost_FreeTrackedMemory( wmmeStreamData->inputBuffers[i].lpData ); /* MEM */
1498             }
1499             PaHost_FreeTrackedMemory( wmmeStreamData->inputBuffers ); /* MEM */
1500         }
1501         waveInClose( wmmeStreamData->hWaveIn );
1502     }
1503 #if (PA_USE_TIMER_CALLBACK == 0)
1504     if( wmmeStreamData->abortEventInited ) CloseHandle( wmmeStreamData->abortEvent );
1505     if( wmmeStreamData->bufferEventInited ) CloseHandle( wmmeStreamData->bufferEvent );
1506 #endif
1507     if( wmmeStreamData->streamLockInited )
1508         DeleteCriticalSection( &wmmeStreamData->streamLock );
1509 
1510     PaHost_FreeWMMEStreamData( stream );
1511 
1512     return paNoError;
1513 }
1514 /*************************************************************************
1515  * Determine minimum number of buffers required for this host based
1516  * on minimum latency. Latency can be optionally set by user by setting
1517  * an environment variable. For example, to set latency to 200 msec, put:
1518  *
1519  *    set PA_MIN_LATENCY_MSEC=200
1520  *
1521  * in the AUTOEXEC.BAT file and reboot.
1522  * If the environment variable is not set, then the latency will be determined
1523  * based on the OS. Windows NT has higher latency than Win95.
1524  */
1525 #define PA_LATENCY_ENV_NAME  ("PA_MIN_LATENCY_MSEC")
Pa_GetMinNumBuffers(int framesPerBuffer,double sampleRate)1526 int Pa_GetMinNumBuffers( int framesPerBuffer, double sampleRate )
1527 {
1528     char      envbuf[PA_ENV_BUF_SIZE];
1529     DWORD     hresult;
1530     int       minLatencyMsec = 0;
1531     double    msecPerBuffer = (1000.0 * framesPerBuffer) / sampleRate;
1532     int       minBuffers;
1533 
1534     /* Let user determine minimal latency by setting environment variable. */
1535     hresult = GetEnvironmentVariable( PA_LATENCY_ENV_NAME, envbuf, PA_ENV_BUF_SIZE );
1536     if( (hresult > 0) && (hresult < PA_ENV_BUF_SIZE) )
1537     {
1538         minLatencyMsec = atoi( envbuf );   /* REVIEW: will we crash if the environment variable contains some nasty value? */
1539     }
1540     else
1541     {
1542         /* Set minimal latency based on whether NT or other OS.
1543          * NT has higher latency.
1544          */
1545         OSVERSIONINFO osvi;
1546 		osvi.dwOSVersionInfoSize = sizeof( osvi );
1547 		GetVersionEx( &osvi );
1548         DBUG(("PA - PlatformId = 0x%x\n", osvi.dwPlatformId ));
1549         DBUG(("PA - MajorVersion = 0x%x\n", osvi.dwMajorVersion ));
1550         DBUG(("PA - MinorVersion = 0x%x\n", osvi.dwMinorVersion ));
1551         /* Check for NT */
1552 		if( (osvi.dwMajorVersion == 4) && (osvi.dwPlatformId == 2) )
1553 		{
1554 			minLatencyMsec = PA_WIN_NT_LATENCY;
1555 		}
1556 		else if(osvi.dwMajorVersion >= 5)
1557 		{
1558 			minLatencyMsec = PA_WIN_WDM_LATENCY;
1559 		}
1560 		else
1561 		{
1562 			minLatencyMsec = PA_WIN_9X_LATENCY;
1563 		}
1564 #if PA_USE_HIGH_LATENCY
1565         PRINT(("PA - Minimum Latency set to %d msec!\n", minLatencyMsec ));
1566 #endif
1567 
1568     }
1569     DBUG(("PA - Minimum Latency set to %d msec!\n", minLatencyMsec ));
1570     minBuffers = (int) (1.0 + ((double)minLatencyMsec / msecPerBuffer));
1571     if( minBuffers < 2 ) minBuffers = 2;
1572     return minBuffers;
1573 }
1574 /*************************************************************************
1575  * Cleanup device info.
1576  */
PaHost_Term(void)1577 PaError PaHost_Term( void )
1578 {
1579     int i;
1580 
1581     if( sNumDevices > 0 )
1582     {
1583         if( sDevicePtrs != NULL )
1584         {
1585             for( i=0; i<sNumDevices; i++ )
1586             {
1587                 if( sDevicePtrs[i] != NULL )
1588                 {
1589                     PaHost_FreeTrackedMemory( (char*)sDevicePtrs[i]->name ); /* MEM */
1590                     PaHost_FreeTrackedMemory( (void*)sDevicePtrs[i]->sampleRates ); /* MEM */
1591                     PaHost_FreeTrackedMemory( sDevicePtrs[i] ); /* MEM */
1592                 }
1593             }
1594             PaHost_FreeTrackedMemory( sDevicePtrs ); /* MEM */
1595             sDevicePtrs = NULL;
1596         }
1597         sNumDevices = 0;
1598     }
1599 
1600 #if PA_TRACK_MEMORY
1601     PRINT(("PaHost_Term: sNumAllocations = %d\n", sNumAllocations ));
1602 #endif
1603 
1604     return paNoError;
1605 }
1606 /*************************************************************************/
Pa_Sleep(long msec)1607 void Pa_Sleep( long msec )
1608 {
1609     Sleep( msec );
1610 }
1611 /*************************************************************************
1612 FIXME: the following memory allocation routines should not be declared here
1613  * Allocate memory that can be accessed in real-time.
1614  * This may need to be held in physical memory so that it is not
1615  * paged to virtual memory.
1616  * This call MUST be balanced with a call to PaHost_FreeFastMemory().
1617  * Memory will be set to zero.
1618  */
PaHost_AllocateFastMemory(long numBytes)1619 void *PaHost_AllocateFastMemory( long numBytes )
1620 {
1621     return PaHost_AllocateTrackedMemory( numBytes ); /* FIXME - do we need physical memory? Use VirtualLock() */ /* MEM */
1622 }
1623 /*************************************************************************
1624  * Free memory that could be accessed in real-time.
1625  * This call MUST be balanced with a call to PaHost_AllocateFastMemory().
1626  */
PaHost_FreeFastMemory(void * addr,long numBytes)1627 void PaHost_FreeFastMemory( void *addr, long numBytes )
1628 {
1629     (void) numBytes; /* unused parameter */
1630 
1631     PaHost_FreeTrackedMemory( addr ); /* MEM */
1632 }
1633 
1634 /*************************************************************************
1635  * Track memory allocations to avoid leaks.
1636  */
PaHost_AllocateTrackedMemory(long numBytes)1637 static void *PaHost_AllocateTrackedMemory( long numBytes )
1638 {
1639     void *result = GlobalAlloc( GPTR, numBytes ); /* MEM */
1640 
1641 #if PA_TRACK_MEMORY
1642     if( result != NULL ) sNumAllocations += 1;
1643 #endif
1644     return result;
1645 }
1646 
PaHost_FreeTrackedMemory(void * addr)1647 static void PaHost_FreeTrackedMemory( void *addr )
1648 {
1649     if( addr != NULL )
1650     {
1651         GlobalFree( addr ); /* MEM */
1652 #if PA_TRACK_MEMORY
1653         sNumAllocations -= 1;
1654 #endif
1655     }
1656 }
1657 
1658 /***********************************************************************/
PaHost_StreamActive(internalPortAudioStream * internalStream)1659 PaError PaHost_StreamActive( internalPortAudioStream *internalStream )
1660 {
1661     if( internalStream == NULL ) return paBadStreamPtr;
1662 
1663     return (PaError) internalStream->past_IsActive;
1664 }
1665 /*************************************************************************
1666  * This must be called periodically because mmtime.u.sample
1667  * is a DWORD and can wrap and lose sync after a few hours.
1668  */
PaHost_UpdateStreamTime(PaWMMEStreamData * wmmeStreamData)1669 static PaError PaHost_UpdateStreamTime( PaWMMEStreamData *wmmeStreamData )
1670 {
1671     MMRESULT  mmresult;
1672     MMTIME    mmtime;
1673     mmtime.wType = TIME_SAMPLES;
1674 
1675     if( wmmeStreamData->hWaveOut != NULL )
1676     {
1677         mmresult = waveOutGetPosition( wmmeStreamData->hWaveOut, &mmtime, sizeof(mmtime) );
1678     }
1679     else
1680     {
1681         mmresult = waveInGetPosition( wmmeStreamData->hWaveIn, &mmtime, sizeof(mmtime) );
1682     }
1683 
1684     if( mmresult != MMSYSERR_NOERROR )
1685     {
1686         sPaHostError = mmresult;
1687         return paHostError;
1688     }
1689 
1690     /* This data has two variables and is shared by foreground and background.
1691      * So we need to make it thread safe. */
1692     EnterCriticalSection( &wmmeStreamData->streamLock );
1693     wmmeStreamData->framesPlayed += ((long)mmtime.u.sample) - wmmeStreamData->lastPosition;
1694     wmmeStreamData->lastPosition = (long)mmtime.u.sample;
1695     LeaveCriticalSection( &wmmeStreamData->streamLock );
1696 
1697     return paNoError;
1698 }
1699 /*************************************************************************/
Pa_StreamTime(PortAudioStream * stream)1700 PaTimestamp Pa_StreamTime( PortAudioStream *stream )
1701 {
1702     internalPortAudioStream *internalStream = PaHost_GetStreamRepresentation( stream );
1703     PaWMMEStreamData *wmmeStreamData = PaHost_GetWMMEStreamData( internalStream );
1704 
1705     if( internalStream == NULL ) return paBadStreamPtr;
1706     if( wmmeStreamData == NULL ) return paInternalError;
1707 
1708     PaHost_UpdateStreamTime( wmmeStreamData );
1709     return wmmeStreamData->framesPlayed;
1710 }
1711 /*************************************************************************/
1712 
1713 
1714 
1715