cannam@89
|
1 /*
|
cannam@89
|
2 * $Id: $
|
cannam@89
|
3 * Portable Audio I/O Library
|
cannam@89
|
4 * Windows MME low level buffer user guided parameters search
|
cannam@89
|
5 *
|
cannam@89
|
6 * Copyright (c) 2010 Ross Bencina
|
cannam@89
|
7 *
|
cannam@89
|
8 * Permission is hereby granted, free of charge, to any person obtaining
|
cannam@89
|
9 * a copy of this software and associated documentation files
|
cannam@89
|
10 * (the "Software"), to deal in the Software without restriction,
|
cannam@89
|
11 * including without limitation the rights to use, copy, modify, merge,
|
cannam@89
|
12 * publish, distribute, sublicense, and/or sell copies of the Software,
|
cannam@89
|
13 * and to permit persons to whom the Software is furnished to do so,
|
cannam@89
|
14 * subject to the following conditions:
|
cannam@89
|
15 *
|
cannam@89
|
16 * The above copyright notice and this permission notice shall be
|
cannam@89
|
17 * included in all copies or substantial portions of the Software.
|
cannam@89
|
18 *
|
cannam@89
|
19 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
cannam@89
|
20 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
cannam@89
|
21 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
cannam@89
|
22 * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
|
cannam@89
|
23 * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
|
cannam@89
|
24 * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
cannam@89
|
25 * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
cannam@89
|
26 */
|
cannam@89
|
27
|
cannam@89
|
28 /*
|
cannam@89
|
29 * The text above constitutes the entire PortAudio license; however,
|
cannam@89
|
30 * the PortAudio community also makes the following non-binding requests:
|
cannam@89
|
31 *
|
cannam@89
|
32 * Any person wishing to distribute modifications to the Software is
|
cannam@89
|
33 * requested to send the modifications to the original developer so that
|
cannam@89
|
34 * they can be incorporated into the canonical version. It is also
|
cannam@89
|
35 * requested that these non-binding requests be included along with the
|
cannam@89
|
36 * license above.
|
cannam@89
|
37 */
|
cannam@89
|
38
|
cannam@89
|
39 #include <stdio.h>
|
cannam@89
|
40 #include <time.h>
|
cannam@89
|
41 #include <math.h>
|
cannam@89
|
42
|
cannam@89
|
43 #define _WIN32_WINNT 0x0501 /* for GetNativeSystemInfo */
|
cannam@89
|
44 #include <windows.h> /* required when using pa_win_wmme.h */
|
cannam@89
|
45 #include <mmsystem.h> /* required when using pa_win_wmme.h */
|
cannam@89
|
46
|
cannam@89
|
47 #include <conio.h> /* for _getch */
|
cannam@89
|
48
|
cannam@89
|
49
|
cannam@89
|
50 #include "portaudio.h"
|
cannam@89
|
51 #include "pa_win_wmme.h"
|
cannam@89
|
52
|
cannam@89
|
53
|
cannam@89
|
54 #define DEFAULT_SAMPLE_RATE (44100.)
|
cannam@89
|
55
|
cannam@89
|
56 #ifndef M_PI
|
cannam@89
|
57 #define M_PI (3.14159265)
|
cannam@89
|
58 #endif
|
cannam@89
|
59
|
cannam@89
|
60 #define TABLE_SIZE (2048)
|
cannam@89
|
61
|
cannam@89
|
62 #define CHANNEL_COUNT (2)
|
cannam@89
|
63
|
cannam@89
|
64
|
cannam@89
|
65 /* seach parameters. we test all buffer counts in this range */
|
cannam@89
|
66 #define MIN_WMME_BUFFER_COUNT (2)
|
cannam@89
|
67 #define MAX_WMME_BUFFER_COUNT (12)
|
cannam@89
|
68
|
cannam@89
|
69
|
cannam@89
|
70 /*******************************************************************/
|
cannam@89
|
71 /* functions to query and print Windows version information */
|
cannam@89
|
72
|
cannam@89
|
73 typedef BOOL (WINAPI *LPFN_ISWOW64PROCESS) (HANDLE, PBOOL);
|
cannam@89
|
74
|
cannam@89
|
75 LPFN_ISWOW64PROCESS fnIsWow64Process;
|
cannam@89
|
76
|
cannam@89
|
77 static BOOL IsWow64()
|
cannam@89
|
78 {
|
cannam@89
|
79 BOOL bIsWow64 = FALSE;
|
cannam@89
|
80
|
cannam@89
|
81 //IsWow64Process is not available on all supported versions of Windows.
|
cannam@89
|
82 //Use GetModuleHandle to get a handle to the DLL that contains the function
|
cannam@89
|
83 //and GetProcAddress to get a pointer to the function if available.
|
cannam@89
|
84
|
cannam@89
|
85 fnIsWow64Process = (LPFN_ISWOW64PROCESS) GetProcAddress(
|
cannam@89
|
86 GetModuleHandle(TEXT("kernel32")),"IsWow64Process" );
|
cannam@89
|
87
|
cannam@89
|
88 if(NULL != fnIsWow64Process)
|
cannam@89
|
89 {
|
cannam@89
|
90 if (!fnIsWow64Process(GetCurrentProcess(),&bIsWow64))
|
cannam@89
|
91 {
|
cannam@89
|
92 //handle error
|
cannam@89
|
93 }
|
cannam@89
|
94 }
|
cannam@89
|
95 return bIsWow64;
|
cannam@89
|
96 }
|
cannam@89
|
97
|
cannam@89
|
98 static void printWindowsVersionInfo( FILE *fp )
|
cannam@89
|
99 {
|
cannam@89
|
100 OSVERSIONINFOEX osVersionInfoEx;
|
cannam@89
|
101 SYSTEM_INFO systemInfo;
|
cannam@89
|
102 const char *osName = "Unknown";
|
cannam@89
|
103 const char *osProductType = "";
|
cannam@89
|
104 const char *processorArchitecture = "Unknown";
|
cannam@89
|
105
|
cannam@89
|
106 memset( &osVersionInfoEx, 0, sizeof(OSVERSIONINFOEX) );
|
cannam@89
|
107 osVersionInfoEx.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);
|
cannam@89
|
108 GetVersionEx( &osVersionInfoEx );
|
cannam@89
|
109
|
cannam@89
|
110
|
cannam@89
|
111 if( osVersionInfoEx.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS ){
|
cannam@89
|
112 switch( osVersionInfoEx.dwMinorVersion ){
|
cannam@89
|
113 case 0: osName = "Windows 95"; break;
|
cannam@89
|
114 case 10: osName = "Windows 98"; break; // could also be 98SE (I've seen code discriminate based
|
cannam@89
|
115 // on osInfo.Version.Revision.ToString() == "2222A")
|
cannam@89
|
116 case 90: osName = "Windows Me"; break;
|
cannam@89
|
117 }
|
cannam@89
|
118 }else if( osVersionInfoEx.dwPlatformId == VER_PLATFORM_WIN32_NT ){
|
cannam@89
|
119 switch( osVersionInfoEx.dwMajorVersion ){
|
cannam@89
|
120 case 3: osName = "Windows NT 3.51"; break;
|
cannam@89
|
121 case 4: osName = "Windows NT 4.0"; break;
|
cannam@89
|
122 case 5: switch( osVersionInfoEx.dwMinorVersion ){
|
cannam@89
|
123 case 0: osName = "Windows 2000"; break;
|
cannam@89
|
124 case 1: osName = "Windows XP"; break;
|
cannam@89
|
125 case 2:
|
cannam@89
|
126 if( osVersionInfoEx.wSuiteMask & 0x00008000 /*VER_SUITE_WH_SERVER*/ ){
|
cannam@89
|
127 osName = "Windows Home Server";
|
cannam@89
|
128 }else{
|
cannam@89
|
129 if( osVersionInfoEx.wProductType == VER_NT_WORKSTATION ){
|
cannam@89
|
130 osName = "Windows XP Professional x64 Edition (?)";
|
cannam@89
|
131 }else{
|
cannam@89
|
132 if( GetSystemMetrics(/*SM_SERVERR2*/89) == 0 )
|
cannam@89
|
133 osName = "Windows Server 2003";
|
cannam@89
|
134 else
|
cannam@89
|
135 osName = "Windows Server 2003 R2";
|
cannam@89
|
136 }
|
cannam@89
|
137 }break;
|
cannam@89
|
138 }break;
|
cannam@89
|
139 case 6:switch( osVersionInfoEx.dwMinorVersion ){
|
cannam@89
|
140 case 0:
|
cannam@89
|
141 if( osVersionInfoEx.wProductType == VER_NT_WORKSTATION )
|
cannam@89
|
142 osName = "Windows Vista";
|
cannam@89
|
143 else
|
cannam@89
|
144 osName = "Windows Server 2008";
|
cannam@89
|
145 break;
|
cannam@89
|
146 case 1:
|
cannam@89
|
147 if( osVersionInfoEx.wProductType == VER_NT_WORKSTATION )
|
cannam@89
|
148 osName = "Windows 7";
|
cannam@89
|
149 else
|
cannam@89
|
150 osName = "Windows Server 2008 R2";
|
cannam@89
|
151 break;
|
cannam@89
|
152 }break;
|
cannam@89
|
153 }
|
cannam@89
|
154 }
|
cannam@89
|
155
|
cannam@89
|
156 if(osVersionInfoEx.dwMajorVersion == 4)
|
cannam@89
|
157 {
|
cannam@89
|
158 if(osVersionInfoEx.wProductType == VER_NT_WORKSTATION)
|
cannam@89
|
159 osProductType = "Workstation";
|
cannam@89
|
160 else if(osVersionInfoEx.wProductType == VER_NT_SERVER)
|
cannam@89
|
161 osProductType = "Server";
|
cannam@89
|
162 }
|
cannam@89
|
163 else if(osVersionInfoEx.dwMajorVersion == 5)
|
cannam@89
|
164 {
|
cannam@89
|
165 if(osVersionInfoEx.wProductType == VER_NT_WORKSTATION)
|
cannam@89
|
166 {
|
cannam@89
|
167 if((osVersionInfoEx.wSuiteMask & VER_SUITE_PERSONAL) == VER_SUITE_PERSONAL)
|
cannam@89
|
168 osProductType = "Home Edition"; // Windows XP Home Edition
|
cannam@89
|
169 else
|
cannam@89
|
170 osProductType = "Professional"; // Windows XP / Windows 2000 Professional
|
cannam@89
|
171 }
|
cannam@89
|
172 else if(osVersionInfoEx.wProductType == VER_NT_SERVER)
|
cannam@89
|
173 {
|
cannam@89
|
174 if(osVersionInfoEx.dwMinorVersion == 0)
|
cannam@89
|
175 {
|
cannam@89
|
176 if((osVersionInfoEx.wSuiteMask & VER_SUITE_DATACENTER) == VER_SUITE_DATACENTER)
|
cannam@89
|
177 osProductType = "Datacenter Server"; // Windows 2000 Datacenter Server
|
cannam@89
|
178 else if((osVersionInfoEx.wSuiteMask & VER_SUITE_ENTERPRISE) == VER_SUITE_ENTERPRISE)
|
cannam@89
|
179 osProductType = "Advanced Server"; // Windows 2000 Advanced Server
|
cannam@89
|
180 else
|
cannam@89
|
181 osProductType = "Server"; // Windows 2000 Server
|
cannam@89
|
182 }
|
cannam@89
|
183 }
|
cannam@89
|
184 else
|
cannam@89
|
185 {
|
cannam@89
|
186 if((osVersionInfoEx.wSuiteMask & VER_SUITE_DATACENTER) == VER_SUITE_DATACENTER)
|
cannam@89
|
187 osProductType = "Datacenter Edition"; // Windows Server 2003 Datacenter Edition
|
cannam@89
|
188 else if((osVersionInfoEx.wSuiteMask & VER_SUITE_ENTERPRISE) == VER_SUITE_ENTERPRISE)
|
cannam@89
|
189 osProductType = "Enterprise Edition"; // Windows Server 2003 Enterprise Edition
|
cannam@89
|
190 else if((osVersionInfoEx.wSuiteMask & VER_SUITE_BLADE) == VER_SUITE_BLADE)
|
cannam@89
|
191 osProductType = "Web Edition"; // Windows Server 2003 Web Edition
|
cannam@89
|
192 else
|
cannam@89
|
193 osProductType = "Standard Edition"; // Windows Server 2003 Standard Edition
|
cannam@89
|
194 }
|
cannam@89
|
195 }
|
cannam@89
|
196
|
cannam@89
|
197 memset( &systemInfo, 0, sizeof(SYSTEM_INFO) );
|
cannam@89
|
198 GetNativeSystemInfo( &systemInfo );
|
cannam@89
|
199
|
cannam@89
|
200 if( systemInfo.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_INTEL )
|
cannam@89
|
201 processorArchitecture = "x86";
|
cannam@89
|
202 else if( systemInfo.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_AMD64 )
|
cannam@89
|
203 processorArchitecture = "x64";
|
cannam@89
|
204 else if( systemInfo.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_IA64 )
|
cannam@89
|
205 processorArchitecture = "Itanium";
|
cannam@89
|
206
|
cannam@89
|
207
|
cannam@89
|
208 fprintf( fp, "OS name and edition: %s %s\n", osName, osProductType );
|
cannam@89
|
209 fprintf( fp, "OS version: %d.%d.%d %S\n",
|
cannam@89
|
210 osVersionInfoEx.dwMajorVersion, osVersionInfoEx.dwMinorVersion,
|
cannam@89
|
211 osVersionInfoEx.dwBuildNumber, osVersionInfoEx.szCSDVersion );
|
cannam@89
|
212 fprintf( fp, "Processor architecture: %s\n", processorArchitecture );
|
cannam@89
|
213 fprintf( fp, "WoW64 process: %s\n", IsWow64() ? "Yes" : "No" );
|
cannam@89
|
214 }
|
cannam@89
|
215
|
cannam@89
|
216 static void printTimeAndDate( FILE *fp )
|
cannam@89
|
217 {
|
cannam@89
|
218 struct tm *local;
|
cannam@89
|
219 time_t t;
|
cannam@89
|
220
|
cannam@89
|
221 t = time(NULL);
|
cannam@89
|
222 local = localtime(&t);
|
cannam@89
|
223 fprintf(fp, "Local time and date: %s", asctime(local));
|
cannam@89
|
224 local = gmtime(&t);
|
cannam@89
|
225 fprintf(fp, "UTC time and date: %s", asctime(local));
|
cannam@89
|
226 }
|
cannam@89
|
227
|
cannam@89
|
228 /*******************************************************************/
|
cannam@89
|
229
|
cannam@89
|
230 typedef struct
|
cannam@89
|
231 {
|
cannam@89
|
232 float sine[TABLE_SIZE];
|
cannam@89
|
233 double phase;
|
cannam@89
|
234 double phaseIncrement;
|
cannam@89
|
235 volatile int fadeIn;
|
cannam@89
|
236 volatile int fadeOut;
|
cannam@89
|
237 double amp;
|
cannam@89
|
238 }
|
cannam@89
|
239 paTestData;
|
cannam@89
|
240
|
cannam@89
|
241 static paTestData data;
|
cannam@89
|
242
|
cannam@89
|
243 /* This routine will be called by the PortAudio engine when audio is needed.
|
cannam@89
|
244 ** It may called at interrupt level on some machines so don't do anything
|
cannam@89
|
245 ** that could mess up the system like calling malloc() or free().
|
cannam@89
|
246 */
|
cannam@89
|
247 static int patestCallback( const void *inputBuffer, void *outputBuffer,
|
cannam@89
|
248 unsigned long framesPerBuffer,
|
cannam@89
|
249 const PaStreamCallbackTimeInfo* timeInfo,
|
cannam@89
|
250 PaStreamCallbackFlags statusFlags,
|
cannam@89
|
251 void *userData )
|
cannam@89
|
252 {
|
cannam@89
|
253 paTestData *data = (paTestData*)userData;
|
cannam@89
|
254 float *out = (float*)outputBuffer;
|
cannam@89
|
255 unsigned long i,j;
|
cannam@89
|
256
|
cannam@89
|
257 (void) timeInfo; /* Prevent unused variable warnings. */
|
cannam@89
|
258 (void) statusFlags;
|
cannam@89
|
259 (void) inputBuffer;
|
cannam@89
|
260
|
cannam@89
|
261 for( i=0; i<framesPerBuffer; i++ )
|
cannam@89
|
262 {
|
cannam@89
|
263 float x = data->sine[(int)data->phase];
|
cannam@89
|
264 data->phase += data->phaseIncrement;
|
cannam@89
|
265 if( data->phase >= TABLE_SIZE ){
|
cannam@89
|
266 data->phase -= TABLE_SIZE;
|
cannam@89
|
267 }
|
cannam@89
|
268
|
cannam@89
|
269 x *= data->amp;
|
cannam@89
|
270 if( data->fadeIn ){
|
cannam@89
|
271 data->amp += .001;
|
cannam@89
|
272 if( data->amp >= 1. )
|
cannam@89
|
273 data->fadeIn = 0;
|
cannam@89
|
274 }else if( data->fadeOut ){
|
cannam@89
|
275 if( data->amp > 0 )
|
cannam@89
|
276 data->amp -= .001;
|
cannam@89
|
277 }
|
cannam@89
|
278
|
cannam@89
|
279 for( j = 0; j < CHANNEL_COUNT; ++j ){
|
cannam@89
|
280 *out++ = x;
|
cannam@89
|
281 }
|
cannam@89
|
282 }
|
cannam@89
|
283
|
cannam@89
|
284 if( data->amp > 0 )
|
cannam@89
|
285 return paContinue;
|
cannam@89
|
286 else
|
cannam@89
|
287 return paComplete;
|
cannam@89
|
288 }
|
cannam@89
|
289
|
cannam@89
|
290
|
cannam@89
|
291 #define YES 1
|
cannam@89
|
292 #define NO 0
|
cannam@89
|
293
|
cannam@89
|
294
|
cannam@89
|
295 static int playUntilKeyPress( int deviceIndex, float sampleRate,
|
cannam@89
|
296 int framesPerUserBuffer, int framesPerWmmeBuffer, int wmmeBufferCount )
|
cannam@89
|
297 {
|
cannam@89
|
298 PaStreamParameters outputParameters;
|
cannam@89
|
299 PaWinMmeStreamInfo wmmeStreamInfo;
|
cannam@89
|
300 PaStream *stream;
|
cannam@89
|
301 PaError err;
|
cannam@89
|
302 int c;
|
cannam@89
|
303
|
cannam@89
|
304 outputParameters.device = deviceIndex;
|
cannam@89
|
305 outputParameters.channelCount = CHANNEL_COUNT;
|
cannam@89
|
306 outputParameters.sampleFormat = paFloat32; /* 32 bit floating point processing */
|
cannam@89
|
307 outputParameters.suggestedLatency = 0; /*Pa_GetDeviceInfo( outputParameters.device )->defaultLowOutputLatency;*/
|
cannam@89
|
308 outputParameters.hostApiSpecificStreamInfo = NULL;
|
cannam@89
|
309
|
cannam@89
|
310 wmmeStreamInfo.size = sizeof(PaWinMmeStreamInfo);
|
cannam@89
|
311 wmmeStreamInfo.hostApiType = paMME;
|
cannam@89
|
312 wmmeStreamInfo.version = 1;
|
cannam@89
|
313 wmmeStreamInfo.flags = paWinMmeUseLowLevelLatencyParameters | paWinMmeDontThrottleOverloadedProcessingThread;
|
cannam@89
|
314 wmmeStreamInfo.framesPerBuffer = framesPerWmmeBuffer;
|
cannam@89
|
315 wmmeStreamInfo.bufferCount = wmmeBufferCount;
|
cannam@89
|
316 outputParameters.hostApiSpecificStreamInfo = &wmmeStreamInfo;
|
cannam@89
|
317
|
cannam@89
|
318 err = Pa_OpenStream(
|
cannam@89
|
319 &stream,
|
cannam@89
|
320 NULL, /* no input */
|
cannam@89
|
321 &outputParameters,
|
cannam@89
|
322 sampleRate,
|
cannam@89
|
323 framesPerUserBuffer,
|
cannam@89
|
324 paClipOff | paPrimeOutputBuffersUsingStreamCallback, /* we won't output out of range samples so don't bother clipping them */
|
cannam@89
|
325 patestCallback,
|
cannam@89
|
326 &data );
|
cannam@89
|
327 if( err != paNoError ) goto error;
|
cannam@89
|
328
|
cannam@89
|
329 data.amp = 0;
|
cannam@89
|
330 data.fadeIn = 1;
|
cannam@89
|
331 data.fadeOut = 0;
|
cannam@89
|
332 data.phase = 0;
|
cannam@89
|
333 data.phaseIncrement = 15 + ((rand()%100) / 10); // randomise pitch
|
cannam@89
|
334
|
cannam@89
|
335 err = Pa_StartStream( stream );
|
cannam@89
|
336 if( err != paNoError ) goto error;
|
cannam@89
|
337
|
cannam@89
|
338
|
cannam@89
|
339 do{
|
cannam@89
|
340 printf( "Trying buffer size %d.\nIf it sounds smooth (without clicks or glitches) press 'y', if it sounds bad press 'n' ('q' to quit)\n", framesPerWmmeBuffer );
|
cannam@89
|
341 c = tolower(_getch());
|
cannam@89
|
342 if( c == 'q' ){
|
cannam@89
|
343 Pa_Terminate();
|
cannam@89
|
344 exit(0);
|
cannam@89
|
345 }
|
cannam@89
|
346 }while( c != 'y' && c != 'n' );
|
cannam@89
|
347
|
cannam@89
|
348 data.fadeOut = 1;
|
cannam@89
|
349 while( Pa_IsStreamActive(stream) == 1 )
|
cannam@89
|
350 Pa_Sleep( 100 );
|
cannam@89
|
351
|
cannam@89
|
352 err = Pa_StopStream( stream );
|
cannam@89
|
353 if( err != paNoError ) goto error;
|
cannam@89
|
354
|
cannam@89
|
355 err = Pa_CloseStream( stream );
|
cannam@89
|
356 if( err != paNoError ) goto error;
|
cannam@89
|
357
|
cannam@89
|
358 return (c == 'y') ? YES : NO;
|
cannam@89
|
359
|
cannam@89
|
360 error:
|
cannam@89
|
361 return err;
|
cannam@89
|
362 }
|
cannam@89
|
363
|
cannam@89
|
364 /*******************************************************************/
|
cannam@89
|
365 static void usage( int wmmeHostApiIndex )
|
cannam@89
|
366 {
|
cannam@89
|
367 int i;
|
cannam@89
|
368
|
cannam@89
|
369 fprintf( stderr, "PortAudio WMME output latency user guided test\n" );
|
cannam@89
|
370 fprintf( stderr, "Usage: x.exe mme-device-index [sampleRate [min-buffer-count max-buffer-count]]\n" );
|
cannam@89
|
371 fprintf( stderr, "Invalid device index. Use one of these:\n" );
|
cannam@89
|
372 for( i=0; i < Pa_GetDeviceCount(); ++i ){
|
cannam@89
|
373
|
cannam@89
|
374 if( Pa_GetDeviceInfo(i)->hostApi == wmmeHostApiIndex && Pa_GetDeviceInfo(i)->maxOutputChannels > 0 )
|
cannam@89
|
375 fprintf( stderr, "%d (%s)\n", i, Pa_GetDeviceInfo(i)->name );
|
cannam@89
|
376 }
|
cannam@89
|
377 Pa_Terminate();
|
cannam@89
|
378 exit(-1);
|
cannam@89
|
379 }
|
cannam@89
|
380
|
cannam@89
|
381 /*
|
cannam@89
|
382 ideas:
|
cannam@89
|
383 o- could be testing with 80% CPU load
|
cannam@89
|
384 o- could test with different channel counts
|
cannam@89
|
385 */
|
cannam@89
|
386
|
cannam@89
|
387 int main(int argc, char* argv[])
|
cannam@89
|
388 {
|
cannam@89
|
389 PaError err;
|
cannam@89
|
390 int i;
|
cannam@89
|
391 int deviceIndex;
|
cannam@89
|
392 int wmmeBufferCount, wmmeBufferSize, smallestWorkingBufferSize;
|
cannam@89
|
393 int smallestWorkingBufferingLatencyFrames;
|
cannam@89
|
394 int min, max, mid;
|
cannam@89
|
395 int testResult;
|
cannam@89
|
396 FILE *resultsFp;
|
cannam@89
|
397 int wmmeHostApiIndex;
|
cannam@89
|
398 const PaHostApiInfo *wmmeHostApiInfo;
|
cannam@89
|
399 double sampleRate = DEFAULT_SAMPLE_RATE;
|
cannam@89
|
400 int wmmeMinBufferCount = MIN_WMME_BUFFER_COUNT;
|
cannam@89
|
401 int wmmeMaxBufferCount = MAX_WMME_BUFFER_COUNT;
|
cannam@89
|
402
|
cannam@89
|
403 err = Pa_Initialize();
|
cannam@89
|
404 if( err != paNoError ) goto error;
|
cannam@89
|
405
|
cannam@89
|
406 wmmeHostApiIndex = Pa_HostApiTypeIdToHostApiIndex( paMME );
|
cannam@89
|
407 wmmeHostApiInfo = Pa_GetHostApiInfo( wmmeHostApiIndex );
|
cannam@89
|
408
|
cannam@89
|
409 if( argc > 5 )
|
cannam@89
|
410 usage(wmmeHostApiIndex);
|
cannam@89
|
411
|
cannam@89
|
412 deviceIndex = wmmeHostApiInfo->defaultOutputDevice;
|
cannam@89
|
413 if( argc >= 2 ){
|
cannam@89
|
414 deviceIndex = -1;
|
cannam@89
|
415 if( sscanf( argv[1], "%d", &deviceIndex ) != 1 )
|
cannam@89
|
416 usage(wmmeHostApiIndex);
|
cannam@89
|
417 if( deviceIndex < 0 || deviceIndex >= Pa_GetDeviceCount() || Pa_GetDeviceInfo(deviceIndex)->hostApi != wmmeHostApiIndex ){
|
cannam@89
|
418 usage(wmmeHostApiIndex);
|
cannam@89
|
419 }
|
cannam@89
|
420 }
|
cannam@89
|
421
|
cannam@89
|
422 printf( "Using device id %d (%s)\n", deviceIndex, Pa_GetDeviceInfo(deviceIndex)->name );
|
cannam@89
|
423
|
cannam@89
|
424 if( argc >= 3 ){
|
cannam@89
|
425 if( sscanf( argv[2], "%lf", &sampleRate ) != 1 )
|
cannam@89
|
426 usage(wmmeHostApiIndex);
|
cannam@89
|
427 }
|
cannam@89
|
428
|
cannam@89
|
429 printf( "Testing with sample rate %f.\n", (float)sampleRate );
|
cannam@89
|
430
|
cannam@89
|
431 if( argc == 4 ){
|
cannam@89
|
432 if( sscanf( argv[3], "%d", &wmmeMinBufferCount ) != 1 )
|
cannam@89
|
433 usage(wmmeHostApiIndex);
|
cannam@89
|
434 wmmeMaxBufferCount = wmmeMinBufferCount;
|
cannam@89
|
435 }
|
cannam@89
|
436
|
cannam@89
|
437 if( argc == 5 ){
|
cannam@89
|
438 if( sscanf( argv[3], "%d", &wmmeMinBufferCount ) != 1 )
|
cannam@89
|
439 usage(wmmeHostApiIndex);
|
cannam@89
|
440 if( sscanf( argv[4], "%d", &wmmeMaxBufferCount ) != 1 )
|
cannam@89
|
441 usage(wmmeHostApiIndex);
|
cannam@89
|
442 }
|
cannam@89
|
443
|
cannam@89
|
444 printf( "Testing buffer counts from %d to %d\n", wmmeMinBufferCount, wmmeMaxBufferCount );
|
cannam@89
|
445
|
cannam@89
|
446
|
cannam@89
|
447 /* initialise sinusoidal wavetable */
|
cannam@89
|
448 for( i=0; i<TABLE_SIZE; i++ )
|
cannam@89
|
449 {
|
cannam@89
|
450 data.sine[i] = (float) sin( ((double)i/(double)TABLE_SIZE) * M_PI * 2. );
|
cannam@89
|
451 }
|
cannam@89
|
452
|
cannam@89
|
453 data.phase = 0;
|
cannam@89
|
454
|
cannam@89
|
455 resultsFp = fopen( "results.txt", "at" );
|
cannam@89
|
456 fprintf( resultsFp, "*** WMME smallest working output buffer sizes\n" );
|
cannam@89
|
457
|
cannam@89
|
458 printTimeAndDate( resultsFp );
|
cannam@89
|
459 printWindowsVersionInfo( resultsFp );
|
cannam@89
|
460
|
cannam@89
|
461 fprintf( resultsFp, "audio device: %s\n", Pa_GetDeviceInfo( deviceIndex )->name );
|
cannam@89
|
462 fflush( resultsFp );
|
cannam@89
|
463
|
cannam@89
|
464 fprintf( resultsFp, "Sample rate: %f\n", (float)sampleRate );
|
cannam@89
|
465 fprintf( resultsFp, "Buffer count, Smallest working buffer size (frames), Smallest working buffering latency (frames), Smallest working buffering latency (Seconds)\n" );
|
cannam@89
|
466
|
cannam@89
|
467 for( wmmeBufferCount = wmmeMinBufferCount; wmmeBufferCount <= wmmeMaxBufferCount; ++wmmeBufferCount ){
|
cannam@89
|
468
|
cannam@89
|
469 printf( "Test %d of %d\n", (wmmeBufferCount - wmmeMinBufferCount) + 1, (wmmeMaxBufferCount-wmmeMinBufferCount) + 1 );
|
cannam@89
|
470 printf( "Testing with %d buffers...\n", wmmeBufferCount );
|
cannam@89
|
471
|
cannam@89
|
472 /*
|
cannam@89
|
473 Binary search after Niklaus Wirth
|
cannam@89
|
474 from http://en.wikipedia.org/wiki/Binary_search_algorithm#The_algorithm
|
cannam@89
|
475 */
|
cannam@89
|
476 min = 1;
|
cannam@89
|
477 max = (int)((sampleRate * .3) / (wmmeBufferCount-1)); //8192; /* we assume that this size works 300ms */
|
cannam@89
|
478 smallestWorkingBufferSize = 0;
|
cannam@89
|
479
|
cannam@89
|
480 do{
|
cannam@89
|
481 mid = min + ((max - min) / 2);
|
cannam@89
|
482
|
cannam@89
|
483 wmmeBufferSize = mid;
|
cannam@89
|
484 testResult = playUntilKeyPress( deviceIndex, sampleRate, wmmeBufferSize, wmmeBufferSize, wmmeBufferCount );
|
cannam@89
|
485
|
cannam@89
|
486 if( testResult == YES ){
|
cannam@89
|
487 max = mid - 1;
|
cannam@89
|
488 smallestWorkingBufferSize = wmmeBufferSize;
|
cannam@89
|
489 }else{
|
cannam@89
|
490 min = mid + 1;
|
cannam@89
|
491 }
|
cannam@89
|
492
|
cannam@89
|
493 }while( (min <= max) && (testResult == YES || testResult == NO) );
|
cannam@89
|
494
|
cannam@89
|
495 smallestWorkingBufferingLatencyFrames = smallestWorkingBufferSize * (wmmeBufferCount - 1);
|
cannam@89
|
496
|
cannam@89
|
497 printf( "Smallest working buffer size for %d buffers is: %d\n", wmmeBufferCount, smallestWorkingBufferSize );
|
cannam@89
|
498 printf( "Corresponding to buffering latency of %d frames, or %f seconds.\n", smallestWorkingBufferingLatencyFrames, smallestWorkingBufferingLatencyFrames / sampleRate );
|
cannam@89
|
499
|
cannam@89
|
500 fprintf( resultsFp, "%d, %d, %d, %f\n", wmmeBufferCount, smallestWorkingBufferSize, smallestWorkingBufferingLatencyFrames, smallestWorkingBufferingLatencyFrames / sampleRate );
|
cannam@89
|
501 fflush( resultsFp );
|
cannam@89
|
502 }
|
cannam@89
|
503
|
cannam@89
|
504 fprintf( resultsFp, "###\n" );
|
cannam@89
|
505 fclose( resultsFp );
|
cannam@89
|
506
|
cannam@89
|
507 Pa_Terminate();
|
cannam@89
|
508 printf("Test finished.\n");
|
cannam@89
|
509
|
cannam@89
|
510 return err;
|
cannam@89
|
511 error:
|
cannam@89
|
512 Pa_Terminate();
|
cannam@89
|
513 fprintf( stderr, "An error occured while using the portaudio stream\n" );
|
cannam@89
|
514 fprintf( stderr, "Error number: %d\n", err );
|
cannam@89
|
515 fprintf( stderr, "Error message: %s\n", Pa_GetErrorText( err ) );
|
cannam@89
|
516 return err;
|
cannam@89
|
517 }
|
cannam@89
|
518
|