mirror of
https://github.com/ZDoom/qzdoom.git
synced 2024-11-10 14:51:51 +00:00
- 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:
parent
3f497fe8e5
commit
10c0d67b78
40 changed files with 8807 additions and 112 deletions
|
@ -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/
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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('\\', '/'); }
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -50,6 +50,7 @@
|
|||
#include "gi.h"
|
||||
#include "templates.h"
|
||||
#include "zstring.h"
|
||||
#include "timidity/timidity.h"
|
||||
|
||||
// MACROS ------------------------------------------------------------------
|
||||
|
||||
|
|
|
@ -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 ();
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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.";
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
519
src/sound/music_timidity_mididevice.cpp
Normal file
519
src/sound/music_timidity_mididevice.cpp
Normal 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
58
src/timidity/CHANGES
Normal 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
513
src/timidity/COPYING
Normal 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
100
src/timidity/FAQ
Normal 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
61
src/timidity/README
Normal 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
152
src/timidity/common.cpp
Normal 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
266
src/timidity/dls1.h
Normal 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
130
src/timidity/dls2.h
Normal 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
952
src/timidity/instrum.cpp
Normal 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
BIN
src/timidity/instrum.obj
Normal file
Binary file not shown.
1260
src/timidity/instrum_dls.cpp
Normal file
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
584
src/timidity/mix.cpp
Normal 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
1575
src/timidity/playmidi.cpp
Normal file
File diff suppressed because it is too large
Load diff
608
src/timidity/resample.cpp
Normal file
608
src/timidity/resample.cpp
Normal 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
224
src/timidity/tables.cpp
Normal 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
513
src/timidity/timidity.cpp
Normal 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
604
src/timidity/timidity.h
Normal 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
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
/********************************/
|
||||
/* */
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
120
zdoom.vcproj
120
zdoom.vcproj
|
@ -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
|
||||
|
|
Loading…
Reference in a new issue