Mercurial > audio-send
comparison Alc/backends/oss.c @ 0:f9476ff7637e
initial forking of open-al to create multiple listeners
author | Robert McIntyre <rlm@mit.edu> |
---|---|
date | Tue, 25 Oct 2011 13:02:31 -0700 |
parents | |
children |
comparison
equal
deleted
inserted
replaced
-1:000000000000 | 0:f9476ff7637e |
---|---|
1 /** | |
2 * OpenAL cross platform audio library | |
3 * Copyright (C) 1999-2007 by authors. | |
4 * This library is free software; you can redistribute it and/or | |
5 * modify it under the terms of the GNU Library General Public | |
6 * License as published by the Free Software Foundation; either | |
7 * version 2 of the License, or (at your option) any later version. | |
8 * | |
9 * This library is distributed in the hope that it will be useful, | |
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
12 * Library General Public License for more details. | |
13 * | |
14 * You should have received a copy of the GNU Library General Public | |
15 * License along with this library; if not, write to the | |
16 * Free Software Foundation, Inc., 59 Temple Place - Suite 330, | |
17 * Boston, MA 02111-1307, USA. | |
18 * Or go to http://www.gnu.org/copyleft/lgpl.html | |
19 */ | |
20 | |
21 #include "config.h" | |
22 | |
23 #include <sys/ioctl.h> | |
24 #include <sys/types.h> | |
25 #include <sys/stat.h> | |
26 #include <fcntl.h> | |
27 #include <stdlib.h> | |
28 #include <stdio.h> | |
29 #include <memory.h> | |
30 #include <unistd.h> | |
31 #include <errno.h> | |
32 #include <math.h> | |
33 #include "alMain.h" | |
34 #include "AL/al.h" | |
35 #include "AL/alc.h" | |
36 | |
37 #include <sys/soundcard.h> | |
38 | |
39 /* | |
40 * The OSS documentation talks about SOUND_MIXER_READ, but the header | |
41 * only contains MIXER_READ. Play safe. Same for WRITE. | |
42 */ | |
43 #ifndef SOUND_MIXER_READ | |
44 #define SOUND_MIXER_READ MIXER_READ | |
45 #endif | |
46 #ifndef SOUND_MIXER_WRITE | |
47 #define SOUND_MIXER_WRITE MIXER_WRITE | |
48 #endif | |
49 | |
50 static const ALCchar oss_device[] = "OSS Default"; | |
51 | |
52 typedef struct { | |
53 int fd; | |
54 volatile int killNow; | |
55 ALvoid *thread; | |
56 | |
57 ALubyte *mix_data; | |
58 int data_size; | |
59 | |
60 RingBuffer *ring; | |
61 int doCapture; | |
62 } oss_data; | |
63 | |
64 | |
65 static int log2i(ALCuint x) | |
66 { | |
67 int y = 0; | |
68 while (x > 1) | |
69 { | |
70 x >>= 1; | |
71 y++; | |
72 } | |
73 return y; | |
74 } | |
75 | |
76 | |
77 static ALuint OSSProc(ALvoid *ptr) | |
78 { | |
79 ALCdevice *pDevice = (ALCdevice*)ptr; | |
80 oss_data *data = (oss_data*)pDevice->ExtraData; | |
81 ALint frameSize; | |
82 ssize_t wrote; | |
83 | |
84 SetRTPriority(); | |
85 | |
86 frameSize = FrameSizeFromDevFmt(pDevice->FmtChans, pDevice->FmtType); | |
87 | |
88 while(!data->killNow && pDevice->Connected) | |
89 { | |
90 ALint len = data->data_size; | |
91 ALubyte *WritePtr = data->mix_data; | |
92 | |
93 aluMixData(pDevice, WritePtr, len/frameSize); | |
94 while(len > 0 && !data->killNow) | |
95 { | |
96 wrote = write(data->fd, WritePtr, len); | |
97 if(wrote < 0) | |
98 { | |
99 if(errno != EAGAIN && errno != EWOULDBLOCK && errno != EINTR) | |
100 { | |
101 ERR("write failed: %s\n", strerror(errno)); | |
102 aluHandleDisconnect(pDevice); | |
103 break; | |
104 } | |
105 | |
106 Sleep(1); | |
107 continue; | |
108 } | |
109 | |
110 len -= wrote; | |
111 WritePtr += wrote; | |
112 } | |
113 } | |
114 | |
115 return 0; | |
116 } | |
117 | |
118 static ALuint OSSCaptureProc(ALvoid *ptr) | |
119 { | |
120 ALCdevice *pDevice = (ALCdevice*)ptr; | |
121 oss_data *data = (oss_data*)pDevice->ExtraData; | |
122 int frameSize; | |
123 int amt; | |
124 | |
125 SetRTPriority(); | |
126 | |
127 frameSize = FrameSizeFromDevFmt(pDevice->FmtChans, pDevice->FmtType); | |
128 | |
129 while(!data->killNow) | |
130 { | |
131 amt = read(data->fd, data->mix_data, data->data_size); | |
132 if(amt < 0) | |
133 { | |
134 ERR("read failed: %s\n", strerror(errno)); | |
135 aluHandleDisconnect(pDevice); | |
136 break; | |
137 } | |
138 if(amt == 0) | |
139 { | |
140 Sleep(1); | |
141 continue; | |
142 } | |
143 if(data->doCapture) | |
144 WriteRingBuffer(data->ring, data->mix_data, amt/frameSize); | |
145 } | |
146 | |
147 return 0; | |
148 } | |
149 | |
150 static ALCboolean oss_open_playback(ALCdevice *device, const ALCchar *deviceName) | |
151 { | |
152 char driver[64]; | |
153 oss_data *data; | |
154 | |
155 strncpy(driver, GetConfigValue("oss", "device", "/dev/dsp"), sizeof(driver)-1); | |
156 driver[sizeof(driver)-1] = 0; | |
157 if(!deviceName) | |
158 deviceName = oss_device; | |
159 else if(strcmp(deviceName, oss_device) != 0) | |
160 return ALC_FALSE; | |
161 | |
162 data = (oss_data*)calloc(1, sizeof(oss_data)); | |
163 data->killNow = 0; | |
164 | |
165 data->fd = open(driver, O_WRONLY); | |
166 if(data->fd == -1) | |
167 { | |
168 free(data); | |
169 ERR("Could not open %s: %s\n", driver, strerror(errno)); | |
170 return ALC_FALSE; | |
171 } | |
172 | |
173 device->szDeviceName = strdup(deviceName); | |
174 device->ExtraData = data; | |
175 return ALC_TRUE; | |
176 } | |
177 | |
178 static void oss_close_playback(ALCdevice *device) | |
179 { | |
180 oss_data *data = (oss_data*)device->ExtraData; | |
181 | |
182 close(data->fd); | |
183 free(data); | |
184 device->ExtraData = NULL; | |
185 } | |
186 | |
187 static ALCboolean oss_reset_playback(ALCdevice *device) | |
188 { | |
189 oss_data *data = (oss_data*)device->ExtraData; | |
190 int numFragmentsLogSize; | |
191 int log2FragmentSize; | |
192 unsigned int periods; | |
193 audio_buf_info info; | |
194 ALuint frameSize; | |
195 int numChannels; | |
196 int ossFormat; | |
197 int ossSpeed; | |
198 char *err; | |
199 | |
200 switch(device->FmtType) | |
201 { | |
202 case DevFmtByte: | |
203 ossFormat = AFMT_S8; | |
204 break; | |
205 case DevFmtUByte: | |
206 ossFormat = AFMT_U8; | |
207 break; | |
208 case DevFmtUShort: | |
209 case DevFmtFloat: | |
210 device->FmtType = DevFmtShort; | |
211 /* fall-through */ | |
212 case DevFmtShort: | |
213 ossFormat = AFMT_S16_NE; | |
214 break; | |
215 } | |
216 | |
217 periods = device->NumUpdates; | |
218 numChannels = ChannelsFromDevFmt(device->FmtChans); | |
219 frameSize = numChannels * BytesFromDevFmt(device->FmtType); | |
220 | |
221 ossSpeed = device->Frequency; | |
222 log2FragmentSize = log2i(device->UpdateSize * frameSize); | |
223 | |
224 /* according to the OSS spec, 16 bytes are the minimum */ | |
225 if (log2FragmentSize < 4) | |
226 log2FragmentSize = 4; | |
227 /* Subtract one period since the temp mixing buffer counts as one. Still | |
228 * need at least two on the card, though. */ | |
229 if(periods > 2) periods--; | |
230 numFragmentsLogSize = (periods << 16) | log2FragmentSize; | |
231 | |
232 #define CHECKERR(func) if((func) < 0) { \ | |
233 err = #func; \ | |
234 goto err; \ | |
235 } | |
236 /* Don't fail if SETFRAGMENT fails. We can handle just about anything | |
237 * that's reported back via GETOSPACE */ | |
238 ioctl(data->fd, SNDCTL_DSP_SETFRAGMENT, &numFragmentsLogSize); | |
239 CHECKERR(ioctl(data->fd, SNDCTL_DSP_SETFMT, &ossFormat)); | |
240 CHECKERR(ioctl(data->fd, SNDCTL_DSP_CHANNELS, &numChannels)); | |
241 CHECKERR(ioctl(data->fd, SNDCTL_DSP_SPEED, &ossSpeed)); | |
242 CHECKERR(ioctl(data->fd, SNDCTL_DSP_GETOSPACE, &info)); | |
243 if(0) | |
244 { | |
245 err: | |
246 ERR("%s failed: %s\n", err, strerror(errno)); | |
247 return ALC_FALSE; | |
248 } | |
249 #undef CHECKERR | |
250 | |
251 if((int)ChannelsFromDevFmt(device->FmtChans) != numChannels) | |
252 { | |
253 ERR("Failed to set %s, got %d channels instead\n", DevFmtChannelsString(device->FmtChans), numChannels); | |
254 return ALC_FALSE; | |
255 } | |
256 | |
257 if(!((ossFormat == AFMT_S8 && device->FmtType == DevFmtByte) || | |
258 (ossFormat == AFMT_U8 && device->FmtType == DevFmtUByte) || | |
259 (ossFormat == AFMT_S16_NE && device->FmtType == DevFmtShort))) | |
260 { | |
261 ERR("Failed to set %s samples, got OSS format %#x\n", DevFmtTypeString(device->FmtType), ossFormat); | |
262 return ALC_FALSE; | |
263 } | |
264 | |
265 if(device->Frequency != (ALuint)ossSpeed) | |
266 { | |
267 if((device->Flags&DEVICE_FREQUENCY_REQUEST)) | |
268 ERR("Failed to set %dhz, got %dhz instead\n", device->Frequency, ossSpeed); | |
269 device->Flags &= ~DEVICE_FREQUENCY_REQUEST; | |
270 device->Frequency = ossSpeed; | |
271 } | |
272 device->UpdateSize = info.fragsize / frameSize; | |
273 device->NumUpdates = info.fragments + 1; | |
274 | |
275 data->data_size = device->UpdateSize * frameSize; | |
276 data->mix_data = calloc(1, data->data_size); | |
277 | |
278 SetDefaultChannelOrder(device); | |
279 | |
280 data->thread = StartThread(OSSProc, device); | |
281 if(data->thread == NULL) | |
282 { | |
283 free(data->mix_data); | |
284 data->mix_data = NULL; | |
285 return ALC_FALSE; | |
286 } | |
287 | |
288 return ALC_TRUE; | |
289 } | |
290 | |
291 static void oss_stop_playback(ALCdevice *device) | |
292 { | |
293 oss_data *data = (oss_data*)device->ExtraData; | |
294 | |
295 if(!data->thread) | |
296 return; | |
297 | |
298 data->killNow = 1; | |
299 StopThread(data->thread); | |
300 data->thread = NULL; | |
301 | |
302 data->killNow = 0; | |
303 if(ioctl(data->fd, SNDCTL_DSP_RESET) != 0) | |
304 ERR("Error resetting device: %s\n", strerror(errno)); | |
305 | |
306 free(data->mix_data); | |
307 data->mix_data = NULL; | |
308 } | |
309 | |
310 | |
311 static ALCboolean oss_open_capture(ALCdevice *device, const ALCchar *deviceName) | |
312 { | |
313 int numFragmentsLogSize; | |
314 int log2FragmentSize; | |
315 unsigned int periods; | |
316 audio_buf_info info; | |
317 ALuint frameSize; | |
318 int numChannels; | |
319 char driver[64]; | |
320 oss_data *data; | |
321 int ossFormat; | |
322 int ossSpeed; | |
323 char *err; | |
324 | |
325 strncpy(driver, GetConfigValue("oss", "capture", "/dev/dsp"), sizeof(driver)-1); | |
326 driver[sizeof(driver)-1] = 0; | |
327 if(!deviceName) | |
328 deviceName = oss_device; | |
329 else if(strcmp(deviceName, oss_device) != 0) | |
330 return ALC_FALSE; | |
331 | |
332 data = (oss_data*)calloc(1, sizeof(oss_data)); | |
333 data->killNow = 0; | |
334 | |
335 data->fd = open(driver, O_RDONLY); | |
336 if(data->fd == -1) | |
337 { | |
338 free(data); | |
339 ERR("Could not open %s: %s\n", driver, strerror(errno)); | |
340 return ALC_FALSE; | |
341 } | |
342 | |
343 switch(device->FmtType) | |
344 { | |
345 case DevFmtByte: | |
346 ossFormat = AFMT_S8; | |
347 break; | |
348 case DevFmtUByte: | |
349 ossFormat = AFMT_U8; | |
350 break; | |
351 case DevFmtShort: | |
352 ossFormat = AFMT_S16_NE; | |
353 break; | |
354 case DevFmtUShort: | |
355 case DevFmtFloat: | |
356 free(data); | |
357 ERR("%s capture samples not supported on OSS\n", DevFmtTypeString(device->FmtType)); | |
358 return ALC_FALSE; | |
359 } | |
360 | |
361 periods = 4; | |
362 numChannels = ChannelsFromDevFmt(device->FmtChans); | |
363 frameSize = numChannels * BytesFromDevFmt(device->FmtType); | |
364 ossSpeed = device->Frequency; | |
365 log2FragmentSize = log2i(device->UpdateSize * device->NumUpdates * | |
366 frameSize / periods); | |
367 | |
368 /* according to the OSS spec, 16 bytes are the minimum */ | |
369 if (log2FragmentSize < 4) | |
370 log2FragmentSize = 4; | |
371 numFragmentsLogSize = (periods << 16) | log2FragmentSize; | |
372 | |
373 #define CHECKERR(func) if((func) < 0) { \ | |
374 err = #func; \ | |
375 goto err; \ | |
376 } | |
377 CHECKERR(ioctl(data->fd, SNDCTL_DSP_SETFRAGMENT, &numFragmentsLogSize)); | |
378 CHECKERR(ioctl(data->fd, SNDCTL_DSP_SETFMT, &ossFormat)); | |
379 CHECKERR(ioctl(data->fd, SNDCTL_DSP_CHANNELS, &numChannels)); | |
380 CHECKERR(ioctl(data->fd, SNDCTL_DSP_SPEED, &ossSpeed)); | |
381 CHECKERR(ioctl(data->fd, SNDCTL_DSP_GETISPACE, &info)); | |
382 if(0) | |
383 { | |
384 err: | |
385 ERR("%s failed: %s\n", err, strerror(errno)); | |
386 close(data->fd); | |
387 free(data); | |
388 return ALC_FALSE; | |
389 } | |
390 #undef CHECKERR | |
391 | |
392 if((int)ChannelsFromDevFmt(device->FmtChans) != numChannels) | |
393 { | |
394 ERR("Failed to set %s, got %d channels instead\n", DevFmtChannelsString(device->FmtChans), numChannels); | |
395 close(data->fd); | |
396 free(data); | |
397 return ALC_FALSE; | |
398 } | |
399 | |
400 if(!((ossFormat == AFMT_S8 && device->FmtType == DevFmtByte) || | |
401 (ossFormat == AFMT_U8 && device->FmtType == DevFmtUByte) || | |
402 (ossFormat == AFMT_S16_NE && device->FmtType == DevFmtShort))) | |
403 { | |
404 ERR("Failed to set %s samples, got OSS format %#x\n", DevFmtTypeString(device->FmtType), ossFormat); | |
405 close(data->fd); | |
406 free(data); | |
407 return ALC_FALSE; | |
408 } | |
409 | |
410 data->ring = CreateRingBuffer(frameSize, device->UpdateSize * device->NumUpdates); | |
411 if(!data->ring) | |
412 { | |
413 ERR("Ring buffer create failed\n"); | |
414 close(data->fd); | |
415 free(data); | |
416 return ALC_FALSE; | |
417 } | |
418 | |
419 data->data_size = info.fragsize; | |
420 data->mix_data = calloc(1, data->data_size); | |
421 | |
422 device->ExtraData = data; | |
423 data->thread = StartThread(OSSCaptureProc, device); | |
424 if(data->thread == NULL) | |
425 { | |
426 device->ExtraData = NULL; | |
427 free(data->mix_data); | |
428 free(data); | |
429 return ALC_FALSE; | |
430 } | |
431 | |
432 device->szDeviceName = strdup(deviceName); | |
433 return ALC_TRUE; | |
434 } | |
435 | |
436 static void oss_close_capture(ALCdevice *device) | |
437 { | |
438 oss_data *data = (oss_data*)device->ExtraData; | |
439 data->killNow = 1; | |
440 StopThread(data->thread); | |
441 | |
442 close(data->fd); | |
443 | |
444 DestroyRingBuffer(data->ring); | |
445 | |
446 free(data->mix_data); | |
447 free(data); | |
448 device->ExtraData = NULL; | |
449 } | |
450 | |
451 static void oss_start_capture(ALCdevice *pDevice) | |
452 { | |
453 oss_data *data = (oss_data*)pDevice->ExtraData; | |
454 data->doCapture = 1; | |
455 } | |
456 | |
457 static void oss_stop_capture(ALCdevice *pDevice) | |
458 { | |
459 oss_data *data = (oss_data*)pDevice->ExtraData; | |
460 data->doCapture = 0; | |
461 } | |
462 | |
463 static void oss_capture_samples(ALCdevice *pDevice, ALCvoid *pBuffer, ALCuint lSamples) | |
464 { | |
465 oss_data *data = (oss_data*)pDevice->ExtraData; | |
466 if(lSamples <= (ALCuint)RingBufferSize(data->ring)) | |
467 ReadRingBuffer(data->ring, pBuffer, lSamples); | |
468 else | |
469 alcSetError(pDevice, ALC_INVALID_VALUE); | |
470 } | |
471 | |
472 static ALCuint oss_available_samples(ALCdevice *pDevice) | |
473 { | |
474 oss_data *data = (oss_data*)pDevice->ExtraData; | |
475 return RingBufferSize(data->ring); | |
476 } | |
477 | |
478 | |
479 static const BackendFuncs oss_funcs = { | |
480 oss_open_playback, | |
481 oss_close_playback, | |
482 oss_reset_playback, | |
483 oss_stop_playback, | |
484 oss_open_capture, | |
485 oss_close_capture, | |
486 oss_start_capture, | |
487 oss_stop_capture, | |
488 oss_capture_samples, | |
489 oss_available_samples | |
490 }; | |
491 | |
492 ALCboolean alc_oss_init(BackendFuncs *func_list) | |
493 { | |
494 *func_list = oss_funcs; | |
495 return ALC_TRUE; | |
496 } | |
497 | |
498 void alc_oss_deinit(void) | |
499 { | |
500 } | |
501 | |
502 void alc_oss_probe(enum DevProbe type) | |
503 { | |
504 switch(type) | |
505 { | |
506 case DEVICE_PROBE: | |
507 { | |
508 #ifdef HAVE_STAT | |
509 struct stat buf; | |
510 if(stat(GetConfigValue("oss", "device", "/dev/dsp"), &buf) == 0) | |
511 #endif | |
512 AppendDeviceList(oss_device); | |
513 } | |
514 break; | |
515 | |
516 case ALL_DEVICE_PROBE: | |
517 { | |
518 #ifdef HAVE_STAT | |
519 struct stat buf; | |
520 if(stat(GetConfigValue("oss", "device", "/dev/dsp"), &buf) == 0) | |
521 #endif | |
522 AppendAllDeviceList(oss_device); | |
523 } | |
524 break; | |
525 | |
526 case CAPTURE_DEVICE_PROBE: | |
527 { | |
528 #ifdef HAVE_STAT | |
529 struct stat buf; | |
530 if(stat(GetConfigValue("oss", "capture", "/dev/dsp"), &buf) == 0) | |
531 #endif | |
532 AppendCaptureDeviceList(oss_device); | |
533 } | |
534 break; | |
535 } | |
536 } |