- Changed the MIDIStreamer to send the all notes off controller to each

channel when restarting the song, rather than emitting a single note off
  event which only has a 1 in 127 chance of being for a note that's playing
  on that channel. Then I decided it would probably be a good idea to reset
  all the controllers as well.
- Increasing the size of the internal Timidity stream buffer from 1/14 sec
  (copied from the OPL player) improved its sound dramatically, so apparently
  Timidity has issues with short stream buffers. It's now at 1/2 sec in
  length. However, there seems to be something weird going on with
  corazonazul_ff6boss.mid near the beginning where it stops and immediately
  restarts a guitar on the exact same note.
- Added a new sound debugging cvar: snd_drawoutput, which can show various
  oscilloscopes and spectrums.
- Internal TiMidity now plays music.
- Changed the progdir global variable into an FString.

SVN r900 (trunk)
This commit is contained in:
Randy Heit 2008-04-11 04:59:23 +00:00
parent 3f497fe8e5
commit 10c0d67b78
40 changed files with 8807 additions and 112 deletions

View file

@ -20,7 +20,7 @@ CFLAGS += -Dstricmp=strcasecmp -Dstrnicmp=strncasecmp -DNEED_STRUPR
LDFLAGS += -lz -ljpeg -`sdl-config --libs` `pkg-config gtk+-2.0 --libs` $(FMOD_PREFIX)/lib/libfmodex.so
NASMFLAGS += -f elf -DM_TARGET_LINUX
SRCDIRS = src/ $(addprefix src/,g_doom/ g_heretic/ g_hexen/ g_raven/ g_shared/ g_strife/ oplsynth/ sound/ sdl/ textures/ thingdef/ xlat/)
SRCDIRS = src/ $(addprefix src/,g_doom/ g_heretic/ g_hexen/ g_raven/ g_shared/ g_strife/ oplsynth/ sound/ sdl/ textures/ thingdef/ xlat/ timidity/)
VPATH = $(SRCDIRS)
INCLUDES = $(addprefix -I,$(SRCDIRS))
INCLUDES += -Isnes_spc/snes_spc/ -I$(FMOD_PREFIX)/include/fmodex/

View file

@ -56,7 +56,7 @@ ifeq ($(CONFIG),Release)
TARGET = $(RELEASETARGET)
endif
SRCDIRS = src/ $(addprefix src/,g_doom/ g_heretic/ g_hexen/ g_raven/ g_shared/ g_strife/ oplsynth/ sound/ win32/ textures/ thingdef/)
SRCDIRS = src/ $(addprefix src/,g_doom/ g_heretic/ g_hexen/ g_raven/ g_shared/ g_strife/ oplsynth/ sound/ win32/ textures/ thingdef/ timidity/)
VPATH = $(SRCDIRS)
CPPSRCS = $(wildcard $(addsuffix *.cpp,$(SRCDIRS))) src/xlat/parse_xlat.cpp

View file

@ -1,7 +1,26 @@
April 10, 2008
- Changed the MIDIStreamer to send the all notes off controller to each
channel when restarting the song, rather than emitting a single note off
event which only has 1 in 127 chance of being for a note that's playing
on that channel. Then I decided it would probably be a good idea to reset
all the controllers as well.
- Increasing the size of the internal Timidity stream buffer from 1/14 sec
(copied from the OPL player) improved its sound dramatically, so apparently
Timidity has issues with short stream buffers. It's now at 1/2 sec in
length. However, there seems to be something weird going on with
corazonazul_ff6boss.mid near the beginning where it stops and immediately
restarts a guitar on the exact same note.
- Added a new sound debugging cvar: snd_drawoutput, which can show various
oscilloscopes and spectrums.
April 10, 2008 (Changes by Graf Zahl)
- Eliminated some more global variables (onmobj, DoRipping, LastRipped,
MissileActor, bulletpitch and linetarget.)
April 9, 2008
- Internal TiMidity now plays music. Unfortunately, it doesn't sound right. :(
- Changed the progdir global variable into an FString.
April 9, 2008 (Changes by Graf Zahl)
- Replaced P_PathTraverse with an FPathTraverse class, rewrote all code using
P_PathTraverse and got rid of a lot of global variables in the process.

View file

@ -22,7 +22,7 @@ gamedir will hold progdir + the game directory (id1, id2, etc)
*/
char progdir[1024];
FString progdir;
static inline bool IsSeperator (int c)
{

View file

@ -28,7 +28,7 @@
int Q_filelength (FILE *f);
bool FileExists (const char *filename);
extern char progdir[1024];
extern FString progdir;
void FixPathSeperator (char *path);
static void inline FixPathSeperator (FString &path) { path.ReplaceChars('\\', '/'); }

View file

@ -179,6 +179,7 @@ CVAR (Float, timelimit, 0.f, CVAR_SERVERINFO);
CVAR (Bool, queryiwad, true, CVAR_ARCHIVE|CVAR_GLOBALCONFIG);
CVAR (String, defaultiwad, "", CVAR_ARCHIVE|CVAR_GLOBALCONFIG);
CVAR (Int, wipetype, 1, CVAR_ARCHIVE);
CVAR (Int, snd_drawoutput, 0, 0);
bool DrawFSHUD; // [RH] Draw fullscreen HUD?
wadlist_t *wadfiles; // [RH] remove limit on # of loaded wads
@ -666,6 +667,11 @@ void D_Display ()
NoWipe = 10;
}
if (snd_drawoutput && GSnd != NULL)
{
GSnd->DrawWaveDebug(snd_drawoutput);
}
if (!wipe || NoWipe < 0)
{
NetUpdate (); // send out any new accumulation

View file

@ -512,5 +512,5 @@ void OPLMIDIDevice::HandleEvent(int status, int parm1, int parm2)
bool OPLMIDIDevice::FillStream(SoundStream *stream, void *buff, int len, void *userdata)
{
OPLMIDIDevice *device = (OPLMIDIDevice *)userdata;
return device->ServiceStream (buff, len);
return device->ServiceStream(buff, len);
}

View file

@ -50,6 +50,7 @@
#include "gi.h"
#include "templates.h"
#include "zstring.h"
#include "timidity/timidity.h"
// MACROS ------------------------------------------------------------------
@ -275,7 +276,7 @@ void S_Init ()
{
S_ReturnChannel(Channels);
}
// no sounds are playing, and they are not paused
MusicPaused = false;
SoundPaused = false;

View file

@ -244,13 +244,20 @@ int main (int argc, char **argv)
atexit (call_terms);
atterm (I_Quit);
if (realpath (argv[0], progdir) == NULL)
strcpy (progdir, argv[0]);
char *slash = strrchr (progdir, '/');
if (slash)
// Should we even be doing anything with progdir on Unix systems?
char program[PATH_MAX];
if (realpath (argv[0], program) == NULL)
strcpy (program, argv[0]);
char *slash = strrchr (program, '/');
if (slash != NULL)
{
*(slash + 1) = '\0';
progdir = program;
}
else
progdir[0] = '.', progdir[1] = '/', progdir[2] = '\0';
{
progdir = "./";
}
C_InitConsole (80*8, 25*8, false);
D_DoomMain ();

View file

@ -72,6 +72,8 @@ extern HWND Window;
#define ERRCHECK(x)
#define SPECTRUM_SIZE 256
// TYPES -------------------------------------------------------------------
@ -739,6 +741,23 @@ void FMODSoundRenderer::Shutdown()
}
}
//==========================================================================
//
// FMODSoundRenderer :: GetOutputRate
//
//==========================================================================
float FMODSoundRenderer::GetOutputRate()
{
int rate;
if (FMOD_OK == Sys->getSoftwareFormat(&rate, NULL, NULL, NULL, NULL, NULL))
{
return float(rate);
}
return 48000.f; // Guess, but this should never happen.
}
//==========================================================================
//
// FMODSoundRenderer :: PrintStatus
@ -1715,3 +1734,298 @@ float F_CALLBACK FMODSoundRenderer::RolloffCallback(FMOD_CHANNEL *channel, float
return (powf(10.f, volume) - 1.f) / 9.f;
}
}
//==========================================================================
//
// FMODSoundRenderer :: DrawWaveDebug
//
// Bit 0: ( 1) Show oscilloscope for sfx.
// Bit 1: ( 2) Show spectrum for sfx.
// Bit 2: ( 4) Show oscilloscope for music.
// Bit 3: ( 8) Show spectrum for music.
// Bit 4: (16) Show oscilloscope for all sounds.
// Bit 5: (32) Show spectrum for all sounds.
//
//==========================================================================
void FMODSoundRenderer::DrawWaveDebug(int mode)
{
const int window_height = 100;
float wavearray[MAXWIDTH];
int window_size;
int numoutchans;
int y;
if (FMOD_OK != Sys->getSoftwareFormat(NULL, NULL, &numoutchans, NULL, NULL, NULL))
{
return;
}
// Scale all the channel windows so one group fits completely on one row, with
// 16 pixels of padding between each window.
window_size = (screen->GetWidth() - 16) / numoutchans - 16;
y = 16;
y = DrawChannelGroupOutput(SfxGroup, wavearray, window_size, window_height, y, mode);
y = DrawChannelGroupOutput(MusicGroup, wavearray, window_size, window_height, y, mode >> 2);
y = DrawSystemOutput(wavearray, window_size, window_height, y, mode >> 4);
}
//==========================================================================
//
// FMODSoundRenderer :: DrawChannelGroupOutput
//
// Draws an oscilloscope and/or a spectrum for a channel group.
//
//==========================================================================
int FMODSoundRenderer::DrawChannelGroupOutput(FMOD::ChannelGroup *group, float *wavearray, int width, int height, int y, int mode)
{
int y1, y2;
switch (mode & 0x03)
{
case 0x01: // Oscilloscope only
return DrawChannelGroupWaveData(group, wavearray, width, height, y, false);
case 0x02: // Spectrum only
return DrawChannelGroupSpectrum(group, wavearray, width, height, y, false);
case 0x03: // Oscilloscope + Spectrum
width = (width + 16) / 2 - 16;
y1 = DrawChannelGroupSpectrum(group, wavearray, width, height, y, true);
y2 = DrawChannelGroupWaveData(group, wavearray, width, height, y, true);
return MAX(y1, y2);
}
return y;
}
//==========================================================================
//
// FMODSoundRenderer :: DrawSystemOutput
//
// Like DrawChannelGroupOutput(), but uses the system object.
//
//==========================================================================
int FMODSoundRenderer::DrawSystemOutput(float *wavearray, int width, int height, int y, int mode)
{
int y1, y2;
switch (mode & 0x03)
{
case 0x01: // Oscilloscope only
return DrawSystemWaveData(wavearray, width, height, y, false);
case 0x02: // Spectrum only
return DrawSystemSpectrum(wavearray, width, height, y, false);
case 0x03: // Oscilloscope + Spectrum
width = (width + 16) / 2 - 16;
y1 = DrawSystemSpectrum(wavearray, width, height, y, true);
y2 = DrawSystemWaveData(wavearray, width, height, y, true);
return MAX(y1, y2);
}
return y;
}
//==========================================================================
//
// FMODSoundRenderer :: DrawChannelGroupWaveData
//
// Draws all the output channels for a specified channel group.
// Setting skip to true causes it to skip every other window.
//
//==========================================================================
int FMODSoundRenderer::DrawChannelGroupWaveData(FMOD::ChannelGroup *group, float *wavearray, int width, int height, int y, bool skip)
{
int drawn = 0;
int x = 16;
while (FMOD_OK == group->getWaveData(wavearray, width, drawn))
{
drawn++;
DrawWave(wavearray, x, y, width, height);
x += (width + 16) << int(skip);
}
if (drawn)
{
y += height + 16;
}
return y;
}
//==========================================================================
//
// FMODSoundRenderer::DrawSystemWaveData
//
// Like DrawChannelGroupWaveData, but it uses the system object to get the
// complete output.
//
//==========================================================================
int FMODSoundRenderer::DrawSystemWaveData(float *wavearray, int width, int height, int y, bool skip)
{
int drawn = 0;
int x = 16;
while (FMOD_OK == Sys->getWaveData(wavearray, width, drawn))
{
drawn++;
DrawWave(wavearray, x, y, width, height);
x += (width + 16) << int(skip);
}
if (drawn)
{
y += height + 16;
}
return y;
}
//==========================================================================
//
// FMODSoundRenderer :: DrawWave
//
// Draws an oscilloscope at the specified coordinates on the screen. Each
// entry in the wavearray buffer has its own column. IOW, there are <width>
// entries in wavearray.
//
//==========================================================================
void FMODSoundRenderer::DrawWave(float *wavearray, int x, int y, int width, int height)
{
float scale = height / 2.f;
float mid = y + scale;
int i;
// Draw a box around the oscilloscope.
screen->DrawLine(x - 1, y - 1, x + width, y - 1, -1, MAKEARGB(160, 0, 40, 200));
screen->DrawLine(x + width + 1, y - 1, x + width, y + height, -1, MAKEARGB(160, 0, 40, 200));
screen->DrawLine(x + width, y + height, x - 1, y + height, -1, MAKEARGB(160, 0, 40, 200));
screen->DrawLine(x - 1, y + height, x - 1, y - 1, -1, MAKEARGB(160, 0, 40, 200));
// Draw the actual oscilloscope.
if (screen->Accel2D)
{ // Drawing this with lines is super-slow without hardware acceleration, at least with
// the debug build.
float lasty = wavearray[0] * scale + mid;
float newy;
for (i = 1; i < width; ++i)
{
newy = wavearray[i] * scale + mid;
screen->DrawLine(x + i - 1, int(lasty), x + i, int(newy), -1, MAKEARGB(255,255,248,248));
lasty = newy;
}
}
else
{
for (i = 0; i < width; ++i)
{
float y = wavearray[i] * scale + mid;
screen->DrawPixel(x + i, int(y), -1, MAKEARGB(255,255,255,255));
}
}
}
//==========================================================================
//
// FMODSoundRenderer :: DrawChannelGroupSpectrum
//
// Draws all the spectrum for a specified channel group.
// Setting skip to true causes it to skip every other window, starting at
// the second one.
//
//==========================================================================
int FMODSoundRenderer::DrawChannelGroupSpectrum(FMOD::ChannelGroup *group, float *spectrumarray, int width, int height, int y, bool skip)
{
int drawn = 0;
int x = 16;
if (skip)
{
x += width + 16;
}
while (FMOD_OK == group->getSpectrum(spectrumarray, SPECTRUM_SIZE, drawn, FMOD_DSP_FFT_WINDOW_TRIANGLE))
{
drawn++;
DrawSpectrum(spectrumarray, x, y, width, height);
x += (width + 16) << int(skip);
}
if (drawn)
{
y += height + 16;
}
return y;
}
//==========================================================================
//
// FMODSoundRenderer::DrawSystemSpectrum
//
// Like DrawChannelGroupSpectrum, but it uses the system object to get the
// complete output.
//
//==========================================================================
int FMODSoundRenderer::DrawSystemSpectrum(float *spectrumarray, int width, int height, int y, bool skip)
{
int drawn = 0;
int x = 16;
if (skip)
{
x += width + 16;
}
while (FMOD_OK == Sys->getSpectrum(spectrumarray, SPECTRUM_SIZE, drawn, FMOD_DSP_FFT_WINDOW_TRIANGLE))
{
drawn++;
DrawSpectrum(spectrumarray, x, y, width, height);
x += (width + 16) << int(skip);
}
if (drawn)
{
y += height + 16;
}
return y;
}
//==========================================================================
//
// FMODSoundRenderer :: DrawSpectrum
//
// Draws a spectrum at the specified coordinates on the screen.
//
//==========================================================================
void FMODSoundRenderer::DrawSpectrum(float *spectrumarray, int x, int y, int width, int height)
{
float scale = height / 2.f;
float mid = y + scale;
float db;
int top;
// Draw a border and dark background for the spectrum.
screen->DrawLine(x - 1, y - 1, x + width, y - 1, -1, MAKEARGB(160, 0, 40, 200));
screen->DrawLine(x + width + 1, y - 1, x + width, y + height, -1, MAKEARGB(160, 0, 40, 200));
screen->DrawLine(x + width, y + height, x - 1, y + height, -1, MAKEARGB(160, 0, 40, 200));
screen->DrawLine(x - 1, y + height, x - 1, y - 1, -1, MAKEARGB(160, 0, 40, 200));
screen->Dim(MAKERGB(0,0,0), 0.3f, x, y, width, height);
// Draw the actual spectrum.
for (int i = 0; i < width; ++i)
{
db = spectrumarray[i * (SPECTRUM_SIZE - 2) / width + 1];
db = MAX(-150.f, 10 * log10f(db) * 2); // Convert to decibels and clamp
db = 1.f - (db / -150.f);
db *= height;
top = (int)db;
if (top >= height)
{
top = height - 1;
}
// screen->Clear(x + i, int(y + height - db), x + i + 1, y + height, -1, MAKEARGB(255, 255, 255, 40));
screen->Dim(MAKERGB(255,255,40), 0.65f, x + i, y + height - top, 1, top);
}
}

View file

@ -16,6 +16,7 @@ public:
void LoadSound (sfxinfo_t *sfx);
void UnloadSound (sfxinfo_t *sfx);
unsigned int GetMSLength(sfxinfo_t *sfx);
float GetOutputRate();
// Streaming sounds.
SoundStream *CreateStream (SoundStreamCallback callback, int buffsamples, int flags, int samplerate, void *userdata);
@ -48,6 +49,8 @@ public:
FString GatherStats ();
void ResetEnvironment ();
void DrawWaveDebug(int mode);
private:
bool SFXPaused;
bool InitSuccess;
@ -65,6 +68,17 @@ private:
void Shutdown ();
void DumpDriverCaps(FMOD_CAPS caps, int minfrequency, int maxfrequency);
int DrawChannelGroupOutput(FMOD::ChannelGroup *group, float *wavearray, int width, int height, int y, int mode);
int DrawSystemOutput(float *wavearray, int width, int height, int y, int mode);
int DrawChannelGroupWaveData(FMOD::ChannelGroup *group, float *wavearray, int width, int height, int y, bool skip);
int DrawSystemWaveData(float *wavearray, int width, int height, int y, bool skip);
void DrawWave(float *wavearray, int x, int y, int width, int height);
int DrawChannelGroupSpectrum(FMOD::ChannelGroup *group, float *wavearray, int width, int height, int y, bool skip);
int DrawSystemSpectrum(float *wavearray, int width, int height, int y, bool skip);
void DrawSpectrum(float *spectrumarray, int x, int y, int width, int height);
FMOD::System *Sys;
FMOD::ChannelGroup *SfxGroup, *PausableSfx;
FMOD::ChannelGroup *MusicGroup;

View file

