SDL_sndioaudio.c 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365
  1. /*
  2. Simple DirectMedia Layer
  3. Copyright (C) 1997-2023 Sam Lantinga <slouken@libsdl.org>
  4. This software is provided 'as-is', without any express or implied
  5. warranty. In no event will the authors be held liable for any damages
  6. arising from the use of this software.
  7. Permission is granted to anyone to use this software for any purpose,
  8. including commercial applications, and to alter it and redistribute it
  9. freely, subject to the following restrictions:
  10. 1. The origin of this software must not be misrepresented; you must not
  11. claim that you wrote the original software. If you use this software
  12. in a product, an acknowledgment in the product documentation would be
  13. appreciated but is not required.
  14. 2. Altered source versions must be plainly marked as such, and must not be
  15. misrepresented as being the original software.
  16. 3. This notice may not be removed or altered from any source distribution.
  17. */
  18. #include "SDL_internal.h"
  19. #ifdef SDL_AUDIO_DRIVER_SNDIO
  20. /* OpenBSD sndio target */
  21. #ifdef HAVE_STDIO_H
  22. #include <stdio.h>
  23. #endif
  24. #ifdef HAVE_SIGNAL_H
  25. #include <signal.h>
  26. #endif
  27. #include <poll.h>
  28. #include <unistd.h>
  29. #include "../SDL_audio_c.h"
  30. #include "SDL_sndioaudio.h"
  31. #ifdef SDL_AUDIO_DRIVER_SNDIO_DYNAMIC
  32. #endif
  33. #ifndef INFTIM
  34. #define INFTIM -1
  35. #endif
  36. #ifndef SIO_DEVANY
  37. #define SIO_DEVANY "default"
  38. #endif
  39. static struct sio_hdl *(*SNDIO_sio_open)(const char *, unsigned int, int);
  40. static void (*SNDIO_sio_close)(struct sio_hdl *);
  41. static int (*SNDIO_sio_setpar)(struct sio_hdl *, struct sio_par *);
  42. static int (*SNDIO_sio_getpar)(struct sio_hdl *, struct sio_par *);
  43. static int (*SNDIO_sio_start)(struct sio_hdl *);
  44. static int (*SNDIO_sio_stop)(struct sio_hdl *);
  45. static size_t (*SNDIO_sio_read)(struct sio_hdl *, void *, size_t);
  46. static size_t (*SNDIO_sio_write)(struct sio_hdl *, const void *, size_t);
  47. static int (*SNDIO_sio_nfds)(struct sio_hdl *);
  48. static int (*SNDIO_sio_pollfd)(struct sio_hdl *, struct pollfd *, int);
  49. static int (*SNDIO_sio_revents)(struct sio_hdl *, struct pollfd *);
  50. static int (*SNDIO_sio_eof)(struct sio_hdl *);
  51. static void (*SNDIO_sio_initpar)(struct sio_par *);
  52. #ifdef SDL_AUDIO_DRIVER_SNDIO_DYNAMIC
  53. static const char *sndio_library = SDL_AUDIO_DRIVER_SNDIO_DYNAMIC;
  54. static void *sndio_handle = NULL;
  55. static int load_sndio_sym(const char *fn, void **addr)
  56. {
  57. *addr = SDL_LoadFunction(sndio_handle, fn);
  58. if (*addr == NULL) {
  59. /* Don't call SDL_SetError(): SDL_LoadFunction already did. */
  60. return 0;
  61. }
  62. return 1;
  63. }
  64. /* cast funcs to char* first, to please GCC's strict aliasing rules. */
  65. #define SDL_SNDIO_SYM(x) \
  66. if (!load_sndio_sym(#x, (void **)(char *)&SNDIO_##x)) \
  67. return -1
  68. #else
  69. #define SDL_SNDIO_SYM(x) SNDIO_##x = x
  70. #endif
  71. static int load_sndio_syms(void)
  72. {
  73. SDL_SNDIO_SYM(sio_open);
  74. SDL_SNDIO_SYM(sio_close);
  75. SDL_SNDIO_SYM(sio_setpar);
  76. SDL_SNDIO_SYM(sio_getpar);
  77. SDL_SNDIO_SYM(sio_start);
  78. SDL_SNDIO_SYM(sio_stop);
  79. SDL_SNDIO_SYM(sio_read);
  80. SDL_SNDIO_SYM(sio_write);
  81. SDL_SNDIO_SYM(sio_nfds);
  82. SDL_SNDIO_SYM(sio_pollfd);
  83. SDL_SNDIO_SYM(sio_revents);
  84. SDL_SNDIO_SYM(sio_eof);
  85. SDL_SNDIO_SYM(sio_initpar);
  86. return 0;
  87. }
  88. #undef SDL_SNDIO_SYM
  89. #ifdef SDL_AUDIO_DRIVER_SNDIO_DYNAMIC
  90. static void UnloadSNDIOLibrary(void)
  91. {
  92. if (sndio_handle != NULL) {
  93. SDL_UnloadObject(sndio_handle);
  94. sndio_handle = NULL;
  95. }
  96. }
  97. static int LoadSNDIOLibrary(void)
  98. {
  99. int retval = 0;
  100. if (sndio_handle == NULL) {
  101. sndio_handle = SDL_LoadObject(sndio_library);
  102. if (sndio_handle == NULL) {
  103. retval = -1;
  104. /* Don't call SDL_SetError(): SDL_LoadObject already did. */
  105. } else {
  106. retval = load_sndio_syms();
  107. if (retval < 0) {
  108. UnloadSNDIOLibrary();
  109. }
  110. }
  111. }
  112. return retval;
  113. }
  114. #else
  115. static void UnloadSNDIOLibrary(void)
  116. {
  117. }
  118. static int LoadSNDIOLibrary(void)
  119. {
  120. load_sndio_syms();
  121. return 0;
  122. }
  123. #endif /* SDL_AUDIO_DRIVER_SNDIO_DYNAMIC */
  124. static void SNDIO_WaitDevice(SDL_AudioDevice *_this)
  125. {
  126. /* no-op; SNDIO_sio_write() blocks if necessary. */
  127. }
  128. static void SNDIO_PlayDevice(SDL_AudioDevice *_this)
  129. {
  130. const int written = SNDIO_sio_write(_this->hidden->dev,
  131. _this->hidden->mixbuf,
  132. _this->hidden->mixlen);
  133. /* If we couldn't write, assume fatal error for now */
  134. if (written == 0) {
  135. SDL_OpenedAudioDeviceDisconnected(_this);
  136. }
  137. #ifdef DEBUG_AUDIO
  138. fprintf(stderr, "Wrote %d bytes of audio data\n", written);
  139. #endif
  140. }
  141. static int SNDIO_CaptureFromDevice(SDL_AudioDevice *_this, void *buffer, int buflen)
  142. {
  143. size_t r;
  144. int revents;
  145. int nfds;
  146. /* Emulate a blocking read */
  147. r = SNDIO_sio_read(_this->hidden->dev, buffer, buflen);
  148. while (r == 0 && !SNDIO_sio_eof(_this->hidden->dev)) {
  149. nfds = SNDIO_sio_pollfd(_this->hidden->dev, _this->hidden->pfd, POLLIN);
  150. if (nfds <= 0 || poll(_this->hidden->pfd, nfds, INFTIM) < 0) {
  151. return -1;
  152. }
  153. revents = SNDIO_sio_revents(_this->hidden->dev, _this->hidden->pfd);
  154. if (revents & POLLIN) {
  155. r = SNDIO_sio_read(_this->hidden->dev, buffer, buflen);
  156. }
  157. if (revents & POLLHUP) {
  158. break;
  159. }
  160. }
  161. return (int)r;
  162. }
  163. static void SNDIO_FlushCapture(SDL_AudioDevice *_this)
  164. {
  165. char buf[512];
  166. while (SNDIO_sio_read(_this->hidden->dev, buf, sizeof(buf)) != 0) {
  167. /* do nothing */;
  168. }
  169. }
  170. static Uint8 *SNDIO_GetDeviceBuf(SDL_AudioDevice *_this)
  171. {
  172. return _this->hidden->mixbuf;
  173. }
  174. static void SNDIO_CloseDevice(SDL_AudioDevice *_this)
  175. {
  176. if (_this->hidden->pfd != NULL) {
  177. SDL_free(_this->hidden->pfd);
  178. }
  179. if (_this->hidden->dev != NULL) {
  180. SNDIO_sio_stop(_this->hidden->dev);
  181. SNDIO_sio_close(_this->hidden->dev);
  182. }
  183. SDL_free(_this->hidden->mixbuf);
  184. SDL_free(_this->hidden);
  185. }
  186. static int SNDIO_OpenDevice(SDL_AudioDevice *_this, const char *devname)
  187. {
  188. SDL_AudioFormat test_format;
  189. const SDL_AudioFormat *closefmts;
  190. struct sio_par par;
  191. SDL_bool iscapture = _this->iscapture;
  192. _this->hidden = (struct SDL_PrivateAudioData *)
  193. SDL_malloc(sizeof(*_this->hidden));
  194. if (_this->hidden == NULL) {
  195. return SDL_OutOfMemory();
  196. }
  197. SDL_zerop(_this->hidden);
  198. _this->hidden->mixlen = _this->spec.size;
  199. /* Capture devices must be non-blocking for SNDIO_FlushCapture */
  200. _this->hidden->dev = SNDIO_sio_open(devname != NULL ? devname : SIO_DEVANY,
  201. iscapture ? SIO_REC : SIO_PLAY, iscapture);
  202. if (_this->hidden->dev == NULL) {
  203. return SDL_SetError("sio_open() failed");
  204. }
  205. /* Allocate the pollfd array for capture devices */
  206. if (iscapture) {
  207. _this->hidden->pfd = SDL_malloc(sizeof(struct pollfd) * SNDIO_sio_nfds(_this->hidden->dev));
  208. if (_this->hidden->pfd == NULL) {
  209. return SDL_OutOfMemory();
  210. }
  211. }
  212. SNDIO_sio_initpar(&par);
  213. par.rate = _this->spec.freq;
  214. par.pchan = _this->spec.channels;
  215. par.round = _this->spec.samples;
  216. par.appbufsz = par.round * 2;
  217. /* Try for a closest match on audio format */
  218. closefmts = SDL_ClosestAudioFormats(_this->spec.format);
  219. while ((test_format = *(closefmts++)) != 0) {
  220. if (!SDL_AUDIO_ISFLOAT(test_format)) {
  221. par.le = SDL_AUDIO_ISLITTLEENDIAN(test_format) ? 1 : 0;
  222. par.sig = SDL_AUDIO_ISSIGNED(test_format) ? 1 : 0;
  223. par.bits = SDL_AUDIO_BITSIZE(test_format);
  224. if (SNDIO_sio_setpar(_this->hidden->dev, &par) == 0) {
  225. continue;
  226. }
  227. if (SNDIO_sio_getpar(_this->hidden->dev, &par) == 0) {
  228. return SDL_SetError("sio_getpar() failed");
  229. }
  230. if (par.bps != SIO_BPS(par.bits)) {
  231. continue;
  232. }
  233. if ((par.bits == 8 * par.bps) || (par.msb)) {
  234. break;
  235. }
  236. }
  237. }
  238. if (!test_format) {
  239. return SDL_SetError("%s: Unsupported audio format", "sndio");
  240. }
  241. if ((par.bps == 4) && (par.sig) && (par.le)) {
  242. _this->spec.format = SDL_AUDIO_S32LSB;
  243. } else if ((par.bps == 4) && (par.sig) && (!par.le)) {
  244. _this->spec.format = SDL_AUDIO_S32MSB;
  245. } else if ((par.bps == 2) && (par.sig) && (par.le)) {
  246. _this->spec.format = SDL_AUDIO_S16LSB;
  247. } else if ((par.bps == 2) && (par.sig) && (!par.le)) {
  248. _this->spec.format = SDL_AUDIO_S16MSB;
  249. } else if ((par.bps == 1) && (par.sig)) {
  250. _this->spec.format = SDL_AUDIO_S8;
  251. } else if ((par.bps == 1) && (!par.sig)) {
  252. _this->spec.format = SDL_AUDIO_U8;
  253. } else {
  254. return SDL_SetError("sndio: Got unsupported hardware audio format.");
  255. }
  256. _this->spec.freq = par.rate;
  257. _this->spec.channels = par.pchan;
  258. _this->spec.samples = par.round;
  259. /* Calculate the final parameters for this audio specification */
  260. SDL_CalculateAudioSpec(&_this->spec);
  261. /* Allocate mixing buffer */
  262. _this->hidden->mixlen = _this->spec.size;
  263. _this->hidden->mixbuf = (Uint8 *)SDL_malloc(_this->hidden->mixlen);
  264. if (_this->hidden->mixbuf == NULL) {
  265. return SDL_OutOfMemory();
  266. }
  267. SDL_memset(_this->hidden->mixbuf, _this->spec.silence, _this->hidden->mixlen);
  268. if (!SNDIO_sio_start(_this->hidden->dev)) {
  269. return SDL_SetError("sio_start() failed");
  270. }
  271. /* We're ready to rock and roll. :-) */
  272. return 0;
  273. }
  274. static void SNDIO_Deinitialize(void)
  275. {
  276. UnloadSNDIOLibrary();
  277. }
  278. static void SNDIO_DetectDevices(void)
  279. {
  280. SDL_AddAudioDevice(SDL_FALSE, DEFAULT_OUTPUT_DEVNAME, NULL, (void *)0x1);
  281. SDL_AddAudioDevice(SDL_TRUE, DEFAULT_INPUT_DEVNAME, NULL, (void *)0x2);
  282. }
  283. static SDL_bool SNDIO_Init(SDL_AudioDriverImpl *impl)
  284. {
  285. if (LoadSNDIOLibrary() < 0) {
  286. return SDL_FALSE;
  287. }
  288. /* Set the function pointers */
  289. impl->OpenDevice = SNDIO_OpenDevice;
  290. impl->WaitDevice = SNDIO_WaitDevice;
  291. impl->PlayDevice = SNDIO_PlayDevice;
  292. impl->GetDeviceBuf = SNDIO_GetDeviceBuf;
  293. impl->CloseDevice = SNDIO_CloseDevice;
  294. impl->CaptureFromDevice = SNDIO_CaptureFromDevice;
  295. impl->FlushCapture = SNDIO_FlushCapture;
  296. impl->Deinitialize = SNDIO_Deinitialize;
  297. impl->DetectDevices = SNDIO_DetectDevices;
  298. impl->AllowsArbitraryDeviceNames = SDL_TRUE;
  299. impl->HasCaptureSupport = SDL_TRUE;
  300. return SDL_TRUE; /* this audio target is available. */
  301. }
  302. AudioBootStrap SNDIO_bootstrap = {
  303. "sndio", "OpenBSD sndio", SNDIO_Init, SDL_FALSE
  304. };
  305. #endif /* SDL_AUDIO_DRIVER_SNDIO */