// Emacs style mode select -*- C++ -*- //----------------------------------------------------------------------------- // // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 2005 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. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA // 02111-1307, USA. // // DESCRIPTION: // System interface for music. // //----------------------------------------------------------------------------- #include #include #include #include "SDL.h" #include "SDL_mixer.h" #include "doomtype.h" #include "memio.h" #include "mus2mid.h" #include "deh_str.h" #include "i_sound.h" #include "m_misc.h" #include "w_wad.h" #include "z_zone.h" #define MAXMIDLENGTH (96 * 1024) static boolean music_initialized = false; // If this is true, this module initialized SDL sound and has the // responsibility to shut it down static boolean sdl_was_initialized = false; static boolean musicpaused = false; static int current_music_volume; char *timidity_cfg_path = ""; static char *temp_timidity_cfg = NULL; // If the temp_timidity_cfg config variable is set, generate a "wrapper" // config file for Timidity to point to the actual config file. This // is needed to inject a "dir" command so that the patches are read // relative to the actual config file. void I_InitTimidityConfig(void) { char *env_string; char *p, *path; FILE *fstream; temp_timidity_cfg = NULL; if (!strcmp(timidity_cfg_path, "")) { return; } temp_timidity_cfg = M_TempFile("timidity.cfg"); fstream = fopen(temp_timidity_cfg, "w"); if (fstream == NULL) { Z_Free(temp_timidity_cfg); temp_timidity_cfg = NULL; return; } p = strrchr(timidity_cfg_path, DIR_SEPARATOR); if (p != NULL) { path = strdup(timidity_cfg_path); path[p - timidity_cfg_path] = '\0'; fprintf(fstream, "dir %s\n", path); free(path); } fprintf(fstream, "source %s\n", timidity_cfg_path); fclose(fstream); // Set the TIMIDITY_CFG environment variable to point to the temporary // config file. env_string = malloc(strlen(temp_timidity_cfg) + 15); sprintf(env_string, "TIMIDITY_CFG=%s", temp_timidity_cfg); putenv(env_string); } // Remove the temporary config file generated by I_InitTimidityConfig(). static void RemoveTimidityConfig(void) { if (temp_timidity_cfg != NULL) { remove(temp_timidity_cfg); Z_Free(temp_timidity_cfg); } } // Shutdown music static void I_SDL_ShutdownMusic(void) { if (music_initialized) { Mix_HaltMusic(); music_initialized = false; if (sdl_was_initialized) { Mix_CloseAudio(); SDL_QuitSubSystem(SDL_INIT_AUDIO); sdl_was_initialized = false; } } } static boolean SDLIsInitialized(void) { int freq, channels; Uint16 format; return Mix_QuerySpec(&freq, &format, &channels) != 0; } // Initialize music subsystem static boolean I_SDL_InitMusic(void) { // SDL_mixer prior to v1.2.11 has a bug that causes crashes // with MIDI playback. Print a warning message if we are // using an old version. #ifdef __MACOSX__ { const SDL_version *v = Mix_Linked_Version(); if (SDL_VERSIONNUM(v->major, v->minor, v->patch) < SDL_VERSIONNUM(1, 2, 11)) { printf("\n" " *** WARNING ***\n" " You are using an old version of SDL_mixer.\n" " Music playback on this version may cause crashes\n" " under OS X and is disabled by default.\n" "\n"); } } #endif // If SDL_mixer is not initialized, we have to initialize it // and have the responsibility to shut it down later on. if (SDLIsInitialized()) { music_initialized = true; } else { if (SDL_Init(SDL_INIT_AUDIO) < 0) { fprintf(stderr, "Unable to set up sound.\n"); } else if (Mix_OpenAudio(snd_samplerate, AUDIO_S16SYS, 2, 1024) < 0) { fprintf(stderr, "Error initializing SDL_mixer: %s\n", Mix_GetError()); SDL_QuitSubSystem(SDL_INIT_AUDIO); } else { SDL_PauseAudio(0); sdl_was_initialized = true; music_initialized = true; } } // Once initialization is complete, the temporary Timidity config // file can be removed. RemoveTimidityConfig(); return music_initialized; } // // SDL_mixer's native MIDI music playing does not pause properly. // As a workaround, set the volume to 0 when paused. // static void UpdateMusicVolume(void) { int vol; if (musicpaused) { vol = 0; } else { vol = (current_music_volume * MIX_MAX_VOLUME) / 127; } Mix_VolumeMusic(vol); } // Set music volume (0 - 127) static void I_SDL_SetMusicVolume(int volume) { // Internal state variable. current_music_volume = volume; UpdateMusicVolume(); } // Start playing a mid static void I_SDL_PlaySong(void *handle, boolean looping) { Mix_Music *music = (Mix_Music *) handle; int loops; if (!music_initialized) { return; } if (handle == NULL) { return; } if (looping) { loops = -1; } else { loops = 1; } Mix_PlayMusic(music, loops); } static void I_SDL_PauseSong(void) { if (!music_initialized) { return; } musicpaused = true; UpdateMusicVolume(); } static void I_SDL_ResumeSong(void) { if (!music_initialized) { return; } musicpaused = false; UpdateMusicVolume(); } static void I_SDL_StopSong(void) { if (!music_initialized) { return; } Mix_HaltMusic(); } static void I_SDL_UnRegisterSong(void *handle) { Mix_Music *music = (Mix_Music *) handle; if (!music_initialized) { return; } if (handle == NULL) { return; } Mix_FreeMusic(music); } // Determine whether memory block is a .mid file static boolean IsMid(byte *mem, int len) { return len > 4 && !memcmp(mem, "MThd", 4); } static boolean ConvertMus(byte *musdata, int len, char *filename) { MEMFILE *instream; MEMFILE *outstream; void *outbuf; size_t outbuf_len; int result; instream = mem_fopen_read(musdata, len); outstream = mem_fopen_write(); result = mus2mid(instream, outstream); if (result == 0) { mem_get_buf(outstream, &outbuf, &outbuf_len); M_WriteFile(filename, outbuf, outbuf_len); } mem_fclose(instream); mem_fclose(outstream); return result; } static void *I_SDL_RegisterSong(void *data, int len) { char *filename; Mix_Music *music; if (!music_initialized) { return NULL; } // MUS files begin with "MUS" // Reject anything which doesnt have this signature filename = M_TempFile("doom.mid"); if (IsMid(data, len) && len < MAXMIDLENGTH) { M_WriteFile(filename, data, len); } else { // Assume a MUS file and try to convert ConvertMus(data, len, filename); } // Load the MIDI music = Mix_LoadMUS(filename); if (music == NULL) { // Failed to load fprintf(stderr, "Error loading midi: %s\n", Mix_GetError()); } // remove file now remove(filename); Z_Free(filename); return music; } // Is the song playing? static boolean I_SDL_MusicIsPlaying(void) { if (!music_initialized) { return false; } return Mix_PlayingMusic(); } static snddevice_t music_sdl_devices[] = { SNDDEVICE_PAS, SNDDEVICE_GUS, SNDDEVICE_WAVEBLASTER, SNDDEVICE_SOUNDCANVAS, SNDDEVICE_GENMIDI, SNDDEVICE_AWE32, }; music_module_t music_sdl_module = { music_sdl_devices, arrlen(music_sdl_devices), I_SDL_InitMusic, I_SDL_ShutdownMusic, I_SDL_SetMusicVolume, I_SDL_PauseSong, I_SDL_ResumeSong, I_SDL_RegisterSong, I_SDL_UnRegisterSong, I_SDL_PlaySong, I_SDL_StopSong, I_SDL_MusicIsPlaying, };