@ -70,8 +70,7 @@ extern void ChildSigHandler (int signum);
#include "tempfiles.h"
#include "templates.h"
#include "stats.h"
#include <fmod.h>
#include "timidity/timidity.h"
EXTERN_CVAR (Int, snd_samplerate)
EXTERN_CVAR (Int, snd_mididevice)
@ -156,6 +155,8 @@ void I_InitMusic (void)
{
static bool setatterm = false;
Timidity::LoadConfig();
snd_musicvolume.Callback ();
nomusic = !!Args->CheckParm("-nomusic") || !!Args->CheckParm("-nosound");
@ -187,8 +188,9 @@ void I_ShutdownMusic(void)
S_StopMusic (true);
assert (currSong == NULL);
}
Timidity::FreeAll();
#ifdef _WIN32
I_ShutdownMusicWin32 ();
I_ShutdownMusicWin32();
#endif // _WIN32
}
@ -327,12 +329,16 @@ void *I_RegisterSong (const char *filename, char *musiccache, int offset, int le
*/
if ((snd_mididevice == -3 && device == MDEV_DEFAULT) || device == MDEV_OPL)
{
info = new MUSSong2 (file, musiccache, len, true);
info = new MUSSong2 (file, musiccache, len, MIDI_OPL);
}
else if (device == MDEV_TIMIDITY || (device == MDEV_DEFAULT && snd_mididevice == -2))
{
info = new TimiditySong (file, musiccache, len);
}
else if ((snd_mididevice == -4 && device == MDEV_DEFAULT) && GSnd != NULL)
{
info = new MUSSong2(file, musiccache, len, MIDI_Timidity);
}
if (info != NULL && !info->IsValid())
{
delete info;
@ -373,7 +379,7 @@ void *I_RegisterSong (const char *filename, char *musiccache, int offset, int le
#ifdef _WIN32
if (info == NULL)
{
info = new MUSSong2 (file, musiccache, len, false);
info = new MUSSong2 (file, musiccache, len, MIDI_Win);
}
#endif // _WIN32
}
@ -406,12 +412,16 @@ void *I_RegisterSong (const char *filename, char *musiccache, int offset, int le
*/
if ((device == MDEV_OPL || (snd_mididevice == -3 && device == MDEV_DEFAULT)) && GSnd != NULL)
{
info = new MIDISong2 (file, musiccache, len, true);
info = new MIDISong2 (file, musiccache, len, MIDI_OPL);
}
else if ((device == MDEV_TIMIDITY || (snd_mididevice == -2 && device == MDEV_DEFAULT)) && GSnd != NULL)
{
info = new TimiditySong (file, musiccache, len);
}
else if ((snd_mididevice == -4 && device == MDEV_DEFAULT) && GSnd != NULL)
{
info = new MIDISong2(file, musiccache, len, MIDI_Timidity);
}
if (info != NULL && !info->IsValid())
{
delete info;
@ -421,7 +431,7 @@ void *I_RegisterSong (const char *filename, char *musiccache, int offset, int le
#ifdef _WIN32
if (info == NULL && device != MDEV_FMOD && (snd_mididevice >= 0 || device == MDEV_MMAPI))
{
info = new MIDISong2 (file, musiccache, len, false);
info = new MIDISong2 (file, musiccache, len, MIDI_Win);
}
#endif // _WIN32
}

View file

@ -205,12 +205,69 @@ public:
void Stop();
};
// Internal TiMidity MIDI device --------------------------------------------
namespace Timidity { struct Renderer; }
class TimidityMIDIDevice : public MIDIDevice
{
public:
TimidityMIDIDevice();
~TimidityMIDIDevice();
int Open(void (*callback)(unsigned int, void *, DWORD, DWORD), void *userdata);
void Close();
bool IsOpen() const;
int GetTechnology() const;
int SetTempo(int tempo);
int SetTimeDiv(int timediv);
int StreamOut(MIDIHDR *data);
int StreamOutSync(MIDIHDR *data);
int Resume();
void Stop();
int PrepareHeader(MIDIHDR *data);
int UnprepareHeader(MIDIHDR *data);
bool FakeVolume();
bool Pause(bool paused);
bool NeedThreadedCallback();
void PrecacheInstruments(const BYTE *instruments, int count);
protected:
static bool FillStream(SoundStream *stream, void *buff, int len, void *userdata);
bool ServiceStream (void *buff, int numbytes);
void (*Callback)(unsigned int, void *, DWORD, DWORD);
void *CallbackData;
void CalcTickRate();
int PlayTick();
FCriticalSection CritSec;
SoundStream *Stream;
Timidity::Renderer *Renderer;
double Tempo;
double Division;
double SamplesPerTick;
double NextTickIn;
MIDIHDR *Events;
bool Started;
DWORD Position;
};
// Base class for streaming MUS and MIDI files ------------------------------
// MIDI device selection.
enum EMIDIDevice
{
MIDI_Win,
MIDI_OPL,
MIDI_Timidity
};
class MIDIStreamer : public MusInfo
{
public:
MIDIStreamer(bool opl);
MIDIStreamer(EMIDIDevice type);
~MIDIStreamer();
void MusicVolumeChanged();
@ -276,7 +333,7 @@ protected:
int InitialTempo;
BYTE ChannelVolumes[16];
DWORD Volume;
bool UseOPLDevice;
EMIDIDevice DeviceType;
bool CallbackIsThreaded;
FString DumpFilename;
};
@ -286,7 +343,7 @@ protected:
class MUSSong2 : public MIDIStreamer
{
public:
MUSSong2(FILE *file, char *musiccache, int length, bool opl);
MUSSong2(FILE *file, char *musiccache, int length, EMIDIDevice type);
~MUSSong2();
MusInfo *GetOPLDumper(const char *filename);
@ -311,7 +368,7 @@ protected:
class MIDISong2 : public MIDIStreamer
{
public:
MIDISong2(FILE *file, char *musiccache, int length, bool opl);
MIDISong2(FILE *file, char *musiccache, int length, EMIDIDevice type);
~MIDISong2();
MusInfo *GetOPLDumper(const char *filename);

View file

@ -204,6 +204,10 @@ FString SoundRenderer::GatherStats ()
return "No stats for this sound renderer.";
}
void SoundRenderer::DrawWaveDebug(int mode)
{
}
void SoundRenderer::ResetEnvironment ()
{
}
@ -221,3 +225,4 @@ FString SoundStream::GetStats()
{
return "No stream stats available.";
}

View file

@ -75,6 +75,7 @@ public:
virtual void LoadSound (sfxinfo_t *sfx) = 0; // load a sound from disk
virtual void UnloadSound (sfxinfo_t *sfx) = 0; // unloads a sound from memory
virtual unsigned int GetMSLength(sfxinfo_t *sfx) = 0; // Gets the length of a sound at its default frequency
virtual float GetOutputRate() = 0;
// Streaming sounds.
virtual SoundStream *CreateStream (SoundStreamCallback callback, int buffbytes, int flags, int samplerate, void *userdata) = 0;
@ -105,6 +106,8 @@ public:
virtual void PrintDriversList () = 0;
virtual FString GatherStats ();
virtual void ResetEnvironment ();
virtual void DrawWaveDebug(int mode);
};
extern SoundRenderer *GSnd;

View file

@ -19,7 +19,7 @@ CUSTOM_CVAR (Int, snd_mididevice, -1, CVAR_ARCHIVE|CVAR_GLOBALCONFIG)
if (!nummididevicesset)
return;
if ((self >= (signed)nummididevices) || (self < -3))
if ((self >= (signed)nummididevices) || (self < -4))
{
Printf ("ID out of range. Using default device.\n");
self = 0;

View file

@ -100,8 +100,8 @@ static BYTE CommonLengths[15] = { 0, 1, 2, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }
//
//==========================================================================
MIDISong2::MIDISong2 (FILE *file, char *musiccache, int len, bool opl)
: MIDIStreamer(opl), MusHeader(0), Tracks(0)
MIDISong2::MIDISong2 (FILE *file, char *musiccache, int len, EMIDIDevice type)
: MIDIStreamer(type), MusHeader(0), Tracks(0)
{
int p;
int i;

View file

@ -69,12 +69,12 @@ extern UINT mididevice;
//
//==========================================================================
MIDIStreamer::MIDIStreamer(bool opl)
MIDIStreamer::MIDIStreamer(EMIDIDevice type)
:
#ifdef _WIN32
PlayerThread(0), ExitEvent(0), BufferDoneEvent(0),
#endif
MIDI(0), Division(0), InitialTempo(500000), UseOPLDevice(opl)
MIDI(0), Division(0), InitialTempo(500000), DeviceType(type)
{
#ifdef _WIN32
BufferDoneEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
@ -102,7 +102,7 @@ MIDIStreamer::MIDIStreamer(const char *dumpname)
#ifdef _WIN32
PlayerThread(0), ExitEvent(0), BufferDoneEvent(0),
#endif
MIDI(0), Division(0), InitialTempo(500000), UseOPLDevice(true), DumpFilename(dumpname)
MIDI(0), Division(0), InitialTempo(500000), DeviceType(MIDI_OPL), DumpFilename(dumpname)
{
#ifdef _WIN32
BufferDoneEvent = NULL;
@ -198,16 +198,23 @@ void MIDIStreamer::Play(bool looping)
{
MIDI = new OPLDumperMIDIDevice(DumpFilename);
}
else
else switch(DeviceType)
{
case MIDI_Win:
#ifdef _WIN32
if (!UseOPLDevice)
{
MIDI = new WinMIDIDevice(mididevice);
}
else
break;
#endif
{
assert(0);
// Intentional fall-through for non-Windows systems.
case MIDI_Timidity:
MIDI = new TimidityMIDIDevice;
break;
case MIDI_OPL:
MIDI = new OPLMIDIDevice;
break;
}
#ifndef _WIN32
@ -682,8 +689,11 @@ int MIDIStreamer::FillBuffer(int buffer_num, int max_events, DWORD max_time)
{
events[0] = 0; // dwDeltaTime
events[1] = 0; // dwStreamID
events[2] = MIDI_NOTEOFF | i | (60 << 8) | (64<<16);
events += 3;
events[2] = MIDI_CTRLCHANGE | i | (123 << 8); // All notes off
events[3] = 0;
events[4] = 0;
events[5] = MIDI_CTRLCHANGE | i | (121 << 8); // Reset controllers
events += 6;
}
DoRestart();
}

View file

@ -84,8 +84,8 @@ static const BYTE CtrlTranslate[15] =
//
//==========================================================================
MUSSong2::MUSSong2 (FILE *file, char *musiccache, int len, bool opl)
: MIDIStreamer(opl), MusHeader(0), MusBuffer(0)
MUSSong2::MUSSong2 (FILE *file, char *musiccache, int len, EMIDIDevice type)
: MIDIStreamer(type), MusHeader(0), MusBuffer(0)
{
#ifdef _WIN32
if (ExitEvent == NULL)
@ -201,7 +201,7 @@ void MUSSong2::Precache()
}
else if (used[i] >= 135 && used[i] <= 181)
{ // Percussions are 100-based, not 128-based, eh?
work[j++] = (used[i] - 100) | 0x80;
work[j++] = used[i] - 100 + 128;
}
}
MIDI->PrecacheInstruments(&work[0], j);

View file

@ -0,0 +1,519 @@
/*
** music_timidity_mididevice.cpp
** Provides access to TiMidity as a generic MIDI device.
**
**---------------------------------------------------------------------------
** Copyright 2008 Randy Heit
** All rights reserved.
**
** Redistribution and use in source and binary forms, with or without
** modification, are permitted provided that the following conditions
** are met:
**
** 1. Redistributions of source code must retain the above copyright
** notice, this list of conditions and the following disclaimer.
** 2. Redistributions in binary form must reproduce the above copyright
** notice, this list of conditions and the following disclaimer in the
** documentation and/or other materials provided with the distribution.
** 3. The name of the author may not be used to endorse or promote products
** derived from this software without specific prior written permission.
**
** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
**---------------------------------------------------------------------------
**
*/
// HEADER FILES ------------------------------------------------------------
#include "i_musicinterns.h"
#include "templates.h"
#include "doomdef.h"
#include "m_swap.h"
#include "w_wad.h"
#include "timidity/timidity.h"
// MACROS ------------------------------------------------------------------
// EXTERNAL FUNCTION PROTOTYPES --------------------------------------------
// PUBLIC FUNCTION PROTOTYPES ----------------------------------------------
// PRIVATE FUNCTION PROTOTYPES ---------------------------------------------
// EXTERNAL DATA DECLARATIONS ----------------------------------------------
// PRIVATE DATA DEFINITIONS ------------------------------------------------
// PUBLIC DATA DEFINITIONS -------------------------------------------------
CVAR(Bool, timidity_watch, false, 0)
// CODE --------------------------------------------------------------------
//==========================================================================
//
// TimidityMIDIDevice Constructor
//
//==========================================================================
TimidityMIDIDevice::TimidityMIDIDevice()
{
Stream = NULL;
Tempo = 0;
Division = 0;
Events = NULL;
Started = false;
Renderer = NULL;
Renderer = new Timidity::Renderer(GSnd->GetOutputRate());
}
//==========================================================================
//
// TimidityMIDIDevice Destructor
//
//==========================================================================
TimidityMIDIDevice::~TimidityMIDIDevice()
{
Close();
if (Renderer != NULL)
{
delete Renderer;
}
}
//==========================================================================
//
// TimidityMIDIDevice :: Open
//
// Returns 0 on success.
//
//==========================================================================
int TimidityMIDIDevice::Open(void (*callback)(unsigned int, void *, DWORD, DWORD), void *userdata)
{
Stream = GSnd->CreateStream(FillStream, int(Renderer->rate / 2) * 4,
SoundStream::Float, int(Renderer->rate), this);
if (Stream == NULL)
{
return 2;
}
Callback = callback;
CallbackData = userdata;
Tempo = 500000;
Division = 100;
CalcTickRate();
Renderer->Reset();
return 0;
}
//==========================================================================
//
// TimidityMIDIDevice :: Close
//
//==========================================================================
void TimidityMIDIDevice::Close()
{
if (Stream != NULL)
{
delete Stream;
Stream = NULL;
}
Started = false;
}
//==========================================================================
//
// TimidityMIDIDevice :: IsOpen
//
//==========================================================================
bool TimidityMIDIDevice::IsOpen() const
{
return Stream != NULL;
}
//==========================================================================
//
// TimidityMIDIDevice :: GetTechnology
//
//==========================================================================
int TimidityMIDIDevice::GetTechnology() const
{
return MOD_SWSYNTH;
}
//==========================================================================
//
// TimidityMIDIDevice :: SetTempo
//
//==========================================================================
int TimidityMIDIDevice::SetTempo(int tempo)
{
Tempo = tempo;
CalcTickRate();
return 0;
}
//==========================================================================
//
// TimidityMIDIDevice :: SetTimeDiv
//
//==========================================================================
int TimidityMIDIDevice::SetTimeDiv(int timediv)
{
Division = timediv;
CalcTickRate();
return 0;
}
//==========================================================================
//
// TimidityMIDIDevice :: CalcTickRate
//
// Tempo is the number of microseconds per quarter note.
// Division is the number of ticks per quarter note.
//
//==========================================================================
void TimidityMIDIDevice::CalcTickRate()
{
SamplesPerTick = Renderer->rate / (1000000.0 / Tempo) / Division;
}
//==========================================================================
//
// TimidityMIDIDevice :: Resume
//
//==========================================================================
int TimidityMIDIDevice::Resume()
{
if (!Started)
{
if (Stream->Play(true, 1, false))
{
Started = true;
return 0;
}
return 1;
}
return 0;
}
//==========================================================================
//
// TimidityMIDIDevice :: Stop
//
//==========================================================================
void TimidityMIDIDevice::Stop()
{
if (Started)
{
Stream->Stop();
Started = false;
}
}
//==========================================================================
//
// TimidityMIDIDevice :: StreamOutSync
//
// This version is called from the main game thread and needs to
// synchronize with the player thread.
//
//==========================================================================
int TimidityMIDIDevice::StreamOutSync(MIDIHDR *header)
{
CritSec.Enter();
StreamOut(header);
CritSec.Leave();
return 0;
}
//==========================================================================
//
// TimidityMIDIDevice :: StreamOut
//
// This version is called from the player thread so does not need to
// arbitrate for access to the Events pointer.
//
//==========================================================================
int TimidityMIDIDevice::StreamOut(MIDIHDR *header)
{
header->lpNext = NULL;
if (Events == NULL)
{
Events = header;
NextTickIn = SamplesPerTick * *(DWORD *)header->lpData;
Position = 0;
}
else
{
MIDIHDR **p;
for (p = &Events; *p != NULL; p = &(*p)->lpNext)
{ }
*p = header;
}
return 0;
}
//==========================================================================
//
// TimidityMIDIDevice :: PrepareHeader
//
//==========================================================================
int TimidityMIDIDevice::PrepareHeader(MIDIHDR *header)
{
return 0;
}
//==========================================================================
//
// TimidityMIDIDevice :: UnprepareHeader
//
//==========================================================================
int TimidityMIDIDevice::UnprepareHeader(MIDIHDR *header)
{
return 0;
}
//==========================================================================
//
// TimidityMIDIDevice :: FakeVolume
//
// Since the TiMidity output is rendered as a normal stream, its volume is
// controlled through the GSnd interface, not here.
//
//==========================================================================
bool TimidityMIDIDevice::FakeVolume()
{
return false;
}
//==========================================================================
//
// TimidityMIDIDevice :: NeedThreadedCallabck
//
// OPL can service the callback directly rather than using a separate
// thread.
//
//==========================================================================
bool TimidityMIDIDevice::NeedThreadedCallback()
{
return false;
}
//==========================================================================
//
// TimidityMIDIDevice :: Pause
//
//==========================================================================
bool TimidityMIDIDevice::Pause(bool paused)
{
if (Stream != NULL)
{
return Stream->SetPaused(paused);
}
return true;
}
//==========================================================================
//
// TimidityMIDIDevice :: PrecacheInstruments
//
// For each entry, bit 7 set indicates that the instrument is percussion and
// the lower 7 bits contain the note number to use on MIDI channel 10,
// otherwise it is melodic and the lower 7 bits are the program number.
//
//==========================================================================
void TimidityMIDIDevice::PrecacheInstruments(const BYTE *instruments, int count)
{
for (int i = 0; i < count; ++i)
{
Renderer->MarkInstrument(0, instruments[i] >> 7, instruments[i] & 127);
}
Renderer->load_missing_instruments();
}
//==========================================================================
//
// TimidityMIDIDevice :: PlayTick
//
// event[0] = delta time
// event[1] = unused
// event[2] = event
//
//==========================================================================
int TimidityMIDIDevice::PlayTick()
{
DWORD delay = 0;
while (delay == 0 && Events != NULL)
{
DWORD *event = (DWORD *)(Events->lpData + Position);
if (MEVT_EVENTTYPE(event[2]) == MEVT_TEMPO)
{
SetTempo(MEVT_EVENTPARM(event[2]));
}
else if (MEVT_EVENTTYPE(event[2]) == MEVT_LONGMSG)
{
Renderer->HandleLongMessage((BYTE *)&event[3], MEVT_EVENTPARM(event[2]));
}
else if (MEVT_EVENTTYPE(event[2]) == 0)
{ // Short MIDI event
int status = event[2] & 0xff;
int parm1 = (event[2] >> 8) & 0x7f;
int parm2 = (event[2] >> 16) & 0x7f;
Renderer->HandleEvent(status, parm1, parm2);
if (timidity_watch)
{
static const char *const commands[8] =
{
"Note off",
"Note on",
"Poly press",
"Ctrl change",
"Prgm change",
"Chan press",
"Pitch bend",
"SysEx"
};
#ifdef _WIN32
char buffer[128];
sprintf(buffer, "C%02d: %11s %3d %3d\n", (status & 15) + 1, commands[(status >> 4) & 7], parm1, parm2);
OutputDebugString(buffer);
#else
fprintf(stderr, "C%02d: %11s %3d %3d\n", (status & 15) + 1, commands[(status >> 4) & 7], parm1, parm2);
#endif
}
}
// Advance to next event.
if (event[2] < 0x80000000)
{ // Short message
Position += 12;
}
else
{ // Long message
Position += 12 + ((MEVT_EVENTPARM(event[2]) + 3) & ~3);
}
// Did we use up this buffer?
if (Position >= Events->dwBytesRecorded)
{
Events = Events->lpNext;
Position = 0;
if (Callback != NULL)
{
Callback(MOM_DONE, CallbackData, 0, 0);
}
}
if (Events == NULL)
{ // No more events. Just return something to keep the song playing
// while we wait for more to be submitted.
return int(Division);
}
delay = *(DWORD *)(Events->lpData + Position);
}
return delay;
}
//==========================================================================
//
// TimidityMIDIDevice :: ServiceStream
//
//==========================================================================
bool TimidityMIDIDevice::ServiceStream (void *buff, int numbytes)
{
float *samples = (float *)buff;
float *samples1;
int numsamples = numbytes / sizeof(float) / 2;
bool prev_ended = false;
bool res = true;
samples1 = samples;
memset(buff, 0, numbytes);
CritSec.Enter();
while (numsamples > 0)
{
double ticky = NextTickIn;
int tick_in = int(NextTickIn);
int samplesleft = MIN(numsamples, tick_in);
if (samplesleft > 0)
{
Renderer->ComputeOutput(samples1, samplesleft);
assert(NextTickIn == ticky);
NextTickIn -= samplesleft;
assert(NextTickIn >= 0);
numsamples -= samplesleft;
samples1 += samplesleft * 2;
}
if (NextTickIn < 1)
{
int next = PlayTick();
assert(next >= 0);
if (next == 0)
{ // end of song
if (numsamples > 0)
{
Renderer->ComputeOutput(samples1, samplesleft);
}
res = false;
break;
}
else
{
NextTickIn += SamplesPerTick * next;
assert(NextTickIn >= 0);
}
}
}
CritSec.Leave();
return res;
}
//==========================================================================
//
// TimidityMIDIDevice :: FillStream static
//
//==========================================================================
bool TimidityMIDIDevice::FillStream(SoundStream *stream, void *buff, int len, void *userdata)
{
TimidityMIDIDevice *device = (TimidityMIDIDevice *)userdata;
return device->ServiceStream(buff, len);
}

58
src/timidity/CHANGES Normal file
View file

@ -0,0 +1,58 @@
This version of TiMidity should contain all the fixes from the
September 25 2003 SDL_mixer CVS snapshot, plus extended GUS patch
support from later SDL_mixer. In addition, it contains these changes
from SDL_sound:
* Removal of much unused or unnecessary code, such as
+ The "hooks" for putting a user interface onto TiMidity.
+ The antialias filter. It wasn't active, and even at 4 kHz I
couldn't hear any difference when activating it.
+ Removed all traces of LOOKUP_HACK and LOOKUP_INTERPOLATION.
According to the code comments they weren't very good anyway.
("degrades sound quality noticeably"). I also removed the
disclaimer about the "8-bit uLaw to 16-bit PCM and the 13-bit-PCM
to 8-bit uLaw tables" disclaimer, since I believe those were the
tables I removed.
+ Removed LOOKUP_SINE since it was already commented out. I think we
can count on our target audience having math co-processors
nowadays.
+ Removed USE_LDEXP since it wasn't being used and "it doesn't make
much of a difference either way".
+ Removed decompress hack from open_file() since it didn't look very
portable.
+ Removed heaps of unnecessary constants.
+ Removed unused functions.
+ Assume that LINEAR_INTERPOLATION is always used, so remove all
code dealing with it not being so. It's not that I think the
difference in audio quality is that great, but since it wouldn't
compile without code changes I assume no one's used it for quite
some time...
+ Assume PRECALC_LOOPS is always defined. Judging by the comments it
may not make much of a difference either way, so why maintain two
versions of the same code?
* Made TiMidity look for its configuration file in both /etc and
/usr/local/lib/timidity. (Windows version remains unchanged.)
* The following files have been removed: controls.c, controls.h,
filter.c, filter.h, sdl_a.c, sdl_c.c
* Added support for loading DLS format instruments:
Timidity_LoadDLS(), Timidity_FreeDLS(), Timidity_LoadDLSSong()
This version of TiMidity also contains my own changes for ZDoom:
* Removed readmidi.c: TiMidity is now fed MIDI events directly to
produce output. The TimidityMIDIDevice class is responsible for
feeding TiMidity data and collecting output from it. Since
ZDoom's MIDI parser ignores SysEx messages, so does this TiMidity,
though this can be changed if necessary.
* Removed all the precalculated math from tables.c in favor of using
real math functions.
* All sample values are now floats, and only a stereo 32-bit float
output buffer is supported.
* Moved everything into the Timidity namespace.

513
src/timidity/COPYING Normal file
View file

@ -0,0 +1,513 @@
Please note that the included source from Timidity, the MIDI decoder, is also
licensed under the following terms (GNU LGPL), but can also be used
separately under the GNU GPL, or the Perl Artistic License. Those licensing
terms are not reprinted here, but can be found on the web easily.
-------------------
GNU LESSER GENERAL PUBLIC LICENSE
Version 2.1, February 1999
Copyright (C) 1991, 1999 Free Software Foundation, Inc.
59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
[This is the first released version of the Lesser GPL. It also counts
as the successor of the GNU Library Public License, version 2, hence
the version number 2.1.]
Preamble
The licenses for most software are designed to take away your
freedom to share and change it. By contrast, the GNU General Public
Licenses are intended to guarantee your freedom to share and change
free software--to make sure the software is free for all its users.
This license, the Lesser General Public License, applies to some
specially designated software packages--typically libraries--of the
Free Software Foundation and other authors who decide to use it. You
can use it too, but we suggest you first think carefully about whether
this license or the ordinary General Public License is the better
strategy to use in any particular case, based on the explanations below.
When we speak of free software, we are referring to freedom of use,
not price. Our General Public Licenses are designed to make sure that
you have the freedom to distribute copies of free software (and charge
for this service if you wish); that you receive source code or can get
it if you want it; that you can change the software and use pieces of
it in new free programs; and that you are informed that you can do
these things.
To protect your rights, we need to make restrictions that forbid
distributors to deny you these rights or to ask you to surrender these
rights. These restrictions translate to certain responsibilities for
you if you distribute copies of the library or if you modify it.
For example, if you distribute copies of the library, whether gratis
or for a fee, you must give the recipients all the rights that we gave
you. You must make sure that they, too, receive or can get the source
code. If you link other code with the library, you must provide
complete object files to the recipients, so that they can relink them
with the library after making changes to the library and recompiling
it. And you must show them these terms so they know their rights.
We protect your rights with a two-step method: (1) we copyright the
library, and (2) we offer you this license, which gives you legal
permission to copy, distribute and/or modify the library.
To protect each distributor, we want to make it very clear that
there is no warranty for the free library. Also, if the library is
modified by someone else and passed on, the recipients should know
that what they have is not the original version, so that the original
author's reputation will not be affected by problems that might be
introduced by others.
Finally, software patents pose a constant threat to the existence of
any free program. We wish to make sure that a company cannot
effectively restrict the users of a free program by obtaining a
restrictive license from a patent holder. Therefore, we insist that
any patent license obtained for a version of the library must be
consistent with the full freedom of use specified in this license.
Most GNU software, including some libraries, is covered by the
ordinary GNU General Public License. This license, the GNU Lesser
General Public License, applies to certain designated libraries, and
is quite different from the ordinary General Public License. We use
this license for certain libraries in order to permit linking those
libraries into non-free programs.
When a program is linked with a library, whether statically or using
a shared library, the combination of the two is legally speaking a
combined work, a derivative of the original library. The ordinary
General Public License therefore permits such linking only if the
entire combination fits its criteria of freedom. The Lesser General
Public License permits more lax criteria for linking other code with
the library.
We call this license the "Lesser" General Public License because it
does Less to protect the user's freedom than the ordinary General
Public License. It also provides other free software developers Less
of an advantage over competing non-free programs. These disadvantages
are the reason we use the ordinary General Public License for many
libraries. However, the Lesser license provides advantages in certain
special circumstances.
For example, on rare occasions, there may be a special need to
encourage the widest possible use of a certain library, so that it becomes
a de-facto standard. To achieve this, non-free programs must be
allowed to use the library. A more frequent case is that a free
library does the same job as widely used non-free libraries. In this
case, there is little to gain by limiting the free library to free
software only, so we use the Lesser General Public License.
In other cases, permission to use a particular library in non-free
programs enables a greater number of people to use a large body of
free software. For example, permission to use the GNU C Library in
non-free programs enables many more people to use the whole GNU
operating system, as well as its variant, the GNU/Linux operating
system.
Although the Lesser General Public License is Less protective of the
users' freedom, it does ensure that the user of a program that is
linked with the Library has the freedom and the wherewithal to run
that program using a modified version of the Library.
The precise terms and conditions for copying, distribution and
modification follow. Pay close attention to the difference between a
"work based on the library" and a "work that uses the library". The
former contains code derived from the library, whereas the latter must
be combined with the library in order to run.
GNU LESSER GENERAL PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. This License Agreement applies to any software library or other
program which contains a notice placed by the copyright holder or
other authorized party saying it may be distributed under the terms of
this Lesser General Public License (also called "this License").
Each licensee is addressed as "you".
A "library" means a collection of software functions and/or data
prepared so as to be conveniently linked with application programs
(which use some of those functions and data) to form executables.
The "Library", below, refers to any such software library or work
which has been distributed under these terms. A "work based on the
Library" means either the Library or any derivative work under
copyright law: that is to say, a work containing the Library or a
portion of it, either verbatim or with modifications and/or translated
straightforwardly into another language. (Hereinafter, translation is
included without limitation in the term "modification".)
"Source code" for a work means the preferred form of the work for
making modifications to it. For a library, complete source code means
all the source code for all modules it contains, plus any associated
interface definition files, plus the scripts used to control compilation
and installation of the library.
Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope. The act of
running a program using the Library is not restricted, and output from
such a program is covered only if its contents constitute a work based
on the Library (independent of the use of the Library in a tool for
writing it). Whether that is true depends on what the Library does
and what the program that uses the Library does.
1. You may copy and distribute verbatim copies of the Library's
complete source code as you receive it, in any medium, provided that
you conspicuously and appropriately publish on each copy an
appropriate copyright notice and disclaimer of warranty; keep intact
all the notices that refer to this License and to the absence of any
warranty; and distribute a copy of this License along with the
Library.
You may charge a fee for the physical act of transferring a copy,
and you may at your option offer warranty protection in exchange for a
fee.
2. You may modify your copy or copies of the Library or any portion
of it, thus forming a work based on the Library, and copy and
distribute such modifications or work under the terms of Section 1
above, provided that you also meet all of these conditions:
a) The modified work must itself be a software library.
b) You must cause the files modified to carry prominent notices
stating that you changed the files and the date of any change.
c) You must cause the whole of the work to be licensed at no
charge to all third parties under the terms of this License.
d) If a facility in the modified Library refers to a function or a
table of data to be supplied by an application program that uses
the facility, other than as an argument passed when the facility
is invoked, then you must make a good faith effort to ensure that,
in the event an application does not supply such function or
table, the facility still operates, and performs whatever part of
its purpose remains meaningful.
(For example, a function in a library to compute square roots has
a purpose that is entirely well-defined independent of the
application. Therefore, Subsection 2d requires that any
application-supplied function or table used by this function must
be optional: if the application does not supply it, the square
root function must still compute square roots.)
These requirements apply to the modified work as a whole. If
identifiable sections of that work are not derived from the Library,
and can be reasonably considered independent and separate works in
themselves, then this License, and its terms, do not apply to those
sections when you distribute them as separate works. But when you
distribute the same sections as part of a whole which is a work based
on the Library, the distribution of the whole must be on the terms of
this License, whose permissions for other licensees extend to the
entire whole, and thus to each and every part regardless of who wrote
it.
Thus, it is not the intent of this section to claim rights or contest
your rights to work written entirely by you; rather, the intent is to
exercise the right to control the distribution of derivative or
collective works based on the Library.
In addition, mere aggregation of another work not based on the Library
with the Library (or with a work based on the Library) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.
3. You may opt to apply the terms of the ordinary GNU General Public
License instead of this License to a given copy of the Library. To do
this, you must alter all the notices that refer to this License, so
that they refer to the ordinary GNU General Public License, version 2,
instead of to this License. (If a newer version than version 2 of the
ordinary GNU General Public License has appeared, then you can specify
that version instead if you wish.) Do not make any other change in
these notices.
Once this change is made in a given copy, it is irreversible for
that copy, so the ordinary GNU General Public License applies to all
subsequent copies and derivative works made from that copy.
This option is useful when you wish to copy part of the code of
the Library into a program that is not a library.
4. You may copy and distribute the Library (or a portion or
derivative of it, under Section 2) in object code or executable form
under the terms of Sections 1 and 2 above provided that you accompany
it with the complete corresponding machine-readable source code, which
must be distributed under the terms of Sections 1 and 2 above on a
medium customarily used for software interchange.
If distribution of object code is made by offering access to copy
from a designated place, then offering equivalent access to copy the
source code from the same place satisfies the requirement to
distribute the source code, even though third parties are not
compelled to copy the source along with the object code.
5. A program that contains no derivative of any portion of the
Library, but is designed to work with the Library by being compiled or
linked with it, is called a "work that uses the Library". Such a
work, in isolation, is not a derivative work of the Library, and
therefore falls outside the scope of this License.
However, linking a "work that uses the Library" with the Library
creates an executable that is a derivative of the Library (because it
contains portions of the Library), rather than a "work that uses the
library". The executable is therefore covered by this License.
Section 6 states terms for distribution of such executables.
When a "work that uses the Library" uses material from a header file
that is part of the Library, the object code for the work may be a
derivative work of the Library even though the source code is not.
Whether this is true is especially significant if the work can be
linked without the Library, or if the work is itself a library. The
threshold for this to be true is not precisely defined by law.
If such an object file uses only numerical parameters, data
structure layouts and accessors, and small macros and small inline
functions (ten lines or less in length), then the use of the object
file is unrestricted, regardless of whether it is legally a derivative
work. (Executables containing this object code plus portions of the
Library will still fall under Section 6.)
Otherwise, if the work is a derivative of the Library, you may
distribute the object code for the work under the terms of Section 6.
Any executables containing that work also fall under Section 6,
whether or not they are linked directly with the Library itself.
6. As an exception to the Sections above, you may also combine or
link a "work that uses the Library" with the Library to produce a
work containing portions of the Library, and distribute that work
under terms of your choice, provided that the terms permit
modification of the work for the customer's own use and reverse
engineering for debugging such modifications.
You must give prominent notice with each copy of the work that the
Library is used in it and that the Library and its use are covered by
this License. You must supply a copy of this License. If the work
during execution displays copyright notices, you must include the
copyright notice for the Library among them, as well as a reference
directing the user to the copy of this License. Also, you must do one
of these things:
a) Accompany the work with the complete corresponding
machine-readable source code for the Library including whatever
changes were used in the work (which must be distributed under
Sections 1 and 2 above); and, if the work is an executable linked
with the Library, with the complete machine-readable "work that
uses the Library", as object code and/or source code, so that the
user can modify the Library and then relink to produce a modified
executable containing the modified Library. (It is understood
that the user who changes the contents of definitions files in the
Library will not necessarily be able to recompile the application
to use the modified definitions.)
b) Use a suitable shared library mechanism for linking with the
Library. A suitable mechanism is one that (1) uses at run time a
copy of the library already present on the user's computer system,
rather than copying library functions into the executable, and (2)
will operate properly with a modified version of the library, if
the user installs one, as long as the modified version is
interface-compatible with the version that the work was made with.
c) Accompany the work with a written offer, valid for at
least three years, to give the same user the materials
specified in Subsection 6a, above, for a charge no more
than the cost of performing this distribution.
d) If distribution of the work is made by offering access to copy
from a designated place, offer equivalent access to copy the above
specified materials from the same place.
e) Verify that the user has already received a copy of these
materials or that you have already sent this user a copy.
For an executable, the required form of the "work that uses the
Library" must include any data and utility programs needed for
reproducing the executable from it. However, as a special exception,
the materials to be distributed need not include anything that is
normally distributed (in either source or binary form) with the major
components (compiler, kernel, and so on) of the operating system on
which the executable runs, unless that component itself accompanies
the executable.
It may happen that this requirement contradicts the license
restrictions of other proprietary libraries that do not normally
accompany the operating system. Such a contradiction means you cannot
use both them and the Library together in an executable that you
distribute.
7. You may place library facilities that are a work based on the
Library side-by-side in a single library together with other library
facilities not covered by this License, and distribute such a combined
library, provided that the separate distribution of the work based on
the Library and of the other library facilities is otherwise
permitted, and provided that you do these two things:
a) Accompany the combined library with a copy of the same work
based on the Library, uncombined with any other library
facilities. This must be distributed under the terms of the
Sections above.
b) Give prominent notice with the combined library of the fact
that part of it is a work based on the Library, and explaining
where to find the accompanying uncombined form of the same work.
8. You may not copy, modify, sublicense, link with, or distribute
the Library except as expressly provided under this License. Any
attempt otherwise to copy, modify, sublicense, link with, or
distribute the Library is void, and will automatically terminate your
rights under this License. However, parties who have received copies,
or rights, from you under this License will not have their licenses
terminated so long as such parties remain in full compliance.
9. You are not required to accept this License, since you have not
signed it. However, nothing else grants you permission to modify or
distribute the Library or its derivative works. These actions are
prohibited by law if you do not accept this License. Therefore, by
modifying or distributing the Library (or any work based on the
Library), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Library or works based on it.
10. Each time you redistribute the Library (or any work based on the
Library), the recipient automatically receives a license from the
original licensor to copy, distribute, link with or modify the Library
subject to these terms and conditions. You may not impose any further
restrictions on the recipients' exercise of the rights granted herein.
You are not responsible for enforcing compliance by third parties with
this License.
11. If, as a consequence of a court judgment or allegation of patent
infringement or for any other reason (not limited to patent issues),
conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot
distribute so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you
may not distribute the Library at all. For example, if a patent
license would not permit royalty-free redistribution of the Library by
all those who receive copies directly or indirectly through you, then
the only way you could satisfy both it and this License would be to
refrain entirely from distribution of the Library.
If any portion of this section is held invalid or unenforceable under any
particular circumstance, the balance of the section is intended to apply,
and the section as a whole is intended to apply in other circumstances.
It is not the purpose of this section to induce you to infringe any
patents or other property right claims or to contest validity of any
such claims; this section has the sole purpose of protecting the
integrity of the free software distribution system which is
implemented by public license practices. Many people have made
generous contributions to the wide range of software distributed
through that system in reliance on consistent application of that
system; it is up to the author/donor to decide if he or she is willing
to distribute software through any other system and a licensee cannot
impose that choice.
This section is intended to make thoroughly clear what is believed to
be a consequence of the rest of this License.
12. If the distribution and/or use of the Library is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Library under this License may add
an explicit geographical distribution limitation excluding those countries,
so that distribution is permitted only in or among countries not thus
excluded. In such case, this License incorporates the limitation as if
written in the body of this License.
13. The Free Software Foundation may publish revised and/or new
versions of the Lesser General Public License from time to time.
Such new versions will be similar in spirit to the present version,
but may differ in detail to address new problems or concerns.
Each version is given a distinguishing version number. If the Library
specifies a version number of this License which applies to it and
"any later version", you have the option of following the terms and
conditions either of that version or of any later version published by
the Free Software Foundation. If the Library does not specify a
license version number, you may choose any version ever published by
the Free Software Foundation.
14. If you wish to incorporate parts of the Library into other free
programs whose distribution conditions are incompatible with these,
write to the author to ask for permission. For software which is
copyrighted by the Free Software Foundation, write to the Free
Software Foundation; we sometimes make exceptions for this. Our
decision will be guided by the two goals of preserving the free status
of all derivatives of our free software and of promoting the sharing
and reuse of software generally.
NO WARRANTY
15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
DAMAGES.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Libraries
If you develop a new library, and you want it to be of the greatest
possible use to the public, we recommend making it free software that
everyone can redistribute and change. You can do so by permitting
redistribution under these terms (or, alternatively, under the terms of the
ordinary General Public License).
To apply these terms, attach the following notices to the library. It is
safest to attach them to the start of each source file to most effectively
convey the exclusion of warranty; and each file should have at least the
"copyright" line and a pointer to where the full notice is found.
<one line to give the library's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library 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
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
Also add information on how to contact you by electronic and paper mail.
You should also get your employer (if you work as a programmer) or your
school, if any, to sign a "copyright disclaimer" for the library, if
necessary. Here is a sample; alter the names:
Yoyodyne, Inc., hereby disclaims all copyright interest in the
library `Frob' (a library for tweaking knobs) written by James Random Hacker.
<signature of Ty Coon>, 1 April 1990
Ty Coon, President of Vice
That's all there is to it!

100
src/timidity/FAQ Normal file
View file

@ -0,0 +1,100 @@
---------------------------*-indented-text-*------------------------------
TiMidity -- Experimental MIDI to WAVE converter
Copyright (C) 1995 Tuukka Toivonen <toivonen@clinet.fi>
--------------------------------------------------------------------------
Frequently Asked Questions with answers:
--------------------------------------------------------------------------
Q: What is it?
A: Where? Well Chris, TiMidity is a software-only synthesizer, MIDI
renderer, MIDI to WAVE converter, realtime MIDI player for UNIX machines,
even (I've heard) a Netscape helper application. It takes a MIDI file
and writes a WAVE or raw PCM data or plays it on your digital audio
device. It sounds much more realistic than FM synthesis, but you need a
~100Mhz processor to listen to 32kHz stereo music in the background while
you work. 11kHz mono can be played on a low-end 486, and, to some, it
still sounds better than FM.
--------------------------------------------------------------------------
Q: I don't have a GUS, can I use TiMidity?
A: Yes. That's the point. You don't need a Gravis Ultrasound to use
TiMidity, you just need GUS-compatible patches, which are freely
available on the Internet. See below for pointers.
--------------------------------------------------------------------------
Q: I have a GUS, can I use TiMidity?
A: The DOS port doesn't have GUS support, and TiMidity won't be taking
advantage of the board's internal synthesizer under other operating
systems either. So it kind of defeats the purpose. But you can use it.
--------------------------------------------------------------------------
Q: I tried playing a MIDI file I got off the Net but all I got was a
dozen warnings saying "No instrument mapped to tone bank 0, program
xx - this instrument will not be heard". What's wrong?
A: The General MIDI standard specifies 128 melodic instruments and
some sixty percussion sounds. If you wish to play arbitrary General
MIDI files, you'll need to get more patch files.
There's a program called Midia for SGI's, which also plays MIDI
files and has a lot more bells and whistles than TiMidity. It uses
GUS-compatible patches, too -- so you can get the 8 MB set at
ftp://archive.cs.umbc.edu/pub/midia for pretty good GM compatibility.
There are also many excellent patches on the Ultrasound FTP sites.
I can recommend Dustin McCartney's collections gsdrum*.zip and
wow*.zip in the "[.../]sound/patches/files" directory. The huge
ProPats series (pp3-*.zip) contains good patches as well. General
MIDI files can also be found on these sites.
This site list is from the GUS FAQ:
> FTP Sites Archive Directories
> --------- -------------------
> Main N.American Site: archive.orst.edu pub/packages/gravis
> wuarchive.wustl.edu systems/ibmpc/ultrasound
> Main Asian Site: nctuccca.edu.tw PC/ultrasound
> Main European Site: src.doc.ic.ac.uk packages/ultrasound
> Main Australian Site: ftp.mpx.com.au /ultrasound/general
> /ultrasound/submit
> South African Site: ftp.sun.ac.za /pub/packages/ultrasound
> Submissions: archive.epas.utoronto.ca pub/pc/ultrasound/submit
> Newly Validated Files: archive.epas.utoronto.ca pub/pc/ultrasound
>
> Mirrors: garbo.uwasa.fi mirror/ultrasound
> ftp.st.nepean.uws.edu.au pc/ultrasound
> ftp.luth.se pub/msdos/ultrasound
--------------------------------------------------------------------------
Q: Some files have awful clicks and pops.
A: Find out which patch is responsible for the clicking (try "timidity
-P<patch> <midi/test-decay|midi/test-panning>". Add "strip=tail" in
the config file after its name. If this doesn't fix it, mail me the
patch.
--------------------------------------------------------------------------
Q: I'm playing Fantasie Impromptu in the background. When I run Netscape,
the sound gets choppy and it takes ten minutes to load. What can I do?
A: Here are some things to try:
- Use a lower sampling rate.
- Use mono output. This can improve performance by 10-30%.
(Using 8-bit instead of 16-bit output makes no difference.)
- Use a smaller number of simultaneous voices.
- Make sure you compiled with FAST_DECAY enabled in options.h
- Recompile with an Intel-optimized gcc for a 5-15%
performance increase.
--------------------------------------------------------------------------

61
src/timidity/README Normal file
View file

@ -0,0 +1,61 @@
[This version of timidity has been stripped for simplicity in porting to SDL,
and then even further for SDL_sound]
---------------------------------*-text-*---------------------------------
From http://www.cgs.fi/~tt/discontinued.html :
If you'd like to continue hacking on TiMidity, feel free. I'm
hereby extending the TiMidity license agreement: you can now
select the most convenient license for your needs from (1) the
GNU GPL, (2) the GNU LGPL, or (3) the Perl Artistic License.
--------------------------------------------------------------------------
This is the README file for TiMidity v0.2i
TiMidity is a MIDI to WAVE converter that uses Gravis
Ultrasound(*)-compatible patch files to generate digital audio data
from General MIDI files. The audio data can be played through any
sound device or stored on disk. On a fast machine, music can be
played in real time. TiMidity runs under Linux, FreeBSD, HP-UX, SunOS, and
Win32, and porting to other systems with gcc should be easy.
TiMidity Features:
* 32 or more dynamically allocated fully independent voices
* Compatibility with GUS patch files
* Output to 16- or 8-bit PCM or uLaw audio device, file, or
stdout at any sampling rate
* Optional interactive mode with real-time status display
under ncurses and SLang terminal control libraries. Also
a user friendly motif interface since version 0.2h
* Support for transparent loading of compressed MIDI files and
patch files
* Support for the following MIDI events:
- Program change
- Key pressure
- Channel main volume
- Tempo
- Panning
- Damper pedal (Sustain)
- Pitch wheel
- Pitch wheel sensitivity
- Change drum set
* The GNU General Public License can, as always, be found in the file
"../COPYING".
* TiMidity requires sampled instruments (patches) to play MIDI files. You
should get the file "timidity-lib-0.1.tar.gz" and unpack it in the same
directory where you unpacked the source code archive. You'll want more
patches later -- read the file "FAQ" for pointers.
* Timidity is no longer supported, but can be found by searching the web.
Tuukka Toivonen <toivonen@clinet.fi>
[(*) Any Registered Trademarks used anywhere in the documentation or
source code for TiMidity are acknowledged as belonging to their
respective owners.]

152
src/timidity/common.cpp Normal file
View file

@ -0,0 +1,152 @@
/*
TiMidity -- Experimental MIDI to WAVE converter
Copyright (C) 1995 Tuukka Toivonen <toivonen@clinet.fi>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library 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
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
common.c
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include "timidity.h"
#include "zstring.h"
#include "tarray.h"
#include "i_system.h"
namespace Timidity
{
/* I guess "rb" should be right for any libc */
#define OPEN_MODE "rb"
FString current_filename;
static TArray<FString> PathList;
/* Try to open a file for reading. */
static FILE *try_to_open(const char *name, int decompress, int noise_mode)
{
FILE *fp;
fp = fopen(name, OPEN_MODE);
if (!fp)
return 0;
return fp;
}
/* This is meant to find and open files for reading. */
FILE *open_file(const char *name, int decompress, int noise_mode)
{
FILE *fp;
if (!name || !(*name))
{
return 0;
}
/* First try the given name */
current_filename = name;
if ((fp = try_to_open(current_filename, decompress, noise_mode)))
return fp;
#ifdef ENOENT
if (noise_mode && (errno != ENOENT))
{
return 0;
}
#endif
if (name[0] != '/'
#ifdef _WIN32
&& name[0] != '\\'
#endif
)
{
for (unsigned int plp = PathList.Size(); plp-- != 0; )
{ /* Try along the path then */
current_filename = "";
if (PathList[plp].IsNotEmpty())
{
current_filename = PathList[plp];
if (current_filename[current_filename.Len() - 1] != '/'
#ifdef _WIN32
&& current_filename[current_filename.Len() - 1] != '\\'
#endif
)
{
current_filename += '/';
}
}
current_filename += name;
if ((fp = try_to_open(current_filename, decompress, noise_mode)))
return fp;
if (noise_mode && (errno != ENOENT))
{
return 0;
}
}
}
/* Nothing could be opened. */
current_filename = "";
return 0;
}
/* This closes files opened with open_file */
void close_file(FILE *fp)
{
fclose(fp);
}
/* This is meant for skipping a few bytes in a file or fifo. */
void skip(FILE *fp, size_t len)
{
fseek(fp, (long)len, SEEK_CUR);
}
/* This'll allocate memory or die. */
void *safe_malloc(size_t count)
{
void *p;
if (count > (1 << 21))
{
I_Error("Timidity: Tried allocating %d bytes. This must be a bug.", count);
}
else if ((p = malloc(count)))
{
return p;
}
else
{
I_Error("Timidity: Couldn't malloc %d bytes.", count);
}
return 0; // Unreachable.
}
/* This adds a directory to the path list */
void add_to_pathlist(const char *s)
{
PathList.Push(s);
}
}

266
src/timidity/dls1.h Normal file
View file

@ -0,0 +1,266 @@
/*==========================================================================;
//
// dls1.h
//
//
// Description:
//
// Interface defines and structures for the Instrument Collection Form
// RIFF DLS.
//
//
// Written by Sonic Foundry 1996. Released for public use.
//
//=========================================================================*/
#ifndef _INC_DLS1
#define _INC_DLS1
/*//////////////////////////////////////////////////////////////////////////
//
//
// Layout of an instrument collection:
//
//
// RIFF [] 'DLS ' [dlid,colh,INSTLIST,WAVEPOOL,INFOLIST]
//
// INSTLIST
// LIST [] 'lins'
// LIST [] 'ins ' [dlid,insh,RGNLIST,ARTLIST,INFOLIST]
// LIST [] 'ins ' [dlid,insh,RGNLIST,ARTLIST,INFOLIST]
// LIST [] 'ins ' [dlid,insh,RGNLIST,ARTLIST,INFOLIST]
//
// RGNLIST
// LIST [] 'lrgn'
// LIST [] 'rgn ' [rgnh,wsmp,wlnk,ARTLIST]
// LIST [] 'rgn ' [rgnh,wsmp,wlnk,ARTLIST]
// LIST [] 'rgn ' [rgnh,wsmp,wlnk,ARTLIST]
//
// ARTLIST
// LIST [] 'lart'
// 'art1' level 1 Articulation connection graph
// 'art2' level 2 Articulation connection graph
// '3rd1' Possible 3rd party articulation structure 1
// '3rd2' Possible 3rd party articulation structure 2 .... and so on
//
// WAVEPOOL
// ptbl [] [pool table]
// LIST [] 'wvpl'
// [path],
// [path],
// LIST [] 'wave' [dlid,RIFFWAVE]
// LIST [] 'wave' [dlid,RIFFWAVE]
// LIST [] 'wave' [dlid,RIFFWAVE]
// LIST [] 'wave' [dlid,RIFFWAVE]
// LIST [] 'wave' [dlid,RIFFWAVE]
//
// INFOLIST
// LIST [] 'INFO'
// 'icmt' 'One of those crazy comments.'
// 'icop' 'Copyright (C) 1996 Sonic Foundry'
//
/////////////////////////////////////////////////////////////////////////*/
/*/////////////////////////////////////////////////////////////////////////
// FOURCC's used in the DLS file
/////////////////////////////////////////////////////////////////////////*/
#define FOURCC_DLS mmioFOURCC('D','L','S',' ')
#define FOURCC_DLID mmioFOURCC('d','l','i','d')
#define FOURCC_COLH mmioFOURCC('c','o','l','h')
#define FOURCC_WVPL mmioFOURCC('w','v','p','l')
#define FOURCC_PTBL mmioFOURCC('p','t','b','l')
#define FOURCC_PATH mmioFOURCC('p','a','t','h')
#define FOURCC_wave mmioFOURCC('w','a','v','e')
#define FOURCC_LINS mmioFOURCC('l','i','n','s')
#define FOURCC_INS mmioFOURCC('i','n','s',' ')
#define FOURCC_INSH mmioFOURCC('i','n','s','h')
#define FOURCC_LRGN mmioFOURCC('l','r','g','n')
#define FOURCC_RGN mmioFOURCC('r','g','n',' ')
#define FOURCC_RGNH mmioFOURCC('r','g','n','h')
#define FOURCC_LART mmioFOURCC('l','a','r','t')
#define FOURCC_ART1 mmioFOURCC('a','r','t','1')
#define FOURCC_WLNK mmioFOURCC('w','l','n','k')
#define FOURCC_WSMP mmioFOURCC('w','s','m','p')
#define FOURCC_VERS mmioFOURCC('v','e','r','s')
/*/////////////////////////////////////////////////////////////////////////
// Articulation connection graph definitions
/////////////////////////////////////////////////////////////////////////*/
/* Generic Sources */
#define CONN_SRC_NONE 0x0000
#define CONN_SRC_LFO 0x0001
#define CONN_SRC_KEYONVELOCITY 0x0002
#define CONN_SRC_KEYNUMBER 0x0003
#define CONN_SRC_EG1 0x0004
#define CONN_SRC_EG2 0x0005
#define CONN_SRC_PITCHWHEEL 0x0006
/* Midi Controllers 0-127 */
#define CONN_SRC_CC1 0x0081
#define CONN_SRC_CC7 0x0087
#define CONN_SRC_CC10 0x008a
#define CONN_SRC_CC11 0x008b
/* Generic Destinations */
#define CONN_DST_NONE 0x0000
#define CONN_DST_ATTENUATION 0x0001
#define CONN_DST_PITCH 0x0003
#define CONN_DST_PAN 0x0004
/* LFO Destinations */
#define CONN_DST_LFO_FREQUENCY 0x0104
#define CONN_DST_LFO_STARTDELAY 0x0105
/* EG1 Destinations */
#define CONN_DST_EG1_ATTACKTIME 0x0206
#define CONN_DST_EG1_DECAYTIME 0x0207
#define CONN_DST_EG1_RELEASETIME 0x0209
#define CONN_DST_EG1_SUSTAINLEVEL 0x020a
/* EG2 Destinations */
#define CONN_DST_EG2_ATTACKTIME 0x030a
#define CONN_DST_EG2_DECAYTIME 0x030b
#define CONN_DST_EG2_RELEASETIME 0x030d
#define CONN_DST_EG2_SUSTAINLEVEL 0x030e
#define CONN_TRN_NONE 0x0000
#define CONN_TRN_CONCAVE 0x0001
typedef struct _DLSID {
ULONG ulData1;
USHORT usData2;
USHORT usData3;
BYTE abData4[8];
} DLSID, FAR *LPDLSID;
typedef struct _DLSVERSION {
DWORD dwVersionMS;
DWORD dwVersionLS;
} DLSVERSION, FAR *LPDLSVERSION;
typedef struct _CONNECTION {
USHORT usSource;
USHORT usControl;
USHORT usDestination;
USHORT usTransform;
LONG lScale;
} CONNECTION, FAR *LPCONNECTION;
/* Level 1 Articulation Data */
typedef struct _CONNECTIONLIST {
ULONG cbSize; /* size of the connection list structure */
ULONG cConnections; /* count of connections in the list */
} CONNECTIONLIST, FAR *LPCONNECTIONLIST;
/*/////////////////////////////////////////////////////////////////////////
// Generic type defines for regions and instruments
/////////////////////////////////////////////////////////////////////////*/
typedef struct _RGNRANGE {
USHORT usLow;
USHORT usHigh;
} RGNRANGE, FAR * LPRGNRANGE;
#define F_INSTRUMENT_DRUMS 0x80000000
typedef struct _MIDILOCALE {
ULONG ulBank;
ULONG ulInstrument;
} MIDILOCALE, FAR *LPMIDILOCALE;
/*/////////////////////////////////////////////////////////////////////////
// Header structures found in an DLS file for collection, instruments, and
// regions.
/////////////////////////////////////////////////////////////////////////*/
#define F_RGN_OPTION_SELFNONEXCLUSIVE 0x0001
typedef struct _RGNHEADER {
RGNRANGE RangeKey; /* Key range */
RGNRANGE RangeVelocity; /* Velocity Range */
USHORT fusOptions; /* Synthesis options for this range */
USHORT usKeyGroup; /* Key grouping for non simultaneous play */
/* 0 = no group, 1 up is group */
/* for Level 1 only groups 1-15 are allowed */
} RGNHEADER, FAR *LPRGNHEADER;
typedef struct _INSTHEADER {
ULONG cRegions; /* Count of regions in this instrument */
MIDILOCALE Locale; /* Intended MIDI locale of this instrument */
} INSTHEADER, FAR *LPINSTHEADER;
typedef struct _DLSHEADER {
ULONG cInstruments; /* Count of instruments in the collection */
} DLSHEADER, FAR *LPDLSHEADER;
/*////////////////////////////////////////////////////////////////////////////
// definitions for the Wave link structure
////////////////////////////////////////////////////////////////////////////*/
/* **** For level 1 only WAVELINK_CHANNEL_MONO is valid **** */
/* ulChannel allows for up to 32 channels of audio with each bit position */
/* specifiying a channel of playback */
#define WAVELINK_CHANNEL_LEFT 0x0001l
#define WAVELINK_CHANNEL_RIGHT 0x0002l
#define F_WAVELINK_PHASE_MASTER 0x0001
typedef struct _WAVELINK { /* any paths or links are stored right after struct */
USHORT fusOptions; /* options flags for this wave */
USHORT usPhaseGroup; /* Phase grouping for locking channels */
ULONG ulChannel; /* channel placement */
ULONG ulTableIndex; /* index into the wave pool table, 0 based */
} WAVELINK, FAR *LPWAVELINK;
#define POOL_CUE_NULL 0xffffffffl
typedef struct _POOLCUE {
ULONG ulOffset; /* Offset to the entry in the list */
} POOLCUE, FAR *LPPOOLCUE;
typedef struct _POOLTABLE {
ULONG cbSize; /* size of the pool table structure */
ULONG cCues; /* count of cues in the list */
} POOLTABLE, FAR *LPPOOLTABLE;
/*////////////////////////////////////////////////////////////////////////////
// Structures for the "wsmp" chunk
////////////////////////////////////////////////////////////////////////////*/
#define F_WSMP_NO_TRUNCATION 0x0001l
#define F_WSMP_NO_COMPRESSION 0x0002l
typedef struct _rwsmp {
ULONG cbSize;
USHORT usUnityNote; /* MIDI Unity Playback Note */
SHORT sFineTune; /* Fine Tune in log tuning */
LONG lAttenuation; /* Overall Attenuation to be applied to data */
ULONG fulOptions; /* Flag options */
ULONG cSampleLoops; /* Count of Sample loops, 0 loops is one shot */
} WSMPL, FAR *LPWSMPL;
/* This loop type is a normal forward playing loop which is continually */
/* played until the envelope reaches an off threshold in the release */
/* portion of the volume envelope */
#define WLOOP_TYPE_FORWARD 0
typedef struct _rloop {
ULONG cbSize;
ULONG ulType; /* Loop Type */
ULONG ulStart; /* Start of loop in samples */
ULONG ulLength; /* Length of loop in samples */
} WLOOP, FAR *LPWLOOP;
#endif /*_INC_DLS1 */

130
src/timidity/dls2.h Normal file
View file

@ -0,0 +1,130 @@
/*
dls2.h
Description:
Interface defines and structures for the DLS2 extensions of DLS.
Written by Microsoft 1998. Released for public use.
*/
#ifndef _INC_DLS2
#define _INC_DLS2
/*
FOURCC's used in the DLS2 file, in addition to DLS1 chunks
*/
#define FOURCC_RGN2 mmioFOURCC('r','g','n','2')
#define FOURCC_LAR2 mmioFOURCC('l','a','r','2')
#define FOURCC_ART2 mmioFOURCC('a','r','t','2')
#define FOURCC_CDL mmioFOURCC('c','d','l',' ')
#define FOURCC_DLID mmioFOURCC('d','l','i','d')
/*
Articulation connection graph definitions. These are in addition to
the definitions in the DLS1 header.
*/
/* Generic Sources (in addition to DLS1 sources. */
#define CONN_SRC_POLYPRESSURE 0x0007 /* Polyphonic Pressure */
#define CONN_SRC_CHANNELPRESSURE 0x0008 /* Channel Pressure */
#define CONN_SRC_VIBRATO 0x0009 /* Vibrato LFO */
#define CONN_SRC_MONOPRESSURE 0x000a /* MIDI Mono pressure */
/* Midi Controllers */
#define CONN_SRC_CC91 0x00db /* Reverb Send */
#define CONN_SRC_CC93 0x00dd /* Chorus Send */
/* Generic Destinations */
#define CONN_DST_GAIN 0x0001 /* Same as CONN_DST_ ATTENUATION, but more appropriate terminology. */
#define CONN_DST_KEYNUMBER 0x0005 /* Key Number Generator */
/* Audio Channel Output Destinations */
#define CONN_DST_LEFT 0x0010 /* Left Channel Send */
#define CONN_DST_RIGHT 0x0011 /* Right Channel Send */
#define CONN_DST_CENTER 0x0012 /* Center Channel Send */
#define CONN_DST_LEFTREAR 0x0013 /* Left Rear Channel Send */
#define CONN_DST_RIGHTREAR 0x0014 /* Right Rear Channel Send */
#define CONN_DST_LFE_CHANNEL 0x0015 /* LFE Channel Send */
#define CONN_DST_CHORUS 0x0080 /* Chorus Send */
#define CONN_DST_REVERB 0x0081 /* Reverb Send */
/* Vibrato LFO Destinations */
#define CONN_DST_VIB_FREQUENCY 0x0114 /* Vibrato Frequency */
#define CONN_DST_VIB_STARTDELAY 0x0115 /* Vibrato Start Delay */
/* EG1 Destinations */
#define CONN_DST_EG1_DELAYTIME 0x020B /* EG1 Delay Time */
#define CONN_DST_EG1_HOLDTIME 0x020C /* EG1 Hold Time */
#define CONN_DST_EG1_SHUTDOWNTIME 0x020D /* EG1 Shutdown Time */
/* EG2 Destinations */
#define CONN_DST_EG2_DELAYTIME 0x030F /* EG2 Delay Time */
#define CONN_DST_EG2_HOLDTIME 0x0310 /* EG2 Hold Time */
/* Filter Destinations */
#define CONN_DST_FILTER_CUTOFF 0x0500 /* Filter Cutoff Frequency */
#define CONN_DST_FILTER_Q 0x0501 /* Filter Resonance */
/* Transforms */
#define CONN_TRN_CONVEX 0x0002 /* Convex Transform */
#define CONN_TRN_SWITCH 0x0003 /* Switch Transform */
/* Conditional chunk operators */
#define DLS_CDL_AND 0x0001 /* X = X & Y */
#define DLS_CDL_OR 0x0002 /* X = X | Y */
#define DLS_CDL_XOR 0x0003 /* X = X ^ Y */
#define DLS_CDL_ADD 0x0004 /* X = X + Y */
#define DLS_CDL_SUBTRACT 0x0005 /* X = X - Y */
#define DLS_CDL_MULTIPLY 0x0006 /* X = X * Y */
#define DLS_CDL_DIVIDE 0x0007 /* X = X / Y */
#define DLS_CDL_LOGICAL_AND 0x0008 /* X = X && Y */
#define DLS_CDL_LOGICAL_OR 0x0009 /* X = X || Y */
#define DLS_CDL_LT 0x000A /* X = (X < Y) */
#define DLS_CDL_LE 0x000B /* X = (X <= Y) */
#define DLS_CDL_GT 0x000C /* X = (X > Y) */
#define DLS_CDL_GE 0x000D /* X = (X >= Y) */
#define DLS_CDL_EQ 0x000E /* X = (X == Y) */
#define DLS_CDL_NOT 0x000F /* X = !X */
#define DLS_CDL_CONST 0x0010 /* 32-bit constant */
#define DLS_CDL_QUERY 0x0011 /* 32-bit value returned from query */
#define DLS_CDL_QUERYSUPPORTED 0x0012 /* Test to see if query is supported by synth */
/*
Loop and release
*/
#define WLOOP_TYPE_RELEASE 1
/*
WaveLink chunk <wlnk-ck>
*/
#define F_WAVELINK_MULTICHANNEL 0x0002
/*
DLSID queries for <cdl-ck>
*/
DEFINE_GUID(DLSID_GMInHardware, 0x178f2f24, 0xc364, 0x11d1, 0xa7, 0x60, 0x00, 0x00, 0xf8, 0x75, 0xac, 0x12);
DEFINE_GUID(DLSID_GSInHardware, 0x178f2f25, 0xc364, 0x11d1, 0xa7, 0x60, 0x00, 0x00, 0xf8, 0x75, 0xac, 0x12);
DEFINE_GUID(DLSID_XGInHardware, 0x178f2f26, 0xc364, 0x11d1, 0xa7, 0x60, 0x00, 0x00, 0xf8, 0x75, 0xac, 0x12);
DEFINE_GUID(DLSID_SupportsDLS1, 0x178f2f27, 0xc364, 0x11d1, 0xa7, 0x60, 0x00, 0x00, 0xf8, 0x75, 0xac, 0x12);
DEFINE_GUID(DLSID_SupportsDLS2, 0xf14599e5, 0x4689, 0x11d2, 0xaf, 0xa6, 0x0, 0xaa, 0x0, 0x24, 0xd8, 0xb6);
DEFINE_GUID(DLSID_SampleMemorySize, 0x178f2f28, 0xc364, 0x11d1, 0xa7, 0x60, 0x00, 0x00, 0xf8, 0x75, 0xac, 0x12);
DEFINE_GUID(DLSID_ManufacturersID, 0xb03e1181, 0x8095, 0x11d2, 0xa1, 0xef, 0x0, 0x60, 0x8, 0x33, 0xdb, 0xd8);
DEFINE_GUID(DLSID_ProductID, 0xb03e1182, 0x8095, 0x11d2, 0xa1, 0xef, 0x0, 0x60, 0x8, 0x33, 0xdb, 0xd8);
DEFINE_GUID(DLSID_SamplePlaybackRate, 0x2a91f713, 0xa4bf, 0x11d2, 0xbb, 0xdf, 0x0, 0x60, 0x8, 0x33, 0xdb, 0xd8);
#endif /* _INC_DLS2 */

952
src/timidity/instrum.cpp Normal file
View file

@ -0,0 +1,952 @@
/*
TiMidity -- Experimental MIDI to WAVE converter
Copyright (C) 1995 Tuukka Toivonen <toivonen@clinet.fi>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library 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
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
instrum.c
Code to load and unload GUS-compatible instrument patches.
*/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <math.h>
#include "timidity.h"
#include "m_swap.h"
namespace Timidity
{
extern InstrumentLayer *load_instrument_dls(Renderer *song, int drum, int bank, int instrument);
/* Some functions get aggravated if not even the standard banks are
available. */
ToneBank standard_tonebank, standard_drumset;
/* This is only used for tracks that don't specify a program */
int default_program = DEFAULT_PROGRAM;
static void free_instrument(Instrument *ip)
{
Sample *sp;
int i;
if (ip == NULL)
{
return;
}
for (i = 0, sp = &(ip->sample[0]); i < ip->samples; i++, sp++)
{
if (sp->data != NULL)
{
free(sp->data);
}
}
for (i = 0, sp = &(ip->right_sample[0]); i < ip->right_samples; i++)
{
if (sp->data != NULL)
{
free(sp->data);
}
}
free(ip->sample);
if (ip->right_sample != NULL)
{
free(ip->right_sample);
}
free(ip);
}
static void free_layer(InstrumentLayer *lp)
{
InstrumentLayer *next;
for (; lp; lp = next)
{
next = lp->next;
free_instrument(lp->instrument);
free(lp);
}
}
static void free_bank(int dr, int b)
{
int i;
ToneBank *bank = ((dr) ? drumset[b] : tonebank[b]);
for (i = 0; i < MAXPROG; i++)
{
if (bank->tone[i].layer != NULL)
{
/* Not that this could ever happen, of course */
if (bank->tone[i].layer != MAGIC_LOAD_INSTRUMENT)
{
free_layer(bank->tone[i].layer);
bank->tone[i].layer = NULL;
}
}
}
}
int convert_envelope_rate_attack(Renderer *song, BYTE rate, BYTE fastness)
{
int r;
r = 3 - ((rate>>6) & 0x3);
r *= 3;
r = (int)(rate & 0x3f) << r; /* 6.9 fixed point */
/* 15.15 fixed point. */
return int(((r * 44100) / song->rate) * song->control_ratio) << 10;
}
int convert_envelope_rate(Renderer *song, BYTE rate)
{
int r;
r = 3 - ((rate>>6) & 0x3);
r *= 3;
r = (int)(rate & 0x3f) << r; /* 6.9 fixed point */
/* 15.15 fixed point. */
return int(((r * 44100) / song->rate) * song->control_ratio)
<< ((song->fast_decay) ? 10 : 9);
}
int convert_envelope_offset(BYTE offset)
{
/* This is not too good... Can anyone tell me what these values mean?
Are they GUS-style "exponential" volumes? And what does that mean? */
/* 15.15 fixed point */
return offset << (7 + 15);
}
int convert_tremolo_sweep(Renderer *song, BYTE sweep)
{
if (!sweep)
return 0;
return
int(((song->control_ratio * SWEEP_TUNING) << SWEEP_SHIFT) / (song->rate * sweep));
}
int convert_vibrato_sweep(Renderer *song, BYTE sweep, int vib_control_ratio)
{
if (!sweep)
return 0;
return
(int) (FSCALE((double) (vib_control_ratio) * SWEEP_TUNING, SWEEP_SHIFT) / (song->rate * sweep));
/* this was overflowing with seashore.pat
((vib_control_ratio * SWEEP_TUNING) << SWEEP_SHIFT) / (song->rate * sweep);
*/
}
int convert_tremolo_rate(Renderer *song, BYTE rate)
{
return
int(((song->control_ratio * rate) << RATE_SHIFT) / (TREMOLO_RATE_TUNING * song->rate));
}
int convert_vibrato_rate(Renderer *song, BYTE rate)
{
/* Return a suitable vibrato_control_ratio value */
return
int((VIBRATO_RATE_TUNING * song->rate) / (rate * 2 * VIBRATO_SAMPLE_INCREMENTS));
}
static void reverse_data(sample_t *sp, int ls, int le)
{
sample_t s, *ep = sp + le;
sp += ls;
le -= ls;
le /= 2;
while (le--)
{
s = *sp;
*sp++ = *ep;
*ep-- = s;
}
}
/*
If panning or note_to_use != -1, it will be used for all samples,
instead of the sample-specific values in the instrument file.
For note_to_use, any value <0 or >127 will be forced to 0.
For other parameters, 1 means yes, 0 means no, other values are
undefined.
TODO: do reverse loops right */
static InstrumentLayer *load_instrument(Renderer *song, const char *name, int font_type, int percussion,
int panning, int amp, int cfg_tuning, int note_to_use,
int strip_loop, int strip_envelope,
int strip_tail, int bank, int gm_num, int sf_ix)
{
InstrumentLayer *lp, *lastlp, *headlp;
Instrument *ip;
FILE *fp;
BYTE tmp[239];
int i,j;
bool noluck = false;
bool sf2flag = false;
int right_samples = 0;
int stereo_channels = 1, stereo_layer;
int vlayer_list[19][4], vlayer, vlayer_count;
if (!name) return 0;
/* Open patch file */
if ((fp = open_file(name, 1, OF_NORMAL)) == NULL)
{
/* Try with various extensions */
FString tmp = name;
tmp += ".pat";
if ((fp = open_file(tmp, 1, OF_NORMAL)) == NULL)
{
#ifdef unix // Windows isn't case-sensitive.
tmp.ToUpper();
if ((fp = open_file(tmp, 1, OF_NORMAL)) == NULL)
#endif
{
noluck = true;
}
}
}
if (noluck)
{
song->ctl->cmsg(CMSG_ERROR, VERB_NORMAL, "Instrument `%s' can't be found.", name);
return 0;
}
/*song->ctl->cmsg(CMSG_INFO, VERB_NOISY, "Loading instrument %s", current_filename);*/
/* Read some headers and do cursory sanity checks. There are loads
of magic offsets. This could be rewritten... */
if ((239 != fread(tmp, 1, 239, fp)) ||
(memcmp(tmp, "GF1PATCH110\0ID#000002", 22) &&
memcmp(tmp, "GF1PATCH100\0ID#000002", 22))) /* don't know what the
differences are */
{
song->ctl->cmsg(CMSG_ERROR, VERB_NORMAL, "%s: not an instrument", name);
return 0;
}
/* patch layout:
* bytes: info: starts at offset:
* 12 header (see above) 0
* 10 Gravis ID 12
* 60 description 22
* 1 instruments 82
* 1 voices 83
* 1 channels 84
* 2 number of waveforms 85
* 2 master volume 87
* 4 datasize 89
* 36 reserved, but now: 93
* 7 "SF2EXT\0" id 93
* 1 right samples 100
* 28 reserved 101
* 2 instrument number 129
* 16 instrument name 131
* 4 instrument size 147
* 1 number of layers 151
* 40 reserved 152
* 1 layer duplicate 192
* 1 layer number 193
* 4 layer size 194
* 1 number of samples 198
* 40 reserved 199
* 239
* THEN, for each sample, see below
*/
if (!memcmp(tmp + 93, "SF2EXT", 6))
{
sf2flag = true;
vlayer_count = tmp[152];
}
if (tmp[82] != 1 && tmp[82] != 0) /* instruments. To some patch makers, 0 means 1 */
{
song->ctl->cmsg(CMSG_ERROR, VERB_NORMAL, "Can't handle patches with %d instruments", tmp[82]);
return 0;
}
if (tmp[151] != 1 && tmp[151] != 0) /* layers. What's a layer? */
{
song->ctl->cmsg(CMSG_ERROR, VERB_NORMAL, "Can't handle instruments with %d layers", tmp[151]);
return 0;
}
if (sf2flag && vlayer_count > 0)
{
for (i = 0; i < 9; i++)
for (j = 0; j < 4; j++)
vlayer_list[i][j] = tmp[153+i*4+j];
for (i = 9; i < 19; i++)
for (j = 0; j < 4; j++)
vlayer_list[i][j] = tmp[199+(i-9)*4+j];
}
else
{
for (i = 0; i < 19; i++)
for (j = 0; j < 4; j++)
vlayer_list[i][j] = 0;
vlayer_list[0][0] = 0;
vlayer_list[0][1] = 127;
vlayer_list[0][2] = tmp[198];
vlayer_list[0][3] = 0;
vlayer_count = 1;
}
lastlp = 0;
for (vlayer = 0; vlayer < vlayer_count; vlayer++)
{
lp = (InstrumentLayer *)safe_malloc(sizeof(InstrumentLayer));
lp->lo = vlayer_list[vlayer][0];
lp->hi = vlayer_list[vlayer][1];
ip = (Instrument *)safe_malloc(sizeof(Instrument));
lp->instrument = ip;
lp->next = 0;
if (lastlp != NULL)
{
lastlp->next = lp;
}
else
{
headlp = lp;
}
lastlp = lp;
ip->type = sf2flag ? INST_SF2 : INST_GUS;
ip->samples = vlayer_list[vlayer][2];
ip->sample = (Sample *)safe_malloc(sizeof(Sample) * ip->samples);
ip->left_samples = ip->samples;
ip->left_sample = ip->sample;
right_samples = vlayer_list[vlayer][3];
ip->right_samples = right_samples;
if (right_samples)
{
ip->right_sample = (Sample *)safe_malloc(sizeof(Sample) * right_samples);
stereo_channels = 2;
}
else
{
ip->right_sample = NULL;
}
song->ctl->cmsg(CMSG_INFO, VERB_NOISY, "%s%s[%d,%d] %s(%d-%d layer %d of %d)",
(percussion)? " ":"", name,
(percussion)? note_to_use : gm_num, bank,
(right_samples)? "(2) " : "",
lp->lo, lp->hi, vlayer+1, vlayer_count);
for (stereo_layer = 0; stereo_layer < stereo_channels; stereo_layer++)
{
int sample_count;
if (stereo_layer == 0)
{
sample_count = ip->left_samples;
}
else if (stereo_layer == 1)
{
sample_count = ip->right_samples;
}
for (i = 0; i < sample_count; i++)
{
BYTE fractions;
int tmplong;
WORD tmpshort;
WORD sample_volume;
BYTE tmpchar;
Sample *sp;
BYTE sf2delay;
#define READ_CHAR(thing) \
if (1 != fread(&tmpchar, 1, 1, fp)) goto fail; \
thing = tmpchar;
#define READ_SHORT(thing) \
if (1 != fread(&tmpshort, 2, 1, fp)) goto fail; \
thing = LittleShort(tmpshort);
#define READ_LONG(thing) \
if (1 != fread(&tmplong, 4, 1, fp)) goto fail; \
thing = LittleLong(tmplong);
/*
* 7 sample name
* 1 fractions
* 4 length
* 4 loop start
* 4 loop end
* 2 sample rate
* 4 low frequency
* 4 high frequency
* 4 root frequency
* 2 finetune
* 1 panning
* 6 envelope rates |
* 6 envelope offsets | 18 bytes
* 3 tremolo sweep, rate, depth |
* 3 vibrato sweep, rate, depth |
* 1 sample mode
* 2 scale frequency
* 2 scale factor | from 0 to 2048 or 0 to 2
* 2 sample volume (??)
* 34 reserved
* Now: 1 delay
* 33 reserved
*/
skip(fp, 7); /* Skip the wave name */
if (1 != fread(&fractions, 1, 1, fp))
{
fail:
song->ctl->cmsg(CMSG_ERROR, VERB_NORMAL, "Error reading sample %d", i);
if (stereo_layer == 1)
{
for (j = 0; j < i; j++)
{
free(ip->right_sample[j].data);
}
free(ip->right_sample);
i = ip->left_samples;
}
for (j = 0; j < i; j++)
{
free(ip->left_sample[j].data);
}
free(ip->left_sample);
free(ip);
free(lp);
return 0;
}
if (stereo_layer == 0)
{
sp = &(ip->left_sample[i]);
}
else if (stereo_layer == 1)
{
sp = &(ip->right_sample[i]);
}
READ_LONG(sp->data_length);
READ_LONG(sp->loop_start);
READ_LONG(sp->loop_end);
READ_SHORT(sp->sample_rate);
READ_LONG(sp->low_freq);
READ_LONG(sp->high_freq);
READ_LONG(sp->root_freq);
skip(fp, 2); /* Unused by GUS: Why have a "root frequency" and then "tuning"?? */
sp->low_vel = 0;
sp->high_vel = 127;
READ_CHAR(tmp[0]);
if (panning == -1)
sp->panning = (tmp[0] * 8 + 4) & 0x7f;
else
sp->panning = (BYTE)(panning & 0x7F);
sp->panning |= sp->panning << 7;
sp->resonance = 0;
sp->cutoff_freq = 0;
sp->reverberation = 0;
sp->chorusdepth = 0;
sp->exclusiveClass = 0;
sp->keyToModEnvHold = 0;
sp->keyToModEnvDecay = 0;
sp->keyToVolEnvHold = 0;
sp->keyToVolEnvDecay = 0;
if (cfg_tuning)
{
double tune_factor = (double)(cfg_tuning) / 1200.0;
tune_factor = pow(2.0, tune_factor);
sp->root_freq = (uint32)( tune_factor * (double)sp->root_freq );
}
/* envelope, tremolo, and vibrato */
if (18 != fread(tmp, 1, 18, fp)) goto fail;
if (!tmp[13] || !tmp[14])
{
sp->tremolo_sweep_increment = 0;
sp->tremolo_phase_increment = 0;
sp->tremolo_depth = 0;
song->ctl->cmsg(CMSG_INFO, VERB_DEBUG, " * no tremolo");
}
else
{
sp->tremolo_sweep_increment = convert_tremolo_sweep(song, tmp[12]);
sp->tremolo_phase_increment = convert_tremolo_rate(song, tmp[13]);
sp->tremolo_depth = tmp[14];
song->ctl->cmsg(CMSG_INFO, VERB_DEBUG,
" * tremolo: sweep %d, phase %d, depth %d",
sp->tremolo_sweep_increment, sp->tremolo_phase_increment,
sp->tremolo_depth);
}
if (!tmp[16] || !tmp[17])
{
sp->vibrato_sweep_increment = 0;
sp->vibrato_control_ratio = 0;
sp->vibrato_depth = 0;
song->ctl->cmsg(CMSG_INFO, VERB_DEBUG, " * no vibrato");
}
else
{
sp->vibrato_control_ratio = convert_vibrato_rate(song, tmp[16]);
sp->vibrato_sweep_increment= convert_vibrato_sweep(song, tmp[15], sp->vibrato_control_ratio);
sp->vibrato_depth = tmp[17];
song->ctl->cmsg(CMSG_INFO, VERB_DEBUG,
" * vibrato: sweep %d, ctl %d, depth %d",
sp->vibrato_sweep_increment, sp->vibrato_control_ratio,
sp->vibrato_depth);
}
READ_CHAR(sp->modes);
READ_SHORT(sp->freq_center);
READ_SHORT(sp->freq_scale);
if (sf2flag)
{
READ_SHORT(sample_volume);
READ_CHAR(sf2delay);
READ_CHAR(sp->exclusiveClass);
skip(fp, 32);
}
else
{
skip(fp, 36);
}
/* Mark this as a fixed-pitch instrument if such a deed is desired. */
if (note_to_use != -1)
sp->note_to_use = (BYTE)(note_to_use);
else
sp->note_to_use = 0;
/* seashore.pat in the Midia patch set has no Sustain. I don't
understand why, and fixing it by adding the Sustain flag to
all looped patches probably breaks something else. We do it
anyway. */
if (sp->modes & MODES_LOOPING)
sp->modes |= MODES_SUSTAIN;
/* Strip any loops and envelopes we're permitted to */
if ((strip_loop == 1) &&
(sp->modes & (MODES_SUSTAIN | MODES_LOOPING | MODES_PINGPONG | MODES_REVERSE)))
{
song->ctl->cmsg(CMSG_INFO, VERB_DEBUG, " - Removing loop and/or sustain");
sp->modes &=~(MODES_SUSTAIN | MODES_LOOPING | MODES_PINGPONG | MODES_REVERSE);
}
if (strip_envelope == 1)
{
if (sp->modes & MODES_ENVELOPE)
song->ctl->cmsg(CMSG_INFO, VERB_DEBUG, " - Removing envelope");
sp->modes &= ~MODES_ENVELOPE;
}
else if (strip_envelope != 0)
{
/* Have to make a guess. */
if (!(sp->modes & (MODES_LOOPING | MODES_PINGPONG | MODES_REVERSE)))
{
/* No loop? Then what's there to sustain? No envelope needed either... */
sp->modes &= ~(MODES_SUSTAIN|MODES_ENVELOPE);
song->ctl->cmsg(CMSG_INFO, VERB_DEBUG,
" - No loop, removing sustain and envelope");
}
else if (!memcmp(tmp, "??????", 6) || tmp[11] >= 100)
{
/* Envelope rates all maxed out? Envelope end at a high "offset"?
That's a weird envelope. Take it out. */
sp->modes &= ~MODES_ENVELOPE;
song->ctl->cmsg(CMSG_INFO, VERB_DEBUG, " - Weirdness, removing envelope");
}
else if (!(sp->modes & MODES_SUSTAIN))
{
/* No sustain? Then no envelope. I don't know if this is
justified, but patches without sustain usually don't need the
envelope either... at least the Gravis ones. They're mostly
drums. I think. */
sp->modes &= ~MODES_ENVELOPE;
song->ctl->cmsg(CMSG_INFO, VERB_DEBUG, " - No sustain, removing envelope");
}
}
sp->attenuation = 0;
for (j = ATTACK; j < DELAY; j++)
{
sp->envelope_rate[j] =
(j < 3) ? convert_envelope_rate_attack(song, tmp[j], 11) : convert_envelope_rate(song, tmp[j]);
sp->envelope_offset[j] = convert_envelope_offset(tmp[6+j]);
}
if (sf2flag)
{
if (sf2delay > 5)
{
sf2delay = 5;
}
sp->envelope_rate[DELAY] = (int)( (sf2delay * song->rate) / 1000 );
}
else
{
sp->envelope_rate[DELAY] = 0;
}
sp->envelope_offset[DELAY] = 0;
for (j = ATTACK; j < DELAY; j++)
{
sp->modulation_rate[j] = float(sp->envelope_rate[j]);
sp->modulation_offset[j] = float(sp->envelope_offset[j]);
}
sp->modulation_rate[DELAY] = sp->modulation_offset[DELAY] = 0;
sp->modEnvToFilterFc = 0;
sp->modEnvToPitch = 0;
sp->lfo_sweep_increment = 0;
sp->lfo_phase_increment = 0;
sp->modLfoToFilterFc = 0;
/* Then read the sample data */
if (((sp->modes & MODES_16BIT) && sp->data_length/2 > MAX_SAMPLE_SIZE) ||
(!(sp->modes & MODES_16BIT) && sp->data_length > MAX_SAMPLE_SIZE))
{
goto fail;
}
sp->data = (sample_t *)safe_malloc(sp->data_length + 1);
if (1 != fread(sp->data, sp->data_length, 1, fp))
goto fail;
convert_sample_data(sp, sp->data);
/* Reverse reverse loops and pass them off as normal loops */
if (sp->modes & MODES_REVERSE)
{
int t;
/* The GUS apparently plays reverse loops by reversing the
whole sample. We do the same because the GUS does not SUCK. */
song->ctl->cmsg(CMSG_WARNING, VERB_NORMAL, "Reverse loop in %s", name);
reverse_data((sample_t *)sp->data, 0, sp->data_length);
sp->data[sp->data_length] = sp->data[sp->data_length - 1];
t = sp->loop_start;
sp->loop_start = sp->data_length - sp->loop_end;
sp->loop_end = sp->data_length - t;
sp->modes &= ~MODES_REVERSE;
sp->modes |= MODES_LOOPING; /* just in case */
}
if (amp != -1)
{
sp->volume = (amp) / 100.f;
}
else if (sf2flag)
{
sp->volume = (sample_volume) / 255.f;
}
else
{
#if defined(ADJUST_SAMPLE_VOLUMES)
/* Try to determine a volume scaling factor for the sample.
This is a very crude adjustment, but things sound more
balanced with it. Still, this should be a runtime option. */
int i, numsamps = sp->data_length;
sample_t maxamp = 0, a;
sample_t *tmp;
for (i = numsamps, tmp = sp->data; i; --i)
{
a = abs(*tmp++);
if (a > maxamp)
maxamp = a;
}
sp->volume = 1 / maxamp;
song->ctl->cmsg(CMSG_INFO, VERB_DEBUG, " * volume comp: %f", sp->volume);
#else
sp->volume = 1;
#endif
}
/* Then fractional samples */
sp->data_length <<= FRACTION_BITS;
sp->loop_start <<= FRACTION_BITS;
sp->loop_end <<= FRACTION_BITS;
/* Adjust for fractional loop points. */
sp->loop_start |= (fractions & 0x0F) << (FRACTION_BITS-4);
sp->loop_end |= ((fractions>>4) & 0x0F) << (FRACTION_BITS-4);
/* If this instrument will always be played on the same note,
and it's not looped, we can resample it now. */
if (sp->note_to_use && !(sp->modes & MODES_LOOPING))
pre_resample(song, sp);
if (strip_tail == 1)
{
/* Let's not really, just say we did. */
song->ctl->cmsg(CMSG_INFO, VERB_DEBUG, " - Stripping tail");
sp->data_length = sp->loop_end;
}
} /* end of sample loop */
} /* end of stereo layer loop */
} /* end of vlayer loop */
close_file(fp);
return headlp;
}
void convert_sample_data(Sample *sp, const void *data)
{
/* convert everything to 32-bit floating point data */
sample_t *newdata;
switch (sp->modes & (MODES_16BIT | MODES_UNSIGNED))
{
case 0:
{ /* 8-bit, signed */
SBYTE *cp = (SBYTE *)data;
newdata = (sample_t *)safe_malloc((sp->data_length + 1) * sizeof(sample_t));
for (int i = 0; i < sp->data_length; ++i)
{
if (cp[i] < 0)
{
newdata[i] = float(cp[i]) / 128.f;
}
else
{
newdata[i] = float(cp[i]) / 127.f;
}
}
break;
}
case MODES_UNSIGNED:
{ /* 8-bit, unsigned */
BYTE *cp = (BYTE *)data;
newdata = (sample_t *)safe_malloc((sp->data_length + 1) * sizeof(sample_t));
for (int i = 0; i < sp->data_length; ++i)
{
int c = cp[i] - 128;
if (c < 0)
{
newdata[i] = float(c) / 128.f;
}
else
{
newdata[i] = float(c) / 127.f;
}
}
break;
}
case MODES_16BIT:
{ /* 16-bit, signed */
SWORD *cp = (SWORD *)data;
/* Convert these to samples */
sp->data_length >>= 1;
sp->loop_start >>= 1;
sp->loop_end >>= 1;
newdata = (sample_t *)safe_malloc((sp->data_length + 1) * sizeof(sample_t));
for (int i = 0; i < sp->data_length; ++i)
{
int c = LittleShort(cp[i]);
if (c < 0)
{
newdata[i] = float(c) / 32768.f;
}
else
{
newdata[i] = float(c) / 32767.f;
}
}
break;
}
case MODES_16BIT | MODES_UNSIGNED:
{ /* 16-bit, unsigned */
WORD *cp = (WORD *)data;
/* Convert these to samples */
sp->data_length >>= 1;
sp->loop_start >>= 1;
sp->loop_end >>= 1;
newdata = (sample_t *)safe_malloc((sp->data_length + 1) * sizeof(sample_t));
for (int i = 0; i < sp->data_length; ++i)
{
int c = LittleShort(cp[i]) - 32768;
if (c < 0)
{
newdata[i] = float(c) / 32768.f;
}
else
{
newdata[i] = float(c) / 32767.f;
}
}
break;
}
}
/* Duplicate the final sample for linear interpolation. */
newdata[sp->data_length] = newdata[sp->data_length - 1];
if (sp->data != NULL)
{
free(sp->data);
}
sp->data = newdata;
}
static int fill_bank(Renderer *song, int dr, int b)
{
int i, errors = 0;
ToneBank *bank = ((dr) ? drumset[b] : tonebank[b]);
if (bank == NULL)
{
song->ctl->cmsg(CMSG_ERROR, VERB_NORMAL,
"Huh. Tried to load instruments in non-existent %s %d",
(dr) ? "drumset" : "tone bank", b);
return 0;
}
for (i = 0; i < MAXPROG; i++)
{
if (bank->tone[i].layer == MAGIC_LOAD_INSTRUMENT)
{
bank->tone[i].layer = load_instrument_dls(song, dr, b, i);
if (bank->tone[i].layer != NULL)
{
continue;
}
if (bank->tone[i].name.IsEmpty())
{
song->ctl->cmsg(CMSG_WARNING, (b!=0) ? VERB_VERBOSE : VERB_NORMAL,
"No instrument mapped to %s %d, program %d%s",
(dr)? "drum set" : "tone bank", b, i,
(b!=0) ? "" : " - this instrument will not be heard");
if (b!=0)
{
/* Mark the corresponding instrument in the default
bank / drumset for loading (if it isn't already) */
if (!dr)
{
if (!(standard_tonebank.tone[i].layer))
standard_tonebank.tone[i].layer=
MAGIC_LOAD_INSTRUMENT;
}
else
{
if (!(standard_drumset.tone[i].layer))
standard_drumset.tone[i].layer=
MAGIC_LOAD_INSTRUMENT;
}
}
bank->tone[i].layer=0;
errors++;
}
else if (!(bank->tone[i].layer=
load_instrument(song, bank->tone[i].name,
bank->tone[i].font_type,
(dr) ? 1 : 0,
bank->tone[i].pan,
bank->tone[i].amp,
bank->tone[i].tuning,
(bank->tone[i].note!=-1) ?
bank->tone[i].note :
((dr) ? i : -1),
(bank->tone[i].strip_loop!=-1) ?
bank->tone[i].strip_loop :
((dr) ? 1 : -1),
(bank->tone[i].strip_envelope != -1) ?
bank->tone[i].strip_envelope :
((dr) ? 1 : -1),
bank->tone[i].strip_tail,
b,
((dr) ? i + 128 : i),
bank->tone[i].sf_ix
)))
{
song->ctl->cmsg(CMSG_ERROR, VERB_NORMAL,
"Couldn't load instrument %s (%s %d, program %d)",
bank->tone[i].name,
(dr)? "drum set" : "tone bank", b, i);
errors++;
}
}
}
return errors;
}
int Renderer::load_missing_instruments()
{
int i = MAXBANK, errors = 0;
while (i--)
{
if (tonebank[i] != NULL)
errors += fill_bank(this, 0,i);
if (drumset[i] != NULL)
errors += fill_bank(this, 1,i);
}
return errors;
}
void free_instruments()
{
int i = 128;
while (i--)
{
if (tonebank[i] != NULL)
free_bank(0,i);
if (drumset[i] != NULL)
free_bank(1,i);
}
}
int Renderer::set_default_instrument(const char *name)
{
InstrumentLayer *lp;
if (!(lp = load_instrument(this, name, FONT_NORMAL, 0, -1, -1, 0, -1, -1, -1, -1, 0, -1, -1)))
return -1;
if (default_instrument)
free_layer(default_instrument);
default_instrument = lp;
default_program = SPECIAL_PROGRAM;
return 0;
}
}

