Add Allegro code
This commit is contained in:
@@ -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;
|
||||
}
|
||||
@@ -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,
|
||||
};
|
||||
|
||||
@@ -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,
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user