shithub: choc

Download patch

ref: beab4eb58b667a5883166bd1dd7bc33369a005c7
parent: 5fc7913741e9f722e31735baea10f635fc18acce
author: Simon Howard <[email protected]>
date: Sun Jun 17 14:40:02 EDT 2007

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

--- 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;
@@ -53,6 +54,13 @@
 };
 
 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)
 {
--- 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 */
 
--- /dev/null
+++ b/pcsound/pcsound_internal.h
@@ -1,0 +1,47 @@
+// Emacs style mode select   -*- C++ -*- 
+//-----------------------------------------------------------------------------
+//
+// Copyright(C) 2007 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:
+//    PC speaker interface.
+//
+//-----------------------------------------------------------------------------
+
+#ifndef PCSOUND_INTERNAL_H
+#define PCSOUND_INTERNAL_H
+
+#include "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 */
+
--- 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"
 
--- 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 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 @@
         fprintf(stderr, 
                 "PCSound_SDL only supports native signed 16-bit LSB, "
                 "stereo format!\n");
+
+        PCSound_SDL_Shutdown();
         return 0;
     }
 
@@ -164,10 +215,6 @@
     Mix_SetPostMix(PCSound_Mix_Callback, NULL);
 
     return 1;
-}
-
-static void PCSound_SDL_Shutdown(void)
-{
 }
 
 pcsound_driver_t pcsound_sdl_driver = 
--- 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;
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -95,6 +95,7 @@
 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 @@
 
 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)    \
--- 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"
 
--- a/src/deh_sound.c
+++ b/src/deh_sound.c
@@ -68,15 +68,7 @@
                              "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)
--- /dev/null
+++ b/src/i_music.h
@@ -1,0 +1,76 @@
+// 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, music.
+//
+//-----------------------------------------------------------------------------
+
+#ifndef __I_SOUND__
+#define __I_SOUND__
+
+#include "doomdef.h"
+
+#include "doomstat.h"
+#include "sounds.h"
+#include "s_sound.h"
+
+//
+//  MUSIC I/O
+//
+
+void I_InitMusic(void);
+void I_ShutdownMusic(void);
+
+// Volume.
+
+void I_SetMusicVolume(int volume);
+
+// PAUSE game handling.
+
+void I_PauseSong(void *handle);
+void I_ResumeSong(void *handle);
+
+// Registers a song handle to song data.
+
+void *I_RegisterSong(void *data, int length);
+
+// Called by anything that wishes to start music.
+//  plays a song, and when the song is done,
+//  starts playing it again in an endless loop.
+// Horrible thing to do, considering.
+
+void I_PlaySong(void *handle, int looping);
+
+// Stops a song over 3 seconds.
+
+void I_StopSong(void *handle);
+
+// See above (register), then think backwards
+
+void I_UnRegisterSong(void *handle);
+
+boolean I_QrySongPlaying(void *handle);
+
+
+#endif
+
--- 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 @@
 
 #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 @@
     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 @@
     }
 }
 