BIN
src/timidity/instrum.obj Normal file

Binary file not shown.

1260
src/timidity/instrum_dls.cpp Normal file

File diff suppressed because it is too large Load diff

584
src/timidity/mix.cpp Normal file
View file

@ -0,0 +1,584 @@
/*
TiMidity -- Experimental MIDI to WAVE converter
Copyright (C) 1995 Tuukka Toivonen <toivonen@clinet.fi>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library 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
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
mix.c
*/
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include "timidity.h"
namespace Timidity
{
/* Returns 1 if envelope runs out */
int recompute_envelope(Voice *v)
{
int stage;
stage = v->envelope_stage;
if (stage >= DELAY)
{
/* Envelope ran out. */
int tmp = (v->status == VOICE_DIE); /* Already displayed as dead */
v->status = VOICE_FREE;
return 1;
}
if (v->sample->modes & MODES_ENVELOPE)
{
if (v->status == VOICE_ON || v->status == VOICE_SUSTAINED)
{
if (stage > DECAY)
{
/* Freeze envelope until note turns off. Trumpets want this. */
v->envelope_increment = 0;
return 0;
}
}
}
v->envelope_stage = stage + 1;
if (v->envelope_volume == v->sample->envelope_offset[stage])
return recompute_envelope(v);
v->envelope_target = v->sample->envelope_offset[stage];
v->envelope_increment = v->sample->envelope_rate[stage];
if (v->envelope_target < v->envelope_volume)
v->envelope_increment = -v->envelope_increment;
return 0;
}
void apply_envelope_to_amp(Voice *v)
{
double lamp = v->left_amp, ramp;
if (v->panned == PANNED_MYSTERY)
{
ramp = v->right_amp;
if (v->tremolo_phase_increment)
{
lamp *= v->tremolo_volume;
ramp *= v->tremolo_volume;
}
if (v->sample->modes & MODES_ENVELOPE)
{
double vol = calc_vol(v->envelope_volume / float(1 << 30));
lamp *= vol;
ramp *= vol;
}
v->left_mix = float(lamp);
v->right_mix = float(ramp);
}
else
{
if (v->tremolo_phase_increment)
lamp *= v->tremolo_volume;
if (v->sample->modes & MODES_ENVELOPE)
lamp *= calc_vol(v->envelope_volume / float(1 << 30));
v->left_mix = float(lamp);
}
}
static int update_envelope(Voice *v)
{
v->envelope_volume += v->envelope_increment;
/* Why is there no ^^ operator?? */
if (((v->envelope_increment < 0) &&
(v->envelope_volume <= v->envelope_target)) ||
((v->envelope_increment > 0) &&
(v->envelope_volume >= v->envelope_target)))
{
v->envelope_volume = v->envelope_target;
if (recompute_envelope(v))
return 1;
}
return 0;
}
static void update_tremolo(Voice *v)
{
int depth = v->sample->tremolo_depth << 7;
if (v->tremolo_sweep)
{
/* Update sweep position */
v->tremolo_sweep_position += v->tremolo_sweep;
if (v->tremolo_sweep_position >= (1 << SWEEP_SHIFT))
{
/* Swept to max amplitude */
v->tremolo_sweep = 0;
}
else
{
/* Need to adjust depth */
depth *= v->tremolo_sweep_position;
depth >>= SWEEP_SHIFT;
}
}
v->tremolo_phase += v->tremolo_phase_increment;
v->tremolo_volume = (float)
(1.0 - FSCALENEG((sine(v->tremolo_phase >> RATE_SHIFT) + 1.0)
* depth * TREMOLO_AMPLITUDE_TUNING,
17));
/* I'm not sure about the +1.0 there -- it makes tremoloed voices'
volumes on average the lower the higher the tremolo amplitude. */
}
/* Returns 1 if the note died */
static int update_signal(Voice *v)
{
if (v->envelope_increment && update_envelope(v))
return 1;
if (v->tremolo_phase_increment)
update_tremolo(v);
apply_envelope_to_amp(v);
return 0;
}
static void mix_mystery_signal(SDWORD control_ratio, const sample_t *sp, float *lp, Voice *v, int count)
{
final_volume_t
left = v->left_mix,
right = v->right_mix;
int cc;
sample_t s;
if (!(cc = v->control_counter))
{
cc = control_ratio;
if (update_signal(v))
return; /* Envelope ran out */
left = v->left_mix;
right = v->right_mix;
}
while (count)
{
if (cc < count)
{
count -= cc;
while (cc--)
{
s = *sp++;
lp[0] += left * s;
lp[1] += right * s;
lp += 2;
}
cc = control_ratio;
if (update_signal(v))
return; /* Envelope ran out */
left = v->left_mix;
right = v->right_mix;
}
else
{
v->control_counter = cc - count;
while (count--)
{
s = *sp++;
lp[0] += left * s;
lp[1] += right * s;
lp += 2;
}
return;
}
}
}
static void mix_center_signal(SDWORD control_ratio, const sample_t *sp, float *lp, Voice *v, int count)
{
final_volume_t
left = v->left_mix;
int cc;
sample_t s;
if (!(cc = v->control_counter))
{
cc = control_ratio;
if (update_signal(v))
return; /* Envelope ran out */
left = v->left_mix;
}
while (count)
{
if (cc < count)
{
count -= cc;
while (cc--)
{
s = *sp++ * left;
lp[0] += s;
lp[1] += s;
lp += 2;
}
cc = control_ratio;
if (update_signal(v))
return; /* Envelope ran out */
left = v->left_mix;
}
else
{
v->control_counter = cc - count;
while (count--)
{
s = *sp++ * left;
lp[0] += s;
lp[1] += s;
lp += 2;
}
return;
}
}
}
static void mix_single_left_signal(SDWORD control_ratio, const sample_t *sp, float *lp, Voice *v, int count)
{
final_volume_t
left = v->left_mix;
int cc;
if (!(cc = v->control_counter))
{
cc = control_ratio;
if (update_signal(v))
return; /* Envelope ran out */
left = v->left_mix;
}
while (count)
{
if (cc < count)
{
count -= cc;
while (cc--)
{
lp[0] += *sp++ * left;
lp += 2;
}
cc = control_ratio;
if (update_signal(v))
return; /* Envelope ran out */
left = v->left_mix;
}
else
{
v->control_counter = cc - count;
while (count--)
{
lp[0] += *sp++ * left;
lp += 2;
}
return;
}
}
}
static void mix_single_right_signal(SDWORD control_ratio, const sample_t *sp, float *lp, Voice *v, int count)
{
final_volume_t
left = v->left_mix;
int cc;
if (!(cc = v->control_counter))
{
cc = control_ratio;
if (update_signal(v))
return; /* Envelope ran out */
left = v->left_mix;
}
while (count)
{
if (cc < count)
{
count -= cc;
while (cc--)
{
lp[1] += *sp++ * left;
lp += 2;
}
cc = control_ratio;
if (update_signal(v))
return; /* Envelope ran out */
left = v->left_mix;
}
else
{
v->control_counter = cc - count;
while (count--)
{
lp[1] += *sp++ * left;
lp += 2;
}
return;
}
}
}
static void mix_mono_signal(SDWORD control_ratio, const sample_t *sp, float *lp, Voice *v, int count)
{
final_volume_t
left = v->left_mix;
int cc;
if (!(cc = v->control_counter))
{
cc = control_ratio;
if (update_signal(v))
return; /* Envelope ran out */
left = v->left_mix;
}
while (count)
{
if (cc < count)
{
count -= cc;
while (cc--)
{
*lp++ += *sp++ * left;
}
cc = control_ratio;
if (update_signal(v))
return; /* Envelope ran out */
left = v->left_mix;
}
else
{
v->control_counter = cc - count;
while (count--)
{
*lp++ += *sp++ * left;
}
return;
}
}
}
static void mix_mystery(SDWORD control_ratio, const sample_t *sp, float *lp, Voice *v, int count)
{
final_volume_t
left = v->left_mix,
right = v->right_mix;
sample_t s;
while (count--)
{
s = *sp++;
lp[0] += s * left;
lp[1] += s * right;
lp += 2;
}
}
static void mix_center(const sample_t *sp, float *lp, Voice *v, int count)
{
final_volume_t
left = v->left_mix;
sample_t s;
while (count--)
{
s = *sp++ * left;
lp[0] += s;
lp[1] += s;
lp += 2;
}
}
static void mix_single_left(const sample_t *sp, float *lp, Voice *v, int count)
{
final_volume_t
left = v->left_mix;
while (count--)
{
lp[0] += *sp++ * left;
lp += 2;
}
}
static void mix_single_right(const sample_t *sp, float *lp, Voice *v, int count)
{
final_volume_t
left = v->left_mix;
while (count--)
{
lp[1] += *sp++ * left;
lp += 2;
}
}
static void mix_mono(const sample_t *sp, float *lp, Voice *v, int count)
{
final_volume_t
left = v->left_mix;
while (count--)
{
*lp++ += *sp++ * left;
}
}
/* Ramp a note out in c samples */
static void ramp_out(const sample_t *sp, float *lp, Voice *v, int c)
{
final_volume_t left, right, li, ri;
sample_t s = 0; /* silly warning about uninitialized s */
/* Fix by James Caldwell */
if ( c == 0 ) c = 1;
left = v->left_mix;
li = -(left/c);
if (li == 0) li = -1;
/* printf("Ramping out: left=%d, c=%d, li=%d\n", left, c, li); */
if (v->panned == PANNED_MYSTERY)
{
right = v->right_mix;
ri = -(right/c);
while (c--)
{
left += li; if (left < 0) left = 0;
right += ri; if (right < 0) right = 0;
s = *sp++;
lp[0] += s * left;
lp[1] += s * right;
lp += 2;
}
}
else if (v->panned == PANNED_CENTER)
{
while (c--)
{
left += li;
if (left < 0)
return;
s = *sp++ * left;
lp[0] += s;
lp[1] += s;
lp += 2;
}
}
else if (v->panned == PANNED_LEFT)
{
while (c--)
{
left += li;
if (left < 0)
return;
lp[0] += *sp++ * left;
lp += 2;
}
}
else if (v->panned == PANNED_RIGHT)
{
while (c--)
{
left += li;
if (left < 0)
return;
s = *sp++;
lp[1] += *sp++ * left;
lp += 2;
}
}
}
/**************** interface function ******************/
void mix_voice(Renderer *song, float *buf, Voice *v, int c)
{
int count = c;
sample_t *sp;
if (c < 0)
{
return;
}
if (v->status == VOICE_DIE)
{
if (count >= MAX_DIE_TIME)
count = MAX_DIE_TIME;
sp = resample_voice(song, v, &count);
ramp_out(sp, buf, v, count);
v->status = VOICE_FREE;
}
else
{
sp = resample_voice(song, v, &count);
if (count < 0)
{
return;
}
if (v->panned == PANNED_MYSTERY)
{
if (v->envelope_increment || v->tremolo_phase_increment)
mix_mystery_signal(song->control_ratio, sp, buf, v, count);
else
mix_mystery(song->control_ratio, sp, buf, v, count);
}
else if (v->panned == PANNED_CENTER)
{
if (v->envelope_increment || v->tremolo_phase_increment)
mix_center_signal(song->control_ratio, sp, buf, v, count);
else
mix_center(sp, buf, v, count);
}
else
{
/* It's either full left or full right. In either case,
every other sample is 0. Just get the offset right: */
if (v->envelope_increment || v->tremolo_phase_increment)
{
if (v->panned == PANNED_RIGHT)
mix_single_right_signal(song->control_ratio, sp, buf, v, count);
else
mix_single_left_signal(song->control_ratio, sp, buf, v, count);
}
else
{
if (v->panned == PANNED_RIGHT)
mix_single_right(sp, buf, v, count);
else
mix_single_left(sp, buf, v, count);
}
}
}
}
}

