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 Default";
65 static int log2i(ALCuint x
)
77 static ALuint
OSSProc(ALvoid
*ptr
)
79 ALCdevice
*pDevice
= (ALCdevice
*)ptr
;
80 oss_data
*data
= (oss_data
*)pDevice
->ExtraData
;
86 frameSize
= FrameSizeFromDevFmt(pDevice
->FmtChans
, pDevice
->FmtType
);
88 while(!data
->killNow
&& pDevice
->Connected
)
90 ALint len
= data
->data_size
;
91 ALubyte
*WritePtr
= data
->mix_data
;
93 aluMixData(pDevice
, WritePtr
, len
/frameSize
);
94 while(len
> 0 && !data
->killNow
)
96 wrote
= write(data
->fd
, WritePtr
, len
);
99 if(errno
!= EAGAIN
&& errno
!= EWOULDBLOCK
&& errno
!= EINTR
)
101 AL_PRINT("write failed: %s\n", strerror(errno
));
102 aluHandleDisconnect(pDevice
);
118 static ALuint
OSSCaptureProc(ALvoid
*ptr
)
120 ALCdevice
*pDevice
= (ALCdevice
*)ptr
;
121 oss_data
*data
= (oss_data
*)pDevice
->ExtraData
;
127 frameSize
= FrameSizeFromDevFmt(pDevice
->FmtChans
, pDevice
->FmtType
);
129 while(!data
->killNow
)
131 amt
= read(data
->fd
, data
->mix_data
, data
->data_size
);
134 AL_PRINT("read failed: %s\n", strerror(errno
));
135 aluHandleDisconnect(pDevice
);
144 WriteRingBuffer(data
->ring
, data
->mix_data
, amt
/frameSize
);
150 static ALCboolean
oss_open_playback(ALCdevice
*device
, const ALCchar
*deviceName
)
155 strncpy(driver
, GetConfigValue("oss", "device", "/dev/dsp"), sizeof(driver
)-1);
156 driver
[sizeof(driver
)-1] = 0;
158 deviceName
= oss_device
;
159 else if(strcmp(deviceName
, oss_device
) != 0)
162 data
= (oss_data
*)calloc(1, sizeof(oss_data
));
165 data
->fd
= open(driver
, O_WRONLY
);
169 AL_PRINT("Could not open %s: %s\n", driver
, strerror(errno
));
173 device
->szDeviceName
= strdup(deviceName
);
174 device
->ExtraData
= data
;
178 static void oss_close_playback(ALCdevice
*device
)
180 oss_data
*data
= (oss_data
*)device
->ExtraData
;
184 device
->ExtraData
= NULL
;
187 static ALCboolean
oss_reset_playback(ALCdevice
*device
)
189 oss_data
*data
= (oss_data
*)device
->ExtraData
;
190 int numFragmentsLogSize
;
191 int log2FragmentSize
;
192 unsigned int periods
;
201 switch(device
->FmtType
)
211 device
->FmtType
= DevFmtShort
;
214 ossFormat
= AFMT_S16_NE
;
218 periods
= device
->NumUpdates
;
219 numChannels
= ChannelsFromDevFmt(device
->FmtChans
);
220 frameSize
= numChannels
* BytesFromDevFmt(device
->FmtType
);
222 ossSpeed
= device
->Frequency
;
223 log2FragmentSize
= log2i(device
->UpdateSize
* frameSize
);
225 /* according to the OSS spec, 16 bytes are the minimum */
226 if (log2FragmentSize
< 4)
227 log2FragmentSize
= 4;
228 /* Subtract one period since the temp mixing buffer counts as one. Still
229 * need at least two on the card, though. */
230 if(periods
> 2) periods
--;
231 numFragmentsLogSize
= (periods
<< 16) | log2FragmentSize
;
233 #define ok(func, str) (i=(func),((i<0)?(err=(str)),0:1))
234 /* Don't fail if SETFRAGMENT fails. We can handle just about anything
235 * that's reported back via GETOSPACE */
236 ioctl(data
->fd
, SNDCTL_DSP_SETFRAGMENT
, &numFragmentsLogSize
);
237 if (!(ok(ioctl(data
->fd
, SNDCTL_DSP_SETFMT
, &ossFormat
), "set format") &&
238 ok(ioctl(data
->fd
, SNDCTL_DSP_CHANNELS
, &numChannels
), "set channels") &&
239 ok(ioctl(data
->fd
, SNDCTL_DSP_SPEED
, &ossSpeed
), "set speed") &&
240 ok(ioctl(data
->fd
, SNDCTL_DSP_GETOSPACE
, &info
), "get space")))
242 AL_PRINT("%s failed: %s\n", err
, strerror(errno
));
247 if((int)ChannelsFromDevFmt(device
->FmtChans
) != numChannels
)
249 AL_PRINT("Could not set %d channels, got %d instead\n", ChannelsFromDevFmt(device
->FmtChans
), numChannels
);
253 if(!((ossFormat
== AFMT_S8
&& device
->FmtType
== DevFmtByte
) ||
254 (ossFormat
== AFMT_U8
&& device
->FmtType
== DevFmtUByte
) ||
255 (ossFormat
== AFMT_S16_NE
&& device
->FmtType
== DevFmtShort
)))
257 AL_PRINT("Could not set %#x format type, got OSS format %#x\n", device
->FmtType
, ossFormat
);
261 device
->Frequency
= ossSpeed
;
262 device
->UpdateSize
= info
.fragsize
/ frameSize
;
263 device
->NumUpdates
= info
.fragments
+ 1;
265 data
->data_size
= device
->UpdateSize
* frameSize
;
266 data
->mix_data
= calloc(1, data
->data_size
);
268 SetDefaultChannelOrder(device
);
270 data
->thread
= StartThread(OSSProc
, device
);
271 if(data
->thread
== NULL
)
273 free(data
->mix_data
);
274 data
->mix_data
= NULL
;
281 static void oss_stop_playback(ALCdevice
*device
)
283 oss_data
*data
= (oss_data
*)device
->ExtraData
;
289 StopThread(data
->thread
);
293 if(ioctl(data
->fd
, SNDCTL_DSP_RESET
) != 0)
294 AL_PRINT("Error resetting device: %s\n", strerror(errno
));
296 free(data
->mix_data
);
297 data
->mix_data
= NULL
;
301 static ALCboolean
oss_open_capture(ALCdevice
*device
, const ALCchar
*deviceName
)
303 int numFragmentsLogSize
;
304 int log2FragmentSize
;
305 unsigned int periods
;
316 strncpy(driver
, GetConfigValue("oss", "capture", "/dev/dsp"), sizeof(driver
)-1);
317 driver
[sizeof(driver
)-1] = 0;
319 deviceName
= oss_device
;
320 else if(strcmp(deviceName
, oss_device
) != 0)
323 data
= (oss_data
*)calloc(1, sizeof(oss_data
));
326 data
->fd
= open(driver
, O_RDONLY
);
330 AL_PRINT("Could not open %s: %s\n", driver
, strerror(errno
));
334 switch(device
->FmtType
)
343 ossFormat
= AFMT_S16_NE
;
348 AL_PRINT("Format type %#x capture not supported on OSS\n", device
->FmtType
);
353 numChannels
= ChannelsFromDevFmt(device
->FmtChans
);
354 frameSize
= numChannels
* BytesFromDevFmt(device
->FmtType
);
355 ossSpeed
= device
->Frequency
;
356 log2FragmentSize
= log2i(device
->UpdateSize
* device
->NumUpdates
*
357 frameSize
/ periods
);
359 /* according to the OSS spec, 16 bytes are the minimum */
360 if (log2FragmentSize
< 4)
361 log2FragmentSize
= 4;
362 numFragmentsLogSize
= (periods
<< 16) | log2FragmentSize
;
364 #define ok(func, str) (i=(func),((i<0)?(err=(str)),0:1))
365 if (!(ok(ioctl(data
->fd
, SNDCTL_DSP_SETFRAGMENT
, &numFragmentsLogSize
), "set fragment") &&
366 ok(ioctl(data
->fd
, SNDCTL_DSP_SETFMT
, &ossFormat
), "set format") &&
367 ok(ioctl(data
->fd
, SNDCTL_DSP_CHANNELS
, &numChannels
), "set channels") &&
368 ok(ioctl(data
->fd
, SNDCTL_DSP_SPEED
, &ossSpeed
), "set speed") &&
369 ok(ioctl(data
->fd
, SNDCTL_DSP_GETISPACE
, &info
), "get space")))
371 AL_PRINT("%s failed: %s\n", err
, strerror(errno
));
378 if((int)ChannelsFromDevFmt(device
->FmtChans
) != numChannels
)
380 AL_PRINT("Could not set %d channels, got %d instead\n", ChannelsFromDevFmt(device
->FmtChans
), numChannels
);
386 if(!((ossFormat
== AFMT_S8
&& device
->FmtType
== DevFmtByte
) ||
387 (ossFormat
== AFMT_U8
&& device
->FmtType
== DevFmtUByte
) ||
388 (ossFormat
== AFMT_S16_NE
&& device
->FmtType
== DevFmtShort
)))
390 AL_PRINT("Could not set %#x format type, got OSS format %#x\n", device
->FmtType
, ossFormat
);
396 data
->ring
= CreateRingBuffer(frameSize
, device
->UpdateSize
* device
->NumUpdates
);
399 AL_PRINT("ring buffer create failed\n");
405 data
->data_size
= info
.fragsize
;
406 data
->mix_data
= calloc(1, data
->data_size
);
408 device
->ExtraData
= data
;
409 data
->thread
= StartThread(OSSCaptureProc
, device
);
410 if(data
->thread
== NULL
)
412 device
->ExtraData
= NULL
;
413 free(data
->mix_data
);
418 device
->szDeviceName
= strdup(deviceName
);
422 static void oss_close_capture(ALCdevice
*device
)
424 oss_data
*data
= (oss_data
*)device
->ExtraData
;
426 StopThread(data
->thread
);
430 DestroyRingBuffer(data
->ring
);
432 free(data
->mix_data
);
434 device
->ExtraData
= NULL
;
437 static void oss_start_capture(ALCdevice
*pDevice
)
439 oss_data
*data
= (oss_data
*)pDevice
->ExtraData
;
443 static void oss_stop_capture(ALCdevice
*pDevice
)
445 oss_data
*data
= (oss_data
*)pDevice
->ExtraData
;
449 static void oss_capture_samples(ALCdevice
*pDevice
, ALCvoid
*pBuffer
, ALCuint lSamples
)
451 oss_data
*data
= (oss_data
*)pDevice
->ExtraData
;
452 if(lSamples
<= (ALCuint
)RingBufferSize(data
->ring
))
453 ReadRingBuffer(data
->ring
, pBuffer
, lSamples
);
455 alcSetError(pDevice
, ALC_INVALID_VALUE
);
458 static ALCuint
oss_available_samples(ALCdevice
*pDevice
)
460 oss_data
*data
= (oss_data
*)pDevice
->ExtraData
;
461 return RingBufferSize(data
->ring
);
465 BackendFuncs oss_funcs
= {
475 oss_available_samples
478 void alc_oss_init(BackendFuncs
*func_list
)
480 *func_list
= oss_funcs
;
483 void alc_oss_deinit(void)
487 void alc_oss_probe(int type
)
489 if(type
== DEVICE_PROBE
)
493 if(stat(GetConfigValue("oss", "device", "/dev/dsp"), &buf
) == 0)
495 AppendDeviceList(oss_device
);
497 else if(type
== ALL_DEVICE_PROBE
)
501 if(stat(GetConfigValue("oss", "device", "/dev/dsp"), &buf
) == 0)
503 AppendAllDeviceList(oss_device
);
505 else if(type
== CAPTURE_DEVICE_PROBE
)
509 if(stat(GetConfigValue("oss", "capture", "/dev/dsp"), &buf
) == 0)
511 AppendCaptureDeviceList(oss_device
);