diff --git a/docs/rh-log.txt b/docs/rh-log.txt index 5198686835..0ff748c667 100644 --- a/docs/rh-log.txt +++ b/docs/rh-log.txt @@ -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 diff --git a/src/oplsynth/music_opl_mididevice.cpp b/src/oplsynth/music_opl_mididevice.cpp index 0273af6635..d08c97a1d4 100644 --- a/src/oplsynth/music_opl_mididevice.cpp +++ b/src/oplsynth/music_opl_mididevice.cpp @@ -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; +} diff --git a/src/oplsynth/muslib.h b/src/oplsynth/muslib.h index ad9bbf6f50..cf642ad73a 100644 --- a/src/oplsynth/muslib.h +++ b/src/oplsynth/muslib.h @@ -233,7 +233,7 @@ struct musicBlock { int OPLloadBank (FileReader &data); -private: +protected: /* OPL channel (voice) data */ struct channelEntry { uchar channel; /* MUS channel number */ diff --git a/src/oplsynth/opl_mus_player.cpp b/src/oplsynth/opl_mus_player.cpp index 2e0c5e90f3..b71d8230f9 100644 --- a/src/oplsynth/opl_mus_player.cpp +++ b/src/oplsynth/opl_mus_player.cpp @@ -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) diff --git a/src/sound/i_musicinterns.h b/src/sound/i_musicinterns.h index c1f04c3851..0d7f8c665e 100644 --- a/src/sound/i_musicinterns.h +++ b/src/sound/i_musicinterns.h @@ -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); diff --git a/src/sound/music_midistream.cpp b/src/sound/music_midistream.cpp index 98b36a5efc..0e025efe11 100644 --- a/src/sound/music_midistream.cpp +++ b/src/sound/music_midistream.cpp @@ -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."; +} diff --git a/src/sound/music_timidity_mididevice.cpp b/src/sound/music_timidity_mididevice.cpp index b138abdf91..a3c03c19e7 100644 --- a/src/sound/music_timidity_mididevice.cpp +++ b/src/sound/music_timidity_mididevice.cpp @@ -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; +} diff --git a/src/timidity/instrum.cpp b/src/timidity/instrum.cpp index 247ce646af..67d2decb0d 100644 --- a/src/timidity/instrum.cpp +++ b/src/timidity/instrum.cpp @@ -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) diff --git a/src/timidity/mix.cpp b/src/timidity/mix.cpp index 34db6b40ea..8abc2c0fc6 100644 --- a/src/timidity/mix.cpp +++ b/src/timidity/mix.cpp @@ -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); } } } diff --git a/src/timidity/playmidi.cpp b/src/timidity/playmidi.cpp index 9f261a9e72..dcda31a5a6 100644 --- a/src/timidity/playmidi.cpp +++ b/src/timidity/playmidi.cpp @@ -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]); } } diff --git a/src/timidity/timidity.cpp b/src/timidity/timidity.cpp index 24c4725d2c..2927fc74ea 100644 --- a/src/timidity/timidity.cpp +++ b/src/timidity/timidity.cpp @@ -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(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) diff --git a/src/timidity/timidity.h b/src/timidity/timidity.h index 2fbfb6848b..0e8837199a 100644 --- a/src/timidity/timidity.h +++ b/src/timidity/timidity.h @@ -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); }; }