1575
src/timidity/playmidi.cpp Normal file

File diff suppressed because it is too large Load diff

608
src/timidity/resample.cpp Normal file
View file

@ -0,0 +1,608 @@
/*
TiMidity -- Experimental MIDI to WAVE converter
Copyright (C) 1995 Tuukka Toivonen <toivonen@clinet.fi>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library 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
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
resample.c
*/
#include <math.h>
#include <stdio.h>
#include <malloc.h>
#include "timidity.h"
namespace Timidity
{
#define RESAMPLATION {\
int o = ofs >> FRACTION_BITS, m = ofs & FRACTION_MASK; \
*dest++ = src[o] + (src[o + 1] - src[o]) * m / (1 << FRACTION_BITS);\
}
#define FINALINTERP if (ofs == le) *dest++ = src[ofs >> FRACTION_BITS];
/* So it isn't interpolation. At least it's final. */
/*************** resampling with fixed increment *****************/
static sample_t *rs_plain(sample_t *resample_buffer, Voice *v, int *countptr)
{
/* Play sample until end, then free the voice. */
const sample_t
*src = v->sample->data;
sample_t
*dest = resample_buffer;
int
ofs = v->sample_offset,
incr = v->sample_increment,
le = v->sample->data_length,
count = *countptr;
int i;
if (incr < 0) incr = -incr; /* In case we're coming out of a bidir loop */
/* Precalc how many times we should go through the loop.
NOTE: Assumes that incr > 0 and that ofs <= le */
i = (le - ofs) / incr + 1;
if (i > count)
{
i = count;
count = 0;
}
else
{
count -= i;
}
while (i--)
{
RESAMPLATION;
ofs += incr;
}
if (ofs >= le)
{
FINALINTERP;
v->status = VOICE_FREE;
*countptr -= count + 1;
}
v->sample_offset = ofs; /* Update offset */
return resample_buffer;
}
static sample_t *rs_loop(sample_t *resample_buffer, Voice *vp, int count)
{
/* Play sample until end-of-loop, skip back and continue. */
int
ofs = vp->sample_offset,
incr = vp->sample_increment,
le = vp->sample->loop_end,
ll = le - vp->sample->loop_start;
sample_t
*dest = resample_buffer;
const sample_t
*src = vp->sample->data;
int i;
while (count)
{
if (ofs >= le)
/* NOTE: Assumes that ll > incr and that incr > 0. */
ofs -= ll;
/* Precalc how many times we should go through the loop */
i = (le - ofs) / incr + 1;
if (i > count)
{
i = count;
count = 0;
}
else
{
count -= i;
}
while (i--)
{
RESAMPLATION;
ofs += incr;
}
}
vp->sample_offset=ofs; /* Update offset */
return resample_buffer;
}
static sample_t *rs_bidir(sample_t *resample_buffer, Voice *vp, int count)
{
int
ofs = vp->sample_offset,
incr = vp->sample_increment,
le = vp->sample->loop_end,
ls = vp->sample->loop_start;
sample_t
*dest = resample_buffer;
const sample_t
*src = vp->sample->data;
int
le2 = le << 1,
ls2 = ls << 1,
i;
/* Play normally until inside the loop region */
if (ofs <= ls)
{
/* NOTE: Assumes that incr > 0, which is NOT always the case
when doing bidirectional looping. I have yet to see a case
where both ofs <= ls AND incr < 0, however. */
i = (ls - ofs) / incr + 1;
if (i > count)
{
i = count;
count = 0;
}
else
{
count -= i;
}
while (i--)
{
RESAMPLATION;
ofs += incr;
}
}
/* Then do the bidirectional looping */
while(count)
{
/* Precalc how many times we should go through the loop */
i = ((incr > 0 ? le : ls) - ofs) / incr + 1;
if (i > count)
{
i = count;
count = 0;
}
else
{
count -= i;
}
while (i--)
{
RESAMPLATION;
ofs += incr;
}
if (ofs >= le)
{
/* fold the overshoot back in */
ofs = le2 - ofs;
incr *= -1;
}
else if (ofs <= ls)
{
ofs = ls2 - ofs;
incr *= -1;
}
}
vp->sample_increment = incr;
vp->sample_offset = ofs; /* Update offset */
return resample_buffer;
}
/*********************** vibrato versions ***************************/
/* We only need to compute one half of the vibrato sine cycle */
static int vib_phase_to_inc_ptr(int phase)
{
if (phase < VIBRATO_SAMPLE_INCREMENTS/2)
return VIBRATO_SAMPLE_INCREMENTS/2-1-phase;
else if (phase >= 3*VIBRATO_SAMPLE_INCREMENTS/2)
return 5*VIBRATO_SAMPLE_INCREMENTS/2-1-phase;
else
return phase-VIBRATO_SAMPLE_INCREMENTS/2;
}
static int update_vibrato(float output_rate, Voice *vp, int sign)
{
int depth;
int phase;
double a, pb;
if (vp->vibrato_phase++ >= 2*VIBRATO_SAMPLE_INCREMENTS-1)
vp->vibrato_phase=0;
phase = vib_phase_to_inc_ptr(vp->vibrato_phase);
if (vp->vibrato_sample_increment[phase])
{
if (sign)
return -vp->vibrato_sample_increment[phase];
else
return vp->vibrato_sample_increment[phase];
}
/* Need to compute this sample increment. */
depth = vp->sample->vibrato_depth << 7;
if (vp->vibrato_sweep)
{
/* Need to update sweep */
vp->vibrato_sweep_position += vp->vibrato_sweep;
if (vp->vibrato_sweep_position >= (1<<SWEEP_SHIFT))
vp->vibrato_sweep=0;
else
{
/* Adjust depth */
depth *= vp->vibrato_sweep_position;
depth >>= SWEEP_SHIFT;
}
}
a = FSCALE(((double)(vp->sample->sample_rate) * vp->frequency) /
((double)(vp->sample->root_freq) * output_rate),
FRACTION_BITS);
pb = (sine(vp->vibrato_phase * (1.0/(2*VIBRATO_SAMPLE_INCREMENTS)))
* (double)(depth) * VIBRATO_AMPLITUDE_TUNING);
a *= pow(2.0, pb / (8191 * 12.f));
/* If the sweep's over, we can store the newly computed sample_increment */
if (!vp->vibrato_sweep)
vp->vibrato_sample_increment[phase] = (int) a;
if (sign)
a = -a; /* need to preserve the loop direction */
return (int) a;
}
static sample_t *rs_vib_plain(sample_t *resample_buffer, float rate, Voice *vp, int *countptr)
{
/* Play sample until end, then free the voice. */
sample_t
*dest = resample_buffer;
const sample_t
*src = vp->sample->data;
int
le = vp->sample->data_length,
ofs = vp->sample_offset,
incr = vp->sample_increment,
count = *countptr;
int
cc = vp->vibrato_control_counter;
/* This has never been tested */
if (incr < 0) incr = -incr; /* In case we're coming out of a bidir loop */
while (count--)
{
if (!cc--)
{
cc = vp->vibrato_control_ratio;
incr = update_vibrato(rate, vp, 0);
}
RESAMPLATION;
ofs += incr;
if (ofs >= le)
{
FINALINTERP;
vp->status = VOICE_FREE;
*countptr -= count+1;
break;
}
}
vp->vibrato_control_counter = cc;
vp->sample_increment = incr;
vp->sample_offset = ofs; /* Update offset */
return resample_buffer;
}
static sample_t *rs_vib_loop(sample_t *resample_buffer, float rate, Voice *vp, int count)
{
/* Play sample until end-of-loop, skip back and continue. */
int
ofs = vp->sample_offset,
incr = vp->sample_increment,
le = vp->sample->loop_end,
ll = le - vp->sample->loop_start;
sample_t
*dest = resample_buffer;
const sample_t
*src = vp->sample->data;
int
cc = vp->vibrato_control_counter;
int i;
int
vibflag=0;
while (count)
{
/* Hopefully the loop is longer than an increment */
if (ofs >= le)
ofs -= ll;
/* Precalc how many times to go through the loop, taking
the vibrato control ratio into account this time. */
i = (le - ofs) / incr + 1;
if (i > count) i = count;
if (i > cc)
{
i = cc;
vibflag = 1;
}
else
{
cc -= i;
}
count -= i;
while (i--)
{
RESAMPLATION;
ofs += incr;
}
if (vibflag)
{
cc = vp->vibrato_control_ratio;
incr = update_vibrato(rate, vp, 0);
vibflag = 0;
}
}
vp->vibrato_control_counter = cc;
vp->sample_increment = incr;
vp->sample_offset = ofs; /* Update offset */
return resample_buffer;
}
static sample_t *rs_vib_bidir(sample_t *resample_buffer, float rate, Voice *vp, int count)
{
int
ofs = vp->sample_offset,
incr = vp->sample_increment,
le = vp->sample->loop_end,
ls = vp->sample->loop_start;
sample_t
*dest = resample_buffer;
const sample_t
*src = vp->sample->data;
int
cc = vp->vibrato_control_counter;
int
le2 = le << 1,
ls2 = ls << 1,
i;
int
vibflag = 0;
/* Play normally until inside the loop region */
while (count && (ofs <= ls))
{
i = (ls - ofs) / incr + 1;
if (i > count)
{
i = count;
}
if (i > cc)
{
i = cc;
vibflag = 1;
}
else
{
cc -= i;
}
count -= i;
while (i--)
{
RESAMPLATION;
ofs += incr;
}
if (vibflag)
{
cc = vp->vibrato_control_ratio;
incr = update_vibrato(rate, vp, 0);
vibflag = 0;
}
}
/* Then do the bidirectional looping */
while (count)
{
/* Precalc how many times we should go through the loop */
i = ((incr > 0 ? le : ls) - ofs) / incr + 1;
if(i > count)
{
i = count;
}
if(i > cc)
{
i = cc;
vibflag = 1;
}
else
{
cc -= i;
}
count -= i;
while (i--)
{
RESAMPLATION;
ofs += incr;
}
if (vibflag)
{
cc = vp->vibrato_control_ratio;
incr = update_vibrato(rate, vp, (incr < 0));
vibflag = 0;
}
if (ofs >= le)
{
/* fold the overshoot back in */
ofs = le2 - ofs;
incr *= -1;
}
else if (ofs <= ls)
{
ofs = ls2 - ofs;
incr *= -1;
}
}
vp->vibrato_control_counter = cc;
vp->sample_increment = incr;
vp->sample_offset = ofs; /* Update offset */
return resample_buffer;
}
sample_t *resample_voice(Renderer *song, Voice *vp, int *countptr)
{
int ofs;
BYTE modes;
if (vp->sample->sample_rate == 0)
{
/* Pre-resampled data -- just update the offset and check if
we're out of data. */
ofs = vp->sample_offset >> FRACTION_BITS; /* Kind of silly to use FRACTION_BITS here... */
if (*countptr >= (vp->sample->data_length >> FRACTION_BITS) - ofs)
{
/* Note finished. Free the voice. */
vp->status = VOICE_FREE;
/* Let the caller know how much data we had left */
*countptr = (vp->sample->data_length >> FRACTION_BITS) - ofs;
}
else
{
vp->sample_offset += *countptr << FRACTION_BITS;
}
return vp->sample->data + ofs;
}
/* Need to resample. Use the proper function. */
modes = vp->sample->modes;
if (vp->vibrato_control_ratio)
{
if ((modes & MODES_LOOPING) &&
((modes & MODES_ENVELOPE) ||
(vp->status == VOICE_ON || vp->status == VOICE_SUSTAINED)))
{
if (modes & MODES_PINGPONG)
return rs_vib_bidir(song->resample_buffer, song->rate, vp, *countptr);
else
return rs_vib_loop(song->resample_buffer, song->rate, vp, *countptr);
}
else
{
return rs_vib_plain(song->resample_buffer, song->rate, vp, countptr);
}
}
else
{
if ((modes & MODES_LOOPING) &&
((modes & MODES_ENVELOPE) ||
(vp->status == VOICE_ON || vp->status == VOICE_SUSTAINED)))
{
if (modes & MODES_PINGPONG)
return rs_bidir(song->resample_buffer, vp, *countptr);
else
return rs_loop(song->resample_buffer, vp, *countptr);
}
else
{
return rs_plain(song->resample_buffer, vp, countptr);
}
}
}
void pre_resample(Renderer *song, Sample *sp)
{
double a, xdiff;
int incr, ofs, newlen, count;
sample_t *newdata, *dest, *src = sp->data;
sample_t v1, v2, v3, v4, *vptr;
static const char note_name[12][3] =
{
"C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B"
};
return;
song->ctl->cmsg(CMSG_INFO, VERB_NOISY, " * pre-resampling for note %d (%s%d)",
sp->note_to_use,
note_name[sp->note_to_use % 12], (sp->note_to_use & 0x7F) / 12);
a = (sp->sample_rate * note_to_freq(sp->note_to_use)) / (sp->root_freq * song->rate);
if (a <= 0)
return;
newlen = (int)(sp->data_length / a);
if (newlen < 0 || (newlen >> FRACTION_BITS) > MAX_SAMPLE_SIZE)
return;
dest = newdata = (sample_t *)safe_malloc(newlen >> (FRACTION_BITS - 2));
count = (newlen >> FRACTION_BITS) - 1;
ofs = incr = (sp->data_length - (1 << FRACTION_BITS)) / count;
if (--count)
*dest++ = src[0];
/* Since we're pre-processing and this doesn't have to be done in
real-time, we go ahead and do the full sliding cubic interpolation. */
while (--count)
{
vptr = src + (ofs >> FRACTION_BITS);
v1 = (vptr == src) ? *vptr : *(vptr - 1);
v2 = *vptr;
v3 = *(vptr + 1);
v4 = *(vptr + 2);
xdiff = FSCALENEG(ofs & FRACTION_MASK, FRACTION_BITS);
*dest++ = sample_t(v2 + (xdiff / 6.0) * (-2 * v1 - 3 * v2 + 6 * v3 - v4 +
xdiff * (3 * (v1 - 2 * v2 + v3) + xdiff * (-v1 + 3 * (v2 - v3) + v4))));
ofs += incr;
}
if (ofs & FRACTION_MASK)
{
RESAMPLATION
}
else
{
*dest++ = src[ofs >> FRACTION_BITS];
}
sp->data_length = newlen;
sp->loop_start = int(sp->loop_start / a);
sp->loop_end = int(sp->loop_end / a);
free(sp->data);
sp->data = newdata;
sp->sample_rate = 0;
}
}

