doom3-gpl
Doom 3 GPL source release
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
sound.cpp
Go to the documentation of this file.
1 /*
2 ===========================================================================
3 
4 Doom 3 GPL Source Code
5 Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company.
6 
7 This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?).
8 
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.
13 
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.
18 
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/>.
21 
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.
23 
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.
25 
26 ===========================================================================
27 */
28 #include <stdio.h>
29 #include <unistd.h>
30 #include <fcntl.h>
31 #include <errno.h>
32 #include <malloc.h>
33 #include <sys/ioctl.h>
34 #include <sys/mman.h>
35 // OSS sound interface
36 // http://www.opensound.com/
37 #include <sys/soundcard.h>
38 
39 #include "../../idlib/precompiled.h"
40 #include "../../sound/snd_local.h"
41 #include "../posix/posix_public.h"
42 #include "sound.h"
43 
44 const char *s_driverArgs[] = { "best", "oss", "alsa", NULL };
45 
46 #ifndef NO_ALSA
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> );
48 #else
49 static idCVar s_driver( "s_driver", "oss", CVAR_SYSTEM | CVAR_ARCHIVE | CVAR_ROM, "sound driver. only OSS is supported in this build" );
50 #endif
51 
53 #ifndef NO_ALSA
54  if ( !strcmp( s_driver.GetString(), "best" ) ) {
56  if ( test->DLOpen() ) {
57  common->Printf( "Alsa is available\n" );
58  return test;
59  }
60  common->Printf( "Alsa is not available\n" );
61  delete test;
62  return new idAudioHardwareOSS;
63  }
64  if ( !strcmp( s_driver.GetString(), "alsa" ) ) {
65  return new idAudioHardwareALSA;
66  }
67 #endif
68  return new idAudioHardwareOSS;
69 }
70 
71 // OSS sound ----------------------------------------------------
72 
73 /*
74 ===============
75 idAudioHardware::~idAudioHardware
76 ===============
77 */
79 
80 /*
81 =================
82 idAudioHardwareOSS::~idAudioHardwareOSS
83 =================
84 */
86  Release();
87 }
88 
89 /*
90 =================
91 idAudioHardwareOSS::Release
92 =================
93 */
94 void idAudioHardwareOSS::Release( bool bSilent ) {
95  if (m_audio_fd) {
96  if (!bSilent) {
97  common->Printf("------ OSS Sound Shutdown ------\n");
98  }
99  if (m_buffer) {
100  free( m_buffer );
101  m_buffer = NULL;
102  m_buffer_size = 0;
103  }
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) );
107  }
108  m_audio_fd = 0;
109  if (!bSilent) {
110  common->Printf("--------------------------------\n");
111  }
112  }
113 }
114 
115 /*
116 =================
117 idAudioHardwareOSS::InitFailed
118 =================
119 */
121  Release( true );
122  cvarSystem->SetCVarBool( "s_noSound", true );
123  common->Warning( "sound subsystem disabled" );
124  common->Printf( "--------------------------------------\n" );
125 }
126 
127 /*
128 =================
129 idAudioHardwareOSS::ExtractOSSVersion
130 =================
131 */
133  sprintf( str, "%d.%d.%d", ( version & 0xFF0000 ) >> 16, ( version & 0xFF00 ) >> 8, version & 0xFF );
134 }
135 
136 /*
137 =================
138 idAudioHardwareOSS::Initialize
139 
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
142 
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.
145 =================
146 */
147 idCVar s_device( "s_dsp", "/dev/dsp", CVAR_SYSTEM | CVAR_ARCHIVE, "" );
148 
150  common->Printf("------ OSS Sound Initialization ------\n");
151 
152  int requested_sample_format, caps, oss_version;
153  idStr s_compiled_oss_version, s_oss_version;
154  struct audio_buf_info info;
155 
156  memset( &info, 0, sizeof( info ) );
157 
158  if (m_audio_fd) {
159  Release();
160  }
161 
162  // open device ------------------------------------------------
163  if ((m_audio_fd = open( s_device.GetString(), O_WRONLY | O_NONBLOCK, 0)) == -1) {
164  m_audio_fd = 0;
165  common->Warning( "failed to open sound device '%s': %s", s_device.GetString(), strerror(errno) );
166  InitFailed();
167  return false;
168  }
169  // make it blocking - so write overruns don't fail with 'Resource temporarily unavailable'
170  int flags;
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 ) );
173  InitFailed();
174  return false;
175  }
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 ) );
179  InitFailed();
180  return false;
181  }
182 
183  common->Printf("opened sound device '%s'\n", s_device.GetString());
184 
185  // verify capabilities -----------------------------------------
186 
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
190  oss_sysinfo si;
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 ) );
193  } else {
194  common->Printf( "%s: %s %s\n", s_device.GetString(), si.product, si.version );
195  }
196 
197  if ( ioctl( m_audio_fd, SNDCTL_DSP_GETCAPS, &caps ) == -1 ) {
198  common->Warning( "ioctl SNDCTL_DSP_GETCAPS failed - driver too old?" );
199  InitFailed();
200  return false;
201  }
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" );
205  InitFailed();
206  return false;
207  }
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" );
213  InitFailed();
214  return false;
215  }
216  if (!(caps & DSP_CAP_TRIGGER)) {
217  common->Warning( "driver doesn't have DSP_CAP_TRIGGER capability" );
218  InitFailed();
219  return false;
220  }
221 
222  // sample format -----------------------------------------------
223  requested_sample_format = AFMT_S16_LE;
224  m_sample_format = requested_sample_format;
226  common->Warning( "ioctl SNDCTL_DSP_SETFMT %d failed: %s", requested_sample_format, strerror(errno) );
227  InitFailed();
228  return false;
229  }
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 );
232  InitFailed();
233  return false;
234  }
235 
236  // channels ----------------------------------------------------
237 
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" );
242  }
243 
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) );
247  InitFailed();
248  return false;
249  }
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
254  m_channels = 2;
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) );
257  InitFailed();
258  return false;
259  }
260  }
261  if ( m_channels == 2 ) {
262  // tell the system to mix 2 channels
263  common->Warning( "falling back to stereo" );
265  } else {
266  // disable sound
267  InitFailed();
268  return false;
269  }
270  }
272 
273  // sampling rate ------------------------------------------------
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) );
277  InitFailed();
278  return false;
279  }
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 );
284  InitFailed();
285  return false;
286  }
287  common->Printf("%s - bit rate: %d, channels: %d, frequency: %d\n", s_device.GetString(), m_sample_format, m_channels, m_speed);
288 
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 );
299 
300  // toggle sound -------------------------------------------------
301 
302  // toggle off before toggling on. that's what OSS source code samples recommends
303  int flag = 0;
304  if (ioctl(m_audio_fd, SNDCTL_DSP_SETTRIGGER, &flag) == -1) {
305  common->Warning( "ioctl SNDCTL_DSP_SETTRIGGER 0 failed: %s", strerror(errno) );
306  }
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) );
310  }
311 
312  common->Printf("--------------------------------------\n");
313  return true;
314 }
315 
316 /*
317 ===============
318 idAudioHardwareOSS::Flush
319 ===============
320 */
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 ) );
325  return false;
326  }
327  // how many chunks can we write to the audio device right now
329  if ( m_writeChunks ) {
330  // flush out any remaining chunks we could now
331  Write( true );
332  }
333  return ( m_freeWriteChunks > 0 );
334 }
335 
336 /*
337 =================
338 idAudioHardwareOSS::GetMixBufferSize
339 =================
340 */
342  // return MIXBUFFER_SAMPLES * 2 * m_channels;
343  return m_buffer_size;
344 }
345 
346 /*
347 =================
348 idAudioHardwareOSS::GetMixBuffer
349 =================
350 */
352  return (short *)m_buffer;
353 }
354 
355 /*
356 ===============
357 idAudioHardwareOSS::Write
358 rely on m_freeWriteChunks which has been set in Flush() before engine did the mixing for this MIXBUFFER_SAMPLE
359 ===============
360 */
361 void idAudioHardwareOSS::Write( bool flushing ) {
362  assert( m_audio_fd );
363  int ret;
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 );
368  }
369  if ( !flushing ) {
370  // if running after the mix loop, then we have a full buffer to write out
372  }
373  if ( m_freeWriteChunks == 0 ) {
374  return;
375  }
376  // what to write and how much
379  assert( len > 0 );
380  if ( ( ret = write( m_audio_fd, (void*)pos, len ) ) == -1 ) {
381  Sys_Printf( "write to audio fd failed: %s\n", strerror( errno ) );
382  return;
383  }
384  if ( len != ret ) {
385  Sys_Printf( "short write to audio fd: wrote %d out of %d\n", ret, m_buffer_size );
386  return;
387  }
389 }
390 
391 /*
392  ===============
393  Sys_LoadOpenAL
394  -===============
395  */
396 bool Sys_LoadOpenAL( void ) {
397  return false;
398 }
399 
#define strcmp
Definition: Str.h:41
assert(prefInfo.fullscreenBtn)
idCVarSystem * cvarSystem
Definition: CVarSystem.cpp:487
#define SOUND_VERSION
Definition: soundcard.h:49
#define SNDCTL_DSP_SETTRIGGER
Definition: soundcard.h:715
int m_sample_format
Definition: sound.h:36
void InitFailed()
Definition: sound.cpp:120
static idCVar s_numberOfSpeakers
Definition: snd_local.h:802
#define DSP_CAP_MMAP
Definition: soundcard.h:669
const int MIXBUFFER_SAMPLES
Definition: Simd.h:84
void Sys_Printf(const char *msg,...)
case const int
Definition: Callbacks.cpp:52
void * m_buffer
Definition: sound.h:39
static const int MIXBUFFER_CHUNKS
Definition: sound.h:33
int m_buffer_size
Definition: sound.h:40
bool Initialize(void)
Definition: sound.cpp:149
short * GetMixBuffer()
Definition: sound.cpp:351
void ExtractOSSVersion(int version, idStr &str) const
Definition: sound.cpp:132
GLenum GLsizei len
Definition: glext.h:3472
virtual ~idAudioHardwareOSS()
Definition: sound.cpp:85
int test(char *url)
Definition: lib500.c:3
#define OSS_GETVERSION
Definition: soundcard.h:1193
#define ioctl(a, b, c, d)
Definition: amigaos.h:41
#define DSP_CAP_TRIGGER
Definition: soundcard.h:668
struct version_s version
#define SNDCTL_DSP_SPEED
Definition: soundcard.h:590
idCVar s_device("s_dsp","/dev/dsp", CVAR_SYSTEM|CVAR_ARCHIVE,"")
unsigned int m_channels
Definition: sound.h:37
#define PCM_ENABLE_OUTPUT
Definition: soundcard.h:717
char product[32]
Definition: soundcard.h:1221
idCommon * common
Definition: Common.cpp:206
#define OSS_SYSINFO
Definition: soundcard.h:1345
#define SNDCTL_DSP_CHANNELS
Definition: soundcard.h:594
#define NULL
Definition: Lib.h:88
void SetInteger(const int value)
Definition: CVarSystem.h:148
virtual void SetCVarBool(const char *name, const bool value, int flags=0)=0
int GetInteger(void) const
Definition: CVarSystem.h:143
int GetMixBufferSize()
Definition: sound.cpp:341
#define SNDCTL_DSP_GETOSPACE
Definition: soundcard.h:654
#define SNDCTL_DSP_GETCAPS
Definition: soundcard.h:657
virtual void Printf(const char *fmt,...) id_attribute((format(printf
const char * s_driverArgs[]
Definition: sound.cpp:44
char version[32]
Definition: soundcard.h:1222
#define AFMT_S16_LE
Definition: soundcard.h:607
int m_freeWriteChunks
Definition: sound.h:48
const char * GetString(void) const
Definition: CVarSystem.h:141
void Write(bool flushing)
Definition: sound.cpp:361
void Release(bool bSilent=false)
Definition: sound.cpp:94
bool Sys_LoadOpenAL(void)
Definition: sound.cpp:396
Definition: Str.h:116
unsigned int m_speed
Definition: sound.h:38
const char * c_str(void) const
Definition: Str.h:487
#define SNDCTL_DSP_SETFMT
Definition: soundcard.h:601
static idAudioHardware * Alloc()
Definition: sound.cpp:52
virtual void DPrintf(const char *fmt,...) id_attribute((format(printf
#define DSP_CAP_REVISION
Definition: soundcard.h:658
const int PRIMARYFREQ
Definition: snd_local.h:67
virtual void virtual void Warning(const char *fmt,...) id_attribute((format(printf
ID_INLINE T Min(T x, T y)
Definition: Lib.h:159
const char * str
Definition: brandelf.c:64
int sprintf(idStr &string, const char *fmt,...)
Definition: Str.cpp:1528
virtual ~idAudioHardware()
Definition: sound.cpp:78
int m_writeChunks
Definition: sound.h:46