2 ===========================================================================
5 Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company.
7 This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?).
9 Doom 3 Source Code is free software: you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation, either version 3 of the License, or
12 (at your option) any later version.
14 Doom 3 Source Code is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License for more details.
19 You should have received a copy of the GNU General Public License
20 along with Doom 3 Source Code. If not, see <http://www.gnu.org/licenses/>.
22 In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below.
24 If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
26 ===========================================================================
33 #include <sys/ioctl.h>
35 // OSS sound interface
36 // http://www.opensound.com/
37 #include <sys/soundcard.h>
39 #include "../../idlib/precompiled.h"
40 #include "../../sound/snd_local.h"
41 #include "../posix/posix_public.h"
44 const char *s_driverArgs[] = { "best", "oss", "alsa", NULL };
47 static idCVar s_driver( "s_driver", s_driverArgs[0], CVAR_SYSTEM | CVAR_ARCHIVE, "sound driver. 'best' will attempt to use alsa and fallback to OSS if not available", s_driverArgs, idCmdSystem::ArgCompletion_String<s_driverArgs> );
49 static idCVar s_driver( "s_driver", "oss", CVAR_SYSTEM | CVAR_ARCHIVE | CVAR_ROM, "sound driver. only OSS is supported in this build" );
52 idAudioHardware *idAudioHardware::Alloc() {
54 if ( !strcmp( s_driver.GetString(), "best" ) ) {
55 idAudioHardwareALSA *test = new idAudioHardwareALSA;
56 if ( test->DLOpen() ) {
57 common->Printf( "Alsa is available\n" );
60 common->Printf( "Alsa is not available\n" );
62 return new idAudioHardwareOSS;
64 if ( !strcmp( s_driver.GetString(), "alsa" ) ) {
65 return new idAudioHardwareALSA;
68 return new idAudioHardwareOSS;
71 // OSS sound ----------------------------------------------------
75 idAudioHardware::~idAudioHardware
78 idAudioHardware::~idAudioHardware() { }
82 idAudioHardwareOSS::~idAudioHardwareOSS
85 idAudioHardwareOSS::~idAudioHardwareOSS() {
91 idAudioHardwareOSS::Release
94 void idAudioHardwareOSS::Release( bool bSilent ) {
97 common->Printf("------ OSS Sound Shutdown ------\n");
104 common->Printf("close sound device\n");
105 if (close(m_audio_fd) == -1) {
106 common->Warning( "failed to close sound device: %s", strerror(errno) );
110 common->Printf("--------------------------------\n");
117 idAudioHardwareOSS::InitFailed
120 void idAudioHardwareOSS::InitFailed() {
122 cvarSystem->SetCVarBool( "s_noSound", true );
123 common->Warning( "sound subsystem disabled" );
124 common->Printf( "--------------------------------------\n" );
129 idAudioHardwareOSS::ExtractOSSVersion
132 void idAudioHardwareOSS::ExtractOSSVersion( int version, idStr &str ) const {
133 sprintf( str, "%d.%d.%d", ( version & 0xFF0000 ) >> 16, ( version & 0xFF00 ) >> 8, version & 0xFF );
138 idAudioHardwareOSS::Initialize
140 http://www.4front-tech.com/pguide/index.html
141 though OSS API docs (1.1) advertise AFMT_S32_LE, AFMT_S16_LE is the only output format I've found in kernel emu10k1 headers
143 BSD NOTE: With the GNU library, you can use free to free the blocks that memalign, posix_memalign, and valloc return.
144 That does not work in BSD, however--BSD does not provide any way to free such blocks.
147 idCVar s_device( "s_dsp", "/dev/dsp", CVAR_SYSTEM | CVAR_ARCHIVE, "" );
149 bool idAudioHardwareOSS::Initialize( ) {
150 common->Printf("------ OSS Sound Initialization ------\n");
152 int requested_sample_format, caps, oss_version;
153 idStr s_compiled_oss_version, s_oss_version;
154 struct audio_buf_info info;
156 memset( &info, 0, sizeof( info ) );
162 // open device ------------------------------------------------
163 if ((m_audio_fd = open( s_device.GetString(), O_WRONLY | O_NONBLOCK, 0)) == -1) {
165 common->Warning( "failed to open sound device '%s': %s", s_device.GetString(), strerror(errno) );
169 // make it blocking - so write overruns don't fail with 'Resource temporarily unavailable'
171 if ( ( flags = fcntl( m_audio_fd, F_GETFL ) ) == -1 ) {
172 common->Warning( "failed fcntl F_GETFL on sound device '%s': %s", s_device.GetString(), strerror( errno ) );
176 flags &= ~O_NONBLOCK;
177 if ( fcntl( m_audio_fd, F_SETFL, flags ) == -1 ) {
178 common->Warning( "failed to clear O_NONBLOCK on sound device '%s': %s", s_device.GetString(), strerror( errno ) );
183 common->Printf("opened sound device '%s'\n", s_device.GetString());
185 // verify capabilities -----------------------------------------
187 // may only be available starting with OSS API v4.0
188 // http://www.fi.opensound.com/developer/SNDCTL_SYSINFO.html
189 // NOTE: at OSS API 4.0 headers, replace OSS_SYSINFO with SNDCTL_SYSINFO
191 if ( ioctl( m_audio_fd, OSS_SYSINFO, &si ) == -1 ) {
192 common->Printf( "ioctl SNDCTL_SYSINFO failed: %s\nthis ioctl is only available in OSS/Linux implementation. If you run OSS/Free, don't bother.", strerror( errno ) );
194 common->Printf( "%s: %s %s\n", s_device.GetString(), si.product, si.version );
197 if ( ioctl( m_audio_fd, SNDCTL_DSP_GETCAPS, &caps ) == -1 ) {
198 common->Warning( "ioctl SNDCTL_DSP_GETCAPS failed - driver too old?" );
202 common->DPrintf("driver rev %d - capabilities %d\n", caps & DSP_CAP_REVISION, caps);
203 if (ioctl( m_audio_fd, OSS_GETVERSION, &oss_version ) == -1) {
204 common->Warning( "ioctl OSS_GETVERSION failed" );
208 ExtractOSSVersion( oss_version, s_oss_version );
209 ExtractOSSVersion( SOUND_VERSION, s_compiled_oss_version );
210 common->DPrintf( "OSS interface version %s - compile time %s\n", s_oss_version.c_str(), s_compiled_oss_version.c_str() );
211 if (!(caps & DSP_CAP_MMAP)) {
212 common->Warning( "driver doesn't have DSP_CAP_MMAP capability" );
216 if (!(caps & DSP_CAP_TRIGGER)) {
217 common->Warning( "driver doesn't have DSP_CAP_TRIGGER capability" );
222 // sample format -----------------------------------------------
223 requested_sample_format = AFMT_S16_LE;
224 m_sample_format = requested_sample_format;
225 if (ioctl(m_audio_fd, SNDCTL_DSP_SETFMT, &m_sample_format) == -1) {
226 common->Warning( "ioctl SNDCTL_DSP_SETFMT %d failed: %s", requested_sample_format, strerror(errno) );
230 if ( m_sample_format != requested_sample_format ) {
231 common->Warning( "ioctl SNDCTL_DSP_SETFMT failed to get the requested sample format %d, got %d", requested_sample_format, m_sample_format );
236 // channels ----------------------------------------------------
238 // sanity over number of speakers
239 if ( idSoundSystemLocal::s_numberOfSpeakers.GetInteger() != 6 && idSoundSystemLocal::s_numberOfSpeakers.GetInteger() != 2 ) {
240 common->Warning( "invalid value for s_numberOfSpeakers. Use either 2 or 6" );
241 idSoundSystemLocal::s_numberOfSpeakers.SetInteger( 2 );
244 m_channels = idSoundSystemLocal::s_numberOfSpeakers.GetInteger();
245 if ( ioctl( m_audio_fd, SNDCTL_DSP_CHANNELS, &m_channels ) == -1 ) {
246 common->Warning( "ioctl SNDCTL_DSP_CHANNELS %d failed: %s", idSoundSystemLocal::s_numberOfSpeakers.GetInteger(), strerror(errno) );
250 if ( m_channels != (unsigned int)idSoundSystemLocal::s_numberOfSpeakers.GetInteger() ) {
251 common->Warning( "ioctl SNDCTL_DSP_CHANNELS failed to get the %d requested channels, got %d", idSoundSystemLocal::s_numberOfSpeakers.GetInteger(), m_channels );
252 if ( m_channels != 2 && idSoundSystemLocal::s_numberOfSpeakers.GetInteger() != 2 ) {
253 // we didn't request 2 channels, some drivers reply 1 channel on error but may still let us still get 2 if properly asked
255 if ( ioctl( m_audio_fd, SNDCTL_DSP_CHANNELS, &m_channels ) == -1 ) {
256 common->Warning( "ioctl SNDCTL_DSP_CHANNELS fallback to 2 failed: %s", strerror(errno) );
261 if ( m_channels == 2 ) {
262 // tell the system to mix 2 channels
263 common->Warning( "falling back to stereo" );
264 idSoundSystemLocal::s_numberOfSpeakers.SetInteger( 2 );
271 assert( (int)m_channels == idSoundSystemLocal::s_numberOfSpeakers.GetInteger() );
273 // sampling rate ------------------------------------------------
274 m_speed = PRIMARYFREQ;
275 if ( ioctl( m_audio_fd, SNDCTL_DSP_SPEED, &m_speed ) == -1 ) {
276 common->Warning( "ioctl SNDCTL_DSP_SPEED %d failed: %s", PRIMARYFREQ, strerror(errno) );
280 // instead of an exact match, do a very close to
281 // there is some horrible Ensonic ES1371 which replies 44101 for a 44100 request
282 if ( abs( m_speed - PRIMARYFREQ ) > 5 ) {
283 common->Warning( "ioctl SNDCTL_DSP_SPEED failed to get the requested frequency %d, got %d", PRIMARYFREQ, m_speed );
287 common->Printf("%s - bit rate: %d, channels: %d, frequency: %d\n", s_device.GetString(), m_sample_format, m_channels, m_speed);
289 // output buffer ------------------------------------------------
290 // allocate a final buffer target, the sound engine locks, writes, and we write back to the device
291 // we want m_buffer_size ( will have to rename those )
292 // ROOM_SLICES_IN_BUFFER is fixed ( system default, 10 )
293 // MIXBUFFER_SAMPLES is the number of samples found in a slice
294 // each sample is m_channels * sizeof( float ) bytes
295 // in AsyncUpdate we only write one block at a time, so we'd only need to have a final mix buffer sized of a single block
296 m_buffer_size = MIXBUFFER_SAMPLES * m_channels * 2;
297 m_buffer = malloc( m_buffer_size );
298 common->Printf( "allocated a mix buffer of %d bytes\n", m_buffer_size );
300 // toggle sound -------------------------------------------------
302 // toggle off before toggling on. that's what OSS source code samples recommends
304 if (ioctl(m_audio_fd, SNDCTL_DSP_SETTRIGGER, &flag) == -1) {
305 common->Warning( "ioctl SNDCTL_DSP_SETTRIGGER 0 failed: %s", strerror(errno) );
307 flag = PCM_ENABLE_OUTPUT;
308 if (ioctl(m_audio_fd, SNDCTL_DSP_SETTRIGGER, &flag) == -1) {
309 common->Warning( "ioctl SNDCTL_DSP_SETTRIGGER PCM_ENABLE_OUTPUT failed: %s", strerror(errno) );
312 common->Printf("--------------------------------------\n");
318 idAudioHardwareOSS::Flush
321 bool idAudioHardwareOSS::Flush( void ) {
322 audio_buf_info ospace;
323 if ( ioctl( m_audio_fd, SNDCTL_DSP_GETOSPACE, &ospace ) == -1 ) {
324 Sys_Printf( "ioctl SNDCTL_DSP_GETOSPACE failed: %s\n", strerror( errno ) );
327 // how many chunks can we write to the audio device right now
328 m_freeWriteChunks = ( ospace.bytes * MIXBUFFER_CHUNKS ) / ( MIXBUFFER_SAMPLES * m_channels * 2 );
329 if ( m_writeChunks ) {
330 // flush out any remaining chunks we could now
333 return ( m_freeWriteChunks > 0 );
338 idAudioHardwareOSS::GetMixBufferSize
341 int idAudioHardwareOSS::GetMixBufferSize() {
342 // return MIXBUFFER_SAMPLES * 2 * m_channels;
343 return m_buffer_size;
348 idAudioHardwareOSS::GetMixBuffer
351 short* idAudioHardwareOSS::GetMixBuffer() {
352 return (short *)m_buffer;
357 idAudioHardwareOSS::Write
358 rely on m_freeWriteChunks which has been set in Flush() before engine did the mixing for this MIXBUFFER_SAMPLE
361 void idAudioHardwareOSS::Write( bool flushing ) {
362 assert( m_audio_fd );
364 if ( !flushing && m_writeChunks ) {
365 // if we write after a new mixing loop, we should have m_writeChunk == 0
366 // otherwise that last remaining chunk that was never flushed out to the audio device has just been overwritten
367 Sys_Printf( "idAudioHardwareOSS::Write: %d samples were overflowed and dropped\n", m_writeChunks * MIXBUFFER_SAMPLES / MIXBUFFER_CHUNKS );
370 // if running after the mix loop, then we have a full buffer to write out
371 m_writeChunks = MIXBUFFER_CHUNKS;
373 if ( m_freeWriteChunks == 0 ) {
376 // what to write and how much
377 int pos = (int)m_buffer + ( MIXBUFFER_CHUNKS - m_writeChunks ) * m_channels * 2 * MIXBUFFER_SAMPLES / MIXBUFFER_CHUNKS;
378 int len = Min( m_writeChunks, m_freeWriteChunks ) * m_channels * 2 * MIXBUFFER_SAMPLES / MIXBUFFER_CHUNKS;
380 if ( ( ret = write( m_audio_fd, (void*)pos, len ) ) == -1 ) {
381 Sys_Printf( "write to audio fd failed: %s\n", strerror( errno ) );
385 Sys_Printf( "short write to audio fd: wrote %d out of %d\n", ret, m_buffer_size );
388 m_writeChunks -= Min( m_writeChunks, m_freeWriteChunks );
396 bool Sys_LoadOpenAL( void ) {