224
src/timidity/tables.cpp Normal file
View file

@ -0,0 +1,224 @@
/*
TiMidity -- Experimental MIDI to WAVE converter
Copyright (C) 1995 Tuukka Toivonen <toivonen@clinet.fi>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library 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
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "timidity.h"
namespace Timidity
{
/* $Id: tables.c 1404 2004-08-21 12:27:02Z slouken $ Greg Lee */
const BYTE xmap[XMAPMAX][5] =
{
{ SFXBANK, 0, 0, 120, 0 },
{ SFXBANK, 0, 1, 120, 1 },
{ SFXBANK, 0, 2, 120, 2 },
{ SFXBANK, 0, 3, 120, 3 },
{ SFXBANK, 0, 4, 120, 4 },
{ SFXBANK, 0, 5, 120, 5 },
{ SFXBANK, 0, 16, 120, 16 },
{ SFXBANK, 0, 32, 120, 32 },
{ SFXBANK, 0, 33, 120, 33 },
{ SFXBANK, 0, 34, 120, 34 },
{ SFXBANK, 0, 35, 120, 35 },
{ SFXBANK, 0, 36, 120, 36 },
{ SFXBANK, 0, 48, 120, 48 },
{ SFXBANK, 0, 49, 120, 49 },
{ SFXBANK, 0, 50, 120, 50 },
{ SFXBANK, 0, 51, 120, 51 },
{ SFXBANK, 0, 52, 120, 52 },
{ SFXBANK, 0, 54, 120, 54 },
{ SFXBANK, 0, 55, 120, 55 },
{ SFXBANK, 0, 64, 120, 64 },
{ SFXBANK, 0, 65, 120, 65 },
{ SFXBANK, 0, 66, 120, 66 },
{ SFXBANK, 0, 67, 120, 67 },
{ SFXBANK, 0, 68, 120, 68 },
{ SFXBANK, 0, 69, 120, 69 },
{ SFXBANK, 0, 70, 120, 70 },
{ SFXBANK, 0, 80, 120, 80 },
{ SFXBANK, 0, 81, 120, 81 },
{ SFXBANK, 0, 82, 120, 82 },
{ SFXBANK, 0, 83, 120, 83 },
{ SFXBANK, 0, 84, 120, 84 },
{ SFXBANK, 0, 85, 120, 85 },
{ SFXBANK, 0, 86, 120, 86 },
{ SFXBANK, 0, 87, 120, 87 },
{ SFXBANK, 0, 88, 120, 88 },
{ SFXBANK, 0, 89, 120, 89 },
{ SFXBANK, 0, 90, 120, 90 },
{ SFXBANK, 0, 96, 120, 96 },
{ SFXBANK, 0, 97, 120, 97 },
{ SFXBANK, 0, 98, 120, 98 },
{ SFXBANK, 0, 99, 120, 99 },
{ SFXBANK, 0, 100, 120, 100 },
{ SFXBANK, 0, 101, 120, 101 },
{ SFXBANK, 0, 112, 120, 112 },
{ SFXBANK, 0, 113, 120, 113 },
{ SFXBANK, 0, 114, 120, 114 },
{ SFXBANK, 0, 115, 120, 115 },
{ SFXDRUM1, 0, 36, 121, 36 },
{ SFXDRUM1, 0, 37, 121, 37 },
{ SFXDRUM1, 0, 38, 121, 38 },
{ SFXDRUM1, 0, 39, 121, 39 },
{ SFXDRUM1, 0, 40, 121, 40 },
{ SFXDRUM1, 0, 41, 121, 41 },
{ SFXDRUM1, 0, 52, 121, 52 },
{ SFXDRUM1, 0, 68, 121, 68 },
{ SFXDRUM1, 0, 69, 121, 69 },
{ SFXDRUM1, 0, 70, 121, 70 },
{ SFXDRUM1, 0, 71, 121, 71 },
{ SFXDRUM1, 0, 72, 121, 72 },
{ SFXDRUM1, 0, 84, 121, 84 },
{ SFXDRUM1, 0, 85, 121, 85 },
{ SFXDRUM1, 0, 86, 121, 86 },
{ SFXDRUM1, 0, 87, 121, 87 },
{ SFXDRUM1, 0, 88, 121, 88 },
{ SFXDRUM1, 0, 90, 121, 90 },
{ SFXDRUM1, 0, 91, 121, 91 },
{ SFXDRUM1, 1, 36, 122, 36 },
{ SFXDRUM1, 1, 37, 122, 37 },
{ SFXDRUM1, 1, 38, 122, 38 },
{ SFXDRUM1, 1, 39, 122, 39 },
{ SFXDRUM1, 1, 40, 122, 40 },
{ SFXDRUM1, 1, 41, 122, 41 },
{ SFXDRUM1, 1, 42, 122, 42 },
{ SFXDRUM1, 1, 52, 122, 52 },
{ SFXDRUM1, 1, 53, 122, 53 },
{ SFXDRUM1, 1, 54, 122, 54 },
{ SFXDRUM1, 1, 55, 122, 55 },
{ SFXDRUM1, 1, 56, 122, 56 },
{ SFXDRUM1, 1, 57, 122, 57 },
{ SFXDRUM1, 1, 58, 122, 58 },
{ SFXDRUM1, 1, 59, 122, 59 },
{ SFXDRUM1, 1, 60, 122, 60 },
{ SFXDRUM1, 1, 61, 122, 61 },
{ SFXDRUM1, 1, 62, 122, 62 },
{ SFXDRUM1, 1, 68, 122, 68 },
{ SFXDRUM1, 1, 69, 122, 69 },
{ SFXDRUM1, 1, 70, 122, 70 },
{ SFXDRUM1, 1, 71, 122, 71 },
{ SFXDRUM1, 1, 72, 122, 72 },
{ SFXDRUM1, 1, 73, 122, 73 },
{ SFXDRUM1, 1, 84, 122, 84 },
{ SFXDRUM1, 1, 85, 122, 85 },
{ SFXDRUM1, 1, 86, 122, 86 },
{ SFXDRUM1, 1, 87, 122, 87 },
{ XGDRUM, 0, 25, 40, 38 },
{ XGDRUM, 0, 26, 40, 40 },
{ XGDRUM, 0, 27, 40, 39 },
{ XGDRUM, 0, 28, 40, 30 },
{ XGDRUM, 0, 29, 0, 25 },
{ XGDRUM, 0, 30, 0, 85 },
{ XGDRUM, 0, 31, 0, 38 },
{ XGDRUM, 0, 32, 0, 37 },
{ XGDRUM, 0, 33, 0, 36 },
{ XGDRUM, 0, 34, 0, 38 },
{ XGDRUM, 0, 62, 0, 101 },
{ XGDRUM, 0, 63, 0, 102 },
{ XGDRUM, 0, 64, 0, 103 },
{ XGDRUM, 8, 25, 40, 38 },
{ XGDRUM, 8, 26, 40, 40 },
{ XGDRUM, 8, 27, 40, 39 },
{ XGDRUM, 8, 28, 40, 40 },
{ XGDRUM, 8, 29, 8, 25 },
{ XGDRUM, 8, 30, 8, 85 },
{ XGDRUM, 8, 31, 8, 38 },
{ XGDRUM, 8, 32, 8, 37 },
{ XGDRUM, 8, 33, 8, 36 },
{ XGDRUM, 8, 34, 8, 38 },
{ XGDRUM, 8, 62, 8, 101 },
{ XGDRUM, 8, 63, 8, 102 },
{ XGDRUM, 8, 64, 8, 103 },
{ XGDRUM, 16, 25, 40, 38 },
{ XGDRUM, 16, 26, 40, 40 },
{ XGDRUM, 16, 27, 40, 39 },
{ XGDRUM, 16, 28, 40, 40 },
{ XGDRUM, 16, 29, 16, 25 },
{ XGDRUM, 16, 30, 16, 85 },
{ XGDRUM, 16, 31, 16, 38 },
{ XGDRUM, 16, 32, 16, 37 },
{ XGDRUM, 16, 33, 16, 36 },
{ XGDRUM, 16, 34, 16, 38 },
{ XGDRUM, 16, 62, 16, 101 },
{ XGDRUM, 16, 63, 16, 102 },
{ XGDRUM, 16, 64, 16, 103 },
{ XGDRUM, 24, 25, 40, 38 },
{ XGDRUM, 24, 26, 40, 40 },
{ XGDRUM, 24, 27, 40, 39 },
{ XGDRUM, 24, 28, 24, 100 },
{ XGDRUM, 24, 29, 24, 25 },
{ XGDRUM, 24, 30, 24, 15 },
{ XGDRUM, 24, 31, 24, 38 },
{ XGDRUM, 24, 32, 24, 37 },
{ XGDRUM, 24, 33, 24, 36 },
{ XGDRUM, 24, 34, 24, 38 },
{ XGDRUM, 24, 62, 24, 101 },
{ XGDRUM, 24, 63, 24, 102 },
{ XGDRUM, 24, 64, 24, 103 },
{ XGDRUM, 24, 78, 0, 17 },
{ XGDRUM, 24, 79, 0, 18 },
{ XGDRUM, 25, 25, 40, 38 },
{ XGDRUM, 25, 26, 40, 40 },
{ XGDRUM, 25, 27, 40, 39 },
{ XGDRUM, 25, 28, 25, 100 },
{ XGDRUM, 25, 29, 25, 25 },
{ XGDRUM, 25, 30, 25, 15 },
{ XGDRUM, 25, 31, 25, 38 },
{ XGDRUM, 25, 32, 25, 37 },
{ XGDRUM, 25, 33, 25, 36 },
{ XGDRUM, 25, 34, 25, 38 },
{ XGDRUM, 25, 78, 0, 17 },
{ XGDRUM, 25, 79, 0, 18 },
{ XGDRUM, 32, 25, 40, 38 },
{ XGDRUM, 32, 26, 40, 40 },
{ XGDRUM, 32, 27, 40, 39 },
{ XGDRUM, 32, 28, 40, 40 },
{ XGDRUM, 32, 29, 32, 25 },
{ XGDRUM, 32, 30, 32, 85 },
{ XGDRUM, 32, 31, 32, 38 },
{ XGDRUM, 32, 32, 32, 37 },
{ XGDRUM, 32, 33, 32, 36 },
{ XGDRUM, 32, 34, 32, 38 },
{ XGDRUM, 32, 62, 32, 101 },
{ XGDRUM, 32, 63, 32, 102 },
{ XGDRUM, 32, 64, 32, 103 },
{ XGDRUM, 40, 25, 40, 38 },
{ XGDRUM, 40, 26, 40, 40 },
{ XGDRUM, 40, 27, 40, 39 },
{ XGDRUM, 40, 28, 40, 40 },
{ XGDRUM, 40, 29, 40, 25 },
{ XGDRUM, 40, 30, 40, 85 },
{ XGDRUM, 40, 31, 40, 39 },
{ XGDRUM, 40, 32, 40, 37 },
{ XGDRUM, 40, 33, 40, 36 },
{ XGDRUM, 40, 34, 40, 38 },
{ XGDRUM, 40, 38, 40, 39 },
{ XGDRUM, 40, 39, 0, 39 },
{ XGDRUM, 40, 40, 40, 38 },
{ XGDRUM, 40, 42, 0, 42 },
{ XGDRUM, 40, 46, 0, 46 },
{ XGDRUM, 40, 62, 40, 101 },
{ XGDRUM, 40, 63, 40, 102 },
{ XGDRUM, 40, 64, 40, 103 },
{ XGDRUM, 40, 87, 40, 87 }
};
}