-void I_PCS_StopSound(int handle)
+static void I_PCS_StopSound(int handle)
 {
     if (!pcs_initialised)
     {
@@ -214,8 +212,23 @@
     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)
     {
         return false;
@@ -229,10 +242,58 @@
     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,
+};
 
--- a/src/i_pcsound.h
+++ /dev/null
@@ -1,40 +1,0 @@
-// Emacs style mode select   -*- C++ -*- 
-//-----------------------------------------------------------------------------
-//
-// Copyright(C) 2007 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 PC speaker sound.
-//
-//-----------------------------------------------------------------------------
-
-#ifndef __I_PCSOUND_H__
-#define __I_PCSOUND_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);
-
-#endif /* #ifndef __I_PCSOUND_H__ */
-
--- /dev/null
+++ b/src/i_sdlmusic.c
@@ -1,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();
+}
+
+
+
--- /dev/null
+++ b/src/i_sdlsound.c
@@ -1,0 +1,447 @@
+// 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 sound.
+//
+//-----------------------------------------------------------------------------
+
+
+#include <stdio.h>
+#include <stdlib.h>
+#include "SDL.h"
+#include "SDL_mixer.h"
+
+#include "deh_main.h"
+#include "s_sound.h"
+#include "m_argv.h"
+#include "w_wad.h"
+#include "z_zone.h"
+
+#include "doomdef.h"
+
+#define NUM_CHANNELS 16
+
+static boolean sound_initialised = false;
+
+static Mix_Chunk sound_chunks[NUMSFX];
+static int channels_playing[NUM_CHANNELS];
+
+static int mixer_freq;
+static Uint16 mixer_format;
+static int mixer_channels;
+
+// 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.
+
+static void ReleaseSoundOnChannel(int channel)
+{
+    int i;
+    int id = channels_playing[channel];
+
+    if (!id)
+    {
+        return;
+    }
+
+    channels_playing[channel] = sfx_None;
+    
+    for (i=0; i<NUM_CHANNELS; ++i)
+    {
+        // Playing on this channel? if so, don't release.
+
+        if (channels_playing[i] == id)
+            return;
+    }
+
+    // Not used on any channel, and can be safely released
+    
+    Z_ChangeTag(sound_chunks[id].abuf, PU_CACHE);
+}
+
+static boolean ConvertibleRatio(int freq1, int freq2)
+{
+    int ratio;
+
+    if (freq1 > freq2)
+    {
+        return ConvertibleRatio(freq2, freq1);
+    }
+    else if ((freq2 % freq1) != 0)
+    {
+        // Not in a direct ratio
+
+        return false;
+    }
+    else
+    {
+        // Check the ratio is a power of 2
+
+        ratio = freq2 / freq1;
+
+        while ((ratio & 1) == 0)
+        {
+            ratio = ratio >> 1;
+        }
+
+        return ratio == 1;
+    }
+}
+
+// Generic sound expansion function for any sample rate
+
+static void ExpandSoundData(byte *data,
+                            int samplerate,
+                            int length,
+                            Mix_Chunk *destination)
+{
+    SDL_AudioCVT convertor;
+    
+    if (samplerate <= mixer_freq
+     && ConvertibleRatio(samplerate, mixer_freq)
+     && SDL_BuildAudioCVT(&convertor,
+                          AUDIO_U8, 1, samplerate,
+                          mixer_format, mixer_channels, mixer_freq))
+    {
+        convertor.buf = destination->abuf;
+        convertor.len = length;
+        memcpy(convertor.buf, data, length);
+
+        SDL_ConvertAudio(&convertor);
+    }
+    else
+    {
+        Sint16 *expanded = (Sint16 *) destination->abuf;
+        int expanded_length;
+        int expand_ratio;
+        int i;
+
+        // Generic expansion if conversion does not work:
+        //
+        // SDL's audio conversion only works for rate conversions that are
+        // powers of 2; if the two formats are not in a direct power of 2
+        // ratio, do this naive conversion instead.
+
+        // number of samples in the converted sound
+
+        expanded_length = (length * mixer_freq) / samplerate;
+        expand_ratio = (length << 8) / expanded_length;
+
+        for (i=0; i<expanded_length; ++i)
+        {
+            Sint16 sample;
+            int src;
+
+            src = (i * expand_ratio) >> 8;
+
+            sample = data[src] | (data[src] << 8);
+            sample -= 32768;
+
+            // expand 8->16 bits, mono->stereo
+
+            expanded[i * 2] = expanded[i * 2 + 1] = sample;
+        }
+    }
+}
+
+// Load and convert a sound effect
+// Returns true if successful
+
+static boolean CacheSFX(int sound)
+{
+    int lumpnum;
+    unsigned int lumplen;
+    int samplerate;
+    unsigned int length;
+    unsigned int expanded_length;
+    byte *data;
+
+    // need to load the sound
+
+    lumpnum = S_sfx[sound].lumpnum;
+    data = W_CacheLumpNum(lumpnum, PU_STATIC);
+    lumplen = W_LumpLength(lumpnum);
+
+    // Check the header, and ensure this is a valid sound
+
+    if (lumplen < 8
+     || data[0] != 0x03 || data[1] != 0x00)
+    {
+        // Invalid sound
+
+        return false;
+    }
+
+    // 16 bit sample rate field, 32 bit length field
+
+    samplerate = (data[3] << 8) | data[2];
+    length = (data[7] << 24) | (data[6] << 16) | (data[5] << 8) | data[4];
+
+    // If the header specifies that the length of the sound is greater than
+    // the length of the lump itself, this is an invalid sound lump
+
+    if (length - 8 > lumplen)
+    {
+        return false;
+    }
+
+    expanded_length = (uint32_t) ((((uint64_t) length) * 4 * mixer_freq) / samplerate);
+
+    sound_chunks[sound].allocated = 1;
+    sound_chunks[sound].alen = expanded_length;
+    sound_chunks[sound].abuf 
+        = Z_Malloc(expanded_length, PU_STATIC, &sound_chunks[sound].abuf);
+    sound_chunks[sound].volume = MIX_MAX_VOLUME;
+
+    ExpandSoundData(data + 8, 
+                    samplerate, 
+                    length - 8, 
+                    &sound_chunks[sound]);
+
+    // don't need the original lump any more
+  
+    Z_ChangeTag(data, PU_CACHE);
+
+    return true;
+}
+
+static Mix_Chunk *GetSFXChunk(int sound_id)
+{
+    if (sound_chunks[sound_id].abuf == NULL)
+    {
+        if (!CacheSFX(sound_id))
+            return NULL;
+    }
+    else
+    {
+        // don't free the sound while it is playing!
+   
+        Z_ChangeTag(sound_chunks[sound_id].abuf, PU_STATIC);
+    }
+
+    return &sound_chunks[sound_id];
+}
+
+
+//
+// Retrieve the raw data lump index
+//  for a given SFX name.
+//
+
+static int I_SDL_GetSfxLumpNum(sfxinfo_t* sfx)
+{
+    char namebuf[9];
+
+    sprintf(namebuf, "ds%s", DEH_String(sfx->name));
+    
+    return W_GetNumForName(namebuf);
+}
+
+static void I_SDL_UpdateSoundParams(int handle, int vol, int sep)
+{
+    int left, right;
+
+    if (!sound_initialised)
+    {
+        return;
+    }
+
+    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
+//  in the internal channels.
+// As the SFX info struct contains
+//  e.g. a pointer to the raw data,
+//  it is ignored.
+// As our sound handling does not handle
+//  priority, it is ignored.
+// Pitching (that is, increased speed of playback)
+//  is set, but currently not used by mixing.
+//
+
+static int I_SDL_StartSound(int id, int channel, int vol, int sep)
+{
+    Mix_Chunk *chunk;
+
+    if (!sound_initialised)
+    {
+        return -1;
+    }
+
+    // Release a sound effect if there is already one playing
+    // on this channel
+
+    ReleaseSoundOnChannel(channel);
+
+    // Get the sound data
+
+    chunk = GetSFXChunk(id);
+
+    if (chunk == NULL)
+    {
+        return -1;
+    }
+
+    // play sound
+
+    Mix_PlayChannelTimed(channel, chunk, 0, -1);
+
+    channels_playing[channel] = id;
+
+    // set separation, etc.
+ 
+    I_SDL_UpdateSoundParams(channel, vol, sep);
+
+    return channel;
+}
+
+static void I_SDL_StopSound (int handle)
+{
+    if (!sound_initialised)
+    {
+        return;
+    }
+
+    Mix_HaltChannel(handle);
+
+    // Sound data is no longer needed; release the
+    // sound data being used for this channel
+
+    ReleaseSoundOnChannel(handle);
+}
+
+
+static boolean I_SDL_SoundIsPlaying(int handle)
+{
+    if (handle < 0)
+    {
+        return false;
+    }
+
+    return Mix_Playing(handle);
+}
+
+// 
+// Periodically called to update the sound system
+//
+
+static void I_SDL_UpdateSound(void)
+{
+    int i;
+
+    // Check all channels to see if a sound has finished
+
+    for (i=0; i<NUM_CHANNELS; ++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
+            
+            ReleaseSoundOnChannel(i);
+        }
+    }
+}
+
+static void I_SDL_ShutdownSound(void)
+{    
+    if (!sound_initialised)
+    {
+        return;
+    }
+
+    Mix_CloseAudio();
+    SDL_QuitSubSystem(SDL_INIT_AUDIO);
+
+    sound_initialised = false;
+}
+
+static boolean I_SDL_InitSound()
+{ 
+    int i;
+    
+    // No sounds yet
+
+    for (i=0; i<NUMSFX; ++i)
+    {
+        sound_chunks[i].abuf = NULL;
+    }
+
+    for (i=0; i<NUM_CHANNELS; ++i)
+    {
+        channels_playing[i] = sfx_None;
+    }
+
+    if (SDL_Init(SDL_INIT_AUDIO) < 0)
+    {
+        fprintf(stderr, "Unable to set up sound.\n");
+        return false;
+    }
+
+    if (Mix_OpenAudio(snd_samplerate, AUDIO_S16SYS, 2, 1024) < 0)
+    {
+        fprintf(stderr, "Error initialising SDL_mixer: %s\n", Mix_GetError());
+        return false;
+    }
+
+    Mix_QuerySpec(&mixer_freq, &mixer_format, &mixer_channels);
+
+    Mix_AllocateChannels(NUM_CHANNELS);
+    
+    SDL_PauseAudio(0);
+
+    sound_initialised = true;
+
+    return true;
+}
+
+static snddevice_t sound_sdl_devices[] = 
+{
+    SNDDEVICE_SB,
+    SNDDEVICE_PAS,
+    SNDDEVICE_GUS,
+    SNDDEVICE_WAVEBLASTER,
+    SNDDEVICE_SOUNDCANVAS,
+    SNDDEVICE_AWE32,
+};
+
+sound_module_t sound_sdl_module = 
+{
+    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,
+};
+
--- a/src/i_sound.c
+++ /dev/null
@@ -1,767 +1,0 @@
-// 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 sound.
-//
-//-----------------------------------------------------------------------------
-
-
-#include <stdio.h>
-#include <stdlib.h>
-#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 "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];
-
-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)
-{
-    int i;
-    int id = channels_playing[channel];
-
-    if (!id)
-        return;
-
-    channels_playing[channel] = sfx_None;
-    
-    for (i=0; i<NUM_CHANNELS; ++i)
-    {
-        // Playing on this channel? if so, don't release.
-
-        if (channels_playing[i] == id)
-            return;
-    }
-
-    // Not used on any channel, and can be safely released
-    
-    Z_ChangeTag(sound_chunks[id].abuf, PU_CACHE);
-}
-
-static boolean ConvertibleRatio(int freq1, int freq2)
-{
-    int ratio;
-
-    if (freq1 > freq2)
-    {
-        return ConvertibleRatio(freq2, freq1);
-    }
-    else if ((freq2 % freq1) != 0)
-    {
-        // Not in a direct ratio
-
-        return false;
-    }
-    else
-    {
-        // Check the ratio is a power of 2
-
-        ratio = freq2 / freq1;
-
-        while ((ratio & 1) == 0)
-        {
-            ratio = ratio >> 1;
-        }
-
-        return ratio == 1;
-    }
-}
-
-// Generic sound expansion function for any sample rate
-
-static void ExpandSoundData(byte *data,
-                            int samplerate,
-                            int length,
-                            Mix_Chunk *destination)
-{
-    SDL_AudioCVT convertor;
-    
-    if (samplerate <= mixer_freq
-     && ConvertibleRatio(samplerate, mixer_freq)
-     && SDL_BuildAudioCVT(&convertor,
-                          AUDIO_U8, 1, samplerate,
-                          mixer_format, mixer_channels, mixer_freq))
-    {
-        convertor.buf = destination->abuf;
-        convertor.len = length;
-        memcpy(convertor.buf, data, length);
-
-        SDL_ConvertAudio(&convertor);
-    }
-    else
-    {
-        Sint16 *expanded = (Sint16 *) destination->abuf;
-        int expanded_length;
-        int expand_ratio;
-        int i;
-
-        // Generic expansion if conversion does not work:
-        //
-        // SDL's audio conversion only works for rate conversions that are
-        // powers of 2; if the two formats are not in a direct power of 2
-        // ratio, do this naive conversion instead.
-
-        // number of samples in the converted sound
-
-        expanded_length = (length * mixer_freq) / samplerate;
-        expand_ratio = (length << 8) / expanded_length;
-
-        for (i=0; i<expanded_length; ++i)
-        {
-            Sint16 sample;
-            int src;
-
-            src = (i * expand_ratio) >> 8;
-
-            sample = data[src] | (data[src] << 8);
-            sample -= 32768;
-
-            // expand 8->16 bits, mono->stereo
-
-            expanded[i * 2] = expanded[i * 2 + 1] = sample;
-        }
-    }
-}
-
-// Load and convert a sound effect
-// Returns true if successful
-
-static boolean CacheSFX(int sound)
-{
-    int lumpnum;
-    unsigned int lumplen;
-    int samplerate;
-    unsigned int length;
-    unsigned int expanded_length;
-    byte *data;
-
-    // need to load the sound
-
-    lumpnum = S_sfx[sound].lumpnum;
-    data = W_CacheLumpNum(lumpnum, PU_STATIC);
-    lumplen = W_LumpLength(lumpnum);
-
-    // Check the header, and ensure this is a valid sound
-
-    if (lumplen < 8
-     || data[0] != 0x03 || data[1] != 0x00)
-    {
-        // Invalid sound
-
-        return false;
-    }
-
-    // 16 bit sample rate field, 32 bit length field
-
-    samplerate = (data[3] << 8) | data[2];
-    length = (data[7] << 24) | (data[6] << 16) | (data[5] << 8) | data[4];
-
-    // If the header specifies that the length of the sound is greater than
-    // the length of the lump itself, this is an invalid sound lump
-
-    if (length - 8 > lumplen)
-    {
-        return false;
-    }
-
-    expanded_length = (uint32_t) ((((uint64_t) length) * 4 * mixer_freq) / samplerate);
-
-    sound_chunks[sound].allocated = 1;
-    sound_chunks[sound].alen = expanded_length;
-    sound_chunks[sound].abuf 
-        = Z_Malloc(expanded_length, PU_STATIC, &sound_chunks[sound].abuf);
-    sound_chunks[sound].volume = MIX_MAX_VOLUME;
-
-    ExpandSoundData(data + 8, 
-                    samplerate, 
-                    length - 8, 
-                    &sound_chunks[sound]);
-
-    // don't need the original lump any more
-  
-    Z_ChangeTag(data, PU_CACHE);
-
-    return true;
-}
-
-static Mix_Chunk *GetSFXChunk(int sound_id)
-{
-    if (sound_chunks[sound_id].abuf == NULL)
-    {
-        if (!CacheSFX(sound_id))
-            return NULL;
-    }
-    else
-    {
-        // don't free the sound while it is playing!
-   
-        Z_ChangeTag(sound_chunks[sound_id].abuf, PU_STATIC);
-    }
-
-    return &sound_chunks[sound_id];
-}
-
-
-//
-// Retrieve the raw data lump index
-//  for a given SFX name.
-//
-
-int I_GetSfxLumpNum(sfxinfo_t* sfx)
-{
-    char namebuf[9];
-    char *prefix;
-
-    // Different prefix for PC speaker sound effects.
-
-    if (snd_sfxdevice == SNDDEVICE_PCSPEAKER)
-    {
-        prefix = "dp";
-    }
-    else
-    {
-        prefix = "ds";
-    }
-
-    sprintf(namebuf, "%s%s", prefix, DEH_String(sfx->name));
-    
-    return W_GetNumForName(namebuf);
-}
-
-//
-// Starting a sound means adding it
-//  to the current list of active sounds
-//  in the internal channels.
-// As the SFX info struct contains
-//  e.g. a pointer to the raw data,
-//  it is ignored.
-// As our sound handling does not handle
-//  priority, it is ignored.
-// Pitching (that is, increased speed of playback)
-//  is set, but currently not used by mixing.
-//
-
-int
-I_StartSound
-( int		id,
-  int           channel,
-  int		vol,
-  int		sep,
-  int		pitch,
-  int		priority )
-{
-    Mix_Chunk *chunk;
-
-    if (!sound_initialised)
-        return 0;
-
-    if (snd_sfxdevice == SNDDEVICE_PCSPEAKER)
-    {
-        return I_PCS_StartSound(id, channel, vol, sep, pitch, priority);
-    }
-
-    // Release a sound effect if there is already one playing
-    // on this channel
-
-    ReleaseSoundOnChannel(channel);
-
-    // Get the sound data
-
-    chunk = GetSFXChunk(id);
-
-    if (chunk == NULL)
-    {
-        return -1;
-    }
-
-    // play sound
-
-    Mix_PlayChannelTimed(channel, chunk, 0, -1);
-
-    channels_playing[channel] = id;
-
-    // set separation, etc.
- 
-    I_UpdateSoundParams(channel, vol, sep, pitch);
-
-    return channel;
-}
-
-void I_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
-    // sound data being used for this channel
-
-    ReleaseSoundOnChannel(handle);
-}
-
-
-int I_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);
-    }
-}
-
-
-
-
-// 
-// Periodically called to update the sound system
-//
-
-void I_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))
-        {
-            // Sound has finished playing on this channel,
-            // but sound data has not been released to cache
-            
-            ReleaseSoundOnChannel(i);
-        }
-    }
-}
-
-
-// 
-// 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;
-
-    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()
-{ 
-    int i;
-    
-    // No sounds yet
-
-    for (i=0; i<NUMSFX; ++i)
-    {
-        sound_chunks[i].abuf = NULL;
-    }
-
-    for (i=0; i<NUM_CHANNELS; ++i)
-    {
-        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;
-    }
-
-    if (Mix_OpenAudio(snd_samplerate, AUDIO_S16SYS, 2, 1024) < 0)
-    {
-        fprintf(stderr, "Error initialising SDL_mixer: %s\n", Mix_GetError());
-        return;
-    }
-
-    Mix_QuerySpec(&mixer_freq, &mixer_format, &mixer_channels);
-
-    Mix_AllocateChannels(NUM_CHANNELS);
-    
-    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;
-
-    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();
-}
-
-
-
--- a/src/i_sound.h
+++ /dev/null
@@ -1,127 +1,0 @@
-// 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, sound.
-//
-//-----------------------------------------------------------------------------
-
-#ifndef __I_SOUND__
-#define __I_SOUND__
-
-#include "doomdef.h"
-
-#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 );
-
-
-//
-//  MUSIC I/O
-//
-
-void I_InitMusic(void);
-void I_ShutdownMusic(void);
-
-// Volume.
-
-void I_SetMusicVolume(int volume);
-
-// PAUSE game handling.
-
-void I_PauseSong(void *handle);
-void I_ResumeSong(void *handle);
-
-// Registers a song handle to song data.
-
-void *I_RegisterSong(void *data, int length);
-
-// Called by anything that wishes to start music.
-//  plays a song, and when the song is done,
-//  starts playing it again in an endless loop.
-// Horrible thing to do, considering.
-
-void I_PlaySong(void *handle, int looping);
-
-// Stops a song over 3 seconds.
-
-void I_StopSong(void *handle);
-
-// See above (register), then think backwards
-
-void I_UnRegisterSong(void *handle);
-
-boolean I_QrySongPlaying(void *handle);
-
-
-#endif
--- a/src/s_sound.c
+++ b/src/s_sound.c
@@ -26,21 +26,25 @@
 #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 "w_wad.h"
