// // Copyright(C) 2005-2014 Simon Howard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // DESCRIPTION: // System interface for PC speaker sound. // #include "SDL.h" #include #include "doomtype.h" #include "deh_str.h" #include "i_sound.h" #include "m_misc.h" #include "w_wad.h" #include "z_zone.h" #include "pcsound.h" #define TIMER_FREQ 1193181 /* hz */ static boolean pcs_initialized = false; static SDL_mutex *sound_lock; static boolean use_sfx_prefix; static uint8_t *current_sound_lump = NULL; static uint8_t *current_sound_pos = NULL; static unsigned int current_sound_remaining = 0; static int current_sound_handle = 0; static int current_sound_lump_num = -1; static const uint16_t divisors[] = { 0, 6818, 6628, 6449, 6279, 6087, 5906, 5736, 5575, 5423, 5279, 5120, 4971, 4830, 4697, 4554, 4435, 4307, 4186, 4058, 3950, 3836, 3728, 3615, 3519, 3418, 3323, 3224, 3131, 3043, 2960, 2875, 2794, 2711, 2633, 2560, 2485, 2415, 2348, 2281, 2213, 2153, 2089, 2032, 1975, 1918, 1864, 1810, 1757, 1709, 1659, 1612, 1565, 1521, 1478, 1435, 1395, 1355, 1316, 1280, 1242, 1207, 1173, 1140, 1107, 1075, 1045, 1015, 986, 959, 931, 905, 879, 854, 829, 806, 783, 760, 739, 718, 697, 677, 658, 640, 621, 604, 586, 570, 553, 538, 522, 507, 493, 479, 465, 452, 439, 427, 415, 403, 391, 380, 369, 359, 348, 339, 329, 319, 310, 302, 293, 285, 276, 269, 261, 253, 246, 239, 232, 226, 219, 213, 207, 201, 195, 190, 184, 179, }; static void PCSCallbackFunc(int *duration, int *freq) { unsigned int tone; *duration = 1000 / 140; if (SDL_LockMutex(sound_lock) < 0) { *freq = 0; return; } if (current_sound_lump != NULL && current_sound_remaining > 0) { // Read the next tone tone = *current_sound_pos; // Use the tone -> frequency lookup table. See pcspkr10.zip // for a full discussion of this. // Check we don't overflow the frequency table. if (tone < arrlen(divisors) && divisors[tone] != 0) { *freq = (int) (TIMER_FREQ / divisors[tone]); } else { *freq = 0; } ++current_sound_pos; --current_sound_remaining; } else { *freq = 0; } SDL_UnlockMutex(sound_lock); } static boolean CachePCSLump(sfxinfo_t *sfxinfo) { int lumplen; int headerlen; // Free the current sound lump back to the cache if (current_sound_lump != NULL) { W_ReleaseLumpNum(current_sound_lump_num); current_sound_lump = NULL; } // Load from WAD current_sound_lump = W_CacheLumpNum(sfxinfo->lumpnum, PU_STATIC); lumplen = W_LumpLength(sfxinfo->lumpnum); // Read header if (current_sound_lump[0] != 0x00 || current_sound_lump[1] != 0x00) { return false; } headerlen = (current_sound_lump[3] << 8) | current_sound_lump[2]; if (headerlen > lumplen - 4) { return false; } // Header checks out ok current_sound_remaining = headerlen; current_sound_pos = current_sound_lump + 4; current_sound_lump_num = sfxinfo->lumpnum; return true; } // These Doom PC speaker sounds are not played - this can be seen in the // Heretic source code, where there are remnants of this left over // from Doom. static boolean IsDisabledSound(sfxinfo_t *sfxinfo) { int i; const char *disabled_sounds[] = { "posact", "bgact", "dmact", "dmpain", "popain", "sawidl", }; for (i=0; iname, disabled_sounds[i])) { return true; } } return false; } static int I_PCS_StartSound(sfxinfo_t *sfxinfo, int channel, int vol, int sep) { int result; if (!pcs_initialized) { return -1; } if (IsDisabledSound(sfxinfo)) { return -1; } if (SDL_LockMutex(sound_lock) < 0) { return -1; } result = CachePCSLump(sfxinfo); if (result) { current_sound_handle = channel; } SDL_UnlockMutex(sound_lock); if (result) { return channel; } else { return -1; } } static void I_PCS_StopSound(int handle) { if (!pcs_initialized) { return; } if (SDL_LockMutex(sound_lock) < 0) { return; } // If this is the channel currently playing, immediately end it. if (current_sound_handle == handle) { current_sound_remaining = 0; } SDL_UnlockMutex(sound_lock); } // // Retrieve the raw data lump index // for a given SFX name. // static int I_PCS_GetSfxLumpNum(sfxinfo_t* sfx) { char namebuf[9]; if (use_sfx_prefix) { M_snprintf(namebuf, sizeof(namebuf), "dp%s", DEH_String(sfx->name)); } else { M_StringCopy(namebuf, DEH_String(sfx->name), sizeof(namebuf)); } return W_GetNumForName(namebuf); } static boolean I_PCS_SoundIsPlaying(int handle) { if (!pcs_initialized) { return false; } if (handle != current_sound_handle) { return false; } return current_sound_lump != NULL && current_sound_remaining > 0; } static boolean I_PCS_InitSound(boolean _use_sfx_prefix) { use_sfx_prefix = _use_sfx_prefix; // Use the sample rate from the configuration file PCSound_SetSampleRate(snd_samplerate); // Initialize the PC speaker subsystem. pcs_initialized = PCSound_Init(PCSCallbackFunc); if (pcs_initialized) { sound_lock = SDL_CreateMutex(); } return pcs_initialized; } static void I_PCS_ShutdownSound(void) { if (pcs_initialized) { PCSound_Shutdown(); } } static void I_PCS_UpdateSound(void) { // no-op. } void I_PCS_UpdateSoundParams(int channel, int vol, int sep) { // no-op. } static snddevice_t sound_pcsound_devices[] = { SNDDEVICE_PCSPEAKER, }; sound_module_t sound_pcsound_module = { sound_pcsound_devices, arrlen(sound_pcsound_devices), I_PCS_InitSound, I_PCS_ShutdownSound, I_PCS_GetSfxLumpNum, I_PCS_UpdateSound, I_PCS_UpdateSoundParams, I_PCS_StartSound, I_PCS_StopSound, I_PCS_SoundIsPlaying, };