513
src/timidity/timidity.cpp Normal file
View file

@ -0,0 +1,513 @@
/*
TiMidity -- Experimental MIDI to WAVE converter
Copyright (C) 1995 Tuukka Toivonen <toivonen@clinet.fi>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library 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
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
timidity.c
*/
#include <stdio.h>
#include <stdlib.h>
#include "timidity.h"
#include "templates.h"
#include "m_alloc.h"
#include "cmdlib.h"
#include "c_cvars.h"
#include "i_system.h"
CVAR(String, timidity_config, CONFIG_FILE, CVAR_ARCHIVE|CVAR_GLOBALCONFIG)
namespace Timidity
{
ToneBank *tonebank[MAXBANK], *drumset[MAXBANK];
static FString def_instr_name;
#define MAXWORDS 10
static int read_config_file(const char *name)
{
FILE *fp;
char tmp[1024], *w[MAXWORDS], *cp;
ToneBank *bank = NULL;
int i, j, k, line = 0, words;
static int rcf_count = 0;
if (rcf_count > 50)
{
Printf("Timidity: Probable source loop in configuration files\n");
return (-1);
}
if (!(fp = open_file(name, 1, OF_VERBOSE)))
return -1;
while (fgets(tmp, sizeof(tmp), fp))
{
line++;
w[words = 0] = strtok(tmp, " \t\r\n\240");
if (!w[0]) continue;
/* Originally the TiMidity++ extensions were prefixed like this */
if (strcmp(w[0], "#extension") == 0)
words = -1;
else if (*w[0] == '#')
continue;
while (w[words] && *w[words] != '#' && (words < MAXWORDS))
w[++words] = strtok(0, " \t\r\n\240");
/*
* TiMidity++ adds a number of extensions to the config file format.
* Many of them are completely irrelevant to SDL_sound, but at least
* we shouldn't choke on them.
*
* Unfortunately the documentation for these extensions is often quite
* vague, gramatically strange or completely absent.
*/
if (
!strcmp(w[0], "comm") /* "comm" program second */
|| !strcmp(w[0], "HTTPproxy") /* "HTTPproxy" hostname:port */
|| !strcmp(w[0], "FTPproxy") /* "FTPproxy" hostname:port */
|| !strcmp(w[0], "mailaddr") /* "mailaddr" your-mail-address */
|| !strcmp(w[0], "opt") /* "opt" timidity-options */
)
{
/*
* + "comm" sets some kind of comment -- the documentation is too
* vague for me to understand at this time.
* + "HTTPproxy", "FTPproxy" and "mailaddr" are for reading data
* over a network, rather than from the file system.
* + "opt" specifies default options for TiMidity++.
*
* These are all quite useless for our version of TiMidity, so
* they can safely remain no-ops.
*/
}
else if (!strcmp(w[0], "timeout")) /* "timeout" program second */
{
/*
* Specifies a timeout value of the program. A number of seconds
* before TiMidity kills the note. This may be useful to implement
* later, but I don't see any urgent need for it.
*/
Printf("FIXME: Implement \"timeout\" in TiMidity config.\n");
}
else if (!strcmp(w[0], "copydrumset") /* "copydrumset" drumset */
|| !strcmp(w[0], "copybank")) /* "copybank" bank */
{
/*
* Copies all the settings of the specified drumset or bank to
* the current drumset or bank. May be useful later, but not a
* high priority.
*/
Printf("FIXME: Implement \"%s\" in TiMidity config.\n", w[0]);
}
else if (!strcmp(w[0], "undef")) /* "undef" progno */
{
/*
* Undefines the tone "progno" of the current tone bank (or
* drum set?). Not a high priority.
*/
Printf("FIXME: Implement \"undef\" in TiMidity config.\n");
}
else if (!strcmp(w[0], "altassign")) /* "altassign" prog1 prog2 ... */
{
/*
* Sets the alternate assign for drum set. Whatever that's
* supposed to mean.
*/
Printf("FIXME: Implement \"altassign\" in TiMidity config.\n");
}
else if (!strcmp(w[0], "soundfont") || !strcmp(w[0], "font"))
{
/*
* I can't find any documentation for these, but I guess they're
* an alternative way of loading/unloading instruments.
*
* "soundfont" sf_file "remove"
* "soundfont" sf_file ["order=" order] ["cutoff=" cutoff]
* ["reso=" reso] ["amp=" amp]
* "font" "exclude" bank preset keynote
* "font" "order" order bank preset keynote
*/
Printf("FIXME: Implmement \"%s\" in TiMidity config.\n", w[0]);
}
else if (!strcmp(w[0], "progbase"))
{
/*
* The documentation for this makes absolutely no sense to me, but
* apparently it sets some sort of base offset for tone numbers.
* Why anyone would want to do this is beyond me.
*/
Printf("FIXME: Implement \"progbase\" in TiMidity config.\n");
}
else if (!strcmp(w[0], "map")) /* "map" name set1 elem1 set2 elem2 */
{
/*
* This extension is the one we will need to implement, as it is
* used by the "eawpats". Unfortunately I cannot find any
* documentation whatsoever for it, but it looks like it's used
* for remapping one instrument to another somehow.
*/
Printf("FIXME: Implement \"map\" in TiMidity config.\n");
}
/* Standard TiMidity config */
else if (!strcmp(w[0], "dir"))
{
if (words < 2)
{
Printf("%s: line %d: No directory given\n", name, line);
return -2;
}
for (i = 1; i < words; i++)
add_to_pathlist(w[i]);
}
else if (!strcmp(w[0], "source"))
{
if (words < 2)
{
Printf("%s: line %d: No file name given\n", name, line);
return -2;
}
for (i=1; i<words; i++)
{
rcf_count++;
read_config_file(w[i]);
rcf_count--;
}
}
else if (!strcmp(w[0], "default"))
{
if (words != 2)
{
Printf("%s: line %d: Must specify exactly one patch name\n", name, line);
return -2;
}
def_instr_name = w[1];
}
else if (!strcmp(w[0], "drumset"))
{
if (words < 2)
{
Printf("%s: line %d: No drum set number given\n", name, line);
return -2;
}
i = atoi(w[1]);
if (i < 0 || i > 127)
{
Printf("%s: line %d: Drum set must be between 0 and 127\n", name, line);
return -2;
}
if (drumset[i] == NULL)
{
drumset[i] = new ToneBank;
}
bank = drumset[i];
}
else if (!strcmp(w[0], "bank"))
{
if (words < 2)
{
Printf("%s: line %d: No bank number given\n", name, line);
return -2;
}
i = atoi(w[1]);
if (i < 0 || i > 127)
{
Printf("%s: line %d: Tone bank must be between 0 and 127\n", name, line);
return -2;
}
if (tonebank[i] == NULL)
{
tonebank[i] = new ToneBank;
}
bank = tonebank[i];
}
else
{
if ((words < 2) || (*w[0] < '0' || *w[0] > '9'))
{
Printf("%s: line %d: syntax error\n", name, line);
return -2;
}
i = atoi(w[0]);
if (i < 0 || i > 127)
{
Printf("%s: line %d: Program must be between 0 and 127\n", name, line);
return -2;
}
if (bank == NULL)
{
Printf("%s: line %d: Must specify tone bank or drum set before assignment\n", name, line);
return -2;
}
bank->tone[i].name = w[1];
bank->tone[i].note = bank->tone[i].amp = bank->tone[i].pan =
bank->tone[i].strip_loop = bank->tone[i].strip_envelope =
bank->tone[i].strip_tail = -1;
for (j = 2; j<words; j++)
{
if (!(cp=strchr(w[j], '=')))
{
Printf("%s: line %d: bad patch option %s\n", name, line, w[j]);
return -2;
}
*cp++ = 0;
if (!strcmp(w[j], "amp"))
{
k = atoi(cp);
if ((k < 0 || k > MAX_AMPLIFICATION) || (*cp < '0' || *cp > '9'))
{
Printf("%s: line %d: amplification must be between 0 and %d\n", name, line, MAX_AMPLIFICATION);
return -2;
}
bank->tone[i].amp = k;
}
else if (!strcmp(w[j], "note"))
{
k = atoi(cp);
if ((k < 0 || k > 127) || (*cp < '0' || *cp > '9'))
{
Printf("%s: line %d: note must be between 0 and 127\n", name, line);
return -2;
}
bank->tone[i].note = k;
}
else if (!strcmp(w[j], "pan"))
{
if (!strcmp(cp, "center"))
k = 64;
else if (!strcmp(cp, "left"))
k = 0;
else if (!strcmp(cp, "right"))
k = 127;
else
k = ((atoi(cp)+100) * 100) / 157;
if ((k < 0 || k > 127) ||
(k == 0 && *cp != '-' && (*cp < '0' || *cp > '9')))
{
Printf("%s: line %d: panning must be left, right, "
"center, or between -100 and 100\n", name, line);
return -2;
}
bank->tone[i].pan = k;
}
else if (!strcmp(w[j], "keep"))
{
if (!strcmp(cp, "env"))
bank->tone[i].strip_envelope = 0;
else if (!strcmp(cp, "loop"))
bank->tone[i].strip_loop = 0;
else
{
Printf("%s: line %d: keep must be env or loop\n", name, line);
return -2;
}
}
else if (!strcmp(w[j], "strip"))
{
if (!strcmp(cp, "env"))
bank->tone[i].strip_envelope = 1;
else if (!strcmp(cp, "loop"))
bank->tone[i].strip_loop = 1;
else if (!strcmp(cp, "tail"))
bank->tone[i].strip_tail = 1;
else
{
Printf("%s: line %d: strip must be env, loop, or tail\n", name, line);
return -2;
}
}
else
{
Printf("%s: line %d: bad patch option %s\n", name, line, w[j]);
return -2;
}
}
}
}
if (ferror(fp))
{
Printf("Can't read %s: %s\n", name, strerror(errno));
close_file(fp);
return -2;
}
close_file(fp);
return 0;
}
void FreeAll()
{
free_instruments();
for (int i = 0; i < MAXBANK; ++i)
{
if (tonebank[i] != NULL)
{
delete tonebank[i];
tonebank[i] = NULL;
}
if (drumset[i] != NULL)
{
delete drumset[i];
drumset[i] = NULL;
}
}
}
int LoadConfig()
{
static bool set_initial_path = false;
if (!set_initial_path)
{
#ifdef _WIN32
add_to_pathlist("\\TIMIDITY");
add_to_pathlist(progdir);
#else
add_to_pathlist("/usr/local/lib/timidity");
add_to_pathlist("/etc/timidity");
add_to_pathlist("/etc");
#endif
set_initial_path = true;
}
/* Some functions get aggravated if not even the standard banks are available. */
if (tonebank[0] == NULL)
{
tonebank[0] = new ToneBank;
drumset[0] = new ToneBank;
}
return read_config_file(timidity_config);
}
Renderer::Renderer(float sample_rate)
{
ctl = new ControlMode;
rate = sample_rate;
patches = NULL;
default_instrument = NULL;
#ifdef FAST_DECAY
fast_decay = true;
#else
fast_decay = false;
#endif
resample_buffer_size = 0;
resample_buffer = NULL;
control_ratio = clamp(int(rate / CONTROLS_PER_SECOND), 1, MAX_CONTROL_RATIO);
if (def_instr_name.IsNotEmpty())
set_default_instrument(def_instr_name);
voices = DEFAULT_VOICES;
memset(voice, 0, sizeof(voice));
memset(drumvolume, 0, sizeof(drumvolume));
memset(drumpanpot, 0, sizeof(drumpanpot));
memset(drumreverberation, 0, sizeof(drumreverberation));
memset(drumchorusdepth, 0, sizeof(drumchorusdepth));
drumchannels = DEFAULT_DRUMCHANNELS;
}
Renderer::~Renderer()
{
if (resample_buffer != NULL)
{
M_Free(resample_buffer);
}
}
void Renderer::ComputeOutput(float *buffer, int count)
{
// count is in samples, not bytes.
if (count <= 0)
{
return;
}
Voice *v = &voice[0];
memset(buffer, 0, sizeof(float)*count*2); // An integer 0 is also a float 0.
if (resample_buffer_size < count)
{
resample_buffer_size = count;
resample_buffer = (sample_t *)M_Realloc(resample_buffer, count * sizeof(float) * 2);
}
for (int i = 0; i < voices; i++, v++)
{
if (v->status != VOICE_FREE)
{
if (v->sample_offset == 0 && v->echo_delay_count)
{
if (v->echo_delay_count >= count)
{
v->echo_delay_count -= count;
}
else
{
mix_voice(this, buffer + v->echo_delay_count, v, count - v->echo_delay_count);
v->echo_delay_count = 0;
}
}
else
{
mix_voice(this, buffer, v, count);
}
}
}
}
void Renderer::MarkInstrument(int banknum, int percussion, int instr)
{
ToneBank *bank;
if (banknum >= MAXBANK)
{
return;
}
if (percussion)
{
bank = drumset[banknum];
}
else
{
bank = tonebank[banknum];
}
if (bank == NULL)
{
return;
}
if (bank->tone[instr].layer == NULL)
{
bank->tone[instr].layer = MAGIC_LOAD_INSTRUMENT;
}
}
ControlMode::~ControlMode()
{
}
void ControlMode::cmsg(int type, int verbosity_level, const char *fmt, ...)
{
}
}

