summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/Makefile.am1
-rw-r--r--src/gusconf.c268
-rw-r--r--src/gusconf.h37
-rw-r--r--src/i_sdlmusic.c49
-rw-r--r--src/i_sound.c7
-rw-r--r--src/m_config.c14
-rw-r--r--src/setup/sound.c4
7 files changed, 366 insertions, 14 deletions
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 <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+
+#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);