Added
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, so expose only |
65 |
* the default device, the user will have a chance to change it |
66 |
* using the environment variable |
67 |
*/ |
68 |
PaDeviceInfo *infos[1], default_info; |
69 |
} PaSndioHostApiRepresentation; |
70 |
|
71 |
/* |
72 |
* callback invoked when blocks are processed by the hardware |
73 |
*/ |
74 |
static void |
75 |
sndioOnMove(void *addr, int delta) |
76 |
{ |
77 |
PaSndioStream *s = (PaSndioStream *)addr; |
78 |
|
79 |
s->realpos += delta; |
80 |
} |
81 |
|
82 |
/* |
83 |
* convert PA encoding to sndio encoding, return true on success |
84 |
*/ |
85 |
static int |
86 |
sndioSetFmt(struct sio_par *sio, PaSampleFormat fmt) |
87 |
{ |
88 |
switch (fmt & ~paNonInterleaved) { |
89 |
case paInt32: |
90 |
sio->sig = 1; |
91 |
sio->bits = 32; |
92 |
break; |
93 |
case paInt24: |
94 |
sio->sig = 1; |
95 |
sio->bits = 24; |
96 |
sio->bps = 3; /* paInt24 is packed format */ |
97 |
break; |
98 |
case paInt16: |
99 |
case paFloat32: |
100 |
sio->sig = 1; |
101 |
sio->bits = 16; |
102 |
break; |
103 |
case paInt8: |
104 |
sio->sig = 1; |
105 |
sio->bits = 8; |
106 |
break; |
107 |
case paUInt8: |
108 |
sio->sig = 0; |
109 |
sio->bits = 8; |
110 |
break; |
111 |
default: |
112 |
DPR("sndioSetFmt: %x: unsupported\n", fmt); |
113 |
return 0; |
114 |
} |
115 |
sio->le = SIO_LE_NATIVE; |
116 |
return 1; |
117 |
} |
118 |
|
119 |
/* |
120 |
* convert sndio encoding to PA encoding, return true on success |
121 |
*/ |
122 |
static int |
123 |
sndioGetFmt(struct sio_par *sio, PaSampleFormat *fmt) |
124 |
{ |
125 |
if ((sio->bps * 8 != sio->bits && !sio->msb) || |
126 |
(sio->bps > 1 && sio->le != SIO_LE_NATIVE)) { |
127 |
DPR("sndioGetFmt: bits = %u, le = %u, msb = %u, bps = %u\n", |
128 |
sio->bits, sio->le, sio->msb, sio->bps); |
129 |
return 0; |
130 |
} |
131 |
|
132 |
switch (sio->bits) { |
133 |
case 32: |
134 |
if (!sio->sig) |
135 |
return 0; |
136 |
*fmt = paInt32; |
137 |
break; |
138 |
case 24: |
139 |
if (!sio->sig) |
140 |
return 0; |
141 |
*fmt = (sio->bps == 3) ? paInt24 : paInt32; |
142 |
break; |
143 |
case 16: |
144 |
if (!sio->sig) |
145 |
return 0; |
146 |
*fmt = paInt16; |
147 |
break; |
148 |
case 8: |
149 |
*fmt = sio->sig ? paInt8 : paUInt8; |
150 |
break; |
151 |
default: |
152 |
DPR("sndioGetFmt: %u: unsupported\n", sio->bits); |
153 |
return 0; |
154 |
} |
155 |
return 1; |
156 |
} |
157 |
|
158 |
/* |
159 |
* I/O loop for callback interface |
160 |
*/ |
161 |
static void * |
162 |
sndioThread(void *arg) |
163 |
{ |
164 |
PaSndioStream *s = (PaSndioStream *)arg; |
165 |
PaStreamCallbackTimeInfo ti; |
166 |
unsigned char *data; |
167 |
unsigned todo, rblksz, wblksz; |
168 |
int n, result; |
169 |
|
170 |
rblksz = s->par.round * s->par.rchan * s->par.bps; |
171 |
wblksz = s->par.round * s->par.pchan * s->par.bps; |
172 |
|
173 |
DPR("sndioThread: mode = %x, round = %u, rblksz = %u, wblksz = %u\n", |
174 |
s->mode, s->par.round, rblksz, wblksz); |
175 |
|
176 |
while (!s->stopped) { |
177 |
if (s->mode & SIO_REC) { |
178 |
todo = rblksz; |
179 |
data = s->rbuf; |
180 |
while (todo > 0) { |
181 |
n = sio_read(s->hdl, data, todo); |
182 |
if (n == 0) { |
183 |
DPR("sndioThread: sio_read failed\n"); |
184 |
goto failed; |
185 |
} |
186 |
todo -= n; |
187 |
data += n; |
188 |
} |
189 |
s->rpos += s->par.round; |
190 |
ti.inputBufferAdcTime = |
191 |
(double)s->realpos / s->par.rate; |
192 |
} |
193 |
if (s->mode & SIO_PLAY) { |
194 |
ti.outputBufferDacTime = |
195 |
(double)(s->realpos + s->par.bufsz) / s->par.rate; |
196 |
} |
197 |
ti.currentTime = s->realpos / (double)s->par.rate; |
198 |
PaUtil_BeginBufferProcessing(&s->bufproc, &ti, 0); |
199 |
if (s->mode & SIO_PLAY) { |
200 |
PaUtil_SetOutputFrameCount(&s->bufproc, s->par.round); |
201 |
PaUtil_SetInterleavedOutputChannels(&s->bufproc, |
202 |
0, s->wbuf, s->par.pchan); |
203 |
} |
204 |
if (s->mode & SIO_REC) { |
205 |
PaUtil_SetInputFrameCount(&s->bufproc, s->par.round); |
206 |
PaUtil_SetInterleavedInputChannels(&s->bufproc, |
207 |
0, s->rbuf, s->par.rchan); |
208 |
} |
209 |
result = paContinue; |
210 |
n = PaUtil_EndBufferProcessing(&s->bufproc, &result); |
211 |
if (n != s->par.round) { |
212 |
DPR("sndioThread: %d < %u frames, result = %d\n", |
213 |
n, s->par.round, result); |
214 |
} |
215 |
if (result != paContinue) { |
216 |
break; |
217 |
} |
218 |
if (s->mode & SIO_PLAY) { |
219 |
n = sio_write(s->hdl, s->wbuf, wblksz); |
220 |
if (n < wblksz) { |
221 |
DPR("sndioThread: sio_write failed\n"); |
222 |
goto failed; |
223 |
} |
224 |
s->wpos += s->par.round; |
225 |
} |
226 |
} |
227 |
failed: |
228 |
s->active = 0; |
229 |
DPR("sndioThread: done\n"); |
230 |
} |
231 |
|
232 |
static PaError |
233 |
OpenStream(struct PaUtilHostApiRepresentation *hostApi, |
234 |
PaStream **stream, |
235 |
const PaStreamParameters *inputPar, |
236 |
const PaStreamParameters *outputPar, |
237 |
double sampleRate, |
238 |
unsigned long framesPerBuffer, |
239 |
PaStreamFlags streamFlags, |
240 |
PaStreamCallback *streamCallback, |
241 |
void *userData) |
242 |
{ |
243 |
PaSndioHostApiRepresentation *sndioHostApi = (PaSndioHostApiRepresentation *)hostApi; |
244 |
PaSndioStream *s; |
245 |
PaError err; |
246 |
struct sio_hdl *hdl; |
247 |
struct sio_par par; |
248 |
unsigned mode; |
249 |
int inch, onch; |
250 |
PaSampleFormat ifmt, ofmt, siofmt; |
251 |
|
252 |
DPR("OpenStream:\n"); |
253 |
|
254 |
mode = 0; |
255 |
inch = onch = 0; |
256 |
ifmt = ofmt = 0; |
257 |
sio_initpar(&par); |
258 |
|
259 |
if (outputPar && outputPar->channelCount > 0) { |
260 |
if (outputPar->device != 0) { |
261 |
DPR("OpenStream: %d: bad output device\n", outputPar->device); |
262 |
return paInvalidDevice; |
263 |
} |
264 |
if (outputPar->hostApiSpecificStreamInfo) { |
265 |
DPR("OpenStream: output specific info\n"); |
266 |
return paIncompatibleHostApiSpecificStreamInfo; |
267 |
} |
268 |
if (!sndioSetFmt(&par, outputPar->sampleFormat)) { |
269 |
return paSampleFormatNotSupported; |
270 |
} |
271 |
ofmt = outputPar->sampleFormat; |
272 |
onch = par.pchan = outputPar->channelCount; |
273 |
mode |= SIO_PLAY; |
274 |
} |
275 |
if (inputPar && inputPar->channelCount > 0) { |
276 |
if (inputPar->device != 0) { |
277 |
DPR("OpenStream: %d: bad input device\n", inputPar->device); |
278 |
return paInvalidDevice; |
279 |
} |
280 |
if (inputPar->hostApiSpecificStreamInfo) { |
281 |
DPR("OpenStream: input specific info\n"); |
282 |
return paIncompatibleHostApiSpecificStreamInfo; |
283 |
} |
284 |
if (!sndioSetFmt(&par, inputPar->sampleFormat)) { |
285 |
return paSampleFormatNotSupported; |
286 |
} |
287 |
ifmt = inputPar->sampleFormat; |
288 |
inch = par.rchan = inputPar->channelCount; |
289 |
mode |= SIO_REC; |
290 |
} |
291 |
par.rate = sampleRate; |
292 |
if (framesPerBuffer != paFramesPerBufferUnspecified) |
293 |
par.round = framesPerBuffer; |
294 |
|
295 |
DPR("OpenStream: mode = %x, trying rate = %u\n", mode, par.rate); |
296 |
|
297 |
hdl = sio_open(SIO_DEVANY, mode, 0); |
298 |
if (hdl == NULL) |
299 |
return paUnanticipatedHostError; |
300 |
if (!sio_setpar(hdl, &par)) { |
301 |
sio_close(hdl); |
302 |
return paUnanticipatedHostError; |
303 |
} |
304 |
if (!sio_getpar(hdl, &par)) { |
305 |
sio_close(hdl); |
306 |
return paUnanticipatedHostError; |
307 |
} |
308 |
if (!sndioGetFmt(&par, &siofmt)) { |
309 |
sio_close(hdl); |
310 |
return paSampleFormatNotSupported; |
311 |
} |
312 |
if ((mode & SIO_REC) && par.rchan != inputPar->channelCount) { |
313 |
DPR("OpenStream: rchan(%u) != %d\n", par.rchan, inputPar->channelCount); |
314 |
sio_close(hdl); |
315 |
return paInvalidChannelCount; |
316 |
} |
317 |
if ((mode & SIO_PLAY) && par.pchan != outputPar->channelCount) { |
318 |
DPR("OpenStream: pchan(%u) != %d\n", par.pchan, outputPar->channelCount); |
319 |
sio_close(hdl); |
320 |
return paInvalidChannelCount; |
321 |
} |
322 |
if ((double)par.rate < sampleRate * 0.995 || |
323 |
(double)par.rate > sampleRate * 1.005) { |
324 |
DPR("OpenStream: rate(%u) != %g\n", par.rate, sampleRate); |
325 |
sio_close(hdl); |
326 |
return paInvalidSampleRate; |
327 |
} |
328 |
|
329 |
s = (PaSndioStream *)PaUtil_AllocateMemory(sizeof(PaSndioStream)); |
330 |
if (s == NULL) { |
331 |
sio_close(hdl); |
332 |
return paInsufficientMemory; |
333 |
} |
334 |
PaUtil_InitializeStreamRepresentation(&s->base, |
335 |
streamCallback ? &sndioHostApi->callback : &sndioHostApi->blocking, |
336 |
streamCallback, userData); |
337 |
DPR("inch = %d, onch = %d, ifmt = %x, ofmt = %x\n", |
338 |
inch, onch, ifmt, ofmt); |
339 |
err = PaUtil_InitializeBufferProcessor(&s->bufproc, |
340 |
inch, ifmt, siofmt, |
341 |
onch, ofmt, siofmt, |
342 |
sampleRate, |
343 |
streamFlags, |
344 |
framesPerBuffer, |
345 |
par.round, |
346 |
paUtilFixedHostBufferSize, |
347 |
streamCallback, userData); |
348 |
if (err) { |
349 |
DPR("OpenStream: PaUtil_InitializeBufferProcessor failed\n"); |
350 |
PaUtil_FreeMemory(s); |
351 |
sio_close(hdl); |
352 |
return err; |
353 |
} |
354 |
if (mode & SIO_REC) { |
355 |
s->rbuf = malloc(par.round * par.rchan * par.bps); |
356 |
if (s->rbuf == NULL) { |
357 |
DPR("OpenStream: failed to allocate rbuf\n"); |
358 |
PaUtil_FreeMemory(s); |
359 |
sio_close(hdl); |
360 |
return paInsufficientMemory; |
361 |
} |
362 |
} |
363 |
if (mode & SIO_PLAY) { |
364 |
s->wbuf = malloc(par.round * par.pchan * par.bps); |
365 |
if (s->wbuf == NULL) { |
366 |
DPR("OpenStream: failed to allocate wbuf\n"); |
367 |
free(s->rbuf); |
368 |
PaUtil_FreeMemory(s); |
369 |
sio_close(hdl); |
370 |
return paInsufficientMemory; |
371 |
} |
372 |
} |
373 |
s->base.streamInfo.inputLatency = 0; |
374 |
s->base.streamInfo.outputLatency = (mode & SIO_PLAY) ? |
375 |
(double)(par.bufsz + PaUtil_GetBufferProcessorOutputLatencyFrames(&s->bufproc)) / (double)par.rate : 0; |
376 |
s->base.streamInfo.sampleRate = par.rate; |
377 |
s->active = 0; |
378 |
s->stopped = 1; |
379 |
s->mode = mode; |
380 |
s->hdl = hdl; |
381 |
s->par = par; |
382 |
*stream = s; |
383 |
DPR("OpenStream: done\n"); |
384 |
return paNoError; |
385 |
} |
386 |
|
387 |
static PaError |
388 |
BlockingReadStream(PaStream *stream, void *data, unsigned long numFrames) |
389 |
{ |
390 |
PaSndioStream *s = (PaSndioStream *)stream; |
391 |
unsigned n, res, todo; |
392 |
void *buf; |
393 |
|
394 |
while (numFrames > 0) { |
395 |
n = s->par.round; |
396 |
if (n > numFrames) |
397 |
n = numFrames; |
398 |
buf = s->rbuf; |
399 |
todo = n * s->par.rchan * s->par.bps; |
400 |
while (todo > 0) { |
401 |
res = sio_read(s->hdl, buf, todo); |
402 |
if (res == 0) |
403 |
return paUnanticipatedHostError; |
404 |
buf = (char *)buf + res; |
405 |
todo -= res; |
406 |
} |
407 |
s->rpos += n; |
408 |
PaUtil_SetInputFrameCount(&s->bufproc, n); |
409 |
PaUtil_SetInterleavedInputChannels(&s->bufproc, 0, s->rbuf, s->par.rchan); |
410 |
res = PaUtil_CopyInput(&s->bufproc, &data, n); |
411 |
if (res != n) { |
412 |
DPR("BlockingReadStream: copyInput: %u != %u\n"); |
413 |
return paUnanticipatedHostError; |
414 |
} |
415 |
numFrames -= n; |
416 |
} |
417 |
return paNoError; |
418 |
} |
419 |
|
420 |
static PaError |
421 |
BlockingWriteStream(PaStream* stream, const void *data, unsigned long numFrames) |
422 |
{ |
423 |
PaSndioStream *s = (PaSndioStream *)stream; |
424 |
unsigned n, res; |
425 |
|
426 |
while (numFrames > 0) { |
427 |
n = s->par.round; |
428 |
if (n > numFrames) |
429 |
n = numFrames; |
430 |
PaUtil_SetOutputFrameCount(&s->bufproc, n); |
431 |
PaUtil_SetInterleavedOutputChannels(&s->bufproc, 0, s->wbuf, s->par.pchan); |
432 |
res = PaUtil_CopyOutput(&s->bufproc, &data, n); |
433 |
if (res != n) { |
434 |
DPR("BlockingWriteStream: copyOutput: %u != %u\n"); |
435 |
return paUnanticipatedHostError; |
436 |
} |
437 |
res = sio_write(s->hdl, s->wbuf, n * s->par.pchan * s->par.bps); |
438 |
if (res == 0) |
439 |
return paUnanticipatedHostError; |
440 |
s->wpos += n; |
441 |
numFrames -= n; |
442 |
} |
443 |
return paNoError; |
444 |
} |
445 |
|
446 |
static signed long |
447 |
BlockingGetStreamReadAvailable(PaStream *stream) |
448 |
{ |
449 |
PaSndioStream *s = (PaSndioStream *)stream; |
450 |
struct pollfd pfd; |
451 |
int n, events; |
452 |
|
453 |
n = sio_pollfd(s->hdl, &pfd, POLLIN); |
454 |
while (poll(&pfd, n, 0) < 0) { |
455 |
if (errno == EINTR) |
456 |
continue; |
457 |
perror("poll"); |
458 |
abort(); |
459 |
} |
460 |
events = sio_revents(s->hdl, &pfd); |
461 |
if (!(events & POLLIN)) |
462 |
return 0; |
463 |
|
464 |
return s->realpos - s->rpos; |
465 |
} |
466 |
|
467 |
static signed long |
468 |
BlockingGetStreamWriteAvailable(PaStream *stream) |
469 |
{ |
470 |
PaSndioStream *s = (PaSndioStream *)stream; |
471 |
struct pollfd pfd; |
472 |
int n, events; |
473 |
|
474 |
n = sio_pollfd(s->hdl, &pfd, POLLOUT); |
475 |
while (poll(&pfd, n, 0) < 0) { |
476 |
if (errno == EINTR) |
477 |
continue; |
478 |
perror("poll"); |
479 |
abort(); |
480 |
} |
481 |
events = sio_revents(s->hdl, &pfd); |
482 |
if (!(events & POLLOUT)) |
483 |
return 0; |
484 |
|
485 |
return s->par.bufsz - (s->wpos - s->realpos); |
486 |
} |
487 |
|
488 |
static PaError |
489 |
BlockingWaitEmpty( PaStream *stream ) |
490 |
{ |
491 |
PaSndioStream *s = (PaSndioStream *)stream; |
492 |
|
493 |
/* |
494 |
* drain playback buffers; sndio always does it in background |
495 |
* and there is no way to wait for completion |
496 |
*/ |
497 |
DPR("BlockingWaitEmpty: s=%d, a=%d\n", s->stopped, s->active); |
498 |
|
499 |
return paNoError; |
500 |
} |
501 |
|
502 |
static PaError |
503 |
StartStream(PaStream *stream) |
504 |
{ |
505 |
PaSndioStream *s = (PaSndioStream *)stream; |
506 |
unsigned primes, wblksz; |
507 |
int err; |
508 |
|
509 |
DPR("StartStream: s=%d, a=%d\n", s->stopped, s->active); |
510 |
|
511 |
if (!s->stopped) { |
512 |
DPR("StartStream: already started\n"); |
513 |
return paNoError; |
514 |
} |
515 |
s->stopped = 0; |
516 |
s->active = 1; |
517 |
s->realpos = 0; |
518 |
s->wpos = 0; |
519 |
s->rpos = 0; |
520 |
PaUtil_ResetBufferProcessor(&s->bufproc); |
521 |
if (!sio_start(s->hdl)) |
522 |
return paUnanticipatedHostError; |
523 |
|
524 |
/* |
525 |
* send a complete buffer of silence |
526 |
*/ |
527 |
if (s->mode & SIO_PLAY) { |
528 |
wblksz = s->par.round * s->par.pchan * s->par.bps; |
529 |
memset(s->wbuf, 0, wblksz); |
530 |
for (primes = s->par.bufsz / s->par.round; primes > 0; primes--) |
531 |
s->wpos += sio_write(s->hdl, s->wbuf, wblksz); |
532 |
} |
533 |
if (s->base.streamCallback) { |
534 |
err = pthread_create(&s->thread, NULL, sndioThread, s); |
535 |
if (err) { |
536 |
DPR("SndioStartStream: couldn't create thread\n"); |
537 |
return paUnanticipatedHostError; |
538 |
} |
539 |
DPR("StartStream: started...\n"); |
540 |
} |
541 |
return paNoError; |
542 |
} |
543 |
|
544 |
static PaError |
545 |
StopStream(PaStream *stream) |
546 |
{ |
547 |
PaSndioStream *s = (PaSndioStream *)stream; |
548 |
void *ret; |
549 |
int err; |
550 |
|
551 |
DPR("StopStream: s=%d, a=%d\n", s->stopped, s->active); |
552 |
|
553 |
if (s->stopped) { |
554 |
DPR("StartStream: already started\n"); |
555 |
return paNoError; |
556 |
} |
557 |
s->stopped = 1; |
558 |
if (s->base.streamCallback) { |
559 |
err = pthread_join(s->thread, &ret); |
560 |
if (err) { |
561 |
DPR("SndioStop: couldn't join thread\n"); |
562 |
return paUnanticipatedHostError; |
563 |
} |
564 |
} |
565 |
if (!sio_stop(s->hdl)) |
566 |
return paUnanticipatedHostError; |
567 |
return paNoError; |
568 |
} |
569 |
|
570 |
static PaError |
571 |
CloseStream(PaStream *stream) |
572 |
{ |
573 |
PaSndioStream *s = (PaSndioStream *)stream; |
574 |
|
575 |
DPR("CloseStream:\n"); |
576 |
|
577 |
if (!s->stopped) |
578 |
StopStream(stream); |
579 |
|
580 |
if (s->mode & SIO_REC) |
581 |
free(s->rbuf); |
582 |
if (s->mode & SIO_PLAY) |
583 |
free(s->wbuf); |
584 |
sio_close(s->hdl); |
585 |
PaUtil_TerminateStreamRepresentation(&s->base); |
586 |
PaUtil_TerminateBufferProcessor(&s->bufproc); |
587 |
PaUtil_FreeMemory(s); |
588 |
return paNoError; |
589 |
} |
590 |
|
591 |
static PaError |
592 |
AbortStream(PaStream *stream) |
593 |
{ |
594 |
DPR("AbortStream:\n"); |
595 |
|
596 |
return StopStream(stream); |
597 |
} |
598 |
|
599 |
static PaError |
600 |
IsStreamStopped(PaStream *stream) |
601 |
{ |
602 |
PaSndioStream *s = (PaSndioStream *)stream; |
603 |
|
604 |
//DPR("IsStreamStopped: s=%d, a=%d\n", s->stopped, s->active); |
605 |
|
606 |
return s->stopped; |
607 |
} |
608 |
|
609 |
static PaError |
610 |
IsStreamActive(PaStream *stream) |
611 |
{ |
612 |
PaSndioStream *s = (PaSndioStream *)stream; |
613 |
|
614 |
//DPR("IsStreamActive: s=%d, a=%d\n", s->stopped, s->active); |
615 |
|
616 |
return s->active; |
617 |
} |
618 |
|
619 |
static PaTime |
620 |
GetStreamTime(PaStream *stream) |
621 |
{ |
622 |
PaSndioStream *s = (PaSndioStream *)stream; |
623 |
|
624 |
return (double)s->realpos / s->base.streamInfo.sampleRate; |
625 |
} |
626 |
|
627 |
static PaError |
628 |
IsFormatSupported(struct PaUtilHostApiRepresentation *hostApi, |
629 |
const PaStreamParameters *inputPar, |
630 |
const PaStreamParameters *outputPar, |
631 |
double sampleRate) |
632 |
{ |
633 |
return paFormatIsSupported; |
634 |
} |
635 |
|
636 |
static void |
637 |
Terminate(struct PaUtilHostApiRepresentation *hostApi) |
638 |
{ |
639 |
PaUtil_FreeMemory(hostApi); |
640 |
} |
641 |
|
642 |
PaError |
643 |
PaSndio_Initialize(PaUtilHostApiRepresentation **hostApi, PaHostApiIndex hostApiIndex) |
644 |
{ |
645 |
PaSndioHostApiRepresentation *sndioHostApi; |
646 |
PaDeviceInfo *info; |
647 |
struct sio_hdl *hdl; |
648 |
|
649 |
DPR("PaSndio_Initialize: initializing...\n"); |
650 |
|
651 |
/* unusable APIs should return paNoError and a NULL hostApi */ |
652 |
*hostApi = NULL; |
653 |
|
654 |
sndioHostApi = PaUtil_AllocateMemory(sizeof(PaSndioHostApiRepresentation)); |
655 |
if (sndioHostApi == NULL) |
656 |
return paNoError; |
657 |
|
658 |
info = &sndioHostApi->default_info; |
659 |
info->structVersion = 2; |
660 |
info->name = "default"; |
661 |
info->hostApi = hostApiIndex; |
662 |
info->maxInputChannels = 128; |
663 |
info->maxOutputChannels = 128; |
664 |
info->defaultLowInputLatency = 0.01; |
665 |
info->defaultLowOutputLatency = 0.01; |
666 |
info->defaultHighInputLatency = 0.5; |
667 |
info->defaultHighOutputLatency = 0.5; |
668 |
info->defaultSampleRate = 48000; |
669 |
sndioHostApi->infos[0] = info; |
670 |
|
671 |
*hostApi = &sndioHostApi->base; |
672 |
(*hostApi)->info.structVersion = 1; |
673 |
(*hostApi)->info.type = paSndio; |
674 |
(*hostApi)->info.name = "sndio"; |
675 |
(*hostApi)->info.deviceCount = 1; |
676 |
(*hostApi)->info.defaultInputDevice = 0; |
677 |
(*hostApi)->info.defaultOutputDevice = 0; |
678 |
(*hostApi)->deviceInfos = sndioHostApi->infos; |
679 |
(*hostApi)->Terminate = Terminate; |
680 |
(*hostApi)->OpenStream = OpenStream; |
681 |
(*hostApi)->IsFormatSupported = IsFormatSupported; |
682 |
|
683 |
PaUtil_InitializeStreamInterface(&sndioHostApi->blocking, |
684 |
CloseStream, |
685 |
StartStream, |
686 |
StopStream, |
687 |
AbortStream, |
688 |
IsStreamStopped, |
689 |
IsStreamActive, |
690 |
GetStreamTime, |
691 |
PaUtil_DummyGetCpuLoad, |
692 |
BlockingReadStream, |
693 |
BlockingWriteStream, |
694 |
BlockingGetStreamReadAvailable, |
695 |
BlockingGetStreamWriteAvailable); |
696 |
|
697 |
PaUtil_InitializeStreamInterface(&sndioHostApi->callback, |
698 |
CloseStream, |
699 |
StartStream, |
700 |
StopStream, |
701 |
AbortStream, |
702 |
IsStreamStopped, |
703 |
IsStreamActive, |
704 |
GetStreamTime, |
705 |
PaUtil_DummyGetCpuLoad, |
706 |
PaUtil_DummyRead, |
707 |
PaUtil_DummyWrite, |
708 |
PaUtil_DummyGetReadAvailable, |
709 |
PaUtil_DummyGetWriteAvailable); |
710 |
|
711 |
DPR("PaSndio_Initialize: done\n"); |
712 |
return paNoError; |
713 |
} |