604
src/timidity/timidity.h Normal file
View file

@ -0,0 +1,604 @@
/*
TiMidity -- Experimental MIDI to WAVE converter
Copyright (C) 1995 Tuukka Toivonen <toivonen@clinet.fi>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library 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
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#ifndef TIMIDITY_H
#define TIMIDITY_H
#include "doomtype.h"
#include "zstring.h"
namespace Timidity
{
/*
config.h
*/
/* Acoustic Grand Piano seems to be the usual default instrument. */
#define DEFAULT_PROGRAM 0
/* 9 here is MIDI channel 10, which is the standard percussion channel.
Some files (notably C:\WINDOWS\CANYON.MID) think that 16 is one too.
On the other hand, some files know that 16 is not a drum channel and
try to play music on it. This is now a runtime option, so this isn't
a critical choice anymore. */
#define DEFAULT_DRUMCHANNELS (1<<9)
/*#define DEFAULT_DRUMCHANNELS ((1<<9) | (1<<15))*/
/* Default sampling rate, default polyphony, and maximum polyphony.
All but the last can be overridden from the command line. */
#define DEFAULT_RATE 32000
#define DEFAULT_VOICES 32
#define MAX_VOICES 256
#define MAXCHAN 16
#define MAXNOTE 128
/* 1000 here will give a control ratio of 22:1 with 22 kHz output.
Higher CONTROLS_PER_SECOND values allow more accurate rendering
of envelopes and tremolo. The cost is CPU time. */
#define CONTROLS_PER_SECOND 1000
/* Make envelopes twice as fast. Saves ~20% CPU time (notes decay
faster) and sounds more like a GUS. There is now a command line
option to toggle this as well. */
//#define FAST_DECAY
/* How many bits to use for the fractional part of sample positions.
This affects tonal accuracy. The entire position counter must fit
in 32 bits, so with FRACTION_BITS equal to 12, the maximum size of
a sample is 1048576 samples (2 megabytes in memory). The GUS gets
by with just 9 bits and a little help from its friends...
"The GUS does not SUCK!!!" -- a happy user :) */
#define FRACTION_BITS 12
/* For some reason the sample volume is always set to maximum in all
patch files. Define this for a crude adjustment that may help
equalize instrument volumes. */
#define ADJUST_SAMPLE_VOLUMES
/* The number of samples to use for ramping out a dying note. Affects
click removal. */
#define MAX_DIE_TIME 20
/**************************************************************************/
/* Anything below this shouldn't need to be changed unless you're porting
to a new machine with other than 32-bit, big-endian words. */
/**************************************************************************/
/* change FRACTION_BITS above, not these */
#define INTEGER_BITS (32 - FRACTION_BITS)
#define INTEGER_MASK (0xFFFFFFFF << FRACTION_BITS)
#define FRACTION_MASK (~ INTEGER_MASK)
#define MAX_SAMPLE_SIZE (1 << INTEGER_BITS)
/* This is enforced by some computations that must fit in an int */
#define MAX_CONTROL_RATIO 255
#define MAX_AMPLIFICATION 800
/* The TiMiditiy configuration file */
#define CONFIG_FILE "timidity.cfg"
typedef float sample_t;
typedef float final_volume_t;
#define FINAL_VOLUME(v) (v)
#define FSCALE(a,b) ((a) * (float)(1<<(b)))
#define FSCALENEG(a,b) ((a) * (1.0L / (float)(1<<(b))))
/* Vibrato and tremolo Choices of the Day */
#define SWEEP_TUNING 38
#define VIBRATO_AMPLITUDE_TUNING 1.0L
#define VIBRATO_RATE_TUNING 38
#define TREMOLO_AMPLITUDE_TUNING 1.0L
#define TREMOLO_RATE_TUNING 38
#define SWEEP_SHIFT 16
#define RATE_SHIFT 5
#define VIBRATO_SAMPLE_INCREMENTS 32
#ifndef PI
#define PI 3.14159265358979323846
#endif
#if defined(__GNUC__) && (defined(__i386__) || defined(__x86_64__))
// [RH] MinGW's pow() function is terribly slow compared to VC8's
// (I suppose because it's using an old version from MSVCRT.DLL).
// On an Opteron running x86-64 Linux, this also ended up being about
// 100 cycles faster than libm's pow(), which is why I'm using this
// for GCC in general and not just for MinGW.
extern __inline__ double pow_x87_inline(double x,double y)
{
double result;
if (y == 0)
{
return 1;
}
if (x == 0)
{
if (y > 0)
{
return 0;
}
else
{
union { double fp; long long ip; } infinity;
infinity.ip = 0x7FF0000000000000ll;
return infinity.fp;
}
}
__asm__ (
"fyl2x\n\t"
"fld %%st(0)\n\t"
"frndint\n\t"
"fxch\n\t"
"fsub %%st(1),%%st(0)\n\t"
"f2xm1\n\t"
"fld1\n\t"
"faddp\n\t"
"fxch\n\t"
"fld1\n\t"
"fscale\n\t"
"fstp %%st(1)\n\t"
"fmulp\n\t"
: "=t" (result)
: "0" (x), "u" (y)
: "st(1)", "st(7)", "%3", "%4" );
return result;
}
#define pow pow_x87_inline
#endif
/*
common.h
*/
extern FString current_filename;
/* Noise modes for open_file */
#define OF_SILENT 0
#define OF_NORMAL 1
#define OF_VERBOSE 2
extern FILE *open_file(const char *name, int decompress, int noise_mode);
extern void add_to_pathlist(const char *s);
extern void close_file(FILE *fp);
extern void skip(FILE *fp, size_t len);
extern void *safe_malloc(size_t count);
/*
controls.h
*/
#define CMSG_INFO 0
#define CMSG_WARNING 1
#define CMSG_ERROR 2
#define CMSG_FATAL 3
#define CMSG_TRACE 4
#define CMSG_TIME 5
#define CMSG_TOTAL 6
#define CMSG_FILE 7
#define CMSG_TEXT 8
#define VERB_NORMAL 0
#define VERB_VERBOSE 1
#define VERB_NOISY 2
#define VERB_DEBUG 3
#define VERB_DEBUG_SILLY 4
struct ControlMode
{
virtual ~ControlMode();
void cmsg(int type, int verbosity_level, const char *fmt, ...);
};
/*
instrum.h
*/
struct Sample
{
SDWORD
loop_start, loop_end, data_length,
sample_rate, low_vel, high_vel, low_freq, high_freq, root_freq;
SDWORD
envelope_rate[7], envelope_offset[7];
float
modulation_rate[7], modulation_offset[7];
float
volume, resonance,
modEnvToFilterFc, modEnvToPitch, modLfoToFilterFc;
sample_t *data;
SDWORD
tremolo_sweep_increment, tremolo_phase_increment,
lfo_sweep_increment, lfo_phase_increment,
vibrato_sweep_increment, vibrato_control_ratio,
cutoff_freq;
BYTE
reverberation, chorusdepth,
tremolo_depth, vibrato_depth,
modes,
attenuation;
WORD
freq_center, panning;
SBYTE
note_to_use, exclusiveClass;
SWORD
keyToModEnvHold, keyToModEnvDecay,
keyToVolEnvHold, keyToVolEnvDecay;
SDWORD
freq_scale;
};
void convert_sample_data(Sample *sample, const void *data);
void free_instruments();
/* Bits in modes: */
#define MODES_16BIT (1<<0)
#define MODES_UNSIGNED (1<<1)
#define MODES_LOOPING (1<<2)
#define MODES_PINGPONG (1<<3)
#define MODES_REVERSE (1<<4)
#define MODES_SUSTAIN (1<<5)
#define MODES_ENVELOPE (1<<6)
#define MODES_FAST_RELEASE (1<<7)
#define INST_GUS 0
#define INST_SF2 1
#define INST_DLS 2
struct Instrument
{
int type;
int samples;
Sample *sample;
int left_samples;
Sample *left_sample;
int right_samples;
Sample *right_sample;
};
struct InstrumentLayer
{
BYTE lo, hi;
Instrument *instrument;
InstrumentLayer *next;
};
struct cfg_type
{
int font_code;
int num;
const char *name;
};
#define FONT_NORMAL 0
#define FONT_FFF 1
#define FONT_SBK 2
#define FONT_TONESET 3
#define FONT_DRUMSET 4
#define FONT_PRESET 5
struct ToneBankElement
{
ToneBankElement() : layer(NULL), font_type(0), sf_ix(0), tuning(0),
note(0), amp(0), pan(0), strip_loop(0), strip_envelope(0), strip_tail(0)
{}
FString name;
InstrumentLayer *layer;
int font_type, sf_ix, tuning;
int note, amp, pan, strip_loop, strip_envelope, strip_tail;
};
/* A hack to delay instrument loading until after reading the
entire MIDI file. */
#define MAGIC_LOAD_INSTRUMENT ((InstrumentLayer *)(-1))
#define MAXPROG 128
#define MAXBANK 130
#define SFXBANK (MAXBANK-1)
#define SFXDRUM1 (MAXBANK-2)
#define SFXDRUM2 (MAXBANK-1)
#define XGDRUM 1
struct ToneBank
{
FString name;
ToneBankElement tone[MAXPROG];
};
#define SPECIAL_PROGRAM -1
extern void pcmap(int *b, int *v, int *p, int *drums);
/*
mix.h
*/
extern void mix_voice(struct Renderer *song, float *buf, struct Voice *v, int c);
extern int recompute_envelope(struct Voice *v);
extern void apply_envelope_to_amp(struct Voice *v);
/*
playmidi.h
*/
/* Midi events */
#define ME_NOTEOFF 0x80
#define ME_NOTEON 0x90
#define ME_KEYPRESSURE 0xA0
#define ME_CONTROLCHANGE 0xB0
#define ME_PROGRAM 0xC0
#define ME_CHANNELPRESSURE 0xD0
#define ME_PITCHWHEEL 0xE0
/* Controllers */
#define CTRL_BANK_SELECT 0
#define CTRL_DATA_ENTRY 6
#define CTRL_VOLUME 7
#define CTRL_PAN 10
#define CTRL_EXPRESSION 11
#define CTRL_SUSTAIN 64
#define CTRL_HARMONICCONTENT 71
#define CTRL_RELEASETIME 72
#define CTRL_ATTACKTIME 73
#define CTRL_BRIGHTNESS 74
#define CTRL_REVERBERATION 91
#define CTRL_CHORUSDEPTH 93
#define CTRL_NRPN_LSB 98
#define CTRL_NRPN_MSB 99
#define CTRL_RPN_LSB 100
#define CTRL_RPN_MSB 101
#define CTRL_ALL_SOUNDS_OFF 120
#define CTRL_RESET_CONTROLLERS 121
#define CTRL_ALL_NOTES_OFF 123
/* NRPNs */
#define NRPN_BRIGHTNESS 0x00A0
#define NRPN_HARMONICCONTENT 0x00A1
#define NRPN_DRUMVOLUME (26<<7) // drum number in low 7 bits
#define NRPN_DRUMPANPOT (28<<7) // "
#define NRPN_DRUMREVERBERATION (29<<7) // "
#define NRPN_DRUMCHORUSDEPTH (30<<7) // "
/* RPNs */
#define RPN_PITCH_SENS 0x0000
#define RPN_FINE_TUNING 0x0001
#define RPN_COARSE_TUNING 0x0002
#define RPN_RESET 0x3fff
#define SFX_BANKTYPE 64
struct Channel
{
int
bank, program, sustain, pitchbend,
mono, /* one note only on this channel -- not implemented yet */
/* new stuff */
variationbank, reverberation, chorusdepth, harmoniccontent,
releasetime, attacktime, brightness, kit, sfx,
/* end new */
pitchsens;
WORD
volume, expression;
SWORD
panning;
WORD
rpn, nrpn;
bool
nrpn_mode;
char
transpose;
float
pitchfactor; /* precomputed pitch bend factor to save some fdiv's */
};
/* Causes the instrument's default panning to be used. */
#define NO_PANNING -1
/* envelope points */
#define MAXPOINT 7
struct Voice
{
BYTE
status, channel, note, velocity, clone_type;
Sample *sample;
Sample *left_sample;
Sample *right_sample;
int clone_voice;
float
orig_frequency, frequency;
int
sample_offset, loop_start, loop_end;
int
envelope_volume, modulation_volume;
int
envelope_target, modulation_target;
int
tremolo_sweep, tremolo_sweep_position, tremolo_phase,
lfo_sweep, lfo_sweep_position, lfo_phase,
vibrato_sweep, vibrato_sweep_position, vibrato_depth,
echo_delay_count;
int
echo_delay,
sample_increment,
envelope_increment,
modulation_increment,
tremolo_phase_increment,
lfo_phase_increment;
final_volume_t left_mix, right_mix;
float
left_amp, right_amp,
volume, tremolo_volume, lfo_volume;
int
vibrato_sample_increment[VIBRATO_SAMPLE_INCREMENTS];
int
envelope_rate[MAXPOINT], envelope_offset[MAXPOINT];
int
vibrato_phase, vibrato_control_ratio, vibrato_control_counter,
envelope_stage, modulation_stage, control_counter,
modulation_delay, modulation_counter, panning, panned;
};
/* Voice status options: */
#define VOICE_FREE 0
#define VOICE_ON 1
#define VOICE_SUSTAINED 2
#define VOICE_OFF 3
#define VOICE_DIE 4
/* Voice panned options: */
#define PANNED_MYSTERY 0
#define PANNED_LEFT 1
#define PANNED_RIGHT 2
#define PANNED_CENTER 3
/* Anything but PANNED_MYSTERY only uses the left volume */
/* Envelope stages: */
#define ATTACK 0
#define HOLD 1
#define DECAY 2
#define RELEASE 3
#define RELEASEB 4
#define RELEASEC 5
#define DELAY 6
#define ISDRUMCHANNEL(c) ((drumchannels & (1<<(c))))
/*
resample.h
*/
extern sample_t *resample_voice(struct Renderer *song, Voice *v, int *countptr);
extern void pre_resample(struct Renderer *song, Sample *sp);
/*
tables.h
*/
#define sine(x) (sin((2*PI/1024.0) * (x)))
#define note_to_freq(x) (float(8175.7989473096690661233836992789 * pow(2.0, (x) / 12.0)))
// Use TiMidity++'s volume equation rather than TiMidity's, since it's louder.
//#define calc_vol(x) (pow(2.0,((x)*6.0 - 6.0)))
#define calc_vol(x) (pow((double)(x), (double)1.66096404744))
#define XMAPMAX 800
extern const BYTE xmap[XMAPMAX][5];
/*
timidity.h
*/
struct DLS_Data;
extern int LoadConfig();
extern void FreeAll();
extern ToneBank *tonebank[MAXBANK];
extern ToneBank *drumset[MAXBANK];
struct Renderer
{
ControlMode *ctl;
float rate;
DLS_Data *patches;
InstrumentLayer *default_instrument;
int default_program;
bool fast_decay;
int resample_buffer_size;
sample_t *resample_buffer;
Channel channel[16];
Voice voice[MAX_VOICES];
signed char drumvolume[MAXCHAN][MAXNOTE];
signed char drumpanpot[MAXCHAN][MAXNOTE];
signed char drumreverberation[MAXCHAN][MAXNOTE];
signed char drumchorusdepth[MAXCHAN][MAXNOTE];
int control_ratio, amp_with_poly;
int drumchannels;
int adjust_panning_immediately;
int voices;
int GM_System_On;
int XG_System_On;
int GS_System_On;
int XG_System_reverb_type;
int XG_System_chorus_type;
int XG_System_variation_type;
int lost_notes, cut_notes;
Renderer(float sample_rate);
~Renderer();
void HandleEvent(int status, int parm1, int parm2);
void HandleLongMessage(const BYTE *data, int len);
void HandleController(int chan, int ctrl, int val);
void ComputeOutput(float *buffer, int num_samples);
void MarkInstrument(int bank, int percussion, int instr);
void Reset();
int load_missing_instruments();
int set_default_instrument(const char *name);
int convert_tremolo_sweep(BYTE sweep);
int convert_vibrato_sweep(BYTE sweep, int vib_control_ratio);
int convert_tremolo_rate(BYTE rate);
int convert_vibrato_rate(BYTE rate);
void recompute_amp(Voice *v);
int vc_alloc(int not_this_voice);
void kill_others(int voice);
void clone_voice(Instrument *ip, int v, int note, int vel, int clone_type, int variationbank);
void xremap(int *banknumpt, int *this_notept, int this_kit);
void start_note(int chan, int note, int vel, int voice);
void note_on(int chan, int note, int vel);
void note_off(int chan, int note, int vel);
void all_notes_off(int chan);
void all_sounds_off(int chan);
void adjust_pressure(int chan, int note, int amount);
void adjust_panning(int chan);
void drop_sustain(int chan);
void adjust_pitchbend(int chan);
void adjust_volume(int chan);
void reset_voices();
void reset_controllers(int chan);
void reset_midi();
void select_sample(int voice, Instrument *instr);
void select_stereo_samples(int voice, InstrumentLayer *layer);
void recompute_freq(int voice);
void kill_note(int voice);
void finish_note(int voice);
void DataEntryCoarseRPN(int chan, int rpn, int val);
void DataEntryFineRPN(int chan, int rpn, int val);
void DataEntryCoarseNRPN(int chan, int nrpn, int val);
void DataEntryFineNRPN(int chan, int nrpn, int val);
};
}
#endif

