foxygit / doom Log in
commit beab4eb58b667a5883166bd1dd7bc33369a005c7
Author:     Simon Howard <fraggle@gmail.com>
AuthorDate: Sun Jun 17 18:40:02 2007 +0000
Commit:     Simon Howard <fraggle@gmail.com>
CommitDate: Sun Jun 17 18:40:02 2007 +0000

    Split i_sound.c into i_sdlsound.c, i_sdlmusic.c, with generic "sound
    driver" modules, one for PC speaker and one for digital output.

    Subversion-branch: /trunk/chocolate-doom
    Subversion-revision: 913
---
 pcsound/pcsound.c                             |   8 +
 pcsound/pcsound.h                             |  21 +-
 src/i_pcsound.h => pcsound/pcsound_internal.h |  33 +-
 pcsound/pcsound_linux.c                       |   1 +
 pcsound/pcsound_sdl.c                         |  63 +++-
 pcsound/pcsound_win32.c                       |   1 +
 src/Makefile.am                               |   7 +-
 src/d_main.c                                  |   1 -
 src/deh_sound.c                               |   8 -
 src/{i_sound.h => i_music.h}                  |  57 +---
 src/i_pcsound.c                               |  87 ++++-
 src/i_sdlmusic.c                              | 317 +++++++++++++++++++
 src/{i_sound.c => i_sdlsound.c}               | 438 ++++----------------------
 src/s_sound.c                                 | 235 +++++++++-----
 src/s_sound.h                                 |  44 +++
 15 files changed, 745 insertions(+), 576 deletions(-)

diff --git a/pcsound/pcsound.c b/pcsound/pcsound.c
index 1491ff61..8c739b35 100644
--- a/pcsound/pcsound.c
+++ b/pcsound/pcsound.c
@@ -29,6 +29,7 @@

 #include "config.h"
 #include "pcsound.h"
+#include "pcsound_internal.h"

 #ifdef _WIN32
 extern pcsound_driver_t pcsound_win32_driver;
@@ -54,6 +55,13 @@ static pcsound_driver_t *drivers[] =

 static pcsound_driver_t *pcsound_driver = NULL;

+int pcsound_sample_rate;
+
+void PCSound_SetSampleRate(int rate)
+{
+    pcsound_sample_rate = rate;
+}
+
 int PCSound_Init(pcsound_callback_func callback_func)
 {
     char *driver_name;
diff --git a/pcsound/pcsound.h b/pcsound/pcsound.h
index 12a4ffe9..e64796b8 100644
--- a/pcsound/pcsound.h
+++ b/pcsound/pcsound.h
@@ -26,22 +26,21 @@
 #ifndef PCSOUND_H
 #define PCSOUND_H

-#define PCSOUND_8253_FREQUENCY 1193280
-
-typedef struct pcsound_driver_s pcsound_driver_t;
 typedef void (*pcsound_callback_func)(int *duration, int *frequency);
-typedef int (*pcsound_init_func)(pcsound_callback_func callback);
-typedef void (*pcsound_shutdown_func)(void);

-struct pcsound_driver_s
-{
-    char *name;
-    pcsound_init_func init_func;
-    pcsound_shutdown_func shutdown_func;
-};
+// Initialise the PC speaker subsystem.  The given function is called
+// periodically to request more sound data to play.

 int PCSound_Init(pcsound_callback_func callback_func);
+
+// Shut down the PC speaker subsystem.
+
 void PCSound_Shutdown(void);

+// Set the preferred output sample rate when emulating a PC speaker.
+// This must be called before PCSound_Init.
+
+void PCSound_SetSampleRate(int rate);
+
 #endif /* #ifndef PCSOUND_H */

diff --git a/src/i_pcsound.h b/pcsound/pcsound_internal.h
similarity index 66%
rename from src/i_pcsound.h
rename to pcsound/pcsound_internal.h
index 601f4ee5..c5ae90b6 100644
--- a/src/i_pcsound.h
+++ b/pcsound/pcsound_internal.h
@@ -19,22 +19,29 @@
 // 02111-1307, USA.
 //
 // DESCRIPTION:
-//	System interface for PC speaker sound.
+//    PC speaker interface.
 //
 //-----------------------------------------------------------------------------

-#ifndef __I_PCSOUND_H__
-#define __I_PCSOUND_H__
+#ifndef PCSOUND_INTERNAL_H
+#define PCSOUND_INTERNAL_H

-int I_PCS_StartSound(int id,
-                     int channel,
-                     int vol,
-                     int sep,
-                     int pitch,
-                     int priority);
-void I_PCS_StopSound(int handle);
-int I_PCS_SoundIsPlaying(int handle);
-void I_PCS_InitSound(void);
+#include "pcsound.h"

-#endif /* #ifndef __I_PCSOUND_H__ */
+#define PCSOUND_8253_FREQUENCY 1193280
+
+typedef struct pcsound_driver_s pcsound_driver_t;
+typedef int (*pcsound_init_func)(pcsound_callback_func callback);
+typedef void (*pcsound_shutdown_func)(void);
+
+struct pcsound_driver_s
+{
+    char *name;
+    pcsound_init_func init_func;
+    pcsound_shutdown_func shutdown_func;
+};
+
+extern int pcsound_sample_rate;
+
+#endif /* #ifndef PCSOUND_INTERNAL_H */

diff --git a/pcsound/pcsound_linux.c b/pcsound/pcsound_linux.c
index 7c1abc7b..02ab47b5 100644
--- a/pcsound/pcsound_linux.c
+++ b/pcsound/pcsound_linux.c
@@ -40,6 +40,7 @@
 #include "SDL_thread.h"

 #include "pcsound.h"
+#include "pcsound_internal.h"

 #define CONSOLE_DEVICE "/dev/console"

diff --git a/pcsound/pcsound_sdl.c b/pcsound/pcsound_sdl.c
index 50820394..f862dfa7 100644
--- a/pcsound/pcsound_sdl.c
+++ b/pcsound/pcsound_sdl.c
@@ -30,9 +30,17 @@
 #include "SDL_mixer.h"

 #include "pcsound.h"
+#include "pcsound_internal.h"

 #define SQUARE_WAVE_AMP 0x2000

+// If true, we initialised SDL and have the responsibility to shut it
+// down
+
+static int sdl_was_initialised = 0;
+
+// Callback function to invoke when we want new sound data
+
 static pcsound_callback_func callback;

 // Output sound format
@@ -137,16 +145,57 @@ static void PCSound_Mix_Callback(void *udata, Uint8 *stream, int len)
     }
 }

