Removed
Link Here
|
1 |
/* |
2 |
* Copyright (c) 2009 Alexandre Ratchov <alex@caoua.org> |
3 |
* |
4 |
* Permission to use, copy, modify, and distribute this software for any |
5 |
* purpose with or without fee is hereby granted, provided that the above |
6 |
* copyright notice and this permission notice appear in all copies. |
7 |
* |
8 |
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES |
9 |
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF |
10 |
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR |
11 |
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES |
12 |
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN |
13 |
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF |
14 |
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
15 |
*/ |
16 |
#include <sys/types.h> |
17 |
#include <pthread.h> |
18 |
#include <poll.h> |
19 |
#include <errno.h> |
20 |
#include <string.h> |
21 |
#include <stdlib.h> |
22 |
#include <stdio.h> |
23 |
#include <sndio.h> |
24 |
|
25 |
#include "pa_util.h" |
26 |
#include "pa_hostapi.h" |
27 |
#include "pa_stream.h" |
28 |
#include "pa_process.h" |
29 |
#include "pa_allocation.h" |
30 |
|
31 |
#if 0 |
32 |
#define DPR(...) do { fprintf(stderr, __VA_ARGS__); } while (0) |
33 |
#else |
34 |
#define DPR(...) do {} while (0) |
35 |
#endif |
36 |
|
37 |
/* |
38 |
* per-stream data |
39 |
*/ |
40 |
typedef struct PaSndioStream |
41 |
{ |
42 |
PaUtilStreamRepresentation base; |
43 |
PaUtilBufferProcessor bufproc; /* format conversion */ |
44 |
struct sio_hdl *hdl; /* handle for device i/o */ |
45 |
struct sio_par par; /* current device parameters */ |
46 |
unsigned mode; /* SIO_PLAY, SIO_REC or both */ |
47 |
int stopped; /* stop requested or not started */ |
48 |
int active; /* thread is running */ |
49 |
unsigned long long realpos; /* frame number h/w is processing */ |
50 |
char *rbuf, *wbuf; /* bounce buffers for conversions */ |
51 |
unsigned long long rpos, wpos; /* bytes read/written */ |
52 |
pthread_t thread; /* thread of the callback interface */ |
53 |
} PaSndioStream; |
54 |
|
55 |
/* |
56 |
* api "class" data, common to all streams |
57 |
*/ |
58 |
typedef struct PaSndioHostApiRepresentation |
59 |
{ |
60 |
PaUtilHostApiRepresentation base; |
61 |
PaUtilStreamInterface callback; |
62 |
PaUtilStreamInterface blocking; |
63 |
/* |
64 |
* sndio has no device discovery mechanism and PortAudio has |
65 |
* no way of accepting raw device strings from users. |
66 |
* Normally we just expose the default device, which can be |
67 |
* changed via the AUDIODEVICE environment variable, but we |
68 |
* also allow specifying a list of up to 16 devices via the |
69 |
* PA_SNDIO_AUDIODEVICES environment variable. |
70 |
* |
71 |
* Example: |
72 |
* PA_SNDIO_AUDIODEVICES=default:snd/0.monitor:snd@remote/0 |
73 |
*/ |
74 |
#define PA_SNDIO_AUDIODEVICES_MAX 16 |
75 |
PaDeviceInfo device_info[PA_SNDIO_AUDIODEVICES_MAX]; |
76 |
PaDeviceInfo *infos[PA_SNDIO_AUDIODEVICES_MAX]; |
77 |
char *audiodevices; |
78 |
} PaSndioHostApiRepresentation; |
79 |
|
80 |
/* |
81 |
* callback invoked when blocks are processed by the hardware |
82 |
*/ |
83 |
static void |
84 |
sndioOnMove(void *addr, int delta) |
85 |
{ |
86 |
PaSndioStream *s = (PaSndioStream *)addr; |
87 |
|
88 |
s->realpos += delta; |
89 |
} |
90 |
|
91 |
/* |
92 |
* convert PA encoding to sndio encoding, return true on success |
93 |
*/ |
94 |
static int |
95 |
sndioSetFmt(struct sio_par *sio, PaSampleFormat fmt) |
96 |
{ |
97 |
switch (fmt & ~paNonInterleaved) { |
98 |
case paInt32: |
99 |
sio->sig = 1; |
100 |
sio->bits = 32; |
101 |
break; |
102 |
case paInt24: |
103 |
sio->sig = 1; |
104 |
sio->bits = 24; |
105 |
sio->bps = 3; /* paInt24 is packed format */ |
106 |
break; |
107 |
case paInt16: |
108 |
case paFloat32: |
109 |
sio->sig = 1; |
110 |
sio->bits = 16; |
111 |
break; |
112 |
case paInt8: |
113 |
sio->sig = 1; |
114 |
sio->bits = 8; |
115 |
break; |
116 |
case paUInt8: |
117 |
sio->sig = 0; |
118 |
sio->bits = 8; |
119 |
break; |
120 |
default: |
121 |
DPR("sndioSetFmt: %x: unsupported\n", fmt); |
122 |
return 0; |
123 |
} |
124 |
sio->le = SIO_LE_NATIVE; |
125 |
return 1; |
126 |
} |
127 |
|
128 |
/* |
129 |
* convert sndio encoding to PA encoding, return true on success |
130 |
*/ |
131 |
static int |
132 |
sndioGetFmt(struct sio_par *sio, PaSampleFormat *fmt) |
133 |
{ |
134 |
if ((sio->bps * 8 != sio->bits && !sio->msb) || |
135 |
(sio->bps > 1 && sio->le != SIO_LE_NATIVE)) { |
136 |
DPR("sndioGetFmt: bits = %u, le = %u, msb = %u, bps = %u\n", |
137 |
sio->bits, sio->le, sio->msb, sio->bps); |
138 |
return 0; |
139 |
} |
140 |
|
141 |
switch (sio->bits) { |
142 |
case 32: |
143 |
if (!sio->sig) |
144 |
return 0; |
145 |
*fmt = paInt32; |
146 |
break; |
147 |
case 24: |
148 |
if (!sio->sig) |
149 |
return 0; |
150 |
*fmt = (sio->bps == 3) ? paInt24 : paInt32; |
151 |
break; |
152 |
case 16: |
153 |
if (!sio->sig) |
154 |
return 0; |
155 |
*fmt = paInt16; |
156 |
break; |
157 |
case 8: |
158 |
*fmt = sio->sig ? paInt8 : paUInt8; |
159 |
break; |
160 |
default: |
161 |
DPR("sndioGetFmt: %u: unsupported\n", sio->bits); |
162 |
return 0; |
163 |
} |
164 |
return 1; |
165 |
} |
166 |
|
167 |
/* |
168 |
* I/O loop for callback interface |
169 |
*/ |
170 |
static void * |
171 |
sndioThread(void *arg) |
172 |
{ |
173 |
PaSndioStream *s = (PaSndioStream *)arg; |
174 |
PaStreamCallbackTimeInfo ti; |
175 |
unsigned char *data; |
176 |
unsigned todo, rblksz, wblksz; |
177 |
int n, result; |
178 |
|
179 |
rblksz = s->par.round * s->par.rchan * s->par.bps; |
180 |
wblksz = s->par.round * s->par.pchan * s->par.bps; |
181 |
|
182 |
DPR("sndioThread: mode = %x, round = %u, rblksz = %u, wblksz = %u\n", |
183 |
s->mode, s->par.round, rblksz, wblksz); |
184 |
|
185 |
while (!s->stopped) { |
186 |
if (s->mode & SIO_REC) { |
187 |
todo = rblksz; |
188 |
data = s->rbuf; |
189 |
while (todo > 0) { |
190 |
n = sio_read(s->hdl, data, todo); |
191 |
if (n == 0) { |
192 |
DPR("sndioThread: sio_read failed\n"); |
193 |
goto failed; |
194 |
} |
195 |
todo -= n; |
196 |
data += n; |
197 |
} |
198 |
s->rpos += s->par.round; |
199 |
ti.inputBufferAdcTime = |
200 |
(double)s->realpos / s->par.rate; |
201 |
} |
202 |
if (s->mode & SIO_PLAY) { |
203 |
ti.outputBufferDacTime = |
204 |
(double)(s->realpos + s->par.bufsz) / s->par.rate; |
205 |
} |
206 |
ti.currentTime = s->realpos / (double)s->par.rate; |
207 |
PaUtil_BeginBufferProcessing(&s->bufproc, &ti, 0); |
208 |
if (s->mode & SIO_PLAY) { |
209 |
PaUtil_SetOutputFrameCount(&s->bufproc, s->par.round); |
210 |
PaUtil_SetInterleavedOutputChannels(&s->bufproc, |
211 |
0, s->wbuf, s->par.pchan); |
212 |
} |
213 |
if (s->mode & SIO_REC) { |
214 |
PaUtil_SetInputFrameCount(&s->bufproc, s->par.round); |
215 |
PaUtil_SetInterleavedInputChannels(&s->bufproc, |
216 |
0, s->rbuf, s->par.rchan); |
217 |
} |
218 |
result = paContinue; |
219 |
n = PaUtil_EndBufferProcessing(&s->bufproc, &result); |
220 |
if (n != s->par.round) { |
221 |
DPR("sndioThread: %d < %u frames, result = %d\n", |
222 |
n, s->par.round, result); |
223 |
} |
224 |
if (result != paContinue) { |
225 |
break; |
226 |
} |
227 |
if (s->mode & SIO_PLAY) { |
228 |
n = sio_write(s->hdl, s->wbuf, wblksz); |
229 |
if (n < wblksz) { |
230 |
DPR("sndioThread: sio_write failed\n"); |
231 |
goto failed; |
232 |
} |
233 |
s->wpos += s->par.round; |
234 |
} |
235 |
} |
236 |
failed: |
237 |
s->active = 0; |
238 |
DPR("sndioThread: done\n"); |
239 |
} |
240 |
|
241 |
static PaError |
242 |
OpenStream(struct PaUtilHostApiRepresentation *hostApi, |
243 |
PaStream **stream, |
244 |
const PaStreamParameters *inputPar, |
245 |
const PaStreamParameters *outputPar, |
246 |
double sampleRate, |
247 |
unsigned long framesPerBuffer, |
248 |
PaStreamFlags streamFlags, |
249 |
PaStreamCallback *streamCallback, |
250 |
void *userData) |
251 |
{ |
252 |
PaSndioHostApiRepresentation *sndioHostApi = (PaSndioHostApiRepresentation *)hostApi; |
253 |
PaSndioStream *s; |
254 |
PaError err; |
255 |
struct sio_hdl *hdl; |
256 |
struct sio_par par; |
257 |
unsigned mode; |
258 |
int inch, onch; |
259 |
PaSampleFormat ifmt, ofmt, siofmt; |
260 |
const char *dev; |
261 |
|
262 |
DPR("OpenStream:\n"); |
263 |
|
264 |
mode = 0; |
265 |
inch = onch = 0; |
266 |
ifmt = ofmt = 0; |
267 |
sio_initpar(&par); |
268 |
|
269 |
if (outputPar && outputPar->channelCount > 0) { |
270 |
if (outputPar->device >= sndioHostApi->base.info.deviceCount) { |
271 |
DPR("OpenStream: %d: bad output device\n", outputPar->device); |
272 |
return paInvalidDevice; |
273 |
} |
274 |
if (outputPar->hostApiSpecificStreamInfo) { |
275 |
DPR("OpenStream: output specific info\n"); |
276 |
return paIncompatibleHostApiSpecificStreamInfo; |
277 |
} |
278 |
if (!sndioSetFmt(&par, outputPar->sampleFormat)) { |
279 |
return paSampleFormatNotSupported; |
280 |
} |
281 |
ofmt = outputPar->sampleFormat; |
282 |
onch = par.pchan = outputPar->channelCount; |
283 |
mode |= SIO_PLAY; |
284 |
} |
285 |
if (inputPar && inputPar->channelCount > 0) { |
286 |
if (inputPar->device >= sndioHostApi->base.info.deviceCount) { |
287 |
DPR("OpenStream: %d: bad input device\n", inputPar->device); |
288 |
return paInvalidDevice; |
289 |
} |
290 |
if (inputPar->hostApiSpecificStreamInfo) { |
291 |
DPR("OpenStream: input specific info\n"); |
292 |
return paIncompatibleHostApiSpecificStreamInfo; |
293 |
} |
294 |
if (!sndioSetFmt(&par, inputPar->sampleFormat)) { |
295 |
return paSampleFormatNotSupported; |
296 |
} |
297 |
ifmt = inputPar->sampleFormat; |
298 |
inch = par.rchan = inputPar->channelCount; |
299 |
mode |= SIO_REC; |
300 |
} |
301 |
par.rate = sampleRate; |
302 |
if (framesPerBuffer != paFramesPerBufferUnspecified) |
303 |
par.round = framesPerBuffer; |
304 |
|
305 |
DPR("OpenStream: mode = %x, trying rate = %u\n", mode, par.rate); |
306 |
|
307 |
if (outputPar) { |
308 |
dev = sndioHostApi->device_info[outputPar->device].name; |
309 |
} else if (inputPar) { |
310 |
dev = sndioHostApi->device_info[inputPar->device].name; |
311 |
} else { |
312 |
return paUnanticipatedHostError; |
313 |
} |
314 |
hdl = sio_open(dev, mode, 0); |
315 |
if (hdl == NULL) |
316 |
return paUnanticipatedHostError; |
317 |
if (!sio_setpar(hdl, &par)) { |
318 |
sio_close(hdl); |
319 |
return paUnanticipatedHostError; |
320 |
} |
321 |
if (!sio_getpar(hdl, &par)) { |
322 |
sio_close(hdl); |
323 |
return paUnanticipatedHostError; |
324 |
} |
325 |
if (!sndioGetFmt(&par, &siofmt)) { |
326 |
sio_close(hdl); |
327 |
return paSampleFormatNotSupported; |
328 |
} |
329 |
if ((mode & SIO_REC) && par.rchan != inputPar->channelCount) { |
330 |
DPR("OpenStream: rchan(%u) != %d\n", par.rchan, inputPar->channelCount); |
331 |
sio_close(hdl); |
332 |
return paInvalidChannelCount; |
333 |
} |
334 |
if ((mode & SIO_PLAY) && par.pchan != outputPar->channelCount) { |
335 |
DPR("OpenStream: pchan(%u) != %d\n", par.pchan, outputPar->channelCount); |
336 |
sio_close(hdl); |
337 |
return paInvalidChannelCount; |
338 |
} |
339 |
if ((double)par.rate < sampleRate * 0.995 || |
340 |
(double)par.rate > sampleRate * 1.005) { |
341 |
DPR("OpenStream: rate(%u) != %g\n", par.rate, sampleRate); |
342 |
sio_close(hdl); |
343 |
return paInvalidSampleRate; |
344 |
} |
345 |
|
346 |
s = (PaSndioStream *)PaUtil_AllocateMemory(sizeof(PaSndioStream)); |
347 |
if (s == NULL) { |
348 |
sio_close(hdl); |
349 |
return paInsufficientMemory; |
350 |
} |
351 |
PaUtil_InitializeStreamRepresentation(&s->base, |
352 |
streamCallback ? &sndioHostApi->callback : &sndioHostApi->blocking, |
353 |
streamCallback, userData); |
354 |
DPR("inch = %d, onch = %d, ifmt = %x, ofmt = %x\n", |
355 |
inch, onch, ifmt, ofmt); |
356 |
err = PaUtil_InitializeBufferProcessor(&s->bufproc, |
357 |
inch, ifmt, siofmt, |
358 |
onch, ofmt, siofmt, |
359 |
sampleRate, |
360 |
streamFlags, |
361 |
framesPerBuffer, |
362 |
par.round, |
363 |
paUtilFixedHostBufferSize, |
364 |
streamCallback, userData); |
365 |
if (err) { |
366 |
DPR("OpenStream: PaUtil_InitializeBufferProcessor failed\n"); |
367 |
PaUtil_FreeMemory(s); |
368 |
sio_close(hdl); |
369 |
return err; |
370 |
} |
371 |
if (mode & SIO_REC) { |
372 |
s->rbuf = malloc(par.round * par.rchan * par.bps); |
373 |
if (s->rbuf == NULL) { |
374 |
DPR("OpenStream: failed to allocate rbuf\n"); |
375 |
PaUtil_FreeMemory(s); |
376 |
sio_close(hdl); |
377 |
return paInsufficientMemory; |
378 |
} |
379 |
} |
380 |
if (mode & SIO_PLAY) { |
381 |
s->wbuf = malloc(par.round * par.pchan * par.bps); |
382 |
if (s->wbuf == NULL) { |
383 |
DPR("OpenStream: failed to allocate wbuf\n"); |
384 |
free(s->rbuf); |
385 |
PaUtil_FreeMemory(s); |
386 |
sio_close(hdl); |
387 |
return paInsufficientMemory; |
388 |
} |
389 |
} |
390 |
s->base.streamInfo.inputLatency = 0; |
391 |
s->base.streamInfo.outputLatency = (mode & SIO_PLAY) ? |
392 |
(double)(par.bufsz + PaUtil_GetBufferProcessorOutputLatencyFrames(&s->bufproc)) / (double)par.rate : 0; |
393 |
s->base.streamInfo.sampleRate = par.rate; |
394 |
s->active = 0; |
395 |
s->stopped = 1; |
396 |
s->mode = mode; |
397 |
s->hdl = hdl; |
398 |
s->par = par; |
399 |
*stream = s; |
400 |
DPR("OpenStream: done\n"); |
401 |
return paNoError; |
402 |
} |
403 |
|
404 |
static PaError |
405 |
BlockingReadStream(PaStream *stream, void *data, unsigned long numFrames) |
406 |
{ |
407 |
PaSndioStream *s = (PaSndioStream *)stream; |
408 |
unsigned n, res, todo; |
409 |
void *buf; |
410 |
|
411 |
while (numFrames > 0) { |
412 |
n = s->par.round; |
413 |
if (n > numFrames) |
414 |
n = numFrames; |
415 |
buf = s->rbuf; |
416 |
todo = n * s->par.rchan * s->par.bps; |
417 |
while (todo > 0) { |
418 |
res = sio_read(s->hdl, buf, todo); |
419 |
if (res == 0) |
420 |
return paUnanticipatedHostError; |
421 |
buf = (char *)buf + res; |
422 |
todo -= res; |
423 |
} |
424 |
s->rpos += n; |
425 |
PaUtil_SetInputFrameCount(&s->bufproc, n); |
426 |
PaUtil_SetInterleavedInputChannels(&s->bufproc, 0, s->rbuf, s->par.rchan); |
427 |
res = PaUtil_CopyInput(&s->bufproc, &data, n); |
428 |
if (res != n) { |
429 |
DPR("BlockingReadStream: copyInput: %u != %u\n"); |
430 |
return paUnanticipatedHostError; |
431 |
} |
432 |
numFrames -= n; |
433 |
} |
434 |
return paNoError; |
435 |
} |
436 |
|
437 |
static PaError |
438 |
BlockingWriteStream(PaStream* stream, const void *data, unsigned long numFrames) |
439 |
{ |
440 |
PaSndioStream *s = (PaSndioStream *)stream; |
441 |
unsigned n, res; |
442 |
|
443 |
while (numFrames > 0) { |
444 |
n = s->par.round; |
445 |
if (n > numFrames) |
446 |
n = numFrames; |
447 |
PaUtil_SetOutputFrameCount(&s->bufproc, n); |
448 |
PaUtil_SetInterleavedOutputChannels(&s->bufproc, 0, s->wbuf, s->par.pchan); |
449 |
res = PaUtil_CopyOutput(&s->bufproc, &data, n); |
450 |
if (res != n) { |
451 |
DPR("BlockingWriteStream: copyOutput: %u != %u\n"); |
452 |
return paUnanticipatedHostError; |
453 |
} |
454 |
res = sio_write(s->hdl, s->wbuf, n * s->par.pchan * s->par.bps); |
455 |
if (res == 0) |
456 |
return paUnanticipatedHostError; |
457 |
s->wpos += n; |
458 |
numFrames -= n; |
459 |
} |
460 |
return paNoError; |
461 |
} |
462 |
|
463 |
static signed long |
464 |
BlockingGetStreamReadAvailable(PaStream *stream) |
465 |
{ |
466 |
PaSndioStream *s = (PaSndioStream *)stream; |
467 |
struct pollfd pfd; |
468 |
int n, events; |
469 |
|
470 |
n = sio_pollfd(s->hdl, &pfd, POLLIN); |
471 |
while (poll(&pfd, n, 0) < 0) { |
472 |
if (errno == EINTR) |
473 |
continue; |
474 |
perror("poll"); |
475 |
abort(); |
476 |
} |
477 |
events = sio_revents(s->hdl, &pfd); |
478 |
if (!(events & POLLIN)) |
479 |
return 0; |
480 |
|
481 |
return s->realpos - s->rpos; |
482 |
} |
483 |
|
484 |
static signed long |
485 |
BlockingGetStreamWriteAvailable(PaStream *stream) |
486 |
{ |
487 |
PaSndioStream *s = (PaSndioStream *)stream; |
488 |
struct pollfd pfd; |
489 |
int n, events; |
490 |
|
491 |
n = sio_pollfd(s->hdl, &pfd, POLLOUT); |
492 |
while (poll(&pfd, n, 0) < 0) { |
493 |
if (errno == EINTR) |
494 |
continue; |
495 |
perror("poll"); |
496 |
abort(); |
497 |
} |
498 |
events = sio_revents(s->hdl, &pfd); |
499 |
if (!(events & POLLOUT)) |
500 |
return 0; |
501 |
|
502 |
return s->par.bufsz - (s->wpos - s->realpos); |
503 |
} |
504 |
|
505 |
static PaError |
506 |
BlockingWaitEmpty( PaStream *stream ) |
507 |
{ |
508 |
PaSndioStream *s = (PaSndioStream *)stream; |
509 |
|
510 |
/* |
511 |
* drain playback buffers; sndio always does it in background |
512 |
* and there is no way to wait for completion |
513 |
*/ |
514 |
DPR("BlockingWaitEmpty: s=%d, a=%d\n", s->stopped, s->active); |
515 |
|
516 |
return paNoError; |
517 |
} |
518 |
|
519 |
static PaError |
520 |
StartStream(PaStream *stream) |
521 |
{ |
522 |
PaSndioStream *s = (PaSndioStream *)stream; |
523 |
unsigned primes, wblksz; |
524 |
int err; |
525 |
|
526 |
DPR("StartStream: s=%d, a=%d\n", s->stopped, s->active); |
527 |
|
528 |
if (!s->stopped) { |
529 |
DPR("StartStream: already started\n"); |
530 |
return paNoError; |
531 |
} |
532 |
s->stopped = 0; |
533 |
s->active = 1; |
534 |
s->realpos = 0; |
535 |
s->wpos = 0; |
536 |
s->rpos = 0; |
537 |
PaUtil_ResetBufferProcessor(&s->bufproc); |
538 |
if (!sio_start(s->hdl)) |
539 |
return paUnanticipatedHostError; |
540 |
|
541 |
/* |
542 |
* send a complete buffer of silence |
543 |
*/ |
544 |
if (s->mode & SIO_PLAY) { |
545 |
wblksz = s->par.round * s->par.pchan * s->par.bps; |
546 |
memset(s->wbuf, 0, wblksz); |
547 |
for (primes = s->par.bufsz / s->par.round; primes > 0; primes--) |
548 |
s->wpos += sio_write(s->hdl, s->wbuf, wblksz); |
549 |
} |
550 |
if (s->base.streamCallback) { |
551 |
err = pthread_create(&s->thread, NULL, sndioThread, s); |
552 |
if (err) { |
553 |
DPR("SndioStartStream: couldn't create thread\n"); |
554 |
return paUnanticipatedHostError; |
555 |
} |
556 |
DPR("StartStream: started...\n"); |
557 |
} |
558 |
return paNoError; |
559 |
} |
560 |
|
561 |
static PaError |
562 |
StopStream(PaStream *stream) |
563 |
{ |
564 |
PaSndioStream *s = (PaSndioStream *)stream; |
565 |
void *ret; |
566 |
int err; |
567 |
|
568 |
DPR("StopStream: s=%d, a=%d\n", s->stopped, s->active); |
569 |
|
570 |
if (s->stopped) { |
571 |
DPR("StartStream: already started\n"); |
572 |
return paNoError; |
573 |
} |
574 |
s->stopped = 1; |
575 |
if (s->base.streamCallback) { |
576 |
err = pthread_join(s->thread, &ret); |
577 |
if (err) { |
578 |
DPR("SndioStop: couldn't join thread\n"); |
579 |
return paUnanticipatedHostError; |
580 |
} |
581 |
} |
582 |
if (!sio_stop(s->hdl)) |
583 |
return paUnanticipatedHostError; |
584 |
return paNoError; |
585 |
} |
586 |
|
587 |
static PaError |
588 |
CloseStream(PaStream *stream) |
589 |
{ |
590 |
PaSndioStream *s = (PaSndioStream *)stream; |
591 |
|
592 |
DPR("CloseStream:\n"); |
593 |
|
594 |
if (!s->stopped) |
595 |
StopStream(stream); |
596 |
|
597 |
if (s->mode & SIO_REC) |
598 |
free(s->rbuf); |
599 |
if (s->mode & SIO_PLAY) |
600 |
free(s->wbuf); |
601 |
sio_close(s->hdl); |
602 |
PaUtil_TerminateStreamRepresentation(&s->base); |
603 |
PaUtil_TerminateBufferProcessor(&s->bufproc); |
604 |
PaUtil_FreeMemory(s); |
605 |
return paNoError; |
606 |
} |
607 |
|
608 |
static PaError |
609 |
AbortStream(PaStream *stream) |
610 |
{ |
611 |
DPR("AbortStream:\n"); |
612 |
|
613 |
return StopStream(stream); |
614 |
} |
615 |
|
616 |
static PaError |
617 |
IsStreamStopped(PaStream *stream) |
618 |
{ |
619 |
PaSndioStream *s = (PaSndioStream *)stream; |
620 |
|
621 |
//DPR("IsStreamStopped: s=%d, a=%d\n", s->stopped, s->active); |
622 |
|
623 |
return s->stopped; |
624 |
} |
625 |
|
626 |
static PaError |
627 |
IsStreamActive(PaStream *stream) |
628 |
{ |
629 |
PaSndioStream *s = (PaSndioStream *)stream; |
630 |
|
631 |
//DPR("IsStreamActive: s=%d, a=%d\n", s->stopped, s->active); |
632 |
|
633 |
return s->active; |
634 |
} |
635 |
|
636 |
static PaTime |
637 |
GetStreamTime(PaStream *stream) |
638 |
{ |
639 |
PaSndioStream *s = (PaSndioStream *)stream; |
640 |
|
641 |
return (double)s->realpos / s->base.streamInfo.sampleRate; |
642 |
} |
643 |
|
644 |
static PaError |
645 |
IsFormatSupported(struct PaUtilHostApiRepresentation *hostApi, |
646 |
const PaStreamParameters *inputPar, |
647 |
const PaStreamParameters *outputPar, |
648 |
double sampleRate) |
649 |
{ |
650 |
return paFormatIsSupported; |
651 |
} |
652 |
|
653 |
static void |
654 |
Terminate(struct PaUtilHostApiRepresentation *hostApi) |
655 |
{ |
656 |
PaSndioHostApiRepresentation *sndioHostApi; |
657 |
sndioHostApi = (PaSndioHostApiRepresentation *)hostApi; |
658 |
free(sndioHostApi->audiodevices); |
659 |
PaUtil_FreeMemory(hostApi); |
660 |
} |
661 |
|
662 |
static void |
663 |
InitDeviceInfo(PaDeviceInfo *info, PaHostApiIndex hostApiIndex, const char *name) |
664 |
{ |
665 |
info->structVersion = 2; |
666 |
info->name = name; |
667 |
info->hostApi = hostApiIndex; |
668 |
info->maxInputChannels = 128; |
669 |
info->maxOutputChannels = 128; |
670 |
info->defaultLowInputLatency = 0.01; |
671 |
info->defaultLowOutputLatency = 0.01; |
672 |
info->defaultHighInputLatency = 0.5; |
673 |
info->defaultHighOutputLatency = 0.5; |
674 |
info->defaultSampleRate = 48000; |
675 |
} |
676 |
|
677 |
PaError |
678 |
PaSndio_Initialize(PaUtilHostApiRepresentation **hostApi, PaHostApiIndex hostApiIndex) |
679 |
{ |
680 |
PaSndioHostApiRepresentation *sndioHostApi; |
681 |
PaDeviceInfo *info; |
682 |
struct sio_hdl *hdl; |
683 |
char *audiodevices; |
684 |
char *device; |
685 |
size_t deviceCount; |
686 |
|
687 |
DPR("PaSndio_Initialize: initializing...\n"); |
688 |
|
689 |
/* unusable APIs should return paNoError and a NULL hostApi */ |
690 |
*hostApi = NULL; |
691 |
|
692 |
sndioHostApi = PaUtil_AllocateMemory(sizeof(PaSndioHostApiRepresentation)); |
693 |
if (sndioHostApi == NULL) |
694 |
return paNoError; |
695 |
|
696 |
// Add default device |
697 |
info = &sndioHostApi->device_info[0]; |
698 |
InitDeviceInfo(info, hostApiIndex, SIO_DEVANY); |
699 |
sndioHostApi->infos[0] = info; |
700 |
deviceCount = 1; |
701 |
|
702 |
// Add additional devices as specified in the PA_SNDIO_AUDIODEVICES |
703 |
// environment variable as a colon separated list |
704 |
sndioHostApi->audiodevices = NULL; |
705 |
audiodevices = getenv("PA_SNDIO_AUDIODEVICES"); |
706 |
if (audiodevices != NULL) { |
707 |
sndioHostApi->audiodevices = strdup(audiodevices); |
708 |
if (sndioHostApi->audiodevices == NULL) |
709 |
return paNoError; |
710 |
|
711 |
audiodevices = sndioHostApi->audiodevices; |
712 |
while ((device = strsep(&audiodevices, ":")) != NULL && |
713 |
deviceCount < PA_SNDIO_AUDIODEVICES_MAX) { |
714 |
if (*device == '\0') |
715 |
continue; |
716 |
info = &sndioHostApi->device_info[deviceCount]; |
717 |
InitDeviceInfo(info, hostApiIndex, device); |
718 |
sndioHostApi->infos[deviceCount] = info; |
719 |
deviceCount++; |
720 |
} |
721 |
} |
722 |
|
723 |
*hostApi = &sndioHostApi->base; |
724 |
(*hostApi)->info.structVersion = 1; |
725 |
(*hostApi)->info.type = paSndio; |
726 |
(*hostApi)->info.name = "sndio"; |
727 |
(*hostApi)->info.deviceCount = deviceCount; |
728 |
(*hostApi)->info.defaultInputDevice = 0; |
729 |
(*hostApi)->info.defaultOutputDevice = 0; |
730 |
(*hostApi)->deviceInfos = sndioHostApi->infos; |
731 |
(*hostApi)->Terminate = Terminate; |
732 |
(*hostApi)->OpenStream = OpenStream; |
733 |
(*hostApi)->IsFormatSupported = IsFormatSupported; |
734 |
|
735 |
PaUtil_InitializeStreamInterface(&sndioHostApi->blocking, |
736 |
CloseStream, |
737 |
StartStream, |
738 |
StopStream, |
739 |
AbortStream, |
740 |
IsStreamStopped, |
741 |
IsStreamActive, |
742 |
GetStreamTime, |
743 |
PaUtil_DummyGetCpuLoad, |
744 |
BlockingReadStream, |
745 |
BlockingWriteStream, |
746 |
BlockingGetStreamReadAvailable, |
747 |
BlockingGetStreamWriteAvailable); |
748 |
|
749 |
PaUtil_InitializeStreamInterface(&sndioHostApi->callback, |
750 |
CloseStream, |
751 |
StartStream, |
752 |
StopStream, |
753 |
AbortStream, |
754 |
IsStreamStopped, |
755 |
IsStreamActive, |
756 |
GetStreamTime, |
757 |
PaUtil_DummyGetCpuLoad, |
758 |
PaUtil_DummyRead, |
759 |
PaUtil_DummyWrite, |
760 |
PaUtil_DummyGetReadAvailable, |
761 |
PaUtil_DummyGetWriteAvailable); |
762 |
|
763 |
DPR("PaSndio_Initialize: done\n"); |
764 |
return paNoError; |
765 |
} |