View file

@ -56,6 +56,34 @@ int CleanWidth, CleanHeight;
CVAR (Bool, hud_scale, false, CVAR_ARCHIVE);
// For routines that take RGB colors, cache the previous lookup in case there
// are several repetitions with the same color.
static int LastPal = -1;
static uint32 LastRGB;
static int PalFromRGB(uint32 rgb)
{
if (LastPal >= 0 && LastRGB == rgb)
{
return LastPal;
}
// Quick check for black and white.
if (rgb == MAKEARGB(255,0,0,0))
{
LastPal = GPalette.BlackIndex;
}
else if (rgb == MAKEARGB(255,255,255,255))
{
LastPal = GPalette.WhiteIndex;
}
else
{
LastPal = ColorMatcher.Pick(RPART(rgb), GPART(rgb), BPART(rgb));
}
LastRGB = rgb;
return LastPal;
}
void STACK_ARGS DCanvas::DrawTexture (FTexture *img, int x, int y, int tags_first, ...)
{
va_list tags;
@ -757,15 +785,7 @@ void DCanvas::DrawLine(int x0, int y0, int x1, int y1, int palColor, uint32 real
if (palColor < 0)
{
// Quick check for black.
if (realcolor == MAKEARGB(255,0,0,0))
{
palColor = 0;
}
else
{
palColor = ColorMatcher.Pick(RPART(realcolor), GPART(realcolor), BPART(realcolor));
}
palColor = PalFromRGB(realcolor);
}
Lock();
@ -918,22 +938,52 @@ void DCanvas::DrawPixel(int x, int y, int palColor, uint32 realcolor)
{
if (palColor < 0)
{
// Quick check for black.
if (realcolor == MAKEARGB(255,0,0,0))
{
palColor = 0;
}
else
{
palColor = ColorMatcher.Pick(RPART(realcolor), GPART(realcolor), BPART(realcolor));
}
palColor = PalFromRGB(realcolor);
}
Lock();
GetBuffer()[GetPitch() * y + x] = (BYTE)palColor;
Unlock();
Buffer[Pitch * y + x] = (BYTE)palColor;
}
//==========================================================================
//
// DCanvas :: Clear
//
// Set an area to a specified color.
//
//==========================================================================
void DCanvas::Clear (int left, int top, int right, int bottom, int palcolor, uint32 color)
{
int x, y;
BYTE *dest;
if (left == right || top == bottom)
{
return;
}
assert(left < right);
assert(top < bottom);
if (palcolor < 0)
{
if (APART(color) != 255)
{
Dim(color, APART(color)/255.f, left, top, right - left, bottom - top);
return;
}
palcolor = PalFromRGB(color);
}
dest = Buffer + top * Pitch + left;
x = right - left;
for (y = top; y < bottom; y++)
{
memset(dest, palcolor, x);
dest += Pitch;
}
}
/********************************/
/* */

View file

@ -284,55 +284,6 @@ void DCanvas::FlatFill (int left, int top, int right, int bottom, FTexture *src,
}
}
//==========================================================================
//
// DCanvas :: Clear
//
// Set an area to a specified color.
//
//==========================================================================
void DCanvas::Clear (int left, int top, int right, int bottom, int palcolor, uint32 color)
{
int x, y;
BYTE *dest;
if (left == right || top == bottom)
{
return;
}
assert(left < right);
assert(top < bottom);
if (palcolor < 0)
{
if (APART(color) != 255)
{
Dim(color, APART(color)/255.f, left, top, right - left, bottom - top);
return;
}
// Quick check for black.
if (color == MAKEARGB(255,0,0,0))
{
palcolor = 0;
}
else
{
palcolor = ColorMatcher.Pick(RPART(color), GPART(color), BPART(color));
}
}
dest = Buffer + top * Pitch + left;
x = right - left;
for (y = top; y < bottom; y++)
{
memset(dest, palcolor, x);
dest += Pitch;
}
}
//==========================================================================
//
// DCanvas :: Dim

View file

@ -823,9 +823,18 @@ void DoMain (HINSTANCE hInstance)
atterm (I_Quit);
// Figure out what directory the program resides in.
GetModuleFileName (NULL, progdir, 1024);
*(strrchr (progdir, '\\') + 1) = 0;
FixPathSeperator (progdir);
char *program;
if (_get_pgmptr(&program) != 0)
{
I_FatalError("Could not determine program location.");
}
progdir = program;
program = progdir.LockBuffer();
*(strrchr(program, '\\') + 1) = '\0';
FixPathSeperator(program);
progdir.Truncate((long)strlen(program));
progdir.UnlockBuffer();
/*
height = GetSystemMetrics (SM_CYFIXEDFRAME) * 2 +
GetSystemMetrics (SM_CYCAPTION) + 12 * 32;

View file

@ -2868,6 +2868,10 @@
RelativePath="src\sound\music_stream.cpp"
>
</File>
<File
RelativePath=".\src\sound\music_timidity_mididevice.cpp"
>
</File>
<File
RelativePath=".\src\sound\music_win_mididevice.cpp"
>
@ -2927,6 +2931,122 @@
<Filter
Name="Timidity"
>
<File
RelativePath=".\src\timidity\common.cpp"
>
</File>
<File
RelativePath=".\src\timidity\dls1.h"
>
</File>
<File
RelativePath=".\src\timidity\dls2.h"
>
</File>
<File
RelativePath=".\src\timidity\instrum.cpp"
>
</File>
<File
RelativePath=".\src\timidity\instrum_dls.cpp"
>
</File>
<File
RelativePath=".\src\timidity\mix.cpp"
>
</File>
<File
RelativePath=".\src\timidity\playmidi.cpp"
>
<FileConfiguration
Name="Release|Win32"
>
<Tool
Name="VCCLCompilerTool"
EnableEnhancedInstructionSet="0"
/>
</FileConfiguration>
</File>
<File
RelativePath=".\src\timidity\resample.cpp"
>
<FileConfiguration
Name="Release|Win32"
>
<Tool
Name="VCCLCompilerTool"
EnableEnhancedInstructionSet="0"
/>
</FileConfiguration>
</File>
<File
RelativePath=".\src\timidity\tables.cpp"
>
<FileConfiguration
Name="Release|Win32"
>
<Tool
Name="VCCLCompilerTool"
ObjectFile="$(IntDir)\$(InputName)1.obj"
XMLDocumentationFileName="$(IntDir)\$(InputName)1.xdc"
/>
</FileConfiguration>
<FileConfiguration
Name="Release|x64"
>
<Tool
Name="VCCLCompilerTool"
ObjectFile="$(IntDir)\$(InputName)1.obj"
XMLDocumentationFileName="$(IntDir)\$(InputName)1.xdc"
/>
</FileConfiguration>
<FileConfiguration
Name="Debug|Win32"
>
<Tool
Name="VCCLCompilerTool"
ObjectFile="$(IntDir)\$(InputName)1.obj"
XMLDocumentationFileName="$(IntDir)\$(InputName)1.xdc"
/>
</FileConfiguration>
<FileConfiguration
Name="Debug|x64"
>
<Tool
Name="VCCLCompilerTool"
ObjectFile="$(IntDir)\$(InputName)1.obj"
XMLDocumentationFileName="$(IntDir)\$(InputName)1.xdc"
/>
</FileConfiguration>
</File>
<File
RelativePath=".\src\timidity\timidity.cpp"
>
</File>
<File
RelativePath=".\src\timidity\timidity.h"
>
</File>
<Filter
Name="Docs"
>
<File
RelativePath=".\src\timidity\CHANGES"
>
</File>
<File
RelativePath=".\src\timidity\COPYING"
>
</File>
<File
RelativePath=".\src\timidity\FAQ"
>
</File>
<File
RelativePath=".\src\timidity\README"
>
</File>
</Filter>
</Filter>
</Filter>
<Filter