1 /*
2 * Copyright (c) 2002, 2012, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation. Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25
26 #define USE_ERROR
27 #define USE_TRACE
28
29 #include "PLATFORM_API_BsdOS_ALSA_PCMUtils.h"
30 #include "PLATFORM_API_BsdOS_ALSA_CommonUtils.h"
31 #include "DirectAudio.h"
32
33 #if USE_DAUDIO == TRUE
34
35 #ifndef ESTRPIPE
36 # define ESTRPIPE EPIPE
37 #endif
38
39 // GetPosition method 1: based on how many bytes are passed to the kernel driver
40 // + does not need much processor resources
41 // - not very exact, "jumps"
42 // GetPosition method 2: ask kernel about actual position of playback.
43 // - very exact
44 // - switch to kernel layer for each call
45 // GetPosition method 3: use snd_pcm_avail() call - not yet in official ALSA
46 // quick tests on a Pentium 200MMX showed max. 1.5% processor usage
47 // for playing back a CD-quality file and printing 20x per second a line
48 // on the console with the current time. So I guess performance is not such a
49 // factor here.
50 //#define GET_POSITION_METHOD1
51 #define GET_POSITION_METHOD2
52
53
54 // The default time for a period in microseconds.
55 // For very small buffers, only 2 periods are used.
56 #define DEFAULT_PERIOD_TIME 20000 /* 20ms */
57
58 ///// implemented functions of DirectAudio.h
59
DAUDIO_GetDirectAudioDeviceCount()60 INT32 DAUDIO_GetDirectAudioDeviceCount() {
61 return (INT32) getAudioDeviceCount();
62 }
63
64
DAUDIO_GetDirectAudioDeviceDescription(INT32 mixerIndex,DirectAudioDeviceDescription * description)65 INT32 DAUDIO_GetDirectAudioDeviceDescription(INT32 mixerIndex, DirectAudioDeviceDescription* description) {
66 ALSA_AudioDeviceDescription adesc;
67
68 adesc.index = (int) mixerIndex;
69 adesc.strLen = DAUDIO_STRING_LENGTH;
70
71 adesc.maxSimultaneousLines = (int*) (&(description->maxSimulLines));
72 adesc.deviceID = &(description->deviceID);
73 adesc.name = description->name;
74 adesc.vendor = description->vendor;
75 adesc.description = description->description;
76 adesc.version = description->version;
77
78 return getAudioDeviceDescriptionByIndex(&adesc);
79 }
80
81 #define MAX_BIT_INDEX 6
82 // returns
83 // 6: for anything above 24-bit
84 // 5: for 4 bytes sample size, 24-bit
85 // 4: for 3 bytes sample size, 24-bit
86 // 3: for 3 bytes sample size, 20-bit
87 // 2: for 2 bytes sample size, 16-bit
88 // 1: for 1 byte sample size, 8-bit
89 // 0: for anything else
getBitIndex(int sampleSizeInBytes,int significantBits)90 int getBitIndex(int sampleSizeInBytes, int significantBits) {
91 if (significantBits > 24) return 6;
92 if (sampleSizeInBytes == 4 && significantBits == 24) return 5;
93 if (sampleSizeInBytes == 3) {
94 if (significantBits == 24) return 4;
95 if (significantBits == 20) return 3;
96 }
97 if (sampleSizeInBytes == 2 && significantBits == 16) return 2;
98 if (sampleSizeInBytes == 1 && significantBits == 8) return 1;
99 return 0;
100 }
101
getSampleSizeInBytes(int bitIndex,int sampleSizeInBytes)102 int getSampleSizeInBytes(int bitIndex, int sampleSizeInBytes) {
103 switch(bitIndex) {
104 case 1: return 1;
105 case 2: return 2;
106 case 3: /* fall through */
107 case 4: return 3;
108 case 5: return 4;
109 }
110 return sampleSizeInBytes;
111 }
112
getSignificantBits(int bitIndex,int significantBits)113 int getSignificantBits(int bitIndex, int significantBits) {
114 switch(bitIndex) {
115 case 1: return 8;
116 case 2: return 16;
117 case 3: return 20;
118 case 4: /* fall through */
119 case 5: return 24;
120 }
121 return significantBits;
122 }
123
DAUDIO_GetFormats(INT32 mixerIndex,INT32 deviceID,int isSource,void * creator)124 void DAUDIO_GetFormats(INT32 mixerIndex, INT32 deviceID, int isSource, void* creator) {
125 snd_pcm_t* handle;
126 snd_pcm_format_mask_t* formatMask;
127 snd_pcm_format_t format;
128 snd_pcm_hw_params_t* hwParams;
129 int handledBits[MAX_BIT_INDEX+1];
130
131 int ret;
132 int sampleSizeInBytes, significantBits, isSigned, isBigEndian, enc;
133 int origSampleSizeInBytes, origSignificantBits;
134 unsigned int channels, minChannels, maxChannels;
135 int rate, bitIndex;
136
137 for (bitIndex = 0; bitIndex <= MAX_BIT_INDEX; bitIndex++) handledBits[bitIndex] = FALSE;
138 if (openPCMfromDeviceID(deviceID, &handle, isSource, TRUE /*query hardware*/) < 0) {
139 return;
140 }
141 ret = snd_pcm_format_mask_malloc(&formatMask);
142 if (ret != 0) {
143 ERROR1("snd_pcm_format_mask_malloc returned error %d\n", ret);
144 } else {
145 ret = snd_pcm_hw_params_malloc(&hwParams);
146 if (ret != 0) {
147 ERROR1("snd_pcm_hw_params_malloc returned error %d\n", ret);
148 } else {
149 ret = snd_pcm_hw_params_any(handle, hwParams);
150 /* snd_pcm_hw_params_any can return a positive value on success too */
151 if (ret < 0) {
152 ERROR1("snd_pcm_hw_params_any returned error %d\n", ret);
153 } else {
154 /* for the logic following this code, set ret to 0 to indicate success */
155 ret = 0;
156 }
157 }
158 snd_pcm_hw_params_get_format_mask(hwParams, formatMask);
159 if (ret == 0) {
160 ret = snd_pcm_hw_params_get_channels_min(hwParams, &minChannels);
161 if (ret != 0) {
162 ERROR1("snd_pcm_hw_params_get_channels_min returned error %d\n", ret);
163 }
164 }
165 if (ret == 0) {
166 ret = snd_pcm_hw_params_get_channels_max(hwParams, &maxChannels);
167 if (ret != 0) {
168 ERROR1("snd_pcm_hw_params_get_channels_max returned error %d\n", ret);
169 }
170 }
171
172 // since we queried the hw: device, for many soundcards, it will only
173 // report the maximum number of channels (which is the only way to talk
174 // to the hw: device). Since we will, however, open the plughw: device
175 // when opening the Source/TargetDataLine, we can safely assume that
176 // also the channels 1..maxChannels are available.
177 #ifdef ALSA_PCM_USE_PLUGHW
178 minChannels = 1;
179 #endif
180 if (ret == 0) {
181 // plughw: supports any sample rate
182 rate = -1;
183 for (format = 0; format <= SND_PCM_FORMAT_LAST; format++) {
184 if (snd_pcm_format_mask_test(formatMask, format)) {
185 // format exists
186 if (getFormatFromAlsaFormat(format, &origSampleSizeInBytes,
187 &origSignificantBits,
188 &isSigned, &isBigEndian, &enc)) {
189 // now if we use plughw:, we can use any bit size below the
190 // natively supported ones. Some ALSA drivers only support the maximum
191 // bit size, so we add any sample rates below the reported one.
192 // E.g. this iteration reports support for 16-bit.
193 // getBitIndex will return 2, so it will add entries for
194 // 16-bit (bitIndex=2) and in the next do-while loop iteration,
195 // it will decrease bitIndex and will therefore add 8-bit support.
196 bitIndex = getBitIndex(origSampleSizeInBytes, origSignificantBits);
197 do {
198 if (bitIndex == 0
199 || bitIndex == MAX_BIT_INDEX
200 || !handledBits[bitIndex]) {
201 handledBits[bitIndex] = TRUE;
202 sampleSizeInBytes = getSampleSizeInBytes(bitIndex, origSampleSizeInBytes);
203 significantBits = getSignificantBits(bitIndex, origSignificantBits);
204 if (maxChannels - minChannels > MAXIMUM_LISTED_CHANNELS) {
205 // avoid too many channels explicitly listed
206 // just add -1, min, and max
207 DAUDIO_AddAudioFormat(creator, significantBits,
208 -1, -1, rate,
209 enc, isSigned, isBigEndian);
210 DAUDIO_AddAudioFormat(creator, significantBits,
211 sampleSizeInBytes * minChannels,
212 minChannels, rate,
213 enc, isSigned, isBigEndian);
214 DAUDIO_AddAudioFormat(creator, significantBits,
215 sampleSizeInBytes * maxChannels,
216 maxChannels, rate,
217 enc, isSigned, isBigEndian);
218 } else {
219 for (channels = minChannels; channels <= maxChannels; channels++) {
220 DAUDIO_AddAudioFormat(creator, significantBits,
221 sampleSizeInBytes * channels,
222 channels, rate,
223 enc, isSigned, isBigEndian);
224 }
225 }
226 }
227 #ifndef ALSA_PCM_USE_PLUGHW
228 // without plugin, do not add fake formats
229 break;
230 #endif
231 } while (--bitIndex > 0);
232 } else {
233 TRACE1("could not get format from alsa for format %d\n", format);
234 }
235 } else {
236 //TRACE1("Format %d not supported\n", format);
237 }
238 } // for loop
239 snd_pcm_hw_params_free(hwParams);
240 }
241 snd_pcm_format_mask_free(formatMask);
242 }
243 snd_pcm_close(handle);
244 }
245
246 /** Workaround for cr 7033899, 7030629:
247 * dmix plugin doesn't like flush (snd_pcm_drop) when the buffer is empty
248 * (just opened, underruned or already flushed).
249 * Sometimes it causes PCM falls to -EBADFD error,
250 * sometimes causes bufferSize change.
251 * To prevent unnecessary flushes AlsaPcmInfo::isRunning & isFlushed are used.
252 */
253 /* ******* ALSA PCM INFO ******************** */
254 typedef struct tag_AlsaPcmInfo {
255 snd_pcm_t* handle;
256 snd_pcm_hw_params_t* hwParams;
257 snd_pcm_sw_params_t* swParams;
258 int bufferSizeInBytes;
259 int frameSize; // storage size in Bytes
260 unsigned int periods;
261 snd_pcm_uframes_t periodSize;
262 short int isRunning; // see comment above
263 short int isFlushed; // see comment above
264 #ifdef GET_POSITION_METHOD2
265 // to be used exclusively by getBytePosition!
266 snd_pcm_status_t* positionStatus;
267 #endif
268 } AlsaPcmInfo;
269
270
setStartThresholdNoCommit(AlsaPcmInfo * info,int useThreshold)271 int setStartThresholdNoCommit(AlsaPcmInfo* info, int useThreshold) {
272 int ret;
273 int threshold;
274
275 if (useThreshold) {
276 // start device whenever anything is written to the buffer
277 threshold = 1;
278 } else {
279 // never start the device automatically
280 threshold = 2000000000; /* near UINT_MAX */
281 }
282 ret = snd_pcm_sw_params_set_start_threshold(info->handle, info->swParams, threshold);
283 if (ret < 0) {
284 ERROR1("Unable to set start threshold mode: %s\n", snd_strerror(ret));
285 return FALSE;
286 }
287 return TRUE;
288 }
289
setStartThreshold(AlsaPcmInfo * info,int useThreshold)290 int setStartThreshold(AlsaPcmInfo* info, int useThreshold) {
291 int ret = 0;
292
293 if (!setStartThresholdNoCommit(info, useThreshold)) {
294 ret = -1;
295 }
296 if (ret == 0) {
297 // commit it
298 ret = snd_pcm_sw_params(info->handle, info->swParams);
299 if (ret < 0) {
300 ERROR1("Unable to set sw params: %s\n", snd_strerror(ret));
301 }
302 }
303 return (ret == 0)?TRUE:FALSE;
304 }
305
306
307 // returns TRUE if successful
setHWParams(AlsaPcmInfo * info,float sampleRate,int channels,int bufferSizeInFrames,snd_pcm_format_t format)308 int setHWParams(AlsaPcmInfo* info,
309 float sampleRate,
310 int channels,
311 int bufferSizeInFrames,
312 snd_pcm_format_t format) {
313 unsigned int rrate, periodTime, periods;
314 int ret, dir;
315 snd_pcm_uframes_t alsaBufferSizeInFrames = (snd_pcm_uframes_t) bufferSizeInFrames;
316
317 /* choose all parameters */
318 ret = snd_pcm_hw_params_any(info->handle, info->hwParams);
319 if (ret < 0) {
320 ERROR1("Broken configuration: no configurations available: %s\n", snd_strerror(ret));
321 return FALSE;
322 }
323 /* set the interleaved read/write format */
324 ret = snd_pcm_hw_params_set_access(info->handle, info->hwParams, SND_PCM_ACCESS_RW_INTERLEAVED);
325 if (ret < 0) {
326 ERROR1("SND_PCM_ACCESS_RW_INTERLEAVED access type not available: %s\n", snd_strerror(ret));
327 return FALSE;
328 }
329 /* set the sample format */
330 ret = snd_pcm_hw_params_set_format(info->handle, info->hwParams, format);
331 if (ret < 0) {
332 ERROR1("Sample format not available: %s\n", snd_strerror(ret));
333 return FALSE;
334 }
335 /* set the count of channels */
336 ret = snd_pcm_hw_params_set_channels(info->handle, info->hwParams, channels);
337 if (ret < 0) {
338 ERROR2("Channels count (%d) not available: %s\n", channels, snd_strerror(ret));
339 return FALSE;
340 }
341 /* set the stream rate */
342 rrate = (int) (sampleRate + 0.5f);
343 dir = 0;
344 ret = snd_pcm_hw_params_set_rate_near(info->handle, info->hwParams, &rrate, &dir);
345 if (ret < 0) {
346 ERROR2("Rate %dHz not available for playback: %s\n", (int) (sampleRate+0.5f), snd_strerror(ret));
347 return FALSE;
348 }
349 if ((rrate-sampleRate > 2) || (rrate-sampleRate < - 2)) {
350 ERROR2("Rate doesn't match (requested %2.2fHz, got %dHz)\n", sampleRate, rrate);
351 return FALSE;
352 }
353 /* set the buffer time */
354 ret = snd_pcm_hw_params_set_buffer_size_near(info->handle, info->hwParams, &alsaBufferSizeInFrames);
355 if (ret < 0) {
356 ERROR2("Unable to set buffer size to %d frames: %s\n",
357 (int) alsaBufferSizeInFrames, snd_strerror(ret));
358 return FALSE;
359 }
360 bufferSizeInFrames = (int) alsaBufferSizeInFrames;
361 /* set the period time */
362 if (bufferSizeInFrames > 1024) {
363 dir = 0;
364 periodTime = DEFAULT_PERIOD_TIME;
365 ret = snd_pcm_hw_params_set_period_time_near(info->handle, info->hwParams, &periodTime, &dir);
366 if (ret < 0) {
367 ERROR2("Unable to set period time to %d: %s\n", DEFAULT_PERIOD_TIME, snd_strerror(ret));
368 return FALSE;
369 }
370 } else {
371 /* set the period count for very small buffer sizes to 2 */
372 dir = 0;
373 periods = 2;
374 ret = snd_pcm_hw_params_set_periods_near(info->handle, info->hwParams, &periods, &dir);
375 if (ret < 0) {
376 ERROR2("Unable to set period count to %d: %s\n", /*periods*/ 2, snd_strerror(ret));
377 return FALSE;
378 }
379 }
380 /* write the parameters to device */
381 ret = snd_pcm_hw_params(info->handle, info->hwParams);
382 if (ret < 0) {
383 ERROR1("Unable to set hw params: %s\n", snd_strerror(ret));
384 return FALSE;
385 }
386 return TRUE;
387 }
388
389 // returns 1 if successful
setSWParams(AlsaPcmInfo * info)390 int setSWParams(AlsaPcmInfo* info) {
391 int ret;
392
393 /* get the current swparams */
394 ret = snd_pcm_sw_params_current(info->handle, info->swParams);
395 if (ret < 0) {
396 ERROR1("Unable to determine current swparams: %s\n", snd_strerror(ret));
397 return FALSE;
398 }
399 /* never start the transfer automatically */
400 if (!setStartThresholdNoCommit(info, FALSE /* don't use threshold */)) {
401 return FALSE;
402 }
403
404 /* allow the transfer when at least period_size samples can be processed */
405 ret = snd_pcm_sw_params_set_avail_min(info->handle, info->swParams, info->periodSize);
406 if (ret < 0) {
407 ERROR1("Unable to set avail min for playback: %s\n", snd_strerror(ret));
408 return FALSE;
409 }
410 /* write the parameters to the playback device */
411 ret = snd_pcm_sw_params(info->handle, info->swParams);
412 if (ret < 0) {
413 ERROR1("Unable to set sw params: %s\n", snd_strerror(ret));
414 return FALSE;
415 }
416 return TRUE;
417 }
418
419 static snd_output_t* ALSA_OUTPUT = NULL;
420
DAUDIO_Open(INT32 mixerIndex,INT32 deviceID,int isSource,int encoding,float sampleRate,int sampleSizeInBits,int frameSize,int channels,int isSigned,int isBigEndian,int bufferSizeInBytes)421 void* DAUDIO_Open(INT32 mixerIndex, INT32 deviceID, int isSource,
422 int encoding, float sampleRate, int sampleSizeInBits,
423 int frameSize, int channels,
424 int isSigned, int isBigEndian, int bufferSizeInBytes) {
425 snd_pcm_format_mask_t* formatMask;
426 snd_pcm_format_t format;
427 int dir;
428 int ret = 0;
429 AlsaPcmInfo* info = NULL;
430 /* snd_pcm_uframes_t is 64 bit on 64-bit systems */
431 snd_pcm_uframes_t alsaBufferSizeInFrames = 0;
432
433
434 TRACE0("> DAUDIO_Open\n");
435 #ifdef USE_TRACE
436 // for using ALSA debug dump methods
437 if (ALSA_OUTPUT == NULL) {
438 snd_output_stdio_attach(&ALSA_OUTPUT, stdout, 0);
439 }
440 #endif
441 if (channels <= 0) {
442 ERROR1("ERROR: Invalid number of channels=%d!\n", channels);
443 return NULL;
444 }
445 info = (AlsaPcmInfo*) malloc(sizeof(AlsaPcmInfo));
446 if (!info) {
447 ERROR0("Out of memory\n");
448 return NULL;
449 }
450 memset(info, 0, sizeof(AlsaPcmInfo));
451 // initial values are: stopped, flushed
452 info->isRunning = 0;
453 info->isFlushed = 1;
454
455 ret = openPCMfromDeviceID(deviceID, &(info->handle), isSource, FALSE /* do open device*/);
456 if (ret == 0) {
457 // set to blocking mode
458 snd_pcm_nonblock(info->handle, 0);
459 ret = snd_pcm_hw_params_malloc(&(info->hwParams));
460 if (ret != 0) {
461 ERROR1(" snd_pcm_hw_params_malloc returned error %d\n", ret);
462 } else {
463 ret = -1;
464 if (getAlsaFormatFromFormat(&format, frameSize / channels, sampleSizeInBits,
465 isSigned, isBigEndian, encoding)) {
466 if (setHWParams(info,
467 sampleRate,
468 channels,
469 bufferSizeInBytes / frameSize,
470 format)) {
471 info->frameSize = frameSize;
472 ret = snd_pcm_hw_params_get_period_size(info->hwParams, &info->periodSize, &dir);
473 if (ret < 0) {
474 ERROR1("ERROR: snd_pcm_hw_params_get_period: %s\n", snd_strerror(ret));
475 }
476 snd_pcm_hw_params_get_periods(info->hwParams, &(info->periods), &dir);
477 snd_pcm_hw_params_get_buffer_size(info->hwParams, &alsaBufferSizeInFrames);
478 info->bufferSizeInBytes = (int) alsaBufferSizeInFrames * frameSize;
479 TRACE3(" DAUDIO_Open: period size = %d frames, periods = %d. Buffer size: %d bytes.\n",
480 (int) info->periodSize, info->periods, info->bufferSizeInBytes);
481 }
482 }
483 }
484 if (ret == 0) {
485 // set software parameters
486 ret = snd_pcm_sw_params_malloc(&(info->swParams));
487 if (ret != 0) {
488 ERROR1("snd_pcm_hw_params_malloc returned error %d\n", ret);
489 } else {
490 if (!setSWParams(info)) {
491 ret = -1;
492 }
493 }
494 }
495 if (ret == 0) {
496 // prepare device
497 ret = snd_pcm_prepare(info->handle);
498 if (ret < 0) {
499 ERROR1("ERROR: snd_pcm_prepare: %s\n", snd_strerror(ret));
500 }
501 }
502
503 #ifdef GET_POSITION_METHOD2
504 if (ret == 0) {
505 ret = snd_pcm_status_malloc(&(info->positionStatus));
506 if (ret != 0) {
507 ERROR1("ERROR in snd_pcm_status_malloc: %s\n", snd_strerror(ret));
508 }
509 }
510 #endif
511 }
512 if (ret != 0) {
513 DAUDIO_Close((void*) info, isSource);
514 info = NULL;
515 } else {
516 // set to non-blocking mode
517 snd_pcm_nonblock(info->handle, 1);
518 TRACE1("< DAUDIO_Open: Opened device successfully. Handle=%p\n",
519 (void*) info->handle);
520 }
521 return (void*) info;
522 }
523
524 #ifdef USE_TRACE
printState(snd_pcm_state_t state)525 void printState(snd_pcm_state_t state) {
526 if (state == SND_PCM_STATE_OPEN) {
527 TRACE0("State: SND_PCM_STATE_OPEN\n");
528 }
529 else if (state == SND_PCM_STATE_SETUP) {
530 TRACE0("State: SND_PCM_STATE_SETUP\n");
531 }
532 else if (state == SND_PCM_STATE_PREPARED) {
533 TRACE0("State: SND_PCM_STATE_PREPARED\n");
534 }
535 else if (state == SND_PCM_STATE_RUNNING) {
536 TRACE0("State: SND_PCM_STATE_RUNNING\n");
537 }
538 else if (state == SND_PCM_STATE_XRUN) {
539 TRACE0("State: SND_PCM_STATE_XRUN\n");
540 }
541 else if (state == SND_PCM_STATE_DRAINING) {
542 TRACE0("State: SND_PCM_STATE_DRAINING\n");
543 }
544 else if (state == SND_PCM_STATE_PAUSED) {
545 TRACE0("State: SND_PCM_STATE_PAUSED\n");
546 }
547 else if (state == SND_PCM_STATE_SUSPENDED) {
548 TRACE0("State: SND_PCM_STATE_SUSPENDED\n");
549 }
550 }
551 #endif
552
DAUDIO_Start(void * id,int isSource)553 int DAUDIO_Start(void* id, int isSource) {
554 AlsaPcmInfo* info = (AlsaPcmInfo*) id;
555 int ret;
556 snd_pcm_state_t state;
557
558 TRACE0("> DAUDIO_Start\n");
559 // set to blocking mode
560 snd_pcm_nonblock(info->handle, 0);
561 // set start mode so that it always starts as soon as data is there
562 setStartThreshold(info, TRUE /* use threshold */);
563 state = snd_pcm_state(info->handle);
564 if (state == SND_PCM_STATE_PAUSED) {
565 // in case it was stopped previously
566 TRACE0(" Un-pausing...\n");
567 ret = snd_pcm_pause(info->handle, FALSE);
568 if (ret != 0) {
569 ERROR2(" NOTE: error in snd_pcm_pause:%d: %s\n", ret, snd_strerror(ret));
570 }
571 }
572 if (state == SND_PCM_STATE_SUSPENDED) {
573 TRACE0(" Resuming...\n");
574 ret = snd_pcm_resume(info->handle);
575 if (ret < 0) {
576 if ((ret != -EAGAIN) && (ret != -ENOSYS)) {
577 ERROR2(" ERROR: error in snd_pcm_resume:%d: %s\n", ret, snd_strerror(ret));
578 }
579 }
580 }
581 if (state == SND_PCM_STATE_SETUP) {
582 TRACE0("need to call prepare again...\n");
583 // prepare device
584 ret = snd_pcm_prepare(info->handle);
585 if (ret < 0) {
586 ERROR1("ERROR: snd_pcm_prepare: %s\n", snd_strerror(ret));
587 }
588 }
589 // in case there is still data in the buffers
590 ret = snd_pcm_start(info->handle);
591 if (ret != 0) {
592 if (ret != -EPIPE) {
593 ERROR2(" NOTE: error in snd_pcm_start: %d: %s\n", ret, snd_strerror(ret));
594 }
595 }
596 // set to non-blocking mode
597 ret = snd_pcm_nonblock(info->handle, 1);
598 if (ret != 0) {
599 ERROR1(" ERROR in snd_pcm_nonblock: %s\n", snd_strerror(ret));
600 }
601 state = snd_pcm_state(info->handle);
602 #ifdef USE_TRACE
603 printState(state);
604 #endif
605 ret = (state == SND_PCM_STATE_PREPARED)
606 || (state == SND_PCM_STATE_RUNNING)
607 || (state == SND_PCM_STATE_XRUN)
608 || (state == SND_PCM_STATE_SUSPENDED);
609 if (ret) {
610 info->isRunning = 1;
611 // source line should keep isFlushed value until Write() is called;
612 // for target data line reset it right now.
613 if (!isSource) {
614 info->isFlushed = 0;
615 }
616 }
617 TRACE1("< DAUDIO_Start %s\n", ret?"success":"error");
618 return ret?TRUE:FALSE;
619 }
620
DAUDIO_Stop(void * id,int isSource)621 int DAUDIO_Stop(void* id, int isSource) {
622 AlsaPcmInfo* info = (AlsaPcmInfo*) id;
623 int ret;
624
625 TRACE0("> DAUDIO_Stop\n");
626 // set to blocking mode
627 snd_pcm_nonblock(info->handle, 0);
628 setStartThreshold(info, FALSE /* don't use threshold */); // device will not start after buffer xrun
629 ret = snd_pcm_pause(info->handle, 1);
630 // set to non-blocking mode
631 snd_pcm_nonblock(info->handle, 1);
632 if (ret != 0) {
633 ERROR1("ERROR in snd_pcm_pause: %s\n", snd_strerror(ret));
634 return FALSE;
635 }
636 info->isRunning = 0;
637 TRACE0("< DAUDIO_Stop success\n");
638 return TRUE;
639 }
640
DAUDIO_Close(void * id,int isSource)641 void DAUDIO_Close(void* id, int isSource) {
642 AlsaPcmInfo* info = (AlsaPcmInfo*) id;
643
644 TRACE0("DAUDIO_Close\n");
645 if (info != NULL) {
646 if (info->handle != NULL) {
647 snd_pcm_close(info->handle);
648 }
649 if (info->hwParams) {
650 snd_pcm_hw_params_free(info->hwParams);
651 }
652 if (info->swParams) {
653 snd_pcm_sw_params_free(info->swParams);
654 }
655 #ifdef GET_POSITION_METHOD2
656 if (info->positionStatus) {
657 snd_pcm_status_free(info->positionStatus);
658 }
659 #endif
660 free(info);
661 }
662 }
663
664 /*
665 * Underrun and suspend recovery
666 * returns
667 * 0: exit native and return 0
668 * 1: try again to write/read
669 * -1: error - exit native with return value -1
670 */
xrun_recovery(AlsaPcmInfo * info,int err)671 int xrun_recovery(AlsaPcmInfo* info, int err) {
672 int ret;
673
674 if (err == -EPIPE) { /* underrun / overflow */
675 TRACE0("xrun_recovery: underrun/overflow.\n");
676 ret = snd_pcm_prepare(info->handle);
677 if (ret < 0) {
678 ERROR1("Can't recover from underrun/overflow, prepare failed: %s\n", snd_strerror(ret));
679 return -1;
680 }
681 return 1;
682 } else if (err == -ESTRPIPE) {
683 TRACE0("xrun_recovery: suspended.\n");
684 ret = snd_pcm_resume(info->handle);
685 if (ret < 0) {
686 if (ret == -EAGAIN) {
687 return 0; /* wait until the suspend flag is released */
688 }
689 return -1;
690 }
691 ret = snd_pcm_prepare(info->handle);
692 if (ret < 0) {
693 ERROR1("Can't recover from underrun/overflow, prepare failed: %s\n", snd_strerror(ret));
694 return -1;
695 }
696 return 1;
697 } else if (err == -EAGAIN) {
698 TRACE0("xrun_recovery: EAGAIN try again flag.\n");
699 return 0;
700 }
701
702 TRACE2("xrun_recovery: unexpected error %d: %s\n", err, snd_strerror(err));
703 return -1;
704 }
705
706 // returns -1 on error
DAUDIO_Write(void * id,char * data,int byteSize)707 int DAUDIO_Write(void* id, char* data, int byteSize) {
708 AlsaPcmInfo* info = (AlsaPcmInfo*) id;
709 int ret, count;
710 snd_pcm_sframes_t frameSize, writtenFrames;
711
712 TRACE1("> DAUDIO_Write %d bytes\n", byteSize);
713
714 /* sanity */
715 if (byteSize <= 0 || info->frameSize <= 0) {
716 ERROR2(" DAUDIO_Write: byteSize=%d, frameSize=%d!\n",
717 (int) byteSize, (int) info->frameSize);
718 TRACE0("< DAUDIO_Write returning -1\n");
719 return -1;
720 }
721
722 count = 2; // maximum number of trials to recover from underrun
723 //frameSize = snd_pcm_bytes_to_frames(info->handle, byteSize);
724 frameSize = (snd_pcm_sframes_t) (byteSize / info->frameSize);
725 do {
726 writtenFrames = snd_pcm_writei(info->handle, (const void*) data, (snd_pcm_uframes_t) frameSize);
727
728 if (writtenFrames < 0) {
729 ret = xrun_recovery(info, (int) writtenFrames);
730 if (ret <= 0) {
731 TRACE1("DAUDIO_Write: xrun recovery returned %d -> return.\n", ret);
732 return ret;
733 }
734 if (count-- <= 0) {
735 ERROR0("DAUDIO_Write: too many attempts to recover from xrun/suspend\n");
736 return -1;
737 }
738 } else {
739 break;
740 }
741 } while (TRUE);
742 //ret = snd_pcm_frames_to_bytes(info->handle, writtenFrames);
743
744 if (writtenFrames > 0) {
745 // reset "flushed" flag
746 info->isFlushed = 0;
747 }
748
749 ret = (int) (writtenFrames * info->frameSize);
750 TRACE1("< DAUDIO_Write: returning %d bytes.\n", ret);
751 return ret;
752 }
753
754 // returns -1 on error
DAUDIO_Read(void * id,char * data,int byteSize)755 int DAUDIO_Read(void* id, char* data, int byteSize) {
756 AlsaPcmInfo* info = (AlsaPcmInfo*) id;
757 int ret, count;
758 snd_pcm_sframes_t frameSize, readFrames;
759
760 TRACE1("> DAUDIO_Read %d bytes\n", byteSize);
761 /*TRACE3(" info=%p, data=%p, byteSize=%d\n",
762 (void*) info, (void*) data, (int) byteSize);
763 TRACE2(" info->frameSize=%d, info->handle=%p\n",
764 (int) info->frameSize, (void*) info->handle);
765 */
766 /* sanity */
767 if (byteSize <= 0 || info->frameSize <= 0) {
768 ERROR2(" DAUDIO_Read: byteSize=%d, frameSize=%d!\n",
769 (int) byteSize, (int) info->frameSize);
770 TRACE0("< DAUDIO_Read returning -1\n");
771 return -1;
772 }
773 if (!info->isRunning && info->isFlushed) {
774 // PCM has nothing to read
775 return 0;
776 }
777
778 count = 2; // maximum number of trials to recover from error
779 //frameSize = snd_pcm_bytes_to_frames(info->handle, byteSize);
780 frameSize = (snd_pcm_sframes_t) (byteSize / info->frameSize);
781 do {
782 readFrames = snd_pcm_readi(info->handle, (void*) data, (snd_pcm_uframes_t) frameSize);
783 if (readFrames < 0) {
784 ret = xrun_recovery(info, (int) readFrames);
785 if (ret <= 0) {
786 TRACE1("DAUDIO_Read: xrun recovery returned %d -> return.\n", ret);
787 return ret;
788 }
789 if (count-- <= 0) {
790 ERROR0("DAUDIO_Read: too many attempts to recover from xrun/suspend\n");
791 return -1;
792 }
793 } else {
794 break;
795 }
796 } while (TRUE);
797 //ret = snd_pcm_frames_to_bytes(info->handle, readFrames);
798 ret = (int) (readFrames * info->frameSize);
799 TRACE1("< DAUDIO_Read: returning %d bytes.\n", ret);
800 return ret;
801 }
802
803
DAUDIO_GetBufferSize(void * id,int isSource)804 int DAUDIO_GetBufferSize(void* id, int isSource) {
805 AlsaPcmInfo* info = (AlsaPcmInfo*) id;
806
807 return info->bufferSizeInBytes;
808 }
809
DAUDIO_StillDraining(void * id,int isSource)810 int DAUDIO_StillDraining(void* id, int isSource) {
811 AlsaPcmInfo* info = (AlsaPcmInfo*) id;
812 snd_pcm_state_t state;
813
814 state = snd_pcm_state(info->handle);
815 //printState(state);
816 //TRACE1("Still draining: %s\n", (state != SND_PCM_STATE_XRUN)?"TRUE":"FALSE");
817 return (state == SND_PCM_STATE_RUNNING)?TRUE:FALSE;
818 }
819
820
DAUDIO_Flush(void * id,int isSource)821 int DAUDIO_Flush(void* id, int isSource) {
822 AlsaPcmInfo* info = (AlsaPcmInfo*) id;
823 int ret;
824
825 TRACE0("DAUDIO_Flush\n");
826
827 if (info->isFlushed) {
828 // nothing to drop
829 return 1;
830 }
831
832 ret = snd_pcm_drop(info->handle);
833 if (ret != 0) {
834 ERROR1("ERROR in snd_pcm_drop: %s\n", snd_strerror(ret));
835 return FALSE;
836 }
837
838 info->isFlushed = 1;
839 if (info->isRunning) {
840 ret = DAUDIO_Start(id, isSource);
841 }
842 return ret;
843 }
844
DAUDIO_GetAvailable(void * id,int isSource)845 int DAUDIO_GetAvailable(void* id, int isSource) {
846 AlsaPcmInfo* info = (AlsaPcmInfo*) id;
847 snd_pcm_sframes_t availableInFrames;
848 snd_pcm_state_t state;
849 int ret;
850
851 state = snd_pcm_state(info->handle);
852 if (info->isFlushed || state == SND_PCM_STATE_XRUN) {
853 // if in xrun state then we have the entire buffer available,
854 // not 0 as alsa reports
855 ret = info->bufferSizeInBytes;
856 } else {
857 availableInFrames = snd_pcm_avail_update(info->handle);
858 if (availableInFrames < 0) {
859 ret = 0;
860 } else {
861 //ret = snd_pcm_frames_to_bytes(info->handle, availableInFrames);
862 ret = (int) (availableInFrames * info->frameSize);
863 }
864 }
865 TRACE1("DAUDIO_GetAvailable returns %d bytes\n", ret);
866 return ret;
867 }
868
estimatePositionFromAvail(AlsaPcmInfo * info,int isSource,INT64 javaBytePos,int availInBytes)869 INT64 estimatePositionFromAvail(AlsaPcmInfo* info, int isSource, INT64 javaBytePos, int availInBytes) {
870 // estimate the current position with the buffer size and
871 // the available bytes to read or write in the buffer.
872 // not an elegant solution - bytePos will stop on xruns,
873 // and in race conditions it may jump backwards
874 // Advantage is that it is indeed based on the samples that go through
875 // the system (rather than time-based methods)
876 if (isSource) {
877 // javaBytePos is the position that is reached when the current
878 // buffer is played completely
879 return (INT64) (javaBytePos - info->bufferSizeInBytes + availInBytes);
880 } else {
881 // javaBytePos is the position that was when the current buffer was empty
882 return (INT64) (javaBytePos + availInBytes);
883 }
884 }
885
DAUDIO_GetBytePosition(void * id,int isSource,INT64 javaBytePos)886 INT64 DAUDIO_GetBytePosition(void* id, int isSource, INT64 javaBytePos) {
887 AlsaPcmInfo* info = (AlsaPcmInfo*) id;
888 int ret;
889 INT64 result = javaBytePos;
890 snd_pcm_state_t state;
891 state = snd_pcm_state(info->handle);
892
893 if (!info->isFlushed && state != SND_PCM_STATE_XRUN) {
894 #ifdef GET_POSITION_METHOD2
895 snd_timestamp_t* ts;
896 snd_pcm_uframes_t framesAvail;
897
898 // note: slight race condition if this is called simultaneously from 2 threads
899 ret = snd_pcm_status(info->handle, info->positionStatus);
900 if (ret != 0) {
901 ERROR1("ERROR in snd_pcm_status: %s\n", snd_strerror(ret));
902 result = javaBytePos;
903 } else {
904 // calculate from time value, or from available bytes
905 framesAvail = snd_pcm_status_get_avail(info->positionStatus);
906 result = estimatePositionFromAvail(info, isSource, javaBytePos, framesAvail * info->frameSize);
907 }
908 #endif
909 #ifdef GET_POSITION_METHOD3
910 snd_pcm_uframes_t framesAvail;
911 ret = snd_pcm_avail(info->handle, &framesAvail);
912 if (ret != 0) {
913 ERROR1("ERROR in snd_pcm_avail: %s\n", snd_strerror(ret));
914 result = javaBytePos;
915 } else {
916 result = estimatePositionFromAvail(info, isSource, javaBytePos, framesAvail * info->frameSize);
917 }
918 #endif
919 #ifdef GET_POSITION_METHOD1
920 result = estimatePositionFromAvail(info, isSource, javaBytePos, DAUDIO_GetAvailable(id, isSource));
921 #endif
922 }
923 //printf("getbyteposition: javaBytePos=%d , return=%d\n", (int) javaBytePos, (int) result);
924 return result;
925 }
926
927
928
DAUDIO_SetBytePosition(void * id,int isSource,INT64 javaBytePos)929 void DAUDIO_SetBytePosition(void* id, int isSource, INT64 javaBytePos) {
930 /* save to ignore, since GetBytePosition
931 * takes the javaBytePos param into account
932 */
933 }
934
DAUDIO_RequiresServicing(void * id,int isSource)935 int DAUDIO_RequiresServicing(void* id, int isSource) {
936 // never need servicing on Bsd
937 return FALSE;
938 }
939
DAUDIO_Service(void * id,int isSource)940 void DAUDIO_Service(void* id, int isSource) {
941 // never need servicing on Bsd
942 }
943
944
945 #endif // USE_DAUDIO
946