Version 2.0.1
[rtaudio-cdist.git] / RtAudio.cpp
1 /******************************************/\r
2 /*\r
3   RtAudio - realtime sound I/O C++ class\r
4   by Gary P. Scavone, 2001-2002.\r
5 */\r
6 /******************************************/\r
7 \r
8 #include "RtAudio.h"\r
9 #include <vector>\r
10 #include <stdio.h>\r
11 \r
12 // Static variable definitions.\r
13 const unsigned int RtAudio :: SAMPLE_RATES[] = {\r
14   4000, 5512, 8000, 9600, 11025, 16000, 22050,\r
15   32000, 44100, 48000, 88200, 96000, 176400, 192000\r
16 };\r
17 const RtAudio::RTAUDIO_FORMAT RtAudio :: RTAUDIO_SINT8 = 1;\r
18 const RtAudio::RTAUDIO_FORMAT RtAudio :: RTAUDIO_SINT16 = 2;\r
19 const RtAudio::RTAUDIO_FORMAT RtAudio :: RTAUDIO_SINT24 = 4;\r
20 const RtAudio::RTAUDIO_FORMAT RtAudio :: RTAUDIO_SINT32 = 8;\r
21 const RtAudio::RTAUDIO_FORMAT RtAudio :: RTAUDIO_FLOAT32 = 16;\r
22 const RtAudio::RTAUDIO_FORMAT RtAudio :: RTAUDIO_FLOAT64 = 32;\r
23 \r
24 #if defined(__WINDOWS_DS__)\r
25   #define MUTEX_INITIALIZE(A) InitializeCriticalSection(A)\r
26   #define MUTEX_LOCK(A)       EnterCriticalSection(A)\r
27   #define MUTEX_UNLOCK(A)     LeaveCriticalSection(A)\r
28   typedef unsigned THREAD_RETURN;\r
29   typedef unsigned (__stdcall THREAD_FUNCTION)(void *);\r
30 #else // pthread API\r
31   #define MUTEX_INITIALIZE(A) pthread_mutex_init(A, NULL)\r
32   #define MUTEX_LOCK(A)       pthread_mutex_lock(A)\r
33   #define MUTEX_UNLOCK(A)     pthread_mutex_unlock(A)\r
34   typedef void * THREAD_RETURN;\r
35 #endif\r
36 \r
37 // *************************************************** //\r
38 //\r
39 // Public common (OS-independent) methods.\r
40 //\r
41 // *************************************************** //\r
42 \r
43 RtAudio :: RtAudio()\r
44 {\r
45   initialize();\r
46 \r
47   if (nDevices <= 0) {\r
48     sprintf(message, "RtAudio: no audio devices found!");\r
49     error(RtError::NO_DEVICES_FOUND);\r
50  }\r
51 }\r
52 \r
53 RtAudio :: RtAudio(int *streamId,\r
54                    int outputDevice, int outputChannels,\r
55                    int inputDevice, int inputChannels,\r
56                    RTAUDIO_FORMAT format, int sampleRate,\r
57                    int *bufferSize, int numberOfBuffers)\r
58 {\r
59   initialize();\r
60 \r
61   if (nDevices <= 0) {\r
62     sprintf(message, "RtAudio: no audio devices found!");\r
63     error(RtError::NO_DEVICES_FOUND);\r
64   }\r
65 \r
66   try {\r
67     *streamId = openStream(outputDevice, outputChannels, inputDevice, inputChannels,\r
68                            format, sampleRate, bufferSize, numberOfBuffers);\r
69   }\r
70   catch (RtError &exception) {\r
71     // deallocate the RTAUDIO_DEVICE structures\r
72     if (devices) free(devices);\r
73     error(exception.getType());\r
74   }\r
75 }\r
76 \r
77 RtAudio :: ~RtAudio()\r
78 {\r
79   // close any existing streams\r
80   while ( streams.size() )\r
81     closeStream( streams.begin()->first );\r
82 \r
83   // deallocate the RTAUDIO_DEVICE structures\r
84   if (devices) free(devices);\r
85 }\r
86 \r
87 int RtAudio :: openStream(int outputDevice, int outputChannels,\r
88                           int inputDevice, int inputChannels,\r
89                           RTAUDIO_FORMAT format, int sampleRate,\r
90                           int *bufferSize, int numberOfBuffers)\r
91 {\r
92   static int streamKey = 0; // Unique stream identifier ... OK for multiple instances.\r
93 \r
94   if (outputChannels < 1 && inputChannels < 1) {\r
95     sprintf(message,"RtAudio: one or both 'channel' parameters must be greater than zero.");\r
96     error(RtError::INVALID_PARAMETER);\r
97   }\r
98 \r
99   if ( formatBytes(format) == 0 ) {\r
100     sprintf(message,"RtAudio: 'format' parameter value is undefined.");\r
101     error(RtError::INVALID_PARAMETER);\r
102   }\r
103 \r
104   if ( outputChannels > 0 ) {\r
105     if (outputDevice >= nDevices || outputDevice < 0) {\r
106       sprintf(message,"RtAudio: 'outputDevice' parameter value (%d) is invalid.", outputDevice);\r
107       error(RtError::INVALID_PARAMETER);\r
108     }\r
109   }\r
110 \r
111   if ( inputChannels > 0 ) {\r
112     if (inputDevice >= nDevices || inputDevice < 0) {\r
113       sprintf(message,"RtAudio: 'inputDevice' parameter value (%d) is invalid.", inputDevice);\r
114       error(RtError::INVALID_PARAMETER);\r
115     }\r
116   }\r
117 \r
118   // Allocate a new stream structure.\r
119   RTAUDIO_STREAM *stream = (RTAUDIO_STREAM *) calloc(1, sizeof(RTAUDIO_STREAM));\r
120   if (stream == NULL) {\r
121     sprintf(message, "RtAudio: memory allocation error!");\r
122     error(RtError::MEMORY_ERROR);\r
123   }\r
124   streams[++streamKey] = (void *) stream;\r
125   stream->mode = UNINITIALIZED;\r
126   MUTEX_INITIALIZE(&stream->mutex);\r
127 \r
128   bool result = SUCCESS;\r
129   int device;\r
130   STREAM_MODE mode;\r
131   int channels;\r
132   if ( outputChannels > 0 ) {\r
133 \r
134     device = outputDevice;\r
135     mode = PLAYBACK;\r
136     channels = outputChannels;\r
137 \r
138     if (device == 0) { // Try default device first.\r
139       for (int i=0; i<nDevices; i++) {\r
140         if (devices[i].probed == false) {\r
141           // If the device wasn't successfully probed before, try it\r
142           // again now.\r
143           clearDeviceInfo(&devices[i]);\r
144           probeDeviceInfo(&devices[i]);\r
145           if (devices[i].probed == false)\r
146             continue;\r
147         }\r
148         result = probeDeviceOpen(i, stream, mode, channels, sampleRate,\r
149                                  format, bufferSize, numberOfBuffers);\r
150         if (result == SUCCESS)\r
151           break;\r
152       }\r
153     }\r
154     else {\r
155       result = probeDeviceOpen(device, stream, mode, channels, sampleRate,\r
156                                format, bufferSize, numberOfBuffers);\r
157     }\r
158   }\r
159 \r
160   if ( inputChannels > 0 && result == SUCCESS ) {\r
161 \r
162     device = inputDevice;\r
163     mode = RECORD;\r
164     channels = inputChannels;\r
165 \r
166     if (device == 0) { // Try default device first.\r
167       for (int i=0; i<nDevices; i++) {\r
168         if (devices[i].probed == false) {\r
169           // If the device wasn't successfully probed before, try it\r
170           // again now.\r
171           clearDeviceInfo(&devices[i]);\r
172           probeDeviceInfo(&devices[i]);\r
173           if (devices[i].probed == false)\r
174             continue;\r
175         }\r
176         result = probeDeviceOpen(i, stream, mode, channels, sampleRate,\r
177                                  format, bufferSize, numberOfBuffers);\r
178         if (result == SUCCESS)\r
179           break;\r
180       }\r
181     }\r
182     else {\r
183       result = probeDeviceOpen(device, stream, mode, channels, sampleRate,\r
184                                format, bufferSize, numberOfBuffers);\r
185     }\r
186   }\r
187 \r
188   if ( result == SUCCESS )\r
189     return streamKey;\r
190 \r
191   // If we get here, all attempted probes failed.  Close any opened\r
192   // devices and delete the allocated stream.\r
193   closeStream(streamKey);\r
194   sprintf(message,"RtAudio: no devices found for given parameters.");\r
195   error(RtError::INVALID_PARAMETER);\r
196 \r
197   return -1;\r
198 }\r
199 \r
200 int RtAudio :: getDeviceCount(void)\r
201 {\r
202   return nDevices;\r
203 }\r
204 \r
205 void RtAudio :: getDeviceInfo(int device, RTAUDIO_DEVICE *info)\r
206 {\r
207   if (device >= nDevices || device < 0) {\r
208     sprintf(message, "RtAudio: invalid device specifier (%d)!", device);\r
209     error(RtError::INVALID_DEVICE);\r
210   }\r
211 \r
212   // If the device wasn't successfully probed before, try it again.\r
213   if (devices[device].probed == false) {\r
214     clearDeviceInfo(&devices[device]);\r
215     probeDeviceInfo(&devices[device]);\r
216   }\r
217 \r
218   // Clear the info structure.\r
219   memset(info, 0, sizeof(RTAUDIO_DEVICE));\r
220 \r
221   strncpy(info->name, devices[device].name, 128);\r
222   info->probed = devices[device].probed;\r
223   if ( info->probed == true ) {\r
224     info->maxOutputChannels = devices[device].maxOutputChannels;\r
225     info->maxInputChannels = devices[device].maxInputChannels;\r
226     info->maxDuplexChannels = devices[device].maxDuplexChannels;\r
227     info->minOutputChannels = devices[device].minOutputChannels;\r
228     info->minInputChannels = devices[device].minInputChannels;\r
229     info->minDuplexChannels = devices[device].minDuplexChannels;\r
230     info->hasDuplexSupport = devices[device].hasDuplexSupport;\r
231     info->nSampleRates = devices[device].nSampleRates;\r
232     if (info->nSampleRates == -1) {\r
233       info->sampleRates[0] = devices[device].sampleRates[0];\r
234       info->sampleRates[1] = devices[device].sampleRates[1];\r
235     }\r
236     else {\r
237       for (int i=0; i<info->nSampleRates; i++)\r
238         info->sampleRates[i] = devices[device].sampleRates[i];\r
239     }\r
240     info->nativeFormats = devices[device].nativeFormats;\r
241   }\r
242 \r
243   return;\r
244 }\r
245 \r
246 char * const RtAudio :: getStreamBuffer(int streamId)\r
247 {\r
248   RTAUDIO_STREAM *stream = (RTAUDIO_STREAM *) verifyStream(streamId);\r
249 \r
250   return stream->userBuffer;\r
251 }\r
252 \r
253 // This global structure is used to pass information to the thread\r
254 // function.  I tried other methods but had intermittent errors due to\r
255 // variable persistence during thread startup.\r
256 struct {\r
257   RtAudio *object;\r
258   int streamId;\r
259 } thread_info;\r
260 \r
261 extern "C" THREAD_RETURN THREAD_TYPE callbackHandler(void * ptr);\r
262 \r
263 void RtAudio :: setStreamCallback(int streamId, RTAUDIO_CALLBACK callback, void *userData)\r
264 {\r
265   RTAUDIO_STREAM *stream = (RTAUDIO_STREAM *) verifyStream(streamId);\r
266 \r
267   stream->callback = callback;\r
268   stream->userData = userData;\r
269   stream->usingCallback = true;\r
270   thread_info.object = this;\r
271   thread_info.streamId = streamId;\r
272 \r
273   int err = 0;\r
274 #if defined(__WINDOWS_DS__)\r
275   unsigned thread_id;\r
276   stream->thread = _beginthreadex(NULL, 0, &callbackHandler,\r
277                                   &stream->usingCallback, 0, &thread_id);\r
278   if (stream->thread == 0) err = -1;\r
279   // When spawning multiple threads in quick succession, it appears to be\r
280   // necessary to wait a bit for each to initialize ... another windism!\r
281   Sleep(1);\r
282 #else\r
283   err = pthread_create(&stream->thread, NULL, callbackHandler, &stream->usingCallback);\r
284 #endif\r
285 \r
286   if (err) {\r
287     stream->usingCallback = false;\r
288     sprintf(message, "RtAudio: error starting callback thread!");\r
289     error(RtError::THREAD_ERROR);\r
290   }\r
291 }\r
292 \r
293 // *************************************************** //\r
294 //\r
295 // OS/API-specific methods.\r
296 //\r
297 // *************************************************** //\r
298 \r
299 #if defined(__LINUX_ALSA__)\r
300 \r
301 #define MAX_DEVICES 16\r
302 \r
303 void RtAudio :: initialize(void)\r
304 {\r
305   int card, result, device;\r
306   char name[32];\r
307   char deviceNames[MAX_DEVICES][32];\r
308   snd_ctl_t *handle;\r
309   snd_ctl_card_info_t *info;\r
310   snd_ctl_card_info_alloca(&info);\r
311 \r
312   // Count cards and devices\r
313   nDevices = 0;\r
314   card = -1;\r
315   snd_card_next(&card);\r
316   while ( card >= 0 ) {\r
317     sprintf(name, "hw:%d", card);\r
318     result = snd_ctl_open(&handle, name, 0);\r
319     if (result < 0) {\r
320       sprintf(message, "RtAudio: ALSA control open (%i): %s.", card, snd_strerror(result));\r
321       error(RtError::WARNING);\r
322       goto next_card;\r
323                 }\r
324     result = snd_ctl_card_info(handle, info);\r
325                 if (result < 0) {\r
326       sprintf(message, "RtAudio: ALSA control hardware info (%i): %s.", card, snd_strerror(result));\r
327       error(RtError::WARNING);\r
328       goto next_card;\r
329                 }\r
330                 device = -1;\r
331                 while (1) {\r
332       result = snd_ctl_pcm_next_device(handle, &device);\r
333                         if (result < 0) {\r
334         sprintf(message, "RtAudio: ALSA control next device (%i): %s.", card, snd_strerror(result));\r
335         error(RtError::WARNING);\r
336         break;\r
337       }\r
338                         if (device < 0)\r
339         break;\r
340       sprintf( deviceNames[nDevices++], "hw:%d,%d", card, device );\r
341       if ( nDevices > MAX_DEVICES ) break;\r
342     }\r
343     if ( nDevices > MAX_DEVICES ) break;\r
344   next_card:\r
345     snd_ctl_close(handle);\r
346     snd_card_next(&card);\r
347   }\r
348 \r
349   if (nDevices == 0) return;\r
350 \r
351   //  Allocate the RTAUDIO_DEVICE structures.\r
352   devices = (RTAUDIO_DEVICE *) calloc(nDevices, sizeof(RTAUDIO_DEVICE));\r
353   if (devices == NULL) {\r
354     sprintf(message, "RtAudio: memory allocation error!");\r
355     error(RtError::MEMORY_ERROR);\r
356   }\r
357 \r
358   // Write device ascii identifiers to device structures and then\r
359   // probe the device capabilities.\r
360   for (int i=0; i<nDevices; i++) {\r
361     strncpy(devices[i].name, deviceNames[i], 32);\r
362     probeDeviceInfo(&devices[i]);\r
363   }\r
364 \r
365   return;\r
366 }\r
367 \r
368 void RtAudio :: probeDeviceInfo(RTAUDIO_DEVICE *info)\r
369 {\r
370   int err;\r
371   int open_mode = SND_PCM_ASYNC;\r
372   snd_pcm_t *handle;\r
373   snd_pcm_stream_t stream;\r
374 \r
375   // First try for playback\r
376   stream = SND_PCM_STREAM_PLAYBACK;\r
377   err = snd_pcm_open(&handle, info->name, stream, open_mode);\r
378   if (err < 0) {\r
379     sprintf(message, "RtAudio: ALSA pcm playback open (%s): %s.",\r
380             info->name, snd_strerror(err));\r
381     error(RtError::WARNING);\r
382     goto capture_probe;\r
383   }\r
384 \r
385   snd_pcm_hw_params_t *params;\r
386   snd_pcm_hw_params_alloca(&params);\r
387 \r
388   // We have an open device ... allocate the parameter structure.\r
389   err = snd_pcm_hw_params_any(handle, params);\r
390   if (err < 0) {\r
391     snd_pcm_close(handle);\r
392     sprintf(message, "RtAudio: ALSA hardware probe error (%s): %s.",\r
393             info->name, snd_strerror(err));\r
394     error(RtError::WARNING);\r
395     goto capture_probe;\r
396   }\r
397 \r
398   // Get output channel information.\r
399   info->minOutputChannels = snd_pcm_hw_params_get_channels_min(params);\r
400   info->maxOutputChannels = snd_pcm_hw_params_get_channels_max(params);\r
401 \r
402   snd_pcm_close(handle);\r
403 \r
404  capture_probe:\r
405   // Now try for capture\r
406   stream = SND_PCM_STREAM_CAPTURE;\r
407   err = snd_pcm_open(&handle, info->name, stream, open_mode);\r
408   if (err < 0) {\r
409     sprintf(message, "RtAudio: ALSA pcm capture open (%s): %s.",\r
410             info->name, snd_strerror(err));\r
411     error(RtError::WARNING);\r
412     if (info->maxOutputChannels == 0)\r
413       // didn't open for playback either ... device invalid\r
414       return;\r
415     goto probe_parameters;\r
416   }\r
417 \r
418   // We have an open capture device ... allocate the parameter structure.\r
419   err = snd_pcm_hw_params_any(handle, params);\r
420   if (err < 0) {\r
421     snd_pcm_close(handle);\r
422     sprintf(message, "RtAudio: ALSA hardware probe error (%s): %s.",\r
423             info->name, snd_strerror(err));\r
424     error(RtError::WARNING);\r
425     if (info->maxOutputChannels > 0)\r
426       goto probe_parameters;\r
427     else\r
428       return;\r
429   }\r
430 \r
431   // Get input channel information.\r
432   info->minInputChannels = snd_pcm_hw_params_get_channels_min(params);\r
433   info->maxInputChannels = snd_pcm_hw_params_get_channels_max(params);\r
434 \r
435   // If device opens for both playback and capture, we determine the channels.\r
436   if (info->maxOutputChannels == 0 || info->maxInputChannels == 0)\r
437     goto probe_parameters;\r
438 \r
439   info->hasDuplexSupport = true;\r
440   info->maxDuplexChannels = (info->maxOutputChannels > info->maxInputChannels) ?\r
441     info->maxInputChannels : info->maxOutputChannels;\r
442   info->minDuplexChannels = (info->minOutputChannels > info->minInputChannels) ?\r
443     info->minInputChannels : info->minOutputChannels;\r
444 \r
445   snd_pcm_close(handle);\r
446 \r
447  probe_parameters:\r
448   // At this point, we just need to figure out the supported data\r
449   // formats and sample rates.  We'll proceed by opening the device in\r
450   // the direction with the maximum number of channels, or playback if\r
451   // they are equal.  This might limit our sample rate options, but so\r
452   // be it.\r
453 \r
454   if (info->maxOutputChannels >= info->maxInputChannels)\r
455     stream = SND_PCM_STREAM_PLAYBACK;\r
456   else\r
457     stream = SND_PCM_STREAM_CAPTURE;\r
458 \r
459   err = snd_pcm_open(&handle, info->name, stream, open_mode);\r
460   if (err < 0) {\r
461     sprintf(message, "RtAudio: ALSA pcm (%s) won't reopen during probe: %s.",\r
462             info->name, snd_strerror(err));\r
463     error(RtError::WARNING);\r
464     return;\r
465   }\r
466 \r
467   // We have an open device ... allocate the parameter structure.\r
468   err = snd_pcm_hw_params_any(handle, params);\r
469   if (err < 0) {\r
470     snd_pcm_close(handle);\r
471     sprintf(message, "RtAudio: ALSA hardware reopen probe error (%s): %s.",\r
472             info->name, snd_strerror(err));\r
473     error(RtError::WARNING);\r
474     return;\r
475   }\r
476 \r
477   // Test a non-standard sample rate to see if continuous rate is supported.\r
478   int dir = 0;\r
479   if (snd_pcm_hw_params_test_rate(handle, params, 35500, dir) == 0) {\r
480     // It appears that continuous sample rate support is available.\r
481     info->nSampleRates = -1;\r
482     info->sampleRates[0] = snd_pcm_hw_params_get_rate_min(params, &dir);\r
483     info->sampleRates[1] = snd_pcm_hw_params_get_rate_max(params, &dir);\r
484   }\r
485   else {\r
486     // No continuous rate support ... test our discrete set of sample rate values.\r
487     info->nSampleRates = 0;\r
488     for (int i=0; i<MAX_SAMPLE_RATES; i++) {\r
489       if (snd_pcm_hw_params_test_rate(handle, params, SAMPLE_RATES[i], dir) == 0) {\r
490         info->sampleRates[info->nSampleRates] = SAMPLE_RATES[i];\r
491         info->nSampleRates++;\r
492       }\r
493     }\r
494     if (info->nSampleRates == 0) {\r
495       snd_pcm_close(handle);\r
496       return;\r
497     }\r
498   }\r
499 \r
500   // Probe the supported data formats ... we don't care about endian-ness just yet\r
501   snd_pcm_format_t format;\r
502   info->nativeFormats = 0;\r
503   format = SND_PCM_FORMAT_S8;\r
504   if (snd_pcm_hw_params_test_format(handle, params, format) == 0)\r
505     info->nativeFormats |= RTAUDIO_SINT8;\r
506   format = SND_PCM_FORMAT_S16;\r
507   if (snd_pcm_hw_params_test_format(handle, params, format) == 0)\r
508     info->nativeFormats |= RTAUDIO_SINT16;\r
509   format = SND_PCM_FORMAT_S24;\r
510   if (snd_pcm_hw_params_test_format(handle, params, format) == 0)\r
511     info->nativeFormats |= RTAUDIO_SINT24;\r
512   format = SND_PCM_FORMAT_S32;\r
513   if (snd_pcm_hw_params_test_format(handle, params, format) == 0)\r
514     info->nativeFormats |= RTAUDIO_SINT32;\r
515   format = SND_PCM_FORMAT_FLOAT;\r
516   if (snd_pcm_hw_params_test_format(handle, params, format) == 0)\r
517     info->nativeFormats |= RTAUDIO_FLOAT32;\r
518   format = SND_PCM_FORMAT_FLOAT64;\r
519   if (snd_pcm_hw_params_test_format(handle, params, format) == 0)\r
520     info->nativeFormats |= RTAUDIO_FLOAT64;\r
521 \r
522   // Check that we have at least one supported format\r
523   if (info->nativeFormats == 0) {\r
524     snd_pcm_close(handle);\r
525     sprintf(message, "RtAudio: ALSA PCM device (%s) data format not supported by RtAudio.",\r
526             info->name);\r
527     error(RtError::WARNING);\r
528     return;\r
529   }\r
530 \r
531   // That's all ... close the device and return\r
532   snd_pcm_close(handle);\r
533   info->probed = true;\r
534   return;\r
535 }\r
536 \r
537 bool RtAudio :: probeDeviceOpen(int device, RTAUDIO_STREAM *stream,\r
538                                 STREAM_MODE mode, int channels, \r
539                                 int sampleRate, RTAUDIO_FORMAT format,\r
540                                 int *bufferSize, int numberOfBuffers)\r
541 {\r
542 #if defined(RTAUDIO_DEBUG)\r
543   snd_output_t *out;\r
544   snd_output_stdio_attach(&out, stderr, 0);\r
545 #endif\r
546 \r
547   // I'm not using the "plug" interface ... too much inconsistent behavior.\r
548   const char *name = devices[device].name;\r
549 \r
550   snd_pcm_stream_t alsa_stream;\r
551   if (mode == PLAYBACK)\r
552     alsa_stream = SND_PCM_STREAM_PLAYBACK;\r
553   else\r
554     alsa_stream = SND_PCM_STREAM_CAPTURE;\r
555 \r
556   int err;\r
557   snd_pcm_t *handle;\r
558   int alsa_open_mode = SND_PCM_ASYNC;\r
559   err = snd_pcm_open(&handle, name, alsa_stream, alsa_open_mode);\r
560   if (err < 0) {\r
561     sprintf(message,"RtAudio: ALSA pcm device (%s) won't open: %s.",\r
562             name, snd_strerror(err));\r
563     error(RtError::WARNING);\r
564     return FAILURE;\r
565   }\r
566 \r
567   // Fill the parameter structure.\r
568   snd_pcm_hw_params_t *hw_params;\r
569   snd_pcm_hw_params_alloca(&hw_params);\r
570   err = snd_pcm_hw_params_any(handle, hw_params);\r
571   if (err < 0) {\r
572     snd_pcm_close(handle);\r
573     sprintf(message, "RtAudio: ALSA error getting parameter handle (%s): %s.",\r
574             name, snd_strerror(err));\r
575     error(RtError::WARNING);\r
576     return FAILURE;\r
577   }\r
578 \r
579 #if defined(RTAUDIO_DEBUG)\r
580   fprintf(stderr, "\nRtAudio: ALSA dump hardware params just after device open:\n\n");\r
581   snd_pcm_hw_params_dump(hw_params, out);\r
582 #endif\r
583 \r
584   // Set access ... try interleaved access first, then non-interleaved\r
585   err = snd_pcm_hw_params_set_access(handle, hw_params, SND_PCM_ACCESS_RW_INTERLEAVED);\r
586   if (err < 0) {\r
587     // No interleave support ... try non-interleave.\r
588                 err = snd_pcm_hw_params_set_access(handle, hw_params, SND_PCM_ACCESS_RW_NONINTERLEAVED);\r
589     if (err < 0) {\r
590       snd_pcm_close(handle);\r
591       sprintf(message, "RtAudio: ALSA error setting access ( (%s): %s.",\r
592               name, snd_strerror(err));\r
593       error(RtError::WARNING);\r
594       return FAILURE;\r
595     }\r
596     stream->deInterleave[mode] = true;\r
597   }\r
598 \r
599   // Determine how to set the device format.\r
600   stream->userFormat = format;\r
601   snd_pcm_format_t device_format;\r
602 \r
603   if (format == RTAUDIO_SINT8)\r
604     device_format = SND_PCM_FORMAT_S8;\r
605   else if (format == RTAUDIO_SINT16)\r
606     device_format = SND_PCM_FORMAT_S16;\r
607   else if (format == RTAUDIO_SINT24)\r
608     device_format = SND_PCM_FORMAT_S24;\r
609   else if (format == RTAUDIO_SINT32)\r
610     device_format = SND_PCM_FORMAT_S32;\r
611   else if (format == RTAUDIO_FLOAT32)\r
612     device_format = SND_PCM_FORMAT_FLOAT;\r
613   else if (format == RTAUDIO_FLOAT64)\r
614     device_format = SND_PCM_FORMAT_FLOAT64;\r
615 \r
616   if (snd_pcm_hw_params_test_format(handle, hw_params, device_format) == 0) {\r
617     stream->deviceFormat[mode] = format;\r
618     goto set_format;\r
619   }\r
620 \r
621   // The user requested format is not natively supported by the device.\r
622   device_format = SND_PCM_FORMAT_FLOAT64;\r
623   if (snd_pcm_hw_params_test_format(handle, hw_params, device_format) == 0) {\r
624     stream->deviceFormat[mode] = RTAUDIO_FLOAT64;\r
625     goto set_format;\r
626   }\r
627 \r
628   device_format = SND_PCM_FORMAT_FLOAT;\r
629   if (snd_pcm_hw_params_test_format(handle, hw_params, device_format) == 0) {\r
630     stream->deviceFormat[mode] = RTAUDIO_FLOAT32;\r
631     goto set_format;\r
632   }\r
633 \r
634   device_format = SND_PCM_FORMAT_S32;\r
635   if (snd_pcm_hw_params_test_format(handle, hw_params, device_format) == 0) {\r
636     stream->deviceFormat[mode] = RTAUDIO_SINT32;\r
637     goto set_format;\r
638   }\r
639 \r
640   device_format = SND_PCM_FORMAT_S24;\r
641   if (snd_pcm_hw_params_test_format(handle, hw_params, device_format) == 0) {\r
642     stream->deviceFormat[mode] = RTAUDIO_SINT24;\r
643     goto set_format;\r
644   }\r
645 \r
646   device_format = SND_PCM_FORMAT_S16;\r
647   if (snd_pcm_hw_params_test_format(handle, hw_params, device_format) == 0) {\r
648     stream->deviceFormat[mode] = RTAUDIO_SINT16;\r
649     goto set_format;\r
650   }\r
651 \r
652   device_format = SND_PCM_FORMAT_S8;\r
653   if (snd_pcm_hw_params_test_format(handle, hw_params, device_format) == 0) {\r
654     stream->deviceFormat[mode] = RTAUDIO_SINT8;\r
655     goto set_format;\r
656   }\r
657 \r
658   // If we get here, no supported format was found.\r
659   sprintf(message,"RtAudio: ALSA pcm device (%s) data format not supported by RtAudio.", name);\r
660   snd_pcm_close(handle);\r
661   error(RtError::WARNING);\r
662   return FAILURE;\r
663 \r
664  set_format:\r
665   err = snd_pcm_hw_params_set_format(handle, hw_params, device_format);\r
666   if (err < 0) {\r
667     snd_pcm_close(handle);\r
668     sprintf(message, "RtAudio: ALSA error setting format (%s): %s.",\r
669             name, snd_strerror(err));\r
670     error(RtError::WARNING);\r
671     return FAILURE;\r
672   }\r
673 \r
674   // Determine whether byte-swaping is necessary.\r
675   stream->doByteSwap[mode] = false;\r
676   if (device_format != SND_PCM_FORMAT_S8) {\r
677     err = snd_pcm_format_cpu_endian(device_format);\r
678     if (err == 0)\r
679       stream->doByteSwap[mode] = true;\r
680     else if (err < 0) {\r
681       snd_pcm_close(handle);\r
682       sprintf(message, "RtAudio: ALSA error getting format endian-ness (%s): %s.",\r
683               name, snd_strerror(err));\r
684       error(RtError::WARNING);\r
685       return FAILURE;\r
686     }\r
687   }\r
688 \r
689   // Determine the number of channels for this device.  We support a possible\r
690   // minimum device channel number > than the value requested by the user.\r
691   stream->nUserChannels[mode] = channels;\r
692   int device_channels = snd_pcm_hw_params_get_channels_max(hw_params);\r
693   if (device_channels < channels) {\r
694     snd_pcm_close(handle);\r
695     sprintf(message, "RtAudio: channels (%d) not supported by device (%s).",\r
696             channels, name);\r
697     error(RtError::WARNING);\r
698     return FAILURE;\r
699   }\r
700 \r
701   device_channels = snd_pcm_hw_params_get_channels_min(hw_params);\r
702   if (device_channels < channels) device_channels = channels;\r
703   stream->nDeviceChannels[mode] = device_channels;\r
704 \r
705   // Set the device channels.\r
706   err = snd_pcm_hw_params_set_channels(handle, hw_params, device_channels);\r
707   if (err < 0) {\r
708     snd_pcm_close(handle);\r
709     sprintf(message, "RtAudio: ALSA error setting channels (%d) on device (%s): %s.",\r
710             device_channels, name, snd_strerror(err));\r
711     error(RtError::WARNING);\r
712     return FAILURE;\r
713   }\r
714 \r
715   // Set the sample rate.\r
716   err = snd_pcm_hw_params_set_rate(handle, hw_params, (unsigned int)sampleRate, 0);\r
717   if (err < 0) {\r
718     snd_pcm_close(handle);\r
719     sprintf(message, "RtAudio: ALSA error setting sample rate (%d) on device (%s): %s.",\r
720             sampleRate, name, snd_strerror(err));\r
721     error(RtError::WARNING);\r
722     return FAILURE;\r
723   }\r
724 \r
725   // Set the buffer number, which in ALSA is referred to as the "period".\r
726   int dir;\r
727   int periods = numberOfBuffers;\r
728   // Even though the hardware might allow 1 buffer, it won't work reliably.\r
729   if (periods < 2) periods = 2;\r
730   err = snd_pcm_hw_params_get_periods_min(hw_params, &dir);\r
731   if (err > periods) periods = err;\r
732 \r
733   err = snd_pcm_hw_params_set_periods(handle, hw_params, periods, 0);\r
734   if (err < 0) {\r
735     snd_pcm_close(handle);\r
736     sprintf(message, "RtAudio: ALSA error setting periods (%s): %s.",\r
737             name, snd_strerror(err));\r
738     error(RtError::WARNING);\r
739     return FAILURE;\r
740   }\r
741 \r
742   // Set the buffer (or period) size.\r
743   err = snd_pcm_hw_params_get_period_size_min(hw_params, &dir);\r
744   if (err > *bufferSize) *bufferSize = err;\r
745 \r
746   err = snd_pcm_hw_params_set_period_size(handle, hw_params, *bufferSize, 0);\r
747   if (err < 0) {\r
748     snd_pcm_close(handle);\r
749     sprintf(message, "RtAudio: ALSA error setting period size (%s): %s.",\r
750             name, snd_strerror(err));\r
751     error(RtError::WARNING);\r
752     return FAILURE;\r
753   }\r
754 \r
755   stream->bufferSize = *bufferSize;\r
756 \r
757   // Install the hardware configuration\r
758   err = snd_pcm_hw_params(handle, hw_params);\r
759   if (err < 0) {\r
760     snd_pcm_close(handle);\r
761     sprintf(message, "RtAudio: ALSA error installing hardware configuration (%s): %s.",\r
762             name, snd_strerror(err));\r
763     error(RtError::WARNING);\r
764     return FAILURE;\r
765   }\r
766 \r
767 #if defined(RTAUDIO_DEBUG)\r
768   fprintf(stderr, "\nRtAudio: ALSA dump hardware params after installation:\n\n");\r
769   snd_pcm_hw_params_dump(hw_params, out);\r
770 #endif\r
771 \r
772   /*\r
773   // Install the software configuration\r
774   snd_pcm_sw_params_t *sw_params = NULL;\r
775   snd_pcm_sw_params_alloca(&sw_params);\r
776   snd_pcm_sw_params_current(handle, sw_params);\r
777   err = snd_pcm_sw_params(handle, sw_params);\r
778   if (err < 0) {\r
779     snd_pcm_close(handle);\r
780     sprintf(message, "RtAudio: ALSA error installing software configuration (%s): %s.",\r
781             name, snd_strerror(err));\r
782     error(RtError::WARNING);\r
783     return FAILURE;\r
784   }\r
785   */\r
786 \r
787   // Set handle and flags for buffer conversion\r
788   stream->handle[mode] = handle;\r
789   stream->doConvertBuffer[mode] = false;\r
790   if (stream->userFormat != stream->deviceFormat[mode])\r
791     stream->doConvertBuffer[mode] = true;\r
792   if (stream->nUserChannels[mode] < stream->nDeviceChannels[mode])\r
793     stream->doConvertBuffer[mode] = true;\r
794   if (stream->nUserChannels[mode] > 1 && stream->deInterleave[mode])\r
795     stream->doConvertBuffer[mode] = true;\r
796 \r
797   // Allocate necessary internal buffers\r
798   if ( stream->nUserChannels[0] != stream->nUserChannels[1] ) {\r
799 \r
800     long buffer_bytes;\r
801     if (stream->nUserChannels[0] >= stream->nUserChannels[1])\r
802       buffer_bytes = stream->nUserChannels[0];\r
803     else\r
804       buffer_bytes = stream->nUserChannels[1];\r
805 \r
806     buffer_bytes *= *bufferSize * formatBytes(stream->userFormat);\r
807     if (stream->userBuffer) free(stream->userBuffer);\r
808     stream->userBuffer = (char *) calloc(buffer_bytes, 1);\r
809     if (stream->userBuffer == NULL)\r
810       goto memory_error;\r
811   }\r
812 \r
813   if ( stream->doConvertBuffer[mode] ) {\r
814 \r
815     long buffer_bytes;\r
816     bool makeBuffer = true;\r
817     if ( mode == PLAYBACK )\r
818       buffer_bytes = stream->nDeviceChannels[0] * formatBytes(stream->deviceFormat[0]);\r
819     else { // mode == RECORD\r
820       buffer_bytes = stream->nDeviceChannels[1] * formatBytes(stream->deviceFormat[1]);\r
821       if ( stream->mode == PLAYBACK ) {\r
822         long bytes_out = stream->nDeviceChannels[0] * formatBytes(stream->deviceFormat[0]);\r
823         if ( buffer_bytes > bytes_out )\r
824           buffer_bytes = (buffer_bytes > bytes_out) ? buffer_bytes : bytes_out;\r
825         else\r
826           makeBuffer = false;\r
827       }\r
828     }\r
829 \r
830     if ( makeBuffer ) {\r
831       buffer_bytes *= *bufferSize;\r
832       if (stream->deviceBuffer) free(stream->deviceBuffer);\r
833       stream->deviceBuffer = (char *) calloc(buffer_bytes, 1);\r
834       if (stream->deviceBuffer == NULL)\r
835         goto memory_error;\r
836     }\r
837   }\r
838 \r
839   stream->device[mode] = device;\r
840   stream->state = STREAM_STOPPED;\r
841   if ( stream->mode == PLAYBACK && mode == RECORD )\r
842     // We had already set up an output stream.\r
843     stream->mode = DUPLEX;\r
844   else\r
845     stream->mode = mode;\r
846   stream->nBuffers = periods;\r
847   stream->sampleRate = sampleRate;\r
848 \r
849   return SUCCESS;\r
850 \r
851  memory_error:\r
852   if (stream->handle[0]) {\r
853     snd_pcm_close(stream->handle[0]);\r
854     stream->handle[0] = 0;\r
855   }\r
856   if (stream->handle[1]) {\r
857     snd_pcm_close(stream->handle[1]);\r
858     stream->handle[1] = 0;\r
859   }\r
860   if (stream->userBuffer) {\r
861     free(stream->userBuffer);\r
862     stream->userBuffer = 0;\r
863   }\r
864   sprintf(message, "RtAudio: ALSA error allocating buffer memory (%s).", name);\r
865   error(RtError::WARNING);\r
866   return FAILURE;\r
867 }\r
868 \r
869 void RtAudio :: cancelStreamCallback(int streamId)\r
870 {\r
871   RTAUDIO_STREAM *stream = (RTAUDIO_STREAM *) verifyStream(streamId);\r
872 \r
873   if (stream->usingCallback) {\r
874     stream->usingCallback = false;\r
875     pthread_cancel(stream->thread);\r
876     pthread_join(stream->thread, NULL);\r
877     stream->thread = 0;\r
878     stream->callback = NULL;\r
879     stream->userData = NULL;\r
880   }\r
881 }\r
882 \r
883 void RtAudio :: closeStream(int streamId)\r
884 {\r
885   // We don't want an exception to be thrown here because this\r
886   // function is called by our class destructor.  So, do our own\r
887   // streamId check.\r
888   if ( streams.find( streamId ) == streams.end() ) {\r
889     sprintf(message, "RtAudio: invalid stream identifier!");\r
890     error(RtError::WARNING);\r
891     return;\r
892   }\r
893 \r
894   RTAUDIO_STREAM *stream = (RTAUDIO_STREAM *) streams[streamId];\r
895 \r
896   if (stream->usingCallback) {\r
897     pthread_cancel(stream->thread);\r
898     pthread_join(stream->thread, NULL);\r
899   }\r
900 \r
901   if (stream->state == STREAM_RUNNING) {\r
902     if (stream->mode == PLAYBACK || stream->mode == DUPLEX)\r
903       snd_pcm_drop(stream->handle[0]);\r
904     if (stream->mode == RECORD || stream->mode == DUPLEX)\r
905       snd_pcm_drop(stream->handle[1]);\r
906   }\r
907 \r
908   pthread_mutex_destroy(&stream->mutex);\r
909 \r
910   if (stream->handle[0])\r
911     snd_pcm_close(stream->handle[0]);\r
912 \r
913   if (stream->handle[1])\r
914     snd_pcm_close(stream->handle[1]);\r
915 \r
916   if (stream->userBuffer)\r
917     free(stream->userBuffer);\r
918 \r
919   if (stream->deviceBuffer)\r
920     free(stream->deviceBuffer);\r
921 \r
922   free(stream);\r
923   streams.erase(streamId);\r
924 }\r
925 \r
926 void RtAudio :: startStream(int streamId)\r
927 {\r
928   // This method calls snd_pcm_prepare if the device isn't already in that state.\r
929 \r
930   RTAUDIO_STREAM *stream = (RTAUDIO_STREAM *) verifyStream(streamId);\r
931 \r
932   MUTEX_LOCK(&stream->mutex);\r
933 \r
934   if (stream->state == STREAM_RUNNING)\r
935     goto unlock;\r
936 \r
937   int err;\r
938   snd_pcm_state_t state;\r
939   if (stream->mode == PLAYBACK || stream->mode == DUPLEX) {\r
940     state = snd_pcm_state(stream->handle[0]);\r
941     if (state != SND_PCM_STATE_PREPARED) {\r
942       err = snd_pcm_prepare(stream->handle[0]);\r
943       if (err < 0) {\r
944         sprintf(message, "RtAudio: ALSA error preparing pcm device (%s): %s.",\r
945                 devices[stream->device[0]].name, snd_strerror(err));\r
946         MUTEX_UNLOCK(&stream->mutex);\r
947         error(RtError::DRIVER_ERROR);\r
948       }\r
949     }\r
950   }\r
951 \r
952   if (stream->mode == RECORD || stream->mode == DUPLEX) {\r
953     state = snd_pcm_state(stream->handle[1]);\r
954     if (state != SND_PCM_STATE_PREPARED) {\r
955       err = snd_pcm_prepare(stream->handle[1]);\r
956       if (err < 0) {\r
957         sprintf(message, "RtAudio: ALSA error preparing pcm device (%s): %s.",\r
958                 devices[stream->device[1]].name, snd_strerror(err));\r
959         MUTEX_UNLOCK(&stream->mutex);\r
960         error(RtError::DRIVER_ERROR);\r
961       }\r
962     }\r
963   }\r
964   stream->state = STREAM_RUNNING;\r
965 \r
966  unlock:\r
967   MUTEX_UNLOCK(&stream->mutex);\r
968 }\r
969 \r
970 void RtAudio :: stopStream(int streamId)\r
971 {\r
972   RTAUDIO_STREAM *stream = (RTAUDIO_STREAM *) verifyStream(streamId);\r
973 \r
974   MUTEX_LOCK(&stream->mutex);\r
975 \r
976   if (stream->state == STREAM_STOPPED)\r
977     goto unlock;\r
978 \r
979   int err;\r
980   if (stream->mode == PLAYBACK || stream->mode == DUPLEX) {\r
981     err = snd_pcm_drain(stream->handle[0]);\r
982     if (err < 0) {\r
983       sprintf(message, "RtAudio: ALSA error draining pcm device (%s): %s.",\r
984               devices[stream->device[0]].name, snd_strerror(err));\r
985       MUTEX_UNLOCK(&stream->mutex);\r
986       error(RtError::DRIVER_ERROR);\r
987     }\r
988   }\r
989 \r
990   if (stream->mode == RECORD || stream->mode == DUPLEX) {\r
991     err = snd_pcm_drain(stream->handle[1]);\r
992     if (err < 0) {\r
993       sprintf(message, "RtAudio: ALSA error draining pcm device (%s): %s.",\r
994               devices[stream->device[1]].name, snd_strerror(err));\r
995       MUTEX_UNLOCK(&stream->mutex);\r
996       error(RtError::DRIVER_ERROR);\r
997     }\r
998   }\r
999   stream->state = STREAM_STOPPED;\r
1000 \r
1001  unlock:\r
1002   MUTEX_UNLOCK(&stream->mutex);\r
1003 }\r
1004 \r
1005 void RtAudio :: abortStream(int streamId)\r
1006 {\r
1007   RTAUDIO_STREAM *stream = (RTAUDIO_STREAM *) verifyStream(streamId);\r
1008 \r
1009   MUTEX_LOCK(&stream->mutex);\r
1010 \r
1011   if (stream->state == STREAM_STOPPED)\r
1012     goto unlock;\r
1013 \r
1014   int err;\r
1015   if (stream->mode == PLAYBACK || stream->mode == DUPLEX) {\r
1016     err = snd_pcm_drop(stream->handle[0]);\r
1017     if (err < 0) {\r
1018       sprintf(message, "RtAudio: ALSA error draining pcm device (%s): %s.",\r
1019               devices[stream->device[0]].name, snd_strerror(err));\r
1020       MUTEX_UNLOCK(&stream->mutex);\r
1021       error(RtError::DRIVER_ERROR);\r
1022     }\r
1023   }\r
1024 \r
1025   if (stream->mode == RECORD || stream->mode == DUPLEX) {\r
1026     err = snd_pcm_drop(stream->handle[1]);\r
1027     if (err < 0) {\r
1028       sprintf(message, "RtAudio: ALSA error draining pcm device (%s): %s.",\r
1029               devices[stream->device[1]].name, snd_strerror(err));\r
1030       MUTEX_UNLOCK(&stream->mutex);\r
1031       error(RtError::DRIVER_ERROR);\r
1032     }\r
1033   }\r
1034   stream->state = STREAM_STOPPED;\r
1035 \r
1036  unlock:\r
1037   MUTEX_UNLOCK(&stream->mutex);\r
1038 }\r
1039 \r
1040 int RtAudio :: streamWillBlock(int streamId)\r
1041 {\r
1042   RTAUDIO_STREAM *stream = (RTAUDIO_STREAM *) verifyStream(streamId);\r
1043 \r
1044   MUTEX_LOCK(&stream->mutex);\r
1045 \r
1046   int err = 0, frames = 0;\r
1047   if (stream->state == STREAM_STOPPED)\r
1048     goto unlock;\r
1049 \r
1050   if (stream->mode == PLAYBACK || stream->mode == DUPLEX) {\r
1051     err = snd_pcm_avail_update(stream->handle[0]);\r
1052     if (err < 0) {\r
1053       sprintf(message, "RtAudio: ALSA error getting available frames for device (%s): %s.",\r
1054               devices[stream->device[0]].name, snd_strerror(err));\r
1055       MUTEX_UNLOCK(&stream->mutex);\r
1056       error(RtError::DRIVER_ERROR);\r
1057     }\r
1058   }\r
1059 \r
1060   frames = err;\r
1061 \r
1062   if (stream->mode == RECORD || stream->mode == DUPLEX) {\r
1063     err = snd_pcm_avail_update(stream->handle[1]);\r
1064     if (err < 0) {\r
1065       sprintf(message, "RtAudio: ALSA error getting available frames for device (%s): %s.",\r
1066               devices[stream->device[1]].name, snd_strerror(err));\r
1067       MUTEX_UNLOCK(&stream->mutex);\r
1068       error(RtError::DRIVER_ERROR);\r
1069     }\r
1070     if (frames > err) frames = err;\r
1071   }\r
1072 \r
1073   frames = stream->bufferSize - frames;\r
1074   if (frames < 0) frames = 0;\r
1075 \r
1076  unlock:\r
1077   MUTEX_UNLOCK(&stream->mutex);\r
1078   return frames;\r
1079 }\r
1080 \r
1081 void RtAudio :: tickStream(int streamId)\r
1082 {\r
1083   RTAUDIO_STREAM *stream = (RTAUDIO_STREAM *) verifyStream(streamId);\r
1084 \r
1085   int stopStream = 0;\r
1086   if (stream->state == STREAM_STOPPED) {\r
1087     if (stream->usingCallback) usleep(50000); // sleep 50 milliseconds\r
1088     return;\r
1089   }\r
1090   else if (stream->usingCallback) {\r
1091     stopStream = stream->callback(stream->userBuffer, stream->bufferSize, stream->userData);\r
1092   }\r
1093 \r
1094   MUTEX_LOCK(&stream->mutex);\r
1095 \r
1096   // The state might change while waiting on a mutex.\r
1097   if (stream->state == STREAM_STOPPED)\r
1098     goto unlock;\r
1099 \r
1100   int err;\r
1101   char *buffer;\r
1102   int channels;\r
1103   RTAUDIO_FORMAT format;\r
1104   if (stream->mode == PLAYBACK || stream->mode == DUPLEX) {\r
1105 \r
1106     // Setup parameters and do buffer conversion if necessary.\r
1107     if (stream->doConvertBuffer[0]) {\r
1108       convertStreamBuffer(stream, PLAYBACK);\r
1109       buffer = stream->deviceBuffer;\r
1110       channels = stream->nDeviceChannels[0];\r
1111       format = stream->deviceFormat[0];\r
1112     }\r
1113     else {\r
1114       buffer = stream->userBuffer;\r
1115       channels = stream->nUserChannels[0];\r
1116       format = stream->userFormat;\r
1117     }\r
1118 \r
1119     // Do byte swapping if necessary.\r
1120     if (stream->doByteSwap[0])\r
1121       byteSwapBuffer(buffer, stream->bufferSize * channels, format);\r
1122 \r
1123     // Write samples to device in interleaved/non-interleaved format.\r
1124     if (stream->deInterleave[0]) {\r
1125       void *bufs[channels];\r
1126       size_t offset = stream->bufferSize * formatBytes(format);\r
1127       for (int i=0; i<channels; i++)\r
1128         bufs[i] = (void *) (buffer + (i * offset));\r
1129       err = snd_pcm_writen(stream->handle[0], bufs, stream->bufferSize);\r
1130     }\r
1131     else\r
1132       err = snd_pcm_writei(stream->handle[0], buffer, stream->bufferSize);\r
1133 \r
1134     if (err < stream->bufferSize) {\r
1135       // Either an error or underrun occured.\r
1136       if (err == -EPIPE) {\r
1137         snd_pcm_state_t state = snd_pcm_state(stream->handle[0]);\r
1138         if (state == SND_PCM_STATE_XRUN) {\r
1139           sprintf(message, "RtAudio: ALSA underrun detected.");\r
1140           error(RtError::WARNING);\r
1141           err = snd_pcm_prepare(stream->handle[0]);\r
1142           if (err < 0) {\r
1143             sprintf(message, "RtAudio: ALSA error preparing handle after underrun: %s.",\r
1144                     snd_strerror(err));\r
1145             MUTEX_UNLOCK(&stream->mutex);\r
1146             error(RtError::DRIVER_ERROR);\r
1147           }\r
1148         }\r
1149         else {\r
1150           sprintf(message, "RtAudio: ALSA error, current state is %s.",\r
1151                   snd_pcm_state_name(state));\r
1152           MUTEX_UNLOCK(&stream->mutex);\r
1153           error(RtError::DRIVER_ERROR);\r
1154         }\r
1155         goto unlock;\r
1156       }\r
1157       else {\r
1158         sprintf(message, "RtAudio: ALSA audio write error for device (%s): %s.",\r
1159                 devices[stream->device[0]].name, snd_strerror(err));\r
1160         MUTEX_UNLOCK(&stream->mutex);\r
1161         error(RtError::DRIVER_ERROR);\r
1162       }\r
1163     }\r
1164   }\r
1165 \r
1166   if (stream->mode == RECORD || stream->mode == DUPLEX) {\r
1167 \r
1168     // Setup parameters.\r
1169     if (stream->doConvertBuffer[1]) {\r
1170       buffer = stream->deviceBuffer;\r
1171       channels = stream->nDeviceChannels[1];\r
1172       format = stream->deviceFormat[1];\r
1173     }\r
1174     else {\r
1175       buffer = stream->userBuffer;\r
1176       channels = stream->nUserChannels[1];\r
1177       format = stream->userFormat;\r
1178     }\r
1179 \r
1180     // Read samples from device in interleaved/non-interleaved format.\r
1181     if (stream->deInterleave[1]) {\r
1182       void *bufs[channels];\r
1183       size_t offset = stream->bufferSize * formatBytes(format);\r
1184       for (int i=0; i<channels; i++)\r
1185         bufs[i] = (void *) (buffer + (i * offset));\r
1186       err = snd_pcm_readn(stream->handle[1], bufs, stream->bufferSize);\r
1187     }\r
1188     else\r
1189       err = snd_pcm_readi(stream->handle[1], buffer, stream->bufferSize);\r
1190 \r
1191     if (err < stream->bufferSize) {\r
1192       // Either an error or underrun occured.\r
1193       if (err == -EPIPE) {\r
1194         snd_pcm_state_t state = snd_pcm_state(stream->handle[1]);\r
1195         if (state == SND_PCM_STATE_XRUN) {\r
1196           sprintf(message, "RtAudio: ALSA overrun detected.");\r
1197           error(RtError::WARNING);\r
1198           err = snd_pcm_prepare(stream->handle[1]);\r
1199           if (err < 0) {\r
1200             sprintf(message, "RtAudio: ALSA error preparing handle after overrun: %s.",\r
1201                     snd_strerror(err));\r
1202             MUTEX_UNLOCK(&stream->mutex);\r
1203             error(RtError::DRIVER_ERROR);\r
1204           }\r
1205         }\r
1206         else {\r
1207           sprintf(message, "RtAudio: ALSA error, current state is %s.",\r
1208                   snd_pcm_state_name(state));\r
1209           MUTEX_UNLOCK(&stream->mutex);\r
1210           error(RtError::DRIVER_ERROR);\r
1211         }\r
1212         goto unlock;\r
1213       }\r
1214       else {\r
1215         sprintf(message, "RtAudio: ALSA audio read error for device (%s): %s.",\r
1216                 devices[stream->device[1]].name, snd_strerror(err));\r
1217         MUTEX_UNLOCK(&stream->mutex);\r
1218         error(RtError::DRIVER_ERROR);\r
1219       }\r
1220     }\r
1221 \r
1222     // Do byte swapping if necessary.\r
1223     if (stream->doByteSwap[1])\r
1224       byteSwapBuffer(buffer, stream->bufferSize * channels, format);\r
1225 \r
1226     // Do buffer conversion if necessary.\r
1227     if (stream->doConvertBuffer[1])\r
1228       convertStreamBuffer(stream, RECORD);\r
1229   }\r
1230 \r
1231  unlock:\r
1232   MUTEX_UNLOCK(&stream->mutex);\r
1233 \r
1234   if (stream->usingCallback && stopStream)\r
1235     this->stopStream(streamId);\r
1236 }\r
1237 \r
1238 extern "C" void *callbackHandler(void *ptr)\r
1239 {\r
1240   RtAudio *object = thread_info.object;\r
1241   int stream = thread_info.streamId;\r
1242   bool *usingCallback = (bool *) ptr;\r
1243 \r
1244   while ( *usingCallback ) {\r
1245     pthread_testcancel();\r
1246     try {\r
1247       object->tickStream(stream);\r
1248     }\r
1249     catch (RtError &exception) {\r
1250       fprintf(stderr, "\nCallback thread error (%s) ... closing thread.\n\n",\r
1251               exception.getMessage());\r
1252       break;\r
1253     }\r
1254   }\r
1255 \r
1256   return 0;\r
1257 }\r
1258 \r
1259 //******************** End of __LINUX_ALSA__ *********************//\r
1260 \r
1261 #elif defined(__LINUX_OSS__)\r
1262 \r
1263 #include <sys/stat.h>\r
1264 #include <sys/types.h>\r
1265 #include <sys/ioctl.h>\r
1266 #include <unistd.h>\r
1267 #include <fcntl.h>\r
1268 #include <sys/soundcard.h>\r
1269 #include <errno.h>\r
1270 #include <math.h>\r
1271 \r
1272 #define DAC_NAME "/dev/dsp"\r
1273 #define MAX_DEVICES 16\r
1274 #define MAX_CHANNELS 16\r
1275 \r
1276 void RtAudio :: initialize(void)\r
1277 {\r
1278   // Count cards and devices\r
1279   nDevices = 0;\r
1280 \r
1281   // We check /dev/dsp before probing devices.  /dev/dsp is supposed to\r
1282   // be a link to the "default" audio device, of the form /dev/dsp0,\r
1283   // /dev/dsp1, etc...  However, I've seen one case where /dev/dsp was a\r
1284   // real device, so we need to check for that.  Also, sometimes the\r
1285   // link is to /dev/dspx and other times just dspx.  I'm not sure how\r
1286   // the latter works, but it does.\r
1287   char device_name[16];\r
1288   struct stat dspstat;\r
1289   int dsplink = -1;\r
1290   int i = 0;\r
1291   if (lstat(DAC_NAME, &dspstat) == 0) {\r
1292     if (S_ISLNK(dspstat.st_mode)) {\r
1293       i = readlink(DAC_NAME, device_name, sizeof(device_name));\r
1294       if (i > 0) {\r
1295         device_name[i] = '\0';\r
1296         if (i > 8) { // check for "/dev/dspx"\r
1297           if (!strncmp(DAC_NAME, device_name, 8))\r
1298             dsplink = atoi(&device_name[8]);\r
1299         }\r
1300         else if (i > 3) { // check for "dspx"\r
1301           if (!strncmp("dsp", device_name, 3))\r
1302             dsplink = atoi(&device_name[3]);\r
1303         }\r
1304       }\r
1305       else {\r
1306         sprintf(message, "RtAudio: cannot read value of symbolic link %s.", DAC_NAME);\r
1307         error(RtError::SYSTEM_ERROR);\r
1308       }\r
1309     }\r
1310   }\r
1311   else {\r
1312     sprintf(message, "RtAudio: cannot stat %s.", DAC_NAME);\r
1313     error(RtError::SYSTEM_ERROR);\r
1314   }\r
1315 \r
1316   // The OSS API doesn't provide a routine for determining the number\r
1317   // of devices.  Thus, we'll just pursue a brute force method.  The\r
1318   // idea is to start with /dev/dsp(0) and continue with higher device\r
1319   // numbers until we reach MAX_DSP_DEVICES.  This should tell us how\r
1320   // many devices we have ... it is not a fullproof scheme, but hopefully\r
1321   // it will work most of the time.\r
1322 \r
1323   int fd = 0;\r
1324   char names[MAX_DEVICES][16];\r
1325   for (i=-1; i<MAX_DEVICES; i++) {\r
1326 \r
1327     // Probe /dev/dsp first, since it is supposed to be the default device.\r
1328     if (i == -1)\r
1329       sprintf(device_name, "%s", DAC_NAME);\r
1330     else if (i == dsplink)\r
1331       continue; // We've aready probed this device via /dev/dsp link ... try next device.\r
1332     else\r
1333       sprintf(device_name, "%s%d", DAC_NAME, i);\r
1334 \r
1335     // First try to open the device for playback, then record mode.\r
1336     fd = open(device_name, O_WRONLY | O_NONBLOCK);\r
1337     if (fd == -1) {\r
1338       // Open device for playback failed ... either busy or doesn't exist.\r
1339       if (errno != EBUSY && errno != EAGAIN) {\r
1340         // Try to open for capture\r
1341         fd = open(device_name, O_RDONLY | O_NONBLOCK);\r
1342         if (fd == -1) {\r
1343           // Open device for record failed.\r
1344           if (errno != EBUSY && errno != EAGAIN)\r
1345             continue;\r
1346           else {\r
1347             sprintf(message, "RtAudio: OSS record device (%s) is busy.", device_name);\r
1348             error(RtError::WARNING);\r
1349             // still count it for now\r
1350           }\r
1351         }\r
1352       }\r
1353       else {\r
1354         sprintf(message, "RtAudio: OSS playback device (%s) is busy.", device_name);\r
1355         error(RtError::WARNING);\r
1356         // still count it for now\r
1357       }\r
1358     }\r
1359 \r
1360     if (fd >= 0) close(fd);\r
1361     strncpy(names[nDevices], device_name, 16);\r
1362     nDevices++;\r
1363   }\r
1364 \r
1365   if (nDevices == 0) return;\r
1366 \r
1367   //  Allocate the RTAUDIO_DEVICE structures.\r
1368   devices = (RTAUDIO_DEVICE *) calloc(nDevices, sizeof(RTAUDIO_DEVICE));\r
1369   if (devices == NULL) {\r
1370     sprintf(message, "RtAudio: memory allocation error!");\r
1371     error(RtError::MEMORY_ERROR);\r
1372   }\r
1373 \r
1374   // Write device ascii identifiers to device control structure and then probe capabilities.\r
1375   for (i=0; i<nDevices; i++) {\r
1376     strncpy(devices[i].name, names[i], 16);\r
1377     probeDeviceInfo(&devices[i]);\r
1378   }\r
1379 \r
1380   return;\r
1381 }\r
1382 \r
1383 void RtAudio :: probeDeviceInfo(RTAUDIO_DEVICE *info)\r
1384 {\r
1385   int i, fd, channels, mask;\r
1386 \r
1387   // The OSS API doesn't provide a means for probing the capabilities\r
1388   // of devices.  Thus, we'll just pursue a brute force method.\r
1389 \r
1390   // First try for playback\r
1391   fd = open(info->name, O_WRONLY | O_NONBLOCK);\r
1392   if (fd == -1) {\r
1393     // Open device failed ... either busy or doesn't exist\r
1394     if (errno == EBUSY || errno == EAGAIN)\r
1395       sprintf(message, "RtAudio: OSS playback device (%s) is busy and cannot be probed.",\r
1396               info->name);\r
1397     else\r
1398       sprintf(message, "RtAudio: OSS playback device (%s) open error.", info->name);\r
1399     error(RtError::WARNING);\r
1400     goto capture_probe;\r
1401   }\r
1402 \r
1403   // We have an open device ... see how many channels it can handle\r
1404   for (i=MAX_CHANNELS; i>0; i--) {\r
1405     channels = i;\r
1406     if (ioctl(fd, SNDCTL_DSP_CHANNELS, &channels) == -1) {\r
1407       // This would normally indicate some sort of hardware error, but under ALSA's\r
1408       // OSS emulation, it sometimes indicates an invalid channel value.  Further,\r
1409       // the returned channel value is not changed. So, we'll ignore the possible\r
1410       // hardware error.\r
1411       continue; // try next channel number\r
1412     }\r
1413     // Check to see whether the device supports the requested number of channels\r
1414     if (channels != i ) continue; // try next channel number\r
1415     // If here, we found the largest working channel value\r
1416     break;\r
1417   }\r
1418   info->maxOutputChannels = channels;\r
1419 \r
1420   // Now find the minimum number of channels it can handle\r
1421   for (i=1; i<=info->maxOutputChannels; i++) {\r
1422     channels = i;\r
1423     if (ioctl(fd, SNDCTL_DSP_CHANNELS, &channels) == -1 || channels != i)\r
1424       continue; // try next channel number\r
1425     // If here, we found the smallest working channel value\r
1426     break;\r
1427   }\r
1428   info->minOutputChannels = channels;\r
1429   close(fd);\r
1430 \r
1431  capture_probe:\r
1432   // Now try for capture\r
1433   fd = open(info->name, O_RDONLY | O_NONBLOCK);\r
1434   if (fd == -1) {\r
1435     // Open device for capture failed ... either busy or doesn't exist\r
1436     if (errno == EBUSY || errno == EAGAIN)\r
1437       sprintf(message, "RtAudio: OSS capture device (%s) is busy and cannot be probed.",\r
1438               info->name);\r
1439     else\r
1440       sprintf(message, "RtAudio: OSS capture device (%s) open error.", info->name);\r
1441     error(RtError::WARNING);\r
1442     if (info->maxOutputChannels == 0)\r
1443       // didn't open for playback either ... device invalid\r
1444       return;\r
1445     goto probe_parameters;\r
1446   }\r
1447 \r
1448   // We have the device open for capture ... see how many channels it can handle\r
1449   for (i=MAX_CHANNELS; i>0; i--) {\r
1450     channels = i;\r
1451     if (ioctl(fd, SNDCTL_DSP_CHANNELS, &channels) == -1 || channels != i) {\r
1452       continue; // as above\r
1453     }\r
1454     // If here, we found a working channel value\r
1455     break;\r
1456   }\r
1457   info->maxInputChannels = channels;\r
1458 \r
1459   // Now find the minimum number of channels it can handle\r
1460   for (i=1; i<=info->maxInputChannels; i++) {\r
1461     channels = i;\r
1462     if (ioctl(fd, SNDCTL_DSP_CHANNELS, &channels) == -1 || channels != i)\r
1463       continue; // try next channel number\r
1464     // If here, we found the smallest working channel value\r
1465     break;\r
1466   }\r
1467   info->minInputChannels = channels;\r
1468   close(fd);\r
1469 \r
1470   // If device opens for both playback and capture, we determine the channels.\r
1471   if (info->maxOutputChannels == 0 || info->maxInputChannels == 0)\r
1472     goto probe_parameters;\r
1473 \r
1474   fd = open(info->name, O_RDWR | O_NONBLOCK);\r
1475   if (fd == -1)\r
1476     goto probe_parameters;\r
1477 \r
1478   ioctl(fd, SNDCTL_DSP_SETDUPLEX, 0);\r
1479   ioctl(fd, SNDCTL_DSP_GETCAPS, &mask);\r
1480   if (mask & DSP_CAP_DUPLEX) {\r
1481     info->hasDuplexSupport = true;\r
1482     // We have the device open for duplex ... see how many channels it can handle\r
1483     for (i=MAX_CHANNELS; i>0; i--) {\r
1484       channels = i;\r
1485       if (ioctl(fd, SNDCTL_DSP_CHANNELS, &channels) == -1 || channels != i)\r
1486         continue; // as above\r
1487       // If here, we found a working channel value\r
1488       break;\r
1489     }\r
1490     info->maxDuplexChannels = channels;\r
1491 \r
1492     // Now find the minimum number of channels it can handle\r
1493     for (i=1; i<=info->maxDuplexChannels; i++) {\r
1494       channels = i;\r
1495       if (ioctl(fd, SNDCTL_DSP_CHANNELS, &channels) == -1 || channels != i)\r
1496         continue; // try next channel number\r
1497       // If here, we found the smallest working channel value\r
1498       break;\r
1499     }\r
1500     info->minDuplexChannels = channels;\r
1501   }\r
1502   close(fd);\r
1503 \r
1504  probe_parameters:\r
1505   // At this point, we need to figure out the supported data formats\r
1506   // and sample rates.  We'll proceed by openning the device in the\r
1507   // direction with the maximum number of channels, or playback if\r
1508   // they are equal.  This might limit our sample rate options, but so\r
1509   // be it.\r
1510 \r
1511   if (info->maxOutputChannels >= info->maxInputChannels) {\r
1512     fd = open(info->name, O_WRONLY | O_NONBLOCK);\r
1513     channels = info->maxOutputChannels;\r
1514   }\r
1515   else {\r
1516     fd = open(info->name, O_RDONLY | O_NONBLOCK);\r
1517     channels = info->maxInputChannels;\r
1518   }\r
1519 \r
1520   if (fd == -1) {\r
1521     // We've got some sort of conflict ... abort\r
1522     sprintf(message, "RtAudio: OSS device (%s) won't reopen during probe.",\r
1523             info->name);\r
1524     error(RtError::WARNING);\r
1525     return;\r
1526   }\r
1527 \r
1528   // We have an open device ... set to maximum channels.\r
1529   i = channels;\r
1530   if (ioctl(fd, SNDCTL_DSP_CHANNELS, &channels) == -1 || channels != i) {\r
1531     // We've got some sort of conflict ... abort\r
1532     close(fd);\r
1533     sprintf(message, "RtAudio: OSS device (%s) won't revert to previous channel setting.",\r
1534             info->name);\r
1535     error(RtError::WARNING);\r
1536     return;\r
1537   }\r
1538 \r
1539   if (ioctl(fd, SNDCTL_DSP_GETFMTS, &mask) == -1) {\r
1540     close(fd);\r
1541     sprintf(message, "RtAudio: OSS device (%s) can't get supported audio formats.",\r
1542             info->name);\r
1543     error(RtError::WARNING);\r
1544     return;\r
1545   }\r
1546 \r
1547   // Probe the supported data formats ... we don't care about endian-ness just yet.\r
1548   int format;\r
1549   info->nativeFormats = 0;\r
1550 #if defined (AFMT_S32_BE)\r
1551   // This format does not seem to be in the 2.4 kernel version of OSS soundcard.h\r
1552   if (mask & AFMT_S32_BE) {\r
1553     format = AFMT_S32_BE;\r
1554     info->nativeFormats |= RTAUDIO_SINT32;\r
1555   }\r
1556 #endif\r
1557 #if defined (AFMT_S32_LE)\r
1558   /* This format is not in the 2.4.4 kernel version of OSS soundcard.h */\r
1559   if (mask & AFMT_S32_LE) {\r
1560     format = AFMT_S32_LE;\r
1561     info->nativeFormats |= RTAUDIO_SINT32;\r
1562   }\r
1563 #endif\r
1564   if (mask & AFMT_S8) {\r
1565     format = AFMT_S8;\r
1566     info->nativeFormats |= RTAUDIO_SINT8;\r
1567   }\r
1568   if (mask & AFMT_S16_BE) {\r
1569     format = AFMT_S16_BE;\r
1570     info->nativeFormats |= RTAUDIO_SINT16;\r
1571   }\r
1572   if (mask & AFMT_S16_LE) {\r
1573     format = AFMT_S16_LE;\r
1574     info->nativeFormats |= RTAUDIO_SINT16;\r
1575   }\r
1576 \r
1577   // Check that we have at least one supported format\r
1578   if (info->nativeFormats == 0) {\r
1579     close(fd);\r
1580     sprintf(message, "RtAudio: OSS device (%s) data format not supported by RtAudio.",\r
1581             info->name);\r
1582     error(RtError::WARNING);\r
1583     return;\r
1584   }\r
1585 \r
1586   // Set the format\r
1587   i = format;\r
1588   if (ioctl(fd, SNDCTL_DSP_SETFMT, &format) == -1 || format != i) {\r
1589     close(fd);\r
1590     sprintf(message, "RtAudio: OSS device (%s) error setting data format.",\r
1591             info->name);\r
1592     error(RtError::WARNING);\r
1593     return;\r
1594   }\r
1595 \r
1596   // Probe the supported sample rates ... first get lower limit\r
1597   int speed = 1;\r
1598   if (ioctl(fd, SNDCTL_DSP_SPEED, &speed) == -1) {\r
1599     // If we get here, we're probably using an ALSA driver with OSS-emulation,\r
1600     // which doesn't conform to the OSS specification.  In this case,\r
1601     // we'll probe our predefined list of sample rates for working values.\r
1602     info->nSampleRates = 0;\r
1603     for (i=0; i<MAX_SAMPLE_RATES; i++) {\r
1604       speed = SAMPLE_RATES[i];\r
1605       if (ioctl(fd, SNDCTL_DSP_SPEED, &speed) != -1) {\r
1606         info->sampleRates[info->nSampleRates] = SAMPLE_RATES[i];\r
1607         info->nSampleRates++;\r
1608       }\r
1609     }\r
1610     if (info->nSampleRates == 0) {\r
1611       close(fd);\r
1612       return;\r
1613     }\r
1614     goto finished;\r
1615   }\r
1616   info->sampleRates[0] = speed;\r
1617 \r
1618   // Now get upper limit\r
1619   speed = 1000000;\r
1620   if (ioctl(fd, SNDCTL_DSP_SPEED, &speed) == -1) {\r
1621     close(fd);\r
1622     sprintf(message, "RtAudio: OSS device (%s) error setting sample rate.",\r
1623             info->name);\r
1624     error(RtError::WARNING);\r
1625     return;\r
1626   }\r
1627   info->sampleRates[1] = speed;\r
1628   info->nSampleRates = -1;\r
1629 \r
1630  finished: // That's all ... close the device and return\r
1631   close(fd);\r
1632   info->probed = true;\r
1633   return;\r
1634 }\r
1635 \r
1636 bool RtAudio :: probeDeviceOpen(int device, RTAUDIO_STREAM *stream,\r
1637                                 STREAM_MODE mode, int channels, \r
1638                                 int sampleRate, RTAUDIO_FORMAT format,\r
1639                                 int *bufferSize, int numberOfBuffers)\r
1640 {\r
1641   int buffers, buffer_bytes, device_channels, device_format;\r
1642   int srate, temp, fd;\r
1643 \r
1644   const char *name = devices[device].name;\r
1645 \r
1646   if (mode == PLAYBACK)\r
1647     fd = open(name, O_WRONLY | O_NONBLOCK);\r
1648   else { // mode == RECORD\r
1649     if (stream->mode == PLAYBACK && stream->device[0] == device) {\r
1650       // We just set the same device for playback ... close and reopen for duplex (OSS only).\r
1651       close(stream->handle[0]);\r
1652       stream->handle[0] = 0;\r
1653       // First check that the number previously set channels is the same.\r
1654       if (stream->nUserChannels[0] != channels) {\r
1655         sprintf(message, "RtAudio: input/output channels must be equal for OSS duplex device (%s).", name);\r
1656         goto error;\r
1657       }\r
1658       fd = open(name, O_RDWR | O_NONBLOCK);\r
1659     }\r
1660     else\r
1661       fd = open(name, O_RDONLY | O_NONBLOCK);\r
1662   }\r
1663 \r
1664   if (fd == -1) {\r
1665     if (errno == EBUSY || errno == EAGAIN)\r
1666       sprintf(message, "RtAudio: OSS device (%s) is busy and cannot be opened.",\r
1667               name);\r
1668     else\r
1669       sprintf(message, "RtAudio: OSS device (%s) cannot be opened.", name);\r
1670     goto error;\r
1671   }\r
1672 \r
1673   // Now reopen in blocking mode.\r
1674   close(fd);\r
1675   if (mode == PLAYBACK)\r
1676     fd = open(name, O_WRONLY | O_SYNC);\r
1677   else { // mode == RECORD\r
1678     if (stream->mode == PLAYBACK && stream->device[0] == device)\r
1679       fd = open(name, O_RDWR | O_SYNC);\r
1680     else\r
1681       fd = open(name, O_RDONLY | O_SYNC);\r
1682   }\r
1683 \r
1684   if (fd == -1) {\r
1685     sprintf(message, "RtAudio: OSS device (%s) cannot be opened.", name);\r
1686     goto error;\r
1687   }\r
1688 \r
1689   // Get the sample format mask\r
1690   int mask;\r
1691   if (ioctl(fd, SNDCTL_DSP_GETFMTS, &mask) == -1) {\r
1692     close(fd);\r
1693     sprintf(message, "RtAudio: OSS device (%s) can't get supported audio formats.",\r
1694             name);\r
1695     goto error;\r
1696   }\r
1697 \r
1698   // Determine how to set the device format.\r
1699   stream->userFormat = format;\r
1700   device_format = -1;\r
1701   stream->doByteSwap[mode] = false;\r
1702   if (format == RTAUDIO_SINT8) {\r
1703     if (mask & AFMT_S8) {\r
1704       device_format = AFMT_S8;\r
1705       stream->deviceFormat[mode] = RTAUDIO_SINT8;\r
1706     }\r
1707   }\r
1708   else if (format == RTAUDIO_SINT16) {\r
1709     if (mask & AFMT_S16_NE) {\r
1710       device_format = AFMT_S16_NE;\r
1711       stream->deviceFormat[mode] = RTAUDIO_SINT16;\r
1712     }\r
1713 #if BYTE_ORDER == LITTLE_ENDIAN\r
1714     else if (mask & AFMT_S16_BE) {\r
1715       device_format = AFMT_S16_BE;\r
1716       stream->deviceFormat[mode] = RTAUDIO_SINT16;\r
1717       stream->doByteSwap[mode] = true;\r
1718     }\r
1719 #else\r
1720     else if (mask & AFMT_S16_LE) {\r
1721       device_format = AFMT_S16_LE;\r
1722       stream->deviceFormat[mode] = RTAUDIO_SINT16;\r
1723       stream->doByteSwap[mode] = true;\r
1724     }\r
1725 #endif\r
1726   }\r
1727 #if defined (AFMT_S32_NE) && defined (AFMT_S32_LE) && defined (AFMT_S32_BE)\r
1728   else if (format == RTAUDIO_SINT32) {\r
1729     if (mask & AFMT_S32_NE) {\r
1730       device_format = AFMT_S32_NE;\r
1731       stream->deviceFormat[mode] = RTAUDIO_SINT32;\r
1732     }\r
1733 #if BYTE_ORDER == LITTLE_ENDIAN\r
1734     else if (mask & AFMT_S32_BE) {\r
1735       device_format = AFMT_S32_BE;\r
1736       stream->deviceFormat[mode] = RTAUDIO_SINT32;\r
1737       stream->doByteSwap[mode] = true;\r
1738     }\r
1739 #else\r
1740     else if (mask & AFMT_S32_LE) {\r
1741       device_format = AFMT_S32_LE;\r
1742       stream->deviceFormat[mode] = RTAUDIO_SINT32;\r
1743       stream->doByteSwap[mode] = true;\r
1744     }\r
1745 #endif\r
1746   }\r
1747 #endif\r
1748 \r
1749   if (device_format == -1) {\r
1750     // The user requested format is not natively supported by the device.\r
1751     if (mask & AFMT_S16_NE) {\r
1752       device_format = AFMT_S16_NE;\r
1753       stream->deviceFormat[mode] = RTAUDIO_SINT16;\r
1754     }\r
1755 #if BYTE_ORDER == LITTLE_ENDIAN\r
1756     else if (mask & AFMT_S16_BE) {\r
1757       device_format = AFMT_S16_BE;\r
1758       stream->deviceFormat[mode] = RTAUDIO_SINT16;\r
1759       stream->doByteSwap[mode] = true;\r
1760     }\r
1761 #else\r
1762     else if (mask & AFMT_S16_LE) {\r
1763       device_format = AFMT_S16_LE;\r
1764       stream->deviceFormat[mode] = RTAUDIO_SINT16;\r
1765       stream->doByteSwap[mode] = true;\r
1766     }\r
1767 #endif\r
1768 #if defined (AFMT_S32_NE) && defined (AFMT_S32_LE) && defined (AFMT_S32_BE)\r
1769     else if (mask & AFMT_S32_NE) {\r
1770       device_format = AFMT_S32_NE;\r
1771       stream->deviceFormat[mode] = RTAUDIO_SINT32;\r
1772     }\r
1773 #if BYTE_ORDER == LITTLE_ENDIAN\r
1774     else if (mask & AFMT_S32_BE) {\r
1775       device_format = AFMT_S32_BE;\r
1776       stream->deviceFormat[mode] = RTAUDIO_SINT32;\r
1777       stream->doByteSwap[mode] = true;\r
1778     }\r
1779 #else\r
1780     else if (mask & AFMT_S32_LE) {\r
1781       device_format = AFMT_S32_LE;\r
1782       stream->deviceFormat[mode] = RTAUDIO_SINT32;\r
1783       stream->doByteSwap[mode] = true;\r
1784     }\r
1785 #endif\r
1786 #endif\r
1787     else if (mask & AFMT_S8) {\r
1788       device_format = AFMT_S8;\r
1789       stream->deviceFormat[mode] = RTAUDIO_SINT8;\r
1790     }\r
1791   }\r
1792 \r
1793   if (stream->deviceFormat[mode] == 0) {\r
1794     // This really shouldn't happen ...\r
1795     close(fd);\r
1796     sprintf(message, "RtAudio: OSS device (%s) data format not supported by RtAudio.",\r
1797             name);\r
1798     goto error;\r
1799   }\r
1800 \r
1801   // Determine the number of channels for this device.  Note that the\r
1802   // channel value requested by the user might be < min_X_Channels.\r
1803   stream->nUserChannels[mode] = channels;\r
1804   device_channels = channels;\r
1805   if (mode == PLAYBACK) {\r
1806     if (channels < devices[device].minOutputChannels)\r
1807       device_channels = devices[device].minOutputChannels;\r
1808   }\r
1809   else { // mode == RECORD\r
1810     if (stream->mode == PLAYBACK && stream->device[0] == device) {\r
1811       // We're doing duplex setup here.\r
1812       if (channels < devices[device].minDuplexChannels)\r
1813         device_channels = devices[device].minDuplexChannels;\r
1814     }\r
1815     else {\r
1816       if (channels < devices[device].minInputChannels)\r
1817         device_channels = devices[device].minInputChannels;\r
1818     }\r
1819   }\r
1820   stream->nDeviceChannels[mode] = device_channels;\r
1821 \r
1822   // Attempt to set the buffer size.  According to OSS, the minimum\r
1823   // number of buffers is two.  The supposed minimum buffer size is 16\r
1824   // bytes, so that will be our lower bound.  The argument to this\r
1825   // call is in the form 0xMMMMSSSS (hex), where the buffer size (in\r
1826   // bytes) is given as 2^SSSS and the number of buffers as 2^MMMM.\r
1827   // We'll check the actual value used near the end of the setup\r
1828   // procedure.\r
1829   buffer_bytes = *bufferSize * formatBytes(stream->deviceFormat[mode]) * device_channels;\r
1830   if (buffer_bytes < 16) buffer_bytes = 16;\r
1831   buffers = numberOfBuffers;\r
1832   if (buffers < 2) buffers = 2;\r
1833   temp = ((int) buffers << 16) + (int)(log10((double)buffer_bytes)/log10(2.0));\r
1834   if (ioctl(fd, SNDCTL_DSP_SETFRAGMENT, &temp)) {\r
1835     close(fd);\r
1836     sprintf(message, "RtAudio: OSS error setting fragment size for device (%s).",\r
1837             name);\r
1838     goto error;\r
1839   }\r
1840   stream->nBuffers = buffers;\r
1841 \r
1842   // Set the data format.\r
1843   temp = device_format;\r
1844   if (ioctl(fd, SNDCTL_DSP_SETFMT, &device_format) == -1 || device_format != temp) {\r
1845     close(fd);\r
1846     sprintf(message, "RtAudio: OSS error setting data format for device (%s).",\r
1847             name);\r
1848     goto error;\r
1849   }\r
1850 \r
1851   // Set the number of channels.\r
1852   temp = device_channels;\r
1853   if (ioctl(fd, SNDCTL_DSP_CHANNELS, &device_channels) == -1 || device_channels != temp) {\r
1854     close(fd);\r
1855     sprintf(message, "RtAudio: OSS error setting %d channels on device (%s).",\r
1856             temp, name);\r
1857     goto error;\r
1858   }\r
1859 \r
1860   // Set the sample rate.\r
1861   srate = sampleRate;\r
1862   temp = srate;\r
1863   if (ioctl(fd, SNDCTL_DSP_SPEED, &srate) == -1) {\r
1864     close(fd);\r
1865     sprintf(message, "RtAudio: OSS error setting sample rate = %d on device (%s).",\r
1866             temp, name);\r
1867     goto error;\r
1868   }\r
1869 \r
1870   // Verify the sample rate setup worked.\r
1871   if (abs(srate - temp) > 100) {\r
1872     close(fd);\r
1873     sprintf(message, "RtAudio: OSS error ... audio device (%s) doesn't support sample rate of %d.",\r
1874             name, temp);\r
1875     goto error;\r
1876   }\r
1877   stream->sampleRate = sampleRate;\r
1878 \r
1879   if (ioctl(fd, SNDCTL_DSP_GETBLKSIZE, &buffer_bytes) == -1) {\r
1880     close(fd);\r
1881     sprintf(message, "RtAudio: OSS error getting buffer size for device (%s).",\r
1882             name);\r
1883     goto error;\r
1884   }\r
1885 \r
1886   // Save buffer size (in sample frames).\r
1887   *bufferSize = buffer_bytes / (formatBytes(stream->deviceFormat[mode]) * device_channels);\r
1888   stream->bufferSize = *bufferSize;\r
1889 \r
1890   if (mode == RECORD && stream->mode == PLAYBACK &&\r
1891       stream->device[0] == device) {\r
1892     // We're doing duplex setup here.\r
1893     stream->deviceFormat[0] = stream->deviceFormat[1];\r
1894     stream->nDeviceChannels[0] = device_channels;\r
1895   }\r
1896 \r
1897   // Set flags for buffer conversion\r
1898   stream->doConvertBuffer[mode] = false;\r
1899   if (stream->userFormat != stream->deviceFormat[mode])\r
1900     stream->doConvertBuffer[mode] = true;\r
1901   if (stream->nUserChannels[mode] < stream->nDeviceChannels[mode])\r
1902     stream->doConvertBuffer[mode] = true;\r
1903 \r
1904   // Allocate necessary internal buffers\r
1905   if ( stream->nUserChannels[0] != stream->nUserChannels[1] ) {\r
1906 \r
1907     long buffer_bytes;\r
1908     if (stream->nUserChannels[0] >= stream->nUserChannels[1])\r
1909       buffer_bytes = stream->nUserChannels[0];\r
1910     else\r
1911       buffer_bytes = stream->nUserChannels[1];\r
1912 \r
1913     buffer_bytes *= *bufferSize * formatBytes(stream->userFormat);\r
1914     if (stream->userBuffer) free(stream->userBuffer);\r
1915     stream->userBuffer = (char *) calloc(buffer_bytes, 1);\r
1916     if (stream->userBuffer == NULL) {\r
1917       close(fd);\r
1918       sprintf(message, "RtAudio: OSS error allocating user buffer memory (%s).",\r
1919               name);\r
1920       goto error;\r
1921     }\r
1922   }\r
1923 \r
1924   if ( stream->doConvertBuffer[mode] ) {\r
1925 \r
1926     long buffer_bytes;\r
1927     bool makeBuffer = true;\r
1928     if ( mode == PLAYBACK )\r
1929       buffer_bytes = stream->nDeviceChannels[0] * formatBytes(stream->deviceFormat[0]);\r
1930     else { // mode == RECORD\r
1931       buffer_bytes = stream->nDeviceChannels[1] * formatBytes(stream->deviceFormat[1]);\r
1932       if ( stream->mode == PLAYBACK ) {\r
1933         long bytes_out = stream->nDeviceChannels[0] * formatBytes(stream->deviceFormat[0]);\r
1934         if ( buffer_bytes > bytes_out )\r
1935           buffer_bytes = (buffer_bytes > bytes_out) ? buffer_bytes : bytes_out;\r
1936         else\r
1937           makeBuffer = false;\r
1938       }\r
1939     }\r
1940 \r
1941     if ( makeBuffer ) {\r
1942       buffer_bytes *= *bufferSize;\r
1943       if (stream->deviceBuffer) free(stream->deviceBuffer);\r
1944       stream->deviceBuffer = (char *) calloc(buffer_bytes, 1);\r
1945       if (stream->deviceBuffer == NULL) {\r
1946         close(fd);\r
1947         free(stream->userBuffer);\r
1948         sprintf(message, "RtAudio: OSS error allocating device buffer memory (%s).",\r
1949                 name);\r
1950         goto error;\r
1951       }\r
1952     }\r
1953   }\r
1954 \r
1955   stream->device[mode] = device;\r
1956   stream->handle[mode] = fd;\r
1957   stream->state = STREAM_STOPPED;\r
1958   if ( stream->mode == PLAYBACK && mode == RECORD ) {\r
1959     stream->mode = DUPLEX;\r
1960     if (stream->device[0] == device)\r
1961       stream->handle[0] = fd;\r
1962   }\r
1963   else\r
1964     stream->mode = mode;\r
1965 \r
1966   return SUCCESS;\r
1967 \r
1968  error:\r
1969   if (stream->handle[0]) {\r
1970     close(stream->handle[0]);\r
1971     stream->handle[0] = 0;\r
1972   }\r
1973   error(RtError::WARNING);\r
1974   return FAILURE;\r
1975 }\r
1976 \r
1977 void RtAudio :: cancelStreamCallback(int streamId)\r
1978 {\r
1979   RTAUDIO_STREAM *stream = (RTAUDIO_STREAM *) verifyStream(streamId);\r
1980 \r
1981   if (stream->usingCallback) {\r
1982     stream->usingCallback = false;\r
1983     pthread_cancel(stream->thread);\r
1984     pthread_join(stream->thread, NULL);\r
1985     stream->thread = 0;\r
1986     stream->callback = NULL;\r
1987     stream->userData = NULL;\r
1988   }\r
1989 }\r
1990 \r
1991 void RtAudio :: closeStream(int streamId)\r
1992 {\r
1993   // We don't want an exception to be thrown here because this\r
1994   // function is called by our class destructor.  So, do our own\r
1995   // streamId check.\r
1996   if ( streams.find( streamId ) == streams.end() ) {\r
1997     sprintf(message, "RtAudio: invalid stream identifier!");\r
1998     error(RtError::WARNING);\r
1999     return;\r
2000   }\r
2001 \r
2002   RTAUDIO_STREAM *stream = (RTAUDIO_STREAM *) streams[streamId];\r
2003 \r
2004   if (stream->usingCallback) {\r
2005     pthread_cancel(stream->thread);\r
2006     pthread_join(stream->thread, NULL);\r
2007   }\r
2008 \r
2009   if (stream->state == STREAM_RUNNING) {\r
2010     if (stream->mode == PLAYBACK || stream->mode == DUPLEX)\r
2011       ioctl(stream->handle[0], SNDCTL_DSP_RESET, 0);\r
2012     if (stream->mode == RECORD || stream->mode == DUPLEX)\r
2013       ioctl(stream->handle[1], SNDCTL_DSP_RESET, 0);\r
2014   }\r
2015 \r
2016   pthread_mutex_destroy(&stream->mutex);\r
2017 \r
2018   if (stream->handle[0])\r
2019     close(stream->handle[0]);\r
2020 \r
2021   if (stream->handle[1])\r
2022     close(stream->handle[1]);\r
2023 \r
2024   if (stream->userBuffer)\r
2025     free(stream->userBuffer);\r
2026 \r
2027   if (stream->deviceBuffer)\r
2028     free(stream->deviceBuffer);\r
2029 \r
2030   free(stream);\r
2031   streams.erase(streamId);\r
2032 }\r
2033 \r
2034 void RtAudio :: startStream(int streamId)\r
2035 {\r
2036   RTAUDIO_STREAM *stream = (RTAUDIO_STREAM *) verifyStream(streamId);\r
2037 \r
2038   stream->state = STREAM_RUNNING;\r
2039 \r
2040   // No need to do anything else here ... OSS automatically starts when fed samples.\r
2041 }\r
2042 \r
2043 void RtAudio :: stopStream(int streamId)\r
2044 {\r
2045   RTAUDIO_STREAM *stream = (RTAUDIO_STREAM *) verifyStream(streamId);\r
2046 \r
2047   MUTEX_LOCK(&stream->mutex);\r
2048 \r
2049   if (stream->state == STREAM_STOPPED)\r
2050     goto unlock;\r
2051 \r
2052   int err;\r
2053   if (stream->mode == PLAYBACK || stream->mode == DUPLEX) {\r
2054     err = ioctl(stream->handle[0], SNDCTL_DSP_SYNC, 0);\r
2055     if (err < -1) {\r
2056       sprintf(message, "RtAudio: OSS error stopping device (%s).",\r
2057               devices[stream->device[0]].name);\r
2058       error(RtError::DRIVER_ERROR);\r
2059     }\r
2060   }\r
2061   else {\r
2062     err = ioctl(stream->handle[1], SNDCTL_DSP_SYNC, 0);\r
2063     if (err < -1) {\r
2064       sprintf(message, "RtAudio: OSS error stopping device (%s).",\r
2065               devices[stream->device[1]].name);\r
2066       error(RtError::DRIVER_ERROR);\r
2067     }\r
2068   }\r
2069   stream->state = STREAM_STOPPED;\r
2070 \r
2071  unlock:\r
2072   MUTEX_UNLOCK(&stream->mutex);\r
2073 }\r
2074 \r
2075 void RtAudio :: abortStream(int streamId)\r
2076 {\r
2077   RTAUDIO_STREAM *stream = (RTAUDIO_STREAM *) verifyStream(streamId);\r
2078 \r
2079   MUTEX_LOCK(&stream->mutex);\r
2080 \r
2081   if (stream->state == STREAM_STOPPED)\r
2082     goto unlock;\r
2083 \r
2084   int err;\r
2085   if (stream->mode == PLAYBACK || stream->mode == DUPLEX) {\r
2086     err = ioctl(stream->handle[0], SNDCTL_DSP_RESET, 0);\r
2087     if (err < -1) {\r
2088       sprintf(message, "RtAudio: OSS error aborting device (%s).",\r
2089               devices[stream->device[0]].name);\r
2090       error(RtError::DRIVER_ERROR);\r
2091     }\r
2092   }\r
2093   else {\r
2094     err = ioctl(stream->handle[1], SNDCTL_DSP_RESET, 0);\r
2095     if (err < -1) {\r
2096       sprintf(message, "RtAudio: OSS error aborting device (%s).",\r
2097               devices[stream->device[1]].name);\r
2098       error(RtError::DRIVER_ERROR);\r
2099     }\r
2100   }\r
2101   stream->state = STREAM_STOPPED;\r
2102 \r
2103  unlock:\r
2104   MUTEX_UNLOCK(&stream->mutex);\r
2105 }\r
2106 \r
2107 int RtAudio :: streamWillBlock(int streamId)\r
2108 {\r
2109   RTAUDIO_STREAM *stream = (RTAUDIO_STREAM *) verifyStream(streamId);\r
2110 \r
2111   MUTEX_LOCK(&stream->mutex);\r
2112 \r
2113   int bytes = 0, channels = 0, frames = 0;\r
2114   if (stream->state == STREAM_STOPPED)\r
2115     goto unlock;\r
2116 \r
2117   audio_buf_info info;\r
2118   if (stream->mode == PLAYBACK || stream->mode == DUPLEX) {\r
2119     ioctl(stream->handle[0], SNDCTL_DSP_GETOSPACE, &info);\r
2120     bytes = info.bytes;\r
2121     channels = stream->nDeviceChannels[0];\r
2122   }\r
2123 \r
2124   if (stream->mode == RECORD || stream->mode == DUPLEX) {\r
2125     ioctl(stream->handle[1], SNDCTL_DSP_GETISPACE, &info);\r
2126     if (stream->mode == DUPLEX ) {\r
2127       bytes = (bytes < info.bytes) ? bytes : info.bytes;\r
2128       channels = stream->nDeviceChannels[0];\r
2129     }\r
2130     else {\r
2131       bytes = info.bytes;\r
2132       channels = stream->nDeviceChannels[1];\r
2133     }\r
2134   }\r
2135 \r
2136   frames = (int) (bytes / (channels * formatBytes(stream->deviceFormat[0])));\r
2137   frames -= stream->bufferSize;\r
2138   if (frames < 0) frames = 0;\r
2139 \r
2140  unlock:\r
2141   MUTEX_UNLOCK(&stream->mutex);\r
2142   return frames;\r
2143 }\r
2144 \r
2145 void RtAudio :: tickStream(int streamId)\r
2146 {\r
2147   RTAUDIO_STREAM *stream = (RTAUDIO_STREAM *) verifyStream(streamId);\r
2148 \r
2149   int stopStream = 0;\r
2150   if (stream->state == STREAM_STOPPED) {\r
2151     if (stream->usingCallback) usleep(50000); // sleep 50 milliseconds\r
2152     return;\r
2153   }\r
2154   else if (stream->usingCallback) {\r
2155     stopStream = stream->callback(stream->userBuffer, stream->bufferSize, stream->userData);\r
2156   }\r
2157 \r
2158   MUTEX_LOCK(&stream->mutex);\r
2159 \r
2160   // The state might change while waiting on a mutex.\r
2161   if (stream->state == STREAM_STOPPED)\r
2162     goto unlock;\r
2163 \r
2164   int result;\r
2165   char *buffer;\r
2166   int samples;\r
2167   RTAUDIO_FORMAT format;\r
2168   if (stream->mode == PLAYBACK || stream->mode == DUPLEX) {\r
2169 \r
2170     // Setup parameters and do buffer conversion if necessary.\r
2171     if (stream->doConvertBuffer[0]) {\r
2172       convertStreamBuffer(stream, PLAYBACK);\r
2173       buffer = stream->deviceBuffer;\r
2174       samples = stream->bufferSize * stream->nDeviceChannels[0];\r
2175       format = stream->deviceFormat[0];\r
2176     }\r
2177     else {\r
2178       buffer = stream->userBuffer;\r
2179       samples = stream->bufferSize * stream->nUserChannels[0];\r
2180       format = stream->userFormat;\r
2181     }\r
2182 \r
2183     // Do byte swapping if necessary.\r
2184     if (stream->doByteSwap[0])\r
2185       byteSwapBuffer(buffer, samples, format);\r
2186 \r
2187     // Write samples to device.\r
2188     result = write(stream->handle[0], buffer, samples * formatBytes(format));\r
2189 \r
2190     if (result == -1) {\r
2191       // This could be an underrun, but the basic OSS API doesn't provide a means for determining that.\r
2192       sprintf(message, "RtAudio: OSS audio write error for device (%s).",\r
2193               devices[stream->device[0]].name);\r
2194       error(RtError::DRIVER_ERROR);\r
2195     }\r
2196   }\r
2197 \r
2198   if (stream->mode == RECORD || stream->mode == DUPLEX) {\r
2199 \r
2200     // Setup parameters.\r
2201     if (stream->doConvertBuffer[1]) {\r
2202       buffer = stream->deviceBuffer;\r
2203       samples = stream->bufferSize * stream->nDeviceChannels[1];\r
2204       format = stream->deviceFormat[1];\r
2205     }\r
2206     else {\r
2207       buffer = stream->userBuffer;\r
2208       samples = stream->bufferSize * stream->nUserChannels[1];\r
2209       format = stream->userFormat;\r
2210     }\r
2211 \r
2212     // Read samples from device.\r
2213     result = read(stream->handle[1], buffer, samples * formatBytes(format));\r
2214 \r
2215     if (result == -1) {\r
2216       // This could be an overrun, but the basic OSS API doesn't provide a means for determining that.\r
2217       sprintf(message, "RtAudio: OSS audio read error for device (%s).",\r
2218               devices[stream->device[1]].name);\r
2219       error(RtError::DRIVER_ERROR);\r
2220     }\r
2221 \r
2222     // Do byte swapping if necessary.\r
2223     if (stream->doByteSwap[1])\r
2224       byteSwapBuffer(buffer, samples, format);\r
2225 \r
2226     // Do buffer conversion if necessary.\r
2227     if (stream->doConvertBuffer[1])\r
2228       convertStreamBuffer(stream, RECORD);\r
2229   }\r
2230 \r
2231  unlock:\r
2232   MUTEX_UNLOCK(&stream->mutex);\r
2233 \r
2234   if (stream->usingCallback && stopStream)\r
2235     this->stopStream(streamId);\r
2236 }\r
2237 \r
2238 extern "C" void *callbackHandler(void *ptr)\r
2239 {\r
2240   RtAudio *object = thread_info.object;\r
2241   int stream = thread_info.streamId;\r
2242   bool *usingCallback = (bool *) ptr;\r
2243 \r
2244   while ( *usingCallback ) {\r
2245     pthread_testcancel();\r
2246     try {\r
2247       object->tickStream(stream);\r
2248     }\r
2249     catch (RtError &exception) {\r
2250       fprintf(stderr, "\nCallback thread error (%s) ... closing thread.\n\n",\r
2251               exception.getMessage());\r
2252       break;\r
2253     }\r
2254   }\r
2255 \r
2256   return 0;\r
2257 }\r
2258 \r
2259 //******************** End of __LINUX_OSS__ *********************//\r
2260 \r
2261 #elif defined(__WINDOWS_DS__) // Windows DirectSound API\r
2262 \r
2263 #include <dsound.h>\r
2264 \r
2265 // Declarations for utility functions, callbacks, and structures\r
2266 // specific to the DirectSound implementation.\r
2267 static bool CALLBACK deviceCountCallback(LPGUID lpguid,\r
2268                                          LPCSTR lpcstrDescription,\r
2269                                          LPCSTR lpcstrModule,\r
2270                                          LPVOID lpContext);\r
2271 \r
2272 static bool CALLBACK deviceInfoCallback(LPGUID lpguid,\r
2273                                         LPCSTR lpcstrDescription,\r
2274                                         LPCSTR lpcstrModule,\r
2275                                         LPVOID lpContext);\r
2276 \r
2277 static char* getErrorString(int code);\r
2278 \r
2279 struct enum_info {\r
2280   char name[64];\r
2281   LPGUID id;\r
2282   bool isInput;\r
2283   bool isValid;\r
2284 };\r
2285 \r
2286 // RtAudio methods for DirectSound implementation.\r
2287 void RtAudio :: initialize(void)\r
2288 {\r
2289   int i, ins = 0, outs = 0, count = 0;\r
2290   int index = 0;\r
2291   HRESULT result;\r
2292   nDevices = 0;\r
2293 \r
2294   // Count DirectSound devices.\r
2295   result = DirectSoundEnumerate((LPDSENUMCALLBACK)deviceCountCallback, &outs);\r
2296   if ( FAILED(result) ) {\r
2297     sprintf(message, "RtAudio: Unable to enumerate through sound playback devices: %s.",\r
2298             getErrorString(result));\r
2299     error(RtError::DRIVER_ERROR);\r
2300   }\r
2301 \r
2302   // Count DirectSoundCapture devices.\r
2303   result = DirectSoundCaptureEnumerate((LPDSENUMCALLBACK)deviceCountCallback, &ins);\r
2304   if ( FAILED(result) ) {\r
2305     sprintf(message, "RtAudio: Unable to enumerate through sound capture devices: %s.",\r
2306             getErrorString(result));\r
2307     error(RtError::DRIVER_ERROR);\r
2308   }\r
2309 \r
2310   count = ins + outs;\r
2311   if (count == 0) return;\r
2312 \r
2313   std::vector<enum_info> info(count);\r
2314   for (i=0; i<count; i++) {\r
2315     info[i].name[0] = '\0';\r
2316     if (i < outs) info[i].isInput = false;\r
2317     else info[i].isInput = true;\r
2318   }\r
2319 \r
2320   // Get playback device info and check capabilities.\r
2321   result = DirectSoundEnumerate((LPDSENUMCALLBACK)deviceInfoCallback, &info[0]);\r
2322   if ( FAILED(result) ) {\r
2323     sprintf(message, "RtAudio: Unable to enumerate through sound playback devices: %s.",\r
2324             getErrorString(result));\r
2325     error(RtError::DRIVER_ERROR);\r
2326   }\r
2327 \r
2328   // Get capture device info and check capabilities.\r
2329   result = DirectSoundCaptureEnumerate((LPDSENUMCALLBACK)deviceInfoCallback, &info[0]);\r
2330   if ( FAILED(result) ) {\r
2331     sprintf(message, "RtAudio: Unable to enumerate through sound capture devices: %s.",\r
2332             getErrorString(result));\r
2333     error(RtError::DRIVER_ERROR);\r
2334   }\r
2335 \r
2336   // Parse the devices and check validity.  Devices are considered\r
2337   // invalid if they cannot be opened, they report no supported data\r
2338   // formats, or they report < 1 supported channels.\r
2339   for (i=0; i<count; i++) {\r
2340     if (info[i].isValid && info[i].id == NULL ) // default device\r
2341       nDevices++;\r
2342   }\r
2343 \r
2344   // We group the default input and output devices together (as one\r
2345   // device) .\r
2346   if (nDevices > 0) {\r
2347     nDevices = 1;\r
2348     index = 1;\r
2349   }\r
2350 \r
2351   // Non-default devices are listed separately.\r
2352   for (i=0; i<count; i++) {\r
2353     if (info[i].isValid && info[i].id != NULL )\r
2354       nDevices++;\r
2355   }\r
2356 \r
2357   if (nDevices == 0) return;\r
2358 \r
2359   //  Allocate the RTAUDIO_DEVICE structures.\r
2360   devices = (RTAUDIO_DEVICE *) calloc(nDevices, sizeof(RTAUDIO_DEVICE));\r
2361   if (devices == NULL) {\r
2362     sprintf(message, "RtAudio: memory allocation error!");\r
2363     error(RtError::MEMORY_ERROR);\r
2364   }\r
2365 \r
2366   // Initialize the GUIDs to NULL for later validation.\r
2367   for (i=0; i<nDevices; i++) {\r
2368     devices[i].id[0] = NULL;\r
2369     devices[i].id[1] = NULL;\r
2370   }\r
2371 \r
2372   // Rename the default device(s).\r
2373   if (index)\r
2374     strcpy(devices[0].name, "Default Input/Output Devices");\r
2375 \r
2376   // Copy the names and GUIDs to our devices structures.\r
2377   for (i=0; i<count; i++) {\r
2378     if (info[i].isValid && info[i].id != NULL ) {\r
2379       strncpy(devices[index].name, info[i].name, 64);\r
2380       if (info[i].isInput)\r
2381         devices[index].id[1] = info[i].id;\r
2382       else\r
2383         devices[index].id[0] = info[i].id;\r
2384       index++;\r
2385     }\r
2386   }\r
2387 \r
2388   for (i=0;i<nDevices; i++)\r
2389     probeDeviceInfo(&devices[i]);\r
2390 \r
2391   return;\r
2392 }\r
2393 \r
2394 void RtAudio :: probeDeviceInfo(RTAUDIO_DEVICE *info)\r
2395 {\r
2396   HRESULT result;\r
2397 \r
2398   // Get the device index so that we can check the device handle.\r
2399   int index;\r
2400   for (index=0; index<nDevices; index++)\r
2401     if ( info == &devices[index] ) break;\r
2402 \r
2403   if ( index >= nDevices ) {\r
2404     sprintf(message, "RtAudio: device (%s) indexing error in DirectSound probeDeviceInfo().",\r
2405             info->name);\r
2406     error(RtError::WARNING);\r
2407     return;\r
2408   }\r
2409 \r
2410   // Do capture probe first.  If this is not the default device (index\r
2411   // = 0) _and_ GUID = NULL, then the capture handle is invalid.\r
2412   if ( index != 0 && info->id[1] == NULL )\r
2413     goto playback_probe;\r
2414 \r
2415   LPDIRECTSOUNDCAPTURE  input;\r
2416   result = DirectSoundCaptureCreate( info->id[0], &input, NULL );\r
2417   if ( FAILED(result) ) {\r
2418     sprintf(message, "RtAudio: Could not create DirectSound capture object (%s): %s.",\r
2419             info->name, getErrorString(result));\r
2420     error(RtError::WARNING);\r
2421     goto playback_probe;\r
2422   }\r
2423 \r
2424   DSCCAPS in_caps;\r
2425   in_caps.dwSize = sizeof(in_caps);\r
2426   result = input->GetCaps( &in_caps );\r
2427   if ( FAILED(result) ) {\r
2428     input->Release();\r
2429     sprintf(message, "RtAudio: Could not get DirectSound capture capabilities (%s): %s.",\r
2430             info->name, getErrorString(result));\r
2431     error(RtError::WARNING);\r
2432     goto playback_probe;\r
2433   }\r
2434 \r
2435   // Get input channel information.\r
2436   info->minInputChannels = 1;\r
2437   info->maxInputChannels = in_caps.dwChannels;\r
2438 \r
2439   // Get sample rate and format information.\r
2440   if( in_caps.dwChannels == 2 ) {\r
2441     if( in_caps.dwFormats & WAVE_FORMAT_1S16 ) info->nativeFormats |= RTAUDIO_SINT16;\r
2442     if( in_caps.dwFormats & WAVE_FORMAT_2S16 ) info->nativeFormats |= RTAUDIO_SINT16;\r
2443     if( in_caps.dwFormats & WAVE_FORMAT_4S16 ) info->nativeFormats |= RTAUDIO_SINT16;\r
2444     if( in_caps.dwFormats & WAVE_FORMAT_1S08 ) info->nativeFormats |= RTAUDIO_SINT8;\r
2445     if( in_caps.dwFormats & WAVE_FORMAT_2S08 ) info->nativeFormats |= RTAUDIO_SINT8;\r
2446     if( in_caps.dwFormats & WAVE_FORMAT_4S08 ) info->nativeFormats |= RTAUDIO_SINT8;\r
2447 \r
2448     if ( info->nativeFormats & RTAUDIO_SINT16 ) {\r
2449       if( in_caps.dwFormats & WAVE_FORMAT_1S16 ) info->sampleRates[info->nSampleRates++] = 11025;\r
2450       if( in_caps.dwFormats & WAVE_FORMAT_2S16 ) info->sampleRates[info->nSampleRates++] = 22050;\r
2451       if( in_caps.dwFormats & WAVE_FORMAT_4S16 ) info->sampleRates[info->nSampleRates++] = 44100;\r
2452     }\r
2453     else if ( info->nativeFormats & RTAUDIO_SINT8 ) {\r
2454       if( in_caps.dwFormats & WAVE_FORMAT_1S08 ) info->sampleRates[info->nSampleRates++] = 11025;\r
2455       if( in_caps.dwFormats & WAVE_FORMAT_2S08 ) info->sampleRates[info->nSampleRates++] = 22050;\r
2456       if( in_caps.dwFormats & WAVE_FORMAT_4S08 ) info->sampleRates[info->nSampleRates++] = 44100;\r
2457     }\r
2458   }\r
2459   else if ( in_caps.dwChannels == 1 ) {\r
2460     if( in_caps.dwFormats & WAVE_FORMAT_1M16 ) info->nativeFormats |= RTAUDIO_SINT16;\r
2461     if( in_caps.dwFormats & WAVE_FORMAT_2M16 ) info->nativeFormats |= RTAUDIO_SINT16;\r
2462     if( in_caps.dwFormats & WAVE_FORMAT_4M16 ) info->nativeFormats |= RTAUDIO_SINT16;\r
2463     if( in_caps.dwFormats & WAVE_FORMAT_1M08 ) info->nativeFormats |= RTAUDIO_SINT8;\r
2464     if( in_caps.dwFormats & WAVE_FORMAT_2M08 ) info->nativeFormats |= RTAUDIO_SINT8;\r
2465     if( in_caps.dwFormats & WAVE_FORMAT_4M08 ) info->nativeFormats |= RTAUDIO_SINT8;\r
2466 \r
2467     if ( info->nativeFormats & RTAUDIO_SINT16 ) {\r
2468       if( in_caps.dwFormats & WAVE_FORMAT_1M16 ) info->sampleRates[info->nSampleRates++] = 11025;\r
2469       if( in_caps.dwFormats & WAVE_FORMAT_2M16 ) info->sampleRates[info->nSampleRates++] = 22050;\r
2470       if( in_caps.dwFormats & WAVE_FORMAT_4M16 ) info->sampleRates[info->nSampleRates++] = 44100;\r
2471     }\r
2472     else if ( info->nativeFormats & RTAUDIO_SINT8 ) {\r
2473       if( in_caps.dwFormats & WAVE_FORMAT_1M08 ) info->sampleRates[info->nSampleRates++] = 11025;\r
2474       if( in_caps.dwFormats & WAVE_FORMAT_2M08 ) info->sampleRates[info->nSampleRates++] = 22050;\r
2475       if( in_caps.dwFormats & WAVE_FORMAT_4M08 ) info->sampleRates[info->nSampleRates++] = 44100;\r
2476     }\r
2477   }\r
2478   else info->minInputChannels = 0; // technically, this would be an error\r
2479 \r
2480   input->Release();\r
2481 \r
2482  playback_probe:\r
2483   LPDIRECTSOUND  output;\r
2484   DSCAPS out_caps;\r
2485 \r
2486   // Now do playback probe.  If this is not the default device (index\r
2487   // = 0) _and_ GUID = NULL, then the playback handle is invalid.\r
2488   if ( index != 0 && info->id[0] == NULL )\r
2489     goto check_parameters;\r
2490 \r
2491   result = DirectSoundCreate( info->id[0], &output, NULL );\r
2492   if ( FAILED(result) ) {\r
2493     sprintf(message, "RtAudio: Could not create DirectSound playback object (%s): %s.",\r
2494             info->name, getErrorString(result));\r
2495     error(RtError::WARNING);\r
2496     goto check_parameters;\r
2497   }\r
2498 \r
2499   out_caps.dwSize = sizeof(out_caps);\r
2500   result = output->GetCaps( &out_caps );\r
2501   if ( FAILED(result) ) {\r
2502     output->Release();\r
2503     sprintf(message, "RtAudio: Could not get DirectSound playback capabilities (%s): %s.",\r
2504             info->name, getErrorString(result));\r
2505     error(RtError::WARNING);\r
2506     goto check_parameters;\r
2507   }\r
2508 \r
2509   // Get output channel information.\r
2510   info->minOutputChannels = 1;\r
2511   info->maxOutputChannels = ( out_caps.dwFlags & DSCAPS_PRIMARYSTEREO ) ? 2 : 1;\r
2512 \r
2513   // Get sample rate information.  Use capture device rate information\r
2514   // if it exists.\r
2515   if ( info->nSampleRates == 0 ) {\r
2516     info->sampleRates[0] = (int) out_caps.dwMinSecondarySampleRate;\r
2517     info->sampleRates[1] = (int) out_caps.dwMaxSecondarySampleRate;\r
2518     if ( out_caps.dwFlags & DSCAPS_CONTINUOUSRATE )\r
2519       info->nSampleRates = -1;\r
2520     else if ( out_caps.dwMinSecondarySampleRate == out_caps.dwMaxSecondarySampleRate ) {\r
2521       if ( out_caps.dwMinSecondarySampleRate == 0 ) {\r
2522         // This is a bogus driver report ... fake the range and cross\r
2523         // your fingers.\r
2524         info->sampleRates[0] = 11025;\r
2525                                 info->sampleRates[1] = 48000;\r
2526         info->nSampleRates = -1; /* continuous range */\r
2527         sprintf(message, "RtAudio: bogus sample rates reported by DirectSound driver ... using defaults (%s).",\r
2528                 info->name);\r
2529         error(RtError::WARNING);\r
2530       }\r
2531       else {\r
2532         info->nSampleRates = 1;\r
2533                         }\r
2534     }\r
2535     else if ( (out_caps.dwMinSecondarySampleRate < 1000.0) &&\r
2536               (out_caps.dwMaxSecondarySampleRate > 50000.0) ) {\r
2537       // This is a bogus driver report ... support for only two\r
2538       // distant rates.  We'll assume this is a range.\r
2539       info->nSampleRates = -1;\r
2540       sprintf(message, "RtAudio: bogus sample rates reported by DirectSound driver ... using range (%s).",\r
2541               info->name);\r
2542       error(RtError::WARNING);\r
2543     }\r
2544     else info->nSampleRates = 2;\r
2545   }\r
2546   else {\r
2547     // Check input rates against output rate range\r
2548     for ( int i=info->nSampleRates-1; i>=0; i-- ) {\r
2549       if ( info->sampleRates[i] <= out_caps.dwMaxSecondarySampleRate )\r
2550         break;\r
2551       info->nSampleRates--;\r
2552     }\r
2553     while ( info->sampleRates[0] < out_caps.dwMinSecondarySampleRate ) {\r
2554       info->nSampleRates--;\r
2555       for ( int i=0; i<info->nSampleRates; i++)\r
2556         info->sampleRates[i] = info->sampleRates[i+1];\r
2557       if ( info->nSampleRates <= 0 ) break;\r
2558     }\r
2559   }\r
2560 \r
2561   // Get format information.\r
2562   if ( out_caps.dwFlags & DSCAPS_PRIMARY16BIT ) info->nativeFormats |= RTAUDIO_SINT16;\r
2563   if ( out_caps.dwFlags & DSCAPS_PRIMARY8BIT ) info->nativeFormats |= RTAUDIO_SINT8;\r
2564 \r
2565   output->Release();\r
2566 \r
2567  check_parameters:\r
2568   if ( info->maxInputChannels == 0 && info->maxOutputChannels == 0 )\r
2569     return;\r
2570   if ( info->nSampleRates == 0 || info->nativeFormats == 0 )\r
2571     return;\r
2572 \r
2573   // Determine duplex status.\r
2574   if (info->maxInputChannels < info->maxOutputChannels)\r
2575     info->maxDuplexChannels = info->maxInputChannels;\r
2576   else\r
2577     info->maxDuplexChannels = info->maxOutputChannels;\r
2578   if (info->minInputChannels < info->minOutputChannels)\r
2579     info->minDuplexChannels = info->minInputChannels;\r
2580   else\r
2581     info->minDuplexChannels = info->minOutputChannels;\r
2582 \r
2583   if ( info->maxDuplexChannels > 0 ) info->hasDuplexSupport = true;\r
2584   else info->hasDuplexSupport = false;\r
2585 \r
2586   info->probed = true;\r
2587 \r
2588   return;\r
2589 }\r
2590 \r
2591 bool RtAudio :: probeDeviceOpen(int device, RTAUDIO_STREAM *stream,\r
2592                                 STREAM_MODE mode, int channels, \r
2593                                 int sampleRate, RTAUDIO_FORMAT format,\r
2594                                 int *bufferSize, int numberOfBuffers)\r
2595 {\r
2596   HRESULT result;\r
2597   HWND hWnd = GetForegroundWindow();\r
2598   // According to a note in PortAudio, using GetDesktopWindow()\r
2599   // instead of GetForegroundWindow() is supposed to avoid problems\r
2600   // that occur when the application's window is not the foreground\r
2601   // window.  Also, if the application window closes before the\r
2602   // DirectSound buffer, DirectSound can crash.  However, for console\r
2603   // applications, no sound was produced when using GetDesktopWindow().\r
2604   long buffer_size;\r
2605   LPVOID audioPtr;\r
2606   DWORD dataLen;\r
2607   int nBuffers;\r
2608 \r
2609   // Check the numberOfBuffers parameter and limit the lowest value to\r
2610   // two.  This is a judgement call and a value of two is probably too\r
2611   // low for capture, but it should work for playback.\r
2612   if (numberOfBuffers < 2)\r
2613     nBuffers = 2;\r
2614   else\r
2615     nBuffers = numberOfBuffers;\r
2616 \r
2617   // Define the wave format structure (16-bit PCM, srate, channels)\r
2618   WAVEFORMATEX waveFormat;\r
2619   ZeroMemory(&waveFormat, sizeof(WAVEFORMATEX));\r
2620   waveFormat.wFormatTag = WAVE_FORMAT_PCM;\r
2621   waveFormat.nChannels = channels;\r
2622   waveFormat.nSamplesPerSec = (unsigned long) sampleRate;\r
2623 \r
2624   // Determine the data format.\r
2625   if ( devices[device].nativeFormats ) { // 8-bit and/or 16-bit support\r
2626     if ( format == RTAUDIO_SINT8 ) {\r
2627       if ( devices[device].nativeFormats & RTAUDIO_SINT8 )\r
2628         waveFormat.wBitsPerSample = 8;\r
2629       else\r
2630         waveFormat.wBitsPerSample = 16;\r
2631     }\r
2632     else {\r
2633       if ( devices[device].nativeFormats & RTAUDIO_SINT16 )\r
2634         waveFormat.wBitsPerSample = 16;\r
2635       else\r
2636         waveFormat.wBitsPerSample = 8;\r
2637     }\r
2638   }\r
2639   else {\r
2640     sprintf(message, "RtAudio: no reported data formats for DirectSound device (%s).",\r
2641             devices[device].name);\r
2642     error(RtError::WARNING);\r
2643     return FAILURE;\r
2644   }\r
2645 \r
2646   waveFormat.nBlockAlign = waveFormat.nChannels * waveFormat.wBitsPerSample / 8;\r
2647   waveFormat.nAvgBytesPerSec = waveFormat.nSamplesPerSec * waveFormat.nBlockAlign;\r
2648 \r
2649   if ( mode == PLAYBACK ) {\r
2650 \r
2651     if ( devices[device].maxOutputChannels < channels )\r
2652       return FAILURE;\r
2653 \r
2654     LPGUID id = devices[device].id[0];\r
2655     LPDIRECTSOUND  object;\r
2656     LPDIRECTSOUNDBUFFER buffer;\r
2657     DSBUFFERDESC bufferDescription;\r
2658     \r
2659     result = DirectSoundCreate( id, &object, NULL );\r
2660     if ( FAILED(result) ) {\r
2661       sprintf(message, "RtAudio: Could not create DirectSound playback object (%s): %s.",\r
2662               devices[device].name, getErrorString(result));\r
2663       error(RtError::WARNING);\r
2664       return FAILURE;\r
2665     }\r
2666 \r
2667     // Set cooperative level to DSSCL_EXCLUSIVE\r
2668     result = object->SetCooperativeLevel(hWnd, DSSCL_EXCLUSIVE);\r
2669     if ( FAILED(result) ) {\r
2670       object->Release();\r
2671       sprintf(message, "RtAudio: Unable to set DirectSound cooperative level (%s): %s.",\r
2672               devices[device].name, getErrorString(result));\r
2673       error(RtError::WARNING);\r
2674       return FAILURE;\r
2675     }\r
2676 \r
2677     // Even though we will write to the secondary buffer, we need to\r
2678     // access the primary buffer to set the correct output format.\r
2679     // The default is 8-bit, 22 kHz!\r
2680     // Setup the DS primary buffer description.\r
2681     ZeroMemory(&bufferDescription, sizeof(DSBUFFERDESC));\r
2682     bufferDescription.dwSize = sizeof(DSBUFFERDESC);\r
2683     bufferDescription.dwFlags = DSBCAPS_PRIMARYBUFFER;\r
2684     // Obtain the primary buffer\r
2685     result = object->CreateSoundBuffer(&bufferDescription, &buffer, NULL);\r
2686     if ( FAILED(result) ) {\r
2687       object->Release();\r
2688       sprintf(message, "RtAudio: Unable to access DS primary buffer (%s): %s.",\r
2689               devices[device].name, getErrorString(result));\r
2690       error(RtError::WARNING);\r
2691       return FAILURE;\r
2692     }\r
2693 \r
2694     // Set the primary DS buffer sound format.\r
2695     result = buffer->SetFormat(&waveFormat);\r
2696     if ( FAILED(result) ) {\r
2697       object->Release();\r
2698       sprintf(message, "RtAudio: Unable to set DS primary buffer format (%s): %s.",\r
2699               devices[device].name, getErrorString(result));\r
2700       error(RtError::WARNING);\r
2701       return FAILURE;\r
2702     }\r
2703 \r
2704     // Setup the secondary DS buffer description.\r
2705     buffer_size = channels * *bufferSize * nBuffers * waveFormat.wBitsPerSample / 8;\r
2706     ZeroMemory(&bufferDescription, sizeof(DSBUFFERDESC));\r
2707     bufferDescription.dwSize = sizeof(DSBUFFERDESC);\r
2708     bufferDescription.dwFlags = ( DSBCAPS_STICKYFOCUS |\r
2709                                   DSBCAPS_GETCURRENTPOSITION2 |\r
2710                                   DSBCAPS_LOCHARDWARE );  // Force hardware mixing\r
2711     bufferDescription.dwBufferBytes = buffer_size;\r
2712     bufferDescription.lpwfxFormat = &waveFormat;\r
2713 \r
2714     // Try to create the secondary DS buffer.  If that doesn't work,\r
2715     // try to use software mixing.  Otherwise, there's a problem.\r
2716     result = object->CreateSoundBuffer(&bufferDescription, &buffer, NULL);\r
2717     if ( FAILED(result) ) {\r
2718       bufferDescription.dwFlags = ( DSBCAPS_STICKYFOCUS |\r
2719                                     DSBCAPS_GETCURRENTPOSITION2 |\r
2720                                     DSBCAPS_LOCSOFTWARE );  // Force software mixing\r
2721       result = object->CreateSoundBuffer(&bufferDescription, &buffer, NULL);\r
2722       if ( FAILED(result) ) {\r
2723         object->Release();\r
2724         sprintf(message, "RtAudio: Unable to create secondary DS buffer (%s): %s.",\r
2725                 devices[device].name, getErrorString(result));\r
2726         error(RtError::WARNING);\r
2727         return FAILURE;\r
2728       }\r
2729     }\r
2730 \r
2731     // Get the buffer size ... might be different from what we specified.\r
2732     DSBCAPS dsbcaps;\r
2733     dsbcaps.dwSize = sizeof(DSBCAPS);\r
2734     buffer->GetCaps(&dsbcaps);\r
2735     buffer_size = dsbcaps.dwBufferBytes;\r
2736 \r
2737     // Lock the DS buffer\r
2738     result = buffer->Lock(0, buffer_size, &audioPtr, &dataLen, NULL, NULL, 0);\r
2739     if ( FAILED(result) ) {\r
2740       object->Release();\r
2741       sprintf(message, "RtAudio: Unable to lock DS buffer (%s): %s.",\r
2742               devices[device].name, getErrorString(result));\r
2743       error(RtError::WARNING);\r
2744       return FAILURE;\r
2745     }\r
2746 \r
2747     // Zero the DS buffer\r
2748     ZeroMemory(audioPtr, dataLen);\r
2749 \r
2750     // Unlock the DS buffer\r
2751     result = buffer->Unlock(audioPtr, dataLen, NULL, 0);\r
2752     if ( FAILED(result) ) {\r
2753       object->Release();\r
2754       sprintf(message, "RtAudio: Unable to unlock DS buffer(%s): %s.",\r
2755               devices[device].name, getErrorString(result));\r
2756       error(RtError::WARNING);\r
2757       return FAILURE;\r
2758     }\r
2759 \r
2760     stream->handle[0].object = (void *) object;\r
2761     stream->handle[0].buffer = (void *) buffer;\r
2762     stream->nDeviceChannels[0] = channels;\r
2763   }\r
2764 \r
2765   if ( mode == RECORD ) {\r
2766 \r
2767     if ( devices[device].maxInputChannels < channels )\r
2768       return FAILURE;\r
2769 \r
2770     LPGUID id = devices[device].id[1];\r
2771     LPDIRECTSOUNDCAPTURE  object;\r
2772     LPDIRECTSOUNDCAPTUREBUFFER buffer;\r
2773     DSCBUFFERDESC bufferDescription;\r
2774 \r
2775     result = DirectSoundCaptureCreate( id, &object, NULL );\r
2776     if ( FAILED(result) ) {\r
2777       sprintf(message, "RtAudio: Could not create DirectSound capture object (%s): %s.",\r
2778               devices[device].name, getErrorString(result));\r
2779       error(RtError::WARNING);\r
2780       return FAILURE;\r
2781     }\r
2782 \r
2783     // Setup the secondary DS buffer description.\r
2784     buffer_size = channels * *bufferSize * nBuffers * waveFormat.wBitsPerSample / 8;\r
2785     ZeroMemory(&bufferDescription, sizeof(DSCBUFFERDESC));\r
2786     bufferDescription.dwSize = sizeof(DSCBUFFERDESC);\r
2787     bufferDescription.dwFlags = 0;\r
2788     bufferDescription.dwReserved = 0;\r
2789     bufferDescription.dwBufferBytes = buffer_size;\r
2790     bufferDescription.lpwfxFormat = &waveFormat;\r
2791 \r
2792     // Create the capture buffer.\r
2793     result = object->CreateCaptureBuffer(&bufferDescription, &buffer, NULL);\r
2794     if ( FAILED(result) ) {\r
2795       object->Release();\r
2796       sprintf(message, "RtAudio: Unable to create DS capture buffer (%s): %s.",\r
2797               devices[device].name, getErrorString(result));\r
2798       error(RtError::WARNING);\r
2799       return FAILURE;\r
2800     }\r
2801 \r
2802     // Lock the capture buffer\r
2803     result = buffer->Lock(0, buffer_size, &audioPtr, &dataLen, NULL, NULL, 0);\r
2804     if ( FAILED(result) ) {\r
2805       object->Release();\r
2806       sprintf(message, "RtAudio: Unable to lock DS capture buffer (%s): %s.",\r
2807               devices[device].name, getErrorString(result));\r
2808       error(RtError::WARNING);\r
2809       return FAILURE;\r
2810     }\r
2811 \r
2812     // Zero the buffer\r
2813     ZeroMemory(audioPtr, dataLen);\r
2814 \r
2815     // Unlock the buffer\r
2816     result = buffer->Unlock(audioPtr, dataLen, NULL, 0);\r
2817     if ( FAILED(result) ) {\r
2818       object->Release();\r
2819       sprintf(message, "RtAudio: Unable to unlock DS capture buffer (%s): %s.",\r
2820               devices[device].name, getErrorString(result));\r
2821       error(RtError::WARNING);\r
2822       return FAILURE;\r
2823     }\r
2824 \r
2825     stream->handle[1].object = (void *) object;\r
2826     stream->handle[1].buffer = (void *) buffer;\r
2827     stream->nDeviceChannels[1] = channels;\r
2828   }\r
2829 \r
2830   stream->userFormat = format;\r
2831   if ( waveFormat.wBitsPerSample == 8 )\r
2832     stream->deviceFormat[mode] = RTAUDIO_SINT8;\r
2833   else\r
2834     stream->deviceFormat[mode] = RTAUDIO_SINT16;\r
2835   stream->nUserChannels[mode] = channels;\r
2836   *bufferSize = buffer_size / (channels * nBuffers * waveFormat.wBitsPerSample / 8);\r
2837   stream->bufferSize = *bufferSize;\r
2838 \r
2839   // Set flags for buffer conversion\r
2840   stream->doConvertBuffer[mode] = false;\r
2841   if (stream->userFormat != stream->deviceFormat[mode])\r
2842     stream->doConvertBuffer[mode] = true;\r
2843   if (stream->nUserChannels[mode] < stream->nDeviceChannels[mode])\r
2844     stream->doConvertBuffer[mode] = true;\r
2845 \r
2846   // Allocate necessary internal buffers\r
2847   if ( stream->nUserChannels[0] != stream->nUserChannels[1] ) {\r
2848 \r
2849     long buffer_bytes;\r
2850     if (stream->nUserChannels[0] >= stream->nUserChannels[1])\r
2851       buffer_bytes = stream->nUserChannels[0];\r
2852     else\r
2853       buffer_bytes = stream->nUserChannels[1];\r
2854 \r
2855     buffer_bytes *= *bufferSize * formatBytes(stream->userFormat);\r
2856     if (stream->userBuffer) free(stream->userBuffer);\r
2857     stream->userBuffer = (char *) calloc(buffer_bytes, 1);\r
2858     if (stream->userBuffer == NULL)\r
2859       goto memory_error;\r
2860   }\r
2861 \r
2862   if ( stream->doConvertBuffer[mode] ) {\r
2863 \r
2864     long buffer_bytes;\r
2865     bool makeBuffer = true;\r
2866     if ( mode == PLAYBACK )\r
2867       buffer_bytes = stream->nDeviceChannels[0] * formatBytes(stream->deviceFormat[0]);\r
2868     else { // mode == RECORD\r
2869       buffer_bytes = stream->nDeviceChannels[1] * formatBytes(stream->deviceFormat[1]);\r
2870       if ( stream->mode == PLAYBACK ) {\r
2871         long bytes_out = stream->nDeviceChannels[0] * formatBytes(stream->deviceFormat[0]);\r
2872         if ( buffer_bytes > bytes_out )\r
2873           buffer_bytes = (buffer_bytes > bytes_out) ? buffer_bytes : bytes_out;\r
2874         else\r
2875           makeBuffer = false;\r
2876       }\r
2877     }\r
2878 \r
2879     if ( makeBuffer ) {\r
2880       buffer_bytes *= *bufferSize;\r
2881       if (stream->deviceBuffer) free(stream->deviceBuffer);\r
2882       stream->deviceBuffer = (char *) calloc(buffer_bytes, 1);\r
2883       if (stream->deviceBuffer == NULL)\r
2884         goto memory_error;\r
2885     }\r
2886   }\r
2887 \r
2888   stream->device[mode] = device;\r
2889   stream->state = STREAM_STOPPED;\r
2890   if ( stream->mode == PLAYBACK && mode == RECORD )\r
2891     // We had already set up an output stream.\r
2892     stream->mode = DUPLEX;\r
2893   else\r
2894     stream->mode = mode;\r
2895   stream->nBuffers = nBuffers;\r
2896   stream->sampleRate = sampleRate;\r
2897 \r
2898   return SUCCESS;\r
2899 \r
2900  memory_error:\r
2901   if (stream->handle[0].object) {\r
2902     LPDIRECTSOUND object = (LPDIRECTSOUND) stream->handle[0].object;\r
2903     LPDIRECTSOUNDBUFFER buffer = (LPDIRECTSOUNDBUFFER) stream->handle[0].buffer;\r
2904     if (buffer) {\r
2905       buffer->Release();\r
2906       stream->handle[0].buffer = NULL;\r
2907     }\r
2908     object->Release();\r
2909     stream->handle[0].object = NULL;\r
2910   }\r
2911   if (stream->handle[1].object) {\r
2912     LPDIRECTSOUNDCAPTURE object = (LPDIRECTSOUNDCAPTURE) stream->handle[1].object;\r
2913     LPDIRECTSOUNDCAPTUREBUFFER buffer = (LPDIRECTSOUNDCAPTUREBUFFER) stream->handle[1].buffer;\r
2914     if (buffer) {\r
2915       buffer->Release();\r
2916       stream->handle[1].buffer = NULL;\r
2917     }\r
2918     object->Release();\r
2919     stream->handle[1].object = NULL;\r
2920   }\r
2921   if (stream->userBuffer) {\r
2922     free(stream->userBuffer);\r
2923     stream->userBuffer = 0;\r
2924   }\r
2925   sprintf(message, "RtAudio: error allocating buffer memory (%s).",\r
2926           devices[device].name);\r
2927   error(RtError::WARNING);\r
2928   return FAILURE;\r
2929 }\r
2930 \r
2931 void RtAudio :: cancelStreamCallback(int streamId)\r
2932 {\r
2933   RTAUDIO_STREAM *stream = (RTAUDIO_STREAM *) verifyStream(streamId);\r
2934 \r
2935   if (stream->usingCallback) {\r
2936     stream->usingCallback = false;\r
2937     WaitForSingleObject( (HANDLE)stream->thread, INFINITE );\r
2938     CloseHandle( (HANDLE)stream->thread );\r
2939     stream->thread = 0;\r
2940     stream->callback = NULL;\r
2941     stream->userData = NULL;\r
2942   }\r
2943 }\r
2944 \r
2945 void RtAudio :: closeStream(int streamId)\r
2946 {\r
2947   // We don't want an exception to be thrown here because this\r
2948   // function is called by our class destructor.  So, do our own\r
2949   // streamId check.\r
2950   if ( streams.find( streamId ) == streams.end() ) {\r
2951     sprintf(message, "RtAudio: invalid stream identifier!");\r
2952     error(RtError::WARNING);\r
2953     return;\r
2954   }\r
2955 \r
2956   RTAUDIO_STREAM *stream = (RTAUDIO_STREAM *) streams[streamId];\r
2957 \r
2958   if (stream->usingCallback) {\r
2959     stream->usingCallback = false;\r
2960     WaitForSingleObject( (HANDLE)stream->thread, INFINITE );\r
2961     CloseHandle( (HANDLE)stream->thread );\r
2962   }\r
2963 \r
2964   DeleteCriticalSection(&stream->mutex);\r
2965 \r
2966   if (stream->handle[0].object) {\r
2967     LPDIRECTSOUND object = (LPDIRECTSOUND) stream->handle[0].object;\r
2968     LPDIRECTSOUNDBUFFER buffer = (LPDIRECTSOUNDBUFFER) stream->handle[0].buffer;\r
2969     if (buffer) {\r
2970       buffer->Stop();\r
2971       buffer->Release();\r
2972     }\r
2973     object->Release();\r
2974   }\r
2975 \r
2976   if (stream->handle[1].object) {\r
2977     LPDIRECTSOUNDCAPTURE object = (LPDIRECTSOUNDCAPTURE) stream->handle[1].object;\r
2978     LPDIRECTSOUNDCAPTUREBUFFER buffer = (LPDIRECTSOUNDCAPTUREBUFFER) stream->handle[1].buffer;\r
2979     if (buffer) {\r
2980       buffer->Stop();\r
2981       buffer->Release();\r
2982     }\r
2983     object->Release();\r
2984   }\r
2985 \r
2986   if (stream->userBuffer)\r
2987     free(stream->userBuffer);\r
2988 \r
2989   if (stream->deviceBuffer)\r
2990     free(stream->deviceBuffer);\r
2991 \r
2992   free(stream);\r
2993   streams.erase(streamId);\r
2994 }\r
2995 \r
2996 void RtAudio :: startStream(int streamId)\r
2997 {\r
2998   RTAUDIO_STREAM *stream = (RTAUDIO_STREAM *) verifyStream(streamId);\r
2999 \r
3000   MUTEX_LOCK(&stream->mutex);\r
3001 \r
3002   if (stream->state == STREAM_RUNNING)\r
3003     goto unlock;\r
3004 \r
3005   HRESULT result;\r
3006   if (stream->mode == PLAYBACK || stream->mode == DUPLEX) {\r
3007     LPDIRECTSOUNDBUFFER buffer = (LPDIRECTSOUNDBUFFER) stream->handle[0].buffer;\r
3008     result = buffer->Play(0, 0, DSBPLAY_LOOPING );\r
3009     if ( FAILED(result) ) {\r
3010       sprintf(message, "RtAudio: Unable to start DS buffer (%s): %s.",\r
3011               devices[stream->device[0]].name, getErrorString(result));\r
3012       error(RtError::DRIVER_ERROR);\r
3013     }\r
3014   }\r
3015 \r
3016   if (stream->mode == RECORD || stream->mode == DUPLEX) {\r
3017     LPDIRECTSOUNDCAPTUREBUFFER buffer = (LPDIRECTSOUNDCAPTUREBUFFER) stream->handle[1].buffer;\r
3018     result = buffer->Start(DSCBSTART_LOOPING );\r
3019     if ( FAILED(result) ) {\r
3020       sprintf(message, "RtAudio: Unable to start DS capture buffer (%s): %s.",\r
3021               devices[stream->device[1]].name, getErrorString(result));\r
3022       error(RtError::DRIVER_ERROR);\r
3023     }\r
3024   }\r
3025   stream->state = STREAM_RUNNING;\r
3026 \r
3027  unlock:\r
3028   MUTEX_UNLOCK(&stream->mutex);\r
3029 }\r
3030 \r
3031 void RtAudio :: stopStream(int streamId)\r
3032 {\r
3033   RTAUDIO_STREAM *stream = (RTAUDIO_STREAM *) verifyStream(streamId);\r
3034 \r
3035   MUTEX_LOCK(&stream->mutex);\r
3036 \r
3037   if (stream->state == STREAM_STOPPED) {\r
3038     MUTEX_UNLOCK(&stream->mutex);\r
3039     return;\r
3040   }\r
3041 \r
3042   // There is no specific DirectSound API call to "drain" a buffer\r
3043   // before stopping.  We can hack this for playback by writing zeroes\r
3044   // for another bufferSize * nBuffers frames.  For capture, the\r
3045   // concept is less clear so we'll repeat what we do in the\r
3046   // abortStream() case.\r
3047   HRESULT result;\r
3048   DWORD dsBufferSize;\r
3049   LPVOID buffer1 = NULL;\r
3050   LPVOID buffer2 = NULL;\r
3051   DWORD bufferSize1 = 0;\r
3052   DWORD bufferSize2 = 0;\r
3053   if (stream->mode == PLAYBACK || stream->mode == DUPLEX) {\r
3054 \r
3055     DWORD currentPos, safePos;\r
3056     long buffer_bytes = stream->bufferSize * stream->nDeviceChannels[0];\r
3057     buffer_bytes *= formatBytes(stream->deviceFormat[0]);\r
3058 \r
3059     LPDIRECTSOUNDBUFFER dsBuffer = (LPDIRECTSOUNDBUFFER) stream->handle[0].buffer;\r
3060     UINT nextWritePos = stream->handle[0].bufferPointer;\r
3061     dsBufferSize = buffer_bytes * stream->nBuffers;\r
3062 \r
3063     // Write zeroes for nBuffer counts.\r
3064     for (int i=0; i<stream->nBuffers; i++) {\r
3065 \r
3066       // Find out where the read and "safe write" pointers are.\r
3067       result = dsBuffer->GetCurrentPosition(&currentPos, &safePos);\r
3068       if ( FAILED(result) ) {\r
3069         sprintf(message, "RtAudio: Unable to get current DS position (%s): %s.",\r
3070                 devices[stream->device[0]].name, getErrorString(result));\r
3071         error(RtError::DRIVER_ERROR);\r
3072       }\r
3073 \r
3074       if ( currentPos < nextWritePos ) currentPos += dsBufferSize; // unwrap offset\r
3075       DWORD endWrite = nextWritePos + buffer_bytes;\r
3076 \r
3077       // Check whether the entire write region is behind the play pointer.\r
3078       while ( currentPos < endWrite ) {\r
3079         float millis = (endWrite - currentPos) * 900.0;\r
3080         millis /= ( formatBytes(stream->deviceFormat[0]) * stream->sampleRate);\r
3081         if ( millis < 1.0 ) millis = 1.0;\r
3082         Sleep( (DWORD) millis );\r
3083 \r
3084         // Wake up, find out where we are now\r
3085         result = dsBuffer->GetCurrentPosition( &currentPos, &safePos );\r
3086         if ( FAILED(result) ) {\r
3087           sprintf(message, "RtAudio: Unable to get current DS position (%s): %s.",\r
3088                   devices[stream->device[0]].name, getErrorString(result));\r
3089           error(RtError::DRIVER_ERROR);\r
3090         }\r
3091         if ( currentPos < nextWritePos ) currentPos += dsBufferSize; // unwrap offset\r
3092       }\r
3093 \r
3094       // Lock free space in the buffer\r
3095       result = dsBuffer->Lock (nextWritePos, buffer_bytes, &buffer1,\r
3096                                &bufferSize1, &buffer2, &bufferSize2, 0);\r
3097       if ( FAILED(result) ) {\r
3098         sprintf(message, "RtAudio: Unable to lock DS buffer during playback (%s): %s.",\r
3099                 devices[stream->device[0]].name, getErrorString(result));\r
3100         error(RtError::DRIVER_ERROR);\r
3101       }\r
3102 \r
3103       // Zero the free space\r
3104       ZeroMemory(buffer1, bufferSize1);\r
3105       if (buffer2 != NULL) ZeroMemory(buffer2, bufferSize2);\r
3106 \r
3107       // Update our buffer offset and unlock sound buffer\r
3108       dsBuffer->Unlock (buffer1, bufferSize1, buffer2, bufferSize2);\r
3109       if ( FAILED(result) ) {\r
3110         sprintf(message, "RtAudio: Unable to unlock DS buffer during playback (%s): %s.",\r
3111                 devices[stream->device[0]].name, getErrorString(result));\r
3112         error(RtError::DRIVER_ERROR);\r
3113       }\r
3114       nextWritePos = (nextWritePos + bufferSize1 + bufferSize2) % dsBufferSize;\r
3115       stream->handle[0].bufferPointer = nextWritePos;\r
3116     }\r
3117 \r
3118     // If we play again, start at the beginning of the buffer.\r
3119     stream->handle[0].bufferPointer = 0;\r
3120   }\r
3121 \r
3122   if (stream->mode == RECORD || stream->mode == DUPLEX) {\r
3123     LPDIRECTSOUNDCAPTUREBUFFER buffer = (LPDIRECTSOUNDCAPTUREBUFFER) stream->handle[1].buffer;\r
3124     buffer1 = NULL;\r
3125     bufferSize1 = 0;\r
3126 \r
3127     result = buffer->Stop();\r
3128     if ( FAILED(result) ) {\r
3129       sprintf(message, "RtAudio: Unable to stop DS capture buffer (%s): %s",\r
3130               devices[stream->device[1]].name, getErrorString(result));\r
3131       error(RtError::DRIVER_ERROR);\r
3132     }\r
3133 \r
3134     dsBufferSize = stream->bufferSize * stream->nDeviceChannels[1];\r
3135     dsBufferSize *= formatBytes(stream->deviceFormat[1]) * stream->nBuffers;\r
3136 \r
3137     // Lock the buffer and clear it so that if we start to play again,\r
3138     // we won't have old data playing.\r
3139     result = buffer->Lock(0, dsBufferSize, &buffer1, &bufferSize1, NULL, NULL, 0);\r
3140     if ( FAILED(result) ) {\r
3141       sprintf(message, "RtAudio: Unable to lock DS capture buffer (%s): %s.",\r
3142               devices[stream->device[1]].name, getErrorString(result));\r
3143       error(RtError::DRIVER_ERROR);\r
3144     }\r
3145 \r
3146     // Zero the DS buffer\r
3147     ZeroMemory(buffer1, bufferSize1);\r
3148 \r
3149     // Unlock the DS buffer\r
3150     result = buffer->Unlock(buffer1, bufferSize1, NULL, 0);\r
3151     if ( FAILED(result) ) {\r
3152       sprintf(message, "RtAudio: Unable to unlock DS capture buffer (%s): %s.",\r
3153               devices[stream->device[1]].name, getErrorString(result));\r
3154       error(RtError::DRIVER_ERROR);\r
3155     }\r
3156 \r
3157     // If we start recording again, we must begin at beginning of buffer.\r
3158     stream->handle[1].bufferPointer = 0;\r
3159   }\r
3160   stream->state = STREAM_STOPPED;\r
3161 \r
3162   MUTEX_UNLOCK(&stream->mutex);\r
3163 }\r
3164 \r
3165 void RtAudio :: abortStream(int streamId)\r
3166 {\r
3167   RTAUDIO_STREAM *stream = (RTAUDIO_STREAM *) verifyStream(streamId);\r
3168 \r
3169   MUTEX_LOCK(&stream->mutex);\r
3170 \r
3171   if (stream->state == STREAM_STOPPED)\r
3172     goto unlock;\r
3173 \r
3174   HRESULT result;\r
3175   long dsBufferSize;\r
3176   LPVOID audioPtr;\r
3177   DWORD dataLen;\r
3178   if (stream->mode == PLAYBACK || stream->mode == DUPLEX) {\r
3179     LPDIRECTSOUNDBUFFER buffer = (LPDIRECTSOUNDBUFFER) stream->handle[0].buffer;\r
3180     result = buffer->Stop();\r
3181     if ( FAILED(result) ) {\r
3182       sprintf(message, "RtAudio: Unable to stop DS buffer (%s): %s",\r
3183               devices[stream->device[0]].name, getErrorString(result));\r
3184       error(RtError::DRIVER_ERROR);\r
3185     }\r
3186 \r
3187     dsBufferSize = stream->bufferSize * stream->nDeviceChannels[0];\r
3188     dsBufferSize *= formatBytes(stream->deviceFormat[0]) * stream->nBuffers;\r
3189 \r
3190     // Lock the buffer and clear it so that if we start to play again,\r
3191     // we won't have old data playing.\r
3192     result = buffer->Lock(0, dsBufferSize, &audioPtr, &dataLen, NULL, NULL, 0);\r
3193     if ( FAILED(result) ) {\r
3194       sprintf(message, "RtAudio: Unable to lock DS buffer (%s): %s.",\r
3195               devices[stream->device[0]].name, getErrorString(result));\r
3196       error(RtError::DRIVER_ERROR);\r
3197     }\r
3198 \r
3199     // Zero the DS buffer\r
3200     ZeroMemory(audioPtr, dataLen);\r
3201 \r
3202     // Unlock the DS buffer\r
3203     result = buffer->Unlock(audioPtr, dataLen, NULL, 0);\r
3204     if ( FAILED(result) ) {\r
3205       sprintf(message, "RtAudio: Unable to unlock DS buffer (%s): %s.",\r
3206               devices[stream->device[0]].name, getErrorString(result));\r
3207       error(RtError::DRIVER_ERROR);\r
3208     }\r
3209 \r
3210     // If we start playing again, we must begin at beginning of buffer.\r
3211     stream->handle[0].bufferPointer = 0;\r
3212   }\r
3213 \r
3214   if (stream->mode == RECORD || stream->mode == DUPLEX) {\r
3215     LPDIRECTSOUNDCAPTUREBUFFER buffer = (LPDIRECTSOUNDCAPTUREBUFFER) stream->handle[1].buffer;\r
3216     audioPtr = NULL;\r
3217     dataLen = 0;\r
3218 \r
3219     result = buffer->Stop();\r
3220     if ( FAILED(result) ) {\r
3221       sprintf(message, "RtAudio: Unable to stop DS capture buffer (%s): %s",\r
3222               devices[stream->device[1]].name, getErrorString(result));\r
3223       error(RtError::DRIVER_ERROR);\r
3224     }\r
3225 \r
3226     dsBufferSize = stream->bufferSize * stream->nDeviceChannels[1];\r
3227     dsBufferSize *= formatBytes(stream->deviceFormat[1]) * stream->nBuffers;\r
3228 \r
3229     // Lock the buffer and clear it so that if we start to play again,\r
3230     // we won't have old data playing.\r
3231     result = buffer->Lock(0, dsBufferSize, &audioPtr, &dataLen, NULL, NULL, 0);\r
3232     if ( FAILED(result) ) {\r
3233       sprintf(message, "RtAudio: Unable to lock DS capture buffer (%s): %s.",\r
3234               devices[stream->device[1]].name, getErrorString(result));\r
3235       error(RtError::DRIVER_ERROR);\r
3236     }\r
3237 \r
3238     // Zero the DS buffer\r
3239     ZeroMemory(audioPtr, dataLen);\r
3240 \r
3241     // Unlock the DS buffer\r
3242     result = buffer->Unlock(audioPtr, dataLen, NULL, 0);\r
3243     if ( FAILED(result) ) {\r
3244       sprintf(message, "RtAudio: Unable to unlock DS capture buffer (%s): %s.",\r
3245               devices[stream->device[1]].name, getErrorString(result));\r
3246       error(RtError::DRIVER_ERROR);\r
3247     }\r
3248 \r
3249     // If we start recording again, we must begin at beginning of buffer.\r
3250     stream->handle[1].bufferPointer = 0;\r
3251   }\r
3252   stream->state = STREAM_STOPPED;\r
3253 \r
3254  unlock:\r
3255   MUTEX_UNLOCK(&stream->mutex);\r
3256 }\r
3257 \r
3258 int RtAudio :: streamWillBlock(int streamId)\r
3259 {\r
3260   RTAUDIO_STREAM *stream = (RTAUDIO_STREAM *) verifyStream(streamId);\r
3261 \r
3262   MUTEX_LOCK(&stream->mutex);\r
3263 \r
3264   int channels;\r
3265   int frames = 0;\r
3266   if (stream->state == STREAM_STOPPED)\r
3267     goto unlock;\r
3268 \r
3269   HRESULT result;\r
3270   DWORD currentPos, safePos;\r
3271   channels = 1;\r
3272   if (stream->mode == PLAYBACK || stream->mode == DUPLEX) {\r
3273 \r
3274     LPDIRECTSOUNDBUFFER dsBuffer = (LPDIRECTSOUNDBUFFER) stream->handle[0].buffer;\r
3275     UINT nextWritePos = stream->handle[0].bufferPointer;\r
3276     channels = stream->nDeviceChannels[0];\r
3277     DWORD dsBufferSize = stream->bufferSize * channels;\r
3278     dsBufferSize *= formatBytes(stream->deviceFormat[0]) * stream->nBuffers;\r
3279 \r
3280     // Find out where the read and "safe write" pointers are.\r
3281     result = dsBuffer->GetCurrentPosition(&currentPos, &safePos);\r
3282     if ( FAILED(result) ) {\r
3283       sprintf(message, "RtAudio: Unable to get current DS position (%s): %s.",\r
3284               devices[stream->device[0]].name, getErrorString(result));\r
3285       error(RtError::DRIVER_ERROR);\r
3286     }\r
3287 \r
3288     if ( currentPos < nextWritePos ) currentPos += dsBufferSize; // unwrap offset\r
3289     frames = currentPos - nextWritePos;\r
3290     frames /= channels * formatBytes(stream->deviceFormat[0]);\r
3291   }\r
3292 \r
3293   if (stream->mode == RECORD || stream->mode == DUPLEX) {\r
3294 \r
3295     LPDIRECTSOUNDCAPTUREBUFFER dsBuffer = (LPDIRECTSOUNDCAPTUREBUFFER) stream->handle[1].buffer;\r
3296     UINT nextReadPos = stream->handle[1].bufferPointer;\r
3297     channels = stream->nDeviceChannels[1];\r
3298     DWORD dsBufferSize = stream->bufferSize * channels;\r
3299     dsBufferSize *= formatBytes(stream->deviceFormat[1]) * stream->nBuffers;\r
3300 \r
3301     // Find out where the write and "safe read" pointers are.\r
3302     result = dsBuffer->GetCurrentPosition(&currentPos, &safePos);\r
3303     if ( FAILED(result) ) {\r
3304       sprintf(message, "RtAudio: Unable to get current DS capture position (%s): %s.",\r
3305               devices[stream->device[1]].name, getErrorString(result));\r
3306       error(RtError::DRIVER_ERROR);\r
3307     }\r
3308 \r
3309     if ( safePos < nextReadPos ) safePos += dsBufferSize; // unwrap offset\r
3310 \r
3311     if (stream->mode == DUPLEX ) {\r
3312       // Take largest value of the two.\r
3313       int temp = safePos - nextReadPos;\r
3314       temp /= channels * formatBytes(stream->deviceFormat[1]);\r
3315       frames = ( temp > frames ) ? temp : frames;\r
3316     }\r
3317     else {\r
3318       frames = safePos - nextReadPos;\r
3319       frames /= channels * formatBytes(stream->deviceFormat[1]);\r
3320     }\r
3321   }\r
3322 \r
3323   frames = stream->bufferSize - frames;\r
3324   if (frames < 0) frames = 0;\r
3325 \r
3326  unlock:\r
3327   MUTEX_UNLOCK(&stream->mutex);\r
3328   return frames;\r
3329 }\r
3330 \r
3331 void RtAudio :: tickStream(int streamId)\r
3332 {\r
3333   RTAUDIO_STREAM *stream = (RTAUDIO_STREAM *) verifyStream(streamId);\r
3334 \r
3335   int stopStream = 0;\r
3336   if (stream->state == STREAM_STOPPED) {\r
3337     if (stream->usingCallback) Sleep(50); // sleep 50 milliseconds\r
3338     return;\r
3339   }\r
3340   else if (stream->usingCallback) {\r
3341     stopStream = stream->callback(stream->userBuffer, stream->bufferSize, stream->userData);    \r
3342   }\r
3343 \r
3344   MUTEX_LOCK(&stream->mutex);\r
3345 \r
3346   // The state might change while waiting on a mutex.\r
3347   if (stream->state == STREAM_STOPPED) {\r
3348     MUTEX_UNLOCK(&stream->mutex);\r
3349     if (stream->usingCallback && stopStream)\r
3350       this->stopStream(streamId);\r
3351   }\r
3352 \r
3353   HRESULT result;\r
3354   DWORD currentPos, safePos;\r
3355   LPVOID buffer1 = NULL;\r
3356   LPVOID buffer2 = NULL;\r
3357   DWORD bufferSize1 = 0;\r
3358   DWORD bufferSize2 = 0;\r
3359   char *buffer;\r
3360   long buffer_bytes;\r
3361   if (stream->mode == PLAYBACK || stream->mode == DUPLEX) {\r
3362 \r
3363     // Setup parameters and do buffer conversion if necessary.\r
3364     if (stream->doConvertBuffer[0]) {\r
3365       convertStreamBuffer(stream, PLAYBACK);\r
3366       buffer = stream->deviceBuffer;\r
3367       buffer_bytes = stream->bufferSize * stream->nDeviceChannels[0];\r
3368       buffer_bytes *= formatBytes(stream->deviceFormat[0]);\r
3369     }\r
3370     else {\r
3371       buffer = stream->userBuffer;\r
3372       buffer_bytes = stream->bufferSize * stream->nUserChannels[0];\r
3373       buffer_bytes *= formatBytes(stream->userFormat);\r
3374     }\r
3375 \r
3376     // No byte swapping necessary in DirectSound implementation.\r
3377 \r
3378     LPDIRECTSOUNDBUFFER dsBuffer = (LPDIRECTSOUNDBUFFER) stream->handle[0].buffer;\r
3379     UINT nextWritePos = stream->handle[0].bufferPointer;\r
3380     DWORD dsBufferSize = buffer_bytes * stream->nBuffers;\r
3381 \r
3382     // Find out where the read and "safe write" pointers are.\r
3383     result = dsBuffer->GetCurrentPosition(&currentPos, &safePos);\r
3384     if ( FAILED(result) ) {\r
3385       sprintf(message, "RtAudio: Unable to get current DS position (%s): %s.",\r
3386               devices[stream->device[0]].name, getErrorString(result));\r
3387       error(RtError::DRIVER_ERROR);\r
3388     }\r
3389 \r
3390     if ( currentPos < nextWritePos ) currentPos += dsBufferSize; // unwrap offset\r
3391     DWORD endWrite = nextWritePos + buffer_bytes;\r
3392 \r
3393     // Check whether the entire write region is behind the play pointer.\r
3394     while ( currentPos < endWrite ) {\r
3395       // If we are here, then we must wait until the play pointer gets\r
3396       // beyond the write region.  The approach here is to use the\r
3397       // Sleep() function to suspend operation until safePos catches\r
3398       // up. Calculate number of milliseconds to wait as:\r
3399       //   time = distance * (milliseconds/second) * fudgefactor /\r
3400       //          ((bytes/sample) * (samples/second))\r
3401       // A "fudgefactor" less than 1 is used because it was found\r
3402       // that sleeping too long was MUCH worse than sleeping for\r
3403       // several shorter periods.\r
3404       float millis = (endWrite - currentPos) * 900.0;\r
3405       millis /= ( formatBytes(stream->deviceFormat[0]) * stream->sampleRate);\r
3406       if ( millis < 1.0 ) millis = 1.0;\r
3407       Sleep( (DWORD) millis );\r
3408 \r
3409       // Wake up, find out where we are now\r
3410       result = dsBuffer->GetCurrentPosition( &currentPos, &safePos );\r
3411       if ( FAILED(result) ) {\r
3412         sprintf(message, "RtAudio: Unable to get current DS position (%s): %s.",\r
3413               devices[stream->device[0]].name, getErrorString(result));\r
3414         error(RtError::DRIVER_ERROR);\r
3415       }\r
3416       if ( currentPos < nextWritePos ) currentPos += dsBufferSize; // unwrap offset\r
3417     }\r
3418 \r
3419     // Lock free space in the buffer\r
3420     result = dsBuffer->Lock (nextWritePos, buffer_bytes, &buffer1,\r
3421                              &bufferSize1, &buffer2, &bufferSize2, 0);\r
3422     if ( FAILED(result) ) {\r
3423       sprintf(message, "RtAudio: Unable to lock DS buffer during playback (%s): %s.",\r
3424               devices[stream->device[0]].name, getErrorString(result));\r
3425       error(RtError::DRIVER_ERROR);\r
3426     }\r
3427 \r
3428     // Copy our buffer into the DS buffer\r
3429     CopyMemory(buffer1, buffer, bufferSize1);\r
3430     if (buffer2 != NULL) CopyMemory(buffer2, buffer+bufferSize1, bufferSize2);\r
3431 \r
3432     // Update our buffer offset and unlock sound buffer\r
3433     dsBuffer->Unlock (buffer1, bufferSize1, buffer2, bufferSize2);\r
3434     if ( FAILED(result) ) {\r
3435       sprintf(message, "RtAudio: Unable to unlock DS buffer during playback (%s): %s.",\r
3436               devices[stream->device[0]].name, getErrorString(result));\r
3437       error(RtError::DRIVER_ERROR);\r
3438     }\r
3439     nextWritePos = (nextWritePos + bufferSize1 + bufferSize2) % dsBufferSize;\r
3440     stream->handle[0].bufferPointer = nextWritePos;\r
3441   }\r
3442 \r
3443   if (stream->mode == RECORD || stream->mode == DUPLEX) {\r
3444 \r
3445     // Setup parameters.\r
3446     if (stream->doConvertBuffer[1]) {\r
3447       buffer = stream->deviceBuffer;\r
3448       buffer_bytes = stream->bufferSize * stream->nDeviceChannels[1];\r
3449       buffer_bytes *= formatBytes(stream->deviceFormat[1]);\r
3450     }\r
3451     else {\r
3452       buffer = stream->userBuffer;\r
3453       buffer_bytes = stream->bufferSize * stream->nUserChannels[1];\r
3454       buffer_bytes *= formatBytes(stream->userFormat);\r
3455     }\r
3456 \r
3457     LPDIRECTSOUNDCAPTUREBUFFER dsBuffer = (LPDIRECTSOUNDCAPTUREBUFFER) stream->handle[1].buffer;\r
3458     UINT nextReadPos = stream->handle[1].bufferPointer;\r
3459     DWORD dsBufferSize = buffer_bytes * stream->nBuffers;\r
3460 \r
3461     // Find out where the write and "safe read" pointers are.\r
3462     result = dsBuffer->GetCurrentPosition(&currentPos, &safePos);\r
3463     if ( FAILED(result) ) {\r
3464       sprintf(message, "RtAudio: Unable to get current DS capture position (%s): %s.",\r
3465               devices[stream->device[1]].name, getErrorString(result));\r
3466       error(RtError::DRIVER_ERROR);\r
3467     }\r
3468 \r
3469     if ( safePos < nextReadPos ) safePos += dsBufferSize; // unwrap offset\r
3470     DWORD endRead = nextReadPos + buffer_bytes;\r
3471 \r
3472     // Check whether the entire write region is behind the play pointer.\r
3473     while ( safePos < endRead ) {\r
3474       // See comments for playback.\r
3475       float millis = (endRead - safePos) * 900.0;\r
3476       millis /= ( formatBytes(stream->deviceFormat[1]) * stream->sampleRate);\r
3477       if ( millis < 1.0 ) millis = 1.0;\r
3478       Sleep( (DWORD) millis );\r
3479 \r
3480       // Wake up, find out where we are now\r
3481       result = dsBuffer->GetCurrentPosition( &currentPos, &safePos );\r
3482       if ( FAILED(result) ) {\r
3483         sprintf(message, "RtAudio: Unable to get current DS capture position (%s): %s.",\r
3484                 devices[stream->device[1]].name, getErrorString(result));\r
3485         error(RtError::DRIVER_ERROR);\r
3486       }\r
3487       \r
3488       if ( safePos < nextReadPos ) safePos += dsBufferSize; // unwrap offset\r
3489     }\r
3490 \r
3491     // Lock free space in the buffer\r
3492     result = dsBuffer->Lock (nextReadPos, buffer_bytes, &buffer1,\r
3493                              &bufferSize1, &buffer2, &bufferSize2, 0);\r
3494     if ( FAILED(result) ) {\r
3495       sprintf(message, "RtAudio: Unable to lock DS buffer during capture (%s): %s.",\r
3496               devices[stream->device[1]].name, getErrorString(result));\r
3497       error(RtError::DRIVER_ERROR);\r
3498     }\r
3499 \r
3500     // Copy our buffer into the DS buffer\r
3501     CopyMemory(buffer, buffer1, bufferSize1);\r
3502     if (buffer2 != NULL) CopyMemory(buffer+bufferSize1, buffer2, bufferSize2);\r
3503 \r
3504     // Update our buffer offset and unlock sound buffer\r
3505     nextReadPos = (nextReadPos + bufferSize1 + bufferSize2) % dsBufferSize;\r
3506     dsBuffer->Unlock (buffer1, bufferSize1, buffer2, bufferSize2);\r
3507     if ( FAILED(result) ) {\r
3508       sprintf(message, "RtAudio: Unable to unlock DS buffer during capture (%s): %s.",\r
3509               devices[stream->device[1]].name, getErrorString(result));\r
3510       error(RtError::DRIVER_ERROR);\r
3511     }\r
3512     stream->handle[1].bufferPointer = nextReadPos;\r
3513 \r
3514     // No byte swapping necessary in DirectSound implementation.\r
3515 \r
3516     // Do buffer conversion if necessary.\r
3517     if (stream->doConvertBuffer[1])\r
3518       convertStreamBuffer(stream, RECORD);\r
3519   }\r
3520 \r
3521   MUTEX_UNLOCK(&stream->mutex);\r
3522 \r
3523   if (stream->usingCallback && stopStream)\r
3524     this->stopStream(streamId);\r
3525 }\r
3526 \r
3527 // Definitions for utility functions and callbacks\r
3528 // specific to the DirectSound implementation.\r
3529 \r
3530 extern "C" unsigned __stdcall callbackHandler(void *ptr)\r
3531 {\r
3532   RtAudio *object = thread_info.object;\r
3533   int stream = thread_info.streamId;\r
3534   bool *usingCallback = (bool *) ptr;\r
3535 \r
3536   while ( *usingCallback ) {\r
3537     try {\r
3538       object->tickStream(stream);\r
3539     }\r
3540     catch (RtError &exception) {\r
3541       fprintf(stderr, "\nCallback thread error (%s) ... closing thread.\n\n",\r
3542               exception.getMessage());\r
3543       break;\r
3544     }\r
3545   }\r
3546 \r
3547   _endthreadex( 0 );\r
3548   return 0;\r
3549 }\r
3550 \r
3551 static bool CALLBACK deviceCountCallback(LPGUID lpguid,\r
3552                                          LPCSTR lpcstrDescription,\r
3553                                          LPCSTR lpcstrModule,\r
3554                                          LPVOID lpContext)\r
3555 {\r
3556   int *pointer = ((int *) lpContext);\r
3557   (*pointer)++;\r
3558 \r
3559   return true;\r
3560 }\r
3561 \r
3562 static bool CALLBACK deviceInfoCallback(LPGUID lpguid,\r
3563                                         LPCSTR lpcstrDescription,\r
3564                                         LPCSTR lpcstrModule,\r
3565                                         LPVOID lpContext)\r
3566 {\r
3567   enum_info *info = ((enum_info *) lpContext);\r
3568   while (strlen(info->name) > 0) info++;\r
3569 \r
3570   strncpy(info->name, lpcstrDescription, 64);\r
3571   info->id = lpguid;\r
3572 \r
3573         HRESULT    hr;\r
3574   info->isValid = false;\r
3575   if (info->isInput == true) {\r
3576     DSCCAPS               caps;\r
3577     LPDIRECTSOUNDCAPTURE  object;\r
3578 \r
3579     hr = DirectSoundCaptureCreate(  lpguid, &object,   NULL );\r
3580     if( hr != DS_OK ) return true;\r
3581 \r
3582     caps.dwSize = sizeof(caps);\r
3583     hr = object->GetCaps( &caps );\r
3584     if( hr == DS_OK ) {\r
3585       if (caps.dwChannels > 0 && caps.dwFormats > 0)\r
3586         info->isValid = true;\r
3587     }\r
3588     object->Release();\r
3589   }\r
3590   else {\r
3591     DSCAPS         caps;\r
3592     LPDIRECTSOUND  object;\r
3593     hr = DirectSoundCreate(  lpguid, &object,   NULL );\r
3594     if( hr != DS_OK ) return true;\r
3595 \r
3596     caps.dwSize = sizeof(caps);\r
3597     hr = object->GetCaps( &caps );\r
3598     if( hr == DS_OK ) {\r
3599       if ( caps.dwFlags & DSCAPS_PRIMARYMONO || caps.dwFlags & DSCAPS_PRIMARYSTEREO )\r
3600         info->isValid = true;\r
3601     }\r
3602     object->Release();\r
3603   }\r
3604 \r
3605   return true;\r
3606 }\r
3607 \r
3608 static char* getErrorString(int code)\r
3609 {\r
3610         switch (code) {\r
3611 \r
3612   case DSERR_ALLOCATED:\r
3613     return "Direct Sound already allocated";\r
3614 \r
3615   case DSERR_CONTROLUNAVAIL:\r
3616     return "Direct Sound control unavailable";\r
3617 \r
3618   case DSERR_INVALIDPARAM:\r
3619     return "Direct Sound invalid parameter";\r
3620 \r
3621   case DSERR_INVALIDCALL:\r
3622     return "Direct Sound invalid call";\r
3623 \r
3624   case DSERR_GENERIC:\r
3625     return "Direct Sound generic error";\r
3626 \r
3627   case DSERR_PRIOLEVELNEEDED:\r
3628     return "Direct Sound Priority level needed";\r
3629 \r
3630   case DSERR_OUTOFMEMORY:\r
3631     return "Direct Sound out of memory";\r
3632 \r
3633   case DSERR_BADFORMAT:\r
3634     return "Direct Sound bad format";\r
3635 \r
3636   case DSERR_UNSUPPORTED:\r
3637     return "Direct Sound unsupported error";\r
3638 \r
3639   case DSERR_NODRIVER:\r
3640     return "Direct Sound no driver error";\r
3641 \r
3642   case DSERR_ALREADYINITIALIZED:\r
3643     return "Direct Sound already initialized";\r
3644 \r
3645   case DSERR_NOAGGREGATION:\r
3646     return "Direct Sound no aggregation";\r
3647 \r
3648   case DSERR_BUFFERLOST:\r
3649     return "Direct Sound buffer lost";\r
3650 \r
3651   case DSERR_OTHERAPPHASPRIO:\r
3652     return "Direct Sound other app has priority";\r
3653 \r
3654   case DSERR_UNINITIALIZED:\r
3655     return "Direct Sound uninitialized";\r
3656 \r
3657   default:\r
3658     return "Direct Sound unknown error";\r
3659         }\r
3660 }\r
3661 \r
3662 //******************** End of __WINDOWS_DS__ *********************//\r
3663 \r
3664 #elif defined(__IRIX_AL__) // SGI's AL API for IRIX\r
3665 \r
3666 #include <unistd.h>\r
3667 #include <errno.h>\r
3668 \r
3669 void RtAudio :: initialize(void)\r
3670 {\r
3671 \r
3672   // Count cards and devices\r
3673   nDevices = 0;\r
3674 \r
3675   // Determine the total number of input and output devices.\r
3676   nDevices = alQueryValues(AL_SYSTEM, AL_DEVICES, 0, 0, 0, 0);\r
3677   if (nDevices < 0) {\r
3678     sprintf(message, "RtAudio: AL error counting devices: %s.",\r
3679             alGetErrorString(oserror()));\r
3680     error(RtError::DRIVER_ERROR);\r
3681   }\r
3682 \r
3683   if (nDevices <= 0) return;\r
3684 \r
3685   ALvalue *vls = (ALvalue *) new ALvalue[nDevices];\r
3686 \r
3687   // Add one for our default input/output devices.\r
3688   nDevices++;\r
3689 \r
3690   //  Allocate the RTAUDIO_DEVICE structures.\r
3691   devices = (RTAUDIO_DEVICE *) calloc(nDevices, sizeof(RTAUDIO_DEVICE));\r
3692   if (devices == NULL) {\r
3693     sprintf(message, "RtAudio: memory allocation error!");\r
3694     error(RtError::MEMORY_ERROR);\r
3695   }\r
3696 \r
3697   // Write device ascii identifiers to device info structure.\r
3698   char name[32];\r
3699   int outs, ins, i;\r
3700   ALpv pvs[1];\r
3701   pvs[0].param = AL_NAME;\r
3702   pvs[0].value.ptr = name;\r
3703   pvs[0].sizeIn = 32;\r
3704 \r
3705   strcpy(devices[0].name, "Default Input/Output Devices");\r
3706 \r
3707   outs = alQueryValues(AL_SYSTEM, AL_DEFAULT_OUTPUT, vls, nDevices-1, 0, 0);\r
3708   if (outs < 0) {\r
3709     sprintf(message, "RtAudio: AL error getting output devices: %s.",\r
3710             alGetErrorString(oserror()));\r
3711     error(RtError::DRIVER_ERROR);\r
3712   }\r
3713 \r
3714   for (i=0; i<outs; i++) {\r
3715     if (alGetParams(vls[i].i, pvs, 1) < 0) {\r
3716       sprintf(message, "RtAudio: AL error querying output devices: %s.",\r
3717               alGetErrorString(oserror()));\r
3718       error(RtError::DRIVER_ERROR);\r
3719     }\r
3720     strncpy(devices[i+1].name, name, 32);\r
3721     devices[i+1].id[0] = vls[i].i;\r
3722   }\r
3723 \r
3724   ins = alQueryValues(AL_SYSTEM, AL_DEFAULT_INPUT, &vls[outs], nDevices-outs-1, 0, 0);\r
3725   if (ins < 0) {\r
3726     sprintf(message, "RtAudio: AL error getting input devices: %s.",\r
3727             alGetErrorString(oserror()));\r
3728     error(RtError::DRIVER_ERROR);\r
3729   }\r
3730 \r
3731   for (i=outs; i<ins+outs; i++) {\r
3732     if (alGetParams(vls[i].i, pvs, 1) < 0) {\r
3733       sprintf(message, "RtAudio: AL error querying input devices: %s.",\r
3734               alGetErrorString(oserror()));\r
3735       error(RtError::DRIVER_ERROR);\r
3736     }\r
3737     strncpy(devices[i+1].name, name, 32);\r
3738     devices[i+1].id[1] = vls[i].i;\r
3739   }\r
3740 \r
3741   delete [] vls;\r
3742 \r
3743   return;\r
3744 }\r
3745 \r
3746 void RtAudio :: probeDeviceInfo(RTAUDIO_DEVICE *info)\r
3747 {\r
3748   int resource, result, i;\r
3749   ALvalue value;\r
3750   ALparamInfo pinfo;\r
3751 \r
3752   // Get output resource ID if it exists.\r
3753   if ( !strncmp(info->name, "Default Input/Output Devices", 28) ) {\r
3754     result = alQueryValues(AL_SYSTEM, AL_DEFAULT_OUTPUT, &value, 1, 0, 0);\r
3755     if (result < 0) {\r
3756       sprintf(message, "RtAudio: AL error getting default output device id: %s.",\r
3757               alGetErrorString(oserror()));\r
3758       error(RtError::WARNING);\r
3759     }\r
3760     else\r
3761       resource = value.i;\r
3762   }\r
3763   else\r
3764     resource = info->id[0];\r
3765 \r
3766   if (resource > 0) {\r
3767 \r
3768     // Probe output device parameters.\r
3769     result = alQueryValues(resource, AL_CHANNELS, &value, 1, 0, 0);\r
3770     if (result < 0) {\r
3771       sprintf(message, "RtAudio: AL error getting device (%s) channels: %s.",\r
3772               info->name, alGetErrorString(oserror()));\r
3773       error(RtError::WARNING);\r
3774     }\r
3775     else {\r
3776       info->maxOutputChannels = value.i;\r
3777       info->minOutputChannels = 1;\r
3778     }\r
3779 \r
3780     result = alGetParamInfo(resource, AL_RATE, &pinfo);\r
3781     if (result < 0) {\r
3782       sprintf(message, "RtAudio: AL error getting device (%s) rates: %s.",\r
3783               info->name, alGetErrorString(oserror()));\r
3784       error(RtError::WARNING);\r
3785     }\r
3786     else {\r
3787       info->nSampleRates = 0;\r
3788       for (i=0; i<MAX_SAMPLE_RATES; i++) {\r
3789         if ( SAMPLE_RATES[i] >= pinfo.min.i && SAMPLE_RATES[i] <= pinfo.max.i ) {\r
3790           info->sampleRates[info->nSampleRates] = SAMPLE_RATES[i];\r
3791           info->nSampleRates++;\r
3792         }\r
3793       }\r
3794     }\r
3795 \r
3796     // The AL library supports all our formats, except 24-bit and 32-bit ints.\r
3797     info->nativeFormats = (RTAUDIO_FORMAT) 51;\r
3798   }\r
3799 \r
3800   // Now get input resource ID if it exists.\r
3801   if ( !strncmp(info->name, "Default Input/Output Devices", 28) ) {\r
3802     result = alQueryValues(AL_SYSTEM, AL_DEFAULT_INPUT, &value, 1, 0, 0);\r
3803     if (result < 0) {\r
3804       sprintf(message, "RtAudio: AL error getting default input device id: %s.",\r
3805               alGetErrorString(oserror()));\r
3806       error(RtError::WARNING);\r
3807     }\r
3808     else\r
3809       resource = value.i;\r
3810   }\r
3811   else\r
3812     resource = info->id[1];\r
3813 \r
3814   if (resource > 0) {\r
3815 \r
3816     // Probe input device parameters.\r
3817     result = alQueryValues(resource, AL_CHANNELS, &value, 1, 0, 0);\r
3818     if (result < 0) {\r
3819       sprintf(message, "RtAudio: AL error getting device (%s) channels: %s.",\r
3820               info->name, alGetErrorString(oserror()));\r
3821       error(RtError::WARNING);\r
3822     }\r
3823     else {\r
3824       info->maxInputChannels = value.i;\r
3825       info->minInputChannels = 1;\r
3826     }\r
3827 \r
3828     result = alGetParamInfo(resource, AL_RATE, &pinfo);\r
3829     if (result < 0) {\r
3830       sprintf(message, "RtAudio: AL error getting device (%s) rates: %s.",\r
3831               info->name, alGetErrorString(oserror()));\r
3832       error(RtError::WARNING);\r
3833     }\r
3834     else {\r
3835       // In the case of the default device, these values will\r
3836       // overwrite the rates determined for the output device.  Since\r
3837       // the input device is most likely to be more limited than the\r
3838       // output device, this is ok.\r
3839       info->nSampleRates = 0;\r
3840       for (i=0; i<MAX_SAMPLE_RATES; i++) {\r
3841         if ( SAMPLE_RATES[i] >= pinfo.min.i && SAMPLE_RATES[i] <= pinfo.max.i ) {\r
3842           info->sampleRates[info->nSampleRates] = SAMPLE_RATES[i];\r
3843           info->nSampleRates++;\r
3844         }\r
3845       }\r
3846     }\r
3847 \r
3848     // The AL library supports all our formats, except 24-bit and 32-bit ints.\r
3849     info->nativeFormats = (RTAUDIO_FORMAT) 51;\r
3850   }\r
3851 \r
3852   if ( info->maxInputChannels == 0 && info->maxOutputChannels == 0 )\r
3853     return;\r
3854   if ( info->nSampleRates == 0 )\r
3855     return;\r
3856 \r
3857   // Determine duplex status.\r
3858   if (info->maxInputChannels < info->maxOutputChannels)\r
3859     info->maxDuplexChannels = info->maxInputChannels;\r
3860   else\r
3861     info->maxDuplexChannels = info->maxOutputChannels;\r
3862   if (info->minInputChannels < info->minOutputChannels)\r
3863     info->minDuplexChannels = info->minInputChannels;\r
3864   else\r
3865     info->minDuplexChannels = info->minOutputChannels;\r
3866 \r
3867   if ( info->maxDuplexChannels > 0 ) info->hasDuplexSupport = true;\r
3868   else info->hasDuplexSupport = false;\r
3869 \r
3870   info->probed = true;\r
3871 \r
3872   return;\r
3873 }\r
3874 \r
3875 bool RtAudio :: probeDeviceOpen(int device, RTAUDIO_STREAM *stream,\r
3876                                 STREAM_MODE mode, int channels, \r
3877                                 int sampleRate, RTAUDIO_FORMAT format,\r
3878                                 int *bufferSize, int numberOfBuffers)\r
3879 {\r
3880   int result, resource, nBuffers;\r
3881   ALconfig al_config;\r
3882   ALport port;\r
3883   ALpv pvs[2];\r
3884 \r
3885   // Get a new ALconfig structure.\r
3886   al_config = alNewConfig();\r
3887   if ( !al_config ) {\r
3888     sprintf(message,"RtAudio: can't get AL config: %s.",\r
3889             alGetErrorString(oserror()));\r
3890     error(RtError::WARNING);\r
3891     return FAILURE;\r
3892   }\r
3893 \r
3894   // Set the channels.\r
3895   result = alSetChannels(al_config, channels);\r
3896   if ( result < 0 ) {\r
3897     sprintf(message,"RtAudio: can't set %d channels in AL config: %s.",\r
3898             channels, alGetErrorString(oserror()));\r
3899     error(RtError::WARNING);\r
3900     return FAILURE;\r
3901   }\r
3902 \r
3903   // Set the queue (buffer) size.\r
3904   if ( numberOfBuffers < 1 )\r
3905     nBuffers = 1;\r
3906   else\r
3907     nBuffers = numberOfBuffers;\r
3908   long buffer_size = *bufferSize * nBuffers;\r
3909   result = alSetQueueSize(al_config, buffer_size); // in sample frames\r
3910   if ( result < 0 ) {\r
3911     sprintf(message,"RtAudio: can't set buffer size (%ld) in AL config: %s.",\r
3912             buffer_size, alGetErrorString(oserror()));\r
3913     error(RtError::WARNING);\r
3914     return FAILURE;\r
3915   }\r
3916 \r
3917   // Set the data format.\r
3918   stream->userFormat = format;\r
3919   stream->deviceFormat[mode] = format;\r
3920   if (format == RTAUDIO_SINT8) {\r
3921     result = alSetSampFmt(al_config, AL_SAMPFMT_TWOSCOMP);\r
3922     result = alSetWidth(al_config, AL_SAMPLE_8);\r
3923   }\r
3924   else if (format == RTAUDIO_SINT16) {\r
3925     result = alSetSampFmt(al_config, AL_SAMPFMT_TWOSCOMP);\r
3926     result = alSetWidth(al_config, AL_SAMPLE_16);\r
3927   }\r
3928   else if (format == RTAUDIO_SINT24) {\r
3929     // Our 24-bit format assumes the upper 3 bytes of a 4 byte word.\r
3930     // The AL library uses the lower 3 bytes, so we'll need to do our\r
3931     // own conversion.\r
3932     result = alSetSampFmt(al_config, AL_SAMPFMT_FLOAT);\r
3933     stream->deviceFormat[mode] = RTAUDIO_FLOAT32;\r
3934   }\r
3935   else if (format == RTAUDIO_SINT32) {\r
3936     // The AL library doesn't seem to support the 32-bit integer\r
3937     // format, so we'll need to do our own conversion.\r
3938     result = alSetSampFmt(al_config, AL_SAMPFMT_FLOAT);\r
3939     stream->deviceFormat[mode] = RTAUDIO_FLOAT32;\r
3940   }\r
3941   else if (format == RTAUDIO_FLOAT32)\r
3942     result = alSetSampFmt(al_config, AL_SAMPFMT_FLOAT);\r
3943   else if (format == RTAUDIO_FLOAT64)\r
3944     result = alSetSampFmt(al_config, AL_SAMPFMT_DOUBLE);\r
3945 \r
3946   if ( result == -1 ) {\r
3947     sprintf(message,"RtAudio: AL error setting sample format in AL config: %s.",\r
3948             alGetErrorString(oserror()));\r
3949     error(RtError::WARNING);\r
3950     return FAILURE;\r
3951   }\r
3952 \r
3953   if (mode == PLAYBACK) {\r
3954 \r
3955     // Set our device.\r
3956     if (device == 0)\r
3957       resource = AL_DEFAULT_OUTPUT;\r
3958     else\r
3959       resource = devices[device].id[0];\r
3960     result = alSetDevice(al_config, resource);\r
3961     if ( result == -1 ) {\r
3962       sprintf(message,"RtAudio: AL error setting device (%s) in AL config: %s.",\r
3963               devices[device].name, alGetErrorString(oserror()));\r
3964       error(RtError::WARNING);\r
3965       return FAILURE;\r
3966     }\r
3967 \r
3968     // Open the port.\r
3969     port = alOpenPort("RtAudio Output Port", "w", al_config);\r
3970     if( !port ) {\r
3971       sprintf(message,"RtAudio: AL error opening output port: %s.",\r
3972               alGetErrorString(oserror()));\r
3973       error(RtError::WARNING);\r
3974       return FAILURE;\r
3975     }\r
3976 \r
3977     // Set the sample rate\r
3978     pvs[0].param = AL_MASTER_CLOCK;\r
3979     pvs[0].value.i = AL_CRYSTAL_MCLK_TYPE;\r
3980     pvs[1].param = AL_RATE;\r
3981     pvs[1].value.ll = alDoubleToFixed((double)sampleRate);\r
3982     result = alSetParams(resource, pvs, 2);\r
3983     if ( result < 0 ) {\r
3984       alClosePort(port);\r
3985       sprintf(message,"RtAudio: AL error setting sample rate (%d) for device (%s): %s.",\r
3986               sampleRate, devices[device].name, alGetErrorString(oserror()));\r
3987       error(RtError::WARNING);\r
3988       return FAILURE;\r
3989     }\r
3990   }\r
3991   else { // mode == RECORD\r
3992 \r
3993     // Set our device.\r
3994     if (device == 0)\r
3995       resource = AL_DEFAULT_INPUT;\r
3996     else\r
3997       resource = devices[device].id[1];\r
3998     result = alSetDevice(al_config, resource);\r
3999     if ( result == -1 ) {\r
4000       sprintf(message,"RtAudio: AL error setting device (%s) in AL config: %s.",\r
4001               devices[device].name, alGetErrorString(oserror()));\r
4002       error(RtError::WARNING);\r
4003       return FAILURE;\r
4004     }\r
4005 \r
4006     // Open the port.\r
4007     port = alOpenPort("RtAudio Output Port", "r", al_config);\r
4008     if( !port ) {\r
4009       sprintf(message,"RtAudio: AL error opening input port: %s.",\r
4010               alGetErrorString(oserror()));\r
4011       error(RtError::WARNING);\r
4012       return FAILURE;\r
4013     }\r
4014 \r
4015     // Set the sample rate\r
4016     pvs[0].param = AL_MASTER_CLOCK;\r
4017     pvs[0].value.i = AL_CRYSTAL_MCLK_TYPE;\r
4018     pvs[1].param = AL_RATE;\r
4019     pvs[1].value.ll = alDoubleToFixed((double)sampleRate);\r
4020     result = alSetParams(resource, pvs, 2);\r
4021     if ( result < 0 ) {\r
4022       alClosePort(port);\r
4023       sprintf(message,"RtAudio: AL error setting sample rate (%d) for device (%s): %s.",\r
4024               sampleRate, devices[device].name, alGetErrorString(oserror()));\r
4025       error(RtError::WARNING);\r
4026       return FAILURE;\r
4027     }\r
4028   }\r
4029 \r
4030   alFreeConfig(al_config);\r
4031 \r
4032   stream->nUserChannels[mode] = channels;\r
4033   stream->nDeviceChannels[mode] = channels;\r
4034 \r
4035   // Set handle and flags for buffer conversion\r
4036   stream->handle[mode] = port;\r
4037   stream->doConvertBuffer[mode] = false;\r
4038   if (stream->userFormat != stream->deviceFormat[mode])\r
4039     stream->doConvertBuffer[mode] = true;\r
4040 \r
4041   // Allocate necessary internal buffers\r
4042   if ( stream->nUserChannels[0] != stream->nUserChannels[1] ) {\r
4043 \r
4044     long buffer_bytes;\r
4045     if (stream->nUserChannels[0] >= stream->nUserChannels[1])\r
4046       buffer_bytes = stream->nUserChannels[0];\r
4047     else\r
4048       buffer_bytes = stream->nUserChannels[1];\r
4049 \r
4050     buffer_bytes *= *bufferSize * formatBytes(stream->userFormat);\r
4051     if (stream->userBuffer) free(stream->userBuffer);\r
4052     stream->userBuffer = (char *) calloc(buffer_bytes, 1);\r
4053     if (stream->userBuffer == NULL)\r
4054       goto memory_error;\r
4055   }\r
4056 \r
4057   if ( stream->doConvertBuffer[mode] ) {\r
4058 \r
4059     long buffer_bytes;\r
4060     bool makeBuffer = true;\r
4061     if ( mode == PLAYBACK )\r
4062       buffer_bytes = stream->nDeviceChannels[0] * formatBytes(stream->deviceFormat[0]);\r
4063     else { // mode == RECORD\r
4064       buffer_bytes = stream->nDeviceChannels[1] * formatBytes(stream->deviceFormat[1]);\r
4065       if ( stream->mode == PLAYBACK ) {\r
4066         long bytes_out = stream->nDeviceChannels[0] * formatBytes(stream->deviceFormat[0]);\r
4067         if ( buffer_bytes > bytes_out )\r
4068           buffer_bytes = (buffer_bytes > bytes_out) ? buffer_bytes : bytes_out;\r
4069         else\r
4070           makeBuffer = false;\r
4071       }\r
4072     }\r
4073 \r
4074     if ( makeBuffer ) {\r
4075       buffer_bytes *= *bufferSize;\r
4076       if (stream->deviceBuffer) free(stream->deviceBuffer);\r
4077       stream->deviceBuffer = (char *) calloc(buffer_bytes, 1);\r
4078       if (stream->deviceBuffer == NULL)\r
4079         goto memory_error;\r
4080     }\r
4081   }\r
4082 \r
4083   stream->device[mode] = device;\r
4084   stream->state = STREAM_STOPPED;\r
4085   if ( stream->mode == PLAYBACK && mode == RECORD )\r
4086     // We had already set up an output stream.\r
4087     stream->mode = DUPLEX;\r
4088   else\r
4089     stream->mode = mode;\r
4090   stream->nBuffers = nBuffers;\r
4091   stream->bufferSize = *bufferSize;\r
4092   stream->sampleRate = sampleRate;\r
4093 \r
4094   return SUCCESS;\r
4095 \r
4096  memory_error:\r
4097   if (stream->handle[0]) {\r
4098     alClosePort(stream->handle[0]);\r
4099     stream->handle[0] = 0;\r
4100   }\r
4101   if (stream->handle[1]) {\r
4102     alClosePort(stream->handle[1]);\r
4103     stream->handle[1] = 0;\r
4104   }\r
4105   if (stream->userBuffer) {\r
4106     free(stream->userBuffer);\r
4107     stream->userBuffer = 0;\r
4108   }\r
4109   sprintf(message, "RtAudio: ALSA error allocating buffer memory for device (%s).",\r
4110           devices[device].name);\r
4111   error(RtError::WARNING);\r
4112   return FAILURE;\r
4113 }\r
4114 \r
4115 void RtAudio :: cancelStreamCallback(int streamId)\r
4116 {\r
4117   RTAUDIO_STREAM *stream = (RTAUDIO_STREAM *) verifyStream(streamId);\r
4118 \r
4119   if (stream->usingCallback) {\r
4120     stream->usingCallback = false;\r
4121     pthread_cancel(stream->thread);\r
4122     pthread_join(stream->thread, NULL);\r
4123     stream->thread = 0;\r
4124     stream->callback = NULL;\r
4125     stream->userData = NULL;\r
4126   }\r
4127 }\r
4128 \r
4129 void RtAudio :: closeStream(int streamId)\r
4130 {\r
4131   // We don't want an exception to be thrown here because this\r
4132   // function is called by our class destructor.  So, do our own\r
4133   // streamId check.\r
4134   if ( streams.find( streamId ) == streams.end() ) {\r
4135     sprintf(message, "RtAudio: invalid stream identifier!");\r
4136     error(RtError::WARNING);\r
4137     return;\r
4138   }\r
4139 \r
4140   RTAUDIO_STREAM *stream = (RTAUDIO_STREAM *) streams[streamId];\r
4141 \r
4142   if (stream->usingCallback) {\r
4143     pthread_cancel(stream->thread);\r
4144     pthread_join(stream->thread, NULL);\r
4145   }\r
4146 \r
4147   pthread_mutex_destroy(&stream->mutex);\r
4148 \r
4149   if (stream->handle[0])\r
4150     alClosePort(stream->handle[0]);\r
4151 \r
4152   if (stream->handle[1])\r
4153     alClosePort(stream->handle[1]);\r
4154 \r
4155   if (stream->userBuffer)\r
4156     free(stream->userBuffer);\r
4157 \r
4158   if (stream->deviceBuffer)\r
4159     free(stream->deviceBuffer);\r
4160 \r
4161   free(stream);\r
4162   streams.erase(streamId);\r
4163 }\r
4164 \r
4165 void RtAudio :: startStream(int streamId)\r
4166 {\r
4167   RTAUDIO_STREAM *stream = (RTAUDIO_STREAM *) verifyStream(streamId);\r
4168 \r
4169   if (stream->state == STREAM_RUNNING)\r
4170     return;\r
4171 \r
4172   // The AL port is ready as soon as it is opened.\r
4173   stream->state = STREAM_RUNNING;\r
4174 }\r
4175 \r
4176 void RtAudio :: stopStream(int streamId)\r
4177 {\r
4178   RTAUDIO_STREAM *stream = (RTAUDIO_STREAM *) verifyStream(streamId);\r
4179 \r
4180   MUTEX_LOCK(&stream->mutex);\r
4181 \r
4182   if (stream->state == STREAM_STOPPED)\r
4183     goto unlock;\r
4184 \r
4185   int result;\r
4186   int buffer_size = stream->bufferSize * stream->nBuffers;\r
4187 \r
4188   if (stream->mode == PLAYBACK || stream->mode == DUPLEX)\r
4189     alZeroFrames(stream->handle[0], buffer_size);\r
4190 \r
4191   if (stream->mode == RECORD || stream->mode == DUPLEX) {\r
4192     result = alDiscardFrames(stream->handle[1], buffer_size);\r
4193     if (result == -1) {\r
4194       sprintf(message, "RtAudio: AL error draining stream device (%s): %s.",\r
4195               devices[stream->device[1]].name, alGetErrorString(oserror()));\r
4196       error(RtError::DRIVER_ERROR);\r
4197     }\r
4198   }\r
4199   stream->state = STREAM_STOPPED;\r
4200 \r
4201  unlock:\r
4202   MUTEX_UNLOCK(&stream->mutex);\r
4203 }\r
4204 \r
4205 void RtAudio :: abortStream(int streamId)\r
4206 {\r
4207   RTAUDIO_STREAM *stream = (RTAUDIO_STREAM *) verifyStream(streamId);\r
4208 \r
4209   MUTEX_LOCK(&stream->mutex);\r
4210 \r
4211   if (stream->state == STREAM_STOPPED)\r
4212     goto unlock;\r
4213 \r
4214   if (stream->mode == PLAYBACK || stream->mode == DUPLEX) {\r
4215 \r
4216     int buffer_size = stream->bufferSize * stream->nBuffers;\r
4217     int result = alDiscardFrames(stream->handle[0], buffer_size);\r
4218     if (result == -1) {\r
4219       sprintf(message, "RtAudio: AL error aborting stream device (%s): %s.",\r
4220               devices[stream->device[0]].name, alGetErrorString(oserror()));\r
4221       error(RtError::DRIVER_ERROR);\r
4222     }\r
4223   }\r
4224 \r
4225   // There is no clear action to take on the input stream, since the\r
4226   // port will continue to run in any event.\r
4227   stream->state = STREAM_STOPPED;\r
4228 \r
4229  unlock:\r
4230   MUTEX_UNLOCK(&stream->mutex);\r
4231 }\r
4232 \r
4233 int RtAudio :: streamWillBlock(int streamId)\r
4234 {\r
4235   RTAUDIO_STREAM *stream = (RTAUDIO_STREAM *) verifyStream(streamId);\r
4236 \r
4237   MUTEX_LOCK(&stream->mutex);\r
4238 \r
4239   int frames = 0;\r
4240   if (stream->state == STREAM_STOPPED)\r
4241     goto unlock;\r
4242 \r
4243   int err = 0;\r
4244   if (stream->mode == PLAYBACK || stream->mode == DUPLEX) {\r
4245     err = alGetFillable(stream->handle[0]);\r
4246     if (err < 0) {\r
4247       sprintf(message, "RtAudio: AL error getting available frames for stream (%s): %s.",\r
4248               devices[stream->device[0]].name, alGetErrorString(oserror()));\r
4249       error(RtError::DRIVER_ERROR);\r
4250     }\r
4251   }\r
4252 \r
4253   frames = err;\r
4254 \r
4255   if (stream->mode == RECORD || stream->mode == DUPLEX) {\r
4256     err = alGetFilled(stream->handle[1]);\r
4257     if (err < 0) {\r
4258       sprintf(message, "RtAudio: AL error getting available frames for stream (%s): %s.",\r
4259               devices[stream->device[1]].name, alGetErrorString(oserror()));\r
4260       error(RtError::DRIVER_ERROR);\r
4261     }\r
4262     if (frames > err) frames = err;\r
4263   }\r
4264 \r
4265   frames = stream->bufferSize - frames;\r
4266   if (frames < 0) frames = 0;\r
4267 \r
4268  unlock:\r
4269   MUTEX_UNLOCK(&stream->mutex);\r
4270   return frames;\r
4271 }\r
4272 \r
4273 void RtAudio :: tickStream(int streamId)\r
4274 {\r
4275   RTAUDIO_STREAM *stream = (RTAUDIO_STREAM *) verifyStream(streamId);\r
4276 \r
4277   int stopStream = 0;\r
4278   if (stream->state == STREAM_STOPPED) {\r
4279     if (stream->usingCallback) usleep(50000); // sleep 50 milliseconds\r
4280     return;\r
4281   }\r
4282   else if (stream->usingCallback) {\r
4283     stopStream = stream->callback(stream->userBuffer, stream->bufferSize, stream->userData);    \r
4284   }\r
4285 \r
4286   MUTEX_LOCK(&stream->mutex);\r
4287 \r
4288   // The state might change while waiting on a mutex.\r
4289   if (stream->state == STREAM_STOPPED)\r
4290     goto unlock;\r
4291 \r
4292   char *buffer;\r
4293   int channels;\r
4294   RTAUDIO_FORMAT format;\r
4295   if (stream->mode == PLAYBACK || stream->mode == DUPLEX) {\r
4296 \r
4297     // Setup parameters and do buffer conversion if necessary.\r
4298     if (stream->doConvertBuffer[0]) {\r
4299       convertStreamBuffer(stream, PLAYBACK);\r
4300       buffer = stream->deviceBuffer;\r
4301       channels = stream->nDeviceChannels[0];\r
4302       format = stream->deviceFormat[0];\r
4303     }\r
4304     else {\r
4305       buffer = stream->userBuffer;\r
4306       channels = stream->nUserChannels[0];\r
4307       format = stream->userFormat;\r
4308     }\r
4309 \r
4310     // Do byte swapping if necessary.\r
4311     if (stream->doByteSwap[0])\r
4312       byteSwapBuffer(buffer, stream->bufferSize * channels, format);\r
4313 \r
4314     // Write interleaved samples to device.\r
4315     alWriteFrames(stream->handle[0], buffer, stream->bufferSize);\r
4316   }\r
4317 \r
4318   if (stream->mode == RECORD || stream->mode == DUPLEX) {\r
4319 \r
4320     // Setup parameters.\r
4321     if (stream->doConvertBuffer[1]) {\r
4322       buffer = stream->deviceBuffer;\r
4323       channels = stream->nDeviceChannels[1];\r
4324       format = stream->deviceFormat[1];\r
4325     }\r
4326     else {\r
4327       buffer = stream->userBuffer;\r
4328       channels = stream->nUserChannels[1];\r
4329       format = stream->userFormat;\r
4330     }\r
4331 \r
4332     // Read interleaved samples from device.\r
4333     alReadFrames(stream->handle[1], buffer, stream->bufferSize);\r
4334 \r
4335     // Do byte swapping if necessary.\r
4336     if (stream->doByteSwap[1])\r
4337       byteSwapBuffer(buffer, stream->bufferSize * channels, format);\r
4338 \r
4339     // Do buffer conversion if necessary.\r
4340     if (stream->doConvertBuffer[1])\r
4341       convertStreamBuffer(stream, RECORD);\r
4342   }\r
4343 \r
4344  unlock:\r
4345   MUTEX_UNLOCK(&stream->mutex);\r
4346 \r
4347   if (stream->usingCallback && stopStream)\r
4348     this->stopStream(streamId);\r
4349 }\r
4350 \r
4351 extern "C" void *callbackHandler(void *ptr)\r
4352 {\r
4353   RtAudio *object = thread_info.object;\r
4354   int stream = thread_info.streamId;\r
4355   bool *usingCallback = (bool *) ptr;\r
4356 \r
4357   while ( *usingCallback ) {\r
4358     pthread_testcancel();\r
4359     try {\r
4360       object->tickStream(stream);\r
4361     }\r
4362     catch (RtError &exception) {\r
4363       fprintf(stderr, "\nCallback thread error (%s) ... closing thread.\n\n",\r
4364               exception.getMessage());\r
4365       break;\r
4366     }\r
4367   }\r
4368 \r
4369   return 0;\r
4370 }\r
4371 \r
4372 //******************** End of __IRIX_AL__ *********************//\r
4373 \r
4374 #endif\r
4375 \r
4376 \r
4377 // *************************************************** //\r
4378 //\r
4379 // Private common (OS-independent) RtAudio methods.\r
4380 //\r
4381 // *************************************************** //\r
4382 \r
4383 // This method can be modified to control the behavior of error\r
4384 // message reporting and throwing.\r
4385 void RtAudio :: error(RtError::TYPE type)\r
4386 {\r
4387   if (type == RtError::WARNING) {\r
4388 #if defined(RTAUDIO_DEBUG)\r
4389     fprintf(stderr, "\n%s\n\n", message);\r
4390   else if (type == RtError::DEBUG_WARNING) {\r
4391     fprintf(stderr, "\n%s\n\n", message);\r
4392 #endif\r
4393   }\r
4394   else {\r
4395     fprintf(stderr, "\n%s\n\n", message);\r
4396     throw RtError(message, type);\r
4397   }\r
4398 }\r
4399 \r
4400 void *RtAudio :: verifyStream(int streamId)\r
4401 {\r
4402   // Verify the stream key.\r
4403   if ( streams.find( streamId ) == streams.end() ) {\r
4404     sprintf(message, "RtAudio: invalid stream identifier!");\r
4405     error(RtError::INVALID_STREAM);\r
4406   }\r
4407 \r
4408   return streams[streamId];\r
4409 }\r
4410 \r
4411 void RtAudio :: clearDeviceInfo(RTAUDIO_DEVICE *info)\r
4412 {\r
4413   // Don't clear the name or DEVICE_ID fields here ... they are\r
4414   // typically set prior to a call of this function.\r
4415   info->probed = false;\r
4416   info->maxOutputChannels = 0;\r
4417   info->maxInputChannels = 0;\r
4418   info->maxDuplexChannels = 0;\r
4419   info->minOutputChannels = 0;\r
4420   info->minInputChannels = 0;\r
4421   info->minDuplexChannels = 0;\r
4422   info->hasDuplexSupport = false;\r
4423   info->nSampleRates = 0;\r
4424   for (int i=0; i<MAX_SAMPLE_RATES; i++)\r
4425     info->sampleRates[i] = 0;\r
4426   info->nativeFormats = 0;\r
4427 }\r
4428 \r
4429 int RtAudio :: formatBytes(RTAUDIO_FORMAT format)\r
4430 {\r
4431   if (format == RTAUDIO_SINT16)\r
4432     return 2;\r
4433   else if (format == RTAUDIO_SINT24 || format == RTAUDIO_SINT32 ||\r
4434            format == RTAUDIO_FLOAT32)\r
4435     return 4;\r
4436   else if (format == RTAUDIO_FLOAT64)\r
4437     return 8;\r
4438   else if (format == RTAUDIO_SINT8)\r
4439     return 1;\r
4440 \r
4441   sprintf(message,"RtAudio: undefined format in formatBytes().");\r
4442   error(RtError::WARNING);\r
4443 \r
4444   return 0;\r
4445 }\r
4446 \r
4447 void RtAudio :: convertStreamBuffer(RTAUDIO_STREAM *stream, STREAM_MODE mode)\r
4448 {\r
4449   // This method does format conversion, input/output channel compensation, and\r
4450   // data interleaving/deinterleaving.  24-bit integers are assumed to occupy\r
4451   // the upper three bytes of a 32-bit integer.\r
4452 \r
4453   int j, channels_in, channels_out, channels;\r
4454   RTAUDIO_FORMAT format_in, format_out;\r
4455   char *input, *output;\r
4456 \r
4457   if (mode == RECORD) { // convert device to user buffer\r
4458     input = stream->deviceBuffer;\r
4459     output = stream->userBuffer;\r
4460     channels_in = stream->nDeviceChannels[1];\r
4461     channels_out = stream->nUserChannels[1];\r
4462     format_in = stream->deviceFormat[1];\r
4463     format_out = stream->userFormat;\r
4464   }\r
4465   else { // convert user to device buffer\r
4466     input = stream->userBuffer;\r
4467     output = stream->deviceBuffer;\r
4468     channels_in = stream->nUserChannels[0];\r
4469     channels_out = stream->nDeviceChannels[0];\r
4470     format_in = stream->userFormat;\r
4471     format_out = stream->deviceFormat[0];\r
4472 \r
4473     // clear our device buffer when in/out duplex device channels are different\r
4474     if ( stream->mode == DUPLEX &&\r
4475          stream->nDeviceChannels[0] != stream->nDeviceChannels[1] )\r
4476       memset(output, 0, stream->bufferSize * channels_out * formatBytes(format_out));\r
4477   }\r
4478 \r
4479   channels = (channels_in < channels_out) ? channels_in : channels_out;\r
4480 \r
4481   // Set up the interleave/deinterleave offsets\r
4482   std::vector<int> offset_in(channels);\r
4483   std::vector<int> offset_out(channels);\r
4484   if (mode == RECORD && stream->deInterleave[1]) {\r
4485     for (int k=0; k<channels; k++) {\r
4486       offset_in[k] = k * stream->bufferSize;\r
4487       offset_out[k] = k;\r
4488     }\r
4489   }\r
4490   else if (mode == PLAYBACK && stream->deInterleave[0]) {\r
4491     for (int k=0; k<channels; k++) {\r
4492       offset_in[k] = k;\r
4493       offset_out[k] = k * stream->bufferSize;\r
4494     }\r
4495   }\r
4496   else {\r
4497     for (int k=0; k<channels; k++) {\r
4498       offset_in[k] = k;\r
4499       offset_out[k] = k;\r
4500     }\r
4501   }\r
4502 \r
4503   if (format_out == RTAUDIO_FLOAT64) {\r
4504     FLOAT64 scale;\r
4505     FLOAT64 *out = (FLOAT64 *)output;\r
4506 \r
4507     if (format_in == RTAUDIO_SINT8) {\r
4508       signed char *in = (signed char *)input;\r
4509       scale = 1.0 / 128.0;\r
4510       for (int i=0; i<stream->bufferSize; i++) {\r
4511         for (j=0; j<channels; j++) {\r
4512           out[offset_out[j]] = (FLOAT64) in[offset_in[j]];\r
4513           out[offset_out[j]] *= scale;\r
4514         }\r
4515         in += channels_in;\r
4516         out += channels_out;\r
4517       }\r
4518     }\r
4519     else if (format_in == RTAUDIO_SINT16) {\r
4520       INT16 *in = (INT16 *)input;\r
4521       scale = 1.0 / 32768.0;\r
4522       for (int i=0; i<stream->bufferSize; i++) {\r
4523         for (j=0; j<channels; j++) {\r
4524           out[offset_out[j]] = (FLOAT64) in[offset_in[j]];\r
4525           out[offset_out[j]] *= scale;\r
4526         }\r
4527         in += channels_in;\r
4528         out += channels_out;\r
4529       }\r
4530     }\r
4531     else if (format_in == RTAUDIO_SINT24) {\r
4532       INT32 *in = (INT32 *)input;\r
4533       scale = 1.0 / 2147483648.0;\r
4534       for (int i=0; i<stream->bufferSize; i++) {\r
4535         for (j=0; j<channels; j++) {\r
4536           out[offset_out[j]] = (FLOAT64) (in[offset_in[j]] & 0xffffff00);\r
4537           out[offset_out[j]] *= scale;\r
4538         }\r
4539         in += channels_in;\r
4540         out += channels_out;\r
4541       }\r
4542     }\r
4543     else if (format_in == RTAUDIO_SINT32) {\r
4544       INT32 *in = (INT32 *)input;\r
4545       scale = 1.0 / 2147483648.0;\r
4546       for (int i=0; i<stream->bufferSize; i++) {\r
4547         for (j=0; j<channels; j++) {\r
4548           out[offset_out[j]] = (FLOAT64) in[offset_in[j]];\r
4549           out[offset_out[j]] *= scale;\r
4550         }\r
4551         in += channels_in;\r
4552         out += channels_out;\r
4553       }\r
4554     }\r
4555     else if (format_in == RTAUDIO_FLOAT32) {\r
4556       FLOAT32 *in = (FLOAT32 *)input;\r
4557       for (int i=0; i<stream->bufferSize; i++) {\r
4558         for (j=0; j<channels; j++) {\r
4559           out[offset_out[j]] = (FLOAT64) in[offset_in[j]];\r
4560         }\r
4561         in += channels_in;\r
4562         out += channels_out;\r
4563       }\r
4564     }\r
4565     else if (format_in == RTAUDIO_FLOAT64) {\r
4566       // Channel compensation and/or (de)interleaving only.\r
4567       FLOAT64 *in = (FLOAT64 *)input;\r
4568       for (int i=0; i<stream->bufferSize; i++) {\r
4569         for (j=0; j<channels; j++) {\r
4570           out[offset_out[j]] = in[offset_in[j]];\r
4571         }\r
4572         in += channels_in;\r
4573         out += channels_out;\r
4574       }\r
4575     }\r
4576   }\r
4577   else if (format_out == RTAUDIO_FLOAT32) {\r
4578     FLOAT32 scale;\r
4579     FLOAT32 *out = (FLOAT32 *)output;\r
4580 \r
4581     if (format_in == RTAUDIO_SINT8) {\r
4582       signed char *in = (signed char *)input;\r
4583       scale = 1.0 / 128.0;\r
4584       for (int i=0; i<stream->bufferSize; i++) {\r
4585         for (j=0; j<channels; j++) {\r
4586           out[offset_out[j]] = (FLOAT32) in[offset_in[j]];\r
4587           out[offset_out[j]] *= scale;\r
4588         }\r
4589         in += channels_in;\r
4590         out += channels_out;\r
4591       }\r
4592     }\r
4593     else if (format_in == RTAUDIO_SINT16) {\r
4594       INT16 *in = (INT16 *)input;\r
4595       scale = 1.0 / 32768.0;\r
4596       for (int i=0; i<stream->bufferSize; i++) {\r
4597         for (j=0; j<channels; j++) {\r
4598           out[offset_out[j]] = (FLOAT32) in[offset_in[j]];\r
4599           out[offset_out[j]] *= scale;\r
4600         }\r
4601         in += channels_in;\r
4602         out += channels_out;\r
4603       }\r
4604     }\r
4605     else if (format_in == RTAUDIO_SINT24) {\r
4606       INT32 *in = (INT32 *)input;\r
4607       scale = 1.0 / 2147483648.0;\r
4608       for (int i=0; i<stream->bufferSize; i++) {\r
4609         for (j=0; j<channels; j++) {\r
4610           out[offset_out[j]] = (FLOAT32) (in[offset_in[j]] & 0xffffff00);\r
4611           out[offset_out[j]] *= scale;\r
4612         }\r
4613         in += channels_in;\r
4614         out += channels_out;\r
4615       }\r
4616     }\r
4617     else if (format_in == RTAUDIO_SINT32) {\r
4618       INT32 *in = (INT32 *)input;\r
4619       scale = 1.0 / 2147483648.0;\r
4620       for (int i=0; i<stream->bufferSize; i++) {\r
4621         for (j=0; j<channels; j++) {\r
4622           out[offset_out[j]] = (FLOAT32) in[offset_in[j]];\r
4623           out[offset_out[j]] *= scale;\r
4624         }\r
4625         in += channels_in;\r
4626         out += channels_out;\r
4627       }\r
4628     }\r
4629     else if (format_in == RTAUDIO_FLOAT32) {\r
4630       // Channel compensation and/or (de)interleaving only.\r
4631       FLOAT32 *in = (FLOAT32 *)input;\r
4632       for (int i=0; i<stream->bufferSize; i++) {\r
4633         for (j=0; j<channels; j++) {\r
4634           out[offset_out[j]] = in[offset_in[j]];\r
4635         }\r
4636         in += channels_in;\r
4637         out += channels_out;\r
4638       }\r
4639     }\r
4640     else if (format_in == RTAUDIO_FLOAT64) {\r
4641       FLOAT64 *in = (FLOAT64 *)input;\r
4642       for (int i=0; i<stream->bufferSize; i++) {\r
4643         for (j=0; j<channels; j++) {\r
4644           out[offset_out[j]] = (FLOAT32) in[offset_in[j]];\r
4645         }\r
4646         in += channels_in;\r
4647         out += channels_out;\r
4648       }\r
4649     }\r
4650   }\r
4651   else if (format_out == RTAUDIO_SINT32) {\r
4652     INT32 *out = (INT32 *)output;\r
4653     if (format_in == RTAUDIO_SINT8) {\r
4654       signed char *in = (signed char *)input;\r
4655       for (int i=0; i<stream->bufferSize; i++) {\r
4656         for (j=0; j<channels; j++) {\r
4657           out[offset_out[j]] = (INT32) in[offset_in[j]];\r
4658           out[offset_out[j]] <<= 24;\r
4659         }\r
4660         in += channels_in;\r
4661         out += channels_out;\r
4662       }\r
4663     }\r
4664     else if (format_in == RTAUDIO_SINT16) {\r
4665       INT16 *in = (INT16 *)input;\r
4666       for (int i=0; i<stream->bufferSize; i++) {\r
4667         for (j=0; j<channels; j++) {\r
4668           out[offset_out[j]] = (INT32) in[offset_in[j]];\r
4669           out[offset_out[j]] <<= 16;\r
4670         }\r
4671         in += channels_in;\r
4672         out += channels_out;\r
4673       }\r
4674     }\r
4675     else if (format_in == RTAUDIO_SINT24) {\r
4676       INT32 *in = (INT32 *)input;\r
4677       for (int i=0; i<stream->bufferSize; i++) {\r
4678         for (j=0; j<channels; j++) {\r
4679           out[offset_out[j]] = (INT32) in[offset_in[j]];\r
4680         }\r
4681         in += channels_in;\r
4682         out += channels_out;\r
4683       }\r
4684     }\r
4685     else if (format_in == RTAUDIO_SINT32) {\r
4686       // Channel compensation and/or (de)interleaving only.\r
4687       INT32 *in = (INT32 *)input;\r
4688       for (int i=0; i<stream->bufferSize; i++) {\r
4689         for (j=0; j<channels; j++) {\r
4690           out[offset_out[j]] = in[offset_in[j]];\r
4691         }\r
4692         in += channels_in;\r
4693         out += channels_out;\r
4694       }\r
4695     }\r
4696     else if (format_in == RTAUDIO_FLOAT32) {\r
4697       FLOAT32 *in = (FLOAT32 *)input;\r
4698       for (int i=0; i<stream->bufferSize; i++) {\r
4699         for (j=0; j<channels; j++) {\r
4700           out[offset_out[j]] = (INT32) (in[offset_in[j]] * 2147483647.0);\r
4701         }\r
4702         in += channels_in;\r
4703         out += channels_out;\r
4704       }\r
4705     }\r
4706     else if (format_in == RTAUDIO_FLOAT64) {\r
4707       FLOAT64 *in = (FLOAT64 *)input;\r
4708       for (int i=0; i<stream->bufferSize; i++) {\r
4709         for (j=0; j<channels; j++) {\r
4710           out[offset_out[j]] = (INT32) (in[offset_in[j]] * 2147483647.0);\r
4711         }\r
4712         in += channels_in;\r
4713         out += channels_out;\r
4714       }\r
4715     }\r
4716   }\r
4717   else if (format_out == RTAUDIO_SINT24) {\r
4718     INT32 *out = (INT32 *)output;\r
4719     if (format_in == RTAUDIO_SINT8) {\r
4720       signed char *in = (signed char *)input;\r
4721       for (int i=0; i<stream->bufferSize; i++) {\r
4722         for (j=0; j<channels; j++) {\r
4723           out[offset_out[j]] = (INT32) in[offset_in[j]];\r
4724           out[offset_out[j]] <<= 24;\r
4725         }\r
4726         in += channels_in;\r
4727         out += channels_out;\r
4728       }\r
4729     }\r
4730     else if (format_in == RTAUDIO_SINT16) {\r
4731       INT16 *in = (INT16 *)input;\r
4732       for (int i=0; i<stream->bufferSize; i++) {\r
4733         for (j=0; j<channels; j++) {\r
4734           out[offset_out[j]] = (INT32) in[offset_in[j]];\r
4735           out[offset_out[j]] <<= 16;\r
4736         }\r
4737         in += channels_in;\r
4738         out += channels_out;\r
4739       }\r
4740     }\r
4741     else if (format_in == RTAUDIO_SINT24) {\r
4742       // Channel compensation and/or (de)interleaving only.\r
4743       INT32 *in = (INT32 *)input;\r
4744       for (int i=0; i<stream->bufferSize; i++) {\r
4745         for (j=0; j<channels; j++) {\r
4746           out[offset_out[j]] = in[offset_in[j]];\r
4747         }\r
4748         in += channels_in;\r
4749         out += channels_out;\r
4750       }\r
4751     }\r
4752     else if (format_in == RTAUDIO_SINT32) {\r
4753       INT32 *in = (INT32 *)input;\r
4754       for (int i=0; i<stream->bufferSize; i++) {\r
4755         for (j=0; j<channels; j++) {\r
4756           out[offset_out[j]] = (INT32) (in[offset_in[j]] & 0xffffff00);\r
4757         }\r
4758         in += channels_in;\r
4759         out += channels_out;\r
4760       }\r
4761     }\r
4762     else if (format_in == RTAUDIO_FLOAT32) {\r
4763       FLOAT32 *in = (FLOAT32 *)input;\r
4764       for (int i=0; i<stream->bufferSize; i++) {\r
4765         for (j=0; j<channels; j++) {\r
4766           out[offset_out[j]] = (INT32) (in[offset_in[j]] * 2147483647.0);\r
4767         }\r
4768         in += channels_in;\r
4769         out += channels_out;\r
4770       }\r
4771     }\r
4772     else if (format_in == RTAUDIO_FLOAT64) {\r
4773       FLOAT64 *in = (FLOAT64 *)input;\r
4774       for (int i=0; i<stream->bufferSize; i++) {\r
4775         for (j=0; j<channels; j++) {\r
4776           out[offset_out[j]] = (INT32) (in[offset_in[j]] * 2147483647.0);\r
4777         }\r
4778         in += channels_in;\r
4779         out += channels_out;\r
4780       }\r
4781     }\r
4782   }\r
4783   else if (format_out == RTAUDIO_SINT16) {\r
4784     INT16 *out = (INT16 *)output;\r
4785     if (format_in == RTAUDIO_SINT8) {\r
4786       signed char *in = (signed char *)input;\r
4787       for (int i=0; i<stream->bufferSize; i++) {\r
4788         for (j=0; j<channels; j++) {\r
4789           out[offset_out[j]] = (INT16) in[offset_in[j]];\r
4790           out[offset_out[j]] <<= 8;\r
4791         }\r
4792         in += channels_in;\r
4793         out += channels_out;\r
4794       }\r
4795     }\r
4796     else if (format_in == RTAUDIO_SINT16) {\r
4797       // Channel compensation and/or (de)interleaving only.\r
4798       INT16 *in = (INT16 *)input;\r
4799       for (int i=0; i<stream->bufferSize; i++) {\r
4800         for (j=0; j<channels; j++) {\r
4801           out[offset_out[j]] = in[offset_in[j]];\r
4802         }\r
4803         in += channels_in;\r
4804         out += channels_out;\r
4805       }\r
4806     }\r
4807     else if (format_in == RTAUDIO_SINT24) {\r
4808       INT32 *in = (INT32 *)input;\r
4809       for (int i=0; i<stream->bufferSize; i++) {\r
4810         for (j=0; j<channels; j++) {\r
4811           out[offset_out[j]] = (INT16) ((in[offset_in[j]] >> 16) & 0x0000ffff);\r
4812         }\r
4813         in += channels_in;\r
4814         out += channels_out;\r
4815       }\r
4816     }\r
4817     else if (format_in == RTAUDIO_SINT32) {\r
4818       INT32 *in = (INT32 *)input;\r
4819       for (int i=0; i<stream->bufferSize; i++) {\r
4820         for (j=0; j<channels; j++) {\r
4821           out[offset_out[j]] = (INT16) ((in[offset_in[j]] >> 16) & 0x0000ffff);\r
4822         }\r
4823         in += channels_in;\r
4824         out += channels_out;\r
4825       }\r
4826     }\r
4827     else if (format_in == RTAUDIO_FLOAT32) {\r
4828       FLOAT32 *in = (FLOAT32 *)input;\r
4829       for (int i=0; i<stream->bufferSize; i++) {\r
4830         for (j=0; j<channels; j++) {\r
4831           out[offset_out[j]] = (INT16) (in[offset_in[j]] * 32767.0);\r
4832         }\r
4833         in += channels_in;\r
4834         out += channels_out;\r
4835       }\r
4836     }\r
4837     else if (format_in == RTAUDIO_FLOAT64) {\r
4838       FLOAT64 *in = (FLOAT64 *)input;\r
4839       for (int i=0; i<stream->bufferSize; i++) {\r
4840         for (j=0; j<channels; j++) {\r
4841           out[offset_out[j]] = (INT16) (in[offset_in[j]] * 32767.0);\r
4842         }\r
4843         in += channels_in;\r
4844         out += channels_out;\r
4845       }\r
4846     }\r
4847   }\r
4848   else if (format_out == RTAUDIO_SINT8) {\r
4849     signed char *out = (signed char *)output;\r
4850     if (format_in == RTAUDIO_SINT8) {\r
4851       // Channel compensation and/or (de)interleaving only.\r
4852       signed char *in = (signed char *)input;\r
4853       for (int i=0; i<stream->bufferSize; i++) {\r
4854         for (j=0; j<channels; j++) {\r
4855           out[offset_out[j]] = in[offset_in[j]];\r
4856         }\r
4857         in += channels_in;\r
4858         out += channels_out;\r
4859       }\r
4860     }\r
4861     if (format_in == RTAUDIO_SINT16) {\r
4862       INT16 *in = (INT16 *)input;\r
4863       for (int i=0; i<stream->bufferSize; i++) {\r
4864         for (j=0; j<channels; j++) {\r
4865           out[offset_out[j]] = (signed char) ((in[offset_in[j]] >> 8) & 0x00ff);\r
4866         }\r
4867         in += channels_in;\r
4868         out += channels_out;\r
4869       }\r
4870     }\r
4871     else if (format_in == RTAUDIO_SINT24) {\r
4872       INT32 *in = (INT32 *)input;\r
4873       for (int i=0; i<stream->bufferSize; i++) {\r
4874         for (j=0; j<channels; j++) {\r
4875           out[offset_out[j]] = (signed char) ((in[offset_in[j]] >> 24) & 0x000000ff);\r
4876         }\r
4877         in += channels_in;\r
4878         out += channels_out;\r
4879       }\r
4880     }\r
4881     else if (format_in == RTAUDIO_SINT32) {\r
4882       INT32 *in = (INT32 *)input;\r
4883       for (int i=0; i<stream->bufferSize; i++) {\r
4884         for (j=0; j<channels; j++) {\r
4885           out[offset_out[j]] = (signed char) ((in[offset_in[j]] >> 24) & 0x000000ff);\r
4886         }\r
4887         in += channels_in;\r
4888         out += channels_out;\r
4889       }\r
4890     }\r
4891     else if (format_in == RTAUDIO_FLOAT32) {\r
4892       FLOAT32 *in = (FLOAT32 *)input;\r
4893       for (int i=0; i<stream->bufferSize; i++) {\r
4894         for (j=0; j<channels; j++) {\r
4895           out[offset_out[j]] = (signed char) (in[offset_in[j]] * 127.0);\r
4896         }\r
4897         in += channels_in;\r
4898         out += channels_out;\r
4899       }\r
4900     }\r
4901     else if (format_in == RTAUDIO_FLOAT64) {\r
4902       FLOAT64 *in = (FLOAT64 *)input;\r
4903       for (int i=0; i<stream->bufferSize; i++) {\r
4904         for (j=0; j<channels; j++) {\r
4905           out[offset_out[j]] = (signed char) (in[offset_in[j]] * 127.0);\r
4906         }\r
4907         in += channels_in;\r
4908         out += channels_out;\r
4909       }\r
4910     }\r
4911   }\r
4912 }\r
4913 \r
4914 void RtAudio :: byteSwapBuffer(char *buffer, int samples, RTAUDIO_FORMAT format)\r
4915 {\r
4916   register char val;\r
4917   register char *ptr;\r
4918 \r
4919   ptr = buffer;\r
4920   if (format == RTAUDIO_SINT16) {\r
4921     for (int i=0; i<samples; i++) {\r
4922       // Swap 1st and 2nd bytes.\r
4923       val = *(ptr);\r
4924       *(ptr) = *(ptr+1);\r
4925       *(ptr+1) = val;\r
4926 \r
4927       // Increment 2 bytes.\r
4928       ptr += 2;\r
4929     }\r
4930   }\r
4931   else if (format == RTAUDIO_SINT24 ||\r
4932            format == RTAUDIO_SINT32 ||\r
4933            format == RTAUDIO_FLOAT32) {\r
4934     for (int i=0; i<samples; i++) {\r
4935       // Swap 1st and 4th bytes.\r
4936       val = *(ptr);\r
4937       *(ptr) = *(ptr+3);\r
4938       *(ptr+3) = val;\r
4939 \r
4940       // Swap 2nd and 3rd bytes.\r
4941       ptr += 1;\r
4942       val = *(ptr);\r
4943       *(ptr) = *(ptr+1);\r
4944       *(ptr+1) = val;\r
4945 \r
4946       // Increment 4 bytes.\r
4947       ptr += 4;\r
4948     }\r
4949   }\r
4950   else if (format == RTAUDIO_FLOAT64) {\r
4951     for (int i=0; i<samples; i++) {\r
4952       // Swap 1st and 8th bytes\r
4953       val = *(ptr);\r
4954       *(ptr) = *(ptr+7);\r
4955       *(ptr+7) = val;\r
4956 \r
4957       // Swap 2nd and 7th bytes\r
4958       ptr += 1;\r
4959       val = *(ptr);\r
4960       *(ptr) = *(ptr+5);\r
4961       *(ptr+5) = val;\r
4962 \r
4963       // Swap 3rd and 6th bytes\r
4964       ptr += 1;\r
4965       val = *(ptr);\r
4966       *(ptr) = *(ptr+3);\r
4967       *(ptr+3) = val;\r
4968 \r
4969       // Swap 4th and 5th bytes\r
4970       ptr += 1;\r
4971       val = *(ptr);\r
4972       *(ptr) = *(ptr+1);\r
4973       *(ptr+1) = val;\r
4974 \r
4975       // Increment 8 bytes.\r
4976       ptr += 8;\r
4977     }\r
4978   }\r
4979 }\r
4980 \r
4981 \r
4982 // *************************************************** //\r
4983 //\r
4984 // RtError class definition.\r
4985 //\r
4986 // *************************************************** //\r
4987 \r
4988 RtError :: RtError(const char *p, TYPE tipe)\r
4989 {\r
4990   type = tipe;\r
4991   strncpy(error_message, p, 256);\r
4992 }\r
4993 \r
4994 RtError :: ~RtError()\r
4995 {\r
4996 }\r
4997 \r
4998 void RtError :: printMessage()\r
4999 {\r
5000   printf("\n%s\n\n", error_message);\r
5001 }\r