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