From f6866df0e642b150c82a05a27ce6ce945667b0df Mon Sep 17 00:00:00 2001 From: Randy Heit Date: Sat, 19 Apr 2008 05:57:09 +0000 Subject: [PATCH] - 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) --- docs/rh-log.txt | 11 + src/oplsynth/music_opl_mididevice.cpp | 38 ++- src/oplsynth/muslib.h | 2 +- src/oplsynth/opl_mus_player.cpp | 36 +-- src/sound/i_musicinterns.h | 6 +- src/sound/music_midistream.cpp | 36 +++ src/sound/music_timidity_mididevice.cpp | 61 +++++ src/timidity/instrum.cpp | 1 + src/timidity/mix.cpp | 299 ++++++++---------------- src/timidity/playmidi.cpp | 95 +++++--- src/timidity/timidity.cpp | 9 +- src/timidity/timidity.h | 39 ++-- 12 files changed, 337 insertions(+), 296 deletions(-) diff --git a/docs/rh-log.txt b/docs/rh-log.txt index 519868683..0ff748c66 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 0273af663..d08c97a1d 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 ad9bbf6f5..cf642ad73 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 2e0c5e90f..b71d8230f 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 c1f04c385..0d7f8c665 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 98b36a5ef..0e025efe1 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 b138abdf9..a3c03c19e 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 247ce646a..67d2decb0 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 34db6b40e..8abc2c0fc 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 9f261a9e7..dcda31a5a 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 24c4725d2..2927fc74e 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 2fbfb6848..0e8837199 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); }; }