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.
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.
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
23 #include <sys/ioctl.h>
24 #include <sys/types.h>
37 #include <sys/soundcard.h>
40 * The OSS documentation talks about SOUND_MIXER_READ, but the header
41 * only contains MIXER_READ. Play safe. Same for WRITE.
43 #ifndef SOUND_MIXER_READ
44 #define SOUND_MIXER_READ MIXER_READ
46 #ifndef SOUND_MIXER_WRITE
47 #define SOUND_MIXER_WRITE MIXER_WRITE
50 static const ALCchar oss_device
[] = "OSS Software";
51 static const ALCchar oss_device_capture
[] = "OSS Capture";
66 static int log2i(ALCuint x
)
78 static ALuint
OSSProc(ALvoid
*ptr
)
80 ALCdevice
*pDevice
= (ALCdevice
*)ptr
;
81 oss_data
*data
= (oss_data
*)pDevice
->ExtraData
;
87 frameSize
= aluChannelsFromFormat(pDevice
->Format
) *
88 aluBytesFromFormat(pDevice
->Format
);
90 while(!data
->killNow
&& pDevice
->Connected
)
92 ALint len
= data
->data_size
;
93 ALubyte
*WritePtr
= data
->mix_data
;
95 aluMixData(pDevice
, WritePtr
, len
/frameSize
);
96 while(len
> 0 && !data
->killNow
)
98 wrote
= write(data
->fd
, WritePtr
, len
);
101 if(errno
!= EAGAIN
&& errno
!= EWOULDBLOCK
&& errno
!= EINTR
)
103 AL_PRINT("write failed: %s\n", strerror(errno
));
104 aluHandleDisconnect(pDevice
);
120 static ALuint
OSSCaptureProc(ALvoid
*ptr
)
122 ALCdevice
*pDevice
= (ALCdevice
*)ptr
;
123 oss_data
*data
= (oss_data
*)pDevice
->ExtraData
;
129 frameSize
= aluBytesFromFormat(pDevice
->Format
);
130 frameSize
*= aluChannelsFromFormat(pDevice
->Format
);
132 while(!data
->killNow
)
134 amt
= read(data
->fd
, data
->mix_data
, data
->data_size
);
137 AL_PRINT("read failed: %s\n", strerror(errno
));
138 aluHandleDisconnect(pDevice
);
147 WriteRingBuffer(data
->ring
, data
->mix_data
, amt
/frameSize
);
153 static ALCboolean
oss_open_playback(ALCdevice
*device
, const ALCchar
*deviceName
)
158 strncpy(driver
, GetConfigValue("oss", "device", "/dev/dsp"), sizeof(driver
)-1);
159 driver
[sizeof(driver
)-1] = 0;
161 deviceName
= oss_device
;
162 else if(strcmp(deviceName
, oss_device
) != 0)
165 data
= (oss_data
*)calloc(1, sizeof(oss_data
));
168 data
->fd
= open(driver
, O_WRONLY
);
173 AL_PRINT("Could not open %s: %s\n", driver
, strerror(errno
));
177 device
->szDeviceName
= strdup(deviceName
);
178 device
->ExtraData
= data
;
182 static void oss_close_playback(ALCdevice
*device
)
184 oss_data
*data
= (oss_data
*)device
->ExtraData
;
188 device
->ExtraData
= NULL
;
191 static ALCboolean
oss_reset_playback(ALCdevice
*device
)
193 oss_data
*data
= (oss_data
*)device
->ExtraData
;
194 int numFragmentsLogSize
;
195 int log2FragmentSize
;
196 unsigned int periods
;
205 switch(aluBytesFromFormat(device
->Format
))
211 switch(aluChannelsFromFormat(device
->Format
))
213 case 1: device
->Format
= AL_FORMAT_MONO16
; break;
214 case 2: device
->Format
= AL_FORMAT_STEREO16
; break;
215 case 4: device
->Format
= AL_FORMAT_QUAD16
; break;
216 case 6: device
->Format
= AL_FORMAT_51CHN16
; break;
217 case 7: device
->Format
= AL_FORMAT_61CHN16
; break;
218 case 8: device
->Format
= AL_FORMAT_71CHN16
; break;
222 ossFormat
= AFMT_S16_NE
;
225 AL_PRINT("Unknown format: 0x%x\n", device
->Format
);
229 periods
= device
->NumUpdates
;
230 numChannels
= aluChannelsFromFormat(device
->Format
);
231 frameSize
= numChannels
* aluBytesFromFormat(device
->Format
);
233 ossSpeed
= device
->Frequency
;
234 log2FragmentSize
= log2i(device
->UpdateSize
* frameSize
);
236 /* according to the OSS spec, 16 bytes are the minimum */
237 if (log2FragmentSize
< 4)
238 log2FragmentSize
= 4;
239 /* Subtract one period since the temp mixing buffer counts as one. Still
240 * need at least two on the card, though. */
241 if(periods
> 2) periods
--;
242 numFragmentsLogSize
= (periods
<< 16) | log2FragmentSize
;
244 #define ok(func, str) (i=(func),((i<0)?(err=(str)),0:1))
245 /* Don't fail if SETFRAGMENT fails. We can handle just about anything
246 * that's reported back via GETOSPACE */
247 ioctl(data
->fd
, SNDCTL_DSP_SETFRAGMENT
, &numFragmentsLogSize
);
248 if (!(ok(ioctl(data
->fd
, SNDCTL_DSP_SETFMT
, &ossFormat
), "set format") &&
249 ok(ioctl(data
->fd
, SNDCTL_DSP_CHANNELS
, &numChannels
), "set channels") &&
250 ok(ioctl(data
->fd
, SNDCTL_DSP_SPEED
, &ossSpeed
), "set speed") &&
251 ok(ioctl(data
->fd
, SNDCTL_DSP_GETOSPACE
, &info
), "get space")))
253 AL_PRINT("%s failed: %s\n", err
, strerror(errno
));
258 if((int)aluChannelsFromFormat(device
->Format
) != numChannels
)
260 AL_PRINT("Could not set %d channels, got %d instead\n", aluChannelsFromFormat(device
->Format
), numChannels
);
264 if(!((ossFormat
== AFMT_U8
&& aluBytesFromFormat(device
->Format
) == 1) ||
265 (ossFormat
== AFMT_S16_NE
&& aluBytesFromFormat(device
->Format
) == 2)))
267 AL_PRINT("Could not set %d-bit output, got format %#x\n", aluBytesFromFormat(device
->Format
)*8, ossFormat
);
271 device
->Frequency
= ossSpeed
;
272 device
->UpdateSize
= info
.fragsize
/ frameSize
;
273 device
->NumUpdates
= info
.fragments
+ 1;
275 data
->data_size
= device
->UpdateSize
* frameSize
;
276 data
->mix_data
= calloc(1, data
->data_size
);
278 SetDefaultChannelOrder(device
);
280 data
->thread
= StartThread(OSSProc
, device
);
281 if(data
->thread
== NULL
)
283 free(data
->mix_data
);
284 data
->mix_data
= NULL
;
291 static void oss_stop_playback(ALCdevice
*device
)
293 oss_data
*data
= (oss_data
*)device
->ExtraData
;
299 StopThread(data
->thread
);
303 if(ioctl(data
->fd
, SNDCTL_DSP_RESET
) != 0)
304 AL_PRINT("Error resetting device: %s\n", strerror(errno
));
306 free(data
->mix_data
);
307 data
->mix_data
= NULL
;
311 static ALCboolean
oss_open_capture(ALCdevice
*device
, const ALCchar
*deviceName
)
313 int numFragmentsLogSize
;
314 int log2FragmentSize
;
315 unsigned int periods
;
326 strncpy(driver
, GetConfigValue("oss", "capture", "/dev/dsp"), sizeof(driver
)-1);
327 driver
[sizeof(driver
)-1] = 0;
329 deviceName
= oss_device_capture
;
330 else if(strcmp(deviceName
, oss_device_capture
) != 0)
333 data
= (oss_data
*)calloc(1, sizeof(oss_data
));
336 data
->fd
= open(driver
, O_RDONLY
);
341 AL_PRINT("Could not open %s: %s\n", driver
, strerror(errno
));
345 switch(aluBytesFromFormat(device
->Format
))
351 ossFormat
= AFMT_S16_NE
;
354 AL_PRINT("Unknown format: 0x%x\n", device
->Format
);
361 numChannels
= aluChannelsFromFormat(device
->Format
);
362 frameSize
= numChannels
* aluBytesFromFormat(device
->Format
);
363 ossSpeed
= device
->Frequency
;
364 log2FragmentSize
= log2i(device
->UpdateSize
* device
->NumUpdates
*
365 frameSize
/ periods
);
367 /* according to the OSS spec, 16 bytes are the minimum */
368 if (log2FragmentSize
< 4)
369 log2FragmentSize
= 4;
370 numFragmentsLogSize
= (periods
<< 16) | log2FragmentSize
;
372 #define ok(func, str) (i=(func),((i<0)?(err=(str)),0:1))
373 if (!(ok(ioctl(data
->fd
, SNDCTL_DSP_SETFRAGMENT
, &numFragmentsLogSize
), "set fragment") &&
374 ok(ioctl(data
->fd
, SNDCTL_DSP_SETFMT
, &ossFormat
), "set format") &&
375 ok(ioctl(data
->fd
, SNDCTL_DSP_CHANNELS
, &numChannels
), "set channels") &&
376 ok(ioctl(data
->fd
, SNDCTL_DSP_SPEED
, &ossSpeed
), "set speed") &&
377 ok(ioctl(data
->fd
, SNDCTL_DSP_GETISPACE
, &info
), "get space")))
379 AL_PRINT("%s failed: %s\n", err
, strerror(errno
));
386 if((int)aluChannelsFromFormat(device
->Format
) != numChannels
)
388 AL_PRINT("Could not set %d channels, got %d instead\n", aluChannelsFromFormat(device
->Format
), numChannels
);
394 if(!((ossFormat
== AFMT_U8
&& aluBytesFromFormat(device
->Format
) == 1) ||
395 (ossFormat
== AFMT_S16_NE
&& aluBytesFromFormat(device
->Format
) == 2)))
397 AL_PRINT("Could not set %d-bit input, got format %#x\n", aluBytesFromFormat(device
->Format
)*8, ossFormat
);
403 data
->ring
= CreateRingBuffer(frameSize
, device
->UpdateSize
* device
->NumUpdates
);
406 AL_PRINT("ring buffer create failed\n");
412 data
->data_size
= info
.fragsize
;
413 data
->mix_data
= calloc(1, data
->data_size
);
415 device
->ExtraData
= data
;
416 data
->thread
= StartThread(OSSCaptureProc
, device
);
417 if(data
->thread
== NULL
)
419 device
->ExtraData
= NULL
;
420 free(data
->mix_data
);
425 device
->szDeviceName
= strdup(deviceName
);
429 static void oss_close_capture(ALCdevice
*device
)
431 oss_data
*data
= (oss_data
*)device
->ExtraData
;
433 StopThread(data
->thread
);
437 DestroyRingBuffer(data
->ring
);
439 free(data
->mix_data
);
441 device
->ExtraData
= NULL
;
444 static void oss_start_capture(ALCdevice
*pDevice
)
446 oss_data
*data
= (oss_data
*)pDevice
->ExtraData
;
450 static void oss_stop_capture(ALCdevice
*pDevice
)
452 oss_data
*data
= (oss_data
*)pDevice
->ExtraData
;
456 static void oss_capture_samples(ALCdevice
*pDevice
, ALCvoid
*pBuffer
, ALCuint lSamples
)
458 oss_data
*data
= (oss_data
*)pDevice
->ExtraData
;
459 if(lSamples
<= (ALCuint
)RingBufferSize(data
->ring
))
460 ReadRingBuffer(data
->ring
, pBuffer
, lSamples
);
462 alcSetError(pDevice
, ALC_INVALID_VALUE
);
465 static ALCuint
oss_available_samples(ALCdevice
*pDevice
)
467 oss_data
*data
= (oss_data
*)pDevice
->ExtraData
;
468 return RingBufferSize(data
->ring
);
472 BackendFuncs oss_funcs
= {
482 oss_available_samples
485 void alc_oss_init(BackendFuncs
*func_list
)
487 *func_list
= oss_funcs
;
490 void alc_oss_deinit(void)
494 void alc_oss_probe(int type
)
496 if(type
== DEVICE_PROBE
)
500 if(stat(GetConfigValue("oss", "device", "/dev/dsp"), &buf
) == 0)
502 AppendDeviceList(oss_device
);
504 else if(type
== ALL_DEVICE_PROBE
)
508 if(stat(GetConfigValue("oss", "device", "/dev/dsp"), &buf
) == 0)
510 AppendAllDeviceList(oss_device
);
512 else if(type
== CAPTURE_DEVICE_PROBE
)
516 if(stat(GetConfigValue("oss", "capture", "/dev/dsp"), &buf
) == 0)
518 AppendCaptureDeviceList(oss_device_capture
);