+#include "z_zone.h"
 
-#include "doomstat.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,7 +91,10 @@
     
 } channel_t;
 
+// Low-level sound module we are using
 
+static sound_module_t *sound_module;
+
 // The set of channels available
 
 static channel_t *channels;
@@ -105,6 +108,10 @@
 
 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 @@
 
 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,8 +213,21 @@
 {  
     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_Shutdown(void)
 {
-    I_ShutdownSound();
+    if (sound_module != NULL)
+    {
+        sound_module->Shutdown();
+    }
+
+    I_ShutdownMusic();
 }
 
 static void S_StopChannel(int cnum)
@@ -172,9 +272,13 @@
     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 @@
         {
             // 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,7 +430,7 @@
 }
 
 //
-// 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.
@@ -333,7 +437,7 @@
 //
 
 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 @@
     mobj_t *origin;
     int rc;
     int sep;
-    int pitch;
     int priority;
     int cnum;
     int volume;
@@ -425,7 +528,6 @@
     // Initialize sound parameters
     if (sfx->link)
     {
-        pitch = sfx->pitch;
         priority = sfx->priority;
         volume += sfx->volume;
 
@@ -441,7 +543,6 @@
     }        
     else
     {
-        pitch = NORM_PITCH;
         priority = NORM_PRIORITY;
     }
 
@@ -453,8 +554,7 @@
         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 @@
         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,18 +583,6 @@
         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)
     {
@@ -531,14 +589,23 @@
         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 @@
     int                cnum;
     int                volume;
     int                sep;
-    int                pitch;
     sfxinfo_t*        sfx;
     channel_t*        c;
 
@@ -584,16 +650,14 @@
 
         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 @@
                     audible = S_AdjustSoundParams(listener,
                                                   c->origin,
                                                   &volume,
-                                                  &sep,
-                                                  &pitch);
+                                                  &sep);
                     
                     if (!audible)
                     {
@@ -622,7 +685,7 @@
                     }
                     else
                     {
-                        I_UpdateSoundParams(c->handle, volume, sep, pitch);
+                        sound_module->UpdateSoundParams(c->handle, volume, sep);
                     }
                 }
             }
--- 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 @@
     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