blob: e179ab025cb8a6f038f65007fb130a6d09ccbbaa [file] [log] [blame]
J. Duke319a3b92007-12-01 00:00:00 +00001/*
2 * Copyright 2002-2007 Sun Microsystems, Inc. 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. Sun designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Sun 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
22 * CA 95054 USA or visit www.sun.com if you need additional information or
23 * have any 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 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 if (ret != 0) {
147 ERROR1("snd_pcm_hw_params_any returned error %d\n", ret);
148 }
149 }
150 snd_pcm_hw_params_get_format_mask(hwParams, formatMask);
151#ifdef ALSA_PCM_NEW_HW_PARAMS_API
152 if (ret == 0) {
153 ret = snd_pcm_hw_params_get_channels_min(hwParams, &minChannels);
154 if (ret != 0) {
155 ERROR1("snd_pcm_hw_params_get_channels_min returned error %d\n", ret);
156 }
157 }
158 if (ret == 0) {
159 ret = snd_pcm_hw_params_get_channels_max(hwParams, &maxChannels);
160 if (ret != 0) {
161 ERROR1("snd_pcm_hw_params_get_channels_max returned error %d\n", ret);
162 }
163 }
164#else
165 minChannels = snd_pcm_hw_params_get_channels_min(hwParams);
166 maxChannels = snd_pcm_hw_params_get_channels_max(hwParams);
167 if (minChannels > maxChannels) {
168 ERROR2("MinChannels=%d, maxChannels=%d\n", minChannels, maxChannels);
169 }
170#endif
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 (channels < 0)?-1:(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/* ******* ALSA PCM INFO ******************** */
247typedef struct tag_AlsaPcmInfo {
248 snd_pcm_t* handle;
249 snd_pcm_hw_params_t* hwParams;
250 snd_pcm_sw_params_t* swParams;
251 int bufferSizeInBytes;
252 int frameSize; // storage size in Bytes
253 int periods;
254 snd_pcm_uframes_t periodSize;
255#ifdef GET_POSITION_METHOD2
256 // to be used exclusively by getBytePosition!
257 snd_pcm_status_t* positionStatus;
258#endif
259} AlsaPcmInfo;
260
261
262int setStartThresholdNoCommit(AlsaPcmInfo* info, int useThreshold) {
263 int ret;
264 int threshold;
265
266 if (useThreshold) {
267 // start device whenever anything is written to the buffer
268 threshold = 1;
269 } else {
270 // never start the device automatically
271 threshold = 2000000000; /* near UINT_MAX */
272 }
273 ret = snd_pcm_sw_params_set_start_threshold(info->handle, info->swParams, threshold);
274 if (ret < 0) {
275 ERROR1("Unable to set start threshold mode: %s\n", snd_strerror(ret));
276 return FALSE;
277 }
278 return TRUE;
279}
280
281int setStartThreshold(AlsaPcmInfo* info, int useThreshold) {
282 int ret = 0;
283
284 if (!setStartThresholdNoCommit(info, useThreshold)) {
285 ret = -1;
286 }
287 if (ret == 0) {
288 // commit it
289 ret = snd_pcm_sw_params(info->handle, info->swParams);
290 if (ret < 0) {
291 ERROR1("Unable to set sw params: %s\n", snd_strerror(ret));
292 }
293 }
294 return (ret == 0)?TRUE:FALSE;
295}
296
297
298// returns TRUE if successful
299int setHWParams(AlsaPcmInfo* info,
300 float sampleRate,
301 int channels,
302 int bufferSizeInFrames,
303 snd_pcm_format_t format) {
304 unsigned int rrate;
305 int ret, dir, periods, periodTime;
306 snd_pcm_uframes_t alsaBufferSizeInFrames = (snd_pcm_uframes_t) bufferSizeInFrames;
307
308 /* choose all parameters */
309 ret = snd_pcm_hw_params_any(info->handle, info->hwParams);
310 if (ret < 0) {
311 ERROR1("Broken configuration: no configurations available: %s\n", snd_strerror(ret));
312 return FALSE;
313 }
314 /* set the interleaved read/write format */
315 ret = snd_pcm_hw_params_set_access(info->handle, info->hwParams, SND_PCM_ACCESS_RW_INTERLEAVED);
316 if (ret < 0) {
317 ERROR1("SND_PCM_ACCESS_RW_INTERLEAVED access type not available: %s\n", snd_strerror(ret));
318 return FALSE;
319 }
320 /* set the sample format */
321 ret = snd_pcm_hw_params_set_format(info->handle, info->hwParams, format);
322 if (ret < 0) {
323 ERROR1("Sample format not available: %s\n", snd_strerror(ret));
324 return FALSE;
325 }
326 /* set the count of channels */
327 ret = snd_pcm_hw_params_set_channels(info->handle, info->hwParams, channels);
328 if (ret < 0) {
329 ERROR2("Channels count (%d) not available: %s\n", channels, snd_strerror(ret));
330 return FALSE;
331 }
332 /* set the stream rate */
333 rrate = (int) (sampleRate + 0.5f);
334#ifdef ALSA_PCM_NEW_HW_PARAMS_API
335 dir = 0;
336 ret = snd_pcm_hw_params_set_rate_near(info->handle, info->hwParams, &rrate, &dir);
337#else
338 ret = snd_pcm_hw_params_set_rate_near(info->handle, info->hwParams, rrate, 0);
339#endif
340 if (ret < 0) {
341 ERROR2("Rate %dHz not available for playback: %s\n", (int) (sampleRate+0.5f), snd_strerror(ret));
342 return FALSE;
343 }
344 if ((rrate-sampleRate > 2) || (rrate-sampleRate < - 2)) {
345 ERROR2("Rate doesn't match (requested %2.2fHz, got %dHz)\n", sampleRate, rrate);
346 return FALSE;
347 }
348 /* set the buffer time */
349#ifdef ALSA_PCM_NEW_HW_PARAMS_API
350
351 ret = snd_pcm_hw_params_set_buffer_size_near(info->handle, info->hwParams, &alsaBufferSizeInFrames);
352#else
353 ret = snd_pcm_hw_params_set_buffer_size_near(info->handle, info->hwParams, alsaBufferSizeInFrames);
354#endif
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#ifdef ALSA_PCM_NEW_HW_PARAMS_API
366 ret = snd_pcm_hw_params_set_period_time_near(info->handle, info->hwParams, &periodTime, &dir);
367#else
368 periodTime = snd_pcm_hw_params_set_period_time_near(info->handle, info->hwParams, periodTime, &dir);
369 ret = periodTime;
370#endif
371 if (ret < 0) {
372 ERROR2("Unable to set period time to %d: %s\n", DEFAULT_PERIOD_TIME, snd_strerror(ret));
373 return FALSE;
374 }
375 } else {
376 /* set the period count for very small buffer sizes to 2 */
377 dir = 0;
378 periods = 2;
379#ifdef ALSA_PCM_NEW_HW_PARAMS_API
380 ret = snd_pcm_hw_params_set_periods_near(info->handle, info->hwParams, &periods, &dir);
381#else
382 periods = snd_pcm_hw_params_set_periods_near(info->handle, info->hwParams, periods, &dir);
383 ret = periods;
384#endif
385 if (ret < 0) {
386 ERROR2("Unable to set period count to %d: %s\n", /*periods*/ 2, snd_strerror(ret));
387 return FALSE;
388 }
389 }
390 /* write the parameters to device */
391 ret = snd_pcm_hw_params(info->handle, info->hwParams);
392 if (ret < 0) {
393 ERROR1("Unable to set hw params: %s\n", snd_strerror(ret));
394 return FALSE;
395 }
396 return TRUE;
397}
398
399// returns 1 if successful
400int setSWParams(AlsaPcmInfo* info) {
401 int ret;
402
403 /* get the current swparams */
404 ret = snd_pcm_sw_params_current(info->handle, info->swParams);
405 if (ret < 0) {
406 ERROR1("Unable to determine current swparams: %s\n", snd_strerror(ret));
407 return FALSE;
408 }
409 /* never start the transfer automatically */
410 if (!setStartThresholdNoCommit(info, FALSE /* don't use threshold */)) {
411 return FALSE;
412 }
413
414 /* allow the transfer when at least period_size samples can be processed */
415 ret = snd_pcm_sw_params_set_avail_min(info->handle, info->swParams, info->periodSize);
416 if (ret < 0) {
417 ERROR1("Unable to set avail min for playback: %s\n", snd_strerror(ret));
418 return FALSE;
419 }
420 /* align all transfers to 1 sample */
421 ret = snd_pcm_sw_params_set_xfer_align(info->handle, info->swParams, 1);
422 if (ret < 0) {
423 ERROR1("Unable to set transfer align: %s\n", snd_strerror(ret));
424 return FALSE;
425 }
426 /* write the parameters to the playback device */
427 ret = snd_pcm_sw_params(info->handle, info->swParams);
428 if (ret < 0) {
429 ERROR1("Unable to set sw params: %s\n", snd_strerror(ret));
430 return FALSE;
431 }
432 return TRUE;
433}
434
435static snd_output_t* ALSA_OUTPUT = NULL;
436
437void* DAUDIO_Open(INT32 mixerIndex, INT32 deviceID, int isSource,
438 int encoding, float sampleRate, int sampleSizeInBits,
439 int frameSize, int channels,
440 int isSigned, int isBigEndian, int bufferSizeInBytes) {
441 snd_pcm_format_mask_t* formatMask;
442 snd_pcm_format_t format;
443 int dir;
444 int ret = 0;
445 AlsaPcmInfo* info = NULL;
446 /* snd_pcm_uframes_t is 64 bit on 64-bit systems */
447 snd_pcm_uframes_t alsaPeriodSize = 0;
448 snd_pcm_uframes_t alsaBufferSizeInFrames = 0;
449
450
451 TRACE0("> DAUDIO_Open\n");
452#ifdef USE_TRACE
453 // for using ALSA debug dump methods
454 if (ALSA_OUTPUT == NULL) {
455 snd_output_stdio_attach(&ALSA_OUTPUT, stdout, 0);
456 }
457#endif
458
459 info = (AlsaPcmInfo*) malloc(sizeof(AlsaPcmInfo));
460 if (!info) {
461 ERROR0("Out of memory\n");
462 return NULL;
463 }
464 memset(info, 0, sizeof(AlsaPcmInfo));
465
466 ret = openPCMfromDeviceID(deviceID, &(info->handle), isSource, FALSE /* do open device*/);
467 if (ret == 0) {
468 // set to blocking mode
469 snd_pcm_nonblock(info->handle, 0);
470 ret = snd_pcm_hw_params_malloc(&(info->hwParams));
471 if (ret != 0) {
472 ERROR1(" snd_pcm_hw_params_malloc returned error %d\n", ret);
473 } else {
474 ret = -1;
475 if (getAlsaFormatFromFormat(&format, frameSize / channels, sampleSizeInBits,
476 isSigned, isBigEndian, encoding)) {
477 if (setHWParams(info,
478 sampleRate,
479 channels,
480 bufferSizeInBytes / frameSize,
481 format)) {
482 info->frameSize = frameSize;
483#ifdef ALSA_PCM_NEW_HW_PARAMS_API
484 ret = snd_pcm_hw_params_get_period_size(info->hwParams, &alsaPeriodSize, &dir);
485 info->periodSize = (int) alsaPeriodSize;
486 if (ret < 0) {
487 ERROR1("ERROR: snd_pcm_hw_params_get_period: %s\n", snd_strerror(ret));
488 }
489 snd_pcm_hw_params_get_periods(info->hwParams, &(info->periods), &dir);
490 snd_pcm_hw_params_get_buffer_size(info->hwParams, &alsaBufferSizeInFrames);
491 info->bufferSizeInBytes = (int) alsaBufferSizeInFrames * frameSize;
492#else
493 info->periodSize = snd_pcm_hw_params_get_period_size(info->hwParams, &dir);
494 info->periods = snd_pcm_hw_params_get_periods(info->hwParams, &dir);
495 info->bufferSizeInBytes = snd_pcm_hw_params_get_buffer_size(info->hwParams) * frameSize;
496 ret = 0;
497#endif
498 TRACE3(" DAUDIO_Open: period size = %d frames, periods = %d. Buffer size: %d bytes.\n",
499 (int) info->periodSize, info->periods, info->bufferSizeInBytes);
500 }
501 }
502 }
503 if (ret == 0) {
504 // set software parameters
505 ret = snd_pcm_sw_params_malloc(&(info->swParams));
506 if (ret != 0) {
507 ERROR1("snd_pcm_hw_params_malloc returned error %d\n", ret);
508 } else {
509 if (!setSWParams(info)) {
510 ret = -1;
511 }
512 }
513 }
514 if (ret == 0) {
515 // prepare device
516 ret = snd_pcm_prepare(info->handle);
517 if (ret < 0) {
518 ERROR1("ERROR: snd_pcm_prepare: %s\n", snd_strerror(ret));
519 }
520 }
521
522#ifdef GET_POSITION_METHOD2
523 if (ret == 0) {
524 ret = snd_pcm_status_malloc(&(info->positionStatus));
525 if (ret != 0) {
526 ERROR1("ERROR in snd_pcm_status_malloc: %s\n", snd_strerror(ret));
527 }
528 }
529#endif
530 }
531 if (ret != 0) {
532 DAUDIO_Close((void*) info, isSource);
533 info = NULL;
534 } else {
535 // set to non-blocking mode
536 snd_pcm_nonblock(info->handle, 1);
537 TRACE1("< DAUDIO_Open: Opened device successfully. Handle=%p\n",
538 (void*) info->handle);
539 }
540 return (void*) info;
541}
542
543#ifdef USE_TRACE
544void printState(snd_pcm_state_t state) {
545 if (state == SND_PCM_STATE_OPEN) {
546 TRACE0("State: SND_PCM_STATE_OPEN\n");
547 }
548 else if (state == SND_PCM_STATE_SETUP) {
549 TRACE0("State: SND_PCM_STATE_SETUP\n");
550 }
551 else if (state == SND_PCM_STATE_PREPARED) {
552 TRACE0("State: SND_PCM_STATE_PREPARED\n");
553 }
554 else if (state == SND_PCM_STATE_RUNNING) {
555 TRACE0("State: SND_PCM_STATE_RUNNING\n");
556 }
557 else if (state == SND_PCM_STATE_XRUN) {
558 TRACE0("State: SND_PCM_STATE_XRUN\n");
559 }
560 else if (state == SND_PCM_STATE_DRAINING) {
561 TRACE0("State: SND_PCM_STATE_DRAINING\n");
562 }
563 else if (state == SND_PCM_STATE_PAUSED) {
564 TRACE0("State: SND_PCM_STATE_PAUSED\n");
565 }
566 else if (state == SND_PCM_STATE_SUSPENDED) {
567 TRACE0("State: SND_PCM_STATE_SUSPENDED\n");
568 }
569}
570#endif
571
572int DAUDIO_Start(void* id, int isSource) {
573 AlsaPcmInfo* info = (AlsaPcmInfo*) id;
574 int ret;
575 snd_pcm_state_t state;
576
577 TRACE0("> DAUDIO_Start\n");
578 // set to blocking mode
579 snd_pcm_nonblock(info->handle, 0);
580 // set start mode so that it always starts as soon as data is there
581 setStartThreshold(info, TRUE /* use threshold */);
582 state = snd_pcm_state(info->handle);
583 if (state == SND_PCM_STATE_PAUSED) {
584 // in case it was stopped previously
585 TRACE0(" Un-pausing...\n");
586 ret = snd_pcm_pause(info->handle, FALSE);
587 if (ret != 0) {
588 ERROR2(" NOTE: error in snd_pcm_pause:%d: %s\n", ret, snd_strerror(ret));
589 }
590 }
591 if (state == SND_PCM_STATE_SUSPENDED) {
592 TRACE0(" Resuming...\n");
593 ret = snd_pcm_resume(info->handle);
594 if (ret < 0) {
595 if ((ret != -EAGAIN) && (ret != -ENOSYS)) {
596 ERROR2(" ERROR: error in snd_pcm_resume:%d: %s\n", ret, snd_strerror(ret));
597 }
598 }
599 }
600 if (state == SND_PCM_STATE_SETUP) {
601 TRACE0("need to call prepare again...\n");
602 // prepare device
603 ret = snd_pcm_prepare(info->handle);
604 if (ret < 0) {
605 ERROR1("ERROR: snd_pcm_prepare: %s\n", snd_strerror(ret));
606 }
607 }
608 // in case there is still data in the buffers
609 ret = snd_pcm_start(info->handle);
610 if (ret != 0) {
611 if (ret != -EPIPE) {
612 ERROR2(" NOTE: error in snd_pcm_start: %d: %s\n", ret, snd_strerror(ret));
613 }
614 }
615 // set to non-blocking mode
616 ret = snd_pcm_nonblock(info->handle, 1);
617 if (ret != 0) {
618 ERROR1(" ERROR in snd_pcm_nonblock: %s\n", snd_strerror(ret));
619 }
620 state = snd_pcm_state(info->handle);
621#ifdef USE_TRACE
622 printState(state);
623#endif
624 ret = (state == SND_PCM_STATE_PREPARED)
625 || (state == SND_PCM_STATE_RUNNING)
626 || (state == SND_PCM_STATE_XRUN)
627 || (state == SND_PCM_STATE_SUSPENDED);
628 TRACE1("< DAUDIO_Start %s\n", ret?"success":"error");
629 return ret?TRUE:FALSE;
630}
631
632int DAUDIO_Stop(void* id, int isSource) {
633 AlsaPcmInfo* info = (AlsaPcmInfo*) id;
634 int ret;
635
636 TRACE0("> DAUDIO_Stop\n");
637 // set to blocking mode
638 snd_pcm_nonblock(info->handle, 0);
639 setStartThreshold(info, FALSE /* don't use threshold */); // device will not start after buffer xrun
640 ret = snd_pcm_pause(info->handle, 1);
641 // set to non-blocking mode
642 snd_pcm_nonblock(info->handle, 1);
643 if (ret != 0) {
644 ERROR1("ERROR in snd_pcm_pause: %s\n", snd_strerror(ret));
645 return FALSE;
646 }
647 TRACE0("< DAUDIO_Stop success\n");
648 return TRUE;
649}
650
651void DAUDIO_Close(void* id, int isSource) {
652 AlsaPcmInfo* info = (AlsaPcmInfo*) id;
653
654 TRACE0("DAUDIO_Close\n");
655 if (info != NULL) {
656 if (info->handle != NULL) {
657 snd_pcm_close(info->handle);
658 }
659 if (info->hwParams) {
660 snd_pcm_hw_params_free(info->hwParams);
661 }
662 if (info->swParams) {
663 snd_pcm_sw_params_free(info->swParams);
664 }
665#ifdef GET_POSITION_METHOD2
666 if (info->positionStatus) {
667 snd_pcm_status_free(info->positionStatus);
668 }
669#endif
670 free(info);
671 }
672}
673
674/*
675 * Underrun and suspend recovery
676 * returns
677 * 0: exit native and return 0
678 * 1: try again to write/read
679 * -1: error - exit native with return value -1
680 */
681int xrun_recovery(AlsaPcmInfo* info, int err) {
682 int ret;
683
684 if (err == -EPIPE) { /* underrun / overflow */
685 TRACE0("xrun_recovery: underrun/overflow.\n");
686 ret = snd_pcm_prepare(info->handle);
687 if (ret < 0) {
688 ERROR1("Can't recover from underrun/overflow, prepare failed: %s\n", snd_strerror(ret));
689 return -1;
690 }
691 return 1;
692 }
693 else if (err == -ESTRPIPE) {
694 TRACE0("xrun_recovery: suspended.\n");
695 ret = snd_pcm_resume(info->handle);
696 if (ret < 0) {
697 if (ret == -EAGAIN) {
698 return 0; /* wait until the suspend flag is released */
699 }
700 return -1;
701 }
702 ret = snd_pcm_prepare(info->handle);
703 if (ret < 0) {
704 ERROR1("Can't recover from underrun/overflow, prepare failed: %s\n", snd_strerror(ret));
705 return -1;
706 }
707 return 1;
708 }
709 else if (err == -EAGAIN) {
710 TRACE0("xrun_recovery: EAGAIN try again flag.\n");
711 return 0;
712 }
713 TRACE2("xrun_recovery: unexpected error %d: %s\n", err, snd_strerror(err));
714 return -1;
715}
716
717// returns -1 on error
718int DAUDIO_Write(void* id, char* data, int byteSize) {
719 AlsaPcmInfo* info = (AlsaPcmInfo*) id;
720 int ret, count;
721 snd_pcm_sframes_t frameSize, writtenFrames;
722
723 TRACE1("> DAUDIO_Write %d bytes\n", byteSize);
724
725 /* sanity */
726 if (byteSize <= 0 || info->frameSize <= 0) {
727 ERROR2(" DAUDIO_Write: byteSize=%d, frameSize=%d!\n",
728 (int) byteSize, (int) info->frameSize);
729 TRACE0("< DAUDIO_Write returning -1\n");
730 return -1;
731 }
732 count = 2; // maximum number of trials to recover from underrun
733 //frameSize = snd_pcm_bytes_to_frames(info->handle, byteSize);
734 frameSize = (snd_pcm_sframes_t) (byteSize / info->frameSize);
735 do {
736 writtenFrames = snd_pcm_writei(info->handle, (const void*) data, (snd_pcm_uframes_t) frameSize);
737
738 if (writtenFrames < 0) {
739 ret = xrun_recovery(info, (int) writtenFrames);
740 if (ret <= 0) {
741 TRACE1("DAUDIO_Write: xrun recovery returned %d -> return.\n", ret);
742 return ret;
743 }
744 if (count-- <= 0) {
745 ERROR0("DAUDIO_Write: too many attempts to recover from xrun/suspend\n");
746 return -1;
747 }
748 } else {
749 break;
750 }
751 } while (TRUE);
752 //ret = snd_pcm_frames_to_bytes(info->handle, writtenFrames);
753 ret = (int) (writtenFrames * info->frameSize);
754 TRACE1("< DAUDIO_Write: returning %d bytes.\n", ret);
755 return ret;
756}
757
758// returns -1 on error
759int DAUDIO_Read(void* id, char* data, int byteSize) {
760 AlsaPcmInfo* info = (AlsaPcmInfo*) id;
761 int ret, count;
762 snd_pcm_sframes_t frameSize, readFrames;
763
764 TRACE1("> DAUDIO_Read %d bytes\n", byteSize);
765 /*TRACE3(" info=%p, data=%p, byteSize=%d\n",
766 (void*) info, (void*) data, (int) byteSize);
767 TRACE2(" info->frameSize=%d, info->handle=%p\n",
768 (int) info->frameSize, (void*) info->handle);
769 */
770 /* sanity */
771 if (byteSize <= 0 || info->frameSize <= 0) {
772 ERROR2(" DAUDIO_Read: byteSize=%d, frameSize=%d!\n",
773 (int) byteSize, (int) info->frameSize);
774 TRACE0("< DAUDIO_Read returning -1\n");
775 return -1;
776 }
777 count = 2; // maximum number of trials to recover from error
778 //frameSize = snd_pcm_bytes_to_frames(info->handle, byteSize);
779 frameSize = (snd_pcm_sframes_t) (byteSize / info->frameSize);
780 do {
781 readFrames = snd_pcm_readi(info->handle, (void*) data, (snd_pcm_uframes_t) frameSize);
782 if (readFrames < 0) {
783 ret = xrun_recovery(info, (int) readFrames);
784 if (ret <= 0) {
785 TRACE1("DAUDIO_Read: xrun recovery returned %d -> return.\n", ret);
786 return ret;
787 }
788 if (count-- <= 0) {
789 ERROR0("DAUDIO_Read: too many attempts to recover from xrun/suspend\n");
790 return -1;
791 }
792 } else {
793 break;
794 }
795 } while (TRUE);
796 //ret = snd_pcm_frames_to_bytes(info->handle, readFrames);
797 ret = (int) (readFrames * info->frameSize);
798 TRACE1("< DAUDIO_Read: returning %d bytes.\n", ret);
799 return ret;
800}
801
802
803int DAUDIO_GetBufferSize(void* id, int isSource) {
804 AlsaPcmInfo* info = (AlsaPcmInfo*) id;
805
806 return info->bufferSizeInBytes;
807}
808
809int DAUDIO_StillDraining(void* id, int isSource) {
810 AlsaPcmInfo* info = (AlsaPcmInfo*) id;
811 snd_pcm_state_t state;
812
813 state = snd_pcm_state(info->handle);
814 //printState(state);
815 //TRACE1("Still draining: %s\n", (state != SND_PCM_STATE_XRUN)?"TRUE":"FALSE");
816 return (state == SND_PCM_STATE_RUNNING)?TRUE:FALSE;
817}
818
819
820int DAUDIO_Flush(void* id, int isSource) {
821 AlsaPcmInfo* info = (AlsaPcmInfo*) id;
822 int ret;
823
824 TRACE0("DAUDIO_Flush\n");
825 ret = snd_pcm_drop(info->handle);
826 if (ret != 0) {
827 ERROR1("ERROR in snd_pcm_drop: %s\n", snd_strerror(ret));
828 return FALSE;
829 }
830 ret = DAUDIO_Start(id, isSource);
831 return ret;
832}
833
834int DAUDIO_GetAvailable(void* id, int isSource) {
835 AlsaPcmInfo* info = (AlsaPcmInfo*) id;
836 snd_pcm_sframes_t availableInFrames;
837 snd_pcm_state_t state;
838 int ret;
839
840 state = snd_pcm_state(info->handle);
841 if (state == SND_PCM_STATE_XRUN) {
842 // if in xrun state then we have the entire buffer available,
843 // not 0 as alsa reports
844 ret = info->bufferSizeInBytes;
845 } else {
846 availableInFrames = snd_pcm_avail_update(info->handle);
847 if (availableInFrames < 0) {
848 ret = 0;
849 } else {
850 //ret = snd_pcm_frames_to_bytes(info->handle, availableInFrames);
851 ret = (int) (availableInFrames * info->frameSize);
852 }
853 }
854 TRACE1("DAUDIO_GetAvailable returns %d bytes\n", ret);
855 return ret;
856}
857
858INT64 estimatePositionFromAvail(AlsaPcmInfo* info, int isSource, INT64 javaBytePos, int availInBytes) {
859 // estimate the current position with the buffer size and
860 // the available bytes to read or write in the buffer.
861 // not an elegant solution - bytePos will stop on xruns,
862 // and in race conditions it may jump backwards
863 // Advantage is that it is indeed based on the samples that go through
864 // the system (rather than time-based methods)
865 if (isSource) {
866 // javaBytePos is the position that is reached when the current
867 // buffer is played completely
868 return (INT64) (javaBytePos - info->bufferSizeInBytes + availInBytes);
869 } else {
870 // javaBytePos is the position that was when the current buffer was empty
871 return (INT64) (javaBytePos + availInBytes);
872 }
873}
874
875INT64 DAUDIO_GetBytePosition(void* id, int isSource, INT64 javaBytePos) {
876 AlsaPcmInfo* info = (AlsaPcmInfo*) id;
877 int ret;
878 INT64 result = javaBytePos;
879 snd_pcm_state_t state;
880 state = snd_pcm_state(info->handle);
881
882 if (state != SND_PCM_STATE_XRUN) {
883#ifdef GET_POSITION_METHOD2
884 snd_timestamp_t* ts;
885 snd_pcm_uframes_t framesAvail;
886
887 // note: slight race condition if this is called simultaneously from 2 threads
888 ret = snd_pcm_status(info->handle, info->positionStatus);
889 if (ret != 0) {
890 ERROR1("ERROR in snd_pcm_status: %s\n", snd_strerror(ret));
891 result = javaBytePos;
892 } else {
893 // calculate from time value, or from available bytes
894 framesAvail = snd_pcm_status_get_avail(info->positionStatus);
895 result = estimatePositionFromAvail(info, isSource, javaBytePos, framesAvail * info->frameSize);
896 }
897#endif
898#ifdef GET_POSITION_METHOD3
899 snd_pcm_uframes_t framesAvail;
900 ret = snd_pcm_avail(info->handle, &framesAvail);
901 if (ret != 0) {
902 ERROR1("ERROR in snd_pcm_avail: %s\n", snd_strerror(ret));
903 result = javaBytePos;
904 } else {
905 result = estimatePositionFromAvail(info, isSource, javaBytePos, framesAvail * info->frameSize);
906 }
907#endif
908#ifdef GET_POSITION_METHOD1
909 result = estimatePositionFromAvail(info, isSource, javaBytePos, DAUDIO_GetAvailable(id, isSource));
910#endif
911 }
912 //printf("getbyteposition: javaBytePos=%d , return=%d\n", (int) javaBytePos, (int) result);
913 return result;
914}
915
916
917
918void DAUDIO_SetBytePosition(void* id, int isSource, INT64 javaBytePos) {
919 /* save to ignore, since GetBytePosition
920 * takes the javaBytePos param into account
921 */
922}
923
924int DAUDIO_RequiresServicing(void* id, int isSource) {
925 // never need servicing on Linux
926 return FALSE;
927}
928
929void DAUDIO_Service(void* id, int isSource) {
930 // never need servicing on Linux
931}
932
933
934#endif // USE_DAUDIO