From f3b53778ca850ca0b01ff445000f4f07545b62fc Mon Sep 17 00:00:00 2001 From: Simon Howard Date: Sun, 3 Mar 2013 20:29:15 +0000 Subject: Add GUS pseudo-emulation. Subversion-branch: /branches/v2-branch Subversion-revision: 2566 --- src/Makefile.am | 1 + src/gusconf.c | 268 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/gusconf.h | 37 ++++++++ src/i_sdlmusic.c | 49 +++++++--- src/i_sound.c | 7 +- src/m_config.c | 14 +++ src/setup/sound.c | 4 + 7 files changed, 366 insertions(+), 14 deletions(-) create mode 100644 src/gusconf.c create mode 100644 src/gusconf.h diff --git a/src/Makefile.am b/src/Makefile.am index a67660e2..b9112c21 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -117,6 +117,7 @@ w_merge.c w_merge.h # source files needed for FEATURE_SOUND FEATURE_SOUND_SOURCE_FILES = \ +gusconf.c gusconf.h \ i_pcsound.c \ i_sdlsound.c \ i_sdlmusic.c \ diff --git a/src/gusconf.c b/src/gusconf.c new file mode 100644 index 00000000..8efd9803 --- /dev/null +++ b/src/gusconf.c @@ -0,0 +1,268 @@ +// Emacs style mode select -*- C++ -*- +//----------------------------------------------------------------------------- +// +// Copyright(C) 2013 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: +// GUS emulation code. +// +// Actually emulating a GUS is far too much work; fortunately +// GUS "emulation" already exists in the form of Timidity, which +// supports GUS patch files. This code therefore converts Doom's +// DMXGUS lump into an equivalent Timidity configuration file. +// +//----------------------------------------------------------------------------- + + +#include +#include +#include +#include + +#include "w_wad.h" +#include "z_zone.h" + +#define MAX_INSTRUMENTS 256 + +typedef struct +{ + char *patch_names[MAX_INSTRUMENTS]; + int mapping[MAX_INSTRUMENTS]; +} gus_config_t; + +char *gus_patch_path = ""; +unsigned int gus_ram_kb = 1024; + +static unsigned int MappingIndex(void) +{ + unsigned int result = gus_ram_kb / 256; + + if (result < 1) + { + return 1; + } + else if (result > 4) + { + return 4; + } + else + { + return result; + } +} + +static int SplitLine(char *line, char **fields, unsigned int max_fields) +{ + unsigned int num_fields; + char *p; + + fields[0] = line; + num_fields = 1; + + for (p = line; *p != '\0'; ++p) + { + if (*p == ',') + { + *p = '\0'; + + // Skip spaces following the comma. + do + { + ++p; + } while (*p != '\0' && isspace(*p)); + + fields[num_fields] = p; + ++num_fields; + --p; + + if (num_fields >= max_fields) + { + break; + } + } + else if (*p == '#') + { + *p = '\0'; + break; + } + } + + // Strip off trailing whitespace from the end of the line. + p = fields[num_fields - 1] + strlen(fields[num_fields - 1]); + while (p > fields[num_fields - 1] && isspace(*(p - 1))) + { + --p; + *p = '\0'; + } + + return num_fields; +} + +static void ParseLine(gus_config_t *config, char *line) +{ + char *fields[6]; + unsigned int num_fields; + unsigned int instr_id, mapped_id; + + num_fields = SplitLine(line, fields, 6); + + if (num_fields < 6) + { + return; + } + + instr_id = atoi(fields[0]); + mapped_id = atoi(fields[MappingIndex()]); + + free(config->patch_names[instr_id]); + config->patch_names[instr_id] = strdup(fields[5]); + config->mapping[instr_id] = mapped_id; +} + +static void ParseDMXConfig(char *dmxconf, gus_config_t *config) +{ + char *p, *newline; + unsigned int i; + + memset(config, 0, sizeof(gus_config_t)); + + for (i = 0; i < MAX_INSTRUMENTS; ++i) + { + config->mapping[i] = -1; + } + + p = dmxconf; + + for (;;) + { + newline = strchr(p, '\n'); + + if (newline != NULL) + { + *newline = '\0'; + } + + ParseLine(config, p); + + if (newline == NULL) + { + break; + } + else + { + p = newline + 1; + } + } +} + +static void FreeDMXConfig(gus_config_t *config) +{ + unsigned int i; + + for (i = 0; i < MAX_INSTRUMENTS; ++i) + { + free(config->patch_names[i]); + } +} + +static char *ReadDMXConfig(void) +{ + int lumpnum; + unsigned int len; + char *data; + + // TODO: This should be chosen based on gamemode == commercial: + + lumpnum = W_CheckNumForName("DMXGUS"); + + if (lumpnum < 0) + { + lumpnum = W_GetNumForName("DMXGUSC"); + } + + len = W_LumpLength(lumpnum); + data = Z_Malloc(len + 1, PU_STATIC, NULL); + W_ReadLump(lumpnum, data); + + return data; +} + +static boolean WriteTimidityConfig(char *path, gus_config_t *config) +{ + FILE *fstream; + unsigned int i; + + fstream = fopen(path, "w"); + + if (fstream == NULL) + { + return false; + } + + fprintf(fstream, "# Autogenerated Timidity config.\n\n"); + + fprintf(fstream, "dir %s\n", gus_patch_path); + + fprintf(fstream, "\nbank 0\n\n"); + + for (i = 0; i < 128; ++i) + { + if (config->mapping[i] >= 0 && config->mapping[i] < MAX_INSTRUMENTS + && config->patch_names[config->mapping[i]] != NULL) + { + fprintf(fstream, "%i %s\n", + i, config->patch_names[config->mapping[i]]); + } + } + + fprintf(fstream, "\ndrumset 0\n\n"); + + for (i = 128 + 25; i < MAX_INSTRUMENTS; ++i) + { + if (config->mapping[i] >= 0 && config->mapping[i] < MAX_INSTRUMENTS + && config->patch_names[config->mapping[i]] != NULL) + { + fprintf(fstream, "%i %s\n", + i - 128, config->patch_names[config->mapping[i]]); + } + } + + fprintf(fstream, "\n"); + + fclose(fstream); + + return true; +} + +boolean GUS_WriteConfig(char *path) +{ + boolean result; + char *dmxconf; + gus_config_t config; + + dmxconf = ReadDMXConfig(); + ParseDMXConfig(dmxconf, &config); + + result = WriteTimidityConfig(path, &config); + + FreeDMXConfig(&config); + Z_Free(dmxconf); + + return result; +} + diff --git a/src/gusconf.h b/src/gusconf.h new file mode 100644 index 00000000..b0757149 --- /dev/null +++ b/src/gusconf.h @@ -0,0 +1,37 @@ +// Emacs style mode select -*- C++ -*- +//----------------------------------------------------------------------------- +// +// Copyright(C) 2013 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: +// GUS emulation code. +// +//----------------------------------------------------------------------------- + +#ifndef __GUSCONF_H__ +#define __GUSCONF_H__ + +#include "doomtype.h" + +extern char *gus_patch_path; +extern unsigned int gus_ram_kb; + +boolean GUS_WriteConfig(char *path); + +#endif /* #ifndef __GUSCONF_H__ */ + diff --git a/src/i_sdlmusic.c b/src/i_sdlmusic.c index e1f9212d..79755890 100644 --- a/src/i_sdlmusic.c +++ b/src/i_sdlmusic.c @@ -36,6 +36,7 @@ #include "mus2mid.h" #include "deh_str.h" +#include "gusconf.h" #include "i_sound.h" #include "m_misc.h" #include "w_wad.h" @@ -54,6 +55,7 @@ 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" @@ -61,27 +63,21 @@ static char *temp_timidity_cfg = NULL; // is needed to inject a "dir" command so that the patches are read // relative to the actual config file. -void I_InitTimidityConfig(void) +static boolean WriteWrapperTimidityConfig(char *write_path) { - char *env_string; char *p, *path; FILE *fstream; - temp_timidity_cfg = NULL; - if (!strcmp(timidity_cfg_path, "")) { - return; + return false; } - temp_timidity_cfg = M_TempFile("timidity.cfg"); - fstream = fopen(temp_timidity_cfg, "w"); + fstream = fopen(write_path, "w"); if (fstream == NULL) { - Z_Free(temp_timidity_cfg); - temp_timidity_cfg = NULL; - return; + return false; } p = strrchr(timidity_cfg_path, DIR_SEPARATOR); @@ -96,12 +92,39 @@ void I_InitTimidityConfig(void) fprintf(fstream, "source %s\n", timidity_cfg_path); fclose(fstream); + return true; +} + +void I_InitTimidityConfig(void) +{ + char *env_string; + boolean success; + + temp_timidity_cfg = M_TempFile("timidity.cfg"); + + if (snd_musicdevice == SNDDEVICE_GUS) + { + success = GUS_WriteConfig(temp_timidity_cfg); + } + else + { + success = WriteWrapperTimidityConfig(temp_timidity_cfg); + } + // 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); + if (success) + { + env_string = malloc(strlen(temp_timidity_cfg) + 15); + sprintf(env_string, "TIMIDITY_CFG=%s", temp_timidity_cfg); + putenv(env_string); + } + else + { + Z_Free(temp_timidity_cfg); + temp_timidity_cfg = NULL; + } } // Remove the temporary config file generated by I_InitTimidityConfig(). diff --git a/src/i_sound.c b/src/i_sound.c index 198b233e..47cae682 100644 --- a/src/i_sound.c +++ b/src/i_sound.c @@ -32,6 +32,7 @@ #include "doomfeatures.h" #include "doomtype.h" +#include "gusconf.h" #include "i_sound.h" #include "i_video.h" #include "m_argv.h" @@ -218,7 +219,9 @@ void I_InitSound(boolean use_sfx_prefix) // the TIMIDITY_CFG environment variable here before SDL_mixer // is opened. - if (!nomusic && snd_musicdevice == SNDDEVICE_GENMIDI) + if (!nomusic + && (snd_musicdevice == SNDDEVICE_GENMIDI + || snd_musicdevice == SNDDEVICE_GUS)) { I_InitTimidityConfig(); } @@ -435,6 +438,8 @@ void I_BindSoundVariables(void) M_BindVariable("opl_io_port", &opl_io_port); M_BindVariable("timidity_cfg_path", &timidity_cfg_path); + M_BindVariable("gus_patch_path", &gus_patch_path); + M_BindVariable("gus_ram_kb", &gus_ram_kb); #ifdef FEATURE_SOUND M_BindVariable("use_libsamplerate", &use_libsamplerate); diff --git a/src/m_config.c b/src/m_config.c index e2afe6cc..87079bd5 100644 --- a/src/m_config.c +++ b/src/m_config.c @@ -915,6 +915,20 @@ static default_t extra_defaults_list[] = CONFIG_VARIABLE_STRING(timidity_cfg_path), + //! + // Path to GUS patch files to use when operating in GUS emulation + // mode. + // + + CONFIG_VARIABLE_STRING(gus_patch_path), + + //! + // Number of kilobytes of RAM to use in GUS emulation mode. Valid + // values are 256, 512, 768 or 1024. + // + + CONFIG_VARIABLE_INT(gus_ram_kb), + #endif //! diff --git a/src/setup/sound.c b/src/setup/sound.c index 4a9b80b3..af7e5dc1 100644 --- a/src/setup/sound.c +++ b/src/setup/sound.c @@ -79,6 +79,8 @@ static int show_talk = 0; static int use_libsamplerate = 0; static char *timidity_cfg_path = ""; +static char *gus_patch_path = ""; +static unsigned int gus_ram_kb = 1024; // DOS specific variables: these are unused but should be maintained // so that the config file can be shared between chocolate @@ -254,6 +256,8 @@ void BindSoundVariables(void) M_BindVariable("snd_samplerate", &snd_samplerate); M_BindVariable("use_libsamplerate", &use_libsamplerate); M_BindVariable("timidity_cfg_path", &timidity_cfg_path); + M_BindVariable("gus_patch_path", &gus_patch_path); + M_BindVariable("gus_ram_kb", &gus_ram_kb); M_BindVariable("snd_sbport", &snd_sbport); M_BindVariable("snd_sbirq", &snd_sbirq); -- cgit v1.2.3