mirror of
https://github.com/ZDoom/qzdoom.git
synced 2024-11-27 22:42:57 +00:00
- Made the maximum number of TiMidity voices configurable through the
timidity_voices cvar. - Added stats lines for the OPL and Timidity MIDI devices. - Completely changed the way TiMidity volume calculations are done. It should now be extremely close to the output a real GUS would produce with its official MIDI player (excepting where TiMidity normalizes sample volumes). The new equations more closely match what is specified by the DLS and SF2 specs (but not quite), so I presume it's also more musically correct than what TiMidity (and TiMidity++) do. SVN r925 (trunk)
This commit is contained in:
parent
48d8881065
commit
f6866df0e6
12 changed files with 337 additions and 296 deletions
|
@ -1,3 +1,14 @@
|
|||
April 18, 2008
|
||||
- Made the maximum number of TiMidity voices configurable through the
|
||||
timidity_voices cvar.
|
||||
- Added stats lines for the OPL and Timidity MIDI devices.
|
||||
- Completely changed the way TiMidity volume calculations are done. It
|
||||
should now be extremely close to the output a real GUS would produce with
|
||||
its official MIDI player (excepting where TiMidity normalizes sample
|
||||
volumes). The new equations more closely match what is specified by the DLS
|
||||
and SF2 specs (but not quite), so I presume it's also more musically
|
||||
correct than what TiMidity (and TiMidity++) do.
|
||||
|
||||
April 18, 2008 (Changes by Graf Zahl)
|
||||
- Fixed: FBlockThingsIterator didn't set the current block coordinates if
|
||||
they were outside the blockmap. This could cause extreme delays if an
|
||||
|
|
|
@ -40,6 +40,7 @@
|
|||
#include "doomdef.h"
|
||||
#include "m_swap.h"
|
||||
#include "w_wad.h"
|
||||
#include "v_text.h"
|
||||
#include "fmopl.h"
|
||||
|
||||
// MACROS ------------------------------------------------------------------
|
||||
|
@ -59,8 +60,6 @@
|
|||
|
||||
// EXTERNAL DATA DECLARATIONS ----------------------------------------------
|
||||
|
||||
extern OPLmusicBlock *BlockForStats;
|
||||
|
||||
// PRIVATE DATA DEFINITIONS ------------------------------------------------
|
||||
|
||||
// PUBLIC DATA DEFINITIONS -------------------------------------------------
|
||||
|
@ -226,7 +225,6 @@ int OPLMIDIDevice::Resume()
|
|||
if (Stream->Play(true, 1))
|
||||
{
|
||||
Started = true;
|
||||
BlockForStats = this;
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
|
@ -246,7 +244,6 @@ void OPLMIDIDevice::Stop()
|
|||
{
|
||||
Stream->Stop();
|
||||
Started = false;
|
||||
BlockForStats = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -514,3 +511,36 @@ bool OPLMIDIDevice::FillStream(SoundStream *stream, void *buff, int len, void *u
|
|||
OPLMIDIDevice *device = (OPLMIDIDevice *)userdata;
|
||||
return device->ServiceStream(buff, len);
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// OPLMIDIDevice :: GetStats
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
FString OPLMIDIDevice::GetStats()
|
||||
{
|
||||
FString out;
|
||||
char star[3] = { TEXTCOLOR_ESCAPE, 'A', '*' };
|
||||
for (uint i = 0; i < io->OPLchannels; ++i)
|
||||
{
|
||||
if (channels[i].flags & CH_FREE)
|
||||
{
|
||||
star[1] = CR_BRICK + 'A';
|
||||
}
|
||||
else if (channels[i].flags & CH_SUSTAIN)
|
||||
{
|
||||
star[1] = CR_ORANGE + 'A';
|
||||
}
|
||||
else if (channels[i].flags & CH_SECONDARY)
|
||||
{
|
||||
star[1] = CR_BLUE + 'A';
|
||||
}
|
||||
else
|
||||
{
|
||||
star[1] = CR_GREEN + 'A';
|
||||
}
|
||||
out.AppendCStrPart (star, 3);
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
|
|
@ -233,7 +233,7 @@ struct musicBlock {
|
|||
|
||||
int OPLloadBank (FileReader &data);
|
||||
|
||||
private:
|
||||
protected:
|
||||
/* OPL channel (voice) data */
|
||||
struct channelEntry {
|
||||
uchar channel; /* MUS channel number */
|
||||
|
|
|
@ -12,15 +12,11 @@
|
|||
#include "c_cvars.h"
|
||||
#include "i_system.h"
|
||||
#include "stats.h"
|
||||
#include "v_text.h"
|
||||
#include "c_dispatch.h"
|
||||
|
||||
#define IMF_RATE 700.0
|
||||
|
||||
EXTERN_CVAR (Bool, opl_onechip)
|
||||
|
||||
OPLmusicBlock *BlockForStats;
|
||||
|
||||
OPLmusicBlock::OPLmusicBlock()
|
||||
{
|
||||
scoredata = NULL;
|
||||
|
@ -33,7 +29,6 @@ OPLmusicBlock::OPLmusicBlock()
|
|||
|
||||
OPLmusicBlock::~OPLmusicBlock()
|
||||
{
|
||||
BlockForStats = NULL;
|
||||
delete io;
|
||||
}
|
||||
|
||||
|
@ -377,36 +372,7 @@ int OPLmusicFile::PlayTick ()
|
|||
|
||||
ADD_STAT (opl)
|
||||
{
|
||||
if (BlockForStats != NULL)
|
||||
{
|
||||
FString out;
|
||||
char star[3] = { TEXTCOLOR_ESCAPE, 'A', '*' };
|
||||
for (uint i = 0; i < BlockForStats->io->OPLchannels; ++i)
|
||||
{
|
||||
if (BlockForStats->channels[i].flags & CH_FREE)
|
||||
{
|
||||
star[1] = CR_BRICK + 'A';
|
||||
}
|
||||
else if (BlockForStats->channels[i].flags & CH_SUSTAIN)
|
||||
{
|
||||
star[1] = CR_ORANGE + 'A';
|
||||
}
|
||||
else if (BlockForStats->channels[i].flags & CH_SECONDARY)
|
||||
{
|
||||
star[1] = CR_BLUE + 'A';
|
||||
}
|
||||
else
|
||||
{
|
||||
star[1] = CR_GREEN + 'A';
|
||||
}
|
||||
out.AppendCStrPart (star, 3);
|
||||
}
|
||||
return out;
|
||||
}
|
||||
else
|
||||
{
|
||||
return YM3812GetVoiceString ();
|
||||
}
|
||||
return YM3812GetVoiceString ();
|
||||
}
|
||||
|
||||
OPLmusicFile::OPLmusicFile(const OPLmusicFile *source, const char *filename)
|
||||
|
|
|
@ -122,7 +122,8 @@ public:
|
|||
virtual bool Pause(bool paused) = 0;
|
||||
virtual bool NeedThreadedCallback() = 0;
|
||||
virtual void PrecacheInstruments(const WORD *instruments, int count);
|
||||
virtual void TimidityVolumeChanged() {}
|
||||
virtual void TimidityVolumeChanged();
|
||||
virtual FString GetStats();
|
||||
};
|
||||
|
||||
// WinMM implementation of a MIDI output device -----------------------------
|
||||
|
@ -185,6 +186,7 @@ public:
|
|||
bool FakeVolume();
|
||||
bool NeedThreadedCallback();
|
||||
bool Pause(bool paused);
|
||||
FString GetStats();
|
||||
|
||||
protected:
|
||||
static bool FillStream(SoundStream *stream, void *buff, int len, void *userdata);
|
||||
|
@ -242,6 +244,7 @@ public:
|
|||
bool NeedThreadedCallback();
|
||||
void PrecacheInstruments(const WORD *instruments, int count);
|
||||
void TimidityVolumeChanged();
|
||||
FString GetStats();
|
||||
|
||||
protected:
|
||||
static bool FillStream(SoundStream *stream, void *buff, int len, void *userdata);
|
||||
|
@ -291,6 +294,7 @@ public:
|
|||
bool IsMIDI() const;
|
||||
bool IsValid() const;
|
||||
void Update();
|
||||
FString GetStats();
|
||||
|
||||
protected:
|
||||
MIDIStreamer(const char *dumpname);
|
||||
|
|
|
@ -719,6 +719,21 @@ int MIDIStreamer::FillBuffer(int buffer_num, int max_events, DWORD max_time)
|
|||
return SONG_MORE;
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// MIDIStreamer :: GetStats
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
FString MIDIStreamer::GetStats()
|
||||
{
|
||||
if (MIDI == NULL)
|
||||
{
|
||||
return "No MIDI device in use.";
|
||||
}
|
||||
return MIDI->GetStats();
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// MIDIDevice stubs.
|
||||
|
@ -752,3 +767,24 @@ MIDIDevice::~MIDIDevice()
|
|||
void MIDIDevice::PrecacheInstruments(const WORD *instruments, int count)
|
||||
{
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// MIDIDevice :: TimidityVolumeChanged
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
void MIDIDevice::TimidityVolumeChanged()
|
||||
{
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// MIDIDevice :: GetStats
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
FString MIDIDevice::GetStats()
|
||||
{
|
||||
return "This MIDI device does not have any stats.";
|
||||
}
|
||||
|
|
|
@ -39,6 +39,7 @@
|
|||
#include "doomdef.h"
|
||||
#include "m_swap.h"
|
||||
#include "w_wad.h"
|
||||
#include "v_text.h"
|
||||
#include "timidity/timidity.h"
|
||||
|
||||
// MACROS ------------------------------------------------------------------
|
||||
|
@ -535,3 +536,63 @@ bool TimidityMIDIDevice::FillStream(SoundStream *stream, void *buff, int len, vo
|
|||
TimidityMIDIDevice *device = (TimidityMIDIDevice *)userdata;
|
||||
return device->ServiceStream(buff, len);
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// TimidityMIDIDevice :: GetStats
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
FString TimidityMIDIDevice::GetStats()
|
||||
{
|
||||
FString dots;
|
||||
FString out;
|
||||
int i, used;
|
||||
|
||||
CritSec.Enter();
|
||||
for (i = used = 0; i < Renderer->voices; ++i)
|
||||
{
|
||||
switch (Renderer->voice[i].status)
|
||||
{
|
||||
case Timidity::VOICE_FREE:
|
||||
dots << TEXTCOLOR_PURPLE".";
|
||||
break;
|
||||
|
||||
case Timidity::VOICE_ON:
|
||||
dots << TEXTCOLOR_GREEN;
|
||||
break;
|
||||
|
||||
case Timidity::VOICE_SUSTAINED:
|
||||
dots << TEXTCOLOR_BLUE;
|
||||
break;
|
||||
|
||||
case Timidity::VOICE_OFF:
|
||||
dots << TEXTCOLOR_ORANGE;
|
||||
break;
|
||||
|
||||
case Timidity::VOICE_DIE:
|
||||
dots << TEXTCOLOR_RED;
|
||||
break;
|
||||
}
|
||||
if (Renderer->voice[i].status != Timidity::VOICE_FREE)
|
||||
{
|
||||
used++;
|
||||
if (Renderer->voice[i].envelope_increment == 0)
|
||||
{
|
||||
dots << TEXTCOLOR_BLUE"+";
|
||||
}
|
||||
else
|
||||
{
|
||||
dots << ('1' + Renderer->voice[i].envelope_stage);
|
||||
}
|
||||
}
|
||||
}
|
||||
CritSec.Leave();
|
||||
out.Format(TEXTCOLOR_YELLOW"%3d/%3d ", used, Renderer->voices);
|
||||
out += dots;
|
||||
if (Renderer->cut_notes | Renderer->lost_notes)
|
||||
{
|
||||
out.AppendFormat(TEXTCOLOR_RED" %d/%d", Renderer->cut_notes, Renderer->lost_notes);
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
|
|
@ -300,6 +300,7 @@ fail:
|
|||
sp->panning = panning & 0x7f;
|
||||
}
|
||||
sp->panning |= sp->panning << 7;
|
||||
song->compute_pan(sp->panning, sp->left_offset, sp->right_offset);
|
||||
|
||||
/* tremolo */
|
||||
if (patch_data.TremoloRate == 0 || patch_data.TremoloDepth == 0)
|
||||
|
|
|
@ -71,37 +71,19 @@ int recompute_envelope(Voice *v)
|
|||
|
||||
void apply_envelope_to_amp(Voice *v)
|
||||
{
|
||||
double lamp = v->left_amp, ramp;
|
||||
if (v->panned == PANNED_MYSTERY)
|
||||
float env_vol = v->attenuation;
|
||||
float final_amp = v->sample->volume * FINAL_MIX_SCALE;
|
||||
if (v->tremolo_phase_increment != 0)
|
||||
{
|
||||
ramp = v->right_amp;
|
||||
|
||||
if (v->tremolo_phase_increment != 0)
|
||||
{
|
||||
lamp *= v->tremolo_volume;
|
||||
ramp *= v->tremolo_volume;
|
||||
}
|
||||
if (v->sample->modes & PATCH_NO_SRELEASE)
|
||||
{
|
||||
double vol = calc_vol(v->envelope_volume / float(1 << 30));
|
||||
lamp *= vol;
|
||||
ramp *= vol;
|
||||
}
|
||||
v->left_mix = float(lamp);
|
||||
v->right_mix = float(ramp);
|
||||
env_vol *= v->tremolo_volume;
|
||||
}
|
||||
else
|
||||
if (v->sample->modes & PATCH_NO_SRELEASE)
|
||||
{
|
||||
if (v->tremolo_phase_increment != 0)
|
||||
{
|
||||
lamp *= v->tremolo_volume;
|
||||
}
|
||||
if (v->sample->modes & PATCH_NO_SRELEASE)
|
||||
{
|
||||
lamp *= calc_vol(v->envelope_volume / float(1 << 30));
|
||||
}
|
||||
v->left_mix = float(lamp);
|
||||
env_vol *= v->envelope_volume / float(1 << 30);
|
||||
}
|
||||
// Note: The pan offsets are negative.
|
||||
v->left_mix = (float)calc_gf1_amp(env_vol + v->left_offset) * final_amp;
|
||||
v->right_mix = (float)calc_gf1_amp(env_vol + v->right_offset) * final_amp;
|
||||
}
|
||||
|
||||
static int update_envelope(Voice *v)
|
||||
|
@ -218,20 +200,18 @@ static void mix_mystery_signal(SDWORD control_ratio, const sample_t *sp, float *
|
|||
}
|
||||
}
|
||||
|
||||
static void mix_center_signal(SDWORD control_ratio, const sample_t *sp, float *lp, Voice *v, int count)
|
||||
static void mix_single_signal(SDWORD control_ratio, const sample_t *sp, float *lp, Voice *v, float *ampat, int count)
|
||||
{
|
||||
final_volume_t
|
||||
left = v->left_mix;
|
||||
final_volume_t amp;
|
||||
int cc;
|
||||
sample_t s;
|
||||
|
||||
if (!(cc = v->control_counter))
|
||||
if (0 == (cc = v->control_counter))
|
||||
{
|
||||
cc = control_ratio;
|
||||
if (update_signal(v))
|
||||
return; /* Envelope ran out */
|
||||
left = v->left_mix;
|
||||
return; /* Envelope ran out */
|
||||
}
|
||||
amp = *ampat;
|
||||
|
||||
while (count)
|
||||
{
|
||||
|
@ -240,24 +220,20 @@ static void mix_center_signal(SDWORD control_ratio, const sample_t *sp, float *l
|
|||
count -= cc;
|
||||
while (cc--)
|
||||
{
|
||||
s = *sp++ * left;
|
||||
lp[0] += s;
|
||||
lp[1] += s;
|
||||
lp[0] += *sp++ * amp;
|
||||
lp += 2;
|
||||
}
|
||||
cc = control_ratio;
|
||||
if (update_signal(v))
|
||||
return; /* Envelope ran out */
|
||||
left = v->left_mix;
|
||||
amp = *ampat;
|
||||
}
|
||||
else
|
||||
{
|
||||
v->control_counter = cc - count;
|
||||
while (count--)
|
||||
{
|
||||
s = *sp++ * left;
|
||||
lp[0] += s;
|
||||
lp[1] += s;
|
||||
lp[0] += *sp++ * amp;
|
||||
lp += 2;
|
||||
}
|
||||
return;
|
||||
|
@ -267,86 +243,12 @@ static void mix_center_signal(SDWORD control_ratio, const sample_t *sp, float *l
|
|||
|
||||
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;
|
||||
}
|
||||
}
|
||||
mix_single_signal(control_ratio, sp, lp, v, &v->left_mix, count);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
mix_single_signal(control_ratio, sp, lp + 1, v, &v->right_mix, count);
|
||||
}
|
||||
|
||||
static void mix_mono_signal(SDWORD control_ratio, const sample_t *sp, float *lp, Voice *v, int count)
|
||||
|
@ -405,42 +307,22 @@ static void mix_mystery(SDWORD control_ratio, const sample_t *sp, float *lp, Voi
|
|||
}
|
||||
}
|
||||
|
||||
static void mix_center(const sample_t *sp, float *lp, Voice *v, int count)
|
||||
static void mix_single(const sample_t *sp, float *lp, final_volume_t amp, int count)
|
||||
{
|
||||
final_volume_t
|
||||
left = v->left_mix;
|
||||
sample_t s;
|
||||
|
||||
while (count--)
|
||||
{
|
||||
s = *sp++ * left;
|
||||
lp[0] += s;
|
||||
lp[1] += s;
|
||||
lp[0] += *sp++ * amp;
|
||||
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;
|
||||
}
|
||||
mix_single(sp, lp, v->left_mix, count);
|
||||
}
|
||||
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;
|
||||
}
|
||||
mix_single(sp, lp + 1, v->right_mix, count);
|
||||
}
|
||||
|
||||
static void mix_mono(const sample_t *sp, float *lp, Voice *v, int count)
|
||||
|
@ -464,41 +346,14 @@ static void ramp_out(const sample_t *sp, float *lp, Voice *v, int c)
|
|||
/* 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)
|
||||
if (v->left_offset == 0) // All the way to the left
|
||||
{
|
||||
left = v->left_mix;
|
||||
li = -(left/c);
|
||||
if (li == 0) li = -1;
|
||||
|
||||
while (c--)
|
||||
{
|
||||
left += li;
|
||||
|
@ -508,15 +363,52 @@ static void ramp_out(const sample_t *sp, float *lp, Voice *v, int c)
|
|||
lp += 2;
|
||||
}
|
||||
}
|
||||
else if (v->panned == PANNED_RIGHT)
|
||||
else if (v->right_offset == 0) // All the way to the right
|
||||
{
|
||||
right = v->right_mix;
|
||||
ri = -(right/c);
|
||||
if (ri == 0) ri = -1;
|
||||
|
||||
while (c--)
|
||||
{
|
||||
right += ri;
|
||||
if (right < 0)
|
||||
return;
|
||||
s = *sp++;
|
||||
lp[1] += *sp++ * right;
|
||||
lp += 2;
|
||||
}
|
||||
}
|
||||
else // Somewhere in the middle
|
||||
{
|
||||
left = v->left_mix;
|
||||
li = -(left/c);
|
||||
if (li == 0) li = -1;
|
||||
right = v->right_mix;
|
||||
ri = -(right/c);
|
||||
if (ri == 0) ri = -1;
|
||||
|
||||
right = v->right_mix;
|
||||
ri = -(right/c);
|
||||
while (c--)
|
||||
{
|
||||
left += li;
|
||||
right += ri;
|
||||
if (left < 0)
|
||||
return;
|
||||
{
|
||||
if (right < 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
left = 0;
|
||||
}
|
||||
else if (right < 0)
|
||||
{
|
||||
right = 0;
|
||||
}
|
||||
s = *sp++;
|
||||
lp[1] += *sp++ * left;
|
||||
lp[0] += s * left;
|
||||
lp[1] += s * right;
|
||||
lp += 2;
|
||||
}
|
||||
}
|
||||
|
@ -548,38 +440,37 @@ void mix_voice(Renderer *song, float *buf, Voice *v, int c)
|
|||
{
|
||||
return;
|
||||
}
|
||||
if (v->panned == PANNED_MYSTERY)
|
||||
if (v->left_offset == 0) // All the way to the left
|
||||
{
|
||||
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->envelope_increment != 0 || v->tremolo_phase_increment != 0)
|
||||
{
|
||||
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);
|
||||
mix_single_left_signal(song->control_ratio, sp, buf, v, count);
|
||||
}
|
||||
else
|
||||
else
|
||||
{
|
||||
if (v->panned == PANNED_RIGHT)
|
||||
mix_single_right(sp, buf, v, count);
|
||||
else
|
||||
mix_single_left(sp, buf, v, count);
|
||||
mix_single_left(sp, buf, v, count);
|
||||
}
|
||||
}
|
||||
else if (v->right_offset == 0) // All the way to the right
|
||||
{
|
||||
if (v->envelope_increment != 0 || v->tremolo_phase_increment != 0)
|
||||
{
|
||||
mix_single_right_signal(song->control_ratio, sp, buf, v, count);
|
||||
}
|
||||
else
|
||||
{
|
||||
mix_single_right(sp, buf, v, count);
|
||||
}
|
||||
}
|
||||
else // Somewhere in the middle
|
||||
{
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -31,9 +31,11 @@
|
|||
namespace Timidity
|
||||
{
|
||||
|
||||
static const double log_of_2 = 0.69314718055994529;
|
||||
|
||||
void Renderer::reset_voices()
|
||||
{
|
||||
for (int i = 0; i < MAX_VOICES; i++)
|
||||
for (int i = 0; i < voices; i++)
|
||||
{
|
||||
voice[i].status = VOICE_FREE;
|
||||
}
|
||||
|
@ -167,36 +169,67 @@ void Renderer::recompute_freq(int v)
|
|||
voice[v].sample_increment = (int)(a);
|
||||
}
|
||||
|
||||
static BYTE vol_table[] = {
|
||||
000 /* 000 */, 129 /* 001 */, 145 /* 002 */, 155 /* 003 */,
|
||||
161 /* 004 */, 166 /* 005 */, 171 /* 006 */, 174 /* 007 */,
|
||||
177 /* 008 */, 180 /* 009 */, 182 /* 010 */, 185 /* 011 */,
|
||||
187 /* 012 */, 188 /* 013 */, 190 /* 014 */, 192 /* 015 */,
|
||||
193 /* 016 */, 195 /* 017 */, 196 /* 018 */, 197 /* 019 */,
|
||||
198 /* 020 */, 199 /* 021 */, 201 /* 022 */, 202 /* 023 */,
|
||||
203 /* 024 */, 203 /* 025 */, 204 /* 026 */, 205 /* 027 */,
|
||||
206 /* 028 */, 207 /* 029 */, 208 /* 030 */, 208 /* 031 */,
|
||||
209 /* 032 */, 210 /* 033 */, 211 /* 034 */, 211 /* 035 */,
|
||||
212 /* 036 */, 213 /* 037 */, 213 /* 038 */, 214 /* 039 */,
|
||||
214 /* 040 */, 215 /* 041 */, 215 /* 042 */, 216 /* 043 */,
|
||||
217 /* 044 */, 217 /* 045 */, 218 /* 046 */, 218 /* 047 */,
|
||||
219 /* 048 */, 219 /* 049 */, 219 /* 050 */, 220 /* 051 */,
|
||||
220 /* 052 */, 221 /* 053 */, 221 /* 054 */, 222 /* 055 */,
|
||||
222 /* 056 */, 222 /* 057 */, 223 /* 058 */, 223 /* 059 */,
|
||||
224 /* 060 */, 224 /* 061 */, 224 /* 062 */, 225 /* 063 */,
|
||||
225 /* 064 */, 226 /* 065 */, 227 /* 066 */, 228 /* 067 */,
|
||||
229 /* 068 */, 230 /* 069 */, 231 /* 070 */, 231 /* 071 */,
|
||||
232 /* 072 */, 233 /* 073 */, 234 /* 074 */, 234 /* 075 */,
|
||||
235 /* 076 */, 236 /* 077 */, 236 /* 078 */, 237 /* 079 */,
|
||||
238 /* 080 */, 238 /* 081 */, 239 /* 082 */, 239 /* 083 */,
|
||||
240 /* 084 */, 241 /* 085 */, 241 /* 086 */, 242 /* 087 */,
|
||||
242 /* 088 */, 243 /* 089 */, 243 /* 090 */, 244 /* 091 */,
|
||||
244 /* 092 */, 244 /* 093 */, 245 /* 094 */, 245 /* 095 */,
|
||||
246 /* 096 */, 246 /* 097 */, 247 /* 098 */, 247 /* 099 */,
|
||||
247 /* 100 */, 248 /* 101 */, 248 /* 102 */, 249 /* 103 */,
|
||||
249 /* 104 */, 249 /* 105 */, 250 /* 106 */, 250 /* 107 */,
|
||||
250 /* 108 */, 251 /* 109 */, 251 /* 110 */, 251 /* 111 */,
|
||||
252 /* 112 */, 252 /* 113 */, 252 /* 114 */, 253 /* 115 */,
|
||||
253 /* 116 */, 253 /* 117 */, 254 /* 118 */, 254 /* 119 */,
|
||||
254 /* 120 */, 254 /* 121 */, 255 /* 122 */, 255 /* 123 */,
|
||||
255 /* 124 */, 255 /* 125 */, 255 /* 126 */, 255 /* 127 */,
|
||||
};
|
||||
|
||||
void Renderer::recompute_amp(Voice *v)
|
||||
{
|
||||
int chan = v->channel;
|
||||
int panning = v->panning;
|
||||
double vol = channel[chan].volume / 16383.f;
|
||||
double expr = channel[chan].expression / 16383.f;
|
||||
double vel = v->velocity / 127.f;
|
||||
double tempamp = calc_vol(vol * expr * vel) * v->sample->volume;
|
||||
Channel *chan = &channel[v->channel];
|
||||
int chanvol = chan->volume;
|
||||
int chanexpr = chan->expression;
|
||||
|
||||
if (panning >= 0x1DBB && panning < 0x2244)
|
||||
v->attenuation = (vol_table[(chanvol * chanexpr) / 2113407] * vol_table[v->velocity]) * ((127 + 64) / 12419775.f);
|
||||
}
|
||||
|
||||
void Renderer::compute_pan(int panning, float &left_offset, float &right_offset)
|
||||
{
|
||||
if (panning == 0)
|
||||
{
|
||||
v->panned = PANNED_CENTER;
|
||||
v->left_amp = (float)(tempamp * 0.70710678118654752440084436210485); // * sqrt(0.5)
|
||||
left_offset = 0;
|
||||
right_offset = (float)-HUGE_VAL;
|
||||
}
|
||||
else if (panning == 0)
|
||||
else if (panning == 32767)
|
||||
{
|
||||
v->panned = PANNED_LEFT;
|
||||
v->left_amp = (float)tempamp;
|
||||
}
|
||||
else if (panning == 0x3FFF)
|
||||
{
|
||||
v->panned = PANNED_RIGHT;
|
||||
v->left_amp = (float)tempamp; /* left_amp will be used */
|
||||
left_offset = (float)-HUGE_VAL;
|
||||
right_offset = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
double pan = panning / 16384.0;
|
||||
v->panned = PANNED_MYSTERY;
|
||||
v->left_amp = (float)(tempamp * sqrt(1.0 - pan));
|
||||
v->right_amp = (float)(tempamp * sqrt(pan));
|
||||
double pan = panning * (1 / 32767.0);
|
||||
right_offset = (float)(log(pan) * (1 / (log_of_2 * 32)));
|
||||
left_offset = (float)(log(1 - pan) * (1 / (log_of_2 * 32)));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -288,11 +321,13 @@ void Renderer::start_note(int chan, int note, int vel, int i)
|
|||
|
||||
if (channel[chan].panning != NO_PANNING)
|
||||
{
|
||||
voice[i].panning = channel[chan].panning;
|
||||
voice[i].left_offset = channel[chan].left_offset;
|
||||
voice[i].right_offset = channel[chan].right_offset;
|
||||
}
|
||||
else
|
||||
{
|
||||
voice[i].panning = voice[i].sample->panning;
|
||||
voice[i].left_offset = voice[i].sample->left_offset;
|
||||
voice[i].right_offset = voice[i].sample->right_offset;
|
||||
}
|
||||
|
||||
recompute_freq(i);
|
||||
|
@ -350,11 +385,7 @@ void Renderer::note_on(int chan, int note, int vel)
|
|||
{
|
||||
if (voice[i].status != VOICE_ON && voice[i].status != VOICE_DIE)
|
||||
{
|
||||
v = voice[i].left_mix;
|
||||
if ((voice[i].panned == PANNED_MYSTERY) && (voice[i].right_mix > v))
|
||||
{
|
||||
v = voice[i].right_mix;
|
||||
}
|
||||
v = voice[i].attenuation;
|
||||
if (v < lv)
|
||||
{
|
||||
lv = v;
|
||||
|
@ -482,14 +513,16 @@ void Renderer::adjust_pressure(int chan, int note, int amount)
|
|||
|
||||
void Renderer::adjust_panning(int chan)
|
||||
{
|
||||
Channel *chanp = &channel[chan];
|
||||
compute_pan(chanp->panning, chanp->left_offset, chanp->right_offset);
|
||||
int i = voices;
|
||||
while (i--)
|
||||
{
|
||||
if ((voice[i].channel == chan) &&
|
||||
(voice[i].status == VOICE_ON || voice[i].status == VOICE_SUSTAINED))
|
||||
{
|
||||
voice[i].panning = channel[chan].panning;
|
||||
recompute_amp(&voice[i]);
|
||||
voice[i].left_offset = chanp->left_offset;
|
||||
voice[i].right_offset = chanp->right_offset;
|
||||
apply_envelope_to_amp(&voice[i]);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -32,6 +32,7 @@
|
|||
#include "files.h"
|
||||
|
||||
CVAR(String, timidity_config, CONFIG_FILE, CVAR_ARCHIVE|CVAR_GLOBALCONFIG)
|
||||
CVAR(Int, timidity_voices, 32, CVAR_ARCHIVE|CVAR_GLOBALCONFIG)
|
||||
|
||||
namespace Timidity
|
||||
{
|
||||
|
@ -452,6 +453,7 @@ Renderer::Renderer(float sample_rate)
|
|||
patches = NULL;
|
||||
resample_buffer_size = 0;
|
||||
resample_buffer = NULL;
|
||||
voice = NULL;
|
||||
adjust_panning_immediately = false;
|
||||
|
||||
control_ratio = clamp(int(rate / CONTROLS_PER_SECOND), 1, MAX_CONTROL_RATIO);
|
||||
|
@ -464,7 +466,8 @@ Renderer::Renderer(float sample_rate)
|
|||
if (def_instr_name.IsNotEmpty())
|
||||
set_default_instrument(def_instr_name);
|
||||
|
||||
voices = DEFAULT_VOICES;
|
||||
voices = clamp<int>(timidity_voices, 16, 256);
|
||||
voice = new Voice[voices];
|
||||
drumchannels = DEFAULT_DRUMCHANNELS;
|
||||
}
|
||||
|
||||
|
@ -474,6 +477,10 @@ Renderer::~Renderer()
|
|||
{
|
||||
M_Free(resample_buffer);
|
||||
}
|
||||
if (voice != NULL)
|
||||
{
|
||||
delete[] voice;
|
||||
}
|
||||
}
|
||||
|
||||
void Renderer::ComputeOutput(float *buffer, int count)
|
||||
|
|
|
@ -43,10 +43,6 @@ config.h
|
|||
#define DEFAULT_DRUMCHANNELS (1<<9)
|
||||
/*#define DEFAULT_DRUMCHANNELS ((1<<9) | (1<<15))*/
|
||||
|
||||
/* Default polyphony, and maximum polyphony. */
|
||||
#define DEFAULT_VOICES 32
|
||||
#define MAX_VOICES 256
|
||||
|
||||
#define MAXCHAN 16
|
||||
#define MAXNOTE 128
|
||||
|
||||
|
@ -55,6 +51,10 @@ config.h
|
|||
of envelopes and tremolo. The cost is CPU time. */
|
||||
#define CONTROLS_PER_SECOND 1000
|
||||
|
||||
/* A scalar applied to the final mix to try and approximate the
|
||||
volume level of FMOD's built-in MIDI player. */
|
||||
#define FINAL_MIX_SCALE 0.5f
|
||||
|
||||
/* 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
|
||||
|
@ -228,6 +228,8 @@ struct Sample
|
|||
self_nonexclusive;
|
||||
BYTE
|
||||
key_group;
|
||||
float
|
||||
left_offset, right_offset;
|
||||
};
|
||||
|
||||
void convert_sample_data(Sample *sample, const void *data);
|
||||
|
@ -454,6 +456,8 @@ struct Channel
|
|||
nrpn_mode;
|
||||
float
|
||||
pitchfactor; /* precomputed pitch bend factor to save some fdiv's */
|
||||
float
|
||||
left_offset, right_offset; /* precomputed panning values */
|
||||
};
|
||||
|
||||
/* Causes the instrument's default panning to be used. */
|
||||
|
@ -478,12 +482,14 @@ struct Voice
|
|||
final_volume_t left_mix, right_mix;
|
||||
|
||||
float
|
||||
left_amp, right_amp, tremolo_volume;
|
||||
attenuation, left_offset, right_offset;
|
||||
float
|
||||
tremolo_volume;
|
||||
int
|
||||
vibrato_sample_increment[VIBRATO_SAMPLE_INCREMENTS];
|
||||
int
|
||||
vibrato_phase, vibrato_control_ratio, vibrato_control_counter,
|
||||
envelope_stage, control_counter, panning, panned;
|
||||
envelope_stage, control_counter;
|
||||
|
||||
};
|
||||
|
||||
|
@ -497,16 +503,6 @@ enum
|
|||
VOICE_DIE
|
||||
};
|
||||
|
||||
/* Voice panned options: */
|
||||
enum
|
||||
{
|
||||
PANNED_MYSTERY,
|
||||
PANNED_LEFT,
|
||||
PANNED_RIGHT,
|
||||
PANNED_CENTER
|
||||
};
|
||||
/* Anything but PANNED_MYSTERY only uses the left volume */
|
||||
|
||||
/* Envelope stages: */
|
||||
enum
|
||||
{
|
||||
|
@ -533,7 +529,8 @@ 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)))
|
||||
#define calc_vol(x) (pow(2.0,((x)*6.0 - 6.0)))
|
||||
//#define calc_vol(x) (pow(2.0,((x)*6.0 - 6.0))) // Physically ideal equation
|
||||
#define calc_gf1_amp(x) (pow(2.0,((x)*16.0 - 16.0))) // Actual GUS equation
|
||||
|
||||
/*
|
||||
timidity.h
|
||||
|
@ -555,7 +552,7 @@ struct Renderer
|
|||
int resample_buffer_size;
|
||||
sample_t *resample_buffer;
|
||||
Channel channel[16];
|
||||
Voice voice[MAX_VOICES];
|
||||
Voice *voice;
|
||||
int control_ratio, amp_with_poly;
|
||||
int drumchannels;
|
||||
int adjust_panning_immediately;
|
||||
|
@ -579,7 +576,10 @@ struct Renderer
|
|||
int convert_tremolo_rate(BYTE rate);
|
||||
int convert_vibrato_rate(BYTE rate);
|
||||
|
||||
void recompute_freq(int voice);
|
||||
void recompute_amp(Voice *v);
|
||||
void recompute_pan(Channel *chan);
|
||||
|
||||
void kill_key_group(int voice);
|
||||
float calculate_scaled_frequency(Sample *sample, int note);
|
||||
void start_note(int chan, int note, int vel, int voice);
|
||||
|
@ -599,7 +599,6 @@ struct Renderer
|
|||
void reset_midi();
|
||||
|
||||
void select_sample(int voice, Instrument *instr, int vel);
|
||||
void recompute_freq(int voice);
|
||||
|
||||
void kill_note(int voice);
|
||||
void finish_note(int voice);
|
||||
|
@ -608,6 +607,8 @@ struct Renderer
|
|||
void DataEntryFineRPN(int chan, int rpn, int val);
|
||||
void DataEntryCoarseNRPN(int chan, int nrpn, int val);
|
||||
void DataEntryFineNRPN(int chan, int nrpn, int val);
|
||||
|
||||
static void compute_pan(int panning, float &left_offset, float &right_offset);
|
||||
};
|
||||
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue