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
21 #include <sys/ioctl.h>
22 #include <sys/types.h>
35 #include <sys/soundcard.h>
38 * The OSS documentation talks about SOUND_MIXER_READ, but the header
39 * only contains MIXER_READ. Play safe. Same for WRITE.
41 #ifndef SOUND_MIXER_READ
42 #define SOUND_MIXER_READ MIXER_READ
44 #ifndef SOUND_MIXER_WRITE
45 #define SOUND_MIXER_WRITE MIXER_WRITE
48 static char *oss_device
;
49 static char *oss_device_capture
;
64 static int log2i(ALCuint x
)
76 static ALuint
OSSProc(ALvoid
*ptr
)
78 ALCdevice
*pDevice
= (ALCdevice
*)ptr
;
79 oss_data
*data
= (oss_data
*)pDevice
->ExtraData
;
85 int len
= data
->data_size
- remaining
;
90 aluMixData(pDevice
->Context
, data
->mix_data
+remaining
, len
, pDevice
->Format
);
95 wrote
= write(data
->fd
, data
->mix_data
, remaining
);
98 AL_PRINT("write failed: %s\n", strerror(errno
));
105 memmove(data
->mix_data
, data
->mix_data
+wrote
, remaining
);
114 static ALuint
OSSCaptureProc(ALvoid
*ptr
)
116 ALCdevice
*pDevice
= (ALCdevice
*)ptr
;
117 oss_data
*data
= (oss_data
*)pDevice
->ExtraData
;
121 frameSize
= aluBytesFromFormat(pDevice
->Format
);
122 frameSize
*= aluChannelsFromFormat(pDevice
->Format
);
124 while(!data
->killNow
)
126 amt
= read(data
->fd
, data
->mix_data
, data
->data_size
);
129 AL_PRINT("read failed: %s\n", strerror(errno
));
138 WriteRingBuffer(data
->ring
, data
->mix_data
, amt
/frameSize
);
144 static ALCboolean
oss_open_playback(ALCdevice
*device
, const ALCchar
*deviceName
)
146 int numFragmentsLogSize
;
147 int log2FragmentSize
;
148 unsigned int periods
;
158 strncpy(driver
, GetConfigValue("oss", "device", "/dev/dsp"), sizeof(driver
)-1);
159 driver
[sizeof(driver
)-1] = 0;
162 if(strcmp(deviceName
, oss_device
))
164 device
->szDeviceName
= oss_device
;
167 device
->szDeviceName
= oss_device
;
169 data
= (oss_data
*)calloc(1, sizeof(oss_data
));
172 data
->fd
= open(driver
, O_WRONLY
);
176 AL_PRINT("Could not open %s: %s\n", driver
, strerror(errno
));
180 switch(aluBytesFromFormat(device
->Format
))
186 ossFormat
= AFMT_S16_NE
;
190 AL_PRINT("Unknown format?! %x\n", device
->Format
);
193 periods
= GetConfigValueInt("oss", "periods", 4);
194 if((int)periods
<= 0)
196 numChannels
= device
->Channels
;
197 ossSpeed
= device
->Frequency
;
198 log2FragmentSize
= log2i(device
->UpdateFreq
* device
->FrameSize
/ periods
);
200 /* according to the OSS spec, 16 bytes are the minimum */
201 if (log2FragmentSize
< 4)
202 log2FragmentSize
= 4;
203 numFragmentsLogSize
= (periods
<< 16) | log2FragmentSize
;
205 #define ok(func, str) (i=(func),((i<0)?(err=(str)),0:1))
206 if (!(ok(ioctl(data
->fd
, SNDCTL_DSP_SETFRAGMENT
, &numFragmentsLogSize
), "set fragment") &&
207 ok(ioctl(data
->fd
, SNDCTL_DSP_SETFMT
, &ossFormat
), "set format") &&
208 ok(ioctl(data
->fd
, SNDCTL_DSP_CHANNELS
, &numChannels
), "set channels") &&
209 ok(ioctl(data
->fd
, SNDCTL_DSP_SPEED
, &ossSpeed
), "set speed") &&
210 ok(ioctl(data
->fd
, SNDCTL_DSP_GETOSPACE
, &info
), "get space")))
212 AL_PRINT("%s failed: %s\n", err
, strerror(errno
));
219 device
->Frequency
= ossSpeed
;
221 if((int)device
->Channels
!= numChannels
)
223 AL_PRINT("Could not set %d channels, got %d instead\n", device
->Channels
, numChannels
);
229 if(!((ossFormat
== AFMT_U8
&& aluBytesFromFormat(device
->Format
) == 1) ||
230 (ossFormat
== AFMT_S16_NE
&& aluBytesFromFormat(device
->Format
) == 2)))
232 AL_PRINT("Could not set %d-bit output, got format %#x\n", aluBytesFromFormat(device
->Format
)*8, ossFormat
);
238 device
->UpdateFreq
= info
.fragsize
/ device
->FrameSize
;
240 data
->data_size
= device
->UpdateFreq
* device
->FrameSize
;
241 data
->mix_data
= calloc(1, data
->data_size
);
243 device
->MaxNoOfSources
= 256;
245 device
->ExtraData
= data
;
246 data
->thread
= StartThread(OSSProc
, device
);
247 if(data
->thread
== NULL
)
249 device
->ExtraData
= NULL
;
250 free(data
->mix_data
);
258 static void oss_close_playback(ALCdevice
*device
)
260 oss_data
*data
= (oss_data
*)device
->ExtraData
;
262 StopThread(data
->thread
);
266 free(data
->mix_data
);
268 device
->ExtraData
= NULL
;
272 static ALCboolean
oss_open_capture(ALCdevice
*device
, const ALCchar
*deviceName
, ALCuint frequency
, ALCenum format
, ALCsizei SampleSize
)
274 int numFragmentsLogSize
;
275 int log2FragmentSize
;
276 unsigned int periods
;
286 strncpy(driver
, GetConfigValue("oss", "capture", "/dev/dsp"), sizeof(driver
)-1);
287 driver
[sizeof(driver
)-1] = 0;
290 if(strcmp(deviceName
, oss_device_capture
))
292 device
->szDeviceName
= oss_device_capture
;
295 device
->szDeviceName
= oss_device_capture
;
297 data
= (oss_data
*)calloc(1, sizeof(oss_data
));
300 data
->fd
= open(driver
, O_RDONLY
);
304 AL_PRINT("Could not open %s: %s\n", driver
, strerror(errno
));
308 switch(aluBytesFromFormat(format
))
314 ossFormat
= AFMT_S16_NE
;
318 AL_PRINT("Unknown format?! %x\n", device
->Format
);
322 numChannels
= device
->Channels
;
323 ossSpeed
= frequency
;
324 log2FragmentSize
= log2i(device
->UpdateFreq
* device
->FrameSize
/ periods
);
326 /* according to the OSS spec, 16 bytes are the minimum */
327 if (log2FragmentSize
< 4)
328 log2FragmentSize
= 4;
329 numFragmentsLogSize
= (periods
<< 16) | log2FragmentSize
;
331 #define ok(func, str) (i=(func),((i<0)?(err=(str)),0:1))
332 if (!(ok(ioctl(data
->fd
, SNDCTL_DSP_SETFRAGMENT
, &numFragmentsLogSize
), "set fragment") &&
333 ok(ioctl(data
->fd
, SNDCTL_DSP_SETFMT
, &ossFormat
), "set format") &&
334 ok(ioctl(data
->fd
, SNDCTL_DSP_CHANNELS
, &numChannels
), "set channels") &&
335 ok(ioctl(data
->fd
, SNDCTL_DSP_SPEED
, &ossSpeed
), "set speed") &&
336 ok(ioctl(data
->fd
, SNDCTL_DSP_GETISPACE
, &info
), "get space")))
338 AL_PRINT("%s failed: %s\n", err
, strerror(errno
));
345 device
->Channels
= numChannels
;
346 device
->Frequency
= ossSpeed
;
348 if(ossFormat
== AFMT_U8
)
350 if(device
->Channels
== 1)
352 device
->Format
= AL_FORMAT_MONO8
;
353 device
->FrameSize
= 1;
355 else if(device
->Channels
== 2)
357 device
->Format
= AL_FORMAT_STEREO8
;
358 device
->FrameSize
= 2;
360 else if(device
->Channels
== 4)
362 device
->Format
= AL_FORMAT_QUAD8
;
363 device
->FrameSize
= 4;
366 else if(ossFormat
== AFMT_S16_NE
)
368 if(device
->Channels
== 1)
370 device
->Format
= AL_FORMAT_MONO16
;
371 device
->FrameSize
= 2;
373 else if(device
->Channels
== 2)
375 device
->Format
= AL_FORMAT_STEREO16
;
376 device
->FrameSize
= 4;
378 else if(device
->Channels
== 4)
380 device
->Format
= AL_FORMAT_QUAD16
;
381 device
->FrameSize
= 8;
387 AL_PRINT("returned unknown format: %#x %d!\n", ossFormat
, numChannels
);
393 data
->ring
= CreateRingBuffer(device
->FrameSize
, SampleSize
);
396 AL_PRINT("ring buffer create failed\n");
402 device
->UpdateFreq
= info
.fragsize
/ device
->FrameSize
;
404 data
->data_size
= device
->UpdateFreq
* device
->FrameSize
* info
.fragments
;
405 data
->mix_data
= calloc(1, data
->data_size
);
407 device
->ExtraData
= data
;
408 data
->thread
= StartThread(OSSCaptureProc
, device
);
409 if(data
->thread
== NULL
)
411 device
->ExtraData
= NULL
;
412 free(data
->mix_data
);
420 static void oss_close_capture(ALCdevice
*device
)
422 oss_data
*data
= (oss_data
*)device
->ExtraData
;
424 StopThread(data
->thread
);
428 DestroyRingBuffer(data
->ring
);
430 free(data
->mix_data
);
432 device
->ExtraData
= NULL
;
435 static void oss_start_capture(ALCdevice
*pDevice
)
437 oss_data
*data
= (oss_data
*)pDevice
->ExtraData
;
441 static void oss_stop_capture(ALCdevice
*pDevice
)
443 oss_data
*data
= (oss_data
*)pDevice
->ExtraData
;
447 static void oss_capture_samples(ALCdevice
*pDevice
, ALCvoid
*pBuffer
, ALCuint lSamples
)
449 oss_data
*data
= (oss_data
*)pDevice
->ExtraData
;
450 if(lSamples
<= (ALCuint
)RingBufferSize(data
->ring
))
451 ReadRingBuffer(data
->ring
, pBuffer
, lSamples
);
453 SetALCError(ALC_INVALID_VALUE
);
456 static ALCuint
oss_available_samples(ALCdevice
*pDevice
)
458 oss_data
*data
= (oss_data
*)pDevice
->ExtraData
;
459 return RingBufferSize(data
->ring
);
463 BackendFuncs oss_funcs
= {
471 oss_available_samples
474 void alc_oss_init(BackendFuncs
*func_list
)
476 *func_list
= oss_funcs
;
478 oss_device
= AppendDeviceList("OSS Software");
479 AppendAllDeviceList(oss_device
);
481 oss_device_capture
= AppendCaptureDeviceList("OSS Capture");