sound.cpp 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400
  1. /*
  2. ===========================================================================
  3. Doom 3 GPL Source Code
  4. Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company.
  5. This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?).
  6. Doom 3 Source Code is free software: you can redistribute it and/or modify
  7. it under the terms of the GNU General Public License as published by
  8. the Free Software Foundation, either version 3 of the License, or
  9. (at your option) any later version.
  10. Doom 3 Source Code is distributed in the hope that it will be useful,
  11. but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. GNU General Public License for more details.
  14. You should have received a copy of the GNU General Public License
  15. along with Doom 3 Source Code. If not, see <http://www.gnu.org/licenses/>.
  16. 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.
  17. 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.
  18. ===========================================================================
  19. */
  20. #include <stdio.h>
  21. #include <unistd.h>
  22. #include <fcntl.h>
  23. #include <errno.h>
  24. #include <malloc.h>
  25. #include <sys/ioctl.h>
  26. #include <sys/mman.h>
  27. // OSS sound interface
  28. // http://www.opensound.com/
  29. #include <sys/soundcard.h>
  30. #include "../../idlib/precompiled.h"
  31. #include "../../sound/snd_local.h"
  32. #include "../posix/posix_public.h"
  33. #include "sound.h"
  34. const char *s_driverArgs[] = { "best", "oss", "alsa", NULL };
  35. #ifndef NO_ALSA
  36. 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> );
  37. #else
  38. static idCVar s_driver( "s_driver", "oss", CVAR_SYSTEM | CVAR_ARCHIVE | CVAR_ROM, "sound driver. only OSS is supported in this build" );
  39. #endif
  40. idAudioHardware *idAudioHardware::Alloc() {
  41. #ifndef NO_ALSA
  42. if ( !strcmp( s_driver.GetString(), "best" ) ) {
  43. idAudioHardwareALSA *test = new idAudioHardwareALSA;
  44. if ( test->DLOpen() ) {
  45. common->Printf( "Alsa is available\n" );
  46. return test;
  47. }
  48. common->Printf( "Alsa is not available\n" );
  49. delete test;
  50. return new idAudioHardwareOSS;
  51. }
  52. if ( !strcmp( s_driver.GetString(), "alsa" ) ) {
  53. return new idAudioHardwareALSA;
  54. }
  55. #endif
  56. return new idAudioHardwareOSS;
  57. }
  58. // OSS sound ----------------------------------------------------
  59. /*
  60. ===============
  61. idAudioHardware::~idAudioHardware
  62. ===============
  63. */
  64. idAudioHardware::~idAudioHardware() { }
  65. /*
  66. =================
  67. idAudioHardwareOSS::~idAudioHardwareOSS
  68. =================
  69. */
  70. idAudioHardwareOSS::~idAudioHardwareOSS() {
  71. Release();
  72. }
  73. /*
  74. =================
  75. idAudioHardwareOSS::Release
  76. =================
  77. */
  78. void idAudioHardwareOSS::Release( bool bSilent ) {
  79. if (m_audio_fd) {
  80. if (!bSilent) {
  81. common->Printf("------ OSS Sound Shutdown ------\n");
  82. }
  83. if (m_buffer) {
  84. free( m_buffer );
  85. m_buffer = NULL;
  86. m_buffer_size = 0;
  87. }
  88. common->Printf("close sound device\n");
  89. if (close(m_audio_fd) == -1) {
  90. common->Warning( "failed to close sound device: %s", strerror(errno) );
  91. }
  92. m_audio_fd = 0;
  93. if (!bSilent) {
  94. common->Printf("--------------------------------\n");
  95. }
  96. }
  97. }
  98. /*
  99. =================
  100. idAudioHardwareOSS::InitFailed
  101. =================
  102. */
  103. void idAudioHardwareOSS::InitFailed() {
  104. Release( true );
  105. cvarSystem->SetCVarBool( "s_noSound", true );
  106. common->Warning( "sound subsystem disabled" );
  107. common->Printf( "--------------------------------------\n" );
  108. }
  109. /*
  110. =================
  111. idAudioHardwareOSS::ExtractOSSVersion
  112. =================
  113. */
  114. void idAudioHardwareOSS::ExtractOSSVersion( int version, idStr &str ) const {
  115. sprintf( str, "%d.%d.%d", ( version & 0xFF0000 ) >> 16, ( version & 0xFF00 ) >> 8, version & 0xFF );
  116. }
  117. /*
  118. =================
  119. idAudioHardwareOSS::Initialize
  120. http://www.4front-tech.com/pguide/index.html
  121. 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
  122. BSD NOTE: With the GNU library, you can use free to free the blocks that memalign, posix_memalign, and valloc return.
  123. That does not work in BSD, however--BSD does not provide any way to free such blocks.
  124. =================
  125. */
  126. idCVar s_device( "s_dsp", "/dev/dsp", CVAR_SYSTEM | CVAR_ARCHIVE, "" );
  127. bool idAudioHardwareOSS::Initialize( ) {
  128. common->Printf("------ OSS Sound Initialization ------\n");
  129. int requested_sample_format, caps, oss_version;
  130. idStr s_compiled_oss_version, s_oss_version;
  131. struct audio_buf_info info;
  132. memset( &info, 0, sizeof( info ) );
  133. if (m_audio_fd) {
  134. Release();
  135. }
  136. // open device ------------------------------------------------
  137. if ((m_audio_fd = open( s_device.GetString(), O_WRONLY | O_NONBLOCK, 0)) == -1) {
  138. m_audio_fd = 0;
  139. common->Warning( "failed to open sound device '%s': %s", s_device.GetString(), strerror(errno) );
  140. InitFailed();
  141. return false;
  142. }
  143. // make it blocking - so write overruns don't fail with 'Resource temporarily unavailable'
  144. int flags;
  145. if ( ( flags = fcntl( m_audio_fd, F_GETFL ) ) == -1 ) {
  146. common->Warning( "failed fcntl F_GETFL on sound device '%s': %s", s_device.GetString(), strerror( errno ) );
  147. InitFailed();
  148. return false;
  149. }
  150. flags &= ~O_NONBLOCK;
  151. if ( fcntl( m_audio_fd, F_SETFL, flags ) == -1 ) {
  152. common->Warning( "failed to clear O_NONBLOCK on sound device '%s': %s", s_device.GetString(), strerror( errno ) );
  153. InitFailed();
  154. return false;
  155. }
  156. common->Printf("opened sound device '%s'\n", s_device.GetString());
  157. // verify capabilities -----------------------------------------
  158. // may only be available starting with OSS API v4.0
  159. // http://www.fi.opensound.com/developer/SNDCTL_SYSINFO.html
  160. // NOTE: at OSS API 4.0 headers, replace OSS_SYSINFO with SNDCTL_SYSINFO
  161. oss_sysinfo si;
  162. if ( ioctl( m_audio_fd, OSS_SYSINFO, &si ) == -1 ) {
  163. 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 ) );
  164. } else {
  165. common->Printf( "%s: %s %s\n", s_device.GetString(), si.product, si.version );
  166. }
  167. if ( ioctl( m_audio_fd, SNDCTL_DSP_GETCAPS, &caps ) == -1 ) {
  168. common->Warning( "ioctl SNDCTL_DSP_GETCAPS failed - driver too old?" );
  169. InitFailed();
  170. return false;
  171. }
  172. common->DPrintf("driver rev %d - capabilities %d\n", caps & DSP_CAP_REVISION, caps);
  173. if (ioctl( m_audio_fd, OSS_GETVERSION, &oss_version ) == -1) {
  174. common->Warning( "ioctl OSS_GETVERSION failed" );
  175. InitFailed();
  176. return false;
  177. }
  178. ExtractOSSVersion( oss_version, s_oss_version );
  179. ExtractOSSVersion( SOUND_VERSION, s_compiled_oss_version );
  180. common->DPrintf( "OSS interface version %s - compile time %s\n", s_oss_version.c_str(), s_compiled_oss_version.c_str() );
  181. if (!(caps & DSP_CAP_MMAP)) {
  182. common->Warning( "driver doesn't have DSP_CAP_MMAP capability" );
  183. InitFailed();
  184. return false;
  185. }
  186. if (!(caps & DSP_CAP_TRIGGER)) {
  187. common->Warning( "driver doesn't have DSP_CAP_TRIGGER capability" );
  188. InitFailed();
  189. return false;
  190. }
  191. // sample format -----------------------------------------------
  192. requested_sample_format = AFMT_S16_LE;
  193. m_sample_format = requested_sample_format;
  194. if (ioctl(m_audio_fd, SNDCTL_DSP_SETFMT, &m_sample_format) == -1) {
  195. common->Warning( "ioctl SNDCTL_DSP_SETFMT %d failed: %s", requested_sample_format, strerror(errno) );
  196. InitFailed();
  197. return false;
  198. }
  199. if ( m_sample_format != requested_sample_format ) {
  200. common->Warning( "ioctl SNDCTL_DSP_SETFMT failed to get the requested sample format %d, got %d", requested_sample_format, m_sample_format );
  201. InitFailed();
  202. return false;
  203. }
  204. // channels ----------------------------------------------------
  205. // sanity over number of speakers
  206. if ( idSoundSystemLocal::s_numberOfSpeakers.GetInteger() != 6 && idSoundSystemLocal::s_numberOfSpeakers.GetInteger() != 2 ) {
  207. common->Warning( "invalid value for s_numberOfSpeakers. Use either 2 or 6" );
  208. idSoundSystemLocal::s_numberOfSpeakers.SetInteger( 2 );
  209. }
  210. m_channels = idSoundSystemLocal::s_numberOfSpeakers.GetInteger();
  211. if ( ioctl( m_audio_fd, SNDCTL_DSP_CHANNELS, &m_channels ) == -1 ) {
  212. common->Warning( "ioctl SNDCTL_DSP_CHANNELS %d failed: %s", idSoundSystemLocal::s_numberOfSpeakers.GetInteger(), strerror(errno) );
  213. InitFailed();
  214. return false;
  215. }
  216. if ( m_channels != (unsigned int)idSoundSystemLocal::s_numberOfSpeakers.GetInteger() ) {
  217. common->Warning( "ioctl SNDCTL_DSP_CHANNELS failed to get the %d requested channels, got %d", idSoundSystemLocal::s_numberOfSpeakers.GetInteger(), m_channels );
  218. if ( m_channels != 2 && idSoundSystemLocal::s_numberOfSpeakers.GetInteger() != 2 ) {
  219. // we didn't request 2 channels, some drivers reply 1 channel on error but may still let us still get 2 if properly asked
  220. m_channels = 2;
  221. if ( ioctl( m_audio_fd, SNDCTL_DSP_CHANNELS, &m_channels ) == -1 ) {
  222. common->Warning( "ioctl SNDCTL_DSP_CHANNELS fallback to 2 failed: %s", strerror(errno) );
  223. InitFailed();
  224. return false;
  225. }
  226. }
  227. if ( m_channels == 2 ) {
  228. // tell the system to mix 2 channels
  229. common->Warning( "falling back to stereo" );
  230. idSoundSystemLocal::s_numberOfSpeakers.SetInteger( 2 );
  231. } else {
  232. // disable sound
  233. InitFailed();
  234. return false;
  235. }
  236. }
  237. assert( (int)m_channels == idSoundSystemLocal::s_numberOfSpeakers.GetInteger() );
  238. // sampling rate ------------------------------------------------
  239. m_speed = PRIMARYFREQ;
  240. if ( ioctl( m_audio_fd, SNDCTL_DSP_SPEED, &m_speed ) == -1 ) {
  241. common->Warning( "ioctl SNDCTL_DSP_SPEED %d failed: %s", PRIMARYFREQ, strerror(errno) );
  242. InitFailed();
  243. return false;
  244. }
  245. // instead of an exact match, do a very close to
  246. // there is some horrible Ensonic ES1371 which replies 44101 for a 44100 request
  247. if ( abs( m_speed - PRIMARYFREQ ) > 5 ) {
  248. common->Warning( "ioctl SNDCTL_DSP_SPEED failed to get the requested frequency %d, got %d", PRIMARYFREQ, m_speed );
  249. InitFailed();
  250. return false;
  251. }
  252. common->Printf("%s - bit rate: %d, channels: %d, frequency: %d\n", s_device.GetString(), m_sample_format, m_channels, m_speed);
  253. // output buffer ------------------------------------------------
  254. // allocate a final buffer target, the sound engine locks, writes, and we write back to the device
  255. // we want m_buffer_size ( will have to rename those )
  256. // ROOM_SLICES_IN_BUFFER is fixed ( system default, 10 )
  257. // MIXBUFFER_SAMPLES is the number of samples found in a slice
  258. // each sample is m_channels * sizeof( float ) bytes
  259. // 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
  260. m_buffer_size = MIXBUFFER_SAMPLES * m_channels * 2;
  261. m_buffer = malloc( m_buffer_size );
  262. common->Printf( "allocated a mix buffer of %d bytes\n", m_buffer_size );
  263. // toggle sound -------------------------------------------------
  264. // toggle off before toggling on. that's what OSS source code samples recommends
  265. int flag = 0;
  266. if (ioctl(m_audio_fd, SNDCTL_DSP_SETTRIGGER, &flag) == -1) {
  267. common->Warning( "ioctl SNDCTL_DSP_SETTRIGGER 0 failed: %s", strerror(errno) );
  268. }
  269. flag = PCM_ENABLE_OUTPUT;
  270. if (ioctl(m_audio_fd, SNDCTL_DSP_SETTRIGGER, &flag) == -1) {
  271. common->Warning( "ioctl SNDCTL_DSP_SETTRIGGER PCM_ENABLE_OUTPUT failed: %s", strerror(errno) );
  272. }
  273. common->Printf("--------------------------------------\n");
  274. return true;
  275. }
  276. /*
  277. ===============
  278. idAudioHardwareOSS::Flush
  279. ===============
  280. */
  281. bool idAudioHardwareOSS::Flush( void ) {
  282. audio_buf_info ospace;
  283. if ( ioctl( m_audio_fd, SNDCTL_DSP_GETOSPACE, &ospace ) == -1 ) {
  284. Sys_Printf( "ioctl SNDCTL_DSP_GETOSPACE failed: %s\n", strerror( errno ) );
  285. return false;
  286. }
  287. // how many chunks can we write to the audio device right now
  288. m_freeWriteChunks = ( ospace.bytes * MIXBUFFER_CHUNKS ) / ( MIXBUFFER_SAMPLES * m_channels * 2 );
  289. if ( m_writeChunks ) {
  290. // flush out any remaining chunks we could now
  291. Write( true );
  292. }
  293. return ( m_freeWriteChunks > 0 );
  294. }
  295. /*
  296. =================
  297. idAudioHardwareOSS::GetMixBufferSize
  298. =================
  299. */
  300. int idAudioHardwareOSS::GetMixBufferSize() {
  301. // return MIXBUFFER_SAMPLES * 2 * m_channels;
  302. return m_buffer_size;
  303. }
  304. /*
  305. =================
  306. idAudioHardwareOSS::GetMixBuffer
  307. =================
  308. */
  309. short* idAudioHardwareOSS::GetMixBuffer() {
  310. return (short *)m_buffer;
  311. }
  312. /*
  313. ===============
  314. idAudioHardwareOSS::Write
  315. rely on m_freeWriteChunks which has been set in Flush() before engine did the mixing for this MIXBUFFER_SAMPLE
  316. ===============
  317. */
  318. void idAudioHardwareOSS::Write( bool flushing ) {
  319. assert( m_audio_fd );
  320. int ret;
  321. if ( !flushing && m_writeChunks ) {
  322. // if we write after a new mixing loop, we should have m_writeChunk == 0
  323. // otherwise that last remaining chunk that was never flushed out to the audio device has just been overwritten
  324. Sys_Printf( "idAudioHardwareOSS::Write: %d samples were overflowed and dropped\n", m_writeChunks * MIXBUFFER_SAMPLES / MIXBUFFER_CHUNKS );
  325. }
  326. if ( !flushing ) {
  327. // if running after the mix loop, then we have a full buffer to write out
  328. m_writeChunks = MIXBUFFER_CHUNKS;
  329. }
  330. if ( m_freeWriteChunks == 0 ) {
  331. return;
  332. }
  333. // what to write and how much
  334. int pos = (int)m_buffer + ( MIXBUFFER_CHUNKS - m_writeChunks ) * m_channels * 2 * MIXBUFFER_SAMPLES / MIXBUFFER_CHUNKS;
  335. int len = Min( m_writeChunks, m_freeWriteChunks ) * m_channels * 2 * MIXBUFFER_SAMPLES / MIXBUFFER_CHUNKS;
  336. assert( len > 0 );
  337. if ( ( ret = write( m_audio_fd, (void*)pos, len ) ) == -1 ) {
  338. Sys_Printf( "write to audio fd failed: %s\n", strerror( errno ) );
  339. return;
  340. }
  341. if ( len != ret ) {
  342. Sys_Printf( "short write to audio fd: wrote %d out of %d\n", ret, m_buffer_size );
  343. return;
  344. }
  345. m_writeChunks -= Min( m_writeChunks, m_freeWriteChunks );
  346. }
  347. /*
  348. ===============
  349. Sys_LoadOpenAL
  350. -===============
  351. */
  352. bool Sys_LoadOpenAL( void ) {
  353. return false;
  354. }