logo

overlay

My own overlay for experimentations, use with caution, no support is provided git clone https://hacktivis.me/git/overlay.git

portaudio-19.06.00-pa_sndio.c (19522B)


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