- 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:
Randy Heit 2008-04-19 05:57:09 +00:00
parent 48d8881065
commit f6866df0e6
12 changed files with 337 additions and 296 deletions

View file

@ -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

View file

@ -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;
}

View file

@ -233,7 +233,7 @@ struct musicBlock {
int OPLloadBank (FileReader &data);
private:
protected:
/* OPL channel (voice) data */
struct channelEntry {
uchar channel; /* MUS channel number */

View file

@ -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)

View file

@ -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);

View file

@ -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.";
}

View file

@ -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;
}

View file

@ -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)

View file

@ -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);
}
}
}

View file

@ -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]);
}
}

View file

@ -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)

View file

@ -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);
};
}