+static int SDLIsInitialised(void)
+{
+    int freq, channels;
+    Uint16 format;
+
+    return Mix_QuerySpec(&freq, &format, &channels);
+}
+
+static void PCSound_SDL_Shutdown(void)
+{
+    if (sdl_was_initialised)
+    {
+        Mix_CloseAudio();
+        SDL_QuitSubSystem(SDL_INIT_AUDIO);
+        sdl_was_initialised = 0;
+    }
+}
+
 static int PCSound_SDL_Init(pcsound_callback_func callback_func)
 {
-    // Check that SDL_mixer has been opened already
-    // If not, fail
+    // Check if SDL_mixer has been opened already
+    // If not, we must initialise it now

-    if (!Mix_QuerySpec(&mixing_freq, &mixing_format, &mixing_channels))
+    if (!SDLIsInitialised())
     {
-        return 0;
+        if (SDL_Init(SDL_INIT_AUDIO) < 0)
+        {
+            fprintf(stderr, "Unable to set up sound.\n");
+            return 0;
+        }
+
+        if (Mix_OpenAudio(pcsound_sample_rate, AUDIO_S16SYS, 2, 1024) < 0)
+        {
+            fprintf(stderr, "Error initialising SDL_mixer: %s\n", Mix_GetError());
+
+            SDL_QuitSubSystem(SDL_INIT_AUDIO);
+            return 0;
+        }
+
+        SDL_PauseAudio(0);
+
+        // When this module shuts down, it has the responsibility to
+        // shut down SDL.
+
+        sdl_was_initialised = 1;
     }

+    // Get the mixer frequency, format and number of channels.
+
+    Mix_QuerySpec(&mixing_freq, &mixing_format, &mixing_channels);
+
     // Only supports AUDIO_S16SYS

     if (mixing_format != AUDIO_S16SYS || mixing_channels != 2)
@@ -154,6 +203,8 @@ static int PCSound_SDL_Init(pcsound_callback_func callback_func)
         fprintf(stderr,
                 "PCSound_SDL only supports native signed 16-bit LSB, "
                 "stereo format!\n");
+
+        PCSound_SDL_Shutdown();
         return 0;
     }

@@ -166,10 +217,6 @@ static int PCSound_SDL_Init(pcsound_callback_func callback_func)
     return 1;
 }

-static void PCSound_SDL_Shutdown(void)
-{
-}
-
 pcsound_driver_t pcsound_sdl_driver =
 {
     "SDL",
diff --git a/pcsound/pcsound_win32.c b/pcsound/pcsound_win32.c
index 48677d73..7976b444 100644
--- a/pcsound/pcsound_win32.c
+++ b/pcsound/pcsound_win32.c
@@ -29,6 +29,7 @@
 #include <windows.h>

 #include "pcsound.h"
+#include "pcsound_internal.h"

 static SDL_Thread *sound_thread_handle;
 static int sound_thread_running;
diff --git a/src/Makefile.am b/src/Makefile.am
index 75c08c05..031369a7 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -95,6 +95,7 @@ r_sky.c              r_sky.h               \
 r_state.h                                  \
 r_things.c           r_things.h            \
 sounds.c             sounds.h              \
+s_sound.c            s_sound.h             \
 st_lib.c             st_lib.h              \
 st_stuff.c           st_stuff.h            \
 tables.c             tables.h              \
@@ -146,9 +147,9 @@ w_merge.c            w_merge.h

 FEATURE_SOUND_SOURCE_FILES =               \
 i_pcsound.c          i_pcsound.h           \
-i_sound.c            i_sound.h             \
-mus2mid.c            mus2mid.h             \
-s_sound.c            s_sound.h
+i_sdlsound.c                               \
+i_sdlmusic.c         i_music.h             \
+mus2mid.c            mus2mid.h

 SOURCE_FILES = $(MAIN_SOURCE_FILES)                \
                $(FEATURE_DEHACKED_SOURCE_FILES)    \
diff --git a/src/d_main.c b/src/d_main.c
index 65cb6bb9..6981b884 100644
--- a/src/d_main.c
+++ b/src/d_main.c
@@ -59,7 +59,6 @@
 #include "p_saveg.h"

 #include "i_system.h"
-#include "i_sound.h"
 #include "i_timer.h"
 #include "i_video.h"

diff --git a/src/deh_sound.c b/src/deh_sound.c
index 7068d94a..761ed069 100644
--- a/src/deh_sound.c
+++ b/src/deh_sound.c
@@ -68,15 +68,7 @@ static void *DEH_SoundStart(deh_context_t *context, char *line)
                              "in Vanilla dehacked.", sound_number);
     }

-#ifdef FEATURE_SOUND
-
     return &S_sfx[sound_number];
-
-#else
-
-    return NULL;
-
-#endif
 }

 static void DEH_SoundParseLine(deh_context_t *context, char *line, void *tag)
diff --git a/src/i_sound.h b/src/i_music.h
similarity index 66%
rename from src/i_sound.h
rename to src/i_music.h
index ad3852ce..70664c9a 100644
--- a/src/i_sound.h
+++ b/src/i_music.h
@@ -21,7 +21,7 @@
 //
 //
 // DESCRIPTION:
