From f9574a98fde94b2c7e32145a142a048d351d0a2c Mon Sep 17 00:00:00 2001 From: Randy Heit Date: Wed, 13 Jan 2016 17:25:24 -0600 Subject: [PATCH 1/5] Fix Timidity's DLS instrument loading: - Envelope data needed to be converted to SF2 values. - Fine tuning was ignored which made pretty much every instrument off tune. - Despite this, it still sounds like shit compared to FMOD or Microsoft's wavetable synth. There are lots of missing notes and some instruments are still off tune. I'm not sure it's worth trying to salvage it. It'd probably be better to scrap it, since Timidity is very much oriented toward GF1 patches, which it handles perfectly fine. --- src/timidity/instrum_dls.cpp | 35 ++++++++++++++++------------------- src/timidity/mix.cpp | 29 +++++++++++++++++++++++------ src/timidity/timidity.cpp | 12 ++++++++++++ src/timidity/timidity.h | 3 +-- 4 files changed, 52 insertions(+), 27 deletions(-) diff --git a/src/timidity/instrum_dls.cpp b/src/timidity/instrum_dls.cpp index 286fcb1a3..02b8e59c0 100644 --- a/src/timidity/instrum_dls.cpp +++ b/src/timidity/instrum_dls.cpp @@ -1126,7 +1126,7 @@ static void load_region_dls(Renderer *song, Sample *sample, DLS_Instrument *ins, sample->key_group = (SBYTE)rgn->header->usKeyGroup; sample->low_freq = note_to_freq(rgn->header->RangeKey.usLow); sample->high_freq = note_to_freq(rgn->header->RangeKey.usHigh); - sample->root_freq = note_to_freq(rgn->wsmp->usUnityNote); + sample->root_freq = note_to_freq(rgn->wsmp->usUnityNote + rgn->wsmp->sFineTune * .01f); sample->low_vel = (BYTE)rgn->header->RangeVelocity.usLow; sample->high_vel = (BYTE)rgn->header->RangeVelocity.usHigh; @@ -1137,15 +1137,17 @@ static void load_region_dls(Renderer *song, Sample *sample, DLS_Instrument *ins, convert_sample_data(sample, wave->data); if (rgn->wsmp->cSampleLoops) { - sample->modes |= (PATCH_LOOPEN | PATCH_SUSTAIN); + sample->modes |= (PATCH_LOOPEN | PATCH_SUSTAIN/* | PATCH_NO_SRELEASE*/); sample->loop_start = rgn->wsmp_loop->ulStart / 2; sample->loop_end = sample->loop_start + (rgn->wsmp_loop->ulLength / 2); } + sample->scale_factor = 1024; + sample->scale_note = rgn->wsmp->usUnityNote; if (sample->modes & PATCH_SUSTAIN) { int value; - double attack, hold, decay, release; int sustain; + int attack, hold, decay, release; int sustain; CONNECTIONLIST *art = NULL; CONNECTION *artList = NULL; @@ -1157,16 +1159,11 @@ static void load_region_dls(Renderer *song, Sample *sample, DLS_Instrument *ins, artList = rgn->artList; } - value = load_connection(art->cConnections, artList, CONN_DST_EG1_ATTACKTIME); - attack = to_msec(value); - value = load_connection(art->cConnections, artList, CONN_DST_EG1_HOLDTIME); - hold = to_msec(value); - value = load_connection(art->cConnections, artList, CONN_DST_EG1_DECAYTIME); - decay = to_msec(value); - value = load_connection(art->cConnections, artList, CONN_DST_EG1_RELEASETIME); - release = to_msec(value); - value = load_connection(art->cConnections, artList, CONN_DST_EG1_SUSTAINLEVEL); - sustain = (int)((1.0 - to_normalized_percent(value)) * 250.0); + attack = load_connection(art->cConnections, artList, CONN_DST_EG1_ATTACKTIME); + hold = load_connection(art->cConnections, artList, CONN_DST_EG1_HOLDTIME); + decay = load_connection(art->cConnections, artList, CONN_DST_EG1_DECAYTIME); + release = load_connection(art->cConnections, artList, CONN_DST_EG1_RELEASETIME); + sustain = load_connection(art->cConnections, artList, CONN_DST_EG1_SUSTAINLEVEL); value = load_connection(art->cConnections, artList, CONN_DST_PAN); sample->panning = (int)((0.5 + to_normalized_percent(value)) * 16383.f); @@ -1174,12 +1171,12 @@ static void load_region_dls(Renderer *song, Sample *sample, DLS_Instrument *ins, printf("%d, Rate=%d LV=%d HV=%d Low=%d Hi=%d Root=%d Pan=%d Attack=%f Hold=%f Sustain=%d Decay=%f Release=%f\n", index, sample->sample_rate, rgn->header->RangeVelocity.usLow, rgn->header->RangeVelocity.usHigh, sample->low_freq, sample->high_freq, sample->root_freq, sample->panning, attack, hold, sustain, decay, release); */ - sample->envelope.sf2.decay_vol = 0; - sample->envelope.sf2.attack_vol = (short)attack; - sample->envelope.sf2.hold_vol = (short)hold; - sample->envelope.sf2.decay_vol = (short)decay; - sample->envelope.sf2.release_vol = (short)release; - sample->envelope.sf2.sustain_vol = (short)sustain; + sample->envelope.sf2.delay_vol = -32768; + sample->envelope.sf2.attack_vol = (short)(attack >> 16); + sample->envelope.sf2.hold_vol = (short)(hold >> 16); + sample->envelope.sf2.decay_vol = (short)(decay >> 16); + sample->envelope.sf2.release_vol = (short)(release >> 16); + sample->envelope.sf2.sustain_vol = (short)(sustain >> 16); } sample->data_length <<= FRACTION_BITS; diff --git a/src/timidity/mix.cpp b/src/timidity/mix.cpp index f97710326..beda91e16 100644 --- a/src/timidity/mix.cpp +++ b/src/timidity/mix.cpp @@ -231,6 +231,10 @@ bool SF2Envelope::Update(Voice *v) double sec; double newvolume = 0; + // NOTE! The volume scale is different for different stages of the + // envelope generator: + // Attack stage goes from 0.0 -> 1.0, multiplied directly to the output. + // The following stages go from 0 -> -1000 cB (but recorded positively) switch (stage) { case SF2_DELAY: @@ -333,6 +337,11 @@ bool SF2Envelope::Update(Voice *v) #define FLUID_ATTEN_POWER_FACTOR (-531.509) #define atten2amp(x) pow(10.0, (x) / FLUID_ATTEN_POWER_FACTOR) +static double cb_to_amp(double x) // centibels to amp +{ + return pow(10, x / -200.f); +} + void SF2Envelope::ApplyToAmp(Voice *v) { double amp; @@ -343,13 +352,21 @@ void SF2Envelope::ApplyToAmp(Voice *v) v->right_mix = 0; return; } - else if (stage == SF2_ATTACK) + + amp = v->sample->type == INST_SF2 ? atten2amp(v->attenuation) : cb_to_amp(v->attenuation); + + switch (stage) { - amp = atten2amp(v->attenuation) * volume; - } - else - { - amp = atten2amp(v->attenuation) * cb_to_amp(volume); + case SF2_ATTACK: + amp *= volume; + break; + + case SF2_HOLD: + break; + + default: + amp *= cb_to_amp(volume); + break; } amp *= FINAL_MIX_SCALE * 0.5; v->left_mix = float(amp * v->left_offset); diff --git a/src/timidity/timidity.cpp b/src/timidity/timidity.cpp index 440db9211..267edbb7e 100644 --- a/src/timidity/timidity.cpp +++ b/src/timidity/timidity.cpp @@ -678,6 +678,9 @@ int LoadDMXGUS() return 0; } +DLS_Data *LoadDLS(FILE *src); +void FreeDLS(DLS_Data *data); + Renderer::Renderer(float sample_rate, const char *args) { // 'args' should be used to load a custom config or DMXGUS, but since setup currently requires a snd_reset call, this will need some refactoring first @@ -701,6 +704,11 @@ Renderer::Renderer(float sample_rate, const char *args) voices = MAX(*midi_voices, 16); voice = new Voice[voices]; drumchannels = DEFAULT_DRUMCHANNELS; +#if 0 + FILE *f = fopen("c:\\windows\\system32\\drivers\\gm.dls", "rb"); + patches = LoadDLS(f); + fclose(f); +#endif } Renderer::~Renderer() @@ -713,6 +721,10 @@ Renderer::~Renderer() { delete[] voice; } + if (patches != NULL) + { + FreeDLS(patches); + } } void Renderer::ComputeOutput(float *buffer, int count) diff --git a/src/timidity/timidity.h b/src/timidity/timidity.h index 45e23a9f9..ac423c1e2 100644 --- a/src/timidity/timidity.h +++ b/src/timidity/timidity.h @@ -445,7 +445,7 @@ struct Channel struct MinEnvelope { - int stage; + BYTE stage; BYTE bUpdating; }; @@ -599,7 +599,6 @@ const double log_of_2 = 0.69314718055994529; #define freq_to_note(x) (log((x) / 8175.7989473096690661233836992789) * (12.0 / log_of_2)) #define calc_gf1_amp(x) (pow(2.0,((x)*16.0 - 16.0))) // Actual GUS equation -#define cb_to_amp(x) (pow(10.0, (x) * (1 / -200.0))) // centibels to amp /* timidity.h From 629eaa35a32c10ff77a046cabde88a9edd152d51 Mon Sep 17 00:00:00 2001 From: MajorCooke Date: Wed, 13 Jan 2016 20:26:15 -0600 Subject: [PATCH 2/5] - Fixed: A_Fade* could destroy live player bodies because it was calling the Destroy() function directly instead of going through P_RemoveThing. --- src/thingdef/thingdef_codeptr.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/thingdef/thingdef_codeptr.cpp b/src/thingdef/thingdef_codeptr.cpp index b6575ee19..8343b79b8 100644 --- a/src/thingdef/thingdef_codeptr.cpp +++ b/src/thingdef/thingdef_codeptr.cpp @@ -2439,7 +2439,7 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_FadeIn) if (flags & FTF_CLAMP) self->alpha = (FRACUNIT * 1); if (flags & FTF_REMOVE) - self->Destroy(); + P_RemoveThing(self); } } @@ -2467,7 +2467,7 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_FadeOut) if (flags & FTF_CLAMP) self->alpha = 0; if (flags & FTF_REMOVE) - self->Destroy(); + P_RemoveThing(self); } } @@ -2515,7 +2515,7 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_FadeTo) } if (self->alpha == target && (flags & FTF_REMOVE)) { - self->Destroy(); + P_RemoveThing(self); } } From 6a87c6cd19051ef3a3d454f2ab808feb028ba343 Mon Sep 17 00:00:00 2001 From: Braden Obrzut Date: Thu, 14 Jan 2016 00:49:57 -0500 Subject: [PATCH 3/5] - Fixed usage of uninitialized value in skybox rendering. --- src/r_plane.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/r_plane.cpp b/src/r_plane.cpp index 8cab34230..96a77930a 100644 --- a/src/r_plane.cpp +++ b/src/r_plane.cpp @@ -1280,6 +1280,7 @@ void R_DrawSkyBoxes () ds_p->sprtopclip = R_NewOpening (pl->maxx - pl->minx + 1); ds_p->maskedtexturecol = ds_p->swall = -1; ds_p->bFogBoundary = false; + ds_p->fake = 0; memcpy (openings + ds_p->sprbottomclip, floorclip + pl->minx, (pl->maxx - pl->minx + 1)*sizeof(short)); memcpy (openings + ds_p->sprtopclip, ceilingclip + pl->minx, (pl->maxx - pl->minx + 1)*sizeof(short)); From 16781e47aea55d46abe022a9ccb7134923d80351 Mon Sep 17 00:00:00 2001 From: Randy Heit Date: Thu, 14 Jan 2016 20:41:09 -0600 Subject: [PATCH 4/5] Use a 64k stream buffer for all streams, not just net streams - FLACs need a larger buffer than the default to run without starving on later versions of FMOD. --- src/sound/fmodsound.cpp | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/src/sound/fmodsound.cpp b/src/sound/fmodsound.cpp index 4042ca421..e7a617f5a 100644 --- a/src/sound/fmodsound.cpp +++ b/src/sound/fmodsound.cpp @@ -462,14 +462,12 @@ public: Stream->release(); Channel = NULL; Stream = NULL; - Owner->Sys->setStreamBufferSize(64*1024, FMOD_TIMEUNIT_RAWBYTES); // Open the stream asynchronously, so we don't hang the game while trying to reconnect. // (It would be nice to do the initial open asynchronously as well, but I'd need to rethink // the music system design to pull that off.) result = Owner->Sys->createSound(URL, (Loop ? FMOD_LOOP_NORMAL : FMOD_LOOP_OFF) | FMOD_SOFTWARE | FMOD_2D | FMOD_CREATESTREAM | FMOD_NONBLOCKING, NULL, &Stream); JustStarted = true; - Owner->Sys->setStreamBufferSize(16*1024, FMOD_TIMEUNIT_RAWBYTES); return result != FMOD_OK; } if (JustStarted && openstate == FMOD_OPENSTATE_PLAYING) @@ -1170,6 +1168,9 @@ bool FMODSoundRenderer::Init() } Sys->set3DSettings(0.5f, 96.f, 1.f); Sys->set3DRolloffCallback(RolloffCallback); + // The default is 16k, which periodically starves later FMOD versions + // when streaming FLAC files. + Sys->setStreamBufferSize(64*1024, FMOD_TIMEUNIT_RAWBYTES); snd_sfxvolume.Callback (); return true; } @@ -1733,10 +1734,6 @@ SoundStream *FMODSoundRenderer::OpenStream(const char *url, int flags) exinfo.dlsname = patches; } - // Use a larger buffer for URLs so that it's less likely to be effected - // by hiccups in the data rate from the remote server. - Sys->setStreamBufferSize(64*1024, FMOD_TIMEUNIT_RAWBYTES); - result = Sys->createSound(url, mode, &exinfo, &stream); if(result == FMOD_ERR_FORMAT && exinfo.dlsname != NULL) { @@ -1748,9 +1745,6 @@ SoundStream *FMODSoundRenderer::OpenStream(const char *url, int flags) } } - // Restore standard buffer size. - Sys->setStreamBufferSize(16*1024, FMOD_TIMEUNIT_RAWBYTES); - if(result != FMOD_OK) return NULL; From 867b7767ef78f8553fdb0afa7378601562c18d3c Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Fri, 15 Jan 2016 12:56:27 +0100 Subject: [PATCH 5/5] - ignore COMPAT_POINTONSIDE in a few places where the side effects of the old P_PointOn*Side functions are either not needed or problematic: * the sight checking code needs to be as precise as possible and should not depend on some old semi-broken routines. (This is more a precision issue of these routines - P_PointOnDivlineSide removes the lower 8 bits of each value - than having an issue with returning the wrong side in some cases.) * for slope creations it is flat out wrong to use the old routines at all. * also ignore this in the modern (box-shaped) case of FPathTraverse::AddLineIntercepts. This functionality is new to ZDoom and therefore not subject to compatibility concerns. * the line-to-line teleporter. It seems the hideous fudging code was just there to work around the design issues of these functions, so let's better not ever call them here in the first place. * A_PainShootSkull: Its usage here does not depend on these issues. * P_ExplodeMissile: New code exclusive to ZDoom. * FPolyObj::CheckMobjBlocking All occurences in p_map.cpp have been left alone although most of them probably won't need the compatibility option either. --- src/g_doom/a_painelemental.cpp | 2 +- src/p_local.h | 12 ++++++++++++ src/p_maputl.cpp | 6 +++--- src/p_mobj.cpp | 2 +- src/p_sight.cpp | 10 +++++----- src/p_slopes.cpp | 4 ++-- src/p_teleport.cpp | 2 +- src/po_man.cpp | 2 +- 8 files changed, 26 insertions(+), 14 deletions(-) diff --git a/src/g_doom/a_painelemental.cpp b/src/g_doom/a_painelemental.cpp index b52224c0c..f361fb78d 100644 --- a/src/g_doom/a_painelemental.cpp +++ b/src/g_doom/a_painelemental.cpp @@ -103,7 +103,7 @@ void A_PainShootSkull (AActor *self, angle_t angle, const PClass *spawntype, int box.Top() < ld->bbox[BOXBOTTOM] || box.Bottom() > ld->bbox[BOXTOP])) { - if (P_PointOnLineSide(self->x,self->y,ld) != P_PointOnLineSide(x,y,ld)) + if (P_PointOnLineSidePrecise(self->x,self->y,ld) != P_PointOnLineSidePrecise(x,y,ld)) return; // line blocks trajectory // ^ } } diff --git a/src/p_local.h b/src/p_local.h index fd5064f3c..e8cc5fde0 100644 --- a/src/p_local.h +++ b/src/p_local.h @@ -249,6 +249,12 @@ inline int P_PointOnLineSide (fixed_t x, fixed_t y, const line_t *line) : DMulScale32 (y-line->v1->y, line->dx, line->v1->x-x, line->dy) > 0; } +inline int P_PointOnLineSidePrecise (fixed_t x, fixed_t y, const line_t *line) +{ + return DMulScale32 (y-line->v1->y, line->dx, line->v1->x-x, line->dy) > 0; +} + + //========================================================================== // // P_PointOnDivlineSide @@ -267,6 +273,12 @@ inline int P_PointOnDivlineSide (fixed_t x, fixed_t y, const divline_t *line) : (DMulScale32 (y-line->y, line->dx, line->x-x, line->dy) > 0); } +inline int P_PointOnDivlineSidePrecise (fixed_t x, fixed_t y, const divline_t *line) +{ + return DMulScale32 (y-line->y, line->dx, line->x-x, line->dy) > 0; +} + + //========================================================================== // // P_MakeDivline diff --git a/src/p_maputl.cpp b/src/p_maputl.cpp index e6d5a99e6..1d806d044 100644 --- a/src/p_maputl.cpp +++ b/src/p_maputl.cpp @@ -1056,13 +1056,13 @@ void FPathTraverse::AddThingIntercepts (int bx, int by, FBlockThingsIterator &it break; } // Check if this side is facing the trace origin - if (P_PointOnDivlineSide (trace.x, trace.y, &line) == 0) + if (P_PointOnDivlineSidePrecise (trace.x, trace.y, &line) == 0) { numfronts++; // If it is, see if the trace crosses it - if (P_PointOnDivlineSide (line.x, line.y, &trace) != - P_PointOnDivlineSide (line.x + line.dx, line.y + line.dy, &trace)) + if (P_PointOnDivlineSidePrecise (line.x, line.y, &trace) != + P_PointOnDivlineSidePrecise (line.x + line.dx, line.y + line.dy, &trace)) { // It's a hit fixed_t frac = P_InterceptVector (&trace, &line); diff --git a/src/p_mobj.cpp b/src/p_mobj.cpp index 750cc18fb..bcaa77855 100644 --- a/src/p_mobj.cpp +++ b/src/p_mobj.cpp @@ -1284,7 +1284,7 @@ void P_ExplodeMissile (AActor *mo, line_t *line, AActor *target) if (line != NULL && cl_missiledecals) { - int side = P_PointOnLineSide (mo->x, mo->y, line); + int side = P_PointOnLineSidePrecise (mo->x, mo->y, line); if (line->sidedef[side] == NULL) side ^= 1; if (line->sidedef[side] != NULL) diff --git a/src/p_sight.cpp b/src/p_sight.cpp index 135698b8b..2d465aab5 100644 --- a/src/p_sight.cpp +++ b/src/p_sight.cpp @@ -132,7 +132,7 @@ bool SightCheck::PTR_SightTraverse (intercept_t *in) { int frontflag; - frontflag = P_PointOnLineSide(sightthing->x, sightthing->y, li); + frontflag = P_PointOnLineSidePrecise(sightthing->x, sightthing->y, li); //Check 3D FLOORS! for(int i=1;i<=2;i++) @@ -241,14 +241,14 @@ bool SightCheck::P_SightCheckLine (line_t *ld) return true; } ld->validcount = validcount; - if (P_PointOnDivlineSide (ld->v1->x, ld->v1->y, &trace) == - P_PointOnDivlineSide (ld->v2->x, ld->v2->y, &trace)) + if (P_PointOnDivlineSidePrecise (ld->v1->x, ld->v1->y, &trace) == + P_PointOnDivlineSidePrecise (ld->v2->x, ld->v2->y, &trace)) { return true; // line isn't crossed } P_MakeDivline (ld, &dl); - if (P_PointOnDivlineSide (trace.x, trace.y, &dl) == - P_PointOnDivlineSide (trace.x+trace.dx, trace.y+trace.dy, &dl)) + if (P_PointOnDivlineSidePrecise (trace.x, trace.y, &dl) == + P_PointOnDivlineSidePrecise (trace.x+trace.dx, trace.y+trace.dy, &dl)) { return true; // line isn't crossed } diff --git a/src/p_slopes.cpp b/src/p_slopes.cpp index c0b9b5ef0..a3db26526 100644 --- a/src/p_slopes.cpp +++ b/src/p_slopes.cpp @@ -54,7 +54,7 @@ static void P_SlopeLineToPoint (int lineid, fixed_t x, fixed_t y, fixed_t z, boo sector_t *sec; secplane_t *plane; - if (P_PointOnLineSide (x, y, line) == 0) + if (P_PointOnLineSidePrecise (x, y, line) == 0) { sec = line->frontsector; } @@ -363,7 +363,7 @@ static void P_SetSlopesFromVertexHeights(FMapThing *firstmt, FMapThing *lastmt, z3 = h3? *h3 : j==0? sec->GetPlaneTexZ(sector_t::floor) : sec->GetPlaneTexZ(sector_t::ceiling); vt3.Z = FIXED2FLOAT(z3); - if (P_PointOnLineSide(vertexes[vi3].x, vertexes[vi3].y, sec->lines[0]) == 0) + if (P_PointOnLineSidePrecise(vertexes[vi3].x, vertexes[vi3].y, sec->lines[0]) == 0) { vec1 = vt2 - vt3; vec2 = vt1 - vt3; diff --git a/src/p_teleport.cpp b/src/p_teleport.cpp index 040aca9f4..380f5572d 100644 --- a/src/p_teleport.cpp +++ b/src/p_teleport.cpp @@ -530,7 +530,7 @@ bool EV_SilentLineTeleport (line_t *line, int side, AActor *thing, int id, INTBO int fudge = FUDGEFACTOR; // Make sure we are on correct side of exit linedef. - while (P_PointOnLineSide(x, y, l) != side && --fudge >= 0) + while (P_PointOnLineSidePrecise(x, y, l) != side && --fudge >= 0) { if (abs(l->dx) > abs(l->dy)) y -= (l->dx < 0) != side ? -1 : 1; diff --git a/src/po_man.cpp b/src/po_man.cpp index 727591bd5..98fbbcbcb 100644 --- a/src/po_man.cpp +++ b/src/po_man.cpp @@ -1231,7 +1231,7 @@ bool FPolyObj::CheckMobjBlocking (side_t *sd) // Best use the one facing the player and ignore the back side. if (ld->sidedef[1] != NULL) { - int side = P_PointOnLineSide(mobj->x, mobj->y, ld); + int side = P_PointOnLineSidePrecise(mobj->x, mobj->y, ld); if (ld->sidedef[side] != sd) { continue;