aRts audio server
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

311 lines
6.0 KiB

  1. /*
  2. * Copyright (c) 2008 Jacob Meuser <jakemsr@sdf.lonestar.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. #ifdef HAVE_CONFIG_H
  17. #include <config.h>
  18. #endif
  19. #ifdef HAVE_LIBSNDIO
  20. #include <sys/types.h>
  21. #include <errno.h>
  22. #include <math.h>
  23. #include <sndio.h>
  24. #include <poll.h>
  25. #include "debug.h"
  26. #include "audioio.h"
  27. #include "audiosubsys.h"
  28. #include "iomanager.h"
  29. #include "dispatcher.h"
  30. #include <cstdlib>
  31. #include <cstring>
  32. int bps, chans;
  33. long long realpos, playpos, recpos;
  34. void movecb(void *addr, int delta)
  35. {
  36. realpos += delta * (int)(bps * chans);
  37. }
  38. namespace Arts {
  39. class AudioIOSNDIO : public AudioIO, public TimeNotify {
  40. protected:
  41. struct sio_hdl *hdl;
  42. struct sio_par par;
  43. int bufsz;
  44. int bufused;
  45. int duplex;
  46. public:
  47. AudioIOSNDIO();
  48. void notifyTime();
  49. void setParam(AudioParam param, int& value);
  50. int getParam(AudioParam param);
  51. bool open();
  52. void close();
  53. int read(void *buffer, int size);
  54. int write(void *buffer, int size);
  55. };
  56. REGISTER_AUDIO_IO(AudioIOSNDIO,"sndio","libsndio");
  57. }
  58. using namespace std;
  59. using namespace Arts;
  60. AudioIOSNDIO::AudioIOSNDIO()
  61. {
  62. /* default parameters */
  63. param(samplingRate) = 48000;
  64. paramStr(deviceName) = "";
  65. param(fragmentSize) = 4096;
  66. param(fragmentCount) = 4;
  67. param(format) = 16;
  68. param(channels) = 2;
  69. param(direction) = directionWrite;
  70. }
  71. bool AudioIOSNDIO::open()
  72. {
  73. string& _error = paramStr(lastError);
  74. string& _deviceName = paramStr(deviceName);
  75. int& _channels = param(channels);
  76. int& _fragmentSize = param(fragmentSize);
  77. int& _fragmentCount = param(fragmentCount);
  78. int& _samplingRate = param(samplingRate);
  79. int& _format = param(format);
  80. struct sio_par testpar;
  81. char dev[PATH_MAX];
  82. int mode, bpf;
  83. duplex = 0;
  84. if (param(direction) == (directionRead | directionWrite)) {
  85. mode = SIO_PLAY | SIO_REC;
  86. duplex = 1;
  87. } else if (param(direction) == directionWrite) {
  88. mode = SIO_PLAY;
  89. } else {
  90. _error = "invalid direction";
  91. return false;
  92. }
  93. strlcpy(dev, _deviceName.c_str(), PATH_MAX);
  94. if (strcmp(dev, "") == 0)
  95. hdl = sio_open(NULL, mode, 0);
  96. else
  97. hdl = sio_open(dev, mode, 0);
  98. if (hdl == NULL) {
  99. _error = "device ";
  100. if (strcmp(_deviceName.c_str(), "") == 0)
  101. _error += "(default sndio device)";
  102. else
  103. _error += _deviceName.c_str();
  104. _error += " can't be opened";
  105. return false;
  106. }
  107. sio_initpar(&par);
  108. if (_format == 8) {
  109. par.bits = 8;
  110. par.sig = 0;
  111. } else {
  112. par.bits = 16;
  113. par.sig = 1;
  114. par.le = 1;
  115. }
  116. if (duplex)
  117. par.pchan = par.rchan = _channels;
  118. else
  119. par.pchan = _channels;
  120. par.rate = _samplingRate;
  121. /* limit the buffer size for hardware constraints */
  122. if (_fragmentSize > 1024*16)
  123. _fragmentSize = 1024*16;
  124. while (_fragmentSize * _fragmentCount > 1024*32)
  125. _fragmentCount--;
  126. bpf = ((par.bits / NBBY) * par.pchan);
  127. par.round = _fragmentSize / bpf;
  128. par.appbufsz = _fragmentSize * _fragmentCount / bpf;
  129. testpar = par;
  130. char details[128];
  131. snprintf(details, 128, " rate=%d pchan=%d bits=%d le=%d sig=%d sz=%d",
  132. par.rate, par.pchan, par.bits, par.le, par.sig, par.appbufsz);
  133. if (!sio_setpar(hdl, &par)) {
  134. _error = "sio_setpar failed:";
  135. _error += details;
  136. close();
  137. return false;
  138. }
  139. if (!sio_getpar(hdl, &par)) {
  140. _error = "sio_getpar failed";
  141. close();
  142. return false;
  143. }
  144. if (testpar.bits != par.bits ||
  145. testpar.sig != par.sig ||
  146. testpar.le != par.le ||
  147. testpar.pchan != par.pchan ||
  148. fabs((testpar.rate - par.rate)/testpar.rate) > 0.05) {
  149. _error = "returned params do not match requested params";
  150. close();
  151. return false;
  152. }
  153. bpf = par.pchan * par.bps;
  154. bufsz = par.bufsz * bpf;
  155. ::bps = par.bps;
  156. ::chans = par.pchan;
  157. ::realpos = ::playpos = ::recpos = 0;
  158. sio_onmove(hdl, ::movecb, NULL);
  159. if (!sio_start(hdl)) {
  160. _error = "sio_start failed";
  161. close();
  162. return false;
  163. }
  164. /* use a timer instead of select() */
  165. Dispatcher::the()->ioManager()->addTimer(10, this);
  166. return true;
  167. }
  168. void AudioIOSNDIO::close()
  169. {
  170. if (hdl != NULL) {
  171. sio_close(hdl);
  172. hdl = NULL;
  173. }
  174. }
  175. void AudioIOSNDIO::setParam(AudioParam p, int& value)
  176. {
  177. param(p) = value;
  178. }
  179. int AudioIOSNDIO::getParam(AudioParam p)
  180. {
  181. struct pollfd gpfd;
  182. int n, events;
  183. /* update ::realpos */
  184. if ((p == canRead || p == canWrite) && hdl != NULL) {
  185. events = POLLOUT;
  186. if (duplex)
  187. events |= POLLIN;
  188. n = sio_pollfd(hdl, &gpfd, events);
  189. while (poll(&gpfd, n, 0) < 0 && errno == EINTR)
  190. ;
  191. sio_revents(hdl, &gpfd);
  192. }
  193. switch(p) {
  194. case canRead:
  195. bufused = (::realpos - ::recpos < 0) ? 0 : ::realpos - ::recpos;
  196. return bufused;
  197. break;
  198. case canWrite:
  199. bufused = (::realpos < 0) ? ::playpos : ::playpos - ::realpos;
  200. return bufsz - bufused;
  201. break;
  202. case autoDetect:
  203. return 15;
  204. break;
  205. default:
  206. return param(p);
  207. break;
  208. }
  209. }
  210. int AudioIOSNDIO::read(void *buffer, int size)
  211. {
  212. arts_assert(hdl != NULL);
  213. size_t result;
  214. result = sio_read(hdl, buffer, size);
  215. ::recpos += result;
  216. return (int)result;
  217. }
  218. int AudioIOSNDIO::write(void *buffer, int size)
  219. {
  220. arts_assert(hdl != NULL);
  221. size_t result;
  222. result = sio_write(hdl, buffer, size);
  223. ::playpos += result;
  224. return (int)result;
  225. }
  226. void AudioIOSNDIO::notifyTime()
  227. {
  228. int& _direction = param(direction);
  229. for (;;) {
  230. int todo = 0;
  231. if ((_direction & directionRead) && (getParam(canRead) > 0))
  232. todo |= AudioSubSystem::ioRead;
  233. if ((_direction & directionWrite) && (getParam(canWrite) > 0))
  234. todo |= AudioSubSystem::ioWrite;
  235. if (todo == 0)
  236. return;
  237. AudioSubSystem::the()->handleIO(todo);
  238. }
  239. }
  240. #endif