-//	System interface, sound.
+//	System interface, music.
 //
 //-----------------------------------------------------------------------------

@@ -32,59 +32,7 @@

 #include "doomstat.h"
 #include "sounds.h"
-
-
-
-// Init at program start...
-void I_InitSound();
-
-// ... update sound buffer and audio device at runtime...
-void I_UpdateSound(void);
-void I_SubmitSound(void);
-
-// ... shut down and relase at program termination.
-void I_ShutdownSound(void);
-
-
-//
-//  SFX I/O
-//
-
-// Initialize channels?
-void I_SetChannels();
-
-// Get raw data lump index for sound descriptor.
-int I_GetSfxLumpNum (sfxinfo_t* sfxinfo );
-
-
-// Starts a sound in a particular sound channel.
-int
-I_StartSound
-( int		id,
-  int           channel,
-  int		vol,
-  int		sep,
-  int		pitch,
-  int		priority );
-
-
-// Stops a sound channel.
-void I_StopSound(int handle);
-
-// Called by S_*() functions
-//  to see if a channel is still playing.
-// Returns 0 if no longer playing, 1 if playing.
-int I_SoundIsPlaying(int handle);
-
-// Updates the volume, separation,
-//  and pitch of a sound channel.
-void
-I_UpdateSoundParams
-( int		handle,
-  int		vol,
-  int		sep,
-  int		pitch );
-
+#include "s_sound.h"

 //
 //  MUSIC I/O
@@ -125,3 +73,4 @@ boolean I_QrySongPlaying(void *handle);


 #endif
+
diff --git a/src/i_pcsound.c b/src/i_pcsound.c
index 0dced9c1..08ba92e9 100644
--- a/src/i_pcsound.c
+++ b/src/i_pcsound.c
@@ -28,8 +28,8 @@
 #include "doomdef.h"
 #include "doomtype.h"

-#include "i_pcsound.h"
-#include "i_sound.h"
+#include "deh_main.h"
+#include "s_sound.h"
 #include "sounds.h"

 #include "w_wad.h"
