]> git.0x19.co - doomgeneric-snom360/commitdiff
Add Allegro code
authorTuro Lamminen <turol@iki.fi>
Tue, 23 Apr 2024 17:23:03 +0000 (20:23 +0300)
committerTuro Lamminen <turol@iki.fi>
Tue, 23 Apr 2024 17:37:20 +0000 (20:37 +0300)
doomgeneric/doomgeneric_allegro.c [new file with mode: 0644]
doomgeneric/i_allegromusic.c [new file with mode: 0644]
doomgeneric/i_allegrosound.c [new file with mode: 0644]

diff --git a/doomgeneric/doomgeneric_allegro.c b/doomgeneric/doomgeneric_allegro.c
new file mode 100644 (file)
index 0000000..b120366
--- /dev/null
@@ -0,0 +1,453 @@
+// doomgeneric for Allegro library
+
+#include "doomkeys.h"
+#include "i_system.h"
+#include "i_video.h"
+#include "m_argv.h"
+#include "doomgeneric.h"
+
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <unistd.h>
+
+#define ALLEGRO_NO_KEY_DEFINES 1
+#include <allegro.h>
+#undef uint32_t // ugly hack because Allegro is Old and doesn't know modern GCC has stdint.h
+
+
+static bool videomode_set = false;
+
+static BITMAP *temp_bitmap = NULL;
+
+static PALETTE pal;
+
+#define KEYQUEUE_SIZE 16
+
+static volatile unsigned int s_KeyQueue[KEYQUEUE_SIZE];
+static volatile unsigned int s_KeyQueueWriteIndex = 0;
+static unsigned int s_KeyQueueReadIndex = 0;
+
+static volatile unsigned int s_ticks = 0;
+
+
+void key_callback(int scancode) {
+       // this is in interrupt context, avoid heavy processing and be careful about volatile writes
+       s_KeyQueue[s_KeyQueueWriteIndex] = scancode;
+       s_KeyQueueWriteIndex = (s_KeyQueueWriteIndex + 1) % KEYQUEUE_SIZE;
+}
+END_OF_FUNCTION(key_callback);
+
+
+void timer_callback(void) {
+       // also in interrupt context
+       s_ticks++;
+}
+END_OF_FUNCTION(timer_callback);
+
+
+void DG_Init() {
+       int result;
+
+       printf("Initializing Allegro\n");
+
+       result = allegro_init();
+       if (result != 0)
+       {
+               I_Error("Allegro init failed: %d %s\n", result, allegro_error);
+       }
+
+       I_AtExit(allegro_exit, true);
+
+       LOCK_FUNCTION(key_callback);
+       LOCK_VARIABLE(s_KeyQueue);
+       LOCK_VARIABLE(s_KeyQueueWriteIndex);
+       LOCK_VARIABLE(s_KeyQueueReadIndex);
+
+       LOCK_FUNCTION(timer_callback);
+       LOCK_VARIABLE(s_ticks);
+
+       install_keyboard();
+       keyboard_lowlevel_callback = key_callback;
+
+       install_timer();
+       result = install_int(timer_callback, 1);
+       if (result < 0) {
+               I_Error("Unable to install timer: %d %s\n", result, allegro_error);
+       }
+
+       // don't set video mode yet so initialization messages stay on screen
+}
+
+
+static void back_to_text_mode(void) {
+       set_gfx_mode(GFX_TEXT, 80, 25, 0, 0);
+}
+
+
+void DG_DrawFrame()
+{
+       if (!DG_ScreenBuffer) {
+               return;
+       }
+
+       if (!videomode_set) {
+               int y;
+               int result;
+
+#ifdef CMAP256
+               set_color_depth(8);
+#else  // CMAP256
+               // does not seem to work on real DOS hardware
+               set_color_depth(32);
+#endif  // CMAP256
+
+               result = set_gfx_mode(GFX_AUTODETECT_FULLSCREEN, DOOMGENERIC_RESX, DOOMGENERIC_RESY, 0, 0);
+               if (result < 0) {
+                       I_Error("Failed to set video mode: %s\n", allegro_error);
+               }
+
+               // register an exit handler to return to text mode
+               // while this is also done by allegro_exit handler registered earlier
+               // that's too late for ENDOOM screen to work correctly
+               I_AtExit(back_to_text_mode, true);
+
+               if (!screen) {
+                       I_Error("screen is null\n");
+               }
+
+               temp_bitmap = create_bitmap(DOOMGENERIC_RESX, DOOMGENERIC_RESY);
+               if (!temp_bitmap) {
+                       I_Error("Failed to create temp bitmap\n");
+               }
+
+               // this is an evil hack
+               // replace the internal line pointers with pointers into DG_ScreenBuffer
+               // this avoids some copying
+               // it might crash when freeing the bitmap but we never do
+               for (y = 0; y < DOOMGENERIC_RESY; y++) {
+                       temp_bitmap->line[y] = (unsigned char *) &DG_ScreenBuffer[y * DOOMGENERIC_RESX];
+               }
+
+               clear_bitmap(temp_bitmap);
+
+               videomode_set = true;
+               palette_changed = true;
+       }
+
+#ifdef CMAP256
+
+       // changing palette implicitly waits for vsync
+       // TODO: should do it explicitly in case where we don't change palette
+       if (palette_changed) {
+               int i;
+
+               for (i = 0; i < 256; i++) {
+                       // allegro uses VGA range 0..63
+                       pal[i].r = colors[i].r >> 2;
+                       pal[i].g = colors[i].g >> 2;
+                       pal[i].b = colors[i].b >> 2;
+               }
+               set_palette(pal);
+
+               palette_changed = false;
+       }
+
+#endif  // CMAP256
+
+       blit(temp_bitmap, screen, 0, 0, 0, 0, DOOMGENERIC_RESX, DOOMGENERIC_RESY);
+}
+
+
+void DG_SleepMs(uint32_t ms)
+{
+       rest(ms);
+}
+
+
+uint32_t DG_GetTicksMs()
+{
+       return s_ticks;
+}
+
+
+int DG_GetKey(int *pressed, unsigned char *doomKey)
+{
+       if (s_KeyQueueReadIndex == s_KeyQueueWriteIndex){
+               //key queue is empty
+               return 0;
+       } else {
+               bool released;
+               short keyData = 0;
+               int scancode = s_KeyQueue[s_KeyQueueReadIndex];
+               s_KeyQueueReadIndex++;
+               s_KeyQueueReadIndex %= KEYQUEUE_SIZE;
+
+               released = scancode & 0x80;
+               *pressed = !released;
+
+               scancode = scancode & 0x7F;
+
+               switch (scancode) {
+               case  __allegro_KEY_RIGHT:
+                       keyData = KEY_RIGHTARROW;
+                       break;
+
+               case  __allegro_KEY_LEFT:
+                       keyData = KEY_LEFTARROW;
+                       break;
+
+               case  __allegro_KEY_UP:
+                       keyData = KEY_UPARROW;
+                       break;
+
+               case  __allegro_KEY_DOWN:
+                       keyData = KEY_DOWNARROW;
+                       break;
+
+               case  __allegro_KEY_COMMA:
+                       keyData = KEY_STRAFE_L;
+                       break;
+
+               case  __allegro_KEY_STOP:
+                       keyData = KEY_STRAFE_R;
+                       break;
+
+               case  __allegro_KEY_SPACE:
+                       keyData = KEY_USE;
+                       break;
+
+               case  __allegro_KEY_LCONTROL:
+                       keyData = KEY_FIRE;
+                       break;
+
+               case  __allegro_KEY_ESC:
+                       keyData = KEY_ESCAPE;
+                       break;
+
+               case  __allegro_KEY_ENTER:
+                       keyData = KEY_ENTER;
+                       break;
+
+               case  __allegro_KEY_TAB:
+                       keyData = KEY_TAB;
+                       break;
+
+               case  __allegro_KEY_F1:
+                       keyData = KEY_F1;
+                       break;
+
+               case  __allegro_KEY_F2:
+                       keyData = KEY_F2;
+                       break;
+
+               case  __allegro_KEY_F3:
+                       keyData = KEY_F3;
+                       break;
+
+               case  __allegro_KEY_F4:
+                       keyData = KEY_F4;
+                       break;
+
+               case  __allegro_KEY_F5:
+                       keyData = KEY_F5;
+                       break;
+
+               case  __allegro_KEY_F6:
+                       keyData = KEY_F6;
+                       break;
+
+               case  __allegro_KEY_F7:
+                       keyData = KEY_F7;
+                       break;
+
+               case  __allegro_KEY_F8:
+                       keyData = KEY_F8;
+                       break;
+
+               case  __allegro_KEY_F9:
+                       keyData = KEY_F9;
+                       break;
+
+               case  __allegro_KEY_F10:
+                       keyData = KEY_F10;
+                       break;
+
+               case  __allegro_KEY_F11:
+                       keyData = KEY_F11;
+                       break;
+
+               case  __allegro_KEY_F12:
+                       keyData = KEY_F12;
+                       break;
+
+               case  __allegro_KEY_BACKSPACE:
+                       keyData = KEY_BACKSPACE;
+                       break;
+
+               case  __allegro_KEY_PAUSE:
+                       keyData = KEY_PAUSE;
+                       break;
+
+               case  __allegro_KEY_EQUALS:
+                       keyData = KEY_EQUALS;
+                       break;
+
+               case  __allegro_KEY_MINUS:
+                       keyData = KEY_MINUS;
+                       break;
+
+               case  __allegro_KEY_LSHIFT:
+               case  __allegro_KEY_RSHIFT:
+                       keyData = KEY_RSHIFT;
+                       break;
+
+               case  __allegro_KEY_RCONTROL:
+                       keyData = KEY_RCTRL;
+                       break;
+
+               case  __allegro_KEY_ALT:
+                       keyData = KEY_RALT;
+                       break;
+
+               case  __allegro_KEY_CAPSLOCK:
+                       keyData = KEY_CAPSLOCK;
+                       break;
+
+               case  __allegro_KEY_NUMLOCK:
+                       keyData = KEY_NUMLOCK;
+                       break;
+
+               case  __allegro_KEY_SCRLOCK:
+                       keyData = KEY_SCRLCK;
+                       break;
+
+               case  __allegro_KEY_PRTSCR:
+                       keyData = KEY_PRTSCR;
+                       break;
+
+               case  __allegro_KEY_HOME:
+                       keyData = KEY_HOME;
+                       break;
+
+               case  __allegro_KEY_END:
+                       keyData = KEY_END;
+                       break;
+
+               case  __allegro_KEY_PGUP:
+                       keyData = KEY_PGUP;
+                       break;
+
+               case  __allegro_KEY_PGDN:
+                       keyData = KEY_PGDN;
+                       break;
+
+               case  __allegro_KEY_INSERT:
+                       keyData = KEY_INS;
+                       break;
+
+               case  __allegro_KEY_DEL:
+                       keyData = KEY_DEL;
+                       break;
+
+               case  __allegro_KEY_0_PAD:
+                       keyData = KEYP_0;
+                       break;
+
+               case  __allegro_KEY_1_PAD:
+                       keyData = KEYP_1;
+                       break;
+
+               case  __allegro_KEY_2_PAD:
+                       keyData = KEYP_2;
+                       break;
+
+               case  __allegro_KEY_3_PAD:
+                       keyData = KEYP_3;
+                       break;
+
+               case  __allegro_KEY_4_PAD:
+                       keyData = KEYP_4;
+                       break;
+
+               case  __allegro_KEY_5_PAD:
+                       keyData = KEYP_5;
+                       break;
+
+               case  __allegro_KEY_6_PAD:
+                       keyData = KEYP_6;
+                       break;
+
+               case  __allegro_KEY_7_PAD:
+                       keyData = KEYP_7;
+                       break;
+
+               case  __allegro_KEY_8_PAD:
+                       keyData = KEYP_8;
+                       break;
+
+               case  __allegro_KEY_9_PAD:
+                       keyData = KEYP_9;
+                       break;
+
+               case  __allegro_KEY_SLASH_PAD:
+                       keyData = KEYP_DIVIDE;
+                       break;
+
+               case  __allegro_KEY_PLUS_PAD:
+                       keyData = KEYP_PLUS;
+                       break;
+
+               case  __allegro_KEY_MINUS_PAD:
+                       keyData = KEYP_MINUS;
+                       break;
+
+               case  __allegro_KEY_ASTERISK:
+                       keyData = KEYP_MULTIPLY;
+                       break;
+
+               case  __allegro_KEY_DEL_PAD:
+                       keyData = KEYP_PERIOD;
+                       break;
+
+               case  __allegro_KEY_EQUALS_PAD:
+                       keyData = KEYP_EQUALS;
+                       break;
+
+               case  __allegro_KEY_ENTER_PAD:
+                       keyData = KEYP_ENTER;
+                       break;
+
+               default:
+                       keyData = scancode_to_ascii(scancode);
+                       break;
+               }
+
+               if (keyData != 0) {
+                       *doomKey = keyData & 0xFF;
+               }
+
+               return 1;
+       }
+
+       return 0;
+}
+
+
+void DG_SetWindowTitle(const char *title)
+{
+       set_window_title(title);
+}
+
+
+int main(int argc, char **argv)
+{
+       doomgeneric_Create(argc, argv);
+
+       while (true)
+       {
+               doomgeneric_Tick();
+       }
+
+       return 0;
+}
diff --git a/doomgeneric/i_allegromusic.c b/doomgeneric/i_allegromusic.c
new file mode 100644 (file)
index 0000000..2c6c055
--- /dev/null
@@ -0,0 +1,308 @@
+//
+// Copyright(C) 1993-1996 Id Software, Inc.
+// Copyright(C) 2005-2014 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.
+//
+// DESCRIPTION:
+//     System interface for music.
+//
+
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+
+#include "config.h"
+#include "doomtype.h"
+#include "memio.h"
+#include "mus2mid.h"
+
+#include "deh_str.h"
+#include "gusconf.h"
+#include "i_sound.h"
+#include "i_system.h"
+#include "i_swap.h"
+#include "m_argv.h"
+#include "m_config.h"
+#include "m_misc.h"
+#include "sha1.h"
+#include "w_wad.h"
+#include "z_zone.h"
+
+#include <allegro/base.h>
+#include <allegro/midi.h>
+#include <allegro/sound.h>
+#include <allegro/system.h>
+
+
+#define MAXMIDLENGTH (96 * 1024)
+#define MID_HEADER_MAGIC "MThd"
+#define MUS_HEADER_MAGIC "MUS\x1a"
+
+
+static boolean music_initialized = false;
+
+
+static boolean musicpaused = false;
+static int current_music_volume;
+
+
+// Currently playing music track.
+MIDI *current_track_music = NULL;
+
+// If true, the currently playing track is being played on loop.
+static boolean current_track_loop;
+
+
+
+// Shutdown music
+static void I_Allegro_ShutdownMusic(void)
+{
+       // nothing here
+       // TODO: stop song?
+}
+
+
+// Initialize music subsystem
+static boolean I_Allegro_InitMusic(void)
+{
+       // nothing here, it's all done by I_Allegro_InitSound
+       music_initialized = true;
+
+       return true;
+}
+
+
+// Set music volume (0 - 127)
+static void I_Allegro_SetMusicVolume(int volume)
+{
+       int digivol = -1;
+       get_volume(&digivol, NULL);
+       // allegro range 0 - 255
+       set_volume(digivol, volume * 2);
+}
+
+
+// Start playing a mid
+static void I_Allegro_PlaySong(void *handle, boolean looping)
+{
+       int retval;
+
+       if (!music_initialized)
+       {
+               return;
+       }
+
+       if (handle == NULL)
+       {
+               return;
+       }
+
+       current_track_music = (MIDI *) handle;
+       current_track_loop = looping;
+
+       retval = play_midi(current_track_music, looping);
+       if (retval < 0) {
+               fprintf(stderr, "Error playing midi: %d \"%s\"\n", retval, allegro_error);
+       }
+}
+
+
+static void I_Allegro_PauseSong(void)
+{
+       if (!music_initialized)
+       {
+               return;
+       }
+
+       musicpaused = true;
+
+       midi_pause();
+}
+
+
+static void I_Allegro_ResumeSong(void)
+{
+       if (!music_initialized)
+       {
+               return;
+       }
+
+       musicpaused = false;
+
+       midi_resume();
+}
+
+
+static void I_Allegro_StopSong(void)
+{
+       if (!music_initialized)
+       {
+               return;
+       }
+
+       stop_midi();
+
+       current_track_music = NULL;
+}
+
+
+static void I_Allegro_UnRegisterSong(void *handle)
+{
+       MIDI *midi = (MIDI *) handle;
+
+       if (!music_initialized)
+       {
+               return;
+       }
+
+       if (handle == NULL)
+       {
+               return;
+       }
+
+       destroy_midi(midi);
+}
+
+
+// Determine whether memory block is a .mid file
+static boolean IsMid(byte *mem, int len)
+{
+    return len > 4 && !memcmp(mem, "MThd", 4);
+}
+
+
+static boolean ConvertMus(byte *musdata, int len, char *filename)
+{
+    MEMFILE *instream;
+    MEMFILE *outstream;
+    void *outbuf;
+    size_t outbuf_len;
+    int result;
+
+    instream = mem_fopen_read(musdata, len);
+    outstream = mem_fopen_write();
+
+    result = mus2mid(instream, outstream);
+
+    if (result == 0)
+    {
+        mem_get_buf(outstream, &outbuf, &outbuf_len);
+
+        M_WriteFile(filename, outbuf, outbuf_len);
+    }
+
+    mem_fclose(instream);
+    mem_fclose(outstream);
+
+    return result;
+}
+
+
+static void *I_Allegro_RegisterSong(void *data, int len)
+{
+       char *filename;
+       MIDI *music;
+
+       if (!music_initialized)
+       {
+               return NULL;
+       }
+
+       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. In an ideal world we'd load it directly from memory but Allegro
+       // doesn't support that so we have to generate a temporary file.
+
+       music = load_midi(filename);
+
+       if (music == NULL)
+       {
+               // Failed to load
+               fprintf(stderr, "Error loading midi: %s\n", allegro_error);
+       }
+
+       // Remove the temporary MIDI file;
+       remove(filename);
+
+       free(filename);
+
+       return music;
+}
+
+
+// Is the song playing?
+static boolean I_Allegro_MusicIsPlaying(void)
+{
+       if (!music_initialized)
+       {
+               return false;
+       }
+
+       return (current_track_music != NULL) && (midi_pos > 0);
+}
+
+
+// Get position in substitute music track, in seconds since start of track.
+static double GetMusicPosition(void)
+{
+       return midi_time;
+}
+
+
+// Poll music position; if we have passed the loop point end position
+// then we need to go back.
+static void I_Allegro_PollMusic(void)
+{
+       // nothing here, allegro takes care of it
+}
+
+
+static snddevice_t music_allegro_devices[] =
+{
+       SNDDEVICE_PAS,
+       SNDDEVICE_GUS,
+       SNDDEVICE_WAVEBLASTER,
+       SNDDEVICE_SOUNDCANVAS,
+       SNDDEVICE_GENMIDI,
+       SNDDEVICE_AWE32,
+};
+
+
+music_module_t DG_music_module =
+{
+       music_allegro_devices,
+       arrlen(music_allegro_devices),
+       I_Allegro_InitMusic,
+       I_Allegro_ShutdownMusic,
+       I_Allegro_SetMusicVolume,
+       I_Allegro_PauseSong,
+       I_Allegro_ResumeSong,
+       I_Allegro_RegisterSong,
+       I_Allegro_UnRegisterSong,
+       I_Allegro_PlaySong,
+       I_Allegro_StopSong,
+       I_Allegro_MusicIsPlaying,
+       I_Allegro_PollMusic,
+};
+
diff --git a/doomgeneric/i_allegrosound.c b/doomgeneric/i_allegrosound.c
new file mode 100644 (file)
index 0000000..d8f5714
--- /dev/null
@@ -0,0 +1,433 @@
+//
+// Copyright(C) 1993-1996 Id Software, Inc.
+// Copyright(C) 2005-2014 Simon Howard
+// Copyright(C) 2008 David Flater
+//
+// 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.
+//
+// DESCRIPTION:
+//     System interface for sound.
+//
+
+#include "config.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+#include <ctype.h>
+
+#include "deh_str.h"
+#include "i_sound.h"
+#include "i_system.h"
+#include "i_swap.h"
+#include "m_argv.h"
+#include "m_misc.h"
+#include "w_wad.h"
+#include "z_zone.h"
+
+#include "doomtype.h"
+
+#include <allegro/base.h>
+#include <allegro/sound.h>
+#include <allegro/system.h>
+
+
+#define NUM_CHANNELS 16
+
+#define NUM_MIDI_CHANNELS 16
+
+
+static boolean sound_initialized = false;
+
+static sfxinfo_t *channels_playing[NUM_CHANNELS];
+
+static int allegro_voices[NUM_CHANNELS];
+
+static SAMPLE *dummy_sample = NULL;
+
+static boolean use_sfx_prefix;
+
+
+// We don't support libsamplerate with Allegro but these have to be here since
+// other code requires them
+int use_libsamplerate = 0;
+
+// Scale factor used when converting libsamplerate floating point numbers
+// to integers. Too high means the sounds can clip; too low means they
+// will be too quiet. This is an amount that should avoid clipping most
+// of the time: with all the Doom IWAD sound effects, at least. If a PWAD
+// is used, clipping might occur.
+
+float libsamplerate_scale = 0.65f;
+
+
+// Load and convert a sound effect
+// Returns true if successful
+
+static boolean CacheSFX(sfxinfo_t *sfxinfo)
+{
+       int lumpnum;
+       unsigned int lumplen;
+       int samplerate;
+       unsigned int length;
+       byte *data;
+       SAMPLE *sample;
+
+       // need to load the sound
+
+       lumpnum = sfxinfo->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
+
+       // We also discard sound lumps that are less than 49 samples long,
+       // as this is how DMX behaves - although the actual cut-off length
+       // seems to vary slightly depending on the sample rate.  This needs
+       // further investigation to better understand the correct
+       // behavior.
+
+       if (length > lumplen - 8 || length <= 48)
+       {
+               return false;
+       }
+
+       // The DMX sound library seems to skip the first 16 and last 16
+       // bytes of the lump - reason unknown.
+
+       data += 16;
+       length -= 32;
+
+       sample = create_sample(8, 0, samplerate, length);
+       if (sample == NULL) {
+               return false;
+       }
+       memcpy(sample->data, data, length);
+
+       sfxinfo->driver_data = sample;
+
+       // don't need the original lump any more
+
+       W_ReleaseLumpNum(lumpnum);
+
+       return true;
+}
+
+
+static void GetSfxLumpName(sfxinfo_t *sfx, char *buf, size_t buf_len)
+{
+       // Linked sfx lumps? Get the lump number for the sound linked to.
+
+       if (sfx->link != NULL)
+       {
+               sfx = sfx->link;
+       }
+
+       // Doom adds a DS* prefix to sound lumps; Heretic and Hexen don't
+       // do this.
+
+       if (use_sfx_prefix)
+       {
+               M_snprintf(buf, buf_len, "ds%s", DEH_String(sfx->name));
+       }
+       else
+       {
+               M_StringCopy(buf, DEH_String(sfx->name), buf_len);
+       }
+}
+
+
+static void I_Allegro_PrecacheSounds(sfxinfo_t *sounds, int num_sounds)
+{
+       char namebuf[9];
+       int i;
+
+       printf("I_Allegro_PrecacheSounds: Precaching all sound effects..");
+
+       for (i=0; i<num_sounds; ++i)
+       {
+               if ((i % 6) == 0)
+               {
+                       printf(".");
+                       fflush(stdout);
+               }
+
+               GetSfxLumpName(&sounds[i], namebuf, sizeof(namebuf));
+
+               sounds[i].lumpnum = W_CheckNumForName(namebuf);
+
+               if (sounds[i].lumpnum != -1)
+               {
+                       CacheSFX(&sounds[i]);
+               }
+       }
+
+       printf("\n");
+}
+
+
+//
+// Retrieve the raw data lump index
+//  for a given SFX name.
+//
+
+static int I_Allegro_GetSfxLumpNum(sfxinfo_t *sfx)
+{
+       char namebuf[9];
+
+       GetSfxLumpName(sfx, namebuf, sizeof(namebuf));
+
+       return W_GetNumForName(namebuf);
+}
+
+static void I_Allegro_UpdateSoundParams(int handle, int vol, int sep)
+{
+       int left, right;
+
+       if (!sound_initialized || handle < 0 || handle >= NUM_CHANNELS)
+       {
+               return;
+       }
+
+       if (channels_playing[handle] == NULL) {
+               return;
+       }
+
+       voice_set_volume(allegro_voices[handle], vol);
+       voice_set_pan(allegro_voices[handle], sep);
+}
+
+//
+// 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_Allegro_StartSound(sfxinfo_t *sfxinfo, int channel, int vol, int sep)
+{
+       if (!sound_initialized || channel < 0 || channel >= NUM_CHANNELS)
+       {
+               return -1;
+       }
+
+       // Release a sound effect if there is already one playing
+       // on this channel
+       if (channels_playing[channel]) {
+               voice_stop(allegro_voices[channel]);
+               channels_playing[channel] = NULL;
+       }
+
+       // Get the sound data
+       if (sfxinfo->driver_data == NULL)
+       {
+               if (!CacheSFX(sfxinfo))
+               {
+                       return -1;
+               }
+       }
+       assert(sfxinfo->driver_data);
+
+       // play sound
+       reallocate_voice(allegro_voices[channel], sfxinfo->driver_data);
+       voice_set_volume(allegro_voices[channel], vol);
+       voice_set_pan(allegro_voices[channel], sep);
+       voice_start(allegro_voices[channel]);
+
+       channels_playing[channel] = sfxinfo;
+
+       return channel;
+}
+
+
+static void I_Allegro_StopSound(int handle)
+{
+       if (!sound_initialized || handle < 0 || handle >= NUM_CHANNELS)
+       {
+               return;
+       }
+
+       if (channels_playing[handle] == NULL) {
+               return;
+       }
+
+       voice_stop(allegro_voices[handle]);
+       channels_playing[handle] = NULL;
+}
+
+
+static boolean I_Allegro_SoundIsPlaying(int handle)
+{
+       int position;
+
+       if (!sound_initialized || handle < 0 || handle >= NUM_CHANNELS)
+       {
+               return false;
+       }
+
+       if (channels_playing[handle] == NULL) {
+               return false;
+       }
+
+       position = voice_get_position(allegro_voices[handle]);
+       if (position < 0) {
+               // finished
+               return false;
+       }
+
+       // still playing
+       return true;
+}
+
+// 
+// Periodically called to update the sound system
+//
+
+static void I_Allegro_UpdateSound(void)
+{
+       int i;
+
+       // loop through all channels which have sample, check if they're finished
+       for (i = 0; i < NUM_CHANNELS; i++) {
+               if (channels_playing[i]) {
+                       int position = voice_get_position(allegro_voices[i]);
+                       if (position < 0) {
+                               // finished
+                               channels_playing[i] = NULL;
+                       }
+               }
+       }
+}
+
+
+static void I_Allegro_ShutdownSound(void)
+{
+       int i;
+
+       if (!sound_initialized)
+       {
+               return;
+       }
+
+       for (i = 0; i < NUM_CHANNELS; i++) {
+               if (channels_playing[i] != NULL) {
+                       voice_stop(allegro_voices[i]);
+                       channels_playing[i] = NULL;
+               }
+               deallocate_voice(allegro_voices[i]);
+               allegro_voices[i] = -1;
+       }
+
+       destroy_sample(dummy_sample);
+       dummy_sample = NULL;
+
+       remove_sound();
+
+       sound_initialized = false;
+}
+
+
+static boolean I_Allegro_InitSound(boolean _use_sfx_prefix)
+{
+       int i;
+
+       use_sfx_prefix = _use_sfx_prefix;
+
+       // No sounds yet
+
+       for (i=0; i<NUM_CHANNELS; ++i)
+       {
+               channels_playing[i] = NULL;
+       }
+
+       reserve_voices(NUM_CHANNELS, NUM_MIDI_CHANNELS);
+
+       i = install_sound(DIGI_AUTODETECT, MIDI_AUTODETECT, "allegro.cfg");
+       if (i < 0) {
+               printf("install_sound failed: %d \"%s\"\n", i, allegro_error);
+               return false;
+       }
+
+       printf("Allegro sound initialized\n");
+
+       printf("Mixer quality %d\n",       get_mixer_quality());
+       printf("Mixer freq %d\n",          get_mixer_frequency());
+       printf("Mixer bits %d\n",          get_mixer_bits());
+       printf("Mixer channels %d\n",      get_mixer_channels());
+       printf("Mixer voices %d\n",        get_mixer_voices());
+       printf("Mixer buffer length %d\n", get_mixer_buffer_length());
+
+       // create a dummy sample used to create voices
+       dummy_sample = create_sample(8, 0, 11025, 8);
+       if (dummy_sample == NULL) {
+               printf("Failed to create sound sample: %s\n", allegro_error);
+               return false;
+       }
+
+       for (i = 0; i < NUM_CHANNELS; i++) {
+               allegro_voices[i] = allocate_voice(dummy_sample);
+       }
+
+       sound_initialized = true;
+
+       return true;
+}
+
+
+static snddevice_t sound_allegro_devices[] =
+{
+       SNDDEVICE_SB,
+       SNDDEVICE_PAS,
+       SNDDEVICE_GUS,
+       SNDDEVICE_WAVEBLASTER,
+       SNDDEVICE_SOUNDCANVAS,
+       SNDDEVICE_AWE32,
+};
+
+
+sound_module_t DG_sound_module = 
+{
+       sound_allegro_devices,
+       arrlen(sound_allegro_devices),
+       I_Allegro_InitSound,
+       I_Allegro_ShutdownSound,
+       I_Allegro_GetSfxLumpNum,
+       I_Allegro_UpdateSound,
+       I_Allegro_UpdateSoundParams,
+       I_Allegro_StartSound,
+       I_Allegro_StopSound,
+       I_Allegro_SoundIsPlaying,
+       I_Allegro_PrecacheSounds,
+};
+