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 char *oss_device
;
51 static char *oss_device_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 int len
= data
->data_size
- remaining
;
92 aluMixData(pDevice
->Context
, data
->mix_data
+remaining
, len
, pDevice
->Format
);
97 wrote
= write(data
->fd
, data
->mix_data
, remaining
);
100 AL_PRINT("write failed: %s\n", strerror(errno
));
107 memmove(data
->mix_data
, data
->mix_data
+wrote
, remaining
);
116 static ALuint
OSSCaptureProc(ALvoid
*ptr
)
118 ALCdevice
*pDevice
= (ALCdevice
*)ptr
;
119 oss_data
*data
= (oss_data
*)pDevice
->ExtraData
;
123 frameSize
= aluBytesFromFormat(pDevice
->Format
);
124 frameSize
*= aluChannelsFromFormat(pDevice
->Format
);
126 while(!data
->killNow
)
128 amt
= read(data
->fd
, data
->mix_data
, data
->data_size
);
131 AL_PRINT("read failed: %s\n", strerror(errno
));
140 WriteRingBuffer(data
->ring
, data
->mix_data
, amt
/frameSize
);
146 static ALCboolean
oss_open_playback(ALCdevice
*device
, const ALCchar
*deviceName
)
148 int numFragmentsLogSize
;
149 int log2FragmentSize
;
150 unsigned int periods
;
161 strncpy(driver
, GetConfigValue("oss", "device", "/dev/dsp"), sizeof(driver
)-1);
162 driver
[sizeof(driver
)-1] = 0;
165 if(strcmp(deviceName
, oss_device
))
167 device
->szDeviceName
= oss_device
;
170 device
->szDeviceName
= oss_device
;
172 data
= (oss_data
*)calloc(1, sizeof(oss_data
));
175 data
->fd
= open(driver
, O_WRONLY
);
179 AL_PRINT("Could not open %s: %s\n", driver
, strerror(errno
));
183 switch(aluBytesFromFormat(device
->Format
))
189 ossFormat
= AFMT_S16_NE
;
193 AL_PRINT("Unknown format?! %x\n", device
->Format
);
196 periods
= GetConfigValueInt("oss", "periods", 4);
197 if((int)periods
<= 0)
199 numChannels
= aluChannelsFromFormat(device
->Format
);
200 frameSize
= numChannels
* aluBytesFromFormat(device
->Format
);
202 ossSpeed
= device
->Frequency
;
203 log2FragmentSize
= log2i(device
->UpdateSize
* frameSize
/ periods
);
205 /* according to the OSS spec, 16 bytes are the minimum */
206 if (log2FragmentSize
< 4)
207 log2FragmentSize
= 4;
208 numFragmentsLogSize
= (periods
<< 16) | log2FragmentSize
;
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")))
217 AL_PRINT("%s failed: %s\n", err
, strerror(errno
));
224 device
->Frequency
= ossSpeed
;
226 if((int)aluChannelsFromFormat(device
->Format
) != numChannels
)
228 AL_PRINT("Could not set %d channels, got %d instead\n", aluChannelsFromFormat(device
->Format
), numChannels
);
234 if(!((ossFormat
== AFMT_U8
&& aluBytesFromFormat(device
->Format
) == 1) ||
235 (ossFormat
== AFMT_S16_NE
&& aluBytesFromFormat(device
->Format
) == 2)))
237 AL_PRINT("Could not set %d-bit output, got format %#x\n", aluBytesFromFormat(device
->Format
)*8, ossFormat
);
243 device
->UpdateSize
= info
.fragsize
/ frameSize
;
245 data
->data_size
= device
->UpdateSize
* frameSize
;
246 data
->mix_data
= calloc(1, data
->data_size
);
248 device
->ExtraData
= data
;
249 data
->thread
= StartThread(OSSProc
, device
);
250 if(data
->thread
== NULL
)
252 device
->ExtraData
= NULL
;
253 free(data
->mix_data
);
261 static void oss_close_playback(ALCdevice
*device
)
263 oss_data
*data
= (oss_data
*)device
->ExtraData
;
265 StopThread(data
->thread
);
269 free(data
->mix_data
);
271 device
->ExtraData
= NULL
;
275 static ALCboolean
oss_open_capture(ALCdevice
*device
, const ALCchar
*deviceName
, ALCuint frequency
, ALCenum format
, ALCsizei SampleSize
)
277 int numFragmentsLogSize
;
278 int log2FragmentSize
;
279 unsigned int periods
;
290 strncpy(driver
, GetConfigValue("oss", "capture", "/dev/dsp"), sizeof(driver
)-1);
291 driver
[sizeof(driver
)-1] = 0;
294 if(strcmp(deviceName
, oss_device_capture
))
296 device
->szDeviceName
= oss_device_capture
;
299 device
->szDeviceName
= oss_device_capture
;
301 data
= (oss_data
*)calloc(1, sizeof(oss_data
));
304 data
->fd
= open(driver
, O_RDONLY
);
308 AL_PRINT("Could not open %s: %s\n", driver
, strerror(errno
));
312 switch(aluBytesFromFormat(format
))
318 ossFormat
= AFMT_S16_NE
;
322 AL_PRINT("Unknown format?! %x\n", device
->Format
);
326 numChannels
= aluChannelsFromFormat(device
->Format
);
327 frameSize
= numChannels
* aluBytesFromFormat(device
->Format
);
328 ossSpeed
= frequency
;
329 log2FragmentSize
= log2i(SampleSize
* frameSize
/ periods
);
331 /* according to the OSS spec, 16 bytes are the minimum */
332 if (log2FragmentSize
< 4)
333 log2FragmentSize
= 4;
334 numFragmentsLogSize
= (periods
<< 16) | log2FragmentSize
;
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")))
343 AL_PRINT("%s failed: %s\n", err
, strerror(errno
));
350 if((int)aluChannelsFromFormat(device
->Format
) != numChannels
)
352 AL_PRINT("Could not set %d channels, got %d instead\n", aluChannelsFromFormat(device
->Format
), numChannels
);
358 if(!((ossFormat
== AFMT_U8
&& aluBytesFromFormat(device
->Format
) == 1) ||
359 (ossFormat
== AFMT_S16_NE
&& aluBytesFromFormat(device
->Format
) == 2)))
361 AL_PRINT("Could not set %d-bit input, got format %#x\n", aluBytesFromFormat(device
->Format
)*8, ossFormat
);
367 data
->ring
= CreateRingBuffer(frameSize
, SampleSize
);
370 AL_PRINT("ring buffer create failed\n");
376 data
->data_size
= info
.fragsize
;
377 data
->mix_data
= calloc(1, data
->data_size
);
379 device
->ExtraData
= data
;
380 data
->thread
= StartThread(OSSCaptureProc
, device
);
381 if(data
->thread
== NULL
)
383 device
->ExtraData
= NULL
;
384 free(data
->mix_data
);
392 static void oss_close_capture(ALCdevice
*device
)
394 oss_data
*data
= (oss_data
*)device
->ExtraData
;
396 StopThread(data
->thread
);
400 DestroyRingBuffer(data
->ring
);
402 free(data
->mix_data
);
404 device
->ExtraData
= NULL
;
407 static void oss_start_capture(ALCdevice
*pDevice
)
409 oss_data
*data
= (oss_data
*)pDevice
->ExtraData
;
413 static void oss_stop_capture(ALCdevice
*pDevice
)
415 oss_data
*data
= (oss_data
*)pDevice
->ExtraData
;
419 static void oss_capture_samples(ALCdevice
*pDevice
, ALCvoid
*pBuffer
, ALCuint lSamples
)
421 oss_data
*data
= (oss_data
*)pDevice
->ExtraData
;
422 if(lSamples
<= (ALCuint
)RingBufferSize(data
->ring
))
423 ReadRingBuffer(data
->ring
, pBuffer
, lSamples
);
425 SetALCError(ALC_INVALID_VALUE
);
428 static ALCuint
oss_available_samples(ALCdevice
*pDevice
)
430 oss_data
*data
= (oss_data
*)pDevice
->ExtraData
;
431 return RingBufferSize(data
->ring
);
435 BackendFuncs oss_funcs
= {
443 oss_available_samples
446 void alc_oss_init(BackendFuncs
*func_list
)
448 *func_list
= oss_funcs
;
450 oss_device
= AppendDeviceList("OSS Software");
451 AppendAllDeviceList(oss_device
);
453 oss_device_capture
= AppendCaptureDeviceList("OSS Capture");