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";
52 static const char *oss_driver
= "/dev/dsp";
53 static const char *oss_capture
= "/dev/dsp";
68 static int log2i(ALCuint x
)
80 static ALuint
OSSProc(ALvoid
*ptr
)
82 ALCdevice
*Device
= (ALCdevice
*)ptr
;
83 oss_data
*data
= (oss_data
*)Device
->ExtraData
;
89 frameSize
= FrameSizeFromDevFmt(Device
->FmtChans
, Device
->FmtType
);
91 while(!data
->killNow
&& Device
->Connected
)
93 ALint len
= data
->data_size
;
94 ALubyte
*WritePtr
= data
->mix_data
;
96 aluMixData(Device
, WritePtr
, len
/frameSize
);
97 while(len
> 0 && !data
->killNow
)
99 wrote
= write(data
->fd
, WritePtr
, len
);
102 if(errno
!= EAGAIN
&& errno
!= EWOULDBLOCK
&& errno
!= EINTR
)
104 ERR("write failed: %s\n", strerror(errno
));
105 aluHandleDisconnect(Device
);
121 static ALuint
OSSCaptureProc(ALvoid
*ptr
)
123 ALCdevice
*Device
= (ALCdevice
*)ptr
;
124 oss_data
*data
= (oss_data
*)Device
->ExtraData
;
130 frameSize
= FrameSizeFromDevFmt(Device
->FmtChans
, Device
->FmtType
);
132 while(!data
->killNow
)
134 amt
= read(data
->fd
, data
->mix_data
, data
->data_size
);
137 ERR("read failed: %s\n", strerror(errno
));
138 aluHandleDisconnect(Device
);
147 WriteRingBuffer(data
->ring
, data
->mix_data
, amt
/frameSize
);
153 static ALCenum
oss_open_playback(ALCdevice
*device
, const ALCchar
*deviceName
)
158 deviceName
= oss_device
;
159 else if(strcmp(deviceName
, oss_device
) != 0)
160 return ALC_INVALID_VALUE
;
162 data
= (oss_data
*)calloc(1, sizeof(oss_data
));
165 data
->fd
= open(oss_driver
, O_WRONLY
);
169 ERR("Could not open %s: %s\n", oss_driver
, strerror(errno
));
170 return ALC_INVALID_VALUE
;
173 device
->DeviceName
= 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
;
200 switch(device
->FmtType
)
212 device
->FmtType
= DevFmtShort
;
215 ossFormat
= AFMT_S16_NE
;
219 periods
= device
->NumUpdates
;
220 numChannels
= ChannelsFromDevFmt(device
->FmtChans
);
221 frameSize
= numChannels
* BytesFromDevFmt(device
->FmtType
);
223 ossSpeed
= device
->Frequency
;
224 log2FragmentSize
= log2i(device
->UpdateSize
* frameSize
);
226 /* according to the OSS spec, 16 bytes are the minimum */
227 if (log2FragmentSize
< 4)
228 log2FragmentSize
= 4;
229 /* Subtract one period since the temp mixing buffer counts as one. Still
230 * need at least two on the card, though. */
231 if(periods
> 2) periods
--;
232 numFragmentsLogSize
= (periods
<< 16) | log2FragmentSize
;
234 #define CHECKERR(func) if((func) < 0) { \
238 /* Don't fail if SETFRAGMENT fails. We can handle just about anything
239 * that's reported back via GETOSPACE */
240 ioctl(data
->fd
, SNDCTL_DSP_SETFRAGMENT
, &numFragmentsLogSize
);
241 CHECKERR(ioctl(data
->fd
, SNDCTL_DSP_SETFMT
, &ossFormat
));
242 CHECKERR(ioctl(data
->fd
, SNDCTL_DSP_CHANNELS
, &numChannels
));
243 CHECKERR(ioctl(data
->fd
, SNDCTL_DSP_SPEED
, &ossSpeed
));
244 CHECKERR(ioctl(data
->fd
, SNDCTL_DSP_GETOSPACE
, &info
));
248 ERR("%s failed: %s\n", err
, strerror(errno
));
253 if((int)ChannelsFromDevFmt(device
->FmtChans
) != numChannels
)
255 ERR("Failed to set %s, got %d channels instead\n", DevFmtChannelsString(device
->FmtChans
), numChannels
);
259 if(!((ossFormat
== AFMT_S8
&& device
->FmtType
== DevFmtByte
) ||
260 (ossFormat
== AFMT_U8
&& device
->FmtType
== DevFmtUByte
) ||
261 (ossFormat
== AFMT_S16_NE
&& device
->FmtType
== DevFmtShort
)))
263 ERR("Failed to set %s samples, got OSS format %#x\n", DevFmtTypeString(device
->FmtType
), ossFormat
);
267 device
->Frequency
= ossSpeed
;
268 device
->UpdateSize
= info
.fragsize
/ frameSize
;
269 device
->NumUpdates
= info
.fragments
+ 1;
271 SetDefaultChannelOrder(device
);
276 static ALCboolean
oss_start_playback(ALCdevice
*device
)
278 oss_data
*data
= (oss_data
*)device
->ExtraData
;
280 data
->data_size
= device
->UpdateSize
* FrameSizeFromDevFmt(device
->FmtChans
, device
->FmtType
);
281 data
->mix_data
= calloc(1, data
->data_size
);
283 data
->thread
= StartThread(OSSProc
, device
);
284 if(data
->thread
== NULL
)
286 free(data
->mix_data
);
287 data
->mix_data
= NULL
;
294 static void oss_stop_playback(ALCdevice
*device
)
296 oss_data
*data
= (oss_data
*)device
->ExtraData
;
302 StopThread(data
->thread
);
306 if(ioctl(data
->fd
, SNDCTL_DSP_RESET
) != 0)
307 ERR("Error resetting device: %s\n", strerror(errno
));
309 free(data
->mix_data
);
310 data
->mix_data
= NULL
;
314 static ALCenum
oss_open_capture(ALCdevice
*device
, const ALCchar
*deviceName
)
316 int numFragmentsLogSize
;
317 int log2FragmentSize
;
318 unsigned int periods
;
328 deviceName
= oss_device
;
329 else if(strcmp(deviceName
, oss_device
) != 0)
330 return ALC_INVALID_VALUE
;
332 data
= (oss_data
*)calloc(1, sizeof(oss_data
));
335 data
->fd
= open(oss_capture
, O_RDONLY
);
339 ERR("Could not open %s: %s\n", oss_capture
, strerror(errno
));
340 return ALC_INVALID_VALUE
;
343 switch(device
->FmtType
)
352 ossFormat
= AFMT_S16_NE
;
359 ERR("%s capture samples not supported\n", DevFmtTypeString(device
->FmtType
));
360 return ALC_INVALID_VALUE
;
364 numChannels
= ChannelsFromDevFmt(device
->FmtChans
);
365 frameSize
= numChannels
* BytesFromDevFmt(device
->FmtType
);
366 ossSpeed
= device
->Frequency
;
367 log2FragmentSize
= log2i(device
->UpdateSize
* device
->NumUpdates
*
368 frameSize
/ periods
);
370 /* according to the OSS spec, 16 bytes are the minimum */
371 if (log2FragmentSize
< 4)
372 log2FragmentSize
= 4;
373 numFragmentsLogSize
= (periods
<< 16) | log2FragmentSize
;
375 #define CHECKERR(func) if((func) < 0) { \
379 CHECKERR(ioctl(data
->fd
, SNDCTL_DSP_SETFRAGMENT
, &numFragmentsLogSize
));
380 CHECKERR(ioctl(data
->fd
, SNDCTL_DSP_SETFMT
, &ossFormat
));
381 CHECKERR(ioctl(data
->fd
, SNDCTL_DSP_CHANNELS
, &numChannels
));
382 CHECKERR(ioctl(data
->fd
, SNDCTL_DSP_SPEED
, &ossSpeed
));
383 CHECKERR(ioctl(data
->fd
, SNDCTL_DSP_GETISPACE
, &info
));
387 ERR("%s failed: %s\n", err
, strerror(errno
));
390 return ALC_INVALID_VALUE
;
394 if((int)ChannelsFromDevFmt(device
->FmtChans
) != numChannels
)
396 ERR("Failed to set %s, got %d channels instead\n", DevFmtChannelsString(device
->FmtChans
), numChannels
);
399 return ALC_INVALID_VALUE
;
402 if(!((ossFormat
== AFMT_S8
&& device
->FmtType
== DevFmtByte
) ||
403 (ossFormat
== AFMT_U8
&& device
->FmtType
== DevFmtUByte
) ||
404 (ossFormat
== AFMT_S16_NE
&& device
->FmtType
== DevFmtShort
)))
406 ERR("Failed to set %s samples, got OSS format %#x\n", DevFmtTypeString(device
->FmtType
), ossFormat
);
409 return ALC_INVALID_VALUE
;
412 data
->ring
= CreateRingBuffer(frameSize
, device
->UpdateSize
* device
->NumUpdates
);
415 ERR("Ring buffer create failed\n");
418 return ALC_OUT_OF_MEMORY
;
421 data
->data_size
= info
.fragsize
;
422 data
->mix_data
= calloc(1, data
->data_size
);
424 device
->ExtraData
= data
;
425 data
->thread
= StartThread(OSSCaptureProc
, device
);
426 if(data
->thread
== NULL
)
428 device
->ExtraData
= NULL
;
429 free(data
->mix_data
);
431 return ALC_OUT_OF_MEMORY
;
434 device
->DeviceName
= strdup(deviceName
);
438 static void oss_close_capture(ALCdevice
*device
)
440 oss_data
*data
= (oss_data
*)device
->ExtraData
;
442 StopThread(data
->thread
);
446 DestroyRingBuffer(data
->ring
);
448 free(data
->mix_data
);
450 device
->ExtraData
= NULL
;
453 static void oss_start_capture(ALCdevice
*Device
)
455 oss_data
*data
= (oss_data
*)Device
->ExtraData
;
459 static void oss_stop_capture(ALCdevice
*Device
)
461 oss_data
*data
= (oss_data
*)Device
->ExtraData
;
465 static ALCenum
oss_capture_samples(ALCdevice
*Device
, ALCvoid
*pBuffer
, ALCuint lSamples
)
467 oss_data
*data
= (oss_data
*)Device
->ExtraData
;
468 ReadRingBuffer(data
->ring
, pBuffer
, lSamples
);
472 static ALCuint
oss_available_samples(ALCdevice
*Device
)
474 oss_data
*data
= (oss_data
*)Device
->ExtraData
;
475 return RingBufferSize(data
->ring
);
479 static const BackendFuncs oss_funcs
= {
490 oss_available_samples
,
491 ALCdevice_LockDefault
,
492 ALCdevice_UnlockDefault
,
493 ALCdevice_GetLatencyDefault
496 ALCboolean
alc_oss_init(BackendFuncs
*func_list
)
498 ConfigValueStr("oss", "device", &oss_driver
);
499 ConfigValueStr("oss", "capture", &oss_capture
);
501 *func_list
= oss_funcs
;
505 void alc_oss_deinit(void)
509 void alc_oss_probe(enum DevProbe type
)
513 case ALL_DEVICE_PROBE
:
517 if(stat(oss_driver
, &buf
) == 0)
519 AppendAllDevicesList(oss_device
);
523 case CAPTURE_DEVICE_PROBE
:
527 if(stat(oss_capture
, &buf
) == 0)
529 AppendCaptureDeviceList(oss_device
);