Mercurial > fife-parpg
comparison ext/openal-soft/Alc/oss.c @ 0:4a0efb7baf70
* Datasets becomes the new trunk and retires after that :-)
author | mvbarracuda@33b003aa-7bff-0310-803a-e67f0ece8222 |
---|---|
date | Sun, 29 Jun 2008 18:44:17 +0000 |
parents | |
children |
comparison
equal
deleted
inserted
replaced
-1:000000000000 | 0:4a0efb7baf70 |
---|---|
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 char *oss_device; | |
51 static char *oss_device_capture; | |
52 | |
53 typedef struct { | |
54 int fd; | |
55 int killNow; | |
56 ALvoid *thread; | |
57 | |
58 ALubyte *mix_data; | |
59 int data_size; | |
60 | |
61 RingBuffer *ring; | |
62 int doCapture; | |
63 } oss_data; | |
64 | |
65 | |
66 static int log2i(ALCuint x) | |
67 { | |
68 int y = 0; | |
69 while (x > 1) | |
70 { | |
71 x >>= 1; | |
72 y++; | |
73 } | |
74 return y; | |
75 } | |
76 | |
77 | |
78 static ALuint OSSProc(ALvoid *ptr) | |
79 { | |
80 ALCdevice *pDevice = (ALCdevice*)ptr; | |
81 oss_data *data = (oss_data*)pDevice->ExtraData; | |
82 int remaining = 0; | |
83 int wrote; | |
84 | |
85 while(!data->killNow) | |
86 { | |
87 int len = data->data_size - remaining; | |
88 | |
89 if(len > 0) | |
90 { | |
91 SuspendContext(NULL); | |
92 aluMixData(pDevice->Context, data->mix_data+remaining, len, pDevice->Format); | |
93 ProcessContext(NULL); | |
94 } | |
95 | |
96 remaining += len; | |
97 wrote = write(data->fd, data->mix_data, remaining); | |
98 if(wrote < 0) | |
99 { | |
100 AL_PRINT("write failed: %s\n", strerror(errno)); | |
101 remaining = 0; | |
102 } | |
103 else if(wrote > 0) | |
104 { | |
105 remaining -= wrote; | |
106 if(remaining > 0) | |
107 memmove(data->mix_data, data->mix_data+wrote, remaining); | |
108 } | |
109 else | |
110 Sleep(1); | |
111 } | |
112 | |
113 return 0; | |
114 } | |
115 | |
116 static ALuint OSSCaptureProc(ALvoid *ptr) | |
117 { | |
118 ALCdevice *pDevice = (ALCdevice*)ptr; | |
119 oss_data *data = (oss_data*)pDevice->ExtraData; | |
120 int frameSize; | |
121 int amt; | |
122 | |
123 frameSize = aluBytesFromFormat(pDevice->Format); | |
124 frameSize *= aluChannelsFromFormat(pDevice->Format); | |
125 | |
126 while(!data->killNow) | |
127 { | |
128 amt = read(data->fd, data->mix_data, data->data_size); | |
129 if(amt < 0) | |
130 { | |
131 AL_PRINT("read failed: %s\n", strerror(errno)); | |
132 break; | |
133 } | |
134 if(amt == 0) | |
135 { | |
136 Sleep(1); | |
137 continue; | |
138 } | |
139 if(data->doCapture) | |
140 WriteRingBuffer(data->ring, data->mix_data, amt/frameSize); | |
141 } | |
142 | |
143 return 0; | |
144 } | |
145 | |
146 static ALCboolean oss_open_playback(ALCdevice *device, const ALCchar *deviceName) | |
147 { | |
148 int numFragmentsLogSize; | |
149 int log2FragmentSize; | |
150 unsigned int periods; | |
151 audio_buf_info info; | |
152 ALuint frameSize; | |
153 char driver[64]; | |
154 int numChannels; | |
155 oss_data *data; | |
156 int ossFormat; | |
157 int ossSpeed; | |
158 char *err; | |
159 int i; | |
160 | |
161 strncpy(driver, GetConfigValue("oss", "device", "/dev/dsp"), sizeof(driver)-1); | |
162 driver[sizeof(driver)-1] = 0; | |
163 if(deviceName) | |
164 { | |
165 if(strcmp(deviceName, oss_device)) | |
166 return ALC_FALSE; | |
167 device->szDeviceName = oss_device; | |
168 } | |
169 else | |
170 device->szDeviceName = oss_device; | |
171 | |
172 data = (oss_data*)calloc(1, sizeof(oss_data)); | |
173 data->killNow = 0; | |
174 | |
175 data->fd = open(driver, O_WRONLY); | |
176 if(data->fd == -1) | |
177 { | |
178 free(data); | |
179 AL_PRINT("Could not open %s: %s\n", driver, strerror(errno)); | |
180 return ALC_FALSE; | |
181 } | |
182 | |
183 switch(aluBytesFromFormat(device->Format)) | |
184 { | |
185 case 1: | |
186 ossFormat = AFMT_U8; | |
187 break; | |
188 case 2: | |
189 ossFormat = AFMT_S16_NE; | |
190 break; | |
191 default: | |
192 ossFormat = -1; | |
193 AL_PRINT("Unknown format?! %x\n", device->Format); | |
194 } | |
195 | |
196 periods = GetConfigValueInt("oss", "periods", 4); | |
197 if((int)periods <= 0) | |
198 periods = 4; | |
199 numChannels = aluChannelsFromFormat(device->Format); | |
200 frameSize = numChannels * aluBytesFromFormat(device->Format); | |
201 | |
202 ossSpeed = device->Frequency; | |
203 log2FragmentSize = log2i(device->UpdateSize * frameSize / periods); | |
204 | |
205 /* according to the OSS spec, 16 bytes are the minimum */ | |
206 if (log2FragmentSize < 4) | |
207 log2FragmentSize = 4; | |
208 numFragmentsLogSize = (periods << 16) | log2FragmentSize; | |
209 | |
210 #define ok(func, str) (i=(func),((i<0)?(err=(str)),0:1)) | |
211 if (!(ok(ioctl(data->fd, SNDCTL_DSP_SETFRAGMENT, &numFragmentsLogSize), "set fragment") && | |
212 ok(ioctl(data->fd, SNDCTL_DSP_SETFMT, &ossFormat), "set format") && | |
213 ok(ioctl(data->fd, SNDCTL_DSP_CHANNELS, &numChannels), "set channels") && | |
214 ok(ioctl(data->fd, SNDCTL_DSP_SPEED, &ossSpeed), "set speed") && | |
215 ok(ioctl(data->fd, SNDCTL_DSP_GETOSPACE, &info), "get space"))) | |
216 { | |
217 AL_PRINT("%s failed: %s\n", err, strerror(errno)); | |
218 close(data->fd); | |
219 free(data); | |
220 return ALC_FALSE; | |
221 } | |
222 #undef ok | |
223 | |
224 device->Frequency = ossSpeed; | |
225 | |
226 if((int)aluChannelsFromFormat(device->Format) != numChannels) | |
227 { | |
228 AL_PRINT("Could not set %d channels, got %d instead\n", aluChannelsFromFormat(device->Format), numChannels); | |
229 close(data->fd); | |
230 free(data); | |
231 return ALC_FALSE; | |
232 } | |
233 | |
234 if(!((ossFormat == AFMT_U8 && aluBytesFromFormat(device->Format) == 1) || | |
235 (ossFormat == AFMT_S16_NE && aluBytesFromFormat(device->Format) == 2))) | |
236 { | |
237 AL_PRINT("Could not set %d-bit output, got format %#x\n", aluBytesFromFormat(device->Format)*8, ossFormat); | |
238 close(data->fd); | |
239 free(data); | |
240 return ALC_FALSE; | |
241 } | |
242 | |
243 device->UpdateSize = info.fragsize / frameSize; | |
244 | |
245 data->data_size = device->UpdateSize * frameSize; | |
246 data->mix_data = calloc(1, data->data_size); | |
247 | |
248 device->ExtraData = data; | |
249 data->thread = StartThread(OSSProc, device); | |
250 if(data->thread == NULL) | |
251 { | |
252 device->ExtraData = NULL; | |
253 free(data->mix_data); | |
254 free(data); | |
255 return ALC_FALSE; | |
256 } | |
257 | |
258 return ALC_TRUE; | |
259 } | |
260 | |
261 static void oss_close_playback(ALCdevice *device) | |
262 { | |
263 oss_data *data = (oss_data*)device->ExtraData; | |
264 data->killNow = 1; | |
265 StopThread(data->thread); | |
266 | |
267 close(data->fd); | |
268 | |
269 free(data->mix_data); | |
270 free(data); | |
271 device->ExtraData = NULL; | |
272 } | |
273 | |
274 | |
275 static ALCboolean oss_open_capture(ALCdevice *device, const ALCchar *deviceName, ALCuint frequency, ALCenum format, ALCsizei SampleSize) | |
276 { | |
277 int numFragmentsLogSize; | |
278 int log2FragmentSize; | |
279 unsigned int periods; | |
280 audio_buf_info info; | |
281 ALuint frameSize; | |
282 int numChannels; | |
283 char driver[64]; | |
284 oss_data *data; | |
285 int ossFormat; | |
286 int ossSpeed; | |
287 char *err; | |
288 int i; | |
289 | |
290 strncpy(driver, GetConfigValue("oss", "capture", "/dev/dsp"), sizeof(driver)-1); | |
291 driver[sizeof(driver)-1] = 0; | |
292 if(deviceName) | |
293 { | |
294 if(strcmp(deviceName, oss_device_capture)) | |
295 return ALC_FALSE; | |
296 device->szDeviceName = oss_device_capture; | |
297 } | |
298 else | |
299 device->szDeviceName = oss_device_capture; | |
300 | |
301 data = (oss_data*)calloc(1, sizeof(oss_data)); | |
302 data->killNow = 0; | |
303 | |
304 data->fd = open(driver, O_RDONLY); | |
305 if(data->fd == -1) | |
306 { | |
307 free(data); | |
308 AL_PRINT("Could not open %s: %s\n", driver, strerror(errno)); | |
309 return ALC_FALSE; | |
310 } | |
311 | |
312 switch(aluBytesFromFormat(format)) | |
313 { | |
314 case 1: | |
315 ossFormat = AFMT_U8; | |
316 break; | |
317 case 2: | |
318 ossFormat = AFMT_S16_NE; | |
319 break; | |
320 default: | |
321 ossFormat = -1; | |
322 AL_PRINT("Unknown format?! %x\n", device->Format); | |
323 } | |
324 | |
325 periods = 4; | |
326 numChannels = aluChannelsFromFormat(device->Format); | |
327 frameSize = numChannels * aluBytesFromFormat(device->Format); | |
328 ossSpeed = frequency; | |
329 log2FragmentSize = log2i(SampleSize * frameSize / periods); | |
330 | |
331 /* according to the OSS spec, 16 bytes are the minimum */ | |
332 if (log2FragmentSize < 4) | |
333 log2FragmentSize = 4; | |
334 numFragmentsLogSize = (periods << 16) | log2FragmentSize; | |
335 | |
336 #define ok(func, str) (i=(func),((i<0)?(err=(str)),0:1)) | |
337 if (!(ok(ioctl(data->fd, SNDCTL_DSP_SETFRAGMENT, &numFragmentsLogSize), "set fragment") && | |
338 ok(ioctl(data->fd, SNDCTL_DSP_SETFMT, &ossFormat), "set format") && | |
339 ok(ioctl(data->fd, SNDCTL_DSP_CHANNELS, &numChannels), "set channels") && | |
340 ok(ioctl(data->fd, SNDCTL_DSP_SPEED, &ossSpeed), "set speed") && | |
341 ok(ioctl(data->fd, SNDCTL_DSP_GETISPACE, &info), "get space"))) | |
342 { | |
343 AL_PRINT("%s failed: %s\n", err, strerror(errno)); | |
344 close(data->fd); | |
345 free(data); | |
346 return ALC_FALSE; | |
347 } | |
348 #undef ok | |
349 | |
350 if((int)aluChannelsFromFormat(device->Format) != numChannels) | |
351 { | |
352 AL_PRINT("Could not set %d channels, got %d instead\n", aluChannelsFromFormat(device->Format), numChannels); | |
353 close(data->fd); | |
354 free(data); | |
355 return ALC_FALSE; | |
356 } | |
357 | |
358 if(!((ossFormat == AFMT_U8 && aluBytesFromFormat(device->Format) == 1) || | |
359 (ossFormat == AFMT_S16_NE && aluBytesFromFormat(device->Format) == 2))) | |
360 { | |
361 AL_PRINT("Could not set %d-bit input, got format %#x\n", aluBytesFromFormat(device->Format)*8, ossFormat); | |
362 close(data->fd); | |
363 free(data); | |
364 return ALC_FALSE; | |
365 } | |
366 | |
367 data->ring = CreateRingBuffer(frameSize, SampleSize); | |
368 if(!data->ring) | |
369 { | |
370 AL_PRINT("ring buffer create failed\n"); | |
371 close(data->fd); | |
372 free(data); | |
373 return ALC_FALSE; | |
374 } | |
375 | |
376 data->data_size = info.fragsize; | |
377 data->mix_data = calloc(1, data->data_size); | |
378 | |
379 device->ExtraData = data; | |
380 data->thread = StartThread(OSSCaptureProc, device); | |
381 if(data->thread == NULL) | |
382 { | |
383 device->ExtraData = NULL; | |
384 free(data->mix_data); | |
385 free(data); | |
386 return ALC_FALSE; | |
387 } | |
388 | |
389 return ALC_TRUE; | |
390 } | |
391 | |
392 static void oss_close_capture(ALCdevice *device) | |
393 { | |
394 oss_data *data = (oss_data*)device->ExtraData; | |
395 data->killNow = 1; | |
396 StopThread(data->thread); | |
397 | |
398 close(data->fd); | |
399 | |
400 DestroyRingBuffer(data->ring); | |
401 | |
402 free(data->mix_data); | |
403 free(data); | |
404 device->ExtraData = NULL; | |
405 } | |
406 | |
407 static void oss_start_capture(ALCdevice *pDevice) | |
408 { | |
409 oss_data *data = (oss_data*)pDevice->ExtraData; | |
410 data->doCapture = 1; | |
411 } | |
412 | |
413 static void oss_stop_capture(ALCdevice *pDevice) | |
414 { | |
415 oss_data *data = (oss_data*)pDevice->ExtraData; | |
416 data->doCapture = 0; | |
417 } | |
418 | |
419 static void oss_capture_samples(ALCdevice *pDevice, ALCvoid *pBuffer, ALCuint lSamples) | |
420 { | |
421 oss_data *data = (oss_data*)pDevice->ExtraData; | |
422 if(lSamples <= (ALCuint)RingBufferSize(data->ring)) | |
423 ReadRingBuffer(data->ring, pBuffer, lSamples); | |
424 else | |
425 SetALCError(ALC_INVALID_VALUE); | |
426 } | |
427 | |
428 static ALCuint oss_available_samples(ALCdevice *pDevice) | |
429 { | |
430 oss_data *data = (oss_data*)pDevice->ExtraData; | |
431 return RingBufferSize(data->ring); | |
432 } | |
433 | |
434 | |
435 BackendFuncs oss_funcs = { | |
436 oss_open_playback, | |
437 oss_close_playback, | |
438 oss_open_capture, | |
439 oss_close_capture, | |
440 oss_start_capture, | |
441 oss_stop_capture, | |
442 oss_capture_samples, | |
443 oss_available_samples | |
444 }; | |
445 | |
446 void alc_oss_init(BackendFuncs *func_list) | |
447 { | |
448 *func_list = oss_funcs; | |
449 | |
450 oss_device = AppendDeviceList("OSS Software"); | |
451 AppendAllDeviceList(oss_device); | |
452 | |
453 oss_device_capture = AppendCaptureDeviceList("OSS Capture"); | |
454 } |