mirror of
https://github.com/ZDoom/gzdoom.git
synced 2024-11-22 20:21:26 +00:00
- Cleaned up the ugly MIDI song creating code a little.
- Added a generic Standard MIDI File creator that works with any of the sequencers. mus2midi.cpp is no longer used but is kept around as a reference. SVN r2677 (trunk)
This commit is contained in:
parent
092cbfd55b
commit
070ec75785
12 changed files with 344 additions and 202 deletions
|
@ -661,7 +661,6 @@ add_executable( zdoom WIN32
|
|||
m_png.cpp
|
||||
m_random.cpp
|
||||
md5.cpp
|
||||
mus2midi.cpp
|
||||
name.cpp
|
||||
nodebuild.cpp
|
||||
nodebuild_classify_nosse2.cpp
|
||||
|
|
|
@ -195,9 +195,9 @@ bool ProduceMIDI (const BYTE *musBuf, int len, TArray<BYTE> &outFile)
|
|||
switch (event & 0x70)
|
||||
{
|
||||
case MUS_NOTEOFF:
|
||||
midStatus |= MIDI_NOTEOFF;
|
||||
midStatus |= MIDI_NOTEON;
|
||||
mid1 = t & 127;
|
||||
mid2 = 64;
|
||||
mid2 = 0;
|
||||
break;
|
||||
|
||||
case MUS_NOTEON:
|
||||
|
|
|
@ -75,7 +75,4 @@ typedef struct
|
|||
// WORD UsedInstruments[NumInstruments];
|
||||
} MUSHeader;
|
||||
|
||||
bool ProduceMIDI (const BYTE *musBuf, int len, TArray<BYTE> &outFile);
|
||||
bool ProduceMIDI (const BYTE *musBuf, int len, FILE *outFile);
|
||||
|
||||
#endif //__MUS2MIDI_H__
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
** Plays music
|
||||
**
|
||||
**---------------------------------------------------------------------------
|
||||
** Copyright 1998-2006 Randy Heit
|
||||
** Copyright 1998-2010 Randy Heit
|
||||
** All rights reserved.
|
||||
**
|
||||
** Redistribution and use in source and binary forms, with or without
|
||||
|
@ -84,6 +84,14 @@ extern void ChildSigHandler (int signum);
|
|||
#define GZIP_FNAME 8
|
||||
#define GZIP_FCOMMENT 16
|
||||
|
||||
enum EMIDIType
|
||||
{
|
||||
MIDI_NOTMIDI,
|
||||
MIDI_MIDI,
|
||||
MIDI_HMI,
|
||||
MIDI_MUS
|
||||
};
|
||||
|
||||
extern int MUSHeaderSearch(const BYTE *head, int len);
|
||||
|
||||
EXTERN_CVAR (Int, snd_samplerate)
|
||||
|
@ -305,6 +313,40 @@ MusInfo *I_RegisterURLSong (const char *url)
|
|||
return NULL;
|
||||
}
|
||||
|
||||
static MusInfo *CreateMIDISong(FILE *file, const char *filename, BYTE *musiccache, int offset, int len, EMIDIDevice devtype, EMIDIType miditype)
|
||||
{
|
||||
if (devtype == MIDI_Timidity)
|
||||
{
|
||||
assert(miditype == MIDI_MIDI);
|
||||
return new TimiditySong(file, musiccache, len);
|
||||
}
|
||||
else if (devtype >= MIDI_Null)
|
||||
{
|
||||
assert(miditype == MIDI_MIDI);
|
||||
if (musiccache != NULL)
|
||||
{
|
||||
return new StreamSong((char *)musiccache, -1, len);
|
||||
}
|
||||
else
|
||||
{
|
||||
return new StreamSong(filename, offset, len);
|
||||
}
|
||||
}
|
||||
else if (miditype == MIDI_MUS)
|
||||
{
|
||||
return new MUSSong2(file, musiccache, len, devtype);
|
||||
}
|
||||
else if (miditype == MIDI_MIDI)
|
||||
{
|
||||
return new MIDISong2(file, musiccache, len, devtype);
|
||||
}
|
||||
else if (miditype == MIDI_HMI)
|
||||
{
|
||||
return new HMISong(file, musiccache, len, devtype);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
MusInfo *I_RegisterSong (const char *filename, BYTE *musiccache, int offset, int len, int device)
|
||||
{
|
||||
FILE *file;
|
||||
|
@ -405,191 +447,147 @@ MusInfo *I_RegisterSong (const char *filename, BYTE *musiccache, int offset, int
|
|||
}
|
||||
}
|
||||
|
||||
EMIDIType miditype = MIDI_NOTMIDI;
|
||||
|
||||
// Check for MUS format
|
||||
// Tolerate sloppy wads by searching up to 32 bytes for the header
|
||||
if (MUSHeaderSearch(idstr, sizeof(idstr)) >= 0)
|
||||
{
|
||||
/* MUS are played as:
|
||||
- OPL:
|
||||
- if explicitly selected by $mididevice
|
||||
- when snd_mididevice is -3 and no midi device is set for the song
|
||||
miditype = MIDI_MUS;
|
||||
}
|
||||
// Check for HMI format
|
||||
else
|
||||
if (id[0] == MAKE_ID('H','M','I','-') &&
|
||||
id[1] == MAKE_ID('M','I','D','I') &&
|
||||
id[2] == MAKE_ID('S','O','N','G'))
|
||||
{
|
||||
miditype = MIDI_HMI;
|
||||
}
|
||||
// Check for MIDI format
|
||||
else if (id[0] == MAKE_ID('M','T','h','d'))
|
||||
{
|
||||
miditype = MIDI_MIDI;
|
||||
}
|
||||
|
||||
Timidity:
|
||||
- if explicitly selected by $mididevice
|
||||
- when snd_mididevice is -2 and no midi device is set for the song
|
||||
if (miditype != MIDI_NOTMIDI)
|
||||
{
|
||||
TArray<BYTE> midi;
|
||||
/* MIDI are played as:
|
||||
- OPL:
|
||||
- if explicitly selected by $mididevice
|
||||
- when snd_mididevice is -3 and no midi device is set for the song
|
||||
|
||||
FMod:
|
||||
- if explicitly selected by $mididevice
|
||||
- when snd_mididevice is -1 and no midi device is set for the song
|
||||
- as fallback when both OPL and Timidity failed unless snd_mididevice is >= 0
|
||||
- Timidity:
|
||||
- if explicitly selected by $mididevice
|
||||
- when snd_mididevice is -2 and no midi device is set for the song
|
||||
|
||||
MMAPI (Win32 only):
|
||||
- if explicitly selected by $mididevice (non-Win32 redirects this to FMOD)
|
||||
- when snd_mididevice is >= 0 and no midi device is set for the song
|
||||
- as fallback when both OPL and Timidity failed and snd_mididevice is >= 0
|
||||
- FMod:
|
||||
- if explicitly selected by $mididevice
|
||||
- when snd_mididevice is -1 and no midi device is set for the song
|
||||
- as fallback when both OPL and Timidity failed unless snd_mididevice is >= 0
|
||||
|
||||
- MMAPI (Win32 only):
|
||||
- if explicitly selected by $mididevice (non-Win32 redirects this to FMOD)
|
||||
- when snd_mididevice is >= 0 and no midi device is set for the song
|
||||
- as fallback when both OPL and Timidity failed and snd_mididevice is >= 0
|
||||
*/
|
||||
if ((snd_mididevice == -3 && device == MDEV_DEFAULT) || device == MDEV_OPL)
|
||||
EMIDIDevice devtype = MIDI_Null;
|
||||
|
||||
// Choose the type of MIDI device we want.
|
||||
if (device == MDEV_FMOD || (snd_mididevice == -1 && device == MDEV_DEFAULT))
|
||||
{
|
||||
info = new MUSSong2 (file, musiccache, len, MIDI_OPL);
|
||||
devtype = MIDI_FMOD;
|
||||
}
|
||||
else if (device == MDEV_TIMIDITY || (device == MDEV_DEFAULT && snd_mididevice == -2))
|
||||
else if (device == MDEV_TIMIDITY || (snd_mididevice == -2 && device == MDEV_DEFAULT))
|
||||
{
|
||||
info = new TimiditySong (file, musiccache, len);
|
||||
devtype = MIDI_Timidity;
|
||||
}
|
||||
else if (device == MDEV_OPL || (snd_mididevice == -3 && device == MDEV_DEFAULT))
|
||||
{
|
||||
devtype = MIDI_OPL;
|
||||
}
|
||||
else if (snd_mididevice == -4 && device == MDEV_DEFAULT)
|
||||
{
|
||||
info = new MUSSong2(file, musiccache, len, MIDI_Timidity);
|
||||
devtype = MIDI_GUS;
|
||||
}
|
||||
#ifdef HAVE_FLUIDSYNTH
|
||||
else if (device == MDEV_FLUIDSYNTH || (snd_mididevice == -5 && device == MDEV_DEFAULT))
|
||||
{
|
||||
info = new MUSSong2(file, musiccache, len, MIDI_Fluid);
|
||||
devtype = MIDI_Fluid;
|
||||
}
|
||||
#endif
|
||||
#ifdef _WIN32
|
||||
else
|
||||
{
|
||||
devtype = MIDI_Win;
|
||||
}
|
||||
#endif
|
||||
|
||||
retry_as_fmod:
|
||||
if (miditype != MIDI_MIDI && devtype >= MIDI_Null)
|
||||
{
|
||||
// Convert to standard MIDI for external sequencers.
|
||||
MIDIStreamer *streamer;
|
||||
|
||||
if (miditype == MIDI_MUS)
|
||||
{
|
||||
streamer = new MUSSong2(file, musiccache, len, MIDI_Null);
|
||||
}
|
||||
else
|
||||
{
|
||||
assert(miditype == MIDI_HMI);
|
||||
streamer = new HMISong(file, musiccache, len, MIDI_Null);
|
||||
}
|
||||
if (streamer->IsValid())
|
||||
{
|
||||
streamer->CreateSMF(midi);
|
||||
miditype = MIDI_MIDI;
|
||||
musiccache = &midi[0];
|
||||
len = midi.Size();
|
||||
if (file != NULL)
|
||||
{
|
||||
fclose(file);
|
||||
file = NULL;
|
||||
}
|
||||
}
|
||||
delete streamer;
|
||||
}
|
||||
info = CreateMIDISong(file, filename, musiccache, offset, len, devtype, miditype);
|
||||
if (info != NULL && !info->IsValid())
|
||||
{
|
||||
delete info;
|
||||
info = NULL;
|
||||
device = MDEV_DEFAULT;
|
||||
}
|
||||
if (info == NULL && (snd_mididevice == -1 || device == MDEV_FMOD) && device != MDEV_MMAPI)
|
||||
if (info == NULL && devtype != MIDI_FMOD && snd_mididevice < 0)
|
||||
{
|
||||
TArray<BYTE> midi;
|
||||
bool midi_made = false;
|
||||
|
||||
if (file == NULL)
|
||||
{
|
||||
midi_made = ProduceMIDI((BYTE *)musiccache, len, midi);
|
||||
}
|
||||
else
|
||||
{
|
||||
BYTE *mus = new BYTE[len];
|
||||
size_t did_read = fread(mus, 1, len, file);
|
||||
if (did_read == (size_t)len)
|
||||
{
|
||||
midi_made = ProduceMIDI(mus, len, midi);
|
||||
}
|
||||
fseek(file, -(long)did_read, SEEK_CUR);
|
||||
delete[] mus;
|
||||
}
|
||||
if (midi_made)
|
||||
{
|
||||
info = new StreamSong((char *)&midi[0], -1, midi.Size());
|
||||
if (!info->IsValid())
|
||||
{
|
||||
delete info;
|
||||
info = NULL;
|
||||
}
|
||||
}
|
||||
devtype = MIDI_FMOD;
|
||||
goto retry_as_fmod;
|
||||
}
|
||||
#ifdef _WIN32
|
||||
if (info == NULL)
|
||||
if (info == NULL && devtype != MIDI_Win && snd_mididevice >= 0)
|
||||
{
|
||||
info = new MUSSong2 (file, musiccache, len, MIDI_Win);
|
||||
info = CreateMIDISong(file, filename, musiccache, offset, len, MIDI_Win, miditype);
|
||||
}
|
||||
#endif // _WIN32
|
||||
#endif
|
||||
}
|
||||
else
|
||||
|
||||
// Check for various raw OPL formats
|
||||
else if (
|
||||
(id[0] == MAKE_ID('R','A','W','A') && id[1] == MAKE_ID('D','A','T','A')) || // Rdos Raw OPL
|
||||
(id[0] == MAKE_ID('D','B','R','A') && id[1] == MAKE_ID('W','O','P','L')) || // DosBox Raw OPL
|
||||
(id[0] == MAKE_ID('A','D','L','I') && *((BYTE *)id + 4) == 'B')) // Martin Fernandez's modified IMF
|
||||
{
|
||||
// Check for HMI format
|
||||
if (id[0] == MAKE_ID('H','M','I','-') &&
|
||||
id[1] == MAKE_ID('M','I','D','I') &&
|
||||
id[2] == MAKE_ID('S','O','N','G'))
|
||||
{
|
||||
if ((snd_mididevice == -3 && device == MDEV_DEFAULT) || device == MDEV_OPL)
|
||||
{
|
||||
info = new HMISong(file, musiccache, len, MIDI_OPL);
|
||||
}
|
||||
else if (snd_mididevice == -4 && device == MDEV_DEFAULT)
|
||||
{
|
||||
info = new HMISong(file, musiccache, len, MIDI_Timidity);
|
||||
}
|
||||
#ifdef HAVE_FLUIDSYNTH
|
||||
else if (device == MDEV_FLUIDSYNTH || (snd_mididevice == -5 && device == MDEV_DEFAULT))
|
||||
{
|
||||
info = new HMISong(file, musiccache, len, MIDI_Fluid);
|
||||
}
|
||||
#endif
|
||||
#ifdef _WIN32
|
||||
else
|
||||
{
|
||||
info = new HMISong(file, musiccache, len, MIDI_Win);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
// Check for MIDI format
|
||||
else if (id[0] == MAKE_ID('M','T','h','d'))
|
||||
{
|
||||
// This is a midi file
|
||||
|
||||
/* MIDI are played as:
|
||||
OPL:
|
||||
- if explicitly selected by $mididevice
|
||||
- when snd_mididevice is -3 and no midi device is set for the song
|
||||
|
||||
Timidity:
|
||||
- if explicitly selected by $mididevice
|
||||
- when snd_mididevice is -2 and no midi device is set for the song
|
||||
|
||||
FMOD:
|
||||
- if explicitly selected by $mididevice
|
||||
- when snd_mididevice is -1 and no midi device is set for the song
|
||||
- as fallback when Timidity failed unless snd_mididevice is >= 0
|
||||
|
||||
MMAPI (Win32 only):
|
||||
- if explicitly selected by $mididevice (non-Win32 redirects this to FMOD)
|
||||
- when snd_mididevice is >= 0 and no midi device is set for the song
|
||||
- as fallback when Timidity failed and snd_mididevice is >= 0
|
||||
*/
|
||||
if (device == MDEV_OPL || (snd_mididevice == -3 && device == MDEV_DEFAULT))
|
||||
{
|
||||
info = new MIDISong2 (file, musiccache, len, MIDI_OPL);
|
||||
}
|
||||
else if (device == MDEV_TIMIDITY || (snd_mididevice == -2 && device == MDEV_DEFAULT))
|
||||
{
|
||||
info = new TimiditySong (file, musiccache, len);
|
||||
}
|
||||
else if (snd_mididevice == -4 && device == MDEV_DEFAULT)
|
||||
{
|
||||
info = new MIDISong2(file, musiccache, len, MIDI_Timidity);
|
||||
}
|
||||
#ifdef HAVE_FLUIDSYNTH
|
||||
else if (device == MDEV_FLUIDSYNTH || (snd_mididevice == -5 && device == MDEV_DEFAULT))
|
||||
{
|
||||
info = new MIDISong2(file, musiccache, len, MIDI_Fluid);
|
||||
}
|
||||
#endif
|
||||
if (info != NULL && !info->IsValid())
|
||||
{
|
||||
delete info;
|
||||
info = NULL;
|
||||
device = MDEV_DEFAULT;
|
||||
}
|
||||
#ifdef _WIN32
|
||||
if (info == NULL && device != MDEV_FMOD && (snd_mididevice >= 0 || device == MDEV_MMAPI))
|
||||
{
|
||||
info = new MIDISong2 (file, musiccache, len, MIDI_Win);
|
||||
}
|
||||
#endif // _WIN32
|
||||
}
|
||||
// Check for various raw OPL formats
|
||||
else if (
|
||||
(id[0] == MAKE_ID('R','A','W','A') && id[1] == MAKE_ID('D','A','T','A')) || // Rdos Raw OPL
|
||||
(id[0] == MAKE_ID('D','B','R','A') && id[1] == MAKE_ID('W','O','P','L')) || // DosBox Raw OPL
|
||||
(id[0] == MAKE_ID('A','D','L','I') && *((BYTE *)id + 4) == 'B')) // Martin Fernandez's modified IMF
|
||||
{
|
||||
info = new OPLMUSSong (file, musiccache, len);
|
||||
}
|
||||
// Check for game music
|
||||
else if ((fmt = GME_CheckFormat(id[0])) != NULL && fmt[0] != '\0')
|
||||
{
|
||||
info = GME_OpenSong(file, musiccache, len, fmt);
|
||||
}
|
||||
// Check for module formats
|
||||
else
|
||||
{
|
||||
info = MOD_OpenSong(file, musiccache, len);
|
||||
}
|
||||
info = new OPLMUSSong (file, musiccache, len);
|
||||
}
|
||||
// Check for game music
|
||||
else if ((fmt = GME_CheckFormat(id[0])) != NULL && fmt[0] != '\0')
|
||||
{
|
||||
info = GME_OpenSong(file, musiccache, len, fmt);
|
||||
}
|
||||
// Check for module formats
|
||||
else
|
||||
{
|
||||
info = MOD_OpenSong(file, musiccache, len);
|
||||
}
|
||||
|
||||
if (info == NULL)
|
||||
|
|
|
@ -333,8 +333,13 @@ enum EMIDIDevice
|
|||
{
|
||||
MIDI_Win,
|
||||
MIDI_OPL,
|
||||
MIDI_Timidity,
|
||||
MIDI_Fluid
|
||||
MIDI_GUS,
|
||||
MIDI_Fluid,
|
||||
|
||||
// only used by I_RegisterSong
|
||||
MIDI_Null,
|
||||
MIDI_FMOD,
|
||||
MIDI_Timidity
|
||||
};
|
||||
|
||||
class MIDIStreamer : public MusInfo
|
||||
|
@ -357,6 +362,7 @@ public:
|
|||
void FluidSettingInt(const char *setting, int value);
|
||||
void FluidSettingNum(const char *setting, double value);
|
||||
void FluidSettingStr(const char *setting, const char *value);
|
||||
void CreateSMF(TArray<BYTE> &file);
|
||||
|
||||
protected:
|
||||
MIDIStreamer(const char *dumpname, EMIDIDevice type);
|
||||
|
@ -369,7 +375,7 @@ protected:
|
|||
static void Callback(unsigned int uMsg, void *userdata, DWORD dwParam1, DWORD dwParam2);
|
||||
|
||||
// Virtuals for subclasses to override
|
||||
virtual void CheckCaps();
|
||||
virtual void CheckCaps(int tech);
|
||||
virtual void DoInitialSetup() = 0;
|
||||
virtual void DoRestart() = 0;
|
||||
virtual bool CheckDone() = 0;
|
||||
|
@ -457,7 +463,7 @@ public:
|
|||
protected:
|
||||
MIDISong2(const MIDISong2 *original, const char *filename, EMIDIDevice type); // file dump constructor
|
||||
|
||||
void CheckCaps();
|
||||
void CheckCaps(int tech);
|
||||
void DoInitialSetup();
|
||||
void DoRestart();
|
||||
bool CheckDone();
|
||||
|
@ -494,7 +500,7 @@ public:
|
|||
protected:
|
||||
HMISong(const HMISong *original, const char *filename, EMIDIDevice type); // file dump constructor
|
||||
|
||||
void CheckCaps();
|
||||
void CheckCaps(int tech);
|
||||
void DoInitialSetup();
|
||||
void DoRestart();
|
||||
bool CheckDone();
|
||||
|
|
|
@ -273,10 +273,8 @@ HMISong::~HMISong ()
|
|||
//
|
||||
//==========================================================================
|
||||
|
||||
void HMISong::CheckCaps()
|
||||
void HMISong::CheckCaps(int tech)
|
||||
{
|
||||
int tech = MIDI->GetTechnology();
|
||||
|
||||
// What's the equivalent HMI device for our technology?
|
||||
if (tech == MOD_FMSYNTH)
|
||||
{
|
||||
|
@ -851,7 +849,7 @@ MusInfo *HMISong::GetOPLDumper(const char *filename)
|
|||
|
||||
MusInfo *HMISong::GetWaveDumper(const char *filename, int rate)
|
||||
{
|
||||
return new HMISong(this, filename, MIDI_Timidity);
|
||||
return new HMISong(this, filename, MIDI_GUS);
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
|
|
|
@ -207,7 +207,7 @@ TimiditySong::TimiditySong (FILE *file, BYTE *musiccache, int len)
|
|||
|
||||
BYTE *buf;
|
||||
|
||||
if (file!=NULL)
|
||||
if (file != NULL)
|
||||
{
|
||||
buf = new BYTE[len];
|
||||
fread (buf, 1, len, file);
|
||||
|
@ -217,18 +217,8 @@ TimiditySong::TimiditySong (FILE *file, BYTE *musiccache, int len)
|
|||
buf = musiccache;
|
||||
}
|
||||
|
||||
|
||||
// The file type has already been checked before this class instance was
|
||||
// created, so we only need to check one character to determine if this
|
||||
// is a MUS or MIDI file and write it to disk as appropriate.
|
||||
if (buf[1] == 'T')
|
||||
{
|
||||
success = (fwrite (buf, 1, len, f) == (size_t)len);
|
||||
}
|
||||
else
|
||||
{
|
||||
success = ProduceMIDI (buf, len, f);
|
||||
}
|
||||
// Write to temporary file
|
||||
success = (fwrite (buf, 1, len, f) == (size_t)len);
|
||||
fclose (f);
|
||||
if (file != NULL)
|
||||
{
|
||||
|
|
|
@ -49,6 +49,8 @@
|
|||
|
||||
// PRIVATE FUNCTION PROTOTYPES ---------------------------------------------
|
||||
|
||||
static void WriteVarLen (TArray<BYTE> &file, DWORD value);
|
||||
|
||||
// EXTERNAL DATA DECLARATIONS ----------------------------------------------
|
||||
|
||||
EXTERN_CVAR(Float, snd_musicvolume)
|
||||
|
@ -57,8 +59,21 @@ EXTERN_CVAR(Float, snd_musicvolume)
|
|||
extern UINT mididevice;
|
||||
#endif
|
||||
|
||||
extern char MIDI_EventLengths[7];
|
||||
|
||||
// PRIVATE DATA DEFINITIONS ------------------------------------------------
|
||||
|
||||
static const BYTE StaticMIDIhead[] =
|
||||
{
|
||||
'M','T','h','d', 0, 0, 0, 6,
|
||||
0, 0, // format 0: only one track
|
||||
0, 1, // yes, there is really only one track
|
||||
0, 0, // divisions (filled in)
|
||||
'M','T','r','k', 0, 0, 0, 0,
|
||||
// The first event sets the tempo (filled in)
|
||||
0, 255, 81, 3, 0, 0, 0
|
||||
};
|
||||
|
||||
// PUBLIC DATA DEFINITIONS -------------------------------------------------
|
||||
|
||||
// CODE --------------------------------------------------------------------
|
||||
|
@ -172,7 +187,7 @@ bool MIDIStreamer::IsValid() const
|
|||
//
|
||||
//==========================================================================
|
||||
|
||||
void MIDIStreamer::CheckCaps()
|
||||
void MIDIStreamer::CheckCaps(int tech)
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -200,7 +215,7 @@ void MIDIStreamer::Play(bool looping, int subsong)
|
|||
{
|
||||
MIDI = new OPLDumperMIDIDevice(DumpFilename);
|
||||
}
|
||||
else if (DeviceType == MIDI_Timidity)
|
||||
else if (DeviceType == MIDI_GUS)
|
||||
{
|
||||
MIDI = new TimidityWaveWriterMIDIDevice(DumpFilename, 0);
|
||||
}
|
||||
|
@ -221,13 +236,17 @@ void MIDIStreamer::Play(bool looping, int subsong)
|
|||
break;
|
||||
#endif
|
||||
|
||||
case MIDI_Timidity:
|
||||
case MIDI_GUS:
|
||||
MIDI = new TimidityMIDIDevice;
|
||||
break;
|
||||
|
||||
case MIDI_OPL:
|
||||
MIDI = new OPLMIDIDevice;
|
||||
break;
|
||||
|
||||
default:
|
||||
MIDI = NULL;
|
||||
break;
|
||||
}
|
||||
|
||||
#ifndef _WIN32
|
||||
|
@ -240,9 +259,9 @@ void MIDIStreamer::Play(bool looping, int subsong)
|
|||
return;
|
||||
}
|
||||
|
||||
CheckCaps();
|
||||
CheckCaps(MIDI->GetTechnology());
|
||||
Precache();
|
||||
IgnoreLoops = true;
|
||||
IgnoreLoops = false;
|
||||
|
||||
// Set time division and tempo.
|
||||
if (0 != MIDI->SetTimeDiv(Division) ||
|
||||
|
@ -515,7 +534,7 @@ void MIDIStreamer::OutputVolume (DWORD volume)
|
|||
int MIDIStreamer::VolumeControllerChange(int channel, int volume)
|
||||
{
|
||||
ChannelVolumes[channel] = volume;
|
||||
return ((volume + 1) * Volume) >> 16;
|
||||
return IgnoreLoops ? volume : ((volume + 1) * Volume) >> 16;
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
|
@ -834,9 +853,9 @@ int MIDIStreamer::FillBuffer(int buffer_num, int max_events, DWORD max_time)
|
|||
//
|
||||
// MIDIStreamer :: Precache
|
||||
//
|
||||
// Generates a list of instruments this song uses them and passes them to
|
||||
// the MIDI device for precaching. The default implementation here pretends
|
||||
// to play the song and watches for program change events on normal channels
|
||||
// Generates a list of instruments this song uses and passes them to the
|
||||
// MIDI device for precaching. The default implementation here pretends to
|
||||
// play the song and watches for program change events on normal channels
|
||||
// and note on events on channel 10.
|
||||
//
|
||||
//==========================================================================
|
||||
|
@ -933,6 +952,138 @@ void MIDIStreamer::Precache()
|
|||
MIDI->PrecacheInstruments(&packed[0], packed.Size());
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// MIDIStreamer :: CreateSMF
|
||||
//
|
||||
// Simulates playback to create a Standard MIDI File.
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
void MIDIStreamer::CreateSMF(TArray<BYTE> &file)
|
||||
{
|
||||
DWORD delay = 0;
|
||||
BYTE running_status = 0;
|
||||
|
||||
// Always create songs aimed at GM devices.
|
||||
CheckCaps(MOD_MIDIPORT);
|
||||
IgnoreLoops = true;
|
||||
DoRestart();
|
||||
|
||||
file.Reserve(sizeof(StaticMIDIhead));
|
||||
memcpy(&file[0], StaticMIDIhead, sizeof(StaticMIDIhead));
|
||||
file[12] = Division >> 8;
|
||||
file[13] = Division & 0xFF;
|
||||
file[26] = InitialTempo >> 16;
|
||||
file[27] = InitialTempo >> 8;
|
||||
file[28] = InitialTempo;
|
||||
|
||||
while (!CheckDone())
|
||||
{
|
||||
DWORD *event_end = MakeEvents(Events[0], &Events[0][MAX_EVENTS*3], 1000000*600);
|
||||
for (DWORD *event = Events[0]; event < event_end; )
|
||||
{
|
||||
delay += event[0];
|
||||
if (MEVT_EVENTTYPE(event[2]) == MEVT_TEMPO)
|
||||
{
|
||||
WriteVarLen(file, delay);
|
||||
delay = 0;
|
||||
DWORD tempo = MEVT_EVENTPARM(event[2]);
|
||||
file.Push(MIDI_META);
|
||||
file.Push(MIDI_META_TEMPO);
|
||||
file.Push(3);
|
||||
file.Push(BYTE(tempo >> 16));
|
||||
file.Push(BYTE(tempo >> 8));
|
||||
file.Push(BYTE(tempo));
|
||||
}
|
||||
else if (MEVT_EVENTTYPE(event[2]) == MEVT_LONGMSG)
|
||||
{
|
||||
WriteVarLen(file, delay);
|
||||
delay = 0;
|
||||
DWORD len = MEVT_EVENTPARM(event[2]);
|
||||
BYTE *bytes = (BYTE *)&event[3];
|
||||
if (bytes[0] == MIDI_SYSEX)
|
||||
{
|
||||
len--;
|
||||
file.Push(MIDI_SYSEX);
|
||||
WriteVarLen(file, len);
|
||||
memcpy(&file[file.Reserve(len - 1)], bytes, len);
|
||||
}
|
||||
}
|
||||
else if (MEVT_EVENTTYPE(event[2]) == 0)
|
||||
{
|
||||
WriteVarLen(file, delay);
|
||||
delay = 0;
|
||||
BYTE status = BYTE(event[2]);
|
||||
if (status != running_status)
|
||||
{
|
||||
running_status = status;
|
||||
file.Push(status);
|
||||
}
|
||||
file.Push(BYTE((event[2] >> 8) & 0x7F));
|
||||
if (MIDI_EventLengths[(status >> 4) & 7] == 2)
|
||||
{
|
||||
file.Push(BYTE((event[2] >> 16) & 0x7F));
|
||||
}
|
||||
}
|
||||
// Advance to next event
|
||||
if (event[2] < 0x80000000)
|
||||
{ // short message
|
||||
event += 3;
|
||||
}
|
||||
else
|
||||
{ // long message
|
||||
event += 3 + ((MEVT_EVENTPARM(event[2]) + 3) >> 2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// End track
|
||||
WriteVarLen(file, delay);
|
||||
file.Push(MIDI_META);
|
||||
file.Push(MIDI_META_EOT);
|
||||
file.Push(0);
|
||||
|
||||
// Fill in track length
|
||||
DWORD len = file.Size() - 22;
|
||||
file[18] = BYTE(len >> 24);
|
||||
file[19] = BYTE(len >> 16);
|
||||
file[20] = BYTE(len >> 8);
|
||||
file[21] = BYTE(len & 255);
|
||||
|
||||
IgnoreLoops = false;
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// WriteVarLen
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
static void WriteVarLen (TArray<BYTE> &file, DWORD value)
|
||||
{
|
||||
DWORD buffer = value & 0x7F;
|
||||
|
||||
while ( (value >>= 7) )
|
||||
{
|
||||
buffer <<= 8;
|
||||
buffer |= (value & 0x7F) | 0x80;
|
||||
}
|
||||
|
||||
for (;;)
|
||||
{
|
||||
file.Push(BYTE(buffer));
|
||||
if (buffer & 0x80)
|
||||
{
|
||||
buffer >>= 8;
|
||||
}
|
||||
else
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// MIDIStreamer :: GetStats
|
||||
|
|
|
@ -283,9 +283,9 @@ DWORD *MUSSong2::MakeEvents(DWORD *events, DWORD *max_event_p, DWORD max_time)
|
|||
switch (event & 0x70)
|
||||
{
|
||||
case MUS_NOTEOFF:
|
||||
status |= MIDI_NOTEOFF;
|
||||
status |= MIDI_NOTEON;
|
||||
mid1 = t;
|
||||
mid2 = 64;
|
||||
mid2 = 0;
|
||||
break;
|
||||
|
||||
case MUS_NOTEON:
|
||||
|
@ -382,7 +382,7 @@ MusInfo *MUSSong2::GetOPLDumper(const char *filename)
|
|||
|
||||
MusInfo *MUSSong2::GetWaveDumper(const char *filename, int rate)
|
||||
{
|
||||
return new MUSSong2(this, filename, MIDI_Timidity);
|
||||
return new MUSSong2(this, filename, MIDI_GUS);
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
|
|
|
@ -216,10 +216,8 @@ MIDISong2::~MIDISong2 ()
|
|||
//
|
||||
//==========================================================================
|
||||
|
||||
void MIDISong2::CheckCaps()
|
||||
void MIDISong2::CheckCaps(int tech)
|
||||
{
|
||||
int tech = MIDI->GetTechnology();
|
||||
|
||||
DesignationMask = 0xFF0F;
|
||||
if (tech == MOD_FMSYNTH)
|
||||
{
|
||||
|
@ -801,7 +799,7 @@ MusInfo *MIDISong2::GetOPLDumper(const char *filename)
|
|||
|
||||
MusInfo *MIDISong2::GetWaveDumper(const char *filename, int rate)
|
||||
{
|
||||
return new MIDISong2(this, filename, MIDI_Timidity);
|
||||
return new MIDISong2(this, filename, MIDI_GUS);
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
|
|
|
@ -141,6 +141,15 @@ public:
|
|||
::new((void*)&Array[Count]) T(item);
|
||||
return Count++;
|
||||
}
|
||||
bool Pop ()
|
||||
{
|
||||
if (Count > 0)
|
||||
{
|
||||
Array[--Count].~T();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
bool Pop (T &item)
|
||||
{
|
||||
if (Count > 0)
|
||||
|
|
|
@ -712,10 +712,6 @@
|
|||
RelativePath=".\src\md5.cpp"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\src\mus2midi.cpp"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\src\name.cpp"
|
||||
>
|
||||
|
|
Loading…
Reference in a new issue