From 490fd8f3a017bf1ecf19c001f4dfffce1295c601 Mon Sep 17 00:00:00 2001 From: "alexey.lysiuk" Date: Sat, 15 Apr 2017 12:33:26 +0300 Subject: [PATCH 01/29] Fixed applying of multiple damage factors https://mantis.zdoom.org/view.php?id=586 --- src/info.cpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/info.cpp b/src/info.cpp index 4619b7585..94a4c4fb5 100644 --- a/src/info.cpp +++ b/src/info.cpp @@ -513,8 +513,11 @@ void PClassActor::SetDamageFactor(FName type, double factor) { for (auto & p : ActorInfo()->DamageFactors) { - if (p.first == type) p.second = factor; - return; + if (p.first == type) + { + p.second = factor; + return; + } } ActorInfo()->DamageFactors.Push({ type, factor }); } From e333e314100eba432f1208403406daea8aec408b Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Sat, 15 Apr 2017 12:01:44 +0200 Subject: [PATCH 02/29] - fixed: Vector array elements failed to allocate the proper amount of registers. --- src/scripting/backend/codegen.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/scripting/backend/codegen.cpp b/src/scripting/backend/codegen.cpp index 737bb3c31..35582aa42 100644 --- a/src/scripting/backend/codegen.cpp +++ b/src/scripting/backend/codegen.cpp @@ -7445,7 +7445,7 @@ ExpEmit FxArrayElement::Emit(VMFunctionBuilder *build) else { start.Free(build); - ExpEmit dest(build, ValueType->GetRegType()); + ExpEmit dest(build, ValueType->GetRegType(), ValueType->GetRegCount()); // added 1 to use the *_R version that takes the offset from a register build->Emit(arraytype->ElementType->GetLoadOp() + 1, dest.RegNum, start.RegNum, indexwork.RegNum); return dest; From 069e4e9b0b4d160f8eba2f934b6e3803f9db4202 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Sat, 15 Apr 2017 12:02:33 +0200 Subject: [PATCH 03/29] - allow tag == 0 for backside activation on a few more actions. --- src/p_ceiling.cpp | 4 ++-- src/p_floor.cpp | 8 ++++---- src/p_lnspec.cpp | 6 +++--- src/p_spec.h | 7 +++---- 4 files changed, 12 insertions(+), 13 deletions(-) diff --git a/src/p_ceiling.cpp b/src/p_ceiling.cpp index 40b0094ab..b45ded588 100644 --- a/src/p_ceiling.cpp +++ b/src/p_ceiling.cpp @@ -585,9 +585,9 @@ bool EV_CeilingCrushStop (int tag, bool remove) return rtn; } -bool EV_StopCeiling(int tag) +bool EV_StopCeiling(int tag, line_t *line) { - FSectorTagIterator it(tag); + FSectorTagIterator it(tag, line); while (int sec = it.Next()) { if (level.sectors[sec].ceilingdata) diff --git a/src/p_floor.cpp b/src/p_floor.cpp index 580726155..d370cdccf 100644 --- a/src/p_floor.cpp +++ b/src/p_floor.cpp @@ -543,10 +543,10 @@ bool EV_DoFloor (DFloor::EFloor floortype, line_t *line, int tag, // //========================================================================== -bool EV_FloorCrushStop (int tag) +bool EV_FloorCrushStop (int tag, line_t *line) { int secnum; - FSectorTagIterator it(tag); + FSectorTagIterator it(tag, line); while ((secnum = it.Next()) >= 0) { sector_t *sec = &level.sectors[secnum]; @@ -563,9 +563,9 @@ bool EV_FloorCrushStop (int tag) } // same as above but stops any floor mover that was active on the given sector. -bool EV_StopFloor(int tag) +bool EV_StopFloor(int tag, line_t *line) { - FSectorTagIterator it(tag); + FSectorTagIterator it(tag, line); while (int sec = it.Next()) { if (level.sectors[sec].floordata) diff --git a/src/p_lnspec.cpp b/src/p_lnspec.cpp index 12a29dd3b..01f4f85ff 100644 --- a/src/p_lnspec.cpp +++ b/src/p_lnspec.cpp @@ -417,7 +417,7 @@ FUNC(LS_Floor_LowerByValueTimes8) FUNC(LS_Floor_CrushStop) // Floor_CrushStop (tag) { - return EV_FloorCrushStop (arg0); + return EV_FloorCrushStop (arg0, ln); } FUNC(LS_Floor_LowerInstant) @@ -571,7 +571,7 @@ FUNC(LS_Generic_Floor) FUNC(LS_Floor_Stop) // Floor_Stop (tag) { - return EV_StopFloor(arg0); + return EV_StopFloor(arg0, ln); } @@ -882,7 +882,7 @@ FUNC(LS_Ceiling_LowerByTexture) FUNC(LS_Ceiling_Stop) // Ceiling_Stop (tag) { - return EV_StopCeiling(arg0); + return EV_StopCeiling(arg0, ln); } diff --git a/src/p_spec.h b/src/p_spec.h index 5c8130a53..cd3107c4f 100644 --- a/src/p_spec.h +++ b/src/p_spec.h @@ -452,7 +452,7 @@ bool P_CreateCeiling(sector_t *sec, DCeiling::ECeiling type, line_t *line, int t bool EV_DoCeiling (DCeiling::ECeiling type, line_t *line, int tag, double speed, double speed2, double height, int crush, int silent, int change, DCeiling::ECrushMode hexencrush = DCeiling::ECrushMode::crushDoom); bool EV_CeilingCrushStop (int tag, bool remove); -bool EV_StopCeiling(int tag); +bool EV_StopCeiling(int tag, line_t *line); void P_ActivateInStasisCeiling (int tag); @@ -550,7 +550,6 @@ public: int usespecials); friend bool EV_DoFloor (DFloor::EFloor floortype, line_t *line, int tag, double speed, double height, int crush, int change, bool hexencrush, bool hereticlower); - friend bool EV_FloorCrushStop (int tag); friend bool EV_DoDonut (int tag, line_t *line, double pillarspeed, double slimespeed); private: DFloor (); @@ -565,8 +564,8 @@ bool EV_BuildStairs (int tag, DFloor::EStair type, line_t *line, bool EV_DoFloor(DFloor::EFloor floortype, line_t *line, int tag, double speed, double height, int crush, int change, bool hexencrush, bool hereticlower = false); -bool EV_FloorCrushStop (int tag); -bool EV_StopFloor(int tag); +bool EV_FloorCrushStop (int tag, line_t *line); +bool EV_StopFloor(int tag, line_t *line); bool EV_DoDonut (int tag, line_t *line, double pillarspeed, double slimespeed); class DElevator : public DMover From 2d0da4fcfa4aafe80140818c0e90071c41eb5e9b Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Sat, 15 Apr 2017 13:20:58 +0200 Subject: [PATCH 04/29] - fixed: Dynamic arrays of objects in structs were not registered for garbage collection. --- src/scripting/types.cpp | 24 ++++++++++++++++++++++-- src/scripting/types.h | 5 +++-- 2 files changed, 25 insertions(+), 4 deletions(-) diff --git a/src/scripting/types.cpp b/src/scripting/types.cpp index 5a8581812..82263dfcd 100644 --- a/src/scripting/types.cpp +++ b/src/scripting/types.cpp @@ -183,7 +183,7 @@ void PType::SetPointer(void *base, unsigned offset, TArray *stroffs) { } -void PType::SetPointerArray(void *base, unsigned offset, TArray *stroffs) const +void PType::SetPointerArray(void *base, unsigned offset, TArray *stroffs) { } @@ -1951,7 +1951,7 @@ void PDynArray::SetDefaultValue(void *base, unsigned offset, TArray *special) const +void PDynArray::SetPointerArray(void *base, unsigned offset, TArray *special) { if (ElementType->isObjectPointer()) { @@ -2185,6 +2185,26 @@ void PStruct::SetPointer(void *base, unsigned offset, TArray *special) } } +//========================================================================== +// +// PStruct :: SetPointerArray +// +//========================================================================== + +void PStruct::SetPointerArray(void *base, unsigned offset, TArray *special) +{ + auto it = Symbols.GetIterator(); + PSymbolTable::MapType::Pair *pair; + while (it.NextPair(pair)) + { + auto field = dyn_cast(pair->Value); + if (field && !(field->Flags & VARF_Transient)) + { + field->Type->SetPointerArray(base, unsigned(offset + field->Offset), special); + } + } +} + //========================================================================== // // PStruct :: WriteValue diff --git a/src/scripting/types.h b/src/scripting/types.h index d4dc196f7..5c55db6c1 100644 --- a/src/scripting/types.h +++ b/src/scripting/types.h @@ -125,7 +125,7 @@ public: // object is destroyed. virtual void SetDefaultValue(void *base, unsigned offset, TArray *special=NULL); virtual void SetPointer(void *base, unsigned offset, TArray *ptrofs = NULL); - virtual void SetPointerArray(void *base, unsigned offset, TArray *ptrofs = NULL) const; + virtual void SetPointerArray(void *base, unsigned offset, TArray *ptrofs = NULL); // Initialize the value, if needed (e.g. strings) virtual void InitializeValue(void *addr, const void *def) const; @@ -512,7 +512,7 @@ public: void SetDefaultValue(void *base, unsigned offset, TArray *specials) override; void InitializeValue(void *addr, const void *def) const override; void DestroyValue(void *addr) const override; - void SetPointerArray(void *base, unsigned offset, TArray *ptrofs = NULL) const override; + void SetPointerArray(void *base, unsigned offset, TArray *ptrofs = NULL) override; }; class PMap : public PCompoundType @@ -544,6 +544,7 @@ public: bool ReadValue(FSerializer &ar, const char *key,void *addr) const override; void SetDefaultValue(void *base, unsigned offset, TArray *specials) override; void SetPointer(void *base, unsigned offset, TArray *specials) override; + void SetPointerArray(void *base, unsigned offset, TArray *special) override; }; class PPrototype : public PCompoundType From 046e250f2e32399450ec74d43518384a455d65e1 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Sat, 15 Apr 2017 14:54:43 +0200 Subject: [PATCH 05/29] - fixed: Ceiling_CrushStop checked the wrong argument for the remove parameter. --- src/p_lnspec.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/p_lnspec.cpp b/src/p_lnspec.cpp index 01f4f85ff..3a5d0d94b 100644 --- a/src/p_lnspec.cpp +++ b/src/p_lnspec.cpp @@ -717,7 +717,7 @@ FUNC(LS_Ceiling_CrushStop) // Ceiling_CrushStop (tag, remove) { bool remove; - switch (arg3) + switch (arg1) { case 1: remove = false; From 1ee925684226663dda43fa6d1a3b9bf9cacee676 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Sat, 15 Apr 2017 16:41:00 +0200 Subject: [PATCH 06/29] - did a complete workover of the weapon sprite translucency code that got inherited from QZDoom. This was very poorly done without ever addressing the issues a composite render style can bring, it merely dealt with the known legacy render styles. The same, identical code was also present in two different places. The oversight that AlterWeaponSprite overrode even forced styles was also fixed. OpenGL is not implemented yet but with the problems eliminated should be doable now. --- src/p_pspr.cpp | 80 ++++++++++++- src/p_pspr.h | 5 +- src/polyrenderer/scene/poly_playersprite.cpp | 115 ++----------------- src/r_data/renderstyle.h | 3 - src/swrenderer/things/r_playersprite.cpp | 114 ++---------------- src/swrenderer/viewport/r_spritedrawer.cpp | 6 +- wadsrc/static/zscript/shared/player.txt | 2 +- 7 files changed, 96 insertions(+), 229 deletions(-) diff --git a/src/p_pspr.cpp b/src/p_pspr.cpp index cb89305d8..73afa0067 100644 --- a/src/p_pspr.cpp +++ b/src/p_pspr.cpp @@ -122,7 +122,6 @@ DEFINE_FIELD(DPSprite, oldy) DEFINE_FIELD(DPSprite, firstTic) DEFINE_FIELD(DPSprite, Tics) DEFINE_FIELD(DPSprite, alpha) -DEFINE_FIELD(DPSprite, RenderStyle) DEFINE_FIELD_BIT(DPSprite, Flags, bAddWeapon, PSPF_ADDWEAPON) DEFINE_FIELD_BIT(DPSprite, Flags, bAddBob, PSPF_ADDBOB) DEFINE_FIELD_BIT(DPSprite, Flags, bPowDouble, PSPF_POWDOUBLE) @@ -147,7 +146,7 @@ DPSprite::DPSprite(player_t *owner, AActor *caller, int id) processPending(true) { alpha = 1; - RenderStyle = STYLE_Normal; + Renderstyle = STYLE_Normal; DPSprite *prev = nullptr; DPSprite *next = Owner->psprites; @@ -298,6 +297,79 @@ DEFINE_ACTION_FUNCTION(_PlayerInfo, GetPSprite) // the underscore is needed to g } + +std::pair DPSprite::GetRenderStyle(FRenderStyle ownerstyle, double owneralpha) +{ + FRenderStyle returnstyle, mystyle; + double returnalpha; + + ownerstyle.CheckFuzz(); + mystyle = Renderstyle; + mystyle.CheckFuzz(); + if (Flags & PSPF_RENDERSTYLE) + { + ownerstyle.CheckFuzz(); + if (Flags & PSPF_FORCESTYLE) + { + returnstyle = mystyle; + } + else if (ownerstyle.BlendOp != STYLEOP_Add) + { + // all styles that do more than simple blending need to be fully preserved. + returnstyle = ownerstyle; + } + else + { + returnstyle = mystyle; + if (ownerstyle.DestAlpha == STYLEALPHA_One) + { + // If the owner is additive and the overlay translucent, force additive result. + returnstyle.DestAlpha = STYLEALPHA_One; + } + if (ownerstyle.Flags & (STYLEF_ColorIsFixed|STYLEF_RedIsAlpha)) + { + // If the owner's style is a stencil type, this must be preserved. + returnstyle.Flags = ownerstyle.Flags; + } + } + } + else + { + returnstyle = ownerstyle; + } + + if ((Flags & PSPF_FORCEALPHA) && returnstyle.BlendOp != STYLEOP_Fuzz && returnstyle.BlendOp != STYLEOP_Shadow) + { + // ALWAYS take priority if asked for, except fuzz. Fuzz does absolutely nothing + // no matter what way it's changed. + returnalpha = alpha; + returnstyle.Flags &= ~(STYLEF_Alpha1 | STYLEF_TransSoulsAlpha); + } + else if (Flags & PSPF_ALPHA) + { + // Set the alpha based on if using the overlay's own or not. Also adjust + // and override the alpha if not forced. + if (returnstyle.BlendOp != STYLEOP_Add) + { + returnalpha = owneralpha; + } + else + { + returnalpha = owneralpha * alpha; + } + } + + // Should normal renderstyle come out on top at the end and we desire alpha, + // switch it to translucent. Normal never applies any sort of alpha. + if (returnstyle.BlendOp == STYLEOP_Add && returnstyle.SrcAlpha == STYLEALPHA_One && returnstyle.DestAlpha == STYLEALPHA_Zero && returnalpha < 1.) + { + returnstyle = LegacyRenderStyles[STYLE_Translucent]; + returnalpha = owneralpha * alpha; + } + + return{ returnstyle, clamp(float(alpha), 0.f, 1.f) }; +} + //--------------------------------------------------------------------------- // // PROC P_NewPspriteTick @@ -1237,7 +1309,7 @@ DEFINE_ACTION_FUNCTION(AActor, A_OverlayRenderStyle) if (pspr == nullptr || style >= STYLE_Count || style < 0) return 0; - pspr->RenderStyle = style; + pspr->Renderstyle = ERenderStyle(style); } return 0; } @@ -1472,7 +1544,7 @@ void DPSprite::Serialize(FSerializer &arc) ("oldx", oldx) ("oldy", oldy) ("alpha", alpha) - ("renderstyle", RenderStyle); + ("renderstyle_", Renderstyle); // The underscore is intentional to avoid problems with old savegames which had this as an ERenderStyle (which is not future proof.) } //------------------------------------------------------------------------ diff --git a/src/p_pspr.h b/src/p_pspr.h index 08d78429a..f91c4b30f 100644 --- a/src/p_pspr.h +++ b/src/p_pspr.h @@ -23,6 +23,8 @@ #ifndef __P_PSPR_H__ #define __P_PSPR_H__ +#include "r_data/renderstyle.h" + // Basic data types. // Needs fixed point, and BAM angles. @@ -81,13 +83,14 @@ public: void SetCaller(AActor *newcaller) { Caller = newcaller; } void ResetInterpolation() { oldx = x; oldy = y; } void OnDestroy() override; + std::pair GetRenderStyle(FRenderStyle ownerstyle, double owneralpha); double x, y, alpha; double oldx, oldy; bool firstTic; int Tics; int Flags; - int RenderStyle; + FRenderStyle Renderstyle; private: DPSprite () {} diff --git a/src/polyrenderer/scene/poly_playersprite.cpp b/src/polyrenderer/scene/poly_playersprite.cpp index eb1ff323e..ac8af89ce 100644 --- a/src/polyrenderer/scene/poly_playersprite.cpp +++ b/src/polyrenderer/scene/poly_playersprite.cpp @@ -217,7 +217,7 @@ void RenderPolyPlayerSprites::RenderSprite(DPSprite *pspr, AActor *owner, float flip = sprframe->Flip & 1; tex = TexMan(picnum); - if (tex->UseType == FTexture::TEX_Null || pspr->RenderStyle == STYLE_None) + if (tex->UseType == FTexture::TEX_Null) return; if (pspr->firstTic) @@ -319,112 +319,11 @@ void RenderPolyPlayerSprites::RenderSprite(DPSprite *pspr, AActor *owner, float FDynamicColormap *colormap_to_use = nullptr; if (pspr->GetID() < PSP_TARGETCENTER) { - // [MC] Set the render style + auto rs = pspr->GetRenderStyle(owner->RenderStyle, owner->Alpha); + vis.RenderStyle = rs.first; + vis.Alpha = rs.second; - if (pspr->Flags & PSPF_RENDERSTYLE) - { - const int rs = clamp(pspr->RenderStyle, 0, STYLE_Count); - - if (pspr->Flags & PSPF_FORCESTYLE) - { - vis.RenderStyle = LegacyRenderStyles[rs]; - } - else if (owner->RenderStyle == LegacyRenderStyles[STYLE_Fuzzy]) - { - vis.RenderStyle = LegacyRenderStyles[STYLE_Fuzzy]; - } - else if (owner->RenderStyle == LegacyRenderStyles[STYLE_OptFuzzy]) - { - vis.RenderStyle = LegacyRenderStyles[STYLE_OptFuzzy]; - vis.RenderStyle.CheckFuzz(); - } - else if (owner->RenderStyle == LegacyRenderStyles[STYLE_Subtract]) - { - vis.RenderStyle = LegacyRenderStyles[STYLE_Subtract]; - } - else - { - vis.RenderStyle = LegacyRenderStyles[rs]; - } - } - else - { - vis.RenderStyle = owner->RenderStyle; - } - - // Set the alpha based on if using the overlay's own or not. Also adjust - // and override the alpha if not forced. - if (pspr->Flags & PSPF_ALPHA) - { - if (vis.RenderStyle == LegacyRenderStyles[STYLE_Fuzzy]) - { - alpha = owner->Alpha; - } - else if (vis.RenderStyle == LegacyRenderStyles[STYLE_OptFuzzy]) - { - FRenderStyle style = vis.RenderStyle; - style.CheckFuzz(); - switch (style.BlendOp) - { - default: - alpha = pspr->alpha * owner->Alpha; - break; - case STYLEOP_Fuzz: - case STYLEOP_Sub: - alpha = owner->Alpha; - break; - } - - } - else if (vis.RenderStyle == LegacyRenderStyles[STYLE_Subtract]) - { - alpha = owner->Alpha; - } - else if (vis.RenderStyle == LegacyRenderStyles[STYLE_Add] || - vis.RenderStyle == LegacyRenderStyles[STYLE_Translucent] || - vis.RenderStyle == LegacyRenderStyles[STYLE_TranslucentStencil] || - vis.RenderStyle == LegacyRenderStyles[STYLE_AddStencil] || - vis.RenderStyle == LegacyRenderStyles[STYLE_AddShaded]) - { - alpha = owner->Alpha * pspr->alpha; - } - else - { - alpha = owner->Alpha; - } - } - - // Should normal renderstyle come out on top at the end and we desire alpha, - // switch it to translucent. Normal never applies any sort of alpha. - if ((pspr->Flags & PSPF_ALPHA) && - vis.RenderStyle == LegacyRenderStyles[STYLE_Normal] && - vis.Alpha < 1.0) - { - vis.RenderStyle = LegacyRenderStyles[STYLE_Translucent]; - alpha = owner->Alpha * pspr->alpha; - } - - // ALWAYS take priority if asked for, except fuzz. Fuzz does absolutely nothing - // no matter what way it's changed. - if (pspr->Flags & PSPF_FORCEALPHA) - { - //Due to lack of != operators... - if (vis.RenderStyle == LegacyRenderStyles[STYLE_Fuzzy] || - vis.RenderStyle == LegacyRenderStyles[STYLE_SoulTrans] || - vis.RenderStyle == LegacyRenderStyles[STYLE_Stencil]) - { - } - else - { - alpha = pspr->alpha; - vis.RenderStyle.Flags |= STYLEF_ForceAlpha; - } - } - vis.Alpha = clamp(float(alpha), 0.f, 1.f); - - // Due to how some of the effects are handled, going to 0 or less causes some - // weirdness to display. There's no point rendering it anyway if it's 0. - if (vis.Alpha <= 0.) + if (!vis.RenderStyle.IsVisible(vis.Alpha)) return; //----------------------------------------------------------------------------- @@ -455,9 +354,9 @@ void RenderPolyPlayerSprites::RenderSprite(DPSprite *pspr, AActor *owner, float viewpoint.camera->Inventory->AlterWeaponSprite(&visstyle); - vis.Alpha = visstyle.Alpha; + if (!(pspr->Flags & PSPF_FORCEALPHA)) vis.Alpha = visstyle.Alpha; - if (visstyle.RenderStyle != STYLE_Count) + if (visstyle.RenderStyle != STYLE_Count && !(pspr->Flags & PSPF_FORCESTYLE)) { vis.RenderStyle = visstyle.RenderStyle; } diff --git a/src/r_data/renderstyle.h b/src/r_data/renderstyle.h index ebc1bd53f..b1fd98a5c 100644 --- a/src/r_data/renderstyle.h +++ b/src/r_data/renderstyle.h @@ -119,9 +119,6 @@ enum ERenderFlags // Actors only: Ignore sector fade and fade to black. To fade to white, // combine this with STYLEF_InvertOverlay. STYLEF_FadeToBlack = 64, - - // Force alpha. - STYLEF_ForceAlpha = 128, }; union FRenderStyle diff --git a/src/swrenderer/things/r_playersprite.cpp b/src/swrenderer/things/r_playersprite.cpp index c56b95980..cd8fc62d3 100644 --- a/src/swrenderer/things/r_playersprite.cpp +++ b/src/swrenderer/things/r_playersprite.cpp @@ -217,7 +217,7 @@ namespace swrenderer flip = sprframe->Flip & 1; tex = TexMan(picnum); - if (tex->UseType == FTexture::TEX_Null || pspr->RenderStyle == STYLE_None) + if (tex->UseType == FTexture::TEX_Null) return; if (pspr->firstTic) @@ -320,111 +320,11 @@ namespace swrenderer if (pspr->GetID() < PSP_TARGETCENTER) { // [MC] Set the render style + auto rs = pspr->GetRenderStyle(owner->RenderStyle, owner->Alpha); + vis.RenderStyle = rs.first; + vis.Alpha = rs.second; - if (pspr->Flags & PSPF_RENDERSTYLE) - { - const int rs = clamp(pspr->RenderStyle, 0, STYLE_Count); - - if (pspr->Flags & PSPF_FORCESTYLE) - { - vis.RenderStyle = LegacyRenderStyles[rs]; - } - else if (owner->RenderStyle == LegacyRenderStyles[STYLE_Fuzzy]) - { - vis.RenderStyle = LegacyRenderStyles[STYLE_Fuzzy]; - } - else if (owner->RenderStyle == LegacyRenderStyles[STYLE_OptFuzzy]) - { - vis.RenderStyle = LegacyRenderStyles[STYLE_OptFuzzy]; - vis.RenderStyle.CheckFuzz(); - } - else if (owner->RenderStyle == LegacyRenderStyles[STYLE_Subtract]) - { - vis.RenderStyle = LegacyRenderStyles[STYLE_Subtract]; - } - else - { - vis.RenderStyle = LegacyRenderStyles[rs]; - } - } - else - { - vis.RenderStyle = owner->RenderStyle; - } - - // Set the alpha based on if using the overlay's own or not. Also adjust - // and override the alpha if not forced. - if (pspr->Flags & PSPF_ALPHA) - { - if (vis.RenderStyle == LegacyRenderStyles[STYLE_Fuzzy]) - { - alpha = owner->Alpha; - } - else if (vis.RenderStyle == LegacyRenderStyles[STYLE_OptFuzzy]) - { - FRenderStyle style = vis.RenderStyle; - style.CheckFuzz(); - switch (style.BlendOp) - { - default: - alpha = pspr->alpha * owner->Alpha; - break; - case STYLEOP_Fuzz: - case STYLEOP_Sub: - alpha = owner->Alpha; - break; - } - - } - else if (vis.RenderStyle == LegacyRenderStyles[STYLE_Subtract]) - { - alpha = owner->Alpha; - } - else if (vis.RenderStyle == LegacyRenderStyles[STYLE_Add] || - vis.RenderStyle == LegacyRenderStyles[STYLE_Translucent] || - vis.RenderStyle == LegacyRenderStyles[STYLE_TranslucentStencil] || - vis.RenderStyle == LegacyRenderStyles[STYLE_AddStencil] || - vis.RenderStyle == LegacyRenderStyles[STYLE_AddShaded]) - { - alpha = owner->Alpha * pspr->alpha; - } - else - { - alpha = owner->Alpha; - } - } - - // Should normal renderstyle come out on top at the end and we desire alpha, - // switch it to translucent. Normal never applies any sort of alpha. - if ((pspr->Flags & PSPF_ALPHA) && - vis.RenderStyle == LegacyRenderStyles[STYLE_Normal] && - vis.Alpha < 1.0) - { - vis.RenderStyle = LegacyRenderStyles[STYLE_Translucent]; - alpha = owner->Alpha * pspr->alpha; - } - - // ALWAYS take priority if asked for, except fuzz. Fuzz does absolutely nothing - // no matter what way it's changed. - if (pspr->Flags & PSPF_FORCEALPHA) - { - //Due to lack of != operators... - if (vis.RenderStyle == LegacyRenderStyles[STYLE_Fuzzy] || - vis.RenderStyle == LegacyRenderStyles[STYLE_SoulTrans] || - vis.RenderStyle == LegacyRenderStyles[STYLE_Stencil]) - { - } - else - { - alpha = pspr->alpha; - vis.RenderStyle.Flags |= STYLEF_ForceAlpha; - } - } - vis.Alpha = clamp(float(alpha), 0.f, 1.f); - - // Due to how some of the effects are handled, going to 0 or less causes some - // weirdness to display. There's no point rendering it anyway if it's 0. - if (vis.Alpha <= 0.) + if (!vis.RenderStyle.IsVisible(vis.Alpha)) return; //----------------------------------------------------------------------------- @@ -455,9 +355,9 @@ namespace swrenderer Thread->Viewport->viewpoint.camera->Inventory->AlterWeaponSprite(&visstyle); - vis.Alpha = visstyle.Alpha; + if (!(pspr->Flags & PSPF_FORCEALPHA)) vis.Alpha = visstyle.Alpha; - if (visstyle.RenderStyle != STYLE_Count) + if (visstyle.RenderStyle != STYLE_Count && !(pspr->Flags & PSPF_FORCESTYLE)) { vis.RenderStyle = visstyle.RenderStyle; } diff --git a/src/swrenderer/viewport/r_spritedrawer.cpp b/src/swrenderer/viewport/r_spritedrawer.cpp index 116a5037b..006af41b1 100644 --- a/src/swrenderer/viewport/r_spritedrawer.cpp +++ b/src/swrenderer/viewport/r_spritedrawer.cpp @@ -393,11 +393,7 @@ namespace swrenderer color = 0; } - if (style.Flags & STYLEF_ForceAlpha) - { - alpha = clamp(alpha, 0, OPAQUE); - } - else if (style.Flags & STYLEF_TransSoulsAlpha) + if (style.Flags & STYLEF_TransSoulsAlpha) { alpha = fixed_t(transsouls * OPAQUE); } diff --git a/wadsrc/static/zscript/shared/player.txt b/wadsrc/static/zscript/shared/player.txt index 20b2b320a..43c01044f 100644 --- a/wadsrc/static/zscript/shared/player.txt +++ b/wadsrc/static/zscript/shared/player.txt @@ -223,7 +223,7 @@ class PSprite : Object native play native readonly PlayerInfo Owner; native SpriteID Sprite; native int Frame; - native readonly int RenderStyle; + //native readonly int RenderStyle; had to be blocked because the internal representation was not ok. Renderstyle is still pending a proper solution. native readonly int ID; native Bool processPending; native double x; From 259e3127feef079e75d7eb14eff6ceb3dc9b619c Mon Sep 17 00:00:00 2001 From: Major Cooke Date: Sat, 15 Apr 2017 09:33:04 -0500 Subject: [PATCH 07/29] - Fixed crash with corpse queueing. --- src/g_shared/a_action.cpp | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/src/g_shared/a_action.cpp b/src/g_shared/a_action.cpp index 627c7323d..a15628cb4 100644 --- a/src/g_shared/a_action.cpp +++ b/src/g_shared/a_action.cpp @@ -125,16 +125,19 @@ DCorpsePointer::DCorpsePointer (AActor *ptr) TThinkerIterator iterator (STAT_CORPSEPOINTER); DCorpsePointer *first = iterator.Next (); - if (first != this) + if (first != nullptr) { - if (first->Count >= (uint32_t)sv_corpsequeuesize) + if (first != this) { - DCorpsePointer *next = iterator.Next (); - first->Destroy (); - first = next; + if (first->Count >= (uint32_t)sv_corpsequeuesize) + { + DCorpsePointer *next = iterator.Next(); + first->Destroy(); + first = next; + } } + ++first->Count; } - ++first->Count; } void DCorpsePointer::OnDestroy () From 47bcd8aaa2f8f54bb9d148611da33256c57ea5a3 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Sat, 15 Apr 2017 17:25:46 +0200 Subject: [PATCH 08/29] - added per-layer translucency support to the OpenGL PSprite drawer. --- src/gl/scene/gl_weapon.cpp | 114 +++++++++++++++++++------------------ 1 file changed, 59 insertions(+), 55 deletions(-) diff --git a/src/gl/scene/gl_weapon.cpp b/src/gl/scene/gl_weapon.cpp index c0dc250f7..2d2d827c6 100644 --- a/src/gl/scene/gl_weapon.cpp +++ b/src/gl/scene/gl_weapon.cpp @@ -307,38 +307,47 @@ void GLSceneDrawer::DrawPlayerSprites(sector_t * viewsector, bool hudModelStep) lightlevel = 255; } - PalEntry ThingColor = (playermo->RenderStyle.Flags & STYLEF_ColorIsFixed) ? playermo->fillcolor : 0xffffff; - ThingColor.a = 255; + gl_RenderState.AlphaFunc(GL_GEQUAL, gl_mask_sprite_threshold); - visstyle_t vis; + // hack alert! Rather than changing everything in the underlying lighting code let's just temporarily change + // light mode here to draw the weapon sprite. + int oldlightmode = glset.lightmode; + if (glset.lightmode == 8) glset.lightmode = 2; - vis.RenderStyle = STYLE_Count; - vis.Alpha = playermo->Alpha; - vis.Invert = false; - playermo->AlterWeaponSprite(&vis); - - FRenderStyle RenderStyle; - if (vis.RenderStyle == STYLE_Count) RenderStyle = playermo->RenderStyle; - else RenderStyle = vis.RenderStyle; - - if (vis.Invert) + for(DPSprite *psp = player->psprites; psp != nullptr && psp->GetID() < PSP_TARGETCENTER; psp = psp->GetNext()) { - // this only happens for Strife's inverted weapon sprite - RenderStyle.Flags |= STYLEF_InvertSource; - } - if (RenderStyle.AsDWORD == 0) - { - // This is RenderStyle None. - return; - } + auto rs = psp->GetRenderStyle(playermo->RenderStyle, playermo->Alpha); - // Set the render parameters + visstyle_t vis; + float trans = 0.f; - int OverrideShader = -1; - float trans = 0.f; - if (RenderStyle.BlendOp >= STYLEOP_Fuzz && RenderStyle.BlendOp <= STYLEOP_FuzzOrRevSub) - { - RenderStyle.CheckFuzz(); + vis.RenderStyle = STYLE_Count; + vis.Alpha = rs.second; + vis.Invert = false; + playermo->AlterWeaponSprite(&vis); + + FRenderStyle RenderStyle; + + if (!(psp->Flags & PSPF_FORCEALPHA)) trans = vis.Alpha; + + if (vis.RenderStyle != STYLE_Count && !(psp->Flags & PSPF_FORCESTYLE)) + { + RenderStyle = vis.RenderStyle; + } + else + { + RenderStyle = rs.first; + } + + if (vis.Invert) + { + // this only happens for Strife's inverted weapon sprite + RenderStyle.Flags |= STYLEF_InvertSource; + } + + // Set the render parameters + + int OverrideShader = -1; if (RenderStyle.BlendOp == STYLEOP_Fuzz) { if (gl_fuzztype != 0) @@ -353,40 +362,35 @@ void GLSceneDrawer::DrawPlayerSprites(sector_t * viewsector, bool hudModelStep) RenderStyle.BlendOp = STYLEOP_Shadow; } } - } - gl_SetRenderStyle(RenderStyle, false, false); + gl_SetRenderStyle(RenderStyle, false, false); - if (RenderStyle.Flags & STYLEF_TransSoulsAlpha) - { - trans = transsouls; - } - else if (RenderStyle.Flags & STYLEF_Alpha1) - { - trans = 1.f; - } - else if (trans == 0.f) - { - trans = vis.Alpha; - } + if (RenderStyle.Flags & STYLEF_TransSoulsAlpha) + { + trans = transsouls; + } + else if (RenderStyle.Flags & STYLEF_Alpha1) + { + trans = 1.f; + } + else if (trans == 0.f) + { + trans = vis.Alpha; + } - // now draw the different layers of the weapon - gl_RenderState.EnableBrightmap(true); - PalEntry finalcol(ThingColor.a, - ThingColor.r * viewsector->SpecialColors[sector_t::sprites].r / 255, - ThingColor.g * viewsector->SpecialColors[sector_t::sprites].g / 255, - ThingColor.b * viewsector->SpecialColors[sector_t::sprites].b / 255); + PalEntry ThingColor = (playermo->RenderStyle.Flags & STYLEF_ColorIsFixed) ? playermo->fillcolor : 0xffffff; + ThingColor.a = 255; - gl_RenderState.SetObjectColor(finalcol); - gl_RenderState.AlphaFunc(GL_GEQUAL, gl_mask_sprite_threshold); + // now draw the different layers of the weapon. + // For stencil render styles brightmaps need to be disabled. + gl_RenderState.EnableBrightmap(!(RenderStyle.Flags & STYLEF_ColorIsFixed)); + PalEntry finalcol(ThingColor.a, + ThingColor.r * viewsector->SpecialColors[sector_t::sprites].r / 255, + ThingColor.g * viewsector->SpecialColors[sector_t::sprites].g / 255, + ThingColor.b * viewsector->SpecialColors[sector_t::sprites].b / 255); - // hack alert! Rather than changing everything in the underlying lighting code let's just temporarily change - // light mode here to draw the weapon sprite. - int oldlightmode = glset.lightmode; - if (glset.lightmode == 8) glset.lightmode = 2; + gl_RenderState.SetObjectColor(finalcol); - for(DPSprite *psp = player->psprites; psp != nullptr && psp->GetID() < PSP_TARGETCENTER; psp = psp->GetNext()) - { if (psp->GetState() != nullptr) { FColormap cmc = cm; From 4f67dc4f016c3e62fec838130def42e7affca297 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Sat, 15 Apr 2017 19:07:13 +0200 Subject: [PATCH 09/29] - fixed: DCorpsePointer's constructor was doing nasty stuff while the object wasn't fully set up yet. --- src/g_shared/a_action.cpp | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/src/g_shared/a_action.cpp b/src/g_shared/a_action.cpp index a15628cb4..fe4ab9d4b 100644 --- a/src/g_shared/a_action.cpp +++ b/src/g_shared/a_action.cpp @@ -85,6 +85,7 @@ class DCorpsePointer : public DThinker HAS_OBJECT_POINTERS public: DCorpsePointer (AActor *ptr); + void Queue(); void OnDestroy() override; void Serialize(FSerializer &arc); TObjPtr Corpse; @@ -115,11 +116,14 @@ CUSTOM_CVAR(Int, sv_corpsequeuesize, 64, CVAR_ARCHIVE|CVAR_SERVERINFO) } -DCorpsePointer::DCorpsePointer (AActor *ptr) -: DThinker (STAT_CORPSEPOINTER), Corpse (ptr) +DCorpsePointer::DCorpsePointer(AActor *ptr) + : DThinker(STAT_CORPSEPOINTER), Corpse(ptr) { Count = 0; +} +void DCorpsePointer::Queue() +{ // Thinkers are added to the end of their respective lists, so // the first thinker in the list is the oldest one. TThinkerIterator iterator (STAT_CORPSEPOINTER); @@ -184,7 +188,8 @@ DEFINE_ACTION_FUNCTION(AActor, A_QueueCorpse) if (sv_corpsequeuesize > 0) { - Create (self); + auto p = Create (self); + p->Queue(); } return 0; } From ba5721f98a0efce8310d89f18ad10cf45d98bd90 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Sun, 16 Apr 2017 01:36:53 +0200 Subject: [PATCH 10/29] - rewrote the OPL middle layer to remove the MusLib code. The new version keeps the non-MusLib code of both files and replaces most of the rest with code from Chocolate Doom. --- src/CMakeLists.txt | 4 +- .../mididevices/music_opl_mididevice.cpp | 73 +-- .../music_opldumper_mididevice.cpp | 10 +- src/sound/musicformats/music_opl.cpp | 2 +- src/sound/oplsynth/dosbox/opl.cpp | 2 +- src/sound/oplsynth/fmopl.cpp | 14 +- src/sound/oplsynth/genmidi.h | 47 ++ src/sound/oplsynth/mlopl.cpp | 485 ----------------- src/sound/oplsynth/mlopl_io.cpp | 380 -------------- src/sound/oplsynth/musicblock.cpp | 454 ++++++++++++++++ src/sound/oplsynth/musicblock.h | 58 +++ src/sound/oplsynth/muslib.h | 267 ---------- src/sound/oplsynth/nukedopl3.h | 2 +- src/sound/oplsynth/opl.h | 2 + src/sound/oplsynth/opl_mus_player.cpp | 24 +- src/sound/oplsynth/opl_mus_player.h | 2 + src/sound/oplsynth/oplio.cpp | 493 ++++++++++++++++++ src/sound/oplsynth/oplio.h | 110 ++++ 18 files changed, 1233 insertions(+), 1196 deletions(-) create mode 100644 src/sound/oplsynth/genmidi.h delete mode 100644 src/sound/oplsynth/mlopl.cpp delete mode 100644 src/sound/oplsynth/mlopl_io.cpp create mode 100644 src/sound/oplsynth/musicblock.cpp create mode 100644 src/sound/oplsynth/musicblock.h delete mode 100644 src/sound/oplsynth/muslib.h create mode 100644 src/sound/oplsynth/oplio.cpp create mode 100644 src/sound/oplsynth/oplio.h diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index da79cd408..54dce36ca 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1256,8 +1256,8 @@ set (PCH_SOURCES sound/musicformats/music_opl.cpp sound/musicformats/music_stream.cpp sound/oplsynth/fmopl.cpp - sound/oplsynth/mlopl.cpp - sound/oplsynth/mlopl_io.cpp + sound/oplsynth/musicblock.cpp + sound/oplsynth/oplio.cpp sound/oplsynth/dosbox/opl.cpp sound/oplsynth/OPL3.cpp sound/oplsynth/nukedopl3.cpp diff --git a/src/sound/mididevices/music_opl_mididevice.cpp b/src/sound/mididevices/music_opl_mididevice.cpp index e61a5c10d..fe09e0448 100644 --- a/src/sound/mididevices/music_opl_mididevice.cpp +++ b/src/sound/mididevices/music_opl_mididevice.cpp @@ -83,7 +83,11 @@ OPLMIDIDevice::OPLMIDIDevice(const char *args) OPL_SetCore(args); FullPan = opl_fullpan; FWadLump data = Wads.OpenLumpName("GENMIDI"); - OPLloadBank(data); + + uint8_t filehdr[8]; + data.Read(filehdr, 8); + if (memcmp(filehdr, "#OPL_II#", 8)) I_Error("Corrupt GENMIDI lump"); + data.Read(OPLinstruments, sizeof(genmidi_instr_t) * GENMIDI_NUM_TOTAL); SampleRate = (int)OPL_SAMPLE_RATE; } @@ -97,15 +101,15 @@ OPLMIDIDevice::OPLMIDIDevice(const char *args) int OPLMIDIDevice::Open(MidiCallback callback, void *userdata) { - if (io == NULL || 0 == (NumChips = io->OPLinit(opl_numchips, FullPan, true))) + if (io == NULL || 0 == (NumChips = io->Init(opl_numchips, FullPan, true))) { return 1; } int ret = OpenStream(14, (FullPan || io->IsOPL3) ? 0 : SoundStream::Mono, callback, userdata); if (ret == 0) { - OPLstopMusic(); - OPLplayMusic(100); + stopAllVoices(); + resetAllControllers(100); DEBUGOUT("========= New song started ==========\n", 0, 0, 0); } return ret; @@ -120,7 +124,7 @@ int OPLMIDIDevice::Open(MidiCallback callback, void *userdata) void OPLMIDIDevice::Close() { SoftSynthMIDIDevice::Close(); - io->OPLdeinit(); + io->Reset(); } //========================================================================== @@ -176,7 +180,7 @@ void OPLMIDIDevice::HandleEvent(int status, int parm1, int parm2) int command = status & 0xF0; int channel = status & 0x0F; - // Swap channels 9 and 15, because their roles are reversed + // Swap voices 9 and 15, because their roles are reversed // in MUS and MIDI formats. if (channel == 9) { @@ -191,12 +195,12 @@ void OPLMIDIDevice::HandleEvent(int status, int parm1, int parm2) { case MIDI_NOTEOFF: playingcount--; - OPLreleaseNote(channel, parm1); + noteOff(channel, parm1); break; case MIDI_NOTEON: playingcount++; - OPLplayNote(channel, parm1, parm2); + noteOn(channel, parm1, parm2); break; case MIDI_POLYPRESS: @@ -206,26 +210,27 @@ void OPLMIDIDevice::HandleEvent(int status, int parm1, int parm2) case MIDI_CTRLCHANGE: switch (parm1) { - case 0: OPLchangeControl(channel, ctrlBank, parm2); break; - case 1: OPLchangeControl(channel, ctrlModulation, parm2); break; - case 6: OPLchangeControl(channel, ctrlDataEntryHi, parm2); break; - case 7: OPLchangeControl(channel, ctrlVolume, parm2); break; - case 10: OPLchangeControl(channel, ctrlPan, parm2); break; - case 11: OPLchangeControl(channel, ctrlExpression, parm2); break; - case 38: OPLchangeControl(channel, ctrlDataEntryLo, parm2); break; - case 64: OPLchangeControl(channel, ctrlSustainPedal, parm2); break; - case 67: OPLchangeControl(channel, ctrlSoftPedal, parm2); break; - case 91: OPLchangeControl(channel, ctrlReverb, parm2); break; - case 93: OPLchangeControl(channel, ctrlChorus, parm2); break; - case 98: OPLchangeControl(channel, ctrlNRPNLo, parm2); break; - case 99: OPLchangeControl(channel, ctrlNRPNHi, parm2); break; - case 100: OPLchangeControl(channel, ctrlRPNLo, parm2); break; - case 101: OPLchangeControl(channel, ctrlRPNHi, parm2); break; - case 120: OPLchangeControl(channel, ctrlSoundsOff, parm2); break; - case 121: OPLresetControllers(channel, 100); break; - case 123: OPLchangeControl(channel, ctrlNotesOff, parm2); break; - case 126: OPLchangeControl(channel, ctrlMono, parm2); break; - case 127: OPLchangeControl(channel, ctrlPoly, parm2); break; + // some controllers here get passed on but are not handled by the player. + //case 0: changeBank(channel, parm2); break; + case 1: changeModulation(channel, parm2); break; + case 6: changeExtended(channel, ctrlDataEntryHi, parm2); break; + case 7: changeVolume(channel, parm2, false); break; + case 10: changePanning(channel, parm2); break; + case 11: changeVolume(channel, parm2, true); break; + case 38: changeExtended(channel, ctrlDataEntryLo, parm2); break; + case 64: changeSustain(channel, parm2); break; + //case 67: changeSoftPedal(channel, parm2); break; + //case 91: changeReverb(channel, parm2); break; + //case 93: changeChorus(channel, parm2); break; + case 98: changeExtended(channel, ctrlNRPNLo, parm2); break; + case 99: changeExtended(channel, ctrlNRPNHi, parm2); break; + case 100: changeExtended(channel, ctrlRPNLo, parm2); break; + case 101: changeExtended(channel, ctrlRPNHi, parm2); break; + case 120: allNotesOff(channel, parm2); break; + case 121: resetControllers(channel, 100); break; + case 123: notesOff(channel, parm2); break; + //case 126: changeMono(channel, parm2); break; + //case 127: changePoly(channel, parm2); break; default: DEBUGOUT("Unhandled controller: Channel %d, controller %d, value %d\n", channel, parm1, parm2); break; @@ -233,7 +238,7 @@ void OPLMIDIDevice::HandleEvent(int status, int parm1, int parm2) break; case MIDI_PRGMCHANGE: - OPLprogramChange(channel, parm1); + programChange(channel, parm1); break; case MIDI_CHANPRESS: @@ -241,7 +246,7 @@ void OPLMIDIDevice::HandleEvent(int status, int parm1, int parm2) break; case MIDI_PITCHBEND: - OPLpitchWheel(channel, parm1 | (parm2 << 7)); + changePitch(channel, parm1, parm2); break; } } @@ -289,17 +294,17 @@ FString OPLMIDIDevice::GetStats() { FString out; char star[3] = { TEXTCOLOR_ESCAPE, 'A', '*' }; - for (uint32_t i = 0; i < io->OPLchannels; ++i) + for (uint32_t i = 0; i < io->NumChannels; ++i) { - if (channels[i].flags & CH_FREE) + if (voices[i].index == -1) { star[1] = CR_BRICK + 'A'; } - else if (channels[i].flags & CH_SUSTAIN) + else if (voices[i].sustained) { star[1] = CR_ORANGE + 'A'; } - else if (channels[i].flags & CH_SECONDARY) + else if (voices[i].current_instr_voice == &voices[i].current_instr->voices[1]) { star[1] = CR_BLUE + 'A'; } diff --git a/src/sound/mididevices/music_opldumper_mididevice.cpp b/src/sound/mididevices/music_opldumper_mididevice.cpp index cf7000d4c..e301a3054 100644 --- a/src/sound/mididevices/music_opldumper_mididevice.cpp +++ b/src/sound/mididevices/music_opldumper_mididevice.cpp @@ -327,16 +327,16 @@ DiskWriterIO::DiskWriterIO(const char *filename) DiskWriterIO::~DiskWriterIO() { - OPLdeinit(); + Reset(); } //========================================================================== // -// DiskWriterIO :: OPLinit +// DiskWriterIO :: Init // //========================================================================== -int DiskWriterIO::OPLinit(uint32_t numchips, bool, bool initopl3) +int DiskWriterIO::Init(uint32_t numchips, bool, bool initopl3) { FILE *file = fopen(Filename, "wb"); if (file == NULL) @@ -357,10 +357,10 @@ int DiskWriterIO::OPLinit(uint32_t numchips, bool, bool initopl3) { chips[0] = new OPL_DOSBOXdump(file, numchips > 1); } - OPLchannels = OPL2CHANNELS * numchips; + NumChannels = OPL_NUM_VOICES * numchips; NumChips = numchips; IsOPL3 = numchips > 1; - OPLwriteInitState(initopl3); + WriteInitState(initopl3); return numchips; } diff --git a/src/sound/musicformats/music_opl.cpp b/src/sound/musicformats/music_opl.cpp index 85f4b815b..0f949fae0 100644 --- a/src/sound/musicformats/music_opl.cpp +++ b/src/sound/musicformats/music_opl.cpp @@ -32,7 +32,7 @@ */ #include "i_musicinterns.h" -#include "oplsynth/muslib.h" +#include "oplsynth/musicblock.h" #include "oplsynth/opl.h" static bool OPL_Active; diff --git a/src/sound/oplsynth/dosbox/opl.cpp b/src/sound/oplsynth/dosbox/opl.cpp index 25b446210..67fcefc0b 100644 --- a/src/sound/oplsynth/dosbox/opl.cpp +++ b/src/sound/oplsynth/dosbox/opl.cpp @@ -26,7 +26,7 @@ #include "doomtype.h" #include "../opl.h" -#include "../muslib.h" +#include "../musicblock.h" #include #include "m_random.h" diff --git a/src/sound/oplsynth/fmopl.cpp b/src/sound/oplsynth/fmopl.cpp index 0e270e6f5..1bbd74dc4 100644 --- a/src/sound/oplsynth/fmopl.cpp +++ b/src/sound/oplsynth/fmopl.cpp @@ -1311,7 +1311,7 @@ static inline void set_sl_rr(FM_OPL *OPL,int slot,int v) /* write a value v to register r on OPL chip */ -static void OPLWriteReg(FM_OPL *OPL, int r, int v) +static void WriteRegister(FM_OPL *OPL, int r, int v) { OPL_CH *CH; int slot; @@ -1523,11 +1523,11 @@ static void OPLResetChip(FM_OPL *OPL) OPL_STATUS_RESET(OPL,0x7f); /* reset with register write */ - OPLWriteReg(OPL,0x01,0); /* wavesel disable */ - OPLWriteReg(OPL,0x02,0); /* Timer1 */ - OPLWriteReg(OPL,0x03,0); /* Timer2 */ - OPLWriteReg(OPL,0x04,0); /* IRQ mask clear */ - for(i = 0xff ; i >= 0x20 ; i-- ) OPLWriteReg(OPL,i,0); + WriteRegister(OPL,0x01,0); /* wavesel disable */ + WriteRegister(OPL,0x02,0); /* Timer1 */ + WriteRegister(OPL,0x03,0); /* Timer2 */ + WriteRegister(OPL,0x04,0); /* IRQ mask clear */ + for(i = 0xff ; i >= 0x20 ; i-- ) WriteRegister(OPL,i,0); /* reset operator parameters */ for( c = 0 ; c < 9 ; c++ ) @@ -1569,7 +1569,7 @@ public: /* YM3812 I/O interface */ void WriteReg(int reg, int v) { - OPLWriteReg(&Chip, reg & 0xff, v); + WriteRegister(&Chip, reg & 0xff, v); } void Reset() diff --git a/src/sound/oplsynth/genmidi.h b/src/sound/oplsynth/genmidi.h new file mode 100644 index 000000000..76ac26a96 --- /dev/null +++ b/src/sound/oplsynth/genmidi.h @@ -0,0 +1,47 @@ +#include "doomtype.h" + +#pragma pack(push, 1) +struct genmidi_op_t +{ + uint8_t tremolo; + uint8_t attack; + uint8_t sustain; + uint8_t waveform; + uint8_t scale; + uint8_t level; +} FORCE_PACKED; + +struct genmidi_voice_t +{ + genmidi_op_t modulator; + uint8_t feedback; + genmidi_op_t carrier; + uint8_t unused; + int16_t base_note_offset; +} FORCE_PACKED; + + +struct genmidi_instr_t +{ + uint16_t flags; + uint8_t fine_tuning; + uint8_t fixed_note; + genmidi_voice_t voices[2]; +} FORCE_PACKED; + +#pragma pack(pop) + +enum +{ + GENMIDI_FLAG_FIXED = 0x0001, /* fixed pitch */ + GENMIDI_FLAG_2VOICE = 0x0004 /* double voice (OPL3) */ +}; + +enum +{ + GENMIDI_NUM_INSTRS = 128, + GENMIDI_FIST_PERCUSSION = 35, + GENMIDI_NUM_PERCUSSION = 47, + GENMIDI_NUM_TOTAL = GENMIDI_NUM_INSTRS + GENMIDI_NUM_PERCUSSION +}; + diff --git a/src/sound/oplsynth/mlopl.cpp b/src/sound/oplsynth/mlopl.cpp deleted file mode 100644 index 87cd7f450..000000000 --- a/src/sound/oplsynth/mlopl.cpp +++ /dev/null @@ -1,485 +0,0 @@ -/* -* Name: OPL2/OPL3 Music driver -* Project: MUS File Player Library -* Version: 1.67 -* Author: Vladimir Arnost (QA-Software) -* Last revision: May-2-1996 -* Compiler: Borland C++ 3.1, Watcom C/C++ 10.0 -* -*/ - -/* -* Revision History: -* -* Aug-8-1994 V1.00 V.Arnost -* Written from scratch -* Aug-9-1994 V1.10 V.Arnost -* Some minor changes to improve sound quality. Tried to add -* stereo sound capabilities, but failed to -- my SB Pro refuses -* to switch to stereo mode. -* Aug-13-1994 V1.20 V.Arnost -* Stereo sound fixed. Now works also with Sound Blaster Pro II -* (chip OPL3 -- gives 18 "stereo" (ahem) channels). -* Changed code to handle properly notes without volume. -* (Uses previous volume on given channel.) -* Added cyclic channel usage to avoid annoying clicking noise. -* Aug-28-1994 V1.40 V.Arnost -* Added Adlib and SB Pro II detection. -* Apr-16-1995 V1.60 V.Arnost -* Moved into separate source file MUSLIB.C -* Jul-12-1995 V1.62 V.Arnost -* Module created and source code copied from MUSLIB.C -* Aug-08-1995 V1.63 V.Arnost -* Modified to follow changes in MLOPL_IO.C -* Aug-13-1995 V1.64 V.Arnost -* Added OPLsendMIDI() function -* Sep-8-1995 V1.65 V.Arnost -* Added sustain pedal support. -* Improved pause/unpause functions. Now all notes are made -* silent (even released notes, which haven't sounded off yet). -* Mar-1-1996 V1.66 V.Arnost -* Cleaned up the source -* May-2-1996 V1.67 V.Arnost -* Added modulation wheel (vibrato) support -*/ - -#include -#include -#ifdef _WIN32 -#include -#endif -#include "muslib.h" -#include "files.h" - -#include "c_cvars.h" - -#define MOD_MIN 40 /* vibrato threshold */ - - -//#define HIGHEST_NOTE 102 -#define HIGHEST_NOTE 127 - -musicBlock::musicBlock () -{ - memset (this, 0, sizeof(*this)); -} - -musicBlock::~musicBlock () -{ - if (OPLinstruments != NULL) free(OPLinstruments); -} - -void musicBlock::writeFrequency(uint32_t slot, uint32_t note, int pitch, uint32_t keyOn) -{ - io->OPLwriteFreq (slot, note, pitch, keyOn); -} - -void musicBlock::writeModulation(uint32_t slot, struct OPL2instrument *instr, int state) -{ - if (state) - state = 0x40; /* enable Frequency Vibrato */ - io->OPLwriteChannel(0x20, slot, - (instr->feedback & 1) ? (instr->trem_vibr_1 | state) : instr->trem_vibr_1, - instr->trem_vibr_2 | state); -} - -uint32_t musicBlock::calcVolume(uint32_t channelVolume, uint32_t channelExpression, uint32_t noteVolume) -{ - noteVolume = ((uint64_t)channelVolume * channelExpression * noteVolume) / (127*127); - if (noteVolume > 127) - return 127; - else - return noteVolume; -} - -int musicBlock::occupyChannel(uint32_t slot, uint32_t channel, - int note, int volume, struct OP2instrEntry *instrument, uint8_t secondary) -{ - struct OPL2instrument *instr; - struct channelEntry *ch = &channels[slot]; - - ch->channel = channel; - ch->note = note; - ch->flags = secondary ? CH_SECONDARY : 0; - if (driverdata.channelModulation[channel] >= MOD_MIN) - ch->flags |= CH_VIBRATO; - ch->time = MLtime; - if (volume == -1) - volume = driverdata.channelLastVolume[channel]; - else - driverdata.channelLastVolume[channel] = volume; - ch->realvolume = calcVolume(driverdata.channelVolume[channel], - driverdata.channelExpression[channel], ch->volume = volume); - if (instrument->flags & FL_FIXED_PITCH) - note = instrument->note; - else if (channel == PERCUSSION) - note = 60; // C-5 - if (secondary && (instrument->flags & FL_DOUBLE_VOICE)) - ch->finetune = (instrument->finetune - 0x80) >> 1; - else - ch->finetune = 0; - ch->pitch = ch->finetune + driverdata.channelPitch[channel]; - if (secondary) - instr = &instrument->instr[1]; - else - instr = &instrument->instr[0]; - ch->instr = instr; - if (channel != PERCUSSION && !(instrument->flags & FL_FIXED_PITCH)) - { - if ( (note += instr->basenote) < 0) - while ((note += 12) < 0) {} - else if (note > HIGHEST_NOTE) - while ((note -= 12) > HIGHEST_NOTE) {} - } - ch->realnote = note; - - io->OPLwriteInstrument(slot, instr); - if (ch->flags & CH_VIBRATO) - writeModulation(slot, instr, 1); - io->OPLwritePan(slot, instr, driverdata.channelPan[channel]); - io->OPLwriteVolume(slot, instr, ch->realvolume); - writeFrequency(slot, note, ch->pitch, 1); - return slot; -} - -int musicBlock::releaseChannel(uint32_t slot, uint32_t killed) -{ - struct channelEntry *ch = &channels[slot]; - writeFrequency(slot, ch->realnote, ch->pitch, 0); - ch->channel |= CH_FREE; - ch->time = MLtime; - ch->flags = CH_FREE; - if (killed) - { - io->OPLwriteChannel(0x80, slot, 0x0F, 0x0F); // release rate - fastest - io->OPLwriteChannel(0x40, slot, 0x3F, 0x3F); // no volume - } - return slot; -} - -int musicBlock::releaseSustain(uint32_t channel) -{ - uint32_t i; - uint32_t id = channel; - - for(i = 0; i < io->OPLchannels; i++) - { - if (channels[i].channel == id && channels[i].flags & CH_SUSTAIN) - releaseChannel(i, 0); - } - return 0; -} - -int musicBlock::findFreeChannel(uint32_t flag, uint32_t channel, uint8_t note) -{ - uint32_t i; - - uint32_t bestfit = 0; - uint32_t bestvoice = 0; - - for (i = 0; i < io->OPLchannels; ++i) - { - uint32_t magic; - - magic = ((channels[i].flags & CH_FREE) << 24) | - ((channels[i].note == note && - channels[i].channel == channel) << 30) | - ((channels[i].flags & CH_SUSTAIN) << 28) | - ((MLtime - channels[i].time) & 0x1fffffff); - if (magic > bestfit) - { - bestfit = magic; - bestvoice = i; - } - } - if ((flag & 1) && !(bestfit & 0x80000000)) - { // No free channels good enough - return -1; - } - releaseChannel (bestvoice, 1); - return bestvoice; -} - -struct OP2instrEntry *musicBlock::getInstrument(uint32_t channel, uint8_t note) -{ - uint32_t instrnumber; - - if (channel == PERCUSSION) - { - if (note < 35 || note > 81) - return NULL; /* wrong percussion number */ - instrnumber = note + (128-35); - } - else - { - instrnumber = driverdata.channelInstr[channel]; - } - - if (OPLinstruments) - return &OPLinstruments[instrnumber]; - else - return NULL; -} - - -// code 1: play note -CVAR (Bool, opl_singlevoice, 0, 0) - -void musicBlock::OPLplayNote(uint32_t channel, uint8_t note, int volume) -{ - int i; - struct OP2instrEntry *instr; - - if (volume == 0) - { - OPLreleaseNote (channel, note); - return; - } - - if ( (instr = getInstrument(channel, note)) == NULL ) - return; - - if ( (i = findFreeChannel((channel == PERCUSSION) ? 2 : 0, channel, note)) != -1) - { - occupyChannel(i, channel, note, volume, instr, 0); - if ((instr->flags & FL_DOUBLE_VOICE) && !opl_singlevoice) - { - if ( (i = findFreeChannel((channel == PERCUSSION) ? 3 : 1, channel, note)) != -1) - occupyChannel(i, channel, note, volume, instr, 1); - } - } -} - -// code 0: release note -void musicBlock::OPLreleaseNote(uint32_t channel, uint8_t note) -{ - uint32_t i; - uint32_t id = channel; - uint32_t sustain = driverdata.channelSustain[channel]; - - for(i = 0; i < io->OPLchannels; i++) - { - if (channels[i].channel == id && channels[i].note == note) - { - if (sustain < 0x40) - releaseChannel(i, 0); - else - channels[i].flags |= CH_SUSTAIN; - } - } -} - -// code 2: change pitch wheel (bender) -void musicBlock::OPLpitchWheel(uint32_t channel, int pitch) -{ - uint32_t i; - uint32_t id = channel; - - // Convert pitch from 14-bit to 7-bit, then scale it, since the player - // code only understands sensitivities of 2 semitones. - pitch = (pitch - 8192) * driverdata.channelPitchSens[channel] / (200 * 128) + 64; - driverdata.channelPitch[channel] = pitch; - for(i = 0; i < io->OPLchannels; i++) - { - struct channelEntry *ch = &channels[i]; - if (ch->channel == id) - { - ch->time = MLtime; - ch->pitch = ch->finetune + pitch; - writeFrequency(i, ch->realnote, ch->pitch, 1); - } - } -} - -// code 4: change control -void musicBlock::OPLchangeControl(uint32_t channel, uint8_t controller, int value) -{ - uint32_t i; - uint32_t id = channel; - - switch (controller) - { - case ctrlPatch: /* change instrument */ - OPLprogramChange(channel, value); - break; - - case ctrlModulation: - driverdata.channelModulation[channel] = value; - for(i = 0; i < io->OPLchannels; i++) - { - struct channelEntry *ch = &channels[i]; - if (ch->channel == id) - { - uint8_t flags = ch->flags; - ch->time = MLtime; - if (value >= MOD_MIN) - { - ch->flags |= CH_VIBRATO; - if (ch->flags != flags) - writeModulation(i, ch->instr, 1); - } else { - ch->flags &= ~CH_VIBRATO; - if (ch->flags != flags) - writeModulation(i, ch->instr, 0); - } - } - } - break; - - case ctrlVolume: /* change volume */ - driverdata.channelVolume[channel] = value; - /* fall-through */ - case ctrlExpression: /* change expression */ - if (controller == ctrlExpression) - { - driverdata.channelExpression[channel] = value; - } - for(i = 0; i < io->OPLchannels; i++) - { - struct channelEntry *ch = &channels[i]; - if (ch->channel == id) - { - ch->time = MLtime; - ch->realvolume = calcVolume(driverdata.channelVolume[channel], - driverdata.channelExpression[channel], ch->volume); - io->OPLwriteVolume(i, ch->instr, ch->realvolume); - } - } - break; - - case ctrlPan: /* change pan (balance) */ - driverdata.channelPan[channel] = value -= 64; - for(i = 0; i < io->OPLchannels; i++) - { - struct channelEntry *ch = &channels[i]; - if (ch->channel == id) - { - ch->time = MLtime; - io->OPLwritePan(i, ch->instr, value); - } - } - break; - - case ctrlSustainPedal: /* change sustain pedal (hold) */ - driverdata.channelSustain[channel] = value; - if (value < 0x40) - releaseSustain(channel); - break; - - case ctrlNotesOff: /* turn off all notes that are not sustained */ - for (i = 0; i < io->OPLchannels; ++i) - { - if (channels[i].channel == id) - { - if (driverdata.channelSustain[id] < 0x40) - releaseChannel(i, 0); - else - channels[i].flags |= CH_SUSTAIN; - } - } - break; - - case ctrlSoundsOff: /* release all notes for this channel */ - for (i = 0; i < io->OPLchannels; ++i) - { - if (channels[i].channel == id) - { - releaseChannel(i, 0); - } - } - break; - - case ctrlRPNHi: - driverdata.channelRPN[id] = (driverdata.channelRPN[id] & 0x007F) | (value << 7); - break; - - case ctrlRPNLo: - driverdata.channelRPN[id] = (driverdata.channelRPN[id] & 0x3F80) | value; - break; - - case ctrlNRPNLo: - case ctrlNRPNHi: - driverdata.channelRPN[id] = 0x3FFF; - break; - - case ctrlDataEntryHi: - if (driverdata.channelRPN[id] == 0) - { - driverdata.channelPitchSens[id] = value * 100 + (driverdata.channelPitchSens[id] % 100); - } - break; - - case ctrlDataEntryLo: - if (driverdata.channelRPN[id] == 0) - { - driverdata.channelPitchSens[id] = value + (driverdata.channelPitchSens[id] / 100) * 100; - } - break; - } -} - -void musicBlock::OPLresetControllers(uint32_t chan, int vol) -{ - driverdata.channelVolume[chan] = vol; - driverdata.channelExpression[chan] = 127; - driverdata.channelSustain[chan] = 0; - driverdata.channelLastVolume[chan] = 64; - driverdata.channelPitch[chan] = 64; - driverdata.channelRPN[chan] = 0x3fff; - driverdata.channelPitchSens[chan] = 200; -} - -void musicBlock::OPLprogramChange(uint32_t channel, int value) -{ - driverdata.channelInstr[channel] = value; -} - -void musicBlock::OPLplayMusic(int vol) -{ - uint32_t i; - - for (i = 0; i < CHANNELS; i++) - { - OPLresetControllers(i, vol); - } -} - -void musicBlock::OPLstopMusic() -{ - uint32_t i; - for(i = 0; i < io->OPLchannels; i++) - if (!(channels[i].flags & CH_FREE)) - releaseChannel(i, 1); -} - -int musicBlock::OPLloadBank (FileReader &data) -{ - static const uint8_t masterhdr[8] = { '#','O','P','L','_','I','I','#' }; - struct OP2instrEntry *instruments; - - uint8_t filehdr[8]; - - data.Read (filehdr, 8); - if (memcmp(filehdr, masterhdr, 8)) - return -2; /* bad instrument file */ - if ( (instruments = (struct OP2instrEntry *)calloc(OP2INSTRCOUNT, OP2INSTRSIZE)) == NULL) - return -3; /* not enough memory */ - data.Read (instruments, OP2INSTRSIZE * OP2INSTRCOUNT); - if (OPLinstruments != NULL) - { - free(OPLinstruments); - } - OPLinstruments = instruments; -#if 0 - for (int i = 0; i < 175; ++i) - { - Printf ("%3d.%-33s%3d %3d %3d %d\n", i, - (uint8_t *)data+6308+i*32, - OPLinstruments[i].instr[0].basenote, - OPLinstruments[i].instr[1].basenote, - OPLinstruments[i].note, - OPLinstruments[i].flags); - } -#endif - return 0; -} diff --git a/src/sound/oplsynth/mlopl_io.cpp b/src/sound/oplsynth/mlopl_io.cpp deleted file mode 100644 index ef89bfc27..000000000 --- a/src/sound/oplsynth/mlopl_io.cpp +++ /dev/null @@ -1,380 +0,0 @@ -/* -* Name: Low-level OPL2/OPL3 I/O interface -* Project: MUS File Player Library -* Version: 1.64 -* Author: Vladimir Arnost (QA-Software) -* Last revision: Mar-1-1996 -* Compiler: Borland C++ 3.1, Watcom C/C++ 10.0 -* -*/ - -/* -* Revision History: -* -* Aug-8-1994 V1.00 V.Arnost -* Written from scratch -* Aug-9-1994 V1.10 V.Arnost -* Added stereo capabilities -* Aug-13-1994 V1.20 V.Arnost -* Stereo capabilities made functional -* Aug-24-1994 V1.30 V.Arnost -* Added Adlib and SB Pro II detection -* Oct-30-1994 V1.40 V.Arnost -* Added BLASTER variable parsing -* Apr-14-1995 V1.50 V.Arnost -* Some declarations moved from adlib.h to doomtype.h -* Jul-22-1995 V1.60 V.Arnost -* Ported to Watcom C -* Simplified WriteChannel() and WriteValue() -* Jul-24-1995 V1.61 V.Arnost -* DetectBlaster() moved to MLMISC.C -* Aug-8-1995 V1.62 V.Arnost -* Module renamed to MLOPL_IO.C and functions renamed to OPLxxx -* Mixer-related functions moved to module MLSBMIX.C -* Sep-8-1995 V1.63 V.Arnost -* OPLwriteReg() routine sped up on OPL3 cards -* Mar-1-1996 V1.64 V.Arnost -* Cleaned up the source -*/ - -#include -#ifdef _WIN32 -#include -#include -#endif -#include "muslib.h" -#include "opl.h" -#include "c_cvars.h" - -const double HALF_PI = (M_PI*0.5); - -EXTERN_CVAR(Int, opl_core) -extern int current_opl_core; - -OPLio::~OPLio() -{ -} - -void OPLio::SetClockRate(double samples_per_tick) -{ -} - -void OPLio::WriteDelay(int ticks) -{ -} - -void OPLio::OPLwriteReg(int which, uint32_t reg, uint8_t data) -{ - if (IsOPL3) - { - reg |= (which & 1) << 8; - which >>= 1; - } - if (chips[which] != NULL) - { - chips[which]->WriteReg(reg, data); - } -} - -/* -* Write to an operator pair. To be used for register bases of 0x20, 0x40, -* 0x60, 0x80 and 0xE0. -*/ -void OPLio::OPLwriteChannel(uint32_t regbase, uint32_t channel, uint8_t data1, uint8_t data2) -{ - static const uint32_t op_num[OPL2CHANNELS] = { - 0x00, 0x01, 0x02, 0x08, 0x09, 0x0A, 0x10, 0x11, 0x12}; - - uint32_t which = channel / OPL2CHANNELS; - uint32_t reg = regbase + op_num[channel % OPL2CHANNELS]; - OPLwriteReg (which, reg, data1); - OPLwriteReg (which, reg+3, data2); -} - -/* -* Write to channel a single value. To be used for register bases of -* 0xA0, 0xB0 and 0xC0. -*/ -void OPLio::OPLwriteValue(uint32_t regbase, uint32_t channel, uint8_t value) -{ - uint32_t which = channel / OPL2CHANNELS; - uint32_t reg = regbase + (channel % OPL2CHANNELS); - OPLwriteReg (which, reg, value); -} - -static uint16_t frequencies[] = -{ - 0x133, 0x133, 0x134, 0x134, 0x135, 0x136, 0x136, 0x137, 0x137, 0x138, 0x138, 0x139, - 0x139, 0x13a, 0x13b, 0x13b, 0x13c, 0x13c, 0x13d, 0x13d, 0x13e, 0x13f, 0x13f, 0x140, - 0x140, 0x141, 0x142, 0x142, 0x143, 0x143, 0x144, 0x144, 0x145, 0x146, 0x146, 0x147, - 0x147, 0x148, 0x149, 0x149, 0x14a, 0x14a, 0x14b, 0x14c, 0x14c, 0x14d, 0x14d, 0x14e, - 0x14f, 0x14f, 0x150, 0x150, 0x151, 0x152, 0x152, 0x153, 0x153, 0x154, 0x155, 0x155, - 0x156, 0x157, 0x157, 0x158, 0x158, 0x159, 0x15a, 0x15a, 0x15b, 0x15b, 0x15c, 0x15d, - 0x15d, 0x15e, 0x15f, 0x15f, 0x160, 0x161, 0x161, 0x162, 0x162, 0x163, 0x164, 0x164, - 0x165, 0x166, 0x166, 0x167, 0x168, 0x168, 0x169, 0x16a, 0x16a, 0x16b, 0x16c, 0x16c, - 0x16d, 0x16e, 0x16e, 0x16f, 0x170, 0x170, 0x171, 0x172, 0x172, 0x173, 0x174, 0x174, - 0x175, 0x176, 0x176, 0x177, 0x178, 0x178, 0x179, 0x17a, 0x17a, 0x17b, 0x17c, 0x17c, - 0x17d, 0x17e, 0x17e, 0x17f, 0x180, 0x181, 0x181, 0x182, 0x183, 0x183, 0x184, 0x185, - 0x185, 0x186, 0x187, 0x188, 0x188, 0x189, 0x18a, 0x18a, 0x18b, 0x18c, 0x18d, 0x18d, - 0x18e, 0x18f, 0x18f, 0x190, 0x191, 0x192, 0x192, 0x193, 0x194, 0x194, 0x195, 0x196, - 0x197, 0x197, 0x198, 0x199, 0x19a, 0x19a, 0x19b, 0x19c, 0x19d, 0x19d, 0x19e, 0x19f, - 0x1a0, 0x1a0, 0x1a1, 0x1a2, 0x1a3, 0x1a3, 0x1a4, 0x1a5, 0x1a6, 0x1a6, 0x1a7, 0x1a8, - 0x1a9, 0x1a9, 0x1aa, 0x1ab, 0x1ac, 0x1ad, 0x1ad, 0x1ae, 0x1af, 0x1b0, 0x1b0, 0x1b1, - 0x1b2, 0x1b3, 0x1b4, 0x1b4, 0x1b5, 0x1b6, 0x1b7, 0x1b8, 0x1b8, 0x1b9, 0x1ba, 0x1bb, - 0x1bc, 0x1bc, 0x1bd, 0x1be, 0x1bf, 0x1c0, 0x1c0, 0x1c1, 0x1c2, 0x1c3, 0x1c4, 0x1c4, - 0x1c5, 0x1c6, 0x1c7, 0x1c8, 0x1c9, 0x1c9, 0x1ca, 0x1cb, 0x1cc, 0x1cd, 0x1ce, 0x1ce, - 0x1cf, 0x1d0, 0x1d1, 0x1d2, 0x1d3, 0x1d3, 0x1d4, 0x1d5, 0x1d6, 0x1d7, 0x1d8, 0x1d8, - 0x1d9, 0x1da, 0x1db, 0x1dc, 0x1dd, 0x1de, 0x1de, 0x1df, 0x1e0, 0x1e1, 0x1e2, 0x1e3, - 0x1e4, 0x1e5, 0x1e5, 0x1e6, 0x1e7, 0x1e8, 0x1e9, 0x1ea, 0x1eb, 0x1ec, 0x1ed, 0x1ed, - 0x1ee, 0x1ef, 0x1f0, 0x1f1, 0x1f2, 0x1f3, 0x1f4, 0x1f5, 0x1f6, 0x1f6, 0x1f7, 0x1f8, - 0x1f9, 0x1fa, 0x1fb, 0x1fc, 0x1fd, 0x1fe, 0x1ff, 0x200, - - 0x201, 0x201, 0x202, 0x203, 0x204, 0x205, 0x206, 0x207, 0x208, 0x209, 0x20a, 0x20b, 0x20c, 0x20d, 0x20e, 0x20f, - 0x210, 0x210, 0x211, 0x212, 0x213, 0x214, 0x215, 0x216, 0x217, 0x218, 0x219, 0x21a, 0x21b, 0x21c, 0x21d, 0x21e, - - 0x21f, 0x220, 0x221, 0x222, 0x223, 0x224, 0x225, 0x226, 0x227, 0x228, 0x229, 0x22a, 0x22b, 0x22c, 0x22d, 0x22e, - 0x22f, 0x230, 0x231, 0x232, 0x233, 0x234, 0x235, 0x236, 0x237, 0x238, 0x239, 0x23a, 0x23b, 0x23c, 0x23d, 0x23e, - - 0x23f, 0x240, 0x241, 0x242, 0x244, 0x245, 0x246, 0x247, 0x248, 0x249, 0x24a, 0x24b, 0x24c, 0x24d, 0x24e, 0x24f, - 0x250, 0x251, 0x252, 0x253, 0x254, 0x256, 0x257, 0x258, 0x259, 0x25a, 0x25b, 0x25c, 0x25d, 0x25e, 0x25f, 0x260, - - 0x262, 0x263, 0x264, 0x265, 0x266, 0x267, 0x268, 0x269, 0x26a, 0x26c, 0x26d, 0x26e, 0x26f, 0x270, 0x271, 0x272, - 0x273, 0x275, 0x276, 0x277, 0x278, 0x279, 0x27a, 0x27b, 0x27d, 0x27e, 0x27f, 0x280, 0x281, 0x282, 0x284, 0x285, - - 0x286, 0x287, 0x288, 0x289, 0x28b, 0x28c, 0x28d, 0x28e, 0x28f, 0x290, 0x292, 0x293, 0x294, 0x295, 0x296, 0x298, - 0x299, 0x29a, 0x29b, 0x29c, 0x29e, 0x29f, 0x2a0, 0x2a1, 0x2a2, 0x2a4, 0x2a5, 0x2a6, 0x2a7, 0x2a9, 0x2aa, 0x2ab, - - 0x2ac, 0x2ae, 0x2af, 0x2b0, 0x2b1, 0x2b2, 0x2b4, 0x2b5, 0x2b6, 0x2b7, 0x2b9, 0x2ba, 0x2bb, 0x2bd, 0x2be, 0x2bf, - 0x2c0, 0x2c2, 0x2c3, 0x2c4, 0x2c5, 0x2c7, 0x2c8, 0x2c9, 0x2cb, 0x2cc, 0x2cd, 0x2ce, 0x2d0, 0x2d1, 0x2d2, 0x2d4, - - 0x2d5, 0x2d6, 0x2d8, 0x2d9, 0x2da, 0x2dc, 0x2dd, 0x2de, 0x2e0, 0x2e1, 0x2e2, 0x2e4, 0x2e5, 0x2e6, 0x2e8, 0x2e9, - 0x2ea, 0x2ec, 0x2ed, 0x2ee, 0x2f0, 0x2f1, 0x2f2, 0x2f4, 0x2f5, 0x2f6, 0x2f8, 0x2f9, 0x2fb, 0x2fc, 0x2fd, 0x2ff, - - 0x300, 0x302, 0x303, 0x304, 0x306, 0x307, 0x309, 0x30a, 0x30b, 0x30d, 0x30e, 0x310, 0x311, 0x312, 0x314, 0x315, - 0x317, 0x318, 0x31a, 0x31b, 0x31c, 0x31e, 0x31f, 0x321, 0x322, 0x324, 0x325, 0x327, 0x328, 0x329, 0x32b, 0x32c, - - 0x32e, 0x32f, 0x331, 0x332, 0x334, 0x335, 0x337, 0x338, 0x33a, 0x33b, 0x33d, 0x33e, 0x340, 0x341, 0x343, 0x344, - 0x346, 0x347, 0x349, 0x34a, 0x34c, 0x34d, 0x34f, 0x350, 0x352, 0x353, 0x355, 0x357, 0x358, 0x35a, 0x35b, 0x35d, - - 0x35e, 0x360, 0x361, 0x363, 0x365, 0x366, 0x368, 0x369, 0x36b, 0x36c, 0x36e, 0x370, 0x371, 0x373, 0x374, 0x376, - 0x378, 0x379, 0x37b, 0x37c, 0x37e, 0x380, 0x381, 0x383, 0x384, 0x386, 0x388, 0x389, 0x38b, 0x38d, 0x38e, 0x390, - - 0x392, 0x393, 0x395, 0x397, 0x398, 0x39a, 0x39c, 0x39d, 0x39f, 0x3a1, 0x3a2, 0x3a4, 0x3a6, 0x3a7, 0x3a9, 0x3ab, - 0x3ac, 0x3ae, 0x3b0, 0x3b1, 0x3b3, 0x3b5, 0x3b7, 0x3b8, 0x3ba, 0x3bc, 0x3bd, 0x3bf, 0x3c1, 0x3c3, 0x3c4, 0x3c6, - - 0x3c8, 0x3ca, 0x3cb, 0x3cd, 0x3cf, 0x3d1, 0x3d2, 0x3d4, 0x3d6, 0x3d8, 0x3da, 0x3db, 0x3dd, 0x3df, 0x3e1, 0x3e3, - 0x3e4, 0x3e6, 0x3e8, 0x3ea, 0x3ec, 0x3ed, 0x3ef, 0x3f1, 0x3f3, 0x3f5, 0x3f6, 0x3f8, 0x3fa, 0x3fc, 0x3fe, 0x36c -}; - -/* -* Write frequency/octave/keyon data to a channel -* [RH] This is totally different from the original MUS library code -* but matches exactly what DMX does. I haven't a clue why there are 284 -* special bytes at the beginning of the table for the first few notes. -* That last byte in the table doesn't look right, either, but that's what -* it really is. -*/ -void OPLio::OPLwriteFreq(uint32_t channel, uint32_t note, uint32_t pitch, uint32_t keyon) -{ - int octave = 0; - int j = (note << 5) + pitch; - - if (j < 0) - { - j = 0; - } - else if (j >= 284) - { - j -= 284; - octave = j / (32*12); - if (octave > 7) - { - octave = 7; - } - j = (j % (32*12)) + 284; - } - int i = frequencies[j] | (octave << 10); - - OPLwriteValue (0xA0, channel, (uint8_t)i); - OPLwriteValue (0xB0, channel, (uint8_t)(i>>8)|(keyon<<5)); -} - -/* -* Adjust volume value (register 0x40) -*/ -inline uint32_t OPLio::OPLconvertVolume(uint32_t data, uint32_t volume) -{ - static uint8_t volumetable[128] = { - 0, 1, 3, 5, 6, 8, 10, 11, - 13, 14, 16, 17, 19, 20, 22, 23, - 25, 26, 27, 29, 30, 32, 33, 34, - 36, 37, 39, 41, 43, 45, 47, 49, - 50, 52, 54, 55, 57, 59, 60, 61, - 63, 64, 66, 67, 68, 69, 71, 72, - 73, 74, 75, 76, 77, 79, 80, 81, - 82, 83, 84, 84, 85, 86, 87, 88, - 89, 90, 91, 92, 92, 93, 94, 95, - 96, 96, 97, 98, 99, 99, 100, 101, - 101, 102, 103, 103, 104, 105, 105, 106, - 107, 107, 108, 109, 109, 110, 110, 111, - 112, 112, 113, 113, 114, 114, 115, 115, - 116, 117, 117, 118, 118, 119, 119, 120, - 120, 121, 121, 122, 122, 123, 123, 123, - 124, 124, 125, 125, 126, 126, 127, 127}; - - return 0x3F - (((0x3F - data) * - (uint32_t)volumetable[volume <= 127 ? volume : 127]) >> 7); - -} - -uint32_t OPLio::OPLpanVolume(uint32_t volume, int pan) -{ - if (pan >= 0) - return volume; - else - return (volume * (pan + 64)) / 64; -} - -/* -* Write volume data to a channel -*/ -void OPLio::OPLwriteVolume(uint32_t channel, struct OPL2instrument *instr, uint32_t volume) -{ - if (instr != 0) - { - OPLwriteChannel(0x40, channel, ((instr->feedback & 1) ? - OPLconvertVolume(instr->level_1, volume) : instr->level_1) | instr->scale_1, - OPLconvertVolume(instr->level_2, volume) | instr->scale_2); - } -} - -/* -* Write pan (balance) data to a channel -*/ -void OPLio::OPLwritePan(uint32_t channel, struct OPL2instrument *instr, int pan) -{ - if (instr != 0) - { - uint8_t bits; - if (pan < -36) bits = 0x10; // left - else if (pan > 36) bits = 0x20; // right - else bits = 0x30; // both - - OPLwriteValue(0xC0, channel, instr->feedback | bits); - - // Set real panning if we're using emulated chips. - int chanper = IsOPL3 ? OPL3CHANNELS : OPL2CHANNELS; - int which = channel / chanper; - if (chips[which] != NULL) - { - // This is the MIDI-recommended pan formula. 0 and 1 are - // both hard left so that 64 can be perfectly center. - // (Note that the 'pan' passed to this function is the - // MIDI pan position, subtracted by 64.) - double level = (pan <= -63) ? 0 : (pan + 64 - 1) / 126.0; - chips[which]->SetPanning(channel % chanper, - (float)cos(HALF_PI * level), (float)sin(HALF_PI * level)); - } - } -} - -/* -* Write an instrument to a channel -* -* Instrument layout: -* -* Operator1 Operator2 Descr. -* data[0] data[7] reg. 0x20 - tremolo/vibrato/sustain/KSR/multi -* data[1] data[8] reg. 0x60 - attack rate/decay rate -* data[2] data[9] reg. 0x80 - sustain level/release rate -* data[3] data[10] reg. 0xE0 - waveform select -* data[4] data[11] reg. 0x40 - key scale level -* data[5] data[12] reg. 0x40 - output level (bottom 6 bits only) -* data[6] reg. 0xC0 - feedback/AM-FM (both operators) -*/ -void OPLio::OPLwriteInstrument(uint32_t channel, struct OPL2instrument *instr) -{ - OPLwriteChannel(0x40, channel, 0x3F, 0x3F); // no volume - OPLwriteChannel(0x20, channel, instr->trem_vibr_1, instr->trem_vibr_2); - OPLwriteChannel(0x60, channel, instr->att_dec_1, instr->att_dec_2); - OPLwriteChannel(0x80, channel, instr->sust_rel_1, instr->sust_rel_2); - OPLwriteChannel(0xE0, channel, instr->wave_1, instr->wave_2); - OPLwriteValue (0xC0, channel, instr->feedback | 0x30); -} - -/* -* Stop all sounds -*/ -void OPLio::OPLshutup(void) -{ - uint32_t i; - - for(i = 0; i < OPLchannels; i++) - { - OPLwriteChannel(0x40, i, 0x3F, 0x3F); // turn off volume - OPLwriteChannel(0x60, i, 0xFF, 0xFF); // the fastest attack, decay - OPLwriteChannel(0x80, i, 0x0F, 0x0F); // ... and release - OPLwriteValue(0xB0, i, 0); // KEY-OFF - } -} - -/* -* Initialize hardware upon startup -*/ -int OPLio::OPLinit(uint32_t numchips, bool stereo, bool initopl3) -{ - assert(numchips >= 1 && numchips <= countof(chips)); - uint32_t i; - IsOPL3 = (current_opl_core == 1 || current_opl_core == 2 || current_opl_core == 3); - - memset(chips, 0, sizeof(chips)); - if (IsOPL3) - { - numchips = (numchips + 1) >> 1; - } - for (i = 0; i < numchips; ++i) - { - OPLEmul *chip = IsOPL3 ? (current_opl_core == 1 ? DBOPLCreate(stereo) : (current_opl_core == 2 ? JavaOPLCreate(stereo) : NukedOPL3Create(stereo))) : YM3812Create(stereo); - if (chip == NULL) - { - break; - } - chips[i] = chip; - } - NumChips = i; - OPLchannels = i * (IsOPL3 ? OPL3CHANNELS : OPL2CHANNELS); - OPLwriteInitState(initopl3); - return i; -} - -void OPLio::OPLwriteInitState(bool initopl3) -{ - for (uint32_t i = 0; i < NumChips; ++i) - { - int chip = i << (int)IsOPL3; - if (IsOPL3 && initopl3) - { - OPLwriteReg(chip, 0x105, 0x01); // enable YMF262/OPL3 mode - OPLwriteReg(chip, 0x104, 0x00); // disable 4-operator mode - } - OPLwriteReg(chip, 0x01, 0x20); // enable Waveform Select - OPLwriteReg(chip, 0x0B, 0x40); // turn off CSW mode - OPLwriteReg(chip, 0xBD, 0x00); // set vibrato/tremolo depth to low, set melodic mode - } - OPLshutup(); -} - -/* -* Deinitialize hardware before shutdown -*/ -void OPLio::OPLdeinit(void) -{ - for (size_t i = 0; i < countof(chips); ++i) - { - if (chips[i] != NULL) - { - delete chips[i]; - chips[i] = NULL; - } - } -} diff --git a/src/sound/oplsynth/musicblock.cpp b/src/sound/oplsynth/musicblock.cpp new file mode 100644 index 000000000..e0a231476 --- /dev/null +++ b/src/sound/oplsynth/musicblock.cpp @@ -0,0 +1,454 @@ +#include +#include +#include "muslib.h" +#include "files.h" +#include "templates.h" + +#include "c_cvars.h" + +musicBlock::musicBlock () +{ + memset (this, 0, sizeof(*this)); + for(auto &voice : voices) voice.index = -1; // mark all free. +} + +musicBlock::~musicBlock () +{ +} + +//---------------------------------------------------------------------------- +// +// +// +//---------------------------------------------------------------------------- + +int musicBlock::releaseVoice(uint32_t slot, uint32_t killed) +{ + struct OPLVoice *ch = &voices[slot]; + io->WriteFrequency(slot, ch->note, ch->pitch, 0); + ch->index = -1; + if (killed) io->MuteChannel(slot); + return slot; +} + +//---------------------------------------------------------------------------- +// +// +// +//---------------------------------------------------------------------------- + +int musicBlock::findFreeVoice() +{ + for (uint32_t i = 0; i < io->NumChannels; ++i) + { + if (voices[i].index == -1) + { + releaseVoice(i, 1); + return i; + } + } + return -1; +} + +//---------------------------------------------------------------------------- +// +// When all voices are in use, we must discard an existing voice to +// play a new note. Find and free an existing voice. The channel +// passed to the function is the channel for the new note to be +// played. +// +//---------------------------------------------------------------------------- + +int musicBlock::replaceExistingVoice() +{ + // Check the allocated voices, if we find an instrument that is + // of a lower priority to the new instrument, discard it. + // If a voice is being used to play the second voice of an instrument, + // use that, as second voices are non-essential. + // Lower numbered MIDI channels implicitly have a higher priority + // than higher-numbered channels, eg. MIDI channel 1 is never + // discarded for MIDI channel 2. + + int result = 0; + + for (uint32_t i = 0; i < io->NumChannels; ++i) + { + if (voices[i].current_instr_voice == &voices[i].current_instr->voices[1] || + voices[i].index >= voices[result].index) + { + result = i; + } + } + + releaseVoice(result, 1); + return result; +} + +//---------------------------------------------------------------------------- +// +// +// +//---------------------------------------------------------------------------- + +void musicBlock::voiceKeyOn(uint32_t slot, uint32_t channo, genmidi_instr_t *instrument, uint32_t instrument_voice, uint32_t key, uint32_t volume) +{ + struct OPLVoice *voice = &voices[slot]; + auto &channel = oplchannels[channo]; + genmidi_voice_t *gmvoice; + + voice->index = channo; + voice->key = key; + + // Program the voice with the instrument data: + voice->current_instr = instrument; + gmvoice = voice->current_instr_voice = &instrument->voices[instrument_voice]; + io->WriteInstrument(slot,gmvoice, channel.Vibrato); + io->WritePan(slot, gmvoice, channel.Panning); + + // Set the volume level. + voice->note_volume = volume; + io->WriteVolume(slot, gmvoice, channel.Volume, channel.Expression, volume); + + // Write the frequency value to turn the note on. + + // Work out the note to use. This is normally the same as + // the key, unless it is a fixed pitch instrument. + uint32_t note; + if (instrument->flags & GENMIDI_FLAG_FIXED) note = instrument->fixed_note; + else if (channo == CHAN_PERCUSSION) note = 60; + else note = key; + + // If this is the second voice of a double voice instrument, the + // frequency index can be adjusted by the fine tuning field. + voice->fine_tuning = (instrument_voice != 0) ? (voice->current_instr->fine_tuning / 2) - 64 : 0; + voice->pitch = voice->fine_tuning + channel.Pitch; + + if (!(instrument->flags & GENMIDI_FLAG_FIXED) && channo != CHAN_PERCUSSION) + { + note += gmvoice->base_note_offset; + } + + // Avoid possible overflow due to base note offset: + + while (note < 0) + { + note += 12; + } + + while (note > HIGHEST_NOTE) + { + note -= 12; + } + voice->note = note; + io->WriteFrequency(slot, note, voice->pitch, 1); +} + +//---------------------------------------------------------------------------- +// +// +// +//---------------------------------------------------------------------------- + +CVAR(Bool, opl_singlevoice, 0, 0) + +void musicBlock::noteOn(uint32_t channel, uint8_t key, int volume) +{ + if (volume <= 0) + { + noteOff(channel, key); + return; + } + uint32_t note; + genmidi_instr_t *instrument; + + // Percussion channel is treated differently. + if (channel == CHAN_PERCUSSION) + { + if (key < GENMIDI_FIST_PERCUSSION || key >= GENMIDI_FIST_PERCUSSION + GENMIDI_NUM_PERCUSSION) + { + return; + } + + instrument = &OPLinstruments[key + (GENMIDI_NUM_INSTRS - GENMIDI_FIST_PERCUSSION)]; + } + else + { + auto inst = oplchannels[channel].Instrument; + if (inst >= GENMIDI_NUM_TOTAL) return; // better safe than sorry. + instrument = &OPLinstruments[inst]; + } + + bool double_voice = ((instrument->flags) & GENMIDI_FLAG_2VOICE) && !opl_singlevoice; + + int i = findFreeVoice(); + if (i < 0) i = replaceExistingVoice(); + + if (i >= 0) + { + voiceKeyOn(i, channel, instrument, 0, key, volume); + if (double_voice) + { + i = findFreeVoice(); + if (i > 0) + { + voiceKeyOn(i, channel, instrument, 1, key, volume); + } + } + } +} + +//---------------------------------------------------------------------------- +// +// +// +//---------------------------------------------------------------------------- + +void musicBlock::noteOff(uint32_t id, uint8_t note) +{ + uint32_t sustain = oplchannels[id].Sustain; + + for(uint32_t i = 0; i < io->NumChannels; i++) + { + if (voices[i].index == id && voices[i].key == note) + { + if (sustain >= MIN_SUSTAIN) voices[i].sustained = true; + else releaseVoice(i, 0); + } + } +} + +//---------------------------------------------------------------------------- +// +// +// +//---------------------------------------------------------------------------- + +void musicBlock::changePitch(uint32_t id, int val1, int val2) +{ + // Convert pitch from 14-bit to 7-bit, then scale it, since the player + // code only understands sensitivities of 2 semitones. + int pitch = ((val1 | (val2 << 7)) - 8192) * oplchannels[id].PitchSensitivity / (200 * 128) + 64; + oplchannels[id].Pitch = pitch; + for(uint32_t i = 0; i < io->NumChannels; i++) + { + auto &ch = voices[i]; + if (ch.index == id) + { + ch.pitch = ch.fine_tuning + pitch; + io->WriteFrequency(i, ch.note, ch.pitch, 1); + } + } +} + +//---------------------------------------------------------------------------- +// +// +// +//---------------------------------------------------------------------------- + +void musicBlock::changeModulation(uint32_t id, int value) +{ + bool vibrato = (value >= VIBRATO_THRESHOLD); + oplchannels[id].Vibrato = vibrato; + for (uint32_t i = 0; i < io->NumChannels; i++) + { + auto &ch = voices[i]; + if (ch.index == id) + { + io->WriteTremolo(i, ch.current_instr_voice, vibrato); + } + } +} + +//---------------------------------------------------------------------------- +// +// +// +//---------------------------------------------------------------------------- + +void musicBlock::changeSustain(uint32_t id, int value) +{ + oplchannels[id].Sustain = value; + if (value < MIN_SUSTAIN) + { + for (uint32_t i = 0; i < io->NumChannels; i++) + { + if (voices[i].index == id && voices[i].sustained) + releaseVoice(i, 0); + } + } +} + +//---------------------------------------------------------------------------- +// +// Change volume or expression. +// Since both go to the same register, one function can handle both. +// +//---------------------------------------------------------------------------- + +void musicBlock::changeVolume(uint32_t id, int value, bool expression) +{ + auto &chan = oplchannels[id]; + if (!expression) chan.Volume = value; + else chan.Expression = value; + for (uint32_t i = 0; i < io->NumChannels; i++) + { + auto &ch = voices[i]; + if (ch.index == id) + { + io->WriteVolume(i, ch.current_instr_voice, chan.Volume, chan.Expression, ch.note_volume); + } + } +} + +//---------------------------------------------------------------------------- +// +// +// +//---------------------------------------------------------------------------- + +void musicBlock::changePanning(uint32_t id, int value) +{ + oplchannels[id].Panning = value; + for(uint32_t i = 0; i < io->NumChannels; i++) + { + auto &ch = voices[i]; + if (ch.index == id) + { + io->WritePan(i, ch.current_instr_voice, value); + } + } +} + +//---------------------------------------------------------------------------- +// +// +// +//---------------------------------------------------------------------------- + +void musicBlock::notesOff(uint32_t id, int value) +{ + for (uint32_t i = 0; i < io->NumChannels; ++i) + { + if (voices[i].index == id) + { + if (oplchannels[id].Sustain >= MIN_SUSTAIN) voices[i].sustained = true; + else releaseVoice(i, 0); + } + } +} + +//---------------------------------------------------------------------------- +// +// release all notes for this channel +// +//---------------------------------------------------------------------------- + +void musicBlock::allNotesOff(uint32_t id, int value) +{ + for (uint32_t i = 0; i < io->NumChannels; ++i) + { + if (voices[i].index == id) + { + releaseVoice(i, 0); + } + } +} + +//---------------------------------------------------------------------------- +// +// +// +//---------------------------------------------------------------------------- + +void musicBlock::changeExtended(uint32_t id, uint8_t controller, int value) +{ + switch (controller) + { + case ctrlRPNHi: + oplchannels[id].RPN = (oplchannels[id].RPN & 0x007F) | (value << 7); + break; + + case ctrlRPNLo: + oplchannels[id].RPN = (oplchannels[id].RPN & 0x3F80) | value; + break; + + case ctrlNRPNLo: + case ctrlNRPNHi: + oplchannels[id].RPN = 0x3FFF; + break; + + case ctrlDataEntryHi: + if (oplchannels[id].RPN == 0) + { + oplchannels[id].PitchSensitivity = value * 100 + (oplchannels[id].PitchSensitivity % 100); + } + break; + + case ctrlDataEntryLo: + if (oplchannels[id].RPN == 0) + { + oplchannels[id].PitchSensitivity = value + (oplchannels[id].PitchSensitivity / 100) * 100; + } + break; + } +} + +//---------------------------------------------------------------------------- +// +// +// +//---------------------------------------------------------------------------- + +void musicBlock::resetControllers(uint32_t chan, int vol) +{ + auto &channel = oplchannels[chan]; + + channel.Volume = vol; + channel.Expression = 127; + channel.Sustain = 0; + channel.Pitch = 64; + channel.RPN = 0x3fff; + channel.PitchSensitivity = 200; +} + +//---------------------------------------------------------------------------- +// +// +// +//---------------------------------------------------------------------------- + +void musicBlock::programChange(uint32_t channel, int value) +{ + oplchannels[channel].Instrument = value; +} + +//---------------------------------------------------------------------------- +// +// +// +//---------------------------------------------------------------------------- + +void musicBlock::resetAllControllers(int vol) +{ + uint32_t i; + + for (i = 0; i < NUM_CHANNELS; i++) + { + resetControllers(i, vol); + } +} + +//---------------------------------------------------------------------------- +// +// +// +//---------------------------------------------------------------------------- + +void musicBlock::stopAllVoices() +{ + for (uint32_t i = 0; i < io->NumChannels; i++) + { + if (voices[i].index >= 0) releaseVoice(i, 1); + } +} diff --git a/src/sound/oplsynth/musicblock.h b/src/sound/oplsynth/musicblock.h new file mode 100644 index 000000000..1f13aa01f --- /dev/null +++ b/src/sound/oplsynth/musicblock.h @@ -0,0 +1,58 @@ +#pragma once +#include "doomtype.h" +#include "genmidi.h" +#include "oplio.h" + + +struct OPLVoice +{ + int index; // Index of this voice, or -1 if not in use. + unsigned int key; // The midi key that this voice is playing. + unsigned int note; // The note being played. This is normally the same as the key, but if the instrument is a fixed pitch instrument, it is different. + unsigned int note_volume; // The volume of the note being played on this channel. + genmidi_instr_t *current_instr; // Currently-loaded instrument data + genmidi_voice_t *current_instr_voice;// The voice number in the instrument to use. This is normally set to the instrument's first voice; if this is a double voice instrument, it may be the second one + bool sustained; + int8_t fine_tuning; + int pitch; +}; + +struct musicBlock { + musicBlock(); + ~musicBlock(); + + uint8_t *score; + uint8_t *scoredata; + int playingcount; + OPLChannel oplchannels[NUM_CHANNELS]; + OPLio *io; + + struct genmidi_instr_t OPLinstruments[GENMIDI_NUM_TOTAL]; + + void changeModulation(uint32_t id, int value); + void changeSustain(uint32_t id, int value); + void changeVolume(uint32_t id, int value, bool expression); + void changePanning(uint32_t id, int value); + void notesOff(uint32_t id, int value); + void allNotesOff(uint32_t id, int value); + void changeExtended(uint32_t channel, uint8_t controller, int value); + void resetControllers(uint32_t channel, int vol); + void programChange(uint32_t channel, int value); + void resetAllControllers(int vol); + void changePitch(uint32_t channel, int val1, int val2); + + void noteOn(uint32_t channel, uint8_t note, int volume); + void noteOff(uint32_t channel, uint8_t note); + void stopAllVoices(); + +protected: + OPLVoice voices[NUM_VOICES]; + + int findFreeVoice(); + int replaceExistingVoice(); + void voiceKeyOn(uint32_t slot, uint32_t channo, genmidi_instr_t *instrument, uint32_t instrument_voice, uint32_t, uint32_t volume); + int releaseVoice(uint32_t slot, uint32_t killed); + + friend class Stat_opl; + +}; diff --git a/src/sound/oplsynth/muslib.h b/src/sound/oplsynth/muslib.h deleted file mode 100644 index 9d5bdfcbd..000000000 --- a/src/sound/oplsynth/muslib.h +++ /dev/null @@ -1,267 +0,0 @@ -/* - * Name: Main header include file - * Project: MUS File Player Library - * Version: 1.75 - * Author: Vladimir Arnost (QA-Software) - * Last revision: Mar-9-1996 - * Compiler: Borland C++ 3.1, Watcom C/C++ 10.0 - * - */ - -/* From muslib175.zip/README.1ST: - -1.1 - Disclaimer of Warranties ------------------------------- - -#ifdef LAWYER - -THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE -FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS -OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) -HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT -LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY -OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF -SUCH DAMAGE. - -#else - -Use this software at your own risk. - -#endif - - -1.2 - Terms of Use ------------------- - -This library may be used in any freeware or shareware product free of -charge. The product may not be sold for profit (except for shareware) and -should be freely available to the public. It would be nice of you if you -credited me in your product and notified me if you use this library. - -If you want to use this library in a commercial product, contact me -and we will make an agreement. It is a violation of the law to make money -of this product without prior signing an agreement and paying a license fee. -This licence will allow its holder to sell any products based on MUSLib, -royalty-free. There is no need to buy separate licences for different -products once the licence fee is paid. - - -1.3 - Contacting the Author ---------------------------- - -Internet (address valid probably until the end of year 1998): - xarnos00@dcse.fee.vutbr.cz - -FIDO: - 2:423/36.2 - -Snail-mail: - - Vladimir Arnost - Ceska 921 - Chrudim 4 - 537 01 - CZECH REPUBLIC - -Voice-mail (Czech language only, not recommended; weekends only): - - +42-455-2154 -*/ - -#ifndef __MUSLIB_H_ -#define __MUSLIB_H_ - -#ifndef __DEFTYPES_H_ - #include "doomtype.h" -#endif - -class FileReader; - -/* Global Definitions */ - -#define CHANNELS 16 // total channels 0..CHANNELS-1 -#define PERCUSSION 15 // percussion channel - -/* OPL2 instrument */ -struct OPL2instrument { -/*00*/ uint8_t trem_vibr_1; /* OP 1: tremolo/vibrato/sustain/KSR/multi */ -/*01*/ uint8_t att_dec_1; /* OP 1: attack rate/decay rate */ -/*02*/ uint8_t sust_rel_1; /* OP 1: sustain level/release rate */ -/*03*/ uint8_t wave_1; /* OP 1: waveform select */ -/*04*/ uint8_t scale_1; /* OP 1: key scale level */ -/*05*/ uint8_t level_1; /* OP 1: output level */ -/*06*/ uint8_t feedback; /* feedback/AM-FM (both operators) */ -/*07*/ uint8_t trem_vibr_2; /* OP 2: tremolo/vibrato/sustain/KSR/multi */ -/*08*/ uint8_t att_dec_2; /* OP 2: attack rate/decay rate */ -/*09*/ uint8_t sust_rel_2; /* OP 2: sustain level/release rate */ -/*0A*/ uint8_t wave_2; /* OP 2: waveform select */ -/*0B*/ uint8_t scale_2; /* OP 2: key scale level */ -/*0C*/ uint8_t level_2; /* OP 2: output level */ -/*0D*/ uint8_t unused; -/*0E*/ int16_t basenote; /* base note offset */ -}; - -/* OP2 instrument file entry */ -struct OP2instrEntry { -/*00*/ uint16_t flags; // see FL_xxx below -/*02*/ uint8_t finetune; // finetune value for 2-voice sounds -/*03*/ uint8_t note; // note # for fixed instruments -/*04*/ struct OPL2instrument instr[2]; // instruments -}; - -#define FL_FIXED_PITCH 0x0001 // note has fixed pitch (see below) -#define FL_DOUBLE_VOICE 0x0004 // use two voices instead of one - - -#define OP2INSTRSIZE sizeof(struct OP2instrEntry) // instrument size (36 bytes) -#define OP2INSTRCOUNT (128 + 81-35+1) // instrument count - -/* From MLOPL_IO.CPP */ -#define OPL2CHANNELS 9 -#define OPL3CHANNELS 18 -#define MAXOPL2CHIPS 8 -#define MAXCHANNELS (OPL2CHANNELS * MAXOPL2CHIPS) - - -/* Channel Flags: */ -#define CH_SECONDARY 0x01 -#define CH_SUSTAIN 0x02 -#define CH_VIBRATO 0x04 /* set if modulation >= MOD_MIN */ -#define CH_FREE 0x80 - -struct OPLdata { - uint32_t channelInstr[CHANNELS]; // instrument # - uint8_t channelVolume[CHANNELS]; // volume - uint8_t channelLastVolume[CHANNELS]; // last volume - int8_t channelPan[CHANNELS]; // pan, 0=normal - int8_t channelPitch[CHANNELS]; // pitch wheel, 64=normal - uint8_t channelSustain[CHANNELS]; // sustain pedal value - uint8_t channelModulation[CHANNELS]; // modulation pot value - uint16_t channelPitchSens[CHANNELS]; // pitch sensitivity, 2=default - uint16_t channelRPN[CHANNELS]; // RPN number for data entry - uint8_t channelExpression[CHANNELS]; // expression -}; - -struct OPLio { - virtual ~OPLio(); - - void OPLwriteChannel(uint32_t regbase, uint32_t channel, uint8_t data1, uint8_t data2); - void OPLwriteValue(uint32_t regbase, uint32_t channel, uint8_t value); - void OPLwriteFreq(uint32_t channel, uint32_t freq, uint32_t octave, uint32_t keyon); - uint32_t OPLconvertVolume(uint32_t data, uint32_t volume); - uint32_t OPLpanVolume(uint32_t volume, int pan); - void OPLwriteVolume(uint32_t channel, struct OPL2instrument *instr, uint32_t volume); - void OPLwritePan(uint32_t channel, struct OPL2instrument *instr, int pan); - void OPLwriteInstrument(uint32_t channel, struct OPL2instrument *instr); - void OPLshutup(void); - void OPLwriteInitState(bool initopl3); - - virtual int OPLinit(uint32_t numchips, bool stereo=false, bool initopl3=false); - virtual void OPLdeinit(void); - virtual void OPLwriteReg(int which, uint32_t reg, uint8_t data); - virtual void SetClockRate(double samples_per_tick); - virtual void WriteDelay(int ticks); - - class OPLEmul *chips[MAXOPL2CHIPS]; - uint32_t OPLchannels; - uint32_t NumChips; - bool IsOPL3; -}; - -struct DiskWriterIO : public OPLio -{ - DiskWriterIO(const char *filename); - ~DiskWriterIO(); - - int OPLinit(uint32_t numchips, bool notused, bool initopl3); - void SetClockRate(double samples_per_tick); - void WriteDelay(int ticks); - - FString Filename; -}; - -struct musicBlock { - musicBlock(); - ~musicBlock(); - - uint8_t *score; - uint8_t *scoredata; - int playingcount; - OPLdata driverdata; - OPLio *io; - - struct OP2instrEntry *OPLinstruments; - - uint32_t MLtime; - - void OPLplayNote(uint32_t channel, uint8_t note, int volume); - void OPLreleaseNote(uint32_t channel, uint8_t note); - void OPLpitchWheel(uint32_t channel, int pitch); - void OPLchangeControl(uint32_t channel, uint8_t controller, int value); - void OPLprogramChange(uint32_t channel, int value); - void OPLresetControllers(uint32_t channel, int vol); - void OPLplayMusic(int vol); - void OPLstopMusic(); - - int OPLloadBank (FileReader &data); - -protected: - /* OPL channel (voice) data */ - struct channelEntry { - uint8_t channel; /* MUS channel number */ - uint8_t note; /* note number */ - uint8_t flags; /* see CH_xxx below */ - uint8_t realnote; /* adjusted note number */ - int8_t finetune; /* frequency fine-tune */ - int pitch; /* pitch-wheel value */ - uint32_t volume; /* note volume */ - uint32_t realvolume; /* adjusted note volume */ - struct OPL2instrument *instr; /* current instrument */ - uint32_t time; /* note start time */ - } channels[MAXCHANNELS]; - - void writeFrequency(uint32_t slot, uint32_t note, int pitch, uint32_t keyOn); - void writeModulation(uint32_t slot, struct OPL2instrument *instr, int state); - uint32_t calcVolume(uint32_t channelVolume, uint32_t channelExpression, uint32_t noteVolume); - int occupyChannel(uint32_t slot, uint32_t channel, - int note, int volume, struct OP2instrEntry *instrument, uint8_t secondary); - int releaseChannel(uint32_t slot, uint32_t killed); - int releaseSustain(uint32_t channel); - int findFreeChannel(uint32_t flag, uint32_t channel, uint8_t note); - struct OP2instrEntry *getInstrument(uint32_t channel, uint8_t note); - - friend class Stat_opl; - -}; - -enum MUSctrl { - ctrlPatch = 0, - ctrlBank, - ctrlModulation, - ctrlVolume, - ctrlPan, - ctrlExpression, - ctrlReverb, - ctrlChorus, - ctrlSustainPedal, - ctrlSoftPedal, - ctrlRPNHi, - ctrlRPNLo, - ctrlNRPNHi, - ctrlNRPNLo, - ctrlDataEntryHi, - ctrlDataEntryLo, - - ctrlSoundsOff, - ctrlNotesOff, - ctrlMono, - ctrlPoly, -}; - -#define ADLIB_CLOCK_MUL 24.0 - -#endif // __MUSLIB_H_ diff --git a/src/sound/oplsynth/nukedopl3.h b/src/sound/oplsynth/nukedopl3.h index 17ebbe4d2..3c3c65895 100644 --- a/src/sound/oplsynth/nukedopl3.h +++ b/src/sound/oplsynth/nukedopl3.h @@ -30,7 +30,7 @@ //version 1.6 #include "opl.h" -#include "muslib.h" +#include "musicblock.h" typedef uintptr_t Bitu; typedef intptr_t Bits; diff --git a/src/sound/oplsynth/opl.h b/src/sound/oplsynth/opl.h index 1a3842fd3..d5faa136c 100644 --- a/src/sound/oplsynth/opl.h +++ b/src/sound/oplsynth/opl.h @@ -24,6 +24,8 @@ OPLEmul *NukedOPL3Create(bool stereo); #define OPL_SAMPLE_RATE 49716.0 #define CENTER_PANNING_POWER 0.70710678118 /* [RH] volume at center for EQP */ +#define ADLIB_CLOCK_MUL 24.0 + #endif \ No newline at end of file diff --git a/src/sound/oplsynth/opl_mus_player.cpp b/src/sound/oplsynth/opl_mus_player.cpp index 55dd132ce..4829f9cfa 100644 --- a/src/sound/oplsynth/opl_mus_player.cpp +++ b/src/sound/oplsynth/opl_mus_player.cpp @@ -38,16 +38,15 @@ OPLmusicBlock::~OPLmusicBlock() void OPLmusicBlock::ResetChips () { ChipAccess.Enter(); - io->OPLdeinit (); - NumChips = io->OPLinit(MIN(*opl_numchips, 2), FullPan); + io->Reset (); + NumChips = io->Init(MIN(*opl_numchips, 2), FullPan); ChipAccess.Leave(); } void OPLmusicBlock::Restart() { - OPLstopMusic (); - OPLplayMusic (127); - MLtime = 0; + stopAllVoices (); + resetAllControllers (127); playingcount = 0; LastOffset = 0; } @@ -69,7 +68,7 @@ fail: delete[] scoredata; return; } - if (0 == (NumChips = io->OPLinit(NumChips))) + if (0 == (NumChips = io->Init(NumChips))) { goto fail; } @@ -161,7 +160,7 @@ OPLmusicFile::~OPLmusicFile () { if (scoredata != NULL) { - io->OPLdeinit (); + io->Reset (); delete[] scoredata; scoredata = NULL; } @@ -280,7 +279,6 @@ bool OPLmusicBlock::ServiceStream (void *buff, int numbytes) io->WriteDelay(next); NextTickIn += SamplesPerTick * next; assert (NextTickIn >= 0); - MLtime += next; } } } @@ -407,7 +405,7 @@ int OPLmusicFile::PlayTick () break; default: // It's something to stuff into the OPL chip - io->OPLwriteReg(WhichChip, reg, data); + io->WriteRegister(WhichChip, reg, data); break; } } @@ -447,7 +445,7 @@ int OPLmusicFile::PlayTick () { data = *score++; } - io->OPLwriteReg(WhichChip, reg, data); + io->WriteRegister(WhichChip, reg, data); } break; @@ -477,7 +475,7 @@ int OPLmusicFile::PlayTick () } else if (code < to_reg_size) { - io->OPLwriteReg(which, to_reg[code], data); + io->WriteRegister(which, to_reg[code], data); } } } @@ -495,7 +493,7 @@ int OPLmusicFile::PlayTick () data = score[1]; delay = LittleShort(((uint16_t *)score)[1]); score += 4; - io->OPLwriteReg (0, reg, data); + io->WriteRegister (0, reg, data); } return delay; } @@ -524,7 +522,7 @@ OPLmusicFile::OPLmusicFile(const OPLmusicFile *source, const char *filename) delete io; } io = new DiskWriterIO(filename); - NumChips = io->OPLinit(NumChips); + NumChips = io->Init(NumChips); Restart(); } diff --git a/src/sound/oplsynth/opl_mus_player.h b/src/sound/oplsynth/opl_mus_player.h index bc76e0682..a8ace69d2 100644 --- a/src/sound/oplsynth/opl_mus_player.h +++ b/src/sound/oplsynth/opl_mus_player.h @@ -1,6 +1,8 @@ #include "critsec.h" #include "muslib.h" +class FileReader; + class OPLmusicBlock : public musicBlock { public: diff --git a/src/sound/oplsynth/oplio.cpp b/src/sound/oplsynth/oplio.cpp new file mode 100644 index 000000000..68b769db4 --- /dev/null +++ b/src/sound/oplsynth/oplio.cpp @@ -0,0 +1,493 @@ +/* +** oplio.cpp +** low level OPL code +** +**--------------------------------------------------------------------------- +** Copyright 1998-2008 Randy Heit +** All rights reserved. +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1. Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** 2. Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in the +** documentation and/or other materials provided with the distribution. +** 3. The name of the author may not be used to endorse or promote products +** derived from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +**--------------------------------------------------------------------------- +*/ + +#include "genmidi.h" +#include "oplio.h" +#include "opl.h" +#include "c_cvars.h" +#include "templates.h" + +const double HALF_PI = (M_PI*0.5); + +EXTERN_CVAR(Int, opl_core) +extern int current_opl_core; + +OPLio::~OPLio() +{ +} + +void OPLio::SetClockRate(double samples_per_tick) +{ +} + +void OPLio::WriteDelay(int ticks) +{ +} + +//---------------------------------------------------------------------------- +// +// Initialize OPL emulator +// +//---------------------------------------------------------------------------- + +int OPLio::Init(uint32_t numchips, bool stereo, bool initopl3) +{ + assert(numchips >= 1 && numchips <= countof(chips)); + uint32_t i; + IsOPL3 = (current_opl_core == 1 || current_opl_core == 2 || current_opl_core == 3); + + memset(chips, 0, sizeof(chips)); + if (IsOPL3) + { + numchips = (numchips + 1) >> 1; + } + for (i = 0; i < numchips; ++i) + { + OPLEmul *chip = IsOPL3 ? (current_opl_core == 1 ? DBOPLCreate(stereo) : (current_opl_core == 2 ? JavaOPLCreate(stereo) : NukedOPL3Create(stereo))) : YM3812Create(stereo); + if (chip == NULL) + { + break; + } + chips[i] = chip; + } + NumChips = i; + NumChannels = i * (IsOPL3 ? OPL3_NUM_VOICES : OPL_NUM_VOICES); + WriteInitState(initopl3); + return i; +} + +//---------------------------------------------------------------------------- +// +// +// +//---------------------------------------------------------------------------- + +void OPLio::WriteInitState(bool initopl3) +{ + for (uint32_t k = 0; k < NumChips; ++k) + { + int chip = k << (int)IsOPL3; + if (IsOPL3 && initopl3) + { + WriteRegister(chip, OPL_REG_OPL3_ENABLE, 1); + WriteRegister(chip, OPL_REG_4OPMODE_ENABLE, 0); + } + WriteRegister(chip, OPL_REG_WAVEFORM_ENABLE, WAVEFORM_ENABLED); + WriteRegister(chip, OPL_REG_PERCUSSION_MODE, 0); // should be the default, but cannot verify for some of the cores. + } + + // Reset all channels. + for (uint32_t k = 0; k < NumChannels; k++) + { + MuteChannel(k); + WriteValue(OPL_REGS_FREQ_2, k, 0); + } +} + + +//---------------------------------------------------------------------------- +// +// Deinitialize emulator before shutdown +// +//---------------------------------------------------------------------------- + +void OPLio::Reset(void) +{ + for (size_t i = 0; i < countof(chips); ++i) + { + if (chips[i] != NULL) + { + delete chips[i]; + chips[i] = NULL; + } + } +} + +//---------------------------------------------------------------------------- +// +// +// +//---------------------------------------------------------------------------- + +void OPLio::WriteRegister(int chipnum, uint32_t reg, uint8_t data) +{ + if (IsOPL3) + { + reg |= (chipnum & 1) << 8; + chipnum >>= 1; + } + if (chips[chipnum] != nullptr) + { + chips[chipnum]->WriteReg(reg, data); + } +} + +//---------------------------------------------------------------------------- +// +// +// +//---------------------------------------------------------------------------- + +void OPLio::WriteValue(uint32_t regbase, uint32_t channel, uint8_t value) +{ + WriteRegister (channel / OPL_NUM_VOICES, regbase + (channel % OPL_NUM_VOICES), value); +} + +//---------------------------------------------------------------------------- +// +// +// +//---------------------------------------------------------------------------- + +static const int voice_operators[OPL_NUM_VOICES] = { 0x00, 0x01, 0x02, 0x08, 0x09, 0x0a, 0x10, 0x11, 0x12 }; + +void OPLio::WriteOperator(uint32_t regbase, uint32_t channel, int index, uint8_t data2) +{ + WriteRegister(channel / OPL_NUM_VOICES, regbase + voice_operators[channel % OPL_NUM_VOICES] + 3*index, data2); +} + +//---------------------------------------------------------------------------- +// +// Write frequency/octave/keyon data to a channel +// +// [RH] This is totally different from the original MUS library code +// but matches exactly what DMX does. I haven't a clue why there are 284 +// special bytes at the beginning of the table for the first few notes. +// That last byte in the table doesn't look right, either, but that's what +// it really is. +// +//---------------------------------------------------------------------------- + +static const uint16_t frequencies[] = { // (this is the table from Chocolate Doom, which contains the same values as ZDoom's original one but is better formatted. + + 0x133, 0x133, 0x134, 0x134, 0x135, 0x136, 0x136, 0x137, // -1 + 0x137, 0x138, 0x138, 0x139, 0x139, 0x13a, 0x13b, 0x13b, + 0x13c, 0x13c, 0x13d, 0x13d, 0x13e, 0x13f, 0x13f, 0x140, + 0x140, 0x141, 0x142, 0x142, 0x143, 0x143, 0x144, 0x144, + + 0x145, 0x146, 0x146, 0x147, 0x147, 0x148, 0x149, 0x149, // -2 + 0x14a, 0x14a, 0x14b, 0x14c, 0x14c, 0x14d, 0x14d, 0x14e, + 0x14f, 0x14f, 0x150, 0x150, 0x151, 0x152, 0x152, 0x153, + 0x153, 0x154, 0x155, 0x155, 0x156, 0x157, 0x157, 0x158, + + // These are used for the first seven MIDI note values: + + 0x158, 0x159, 0x15a, 0x15a, 0x15b, 0x15b, 0x15c, 0x15d, // 0 + 0x15d, 0x15e, 0x15f, 0x15f, 0x160, 0x161, 0x161, 0x162, + 0x162, 0x163, 0x164, 0x164, 0x165, 0x166, 0x166, 0x167, + 0x168, 0x168, 0x169, 0x16a, 0x16a, 0x16b, 0x16c, 0x16c, + + 0x16d, 0x16e, 0x16e, 0x16f, 0x170, 0x170, 0x171, 0x172, // 1 + 0x172, 0x173, 0x174, 0x174, 0x175, 0x176, 0x176, 0x177, + 0x178, 0x178, 0x179, 0x17a, 0x17a, 0x17b, 0x17c, 0x17c, + 0x17d, 0x17e, 0x17e, 0x17f, 0x180, 0x181, 0x181, 0x182, + + 0x183, 0x183, 0x184, 0x185, 0x185, 0x186, 0x187, 0x188, // 2 + 0x188, 0x189, 0x18a, 0x18a, 0x18b, 0x18c, 0x18d, 0x18d, + 0x18e, 0x18f, 0x18f, 0x190, 0x191, 0x192, 0x192, 0x193, + 0x194, 0x194, 0x195, 0x196, 0x197, 0x197, 0x198, 0x199, + + 0x19a, 0x19a, 0x19b, 0x19c, 0x19d, 0x19d, 0x19e, 0x19f, // 3 + 0x1a0, 0x1a0, 0x1a1, 0x1a2, 0x1a3, 0x1a3, 0x1a4, 0x1a5, + 0x1a6, 0x1a6, 0x1a7, 0x1a8, 0x1a9, 0x1a9, 0x1aa, 0x1ab, + 0x1ac, 0x1ad, 0x1ad, 0x1ae, 0x1af, 0x1b0, 0x1b0, 0x1b1, + + 0x1b2, 0x1b3, 0x1b4, 0x1b4, 0x1b5, 0x1b6, 0x1b7, 0x1b8, // 4 + 0x1b8, 0x1b9, 0x1ba, 0x1bb, 0x1bc, 0x1bc, 0x1bd, 0x1be, + 0x1bf, 0x1c0, 0x1c0, 0x1c1, 0x1c2, 0x1c3, 0x1c4, 0x1c4, + 0x1c5, 0x1c6, 0x1c7, 0x1c8, 0x1c9, 0x1c9, 0x1ca, 0x1cb, + + 0x1cc, 0x1cd, 0x1ce, 0x1ce, 0x1cf, 0x1d0, 0x1d1, 0x1d2, // 5 + 0x1d3, 0x1d3, 0x1d4, 0x1d5, 0x1d6, 0x1d7, 0x1d8, 0x1d8, + 0x1d9, 0x1da, 0x1db, 0x1dc, 0x1dd, 0x1de, 0x1de, 0x1df, + 0x1e0, 0x1e1, 0x1e2, 0x1e3, 0x1e4, 0x1e5, 0x1e5, 0x1e6, + + 0x1e7, 0x1e8, 0x1e9, 0x1ea, 0x1eb, 0x1ec, 0x1ed, 0x1ed, // 6 + 0x1ee, 0x1ef, 0x1f0, 0x1f1, 0x1f2, 0x1f3, 0x1f4, 0x1f5, + 0x1f6, 0x1f6, 0x1f7, 0x1f8, 0x1f9, 0x1fa, 0x1fb, 0x1fc, + 0x1fd, 0x1fe, 0x1ff, 0x200, 0x201, 0x201, 0x202, 0x203, + + // First note of looped range used for all octaves: + + 0x204, 0x205, 0x206, 0x207, 0x208, 0x209, 0x20a, 0x20b, // 7 + 0x20c, 0x20d, 0x20e, 0x20f, 0x210, 0x210, 0x211, 0x212, + 0x213, 0x214, 0x215, 0x216, 0x217, 0x218, 0x219, 0x21a, + 0x21b, 0x21c, 0x21d, 0x21e, 0x21f, 0x220, 0x221, 0x222, + + 0x223, 0x224, 0x225, 0x226, 0x227, 0x228, 0x229, 0x22a, // 8 + 0x22b, 0x22c, 0x22d, 0x22e, 0x22f, 0x230, 0x231, 0x232, + 0x233, 0x234, 0x235, 0x236, 0x237, 0x238, 0x239, 0x23a, + 0x23b, 0x23c, 0x23d, 0x23e, 0x23f, 0x240, 0x241, 0x242, + + 0x244, 0x245, 0x246, 0x247, 0x248, 0x249, 0x24a, 0x24b, // 9 + 0x24c, 0x24d, 0x24e, 0x24f, 0x250, 0x251, 0x252, 0x253, + 0x254, 0x256, 0x257, 0x258, 0x259, 0x25a, 0x25b, 0x25c, + 0x25d, 0x25e, 0x25f, 0x260, 0x262, 0x263, 0x264, 0x265, + + 0x266, 0x267, 0x268, 0x269, 0x26a, 0x26c, 0x26d, 0x26e, // 10 + 0x26f, 0x270, 0x271, 0x272, 0x273, 0x275, 0x276, 0x277, + 0x278, 0x279, 0x27a, 0x27b, 0x27d, 0x27e, 0x27f, 0x280, + 0x281, 0x282, 0x284, 0x285, 0x286, 0x287, 0x288, 0x289, + + 0x28b, 0x28c, 0x28d, 0x28e, 0x28f, 0x290, 0x292, 0x293, // 11 + 0x294, 0x295, 0x296, 0x298, 0x299, 0x29a, 0x29b, 0x29c, + 0x29e, 0x29f, 0x2a0, 0x2a1, 0x2a2, 0x2a4, 0x2a5, 0x2a6, + 0x2a7, 0x2a9, 0x2aa, 0x2ab, 0x2ac, 0x2ae, 0x2af, 0x2b0, + + 0x2b1, 0x2b2, 0x2b4, 0x2b5, 0x2b6, 0x2b7, 0x2b9, 0x2ba, // 12 + 0x2bb, 0x2bd, 0x2be, 0x2bf, 0x2c0, 0x2c2, 0x2c3, 0x2c4, + 0x2c5, 0x2c7, 0x2c8, 0x2c9, 0x2cb, 0x2cc, 0x2cd, 0x2ce, + 0x2d0, 0x2d1, 0x2d2, 0x2d4, 0x2d5, 0x2d6, 0x2d8, 0x2d9, + + 0x2da, 0x2dc, 0x2dd, 0x2de, 0x2e0, 0x2e1, 0x2e2, 0x2e4, // 13 + 0x2e5, 0x2e6, 0x2e8, 0x2e9, 0x2ea, 0x2ec, 0x2ed, 0x2ee, + 0x2f0, 0x2f1, 0x2f2, 0x2f4, 0x2f5, 0x2f6, 0x2f8, 0x2f9, + 0x2fb, 0x2fc, 0x2fd, 0x2ff, 0x300, 0x302, 0x303, 0x304, + + 0x306, 0x307, 0x309, 0x30a, 0x30b, 0x30d, 0x30e, 0x310, // 14 + 0x311, 0x312, 0x314, 0x315, 0x317, 0x318, 0x31a, 0x31b, + 0x31c, 0x31e, 0x31f, 0x321, 0x322, 0x324, 0x325, 0x327, + 0x328, 0x329, 0x32b, 0x32c, 0x32e, 0x32f, 0x331, 0x332, + + 0x334, 0x335, 0x337, 0x338, 0x33a, 0x33b, 0x33d, 0x33e, // 15 + 0x340, 0x341, 0x343, 0x344, 0x346, 0x347, 0x349, 0x34a, + 0x34c, 0x34d, 0x34f, 0x350, 0x352, 0x353, 0x355, 0x357, + 0x358, 0x35a, 0x35b, 0x35d, 0x35e, 0x360, 0x361, 0x363, + + 0x365, 0x366, 0x368, 0x369, 0x36b, 0x36c, 0x36e, 0x370, // 16 + 0x371, 0x373, 0x374, 0x376, 0x378, 0x379, 0x37b, 0x37c, + 0x37e, 0x380, 0x381, 0x383, 0x384, 0x386, 0x388, 0x389, + 0x38b, 0x38d, 0x38e, 0x390, 0x392, 0x393, 0x395, 0x397, + + 0x398, 0x39a, 0x39c, 0x39d, 0x39f, 0x3a1, 0x3a2, 0x3a4, // 17 + 0x3a6, 0x3a7, 0x3a9, 0x3ab, 0x3ac, 0x3ae, 0x3b0, 0x3b1, + 0x3b3, 0x3b5, 0x3b7, 0x3b8, 0x3ba, 0x3bc, 0x3bd, 0x3bf, + 0x3c1, 0x3c3, 0x3c4, 0x3c6, 0x3c8, 0x3ca, 0x3cb, 0x3cd, + + // The last note has an incomplete range, and loops round back to + // the start. Note that the last value is actually a buffer overrun + // and does not fit with the other values. + + 0x3cf, 0x3d1, 0x3d2, 0x3d4, 0x3d6, 0x3d8, 0x3da, 0x3db, // 18 + 0x3dd, 0x3df, 0x3e1, 0x3e3, 0x3e4, 0x3e6, 0x3e8, 0x3ea, + 0x3ec, 0x3ed, 0x3ef, 0x3f1, 0x3f3, 0x3f5, 0x3f6, 0x3f8, + 0x3fa, 0x3fc, 0x3fe, 0x36c, +}; + +void OPLio::WriteFrequency(uint32_t channel, uint32_t note, uint32_t pitch, uint32_t keyon) +{ + int octave = 0; + int j = (note << 5) + pitch; + + if (j < 0) + { + j = 0; + } + else if (j >= 284) + { + j -= 284; + octave = j / (32*12); + if (octave > 7) + { + octave = 7; + } + j = (j % (32*12)) + 284; + } + int i = frequencies[j] | (octave << 10); + + WriteValue (OPL_REGS_FREQ_1, channel, (uint8_t)i); + WriteValue (OPL_REGS_FREQ_2, channel, (uint8_t)(i>>8)|(keyon<<5)); +} + +//---------------------------------------------------------------------------- +// +// +// +//---------------------------------------------------------------------------- + +static uint8_t volumetable[128] = { + 0, 1, 3, 5, 6, 8, 10, 11, + 13, 14, 16, 17, 19, 20, 22, 23, + 25, 26, 27, 29, 30, 32, 33, 34, + 36, 37, 39, 41, 43, 45, 47, 49, + 50, 52, 54, 55, 57, 59, 60, 61, + 63, 64, 66, 67, 68, 69, 71, 72, + 73, 74, 75, 76, 77, 79, 80, 81, + 82, 83, 84, 84, 85, 86, 87, 88, + 89, 90, 91, 92, 92, 93, 94, 95, + 96, 96, 97, 98, 99, 99, 100, 101, + 101, 102, 103, 103, 104, 105, 105, 106, + 107, 107, 108, 109, 109, 110, 110, 111, + 112, 112, 113, 113, 114, 114, 115, 115, + 116, 117, 117, 118, 118, 119, 119, 120, + 120, 121, 121, 122, 122, 123, 123, 123, + 124, 124, 125, 125, 126, 126, 127, 127}; + +void OPLio::WriteVolume(uint32_t channel, struct genmidi_voice_t *voice, uint32_t vol1, uint32_t vol2, uint32_t vol3) +{ + if (voice != nullptr) + { + uint32_t full_volume = volumetable[MIN(127, (uint32_t)((uint64_t)vol1*vol2*vol3) / (127 * 127))]; + int reg_volume2 = ((0x3f - voice->carrier.level) * full_volume) / 128; + reg_volume2 = (0x3f - reg_volume2) | voice->carrier.scale; + WriteOperator(OPL_REGS_LEVEL, channel, 1, reg_volume2); + + int reg_volume1; + if (voice->feedback & 0x01) + { + // Chocolate Doom says: + // If we are using non-modulated feedback mode, we must set the + // volume for both voices. + // Note that the same register volume value is written for + // both voices, always calculated from the carrier's level + // value. + + // But Muslib does it differently than the comment above states. Which one is correct? + + reg_volume1 = ((0x3f - voice->modulator.level) * full_volume) / 128; + reg_volume1 = (0x3f - reg_volume1) | voice->modulator.scale; + } + else + { + reg_volume1 = voice->modulator.level | voice->modulator.scale; + } + WriteOperator(OPL_REGS_LEVEL, channel, 0,reg_volume1); + } +} + + +//---------------------------------------------------------------------------- +// +// +// +//---------------------------------------------------------------------------- + +void OPLio::WritePan(uint32_t channel, struct genmidi_voice_t *voice, int pan) +{ + if (voice != 0) + { + WriteValue(OPL_REGS_FEEDBACK, channel, voice->feedback | (pan >= 28 ? 0x20 : 0) | (pan <= 100 ? 0x10 : 0)); + + // Set real panning if we're using emulated chips. + int chanper = IsOPL3 ? OPL3_NUM_VOICES : OPL_NUM_VOICES; + int which = channel / chanper; + if (chips[which] != NULL) + { + // This is the MIDI-recommended pan formula. 0 and 1 are + // both hard left so that 64 can be perfectly center. + double level = (pan <= 1) ? 0 : (pan - 1) / 126.0; + chips[which]->SetPanning(channel % chanper, + (float)cos(HALF_PI * level), (float)sin(HALF_PI * level)); + } + } +} + +//---------------------------------------------------------------------------- +// +// +// +//---------------------------------------------------------------------------- + +void OPLio::WriteTremolo(uint32_t channel, struct genmidi_voice_t *voice, bool vibrato) +{ + int val1 = voice->modulator.tremolo, val2 = voice->carrier.tremolo; + if (vibrato) + { + if (voice->feedback & 1) val1 |= 0x40; + val2 |= 0x40; + } + WriteOperator(OPL_REGS_TREMOLO, channel, 1, val2); + WriteOperator(OPL_REGS_TREMOLO, channel, 0, val2); +} + +//---------------------------------------------------------------------------- +// +// +// +//---------------------------------------------------------------------------- + +void OPLio::MuteChannel(uint32_t channel) +{ + WriteOperator(OPL_REGS_LEVEL, channel, 1, NO_VOLUME); + WriteOperator(OPL_REGS_ATTACK, channel, 1, MAX_ATTACK_DECAY); + WriteOperator(OPL_REGS_SUSTAIN, channel, 1, NO_SUSTAIN_MAX_RELEASE); + + WriteOperator(OPL_REGS_LEVEL, channel, 0, NO_VOLUME); + WriteOperator(OPL_REGS_ATTACK, channel, 0, MAX_ATTACK_DECAY); + WriteOperator(OPL_REGS_SUSTAIN, channel, 0, NO_SUSTAIN_MAX_RELEASE); +} + +//---------------------------------------------------------------------------- +// +// +// +//---------------------------------------------------------------------------- + +void OPLio::LoadOperatorData(uint32_t channel, int op_index, genmidi_op_t *data, bool max_level, bool vibrato) +{ + // The scale and level fields must be combined for the level register. + // For the carrier wave we always set the maximum level. + + int level = data->scale; + if (max_level) level |= 0x3f; + else level |= data->level; + + int tremolo = data->tremolo; + if (vibrato) tremolo |= 0x40; + + WriteOperator(OPL_REGS_LEVEL, channel, op_index, level); + WriteOperator(OPL_REGS_TREMOLO, channel, op_index, tremolo); + WriteOperator(OPL_REGS_ATTACK, channel, op_index, data->attack); + WriteOperator(OPL_REGS_SUSTAIN, channel, op_index, data->sustain); + WriteOperator(OPL_REGS_WAVEFORM, channel, op_index, data->waveform); +} + +//---------------------------------------------------------------------------- +// +// +// +//---------------------------------------------------------------------------- + +void OPLio::WriteInstrument(uint32_t channel, struct genmidi_voice_t *voice, bool vibrato) +{ + bool modulating = (voice->feedback & 0x01) == 0; + + // Doom loads the second operator first, then the first. + // The carrier is set to minimum volume until the voice volume + // is set later. If we are not using modulating mode, we must set both to minimum volume. + + LoadOperatorData(channel, 1, &voice->carrier, true, vibrato); + LoadOperatorData(channel, 0, &voice->modulator, !modulating, vibrato && modulating); + + // The feedback register is written by the calling code. +} diff --git a/src/sound/oplsynth/oplio.h b/src/sound/oplsynth/oplio.h new file mode 100644 index 000000000..94ad8b2f0 --- /dev/null +++ b/src/sound/oplsynth/oplio.h @@ -0,0 +1,110 @@ +#pragma once + + +enum +{ + // Operator registers (21 of each): + OPL_REGS_TREMOLO = 0x20, + OPL_REGS_LEVEL = 0x40, + OPL_REGS_ATTACK = 0x60, + OPL_REGS_SUSTAIN = 0x80, + OPL_REGS_WAVEFORM = 0xE0, + + // Voice registers (9 of each): + OPL_REGS_FREQ_1 = 0xA0, + OPL_REGS_FREQ_2 = 0xB0, + OPL_REGS_FEEDBACK = 0xC0, +}; + +enum +{ + OPL_REG_WAVEFORM_ENABLE = 0x01, + OPL_REG_TIMER1 = 0x02, + OPL_REG_TIMER2 = 0x03, + OPL_REG_TIMER_CTRL = 0x04, + OPL_REG_FM_MODE = 0x08, + OPL_REG_PERCUSSION_MODE = 0xBD, + + OPL_REG_OPL3_ENABLE = 0x105, + OPL_REG_4OPMODE_ENABLE = 0x104, + +}; + +enum +{ + NO_VOLUME = 0x3f, + MAX_ATTACK_DECAY = 0xff, + NO_SUSTAIN_MAX_RELEASE = 0xf, + WAVEFORM_ENABLED = 0x20 +}; + +enum +{ + OPL_NUM_VOICES = 9, + OPL3_NUM_VOICES = 18, + MAXOPL2CHIPS = 8, + NUM_VOICES = (OPL_NUM_VOICES * MAXOPL2CHIPS), + + NUM_CHANNELS = 16, + CHAN_PERCUSSION = 15, + + VIBRATO_THRESHOLD = 40, + MIN_SUSTAIN = 0x40, + HIGHEST_NOTE = 127, + +}; + +struct genmidi_voice_t; + +struct OPLio +{ + virtual ~OPLio(); + + void WriteOperator(uint32_t regbase, uint32_t channel, int index, uint8_t data2); + void LoadOperatorData(uint32_t channel, int op_index, genmidi_op_t *op_data, bool maxlevel, bool vibrato); + void WriteValue(uint32_t regbase, uint32_t channel, uint8_t value); + void WriteFrequency(uint32_t channel, uint32_t freq, uint32_t octave, uint32_t keyon); + void WriteVolume(uint32_t channel, genmidi_voice_t *voice, uint32_t v1, uint32_t v2, uint32_t v3); + void WritePan(uint32_t channel, genmidi_voice_t *voice, int pan); + void WriteTremolo(uint32_t channel, genmidi_voice_t *voice, bool vibrato); + void WriteInstrument(uint32_t channel, genmidi_voice_t *voice, bool vibrato); + void WriteInitState(bool opl3); + void MuteChannel(uint32_t chan); + void StopPlayback(); + + virtual int Init(uint32_t numchips, bool stereo = false, bool initopl3 = false); + virtual void Reset(); + virtual void WriteRegister(int which, uint32_t reg, uint8_t data); + virtual void SetClockRate(double samples_per_tick); + virtual void WriteDelay(int ticks); + + class OPLEmul *chips[OPL_NUM_VOICES]; + uint32_t NumChannels; + uint32_t NumChips; + bool IsOPL3; +}; + +struct DiskWriterIO : public OPLio +{ + DiskWriterIO(const char *filename); + ~DiskWriterIO(); + + int Init(uint32_t numchips, bool notused, bool initopl3); + void SetClockRate(double samples_per_tick); + void WriteDelay(int ticks); + + FString Filename; +}; + +struct OPLChannel +{ + uint32_t Instrument; + uint8_t Volume; + uint8_t Panning; + int8_t Pitch; + uint8_t Sustain; + bool Vibrato; + uint8_t Expression; + uint16_t PitchSensitivity; + uint16_t RPN; +}; From 79ed1f73e742db966a2d31b8d1164b27a764471b Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Mon, 17 Apr 2017 00:46:27 +0200 Subject: [PATCH 11/29] - some minor cleanup, including renaming some data structutrd in the OPL code. --- .../mididevices/music_opl_mididevice.cpp | 2 +- src/sound/oplsynth/genmidi.h | 6 +++--- src/sound/oplsynth/musicblock.cpp | 6 +++--- src/sound/oplsynth/musicblock.h | 20 ++++++++++++------- src/sound/oplsynth/opl_mus_player.h | 5 ++++- src/sound/oplsynth/oplio.cpp | 8 ++++---- src/sound/oplsynth/oplio.h | 10 +++++----- 7 files changed, 33 insertions(+), 24 deletions(-) diff --git a/src/sound/mididevices/music_opl_mididevice.cpp b/src/sound/mididevices/music_opl_mididevice.cpp index fe09e0448..d584d3ebd 100644 --- a/src/sound/mididevices/music_opl_mididevice.cpp +++ b/src/sound/mididevices/music_opl_mididevice.cpp @@ -87,7 +87,7 @@ OPLMIDIDevice::OPLMIDIDevice(const char *args) uint8_t filehdr[8]; data.Read(filehdr, 8); if (memcmp(filehdr, "#OPL_II#", 8)) I_Error("Corrupt GENMIDI lump"); - data.Read(OPLinstruments, sizeof(genmidi_instr_t) * GENMIDI_NUM_TOTAL); + data.Read(OPLinstruments, sizeof(GenMidiInstrument) * GENMIDI_NUM_TOTAL); SampleRate = (int)OPL_SAMPLE_RATE; } diff --git a/src/sound/oplsynth/genmidi.h b/src/sound/oplsynth/genmidi.h index 76ac26a96..f315f8245 100644 --- a/src/sound/oplsynth/genmidi.h +++ b/src/sound/oplsynth/genmidi.h @@ -11,7 +11,7 @@ struct genmidi_op_t uint8_t level; } FORCE_PACKED; -struct genmidi_voice_t +struct GenMidiVoice { genmidi_op_t modulator; uint8_t feedback; @@ -21,12 +21,12 @@ struct genmidi_voice_t } FORCE_PACKED; -struct genmidi_instr_t +struct GenMidiInstrument { uint16_t flags; uint8_t fine_tuning; uint8_t fixed_note; - genmidi_voice_t voices[2]; + GenMidiVoice voices[2]; } FORCE_PACKED; #pragma pack(pop) diff --git a/src/sound/oplsynth/musicblock.cpp b/src/sound/oplsynth/musicblock.cpp index e0a231476..9a2c0d93f 100644 --- a/src/sound/oplsynth/musicblock.cpp +++ b/src/sound/oplsynth/musicblock.cpp @@ -90,11 +90,11 @@ int musicBlock::replaceExistingVoice() // //---------------------------------------------------------------------------- -void musicBlock::voiceKeyOn(uint32_t slot, uint32_t channo, genmidi_instr_t *instrument, uint32_t instrument_voice, uint32_t key, uint32_t volume) +void musicBlock::voiceKeyOn(uint32_t slot, uint32_t channo, GenMidiInstrument *instrument, uint32_t instrument_voice, uint32_t key, uint32_t volume) { struct OPLVoice *voice = &voices[slot]; auto &channel = oplchannels[channo]; - genmidi_voice_t *gmvoice; + GenMidiVoice *gmvoice; voice->index = channo; voice->key = key; @@ -159,7 +159,7 @@ void musicBlock::noteOn(uint32_t channel, uint8_t key, int volume) return; } uint32_t note; - genmidi_instr_t *instrument; + GenMidiInstrument *instrument; // Percussion channel is treated differently. if (channel == CHAN_PERCUSSION) diff --git a/src/sound/oplsynth/musicblock.h b/src/sound/oplsynth/musicblock.h index 1f13aa01f..e806a45e6 100644 --- a/src/sound/oplsynth/musicblock.h +++ b/src/sound/oplsynth/musicblock.h @@ -10,8 +10,8 @@ struct OPLVoice unsigned int key; // The midi key that this voice is playing. unsigned int note; // The note being played. This is normally the same as the key, but if the instrument is a fixed pitch instrument, it is different. unsigned int note_volume; // The volume of the note being played on this channel. - genmidi_instr_t *current_instr; // Currently-loaded instrument data - genmidi_voice_t *current_instr_voice;// The voice number in the instrument to use. This is normally set to the instrument's first voice; if this is a double voice instrument, it may be the second one + GenMidiInstrument *current_instr; // Currently-loaded instrument data + GenMidiVoice *current_instr_voice;// The voice number in the instrument to use. This is normally set to the instrument's first voice; if this is a double voice instrument, it may be the second one bool sustained; int8_t fine_tuning; int pitch; @@ -21,13 +21,10 @@ struct musicBlock { musicBlock(); ~musicBlock(); - uint8_t *score; - uint8_t *scoredata; - int playingcount; OPLChannel oplchannels[NUM_CHANNELS]; OPLio *io; - struct genmidi_instr_t OPLinstruments[GENMIDI_NUM_TOTAL]; + struct GenMidiInstrument OPLinstruments[GENMIDI_NUM_TOTAL]; void changeModulation(uint32_t id, int value); void changeSustain(uint32_t id, int value); @@ -50,9 +47,18 @@ protected: int findFreeVoice(); int replaceExistingVoice(); - void voiceKeyOn(uint32_t slot, uint32_t channo, genmidi_instr_t *instrument, uint32_t instrument_voice, uint32_t, uint32_t volume); + void voiceKeyOn(uint32_t slot, uint32_t channo, GenMidiInstrument *instrument, uint32_t instrument_voice, uint32_t, uint32_t volume); int releaseVoice(uint32_t slot, uint32_t killed); friend class Stat_opl; }; + +enum ExtCtrl { + ctrlRPNHi, + ctrlRPNLo, + ctrlNRPNHi, + ctrlNRPNLo, + ctrlDataEntryHi, + ctrlDataEntryLo, +}; diff --git a/src/sound/oplsynth/opl_mus_player.h b/src/sound/oplsynth/opl_mus_player.h index a8ace69d2..52ef4be76 100644 --- a/src/sound/oplsynth/opl_mus_player.h +++ b/src/sound/oplsynth/opl_mus_player.h @@ -1,5 +1,5 @@ #include "critsec.h" -#include "muslib.h" +#include "musicblock.h" class FileReader; @@ -18,6 +18,9 @@ protected: virtual int PlayTick() = 0; void OffsetSamples(float *buff, int count); + uint8_t *score; + uint8_t *scoredata; + int playingcount; double NextTickIn; double SamplesPerTick; int NumChips; diff --git a/src/sound/oplsynth/oplio.cpp b/src/sound/oplsynth/oplio.cpp index 68b769db4..1229a5bbf 100644 --- a/src/sound/oplsynth/oplio.cpp +++ b/src/sound/oplsynth/oplio.cpp @@ -353,7 +353,7 @@ static uint8_t volumetable[128] = { 120, 121, 121, 122, 122, 123, 123, 123, 124, 124, 125, 125, 126, 126, 127, 127}; -void OPLio::WriteVolume(uint32_t channel, struct genmidi_voice_t *voice, uint32_t vol1, uint32_t vol2, uint32_t vol3) +void OPLio::WriteVolume(uint32_t channel, struct GenMidiVoice *voice, uint32_t vol1, uint32_t vol2, uint32_t vol3) { if (voice != nullptr) { @@ -392,7 +392,7 @@ void OPLio::WriteVolume(uint32_t channel, struct genmidi_voice_t *voice, uint32_ // //---------------------------------------------------------------------------- -void OPLio::WritePan(uint32_t channel, struct genmidi_voice_t *voice, int pan) +void OPLio::WritePan(uint32_t channel, struct GenMidiVoice *voice, int pan) { if (voice != 0) { @@ -418,7 +418,7 @@ void OPLio::WritePan(uint32_t channel, struct genmidi_voice_t *voice, int pan) // //---------------------------------------------------------------------------- -void OPLio::WriteTremolo(uint32_t channel, struct genmidi_voice_t *voice, bool vibrato) +void OPLio::WriteTremolo(uint32_t channel, struct GenMidiVoice *voice, bool vibrato) { int val1 = voice->modulator.tremolo, val2 = voice->carrier.tremolo; if (vibrato) @@ -478,7 +478,7 @@ void OPLio::LoadOperatorData(uint32_t channel, int op_index, genmidi_op_t *data, // //---------------------------------------------------------------------------- -void OPLio::WriteInstrument(uint32_t channel, struct genmidi_voice_t *voice, bool vibrato) +void OPLio::WriteInstrument(uint32_t channel, struct GenMidiVoice *voice, bool vibrato) { bool modulating = (voice->feedback & 0x01) == 0; diff --git a/src/sound/oplsynth/oplio.h b/src/sound/oplsynth/oplio.h index 94ad8b2f0..93b7e281c 100644 --- a/src/sound/oplsynth/oplio.h +++ b/src/sound/oplsynth/oplio.h @@ -54,7 +54,7 @@ enum }; -struct genmidi_voice_t; +struct GenMidiVoice; struct OPLio { @@ -64,10 +64,10 @@ struct OPLio void LoadOperatorData(uint32_t channel, int op_index, genmidi_op_t *op_data, bool maxlevel, bool vibrato); void WriteValue(uint32_t regbase, uint32_t channel, uint8_t value); void WriteFrequency(uint32_t channel, uint32_t freq, uint32_t octave, uint32_t keyon); - void WriteVolume(uint32_t channel, genmidi_voice_t *voice, uint32_t v1, uint32_t v2, uint32_t v3); - void WritePan(uint32_t channel, genmidi_voice_t *voice, int pan); - void WriteTremolo(uint32_t channel, genmidi_voice_t *voice, bool vibrato); - void WriteInstrument(uint32_t channel, genmidi_voice_t *voice, bool vibrato); + void WriteVolume(uint32_t channel, GenMidiVoice *voice, uint32_t v1, uint32_t v2, uint32_t v3); + void WritePan(uint32_t channel, GenMidiVoice *voice, int pan); + void WriteTremolo(uint32_t channel, GenMidiVoice *voice, bool vibrato); + void WriteInstrument(uint32_t channel, GenMidiVoice *voice, bool vibrato); void WriteInitState(bool opl3); void MuteChannel(uint32_t chan); void StopPlayback(); From c219811a542e0d6aae38b920ae9e257c5c2286c1 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Mon, 17 Apr 2017 01:06:54 +0200 Subject: [PATCH 12/29] - removed FMod as the last remaining piece of code that is not GPL compatible. Please consider GZDoom as licensed under the GPL starting with this commit, even though the license headers have not been changed yet. --- CMakeLists.txt | 1 - src/CMakeLists.txt | 180 +- src/menu/menudef.cpp | 4 - src/s_sound.cpp | 1 + src/sound/fmod_wrap.h | 624 ------ src/sound/fmodsound.cpp | 3527 --------------------------------- src/sound/fmodsound.h | 141 -- src/sound/i_music.cpp | 3 - src/sound/i_musicinterns.h | 1 - src/sound/i_sound.cpp | 33 +- src/sound/i_sound.h | 1 - src/sound/music_midi_base.cpp | 17 - src/sound/oalsound.cpp | 2 +- wadsrc/static/language.enu | 13 - wadsrc/static/menudef.txt | 63 +- 15 files changed, 6 insertions(+), 4605 deletions(-) delete mode 100644 src/sound/fmod_wrap.h delete mode 100644 src/sound/fmodsound.cpp delete mode 100644 src/sound/fmodsound.h diff --git a/CMakeLists.txt b/CMakeLists.txt index e96249ff3..f426119c2 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -150,7 +150,6 @@ endif() set(CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}") -option( NO_FMOD "Disable FMODEx sound support" OFF ) option( NO_OPENAL "Disable OpenAL sound support" OFF ) find_package( BZip2 ) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 54dce36ca..d303102b0 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -34,56 +34,6 @@ if( CMAKE_SIZEOF_VOID_P MATCHES "8" ) set( X64 64 ) endif() -# You can either use "make install" on the FMOD distribution to put it -# in standard system locations, or you can unpack the FMOD distribution -# in the root of the zdoom tree. e.g.: -# zdoom -# docs -# fmodapilinux[64] -or simply- fmod -# jpeg-6b -# ... -# The recommended method is to put it in the zdoom tree, since its -# headers are unversioned. Especially now that we can't work properly -# with anything newer than 4.26.xx, you probably don't want to use -# a system-wide version. - -# Construct version numbers for searching for the FMOD library on Linux. -set( MINOR_VERSIONS "61" "50" "49" "48" "47" "46" "45" "44" "43" "42" "41" - "40" "39" "38" "37" "36" "35" "34" "33" "32" "31" "30" "29" "28" - "27" "26" "25" "24" "23" "22" "21" "20" "21" "19" "18" "17" "16" - "15" "14" "13" "12" "11" "10" "09" "08" "07" "06" "05" "04" "03" - "02" "01" "00" ) -set( MAJOR_VERSIONS "44" "34" "28" "26" "24" "22" "20" ) - -if( NOT FMOD_DIR_VERSIONS ) - set( FMOD_DIR_VERSIONS "" ) -endif() -if( NOT FMOD_VERSIONS ) - set( FMOD_VERSIONS "" ) -endif() -if( NOT FMOD_LOCAL_INC_DIRS ) - set( FMOD_LOCAL_INC_DIRS "" ) -endif() -if( NOT FMOD_LOCAL_LIB_DIRS ) - set( FMOD_LOCAL_LIB_DIRS "" ) -endif() - -set( FMOD_DIR_VERSIONS ${FMOD_DIR_VERSIONS} "../fmod" ) -foreach( majver ${MAJOR_VERSIONS} ) - foreach( minver ${MINOR_VERSIONS} ) - set( FMOD_VERSIONS ${FMOD_VERSIONS} "fmodex${X64}-4.${majver}.${minver}" ) - # FMOD Ex version 4.44 unified 32-bit and 64-bit linux packages into one. - if( NOT majver EQUAL "44" ) - set( FMOD_DIR_VERSIONS ${FMOD_DIR_VERSIONS} "${CMAKE_HOME_DIRECTORY}/fmodapi4${majver}${minver}linux${X64}" ) - else() - set( FMOD_DIR_VERSIONS ${FMOD_DIR_VERSIONS} "${CMAKE_HOME_DIRECTORY}/fmodapi4${majver}${minver}linux" ) - endif() - endforeach() - foreach( dir ${FMOD_DIR_VERSIONS} ) - set( FMOD_LOCAL_INC_DIRS ${FMOD_LOCAL_INC_DIRS} "${dir}/api/inc" ) - set( FMOD_LOCAL_LIB_DIRS ${FMOD_LOCAL_LIB_DIRS} "${dir}/api/lib" ) - endforeach() -endforeach() if( NOT ZDOOM_LIBS ) set( ZDOOM_LIBS "" ) @@ -100,15 +50,9 @@ if( WIN32 ) add_definitions( -D_WIN32 ) - set( FMOD_SEARCH_PATHS - "C:/Program Files/FMOD SoundSystem/FMOD Programmers API ${WIN_TYPE}/api" - "C:/Program Files (x86)/FMOD SoundSystem/FMOD Programmers API ${WIN_TYPE}/api" - ) - set( FMOD_INC_PATH_SUFFIXES PATH_SUFFIXES inc ) - set( FMOD_LIB_PATH_SUFFIXES PATH_SUFFIXES lib ) if( ( MSVC14 AND NOT CMAKE_GENERATOR_TOOLSET STREQUAL "v140_xp" ) OR # For VS 2015. - ( MSVC15 AND NOT CMAKE_GENERATOR_TOOLSET STREQUAL "v150_xp" ) ) # For VS 2017. + ( MSVC15 AND NOT CMAKE_GENERATOR_TOOLSET STREQUAL "v141_xp" ) ) # For VS 2017. # for modern Windows SDKs the DirectX headers should be available by default. set( DX_dinput8_LIBRARY dinput8 ) set( DX_dxguid_LIBRARY dxguid ) @@ -189,9 +133,6 @@ if( WIN32 ) endif() else() if( APPLE ) - set( FMOD_SEARCH_PATHS "/Developer/FMOD Programmers API Mac/api" ) - set( FMOD_INC_PATH_SUFFIXES PATH_SUFFIXES inc ) - set( FMOD_LIB_PATH_SUFFIXES PATH_SUFFIXES lib ) set( NO_GTK ON ) set( DYN_GTK OFF ) @@ -204,17 +145,6 @@ else() option( NO_GTK "Disable GTK+ dialogs (Not applicable to Windows)" ) option( DYN_GTK "Load GTK+ at runtime instead of compile time" ON ) - set( FMOD_SEARCH_PATHS - /usr/local/include - /usr/local/include/fmodex - /usr/include - /usr/include/fmodex - /opt/local/include - /opt/local/include/fmodex - /opt/include - /opt/include/fmodex ) - set( FMOD_INC_PATH_SUFFIXES PATH_SUFFIXES fmodex ) - # Use GTK+ for the IWAD picker, if available. if( NOT NO_GTK ) pkg_check_modules( GTK3 gtk+-3.0 ) @@ -281,82 +211,6 @@ if( NOT NO_OPENAL ) endif() endif() -if( NOT NO_FMOD ) - # Search for FMOD include files - if( NOT WIN32 ) - find_path( FMOD_INCLUDE_DIR fmod.hpp - PATHS ${FMOD_LOCAL_INC_DIRS} ) - endif() - - if( NOT FMOD_INCLUDE_DIR ) - find_path( FMOD_INCLUDE_DIR fmod.hpp - PATHS ${FMOD_SEARCH_PATHS} - ${FMOD_INC_PATH_SUFFIXES} ) - endif() - - if( FMOD_INCLUDE_DIR ) - message( STATUS "FMOD include files found at ${FMOD_INCLUDE_DIR}" ) - include_directories( "${FMOD_INCLUDE_DIR}" ) - - if( EXISTS "${FMOD_INCLUDE_DIR}/fmod_common.h" ) - set( FMOD_STUDIO YES ) - set( FMOD_VERSION_FILE "fmod_common.h" ) - else() - set( FMOD_STUDIO NO ) - set( FMOD_VERSION_FILE "fmod.h" ) - endif() - - file( STRINGS "${FMOD_INCLUDE_DIR}/${FMOD_VERSION_FILE}" FMOD_VERSION_LINE REGEX "^#define[ \t]+FMOD_VERSION[ \t]+0x[0-9]+$" ) - string( REGEX REPLACE "^#define[ \t]+FMOD_VERSION[ \t]+0x([0-9][0-9][0-9][0-9])([0-9][0-9])([0-9][0-9])$" "\\1.\\2.\\3" FMOD_VERSION "${FMOD_VERSION_LINE}" ) - message( STATUS "FMOD version: ${FMOD_VERSION}" ) - - # FMOD Ex didn't hide xiph symbols in the past (not applicable to Win32 since symbols are hidden by default there). - if( NOT WIN32 AND NOT FMOD_STUDIO AND NOT NO_OPENAL AND "${FMOD_VERSION}" VERSION_LESS "4.36.00") - message( SEND_ERROR "Use of FMOD Ex ${FMOD_VERSION} with OpenAL will result in crashes. Either update FMOD to 4.36 or later or set NO_OPENAL." ) - endif() - else() - message( STATUS "Could not find FMOD include files" ) - set( NO_FMOD ON ) - endif() -endif() - -if( NOT NO_FMOD ) - # Decide on the name of the FMOD library we want to use. - if( NOT FMOD_LIB_NAME AND MSVC ) - set( FMOD_LIB_NAME fmodex${X64}_vc ) - endif() - - if( NOT FMOD_LIB_NAME AND BORLAND ) - set( FMOD_LIB_NAME fmodex${X64}_bc ) - endif() - - if( NOT FMOD_LIB_NAME ) - set( FMOD_LIB_NAME fmodex${X64} ) - endif() - - # Search for FMOD library - if( WIN32 OR APPLE ) - find_library( FMOD_LIBRARY ${FMOD_LIB_NAME} - PATHS ${FMOD_SEARCH_PATHS} - ${FMOD_LIB_PATH_SUFFIXES} ) - else() - find_library( FMOD_LIBRARY - NAMES ${FMOD_VERSIONS} - PATHS ${FMOD_LOCAL_LIB_DIRS} ) - endif() - - if( FMOD_LIBRARY ) - message( STATUS "FMOD library found at ${FMOD_LIBRARY}" ) - set( ZDOOM_LIBS ${ZDOOM_LIBS} "${FMOD_LIBRARY}" ) - else() - message( STATUS "Could not find FMOD library" ) - set( NO_FMOD ON ) - endif() -endif() - -if( NO_FMOD ) - add_definitions( -DNO_FMOD=1 ) -endif() if( NO_OPENAL ) add_definitions( -DNO_OPENAL=1 ) @@ -683,10 +537,9 @@ elseif( APPLE ) set( OTHER_SYSTEM_SOURCES ${PLAT_WIN32_SOURCES} ${PLAT_COCOA_SOURCES} ${PLAT_UNIX_SOURCES} ) endif() - set( SYSTEM_SOURCES ${SYSTEM_SOURCES} ${PLAT_POSIX_SOURCES} ${PLAT_OSX_SOURCES} "${FMOD_LIBRARY}" ) + set( SYSTEM_SOURCES ${SYSTEM_SOURCES} ${PLAT_POSIX_SOURCES} ${PLAT_OSX_SOURCES} ) set_source_files_properties( posix/osx/zdoom.icns PROPERTIES MACOSX_PACKAGE_LOCATION Resources ) - set_source_files_properties( "${FMOD_LIBRARY}" PROPERTIES MACOSX_PACKAGE_LOCATION Frameworks ) set_source_files_properties( posix/osx/iwadpicker_cocoa.mm PROPERTIES COMPILE_FLAGS -fobjc-exceptions ) else() set( SYSTEM_SOURCES_DIR posix posix/sdl ) @@ -901,7 +754,6 @@ set( FASTMATH_SOURCES swrenderer/r_all.cpp polyrenderer/poly_all.cpp sound/oplsynth/opl_mus_player.cpp - sound/fmodsound.cpp sound/mpg123_decoder.cpp sound/music_midi_base.cpp sound/oalsound.cpp @@ -1372,14 +1224,6 @@ if( MSVC ) option( ZDOOM_GENERATE_MAPFILE "Generate .map file for debugging." OFF ) set( LINKERSTUFF "/MANIFEST:NO" ) - if( NOT NO_FMOD ) - if( FMOD_STUDIO ) - set( LINKERSTUFF "${LINKERSTUFF} /DELAYLOAD:\"fmod${X64}.dll\"" ) - else() - set( LINKERSTUFF "${LINKERSTUFF} /DELAYLOAD:\"fmodex${X64}.dll\"" ) - endif() - endif() - if( ZDOOM_GENERATE_MAPFILE ) set( LINKERSTUFF "${LINKERSTUFF} /MAP" ) endif() @@ -1418,26 +1262,6 @@ if( APPLE ) LINK_FLAGS "-framework AudioToolbox -framework AudioUnit -framework Carbon -framework Cocoa -framework IOKit -framework OpenGL" MACOSX_BUNDLE_INFO_PLIST "${CMAKE_CURRENT_SOURCE_DIR}/posix/osx/zdoom-info.plist" ) - if( NOT NO_FMOD ) - if( FMOD_STUDIO ) - set( FMOD_DYLIB libfmod.dylib ) - else() - set( FMOD_DYLIB libfmodex.dylib ) - endif() - - # Fix fmod link so that it can be found in the app bundle. - find_program( OTOOL otool HINTS "/usr/bin" "${OSX_DEVELOPER_ROOT}/usr/bin" ) - find_program( INSTALL_NAME_TOOL install_name_tool HINTS "/usr/bin" "${OSX_DEVELOPER_ROOT}/usr/bin" ) - execute_process( COMMAND "${OTOOL}" -L "${FMOD_LIBRARY}" - COMMAND grep "${FMOD_DYLIB} (compat" - COMMAND head -n1 - COMMAND awk "{print $1}" - OUTPUT_VARIABLE FMOD_LINK - OUTPUT_STRIP_TRAILING_WHITESPACE ) - add_custom_command( TARGET zdoom POST_BUILD - COMMAND "${INSTALL_NAME_TOOL}" -change "${FMOD_LINK}" @executable_path/../Frameworks/${FMOD_DYLIB} "$" - COMMENT "Relinking FMOD Ex" ) - endif() endif() if( WIN32 ) diff --git a/src/menu/menudef.cpp b/src/menu/menudef.cpp index a14ac25b3..d3a2ee49f 100644 --- a/src/menu/menudef.cpp +++ b/src/menu/menudef.cpp @@ -247,10 +247,6 @@ static bool CheckSkipOptionBlock(FScanner &sc) { filter |= IsOpenALPresent(); } - else if (sc.Compare("FModEx")) - { - filter |= IsFModExPresent(); - } } while (sc.CheckString(",")); sc.MustGetStringName(")"); diff --git a/src/s_sound.cpp b/src/s_sound.cpp index 75f6a68a8..f5fc2ed13 100644 --- a/src/s_sound.cpp +++ b/src/s_sound.cpp @@ -140,6 +140,7 @@ CUSTOM_CVAR (Int, snd_channels, 128, CVAR_ARCHIVE|CVAR_GLOBALCONFIG) // number o if (self < 64) self = 64; } CVAR (Bool, snd_flipstereo, false, CVAR_ARCHIVE|CVAR_GLOBALCONFIG) +CVAR(Bool, snd_waterreverb, true, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) // CODE -------------------------------------------------------------------- diff --git a/src/sound/fmod_wrap.h b/src/sound/fmod_wrap.h deleted file mode 100644 index 02d6a56ae..000000000 --- a/src/sound/fmod_wrap.h +++ /dev/null @@ -1,624 +0,0 @@ - -#ifndef FMOD_WRAP_H -#define FMOD_WRAP_H - -#ifndef NO_FMOD - -#if !defined(_WIN32) || defined(_MSC_VER) -// Use the real C++ interface if it's supported on this platform. -#include "fmod.hpp" -#else -// Use a wrapper C++ interface for non-Microsoft compilers on Windows. - -#include "fmod.h" - -// Create fake definitions for these structs so they can be subclassed. -struct FMOD_SYSTEM {}; -struct FMOD_SOUND {}; -struct FMOD_CHANNEL {}; -struct FMOD_CHANNELGROUP {}; -struct FMOD_SOUNDGROUP {}; -struct FMOD_REVERB {}; -struct FMOD_DSP {}; -struct FMOD_DSPCONNECTION {}; -struct FMOD_POLYGON {}; -struct FMOD_GEOMETRY {}; -struct FMOD_SYNCPOINT {}; -/* - Constant and defines -*/ - -/* - FMOD Namespace -*/ - -namespace FMOD -{ - - class System; - class Sound; - class Channel; - class ChannelGroup; - class SoundGroup; - class Reverb; - class DSP; - class DSPConnection; - class Geometry; - - /* - FMOD global system functions (optional). - */ - inline FMOD_RESULT Memory_Initialize(void *poolmem, int poollen, FMOD_MEMORY_ALLOCCALLBACK useralloc, FMOD_MEMORY_REALLOCCALLBACK userrealloc, FMOD_MEMORY_FREECALLBACK userfree, FMOD_MEMORY_TYPE memtypeflags = (FMOD_MEMORY_NORMAL | FMOD_MEMORY_XBOX360_PHYSICAL)) { return FMOD_Memory_Initialize(poolmem, poollen, useralloc, userrealloc, userfree, memtypeflags); } - //inline FMOD_RESULT Memory_GetStats (int *currentalloced, int *maxalloced) { return FMOD_Memory_GetStats(currentalloced, maxalloced); } - inline FMOD_RESULT Debug_SetLevel(FMOD_DEBUGLEVEL level) { return FMOD_Debug_SetLevel(level); } - inline FMOD_RESULT Debug_GetLevel(FMOD_DEBUGLEVEL *level) { return FMOD_Debug_GetLevel(level); } - inline FMOD_RESULT File_SetDiskBusy(int busy) { return FMOD_File_SetDiskBusy(busy); } - inline FMOD_RESULT File_GetDiskBusy(int *busy) { return FMOD_File_GetDiskBusy(busy); } - - /* - FMOD System factory functions. - */ - inline FMOD_RESULT System_Create(System **system) { return FMOD_System_Create((FMOD_SYSTEM **)system); } - - /* - 'System' API - */ - - class System : FMOD_SYSTEM - { - private: - - System(); /* Constructor made private so user cannot statically instance a System class. - System_Create must be used. */ - public: - - FMOD_RESULT release () { return FMOD_System_Release(this); } - - // Pre-init functions. - FMOD_RESULT setOutput (FMOD_OUTPUTTYPE output) { return FMOD_System_SetOutput(this, output); } - FMOD_RESULT getOutput (FMOD_OUTPUTTYPE *output) { return FMOD_System_GetOutput(this, output); } - FMOD_RESULT getNumDrivers (int *numdrivers) { return FMOD_System_GetNumDrivers(this, numdrivers); } - FMOD_RESULT getDriverInfo (int id, char *name, int namelen, FMOD_GUID *guid) { return FMOD_System_GetDriverInfo(this, id, name, namelen, guid); } - FMOD_RESULT getDriverCaps (int id, FMOD_CAPS *caps, int *minfrequency, int *maxfrequency, FMOD_SPEAKERMODE *controlpanelspeakermode) { return FMOD_System_GetDriverCaps(this, id, caps, minfrequency, maxfrequency, controlpanelspeakermode); } - FMOD_RESULT setDriver (int driver) { return FMOD_System_SetDriver(this, driver); } - FMOD_RESULT getDriver (int *driver) { return FMOD_System_GetDriver(this, driver); } - FMOD_RESULT setHardwareChannels (int min2d, int max2d, int min3d, int max3d) { return FMOD_System_SetHardwareChannels(this, min2d, max2d, min3d, max3d); } - FMOD_RESULT setSoftwareChannels (int numsoftwarechannels) { return FMOD_System_SetSoftwareChannels(this, numsoftwarechannels); } - FMOD_RESULT getSoftwareChannels (int *numsoftwarechannels) { return FMOD_System_GetSoftwareChannels(this, numsoftwarechannels); } - FMOD_RESULT setSoftwareFormat (int samplerate, FMOD_SOUND_FORMAT format, int numoutputchannels, int maxinputchannels, FMOD_DSP_RESAMPLER resamplemethod) { return FMOD_System_SetSoftwareFormat(this, samplerate, format, numoutputchannels, maxinputchannels, resamplemethod); } - FMOD_RESULT getSoftwareFormat (int *samplerate, FMOD_SOUND_FORMAT *format, int *numoutputchannels, int *maxinputchannels, FMOD_DSP_RESAMPLER *resamplemethod, int *bits) { return FMOD_System_GetSoftwareFormat(this, samplerate, format, numoutputchannels, maxinputchannels, resamplemethod, bits); } - FMOD_RESULT setDSPBufferSize (unsigned int bufferlength, int numbuffers) { return FMOD_System_SetDSPBufferSize(this, bufferlength, numbuffers); } - FMOD_RESULT getDSPBufferSize (unsigned int *bufferlength, int *numbuffers) { return FMOD_System_GetDSPBufferSize(this, bufferlength, numbuffers); } - FMOD_RESULT setFileSystem (FMOD_FILE_OPENCALLBACK useropen, FMOD_FILE_CLOSECALLBACK userclose, FMOD_FILE_READCALLBACK userread, FMOD_FILE_SEEKCALLBACK userseek, int blockalign) { return FMOD_System_SetFileSystem(this, useropen, userclose, userread, userseek, blockalign); } - FMOD_RESULT attachFileSystem (FMOD_FILE_OPENCALLBACK useropen, FMOD_FILE_CLOSECALLBACK userclose, FMOD_FILE_READCALLBACK userread, FMOD_FILE_SEEKCALLBACK userseek) { return FMOD_System_AttachFileSystem(this, useropen, userclose, userread, userseek); } - FMOD_RESULT setAdvancedSettings (FMOD_ADVANCEDSETTINGS *settings) { return FMOD_System_SetAdvancedSettings(this, settings); } - FMOD_RESULT getAdvancedSettings (FMOD_ADVANCEDSETTINGS *settings) { return FMOD_System_GetAdvancedSettings(this, settings); } - FMOD_RESULT setSpeakerMode (FMOD_SPEAKERMODE speakermode) { return FMOD_System_SetSpeakerMode(this, speakermode); } - FMOD_RESULT getSpeakerMode (FMOD_SPEAKERMODE *speakermode) { return FMOD_System_GetSpeakerMode(this, speakermode); } - FMOD_RESULT setCallback (FMOD_SYSTEM_CALLBACK callback) { return FMOD_System_SetCallback(this, callback); } - - // Plug-in support - FMOD_RESULT setPluginPath (const char *path) { return FMOD_System_SetPluginPath(this, path); } - FMOD_RESULT loadPlugin (const char *filename, unsigned int *handle, unsigned int priority = 0) { return FMOD_System_LoadPlugin(this, filename, handle, priority); } - FMOD_RESULT unloadPlugin (unsigned int handle) { return FMOD_System_UnloadPlugin(this, handle); } - FMOD_RESULT getNumPlugins (FMOD_PLUGINTYPE plugintype, int *numplugins) { return FMOD_System_GetNumPlugins(this, plugintype, numplugins); } - FMOD_RESULT getPluginHandle (FMOD_PLUGINTYPE plugintype, int index, unsigned int *handle) { return FMOD_System_GetPluginHandle(this, plugintype, index, handle); } - FMOD_RESULT getPluginInfo (unsigned int handle, FMOD_PLUGINTYPE *plugintype, char *name, int namelen, unsigned int *version) { return FMOD_System_GetPluginInfo(this, handle, plugintype, name, namelen, version); } - FMOD_RESULT setOutputByPlugin (unsigned int handle) { return FMOD_System_SetOutputByPlugin(this, handle); } - FMOD_RESULT getOutputByPlugin (unsigned int *handle) { return FMOD_System_GetOutputByPlugin(this, handle); } - FMOD_RESULT createCodec (FMOD_CODEC_DESCRIPTION *description, unsigned int priority = 0) { return FMOD_System_CreateCodec(this, description, priority); } - - // Init/Close - FMOD_RESULT init (int maxchannels, FMOD_INITFLAGS flags, void *extradriverdata) { return FMOD_System_Init(this, maxchannels, flags, extradriverdata); } - FMOD_RESULT close () { return FMOD_System_Close(this); } - - // General post-init system functions - FMOD_RESULT update () /* IMPORTANT! CALL THIS ONCE PER FRAME! */ { return FMOD_System_Update(this); } - - FMOD_RESULT set3DSettings (float dopplerscale, float distancefactor, float rolloffscale) { return FMOD_System_Set3DSettings(this, dopplerscale, distancefactor, rolloffscale); } - FMOD_RESULT get3DSettings (float *dopplerscale, float *distancefactor, float *rolloffscale) { return FMOD_System_Get3DSettings(this, dopplerscale, distancefactor, rolloffscale); } - FMOD_RESULT set3DNumListeners (int numlisteners) { return FMOD_System_Set3DNumListeners(this, numlisteners); } - FMOD_RESULT get3DNumListeners (int *numlisteners) { return FMOD_System_Get3DNumListeners(this, numlisteners); } - FMOD_RESULT set3DListenerAttributes(int listener, const FMOD_VECTOR *pos, const FMOD_VECTOR *vel, const FMOD_VECTOR *forward, const FMOD_VECTOR *up) { return FMOD_System_Set3DListenerAttributes(this, listener, pos, vel, forward, up); } - FMOD_RESULT get3DListenerAttributes(int listener, FMOD_VECTOR *pos, FMOD_VECTOR *vel, FMOD_VECTOR *forward, FMOD_VECTOR *up) { return FMOD_System_Get3DListenerAttributes(this, listener, pos, vel, forward, up); } - FMOD_RESULT set3DRolloffCallback (FMOD_3D_ROLLOFFCALLBACK callback) { return FMOD_System_Set3DRolloffCallback(this, callback); } - FMOD_RESULT set3DSpeakerPosition (FMOD_SPEAKER speaker, float x, float y, bool active) { return FMOD_System_Set3DSpeakerPosition(this, speaker, x, y, active); } - FMOD_RESULT get3DSpeakerPosition (FMOD_SPEAKER speaker, float *x, float *y, bool *active) { FMOD_BOOL b; FMOD_RESULT res = FMOD_System_Get3DSpeakerPosition(this, speaker, x, y, &b); *active = b; return res; } - - FMOD_RESULT setStreamBufferSize (unsigned int filebuffersize, FMOD_TIMEUNIT filebuffersizetype) { return FMOD_System_SetStreamBufferSize(this, filebuffersize, filebuffersizetype); } - FMOD_RESULT getStreamBufferSize (unsigned int *filebuffersize, FMOD_TIMEUNIT *filebuffersizetype) { return FMOD_System_GetStreamBufferSize(this, filebuffersize, filebuffersizetype); } - - // System information functions. - FMOD_RESULT getVersion (unsigned int *version) { return FMOD_System_GetVersion(this, version); } - FMOD_RESULT getOutputHandle (void **handle) { return FMOD_System_GetOutputHandle(this, handle); } - FMOD_RESULT getChannelsPlaying (int *channels) { return FMOD_System_GetChannelsPlaying(this, channels); } - FMOD_RESULT getHardwareChannels (int *num2d, int *num3d, int *total) { return FMOD_System_GetHardwareChannels(this, num2d, num3d, total); } -#if FMOD_VERSION < 0x42501 - FMOD_RESULT getCPUUsage (float *dsp, float *stream, float *update, float *total) { return FMOD_System_GetCPUUsage(this, dsp, stream, update, total); } -#else - FMOD_RESULT getCPUUsage (float *dsp, float *stream, float *geometry, float *update, float *total) { return FMOD_System_GetCPUUsage(this, dsp, stream, geometry, update, total); } -#endif - FMOD_RESULT getSoundRAM (int *currentalloced, int *maxalloced, int *total) { return FMOD_System_GetSoundRAM(this, currentalloced, maxalloced, total); } - FMOD_RESULT getNumCDROMDrives (int *numdrives) { return FMOD_System_GetNumCDROMDrives(this, numdrives); } - FMOD_RESULT getCDROMDriveName (int drive, char *drivename, int drivenamelen, char *scsiname, int scsinamelen, char *devicename, int devicenamelen) { return FMOD_System_GetCDROMDriveName(this, drive, drivename, drivenamelen, scsiname, scsinamelen, devicename, devicenamelen); } - FMOD_RESULT getSpectrum (float *spectrumarray, int numvalues, int channeloffset, FMOD_DSP_FFT_WINDOW windowtype) { return FMOD_System_GetSpectrum(this, spectrumarray, numvalues, channeloffset, windowtype); } - FMOD_RESULT getWaveData (float *wavearray, int numvalues, int channeloffset) { return FMOD_System_GetWaveData(this, wavearray, numvalues, channeloffset); } - - // Sound/DSP/Channel/FX creation and retrieval. - FMOD_RESULT createSound (const char *name_or_data, FMOD_MODE mode, FMOD_CREATESOUNDEXINFO *exinfo, Sound **sound) { return FMOD_System_CreateSound(this, name_or_data, mode, exinfo, (FMOD_SOUND **)sound); } - FMOD_RESULT createStream (const char *name_or_data, FMOD_MODE mode, FMOD_CREATESOUNDEXINFO *exinfo, Sound **sound) { return FMOD_System_CreateStream(this, name_or_data, mode, exinfo, (FMOD_SOUND **)sound); } - FMOD_RESULT createDSP (FMOD_DSP_DESCRIPTION *description, DSP **dsp) { return FMOD_System_CreateDSP(this, description, (FMOD_DSP **)dsp); } - FMOD_RESULT createDSPByType (FMOD_DSP_TYPE type, DSP **dsp) { return FMOD_System_CreateDSPByType(this, type, (FMOD_DSP **)dsp); } - FMOD_RESULT createChannelGroup (const char *name, ChannelGroup **channelgroup) { return FMOD_System_CreateChannelGroup(this, name, (FMOD_CHANNELGROUP **)channelgroup); } - FMOD_RESULT createSoundGroup (const char *name, SoundGroup **soundgroup) { return FMOD_System_CreateSoundGroup(this, name, (FMOD_SOUNDGROUP **)soundgroup); } - FMOD_RESULT createReverb (Reverb **reverb) { return FMOD_System_CreateReverb(this, (FMOD_REVERB **)reverb); } - - FMOD_RESULT playSound (FMOD_CHANNELINDEX channelid, Sound *sound, bool paused, Channel **channel) { return FMOD_System_PlaySound(this, channelid, (FMOD_SOUND *)sound, paused, (FMOD_CHANNEL **)channel); } - FMOD_RESULT playDSP (FMOD_CHANNELINDEX channelid, DSP *dsp, bool paused, Channel **channel) { return FMOD_System_PlayDSP(this, channelid, (FMOD_DSP *)dsp, paused, (FMOD_CHANNEL **)channel); } - FMOD_RESULT getChannel (int channelid, Channel **channel) { return FMOD_System_GetChannel(this, channelid, (FMOD_CHANNEL **)channel); } - FMOD_RESULT getMasterChannelGroup (ChannelGroup **channelgroup) { return FMOD_System_GetMasterChannelGroup(this, (FMOD_CHANNELGROUP **)channelgroup); } - FMOD_RESULT getMasterSoundGroup (SoundGroup **soundgroup) { return FMOD_System_GetMasterSoundGroup(this, (FMOD_SOUNDGROUP **)soundgroup); } - - // Reverb API - FMOD_RESULT setReverbProperties (const FMOD_REVERB_PROPERTIES *prop) { return FMOD_System_SetReverbProperties(this, prop); } - FMOD_RESULT getReverbProperties (FMOD_REVERB_PROPERTIES *prop) { return FMOD_System_GetReverbProperties(this, prop); } - FMOD_RESULT setReverbAmbientProperties(FMOD_REVERB_PROPERTIES *prop) { return FMOD_System_SetReverbAmbientProperties(this, prop); } - FMOD_RESULT getReverbAmbientProperties(FMOD_REVERB_PROPERTIES *prop) { return FMOD_System_GetReverbAmbientProperties(this, prop); } - - // System level DSP access. - FMOD_RESULT getDSPHead (DSP **dsp) { return FMOD_System_GetDSPHead(this, (FMOD_DSP **)dsp); } - FMOD_RESULT addDSP (DSP *dsp, DSPConnection **connection) { return FMOD_System_AddDSP(this, (FMOD_DSP *)dsp, (FMOD_DSPCONNECTION**)dsp); } - FMOD_RESULT lockDSP () { return FMOD_System_LockDSP(this); } - FMOD_RESULT unlockDSP () { return FMOD_System_UnlockDSP(this); } - FMOD_RESULT getDSPClock (unsigned int *hi, unsigned int *lo) { return FMOD_System_GetDSPClock(this, hi, lo); } - - // Recording API. - FMOD_RESULT getRecordNumDrivers (int *numdrivers) { return FMOD_System_GetRecordNumDrivers(this, numdrivers); } - FMOD_RESULT getRecordDriverInfo (int id, char *name, int namelen, FMOD_GUID *guid) { return FMOD_System_GetRecordDriverInfo(this, id, name, namelen, guid); } - FMOD_RESULT getRecordDriverCaps (int id, FMOD_CAPS *caps, int *minfrequency, int *maxfrequency) { return FMOD_System_GetRecordDriverCaps(this, id, caps, minfrequency, maxfrequency); } - FMOD_RESULT getRecordPosition (int id, unsigned int *position) { return FMOD_System_GetRecordPosition(this, id, position); } - - FMOD_RESULT recordStart (int id, Sound *sound, bool loop) { return FMOD_System_RecordStart(this, id, (FMOD_SOUND *)sound, loop); } - FMOD_RESULT recordStop (int id) { return FMOD_System_RecordStop(this, id); } - FMOD_RESULT isRecording (int id, bool *recording) { FMOD_BOOL b; FMOD_RESULT res = FMOD_System_IsRecording(this, id, &b); *recording = b; return res; } - - // Geometry API. - FMOD_RESULT createGeometry (int maxpolygons, int maxvertices, Geometry **geometry) { return FMOD_System_CreateGeometry(this, maxpolygons, maxvertices, (FMOD_GEOMETRY **)geometry); } - FMOD_RESULT setGeometrySettings (float maxworldsize) { return FMOD_System_SetGeometrySettings(this, maxworldsize); } - FMOD_RESULT getGeometrySettings (float *maxworldsize) { return FMOD_System_GetGeometrySettings(this, maxworldsize); } - FMOD_RESULT loadGeometry (const void *data, int datasize, Geometry **geometry) { return FMOD_System_LoadGeometry(this, data, datasize, (FMOD_GEOMETRY **)geometry); } - - // Network functions. - FMOD_RESULT setNetworkProxy (const char *proxy) { return FMOD_System_SetNetworkProxy(this, proxy); } - FMOD_RESULT getNetworkProxy (char *proxy, int proxylen) { return FMOD_System_GetNetworkProxy(this, proxy, proxylen); } - FMOD_RESULT setNetworkTimeout (int timeout) { return FMOD_System_SetNetworkTimeout(this, timeout); } - FMOD_RESULT getNetworkTimeout (int *timeout) { return FMOD_System_GetNetworkTimeout(this, timeout); } - - // Userdata set/get. - FMOD_RESULT setUserData (void *userdata) { return FMOD_System_SetUserData(this, userdata); } - FMOD_RESULT getUserData (void **userdata) { return FMOD_System_GetUserData(this, userdata); } - - FMOD_RESULT getMemoryInfo (unsigned int memorybits, unsigned int event_memorybits, unsigned int *memoryused, unsigned int *memoryused_array) { return FMOD_System_GetMemoryInfo(this, memorybits, event_memorybits, memoryused, memoryused_array); } - }; - - /* - 'Sound' API - */ - class Sound : FMOD_SOUND - { - private: - - Sound(); /* Constructor made private so user cannot statically instance a Sound class. - Appropriate Sound creation or retrieval function must be used. */ - public: - - FMOD_RESULT release () { return FMOD_Sound_Release(this); } - FMOD_RESULT getSystemObject (System **system) { return FMOD_Sound_GetSystemObject(this, (FMOD_SYSTEM **)system); } - - // Standard sound manipulation functions. - FMOD_RESULT lock (unsigned int offset, unsigned int length, void **ptr1, void **ptr2, unsigned int *len1, unsigned int *len2) { return FMOD_Sound_Lock(this, offset, length, ptr1, ptr2, len1, len2); } - FMOD_RESULT unlock (void *ptr1, void *ptr2, unsigned int len1, unsigned int len2) { return FMOD_Sound_Unlock(this, ptr1, ptr2, len1, len2); } - FMOD_RESULT setDefaults (float frequency, float volume, float pan, int priority) { return FMOD_Sound_SetDefaults(this, frequency, volume, pan, priority); } - FMOD_RESULT getDefaults (float *frequency, float *volume, float *pan, int *priority) { return FMOD_Sound_GetDefaults(this, frequency, volume, pan, priority); } - FMOD_RESULT setVariations (float frequencyvar, float volumevar, float panvar) { return FMOD_Sound_SetVariations(this, frequencyvar, volumevar, panvar); } - FMOD_RESULT getVariations (float *frequencyvar, float *volumevar, float *panvar) { return FMOD_Sound_GetVariations(this, frequencyvar, volumevar, panvar); } - FMOD_RESULT set3DMinMaxDistance (float min, float max) { return FMOD_Sound_Set3DMinMaxDistance(this, min, max); } - FMOD_RESULT get3DMinMaxDistance (float *min, float *max) { return FMOD_Sound_Get3DMinMaxDistance(this, min, max); } - FMOD_RESULT set3DConeSettings (float insideconeangle, float outsideconeangle, float outsidevolume) { return FMOD_Sound_Set3DConeSettings(this, insideconeangle, outsideconeangle, outsidevolume); } - FMOD_RESULT get3DConeSettings (float *insideconeangle, float *outsideconeangle, float *outsidevolume) { return FMOD_Sound_Get3DConeSettings(this, insideconeangle, outsideconeangle, outsidevolume); } - FMOD_RESULT set3DCustomRolloff (FMOD_VECTOR *points, int numpoints) { return FMOD_Sound_Set3DCustomRolloff(this, points, numpoints); } - FMOD_RESULT get3DCustomRolloff (FMOD_VECTOR **points, int *numpoints) { return FMOD_Sound_Get3DCustomRolloff(this, points, numpoints); } - FMOD_RESULT setSubSound (int index, Sound *subsound) { return FMOD_Sound_SetSubSound(this, index, subsound); } - FMOD_RESULT getSubSound (int index, Sound **subsound) { return FMOD_Sound_GetSubSound(this, index, (FMOD_SOUND **)subsound); } - FMOD_RESULT setSubSoundSentence (int *subsoundlist, int numsubsounds) { return FMOD_Sound_SetSubSoundSentence(this, subsoundlist, numsubsounds); } - FMOD_RESULT getName (char *name, int namelen) { return FMOD_Sound_GetName(this, name, namelen); } - FMOD_RESULT getLength (unsigned int *length, FMOD_TIMEUNIT lengthtype) { return FMOD_Sound_GetLength(this, length, lengthtype); } - FMOD_RESULT getFormat (FMOD_SOUND_TYPE *type, FMOD_SOUND_FORMAT *format, int *channels, int *bits) { return FMOD_Sound_GetFormat(this, type, format, channels, bits); } - FMOD_RESULT getNumSubSounds (int *numsubsounds) { return FMOD_Sound_GetNumSubSounds(this, numsubsounds); } - FMOD_RESULT getNumTags (int *numtags, int *numtagsupdated) { return FMOD_Sound_GetNumTags(this, numtags, numtagsupdated); } - FMOD_RESULT getTag (const char *name, int index, FMOD_TAG *tag) { return FMOD_Sound_GetTag(this, name, index, tag); } - FMOD_RESULT getOpenState (FMOD_OPENSTATE *openstate, unsigned int *percentbuffered, bool *starving) { FMOD_BOOL b; FMOD_RESULT res = FMOD_Sound_GetOpenState(this, openstate, percentbuffered, &b); *starving = b; return res; } - FMOD_RESULT readData (void *buffer, unsigned int lenbytes, unsigned int *read) { return FMOD_Sound_ReadData(this, buffer, lenbytes, read); } - FMOD_RESULT seekData (unsigned int pcm) { return FMOD_Sound_SeekData(this, pcm); } - - FMOD_RESULT setSoundGroup (SoundGroup *soundgroup) { return FMOD_Sound_SetSoundGroup(this, (FMOD_SOUNDGROUP *)soundgroup); } - FMOD_RESULT getSoundGroup (SoundGroup **soundgroup) { return FMOD_Sound_GetSoundGroup(this, (FMOD_SOUNDGROUP **)soundgroup); } - - // Synchronization point API. These points can come from markers embedded in wav files, and can also generate channel callbacks. - FMOD_RESULT getNumSyncPoints (int *numsyncpoints) { return FMOD_Sound_GetNumSyncPoints(this, numsyncpoints); } - FMOD_RESULT getSyncPoint (int index, FMOD_SYNCPOINT **point) { return FMOD_Sound_GetSyncPoint(this, index, point); } - FMOD_RESULT getSyncPointInfo (FMOD_SYNCPOINT *point, char *name, int namelen, unsigned int *offset, FMOD_TIMEUNIT offsettype) { return FMOD_Sound_GetSyncPointInfo(this, point, name, namelen, offset, offsettype); } - FMOD_RESULT addSyncPoint (unsigned int offset, FMOD_TIMEUNIT offsettype, const char *name, FMOD_SYNCPOINT **point) { return FMOD_Sound_AddSyncPoint(this, offset, offsettype, name, point); } - FMOD_RESULT deleteSyncPoint (FMOD_SYNCPOINT *point) { return FMOD_Sound_DeleteSyncPoint(this, point); } - - // Functions also in Channel class but here they are the 'default' to save having to change it in Channel all the time. - FMOD_RESULT setMode (FMOD_MODE mode) { return FMOD_Sound_SetMode(this, mode); } - FMOD_RESULT getMode (FMOD_MODE *mode) { return FMOD_Sound_GetMode(this, mode); } - FMOD_RESULT setLoopCount (int loopcount) { return FMOD_Sound_SetLoopCount(this, loopcount); } - FMOD_RESULT getLoopCount (int *loopcount) { return FMOD_Sound_GetLoopCount(this, loopcount); } - FMOD_RESULT setLoopPoints (unsigned int loopstart, FMOD_TIMEUNIT loopstarttype, unsigned int loopend, FMOD_TIMEUNIT loopendtype) { return FMOD_Sound_SetLoopPoints(this, loopstart, loopstarttype, loopend, loopendtype); } - FMOD_RESULT getLoopPoints (unsigned int *loopstart, FMOD_TIMEUNIT loopstarttype, unsigned int *loopend, FMOD_TIMEUNIT loopendtype) { return FMOD_Sound_GetLoopPoints(this, loopstart, loopstarttype, loopend, loopendtype); } - - // Userdata set/get. - FMOD_RESULT setUserData (void *userdata) { return FMOD_Sound_SetUserData(this, userdata); } - FMOD_RESULT getUserData (void **userdata) { return FMOD_Sound_GetUserData(this, userdata); } - - FMOD_RESULT getMemoryInfo (unsigned int memorybits, unsigned int event_memorybits, unsigned int *memoryused, unsigned int *memoryused_array) { return FMOD_Sound_GetMemoryInfo(this, memorybits, event_memorybits, memoryused, memoryused_array); } - }; - - /* - 'Channel' API. - */ - class Channel : FMOD_CHANNEL - { - private: - - Channel(); /* Constructor made private so user cannot statically instance a Channel class. - Appropriate Channel creation or retrieval function must be used. */ - public: - - FMOD_RESULT getSystemObject (System **system) { return FMOD_Channel_GetSystemObject(this, (FMOD_SYSTEM **)system); } - - FMOD_RESULT stop () { return FMOD_Channel_Stop(this); } - FMOD_RESULT setPaused (bool paused) { return FMOD_Channel_SetPaused(this, paused); } - FMOD_RESULT getPaused (bool *paused) { FMOD_BOOL b; FMOD_RESULT res = FMOD_Channel_GetPaused(this, &b); *paused = b; return res; } - FMOD_RESULT setVolume (float volume) { return FMOD_Channel_SetVolume(this, volume); } - FMOD_RESULT getVolume (float *volume) { return FMOD_Channel_GetVolume(this, volume); } - FMOD_RESULT setFrequency (float frequency) { return FMOD_Channel_SetFrequency(this, frequency); } - FMOD_RESULT getFrequency (float *frequency) { return FMOD_Channel_GetFrequency(this, frequency); } - FMOD_RESULT setPan (float pan) { return FMOD_Channel_SetPan(this, pan); } - FMOD_RESULT getPan (float *pan) { return FMOD_Channel_GetPan(this, pan); } - FMOD_RESULT setDelay (FMOD_DELAYTYPE delaytype, unsigned int delayhi, unsigned int delaylo) { return FMOD_Channel_SetDelay(this, delaytype, delayhi, delaylo); } - FMOD_RESULT getDelay (FMOD_DELAYTYPE delaytype, unsigned int *delayhi, unsigned int *delaylo) { return FMOD_Channel_GetDelay(this, delaytype, delayhi, delaylo); } - FMOD_RESULT setSpeakerMix (float frontleft, float frontright, float center, float lfe, float backleft, float backright, float sideleft, float sideright) { return FMOD_Channel_SetSpeakerMix(this, frontleft, frontright, center, lfe, backleft, backright, sideleft, sideright); } - FMOD_RESULT getSpeakerMix (float *frontleft, float *frontright, float *center, float *lfe, float *backleft, float *backright, float *sideleft, float *sideright) { return FMOD_Channel_GetSpeakerMix(this, frontleft, frontright, center, lfe, backleft, backright, sideleft, sideright); } - FMOD_RESULT setSpeakerLevels (FMOD_SPEAKER speaker, float *levels, int numlevels) { return FMOD_Channel_SetSpeakerLevels(this, speaker, levels, numlevels); } - FMOD_RESULT getSpeakerLevels (FMOD_SPEAKER speaker, float *levels, int numlevels) { return FMOD_Channel_GetSpeakerLevels(this, speaker, levels, numlevels); } - FMOD_RESULT setInputChannelMix (float *levels, int numlevels) { return FMOD_Channel_SetInputChannelMix(this, levels, numlevels); } - FMOD_RESULT getInputChannelMix (float *levels, int numlevels) { return FMOD_Channel_GetInputChannelMix(this, levels, numlevels); } - FMOD_RESULT setMute (bool mute) { return FMOD_Channel_SetMute(this, mute); } - FMOD_RESULT getMute (bool *mute) { FMOD_BOOL b; FMOD_RESULT res = FMOD_Channel_GetMute(this, &b); *mute = b; return res; } - FMOD_RESULT setPriority (int priority) { return FMOD_Channel_SetPriority(this, priority); } - FMOD_RESULT getPriority (int *priority) { return FMOD_Channel_GetPriority(this, priority); } - FMOD_RESULT setPosition (unsigned int position, FMOD_TIMEUNIT postype) { return FMOD_Channel_SetPosition(this, position, postype); } - FMOD_RESULT getPosition (unsigned int *position, FMOD_TIMEUNIT postype) { return FMOD_Channel_GetPosition(this, position, postype); } - FMOD_RESULT setReverbProperties (const FMOD_REVERB_CHANNELPROPERTIES *prop) { return FMOD_Channel_SetReverbProperties(this, prop); } - FMOD_RESULT getReverbProperties (FMOD_REVERB_CHANNELPROPERTIES *prop) { return FMOD_Channel_GetReverbProperties(this, prop); } - - FMOD_RESULT setChannelGroup (ChannelGroup *channelgroup) { return FMOD_Channel_SetChannelGroup(this, (FMOD_CHANNELGROUP *)channelgroup); } - FMOD_RESULT getChannelGroup (ChannelGroup **channelgroup) { return FMOD_Channel_GetChannelGroup(this, (FMOD_CHANNELGROUP **)channelgroup); } - FMOD_RESULT setCallback (FMOD_CHANNEL_CALLBACK callback) { return FMOD_Channel_SetCallback(this, callback); } - FMOD_RESULT setLowPassGain (float gain) { return FMOD_Channel_SetLowPassGain(this, gain); } - FMOD_RESULT getLowPassGain (float *gain) { return FMOD_Channel_GetLowPassGain(this, gain); } - - // 3D functionality. - FMOD_RESULT set3DAttributes (const FMOD_VECTOR *pos, const FMOD_VECTOR *vel) { return FMOD_Channel_Set3DAttributes(this, pos, vel); } - FMOD_RESULT get3DAttributes (FMOD_VECTOR *pos, FMOD_VECTOR *vel) { return FMOD_Channel_Get3DAttributes(this, pos, vel); } - FMOD_RESULT set3DMinMaxDistance (float mindistance, float maxdistance) { return FMOD_Channel_Set3DMinMaxDistance(this, mindistance, maxdistance); } - FMOD_RESULT get3DMinMaxDistance (float *mindistance, float *maxdistance) { return FMOD_Channel_Get3DMinMaxDistance(this, mindistance, maxdistance); } - FMOD_RESULT set3DConeSettings (float insideconeangle, float outsideconeangle, float outsidevolume) { return FMOD_Channel_Set3DConeSettings(this, insideconeangle, outsideconeangle, outsidevolume); } - FMOD_RESULT get3DConeSettings (float *insideconeangle, float *outsideconeangle, float *outsidevolume) { return FMOD_Channel_Get3DConeSettings(this, insideconeangle, outsideconeangle, outsidevolume); } - FMOD_RESULT set3DConeOrientation (FMOD_VECTOR *orientation) { return FMOD_Channel_Set3DConeOrientation(this, orientation); } - FMOD_RESULT get3DConeOrientation (FMOD_VECTOR *orientation) { return FMOD_Channel_Get3DConeOrientation(this, orientation); } - FMOD_RESULT set3DCustomRolloff (FMOD_VECTOR *points, int numpoints) { return FMOD_Channel_Set3DCustomRolloff(this, points, numpoints); } - FMOD_RESULT get3DCustomRolloff (FMOD_VECTOR **points, int *numpoints) { return FMOD_Channel_Get3DCustomRolloff(this, points, numpoints); } - FMOD_RESULT set3DOcclusion (float directocclusion, float reverbocclusion) { return FMOD_Channel_Set3DOcclusion(this, directocclusion, reverbocclusion); } - FMOD_RESULT get3DOcclusion (float *directocclusion, float *reverbocclusion) { return FMOD_Channel_Get3DOcclusion(this, directocclusion, reverbocclusion); } - FMOD_RESULT set3DSpread (float angle) { return FMOD_Channel_Set3DSpread(this, angle); } - FMOD_RESULT get3DSpread (float *angle) { return FMOD_Channel_Get3DSpread(this, angle); } - FMOD_RESULT set3DPanLevel (float level) { return FMOD_Channel_Set3DPanLevel(this, level); } - FMOD_RESULT get3DPanLevel (float *level) { return FMOD_Channel_Get3DPanLevel(this, level); } - FMOD_RESULT set3DDopplerLevel (float level) { return FMOD_Channel_Set3DDopplerLevel(this, level); } - FMOD_RESULT get3DDopplerLevel (float *level) { return FMOD_Channel_Get3DDopplerLevel(this, level); } - - // DSP functionality only for channels playing sounds created with FMOD_SOFTWARE. - FMOD_RESULT getDSPHead (DSP **dsp) { return FMOD_Channel_GetDSPHead(this, (FMOD_DSP **)dsp); } - FMOD_RESULT addDSP (DSP *dsp, DSPConnection **connection) { return FMOD_Channel_AddDSP(this, (FMOD_DSP *)dsp, (FMOD_DSPCONNECTION **)connection); } - - // Information only functions. - FMOD_RESULT isPlaying (bool *isplaying) { FMOD_BOOL b; FMOD_RESULT res = FMOD_Channel_IsPlaying(this, &b); *isplaying = b; return res; } - FMOD_RESULT isVirtual (bool *isvirtual) { FMOD_BOOL b; FMOD_RESULT res = FMOD_Channel_IsVirtual(this, &b); *isvirtual = b; return res; } - FMOD_RESULT getAudibility (float *audibility) { return FMOD_Channel_GetAudibility(this, audibility); } - FMOD_RESULT getCurrentSound (Sound **sound) { return FMOD_Channel_GetCurrentSound(this, (FMOD_SOUND **)sound); } - FMOD_RESULT getSpectrum (float *spectrumarray, int numvalues, int channeloffset, FMOD_DSP_FFT_WINDOW windowtype) { return FMOD_Channel_GetSpectrum(this, spectrumarray, numvalues, channeloffset, windowtype); } - FMOD_RESULT getWaveData (float *wavearray, int numvalues, int channeloffset) { return FMOD_Channel_GetWaveData(this, wavearray, numvalues, channeloffset); } - FMOD_RESULT getIndex (int *index) { return FMOD_Channel_GetIndex(this, index); } - - // Functions also found in Sound class but here they can be set per channel. - FMOD_RESULT setMode (FMOD_MODE mode) { return FMOD_Channel_SetMode(this, mode); } - FMOD_RESULT getMode (FMOD_MODE *mode) { return FMOD_Channel_GetMode(this, mode); } - FMOD_RESULT setLoopCount (int loopcount) { return FMOD_Channel_SetLoopCount(this, loopcount); } - FMOD_RESULT getLoopCount (int *loopcount) { return FMOD_Channel_GetLoopCount(this, loopcount); } - FMOD_RESULT setLoopPoints (unsigned int loopstart, FMOD_TIMEUNIT loopstarttype, unsigned int loopend, FMOD_TIMEUNIT loopendtype) { return FMOD_Channel_SetLoopPoints(this, loopstart, loopstarttype, loopend, loopendtype); } - FMOD_RESULT getLoopPoints (unsigned int *loopstart, FMOD_TIMEUNIT loopstarttype, unsigned int *loopend, FMOD_TIMEUNIT loopendtype) { return FMOD_Channel_GetLoopPoints(this, loopstart, loopstarttype, loopend, loopendtype); } - - // Userdata set/get. - FMOD_RESULT setUserData (void *userdata) { return FMOD_Channel_SetUserData(this, userdata); } - FMOD_RESULT getUserData (void **userdata) { return FMOD_Channel_GetUserData(this, userdata); } - - FMOD_RESULT getMemoryInfo (unsigned int memorybits, unsigned int event_memorybits, unsigned int *memoryused, unsigned int *memoryused_array) { return FMOD_Channel_GetMemoryInfo(this, memorybits, event_memorybits, memoryused, memoryused_array); } - }; - - /* - 'ChannelGroup' API - */ - class ChannelGroup : FMOD_CHANNELGROUP - { - private: - - ChannelGroup(); /* Constructor made private so user cannot statically instance a ChannelGroup class. - Appropriate ChannelGroup creation or retrieval function must be used. */ - public: - - FMOD_RESULT release () { return FMOD_ChannelGroup_Release(this); } - FMOD_RESULT getSystemObject (System **system) { return FMOD_ChannelGroup_GetSystemObject(this, (FMOD_SYSTEM **)system); } - - // Channelgroup scale values. (changes attributes relative to the channels, doesn't overwrite them) - FMOD_RESULT setVolume (float volume) { return FMOD_ChannelGroup_SetVolume(this, volume); } - FMOD_RESULT getVolume (float *volume) { return FMOD_ChannelGroup_GetVolume(this, volume); } - FMOD_RESULT setPitch (float pitch) { return FMOD_ChannelGroup_SetPitch(this, pitch); } - FMOD_RESULT getPitch (float *pitch) { return FMOD_ChannelGroup_GetPitch(this, pitch); } - FMOD_RESULT set3DOcclusion (float directocclusion, float reverbocclusion) { return FMOD_ChannelGroup_Set3DOcclusion(this, directocclusion, reverbocclusion); } - FMOD_RESULT get3DOcclusion (float *directocclusion, float *reverbocclusion) { return FMOD_ChannelGroup_Get3DOcclusion(this, directocclusion, reverbocclusion); } - FMOD_RESULT setPaused (bool paused) { return FMOD_ChannelGroup_SetPaused(this, paused); } - FMOD_RESULT getPaused (bool *paused) { FMOD_BOOL b; FMOD_RESULT res = FMOD_ChannelGroup_GetPaused(this, &b); *paused = b; return res; } - FMOD_RESULT setMute (bool mute) { return FMOD_ChannelGroup_SetMute(this, mute); } - FMOD_RESULT getMute (bool *mute) { FMOD_BOOL b; FMOD_RESULT res = FMOD_ChannelGroup_GetMute(this, &b); *mute = b; return res; } - - // Channelgroup override values. (recursively overwrites whatever settings the channels had) - FMOD_RESULT stop () { return FMOD_ChannelGroup_Stop(this); } - FMOD_RESULT overrideVolume (float volume) { return FMOD_ChannelGroup_OverrideVolume(this, volume); } - FMOD_RESULT overrideFrequency (float frequency) { return FMOD_ChannelGroup_OverrideFrequency(this, frequency); } - FMOD_RESULT overridePan (float pan) { return FMOD_ChannelGroup_OverridePan(this, pan); } - FMOD_RESULT overrideReverbProperties(const FMOD_REVERB_CHANNELPROPERTIES *prop) { return FMOD_ChannelGroup_OverrideReverbProperties(this, prop); } - FMOD_RESULT override3DAttributes (const FMOD_VECTOR *pos, const FMOD_VECTOR *vel) { return FMOD_ChannelGroup_Override3DAttributes(this, pos, vel); } - FMOD_RESULT overrideSpeakerMix (float frontleft, float frontright, float center, float lfe, float backleft, float backright, float sideleft, float sideright) { return FMOD_ChannelGroup_OverrideSpeakerMix(this, frontleft, frontright, center, lfe, backleft, backright, sideleft, sideright); } - - // Nested channel groups. - FMOD_RESULT addGroup (ChannelGroup *group) { return FMOD_ChannelGroup_AddGroup(this, group); } - FMOD_RESULT getNumGroups (int *numgroups) { return FMOD_ChannelGroup_GetNumGroups(this, numgroups); } - FMOD_RESULT getGroup (int index, ChannelGroup **group) { return FMOD_ChannelGroup_GetGroup(this, index, (FMOD_CHANNELGROUP **)group); } - FMOD_RESULT getParentGroup (ChannelGroup **group) { return FMOD_ChannelGroup_GetParentGroup(this, (FMOD_CHANNELGROUP **)group); } - - // DSP functionality only for channel groups playing sounds created with FMOD_SOFTWARE. - FMOD_RESULT getDSPHead (DSP **dsp) { return FMOD_ChannelGroup_GetDSPHead(this, (FMOD_DSP **)dsp); } - FMOD_RESULT addDSP (DSP *dsp, DSPConnection **connection) { return FMOD_ChannelGroup_AddDSP(this, (FMOD_DSP *)dsp, (FMOD_DSPCONNECTION **)connection); } - - // Information only functions. - FMOD_RESULT getName (char *name, int namelen) { return FMOD_ChannelGroup_GetName(this, name, namelen); } - FMOD_RESULT getNumChannels (int *numchannels) { return FMOD_ChannelGroup_GetNumChannels(this, numchannels); } - FMOD_RESULT getChannel (int index, Channel **channel) { return FMOD_ChannelGroup_GetChannel(this, index, (FMOD_CHANNEL **)channel); } - FMOD_RESULT getSpectrum (float *spectrumarray, int numvalues, int channeloffset, FMOD_DSP_FFT_WINDOW windowtype) { return FMOD_ChannelGroup_GetSpectrum(this, spectrumarray, numvalues, channeloffset, windowtype); } - FMOD_RESULT getWaveData (float *wavearray, int numvalues, int channeloffset) { return FMOD_ChannelGroup_GetWaveData(this, wavearray, numvalues, channeloffset) ;} - - // Userdata set/get. - FMOD_RESULT setUserData (void *userdata) { return FMOD_ChannelGroup_SetUserData(this, userdata); } - FMOD_RESULT getUserData (void **userdata) { return FMOD_ChannelGroup_GetUserData(this, userdata); } - - FMOD_RESULT getMemoryInfo (unsigned int memorybits, unsigned int event_memorybits, unsigned int *memoryused, unsigned int *memoryused_array) { return FMOD_ChannelGroup_GetMemoryInfo(this, memorybits, event_memorybits, memoryused, memoryused_array); } - }; - - /* - 'SoundGroup' API - */ - class SoundGroup : FMOD_SOUNDGROUP - { - private: - - SoundGroup(); /* Constructor made private so user cannot statically instance a SoundGroup class. - Appropriate SoundGroup creation or retrieval function must be used. */ - public: - - FMOD_RESULT release () { return FMOD_SoundGroup_Release(this); } - FMOD_RESULT getSystemObject (System **system) { return FMOD_SoundGroup_GetSystemObject(this, (FMOD_SYSTEM **)system); } - - // SoundGroup control functions. - FMOD_RESULT setMaxAudible (int maxaudible) { return FMOD_SoundGroup_SetMaxAudible(this, maxaudible); } - FMOD_RESULT getMaxAudible (int *maxaudible) { return FMOD_SoundGroup_GetMaxAudible(this, maxaudible); } - FMOD_RESULT setMaxAudibleBehavior (FMOD_SOUNDGROUP_BEHAVIOR behavior) { return FMOD_SoundGroup_SetMaxAudibleBehavior(this, behavior); } - FMOD_RESULT getMaxAudibleBehavior (FMOD_SOUNDGROUP_BEHAVIOR *behavior) { return FMOD_SoundGroup_GetMaxAudibleBehavior(this, behavior); } - FMOD_RESULT setMuteFadeSpeed (float speed) { return FMOD_SoundGroup_SetMuteFadeSpeed(this, speed); } - FMOD_RESULT getMuteFadeSpeed (float *speed) { return FMOD_SoundGroup_GetMuteFadeSpeed(this, speed); } - FMOD_RESULT setVolume (float volume) { return FMOD_SoundGroup_SetVolume(this, volume); } - FMOD_RESULT getVolume (float *volume) { return FMOD_SoundGroup_GetVolume(this, volume); } - FMOD_RESULT stop () { return FMOD_SoundGroup_Stop(this); } - - // Information only functions. - FMOD_RESULT getName (char *name, int namelen) { return FMOD_SoundGroup_GetName(this, name, namelen); } - FMOD_RESULT getNumSounds (int *numsounds) { return FMOD_SoundGroup_GetNumSounds(this, numsounds); } - FMOD_RESULT getSound (int index, Sound **sound) { return FMOD_SoundGroup_GetSound(this, index, (FMOD_SOUND **)sound); } - FMOD_RESULT getNumPlaying (int *numplaying) { return FMOD_SoundGroup_GetNumPlaying(this, numplaying); } - - // Userdata set/get. - FMOD_RESULT setUserData (void *userdata) { return FMOD_SoundGroup_SetUserData(this, userdata); } - FMOD_RESULT getUserData (void **userdata) { return FMOD_SoundGroup_GetUserData(this, userdata); } - - FMOD_RESULT getMemoryInfo (unsigned int memorybits, unsigned int event_memorybits, unsigned int *memoryused, unsigned int *memoryused_array) { return FMOD_SoundGroup_GetMemoryInfo(this, memorybits, event_memorybits, memoryused, memoryused_array); } - }; - - /* - 'DSP' API - */ - class DSP : FMOD_DSP - { - private: - - DSP(); /* Constructor made private so user cannot statically instance a DSP class. - Appropriate DSP creation or retrieval function must be used. */ - public: - - FMOD_RESULT release () { return FMOD_DSP_Release(this); } - FMOD_RESULT getSystemObject (System **system) { return FMOD_DSP_GetSystemObject(this, (FMOD_SYSTEM **)system); } - - // Connection / disconnection / input and output enumeration. - FMOD_RESULT addInput (DSP *target, DSPConnection **connection) { return FMOD_DSP_AddInput(this, target, (FMOD_DSPCONNECTION **)connection); } - FMOD_RESULT disconnectFrom (DSP *target) { return FMOD_DSP_DisconnectFrom(this, target); } - FMOD_RESULT disconnectAll (bool inputs, bool outputs) { return FMOD_DSP_DisconnectAll(this, inputs, outputs); } - FMOD_RESULT remove () { return FMOD_DSP_Remove(this); } - FMOD_RESULT getNumInputs (int *numinputs) { return FMOD_DSP_GetNumInputs(this, numinputs); } - FMOD_RESULT getNumOutputs (int *numoutputs) { return FMOD_DSP_GetNumOutputs(this, numoutputs); } - FMOD_RESULT getInput (int index, DSP **input, DSPConnection **inputconnection) { return FMOD_DSP_GetInput(this, index, (FMOD_DSP **)input, (FMOD_DSPCONNECTION **)inputconnection); } - FMOD_RESULT getOutput (int index, DSP **output, DSPConnection **outputconnection) { return FMOD_DSP_GetOutput(this, index, (FMOD_DSP **)output, (FMOD_DSPCONNECTION **)outputconnection); } - - // DSP unit control. - FMOD_RESULT setActive (bool active) { return FMOD_DSP_SetActive(this, active); } - FMOD_RESULT getActive (bool *active) { FMOD_BOOL b; FMOD_RESULT res = FMOD_DSP_GetActive(this, &b); *active = b; return res; } - FMOD_RESULT setBypass (bool bypass) { return FMOD_DSP_SetBypass(this, bypass); } - FMOD_RESULT getBypass (bool *bypass) { FMOD_BOOL b; FMOD_RESULT res = FMOD_DSP_GetBypass(this, &b); *bypass = b; return res; } - FMOD_RESULT setSpeakerActive (FMOD_SPEAKER speaker, bool active) { return FMOD_DSP_SetSpeakerActive(this, speaker, active); } - FMOD_RESULT getSpeakerActive (FMOD_SPEAKER speaker, bool *active) { FMOD_BOOL b; FMOD_RESULT res = FMOD_DSP_GetSpeakerActive(this, speaker, &b); *active = b; return res; } - FMOD_RESULT reset () { return FMOD_DSP_Reset(this); } - - // DSP parameter control. - FMOD_RESULT setParameter (int index, float value) { return FMOD_DSP_SetParameter(this, index, value); } - FMOD_RESULT getParameter (int index, float *value, char *valuestr, int valuestrlen) { return FMOD_DSP_GetParameter(this, index, value, valuestr, valuestrlen); } - FMOD_RESULT getNumParameters (int *numparams) { return FMOD_DSP_GetNumParameters(this, numparams); } - FMOD_RESULT getParameterInfo (int index, char *name, char *label, char *description, int descriptionlen, float *min, float *max) { return FMOD_DSP_GetParameterInfo(this, index, name, label, description, descriptionlen, min, max); } - FMOD_RESULT showConfigDialog (void *hwnd, bool show) { return FMOD_DSP_ShowConfigDialog(this, hwnd, show); } - - // DSP attributes. - FMOD_RESULT getInfo (char *name, unsigned int *version, int *channels, int *configwidth, int *configheight) { return FMOD_DSP_GetInfo(this, name, version, channels, configwidth, configheight); } - FMOD_RESULT getType (FMOD_DSP_TYPE *type) { return FMOD_DSP_GetType(this, type); } - FMOD_RESULT setDefaults (float frequency, float volume, float pan, int priority) { return FMOD_DSP_SetDefaults(this, frequency, volume, pan, priority); } - FMOD_RESULT getDefaults (float *frequency, float *volume, float *pan, int *priority) { return FMOD_DSP_GetDefaults(this, frequency, volume, pan, priority) ;} - - // Userdata set/get. - FMOD_RESULT setUserData (void *userdata) { return FMOD_DSP_SetUserData(this, userdata); } - FMOD_RESULT getUserData (void **userdata) { return FMOD_DSP_GetUserData(this, userdata); } - - FMOD_RESULT getMemoryInfo (unsigned int memorybits, unsigned int event_memorybits, unsigned int *memoryused, unsigned int *memoryused_array) { return FMOD_DSP_GetMemoryInfo(this, memorybits, event_memorybits, memoryused, memoryused_array); } - }; - - - /* - 'DSPConnection' API - */ - class DSPConnection : FMOD_DSPCONNECTION - { - private: - - DSPConnection(); /* Constructor made private so user cannot statically instance a DSPConnection class. - Appropriate DSPConnection creation or retrieval function must be used. */ - - public: - - FMOD_RESULT getInput (DSP **input) { return FMOD_DSPConnection_GetInput(this, (FMOD_DSP **)input); } - FMOD_RESULT getOutput (DSP **output) { return FMOD_DSPConnection_GetOutput(this, (FMOD_DSP **)output); } - FMOD_RESULT setMix (float volume) { return FMOD_DSPConnection_SetMix(this, volume); } - FMOD_RESULT getMix (float *volume) { return FMOD_DSPConnection_GetMix(this, volume); } - FMOD_RESULT setLevels (FMOD_SPEAKER speaker, float *levels, int numlevels) { return FMOD_DSPConnection_SetLevels(this, speaker, levels, numlevels); } - FMOD_RESULT getLevels (FMOD_SPEAKER speaker, float *levels, int numlevels) { return FMOD_DSPConnection_GetLevels(this, speaker, levels, numlevels); } - - // Userdata set/get. - FMOD_RESULT setUserData (void *userdata) { return FMOD_DSPConnection_SetUserData(this, userdata); } - FMOD_RESULT getUserData (void **userdata) { return FMOD_DSPConnection_GetUserData(this, userdata); } - - FMOD_RESULT getMemoryInfo (unsigned int memorybits, unsigned int event_memorybits, unsigned int *memoryused, unsigned int *memoryused_array) { return FMOD_DSPConnection_GetMemoryInfo(this, memorybits, event_memorybits, memoryused, memoryused_array); } - }; - - - /* - 'Geometry' API - */ - class Geometry : FMOD_GEOMETRY - { - private: - - Geometry(); /* Constructor made private so user cannot statically instance a Geometry class. - Appropriate Geometry creation or retrieval function must be used. */ - - public: - - FMOD_RESULT release () { return FMOD_Geometry_Release(this); } - - // Polygon manipulation. - FMOD_RESULT addPolygon (float directocclusion, float reverbocclusion, bool doublesided, int numvertices, const FMOD_VECTOR *vertices, int *polygonindex) { return FMOD_Geometry_AddPolygon(this, directocclusion, reverbocclusion, doublesided, numvertices, vertices, polygonindex); } - FMOD_RESULT getNumPolygons (int *numpolygons) { return FMOD_Geometry_GetNumPolygons(this, numpolygons); } - FMOD_RESULT getMaxPolygons (int *maxpolygons, int *maxvertices) { return FMOD_Geometry_GetMaxPolygons(this, maxpolygons, maxvertices); } - FMOD_RESULT getPolygonNumVertices (int index, int *numvertices) { return FMOD_Geometry_GetPolygonNumVertices(this, index, numvertices); } - FMOD_RESULT setPolygonVertex (int index, int vertexindex, const FMOD_VECTOR *vertex) { return FMOD_Geometry_SetPolygonVertex(this, index, vertexindex, vertex); } - FMOD_RESULT getPolygonVertex (int index, int vertexindex, FMOD_VECTOR *vertex) { return FMOD_Geometry_GetPolygonVertex(this, index, vertexindex, vertex); } - FMOD_RESULT setPolygonAttributes (int index, float directocclusion, float reverbocclusion, bool doublesided) { return FMOD_Geometry_SetPolygonAttributes(this, index, directocclusion, reverbocclusion, doublesided); } - FMOD_RESULT getPolygonAttributes (int index, float *directocclusion, float *reverbocclusion, bool *doublesided) { FMOD_BOOL b; FMOD_RESULT res = FMOD_Geometry_GetPolygonAttributes(this, index, directocclusion, reverbocclusion, &b); *doublesided = b; return res; } - - // Object manipulation. - FMOD_RESULT setActive (bool active) { return FMOD_Geometry_SetActive(this, active); } - FMOD_RESULT getActive (bool *active) { FMOD_BOOL b; FMOD_RESULT res = FMOD_Geometry_GetActive(this, &b); *active = b; return res; } - FMOD_RESULT setRotation (const FMOD_VECTOR *forward, const FMOD_VECTOR *up) { return FMOD_Geometry_SetRotation(this, forward, up); } - FMOD_RESULT getRotation (FMOD_VECTOR *forward, FMOD_VECTOR *up) { return FMOD_Geometry_GetRotation(this, forward, up); } - FMOD_RESULT setPosition (const FMOD_VECTOR *position) { return FMOD_Geometry_SetPosition(this, position); } - FMOD_RESULT getPosition (FMOD_VECTOR *position) { return FMOD_Geometry_GetPosition(this, position); } - FMOD_RESULT setScale (const FMOD_VECTOR *scale) { return FMOD_Geometry_SetScale(this, scale); } - FMOD_RESULT getScale (FMOD_VECTOR *scale) { return FMOD_Geometry_GetScale(this, scale); } - FMOD_RESULT save (void *data, int *datasize) { return FMOD_Geometry_Save(this, data, datasize); } - - // Userdata set/get. - FMOD_RESULT setUserData (void *userdata) { return FMOD_Geometry_SetUserData(this, userdata); } - FMOD_RESULT getUserData (void **userdata) { return FMOD_Geometry_GetUserData(this, userdata); } - - FMOD_RESULT getMemoryInfo (unsigned int memorybits, unsigned int event_memorybits, unsigned int *memoryused, unsigned int *memoryused_array) { return FMOD_Geometry_GetMemoryInfo(this, memorybits, event_memorybits, memoryused, memoryused_array); } - }; - - - /* - 'Reverb' API - */ - class Reverb : FMOD_REVERB - { - private: - - Reverb(); /* Constructor made private so user cannot statically instance a Reverb class. - Appropriate Reverb creation or retrieval function must be used. */ - - public: - - FMOD_RESULT release () { return FMOD_Reverb_Release(this); } - - // Reverb manipulation. - FMOD_RESULT set3DAttributes (const FMOD_VECTOR *position, float mindistance, float maxdistance) { return FMOD_Reverb_Set3DAttributes(this, position, mindistance, maxdistance); } - FMOD_RESULT get3DAttributes (FMOD_VECTOR *position, float *mindistance, float *maxdistance) { return FMOD_Reverb_Get3DAttributes(this, position, mindistance, maxdistance); } - FMOD_RESULT setProperties (const FMOD_REVERB_PROPERTIES *properties) { return FMOD_Reverb_SetProperties(this, properties); } - FMOD_RESULT getProperties (FMOD_REVERB_PROPERTIES *properties) { return FMOD_Reverb_GetProperties(this, properties); } - FMOD_RESULT setActive (bool active) { return FMOD_Reverb_SetActive(this, active); } - FMOD_RESULT getActive (bool *active) { FMOD_BOOL b; FMOD_RESULT res = FMOD_Reverb_GetActive(this, &b); *active = b; return res; } - - // Userdata set/get. - FMOD_RESULT setUserData (void *userdata) { return FMOD_Reverb_SetUserData(this, userdata); } - FMOD_RESULT getUserData (void **userdata) { return FMOD_Reverb_GetUserData(this, userdata); } - - FMOD_RESULT getMemoryInfo (unsigned int memorybits, unsigned int event_memorybits, unsigned int *memoryused, unsigned int *memoryused_array) { return FMOD_Reverb_GetMemoryInfo(this, memorybits, event_memorybits, memoryused, memoryused_array); } - }; -} - -#endif - -// FMOD Ex vs FMOD Studio -#if FMOD_VERSION >= 0x00040000 && FMOD_VERSION <= 0x0004FFFF -#define FMOD_STUDIO 0 -#else -#define FMOD_STUDIO 1 -#define FMOD_SOFTWARE 0 -#endif - -#endif -#endif diff --git a/src/sound/fmodsound.cpp b/src/sound/fmodsound.cpp deleted file mode 100644 index a5972dceb..000000000 --- a/src/sound/fmodsound.cpp +++ /dev/null @@ -1,3527 +0,0 @@ -/* -** fmodsound.cpp -** System interface for sound; uses FMOD Ex. -** -**--------------------------------------------------------------------------- -** Copyright 1998-2009 Randy Heit -** All rights reserved. -** -** Redistribution and use in source and binary forms, with or without -** modification, are permitted provided that the following conditions -** are met: -** -** 1. Redistributions of source code must retain the above copyright -** notice, this list of conditions and the following disclaimer. -** 2. Redistributions in binary form must reproduce the above copyright -** notice, this list of conditions and the following disclaimer in the -** documentation and/or other materials provided with the distribution. -** 3. The name of the author may not be used to endorse or promote products -** derived from this software without specific prior written permission. -** -** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR -** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES -** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. -** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, -** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT -** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF -** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -**--------------------------------------------------------------------------- -** -*/ - -// HEADER FILES ------------------------------------------------------------ - -#ifdef _WIN32 -#define WIN32_LEAN_AND_MEAN -#include -#include -extern HWND Window; -#else -#define FALSE 0 -#define TRUE 1 -#endif -#if defined(__FreeBSD__) || defined(__APPLE__) -#include -#elif __sun -#include -#else -#include -#endif - -#include "except.h" -#include "templates.h" -#include "fmodsound.h" -#include "c_cvars.h" -#include "i_system.h" -#include "i_music.h" -#include "v_text.h" -#include "v_video.h" -#include "v_palette.h" -#include "cmdlib.h" -#include "s_sound.h" -#include "files.h" -#include "i_musicinterns.h" - -#if FMOD_VERSION > 0x42899 && FMOD_VERSION < 0x43400 -#error You are trying to compile with an unsupported version of FMOD. -#endif - -// MACROS ------------------------------------------------------------------ - -// killough 2/21/98: optionally use varying pitched sounds -#define PITCH(freq,pitch) (snd_pitched ? ((freq)*(pitch))/128.f : float(freq)) - -// Just some extra for music and whatever -#define NUM_EXTRA_SOFTWARE_CHANNELS 1 - -#define MAX_CHANNELS 256 - -#define SPECTRUM_SIZE 256 - -// PUBLIC DATA DEFINITIONS ------------------------------------------------- - -ReverbContainer *ForcedEnvironment; - -CVAR (Int, snd_driver, 0, CVAR_ARCHIVE|CVAR_GLOBALCONFIG) -CVAR (Int, snd_buffercount, 0, CVAR_ARCHIVE|CVAR_GLOBALCONFIG) -CVAR (Bool, snd_hrtf, false, CVAR_ARCHIVE|CVAR_GLOBALCONFIG) -CVAR (Bool, snd_waterreverb, true, CVAR_ARCHIVE|CVAR_GLOBALCONFIG) -CVAR (String, snd_resampler, "Linear", CVAR_ARCHIVE|CVAR_GLOBALCONFIG) -CVAR (String, snd_speakermode, "Auto", CVAR_ARCHIVE|CVAR_GLOBALCONFIG) -CVAR (String, snd_output_format, "PCM-16", CVAR_ARCHIVE|CVAR_GLOBALCONFIG) -CVAR (String, snd_midipatchset, "", CVAR_ARCHIVE|CVAR_GLOBALCONFIG) -CVAR (Bool, snd_profile, false, 0) - -// Underwater low-pass filter cutoff frequency. Set to 0 to disable the filter. -CUSTOM_CVAR (Float, snd_waterlp, 250, CVAR_ARCHIVE|CVAR_GLOBALCONFIG) -{ - // Clamp to the DSP unit's limits. - if (*self < 10 && *self != 0) - { - self = 10; - } - else if (*self > 22000) - { - self = 22000; - } -} - -CUSTOM_CVAR (Int, snd_streambuffersize, 64, CVAR_ARCHIVE|CVAR_GLOBALCONFIG) -{ - if (self < 16) - { - self = 16; - } - else if (self > 1024) - { - self = 1024; - } -} - -#ifndef NO_FMOD -#if !FMOD_STUDIO && FMOD_VERSION < 0x43400 -#define FMOD_OPENSTATE_PLAYING FMOD_OPENSTATE_STREAMING -#endif - -#if !FMOD_STUDIO -#define setParameterFloat setParameter -#endif - -// TYPES ------------------------------------------------------------------- - -struct FEnumList -{ - const char *Name; - int Value; -}; - -// EXTERNAL FUNCTION PROTOTYPES -------------------------------------------- - -// PUBLIC FUNCTION PROTOTYPES ---------------------------------------------- - -// PRIVATE FUNCTION PROTOTYPES --------------------------------------------- - -static int Enum_NumForName(const FEnumList *list, const char *name); -static const char *Enum_NameForNum(const FEnumList *list, int num); - -// EXTERNAL DATA DECLARATIONS ---------------------------------------------- - -EXTERN_CVAR (String, snd_output) -EXTERN_CVAR (Float, snd_sfxvolume) -EXTERN_CVAR (Float, snd_musicvolume) -EXTERN_CVAR (Int, snd_buffersize) -EXTERN_CVAR (Int, snd_samplerate) -EXTERN_CVAR (Bool, snd_pitched) -EXTERN_CVAR (Int, snd_channels) - -extern int sfx_empty; - -// PRIVATE DATA DEFINITIONS ------------------------------------------------ - -static const ReverbContainer *PrevEnvironment; -static bool ShowedBanner; - -// The rolloff callback is called during FMOD::Sound::play, so we need this -// global variable to contain the sound info during that time for the -// callback. -static FRolloffInfo *GRolloff; -static float GDistScale; - -// In the below lists, duplicate entries are for user selection. When -// queried, only the first one for the particular value is shown. -static const FEnumList OutputNames[] = -{ - { "Auto", FMOD_OUTPUTTYPE_AUTODETECT }, - { "Default", FMOD_OUTPUTTYPE_AUTODETECT }, - { "No sound", FMOD_OUTPUTTYPE_NOSOUND }, - - // Windows - { "DirectSound", FMOD_OUTPUTTYPE_DSOUND }, - { "DSound", FMOD_OUTPUTTYPE_DSOUND }, - { "Windows Multimedia", FMOD_OUTPUTTYPE_WINMM }, - { "WinMM", FMOD_OUTPUTTYPE_WINMM }, - { "WaveOut", FMOD_OUTPUTTYPE_WINMM }, - { "WASAPI", FMOD_OUTPUTTYPE_WASAPI }, - { "ASIO", FMOD_OUTPUTTYPE_ASIO }, - -#if FMOD_STUDIO - //Android - - { "OPENSL", FMOD_OUTPUTTYPE_OPENSL }, - { "Android Audio Track", FMOD_OUTPUTTYPE_AUDIOTRACK }, -#endif - - // Linux -#if !FMOD_STUDIO - { "OSS", FMOD_OUTPUTTYPE_OSS }, -#endif - { "ALSA", FMOD_OUTPUTTYPE_ALSA }, -#if !FMOD_STUDIO - { "ESD", FMOD_OUTPUTTYPE_ESD }, -#endif -#if FMOD_STUDIO || FMOD_VERSION >= 0x43400 - { "PulseAudio", FMOD_OUTPUTTYPE_PULSEAUDIO }, - { "Pulse", FMOD_OUTPUTTYPE_PULSEAUDIO }, -#endif -#if !FMOD_STUDIO - { "SDL", 666 }, -#endif - - // Mac - { "Core Audio", FMOD_OUTPUTTYPE_COREAUDIO }, - - { NULL, 0 } -}; - -static const FEnumList SpeakerModeNames[] = -{ - { "Mono", FMOD_SPEAKERMODE_MONO }, - { "Stereo", FMOD_SPEAKERMODE_STEREO }, - { "Quad", FMOD_SPEAKERMODE_QUAD }, - { "Surround", FMOD_SPEAKERMODE_SURROUND }, - { "5.1", FMOD_SPEAKERMODE_5POINT1 }, - { "7.1", FMOD_SPEAKERMODE_7POINT1 }, -#if !FMOD_STUDIO && FMOD_VERSION < 0x44000 - { "Prologic", FMOD_SPEAKERMODE_PROLOGIC }, -#endif - { "1", FMOD_SPEAKERMODE_MONO }, - { "2", FMOD_SPEAKERMODE_STEREO }, - { "4", FMOD_SPEAKERMODE_QUAD }, - { NULL, 0 } -}; - -static const FEnumList ResamplerNames[] = -{ - { "No Interpolation", FMOD_DSP_RESAMPLER_NOINTERP }, - { "NoInterp", FMOD_DSP_RESAMPLER_NOINTERP }, - { "Linear", FMOD_DSP_RESAMPLER_LINEAR }, - // [BL] 64-bit versions of FMOD Ex between 4.24 and 4.26 crash with these resamplers. -#if FMOD_STUDIO || !(defined(_M_X64) || defined(__amd64__)) || !(FMOD_VERSION >= 0x42400 && FMOD_VERSION <= 0x426FF) - { "Cubic", FMOD_DSP_RESAMPLER_CUBIC }, - { "Spline", FMOD_DSP_RESAMPLER_SPLINE }, -#endif - { NULL, 0 } -}; - -static const FEnumList SoundFormatNames[] = -{ - { "None", FMOD_SOUND_FORMAT_NONE }, - { "PCM-8", FMOD_SOUND_FORMAT_PCM8 }, - { "PCM-16", FMOD_SOUND_FORMAT_PCM16 }, - { "PCM-24", FMOD_SOUND_FORMAT_PCM24 }, - { "PCM-32", FMOD_SOUND_FORMAT_PCM32 }, - { "PCM-Float", FMOD_SOUND_FORMAT_PCMFLOAT }, -#if FMOD_STUDIO && FMOD_VERSION < 0x10700 - { "GCADPCM", FMOD_SOUND_FORMAT_GCADPCM }, - { "IMAADPCM", FMOD_SOUND_FORMAT_IMAADPCM }, - { "VAG", FMOD_SOUND_FORMAT_VAG }, - { "XMA", FMOD_SOUND_FORMAT_XMA }, - { "MPEG", FMOD_SOUND_FORMAT_MPEG }, -#endif - { NULL, 0 } -}; - -static const char *OpenStateNames[] = -{ - "Ready", - "Loading", - "Error", - "Connecting", - "Buffering", - "Seeking", - "Streaming" -}; - -const FMODSoundRenderer::spk FMODSoundRenderer::SpeakerNames4[4] = { "L", "R", "BL", "BR" }; -const FMODSoundRenderer::spk FMODSoundRenderer::SpeakerNamesMore[8] = { "L", "R", "C", "LFE", "BL", "BR", "SL", "SR" }; - -// CODE -------------------------------------------------------------------- - -//========================================================================== -// -// Enum_NumForName -// -// Returns the value of an enum name, or -1 if not found. -// -//========================================================================== - -static int Enum_NumForName(const FEnumList *list, const char *name) -{ - while (list->Name != NULL) - { - if (stricmp(list->Name, name) == 0) - { - return list->Value; - } - list++; - } - return -1; -} - -//========================================================================== -// -// Enum_NameForNum -// -// Returns the name of an enum value. If there is more than one name for a -// value, on the first one in the list is returned. Returns NULL if there -// was no match. -// -//========================================================================== - -static const char *Enum_NameForNum(const FEnumList *list, int num) -{ - while (list->Name != NULL) - { - if (list->Value == num) - { - return list->Name; - } - list++; - } - return NULL; -} - -//========================================================================== -// -// The container for a streaming FMOD::Sound, for playing music. -// -//========================================================================== - -class FMODStreamCapsule : public SoundStream -{ -public: - FMODStreamCapsule(FMOD::Sound *stream, FMODSoundRenderer *owner, const char *url) - : Owner(owner), Stream(NULL), Channel(NULL), - UserData(NULL), Callback(NULL), Reader(NULL), URL(url), Ended(false) - { - SetStream(stream); - } - - FMODStreamCapsule(FMOD::Sound *stream, FMODSoundRenderer *owner, FileReader *reader) - : Owner(owner), Stream(NULL), Channel(NULL), - UserData(NULL), Callback(NULL), Reader(reader), Ended(false) - { - SetStream(stream); - } - - FMODStreamCapsule(void *udata, SoundStreamCallback callback, FMODSoundRenderer *owner) - : Owner(owner), Stream(NULL), Channel(NULL), - UserData(udata), Callback(callback), Reader(NULL), Ended(false) - {} - - ~FMODStreamCapsule() - { - if (Channel != NULL) - { - Channel->stop(); - } - if (Stream != NULL) - { - Stream->release(); - } - if (Reader != NULL) - { - delete Reader; - } - } - - void SetStream(FMOD::Sound *stream) - { - float frequency; - - Stream = stream; - - // As this interface is for music, make it super-high priority. -#if FMOD_STUDIO - if (FMOD_OK == stream->getDefaults(&frequency, NULL)) - stream->setDefaults(frequency, 1); -#else - if (FMOD_OK == stream->getDefaults(&frequency, NULL, NULL, NULL)) - stream->setDefaults(frequency, 1, 0, 0); -#endif - } - - bool Play(bool looping, float volume) - { - FMOD_RESULT result; - - if (URL.IsNotEmpty()) - { // Net streams cannot be looped, because they cannot be seeked. - looping = false; - } - Stream->setMode((looping ? FMOD_LOOP_NORMAL : FMOD_LOOP_OFF) | FMOD_SOFTWARE | FMOD_2D); -#if FMOD_STUDIO - result = Owner->Sys->playSound(Stream,0, true, &Channel); -#else - result = Owner->Sys->playSound(FMOD_CHANNEL_FREE, Stream, true, &Channel); -#endif - if (result != FMOD_OK) - { - return false; - } - Channel->setChannelGroup(Owner->MusicGroup); -#if FMOD_STUDIO - Channel->setMixLevelsOutput(1, 1, 1, 1, 1, 1, 1, 1); -#else - Channel->setSpeakerMix(1, 1, 1, 1, 1, 1, 1, 1); -#endif - Channel->setVolume(volume); - // Ensure reverb is disabled. -#if FMOD_STUDIO - Channel->setReverbProperties(0,0.f); -#else - FMOD_REVERB_CHANNELPROPERTIES reverb = { 0, }; - if (FMOD_OK == Channel->getReverbProperties(&reverb)) - { - reverb.Room = -10000; - Channel->setReverbProperties(&reverb); - } -#endif - Channel->setPaused(false); - Ended = false; - JustStarted = true; - Starved = false; - Loop = looping; - Volume = volume; - return true; - } - - void Stop() - { - if (Channel != NULL) - { - Channel->stop(); - Channel = NULL; - } - } - - bool SetPaused(bool paused) - { - if (Channel != NULL) - { - return FMOD_OK == Channel->setPaused(paused); - } - return false; - } - - unsigned int GetPosition() - { - unsigned int pos; - - if (Channel != NULL && FMOD_OK == Channel->getPosition(&pos, FMOD_TIMEUNIT_MS)) - { - return pos; - } - return 0; - } - - bool IsEnded() - { - bool is; - FMOD_OPENSTATE openstate = FMOD_OPENSTATE_MAX; - bool starving; -#if FMOD_STUDIO || FMOD_VERSION >= 0x43400 - bool diskbusy; -#endif - - if (Stream == NULL) - { - return true; - } -#if !FMOD_STUDIO && FMOD_VERSION < 0x43400 - if (FMOD_OK != Stream->getOpenState(&openstate, NULL, &starving)) -#else - if (FMOD_OK != Stream->getOpenState(&openstate, NULL, &starving, &diskbusy)) -#endif - { - openstate = FMOD_OPENSTATE_ERROR; - } - if (openstate == FMOD_OPENSTATE_ERROR) - { - if (Channel != NULL) - { - Channel->stop(); - Channel = NULL; - } - return true; - } - if (Channel != NULL && (FMOD_OK != Channel->isPlaying(&is) || is == false)) - { - return true; - } - if (Ended) - { - Channel->stop(); - Channel = NULL; - return true; - } - if (URL.IsNotEmpty() && !JustStarted && openstate == FMOD_OPENSTATE_READY) - { - // Reconnect the stream, since it seems to have stalled. - // The only way to do this appears to be to completely recreate it. - FMOD_RESULT result; - - Channel->stop(); - Stream->release(); - Channel = NULL; - Stream = NULL; - // 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; - return result != FMOD_OK; - } - if (JustStarted && openstate == FMOD_OPENSTATE_PLAYING) - { - JustStarted = false; - } - if (JustStarted && Channel == NULL && openstate == FMOD_OPENSTATE_READY) - { - return !Play(Loop, Volume); - } - if (starving != Starved) - { // Mute the sound if it's starving. - Channel->setVolume(starving ? 0 : Volume); - Starved = starving; - } - return false; - } - - void SetVolume(float volume) - { - if (Channel != NULL && !Starved) - { - Channel->setVolume(volume); - } - Volume = volume; - } - - // Sets the position in ms. - bool SetPosition(unsigned int ms_pos) - { - return FMOD_OK == Channel->setPosition(ms_pos, FMOD_TIMEUNIT_MS); - } - - // Sets the order number for MOD formats. - bool SetOrder(int order_pos) - { - return FMOD_OK == Channel->setPosition(order_pos, FMOD_TIMEUNIT_MODORDER); - } - - FString GetStats() - { - FString stats; - FMOD_OPENSTATE openstate; - unsigned int percentbuffered; - unsigned int position; - bool starving; -#if FMOD_STUDIO || FMOD_VERSION >= 0x43400 - bool diskbusy; -#endif - float volume; - float frequency; - bool paused; - bool isplaying; - -#if !FMOD_STUDIO && FMOD_VERSION < 0x43400 - if (FMOD_OK == Stream->getOpenState(&openstate, &percentbuffered, &starving)) -#else - if (FMOD_OK == Stream->getOpenState(&openstate, &percentbuffered, &starving, &diskbusy)) -#endif - { - stats = (openstate <= FMOD_OPENSTATE_PLAYING ? OpenStateNames[openstate] : "Unknown state"); - stats.AppendFormat(",%3d%% buffered, %s", percentbuffered, starving ? "Starving" : "Well-fed"); - } - if (Channel == NULL) - { - stats += ", not playing"; - } - if (Channel != NULL && FMOD_OK == Channel->getPosition(&position, FMOD_TIMEUNIT_MS)) - { - stats.AppendFormat(", %d", position); - if (FMOD_OK == Stream->getLength(&position, FMOD_TIMEUNIT_MS)) - { - stats.AppendFormat("/%d", position); - } - stats += " ms"; - } - if (Channel != NULL && FMOD_OK == Channel->getVolume(&volume)) - { - stats.AppendFormat(", %d%%", int(volume * 100)); - } - if (Channel != NULL && FMOD_OK == Channel->getPaused(&paused) && paused) - { - stats += ", paused"; - } - if (Channel != NULL && FMOD_OK == Channel->isPlaying(&isplaying) && isplaying) - { - stats += ", playing"; - } - if (Channel != NULL && FMOD_OK == Channel->getFrequency(&frequency)) - { - stats.AppendFormat(", %g Hz", frequency); - } - if (JustStarted) - { - stats += " JS"; - } - if (Ended) - { - stats += " XX"; - } - return stats; - } - - static FMOD_RESULT F_CALLBACK PCMReadCallback(FMOD_SOUND *sound, void *data, unsigned int datalen) - { - FMOD_RESULT result; - FMODStreamCapsule *self; - - result = ((FMOD::Sound *)sound)->getUserData((void **)&self); - if (result != FMOD_OK || self == NULL || self->Callback == NULL || self->Ended) - { - // Contrary to the docs, this return value is completely ignored. - return FMOD_OK; - } - if (!self->Callback(self, data, datalen, self->UserData)) - { - self->Ended = true; - } - return FMOD_OK; - } - - static FMOD_RESULT F_CALLBACK PCMSetPosCallback(FMOD_SOUND *sound, int subsound, unsigned int position, FMOD_TIMEUNIT postype) - { - // This is useful if the user calls Channel::setPosition and you want - // to seek your data accordingly. - return FMOD_OK; - } - -private: - FMODSoundRenderer *Owner; - FMOD::Sound *Stream; - FMOD::Channel *Channel; - void *UserData; - SoundStreamCallback Callback; - FileReader *Reader; - FString URL; - bool Ended; - bool JustStarted; - bool Starved; - bool Loop; - float Volume; -}; - -//========================================================================== -// -// The interface the game uses to talk to FMOD. -// -//========================================================================== - -FMODSoundRenderer::FMODSoundRenderer() -{ - InitSuccess = Init(); -} - -FMODSoundRenderer::~FMODSoundRenderer() -{ - Shutdown(); -} - -bool FMODSoundRenderer::IsValid() -{ - return InitSuccess; -} - -//========================================================================== -// -// FMODSoundRenderer :: Init -// -//========================================================================== - -bool FMODSoundRenderer::Init() -{ - FMOD_RESULT result; - unsigned int version; - FMOD_SPEAKERMODE speakermode; - FMOD_SOUND_FORMAT format; - FMOD_DSP_RESAMPLER resampler; - FMOD_INITFLAGS initflags; - int samplerate; - int driver; - - int eval; - - SFXPaused = 0; - DSPLocked = false; - MusicGroup = NULL; - SfxGroup = NULL; - PausableSfx = NULL; - SfxConnection = NULL; - WaterLP = NULL; - WaterReverb = NULL; - PrevEnvironment = DefaultEnvironments[0]; - DSPClock.AsOne = 0; - ChannelGroupTargetUnit = NULL; - ChannelGroupTargetUnitOutput = NULL; - SfxReverbHooked = false; - SfxReverbPlaceholder = NULL; - OutputPlugin = 0; - - Printf("I_InitSound: Initializing FMOD\n"); - - // This is just for safety. Normally this should never be called if FMod Ex cannot be found. - if (!IsFModExPresent()) - { - Sys = NULL; - Printf(TEXTCOLOR_ORANGE"Failed to load fmodex" -#ifdef _WIN64 - "64" -#endif - ".dll\n"); - return false; - } - - // Create a System object and initialize. - result = FMOD::System_Create(&Sys); - if (result != FMOD_OK) - { - Sys = NULL; - Printf(TEXTCOLOR_ORANGE"Failed to create FMOD system object: Error %d\n", result); - return false; - } - - result = Sys->getVersion(&version); - if (result != FMOD_OK) - { - Printf(TEXTCOLOR_ORANGE"Could not validate FMOD version: Error %d\n", result); - return false; - } - - const char *wrongver = NULL; -#if FMOD_STUDIO - if (version < (FMOD_VERSION & 0xFFFF00)) -#elif FMOD_VERSION >= 0x43600 - if (version < 0x43600) -#else - if (version < 0x42000) -#endif - { - wrongver = "an old"; - } -#if !FMOD_STUDIO && FMOD_VERSION < 0x42700 - else if ((version & 0xFFFF00) > 0x42600) -#else - else if ((version & 0xFFFF00) > (FMOD_VERSION & 0xFFFF00)) -#endif - { - wrongver = "a new"; - } - if (wrongver != NULL) - { - Printf (" " TEXTCOLOR_ORANGE "Error! You are using %s version of FMOD (%x.%02x.%02x).\n" - " " TEXTCOLOR_ORANGE "This program was built for version %x.%02x.%02x\n", - wrongver, - version >> 16, (version >> 8) & 255, version & 255, - FMOD_VERSION >> 16, (FMOD_VERSION >> 8) & 255, FMOD_VERSION & 255); - return false; - } - ActiveFMODVersion = version; - - if (!ShowedBanner) - { - // '\xa9' is the copyright symbol in the Windows-1252 code page. - Printf("FMOD Sound System, copyright \xa9 Firelight Technologies Pty, Ltd., 1994-2009.\n"); - Printf("Loaded FMOD version %x.%02x.%02x\n", version >> 16, (version >> 8) & 255, version & 255); - ShowedBanner = true; - } -#if defined(_WIN32) && !FMOD_STUDIO - if (OSPlatform == os_WinNT4) - { - // The following was true as of FMOD 3. I don't know if it still - // applies to FMOD Ex, nor do I have an NT 4 install anymore, but - // there's no reason to get rid of it yet. - // - // If running Windows NT 4, we need to initialize DirectSound before - // using WinMM. If we don't, then FSOUND_Close will corrupt a - // heap. This might just be the Audigy's drivers--I don't know why - // it happens. At least the fix is simple enough. I only need to - // initialize DirectSound once, and then I can initialize/close - // WinMM as many times as I want. - // - // Yes, using WinMM under NT 4 is a good idea. I can get latencies as - // low as 20 ms with WinMM, but with DirectSound I need to have the - // latency as high as 120 ms to avoid crackling--quite the opposite - // from the other Windows versions with real DirectSound support. - - static bool inited_dsound = false; - - if (!inited_dsound) - { - if (Sys->setOutput(FMOD_OUTPUTTYPE_DSOUND) == FMOD_OK) - { - if (Sys->init(1, FMOD_INIT_NORMAL, 0) == FMOD_OK) - { - inited_dsound = true; - Sleep(50); - Sys->close(); - } - Sys->setOutput(FMOD_OUTPUTTYPE_WINMM); - } - } - } -#endif - -#if !defined _WIN32 && !defined __APPLE__ && !FMOD_STUDIO - // Try to load SDL output plugin - result = Sys->setPluginPath(progdir); // Should we really look for it in the program directory? - result = Sys->loadPlugin("liboutput_sdl.so", &OutputPlugin); - if (result != FMOD_OK) - { - OutputPlugin = 0; - } -#endif - - // Set the user specified output mode. - eval = Enum_NumForName(OutputNames, snd_output); - if (eval >= 0) - { - if (eval == 666 && OutputPlugin != 0) - { - result = Sys->setOutputByPlugin(OutputPlugin); - } - else - { - result = Sys->setOutput(FMOD_OUTPUTTYPE(eval)); - } - if (result != FMOD_OK) - { - Printf(TEXTCOLOR_BLUE"Setting output type '%s' failed. Using default instead. (Error %d)\n", *snd_output, result); - eval = FMOD_OUTPUTTYPE_AUTODETECT; - Sys->setOutput(FMOD_OUTPUTTYPE_AUTODETECT); - } - } - - result = Sys->getNumDrivers(&driver); -#if defined(__unix__) && !FMOD_STUDIO - if (result == FMOD_OK) - { - // On Linux, FMOD defaults to OSS. If OSS is not present, it doesn't - // try ALSA; it just fails. We'll try for it, but only if OSS wasn't - // explicitly specified for snd_output. - if (driver == 0 && eval == FMOD_OUTPUTTYPE_AUTODETECT) - { - FMOD_OUTPUTTYPE output; - if (FMOD_OK == Sys->getOutput(&output)) - { - if (output == FMOD_OUTPUTTYPE_OSS) - { - Printf(TEXTCOLOR_BLUE"OSS could not be initialized. Trying ALSA.\n"); - Sys->setOutput(FMOD_OUTPUTTYPE_ALSA); - result = Sys->getNumDrivers(&driver); - } - } - } - } -#endif - if (result == FMOD_OK) - { - if (driver == 0) - { - Printf(TEXTCOLOR_ORANGE"No working sound devices found. Try a different snd_output?\n"); - return false; - } - if (snd_driver >= driver) - { - Printf(TEXTCOLOR_BLUE"Driver %d does not exist. Using 0.\n", *snd_driver); - driver = 0; - } - else - { - driver = snd_driver; - } - result = Sys->setDriver(driver); - } - result = Sys->getDriver(&driver); -#if FMOD_STUDIO - // We were built with an FMOD Studio that only returns the control panel frequency - result = Sys->getDriverInfo(driver, nullptr, 0, nullptr, &Driver_MinFrequency, &speakermode, nullptr); - Driver_MaxFrequency = Driver_MinFrequency; -#elif FMOD_VERSION >= 0x43600 - // We were built with an FMOD that only returns the control panel frequency - result = Sys->getDriverCaps(driver, &Driver_Caps, &Driver_MinFrequency, &speakermode); - Driver_MaxFrequency = Driver_MinFrequency; -#else - // We were built with an FMOD that returns a frequency range - result = Sys->getDriverCaps(driver, &Driver_Caps, &Driver_MinFrequency, &Driver_MaxFrequency, &speakermode); -#endif - if (result != FMOD_OK) - { - Printf(TEXTCOLOR_BLUE"Could not ascertain driver capabilities. Some things may be weird. (Error %d)\n", result); - // Fill in some default to pretend it worked. (But as long as we specify a valid driver, - // can this call actually fail?) -#if !FMOD_STUDIO - Driver_Caps = 0; -#endif - Driver_MinFrequency = 4000; - Driver_MaxFrequency = 48000; - speakermode = FMOD_SPEAKERMODE_STEREO; - } - - // Set the user selected speaker mode. - eval = Enum_NumForName(SpeakerModeNames, snd_speakermode); - if (eval >= 0) - { - speakermode = FMOD_SPEAKERMODE(eval); - } -#if !FMOD_STUDIO - result = Sys->setSpeakerMode(speakermode); - if (result != FMOD_OK) - { - Printf(TEXTCOLOR_BLUE"Could not set speaker mode to '%s'. (Error %d)\n", *snd_speakermode, result); - } -#endif - - // Set software format - eval = Enum_NumForName(SoundFormatNames, snd_output_format); - format = eval >= 0 ? FMOD_SOUND_FORMAT(eval) : FMOD_SOUND_FORMAT_PCM16; - if (format == FMOD_SOUND_FORMAT_PCM8) - { - // PCM-8 sounds like garbage with anything but DirectSound. - FMOD_OUTPUTTYPE output; - if (FMOD_OK != Sys->getOutput(&output) || output != FMOD_OUTPUTTYPE_DSOUND) - { - format = FMOD_SOUND_FORMAT_PCM16; - } - } - eval = Enum_NumForName(ResamplerNames, snd_resampler); - resampler = eval >= 0 ? FMOD_DSP_RESAMPLER(eval) : FMOD_DSP_RESAMPLER_LINEAR; - // These represented the frequency limits for hardware channels, which we never used anyway. -// samplerate = clamp(snd_samplerate, Driver_MinFrequency, Driver_MaxFrequency); - samplerate = snd_samplerate; - if (samplerate == 0 || snd_samplerate == 0) - { // Creative's ASIO drivers report the only supported frequency as 0! -#if FMOD_STUDIO - if (FMOD_OK != Sys->getSoftwareFormat(&samplerate, NULL, NULL)) -#else - if (FMOD_OK != Sys->getSoftwareFormat(&samplerate, NULL, NULL, NULL, NULL, NULL)) -#endif - { - samplerate = 48000; - } - } - if (samplerate != snd_samplerate && snd_samplerate != 0) - { - Printf(TEXTCOLOR_BLUE"Sample rate %d is unsupported. Trying %d.\n", *snd_samplerate, samplerate); - } -#if FMOD_STUDIO - result = Sys->setSoftwareFormat(samplerate, speakermode, 0); -#else - result = Sys->setSoftwareFormat(samplerate, format, 0, 0, resampler); -#endif - if (result != FMOD_OK) - { - Printf(TEXTCOLOR_BLUE"Could not set mixing format. Defaults will be used. (Error %d)\n", result); - } - -#if FMOD_STUDIO - FMOD_ADVANCEDSETTINGS advSettings = {}; - advSettings.cbSize = sizeof advSettings; - advSettings.resamplerMethod = resampler; - result = Sys->setAdvancedSettings(&advSettings); - if (result != FMOD_OK) - { - Printf(TEXTCOLOR_BLUE"Could not set resampler method. Defaults will be used. (Error %d)\n", result); - } -#endif - - // Set software channels according to snd_channels - result = Sys->setSoftwareChannels(snd_channels + NUM_EXTRA_SOFTWARE_CHANNELS); - if (result != FMOD_OK) - { - Printf(TEXTCOLOR_BLUE"Failed to set the preferred number of channels. (Error %d)\n", result); - } - -#if !FMOD_STUDIO - if (Driver_Caps & FMOD_CAPS_HARDWARE_EMULATED) - { // The user has the 'Acceleration' slider set to off! - // This is really bad for latency! - Printf (TEXTCOLOR_BLUE"Warning: The sound acceleration slider has been set to off.\n"); - Printf (TEXTCOLOR_BLUE"Please turn it back on if you want decent sound.\n"); - result = Sys->setDSPBufferSize(1024, 10); // At 48khz, the latency between issuing an fmod command and hearing it will now be about 213ms. - } - else -#endif - if (snd_buffersize != 0 || snd_buffercount != 0) - { - int buffersize = snd_buffersize ? snd_buffersize : 1024; - int buffercount = snd_buffercount ? snd_buffercount : 4; - result = Sys->setDSPBufferSize(buffersize, buffercount); - } - else - { - result = FMOD_OK; - } - if (result != FMOD_OK) - { - Printf(TEXTCOLOR_BLUE"Setting DSP buffer size failed. (Error %d)\n", result); - } - - // Try to init - initflags = FMOD_INIT_NORMAL; - if (snd_hrtf) - { - // These flags are the same thing, just with different names. -#ifdef FMOD_INIT_CHANNEL_LOWPASS - initflags |= FMOD_INIT_CHANNEL_LOWPASS; -#elif defined(FMOD_INIT_SOFTWARE_HRTF) - initflags |= FMOD_INIT_SOFTWARE_HRTF; -#else - initflags |= FMOD_INIT_HRTF_LOWPASS; -#endif - } - if (snd_profile) - { -#ifdef FMOD_INIT_PROFILE_ENABLE - initflags |= FMOD_INIT_PROFILE_ENABLE; -#else - initflags |= FMOD_INIT_ENABLE_PROFILE; -#endif - } - for (;;) - { - result = Sys->init(MAX(*snd_channels, MAX_CHANNELS), initflags, 0); - if (result == FMOD_ERR_OUTPUT_CREATEBUFFER) - { - // Possible causes of a buffer creation failure: - // 1. The speaker mode selected isn't supported by this soundcard. Force it to stereo. - // 2. The output format is unsupported. Force it to 16-bit PCM. - // 3. ??? -#if FMOD_STUDIO - result = Sys->getSoftwareFormat(nullptr, &speakermode, nullptr); -#else - result = Sys->getSpeakerMode(&speakermode); -#endif - if (result == FMOD_OK && - speakermode != FMOD_SPEAKERMODE_STEREO && -#if FMOD_STUDIO - FMOD_OK == Sys->setSoftwareFormat(samplerate, FMOD_SPEAKERMODE_STEREO, 0)) -#else - FMOD_OK == Sys->setSpeakerMode(FMOD_SPEAKERMODE_STEREO)) -#endif - { - Printf(TEXTCOLOR_RED" Buffer creation failed. Retrying with stereo output.\n"); - continue; - } -#if !FMOD_STUDIO - result = Sys->getSoftwareFormat(&samplerate, &format, NULL, NULL, &resampler, NULL); - if (result == FMOD_OK && - format != FMOD_SOUND_FORMAT_PCM16 && - FMOD_OK == Sys->setSoftwareFormat(samplerate, FMOD_SOUND_FORMAT_PCM16, 0, 0, resampler)) - { - Printf(TEXTCOLOR_RED" Buffer creation failed. Retrying with PCM-16 output.\n"); - continue; - } -#endif - } - else if (result == FMOD_ERR_NET_SOCKET_ERROR && -#ifdef FMOD_INIT_PROFILE_ENABLE - (initflags & FMOD_INIT_PROFILE_ENABLE)) -#else - (initflags & FMOD_INIT_ENABLE_PROFILE)) -#endif - { - Printf(TEXTCOLOR_RED" Could not create socket. Retrying without profiling.\n"); -#ifdef FMOD_INIT_PROFILE_ENABLE - initflags &= ~FMOD_INIT_PROFILE_ENABLE; -#else - initflags &= ~FMOD_INIT_ENABLE_PROFILE; -#endif - continue; - } -#ifdef _WIN32 - else if (result == FMOD_ERR_OUTPUT_INIT) - { - FMOD_OUTPUTTYPE output; - result = Sys->getOutput(&output); - if (result == FMOD_OK && output != FMOD_OUTPUTTYPE_DSOUND) - { - Printf(TEXTCOLOR_BLUE" Init failed for output type %s. Retrying with DirectSound.\n", - Enum_NameForNum(OutputNames, output)); - if (FMOD_OK == Sys->setOutput(FMOD_OUTPUTTYPE_DSOUND)) - { - continue; - } - } - } -#endif - break; - } - if (result != FMOD_OK) - { // Initializing FMOD failed. Cry cry. - Printf(TEXTCOLOR_ORANGE" System::init returned error code %d\n", result); - return false; - } - - // Create channel groups - result = Sys->createChannelGroup("Music", &MusicGroup); - if (result != FMOD_OK) - { - Printf(TEXTCOLOR_ORANGE" Could not create music channel group. (Error %d)\n", result); - return false; - } - - result = Sys->createChannelGroup("SFX", &SfxGroup); - if (result != FMOD_OK) - { - Printf(TEXTCOLOR_ORANGE" Could not create sfx channel group. (Error %d)\n", result); - return false; - } - - result = Sys->createChannelGroup("Pausable SFX", &PausableSfx); - if (result != FMOD_OK) - { - Printf(TEXTCOLOR_ORANGE" Could not create pausable sfx channel group. (Error %d)\n", result); - return false; - } - - result = SfxGroup->addGroup(PausableSfx); - if (result != FMOD_OK) - { - Printf(TEXTCOLOR_BLUE" Could not attach pausable sfx to sfx channel group. (Error %d)\n", result); - } - - // Create DSP units for underwater effect - result = Sys->createDSPByType(FMOD_DSP_TYPE_LOWPASS, &WaterLP); - if (result != FMOD_OK) - { - Printf(TEXTCOLOR_BLUE" Could not create underwater lowpass unit. (Error %d)\n", result); - } - else - { - result = Sys->createDSPByType(FMOD_DSP_TYPE_SFXREVERB, &WaterReverb); - if (result != FMOD_OK) - { - Printf(TEXTCOLOR_BLUE" Could not create underwater reverb unit. (Error %d)\n", result); - } - } - - // Connect underwater DSP unit between PausableSFX and SFX groups, while - // retaining the connection established by SfxGroup->addGroup(). - if (WaterLP != NULL) - { - FMOD::DSP *sfx_head, *pausable_head; - -#if FMOD_STUDIO - result = SfxGroup->getDSP(FMOD_CHANNELCONTROL_DSP_HEAD, &sfx_head); -#else - result = SfxGroup->getDSPHead(&sfx_head); -#endif - if (result == FMOD_OK) - { - result = sfx_head->getInput(0, &pausable_head, &SfxConnection); - if (result == FMOD_OK) - { - // The placeholder mixer is for reference to where to connect the SFX - // reverb unit once it gets created. - result = Sys->createDSPByType(FMOD_DSP_TYPE_MIXER, &SfxReverbPlaceholder); - if (result == FMOD_OK) - { - // Replace the PausableSFX->SFX connection with - // PausableSFX->ReverbPlaceholder->SFX. - result = SfxReverbPlaceholder->addInput(pausable_head, NULL); - if (result == FMOD_OK) - { - FMOD::DSPConnection *connection; - result = sfx_head->addInput(SfxReverbPlaceholder, &connection); - if (result == FMOD_OK) - { - sfx_head->disconnectFrom(pausable_head); - SfxReverbPlaceholder->setActive(true); - SfxReverbPlaceholder->setBypass(true); - // The placeholder now takes the place of the pausable_head - // for the following connections. - pausable_head = SfxReverbPlaceholder; - SfxConnection = connection; - } - } - else - { - SfxReverbPlaceholder->release(); - SfxReverbPlaceholder = NULL; - } - } - result = WaterLP->addInput(pausable_head, NULL); - WaterLP->setActive(false); - WaterLP->setParameterFloat(FMOD_DSP_LOWPASS_CUTOFF, snd_waterlp); - WaterLP->setParameterFloat(FMOD_DSP_LOWPASS_RESONANCE, 2); - - if (WaterReverb != NULL) - { - result = WaterReverb->addInput(WaterLP, NULL); - if (result == FMOD_OK) - { - result = sfx_head->addInput(WaterReverb, NULL); - if (result == FMOD_OK) - { -// WaterReverb->setParameter(FMOD_DSP_REVERB_ROOMSIZE, 0.001f); -// WaterReverb->setParameter(FMOD_DSP_REVERB_DAMP, 0.2f); - - // These parameters are entirely empirical and can probably - // stand some improvement, but it sounds remarkably close - // to the old reverb unit's output. -#if FMOD_STUDIO - WaterReverb->setParameterFloat(FMOD_DSP_SFXREVERB_LOWSHELFFREQUENCY, 150); -#else - WaterReverb->setParameterFloat(FMOD_DSP_SFXREVERB_LFREFERENCE, 150); -#endif - WaterReverb->setParameterFloat(FMOD_DSP_SFXREVERB_HFREFERENCE, 10000); -#if !FMOD_STUDIO - WaterReverb->setParameterFloat(FMOD_DSP_SFXREVERB_ROOM, 0); - WaterReverb->setParameterFloat(FMOD_DSP_SFXREVERB_ROOMHF, -5000); -#endif - WaterReverb->setParameterFloat(FMOD_DSP_SFXREVERB_DRYLEVEL, 0); -#if FMOD_STUDIO - WaterReverb->setParameterFloat(FMOD_DSP_SFXREVERB_HFDECAYRATIO, 100); -#else - WaterReverb->setParameterFloat(FMOD_DSP_SFXREVERB_DECAYHFRATIO, 1); -#endif - WaterReverb->setParameterFloat(FMOD_DSP_SFXREVERB_DECAYTIME, 0.25f); - WaterReverb->setParameterFloat(FMOD_DSP_SFXREVERB_DENSITY, 100); - WaterReverb->setParameterFloat(FMOD_DSP_SFXREVERB_DIFFUSION, 100); - WaterReverb->setActive(false); - } - } - } - else - { - result = sfx_head->addInput(WaterLP, NULL); - } - } - } - } - LastWaterLP = snd_waterlp; - - // Find the FMOD Channel Group Target Unit. To completely eliminate sound - // while the program is deactivated, we can deactivate this DSP unit, and - // all audio processing will cease. This is not directly exposed by the - // API but can be easily located by getting the master channel group and - // tracing its single output, since it is known to hook up directly to the - // Channel Group Target Unit. (See FMOD Profiler for proof.) - FMOD::ChannelGroup *master_group; - result = Sys->getMasterChannelGroup(&master_group); - if (result == FMOD_OK) - { - FMOD::DSP *master_head; - -#if FMOD_STUDIO - result = master_group->getDSP(FMOD_CHANNELCONTROL_DSP_HEAD, &master_head); -#else - result = master_group->getDSPHead(&master_head); -#endif - if (result == FMOD_OK) - { - result = master_head->getOutput(0, &ChannelGroupTargetUnit, NULL); - if (result != FMOD_OK) - { - ChannelGroupTargetUnit = NULL; - } - else - { - FMOD::DSP *dontcare; - result = ChannelGroupTargetUnit->getOutput(0, &dontcare, &ChannelGroupTargetUnitOutput); - if (result != FMOD_OK) - { - ChannelGroupTargetUnitOutput = NULL; - } - } - } - } - -#if FMOD_STUDIO - if (FMOD_OK != Sys->getSoftwareFormat(&OutputRate, NULL, NULL)) -#else - if (FMOD_OK != Sys->getSoftwareFormat(&OutputRate, NULL, NULL, NULL, NULL, NULL)) -#endif - { - OutputRate = 48000; // Guess, but this should never happen. - } - Sys->set3DSettings(0.5f, 96.f, 1.f); - Sys->set3DRolloffCallback(RolloffCallback); - Sys->setStreamBufferSize(snd_streambuffersize * 1024, FMOD_TIMEUNIT_RAWBYTES); - snd_sfxvolume.Callback (); - return true; -} - -//========================================================================== -// -// FMODSoundRenderer :: Shutdown -// -//========================================================================== - -void FMODSoundRenderer::Shutdown() -{ - if (Sys != NULL) - { - if (MusicGroup != NULL) - { - MusicGroup->release(); - MusicGroup = NULL; - } - if (PausableSfx != NULL) - { - PausableSfx->release(); - PausableSfx = NULL; - } - if (SfxGroup != NULL) - { - SfxGroup->release(); - SfxGroup = NULL; - } - if (WaterLP != NULL) - { - WaterLP->release(); - WaterLP = NULL; - } - if (WaterReverb != NULL) - { - WaterReverb->release(); - WaterReverb = NULL; - } - if (SfxReverbPlaceholder != NULL) - { - SfxReverbPlaceholder->release(); - SfxReverbPlaceholder = NULL; - } - - Sys->close(); - if (OutputPlugin != 0) - { - Sys->unloadPlugin(OutputPlugin); - OutputPlugin = 0; - } - Sys->release(); - Sys = NULL; - } -} - -//========================================================================== -// -// FMODSoundRenderer :: GetOutputRate -// -//========================================================================== - -float FMODSoundRenderer::GetOutputRate() -{ - return (float)OutputRate; -} - -//========================================================================== -// -// FMODSoundRenderer :: PrintStatus -// -//========================================================================== - -void FMODSoundRenderer::PrintStatus() -{ - FMOD_OUTPUTTYPE output; - FMOD_SPEAKERMODE speakermode; - int driver; - int samplerate; - unsigned int bufferlength; - int numbuffers; - - Printf ("Loaded FMOD version: " TEXTCOLOR_GREEN "%x.%02x.%02x\n", ActiveFMODVersion >> 16, - (ActiveFMODVersion >> 8) & 255, ActiveFMODVersion & 255); - if (FMOD_OK == Sys->getOutput(&output)) - { - Printf ("Output type: " TEXTCOLOR_GREEN "%s\n", Enum_NameForNum(OutputNames, output)); - } -#if FMOD_STUDIO - if (FMOD_OK == Sys->getSoftwareFormat(&samplerate, &speakermode, nullptr)) - { - Printf ("Speaker mode: " TEXTCOLOR_GREEN "%s\n", Enum_NameForNum(SpeakerModeNames, speakermode)); - Printf (TEXTCOLOR_LIGHTBLUE "Software mixer sample rate: " TEXTCOLOR_GREEN "%d\n", samplerate); - } -#else - if (FMOD_OK == Sys->getSpeakerMode(&speakermode)) - { - Printf ("Speaker mode: " TEXTCOLOR_GREEN "%s\n", Enum_NameForNum(SpeakerModeNames, speakermode)); - } -#endif - if (FMOD_OK == Sys->getDriver(&driver)) - { - char name[256]; -#if FMOD_STUDIO - if (FMOD_OK != Sys->getDriverInfo(driver, name, sizeof(name), nullptr, nullptr, nullptr, nullptr)) -#else - if (FMOD_OK != Sys->getDriverInfo(driver, name, sizeof(name), NULL)) -#endif - { - strcpy(name, "Unknown"); - } - Printf ("Driver: " TEXTCOLOR_GREEN "%d" TEXTCOLOR_NORMAL " (" TEXTCOLOR_ORANGE "%s" TEXTCOLOR_NORMAL ")\n", driver, name); -#if !FMOD_STUDIO - DumpDriverCaps(Driver_Caps, Driver_MinFrequency, Driver_MaxFrequency); -#endif - } -#if !FMOD_STUDIO - FMOD_SOUND_FORMAT format; - FMOD_DSP_RESAMPLER resampler; - int numoutputchannels; - if (FMOD_OK == Sys->getSoftwareFormat(&samplerate, &format, &numoutputchannels, NULL, &resampler, NULL)) - { - Printf (TEXTCOLOR_LIGHTBLUE "Software mixer sample rate: " TEXTCOLOR_GREEN "%d\n", samplerate); - Printf (TEXTCOLOR_LIGHTBLUE "Software mixer format: " TEXTCOLOR_GREEN "%s\n", Enum_NameForNum(SoundFormatNames, format)); - Printf (TEXTCOLOR_LIGHTBLUE "Software mixer channels: " TEXTCOLOR_GREEN "%d\n", numoutputchannels); - Printf (TEXTCOLOR_LIGHTBLUE "Software mixer resampler: " TEXTCOLOR_GREEN "%s\n", Enum_NameForNum(ResamplerNames, resampler)); - } -#endif - if (FMOD_OK == Sys->getDSPBufferSize(&bufferlength, &numbuffers)) - { - Printf (TEXTCOLOR_LIGHTBLUE "DSP buffers: " TEXTCOLOR_GREEN "%u samples x %d\n", bufferlength, numbuffers); - } -} - -//========================================================================== -// -// FMODSoundRenderer :: DumpDriverCaps -// -//========================================================================== - -#if !FMOD_STUDIO -void FMODSoundRenderer::DumpDriverCaps(FMOD_CAPS caps, int minfrequency, int maxfrequency) -{ - Printf (TEXTCOLOR_OLIVE " Min. frequency: " TEXTCOLOR_GREEN "%d\n", minfrequency); - Printf (TEXTCOLOR_OLIVE " Max. frequency: " TEXTCOLOR_GREEN "%d\n", maxfrequency); - Printf (" Features:\n"); - if (caps == 0) Printf(TEXTCOLOR_OLIVE " None\n"); - if (caps & FMOD_CAPS_HARDWARE) Printf(TEXTCOLOR_OLIVE " Hardware mixing\n"); - if (caps & FMOD_CAPS_HARDWARE_EMULATED) Printf(TEXTCOLOR_OLIVE " Hardware acceleration is turned off!\n"); - if (caps & FMOD_CAPS_OUTPUT_MULTICHANNEL) Printf(TEXTCOLOR_OLIVE " Multichannel\n"); - if (caps & FMOD_CAPS_OUTPUT_FORMAT_PCM8) Printf(TEXTCOLOR_OLIVE " PCM-8"); - if (caps & FMOD_CAPS_OUTPUT_FORMAT_PCM16) Printf(TEXTCOLOR_OLIVE " PCM-16"); - if (caps & FMOD_CAPS_OUTPUT_FORMAT_PCM24) Printf(TEXTCOLOR_OLIVE " PCM-24"); - if (caps & FMOD_CAPS_OUTPUT_FORMAT_PCM32) Printf(TEXTCOLOR_OLIVE " PCM-32"); - if (caps & FMOD_CAPS_OUTPUT_FORMAT_PCMFLOAT) Printf(TEXTCOLOR_OLIVE " PCM-Float"); - if (caps & (FMOD_CAPS_OUTPUT_FORMAT_PCM8 | FMOD_CAPS_OUTPUT_FORMAT_PCM16 | FMOD_CAPS_OUTPUT_FORMAT_PCM24 | FMOD_CAPS_OUTPUT_FORMAT_PCM32 | FMOD_CAPS_OUTPUT_FORMAT_PCMFLOAT)) - { - Printf("\n"); - } - if (caps & FMOD_CAPS_REVERB_LIMITED) Printf(TEXTCOLOR_OLIVE " Limited reverb\n"); -} -#endif - -//========================================================================== -// -// FMODSoundRenderer :: PrintDriversList -// -//========================================================================== - -void FMODSoundRenderer::PrintDriversList() -{ - int numdrivers; - int i; - char name[256]; - - if (FMOD_OK == Sys->getNumDrivers(&numdrivers)) - { - for (i = 0; i < numdrivers; ++i) - { -#if FMOD_STUDIO - if (FMOD_OK == Sys->getDriverInfo(i, name, sizeof(name), nullptr, nullptr, nullptr, nullptr)) -#else - if (FMOD_OK == Sys->getDriverInfo(i, name, sizeof(name), NULL)) -#endif - { - Printf("%d. %s\n", i, name); - } - } - } -} - -//========================================================================== -// -// FMODSoundRenderer :: GatherStats -// -//========================================================================== - -FString FMODSoundRenderer::GatherStats() -{ - int channels; - float dsp, stream, update, geometry, total; - FString out; - - channels = 0; - total = update = geometry = stream = dsp = 0; - Sys->getChannelsPlaying(&channels); -#if FMOD_STUDIO || FMOD_VERSION >= 0x42501 - // We were built with an FMOD with the geometry parameter. - if (ActiveFMODVersion >= 0x42501) - { // And we are running with an FMOD that includes it. - FMOD_System_GetCPUUsage((FMOD_SYSTEM *)Sys, &dsp, &stream, &geometry, &update, &total); - } - else - { // And we are running with an FMOD that does not include it. - // Cast the function to the appropriate type and call through the cast, - // since the headers are for the newer version. - ((FMOD_RESULT (F_API *)(FMOD_SYSTEM *, float *, float *, float *, float *)) - FMOD_System_GetCPUUsage)((FMOD_SYSTEM *)Sys, &dsp, &stream, &update, &total); - } -#else - // Same as above, except the headers we used do not include the geometry parameter. - if (ActiveFMODVersion >= 0x42501) - { - ((FMOD_RESULT (F_API *)(FMOD_SYSTEM *, float *, float *, float *, float *, float *)) - FMOD_System_GetCPUUsage)((FMOD_SYSTEM *)Sys, &dsp, &stream, &geometry, &update, &total); - } - else - { - FMOD_System_GetCPUUsage((FMOD_SYSTEM *)Sys, &dsp, &stream, &update, &total); - } -#endif - - out.Format ("%d channels," TEXTCOLOR_YELLOW "%5.2f" TEXTCOLOR_NORMAL "%% CPU " - "(DSP:" TEXTCOLOR_YELLOW "%5.2f" TEXTCOLOR_NORMAL "%% " - "Stream:" TEXTCOLOR_YELLOW "%5.2f" TEXTCOLOR_NORMAL "%% " - "Geometry:" TEXTCOLOR_YELLOW "%5.2f" TEXTCOLOR_NORMAL "%% " - "Update:" TEXTCOLOR_YELLOW "%5.2f" TEXTCOLOR_NORMAL "%%)", - channels, total, dsp, stream, geometry, update); - return out; -} - -//========================================================================== -// -// FMODSoundRenderer :: SetSfxVolume -// -//========================================================================== - -void FMODSoundRenderer::SetSfxVolume(float volume) -{ - SfxGroup->setVolume(volume); -} - -//========================================================================== -// -// FMODSoundRenderer :: SetMusicVolume -// -//========================================================================== - -void FMODSoundRenderer::SetMusicVolume(float volume) -{ - MusicGroup->setVolume(volume); -} - -//========================================================================== -// -// FMODSoundRenderer :: CreateStream -// -// Creates a streaming sound that receives PCM data through a callback. -// -//========================================================================== - -SoundStream *FMODSoundRenderer::CreateStream (SoundStreamCallback callback, int buffbytes, int flags, int samplerate, void *userdata) -{ - FMODStreamCapsule *capsule; - FMOD::Sound *sound; - FMOD_RESULT result; - FMOD_CREATESOUNDEXINFO exinfo; - FMOD_MODE mode; - int sample_shift; - int channel_shift; - - InitCreateSoundExInfo(&exinfo); - capsule = new FMODStreamCapsule (userdata, callback, this); - - mode = FMOD_2D | FMOD_OPENUSER | FMOD_LOOP_NORMAL | FMOD_SOFTWARE | FMOD_CREATESTREAM | FMOD_OPENONLY; - sample_shift = (flags & (SoundStream::Bits32 | SoundStream::Float)) ? 2 : (flags & SoundStream::Bits8) ? 0 : 1; - channel_shift = (flags & SoundStream::Mono) ? 0 : 1; - - // Chunk size of stream update in samples. This will be the amount of data - // passed to the user callback. - exinfo.decodebuffersize = buffbytes >> (sample_shift + channel_shift); - - // Number of channels in the sound. - exinfo.numchannels = 1 << channel_shift; - - // Length of PCM data in bytes of whole song (for Sound::getLength). - // This pretends it's extremely long. - exinfo.length = ~0u; - - // Default playback rate of sound. */ - exinfo.defaultfrequency = samplerate; - - // Data format of sound. - if (flags & SoundStream::Float) - { - exinfo.format = FMOD_SOUND_FORMAT_PCMFLOAT; - } - else if (flags & SoundStream::Bits32) - { - exinfo.format = FMOD_SOUND_FORMAT_PCM32; - } - else if (flags & SoundStream::Bits8) - { - exinfo.format = FMOD_SOUND_FORMAT_PCM8; - } - else - { - exinfo.format = FMOD_SOUND_FORMAT_PCM16; - } - - // User callback for reading. - exinfo.pcmreadcallback = FMODStreamCapsule::PCMReadCallback; - - // User callback for seeking. - exinfo.pcmsetposcallback = FMODStreamCapsule::PCMSetPosCallback; - - // User data to be attached to the sound during creation. Access via Sound::getUserData. - exinfo.userdata = capsule; - - result = Sys->createSound(NULL, mode, &exinfo, &sound); - if (result != FMOD_OK) - { - delete capsule; - return NULL; - } - capsule->SetStream(sound); - return capsule; -} - -//========================================================================== -// -// GetTagData -// -// Checks for a string-type tag, and returns its data. -// -//========================================================================== - -const char *GetTagData(FMOD::Sound *sound, const char *tag_name) -{ - FMOD_TAG tag; - - if (FMOD_OK == sound->getTag(tag_name, 0, &tag) && - (tag.datatype == FMOD_TAGDATATYPE_STRING || tag.datatype == FMOD_TAGDATATYPE_STRING_UTF8)) - { - return (const char *)tag.data; - } - return NULL; -} - -//========================================================================== -// -// SetCustomLoopPts -// -// Sets up custom sound loops by checking for these tags: -// LOOP_START -// LOOP_END -// LOOP_BIDI -// -//========================================================================== - -static void SetCustomLoopPts(FMOD::Sound *sound) -{ -#if 0 - FMOD_TAG tag; - int numtags; - if (FMOD_OK == stream->getNumTags(&numtags, NULL)) - { - for (int i = 0; i < numtags; ++i) - { - if (FMOD_OK == sound->getTag(NULL, i, &tag)) - { - Printf("Tag %2d. %d %s = %s\n", i, tag.datatype, tag.name, tag.data); - } - } - } -#endif - const char *tag_data; - unsigned int looppt[2]; - bool looppt_as_samples[2], have_looppt[2] = { false }; - static const char *const loop_tags[2] = { "LOOP_START", "LOOP_END" }; - - for (int i = 0; i < 2; ++i) - { - if (NULL != (tag_data = GetTagData(sound, loop_tags[i]))) - { - if (S_ParseTimeTag(tag_data, &looppt_as_samples[i], &looppt[i])) - { - have_looppt[i] = true; - } - else - { - Printf("Invalid %s tag: '%s'\n", loop_tags[i], tag_data); - } - } - } - if (have_looppt[0] && !have_looppt[1]) - { // Have a start tag, but not an end tag: End at the end of the song. - have_looppt[1] = (FMOD_OK == sound->getLength(&looppt[1], FMOD_TIMEUNIT_PCM)); - looppt_as_samples[1] = true; - } - else if (!have_looppt[0] && have_looppt[1]) - { // Have an end tag, but no start tag: Start at beginning of the song. - looppt[0] = 0; - looppt_as_samples[0] = true; - have_looppt[0] = true; - } - if (have_looppt[0] && have_looppt[1]) - { // Have both loop points: Try to set the loop. - FMOD_RESULT res = sound->setLoopPoints( - looppt[0], looppt_as_samples[0] ? FMOD_TIMEUNIT_PCM : FMOD_TIMEUNIT_MS, - looppt[1] - 1, looppt_as_samples[1] ? FMOD_TIMEUNIT_PCM : FMOD_TIMEUNIT_MS); - if (res != FMOD_OK) - { - Printf("Setting custom loop points failed. Error %d\n", res); - } - } - // Check for a bi-directional loop. - if (NULL != (tag_data = GetTagData(sound, "LOOP_BIDI")) && - (stricmp(tag_data, "on") == 0 || - stricmp(tag_data, "true") == 0 || - stricmp(tag_data, "yes") == 0 || - stricmp(tag_data, "1") == 0)) - { - FMOD_MODE mode; - if (FMOD_OK == (sound->getMode(&mode))) - { - sound->setMode((mode & ~(FMOD_LOOP_OFF | FMOD_LOOP_NORMAL)) | FMOD_LOOP_BIDI); - } - } -} - -//========================================================================== -// -// open_reader_callback -// close_reader_callback -// read_reader_callback -// seek_reader_callback -// -// FMOD_CREATESOUNDEXINFO callbacks to handle reading resource data from a -// FileReader. -// -//========================================================================== - -#if FMOD_STUDIO -static FMOD_RESULT F_CALLBACK open_reader_callback(const char *name, unsigned int *filesize, void **handle, void *userdata) -#else -static FMOD_RESULT F_CALLBACK open_reader_callback(const char *name, int unicode, unsigned int *filesize, void **handle, void **userdata) -#endif -{ - FileReader *reader = NULL; - if(sscanf(name, "_FileReader_%p", &reader) != 1) - { - Printf("Invalid name in callback: %s\n", name); - return FMOD_ERR_FILE_NOTFOUND; - } - - *filesize = reader->GetLength(); - *handle = reader; -#if !FMOD_STUDIO - *userdata = reader; -#endif - return FMOD_OK; -} - -static FMOD_RESULT F_CALLBACK close_reader_callback(void *handle, void *userdata) -{ - return FMOD_OK; -} - -static FMOD_RESULT F_CALLBACK read_reader_callback(void *handle, void *buffer, unsigned int sizebytes, unsigned int *bytesread, void *userdata) -{ - FileReader *reader = reinterpret_cast(handle); - *bytesread = reader->Read(buffer, sizebytes); - if(*bytesread > 0) return FMOD_OK; - return FMOD_ERR_FILE_EOF; -} - -static FMOD_RESULT F_CALLBACK seek_reader_callback(void *handle, unsigned int pos, void *userdata) -{ - FileReader *reader = reinterpret_cast(handle); - if(reader->Seek(pos, SEEK_SET) == 0) - return FMOD_OK; - return FMOD_ERR_FILE_COULDNOTSEEK; -} - - -//========================================================================== -// -// FMODSoundRenderer :: OpenStream -// -// Creates a streaming sound from a FileReader. -// -//========================================================================== - -SoundStream *FMODSoundRenderer::OpenStream(FileReader *reader, int flags) -{ - FMOD_MODE mode; - FMOD_CREATESOUNDEXINFO exinfo; - FMOD::Sound *stream; - FMOD_RESULT result; - FString patches; - FString name; - - InitCreateSoundExInfo(&exinfo); -#if FMOD_STUDIO - exinfo.fileuseropen = open_reader_callback; - exinfo.fileuserclose = close_reader_callback; - exinfo.fileuserread = read_reader_callback; - exinfo.fileuserseek = seek_reader_callback; -#else - exinfo.useropen = open_reader_callback; - exinfo.userclose = close_reader_callback; - exinfo.userread = read_reader_callback; - exinfo.userseek = seek_reader_callback; -#endif - - mode = FMOD_SOFTWARE | FMOD_2D | FMOD_CREATESTREAM; - if(flags & SoundStream::Loop) - mode |= FMOD_LOOP_NORMAL; - if((*snd_midipatchset)[0] != '\0') - { -#ifdef _WIN32 - // If the path does not contain any path separators, automatically - // prepend $PROGDIR to the path. - if (strcspn(snd_midipatchset, ":/\\") == strlen(snd_midipatchset)) - { - patches << "$PROGDIR/" << snd_midipatchset; - patches = NicePath(patches); - } - else -#endif - { - patches = NicePath(snd_midipatchset); - } - exinfo.dlsname = patches; - } - - name.Format("_FileReader_%p", reader); - result = Sys->createSound(name, mode, &exinfo, &stream); - if(result == FMOD_ERR_FORMAT && exinfo.dlsname != NULL) - { - // FMOD_ERR_FORMAT could refer to either the main sound file or - // to the DLS instrument set. Try again without special DLS - // instruments to see if that lets it succeed. - exinfo.dlsname = NULL; - result = Sys->createSound(name, mode, &exinfo, &stream); - if (result == FMOD_OK) - { - Printf("%s is an unsupported format.\n", *snd_midipatchset); - } - } - if(result != FMOD_OK) - return NULL; - - SetCustomLoopPts(stream); - return new FMODStreamCapsule(stream, this, reader); -} - -SoundStream *FMODSoundRenderer::OpenStream(const char *url, int flags) -{ - FMOD_MODE mode; - FMOD_CREATESOUNDEXINFO exinfo; - FMOD::Sound *stream; - FMOD_RESULT result; - FString patches; - - InitCreateSoundExInfo(&exinfo); - mode = FMOD_SOFTWARE | FMOD_2D | FMOD_CREATESTREAM; - if(flags & SoundStream::Loop) - mode |= FMOD_LOOP_NORMAL; - if((*snd_midipatchset)[0] != '\0') - { -#ifdef _WIN32 - // If the path does not contain any path separators, automatically - // prepend $PROGDIR to the path. - if (strcspn(snd_midipatchset, ":/\\") == strlen(snd_midipatchset)) - { - patches << "$PROGDIR/" << snd_midipatchset; - patches = NicePath(patches); - } - else -#endif - { - patches = NicePath(snd_midipatchset); - } - exinfo.dlsname = patches; - } - - result = Sys->createSound(url, mode, &exinfo, &stream); - if(result == FMOD_ERR_FORMAT && exinfo.dlsname != NULL) - { - exinfo.dlsname = NULL; - result = Sys->createSound(url, mode, &exinfo, &stream); - if(result == FMOD_OK) - { - Printf("%s is an unsupported format.\n", *snd_midipatchset); - } - } - - if(result != FMOD_OK) - return NULL; - - SetCustomLoopPts(stream); - return new FMODStreamCapsule(stream, this, url); -} - -//========================================================================== -// -// FMODSoundRenderer :: StartSound -// -//========================================================================== - -FISoundChannel *FMODSoundRenderer::StartSound(SoundHandle sfx, float vol, int pitch, int flags, FISoundChannel *reuse_chan) -{ - FMOD_RESULT result; - FMOD_MODE mode; - FMOD::Channel *chan; - float freq; - -#if FMOD_STUDIO - if (FMOD_OK == ((FMOD::Sound *)sfx.data)->getDefaults(&freq, NULL)) -#else - if (FMOD_OK == ((FMOD::Sound *)sfx.data)->getDefaults(&freq, NULL, NULL, NULL)) -#endif - { - freq = PITCH(freq, pitch); - } - else - { - freq = 0; - } - - GRolloff = NULL; // Do 2D sounds need rolloff? -#if FMOD_STUDIO - result = Sys->playSound((FMOD::Sound *)sfx.data, (flags & SNDF_NOPAUSE) ? SfxGroup : PausableSfx, true, &chan); -#else - result = Sys->playSound(FMOD_CHANNEL_FREE, (FMOD::Sound *)sfx.data, true, &chan); -#endif - if (FMOD_OK == result) - { - result = chan->getMode(&mode); - - if (result != FMOD_OK) - { - assert(0); - mode = FMOD_SOFTWARE; - } - mode = (mode & ~FMOD_3D) | FMOD_2D; - if (flags & SNDF_LOOP) - { - mode &= ~FMOD_LOOP_OFF; - if (!(mode & (FMOD_LOOP_NORMAL | FMOD_LOOP_BIDI))) - { - mode |= FMOD_LOOP_NORMAL; - } - } - else - { - mode |= FMOD_LOOP_OFF; - } - chan->setMode(mode); -#if !FMOD_STUDIO - chan->setChannelGroup((flags & SNDF_NOPAUSE) ? SfxGroup : PausableSfx); -#endif - if (freq != 0) - { - chan->setFrequency(freq); - } - chan->setVolume(vol); - if (!HandleChannelDelay(chan, reuse_chan, flags & (SNDF_ABSTIME | SNDF_LOOP), freq)) - { - chan->stop(); - return NULL; - } - if (flags & SNDF_NOREVERB) - { -#if FMOD_STUDIO - chan->setReverbProperties(0,0.f); -#else - FMOD_REVERB_CHANNELPROPERTIES reverb = { 0, }; - if (FMOD_OK == chan->getReverbProperties(&reverb)) - { - reverb.Room = -10000; - chan->setReverbProperties(&reverb); - } -#endif - } - chan->setPaused(false); - return CommonChannelSetup(chan, reuse_chan); - } - - //DPrintf (DMSG_WARNING, "Sound %s failed to play: %d\n", sfx->name.GetChars(), result); - return NULL; -} - -//========================================================================== -// -// FMODSoundRenderer :: StartSound3D -// -//========================================================================== - -FISoundChannel *FMODSoundRenderer::StartSound3D(SoundHandle sfx, SoundListener *listener, float vol, - FRolloffInfo *rolloff, float distscale, - int pitch, int priority, const FVector3 &pos, const FVector3 &vel, - int channum, int flags, FISoundChannel *reuse_chan) -{ - FMOD_RESULT result; - FMOD_MODE mode; - FMOD::Channel *chan; - float freq; - float def_freq; -#if !FMOD_STUDIO - float def_vol, def_pan; -#endif - int numchans; - int def_priority; - -#if FMOD_STUDIO - if (FMOD_OK == ((FMOD::Sound *)sfx.data)->getDefaults(&def_freq, &def_priority)) -#else - if (FMOD_OK == ((FMOD::Sound *)sfx.data)->getDefaults(&def_freq, &def_vol, &def_pan, &def_priority)) -#endif - { - freq = PITCH(def_freq, pitch); - // Change the sound's default priority before playing it. -#if FMOD_STUDIO - ((FMOD::Sound *)sfx.data)->setDefaults(def_freq, clamp(def_priority - priority, 1, 256)); -#else - ((FMOD::Sound *)sfx.data)->setDefaults(def_freq, def_vol, def_pan, clamp(def_priority - priority, 1, 256)); -#endif - } - else - { - freq = 0; - def_priority = -1; - } - - // Play it. - GRolloff = rolloff; - GDistScale = distscale; - - // Experiments indicate that playSound will ignore priorities and always succeed - // as long as the parameters are set properly. It will first try to kick out sounds - // with the same priority level but has no problem with kicking out sounds at - // higher priority levels if it needs to. -#if FMOD_STUDIO - result = Sys->playSound((FMOD::Sound *)sfx.data, (flags & SNDF_NOPAUSE) ? SfxGroup : PausableSfx, true, &chan); -#else - result = Sys->playSound(FMOD_CHANNEL_FREE, (FMOD::Sound *)sfx.data, true, &chan); -#endif - - // Then set the priority back. - if (def_priority >= 0) - { -#if FMOD_STUDIO - ((FMOD::Sound *)sfx.data)->setDefaults(def_freq, def_priority); -#else - ((FMOD::Sound *)sfx.data)->setDefaults(def_freq, def_vol, def_pan, def_priority); -#endif - } - - if (FMOD_OK == result) - { - result = chan->getMode(&mode); - if (result != FMOD_OK) - { - mode = FMOD_3D | FMOD_SOFTWARE; - } - if (flags & SNDF_LOOP) - { - mode &= ~FMOD_LOOP_OFF; - if (!(mode & (FMOD_LOOP_NORMAL | FMOD_LOOP_BIDI))) - { - mode |= FMOD_LOOP_NORMAL; - } - } - else - { - // FMOD_LOOP_OFF overrides FMOD_LOOP_NORMAL and FMOD_LOOP_BIDI - mode |= FMOD_LOOP_OFF; - } - mode = SetChanHeadSettings(listener, chan, pos, !!(flags & SNDF_AREA), mode); - chan->setMode(mode); -#if !FMOD_STUDIO - chan->setChannelGroup((flags & SNDF_NOPAUSE) ? SfxGroup : PausableSfx); -#endif - - if (mode & FMOD_3D) - { - // Reduce volume of stereo sounds, because each channel will be summed together - // and is likely to be very similar, resulting in an amplitude twice what it - // would have been had it been mixed to mono. - if (FMOD_OK == ((FMOD::Sound *)sfx.data)->getFormat(NULL, NULL, &numchans, NULL)) - { - if (numchans > 1) - { - vol *= 0.5f; - } - } - } - if (freq != 0) - { - chan->setFrequency(freq); - } - chan->setVolume(vol); - if (mode & FMOD_3D) - { - chan->set3DAttributes((FMOD_VECTOR *)&pos[0], (FMOD_VECTOR *)&vel[0]); - } - if (!HandleChannelDelay(chan, reuse_chan, flags & (SNDF_ABSTIME | SNDF_LOOP), freq)) - { - // FMOD seems to get confused if you stop a channel right after - // starting it, so hopefully this function will never fail. - // (Presumably you need an update between them, but I haven't - // tested this hypothesis.) - chan->stop(); - return NULL; - } - if (flags & SNDF_NOREVERB) - { -#if FMOD_STUDIO - chan->setReverbProperties(0,0.f); -#else - FMOD_REVERB_CHANNELPROPERTIES reverb = { 0, }; - if (FMOD_OK == chan->getReverbProperties(&reverb)) - { - reverb.Room = -10000; - chan->setReverbProperties(&reverb); - } -#endif - } - chan->setPaused(false); - chan->getPriority(&def_priority); - FISoundChannel *schan = CommonChannelSetup(chan, reuse_chan); - schan->Rolloff = *rolloff; - return schan; - } - - GRolloff = NULL; - //DPrintf (DMSG_WARNING, "Sound %s failed to play: %d\n", sfx->name.GetChars(), result); - return 0; -} - -//========================================================================== -// -// FMODSoundRenderer :: MarkStartTime -// -// Marks a channel's start time without actually playing it. -// -//========================================================================== - -void FMODSoundRenderer::MarkStartTime(FISoundChannel *chan) -{ -#if FMOD_STUDIO - uint64_t dsp_time; - ((FMOD::Channel *)chan->SysChannel)->getDSPClock(&dsp_time,NULL); - chan->StartTime.Lo = dsp_time & 0xFFFFFFFF; - chan->StartTime.Hi = dsp_time >> 32; -#else - Sys->getDSPClock(&chan->StartTime.Hi, &chan->StartTime.Lo); -#endif -} - -//========================================================================== -// -// FMODSoundRenderer :: HandleChannelDelay -// -// If the sound is restarting, seek it to its proper place. Returns false -// if the sound would have ended. -// -// Otherwise, record its starting time, and return true. -// -//========================================================================== - -bool FMODSoundRenderer::HandleChannelDelay(FMOD::Channel *chan, FISoundChannel *reuse_chan, int flags, float freq) const -{ - if (reuse_chan != NULL) - { // Sound is being restarted, so seek it to the position - // it would be in now if it had never been evicted. - QWORD_UNION nowtime; -#if FMOD_STUDIO - uint64_t delay; - chan->getDelay(&delay,NULL,NULL); - nowtime.Lo = delay & 0xFFFFFFFF; - nowtime.Hi = delay >> 32; -#else - chan->getDelay(FMOD_DELAYTYPE_DSPCLOCK_START, &nowtime.Hi, &nowtime.Lo); -#endif - - // If abstime is set, the sound is being restored, and - // the channel's start time is actually its seek position. - if (flags & SNDF_ABSTIME) - { - unsigned int seekpos = reuse_chan->StartTime.Lo; - if (seekpos > 0) - { - chan->setPosition(seekpos, FMOD_TIMEUNIT_PCM); - } - reuse_chan->StartTime.AsOne = uint64_t(nowtime.AsOne - seekpos * OutputRate / freq); - } - else if (reuse_chan->StartTime.AsOne != 0) - { - uint64_t difftime = nowtime.AsOne - reuse_chan->StartTime.AsOne; - if (difftime > 0) - { - // Clamp the position of looping sounds to be within the sound. - // If we try to start it several minutes past its normal end, - // FMOD doesn't like that. - // FIXME: Clamp this right for loops that don't cover the whole sound. - if (flags & SNDF_LOOP) - { - FMOD::Sound *sound; - if (FMOD_OK == chan->getCurrentSound(&sound)) - { - unsigned int len; - if (FMOD_OK == sound->getLength(&len, FMOD_TIMEUNIT_MS) && len != 0) - { - difftime %= len; - } - } - } - return chan->setPosition((unsigned int)(difftime / OutputRate), FMOD_TIMEUNIT_MS) == FMOD_OK; - } - } - } - else - { -// chan->setDelay(FMOD_DELAYTYPE_DSPCLOCK_START, DSPClock.Hi, DSPClock.Lo); - } - return true; -} - -//========================================================================== -// -// FMODSoundRenderer :: SetChanHeadSettings -// -// If this sound is played at the same coordinates as the listener, make -// it head relative. Also, area sounds should use no 3D panning if close -// enough to the listener. -// -//========================================================================== - -FMOD_MODE FMODSoundRenderer::SetChanHeadSettings(SoundListener *listener, FMOD::Channel *chan, - const FVector3 &pos, bool areasound, - FMOD_MODE oldmode) const -{ - if (!listener->valid) - { - return oldmode; - } - FVector3 cpos, mpos; - - cpos = listener->position; - - if (areasound) - { - float level, old_level; - - // How far are we from the perceived sound origin? Within a certain - // short distance, we interpolate between 2D panning and full 3D panning. - const double interp_range = 32.0; - double dist_sqr = (cpos - pos).LengthSquared(); - - if (dist_sqr == 0) - { - level = 0; - } - else if (dist_sqr <= interp_range * interp_range) - { // Within interp_range: Interpolate between none and full 3D panning. - level = float(1 - (interp_range - sqrt(dist_sqr)) / interp_range); - } - else - { // Beyond interp_range: Normal 3D panning. - level = 1; - } -#if FMOD_STUDIO - if (chan->get3DLevel(&old_level) == FMOD_OK && old_level != level) - { // Only set it if it's different. - chan->set3DLevel(level); -#else - if (chan->get3DPanLevel(&old_level) == FMOD_OK && old_level != level) - { // Only set it if it's different. - chan->set3DPanLevel(level); -#endif - if (level < 1) - { // Let the noise come from all speakers, not just the front ones. - // A centered 3D sound does not play at full volume, so neither should the 2D-panned one. - // This is sqrt(0.5), which is the result for a centered equal power panning. -#if FMOD_STUDIO - chan->setMixLevelsOutput(0.70711f,0.70711f,0.70711f,0.70711f,0.70711f,0.70711f,0.70711f,0.70711f); -#else - chan->setSpeakerMix(0.70711f,0.70711f,0.70711f,0.70711f,0.70711f,0.70711f,0.70711f,0.70711f); -#endif - } - } - return oldmode; - } - else if ((cpos - pos).LengthSquared() < (0.0004 * 0.0004)) - { // Head relative - return (oldmode & ~FMOD_3D) | FMOD_2D; - } - // World relative - return (oldmode & ~FMOD_2D) | FMOD_3D; -} - -//========================================================================== -// -// FMODSoundRenderer :: CommonChannelSetup -// -// Assign an end callback to the channel and allocates a game channel for -// it. -// -//========================================================================== - -FISoundChannel *FMODSoundRenderer::CommonChannelSetup(FMOD::Channel *chan, FISoundChannel *reuse_chan) const -{ - FISoundChannel *schan; - - if (reuse_chan != NULL) - { - schan = reuse_chan; - schan->SysChannel = chan; - } - else - { - schan = S_GetChannel(chan); -#if FMOD_STUDIO - uint64_t time; - chan->getDelay(&time,NULL,NULL); - schan->StartTime.Lo = time & 0xFFFFFFFF; - schan->StartTime.Hi = time >> 32; -#else - chan->getDelay(FMOD_DELAYTYPE_DSPCLOCK_START, &schan->StartTime.Hi, &schan->StartTime.Lo); -#endif - } - chan->setUserData(schan); - chan->setCallback(ChannelCallback); - GRolloff = NULL; - return schan; -} - -//========================================================================== -// -// FMODSoundRenderer :: StopChannel -// -//========================================================================== - -void FMODSoundRenderer::StopChannel(FISoundChannel *chan) -{ - if (chan != NULL && chan->SysChannel != NULL) - { - if (((FMOD::Channel *)chan->SysChannel)->stop() == FMOD_ERR_INVALID_HANDLE) - { // The channel handle was invalid; pretend it ended. - S_ChannelEnded(chan); - } - } -} - -//========================================================================== -// -// FMODSoundRenderer :: ChannelVolume -// -//========================================================================== - -void FMODSoundRenderer::ChannelVolume(FISoundChannel *chan, float volume) -{ - if (chan != NULL && chan->SysChannel != NULL) - { - ((FMOD::Channel *)chan->SysChannel)->setVolume(volume); - } -} - -//========================================================================== -// -// FMODSoundRenderer :: GetPosition -// -// Returns position of sound on this channel, in samples. -// -//========================================================================== - -unsigned int FMODSoundRenderer::GetPosition(FISoundChannel *chan) -{ - unsigned int pos; - - if (chan == NULL || chan->SysChannel == NULL) - { - return 0; - } - ((FMOD::Channel *)chan->SysChannel)->getPosition(&pos, FMOD_TIMEUNIT_PCM); - return pos; -} - -//========================================================================== -// -// FMODSoundRenderer :: GetAudibility -// -// Returns the audible volume of the channel, after rollof and any other -// factors are applied. -// -//========================================================================== - -float FMODSoundRenderer::GetAudibility(FISoundChannel *chan) -{ - float aud; - - if (chan == NULL || chan->SysChannel == NULL) - { - return 0; - } - ((FMOD::Channel *)chan->SysChannel)->getAudibility(&aud); - return aud; -} - -//========================================================================== -// -// FMODSoundRenderer :: SetSfxPaused -// -//========================================================================== - -void FMODSoundRenderer::SetSfxPaused(bool paused, int slot) -{ - int oldslots = SFXPaused; - - if (paused) - { - SFXPaused |= 1 << slot; - } - else - { - SFXPaused &= ~(1 << slot); - } - //Printf("%d\n", SFXPaused); - if (oldslots != 0 && SFXPaused == 0) - { - PausableSfx->setPaused(false); - } - else if (oldslots == 0 && SFXPaused != 0) - { - PausableSfx->setPaused(true); - } -} - -//========================================================================== -// -// FMODSoundRenderer :: SetInactive -// -// This is similar to SetSfxPaused but will *pause* everything, including -// the global reverb effect. This is meant to be used only when the -// game is deactivated, not for general sound pausing. -// -//========================================================================== - -void FMODSoundRenderer::SetInactive(SoundRenderer::EInactiveState inactive) -{ - float mix; - bool active; - - if (inactive == INACTIVE_Active) - { - mix = 1; - active = true; - } - else if (inactive == INACTIVE_Complete) - { - mix = 1; - active = false; - } - else // inactive == INACTIVE_Mute - { - mix = 0; - active = true; - } - if (ChannelGroupTargetUnitOutput != NULL) - { - ChannelGroupTargetUnitOutput->setMix(mix); - } - if (ChannelGroupTargetUnit != NULL) - { - ChannelGroupTargetUnit->setActive(active); - } -} - -//========================================================================== -// -// FMODSoundRenderer :: UpdateSoundParams3D -// -//========================================================================== - -void FMODSoundRenderer::UpdateSoundParams3D(SoundListener *listener, FISoundChannel *chan, bool areasound, const FVector3 &pos, const FVector3 &vel) -{ - if (chan == NULL || chan->SysChannel == NULL) - return; - - FMOD::Channel *fchan = (FMOD::Channel *)chan->SysChannel; - FMOD_MODE oldmode, mode; - - if (fchan->getMode(&oldmode) != FMOD_OK) - { - oldmode = FMOD_3D | FMOD_SOFTWARE; - } - mode = SetChanHeadSettings(listener, fchan, pos, areasound, oldmode); - if (mode != oldmode) - { // Only set the mode if it changed. - fchan->setMode(mode); - } - fchan->set3DAttributes((FMOD_VECTOR *)&pos[0], (FMOD_VECTOR *)&vel[0]); -} - -//========================================================================== -// -// FMODSoundRenderer :: UpdateListener -// -//========================================================================== - -void FMODSoundRenderer::UpdateListener(SoundListener *listener) -{ - FMOD_VECTOR pos, vel; - FMOD_VECTOR forward; - FMOD_VECTOR up; - - if (!listener->valid) - { - return; - } - - // Set velocity to 0 to prevent crazy doppler shifts just from running. - - vel.x = listener->velocity.X; - vel.y = listener->velocity.Y; - vel.z = listener->velocity.Z; - pos.x = listener->position.X; - pos.y = listener->position.Y; - pos.z = listener->position.Z; - - float angle = listener->angle; - forward.x = cosf(angle); - forward.y = 0; - forward.z = sinf(angle); - - up.x = 0; - up.y = 1; - up.z = 0; - - Sys->set3DListenerAttributes(0, &pos, &vel, &forward, &up); - - bool underwater = false; - const ReverbContainer *env; - - underwater = (listener->underwater && snd_waterlp); - if (ForcedEnvironment) - { - env = ForcedEnvironment; - } - else - { - env = listener->Environment; - if (env == NULL) - { - env = DefaultEnvironments[0]; - } - } - if (env != PrevEnvironment || env->Modified) - { - DPrintf (DMSG_NOTIFY, "Reverb Environment %s\n", env->Name); - const_cast(env)->Modified = false; - SetSystemReverbProperties(&env->Properties); - PrevEnvironment = env; - - if (!SfxReverbHooked) - { - SfxReverbHooked = ReconnectSFXReverbUnit(); - } - } - - if (underwater || env->SoftwareWater) - { - //PausableSfx->setPitch(0.64171f); // This appears to be what Duke 3D uses - PausableSfx->setPitch(0.7937005f); // Approx. 4 semitones lower; what Nash suggested - if (WaterLP != NULL) - { - if (LastWaterLP != snd_waterlp) - { - LastWaterLP = snd_waterlp; - WaterLP->setParameterFloat(FMOD_DSP_LOWPASS_CUTOFF, snd_waterlp); - } - WaterLP->setActive(true); - if (WaterReverb != NULL && snd_waterreverb) - { - WaterReverb->setActive(true); - WaterReverb->setBypass(false); - SfxConnection->setMix(0); - } - else - { - // Let some of the original mix through so that high frequencies are - // not completely lost. The reverb unit has its own connection and - // preserves dry sounds itself if used. - SfxConnection->setMix(0.1f); - if (WaterReverb != NULL) - { - WaterReverb->setActive(true); - WaterReverb->setBypass(true); - } - } - } - } - else - { - PausableSfx->setPitch(1); - if (WaterLP != NULL) - { - SfxConnection->setMix(1); - WaterLP->setActive(false); - if (WaterReverb != NULL) - { - WaterReverb->setActive(false); - } - } - } -} - -//========================================================================== -// -// FMODSoundRenderer :: ReconnectSFXReverbUnit -// -// Locates the DSP unit responsible for software 3D reverb. There is only -// one, and it by default is connected directly to the ChannelGroup Target -// Unit. Older versions of FMOD created this at startup; newer versions -// delay creating it until the first call to setReverbProperties, at which -// point it persists until the system is closed. -// -// Upon locating the proper DSP unit, reconnects it to serve as an input to -// our water DSP chain after the Pausable SFX ChannelGroup. -// -//========================================================================== - -bool FMODSoundRenderer::ReconnectSFXReverbUnit() -{ - FMOD::DSP *unit; - FMOD_DSP_TYPE type; - int numinputs, i; - - if (ChannelGroupTargetUnit == NULL || SfxReverbPlaceholder == NULL) - { - return false; - } - // Look for SFX Reverb unit - if (FMOD_OK != ChannelGroupTargetUnit->getNumInputs(&numinputs)) - { - return false; - } - for (i = numinputs - 1; i >= 0; --i) - { - if (FMOD_OK == ChannelGroupTargetUnit->getInput(i, &unit, NULL) && - FMOD_OK == unit->getType(&type)) - { - if (type == FMOD_DSP_TYPE_SFXREVERB) - { - break; - } - } - } - if (i < 0) - { - return false; - } - - // Found it! Now move it in the DSP graph to be done before the water - // effect. - if (FMOD_OK != ChannelGroupTargetUnit->disconnectFrom(unit)) - { - return false; - } - if (FMOD_OK != SfxReverbPlaceholder->addInput(unit, NULL)) - { - return false; - } - return true; -} - -//========================================================================== -// -// FMODSoundRenderer :: Sync -// -// Used by the save/load code to restart sounds at the same position they -// were in at the time of saving. Must not be nested. -// -//========================================================================== - -void FMODSoundRenderer::Sync(bool sync) -{ - DSPLocked = sync; - if (sync) - { - Sys->lockDSP(); -#if FMOD_STUDIO - uint64_t clock; - SfxGroup->getDSPClock(&clock,NULL); - DSPClock.Lo = clock & 0xFFFFFFFF; - DSPClock.Hi = clock >> 32; -#else - Sys->getDSPClock(&DSPClock.Hi, &DSPClock.Lo); -#endif - } - else - { - Sys->unlockDSP(); - } -} - -//========================================================================== -// -// FMODSoundRenderer :: UpdateSounds -// -//========================================================================== - -void FMODSoundRenderer::UpdateSounds() -{ - // Any sounds played between now and the next call to this function - // will start exactly one tic from now. -#if FMOD_STUDIO - uint64_t clock; - SfxGroup->getDSPClock(&clock,NULL); - DSPClock.Lo = clock & 0xFFFFFFFF; - DSPClock.Hi = clock >> 32; -#else - Sys->getDSPClock(&DSPClock.Hi, &DSPClock.Lo); -#endif - DSPClock.AsOne += OutputRate / TICRATE; - Sys->update(); -} - -//========================================================================== -// -// FMODSoundRenderer :: LoadSoundRaw -// -//========================================================================== - -std::pair FMODSoundRenderer::LoadSoundRaw(uint8_t *sfxdata, int length, int frequency, int channels, int bits, int loopstart, int loopend, bool monoize) -{ - FMOD_CREATESOUNDEXINFO exinfo; - SoundHandle retval = { NULL }; - int numsamples; - - if (length <= 0) - { - return std::make_pair(retval, true); - } - - InitCreateSoundExInfo(&exinfo); - exinfo.length = length; - exinfo.numchannels = channels; - exinfo.defaultfrequency = frequency; - switch (bits) - { -#if FMOD_STUDIO - case -8: - // Need to convert sample data from signed to unsigned. - for (int i = 0; i < length; i++) - { - sfxdata[i] ^= 0x80; - } - - case 8: -#else // !FMOD_STUDIO - case 8: - // Need to convert sample data from unsigned to signed. - for (int i = 0; i < length; ++i) - { - sfxdata[i] = sfxdata[i] - 128; - } - - case -8: -#endif // FMOD_STUDIO - exinfo.format = FMOD_SOUND_FORMAT_PCM8; - numsamples = length; - break; - - case 16: - exinfo.format = FMOD_SOUND_FORMAT_PCM16; - numsamples = length >> 1; - break; - - case 32: - exinfo.format = FMOD_SOUND_FORMAT_PCM32; - numsamples = length >> 2; - break; - - default: - return std::make_pair(retval, true); - } - - const FMOD_MODE samplemode = FMOD_3D | FMOD_OPENMEMORY | FMOD_SOFTWARE | FMOD_OPENRAW; - FMOD::Sound *sample; - FMOD_RESULT result; - - result = Sys->createSound((char *)sfxdata, samplemode, &exinfo, &sample); - if (result != FMOD_OK) - { - DPrintf(DMSG_ERROR, "Failed to allocate sample: Error %d\n", result); - return std::make_pair(retval, true); - } - - if (loopstart >= 0) - { - if (loopend == -1) - loopend = numsamples - 1; - sample->setLoopPoints(loopstart, FMOD_TIMEUNIT_PCM, loopend, FMOD_TIMEUNIT_PCM); - } - - retval.data = sample; - return std::make_pair(retval, true); -} - -//========================================================================== -// -// FMODSoundRenderer :: LoadSound -// -//========================================================================== - -std::pair FMODSoundRenderer::LoadSound(uint8_t *sfxdata, int length, bool monoize) -{ - FMOD_CREATESOUNDEXINFO exinfo; - SoundHandle retval = { NULL }; - - if (length == 0) return std::make_pair(retval, true); - - InitCreateSoundExInfo(&exinfo); - exinfo.length = length; - - const FMOD_MODE samplemode = FMOD_3D | FMOD_OPENMEMORY | FMOD_SOFTWARE; - FMOD::Sound *sample; - FMOD_RESULT result; - - result = Sys->createSound((char *)sfxdata, samplemode, &exinfo, &sample); - if (result != FMOD_OK) - { - DPrintf(DMSG_ERROR, "Failed to allocate sample: Error %d\n", result); - return std::make_pair(retval, true); - } - SetCustomLoopPts(sample); - retval.data = sample; - return std::make_pair(retval, true); -} - -//========================================================================== -// -// FMODSoundRenderer :: UnloadSound -// -//========================================================================== - -void FMODSoundRenderer::UnloadSound(SoundHandle sfx) -{ - if (sfx.data != NULL) - { - ((FMOD::Sound *)sfx.data)->release(); - } -} - -//========================================================================== -// -// FMODSoundRenderer :: GetMSLength -// -//========================================================================== - -unsigned int FMODSoundRenderer::GetMSLength(SoundHandle sfx) -{ - if (sfx.data != NULL) - { - unsigned int length; - - if (((FMOD::Sound *)sfx.data)->getLength(&length, FMOD_TIMEUNIT_MS) == FMOD_OK) - { - return length; - } - } - return 0; // Don't know. -} - - -//========================================================================== -// -// FMODSoundRenderer :: GetMSLength -// -//========================================================================== - -unsigned int FMODSoundRenderer::GetSampleLength(SoundHandle sfx) -{ - if (sfx.data != NULL) - { - unsigned int length; - - if (((FMOD::Sound *)sfx.data)->getLength(&length, FMOD_TIMEUNIT_PCM) == FMOD_OK) - { - return length; - } - } - return 0; // Don't know. -} - - -//========================================================================== -// -// FMODSoundRenderer :: ChannelCallback static -// -// Handles when a channel finishes playing. This is only called when -// System::update is called and is therefore asynchronous with the actual -// end of the channel. -// -//========================================================================== - -FMOD_RESULT F_CALLBACK FMODSoundRenderer::ChannelCallback -#if FMOD_STUDIO - (FMOD_CHANNELCONTROL *channel, FMOD_CHANNELCONTROL_TYPE controltype, FMOD_CHANNELCONTROL_CALLBACK_TYPE type, void *data1, void *data2) -#else - (FMOD_CHANNEL *channel, FMOD_CHANNEL_CALLBACKTYPE type, void *data1, void *data2) -#endif -{ -#if FMOD_STUDIO - FMOD::ChannelControl *chan = (FMOD::ChannelControl *)channel; -#else - FMOD::Channel *chan = (FMOD::Channel *)channel; -#endif - FISoundChannel *schan; - - if (chan->getUserData((void **)&schan) == FMOD_OK && schan != NULL) - { -#if FMOD_STUDIO - if (type == FMOD_CHANNELCONTROL_CALLBACK_END) -#else - if (type == FMOD_CHANNEL_CALLBACKTYPE_END) -#endif - { - S_ChannelEnded(schan); - } -#if FMOD_STUDIO - else if (type == FMOD_CHANNELCONTROL_CALLBACK_VIRTUALVOICE) -#else - else if (type == FMOD_CHANNEL_CALLBACKTYPE_VIRTUALVOICE) -#endif - { - S_ChannelVirtualChanged(schan, data1 != 0); - } - } - return FMOD_OK; -} - - -//========================================================================== -// -// FMODSoundRenderer :: RolloffCallback static -// -// Calculates a volume for the sound based on distance. -// -//========================================================================== - -#if FMOD_STUDIO -float F_CALLBACK FMODSoundRenderer::RolloffCallback(FMOD_CHANNELCONTROL *channel, float distance) -#else -float F_CALLBACK FMODSoundRenderer::RolloffCallback(FMOD_CHANNEL *channel, float distance) -#endif -{ -#if FMOD_STUDIO - FMOD::ChannelControl *chan = (FMOD::ChannelControl *)channel; -#else - FMOD::Channel *chan = (FMOD::Channel *)channel; -#endif - FISoundChannel *schan; - - if (GRolloff != NULL) - { - return S_GetRolloff(GRolloff, distance * GDistScale, true); - } - else if (chan->getUserData((void **)&schan) == FMOD_OK && schan != NULL) - { - return S_GetRolloff(&schan->Rolloff, distance * schan->DistanceScale, true); - } - else - { - return 0; - } -} - -//========================================================================== -// -// FMODSoundRenderer :: DrawWaveDebug -// -// Bit 0: ( 1) Show oscilloscope for sfx. -// Bit 1: ( 2) Show spectrum for sfx. -// Bit 2: ( 4) Show oscilloscope for music. -// Bit 3: ( 8) Show spectrum for music. -// Bit 4: (16) Show oscilloscope for all sounds. -// Bit 5: (32) Show spectrum for all sounds. -// -//========================================================================== - -void FMODSoundRenderer::DrawWaveDebug(int mode) -{ - const int window_height = 100; - int window_size; - int numoutchans; - int y, yy; - const spk *labels; - int labelcount; - -#if FMOD_STUDIO - if (FMOD_OK != Sys->getSoftwareFormat(NULL, NULL, &numoutchans)) -#else - if (FMOD_OK != Sys->getSoftwareFormat(NULL, NULL, &numoutchans, NULL, NULL, NULL)) -#endif - { - return; - } - - // Decide on which set of labels to use. - labels = (numoutchans == 4) ? SpeakerNames4 : SpeakerNamesMore; - labelcount = MIN(numoutchans, countof(SpeakerNamesMore)); - - // Scale all the channel windows so one group fits completely on one row, with - // 16 pixels of padding between each window. - window_size = (screen->GetWidth() - 16) / numoutchans - 16; - - float *wavearray = (float*)alloca(MAX(SPECTRUM_SIZE,window_size)*sizeof(float)); - y = 16; - - yy = DrawChannelGroupOutput(SfxGroup, wavearray, window_size, window_height, y, mode); - if (y != yy) - { - DrawSpeakerLabels(labels, yy-14, window_size, labelcount); - } - y = DrawChannelGroupOutput(MusicGroup, wavearray, window_size, window_height, yy, mode >> 2); - if (y != yy) - { - DrawSpeakerLabels(labels, y-14, window_size, labelcount); - } - yy = DrawSystemOutput(wavearray, window_size, window_height, y, mode >> 4); - if (y != yy) - { - DrawSpeakerLabels(labels, yy-14, window_size, labelcount); - } -} - -//========================================================================== -// -// FMODSoundRenderer :: DrawSpeakerLabels -// -//========================================================================== - -void FMODSoundRenderer::DrawSpeakerLabels(const spk *labels, int y, int width, int count) -{ - if (labels == NULL) - { - return; - } - for (int i = 0, x = 16; i < count; ++i) - { - screen->DrawText(SmallFont, CR_LIGHTBLUE, x, y, labels[i], TAG_DONE); - x += width + 16; - } -} - -//========================================================================== -// -// FMODSoundRenderer :: DrawChannelGroupOutput -// -// Draws an oscilloscope and/or a spectrum for a channel group. -// -//========================================================================== - -int FMODSoundRenderer::DrawChannelGroupOutput(FMOD::ChannelGroup *group, float *wavearray, int width, int height, int y, int mode) -{ - int y1, y2; - - switch (mode & 0x03) - { - case 0x01: // Oscilloscope only - return DrawChannelGroupWaveData(group, wavearray, width, height, y, false); - - case 0x02: // Spectrum only - return DrawChannelGroupSpectrum(group, wavearray, width, height, y, false); - - case 0x03: // Oscilloscope + Spectrum - width = (width + 16) / 2 - 16; - y1 = DrawChannelGroupSpectrum(group, wavearray, width, height, y, true); - y2 = DrawChannelGroupWaveData(group, wavearray, width, height, y, true); - return MAX(y1, y2); - } - return y; -} - -//========================================================================== -// -// FMODSoundRenderer :: DrawSystemOutput -// -// Like DrawChannelGroupOutput(), but uses the system object. -// -//========================================================================== - -int FMODSoundRenderer::DrawSystemOutput(float *wavearray, int width, int height, int y, int mode) -{ - int y1, y2; - - switch (mode & 0x03) - { - case 0x01: // Oscilloscope only - return DrawSystemWaveData(wavearray, width, height, y, false); - - case 0x02: // Spectrum only - return DrawSystemSpectrum(wavearray, width, height, y, false); - - case 0x03: // Oscilloscope + Spectrum - width = (width + 16) / 2 - 16; - y1 = DrawSystemSpectrum(wavearray, width, height, y, true); - y2 = DrawSystemWaveData(wavearray, width, height, y, true); - return MAX(y1, y2); - } - return y; -} - -//========================================================================== -// -// FMODSoundRenderer :: DrawChannelGroupWaveData -// -// Draws all the output channels for a specified channel group. -// Setting skip to true causes it to skip every other window. -// -//========================================================================== - -int FMODSoundRenderer::DrawChannelGroupWaveData(FMOD::ChannelGroup *group, float *wavearray, int width, int height, int y, bool skip) -{ - int drawn = 0; - int x = 16; - -#if !FMOD_STUDIO - while (FMOD_OK == group->getWaveData(wavearray, width, drawn)) - { - drawn++; - DrawWave(wavearray, x, y, width, height); - x += (width + 16) << int(skip); - } -#endif - if (drawn) - { - y += height + 16; - } - return y; -} - -//========================================================================== -// -// FMODSoundRenderer::DrawSystemWaveData -// -// Like DrawChannelGroupWaveData, but it uses the system object to get the -// complete output. -// -//========================================================================== - -int FMODSoundRenderer::DrawSystemWaveData(float *wavearray, int width, int height, int y, bool skip) -{ - int drawn = 0; - int x = 16; - -#if !FMOD_STUDIO - while (FMOD_OK == Sys->getWaveData(wavearray, width, drawn)) - { - drawn++; - DrawWave(wavearray, x, y, width, height); - x += (width + 16) << int(skip); - } -#endif - if (drawn) - { - y += height + 16; - } - return y; -} - -//========================================================================== -// -// FMODSoundRenderer :: DrawWave -// -// Draws an oscilloscope at the specified coordinates on the screen. Each -// entry in the wavearray buffer has its own column. IOW, there are -// entries in wavearray. -// -//========================================================================== - -void FMODSoundRenderer::DrawWave(float *wavearray, int x, int y, int width, int height) -{ - float scale = height / 2.f; - float mid = y + scale; - int i; - - // Draw a box around the oscilloscope. - screen->DrawLine(x - 1, y - 1, x + width, y - 1, -1, MAKEARGB(160, 0, 40, 200)); - screen->DrawLine(x + width, y - 1, x + width, y + height, -1, MAKEARGB(160, 0, 40, 200)); - screen->DrawLine(x + width, y + height, x - 1, y + height, -1, MAKEARGB(160, 0, 40, 200)); - screen->DrawLine(x - 1, y + height, x - 1, y - 1, -1, MAKEARGB(160, 0, 40, 200)); - - // Draw the actual oscilloscope. - if (screen->Accel2D) - { // Drawing this with lines is super-slow without hardware acceleration, at least with - // the debug build. - float lasty = mid - wavearray[0] * scale; - float newy; - for (i = 1; i < width; ++i) - { - newy = mid - wavearray[i] * scale; - screen->DrawLine(x + i - 1, int(lasty), x + i, int(newy), -1, MAKEARGB(255,255,248,248)); - lasty = newy; - } - } - else - { - for (i = 0; i < width; ++i) - { - float y = wavearray[i] * scale + mid; - screen->DrawPixel(x + i, int(y), -1, MAKEARGB(255,255,255,255)); - } - } -} - -//========================================================================== -// -// FMODSoundRenderer :: DrawChannelGroupSpectrum -// -// Draws all the spectrum for a specified channel group. -// Setting skip to true causes it to skip every other window, starting at -// the second one. -// -//========================================================================== - -int FMODSoundRenderer::DrawChannelGroupSpectrum(FMOD::ChannelGroup *group, float *spectrumarray, int width, int height, int y, bool skip) -{ - int drawn = 0; - int x = 16; - - if (skip) - { - x += width + 16; - } - // TODO: FMOD Studio: Grab from DSP -#if !FMOD_STUDIO - while (FMOD_OK == group->getSpectrum(spectrumarray, SPECTRUM_SIZE, drawn, FMOD_DSP_FFT_WINDOW_TRIANGLE)) - { - drawn++; - DrawSpectrum(spectrumarray, x, y, width, height); - x += (width + 16) << int(skip); - } -#endif - if (drawn) - { - y += height + 16; - } - return y; -} - -//========================================================================== -// -// FMODSoundRenderer::DrawSystemSpectrum -// -// Like DrawChannelGroupSpectrum, but it uses the system object to get the -// complete output. -// -//========================================================================== - -int FMODSoundRenderer::DrawSystemSpectrum(float *spectrumarray, int width, int height, int y, bool skip) -{ - int drawn = 0; - int x = 16; - - if (skip) - { - x += width + 16; - } - // TODO: FMOD Studio: Grab from DSP -#if !FMOD_STUDIO - while (FMOD_OK == Sys->getSpectrum(spectrumarray, SPECTRUM_SIZE, drawn, FMOD_DSP_FFT_WINDOW_TRIANGLE)) - { - drawn++; - DrawSpectrum(spectrumarray, x, y, width, height); - x += (width + 16) << int(skip); - } -#endif - if (drawn) - { - y += height + 16; - } - return y; -} - -//========================================================================== -// -// FMODSoundRenderer :: DrawSpectrum -// -// Draws a spectrum at the specified coordinates on the screen. -// -//========================================================================== - -void FMODSoundRenderer::DrawSpectrum(float *spectrumarray, int x, int y, int width, int height) -{ - float scale = height / 2.f; - float mid = y + scale; - float db; - int top; - - // Draw a border and dark background for the spectrum. - screen->DrawLine(x - 1, y - 1, x + width, y - 1, -1, MAKEARGB(160, 0, 40, 200)); - screen->DrawLine(x + width, y - 1, x + width, y + height, -1, MAKEARGB(160, 0, 40, 200)); - screen->DrawLine(x + width, y + height, x - 1, y + height, -1, MAKEARGB(160, 0, 40, 200)); - screen->DrawLine(x - 1, y + height, x - 1, y - 1, -1, MAKEARGB(160, 0, 40, 200)); - screen->Dim(MAKERGB(0,0,0), 0.3f, x, y, width, height); - - // Draw the actual spectrum. - for (int i = 0; i < width; ++i) - { - db = spectrumarray[i * (SPECTRUM_SIZE - 2) / width + 1]; - db = MAX(-150.f, 10 * log10f(db) * 2); // Convert to decibels and clamp - db = 1.f - (db / -150.f); - db *= height; - top = (int)db; - if (top >= height) - { - top = height - 1; - } -// screen->Clear(x + i, int(y + height - db), x + i + 1, y + height, -1, MAKEARGB(255, 255, 255, 40)); - screen->Dim(MAKERGB(255,255,40), 0.65f, x + i, y + height - top, 1, top); - } -} - -//========================================================================== -// -// FMODSoundRenderer :: DecodeSample -// -// Uses FMOD to decode a compressed sample to a 16-bit buffer. This is used -// by the DUMB XM reader to handle FMOD's OggMods. -// -//========================================================================== - -short *FMODSoundRenderer::DecodeSample(int outlen, const void *coded, int sizebytes, ECodecType type) -{ - FMOD_CREATESOUNDEXINFO exinfo; - FMOD::Sound *sound; - FMOD_SOUND_FORMAT format; - int channels; - unsigned int len, amt_read; - FMOD_RESULT result; - short *outbuf; - - InitCreateSoundExInfo(&exinfo); - if (type == CODEC_Vorbis) - { - exinfo.suggestedsoundtype = FMOD_SOUND_TYPE_OGGVORBIS; - } - exinfo.length = sizebytes; - result = Sys->createSound((const char *)coded, - FMOD_2D | FMOD_SOFTWARE | FMOD_CREATESTREAM | - FMOD_OPENMEMORY_POINT | FMOD_OPENONLY | FMOD_LOWMEM, - &exinfo, &sound); - if (result != FMOD_OK) - { - return NULL; - } - result = sound->getFormat(NULL, &format, &channels, NULL); - // TODO: Handle more formats if it proves necessary. - if (result != FMOD_OK || format != FMOD_SOUND_FORMAT_PCM16 || channels != 1) - { - sound->release(); - return NULL; - } - len = outlen; - // Must be malloc'ed for DUMB, which is C. - outbuf = (short *)malloc(len); - result = sound->readData(outbuf, len, &amt_read); - sound->release(); - if (result == FMOD_ERR_FILE_EOF) - { - memset((uint8_t *)outbuf + amt_read, 0, len - amt_read); - } - else if (result != FMOD_OK || amt_read != len) - { - free(outbuf); - return NULL; - } - return outbuf; -} - -//========================================================================== -// -// FMODSoundRenderer :: InitCreateSoundExInfo -// -// Allow for compiling with 4.26 APIs while still running with older DLLs. -// -//========================================================================== - -void FMODSoundRenderer::InitCreateSoundExInfo(FMOD_CREATESOUNDEXINFO *exinfo) const -{ - memset(exinfo, 0, sizeof(*exinfo)); -#if !FMOD_STUDIO && FMOD_VERSION >= 0x42600 && FMOD_VERSION < 0x43800 - if (ActiveFMODVersion < 0x42600) - { - // This parameter was added for 4.26.00, and trying to pass it to older - // DLLs will fail. - exinfo->cbsize = myoffsetof(FMOD_CREATESOUNDEXINFO, ignoresetfilesystem); - } - else -#endif - { - exinfo->cbsize = sizeof(*exinfo); - } -} - -//========================================================================== -// -// FMODSoundRenderer :: SetSystemReverbProperties -// -// Set the global reverb properties. -// -//========================================================================== - -FMOD_RESULT FMODSoundRenderer::SetSystemReverbProperties(const REVERB_PROPERTIES *props) -{ -#if !FMOD_STUDIO && FMOD_VERSION < 0x43600 - return Sys->setReverbProperties((const FMOD_REVERB_PROPERTIES *)props); -#else - // The reverb format changed when hardware mixing support was dropped, because - // all EAX-only properties were removed from the structure. - FMOD_REVERB_PROPERTIES fr; - -#if FMOD_STUDIO - const float LateEarlyRatio = powf(10.f, (props->Reverb - props->Reflections)/2000.f); - const float EarlyAndLatePower = powf(10.f, props->Reflections/1000.f) + powf(10, props->Reverb/1000.f); - const float HFGain = powf(10.f, props->RoomHF/2000.f); - fr.DecayTime = props->DecayTime*1000.f; - fr.EarlyDelay = props->ReflectionsDelay*1000.f; - fr.LateDelay = props->ReverbDelay*1000.f; - fr.HFReference = props->HFReference; - fr.HFDecayRatio = clamp(props->DecayHFRatio*100.f, 0.f, 100.f); - fr.Diffusion = props->Diffusion; - fr.Density = props->Density; - fr.LowShelfFrequency = props->DecayLFRatio; - fr.LowShelfGain = clamp(props->RoomLF/100.f, -48.f, 12.f); - fr.HighCut = clamp(props->RoomLF < 0 ? props->HFReference/sqrtf((1.f-HFGain)/HFGain) : 20000.f, 20.f, 20000.f); - fr.EarlyLateMix = props->Reflections > -10000.f ? LateEarlyRatio/(LateEarlyRatio + 1)*100.f : 100.f; - fr.WetLevel = clamp(10*log10f(EarlyAndLatePower)+props->Room/100.f, -80.f, 20.f); - - return Sys->setReverbProperties(0, &fr); -#else - fr.Instance = props->Instance; - fr.Environment = props->Environment; - fr.EnvDiffusion = props->EnvDiffusion; - fr.Room = props->Room; - fr.RoomHF = props->RoomHF; - fr.RoomLF = props->RoomLF; - fr.DecayTime = props->DecayTime; - fr.DecayHFRatio = props->DecayHFRatio; - fr.DecayLFRatio = props->DecayLFRatio; - fr.Reflections = props->Reflections; - fr.ReflectionsDelay = props->ReflectionsDelay; - fr.Reverb = props->Reverb; - fr.ReverbDelay = props->ReverbDelay; - fr.ModulationTime = props->ModulationTime; - fr.ModulationDepth = props->ModulationDepth; - fr.HFReference = props->HFReference; - fr.LFReference = props->LFReference; - fr.Diffusion = props->Diffusion; - fr.Density = props->Density; - fr.Flags = props->Flags; - - return Sys->setReverbProperties(&fr); -#endif -#endif -} - -MIDIDevice* FMODSoundRenderer::CreateMIDIDevice() const -{ - return new SndSysMIDIDevice; -} - -#endif // NO_FMOD - - -//========================================================================== -// -// IsFModExPresent -// -// Check if FMod can be used -// -//========================================================================== - -bool IsFModExPresent() -{ -#ifdef NO_FMOD - return false; -#elif !defined _MSC_VER - return true; // on non-MSVC we cannot delay load the library so it has to be present. -#else - static bool cached_result; - static bool done = false; - - if (!done) - { - done = true; - - FMOD::System *Sys; - FMOD_RESULT result; - __try - { - result = FMOD::System_Create(&Sys); - } - __except (CheckException(GetExceptionCode())) - { - // FMod could not be delay loaded - return false; - } - if (result == FMOD_OK) Sys->release(); - cached_result = true; - } - return cached_result; -#endif -} diff --git a/src/sound/fmodsound.h b/src/sound/fmodsound.h deleted file mode 100644 index d80423ebd..000000000 --- a/src/sound/fmodsound.h +++ /dev/null @@ -1,141 +0,0 @@ -#ifndef FMODSOUND_H -#define FMODSOUND_H - -#include "i_sound.h" - -#ifndef NO_FMOD -#include "fmod_wrap.h" - -class FMODSoundRenderer : public SoundRenderer -{ -public: - FMODSoundRenderer (); - ~FMODSoundRenderer (); - bool IsValid (); - - void SetSfxVolume (float volume); - void SetMusicVolume (float volume); - std::pair LoadSound(uint8_t *sfxdata, int length, bool monoize); - std::pair LoadSoundRaw(uint8_t *sfxdata, int length, int frequency, int channels, int bits, int loopstart, int loopend = -1, bool monoize = false); - void UnloadSound (SoundHandle sfx); - unsigned int GetMSLength(SoundHandle sfx); - unsigned int GetSampleLength(SoundHandle sfx); - float GetOutputRate(); - - // Streaming sounds. - SoundStream *CreateStream (SoundStreamCallback callback, int buffsamples, int flags, int samplerate, void *userdata); - SoundStream *OpenStream (FileReader *reader, int flags); - SoundStream *OpenStream (const char *url, int flags); - - // Starts a sound. - FISoundChannel *StartSound (SoundHandle sfx, float vol, int pitch, int chanflags, FISoundChannel *reuse_chan); - FISoundChannel *StartSound3D (SoundHandle sfx, SoundListener *listener, float vol, FRolloffInfo *rolloff, float distscale, int pitch, int priority, const FVector3 &pos, const FVector3 &vel, int channum, int chanflags, FISoundChannel *reuse_chan); - - // Stops a sound channel. - void StopChannel (FISoundChannel *chan); - - // Changes a channel's volume. - void ChannelVolume (FISoundChannel *chan, float volume); - - // Marks a channel's start time without actually playing it. - void MarkStartTime (FISoundChannel *chan); - - // Returns position of sound on this channel, in samples. - unsigned int GetPosition(FISoundChannel *chan); - - // Gets a channel's audibility (real volume). - float GetAudibility(FISoundChannel *chan); - - // Synchronizes following sound startups. - void Sync (bool sync); - - // Pauses or resumes all sound effect channels. - void SetSfxPaused (bool paused, int slot); - - // Pauses or resumes *every* channel, including environmental reverb. - void SetInactive (EInactiveState inactive); - - // Updates the position of a sound channel. - void UpdateSoundParams3D (SoundListener *listener, FISoundChannel *chan, bool areasound, const FVector3 &pos, const FVector3 &vel); - - void UpdateListener (SoundListener *listener); - void UpdateSounds (); - - void PrintStatus (); - void PrintDriversList (); - FString GatherStats (); - short *DecodeSample(int outlen, const void *coded, int sizebytes, ECodecType type); - - void DrawWaveDebug(int mode); - - virtual MIDIDevice* CreateMIDIDevice() const override; - -private: - uint32_t ActiveFMODVersion; - int SFXPaused; - bool InitSuccess; - bool DSPLocked; - QWORD_UNION DSPClock; - int OutputRate; - -#if FMOD_STUDIO - static FMOD_RESULT F_CALLBACK ChannelCallback(FMOD_CHANNELCONTROL *channel, FMOD_CHANNELCONTROL_TYPE controltype, FMOD_CHANNELCONTROL_CALLBACK_TYPE type, void *data1, void *data2); - static float F_CALLBACK RolloffCallback(FMOD_CHANNELCONTROL *channel, float distance); -#else - static FMOD_RESULT F_CALLBACK ChannelCallback(FMOD_CHANNEL *channel, FMOD_CHANNEL_CALLBACKTYPE type, void *data1, void *data2); - static float F_CALLBACK RolloffCallback(FMOD_CHANNEL *channel, float distance); -#endif - - bool HandleChannelDelay(FMOD::Channel *chan, FISoundChannel *reuse_chan, int flags, float freq) const; - FISoundChannel *CommonChannelSetup(FMOD::Channel *chan, FISoundChannel *reuse_chan) const; - FMOD_MODE SetChanHeadSettings(SoundListener *listener, FMOD::Channel *chan, const FVector3 &pos, bool areasound, FMOD_MODE oldmode) const; - - bool ReconnectSFXReverbUnit(); - void InitCreateSoundExInfo(FMOD_CREATESOUNDEXINFO *exinfo) const; - FMOD_RESULT SetSystemReverbProperties(const REVERB_PROPERTIES *props); - - bool Init (); - void Shutdown (); -#if !FMOD_STUDIO - void DumpDriverCaps(FMOD_CAPS caps, int minfrequency, int maxfrequency); -#endif - - int DrawChannelGroupOutput(FMOD::ChannelGroup *group, float *wavearray, int width, int height, int y, int mode); - int DrawSystemOutput(float *wavearray, int width, int height, int y, int mode); - - int DrawChannelGroupWaveData(FMOD::ChannelGroup *group, float *wavearray, int width, int height, int y, bool skip); - int DrawSystemWaveData(float *wavearray, int width, int height, int y, bool skip); - void DrawWave(float *wavearray, int x, int y, int width, int height); - - int DrawChannelGroupSpectrum(FMOD::ChannelGroup *group, float *wavearray, int width, int height, int y, bool skip); - int DrawSystemSpectrum(float *wavearray, int width, int height, int y, bool skip); - void DrawSpectrum(float *spectrumarray, int x, int y, int width, int height); - - typedef char spk[4]; - static const spk SpeakerNames4[4], SpeakerNamesMore[8]; - void DrawSpeakerLabels(const spk *labels, int y, int width, int count); - - FMOD::System *Sys; - FMOD::ChannelGroup *SfxGroup, *PausableSfx; - FMOD::ChannelGroup *MusicGroup; - FMOD::DSP *WaterLP, *WaterReverb; - FMOD::DSPConnection *SfxConnection; - FMOD::DSPConnection *ChannelGroupTargetUnitOutput; - FMOD::DSP *ChannelGroupTargetUnit; - FMOD::DSP *SfxReverbPlaceholder; - bool SfxReverbHooked; - float LastWaterLP; - unsigned int OutputPlugin; - - // Just for snd_status display - int Driver_MinFrequency; - int Driver_MaxFrequency; -#if !FMOD_STUDIO - FMOD_CAPS Driver_Caps; -#endif - - friend class FMODStreamCapsule; -}; - -#endif -#endif diff --git a/src/sound/i_music.cpp b/src/sound/i_music.cpp index af848cbeb..a31c4de74 100644 --- a/src/sound/i_music.cpp +++ b/src/sound/i_music.cpp @@ -184,9 +184,6 @@ void I_ShutdownMusic(bool onexit) } Timidity::FreeAll(); if (onexit) WildMidi_Shutdown(); -#ifdef _WIN32 - I_ShutdownMusicWin32(); -#endif // _WIN32 } void I_ShutdownMusicExit() diff --git a/src/sound/i_musicinterns.h b/src/sound/i_musicinterns.h index 227464c11..6cb2e05a2 100644 --- a/src/sound/i_musicinterns.h +++ b/src/sound/i_musicinterns.h @@ -10,7 +10,6 @@ #include "wildmidi/wildmidi_lib.h" void I_InitMusicWin32 (); -void I_ShutdownMusicWin32 (); extern float relative_volume; diff --git a/src/sound/i_sound.cpp b/src/sound/i_sound.cpp index 42e5a8d7d..74445a98c 100644 --- a/src/sound/i_sound.cpp +++ b/src/sound/i_sound.cpp @@ -39,7 +39,6 @@ #include "doomtype.h" #include -#include "fmodsound.h" #include "oalsound.h" #include "mpg123_decoder.h" @@ -68,9 +67,7 @@ CVAR (Int, snd_samplerate, 0, CVAR_ARCHIVE|CVAR_GLOBALCONFIG) CVAR (Int, snd_buffersize, 0, CVAR_ARCHIVE|CVAR_GLOBALCONFIG) CVAR (String, snd_output, "default", CVAR_ARCHIVE|CVAR_GLOBALCONFIG) -#ifndef NO_FMOD -#define DEF_BACKEND "fmod" -#elif !defined(NO_OPENAL) +#if !defined(NO_OPENAL) #define DEF_BACKEND "openal" #else #define DEF_BACKEND "null" @@ -259,29 +256,10 @@ void I_InitSound () return; } - // This has been extended to allow falling back from FMod to OpenAL and vice versa if the currently active sound system cannot be found. if (stricmp(snd_backend, "null") == 0) { GSnd = new NullSoundRenderer; } - else if(stricmp(snd_backend, "fmod") == 0) - { - #ifndef NO_FMOD - if (IsFModExPresent()) - { - GSnd = new FMODSoundRenderer; - } - #endif - #ifndef NO_OPENAL - if ((!GSnd || !GSnd->IsValid()) && IsOpenALPresent()) - { - Printf (TEXTCOLOR_RED"FMod Ex Sound init failed. Trying OpenAL.\n"); - I_CloseSound(); - GSnd = new OpenALSoundRenderer; - snd_backend = "openal"; - } - #endif - } else if(stricmp(snd_backend, "openal") == 0) { #ifndef NO_OPENAL @@ -290,15 +268,6 @@ void I_InitSound () GSnd = new OpenALSoundRenderer; } #endif - #ifndef NO_FMOD - if ((!GSnd || !GSnd->IsValid()) && IsFModExPresent()) - { - Printf (TEXTCOLOR_RED"OpenAL Sound init failed. Trying FMod Ex.\n"); - I_CloseSound(); - GSnd = new FMODSoundRenderer; - snd_backend = "fmod"; - } - #endif } else { diff --git a/src/sound/i_sound.h b/src/sound/i_sound.h index 5f1917d68..97d81d9dc 100644 --- a/src/sound/i_sound.h +++ b/src/sound/i_sound.h @@ -177,7 +177,6 @@ FISoundChannel *S_GetChannel(void *syschan); extern ReverbContainer *DefaultEnvironments[26]; -bool IsFModExPresent(); bool IsOpenALPresent(); #endif diff --git a/src/sound/music_midi_base.cpp b/src/sound/music_midi_base.cpp index 4e3708eb4..42e8ab863 100644 --- a/src/sound/music_midi_base.cpp +++ b/src/sound/music_midi_base.cpp @@ -96,23 +96,6 @@ void I_InitMusicWin32 () snd_mididevice.Callback (); } -void I_ShutdownMusicWin32 () -{ - // Ancient bug a saw on NT 4.0 and an old version of FMOD 3: If waveout - // is used for sound and a MIDI is also played, then when I quit, the OS - // tells me a free block was modified after being freed. This is - // apparently a synchronization issue between two threads, because if I - // put this Sleep here after stopping the music but before shutting down - // the entire sound system, the error does not happen. Observed with a - // Vortex 2 (may Aureal rest in peace) and an Audigy (damn you, Creative!). - // I no longer have a system with NT4 drivers, so I don't know if this - // workaround is still needed or not. - if (OSPlatform == os_WinNT4) - { - Sleep(50); - } -} - void I_BuildMIDIMenuList (FOptionValues *opt) { AddDefaultMidiDevices(opt); diff --git a/src/sound/oalsound.cpp b/src/sound/oalsound.cpp index d38435be1..f173903ec 100644 --- a/src/sound/oalsound.cpp +++ b/src/sound/oalsound.cpp @@ -630,7 +630,7 @@ public: }; -extern ReverbContainer *ForcedEnvironment; +ReverbContainer *ForcedEnvironment; #define AREA_SOUND_RADIUS (32.f) diff --git a/wadsrc/static/language.enu b/wadsrc/static/language.enu index 550a94634..8e8572b2f 100644 --- a/wadsrc/static/language.enu +++ b/wadsrc/static/language.enu @@ -2128,23 +2128,11 @@ SNDMNU_UNDERWATERREVERB = "Underwater reverb"; SNDMNU_RANDOMIZEPITCHES = "Randomize pitches"; SNDMNU_CHANNELS = "Sound channels"; SNDMNU_BACKEND = "Sound backend"; -SNDMNU_FMOD = "FMOD options"; SNDMNU_OPENAL = "OpenAL options"; SNDMNU_RESTART = "Restart sound"; SNDMNU_ADVANCED = "Advanced options"; SNDMNU_MODREPLAYER = "Module replayer options"; -// Fmod Options -FMODMNU_TITLE = "FMOD OPTIONS"; -FMODMNU_WATERCUTOFF = "Underwater cutoff"; -FMODMNU_OUTPUTSYSTEM = "Output system"; -FMODMNU_OUTPUTFORMAT = "Output format"; -FMODMNU_SPEAKERMODE = "Speaker mode"; -FMODMNU_RESAMPLER = "Resampler"; -FMODMNU_HRTFFILTER = "HRTF filter"; -FMODMNU_BUFFERSIZE = "Buffer size"; -FMODMNU_BUFFERCOUNT = "Buffer count"; - // OpenAL Options OPENALMNU_TITLE = "OPENAL OPTIONS"; OPENALMNU_PLAYBACKDEVICE = "Playback device"; @@ -2428,7 +2416,6 @@ OPTSTR_5POINT1 = "5.1 speakers"; OPTSTR_7POINT1 = "7.1 speakers"; OPTSTR_NOINTERPOLATION = "No interpolation"; OPTSTR_SPLINE = "Spline"; -OPTSTR_FMOD = "FMOD Ex"; OPTSTR_OPENAL = "OpenAL"; diff --git a/wadsrc/static/menudef.txt b/wadsrc/static/menudef.txt index 3c9c88759..c5e3820af 100644 --- a/wadsrc/static/menudef.txt +++ b/wadsrc/static/menudef.txt @@ -1557,51 +1557,12 @@ OptionString Resamplers } -OptionString SoundBackends -{ - "fmod", "$OPTSTR_FMOD" - "openal", "$OPTSTR_OPENAL" - "null", "$OPTSTR_NOSOUND" -} - -OptionString SoundBackendsFModOnly -{ - "fmod", "$OPTSTR_FMOD" - "null", "$OPTSTR_NOSOUND" -} - OptionString SoundBackendsOpenALOnly { "openal", "$OPTSTR_OPENAL" "null", "$OPTSTR_NOSOUND" } -OptionMenu FMODSoundItems -{ - Title "$FMODMNU_TITLE" - Slider "$FMODMNU_WATERCUTOFF", "snd_waterlp", 0.0, 2000.0, 50.0, 0 - IfOption(Windows) - { - Option "$FMODMNU_OUTPUTSYSTEM", "snd_output", "SoundOutputsWindows" - } - IfOption(Unix) - { - Option "$FMODMNU_OUTPUTSYSTEM", "snd_output", "SoundOutputsUnix" - } - IfOption(Mac) - { - Option "$FMODMNU_OUTPUTSYSTEM", "snd_output", "SoundOutputsMac" - } - Option "$FMODMNU_OUTPUTFORMAT", "snd_output_format", "OutputFormats" - Option "$FMODMNU_SPEAKERMODE", "snd_speakermode", "SpeakerModes" - Option "$FMODMNU_RESAMPLER", "snd_resampler", "Resamplers" - Option "$FMODMNU_HRTFFILTER", "snd_hrtf", "OnOff" - StaticText " " - Option "$FMODMNU_BUFFERSIZE", "snd_buffersize", "BufferSizes" - Option "$FMODMNU_BUFFERCOUNT", "snd_buffercount", "BufferCounts" -} - - OptionMenu OpenALSoundItems { Title "$OPENALMNU_TITLE" @@ -1628,31 +1589,9 @@ OptionMenu SoundOptions Slider "$SNDMNU_CHANNELS", "snd_channels", 64, 256, 8, 0 StaticText " " - ifoption(fmodex) - { - ifoption(openal) - { - Option "$SNDMNU_BACKEND", "snd_backend", "SoundBackends" - } - else - { - Option "$SNDMNU_BACKEND", "snd_backend", "SoundBackendsFModOnly" - } - } - else - { - ifoption(openal) - { - Option "$SNDMNU_BACKEND", "snd_backend", "SoundBackendsOpenALOnly" - } - } - - ifoption(fmodex) - { - Submenu "$SNDMNU_FMOD", "FMODSoundItems" - } ifoption(openal) { + Option "$SNDMNU_BACKEND", "snd_backend", "SoundBackendsOpenALOnly" Submenu "$SNDMNU_OPENAL", "OpenALSoundItems" } StaticText " " From 4519ab12e970829373316437be5fcdb171092b24 Mon Sep 17 00:00:00 2001 From: "alexey.lysiuk" Date: Mon, 17 Apr 2017 10:27:11 +0300 Subject: [PATCH 13/29] Fixed compilation errors src/sound/oplsynth/musicblock.cpp:3:10: fatal error: 'muslib.h' file not found src/sound/oplsynth/oplio.cpp:410:12: error: use of undeclared identifier 'cos' src/sound/oplsynth/oplio.cpp:410:41: error: use of undeclared identifier 'sin' --- src/sound/oplsynth/musicblock.cpp | 2 +- src/sound/oplsynth/oplio.cpp | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/sound/oplsynth/musicblock.cpp b/src/sound/oplsynth/musicblock.cpp index 9a2c0d93f..9e26b4732 100644 --- a/src/sound/oplsynth/musicblock.cpp +++ b/src/sound/oplsynth/musicblock.cpp @@ -1,6 +1,6 @@ #include #include -#include "muslib.h" +#include "musicblock.h" #include "files.h" #include "templates.h" diff --git a/src/sound/oplsynth/oplio.cpp b/src/sound/oplsynth/oplio.cpp index 1229a5bbf..140521231 100644 --- a/src/sound/oplsynth/oplio.cpp +++ b/src/sound/oplsynth/oplio.cpp @@ -31,6 +31,7 @@ **--------------------------------------------------------------------------- */ +#include #include "genmidi.h" #include "oplio.h" #include "opl.h" From cdade41ca908acb8cde2abc702879818b2866ccf Mon Sep 17 00:00:00 2001 From: "alexey.lysiuk" Date: Mon, 17 Apr 2017 10:38:21 +0300 Subject: [PATCH 14/29] Removed obsolete file --- specs/fmod_version.txt | 3 --- 1 file changed, 3 deletions(-) delete mode 100644 specs/fmod_version.txt diff --git a/specs/fmod_version.txt b/specs/fmod_version.txt deleted file mode 100644 index e0e03262d..000000000 --- a/specs/fmod_version.txt +++ /dev/null @@ -1,3 +0,0 @@ -This version of ZDoom must be compiled with any version between 4.22 and 4.28 inclusive or 4.34. -Use of the latest 4.26 is recommended though due to technical issues with 4.28. - From 59626cf843dffa7876ff33e6f723106fd53cc67f Mon Sep 17 00:00:00 2001 From: "alexey.lysiuk" Date: Mon, 17 Apr 2017 10:46:38 +0300 Subject: [PATCH 15/29] Simplified transition to OpenAL sound backend --- src/sound/i_sound.cpp | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/sound/i_sound.cpp b/src/sound/i_sound.cpp index 74445a98c..b51cc298b 100644 --- a/src/sound/i_sound.cpp +++ b/src/sound/i_sound.cpp @@ -256,6 +256,15 @@ void I_InitSound () return; } +#ifndef NO_OPENAL + // Simplify transition to OpenAL backend + if (stricmp(snd_backend, "fmod") == 0) + { + Printf (TEXTCOLOR_ORANGE "FMOD Ex sound system was removed, switching to OpenAL\n"); + snd_backend = "openal"; + } +#endif // NO_OPENAL + if (stricmp(snd_backend, "null") == 0) { GSnd = new NullSoundRenderer; From 33ee761b3749716d8491b8dddf1a599c1380c149 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Mon, 17 Apr 2017 10:10:09 +0200 Subject: [PATCH 16/29] - fixed: The timing of Heretic's lava damage was not correct. --- src/p_spec.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/p_spec.cpp b/src/p_spec.cpp index 7c128eb1d..0ce113a25 100644 --- a/src/p_spec.cpp +++ b/src/p_spec.cpp @@ -1155,15 +1155,15 @@ void P_InitSectorSpecial(sector_t *sector, int special) break; case dDamage_LavaWimpy: - P_SetupSectorDamage(sector, 5, 32, 256, NAME_Fire, SECF_DMGTERRAINFX); + P_SetupSectorDamage(sector, 5, 16, 256, NAME_Fire, SECF_DMGTERRAINFX); break; case dDamage_LavaHefty: - P_SetupSectorDamage(sector, 8, 32, 256, NAME_Fire, SECF_DMGTERRAINFX); + P_SetupSectorDamage(sector, 8, 16, 256, NAME_Fire, SECF_DMGTERRAINFX); break; case dScroll_EastLavaDamage: - P_SetupSectorDamage(sector, 5, 32, 256, NAME_Fire, SECF_DMGTERRAINFX); + P_SetupSectorDamage(sector, 5, 16, 256, NAME_Fire, SECF_DMGTERRAINFX); P_CreateScroller(EScroll::sc_floor, -4., 0, -1, sector->Index(), 0); keepspecial = true; break; From f0d741241dfb95fd4139b5fd33569025dd24edd1 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Mon, 17 Apr 2017 10:24:24 +0200 Subject: [PATCH 17/29] - cleaned out most of the cruft from the docs directory and added a copy of the GPL v3. --- CMakeLists.txt | 4 - docs/BUILDLIC.TXT | 71 -- docs/README.asm | 283 ------ docs/README.gl | 149 --- docs/commands.txt | 1742 ------------------------------------ docs/doomlic.txt | 112 --- docs/history.txt | 534 ----------- docs/licenses/README.TXT | 13 +- docs/licenses/buildlic.txt | 71 -- docs/licenses/bzip2.txt | 42 + docs/licenses/doomlic.txt | 112 --- docs/licenses/gpl.txt | 674 ++++++++++++++ docs/licenses/xBRZ.jpg | Bin 60099 -> 0 bytes docs/zdoom.txt | 1134 ----------------------- output_sdl/CMakeLists.txt | 17 - output_sdl/output_sdl.c | 215 ----- 16 files changed, 723 insertions(+), 4450 deletions(-) delete mode 100644 docs/BUILDLIC.TXT delete mode 100644 docs/README.asm delete mode 100644 docs/README.gl delete mode 100644 docs/commands.txt delete mode 100644 docs/doomlic.txt delete mode 100644 docs/history.txt delete mode 100644 docs/licenses/buildlic.txt create mode 100644 docs/licenses/bzip2.txt delete mode 100644 docs/licenses/doomlic.txt create mode 100644 docs/licenses/gpl.txt delete mode 100644 docs/licenses/xBRZ.jpg delete mode 100644 docs/zdoom.txt delete mode 100644 output_sdl/CMakeLists.txt delete mode 100644 output_sdl/output_sdl.c diff --git a/CMakeLists.txt b/CMakeLists.txt index f426119c2..4de1d047f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -327,10 +327,6 @@ add_subdirectory( wadsrc_bm ) add_subdirectory( wadsrc_lights ) add_subdirectory( src ) -if( NOT WIN32 AND NOT APPLE ) - add_subdirectory( output_sdl ) -endif() - if( NOT CMAKE_CROSSCOMPILING ) export(TARGETS ${CROSS_EXPORTS} FILE "${CMAKE_BINARY_DIR}/ImportExecutables.cmake" ) endif() diff --git a/docs/BUILDLIC.TXT b/docs/BUILDLIC.TXT deleted file mode 100644 index a0cec1251..000000000 --- a/docs/BUILDLIC.TXT +++ /dev/null @@ -1,71 +0,0 @@ -BUILD SOURCE CODE LICENSE TERMS: 06/20/2000 - -[1] I give you permission to make modifications to my Build source and - distribute it, BUT: - -[2] Any derivative works based on my Build source may be distributed ONLY - through the INTERNET. - -[3] Distribution of any derivative works MUST be done completely FREE of - charge - no commercial exploitation whatsoever. - -[4] Anything you distribute which uses a part of my Build Engine source - code MUST include: - - [A] The following message somewhere in the archive: - - // "Build Engine & Tools" Copyright (c) 1993-1997 Ken Silverman - // Ken Silverman's official web site: "http://www.advsys.net/ken" - // See the included license file "BUILDLIC.TXT" for license info. - - [B] This text file "BUILDLIC.TXT" along with it. - - [C] Any source files that you modify must include this message as well: - - // This file has been modified from Ken Silverman's original release - -[5] The use of the Build Engine for commercial purposes will require an - appropriate license arrangement with me. Contact information is - on my web site. - -[6] I take no responsibility for damage to your system. - -[7] Technical support: Before contacting me with questions, please read - and do ALL of the following! - - [A] Look though ALL of my text files. There are 7 of them (including this - one). I like to think that I wrote them for a reason. You will find - many of your answers in the history section of BUILD.TXT and - BUILD2.TXT (they're located inside SRC.ZIP). - - [B] If that doesn't satisfy you, then try going to: - - "http://www.advsys.net/ken/buildsrc" - - where I will maintain a Build Source Code FAQ (or perhaps I might - just provide a link to a good FAQ). - - [C] I am willing to respond to questions, but ONLY if they come at a rate - that I can handle. - - PLEASE TRY TO AVOID ASKING DUPLICATE QUESTIONS! - - As my line of defense, I will post my current policy about - answering Build source questions (right below the E-mail address - on my web site.) You can check there to see if I'm getting - overloaded with questions or not. - - If I'm too busy, it might say something like this: - - I'm too busy to answer Build source questions right now. - Sorry, but don't expect a reply from me any time soon. - - If I'm open for Build source questions, please state your question - clearly and don't include any unsolicited attachments unless - they're really small (like less than 50k). Assume that I have - a 28.8k modem. Also, don't leave out important details just - to make your question appear shorter - making me guess what - you're asking doesn't save me time! - ----------------------------------------------------------------------------- --Ken S. (official web site: http://www.advsys.net/ken) diff --git a/docs/README.asm b/docs/README.asm deleted file mode 100644 index 89629769a..000000000 --- a/docs/README.asm +++ /dev/null @@ -1,283 +0,0 @@ - -README - DOOM assembly code - -Okay, I add the DOS assembly module for the historically -inclined here (may rec.games.programmer suffer). If anyone -feels the urge to port these to GNU GCC; either inline or -as separate modules including Makefile support, be my guest. - -Module tmap.S includes the inner loops for texture mapping, -the interesting one being the floor/ceiling span rendering. - -There was another module in the source dump, fpfunc.S, that -had both texture mapping and fixed point functions. It -contained implementations both for i386 and M68k. For -brevity, I include only the i386 fixed point stuff below. - -//==================================================== -// tmap.S as of January 10th, 1997 - -//================ -// -// R_DrawColumn -// -//================ - - .data -loopcount .long 0 -pixelcount .long 0 - - .text - - .align 16 -.globl _R_DrawColumn -_R_DrawColumn: - - pushad - - movl ebp,[_dc_yl] - movl ebx,ebp - movl edi,[_ylookup+ebx*4] - movl ebx,[_dc_x] - addl edi,[_columnofs + ebx*4] - - movl eax,[_dc_yh] - incl eax - subl eax,ebp // pixel count - movl [pixelcount],eax // save for final pixel - js done // nothing to scale - shrl eax,1 // double pixel count - movl [loopcount],eax - - movl ecx,[_dc_iscale] - - movl eax,[_centery] - subl eax,ebp - imull ecx - movl ebp,[_dc_texturemid] - subl ebp,eax - shll ebp,9 // 7 significant bits, 25 frac - - movl esi,[_dc_source] - - - movl ebx,[_dc_iscale] - shll ebx,9 - movl eax,OFFSET patch1+2 // convice tasm to modify code... - movl [eax],ebx - movl eax,OFFSET patch2+2 // convice tasm to modify code... - movl [eax],ebx - -// eax aligned colormap -// ebx aligned colormap -// ecx,edx scratch -// esi virtual source -// edi moving destination pointer -// ebp frac - - movl ecx,ebp // begin calculating first pixel - addl ebp,ebx // advance frac pointer - shrl ecx,25 // finish calculation for first pixel - movl edx,ebp // begin calculating second pixel - addl ebp,ebx // advance frac pointer - shrl edx,25 // finish calculation for second pixel - movl eax,[_dc_colormap] - movl ebx,eax - movb al,[esi+ecx] // get first pixel - movb bl,[esi+edx] // get second pixel - movb al,[eax] // color translate first pixel - movb bl,[ebx] // color translate second pixel - - testl [pixelcount],0fffffffeh - jnz doubleloop // at least two pixels to map - jmp checklast - - .align 16 -doubleloop: - movl ecx,ebp // begin calculating third pixel -patch1: - addl ebp,12345678h // advance frac pointer - movb [edi],al // write first pixel - shrl ecx,25 // finish calculation for third pixel - movl edx,ebp // begin calculating fourth pixel -patch2: - addl ebp,12345678h // advance frac pointer - movl [edi+SCREENWIDTH],bl // write second pixel - shrl edx,25 // finish calculation for fourth pixel - movb al,[esi+ecx] // get third pixel - addl edi,SCREENWIDTH*2 // advance to third pixel destination - movb bl,[esi+edx] // get fourth pixel - decl [loopcount] // done with loop? - movb al,[eax] // color translate third pixel - movb bl,[ebx] // color translate fourth pixel - jnz doubleloop - -// check for final pixel -checklast: - testl [pixelcount],1 - jz done - movb [edi],al // write final pixel - -done: - popad - ret - - - -//================ -// -// R_DrawSpan -// -// Horizontal texture mapping -// -//================ - - - .align 16 -.globl _R_DrawSpan -_R_DrawSpan: - pushad - -// -// find loop count -// - movl eax,[_ds_x2] - incl eax - subl eax,[_ds_x1] // pixel count - movl [pixelcount],eax // save for final pixel - js hdone // nothing to scale - shrl eax,1 // double pixel count - movl [loopcount],eax - -// -// build composite position -// - movl ebp,[_ds_xfrac] - shll ebp,10 - andl ebp,0ffff0000h - movl eax,[_ds_yfrac] - shrl eax,6 - andl eax,0ffffh - orl ebp,eax - - movl esi,[_ds_source] - -// -// calculate screen dest -// - movl edi,[_ds_y] - movl edi,[_ylookup+edi*4] - movl eax,[_ds_x1] - addl edi,[_columnofs+eax*4] - -// -// build composite step -// - movl ebx,[_ds_xstep] - shll ebx,10 - andl ebx,0ffff0000h - movl eax,[_ds_ystep] - shrl eax,6 - andl eax,0ffffh - orl ebx,eax - - movl eax,OFFSET hpatch1+2 // convice tasm to modify code... - movl [eax],ebx - movl eax,OFFSET hpatch2+2 // convice tasm to modify code... - movl [eax],ebx - -// eax aligned colormap -// ebx aligned colormap -// ecx,edx scratch -// esi virtual source -// edi moving destination pointer -// ebp frac - - shldl ecx,ebp,22 // begin calculating third pixel (y units) - shldl ecx,ebp,6 // begin calculating third pixel (x units) - addl ebp,ebx // advance frac pointer - andl ecx,4095 // finish calculation for third pixel - shldl edx,ebp,22 // begin calculating fourth pixel (y units) - shldl edx,ebp,6 // begin calculating fourth pixel (x units) - addl ebp,ebx // advance frac pointer - andl edx,4095 // finish calculation for fourth pixel - movl eax,[_ds_colormap] - movl ebx,eax - movb al,[esi+ecx] // get first pixel - movb bl,[esi+edx] // get second pixel - movb al,[eax] // color translate first pixel - movb bl,[ebx] // color translate second pixel - - testl [pixelcount],0fffffffeh - jnz hdoubleloop // at least two pixels to map - jmp hchecklast - - - .align 16 -hdoubleloop: - shldl ecx,ebp,22 // begin calculating third pixel (y units) - shldl ecx,ebp,6 // begin calculating third pixel (x units) -hpatch1: - addl ebp,12345678h // advance frac pointer - movb [edi],al // write first pixel - andl ecx,4095 // finish calculation for third pixel - shldl edx,ebp,22 // begin calculating fourth pixel (y units) - shldl edx,ebp,6 // begin calculating fourth pixel (x units) -hpatch2: - addl ebp,12345678h // advance frac pointer - movb [edi+1],bl // write second pixel - andl edx,4095 // finish calculation for fourth pixel - movb al,[esi+ecx] // get third pixel - addl edi,2 // advance to third pixel destination - movb bl,[esi+edx] // get fourth pixel - decl [loopcount] // done with loop? - movb al,[eax] // color translate third pixel - movb bl,[ebx] // color translate fourth pixel - jnz hdoubleloop - -// check for final pixel -hchecklast: - testl [pixelcount],1 - jz hdone - movb [edi],al // write final pixel - -hdone: - popad - ret - - - - -//==================================================== -// fpfunc.S as of January 10th, 1997 (parts) - -#ifdef i386 - -.text - .align 4 -.globl _FixedMul -_FixedMul: - pushl %ebp - movl %esp,%ebp - movl 8(%ebp),%eax - imull 12(%ebp) - shrdl $16,%edx,%eax - popl %ebp - ret - - - .align 4 -.globl _FixedDiv2 -_FixedDiv2: - pushl %ebp - movl %esp,%ebp - movl 8(%ebp),%eax - cdq - shldl $16,%eax,%edx - sall $16,%eax - idivl 12(%ebp) - popl %ebp - ret - -#endif - diff --git a/docs/README.gl b/docs/README.gl deleted file mode 100644 index df443a9fb..000000000 --- a/docs/README.gl +++ /dev/null @@ -1,149 +0,0 @@ - -README: glDOOM - -I never got around to do anything with respect to -a Linux glDOOM port except for assembling a Linux3Dfx -HOWTO (which, at that time, was a prerequisite -to get permission to publicly distribute the -already finished LinuxGlide port by Daryll Strauss). - -Linux q2test (and soon LinuxQuake2) demonstrate that -Mesa with the MesaVoodoo driver is quite up to the -requirements for a glDOOM port. If anybody wants to -get into Linux glDOOM, please drop me a line. - -There is a Win32 GLDOOM port in the works, by Jim Dose. -Quoting a recent posting by him: - -"I haven't had as much time lately to really work on -the conversion. I currently have the renderer drawing -the walls and floors as texture spans as the are in -the software renderer. There's lighting on the walls, -but not the floors, and sprites are being drawn, but -not with the right texture. I figure that this is one -nights work to get the game looking "normal". I haven't -tested the game on less than a p200, so I'm not sure -how it will perform under the average machine, but I -don't expect it to be blindingly fast because of the -number of spans that have to be drawn each frame. -Rendering as polys is definitely the way to go. - -The reason I chose to do spans first was because it -left the base renderer intact and I could concentrate -on ironing out any Windows compatibility problems. -Actually, the first version I had running was simply -a blit of the 320x200 game screen through Open GL. -Surprisingly, this actually was very playable, but -certainly wasn't taking any advantage of 3D acceleration. -Once the game was running, I started converting all -the span routines over." - -Comment: for merging Linuxdoom with Win32, this is -probably the best source for getting the Win32 -environment done - before more significant changes -occur. - -"One problem with drawing spans is that the engine -doesn't calculate the texture coordinates with -fractional accuracy, so the bilinear filtering works -vertically, but not horizontally on the walls. I may -try to fix this, but since I plan to use polys for -the final version, it's not really high priority. -Also, spans don't really allow for looking up and -down." - -Comment: true looking up/down vs. Heretic-style -y-shearing seems to require either a strange kind -of transofrmation matrix (he probably does not use -the OpenGL transformation at all), or rendering -all the spans as textured rectangular slices -instead of using glDrawBitmap. No, polys are the -way to go. - -"When I tackle the conversion to polys, one big problem -I'll encounter is drawing floors. Since the world is -stored in a 2D bsp tree, there is no information on -the shape of the floors. In fact the floors can be -concave and may include holes (typically, most renderers -break concave polys down into a collection of convex -polys or triangles). In software, the floors are actually -drawn using an algorithm that's similar to a flood fill -(except that a list of open spans is kept instead of a -buffer of pixels). This makes drawing the floors as -polys fairly difficult." - -A polygon based approach will require significant changes -to the data structures used in the refresh module. I -recommend either separating a libref_soft.so first (a -Quake2 like approach), and creating libref_gl afterwards, -or abandoning the software rendering entirely. - -John Carmack wrote once upon a time: -"... the U64 DOOM engine is much more what I would consider -The Right Thing now -- it turns the subsector boundaries -into polygons for the floors and ceilings ahead of time, -then for rendering it walks the BSP front to back, doing -visibility determination of subsectors by the one dimensional -occlusion buffer and clipping sprites into subsectors, then -it goes backwards through the visible subsectors, drawing -floors, ceilings, walls, then sorted internal sprite fragments. -It's a ton simpler and a ton faster, although it does suffer -some overdraw when a high subsector overlooks a low one (but -that is more than made up for by the simplicity of everything -else)." - -Well, IMO compiling a separate list of floor/ceiling polygons -after having read the WAD file, and thus introducing this as -a completely separate data structure to the current code base -might be the easiest thing to do. Jim Dose writes: - -"One method I may use to draw the floors as polys was suggested -by Billy Zelsnack of Rebel Boat Rocker when we were working -at 3D Realms together a few years ago. Basically, Billy was -designing an engine that dealt with the world in a 2D portal -format similar to the one that Build used, except that it had -true looking up and down (no shearing). Since floors were -basically implicit and could be concave, Billy drew them as -if the walls extended downwards to infinity, but fixed the -texture coordinates to appear that they were on the plane of -the floor. The effect was that you could look up and down and -there were no gaps or overdraw. It's a fairly clever method -and allows you to store the world in a simpler data format. -Had perspective texture mapping been fast enough back then, -both Build and Doom could have done this in software." - -Perhaps the above is sufficient to get you started. -Other Issues: - -1. Occlusion -DOOM uses a per-column lookup (top/bottom index) to do HLHSR. -This works fine with span based rendering (well, getting -horizontal spans of floors/ceilings into the picture is a -separate story). It isn't really mindboggling with polygon -based rendering. GLDOOM should abandon that. - -2. Precalculated Visibility -DOOM has the data used by Quake's PVS - in REJECT. -During Quake development, lots of replacements for the -occlusion buffer were tried, and PVS turned out to be best. -I suggest usind the REJECT as PVS. - -There have been special effects using a utility named RMB. -REJECT is a lump meant for enemy AI LoS calculation - a -nonstandard REJECT will not work as a PVS, and introduce -rendering errors. I suggest looking for a PVS lump in the -WAD, and using REJECT if none is found. That way, it might -be feasible to eat the cake and keep it. - -3. Mipmaps -DOOM does not have mipmapping. As we have 8bit palettized -textures, OpenGL mipmapping might not give the desired -results. Plus, composing textures from patches at runtime -would require runtime mipmapping. Precalculated mipmaps -in the WAD? - -4. Sprites -Partly transparent textures and sprites impose another -problem related to mipmapping. Without alpha channel, -this could give strange results. Precalculated, valid -sprite mipmaps (w/o alpha)? diff --git a/docs/commands.txt b/docs/commands.txt deleted file mode 100644 index 4dfd483f3..000000000 --- a/docs/commands.txt +++ /dev/null @@ -1,1742 +0,0 @@ -This document lists all commands and cvars supported by the current version -of ZDoom (1.22) and a short description of each. There are a total of 130 -commands and 141 cvars. -=========================================================================== - -There are five types of cvars: - -boolean: This is a number that can be either "0" or "1". "0" indicates no/ - false, and "1" indicates yes/true. - - color: This is a series of three hexadecimal numbers representing the - amounts of red, green, and blue (in that order) in a color. For - example, pure redwould be represented as "ffff 0000 0000". The - setcolor command can be used to set one of these cvars using a - color name instead of numbers. (See the description of the - setcolor command below.) - - number: This is an ordinary number. - - integer: This is an ordinary that doesn't take fractional values. - - string: This is a series of text characters enclosed in quotes. - - -Some commands also take parameters. Any parameters that are required are -enclosed in < >, and those that are optional are enclosed in [ ]. - - -ACTION COMMANDS -=============== -NOTE: As in Quake, all action commands come in pairs. When prefixed by a -'+', they activate the corresponding action, and when prefixed by a '-', -they deactivate that action. - - -+attack, -attack - While active, causes the player to fire his active weapon. - -+back, -back - While active, causes the player to move backward. -See also: +forward - -+forward, -forward - While active, causes the player to move forward. -See also: +back - -+jump, -jump - Causes the player to jump. When underwater, the player will swim upward - instead. -See also: +moveup - -+klook, -klook - While active, causes +forward and +back to act like +lookup and +lookdown - instead. -See also: +mlook, +lookup, +lookdown, +forward, +back - -+left, -left - While active, normally causes the player to turn to the left. However, as - long as +strafe is active, this will cause the player to move to the left - instead. -See also: +right, +strafe, +moveleft - -+lookdown, -lookdown - While active, causes the player to look down. -See also: +lookup, +klook - -+lookup, -lookup - While active, causes the player to look up. -See also: +lookdown, +klook - -+mlook, -mlook - While active, causes movement along the mouse's vertical axis to tilt the - player's view up or down instead of moving the player forward or backward. -See also: +klook, freelook, invertmouse, lookspring - -+movedown, -movedown - Moves the player down if swimming or flying. -See also: +moveup - -+moveleft, -moveleft - While active, causes the player to move to the left. -See also: +moveright, +left, +strafe - -+moveright, -moveright - While active, causes the player to move to the right. -See also: +moveleft, +right, +strafe - -+moveup, -moveup - Moves the player up if swimming or flying. -See also: +movedown - -+right, -right - While active, normally causes the player to turn to the right. However, as - long as +strafe is active, this will cause the player to move to the right - instead. -See alse: +left, +strafe, +moveright - -+showscores, -showscores - While this action is active and you are playing a deathmatch game, a list - of the frags made by all players will be displayed on the screen. In - deathmatch games, this list will also automatically be display when you - are dead. - -+speed, -speed - While active, all player movements occur at a rate faster than normal. -See also: cl_run - -+strafe, -strafe - While active, causes all +left and +right commands to act like +moveleft - and +moveright instead. -See also: +left, +right, +moveleft, +moveright - -+use, -use - While active, causes the player to attempt to use any usable items in - front of him/her (such as a door). - - -OTHER COMMANDS AND CVARS -======================== -addbot [name] -(command) - Spawns a bot. If a name is given, the corresponding bot in bots.cfg will be - spawned. Otherwise, a bot will be picked at random from bots.cfg. -See also: listbots, removebots - -alias -(command) - If specified with no parameters, will display a list of all current - aliases. If only is specified, it will be removed from the - list of aliases. If is also specified, it will be added - to the list of aliases as . For example, to create a new - command to kill the monsters on the level, you can use the command: - - alias massacre kill monsters - - Then, you can use the newly created massacre command to kill all the - monsters on the level. - -alwaysapplydmflags -(cvar: boolean) -default: 0 - Normally, some dmflags are only used in deathmatch. If alwaysapplydmflags - is 1, then they will also be used in single-player and co-op games. -See also: dmflags - -am_backcolor -(cvar: color) -default: "6c 54 40" (a light tan) - The color of the automap background. Changes to this cvar take effect the - next time the automap is activated. -See also: all the am_* cvars - -am_cdwallcolor -(cvar: color) -default: "4c 38 20" (a dark tan) - The color of two-sided lines that have a different ceiling height on each - side. Changes to this cvar take effect the next time the automap is - activated. -See also: all the am_* cvars - -am_fdwallcolor -(cvar: color) -default: "88 70 58" (a lighter tan) - The color of two-sided lines that have a different floor height on each - side. Changes to this cvar take effect the next time the automap is - activated. -See also: all the am_* cvars - -am_gridcolor -(cvar: color) -default: "8b 5a 2b" (tan4) - The color of the automap grid. Changes to this cvar take effect the next - time the automap is activated. -See also: all the am_* cvars - -am_interlevelcolor -(cvar: color) -default: "ff 00 00" (red) - The color of inter-level teleporters. These are teleporters that teleport - you to a different map. Changes to this cvar take effect the next time the - automap is activated. -See also: all the am_* cvars - -am_intralevelcolor -(cvar: color) -default: "00 00 ff" (blue) - The color of intra-level teleporters. These are teleporters that teleport - you to a different location on the same map. Changes to this cvar take - effect the next time the automap is activated. -See also: all the am_* cvars - -am_lockedcolor -(cvar: color) -default: "00 00 98" (a blue) - The color of lines that open locked doors. Changes to this cvar take - effetc the next time the automap is activated. -See also: all the am_* cvars - -am_notseencolor -(cvar: color) -default: "6c 6c 6c" (somewhat dark gray) - The color of lines on the automap that haven't yet been seen. Visible with - a computer area map. Changes to this cvar take effect the next time the - automap is activated. -See also: all the am_* cvars - -am_overlay -(cvar: boolean) -default: 0 - Normally, the togglemap command switches the automap between fully off and - fully on. Setting this cvar to "1" will cause togglemap to draw the - automap on top of the player's view before it draws the automap - fullscreen. (Bad description, I know. Just try it) -See also: am_rotate, togglemap - -am_ovotherwallscolor -(cvar: color) -default: "00 88 44" (a dark blueish-green) - The color of passable lines on the automap when the map is overlayed. - Changes to this cvar take effect the next time the automap is activated. -See also: all the am_* cvars - -ov_telecolor -(cvar: color) -default: "ff ff 00" (a bright green) - The color of teleports on the overlayed automap. Changes to this cvar take - effect the next time the automap is activated. -See also: all the am_* cvars - -am_ovthingcolor -(cvar: color) -default: "e8 88 00" (an orange) - The color of things visible with the automap cheat when the map is - overlayed. Changes to this cvar take effect the next time the automap is - activated. -See also: all the am_* cvars - -am_ovunseencolor -(cvar: color) -default: "00 22 6e" (a dark greenish-blue) - The color of unseen lines on the automap when the map is overlayed. - Changes to this cvar take effect the next time the automap is activated. -See also: all the am_* cvars - -am_ovwallcolor -(cvar: color) -default: "00 ff 00" (a bright green) - The color of impassable walls when the automap is overlayed. Changes to - this cvar take effect the next time the automap is activated. -See also: all the am_* cvars - -am_ovyourcolor -(cvar: color) -default: "fc e8 d8" (a very light orange--almost white) - The color of the arrow representing the player in single player games when - the map is overlayed. Changes to this cvar take effect the next time the - automap is activated. -See also: all the am_* cvars - -am_rotate -(cvar: boolean) -default: 0 - Normally, the automap is always drawn such that north is at the top of the - screen. Setting this cvar to "1" causes the automap to be drawn so that - lines toward the top of the screen are always directly in front of the - player's view. Changes to this cvar take effect immediately, unlike most - of the other am_* cvars. This can be particularly useful when the automap - is overlayed. -See also: am_overlay - -am_showmonsters -(cvar: boolean) -default: 1 - When true, the fullscreen automap will display a count of the number of - monsters that have been killed in the current level and the total number - of monsters in the level. -See also: am_showtime, am_showsecrets - -am_showsecrets -(cvar: boolean) -default: 1 - When true, the fullscreen automap will display a count of the number of - secrets that have been found in the current level and the total number of - secrets in the level. -See also: am_showmonsters, am_showtime - -am_showtime -(cvar: boolean) -default: 1 - When true, the fullscreen automap will display the total amount of time - you have been in a level (excluding time that has been paused). -See also: am_showmonsters, am_showsecrets - -am_thingcolor -(cvar: color) -default: "fcfc fcfc fcfc" (almost white) - The color of things revealed with the map cheat. Changes to this cvar take - effect the next time the automap is activated. -See also: all the am_* cvars - -am_tswallcolor -(cvar: color) -default: "8888 8888 8888" (gray) - The color of two-sided lines that don't have any difference in floor or - ceiling heights on either side. Only seen using map cheat. Changes to this - cvar take effect the next time the automap is activated. -See also: all the am_* cvars - -am_usecustomcolors -(cvar: boolean) -default: 1 - When true, the automap uses the colors specified by the am_* cvars, - otherwise it uses the standard DOOM automap colors. Changes to this cvar - take effect the next time the automap is activated. -See also: all the am_* cvars - -am_wallcolor -(cvar: color) -default: "2c2c 1818 0808" (a dark brown) - The color of one-sided and secret walls in the automap. Changes to this - cvar take effect the next time the automap is activated. -See also: all the am_* cvars - -am_xhaircolor -(cvar: color) -default: "8080 8080 8080" (gray) - The color of the "crosshair" dot in the center of the automap. Changes to - this cvar take effect the next time the automap is activated. -See also: all the am_* cvars - -am_yourcolor -(cvar: color) -default: "fcfc e8e8 d8d8" (a very light orange--almost white) - The color of the arrow representing the player in single player games. - Changes to this cvar take effect the next time the automap is activated. -See also: all the am_* cvars - -autoaim -(cvar: number) -default: 5000 - This represents the vertical distance from an object that the player's - sight must be before that object is aimed at. Setting this cvar to "0" - disables autoaiming, while large values such as "5000" will reproduce the - original DOOM behavior of always autoaiming. -See also: color, name - -autoexec -(cvar: string) -default: "/autoexec.cfg" - This is a file that will be automatically executed by ZDoom each time it - starts. This file is executed immediately after the config file is loaded. - It should contain a series of console commands. C++ style comments are - also supported. Comments begin with // and anything after them until the - end of the line will be ignored. - - represents the directory that ZDoom is in and will naturally - vary depending on where you put it. - -bind [key [command string]] -(command) - If no parameters are specified, the entire list of bound keys will be - printed to the console. If only [key] is specified, the binding for that - specific key will be printed. If both [key] and [command string] are - specified, [command string] will be bound to [key]. -See also: doublebind, unbind, unbindall, undoublebind - -binddefaults -(command) - Binds all keys to their default commands. This will not unbind any keys - that do not have any default bindings, so if you want to properly restore - the default controls, you need to use unbindall first. -See also: unbindall, bind - -bot_allowspy -(cvar: boolean) -default: 0 - Allows you to see through the eyes of bots during botmatch games. - -bot_next_color -(cvar: number) -default: 11 - Theoretically, a number representing the color of the next bot to be - spawned. In practice, it doesn't do anything. - -bot_observer -(cvar: boolean) -default: 0 - When set to 1, the player will experience minimal interaction with the - world, and bots will ignore him. - -bumpgamma -(command) - Increases the current gamma level by 0.1. If the new gamma level would be - greater than 3.0, it wraps the gamma around to 1.0 -See also: gamma - -centerview -(command) - Causes the player to look straight ahead. -See also: +lookup, +lookdown - -changemap -(command) - Exits the current level and continues the game on the specified map. - Unlike the map and idclev commands, this command *will* work properly - during network games and is recorded in demos. Unfortunately, it has - occasionally resulted in some problems. It should, however, be safe - enough to use most of the time. -See also: idclev, map - -changemus -(command) - Changes the currently playing music. should be the name of a music - lump or file on disk (which need not have been specified with the -file - parameter). -See also: dir, idmus - -chase -(command) - Turns the chasecom on and off. This command also works while watching - demos, so you can bind it to a key and watch demos in the third person. -See also: chase_dist, chase_height, chasedemo - -chase_dist -(cvar: number) -default: 90 - This is how far away from the player the chasecam likes to be, but it will - get closer as necessary to avoid going inside walls. -See also: chase, chase_height - -chase_height -(cvar: number) -default: -8 - This is the base height above the top of the player's head that the - chasecam will be positioned at. Looking up and down will move the camera - in the opposite direction so that the player stays at approximately the - same height on the screen. Large values of this cvar (either positive or - negative) will produce strange output. -See also: chase, chase_dist - -chasedemo -(cvar: boolean) -default: 0 - If this cvar is true, then demos will automatically start with the - chasecam active. -See also: chase - -chatmacro0 -chatmacro1 -chatmacro2 -chatmacro3 -chatmacro4 -chatmacro5 -chatmacro6 -chatmacro7 -chatmacro8 -chatmacro9 -(cvar: string) - These are all strings programmable to the function keys during a netgame. - To use these, enter chat mode with the messagemode command, and then hold - down Alt and press one of the number keys. The string stored in the - corresponding chatmacro cvar will be sent as if you had typed it yourself. - -cl_bloodtype -(cvar: number) -default: 0 - Controls how blood is drawn. Supported values are: - 0: Blood is drawn as sprites - 1: Blood is drawn as both sprites and particles - 2: Blood is drawn as particles - -cl_pufftype -(cvar: number) -default: 0 - Controls how bullet puffs are drawn. Supported values are: - 0: Puffs are drawn as sprites. - 1: Puffs are drawn as particles. - -cl_rockettrails -(cvar: boolean) -default: 1 - Controls whether or not rockets leave trails of smoke behind them. - -cl_run -(cvar: boolean) -default: 0 - When non-zero, the game will always treat movement commands as if +speed - is active. -See also: +speed - -clear -(command) - Clears the console of all text. - -cmdlist -(command) - Lists all commands currently supported by ZDoom. -See also: cvarlist - -color -(cvar: color) -default: "4040 cfcf 0000" (Mostly green) - This is the color of your player's suit. -See also: gender, name, skin, team - -con_midtime -(cvar: number) -default: 3 - This is the number of seconds that messages in the middle of the screen - will be displayed before they dispapper. -See also: con_notifytime - -con_notifytime -(cvar: number) -default: 3 - This is the number of seconds that new messages will stay at the top of - the screen before they start scrolling away. -See also: con_midtime - -con_scaletext -(cvar: boolean) -default: 0 - If this cvar is true, then message text will be scaled to larger sizes - depending on the screen resolution so that it will stay approximately the - same size it would be on a 320x200 screen. - -configver -(cvar: number) -default: "116" - This cvar is used to keep track of which version of ZDoom was used to write - the current config file and adjust for differences that may have been - introduced between versions. Changing it has no effect, since it will - always be changed to reflect the current game version before the config - file is saved. - -crosshair -(cvar: number) -default: 0 - If this cvar is non-zero, it draws a crosshair. If this cvar is negative, - the crosshair is translucent, otherwise it is opaque. The specific - crosshair drawn depends on the value of this cvar. - -cvarlist -(command) - Lists the values of all currently defined cvars. Each cvar can also be - prefaced by multiple flags. These are: - - A Cvar gets saved in the config file. - U Cvar contains user info. - S Cvar contains server info. - - Cvar can only be changed from the command line. - L Changes to cvar's contents don't take effect until the next game. - C Cvar has an internal callback. - * Cvar was created by the user and is meaningless to the game. - -See also: cmdlist, get, set - -deathmatch -(cvar: boolean) -default: 0 - When true, deathmatch rules are used for the game. - -def_patch -(cvar: string) -default: "" - This is the name of a DeHackEd patch file to automatically apply each time - the game is run. It will only be used if the following conditions are met: - - a) def_patch is not "", and the file exists. - b) No loaded WAD files contain a DEHACKED lump. - c) No patch is specified with the -deh command line parameter. - -developer -(cvar: boolean) -default: 0 - When true, prints various debugging messages to the console. - -dimamount -(cvar: number) -default: 1 - This is the amount of dimcolor to mix with the background when a menu is - displayed. The available values are: - 0: Do not dim the background. - 1: Mix 25% of dimcolor with the background. - 2: Mix 50% of dimcolor with the background. - 3: Mix 75% of dimcolor with the background. -See also: dimcolor - -dimcolor -(cvar: color) -default: "ffff d7d7 0000" (gold) - This is the color to mix with the background when a menu is displayed. -See also: dimamount, setcolor - -dir [[path/][pattern]] -(command) - This command lists the contents of a directory. It supports wildcards (but - will not recurse into multiple directories). If [path] is not specified, - it will display the contents of the directory the game was run from. - -deathmatch -(cvar: boolean) -default: 0 - When set to true, the game is treated as a deathmatch. When the game is - started with -altdeath or -deathmatch, this cvar is automatically set to 1. - -dmflags -(cvar: number) -default: 0 - This cvar controls the behavior of several aspects of gameplay. To - determine what value to store in this cvar, select the desired features - from the table below and add their values together. If a feature is - marked with (DM), then that feature will only be active during a - deathmatch game. - - Value Description - ------ ----------------------------------------------------------------- - 1 Do not spawn health items (DM) - 2 Do not spawn powerups (DM) - 4 Leave weapons around after pickup (DM) - 8 Falling too far hurts - 16 Players cannot hurt teammates (friendly fire avoidance) - 64 Stay on the same map when someone exits (DM) - 128 Spawn players as far as possible from other players (DM) - 256 Automatically respawn dead players (DM) - 512 Do not spawn armor (DM) - 1024 Kill anyone who tries to exit the level (DM) - 2048 Don't use any ammo when firing - 4096 Don't spawn monsters - 8192 Monsters respawn sometime after their death - 16384 Powerups other than invulnerability and invisibilty respawn - 32768 Monsters are fast - 65536 Don't allow jumping - 131072 Don't allow freelook - 262144 Invulnerability and invisibility respawn - -See also: menu_gameplay, teamplay - -doublebind [key [command string]] -(command) - If no parameters are specified, the entire list of doublebound keys will be - printed to the console. If only [key] is specified, the doublebinding for - that specific key will be printed. If both [key] and [command string] are - specified, [command string] will be doublebound to [key]. (Doublebindings - are commands that are executed when a key is pressed twice quickly--such as - double cliking a mouse button.) -See also: bind, unbind, unbindall, undoublebind - -dumpheap -(command) - Prints detailed information about the heap. Probably not very useful to - the average user. -See also: mem - -echo -(command) - Prints to the console. - -endgame -(command) - Ends the current single player game and drops the console down to cover the - screen. -See also: menu_endgame - -error -(command) - Simulates an error by killing the current game and falling back to the - fullscreen console with the specified message. - -exec