Added SDL2-based sound playback
This commit is contained in:
@@ -19,14 +19,14 @@ SDL_LIBS =
|
||||
CC=clang # gcc or g++
|
||||
CFLAGS+=-ggdb3 -Os $(INCLUDES) $(SDL_CFLAGS)
|
||||
LDFLAGS+=-Wl,-dead_strip
|
||||
CFLAGS+=-ggdb3 -Wall -DNORMALUNIX -DLINUX -DSNDSERV # -DUSEASM
|
||||
LIBS+=-lm -lc -lSDL2
|
||||
CFLAGS+=-ggdb3 -Wall -DNORMALUNIX -DLINUX -DSNDSERV -DFEATURE_SOUND # -DUSEASM
|
||||
LIBS+=-lm -lc -lSDL2 -lSDL2_mixer `sdl2-config --cflags --libs`
|
||||
|
||||
# subdirectory for objects
|
||||
OBJDIR=build
|
||||
OUTPUT=doomgeneric
|
||||
|
||||
SRC_DOOM = i_main.o dummy.o am_map.o doomdef.o doomstat.o dstrings.o d_event.o d_items.o d_iwad.o d_loop.o d_main.o d_mode.o d_net.o f_finale.o f_wipe.o g_game.o hu_lib.o hu_stuff.o info.o i_cdmus.o i_endoom.o i_joystick.o i_scale.o i_sound.o i_system.o i_timer.o memio.o m_argv.o m_bbox.o m_cheat.o m_config.o m_controls.o m_fixed.o m_menu.o m_misc.o m_random.o p_ceilng.o p_doors.o p_enemy.o p_floor.o p_inter.o p_lights.o p_map.o p_maputl.o p_mobj.o p_plats.o p_pspr.o p_saveg.o p_setup.o p_sight.o p_spec.o p_switch.o p_telept.o p_tick.o p_user.o r_bsp.o r_data.o r_draw.o r_main.o r_plane.o r_segs.o r_sky.o r_things.o sha1.o sounds.o statdump.o st_lib.o st_stuff.o s_sound.o tables.o v_video.o wi_stuff.o w_checksum.o w_file.o w_main.o w_wad.o z_zone.o w_file_stdc.o i_input.o i_video.o doomgeneric.o doomgeneric_sdl.o
|
||||
SRC_DOOM = i_main.o dummy.o am_map.o doomdef.o doomstat.o dstrings.o d_event.o d_items.o d_iwad.o d_loop.o d_main.o d_mode.o d_net.o f_finale.o f_wipe.o g_game.o hu_lib.o hu_stuff.o info.o i_cdmus.o i_endoom.o i_joystick.o i_scale.o i_sound.o i_system.o i_timer.o memio.o m_argv.o m_bbox.o m_cheat.o m_config.o m_controls.o m_fixed.o m_menu.o m_misc.o m_random.o p_ceilng.o p_doors.o p_enemy.o p_floor.o p_inter.o p_lights.o p_map.o p_maputl.o p_mobj.o p_plats.o p_pspr.o p_saveg.o p_setup.o p_sight.o p_spec.o p_switch.o p_telept.o p_tick.o p_user.o r_bsp.o r_data.o r_draw.o r_main.o r_plane.o r_segs.o r_sky.o r_things.o sha1.o sounds.o statdump.o st_lib.o st_stuff.o s_sound.o tables.o v_video.o wi_stuff.o w_checksum.o w_file.o w_main.o w_wad.o z_zone.o w_file_stdc.o i_input.o i_video.o doomgeneric.o doomgeneric_sdl.o mus2mid.o i_sdlmusic.o i_sdlsound.o
|
||||
OBJS += $(addprefix $(OBJDIR)/, $(SRC_DOOM))
|
||||
|
||||
all: $(OUTPUT)
|
||||
|
||||
@@ -33,7 +33,7 @@
|
||||
|
||||
// Enables sound output
|
||||
|
||||
#undef FEATURE_SOUND
|
||||
//#undef FEATURE_SOUND
|
||||
|
||||
#endif /* #ifndef DOOM_FEATURES_H */
|
||||
|
||||
|
||||
@@ -40,10 +40,6 @@ boolean drone = false;
|
||||
* public functions *
|
||||
*---------------------------------------------------------------------*/
|
||||
|
||||
void I_InitTimidityConfig(void)
|
||||
{
|
||||
}
|
||||
|
||||
/*---------------------------------------------------------------------*
|
||||
* eof *
|
||||
*---------------------------------------------------------------------*/
|
||||
|
||||
@@ -19,8 +19,8 @@
|
||||
#include <stdio.h>
|
||||
|
||||
#ifdef ORIGCODE
|
||||
#include "SDL.h"
|
||||
#include "SDL_cdrom.h"
|
||||
#include "SDL2/SDL.h"
|
||||
#include "SDL2/SDL_cdrom.h"
|
||||
#endif
|
||||
|
||||
#include "doomtype.h"
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
+15
-32
@@ -18,8 +18,8 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#ifdef ORIGCODE
|
||||
#include "SDL_mixer.h"
|
||||
#ifdef FEATURE_SOUND
|
||||
#include "SDL2/SDL_mixer.h"
|
||||
#endif
|
||||
|
||||
#include "config.h"
|
||||
@@ -80,21 +80,18 @@ extern char *timidity_cfg_path;
|
||||
// so that the config file can be shared between chocolate
|
||||
// doom and doom.exe
|
||||
|
||||
#if ORIGCODE
|
||||
static int snd_sbport = 0;
|
||||
static int snd_sbirq = 0;
|
||||
static int snd_sbdma = 0;
|
||||
static int snd_mport = 0;
|
||||
#endif
|
||||
|
||||
// Compiled-in sound modules:
|
||||
|
||||
static sound_module_t *sound_modules[] =
|
||||
{
|
||||
#ifdef FEATURE_SOUND
|
||||
#ifdef FEATURE_SOUND
|
||||
&sound_sdl_module,
|
||||
&sound_pcsound_module,
|
||||
#endif
|
||||
#endif
|
||||
NULL,
|
||||
};
|
||||
|
||||
@@ -102,10 +99,9 @@ static sound_module_t *sound_modules[] =
|
||||
|
||||
static music_module_t *music_modules[] =
|
||||
{
|
||||
#ifdef FEATURE_SOUND
|
||||
#ifdef FEATURE_SOUND
|
||||
&music_sdl_module,
|
||||
&music_opl_module,
|
||||
#endif
|
||||
#endif
|
||||
NULL,
|
||||
};
|
||||
|
||||
@@ -162,9 +158,9 @@ static void InitMusicModule(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
music_module = NULL;
|
||||
music_module = &music_sdl_module; return;
|
||||
|
||||
for (i=0; music_modules[i] != NULL; ++i)
|
||||
/*for (i=0; music_modules[i] != NULL; ++i)
|
||||
{
|
||||
// Is the music device in the list of devices supported
|
||||
// by this module?
|
||||
@@ -181,7 +177,7 @@ static void InitMusicModule(void)
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}*/
|
||||
}
|
||||
|
||||
//
|
||||
@@ -243,6 +239,7 @@ void I_InitSound(boolean use_sfx_prefix)
|
||||
InitMusicModule();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void I_ShutdownSound(void)
|
||||
@@ -356,6 +353,10 @@ void I_PrecacheSounds(sfxinfo_t *sounds, int num_sounds)
|
||||
|
||||
void I_InitMusic(void)
|
||||
{
|
||||
if(music_module != NULL)
|
||||
{
|
||||
music_module->Init();
|
||||
}
|
||||
}
|
||||
|
||||
void I_ShutdownMusic(void)
|
||||
@@ -433,11 +434,11 @@ boolean I_MusicIsPlaying(void)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void I_BindSoundVariables(void)
|
||||
{
|
||||
#ifdef ORIGCODE
|
||||
extern int use_libsamplerate;
|
||||
extern float libsamplerate_scale;
|
||||
|
||||
@@ -451,11 +452,6 @@ void I_BindSoundVariables(void)
|
||||
M_BindVariable("snd_musiccmd", &snd_musiccmd);
|
||||
M_BindVariable("snd_samplerate", &snd_samplerate);
|
||||
M_BindVariable("snd_cachesize", &snd_cachesize);
|
||||
M_BindVariable("opl_io_port", &opl_io_port);
|
||||
|
||||
M_BindVariable("timidity_cfg_path", &timidity_cfg_path);
|
||||
M_BindVariable("gus_patch_path", &gus_patch_path);
|
||||
M_BindVariable("gus_ram_kb", &gus_ram_kb);
|
||||
|
||||
#ifdef FEATURE_SOUND
|
||||
M_BindVariable("use_libsamplerate", &use_libsamplerate);
|
||||
@@ -465,18 +461,5 @@ void I_BindSoundVariables(void)
|
||||
// Before SDL_mixer version 1.2.11, MIDI music caused the game
|
||||
// to crash when it looped. If this is an old SDL_mixer version,
|
||||
// disable MIDI.
|
||||
|
||||
#ifdef __MACOSX__
|
||||
{
|
||||
const SDL_version *v = Mix_Linked_Version();
|
||||
|
||||
if (SDL_VERSIONNUM(v->major, v->minor, v->patch)
|
||||
< SDL_VERSIONNUM(1, 2, 11))
|
||||
{
|
||||
snd_musicdevice = SNDDEVICE_NONE;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
+12
-9
@@ -20,8 +20,7 @@
|
||||
#ifndef __I_SWAP__
|
||||
#define __I_SWAP__
|
||||
|
||||
#ifdef ORIGCODE
|
||||
#include "SDL_endian.h"
|
||||
#include <SDL2/SDL_endian.h>
|
||||
|
||||
// Endianess handling.
|
||||
// WAD files are stored little endian.
|
||||
@@ -42,13 +41,17 @@
|
||||
#define SYS_BIG_ENDIAN
|
||||
#endif
|
||||
|
||||
// cosmito from lsdldoom
|
||||
#define doom_swap_s(x) \
|
||||
((short int)((((unsigned short int)(x) & 0x00ff) << 8) | \
|
||||
(((unsigned short int)(x) & 0xff00) >> 8)))
|
||||
|
||||
|
||||
#if ( SDL_BYTEORDER == SDL_BIG_ENDIAN )
|
||||
#define doom_wtohs(x) doom_swap_s(x)
|
||||
#else
|
||||
|
||||
#define SHORT(x) ((signed short) (x))
|
||||
#define LONG(x) ((signed int) (x))
|
||||
|
||||
#define SYS_LITTLE_ENDIAN
|
||||
|
||||
#endif
|
||||
#define doom_wtohs(x) (short int)(x)
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
@@ -22,6 +22,8 @@
|
||||
#include "doomgeneric.h"
|
||||
|
||||
#include <stdarg.h>
|
||||
#include <stdio.h>
|
||||
#include <SDL2/SDL.h>
|
||||
//#include <sys/time.h>
|
||||
//#include <unistd.h>
|
||||
|
||||
@@ -90,6 +92,12 @@ void I_InitTimer(void)
|
||||
{
|
||||
// initialize timer
|
||||
|
||||
//SDL_Init(SDL_INIT_TIMER);
|
||||
printf("I_InitTimer: Setting up timer.\n");
|
||||
if (SDL_Init(SDL_INIT_TIMER) < 0)
|
||||
{
|
||||
printf("SDL_Init failed: %s\n", SDL_GetError());
|
||||
atexit(SDL_Quit);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,737 @@
|
||||
//
|
||||
// Copyright(C) 1993-1996 Id Software, Inc.
|
||||
// Copyright(C) 2005-2014 Simon Howard
|
||||
// Copyright(C) 2006 Ben Ryves 2006
|
||||
//
|
||||
// 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.
|
||||
//
|
||||
// mus2mid.c - Ben Ryves 2006 - http://benryves.com - benryves@benryves.com
|
||||
// Use to convert a MUS file into a single track, type 0 MIDI file.
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#include "doomtype.h"
|
||||
#include "i_swap.h"
|
||||
|
||||
#include "memio.h"
|
||||
#include "mus2mid.h"
|
||||
|
||||
#define NUM_CHANNELS 16
|
||||
|
||||
#define MIDI_PERCUSSION_CHAN 9
|
||||
#define MUS_PERCUSSION_CHAN 15
|
||||
|
||||
// MUS event codes
|
||||
typedef enum
|
||||
{
|
||||
mus_releasekey = 0x00,
|
||||
mus_presskey = 0x10,
|
||||
mus_pitchwheel = 0x20,
|
||||
mus_systemevent = 0x30,
|
||||
mus_changecontroller = 0x40,
|
||||
mus_scoreend = 0x60
|
||||
} musevent;
|
||||
|
||||
// MIDI event codes
|
||||
typedef enum
|
||||
{
|
||||
midi_releasekey = 0x80,
|
||||
midi_presskey = 0x90,
|
||||
midi_aftertouchkey = 0xA0,
|
||||
midi_changecontroller = 0xB0,
|
||||
midi_changepatch = 0xC0,
|
||||
midi_aftertouchchannel = 0xD0,
|
||||
midi_pitchwheel = 0xE0
|
||||
} midievent;
|
||||
|
||||
// Structure to hold MUS file header
|
||||
typedef struct
|
||||
{
|
||||
byte id[4];
|
||||
unsigned short scorelength;
|
||||
unsigned short scorestart;
|
||||
unsigned short primarychannels;
|
||||
unsigned short secondarychannels;
|
||||
unsigned short instrumentcount;
|
||||
} musheader;
|
||||
|
||||
// Standard MIDI type 0 header + track header
|
||||
static const byte midiheader[] =
|
||||
{
|
||||
'M', 'T', 'h', 'd', // Main header
|
||||
0x00, 0x00, 0x00, 0x06, // Header size
|
||||
0x00, 0x00, // MIDI type (0)
|
||||
0x00, 0x01, // Number of tracks
|
||||
0x00, 0x46, // Resolution
|
||||
'M', 'T', 'r', 'k', // Start of track
|
||||
0x00, 0x00, 0x00, 0x00 // Placeholder for track length
|
||||
};
|
||||
|
||||
// Cached channel velocities
|
||||
static byte channelvelocities[] =
|
||||
{
|
||||
127, 127, 127, 127, 127, 127, 127, 127,
|
||||
127, 127, 127, 127, 127, 127, 127, 127
|
||||
};
|
||||
|
||||
// Timestamps between sequences of MUS events
|
||||
|
||||
static unsigned int queuedtime = 0;
|
||||
|
||||
// Counter for the length of the track
|
||||
|
||||
static unsigned int tracksize;
|
||||
|
||||
static const byte controller_map[] =
|
||||
{
|
||||
0x00, 0x20, 0x01, 0x07, 0x0A, 0x0B, 0x5B, 0x5D,
|
||||
0x40, 0x43, 0x78, 0x7B, 0x7E, 0x7F, 0x79
|
||||
};
|
||||
|
||||
static int channel_map[NUM_CHANNELS];
|
||||
|
||||
// Write timestamp to a MIDI file.
|
||||
|
||||
static boolean WriteTime(unsigned int time, MEMFILE *midioutput)
|
||||
{
|
||||
unsigned int buffer = time & 0x7F;
|
||||
byte writeval;
|
||||
|
||||
while ((time >>= 7) != 0)
|
||||
{
|
||||
buffer <<= 8;
|
||||
buffer |= ((time & 0x7F) | 0x80);
|
||||
}
|
||||
|
||||
for (;;)
|
||||
{
|
||||
writeval = (byte)(buffer & 0xFF);
|
||||
|
||||
if (mem_fwrite(&writeval, 1, 1, midioutput) != 1)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
++tracksize;
|
||||
|
||||
if ((buffer & 0x80) != 0)
|
||||
{
|
||||
buffer >>= 8;
|
||||
}
|
||||
else
|
||||
{
|
||||
queuedtime = 0;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Write the end of track marker
|
||||
static boolean WriteEndTrack(MEMFILE *midioutput)
|
||||
{
|
||||
byte endtrack[] = {0xFF, 0x2F, 0x00};
|
||||
|
||||
if (WriteTime(queuedtime, midioutput))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (mem_fwrite(endtrack, 1, 3, midioutput) != 3)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
tracksize += 3;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Write a key press event
|
||||
static boolean WritePressKey(byte channel, byte key,
|
||||
byte velocity, MEMFILE *midioutput)
|
||||
{
|
||||
byte working = midi_presskey | channel;
|
||||
|
||||
if (WriteTime(queuedtime, midioutput))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (mem_fwrite(&working, 1, 1, midioutput) != 1)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
working = key & 0x7F;
|
||||
|
||||
if (mem_fwrite(&working, 1, 1, midioutput) != 1)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
working = velocity & 0x7F;
|
||||
|
||||
if (mem_fwrite(&working, 1, 1, midioutput) != 1)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
tracksize += 3;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// Write a key release event
|
||||
static boolean WriteReleaseKey(byte channel, byte key,
|
||||
MEMFILE *midioutput)
|
||||
{
|
||||
byte working = midi_releasekey | channel;
|
||||
|
||||
if (WriteTime(queuedtime, midioutput))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (mem_fwrite(&working, 1, 1, midioutput) != 1)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
working = key & 0x7F;
|
||||
|
||||
if (mem_fwrite(&working, 1, 1, midioutput) != 1)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
working = 0;
|
||||
|
||||
if (mem_fwrite(&working, 1, 1, midioutput) != 1)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
tracksize += 3;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// Write a pitch wheel/bend event
|
||||
static boolean WritePitchWheel(byte channel, short wheel,
|
||||
MEMFILE *midioutput)
|
||||
{
|
||||
byte working = midi_pitchwheel | channel;
|
||||
|
||||
if (WriteTime(queuedtime, midioutput))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (mem_fwrite(&working, 1, 1, midioutput) != 1)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
working = wheel & 0x7F;
|
||||
|
||||
if (mem_fwrite(&working, 1, 1, midioutput) != 1)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
working = (wheel >> 7) & 0x7F;
|
||||
|
||||
if (mem_fwrite(&working, 1, 1, midioutput) != 1)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
tracksize += 3;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Write a patch change event
|
||||
static boolean WriteChangePatch(byte channel, byte patch,
|
||||
MEMFILE *midioutput)
|
||||
{
|
||||
byte working = midi_changepatch | channel;
|
||||
|
||||
if (WriteTime(queuedtime, midioutput))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (mem_fwrite(&working, 1, 1, midioutput) != 1)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
working = patch & 0x7F;
|
||||
|
||||
if (mem_fwrite(&working, 1, 1, midioutput) != 1)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
tracksize += 2;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// Write a valued controller change event
|
||||
|
||||
static boolean WriteChangeController_Valued(byte channel,
|
||||
byte control,
|
||||
byte value,
|
||||
MEMFILE *midioutput)
|
||||
{
|
||||
byte working = midi_changecontroller | channel;
|
||||
|
||||
if (WriteTime(queuedtime, midioutput))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (mem_fwrite(&working, 1, 1, midioutput) != 1)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
working = control & 0x7F;
|
||||
|
||||
if (mem_fwrite(&working, 1, 1, midioutput) != 1)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
// Quirk in vanilla DOOM? MUS controller values should be
|
||||
// 7-bit, not 8-bit.
|
||||
|
||||
working = value;// & 0x7F;
|
||||
|
||||
// Fix on said quirk to stop MIDI players from complaining that
|
||||
// the value is out of range:
|
||||
|
||||
if (working & 0x80)
|
||||
{
|
||||
working = 0x7F;
|
||||
}
|
||||
|
||||
if (mem_fwrite(&working, 1, 1, midioutput) != 1)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
tracksize += 3;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// Write a valueless controller change event
|
||||
static boolean WriteChangeController_Valueless(byte channel,
|
||||
byte control,
|
||||
MEMFILE *midioutput)
|
||||
{
|
||||
return WriteChangeController_Valued(channel, control, 0,
|
||||
midioutput);
|
||||
}
|
||||
|
||||
// Allocate a free MIDI channel.
|
||||
|
||||
static int AllocateMIDIChannel(void)
|
||||
{
|
||||
int result;
|
||||
int max;
|
||||
int i;
|
||||
|
||||
// Find the current highest-allocated channel.
|
||||
|
||||
max = -1;
|
||||
|
||||
for (i=0; i<NUM_CHANNELS; ++i)
|
||||
{
|
||||
if (channel_map[i] > max)
|
||||
{
|
||||
max = channel_map[i];
|
||||
}
|
||||
}
|
||||
|
||||
// max is now equal to the highest-allocated MIDI channel. We can
|
||||
// now allocate the next available channel. This also works if
|
||||
// no channels are currently allocated (max=-1)
|
||||
|
||||
result = max + 1;
|
||||
|
||||
// Don't allocate the MIDI percussion channel!
|
||||
|
||||
if (result == MIDI_PERCUSSION_CHAN)
|
||||
{
|
||||
++result;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// Given a MUS channel number, get the MIDI channel number to use
|
||||
// in the outputted file.
|
||||
|
||||
static int GetMIDIChannel(int mus_channel, MEMFILE *midioutput)
|
||||
{
|
||||
// Find the MIDI channel to use for this MUS channel.
|
||||
// MUS channel 15 is the percusssion channel.
|
||||
|
||||
if (mus_channel == MUS_PERCUSSION_CHAN)
|
||||
{
|
||||
return MIDI_PERCUSSION_CHAN;
|
||||
}
|
||||
else
|
||||
{
|
||||
// If a MIDI channel hasn't been allocated for this MUS channel
|
||||
// yet, allocate the next free MIDI channel.
|
||||
|
||||
if (channel_map[mus_channel] == -1)
|
||||
{
|
||||
channel_map[mus_channel] = AllocateMIDIChannel();
|
||||
|
||||
// First time using the channel, send an "all notes off"
|
||||
// event. This fixes "The D_DDTBLU disease" described here:
|
||||
// https://www.doomworld.com/vb/source-ports/66802-the
|
||||
WriteChangeController_Valueless(channel_map[mus_channel], 0x7b,
|
||||
midioutput);
|
||||
}
|
||||
|
||||
return channel_map[mus_channel];
|
||||
}
|
||||
}
|
||||
|
||||
static boolean ReadMusHeader(MEMFILE *file, musheader *header)
|
||||
{
|
||||
boolean result;
|
||||
|
||||
result = mem_fread(&header->id, sizeof(byte), 4, file) == 4
|
||||
&& mem_fread(&header->scorelength, sizeof(short), 1, file) == 1
|
||||
&& mem_fread(&header->scorestart, sizeof(short), 1, file) == 1
|
||||
&& mem_fread(&header->primarychannels, sizeof(short), 1, file) == 1
|
||||
&& mem_fread(&header->secondarychannels, sizeof(short), 1, file) == 1
|
||||
&& mem_fread(&header->instrumentcount, sizeof(short), 1, file) == 1;
|
||||
|
||||
if (result)
|
||||
{
|
||||
header->scorelength = SHORT(header->scorelength);
|
||||
header->scorestart = SHORT(header->scorestart);
|
||||
header->primarychannels = SHORT(header->primarychannels);
|
||||
header->secondarychannels = SHORT(header->secondarychannels);
|
||||
header->instrumentcount = SHORT(header->instrumentcount);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
// Read a MUS file from a stream (musinput) and output a MIDI file to
|
||||
// a stream (midioutput).
|
||||
//
|
||||
// Returns 0 on success or 1 on failure.
|
||||
|
||||
boolean mus2mid(MEMFILE *musinput, MEMFILE *midioutput)
|
||||
{
|
||||
// Header for the MUS file
|
||||
musheader musfileheader;
|
||||
|
||||
// Descriptor for the current MUS event
|
||||
byte eventdescriptor;
|
||||
int channel; // Channel number
|
||||
musevent event;
|
||||
|
||||
|
||||
// Bunch of vars read from MUS lump
|
||||
byte key;
|
||||
byte controllernumber;
|
||||
byte controllervalue;
|
||||
|
||||
// Buffer used for MIDI track size record
|
||||
byte tracksizebuffer[4];
|
||||
|
||||
// Flag for when the score end marker is hit.
|
||||
int hitscoreend = 0;
|
||||
|
||||
// Temp working byte
|
||||
byte working;
|
||||
// Used in building up time delays
|
||||
unsigned int timedelay;
|
||||
|
||||
// Initialise channel map to mark all channels as unused.
|
||||
|
||||
for (channel=0; channel<NUM_CHANNELS; ++channel)
|
||||
{
|
||||
channel_map[channel] = -1;
|
||||
}
|
||||
|
||||
// Grab the header
|
||||
|
||||
if (!ReadMusHeader(musinput, &musfileheader))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
// [crispy] enable MUS format header check
|
||||
#define CHECK_MUS_HEADER
|
||||
#ifdef CHECK_MUS_HEADER
|
||||
// Check MUS header
|
||||
if (musfileheader.id[0] != 'M'
|
||||
|| musfileheader.id[1] != 'U'
|
||||
|| musfileheader.id[2] != 'S'
|
||||
|| musfileheader.id[3] != 0x1A)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
// Seek to where the data is held
|
||||
if (mem_fseek(musinput, (long)musfileheader.scorestart,
|
||||
MEM_SEEK_SET) != 0)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
// So, we can assume the MUS file is faintly legit. Let's start
|
||||
// writing MIDI data...
|
||||
|
||||
mem_fwrite(midiheader, 1, sizeof(midiheader), midioutput);
|
||||
tracksize = 0;
|
||||
|
||||
// Now, process the MUS file:
|
||||
while (!hitscoreend)
|
||||
{
|
||||
// Handle a block of events:
|
||||
|
||||
while (!hitscoreend)
|
||||
{
|
||||
// Fetch channel number and event code:
|
||||
|
||||
if (mem_fread(&eventdescriptor, 1, 1, musinput) != 1)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
channel = GetMIDIChannel(eventdescriptor & 0x0F, midioutput);
|
||||
event = eventdescriptor & 0x70;
|
||||
|
||||
switch (event)
|
||||
{
|
||||
case mus_releasekey:
|
||||
if (mem_fread(&key, 1, 1, musinput) != 1)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (WriteReleaseKey(channel, key, midioutput))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case mus_presskey:
|
||||
if (mem_fread(&key, 1, 1, musinput) != 1)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (key & 0x80)
|
||||
{
|
||||
if (mem_fread(&channelvelocities[channel], 1, 1, musinput) != 1)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
channelvelocities[channel] &= 0x7F;
|
||||
}
|
||||
|
||||
if (WritePressKey(channel, key,
|
||||
channelvelocities[channel], midioutput))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case mus_pitchwheel:
|
||||
if (mem_fread(&key, 1, 1, musinput) != 1)
|
||||
{
|
||||
break;
|
||||
}
|
||||
if (WritePitchWheel(channel, (short)(key * 64), midioutput))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case mus_systemevent:
|
||||
if (mem_fread(&controllernumber, 1, 1, musinput) != 1)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
if (controllernumber < 10 || controllernumber > 14)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (WriteChangeController_Valueless(channel,
|
||||
controller_map[controllernumber],
|
||||
midioutput))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case mus_changecontroller:
|
||||
if (mem_fread(&controllernumber, 1, 1, musinput) != 1)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (mem_fread(&controllervalue, 1, 1, musinput) != 1)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (controllernumber == 0)
|
||||
{
|
||||
if (WriteChangePatch(channel, controllervalue,
|
||||
midioutput))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (controllernumber < 1 || controllernumber > 9)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (WriteChangeController_Valued(channel,
|
||||
controller_map[controllernumber],
|
||||
controllervalue,
|
||||
midioutput))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case mus_scoreend:
|
||||
hitscoreend = 1;
|
||||
break;
|
||||
|
||||
default:
|
||||
return true;
|
||||
break;
|
||||
}
|
||||
|
||||
if (eventdescriptor & 0x80)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
// Now we need to read the time code:
|
||||
if (!hitscoreend)
|
||||
{
|
||||
timedelay = 0;
|
||||
for (;;)
|
||||
{
|
||||
if (mem_fread(&working, 1, 1, musinput) != 1)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
timedelay = timedelay * 128 + (working & 0x7F);
|
||||
if ((working & 0x80) == 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
queuedtime += timedelay;
|
||||
}
|
||||
}
|
||||
|
||||
// End of track
|
||||
if (WriteEndTrack(midioutput))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
// Write the track size into the stream
|
||||
if (mem_fseek(midioutput, 18, MEM_SEEK_SET))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
tracksizebuffer[0] = (tracksize >> 24) & 0xff;
|
||||
tracksizebuffer[1] = (tracksize >> 16) & 0xff;
|
||||
tracksizebuffer[2] = (tracksize >> 8) & 0xff;
|
||||
tracksizebuffer[3] = tracksize & 0xff;
|
||||
|
||||
if (mem_fwrite(tracksizebuffer, 1, 4, midioutput) != 4)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
#ifdef STANDALONE
|
||||
|
||||
#include "m_misc.h"
|
||||
#include "z_zone.h"
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
MEMFILE *src, *dst;
|
||||
byte *infile;
|
||||
long infile_len;
|
||||
void *outfile;
|
||||
size_t outfile_len;
|
||||
|
||||
if (argc != 3)
|
||||
{
|
||||
printf("Usage: %s <musfile> <midfile>\n", argv[0]);
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
Z_Init();
|
||||
|
||||
infile_len = M_ReadFile(argv[1], &infile);
|
||||
|
||||
src = mem_fopen_read(infile, infile_len);
|
||||
dst = mem_fopen_write();
|
||||
|
||||
if (mus2mid(src, dst))
|
||||
{
|
||||
fprintf(stderr, "mus2mid() failed\n");
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
// Write result to output file:
|
||||
|
||||
mem_get_buf(dst, &outfile, &outfile_len);
|
||||
|
||||
M_WriteFile(argv[2], outfile, outfile_len);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
#ifndef MUS2MID_H
|
||||
#define MUS2MID_H
|
||||
|
||||
#include "doomtype.h"
|
||||
#include "memio.h"
|
||||
|
||||
boolean mus2mid(MEMFILE *musinput, MEMFILE *midioutput);
|
||||
|
||||
#endif /* #ifndef MUS2MID_H */
|
||||
Reference in New Issue
Block a user