@@ -62,7 +62,7 @@ static float frequencies[] = {

 #define NUM_FREQUENCIES (sizeof(frequencies) / sizeof(*frequencies))

-void PCSCallbackFunc(int *duration, int *freq)
+static void PCSCallbackFunc(int *duration, int *freq)
 {
     int tone;

@@ -144,12 +144,10 @@ static boolean CachePCSLump(int sound_id)
     return true;
 }

-int I_PCS_StartSound(int id,
-                     int channel,
-                     int vol,
-                     int sep,
-                     int pitch,
-                     int priority)
+static int I_PCS_StartSound(int id,
+                            int channel,
+                            int vol,
+                            int sep)
 {
     int result;

@@ -192,7 +190,7 @@ int I_PCS_StartSound(int id,
     }
 }

-void I_PCS_StopSound(int handle)
+static void I_PCS_StopSound(int handle)
 {
     if (!pcs_initialised)
     {
@@ -214,7 +212,22 @@ void I_PCS_StopSound(int handle)
     SDL_UnlockMutex(sound_lock);
 }

-int I_PCS_SoundIsPlaying(int handle)
+//
+// Retrieve the raw data lump index
+//  for a given SFX name.
+//
+
+static int I_PCS_GetSfxLumpNum(sfxinfo_t* sfx)
+{
+    char namebuf[9];
+
+    sprintf(namebuf, "dp%s", DEH_String(sfx->name));
+
+    return W_GetNumForName(namebuf);
+}
+
+
+static boolean I_PCS_SoundIsPlaying(int handle)
 {
     if (!pcs_initialised)
     {
@@ -229,10 +242,58 @@ int I_PCS_SoundIsPlaying(int handle)
     return current_sound_lump != NULL && current_sound_remaining > 0;
 }

-void I_PCS_InitSound(void)
+static boolean I_PCS_InitSound(void)
 {
+    // Use the sample rate from the configuration file
+
+    PCSound_SetSampleRate(snd_samplerate);
+
+    // Initialise the PC speaker subsystem.
+
     pcs_initialised = PCSound_Init(PCSCallbackFunc);

-    sound_lock = SDL_CreateMutex();
+    if (pcs_initialised)
+    {
+        sound_lock = SDL_CreateMutex();
+    }
+
+    return pcs_initialised;
+}
+
+static void I_PCS_ShutdownSound(void)
+{
+    if (pcs_initialised)
+    {
+        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,
+    sizeof(sound_pcsound_devices) / sizeof(*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,
+};
+
diff --git a/src/i_sdlmusic.c b/src/i_sdlmusic.c
new file mode 100644
index 00000000..d2471578
--- /dev/null
+++ b/src/i_sdlmusic.c
@@ -0,0 +1,317 @@
+// 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 <stdio.h>
+#include <stdlib.h>
+#include "SDL.h"
+#include "SDL_mixer.h"
+
+#include "doomdef.h"
+#include "memio.h"
+#include "mus2mid.h"
+
+#include "deh_main.h"
+#include "m_misc.h"
+#include "s_sound.h"
+#include "w_wad.h"
+#include "z_zone.h"
+
+#define MAXMIDLENGTH (96 * 1024)
+
+static boolean music_initialised = false;
+
+// If this is true, this module initialised SDL sound and has the
+// responsibility to shut it down
+
+static boolean sdl_was_initialised = false;
+
+static boolean musicpaused = false;
+static int current_music_volume;
+
+void I_ShutdownMusic(void)
+{
+    if (music_initialised)
+    {
+        Mix_HaltMusic();
+        music_initialised = false;
+
+        if (sdl_was_initialised)
+        {
+            Mix_CloseAudio();
+            SDL_QuitSubSystem(SDL_INIT_AUDIO);
+            sdl_was_initialised = false;
+        }
+    }
+}
+
+static boolean SDLIsInitialised(void)
+{
+    int freq, channels;
+    Uint16 format;
+
+    return Mix_QuerySpec(&freq, &format, &channels) != 0;
+}
+
+void I_InitMusic()
+{
+    // When trying to run with music enabled on OSX, display
+    // a warning message.
+
+#ifdef __MACOSX__
+    printf("\n"
+           "                   *** WARNING ***\n"
+           "      Music playback on OSX may cause crashes and\n"
+           "      is disabled by default.\n"
+           "\n");
+#endif
+
+    // If SDL_mixer is not initialised, we have to initialise it
+    // and have the responsibility to shut it down later on.
+
+    if (!SDLIsInitialised())
+    {
+        if (SDL_Init(SDL_INIT_AUDIO) < 0)
+        {
+            fprintf(stderr, "Unable to set up sound.\n");
+            return;
+        }
+
+        if (Mix_OpenAudio(snd_samplerate, AUDIO_S16SYS, 2, 1024) < 0)
+        {
+            fprintf(stderr, "Error initialising SDL_mixer: %s\n", Mix_GetError());
+            SDL_QuitSubSystem(SDL_INIT_AUDIO);
+            return;
+        }
+
+        SDL_PauseAudio(0);
+
+        sdl_was_initialised = true;
+    }
+
+    music_initialised = true;
+}
+
+//
+// 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);
+}
+
+// MUSIC API - dummy. Some code from DOS version.
+void I_SetMusicVolume(int volume)
+{
+    // Internal state variable.
+    current_music_volume = volume;
+
+    UpdateMusicVolume();
+}
+
+void I_PlaySong(void *handle, int looping)
+{
+    Mix_Music *music = (Mix_Music *) handle;
+    int loops;
+
+    if (!music_initialised)
+    {
+        return;
+    }
+
+    if (handle == NULL)
+    {
+        return;
+    }
+
+    if (looping)
+    {
+        loops = -1;
+    }
+    else
+    {
+        loops = 1;
+    }
+
+    Mix_PlayMusic(music, loops);
+}
+
+void I_PauseSong (void *handle)
+{
+    if (!music_initialised)
+    {
+        return;
+    }
+
+    musicpaused = true;
+
+    UpdateMusicVolume();
+}
+
+void I_ResumeSong (void *handle)
+{
+    if (!music_initialised)
+    {
+        return;
+    }
+
+    musicpaused = false;
+
+    UpdateMusicVolume();
+}
+
+void I_StopSong(void *handle)
+{
+    if (!music_initialised)
+    {
+        return;
+    }
+
+    Mix_HaltMusic();
+}
+
+void I_UnRegisterSong(void *handle)
+{
+    Mix_Music *music = (Mix_Music *) handle;
+
+    if (!music_initialised)
+    {
+        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;
+}
+
+void *I_RegisterSong(void *data, int len)
+{
+    char *filename;
+    Mix_Music *music;
+
+    if (!music_initialised)
+    {
+        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?
+boolean I_QrySongPlaying(void *handle)
+{
+    if (!music_initialised)
+    {
+        return false;
+    }
+
+    return Mix_PlayingMusic();
+}
+
+
+
diff --git a/src/i_sound.c b/src/i_sdlsound.c
similarity index 56%
rename from src/i_sound.c
rename to src/i_sdlsound.c
index c332c69f..920e74d2 100644
--- a/src/i_sound.c
+++ b/src/i_sdlsound.c
@@ -30,35 +30,17 @@
 #include "SDL.h"
 #include "SDL_mixer.h"

-#ifndef _WIN32
-#include <unistd.h>
-#endif
-
-#include "memio.h"
-#include "mus2mid.h"
-#include "z_zone.h"
-
-#include "i_system.h"
-#include "i_pcsound.h"
-#include "i_sound.h"
-#include "i_swap.h"
 #include "deh_main.h"
 #include "s_sound.h"
 #include "m_argv.h"
-#include "m_misc.h"
 #include "w_wad.h"
+#include "z_zone.h"

 #include "doomdef.h"

 #define NUM_CHANNELS 16

-#define MAXMIDLENGTH (96 * 1024)
-
-static boolean nosfxparm;
-static boolean nomusicparm;
-
 static boolean sound_initialised = false;
-static boolean music_initialised = false;

 static Mix_Chunk sound_chunks[NUMSFX];
 static int channels_playing[NUM_CHANNELS];
@@ -67,19 +49,19 @@ static int mixer_freq;
 static Uint16 mixer_format;
 static int mixer_channels;

-int snd_samplerate = MIX_DEFAULT_FREQUENCY;
-
 // When a sound stops, check if it is still playing.  If it is not,
 // we can mark the sound data as CACHE to be freed back for other
 // means.

-void ReleaseSoundOnChannel(int channel)
+static void ReleaseSoundOnChannel(int channel)
 {
     int i;
     int id = channels_playing[channel];

     if (!id)
+    {
         return;
+    }

     channels_playing[channel] = sfx_None;

@@ -265,27 +247,31 @@ static Mix_Chunk *GetSFXChunk(int sound_id)
 //  for a given SFX name.
 //

-int I_GetSfxLumpNum(sfxinfo_t* sfx)
+static int I_SDL_GetSfxLumpNum(sfxinfo_t* sfx)
 {
     char namebuf[9];
-    char *prefix;

-    // Different prefix for PC speaker sound effects.
+    sprintf(namebuf, "ds%s", DEH_String(sfx->name));
+
+    return W_GetNumForName(namebuf);
+}

-    if (snd_sfxdevice == SNDDEVICE_PCSPEAKER)
-    {
-        prefix = "dp";
-    }
-    else
+static void I_SDL_UpdateSoundParams(int handle, int vol, int sep)
+{
+    int left, right;
+
+    if (!sound_initialised)
     {
-        prefix = "ds";
+        return;
     }

-    sprintf(namebuf, "%s%s", prefix, DEH_String(sfx->name));
-
-    return W_GetNumForName(namebuf);
+    left = ((254 - sep) * vol) / 127;
+    right = ((sep) * vol) / 127;
+
+    Mix_SetPanning(handle, left, right);
 }

+
 //
 // Starting a sound means adding it
 //  to the current list of active sounds
@@ -299,23 +285,13 @@ int I_GetSfxLumpNum(sfxinfo_t* sfx)
 //  is set, but currently not used by mixing.
 //

-int
-I_StartSound
-( int		id,
-  int           channel,
-  int		vol,
-  int		sep,
-  int		pitch,
-  int		priority )
+static int I_SDL_StartSound(int id, int channel, int vol, int sep)
 {
     Mix_Chunk *chunk;

     if (!sound_initialised)
-        return 0;
-
-    if (snd_sfxdevice == SNDDEVICE_PCSPEAKER)
     {
-        return I_PCS_StartSound(id, channel, vol, sep, pitch, priority);
+        return -1;
     }

     // Release a sound effect if there is already one playing
@@ -340,22 +316,18 @@ I_StartSound

     // set separation, etc.

-    I_UpdateSoundParams(channel, vol, sep, pitch);
+    I_SDL_UpdateSoundParams(channel, vol, sep);

     return channel;
 }

-void I_StopSound (int handle)
+static void I_SDL_StopSound (int handle)
 {
     if (!sound_initialised)
-        return;
-
-    if (snd_sfxdevice == SNDDEVICE_PCSPEAKER)
     {
-        I_PCS_StopSound(handle);
         return;
     }
-
+
     Mix_HaltChannel(handle);

     // Sound data is no longer needed; release the
@@ -365,48 +337,29 @@ void I_StopSound (int handle)
 }


-int I_SoundIsPlaying(int handle)
+static boolean I_SDL_SoundIsPlaying(int handle)
 {
-    if (!sound_initialised)
-        return false;
-
     if (handle < 0)
-        return false;
-
-    if (snd_sfxdevice == SNDDEVICE_PCSPEAKER)
-    {
-        return I_PCS_SoundIsPlaying(handle);
-    }
-    else
     {
-        return Mix_Playing(handle);
+        return false;
     }
-}
-
-

+    return Mix_Playing(handle);
+}

 //
 // Periodically called to update the sound system
 //

-void I_UpdateSound( void )
+static void I_SDL_UpdateSound(void)
 {
     int i;

-    if (!sound_initialised)
-        return;
-
-    if (snd_sfxdevice == SNDDEVICE_PCSPEAKER)
-    {
-        return;
-    }
-
     // Check all channels to see if a sound has finished

     for (i=0; i<NUM_CHANNELS; ++i)
     {
-        if (channels_playing[i] && !I_SoundIsPlaying(i))
+        if (channels_playing[i] && !I_SDL_SoundIsPlaying(i))
         {
             // Sound has finished playing on this channel,
             // but sound data has not been released to cache
@@ -416,65 +369,20 @@ void I_UpdateSound( void )
     }
 }

-
-//
-// This would be used to write out the mixbuffer
-//  during each game loop update.
-// Updates sound buffer and audio device at runtime.
-// It is called during Timer interrupt with SNDINTR.
-// Mixing now done synchronous, and
-//  only output be done asynchronous?
-//
-void
-I_SubmitSound(void)
-{
-}
-
-
-
-void
-I_UpdateSoundParams
-( int	handle,
-  int	vol,
-  int	sep,
-  int	pitch)
-{
-    int left, right;
-
+static void I_SDL_ShutdownSound(void)
+{
     if (!sound_initialised)
-        return;
-
-    if (snd_sfxdevice == SNDDEVICE_PCSPEAKER)
     {
         return;
     }

-    left = ((254 - sep) * vol) / 127;
-    right = ((sep) * vol) / 127;
-
-    Mix_SetPanning(handle, left, right);
-}
-
-
-
-
-void I_ShutdownSound(void)
-{
-    if (!sound_initialised && !music_initialised)
-        return;
-
-    Mix_HaltMusic();
     Mix_CloseAudio();
     SDL_QuitSubSystem(SDL_INIT_AUDIO);

     sound_initialised = false;
-    music_initialised = false;
 }

-
-
-void
-I_InitSound()
+static boolean I_SDL_InitSound()
 {
     int i;

@@ -490,75 +398,16 @@ I_InitSound()
         channels_playing[i] = sfx_None;
     }

-    //!
-    // Disable music playback.
-    //
-
-    nomusicparm = M_CheckParm("-nomusic") > 0;
-
-    if (snd_musicdevice < SNDDEVICE_ADLIB)
-    {
-        nomusicparm = true;
-    }
-
-    //!
-    // Disable sound effects.
-    //
-
-    nosfxparm = M_CheckParm("-nosfx") > 0;
-
-    // If the SFX device is 0 (none), then disable sound effects,
-    // just like if we specified -nosfx.  However, we still continue
-    // with initialising digital sound output even if we are using
-    // the PC speaker, because we might be using the SDL PC speaker
-    // emulation.
-
-    if (snd_sfxdevice == SNDDEVICE_NONE)
-    {
-        nosfxparm = true;
-    }
-
-    //!
-    // Disable sound effects and music.
-    //
-
-    if (M_CheckParm("-nosound") > 0)
-    {
-        nosfxparm = true;
-        nomusicparm = true;
-    }
-
-    // When trying to run with music enabled on OSX, display
-    // a warning message.
-
-#ifdef __MACOSX__
-    if (!nomusicparm)
-    {
-        printf("\n"
-               "                   *** WARNING ***\n"
-               "      Music playback on OSX may cause crashes and\n"
-               "      is disabled by default.\n"
-               "\n");
-    }
-#endif
-
-    // If music or sound is going to play, we need to at least
-    // initialise SDL
-    // No sound in screensaver mode.
-
-    if (screensaver_mode || (nomusicparm && nosfxparm))
-        return;
-
     if (SDL_Init(SDL_INIT_AUDIO) < 0)
     {
         fprintf(stderr, "Unable to set up sound.\n");
-        return;
+        return false;
     }

     if (Mix_OpenAudio(snd_samplerate, AUDIO_S16SYS, 2, 1024) < 0)
     {
         fprintf(stderr, "Error initialising SDL_mixer: %s\n", Mix_GetError());
-        return;
+        return false;
     }

     Mix_QuerySpec(&mixer_freq, &mixer_format, &mixer_channels);
@@ -567,201 +416,32 @@ I_InitSound()

     SDL_PauseAudio(0);

-    // If we are using the PC speaker, we now need to initialise it.
-
-    if (snd_sfxdevice == SNDDEVICE_PCSPEAKER)
-    {
-        I_PCS_InitSound();
-    }
-
-    if (!nomusicparm)
-        music_initialised = true;
-
-    if (!nosfxparm)
-        sound_initialised = true;
-}
-
-
-
-
-//
-// MUSIC API.
-//
-
-static boolean  musicpaused = false;
-static int currentMusicVolume;
-
-//
-// 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 = (currentMusicVolume * MIX_MAX_VOLUME) / 127;
-
-    Mix_VolumeMusic(vol);
-}
-
-// MUSIC API - dummy. Some code from DOS version.
-void I_SetMusicVolume(int volume)
-{
-    // Internal state variable.
-    currentMusicVolume = volume;
-
-    UpdateMusicVolume();
-}
-
-void I_PlaySong(void *handle, int looping)
-{
-    Mix_Music *music = (Mix_Music *) handle;
-    int loops;
-
-    if (!music_initialised)
-        return;
-
-    if (handle == NULL)
-        return;
-
-    if (looping)
-        loops = -1;
-    else
-        loops = 1;
-
-    Mix_PlayMusic(music, loops);
-}
-
-void I_PauseSong (void *handle)
-{
-    if (!music_initialised)
-        return;
+    sound_initialised = true;

-    musicpaused = true;
-
-    UpdateMusicVolume();
-}
-
-void I_ResumeSong (void *handle)
-{
-    if (!music_initialised)
-        return;
-
-    musicpaused = false;
-
-    UpdateMusicVolume();
-}
-
-void I_StopSong(void *handle)
-{
-    if (!music_initialised)
-        return;
-
-    Mix_HaltMusic();
-}
-
-void I_UnRegisterSong(void *handle)
-{
-    Mix_Music *music = (Mix_Music *) handle;
-
-    if (!music_initialised)
-        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);
+    return true;
 }

-static boolean ConvertMus(byte *musdata, int len, char *filename)
+static snddevice_t sound_sdl_devices[] =
 {
-    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;
-}
-
-void *I_RegisterSong(void *data, int len)
+    SNDDEVICE_SB,
+    SNDDEVICE_PAS,
+    SNDDEVICE_GUS,
+    SNDDEVICE_WAVEBLASTER,
+    SNDDEVICE_SOUNDCANVAS,
+    SNDDEVICE_AWE32,
+};
+
+sound_module_t sound_sdl_module =
 {
-    char *filename;
-    Mix_Music *music;
-
-    if (!music_initialised)
-        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?
-boolean I_QrySongPlaying(void *handle)
-{
-    if (!music_initialised)
-        return false;
-
-    return Mix_PlayingMusic();
-}
-
-
+    sound_sdl_devices,
+    sizeof(sound_sdl_devices) / sizeof(*sound_sdl_devices),
+    I_SDL_InitSound,
+    I_SDL_ShutdownSound,
+    I_SDL_GetSfxLumpNum,
+    I_SDL_UpdateSound,
+    I_SDL_UpdateSoundParams,
+    I_SDL_StartSound,
+    I_SDL_StopSound,
+    I_SDL_SoundIsPlaying,
+};

diff --git a/src/s_sound.c b/src/s_sound.c
index 1477415e..52462258 100644
--- a/src/s_sound.c
+++ b/src/s_sound.c
@@ -26,20 +26,24 @@
 #include <stdio.h>
 #include <stdlib.h>

+#include "i_music.h"
 #include "i_system.h"
-#include "i_sound.h"
+
+#include "doomfeatures.h"
+#include "deh_main.h"
+
+#include "doomstat.h"
+#include "doomdef.h"
+
 #include "sounds.h"
 #include "s_sound.h"

-#include "deh_main.h"
-#include "z_zone.h"
 #include "m_random.h"
-#include "w_wad.h"
+#include "m_argv.h"

-#include "doomdef.h"
 #include "p_local.h"
-
-#include "doomstat.h"
+#include "w_wad.h"
+#include "z_zone.h"

 // when to clip out sounds
 // Does not fit the large outdoor areas.
@@ -74,10 +78,6 @@
 #define DEFAULT_MUSIC_DEVICE SNDDEVICE_NONE
 #endif

-int snd_musicdevice = DEFAULT_MUSIC_DEVICE;
-int snd_sfxdevice = SNDDEVICE_SB;
-
-
 typedef struct
 {
     // sound information (if null, channel avail.)
@@ -91,6 +91,9 @@ typedef struct

 } channel_t;

+// Low-level sound module we are using
+
+static sound_module_t *sound_module;

 // The set of channels available

@@ -105,6 +108,10 @@ int sfxVolume = 8;

 int musicVolume = 8;

+// Sound sample rate to use for digital output (Hz)
+
+int snd_samplerate = 22050;
+
 // Internal volume level, ranging from 0-127

 static int snd_SfxVolume;
@@ -121,6 +128,81 @@ static musicinfo_t *mus_playing = NULL;

 int numChannels = 8;

+int snd_musicdevice = DEFAULT_MUSIC_DEVICE;
+int snd_sfxdevice = SNDDEVICE_SB;
+
+// Sound effect modules
+
+extern sound_module_t sound_sdl_module;
+extern sound_module_t sound_pcsound_module;
+
+// Compiled-in sound modules:
+
+static sound_module_t *sound_modules[] =
+{
+#ifdef FEATURE_SOUND
+    &sound_sdl_module,
+    &sound_pcsound_module,
+#endif
+};
+
+// Check if a sound device is in the given list of devices
+
+static boolean SndDeviceInList(snddevice_t device, snddevice_t *list,
+                               int len)
+{
+    int i;
+
+    for (i=0; i<len; ++i)
+    {
+        if (device == list[i])
+        {
+            return true;
+        }
+    }
+
+    return false;
+}
+
+// Find and initialise a sound_module_t appropriate for the setting
+// in snd_sfxdevice.
+
+static void InitSfxModule(void)
+{
+    int i;
+
+    sound_module = NULL;
+
+    for (i=0; i<sizeof(sound_modules) / sizeof(*sound_modules); ++i)
+    {
+        // Is the sfx device in the list of devices supported by
+        // this module?
+
+        if (SndDeviceInList(snd_sfxdevice,
+                            sound_modules[i]->sound_devices,
+                            sound_modules[i]->num_sound_devices))
+        {
+            // Initialise the module
+
+            if (sound_modules[i]->Init())
+            {
+                sound_module = sound_modules[i];
+                return;
+            }
+        }
+    }
+}
+
+// Initialise music according to snd_musicdevice.
+
+static void InitMusicModule(void)
+{
+    if (snd_musicdevice >= SNDDEVICE_ADLIB)
+    {
+        I_InitMusic();
+    }
+}
+
 //
 // Initializes sound stuff, including volume
 // Sets channels, SFX and music volume,
@@ -131,7 +213,20 @@ void S_Init(int sfxVolume, int musicVolume)
 {
     int i;

-    I_InitSound();
+    // Initialise the sound and music subsystems.
+
+    if (M_CheckParm("-nosound") <= 0 && !screensaver_mode)
+    {
+        if (M_CheckParm("-nosfx") <= 0)
+        {
+            InitSfxModule();
+        }
+
+        if (M_CheckParm("-nomusic") <= 0)
+        {
+            InitMusicModule();
+        }
+    }

     S_SetSfxVolume(sfxVolume);
     S_SetMusicVolume(musicVolume);
@@ -159,7 +254,12 @@ void S_Init(int sfxVolume, int musicVolume)

 void S_Shutdown(void)
 {
-    I_ShutdownSound();
+    if (sound_module != NULL)
+    {
+        sound_module->Shutdown();
+    }
+
+    I_ShutdownMusic();
 }

 static void S_StopChannel(int cnum)
@@ -172,9 +272,13 @@ static void S_StopChannel(int cnum)
     if (c->sfxinfo)
     {
         // stop the sound playing
-        if (I_SoundIsPlaying(c->handle))
+
+        if (sound_module != NULL)
         {
-            I_StopSound(c->handle);
+            if (sound_module->SoundIsPlaying(c->handle))
+            {
+                sound_module->StopSound(c->handle);
+            }
         }

         // check to see if other channels are playing the sound
@@ -228,15 +332,15 @@ void S_Start(void)
         {
             // Song - Who? - Where?

-            mus_e3m4,        // American        e4m1
-            mus_e3m2,        // Romero        e4m2
+            mus_e3m4,        // American     e4m1
+            mus_e3m2,        // Romero       e4m2
             mus_e3m3,        // Shawn        e4m3
-            mus_e1m5,        // American        e4m4
-            mus_e2m7,        // Tim         e4m5
-            mus_e2m4,        // Romero        e4m6
-            mus_e2m6,        // J.Anderson        e4m7 CHIRON.WAD
+            mus_e1m5,        // American     e4m4
+            mus_e2m7,        // Tim          e4m5
+            mus_e2m4,        // Romero       e4m6
+            mus_e2m6,        // J.Anderson   e4m7 CHIRON.WAD
             mus_e2m5,        // Shawn        e4m8
-            mus_e1m9        // Tim                e4m9
+            mus_e1m9,        // Tim          e4m9
         };

         if (gameepisode < 4)
@@ -326,14 +430,14 @@ static int S_GetChannel(mobj_t *origin, sfxinfo_t *sfxinfo)
 }

 //
-// Changes volume, stereo-separation, and pitch variables
+// Changes volume and stereo-separation variables
 //  from the norm of a sound effect to be played.
 // If the sound is not audible, returns a 0.
 // Otherwise, modifies parameters and returns 1.
 //

 static int S_AdjustSoundParams(mobj_t *listener, mobj_t *source,
-                               int *vol, int *sep, int *pitch)
+                               int *vol, int *sep)
 {
     fixed_t        approx_dist;
     fixed_t        adx;
@@ -406,7 +510,6 @@ void S_StartSound(void *origin_p, int sfx_id)
     mobj_t *origin;
     int rc;
     int sep;
-    int pitch;
     int priority;
     int cnum;
     int volume;
@@ -425,7 +528,6 @@ void S_StartSound(void *origin_p, int sfx_id)
     // Initialize sound parameters
     if (sfx->link)
     {
-        pitch = sfx->pitch;
         priority = sfx->priority;
         volume += sfx->volume;

@@ -441,7 +543,6 @@ void S_StartSound(void *origin_p, int sfx_id)
     }
     else
     {
-        pitch = NORM_PITCH;
         priority = NORM_PRIORITY;
     }

@@ -453,8 +554,7 @@ void S_StartSound(void *origin_p, int sfx_id)
         rc = S_AdjustSoundParams(players[consoleplayer].mo,
                                  origin,
                                  &volume,
-                                 &sep,
-                                 &pitch);
+                                 &sep);

         if (origin->x == players[consoleplayer].mo->x
          && origin->y == players[consoleplayer].mo->y)
@@ -472,36 +572,6 @@ void S_StartSound(void *origin_p, int sfx_id)
         sep = NORM_SEP;
     }

-    // hacks to vary the sfx pitches
-    if (sfx_id >= sfx_sawup
-     && sfx_id <= sfx_sawhit)
-    {
-        pitch += 8 - (M_Random()&15);
-
-        if (pitch < 0)
-        {
-            pitch = 0;
-        }
-        else if (pitch > 255)
-        {
-            pitch = 255;
-        }
-    }
-    else if (sfx_id != sfx_itemup
-          && sfx_id != sfx_tink)
-    {
-        pitch += 16 - (M_Random()&31);
-
-        if (pitch < 0)
-        {
-            pitch = 0;
-        }
-        else if (pitch > 255)
-        {
-            pitch = 255;
-        }
-    }
-
     // kill old sound
     S_StopSound(origin);

@@ -513,32 +583,29 @@ void S_StartSound(void *origin_p, int sfx_id)
         return;
     }

-    //
-    // This is supposed to handle the loading/caching.
-    // For some odd reason, the caching is done nearly
-    //  each time the sound is needed?
-    //
-
-    // get lumpnum if necessary
-    if (sfx->lumpnum < 0)
-    {
-        sfx->lumpnum = I_GetSfxLumpNum(sfx);
-    }
-
     // increase the usefulness
     if (sfx->usefulness++ < 0)
     {
         sfx->usefulness = 1;
     }

-    // Assigns the handle to one of the channels in the
-    //  mix/output buffer.
-    channels[cnum].handle = I_StartSound(sfx_id,
-                                         cnum,
-                                         volume,
-                                         sep,
-                                         pitch,
-                                         priority);
+    if (sound_module != NULL)
+    {
+        // Get lumpnum if necessary
+
+        if (sfx->lumpnum < 0)
+        {
+            sfx->lumpnum = sound_module->GetSfxLumpNum(sfx);
+        }
+
+        // Assigns the handle to one of the channels in the
+        //  mix/output buffer.
+
+        channels[cnum].handle = sound_module->StartSound(sfx_id,
+                                                         cnum,
+                                                         volume,
+                                                         sep);
+    }
 }

 //
@@ -573,7 +640,6 @@ void S_UpdateSounds(mobj_t *listener)
     int                cnum;
     int                volume;
     int                sep;
-    int                pitch;
     sfxinfo_t*        sfx;
     channel_t*        c;

@@ -584,16 +650,14 @@ void S_UpdateSounds(mobj_t *listener)

         if (c->sfxinfo)
         {
-            if (I_SoundIsPlaying(c->handle))
+            if (sound_module != NULL && sound_module->SoundIsPlaying(c->handle))
             {
                 // initialize parameters
                 volume = snd_SfxVolume;
-                pitch = NORM_PITCH;
                 sep = NORM_SEP;

                 if (sfx->link)
                 {
-                    pitch = sfx->pitch;
                     volume += sfx->volume;
                     if (volume < 1)
                     {
@@ -613,8 +677,7 @@ void S_UpdateSounds(mobj_t *listener)
                     audible = S_AdjustSoundParams(listener,
                                                   c->origin,
                                                   &volume,
-                                                  &sep,
-                                                  &pitch);
+                                                  &sep);

                     if (!audible)
                     {
@@ -622,7 +685,7 @@ void S_UpdateSounds(mobj_t *listener)
                     }
                     else
                     {
-                        I_UpdateSoundParams(c->handle, volume, sep, pitch);
+                        sound_module->UpdateSoundParams(c->handle, volume, sep);
                     }
                 }
             }
diff --git a/src/s_sound.h b/src/s_sound.h
index c5460aed..8a5e2c6c 100644
--- a/src/s_sound.h
+++ b/src/s_sound.h
@@ -28,6 +28,8 @@
 #ifndef __S_SOUND__
 #define __S_SOUND__

+#include "p_mobj.h"
+#include "sounds.h"

 typedef enum
 {
@@ -43,8 +45,50 @@ typedef enum
     SNDDEVICE_AWE32 = 9,
 } snddevice_t;

+typedef struct
+{
+    snddevice_t *sound_devices;
+    int num_sound_devices;
+
+    // Initialise sound module
+    // Returns true if successfully initialised
+
+    boolean (*Init)(void);
+
+    // Shutdown sound module
+
+    void (*Shutdown)(void);
+
+    // Returns the lump index of the given sound.
+
+    int (*GetSfxLumpNum)(sfxinfo_t *sfxinfo);
+
+    // Called periodically to update the subsystem.
+
+    void (*Update)(void);
+
+    // Update the sound settings on the given channel.
+
+    void (*UpdateSoundParams)(int channel, int vol, int sep);
+
+    // Start a sound on a given channel.  Returns the channel id
+    // or -1 on failure.
+
+    int (*StartSound)(int id, int channel, int vol, int sep);
+
+    // Stop the sound playing on the given channel.
+
+    void (*StopSound)(int channel);
+
+    // Query if a sound is playing on the given channel
+
+    boolean (*SoundIsPlaying)(int channel);
+
+} sound_module_t;
+
 extern int snd_sfxdevice;
 extern int snd_musicdevice;
+extern int snd_samplerate;

 //
 // Initializes sound stuff, including volume