From 53ee7cfc7babd1b604d33ea09767745466e6fb06 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Sat, 15 Sep 2018 12:27:14 +0200 Subject: [PATCH 01/40] - fixed some warnings in OBJ model code. (This clearly shows that using 'long' as parameters in any interface must be stopped. It is fundamentally unsafe to have a type whose size is not reliable - it's either an int-sized nor a pointer sized value, depending on the platform, and essentially worthless.) --- src/r_data/models/models_obj.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/r_data/models/models_obj.cpp b/src/r_data/models/models_obj.cpp index a3124539b..4cc5ffa17 100644 --- a/src/r_data/models/models_obj.cpp +++ b/src/r_data/models/models_obj.cpp @@ -54,7 +54,7 @@ bool FOBJModel::Load(const char* fn, int lumpnum, const char* buffer, int length } if (nlpos == -1) { - nlpos = objBuf.Len(); + nlpos = (long)objBuf.Len(); } FString lineStr(objBuf.GetChars() + bpos, nlpos - bpos); mtlUsages.Push(lineStr); @@ -72,7 +72,7 @@ bool FOBJModel::Load(const char* fn, int lumpnum, const char* buffer, int length nlpos = objBuf.IndexOf('\n', bpos); if (nlpos == -1) { - nlpos = objBuf.Len(); + nlpos = (long)objBuf.Len(); } memcpy(wObjBuf + bpos, mtlUsages[i].GetChars(), nlpos - bpos); } @@ -384,10 +384,10 @@ void FOBJModel::BuildVertexBuffer(FModelRenderer *renderer) { // https://www.khronos.org/opengl/wiki/Calculating_a_Surface_Normal // Find other sides of triangle - int nextSidx = side + 2; + auto nextSidx = side + 2; if (nextSidx >= 3) nextSidx -= 3; - int lastSidx = side + 1; + auto lastSidx = side + 1; if (lastSidx >= 3) lastSidx -= 3; OBJFaceSide &nextSide = surfaces[i].tris[j].sides[nextSidx]; From 3046a7dd814fe623721e9e7b1b80798c48e3badc Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Sat, 15 Sep 2018 12:30:05 +0200 Subject: [PATCH 02/40] - be more thorough with 'in menu' checks for certain protected functions. They would also pass the test if a menu just was open but not the actual invoker. Also error out if this happens so that modders can see that they are doing unsupported things. Silent failure is not a good idea here. --- src/c_bind.cpp | 16 ++++++++++++++++ src/c_cvars.cpp | 28 ++++++++++++++++++++++++---- src/menu/menu.cpp | 7 +++++++ src/menu/menu.h | 1 + 4 files changed, 48 insertions(+), 4 deletions(-) diff --git a/src/c_bind.cpp b/src/c_bind.cpp index 1e4f7355d..1ed96dafb 100644 --- a/src/c_bind.cpp +++ b/src/c_bind.cpp @@ -45,6 +45,7 @@ #include "dobject.h" #include "vm.h" #include "i_time.h" +#include "menu/menu.h" const char *KeyNames[NUM_KEYS] = { @@ -267,6 +268,14 @@ DEFINE_ACTION_FUNCTION(FKeyBindings, SetBind) PARAM_SELF_STRUCT_PROLOGUE(FKeyBindings); PARAM_INT(k); PARAM_STRING(cmd); + + // Only menus are allowed to change bindings. + if (DMenu::InMenu == 0) + { + I_FatalError("Attempt to change key bindings outside of menu code to '%s'", cmd.GetChars()); + } + + self->SetBind(k, cmd); return 0; } @@ -506,6 +515,13 @@ DEFINE_ACTION_FUNCTION(FKeyBindings, UnbindACommand) { PARAM_SELF_STRUCT_PROLOGUE(FKeyBindings); PARAM_STRING(cmd); + + // Only menus are allowed to change bindings. + if (DMenu::InMenu == 0) + { + I_FatalError("Attempt to unbind key bindings for '%s' outside of menu code", cmd.GetChars()); + } + self->UnbindACommand(cmd); return 0; } diff --git a/src/c_cvars.cpp b/src/c_cvars.cpp index 605338b01..5be8a28a7 100644 --- a/src/c_cvars.cpp +++ b/src/c_cvars.cpp @@ -220,7 +220,14 @@ DEFINE_ACTION_FUNCTION(_CVar, SetInt) { // Only menus are allowed to change CVARs. PARAM_SELF_STRUCT_PROLOGUE(FBaseCVar); - if (!(self->GetFlags() & CVAR_MOD) && CurrentMenu == nullptr) return 0; + if (!(self->GetFlags() & CVAR_MOD)) + { + // Only menus are allowed to change non-mod CVARs. + if (DMenu::InMenu == 0) + { + I_FatalError("Attempt to change CVAR '%s' outside of menu code", self->GetName()); + } + } PARAM_INT(val); UCVarValue v; v.Int = val; @@ -230,9 +237,15 @@ DEFINE_ACTION_FUNCTION(_CVar, SetInt) DEFINE_ACTION_FUNCTION(_CVar, SetFloat) { - // Only menus are allowed to change CVARs. PARAM_SELF_STRUCT_PROLOGUE(FBaseCVar); - if (!(self->GetFlags() & CVAR_MOD) && CurrentMenu == nullptr) return 0; + if (!(self->GetFlags() & CVAR_MOD)) + { + // Only menus are allowed to change non-mod CVARs. + if (DMenu::InMenu == 0) + { + I_FatalError("Attempt to change CVAR '%s' outside of menu code", self->GetName()); + } + } PARAM_FLOAT(val); UCVarValue v; v.Float = (float)val; @@ -244,7 +257,14 @@ DEFINE_ACTION_FUNCTION(_CVar, SetString) { // Only menus are allowed to change CVARs. PARAM_SELF_STRUCT_PROLOGUE(FBaseCVar); - if (!(self->GetFlags() & CVAR_MOD) && CurrentMenu == nullptr) return 0; + if (!(self->GetFlags() & CVAR_MOD)) + { + // Only menus are allowed to change non-mod CVARs. + if (DMenu::InMenu == 0) + { + I_FatalError("Attempt to change CVAR '%s' outside of menu code", self->GetName()); + } + } PARAM_STRING(val); UCVarValue v; v.String = val.GetChars(); diff --git a/src/menu/menu.cpp b/src/menu/menu.cpp index 0613ec255..bcba95930 100644 --- a/src/menu/menu.cpp +++ b/src/menu/menu.cpp @@ -53,6 +53,7 @@ #include "events.h" #include "scripting/types.h" +int DMenu::InMenu; // // Todo: Move these elsewhere // @@ -190,7 +191,9 @@ bool DMenu::CallResponder(event_t *ev) VMValue params[] = { (DObject*)this, &e }; int retval; VMReturn ret(&retval); + InMenu++; VMCall(func, params, 2, &ret, 1); + InMenu--; return !!retval; } } @@ -202,7 +205,9 @@ bool DMenu::CallResponder(event_t *ev) VMValue params[] = { (DObject*)this, &e }; int retval; VMReturn ret(&retval); + InMenu++; VMCall(func, params, 2, &ret, 1); + InMenu--; return !!retval; } } @@ -222,7 +227,9 @@ bool DMenu::CallMenuEvent(int mkey, bool fromcontroller) VMValue params[] = { (DObject*)this, mkey, fromcontroller }; int retval; VMReturn ret(&retval); + InMenu++; VMCall(func, params, 3, &ret, 1); + InMenu--; return !!retval; } else return false; diff --git a/src/menu/menu.h b/src/menu/menu.h index 904ea0080..b374d29e1 100644 --- a/src/menu/menu.h +++ b/src/menu/menu.h @@ -265,6 +265,7 @@ public: bool mMouseCapture; bool mBackbuttonSelected; bool DontDim; + static int InMenu; DMenu(DMenu *parent = NULL); bool TranslateKeyboardEvents(); From 7d1af25b46eb28e9d154a68f92ab3143a4091e08 Mon Sep 17 00:00:00 2001 From: "alexey.lysiuk" Date: Fri, 14 Sep 2018 11:10:49 +0300 Subject: [PATCH 03/40] Fixed code generation of infinite for loop https://forum.zdoom.org/viewtopic.php?t=62023 --- src/scripting/backend/codegen.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/scripting/backend/codegen.cpp b/src/scripting/backend/codegen.cpp index c8fcb1f09..d6fc442c6 100644 --- a/src/scripting/backend/codegen.cpp +++ b/src/scripting/backend/codegen.cpp @@ -10291,9 +10291,14 @@ FxWhileLoop::~FxWhileLoop() FxExpression *FxWhileLoop::DoResolve(FCompileContext &ctx) { CHECKRESOLVED(); - SAFE_RESOLVE(Condition, ctx); + SAFE_RESOLVE_OPT(Condition, ctx); SAFE_RESOLVE_OPT(Code, ctx); + if (Condition == nullptr) + { + Condition = new FxConstant(true, ScriptPosition); + } + if (Condition->ValueType != TypeBool) { Condition = new FxBoolCast(Condition); From 7885a22cadb27d31c5f1966181c29b264d4742a7 Mon Sep 17 00:00:00 2001 From: ZippeyKeys12 Date: Fri, 14 Sep 2018 21:57:07 -0500 Subject: [PATCH 04/40] Add NewGame to EventHandler https://forum.zdoom.org/viewtopic.php?t=61908 --- src/events.cpp | 22 ++++++++++++++++++++++ src/events.h | 6 ++++++ src/g_level.cpp | 4 ++++ wadsrc/static/zscript/events.txt | 3 +++ 4 files changed, 35 insertions(+) diff --git a/src/events.cpp b/src/events.cpp index 91e66b82b..1baa9b1d3 100755 --- a/src/events.cpp +++ b/src/events.cpp @@ -515,6 +515,14 @@ bool E_CheckReplacement( PClassActor *replacee, PClassActor **replacement ) return final; } +void E_NewGame() +{ + for (DStaticEventHandler* handler = E_FirstEventHandler; handler; handler = handler->next) + { + handler->NewGame(); + } +} + // normal event loopers (non-special, argument-less) DEFINE_EVENT_LOOPER(RenderFrame) DEFINE_EVENT_LOOPER(WorldLightning) @@ -668,6 +676,8 @@ DEFINE_EMPTY_HANDLER(DStaticEventHandler, NetworkProcess); DEFINE_EMPTY_HANDLER(DStaticEventHandler, CheckReplacement); +DEFINE_EMPTY_HANDLER(DStaticEventHandler, NewGame) + // =========================================== // // Event handlers @@ -1154,6 +1164,18 @@ void DStaticEventHandler::CheckReplacement( PClassActor *replacee, PClassActor * } } +void DStaticEventHandler::NewGame() +{ + IFVIRTUAL(DStaticEventHandler, NewGame) + { + // don't create excessive DObjects if not going to be processed anyway + if (func == DStaticEventHandler_NewGame_VMPtr) + return; + VMValue params[1] = { (DStaticEventHandler*)this }; + VMCall(func, params, 1, nullptr, 0); + } +} + // void DStaticEventHandler::OnDestroy() { diff --git a/src/events.h b/src/events.h index 54d975566..456db6c10 100755 --- a/src/events.h +++ b/src/events.h @@ -72,6 +72,9 @@ void E_Console(int player, FString name, int arg1, int arg2, int arg3, bool manu // called when looking up the replacement for an actor class bool E_CheckReplacement(PClassActor* replacee, PClassActor** replacement); +// called on new game +void E_NewGame(); + // send networked event. unified function. bool E_SendNetworkEvent(FString name, int arg1, int arg2, int arg3, bool manual); @@ -172,6 +175,9 @@ public: // void CheckReplacement(PClassActor* replacee, PClassActor** replacement, bool* final); + + // + void NewGame(); }; class DEventHandler : public DStaticEventHandler { diff --git a/src/g_level.cpp b/src/g_level.cpp index 86567ea84..36843919b 100644 --- a/src/g_level.cpp +++ b/src/g_level.cpp @@ -527,6 +527,10 @@ void G_InitNew (const char *mapname, bool bTitleLevel) gamestate = GS_LEVEL; } G_DoLoadLevel (0, false); + if(!savegamerestore) + { + E_NewGame(); + } } // diff --git a/wadsrc/static/zscript/events.txt b/wadsrc/static/zscript/events.txt index 9a908f060..d2d8ed324 100755 --- a/wadsrc/static/zscript/events.txt +++ b/wadsrc/static/zscript/events.txt @@ -339,6 +339,9 @@ class StaticEventHandler : Object native play version("2.4") // virtual native void CheckReplacement(ReplaceEvent e); + // + virtual native void NewGame(); + // this value will be queried on Register() to decide the relative order of this handler to every other. // this is most useful in UI systems. // default is 0. From 60f82d1eb9f89d56c8b3ccaf656062fe5d1ec90e Mon Sep 17 00:00:00 2001 From: ZippeyKeys12 Date: Fri, 14 Sep 2018 21:59:25 -0500 Subject: [PATCH 05/40] Make StatusScreen::End virtual https://forum.zdoom.org/viewtopic.php?t=59419 --- wadsrc/static/zscript/statscreen/statscreen.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/wadsrc/static/zscript/statscreen/statscreen.txt b/wadsrc/static/zscript/statscreen/statscreen.txt index 2dcb2de72..a8d8e25ed 100644 --- a/wadsrc/static/zscript/statscreen/statscreen.txt +++ b/wadsrc/static/zscript/statscreen/statscreen.txt @@ -400,7 +400,7 @@ class StatusScreen abstract play version("2.5") // //==================================================================== - void End () + virtual void End () { CurState = LeavingIntermission; From c988a0b3a49a365237b6e2aadfab6ed297d02b86 Mon Sep 17 00:00:00 2001 From: Major Cooke Date: Fri, 7 Sep 2018 11:19:20 -0500 Subject: [PATCH 06/40] Allow LineAttack's LAF_NOINTERACT to fill FTranslatedLineTarget's information. - Originally when the flag was made, LineAttack was not yet exported. This can now be benefitted directly from ZScript. --- src/p_map.cpp | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/src/p_map.cpp b/src/p_map.cpp index dacc7ae8b..a44d37c72 100644 --- a/src/p_map.cpp +++ b/src/p_map.cpp @@ -4754,11 +4754,18 @@ AActor *P_LineAttack(AActor *t1, DAngle angle, double distance, // We must pass the unreplaced puff type here puff = P_SpawnPuff(t1, pufftype, bleedpos, trace.SrcAngleFromTarget, trace.SrcAngleFromTarget - 90, 2, puffFlags | PF_HITTHING, trace.Actor); - - if (nointeract) - { - return puff; - } + } + if (victim != NULL) + { + victim->linetarget = trace.Actor; + victim->attackAngleFromSource = trace.SrcAngleFromTarget; + // With arbitrary portals this cannot be calculated so using the actual attack angle is the only option. + victim->angleFromSource = trace.unlinked ? victim->attackAngleFromSource : t1->AngleTo(trace.Actor); + victim->unlinked = trace.unlinked; + } + if (nointeract) + { + return puff; } // Allow puffs to inflict poison damage, so that hitscans can poison, too. @@ -4833,14 +4840,7 @@ AActor *P_LineAttack(AActor *t1, DAngle angle, double distance, P_TraceBleed(newdam > 0 ? newdam : damage, trace.HitPos, trace.Actor, trace.SrcAngleFromTarget, pitch); } } - if (victim != NULL) - { - victim->linetarget = trace.Actor; - victim->attackAngleFromSource = trace.SrcAngleFromTarget; - // With arbitrary portals this cannot be calculated so using the actual attack angle is the only option. - victim->angleFromSource = trace.unlinked? victim->attackAngleFromSource : t1->AngleTo(trace.Actor); - victim->unlinked = trace.unlinked; - } + } if (trace.Crossed3DWater || trace.CrossedWater) { From 1210e1a951cca43e3328f44415d882c462e33008 Mon Sep 17 00:00:00 2001 From: Major Cooke Date: Thu, 13 Sep 2018 09:42:34 -0500 Subject: [PATCH 07/40] Added DMG_EXPLOSION flag. - This allows modders to determine if damage is caused by an actual explosion, assigned by P_RadiusAttack and BlastActor for +TOUCHY actors. --- src/p_local.h | 1 + src/p_map.cpp | 4 ++-- wadsrc/static/zscript/constants.txt | 1 + wadsrc/static/zscript/hexen/blastradius.txt | 2 +- 4 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/p_local.h b/src/p_local.h index 08c128847..d1a81a992 100644 --- a/src/p_local.h +++ b/src/p_local.h @@ -453,6 +453,7 @@ enum EDmgFlags DMG_NO_PROTECT = 256, DMG_USEANGLE = 512, DMG_NO_PAIN = 1024, + DMG_EXPLOSION = 2048, }; diff --git a/src/p_map.cpp b/src/p_map.cpp index a44d37c72..58c0bb706 100644 --- a/src/p_map.cpp +++ b/src/p_map.cpp @@ -6177,7 +6177,7 @@ int P_RadiusAttack(AActor *bombspot, AActor *bombsource, int bombdamage, int bom { //[MC] Don't count actors saved by buddha if already at 1 health. int prehealth = thing->health; - newdam = P_DamageMobj(thing, bombspot, bombsource, damage, bombmod); + newdam = P_DamageMobj(thing, bombspot, bombsource, damage, bombmod, DMG_EXPLOSION); if (thing->health < prehealth) count++; } else if (thing->player == NULL && (!(flags & RADF_NOIMPACTDAMAGE) && !(thing->flags7 & MF7_DONTTHRUST))) @@ -6229,7 +6229,7 @@ int P_RadiusAttack(AActor *bombspot, AActor *bombsource, int bombdamage, int bom { // OK to damage; target is in direct path //[MC] Don't count actors saved by buddha if already at 1 health. int prehealth = thing->health; - int newdam = P_DamageMobj(thing, bombspot, bombsource, damage, bombmod); + int newdam = P_DamageMobj(thing, bombspot, bombsource, damage, bombmod, DMG_EXPLOSION); P_TraceBleed(newdam > 0 ? newdam : damage, thing, bombspot); if (thing->health < prehealth) count++; } diff --git a/wadsrc/static/zscript/constants.txt b/wadsrc/static/zscript/constants.txt index 156763a27..dc394a0d2 100644 --- a/wadsrc/static/zscript/constants.txt +++ b/wadsrc/static/zscript/constants.txt @@ -939,6 +939,7 @@ enum EDmgFlags DMG_NO_PROTECT = 256, DMG_USEANGLE = 512, DMG_NO_PAIN = 1024, + DMG_EXPLOSION = 2048, } enum EReplace diff --git a/wadsrc/static/zscript/hexen/blastradius.txt b/wadsrc/static/zscript/hexen/blastradius.txt index 3dd355b7c..0f5094892 100644 --- a/wadsrc/static/zscript/hexen/blastradius.txt +++ b/wadsrc/static/zscript/hexen/blastradius.txt @@ -89,7 +89,7 @@ extend class Actor if (victim.bTouchy) { // Touchy objects die when blasted victim.bArmed = false; // Disarm - victim.DamageMobj(self, self, victim.health, 'Melee', DMG_FORCED); + victim.DamageMobj(self, self, victim.health, 'Melee', DMG_FORCED|DMG_EXPLOSION); } } From 74d939c0d28480a96084549f8c72aaefe02ca9b2 Mon Sep 17 00:00:00 2001 From: Rachael Alexanderson Date: Sat, 15 Sep 2018 08:16:02 -0400 Subject: [PATCH 08/40] - archive 'multiplayer' flag in savegames. https://forum.zdoom.org/viewtopic.php?f=2&t=61980 --- src/p_saveg.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/p_saveg.cpp b/src/p_saveg.cpp index 3eb98b808..fcb075e0f 100644 --- a/src/p_saveg.cpp +++ b/src/p_saveg.cpp @@ -942,6 +942,8 @@ void G_SerializeLevel(FSerializer &arc, bool hubload) arc.ReadObjects(hubload); } + arc("multiplayer", multiplayer); + arc("level.flags", level.flags) ("level.flags2", level.flags2) ("level.fadeto", level.fadeto) From 58c6614c03f17aa6192539c98ce63e67da355d36 Mon Sep 17 00:00:00 2001 From: David Carlier Date: Sat, 15 Sep 2018 22:57:22 +0100 Subject: [PATCH 09/40] silent few warnings --- src/hwrenderer/utility/hw_clock.cpp | 2 +- src/v_framebuffer.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/hwrenderer/utility/hw_clock.cpp b/src/hwrenderer/utility/hw_clock.cpp index 40c61924b..a34b1fdb1 100644 --- a/src/hwrenderer/utility/hw_clock.cpp +++ b/src/hwrenderer/utility/hw_clock.cpp @@ -169,7 +169,7 @@ void CheckBench() AppendRenderTimes(compose); AppendLightStats(compose); //AppendMissingTextureStats(compose); - compose.AppendFormat("%llu fps\n\n", screen->GetLastFPS()); + compose.AppendFormat("%llu fps\n\n", (unsigned long long)screen->GetLastFPS()); FILE *f = fopen("benchmarks.txt", "at"); if (f != NULL) diff --git a/src/v_framebuffer.cpp b/src/v_framebuffer.cpp index 0b1338d37..b4a5807f9 100644 --- a/src/v_framebuffer.cpp +++ b/src/v_framebuffer.cpp @@ -227,7 +227,7 @@ void DFrameBuffer::DrawRateStuff () int textScale = active_con_scale(); - chars = mysnprintf (fpsbuff, countof(fpsbuff), "%2llu ms (%3llu fps)", howlong, LastCount); + chars = mysnprintf (fpsbuff, countof(fpsbuff), "%2llu ms (%3llu fps)", (unsigned long long)howlong, (unsigned long long)LastCount); rate_x = Width / textScale - ConFont->StringWidth(&fpsbuff[0]); Clear (rate_x * textScale, 0, Width, ConFont->GetHeight() * textScale, GPalette.BlackIndex, 0); DrawText (ConFont, CR_WHITE, rate_x, 0, (char *)&fpsbuff[0], From e13d1e4d0d43819069d13397e5540635985903c8 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Sun, 16 Sep 2018 13:52:02 +0200 Subject: [PATCH 10/40] - do not render lights from uninitialized data. --- src/gl/scene/gl_flats.cpp | 2 ++ src/hwrenderer/scene/hw_flats.cpp | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/gl/scene/gl_flats.cpp b/src/gl/scene/gl_flats.cpp index e399037fa..159a8e16a 100644 --- a/src/gl/scene/gl_flats.cpp +++ b/src/gl/scene/gl_flats.cpp @@ -57,6 +57,7 @@ void FDrawInfo::SetupSubsectorLights(GLFlat *flat, int pass, subsector_t * sub, { if (dli != NULL && *dli != -1) { + if (flat->renderstyle == STYLE_Add && !level.lightadditivesurfaces) return; // no lights on additively blended surfaces. gl_RenderState.ApplyLightIndex(GLRenderer->mLights->GetIndex(*dli)); (*dli)++; return; @@ -85,6 +86,7 @@ void FDrawInfo::SetupSectorLights(GLFlat *flat, int pass, int *dli) { if (dli != NULL && *dli != -1) { + if (flat->renderstyle == STYLE_Add && !level.lightadditivesurfaces) return; // no lights on additively blended surfaces. gl_RenderState.ApplyLightIndex(GLRenderer->mLights->GetIndex(*dli)); (*dli)++; return; diff --git a/src/hwrenderer/scene/hw_flats.cpp b/src/hwrenderer/scene/hw_flats.cpp index 91cb8027e..0c3ec07e4 100644 --- a/src/hwrenderer/scene/hw_flats.cpp +++ b/src/hwrenderer/scene/hw_flats.cpp @@ -139,9 +139,9 @@ bool GLFlat::SetupLights(int pass, FLightNode * node, FDynLightData &lightdata, { Plane p; + lightdata.Clear(); if (renderstyle == STYLE_Add && !level.lightadditivesurfaces) return false; // no lights on additively blended surfaces. - lightdata.Clear(); while (node) { ADynamicLight * light = node->lightsource; From 38c8f0d585eb571ac7ba537da55e4d4a6afe6e5c Mon Sep 17 00:00:00 2001 From: Marisa Kirisame Date: Sun, 16 Sep 2018 15:05:57 +0200 Subject: [PATCH 11/40] Adds OnDrop virtual to inventory items. Called on the dropped item at the end of AActor::DropInventory. --- src/p_mobj.cpp | 8 ++++++++ wadsrc/static/zscript/inventory/inventory.txt | 12 +++++++++++- 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/src/p_mobj.cpp b/src/p_mobj.cpp index 070745df4..e0ebaea92 100644 --- a/src/p_mobj.cpp +++ b/src/p_mobj.cpp @@ -1158,6 +1158,14 @@ AInventory *AActor::DropInventory (AInventory *item, int amt) drop->Vel += Vel; drop->flags &= ~MF_NOGRAVITY; // Don't float drop->ClearCounters(); // do not count for statistics again + { + // [MK] call OnDrop so item can change its drop behaviour + IFVIRTUALPTR(drop, AInventory, OnDrop) + { + VMValue params[] = { drop, this }; + VMCall(func, params, 2, nullptr, 0); + } + } return drop; } diff --git a/wadsrc/static/zscript/inventory/inventory.txt b/wadsrc/static/zscript/inventory/inventory.txt index 1bc3f42a6..d724c90d9 100644 --- a/wadsrc/static/zscript/inventory/inventory.txt +++ b/wadsrc/static/zscript/inventory/inventory.txt @@ -906,7 +906,17 @@ class Inventory : Actor native return item; } - + + //=========================================================================== + // + // AInventory :: OnDrop + // + // Called by AActor::DropInventory. Allows items to modify how they behave + // after being dropped. + // + //=========================================================================== + + virtual void OnDrop (Actor dropper) {} } //=========================================================================== From bc1e659c7bcc48919e6e0fd79fa09f83a2fc1db7 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Sun, 16 Sep 2018 22:38:20 +0200 Subject: [PATCH 12/40] Revert "- reworked fog uniforms to move the global fog mode setting to the viewpoint buffer." This reverts commit 8b26b6dd1ed67127fe241418e63fad27faea76f0. This was causing problems with light mode 2 because some edge cases were no longer handled properly. --- src/gl/data/gl_viewpointbuffer.cpp | 1 - src/gl/renderer/gl_renderstate.cpp | 24 +++++++++++++++++---- src/gl/scene/gl_scene.cpp | 1 - src/gl/shaders/gl_shader.cpp | 4 ++-- src/gl/shaders/gl_shader.h | 1 + src/hwrenderer/scene/hw_drawinfo.cpp | 1 - src/hwrenderer/scene/hw_viewpointuniforms.h | 1 - wadsrc/static/shaders/glsl/fogboundary.fp | 2 +- wadsrc/static/shaders/glsl/main.fp | 16 +++++++------- 9 files changed, 32 insertions(+), 19 deletions(-) diff --git a/src/gl/data/gl_viewpointbuffer.cpp b/src/gl/data/gl_viewpointbuffer.cpp index eb4d8c92e..611db9e40 100644 --- a/src/gl/data/gl_viewpointbuffer.cpp +++ b/src/gl/data/gl_viewpointbuffer.cpp @@ -144,7 +144,6 @@ void GLViewpointBuffer::Set2D(int width, int height) HWViewpointUniforms matrices; matrices.SetDefaults(); matrices.mProjectionMatrix.ortho(0, width, height, 0, -1.0f, 1.0f); - matrices.mFogEnabled = 3; matrices.CalcDependencies(); Map(); memcpy(mBufferPointer, &matrices, sizeof(matrices)); diff --git a/src/gl/renderer/gl_renderstate.cpp b/src/gl/renderer/gl_renderstate.cpp index a5de63896..e585296f6 100644 --- a/src/gl/renderer/gl_renderstate.cpp +++ b/src/gl/renderer/gl_renderstate.cpp @@ -119,7 +119,7 @@ bool FRenderState::ApplyShader() static uint64_t firstFrame = 0; // if firstFrame is not yet initialized, initialize it to current time // if we're going to overflow a float (after ~4.6 hours, or 24 bits), re-init to regain precision - if ((firstFrame == 0) || (screen->FrameTime - firstFrame >= 1 << 24) || level.ShaderStartTime >= firstFrame) + if ((firstFrame == 0) || (screen->FrameTime - firstFrame >= 1<<24) || level.ShaderStartTime >= firstFrame) firstFrame = screen->FrameTime; static const float nulvec[] = { 0.f, 0.f, 0.f, 0.f }; @@ -133,15 +133,31 @@ bool FRenderState::ApplyShader() activeShader->Bind(); } + int fogset = 0; + + if (mFogEnabled) + { + if (mFogEnabled == 2) + { + fogset = -3; // 2D rendering with 'foggy' overlay. + } + else if ((mFogColor & 0xffffff) == 0) + { + fogset = gl_fogmode; + } + else + { + fogset = -gl_fogmode; + } + } + glVertexAttrib4fv(VATTR_COLOR, mColor.vec); glVertexAttrib4fv(VATTR_NORMAL, mNormal.vec); activeShader->muDesaturation.Set(mDesaturation / 255.f); + activeShader->muFogEnabled.Set(fogset); activeShader->muTextureMode.Set(mTextureMode == TM_MODULATE && mTempTM == TM_OPAQUE ? TM_OPAQUE : mTextureMode); - float fds = mLightParms[2]; - if (!mFogEnabled) mLightParms[2] = 0; activeShader->muLightParms.Set(mLightParms); - mLightParms[2] = fds; activeShader->muFogColor.Set(mFogColor); activeShader->muObjectColor.Set(mObjectColor); activeShader->muObjectColor2.Set(mObjectColor2); diff --git a/src/gl/scene/gl_scene.cpp b/src/gl/scene/gl_scene.cpp index 3044d4bd6..1a83521f1 100644 --- a/src/gl/scene/gl_scene.cpp +++ b/src/gl/scene/gl_scene.cpp @@ -375,7 +375,6 @@ void FDrawInfo::DrawEndScene2D(sector_t * viewsector) HWViewpointUniforms vp = VPUniforms; vp.mViewMatrix.loadIdentity(); vp.mProjectionMatrix = vrmode->GetHUDSpriteProjection(); - vp.mFogEnabled = 0; GLRenderer->mViewpoints->SetViewpoint(&vp); glDisable(GL_DEPTH_TEST); glDisable(GL_MULTISAMPLE); diff --git a/src/gl/shaders/gl_shader.cpp b/src/gl/shaders/gl_shader.cpp index 19d30ed47..e0d656038 100644 --- a/src/gl/shaders/gl_shader.cpp +++ b/src/gl/shaders/gl_shader.cpp @@ -68,8 +68,6 @@ bool FShader::Load(const char * name, const char * vert_prog_lump, const char * int uViewHeight; // Software fuzz scaling float uClipHeight; float uClipHeightDirection; - int uFogEnabled; - }; )"; @@ -100,6 +98,7 @@ bool FShader::Load(const char * name, const char * vert_prog_lump, const char * i_data += "#define uFogDensity uLightAttr.b\n"; i_data += "#define uLightFactor uLightAttr.g\n"; i_data += "#define uLightDist uLightAttr.r\n"; + i_data += "uniform int uFogEnabled;\n"; // dynamic lights i_data += "uniform int uLightIndex;\n"; @@ -331,6 +330,7 @@ bool FShader::Load(const char * name, const char * vert_prog_lump, const char * muDesaturation.Init(hShader, "uDesaturationFactor"); + muFogEnabled.Init(hShader, "uFogEnabled"); muTextureMode.Init(hShader, "uTextureMode"); muLightParms.Init(hShader, "uLightAttr"); muClipSplit.Init(hShader, "uClipSplit"); diff --git a/src/gl/shaders/gl_shader.h b/src/gl/shaders/gl_shader.h index 2d1d28f76..46ba3c793 100644 --- a/src/gl/shaders/gl_shader.h +++ b/src/gl/shaders/gl_shader.h @@ -242,6 +242,7 @@ class FShader FName mName; FBufferedUniform1f muDesaturation; + FBufferedUniform1i muFogEnabled; FBufferedUniform1i muTextureMode; FBufferedUniform4f muLightParms; FBufferedUniform2f muClipSplit; diff --git a/src/hwrenderer/scene/hw_drawinfo.cpp b/src/hwrenderer/scene/hw_drawinfo.cpp index bf04aef6c..b5dce2247 100644 --- a/src/hwrenderer/scene/hw_drawinfo.cpp +++ b/src/hwrenderer/scene/hw_drawinfo.cpp @@ -280,6 +280,5 @@ void HWViewpointUniforms::SetDefaults() mGlobVis = (float)R_GetGlobVis(r_viewwindow, r_visibility) / 32.f; mPalLightLevels = static_cast(gl_bandedswlight) | (static_cast(gl_fogmode) << 8); mClipLine.X = -10000000.0f; - mFogEnabled = gl_fogmode; } diff --git a/src/hwrenderer/scene/hw_viewpointuniforms.h b/src/hwrenderer/scene/hw_viewpointuniforms.h index 675ee71eb..27a78aef4 100644 --- a/src/hwrenderer/scene/hw_viewpointuniforms.h +++ b/src/hwrenderer/scene/hw_viewpointuniforms.h @@ -16,7 +16,6 @@ struct HWViewpointUniforms int mViewHeight = 0; float mClipHeight = 0.f; float mClipHeightDirection = 0.f; - int mFogEnabled = 0; void CalcDependencies() { diff --git a/wadsrc/static/shaders/glsl/fogboundary.fp b/wadsrc/static/shaders/glsl/fogboundary.fp index 81880f984..9dcdb4850 100644 --- a/wadsrc/static/shaders/glsl/fogboundary.fp +++ b/wadsrc/static/shaders/glsl/fogboundary.fp @@ -15,7 +15,7 @@ void main() // // calculate fog factor // - if (uFogEnabled == 1) + if (uFogEnabled == -1) { fogdist = pixelpos.w; } diff --git a/wadsrc/static/shaders/glsl/main.fp b/wadsrc/static/shaders/glsl/main.fp index c4caa785e..d5283b26c 100644 --- a/wadsrc/static/shaders/glsl/main.fp +++ b/wadsrc/static/shaders/glsl/main.fp @@ -358,7 +358,7 @@ vec4 getLightColor(Material material, float fogdist, float fogfactor) float newlightlevel = 1.0 - R_DoomLightingEquation(uLightLevel); color.rgb *= newlightlevel; } - else if (uFogColor.rgb == vec3(0.0)) + else if (uFogEnabled > 0) { // brightening around the player for light mode 2 if (fogdist < uLightDist) @@ -421,7 +421,7 @@ vec3 AmbientOcclusionColor() // // calculate fog factor // - if (uFogEnabled == 1) + if (uFogEnabled == -1) { fogdist = pixelpos.w; } @@ -449,17 +449,17 @@ void main() if (frag.a <= uAlphaThreshold) discard; #endif - if (uFogEnabled != 3) // check for special 2D 'fog' mode. + if (uFogEnabled != -3) // check for special 2D 'fog' mode. { float fogdist = 0.0; - float fogfactor = 1.0; + float fogfactor = 0.0; // // calculate fog factor // - if (uFogEnabled != 0 && uFogDensity != 0) + if (uFogEnabled != 0) { - if (uFogEnabled == 1) + if (uFogEnabled == 1 || uFogEnabled == -1) { fogdist = pixelpos.w; } @@ -476,7 +476,7 @@ void main() // // colored fog // - if (uFogColor.rgb != vec3(0.0)) + if (uFogEnabled < 0) { frag = applyFog(frag, fogfactor); } @@ -494,7 +494,7 @@ void main() vec4 cm = (uObjectColor + gray * (uObjectColor2 - uObjectColor)) * 2; frag = vec4(clamp(cm.rgb, 0.0, 1.0), frag.a); } - frag = frag * ProcessLight(material, vColor); + frag = frag * ProcessLight(material, vColor); frag.rgb = frag.rgb + uFogColor.rgb; } FragColor = frag; From 7d4895d9df907ac83803c85762f8b97beb44eb7f Mon Sep 17 00:00:00 2001 From: Kevin Caccamo Date: Sat, 22 Sep 2018 10:24:01 -0400 Subject: [PATCH 13/40] Calculate normals for OBJ models with smooth groups Add smoothGroup member to OBJFace struct, and assign the current smooth group number to it Move face normal calculation code to CalculateNormalFlat Add AddVertFaces method, which initializes and populates the vertFaces array of arrays, which holds references to triangle references per vertex Only initialize and populate vertFaces if the model has missing normals and smooth groups Assign smooth groups to triangle data Add CalculateNormalSmooth method, which calculates the normals for each face the vertex is attached to, depending on whether or not the faces are part of the given smooth group, and averages them out Add OBJTriRef struct, which holds references to triangles on OBJ surfaces Make {agg,cur}SurfFaceCount unsigned ints Change nvec to a value instead of a pointer --- src/r_data/models/models_obj.cpp | 153 ++++++++++++++++++++++++++----- src/r_data/models/models_obj.h | 21 ++++- 2 files changed, 150 insertions(+), 24 deletions(-) diff --git a/src/r_data/models/models_obj.cpp b/src/r_data/models/models_obj.cpp index 4cc5ffa17..d56d994a0 100644 --- a/src/r_data/models/models_obj.cpp +++ b/src/r_data/models/models_obj.cpp @@ -101,8 +101,9 @@ bool FOBJModel::Load(const char* fn, int lumpnum, const char* buffer, int length FTextureID curMtl = FNullTextureID(); OBJSurface *curSurface = nullptr; - int aggSurfFaceCount = 0; - int curSurfFaceCount = 0; + unsigned int aggSurfFaceCount = 0; + unsigned int curSurfFaceCount = 0; + unsigned int curSmoothGroup = 0; while(sc.GetString()) { @@ -186,9 +187,25 @@ bool FOBJModel::Load(const char* fn, int lumpnum, const char* buffer, int length sc.UnGet(); // No 4th side, move back } } + face.smoothGroup = curSmoothGroup; faces.Push(face); curSurfFaceCount += 1; } + else if (sc.Compare("s")) + { + sc.MustGetString(); + if (sc.Compare("off")) + { + curSmoothGroup = 0; + } + else + { + sc.UnGet(); + sc.MustGetNumber(); + curSmoothGroup = sc.Number; + hasSmoothGroups = hasSmoothGroups || curSmoothGroup > 0; + } + } } sc.Close(); @@ -277,11 +294,13 @@ bool FOBJModel::ParseFaceSide(const FString &sideStr, OBJFace &face, int sidx) else { side.normref = -1; + hasMissingNormals = true; } } else { side.normref = -1; + hasMissingNormals = true; } } else @@ -289,6 +308,7 @@ bool FOBJModel::ParseFaceSide(const FString &sideStr, OBJFace &face, int sidx) origIdx = atoi(sideStr.GetChars()); side.vertref = ResolveIndex(origIdx, FaceElement::VertexIndex); side.normref = -1; + hasMissingNormals = true; side.uvref = -1; } face.sides[sidx] = side; @@ -348,6 +368,11 @@ void FOBJModel::BuildVertexBuffer(FModelRenderer *renderer) surfaces[i].vbStart = vbufsize; vbufsize += surfaces[i].numTris * 3; } + // Initialize/populate vertFaces + if (hasMissingNormals && hasSmoothGroups) + { + AddVertFaces(); + } auto vbuf = renderer->CreateVertexBuffer(false,true); SetVertexBuffer(renderer, vbuf); @@ -372,39 +397,40 @@ void FOBJModel::BuildVertexBuffer(FModelRenderer *renderer) FVector3 curVvec = RealignVector(verts[vidx]); FVector2 curUvec = FixUV(uvs[uvidx]); - FVector3 *nvec = nullptr; + FVector3 nvec; mdv->Set(curVvec.X, curVvec.Y, curVvec.Z, curUvec.X, curUvec.Y); if (nidx >= 0 && (unsigned int)nidx < norms.Size()) { - nvec = new FVector3(RealignVector(norms[nidx])); + nvec = RealignVector(norms[nidx]); } else { - // https://www.khronos.org/opengl/wiki/Calculating_a_Surface_Normal - // Find other sides of triangle - auto nextSidx = side + 2; - if (nextSidx >= 3) nextSidx -= 3; - - auto lastSidx = side + 1; - if (lastSidx >= 3) lastSidx -= 3; - - OBJFaceSide &nextSide = surfaces[i].tris[j].sides[nextSidx]; - OBJFaceSide &lastSide = surfaces[i].tris[j].sides[lastSidx]; - - // Cross-multiply the U-vector and V-vector - FVector3 uvec = RealignVector(verts[nextSide.vertref]) - curVvec; - FVector3 vvec = RealignVector(verts[lastSide.vertref]) - curVvec; - - nvec = new FVector3(uvec ^ vvec); + if (surfaces[i].tris[j].smoothGroup == 0) + { + nvec = CalculateNormalFlat(i, j); + } + else + { + nvec = CalculateNormalSmooth(vidx, surfaces[i].tris[j].smoothGroup); + } } - mdv->SetNormal(nvec->X, nvec->Y, nvec->Z); - delete nvec; + mdv->SetNormal(nvec.X, nvec.Y, nvec.Z); } } delete[] surfaces[i].tris; } + + // Destroy vertFaces + if (hasMissingNormals && hasSmoothGroups) + { + for (size_t i = 0; i < verts.Size(); i++) + { + vertFaces[i].Clear(); + } + delete[] vertFaces; + } vbuf->UnlockVertexBuffer(); } @@ -432,6 +458,7 @@ void FOBJModel::ConstructSurfaceTris(OBJSurface &surf) surf.tris[triIdx].sideCount = 3; if (faces[i].sideCount == 3) { + surf.tris[triIdx].smoothGroup = faces[i].smoothGroup; memcpy(surf.tris[triIdx].sides, faces[i].sides, sizeof(OBJFaceSide) * 3); } else if (faces[i].sideCount == 4) // Triangulate face @@ -443,6 +470,7 @@ void FOBJModel::ConstructSurfaceTris(OBJSurface &surf) delete[] triangulated; triIdx += 1; // Filling out two faces } + DPrintf(DMSG_SPAMMY, "Smooth group: %d\n", surf.tris[triIdx].smoothGroup); } } @@ -455,7 +483,9 @@ void FOBJModel::ConstructSurfaceTris(OBJSurface &surf) void FOBJModel::TriangulateQuad(const OBJFace &quad, OBJFace *tris) { tris[0].sideCount = 3; + tris[0].smoothGroup = quad.smoothGroup; tris[1].sideCount = 3; + tris[1].smoothGroup = quad.smoothGroup; int tsidx[2][3] = {{0, 1, 3}, {1, 2, 3}}; @@ -470,6 +500,26 @@ void FOBJModel::TriangulateQuad(const OBJFace &quad, OBJFace *tris) } } +/** + * Add the vertices of all surfaces' triangles to the array of vertex->triangle references + */ +void FOBJModel::AddVertFaces() { + // Initialize and populate vertFaces - this array stores references to triangles per vertex + vertFaces = new TArray[verts.Size()]; + for (size_t i = 0; i < surfaces.Size(); i++) + { + for (size_t j = 0; j < surfaces[i].numTris; j++) + { + OBJTriRef otr = OBJTriRef(i, j); + for (size_t k = 0; k < surfaces[i].tris[j].sideCount; k++) + { + int vidx = surfaces[i].tris[j].sides[k].vertref; + vertFaces[vidx].Push(otr); + } + } + } +} + /** * Re-align a vector to match MD3 alignment * @@ -494,6 +544,65 @@ inline FVector2 FOBJModel::FixUV(FVector2 vecToRealign) return vecToRealign; } +/** + * Calculate the surface normal for a triangle + * + * @param surfIdx The surface index + * @param triIdx The triangle Index + * @return The surface normal vector + */ +FVector3 FOBJModel::CalculateNormalFlat(unsigned int surfIdx, unsigned int triIdx) +{ + // https://www.khronos.org/opengl/wiki/Calculating_a_Surface_Normal + int curVert = surfaces[surfIdx].tris[triIdx].sides[0].vertref; + int nextVert = surfaces[surfIdx].tris[triIdx].sides[2].vertref; + int lastVert = surfaces[surfIdx].tris[triIdx].sides[1].vertref; + + // Cross-multiply the U-vector and V-vector + FVector3 curVvec = RealignVector(verts[curVert]); + FVector3 uvec = RealignVector(verts[nextVert]) - curVvec; + FVector3 vvec = RealignVector(verts[lastVert]) - curVvec; + + return uvec ^ vvec; +} + +/** + * Calculate the surface normal for a triangle + * + * @param otr A reference to the surface, and a triangle within that surface, as an OBJTriRef + * @return The surface normal vector + */ +FVector3 FOBJModel::CalculateNormalFlat(OBJTriRef otr) +{ + return CalculateNormalFlat(otr.surf, otr.tri); +} + +/** + * Calculate the normal of a vertex in a specific smooth group + * + * @param vidx The index of the vertex in the array of vertices + * @param smoothGroup The smooth group number + */ +FVector3 FOBJModel::CalculateNormalSmooth(unsigned int vidx, unsigned int smoothGroup) +{ + unsigned int connectedFaces = 0; + TArray& vTris = vertFaces[vidx]; + + FVector3 vNormal(0,0,0); + for (size_t face = 0; face < vTris.Size(); face++) + { + OBJFace& tri = surfaces[vTris[face].surf].tris[vTris[face].tri]; + if (tri.smoothGroup == smoothGroup) + { + FVector3 fNormal = CalculateNormalFlat(vTris[face]); + connectedFaces += 1; + vNormal += fNormal; + } + } + vNormal /= connectedFaces; + return vNormal; +} + /** * Find the index of the frame with the given name * diff --git a/src/r_data/models/models_obj.h b/src/r_data/models/models_obj.h index d6d68a58e..01a6e03cf 100644 --- a/src/r_data/models/models_obj.h +++ b/src/r_data/models/models_obj.h @@ -30,6 +30,8 @@ class FOBJModel : public FModel { private: const char *newSideSep = "$"; // OBJ side separator is /, which is parsed as a line comment by FScanner if two of them are next to each other. + bool hasMissingNormals; + bool hasSmoothGroups; enum class FaceElement { @@ -38,6 +40,14 @@ private: VNormalIndex }; + struct OBJTriRef + { + unsigned int surf; + unsigned int tri; + OBJTriRef(): surf(0), tri(0) {} + OBJTriRef(unsigned int surf, unsigned int tri): surf(surf), tri(tri) {} + bool operator== (OBJTriRef other) { return surf == other.surf && tri == other.tri; } + }; struct OBJFaceSide { int vertref; @@ -47,7 +57,9 @@ private: struct OBJFace { unsigned int sideCount; + unsigned int smoothGroup; OBJFaceSide sides[4]; + OBJFace(): sideCount(0), smoothGroup(0) {} }; struct OBJSurface // 1 surface per 'usemtl' { @@ -66,16 +78,21 @@ private: TArray faces; TArray surfaces; FScanner sc; + TArray* vertFaces; + int ResolveIndex(int origIndex, FaceElement el); template void ParseVector(TArray &array); bool ParseFaceSide(const FString &side, OBJFace &face, int sidx); void ConstructSurfaceTris(OBJSurface &surf); - int ResolveIndex(int origIndex, FaceElement el); + void AddVertFaces(); void TriangulateQuad(const OBJFace &quad, OBJFace *tris); FVector3 RealignVector(FVector3 vecToRealign); FVector2 FixUV(FVector2 vecToRealign); + FVector3 CalculateNormalFlat(unsigned int surfIdx, unsigned int triIdx); + FVector3 CalculateNormalFlat(OBJTriRef otr); + FVector3 CalculateNormalSmooth(unsigned int vidx, unsigned int smoothGroup); public: - FOBJModel() {} + FOBJModel(): hasMissingNormals(false), hasSmoothGroups(false), vertFaces(nullptr) {} ~FOBJModel(); bool Load(const char* fn, int lumpnum, const char* buffer, int length) override; int FindFrame(const char* name) override; From 525ab8eda385380dab712443ac1b9944fabaa586 Mon Sep 17 00:00:00 2001 From: Kevin Caccamo Date: Sat, 22 Sep 2018 12:49:54 -0400 Subject: [PATCH 14/40] Attempt to fix warnings from VS2017 Win64 compiler --- src/r_data/models/models_obj.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/r_data/models/models_obj.cpp b/src/r_data/models/models_obj.cpp index d56d994a0..035efd4d4 100644 --- a/src/r_data/models/models_obj.cpp +++ b/src/r_data/models/models_obj.cpp @@ -379,9 +379,9 @@ void FOBJModel::BuildVertexBuffer(FModelRenderer *renderer) FModelVertex *vertptr = vbuf->LockVertexBuffer(vbufsize); - for (size_t i = 0; i < surfaces.Size(); i++) + for (unsigned int i = 0; i < surfaces.Size(); i++) { - for (size_t j = 0; j < surfaces[i].numTris; j++) + for (unsigned int j = 0; j < surfaces[i].numTris; j++) { for (size_t side = 0; side < 3; side++) { @@ -506,9 +506,9 @@ void FOBJModel::TriangulateQuad(const OBJFace &quad, OBJFace *tris) void FOBJModel::AddVertFaces() { // Initialize and populate vertFaces - this array stores references to triangles per vertex vertFaces = new TArray[verts.Size()]; - for (size_t i = 0; i < surfaces.Size(); i++) + for (unsigned int i = 0; i < surfaces.Size(); i++) { - for (size_t j = 0; j < surfaces[i].numTris; j++) + for (unsigned int j = 0; j < surfaces[i].numTris; j++) { OBJTriRef otr = OBJTriRef(i, j); for (size_t k = 0; k < surfaces[i].tris[j].sideCount; k++) @@ -599,7 +599,7 @@ FVector3 FOBJModel::CalculateNormalSmooth(unsigned int vidx, unsigned int smooth vNormal += fNormal; } } - vNormal /= connectedFaces; + vNormal /= (float)connectedFaces; return vNormal; } From 3b8b312fae669c60e78617ab8dd2f6f5b4bf7ef7 Mon Sep 17 00:00:00 2001 From: Magnus Norddahl Date: Sun, 23 Sep 2018 17:58:17 +0200 Subject: [PATCH 15/40] - clamp the software light to never get brighter than the initial light level --- wadsrc/static/shaders/glsl/main.fp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/wadsrc/static/shaders/glsl/main.fp b/wadsrc/static/shaders/glsl/main.fp index c4caa785e..5848058f3 100644 --- a/wadsrc/static/shaders/glsl/main.fp +++ b/wadsrc/static/shaders/glsl/main.fp @@ -147,7 +147,7 @@ float R_DoomLightingEquation(float light) lightscale = shade - vis; // Result is the normalized colormap index (0 bright .. 1 dark) - return clamp(lightscale, 0.0, 31.0 / 32.0); + return clamp(lightscale, 1.0 - light, 31.0 / 32.0); } //=========================================================================== From a9b25242cdfe28804ff77d28fa12dae103bd401d Mon Sep 17 00:00:00 2001 From: Marisa Kirisame Date: Sun, 23 Sep 2018 23:34:20 +0200 Subject: [PATCH 16/40] Hotfix: The output from CheckReplacement no longer permanently overrides an actor's replacement. --- src/info.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/info.cpp b/src/info.cpp index 0ff3928cd..ffae037f7 100644 --- a/src/info.cpp +++ b/src/info.cpp @@ -438,6 +438,7 @@ PClassActor *PClassActor::GetReplacement(bool lookskill) } // The Replacement field is temporarily NULLed to prevent // potential infinite recursion. + PClassActor *oldrep = ActorInfo()->Replacement; ActorInfo()->Replacement = nullptr; PClassActor *rep = Replacement; // Handle skill-based replacement here. It has precedence on DECORATE replacement @@ -451,7 +452,7 @@ PClassActor *PClassActor::GetReplacement(bool lookskill) // Skill replacements are not recursive, contrarily to DECORATE replacements rep = rep->GetReplacement(false); // Reset the temporarily NULLed field - ActorInfo()->Replacement = Replacement; + ActorInfo()->Replacement = oldrep; return rep; } From 670c86cd470aa5f5be9819378a86843b15cc72ba Mon Sep 17 00:00:00 2001 From: Marisa Kirisame Date: Tue, 25 Sep 2018 18:48:00 +0200 Subject: [PATCH 17/40] Fix a major oversight that caused UE1 models to use the normals of the first frame for all frames. --- src/r_data/models/models_ue1.cpp | 23 ++++++++++++++--------- src/r_data/models/models_ue1.h | 2 +- 2 files changed, 15 insertions(+), 10 deletions(-) diff --git a/src/r_data/models/models_ue1.cpp b/src/r_data/models/models_ue1.cpp index f4392e54b..838be2af7 100644 --- a/src/r_data/models/models_ue1.cpp +++ b/src/r_data/models/models_ue1.cpp @@ -123,11 +123,14 @@ void FUE1Model::LoadGeometry() // unpack coords for ( int j=0; j<3; j++ ) Poly.C[j] = FVector2(dpolys[i].uv[j][0]/255.f,dpolys[i].uv[j][1]/255.f); - // compute facet normal - FVector3 dir[2]; - dir[0] = verts[Poly.V[1]].Pos-verts[Poly.V[0]].Pos; - dir[1] = verts[Poly.V[2]].Pos-verts[Poly.V[0]].Pos; - Poly.Normal = dir[0]^dir[1]; + // compute facet normals + for ( int j=0; jSet(V.Pos.X,V.Pos.Y,V.Pos.Z,C.X,C.Y); if ( groups[j].type&PT_Curvy ) // use facet normal { - vert->SetNormal(polys[groups[j].P[k]].Normal.X, - polys[groups[j].P[k]].Normal.Y, - polys[groups[j].P[k]].Normal.Z); + vert->SetNormal(polys[groups[j].P[k]].Normals[i].X, + polys[groups[j].P[k]].Normals[i].Y, + polys[groups[j].P[k]].Normals[i].Z); } else vert->SetNormal(V.Normal.X,V.Normal.Y,V.Normal.Z); } diff --git a/src/r_data/models/models_ue1.h b/src/r_data/models/models_ue1.h index fb67758f7..be9f57a10 100644 --- a/src/r_data/models/models_ue1.h +++ b/src/r_data/models/models_ue1.h @@ -89,7 +89,7 @@ private: { int V[3]; FVector2 C[3]; - FVector3 Normal; + TArray Normals; }; struct UE1Group { From c8852b8fea9291aa6287280a5d0491e576e4ceac Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Sat, 29 Sep 2018 13:23:40 +0200 Subject: [PATCH 18/40] - enabled the linear shadowmap filter. Although this doesn't look as good as the PCF version it is a lot less calculation intensive and therefore more suitable for weaker hardware. It also tends to bleed through walls a lot less. --- src/gl/shaders/gl_shader.cpp | 1 + src/hwrenderer/scene/hw_drawinfo.cpp | 1 + src/hwrenderer/scene/hw_viewpointuniforms.h | 1 + src/hwrenderer/utility/hw_cvars.cpp | 2 ++ src/hwrenderer/utility/hw_cvars.h | 2 ++ wadsrc/static/shaders/glsl/main.fp | 37 +++++++++++---------- 6 files changed, 26 insertions(+), 18 deletions(-) diff --git a/src/gl/shaders/gl_shader.cpp b/src/gl/shaders/gl_shader.cpp index e0d656038..564365341 100644 --- a/src/gl/shaders/gl_shader.cpp +++ b/src/gl/shaders/gl_shader.cpp @@ -68,6 +68,7 @@ bool FShader::Load(const char * name, const char * vert_prog_lump, const char * int uViewHeight; // Software fuzz scaling float uClipHeight; float uClipHeightDirection; + int uShadowmapFilter; }; )"; diff --git a/src/hwrenderer/scene/hw_drawinfo.cpp b/src/hwrenderer/scene/hw_drawinfo.cpp index b5dce2247..c72f510ae 100644 --- a/src/hwrenderer/scene/hw_drawinfo.cpp +++ b/src/hwrenderer/scene/hw_drawinfo.cpp @@ -280,5 +280,6 @@ void HWViewpointUniforms::SetDefaults() mGlobVis = (float)R_GetGlobVis(r_viewwindow, r_visibility) / 32.f; mPalLightLevels = static_cast(gl_bandedswlight) | (static_cast(gl_fogmode) << 8); mClipLine.X = -10000000.0f; + mShadowmapFilter = gl_shadowmap_filter; } diff --git a/src/hwrenderer/scene/hw_viewpointuniforms.h b/src/hwrenderer/scene/hw_viewpointuniforms.h index 27a78aef4..1bd57faa0 100644 --- a/src/hwrenderer/scene/hw_viewpointuniforms.h +++ b/src/hwrenderer/scene/hw_viewpointuniforms.h @@ -16,6 +16,7 @@ struct HWViewpointUniforms int mViewHeight = 0; float mClipHeight = 0.f; float mClipHeightDirection = 0.f; + int mShadowmapFilter = 1; void CalcDependencies() { diff --git a/src/hwrenderer/utility/hw_cvars.cpp b/src/hwrenderer/utility/hw_cvars.cpp index c849e62d5..563bdd9f8 100644 --- a/src/hwrenderer/utility/hw_cvars.cpp +++ b/src/hwrenderer/utility/hw_cvars.cpp @@ -132,3 +132,5 @@ CUSTOM_CVAR(Int, gl_fuzztype, 0, CVAR_ARCHIVE) { if (self < 0 || self > 8) self = 0; } + +CVAR(Bool, gl_shadowmap_filter, 1, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) \ No newline at end of file diff --git a/src/hwrenderer/utility/hw_cvars.h b/src/hwrenderer/utility/hw_cvars.h index 440265930..eb1aafe44 100644 --- a/src/hwrenderer/utility/hw_cvars.h +++ b/src/hwrenderer/utility/hw_cvars.h @@ -69,3 +69,5 @@ EXTERN_CVAR(Bool, gl_billboard_faces_camera) EXTERN_CVAR(Bool, gl_billboard_particles) EXTERN_CVAR(Int, gl_enhanced_nv_stealth) EXTERN_CVAR(Int, gl_fuzztype) + +EXTERN_CVAR(Bool, gl_shadowmap_filter) \ No newline at end of file diff --git a/wadsrc/static/shaders/glsl/main.fp b/wadsrc/static/shaders/glsl/main.fp index 7a6c98436..714f63ed2 100644 --- a/wadsrc/static/shaders/glsl/main.fp +++ b/wadsrc/static/shaders/glsl/main.fp @@ -218,9 +218,6 @@ float sampleShadowmapLinear(vec2 dir, float v) #define PCF_FILTER_STEP_COUNT 3 #define PCF_COUNT (PCF_FILTER_STEP_COUNT * 2 + 1) -// #define USE_LINEAR_SHADOW_FILTER -#define USE_PCF_SHADOW_FILTER 1 - float shadowmapAttenuation(vec4 lightpos, float shadowIndex) { if (shadowIndex >= 1024.0) @@ -235,23 +232,27 @@ float shadowmapAttenuation(vec4 lightpos, float shadowIndex) vec2 dir = ray / length; -#if defined(USE_LINEAR_SHADOW_FILTER) - ray -= dir * 6.0; // Shadow acne margin - return sampleShadowmapLinear(ray, v); -#elif defined(USE_PCF_SHADOW_FILTER) - ray -= dir * 2.0; // Shadow acne margin - dir = dir * min(length / 50.0, 1.0); // avoid sampling behind light - - vec2 normal = vec2(-dir.y, dir.x); - vec2 bias = dir * 10.0; - - float sum = 0.0; - for (float x = -PCF_FILTER_STEP_COUNT; x <= PCF_FILTER_STEP_COUNT; x++) + if (uShadowmapFilter == 0) { - sum += sampleShadowmap(ray + normal * x - bias * abs(x), v); + ray -= dir * 2.0; // Shadow acne margin + return sampleShadowmapLinear(ray, v); } - return sum / PCF_COUNT; -#else // nearest shadow filter + else + { + ray -= dir * 2.0; // Shadow acne margin + dir = dir * min(length / 50.0, 1.0); // avoid sampling behind light + + vec2 normal = vec2(-dir.y, dir.x); + vec2 bias = dir * 10.0; + + float sum = 0.0; + for (float x = -PCF_FILTER_STEP_COUNT; x <= PCF_FILTER_STEP_COUNT; x++) + { + sum += sampleShadowmap(ray + normal * x - bias * abs(x), v); + } + return sum / PCF_COUNT; + } +#if 0 // nearest shadow filter (not used) ray -= dir * 6.0; // Shadow acne margin return sampleShadowmap(ray, v); #endif From 35bb2d30792e9b2d6623271a35a4ad16f7168957 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Sat, 29 Sep 2018 13:31:13 +0200 Subject: [PATCH 19/40] - add new option to menu --- wadsrc/static/language.enu | 1 + wadsrc/static/menudef.txt | 8 ++++++++ 2 files changed, 9 insertions(+) diff --git a/wadsrc/static/language.enu b/wadsrc/static/language.enu index 3e20e1e03..065d547be 100644 --- a/wadsrc/static/language.enu +++ b/wadsrc/static/language.enu @@ -2787,6 +2787,7 @@ GLLIGHTMNU_LIGHTSPRITES = "Lights affect sprites"; GLLIGHTMNU_LIGHTPARTICLES = "Lights affect particles"; GLLIGHTMNU_LIGHTSHADOWMAP = "Light shadowmaps"; GLLIGHTMNU_LIGHTSHADOWMAPQUALITY = "Shadowmap quality"; +GLLIGHTMNU_LIGHTSHADOWMAPFILTER = "Shadowmap filter"; // OpenGL Preferences GLPREFMNU_TITLE = "OPENGL PREFERENCES"; diff --git a/wadsrc/static/menudef.txt b/wadsrc/static/menudef.txt index d8d3c9a4c..9e3f801cc 100644 --- a/wadsrc/static/menudef.txt +++ b/wadsrc/static/menudef.txt @@ -2225,6 +2225,13 @@ OptionValue ShadowMapQuality 1024, "1024" } +OptionValue ShadowMapFilter +{ + 0, "$OPTVAL_LINEAR" + 1, "PCF" +} + + OptionMenu "GLTextureGLOptions" protected { Title "$GLTEXMNU_TITLE" @@ -2259,6 +2266,7 @@ OptionMenu "GLLightOptions" protected Option "$GLLIGHTMNU_LIGHTPARTICLES", gl_light_particles, "YesNo" Option "$GLLIGHTMNU_LIGHTSHADOWMAP", gl_light_shadowmap, "YesNo" Option "$GLLIGHTMNU_LIGHTSHADOWMAPQUALITY", gl_shadowmap_quality, "ShadowMapQuality" + Option "$GLLIGHTMNU_LIGHTSHADOWMAPFILTER", gl_shadowmap_filter, "ShadowMapFilter" } OptionMenu "OpenGLOptions" protected From 797f88a6c8e81a38238158cb5e1509265cd7b198 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Wed, 3 Oct 2018 13:45:54 +0200 Subject: [PATCH 20/40] - some tweaking of shadowmap filter setting to allow changing the PCF filter's number of samplings. --- src/hwrenderer/utility/hw_cvars.cpp | 4 +++- src/hwrenderer/utility/hw_cvars.h | 2 +- wadsrc/static/shaders/glsl/main.fp | 13 ++++++------- 3 files changed, 10 insertions(+), 9 deletions(-) diff --git a/src/hwrenderer/utility/hw_cvars.cpp b/src/hwrenderer/utility/hw_cvars.cpp index 563bdd9f8..7188a96b3 100644 --- a/src/hwrenderer/utility/hw_cvars.cpp +++ b/src/hwrenderer/utility/hw_cvars.cpp @@ -133,4 +133,6 @@ CUSTOM_CVAR(Int, gl_fuzztype, 0, CVAR_ARCHIVE) if (self < 0 || self > 8) self = 0; } -CVAR(Bool, gl_shadowmap_filter, 1, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) \ No newline at end of file +CUSTOM_CVAR(Int, gl_shadowmap_filter, 1, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) +{ + if (self < 0 || self > 8) self = 1; \ No newline at end of file diff --git a/src/hwrenderer/utility/hw_cvars.h b/src/hwrenderer/utility/hw_cvars.h index eb1aafe44..8e4ebbca8 100644 --- a/src/hwrenderer/utility/hw_cvars.h +++ b/src/hwrenderer/utility/hw_cvars.h @@ -70,4 +70,4 @@ EXTERN_CVAR(Bool, gl_billboard_particles) EXTERN_CVAR(Int, gl_enhanced_nv_stealth) EXTERN_CVAR(Int, gl_fuzztype) -EXTERN_CVAR(Bool, gl_shadowmap_filter) \ No newline at end of file +EXTERN_CVAR(Int, gl_shadowmap_filter) \ No newline at end of file diff --git a/wadsrc/static/shaders/glsl/main.fp b/wadsrc/static/shaders/glsl/main.fp index 714f63ed2..c78bb0e02 100644 --- a/wadsrc/static/shaders/glsl/main.fp +++ b/wadsrc/static/shaders/glsl/main.fp @@ -215,9 +215,6 @@ float sampleShadowmapLinear(vec2 dir, float v) // //=========================================================================== -#define PCF_FILTER_STEP_COUNT 3 -#define PCF_COUNT (PCF_FILTER_STEP_COUNT * 2 + 1) - float shadowmapAttenuation(vec4 lightpos, float shadowIndex) { if (shadowIndex >= 1024.0) @@ -232,7 +229,7 @@ float shadowmapAttenuation(vec4 lightpos, float shadowIndex) vec2 dir = ray / length; - if (uShadowmapFilter == 0) + if (uShadowmapFilter <= 0) { ray -= dir * 2.0; // Shadow acne margin return sampleShadowmapLinear(ray, v); @@ -246,11 +243,13 @@ float shadowmapAttenuation(vec4 lightpos, float shadowIndex) vec2 bias = dir * 10.0; float sum = 0.0; - for (float x = -PCF_FILTER_STEP_COUNT; x <= PCF_FILTER_STEP_COUNT; x++) + float step_count = ((uShadowmapFilter - 1) / 2.); + + for (float x = -step_count; x <= step_count; x++) { - sum += sampleShadowmap(ray + normal * x - bias * abs(x), v); + sum += sampleShadowmap(ray + normal * x /*- bias * abs(x)*/, v); } - return sum / PCF_COUNT; + return sum / uShadowmapFilter; } #if 0 // nearest shadow filter (not used) ray -= dir * 6.0; // Shadow acne margin From 92e419d65c605546b93c018b2255e11790e170c5 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Wed, 3 Oct 2018 13:46:25 +0200 Subject: [PATCH 21/40] - toned down the intensity of the dynamic lights for Doom's torches. --- wadsrc_lights/static/filter/doom.doom1/gldefs.txt | 12 ++++++------ wadsrc_lights/static/filter/doom.doom2/gldefs.txt | 12 ++++++------ 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/wadsrc_lights/static/filter/doom.doom1/gldefs.txt b/wadsrc_lights/static/filter/doom.doom1/gldefs.txt index 83c30369d..61536f97f 100644 --- a/wadsrc_lights/static/filter/doom.doom1/gldefs.txt +++ b/wadsrc_lights/static/filter/doom.doom1/gldefs.txt @@ -384,7 +384,7 @@ object TechLamp // Tall red torch flickerlight2 BIGREDTORCH { - color 1.0 0.5 0.2 + color 0.7 0.35 0.14 size 90 secondarySize 99 interval 0.1 @@ -400,7 +400,7 @@ object RedTorch // Tall green torch flickerlight2 BIGGREENTORCH { - color 0.3 1.0 0.3 + color 0.2 0.7 0.2 size 90 secondarySize 99 interval 0.1 @@ -416,7 +416,7 @@ object GreenTorch // Tall blue torch flickerlight2 BIGBLUETORCH { - color 0.3 0.3 1.0 + color 0.2 0.2 0.7 size 90 secondarySize 99 interval 0.1 @@ -432,7 +432,7 @@ object BlueTorch // Small red torch flickerlight2 SMALLREDTORCH { - color 1.0 0.5 0.2 + color 0.7 0.35 0.14 size 72 secondarySize 81 interval 0.1 @@ -448,7 +448,7 @@ object ShortRedTorch // Small green torch flickerlight2 SMALLGREENTORCH { - color 0.3 1.0 0.3 + color 0.2 0.7 0.2 size 72 secondarySize 81 interval 0.1 @@ -464,7 +464,7 @@ object ShortGreenTorch // Small blue torch flickerlight2 SMALLBLUETORCH { - color 0.3 0.3 1.0 + color 0.2 0.2 0.7 size 72 secondarySize 81 interval 0.1 diff --git a/wadsrc_lights/static/filter/doom.doom2/gldefs.txt b/wadsrc_lights/static/filter/doom.doom2/gldefs.txt index 83c30369d..61536f97f 100644 --- a/wadsrc_lights/static/filter/doom.doom2/gldefs.txt +++ b/wadsrc_lights/static/filter/doom.doom2/gldefs.txt @@ -384,7 +384,7 @@ object TechLamp // Tall red torch flickerlight2 BIGREDTORCH { - color 1.0 0.5 0.2 + color 0.7 0.35 0.14 size 90 secondarySize 99 interval 0.1 @@ -400,7 +400,7 @@ object RedTorch // Tall green torch flickerlight2 BIGGREENTORCH { - color 0.3 1.0 0.3 + color 0.2 0.7 0.2 size 90 secondarySize 99 interval 0.1 @@ -416,7 +416,7 @@ object GreenTorch // Tall blue torch flickerlight2 BIGBLUETORCH { - color 0.3 0.3 1.0 + color 0.2 0.2 0.7 size 90 secondarySize 99 interval 0.1 @@ -432,7 +432,7 @@ object BlueTorch // Small red torch flickerlight2 SMALLREDTORCH { - color 1.0 0.5 0.2 + color 0.7 0.35 0.14 size 72 secondarySize 81 interval 0.1 @@ -448,7 +448,7 @@ object ShortRedTorch // Small green torch flickerlight2 SMALLGREENTORCH { - color 0.3 1.0 0.3 + color 0.2 0.7 0.2 size 72 secondarySize 81 interval 0.1 @@ -464,7 +464,7 @@ object ShortGreenTorch // Small blue torch flickerlight2 SMALLBLUETORCH { - color 0.3 0.3 1.0 + color 0.2 0.2 0.7 size 72 secondarySize 81 interval 0.1 From 51dfc82153415169589e0f38b9c523874e473cae Mon Sep 17 00:00:00 2001 From: Rachael Alexanderson Date: Wed, 3 Oct 2018 09:39:32 -0400 Subject: [PATCH 22/40] - fix missing curly brace --- src/hwrenderer/utility/hw_cvars.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/hwrenderer/utility/hw_cvars.cpp b/src/hwrenderer/utility/hw_cvars.cpp index 7188a96b3..f0a0f8c83 100644 --- a/src/hwrenderer/utility/hw_cvars.cpp +++ b/src/hwrenderer/utility/hw_cvars.cpp @@ -135,4 +135,5 @@ CUSTOM_CVAR(Int, gl_fuzztype, 0, CVAR_ARCHIVE) CUSTOM_CVAR(Int, gl_shadowmap_filter, 1, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) { - if (self < 0 || self > 8) self = 1; \ No newline at end of file + if (self < 0 || self > 8) self = 1; +} From 36946a47febba47fd3c53e601bcf41476440da0c Mon Sep 17 00:00:00 2001 From: Magnus Norddahl Date: Thu, 4 Oct 2018 00:46:17 +0200 Subject: [PATCH 23/40] - fix diagonal linear shadow map artifact --- wadsrc/static/shaders/glsl/main.fp | 10 ++++++---- wadsrc/static/shaders/glsl/shadowmap.fp | 11 ++++++----- 2 files changed, 12 insertions(+), 9 deletions(-) diff --git a/wadsrc/static/shaders/glsl/main.fp b/wadsrc/static/shaders/glsl/main.fp index c78bb0e02..219e01581 100644 --- a/wadsrc/static/shaders/glsl/main.fp +++ b/wadsrc/static/shaders/glsl/main.fp @@ -162,17 +162,19 @@ float shadowDirToU(vec2 dir) { if (abs(dir.x) > abs(dir.y)) { + float v = dir.y / dir.x * 0.125; if (dir.x >= 0.0) - return dir.y / dir.x * 0.125 + (0.25 + 0.125); + return (0.25 + 0.125) - v; else - return dir.y / dir.x * 0.125 + (0.75 + 0.125); + return (0.75 + 0.125) - v; } else { + float v = dir.x / dir.y * 0.125; if (dir.y >= 0.0) - return dir.x / dir.y * 0.125 + 0.125; + return 0.125 + v; else - return dir.x / dir.y * 0.125 + (0.50 + 0.125); + return (0.50 + 0.125) + v; } } diff --git a/wadsrc/static/shaders/glsl/shadowmap.fp b/wadsrc/static/shaders/glsl/shadowmap.fp index fb2257b92..5e5438075 100644 --- a/wadsrc/static/shaders/glsl/shadowmap.fp +++ b/wadsrc/static/shaders/glsl/shadowmap.fp @@ -140,12 +140,13 @@ void main() if (radius > 0.0) { vec2 pixelpos; - switch (int(gl_FragCoord.x) / int(ShadowmapQuality/4.0)) + float u = gl_FragCoord.x / ShadowmapQuality * 4.0; + switch (int(u)) { - case 0: pixelpos = vec2((gl_FragCoord.x - float(ShadowmapQuality/8.0)) / float(ShadowmapQuality/8.0), 1.0); break; - case 1: pixelpos = vec2(1.0, (gl_FragCoord.x - float(ShadowmapQuality/4.0 + ShadowmapQuality/8.0)) / float(ShadowmapQuality/8.0)); break; - case 2: pixelpos = vec2(-(gl_FragCoord.x - float(ShadowmapQuality/2.0 + ShadowmapQuality/8.0)) / float(ShadowmapQuality/8.0), -1.0); break; - case 3: pixelpos = vec2(-1.0, -(gl_FragCoord.x - float(ShadowmapQuality*3.0/4.0 + ShadowmapQuality/8.0)) / float(ShadowmapQuality/8.0)); break; + case 0: pixelpos = vec2(u * 2.0 - 1.0, 1.0); break; + case 1: pixelpos = vec2(1.0, 1.0 - (u - 1.0) * 2.0); break; + case 2: pixelpos = vec2(1.0 - (u - 2.0) * 2.0, -1.0); break; + case 3: pixelpos = vec2(-1.0, (u - 3.0) * 2.0 - 1.0); break; } pixelpos = lightpos + pixelpos * radius; From d65d462268a23a25f682256ee40ee936aff20d38 Mon Sep 17 00:00:00 2001 From: Magnus Norddahl Date: Thu, 4 Oct 2018 01:29:25 +0200 Subject: [PATCH 24/40] - add some comments to shadowmap.fp --- wadsrc/static/shaders/glsl/shadowmap.fp | 55 ++++++++++++++++++------- 1 file changed, 41 insertions(+), 14 deletions(-) diff --git a/wadsrc/static/shaders/glsl/shadowmap.fp b/wadsrc/static/shaders/glsl/shadowmap.fp index 5e5438075..4bc507c3d 100644 --- a/wadsrc/static/shaders/glsl/shadowmap.fp +++ b/wadsrc/static/shaders/glsl/shadowmap.fp @@ -2,20 +2,22 @@ in vec2 TexCoord; layout(location=0) out vec4 FragColor; +// A node in an AABB binary tree with lines stored in the leaf nodes struct GPUNode { - vec2 aabb_min; - vec2 aabb_max; - int left; - int right; - int line_index; - int padding; + vec2 aabb_min; // Min xy values for the axis-aligned box containing the node and its subtree + vec2 aabb_max; // Max xy values + int left; // Left subnode index + int right; // Right subnode index + int line_index; // Line index if it is a leaf node, otherwise -1 + int padding; // Unused - maintains 16 byte alignment }; +// 2D line segment, referenced by leaf nodes struct GPULine { - vec2 pos; - vec2 delta; + vec2 pos; // Line start position + vec2 delta; // Line end position - line start position }; layout(std430, binding = 2) buffer LightNodes @@ -33,6 +35,7 @@ layout(std430, binding = 4) buffer LightList vec4 lights[]; }; +// Overlap test between line segment and axis-aligned bounding box. Returns true if they overlap. bool overlapRayAABB(vec2 ray_start2d, vec2 ray_end2d, vec2 aabb_min2d, vec2 aabb_max2d) { // To do: simplify test to use a 2D test @@ -60,6 +63,8 @@ bool overlapRayAABB(vec2 ray_start2d, vec2 ray_end2d, vec2 aabb_min2d, vec2 aabb return true; // overlap; } +// Intersection test between two line segments. +// Returns the intersection point as a value between 0-1 on the ray line segment. 1.0 if there was no hit. float intersectRayLine(vec2 ray_start, vec2 ray_end, int line_index, vec2 raydelta, float rayd, float raydist2) { const float epsilon = 0.0000001; @@ -82,11 +87,14 @@ float intersectRayLine(vec2 ray_start, vec2 ray_end, int line_index, vec2 raydel return 1.0; } +// Returns true if an AABB tree node is a leaf node. Leaf nodes contains a line. bool isLeaf(int node_index) { return nodes[node_index].line_index != -1; } +// Perform ray intersection test between the ray line segment and all the lines in the AABB binary tree. +// Returns the intersection point as a value between 0-1 on the ray line segment. 1.0 if there was no hit. float rayTest(vec2 ray_start, vec2 ray_end) { vec2 raydelta = ray_end - ray_start; @@ -98,6 +106,9 @@ float rayTest(vec2 ray_start, vec2 ray_end) float t = 1.0; + // Walk the AABB binary tree searching for nodes touching the ray line segment's AABB box. + // When it reaches a leaf node, use a line segment intersection test to see if we got a hit. + int stack[16]; int stack_pos = 1; stack[0] = nodes.length() - 1; @@ -131,6 +142,8 @@ float rayTest(vec2 ray_start, vec2 ray_end) void main() { + // Find the light that belongs to this texel in the shadowmap texture we output to: + int lightIndex = int(gl_FragCoord.y); vec4 light = lights[lightIndex]; @@ -139,18 +152,32 @@ void main() if (radius > 0.0) { - vec2 pixelpos; + // We found an active light. Calculate the ray direction for the texel. + // + // The texels are laid out so that there are four projections: + // + // * top-left to top-right + // * top-right to bottom-right + // * bottom-right to bottom-left + // * bottom-left to top-left + // + vec2 raydir; float u = gl_FragCoord.x / ShadowmapQuality * 4.0; switch (int(u)) { - case 0: pixelpos = vec2(u * 2.0 - 1.0, 1.0); break; - case 1: pixelpos = vec2(1.0, 1.0 - (u - 1.0) * 2.0); break; - case 2: pixelpos = vec2(1.0 - (u - 2.0) * 2.0, -1.0); break; - case 3: pixelpos = vec2(-1.0, (u - 3.0) * 2.0 - 1.0); break; + case 0: raydir = vec2(u * 2.0 - 1.0, 1.0); break; + case 1: raydir = vec2(1.0, 1.0 - (u - 1.0) * 2.0); break; + case 2: raydir = vec2(1.0 - (u - 2.0) * 2.0, -1.0); break; + case 3: raydir = vec2(-1.0, (u - 3.0) * 2.0 - 1.0); break; } - pixelpos = lightpos + pixelpos * radius; + // Find the position for the ray starting at the light position and travelling until light contribution is zero: + vec2 pixelpos = lightpos + raydir * radius; + + // Check if we hit any line between the light and the end position: float t = rayTest(lightpos, pixelpos); + + // Calculate the square distance for the hit, if any: vec2 delta = (pixelpos - lightpos) * t; float dist2 = dot(delta, delta); From 30c3f4f5978017bde404509ee2ca82102fd3b23b Mon Sep 17 00:00:00 2001 From: "alexey.lysiuk" Date: Fri, 3 Aug 2018 17:25:39 +0300 Subject: [PATCH 25/40] - update xBRZ upscaler to version 1.6 Fixed build with all suported toolchains thanks to incomplete implementation of C++14 in MSVC 2015 and GCC 4.9 Removed obsolete header comments and support for C++98 Disabled Windows only debug features https://sourceforge.net/projects/xbrz/ https://sourceforge.net/projects/xbrz/files/xBRZ/xBRZ_1.6.zip --- src/textures/hires/hqresize.cpp | 4 +- src/textures/hires/xbr/xbrz.cpp | 473 +++++++++++++++------------ src/textures/hires/xbr/xbrz.h | 63 ++-- src/textures/hires/xbr/xbrz_config.h | 35 +- src/textures/hires/xbr/xbrz_tools.h | 268 +++++++++++++++ 5 files changed, 559 insertions(+), 284 deletions(-) create mode 100644 src/textures/hires/xbr/xbrz_tools.h diff --git a/src/textures/hires/hqresize.cpp b/src/textures/hires/hqresize.cpp index ccd025856..a898b9fb6 100644 --- a/src/textures/hires/hqresize.cpp +++ b/src/textures/hires/hqresize.cpp @@ -285,13 +285,13 @@ static unsigned char *xbrzHelper( void (*xbrzFunction) ( size_t, const uint32_t* parallel_for(inHeight, thresholdHeight, [=](int sliceY) { xbrzFunction(N, reinterpret_cast(inputBuffer), reinterpret_cast(newBuffer), - inWidth, inHeight, xbrz::ARGB, xbrz::ScalerCfg(), sliceY, sliceY + thresholdHeight); + inWidth, inHeight, xbrz::ColorFormat::ARGB, xbrz::ScalerCfg(), sliceY, sliceY + thresholdHeight); }); } else { xbrzFunction(N, reinterpret_cast(inputBuffer), reinterpret_cast(newBuffer), - inWidth, inHeight, xbrz::ARGB, xbrz::ScalerCfg(), 0, std::numeric_limits::max()); + inWidth, inHeight, xbrz::ColorFormat::ARGB, xbrz::ScalerCfg(), 0, std::numeric_limits::max()); } delete[] inputBuffer; diff --git a/src/textures/hires/xbr/xbrz.cpp b/src/textures/hires/xbr/xbrz.cpp index b26d4bbd3..9f480bf00 100644 --- a/src/textures/hires/xbr/xbrz.cpp +++ b/src/textures/hires/xbr/xbrz.cpp @@ -1,63 +1,41 @@ // **************************************************************************** -// * This file is part of the HqMAME project. It is distributed under * -// * GNU General Public License: http://www.gnu.org/licenses/gpl-3.0 * +// * This file is part of the xBRZ project. It is distributed under * +// * GNU General Public License: https://www.gnu.org/licenses/gpl-3.0 * // * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * // * * // * Additionally and as a special exception, the author gives permission * -// * to link the code of this program with the MAME library (or with modified * -// * versions of MAME that use the same license as MAME), and distribute * -// * linked combinations including the two. You must obey the GNU General * -// * Public License in all respects for all of the code used other than MAME. * +// * to link the code of this program with the following libraries * +// * (or with modified versions that use the same licenses), and distribute * +// * linked combinations including the two: MAME, FreeFileSync, Snes9x, ePSXe * +// * You must obey the GNU General Public License in all respects for all of * +// * the code used other than MAME, FreeFileSync, Snes9x, ePSXe. * // * If you modify this file, you may extend this exception to your version * // * of the file, but you are not obligated to do so. If you do not wish to * // * do so, delete this exception statement from your version. * -// * * -// * An explicit permission was granted to use xBRZ in combination with ZDoom * -// * and derived projects as long as it is used for non-commercial purposes. * -// * * -// * Backported to C++98 by Alexey Lysiuk * // **************************************************************************** #include "xbrz.h" - #include -#include -#include #include +#include +#include //std::sqrt +#include "xbrz_tools.h" + +using namespace xbrz; -#if __cplusplus <= 199711 -#define static_assert(VAL, MSG) static_assertion(); -template struct static_assertion; -template<> struct static_assertion {}; -#endif // __cplusplus <= 199711 namespace { -template inline -unsigned char getByte(uint32_t val) { return static_cast((val >> (8 * N)) & 0xff); } - -inline unsigned char getAlpha(uint32_t pix) { return getByte<3>(pix); } -inline unsigned char getRed (uint32_t pix) { return getByte<2>(pix); } -inline unsigned char getGreen(uint32_t pix) { return getByte<1>(pix); } -inline unsigned char getBlue (uint32_t pix) { return getByte<0>(pix); } - -inline uint32_t makePixel( unsigned char r, unsigned char g, unsigned char b) { return (r << 16) | (g << 8) | b; } -inline uint32_t makePixel(unsigned char a, unsigned char r, unsigned char g, unsigned char b) { return (a << 24) | (r << 16) | (g << 8) | b; } - - template inline uint32_t gradientRGB(uint32_t pixFront, uint32_t pixBack) //blend front color with opacity M / N over opaque background: http://en.wikipedia.org/wiki/Alpha_compositing#Alpha_blending { static_assert(0 < M && M < N && N <= 1000, ""); -#define calcColor(colFront, colBack) \ - (((colFront) * M + (colBack) * (N - M)) / N) + auto calcColor = [](unsigned char colFront, unsigned char colBack) -> unsigned char { return (colFront * M + colBack * (N - M)) / N; }; return makePixel(calcColor(getRed (pixFront), getRed (pixBack)), calcColor(getGreen(pixFront), getGreen(pixBack)), calcColor(getBlue (pixFront), getBlue (pixBack))); - -#undef calcColor } @@ -72,15 +50,15 @@ uint32_t gradientARGB(uint32_t pixFront, uint32_t pixBack) //find intermediate c if (weightSum == 0) return 0; -#define calcColor(colFront, colBack) \ - static_cast(((colFront) * weightFront + (colBack) * weightBack) / weightSum) + auto calcColor = [=](unsigned char colFront, unsigned char colBack) + { + return static_cast((colFront * weightFront + colBack * weightBack) / weightSum); + }; return makePixel(static_cast(weightSum / N), calcColor(getRed (pixFront), getRed (pixBack)), calcColor(getGreen(pixFront), getGreen(pixBack)), calcColor(getBlue (pixFront), getBlue (pixBack))); - -#undef calcColor } @@ -96,26 +74,6 @@ uint32_t gradientARGB(uint32_t pixFront, uint32_t pixBack) //find intermediate c // -uint32_t* byteAdvance( uint32_t* ptr, int bytes) { return reinterpret_cast< uint32_t*>(reinterpret_cast< char*>(ptr) + bytes); } -const uint32_t* byteAdvance(const uint32_t* ptr, int bytes) { return reinterpret_cast(reinterpret_cast(ptr) + bytes); } - - -//fill block with the given color -inline -void fillBlock(uint32_t* trg, int pitch, uint32_t col, int blockWidth, int blockHeight) -{ - //for (int y = 0; y < blockHeight; ++y, trg = byteAdvance(trg, pitch)) - // std::fill(trg, trg + blockWidth, col); - - for (int y = 0; y < blockHeight; ++y, trg = byteAdvance(trg, pitch)) - for (int x = 0; x < blockWidth; ++x) - trg[x] = col; -} - -inline -void fillBlock(uint32_t* trg, int pitch, uint32_t col, int n) { fillBlock(trg, pitch, col, n, n); } - - #ifdef _MSC_VER #define FORCE_INLINE __forceinline #elif defined __GNUC__ @@ -178,7 +136,7 @@ template inline T square(T value) { return value * value; } - +#if 0 inline double distRGB(uint32_t pix1, uint32_t pix2) { @@ -189,6 +147,7 @@ double distRGB(uint32_t pix1, uint32_t pix2) //euklidean RGB distance return std::sqrt(square(r_diff) + square(g_diff) + square(b_diff)); } +#endif inline @@ -218,26 +177,20 @@ double distYCbCr(uint32_t pix1, uint32_t pix2, double lumaWeight) } -struct DistYCbCrBuffer //30% perf boost compared to distYCbCr()! +inline +double distYCbCrBuffered(uint32_t pix1, uint32_t pix2) { -public: - static double dist(uint32_t pix1, uint32_t pix2) + //30% perf boost compared to plain distYCbCr()! + //consumes 64 MB memory; using double is only 2% faster, but takes 128 MB + static const std::vector diffToDist = [] { -#if defined _MSC_VER && _MSC_VER < 1900 -#error function scope static initialization is not yet thread-safe! -#endif - static const DistYCbCrBuffer inst; - return inst.distImpl(pix1, pix2); - } + std::vector tmp; -private: - DistYCbCrBuffer() : buffer(256 * 256 * 256) - { for (uint32_t i = 0; i < 256 * 256 * 256; ++i) //startup time: 114 ms on Intel Core i5 (four cores) { - const int r_diff = getByte<2>(i) * 2 - 255; - const int g_diff = getByte<1>(i) * 2 - 255; - const int b_diff = getByte<0>(i) * 2 - 255; + const int r_diff = getByte<2>(i) * 2 - 0xFF; + const int g_diff = getByte<1>(i) * 2 - 0xFF; + const int b_diff = getByte<0>(i) * 2 - 0xFF; const double k_b = 0.0593; //ITU-R BT.2020 conversion const double k_r = 0.2627; // @@ -250,28 +203,31 @@ private: const double c_b = scale_b * (b_diff - y); const double c_r = scale_r * (r_diff - y); - buffer[i] = static_cast(std::sqrt(square(y) + square(c_b) + square(c_r))); + tmp.push_back(static_cast(std::sqrt(square(y) + square(c_b) + square(c_r)))); } - } + return tmp; + }(); - double distImpl(uint32_t pix1, uint32_t pix2) const - { - //if (pix1 == pix2) -> 8% perf degradation! - // return 0; - //if (pix1 > pix2) - // std::swap(pix1, pix2); -> 30% perf degradation!!! + //if (pix1 == pix2) -> 8% perf degradation! + // return 0; + //if (pix1 < pix2) + // std::swap(pix1, pix2); -> 30% perf degradation!!! +#if 1 + const int r_diff = static_cast(getRed (pix1)) - getRed (pix2); + const int g_diff = static_cast(getGreen(pix1)) - getGreen(pix2); + const int b_diff = static_cast(getBlue (pix1)) - getBlue (pix2); - const int r_diff = static_cast(getRed (pix1)) - getRed (pix2); - const int g_diff = static_cast(getGreen(pix1)) - getGreen(pix2); - const int b_diff = static_cast(getBlue (pix1)) - getBlue (pix2); + return diffToDist[(((r_diff + 0xFF) / 2) << 16) | //slightly reduce precision (division by 2) to squeeze value into single byte + (((g_diff + 0xFF) / 2) << 8) | + (( b_diff + 0xFF) / 2)]; +#else //not noticeably faster: + const int r_diff_tmp = ((pix1 & 0xFF0000) + 0xFF0000 - (pix2 & 0xFF0000)) / 2; + const int g_diff_tmp = ((pix1 & 0x00FF00) + 0x00FF00 - (pix2 & 0x00FF00)) / 2; //slightly reduce precision (division by 2) to squeeze value into single byte + const int b_diff_tmp = ((pix1 & 0x0000FF) + 0x0000FF - (pix2 & 0x0000FF)) / 2; - return buffer[(((r_diff + 255) / 2) << 16) | //slightly reduce precision (division by 2) to squeeze value into single byte - (((g_diff + 255) / 2) << 8) | - (( b_diff + 255) / 2)]; - } - - std::vector buffer; //consumes 64 MB memory; using double is only 2% faster, but takes 128 MB -}; + return diffToDist[(r_diff_tmp & 0xFF0000) | (g_diff_tmp & 0x00FF00) | (b_diff_tmp & 0x0000FF)]; +#endif +} enum BlendType @@ -323,15 +279,12 @@ BlendResult preProcessCorners(const Kernel_4x4& ker, const xbrz::ScalerCfg& cfg) ker.g == ker.k)) return result; -#define dist(pix1, pix2) \ - ColorDistance::dist((pix1), (pix2), cfg.luminanceWeight) + auto dist = [&](uint32_t pix1, uint32_t pix2) { return ColorDistance::dist(pix1, pix2, cfg.luminanceWeight); }; const int weight = 4; double jg = dist(ker.i, ker.f) + dist(ker.f, ker.c) + dist(ker.n, ker.k) + dist(ker.k, ker.h) + weight * dist(ker.j, ker.g); double fk = dist(ker.e, ker.j) + dist(ker.j, ker.o) + dist(ker.b, ker.g) + dist(ker.g, ker.l) + weight * dist(ker.f, ker.k); -#undef dist - if (jg < fk) //test sample: 70% of values max(jg, fk) / min(jg, fk) are between 1.1 and 3.7 with median being 1.8 { const bool dominantGradient = cfg.dominantDirectionThreshold * jg < fk; @@ -383,12 +336,12 @@ DEF_GETTER(g, c) DEF_GETTER(h, b) DEF_GETTER(i, a) #define DEF_GETTER(x, y) template <> inline uint32_t get_##x(const Kernel_3x3& ker) { return ker.y; } DEF_GETTER(a, c) DEF_GETTER(b, f) DEF_GETTER(c, i) DEF_GETTER(d, b) DEF_GETTER(e, e) DEF_GETTER(f, h) -DEF_GETTER(g, a) DEF_GETTER(h, d) DEF_GETTER(i, g) +DEF_GETTER(g, a) DEF_GETTER(h, d) DEF_GETTER(i, g) #undef DEF_GETTER //compress four blend types into a single byte -inline BlendType getTopL (unsigned char b) { return static_cast(0x3 & b); } +//inline BlendType getTopL (unsigned char b) { return static_cast(0x3 & b); } inline BlendType getTopR (unsigned char b) { return static_cast(0x3 & (b >> 2)); } inline BlendType getBottomR(unsigned char b) { return static_cast(0x3 & (b >> 4)); } inline BlendType getBottomL(unsigned char b) { return static_cast(0x3 & (b >> 6)); } @@ -407,6 +360,13 @@ template <> inline unsigned char rotateBlendInfo(unsigned char b) { ret template <> inline unsigned char rotateBlendInfo(unsigned char b) { return ((b << 6) | (b >> 2)) & 0xff; } +#if 0 //#ifndef NDEBUG + int debugPixelX = -1; + int debugPixelY = 12; + __declspec(thread) bool breakIntoDebugger = false; +#endif + + /* input kernel area naming convention: ------------- @@ -434,40 +394,37 @@ void blendPixel(const Kernel_3x3& ker, #define h get_h(ker) #define i get_i(ker) +#if 0 //#ifndef NDEBUG + if (breakIntoDebugger) + __debugbreak(); //__asm int 3; +#endif + + (void)a; //silence Clang's -Wunused-function + const unsigned char blend = rotateBlendInfo(blendInfo); if (getBottomR(blend) >= BLEND_NORMAL) { - struct LineBlend + auto eq = [&](uint32_t pix1, uint32_t pix2) { return ColorDistance::dist(pix1, pix2, cfg.luminanceWeight) < cfg.equalColorTolerance; }; + auto dist = [&](uint32_t pix1, uint32_t pix2) { return ColorDistance::dist(pix1, pix2, cfg.luminanceWeight); }; + + const bool doLineBlend = [&]() -> bool { - static bool Eval(const Kernel_3x3& ker, const xbrz::ScalerCfg& cfg, const unsigned char blend) - { - if (getBottomR(blend) >= BLEND_DOMINANT) - return true; - -#define eq(pix1, pix2) \ - (ColorDistance::dist((pix1), (pix2), cfg.luminanceWeight) < cfg.equalColorTolerance) - - //make sure there is no second blending in an adjacent rotation for this pixel: handles insular pixels, mario eyes - if (getTopR(blend) != BLEND_NONE && !eq(e, g)) //but support double-blending for 90 degree corners - return false; - if (getBottomL(blend) != BLEND_NONE && !eq(e, c)) - return false; - - //no full blending for L-shapes; blend corner only (handles "mario mushroom eyes") - if (!eq(e, i) && eq(g, h) && eq(h , i) && eq(i, f) && eq(f, c)) - return false; - -#undef eq - + if (getBottomR(blend) >= BLEND_DOMINANT) return true; - } - }; - const bool doLineBlend = LineBlend::Eval(ker, cfg, blend); + //make sure there is no second blending in an adjacent rotation for this pixel: handles insular pixels, mario eyes + if (getTopR(blend) != BLEND_NONE && !eq(e, g)) //but support double-blending for 90° corners + return false; + if (getBottomL(blend) != BLEND_NONE && !eq(e, c)) + return false; -#define dist(pix1, pix2) \ - ColorDistance::dist((pix1), (pix2), cfg.luminanceWeight) + //no full blending for L-shapes; blend corner only (handles "mario mushroom eyes") + if (!eq(e, i) && eq(g, h) && eq(h, i) && eq(i, f) && eq(f, c)) + return false; + + return true; + }(); const uint32_t px = dist(e, f) <= dist(e, h) ? f : h; //choose most similar color @@ -493,15 +450,13 @@ void blendPixel(const Kernel_3x3& ker, if (haveSteepLine) Scaler::blendLineSteep(px, out); else - Scaler::blendLineDiagonal(px,out); + Scaler::blendLineDiagonal(px, out); } } else Scaler::blendCorner(px, out); } -#undef dist - #undef a #undef b #undef c @@ -528,7 +483,7 @@ void scaleImage(const uint32_t* src, uint32_t* trg, int srcWidth, int srcHeight, //"sizeof(uint32_t) * srcWidth * (yLast - yFirst)" bytes without risk of accidental overwriting before accessing const int bufferSize = srcWidth; unsigned char* preProcBuffer = reinterpret_cast(trg + yLast * Scaler::scale * trgWidth) - bufferSize; - std::fill(preProcBuffer, preProcBuffer + bufferSize, 0); + std::fill(preProcBuffer, preProcBuffer + bufferSize, '\0'); static_assert(BLEND_NONE == 0, ""); //initialize preprocessing buffer for first row of current stripe: detect upper left and right corner blending @@ -599,6 +554,9 @@ void scaleImage(const uint32_t* src, uint32_t* trg, int srcWidth, int srcHeight, for (int x = 0; x < srcWidth; ++x, out += Scaler::scale) { +#if 0 //#ifndef NDEBUG + breakIntoDebugger = debugPixelX == x && debugPixelY == y; +#endif //all those bounds checks have only insignificant impact on performance! const int x_m1 = std::max(x - 1, 0); //perf: prefer array indexing to additional pointers! const int x_p1 = std::min(x + 1, srcWidth - 1); @@ -652,7 +610,8 @@ void scaleImage(const uint32_t* src, uint32_t* trg, int srcWidth, int srcHeight, } //fill block of size scale * scale with the given color - fillBlock(out, trgWidth * sizeof(uint32_t), ker4.f, Scaler::scale); //place *after* preprocessing step, to not overwrite the results while processing the the last pixel! + fillBlock(out, trgWidth * sizeof(uint32_t), ker4.f, Scaler::scale, Scaler::scale); + //place *after* preprocessing step, to not overwrite the results while processing the the last pixel! //blend four corners of current pixel if (blendingNeeded(blend_xy)) //good 5% perf-improvement @@ -1047,7 +1006,7 @@ struct ColorDistanceRGB { static double dist(uint32_t pix1, uint32_t pix2, double luminanceWeight) { - return DistYCbCrBuffer::dist(pix1, pix2); + return distYCbCrBuffered(pix1, pix2); //if (pix1 == pix2) //about 4% perf boost // return 0; @@ -1064,20 +1023,36 @@ struct ColorDistanceARGB /* Requirements for a color distance handling alpha channel: with a1, a2 in [0, 1] - 1. if a1 = a2, distance should be: a1 * distYCbCr() - 2. if a1 = 0, distance should be: a2 * distYCbCr(black, white) = a2 * 255 - 3. if a1 = 1, ??? maybe: 255 * (1 - a2) + a2 * distYCbCr() + 1. if a1 = a2, distance should be: a1 * distYCbCr() + 2. if a1 = 0, distance should be: a2 * distYCbCr(black, white) = a2 * 255 + 3. if a1 = 1, ??? maybe: 255 * (1 - a2) + a2 * distYCbCr() */ - //return std::min(a1, a2) * DistYCbCrBuffer::dist(pix1, pix2) + 255 * abs(a1 - a2); + //return std::min(a1, a2) * distYCbCrBuffered(pix1, pix2) + 255 * abs(a1 - a2); //=> following code is 15% faster: - const double d = DistYCbCrBuffer::dist(pix1, pix2); + const double d = distYCbCrBuffered(pix1, pix2); if (a1 < a2) return a1 * d + 255 * (a2 - a1); else return a2 * d + 255 * (a1 - a2); - //alternative? return std::sqrt(a1 * a2 * square(DistYCbCrBuffer::dist(pix1, pix2)) + square(255 * (a1 - a2))); + //alternative? return std::sqrt(a1 * a2 * square(distYCbCrBuffered(pix1, pix2)) + square(255 * (a1 - a2))); + } +}; + + +struct ColorDistanceUnbufferedARGB +{ + static double dist(uint32_t pix1, uint32_t pix2, double luminanceWeight) + { + const double a1 = getAlpha(pix1) / 255.0 ; + const double a2 = getAlpha(pix2) / 255.0 ; + + const double d = distYCbCr(pix1, pix2, luminanceWeight); + if (a1 < a2) + return a1 * d + 255 * (a2 - a1); + else + return a2 * d + 255 * (a1 - a2); } }; @@ -1104,9 +1079,26 @@ struct ColorGradientARGB void xbrz::scale(size_t factor, const uint32_t* src, uint32_t* trg, int srcWidth, int srcHeight, ColorFormat colFmt, const xbrz::ScalerCfg& cfg, int yFirst, int yLast) { + static_assert(SCALE_FACTOR_MAX == 6, ""); switch (colFmt) { - case ARGB: + case ColorFormat::RGB: + switch (factor) + { + case 2: + return scaleImage, ColorDistanceRGB>(src, trg, srcWidth, srcHeight, cfg, yFirst, yLast); + case 3: + return scaleImage, ColorDistanceRGB>(src, trg, srcWidth, srcHeight, cfg, yFirst, yLast); + case 4: + return scaleImage, ColorDistanceRGB>(src, trg, srcWidth, srcHeight, cfg, yFirst, yLast); + case 5: + return scaleImage, ColorDistanceRGB>(src, trg, srcWidth, srcHeight, cfg, yFirst, yLast); + case 6: + return scaleImage, ColorDistanceRGB>(src, trg, srcWidth, srcHeight, cfg, yFirst, yLast); + } + break; + + case ColorFormat::ARGB: switch (factor) { case 2: @@ -1122,19 +1114,19 @@ void xbrz::scale(size_t factor, const uint32_t* src, uint32_t* trg, int srcWidth } break; - case RGB: + case ColorFormat::ARGB_UNBUFFERED: switch (factor) { case 2: - return scaleImage, ColorDistanceRGB>(src, trg, srcWidth, srcHeight, cfg, yFirst, yLast); + return scaleImage, ColorDistanceUnbufferedARGB>(src, trg, srcWidth, srcHeight, cfg, yFirst, yLast); case 3: - return scaleImage, ColorDistanceRGB>(src, trg, srcWidth, srcHeight, cfg, yFirst, yLast); + return scaleImage, ColorDistanceUnbufferedARGB>(src, trg, srcWidth, srcHeight, cfg, yFirst, yLast); case 4: - return scaleImage, ColorDistanceRGB>(src, trg, srcWidth, srcHeight, cfg, yFirst, yLast); + return scaleImage, ColorDistanceUnbufferedARGB>(src, trg, srcWidth, srcHeight, cfg, yFirst, yLast); case 5: - return scaleImage, ColorDistanceRGB>(src, trg, srcWidth, srcHeight, cfg, yFirst, yLast); + return scaleImage, ColorDistanceUnbufferedARGB>(src, trg, srcWidth, srcHeight, cfg, yFirst, yLast); case 6: - return scaleImage, ColorDistanceRGB>(src, trg, srcWidth, srcHeight, cfg, yFirst, yLast); + return scaleImage, ColorDistanceUnbufferedARGB>(src, trg, srcWidth, srcHeight, cfg, yFirst, yLast); } break; } @@ -1146,84 +1138,133 @@ bool xbrz::equalColorTest(uint32_t col1, uint32_t col2, ColorFormat colFmt, doub { switch (colFmt) { - case ARGB: - return ColorDistanceARGB::dist(col1, col2, luminanceWeight) < equalColorTolerance; - - case RGB: + case ColorFormat::RGB: return ColorDistanceRGB::dist(col1, col2, luminanceWeight) < equalColorTolerance; + case ColorFormat::ARGB: + return ColorDistanceARGB::dist(col1, col2, luminanceWeight) < equalColorTolerance; + case ColorFormat::ARGB_UNBUFFERED: + return ColorDistanceUnbufferedARGB::dist(col1, col2, luminanceWeight) < equalColorTolerance; } assert(false); return false; } -void xbrz::nearestNeighborScale(const uint32_t* src, int srcWidth, int srcHeight, int srcPitch, - uint32_t* trg, int trgWidth, int trgHeight, int trgPitch, - SliceType st, int yFirst, int yLast) +void xbrz::bilinearScale(const uint32_t* src, int srcWidth, int srcHeight, + /**/ uint32_t* trg, int trgWidth, int trgHeight) { - if (srcPitch < srcWidth * static_cast(sizeof(uint32_t)) || - trgPitch < trgWidth * static_cast(sizeof(uint32_t))) - { - assert(false); - return; - } - - switch (st) - { - case NN_SCALE_SLICE_SOURCE: - //nearest-neighbor (going over source image - fast for upscaling, since source is read only once - yFirst = std::max(yFirst, 0); - yLast = std::min(yLast, srcHeight); - if (yFirst >= yLast || trgWidth <= 0 || trgHeight <= 0) return; - - for (int y = yFirst; y < yLast; ++y) - { - //mathematically: ySrc = floor(srcHeight * yTrg / trgHeight) - // => search for integers in: [ySrc, ySrc + 1) * trgHeight / srcHeight - - //keep within for loop to support MT input slices! - const int yTrg_first = ( y * trgHeight + srcHeight - 1) / srcHeight; //=ceil(y * trgHeight / srcHeight) - const int yTrg_last = ((y + 1) * trgHeight + srcHeight - 1) / srcHeight; //=ceil(((y + 1) * trgHeight) / srcHeight) - const int blockHeight = yTrg_last - yTrg_first; - - if (blockHeight > 0) - { - const uint32_t* srcLine = byteAdvance(src, y * srcPitch); - uint32_t* trgLine = byteAdvance(trg, yTrg_first * trgPitch); - int xTrg_first = 0; - - for (int x = 0; x < srcWidth; ++x) - { - int xTrg_last = ((x + 1) * trgWidth + srcWidth - 1) / srcWidth; - const int blockWidth = xTrg_last - xTrg_first; - if (blockWidth > 0) - { - xTrg_first = xTrg_last; - fillBlock(trgLine, trgPitch, srcLine[x], blockWidth, blockHeight); - trgLine += blockWidth; - } - } - } - } - break; - - case NN_SCALE_SLICE_TARGET: - //nearest-neighbor (going over target image - slow for upscaling, since source is read multiple times missing out on cache! Fast for similar image sizes!) - yFirst = std::max(yFirst, 0); - yLast = std::min(yLast, trgHeight); - if (yFirst >= yLast || srcHeight <= 0 || srcWidth <= 0) return; - - for (int y = yFirst; y < yLast; ++y) - { - uint32_t* trgLine = byteAdvance(trg, y * trgPitch); - const int ySrc = srcHeight * y / trgHeight; - const uint32_t* srcLine = byteAdvance(src, ySrc * srcPitch); - for (int x = 0; x < trgWidth; ++x) - { - const int xSrc = srcWidth * x / trgWidth; - trgLine[x] = srcLine[xSrc]; - } - } - break; - } + bilinearScale(src, srcWidth, srcHeight, srcWidth * sizeof(uint32_t), + trg, trgWidth, trgHeight, trgWidth * sizeof(uint32_t), + 0, trgHeight, [](uint32_t pix) { return pix; }); } + + +void xbrz::nearestNeighborScale(const uint32_t* src, int srcWidth, int srcHeight, + /**/ uint32_t* trg, int trgWidth, int trgHeight) +{ + nearestNeighborScale(src, srcWidth, srcHeight, srcWidth * sizeof(uint32_t), + trg, trgWidth, trgHeight, trgWidth * sizeof(uint32_t), + 0, trgHeight, [](uint32_t pix) { return pix; }); +} + + +#if 0 +//#include +void bilinearScaleCpu(const uint32_t* src, int srcWidth, int srcHeight, + /**/ uint32_t* trg, int trgWidth, int trgHeight) +{ + const int TASK_GRANULARITY = 16; + + concurrency::task_group tg; + + for (int i = 0; i < trgHeight; i += TASK_GRANULARITY) + tg.run([=] + { + const int iLast = std::min(i + TASK_GRANULARITY, trgHeight); + xbrz::bilinearScale(src, srcWidth, srcHeight, srcWidth * sizeof(uint32_t), + trg, trgWidth, trgHeight, trgWidth * sizeof(uint32_t), + i, iLast, [](uint32_t pix) { return pix; }); + }); + tg.wait(); +} + + +//Perf: AMP vs CPU: merely ~10% shorter runtime (scaling 1280x800 -> 1920x1080) +//#include +void bilinearScaleAmp(const uint32_t* src, int srcWidth, int srcHeight, //throw concurrency::runtime_exception + /**/ uint32_t* trg, int trgWidth, int trgHeight) +{ + //C++ AMP reference: https://msdn.microsoft.com/en-us/library/hh289390.aspx + //introduction to C++ AMP: https://msdn.microsoft.com/en-us/magazine/hh882446.aspx + using namespace concurrency; + //TODO: pitch + + if (srcHeight <= 0 || srcWidth <= 0) return; + + const float scaleX = static_cast(trgWidth ) / srcWidth; + const float scaleY = static_cast(trgHeight) / srcHeight; + + array_view srcView(srcHeight, srcWidth, src); + array_view< uint32_t, 2> trgView(trgHeight, trgWidth, trg); + trgView.discard_data(); + + parallel_for_each(trgView.extent, [=](index<2> idx) restrict(amp) //throw ? + { + const int y = idx[0]; + const int x = idx[1]; + //Perf notes: + // -> float-based calculation is (almost 2x) faster than double! + // -> no noticeable improvement via tiling: https://msdn.microsoft.com/en-us/magazine/hh882447.aspx + // -> no noticeable improvement with restrict(amp,cpu) + // -> iterating over y-axis only is significantly slower! + // -> pre-calculating x,y-dependent variables in a buffer + array_view<> is ~ 20 % slower! + const int y1 = srcHeight * y / trgHeight; + int y2 = y1 + 1; + if (y2 == srcHeight) --y2; + + const float yy1 = y / scaleY - y1; + const float y2y = 1 - yy1; + //------------------------------------- + const int x1 = srcWidth * x / trgWidth; + int x2 = x1 + 1; + if (x2 == srcWidth) --x2; + + const float xx1 = x / scaleX - x1; + const float x2x = 1 - xx1; + //------------------------------------- + const float x2xy2y = x2x * y2y; + const float xx1y2y = xx1 * y2y; + const float x2xyy1 = x2x * yy1; + const float xx1yy1 = xx1 * yy1; + + auto interpolate = [=](int offset) + { + /* + https://en.wikipedia.org/wiki/Bilinear_interpolation + (c11(x2 - x) + c21(x - x1)) * (y2 - y ) + + (c12(x2 - x) + c22(x - x1)) * (y - y1) + */ + const auto c11 = (srcView(y1, x1) >> (8 * offset)) & 0xff; + const auto c21 = (srcView(y1, x2) >> (8 * offset)) & 0xff; + const auto c12 = (srcView(y2, x1) >> (8 * offset)) & 0xff; + const auto c22 = (srcView(y2, x2) >> (8 * offset)) & 0xff; + + return c11 * x2xy2y + c21 * xx1y2y + + c12 * x2xyy1 + c22 * xx1yy1; + }; + + const float bi = interpolate(0); + const float gi = interpolate(1); + const float ri = interpolate(2); + const float ai = interpolate(3); + + const auto b = static_cast(bi + 0.5f); + const auto g = static_cast(gi + 0.5f); + const auto r = static_cast(ri + 0.5f); + const auto a = static_cast(ai + 0.5f); + + trgView(y, x) = (a << 24) | (r << 16) | (g << 8) | b; + }); + trgView.synchronize(); //throw ? +} +#endif \ No newline at end of file diff --git a/src/textures/hires/xbr/xbrz.h b/src/textures/hires/xbr/xbrz.h index c641429e5..c57649e01 100644 --- a/src/textures/hires/xbr/xbrz.h +++ b/src/textures/hires/xbr/xbrz.h @@ -1,28 +1,24 @@ // **************************************************************************** -// * This file is part of the HqMAME project. It is distributed under * -// * GNU General Public License: http://www.gnu.org/licenses/gpl-3.0 * +// * This file is part of the xBRZ project. It is distributed under * +// * GNU General Public License: https://www.gnu.org/licenses/gpl-3.0 * // * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * // * * // * Additionally and as a special exception, the author gives permission * -// * to link the code of this program with the MAME library (or with modified * -// * versions of MAME that use the same license as MAME), and distribute * -// * linked combinations including the two. You must obey the GNU General * -// * Public License in all respects for all of the code used other than MAME. * +// * to link the code of this program with the following libraries * +// * (or with modified versions that use the same licenses), and distribute * +// * linked combinations including the two: MAME, FreeFileSync, Snes9x, ePSXe * +// * You must obey the GNU General Public License in all respects for all of * +// * the code used other than MAME, FreeFileSync, Snes9x, ePSXe. * // * If you modify this file, you may extend this exception to your version * // * of the file, but you are not obligated to do so. If you do not wish to * // * do so, delete this exception statement from your version. * -// * * -// * An explicit permission was granted to use xBRZ in combination with ZDoom * -// * and derived projects as long as it is used for non-commercial purposes. * -// * * -// * Backported to C++98 by Alexey Lysiuk * // **************************************************************************** #ifndef XBRZ_HEADER_3847894708239054 #define XBRZ_HEADER_3847894708239054 #include //size_t -#include //uint32_t +#include //uint32_t #include #include "xbrz_config.h" @@ -43,60 +39,41 @@ http://board.byuu.org/viewtopic.php?f=10&t=2248 - support scaling up to 6xBRZ */ -enum ColorFormat //from high bits -> low bits, 8 bit per channel +enum class ColorFormat //from high bits -> low bits, 8 bit per channel { RGB, //8 bit for each red, green, blue, upper 8 bits unused ARGB, //including alpha channel, BGRA byte order on little-endian machines + ARGB_UNBUFFERED, //like ARGB, but without the one-time buffer creation overhead (ca. 100 - 300 ms) at the expense of a slightly slower scaling time }; +const int SCALE_FACTOR_MAX = 6; + /* -> map source (srcWidth * srcHeight) to target (scale * width x scale * height) image, optionally processing a half-open slice of rows [yFirst, yLast) only -> support for source/target pitch in bytes! -> if your emulator changes only a few image slices during each cycle (e.g. DOSBox) then there's no need to run xBRZ on the complete image: Just make sure you enlarge the source image slice by 2 rows on top and 2 on bottom (this is the additional range the xBRZ algorithm is using during analysis) - Caveat: If there are multiple changed slices, make sure they do not overlap after adding these additional rows in order to avoid a memory race condition + CAVEAT: If there are multiple changed slices, make sure they do not overlap after adding these additional rows in order to avoid a memory race condition in the target image data if you are using multiple threads for processing each enlarged slice! THREAD-SAFETY: - parts of the same image may be scaled by multiple threads as long as the [yFirst, yLast) ranges do not overlap! - - there is a minor inefficiency for the first row of a slice, so avoid processing single rows only; suggestion: process 8-16 rows at least + - there is a minor inefficiency for the first row of a slice, so avoid processing single rows only; suggestion: process at least 8-16 rows */ -#ifdef max -#undef max -#endif -void scale(size_t factor, //valid range: 2 - 6 +void scale(size_t factor, //valid range: 2 - SCALE_FACTOR_MAX const uint32_t* src, uint32_t* trg, int srcWidth, int srcHeight, ColorFormat colFmt, const ScalerCfg& cfg = ScalerCfg(), int yFirst = 0, int yLast = std::numeric_limits::max()); //slice of source image -void nearestNeighborScale(const uint32_t* src, int srcWidth, int srcHeight, - uint32_t* trg, int trgWidth, int trgHeight); +void bilinearScale(const uint32_t* src, int srcWidth, int srcHeight, + /**/ uint32_t* trg, int trgWidth, int trgHeight); + +void nearestNeighborScale(const uint32_t* src, int srcWidth, int srcHeight, + /**/ uint32_t* trg, int trgWidth, int trgHeight); -enum SliceType -{ - NN_SCALE_SLICE_SOURCE, - NN_SCALE_SLICE_TARGET, -}; -void nearestNeighborScale(const uint32_t* src, int srcWidth, int srcHeight, int srcPitch, //pitch in bytes! - uint32_t* trg, int trgWidth, int trgHeight, int trgPitch, - SliceType st, int yFirst, int yLast); //parameter tuning bool equalColorTest(uint32_t col1, uint32_t col2, ColorFormat colFmt, double luminanceWeight, double equalColorTolerance); - - - - - -//########################### implementation ########################### -inline -void nearestNeighborScale(const uint32_t* src, int srcWidth, int srcHeight, - uint32_t* trg, int trgWidth, int trgHeight) -{ - nearestNeighborScale(src, srcWidth, srcHeight, srcWidth * sizeof(uint32_t), - trg, trgWidth, trgHeight, trgWidth * sizeof(uint32_t), - NN_SCALE_SLICE_TARGET, 0, trgHeight); -} } #endif diff --git a/src/textures/hires/xbr/xbrz_config.h b/src/textures/hires/xbr/xbrz_config.h index 28e9e9044..dc5e5aec8 100644 --- a/src/textures/hires/xbr/xbrz_config.h +++ b/src/textures/hires/xbr/xbrz_config.h @@ -1,21 +1,17 @@ // **************************************************************************** -// * This file is part of the HqMAME project. It is distributed under * -// * GNU General Public License: http://www.gnu.org/licenses/gpl-3.0 * +// * This file is part of the xBRZ project. It is distributed under * +// * GNU General Public License: https://www.gnu.org/licenses/gpl-3.0 * // * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * // * * // * Additionally and as a special exception, the author gives permission * -// * to link the code of this program with the MAME library (or with modified * -// * versions of MAME that use the same license as MAME), and distribute * -// * linked combinations including the two. You must obey the GNU General * -// * Public License in all respects for all of the code used other than MAME. * +// * to link the code of this program with the following libraries * +// * (or with modified versions that use the same licenses), and distribute * +// * linked combinations including the two: MAME, FreeFileSync, Snes9x, ePSXe * +// * You must obey the GNU General Public License in all respects for all of * +// * the code used other than MAME, FreeFileSync, Snes9x, ePSXe. * // * If you modify this file, you may extend this exception to your version * // * of the file, but you are not obligated to do so. If you do not wish to * // * do so, delete this exception statement from your version. * -// * * -// * An explicit permission was granted to use xBRZ in combination with ZDoom * -// * and derived projects as long as it is used for non-commercial purposes. * -// * * -// * Backported to C++98 by Alexey Lysiuk * // **************************************************************************** #ifndef XBRZ_CONFIG_HEADER_284578425345 @@ -27,18 +23,11 @@ namespace xbrz { struct ScalerCfg { - ScalerCfg() : - luminanceWeight(1), - equalColorTolerance(30), - dominantDirectionThreshold(3.6), - steepDirectionThreshold(2.2), - newTestAttribute(0) {} - - double luminanceWeight; - double equalColorTolerance; - double dominantDirectionThreshold; - double steepDirectionThreshold; - double newTestAttribute; //unused; test new parameters + double luminanceWeight = 1; + double equalColorTolerance = 30; + double dominantDirectionThreshold = 3.6; + double steepDirectionThreshold = 2.2; + double newTestAttribute = 0; //unused; test new parameters }; } diff --git a/src/textures/hires/xbr/xbrz_tools.h b/src/textures/hires/xbr/xbrz_tools.h new file mode 100644 index 000000000..5d66f0bf8 --- /dev/null +++ b/src/textures/hires/xbr/xbrz_tools.h @@ -0,0 +1,268 @@ +// **************************************************************************** +// * This file is part of the xBRZ project. It is distributed under * +// * GNU General Public License: https://www.gnu.org/licenses/gpl-3.0 * +// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * +// * * +// * Additionally and as a special exception, the author gives permission * +// * to link the code of this program with the following libraries * +// * (or with modified versions that use the same licenses), and distribute * +// * linked combinations including the two: MAME, FreeFileSync, Snes9x, ePSXe * +// * You must obey the GNU General Public License in all respects for all of * +// * the code used other than MAME, FreeFileSync, Snes9x, ePSXe. * +// * If you modify this file, you may extend this exception to your version * +// * of the file, but you are not obligated to do so. If you do not wish to * +// * do so, delete this exception statement from your version. * +// **************************************************************************** + +#ifndef XBRZ_TOOLS_H_825480175091875 +#define XBRZ_TOOLS_H_825480175091875 + +#include +#include +#include + + +namespace xbrz +{ +template inline +unsigned char getByte(uint32_t val) { return static_cast((val >> (8 * N)) & 0xff); } + +inline unsigned char getAlpha(uint32_t pix) { return getByte<3>(pix); } +inline unsigned char getRed (uint32_t pix) { return getByte<2>(pix); } +inline unsigned char getGreen(uint32_t pix) { return getByte<1>(pix); } +inline unsigned char getBlue (uint32_t pix) { return getByte<0>(pix); } + +inline uint32_t makePixel(unsigned char a, unsigned char r, unsigned char g, unsigned char b) { return (a << 24) | (r << 16) | (g << 8) | b; } +inline uint32_t makePixel( unsigned char r, unsigned char g, unsigned char b) { return (r << 16) | (g << 8) | b; } + +inline uint32_t rgb555to888(uint16_t pix) { return ((pix & 0x7C00) << 9) | ((pix & 0x03E0) << 6) | ((pix & 0x001F) << 3); } +inline uint32_t rgb565to888(uint16_t pix) { return ((pix & 0xF800) << 8) | ((pix & 0x07E0) << 5) | ((pix & 0x001F) << 3); } + +inline uint16_t rgb888to555(uint32_t pix) { return static_cast(((pix & 0xF80000) >> 9) | ((pix & 0x00F800) >> 6) | ((pix & 0x0000F8) >> 3)); } +inline uint16_t rgb888to565(uint32_t pix) { return static_cast(((pix & 0xF80000) >> 8) | ((pix & 0x00FC00) >> 5) | ((pix & 0x0000F8) >> 3)); } + + +template inline +Pix* byteAdvance(Pix* ptr, int bytes) +{ + using PixNonConst = typename std::remove_cv::type; + using PixByte = typename std::conditional::value, char, const char>::type; + + static_assert(std::is_integral::value, "Pix* is expected to be cast-able to char*"); + + return reinterpret_cast(reinterpret_cast(ptr) + bytes); +} + + +//fill block with the given color +template inline +void fillBlock(Pix* trg, int pitch, Pix col, int blockWidth, int blockHeight) +{ + //for (int y = 0; y < blockHeight; ++y, trg = byteAdvance(trg, pitch)) + // std::fill(trg, trg + blockWidth, col); + + for (int y = 0; y < blockHeight; ++y, trg = byteAdvance(trg, pitch)) + for (int x = 0; x < blockWidth; ++x) + trg[x] = col; +} + + +//nearest-neighbor (going over target image - slow for upscaling, since source is read multiple times missing out on cache! Fast for similar image sizes!) +template +void nearestNeighborScale(const PixSrc* src, int srcWidth, int srcHeight, int srcPitch, + /**/ PixTrg* trg, int trgWidth, int trgHeight, int trgPitch, + int yFirst, int yLast, PixConverter pixCvrt /*convert PixSrc to PixTrg*/) +{ + static_assert(std::is_integral::value, "PixSrc* is expected to be cast-able to char*"); + static_assert(std::is_integral::value, "PixTrg* is expected to be cast-able to char*"); + + static_assert(std::is_same::value, "PixConverter returning wrong pixel format"); + + if (srcPitch < srcWidth * static_cast(sizeof(PixSrc)) || + trgPitch < trgWidth * static_cast(sizeof(PixTrg))) + { + assert(false); + return; + } + + yFirst = std::max(yFirst, 0); + yLast = std::min(yLast, trgHeight); + if (yFirst >= yLast || srcHeight <= 0 || srcWidth <= 0) return; + + for (int y = yFirst; y < yLast; ++y) + { + const int ySrc = srcHeight * y / trgHeight; + const PixSrc* const srcLine = byteAdvance(src, ySrc * srcPitch); + PixTrg* const trgLine = byteAdvance(trg, y * trgPitch); + + for (int x = 0; x < trgWidth; ++x) + { + const int xSrc = srcWidth * x / trgWidth; + trgLine[x] = pixCvrt(srcLine[xSrc]); + } + } +} + + +//nearest-neighbor (going over source image - fast for upscaling, since source is read only once +template +void nearestNeighborScaleOverSource(const PixSrc* src, int srcWidth, int srcHeight, int srcPitch, + /**/ PixTrg* trg, int trgWidth, int trgHeight, int trgPitch, + int yFirst, int yLast, PixConverter pixCvrt /*convert PixSrc to PixTrg*/) +{ + static_assert(std::is_integral::value, "PixSrc* is expected to be cast-able to char*"); + static_assert(std::is_integral::value, "PixTrg* is expected to be cast-able to char*"); + + static_assert(std::is_same::value, "PixConverter returning wrong pixel format"); + + if (srcPitch < srcWidth * static_cast(sizeof(PixSrc)) || + trgPitch < trgWidth * static_cast(sizeof(PixTrg))) + { + assert(false); + return; + } + + yFirst = std::max(yFirst, 0); + yLast = std::min(yLast, srcHeight); + if (yFirst >= yLast || trgWidth <= 0 || trgHeight <= 0) return; + + for (int y = yFirst; y < yLast; ++y) + { + //mathematically: ySrc = floor(srcHeight * yTrg / trgHeight) + // => search for integers in: [ySrc, ySrc + 1) * trgHeight / srcHeight + + //keep within for loop to support MT input slices! + const int yTrgFirst = ( y * trgHeight + srcHeight - 1) / srcHeight; //=ceil(y * trgHeight / srcHeight) + const int yTrgLast = ((y + 1) * trgHeight + srcHeight - 1) / srcHeight; //=ceil(((y + 1) * trgHeight) / srcHeight) + const int blockHeight = yTrgLast - yTrgFirst; + + if (blockHeight > 0) + { + const PixSrc* srcLine = byteAdvance(src, y * srcPitch); + /**/ PixTrg* trgLine = byteAdvance(trg, yTrgFirst * trgPitch); + int xTrgFirst = 0; + + for (int x = 0; x < srcWidth; ++x) + { + const int xTrgLast = ((x + 1) * trgWidth + srcWidth - 1) / srcWidth; + const int blockWidth = xTrgLast - xTrgFirst; + if (blockWidth > 0) + { + xTrgFirst = xTrgLast; + + const auto trgPix = pixCvrt(srcLine[x]); + fillBlock(trgLine, trgPitch, trgPix, blockWidth, blockHeight); + trgLine += blockWidth; + } + } + } + } +} + + +template +void bilinearScale(const uint32_t* src, int srcWidth, int srcHeight, int srcPitch, + /**/ PixTrg* trg, int trgWidth, int trgHeight, int trgPitch, + int yFirst, int yLast, PixConverter pixCvrt /*convert uint32_t to PixTrg*/) +{ + static_assert(std::is_integral::value, "PixTrg* is expected to be cast-able to char*"); + static_assert(std::is_same::value, "PixConverter returning wrong pixel format"); + + if (srcPitch < srcWidth * static_cast(sizeof(uint32_t)) || + trgPitch < trgWidth * static_cast(sizeof(PixTrg))) + { + assert(false); + return; + } + + yFirst = std::max(yFirst, 0); + yLast = std::min(yLast, trgHeight); + if (yFirst >= yLast || srcHeight <= 0 || srcWidth <= 0) return; + + const double scaleX = static_cast(trgWidth ) / srcWidth; + const double scaleY = static_cast(trgHeight) / srcHeight; + + //perf notes: + // -> double-based calculation is (slightly) faster than float + // -> precalculation gives significant boost; std::vector<> memory allocation is negligible! + struct CoeffsX + { + int x1; + int x2; + double xx1; + double x2x; + }; + std::vector buf(trgWidth); + for (int x = 0; x < trgWidth; ++x) + { + const int x1 = srcWidth * x / trgWidth; + int x2 = x1 + 1; + if (x2 == srcWidth) --x2; + + const double xx1 = x / scaleX - x1; + const double x2x = 1 - xx1; + + buf[x] = { x1, x2, xx1, x2x }; + } + + for (int y = yFirst; y < yLast; ++y) + { + const int y1 = srcHeight * y / trgHeight; + int y2 = y1 + 1; + if (y2 == srcHeight) --y2; + + const double yy1 = y / scaleY - y1; + const double y2y = 1 - yy1; + + const uint32_t* const srcLine = byteAdvance(src, y1 * srcPitch); + const uint32_t* const srcLineNext = byteAdvance(src, y2 * srcPitch); + PixTrg* const trgLine = byteAdvance(trg, y * trgPitch); + + for (int x = 0; x < trgWidth; ++x) + { + //perf: do NOT "simplify" the variable layout without measurement! + const int x1 = buf[x].x1; + const int x2 = buf[x].x2; + const double xx1 = buf[x].xx1; + const double x2x = buf[x].x2x; + + const double x2xy2y = x2x * y2y; + const double xx1y2y = xx1 * y2y; + const double x2xyy1 = x2x * yy1; + const double xx1yy1 = xx1 * yy1; + + auto interpolate = [=](int offset) + { + /* + https://en.wikipedia.org/wiki/Bilinear_interpolation + (c11(x2 - x) + c21(x - x1)) * (y2 - y ) + + (c12(x2 - x) + c22(x - x1)) * (y - y1) + */ + const auto c11 = (srcLine [x1] >> (8 * offset)) & 0xff; + const auto c21 = (srcLine [x2] >> (8 * offset)) & 0xff; + const auto c12 = (srcLineNext[x1] >> (8 * offset)) & 0xff; + const auto c22 = (srcLineNext[x2] >> (8 * offset)) & 0xff; + + return c11 * x2xy2y + c21 * xx1y2y + + c12 * x2xyy1 + c22 * xx1yy1; + }; + + const double bi = interpolate(0); + const double gi = interpolate(1); + const double ri = interpolate(2); + const double ai = interpolate(3); + + const auto b = static_cast(bi + 0.5); + const auto g = static_cast(gi + 0.5); + const auto r = static_cast(ri + 0.5); + const auto a = static_cast(ai + 0.5); + + const uint32_t trgPix = (a << 24) | (r << 16) | (g << 8) | b; + + trgLine[x] = pixCvrt(trgPix); + } + } +} +} + +#endif //XBRZ_TOOLS_H_825480175091875 From e111e2251cd8cb4be8402672a34b35d0a10e7abf Mon Sep 17 00:00:00 2001 From: "alexey.lysiuk" Date: Fri, 3 Aug 2018 17:52:00 +0300 Subject: [PATCH 26/40] - cleaned up old xBRZ 1.0 upscaler Removed obsolete header comments and support for C++98 Disabled Windows only debug features --- src/textures/hires/xbr/xbrz_config_old.h | 5 -- src/textures/hires/xbr/xbrz_old.cpp | 89 ++++-------------------- src/textures/hires/xbr/xbrz_old.h | 7 +- 3 files changed, 13 insertions(+), 88 deletions(-) diff --git a/src/textures/hires/xbr/xbrz_config_old.h b/src/textures/hires/xbr/xbrz_config_old.h index 480af7976..885dff9f8 100644 --- a/src/textures/hires/xbr/xbrz_config_old.h +++ b/src/textures/hires/xbr/xbrz_config_old.h @@ -11,11 +11,6 @@ // * If you modify this file, you may extend this exception to your version * // * of the file, but you are not obligated to do so. If you do not wish to * // * do so, delete this exception statement from your version. * -// * * -// * An explicit permission was granted to use xBRZ in combination with ZDoom * -// * and derived projects as long as it is used for non-commercial purposes. * -// * * -// * Backported to C++98 by Alexey Lysiuk * // **************************************************************************** #ifndef __XBRZ_CONFIG_OLD_HEADER_INCLUDED__ diff --git a/src/textures/hires/xbr/xbrz_old.cpp b/src/textures/hires/xbr/xbrz_old.cpp index 07527cb95..8eb19bb80 100644 --- a/src/textures/hires/xbr/xbrz_old.cpp +++ b/src/textures/hires/xbr/xbrz_old.cpp @@ -11,11 +11,6 @@ // * If you modify this file, you may extend this exception to your version * // * of the file, but you are not obligated to do so. If you do not wish to * // * do so, delete this exception statement from your version. * -// * * -// * An explicit permission was granted to use xBRZ in combination with ZDoom * -// * and derived projects as long as it is used for non-commercial purposes. * -// * * -// * Backported to C++98 by Alexey Lysiuk * // **************************************************************************** #include "xbrz_old.h" @@ -24,10 +19,6 @@ #include #include -#if __cplusplus > 199711 -#define XBRZ_CXX11 -#endif // __cplusplus > 199711 - namespace { template inline @@ -40,9 +31,7 @@ inline unsigned char getBlue (uint32_t val) { return getByte<0>(val); } template inline T abs(T value) { -#ifdef XBRZ_CXX11 - static_assert(std::numeric_limits::is_signed, ""); -#endif // XBRZ_CXX11 + static_assert(std::is_signed::value, ""); return value < 0 ? -value : value; } @@ -53,11 +42,9 @@ const uint32_t blueMask = 0x0000ff; template inline void alphaBlend(uint32_t& dst, uint32_t col) //blend color over destination with opacity N / M { -#ifdef XBRZ_CXX11 static_assert(N < 256, "possible overflow of (col & redMask) * N"); static_assert(M < 256, "possible overflow of (col & redMask ) * N + (dst & redMask ) * (M - N)"); static_assert(0 < N && N < M, ""); -#endif // XBRZ_CXX11 static const uint32_t ALPHA_MASK = 0xFF000000; static const uint32_t ALPHA_SHIFT = 24; @@ -526,20 +513,12 @@ BlendResult preProcessCorners(const Kernel_4x4& ker, const xbrz_old::ScalerCfg& ker.g == ker.k)) return result; -#ifdef XBRZ_CXX11 auto dist = [&](uint32_t col1, uint32_t col2) { return colorDist(col1, col2, cfg.luminanceWeight_); }; -#else // !XBRZ_CXX11 -#define dist(C1, C2) colorDist((C1), (C2), cfg.luminanceWeight_) -#endif // XBRZ_CXX11 const int weight = 4; double jg = dist(ker.i, ker.f) + dist(ker.f, ker.c) + dist(ker.n, ker.k) + dist(ker.k, ker.h) + weight * dist(ker.j, ker.g); double fk = dist(ker.e, ker.j) + dist(ker.j, ker.o) + dist(ker.b, ker.g) + dist(ker.g, ker.l) + weight * dist(ker.f, ker.k); -#ifndef XBRZ_CXX11 -#undef dist -#endif // !XBRZ_CXX11 - if (jg < fk) //test sample: 70% of values max(jg, fk) / min(jg, fk) are between 1.1 and 3.7 with median being 1.8 { const bool dominantGradient = cfg.dominantDirectionThreshold * jg < fk; @@ -621,42 +600,6 @@ int debugPixelY = 84; bool breakIntoDebugger = false; #endif -#define a get_a(ker) -#define b get_b(ker) -#define c get_c(ker) -#define d get_d(ker) -#define e get_e(ker) -#define f get_f(ker) -#define g get_g(ker) -#define h get_h(ker) -#define i get_i(ker) - -#ifndef XBRZ_CXX11 - -template -bool doLineBlend(const Kernel_3x3& ker, const xbrz_old::ScalerCfg& cfg, const unsigned char blend) -{ - if (getBottomR(blend) >= BLEND_DOMINANT) - return true; - -#define eq(C1, C2) (colorDist((C1), (C2), cfg.luminanceWeight_) < cfg.equalColorTolerance_) - - //make sure there is no second blending in an adjacent rotation for this pixel: handles insular pixels, mario eyes - if (getTopR(blend) != BLEND_NONE && !eq(e, g)) //but support double-blending for 90 degree corners - return false; - if (getBottomL(blend) != BLEND_NONE && !eq(e, c)) - return false; - - //no full blending for L-shapes; blend corner only (handles "mario mushroom eyes") - if (eq(g, h) && eq(h , i) && eq(i, f) && eq(f, c) && !eq(e, i)) - return false; - -#undef eq - - return true; -}; - -#endif // !XBRZ_CXX11 /* input kernel area naming convention: @@ -675,20 +618,25 @@ void scalePixel(const Kernel_3x3& ker, unsigned char blendInfo, //result of preprocessing all four corners of pixel "e" const xbrz_old::ScalerCfg& cfg) { -#ifndef NDEBUG +#define a get_a(ker) +#define b get_b(ker) +#define c get_c(ker) +#define d get_d(ker) +#define e get_e(ker) +#define f get_f(ker) +#define g get_g(ker) +#define h get_h(ker) +#define i get_i(ker) + +#if 0 //#ifndef NDEBUG if (breakIntoDebugger) -#ifdef _MSC_VER __debugbreak(); //__asm int 3; -#else // !_MSC_VER - __builtin_trap(); -#endif // _MSC_VER #endif const unsigned char blend = rotateBlendInfo(blendInfo); if (getBottomR(blend) >= BLEND_NORMAL) { -#ifdef XBRZ_CXX11 auto eq = [&](uint32_t col1, uint32_t col2) { return colorDist(col1, col2, cfg.luminanceWeight_) < cfg.equalColorTolerance_; }; auto dist = [&](uint32_t col1, uint32_t col2) { return colorDist(col1, col2, cfg.luminanceWeight_); }; @@ -709,19 +657,12 @@ void scalePixel(const Kernel_3x3& ker, return true; }(); -#else // !XBRZ_CXX11 -#define dist(C1, C2) colorDist((C1), (C2), cfg.luminanceWeight_) -#endif // XBRZ_CXX11 const uint32_t px = dist(e, f) <= dist(e, h) ? f : h; //choose most similar color OutputMatrix out(target, trgWidth); -#ifdef XBRZ_CXX11 if (doLineBlend) -#else // !XBRZ_CXX11 - if (doLineBlend(ker, cfg, blend)) -#endif // XBRZ_CXX11 { const double fg = dist(f, g); //test sample: 70% of values max(fg, hc) / min(fg, hc) are between 1.1 and 3.7 with median being 1.9 const double hc = dist(h, c); // @@ -748,10 +689,6 @@ void scalePixel(const Kernel_3x3& ker, Scaler::blendCorner(px, out); } -#ifndef XBRZ_CXX11 -#undef dist -#endif // XBRZ_CXX11 - #undef a #undef b #undef c @@ -779,9 +716,7 @@ void scaleImage(const uint32_t* src, uint32_t* trg, int srcWidth, int srcHeight, const int bufferSize = srcWidth; unsigned char* preProcBuffer = reinterpret_cast(trg + yLast * Scaler::scale * trgWidth) - bufferSize; std::fill(preProcBuffer, preProcBuffer + bufferSize, 0); -#ifdef XBRZ_CXX11 static_assert(BLEND_NONE == 0, ""); -#endif // XBRZ_CXX11 //initialize preprocessing buffer for first row: detect upper left and right corner blending //this cannot be optimized for adjacent processing stripes; we must not allow for a memory race condition! diff --git a/src/textures/hires/xbr/xbrz_old.h b/src/textures/hires/xbr/xbrz_old.h index c93a1480a..9a46f2a97 100644 --- a/src/textures/hires/xbr/xbrz_old.h +++ b/src/textures/hires/xbr/xbrz_old.h @@ -11,18 +11,13 @@ // * If you modify this file, you may extend this exception to your version * // * of the file, but you are not obligated to do so. If you do not wish to * // * do so, delete this exception statement from your version. * -// * * -// * An explicit permission was granted to use xBRZ in combination with ZDoom * -// * and derived projects as long as it is used for non-commercial purposes. * -// * * -// * Backported to C++98 by Alexey Lysiuk * // **************************************************************************** #ifndef __XBRZ_OLD_HEADER_INCLUDED__ #define __XBRZ_OLD_HEADER_INCLUDED__ #include //size_t -#include //uint32_t +#include //uint32_t #include #include "xbrz_config_old.h" From 36adbcd9ed6a59c3061620c0065d7e1bfa7b3240 Mon Sep 17 00:00:00 2001 From: "alexey.lysiuk" Date: Fri, 3 Aug 2018 18:02:23 +0300 Subject: [PATCH 27/40] - localized names for xBRZ scaling modes --- wadsrc/static/language.enu | 6 ++++++ wadsrc/static/menudef.txt | 24 ++++++++++++------------ 2 files changed, 18 insertions(+), 12 deletions(-) diff --git a/wadsrc/static/language.enu b/wadsrc/static/language.enu index 065d547be..7ec1b5489 100644 --- a/wadsrc/static/language.enu +++ b/wadsrc/static/language.enu @@ -2877,6 +2877,12 @@ OPTVAL_HQ4X = "hq4x"; OPTVAL_HQ2XMMX = "hq2x MMX"; OPTVAL_HQ3XMMX = "hq3x MMX"; OPTVAL_HQ4XMMX = "hq4x MMX"; +OPTVAL_2XBRZ = "2xBRZ"; +OPTVAL_3XBRZ = "3xBRZ"; +OPTVAL_4XBRZ = "4xBRZ"; +OPTVAL_OLD_2XBRZ = "Old 2xBRZ"; +OPTVAL_OLD_3XBRZ = "Old 3xBRZ"; +OPTVAL_OLD_4XBRZ = "Old 4xBRZ"; OPTVAL_RADIAL = "Radial"; OPTVAL_PIXELFUZZ = "Pixel fuzz"; OPTVAL_SMOOTHFUZZ = "Smooth fuzz"; diff --git a/wadsrc/static/menudef.txt b/wadsrc/static/menudef.txt index 9e3f801cc..5fc1b0f66 100644 --- a/wadsrc/static/menudef.txt +++ b/wadsrc/static/menudef.txt @@ -2155,12 +2155,12 @@ OptionValue "HqResizeModes" 7, "$OPTVAL_HQ2XMMX" 8, "$OPTVAL_HQ3XMMX" 9, "$OPTVAL_HQ4XMMX" - 10, "xBRZ 2x" - 11, "xBRZ 3x" - 12, "xBRZ 4x" - 13, "xBRZ_old 2x" - 14, "xBRZ_old 3x" - 15, "xBRZ_old 4x" + 10, "$OPTVAL_2XBRZ" + 11, "$OPTVAL_3XBRZ" + 12, "$OPTVAL_4XBRZ" + 13, "$OPTVAL_OLD_2XBRZ" + 14, "$OPTVAL_OLD_3XBRZ" + 15, "$OPTVAL_OLD_4XBRZ" } OptionValue "HqResizeModesNoMMX" @@ -2172,12 +2172,12 @@ OptionValue "HqResizeModesNoMMX" 4, "$OPTVAL_HQ2X" 5, "$OPTVAL_HQ3X" 6, "$OPTVAL_HQ4X" - 10, "xBRZ 2x" - 11, "xBRZ 3x" - 12, "xBRZ 4x" - 13, "xBRZ_old 2x" - 14, "xBRZ_old 3x" - 15, "xBRZ_old 4x" + 10, "$OPTVAL_2XBRZ" + 11, "$OPTVAL_3XBRZ" + 12, "$OPTVAL_4XBRZ" + 13, "$OPTVAL_OLD_2XBRZ" + 14, "$OPTVAL_OLD_3XBRZ" + 15, "$OPTVAL_OLD_4XBRZ" } OptionValue "FogMode" From 778a7c370d811ed977d34f27b38d19243706cde8 Mon Sep 17 00:00:00 2001 From: "alexey.lysiuk" Date: Fri, 3 Aug 2018 18:14:53 +0300 Subject: [PATCH 28/40] - added 5x and 6x upscaling with xBRZ --- src/textures/hires/hqresize.cpp | 9 +++++++-- wadsrc/static/language.enu | 4 ++++ wadsrc/static/menudef.txt | 8 ++++++++ 3 files changed, 19 insertions(+), 2 deletions(-) diff --git a/src/textures/hires/hqresize.cpp b/src/textures/hires/hqresize.cpp index a898b9fb6..22f9f8f2f 100644 --- a/src/textures/hires/hqresize.cpp +++ b/src/textures/hires/hqresize.cpp @@ -47,7 +47,7 @@ CUSTOM_CVAR(Int, gl_texture_hqresize, 0, CVAR_ARCHIVE | CVAR_GLOBALCONFIG | CVAR_NOINITCALL) { - if (self < 0 || self > 16) + if (self < 0 || self > 19) { self = 0; } @@ -389,8 +389,13 @@ unsigned char *FTexture::CreateUpsampledTextureBuffer (unsigned char *inputBuffe case 13: case 14: case 15: + case 16: + case 17: return xbrzHelper(xbrzOldScale, type - 11, inputBuffer, inWidth, inHeight, outWidth, outHeight ); - + + case 18: + case 19: + return xbrzHelper(xbrz::scale, type - 13, inputBuffer, inWidth, inHeight, outWidth, outHeight); } } return inputBuffer; diff --git a/wadsrc/static/language.enu b/wadsrc/static/language.enu index 7ec1b5489..a700c6db1 100644 --- a/wadsrc/static/language.enu +++ b/wadsrc/static/language.enu @@ -2880,9 +2880,13 @@ OPTVAL_HQ4XMMX = "hq4x MMX"; OPTVAL_2XBRZ = "2xBRZ"; OPTVAL_3XBRZ = "3xBRZ"; OPTVAL_4XBRZ = "4xBRZ"; +OPTVAL_5XBRZ = "5xBRZ"; +OPTVAL_6XBRZ = "6xBRZ"; OPTVAL_OLD_2XBRZ = "Old 2xBRZ"; OPTVAL_OLD_3XBRZ = "Old 3xBRZ"; OPTVAL_OLD_4XBRZ = "Old 4xBRZ"; +OPTVAL_OLD_5XBRZ = "Old 5xBRZ"; +OPTVAL_OLD_6XBRZ = "Old 6xBRZ"; OPTVAL_RADIAL = "Radial"; OPTVAL_PIXELFUZZ = "Pixel fuzz"; OPTVAL_SMOOTHFUZZ = "Smooth fuzz"; diff --git a/wadsrc/static/menudef.txt b/wadsrc/static/menudef.txt index 5fc1b0f66..6c3571adc 100644 --- a/wadsrc/static/menudef.txt +++ b/wadsrc/static/menudef.txt @@ -2158,9 +2158,13 @@ OptionValue "HqResizeModes" 10, "$OPTVAL_2XBRZ" 11, "$OPTVAL_3XBRZ" 12, "$OPTVAL_4XBRZ" + 18, "$OPTVAL_5XBRZ" + 19, "$OPTVAL_6XBRZ" 13, "$OPTVAL_OLD_2XBRZ" 14, "$OPTVAL_OLD_3XBRZ" 15, "$OPTVAL_OLD_4XBRZ" + 16, "$OPTVAL_OLD_5XBRZ" + 17, "$OPTVAL_OLD_6XBRZ" } OptionValue "HqResizeModesNoMMX" @@ -2175,9 +2179,13 @@ OptionValue "HqResizeModesNoMMX" 10, "$OPTVAL_2XBRZ" 11, "$OPTVAL_3XBRZ" 12, "$OPTVAL_4XBRZ" + 18, "$OPTVAL_5XBRZ" + 19, "$OPTVAL_6XBRZ" 13, "$OPTVAL_OLD_2XBRZ" 14, "$OPTVAL_OLD_3XBRZ" 15, "$OPTVAL_OLD_4XBRZ" + 16, "$OPTVAL_OLD_5XBRZ" + 17, "$OPTVAL_OLD_6XBRZ" } OptionValue "FogMode" From c589f1bba76654077451e431e1db5f913f4a1234 Mon Sep 17 00:00:00 2001 From: "alexey.lysiuk" Date: Sat, 4 Aug 2018 13:30:04 +0300 Subject: [PATCH 29/40] - added a warning for texture upscaling modes --- wadsrc/static/language.enu | 1 + wadsrc/static/menudef.txt | 3 + wadsrc/static/zscript/menu/optionmenu.txt | 72 +++++++++++++++++++++++ 3 files changed, 76 insertions(+) diff --git a/wadsrc/static/language.enu b/wadsrc/static/language.enu index a700c6db1..7880ea125 100644 --- a/wadsrc/static/language.enu +++ b/wadsrc/static/language.enu @@ -2771,6 +2771,7 @@ GLTEXMNU_ANISOTROPIC = "Anisotropic filter"; GLTEXMNU_TEXFORMAT = "Texture Format"; GLTEXMNU_ENABLEHIRES = "Enable hires textures"; GLTEXMNU_HQRESIZE = "High Quality Resize mode"; +GLTEXMNU_HQRESIZEWARN = "This mode requires %d times more video memory"; GLTEXMNU_RESIZETEX = "Resize textures"; GLTEXMNU_RESIZESPR = "Resize sprites"; GLTEXMNU_RESIZEFNT = "Resize fonts"; diff --git a/wadsrc/static/menudef.txt b/wadsrc/static/menudef.txt index 6c3571adc..9b527e2eb 100644 --- a/wadsrc/static/menudef.txt +++ b/wadsrc/static/menudef.txt @@ -2256,12 +2256,15 @@ OptionMenu "GLTextureGLOptions" protected { Option "$GLTEXMNU_HQRESIZE", gl_texture_hqresize, "HqResizeModesNoMMX" } + StaticText "!HQRESIZE_WARNING!" + Option "$GLTEXMNU_RESIZETEX", gl_texture_hqresize_textures, "OnOff" Option "$GLTEXMNU_RESIZESPR", gl_texture_hqresize_sprites, "OnOff" Option "$GLTEXMNU_RESIZEFNT", gl_texture_hqresize_fonts, "OnOff" Option "$GLTEXMNU_PRECACHETEX", gl_precache, "YesNo" Option "$GLTEXMNU_TRIMSPREDGE", gl_trimsprites, "OnOff" Option "$GLTEXMNU_SORTDRAWLIST", gl_sort_textures, "YesNo" + Class "GLTextureGLOptions" } OptionMenu "GLLightOptions" protected diff --git a/wadsrc/static/zscript/menu/optionmenu.txt b/wadsrc/static/zscript/menu/optionmenu.txt index ad8aa1418..a06a31b6a 100644 --- a/wadsrc/static/zscript/menu/optionmenu.txt +++ b/wadsrc/static/zscript/menu/optionmenu.txt @@ -536,3 +536,75 @@ class CompatibilityMenu : OptionMenu DTA_CleanNoMove_1, true); } } + +class GLTextureGLOptions : OptionMenu +{ + private int mWarningIndex; + + override void Init(Menu parent, OptionMenuDescriptor desc) + { + super.Init(parent, desc); + + for (int i=0; i < mDesc.mItems.Size(); ++i) + { + if (mDesc.mItems[i].mLabel == "!HQRESIZE_WARNING!") + { + mWarningIndex = i; + break; + } + } + } + + override void Ticker() + { + Super.Ticker(); + + if (mWarningIndex > 0) + { + string message; + + if (gl_texture_hqresize > 0) + { + int multiplier; + + switch (gl_texture_hqresize) + { + case 1: + case 4: + case 7: + case 10: + case 13: + multiplier = 4; + break; + case 2: + case 5: + case 8: + case 11: + case 14: + multiplier = 9; + break; + case 3: + case 6: + case 9: + case 12: + case 15: + multiplier = 16; + break; + case 16: + case 18: + multiplier = 25; + break; + case 17: + case 19: + multiplier = 36; + break; + } + + string localized = StringTable.Localize("$GLTEXMNU_HQRESIZEWARN"); + message = String.Format(localized, multiplier); + } + + mDesc.mItems[mWarningIndex].mLabel = message; + } + } +} From b6bcc1b0f54284272606c761ddabad12821e6aa8 Mon Sep 17 00:00:00 2001 From: Rachael Alexanderson Date: Tue, 25 Sep 2018 03:34:23 -0400 Subject: [PATCH 30/40] Add 'normalNx' texture scaling --- src/textures/hires/hqresize.cpp | 47 +++++++++++++++++++++++++++++++-- wadsrc/static/language.enu | 3 +++ wadsrc/static/menudef.txt | 3 +++ 3 files changed, 51 insertions(+), 2 deletions(-) diff --git a/src/textures/hires/hqresize.cpp b/src/textures/hires/hqresize.cpp index 22f9f8f2f..6bcb2d805 100644 --- a/src/textures/hires/hqresize.cpp +++ b/src/textures/hires/hqresize.cpp @@ -47,7 +47,7 @@ CUSTOM_CVAR(Int, gl_texture_hqresize, 0, CVAR_ARCHIVE | CVAR_GLOBALCONFIG | CVAR_NOINITCALL) { - if (self < 0 || self > 19) + if (self < 0 || self > 22) { self = 0; } @@ -187,7 +187,6 @@ static void scale4x ( uint32_t* inputBuffer, uint32_t* outputBuffer, int inWidth delete[] buffer2x; } - static unsigned char *scaleNxHelper( void (*scaleNxFunction) ( uint32_t* , uint32_t* , int , int), const int N, unsigned char *inputBuffer, @@ -205,6 +204,44 @@ static unsigned char *scaleNxHelper( void (*scaleNxFunction) ( uint32_t* , uint3 return newBuffer; } +static void normalNx ( uint32_t* inputBuffer, uint32_t* outputBuffer, int inWidth, int inHeight, int size ) +{ + const int width = size * inWidth; + const int height = size * inHeight; + + for ( int i = 0; i < inWidth; ++i ) + { + for ( int j = 0; j < inHeight; ++j ) + { + const uint32_t E = inputBuffer[ i +inWidth*j ]; + for ( int k = 0; k < size; k++ ) + { + for ( int l = 0; l < size; l++ ) + { + outputBuffer[size*i+k + width*(size*j+l)] = E; + } + } + } + } +} + +static unsigned char *normalNxHelper( void (normalNxFunction) ( uint32_t* , uint32_t* , int , int, int), + const int N, + unsigned char *inputBuffer, + const int inWidth, + const int inHeight, + int &outWidth, + int &outHeight ) +{ + outWidth = N * inWidth; + outHeight = N *inHeight; + unsigned char * newBuffer = new unsigned char[outWidth*outHeight*4]; + + normalNxFunction ( reinterpret_cast ( inputBuffer ), reinterpret_cast ( newBuffer ), inWidth, inHeight, N ); + delete[] inputBuffer; + return newBuffer; +} + #ifdef HAVE_MMX static unsigned char *hqNxAsmHelper( void (*hqNxFunction) ( int*, unsigned char*, int, int, int ), const int N, @@ -396,6 +433,12 @@ unsigned char *FTexture::CreateUpsampledTextureBuffer (unsigned char *inputBuffe case 18: case 19: return xbrzHelper(xbrz::scale, type - 13, inputBuffer, inWidth, inHeight, outWidth, outHeight); + case 20: + return normalNxHelper( &normalNx, 2, inputBuffer, inWidth, inHeight, outWidth, outHeight ); + case 21: + return normalNxHelper( &normalNx, 3, inputBuffer, inWidth, inHeight, outWidth, outHeight ); + case 22: + return normalNxHelper( &normalNx, 4, inputBuffer, inWidth, inHeight, outWidth, outHeight ); } } return inputBuffer; diff --git a/wadsrc/static/language.enu b/wadsrc/static/language.enu index 7880ea125..b444f27db 100644 --- a/wadsrc/static/language.enu +++ b/wadsrc/static/language.enu @@ -2872,6 +2872,9 @@ OPTVAL_ROUND = "Round"; OPTVAL_SCALE2X = "Scale2x"; OPTVAL_SCALE3X = "Scale3x"; OPTVAL_SCALE4X = "Scale4x"; +OPTVAL_NORMAL2X = "Normal2x"; +OPTVAL_NORMAL3X = "Normal3x"; +OPTVAL_NORMAL4X = "Normal4x"; OPTVAL_HQ2X = "hq2x"; OPTVAL_HQ3X = "hq3x"; OPTVAL_HQ4X = "hq4x"; diff --git a/wadsrc/static/menudef.txt b/wadsrc/static/menudef.txt index 9b527e2eb..931ba51a1 100644 --- a/wadsrc/static/menudef.txt +++ b/wadsrc/static/menudef.txt @@ -2165,6 +2165,9 @@ OptionValue "HqResizeModes" 15, "$OPTVAL_OLD_4XBRZ" 16, "$OPTVAL_OLD_5XBRZ" 17, "$OPTVAL_OLD_6XBRZ" + 20, "$OPTVAL_NORMAL2X" + 21, "$OPTVAL_NORMAL3X" + 22, "$OPTVAL_NORMAL4X" } OptionValue "HqResizeModesNoMMX" From 2fff5c4c3926b6aa9c66e08c3d3191366d268b94 Mon Sep 17 00:00:00 2001 From: Rachael Alexanderson Date: Thu, 27 Sep 2018 05:46:24 -0400 Subject: [PATCH 31/40] - use correct multipliers for the memory warnings for normalNx scalers --- wadsrc/static/zscript/menu/optionmenu.txt | 3 +++ 1 file changed, 3 insertions(+) diff --git a/wadsrc/static/zscript/menu/optionmenu.txt b/wadsrc/static/zscript/menu/optionmenu.txt index a06a31b6a..ede7c0217 100644 --- a/wadsrc/static/zscript/menu/optionmenu.txt +++ b/wadsrc/static/zscript/menu/optionmenu.txt @@ -574,6 +574,7 @@ class GLTextureGLOptions : OptionMenu case 7: case 10: case 13: + case 20: multiplier = 4; break; case 2: @@ -581,6 +582,7 @@ class GLTextureGLOptions : OptionMenu case 8: case 11: case 14: + case 21: multiplier = 9; break; case 3: @@ -588,6 +590,7 @@ class GLTextureGLOptions : OptionMenu case 9: case 12: case 15: + case 22: multiplier = 16; break; case 16: From 59c8d8ff649254534968e91b52054ab9071fdbfd Mon Sep 17 00:00:00 2001 From: Vitaly Novichkov Date: Tue, 2 Oct 2018 00:07:05 +0300 Subject: [PATCH 32/40] Upgrade libADLMIDI and libOPNMIDI Added full-panning stereo, improvement of channel management, and many other things. Also, I have implemented an ability to use custom WOPL (for libADLMIDI) and WOPN (for libOPNMIDI) banks from the same path as "soundfonts", but also, in the same environment, the "fm_banks" folder was added for WOPL/WOPN storing purposes. To toggle usage of embedded or custom bank, I have added togglable booleans. When bank fails to be loaded, the default embedded bank is getting to be used as fallback. ADLMIDI 1.4.0 2018-10-01 * Implemented a full support for Portamento! (Thanks to [Jean Pierre Cimalando](https://github.com/jpcima) for a work!) * Added support for SysEx event handling! (Thanks to [Jean Pierre Cimalando](https://github.com/jpcima) for a work!) * Added support for GS way of custom drum channels (through SysEx events) * Ignore some NRPN events and lsb bank number when using GS standard (after catching of GS Reset SysEx call) * Added support for CC66-Sostenuto controller (Pedal hold of currently-pressed notes only while CC64 holds also all next notes) * Added support for CC67-SoftPedal controller (SoftPedal lowers the volume of notes played) * Fixed correctness of CMF files playing * Fixed unnecessary overuse of chip channels by blank notes * Added API to disable specific MIDI tracks or play one of MIDI tracks solo * Added support for more complex loop (loopStart=XX, loopEnd=0). Where XX - count of loops, or 0 - infinite. Nested loops are supported without of any limits. * Added working implementation of TMB's velocity offset * Added support for full-panning stereo option (Thanks to [Christopher Snowhill](https://github.com/kode54) for a work!) * Fixed inability to play high notes due physical tone frequency out of range on the OPL3 chip OPNMIDI 1.4.0 2018-10-01 * Implemented a full support for Portamento! (Thanks to [Jean Pierre Cimalando](https://github.com/jpcima) for a work!) * Added support for SysEx event handling! (Thanks to [Jean Pierre Cimalando](https://github.com/jpcima) for a work!) * Added support for GS way of custom drum channels (through SysEx events) * Ignore some NRPN events and lsb bank number when using GS standard (after catching of GS Reset SysEx call) * Added support for CC66-Sostenuto controller (Pedal hold of currently-pressed notes only while CC64 holds also all next notes) * Added support for CC67-SoftPedal controller (SoftPedal lowers the volume of notes played) * Resolved a trouble which sometimes makes a junk noise sound and unnecessary overuse of chip channels * Volume models support taken from libADLMIDI has been adapted to OPN2's chip speficis * Fixed inability to play high notes due physical tone frequency out of range on the OPN2 chip * Added support for full-panning stereo option ADL&OPN Hotfix: re-calculated default banks The fix on side of measurer of OPL3-BE and OPN2-BE where some instruments getting zero releasing time. --- fm_banks/GENMIDI.GS.wopl | Bin 0 -> 118767 bytes fm_banks/gs-by-papiezak-and-sneakernets.wopn | Bin 0 -> 274864 bytes src/CMakeLists.txt | 17 +- src/gameconfigfile.cpp | 10 + src/menu/menudef.cpp | 10 +- src/sound/adlmidi/adldata.cpp | 10069 ++++++++-------- src/sound/adlmidi/adldata.hh | 55 +- src/sound/adlmidi/adlmidi.cpp | 1010 +- src/sound/adlmidi/adlmidi.h | 1279 +- src/sound/adlmidi/adlmidi.hpp | 4 +- src/sound/adlmidi/adlmidi_bankmap.h | 4 +- src/sound/adlmidi/adlmidi_bankmap.tcc | 2 +- src/sound/adlmidi/adlmidi_cvt.hpp | 124 + src/sound/adlmidi/adlmidi_load.cpp | 656 +- src/sound/adlmidi/adlmidi_midiplay.cpp | 2574 ++-- src/sound/adlmidi/adlmidi_opl3.cpp | 621 +- src/sound/adlmidi/adlmidi_private.cpp | 40 +- src/sound/adlmidi/adlmidi_private.hpp | 1364 ++- src/sound/adlmidi/chips/dosbox/dbopl.cpp | 169 +- src/sound/adlmidi/chips/dosbox/dbopl.h | 20 +- src/sound/adlmidi/chips/dosbox_opl3.cpp | 28 +- src/sound/adlmidi/chips/dosbox_opl3.h | 21 + src/sound/adlmidi/chips/nuked/nukedopl3.c | 62 +- src/sound/adlmidi/chips/nuked/nukedopl3.h | 19 +- src/sound/adlmidi/chips/nuked/nukedopl3_174.c | 49 +- src/sound/adlmidi/chips/nuked/nukedopl3_174.h | 2 + src/sound/adlmidi/chips/nuked_opl3.cpp | 26 + src/sound/adlmidi/chips/nuked_opl3.h | 21 + src/sound/adlmidi/chips/nuked_opl3_v174.cpp | 26 + src/sound/adlmidi/chips/nuked_opl3_v174.h | 21 + src/sound/adlmidi/chips/opl_chip_base.h | 21 + src/sound/adlmidi/chips/opl_chip_base.tcc | 4 +- src/sound/adlmidi/file_reader.hpp | 300 + src/sound/adlmidi/fraction.hpp | 215 - src/sound/adlmidi/wopl/wopl_file.c | 10 +- src/sound/adlmidi/wopl/wopl_file.h | 11 +- src/sound/i_musicinterns.h | 8 +- src/sound/i_soundfont.cpp | 17 +- src/sound/i_soundfont.h | 4 +- .../mididevices/music_adlmidi_mididevice.cpp | 42 +- .../mididevices/music_opnmidi_mididevice.cpp | 58 +- src/sound/opnmidi/chips/gens/Ym2612_Emu.cpp | 53 +- src/sound/opnmidi/chips/gens/Ym2612_Emu.h | 3 + src/sound/opnmidi/chips/gens_opn2.cpp | 25 + src/sound/opnmidi/chips/gens_opn2.h | 21 + src/sound/opnmidi/chips/mame/mame_ym2612fm.c | 85 +- src/sound/opnmidi/chips/mame/mame_ym2612fm.h | 5 +- src/sound/opnmidi/chips/mame_opn2.cpp | 25 + src/sound/opnmidi/chips/mame_opn2.h | 21 + src/sound/opnmidi/chips/nuked/ym3438.c | 67 +- src/sound/opnmidi/chips/nuked/ym3438.h | 24 +- src/sound/opnmidi/chips/nuked_opn2.cpp | 26 + src/sound/opnmidi/chips/nuked_opn2.h | 21 + src/sound/opnmidi/chips/opn_chip_base.h | 43 + src/sound/opnmidi/chips/opn_chip_base.tcc | 40 +- src/sound/opnmidi/file_reader.hpp | 300 + src/sound/opnmidi/fraction.hpp | 214 - src/sound/opnmidi/opnbank.h | 23 +- src/sound/opnmidi/opnmidi.cpp | 843 +- src/sound/opnmidi/opnmidi.h | 1050 +- src/sound/opnmidi/opnmidi_bankmap.h | 2 +- src/sound/opnmidi/opnmidi_cvt.hpp | 82 + src/sound/opnmidi/opnmidi_load.cpp | 603 +- src/sound/opnmidi/opnmidi_midiplay.cpp | 2370 ++-- src/sound/opnmidi/opnmidi_opn2.cpp | 378 +- src/sound/opnmidi/opnmidi_private.cpp | 33 +- src/sound/opnmidi/opnmidi_private.hpp | 1251 +- src/sound/opnmidi/wopn/wopn_file.c | 622 + src/sound/opnmidi/wopn/wopn_file.h | 250 + wadsrc/static/language.enu | 4 + wadsrc/static/menudef.txt | 26 +- wadsrc/static/xg.wopn | Bin 62080 -> 62080 bytes 72 files changed, 15167 insertions(+), 12336 deletions(-) create mode 100644 fm_banks/GENMIDI.GS.wopl create mode 100644 fm_banks/gs-by-papiezak-and-sneakernets.wopn create mode 100644 src/sound/adlmidi/adlmidi_cvt.hpp create mode 100644 src/sound/adlmidi/file_reader.hpp delete mode 100644 src/sound/adlmidi/fraction.hpp create mode 100644 src/sound/opnmidi/file_reader.hpp delete mode 100644 src/sound/opnmidi/fraction.hpp create mode 100644 src/sound/opnmidi/opnmidi_cvt.hpp create mode 100644 src/sound/opnmidi/wopn/wopn_file.c create mode 100644 src/sound/opnmidi/wopn/wopn_file.h diff --git a/fm_banks/GENMIDI.GS.wopl b/fm_banks/GENMIDI.GS.wopl new file mode 100644 index 0000000000000000000000000000000000000000..fb9646f947fa95daa6072f0132990358eb1b41df GIT binary patch literal 118767 zcmeHwYjj-ImF7P8Rkuo|w`|!4>|2s;#DHm)D3b)2)HMcG&awkmCOo>dCg3w^?pD=v(K$k z=}5X&(pug0fV1z9($=kW_uc0^-`Qv1b9SA(byLmtr7J4Rzq1i!fHeLw`%z(rb{gLa zUuj&Gq^zDO;azQCBx|xnu~Af4x#Fs;E}L4?l_*gXFJRO}!eSvQQKB?S3E3(ULQ6|l z+80@n(BZW&04!0WG>IX`WQinqDM*x99ly(}VzDLw+SJP1{Z|w2@<)t~fv8brsq?O$ zsv-;OYgb%Ve3ek7WYv@+3!&T>?(B#Le8#mAGt^|%1k6x)#=RtvHO=en8&pc)Jy|Gu zU44V#ee2mmSkoX~R}l#`w?vJEB3l)xLANwO>AQWU3ThZuO5W-lkgho`70kGw-3b|F zMafqCUr=(+ojfIFsb^o1a{lrDQ_70pv$E=NXy@(~QT$9&1Fq2pvdfjNpB+@Z_kGp@ z*-n&7?)|JoSsRfKR0aLMXasSv%r}Z^Tf$BLjwxUP_66Auth)bYSQPI6Sjz53Df{@d zAHu~)p=r8?bvLe9Gwpf+1>_9yy87RP)sGFnA!TRwje=)!?;CKu44~SKv~>i0E#XL$ z9Rd^Qur0FF&kiXoH}@5)D2=d^jenA*RktvuGHBjDy8>)YXSUuq0&HF1O{k%7M1gNZ zFI@c`?633({T)$r;$HTD8OTnjSMNU$j{4z)U`hEXsk|1m9pi)JgIETU%+X%$hi{)8 z_#yN-22gru@Q08aO|PC!gY`YOg%fMBM}qw0ry7-P#~}&kA3N0ui%+;x!Y62#Gub}1 ze;A(o1V8>p?RCJZDwK=^@VrpUUUbK-z;4?8;Rr174G)9T{k{V-zB~AYs#L+fH|`FG z+gidQzy0+v{zs7TD_LQ=^576GS~4;$&4ZB2|JSH!o+C{Nvm&4hX?)ujzaW*z27Q56ebhn`))%tI(We&|`9p@wVhFPc1S)k4L7!I-0W6iWQ~;dJ!su(T2- z)Ua)`Yzhf&P?Tr04(rgi?wJMf9GBWRKC^(^&`{aZ8S%BGssw6A9MaQDy6f|px2ITP z#at@wsZmgaq}TfWO|x8Y^=?JG^RDgrjBV@AXZyGW_1*aj+Hh~V&xaj55Ke+(tZGi~ zX-o9-<;-)#lgpq~UystJ1Iu_V_NS=5_?7I|J$mCJD0sN{Go>I0r7IpASGk4_^=7C! zjIKJRB@W~RlsIZt$C)T-zClBD0D*qJDpHnx{k$qd~T#|2!)_*7u>Boz{O|E&YDqhiu_b*?&)X+6fIi z%sqQV1@XR#-oCs45O{vryM+7dLr{2Y?-G{vJJ>cugD>beL!t_-8+sT`%66UmSk6B9 z#}bxZb?#%9)BVR1`Qqnb?+gvCol$?&G!3Xj&+r}?7y|Dj=h5ky^7?ITbID3u-9H^TeH-gSYFXgCr@N+r?Y zMh$nPr*)T=@#`FT9mpubgogNyao}kUhXTHiDFztv_RZwu=Ai4@4caI2;Sp_cBbO>s zLL073v}T3b(GkC3#vklipm^2OA1bB3hqi-PJ$W8HZ|~o3jd}?rxdx-?wm_)a9}$6c zqlZDeT`sSRqh;}9y<-5GFO50!p|{sjR09puG_(bLJH_iK%#mGE$=!YLf#)BeSq|AZ zj8rLU8wbbKlKrrMeZ(Kdajyddx8$|pe9&{s_Pq$+@4b};x!0R7falS21mOzktqYoM z*42i0;(m62F`CZSfAXTb@YV-El04}TjDW}SSr=sPg(oI8l-ON4an924+>2~p{zD&0 zCF!s3h-tvKfV$nGXiF+1Vo0L9E{1_&D9*rP2an#^hieC%5H!R>+f`HJMP3rnSG!%M zI<|%PfydXfN$Kj>VOv8W91Ns%ah49*rJJg`TrnPMTPBq|%;n%Y)@FIRCaz(Y_`~Yo zycXs2q_$1lEBH7XuLb%j1!I`t%plgf@8k!17zqjv)#kId%s2BSlpJb%?wd<_470Z~ z9EwKF#uQ|*IEg`efphzOrTxf|M-^>=PwON8)^IRv)L~E=YDQkym{DKL+R_9uc^wc* z#qCMoK2Pa=WV1*09IjWD{$rzs64v77n!uht0iPM2&@pMcAfd0;Fs6O&JjS85k1N@F z{dpz#y@%hB^fu|%O*rOZFc&d{1`jQT%LEboW=00a_We-$gAWQZHbtpqXvH5UQs-+y{hCw|MQTWd2A0`4AOzA<&*q}QL)?QIozZw zCA{|L;Z9X8dGwd1IBv548bi>i3U&Bf8-sqsws-sqk#w2+s}!a5yGJBupd^((btDTl z$c+}OCmJGI_r$hHOKUe)E6|o!E=!Zr@iOJ_r)aP>0BeS|Kn?k*q1|0Bu}SH8nf8k$ zH5X62K}SCc1zODzn1K^2cyyGumv#gCN%pK*G^xzv2k@w-1B{yI*Ggtydpb1U|NL4$ z`rjMt-3{S@&!1EQAfo`D?JkQ#U!biXrDBwBJyDM~$jcLqGO7d7WIC`GIP$`|JV(~A z%t|Q-2C5uAzcedV$0+-#KGNCR=1+RBmZt7950R-5|1h0wmZwh&C&TnOeRb%k6*O?_wMEDk27#04hYxdyxs*3Fx+3;&{q z4L9(?-bh{-jDaj4r9cFt`Rdh+>lZM*G~uIQFOE1Nj7Y1)krd7n83o9K$TECJ+`Jl2 z$UPX76;|EN{lk%p2p=!&@Ub;TOv!te?vY=~XP$+3Z3ROga>&J5ogbqH*cJEomY!tY z0$m*X2an#jM)o|^w;HYKTLYel_O0ePOpG{|hGgA>vzlu#AHmVmd;&F0NL<4l4`fsh z)C@E4#x8v3b=3?*$s1j{+=ic)2f2$hccsu|wS@*=sGZL|>)W)cZSh3CKNOCnQX!0+ zb!p7F?`Rso*<@>Os4yb|oPF>oRT9^rLlb=g|SQ^l!fPWF3yk+|#UQ%eyT0eh2O8L}mX+Td4(MzG= zW`F*a2n2T2m?2|UJJ3fd7}NUaL&;xvE@v!%*HOrpdwY4`{Aoon+!Zn^!cmM%lU4*H z(u@ofwyuHxr?x8KNpI(Nu{~^U!3T^Kw@14y9MZ5YN>n)ehBuR&nn03WvB}5zkkZ-4k;J}v8T??)K6Q!) zsNnz3dZ33(gc9`}=;xH8fZna7Ni@27`fcn?IQb7kmKC4(Ri2EoGL}U$m9SFwkRZ zz^PztjN5LT`gAjC#h3WB=)$(RL@F+*UMHP|gDb$ZoTzQKGa=6E zmC$!NGuB5h$FXCwE#MkU1I{*EvS$$eQI+D#Xj;Z-_Dld11|mepR#4tz)Y?I>ChGq?(X1^T()Y^2DjUzp2Yc^qbIAR zxO8d@$6-I$V9_BQnDVGQX#h$X%SoCk@5^H!+|;J1=3V>pl;8j5c56QBe{l^+helii zXfm3EHc+Al{&awxtgVLwPnxAM>4_&dmvSV5Yd|`*`lIIb*_TN(c>Lfl@2;$4cRzNd zR^k%e$)((78_^`R!Qs%#Mll9dp4-au$~Nc4B#y%z z4hLtorg(Zva-rZ}Pl;4e?a2kt174XwU;JORfqRF@Zk#wz3Z>;?3&a>xO&wa0)lS`c5bL{^6-aKbTVyJ=B!CH$6 z;Y@}tnmiKI%C(>OL;bNM{i-EpFFMl43X9wk)PQto4maZSyY1B+sLwf)?T2!6;e$n` zwc!10lzfW{Ytct3Uetiz!H)|VMpGo*W^V(s+T|+A?(PBec-xE6nEf|0c#i*-inY*J z{5u16a6;h3;EHIAzts*2>{%Huk9KDh!^pf5N4+iUB+tCMCf>84j?XF1)}-iRB+aAW zIjDN(xBmnqQk0}4?LU#01*Lz$rL-Nh&K_V4gU{V^Kdy+~*qOt!D~Ct4yt3Y2dBJ_` z4jg$~10DVpfjvGSpbg-X4i9$7>}-E2K0H2sndDeB_N1o&C-%(7aC9~k;@-YcA3Cpj zE*khfA2{P9uX3L>y3q^4820V7uLD18+296TK{^U~C;HDSlQzIGY^yf|JHwU@ zMp4M$HKT)Dv-ZepyzI=d0y+AC4xsldNRQ9%kQ^u3JvaD+;U-)ltq->*vx3_oLk5G7 zUP9^fGb)sgjzT8R(@N56=t*|j#Q6yubS(=8K87POx)Qjn@EX2Z#v+8sA_@t z-u~abV@=dTTZXeJ{-EDloiu83ell_EgfRZe`2WFwFbZ;3|2R)GD*ne`Xff$dlyKBr zSnUr6eBrievgag>2K#Zg8{-E9Jb79%e90J>*!JZQGStAz%G=t4X6KA1n6%+HR=Q zWz>4i$!RowJCkHTa62;K0I&>byy*0YP^jPYB!;uSBT%0IyJ0?d?-fl=A%CT{NO52B zE3zSm2H`+BP`?lnAU=p_kbfXP{8{m#dUqTC3WR4th}E7Z^LbjF{^0o!hfioME|qC~ zCFt0+vdR9Xo)trea3CDSKJu0Fp=5P&u~COPHR2s@Wqh*v9N_!CpUaa|jM%$3*cP67 z@++;y%-h+XAsb?_5DtWc*g=R7#0TPohyd|HM1%YT@qzdtB0zi)(MaJRsyZ=K4}be= z)Z7-|h+;-+P%Jkow)aBzZzuL%$i4OcQ|w9?OPSm)7?HvTp;Ri7G{n#$90&)opM0f! zSY3Q+M{M7Nq(5wSW6ocE{|DgvKLFqV0r>t8e2tanA9&K&1(W6V9o*Ffw;PCFnRK>! zBKLr$4<+`0cn9}?xb=Z1Nq!p|MCzuHzfxAGxUcxt*W8BK{!g)tR?%!h&iEbAf6xjh z^B-96h{iDgf&a2;){NY^rmr~;LVZ(-REroogahFq_7ma*@qzdtB0zi)(IEdod>}rE z2oN7cG{`>?AHG3+;Cl|>b|BXN54ed#(DeCR!a>~bMV$D?Ulrm04~G)_KX56r|AScB z6f*S->K7tCh!4aE;)93)@j*m``~&fU_#h%cd=Swf|3G{oK8OeqA4D|BKM)^?4~3Ri@=+0 z-hav8&xdbxRotHRws}hN5!0)B4o6j`2@ zI1(~0$DP%DEy=fbx8A<((Ic0G$5XxF9Odk+Yf<(lC*u}lTYzo(P+oQ3u?#087*&r;({M>?5jQF zEa4D?gm54n#Lhu{AU+TuL6da1=v$o71=Se6z)b`vTFI90h zYVXV{+nyo+@QvVuZQIQ}Nv%n(DYAn2KztxRhzJlLL^Q}h5FdySA_Bw*5e@PW#0TPo zhyd|HM1%YT@qzdtB0zi)(IEdod>}rE2oN7cG{`>?ABYbk0>lRq4e}4f2jYW>z@G^p z8vL04kaY8hI3WCY?k>;a2l=iKqlHrYVgEd}dGKztApAU=p_kbfXP5FbPYhz}wf&FHK{d4N)R82 z55xx%0pf#*2Kfi#1MxvbfcPMyLH>dGKztApAU=p_kbfXP5FbPYhz}wf90Pp5*U@^|)8XC3+_;B;Kw&6|$uNea-oh z)b6QYP%UD3T<|!okNBfOe@BN=Vd6)TFWk10E9yfD8hko5W5WVf%rgt5D_3gh-i?1AU+Tu zLdGKztApAU=p_kbfXP5FbPYX63^T*BVA8L|ZIQ=yB%@`tt1K}X{ z4dMgwf%qUIKztCdGKztApu;c=86q<3>mM%_5%AI z{O!yO?9MEd6(q#ywfNmt4vZU;zcjm*}T>Y@SP&S{s%|8;XZehb?|IyAaVle$L^N+^w@4Z|Z69GgAU zZ2S}cTeszMy4wlE4%9k=i0D`g6>9;k9(g_MG5#gMG8tUI(0g?GbocG`5R;&*9p2Dyj+8A*XQFgS}ggyv!cxLuGbGq<9@?f~n<>G;uBkmLa-v76yo zWiZ^~Z!)R_%`Gdc&8S_S6mT|Szev}|bU95KxDlLO(v^WN67<+zFykEBkSo}rfn`Gu zw*mX+nHqnS8MI6=Y)u&doVR8%xImTfDUuz^TlEs&13Q)V@jNulxZ}(d?mWZ+-1FfL zY)$tT7PkR74mZHDO>O>A3KIZr060)AyYyxHFNWkxl|S@LT#}Y61O1ZxUl7aL546FJ zHY`ZkpgR8UTHrWvM$H9F)R2Z6E=p){PH5n**;5%YJ6ep&-K~uXsMp3I-Y>Lo;k&CC z43=Ui@4Fve8#ov&shrd)cw_^xo zloU-rFq9=L_pwlAOCZ$Y?}#gzetZ0%Yfv{~EkJ*7CD!7DyCl%xSt&cYq%M+}WkZEO z7__-KehL}29{uD37#NTjD}5gbjFMkxH`kl3jp5EnAmmS?f+MNd$XW)|A8wZAgSU4{ zmQ;E0b(F4vZFOzI$;Pl(!a%K*T`tA>3!kJZ{hKAp`9ht1Q0*_5^nh|zWw@(xmO5Z9 zu0}%`j2uJHG3Ll)8$SWHHjmW8>*=BRGb;|Oz zOJrBtBK3LniTh4?l?BH0`&k(b-=xSYoDun8XM)w5UvfSuc6>O*TDWsED#D@Wuu=3K zf7gtZVnDA5aG5MYrT%ai{9*CfQ_==F_C9>_KSzEDtP|R=4|ktjEcl{XhSRW(+mK3u#VDwbY1pNyaSglT8qUDBO0&`IFm3KVamr%#jTpMQ zx({D2yJ6R@f`5Q}VZ#k(#PpdR6Xj?AwKNoAEO+FU{eO}(_3cM_nfRE6P8aH0!jY)4 zwI$GjuM?YjJ@<0R%H{aYA$amXNXD{W%Q;VsdS@W-dgM_m1u+dvu|W>O<10|ZE19AV zLCCYa?aXu6$nFdwiiE>1#c**ucFiTG<#E(CGKMgd*TJSthk=o~gWUl&l|l1%e+nRD zwb+2<&Viv_FmK)HS)ms21b8e)Z4HN;jEZ2`HX`Ja*gEtuc`$qwav$qIE9H46B#Z`& zIHOW3u-f8VNKDIyT(kjOaH2Kg3=~=Zp|UgJOU9~nNCzD1b>!5+wYZ7GO-E6B^f)*! z85odILyhGh=Aglij=uS71w0#%qqOU&RK5_`7-W3AxKeteDi%)0{? z&8}g544?%0z{<{yND@3frsY<@F`}>~V`Iu(pRxd!S~nUPU`E;-FA2|jIoxr~?VuU0 z7*2!(;UM-H;sf!4_#h%cd=Swf|3G{oK8OeqA4D|BKM)^?4d$`46}nlgNL_!Bu-qkHJ;@WzaL%{0AL9L?ZvedAEw$3!{CYRKbpadHzF9xXT|g zHU^?bUC501Z5NC4;BtmJIWRN?Io$)jjx4xe)w0}}27>DsXT;jt%U`Sf2Y6lO`41{M zV)+kaPF4AP_VR4`4-@PDny%_&4$M6msF%1TSrS_?*BlHh|6!^PIWo85MfS|UP5y%m z=JOnksWwQK4NtRYCRZnIQ!xIO@*iBVAejvh;R@&M2QExrBr}|Kw`t9%*{Lb(-Kp~* z^!|Fj-ra+%_7jr(fOPTfCh&}!CCO^>19nPW@3u3+TF;VjMa*U8KR6E$Wl1p!Ga(dt zIV++3hyM@x5Ap;HSVD3iV1xfA`470x`z>6RljSc|S!ZA52y4yAhbyf0P0D|u{0GW^ z5ZBx(|A8kUQ2qnuKT!Sy^Q90&*TA&&S!d>}rE2oN7cG{`>?ABYbk0>lRq z4e}4f2jYW>0P#UYgZzVq2IW6c{)5;pzUlivU|n!k&Wr235{x;4r9b`8kg0#@z#r}5 z{tx(TyC$)=_VR_0|6tYZK!dDjWay)q(SrF8Ohrior4M(pI5CG^ED*`~{tr`E?Z2M+ z56O1Huz+Y_Zg)}6_S) zU2#?MRWtWJSO817j{?lm8Xg8X*V~J^FTn%3c`VKNOTGmh!4aE U;)93)@j*m``~&gf8^nkI2SExz%>V!Z literal 0 HcmV?d00001 diff --git a/fm_banks/gs-by-papiezak-and-sneakernets.wopn b/fm_banks/gs-by-papiezak-and-sneakernets.wopn new file mode 100644 index 0000000000000000000000000000000000000000..ecd87a53ee15762e3ef5eaf6acb015d4d58daae2 GIT binary patch literal 274864 zcmeFadvF}rb?1NjF*7~yH!uK(=m80UAPI`V5ClOHqzHg7Nqm3=_>e4lJS0ZMSRe)% z4ET^~Np|$MWZAN;ND5=fDO+(;c4~La$vXaR`M3U6vgM?_F58J>JL`mA@5ZI1oRnx& z6eY*u?|ZxP7y#1)NP;LzI`aq4^kDkT=}+IjeY@|s&pEQccVBXCcXHpYBojr+<=(Y7 z(VZT?J29NQA<;smUc35czR{6r>g!p%VZ*B8FKWf#Xf6KF)O%^xH`+@$2^4=L**3Le zk&P99V|}vdKT~fL#os9P1(J)uv1w|(V(Tpa#`@y_Oubi%zfmesQj5RQKDA!4wHJS5 zWAT5c-aCrFv0>`dVrwh@Mq8;}*HrwC)>1zcEdEBa)X!8Ef5Y7S9UaAcU!J}9&3!xT zD{}F^ZEa5&d%vTjH=mpGqZEH5G4(4&Um!*N%CV_-+B)9_DNdudk4NiTf#5bC#H+MRdFY}E6?HI}&FXVH>+2f_Z7Nfa55_1j-iTZXPExqz&OdZs9 zJU2d8z;Eoz18$->n;y>12{=iuD<>4Gj(Qp|C~}arjXx1)RC~rI_t3LZ&)I1w-TCZ^ z!9po7H&ScUc})$|`lg?fq>yvtPgUw8$??2hjnFNBU_WPWZ`nvTk<{>E8-9Cz;}?Xv z=Z3%2Y=}g8#xB1|w>hPM-JKi0`)r~ge@g{HboFKG40Oa}pP?X~t2RtQt9s0~k=nGB zFs(c2x6_drG!mscyn6B`Q@XtlTcs08Bso(a#|COOO=v{eJc>>2}cC*4>d7fwbK z=Y^A2-|Zgz1{(FP6qgSchG+i*RS1I;O)!CC7!eNu1 zT4A2OdWjr#Z{>tX>!+=zNou+u+mSbo`b+iTuA$uVyEDUMBiYQ*JP+mvTj}0FDypVw zvvzyblc6!~cCT{-CAHfFFy4L2zwI3dkwkayjD-h$ytUIOQ$P1<%idn}(G8O#l5EWNaoye8#!w^Bh%`CM((>PHsi(FgAdD7^dJfZeEf#=L4@z(Jj~^Q!O6L>ZnW3Sv5{|HUn(1_Hg{}4yZLF=Z z*FHc=PfXGOg7TiQtUpbk_)lt~FFjls&s#Zs}Ws}2f>BdX+}E=R-0 zHrgKOe^Rwm-WPd5vQxW#*%Q((;e>hy$8o~2b^|9{sURY;QPS-drp<4d(STvjZ&O}A zn8^%SyD+wGE48l(5e^~68)vGa@m^zeyX!i*w;-@+~H6g=|eGjRnJf+J+(Oru`lMoI%rSg4!71o zT@@2fY{32&#I=F8+iv$eDoKw?&3nfSXeebv*+O03RK#_Zx&sq#?`tFn^dh5mNwxb= zW%2`g96D3sl!~M$7^rad(1D6-(PBf(4!La&)M`gF76+gd+nyaOod$Wixgm+EHa9ju5W0~%90x;mm|}IIIJMJI<>wtKix_LhFdx*HafjW zd7y77Jz_d2H8cTc57m&nO7~$q$fHN7pX4U=lDL*}=pK6|t&K>@87IjSJH~K=3&@Zj zr30ii=|1`_`67A%OVV^ZY3@5aTo{}TB~jYVT9W*q@ku*LcIpAOa*j#IrG_FWr9<)H zywkco<KHrJ=b)=s9S@ z}U_(D)DXH#@ptD48-`$fN#x=?@G}t+-&MdAWDSoF!7*W?RlPH9f z>OY93F6983k@))anUlGpT%xa##{n?k69&U=fkNybB@Bk!Otag)_mfz=!!%&BsyBPj zJvd<$tli)!9!nez*bdQXQ_2t1-gwjp(@H77y`5IRF2|>@x`sq|Ffy|dGw(K$9G~!D zh$Oe*D8`v%HM;oY`##pYcR>IO%X122R>~26NebqANW(zlj+B z;AEpxsC+^Z!>zDOkt5XMeatXn!{C(ht;4zOaSYdTmQF6wnn=6j*|40U$oe}TkUvK; z^?cO%Hzcc1-S2)zfc2T1W4M~TB~y`XrH-V>gE91wKj6duX!8U`dmr!w%w|vDoNjE1 z-KFjki0-kIIh~YDcx<*F8eMIeq~=p?J8=#(%$l{*FS_&MEZm2C z8)9@ZHwa^S3w1|7?FEc(PoIRQy4^Hgra8Y2N3nU{F zW7_h1bPGx10zo3#<8>+p$F;~G$^fT<+45vB^agN!BgCk;D>PYh*o6V0-} zR5y&Ig)lgwR@0!5eMt=0a-}a(rUFw#Qf=gZF^)BjH2dp2e}VIe2RIPi@LSQL^QGGBwcl z^|w02`H&v_j4>FNezJ22R|xY~o%O+H4D~KJ;UvBG`))W%uId*~T9r>dV021!`C!Jl zkj&3ot!M#}sI()Jig;e7NOMY~XQ*3ylH9*bvM{2fmcG=BtDOa+=?LBz50d;N3N;+o z=xI7v8LY9N!3q7d_v0uMr;P4g0fXU0_fYP1(eKZ^MI;?`ljE}_{|!zVh~)DWmr_*@ zMB;eLj6{z14W{oNA50$~v>q62ro40@EPt6=SMOKocd5{xa%#T|CZCzyoL?Ln#03vR znfdK`@6=$jNsrSjlx&(1rd4_D5nTKQmA@9I>t5+w`;UuuQg-}kU%qp9Slj^W@)|~( zWsd(?UbQEa9!TK!_t@xoI-glJFARLp8BA7B5Y9AmtzlZj=ooO`F%7^b4JS!BNuMs{ za)rSXZnlJ4E3p~|v<}^`t`z1;(}<-zo|hU7C+ibU$J0YY+1yB}SUM^1#g#!TC2Qde z_Z?h*al@fi7;$ke|CQmS4NeBLciolAmk5NmP;$93tdYGwms)}ANy8*fbLgc>CL4s4 zH2T^TrJQuo2G_wbu0LBNKa_A~Dp&PIX^h5{-&cU_K7B z*jmNrpqY%F&SXaBg#lNhv4z?qHbp5=!8C1}L!c>`Mr;lyfY6)d)7jxtE{uRWmwTG@ zkZ+?09ZATx14Cn6V@GM!R^`D?LSy?0oQU2eH$FUotJ33l&vk{(iO~A6Pf>qOdDD#f zGzBn{0mGc%=KO0o5xq(NET$2L=6WzUXJg{VaE;Htp6-EB*HfF@ufY`D`-RzfgMAoI z&`i>Wlew{x!A!{xauXe@P02U_53bZzTsj`8ODP!D9#B#bW2>r~R2fc0Gco3>hH*1y zZZO!K&cM0lDOnDnZ@Pe+Y?SwVKVh$@q#Sv`-cPrm(T>3hn#qaWG2C{T=fU6#C(>#c zVSlvvaVtfkQQ37r%$aIq>HM}pGn}BAWH7;F;X+)jtQVt43}9Cnrd10BaQk6Q3rH{+ zf_@cFL^C;_U3*t{?xvW5h+7}aZhH*hm)6|z1e)sZ%G*ALyB2vR^?-S=U{zn48?lp4 zFnU}TR;Z5R-ae1(ztSlf4LzIpPMdmyZux65dK~_!^$-Vc#C^6%>^6_*G6nQn88`k$ z*M7mcMMkwQS0#Cda64!)eKI?iDSe4yhSEWrKNS>*Z*9tltx{@@B?S{TS|e3FFW+)& zu241*__>j!`jn=up!K@0${{*ZH(^s(P|$O})}d1Tn0@%x^icWv9kD@MlM^A&Uy$6W z3)A77@cB;Twzuwfo+kG;skS?P0#lJA<%VrIjf!)zM4h!5VYFfb(Br~RN(y7#Ex&zd zc3@zroC~5Y;nui*KfQ=a49`RIA5Izg5=(b%#~r`H^l&8V_Rl}*TEm-Zu zV0hG4?S}ELRV$9Xq~cNdY}P&(JsNaDO^`ciGMP0ZtNa@e4tB z14(GAzMV84`a;NS+?aB?ULwx{<>kGZq1*r_Hv4my+k;zVA)UMqq7}6ifzfmsqY80W z^VvxGxGI~Mw1P>Z^!A7Ya6c^zVWfnKgc{dpsV=HlMpn>>vdruMAEYjm;|DXCLKw>= zdd^~Uv-DnGK_63W3T`HybvPt>Jq2A3oBAtK6`MU&PoaO)K0A5yU)=9b?91VJo*z$V zV5F+rWB&x5sneb4ud7{;KkRmr=6b^Ie36_xq~Y#-CXM^c7V8_M0^F>l@7v|&bTkkU zCLj8Y+wos9U;KQ)d5T;w$}N4FbfOP4=p`bUifIcDIyC!>q*mL+(9K?D7ZRMhY2=3pgA*8*n#K|14ef$PwEo(LK&K zIAwgiYh+|7Jzi?3Od-LUe*{M!*50VZ^*~J9sZ{HsChe4hfzyvQL~`@4@!_$-oD~qU zcjBGoB;8A276)?$hQF#-;rFd1oA$KVzl)UJQu2ThoFLvZFCiWIsRoq@q$3gr8t$ZF zB<*<(mkT#PCVkg*VsQ`0^b98rrjrKK$+}riUX^}mI$2*P5S%h*oQU(fcim4_rHv*d zxurt-;mtySvKDtxEcz2MqNX!q;D_tDxM2#u=g5wMpYLg5TAk|Z?U};(FlGXG^jdO> zhi~f8{qzV<=o5DLC0zXdFNgLZgf+DfYY)*fr@Upxi6xiL@TS@N6Z`Ft+uLc>e*32! z8>r!V`9FGc<0C_eWcSF}4HjTF(|GL#m3~bhS%xc?Fcnr#h_OzcdO=73aDH3;UW*k! z&>tUJjQ-@lDqLRtnno*hp96Q^EA*htE{rBR%OBeJ?psKIBF^2Tm3j?Mc4sQ#>yj{D zaR!X);zqYSX8Zb*?1o91gypCJ&AyG=R(wdMUr}LI%0d5!+|CIH0=iEqjV@vO+kRaImP;ux$?frAGnEBF?vKK0mG=KfiggRC@v(IB$AtP)rP*hobIEO3f1^g zqP!SH!gwHo!Tng%;4;5Oe0qJn4&6OIgwt`kV78F5d_vVOVrq866~2UtTFfHiOfJl; zq^(u&YE7;kGv-z;R1!EBZzfOOgeLzJ?r`6xDKFtV;ixK%|MwgaI^6vaa(s9CbUG`@ zS_?lqXk#6Y+b_{*voSZ?x-w44kaFR^C#{;OP!Ho=c+{md(&-M2z?R~* z6N;&0xY1B1nkG`5Ayq=Z7}r%PME-b4%&3n-;hYy;Ewf5Bg^ZGh>*pf(XS5goiY8S4{XZ;-f~iIq1k%O$5BSGfWui8(IsC z8x>X_Ek2)|!V@!!Rs%Q4Y5Wr|`wm=HN5i!u@=w%sx* z9l4IWM+*-8@!KUZBTkr9e$01Rk4X&U{CU7Mm_zqtOc62-h8A6WGAGIka*V5tHuEZ@ zV4jQHG`HyB3Z_BN7Bidul>VgT;e-43m41nKP*<%k5iUNxVR1swYlacW&iv^4lhuiv zAyhhEu#%sEP&0)hK_X~lDOyQaaZ&pPm+LAnn+)R`AoMHzdPfM%)}NF-zqe5`x?hNe zr9iI}YD3u*@=NU)$Lhj}S(V9ChM_+xdE^LjLQ)7eFfvg7NGW8m=|#5dE7Qkk(#FC4LK@Y^NR^E0Vc2%1p1(KN*qa_DsyFw z%Op%gh^wVi20dHz_S|tiK9J}uBY4_E%a?sY#{I&y?;mAMwkpP}Sz(n8)%boyN84##@G4G{7P->WhhARRU;snE--)5_Oi@(jQeZUCx zr%&RE+_D?J0uz}}x#$6sR)m~tf*i{ZYVrv5TA&(p8 z5Z&OM(A^HwoGD=(SESfm|M{Xdd(u$DL0djn^x2%Z&EymUFel+Df-t>~?)7s~7@0m- z0Yi_|N6}Fq&*Tf{b9f~LF;{s(kYsyJJ3{hNi8J=cB;*%P^%4KAxa62FrbWONzO@!xpd~ZJt_V7U< zfTwT=FkN-5%o$Lno9gcHxO(Wm8pGr@WKU@qnOKDJeM0)h2LTjsBNJc(*M@-kkaF4F zBRAsFV9bQr@f5;DEQHh=bXx@a?k){;Erzb(z>BQwaQG0`DmTs}uXXEmPBAOK`7X3$;}uEnx`HtKqn{;Sq8yMQKlE#FVx|d(W@y zn7~y2llf=Q>7eoSMD3?VN{ZGMNF*45KB7YO(31v}iEm7Q^<95cl>fwPi*BR?D?Cm- zJQGZM+_;VVk+ru4>8~iLd2~#e3f0HEz7-q*6JP>N;QA!cXXHI88`~BnKEZGRNtcB_ z7BZAs#!%L!ODB{ma=XqoksqX*40M-lQXVc9&#WFka~;A zf70?l)II%!BBv+vY*;wK5VRQy68}OpwvOe7X|vlO6c3%42ABC?vsm}deY`sZ$yJ$@ z2Vk-_bT4vrW74kRjNFf>*VIVd;fx>~im@bAsr-6}2y|h76%Tq@JPbei9Qs(yqhZ9? zZJKspK!rLt?=g(nJGjhG>_tu)%SXsXdTn_v9*)GLW@Zjdd38!8sgMfc&@-6<`_Zd`s{$@}F3Z z>VWjbYtj#Fd+lb_z6J5koh)qjCvsy%=tAFuc}gmfW}I;Z(Uq1S2L^J!tE zPtu*!`A@9-*-q+cG7_KI&HN{A^YWkcptuBbgF>cm6;P*2Na0%@eAc5-pbAFPYP@)I zxmAn1L+ixU%;7!h@e_lIrrw^tiM~UVEiE zY7__uke5Z(9)DCjMfPXt+j~XQNvP$K2gNEBC#f!sWB8Y6L3og081K>L)biRXSJSr^(39SvL_?k!k>=0+H?Ni!!pAzpq|%O6RnS zd7D`;k!;7c`Md{mNoqoc7Z}g&n!Iw#h~d^8C&_tEgvQTRuX|7tMykeR^+@Ne9aS`O z?@?D#!r=a|%F&yVtUNo0K}z0o2i1{FlpQ!lQViyhxMH!ZhcISC?yDoT=8#l7D|B91 zIs!;moW83}c3)gH*~wFRp~~NjX}6T$wUX?il;68@YRSD&S~H2# z^N~aw105k`iqq(nXheu(=wp!%u_6>zmLrR$?n4?0 z9DoFa^Gad1vdp+U&?YJY+3~o)Vcggv*^N!)UjBPRW8B)95@j9K23=I| zkqK4GB2;tK{>P<$`rY}gDF2Do2&IZ*wLb}l+GwOI<;0mIxZy%5j1!1*yC2z5j(kPB zv?QUM)d9<>4&446TY<#o?V^IB({UJc8!0z( zMrjX74c8#f_uNm2QYd0@KUu+eUi>(rw~onGf9LWs2}9Ry^&6;NRS^r*ZlvNh%mHUi z6D0{ugPyI|YEAx}2`~XBzy#h;1VsLm+<4gxD43wqjnc!CS$fz}jnczMNbx*i=8xqE zamPC`b>#8gCZl3N$1LOWNI3MqHvUCyWMW2ue5z{?iz1t8B=y6V>oRM)@5J56wd7AyF5h0YP(Rt%PYAIAlyx3^C9PY|lSH$5 zE#cBZ6vRv%96VbXJZa^%6&Ie>B%@+oT=y%e!5TJNfC(@GCh*=OUPUB8StGA6(Tm;e)C0`DY&p21u;pXf>tj+H)%;=!n9%6A$n z_cqfMw8s=A+fJGW!>up8lL4}CCcp%k025#WOn?b60Vco%m;e*Fz6kVYQOs+hkeu-e zuZFVS6?)j-NXjZbAeOY0&%TmYUzCCitFt~f$pRdI%mkPK6JP>N zfC(@GCUBh-*qs|1%AHQ^%??|o$wzHp`%c}4hi#B|OQw}`46?TVd`Y&kcRZiLbmDMg zPbO_q{d+4FydeeE?DGDA%eI^3kY1&AP%_|m*_vqe*(Iu^yd;}*{W#f~E+iTgy_x*o zXRU&&qt^NhZq&C;FQ2g4Wt6A=f=fYBJNq&sr%22@YPBYR&IFd6KyN;Wa);v?E613c ziBP@<0(FVjFE>oV;~`b0cGG;g-eK9+t(<-kKHq`V_M(rq+SaQtBjIyfxPqvG3XRC8 zXvdt_KA*Ca+O(JxOQJ@+)yn+&dz(Pwy&XPB|3N0uSIB3FPbB(=(jz4?7(JVxaIR4C zyf`NHFJolWUmc8*qCFj=DO?hB{U z&7_9)O6e7!KcdIzb)OtTrQa%?Ngs04>l8RgqdmA&-dC`Ug0B7>ZBCr2#6Zzbn;L3` z+1Bz8hS5BaqH1ukU8?Onjwnu+j$$&Zc!+dV-LjLGFpak7p}FmjqnBwUzt^YE{u}PoJ zro5`TqdXO_JDZ4?^|;Fb+kPk$~*$yNfC(@GCcp%kzz2muUuJYXGhE1~hb)CG zo5;TMXc*#csyZyntjTRDpLp_h#Z}Ou~8m;il6eOwM$XAfmVkA4L6-IFwk9=CM%l~mrv9BuFO0CQH+n_jJzbf_#BxA_S6~Z9>!v8I~8h^+H<`6iT!*l3~T|>F!chC9H z^x{@>)Id3-lIAQ6Ud=_9s9n}Hm}R$V|D`*dA4v3{&SVM}^;ta7eAp_2;ou;R zR|F$4Hdinhyh2fTFcd;Mm%a3qk^cl4VNO~EY-9jQRg%q~^XhIqB6v{Q0LwF4%vX!pAvRQ_Dn$^^mxVggKn2`~XBzyz286L|Xs`pym)1`|8;nZi)UqS_2{ z$gI*edXc2HxHD`gDPbDfFu3FAd;4Lq2PVJ-m;e)C0!)AjFaajO1g-}HJw1s&6uT%F z$}*C6;aWf@$v2@O=j&{PjDk;NfC;?M2q4cfYRy=06l4-x=^mdRw7;sUKHY17 z)mQN80dq;1^V=ks_3CfpKd5?>E|k%IT)ouV*=l5`k%?>`qQ8RbV{hEE5@Y?tpZ5F_ zslTL;3jIk>K0Q`OKGK;moV=nzEK-5|C#2PU3i(f3oQBc%QT6UjI$t=JNn2zHCTMbO z*suvdJw)Vt&gF+Hbe55acpBnO?0Nnw6JP>NfC(@GCcp%k025#WOn?b60Vco%m;e)C z0!-lDC$ML4qHlCOoi9DNYQ(2fp~cXj6xJDL)a}=36-^rGPX=}!T)V!drR>JEQEN11 zmy@Rh8BEg96 zr%RgDhzur|G%R^wMm!TisggE5A*H)UMuyVkV;PHNF35k<+7W1#^!w=*(mYUeN~G4; zJ`CgVy8L)37LUO$FB=5^lL;^ZCcp%k025#WOn?b60Vco%m;e)C0!)AjFaajO1m1@P z_H-rs(x=OA*cOs%bekF=S<^%I-E&OSW#nJv7ZYFtOn?b60Vco%m;e)C0`E3~TQY@_ z^w4k_eR^aoNp_LGgho6tHdNY#xlHajjF65ffBSB41phu0U;<2l2`~XBzyz286JP>N zfC(@GCcp%k025#WOn?boYXW_PCv&+IWq(yA*`-&bOuc*~@}K;GBxj93w2CAROXBvj z^o_Es^FNsY6JP?@n!v81-0{0J!-<~3?8!{&AKyw_U3#VVAQhZ7Uga_sLN$JECyk1w zoz#C)UR8QM{*(zY0Vco%t{Vc~>9Mgy-%xfSlW6Kbxc}B2`)2pusCu)CTANZKElL!J zX`sz@)j@v)waTfGyOJFLA=NI))^v{_J2qr-!Vy1!Nc^PwQeOKd)Za<@?U#HGU&?1L z34>&S-(HePj%0@i%+udkNq<)_b#~TudaE_tZt{mKKBaU}z`5)(dlQ|~mOW%YLydn< zFZ5;enX>s$9Mqi98iNz$pi}j3VPv=dBwcZ>cfq{wqHog8J9D|hSRpe~Iu0YaHd2@C zQTh%pFZ7sxg(UStAQ&TAz2J((gq-C}$!*{dnE(@D0`C@q{rMB=;e(_GK z4YQ%)kV|bOS=RmbMq2Z6&Hz>t8-K_Im;e)C0!)AjFaajO1egF5U;<2l2`~XBzyz28 z6JP>NfC(@GCcp%k025#W?==G5>3sUG+^|&4VUb@rwyC0VZ%g5$MgG&g3VxK^EQH8Vb9glB6glD?TqwP37mKs4mz_x{E5s zQ+UDaX@fWxCcp%k025#WOn?b60Vco%m;e)C0!)AjFaajO1egF5U;<2l2`~XBzyz28 z6JP>NfC(^xYf9iCQl%yi4xTLxp0tpkPzP<${%3?vQ9j-$(FPi=fq4+hx|Bp4p+8xo zoORc9v-!uE025#WOyC1g;7B@uGSQzOKRJ>qSitBYsXAh#XJ7&{Dj!H|A{vZLQH^5a z4ETb@iu^GXU;<2l2`~XBzyz286JP>NfC(@GCcp%k025#W?@0pthXxXdM)KJcgQf2p zqS#YsxIJ(fu$wTbv!#zzStVHNRsb@Q<$)6+{H_nrG30RlesIgbFZ}@55!J4 z)}%tTj#@$~mkP7ts81Lv>Uz1j3|Zq{$8Hn3yeAd@_feA1HCcp%k025#WOn?b60Vco%m;e)C0!)AjFaajO1egF5 zU;<2l2`~XBzyz286JP@GUjjSE^SO~sdN|R0m(@l#Q)^|Pjlwiml~NRiS{?l^koCz=iCcp%k025#WOn?b60Vco%m;e)C0!)AjFaajO1egF5U;<2l z2`~XBzyz286JP>NfC;>}2<*-c-+eaGkH3jzsmQOsOr4>D*!j=VHfoEVe-fsk-To=Z zMrt!n)i))z)QbEW6JP>NfC(@GCcp%k025#WOn?b60Vco%m;e)C0!)AjFaajO1egF5 zU;<2l2`~XBzyz286JP>NV3I&@ZX|Iydn{81NSrp-=vA&(+9ga2N-Wh9*=W=>=eH@l z%dE`*VggKn2`~XBzyz286JP>NfC(@GCcp%k025#WOn?b60Vco%m;e)C0!)AjFaajO z1egF5U;<2l2`~XBuowcnvnK`<2g-f3N+nr6T0<8}j^9y9S4h$(qA-f4hhgaJ^jzOi zdZe5SqU07Dm4c)yblRy$=`|X21_O~lr2CwaFpP9NU2fXdw<^(hJfAKcA58R)j}6ZA zxTb@oV8EqzQA>sHQ!mm@O}Zx4k)Hyg$fG3H(w%dh3=Pe5vSOZ-Rg+G7g%f&(?(8dM zkKa8uZ_T0)wltH6;z9ka`I(b!k{;DxpIV|b^wGZ2@pL|u=ox~qqBV=&-l~(lQm>@n zkVF%PSve6imYx!Z2B>atrjXAK=T2Jt+1Q57*9kj9CcQ@N5B0p*EwUOHI|-3IKI5c2 zQvwJgTCI}Lj$(ut#a316=g8@rsP;6HO)<7g{yu%PJ3Tg**q$FhnP@HAgyOdqYpGS$ zBlJ48IBO)ogIctxFeBO_VdM`>%`;9)=6Hj*70d7^6@?Q?i~G?|W*az3_GJe$31b%( z|9bKJmS!rr0QLs}YnZcC6Ro~XPSeo6(!cf%i=9($dm^3maTQ=C>JU!epcc|A{41$I zdca>v@-5P~-Ra>0?BFtOf8z!kAzh~^ZB*iVsFHRnAw9g3PATz-??*)6qnC;zv49fm zMC6F%ix83OF+`$KDu@tll=24=$#QA$p4@5V&O2E;b)JEyx{XMlh=`+joi5W3+91ax zXdnv3J)X;Czg>>@4CTf$1Bu<)wS(zGG0*AuottUD*I=^8X_)<0dRT3xZdw)%G?CO# zPfbN))k=usB9heXNR0kuGLp0$@68OPhsp&)mXS!Is2@l857F$i0}C*zMI<(KVlzw# z2hC-{#0@6ev-0x&Bbni{fzU0R$uF2FmA*R_i8YgiU}8p+ zoZ36#h_QE!NKVPijgGqPK3-us35w0xXgFCfHfPewYowi$ntSr;vB5;o*^|f8^Ls{P zH-C5q<(;}hK|1FNO7eQD_3DcHD^e*I)*8t3w49la#BzHY5s5=LBI!gVJv0-E%k^jE z$;i=z;wUZ`2;JID_bIwWzeE18uF?uBsIjnT=af)=XY4iakV5yJ>uSrK^!tn;!M7 zBzpt>wm*IHSZ+LzaEg9?*4u+^w9OV5{fV;)0bQj1?q~oV99#+`?WKS28yQ+)_k6fH zks@7|56}TgkJwu&?5z2e^AvgMLr>Wo$@^cVSP!~_vRgwpHqko32shG3U5|R(X|NvK z(MU<0&;u*Qx%jum?Ie**%-Rrhp|+FmGr^9M(doyiI_7F1vR8}V-t|5CB4qN|FXg(& zqgxU|iabYGd=%6_?e3r=x9PW{XDjQ2Ho=KU>)77JrN4v^RH9)-gG{FJY8-Kx~mg?>_MVcfcj&i zo8j%LQ1}V*d!7!3vP7d)*Eg8U7ZOJXvttE|-LnxXa~nb~aueN$@LnMwh1?Zurk12f zsdo?BX;~k%zJwEP#))!>dd*07jc4;#N8s))L|)w{*|9l42#8ak{G>3l^T`kly+DVJ zNXq%3mQHG=z8ZSPlN7!ED=yi4YY<~e#cPapWQp#?aP3s499P=fNwHeJqT)LgTXCVv z^D@QkKZ?e`K{4BpBHkF$<5Xufwj;URz_FoR>Cyd`PO6LU5;C29hn_R~lb-SH-Ifimxs&oMJ%04bQjKBG&^~+6jzQ$7g^|Xk zy+(gh)=5(*NsXqHS`XSCh66sawp{7+`~HliH>7VEk@V-Y>ERO=jjek#ZLZL5wwLL2 zRlLge6}m5prLWLU+HIc+yiBNuey$Te(}o(LqQxn-=@R2WW{zEXHtvSa&u?GSF79nm&h5{9|^od zs&n6Cju%Pw?R(VmWpWQoe=N?!iT-@XqHn~nA8JlG>mrZSuYHcXn4f;F*;LQxUE6W_ z=JmMR$nkf!NKbmKkRHwyEW&`3t#NWz>TX;aNPY|=-yqeCk^UPbo5r}16z843@%(_* z@kbpEBvr+ut~&G_sZi)u`0WeQt2m%i0W3*L`F>;Xly$NWPQuepFouQ`4@T6&36|8` z&{gw`2`~XBzyz286JP>NfC(@GCcp%k025#WOyGk=z_^uqpxhQu=}%sn)}P1|#y)Z{QQer}JN`JD= z)StXUHwo!T!qlHUBJ?K@&T&#gZZV}lnePPplSjlo_X7NL@A*`Y3nqnt0YXu=ry?Cjr+I(|3=JY zKj-lsCh1=#m)IO*8qZRL5?@DhSP$UHlYP2>zS$zL5>BQyC?zE*yW1ct!UUO2Eh`Lj z&Zh_CRrE1oZXCP7%qJ@}mS*?D!Q6 zMA9y|2q%-%y$B>xqP6Vlq!BTTD4FIW-DlSWt`l^hBkFUtll^DXaB(E9rF>A+;vzAp zd#(2I8pBD6eLK0sZ!tH=QmkQY-0sfAh^Z^s49$~MqFd$ogtLJ*cn|oT8^{xtFN)2X zj-=#1URQo|&|-g9q$?-^ReWUXPYgv*7<`o};mM>7McFNV>0stmCNK0SR)U&}J6F&- zx6PE(*bF(1YO@(~8e7;~Puk1M%M0sIcBtk7D5lR52_}wxkt#!fQV|mRlUgaZu>RyD z(4UC4XY?oDP!K(|Qws(C=&1i0=bc69PXZ6!m>0G_;5A8r9SKS|oQM+lRVa%Zy>68|wKY4>rZAxEK~ZE z>0`%I`q6O{CBufUDeuK@siaAhG?(Q6k1kL3Czd{F9h^kQ68yzD;T{PF&@DOzTe!CwBWR zCjmJ5meg!SQdT?Cb=~PtBvXGPk)c0XW$I6=g#P4aV;7gzpp?*`d?plr9Qu>b82Xbl zQ>u?^raysn4W|xHlsheI7fft{H65Ch4m+W%?WQ^9iKCf*j%@=fPkg45lt6RMO1fqSRKY88MpS*7BPw-I00Nr4ygUU)rSbri$ zcKl)jOn?b60Vco%m;e)C0!)AjFaajO1egF5SYiUx6V3}JJs>iY9uOHx4_eHm2S_!r zjh>pCE4K78ov&cNlj(c~lheG`J{FT|U`eJm-;4T_qWmXsTYoZ{|74c_1k+TwiT%MQ6bBntAE#*InoB2-+gZwAIn2y9U{Ya#l{EDt3i-VW+xa*7L zve$?w;$fm8Os>2lsf)TZ>=I8V;&EPaXZQ-)#3PAU%p1g*bRMAIQjyGt64^?csc_J= zJA(+pr~Q5v5*2vOB`Gb{_TRj>w`%a1F0`PN)l*J(&0e!;3G$n=k&LS|PAnsdEa;?lDw3|E-=6(8`X-!MMpChO zC*8AuzG$g(@lJY*R-64c_9mQIZcf$Woot`|^F>S53p$zJ#oH|-sae2DQH$MC^xL!F z#usohs4kmoCp*`cRw)rJTZEGZ^PkMupSVo@iS143Pd?OKR)6Ael6Jn${3lH!|H*Uv z^T!7>V}*QrQra^=^$3JLrSs$0S1+}81X?BiehAq#PsI}u z?5g#(55qXTE_HwcF##sP1egF5U;<2l2`~XBzyz286JP>NfC*f`1dO?m z1Lfl5{3rA2Tz~a<@U)xA8-l0ZME(;jsnCF6=?o3L>H0e#E0O=?U=C>)X2d7+W&w$& z@}D462%drWq5cl?pZIJ>{u7^_l&t*x94962KpORTjLa>PQ};n@VHi{laHf1n+M(Pi zed{ghPjpj%Vi=)6DUzL(*c7qD=hWXpRu_t!^>^YT`G~w_(e-!Ys5C$uMgEftGye(d z?}+>-e43@6U>)BokBuS|}33iP2kI72&B{ z*hz6-3(>UZzdpQ%^1(Uvcg%z#dRVI`&%daDFgub`+f%=ABugR`TvYuXqdb{}LlE7{ z|3gkuf2W>CRI~n0lX}m>`A?Rl{?3u|`A<-N;PHj?pS<(>lT|{0^3mdUvZ(wguTyj~ z{|QPaiTo#Z^8HJqKPhS_7Bg1w)A~CoQ7G!~plpw*zq8V;zk{WpOSO}o(jw~byk^$l zF`NkTq)~qdx|mveseJAe(J@Tb-+7IU`aAQTNI|ndDwn&2^PkM`W2frxXr}%|GxaA; zX8sdoK|ubKH>E$BQ-6o`CvU6%4oc_5nr7ACp;`5J9(-@r-x2vw*3H$Qgw6UpFwjwN zGy0RV^><3c#4K7@Nr${>T*7Ki~abncpc^mmpt|R@4^!~_yVifTa4<8uy zcV0!_M)^NfC(@GCcp%k025#WOn?b60Vco%n85p(z%|yNXlDJLD`x$j zubTCDzAEbP^v)@r^JeSsJSJvU<~n(^^>-c<>BD;$pZ`SU8^_B}Hc5}_SErVs`l#+3 z9Z%;`8e+(*UJc!6K_8*GT%u0T#Z#PiHarTzy$o&kzRslb;o-K1xI(_T!ZP1eJJ%6QMuR zP5p^JsXy7Y`1(5)(4Xkj`jbZJPjpj%BE^a#DFIbP0{w|m_v4FXGxaALO$hyojV28J z$qy$>7+BS15k9DSGwml`1(EHfSJQ8(HKs?^G<7LDc^qVaRBB$l{$zd@qiOxglnBOx zDk5pwELyCHM4gHxB_ff`NNnAbYw`M%`H@WPPs&P93?^+wk)*0|$W1aaBC(V0X6a{3 zqCc66#G1)QBN9=ZMWvLELr4;P=M#d7ID{NS($C6DPmImkw7}+cOFt>r6&UNx`?CoB z$#f)^+k;7>>0Mk3Z1)n?-&wr=giQU(|2toQ@`-D#Kat7VWc1hjMg5)E%=$af*~RFU z#pq9{T>YIT$ba%?^(RE5q(^Y<6zfm?LVxlQ>rdv_-!W!sjRR<*{3onGndzG%tLUtH z!ES$_)!*@(`V+sYKUrt$PtZ4fow`|nqR3AMA{f;=pA5m!3v_r7G6p4fjc4<@nU*p8 zT}vlsy8CK~^(Ql2Sbs9ZV(Q(lO?Y=yBn=|^aX=VfUq!`xUVnlMNs(pos+nH#s#$-> zxR8vzJN0*1e=^nL*fIeozyz286JP>NfC(@GCcp%k025#WOyC1c;2P^s?56$%;sgo@ zB407}CtnfzlNa8m{^Sv%KY4JDlQ*kBc|_<>?p?h8h4`rM#we+OE74B!dwwd6kA5tS!?EX6?k3U_^umMG$Im*8A&H1>7kiOT&_POPhO4} z<+ZSA_O~|EeUK!9fj?}R0!q7hcG9F#c1vIS0P0WP>H0fMqCYY6pRoSKtiSV`DB@!z zGI@o5%leaX?*%=)kv`@`Rxk%i+a|@6<#MRd`&578GW91eQ-8AB)Spyy{*xaF;R)+c zXhSDeMvP1>wXO?Qo|h@=(L?cXP*vzc#EU#R-iB>Kkl1D4vYjs}vd;!#%}1f{7^$dHZ%CsUVTNlME18-wAp zPS(Lmc-jf_DiZZ^{u7GDJi5#WzbnqZYkpS)RTkaB+3aLlH1>0{*&5S`A=#m z^PdD~Vz8($Q7Zq5QGdraQ-5b+{mGpCCl`%eA6}9FBu*YX**_z%TD<;be$|_|p8rH- z9Z5wIiDX71vW{FM{mGpCC(}$MWtqqcgGgM9*PoQBzq7FZq-6dRugHJ0%#9ow$baHm zSbs8=|3nr^RH`9E5$RmK$AlAdJA@N;5&DxA+dv??b_GhvAwWqUVzERPYnVH5>^bYzHD<>a#{R!tkDQYPE zmI*KcCcp%k025#WOn?b60Vco%m;e)C0!-k#An;c7C)$MZSiOb^_L0%y>-1c?6cFZC zCOhezEljG-%zyGKjj3k-6EzY>8WiL|d2UYXl10_uX_}Od1VWJ|slUVdPbglcpChMh zqS}M$Sp`*vkOTeu^x~V+pE#jEaZrmB`V$9@IH5mrKz}0D?ik7(pDE%~s{OAvDtEZ5 z4wGz8MJo@Jy+)5jP-vb$`cWd39uY|j2{MRW zsS5flQmayxzBZ(oI3&tu(9h|m#p_REsJ|1}^%(86hxBk0>hHuOz8@2Po1R*n5~jA2 zpele8rW#5ZFU8z}2F&Fx^ZRWi-=eM-nnTWKQakH48-Y33=7x z^(Ts%{{-1OME;ZY$bV81b+?mkRQhg-NaiqEH$M`+Xz#dxU;0^*lW76S?Cn`U-%0T< zc7I9w+2ZskPF%~5SOu{mT5d+SMiS$X*y z*WYBp_m^SJVD7DjIy&m;6QW7Wh zz+29L5>xb;s{yme==)KB=dzNxruBC;xkb&N)*p85qK})#b|-z^IAFR_zQbCa za0AJ<^Iml9d95<+M?EC%df`%NbzhGrddazUgaDcr<)3H`}O7q36*qLvEXr(UF+g#M%sbDhwiJSyt%+&RZdiD@`Y#7n_|OPz5d@}CGN z$ba&v$bWL@;`5(em-Tnf3t5n?8nrht4e|KijF2YePG&6UC0An##E#pa7s(^^C-<59 zlSWg2g8P`?r*F>aPu5!;9`UUd)sDto$kpuB{gKNQa>jkATa$FggIJRPMY=TO1UinA zuP_ogqf$-nugUNHy^6>)xT*WRFYpXGoaZZoJ4t?CjxJt*@;Aa)CH)QZpVXN86T?7% za%o0-lCapG%1&~-FW96IY0im={%h)R{+{3eyR_a}< zRBE;9mEouH6nV;z)G)1D$`@FPT0Zp87NV0P#F%E5Rm^$eHn~Ih1Yf13EnXAejbc&p zFqUlJQJu5&Cnb}ybjq}KQ`Ci(JR!r}Lou-wu*F>=l%0BsJ~|c2Y(YX%AQUKyWWp0f z1DT5Cycvn|Y_a~NWFTZ6Nre%~&*_YX=%gr=@B)#1Tlx1Q{YmLS=&%(NHSY2_N!>vo zor=VoNk#cc)bq+67NV1)&8b?z$sZ}%V*N?!hF{l7UX;dwdtqAk@{5%R1gd#x1fPoF%N8_#El zEvK43(nj0F+I}}~2# znn=2xo}1N9l8K^5Hubi)lkPLYj*5I$|4zmF6Nk{Bti7iC6CpgA(Vs-!D~0|<7QAtDzD>_9UVmaZ3Au`$h$Rdyo?CqVohvlvuQXPp z^Fc@!H~8ZrA13sEF2yE0r4wi>(A1PlwNZ5gTIG4d27HVvLqYsD8Jp8Vp%8jpsaE=h z*c_|X&7B;Xc5+W?C$eOA1?6&=Oyxhh&D5VbP5sFxGyjQGNESzpQ${%-)Ae@@CwBWR zCjmJ57L6K_l+9go{pCM-hwASLx_KQWwW zrjsJ09HQQ9tUtNvNs5!#MVIWoHHc|>#fzeU7!yl$XU|ajRK|KLA1!}tC&g-wsyDF} zM%9~`{YOz$y@}a=WK_L*oa&6mb|jY@I5w0!e)m+DSZueTbWW^P>70OBI%l#!DeGhD z>hG-d_@MxoY7BFR_Su8bD$=Kgk@iYEjsB!;5g*i%lp0MZwI0;ggLKMgtX=8z`~Hli zf0e#r^lW8Qmvka^N!=3WKVkjJWQ&{9pImqKcUXT?+$ZpRCcp%k025#WOn?b60Vco% zm;e)C0!)AjFaajO1en002^iB52g-fDN+rj#qnHUP(ryWLkTRJ&=$rK7;&TVFc54Rp ztvbmop)L9ilm!!pSve6imYxy@k9^k6$gX;ZN>_Z^hzCrCBmgs0q}RZZ`i^L~@Z5JH zoloq{X9|gZx$M|njl$JSJ39mSR#toL_y7OxT}y8hR}?-o9*?gdGaf(UIF7>%wB;d? z8Xif57(&2F2qi#DOuA_m4P-<@Lu~vAv_e&cR1g%X3J5h45_Ch=MHk($M=DY)RV?}k zkdP`xEUM6&HdJk=-?alIO$K&d*_+QZ*8P0%x#ylc()s3Gnp~;-kxE|?xOh(WlN`9D z`froCoj#c-$&}|gj6?Ys=D;^+rcWY&GENO>Rs#>+k0JXe)&K{k>{&w6ojW8&hV> z>gp5=co}&3g1Uy07SqrLiCsL;CP}_dcL$FYZ0k5i^sLPi+>wrw-cN+2olyhUe-_Fw zRs-)XdN@#?I5N7V;ldm1Op-i0udvZd*+%}ZGUQ{*8al!BC{K|dqKAF9RVW>{t+2Yz6GgrZpY9f_tLSo6D7oB|Bd<+~h%-EhMc%diHxR z6ilMNl^{LPFgv33AP`Ry9ix>mNEVmadC{5P0x5iSG9Fkb8aCC`%a}Dftp~9uh0;)^9O=!LJO}UFT18h zc9XbDNba+ajKWH_@}^vKxP5fRdei+_XD?w;IVu9t)g9UrvPy` zl)64mL3|GdukOWMWgNYMe#p6KQANdX8 zQ4gY$*)~cm(6h0f!jIiK_sxQj&v~@Nq;{?9s%=pOC8Efic7g-#PvRunFxND~uS~leX^o#UZEIru8Cr=&p}eJH^Ptu0nEJodEO#IGi0#;_5?*)C;A%M&B+?n;ohujj+@ z(j5SlFC3z^E7b0|!+SzJJmR+N$Ej>EX$L>y^jD(wNiRTFSO5=9?IL{K#gR6H403)C%8 Xw?N$jbqmxjP`5zc0(A@gPZszW=0e@* literal 0 HcmV?d00001 diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 30a98487b..b4ec9a4b9 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -633,12 +633,9 @@ if( NOT SEND_ANON_STATS ) add_definitions( -DNO_SEND_STATS ) endif() -# OPLMIDI needs for USE_LEGACY_EMULATOR macro to be correctly built -add_definitions(-DOPNMIDI_USE_LEGACY_EMULATOR) - -# Disable ADLMIDI's and OPNMIDI's MIDI Sequencer, MUS and XMI converters -add_definitions(-DADLMIDI_DISABLE_MUS_SUPPORT -DADLMIDI_DISABLE_XMI_SUPPORT -DADLMIDI_DISABLE_MIDI_SEQUENCER) -add_definitions(-DOPNMIDI_DISABLE_MUS_SUPPORT -DOPNMIDI_DISABLE_XMI_SUPPORT -DOPNMIDI_DISABLE_MIDI_SEQUENCER) +# Disable ADLMIDI's and OPNMIDI's MIDI Sequencer +add_definitions(-DADLMIDI_DISABLE_MIDI_SEQUENCER) +add_definitions(-DOPNMIDI_DISABLE_MIDI_SEQUENCER) # Disable OPNMIDI's experimental yet emulator (using of it has some issues and missing notes in playback) add_definitions(-DOPNMIDI_DISABLE_GX_EMULATOR) @@ -876,6 +873,7 @@ set( FASTMATH_SOURCES sound/opnmidi/opnmidi_midiplay.cpp sound/opnmidi/opnmidi_opn2.cpp sound/opnmidi/opnmidi_private.cpp + sound/opnmidi/wopn/wopn_file.c ) @@ -1352,7 +1350,12 @@ endif() add_custom_command(TARGET zdoom POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy_if_different - ${CMAKE_SOURCE_DIR}/soundfont/gzdoom.sf2 $/soundfonts/gzdoom.sf2) + ${CMAKE_SOURCE_DIR}/soundfont/gzdoom.sf2 $/soundfonts/gzdoom.sf2 + COMMAND ${CMAKE_COMMAND} -E copy_if_different + ${CMAKE_SOURCE_DIR}/fm_banks/GENMIDI.GS.wopl $/fm_banks/GENMIDI.GS.wopl + COMMAND ${CMAKE_COMMAND} -E copy_if_different + ${CMAKE_SOURCE_DIR}/fm_banks/gs-by-papiezak-and-sneakernets.wopn $/fm_banks/gs-by-papiezak-and-sneakernets.wopn +) if( CMAKE_COMPILER_IS_GNUCXX ) # GCC misoptimizes this file diff --git a/src/gameconfigfile.cpp b/src/gameconfigfile.cpp index 37abce9d7..2979420f9 100644 --- a/src/gameconfigfile.cpp +++ b/src/gameconfigfile.cpp @@ -136,17 +136,27 @@ FGameConfigFile::FGameConfigFile () SetSection("SoundfontSearch.Directories", true); #ifdef __APPLE__ SetValueForKey("Path", user_docs + "/soundfonts", true); + SetValueForKey("Path", user_docs + "/fm_banks", true); SetValueForKey("Path", user_app_support + "/soundfonts", true); + SetValueForKey("Path", user_app_support + "/fm_banks", true); SetValueForKey("Path", "$PROGDIR/soundfonts", true); + SetValueForKey("Path", "$PROGDIR/fm_banks", true); SetValueForKey("Path", local_app_support + "/soundfonts", true); + SetValueForKey("Path", local_app_support + "/fm_banks", true); #elif !defined(__unix__) SetValueForKey("Path", "$PROGDIR/soundfonts", true); + SetValueForKey("Path", "$PROGDIR/fm_banks", true); #else SetValueForKey("Path", "$HOME/" GAME_DIR "/soundfonts", true); + SetValueForKey("Path", "$HOME/" GAME_DIR "/fm_banks", true); SetValueForKey("Path", "/usr/local/share/doom/soundfonts", true); + SetValueForKey("Path", "/usr/local/share/doom/fm_banks", true); SetValueForKey("Path", "/usr/local/share/games/doom/soundfonts", true); + SetValueForKey("Path", "/usr/local/share/games/doom/fm_banks", true); SetValueForKey("Path", "/usr/share/doom/soundfonts", true); + SetValueForKey("Path", "/usr/share/doom/fm_banks", true); SetValueForKey("Path", "/usr/share/games/doom/soundfonts", true); + SetValueForKey("Path", "/usr/share/games/doom/fm_banks", true); #endif } diff --git a/src/menu/menudef.cpp b/src/menu/menudef.cpp index 19217e527..f7bf7e108 100644 --- a/src/menu/menudef.cpp +++ b/src/menu/menudef.cpp @@ -1382,10 +1382,12 @@ static void InitMusicMenus() { DMenuDescriptor **advmenu = MenuDescriptors.CheckKey("AdvSoundOptions"); auto soundfonts = sfmanager.GetList(); - std::tuple sfmenus[] = { std::make_tuple("GusConfigMenu", SF_SF2 | SF_GUS, "midi_config"), - std::make_tuple("WildMidiConfigMenu", SF_GUS, "wildmidi_config"), - std::make_tuple("TimidityConfigMenu", SF_SF2 | SF_GUS, "timidity_config"), - std::make_tuple("FluidPatchsetMenu", SF_SF2, "fluid_patchset") }; + std::tuple sfmenus[] = { std::make_tuple("GusConfigMenu", SF_SF2 | SF_GUS, "midi_config"), + std::make_tuple("WildMidiConfigMenu", SF_GUS, "wildmidi_config"), + std::make_tuple("TimidityConfigMenu", SF_SF2 | SF_GUS, "timidity_config"), + std::make_tuple("FluidPatchsetMenu", SF_SF2, "fluid_patchset"), + std::make_tuple("ADLMIDICustomBanksMenu", SF_WOPL, "adl_custom_bank"), + std::make_tuple("OPNMIDICustomBanksMenu", SF_WOPN, "opn_custom_bank")}; for (auto &p : sfmenus) { diff --git a/src/sound/adlmidi/adldata.cpp b/src/sound/adlmidi/adldata.cpp index 7215d7c89..558508975 100644 --- a/src/sound/adlmidi/adldata.cpp +++ b/src/sound/adlmidi/adldata.cpp @@ -4,7 +4,7 @@ * FROM A NUMBER OF SOURCES, MOSTLY PC GAMES. * PREPROCESSED, CONVERTED, AND POSTPROCESSED OFF-SCREEN. */ -const adldata adl[4537] = +const adldata adl[4528] = { // ,---------+-------- Wave select settings // | ,-------ч-+------ Sustain/release rates // | | ,-----ч-ч-+---- Attack/decay rates @@ -3969,8 +3969,8 @@ const adldata adl[4537] = { 0x185DC85,0x055F401, 0x91,0x0E, 0x3, +0 }, { 0x0F6E181,0x0F6E798, 0x00,0x2B, 0x1, +0 }, { 0x0F4F194,0x0A7E98A, 0x00,0x15, 0x1, +0 }, - { 0x0B3D407,0x0B4C202, 0xA0,0x00, 0xA, -12 }, - { 0x082D307,0x0E3A302, 0x58,0x80, 0xB, +0 }, + { 0x0B3D407,0x0B4C202, 0x9A,0x00, 0xA, -12 }, + { 0x082D307,0x0E3A302, 0x9A,0x00, 0xA, -12 }, { 0x156940A,0x132F411, 0xA7,0x05, 0x5, +0 }, { 0x027A2A0,0x023A522, 0x85,0x9E, 0x7, +0 }, { 0x02AA5A2,0x02AA168, 0x80,0x8F, 0x7, +0 }, @@ -3982,7 +3982,8 @@ const adldata adl[4537] = { 0x013C321,0x00B7022, 0x22,0x00, 0xE, +0 }, { 0x0F4F505,0x0F9F200, 0x29,0x1E, 0x6, +12 }, { 0x0F1F101,0x0F7F100, 0x2F,0x00, 0x6, +0 }, - { 0x0F4F405,0x0F6F100, 0x20,0x19, 0x6, +12 }, + { 0x0F4F405,0x0F6F110, 0x29,0x19, 0x6, +12 }, + { 0x0F1F111,0x0F7F100, 0x31,0x00, 0x6, +0 }, { 0x19F53C8,0x07FFAE4, 0x1C,0x03, 0x9, +0 }, { 0x0049420,0x0A5C523, 0x2A,0x24, 0xE, +12 }, { 0x0F9F200,0x0F8F101, 0x21,0x00, 0xE, +0 }, @@ -4005,15 +4006,20 @@ const adldata adl[4537] = { 0x0049100,0x2045240, 0x0F,0x00, 0x9, +0 }, { 0x0157620,0x0368261, 0x94,0x00, 0xC, +12 }, { 0x02661B1,0x0276171, 0xD3,0x80, 0xD, +0 }, + { 0x118543A,0x5177472, 0x1E,0x00, 0x4, -12 }, { 0x04A6121,0x00B7F21, 0x9F,0x00, 0xE, +0 }, { 0x00A65A1,0x0067F61, 0xA2,0x00, 0xF, +0 }, { 0x0277221,0x0067F21, 0x16,0x05, 0xC, +0 }, { 0x0866131,0x0D6C261, 0x1A,0x00, 0xE, +0 }, - { 0x0678221,0x0179222, 0x17,0x00, 0xE, +0 }, + { 0x0866131,0x0D6C261, 0x16,0x00, 0xF, +0 }, + { 0x0678221,0x0179222, 0x1A,0x00, 0xE, +0 }, + { 0x0678221,0x0179222, 0x15,0x0A, 0x7, +0 }, { 0x00AD961,0x006A861, 0x28,0x1E, 0xE, +0 }, { 0x0069A21,0x00ACF24, 0x25,0x00, 0xE, +0 }, { 0x02A9B32,0x0177221, 0x90,0x00, 0x4, +0 }, { 0x01CB632,0x01B66E1, 0x92,0x82, 0x5, +0 }, + { 0x00457F2,0x0375761, 0xA8,0x00, 0xE, +0 }, + { 0x2545C73,0x0776821, 0x00,0x0D, 0xE, +0 }, { 0x00FFF61,0x00FFF22, 0x1C,0x00, 0xE, +0 }, { 0x00FFF21,0x009CF62, 0x1C,0x00, 0xF, +0 }, { 0x0559622,0x0187421, 0x46,0x80, 0xF, +0 }, @@ -4023,31 +4029,50 @@ const adldata adl[4537] = { 0x2164460,0x00450E1, 0xAB,0x01, 0xB, +0 }, { 0x0022A55,0x0F34212, 0x97,0x86, 0x1, +0 }, { 0x1623524,0x1023171, 0x20,0x05, 0x1, +0 }, + { 0x155F261,0x0A5F242, 0x4D,0x00, 0x0, +0 }, + { 0x2343161,0x00532A1, 0x9D,0x80, 0xD, +0 }, { 0x011A131,0x0137D16, 0x87,0x08, 0x1, +0 }, - { 0x0F0A101,0x0437516, 0x0C,0x03, 0x1, +0 }, - { 0x053F201,0x052F317, 0x8F,0x09, 0x5, +0 }, + { 0x1127533,0x4F4F211, 0x58,0x03, 0x6, +0 }, + { 0x3F0F014,0x6F7F611, 0x40,0x43, 0xA, +0 }, + { 0x033F201,0x373F402, 0xD1,0x8A, 0x0, +0 }, + { 0x6A7F907,0x229A904, 0x1A,0x00, 0xA, -12 }, { 0x055C902,0x024A601, 0x1A,0x05, 0xD, +0 }, - { 0x0175E31,0x20C7B21, 0x18,0x08, 0x7, +0 }, + { 0x1397931,0x2099B22, 0x80,0x00, 0x6, +0 }, + { 0x2137931,0x1079B22, 0x42,0xC2, 0xA, +0 }, { 0x119FFA1,0x0089024, 0x0C,0x11, 0x7, +0 }, { 0x004F007,0x004F081, 0x51,0x13, 0x7, +0 }, { 0x026EC07,0x016F801, 0x15,0x00, 0xA, +0 }, { 0x001FF17,0x0057A12, 0x1C,0x0B, 0xB, +0 }, + { 0x4046306,0x005A902, 0xCA,0x08, 0x6, +0 }, + { 0x0045413,0x005A601, 0x51,0x08, 0xA, +0 }, { 0x09FF831,0x004FF10, 0x8B,0x05, 0x7, +0 }, + { 0x5C8FB00,0x0B7E601, 0x00,0x00, 0x0, +0 }, + { 0x2F0F00F,0x0F8F800, 0x00,0x40, 0xE, +12 }, { 0x001FF0E,0x20F2F01, 0x00,0x0D, 0xE, +0 }, - { 0x2077405,0x106F403, 0x80,0x0F, 0xF, +0 }, + { 0x0534313,0x7574A1F, 0x20,0x03, 0xE, -14 }, { 0x003FF15,0x0934511, 0x09,0x1F, 0xF, +0 }, - { 0x000200E,0x0022F0E, 0x00,0x0F, 0xF, +0 }, + { 0x200C327,0x6021300, 0x80,0x12, 0xE, -23 }, + { 0x200C32B,0x6021300, 0x80,0x12, 0xE, -24 }, { 0x060F209,0x072F214, 0x4F,0x19, 0xB, +0 }, { 0x1111EF0,0x11311E2, 0x00,0xC5, 0xF, +0 }, { 0x000FFEE,0x30318EE, 0x00,0x00, 0xE, +0 }, + { 0x059F802,0x01CF600, 0x11,0x00, 0xC, +0 }, + { 0x2159506,0x65AB701, 0x00,0x04, 0xE, +0 }, + { 0x10F5F81,0x0164611, 0x00,0x0A, 0x6, +0 }, + { 0x00F5F01,0x20F5F00, 0x00,0x00, 0x8, +0 }, + { 0x0D6D725,0x3A9A909, 0x1F,0x00, 0xE, -9 }, + { 0x0F0A00F,0x0F8F80F, 0x80,0x8C, 0xF, +0 }, + { 0x2FDFD00,0x6FAFA00, 0x00,0x00, 0xE, +0 }, + { 0x4F1F103,0x6FAFA07, 0x00,0x00, 0x8, +0 }, + { 0x0F0F007,0x2F6F60F, 0x27,0x00, 0x0, +21 }, { 0x0F7B710,0x005F011, 0x42,0x00, 0x8, +0 }, { 0x6EF8801,0x608B502, 0x0D,0x00, 0x0, +0 }, { 0x0F1F10F,0x007840F, 0x00,0x08, 0xC, +12 }, { 0x6EF8800,0x608F502, 0x13,0x00, 0x0, +8 }, { 0x0F1D101,0x0078400, 0x00,0x00, 0xE, +1 }, - { 0x254F307,0x307F905, 0x04,0x0B, 0x6, -5 }, - { 0x254F307,0x207F905, 0x04,0x0B, 0x8, +0 }, - { 0x25CD808,0x32B8A06, 0x04,0x08, 0xC, +0 }, + { 0x254F307,0x307F905, 0x04,0x08, 0x6, -5 }, + { 0x254F307,0x207F905, 0x04,0x08, 0x8, +0 }, + { 0x254D307,0x3288905, 0x04,0x03, 0xA, -5 }, { 0x2F2E327,0x3F5C525, 0x04,0x08, 0xA, -5 }, { 0x2F2F326,0x2F5C525, 0x04,0x08, 0x8, +0 }, { 0x292F108,0x354F201, 0x00,0x08, 0x8, +12 }, @@ -4057,17 +4082,24 @@ const adldata adl[4537] = { 0x251F206,0x263C504, 0x04,0x09, 0xA, +0 }, { 0x241F287,0x353B502, 0x05,0x09, 0xA, +1 }, { 0x292F108,0x354F201, 0x00,0x03, 0x8, +12 }, - { 0x456FB02,0x017F700, 0x81,0x00, 0x0, +12 }, - { 0x556FA01,0x117F701, 0x00,0x0D, 0x6, +10 }, - { 0x556FB02,0x117F701, 0x81,0x0D, 0x6, +10 }, - { 0x106F680,0x016F610, 0x00,0x00, 0xC, +0 }, - { 0x20F6F00,0x20F6F00, 0x00,0x00, 0x0, +0 }, - { 0x106F680,0x016F610, 0x00,0x00, 0x6, +0 }, - { 0x20F4F00,0x20F4F00, 0x00,0x00, 0x6, +0 }, - { 0x1DC5D01,0x06FF79F, 0x0B,0x00, 0xA, +12 }, - { 0x1C7C900,0x05FF49F, 0x07,0x00, 0xA, +12 }, + { 0x456FB02,0x017F700, 0x81,0x00, 0xC, +12 }, + { 0x556FA01,0x117F701, 0x00,0x0D, 0xA, +10 }, + { 0x556FB02,0x117F701, 0x81,0x0D, 0xA, +10 }, + { 0x0F00000,0x0F00000, 0x3F,0x3F, 0xC, +0 }, + { 0x000F020,0x40A8A00, 0x0A,0x00, 0xE, +0 }, + { 0x70F5F20,0x70F4F00, 0x00,0x00, 0x2, -12 }, + { 0x0D1F815,0x078F512, 0x44,0x00, 0x8, +12 }, + { 0x2D1F213,0x098F614, 0x9D,0x00, 0x0, +0 }, + { 0x2D1F213,0x098F614, 0x9D,0x21, 0x0, -2 }, + { 0x0985900,0x039870F, 0x07,0x00, 0x8, +13 }, + { 0x2F3F307,0x09C9B0F, 0x1D,0x00, 0x0, +13 }, + { 0x09C4B00,0x43A6705, 0x21,0x00, 0xC, +13 }, + { 0x0F7F907,0x2987805, 0x1C,0x00, 0x0, +13 }, { 0x3F0E00A,0x0F7F21F, 0x7C,0x40, 0x8, +0 }, { 0x3E0F50A,0x0FAF31F, 0x7C,0x40, 0x9, +0 }, + { 0x227A305,0x36A560A, 0x87,0x08, 0xE, +12 }, + { 0x247C345,0x3697809, 0x87,0x08, 0xE, +12 }, + { 0x037A309,0x06DF904, 0x11,0x00, 0xE, +0 }, { 0x1F5F213,0x0F5F111, 0xC6,0x0A, 0x0, +0 }, { 0x019F603,0x0F4F212, 0x30,0x10, 0xF, +0 }, { 0x1069FB2,0x10F94B0, 0xC0,0x86, 0x9, +0 }, @@ -4226,7 +4258,6 @@ const adldata adl[4537] = { 0x0235271,0x0198161, 0x1E,0x08, 0xE, +0 }, { 0x0235361,0x0196161, 0x1D,0x03, 0xE, +0 }, { 0x0155331,0x0378261, 0x94,0x00, 0xA, +0 }, - { 0x118543A,0x5177472, 0x1E,0x00, 0x4, -12 }, { 0x0365121,0x0257221, 0x1E,0x08, 0x0, +0 }, { 0x2844521,0x20592A0, 0x23,0x03, 0x0, +0 }, { 0x0578321,0x117C021, 0x19,0x03, 0xC, +0 }, @@ -4250,8 +4281,6 @@ const adldata adl[4537] = { 0x2176522,0x0277421, 0x5A,0x00, 0x6, +0 }, { 0x1267532,0x0166531, 0x8D,0x05, 0x4, +0 }, { 0x2F0F011,0x0987801, 0x03,0x17, 0xA, +0 }, - { 0x00457F2,0x0375761, 0xA8,0x00, 0xE, +0 }, - { 0x2545C73,0x0776821, 0x00,0x0D, 0xE, +0 }, { 0x5543737,0x25D67A1, 0x28,0x00, 0x8, +0 }, { 0x6243371,0x46D6331, 0x20,0x00, 0x6, +0 }, { 0x00F31D1,0x0053271, 0xC7,0x00, 0xB, +0 }, @@ -4304,35 +4333,22 @@ const adldata adl[4537] = { 0x2645321,0x2445521, 0x15,0x0D, 0xA, +0 }, { 0x0B37121,0x5F48221, 0x16,0x08, 0x2, +0 }, { 0x2B37102,0x5F48221, 0x90,0x08, 0x6, +0 }, - { 0x1127533,0x4F4F211, 0x58,0x03, 0x6, +0 }, - { 0x3F0F014,0x6F7F611, 0x40,0x43, 0xA, +0 }, - { 0x033F201,0x373F402, 0xD1,0x8A, 0x0, +0 }, - { 0x6A7F907,0x229A904, 0x1A,0x00, 0xA, -12 }, { 0x5E2F321,0x6E4F523, 0x1B,0x08, 0x8, +0 }, { 0x455F71C,0x0D68501, 0xA3,0x08, 0x6, +0 }, { 0x055F718,0x0D6E501, 0x23,0x08, 0x0, +0 }, - { 0x1397931,0x2099B22, 0x80,0x00, 0x6, +0 }, - { 0x2137931,0x1079B22, 0x42,0xC2, 0xA, +0 }, { 0x302A130,0x0266221, 0x1E,0x00, 0xE, +0 }, { 0x0136031,0x1169131, 0x12,0x80, 0x8, +0 }, { 0x032A115,0x172B212, 0x00,0x80, 0x1, +5 }, { 0x001E79A,0x067961C, 0x81,0x00, 0x4, +0 }, - { 0x4046306,0x005A902, 0xCA,0x08, 0x6, +0 }, - { 0x0045413,0x005A601, 0x51,0x08, 0xA, +0 }, { 0x4D1F214,0x098F715, 0xA0,0x00, 0xC, +0 }, { 0x008F312,0x004F600, 0x08,0xC8, 0x4, -12 }, { 0x27CFA01,0x004F200, 0x08,0x08, 0x0, +0 }, - { 0x5C8FB00,0x0B7E601, 0x00,0x00, 0x0, +0 }, - { 0x2F0F00F,0x0F8F800, 0x00,0x40, 0xE, +12 }, { 0x518F890,0x0E7F310, 0x00,0x00, 0x8, -12 }, { 0x250F610,0x0E7F510, 0x00,0xC8, 0x6, +0 }, { 0x2114109,0x51D2101, 0x05,0x80, 0xA, +0 }, { 0x2114108,0x31D2101, 0x05,0x80, 0xA, +12 }, - { 0x0534313,0x7574A1F, 0x20,0x03, 0xE, -14 }, { 0x00437D2,0x0343471, 0xA1,0x07, 0xC, +0 }, { 0x0F0F00C,0x0F66700, 0x00,0xCD, 0xE, +0 }, - { 0x200C327,0x6021300, 0x80,0x12, 0xE, -23 }, - { 0x200C32B,0x6021300, 0x80,0x12, 0xE, -24 }, { 0x003EBD7,0x06845D8, 0xD4,0x00, 0x7, +12 }, { 0x62FDA20,0x614B009, 0x42,0x48, 0x4, -24 }, { 0x62FDA20,0x614B009, 0x82,0x48, 0x4, -20 }, @@ -4341,15 +4357,7 @@ const adldata adl[4537] = { 0x200832F,0x6044020, 0x80,0x00, 0xE, -36 }, { 0x200832F,0x6044020, 0x80,0x00, 0xE, -35 }, { 0x2305431,0x6E7F600, 0x00,0x00, 0xE, +0 }, - { 0x059F802,0x01CF600, 0x11,0x00, 0xC, +0 }, - { 0x2159506,0x65AB701, 0x00,0x04, 0xE, +0 }, - { 0x10F5F81,0x0164611, 0x00,0x0A, 0x6, +0 }, - { 0x00F5F01,0x20F5F00, 0x00,0x00, 0x8, +0 }, - { 0x0D6D725,0x3A9A909, 0x1F,0x00, 0xE, -9 }, { 0x0F0A00F,0x0F8F80F, 0x00,0x0C, 0xE, +0 }, - { 0x2FDFD00,0x6FAFA00, 0x00,0x00, 0xE, +0 }, - { 0x4F1F103,0x6FAFA07, 0x00,0x00, 0x8, +0 }, - { 0x0F0F007,0x2F6F60F, 0x27,0x00, 0x0, +21 }, { 0x559FA00,0x047F800, 0x00,0x00, 0x4, +0 }, { 0x3F1F102,0x0078400, 0x00,0x26, 0xC, +0 }, { 0x048FA00,0x008F900, 0x00,0x00, 0x6, +12 }, @@ -4365,7 +4373,6 @@ const adldata adl[4537] = { 0x255F308,0x308F909, 0x04,0x08, 0x8, +4 }, { 0x006C604,0x007C604, 0x08,0x08, 0x1, +0 }, { 0x201F312,0x057AB09, 0x03,0x07, 0xC, +12 }, - { 0x254D307,0x3288905, 0x04,0x03, 0xA, -5 }, { 0x0015500,0x007C716, 0x0C,0x00, 0x0, +0 }, { 0x201F312,0x057AB09, 0x00,0x07, 0xC, +12 }, { 0x0015500,0x007C718, 0x0C,0x00, 0x0, +0 }, @@ -4393,9 +4400,6 @@ const adldata adl[4537] = { 0x0A5F7E9,0x0D8994A, 0x29,0x08, 0xC, +10 }, { 0x2A8F9E2,0x0779642, 0x1E,0x00, 0xE, +8 }, { 0x0A5F7E9,0x5D8994A, 0x08,0x00, 0xC, +0 }, - { 0x456FB02,0x017F700, 0x81,0x00, 0xC, +12 }, - { 0x556FA01,0x117F701, 0x00,0x0D, 0xA, +10 }, - { 0x556FB02,0x117F701, 0x81,0x0D, 0xA, +10 }, { 0x367FE06,0x668F701, 0x09,0x08, 0x8, +12 }, { 0x367FD10,0x098F901, 0x00,0x0D, 0x8, +6 }, { 0x367FE05,0x678F701, 0x09,0x08, 0x8, +12 }, @@ -4403,25 +4407,12 @@ const adldata adl[4537] = { 0x098600F,0x3FC8590, 0x08,0xC0, 0xE, +12 }, { 0x009F020,0x27DA788, 0x25,0x00, 0x0, +12 }, { 0x00FC020,0x22DA388, 0x25,0x00, 0xA, +12 }, - { 0x0F00000,0x0F00000, 0x3F,0x3F, 0xC, +0 }, - { 0x000F020,0x40A8A00, 0x0A,0x00, 0xE, +0 }, - { 0x70F5F20,0x70F4F00, 0x00,0x00, 0x2, -12 }, - { 0x0D1F815,0x078F512, 0x44,0x00, 0x8, +12 }, - { 0x2D1F213,0x098F614, 0x9D,0x00, 0x0, +0 }, - { 0x2D1F213,0x098F614, 0x9D,0x21, 0x0, -2 }, - { 0x0985900,0x039870F, 0x07,0x00, 0x8, +13 }, - { 0x2F3F307,0x09C9B0F, 0x1D,0x00, 0x0, +13 }, - { 0x09C4B00,0x43A6705, 0x21,0x00, 0xC, +13 }, - { 0x0F7F907,0x2987805, 0x1C,0x00, 0x0, +13 }, { 0x160F2C6,0x07AF4D4, 0x4F,0x80, 0x8, +12 }, { 0x160F286,0x0B7F294, 0x4F,0x80, 0x8, +12 }, - { 0x227A305,0x36A560A, 0x87,0x08, 0xE, +12 }, - { 0x247C345,0x3697809, 0x87,0x08, 0xE, +12 }, { 0x4755406,0x3667601, 0x87,0x08, 0x6, +12 }, { 0x275A346,0x3667601, 0x87,0x08, 0x6, +12 }, { 0x6E4840B,0x6E4B409, 0x12,0x09, 0x1, +0 }, { 0x6E4440B,0x6E46407, 0x21,0x13, 0x1, +3 }, - { 0x037A309,0x06DF904, 0x11,0x00, 0xE, +0 }, { 0x6F9A902,0x2F7C801, 0x00,0x40, 0x8, +0 }, { 0x4F9F901,0x4F7C713, 0x1F,0x48, 0x0, -7 }, { 0x4B7C720,0x1F3F300, 0x0B,0x00, 0x0, +0 }, @@ -4554,4822 +4545,4697 @@ const adldata adl[4537] = { 0x07BF003,0x07BF502, 0x8A,0x80, 0x8, +0 }, { 0x07BF003,0x07BF402, 0x8A,0x80, 0x8, +0 }, }; -const struct adlinsdata adlins[4804] = +const struct adlinsdata adlins[4678] = { - { 0, 0, 0, 0, 9006, 133,0 }, - { 1, 1, 0, 0, 9206, 146,0 }, - { 2, 2, 0, 0, 9246, 240,0 }, - { 3, 3, 0, 0, 9440, 140,0 }, - { 4, 4, 0, 0, 8900, 120,0 }, - { 5, 5, 0, 0, 9400, 140,0 }, - { 6, 6, 0, 0, 7460, 380,0 }, - { 7, 7, 0, 0, 9226, 93,0 }, - { 8, 8, 0, 0, 4613, 420,0 }, - { 9, 9, 0, 0, 7286, 4713,0 }, - { 10, 10, 0, 0, 2280, 746,0 }, - { 11, 11, 0, 0, 9233, 240,0 }, - { 12, 12, 0, 0, 346, 153,0 }, - { 13, 13, 0, 0, 633, 233,0 }, - { 14, 14, 0, 0, 4660, 1573,0 }, - { 15, 15, 0, 0, 1166, 400,0 }, - { 16, 16, 0, 0, 40000, 126,0 }, - { 17, 17, 0, 0, 40000, 93,0 }, - { 18, 18, 0, 0, 40000, 93,0 }, - { 19, 19, 0, 0, 40000, 553,0 }, - { 20, 20, 0, 0, 40000, 660,0 }, - { 21, 21, 0, 0, 40000, 73,0 }, - { 22, 22, 0, 0, 40000, 146,0 }, - { 23, 23, 0, 0, 40000, 146,0 }, - { 24, 24, 0, 0, 4026, 100,0 }, - { 25, 25, 0, 0, 14286, 120,0 }, - { 26, 26, 0, 0, 9233, 106,0 }, - { 27, 27, 0, 0, 4480, 100,0 }, - { 28, 28, 0, 0, 40000, 60,0 }, - { 29, 29, 0, 0, 40000, 80,0 }, - { 30, 30, 0, 0, 40000, 80,0 }, - { 31, 31, 0, 0, 18226, 100,0 }, - { 32, 32, 0, 0, 40000, 0,0 }, - { 33, 33, 0, 0, 40000, 80,0 }, - { 34, 34, 0, 0, 40000, 0,0 }, - { 35, 35, 0, 0, 40000, 53,0 }, - { 36, 36, 0, 0, 40000, 0,0 }, - { 37, 37, 0, 0, 40000, 0,0 }, - { 38, 38, 0, 0, 40000, 0,0 }, - { 39, 39, 0, 0, 40000, 160,0 }, - { 40, 40, 0, 0, 40000, 233,0 }, - { 41, 41, 0, 0, 40000, 73,0 }, - { 42, 42, 0, 0, 40000, 233,0 }, - { 43, 43, 0, 0, 40000, 213,0 }, - { 44, 44, 0, 0, 1246, 453,0 }, - { 45, 45, 0, 0, 4580, 786,0 }, - { 46, 46, 0, 0, 6873, 1246,0 }, - { 47, 47, 0, 0, 40000, 100,0 }, - { 48, 48, 0, 0, 40000, 140,0 }, - { 49, 49, 0, 0, 40000, 393,0 }, - { 50, 50, 0, 0, 40000, 406,0 }, - { 51, 51, 0, 0, 40000, 373,0 }, - { 52, 52, 0, 0, 40000, 0,0 }, - { 53, 53, 0, 0, 40000, 360,0 }, - { 54, 54, 0, 0, 1060, 380,0 }, - { 55, 55, 0, 0, 40000, 80,0 }, - { 56, 56, 0, 0, 40000, 73,0 }, - { 57, 57, 0, 0, 40000, 66,0 }, - { 58, 58, 0, 0, 40000, 60,0 }, - { 59, 59, 0, 0, 40000, 73,0 }, - { 60, 60, 0, 0, 40000, 66,0 }, - { 61, 61, 0, 0, 40000, 86,0 }, - { 62, 62, 0, 0, 40000, 66,0 }, - { 63, 63, 0, 0, 40000, 73,0 }, - { 64, 64, 0, 0, 40000, 80,0 }, - { 65, 65, 0, 0, 40000, 80,0 }, - { 66, 66, 0, 0, 40000, 73,0 }, - { 67, 67, 0, 0, 40000, 73,0 }, - { 68, 68, 0, 0, 40000, 53,0 }, - { 69, 69, 0, 0, 40000, 73,0 }, - { 70, 70, 0, 0, 40000, 126,0 }, - { 71, 71, 0, 0, 40000, 73,0 }, - { 72, 72, 0, 0, 40000, 73,0 }, - { 73, 73, 0, 0, 40000, 73,0 }, - { 74, 74, 0, 0, 40000, 66,0 }, - { 75, 75, 0, 0, 40000, 153,0 }, - { 76, 76, 0, 0, 40000, 153,0 }, - { 77, 77, 0, 0, 40000, 146,0 }, - { 78, 78, 0, 0, 40000, 146,0 }, - { 79, 79, 0, 0, 40000, 66,0 }, - { 80, 80, 0, 0, 40000, 60,0 }, - { 81, 81, 0, 0, 40000, 86,0 }, - { 82, 82, 0, 0, 40000, 73,0 }, - { 83, 83, 0, 0, 40000, 66,0 }, - { 84, 84, 0, 0, 40000, 153,0 }, - { 85, 85, 0, 0, 40000, 233,0 }, - { 86, 86, 0, 0, 40000, 80,0 }, - { 87, 87, 0, 0, 40000, 400,0 }, - { 88, 88, 0, 0, 40000, 1373,0 }, - { 89, 89, 0, 0, 40000, 193,0 }, - { 90, 90, 0, 0, 40000, 1273,0 }, - { 91, 91, 0, 0, 40000, 186,0 }, - { 92, 92, 0, 0, 40000, 86,0 }, - { 93, 93, 0, 0, 40000, 286,0 }, - { 94, 94, 0, 0, 40000, 140,0 }, - { 95, 95, 0, 0, 7440, 2473,0 }, - { 96, 96, 0, 0, 40000, 1220,0 }, - { 97, 97, 0, 0, 4946, 2713,0 }, - { 98, 98, 0, 0, 40000, 160,0 }, - { 99, 99, 0, 0, 8966, 406,0 }, - { 100, 100, 0, 0, 40000, 1353,0 }, - { 101, 101, 0, 0, 40000, 1306,0 }, - { 102, 102, 0, 0, 40000, 933,0 }, - { 103, 103, 0, 0, 9086, 226,0 }, - { 104, 104, 0, 0, 7233, 326,0 }, - { 105, 105, 0, 0, 7286, 200,0 }, - { 106, 106, 0, 0, 14180, 4406,0 }, - { 107, 107, 0, 0, 1180, 406,0 }, - { 108, 108, 0, 0, 40000, 66,0 }, - { 109, 109, 0, 0, 40000, 213,0 }, - { 110, 110, 0, 0, 40000, 73,0 }, - { 111, 111, 0, 0, 4606, 413,0 }, - { 112, 112, 0, 0, 613, 240,0 }, - { 113, 113, 0, 0, 1166, 400,0 }, - { 114, 114, 0, 0, 200, 353,0 }, - { 115, 115, 0, 0, 4553, 1480,0 }, - { 116, 116, 0, 0, 3740, 1260,0 }, - { 117, 117, 0, 0, 7240, 2300,0 }, - { 118, 118, 0, 0, 3020, 73,0 }, - { 119, 119, 0, 0, 1626, 800,0 }, - { 120, 120, 0, 0, 2466, 620,0 }, - { 121, 121, 0, 0, 12053, 3160,0 }, - { 122, 122, 0, 0, 466, 120,0 }, - { 123, 123, 0, 0, 1000, 320,0 }, - { 124, 124, 0, 0, 380, 60,0 }, - { 125, 125, 0, 0, 40000, 200,0 }, - { 126, 126, 0, 0, 560, 86,0 }, - { 127, 127, 35, 0, 386, 160,0 }, - { 128, 128, 52, 0, 126, 26,0 }, - { 129, 129, 48, 0, 286, 126,0 }, - { 130, 130, 58, 0, 173, 93,0 }, - { 129, 129, 60, 0, 286, 126,0 }, - { 131, 131, 47, 0, 520, 200,0 }, - { 132, 132, 43, 0, 173, 93,0 }, - { 131, 131, 49, 0, 520, 200,0 }, - { 133, 133, 43, 0, 160, 80,0 }, - { 131, 131, 51, 0, 526, 206,0 }, - { 134, 134, 43, 0, 1860, 653,0 }, - { 131, 131, 54, 0, 520, 200,0 }, - { 131, 131, 57, 0, 520, 200,0 }, - { 135, 135, 72, 0, 1860, 633,0 }, - { 131, 131, 60, 0, 506, 200,0 }, - { 136, 136, 76, 0, 1566, 546,0 }, - { 137, 137, 84, 0, 1340, 466,0 }, - { 138, 138, 36, 0, 1220, 433,0 }, - { 139, 139, 65, 0, 293, 133,0 }, - { 140, 140, 84, 0, 1333, 460,0 }, - { 141, 141, 83, 0, 220, 113,0 }, - { 135, 135, 84, 0, 1366, 473,0 }, - { 142, 142, 24, 0, 1893, 633,0 }, - { 136, 136, 77, 0, 1586, 553,0 }, - { 143, 143, 60, 0, 173, 93,0 }, - { 144, 144, 65, 0, 213, 126,0 }, - { 145, 145, 59, 0, 173, 0,0 }, - { 146, 146, 51, 0, 173, 100,0 }, - { 147, 147, 45, 0, 260, 206,0 }, - { 148, 148, 71, 0, 433, 180,0 }, - { 149, 149, 60, 0, 280, 26,0 }, - { 150, 150, 58, 0, 500, 186,0 }, - { 151, 151, 53, 0, 513, 200,0 }, - { 152, 152, 64, 0, 220, 86,0 }, - { 153, 153, 71, 0, 106, 46,0 }, - { 154, 154, 61, 0, 993, 340,0 }, - { 155, 155, 61, 0, 1906, 640,0 }, - { 156, 156, 44, 0, 206, 86,0 }, - { 157, 157, 40, 0, 586, 140,0 }, - { 158, 158, 69, 0, 126, 140,0 }, - { 159, 159, 68, 0, 126, 140,0 }, - { 160, 160, 63, 0, 146, 166,0 }, - { 161, 161, 74, 0, 280, 100,0 }, - { 162, 162, 60, 0, 1026, 320,0 }, - { 163, 163, 80, 0, 226, 100,0 }, - { 164, 164, 64, 0, 2713, 913,0 }, - { 165, 165, 72, 0, 120, 66,0 }, - { 166, 166, 73, 0, 386, 80,0 }, - { 167, 167, 70, 0, 553, 306,0 }, - { 168, 168, 68, 0, 126, 140,0 }, - { 169, 169, 48, 0, 386, 373,0 }, - { 131, 131, 53, 0, 520, 206,0 }, - { 170, 170, 0, 0, 40000, 0,0 }, - { 171, 171, 0, 0, 40000, 73,0 }, - { 172, 173, 0, 4, 5886, 100,0 }, - { 174, 175, 0, 4, 6913, 0,0 }, - { 176, 177, 0, 4, 4873, 0,0 }, - { 178, 178, 0, 0, 40000, 0,0 }, - { 179, 180, 0, 4, 4653, 433,0 }, - { 181, 181, 0, 0, 2280, 746,0 }, - { 182, 182, 0, 0, 40000, 0,0 }, - { 183, 184, 0, 4, 626, 0,0 }, - { 185, 186, 0, 4, 4653, 1546,0 }, - { 187, 187, 0, 0, 1166, 400,0 }, - { 188, 189, 0, 4, 40000, 60,0 }, - { 190, 191, 0, 4, 40000, 60,0 }, - { 192, 193, 0, 4, 40000, 73,0 }, - { 194, 194, 0, 0, 40000, 73,0 }, - { 195, 196, 0, 4, 40000, 66,0 }, - { 197, 198, 0, 4, 40000, 86,0 }, - { 199, 200, 0, 4, 40000, 66,0 }, - { 201, 202, 0, 4, 3713, 100,0 }, - { 203, 204, 0, 4, 14753, 126,0 }, - { 205, 206, 0, 4, 9286, 146,0 }, - { 207, 208, 0, 4, 14713, 126,0 }, - { 209, 210, 0, 4, 4653, 0,0 }, - { 211, 212, 0, 4, 40000, 66,0 }, - { 213, 213, 0, 0, 40000, 73,0 }, - { 214, 215, 0, 4, 626, 0,0 }, - { 216, 217, 0, 4, 4066, 100,0 }, - { 218, 219, 0, 4, 14586, 193,0 }, - { 220, 221, 0, 4, 2813, 106,0 }, - { 222, 223, 0, 4, 500, 0,0 }, - { 224, 224, 0, 0, 40000, 0,0 }, - { 225, 226, 0, 4, 7993, 93,0 }, - { 227, 227, 0, 0, 40000, 0,0 }, - { 228, 228, 0, 0, 40000, 133,0 }, - { 229, 230, 0, 4, 720, 213,0 }, - { 231, 232, 0, 4, 40000, 146,0 }, - { 233, 234, 0, 4, 40000, 0,0 }, - { 235, 236, 0, 4, 1000, 340,0 }, - { 235, 237, 0, 4, 3280, 1120,0 }, - { 46, 238, 0, 4, 6920, 0,0 }, - { 239, 240, 0, 4, 40000, 140,0 }, - { 241, 242, 0, 4, 40000, 146,0 }, - { 243, 243, 0, 0, 40000, 100,0 }, - { 244, 244, 0, 0, 40000, 60,0 }, - { 245, 245, 0, 0, 40000, 73,0 }, - { 246, 247, 0, 4, 720, 106,0 }, - { 248, 249, 0, 4, 40000, 126,0 }, - { 250, 250, 0, 0, 40000, 0,0 }, - { 251, 251, 0, 0, 40000, 126,0 }, - { 252, 253, 0, 4, 40000, 66,0 }, - { 254, 255, 0, 4, 40000, 93,0 }, - { 256, 257, 0, 4, 40000, 73,0 }, - { 258, 259, 0, 4, 40000, 86,0 }, - { 260, 261, 0, 4, 40000, 93,0 }, - { 262, 263, 0, 4, 40000, 80,0 }, - { 264, 265, 0, 4, 40000, 200,0 }, - { 266, 267, 0, 4, 40000, 73,0 }, - { 268, 269, 0, 4, 40000, 80,0 }, - { 270, 271, 0, 4, 40000, 73,0 }, - { 272, 273, 0, 4, 40000, 126,0 }, - { 274, 275, 0, 4, 40000, 100,0 }, - { 276, 276, 0, 0, 40000, 113,0 }, - { 277, 278, 0, 4, 40000, 186,0 }, - { 279, 280, 0, 4, 40000, 160,0 }, - { 281, 282, 0, 4, 40000, 206,0 }, - { 283, 283, 0, 0, 40000, 80,0 }, - { 284, 285, 0, 4, 40000, 73,0 }, - { 286, 287, 0, 4, 40000, 73,0 }, - { 288, 288, 0, 0, 40000, 93,0 }, - { 289, 290, 0, 4, 40000, 66,0 }, - { 291, 292, 0, 4, 40000, 153,0 }, - { 293, 294, 0, 4, 40000, 153,0 }, - { 295, 296, 0, 4, 40000, 320,0 }, - { 88, 297, 0, 4, 40000, 1280,0 }, - { 298, 299, 0, 4, 40000, 266,0 }, - { 300, 301, 0, 4, 40000, 1180,0 }, - { 302, 302, 0, 0, 40000, 286,0 }, - { 303, 303, 0, 0, 40000, 140,0 }, - { 304, 304, 0, 0, 13246, 2473,0 }, - { 305, 306, 0, 4, 40000, 1073,0 }, - { 307, 307, 0, 0, 9233, 240,0 }, - { 308, 308, 0, 0, 1186, 406,0 }, - { 309, 309, 0, 0, 40000, 1306,0 }, - { 310, 310, 0, 0, 40000, 933,0 }, - { 311, 312, 0, 4, 9146, 240,0 }, - { 313, 314, 0, 4, 7306, 326,0 }, - { 315, 316, 0, 4, 3586, 326,0 }, - { 317, 318, 0, 4, 7180, 0,0 }, - { 107, 319, 0, 4, 1180, 406,0 }, - { 108, 320, 0, 4, 40000, 66,0 }, - { 109, 321, 0, 4, 720, 213,0 }, - { 322, 323, 0, 4, 40000, 73,0 }, - { 324, 325, 0, 4, 613, 246,0 }, - { 326, 327, 0, 4, 1213, 386,0 }, - { 328, 328, 0, 0, 173, 106,0 }, - { 329, 329, 0, 0, 966, 333,0 }, - { 330, 331, 0, 4, 1906, 320,0 }, - { 332, 332, 0, 0, 3120, 73,0 }, - { 333, 333, 0, 0, 226, 73,0 }, - { 334, 334, 0, 0, 6600, 806,0 }, - { 335, 335, 0, 0, 273, 60,0 }, - { 336, 336, 0, 0, 12053, 660,0 }, - { 337, 337, 0, 0, 40000, 240,0 }, - { 338, 339, 0, 6, 6, 0,0 }, - { 340, 341, 0, 4, 560, 0,0 }, - { 342, 342, 35, 0, 40000, 0,0 }, - { 343, 343, 0, 0, 180, 100,0 }, - { 344, 344, 35, 0, 340, 146,0 }, - { 345, 345, 35, 0, 213, 33,0 }, - { 346, 346, 50, 0, 306, 20,0 }, - { 347, 347, 18, 0, 420, 146,0 }, - { 348, 348, 72, 0, 173, 86,0 }, - { 349, 349, 74, 0, 160, 93,0 }, - { 350, 350, 35, 0, 380, 146,0 }, - { 351, 351, 16, 0, 1206, 420,0 }, - { 352, 352, 0, 2, 6, 0,0 }, - { 353, 353, 38, 0, 200, 106,0 }, - { 354, 354, 38, 0, 346, 146,0 }, - { 355, 355, 31, 0, 406, 20,0 }, - { 355, 355, 35, 0, 406, 66,0 }, - { 355, 355, 38, 0, 406, 66,0 }, - { 355, 355, 41, 0, 406, 66,0 }, - { 355, 355, 45, 0, 306, 73,0 }, - { 355, 355, 50, 0, 306, 73,0 }, - { 356, 356, 36, 0, 1373, 493,0 }, - { 357, 357, 36, 0, 146, 33,0 }, - { 358, 358, 48, 0, 213, 86,0 }, - { 358, 358, 36, 0, 246, 86,0 }, - { 359, 359, 36, 0, 113, 0,0 }, - { 360, 360, 0, 0, 133, 40,0 }, - { 361, 361, 61, 0, 180, 26,0 }, - { 362, 362, 96, 0, 706, 266,0 }, - { 363, 363, 38, 0, 520, 193,0 }, - { 127, 127, 16, 0, 620, 233,0 }, - { 364, 365, 18, 4, 200, 0,0 }, - { 366, 366, 30, 0, 406, 246,0 }, - { 367, 368, 35, 4, 200, 0,0 }, - { 129, 129, 0, 0, 353, 153,0 }, - { 369, 369, 0, 0, 213, 13,0 }, - { 370, 370, 88, 0, 333, 113,0 }, - { 371, 371, 88, 0, 140, 73,0 }, - { 372, 372, 79, 0, 2540, 1040,0 }, - { 135, 135, 14, 0, 9213, 3066,0 }, - { 373, 373, 46, 0, 1093, 60,0 }, - { 374, 375,129, 4, 1200, 433,0 }, - { 376, 376, 58, 0, 1600, 726,0 }, - { 377, 377,164, 0, 526, 820,0 }, - { 378, 378,142, 0, 9153, 3073,0 }, - { 379, 379, 9, 0, 200, 100,0 }, - { 380, 381, 35, 4, 2353, 813,0 }, - { 382, 382, 28, 0, 1060, 120,0 }, - { 383, 383, 46, 0, 953, 20,0 }, - { 384, 384, 60, 0, 440, 160,0 }, - { 384, 384, 54, 0, 513, 180,0 }, - { 385, 385, 72, 0, 253, 120,0 }, - { 385, 385, 67, 0, 253, 113,0 }, - { 385, 385, 60, 0, 253, 106,0 }, - { 386, 386, 1, 0, 966, 613,0 }, - { 387, 387, 77, 0, 340, 86,0 }, - { 387, 387, 72, 0, 340, 86,0 }, - { 388, 388, 90, 0, 213, 86,0 }, - { 389, 389, 39, 0, 266, 73,0 }, - { 390, 390, 36, 0, 593, 73,0 }, - { 391, 392, 35, 4, 173, 46,0 }, - { 391, 393, 35, 4, 460, 66,0 }, - { 394, 394, 60, 0, 173, 20,0 }, - { 328, 328, 7, 0, 173, 0,0 }, - { 395, 395, 90, 0, 193, 20,0 }, - { 396, 396, 90, 0, 793, 40,0 }, - { 397, 397, 35, 0, 253, 86,0 }, - { 398, 399, 5, 4, 1913, 226,0 }, - { 400, 400,103, 0, 713, 273,0 }, - { 401, 401, 3, 0, 100, 0,0 }, - { 169, 169, 1, 0, 466, 413,0 }, - { 131, 131, 0, 0, 613, 226,0 }, - { 402, 402, 36, 0, 273, 53,0 }, - { 403, 403, 60, 0, 40000, 73,0 }, - { 404, 404, 37, 0, 1193, 426,0 }, - { 405, 405, 36, 0, 406, 20,0 }, - { 406, 406, 32, 0, 146, 73,0 }, - { 407, 407, 50, 0, 40000, 0,0 }, - { 408, 408, 50, 0, 793, 346,0 }, - { 409, 409, 83, 0, 120, 13,0 }, - { 410, 410, 72, 0, 433, 0,0 }, - { 148, 148, 59, 0, 513, 200,0 }, - { 411, 411, 64, 0, 173, 93,0 }, - { 411, 411, 60, 0, 173, 93,0 }, - { 412, 412, 72, 0, 160, 93,0 }, - { 412, 412, 62, 0, 173, 93,0 }, - { 413, 413, 83, 0, 773, 60,0 }, - { 414, 414, 0, 0, 40000, 80,0 }, - { 415, 415, 0, 0, 40000, 0,0 }, - { 416, 416, 0, 0, 40000, 73,0 }, - { 417, 417, 0, 0, 40000, 86,0 }, - { 418, 418, 0, 0, 40000, 0,0 }, - { 419, 419, 0, 0, 3440, 100,0 }, - { 420, 420, 0, 0, 3913, 420,0 }, - { 421, 421, 0, 0, 13620, 4640,0 }, - { 422, 422, 0, 0, 9233, 240,0 }, - { 423, 423, 0, 0, 633, 233,0 }, - { 424, 424, 0, 0, 4660, 1573,0 }, - { 425, 425, 0, 0, 4480, 1413,0 }, - { 426, 426, 0, 0, 40000, 0,0 }, - { 427, 427, 0, 0, 40000, 86,0 }, - { 428, 428, 60, 2, 6, 0,0 }, - { 429, 429, 73, 0, 593, 86,0 }, - { 429, 429, 74, 0, 593, 86,0 }, - { 429, 429, 80, 0, 593, 86,0 }, - { 429, 429, 84, 0, 593, 86,0 }, - { 429, 429, 92, 0, 520, 86,0 }, - { 430, 430, 81, 0, 786, 80,0 }, - { 430, 430, 83, 0, 786, 80,0 }, - { 430, 430, 95, 0, 680, 80,0 }, - { 431, 431, 35, 0, 593, 140,0 }, - { 432, 432, 60, 0, 213, 133,0 }, - { 357, 357, 59, 0, 113, 0,0 }, - { 432, 432, 44, 0, 213, 133,0 }, - { 433, 433, 41, 0, 713, 273,0 }, - { 434, 434, 97, 0, 113, 46,0 }, - { 433, 433, 44, 0, 513, 206,0 }, - { 433, 433, 48, 0, 506, 200,0 }, - { 435, 435, 96, 0, 700, 86,0 }, - { 433, 433, 51, 0, 520, 200,0 }, - { 433, 433, 54, 0, 513, 206,0 }, - { 436, 436, 40, 0, 1506, 793,0 }, - { 433, 433, 57, 0, 380, 160,0 }, - { 437, 437, 58, 0, 1600, 726,0 }, - { 438, 438, 97, 0, 233, 106,0 }, - { 439, 439, 50, 0, 186, 93,0 }, - { 437, 437, 60, 0, 1573, 713,0 }, - { 440, 440, 53, 0, 180, 73,0 }, - { 441, 441, 46, 0, 173, 126,0 }, - { 440, 440, 57, 0, 180, 40,0 }, - { 442, 442, 42, 0, 640, 240,0 }, - { 442, 442, 37, 0, 633, 233,0 }, - { 443, 443, 41, 0, 626, 240,0 }, - { 443, 443, 37, 0, 620, 233,0 }, - { 444, 444, 77, 0, 173, 40,0 }, - { 444, 444, 72, 0, 173, 40,0 }, - { 445, 445, 70, 0, 233, 100,0 }, - { 445, 445, 90, 0, 233, 93,0 }, - { 446, 446, 46, 0, 133, 73,0 }, - { 447, 447, 48, 0, 333, 73,0 }, - { 448, 448, 85, 0, 106, 0,0 }, - { 449, 449, 66, 0, 180, 26,0 }, - { 449, 449, 61, 0, 180, 26,0 }, - { 450, 450, 41, 0, 200, 66,0 }, - { 451, 451, 41, 0, 253, 66,0 }, - { 452, 452, 81, 0, 253, 26,0 }, - { 400, 400, 81, 0, 820, 306,0 }, - { 400, 400, 76, 0, 813, 300,0 }, - { 359, 359, 60, 0, 100, 0,0 }, - { 453, 453, 53, 0, 40000, 0,0 }, - { 454, 454, 0, 2, 6, 0,0 }, - { 455, 455, 0, 0, 200, 20,0 }, - { 456, 456, 0, 0, 4480, 100,0 }, - { 457, 457, 0, 0, 1180, 406,0 }, - { 458, 458, 0, 0, 40000, 86,0 }, - { 459, 459, 0, 0, 40000, 73,0 }, - { 460, 460, 0, 0, 3700, 66,0 }, - { 461, 461, 0, 0, 40000, 0,0 }, - { 462, 462, 0, 0, 6746, 2606,0 }, - { 463, 463, 0, 0, 40000, 213,0 }, - { 464, 464, 0, 0, 40000, 66,0 }, - { 465, 465, 0, 0, 40000, 100,0 }, - { 466, 466, 0, 0, 40000, 100,0 }, - { 467, 467, 0, 0, 5840, 806,0 }, - { 468, 468, 0, 0, 40000, 0,0 }, - { 469, 469, 0, 0, 40000, 0,0 }, - { 470, 470, 0, 0, 40000, 73,0 }, - { 471, 471, 0, 0, 40000, 133,0 }, - { 472, 472, 0, 0, 3320, 800,0 }, - { 473, 473, 0, 0, 40000, 173,0 }, - { 474, 474, 0, 0, 40000, 193,0 }, - { 475, 475, 0, 0, 2373, 800,0 }, - { 476, 476, 0, 0, 40000, 4986,0 }, - { 477, 477, 0, 0, 1180, 413,0 }, - { 478, 478, 0, 0, 3673, 1200,0 }, - { 479, 479, 0, 0, 973, 800,0 }, - { 480, 480, 0, 0, 7233, 2286,0 }, - { 481, 481, 0, 0, 40000, 73,0 }, - { 482, 482, 0, 0, 2526, 73,0 }, - { 483, 483, 0, 0, 393, 126,0 }, - { 484, 484, 0, 0, 40000, 200,0 }, - { 485, 485, 0, 0, 40000, 546,0 }, - { 486, 486, 0, 0, 1186, 413,0 }, - { 487, 487, 0, 0, 14166, 320,0 }, - { 488, 488, 0, 0, 8326, 646,0 }, - { 489, 489, 0, 0, 513, 206,0 }, - { 490, 490, 0, 0, 40000, 93,0 }, - { 491, 491, 50, 0, 1406, 353,0 }, - { 492, 492, 37, 0, 1040, 400,0 }, - { 493, 493, 39, 0, 406, 73,0 }, - { 494, 494, 39, 0, 3746, 860,0 }, - { 495, 495, 86, 0, 2133, 173,0 }, - { 496, 496, 43, 0, 140, 66,0 }, - { 127, 127, 24, 0, 513, 206,0 }, - { 127, 127, 29, 0, 520, 206,0 }, - { 497, 497, 50, 0, 340, 20,0 }, - { 498, 498, 30, 0, 5306, 1266,0 }, - { 498, 498, 33, 0, 3773, 886,0 }, - { 498, 498, 38, 0, 3746, 860,0 }, - { 498, 498, 42, 0, 3793, 906,0 }, - { 499, 499, 24, 0, 266, 0,0 }, - { 499, 499, 27, 0, 260, 153,0 }, - { 499, 499, 29, 0, 260, 153,0 }, - { 499, 499, 32, 0, 260, 153,0 }, - { 500, 500, 32, 0, 106, 0,0 }, - { 501, 501, 53, 0, 373, 186,0 }, - { 501, 501, 57, 0, 380, 193,0 }, - { 502, 502, 60, 0, 286, 133,0 }, - { 503, 503, 55, 0, 460, 126,0 }, - { 486, 486, 85, 0, 813, 293,0 }, - { 504, 504, 90, 0, 1580, 546,0 }, - { 505, 505, 84, 0, 246, 120,0 }, - { 506, 506, 48, 0, 826, 646,0 }, - { 507, 507, 48, 0, 266, 213,0 }, - { 132, 132, 72, 0, 126, 66,0 }, - { 508, 508, 72, 0, 106, 0,0 }, - { 509, 509, 72, 0, 100, 0,0 }, - { 510, 510, 63, 0, 1860, 633,0 }, - { 510, 510, 65, 0, 1853, 633,0 }, - { 511, 511, 79, 0, 1573, 553,0 }, - { 512, 512, 38, 0, 520, 793,0 }, - { 513, 513, 94, 0, 380, 160,0 }, - { 514, 514, 87, 0, 433, 306,0 }, - { 514, 514, 94, 0, 380, 273,0 }, - { 515, 515, 80, 0, 546, 273,0 }, - { 516, 516, 47, 0, 506, 200,0 }, - { 517, 517, 61, 0, 286, 133,0 }, - { 517, 517, 68, 0, 246, 120,0 }, - { 518, 518, 61, 0, 513, 206,0 }, - { 518, 518, 68, 0, 433, 180,0 }, - { 499, 499, 60, 0, 220, 133,0 }, - { 519, 519, 60, 0, 153, 46,0 }, - { 520, 520, 36, 0, 200, 20,0 }, - { 520, 520, 60, 0, 173, 20,0 }, - { 521, 521, 60, 0, 173, 20,0 }, - { 522, 522, 68, 0, 126, 26,0 }, - { 523, 523, 71, 0, 160, 186,0 }, - { 523, 523, 72, 0, 160, 186,0 }, - { 524, 524,101, 0, 966, 353,0 }, - { 525, 525, 36, 0, 3333, 480,0 }, - { 526, 526, 25, 0, 40000, 2293,0 }, - { 527, 527, 37, 0, 2106, 426,0 }, - { 528, 528, 36, 0, 720, 266,0 }, - { 528, 528, 41, 0, 713, 266,0 }, - { 529, 529, 84, 0, 173, 60,0 }, - { 530, 530, 54, 0, 40000, 0,0 }, - { 481, 481, 48, 0, 40000, 73,0 }, - { 531, 531, 0, 0, 10060, 1266,0 }, - { 532, 532, 0, 0, 4600, 606,0 }, - { 533, 533, 0, 0, 40000, 253,0 }, - { 534, 534, 0, 0, 40000, 73,0 }, - { 535, 535, 0, 0, 40000, 66,0 }, - { 536, 536, 0, 0, 40000, 80,0 }, - { 537, 537, 0, 0, 9413, 1393,0 }, - { 538, 538, 0, 0, 9000, 66,0 }, - { 539, 539, 0, 0, 40000, 0,0 }, - { 540, 540, 0, 0, 40000, 80,0 }, - { 541, 541, 0, 0, 40000, 120,0 }, - { 542, 542, 0, 0, 253, 73,0 }, - { 543, 543, 0, 0, 40000, 73,0 }, - { 544, 544, 0, 0, 18280, 800,0 }, - { 545, 545, 0, 0, 40000, 1133,0 }, - { 546, 546, 0, 0, 40000, 1226,0 }, - { 547, 547, 0, 0, 40000, 153,0 }, - { 135, 135, 49, 0, 3633, 1186,0 }, - { 548, 548, 35, 0, 2193, 80,0 }, - { 549, 549, 41, 0, 73, 0,0 }, - { 366, 366, 38, 0, 406, 246,0 }, - { 550, 550, 39, 0, 106, 20,0 }, - { 551, 551, 49, 0, 200, 133,0 }, - { 408, 408, 59, 0, 780, 326,0 }, - { 552, 552, 24, 0, 40000, 0,0 }, - { 552, 552, 27, 0, 40000, 0,0 }, - { 552, 552, 29, 0, 40000, 0,0 }, - { 552, 552, 32, 0, 40000, 0,0 }, - { 553, 553, 84, 0, 200, 33,0 }, - { 512, 512, 79, 0, 346, 460,0 }, - { 554, 554, 61, 0, 400, 126,0 }, - { 554, 554, 68, 0, 353, 120,0 }, - { 555, 555, 36, 0, 146, 86,0 }, - { 555, 555, 60, 0, 113, 0,0 }, - { 556, 556, 36, 0, 273, 53,0 }, - { 115, 115, 37, 0, 4580, 1513,0 }, - { 557, 557, 0, 0, 3806, 73,0 }, - { 558, 558, 0, 0, 40000, 0,0 }, - { 559, 559, 0, 0, 40000, 66,0 }, - { 560, 560, 0, 0, 5886, 133,0 }, - { 561, 561, 0, 0, 253, 26,0 }, - { 562, 562, 0, 0, 3246, 753,0 }, - { 563, 563, 0, 0, 40000, 100,0 }, - { 564, 564, 0, 0, 1620, 366,0 }, - { 565, 565, 0, 0, 40000, 0,0 }, - { 566, 566, 0, 0, 40000, 0,0 }, - { 567, 567, 0, 0, 40000, 0,0 }, - { 568, 568, 0, 0, 40000, 80,0 }, - { 569, 569, 0, 0, 760, 340,0 }, - { 570, 570, 0, 0, 40000, 0,0 }, - { 571, 571, 0, 0, 40000, 0,0 }, - { 572, 572, 0, 0, 40000, 0,0 }, - { 356, 356, 0, 0, 1893, 646,0 }, - { 573, 573, 0, 0, 40000, 93,0 }, - { 574, 574, 0, 0, 40000, 93,0 }, - { 575, 575, 0, 0, 40000, 200,0 }, - { 576, 576, 0, 0, 40000, 200,0 }, - { 577, 577, 0, 0, 40000, 126,0 }, - { 578, 578, 0, 0, 40000, 353,0 }, - { 579, 579, 0, 0, 40000, 346,0 }, - { 580, 580, 0, 0, 40000, 353,0 }, - { 581, 581, 0, 0, 40000, 100,0 }, - { 582, 582, 0, 0, 40000, 133,0 }, - { 583, 583, 0, 0, 2286, 713,0 }, - { 584, 584, 0, 0, 40000, 193,0 }, - { 585, 585, 0, 0, 40000, 0,0 }, - { 516, 516, 0, 0, 633, 240,0 }, - { 586, 586, 0, 0, 40000, 73,0 }, - { 587, 587, 0, 0, 40000, 73,0 }, - { 588, 588, 0, 0, 40000, 73,0 }, - { 498, 498, 26, 0, 5293, 1253,0 }, - { 494, 494, 35, 0, 3800, 913,0 }, - { 350, 350, 41, 0, 380, 153,0 }, - { 353, 353, 48, 0, 173, 100,0 }, - { 354, 354, 67, 0, 246, 120,0 }, - { 502, 502, 24, 0, 340, 146,0 }, - { 346, 346, 36, 0, 406, 73,0 }, - { 346, 346, 38, 0, 406, 20,0 }, - { 346, 346, 40, 0, 406, 73,0 }, - { 346, 346, 42, 0, 406, 20,0 }, - { 346, 346, 44, 0, 306, 20,0 }, - { 510, 510, 55, 0, 1866, 646,0 }, - { 346, 346, 46, 0, 306, 20,0 }, - { 136, 136, 80, 0, 1600, 573,0 }, - { 486, 486, 24, 0, 1193, 426,0 }, - { 153, 153, 50, 0, 106, 40,0 }, - { 346, 346, 24, 0, 540, 73,0 }, - { 516, 516, 31, 0, 626, 240,0 }, - { 498, 498, 35, 0, 3760, 880,0 }, - { 517, 517, 60, 0, 286, 133,0 }, - { 530, 530, 36, 0, 40000, 0,0 }, - { 530, 530, 48, 0, 40000, 0,0 }, - { 589, 589, 0, 0, 40000, 0,0 }, - { 139, 139, 76, 0, 253, 106,0 }, - { 156, 156, 48, 0, 206, 80,0 }, - { 157, 157, 48, 0, 426, 106,0 }, - { 165, 165, 69, 0, 120, 66,0 }, - { 167, 167, 75, 0, 546, 306,0 }, - { 590, 590, 0, 0, 40000, 0,0 }, - { 591, 591, 0, 0, 15486, 1580,0 }, - { 592, 592, 0, 0, 3446, 106,0 }, - { 593, 593, 0, 0, 1926, 146,0 }, - { 594, 594, 0, 0, 7293, 2380,0 }, - { 595, 595, 0, 0, 7613, 1566,0 }, - { 596, 596, 0, 0, 1153, 460,0 }, - { 597, 597, 0, 0, 1166, 400,0 }, - { 598, 598, 0, 0, 40000, 73,0 }, - { 599, 599, 0, 0, 40000, 766,0 }, - { 600, 600, 0, 0, 40000, 80,0 }, - { 601, 601, 0, 0, 1840, 513,0 }, - { 602, 602, 0, 0, 40000, 0,0 }, - { 603, 603, 0, 0, 4480, 733,0 }, - { 604, 604, 0, 0, 18226, 786,0 }, - { 605, 605, 0, 0, 4333, 233,0 }, - { 606, 606, 0, 0, 40000, 106,0 }, - { 607, 607, 0, 0, 40000, 366,0 }, - { 608, 608, 0, 0, 40000, 200,0 }, - { 609, 609, 0, 0, 713, 200,0 }, - { 610, 610, 0, 0, 8866, 1366,0 }, - { 611, 611, 0, 0, 2300, 73,0 }, - { 612, 612, 0, 0, 40000, 126,0 }, - { 613, 613, 0, 0, 40000, 1413,0 }, - { 614, 614, 0, 0, 40000, 333,0 }, - { 615, 615, 0, 0, 40000, 333,0 }, - { 616, 616, 0, 0, 40000, 26,0 }, - { 617, 617, 0, 0, 40000, 40,0 }, - { 618, 618, 0, 0, 4240, 353,0 }, - { 619, 619, 0, 0, 40000, 0,0 }, - { 620, 620, 0, 0, 40000, 73,0 }, - { 621, 621, 0, 0, 9020, 60,0 }, - { 622, 622, 0, 0, 3020, 0,0 }, - { 623, 623, 0, 0, 40000, 60,0 }, - { 624, 624, 0, 0, 40000, 73,0 }, - { 625, 625, 0, 0, 40000, 60,0 }, - { 626, 626, 0, 0, 40000, 53,0 }, - { 627, 627, 0, 0, 40000, 0,0 }, - { 628, 628, 0, 0, 40000, 66,0 }, - { 629, 629, 0, 0, 40000, 66,0 }, - { 630, 630, 0, 0, 5913, 426,0 }, - { 631, 631, 0, 0, 40000, 246,0 }, - { 632, 632, 0, 0, 40000, 206,0 }, - { 633, 633, 0, 0, 40000, 0,0 }, - { 634, 634, 0, 0, 2453, 780,0 }, - { 635, 635, 0, 0, 4740, 240,0 }, - { 636, 636, 0, 0, 1840, 353,0 }, - { 637, 637, 0, 0, 40000, 86,0 }, - { 638, 638, 0, 0, 3446, 1786,0 }, - { 346, 346, 0, 0, 540, 20,0 }, - { 639, 639, 0, 0, 7406, 2486,0 }, - { 404, 404, 0, 0, 1220, 466,0 }, - { 506, 506, 0, 0, 1000, 813,0 }, - { 639, 639, 60, 0, 2666, 913,0 }, - { 639, 639, 79, 0, 1366, 486,0 }, - { 640, 640, 65, 0, 2053, 646,0 }, - { 486, 486, 31, 0, 1206, 440,0 }, - { 486, 486, 36, 0, 1200, 433,0 }, - { 640, 640, 72, 0, 1713, 520,0 }, - { 136, 136, 79, 0, 1580, 560,0 }, - { 148, 148, 57, 0, 520, 206,0 }, - { 150, 150, 53, 0, 500, 193,0 }, - { 641, 641, 84, 0, 226, 66,0 }, - { 520, 520, 66, 0, 173, 20,0 }, - { 642, 642, 31, 0, 40000, 113,0 }, - { 642, 642, 29, 0, 40000, 113,0 }, - { 356, 356, 31, 0, 1366, 486,0 }, - { 356, 356, 19, 0, 1866, 633,0 }, - { 643, 643, 31, 0, 40000, 73,0 }, - { 643, 643, 29, 0, 40000, 73,0 }, - { 644, 644, 31, 0, 2286, 400,0 }, - { 644, 644, 35, 0, 2313, 420,0 }, - { 644, 644, 40, 0, 2353, 433,0 }, - { 644, 644, 47, 0, 1860, 346,0 }, - { 516, 516, 32, 0, 626, 240,0 }, - { 516, 516, 43, 0, 506, 200,0 }, - { 495, 495, 26, 0, 3180, 240,0 }, - { 495, 495, 44, 0, 2553, 206,0 }, - { 496, 496, 26, 0, 160, 73,0 }, - { 496, 496, 51, 0, 146, 66,0 }, - { 496, 496, 39, 0, 160, 73,0 }, - { 495, 495, 30, 0, 3180, 240,0 }, - { 645, 645, 44, 0, 1880, 653,0 }, - { 645, 645, 43, 0, 1886, 653,0 }, - { 646, 646, 0, 0, 2393, 833,0 }, - { 647, 647, 0, 0, 4693, 26,0 }, - { 648, 648, 0, 0, 2306, 773,0 }, - { 649, 649, 0, 0, 40000, 120,0 }, - { 650, 650, 0, 0, 40000, 66,0 }, - { 651, 651, 0, 0, 5866, 1206,0 }, - { 652, 652, 0, 0, 40000, 426,0 }, - { 653, 653, 0, 0, 1873, 633,0 }, - { 654, 654, 0, 0, 40000, 66,0 }, - { 655, 655, 0, 0, 40000, 73,0 }, - { 656, 656, 0, 0, 40000, 73,0 }, - { 657, 657, 0, 0, 40000, 0,0 }, - { 658, 658, 0, 0, 2040, 380,0 }, - { 659, 659, 0, 0, 40000, 73,0 }, - { 660, 660, 0, 0, 3720, 1260,0 }, - { 661, 661, 0, 0, 4080, 1046,0 }, - { 662, 662, 0, 0, 8693, 4666,0 }, - { 663, 663, 0, 0, 1926, 73,0 }, - { 664, 664, 0, 0, 8326, 646,0 }, - { 665, 665, 0, 0, 40000, 240,0 }, - { 666, 666, 0, 0, 40000, 226,0 }, - { 667, 667, 0, 0, 40000, 220,0 }, - { 668, 668, 0, 0, 40000, 0,0 }, - { 669, 669, 0, 0, 40000, 193,0 }, - { 670, 670, 0, 0, 880, 20,0 }, - { 671, 671, 0, 0, 4873, 120,0 }, - { 672, 672, 0, 0, 40000, 413,0 }, - { 673, 673, 0, 0, 700, 106,0 }, - { 674, 674, 0, 0, 700, 100,0 }, - { 675, 675, 0, 0, 40000, 126,0 }, - { 676, 676, 0, 0, 8113, 806,0 }, - { 677, 677, 0, 0, 8900, 80,0 }, - { 678, 678, 0, 0, 1893, 653,0 }, - { 679, 679, 0, 0, 3973, 206,0 }, - { 680, 680, 0, 0, 40000, 173,0 }, - { 681, 681, 0, 0, 40000, 73,0 }, - { 682, 682, 0, 0, 40000, 93,0 }, - { 683, 683, 0, 0, 1606, 640,0 }, - { 684, 684, 0, 0, 15486, 1580,0 }, - { 685, 685, 0, 0, 40000, 346,0 }, - { 686, 686, 0, 0, 40000, 786,0 }, - { 687, 687, 0, 0, 386, 240,0 }, - { 688, 688, 0, 0, 40000, 2066,0 }, - { 689, 689, 0, 0, 15453, 73,0 }, - { 690, 690, 0, 0, 1206, 240,0 }, - { 691, 691, 0, 0, 8866, 1366,0 }, - { 692, 692, 0, 0, 5913, 2253,0 }, - { 693, 693, 0, 0, 773, 106,0 }, - { 694, 694, 0, 0, 3793, 73,0 }, - { 695, 695, 0, 0, 40000, 73,0 }, - { 645, 645, 0, 0, 3633, 1180,0 }, - { 696, 696, 0, 0, 40000, 80,0 }, - { 697, 697, 0, 0, 40000, 0,0 }, - { 698, 698, 0, 0, 40000, 66,0 }, - { 699, 699, 0, 0, 40000, 66,0 }, - { 700, 700, 0, 0, 106, 0,0 }, - { 701, 701, 0, 0, 40000, 200,0 }, - { 702, 702, 0, 0, 3913, 73,0 }, - { 703, 703, 0, 0, 40000, 73,0 }, - { 704, 704, 0, 0, 40000, 73,0 }, - { 705, 705, 0, 0, 40000, 73,0 }, - { 706, 706, 0, 0, 40000, 66,0 }, - { 707, 707, 0, 0, 40000, 313,0 }, - { 708, 708, 0, 0, 40000, 100,0 }, - { 709, 709, 0, 0, 40000, 213,0 }, - { 710, 710, 0, 0, 40000, 53,0 }, - { 711, 711, 0, 0, 40000, 40,0 }, - { 712, 712, 0, 0, 40000, 73,0 }, - { 713, 713, 0, 0, 40000, 140,0 }, - { 714, 714, 0, 0, 40000, 606,0 }, - { 715, 715, 0, 0, 40000, 226,0 }, - { 716, 716, 0, 0, 3746, 1273,0 }, - { 717, 717, 0, 0, 40000, 80,0 }, - { 718, 718, 0, 0, 2360, 806,0 }, - { 719, 719, 0, 0, 1186, 420,0 }, - { 720, 720, 0, 0, 12533, 1953,0 }, - { 721, 721, 0, 0, 973, 1280,0 }, - { 722, 722, 0, 0, 40000, 426,0 }, - { 723, 723, 0, 0, 40000, 53,0 }, - { 724, 724, 0, 0, 40000, 66,0 }, - { 725, 725, 0, 0, 1246, 73,0 }, - { 726, 726, 0, 0, 3726, 1246,0 }, - { 727, 727, 0, 0, 2346, 813,0 }, - { 728, 728, 0, 0, 1206, 433,0 }, - { 507, 507, 0, 0, 306, 246,0 }, - { 512, 512, 0, 0, 526, 840,0 }, - { 729, 729, 0, 0, 14793, 4933,0 }, - { 730, 730, 0, 0, 14640, 4806,0 }, - { 731, 731, 0, 0, 5233, 633,0 }, - { 732, 732, 0, 0, 40000, 2513,0 }, - { 733, 733, 0, 0, 40000, 820,0 }, - { 734, 734, 0, 0, 40000, 0,0 }, - { 735, 735, 0, 0, 1726, 793,0 }, - { 736, 736, 0, 0, 513, 20,0 }, - { 737, 737, 0, 2, 6, 0,0 }, - { 738, 738, 38, 0, 1020, 413,0 }, - { 739, 739, 44, 0, 220, 33,0 }, - { 500, 500, 58, 0, 100, 0,0 }, - { 740, 740, 24, 0, 513, 206,0 }, - { 741, 741, 60, 0, 220, 26,0 }, - { 736, 736, 44, 0, 286, 20,0 }, - { 742, 742, 25, 0, 626, 246,0 }, - { 743, 743, 60, 0, 146, 86,0 }, - { 742, 742, 30, 0, 626, 240,0 }, - { 377, 377, 60, 0, 446, 626,0 }, - { 742, 742, 33, 0, 620, 226,0 }, - { 744, 744, 60, 0, 220, 113,0 }, - { 742, 742, 35, 0, 620, 233,0 }, - { 742, 742, 37, 0, 633, 246,0 }, - { 745, 745, 0, 0, 1880, 640,0 }, - { 742, 742, 40, 0, 640, 260,0 }, - { 746, 746,102, 0, 960, 300,0 }, - { 747, 747, 80, 0, 1106, 126,0 }, - { 377, 377, 0, 0, 500, 760,0 }, - { 748, 748, 56, 0, 100, 0,0 }, - { 749, 749, 0, 0, 973, 1300,0 }, - { 746, 746,100, 0, 960, 340,0 }, - { 750, 750, 40, 0, 626, 240,0 }, - { 750, 750, 35, 0, 626, 240,0 }, - { 751, 751, 29, 0, 206, 106,0 }, - { 750, 750, 29, 0, 633, 240,0 }, - { 750, 750, 22, 0, 640, 233,0 }, - { 500, 500, 0, 0, 106, 0,0 }, - { 752, 752, 0, 0, 206, 26,0 }, - { 753, 753, 84, 0, 166, 20,0 }, - { 754, 754, 84, 0, 1580, 553,0 }, - { 755, 755, 0, 0, 633, 233,0 }, - { 755, 755, 71, 0, 440, 180,0 }, - { 755, 755, 53, 0, 513, 200,0 }, - { 755, 755, 48, 0, 520, 206,0 }, - { 756, 756, 95, 0, 286, 20,0 }, - { 757, 757, 95, 0, 1880, 20,0 }, - { 758, 758, 0, 0, 14413, 333,0 }, - { 759, 759, 0, 0, 14453, 360,0 }, - { 760, 760, 0, 0, 14940, 353,0 }, - { 761, 761, 0, 0, 7286, 340,0 }, - { 762, 762, 0, 0, 14700, 60,0 }, - { 763, 763, 0, 0, 14506, 340,0 }, - { 764, 764, 0, 0, 14706, 200,0 }, - { 765, 765, 0, 0, 40000, 0,0 }, - { 766, 766, 0, 0, 2900, 426,0 }, - { 767, 767, 0, 0, 2986, 753,0 }, - { 768, 768, 0, 0, 1706, 680,0 }, - { 769, 769, 0, 0, 14646, 1253,0 }, - { 770, 770, 0, 0, 1713, 486,0 }, - { 771, 771, 0, 0, 966, 346,0 }, - { 772, 772, 0, 0, 3453, 766,0 }, - { 773, 773, 0, 0, 2866, 486,0 }, - { 774, 774, 0, 0, 40000, 73,0 }, - { 775, 775, 0, 0, 40000, 73,0 }, - { 776, 776, 0, 0, 40000, 166,0 }, - { 777, 777, 0, 0, 40000, 126,0 }, - { 778, 778, 0, 0, 40000, 113,0 }, - { 779, 779, 0, 0, 40000, 113,0 }, - { 780, 780, 0, 0, 40000, 93,0 }, - { 781, 781, 0, 0, 40000, 200,0 }, - { 782, 782, 0, 0, 7186, 93,0 }, - { 783, 783, 0, 0, 6406, 120,0 }, - { 784, 784, 0, 0, 40000, 0,0 }, - { 785, 785, 0, 0, 40000, 0,0 }, - { 786, 786, 0, 0, 1220, 73,0 }, - { 787, 787, 0, 0, 40000, 0,0 }, - { 788, 788, 0, 0, 17566, 66,0 }, - { 789, 789, 0, 0, 2333, 26,0 }, - { 790, 790, 0, 0, 4560, 153,0 }, - { 791, 791, 0, 0, 40000, 0,0 }, - { 792, 792, 0, 0, 40000, 0,0 }, - { 793, 793, 0, 0, 40000, 0,0 }, - { 794, 794, 0, 0, 2506, 126,0 }, - { 795, 795, 0, 0, 2513, 126,0 }, - { 796, 796, 0, 0, 40000, 0,0 }, - { 797, 797, 0, 0, 3386, 80,0 }, - { 798, 798, 0, 0, 40000, 100,0 }, - { 799, 799, 0, 0, 40000, 100,0 }, - { 800, 800, 0, 0, 40000, 120,0 }, - { 801, 801, 0, 0, 40000, 0,0 }, - { 802, 802, 0, 0, 40000, 200,0 }, - { 803, 803, 0, 0, 1080, 180,0 }, - { 804, 804, 0, 0, 3620, 1166,0 }, - { 805, 805, 0, 0, 1186, 393,0 }, - { 806, 806, 0, 0, 40000, 213,0 }, - { 807, 807, 0, 0, 40000, 426,0 }, - { 808, 808, 0, 0, 40000, 146,0 }, - { 809, 809, 0, 0, 40000, 146,0 }, - { 810, 810, 0, 0, 40000, 60,0 }, - { 811, 811, 0, 0, 40000, 113,0 }, - { 812, 812, 0, 0, 40000, 93,0 }, - { 813, 813, 0, 0, 1186, 153,0 }, - { 814, 814, 0, 0, 40000, 0,0 }, - { 815, 815, 0, 0, 40000, 80,0 }, - { 816, 816, 0, 0, 40000, 80,0 }, - { 817, 817, 0, 0, 40000, 46,0 }, - { 818, 818, 0, 0, 40000, 0,0 }, - { 819, 819, 0, 0, 40000, 66,0 }, - { 820, 820, 0, 0, 40000, 126,0 }, - { 821, 821, 0, 0, 40000, 213,0 }, - { 822, 822, 0, 0, 40000, 80,0 }, - { 823, 823, 0, 0, 40000, 73,0 }, - { 824, 824, 0, 0, 40000, 73,0 }, - { 825, 825, 0, 0, 40000, 100,0 }, - { 826, 826, 0, 0, 40000, 93,0 }, - { 827, 827, 0, 0, 40000, 73,0 }, - { 828, 828, 0, 0, 40000, 73,0 }, - { 829, 829, 0, 0, 40000, 80,0 }, - { 830, 830, 0, 0, 40000, 80,0 }, - { 831, 831, 0, 0, 40000, 80,0 }, - { 832, 832, 0, 0, 40000, 73,0 }, - { 833, 833, 0, 0, 40000, 80,0 }, - { 834, 834, 0, 0, 40000, 86,0 }, - { 835, 835, 0, 0, 40000, 100,0 }, - { 836, 836, 0, 0, 40000, 100,0 }, - { 837, 837, 0, 0, 40000, 140,0 }, - { 838, 838, 0, 0, 40000, 73,0 }, - { 839, 839, 0, 0, 40000, 0,0 }, - { 840, 840, 0, 0, 40000, 93,0 }, - { 841, 841, 0, 0, 40000, 0,0 }, - { 842, 842, 0, 0, 40000, 0,0 }, - { 843, 843, 0, 0, 40000, 73,0 }, - { 844, 844, 0, 0, 40000, 66,0 }, - { 845, 845, 0, 0, 40000, 0,0 }, - { 846, 846, 0, 0, 40000, 193,0 }, - { 847, 847, 0, 0, 40000, 340,0 }, - { 848, 848, 0, 0, 40000, 233,0 }, - { 849, 849, 0, 0, 40000, 80,0 }, - { 850, 850, 0, 0, 40000, 186,0 }, - { 851, 851, 0, 0, 9973, 426,0 }, - { 852, 852, 0, 0, 40000, 200,0 }, - { 853, 853, 0, 0, 40000, 400,0 }, - { 854, 854, 0, 0, 14633, 200,0 }, - { 855, 855, 0, 0, 40000, 333,0 }, - { 856, 856, 0, 0, 4620, 800,0 }, - { 857, 857, 0, 0, 8940, 386,0 }, - { 858, 858, 0, 0, 8966, 740,0 }, - { 859, 859, 0, 0, 40000, 273,0 }, - { 860, 860, 0, 0, 40000, 126,0 }, - { 861, 861, 0, 0, 40000, 400,0 }, - { 862, 862, 0, 0, 4480, 213,0 }, - { 863, 863, 0, 0, 633, 100,0 }, - { 864, 864, 0, 0, 3740, 353,0 }, - { 865, 865, 0, 0, 2333, 406,0 }, - { 866, 866, 0, 0, 1933, 566,0 }, - { 867, 867, 0, 0, 40000, 93,0 }, - { 868, 868, 0, 0, 40000, 106,0 }, - { 869, 869, 0, 0, 40000, 100,0 }, - { 870, 870, 0, 0, 3093, 240,0 }, - { 871, 871, 0, 0, 513, 93,0 }, - { 872, 872, 0, 0, 700, 180,0 }, - { 361, 361, 0, 0, 373, 40,0 }, - { 873, 873, 0, 0, 1046, 446,0 }, - { 874, 874, 0, 0, 1886, 520,0 }, - { 875, 875, 0, 0, 1226, 366,0 }, - { 876, 876, 0, 0, 4193, 73,0 }, - { 877, 877, 0, 0, 826, 120,0 }, - { 878, 878, 0, 0, 280, 146,0 }, - { 879, 879, 0, 0, 5266, 806,0 }, - { 880, 880, 0, 0, 386, 80,0 }, - { 881, 881, 0, 0, 40000, 100,0 }, - { 882, 882, 0, 0, 40000, 413,0 }, - { 883, 883, 0, 0, 40000, 0,0 }, - { 884, 884, 36, 0, 233, 80,0 }, - { 885, 885, 48, 0, 193, 93,0 }, - { 885, 885, 36, 0, 226, 100,0 }, - { 886, 886, 36, 0, 113, 0,0 }, - { 887, 887, 32, 0, 133, 40,0 }, - { 767, 767, 96, 0, 1760, 480,0 }, - { 888, 888, 30, 0, 246, 40,0 }, - { 889, 889, 35, 0, 420, 140,0 }, - { 890, 890, 60, 0, 240, 60,0 }, - { 884, 884, 59, 0, 146, 20,0 }, - { 890, 890, 44, 0, 240, 60,0 }, - { 891, 891, 41, 0, 713, 273,0 }, - { 892, 892, 47, 0, 173, 93,0 }, - { 891, 891, 44, 0, 513, 206,0 }, - { 891, 891, 48, 0, 506, 200,0 }, - { 893, 893, 62, 0, 1926, 93,0 }, - { 891, 891, 51, 0, 520, 200,0 }, - { 891, 891, 54, 0, 513, 206,0 }, - { 894, 894, 40, 0, 1280, 793,0 }, - { 891, 891, 57, 0, 380, 160,0 }, - { 895, 895, 97, 0, 233, 106,0 }, - { 896, 896, 50, 0, 220, 93,0 }, - { 376, 376, 60, 0, 1573, 713,0 }, - { 897, 897, 53, 0, 126, 73,0 }, - { 898, 898, 46, 0, 173, 133,0 }, - { 897, 897, 57, 0, 126, 33,0 }, - { 899, 899, 42, 0, 626, 233,0 }, - { 899, 899, 37, 0, 633, 240,0 }, - { 900, 900, 41, 0, 626, 240,0 }, - { 900, 900, 37, 0, 626, 240,0 }, - { 871, 871, 77, 0, 173, 40,0 }, - { 871, 871, 72, 0, 173, 40,0 }, - { 388, 388, 70, 0, 213, 86,0 }, - { 901, 901, 39, 0, 260, 26,0 }, - { 902, 902, 36, 0, 1093, 73,0 }, - { 903, 903, 46, 0, 120, 73,0 }, - { 904, 904, 48, 0, 766, 80,0 }, - { 905, 905, 85, 0, 126, 26,0 }, - { 361, 361, 66, 0, 180, 26,0 }, - { 906, 906, 41, 0, 193, 73,0 }, - { 907, 907, 41, 0, 333, 106,0 }, - { 908, 908, 81, 0, 160, 26,0 }, - { 400, 400, 10, 0, 1186, 413,0 }, - { 886, 886, 60, 0, 100, 0,0 }, - { 873, 873, 53, 0, 846, 360,0 }, - { 909, 909, 0, 0, 5593, 340,0 }, - { 910, 910, 0, 0, 14646, 346,0 }, - { 911, 911, 0, 0, 6826, 280,0 }, - { 912, 912, 0, 0, 7000, 306,0 }, - { 913, 913, 0, 0, 8793, 133,0 }, - { 914, 914, 0, 0, 14680, 346,0 }, - { 915, 915, 0, 0, 7246, 126,0 }, - { 916, 916, 0, 0, 40000, 0,0 }, - { 917, 917, 0, 0, 1866, 433,0 }, - { 362, 362, 0, 0, 1106, 340,0 }, - { 918, 918, 0, 0, 1053, 273,0 }, - { 919, 919, 0, 0, 14513, 1213,0 }, - { 920, 920, 0, 0, 1886, 646,0 }, - { 921, 921, 0, 0, 926, 313,0 }, - { 922, 922, 0, 0, 2340, 806,0 }, - { 923, 923, 0, 0, 2966, 553,0 }, - { 924, 924, 0, 0, 40000, 66,0 }, - { 925, 925, 0, 0, 40000, 73,0 }, - { 926, 926, 0, 0, 40000, 0,0 }, - { 927, 927, 0, 0, 40000, 126,0 }, - { 928, 928, 0, 0, 40000, 113,0 }, - { 929, 929, 0, 0, 40000, 113,0 }, - { 930, 930, 0, 0, 40000, 93,0 }, - { 931, 931, 0, 0, 40000, 113,0 }, - { 932, 932, 0, 0, 7200, 86,0 }, - { 933, 933, 0, 0, 5373, 106,0 }, - { 934, 934, 0, 0, 40000, 0,0 }, - { 935, 935, 0, 0, 40000, 0,0 }, - { 936, 936, 0, 0, 2380, 73,0 }, - { 937, 937, 0, 0, 40000, 0,0 }, - { 938, 938, 0, 0, 40000, 0,0 }, - { 939, 939, 0, 0, 6013, 53,0 }, - { 940, 940, 0, 0, 3713, 126,0 }, - { 941, 941, 0, 0, 17566, 26,0 }, - { 942, 942, 0, 0, 40000, 0,0 }, - { 943, 943, 0, 0, 40000, 0,0 }, - { 944, 944, 0, 0, 2506, 126,0 }, - { 945, 945, 0, 0, 3733, 73,0 }, - { 946, 946, 0, 0, 40000, 0,0 }, - { 947, 947, 0, 0, 3386, 80,0 }, - { 948, 948, 0, 0, 40000, 100,0 }, - { 949, 949, 0, 0, 40000, 100,0 }, - { 950, 950, 0, 0, 40000, 113,0 }, - { 951, 951, 0, 0, 40000, 0,0 }, - { 952, 952, 0, 0, 40000, 200,0 }, - { 953, 953, 0, 0, 1140, 213,0 }, - { 954, 954, 0, 0, 2140, 400,0 }, - { 955, 955, 0, 0, 813, 240,0 }, - { 956, 956, 0, 0, 40000, 100,0 }, - { 957, 957, 0, 0, 40000, 426,0 }, - { 958, 958, 0, 0, 40000, 0,0 }, - { 959, 959, 0, 0, 40000, 146,0 }, - { 960, 960, 0, 0, 40000, 120,0 }, - { 961, 961, 0, 0, 40000, 93,0 }, - { 962, 962, 0, 0, 1193, 153,0 }, - { 963, 963, 0, 0, 40000, 46,0 }, - { 964, 964, 0, 0, 40000, 80,0 }, - { 965, 965, 0, 0, 40000, 80,0 }, - { 966, 966, 0, 0, 40000, 20,0 }, - { 967, 967, 0, 0, 40000, 0,0 }, - { 968, 968, 0, 0, 40000, 93,0 }, - { 969, 969, 0, 0, 40000, 86,0 }, - { 970, 970, 0, 0, 40000, 213,0 }, - { 971, 971, 0, 0, 40000, 80,0 }, - { 972, 972, 0, 0, 40000, 73,0 }, - { 973, 973, 0, 0, 40000, 0,0 }, - { 974, 974, 0, 0, 40000, 93,0 }, - { 975, 975, 0, 0, 40000, 73,0 }, - { 976, 976, 0, 0, 40000, 73,0 }, - { 977, 977, 0, 0, 40000, 66,0 }, - { 978, 978, 0, 0, 40000, 66,0 }, - { 979, 979, 0, 0, 40000, 100,0 }, - { 980, 980, 0, 0, 40000, 73,0 }, - { 981, 981, 0, 0, 40000, 73,0 }, - { 982, 982, 0, 0, 40000, 80,0 }, - { 983, 983, 0, 0, 40000, 100,0 }, - { 984, 984, 0, 0, 40000, 100,0 }, - { 985, 985, 0, 0, 40000, 100,0 }, - { 986, 986, 0, 0, 40000, 80,0 }, - { 987, 987, 0, 0, 40000, 73,0 }, - { 988, 988, 0, 0, 40000, 0,0 }, - { 989, 989, 0, 0, 40000, 86,0 }, - { 990, 990, 0, 0, 40000, 0,0 }, - { 991, 991, 0, 0, 40000, 0,0 }, - { 992, 992, 0, 0, 40000, 80,0 }, - { 993, 993, 0, 0, 40000, 86,0 }, - { 994, 994, 0, 0, 40000, 0,0 }, - { 995, 995, 0, 0, 40000, 0,0 }, - { 996, 996, 0, 0, 40000, 333,0 }, - { 997, 997, 0, 0, 40000, 180,0 }, - { 998, 998, 0, 0, 40000, 80,0 }, - { 999, 999, 0, 0, 40000, 120,0 }, - {1000,1000, 0, 0, 10006, 460,0 }, - {1001,1001, 0, 0, 40000, 186,0 }, - {1002,1002, 0, 0, 40000, 400,0 }, - {1003,1003, 0, 0, 20333, 260,0 }, - {1004,1004, 0, 0, 40000, 373,0 }, - {1005,1005, 0, 0, 4520, 400,0 }, - {1006,1006, 0, 0, 8213, 306,0 }, - {1007,1007, 0, 0, 8646, 360,0 }, - {1008,1008, 0, 0, 40000, 160,0 }, - {1009,1009, 0, 0, 40000, 133,0 }, - {1010,1010, 0, 0, 40000, 400,0 }, - {1011,1011, 0, 0, 4473, 193,0 }, - {1012,1012, 0, 0, 1813, 0,0 }, - {1013,1013, 0, 0, 3726, 353,0 }, - {1014,1014, 0, 0, 4400, 373,0 }, - {1015,1015, 0, 0, 953, 166,0 }, - {1016,1016, 0, 0, 40000, 73,0 }, - {1017,1017, 0, 0, 40000, 100,0 }, - {1018,1018, 0, 0, 40000, 100,0 }, - {1019,1019, 0, 0, 3100, 240,0 }, - { 444, 444, 0, 0, 513, 93,0 }, - {1020,1020, 0, 0, 626, 180,0 }, - { 449, 449, 0, 0, 373, 80,0 }, - { 453, 453, 0, 0, 40000, 0,0 }, - {1021,1021, 0, 0, 1020, 340,0 }, - {1022,1022, 0, 0, 1200, 366,0 }, - {1023,1023, 0, 0, 4193, 73,0 }, - {1024,1024, 0, 0, 820, 120,0 }, - {1025,1025, 0, 0, 680, 213,0 }, - {1026,1026, 0, 0, 5260, 806,0 }, - {1027,1027, 0, 0, 9193, 86,0 }, - {1028,1028, 0, 0, 40000, 100,0 }, - {1029,1029, 0, 0, 40000, 426,0 }, - {1030,1030, 0, 0, 40000, 260,0 }, - {1031,1031, 0, 0, 3480, 66,0 }, - {1032,1032, 32, 0, 133, 46,0 }, - {1033,1033, 30, 0, 200, 40,0 }, - {1034,1034, 96, 0, 146, 73,0 }, - {1035,1035, 60, 0, 553, 186,0 }, - {1036,1036, 0, 0, 13193, 260,0 }, - {1037,1037, 0, 0, 40000, 100,0 }, - {1038,1038, 0, 0, 7980, 66,0 }, - {1039,1039, 0, 0, 40000, 0,0 }, - {1040,1040, 0, 0, 980, 340,0 }, - {1041,1041, 0, 0, 7413, 2480,0 }, - {1042,1042, 0, 0, 2906, 520,0 }, - {1043,1043, 0, 0, 40000, 73,0 }, - {1044,1044, 0, 0, 40000, 53,0 }, - {1045,1045, 0, 0, 40000, 113,0 }, - {1046,1046, 0, 0, 5380, 113,0 }, - {1047,1047, 0, 0, 40000, 0,0 }, - {1048,1048, 0, 0, 2366, 73,0 }, - {1049,1049, 0, 0, 40000, 0,0 }, - {1050,1050, 0, 0, 18293, 80,0 }, - {1051,1051, 0, 0, 18466, 146,0 }, - {1052,1052, 0, 0, 9220, 73,0 }, - {1053,1053, 0, 0, 40000, 240,0 }, - {1054,1054, 0, 0, 40000, 0,0 }, - {1055,1055, 0, 0, 1086, 126,0 }, - {1056,1056, 0, 0, 3766, 73,0 }, - {1057,1057, 0, 0, 1186, 226,0 }, - {1058,1058, 0, 0, 3373, 73,0 }, - {1059,1059, 0, 0, 40000, 246,0 }, - {1060,1060, 0, 0, 340, 220,0 }, - {1061,1061, 0, 0, 1186, 386,0 }, - {1062,1062, 0, 0, 40000, 253,0 }, - {1063,1063, 0, 0, 40000, 440,0 }, - {1064,1064, 0, 0, 40000, 46,0 }, - {1065,1065, 0, 0, 40000, 80,0 }, - {1066,1066, 0, 0, 40000, 126,0 }, - {1067,1067, 0, 0, 40000, 133,0 }, - {1068,1068, 0, 0, 40000, 93,0 }, - {1069,1069, 0, 0, 40000, 86,0 }, - {1070,1070, 0, 0, 40000, 93,0 }, - {1071,1071, 0, 0, 40000, 66,0 }, - {1072,1072, 0, 0, 40000, 93,0 }, - {1073,1073, 0, 0, 40000, 73,0 }, - {1074,1074, 0, 0, 40000, 173,0 }, - {1075,1075, 0, 0, 586, 193,0 }, - {1076,1076, 0, 0, 40000, 146,0 }, - {1077,1077, 0, 0, 18460, 73,0 }, - {1078,1078, 0, 0, 846, 93,0 }, - {1079,1079, 0, 0, 40000, 0,0 }, - {1080,1080, 0, 0, 40000, 86,0 }, - {1081,1081, 0, 0, 40000, 0,0 }, - {1082,1082, 0, 0, 40000, 353,0 }, - {1083,1083, 0, 0, 40000, 300,0 }, - {1084,1084, 0, 0, 40000, 320,0 }, - {1085,1085, 0, 0, 9920, 1553,0 }, - {1086,1086, 0, 0, 40000, 386,0 }, - {1087,1087, 0, 0, 40000, 0,0 }, - {1088,1088, 0, 0, 9980, 873,0 }, - {1089,1089, 0, 0, 40000, 386,0 }, - {1090,1090, 0, 0, 966, 126,0 }, - {1091,1091, 0, 0, 40000, 820,0 }, - {1092,1092, 0, 0, 8620, 366,0 }, - {1093,1093, 0, 0, 40000, 826,0 }, - {1094,1094, 0, 0, 40000, 433,0 }, - {1095,1095, 0, 0, 633, 73,0 }, - {1096,1096, 0, 0, 3693, 126,0 }, - {1097,1097, 0, 0, 40000, 0,0 }, - {1098,1098, 0, 0, 40000, 153,0 }, - {1099,1099, 0, 0, 40000, 0,0 }, - {1100,1100, 0, 0, 40000, 0,0 }, - {1101,1101, 0, 0, 40000, 306,0 }, - {1102,1102, 0, 0, 3666, 3093,0 }, - {1103,1103, 0, 0, 1873, 653,0 }, - {1104,1104, 0, 0, 40000, 0,0 }, - {1105,1105, 0, 0, 11293, 886,0 }, - {1106,1106, 0, 0, 40000, 546,0 }, - { 430, 430, 0, 0, 1146, 80,0 }, - {1107,1107, 35, 0, 580, 80,0 }, - {1090,1090, 77, 0, 280, 60,0 }, - {1090,1090, 72, 0, 280, 60,0 }, - {1108,1108, 0, 0, 10180, 600,0 }, - {1109,1109, 0, 0, 10053, 353,0 }, - {1110,1111, 0, 1, 9940, 480,0 }, - {1112,1113, 0, 1, 10620, 473,0.03125 }, - {1114,1114, 0, 0, 40000, 0,0 }, - {1115,1116, 0, 1, 9833, 220,0 }, - {1117,1117, 0, 0, 10286, 473,0 }, - {1118,1118, 0, 0, 7686, 93,0 }, - {1119,1119, 0, 0, 7220, 613,0 }, - {1120,1120, 0, 0, 11513, 1666,0 }, - {1121,1121, 0, 0, 5200, 1700,0 }, - {1122,1122, 0, 0, 10173, 626,0 }, - {1123,1123, 0, 0, 1206, 380,0 }, - {1124,1124, 0, 0, 1953, 866,0 }, - {1125,1125, 0, 0, 4686, 1586,0 }, - {1126,1126, 0, 0, 3786, 893,0 }, - {1127,1127, 0, 0, 40000, 126,0 }, - {1128,1128, 0, 0, 40000, 120,0 }, - {1129,1130, 0, 1, 40000, 146,0.15625 }, - {1131,1131, 0, 0, 40000, 433,0 }, - {1132,1132, 0, 0, 40000, 133,0 }, - {1133,1134, 0, 1, 40000, 126,-0.046875 }, - {1135,1135, 0, 0, 40000, 113,0 }, - {1136,1137, 0, 1, 40000, 253,2.5e-05 }, - {1138,1138, 0, 0, 18440, 240,0 }, - {1139,1139, 0, 0, 5213, 886,0 }, - {1140,1140, 0, 0, 1446, 113,0 }, - {1141,1141, 0, 0, 5233, 106,0 }, - {1142,1142, 0, 0, 5286, 266,0 }, - {1143,1143, 0, 0, 40000, 66,0 }, - {1144,1144, 0, 0, 40000, 66,0 }, - {1145,1145, 0, 0, 10593, 106,0 }, - {1146,1146, 0, 0, 2733, 160,0 }, - {1147,1147, 0, 0, 10313, 93,0 }, - {1148,1148, 0, 0, 40000, 0,0 }, - {1149,1150, 0, 1, 40000, 0,-0.03125 }, - {1151,1151, 0, 0, 40000, 53,0 }, - {1152,1152, 0, 0, 10560, 246,0 }, - {1153,1153, 0, 0, 2700, 153,0 }, - {1154,1154, 0, 1, 40000, 100,-0.15625 }, - {1155,1155, 0, 0, 40000, 73,0 }, - {1156,1156, 0, 0, 40000, 220,0 }, - {1157,1157, 0, 0, 40000, 140,0 }, - {1158,1158, 0, 0, 40000, 380,0 }, - {1159,1160, 0, 1, 40000, 400,0.171875 }, - {1161,1161, 0, 0, 40000, 0,0 }, - {1162,1162, 0, 0, 40000, 0,0 }, - {1163,1163, 0, 0, 4733, 906,0 }, - {1164,1165, 0, 1, 40000, 393,-0.125 }, - {1166,1167, 0, 1, 40000, 366,0.078125 }, - {1168,1168, 0, 1, 40000, 2453,-0.078125 }, - {1169,1170, 0, 1, 40000, 546,0.0625 }, - {1171,1172, 0, 1, 40000, 786,0.15625 }, - {1173,1173, 0, 0, 40000, 0,0 }, - {1174,1174, 0, 0, 40000, 513,0 }, - {1175,1176, 0, 1, 2300, 533,0 }, - {1177,1177, 0, 0, 40000, 80,0 }, - {1178,1178, 0, 0, 40000, 60,0 }, - {1179,1179, 0, 0, 40000, 0,0 }, - {1180,1180, 0, 0, 10653, 86,0 }, - {1181,1182, 0, 1, 40000, 0,2.5e-05 }, - {1183,1184, 0, 1, 40000, 86,0.046875 }, - {1185,1186, 0, 1, 40000, 0,0.09375 }, - {1187,1188, 0, 1, 40000, 0,0.09375 }, - {1189,1189, 0, 0, 40000, 133,0 }, - {1190,1190, 0, 0, 40000, 140,0 }, - {1191,1191, 0, 0, 40000, 73,0 }, - {1192,1192, 0, 0, 40000, 60,0 }, - {1193,1193, 0, 0, 40000, 106,0 }, - {1194,1194, 0, 0, 40000, 93,0 }, - {1195,1195, 0, 0, 40000, 66,0 }, - {1196,1196, 0, 0, 40000, 93,0 }, - {1197,1197, 0, 0, 40000, 60,0 }, - {1198,1198, 0, 0, 40000, 66,0 }, - {1199,1199, 0, 0, 40000, 120,0 }, - {1200,1200, 0, 0, 40000, 100,0 }, - {1201,1201, 0, 0, 40000, 86,0 }, - {1202,1202, 0, 0, 40000, 0,0 }, - {1203,1203, 0, 0, 40000, 233,0 }, - {1204,1204, 0, 0, 40000, 100,0 }, - {1205,1206, 0, 1, 40000, 266,0.03125 }, - {1207,1208, 0, 1, 40000, 260,-2.5e-05 }, - {1209,1209, 0, 0, 40000, 146,0 }, - {1210,1211, 0, 1, 40000, 60,0.03125 }, - {1212,1212, 0, 0, 40000, 53,0 }, - {1213,1214, 0, 1, 40000, 706,-0.09375 }, - {1215,1216, 0, 1, 40000, 660,-0.046875 }, - {1217,1217, 0, 0, 40000, 133,0 }, - {1218,1219, 0, 1, 40000, 426,0.03125 }, - {1220,1220, 0, 1, 40000, 0,0.03125 }, - {1221,1222, 0, 1, 40000, 260,0.171875 }, - {1223,1223, 0, 0, 40000, 0,0 }, - {1224,1224, 0, 0, 6100, 1580,0 }, - {1225,1150, 0, 1, 40000, 73,-0.03125 }, - {1226,1226, 0, 0, 40000, 1580,0 }, - {1227,1227, 0, 0, 40000, 40,0 }, - {1228,1229, 0, 1, 40000, 113,0.125 }, - {1230,1230, 0, 0, 2666, 846,0 }, - {1231,1232, 0, 1, 40000, 0,-0.03125 }, - {1233,1234, 0, 1, 9233, 2413,-0.1875 }, - {1235,1235, 0, 0, 40000, 1020,0 }, - {1236,1236, 0, 0, 40000, 0,0 }, - {1237,1237, 0, 0, 9633, 3073,0 }, - {1238,1238, 0, 0, 40000, 0,0 }, - {1239,1239, 0, 0, 2446, 386,0 }, - {1240,1241, 0, 1, 3113, 1133,0 }, - {1242,1242, 0, 0, 18473, 813,0 }, - {1243,1243, 0, 0, 1206, 660,0 }, - {1244,1244, 0, 0, 40000, 153,0 }, - {1245,1245, 0, 0, 40000, 160,0 }, - {1246,1246, 0, 0, 40000, 133,0 }, - {1247,1247, 0, 0, 8660, 2386,0 }, - {1248,1248, 0, 0, 293, 106,0 }, - {1249,1249, 0, 0, 40000, 433,0 }, - {1250,1250, 0, 0, 426, 80,0 }, - {1251,1251, 0, 0, 973, 360,0 }, - {1252,1252, 0, 0, 573, 153,0 }, - {1253,1253, 0, 0, 3746, 126,0 }, - {1254,1254, 0, 0, 2313, 73,0 }, - {1255,1255, 0, 0, 1473, 106,0 }, - {1256,1256, 0, 0, 1500, 320,0 }, - {1257,1257, 0, 0, 5280, 1593,0 }, - {1258,1258, 0, 0, 40000, 60,0 }, - {1259,1259, 0, 0, 40000, 146,0 }, - {1260,1260, 29, 0, 40000, 300,0 }, - {1261,1261, 65, 0, 40000, 2040,0 }, - {1262,1262, 0, 0, 626, 240,0 }, - {1263,1263, 25, 0, 626, 226,0 }, - {1264,1264, 83, 0, 180, 80,0 }, - {1265,1265, 32, 0, 260, 140,0 }, - {1266,1266, 60, 0, 40000, 0,0 }, - {1267,1267, 36, 0, 286, 40,0 }, - {1268,1268, 27, 0, 573, 80,0 }, - {1269,1269, 31, 0, 693, 106,0 }, - {1270,1270, 21, 0, 500, 146,0 }, - {1270,1270, 26, 0, 493, 140,0 }, - {1270,1270, 28, 0, 500, 146,0 }, - {1271,1271, 60, 0, 2420, 1080,0 }, - {1270,1270, 32, 0, 413, 126,0 }, - {1272,1272, 60, 0, 806, 300,0 }, - {1273,1273, 96, 0, 1146, 493,0 }, - {1274,1274, 72, 0, 1246, 586,0 }, - {1275,1275, 79, 0, 286, 106,0 }, - {1276,1276, 69, 0, 1193, 1046,0 }, - {1277,1277, 71, 0, 340, 93,0 }, - {1278,1278, 22, 0, 1880, 653,0 }, - {1279,1279, 55, 0, 246, 120,0 }, - {1279,1279, 48, 0, 286, 133,0 }, - {1280,1280, 0, 0, 40, 0,0 }, - {1281,1281, 49, 2, 40, 0,0 }, - {1282,1282, 73, 0, 166, 33,0 }, - {1282,1282, 68, 0, 166, 33,0 }, - {1282,1282, 61, 0, 200, 40,0 }, - {1283,1283, 0, 0, 40, 0,0 }, - {1284,1284, 0, 0, 40000, 100,0 }, - {1285,1285, 0, 0, 40000, 60,0 }, - {1286,1286, 0, 0, 40000, 0,0 }, - {1287,1287, 0, 0, 10460, 153,0 }, - {1288,1289, 0, 1, 40000, 0,0 }, - {1290,1290, 0, 0, 40000, 0,0 }, - {1291,1292, 36, 1, 353, 153,0 }, - {1293,1293, 69, 0, 1206, 1060,0 }, - {1294,1294, 0, 0, 40000, 0,0 }, - {1295,1295, 0, 0, 40000, 73,0 }, - {1296,1296, 0, 0, 40000, 0,0 }, - {1297,1297, 22, 0, 1880, 653,0 }, - {1298,1298, 0, 0, 40000, 73,0 }, - {1299,1299, 0, 0, 3913, 420,0 }, - {1300,1300, 0, 0, 9233, 240,0 }, - {1301,1301, 0, 0, 4660, 1573,0 }, - {1302,1302, 0, 0, 1166, 400,0 }, - {1303,1303, 0, 0, 40000, 126,0 }, - {1304,1304, 0, 0, 40000, 93,0 }, - {1305,1305, 0, 0, 40000, 93,0 }, - {1306,1306, 0, 0, 40000, 553,0 }, - {1307,1307, 0, 0, 40000, 660,0 }, - {1308,1308, 0, 0, 40000, 73,0 }, - {1309,1309, 0, 0, 40000, 146,0 }, - {1310,1310, 0, 0, 40000, 146,0 }, - {1311,1311, 0, 0, 4026, 100,0 }, - {1312,1312, 0, 0, 18226, 100,0 }, - {1313,1313, 0, 0, 40000, 0,0 }, - {1314,1314, 0, 0, 40000, 73,0 }, - {1315,1315, 0, 0, 40000, 140,0 }, - {1316,1316, 0, 0, 40000, 393,0 }, - {1317,1317, 0, 0, 40000, 406,0 }, - {1318,1318, 0, 0, 40000, 373,0 }, - {1319,1319, 0, 0, 40000, 0,0 }, - {1320,1320, 0, 0, 40000, 360,0 }, - {1321,1321, 0, 0, 1060, 380,0 }, - {1322,1322, 0, 0, 40000, 66,0 }, - {1323,1323, 0, 0, 40000, 66,0 }, - {1324,1324, 0, 0, 40000, 86,0 }, - {1325,1325, 0, 0, 40000, 73,0 }, - { 260, 260, 0, 0, 40000, 80,0 }, - {1326,1326, 0, 0, 40000, 80,0 }, - {1327,1327, 0, 0, 40000, 73,0 }, - {1328,1328, 0, 0, 40000, 73,0 }, - {1329,1329, 0, 0, 40000, 153,0 }, - {1330,1330, 0, 0, 40000, 153,0 }, - {1331,1331, 0, 0, 40000, 146,0 }, - {1332,1332, 0, 0, 40000, 146,0 }, - {1333,1333, 0, 0, 40000, 73,0 }, - {1334,1334, 0, 0, 40000, 153,0 }, - {1335,1335, 0, 0, 40000, 233,0 }, - {1336,1336, 0, 0, 40000, 400,0 }, - {1337,1337, 0, 0, 40000, 1373,0 }, - {1338,1338, 0, 0, 40000, 193,0 }, - {1339,1339, 0, 0, 40000, 1273,0 }, - {1340,1340, 0, 0, 40000, 186,0 }, - {1341,1341, 0, 0, 40000, 86,0 }, - {1342,1342, 0, 0, 7440, 2473,0 }, - {1343,1343, 0, 0, 40000, 160,0 }, - {1344,1344, 0, 0, 8966, 406,0 }, - {1345,1345, 0, 0, 40000, 1353,0 }, - {1346,1346, 0, 0, 14180, 4406,0 }, - { 378, 378, 84, 0, 1333, 460,0 }, - {1347,1347, 24, 0, 1893, 633,0 }, - {1348,1348, 44, 0, 206, 86,0 }, - {1349,1349, 40, 0, 586, 140,0 }, - {1350,1350, 60, 0, 1026, 320,0 }, - {1351,1351, 0, 0, 6560, 33,0 }, - {1352,1352, 0, 0, 7373, 2453,0 }, - {1353,1353, 0, 0, 4660, 1573,0 }, - {1354,1354, 0, 0, 40000, 346,0 }, - {1355,1355, 0, 0, 7126, 86,0 }, - {1356,1356, 0, 0, 40000, 213,0 }, - {1357,1357, 0, 0, 1180, 340,0 }, - {1358,1358, 0, 0, 3893, 1466,0 }, - {1359,1359, 0, 0, 2053, 1173,0 }, - {1360,1360, 0, 0, 40000, 200,0 }, - {1361,1361, 0, 0, 40000, 353,0 }, - {1362,1362, 0, 0, 40000, 273,0 }, - {1363,1363, 0, 0, 40000, 433,0 }, - {1364,1364, 0, 0, 1940, 426,0 }, - {1365,1365, 0, 0, 40000, 80,0 }, - {1366,1366, 0, 0, 40000, 106,0 }, - {1367,1367, 0, 0, 40000, 60,0 }, - {1368,1368, 0, 0, 40000, 140,0 }, - {1369,1369, 0, 0, 40000, 93,0 }, - {1370,1370, 0, 0, 40000, 73,0 }, - {1371,1371, 0, 0, 40000, 73,0 }, - {1372,1372, 0, 0, 40000, 93,0 }, - {1373,1373, 0, 0, 40000, 73,0 }, - {1374,1374, 0, 0, 40000, 80,0 }, - {1375,1375, 0, 0, 40000, 746,0 }, - {1376,1376, 0, 0, 2360, 813,0 }, - {1377,1377, 0, 0, 340, 146,0 }, - {1378,1378, 35, 0, 713, 273,0 }, - {1379,1379, 49, 0, 173, 93,0 }, - {1377,1377, 48, 0, 286, 126,0 }, - {1380,1380, 58, 0, 173, 100,0 }, - {1377,1377, 60, 0, 286, 133,0 }, - {1381,1381, 47, 0, 973, 360,0 }, - {1382,1382, 60, 0, 146, 86,0 }, - {1381,1381, 49, 0, 966, 333,0 }, - {1383,1383, 72, 0, 506, 206,0 }, - {1381,1381, 51, 0, 953, 340,0 }, - {1384,1384, 84, 0, 1340, 480,0 }, - {1381,1381, 54, 0, 986, 360,0 }, - {1381,1381, 57, 0, 980, 346,0 }, - {1385,1385, 72, 0, 1573, 440,0 }, - {1381,1381, 60, 0, 953, 340,0 }, - {1386,1386, 36, 0, 2673, 900,0 }, - {1387,1387, 93, 0, 233, 106,0 }, - {1388,1388, 72, 0, 966, 353,0 }, - {1389,1389, 84, 0, 1366, 473,0 }, - {1390,1390, 36, 0, 1326, 446,0 }, - {1391,1391, 64, 0, 220, 86,0 }, - {1392,1392, 68, 0, 126, 220,0 }, - {1393,1393, 0, 0, 4513, 640,0 }, - {1394,1394, 0, 0, 40000, 353,0 }, - {1395,1395, 0, 0, 40000, 73,0 }, - {1396,1396, 0, 0, 2040, 380,0 }, - {1397,1397, 0, 0, 40000, 240,0 }, - {1398,1398, 0, 0, 3246, 753,0 }, - {1399,1399, 0, 0, 40000, 66,0 }, - {1400,1400, 0, 0, 40000, 0,0 }, - {1401,1401, 0, 0, 40000, 0,0 }, - {1402,1402, 0, 0, 7720, 1260,0 }, - {1403,1403, 0, 0, 213, 6420,0 }, - {1404,1404, 0, 0, 40000, 66,0 }, - {1405,1405, 0, 0, 40000, 73,0 }, - {1406,1406, 0, 0, 40000, 93,0 }, - {1407,1407, 0, 0, 1606, 640,0 }, - {1408,1408, 0, 0, 15486, 1580,0 }, - {1409,1409, 0, 0, 40000, 353,0 }, - {1410,1410, 0, 0, 40000, 2066,0 }, - {1411,1411, 0, 0, 40000, 0,0 }, - {1412,1412, 0, 0, 15453, 73,0 }, - {1413,1413, 0, 0, 3726, 1240,0 }, - {1414,1414, 0, 0, 40000, 86,0 }, - {1415,1415, 0, 0, 40000, 200,0 }, - {1416,1416, 0, 0, 40000, 53,0 }, - {1417,1417, 0, 0, 40000, 73,0 }, - {1418,1418, 0, 0, 40000, 66,0 }, - {1419,1419, 0, 0, 40000, 26,0 }, - {1420,1420, 0, 0, 40000, 53,0 }, - {1421,1421, 0, 0, 40000, 40,0 }, - {1422,1422, 0, 0, 40000, 126,0 }, - {1423,1423, 0, 0, 40000, 0,0 }, - {1424,1424, 0, 0, 13653, 0,0 }, - {1425,1425, 0, 0, 12533, 1953,0 }, - {1426,1426, 0, 0, 973, 1280,0 }, - {1427,1427, 0, 0, 40000, 426,0 }, - {1428,1428, 0, 0, 40000, 53,0 }, - {1429,1429, 0, 0, 40000, 66,0 }, - {1430,1430, 0, 0, 526, 840,0 }, - {1431,1431, 0, 0, 286, 1293,0 }, - {1432,1432, 0, 0, 14726, 4920,0 }, - {1433,1433, 0, 0, 5233, 633,0 }, - {1434,1434, 0, 0, 13226, 2500,0 }, - { 740, 740, 0, 0, 513, 200,0 }, - {1435,1435, 0, 0, 40000, 5666,0 }, - { 739, 739, 48, 0, 213, 20,0 }, - { 500, 500, 55, 0, 100, 0,0 }, - { 740, 740, 60, 0, 226, 113,0 }, - { 500, 500, 41, 0, 106, 0,0 }, - {1436,1436, 84, 0, 160, 26,0 }, - {1437,1437, 84, 0, 386, 493,0 }, - { 500, 500, 48, 0, 100, 0,0 }, - {1438,1438, 15, 0, 340, 140,0 }, - { 752, 752, 49, 0, 173, 20,0 }, - {1438,1438, 16, 0, 346, 146,0 }, - {1438,1438, 12, 0, 340, 140,0 }, - { 740, 740, 55, 0, 220, 113,0 }, - { 752, 752, 18, 0, 206, 20,0 }, - { 752, 752, 15, 0, 200, 20,0 }, - { 752, 752, 17, 0, 206, 20,0 }, - {1439,1440, 0, 4, 40000, 0,0 }, - {1441,1442, 0, 4, 7360, 200,0 }, - {1443,1444, 0, 4, 11840, 320,0 }, - {1445,1446, 0, 4, 9920, 326,0 }, - {1447,1448, 0, 4, 10213, 0,0 }, - {1449,1450, 0, 4, 7440, 2486,0 }, - { 181,1451, 0, 4, 2360, 733,0 }, - {1452,1453, 0, 4, 9260, 240,0 }, - {1454,1455, 0, 4, 40000, 0,0 }, - {1456,1457, 0, 4, 660, 126,0 }, - {1458,1459, 0, 4, 40000, 66,0 }, - { 190,1460, 0, 4, 40000, 60,0 }, - { 192,1461, 0, 4, 40000, 73,0 }, - {1462,1463, 0, 4, 40000, 353,0 }, - {1464,1465, 0, 4, 40000, 353,0 }, - {1466,1467, 0, 4, 40000, 66,0 }, - {1468,1469, 0, 4, 40000, 46,0 }, - { 35,1470, 0, 4, 40000, 46,0 }, - { 36,1471, 0, 4, 320, 0,0 }, - {1472,1473, 0, 4, 320, 0,0 }, - {1474,1475, 0, 4, 7986, 93,0 }, - { 39,1476, 0, 4, 1053, 226,0 }, - {1477,1476, 0, 4, 1060, 226,0 }, - {1478,1479, 0, 4, 40000, 453,0 }, - { 50,1480, 0, 4, 40000, 400,0 }, - {1481,1482, 0, 4, 40000, 133,0 }, - {1483,1484, 0, 4, 40000, 0,0 }, - {1485,1486, 0, 4, 40000, 226,0 }, - { 55,1487, 0, 4, 40000, 100,0 }, - {1488,1489, 0, 4, 40000, 93,0 }, - {1490,1491, 0, 4, 40000, 73,0 }, - {1492,1493, 0, 4, 40000, 73,0 }, - {1494,1495, 0, 4, 40000, 73,0 }, - {1496,1497, 0, 4, 40000, 80,0 }, - {1496,1498, 0, 4, 40000, 73,0 }, - {1499,1500, 0, 4, 40000, 66,0 }, - {1501,1502, 0, 4, 40000, 146,0 }, - {1503,1504, 0, 4, 40000, 93,0 }, - {1505,1506, 0, 4, 40000, 73,0 }, - { 86,1507, 0, 4, 40000, 80,0 }, - {1508,1509, 0, 4, 40000, 0,0 }, - {1510,1511, 0, 4, 40000, 60,0 }, - {1512,1513, 0, 4, 40000, 0,0 }, - {1514,1515, 0, 4, 40000, 0,0 }, - {1516,1517, 0, 4, 40000, 773,0 }, - {1518,1519, 0, 4, 5346, 2973,0 }, - {1520,1521, 0, 4, 40000, 406,0 }, - {1522,1523, 0, 4, 9080, 360,0 }, - {1524,1525, 0, 4, 40000, 1200,0 }, - {1526,1527, 0, 4, 40000, 800,0 }, - {1528,1529, 0, 4, 40000, 960,0 }, - { 111,1530, 0, 4, 1200, 433,0 }, - {1531,1532, 0, 4, 226, 386,0 }, - { 115,1533, 0, 4, 2433, 0,0 }, - {1534,1535, 0, 4, 1873, 646,0 }, - {1536,1537, 0, 4, 3013, 53,0 }, - {1538,1539, 0, 4, 1560, 720,0 }, - {1540, 339, 0, 6, 6, 0,0 }, - {1541, 339, 0, 6, 6, 0,0 }, - {1542,1543, 0, 4, 993, 93,0 }, - {1544,1545, 0, 4, 293, 86,0 }, - {1546,1547, 0, 4, 40000, 153,0 }, - { 364, 365, 44, 4, 120, 0,0 }, - { 129,1548, 48, 4, 173, 0,0 }, - { 367, 368, 58, 4, 173, 0,0 }, - { 129,1549, 60, 4, 173, 0,0 }, - {1550,1551, 48, 4, 520, 200,0 }, - { 132,1552, 43, 4, 173, 0,0 }, - {1550,1551, 49, 4, 520, 200,0 }, - {1553,1554, 43, 4, 160, 80,0 }, - {1550,1551, 51, 4, 513, 0,0 }, - { 134,1555, 43, 4, 1733, 0,0 }, - {1550,1551, 54, 4, 506, 0,0 }, - {1550,1551, 57, 4, 506, 0,0 }, - { 380, 381, 72, 4, 1580, 0,0 }, - {1550,1551, 60, 4, 520, 0,0 }, - {1556,1557, 70, 4, 826, 306,0 }, - { 374, 375, 60, 4, 973, 0,0 }, - {1558,1559, 36, 4, 1233, 0,0 }, - {1560,1561, 65, 4, 293, 133,0 }, - {1562,1563, 84, 4, 1360, 0,0 }, - {1564,1565, 59, 4, 380, 0,0 }, - {1566,1567, 84, 4, 1593, 566,0 }, - {1568,1569, 35, 4, 1353, 473,0 }, - {1570,1571, 44, 4, 413, 0,0 }, - {1572,1573, 67, 4, 246, 0,0 }, - {1574,1575, 66, 4, 293, 0,0 }, - { 145,1576, 59, 4, 146, 0,0 }, - {1577,1578, 51, 4, 360, 0,0 }, - {1579,1580, 45, 4, 246, 0,0 }, - {1581,1582, 71, 4, 433, 0,0 }, - { 149,1583, 60, 4, 280, 0,0 }, - {1584,1585, 58, 4, 173, 0,0 }, - {1586,1587, 53, 4, 173, 0,0 }, - { 397,1588, 64, 4, 220, 80,0 }, - {1589,1590, 71, 4, 106, 53,0 }, - {1591,1592, 61, 4, 1000, 340,0 }, - {1593,1594, 61, 4, 1000, 340,0 }, - { 391, 392, 48, 4, 160, 46,0 }, - { 391, 393, 48, 4, 380, 60,0 }, - {1595,1596, 69, 4, 120, 0,0 }, - { 159,1597, 68, 4, 120, 0,0 }, - { 159,1597, 63, 4, 140, 0,0 }, - {1598,1599, 74, 4, 893, 273,0 }, - {1600,1601, 60, 4, 1013, 306,0 }, - {1602,1603, 80, 4, 220, 0,0 }, - {1604,1605, 64, 4, 1366, 0,0 }, - {1606,1607, 69, 4, 120, 73,0 }, - { 398, 399, 55, 4, 1540, 193,0 }, - {1608,1609, 75, 4, 1573, 0,0 }, - {1610,1611, 68, 4, 120, 0,0 }, - {1612,1613, 48, 4, 360, 0,0 }, - {1614,1615, 53, 4, 606, 0,0 }, - {1616,1616, 0, 0, 40000, 1586,0 }, - {1617,1617, 0, 0, 40000, 1226,0 }, - {1618,1618, 0, 0, 4546, 766,0 }, - {1619,1619, 0, 0, 40000, 420,0 }, - {1620,1620, 0, 0, 40000, 1573,0 }, - {1621,1621, 0, 0, 3326, 806,0 }, - {1622,1622, 0, 0, 40000, 746,0 }, - {1623,1623, 0, 0, 40000, 900,0 }, - {1624,1624, 0, 0, 12166, 1573,0 }, - {1625,1625, 0, 0, 40000, 80,0 }, - {1626,1626, 0, 0, 40000, 80,0 }, - {1627,1627, 0, 0, 40000, 80,0 }, - {1628,1628, 0, 0, 40000, 2713,0 }, - {1629,1629, 0, 0, 40000, 86,0 }, - {1630,1630, 0, 0, 40000, 80,0 }, - {1631,1631, 0, 0, 40000, 80,0 }, - {1632,1632, 0, 0, 40000, 813,0 }, - {1633,1633, 0, 0, 40000, 80,0 }, - {1634,1634, 0, 0, 40000, 80,0 }, - {1635,1635, 0, 0, 40000, 80,0 }, - {1636,1636, 0, 0, 40000, 193,0 }, - {1637,1637, 0, 0, 2920, 733,0 }, - {1638,1638, 0, 0, 40000, 373,0 }, - {1639,1639, 0, 0, 2286, 226,0 }, - {1640,1640, 0, 0, 40000, 226,0 }, - {1641,1641, 0, 0, 40000, 226,0 }, - {1642,1642, 0, 0, 40000, 433,0 }, - {1643,1643, 0, 0, 40000, 813,0 }, - {1644,1644, 0, 0, 40000, 80,0 }, - {1645,1645, 0, 0, 40000, 80,0 }, - {1646,1646, 0, 0, 40000, 80,0 }, - {1647,1647, 0, 0, 40000, 80,0 }, - {1648,1648, 0, 0, 40000, 80,0 }, - {1649,1649, 0, 0, 40000, 80,0 }, - {1650,1650, 0, 0, 40000, 146,0 }, - {1651,1651, 0, 0, 40000, 1280,0 }, - {1652,1652, 0, 0, 40000, 513,0 }, - {1653,1653, 0, 0, 40000, 313,0 }, - {1654,1654, 0, 0, 40000, 773,0 }, - {1655,1655, 0, 0, 7400, 2480,0 }, - {1656,1656, 0, 0, 3760, 1253,0 }, - {1657,1657, 0, 0, 40000, 380,0 }, - {1658,1658, 0, 0, 40000, 333,0 }, - {1659,1659, 0, 0, 40000, 2926,0 }, - {1660,1660, 0, 0, 40000, 5666,0 }, - {1661,1661, 0, 0, 40000, 1613,0 }, - {1662,1662, 0, 0, 3746, 1273,0 }, - {1663,1663, 0, 0, 13653, 0,0 }, - {1664,1664, 0, 0, 4640, 1553,0 }, - {1665,1665, 0, 0, 40000, 680,0 }, - {1666,1666, 0, 0, 6393, 426,0 }, - {1667,1667, 0, 0, 40000, 713,0 }, - {1668,1668, 12, 0, 166, 20,0 }, - {1669,1669, 48, 0, 460, 193,0 }, - { 736, 736, 52, 0, 286, 20,0 }, - {1670,1670, 48, 0, 506, 200,0 }, - {1670,1670, 36, 0, 713, 260,0 }, - { 377, 377, 84, 0, 386, 493,0 }, - { 730, 730, 95, 0, 1886, 653,0 }, - {1669,1669, 84, 0, 386, 166,0 }, - { 755, 755, 20, 0, 633, 240,0 }, - { 755, 755, 22, 0, 626, 240,0 }, - { 755, 755, 24, 0, 633, 246,0 }, - {1671,1671, 0, 0, 2233, 220,0 }, - {1672,1672, 0, 0, 2233, 240,0 }, - {1673,1673, 0, 0, 2233, 206,0 }, - {1674,1674, 0, 0, 2126, 173,0 }, - {1675,1675, 0, 0, 7473, 73,0 }, - {1676,1676, 0, 0, 40000, 0,0 }, - {1677,1677, 0, 0, 3493, 193,0 }, - {1678,1678, 0, 0, 1746, 73,0 }, - {1679,1679, 0, 0, 1013, 400,0 }, - {1680,1680, 0, 0, 3473, 1560,0 }, - {1681,1681, 0, 0, 1073, 40,0 }, - {1682,1682, 0, 0, 40000, 380,0 }, - {1683,1683, 0, 0, 1166, 400,0 }, - {1684,1684, 0, 0, 606, 146,0 }, - {1685,1685, 0, 0, 4553, 1486,0 }, - {1686,1686, 0, 0, 1126, 80,0 }, - {1687,1687, 0, 0, 40000, 73,0 }, - {1688,1688, 0, 0, 40000, 60,0 }, - {1689,1689, 0, 0, 40000, 66,0 }, - {1690,1690, 0, 0, 40000, 73,0 }, - {1691,1691, 0, 0, 40000, 73,0 }, - {1692,1692, 0, 0, 40000, 73,0 }, - {1693,1693, 0, 0, 40000, 73,0 }, - {1694,1694, 0, 0, 6380, 53,0 }, - {1695,1695, 0, 0, 6380, 60,0 }, - {1696,1696, 0, 0, 40000, 53,0 }, - {1697,1697, 0, 0, 40000, 0,0 }, - {1698,1698, 0, 0, 1880, 80,0 }, - {1699,1699, 0, 0, 40000, 60,0 }, - {1700,1700, 0, 0, 40000, 60,0 }, - {1701,1701, 0, 0, 1460, 80,0 }, - {1702,1702, 0, 0, 40000, 73,0 }, - {1703,1703, 0, 0, 40000, 0,0 }, - {1704,1704, 0, 0, 40000, 146,0 }, - {1705,1705, 0, 0, 40000, 66,0 }, - {1706,1706, 0, 0, 40000, 73,0 }, - {1707,1707, 0, 0, 40000, 160,0 }, - {1708,1708, 0, 0, 40000, 73,0 }, - {1709,1709, 0, 0, 40000, 193,0 }, - {1710,1710, 0, 0, 3740, 1260,0 }, - {1711,1711, 0, 0, 40000, 180,0 }, - {1712,1712, 0, 0, 40000, 173,0 }, - {1713,1713, 0, 0, 40000, 113,0 }, - {1714,1714, 0, 0, 40000, 86,0 }, - {1715,1715, 0, 0, 1853, 633,0 }, - {1716,1716, 0, 0, 40000, 0,0 }, - {1717,1717, 0, 0, 1066, 306,0 }, - {1718,1718, 0, 0, 40000, 86,0 }, - {1719,1719, 0, 0, 40000, 586,0 }, - {1720,1720, 0, 0, 40000, 86,0 }, - {1721,1721, 0, 0, 40000, 93,0 }, - {1722,1722, 0, 0, 40000, 373,0 }, - {1723,1723, 0, 0, 40000, 113,0 }, - {1724,1724, 0, 0, 40000, 353,0 }, - {1725,1725, 0, 0, 420, 73,0 }, - {1726,1726, 0, 0, 40000, 66,0 }, - {1727,1727, 0, 0, 40000, 53,0 }, - {1728,1728, 0, 0, 40000, 66,0 }, - {1729,1729, 0, 0, 40000, 100,0 }, - {1730,1730, 0, 0, 40000, 93,0 }, - {1731,1731, 0, 0, 40000, 0,0 }, - {1732,1732, 0, 0, 40000, 73,0 }, - {1733,1733, 0, 0, 40000, 80,0 }, - {1734,1734, 0, 0, 40000, 80,0 }, - {1735,1735, 0, 0, 40000, 80,0 }, - {1736,1736, 0, 0, 40000, 80,0 }, - {1737,1737, 0, 0, 40000, 80,0 }, - {1738,1738, 0, 0, 40000, 73,0 }, - {1739,1739, 0, 0, 40000, 73,0 }, - {1740,1740, 0, 0, 40000, 106,0 }, - {1741,1741, 0, 0, 40000, 73,0 }, - {1742,1742, 0, 0, 40000, 73,0 }, - {1743,1743, 0, 0, 40000, 80,0 }, - {1744,1744, 0, 0, 40000, 0,0 }, - {1745,1745, 0, 0, 40000, 80,0 }, - {1746,1746, 0, 0, 40000, 66,0 }, - {1747,1747, 0, 0, 40000, 73,0 }, - {1748,1748, 0, 0, 40000, 0,0 }, - {1749,1749, 0, 0, 40000, 80,0 }, - {1750,1750, 0, 0, 40000, 66,0 }, - {1751,1751, 0, 0, 40000, 73,0 }, - {1752,1752, 0, 0, 40000, 80,0 }, - {1753,1753, 0, 0, 40000, 33,0 }, - {1754,1754, 0, 0, 40000, 0,0 }, - {1755,1755, 0, 0, 40000, 266,0 }, - {1756,1756, 0, 0, 40000, 160,0 }, - {1757,1757, 0, 0, 40000, 93,0 }, - {1758,1758, 0, 0, 40000, 660,0 }, - {1759,1759, 0, 0, 40000, 1453,0 }, - {1760,1760, 0, 0, 40000, 660,0 }, - {1761,1761, 0, 0, 40000, 120,0 }, - {1762,1762, 0, 0, 40000, 140,0 }, - {1763,1763, 0, 0, 9820, 393,0 }, - {1764,1764, 0, 0, 40000, 73,0 }, - {1765,1765, 0, 0, 3620, 1166,0 }, - {1766,1766, 0, 0, 40000, 0,0 }, - {1767,1767, 0, 0, 40000, 0,0 }, - {1768,1768, 0, 0, 40000, 813,0 }, - {1769,1769, 0, 0, 40000, 0,0 }, - {1770,1770, 0, 0, 40000, 2386,0 }, - {1771,1771, 0, 0, 4380, 400,0 }, - {1772,1772, 0, 0, 853, 0,0 }, - {1773,1773, 0, 0, 3700, 93,0 }, - {1774,1774, 0, 0, 1580, 300,0 }, - {1775,1775, 0, 0, 453, 140,0 }, - {1776,1776, 0, 0, 40000, 66,0 }, - {1777,1777, 0, 0, 40000, 73,0 }, - {1778,1778, 0, 0, 40000, 206,0 }, - {1779,1779, 0, 0, 4646, 1560,0 }, - {1780,1780, 0, 0, 353, 146,0 }, - {1781,1781, 0, 0, 1300, 400,0 }, - {1782,1782, 0, 0, 4593, 1546,0 }, - {1783,1783, 0, 0, 613, 226,0 }, - {1784,1784, 0, 0, 626, 233,0 }, - {1785,1785, 0, 0, 3020, 66,0 }, - {1786,1786, 0, 0, 1093, 186,0 }, - {1787,1787, 0, 0, 6053, 1240,0 }, - {1788,1788, 0, 0, 633, 126,0 }, - {1789,1789, 0, 0, 40000, 66,0 }, - {1790,1790, 0, 0, 40000, 73,0 }, - {1791,1791, 0, 0, 40000, 1253,0 }, - {1792,1792, 0, 0, 626, 246,0 }, - {1793,1793, 48, 0, 293, 120,0 }, - {1794,1794, 48, 0, 100, 0,0 }, - {1795,1795, 60, 0, 240, 133,0 }, - {1796,1796, 60, 0, 160, 66,0 }, - {1797,1797, 70, 0, 140, 33,0 }, - {1798,1798, 51, 0, 526, 206,0 }, - {1799,1799, 60, 0, 173, 93,0 }, - {1798,1798, 54, 0, 520, 200,0 }, - {1800,1800, 60, 0, 153, 80,0 }, - {1798,1798, 56, 0, 520, 206,0 }, - {1801,1801, 60, 0, 673, 206,0 }, - {1798,1798, 61, 0, 506, 200,0 }, - {1798,1798, 63, 0, 513, 206,0 }, - {1802,1802, 48, 0, 673, 200,0 }, - {1798,1798, 68, 0, 440, 180,0 }, - {1803,1803, 60, 0, 1873, 653,0 }, - {1804,1804, 60, 0, 673, 200,0 }, - {1805,1805, 66, 0, 306, 120,0 }, - {1806,1806, 60, 0, 673, 200,0 }, - { 379, 379, 59, 0, 173, 93,0 }, - {1802,1802, 64, 0, 673, 206,0 }, - {1807,1807, 48, 0, 1006, 20,0 }, - {1808,1808, 56, 0, 120, 40,0 }, - {1809,1809, 53, 0, 286, 133,0 }, - {1810,1810, 65, 0, 106, 0,0 }, - {1811,1811, 49, 0, 293, 133,0 }, - {1811,1811, 43, 0, 293, 133,0 }, - { 386, 386, 65, 0, 1013, 673,0 }, - { 386, 386, 60, 0, 1000, 660,0 }, - {1812,1812, 70, 0, 260, 113,0 }, - {1812,1812, 65, 0, 306, 120,0 }, - {1813,1813, 60, 0, 246, 106,0 }, - {1814,1814, 60, 0, 193, 120,0 }, - {1815,1815, 56, 0, 206, 13,0 }, - {1816,1816, 53, 0, 433, 73,0 }, - {1817,1817, 60, 0, 220, 113,0 }, - {1818,1818, 48, 0, 300, 66,0 }, - {1819,1819, 69, 0, 126, 0,0 }, - { 328, 328, 67, 0, 140, 93,0 }, - { 328, 328, 62, 0, 153, 100,0 }, - {1820,1820, 65, 0, 433, 100,0 }, - {1821,1821, 60, 0, 426, 100,0 }, - {1822,1822, 63, 0, 113, 46,0 }, - {1823,1823, 63, 0, 1866, 653,0 }, - {1824,1824, 67, 0, 273, 60,0 }, - {1825,1825, 60, 0, 973, 360,0 }, - {1825,1825, 72, 0, 806, 273,0 }, - { 401, 401, 62, 0, 46, 0,0 }, - {1826,1826, 48, 0, 126, 66,0 }, - {1827,1827, 53, 0, 980, 353,0 }, - {1828,1828, 60, 0, 293, 133,0 }, - {1829,1829, 60, 0, 160, 20,0 }, - {1830,1830, 60, 0, 126, 86,0 }, - {1831,1831, 60, 0, 173, 93,0 }, - {1832,1832, 0, 0, 40000, 106,0 }, - {1833,1833, 0, 0, 3780, 73,0 }, - {1834,1834, 0, 0, 3820, 1666,0 }, - {1835,1835, 0, 0, 40000, 73,0 }, - {1836,1836, 0, 0, 40000, 333,0 }, - {1837,1837, 0, 0, 40000, 220,0 }, - {1838,1838, 0, 0, 40000, 0,0 }, - {1839,1839, 0, 0, 40000, 53,0 }, - {1840,1840, 0, 0, 40000, 60,0 }, - {1841,1841, 0, 0, 5913, 2306,0 }, - {1842,1842, 0, 0, 7713, 2466,0 }, - { 525, 525, 0, 0, 4660, 660,0 }, - {1843,1843, 0, 0, 40000, 313,0 }, - {1844,1844, 0, 0, 40000, 0,0 }, - {1845,1845, 0, 0, 40000, 0,0 }, - {1846,1846, 0, 0, 1246, 453,0 }, - {1847,1847, 0, 0, 9600, 1580,0 }, - {1848,1848, 0, 0, 40000, 106,0 }, - {1849,1849, 0, 0, 2040, 400,0 }, - {1850,1850, 0, 0, 40000, 73,0 }, - {1851,1851, 0, 0, 4220, 620,0 }, - {1852,1852, 0, 0, 40000, 0,0 }, - {1853,1853, 0, 0, 40000, 433,0 }, - {1854,1854, 0, 0, 40000, 66,0 }, - {1855,1855, 0, 0, 40000, 46,0 }, - {1856,1856, 0, 0, 40000, 240,0 }, - {1857,1857, 0, 0, 40000, 313,0 }, - {1858,1858, 0, 0, 40000, 26,0 }, - {1859,1859, 0, 0, 40000, 0,0 }, - {1860,1860, 0, 0, 40000, 73,0 }, - {1861,1861, 0, 0, 6940, 66,0 }, - {1862,1862, 0, 0, 40000, 0,0 }, - {1863,1863, 0, 0, 40000, 60,0 }, - {1864,1864, 0, 0, 8140, 1440,0 }, - {1865,1865, 0, 0, 40000, 0,0 }, - {1866,1866, 0, 0, 40000, 613,0 }, - {1867,1867, 0, 0, 40000, 0,0 }, - {1868,1868, 0, 0, 633, 233,0 }, - {1869,1869, 0, 0, 40000, 226,0 }, - {1870,1870, 0, 0, 2280, 746,0 }, - {1871,1871, 0, 0, 1940, 633,0 }, - {1872,1872, 0, 0, 4220, 620,0 }, - {1873,1873, 0, 0, 40000, 133,0 }, - {1874,1874, 41, 0, 380, 153,0 }, - {1875,1875, 70, 0, 106, 0,0 }, - {1876,1876, 60, 0, 380, 206,0 }, - {1877,1877, 80, 0, 100, 0,0 }, - {1878,1878, 84, 0, 120, 0,0 }, - {1879,1879, 72, 0, 500, 433,0 }, - {1880,1880, 84, 0, 860, 553,0 }, - { 128, 128, 70, 0, 106, 0,0 }, - { 132, 132, 60, 0, 146, 86,0 }, - {1881,1882, 0, 4, 40000, 260,0 }, - {1883,1883, 0, 0, 40000, 0,0 }, - {1884,1885, 0, 4, 40000, 73,0 }, - {1886,1887, 0, 4, 40000, 86,0 }, - {1888,1889, 0, 4, 40000, 73,0 }, - {1890,1890, 0, 0, 40000, 300,0 }, - {1891,1891, 0, 0, 40000, 693,0 }, - {1892,1892, 0, 0, 40000, 586,0 }, - {1893,1893, 0, 0, 40000, 286,0 }, - {1894,1894, 0, 0, 1620, 773,0 }, - {1895,1895, 0, 0, 40000, 0,0 }, - {1896,1896, 0, 0, 40000, 193,0 }, - {1897,1897, 0, 0, 1873, 820,0 }, - {1898,1898, 0, 0, 4520, 753,0 }, - {1899,1899, 0, 0, 40000, 0,0 }, - {1900,1900, 0, 0, 40000, 220,0 }, - {1901,1901, 0, 0, 40000, 133,0 }, - {1902,1902, 0, 0, 40000, 73,0 }, - {1903,1903, 0, 0, 40000, 0,0 }, - {1904,1904, 0, 0, 7326, 2420,0 }, - {1905,1905, 0, 0, 1186, 446,0 }, - {1906,1906, 0, 0, 40000, 553,0 }, - {1907,1907, 0, 0, 40000, 293,0 }, - {1908,1908, 0, 0, 40000, 586,0 }, - {1909,1909, 0, 0, 2326, 793,0 }, - { 501, 501, 0, 0, 480, 226,0 }, - {1910,1910, 0, 0, 40000, 93,0 }, - {1911,1911, 0, 0, 620, 226,0 }, - {1912,1912, 0, 0, 2373, 800,0 }, - {1913,1913, 0, 0, 40000, 4986,0 }, - {1914,1914, 0, 0, 626, 240,0 }, - { 511, 511, 0, 0, 2326, 800,0 }, - {1915,1915, 0, 0, 340, 146,0 }, - {1910,1910, 60, 0, 40000, 93,0 }, - { 511, 511, 72, 0, 1566, 546,0 }, - {1915,1915, 84, 0, 246, 120,0 }, - {1916,1916, 0, 0, 40000, 0,0 }, - {1917,1917, 0, 0, 2713, 666,0 }, - {1918,1918, 0, 0, 40000, 0,0 }, - {1919,1919, 0, 0, 40000, 46,0 }, - {1920,1920, 0, 0, 40000, 0,0 }, - {1921,1921, 0, 0, 40000, 53,0 }, - {1922,1922, 0, 0, 40000, 33,0 }, - {1923,1923, 0, 0, 2073, 193,0 }, - {1924,1924, 0, 0, 40000, 146,0 }, - {1925,1925, 0, 0, 40000, 100,0 }, - {1926,1926, 0, 0, 40000, 93,0 }, - {1927,1927, 0, 0, 40000, 73,0 }, - {1928,1928, 0, 0, 40000, 540,0 }, - {1929,1929, 0, 0, 40000, 520,0 }, - {1930,1930, 0, 0, 40000, 506,0 }, - {1931,1931, 0, 0, 7406, 200,0 }, - {1932,1932, 0, 0, 5906, 133,0 }, - {1933,1933, 0, 0, 7426, 240,0 }, - {1934,1934, 0, 0, 7426, 240,0 }, - {1935,1935, 0, 0, 40000, 66,0 }, - {1936,1936, 0, 0, 40000, 66,0 }, - {1937,1937, 0, 0, 40000, 53,0 }, - {1938,1938, 0, 0, 40000, 66,0 }, - {1939,1939, 0, 0, 40000, 66,0 }, - {1940,1940, 0, 0, 40000, 53,0 }, - {1941,1941, 0, 0, 40000, 2146,0 }, - {1942,1942, 0, 0, 40000, 1126,0 }, - {1943,1943, 0, 0, 40000, 1020,0 }, - {1944,1944, 0, 0, 40000, 433,0 }, - {1945,1945, 0, 0, 40000, 0,0 }, - {1946,1946, 0, 0, 40000, 140,0 }, - {1947,1947, 0, 0, 4660, 660,0 }, - {1948,1948, 0, 0, 40000, 66,0 }, - {1949,1949, 0, 0, 40000, 4193,0 }, - {1950,1950, 0, 0, 7713, 2466,0 }, - {1951,1951, 0, 0, 40000, 73,0 }, - {1952,1952, 0, 0, 8100, 2093,0 }, - {1953,1953, 0, 0, 40000, 86,0 }, - {1954,1954, 0, 0, 40000, 80,0 }, - {1955,1955, 0, 0, 4113, 1526,0 }, - {1956,1956, 0, 0, 40000, 66,0 }, - {1957,1957, 0, 0, 40000, 100,0 }, - {1958,1958, 0, 0, 40000, 213,0 }, - {1959,1959, 0, 0, 40000, 100,0 }, - {1960,1960, 0, 0, 1186, 100,0 }, - {1961,1961, 0, 0, 40000, 433,0 }, - {1962,1962, 0, 0, 40000, 146,0 }, - {1963,1963, 0, 0, 40000, 400,0 }, - {1964,1964, 0, 0, 40000, 66,0 }, - {1965,1965, 0, 0, 40000, 193,0 }, - {1966,1966, 0, 0, 1153, 100,0 }, - {1967,1967, 0, 0, 4800, 1400,0 }, - {1968,1968, 0, 0, 2906, 713,0 }, - {1969,1969, 0, 0, 40000, 73,0 }, - {1970,1970, 0, 0, 2280, 746,0 }, - {1971,1971, 0, 0, 40000, 66,0 }, - {1972,1972, 0, 0, 40000, 86,0 }, - {1973,1973, 0, 0, 40000, 86,0 }, - {1974,1974, 0, 0, 40000, 66,0 }, - {1975,1975, 0, 0, 40000, 66,0 }, - {1976,1976, 0, 0, 40000, 66,0 }, - {1977,1977, 0, 0, 40000, 46,0 }, - {1978,1978, 0, 0, 40000, 73,0 }, - {1979,1979, 0, 0, 40000, 73,0 }, - {1980,1980, 0, 0, 40000, 66,0 }, - {1981,1981, 0, 0, 40000, 66,0 }, - {1982,1982, 0, 0, 40000, 66,0 }, - {1983,1983, 0, 0, 40000, 73,0 }, - {1984,1984, 0, 0, 40000, 73,0 }, - {1985,1985, 0, 0, 40000, 253,0 }, - {1986,1986, 0, 0, 40000, 126,0 }, - {1987,1987, 0, 0, 40000, 126,0 }, - {1988,1988, 0, 0, 40000, 66,0 }, - {1989,1989, 0, 0, 40000, 66,0 }, - {1990,1990, 0, 0, 40000, 53,0 }, - {1991,1991, 0, 0, 40000, 140,0 }, - {1992,1992, 0, 0, 40000, 40,0 }, - {1993,1993, 0, 0, 40000, 73,0 }, - {1994,1994, 0, 0, 40000, 66,0 }, - {1995,1995, 0, 0, 40000, 73,0 }, - {1996,1996, 0, 0, 40000, 73,0 }, - {1997,1997, 0, 0, 40000, 73,0 }, - {1998,1998, 0, 0, 40000, 73,0 }, - {1999,1999, 0, 0, 40000, 66,0 }, - {2000,2000, 0, 0, 40000, 433,0 }, - {2001,2001, 0, 0, 40000, 433,0 }, - {2002,2002, 0, 0, 2440, 706,0 }, - {2003,2003, 0, 0, 13960, 4800,0 }, - {2004,2004, 0, 0, 7393, 2480,0 }, - {2005,2005, 0, 0, 7220, 2073,0 }, - {2006,2006, 0, 0, 633, 233,0 }, - {2007,2007, 0, 0, 2326, 780,0 }, - {2008,2008, 0, 0, 40000, 73,0 }, - {2009,2009, 0, 0, 40000, 106,0 }, - {2010,2010, 0, 0, 40000, 126,0 }, - {2011,2011, 0, 0, 40000, 386,0 }, - {2012,2012, 0, 0, 40000, 66,0 }, - {2013,2013, 0, 0, 6893, 1273,0 }, - {2014,2014, 0, 0, 2546, 633,0 }, - {2015,2015, 0, 0, 206, 106,0 }, - {2016,2016, 0, 0, 213, 113,0 }, - {2017,2017, 0, 0, 360, 140,0 }, - {2018,2018, 0, 0, 1013, 193,0 }, - {2019,2019, 0, 0, 266, 66,0 }, - {2020,2020, 0, 0, 1880, 660,0 }, - {2021,2021, 0, 0, 286, 206,0 }, - {2022,2022, 0, 0, 3706, 1353,0 }, - {2023,2023, 0, 0, 1106, 380,0 }, - {2024,2024, 0, 0, 13220, 2466,0 }, - {2025,2025, 0, 0, 333, 26,0 }, - {2026,2026, 0, 0, 7346, 2440,0 }, - {2027,2027, 0, 0, 1273, 453,0 }, - { 352, 352, 51, 2, 6, 0,0 }, - {2028,2028, 35, 0, 700, 253,0 }, - {2028,2028, 36, 0, 706, 266,0 }, - {2029,2029, 47, 0, 100, 0,0 }, - {2030,2030, 38, 0, 346, 140,0 }, - {2019,2019, 39, 0, 220, 106,0 }, - {2031,2031, 45, 0, 286, 133,0 }, - { 492, 492, 41, 0, 1040, 406,0 }, - {2032,2032, 42, 0, 220, 106,0 }, - {2033,2033, 44, 0, 500, 193,0 }, - { 492, 492, 48, 0, 833, 346,0 }, - {2034,2034, 46, 0, 1866, 646,0 }, - { 492, 492, 53, 0, 873, 386,0 }, - { 167, 167, 56, 0, 646, 353,0 }, - {2035,2035, 61, 0, 366, 146,0 }, - {2036,2036, 56, 0, 1346, 473,0 }, - {2037,2037, 60, 0, 213, 126,0 }, - { 144, 144, 59, 0, 213, 0,0 }, - {2038,2038, 59, 0, 106, 0,0 }, - { 169, 169, 51, 0, 380, 366,0 }, - { 169, 169, 45, 0, 380, 366,0 }, - {2039,2039, 72, 0, 246, 20,0 }, - {2040,2040, 60, 0, 280, 20,0 }, - {2041,2041, 58, 0, 373, 360,0 }, - {2042,2042, 53, 0, 380, 366,0 }, - {2043,2043, 73, 0, 120, 26,0 }, - { 158, 158, 75, 0, 126, 140,0 }, - {2044,2044, 0, 0, 6786, 1073,0 }, - {2045,2045, 0, 0, 2046, 473,0 }, - {2046,2046, 0, 0, 3746, 1273,0 }, - {2047,2047, 0, 0, 1200, 3086,0 }, - {2048,2048, 0, 0, 1200, 3080,0 }, - {2049,2049, 0, 0, 40000, 2453,0 }, - {2050,2050, 0, 0, 40000, 413,0 }, - {2051,2051, 0, 0, 980, 2553,0 }, - {2052,2052, 0, 0, 40000, 2420,0 }, - {2053,2053, 0, 0, 40000, 2506,0 }, - {2054,2054, 0, 0, 40000, 380,0 }, - {2055,2055, 0, 0, 40000, 660,0 }, - {2056,2056, 0, 0, 40000, 73,0 }, - {2057,2057, 0, 0, 40000, 333,0 }, - {2058,2058, 0, 0, 833, 146,0 }, - {2059,2059, 0, 0, 1686, 620,0 }, - {2060,2060, 0, 0, 40000, 73,0 }, - {2061,2061, 0, 0, 40000, 0,0 }, - {2062,2062, 0, 0, 1873, 633,0 }, - {2063,2063, 0, 0, 40000, 380,0 }, - {2064,2064, 0, 0, 366, 286,0 }, - {2065,2065, 0, 0, 8866, 1366,0 }, - {2066,2066, 0, 0, 40000, 1513,0 }, - {2067,2067, 0, 0, 40000, 333,0 }, - {2068,2068, 0, 0, 9600, 1573,0 }, - {2069,2069, 0, 0, 3293, 746,0 }, - {2070,2070, 0, 0, 40000, 53,0 }, - {2071,2071, 0, 0, 40000, 73,0 }, - {2072,2072, 0, 0, 40000, 73,0 }, - {2073,2073, 0, 0, 40000, 240,0 }, - {2074,2074, 0, 0, 40000, 240,0 }, - {2075,2075, 0, 0, 40000, 140,0 }, - {2076,2076, 0, 0, 40000, 113,0 }, - {2077,2077, 0, 0, 40000, 240,0 }, - {2078,2078, 0, 0, 3613, 1146,0 }, - {2079,2079, 0, 0, 40000, 126,0 }, - {2080,2080, 0, 0, 40000, 0,0 }, - {2081,2081, 0, 0, 40000, 633,0 }, - {2082,2082, 0, 0, 40000, 453,0 }, - {2083,2083, 0, 0, 40000, 1146,0 }, - {2084,2084, 0, 0, 40000, 3600,0 }, - {2085,2085, 0, 0, 40000, 1586,0 }, - {2086,2086, 0, 0, 40000, 1586,0 }, - {2087,2087, 0, 0, 40000, 1586,0 }, - {2088,2088, 0, 0, 40000, 1646,0 }, - {2089,2089, 0, 0, 40000, 1580,0 }, - {2090,2090, 0, 0, 40000, 4393,0 }, - {2091,2091, 0, 0, 40000, 4540,0 }, - {2092,2092, 0, 0, 21373, 6160,0 }, - {2093,2093, 0, 0, 40000, 633,0 }, - {2094,2094, 0, 0, 18420, 6146,0 }, - {2095,2095, 0, 0, 2306, 813,0 }, - {2096,2096, 0, 0, 2813, 333,0 }, - {2097,2097, 0, 0, 3106, 600,0 }, - {2098,2098, 0, 0, 1026, 1580,0 }, - {2099,2099, 0, 0, 1873, 346,0 }, - {2100,2100, 0, 0, 40000, 73,0 }, - {2101,2101, 0, 0, 40000, 73,0 }, - {2102,2102, 0, 0, 1200, 1906,0 }, - {2103,2103, 0, 0, 980, 1313,0 }, - {2104,2104, 0, 0, 200, 20,0 }, - {2105,2105, 0, 0, 640, 253,0 }, - {2106,2106, 0, 0, 3120, 240,0 }, - {2107,2107, 0, 0, 753, 146,0 }, - {2108,2108, 0, 0, 40000, 3060,0 }, - {2109,2109, 0, 0, 40000, 233,0 }, - {2110,2110, 0, 0, 40000, 246,0 }, - {2111,2111, 0, 0, 40000, 240,0 }, - { 752, 752, 60, 0, 173, 20,0 }, - { 755, 755, 12, 0, 626, 240,0 }, - {2112,2112, 89, 0, 113, 0,0 }, - {2113,2113, 89, 0, 700, 266,0 }, - { 755, 755, 14, 0, 626, 240,0 }, - { 755, 755, 16, 0, 626, 246,0 }, - {2114,2114, 84, 0, 1593, 553,0 }, - { 755, 755, 19, 0, 626, 240,0 }, - {2115,2115, 38, 0, 220, 166,0 }, - {2116,2116, 36, 0, 1686, 760,0 }, - { 755, 755, 28, 0, 626, 240,0 }, - { 755, 755, 26, 0, 626, 240,0 }, - { 755, 755, 35, 0, 633, 246,0 }, - { 755, 755, 30, 0, 626, 240,0 }, - {2117,2117, 60, 0, 180, 53,0 }, - {2104,2104, 60, 0, 173, 20,0 }, - {2104,2104, 55, 0, 173, 20,0 }, - { 730, 730, 94, 0, 1886, 660,0 }, - {2118,2118, 0, 0, 1226, 73,0 }, - {2119,2119, 0, 0, 40000, 0,0 }, - {2120,2120, 0, 0, 40000, 146,0 }, - {2121,2121, 0, 0, 40000, 80,0 }, - {2122,2122, 0, 0, 40000, 80,0 }, - {2123,2123, 0, 0, 40000, 0,0 }, - {2124,2124, 0, 0, 40000, 126,0 }, - {2125,2125, 0, 0, 40000, 213,0 }, - {2126,2126, 0, 0, 40000, 80,0 }, - {2127,2127, 0, 0, 40000, 73,0 }, - {2128,2128, 0, 0, 40000, 73,0 }, - {2129,2129, 0, 0, 40000, 73,0 }, - {2130,2130, 0, 0, 40000, 80,0 }, - {2131,2131, 0, 0, 40000, 73,0 }, - {2132,2132, 0, 0, 40000, 73,0 }, - {2133,2133, 0, 0, 40000, 66,0 }, - {2134,2134, 0, 0, 40000, 186,0 }, - {2135,2135, 0, 0, 9966, 426,0 }, - {2136,2136, 0, 0, 40000, 400,0 }, - {2137,2137, 0, 0, 40000, 326,0 }, - {2138,2138, 0, 0, 386, 80,0 }, - {2139,2139, 0, 0, 40000, 246,0 }, - {2140,2140, 0, 0, 3473, 73,0 }, - {2141,2141, 60, 0, 160, 66,0 }, - {2141,2141, 44, 0, 160, 60,0 }, - {2142,2142, 47, 0, 173, 93,0 }, - {2143,2143, 47, 0, 186, 80,0 }, - {2144,2144, 62, 0, 1933, 93,0 }, - {2145,2145, 93, 0, 1146, 473,0 }, - {2146,2146, 50, 0, 286, 93,0 }, - {2145,2145, 40, 0, 2013, 840,0 }, - {2147,2147, 60, 0, 106, 73,0 }, - { 898, 898, 60, 0, 173, 133,0 }, - {2147,2147, 57, 0, 106, 73,0 }, - { 900, 900, 42, 0, 620, 240,0 }, - { 900, 900, 38, 0, 626, 240,0 }, - { 908, 908, 88, 0, 160, 26,0 }, - {2148,2148, 0, 0, 9440, 140,0 }, - {2149,2149, 0, 0, 40000, 73,0 }, - {2150,2150, 0, 0, 4613, 420,0 }, - {2151,2151, 0, 0, 40000, 86,0 }, - {2152,2152, 0, 0, 40000, 406,0 }, - {2153,2153, 0, 0, 40000, 440,0 }, - {2154,2154, 0, 0, 4340, 133,0 }, - {2155,2155, 0, 0, 4460, 706,0 }, - {2156,2156, 0, 0, 40000, 73,0 }, - {2157,2157, 0, 0, 4660, 1573,0 }, - {2158,2158, 0, 0, 966, 333,0 }, - {2159,2159, 0, 0, 1933, 640,0 }, - { 136, 136, 0, 0, 2326, 786,0 }, - { 168, 168, 0, 0, 286, 366,0 }, - { 164, 164, 0, 0, 7373, 2460,0 }, - { 167, 167, 0, 0, 793, 426,0 }, - {2160,2160, 65, 0, 166, 73,0 }, - {2161,2161, 21, 0, 480, 146,0 }, - {2162, 173, 0, 4, 4220, 80,0 }, - {2163,2164, 0, 4, 4640, 3066,0 }, - {2165,2166, 0, 4, 7273, 3920,0 }, - {2167,2168, 0, 4, 3766, 1253,0 }, - {2169,2170, 0, 4, 6266, 2400,0 }, - {2171,2172, 0, 4, 18213, 0,0 }, - {2173,2174, 0, 4, 40000, 713,0 }, - {2175,2174, 0, 4, 40000, 733,0 }, - {2176, 299, 0, 4, 40000, 273,0 }, - {2177,2178, 0, 4, 40000, 66,0 }, - {2179,2180, 0, 4, 40000, 393,0 }, - {2181,2182, 0, 4, 40000, 413,0 }, - {2183,2184, 0, 4, 7406, 200,0 }, - { 127, 127, 65, 0, 226, 120,0 }, - { 127, 127, 72, 0, 180, 100,0 }, - { 364, 365, 52, 4, 120, 0,0 }, - {2185,2186, 60, 4, 173, 0,0 }, - {1550,1551, 47, 4, 520, 0,0 }, - {1556,1557, 76, 4, 833, 0,0 }, - { 374, 375, 84, 4, 813, 0,0 }, - {1564,1565, 83, 4, 220, 0,0 }, - {1568,1569, 24, 4, 1840, 620,0 }, - {1556,1557, 77, 4, 820, 300,0 }, - {1572,1573, 60, 4, 286, 0,0 }, - {1574,1575, 65, 4, 293, 0,0 }, - { 391, 392, 44, 4, 160, 53,0 }, - { 391, 393, 40, 4, 460, 66,0 }, - {1606,1607, 72, 4, 120, 73,0 }, - { 398, 399, 73, 4, 1293, 173,0 }, - {1608,1609, 70, 4, 1580, 0,0 }, - {2187,2187, 0, 0, 40000, 353,0 }, - {2188,2188, 0, 0, 40000, 333,0 }, - {2189,2189, 0, 0, 5913, 2306,0 }, - {2190,2190, 0, 0, 7720, 1260,0 }, - {2191,2191, 0, 0, 213, 6420,0 }, - {2192,2192, 0, 0, 40000, 380,0 }, - {2193,2193, 0, 0, 1153, 760,0 }, - {2194,2194, 0, 0, 40000, 66,0 }, - {2195,2195, 0, 0, 4440, 66,0 }, - {2196,2196, 0, 0, 40000, 73,0 }, - {2197,2197, 0, 0, 40000, 53,0 }, - {2198,2198, 0, 0, 40000, 60,0 }, - {2199,2199, 0, 0, 40000, 60,0 }, - {2200,2200, 0, 0, 8133, 1433,0 }, - { 528, 528, 0, 0, 966, 346,0 }, - {2201,2201, 0, 0, 40000, 126,0 }, - {2202,2202, 0, 0, 286, 1293,0 }, - {2203,2203, 0, 0, 40000, 0,0 }, - {2204,2204, 41, 0, 246, 20,0 }, - {2205,2205, 84, 0, 160, 26,0 }, - {2206,2206, 72, 0, 440, 180,0 }, - { 741, 741, 48, 0, 220, 26,0 }, - {2207,2207, 0, 0, 2126, 173,0 }, - {2208,2208, 0, 0, 40000, 0,0 }, - {2209,2209, 0, 0, 40000, 380,0 }, - {2210,2210, 0, 0, 4553, 1486,0 }, - {2211,2211, 0, 0, 40000, 73,0 }, - {2212,2212, 0, 0, 40000, 73,0 }, - {2213,2213, 0, 0, 1460, 80,0 }, - {2214,2214, 0, 0, 40000, 66,0 }, - {2215,2215, 0, 0, 40000, 186,0 }, - {2216,2216, 0, 0, 40000, 180,0 }, - {2217,2217, 0, 0, 40000, 173,0 }, - {2218,2218, 0, 0, 40000, 113,0 }, - {2219,2219, 0, 0, 40000, 86,0 }, - {2220,2220, 0, 0, 40000, 373,0 }, - {2221,2221, 0, 0, 40000, 113,0 }, - {2222,2222, 0, 0, 40000, 353,0 }, - {2223,2223, 0, 0, 40000, 66,0 }, - {2224,2224, 0, 0, 40000, 53,0 }, - {2225,2225, 0, 0, 40000, 66,0 }, - {2226,2226, 0, 0, 40000, 100,0 }, - {2227,2227, 0, 0, 40000, 73,0 }, - {2228,2228, 0, 0, 40000, 73,0 }, - {2229,2229, 0, 0, 40000, 66,0 }, - {2230,2230, 0, 0, 40000, 66,0 }, - {2231,2231, 0, 0, 40000, 80,0 }, - {2232,2232, 0, 0, 40000, 66,0 }, - {2233,2233, 0, 0, 40000, 80,0 }, - {2234,2234, 0, 0, 40000, 660,0 }, - {2235,2235, 0, 0, 40000, 120,0 }, - {2236,2236, 0, 0, 9820, 393,0 }, - {2237,2237, 0, 0, 40000, 73,0 }, - {2238,2238, 0, 0, 3620, 1166,0 }, - {2239,2239, 0, 0, 40000, 0,0 }, - {2240,2240, 0, 0, 40000, 0,0 }, - {2241,2241, 0, 0, 3020, 66,0 }, - {2242,2242, 0, 0, 6053, 1240,0 }, - {2243,2243, 0, 0, 633, 126,0 }, - {2244,2244, 0, 0, 40000, 66,0 }, - {2245,2245, 0, 0, 40000, 73,0 }, - {2246,2246, 0, 0, 626, 246,0 }, - {2247,2247, 60, 0, 173, 93,0 }, - {2248,2248, 60, 0, 673, 206,0 }, - {2249,2249, 48, 0, 673, 200,0 }, - {2250,2250, 60, 0, 1873, 653,0 }, - {2251,2251, 60, 0, 673, 200,0 }, - {2252,2252, 66, 0, 306, 120,0 }, - {2253,2253, 60, 0, 673, 200,0 }, - {2249,2249, 64, 0, 673, 206,0 }, - {2254,2254, 60, 0, 246, 106,0 }, - {2255,2255, 60, 0, 193, 120,0 }, - {2256,2256, 56, 0, 206, 13,0 }, - {2257,2257, 53, 0, 433, 73,0 }, - {2258,2258, 60, 0, 220, 113,0 }, - {2259,2259, 48, 0, 300, 66,0 }, - {2260,2260, 67, 0, 273, 60,0 }, - {2261,2261, 60, 0, 973, 360,0 }, - {2261,2261, 72, 0, 806, 273,0 }, - {2262,2262, 60, 0, 173, 93,0 }, - {2263,2263, 0, 0, 2493, 866,0 }, - {2264,2264, 24, 0, 173, 93,0 }, - {2265,2265, 36, 0, 140, 0,0 }, - { 343, 343, 36, 0, 146, 80,0 }, - { 347, 347, 0, 0, 353, 133,0 }, - { 347, 347, 12, 0, 420, 146,0 }, - {2266,2266, 12, 0, 346, 100,0 }, - {2267,2267, 24, 0, 106, 46,0 }, - {2267,2267, 36, 0, 100, 0,0 }, - {2268,2268, 0, 0, 1006, 293,0 }, - {2266,2266, 24, 0, 293, 93,0 }, - {2269,2269, 88, 0, 1106, 120,0 }, - {2270,2270, 88, 0, 666, 120,0 }, - {2271,2271, 13, 0, 760, 360,0 }, - { 351, 351, 0, 0, 966, 346,0 }, - {2271,2271, 15, 0, 760, 420,0 }, - {2272,2272, 0, 0, 4513, 640,0 }, - {2273,2273, 0, 0, 15486, 1580,0 }, - {2274,2274, 0, 0, 6940, 66,0 }, - {2275,2275, 0, 0, 6866, 2380,0 }, - {2276,2276, 0, 0, 7613, 1566,0 }, - {2277,2277, 0, 0, 1186, 420,0 }, - {2278,2278, 0, 0, 1166, 400,0 }, - {2279,2279, 0, 0, 40000, 2940,0 }, - {2280,2280, 0, 0, 40000, 0,0 }, - {2281,2281, 0, 0, 18226, 786,0 }, - {2282,2282, 0, 0, 40000, 0,0 }, - {2283,2283, 0, 0, 713, 200,0 }, - {2284,2284, 0, 0, 40000, 126,0 }, - {2285,2285, 0, 0, 40000, 353,0 }, - {2286,2286, 0, 0, 40000, 333,0 }, - {2287,2287, 0, 0, 40000, 0,0 }, - {2288,2288, 0, 0, 40000, 0,0 }, - {2289,2289, 0, 0, 40000, 0,0 }, - {2290,2290, 0, 0, 40000, 0,0 }, - {2291,2291, 0, 0, 40000, 73,0 }, - {2292,2292, 0, 0, 40000, 66,0 }, - {2293,2293, 0, 0, 15893, 153,0 }, - {2294,2294, 0, 0, 40000, 253,0 }, - {2295,2295, 0, 0, 2813, 333,0 }, - {2296,2296, 0, 0, 40000, 3920,0 }, - {2297,2297, 79, 0, 113, 0,0 }, - {2297,2297, 72, 0, 126, 140,0 }, - {2298,2298, 72, 0, 100, 26,0 }, - {2298,2298, 79, 0, 100, 0,0 }, - { 554, 554, 60, 0, 400, 126,0 }, - {2299,2299, 72, 0, 793, 173,0 }, - {2300,2300, 84, 0, 226, 66,0 }, - { 555, 555, 66, 0, 113, 0,0 }, - {2301,2302, 35, 4, 2333, 800,0 }, - {2303,2304, 52, 4, 120, 0,0 }, - {2305,1548, 48, 4, 173, 0,0 }, - {1595,1595, 58, 0, 146, 166,0 }, - {2305,1548, 60, 4, 173, 0,0 }, - {2306,2307, 47, 4, 1893, 700,0 }, - {2306,2307, 43, 4, 1953, 740,0 }, - {2306,2307, 49, 4, 1880, 686,0 }, - {2306,2307, 51, 4, 1886, 706,0 }, - {2306,2307, 54, 4, 1906, 720,0 }, - {2306,2307, 57, 4, 1900, 720,0 }, - {2306,2307, 72, 4, 1593, 606,0 }, - {2306,2307, 60, 4, 1900, 720,0 }, - {2306,2307, 76, 4, 1593, 606,0 }, - {2306,2307, 84, 4, 1593, 613,0 }, - {2306,2307, 36, 4, 2386, 920,0 }, - {1560,2308, 65, 4, 293, 213,0 }, - {2309,2310, 84, 4, 1373, 306,0 }, - {1564,1564, 83, 0, 220, 113,0 }, - { 380, 381, 84, 4, 1593, 566,0 }, - {1568,1568, 24, 0, 1833, 613,0 }, - {2306,2307, 77, 4, 1593, 606,0 }, - {2311,2312, 60, 4, 286, 0,0 }, - {2313,2314, 65, 4, 513, 0,0 }, - {2315,2315, 59, 0, 106, 0,0 }, - {2316,2316, 51, 0, 386, 373,0 }, - {1612,1612, 45, 0, 393, 380,0 }, - {2317,2317, 71, 0, 446, 180,0 }, - {2318,2318, 60, 0, 280, 20,0 }, - {2319,2319, 58, 0, 393, 373,0 }, - {2320,2320, 53, 0, 393, 380,0 }, - { 397, 397, 64, 0, 220, 86,0 }, - {2321,2321, 71, 0, 106, 46,0 }, - {2322,2322, 61, 0, 986, 340,0 }, - {2323,2323, 61, 0, 1893, 633,0 }, - {2324, 392, 44, 4, 166, 46,0 }, - {2324, 393, 40, 4, 460, 60,0 }, - {1595,1595, 69, 0, 126, 140,0 }, - {1595,1595, 68, 0, 126, 140,0 }, - {1595,1595, 63, 0, 146, 166,0 }, - {2325,2326, 74, 4, 380, 106,0 }, - {2327,2328, 60, 4, 1026, 333,0 }, - {2329,2330, 80, 4, 40000, 0,0 }, - {2331,2332, 64, 4, 1900, 640,0 }, - { 397, 397, 72, 0, 193, 80,0 }, - {2333,2334, 78, 4, 820, 0,0 }, - {1608,1609, 82, 4, 1580, 0,0 }, - {2315,2315, 48, 0, 106, 0,0 }, - {2316,2316, 53, 0, 386, 373,0 }, - {2335,2335, 0, 0, 3586, 1133,0 }, - {2336,2337, 0, 4, 1186, 420,0 }, - {2338,2339, 0, 4, 40000, 320,0 }, - {2340,2340, 0, 0, 8826, 1346,0 }, - {2341,2341, 0, 0, 3440, 753,0 }, - {2342,2342, 0, 0, 40000, 360,0 }, - {2343,2343, 0, 0, 40000, 413,0 }, - {2344,2345, 0, 4, 40000, 60,0 }, - {2346,2346, 0, 0, 40000, 60,0 }, - {2347,2348, 0, 4, 40000, 126,0 }, - {2349,2350, 0, 4, 40000, 73,0 }, - {2351,2352, 0, 4, 40000, 73,0 }, - {2353,2354, 0, 4, 40000, 86,0 }, - {2355,2356, 0, 4, 40000, 453,0 }, - {2357,2357, 14, 0, 186, 20,0 }, - {2358,2358, 35, 0, 246, 73,0 }, - {2357,2357, 19, 0, 166, 26,0 }, - {2359,2359, 43, 0, 286, 133,0 }, - {2360,2360, 41, 0, 300, 113,0 }, - {2360,2360, 43, 0, 253, 106,0 }, - {2360,2360, 45, 0, 240, 100,0 }, - {2360,2360, 47, 0, 240, 100,0 }, - {2361,2362, 0, 4, 14720, 333,0 }, - {2363,2363, 0, 0, 7373, 1246,0 }, - {2364,2364, 0, 0, 4900, 233,0 }, - {2365,2365, 0, 0, 5106, 606,0 }, - {2366,2366, 0, 0, 1333, 153,0 }, - {2367,2367, 0, 0, 2093, 840,0 }, - {2368,2368, 0, 0, 3700, 226,0 }, - {2369,2369, 0, 0, 3546, 0,0 }, - {2370,2370, 0, 0, 4606, 420,0 }, - {2371,2371, 0, 0, 14366, 606,0 }, - {2372,2372, 0, 0, 40000, 426,0 }, - {2373,2373, 0, 0, 3700, 200,0 }, - {2374,2374, 0, 0, 880, 440,0 }, - {2375,2375, 0, 0, 4660, 660,0 }, - {2376,2376, 0, 0, 3600, 1153,0 }, - {2377,2377, 0, 0, 40000, 73,0 }, - {2378,2378, 0, 0, 40000, 53,0 }, - {2379,2379, 0, 0, 40000, 333,0 }, - {2380,2380, 0, 0, 40000, 73,0 }, - {2381,2381, 0, 0, 40000, 73,0 }, - {2382,2382, 0, 0, 40000, 66,0 }, - {2383,2383, 0, 0, 40000, 73,0 }, - {2384,2384, 0, 0, 40000, 73,0 }, - {2385,2385, 0, 0, 840, 226,0 }, - {2386,2386, 0, 0, 2093, 86,0 }, - {2387,2387, 0, 0, 906, 73,0 }, - { 402, 402, 0, 0, 273, 60,0 }, - {2388,2388, 0, 0, 40000, 820,0 }, - {2389,2389, 0, 0, 4740, 93,0 }, - {2390,2390, 0, 0, 706, 106,0 }, - {2391,2391, 0, 0, 40000, 0,0 }, - {2392,2392, 0, 0, 3840, 2306,0 }, - {2393,2393, 0, 0, 3400, 493,0 }, - {2394,2394, 0, 0, 40000, 53,0 }, - {2395,2395, 0, 0, 40000, 133,0 }, - {2396,2397, 0, 4, 3093, 1400,0 }, - {2398,2398, 0, 0, 1080, 580,0 }, - {2399,2400, 0, 4, 2220, 400,0 }, - {2401,2401, 0, 0, 40000, 193,0 }, - {2402,2402, 0, 0, 40000, 60,0 }, - {2403,2404, 0, 4, 40000, 146,0 }, - {2405,2406, 0, 4, 40000, 133,0 }, - {2407,2408, 0, 4, 40000, 66,0 }, - {2409,2409, 0, 0, 40000, 0,0 }, - {2410,2410, 0, 0, 40000, 73,0 }, - {2411,2411, 0, 0, 40000, 66,0 }, - {2412,2413, 0, 4, 40000, 153,0 }, - {2414,2414, 0, 0, 40000, 126,0 }, - {2415,2416, 0, 4, 40000, 466,0 }, - {2417,2418, 0, 4, 40000, 113,0 }, - {2419,2420, 0, 4, 1280, 73,0 }, - {2421,2422, 0, 4, 1113, 146,0 }, - {2423,2424, 0, 4, 3660, 113,0 }, - {2425,2426, 0, 4, 40000, 80,0 }, - {2427,2427, 33, 0, 300, 246,0 }, - {2428,2429, 38, 4, 53, 0,0 }, - {2430,2430, 38, 0, 106, 0,0 }, - {2431,2431, 38, 0, 340, 20,0 }, - {2432,2432, 40, 0, 73, 0,0 }, - {2433,2434, 41, 4, 300, 0,0 }, - {2435,2435, 0, 0, 133, 73,0 }, - {2435,2435, 41, 0, 133, 73,0 }, - {2360,2360, 48, 0, 240, 100,0 }, - {2436,2436, 17, 0, 4620, 1553,0 }, - {2360,2360, 50, 0, 240, 100,0 }, - {2435,2435, 45, 0, 126, 66,0 }, - {2437,2437,254, 2, 6, 0,0 }, - {2438,2438, 60, 0, 226, 93,0 }, - {2439,2439, 56, 0, 233, 93,0 }, - {2440,2440, 60, 0, 140, 66,0 }, - {2440,2440, 55, 0, 140, 60,0 }, - {2441,2441, 63, 0, 286, 126,0 }, - {2442,2442, 57, 0, 173, 93,0 }, - {2443,2443, 0, 0, 40000, 280,0 }, - {2444,2444, 0, 0, 40000, 0,0 }, - {2445,2445, 0, 0, 40000, 746,0 }, - {2446,2446, 0, 0, 40000, 353,0 }, - {2447,2447, 0, 0, 40000, 1173,0 }, - {2448,2448, 0, 0, 40000, 146,0 }, - {2449,2449, 0, 0, 40000, 1160,0 }, - {2450,2450, 0, 0, 40000, 353,0 }, - {2451,2451, 0, 0, 18313, 6046,0 }, - {2452,2452, 0, 0, 1206, 420,0 }, - { 752, 752, 55, 0, 173, 20,0 }, - {2453,2453, 0, 0, 2860, 806,0 }, - {2454,2454, 0, 0, 2506, 126,0 }, - {2455,2455, 0, 0, 520, 93,0 }, - {2456,2456, 0, 0, 1420, 160,0 }, - {2457,2457, 0, 0, 40000, 53,0 }, - {2458,2458, 0, 0, 9106, 100,0 }, - {2459,2459, 0, 0, 3706, 100,0 }, - {2460,2460, 0, 0, 17933, 100,0 }, - {2461,2461, 0, 0, 40000, 0,0 }, - {2462,2462, 0, 0, 40000, 66,0 }, - {2463,2463, 0, 0, 40000, 0,0 }, - { 884, 884, 0, 0, 306, 73,0 }, - { 884, 884, 28, 0, 306, 73,0 }, - {2464,2464, 29, 0, 226, 93,0 }, - { 886, 886, 31, 0, 113, 0,0 }, - { 360, 360, 32, 0, 133, 40,0 }, - { 361, 361, 33, 0, 286, 80,0 }, - {2453,2453, 34, 0, 2873, 813,0 }, - { 888, 888, 29, 0, 246, 46,0 }, - { 886, 886, 55, 0, 100, 0,0 }, - { 890, 890, 48, 0, 240, 60,0 }, - { 884, 884, 58, 0, 146, 26,0 }, - {2465,2465, 45, 0, 173, 93,0 }, - {2465,2465, 43, 0, 173, 93,0 }, - {2466,2466, 73, 0, 1633, 86,0 }, - {2467,2467, 72, 0, 866, 553,0 }, - {2468,2468, 76, 0, 1380, 0,0 }, - {2467,2467, 84, 0, 873, 560,0 }, - {2468,2468, 36, 0, 1933, 880,0 }, - {2469,2469, 65, 0, 300, 120,0 }, - {2470,2470, 83, 0, 193, 86,0 }, - {2471,2471, 50, 0, 966, 126,0 }, - {2468,2468, 77, 0, 1373, 620,0 }, - { 897, 897, 55, 0, 126, 40,0 }, - {2472,2472, 60, 0, 180, 140,0 }, - { 897, 897, 50, 0, 126, 40,0 }, - {2473,2473, 42, 0, 633, 240,0 }, - {2473,2473, 46, 0, 513, 200,0 }, - {2474,2474, 71, 0, 433, 180,0 }, - {2474,2474, 60, 0, 513, 206,0 }, - {2455,2455, 58, 0, 220, 46,0 }, - {2455,2455, 53, 0, 286, 60,0 }, - {2475,2475, 91, 0, 186, 100,0 }, - {2476,2476, 61, 0, 226, 26,0 }, - {2477,2477, 61, 0, 886, 73,0 }, - {2478,2478, 44, 0, 120, 73,0 }, - {2479,2479, 40, 0, 933, 73,0 }, - {2480,2480, 69, 0, 146, 33,0 }, - { 361, 361, 68, 0, 153, 26,0 }, - { 361, 361, 63, 0, 180, 26,0 }, - {2481,2481, 74, 0, 153, 73,0 }, - {2482,2482, 60, 0, 280, 100,0 }, - { 908, 908, 80, 0, 160, 26,0 }, - {2483,2483, 64, 0, 986, 353,0 }, - {2483,2483, 73, 0, 813, 306,0 }, - {2483,2483, 70, 0, 820, 306,0 }, - { 886, 886, 68, 0, 93, 0,0 }, - { 886, 886, 48, 0, 106, 0,0 }, - {2484,2484, 0, 0, 40000, 0,0 }, - {2485,2485, 0, 0, 3226, 753,0 }, - {2486,2486, 0, 0, 1773, 553,0 }, - {2487,2487, 0, 0, 7473, 2460,0 }, - {2488,2488, 0, 0, 40000, 0,0 }, - {2489,2489, 0, 0, 40000, 353,0 }, - {2490,2490, 0, 0, 40000, 206,0 }, - {2491,2491, 0, 0, 40000, 86,0 }, - {2492,2492, 0, 0, 4740, 86,0 }, - {2493,2493, 0, 0, 6193, 193,0 }, - {2494,2494, 0, 0, 6200, 240,0 }, - {2495,2495, 0, 0, 40000, 0,0 }, - {2496,2496, 0, 0, 1586, 73,0 }, - {2497,2497, 0, 0, 560, 73,0 }, - {2498,2498, 0, 0, 40000, 480,0 }, - {2499,2499, 0, 0, 40000, 80,0 }, - {2500,2500, 0, 0, 40000, 66,0 }, - {2501,2501, 0, 0, 40000, 380,0 }, - {2502,2502, 0, 0, 280, 100,0 }, - {2503,2503, 0, 0, 6193, 233,0 }, - {2504,2504, 0, 0, 40000, 380,0 }, - {2505,2505, 0, 0, 40000, 0,0 }, - {2506,2506, 0, 0, 40000, 380,0 }, - {2507,2507, 0, 0, 40000, 200,0 }, - {2508,2508, 0, 0, 40000, 320,0 }, - {2509,2509, 0, 0, 40000, 126,0 }, - {2510,2510, 0, 0, 40000, 293,0 }, - {2511,2511, 0, 0, 40000, 0,0 }, - {2512,2512, 0, 0, 40000, 40,0 }, - {2513,2513, 0, 0, 40000, 106,0 }, - {2514,2514, 0, 0, 3846, 73,0 }, - {2515,2515, 0, 0, 40000, 0,0 }, - {2516,2516, 0, 0, 40000, 73,0 }, - {2517,2517, 0, 0, 40000, 533,0 }, - {2518,2518, 0, 0, 40000, 1020,0 }, - {2519,2519, 0, 0, 40000, 73,0 }, - {2520,2520, 0, 0, 40000, 53,0 }, - {2521,2521, 0, 0, 6153, 1433,0 }, - {2522,2522, 0, 0, 18813, 773,0 }, - {2523,2523, 0, 0, 40000, 433,0 }, - {2524,2524, 0, 0, 40000, 0,0 }, - {2525,2525, 0, 0, 40000, 133,0 }, - {2526,2526, 0, 0, 4486, 73,0 }, - { 346, 346, 30, 0, 540, 33,0 }, - { 346, 346, 31, 0, 406, 20,0 }, - { 346, 346, 32, 0, 406, 20,0 }, - { 346, 346, 33, 0, 406, 73,0 }, - { 346, 346, 34, 0, 406, 20,0 }, - { 346, 346, 35, 0, 406, 20,0 }, - { 346, 346, 37, 0, 406, 73,0 }, - { 346, 346, 39, 0, 406, 73,0 }, - { 346, 346, 41, 0, 406, 20,0 }, - { 346, 346, 43, 0, 306, 20,0 }, - { 346, 346, 45, 0, 306, 20,0 }, - { 346, 346, 47, 0, 306, 20,0 }, - { 346, 346, 48, 0, 306, 20,0 }, - { 346, 346, 49, 0, 306, 20,0 }, - { 512, 512, 84, 0, 353, 466,0 }, - {2206,2206, 84, 0, 440, 180,0 }, - {2527,2527, 55, 0, 100, 0,0 }, - {2528,2528, 36, 0, 400, 160,0 }, - {2529,2529, 38, 0, 313, 226,0 }, - {2530,2530, 60, 0, 286, 133,0 }, - {2531,2531, 38, 0, 200, 100,0 }, - {2532,2532, 17, 0, 6186, 240,0 }, - {2532,2532, 18, 0, 6186, 240,0 }, - {2532,2532, 19, 0, 6193, 233,0 }, - {2532,2532, 20, 0, 6193, 193,0 }, - {2532,2532, 21, 0, 6193, 193,0 }, - {2532,2532, 22, 0, 6193, 193,0 }, - {2532,2532, 23, 0, 6193, 193,0 }, - {2532,2532, 24, 0, 6193, 193,0 }, - {2532,2532, 25, 0, 6193, 193,0 }, - {2532,2532, 26, 0, 6193, 193,0 }, - {2532,2532, 27, 0, 6193, 253,0 }, - {2532,2532, 28, 0, 6193, 246,0 }, - {2532,2532, 29, 0, 6193, 246,0 }, - {2533,2533, 84, 0, 433, 180,0 }, - {2534,2534, 48, 0, 280, 93,0 }, - {2535,2535, 65, 0, 1166, 360,0 }, - {2536,2536, 65, 0, 1853, 633,0 }, - {2537,2537, 55, 0, 453, 366,0 }, - {2537,2537, 41, 0, 540, 433,0 }, - { 346, 346, 63, 0, 240, 66,0 }, - { 346, 346, 55, 0, 240, 66,0 }, - {2538,2538, 55, 0, 2586, 200,0 }, - {2538,2538, 53, 0, 2586, 200,0 }, - {2534,2534, 50, 0, 280, 93,0 }, - { 506, 506, 84, 0, 693, 566,0 }, - { 506, 506, 74, 0, 693, 560,0 }, - { 504, 504, 84, 0, 1566, 546,0 }, - { 504, 504, 74, 0, 1586, 560,0 }, - {2539,2539, 84, 0, 440, 20,0 }, - {2540,2540, 74, 0, 126, 26,0 }, - {1911,1911, 48, 0, 500, 180,0 }, - {1911,1911, 36, 0, 606, 220,0 }, - {2541,2541, 74, 0, 686, 560,0 }, - {2542,2542, 0, 0, 7313, 13,0 }, - {2543,2543, 0, 0, 40000, 1306,0 }, - {2544,2544, 0, 0, 40000, 0,0 }, - {2545,2545, 0, 0, 4613, 13,0 }, - {2546,2547, 0, 4, 6933, 133,0 }, - {2548,2549, 0, 4, 40000, 86,0 }, - {2550,2550, 0, 0, 9233, 100,0 }, - {2551,2552, 0, 4, 4640, 73,0 }, - {2553,2553, 0, 0, 40000, 73,0 }, - {2554,2554, 0, 0, 40000, 0,0 }, - {2555,2556, 0, 4, 40000, 73,0 }, - {2557,2557, 0, 0, 40000, 60,0 }, - {2558,1467, 0, 4, 40000, 66,0 }, - {2559,2560, 0, 4, 40000, 40,0 }, - {2561,2561, 0, 0, 40000, 186,0 }, - {2562,2562, 0, 0, 4026, 66,0 }, - {2563,2564, 0, 4, 14586, 80,0 }, - {2565,2565, 0, 0, 40000, 0,0 }, - {2566,2567, 0, 4, 40000, 40,0 }, - {2568,2568, 0, 0, 4020, 73,0 }, - {2569,2569, 0, 0, 40000, 0,0 }, - {2570,2570, 0, 0, 40000, 0,0 }, - {2571,2572, 0, 4, 40000, 126,0 }, - {2573,2574, 0, 4, 40000, 100,0 }, - {2575,2575, 0, 0, 40000, 213,0 }, - { 229,2576, 0, 4, 40000, 166,0 }, - {2577,2577, 0, 0, 7366, 53,0 }, - { 239,2578, 0, 4, 40000, 133,0 }, - {2579,2579, 0, 0, 40000, 80,0 }, - {2580,2580, 0, 0, 40000, 140,0 }, - {2581,2582, 0, 4, 16980, 1173,0 }, - {2583,2584, 0, 4, 726, 100,0 }, - {2585,2586, 0, 4, 40000, 73,0 }, - {2587,2588, 0, 4, 40000, 73,0 }, - {2589,2589, 0, 0, 40000, 60,0 }, - {2590,2590, 0, 0, 40000, 80,0 }, - {2591,2592, 0, 4, 40000, 73,0 }, - {2593,2594, 0, 4, 40000, 60,0 }, - {2595,2595, 0, 0, 40000, 66,0 }, - {2596,2597, 0, 4, 40000, 66,0 }, - {2598,2599, 0, 4, 40000, 60,0 }, - {2600,2601, 0, 4, 40000, 173,0 }, - {2602,2602, 0, 0, 40000, 60,0 }, - {2603,2603, 0, 0, 40000, 73,0 }, - {2604,2604, 0, 0, 40000, 93,0 }, - {2605,2606, 0, 4, 40000, 73,0 }, - {2607,2607, 0, 0, 40000, 66,0 }, - {2608,2609, 0, 4, 40000, 66,0 }, - {2610,2610, 0, 0, 40000, 86,0 }, - {2611,2611, 0, 0, 40000, 60,0 }, - {2612,2612, 0, 0, 14286, 73,0 }, - {2613,2613, 0, 0, 40000, 0,0 }, - {2614,2615, 0, 4, 40000, 73,0 }, - {2616,2617, 0, 4, 40000, 66,0 }, - {2618,2619, 0, 4, 133, 0,0 }, - {2620,2621, 0, 4, 40000, 1280,0 }, - {2622,2623, 0, 4, 40000, 160,0 }, - {2624,2625, 0, 4, 40000, 0,0 }, - {2626,2627, 0, 4, 40000, 73,0 }, - {2628,2629, 0, 4, 40000, 0,0 }, - {1516,2630, 0, 4, 1193, 406,0 }, - {2631,2632, 0, 4, 40000, 553,0 }, - {2633,2633, 0, 0, 40000, 40,0 }, - {2634,2635, 0, 4, 40000, 773,0 }, - {2636,2636, 0, 0, 40000, 320,0 }, - {2637,2637, 0, 0, 1880, 73,0 }, - {2638,2639, 0, 4, 486, 0,0 }, - {2640,2641, 0, 4, 17020, 1193,0 }, - {2642,2642, 0, 0, 40000, 720,0 }, - {2643,2644, 0, 4, 1880, 40,0 }, - {2645,2645, 0, 0, 40000, 73,0 }, - {2646,2647, 0, 4, 40000, 46,0 }, - {2648,2648, 0, 0, 2466, 80,0 }, - {2649,2649, 0, 0, 40000, 193,0 }, - {2650,2651, 0, 4, 993, 73,0 }, - {2652,2652, 0, 0, 40000, 220,0 }, - {2653,2654, 0, 4, 40000, 46,0 }, - {2655,2656, 0, 4, 40000, 46,0 }, - {2657,2657, 0, 0, 40000, 66,0 }, - {2658,2658, 35, 0, 626, 20,0 }, - {2659,2659, 35, 0, 306, 26,0 }, - {2660,2660, 52, 0, 126, 26,0 }, - {2661,2661, 60, 0, 286, 20,0 }, - {2662,2662, 58, 0, 113, 0,0 }, - {2663,2663, 60, 0, 380, 20,0 }, - {2664,2664, 50, 0, 1640, 66,0 }, - {2665,2665, 43, 0, 153, 20,0 }, - {2664,2664, 55, 0, 1640, 20,0 }, - {1553,1553, 43, 0, 160, 80,0 }, - {2666,2666, 50, 0, 980, 20,0 }, - {2667,2667, 43, 0, 446, 73,0 }, - {2666,2666, 53, 0, 1000, 80,0 }, - {2666,2666, 57, 0, 700, 73,0 }, - {2668,2668, 72, 0, 773, 13,0 }, - {2666,2666, 60, 0, 686, 20,0 }, - { 373, 373, 76, 0, 826, 20,0 }, - {2669,2669, 84, 0, 713, 20,0 }, - {2670,2670, 42, 0, 1186, 20,0 }, - {2671,2671, 65, 0, 293, 33,0 }, - {2672,2672, 84, 0, 386, 33,0 }, - {2673,2673, 84, 0, 1366, 20,0 }, - {2674,2674, 24, 0, 960, 73,0 }, - { 383, 383, 77, 0, 800, 20,0 }, - {2675,2675, 58, 0, 426, 26,0 }, - {2676,2676, 53, 0, 426, 20,0 }, - {2677,2677, 64, 0, 200, 66,0 }, - {2678,2678, 71, 0, 113, 13,0 }, - {2679,2679, 44, 0, 766, 66,0 }, - {2680,2680, 40, 0, 460, 60,0 }, - {2681,2681, 69, 0, 126, 26,0 }, - {2682,2682, 60, 0, 573, 66,0 }, - {2683,2683, 80, 0, 226, 20,0 }, - {2684,2684, 64, 0, 2693, 20,0 }, - {2685,2685, 72, 0, 120, 66,0 }, - {2686,2686, 70, 0, 820, 20,0 }, - {2687,2687, 48, 0, 173, 20,0 }, - {2688,2688, 53, 0, 980, 33,0 }, - {2689,2690, 0, 4, 40000, 286,0 }, - {2691,2692, 0, 4, 2340, 100,0 }, - {2693,2694, 0, 4, 380, 80,0 }, - {2695,2696, 0, 4, 14793, 73,0 }, - {2697,2698, 0, 4, 40000, 40,0 }, - { 192,2699, 0, 4, 40000, 73,0 }, - {2700,2701, 0, 4, 973, 126,0 }, - {2702,2703, 0, 4, 4666, 106,0 }, - {2704,2705, 0, 4, 40000, 73,0 }, - {2706,2707, 0, 4, 40000, 73,0 }, - {2708,2709, 0, 4, 40000, 73,0 }, - {2710,2711, 0, 4, 2053, 0,0 }, - {2712,1473, 0, 4, 320, 26,0 }, - {2713,2714, 0, 4, 573, 93,0 }, - {2715,2716, 0, 4, 6513, 0,0 }, - {1478,2717, 0, 4, 40000, 146,0 }, - {2718,2719, 0, 4, 40000, 66,0 }, - { 286,2720, 0, 4, 40000, 73,0 }, - {2721,2722, 0, 4, 40000, 86,0 }, - {2723,2724, 0, 4, 40000, 60,0 }, - {2725,2726, 0, 4, 393, 73,0 }, - {2727,2724, 0, 4, 40000, 60,0 }, - {1514,2728, 0, 4, 40000, 180,0 }, - {2729,2730, 0, 4, 40000, 0,0 }, - {2731,2732, 0, 4, 486, 0,0 }, - {2733,2734, 0, 4, 733, 0,0 }, - {2735,2736, 0, 4, 286, 40,0 }, - {2737,2738, 0, 4, 40000, 73,0 }, - {2739,2740, 0, 4, 1326, 746,0 }, - {2741,2742, 0, 4, 1340, 700,0 }, - {2743,2744, 0, 4, 40000, 0,0 }, - {2745,2746, 0, 4, 2046, 0,0 }, - {2747,2747, 35, 0, 386, 166,0 }, - {2748,2748, 60, 0, 493, 193,0 }, - {2749,2749, 43, 0, 126, 66,0 }, - {2750,2750, 0, 0, 3740, 1260,0 }, - {2751,2752, 0, 4, 14846, 353,0 }, - {2753,2754, 0, 4, 10266, 0,0 }, - {2755,2756, 0, 4, 18286, 146,0 }, - {2757,2758, 0, 4, 14520, 333,0 }, - {2759,2760, 0, 4, 14686, 633,0 }, - {2761,2762, 0, 4, 14826, 300,0 }, - {2763,2764, 0, 4, 10493, 0,0 }, - {2765,2766, 0, 4, 40000, 60,0 }, - {2767,2768, 0, 4, 40000, 80,0 }, - {2769,2770, 0, 4, 40000, 80,0 }, - {2771,2772, 0, 4, 40000, 73,0 }, - {2773,2774, 0, 4, 40000, 73,0 }, - {2775,2776, 0, 4, 40000, 80,0 }, - {2777,2778, 0, 4, 40000, 73,0 }, - {2779,2780, 0, 4, 40000, 73,0 }, - {2781,2782, 0, 4, 40000, 66,0 }, - {2783,2784, 0, 4, 7260, 186,0 }, - {2785,2786, 0, 4, 10386, 0,0 }, - {2787,2788, 0, 4, 40000, 246,0 }, - {2789,2790, 0, 4, 9173, 746,0 }, - {2791,2792, 0, 4, 7440, 666,0 }, - {2793,2794, 0, 4, 40000, 0,0 }, - {2795,2796, 0, 4, 40000, 413,0 }, - {2795,2797, 0, 4, 40000, 1506,0 }, - {2798,2799, 0, 4, 40000, 60,0 }, - {2800,2801, 0, 4, 40000, 233,0 }, - {2802,2803, 0, 4, 40000, 80,0 }, - {2804,2805, 0, 4, 40000, 80,0 }, - {2806,2807, 0, 4, 4520, 80,0 }, - {2808,2809, 0, 4, 40000, 73,0 }, - {2810,2811, 0, 4, 1186, 100,0 }, - {2812,2813, 0, 4, 953, 153,0 }, - {2814,2815, 0, 4, 14786, 126,0 }, - {2816,2817, 0, 4, 14800, 193,0 }, - {2818,2819, 0, 4, 14573, 626,0 }, - {2820,2821, 0, 4, 2200, 73,0 }, - {2822,2823, 0, 4, 373, 86,0 }, - {2824,2825, 0, 4, 12780, 200,0 }, - {2826,2827, 0, 4, 40000, 73,0 }, - {2828,2829, 0, 4, 9193, 146,0 }, - {2830,2831, 0, 4, 2540, 326,0 }, - {2832,2833, 0, 4, 6933, 200,0 }, - {2834,2835, 0, 4, 40000, 413,0 }, - {2836,2837, 0, 4, 4826, 1313,0 }, - {2838,2839, 0, 4, 14740, 340,0 }, - {2840,2841, 0, 4, 1886, 653,0 }, - {2842,2843, 0, 4, 5280, 260,0 }, - {2844,2845, 0, 4, 40000, 240,0 }, - {2846,2847, 0, 4, 40000, 240,0 }, - {2848,2849, 0, 4, 40000, 240,0 }, - {2850,2851, 0, 4, 40000, 406,0 }, - {2852,2853, 0, 4, 40000, 406,0 }, - {2854,2855, 0, 4, 40000, 146,0 }, - {2856,2856, 0, 0, 2400, 1126,0 }, - {2857,2857, 0, 0, 2400, 1126,0 }, - {2858,2859, 0, 4, 4613, 73,0 }, - {2860,2861, 0, 4, 40000, 426,0 }, - {2862,2863, 0, 4, 4580, 100,0 }, - {2864,2865, 0, 4, 40000, 80,0 }, - {2866,2867, 0, 4, 5300, 53,0 }, - {2868,2869, 0, 4, 5313, 113,0 }, - {2870,2871, 0, 4, 7080, 186,0 }, - {2872,2873, 0, 4, 4720, 106,0 }, - {2874,2875, 0, 4, 40000, 73,0 }, - {2876,2877, 0, 4, 1640, 0,0 }, - {2878,2879, 0, 4, 7306, 186,0 }, - {2880,2881, 0, 4, 7373, 1246,0 }, - {2882,2883, 0, 4, 4620, 93,0 }, - {2884,2885, 0, 4, 3460, 926,0 }, - {2886,2887, 0, 4, 40000, 73,0 }, - {2888,2888, 0, 0, 18926, 426,0 }, - {2889,2889, 0, 0, 18520, 73,0 }, - {2890,2890, 0, 0, 18473, 73,0 }, - {2891,2892, 0, 4, 40000, 93,0 }, - {2893,2893, 0, 0, 8006, 133,0 }, - {2894,2894, 0, 0, 18533, 66,0 }, - {2895,2895, 0, 0, 14786, 4966,0 }, - {2896,2897, 0, 4, 40000, 80,0 }, - {2898,2899, 0, 4, 40000, 73,0 }, - {2353,2900, 0, 4, 18520, 86,0 }, - {2901,2901, 0, 0, 40000, 0,0 }, - {2902,2903, 0, 4, 40000, 100,0 }, - {2904,2905, 0, 4, 40000, 93,0 }, - {2906,2907, 0, 4, 40000, 73,0 }, - {2908,2909, 0, 4, 10720, 153,0 }, - {2910,2911, 0, 4, 40000, 73,0 }, - {2912,2912, 0, 0, 40000, 40,0 }, - {2913,2914, 0, 4, 8720, 446,0 }, - {2915,2916, 0, 4, 14706, 653,0 }, - {2917,2918, 0, 4, 9213, 426,0 }, - {2919,2920, 0, 4, 9286, 240,0 }, - {2921,2922, 0, 4, 8706, 413,0 }, - {2923,2924, 0, 4, 2233, 346,0 }, - {2925,2926, 0, 4, 2373, 426,0 }, - {2927,2928, 0, 4, 2353, 233,0 }, - {2929,2929, 0, 0, 40000, 140,0 }, - {2930,2931, 0, 4, 40000, 100,0 }, - {2932,2933, 0, 4, 40000, 73,0 }, - {2934,2935, 0, 4, 40000, 80,0 }, - {2936,2937, 0, 4, 40000, 80,0 }, - {2938,2939, 0, 4, 40000, 246,0 }, - {2940,2940, 0, 0, 553, 446,0 }, - {2941,2941, 0, 0, 40000, 193,0 }, - {2942,2943, 0, 4, 1206, 406,0 }, - {2944,2944, 0, 0, 7026, 1553,0 }, - {2945,2945, 0, 0, 3426, 360,0 }, - {2946,2947, 0, 4, 7313, 646,0 }, - {2948,2948, 0, 0, 40000, 386,0 }, - {2949,2949, 0, 0, 1953, 726,0 }, - {2950,2951, 0, 4, 14606, 106,0 }, - {2952,2953, 0, 4, 40000, 1566,0 }, - {2954,2954, 60, 2, 6, 0,0 }, - {2955,2956, 0, 4, 40000, 240,0 }, - {2957,2958, 0, 4, 40000, 80,0 }, - {2959,2960, 0, 4, 40000, 113,0 }, - {2961,2962, 0, 4, 40000, 240,0 }, - {2963,2963, 0, 0, 8506, 680,0 }, - {2964,2964, 0, 0, 40000, 1593,0 }, - {2436,2436, 49, 0, 1873, 633,0 }, - {2357,2357, 61, 0, 113, 20,0 }, - {2357,2357, 56, 0, 113, 26,0 }, - {2357,2357, 58, 0, 113, 26,0 }, - {2357,2357, 49, 0, 126, 26,0 }, - {2357,2357, 44, 0, 126, 26,0 }, - {2965,2965, 0, 0, 40000, 380,0 }, - {2966,2966, 0, 0, 4440, 66,0 }, - {2967,2967, 0, 0, 8133, 1433,0 }, - {2968,2968, 0, 0, 40000, 126,0 }, - {2969,2969, 0, 0, 40000, 0,0 }, - {2970,2970, 84, 0, 160, 26,0 }, - {2971,2971, 72, 0, 440, 180,0 }, - {2972,2972, 0, 0, 8313, 580,0 }, - {2973,2973, 0, 0, 40000, 160,0 }, - {2974,2974, 0, 0, 40000, 3000,0 }, - {2975,2975, 0, 0, 8300, 493,0 }, - {2976,2976, 0, 0, 973, 673,0 }, - {2977,2977, 0, 0, 40000, 73,0 }, - {2978,2978, 0, 0, 40000, 133,0 }, - {2979,2979, 0, 0, 40000, 140,0 }, - {2980,2980, 0, 0, 40000, 346,0 }, - {2981,2981, 0, 0, 40000, 1006,0 }, - {2982,2982, 0, 0, 40000, 966,0 }, - {2983,2983, 0, 0, 40000, 0,0 }, - {2984,2984, 0, 0, 40000, 0,0 }, - {2985,2985, 0, 0, 40000, 66,0 }, - {2986,2986, 0, 0, 40000, 66,0 }, - {2987,2987, 0, 0, 40000, 46,0 }, - {2988,2988, 0, 0, 40000, 533,0 }, - {2989,2989, 0, 0, 2400, 780,0 }, - {2990,2990, 0, 0, 820, 66,0 }, - {2991,2991, 0, 0, 40000, 240,0 }, - {2992,2992, 0, 0, 40000, 220,0 }, - {2993,2993, 0, 0, 40000, 0,0 }, - {2994,2994, 0, 0, 15100, 73,0 }, - {2995,2995, 0, 0, 40000, 200,0 }, - {2996,2996, 0, 0, 2426, 93,0 }, - {2997,2997, 0, 0, 4640, 1553,0 }, - {2998,2998, 0, 0, 40000, 73,0 }, - {2999,2999, 0, 0, 40000, 73,0 }, - {3000,3000, 0, 0, 1133, 633,0 }, - {3001,3001, 0, 0, 40000, 0,0 }, - {3002,3002, 0, 0, 40000, 1006,0 }, - {3003,3003, 0, 0, 4653, 653,0 }, - {3004,3004, 0, 0, 40000, 1000,0 }, - {3005,3005, 0, 0, 40000, 53,0 }, - {3006,3006, 0, 0, 40000, 60,0 }, - {3007,3007, 0, 0, 40000, 0,0 }, - { 350, 350, 0, 0, 513, 200,0 }, - {3008,3008, 0, 0, 213, 106,0 }, - {3009,3009, 0, 0, 280, 126,0 }, - {3010,3010, 0, 0, 1193, 426,0 }, - {3011,3011, 0, 0, 14653, 4906,0 }, - {3012,3012, 0, 0, 1040, 326,0 }, - {3013,3013, 0, 0, 5740, 2326,0 }, - {3014,3014, 0, 0, 40000, 73,0 }, - {3015,3015, 0, 0, 40000, 240,0 }, - { 350, 350, 36, 0, 380, 153,0 }, - { 369, 369, 37, 0, 213, 66,0 }, - {3008,3008, 38, 0, 213, 106,0 }, - { 369, 369, 24, 0, 193, 13,0 }, - {3008,3008, 32, 0, 206, 106,0 }, - { 369, 369, 48, 0, 186, 20,0 }, - {3009,3009, 42, 0, 220, 106,0 }, - { 369, 369, 50, 0, 186, 73,0 }, - { 369, 369, 52, 0, 186, 73,0 }, - { 369, 369, 54, 0, 186, 33,0 }, - { 369, 369, 55, 0, 186, 33,0 }, - { 369, 369, 57, 0, 180, 33,0 }, - {3010,3010, 51, 0, 966, 353,0 }, - { 144, 144, 61, 0, 213, 126,0 }, - {3016,3016, 0, 0, 8340, 520,0 }, - {3016,3016, 63, 0, 6106, 373,0 }, - {3016,3016, 64, 0, 6073, 380,0 }, - {3017,3017, 40, 0, 206, 100,0 }, - {3017,3017, 70, 0, 160, 93,0 }, - {3018,3018, 0, 0, 40000, 73,0 }, - {3019,3019, 0, 0, 40000, 73,0 }, - {3020,3020, 0, 0, 40000, 73,0 }, - {3021,3021, 0, 0, 40000, 73,0 }, - {3022,3022, 38, 0, 246, 33,0 }, - {2441,2441, 57, 0, 286, 126,0 }, - {3023,3023, 63, 0, 146, 126,0 }, - {3024,3024, 74, 0, 280, 73,0 }, - {3025,3025, 74, 0, 453, 100,0 }, - {3026,3026, 60, 0, 666, 33,0 }, - {1439,1440, 0, 0, 13566, 273,0 }, - {1593,1594, 35, 0, 2200, 673,0 }, - {1564,1565, 35, 0, 740, 280,0 }, - {1443,1444, 0, 0, 11886, 333,0 }, - {1481,1482, 0, 0, 40000, 133,0 }, - { 185, 186, 0, 0, 5980, 1540,0 }, - { 235, 237, 0, 0, 3366, 1093,0 }, - { 239, 240, 0, 0, 40000, 133,0 }, - {1477,1476, 0, 0, 40000, 160,0 }, - { 268, 269, 0, 0, 40000, 80,0 }, - { 176, 177, 0, 0, 40000, 0,0 }, - {1490,1491, 0, 0, 40000, 60,0 }, - { 231, 232, 0, 0, 40000, 146,0 }, - { 233, 234, 0, 0, 40000, 433,0 }, - { 254, 255, 0, 0, 40000, 93,0 }, - { 192, 193, 0, 0, 40000, 66,0 }, - { 252, 253, 0, 0, 40000, 73,0 }, - { 248,3027, 0, 0, 40000, 80,0 }, - { 39,1476, 0, 0, 40000, 160,0 }, - { 241, 242, 0, 0, 40000, 146,0 }, - {1508,1509, 0, 0, 40000, 0,0 }, - { 246, 247, 0, 0, 3966, 800,0 }, - { 181,1451, 0, 0, 2153, 640,0 }, - { 209, 210, 0, 0, 4453, 100,0 }, - { 270, 271, 0, 0, 40000, 80,0 }, - { 115,1533, 0, 0, 4260, 1720,0 }, - {1454,1455, 0, 0, 40000, 0,0 }, - { 107, 319, 0, 0, 1266, 413,0 }, - { 46, 238, 0, 0, 6873, 1246,0 }, - { 216, 217, 0, 0, 4046, 100,0 }, - { 272, 273, 0, 0, 40000, 126,0 }, - {1445,3028, 0, 0, 9966, 386,0 }, - { 172, 173, 0, 0, 7340, 100,0 }, - { 174, 175, 0, 0, 6913, 100,0 }, - {1447,3029, 0, 0, 10306, 80,0 }, - {1452,3030, 0, 0, 9240, 240,0 }, - { 183, 184, 0, 0, 586, 253,0 }, - {1456,1457, 0, 0, 1386, 180,0 }, - {1458,1459, 0, 0, 40000, 60,0 }, - { 190,1460, 0, 0, 40000, 46,0 }, - {1462,1463, 0, 0, 40000, 340,0 }, - {1464,1465, 0, 0, 40000, 360,0 }, - { 195, 196, 0, 0, 40000, 66,0 }, - { 197, 198, 0, 0, 40000, 86,0 }, - { 199, 200, 0, 0, 40000, 60,0 }, - { 201, 202, 0, 0, 3713, 100,0 }, - { 203, 204, 0, 0, 14633, 126,0 }, - { 205, 206, 0, 0, 9440, 153,0 }, - { 214, 215, 0, 0, 17020, 100,0 }, - { 218, 219, 0, 0, 14000, 180,0 }, - { 220, 221, 0, 0, 2846, 100,0 }, - {1472,1473, 0, 0, 8066, 66,0 }, - {1474,1475, 0, 0, 8040, 93,0 }, - { 225, 226, 0, 0, 8066, 106,0 }, - { 229, 230, 0, 0, 40000, 160,0 }, - {1478,1479, 0, 0, 40000, 413,0 }, - { 50,1480, 0, 0, 40000, 393,0 }, - {1485,1486, 0, 0, 40000, 226,0 }, - { 258, 259, 0, 0, 40000, 73,0 }, - { 262, 263, 0, 0, 40000, 160,0 }, - { 264, 265, 0, 0, 40000, 160,0 }, - {1494,1495, 0, 0, 40000, 80,0 }, - {1496,1497, 0, 0, 40000, 73,0 }, - { 274, 275, 0, 0, 40000, 100,0 }, - {1499,1500, 0, 0, 40000, 73,0 }, - { 277, 278, 0, 0, 40000, 173,0 }, - { 279, 280, 0, 0, 40000, 160,0 }, - { 281, 282, 0, 0, 40000, 173,0 }, - {1501,1502, 0, 0, 40000, 146,0 }, - { 284, 285, 0, 0, 40000, 66,0 }, - { 286, 287, 0, 0, 40000, 86,0 }, - {1503,1504, 0, 0, 40000, 86,0 }, - {1505,1506, 0, 0, 40000, 73,0 }, - { 289, 290, 0, 0, 40000, 66,0 }, - { 291, 292, 0, 0, 40000, 160,0 }, - { 293, 294, 0, 0, 40000, 200,0 }, - { 86,1507, 0, 0, 40000, 80,0 }, - { 88, 297, 0, 0, 40000, 1346,0 }, - { 298, 299, 0, 0, 40000, 320,0 }, - { 300, 301, 0, 0, 40000, 1273,0 }, - {1514,1515, 0, 0, 40000, 100,0 }, - {1518,1519, 0, 0, 4733, 0,0 }, - {1520,1521, 0, 0, 40000, 440,0 }, - {1524,1525, 0, 0, 40000, 1180,0 }, - {1526,1527, 0, 0, 40000, 746,0 }, - {1528,1529, 0, 0, 40000, 920,0 }, - { 311, 312, 0, 0, 15306, 213,0 }, - { 313, 314, 0, 0, 7280, 340,0 }, - { 315, 316, 0, 0, 3693, 346,0 }, - { 317, 318, 0, 0, 13720, 4033,0 }, - { 108, 320, 0, 0, 40000, 66,0 }, - { 109, 321, 0, 0, 40000, 180,0 }, - { 322, 323, 0, 0, 40000, 73,0 }, - { 111,1530, 0, 0, 4053, 426,0 }, - { 324, 325, 0, 0, 626, 260,0 }, - { 326, 327, 0, 0, 1166, 400,0 }, - {1531,1532, 0, 0, 186, 340,0 }, - {1534,1535, 0, 0, 3240, 440,0 }, - { 330, 331, 0, 0, 1920, 360,0 }, - {1536,1537, 0, 0, 3020, 0,0 }, - {1538,1539, 0, 0, 1660, 846,0 }, - {1541, 339, 0, 0, 9213, 813,0 }, - {1542,1543, 0, 0, 993, 100,0 }, - {1544,3031, 0, 0, 860, 180,0 }, - {1546,3032, 0, 0, 40000, 80,0 }, - { 338, 339, 0, 0, 40000, 200,0 }, - { 340, 341, 0, 0, 40000, 0,0 }, - {1441,1442, 0, 0, 7393, 186,0 }, - { 207, 208, 0, 0, 14373, 126,0 }, - {1466,1467, 0, 0, 40000, 66,0 }, - {1468,1469, 0, 0, 40000, 46,0 }, - { 179, 180, 0, 0, 4080, 346,0 }, - {1449,1450, 0, 0, 8313, 3373,0 }, - { 35,1470, 0, 0, 40000, 0,0 }, - { 36,1471, 0, 0, 8093, 40,0 }, - { 235, 236, 0, 0, 2393, 333,0 }, - {1483,1484, 0, 0, 40000, 0,0 }, - { 55,1487, 0, 0, 40000, 80,0 }, - {1488,1489, 0, 0, 40000, 80,0 }, - {1492,1493, 0, 0, 40000, 66,0 }, - { 256, 257, 0, 0, 40000, 53,0 }, - { 260, 261, 0, 0, 40000, 86,0 }, - {1512,1513, 0, 0, 40000, 40,0 }, - {1510,1511, 0, 0, 40000, 80,0 }, - {1496,1498, 0, 0, 40000, 73,0 }, - { 295, 296, 0, 0, 40000, 340,0 }, - {1540, 339, 0, 0, 2466, 633,0 }, - { 398, 399, 35, 0, 1860, 226,0 }, - {1516,1517, 0, 0, 40000, 660,0 }, - {1550,3033, 35, 0, 213, 26,0 }, - {1556,1557, 35, 0, 1200, 426,0 }, - {1558,1559, 35, 0, 1173, 406,0 }, - {1570,1571, 35, 0, 1160, 140,0 }, - {1608,1609, 35, 0, 2100, 320,0 }, - {1595,1596, 35, 0, 220, 273,0 }, - { 159,1597, 35, 0, 220, 266,0 }, - {1610,1611, 35, 0, 220, 273,0 }, - { 397,1588, 35, 0, 253, 86,0 }, - {1606,1607, 35, 0, 133, 66,0 }, - { 145,1576, 35, 0, 180, 0,0 }, - {1612,1613, 35, 0, 526, 400,0 }, - {1577,1578, 35, 0, 506, 453,0 }, - {1614,1615, 35, 0, 773, 933,0 }, - { 305, 306, 0, 0, 40000, 1160,0 }, - {1522,1523, 0, 0, 40000, 0,0 }, - {1550,1551, 35, 0, 526, 146,0 }, - { 364, 365, 35, 0, 186, 20,0 }, - { 129,1549, 35, 0, 326, 133,0 }, - { 132,1552, 35, 0, 226, 113,0 }, - {1553,1554, 35, 0, 206, 73,0 }, - { 129,1548, 35, 0, 326, 133,0 }, - { 134,1555, 35, 0, 2580, 893,0 }, - {1560,1561, 35, 0, 600, 366,0 }, - {1562,1563, 35, 0, 40000, 0,0 }, - {1572,1573, 35, 0, 340, 133,0 }, - {1574,1575, 35, 0, 406, 226,0 }, - {1581,1582, 35, 0, 640, 213,0 }, - { 149,1583, 35, 0, 326, 20,0 }, - {1584,1585, 35, 0, 633, 240,0 }, - {1591,1592, 35, 0, 1226, 413,0 }, - {1568,1569, 35, 0, 1313, 460,0 }, - {1579,1580, 35, 0, 326, 0,0 }, - {1586,1587, 35, 0, 606, 220,0 }, - {1589,1590, 35, 0, 120, 86,0 }, - {1600,1601, 35, 0, 1326, 420,0 }, - {1602,1603, 35, 0, 706, 266,0 }, - {1604,1605, 35, 0, 4540, 1326,0 }, - { 391, 392, 35, 0, 360, 153,0 }, - { 391, 393, 35, 0, 453, 153,0 }, - {1598,1599, 35, 0, 1246, 346,0 }, - { 367, 368, 35, 0, 600, 153,0 }, - { 380, 381, 35, 0, 40000, 0,0 }, - { 374, 375, 35, 0, 40000, 0,0 }, - {1566,1567, 35, 0, 40000, 0,0 }, - {2306,2307, 35, 0, 7660, 1560,0 }, - {3034, 339, 35, 0, 5860, 426,0 }, - {2301,2302, 35, 0, 2146, 753,0 }, - {2305,1548, 35, 0, 326, 133,0 }, - {1595,1595, 35, 0, 220, 273,0 }, - {2303,2304, 35, 0, 186, 20,0 }, - {1560,2308, 35, 0, 600, 373,0 }, - {2309,2310, 35, 0, 40000, 0,0 }, - {1568,1568, 35, 0, 1280, 453,0 }, - {2311,2312, 35, 0, 360, 106,0 }, - {2313,2314, 35, 0, 620, 0,0 }, - {2315,2315, 35, 0, 106, 0,0 }, - {2316,2316, 35, 0, 506, 453,0 }, - {1612,1612, 35, 0, 526, 400,0 }, - {2317,2317, 35, 0, 640, 253,0 }, - {2318,2318, 35, 0, 326, 20,0 }, - {2319,2319, 35, 0, 453, 446,0 }, - {2320,2320, 35, 0, 466, 453,0 }, - {2321,2321, 35, 0, 120, 26,0 }, - {2322,2322, 35, 0, 1220, 406,0 }, - {2323,2323, 35, 0, 2360, 786,0 }, - {2324, 392, 35, 0, 353, 146,0 }, - {2324, 393, 35, 0, 453, 146,0 }, - {2325,2326, 35, 0, 533, 140,0 }, - {2327,2328, 35, 0, 1273, 393,0 }, - {2329,2330, 35, 0, 40000, 0,0 }, - {2331,2332, 35, 0, 40000, 0,0 }, - {3035,3036, 35, 0, 4133, 433,0 }, - {3037,3038, 35, 0, 1740, 286,0 }, - {1564,1564, 35, 0, 713, 273,0 }, - {3039,3039, 0, 0, 40000, 0,0 }, - {3040,3040, 0, 0, 6100, 146,0 }, - {3041,3041, 0, 0, 2386, 26,0 }, - {3042,3042, 0, 0, 4320, 80,0 }, - {3043,3043, 0, 0, 3433, 313,0 }, - {3044,3044, 0, 0, 6620, 2446,0 }, - {3045,3045, 0, 0, 3726, 1253,0 }, - {3046,3046, 0, 0, 40000, 133,0 }, - {3047,3047, 0, 0, 4566, 1253,0 }, - {3048,3048, 0, 0, 40000, 813,0 }, - {3049,3049, 0, 0, 18513, 1560,0 }, - {3050,3050, 0, 0, 2186, 426,0 }, - {3051,3051, 0, 0, 1186, 420,0 }, - {3052,3052, 0, 0, 766, 420,0 }, - {3053,3053, 0, 0, 14513, 4713,0 }, - {3054,3054, 0, 0, 15493, 1580,0 }, - {3055,3055, 0, 0, 40000, 66,0 }, - {3056,3056, 0, 0, 40000, 60,0 }, - {3057,3057, 0, 0, 4740, 100,0 }, - {3058,3058, 0, 0, 40000, 66,0 }, - {3059,3059, 0, 0, 40000, 73,0 }, - {3060,3060, 0, 0, 40000, 73,0 }, - {3061,3061, 0, 0, 40000, 0,0 }, - {3062,3062, 0, 0, 8373, 633,0 }, - {3063,3063, 0, 0, 7560, 133,0 }, - {3064,3064, 0, 0, 40000, 0,0 }, - {3065,3065, 0, 0, 40000, 86,0 }, - {3066,3066, 0, 0, 340, 140,0 }, - {3067,3067, 0, 0, 40000, 0,0 }, - {3068,3068, 0, 0, 40000, 166,0 }, - {3069,3069, 0, 0, 4280, 1466,0 }, - {3070,3070, 0, 0, 2193, 73,0 }, - {3071,3071, 0, 0, 4846, 100,0 }, - {3072,3072, 0, 0, 12740, 93,0 }, - {3073,3073, 0, 0, 6953, 200,0 }, - {3074,3074, 0, 0, 13780, 73,0 }, - {3075,3075, 0, 0, 40000, 73,0 }, - {3076,3076, 0, 0, 5860, 600,0 }, - {3077,3077, 0, 0, 2206, 73,0 }, - {3078,3078, 0, 0, 40000, 140,0 }, - {3079,3079, 0, 0, 40000, 53,0 }, - {3080,3080, 0, 0, 40000, 120,0 }, - {3081,3081, 0, 0, 40000, 140,0 }, - {3082,3082, 0, 0, 40000, 126,0 }, - {3083,3083, 0, 0, 360, 140,0 }, - {3084,3084, 0, 0, 8880, 1373,0 }, - {3085,3085, 0, 0, 593, 73,0 }, - {3086,3086, 0, 0, 40000, 193,0 }, - {3087,3087, 0, 0, 40000, 200,0 }, - {3088,3088, 0, 0, 40000, 160,0 }, - {3089,3089, 0, 0, 40000, 200,0 }, - {3090,3090, 0, 0, 40000, 53,0 }, - {3091,3091, 0, 0, 40000, 73,0 }, - {3092,3092, 0, 0, 40000, 73,0 }, - {3093,3093, 0, 0, 760, 213,0 }, - {3094,3094, 0, 0, 40000, 133,0 }, - {3095,3095, 0, 0, 40000, 220,0 }, - {3096,3096, 0, 0, 40000, 100,0 }, - {3097,3097, 0, 0, 40000, 73,0 }, - {3098,3098, 0, 0, 40000, 140,0 }, - {3099,3099, 0, 0, 40000, 140,0 }, - {3100,3100, 0, 0, 40000, 140,0 }, - {3101,3101, 0, 0, 40000, 73,0 }, - {3102,3102, 0, 0, 40000, 73,0 }, - {3103,3103, 0, 0, 40000, 73,0 }, - {3104,3104, 0, 0, 40000, 73,0 }, - {3105,3105, 0, 0, 40000, 66,0 }, - {3106,3106, 0, 0, 40000, 66,0 }, - {3107,3107, 0, 0, 40000, 73,0 }, - {3108,3108, 0, 0, 40000, 73,0 }, - {3109,3109, 0, 0, 40000, 73,0 }, - {3110,3110, 0, 0, 40000, 73,0 }, - {3111,3111, 0, 0, 40000, 86,0 }, - {3112,3112, 0, 0, 5393, 100,0 }, - {3113,3113, 0, 0, 40000, 60,0 }, - {3114,3114, 0, 0, 18500, 73,0 }, - {3115,3115, 0, 0, 40000, 93,0 }, - {3116,3116, 0, 0, 40000, 86,0 }, - {3117,3117, 0, 0, 40000, 173,0 }, - {3118,3118, 0, 0, 40000, 1353,0 }, - {3119,3119, 0, 0, 17506, 73,0 }, - {3120,3120, 0, 0, 40000, 100,0 }, - {3121,3121, 0, 0, 40000, 73,0 }, - {3122,3122, 0, 0, 5620, 193,0 }, - {3123,3123, 0, 0, 3700, 80,0 }, - {3124,3124, 0, 0, 40000, 66,0 }, - {3125,3125, 0, 0, 2740, 80,0 }, - {3126,3126, 0, 0, 8333, 173,0 }, - {3127,3127, 0, 0, 2226, 466,0 }, - {3128,3128, 0, 0, 340, 146,0 }, - {3129,3129, 0, 0, 19980, 6280,0 }, - {3130,3130, 0, 0, 353, 73,0 }, - {3131,3131, 35, 0, 566, 233,0 }, - {3132,3132, 35, 0, 226, 46,0 }, - {3133,3133, 35, 0, 40000, 100,0 }, - {3134,3134, 35, 0, 40000, 100,0 }, - {3135,3135, 35, 0, 360, 146,0 }, - {3061,3061, 35, 0, 40000, 0,0 }, - {3136,3136, 35, 0, 366, 20,0 }, - { 739, 739, 35, 0, 246, 20,0 }, - {3137,3137, 35, 0, 333, 33,0 }, - {3138,3138, 35, 0, 420, 166,0 }, - {3139,3139, 35, 0, 626, 240,0 }, - {3140,3140, 35, 0, 233, 100,0 }, - {3141,3141, 35, 0, 1166, 440,0 }, - {3142,3142, 35, 0, 166, 66,0 }, - {3143,3143, 35, 0, 1166, 440,0 }, - {3144,3144, 35, 0, 813, 100,0 }, - {3145,3145, 35, 0, 1040, 440,0 }, - {3146,3146, 35, 0, 40000, 0,0 }, - {3147,3147, 35, 0, 40000, 0,0 }, - {3148,3148, 35, 0, 180, 40,0 }, - {3149,3149, 35, 0, 40000, 0,0 }, - {3150,3150, 0, 0, 40000, 0,0 }, - {3151,3151, 0, 0, 4900, 240,0 }, - {3152,3152, 0, 0, 3480, 80,0 }, - {3153,3153, 0, 0, 3586, 86,0 }, - {3154,3154, 0, 0, 4626, 633,0 }, - {3155,3155, 0, 0, 4293, 2286,0 }, - {3156,3156, 0, 0, 13653, 0,0 }, - {3157,3157, 0, 0, 1206, 426,0 }, - {3158,3158, 0, 0, 653, 426,0 }, - {3159,3159, 0, 0, 40000, 0,0 }, - {3160,3160, 0, 0, 4633, 633,0 }, - {3161,3161, 0, 0, 40000, 73,0 }, - {3162,3162, 0, 0, 40000, 60,0 }, - {3163,3163, 0, 0, 40000, 146,0 }, - {3164,3164, 0, 0, 40000, 73,0 }, - {3165,3165, 0, 0, 40000, 73,0 }, - {3166,3166, 0, 0, 40000, 0,0 }, - {3167,3167, 0, 0, 40000, 66,0 }, - {3168,3168, 0, 0, 3680, 1180,0 }, - {3169,3169, 0, 0, 2406, 846,0 }, - {3170,3170, 0, 0, 1560, 73,0 }, - {3171,3171, 0, 0, 1946, 226,0 }, - {3172,3172, 0, 0, 4333, 13,0 }, - {3173,3173, 0, 0, 40000, 0,0 }, - {3174,3174, 0, 0, 40000, 0,0 }, - {3175,3175, 0, 0, 40000, 66,0 }, - {3176,3176, 0, 0, 40000, 180,0 }, - {3177,3177, 0, 0, 15380, 80,0 }, - {3178,3178, 0, 0, 18213, 73,0 }, - {3179,3179, 0, 0, 1706, 0,0 }, - {3180,3180, 0, 0, 5733, 1266,0 }, - {3181,3181, 0, 0, 40000, 0,0 }, - {3182,3182, 0, 0, 40000, 366,0 }, - {3183,3183, 0, 0, 40000, 66,0 }, - {3184,3184, 0, 0, 4786, 73,0 }, - {3185,3185, 0, 0, 5660, 720,0 }, - {3186,3186, 0, 0, 1293, 406,0 }, - {3187,3187, 0, 0, 40000, 0,0 }, - {3188,3188, 0, 0, 2686, 233,0 }, - {3189,3189, 0, 0, 40000, 0,0 }, - {3190,3190, 0, 0, 40000, 73,0 }, - {3191,3191, 0, 0, 40000, 0,0 }, - {3192,3192, 0, 0, 40000, 73,0 }, - {3193,3193, 0, 0, 40000, 0,0 }, - {3194,3194, 0, 0, 3920, 73,0 }, - {3195,3195, 0, 0, 40000, 73,0 }, - {3196,3196, 0, 0, 40000, 66,0 }, - {3197,3197, 0, 0, 40000, 80,0 }, - {3198,3198, 0, 0, 40000, 86,0 }, - {3199,3199, 0, 0, 40000, 60,0 }, - {3200,3200, 0, 0, 40000, 0,0 }, - {3201,3201, 0, 0, 40000, 353,0 }, - {3202,3202, 0, 0, 3920, 73,0 }, - {3203,3203, 0, 0, 5833, 813,0 }, - {3204,3204, 0, 0, 40000, 60,0 }, - {3205,3205, 0, 0, 40000, 73,0 }, - {3206,3206, 0, 0, 1400, 406,0 }, - {3207,3207, 0, 0, 40000, 66,0 }, - {3208,3208, 0, 0, 9066, 2220,0 }, - {3209,3209, 0, 0, 1473, 773,0 }, - {3210,3210, 0, 0, 40000, 120,0 }, - {3211,3211, 0, 0, 40000, 306,0 }, - {3212,3212, 0, 0, 9306, 3013,0 }, - {3213,3213, 0, 0, 40000, 60,0 }, - {3214,3214, 0, 0, 40000, 73,0 }, - {3215,3215, 0, 0, 40000, 73,0 }, - {3216,3216, 0, 0, 40000, 453,0 }, - {3217,3217, 0, 0, 40000, 3460,0 }, - {3218,3218, 0, 0, 40000, 453,0 }, - {3219,3219, 0, 0, 40000, 40,0 }, - {3220,3220, 0, 0, 40000, 3926,0 }, - {3221,3221, 0, 0, 40000, 4506,0 }, - {3222,3222, 0, 0, 4646, 646,0 }, - {3223,3223, 0, 0, 773, 100,0 }, - {3224,3224, 0, 0, 40000, 73,0 }, - {3225,3225, 0, 0, 40000, 173,0 }, - {3226,3226, 0, 0, 1606, 653,0 }, - {3227,3227, 0, 0, 2353, 806,0 }, - {3228,3228, 0, 0, 980, 360,0 }, - {3229,3229, 0, 0, 1193, 413,0 }, - { 499, 499, 0, 0, 266, 0,0 }, - {3230,3230, 0, 0, 973, 360,0 }, - {3231,3231, 0, 0, 273, 53,0 }, - {3232,3232, 0, 0, 726, 220,0 }, - {3233,3233, 0, 0, 19933, 6093,0 }, - {3234,3234, 0, 0, 40000, 0,0 }, - { 403, 403, 0, 0, 40000, 73,0 }, - {3235,3235, 0, 0, 4966, 233,0 }, - {3236,3236, 0, 0, 4946, 240,0 }, - {3237,3237, 0, 0, 4946, 233,0 }, - {3238,3238, 0, 0, 4640, 1613,0 }, - {3239,3239, 0, 0, 2360, 806,0 }, - {3240,3240, 0, 0, 4466, 200,0 }, - {3241,3241, 0, 0, 40000, 73,0 }, - {3242,3242, 0, 0, 40000, 73,0 }, - {3243,3243, 0, 0, 40000, 73,0 }, - {3244,3244, 0, 0, 40000, 73,0 }, - {3245,3245, 0, 0, 40000, 240,0 }, - {3246,3246, 0, 0, 40000, 226,0 }, - {3247,3247, 0, 0, 40000, 233,0 }, - {3248,3248, 0, 0, 40000, 240,0 }, - {3249,3249, 0, 0, 4306, 1253,0 }, - {3250,3250, 0, 0, 3873, 1206,0 }, - {3251,3251, 0, 0, 4640, 633,0 }, - {3252,3252, 0, 0, 1233, 80,0 }, - {3253,3253, 0, 0, 1233, 26,0 }, - {3254,3254, 0, 0, 1233, 26,0 }, - {3255,3255, 0, 0, 4573, 1253,0 }, - {3256,3256, 0, 0, 3793, 1240,0 }, - {3257,3257, 0, 0, 40000, 73,0 }, - {3258,3258, 0, 0, 40000, 73,0 }, - {3259,3259, 0, 0, 40000, 140,0 }, - {3260,3260, 0, 0, 40000, 146,0 }, - {3261,3261, 0, 0, 40000, 80,0 }, - {3262,3262, 0, 0, 5953, 200,0 }, - {3263,3263, 0, 0, 5926, 200,0 }, - {3264,3264, 0, 0, 5866, 26,0 }, - {3265,3265, 0, 0, 18573, 6153,0 }, - {3266,3266, 0, 0, 40000, 2093,0 }, - {3267,3267, 0, 0, 40000, 73,0 }, - {3268,3268, 0, 0, 18626, 1553,0 }, - {3269,3269, 0, 0, 40000, 1820,0 }, - {3270,3270, 0, 0, 40000, 500,0 }, - {3271,3271, 0, 0, 18206, 5900,0 }, - {3272,3272, 0, 0, 14200, 93,0 }, - {3273,3273, 0, 0, 40000, 2873,0 }, - {3274,3274, 0, 0, 14960, 4913,0 }, - {3275,3275, 0, 0, 40000, 86,0 }, - {3276,3276, 0, 0, 40000, 826,0 }, - {3277,3277, 0, 0, 40000, 200,0 }, - {3278,3278, 0, 0, 40000, 340,0 }, - {3279,3279, 0, 0, 13220, 2500,0 }, - {3280,3280, 0, 0, 40000, 100,0 }, - {3281,3281, 0, 0, 40000, 1026,0 }, - {3282,3282, 0, 0, 40000, 366,0 }, - {3283,3283, 0, 0, 40000, 386,0 }, - {3284,3284, 0, 0, 40000, 0,0 }, - {3285,3285, 0, 0, 40000, 0,0 }, - {3286,3286, 0, 0, 40000, 140,0 }, - {3287,3287, 0, 0, 40000, 53,0 }, - {3288,3288, 0, 0, 40000, 120,0 }, - {3289,3289, 0, 0, 8866, 1366,0 }, - {3290,3290, 0, 0, 4193, 1400,0 }, - {3291,3291, 0, 0, 8353, 673,0 }, - {3292,3292, 0, 0, 8353, 673,0 }, - {3293,3293, 0, 0, 8400, 593,0 }, - {3294,3294, 0, 0, 8440, 666,0 }, - {3295,3295, 0, 0, 9600, 1580,0 }, - {3296,3296, 0, 0, 40000, 46,0 }, - {3297,3297, 0, 0, 40000, 0,0 }, - {3298,3298, 0, 0, 1653, 93,0 }, - {3299,3299, 0, 0, 2706, 73,0 }, - {3300,3300, 0, 0, 11680, 26,0 }, - {3301,3301, 0, 0, 6500, 340,0 }, - {3302,3302, 0, 0, 40000, 0,0 }, - {3303,3303, 0, 0, 40000, 0,0 }, - {3304,3304, 0, 0, 40000, 73,0 }, - {3305,3305, 0, 0, 40000, 73,0 }, - {3306,3306, 0, 0, 40000, 73,0 }, - {3307,3307, 0, 0, 40000, 73,0 }, - {3308,3308, 0, 0, 40000, 73,0 }, - {3309,3309, 0, 0, 40000, 73,0 }, - {3310,3310, 0, 0, 40000, 73,0 }, - {3311,3311, 0, 0, 40000, 73,0 }, - {3312,3312, 0, 0, 40000, 73,0 }, - {3313,3313, 0, 0, 40000, 133,0 }, - {3314,3314, 0, 0, 40000, 126,0 }, - {3315,3315, 0, 0, 40000, 73,0 }, - {3316,3316, 0, 0, 40000, 73,0 }, - {3317,3317, 0, 0, 40000, 73,0 }, - {3318,3318, 0, 0, 40000, 200,0 }, - {3319,3319, 0, 0, 40000, 133,0 }, - {3320,3320, 0, 0, 40000, 0,0 }, - {3321,3321, 0, 0, 40000, 240,0 }, - {3322,3322, 0, 0, 40000, 220,0 }, - {3323,3323, 0, 0, 40000, 226,0 }, - {3324,3324, 0, 0, 40000, 100,0 }, - {3325,3325, 0, 0, 40000, 140,0 }, - {3326,3326, 0, 0, 40000, 0,0 }, - {3327,3327, 0, 0, 40000, 426,0 }, - {3328,3328, 0, 0, 40000, 426,0 }, - {3329,3329, 0, 0, 3680, 1220,0 }, - {3330,3330, 0, 0, 40000, 533,0 }, - {3331,3331, 0, 0, 40000, 813,0 }, - {3332,3332, 0, 0, 14506, 4706,0 }, - {3333,3333, 0, 0, 766, 420,0 }, - {3334,3334, 0, 0, 40000, 1566,0 }, - {3335,3335, 0, 0, 40000, 120,0 }, - {3336,3336, 0, 0, 40000, 2380,0 }, - {3337,3337, 0, 0, 5666, 300,0 }, - {3338,3338, 0, 0, 40000, 73,0 }, - {3339,3339, 0, 0, 40000, 2513,0 }, - {3340,3340, 0, 0, 1260, 826,0 }, - {3341,3341, 0, 0, 2420, 413,0 }, - {3342,3342, 0, 0, 626, 240,0 }, - {3343,3343, 0, 0, 273, 60,0 }, - {3344,3344, 0, 0, 540, 20,0 }, - {3345,3345, 0, 0, 540, 20,0 }, - {3346,3346, 0, 0, 540, 20,0 }, - {3347,3347, 0, 0, 1153, 760,0 }, - {3348,3348, 0, 0, 40000, 100,0 }, - {3349,3349, 0, 0, 7326, 2380,0 }, - {3350,3350, 0, 0, 40000, 4426,0 }, - {3351,3351, 0, 0, 7413, 2493,0 }, - {3352,3352, 0, 0, 253, 20,0 }, - {3353,3353, 0, 0, 246, 33,0 }, - {3354,3354, 0, 0, 286, 13,0 }, - {3355,3355, 0, 0, 953, 13,0 }, - {3356,3356, 0, 0, 293, 20,0 }, - { 142, 142, 20, 0, 1893, 620,0 }, - {3357,1451, 0, 4, 2373, 780,0 }, - {3358,3359, 0, 4, 9260, 246,0 }, - {3360,1455, 0, 4, 40000, 0,0 }, - {3361,1463, 0, 4, 40000, 266,0 }, - { 225,3362, 0, 4, 7993, 100,0 }, - {3363,1545, 0, 4, 293, 86,0 }, - {3364,1547, 0, 4, 40000, 180,0 }, - {3365,3366, 39, 4, 66, 0,0 }, - {3367, 368, 58, 4, 173, 0,0 }, - {3368,1551, 48, 4, 520, 200,0 }, - {3368,3033, 49, 4, 53, 0,0 }, - {3368,3033, 51, 4, 53, 0,0 }, - {3368,3033, 54, 4, 60, 0,0 }, - {3368,3033, 57, 4, 60, 0,0 }, - {3368,3033, 60, 4, 60, 0,0 }, - {3369,3370, 70, 4, 840, 0,0 }, - {1564,1565, 80, 4, 220, 0,0 }, - {3371,1571, 44, 4, 420, 0,0 }, - {3372,3372, 0, 0, 8366, 666,0 }, - {3373,3373, 0, 0, 8366, 666,0 }, - {3374,3374, 0, 0, 3773, 73,0 }, - {3375,3375, 0, 0, 8366, 666,0 }, - {3376,3376, 0, 0, 4693, 26,0 }, - {3377,3377, 0, 0, 7400, 80,0 }, - {3378,3378, 0, 0, 3586, 80,0 }, - {3379,3379, 0, 0, 8366, 666,0 }, - {3380,3380, 0, 0, 3786, 1240,0 }, - {3381,3381, 0, 0, 9013, 1466,0 }, - {3382,3382, 0, 0, 1200, 73,0 }, - {3383,3383, 0, 0, 8146, 1446,0 }, - {3384,3384, 0, 0, 3660, 1206,0 }, - {3385,3385, 0, 0, 200, 100,0 }, - {3386,3386, 0, 0, 40000, 0,0 }, - {3387,3387, 0, 0, 1213, 426,0 }, - {3388,3388, 0, 0, 40000, 2573,0 }, - {3389,3389, 0, 0, 40000, 3446,0 }, - {3390,3390, 0, 0, 40000, 333,0 }, - {3391,3391, 0, 0, 40000, 73,0 }, - {3392,3392, 0, 0, 40000, 93,0 }, - {3393,3393, 0, 0, 40000, 73,0 }, - {3394,3394, 0, 0, 40000, 73,0 }, - {3395,3395, 0, 0, 40000, 73,0 }, - {3396,3396, 0, 0, 2193, 413,0 }, - {3397,3397, 0, 0, 14606, 2886,0 }, - {3398,3398, 0, 0, 10626, 4520,0 }, - {3399,3399, 0, 0, 2413, 100,0 }, - {3400,3400, 0, 0, 3593, 1140,0 }, - {3401,3401, 0, 0, 40000, 146,0 }, - {3402,3402, 0, 0, 40000, 86,0 }, - {3403,3403, 0, 0, 40000, 86,0 }, - {3404,3404, 0, 0, 9366, 106,0 }, - {3405,3405, 0, 0, 40000, 73,0 }, - {3406,3406, 0, 0, 40000, 0,0 }, - {3407,3407, 0, 0, 40000, 0,0 }, - {3408,3408, 0, 0, 1626, 400,0 }, - {3409,3409, 0, 0, 4473, 2933,0 }, - {3410,3410, 0, 0, 40000, 66,0 }, - {3411,3411, 0, 0, 40000, 0,0 }, - {3412,3412, 0, 0, 40000, 253,0 }, - {3413,3413, 0, 0, 40000, 233,0 }, - {3414,3414, 0, 0, 40000, 346,0 }, - {3415,3415, 0, 0, 1966, 26,0 }, - {3416,3416, 0, 0, 40000, 366,0 }, - {3417,3417, 0, 0, 2266, 386,0 }, - {3418,3418, 0, 0, 40000, 0,0 }, - {3419,3419, 0, 0, 2313, 766,0 }, - {3420,3420, 0, 0, 40000, 340,0 }, - {3421,3421, 0, 0, 40000, 346,0 }, - {3422,3422, 0, 0, 40000, 340,0 }, - {3423,3423, 0, 0, 40000, 353,0 }, - {3424,3424, 0, 0, 40000, 353,0 }, - {3425,3425, 0, 0, 40000, 226,0 }, - {3426,3426, 0, 0, 40000, 73,0 }, - {3427,3427, 0, 0, 940, 253,0 }, - {3428,3428, 0, 0, 40000, 73,0 }, - {3429,3429, 0, 0, 40000, 80,0 }, - {3430,3430, 0, 0, 40000, 240,0 }, - {3431,3431, 0, 0, 40000, 80,0 }, - {3432,3432, 0, 0, 40000, 73,0 }, - {3433,3433, 0, 0, 40000, 73,0 }, - {3434,3434, 0, 0, 40000, 73,0 }, - {3435,3435, 0, 0, 40000, 73,0 }, - {3436,3436, 0, 0, 40000, 73,0 }, - {3437,3437, 0, 0, 40000, 73,0 }, - {3438,3438, 0, 0, 40000, 73,0 }, - {3439,3439, 0, 0, 40000, 73,0 }, - {3440,3440, 0, 0, 40000, 73,0 }, - {3441,3441, 0, 0, 40000, 73,0 }, - {3442,3442, 0, 0, 40000, 66,0 }, - {3443,3443, 0, 0, 40000, 73,0 }, - {3444,3444, 0, 0, 40000, 80,0 }, - {3445,3445, 0, 0, 40000, 66,0 }, - {3446,3446, 0, 0, 40000, 66,0 }, - {3447,3447, 0, 0, 40000, 66,0 }, - {3448,3448, 0, 0, 40000, 66,0 }, - {3449,3449, 0, 0, 40000, 80,0 }, - {3450,3450, 0, 0, 40000, 353,0 }, - {3451,3451, 0, 0, 40000, 0,0 }, - {3452,3452, 0, 0, 18440, 100,0 }, - {3453,3453, 0, 0, 18086, 100,0 }, - {3454,3454, 0, 0, 266, 66,0 }, - {3455,3455, 0, 0, 40000, 80,0 }, - {3456,3456, 0, 0, 40000, 100,0 }, - {3457,3457, 0, 0, 40000, 80,0 }, - {3458,3458, 0, 0, 40000, 120,0 }, - {3459,3459, 0, 0, 40000, 93,0 }, - {3460,3460, 0, 0, 40000, 233,0 }, - {3461,3461, 0, 0, 40000, 0,0 }, - {3462,3462, 0, 0, 40000, 86,0 }, - {3463,3463, 0, 0, 40000, 820,0 }, - {3464,3464, 0, 0, 40000, 4986,0 }, - {3465,3465, 0, 0, 40000, 146,0 }, - {3466,3466, 0, 0, 40000, 100,0 }, - {3467,3467, 0, 0, 40000, 3346,0 }, - {3468,3468, 0, 0, 40000, 660,0 }, - {3469,3469, 0, 0, 40000, 366,0 }, - {3470,3470, 0, 0, 40000, 1480,0 }, - {3471,3471, 0, 0, 40000, 646,0 }, - {3472,3472, 0, 0, 40000, 2673,0 }, - {3473,3473, 0, 0, 40000, 2500,0 }, - {3474,3474, 0, 0, 40000, 2513,0 }, - {3475,3475, 0, 0, 40000, 66,0 }, - {3476,3476, 0, 0, 9600, 1580,0 }, - {3477,3477, 0, 0, 40000, 46,0 }, - {3478,3478, 0, 0, 10673, 100,0 }, - {3479,3479, 0, 0, 2333, 800,0 }, - {3480,3480, 0, 0, 3673, 1200,0 }, - {3481,3481, 0, 0, 40000, 73,0 }, - {3482,3482, 0, 0, 40000, 146,0 }, - {3483,3483, 0, 0, 40000, 73,0 }, - {3484,3484, 0, 0, 2266, 726,0 }, - {3485,3485, 0, 0, 333, 140,0 }, - {3486,3486, 0, 0, 2286, 746,0 }, - {3487,3487, 0, 0, 293, 126,0 }, - {3488,3488, 0, 0, 3700, 1213,0 }, - {3489,3489, 0, 0, 3773, 1186,0 }, - {3490,3490, 0, 0, 3646, 1200,0 }, - {3491,3491, 0, 0, 3020, 73,0 }, - {3492,3492, 0, 0, 786, 273,0 }, - {3493,3493, 0, 0, 40000, 146,0 }, - {3494,3494, 0, 0, 40000, 3093,0 }, - {3495,3495, 0, 0, 273, 60,0 }, - {3496,3496, 0, 0, 40000, 73,0 }, - {3497,3497, 0, 0, 40000, 73,0 }, - {3498,3498, 0, 0, 40000, 3093,0 }, - {3499,3499, 0, 0, 40000, 240,0 }, - {3500,3500, 0, 2, 6, 0,0 }, - { 739, 739, 46, 0, 220, 33,0 }, - {3501,3501, 47, 0, 973, 93,0 }, - {3502,3502, 64, 0, 126, 66,0 }, - {3503,3503, 40, 0, 340, 146,0 }, - {3504,3504, 48, 0, 100, 0,0 }, - {3505,3505, 48, 0, 286, 133,0 }, - {3506,3506, 46, 0, 466, 166,0 }, - {3507,3507,111, 0, 226, 113,0 }, - {3508,3508, 49, 0, 473, 166,0 }, - {3509,3509, 56, 0, 126, 40,0 }, - {3510,3510, 52, 0, 520, 206,0 }, - {3511,3511, 96, 0, 1346, 473,0 }, - {3510,3510, 54, 0, 513, 206,0 }, - {3512,3512, 57, 0, 973, 266,0 }, - {3513,3513, 82, 0, 1580, 553,0 }, - {3510,3510, 60, 0, 506, 200,0 }, - {3514,3514, 60, 0, 1886, 646,0 }, - {3515,3515, 92, 0, 1026, 520,0 }, - {3516,3516, 60, 0, 180, 93,0 }, - {3517,3517, 58, 0, 213, 213,0 }, - {3518,3518, 22, 0, 2300, 766,0 }, - {3519,3519, 60, 0, 1873, 653,0 }, - {3520,3520, 72, 0, 260, 93,0 }, - {3521,3521, 77, 0, 253, 93,0 }, - {3522,3522, 70, 0, 206, 93,0 }, - {3523,3523, 75, 0, 173, 93,0 }, - {3524,3524, 69, 0, 406, 113,0 }, - {3525,3525, 59, 0, 380, 160,0 }, - {3526,3526, 48, 0, 373, 40,0 }, - {3527,3527, 89, 0, 433, 180,0 }, - {3528,3528, 84, 0, 813, 180,0 }, - {3529,3529, 33, 0, 240, 53,0 }, - {3530,3530, 55, 0, 220, 86,0 }, - {3531,3531, 58, 0, 526, 200,0 }, - {3532,3532, 52, 0, 526, 193,0 }, - {3533,3533, 57, 0, 166, 80,0 }, - {3534,3534, 57, 0, 240, 100,0 }, - {3535,3535, 85, 0, 220, 113,0 }, - {3536,3536, 68, 0, 173, 93,0 }, - {3536,3536, 61, 0, 220, 113,0 }, - {3537,3537, 64, 0, 346, 53,0 }, - {3538,3538, 44, 0, 1080, 346,0 }, - {3539,3539,100, 0, 193, 20,0 }, - {3540,3540,100, 0, 793, 26,0 }, - {3541,3541, 0, 0, 14166, 320,0 }, - {3542,3542, 0, 0, 3873, 1613,0 }, - {3543,3543, 0, 0, 3586, 86,0 }, - {3544,3544, 0, 0, 7406, 2486,0 }, - {3545,3545, 0, 0, 4640, 1560,0 }, - {3546,3546, 0, 0, 446, 440,0 }, - {3547,3547, 0, 0, 9253, 3100,0 }, - {3548,3548, 0, 0, 4646, 646,0 }, - {3549,3549, 0, 0, 40000, 66,0 }, - {3550,3550, 0, 0, 40000, 73,0 }, - {3551,3551, 0, 0, 40000, 113,0 }, - {3552,3552, 0, 0, 40000, 73,0 }, - {3553,3553, 0, 0, 40000, 73,0 }, - {3554,3554, 0, 0, 40000, 0,0 }, - {3555,3555, 0, 0, 40000, 60,0 }, - {3556,3556, 0, 0, 3673, 1206,0 }, - {3557,3557, 0, 0, 3706, 1293,0 }, - {3558,3558, 0, 0, 5693, 1126,0 }, - {3559,3559, 0, 0, 2406, 846,0 }, - {3560,3560, 0, 0, 40000, 66,0 }, - {3561,3561, 0, 0, 40000, 73,0 }, - {3562,3562, 0, 0, 4333, 13,0 }, - {3563,3563, 0, 0, 3700, 66,0 }, - {3564,3564, 0, 0, 40000, 0,0 }, - {3565,3565, 0, 0, 3713, 1260,0 }, - {3566,3566, 0, 0, 1140, 126,0 }, - {3567,3567, 0, 0, 40000, 186,0 }, - {3568,3568, 0, 0, 40000, 0,0 }, - {3569,3569, 0, 0, 14400, 6,0 }, - {3570,3570, 0, 0, 14580, 66,0 }, - {3571,3571, 0, 0, 40000, 73,0 }, - {3572,3572, 0, 0, 40000, 353,0 }, - {3573,3573, 0, 0, 40000, 0,0 }, - {3574,3574, 0, 0, 40000, 173,0 }, - {3575,3575, 0, 0, 1833, 600,0 }, - {3576,3576, 0, 0, 40000, 0,0 }, - {3577,3577, 0, 0, 40000, 206,0 }, - {3578,3578, 0, 0, 40000, 46,0 }, - {3579,3579, 0, 0, 40000, 73,0 }, - {3580,3580, 0, 0, 9166, 2900,0 }, - {3581,3581, 0, 0, 5640, 680,0 }, - {3582,3582, 0, 0, 640, 220,0 }, - {3583,3583, 0, 0, 40000, 53,0 }, - {3584,3584, 0, 0, 40000, 26,0 }, - {3585,3585, 0, 0, 40000, 0,0 }, - {3586,3586, 0, 0, 40000, 66,0 }, - {3587,3587, 0, 0, 40000, 60,0 }, - {3588,3588, 0, 0, 40000, 0,0 }, - {3589,3589, 0, 0, 40000, 73,0 }, - {3590,3590, 0, 0, 40000, 0,0 }, - {3591,3591, 0, 0, 40000, 0,0 }, - {3592,3592, 0, 0, 3780, 73,0 }, - {3593,3593, 0, 0, 40000, 0,0 }, - {3594,3594, 0, 0, 3786, 73,0 }, - {3595,3595, 0, 0, 40000, 73,0 }, - {3596,3596, 0, 0, 40000, 66,0 }, - {3597,3597, 0, 0, 40000, 73,0 }, - {3598,3598, 0, 0, 40000, 53,0 }, - {3599,3599, 0, 0, 40000, 426,0 }, - {3600,3600, 0, 0, 40000, 133,0 }, - {3601,3601, 0, 0, 40000, 66,0 }, - {3602,3602, 0, 0, 40000, 433,0 }, - {3603,3603, 0, 0, 393, 126,0 }, - {3604,3604, 0, 0, 40000, 66,0 }, - {3605,3605, 0, 0, 40000, 353,0 }, - {3606,3606, 0, 0, 3813, 73,0 }, - {3607,3607, 0, 0, 5793, 780,0 }, - {3608,3608, 0, 0, 40000, 73,0 }, - {3609,3609, 0, 0, 40000, 86,0 }, - {3610,3610, 0, 0, 820, 206,0 }, - {3611,3611, 0, 0, 40000, 66,0 }, - {3612,3612, 0, 0, 40000, 200,0 }, - {3613,3613, 0, 0, 18186, 720,0 }, - {3614,3614, 0, 0, 40000, 0,0 }, - {3615,3615, 0, 0, 40000, 493,0 }, - {3616,3616, 0, 0, 40000, 306,0 }, - {3617,3617, 0, 0, 2166, 600,0 }, - {3618,3618, 0, 0, 40000, 73,0 }, - {3619,3619, 0, 0, 40000, 3073,0 }, - {3620,3620, 0, 0, 2333, 413,0 }, - {3621,3621, 0, 0, 14880, 73,0 }, - {3622,3622, 0, 0, 40000, 66,0 }, - {3623,3623, 0, 0, 40000, 73,0 }, - {3624,3624, 0, 0, 40000, 1873,0 }, - {3625,3625, 0, 0, 40000, 446,0 }, - {3626,3626, 0, 0, 40000, 3126,0 }, - {3627,3627, 0, 0, 18446, 6140,0 }, - {3628,3628, 0, 0, 1113, 240,0 }, - {3629,3629, 0, 0, 40000, 3600,0 }, - {3630,3630, 0, 0, 40000, 4726,0 }, - {3631,3631, 0, 0, 40000, 0,0 }, - {3632,3632, 0, 0, 2893, 606,0 }, - {3633,3633, 0, 0, 40000, 0,0 }, - {3634,3634, 0, 0, 40000, 0,0 }, - {3635,3635, 0, 0, 40000, 173,0 }, - {3636,3636, 0, 0, 40000, 60,0 }, - {3637,3637, 0, 0, 40000, 0,0 }, - {3638,3638, 0, 0, 986, 326,0 }, - {3639,3639, 0, 0, 1873, 646,0 }, - {3640,3640, 0, 0, 200, 260,0 }, - {3641,3641, 0, 0, 1180, 393,0 }, - {3642,3642, 0, 0, 266, 0,0 }, - {3643,3643, 0, 0, 313, 126,0 }, - {3644,3644, 0, 0, 406, 253,0 }, - {3645,3645, 0, 0, 1013, 813,0 }, - {3646,3646, 0, 0, 273, 53,0 }, - {3647,3647, 0, 0, 720, 213,0 }, - {3648,3648, 0, 0, 386, 120,0 }, - {3649,3649, 0, 0, 40000, 766,0 }, - {3650,3650, 0, 0, 40000, 66,0 }, - {3651,3651, 0, 0, 40000, 73,0 }, - {3652,3652, 0, 0, 1186, 426,0 }, - {3653,3653, 0, 0, 16720, 240,0 }, - {3654,3654, 0, 0, 8026, 246,0 }, - {3655,3655, 0, 0, 18186, 140,0 }, - {3656,3656, 0, 0, 14566, 200,0 }, - {3657,3657, 0, 0, 7973, 20,0 }, - {3658,3658, 0, 0, 4446, 86,0 }, - {3659,3659, 0, 0, 4473, 100,0 }, - {3660,3660, 0, 0, 8646, 153,0 }, - {3661,3661, 0, 0, 3726, 660,0 }, - {3662,3662, 0, 0, 1893, 653,0 }, - {3663,3663, 0, 0, 1933, 760,0 }, - {3664,3664, 0, 0, 9160, 240,0 }, - {3665,3665, 0, 0, 1133, 100,0 }, - {3666,3666, 0, 0, 633, 233,0 }, - {3667,3667, 0, 0, 9153, 3060,0 }, - {3668,3668, 0, 0, 2166, 406,0 }, - {3669,3669, 0, 0, 40000, 66,0 }, - {3670,3670, 0, 0, 40000, 73,0 }, - {3671,3671, 0, 0, 40000, 73,0 }, - {3672,3672, 0, 0, 40000, 73,0 }, - {3673,3673, 0, 0, 40000, 346,0 }, - {3674,3674, 0, 0, 40000, 353,0 }, - {3675,3675, 0, 0, 40000, 200,0 }, - {3676,3676, 0, 0, 40000, 320,0 }, - {3677,3677, 0, 0, 4646, 100,0 }, - {3678,3678, 0, 0, 4426, 133,0 }, - {3679,3679, 0, 0, 4633, 100,0 }, - {3680,3680, 0, 0, 2266, 133,0 }, - {3681,3681, 0, 0, 2346, 53,0 }, - {3682,3682, 0, 0, 40000, 66,0 }, - {3683,3683, 0, 0, 9686, 173,0 }, - {3684,3684, 0, 0, 14300, 66,0 }, - {3685,3685, 0, 0, 40000, 0,0 }, - {3686,3686, 0, 0, 40000, 0,0 }, - {3687,3687, 0, 0, 40000, 0,0 }, - {3688,3688, 0, 0, 8613, 73,0 }, - {3689,3689, 0, 0, 40000, 0,0 }, - {3690,3690, 0, 0, 40000, 0,0 }, - {3691,3691, 0, 0, 40000, 0,0 }, - {3692,3692, 0, 0, 40000, 0,0 }, - {3693,3693, 0, 0, 40000, 393,0 }, - {3694,3694, 0, 0, 40000, 126,0 }, - {3695,3695, 0, 0, 40000, 120,0 }, - {3696,3696, 0, 0, 40000, 0,0 }, - {3697,3697, 0, 0, 40000, 226,0 }, - {3698,3698, 0, 0, 7420, 1186,0 }, - {3699,3699, 0, 0, 3280, 1726,0 }, - {3700,3700, 0, 0, 3680, 1220,0 }, - {3701,3701, 0, 0, 40000, 480,0 }, - {3702,3702, 0, 0, 40000, 306,0 }, - {3703,3703, 0, 0, 40000, 433,0 }, - {3704,3704, 0, 0, 40000, 133,0 }, - {3705,3705, 0, 0, 40000, 0,0 }, - {3706,3706, 0, 0, 40000, 0,0 }, - {3707,3707, 0, 0, 1166, 380,0 }, - {3708,3708, 0, 0, 40000, 140,0 }, - {3709,3709, 0, 0, 40000, 126,0 }, - {3710,3710, 0, 0, 40000, 100,0 }, - {3711,3711, 0, 0, 40000, 66,0 }, - {3712,3712, 0, 0, 40000, 226,0 }, - {3713,3713, 0, 0, 40000, 133,0 }, - {3714,3714, 0, 0, 40000, 73,0 }, - {3715,3715, 0, 0, 40000, 226,0 }, - {3716,3716, 0, 0, 40000, 100,0 }, - {3717,3717, 0, 0, 40000, 80,0 }, - {3718,3718, 0, 0, 40000, 100,0 }, - {3719,3719, 0, 0, 40000, 73,0 }, - {3720,3720, 0, 0, 40000, 73,0 }, - {3721,3721, 0, 0, 40000, 73,0 }, - {3722,3722, 0, 0, 40000, 126,0 }, - {3723,3723, 0, 0, 40000, 80,0 }, - {3724,3724, 0, 0, 40000, 73,0 }, - {3725,3725, 0, 0, 40000, 86,0 }, - {3726,3726, 0, 0, 40000, 100,0 }, - {3727,3727, 0, 0, 40000, 0,0 }, - {3728,3728, 0, 0, 40000, 126,0 }, - {3729,3729, 0, 0, 40000, 133,0 }, - {3730,3730, 0, 0, 40000, 140,0 }, - {3731,3731, 0, 0, 40000, 73,0 }, - {3732,3732, 0, 0, 40000, 60,0 }, - {3733,3733, 0, 0, 40000, 93,0 }, - {3734,3734, 0, 0, 40000, 80,0 }, - {3735,3735, 0, 0, 40000, 66,0 }, - {3736,3736, 0, 0, 40000, 0,0 }, - {3737,3737, 0, 0, 40000, 220,0 }, - {3738,3738, 0, 0, 40000, 80,0 }, - {3739,3739, 0, 0, 40000, 400,0 }, - {3740,3740, 0, 0, 40000, 1373,0 }, - {3741,3741, 0, 0, 40000, 86,0 }, - {3742,3742, 0, 0, 40000, 1313,0 }, - {3743,3743, 0, 0, 40000, 0,0 }, - {3744,3744, 0, 0, 11486, 593,0 }, - {3745,3745, 0, 0, 40000, 1246,0 }, - {3746,3746, 0, 0, 40000, 140,0 }, - {3747,3747, 0, 0, 14386, 2680,0 }, - {3748,3748, 0, 0, 40000, 653,0 }, - {3749,3749, 0, 0, 2286, 713,0 }, - {3750,3750, 0, 0, 40000, 253,0 }, - {3751,3751, 0, 0, 6933, 406,0 }, - {3752,3752, 0, 0, 40000, 1313,0 }, - {3753,3753, 0, 0, 40000, 1440,0 }, - {3754,3754, 0, 0, 40000, 73,0 }, - {3755,3755, 0, 0, 11100, 420,0 }, - {3756,3756, 0, 0, 6493, 320,0 }, - {3757,3757, 0, 0, 3486, 126,0 }, - {3758,3758, 0, 0, 6620, 2133,0 }, - {3759,3759, 0, 0, 1180, 413,0 }, - {3760,3760, 0, 0, 40000, 73,0 }, - {3761,3761, 0, 0, 40000, 73,0 }, - {3762,3762, 0, 0, 40000, 66,0 }, - {3763,3763, 0, 0, 4580, 413,0 }, - {3764,3764, 0, 0, 340, 146,0 }, - {3765,3765, 0, 0, 1166, 400,0 }, - {3766,3766, 0, 0, 1346, 660,0 }, - {3767,3767, 0, 0, 1260, 393,0 }, - {3768,3768, 0, 0, 3646, 1186,0 }, - {3769,3769, 0, 0, 2713, 400,0 }, - {3770,3770, 0, 0, 1780, 73,0 }, - {3771,3771, 0, 0, 800, 213,0 }, - {3772,3772, 0, 0, 660, 173,0 }, - {3773,3773, 0, 0, 12146, 73,0 }, - {3774,3774, 0, 0, 273, 60,0 }, - {3775,3775, 0, 0, 40000, 73,0 }, - {3776,3776, 0, 0, 380, 53,0 }, - {3777,3777, 0, 0, 40000, 200,0 }, - {3778,3778, 0, 0, 586, 20,0 }, - {3779,3779, 0, 2, 6, 0,0 }, - { 738, 738, 44, 0, 840, 340,0 }, - {3780,3780, 36, 0, 7366, 140,0 }, - {3781,3781, 32, 0, 100, 0,0 }, - {2030,2030, 60, 0, 293, 126,0 }, - {3782,3782, 24, 0, 100, 0,0 }, - {3783,3783, 60, 0, 126, 73,0 }, - {3784,3784, 44, 0, 393, 93,0 }, - { 132, 132, 44, 0, 173, 100,0 }, - {3785,3785, 47, 0, 393, 93,0 }, - { 152, 152, 44, 0, 213, 86,0 }, - {3784,3784, 50, 0, 393, 93,0 }, - { 139, 139, 44, 0, 293, 100,0 }, - {3784,3784, 54, 0, 393, 93,0 }, - {3784,3784, 57, 0, 393, 93,0 }, - {3786,3786, 60, 0, 1900, 666,0 }, - {3784,3784, 60, 0, 393, 93,0 }, - {3787,3787, 60, 0, 1900, 666,0 }, - {3788,3788, 60, 0, 1886, 666,0 }, - {3789,3789, 60, 0, 1866, 653,0 }, - {3790,3790, 60, 0, 1873, 633,0 }, - {3791,3791, 44, 0, 946, 333,0 }, - {2037,2037, 44, 0, 213, 126,0 }, - { 144, 144, 44, 0, 213, 126,0 }, - {2038,2038, 44, 0, 106, 0,0 }, - {3792,3792, 44, 0, 380, 360,0 }, - {3793,3793, 44, 0, 520, 206,0 }, - {3794,3794, 45, 0, 273, 100,0 }, - {3795,3795, 33, 0, 326, 106,0 }, - {3796,3796, 56, 0, 506, 200,0 }, - {3796,3796, 51, 0, 506, 200,0 }, - {3797,3797, 44, 0, 126, 66,0 }, - {3798,3798, 44, 0, 553, 186,0 }, - {3534,3534, 56, 0, 240, 100,0 }, - { 158, 158, 68, 0, 126, 140,0 }, - {3799,3799, 51, 0, 513, 206,0 }, - {3800,3800, 46, 0, 506, 200,0 }, - {3801,3801, 44, 0, 513, 206,0 }, - {3802,3802, 44, 0, 3720, 1260,0 }, - { 152, 152, 45, 0, 220, 80,0 }, - {3803,3803, 0, 0, 40000, 86,0 }, - {3804,3804, 0, 0, 40000, 226,0 }, - {3805,3805, 0, 0, 40000, 73,0 }, - {3806,3806, 0, 0, 40000, 73,0 }, - {3807,3807, 0, 0, 40000, 93,0 }, - {3808,3808, 0, 0, 4653, 660,0 }, - {3809,3809, 0, 0, 6686, 2246,0 }, - {3810,3810, 0, 0, 1180, 413,0 }, - {3811,3811, 0, 0, 966, 293,0 }, - {3812,3812, 0, 0, 1780, 66,0 }, - {3780,3780, 45, 0, 5900, 113,0 }, - {3061,3061, 45, 0, 40000, 0,0 }, - {3813,3813, 60, 0, 126, 226,0 }, - {3781,3781, 60, 0, 93, 0,0 }, - {3814,3814, 44, 0, 393, 86,0 }, - {3815,3815, 57, 0, 166, 80,0 }, - {3816,3816, 56, 0, 240, 100,0 }, - {3817,3817, 60, 0, 113, 0,0 }, - {3818,3818, 60, 0, 113, 0,0 }, - {3517,3517, 45, 0, 213, 213,0 }, - {3819,3819, 0, 0, 4033, 100,0 }, - {3820,3820, 0, 0, 5200, 873,0 }, - {3821,3821, 0, 0, 40000, 0,0 }, - {3822,3822, 0, 0, 10493, 160,0 }, - {3823,3823, 0, 0, 40000, 740,0 }, - {3824,3825, 0, 1, 40000, 366,0.078125 }, - {3826,3826, 0, 0, 40000, 5100,0 }, - {3827,3827, 0, 0, 40000, 766,0 }, - {3828,1172, 0, 1, 40000, 780,0.15625 }, - {3829,3829, 0, 0, 40000, 60,0 }, - {3830,3830, 0, 0, 566, 133,0 }, - {3831,3831, 32, 0, 146, 0,0 }, - {3832,3832, 36, 0, 273, 0,0 }, - {3833,3833, 88, 0, 340, 120,0 }, - {3834,3834, 0, 0, 9006, 240,0 }, - {3835,3835, 0, 0, 9206, 246,0 }, - {3836,3836, 0, 0, 9246, 386,0 }, - {3837,3837, 0, 0, 9440, 220,0 }, - {3838,3838, 0, 0, 8900, 133,0 }, - {3839,3839, 0, 0, 9400, 253,0 }, - {3840,3840, 0, 0, 4613, 420,0 }, - {3841,3841, 0, 0, 9233, 426,0 }, - {3842,3842, 0, 0, 40000, 526,0 }, - {3843,3843, 0, 0, 40000, 640,0 }, - {3844,3844, 0, 0, 40000, 666,0 }, - {3845,3845, 0, 0, 40000, 1053,0 }, - {3846,3846, 0, 0, 40000, 173,0 }, - {3847,3847, 0, 0, 40000, 246,0 }, - {3848,3848, 0, 0, 40000, 226,0 }, - {3849,3849, 0, 0, 4073, 233,0 }, - {3850,3850, 0, 0, 14286, 326,0 }, - {3851,3851, 0, 0, 9233, 146,0 }, - {3852,3852, 0, 0, 4480, 133,0 }, - {3853,3853, 0, 0, 40000, 53,0 }, - {3854,3854, 0, 0, 40000, 126,0 }, - {3855,3855, 0, 0, 40000, 126,0 }, - {3856,3856, 0, 0, 18226, 146,0 }, - {3857,3857, 0, 0, 40000, 326,0 }, - {3858,3858, 0, 0, 40000, 0,0 }, - {3859,3859, 0, 0, 40000, 300,0 }, - {3860,3860, 0, 0, 40000, 0,0 }, - {3861,3861, 0, 0, 40000, 0,0 }, - {3862,3862, 0, 0, 40000, 0,0 }, - {3863,3863, 0, 0, 40000, 140,0 }, - {3864,3864, 0, 0, 40000, 153,0 }, - {3865,3865, 0, 0, 40000, 233,0 }, - {3866,3866, 0, 0, 40000, 186,0 }, - {3867,3867, 0, 0, 40000, 413,0 }, - {3868,3868, 0, 0, 40000, 373,0 }, - {3869,3869, 0, 0, 1246, 440,0 }, - {3870,3870, 0, 0, 4620, 1513,0 }, - {3871,3871, 0, 0, 40000, 433,0 }, - {3872,3872, 0, 0, 40000, 453,0 }, - {3873,3873, 0, 0, 40000, 1440,0 }, - {3874,3874, 0, 0, 40000, 480,0 }, - {3875,3875, 0, 0, 40000, 1360,0 }, - {3876,3876, 0, 0, 40000, 0,0 }, - {3877,3877, 0, 0, 40000, 353,0 }, - {3878,3878, 0, 0, 40000, 86,0 }, - {3879,3879, 0, 0, 40000, 126,0 }, - {3880,3880, 0, 0, 40000, 73,0 }, - {3881,3881, 0, 0, 40000, 80,0 }, - {3882,3882, 0, 0, 40000, 246,0 }, - {3883,3883, 0, 0, 40000, 93,0 }, - {3884,3884, 0, 0, 40000, 120,0 }, - {3885,3885, 0, 0, 40000, 180,0 }, - {3886,3886, 0, 0, 40000, 133,0 }, - {3887,3887, 0, 0, 40000, 133,0 }, - {3888,3888, 0, 0, 40000, 153,0 }, - {3889,3889, 0, 0, 40000, 93,0 }, - {3890,3890, 0, 0, 40000, 140,0 }, - {3891,3891, 0, 0, 40000, 100,0 }, - {3892,3892, 0, 0, 40000, 146,0 }, - {3893,3893, 0, 0, 40000, 126,0 }, - {3894,3894, 0, 0, 40000, 160,0 }, - {3895,3895, 0, 0, 40000, 226,0 }, - {3896,3896, 0, 0, 40000, 140,0 }, - {3897,3897, 0, 0, 40000, 200,0 }, - {3898,3898, 0, 0, 40000, 66,0 }, - {3899,3899, 0, 0, 40000, 446,0 }, - {3900,3900, 0, 0, 40000, 140,0 }, - {3901,3901, 0, 0, 40000, 400,0 }, - {3902,3902, 0, 0, 40000, 373,0 }, - {3903,3903, 0, 0, 40000, 1306,0 }, - {3904,3904, 0, 0, 40000, 186,0 }, - {3905,3905, 0, 0, 40000, 640,0 }, - {3906,3906, 0, 0, 40000, 346,0 }, - {3907,3907, 0, 0, 40000, 140,0 }, - {3908,3908, 0, 0, 40000, 253,0 }, - {3909,3909, 0, 0, 8980, 746,0 }, - {3910,3910, 0, 0, 40000, 1266,0 }, - {3911,3911, 0, 0, 40000, 1306,0 }, - {3912,3912, 0, 0, 7226, 593,0 }, - {3913,3913, 0, 0, 40000, 140,0 }, - {3914,3914, 0, 0, 40000, 220,0 }, - {3915,3915, 0, 0, 40000, 146,0 }, - {3916,3916, 0, 0, 4606, 1506,0 }, - {3917,3917, 0, 0, 40000, 80,0 }, - {3918,3918, 0, 0, 40000, 0,0 }, - {3919,3919, 0, 0, 40000, 0,0 }, - {3920,3920, 0, 0, 613, 226,0 }, - {3921,3921, 0, 0, 9073, 2946,0 }, - {3922,3922, 0, 0, 40000, 73,0 }, - {3923,3923, 0, 0, 3726, 200,0 }, - {3924,3924, 0, 0, 3680, 373,0 }, - {3925,3925, 0, 0, 7113, 186,0 }, - {3926,3926, 0, 0, 2406, 106,0 }, - {3927,3927, 0, 0, 40000, 0,0 }, - {3928,3928, 0, 0, 40000, 253,0 }, - {3929,3929, 0, 0, 40000, 0,0 }, - {3930,3930, 0, 0, 40000, 80,0 }, - {3931,3931, 0, 0, 40000, 86,0 }, - {3932,3932, 0, 0, 18186, 740,0 }, - {3933,3933, 0, 0, 18426, 813,0 }, - { 523, 523, 0, 0, 200, 260,0 }, - {3934,3934, 0, 0, 340, 146,0 }, - {3935,3935, 0, 0, 366, 260,0 }, - {3936,3936, 48, 0, 126, 0,0 }, - {3937,3937, 27, 0, 200, 106,0 }, - {3938,3938, 40, 0, 1073, 800,0 }, - {3939,3939, 48, 0, 100, 0,0 }, - {3938,3938, 45, 0, 933, 666,0 }, - {3940,3940, 48, 0, 140, 333,0 }, - {3938,3938, 47, 0, 933, 666,0 }, - {3941,3941, 48, 0, 1840, 0,0 }, - {3938,3938, 49, 0, 953, 686,0 }, - {3938,3938, 53, 0, 906, 686,0 }, - {3938,3938, 56, 0, 913, 693,0 }, - { 129, 129, 52, 0, 293, 126,0 }, - { 130, 130, 48, 0, 173, 93,0 }, - { 129, 129, 58, 0, 286, 126,0 }, - { 132, 132, 47, 0, 173, 100,0 }, - { 492, 492, 43, 0, 820, 306,0 }, - { 132, 132, 49, 0, 173, 93,0 }, - { 132, 132, 51, 0, 173, 93,0 }, - { 132, 132, 54, 0, 173, 93,0 }, - { 132, 132, 57, 0, 146, 86,0 }, - { 492, 492, 72, 0, 706, 300,0 }, - { 137, 137, 76, 0, 1900, 666,0 }, - { 138, 138, 84, 0, 740, 300,0 }, - { 139, 139, 36, 0, 353, 146,0 }, - { 140, 140, 76, 0, 1886, 680,0 }, - { 141, 141, 84, 0, 220, 113,0 }, - { 135, 135, 83, 0, 1353, 480,0 }, - { 142, 142, 84, 0, 386, 160,0 }, - {3942,3942, 24, 0, 2313, 780,0 }, - { 137, 137, 77, 0, 1893, 660,0 }, - { 144, 144, 60, 0, 213, 0,0 }, - { 145, 145, 65, 0, 180, 146,0 }, - { 146, 146, 59, 0, 173, 93,0 }, - { 147, 147, 51, 0, 266, 213,0 }, - { 148, 148, 45, 0, 513, 200,0 }, - { 149, 149, 71, 0, 246, 26,0 }, - { 150, 150, 60, 0, 500, 193,0 }, - { 151, 151, 58, 0, 513, 200,0 }, - { 152, 152, 53, 0, 220, 86,0 }, - { 153, 153, 64, 0, 113, 40,0 }, - { 154, 154, 71, 0, 840, 300,0 }, - { 156, 156, 61, 0, 166, 80,0 }, - { 158, 158, 48, 0, 173, 213,0 }, - { 159, 159, 69, 0, 126, 140,0 }, - { 160, 160, 68, 0, 126, 140,0 }, - { 161, 161, 63, 0, 326, 113,0 }, - { 162, 162, 74, 0, 860, 286,0 }, - { 163, 163, 60, 0, 386, 160,0 }, - { 164, 164, 80, 0, 1106, 273,0 }, - { 165, 165, 64, 0, 126, 66,0 }, - { 166, 166, 69, 0, 386, 80,0 }, - { 167, 167, 73, 0, 546, 306,0 }, - { 168, 168, 75, 0, 126, 140,0 }, - { 169, 169, 68, 0, 340, 320,0 }, - { 131, 131, 48, 0, 520, 200,0 }, - {3061,3061, 53, 0, 40000, 0,0 }, - {3943,3944, 0, 4, 2153, 0,0 }, - {3945,3946, 0, 4, 9060, 393,0 }, - { 174,3947, 0, 4, 6953, 0,0 }, - {3948,3949, 0, 4, 9386, 140,0 }, - { 9,3950, 0, 4, 1626, 426,0 }, - {3951,3952, 0, 4, 18466, 240,0 }, - {3953,3954, 0, 4, 4666, 1486,0 }, - { 15,3955, 0, 4, 5700, 1986,0 }, - {3956,3957, 0, 4, 40000, 100,0 }, - {3958,3959, 0, 4, 40000, 73,0 }, - {3960,3961, 0, 4, 40000, 73,0 }, - {3962,3963, 0, 4, 40000, 73,0 }, - {3964,3965, 0, 4, 18280, 153,0 }, - {3966,3965, 0, 4, 18686, 160,0 }, - { 31,3967, 0, 4, 40000, 0,0 }, - {3968,3969, 0, 4, 17966, 100,0 }, - {3970,3971, 0, 4, 40000, 66,0 }, - {3972,3971, 0, 4, 40000, 66,0 }, - {3973,3974, 0, 4, 40000, 46,0 }, - {3975,3976, 0, 4, 18693, 106,0 }, - {3977,3976, 0, 4, 18593, 106,0 }, - {3978,3979, 0, 4, 9366, 106,0 }, - {3980,3981, 0, 4, 9120, 226,0 }, - {3982,3983, 0, 4, 40000, 140,0 }, - {3984,3985, 0, 4, 40000, 800,0 }, - { 54,3986, 0, 4, 2520, 706,0 }, - {3987,3988, 0, 4, 40000, 86,0 }, - {3989,3990, 0, 4, 40000, 233,0 }, - {3991, 253, 0, 4, 40000, 66,0 }, - {3992,3992, 0, 0, 40000, 0,0 }, - {3993,3993, 0, 0, 40000, 120,0 }, - {3994,3995, 0, 4, 40000, 80,0 }, - {3996,3997, 0, 4, 40000, 73,0 }, - {3998,3999, 0, 4, 40000, 86,0 }, - {1503,4000, 0, 4, 40000, 93,0 }, - { 88,4001, 0, 4, 40000, 1220,0 }, - {3743,4002, 0, 4, 7706, 1260,0 }, - { 92,4003, 0, 4, 40000, 186,0 }, - { 93,4004, 0, 4, 40000, 813,0 }, - { 94,4005, 0, 4, 7720, 1260,0 }, - { 96,4006, 0, 4, 40000, 2460,0 }, - { 103,4007, 0, 4, 3720, 1240,0 }, - { 104,4008, 0, 4, 3020, 1226,0 }, - { 105,4009, 0, 4, 6086, 2453,0 }, - { 107,4010, 0, 4, 2100, 760,0 }, - { 108,4011, 0, 4, 40000, 73,0 }, - { 110,4012, 0, 4, 40000, 100,0 }, - { 111,4013, 0, 4, 2366, 820,0 }, - {4014,4015, 0, 4, 1026, 326,0 }, - { 115,4016, 0, 4, 1853, 0,0 }, - { 118,4017, 0, 4, 1553, 53,0 }, - { 119,4018, 0, 4, 593, 0,0 }, - { 120,4019, 0, 4, 2293, 1173,0 }, - { 121,4020, 0, 4, 10673, 3000,0 }, - { 123,4021, 0, 4, 7413, 2486,0 }, - { 124,4022, 0, 4, 40000, 1126,0 }, - { 125,4023, 0, 4, 40000, 1546,0 }, - {4024,4024, 35, 0, 706, 266,0 }, - {4025,4026, 38, 1, 273, 106,0 }, - {4027,4028, 38, 1, 366, 133,0 }, - {4029,4030, 48, 1, 280, 133,-1.90625 }, - {4031,4031, 51, 0, 113, 80,0 }, - {4032,4033, 48, 1, 953, 346,-1.90625 }, - {4034,4034, 61, 1, 3200, 540,0.09375 }, - {3369,1557, 70, 4, 833, 0,0 }, - {4035,4036, 79, 1, 1306, 513,0.078125 }, - {4037,4037, 62, 0, 5200, 466,0 }, - {4038,4039, 67, 1, 2153, 1080,0.078125 }, - {4040,4040, 62, 1, 3226, 573,0.09375 }, - {4041,4042, 54, 1, 286, 133,0 }, - {4041,4043, 48, 1, 286, 126,0 }, - { 389, 389, 42, 0, 266, 73,0 }, - {4044,4045, 48, 1, 280, 126,0 }, - {4046,4047, 48, 1, 380, 60,0 }, - {4048,4048, 16, 0, 180, 20,0 }, - {4049,4049, 16, 0, 740, 20,0 }, - {4050,4051, 64, 4, 1366, 0,0 }, - { 844, 844,244, 2, 6, 0,0 }, - { 855, 855,244, 2, 6, 0,0 }, - { 880, 880,232, 0, 253, 80,0 }, - { 882, 882,220, 0, 40000, 266,0 }, - { 887, 887, 35, 0, 133, 46,0 }, - { 884, 884, 35, 0, 233, 80,0 }, - { 885, 885, 35, 0, 226, 86,0 }, - { 886, 886, 35, 0, 113, 0,0 }, - { 361, 361, 35, 0, 286, 73,0 }, - { 767, 767, 35, 0, 3020, 786,0 }, - { 888, 888, 35, 0, 246, 53,0 }, - {2141,2141, 35, 0, 186, 73,0 }, - { 891, 891, 35, 0, 713, 266,0 }, - {2142,2142, 35, 0, 200, 100,0 }, - {2143,2143, 35, 0, 220, 80,0 }, - {2144,2144, 35, 0, 2393, 100,0 }, - {2145,2145, 35, 0, 1980, 813,0 }, - { 376, 376, 35, 0, 1880, 840,0 }, - { 895, 895, 35, 0, 366, 140,0 }, - {2146,2146, 35, 0, 346, 106,0 }, - { 382, 382, 35, 0, 1073, 113,0 }, - {2147,2147, 35, 0, 106, 80,0 }, - { 898, 898, 35, 0, 206, 153,0 }, - { 899, 899, 35, 0, 633, 240,0 }, - { 900, 900, 35, 0, 620, 240,0 }, - { 871, 871, 35, 0, 380, 73,0 }, - { 388, 388, 35, 0, 286, 80,0 }, - { 901, 901, 35, 0, 260, 26,0 }, - { 902, 902, 35, 0, 1093, 73,0 }, - { 903, 903, 35, 0, 126, 73,0 }, - {3500,3500, 35, 2, 6, 0,0 }, - {4052,4052, 0, 0, 14166, 320,0 }, - {4053,4053, 0, 0, 7413, 653,0 }, - {4054,4054, 0, 0, 40000, 146,0 }, - {4055,4055, 0, 0, 40000, 113,0 }, - {4056,4056, 0, 0, 16773, 193,0 }, - {4057,4057, 0, 0, 40000, 73,0 }, - {4058,4058, 0, 0, 40000, 0,0 }, - {4059,4059, 0, 0, 966, 373,0 }, - {4060,4060, 0, 0, 40000, 80,0 }, - {4061,4061, 0, 0, 40000, 80,0 }, - {4062,4062, 0, 0, 18473, 93,0 }, - {4063,4063, 0, 0, 40000, 60,0 }, - {4064,4064, 0, 0, 40000, 73,0 }, - {4065,4065, 0, 0, 40000, 0,0 }, - {4066,4066, 0, 0, 40000, 213,0 }, - {4067,4067, 0, 0, 40000, 66,0 }, - {4068,4068, 0, 0, 1413, 1026,0 }, - {4069,4069, 0, 0, 506, 200,0 }, - {4070,4070, 0, 0, 3793, 1106,0 }, - {4071,4071, 0, 0, 40000, 220,0 }, - {4072,4072, 0, 0, 40000, 46,0 }, - {4073,4073, 0, 0, 40000, 0,0 }, - {4074,4074, 0, 0, 40000, 60,0 }, - {4075,4075, 0, 0, 40000, 0,0 }, - {4076,4076, 0, 0, 40000, 33,0 }, - {4077,4077, 0, 0, 40000, 0,0 }, - {4078,4078, 0, 0, 40000, 146,0 }, - {4079,4079, 0, 0, 40000, 66,0 }, - {4080,4080, 0, 0, 40000, 353,0 }, - {4081,4081, 0, 0, 40000, 66,0 }, - {4082,4082, 0, 0, 40000, 53,0 }, - {4083,4083, 0, 0, 40000, 73,0 }, - {4084,4084, 0, 0, 40000, 66,0 }, - {4085,4085, 0, 0, 40000, 926,0 }, - {4086,4086, 0, 0, 2833, 200,0 }, - { 127, 127, 36, 0, 386, 166,0 }, - {4087,4087, 36, 0, 100, 0,0 }, - {2030,2030, 36, 0, 346, 140,0 }, - {3782,3782, 48, 0, 93, 0,0 }, - {3783,3783, 36, 0, 146, 0,0 }, - {4088,4088, 48, 0, 1886, 653,0 }, - { 132, 132, 69, 0, 126, 66,0 }, - {4088,4088, 52, 0, 1853, 626,0 }, - { 152, 152, 48, 0, 220, 86,0 }, - {4088,4088, 55, 0, 1886, 640,0 }, - { 139, 139, 57, 0, 293, 133,0 }, - {4088,4088, 58, 0, 1860, 633,0 }, - {4088,4088, 60, 0, 1886, 633,0 }, - {4089,4089, 62, 0, 2660, 900,0 }, - {4088,4088, 63, 0, 1880, 646,0 }, - { 134, 134, 70, 0, 966, 360,0 }, - {4090,4090, 70, 0, 973, 346,0 }, - {4091,4091, 53, 0, 1866, 640,0 }, - {3516,3516, 48, 0, 180, 93,0 }, - {4092,4092, 84, 0, 1360, 473,0 }, - {4093,4093, 43, 0, 513, 206,0 }, - {4094,4094, 56, 0, 1353, 480,0 }, - {3791,3791, 24, 0, 1866, 613,0 }, - { 134, 134, 65, 0, 1346, 486,0 }, - { 146, 146, 48, 0, 173, 93,0 }, - { 146, 146, 54, 0, 173, 93,0 }, - {4095,4095, 42, 0, 246, 140,0 }, - {4095,4095, 39, 0, 240, 133,0 }, - {3816,3816, 52, 0, 306, 113,0 }, - {4096,4096, 52, 0, 413, 86,0 }, - { 158, 158, 60, 0, 146, 166,0 }, - { 158, 158, 66, 0, 146, 166,0 }, - { 158, 158, 59, 0, 146, 166,0 }, - {3538,3538, 91, 0, 773, 233,0 }, - {3547,3547,109, 0, 5300, 1786,0 }, - {4097,4097, 79, 0, 560, 313,0 }, - {4098,4098, 0, 0, 10646, 73,0 }, - {4099,4100, 0, 1, 14166, 586,0.03125 }, - {4101,4102, 0, 1, 15553, 546,0.03125 }, - {4103,4104, 0, 1, 11746, 320,0.046875 }, - {4105,4106, 0, 1, 14706, 646,0.15625 }, - {4107,4108, 0, 1, 7320, 100,0.046875 }, - {4109,4110, 0, 1, 40000, 0,0.0625 }, - {4111,4112, 0, 1, 13660, 260,0 }, - {4113,4114, 0, 1, 15026, 133,0 }, - {4115,4116, 0, 1, 40000, 0,2.5e-05 }, - {4117,4118, 0, 1, 4980, 3400,0 }, - {4119,4120, 0, 1, 7840, 2660,0.046875 }, - {4121,4122, 0, 1, 8326, 180,0 }, - {4123,4124, 0, 1, 1093, 140,0 }, - {4125,4126, 0, 1, 2280, 400,0 }, - {4127,4128, 0, 1, 4553, 1486,0.03125 }, - {4129,4129, 0, 1, 40000, 0,0.03125 }, - {4130,4131, 0, 1, 40000, 60,0.15625 }, - {4132,4133, 0, 1, 40000, 93,0.078125 }, - {4134,4135, 0, 1, 40000, 86,0.15625 }, - {4136,4137, 0, 1, 40000, 520,0.03125 }, - {4138,4139, 0, 1, 40000, 140,0.0625 }, - {4140,4141, 0, 1, 40000, 133,0.140625 }, - {4142,4143, 0, 1, 40000, 73,0 }, - {4144,4145, 0, 1, 40000, 346,0.109375 }, - {4146,4147, 0, 1, 3693, 86,0 }, - {4148,4149, 0, 1, 6586, 460,2.5e-05 }, - {4150,4151, 0, 1, 4320, 93,0 }, - {4152,4153, 0, 1, 7346, 126,0.046875 }, - {4154,4155, 0, 1, 3633, 260,0 }, - {4156,4157, 0, 1, 40000, 126,-1.95312 }, - {4158,4159, 0, 1, 40000, 126,-1.9375 }, - {4160,4161, 0, 1, 40000, 46,0.234375 }, - {4162,4163, 0, 1, 40000, 0,0.03125 }, - {4164,4165, 0, 1, 10320, 86,0 }, - {4166,4167, 0, 1, 12933, 133,0 }, - {4168,4169, 0, 1, 11820, 240,0.046875 }, - {4170,4171, 0, 1, 3966, 166,0 }, - {4172,4173, 0, 1, 40000, 0,0 }, - {4174,4174, 0, 0, 2666, 160,0 }, - {4175,4176, 0, 1, 15046, 93,0.078125 }, - {4177,4178, 0, 1, 40000, 100,0 }, - {4179,4179, 0, 0, 40000, 260,0 }, - {4180,4181, 0, 1, 40000, 126,2.5e-05 }, - {4182,4182, 0, 0, 40000, 233,0 }, - {4183,4184, 0, 1, 40000, 440,0.078125 }, - {4185,4186, 0, 1, 2160, 606,0.109375 }, - {4187,4188, 0, 1, 14753, 2400,0.03125 }, - {4189,4190, 0, 1, 7680, 646,0.03125 }, - {4191,4192, 0, 1, 40000, 446,0.0625 }, - {4193,4194, 0, 1, 40000, 866,-0.0625 }, - {4195,4195, 0, 1, 40000, 1220,0.078125 }, - {4196,4196, 0, 1, 40000, 1960,0.0625 }, - {4197,4198, 0, 1, 40000, 433,0.125 }, - {4199,4200, 0, 1, 40000, 140,0.140625 }, - {4201,4202, 0, 1, 40000, 806,0.109375 }, - {4203,4204, 0, 1, 2040, 486,0.125 }, - {4205,4206, 0, 1, 40000, 86,0 }, - {4207,4208, 0, 1, 40000, 80,0.03125 }, - {4209,4209, 0, 0, 40000, 73,0 }, - {4210,4210, 0, 0, 40000, 126,0 }, - {4211,4212, 0, 1, 40000, 400,0.0625 }, - {4213,4214, 0, 1, 40000, 120,0.0625 }, - {4215,4216, 0, 1, 40000, 0,0.09375 }, - {4217,4217, 0, 1, 40000, 0,0.125 }, - {4218,4219, 0, 1, 40000, 186,0 }, - {4220,4220, 0, 0, 40000, 166,0 }, - {4221,4221, 0, 0, 40000, 73,0 }, - {4222,4222, 0, 0, 40000, 60,0 }, - {4223,4224, 0, 1, 40000, 140,0 }, - {4225,4225, 0, 0, 40000, 140,0 }, - {4226,4226, 0, 0, 40000, 66,0 }, - {4227,4228, 0, 1, 40000, 133,0 }, - {4229,4229, 0, 0, 40000, 86,0 }, - {4230,4230, 0, 0, 40000, 73,0 }, - {4231,4231, 0, 0, 40000, 106,0 }, - {4232,4233, 0, 1, 40000, 186,0.03125 }, - {4234,4235, 0, 1, 40000, 86,0.046875 }, - {4236,4237, 0, 1, 40000, 0,0.03125 }, - {4238,4238, 0, 0, 40000, 300,0 }, - {4239,4239, 0, 0, 40000, 66,0 }, - {4240,4241, 0, 1, 40000, 73,0.125 }, - {4242,4243, 0, 1, 40000, 86,0.109375 }, - {4244,4245, 0, 1, 40000, 146,0.109375 }, - {4246,4247, 0, 1, 40000, 66,-0.03125 }, - {4248,4248, 0, 0, 40000, 60,0 }, - {4249,4250, 0, 1, 40000, 213,0.15625 }, - {4251,4252, 0, 1, 40000, 66,0.125 }, - {4253,4254, 0, 1, 40000, 100,0.03125 }, - {4255,4256, 0, 1, 40000, 1513,0.078125 }, - {4257,4258, 0, 1, 40000, 353,0.109375 }, - {4259,4260, 0, 1, 40000, 133,0.078125 }, - {4261,4262, 0, 1, 40000, 746,0.140625 }, - {4263,4264, 0, 1, 40000, 0,0.109375 }, - {4265,4266, 0, 1, 5033, 1606,0.0625 }, - {4267,4268, 0, 1, 40000, 1146,0.09375 }, - {4269,4270, 0, 1, 40000, 1586,0.109375 }, - {4271,4272, 0, 1, 40000, 0,0.09375 }, - {4273,4274, 0, 1, 40000, 1006,0.125 }, - {4275,4275, 0, 1, 2680, 793,0.109375 }, - {4276,4277, 0, 1, 40000, 0,-0.046875 }, - {4278,4279, 0, 1, 9000, 3186,0.125 }, - {4280,4281, 0, 1, 40000, 1073,-0.078125 }, - {4282,4283, 0, 1, 40000, 2093,0.140625 }, - {4284,4285, 0, 1, 40000, 0,0.078125 }, - {4286,4287, 0, 1, 9580, 713,0.03125 }, - {4288,4289, 0, 1, 6286, 380,0 }, - {4290,4291, 0, 1, 2220, 426,0.03125 }, - {4292,4292, 0, 0, 1166, 760,0 }, - {4293,4294, 0, 1, 1186, 240,0 }, - {4295,4296, 0, 1, 40000, 100,0.0625 }, - {4297,4297, 0, 0, 40000, 160,0 }, - {4298,4298, 0, 0, 40000, 120,0 }, - {4299,4299, 0, 0, 8673, 2413,0 }, - {4300,4300, 0, 0, 393, 126,0 }, - {4301,4302, 0, 1, 1220, 393,0.03125 }, - {4303,4303, 0, 0, 246, 93,0 }, - {4304,4305, 0, 1, 1953, 393,0 }, - {4306,4307, 0, 1, 566, 146,0 }, - {4308,4309, 0, 1, 4220, 133,0 }, - {4310,4311, 0, 1, 2873, 73,0.109375 }, - {4312,4312, 0, 0, 613, 60,0 }, - {4313,4314, 0, 1, 40000, 186,0 }, - {4315,4316, 0, 1, 11880, 2993,0 }, - {4317,4317, 0, 0, 1573, 86,0 }, - {4318,4319, 0, 1, 40000, 793,0 }, - {4320,4321, 0, 1, 40000, 173,0 }, - {4322,4323, 0, 1, 40000, 793,0 }, - {4324,4324, 0, 0, 606, 133,0 }, - {4325,4325, 34, 0, 133, 40,0 }, - {4326,4326, 28, 0, 193, 46,0 }, - {4327,4328, 39, 1, 553, 126,0 }, - {4327,4328, 33, 1, 553, 126,0 }, - {4329,4330, 63, 1, 160, 80,0 }, - {4331,4331, 15, 0, 113, 0,0 }, - {4332,4332, 36, 0, 106, 0,0 }, - {4332,4333, 36, 1, 480, 173,0.40625 }, - {4334,4335, 25, 1, 313, 153,0 }, - {4336,4335, 25, 1, 206, 100,0 }, - {4337,4338, 61, 1, 153, 93,0 }, - {4339,4340, 38, 1, 340, 133,0 }, - {4341,4342, 37, 1, 206, 93,0 }, - {4343,4344, 15, 1, 346, 153,0 }, - {4345,4346,100, 1, 146, 0,0.140625 }, - {4347,4348, 19, 1, 553, 200,0 }, - {4349,4349, 48, 0, 180, 86,0 }, - {4350,4351, 15, 1, 333, 153,0 }, - {4352,4353, 12, 1, 340, 146,0 }, - {4354,4355, 11, 1, 346, 146,0 }, - {4356,4357, 61, 1, 2706, 1033,0.09375 }, - {4358,4355, 8, 1, 340, 146,0 }, - {4359,4360, 91, 1, 1166, 366,-0.046875 }, - {4361,4361, 70, 0, 966, 346,0 }, - {4362,4363, 80, 1, 300, 93,0.125 }, - {4364,4364, 58, 0, 206, 53,0 }, - {4365,4357, 62, 1, 2333, 820,0.09375 }, - {4366,4367, 31, 1, 773, 200,0 }, - {4368,4360, 91, 1, 1160, 360,-0.03125 }, - {4369,4370, 41, 1, 373, 113,0 }, - {4371,4372, 35, 1, 406, 126,0 }, - {4373,4374, 29, 1, 146, 106,0 }, - {4375,4376, 41, 1, 400, 126,0 }, - {4375,4376, 37, 1, 400, 126,0 }, - {4377,4378, 54, 1, 286, 133,0 }, - {4377,4379, 48, 1, 286, 126,0 }, - {4380,4381, 77, 1, 193, 93,0 }, - {4382,4383, 72, 1, 200, 93,0 }, - {4384,4384, 40, 0, 513, 0,0 }, - {4385,4385, 38, 0, 200, 20,0 }, - {4386,4386, 36, 0, 620, 20,0 }, - {4387,4388, 60, 1, 120, 80,0 }, - {4388,4389, 60, 1, 380, 80,0 }, - {4390,4390, 73, 0, 166, 33,0 }, - {4391,4392, 68, 1, 153, 40,0 }, - {4393,4394, 18, 1, 200, 80,0 }, - {4395,4396, 18, 1, 253, 73,0 }, - {4397,4397, 90, 0, 193, 20,0 }, - {4398,4398, 90, 0, 793, 40,0 }, - {4399,4400, 64, 1, 373, 73,0.03125 }, - {4401,4402, 80, 1, 406, 153,0.03125 }, - {4403,4404, 64, 1, 1866, 606,0 }, - {4405,4405, 67, 0, 106, 26,0 }, - {4406,4407, 50, 1, 173, 0,0 }, - {4408,4408, 36, 0, 4646, 0,0 }, - {4409,4409, 0, 0, 40000, 86,0 }, - {4410,4410, 0, 0, 40000, 73,0 }, - {4411,4411, 0, 0, 2433, 700,0 }, - {4412,4412, 0, 0, 1233, 26,0 }, - {4413,4413, 0, 0, 40000, 66,0 }, - {4414,4414, 0, 0, 40000, 60,0 }, - {4415,4415, 0, 0, 40000, 60,0 }, - {4416,4416, 0, 0, 40000, 66,0 }, - {4417,4417, 0, 0, 40000, 66,0 }, - {4418,4418, 0, 0, 40000, 0,0 }, - {4418,4418, 73, 0, 40000, 0,0 }, - {4419,4419, 0, 0, 40000, 60,0 }, - {4420,4420, 0, 0, 40000, 60,0 }, - {4421,4421, 0, 0, 7326, 2486,0 }, - {4422,4422, 0, 0, 4886, 1586,0 }, - {4423,4423, 0, 0, 646, 20,0 }, - {4424,4424, 0, 0, 253, 20,0 }, - {4424,4424, 12, 0, 253, 20,0 }, - {4425,4425, 0, 0, 640, 100,0 }, - {4425,4425, 1, 0, 640, 106,0 }, - {4426,4426, 0, 0, 133, 106,0 }, - {4426,4426, 23, 0, 133, 106,0 }, - {4427,4427, 0, 0, 653, 100,0 }, - {4428,4428, 0, 0, 4166, 1546,0 }, - {4429,4429, 0, 0, 40000, 73,0 }, - {4430,4430, 0, 0, 40000, 60,0 }, - {4431,4431, 0, 0, 40000, 53,0 }, - {4432,4432, 0, 0, 40000, 0,0 }, - {4433,4433, 0, 0, 246, 20,0 }, - {4434,4434, 0, 2, 6, 0,0 }, - {4435,4435, 0, 0, 4946, 233,0 }, - {4436,4436, 0, 0, 4946, 233,0 }, - {4437,4437, 0, 0, 4953, 240,0 }, - {4438,4438, 0, 0, 4946, 233,0 }, - {4439,4439, 0, 0, 18233, 46,0 }, - {4440,4440, 0, 0, 2386, 26,0 }, - {4441,4441, 0, 0, 4640, 633,0 }, - {4442,4442, 0, 0, 18466, 100,0 }, - {4443,4443, 0, 0, 18440, 66,-2 }, - {4444,4444, 0, 0, 18440, 6140,-2 }, - {4445,4445, 0, 0, 1206, 433,-2 }, - {4446,4446, 0, 0, 4626, 240,0 }, - {4447,4447, 0, 0, 726, 400,0 }, - {4448,4448, 0, 0, 5866, 73,0 }, - {4449,4449, 0, 0, 40000, 73,0 }, - {4450,4450, 0, 0, 40000, 73,0 }, - {4451,4451, 0, 0, 40000, 73,0 }, - {4452,4452, 0, 0, 40000, 73,0 }, - {4453,4453, 0, 0, 6500, 346,0 }, - {4454,4454, 0, 0, 6506, 346,0 }, - {4455,4455, 0, 0, 40000, 66,-2 }, - {4456,4456, 0, 0, 40000, 66,-2 }, - {4457,4457, 0, 0, 40000, 0,0 }, - {4458,4458, 0, 0, 40000, 46,0 }, - {4459,4459, 0, 0, 40000, 0,0 }, - {4459,4459, 0, 0, 40000, 0,-2 }, - {4460,4460, 0, 0, 2386, 26,0 }, - {4461,4461, 0, 0, 40000, 73,-2 }, - {4462,4462, 0, 0, 5866, 26,-2 }, - {4463,4463, 0, 0, 40000, 133,0 }, - {4464,4464, 0, 0, 40000, 133,0 }, - {4465,4465, 0, 0, 40000, 126,0 }, - {4466,4466, 0, 0, 253, 20,0 }, - {4467,4467, 0, 0, 8866, 1366,0 }, - {4468,4468, 0, 0, 1040, 766,0 }, - {4469,4469, 0, 0, 40000, 146,-2 }, - {4470,4470, 0, 0, 40000, 153,-2 }, - {4471,4471, 0, 0, 40000, 466,-2 }, - {4472,4472, 0, 0, 40000, 66,0 }, - {4473,4473, 0, 0, 2333, 566,0 }, - {4474,4474, 0, 0, 40000, 140,-2 }, - {4475,4475, 0, 0, 40000, 100,-2 }, - {4476,4476, 0, 0, 40000, 226,-2 }, - {4477,4477, 0, 0, 40000, 0,0 }, - {3712,3712, 0, 0, 40000, 226,-2 }, - {4478,4478, 0, 0, 40000, 140,-2 }, - {4479,4479, 0, 0, 40000, 66,0 }, - {4480,4480, 0, 0, 40000, 73,0 }, - {4481,4481, 0, 0, 40000, 73,0 }, - {4482,4482, 0, 0, 40000, 86,-2 }, - {4483,4483, 0, 0, 40000, 80,0 }, - {4484,4484, 0, 0, 40000, 73,-2 }, - {4485,4485, 0, 0, 40000, 80,-2 }, - {4486,4486, 0, 0, 40000, 73,-2 }, - {4487,4487, 0, 0, 40000, 73,0 }, - {4488,4488, 0, 0, 40000, 73,0 }, - {4489,4489, 0, 0, 40000, 93,0 }, - {4490,4490, 0, 0, 40000, 73,0 }, - {4491,4491, 0, 0, 11946, 13,0 }, - {4492,4492, 0, 0, 40000, 73,0 }, - {4462,4462, 0, 0, 5866, 26,0 }, - {4493,4493, 0, 0, 40000, 820,0 }, - {4494,4494, 0, 0, 2153, 873,0 }, - {1221,1221, 0, 0, 40000, 293,0.171875 }, - {4495,4495, 0, 0, 1620, 120,0 }, - {4496,4496, 0, 0, 15120, 93,0 }, - {4497,4497, 0, 0, 14613, 93,0 }, - {4498,4498, 0, 0, 2346, 793,0 }, - {4499,4499, 0, 0, 40000, 2380,0 }, - {4500,4500, 0, 0, 40000, 1280,0 }, - {4501,4501, 0, 0, 40000, 1460,0 }, - {4502,4502, 0, 0, 40000, 2513,0 }, - {4503,4503, 0, 0, 14840, 1266,0 }, - {4504,4504, 0, 0, 4513, 640,0 }, - {4505,4505, 0, 0, 4680, 806,0 }, - {4506,4506, 0, 0, 40000, 100,0 }, - {4507,4507, 0, 0, 40000, 66,0 }, - {4508,4508, 0, 0, 2420, 413,0 }, - {4509,4509, 0, 0, 406, 73,-2 }, - {4510,4510, 0, 0, 1166, 400,0 }, - {4511,4511, 0, 0, 1213, 106,0 }, - {4512,4512, 0, 0, 273, 60,-2 }, - {4513,4513, 0, 0, 40000, 2380,0 }, - {4514,4514, 0, 0, 40000, 440,0 }, - {1261,1261, 0, 0, 40000, 2960,0 }, - {4515,4515, 37, 0, 973, 73,-2 }, - {4516,4516, 48, 0, 106, 0,-2 }, - {4517,4517, 48, 0, 286, 133,-2 }, - {4518,4518, 62, 0, 166, 60,0 }, - {4519,4519, 44, 0, 980, 360,0 }, - {4520,4520, 80, 0, 100, 0,0 }, - {4519,4519, 50, 0, 980, 346,0 }, - {4521,4521, 48, 0, 106, 0,-2 }, - {4519,4519, 55, 0, 973, 360,0 }, - {4522,4522, 61, 0, 513, 20,0 }, - {4519,4519, 58, 0, 966, 353,0 }, - {4519,4519, 63, 0, 973, 353,0 }, - {4523,4523, 71, 0, 1366, 580,0 }, - {4519,4519, 72, 0, 820, 306,0 }, - {4524,4524, 70, 0, 1886, 666,0 }, - {4523,4523, 88, 0, 1353, 560,0 }, - {4525,4525, 76, 0, 1873, 653,0 }, - {4526,4526, 84, 0, 260, 113,0 }, - {4523,4523, 68, 0, 1366, 553,0 }, - {4527,4527, 72, 0, 153, 53,0 }, - {4528,4528, 28, 0, 1193, 413,0 }, - {4524,4524, 81, 0, 1353, 480,0 }, - {4529,4529, 58, 0, 246, 120,-2 }, - {4529,4529, 55, 0, 246, 120,-2 }, - {4529,4529, 44, 0, 246, 120,-2 }, - {4529,4529, 49, 0, 246, 120,-2 }, - {4529,4529, 40, 0, 286, 133,-2 }, - {4530,4530, 55, 0, 740, 560,-2 }, - {4530,4530, 48, 0, 893, 693,-2 }, - {4531,4531, 52, 0, 513, 206,0 }, - {4531,4531, 45, 0, 513, 206,0 }, - {4532,4532, 48, 0, 173, 100,-2 }, - {4533,4533, 48, 0, 120, 266,-2 }, - {4534,4534, 48, 0, 253, 60,-2 }, - {4509,4509, 73, 0, 160, 20,-2 }, - {4509,4509, 68, 0, 160, 20,-2 }, - {4509,4509, 63, 0, 193, 20,-2 }, - {4535,4535,108, 0, 406, 26,0 }, - {4536,4536,108, 0, 740, 26,0 }, + { 0, 0, 0, 0, 9006, 133, 0, 0 }, + { 1, 1, 0, 0, 9206, 146, 0, 0 }, + { 2, 2, 0, 0, 9246, 240, 0, 0 }, + { 3, 3, 0, 0, 9440, 140, 0, 0 }, + { 4, 4, 0, 0, 8900, 120, 0, 0 }, + { 5, 5, 0, 0, 9400, 140, 0, 0 }, + { 6, 6, 0, 0, 7460, 380, 0, 0 }, + { 7, 7, 0, 0, 9226, 93, 0, 0 }, + { 8, 8, 0, 0, 4613, 420, 0, 0 }, + { 9, 9, 0, 0, 7286, 4713, 0, 0 }, + { 10, 10, 0, 0, 2280, 746, 0, 0 }, + { 11, 11, 0, 0, 9233, 240, 0, 0 }, + { 12, 12, 0, 0, 346, 153, 0, 0 }, + { 13, 13, 0, 0, 633, 233, 0, 0 }, + { 14, 14, 0, 0, 4660, 1573, 0, 0 }, + { 15, 15, 0, 0, 1166, 400, 0, 0 }, + { 16, 16, 0, 0, 40000, 126, 0, 0 }, + { 17, 17, 0, 0, 40000, 93, 0, 0 }, + { 18, 18, 0, 0, 40000, 93, 0, 0 }, + { 19, 19, 0, 0, 40000, 553, 0, 0 }, + { 20, 20, 0, 0, 40000, 660, 0, 0 }, + { 21, 21, 0, 0, 40000, 73, 0, 0 }, + { 22, 22, 0, 0, 40000, 146, 0, 0 }, + { 23, 23, 0, 0, 40000, 146, 0, 0 }, + { 24, 24, 0, 0, 4026, 100, 0, 0 }, + { 25, 25, 0, 0, 14286, 120, 0, 0 }, + { 26, 26, 0, 0, 9233, 106, 0, 0 }, + { 27, 27, 0, 0, 4480, 100, 0, 0 }, + { 28, 28, 0, 0, 40000, 60, 0, 0 }, + { 29, 29, 0, 0, 40000, 80, 0, 0 }, + { 30, 30, 0, 0, 40000, 80, 0, 0 }, + { 31, 31, 0, 0, 18226, 100, 0, 0 }, + { 32, 32, 0, 0, 40000, 0, 0, 0 }, + { 33, 33, 0, 0, 40000, 80, 0, 0 }, + { 34, 34, 0, 0, 40000, 0, 0, 0 }, + { 35, 35, 0, 0, 40000, 53, 0, 0 }, + { 36, 36, 0, 0, 40000, 0, 0, 0 }, + { 37, 37, 0, 0, 40000, 0, 0, 0 }, + { 38, 38, 0, 0, 40000, 0, 0, 0 }, + { 39, 39, 0, 0, 40000, 160, 0, 0 }, + { 40, 40, 0, 0, 40000, 233, 0, 0 }, + { 41, 41, 0, 0, 40000, 73, 0, 0 }, + { 42, 42, 0, 0, 40000, 233, 0, 0 }, + { 43, 43, 0, 0, 40000, 213, 0, 0 }, + { 44, 44, 0, 0, 1246, 453, 0, 0 }, + { 45, 45, 0, 0, 4580, 786, 0, 0 }, + { 46, 46, 0, 0, 6873, 1246, 0, 0 }, + { 47, 47, 0, 0, 40000, 100, 0, 0 }, + { 48, 48, 0, 0, 40000, 140, 0, 0 }, + { 49, 49, 0, 0, 40000, 393, 0, 0 }, + { 50, 50, 0, 0, 40000, 406, 0, 0 }, + { 51, 51, 0, 0, 40000, 373, 0, 0 }, + { 52, 52, 0, 0, 40000, 0, 0, 0 }, + { 53, 53, 0, 0, 40000, 360, 0, 0 }, + { 54, 54, 0, 0, 1060, 380, 0, 0 }, + { 55, 55, 0, 0, 40000, 80, 0, 0 }, + { 56, 56, 0, 0, 40000, 73, 0, 0 }, + { 57, 57, 0, 0, 40000, 66, 0, 0 }, + { 58, 58, 0, 0, 40000, 60, 0, 0 }, + { 59, 59, 0, 0, 40000, 73, 0, 0 }, + { 60, 60, 0, 0, 40000, 66, 0, 0 }, + { 61, 61, 0, 0, 40000, 86, 0, 0 }, + { 62, 62, 0, 0, 40000, 66, 0, 0 }, + { 63, 63, 0, 0, 40000, 73, 0, 0 }, + { 64, 64, 0, 0, 40000, 80, 0, 0 }, + { 65, 65, 0, 0, 40000, 80, 0, 0 }, + { 66, 66, 0, 0, 40000, 73, 0, 0 }, + { 67, 67, 0, 0, 40000, 73, 0, 0 }, + { 68, 68, 0, 0, 40000, 53, 0, 0 }, + { 69, 69, 0, 0, 40000, 73, 0, 0 }, + { 70, 70, 0, 0, 40000, 126, 0, 0 }, + { 71, 71, 0, 0, 40000, 73, 0, 0 }, + { 72, 72, 0, 0, 40000, 73, 0, 0 }, + { 73, 73, 0, 0, 40000, 73, 0, 0 }, + { 74, 74, 0, 0, 40000, 66, 0, 0 }, + { 75, 75, 0, 0, 40000, 153, 0, 0 }, + { 76, 76, 0, 0, 40000, 153, 0, 0 }, + { 77, 77, 0, 0, 40000, 146, 0, 0 }, + { 78, 78, 0, 0, 40000, 146, 0, 0 }, + { 79, 79, 0, 0, 40000, 66, 0, 0 }, + { 80, 80, 0, 0, 40000, 60, 0, 0 }, + { 81, 81, 0, 0, 40000, 86, 0, 0 }, + { 82, 82, 0, 0, 40000, 73, 0, 0 }, + { 83, 83, 0, 0, 40000, 66, 0, 0 }, + { 84, 84, 0, 0, 40000, 153, 0, 0 }, + { 85, 85, 0, 0, 40000, 233, 0, 0 }, + { 86, 86, 0, 0, 40000, 80, 0, 0 }, + { 87, 87, 0, 0, 40000, 400, 0, 0 }, + { 88, 88, 0, 0, 40000, 1373, 0, 0 }, + { 89, 89, 0, 0, 40000, 193, 0, 0 }, + { 90, 90, 0, 0, 40000, 1273, 0, 0 }, + { 91, 91, 0, 0, 40000, 186, 0, 0 }, + { 92, 92, 0, 0, 40000, 86, 0, 0 }, + { 93, 93, 0, 0, 40000, 286, 0, 0 }, + { 94, 94, 0, 0, 40000, 140, 0, 0 }, + { 95, 95, 0, 0, 7440, 2473, 0, 0 }, + { 96, 96, 0, 0, 40000, 1220, 0, 0 }, + { 97, 97, 0, 0, 4946, 2713, 0, 0 }, + { 98, 98, 0, 0, 40000, 160, 0, 0 }, + { 99, 99, 0, 0, 8966, 406, 0, 0 }, + { 100, 100, 0, 0, 40000, 1353, 0, 0 }, + { 101, 101, 0, 0, 40000, 1306, 0, 0 }, + { 102, 102, 0, 0, 40000, 933, 0, 0 }, + { 103, 103, 0, 0, 9086, 226, 0, 0 }, + { 104, 104, 0, 0, 7233, 326, 0, 0 }, + { 105, 105, 0, 0, 7286, 200, 0, 0 }, + { 106, 106, 0, 0, 14180, 4406, 0, 0 }, + { 107, 107, 0, 0, 1180, 406, 0, 0 }, + { 108, 108, 0, 0, 40000, 66, 0, 0 }, + { 109, 109, 0, 0, 40000, 213, 0, 0 }, + { 110, 110, 0, 0, 40000, 73, 0, 0 }, + { 111, 111, 0, 0, 4606, 413, 0, 0 }, + { 112, 112, 0, 0, 613, 240, 0, 0 }, + { 113, 113, 0, 0, 1166, 400, 0, 0 }, + { 114, 114, 0, 0, 200, 353, 0, 0 }, + { 115, 115, 0, 0, 4553, 1480, 0, 0 }, + { 116, 116, 0, 0, 3740, 1260, 0, 0 }, + { 117, 117, 0, 0, 7240, 2300, 0, 0 }, + { 118, 118, 0, 0, 3020, 73, 0, 0 }, + { 119, 119, 0, 0, 1626, 800, 0, 0 }, + { 120, 120, 0, 0, 2466, 620, 0, 0 }, + { 121, 121, 0, 0, 12053, 3160, 0, 0 }, + { 122, 122, 0, 0, 466, 120, 0, 0 }, + { 123, 123, 0, 0, 1000, 320, 0, 0 }, + { 124, 124, 0, 0, 380, 60, 0, 0 }, + { 125, 125, 0, 0, 40000, 200, 0, 0 }, + { 126, 126, 0, 0, 560, 86, 0, 0 }, + { 127, 127, 35, 0, 386, 160, 0, 0 }, + { 128, 128, 52, 0, 126, 26, 0, 0 }, + { 129, 129, 48, 0, 286, 126, 0, 0 }, + { 130, 130, 58, 0, 173, 93, 0, 0 }, + { 129, 129, 60, 0, 286, 126, 0, 0 }, + { 131, 131, 47, 0, 520, 200, 0, 0 }, + { 132, 132, 43, 0, 173, 93, 0, 0 }, + { 131, 131, 49, 0, 520, 200, 0, 0 }, + { 133, 133, 43, 0, 160, 80, 0, 0 }, + { 131, 131, 51, 0, 526, 206, 0, 0 }, + { 134, 134, 43, 0, 1860, 653, 0, 0 }, + { 131, 131, 54, 0, 520, 200, 0, 0 }, + { 131, 131, 57, 0, 520, 200, 0, 0 }, + { 135, 135, 72, 0, 1860, 633, 0, 0 }, + { 131, 131, 60, 0, 506, 200, 0, 0 }, + { 136, 136, 76, 0, 1566, 546, 0, 0 }, + { 137, 137, 84, 0, 1340, 466, 0, 0 }, + { 138, 138, 36, 0, 1220, 433, 0, 0 }, + { 139, 139, 65, 0, 293, 133, 0, 0 }, + { 140, 140, 84, 0, 1333, 460, 0, 0 }, + { 141, 141, 83, 0, 220, 113, 0, 0 }, + { 135, 135, 84, 0, 1366, 473, 0, 0 }, + { 142, 142, 24, 0, 1893, 633, 0, 0 }, + { 136, 136, 77, 0, 1586, 553, 0, 0 }, + { 143, 143, 60, 0, 173, 93, 0, 0 }, + { 144, 144, 65, 0, 213, 126, 0, 0 }, + { 145, 145, 59, 0, 173, 140, 0, 0 }, + { 146, 146, 51, 0, 173, 100, 0, 0 }, + { 147, 147, 45, 0, 260, 206, 0, 0 }, + { 148, 148, 71, 0, 433, 180, 0, 0 }, + { 149, 149, 60, 0, 280, 26, 0, 0 }, + { 150, 150, 58, 0, 500, 186, 0, 0 }, + { 151, 151, 53, 0, 513, 200, 0, 0 }, + { 152, 152, 64, 0, 220, 86, 0, 0 }, + { 153, 153, 71, 0, 106, 46, 0, 0 }, + { 154, 154, 61, 0, 993, 340, 0, 0 }, + { 155, 155, 61, 0, 1906, 640, 0, 0 }, + { 156, 156, 44, 0, 206, 86, 0, 0 }, + { 157, 157, 40, 0, 586, 140, 0, 0 }, + { 158, 158, 69, 0, 126, 140, 0, 0 }, + { 159, 159, 68, 0, 126, 140, 0, 0 }, + { 160, 160, 63, 0, 146, 166, 0, 0 }, + { 161, 161, 74, 0, 280, 100, 0, 0 }, + { 162, 162, 60, 0, 1026, 320, 0, 0 }, + { 163, 163, 80, 0, 226, 100, 0, 0 }, + { 164, 164, 64, 0, 2713, 913, 0, 0 }, + { 165, 165, 72, 0, 120, 66, 0, 0 }, + { 166, 166, 73, 0, 386, 80, 0, 0 }, + { 167, 167, 70, 0, 553, 306, 0, 0 }, + { 168, 168, 68, 0, 126, 140, 0, 0 }, + { 169, 169, 48, 0, 386, 373, 0, 0 }, + { 131, 131, 53, 0, 520, 206, 0, 0 }, + { 170, 170, 0, 0, 40000, 0, 0, 0 }, + { 171, 171, 0, 0, 40000, 73, 0, 0 }, + { 172, 173, 0, 4, 5886, 100, 0, 0 }, + { 174, 175, 0, 4, 6913, 93, 0, 0 }, + { 176, 177, 0, 4, 4873, 120, 0, 0 }, + { 178, 178, 0, 0, 40000, 0, 0, 0 }, + { 179, 180, 0, 4, 4626, 433, 0, 0 }, + { 181, 181, 0, 0, 2280, 746, 0, 0 }, + { 182, 182, 0, 0, 40000, 0, 0, 0 }, + { 183, 184, 0, 4, 620, 233, 0, 0 }, + { 185, 186, 0, 4, 4626, 1546, 0, 0 }, + { 187, 187, 0, 0, 1166, 400, 0, 0 }, + { 188, 189, 0, 4, 40000, 60, 0, 0 }, + { 190, 191, 0, 4, 40000, 60, 0, 0 }, + { 192, 193, 0, 4, 40000, 73, 0, 0 }, + { 194, 194, 0, 0, 40000, 73, 0, 0 }, + { 195, 196, 0, 4, 40000, 66, 0, 0 }, + { 197, 198, 0, 4, 40000, 86, 0, 0 }, + { 199, 200, 0, 4, 40000, 66, 0, 0 }, + { 201, 202, 0, 4, 3713, 100, 0, 0 }, + { 203, 204, 0, 4, 14686, 126, 0, 0 }, + { 205, 206, 0, 4, 9233, 153, 0, 0 }, + { 207, 208, 0, 4, 14640, 133, 0, 0 }, + { 209, 210, 0, 4, 4626, 106, 0, 0 }, + { 211, 212, 0, 4, 40000, 66, 0, 0 }, + { 213, 213, 0, 0, 40000, 73, 0, 0 }, + { 214, 215, 0, 4, 620, 100, 0, 0 }, + { 216, 217, 0, 4, 4060, 100, 0, 0 }, + { 218, 219, 0, 4, 14513, 193, 0, 0 }, + { 220, 221, 0, 4, 2813, 106, 0, 0 }, + { 222, 223, 0, 4, 493, 153, 0, 0 }, + { 224, 224, 0, 0, 40000, 0, 0, 0 }, + { 225, 226, 0, 4, 7993, 93, 0, 0 }, + { 227, 227, 0, 0, 40000, 0, 0, 0 }, + { 228, 228, 0, 0, 40000, 133, 0, 0 }, + { 229, 230, 0, 4, 713, 213, 0, 0 }, + { 231, 232, 0, 4, 40000, 146, 0, 0 }, + { 233, 234, 0, 4, 40000, 0, 0, 0 }, + { 235, 236, 0, 4, 993, 340, 0, 0 }, + { 235, 237, 0, 4, 3260, 1120, 0, 0 }, + { 46, 238, 0, 4, 6720, 1246, 0, 0 }, + { 239, 240, 0, 4, 40000, 140, 0, 0 }, + { 241, 242, 0, 4, 40000, 146, 0, 0 }, + { 243, 243, 0, 0, 40000, 100, 0, 0 }, + { 244, 244, 0, 0, 40000, 60, 0, 0 }, + { 245, 245, 0, 0, 40000, 73, 0, 0 }, + { 246, 247, 0, 4, 720, 106, 0, 0 }, + { 248, 249, 0, 4, 40000, 126, 0, 0 }, + { 250, 250, 0, 0, 40000, 0, 0, 0 }, + { 251, 251, 0, 0, 40000, 126, 0, 0 }, + { 252, 253, 0, 4, 40000, 66, 0, 0 }, + { 254, 255, 0, 4, 40000, 93, 0, 0 }, + { 256, 257, 0, 4, 40000, 73, 0, 0 }, + { 258, 259, 0, 4, 40000, 86, 0, 0 }, + { 260, 261, 0, 4, 40000, 93, 0, 0 }, + { 262, 263, 0, 4, 40000, 80, 0, 0 }, + { 264, 265, 0, 4, 40000, 200, 0, 0 }, + { 266, 267, 0, 4, 40000, 73, 0, 0 }, + { 268, 269, 0, 4, 40000, 80, 0, 0 }, + { 270, 271, 0, 4, 40000, 73, 0, 0 }, + { 272, 273, 0, 4, 40000, 126, 0, 0 }, + { 274, 275, 0, 4, 40000, 100, 0, 0 }, + { 276, 276, 0, 0, 40000, 113, 0, 0 }, + { 277, 278, 0, 4, 40000, 186, 0, 0 }, + { 279, 280, 0, 4, 40000, 160, 0, 0 }, + { 281, 282, 0, 4, 40000, 206, 0, 0 }, + { 283, 283, 0, 0, 40000, 80, 0, 0 }, + { 284, 285, 0, 4, 40000, 73, 0, 0 }, + { 286, 287, 0, 4, 40000, 73, 0, 0 }, + { 288, 288, 0, 0, 40000, 93, 0, 0 }, + { 289, 290, 0, 4, 40000, 66, 0, 0 }, + { 291, 292, 0, 4, 40000, 153, 0, 0 }, + { 293, 294, 0, 4, 40000, 153, 0, 0 }, + { 295, 296, 0, 4, 40000, 320, 0, 0 }, + { 88, 297, 0, 4, 40000, 1280, 0, 0 }, + { 298, 299, 0, 4, 40000, 266, 0, 0 }, + { 300, 301, 0, 4, 40000, 1180, 0, 0 }, + { 302, 302, 0, 0, 40000, 286, 0, 0 }, + { 303, 303, 0, 0, 40000, 140, 0, 0 }, + { 304, 304, 0, 0, 13246, 2473, 0, 0 }, + { 305, 306, 0, 4, 40000, 1073, 0, 0 }, + { 307, 307, 0, 0, 9233, 240, 0, 0 }, + { 308, 308, 0, 0, 1186, 406, 0, 0 }, + { 309, 309, 0, 0, 40000, 1306, 0, 0 }, + { 310, 310, 0, 0, 40000, 933, 0, 0 }, + { 311, 312, 0, 4, 9100, 240, 0, 0 }, + { 313, 314, 0, 4, 7280, 326, 0, 0 }, + { 315, 316, 0, 4, 3553, 326, 0, 0 }, + { 317, 318, 0, 4, 6966, 2206, 0, 0 }, + { 107, 319, 0, 4, 1160, 406, 0, 0 }, + { 108, 320, 0, 4, 40000, 66, 0, 0 }, + { 109, 321, 0, 4, 720, 213, 0, 0 }, + { 322, 323, 0, 4, 40000, 73, 0, 0 }, + { 324, 325, 0, 4, 613, 246, 0, 0 }, + { 326, 327, 0, 4, 1206, 386, 0, 0 }, + { 328, 328, 0, 0, 173, 106, 0, 0 }, + { 329, 329, 0, 0, 966, 333, 0, 0 }, + { 330, 331, 0, 4, 1906, 320, 0, 0 }, + { 332, 332, 0, 0, 3120, 73, 0, 0 }, + { 333, 333, 0, 0, 226, 73, 0, 0 }, + { 334, 334, 0, 0, 6600, 806, 0, 0 }, + { 335, 335, 0, 0, 273, 60, 0, 0 }, + { 336, 336, 0, 0, 12053, 660, 0, 0 }, + { 337, 337, 0, 0, 40000, 240, 0, 0 }, + { 338, 339, 0, 6, 6, 0, 0, 0 }, + { 340, 341, 0, 4, 560, 73, 0, 0 }, + { 342, 342, 35, 0, 40000, 0, 0, 0 }, + { 343, 343, 0, 0, 180, 100, 0, 0 }, + { 344, 344, 35, 0, 340, 146, 0, 0 }, + { 345, 345, 35, 0, 213, 33, 0, 0 }, + { 346, 346, 50, 0, 306, 20, 0, 0 }, + { 347, 347, 18, 0, 420, 146, 0, 0 }, + { 348, 348, 72, 0, 173, 86, 0, 0 }, + { 349, 349, 74, 0, 160, 93, 0, 0 }, + { 350, 350, 35, 0, 380, 146, 0, 0 }, + { 351, 351, 16, 0, 1206, 420, 0, 0 }, + { 352, 352, 0, 2, 6, 0, 0, 0 }, + { 353, 353, 38, 0, 200, 106, 0, 0 }, + { 354, 354, 38, 0, 346, 146, 0, 0 }, + { 355, 355, 31, 0, 406, 20, 0, 0 }, + { 355, 355, 35, 0, 406, 66, 0, 0 }, + { 355, 355, 38, 0, 406, 66, 0, 0 }, + { 355, 355, 41, 0, 406, 66, 0, 0 }, + { 355, 355, 45, 0, 306, 73, 0, 0 }, + { 355, 355, 50, 0, 306, 73, 0, 0 }, + { 356, 356, 36, 0, 1373, 493, 0, 0 }, + { 357, 357, 36, 0, 146, 33, 0, 0 }, + { 358, 358, 48, 0, 213, 86, 0, 0 }, + { 358, 358, 36, 0, 246, 86, 0, 0 }, + { 359, 359, 36, 0, 113, 53, 0, 0 }, + { 360, 360, 0, 0, 133, 40, 0, 0 }, + { 361, 361, 61, 0, 180, 26, 0, 0 }, + { 362, 362, 96, 0, 706, 266, 0, 0 }, + { 363, 363, 38, 0, 520, 193, 0, 0 }, + { 127, 127, 16, 0, 620, 233, 0, 0 }, + { 364, 365, 18, 4, 200, 26, 0, 0 }, + { 366, 366, 30, 0, 406, 246, 0, 0 }, + { 367, 368, 35, 4, 200, 100, 0, 0 }, + { 129, 129, 0, 0, 353, 153, 0, 0 }, + { 369, 369, 0, 0, 213, 13, 0, 0 }, + { 370, 370, 88, 0, 333, 113, 0, 0 }, + { 371, 371, 88, 0, 140, 73, 0, 0 }, + { 372, 372, 79, 0, 2540, 1040, 0, 0 }, + { 135, 135, 14, 0, 9213, 3066, 0, 0 }, + { 373, 373, 46, 0, 1093, 60, 0, 0 }, + { 374, 375,129, 4, 1193, 433, 0, 0 }, + { 376, 376, 58, 0, 1600, 726, 0, 0 }, + { 377, 377,164, 0, 526, 820, 0, 0 }, + { 378, 378,142, 0, 9153, 3073, 0, 0 }, + { 379, 379, 9, 0, 200, 100, 0, 0 }, + { 380, 381, 35, 4, 2340, 806, 0, 0 }, + { 382, 382, 28, 0, 1060, 120, 0, 0 }, + { 383, 383, 46, 0, 953, 20, 0, 0 }, + { 384, 384, 60, 0, 440, 160, 0, 0 }, + { 384, 384, 54, 0, 513, 180, 0, 0 }, + { 385, 385, 72, 0, 253, 120, 0, 0 }, + { 385, 385, 67, 0, 253, 113, 0, 0 }, + { 385, 385, 60, 0, 253, 106, 0, 0 }, + { 386, 386, 1, 0, 966, 613, 0, 0 }, + { 387, 387, 77, 0, 340, 86, 0, 0 }, + { 387, 387, 72, 0, 340, 86, 0, 0 }, + { 388, 388, 90, 0, 213, 86, 0, 0 }, + { 389, 389, 39, 0, 266, 73, 0, 0 }, + { 390, 390, 36, 0, 593, 73, 0, 0 }, + { 391, 392, 35, 4, 173, 46, 0, 0 }, + { 391, 393, 35, 4, 460, 66, 0, 0 }, + { 394, 394, 60, 0, 173, 20, 0, 0 }, + { 328, 328, 7, 0, 173, 106, 0, 0 }, + { 395, 395, 90, 0, 193, 20, 0, 0 }, + { 396, 396, 90, 0, 793, 40, 0, 0 }, + { 397, 397, 35, 0, 253, 86, 0, 0 }, + { 398, 399, 5, 4, 1913, 226, 0, 0 }, + { 400, 400,103, 0, 713, 273, 0, 0 }, + { 401, 401, 3, 0, 100, 26, 0, 0 }, + { 169, 169, 1, 0, 466, 413, 0, 0 }, + { 131, 131, 0, 0, 613, 226, 0, 0 }, + { 402, 402, 36, 0, 273, 53, 0, 0 }, + { 403, 403, 60, 0, 40000, 73, 0, 0 }, + { 404, 404, 37, 0, 1193, 426, 0, 0 }, + { 405, 405, 36, 0, 406, 20, 0, 0 }, + { 406, 406, 32, 0, 146, 73, 0, 0 }, + { 407, 407, 50, 0, 40000, 0, 0, 0 }, + { 408, 408, 50, 0, 793, 346, 0, 0 }, + { 409, 409, 83, 0, 120, 13, 0, 0 }, + { 410, 410, 72, 0, 433, 193, 0, 0 }, + { 148, 148, 59, 0, 513, 200, 0, 0 }, + { 411, 411, 64, 0, 173, 93, 0, 0 }, + { 411, 411, 60, 0, 173, 93, 0, 0 }, + { 412, 412, 72, 0, 160, 93, 0, 0 }, + { 412, 412, 62, 0, 173, 93, 0, 0 }, + { 413, 413, 83, 0, 773, 60, 0, 0 }, + { 414, 414, 0, 0, 40000, 80, 0, 0 }, + { 415, 415, 0, 0, 40000, 0, 0, 0 }, + { 416, 416, 0, 0, 40000, 73, 0, 0 }, + { 417, 417, 0, 0, 40000, 86, 0, 0 }, + { 418, 418, 0, 0, 40000, 0, 0, 0 }, + { 419, 419, 0, 0, 3440, 100, 0, 0 }, + { 420, 420, 0, 0, 3913, 420, 0, 0 }, + { 421, 421, 0, 0, 13620, 4640, 0, 0 }, + { 422, 422, 0, 0, 9233, 240, 0, 0 }, + { 423, 423, 0, 0, 633, 233, 0, 0 }, + { 424, 424, 0, 0, 4660, 1573, 0, 0 }, + { 425, 425, 0, 0, 4480, 1413, 0, 0 }, + { 426, 426, 0, 0, 40000, 0, 0, 0 }, + { 427, 427, 0, 0, 40000, 86, 0, 0 }, + { 428, 428, 60, 2, 6, 0, 0, 0 }, + { 429, 429, 73, 0, 593, 86, 0, 0 }, + { 429, 429, 74, 0, 593, 86, 0, 0 }, + { 429, 429, 80, 0, 593, 86, 0, 0 }, + { 429, 429, 84, 0, 593, 86, 0, 0 }, + { 429, 429, 92, 0, 520, 86, 0, 0 }, + { 430, 430, 81, 0, 786, 80, 0, 0 }, + { 430, 430, 83, 0, 786, 80, 0, 0 }, + { 430, 430, 95, 0, 680, 80, 0, 0 }, + { 431, 431, 35, 0, 593, 140, 0, 0 }, + { 432, 432, 60, 0, 213, 133, 0, 0 }, + { 357, 357, 59, 0, 113, 33, 0, 0 }, + { 432, 432, 44, 0, 213, 133, 0, 0 }, + { 433, 433, 41, 0, 713, 273, 0, 0 }, + { 434, 434, 97, 0, 113, 46, 0, 0 }, + { 433, 433, 44, 0, 513, 206, 0, 0 }, + { 433, 433, 48, 0, 506, 200, 0, 0 }, + { 435, 435, 96, 0, 700, 86, 0, 0 }, + { 433, 433, 51, 0, 520, 200, 0, 0 }, + { 433, 433, 54, 0, 513, 206, 0, 0 }, + { 436, 436, 40, 0, 1506, 793, 0, 0 }, + { 433, 433, 57, 0, 380, 160, 0, 0 }, + { 437, 437, 58, 0, 1600, 726, 0, 0 }, + { 438, 438, 97, 0, 233, 106, 0, 0 }, + { 439, 439, 50, 0, 186, 93, 0, 0 }, + { 437, 437, 60, 0, 1573, 713, 0, 0 }, + { 440, 440, 53, 0, 180, 73, 0, 0 }, + { 441, 441, 46, 0, 173, 126, 0, 0 }, + { 440, 440, 57, 0, 180, 40, 0, 0 }, + { 442, 442, 42, 0, 640, 240, 0, 0 }, + { 442, 442, 37, 0, 633, 233, 0, 0 }, + { 443, 443, 41, 0, 626, 240, 0, 0 }, + { 443, 443, 37, 0, 620, 233, 0, 0 }, + { 444, 444, 77, 0, 173, 40, 0, 0 }, + { 444, 444, 72, 0, 173, 40, 0, 0 }, + { 445, 445, 70, 0, 233, 100, 0, 0 }, + { 445, 445, 90, 0, 233, 93, 0, 0 }, + { 446, 446, 46, 0, 133, 73, 0, 0 }, + { 447, 447, 48, 0, 333, 73, 0, 0 }, + { 448, 448, 85, 0, 106, 33, 0, 0 }, + { 449, 449, 66, 0, 180, 26, 0, 0 }, + { 449, 449, 61, 0, 180, 26, 0, 0 }, + { 450, 450, 41, 0, 200, 66, 0, 0 }, + { 451, 451, 41, 0, 253, 66, 0, 0 }, + { 452, 452, 81, 0, 253, 26, 0, 0 }, + { 400, 400, 81, 0, 820, 306, 0, 0 }, + { 400, 400, 76, 0, 813, 300, 0, 0 }, + { 359, 359, 60, 0, 100, 40, 0, 0 }, + { 453, 453, 53, 0, 40000, 0, 0, 0 }, + { 454, 454, 0, 2, 6, 0, 0, 0 }, + { 455, 455, 0, 0, 200, 20, 0, 0 }, + { 456, 456, 0, 0, 4480, 100, 0, 0 }, + { 457, 457, 0, 0, 1180, 406, 0, 0 }, + { 458, 458, 0, 0, 40000, 86, 0, 0 }, + { 459, 459, 0, 0, 40000, 73, 0, 0 }, + { 460, 460, 0, 0, 3700, 66, 0, 0 }, + { 461, 461, 0, 0, 40000, 0, 0, 0 }, + { 462, 462, 0, 0, 6746, 2606, 0, 0 }, + { 463, 463, 0, 0, 40000, 213, 0, 0 }, + { 464, 464, 0, 0, 40000, 66, 0, 0 }, + { 465, 465, 0, 0, 40000, 100, 0, 0 }, + { 466, 466, 0, 0, 40000, 100, 0, 0 }, + { 467, 467, 0, 0, 5840, 806, 0, 0 }, + { 468, 468, 0, 0, 40000, 0, 0, 0 }, + { 469, 469, 0, 0, 40000, 0, 0, 0 }, + { 470, 470, 0, 0, 40000, 73, 0, 0 }, + { 471, 471, 0, 0, 40000, 133, 0, 0 }, + { 472, 472, 0, 0, 3320, 800, 0, 0 }, + { 473, 473, 0, 0, 40000, 173, 0, 0 }, + { 474, 474, 0, 0, 40000, 193, 0, 0 }, + { 475, 475, 0, 0, 2373, 800, 0, 0 }, + { 476, 476, 0, 0, 40000, 4986, 0, 0 }, + { 477, 477, 0, 0, 1180, 413, 0, 0 }, + { 478, 478, 0, 0, 3673, 1200, 0, 0 }, + { 479, 479, 0, 0, 973, 800, 0, 0 }, + { 480, 480, 0, 0, 7233, 2286, 0, 0 }, + { 481, 481, 0, 0, 40000, 73, 0, 0 }, + { 482, 482, 0, 0, 2526, 73, 0, 0 }, + { 483, 483, 0, 0, 393, 126, 0, 0 }, + { 484, 484, 0, 0, 40000, 200, 0, 0 }, + { 485, 485, 0, 0, 40000, 546, 0, 0 }, + { 486, 486, 0, 0, 1186, 413, 0, 0 }, + { 487, 487, 0, 0, 14166, 320, 0, 0 }, + { 488, 488, 0, 0, 8326, 646, 0, 0 }, + { 489, 489, 0, 0, 513, 206, 0, 0 }, + { 490, 490, 0, 0, 40000, 93, 0, 0 }, + { 491, 491, 50, 0, 1406, 353, 0, 0 }, + { 492, 492, 37, 0, 1040, 400, 0, 0 }, + { 493, 493, 39, 0, 406, 73, 0, 0 }, + { 494, 494, 39, 0, 3746, 860, 0, 0 }, + { 495, 495, 86, 0, 2133, 173, 0, 0 }, + { 496, 496, 43, 0, 140, 66, 0, 0 }, + { 127, 127, 24, 0, 513, 206, 0, 0 }, + { 127, 127, 29, 0, 520, 206, 0, 0 }, + { 497, 497, 50, 0, 340, 20, 0, 0 }, + { 498, 498, 30, 0, 5306, 1266, 0, 0 }, + { 498, 498, 33, 0, 3773, 886, 0, 0 }, + { 498, 498, 38, 0, 3746, 860, 0, 0 }, + { 498, 498, 42, 0, 3793, 906, 0, 0 }, + { 499, 499, 24, 0, 266, 160, 0, 0 }, + { 499, 499, 27, 0, 260, 153, 0, 0 }, + { 499, 499, 29, 0, 260, 153, 0, 0 }, + { 499, 499, 32, 0, 260, 153, 0, 0 }, + { 500, 500, 32, 0, 106, 26, 0, 0 }, + { 501, 501, 53, 0, 373, 186, 0, 0 }, + { 501, 501, 57, 0, 380, 193, 0, 0 }, + { 502, 502, 60, 0, 286, 133, 0, 0 }, + { 503, 503, 55, 0, 460, 126, 0, 0 }, + { 486, 486, 85, 0, 813, 293, 0, 0 }, + { 504, 504, 90, 0, 1580, 546, 0, 0 }, + { 505, 505, 84, 0, 246, 120, 0, 0 }, + { 506, 506, 48, 0, 826, 646, 0, 0 }, + { 507, 507, 48, 0, 266, 213, 0, 0 }, + { 132, 132, 72, 0, 126, 66, 0, 0 }, + { 508, 508, 72, 0, 106, 46, 0, 0 }, + { 509, 509, 72, 0, 100, 26, 0, 0 }, + { 510, 510, 63, 0, 1860, 633, 0, 0 }, + { 510, 510, 65, 0, 1853, 633, 0, 0 }, + { 511, 511, 79, 0, 1573, 553, 0, 0 }, + { 512, 512, 38, 0, 520, 793, 0, 0 }, + { 513, 513, 94, 0, 380, 160, 0, 0 }, + { 514, 514, 87, 0, 433, 306, 0, 0 }, + { 514, 514, 94, 0, 380, 273, 0, 0 }, + { 515, 515, 80, 0, 546, 273, 0, 0 }, + { 516, 516, 47, 0, 506, 200, 0, 0 }, + { 517, 517, 61, 0, 286, 133, 0, 0 }, + { 517, 517, 68, 0, 246, 120, 0, 0 }, + { 518, 518, 61, 0, 513, 206, 0, 0 }, + { 518, 518, 68, 0, 433, 180, 0, 0 }, + { 499, 499, 60, 0, 220, 133, 0, 0 }, + { 519, 519, 60, 0, 153, 46, 0, 0 }, + { 520, 520, 36, 0, 200, 20, 0, 0 }, + { 520, 520, 60, 0, 173, 20, 0, 0 }, + { 521, 521, 60, 0, 173, 20, 0, 0 }, + { 522, 522, 68, 0, 126, 26, 0, 0 }, + { 523, 523, 71, 0, 160, 186, 0, 0 }, + { 523, 523, 72, 0, 160, 186, 0, 0 }, + { 524, 524,101, 0, 966, 353, 0, 0 }, + { 525, 525, 36, 0, 3333, 480, 0, 0 }, + { 526, 526, 25, 0, 40000, 2293, 0, 0 }, + { 527, 527, 37, 0, 2106, 426, 0, 0 }, + { 528, 528, 36, 0, 720, 266, 0, 0 }, + { 528, 528, 41, 0, 713, 266, 0, 0 }, + { 529, 529, 84, 0, 173, 60, 0, 0 }, + { 530, 530, 54, 0, 40000, 0, 0, 0 }, + { 481, 481, 48, 0, 40000, 73, 0, 0 }, + { 531, 531, 0, 0, 10060, 1266, 0, 0 }, + { 532, 532, 0, 0, 4600, 606, 0, 0 }, + { 533, 533, 0, 0, 40000, 253, 0, 0 }, + { 534, 534, 0, 0, 40000, 73, 0, 0 }, + { 535, 535, 0, 0, 40000, 66, 0, 0 }, + { 536, 536, 0, 0, 40000, 80, 0, 0 }, + { 537, 537, 0, 0, 9413, 1393, 0, 0 }, + { 538, 538, 0, 0, 9000, 66, 0, 0 }, + { 539, 539, 0, 0, 40000, 0, 0, 0 }, + { 540, 540, 0, 0, 40000, 80, 0, 0 }, + { 541, 541, 0, 0, 40000, 120, 0, 0 }, + { 542, 542, 0, 0, 253, 73, 0, 0 }, + { 543, 543, 0, 0, 40000, 73, 0, 0 }, + { 544, 544, 0, 0, 18280, 800, 0, 0 }, + { 545, 545, 0, 0, 40000, 1133, 0, 0 }, + { 546, 546, 0, 0, 40000, 1226, 0, 0 }, + { 547, 547, 0, 0, 40000, 153, 0, 0 }, + { 135, 135, 49, 0, 3633, 1186, 0, 0 }, + { 548, 548, 35, 0, 2193, 80, 0, 0 }, + { 549, 549, 41, 0, 73, 26, 0, 0 }, + { 366, 366, 38, 0, 406, 246, 0, 0 }, + { 550, 550, 39, 0, 106, 20, 0, 0 }, + { 551, 551, 49, 0, 200, 133, 0, 0 }, + { 408, 408, 59, 0, 780, 326, 0, 0 }, + { 552, 552, 24, 0, 40000, 0, 0, 0 }, + { 552, 552, 27, 0, 40000, 0, 0, 0 }, + { 552, 552, 29, 0, 40000, 0, 0, 0 }, + { 552, 552, 32, 0, 40000, 0, 0, 0 }, + { 553, 553, 84, 0, 200, 33, 0, 0 }, + { 512, 512, 79, 0, 346, 460, 0, 0 }, + { 554, 554, 61, 0, 400, 126, 0, 0 }, + { 554, 554, 68, 0, 353, 120, 0, 0 }, + { 555, 555, 36, 0, 146, 86, 0, 0 }, + { 555, 555, 60, 0, 113, 53, 0, 0 }, + { 556, 556, 36, 0, 273, 53, 0, 0 }, + { 115, 115, 37, 0, 4580, 1513, 0, 0 }, + { 557, 557, 0, 0, 3806, 73, 0, 0 }, + { 558, 558, 0, 0, 40000, 0, 0, 0 }, + { 559, 559, 0, 0, 40000, 66, 0, 0 }, + { 560, 560, 0, 0, 5886, 133, 0, 0 }, + { 561, 561, 0, 0, 253, 26, 0, 0 }, + { 562, 562, 0, 0, 3246, 753, 0, 0 }, + { 563, 563, 0, 0, 40000, 100, 0, 0 }, + { 564, 564, 0, 0, 1620, 366, 0, 0 }, + { 565, 565, 0, 0, 40000, 0, 0, 0 }, + { 566, 566, 0, 0, 40000, 0, 0, 0 }, + { 567, 567, 0, 0, 40000, 0, 0, 0 }, + { 568, 568, 0, 0, 40000, 80, 0, 0 }, + { 569, 569, 0, 0, 760, 340, 0, 0 }, + { 570, 570, 0, 0, 40000, 0, 0, 0 }, + { 571, 571, 0, 0, 40000, 0, 0, 0 }, + { 572, 572, 0, 0, 40000, 0, 0, 0 }, + { 356, 356, 0, 0, 1893, 646, 0, 0 }, + { 573, 573, 0, 0, 40000, 93, 0, 0 }, + { 574, 574, 0, 0, 40000, 93, 0, 0 }, + { 575, 575, 0, 0, 40000, 200, 0, 0 }, + { 576, 576, 0, 0, 40000, 200, 0, 0 }, + { 577, 577, 0, 0, 40000, 126, 0, 0 }, + { 578, 578, 0, 0, 40000, 353, 0, 0 }, + { 579, 579, 0, 0, 40000, 346, 0, 0 }, + { 580, 580, 0, 0, 40000, 353, 0, 0 }, + { 581, 581, 0, 0, 40000, 100, 0, 0 }, + { 582, 582, 0, 0, 40000, 133, 0, 0 }, + { 583, 583, 0, 0, 2286, 713, 0, 0 }, + { 584, 584, 0, 0, 40000, 193, 0, 0 }, + { 585, 585, 0, 0, 40000, 0, 0, 0 }, + { 516, 516, 0, 0, 633, 240, 0, 0 }, + { 586, 586, 0, 0, 40000, 73, 0, 0 }, + { 587, 587, 0, 0, 40000, 73, 0, 0 }, + { 588, 588, 0, 0, 40000, 73, 0, 0 }, + { 498, 498, 26, 0, 5293, 1253, 0, 0 }, + { 494, 494, 35, 0, 3800, 913, 0, 0 }, + { 350, 350, 41, 0, 380, 153, 0, 0 }, + { 353, 353, 48, 0, 173, 100, 0, 0 }, + { 354, 354, 67, 0, 246, 120, 0, 0 }, + { 502, 502, 24, 0, 340, 146, 0, 0 }, + { 346, 346, 36, 0, 406, 73, 0, 0 }, + { 346, 346, 38, 0, 406, 20, 0, 0 }, + { 346, 346, 40, 0, 406, 73, 0, 0 }, + { 346, 346, 42, 0, 406, 20, 0, 0 }, + { 346, 346, 44, 0, 306, 20, 0, 0 }, + { 510, 510, 55, 0, 1866, 646, 0, 0 }, + { 346, 346, 46, 0, 306, 20, 0, 0 }, + { 136, 136, 80, 0, 1600, 573, 0, 0 }, + { 486, 486, 24, 0, 1193, 426, 0, 0 }, + { 153, 153, 50, 0, 106, 40, 0, 0 }, + { 346, 346, 24, 0, 540, 73, 0, 0 }, + { 516, 516, 31, 0, 626, 240, 0, 0 }, + { 498, 498, 35, 0, 3760, 880, 0, 0 }, + { 517, 517, 60, 0, 286, 133, 0, 0 }, + { 530, 530, 36, 0, 40000, 0, 0, 0 }, + { 530, 530, 48, 0, 40000, 0, 0, 0 }, + { 589, 589, 0, 0, 40000, 0, 0, 0 }, + { 139, 139, 76, 0, 253, 106, 0, 0 }, + { 156, 156, 48, 0, 206, 80, 0, 0 }, + { 157, 157, 48, 0, 426, 106, 0, 0 }, + { 165, 165, 69, 0, 120, 66, 0, 0 }, + { 167, 167, 75, 0, 546, 306, 0, 0 }, + { 590, 590, 0, 0, 40000, 0, 0, 0 }, + { 591, 591, 0, 0, 15486, 1580, 0, 0 }, + { 592, 592, 0, 0, 3446, 106, 0, 0 }, + { 593, 593, 0, 0, 1926, 146, 0, 0 }, + { 594, 594, 0, 0, 7293, 2380, 0, 0 }, + { 595, 595, 0, 0, 7613, 1566, 0, 0 }, + { 596, 596, 0, 0, 1153, 460, 0, 0 }, + { 597, 597, 0, 0, 1166, 400, 0, 0 }, + { 598, 598, 0, 0, 40000, 73, 0, 0 }, + { 599, 599, 0, 0, 40000, 766, 0, 0 }, + { 600, 600, 0, 0, 40000, 80, 0, 0 }, + { 601, 601, 0, 0, 1840, 513, 0, 0 }, + { 602, 602, 0, 0, 40000, 0, 0, 0 }, + { 603, 603, 0, 0, 4480, 733, 0, 0 }, + { 604, 604, 0, 0, 18226, 786, 0, 0 }, + { 605, 605, 0, 0, 4333, 233, 0, 0 }, + { 606, 606, 0, 0, 40000, 106, 0, 0 }, + { 607, 607, 0, 0, 40000, 366, 0, 0 }, + { 608, 608, 0, 0, 40000, 200, 0, 0 }, + { 609, 609, 0, 0, 713, 200, 0, 0 }, + { 610, 610, 0, 0, 8866, 1366, 0, 0 }, + { 611, 611, 0, 0, 2300, 73, 0, 0 }, + { 612, 612, 0, 0, 40000, 126, 0, 0 }, + { 613, 613, 0, 0, 40000, 1413, 0, 0 }, + { 614, 614, 0, 0, 40000, 333, 0, 0 }, + { 615, 615, 0, 0, 40000, 333, 0, 0 }, + { 616, 616, 0, 0, 40000, 26, 0, 0 }, + { 617, 617, 0, 0, 40000, 40, 0, 0 }, + { 618, 618, 0, 0, 4240, 353, 0, 0 }, + { 619, 619, 0, 0, 40000, 0, 0, 0 }, + { 620, 620, 0, 0, 40000, 73, 0, 0 }, + { 621, 621, 0, 0, 9020, 60, 0, 0 }, + { 622, 622, 0, 0, 3020, 0, 0, 0 }, + { 623, 623, 0, 0, 40000, 60, 0, 0 }, + { 624, 624, 0, 0, 40000, 73, 0, 0 }, + { 625, 625, 0, 0, 40000, 60, 0, 0 }, + { 626, 626, 0, 0, 40000, 53, 0, 0 }, + { 627, 627, 0, 0, 40000, 0, 0, 0 }, + { 628, 628, 0, 0, 40000, 66, 0, 0 }, + { 629, 629, 0, 0, 40000, 66, 0, 0 }, + { 630, 630, 0, 0, 5913, 426, 0, 0 }, + { 631, 631, 0, 0, 40000, 246, 0, 0 }, + { 632, 632, 0, 0, 40000, 206, 0, 0 }, + { 633, 633, 0, 0, 40000, 0, 0, 0 }, + { 634, 634, 0, 0, 2453, 780, 0, 0 }, + { 635, 635, 0, 0, 4740, 240, 0, 0 }, + { 636, 636, 0, 0, 1840, 353, 0, 0 }, + { 637, 637, 0, 0, 40000, 86, 0, 0 }, + { 638, 638, 0, 0, 3446, 1786, 0, 0 }, + { 346, 346, 0, 0, 540, 20, 0, 0 }, + { 639, 639, 0, 0, 7406, 2486, 0, 0 }, + { 404, 404, 0, 0, 1220, 466, 0, 0 }, + { 506, 506, 0, 0, 1000, 813, 0, 0 }, + { 639, 639, 60, 0, 2666, 913, 0, 0 }, + { 639, 639, 79, 0, 1366, 486, 0, 0 }, + { 640, 640, 65, 0, 2053, 646, 0, 0 }, + { 486, 486, 31, 0, 1206, 440, 0, 0 }, + { 486, 486, 36, 0, 1200, 433, 0, 0 }, + { 640, 640, 72, 0, 1713, 520, 0, 0 }, + { 136, 136, 79, 0, 1580, 560, 0, 0 }, + { 148, 148, 57, 0, 520, 206, 0, 0 }, + { 150, 150, 53, 0, 500, 193, 0, 0 }, + { 641, 641, 84, 0, 226, 66, 0, 0 }, + { 520, 520, 66, 0, 173, 20, 0, 0 }, + { 642, 642, 31, 0, 40000, 113, 0, 0 }, + { 642, 642, 29, 0, 40000, 113, 0, 0 }, + { 356, 356, 31, 0, 1366, 486, 0, 0 }, + { 356, 356, 19, 0, 1866, 633, 0, 0 }, + { 643, 643, 31, 0, 40000, 73, 0, 0 }, + { 643, 643, 29, 0, 40000, 73, 0, 0 }, + { 644, 644, 31, 0, 2286, 400, 0, 0 }, + { 644, 644, 35, 0, 2313, 420, 0, 0 }, + { 644, 644, 40, 0, 2353, 433, 0, 0 }, + { 644, 644, 47, 0, 1860, 346, 0, 0 }, + { 516, 516, 32, 0, 626, 240, 0, 0 }, + { 516, 516, 43, 0, 506, 200, 0, 0 }, + { 495, 495, 26, 0, 3180, 240, 0, 0 }, + { 495, 495, 44, 0, 2553, 206, 0, 0 }, + { 496, 496, 26, 0, 160, 73, 0, 0 }, + { 496, 496, 51, 0, 146, 66, 0, 0 }, + { 496, 496, 39, 0, 160, 73, 0, 0 }, + { 495, 495, 30, 0, 3180, 240, 0, 0 }, + { 645, 645, 44, 0, 1880, 653, 0, 0 }, + { 645, 645, 43, 0, 1886, 653, 0, 0 }, + { 646, 646, 0, 0, 2393, 833, 0, 0 }, + { 647, 647, 0, 0, 4693, 26, 0, 0 }, + { 648, 648, 0, 0, 2306, 773, 0, 0 }, + { 649, 649, 0, 0, 40000, 120, 0, 0 }, + { 650, 650, 0, 0, 40000, 66, 0, 0 }, + { 651, 651, 0, 0, 5866, 1206, 0, 0 }, + { 652, 652, 0, 0, 40000, 426, 0, 0 }, + { 653, 653, 0, 0, 1873, 633, 0, 0 }, + { 654, 654, 0, 0, 40000, 66, 0, 0 }, + { 655, 655, 0, 0, 40000, 73, 0, 0 }, + { 656, 656, 0, 0, 40000, 73, 0, 0 }, + { 657, 657, 0, 0, 40000, 0, 0, 0 }, + { 658, 658, 0, 0, 2040, 380, 0, 0 }, + { 659, 659, 0, 0, 40000, 73, 0, 0 }, + { 660, 660, 0, 0, 3720, 1260, 0, 0 }, + { 661, 661, 0, 0, 4080, 1046, 0, 0 }, + { 662, 662, 0, 0, 8693, 4666, 0, 0 }, + { 663, 663, 0, 0, 1926, 73, 0, 0 }, + { 664, 664, 0, 0, 8326, 646, 0, 0 }, + { 665, 665, 0, 0, 40000, 240, 0, 0 }, + { 666, 666, 0, 0, 40000, 226, 0, 0 }, + { 667, 667, 0, 0, 40000, 220, 0, 0 }, + { 668, 668, 0, 0, 40000, 0, 0, 0 }, + { 669, 669, 0, 0, 40000, 193, 0, 0 }, + { 670, 670, 0, 0, 880, 20, 0, 0 }, + { 671, 671, 0, 0, 4873, 120, 0, 0 }, + { 672, 672, 0, 0, 40000, 413, 0, 0 }, + { 673, 673, 0, 0, 700, 106, 0, 0 }, + { 674, 674, 0, 0, 700, 100, 0, 0 }, + { 675, 675, 0, 0, 40000, 126, 0, 0 }, + { 676, 676, 0, 0, 8113, 806, 0, 0 }, + { 677, 677, 0, 0, 8900, 80, 0, 0 }, + { 678, 678, 0, 0, 1893, 653, 0, 0 }, + { 679, 679, 0, 0, 3973, 206, 0, 0 }, + { 680, 680, 0, 0, 40000, 173, 0, 0 }, + { 681, 681, 0, 0, 40000, 73, 0, 0 }, + { 682, 682, 0, 0, 40000, 93, 0, 0 }, + { 683, 683, 0, 0, 1606, 640, 0, 0 }, + { 684, 684, 0, 0, 15486, 1580, 0, 0 }, + { 685, 685, 0, 0, 40000, 346, 0, 0 }, + { 686, 686, 0, 0, 40000, 786, 0, 0 }, + { 687, 687, 0, 0, 386, 240, 0, 0 }, + { 688, 688, 0, 0, 40000, 2066, 0, 0 }, + { 689, 689, 0, 0, 15453, 73, 0, 0 }, + { 690, 690, 0, 0, 1206, 240, 0, 0 }, + { 691, 691, 0, 0, 8866, 1366, 0, 0 }, + { 692, 692, 0, 0, 5913, 2253, 0, 0 }, + { 693, 693, 0, 0, 773, 106, 0, 0 }, + { 694, 694, 0, 0, 3793, 73, 0, 0 }, + { 695, 695, 0, 0, 40000, 73, 0, 0 }, + { 645, 645, 0, 0, 3633, 1180, 0, 0 }, + { 696, 696, 0, 0, 40000, 80, 0, 0 }, + { 697, 697, 0, 0, 40000, 0, 0, 0 }, + { 698, 698, 0, 0, 40000, 66, 0, 0 }, + { 699, 699, 0, 0, 40000, 66, 0, 0 }, + { 700, 700, 0, 0, 106, 53, 0, 0 }, + { 701, 701, 0, 0, 40000, 200, 0, 0 }, + { 702, 702, 0, 0, 3913, 73, 0, 0 }, + { 703, 703, 0, 0, 40000, 73, 0, 0 }, + { 704, 704, 0, 0, 40000, 73, 0, 0 }, + { 705, 705, 0, 0, 40000, 73, 0, 0 }, + { 706, 706, 0, 0, 40000, 66, 0, 0 }, + { 707, 707, 0, 0, 40000, 313, 0, 0 }, + { 708, 708, 0, 0, 40000, 100, 0, 0 }, + { 709, 709, 0, 0, 40000, 213, 0, 0 }, + { 710, 710, 0, 0, 40000, 53, 0, 0 }, + { 711, 711, 0, 0, 40000, 40, 0, 0 }, + { 712, 712, 0, 0, 40000, 73, 0, 0 }, + { 713, 713, 0, 0, 40000, 140, 0, 0 }, + { 714, 714, 0, 0, 40000, 606, 0, 0 }, + { 715, 715, 0, 0, 40000, 226, 0, 0 }, + { 716, 716, 0, 0, 3746, 1273, 0, 0 }, + { 717, 717, 0, 0, 40000, 80, 0, 0 }, + { 718, 718, 0, 0, 2360, 806, 0, 0 }, + { 719, 719, 0, 0, 1186, 420, 0, 0 }, + { 720, 720, 0, 0, 12533, 1953, 0, 0 }, + { 721, 721, 0, 0, 973, 1280, 0, 0 }, + { 722, 722, 0, 0, 40000, 426, 0, 0 }, + { 723, 723, 0, 0, 40000, 53, 0, 0 }, + { 724, 724, 0, 0, 40000, 66, 0, 0 }, + { 725, 725, 0, 0, 1246, 73, 0, 0 }, + { 726, 726, 0, 0, 3726, 1246, 0, 0 }, + { 727, 727, 0, 0, 2346, 813, 0, 0 }, + { 728, 728, 0, 0, 1206, 433, 0, 0 }, + { 507, 507, 0, 0, 306, 246, 0, 0 }, + { 512, 512, 0, 0, 526, 840, 0, 0 }, + { 729, 729, 0, 0, 14793, 4933, 0, 0 }, + { 730, 730, 0, 0, 14640, 4806, 0, 0 }, + { 731, 731, 0, 0, 5233, 633, 0, 0 }, + { 732, 732, 0, 0, 40000, 2513, 0, 0 }, + { 733, 733, 0, 0, 40000, 820, 0, 0 }, + { 734, 734, 0, 0, 40000, 0, 0, 0 }, + { 735, 735, 0, 0, 1726, 793, 0, 0 }, + { 736, 736, 0, 0, 513, 20, 0, 0 }, + { 737, 737, 0, 2, 6, 0, 0, 0 }, + { 738, 738, 38, 0, 1020, 413, 0, 0 }, + { 739, 739, 44, 0, 220, 33, 0, 0 }, + { 500, 500, 58, 0, 100, 26, 0, 0 }, + { 740, 740, 24, 0, 513, 206, 0, 0 }, + { 741, 741, 60, 0, 220, 26, 0, 0 }, + { 736, 736, 44, 0, 286, 20, 0, 0 }, + { 742, 742, 25, 0, 626, 246, 0, 0 }, + { 743, 743, 60, 0, 146, 86, 0, 0 }, + { 742, 742, 30, 0, 626, 240, 0, 0 }, + { 377, 377, 60, 0, 446, 626, 0, 0 }, + { 742, 742, 33, 0, 620, 226, 0, 0 }, + { 744, 744, 60, 0, 220, 113, 0, 0 }, + { 742, 742, 35, 0, 620, 233, 0, 0 }, + { 742, 742, 37, 0, 633, 246, 0, 0 }, + { 745, 745, 0, 0, 1880, 640, 0, 0 }, + { 742, 742, 40, 0, 640, 260, 0, 0 }, + { 746, 746,102, 0, 960, 300, 0, 0 }, + { 747, 747, 80, 0, 1106, 126, 0, 0 }, + { 377, 377, 0, 0, 500, 760, 0, 0 }, + { 748, 748, 56, 0, 100, 26, 0, 0 }, + { 749, 749, 0, 0, 973, 1300, 0, 0 }, + { 746, 746,100, 0, 960, 340, 0, 0 }, + { 750, 750, 40, 0, 626, 240, 0, 0 }, + { 750, 750, 35, 0, 626, 240, 0, 0 }, + { 751, 751, 29, 0, 206, 106, 0, 0 }, + { 750, 750, 29, 0, 633, 240, 0, 0 }, + { 750, 750, 22, 0, 640, 233, 0, 0 }, + { 500, 500, 0, 0, 106, 26, 0, 0 }, + { 752, 752, 0, 0, 206, 26, 0, 0 }, + { 753, 753, 84, 0, 166, 20, 0, 0 }, + { 754, 754, 84, 0, 1580, 553, 0, 0 }, + { 755, 755, 0, 0, 633, 233, 0, 0 }, + { 755, 755, 71, 0, 440, 180, 0, 0 }, + { 755, 755, 53, 0, 513, 200, 0, 0 }, + { 755, 755, 48, 0, 520, 206, 0, 0 }, + { 756, 756, 95, 0, 286, 20, 0, 0 }, + { 757, 757, 95, 0, 1880, 20, 0, 0 }, + { 758, 758, 0, 0, 14413, 333, 0, 0 }, + { 759, 759, 0, 0, 14453, 360, 0, 0 }, + { 760, 760, 0, 0, 14940, 353, 0, 0 }, + { 761, 761, 0, 0, 7286, 340, 0, 0 }, + { 762, 762, 0, 0, 14700, 60, 0, 0 }, + { 763, 763, 0, 0, 14506, 340, 0, 0 }, + { 764, 764, 0, 0, 14706, 200, 0, 0 }, + { 765, 765, 0, 0, 40000, 0, 0, 0 }, + { 766, 766, 0, 0, 2900, 426, 0, 0 }, + { 767, 767, 0, 0, 2986, 753, 0, 0 }, + { 768, 768, 0, 0, 1706, 680, 0, 0 }, + { 769, 769, 0, 0, 14646, 1253, 0, 0 }, + { 770, 770, 0, 0, 1713, 486, 0, 0 }, + { 771, 771, 0, 0, 966, 346, 0, 0 }, + { 772, 772, 0, 0, 3453, 766, 0, 0 }, + { 773, 773, 0, 0, 2866, 486, 0, 0 }, + { 774, 774, 0, 0, 40000, 73, 0, 0 }, + { 775, 775, 0, 0, 40000, 73, 0, 0 }, + { 776, 776, 0, 0, 40000, 166, 0, 0 }, + { 777, 777, 0, 0, 40000, 126, 0, 0 }, + { 778, 778, 0, 0, 40000, 113, 0, 0 }, + { 779, 779, 0, 0, 40000, 113, 0, 0 }, + { 780, 780, 0, 0, 40000, 93, 0, 0 }, + { 781, 781, 0, 0, 40000, 200, 0, 0 }, + { 782, 782, 0, 0, 7186, 93, 0, 0 }, + { 783, 783, 0, 0, 6406, 120, 0, 0 }, + { 784, 784, 0, 0, 40000, 0, 0, 0 }, + { 785, 785, 0, 0, 40000, 0, 0, 0 }, + { 786, 786, 0, 0, 1220, 73, 0, 0 }, + { 787, 787, 0, 0, 40000, 0, 0, 0 }, + { 788, 788, 0, 0, 17566, 66, 0, 0 }, + { 789, 789, 0, 0, 2333, 26, 0, 0 }, + { 790, 790, 0, 0, 4560, 153, 0, 0 }, + { 791, 791, 0, 0, 40000, 0, 0, 0 }, + { 792, 792, 0, 0, 40000, 0, 0, 0 }, + { 793, 793, 0, 0, 40000, 0, 0, 0 }, + { 794, 794, 0, 0, 2506, 126, 0, 0 }, + { 795, 795, 0, 0, 2513, 126, 0, 0 }, + { 796, 796, 0, 0, 40000, 0, 0, 0 }, + { 797, 797, 0, 0, 3386, 80, 0, 0 }, + { 798, 798, 0, 0, 40000, 100, 0, 0 }, + { 799, 799, 0, 0, 40000, 100, 0, 0 }, + { 800, 800, 0, 0, 40000, 120, 0, 0 }, + { 801, 801, 0, 0, 40000, 0, 0, 0 }, + { 802, 802, 0, 0, 40000, 200, 0, 0 }, + { 803, 803, 0, 0, 1080, 180, 0, 0 }, + { 804, 804, 0, 0, 3620, 1166, 0, 0 }, + { 805, 805, 0, 0, 1186, 393, 0, 0 }, + { 806, 806, 0, 0, 40000, 213, 0, 0 }, + { 807, 807, 0, 0, 40000, 426, 0, 0 }, + { 808, 808, 0, 0, 40000, 146, 0, 0 }, + { 809, 809, 0, 0, 40000, 146, 0, 0 }, + { 810, 810, 0, 0, 40000, 60, 0, 0 }, + { 811, 811, 0, 0, 40000, 113, 0, 0 }, + { 812, 812, 0, 0, 40000, 93, 0, 0 }, + { 813, 813, 0, 0, 1186, 153, 0, 0 }, + { 814, 814, 0, 0, 40000, 0, 0, 0 }, + { 815, 815, 0, 0, 40000, 80, 0, 0 }, + { 816, 816, 0, 0, 40000, 80, 0, 0 }, + { 817, 817, 0, 0, 40000, 46, 0, 0 }, + { 818, 818, 0, 0, 40000, 0, 0, 0 }, + { 819, 819, 0, 0, 40000, 66, 0, 0 }, + { 820, 820, 0, 0, 40000, 126, 0, 0 }, + { 821, 821, 0, 0, 40000, 213, 0, 0 }, + { 822, 822, 0, 0, 40000, 80, 0, 0 }, + { 823, 823, 0, 0, 40000, 73, 0, 0 }, + { 824, 824, 0, 0, 40000, 73, 0, 0 }, + { 825, 825, 0, 0, 40000, 100, 0, 0 }, + { 826, 826, 0, 0, 40000, 93, 0, 0 }, + { 827, 827, 0, 0, 40000, 73, 0, 0 }, + { 828, 828, 0, 0, 40000, 73, 0, 0 }, + { 829, 829, 0, 0, 40000, 80, 0, 0 }, + { 830, 830, 0, 0, 40000, 80, 0, 0 }, + { 831, 831, 0, 0, 40000, 80, 0, 0 }, + { 832, 832, 0, 0, 40000, 73, 0, 0 }, + { 833, 833, 0, 0, 40000, 80, 0, 0 }, + { 834, 834, 0, 0, 40000, 86, 0, 0 }, + { 835, 835, 0, 0, 40000, 100, 0, 0 }, + { 836, 836, 0, 0, 40000, 100, 0, 0 }, + { 837, 837, 0, 0, 40000, 140, 0, 0 }, + { 838, 838, 0, 0, 40000, 73, 0, 0 }, + { 839, 839, 0, 0, 40000, 0, 0, 0 }, + { 840, 840, 0, 0, 40000, 93, 0, 0 }, + { 841, 841, 0, 0, 40000, 0, 0, 0 }, + { 842, 842, 0, 0, 40000, 0, 0, 0 }, + { 843, 843, 0, 0, 40000, 73, 0, 0 }, + { 844, 844, 0, 0, 40000, 66, 0, 0 }, + { 845, 845, 0, 0, 40000, 0, 0, 0 }, + { 846, 846, 0, 0, 40000, 193, 0, 0 }, + { 847, 847, 0, 0, 40000, 340, 0, 0 }, + { 848, 848, 0, 0, 40000, 233, 0, 0 }, + { 849, 849, 0, 0, 40000, 80, 0, 0 }, + { 850, 850, 0, 0, 40000, 186, 0, 0 }, + { 851, 851, 0, 0, 9973, 426, 0, 0 }, + { 852, 852, 0, 0, 40000, 200, 0, 0 }, + { 853, 853, 0, 0, 40000, 400, 0, 0 }, + { 854, 854, 0, 0, 14633, 200, 0, 0 }, + { 855, 855, 0, 0, 40000, 333, 0, 0 }, + { 856, 856, 0, 0, 4620, 800, 0, 0 }, + { 857, 857, 0, 0, 8940, 386, 0, 0 }, + { 858, 858, 0, 0, 8966, 740, 0, 0 }, + { 859, 859, 0, 0, 40000, 273, 0, 0 }, + { 860, 860, 0, 0, 40000, 126, 0, 0 }, + { 861, 861, 0, 0, 40000, 400, 0, 0 }, + { 862, 862, 0, 0, 4480, 213, 0, 0 }, + { 863, 863, 0, 0, 633, 100, 0, 0 }, + { 864, 864, 0, 0, 3740, 353, 0, 0 }, + { 865, 865, 0, 0, 2333, 406, 0, 0 }, + { 866, 866, 0, 0, 1933, 566, 0, 0 }, + { 867, 867, 0, 0, 40000, 93, 0, 0 }, + { 868, 868, 0, 0, 40000, 106, 0, 0 }, + { 869, 869, 0, 0, 40000, 100, 0, 0 }, + { 870, 870, 0, 0, 3093, 240, 0, 0 }, + { 871, 871, 0, 0, 513, 93, 0, 0 }, + { 872, 872, 0, 0, 700, 180, 0, 0 }, + { 361, 361, 0, 0, 373, 40, 0, 0 }, + { 873, 873, 0, 0, 1046, 446, 0, 0 }, + { 874, 874, 0, 0, 1886, 520, 0, 0 }, + { 875, 875, 0, 0, 1226, 366, 0, 0 }, + { 876, 876, 0, 0, 4193, 73, 0, 0 }, + { 877, 877, 0, 0, 826, 120, 0, 0 }, + { 878, 878, 0, 0, 280, 146, 0, 0 }, + { 879, 879, 0, 0, 5266, 806, 0, 0 }, + { 880, 880, 0, 0, 386, 80, 0, 0 }, + { 881, 881, 0, 0, 40000, 100, 0, 0 }, + { 882, 882, 0, 0, 40000, 413, 0, 0 }, + { 883, 883, 0, 0, 40000, 0, 0, 0 }, + { 884, 884, 36, 0, 233, 80, 0, 0 }, + { 885, 885, 48, 0, 193, 93, 0, 0 }, + { 885, 885, 36, 0, 226, 100, 0, 0 }, + { 886, 886, 36, 0, 113, 53, 0, 0 }, + { 887, 887, 32, 0, 133, 40, 0, 0 }, + { 767, 767, 96, 0, 1760, 480, 0, 0 }, + { 888, 888, 30, 0, 246, 40, 0, 0 }, + { 889, 889, 35, 0, 420, 140, 0, 0 }, + { 890, 890, 60, 0, 240, 60, 0, 0 }, + { 884, 884, 59, 0, 146, 20, 0, 0 }, + { 890, 890, 44, 0, 240, 60, 0, 0 }, + { 891, 891, 41, 0, 713, 273, 0, 0 }, + { 892, 892, 47, 0, 173, 93, 0, 0 }, + { 891, 891, 44, 0, 513, 206, 0, 0 }, + { 891, 891, 48, 0, 506, 200, 0, 0 }, + { 893, 893, 62, 0, 1926, 93, 0, 0 }, + { 891, 891, 51, 0, 520, 200, 0, 0 }, + { 891, 891, 54, 0, 513, 206, 0, 0 }, + { 894, 894, 40, 0, 1280, 793, 0, 0 }, + { 891, 891, 57, 0, 380, 160, 0, 0 }, + { 895, 895, 97, 0, 233, 106, 0, 0 }, + { 896, 896, 50, 0, 220, 93, 0, 0 }, + { 376, 376, 60, 0, 1573, 713, 0, 0 }, + { 897, 897, 53, 0, 126, 73, 0, 0 }, + { 898, 898, 46, 0, 173, 133, 0, 0 }, + { 897, 897, 57, 0, 126, 33, 0, 0 }, + { 899, 899, 42, 0, 626, 233, 0, 0 }, + { 899, 899, 37, 0, 633, 240, 0, 0 }, + { 900, 900, 41, 0, 626, 240, 0, 0 }, + { 900, 900, 37, 0, 626, 240, 0, 0 }, + { 871, 871, 77, 0, 173, 40, 0, 0 }, + { 871, 871, 72, 0, 173, 40, 0, 0 }, + { 388, 388, 70, 0, 213, 86, 0, 0 }, + { 901, 901, 39, 0, 260, 26, 0, 0 }, + { 902, 902, 36, 0, 1093, 73, 0, 0 }, + { 903, 903, 46, 0, 120, 73, 0, 0 }, + { 904, 904, 48, 0, 766, 80, 0, 0 }, + { 905, 905, 85, 0, 126, 26, 0, 0 }, + { 361, 361, 66, 0, 180, 26, 0, 0 }, + { 906, 906, 41, 0, 193, 73, 0, 0 }, + { 907, 907, 41, 0, 333, 106, 0, 0 }, + { 908, 908, 81, 0, 160, 26, 0, 0 }, + { 400, 400, 10, 0, 1186, 413, 0, 0 }, + { 886, 886, 60, 0, 100, 40, 0, 0 }, + { 873, 873, 53, 0, 846, 360, 0, 0 }, + { 909, 909, 0, 0, 5593, 340, 0, 0 }, + { 910, 910, 0, 0, 14646, 346, 0, 0 }, + { 911, 911, 0, 0, 6826, 280, 0, 0 }, + { 912, 912, 0, 0, 7000, 306, 0, 0 }, + { 913, 913, 0, 0, 8793, 133, 0, 0 }, + { 914, 914, 0, 0, 14680, 346, 0, 0 }, + { 915, 915, 0, 0, 7246, 126, 0, 0 }, + { 916, 916, 0, 0, 40000, 0, 0, 0 }, + { 917, 917, 0, 0, 1866, 433, 0, 0 }, + { 362, 362, 0, 0, 1106, 340, 0, 0 }, + { 918, 918, 0, 0, 1053, 273, 0, 0 }, + { 919, 919, 0, 0, 14513, 1213, 0, 0 }, + { 920, 920, 0, 0, 1886, 646, 0, 0 }, + { 921, 921, 0, 0, 926, 313, 0, 0 }, + { 922, 922, 0, 0, 2340, 806, 0, 0 }, + { 923, 923, 0, 0, 2966, 553, 0, 0 }, + { 924, 924, 0, 0, 40000, 66, 0, 0 }, + { 925, 925, 0, 0, 40000, 73, 0, 0 }, + { 926, 926, 0, 0, 40000, 0, 0, 0 }, + { 927, 927, 0, 0, 40000, 126, 0, 0 }, + { 928, 928, 0, 0, 40000, 113, 0, 0 }, + { 929, 929, 0, 0, 40000, 113, 0, 0 }, + { 930, 930, 0, 0, 40000, 93, 0, 0 }, + { 931, 931, 0, 0, 40000, 113, 0, 0 }, + { 932, 932, 0, 0, 7200, 86, 0, 0 }, + { 933, 933, 0, 0, 5373, 106, 0, 0 }, + { 934, 934, 0, 0, 40000, 0, 0, 0 }, + { 935, 935, 0, 0, 40000, 0, 0, 0 }, + { 936, 936, 0, 0, 2380, 73, 0, 0 }, + { 937, 937, 0, 0, 40000, 0, 0, 0 }, + { 938, 938, 0, 0, 40000, 0, 0, 0 }, + { 939, 939, 0, 0, 6013, 53, 0, 0 }, + { 940, 940, 0, 0, 3713, 126, 0, 0 }, + { 941, 941, 0, 0, 17566, 26, 0, 0 }, + { 942, 942, 0, 0, 40000, 0, 0, 0 }, + { 943, 943, 0, 0, 40000, 0, 0, 0 }, + { 944, 944, 0, 0, 2506, 126, 0, 0 }, + { 945, 945, 0, 0, 3733, 73, 0, 0 }, + { 946, 946, 0, 0, 40000, 0, 0, 0 }, + { 947, 947, 0, 0, 3386, 80, 0, 0 }, + { 948, 948, 0, 0, 40000, 100, 0, 0 }, + { 949, 949, 0, 0, 40000, 100, 0, 0 }, + { 950, 950, 0, 0, 40000, 113, 0, 0 }, + { 951, 951, 0, 0, 40000, 0, 0, 0 }, + { 952, 952, 0, 0, 40000, 200, 0, 0 }, + { 953, 953, 0, 0, 1140, 213, 0, 0 }, + { 954, 954, 0, 0, 2140, 400, 0, 0 }, + { 955, 955, 0, 0, 813, 240, 0, 0 }, + { 956, 956, 0, 0, 40000, 100, 0, 0 }, + { 957, 957, 0, 0, 40000, 426, 0, 0 }, + { 958, 958, 0, 0, 40000, 0, 0, 0 }, + { 959, 959, 0, 0, 40000, 146, 0, 0 }, + { 960, 960, 0, 0, 40000, 120, 0, 0 }, + { 961, 961, 0, 0, 40000, 93, 0, 0 }, + { 962, 962, 0, 0, 1193, 153, 0, 0 }, + { 963, 963, 0, 0, 40000, 46, 0, 0 }, + { 964, 964, 0, 0, 40000, 80, 0, 0 }, + { 965, 965, 0, 0, 40000, 80, 0, 0 }, + { 966, 966, 0, 0, 40000, 20, 0, 0 }, + { 967, 967, 0, 0, 40000, 0, 0, 0 }, + { 968, 968, 0, 0, 40000, 93, 0, 0 }, + { 969, 969, 0, 0, 40000, 86, 0, 0 }, + { 970, 970, 0, 0, 40000, 213, 0, 0 }, + { 971, 971, 0, 0, 40000, 80, 0, 0 }, + { 972, 972, 0, 0, 40000, 73, 0, 0 }, + { 973, 973, 0, 0, 40000, 0, 0, 0 }, + { 974, 974, 0, 0, 40000, 93, 0, 0 }, + { 975, 975, 0, 0, 40000, 73, 0, 0 }, + { 976, 976, 0, 0, 40000, 73, 0, 0 }, + { 977, 977, 0, 0, 40000, 66, 0, 0 }, + { 978, 978, 0, 0, 40000, 66, 0, 0 }, + { 979, 979, 0, 0, 40000, 100, 0, 0 }, + { 980, 980, 0, 0, 40000, 73, 0, 0 }, + { 981, 981, 0, 0, 40000, 73, 0, 0 }, + { 982, 982, 0, 0, 40000, 80, 0, 0 }, + { 983, 983, 0, 0, 40000, 100, 0, 0 }, + { 984, 984, 0, 0, 40000, 100, 0, 0 }, + { 985, 985, 0, 0, 40000, 100, 0, 0 }, + { 986, 986, 0, 0, 40000, 80, 0, 0 }, + { 987, 987, 0, 0, 40000, 73, 0, 0 }, + { 988, 988, 0, 0, 40000, 0, 0, 0 }, + { 989, 989, 0, 0, 40000, 86, 0, 0 }, + { 990, 990, 0, 0, 40000, 0, 0, 0 }, + { 991, 991, 0, 0, 40000, 0, 0, 0 }, + { 992, 992, 0, 0, 40000, 80, 0, 0 }, + { 993, 993, 0, 0, 40000, 86, 0, 0 }, + { 994, 994, 0, 0, 40000, 0, 0, 0 }, + { 995, 995, 0, 0, 40000, 0, 0, 0 }, + { 996, 996, 0, 0, 40000, 333, 0, 0 }, + { 997, 997, 0, 0, 40000, 180, 0, 0 }, + { 998, 998, 0, 0, 40000, 80, 0, 0 }, + { 999, 999, 0, 0, 40000, 120, 0, 0 }, + {1000,1000, 0, 0, 10006, 460, 0, 0 }, + {1001,1001, 0, 0, 40000, 186, 0, 0 }, + {1002,1002, 0, 0, 40000, 400, 0, 0 }, + {1003,1003, 0, 0, 20333, 260, 0, 0 }, + {1004,1004, 0, 0, 40000, 373, 0, 0 }, + {1005,1005, 0, 0, 4520, 400, 0, 0 }, + {1006,1006, 0, 0, 8213, 306, 0, 0 }, + {1007,1007, 0, 0, 8646, 360, 0, 0 }, + {1008,1008, 0, 0, 40000, 160, 0, 0 }, + {1009,1009, 0, 0, 40000, 133, 0, 0 }, + {1010,1010, 0, 0, 40000, 400, 0, 0 }, + {1011,1011, 0, 0, 4473, 193, 0, 0 }, + {1012,1012, 0, 0, 1813, 120, 0, 0 }, + {1013,1013, 0, 0, 3726, 353, 0, 0 }, + {1014,1014, 0, 0, 4400, 373, 0, 0 }, + {1015,1015, 0, 0, 953, 166, 0, 0 }, + {1016,1016, 0, 0, 40000, 73, 0, 0 }, + {1017,1017, 0, 0, 40000, 100, 0, 0 }, + {1018,1018, 0, 0, 40000, 100, 0, 0 }, + {1019,1019, 0, 0, 3100, 240, 0, 0 }, + { 444, 444, 0, 0, 513, 93, 0, 0 }, + {1020,1020, 0, 0, 626, 180, 0, 0 }, + { 449, 449, 0, 0, 373, 80, 0, 0 }, + { 453, 453, 0, 0, 40000, 0, 0, 0 }, + {1021,1021, 0, 0, 1020, 340, 0, 0 }, + {1022,1022, 0, 0, 1200, 366, 0, 0 }, + {1023,1023, 0, 0, 4193, 73, 0, 0 }, + {1024,1024, 0, 0, 820, 120, 0, 0 }, + {1025,1025, 0, 0, 680, 213, 0, 0 }, + {1026,1026, 0, 0, 5260, 806, 0, 0 }, + {1027,1027, 0, 0, 9193, 86, 0, 0 }, + {1028,1028, 0, 0, 40000, 100, 0, 0 }, + {1029,1029, 0, 0, 40000, 426, 0, 0 }, + {1030,1030, 0, 0, 40000, 260, 0, 0 }, + {1031,1031, 0, 0, 3480, 66, 0, 0 }, + {1032,1032, 32, 0, 133, 46, 0, 0 }, + {1033,1033, 30, 0, 200, 40, 0, 0 }, + {1034,1034, 96, 0, 146, 73, 0, 0 }, + {1035,1035, 60, 0, 553, 186, 0, 0 }, + {1036,1036, 0, 0, 13193, 260, 0, 0 }, + {1037,1037, 0, 0, 40000, 100, 0, 0 }, + {1038,1038, 0, 0, 7980, 66, 0, 0 }, + {1039,1039, 0, 0, 40000, 0, 0, 0 }, + {1040,1040, 0, 0, 980, 340, 0, 0 }, + {1041,1041, 0, 0, 7413, 2480, 0, 0 }, + {1042,1042, 0, 0, 2906, 520, 0, 0 }, + {1043,1043, 0, 0, 40000, 73, 0, 0 }, + {1044,1044, 0, 0, 40000, 53, 0, 0 }, + {1045,1045, 0, 0, 40000, 113, 0, 0 }, + {1046,1046, 0, 0, 5380, 113, 0, 0 }, + {1047,1047, 0, 0, 40000, 0, 0, 0 }, + {1048,1048, 0, 0, 2366, 73, 0, 0 }, + {1049,1049, 0, 0, 40000, 0, 0, 0 }, + {1050,1050, 0, 0, 18293, 80, 0, 0 }, + {1051,1051, 0, 0, 18466, 146, 0, 0 }, + {1052,1052, 0, 0, 9220, 73, 0, 0 }, + {1053,1053, 0, 0, 40000, 240, 0, 0 }, + {1054,1054, 0, 0, 40000, 0, 0, 0 }, + {1055,1055, 0, 0, 1086, 126, 0, 0 }, + {1056,1056, 0, 0, 3766, 73, 0, 0 }, + {1057,1057, 0, 0, 1186, 226, 0, 0 }, + {1058,1058, 0, 0, 3373, 73, 0, 0 }, + {1059,1059, 0, 0, 40000, 246, 0, 0 }, + {1060,1060, 0, 0, 340, 220, 0, 0 }, + {1061,1061, 0, 0, 1186, 386, 0, 0 }, + {1062,1062, 0, 0, 40000, 253, 0, 0 }, + {1063,1063, 0, 0, 40000, 440, 0, 0 }, + {1064,1064, 0, 0, 40000, 46, 0, 0 }, + {1065,1065, 0, 0, 40000, 80, 0, 0 }, + {1066,1066, 0, 0, 40000, 126, 0, 0 }, + {1067,1067, 0, 0, 40000, 133, 0, 0 }, + {1068,1068, 0, 0, 40000, 93, 0, 0 }, + {1069,1069, 0, 0, 40000, 86, 0, 0 }, + {1070,1070, 0, 0, 40000, 93, 0, 0 }, + {1071,1071, 0, 0, 40000, 66, 0, 0 }, + {1072,1072, 0, 0, 40000, 93, 0, 0 }, + {1073,1073, 0, 0, 40000, 73, 0, 0 }, + {1074,1074, 0, 0, 40000, 173, 0, 0 }, + {1075,1075, 0, 0, 586, 193, 0, 0 }, + {1076,1076, 0, 0, 40000, 146, 0, 0 }, + {1077,1077, 0, 0, 18460, 73, 0, 0 }, + {1078,1078, 0, 0, 846, 93, 0, 0 }, + {1079,1079, 0, 0, 40000, 0, 0, 0 }, + {1080,1080, 0, 0, 40000, 86, 0, 0 }, + {1081,1081, 0, 0, 40000, 0, 0, 0 }, + {1082,1082, 0, 0, 40000, 353, 0, 0 }, + {1083,1083, 0, 0, 40000, 300, 0, 0 }, + {1084,1084, 0, 0, 40000, 320, 0, 0 }, + {1085,1085, 0, 0, 9920, 1553, 0, 0 }, + {1086,1086, 0, 0, 40000, 386, 0, 0 }, + {1087,1087, 0, 0, 40000, 0, 0, 0 }, + {1088,1088, 0, 0, 9980, 873, 0, 0 }, + {1089,1089, 0, 0, 40000, 386, 0, 0 }, + {1090,1090, 0, 0, 966, 126, 0, 0 }, + {1091,1091, 0, 0, 40000, 820, 0, 0 }, + {1092,1092, 0, 0, 8620, 366, 0, 0 }, + {1093,1093, 0, 0, 40000, 826, 0, 0 }, + {1094,1094, 0, 0, 40000, 433, 0, 0 }, + {1095,1095, 0, 0, 633, 73, 0, 0 }, + {1096,1096, 0, 0, 3693, 126, 0, 0 }, + {1097,1097, 0, 0, 40000, 0, 0, 0 }, + {1098,1098, 0, 0, 40000, 153, 0, 0 }, + {1099,1099, 0, 0, 40000, 0, 0, 0 }, + {1100,1100, 0, 0, 40000, 0, 0, 0 }, + {1101,1101, 0, 0, 40000, 306, 0, 0 }, + {1102,1102, 0, 0, 3666, 3093, 0, 0 }, + {1103,1103, 0, 0, 1873, 653, 0, 0 }, + {1104,1104, 0, 0, 40000, 0, 0, 0 }, + {1105,1105, 0, 0, 11293, 886, 0, 0 }, + {1106,1106, 0, 0, 40000, 546, 0, 0 }, + { 430, 430, 0, 0, 1146, 80, 0, 0 }, + {1107,1107, 35, 0, 580, 80, 0, 0 }, + {1090,1090, 77, 0, 280, 60, 0, 0 }, + {1090,1090, 72, 0, 280, 60, 0, 0 }, + {1108,1108, 0, 0, 10180, 600, 0, 0 }, + {1109,1109, 0, 0, 10053, 353, 0, 0 }, + {1110,1111, 0, 1, 9940, 480, 0, 0 }, + {1112,1113, 0, 1, 10620, 473, 0, 0.03125 }, + {1114,1114, 0, 0, 40000, 0, 0, 0 }, + {1115,1116, 0, 1, 9833, 220, 0, 0 }, + {1117,1117, 0, 0, 10286, 473, 0, 0 }, + {1118,1118, 0, 0, 7686, 93, 0, 0 }, + {1119,1119, 0, 0, 7220, 613, 0, 0 }, + {1120,1120, 0, 0, 11513, 1666, 0, 0 }, + {1121,1121, 0, 0, 5200, 1700, 0, 0 }, + {1122,1122, 0, 0, 10173, 626, 0, 0 }, + {1123,1123, 0, 0, 1206, 380, 0, 0 }, + {1124,1124, 0, 0, 1953, 866, 0, 0 }, + {1125,1125, 0, 0, 4686, 1586, 0, 0 }, + {1126,1126, 0, 0, 3786, 893, 0, 0 }, + {1127,1127, 0, 0, 40000, 126, 0, 0 }, + {1128,1128, 0, 0, 40000, 120, 0, 0 }, + {1129,1130, 0, 1, 40000, 146, 0, 0.15625 }, + {1131,1131, 0, 0, 40000, 433, 0, 0 }, + {1132,1132, 0, 0, 40000, 133, 0, 0 }, + {1133,1134, 0, 1, 40000, 126, 0, -0.046875 }, + {1135,1135, 0, 0, 40000, 113, 0, 0 }, + {1136,1137, 0, 1, 40000, 253, 0, 2.5e-05 }, + {1138,1138, 0, 0, 18440, 240, 0, 0 }, + {1139,1139, 0, 0, 5213, 886, 0, 0 }, + {1140,1140, 0, 0, 1446, 113, 0, 0 }, + {1141,1141, 0, 0, 5233, 106, 0, 0 }, + {1142,1142, 0, 0, 5286, 266, 0, 0 }, + {1143,1143, 0, 0, 40000, 66, 0, 0 }, + {1144,1144, 0, 0, 40000, 66, 0, 0 }, + {1145,1145, 0, 0, 10593, 106, 0, 0 }, + {1146,1146, 0, 0, 2733, 160, 0, 0 }, + {1147,1147, 0, 0, 10313, 93, 0, 0 }, + {1148,1148, 0, 0, 40000, 0, 0, 0 }, + {1149,1150, 0, 1, 40000, 0, 0, -0.03125 }, + {1151,1151, 0, 0, 40000, 53, 0, 0 }, + {1152,1152, 0, 0, 10560, 246, 0, 0 }, + {1153,1153, 0, 0, 2700, 153, 0, 0 }, + {1154,1154, 0, 1, 40000, 100, 0, -0.15625 }, + {1155,1155, 0, 0, 40000, 73, 0, 0 }, + {1156,1156, 0, 0, 40000, 220, 0, 0 }, + {1157,1157, 0, 0, 40000, 140, 0, 0 }, + {1158,1158, 0, 0, 40000, 380, 0, 0 }, + {1159,1160, 0, 1, 40000, 400, 0, 0.171875 }, + {1161,1161, 0, 0, 40000, 0, 0, 0 }, + {1162,1162, 0, 0, 40000, 0, 0, 0 }, + {1163,1163, 0, 0, 4733, 906, 0, 0 }, + {1164,1165, 0, 1, 40000, 393, 0, -0.125 }, + {1166,1167, 0, 1, 40000, 366, 0, 0.078125 }, + {1168,1168, 0, 1, 40000, 2453, 0, -0.078125 }, + {1169,1170, 0, 1, 40000, 546, 0, 0.0625 }, + {1171,1172, 0, 1, 40000, 786, 0, 0.15625 }, + {1173,1173, 0, 0, 40000, 0, 0, 0 }, + {1174,1174, 0, 0, 40000, 513, 0, 0 }, + {1175,1176, 0, 1, 2300, 533, 0, 0 }, + {1177,1177, 0, 0, 40000, 80, 0, 0 }, + {1178,1178, 0, 0, 40000, 60, 0, 0 }, + {1179,1179, 0, 0, 40000, 0, 0, 0 }, + {1180,1180, 0, 0, 10653, 86, 0, 0 }, + {1181,1182, 0, 1, 40000, 0, 0, 2.5e-05 }, + {1183,1184, 0, 1, 40000, 86, 0, 0.046875 }, + {1185,1186, 0, 1, 40000, 0, 0, 0.09375 }, + {1187,1188, 0, 1, 40000, 0, 0, 0.09375 }, + {1189,1189, 0, 0, 40000, 133, 0, 0 }, + {1190,1190, 0, 0, 40000, 140, 0, 0 }, + {1191,1191, 0, 0, 40000, 73, 0, 0 }, + {1192,1192, 0, 0, 40000, 60, 0, 0 }, + {1193,1193, 0, 0, 40000, 106, 0, 0 }, + {1194,1194, 0, 0, 40000, 93, 0, 0 }, + {1195,1195, 0, 0, 40000, 66, 0, 0 }, + {1196,1196, 0, 0, 40000, 93, 0, 0 }, + {1197,1197, 0, 0, 40000, 60, 0, 0 }, + {1198,1198, 0, 0, 40000, 66, 0, 0 }, + {1199,1199, 0, 0, 40000, 120, 0, 0 }, + {1200,1200, 0, 0, 40000, 100, 0, 0 }, + {1201,1201, 0, 0, 40000, 86, 0, 0 }, + {1202,1202, 0, 0, 40000, 0, 0, 0 }, + {1203,1203, 0, 0, 40000, 233, 0, 0 }, + {1204,1204, 0, 0, 40000, 100, 0, 0 }, + {1205,1206, 0, 1, 40000, 266, 0, 0.03125 }, + {1207,1208, 0, 1, 40000, 260, 0, -2.5e-05 }, + {1209,1209, 0, 0, 40000, 146, 0, 0 }, + {1210,1211, 0, 1, 40000, 60, 0, 0.03125 }, + {1212,1212, 0, 0, 40000, 53, 0, 0 }, + {1213,1214, 0, 1, 40000, 706, 0, -0.09375 }, + {1215,1216, 0, 1, 40000, 660, 0, -0.046875 }, + {1217,1217, 0, 0, 40000, 133, 0, 0 }, + {1218,1219, 0, 1, 40000, 426, 0, 0.03125 }, + {1220,1220, 0, 1, 40000, 0, 0, 0.03125 }, + {1221,1222, 0, 1, 40000, 260, 0, 0.171875 }, + {1223,1223, 0, 0, 40000, 0, 0, 0 }, + {1224,1224, 0, 0, 6100, 1580, 0, 0 }, + {1225,1150, 0, 1, 40000, 73, 0, -0.03125 }, + {1226,1226, 0, 0, 40000, 1580, 0, 0 }, + {1227,1227, 0, 0, 40000, 40, 0, 0 }, + {1228,1229, 0, 1, 40000, 113, 0, 0.125 }, + {1230,1230, 0, 0, 2666, 846, 0, 0 }, + {1231,1232, 0, 1, 40000, 0, 0, -0.03125 }, + {1233,1234, 0, 1, 9233, 2413, 0, -0.1875 }, + {1235,1235, 0, 0, 40000, 1020, 0, 0 }, + {1236,1236, 0, 0, 40000, 0, 0, 0 }, + {1237,1237, 0, 0, 9633, 3073, 0, 0 }, + {1238,1238, 0, 0, 40000, 0, 0, 0 }, + {1239,1239, 0, 0, 2446, 386, 0, 0 }, + {1240,1241, 0, 1, 3113, 1133, 0, 0 }, + {1242,1242, 0, 0, 18473, 813, 0, 0 }, + {1243,1243, 0, 0, 1206, 660, 0, 0 }, + {1244,1244, 0, 0, 40000, 153, 0, 0 }, + {1245,1245, 0, 0, 40000, 160, 0, 0 }, + {1246,1246, 0, 0, 40000, 133, 0, 0 }, + {1247,1247, 0, 0, 8660, 2386, 0, 0 }, + {1248,1248, 0, 0, 293, 106, 0, 0 }, + {1249,1249, 0, 0, 40000, 433, 0, 0 }, + {1250,1250, 0, 0, 426, 80, 0, 0 }, + {1251,1251, 0, 0, 973, 360, 0, 0 }, + {1252,1252, 0, 0, 573, 153, 0, 0 }, + {1253,1253, 0, 0, 3746, 126, 0, 0 }, + {1254,1254, 0, 0, 2313, 73, 0, 0 }, + {1255,1255, 0, 0, 1473, 106, 0, 0 }, + {1256,1256, 0, 0, 1500, 320, 0, 0 }, + {1257,1257, 0, 0, 5280, 1593, 0, 0 }, + {1258,1258, 0, 0, 40000, 60, 0, 0 }, + {1259,1259, 0, 0, 40000, 146, 0, 0 }, + {1260,1260, 29, 0, 40000, 300, 0, 0 }, + {1261,1261, 65, 0, 40000, 2040, 0, 0 }, + {1262,1262, 0, 0, 626, 240, 0, 0 }, + {1263,1263, 25, 0, 626, 226, 0, 0 }, + {1264,1264, 83, 0, 180, 80, 0, 0 }, + {1265,1265, 32, 0, 260, 140, 0, 0 }, + {1266,1266, 60, 0, 40000, 0, 0, 0 }, + {1267,1267, 36, 0, 286, 40, 0, 0 }, + {1268,1268, 27, 0, 573, 80, 0, 0 }, + {1269,1269, 31, 0, 693, 106, 0, 0 }, + {1270,1270, 21, 0, 500, 146, 0, 0 }, + {1270,1270, 26, 0, 493, 140, 0, 0 }, + {1270,1270, 28, 0, 500, 146, 0, 0 }, + {1271,1271, 60, 0, 2420, 1080, 0, 0 }, + {1270,1270, 32, 0, 413, 126, 0, 0 }, + {1272,1272, 60, 0, 806, 300, 0, 0 }, + {1273,1273, 96, 0, 1146, 493, 0, 0 }, + {1274,1274, 72, 0, 1246, 586, 0, 0 }, + {1275,1275, 79, 0, 286, 106, 0, 0 }, + {1276,1276, 69, 0, 1193, 1046, 0, 0 }, + {1277,1277, 71, 0, 340, 93, 0, 0 }, + {1278,1278, 22, 0, 1880, 653, 0, 0 }, + {1279,1279, 55, 0, 246, 120, 0, 0 }, + {1279,1279, 48, 0, 286, 133, 0, 0 }, + {1280,1280, 0, 0, 40, 93, 0, 0 }, + {1281,1281, 49, 2, 40, 93, 0, 0 }, + {1282,1282, 73, 0, 166, 33, 0, 0 }, + {1282,1282, 68, 0, 166, 33, 0, 0 }, + {1282,1282, 61, 0, 200, 40, 0, 0 }, + {1283,1283, 0, 0, 40, 93, 0, 0 }, + {1284,1284, 0, 0, 40000, 100, 0, 0 }, + {1285,1285, 0, 0, 40000, 60, 0, 0 }, + {1286,1286, 0, 0, 40000, 0, 0, 0 }, + {1287,1287, 0, 0, 10460, 153, 0, 0 }, + {1288,1289, 0, 1, 40000, 0, 0, 0 }, + {1290,1290, 0, 0, 40000, 0, 0, 0 }, + {1291,1292, 36, 1, 353, 153, 0, 0 }, + {1293,1293, 69, 0, 1206, 1060, 0, 0 }, + {1294,1294, 0, 0, 40000, 0, 0, 0 }, + {1295,1295, 0, 0, 40000, 73, 0, 0 }, + {1296,1296, 0, 0, 40000, 0, 0, 0 }, + {1297,1297, 22, 0, 1880, 653, 0, 0 }, + {1298,1298, 0, 0, 40000, 73, 0, 0 }, + {1299,1299, 0, 0, 3913, 420, 0, 0 }, + {1300,1300, 0, 0, 9233, 240, 0, 0 }, + {1301,1301, 0, 0, 4660, 1573, 0, 0 }, + {1302,1302, 0, 0, 1166, 400, 0, 0 }, + {1303,1303, 0, 0, 40000, 126, 0, 0 }, + {1304,1304, 0, 0, 40000, 93, 0, 0 }, + {1305,1305, 0, 0, 40000, 93, 0, 0 }, + {1306,1306, 0, 0, 40000, 553, 0, 0 }, + {1307,1307, 0, 0, 40000, 660, 0, 0 }, + {1308,1308, 0, 0, 40000, 73, 0, 0 }, + {1309,1309, 0, 0, 40000, 146, 0, 0 }, + {1310,1310, 0, 0, 40000, 146, 0, 0 }, + {1311,1311, 0, 0, 4026, 100, 0, 0 }, + {1312,1312, 0, 0, 18226, 100, 0, 0 }, + {1313,1313, 0, 0, 40000, 0, 0, 0 }, + {1314,1314, 0, 0, 40000, 73, 0, 0 }, + {1315,1315, 0, 0, 40000, 140, 0, 0 }, + {1316,1316, 0, 0, 40000, 393, 0, 0 }, + {1317,1317, 0, 0, 40000, 406, 0, 0 }, + {1318,1318, 0, 0, 40000, 373, 0, 0 }, + {1319,1319, 0, 0, 40000, 0, 0, 0 }, + {1320,1320, 0, 0, 40000, 360, 0, 0 }, + {1321,1321, 0, 0, 1060, 380, 0, 0 }, + {1322,1322, 0, 0, 40000, 66, 0, 0 }, + {1323,1323, 0, 0, 40000, 66, 0, 0 }, + {1324,1324, 0, 0, 40000, 86, 0, 0 }, + {1325,1325, 0, 0, 40000, 73, 0, 0 }, + { 260, 260, 0, 0, 40000, 80, 0, 0 }, + {1326,1326, 0, 0, 40000, 80, 0, 0 }, + {1327,1327, 0, 0, 40000, 73, 0, 0 }, + {1328,1328, 0, 0, 40000, 73, 0, 0 }, + {1329,1329, 0, 0, 40000, 153, 0, 0 }, + {1330,1330, 0, 0, 40000, 153, 0, 0 }, + {1331,1331, 0, 0, 40000, 146, 0, 0 }, + {1332,1332, 0, 0, 40000, 146, 0, 0 }, + {1333,1333, 0, 0, 40000, 73, 0, 0 }, + {1334,1334, 0, 0, 40000, 153, 0, 0 }, + {1335,1335, 0, 0, 40000, 233, 0, 0 }, + {1336,1336, 0, 0, 40000, 400, 0, 0 }, + {1337,1337, 0, 0, 40000, 1373, 0, 0 }, + {1338,1338, 0, 0, 40000, 193, 0, 0 }, + {1339,1339, 0, 0, 40000, 1273, 0, 0 }, + {1340,1340, 0, 0, 40000, 186, 0, 0 }, + {1341,1341, 0, 0, 40000, 86, 0, 0 }, + {1342,1342, 0, 0, 7440, 2473, 0, 0 }, + {1343,1343, 0, 0, 40000, 160, 0, 0 }, + {1344,1344, 0, 0, 8966, 406, 0, 0 }, + {1345,1345, 0, 0, 40000, 1353, 0, 0 }, + {1346,1346, 0, 0, 14180, 4406, 0, 0 }, + { 378, 378, 84, 0, 1333, 460, 0, 0 }, + {1347,1347, 24, 0, 1893, 633, 0, 0 }, + {1348,1348, 44, 0, 206, 86, 0, 0 }, + {1349,1349, 40, 0, 586, 140, 0, 0 }, + {1350,1350, 60, 0, 1026, 320, 0, 0 }, + {1351,1351, 0, 0, 6560, 33, 0, 0 }, + {1352,1352, 0, 0, 7373, 2453, 0, 0 }, + {1353,1353, 0, 0, 4660, 1573, 0, 0 }, + {1354,1354, 0, 0, 40000, 346, 0, 0 }, + {1355,1355, 0, 0, 7126, 86, 0, 0 }, + {1356,1356, 0, 0, 40000, 213, 0, 0 }, + {1357,1357, 0, 0, 1180, 340, 0, 0 }, + {1358,1358, 0, 0, 3893, 1466, 0, 0 }, + {1359,1359, 0, 0, 2053, 1173, 0, 0 }, + {1360,1360, 0, 0, 40000, 200, 0, 0 }, + {1361,1361, 0, 0, 40000, 353, 0, 0 }, + {1362,1362, 0, 0, 40000, 273, 0, 0 }, + {1363,1363, 0, 0, 40000, 433, 0, 0 }, + {1364,1364, 0, 0, 1940, 426, 0, 0 }, + {1365,1365, 0, 0, 40000, 80, 0, 0 }, + {1366,1366, 0, 0, 40000, 106, 0, 0 }, + {1367,1367, 0, 0, 40000, 60, 0, 0 }, + {1368,1368, 0, 0, 40000, 140, 0, 0 }, + {1369,1369, 0, 0, 40000, 93, 0, 0 }, + {1370,1370, 0, 0, 40000, 73, 0, 0 }, + {1371,1371, 0, 0, 40000, 73, 0, 0 }, + {1372,1372, 0, 0, 40000, 93, 0, 0 }, + {1373,1373, 0, 0, 40000, 73, 0, 0 }, + {1374,1374, 0, 0, 40000, 80, 0, 0 }, + {1375,1375, 0, 0, 40000, 746, 0, 0 }, + {1376,1376, 0, 0, 2360, 813, 0, 0 }, + {1377,1377, 0, 0, 340, 146, 0, 0 }, + {1378,1378, 35, 0, 713, 273, 0, 0 }, + {1379,1379, 49, 0, 173, 93, 0, 0 }, + {1377,1377, 48, 0, 286, 126, 0, 0 }, + {1380,1380, 58, 0, 173, 100, 0, 0 }, + {1377,1377, 60, 0, 286, 133, 0, 0 }, + {1381,1381, 47, 0, 973, 360, 0, 0 }, + {1382,1382, 60, 0, 146, 86, 0, 0 }, + {1381,1381, 49, 0, 966, 333, 0, 0 }, + {1383,1383, 72, 0, 506, 206, 0, 0 }, + {1381,1381, 51, 0, 953, 340, 0, 0 }, + {1384,1384, 84, 0, 1340, 480, 0, 0 }, + {1381,1381, 54, 0, 986, 360, 0, 0 }, + {1381,1381, 57, 0, 980, 346, 0, 0 }, + {1385,1385, 72, 0, 1573, 440, 0, 0 }, + {1381,1381, 60, 0, 953, 340, 0, 0 }, + {1386,1386, 36, 0, 2673, 900, 0, 0 }, + {1387,1387, 93, 0, 233, 106, 0, 0 }, + {1388,1388, 72, 0, 966, 353, 0, 0 }, + {1389,1389, 84, 0, 1366, 473, 0, 0 }, + {1390,1390, 36, 0, 1326, 446, 0, 0 }, + {1391,1391, 64, 0, 220, 86, 0, 0 }, + {1392,1392, 68, 0, 126, 220, 0, 0 }, + {1393,1393, 0, 0, 4513, 640, 0, 0 }, + {1394,1394, 0, 0, 40000, 353, 0, 0 }, + {1395,1395, 0, 0, 40000, 73, 0, 0 }, + {1396,1396, 0, 0, 2040, 380, 0, 0 }, + {1397,1397, 0, 0, 40000, 240, 0, 0 }, + {1398,1398, 0, 0, 3246, 753, 0, 0 }, + {1399,1399, 0, 0, 40000, 66, 0, 0 }, + {1400,1400, 0, 0, 40000, 0, 0, 0 }, + {1401,1401, 0, 0, 40000, 0, 0, 0 }, + {1402,1402, 0, 0, 7720, 1260, 0, 0 }, + {1403,1403, 0, 0, 213, 6420, 0, 0 }, + {1404,1404, 0, 0, 40000, 66, 0, 0 }, + {1405,1405, 0, 0, 40000, 73, 0, 0 }, + {1406,1406, 0, 0, 40000, 93, 0, 0 }, + {1407,1407, 0, 0, 1606, 640, 0, 0 }, + {1408,1408, 0, 0, 15486, 1580, 0, 0 }, + {1409,1409, 0, 0, 40000, 353, 0, 0 }, + {1410,1410, 0, 0, 40000, 2066, 0, 0 }, + {1411,1411, 0, 0, 40000, 0, 0, 0 }, + {1412,1412, 0, 0, 15453, 73, 0, 0 }, + {1413,1413, 0, 0, 3726, 1240, 0, 0 }, + {1414,1414, 0, 0, 40000, 86, 0, 0 }, + {1415,1415, 0, 0, 40000, 200, 0, 0 }, + {1416,1416, 0, 0, 40000, 53, 0, 0 }, + {1417,1417, 0, 0, 40000, 73, 0, 0 }, + {1418,1418, 0, 0, 40000, 66, 0, 0 }, + {1419,1419, 0, 0, 40000, 26, 0, 0 }, + {1420,1420, 0, 0, 40000, 53, 0, 0 }, + {1421,1421, 0, 0, 40000, 40, 0, 0 }, + {1422,1422, 0, 0, 40000, 126, 0, 0 }, + {1423,1423, 0, 0, 40000, 0, 0, 0 }, + {1424,1424, 0, 0, 13653, 4720, 0, 0 }, + {1425,1425, 0, 0, 12533, 1953, 0, 0 }, + {1426,1426, 0, 0, 973, 1280, 0, 0 }, + {1427,1427, 0, 0, 40000, 426, 0, 0 }, + {1428,1428, 0, 0, 40000, 53, 0, 0 }, + {1429,1429, 0, 0, 40000, 66, 0, 0 }, + {1430,1430, 0, 0, 526, 840, 0, 0 }, + {1431,1431, 0, 0, 286, 1293, 0, 0 }, + {1432,1432, 0, 0, 14726, 4920, 0, 0 }, + {1433,1433, 0, 0, 5233, 633, 0, 0 }, + {1434,1434, 0, 0, 13226, 2500, 0, 0 }, + { 740, 740, 0, 0, 513, 200, 0, 0 }, + {1435,1435, 0, 0, 40000, 5666, 0, 0 }, + { 739, 739, 48, 0, 213, 20, 0, 0 }, + { 500, 500, 55, 0, 100, 26, 0, 0 }, + { 740, 740, 60, 0, 226, 113, 0, 0 }, + { 500, 500, 41, 0, 106, 26, 0, 0 }, + {1436,1436, 84, 0, 160, 26, 0, 0 }, + {1437,1437, 84, 0, 386, 493, 0, 0 }, + { 500, 500, 48, 0, 100, 26, 0, 0 }, + {1438,1438, 15, 0, 340, 140, 0, 0 }, + { 752, 752, 49, 0, 173, 20, 0, 0 }, + {1438,1438, 16, 0, 346, 146, 0, 0 }, + {1438,1438, 12, 0, 340, 140, 0, 0 }, + { 740, 740, 55, 0, 220, 113, 0, 0 }, + { 752, 752, 18, 0, 206, 20, 0, 0 }, + { 752, 752, 15, 0, 200, 20, 0, 0 }, + { 752, 752, 17, 0, 206, 20, 0, 0 }, + {1439,1440, 0, 4, 40000, 0, 0, 0 }, + {1441,1442, 0, 4, 7320, 193, 0, 0 }, + {1443,1444, 0, 4, 11833, 320, 0, 0 }, + {1445,1446, 0, 4, 9920, 326, 0, 0 }, + {1447,1448, 0, 4, 10133, 26, 0, 0 }, + {1449,1450, 0, 4, 7373, 2486, 0, 0 }, + { 181,1451, 0, 4, 2313, 733, 0, 0 }, + {1452,1453, 0, 4, 9213, 240, 0, 0 }, + {1454,1455, 0, 4, 40000, 0, 0, 0 }, + {1456,1457, 0, 4, 660, 126, 0, 0 }, + {1458,1459, 0, 4, 40000, 66, 0, 0 }, + { 190,1460, 0, 4, 40000, 60, 0, 0 }, + { 192,1461, 0, 4, 40000, 73, 0, 0 }, + {1462,1463, 0, 4, 40000, 353, 0, 0 }, + {1464,1465, 0, 4, 40000, 353, 0, 0 }, + {1466,1467, 0, 4, 40000, 66, 0, 0 }, + {1468,1469, 0, 4, 40000, 46, 0, 0 }, + { 35,1470, 0, 4, 40000, 46, 0, 0 }, + { 36,1471, 0, 4, 320, 26, 0, 0 }, + {1472,1473, 0, 4, 320, 26, 0, 0 }, + {1474,1475, 0, 4, 7986, 93, 0, 0 }, + { 39,1476, 0, 4, 1046, 226, 0, 0 }, + {1477,1476, 0, 4, 1046, 226, 0, 0 }, + {1478,1479, 0, 4, 40000, 453, 0, 0 }, + { 50,1480, 0, 4, 40000, 400, 0, 0 }, + {1481,1482, 0, 4, 40000, 133, 0, 0 }, + {1483,1484, 0, 4, 40000, 0, 0, 0 }, + {1485,1486, 0, 4, 40000, 226, 0, 0 }, + { 55,1487, 0, 4, 40000, 100, 0, 0 }, + {1488,1489, 0, 4, 40000, 93, 0, 0 }, + {1490,1491, 0, 4, 40000, 73, 0, 0 }, + {1492,1493, 0, 4, 40000, 73, 0, 0 }, + {1494,1495, 0, 4, 40000, 73, 0, 0 }, + {1496,1497, 0, 4, 40000, 80, 0, 0 }, + {1496,1498, 0, 4, 40000, 73, 0, 0 }, + {1499,1500, 0, 4, 40000, 66, 0, 0 }, + {1501,1502, 0, 4, 40000, 146, 0, 0 }, + {1503,1504, 0, 4, 40000, 93, 0, 0 }, + {1505,1506, 0, 4, 40000, 73, 0, 0 }, + { 86,1507, 0, 4, 40000, 80, 0, 0 }, + {1508,1509, 0, 4, 40000, 0, 0, 0 }, + {1510,1511, 0, 4, 40000, 60, 0, 0 }, + {1512,1513, 0, 4, 40000, 0, 0, 0 }, + {1514,1515, 0, 4, 40000, 0, 0, 0 }, + {1516,1517, 0, 4, 40000, 766, 0, 0 }, + {1518,1519, 0, 4, 5286, 2966, 0, 0 }, + {1520,1521, 0, 4, 40000, 406, 0, 0 }, + {1522,1523, 0, 4, 9040, 360, 0, 0 }, + {1524,1525, 0, 4, 40000, 1200, 0, 0 }, + {1526,1527, 0, 4, 40000, 800, 0, 0 }, + {1528,1529, 0, 4, 40000, 960, 0, 0 }, + { 111,1530, 0, 4, 1193, 433, 0, 0 }, + {1531,1532, 0, 4, 220, 386, 0, 0 }, + { 115,1533, 0, 4, 2413, 1646, 0, 0 }, + {1534,1535, 0, 4, 1853, 640, 0, 0 }, + {1536,1537, 0, 4, 3006, 53, 0, 0 }, + {1538,1539, 0, 4, 1506, 720, 0, 0 }, + {1540, 339, 0, 6, 6, 0, 0, 0 }, + {1541, 339, 0, 6, 6, 0, 0, 0 }, + {1542,1543, 0, 4, 993, 93, 0, 0 }, + {1544,1545, 0, 4, 293, 86, 0, 0 }, + {1546,1547, 0, 4, 40000, 153, 0, 0 }, + { 364, 365, 44, 4, 120, 26, 0, 0 }, + { 129,1548, 48, 4, 173, 93, 0, 0 }, + { 367, 368, 58, 4, 173, 93, 0, 0 }, + { 129,1549, 60, 4, 173, 93, 0, 0 }, + {1550,1551, 48, 4, 520, 200, 0, 0 }, + { 132,1552, 43, 4, 173, 93, 0, 0 }, + {1550,1551, 49, 4, 520, 200, 0, 0 }, + {1553,1554, 43, 4, 160, 80, 0, 0 }, + {1550,1551, 51, 4, 513, 206, 0, 0 }, + { 134,1555, 43, 4, 1686, 613, 0, 0 }, + {1550,1551, 54, 4, 506, 200, 0, 0 }, + {1550,1551, 57, 4, 506, 200, 0, 0 }, + { 380, 381, 72, 4, 1573, 553, 0, 0 }, + {1550,1551, 60, 4, 513, 206, 0, 0 }, + {1556,1557, 70, 4, 766, 306, 0, 0 }, + { 374, 375, 60, 4, 973, 360, 0, 0 }, + {1558,1559, 36, 4, 1126, 420, 0, 0 }, + {1560,1561, 65, 4, 293, 133, 0, 0 }, + {1562,1563, 84, 4, 1353, 300, 0, 0 }, + {1564,1565, 59, 4, 380, 160, 0, 0 }, + {1566,1567, 84, 4, 1586, 566, 0, 0 }, + {1568,1569, 35, 4, 1320, 473, 0, 0 }, + {1570,1571, 44, 4, 406, 93, 0, 0 }, + {1572,1573, 67, 4, 246, 113, 0, 0 }, + {1574,1575, 66, 4, 286, 193, 0, 0 }, + { 145,1576, 59, 4, 140, 120, 0, 0 }, + {1577,1578, 51, 4, 326, 340, 0, 0 }, + {1579,1580, 45, 4, 233, 193, 0, 0 }, + {1581,1582, 71, 4, 433, 180, 0, 0 }, + { 149,1583, 60, 4, 280, 26, 0, 0 }, + {1584,1585, 58, 4, 166, 93, 0, 0 }, + {1586,1587, 53, 4, 173, 93, 0, 0 }, + { 397,1588, 64, 4, 213, 80, 0, 0 }, + {1589,1590, 71, 4, 106, 53, 0, 0 }, + {1591,1592, 61, 4, 973, 340, 0, 0 }, + {1593,1594, 61, 4, 986, 340, 0, 0 }, + { 391, 392, 48, 4, 160, 46, 0, 0 }, + { 391, 393, 48, 4, 380, 60, 0, 0 }, + {1595,1596, 69, 4, 120, 120, 0, 0 }, + { 159,1597, 68, 4, 120, 120, 0, 0 }, + { 159,1597, 63, 4, 140, 153, 0, 0 }, + {1598,1599, 74, 4, 893, 273, 0, 0 }, + {1600,1601, 60, 4, 1006, 306, 0, 0 }, + {1602,1603, 80, 4, 213, 106, 0, 0 }, + {1604,1605, 64, 4, 1346, 486, 0, 0 }, + {1606,1607, 69, 4, 120, 73, 0, 0 }, + { 398, 399, 55, 4, 1533, 193, 0, 0 }, + {1608,1609, 75, 4, 1560, 300, 0, 0 }, + {1610,1611, 68, 4, 120, 120, 0, 0 }, + {1612,1613, 48, 4, 333, 340, 0, 0 }, + {1614,1615, 53, 4, 593, 620, 0, 0 }, + {1616,1616, 0, 0, 40000, 1586, 0, 0 }, + {1617,1617, 0, 0, 40000, 1226, 0, 0 }, + {1618,1618, 0, 0, 4546, 766, 0, 0 }, + {1619,1619, 0, 0, 40000, 420, 0, 0 }, + {1620,1620, 0, 0, 40000, 1573, 0, 0 }, + {1621,1621, 0, 0, 3326, 806, 0, 0 }, + {1622,1622, 0, 0, 40000, 746, 0, 0 }, + {1623,1623, 0, 0, 40000, 900, 0, 0 }, + {1624,1624, 0, 0, 12166, 1573, 0, 0 }, + {1625,1625, 0, 0, 40000, 80, 0, 0 }, + {1626,1626, 0, 0, 40000, 80, 0, 0 }, + {1627,1627, 0, 0, 40000, 80, 0, 0 }, + {1628,1628, 0, 0, 40000, 2713, 0, 0 }, + {1629,1629, 0, 0, 40000, 86, 0, 0 }, + {1630,1630, 0, 0, 40000, 80, 0, 0 }, + {1631,1631, 0, 0, 40000, 80, 0, 0 }, + {1632,1632, 0, 0, 40000, 813, 0, 0 }, + {1633,1633, 0, 0, 40000, 80, 0, 0 }, + {1634,1634, 0, 0, 40000, 80, 0, 0 }, + {1635,1635, 0, 0, 40000, 80, 0, 0 }, + {1636,1636, 0, 0, 40000, 193, 0, 0 }, + {1637,1637, 0, 0, 2920, 733, 0, 0 }, + {1638,1638, 0, 0, 40000, 373, 0, 0 }, + {1639,1639, 0, 0, 2286, 226, 0, 0 }, + {1640,1640, 0, 0, 40000, 226, 0, 0 }, + {1641,1641, 0, 0, 40000, 226, 0, 0 }, + {1642,1642, 0, 0, 40000, 433, 0, 0 }, + {1643,1643, 0, 0, 40000, 813, 0, 0 }, + {1644,1644, 0, 0, 40000, 80, 0, 0 }, + {1645,1645, 0, 0, 40000, 80, 0, 0 }, + {1646,1646, 0, 0, 40000, 80, 0, 0 }, + {1647,1647, 0, 0, 40000, 80, 0, 0 }, + {1648,1648, 0, 0, 40000, 80, 0, 0 }, + {1649,1649, 0, 0, 40000, 80, 0, 0 }, + {1650,1650, 0, 0, 40000, 146, 0, 0 }, + {1651,1651, 0, 0, 40000, 1280, 0, 0 }, + {1652,1652, 0, 0, 40000, 513, 0, 0 }, + {1653,1653, 0, 0, 40000, 313, 0, 0 }, + {1654,1654, 0, 0, 40000, 773, 0, 0 }, + {1655,1655, 0, 0, 7400, 2480, 0, 0 }, + {1656,1656, 0, 0, 3760, 1253, 0, 0 }, + {1657,1657, 0, 0, 40000, 380, 0, 0 }, + {1658,1658, 0, 0, 40000, 333, 0, 0 }, + {1659,1659, 0, 0, 40000, 2926, 0, 0 }, + {1660,1660, 0, 0, 40000, 5666, 0, 0 }, + {1661,1661, 0, 0, 40000, 1613, 0, 0 }, + {1662,1662, 0, 0, 3746, 1273, 0, 0 }, + {1663,1663, 0, 0, 13653, 4720, 0, 0 }, + {1664,1664, 0, 0, 4640, 1553, 0, 0 }, + {1665,1665, 0, 0, 40000, 680, 0, 0 }, + {1666,1666, 0, 0, 6393, 426, 0, 0 }, + {1667,1667, 0, 0, 40000, 713, 0, 0 }, + {1668,1668, 12, 0, 166, 20, 0, 0 }, + {1669,1669, 48, 0, 460, 193, 0, 0 }, + { 736, 736, 52, 0, 286, 20, 0, 0 }, + {1670,1670, 48, 0, 506, 200, 0, 0 }, + {1670,1670, 36, 0, 713, 260, 0, 0 }, + { 377, 377, 84, 0, 386, 493, 0, 0 }, + { 730, 730, 95, 0, 1886, 653, 0, 0 }, + {1669,1669, 84, 0, 386, 166, 0, 0 }, + { 755, 755, 20, 0, 633, 240, 0, 0 }, + { 755, 755, 22, 0, 626, 240, 0, 0 }, + { 755, 755, 24, 0, 633, 246, 0, 0 }, + {1671,1671, 0, 0, 2233, 220, 0, 0 }, + {1672,1672, 0, 0, 2233, 240, 0, 0 }, + {1673,1673, 0, 0, 2233, 206, 0, 0 }, + {1674,1674, 0, 0, 2126, 173, 0, 0 }, + {1675,1675, 0, 0, 7473, 73, 0, 0 }, + {1676,1676, 0, 0, 40000, 0, 0, 0 }, + {1677,1677, 0, 0, 3493, 193, 0, 0 }, + {1678,1678, 0, 0, 1746, 73, 0, 0 }, + {1679,1679, 0, 0, 1013, 400, 0, 0 }, + {1680,1680, 0, 0, 3473, 1560, 0, 0 }, + {1681,1681, 0, 0, 1073, 40, 0, 0 }, + {1682,1682, 0, 0, 40000, 380, 0, 0 }, + {1683,1683, 0, 0, 1166, 400, 0, 0 }, + {1684,1684, 0, 0, 606, 146, 0, 0 }, + {1685,1685, 0, 0, 4553, 1486, 0, 0 }, + {1686,1686, 0, 0, 1126, 80, 0, 0 }, + {1687,1687, 0, 0, 40000, 73, 0, 0 }, + {1688,1688, 0, 0, 40000, 60, 0, 0 }, + {1689,1689, 0, 0, 40000, 66, 0, 0 }, + {1690,1690, 0, 0, 40000, 73, 0, 0 }, + {1691,1691, 0, 0, 40000, 73, 0, 0 }, + {1692,1692, 0, 0, 40000, 73, 0, 0 }, + {1693,1693, 0, 0, 40000, 73, 0, 0 }, + {1694,1694, 0, 0, 6380, 53, 0, 0 }, + {1695,1695, 0, 0, 6380, 60, 0, 0 }, + {1696,1696, 0, 0, 40000, 53, 0, 0 }, + {1697,1697, 0, 0, 40000, 0, 0, 0 }, + {1698,1698, 0, 0, 1880, 80, 0, 0 }, + {1699,1699, 0, 0, 40000, 60, 0, 0 }, + {1700,1700, 0, 0, 40000, 60, 0, 0 }, + {1701,1701, 0, 0, 1460, 80, 0, 0 }, + {1702,1702, 0, 0, 40000, 73, 0, 0 }, + {1703,1703, 0, 0, 40000, 0, 0, 0 }, + {1704,1704, 0, 0, 40000, 146, 0, 0 }, + {1705,1705, 0, 0, 40000, 66, 0, 0 }, + {1706,1706, 0, 0, 40000, 73, 0, 0 }, + {1707,1707, 0, 0, 40000, 160, 0, 0 }, + {1708,1708, 0, 0, 40000, 73, 0, 0 }, + {1709,1709, 0, 0, 40000, 193, 0, 0 }, + {1710,1710, 0, 0, 3740, 1260, 0, 0 }, + {1711,1711, 0, 0, 40000, 180, 0, 0 }, + {1712,1712, 0, 0, 40000, 173, 0, 0 }, + {1713,1713, 0, 0, 40000, 113, 0, 0 }, + {1714,1714, 0, 0, 40000, 86, 0, 0 }, + {1715,1715, 0, 0, 1853, 633, 0, 0 }, + {1716,1716, 0, 0, 40000, 0, 0, 0 }, + {1717,1717, 0, 0, 1066, 306, 0, 0 }, + {1718,1718, 0, 0, 40000, 86, 0, 0 }, + {1719,1719, 0, 0, 40000, 586, 0, 0 }, + {1720,1720, 0, 0, 40000, 86, 0, 0 }, + {1721,1721, 0, 0, 40000, 93, 0, 0 }, + {1722,1722, 0, 0, 40000, 373, 0, 0 }, + {1723,1723, 0, 0, 40000, 113, 0, 0 }, + {1724,1724, 0, 0, 40000, 353, 0, 0 }, + {1725,1725, 0, 0, 420, 73, 0, 0 }, + {1726,1726, 0, 0, 40000, 66, 0, 0 }, + {1727,1727, 0, 0, 40000, 53, 0, 0 }, + {1728,1728, 0, 0, 40000, 66, 0, 0 }, + {1729,1729, 0, 0, 40000, 100, 0, 0 }, + {1730,1730, 0, 0, 40000, 93, 0, 0 }, + {1731,1731, 0, 0, 40000, 0, 0, 0 }, + {1732,1732, 0, 0, 40000, 73, 0, 0 }, + {1733,1733, 0, 0, 40000, 80, 0, 0 }, + {1734,1734, 0, 0, 40000, 80, 0, 0 }, + {1735,1735, 0, 0, 40000, 80, 0, 0 }, + {1736,1736, 0, 0, 40000, 80, 0, 0 }, + {1737,1737, 0, 0, 40000, 80, 0, 0 }, + {1738,1738, 0, 0, 40000, 73, 0, 0 }, + {1739,1739, 0, 0, 40000, 73, 0, 0 }, + {1740,1740, 0, 0, 40000, 106, 0, 0 }, + {1741,1741, 0, 0, 40000, 73, 0, 0 }, + {1742,1742, 0, 0, 40000, 73, 0, 0 }, + {1743,1743, 0, 0, 40000, 80, 0, 0 }, + {1744,1744, 0, 0, 40000, 0, 0, 0 }, + {1745,1745, 0, 0, 40000, 80, 0, 0 }, + {1746,1746, 0, 0, 40000, 66, 0, 0 }, + {1747,1747, 0, 0, 40000, 73, 0, 0 }, + {1748,1748, 0, 0, 40000, 0, 0, 0 }, + {1749,1749, 0, 0, 40000, 80, 0, 0 }, + {1750,1750, 0, 0, 40000, 66, 0, 0 }, + {1751,1751, 0, 0, 40000, 73, 0, 0 }, + {1752,1752, 0, 0, 40000, 80, 0, 0 }, + {1753,1753, 0, 0, 40000, 33, 0, 0 }, + {1754,1754, 0, 0, 40000, 0, 0, 0 }, + {1755,1755, 0, 0, 40000, 266, 0, 0 }, + {1756,1756, 0, 0, 40000, 160, 0, 0 }, + {1757,1757, 0, 0, 40000, 93, 0, 0 }, + {1758,1758, 0, 0, 40000, 660, 0, 0 }, + {1759,1759, 0, 0, 40000, 1453, 0, 0 }, + {1760,1760, 0, 0, 40000, 660, 0, 0 }, + {1761,1761, 0, 0, 40000, 120, 0, 0 }, + {1762,1762, 0, 0, 40000, 140, 0, 0 }, + {1763,1763, 0, 0, 9820, 393, 0, 0 }, + {1764,1764, 0, 0, 40000, 73, 0, 0 }, + {1765,1765, 0, 0, 3620, 1166, 0, 0 }, + {1766,1766, 0, 0, 40000, 0, 0, 0 }, + {1767,1767, 0, 0, 40000, 0, 0, 0 }, + {1768,1768, 0, 0, 40000, 813, 0, 0 }, + {1769,1769, 0, 0, 40000, 0, 0, 0 }, + {1770,1770, 0, 0, 40000, 2386, 0, 0 }, + {1771,1771, 0, 0, 4380, 400, 0, 0 }, + {1772,1772, 0, 0, 853, 220, 0, 0 }, + {1773,1773, 0, 0, 3700, 93, 0, 0 }, + {1774,1774, 0, 0, 1580, 300, 0, 0 }, + {1775,1775, 0, 0, 453, 140, 0, 0 }, + {1776,1776, 0, 0, 40000, 66, 0, 0 }, + {1777,1777, 0, 0, 40000, 73, 0, 0 }, + {1778,1778, 0, 0, 40000, 206, 0, 0 }, + {1779,1779, 0, 0, 4646, 1560, 0, 0 }, + {1780,1780, 0, 0, 353, 146, 0, 0 }, + {1781,1781, 0, 0, 1300, 400, 0, 0 }, + {1782,1782, 0, 0, 4593, 1546, 0, 0 }, + {1783,1783, 0, 0, 613, 226, 0, 0 }, + {1784,1784, 0, 0, 626, 233, 0, 0 }, + {1785,1785, 0, 0, 3020, 66, 0, 0 }, + {1786,1786, 0, 0, 1093, 186, 0, 0 }, + {1787,1787, 0, 0, 6053, 1240, 0, 0 }, + {1788,1788, 0, 0, 633, 126, 0, 0 }, + {1789,1789, 0, 0, 40000, 66, 0, 0 }, + {1790,1790, 0, 0, 40000, 73, 0, 0 }, + {1791,1791, 0, 0, 40000, 1253, 0, 0 }, + {1792,1792, 0, 0, 626, 246, 0, 0 }, + {1793,1793, 48, 0, 293, 120, 0, 0 }, + {1794,1794, 48, 0, 100, 40, 0, 0 }, + {1795,1795, 60, 0, 240, 133, 0, 0 }, + {1796,1796, 60, 0, 160, 66, 0, 0 }, + {1797,1797, 70, 0, 140, 33, 0, 0 }, + {1798,1798, 51, 0, 526, 206, 0, 0 }, + {1799,1799, 60, 0, 173, 93, 0, 0 }, + {1798,1798, 54, 0, 520, 200, 0, 0 }, + {1800,1800, 60, 0, 153, 80, 0, 0 }, + {1798,1798, 56, 0, 520, 206, 0, 0 }, + {1801,1801, 60, 0, 673, 206, 0, 0 }, + {1798,1798, 61, 0, 506, 200, 0, 0 }, + {1798,1798, 63, 0, 513, 206, 0, 0 }, + {1802,1802, 48, 0, 673, 200, 0, 0 }, + {1798,1798, 68, 0, 440, 180, 0, 0 }, + {1803,1803, 60, 0, 1873, 653, 0, 0 }, + {1804,1804, 60, 0, 673, 200, 0, 0 }, + {1805,1805, 66, 0, 306, 120, 0, 0 }, + {1806,1806, 60, 0, 673, 200, 0, 0 }, + { 379, 379, 59, 0, 173, 93, 0, 0 }, + {1802,1802, 64, 0, 673, 206, 0, 0 }, + {1807,1807, 48, 0, 1006, 20, 0, 0 }, + {1808,1808, 56, 0, 120, 40, 0, 0 }, + {1809,1809, 53, 0, 286, 133, 0, 0 }, + {1810,1810, 65, 0, 106, 46, 0, 0 }, + {1811,1811, 49, 0, 293, 133, 0, 0 }, + {1811,1811, 43, 0, 293, 133, 0, 0 }, + { 386, 386, 65, 0, 1013, 673, 0, 0 }, + { 386, 386, 60, 0, 1000, 660, 0, 0 }, + {1812,1812, 70, 0, 260, 113, 0, 0 }, + {1812,1812, 65, 0, 306, 120, 0, 0 }, + {1813,1813, 60, 0, 246, 106, 0, 0 }, + {1814,1814, 60, 0, 193, 120, 0, 0 }, + {1815,1815, 56, 0, 206, 13, 0, 0 }, + {1816,1816, 53, 0, 433, 73, 0, 0 }, + {1817,1817, 60, 0, 220, 113, 0, 0 }, + {1818,1818, 48, 0, 300, 66, 0, 0 }, + {1819,1819, 69, 0, 126, 0, 0, 0 }, + { 328, 328, 67, 0, 140, 93, 0, 0 }, + { 328, 328, 62, 0, 153, 100, 0, 0 }, + {1820,1820, 65, 0, 433, 100, 0, 0 }, + {1821,1821, 60, 0, 426, 100, 0, 0 }, + {1822,1822, 63, 0, 113, 46, 0, 0 }, + {1823,1823, 63, 0, 1866, 653, 0, 0 }, + {1824,1824, 67, 0, 273, 60, 0, 0 }, + {1825,1825, 60, 0, 973, 360, 0, 0 }, + {1825,1825, 72, 0, 806, 273, 0, 0 }, + { 401, 401, 62, 0, 46, 33, 0, 0 }, + {1826,1826, 48, 0, 126, 66, 0, 0 }, + {1827,1827, 53, 0, 980, 353, 0, 0 }, + {1828,1828, 60, 0, 293, 133, 0, 0 }, + {1829,1829, 60, 0, 160, 20, 0, 0 }, + {1830,1830, 60, 0, 126, 86, 0, 0 }, + {1831,1831, 60, 0, 173, 93, 0, 0 }, + {1832,1832, 0, 0, 40000, 106, 0, 0 }, + {1833,1833, 0, 0, 3780, 73, 0, 0 }, + {1834,1834, 0, 0, 3820, 1666, 0, 0 }, + {1835,1835, 0, 0, 40000, 73, 0, 0 }, + {1836,1836, 0, 0, 40000, 333, 0, 0 }, + {1837,1837, 0, 0, 40000, 220, 0, 0 }, + {1838,1838, 0, 0, 40000, 0, 0, 0 }, + {1839,1839, 0, 0, 40000, 53, 0, 0 }, + {1840,1840, 0, 0, 40000, 60, 0, 0 }, + {1841,1841, 0, 0, 5913, 2306, 0, 0 }, + {1842,1842, 0, 0, 7713, 2466, 0, 0 }, + { 525, 525, 0, 0, 4660, 660, 0, 0 }, + {1843,1843, 0, 0, 40000, 313, 0, 0 }, + {1844,1844, 0, 0, 40000, 0, 0, 0 }, + {1845,1845, 0, 0, 40000, 0, 0, 0 }, + {1846,1846, 0, 0, 1246, 453, 0, 0 }, + {1847,1847, 0, 0, 9600, 1580, 0, 0 }, + {1848,1848, 0, 0, 40000, 106, 0, 0 }, + {1849,1849, 0, 0, 2040, 400, 0, 0 }, + {1850,1850, 0, 0, 40000, 73, 0, 0 }, + {1851,1851, 0, 0, 4220, 620, 0, 0 }, + {1852,1852, 0, 0, 40000, 0, 0, 0 }, + {1853,1853, 0, 0, 40000, 433, 0, 0 }, + {1854,1854, 0, 0, 40000, 66, 0, 0 }, + {1855,1855, 0, 0, 40000, 46, 0, 0 }, + {1856,1856, 0, 0, 40000, 240, 0, 0 }, + {1857,1857, 0, 0, 40000, 313, 0, 0 }, + {1858,1858, 0, 0, 40000, 26, 0, 0 }, + {1859,1859, 0, 0, 40000, 0, 0, 0 }, + {1860,1860, 0, 0, 40000, 73, 0, 0 }, + {1861,1861, 0, 0, 6940, 66, 0, 0 }, + {1862,1862, 0, 0, 40000, 0, 0, 0 }, + {1863,1863, 0, 0, 40000, 60, 0, 0 }, + {1864,1864, 0, 0, 8140, 1440, 0, 0 }, + {1865,1865, 0, 0, 40000, 0, 0, 0 }, + {1866,1866, 0, 0, 40000, 613, 0, 0 }, + {1867,1867, 0, 0, 40000, 0, 0, 0 }, + {1868,1868, 0, 0, 633, 233, 0, 0 }, + {1869,1869, 0, 0, 40000, 226, 0, 0 }, + {1870,1870, 0, 0, 2280, 746, 0, 0 }, + {1871,1871, 0, 0, 1940, 633, 0, 0 }, + {1872,1872, 0, 0, 4220, 620, 0, 0 }, + {1873,1873, 0, 0, 40000, 133, 0, 0 }, + {1874,1874, 41, 0, 380, 153, 0, 0 }, + {1875,1875, 70, 0, 106, 26, 0, 0 }, + {1876,1876, 60, 0, 380, 206, 0, 0 }, + {1877,1877, 80, 0, 100, 26, 0, 0 }, + {1878,1878, 84, 0, 120, 60, 0, 0 }, + {1879,1879, 72, 0, 500, 433, 0, 0 }, + {1880,1880, 84, 0, 860, 553, 0, 0 }, + { 128, 128, 70, 0, 106, 26, 0, 0 }, + { 132, 132, 60, 0, 146, 86, 0, 0 }, + {1881,1882, 0, 4, 40000, 260, 0, 0 }, + {1883,1883, 0, 0, 40000, 0, 0, 0 }, + {1884,1885, 0, 4, 40000, 73, 0, 0 }, + {1886,1887, 0, 4, 40000, 86, 0, 0 }, + {1888,1889, 0, 4, 40000, 73, 0, 0 }, + {1890,1890, 0, 0, 40000, 300, 0, 0 }, + {1891,1891, 0, 0, 40000, 693, 0, 0 }, + {1892,1892, 0, 0, 40000, 586, 0, 0 }, + {1893,1893, 0, 0, 40000, 286, 0, 0 }, + {1894,1894, 0, 0, 1620, 773, 0, 0 }, + {1895,1895, 0, 0, 40000, 0, 0, 0 }, + {1896,1896, 0, 0, 40000, 193, 0, 0 }, + {1897,1897, 0, 0, 1873, 820, 0, 0 }, + {1898,1898, 0, 0, 4520, 753, 0, 0 }, + {1899,1899, 0, 0, 40000, 0, 0, 0 }, + {1900,1900, 0, 0, 40000, 220, 0, 0 }, + {1901,1901, 0, 0, 40000, 133, 0, 0 }, + {1902,1902, 0, 0, 40000, 73, 0, 0 }, + {1903,1903, 0, 0, 40000, 0, 0, 0 }, + {1904,1904, 0, 0, 7326, 2420, 0, 0 }, + {1905,1905, 0, 0, 1186, 446, 0, 0 }, + {1906,1906, 0, 0, 40000, 553, 0, 0 }, + {1907,1907, 0, 0, 40000, 293, 0, 0 }, + {1908,1908, 0, 0, 40000, 586, 0, 0 }, + {1909,1909, 0, 0, 2326, 793, 0, 0 }, + { 501, 501, 0, 0, 480, 226, 0, 0 }, + {1910,1910, 0, 0, 40000, 93, 0, 0 }, + {1911,1911, 0, 0, 620, 226, 0, 0 }, + {1912,1912, 0, 0, 2373, 800, 0, 0 }, + {1913,1913, 0, 0, 40000, 4986, 0, 0 }, + {1914,1914, 0, 0, 626, 240, 0, 0 }, + { 511, 511, 0, 0, 2326, 800, 0, 0 }, + {1915,1915, 0, 0, 340, 146, 0, 0 }, + {1910,1910, 60, 0, 40000, 93, 0, 0 }, + { 511, 511, 72, 0, 1566, 546, 0, 0 }, + {1915,1915, 84, 0, 246, 120, 0, 0 }, + {1916,1916, 0, 0, 40000, 0, 0, 0 }, + {1917,1917, 0, 0, 2713, 666, 0, 0 }, + {1918,1918, 0, 0, 40000, 0, 0, 0 }, + {1919,1919, 0, 0, 40000, 46, 0, 0 }, + {1920,1920, 0, 0, 40000, 0, 0, 0 }, + {1921,1921, 0, 0, 40000, 53, 0, 0 }, + {1922,1922, 0, 0, 40000, 33, 0, 0 }, + {1923,1923, 0, 0, 2073, 193, 0, 0 }, + {1924,1924, 0, 0, 40000, 146, 0, 0 }, + {1925,1925, 0, 0, 40000, 100, 0, 0 }, + {1926,1926, 0, 0, 40000, 93, 0, 0 }, + {1927,1927, 0, 0, 40000, 73, 0, 0 }, + {1928,1928, 0, 0, 40000, 540, 0, 0 }, + {1929,1929, 0, 0, 40000, 520, 0, 0 }, + {1930,1930, 0, 0, 40000, 506, 0, 0 }, + {1931,1931, 0, 0, 7406, 200, 0, 0 }, + {1932,1932, 0, 0, 5906, 133, 0, 0 }, + {1933,1933, 0, 0, 7426, 240, 0, 0 }, + {1934,1934, 0, 0, 7426, 240, 0, 0 }, + {1935,1935, 0, 0, 40000, 66, 0, 0 }, + {1936,1936, 0, 0, 40000, 66, 0, 0 }, + {1937,1937, 0, 0, 40000, 53, 0, 0 }, + {1938,1938, 0, 0, 40000, 66, 0, 0 }, + {1939,1939, 0, 0, 40000, 66, 0, 0 }, + {1940,1940, 0, 0, 40000, 53, 0, 0 }, + {1941,1941, 0, 0, 40000, 2146, 0, 0 }, + {1942,1942, 0, 0, 40000, 1126, 0, 0 }, + {1943,1943, 0, 0, 40000, 1020, 0, 0 }, + {1944,1944, 0, 0, 40000, 433, 0, 0 }, + {1945,1945, 0, 0, 40000, 0, 0, 0 }, + {1946,1946, 0, 0, 40000, 140, 0, 0 }, + {1947,1947, 0, 0, 4660, 660, 0, 0 }, + {1948,1948, 0, 0, 40000, 66, 0, 0 }, + {1949,1949, 0, 0, 40000, 4193, 0, 0 }, + {1950,1950, 0, 0, 7713, 2466, 0, 0 }, + {1951,1951, 0, 0, 40000, 73, 0, 0 }, + {1952,1952, 0, 0, 8100, 2093, 0, 0 }, + {1953,1953, 0, 0, 40000, 86, 0, 0 }, + {1954,1954, 0, 0, 40000, 80, 0, 0 }, + {1955,1955, 0, 0, 4113, 1526, 0, 0 }, + {1956,1956, 0, 0, 40000, 66, 0, 0 }, + {1957,1957, 0, 0, 40000, 100, 0, 0 }, + {1958,1958, 0, 0, 40000, 213, 0, 0 }, + {1959,1959, 0, 0, 40000, 100, 0, 0 }, + {1960,1960, 0, 0, 1186, 100, 0, 0 }, + {1961,1961, 0, 0, 40000, 433, 0, 0 }, + {1962,1962, 0, 0, 40000, 146, 0, 0 }, + {1963,1963, 0, 0, 40000, 400, 0, 0 }, + {1964,1964, 0, 0, 40000, 66, 0, 0 }, + {1965,1965, 0, 0, 40000, 193, 0, 0 }, + {1966,1966, 0, 0, 1153, 100, 0, 0 }, + {1967,1967, 0, 0, 4800, 1400, 0, 0 }, + {1968,1968, 0, 0, 2906, 713, 0, 0 }, + {1969,1969, 0, 0, 40000, 73, 0, 0 }, + {1970,1970, 0, 0, 2280, 746, 0, 0 }, + {1971,1971, 0, 0, 40000, 66, 0, 0 }, + {1972,1972, 0, 0, 40000, 86, 0, 0 }, + {1973,1973, 0, 0, 40000, 86, 0, 0 }, + {1974,1974, 0, 0, 40000, 66, 0, 0 }, + {1975,1975, 0, 0, 40000, 66, 0, 0 }, + {1976,1976, 0, 0, 40000, 66, 0, 0 }, + {1977,1977, 0, 0, 40000, 46, 0, 0 }, + {1978,1978, 0, 0, 40000, 73, 0, 0 }, + {1979,1979, 0, 0, 40000, 73, 0, 0 }, + {1980,1980, 0, 0, 40000, 66, 0, 0 }, + {1981,1981, 0, 0, 40000, 66, 0, 0 }, + {1982,1982, 0, 0, 40000, 66, 0, 0 }, + {1983,1983, 0, 0, 40000, 73, 0, 0 }, + {1984,1984, 0, 0, 40000, 73, 0, 0 }, + {1985,1985, 0, 0, 40000, 253, 0, 0 }, + {1986,1986, 0, 0, 40000, 126, 0, 0 }, + {1987,1987, 0, 0, 40000, 126, 0, 0 }, + {1988,1988, 0, 0, 40000, 66, 0, 0 }, + {1989,1989, 0, 0, 40000, 66, 0, 0 }, + {1990,1990, 0, 0, 40000, 53, 0, 0 }, + {1991,1991, 0, 0, 40000, 140, 0, 0 }, + {1992,1992, 0, 0, 40000, 40, 0, 0 }, + {1993,1993, 0, 0, 40000, 73, 0, 0 }, + {1994,1994, 0, 0, 40000, 66, 0, 0 }, + {1995,1995, 0, 0, 40000, 73, 0, 0 }, + {1996,1996, 0, 0, 40000, 73, 0, 0 }, + {1997,1997, 0, 0, 40000, 73, 0, 0 }, + {1998,1998, 0, 0, 40000, 73, 0, 0 }, + {1999,1999, 0, 0, 40000, 66, 0, 0 }, + {2000,2000, 0, 0, 40000, 433, 0, 0 }, + {2001,2001, 0, 0, 40000, 433, 0, 0 }, + {2002,2002, 0, 0, 2440, 706, 0, 0 }, + {2003,2003, 0, 0, 13960, 4800, 0, 0 }, + {2004,2004, 0, 0, 7393, 2480, 0, 0 }, + {2005,2005, 0, 0, 7220, 2073, 0, 0 }, + {2006,2006, 0, 0, 633, 233, 0, 0 }, + {2007,2007, 0, 0, 2326, 780, 0, 0 }, + {2008,2008, 0, 0, 40000, 73, 0, 0 }, + {2009,2009, 0, 0, 40000, 106, 0, 0 }, + {2010,2010, 0, 0, 40000, 126, 0, 0 }, + {2011,2011, 0, 0, 40000, 386, 0, 0 }, + {2012,2012, 0, 0, 40000, 66, 0, 0 }, + {2013,2013, 0, 0, 6893, 1273, 0, 0 }, + {2014,2014, 0, 0, 2546, 633, 0, 0 }, + {2015,2015, 0, 0, 206, 106, 0, 0 }, + {2016,2016, 0, 0, 213, 113, 0, 0 }, + {2017,2017, 0, 0, 360, 140, 0, 0 }, + {2018,2018, 0, 0, 1013, 193, 0, 0 }, + {2019,2019, 0, 0, 266, 66, 0, 0 }, + {2020,2020, 0, 0, 1880, 660, 0, 0 }, + {2021,2021, 0, 0, 286, 206, 0, 0 }, + {2022,2022, 0, 0, 3706, 1353, 0, 0 }, + {2023,2023, 0, 0, 1106, 380, 0, 0 }, + {2024,2024, 0, 0, 13220, 2466, 0, 0 }, + {2025,2025, 0, 0, 333, 26, 0, 0 }, + {2026,2026, 0, 0, 7346, 2440, 0, 0 }, + {2027,2027, 0, 0, 1273, 453, 0, 0 }, + { 352, 352, 51, 2, 6, 0, 0, 0 }, + {2028,2028, 35, 0, 700, 253, 0, 0 }, + {2028,2028, 36, 0, 706, 266, 0, 0 }, + {2029,2029, 47, 0, 100, 33, 0, 0 }, + {2030,2030, 38, 0, 346, 140, 0, 0 }, + {2019,2019, 39, 0, 220, 106, 0, 0 }, + {2031,2031, 45, 0, 286, 133, 0, 0 }, + { 492, 492, 41, 0, 1040, 406, 0, 0 }, + {2032,2032, 42, 0, 220, 106, 0, 0 }, + {2033,2033, 44, 0, 500, 193, 0, 0 }, + { 492, 492, 48, 0, 833, 346, 0, 0 }, + {2034,2034, 46, 0, 1866, 646, 0, 0 }, + { 492, 492, 53, 0, 873, 386, 0, 0 }, + { 167, 167, 56, 0, 646, 353, 0, 0 }, + {2035,2035, 61, 0, 366, 146, 0, 0 }, + {2036,2036, 56, 0, 1346, 473, 0, 0 }, + {2037,2037, 60, 0, 213, 126, 0, 0 }, + { 144, 144, 59, 0, 213, 126, 0, 0 }, + {2038,2038, 59, 0, 106, 40, 0, 0 }, + { 169, 169, 51, 0, 380, 366, 0, 0 }, + { 169, 169, 45, 0, 380, 366, 0, 0 }, + {2039,2039, 72, 0, 246, 20, 0, 0 }, + {2040,2040, 60, 0, 280, 20, 0, 0 }, + {2041,2041, 58, 0, 373, 360, 0, 0 }, + {2042,2042, 53, 0, 380, 366, 0, 0 }, + {2043,2043, 73, 0, 120, 26, 0, 0 }, + { 158, 158, 75, 0, 126, 140, 0, 0 }, + {2044,2044, 0, 0, 6786, 1073, 0, 0 }, + {2045,2045, 0, 0, 2046, 473, 0, 0 }, + {2046,2046, 0, 0, 3746, 1273, 0, 0 }, + {2047,2047, 0, 0, 1200, 3086, 0, 0 }, + {2048,2048, 0, 0, 1200, 3080, 0, 0 }, + {2049,2049, 0, 0, 40000, 2453, 0, 0 }, + {2050,2050, 0, 0, 40000, 413, 0, 0 }, + {2051,2051, 0, 0, 980, 2553, 0, 0 }, + {2052,2052, 0, 0, 40000, 2420, 0, 0 }, + {2053,2053, 0, 0, 40000, 2506, 0, 0 }, + {2054,2054, 0, 0, 40000, 380, 0, 0 }, + {2055,2055, 0, 0, 40000, 660, 0, 0 }, + {2056,2056, 0, 0, 40000, 73, 0, 0 }, + {2057,2057, 0, 0, 40000, 333, 0, 0 }, + {2058,2058, 0, 0, 833, 146, 0, 0 }, + {2059,2059, 0, 0, 1686, 620, 0, 0 }, + {2060,2060, 0, 0, 40000, 73, 0, 0 }, + {2061,2061, 0, 0, 40000, 0, 0, 0 }, + {2062,2062, 0, 0, 1873, 633, 0, 0 }, + {2063,2063, 0, 0, 40000, 380, 0, 0 }, + {2064,2064, 0, 0, 366, 286, 0, 0 }, + {2065,2065, 0, 0, 8866, 1366, 0, 0 }, + {2066,2066, 0, 0, 40000, 1513, 0, 0 }, + {2067,2067, 0, 0, 40000, 333, 0, 0 }, + {2068,2068, 0, 0, 9600, 1573, 0, 0 }, + {2069,2069, 0, 0, 3293, 746, 0, 0 }, + {2070,2070, 0, 0, 40000, 53, 0, 0 }, + {2071,2071, 0, 0, 40000, 73, 0, 0 }, + {2072,2072, 0, 0, 40000, 73, 0, 0 }, + {2073,2073, 0, 0, 40000, 240, 0, 0 }, + {2074,2074, 0, 0, 40000, 240, 0, 0 }, + {2075,2075, 0, 0, 40000, 140, 0, 0 }, + {2076,2076, 0, 0, 40000, 113, 0, 0 }, + {2077,2077, 0, 0, 40000, 240, 0, 0 }, + {2078,2078, 0, 0, 3613, 1146, 0, 0 }, + {2079,2079, 0, 0, 40000, 126, 0, 0 }, + {2080,2080, 0, 0, 40000, 0, 0, 0 }, + {2081,2081, 0, 0, 40000, 633, 0, 0 }, + {2082,2082, 0, 0, 40000, 453, 0, 0 }, + {2083,2083, 0, 0, 40000, 1146, 0, 0 }, + {2084,2084, 0, 0, 40000, 3600, 0, 0 }, + {2085,2085, 0, 0, 40000, 1586, 0, 0 }, + {2086,2086, 0, 0, 40000, 1586, 0, 0 }, + {2087,2087, 0, 0, 40000, 1586, 0, 0 }, + {2088,2088, 0, 0, 40000, 1646, 0, 0 }, + {2089,2089, 0, 0, 40000, 1580, 0, 0 }, + {2090,2090, 0, 0, 40000, 4393, 0, 0 }, + {2091,2091, 0, 0, 40000, 4540, 0, 0 }, + {2092,2092, 0, 0, 21373, 6160, 0, 0 }, + {2093,2093, 0, 0, 40000, 633, 0, 0 }, + {2094,2094, 0, 0, 18420, 6146, 0, 0 }, + {2095,2095, 0, 0, 2306, 813, 0, 0 }, + {2096,2096, 0, 0, 2813, 333, 0, 0 }, + {2097,2097, 0, 0, 3106, 600, 0, 0 }, + {2098,2098, 0, 0, 1026, 1580, 0, 0 }, + {2099,2099, 0, 0, 1873, 346, 0, 0 }, + {2100,2100, 0, 0, 40000, 73, 0, 0 }, + {2101,2101, 0, 0, 40000, 73, 0, 0 }, + {2102,2102, 0, 0, 1200, 1906, 0, 0 }, + {2103,2103, 0, 0, 980, 1313, 0, 0 }, + {2104,2104, 0, 0, 200, 20, 0, 0 }, + {2105,2105, 0, 0, 640, 253, 0, 0 }, + {2106,2106, 0, 0, 3120, 240, 0, 0 }, + {2107,2107, 0, 0, 753, 146, 0, 0 }, + {2108,2108, 0, 0, 40000, 3060, 0, 0 }, + {2109,2109, 0, 0, 40000, 233, 0, 0 }, + {2110,2110, 0, 0, 40000, 246, 0, 0 }, + {2111,2111, 0, 0, 40000, 240, 0, 0 }, + { 752, 752, 60, 0, 173, 20, 0, 0 }, + { 755, 755, 12, 0, 626, 240, 0, 0 }, + {2112,2112, 89, 0, 113, 26, 0, 0 }, + {2113,2113, 89, 0, 700, 266, 0, 0 }, + { 755, 755, 14, 0, 626, 240, 0, 0 }, + { 755, 755, 16, 0, 626, 246, 0, 0 }, + {2114,2114, 84, 0, 1593, 553, 0, 0 }, + { 755, 755, 19, 0, 626, 240, 0, 0 }, + {2115,2115, 38, 0, 220, 166, 0, 0 }, + {2116,2116, 36, 0, 1686, 760, 0, 0 }, + { 755, 755, 28, 0, 626, 240, 0, 0 }, + { 755, 755, 26, 0, 626, 240, 0, 0 }, + { 755, 755, 35, 0, 633, 246, 0, 0 }, + { 755, 755, 30, 0, 626, 240, 0, 0 }, + {2117,2117, 60, 0, 180, 53, 0, 0 }, + {2104,2104, 60, 0, 173, 20, 0, 0 }, + {2104,2104, 55, 0, 173, 20, 0, 0 }, + { 730, 730, 94, 0, 1886, 660, 0, 0 }, + {2118,2118, 0, 0, 1226, 73, 0, 0 }, + {2119,2119, 0, 0, 40000, 0, 0, 0 }, + {2120,2120, 0, 0, 40000, 146, 0, 0 }, + {2121,2121, 0, 0, 40000, 80, 0, 0 }, + {2122,2122, 0, 0, 40000, 80, 0, 0 }, + {2123,2123, 0, 0, 40000, 0, 0, 0 }, + {2124,2124, 0, 0, 40000, 126, 0, 0 }, + {2125,2125, 0, 0, 40000, 213, 0, 0 }, + {2126,2126, 0, 0, 40000, 80, 0, 0 }, + {2127,2127, 0, 0, 40000, 73, 0, 0 }, + {2128,2128, 0, 0, 40000, 73, 0, 0 }, + {2129,2129, 0, 0, 40000, 73, 0, 0 }, + {2130,2130, 0, 0, 40000, 80, 0, 0 }, + {2131,2131, 0, 0, 40000, 73, 0, 0 }, + {2132,2132, 0, 0, 40000, 73, 0, 0 }, + {2133,2133, 0, 0, 40000, 66, 0, 0 }, + {2134,2134, 0, 0, 40000, 186, 0, 0 }, + {2135,2135, 0, 0, 9966, 426, 0, 0 }, + {2136,2136, 0, 0, 40000, 400, 0, 0 }, + {2137,2137, 0, 0, 40000, 326, 0, 0 }, + {2138,2138, 0, 0, 386, 80, 0, 0 }, + {2139,2139, 0, 0, 40000, 246, 0, 0 }, + {2140,2140, 0, 0, 3473, 73, 0, 0 }, + {2141,2141, 60, 0, 160, 66, 0, 0 }, + {2141,2141, 44, 0, 160, 60, 0, 0 }, + {2142,2142, 47, 0, 173, 93, 0, 0 }, + {2143,2143, 47, 0, 186, 80, 0, 0 }, + {2144,2144, 62, 0, 1933, 93, 0, 0 }, + {2145,2145, 93, 0, 1146, 473, 0, 0 }, + {2146,2146, 50, 0, 286, 93, 0, 0 }, + {2145,2145, 40, 0, 2013, 840, 0, 0 }, + {2147,2147, 60, 0, 106, 73, 0, 0 }, + { 898, 898, 60, 0, 173, 133, 0, 0 }, + {2147,2147, 57, 0, 106, 73, 0, 0 }, + { 900, 900, 42, 0, 620, 240, 0, 0 }, + { 900, 900, 38, 0, 626, 240, 0, 0 }, + { 908, 908, 88, 0, 160, 26, 0, 0 }, + {2148,2148, 0, 0, 9440, 140, 0, 0 }, + {2149,2149, 0, 0, 40000, 73, 0, 0 }, + {2150,2150, 0, 0, 4613, 420, 0, 0 }, + {2151,2151, 0, 0, 40000, 86, 0, 0 }, + {2152,2152, 0, 0, 40000, 406, 0, 0 }, + {2153,2153, 0, 0, 40000, 440, 0, 0 }, + {2154,2154, 0, 0, 4340, 133, 0, 0 }, + {2155,2155, 0, 0, 4460, 706, 0, 0 }, + {2156,2156, 0, 0, 40000, 73, 0, 0 }, + {2157,2157, 0, 0, 4660, 1573, 0, 0 }, + {2158,2158, 0, 0, 966, 333, 0, 0 }, + {2159,2159, 0, 0, 1933, 640, 0, 0 }, + { 136, 136, 0, 0, 2326, 786, 0, 0 }, + { 168, 168, 0, 0, 286, 366, 0, 0 }, + { 164, 164, 0, 0, 7373, 2460, 0, 0 }, + { 167, 167, 0, 0, 793, 426, 0, 0 }, + {2160,2160, 65, 0, 166, 73, 0, 0 }, + {2161,2161, 21, 0, 480, 146, 0, 0 }, + {2162, 173, 0, 4, 4220, 80, 0, 0 }, + {2163,2164, 0, 4, 4613, 3060, 0, 0 }, + {2165,2166, 0, 4, 7193, 3920, 0, 0 }, + {2167,2168, 0, 4, 3746, 1253, 0, 0 }, + {2169,2170, 0, 4, 6226, 2393, 0, 0 }, + {2171,2172, 0, 4, 18053, 226, 0, 0 }, + {2173,2174, 0, 4, 40000, 713, 0, 0 }, + {2175,2174, 0, 4, 40000, 733, 0, 0 }, + {2176, 299, 0, 4, 40000, 273, 0, 0 }, + {2177,2178, 0, 4, 40000, 66, 0, 0 }, + {2179,2180, 0, 4, 40000, 393, 0, 0 }, + {2181,2182, 0, 4, 40000, 413, 0, 0 }, + {2183,2184, 0, 4, 7366, 200, 0, 0 }, + { 127, 127, 65, 0, 226, 120, 0, 0 }, + { 127, 127, 72, 0, 180, 100, 0, 0 }, + { 364, 365, 52, 4, 120, 26, 0, 0 }, + {2185,2186, 60, 4, 173, 93, 0, 0 }, + {1550,1551, 47, 4, 520, 213, 0, 0 }, + {1556,1557, 76, 4, 766, 306, 0, 0 }, + { 374, 375, 84, 4, 813, 300, 0, 0 }, + {1564,1565, 83, 4, 220, 106, 0, 0 }, + {1568,1569, 24, 4, 1806, 620, 0, 0 }, + {1556,1557, 77, 4, 760, 300, 0, 0 }, + {1572,1573, 60, 4, 280, 126, 0, 0 }, + {1574,1575, 65, 4, 286, 193, 0, 0 }, + { 391, 392, 44, 4, 160, 53, 0, 0 }, + { 391, 393, 40, 4, 460, 66, 0, 0 }, + {1606,1607, 72, 4, 120, 73, 0, 0 }, + { 398, 399, 73, 4, 1286, 173, 0, 0 }, + {1608,1609, 70, 4, 1560, 300, 0, 0 }, + {2187,2187, 0, 0, 40000, 353, 0, 0 }, + {2188,2188, 0, 0, 40000, 333, 0, 0 }, + {2189,2189, 0, 0, 5913, 2306, 0, 0 }, + {2190,2190, 0, 0, 7720, 1260, 0, 0 }, + {2191,2191, 0, 0, 213, 6420, 0, 0 }, + {2192,2192, 0, 0, 40000, 380, 0, 0 }, + {2193,2193, 0, 0, 1153, 760, 0, 0 }, + {2194,2194, 0, 0, 40000, 66, 0, 0 }, + {2195,2195, 0, 0, 4440, 66, 0, 0 }, + {2196,2196, 0, 0, 40000, 73, 0, 0 }, + {2197,2197, 0, 0, 40000, 53, 0, 0 }, + {2198,2198, 0, 0, 40000, 60, 0, 0 }, + {2199,2199, 0, 0, 40000, 60, 0, 0 }, + {2200,2200, 0, 0, 8133, 1433, 0, 0 }, + { 528, 528, 0, 0, 966, 346, 0, 0 }, + {2201,2201, 0, 0, 40000, 126, 0, 0 }, + {2202,2202, 0, 0, 286, 1293, 0, 0 }, + {2203,2203, 0, 0, 40000, 0, 0, 0 }, + {2204,2204, 41, 0, 246, 20, 0, 0 }, + {2205,2205, 84, 0, 160, 26, 0, 0 }, + {2206,2206, 72, 0, 440, 180, 0, 0 }, + { 741, 741, 48, 0, 220, 26, 0, 0 }, + {2207,2207, 0, 0, 2126, 173, 0, 0 }, + {2208,2208, 0, 0, 40000, 0, 0, 0 }, + {2209,2209, 0, 0, 40000, 380, 0, 0 }, + {2210,2210, 0, 0, 4553, 1486, 0, 0 }, + {2211,2211, 0, 0, 40000, 73, 0, 0 }, + {2212,2212, 0, 0, 40000, 73, 0, 0 }, + {2213,2213, 0, 0, 1460, 80, 0, 0 }, + {2214,2214, 0, 0, 40000, 66, 0, 0 }, + {2215,2215, 0, 0, 40000, 186, 0, 0 }, + {2216,2216, 0, 0, 40000, 180, 0, 0 }, + {2217,2217, 0, 0, 40000, 173, 0, 0 }, + {2218,2218, 0, 0, 40000, 113, 0, 0 }, + {2219,2219, 0, 0, 40000, 86, 0, 0 }, + {2220,2220, 0, 0, 40000, 373, 0, 0 }, + {2221,2221, 0, 0, 40000, 113, 0, 0 }, + {2222,2222, 0, 0, 40000, 353, 0, 0 }, + {2223,2223, 0, 0, 40000, 66, 0, 0 }, + {2224,2224, 0, 0, 40000, 53, 0, 0 }, + {2225,2225, 0, 0, 40000, 66, 0, 0 }, + {2226,2226, 0, 0, 40000, 100, 0, 0 }, + {2227,2227, 0, 0, 40000, 73, 0, 0 }, + {2228,2228, 0, 0, 40000, 73, 0, 0 }, + {2229,2229, 0, 0, 40000, 66, 0, 0 }, + {2230,2230, 0, 0, 40000, 66, 0, 0 }, + {2231,2231, 0, 0, 40000, 80, 0, 0 }, + {2232,2232, 0, 0, 40000, 66, 0, 0 }, + {2233,2233, 0, 0, 40000, 80, 0, 0 }, + {2234,2234, 0, 0, 40000, 660, 0, 0 }, + {2235,2235, 0, 0, 40000, 120, 0, 0 }, + {2236,2236, 0, 0, 9820, 393, 0, 0 }, + {2237,2237, 0, 0, 40000, 73, 0, 0 }, + {2238,2238, 0, 0, 3620, 1166, 0, 0 }, + {2239,2239, 0, 0, 40000, 0, 0, 0 }, + {2240,2240, 0, 0, 40000, 0, 0, 0 }, + {2241,2241, 0, 0, 3020, 66, 0, 0 }, + {2242,2242, 0, 0, 6053, 1240, 0, 0 }, + {2243,2243, 0, 0, 633, 126, 0, 0 }, + {2244,2244, 0, 0, 40000, 66, 0, 0 }, + {2245,2245, 0, 0, 40000, 73, 0, 0 }, + {2246,2246, 0, 0, 626, 246, 0, 0 }, + {2247,2247, 60, 0, 173, 93, 0, 0 }, + {2248,2248, 60, 0, 673, 206, 0, 0 }, + {2249,2249, 48, 0, 673, 200, 0, 0 }, + {2250,2250, 60, 0, 1873, 653, 0, 0 }, + {2251,2251, 60, 0, 673, 200, 0, 0 }, + {2252,2252, 66, 0, 306, 120, 0, 0 }, + {2253,2253, 60, 0, 673, 200, 0, 0 }, + {2249,2249, 64, 0, 673, 206, 0, 0 }, + {2254,2254, 60, 0, 246, 106, 0, 0 }, + {2255,2255, 60, 0, 193, 120, 0, 0 }, + {2256,2256, 56, 0, 206, 13, 0, 0 }, + {2257,2257, 53, 0, 433, 73, 0, 0 }, + {2258,2258, 60, 0, 220, 113, 0, 0 }, + {2259,2259, 48, 0, 300, 66, 0, 0 }, + {2260,2260, 67, 0, 273, 60, 0, 0 }, + {2261,2261, 60, 0, 973, 360, 0, 0 }, + {2261,2261, 72, 0, 806, 273, 0, 0 }, + {2262,2262, 60, 0, 173, 93, 0, 0 }, + {2263,2263, 0, 0, 2493, 866, 0, 0 }, + {2264,2264, 24, 0, 173, 93, 0, 0 }, + {2265,2265, 36, 0, 140, 66, 0, 0 }, + { 343, 343, 36, 0, 146, 80, 0, 0 }, + { 347, 347, 0, 0, 353, 133, 0, 0 }, + { 347, 347, 12, 0, 420, 146, 0, 0 }, + {2266,2266, 12, 0, 346, 100, 0, 0 }, + {2267,2267, 24, 0, 106, 46, 0, 0 }, + {2267,2267, 36, 0, 100, 40, 0, 0 }, + {2268,2268, 0, 0, 1006, 293, 0, 0 }, + {2266,2266, 24, 0, 293, 93, 0, 0 }, + {2269,2269, 88, 0, 1106, 120, 0, 0 }, + {2270,2270, 88, 0, 666, 120, 0, 0 }, + {2271,2271, 13, 0, 760, 360, 0, 0 }, + { 351, 351, 0, 0, 966, 346, 0, 0 }, + {2271,2271, 15, 0, 760, 420, 0, 0 }, + {2272,2272, 0, 0, 4513, 640, 0, 0 }, + {2273,2273, 0, 0, 15486, 1580, 0, 0 }, + {2274,2274, 0, 0, 6940, 66, 0, 0 }, + {2275,2275, 0, 0, 6866, 2380, 0, 0 }, + {2276,2276, 0, 0, 7613, 1566, 0, 0 }, + {2277,2277, 0, 0, 1186, 420, 0, 0 }, + {2278,2278, 0, 0, 1166, 400, 0, 0 }, + {2279,2279, 0, 0, 40000, 2940, 0, 0 }, + {2280,2280, 0, 0, 40000, 0, 0, 0 }, + {2281,2281, 0, 0, 18226, 786, 0, 0 }, + {2282,2282, 0, 0, 40000, 0, 0, 0 }, + {2283,2283, 0, 0, 713, 200, 0, 0 }, + {2284,2284, 0, 0, 40000, 126, 0, 0 }, + {2285,2285, 0, 0, 40000, 353, 0, 0 }, + {2286,2286, 0, 0, 40000, 333, 0, 0 }, + {2287,2287, 0, 0, 40000, 0, 0, 0 }, + {2288,2288, 0, 0, 40000, 0, 0, 0 }, + {2289,2289, 0, 0, 40000, 0, 0, 0 }, + {2290,2290, 0, 0, 40000, 0, 0, 0 }, + {2291,2291, 0, 0, 40000, 73, 0, 0 }, + {2292,2292, 0, 0, 40000, 66, 0, 0 }, + {2293,2293, 0, 0, 15893, 153, 0, 0 }, + {2294,2294, 0, 0, 40000, 253, 0, 0 }, + {2295,2295, 0, 0, 2813, 333, 0, 0 }, + {2296,2296, 0, 0, 40000, 3920, 0, 0 }, + {2297,2297, 79, 0, 113, 113, 0, 0 }, + {2297,2297, 72, 0, 126, 140, 0, 0 }, + {2298,2298, 72, 0, 100, 26, 0, 0 }, + {2298,2298, 79, 0, 100, 26, 0, 0 }, + { 554, 554, 60, 0, 400, 126, 0, 0 }, + {2299,2299, 72, 0, 793, 173, 0, 0 }, + {2300,2300, 84, 0, 226, 66, 0, 0 }, + { 555, 555, 66, 0, 113, 53, 0, 0 }, + {2301,2302, 35, 4, 2320, 800, 0, 0 }, + {2303,2304, 52, 4, 120, 26, 0, 0 }, + {2305,1548, 48, 4, 173, 93, 0, 0 }, + {1595,1595, 58, 0, 146, 166, 0, 0 }, + {2305,1548, 60, 4, 173, 93, 0, 0 }, + {2306,2307, 47, 4, 1886, 700, 0, 0 }, + {2306,2307, 43, 4, 1946, 740, 0, 0 }, + {2306,2307, 49, 4, 1873, 686, 0, 0 }, + {2306,2307, 51, 4, 1880, 706, 0, 0 }, + {2306,2307, 54, 4, 1900, 720, 0, 0 }, + {2306,2307, 57, 4, 1893, 720, 0, 0 }, + {2306,2307, 72, 4, 1586, 606, 0, 0 }, + {2306,2307, 60, 4, 1893, 720, 0, 0 }, + {2306,2307, 76, 4, 1586, 606, 0, 0 }, + {2306,2307, 84, 4, 1593, 613, 0, 0 }, + {2306,2307, 36, 4, 2380, 920, 0, 0 }, + {1560,2308, 65, 4, 293, 213, 0, 0 }, + {2309,2310, 84, 4, 1366, 306, 0, 0 }, + {1564,1564, 83, 0, 220, 113, 0, 0 }, + { 380, 381, 84, 4, 1580, 566, 0, 0 }, + {1568,1568, 24, 0, 1833, 613, 0, 0 }, + {2306,2307, 77, 4, 1586, 606, 0, 0 }, + {2311,2312, 60, 4, 280, 126, 0, 0 }, + {2313,2314, 65, 4, 506, 200, 0, 0 }, + {2315,2315, 59, 0, 106, 40, 0, 0 }, + {2316,2316, 51, 0, 386, 373, 0, 0 }, + {1612,1612, 45, 0, 393, 380, 0, 0 }, + {2317,2317, 71, 0, 446, 180, 0, 0 }, + {2318,2318, 60, 0, 280, 20, 0, 0 }, + {2319,2319, 58, 0, 393, 373, 0, 0 }, + {2320,2320, 53, 0, 393, 380, 0, 0 }, + { 397, 397, 64, 0, 220, 86, 0, 0 }, + {2321,2321, 71, 0, 106, 46, 0, 0 }, + {2322,2322, 61, 0, 986, 340, 0, 0 }, + {2323,2323, 61, 0, 1893, 633, 0, 0 }, + {2324, 392, 44, 4, 166, 46, 0, 0 }, + {2324, 393, 40, 4, 460, 60, 0, 0 }, + {1595,1595, 69, 0, 126, 140, 0, 0 }, + {1595,1595, 68, 0, 126, 140, 0, 0 }, + {1595,1595, 63, 0, 146, 166, 0, 0 }, + {2325,2326, 74, 4, 380, 106, 0, 0 }, + {2327,2328, 60, 4, 1020, 333, 0, 0 }, + {2329,2330, 80, 4, 453, 560, 0, 0 }, + {2331,2332, 64, 4, 1880, 640, 0, 0 }, + { 397, 397, 72, 0, 193, 80, 0, 0 }, + {2333,2334, 78, 4, 793, 306, 0, 0 }, + {1608,1609, 82, 4, 1560, 300, 0, 0 }, + {2315,2315, 48, 0, 106, 46, 0, 0 }, + {2316,2316, 53, 0, 386, 373, 0, 0 }, + {2335,2335, 0, 0, 3586, 1133, 0, 0 }, + {2336,2337, 0, 4, 1180, 420, 0, 0 }, + {2338,2339, 0, 4, 40000, 320, 0, 0 }, + {2340,2340, 0, 0, 8826, 1346, 0, 0 }, + {2341,2341, 0, 0, 3440, 753, 0, 0 }, + {2342,2342, 0, 0, 40000, 360, 0, 0 }, + {2343,2343, 0, 0, 40000, 413, 0, 0 }, + {2344,2345, 0, 4, 40000, 60, 0, 0 }, + {2346,2346, 0, 0, 40000, 60, 0, 0 }, + {2347,2348, 0, 4, 40000, 126, 0, 0 }, + {2349,2350, 0, 4, 40000, 73, 0, 0 }, + {2351,2352, 0, 4, 40000, 73, 0, 0 }, + {2353,2354, 0, 4, 40000, 86, 0, 0 }, + {2355,2356, 0, 4, 40000, 453, 0, 0 }, + {2357,2357, 14, 0, 186, 20, 0, 0 }, + {2358,2358, 35, 0, 246, 73, 0, 0 }, + {2357,2357, 19, 0, 166, 26, 0, 0 }, + {2359,2359, 43, 0, 286, 133, 0, 0 }, + {2360,2360, 41, 0, 300, 113, 0, 0 }, + {2360,2360, 43, 0, 253, 106, 0, 0 }, + {2360,2360, 45, 0, 240, 100, 0, 0 }, + {2360,2360, 47, 0, 240, 100, 0, 0 }, + {2361,2362, 0, 4, 14633, 333, 0, 0 }, + {2363,2363, 0, 0, 7373, 1246, 0, 0 }, + {2364,2364, 0, 0, 4900, 233, 0, 0 }, + {2365,2365, 0, 0, 5106, 606, 0, 0 }, + {2366,2366, 0, 0, 1333, 153, 0, 0 }, + {2367,2367, 0, 0, 2093, 840, 0, 0 }, + {2368,2368, 0, 0, 3700, 226, 0, 0 }, + {2369,2369, 0, 0, 3546, 1266, 0, 0 }, + {2370,2370, 0, 0, 4606, 420, 0, 0 }, + {2371,2371, 0, 0, 14366, 606, 0, 0 }, + {2372,2372, 0, 0, 40000, 426, 0, 0 }, + {2373,2373, 0, 0, 3700, 200, 0, 0 }, + {2374,2374, 0, 0, 880, 440, 0, 0 }, + {2375,2375, 0, 0, 4660, 660, 0, 0 }, + {2376,2376, 0, 0, 3600, 1153, 0, 0 }, + {2377,2377, 0, 0, 40000, 73, 0, 0 }, + {2378,2378, 0, 0, 40000, 53, 0, 0 }, + {2379,2379, 0, 0, 40000, 333, 0, 0 }, + {2380,2380, 0, 0, 40000, 73, 0, 0 }, + {2381,2381, 0, 0, 40000, 73, 0, 0 }, + {2382,2382, 0, 0, 40000, 66, 0, 0 }, + {2383,2383, 0, 0, 40000, 73, 0, 0 }, + {2384,2384, 0, 0, 40000, 73, 0, 0 }, + {2385,2385, 0, 0, 840, 226, 0, 0 }, + {2386,2386, 0, 0, 2093, 86, 0, 0 }, + {2387,2387, 0, 0, 906, 73, 0, 0 }, + { 402, 402, 0, 0, 273, 60, 0, 0 }, + {2388,2388, 0, 0, 40000, 820, 0, 0 }, + {2389,2389, 0, 0, 4740, 93, 0, 0 }, + {2390,2390, 0, 0, 706, 106, 0, 0 }, + {2391,2391, 0, 0, 40000, 0, 0, 0 }, + {2392,2392, 0, 0, 3840, 2306, 0, 0 }, + {2393,2393, 0, 0, 3400, 493, 0, 0 }, + {2394,2394, 0, 0, 40000, 53, 0, 0 }, + {2395,2395, 0, 0, 40000, 133, 0, 0 }, + {2396,2397, 0, 4, 3066, 1400, 0, 0 }, + {2398,2398, 0, 0, 1080, 580, 0, 0 }, + {2399,2400, 0, 4, 2220, 400, 0, 0 }, + {2401,2401, 0, 0, 40000, 193, 0, 0 }, + {2402,2402, 0, 0, 40000, 60, 0, 0 }, + {2403,2404, 0, 4, 40000, 146, 0, 0 }, + {2405,2406, 0, 4, 40000, 133, 0, 0 }, + {2407,2408, 0, 4, 40000, 66, 0, 0 }, + {2409,2409, 0, 0, 40000, 0, 0, 0 }, + {2410,2410, 0, 0, 40000, 73, 0, 0 }, + {2411,2411, 0, 0, 40000, 66, 0, 0 }, + {2412,2413, 0, 4, 40000, 153, 0, 0 }, + {2414,2414, 0, 0, 40000, 126, 0, 0 }, + {2415,2416, 0, 4, 40000, 466, 0, 0 }, + {2417,2418, 0, 4, 40000, 113, 0, 0 }, + {2419,2420, 0, 4, 1280, 73, 0, 0 }, + {2421,2422, 0, 4, 1106, 146, 0, 0 }, + {2423,2424, 0, 4, 3640, 113, 0, 0 }, + {2425,2426, 0, 4, 40000, 80, 0, 0 }, + {2427,2427, 33, 0, 300, 246, 0, 0 }, + {2428,2429, 38, 4, 53, 26, 0, 0 }, + {2430,2430, 38, 0, 106, 46, 0, 0 }, + {2431,2431, 38, 0, 340, 20, 0, 0 }, + {2432,2432, 40, 0, 73, 40, 0, 0 }, + {2433,2434, 41, 4, 293, 106, 0, 0 }, + {2435,2435, 0, 0, 133, 73, 0, 0 }, + {2435,2435, 41, 0, 133, 73, 0, 0 }, + {2360,2360, 48, 0, 240, 100, 0, 0 }, + {2436,2436, 17, 0, 4620, 1553, 0, 0 }, + {2360,2360, 50, 0, 240, 100, 0, 0 }, + {2435,2435, 45, 0, 126, 66, 0, 0 }, + {2437,2437,254, 2, 6, 0, 0, 0 }, + {2438,2438, 60, 0, 226, 93, 0, 0 }, + {2439,2439, 56, 0, 233, 93, 0, 0 }, + {2440,2440, 60, 0, 140, 66, 0, 0 }, + {2440,2440, 55, 0, 140, 60, 0, 0 }, + {2441,2441, 63, 0, 286, 126, 0, 0 }, + {2442,2442, 57, 0, 173, 93, 0, 0 }, + {2443,2443, 0, 0, 40000, 280, 0, 0 }, + {2444,2444, 0, 0, 40000, 0, 0, 0 }, + {2445,2445, 0, 0, 40000, 746, 0, 0 }, + {2446,2446, 0, 0, 40000, 353, 0, 0 }, + {2447,2447, 0, 0, 40000, 1173, 0, 0 }, + {2448,2448, 0, 0, 40000, 146, 0, 0 }, + {2449,2449, 0, 0, 40000, 1160, 0, 0 }, + {2450,2450, 0, 0, 40000, 353, 0, 0 }, + {2451,2451, 0, 0, 18313, 6046, 0, 0 }, + {2452,2452, 0, 0, 1206, 420, 0, 0 }, + { 752, 752, 55, 0, 173, 20, 0, 0 }, + {2453,2453, 0, 0, 2860, 806, 0, 0 }, + {2454,2454, 0, 0, 2506, 126, 0, 0 }, + {2455,2455, 0, 0, 520, 93, 0, 0 }, + {2456,2456, 0, 0, 1420, 160, 0, 0 }, + {2457,2457, 0, 0, 40000, 53, 0, 0 }, + {2458,2458, 0, 0, 9106, 100, 0, 0 }, + {2459,2459, 0, 0, 3706, 100, 0, 0 }, + {2460,2460, 0, 0, 17933, 100, 0, 0 }, + {2461,2461, 0, 0, 40000, 0, 0, 0 }, + {2462,2462, 0, 0, 40000, 66, 0, 0 }, + {2463,2463, 0, 0, 40000, 0, 0, 0 }, + { 884, 884, 0, 0, 306, 73, 0, 0 }, + { 884, 884, 28, 0, 306, 73, 0, 0 }, + {2464,2464, 29, 0, 226, 93, 0, 0 }, + { 886, 886, 31, 0, 113, 53, 0, 0 }, + { 360, 360, 32, 0, 133, 40, 0, 0 }, + { 361, 361, 33, 0, 286, 80, 0, 0 }, + {2453,2453, 34, 0, 2873, 813, 0, 0 }, + { 888, 888, 29, 0, 246, 46, 0, 0 }, + { 886, 886, 55, 0, 100, 33, 0, 0 }, + { 890, 890, 48, 0, 240, 60, 0, 0 }, + { 884, 884, 58, 0, 146, 26, 0, 0 }, + {2465,2465, 45, 0, 173, 93, 0, 0 }, + {2465,2465, 43, 0, 173, 93, 0, 0 }, + {2466,2466, 73, 0, 1633, 86, 0, 0 }, + {2467,2467, 72, 0, 866, 553, 0, 0 }, + {2468,2468, 76, 0, 1380, 620, 0, 0 }, + {2467,2467, 84, 0, 873, 560, 0, 0 }, + {2468,2468, 36, 0, 1933, 880, 0, 0 }, + {2469,2469, 65, 0, 300, 120, 0, 0 }, + {2470,2470, 83, 0, 193, 86, 0, 0 }, + {2471,2471, 50, 0, 966, 126, 0, 0 }, + {2468,2468, 77, 0, 1373, 620, 0, 0 }, + { 897, 897, 55, 0, 126, 40, 0, 0 }, + {2472,2472, 60, 0, 180, 140, 0, 0 }, + { 897, 897, 50, 0, 126, 40, 0, 0 }, + {2473,2473, 42, 0, 633, 240, 0, 0 }, + {2473,2473, 46, 0, 513, 200, 0, 0 }, + {2474,2474, 71, 0, 433, 180, 0, 0 }, + {2474,2474, 60, 0, 513, 206, 0, 0 }, + {2455,2455, 58, 0, 220, 46, 0, 0 }, + {2455,2455, 53, 0, 286, 60, 0, 0 }, + {2475,2475, 91, 0, 186, 100, 0, 0 }, + {2476,2476, 61, 0, 226, 26, 0, 0 }, + {2477,2477, 61, 0, 886, 73, 0, 0 }, + {2478,2478, 44, 0, 120, 73, 0, 0 }, + {2479,2479, 40, 0, 933, 73, 0, 0 }, + {2480,2480, 69, 0, 146, 33, 0, 0 }, + { 361, 361, 68, 0, 153, 26, 0, 0 }, + { 361, 361, 63, 0, 180, 26, 0, 0 }, + {2481,2481, 74, 0, 153, 73, 0, 0 }, + {2482,2482, 60, 0, 280, 100, 0, 0 }, + { 908, 908, 80, 0, 160, 26, 0, 0 }, + {2483,2483, 64, 0, 986, 353, 0, 0 }, + {2483,2483, 73, 0, 813, 306, 0, 0 }, + {2483,2483, 70, 0, 820, 306, 0, 0 }, + { 886, 886, 68, 0, 93, 33, 0, 0 }, + { 886, 886, 48, 0, 106, 40, 0, 0 }, + {2484,2484, 0, 0, 40000, 0, 0, 0 }, + {2485,2485, 0, 0, 3226, 753, 0, 0 }, + {2486,2486, 0, 0, 1773, 553, 0, 0 }, + {2487,2487, 0, 0, 7473, 2460, 0, 0 }, + {2488,2488, 0, 0, 40000, 0, 0, 0 }, + {2489,2489, 0, 0, 40000, 353, 0, 0 }, + {2490,2490, 0, 0, 40000, 206, 0, 0 }, + {2491,2491, 0, 0, 40000, 86, 0, 0 }, + {2492,2492, 0, 0, 4740, 86, 0, 0 }, + {2493,2493, 0, 0, 6193, 193, 0, 0 }, + {2494,2494, 0, 0, 6200, 240, 0, 0 }, + {2495,2495, 0, 0, 40000, 0, 0, 0 }, + {2496,2496, 0, 0, 1586, 73, 0, 0 }, + {2497,2497, 0, 0, 560, 73, 0, 0 }, + {2498,2498, 0, 0, 40000, 480, 0, 0 }, + {2499,2499, 0, 0, 40000, 80, 0, 0 }, + {2500,2500, 0, 0, 40000, 66, 0, 0 }, + {2501,2501, 0, 0, 40000, 380, 0, 0 }, + {2502,2502, 0, 0, 280, 100, 0, 0 }, + {2503,2503, 0, 0, 6193, 233, 0, 0 }, + {2504,2504, 0, 0, 40000, 380, 0, 0 }, + {2505,2505, 0, 0, 40000, 0, 0, 0 }, + {2506,2506, 0, 0, 40000, 380, 0, 0 }, + {2507,2507, 0, 0, 40000, 200, 0, 0 }, + {2508,2508, 0, 0, 40000, 320, 0, 0 }, + {2509,2509, 0, 0, 40000, 126, 0, 0 }, + {2510,2510, 0, 0, 40000, 293, 0, 0 }, + {2511,2511, 0, 0, 40000, 0, 0, 0 }, + {2512,2512, 0, 0, 40000, 40, 0, 0 }, + {2513,2513, 0, 0, 40000, 106, 0, 0 }, + {2514,2514, 0, 0, 3846, 73, 0, 0 }, + {2515,2515, 0, 0, 40000, 0, 0, 0 }, + {2516,2516, 0, 0, 40000, 73, 0, 0 }, + {2517,2517, 0, 0, 40000, 533, 0, 0 }, + {2518,2518, 0, 0, 40000, 1020, 0, 0 }, + {2519,2519, 0, 0, 40000, 73, 0, 0 }, + {2520,2520, 0, 0, 40000, 53, 0, 0 }, + {2521,2521, 0, 0, 6153, 1433, 0, 0 }, + {2522,2522, 0, 0, 18813, 773, 0, 0 }, + {2523,2523, 0, 0, 40000, 433, 0, 0 }, + {2524,2524, 0, 0, 40000, 0, 0, 0 }, + {2525,2525, 0, 0, 40000, 133, 0, 0 }, + {2526,2526, 0, 0, 4486, 73, 0, 0 }, + { 346, 346, 30, 0, 540, 33, 0, 0 }, + { 346, 346, 31, 0, 406, 20, 0, 0 }, + { 346, 346, 32, 0, 406, 20, 0, 0 }, + { 346, 346, 33, 0, 406, 73, 0, 0 }, + { 346, 346, 34, 0, 406, 20, 0, 0 }, + { 346, 346, 35, 0, 406, 20, 0, 0 }, + { 346, 346, 37, 0, 406, 73, 0, 0 }, + { 346, 346, 39, 0, 406, 73, 0, 0 }, + { 346, 346, 41, 0, 406, 20, 0, 0 }, + { 346, 346, 43, 0, 306, 20, 0, 0 }, + { 346, 346, 45, 0, 306, 20, 0, 0 }, + { 346, 346, 47, 0, 306, 20, 0, 0 }, + { 346, 346, 48, 0, 306, 20, 0, 0 }, + { 346, 346, 49, 0, 306, 20, 0, 0 }, + { 512, 512, 84, 0, 353, 466, 0, 0 }, + {2206,2206, 84, 0, 440, 180, 0, 0 }, + {2527,2527, 55, 0, 100, 0, 0, 0 }, + {2528,2528, 36, 0, 400, 160, 0, 0 }, + {2529,2529, 38, 0, 313, 226, 0, 0 }, + {2530,2530, 60, 0, 286, 133, 0, 0 }, + {2531,2531, 38, 0, 200, 100, 0, 0 }, + {2532,2532, 17, 0, 6186, 240, 0, 0 }, + {2532,2532, 18, 0, 6186, 240, 0, 0 }, + {2532,2532, 19, 0, 6193, 233, 0, 0 }, + {2532,2532, 20, 0, 6193, 193, 0, 0 }, + {2532,2532, 21, 0, 6193, 193, 0, 0 }, + {2532,2532, 22, 0, 6193, 193, 0, 0 }, + {2532,2532, 23, 0, 6193, 193, 0, 0 }, + {2532,2532, 24, 0, 6193, 193, 0, 0 }, + {2532,2532, 25, 0, 6193, 193, 0, 0 }, + {2532,2532, 26, 0, 6193, 193, 0, 0 }, + {2532,2532, 27, 0, 6193, 253, 0, 0 }, + {2532,2532, 28, 0, 6193, 246, 0, 0 }, + {2532,2532, 29, 0, 6193, 246, 0, 0 }, + {2533,2533, 84, 0, 433, 180, 0, 0 }, + {2534,2534, 48, 0, 280, 93, 0, 0 }, + {2535,2535, 65, 0, 1166, 360, 0, 0 }, + {2536,2536, 65, 0, 1853, 633, 0, 0 }, + {2537,2537, 55, 0, 453, 366, 0, 0 }, + {2537,2537, 41, 0, 540, 433, 0, 0 }, + { 346, 346, 63, 0, 240, 66, 0, 0 }, + { 346, 346, 55, 0, 240, 66, 0, 0 }, + {2538,2538, 55, 0, 2586, 200, 0, 0 }, + {2538,2538, 53, 0, 2586, 200, 0, 0 }, + {2534,2534, 50, 0, 280, 93, 0, 0 }, + { 506, 506, 84, 0, 693, 566, 0, 0 }, + { 506, 506, 74, 0, 693, 560, 0, 0 }, + { 504, 504, 84, 0, 1566, 546, 0, 0 }, + { 504, 504, 74, 0, 1586, 560, 0, 0 }, + {2539,2539, 84, 0, 440, 20, 0, 0 }, + {2540,2540, 74, 0, 126, 26, 0, 0 }, + {1911,1911, 48, 0, 500, 180, 0, 0 }, + {1911,1911, 36, 0, 606, 220, 0, 0 }, + {2541,2541, 74, 0, 686, 560, 0, 0 }, + {2542,2542, 0, 0, 7313, 13, 0, 0 }, + {2543,2543, 0, 0, 40000, 1306, 0, 0 }, + {2544,2544, 0, 0, 40000, 0, 0, 0 }, + {2545,2545, 0, 0, 4613, 13, 0, 0 }, + {2546,2547, 0, 4, 6926, 126, 0, 0 }, + {2548,2549, 0, 4, 40000, 86, 0, 0 }, + {2550,2550, 0, 0, 9233, 100, 0, 0 }, + {2551,2552, 0, 4, 4620, 73, 0, 0 }, + {2553,2553, 0, 0, 40000, 73, 0, 0 }, + {2554,2554, 0, 0, 40000, 0, 0, 0 }, + {2555,2556, 0, 4, 40000, 73, 0, 0 }, + {2557,2557, 0, 0, 40000, 60, 0, 0 }, + {2558,1467, 0, 4, 40000, 66, 0, 0 }, + {2559,2560, 0, 4, 40000, 40, 0, 0 }, + {2561,2561, 0, 0, 40000, 186, 0, 0 }, + {2562,2562, 0, 0, 4026, 66, 0, 0 }, + {2563,2564, 0, 4, 14513, 80, 0, 0 }, + {2565,2565, 0, 0, 40000, 0, 0, 0 }, + {2566,2567, 0, 4, 40000, 40, 0, 0 }, + {2568,2568, 0, 0, 4020, 73, 0, 0 }, + {2569,2569, 0, 0, 40000, 0, 0, 0 }, + {2570,2570, 0, 0, 40000, 0, 0, 0 }, + {2571,2572, 0, 4, 40000, 126, 0, 0 }, + {2573,2574, 0, 4, 40000, 100, 0, 0 }, + {2575,2575, 0, 0, 40000, 213, 0, 0 }, + { 229,2576, 0, 4, 40000, 166, 0, 0 }, + {2577,2577, 0, 0, 7366, 53, 0, 0 }, + { 239,2578, 0, 4, 40000, 133, 0, 0 }, + {2579,2579, 0, 0, 40000, 80, 0, 0 }, + {2580,2580, 0, 0, 40000, 140, 0, 0 }, + {2581,2582, 0, 4, 16913, 1173, 0, 0 }, + {2583,2584, 0, 4, 726, 100, 0, 0 }, + {2585,2586, 0, 4, 40000, 73, 0, 0 }, + {2587,2588, 0, 4, 40000, 73, 0, 0 }, + {2589,2589, 0, 0, 40000, 60, 0, 0 }, + {2590,2590, 0, 0, 40000, 80, 0, 0 }, + {2591,2592, 0, 4, 40000, 73, 0, 0 }, + {2593,2594, 0, 4, 40000, 60, 0, 0 }, + {2595,2595, 0, 0, 40000, 66, 0, 0 }, + {2596,2597, 0, 4, 40000, 66, 0, 0 }, + {2598,2599, 0, 4, 40000, 60, 0, 0 }, + {2600,2601, 0, 4, 40000, 173, 0, 0 }, + {2602,2602, 0, 0, 40000, 60, 0, 0 }, + {2603,2603, 0, 0, 40000, 73, 0, 0 }, + {2604,2604, 0, 0, 40000, 93, 0, 0 }, + {2605,2606, 0, 4, 40000, 73, 0, 0 }, + {2607,2607, 0, 0, 40000, 66, 0, 0 }, + {2608,2609, 0, 4, 40000, 66, 0, 0 }, + {2610,2610, 0, 0, 40000, 86, 0, 0 }, + {2611,2611, 0, 0, 40000, 60, 0, 0 }, + {2612,2612, 0, 0, 14286, 73, 0, 0 }, + {2613,2613, 0, 0, 40000, 0, 0, 0 }, + {2614,2615, 0, 4, 40000, 73, 0, 0 }, + {2616,2617, 0, 4, 40000, 66, 0, 0 }, + {2618,2619, 0, 4, 133, 26, 0, 0 }, + {2620,2621, 0, 4, 40000, 1280, 0, 0 }, + {2622,2623, 0, 4, 40000, 160, 0, 0 }, + {2624,2625, 0, 4, 40000, 0, 0, 0 }, + {2626,2627, 0, 4, 40000, 73, 0, 0 }, + {2628,2629, 0, 4, 40000, 0, 0, 0 }, + {1516,2630, 0, 4, 1186, 406, 0, 0 }, + {2631,2632, 0, 4, 40000, 553, 0, 0 }, + {2633,2633, 0, 0, 40000, 40, 0, 0 }, + {2634,2635, 0, 4, 40000, 773, 0, 0 }, + {2636,2636, 0, 0, 40000, 320, 0, 0 }, + {2637,2637, 0, 0, 1880, 73, 0, 0 }, + {2638,2639, 0, 4, 473, 186, 0, 0 }, + {2640,2641, 0, 4, 16946, 1193, 0, 0 }, + {2642,2642, 0, 0, 40000, 720, 0, 0 }, + {2643,2644, 0, 4, 1880, 40, 0, 0 }, + {2645,2645, 0, 0, 40000, 73, 0, 0 }, + {2646,2647, 0, 4, 40000, 46, 0, 0 }, + {2648,2648, 0, 0, 2466, 80, 0, 0 }, + {2649,2649, 0, 0, 40000, 193, 0, 0 }, + {2650,2651, 0, 4, 993, 73, 0, 0 }, + {2652,2652, 0, 0, 40000, 220, 0, 0 }, + {2653,2654, 0, 4, 40000, 46, 0, 0 }, + {2655,2656, 0, 4, 40000, 46, 0, 0 }, + {2657,2657, 0, 0, 40000, 66, 0, 0 }, + {2658,2658, 35, 0, 626, 20, 0, 0 }, + {2659,2659, 35, 0, 306, 26, 0, 0 }, + {2660,2660, 52, 0, 126, 26, 0, 0 }, + {2661,2661, 60, 0, 286, 20, 0, 0 }, + {2662,2662, 58, 0, 113, 26, 0, 0 }, + {2663,2663, 60, 0, 380, 20, 0, 0 }, + {2664,2664, 50, 0, 1640, 66, 0, 0 }, + {2665,2665, 43, 0, 153, 20, 0, 0 }, + {2664,2664, 55, 0, 1640, 20, 0, 0 }, + {1553,1553, 43, 0, 160, 80, 0, 0 }, + {2666,2666, 50, 0, 980, 20, 0, 0 }, + {2667,2667, 43, 0, 446, 73, 0, 0 }, + {2666,2666, 53, 0, 1000, 80, 0, 0 }, + {2666,2666, 57, 0, 700, 73, 0, 0 }, + {2668,2668, 72, 0, 773, 13, 0, 0 }, + {2666,2666, 60, 0, 686, 20, 0, 0 }, + { 373, 373, 76, 0, 826, 20, 0, 0 }, + {2669,2669, 84, 0, 713, 20, 0, 0 }, + {2670,2670, 42, 0, 1186, 20, 0, 0 }, + {2671,2671, 65, 0, 293, 33, 0, 0 }, + {2672,2672, 84, 0, 386, 33, 0, 0 }, + {2673,2673, 84, 0, 1366, 20, 0, 0 }, + {2674,2674, 24, 0, 960, 73, 0, 0 }, + { 383, 383, 77, 0, 800, 20, 0, 0 }, + {2675,2675, 58, 0, 426, 26, 0, 0 }, + {2676,2676, 53, 0, 426, 20, 0, 0 }, + {2677,2677, 64, 0, 200, 66, 0, 0 }, + {2678,2678, 71, 0, 113, 13, 0, 0 }, + {2679,2679, 44, 0, 766, 66, 0, 0 }, + {2680,2680, 40, 0, 460, 60, 0, 0 }, + {2681,2681, 69, 0, 126, 26, 0, 0 }, + {2682,2682, 60, 0, 573, 66, 0, 0 }, + {2683,2683, 80, 0, 226, 20, 0, 0 }, + {2684,2684, 64, 0, 2693, 20, 0, 0 }, + {2685,2685, 72, 0, 120, 66, 0, 0 }, + {2686,2686, 70, 0, 820, 20, 0, 0 }, + {2687,2687, 48, 0, 173, 20, 0, 0 }, + {2688,2688, 53, 0, 980, 33, 0, 0 }, + {2689,2690, 0, 4, 40000, 286, 0, 0 }, + {2691,2692, 0, 4, 2326, 100, 0, 0 }, + {2693,2694, 0, 4, 380, 80, 0, 0 }, + {2695,2696, 0, 4, 14766, 73, 0, 0 }, + {2697,2698, 0, 4, 40000, 40, 0, 0 }, + { 192,2699, 0, 4, 40000, 73, 0, 0 }, + {2700,2701, 0, 4, 973, 126, 0, 0 }, + {2702,2703, 0, 4, 4626, 106, 0, 0 }, + {2704,2705, 0, 4, 40000, 73, 0, 0 }, + {2706,2707, 0, 4, 40000, 73, 0, 0 }, + {2708,2709, 0, 4, 40000, 73, 0, 0 }, + {2710,2711, 0, 4, 2053, 93, 0, 0 }, + {2712,1473, 0, 4, 320, 26, 0, 0 }, + {2713,2714, 0, 4, 573, 93, 0, 0 }, + {2715,2716, 0, 4, 6466, 353, 0, 0 }, + {1478,2717, 0, 4, 40000, 146, 0, 0 }, + {2718,2719, 0, 4, 40000, 66, 0, 0 }, + { 286,2720, 0, 4, 40000, 73, 0, 0 }, + {2721,2722, 0, 4, 40000, 86, 0, 0 }, + {2723,2724, 0, 4, 40000, 60, 0, 0 }, + {2725,2726, 0, 4, 393, 73, 0, 0 }, + {2727,2724, 0, 4, 40000, 60, 0, 0 }, + {1514,2728, 0, 4, 40000, 180, 0, 0 }, + {2729,2730, 0, 4, 40000, 0, 0, 0 }, + {2731,2732, 0, 4, 473, 186, 0, 0 }, + {2733,2734, 0, 4, 733, 33, 0, 0 }, + {2735,2736, 0, 4, 286, 40, 0, 0 }, + {2737,2738, 0, 4, 40000, 73, 0, 0 }, + {2739,2740, 0, 4, 1313, 746, 0, 0 }, + {2741,2742, 0, 4, 1326, 700, 0, 0 }, + {2743,2744, 0, 4, 40000, 0, 0, 0 }, + {2745,2746, 0, 4, 2046, 73, 0, 0 }, + {2747,2747, 35, 0, 386, 166, 0, 0 }, + {2748,2748, 60, 0, 493, 193, 0, 0 }, + {2749,2749, 43, 0, 126, 66, 0, 0 }, + {2750,2750, 0, 0, 3740, 1260, 0, 0 }, + {2751,2752, 0, 4, 14833, 360, 0, 0 }, + {2753,2754, 0, 4, 10206, 273, 0, 0 }, + {2755,2756, 0, 4, 18033, 146, 0, 0 }, + {2757,2758, 0, 4, 14433, 326, 0, 0 }, + {2759,2760, 0, 4, 14580, 626, 0, 0 }, + {2761,2762, 0, 4, 14720, 300, 0, 0 }, + {2763,2764, 0, 4, 10426, 106, 0, 0 }, + {2765,2766, 0, 4, 40000, 60, 0, 0 }, + {2767,2768, 0, 4, 40000, 80, 0, 0 }, + {2769,2770, 0, 4, 40000, 80, 0, 0 }, + {2771,2772, 0, 4, 40000, 73, 0, 0 }, + {2773,2774, 0, 4, 40000, 73, 0, 0 }, + {2775,2776, 0, 4, 40000, 80, 0, 0 }, + {2777,2778, 0, 4, 40000, 73, 0, 0 }, + {2779,2780, 0, 4, 40000, 73, 0, 0 }, + {2781,2782, 0, 4, 40000, 66, 0, 0 }, + {2783,2784, 0, 4, 7220, 186, 0, 0 }, + {2785,2786, 0, 4, 10300, 113, 0, 0 }, + {2787,2788, 0, 4, 40000, 246, 0, 0 }, + {2789,2790, 0, 4, 9106, 746, 0, 0 }, + {2791,2792, 0, 4, 7373, 666, 0, 0 }, + {2793,2794, 0, 4, 1200, 426, 0, 0 }, + {2795,2796, 0, 4, 40000, 413, 0, 0 }, + {2795,2797, 0, 4, 40000, 1506, 0, 0 }, + {2798,2799, 0, 4, 40000, 60, 0, 0 }, + {2800,2801, 0, 4, 40000, 233, 0, 0 }, + {2802,2803, 0, 4, 40000, 80, 0, 0 }, + {2804,2805, 0, 4, 40000, 80, 0, 0 }, + {2806,2807, 0, 4, 4500, 80, 0, 0 }, + {2808,2809, 0, 4, 40000, 73, 0, 0 }, + {2810,2811, 0, 4, 1180, 100, 0, 0 }, + {2812,2813, 0, 4, 953, 153, 0, 0 }, + {2814,2815, 0, 4, 14693, 126, 0, 0 }, + {2816,2817, 0, 4, 14693, 193, 0, 0 }, + {2818,2819, 0, 4, 14473, 633, 0, 0 }, + {2820,2821, 0, 4, 2200, 73, 0, 0 }, + {2822,2823, 0, 4, 366, 86, 0, 0 }, + {2824,2825, 0, 4, 12780, 200, 0, 0 }, + {2826,2827, 0, 4, 40000, 73, 0, 0 }, + {2828,2829, 0, 4, 9066, 146, 0, 0 }, + {2830,2831, 0, 4, 2526, 326, 0, 0 }, + {2832,2833, 0, 4, 6933, 200, 0, 0 }, + {2834,2835, 0, 4, 40000, 413, 0, 0 }, + {2836,2837, 0, 4, 4806, 1313, 0, 0 }, + {2838,2839, 0, 4, 14620, 340, 0, 0 }, + {2840,2841, 0, 4, 1866, 653, 0, 0 }, + {2842,2843, 0, 4, 5200, 260, 0, 0 }, + {2844,2845, 0, 4, 40000, 240, 0, 0 }, + {2846,2847, 0, 4, 40000, 240, 0, 0 }, + {2848,2849, 0, 4, 40000, 240, 0, 0 }, + {2850,2851, 0, 4, 40000, 406, 0, 0 }, + {2852,2853, 0, 4, 40000, 406, 0, 0 }, + {2854,2855, 0, 4, 40000, 146, 0, 0 }, + {2856,2856, 0, 0, 2400, 1126, 0, 0 }, + {2857,2857, 0, 0, 2400, 1126, 0, 0 }, + {2858,2859, 0, 4, 4586, 73, 0, 0 }, + {2860,2861, 0, 4, 40000, 426, 0, 0 }, + {2862,2863, 0, 4, 4553, 100, 0, 0 }, + {2864,2865, 0, 4, 40000, 80, 0, 0 }, + {2866,2867, 0, 4, 5260, 53, 0, 0 }, + {2868,2869, 0, 4, 5286, 113, 0, 0 }, + {2870,2871, 0, 4, 7040, 186, 0, 0 }, + {2872,2873, 0, 4, 4693, 106, 0, 0 }, + {2874,2875, 0, 4, 40000, 73, 0, 0 }, + {2876,2877, 0, 4, 1633, 146, 0, 0 }, + {2878,2879, 0, 4, 7266, 186, 0, 0 }, + {2880,2881, 0, 4, 7340, 1246, 0, 0 }, + {2882,2883, 0, 4, 4600, 93, 0, 0 }, + {2884,2885, 0, 4, 3446, 926, 0, 0 }, + {2886,2887, 0, 4, 40000, 73, 0, 0 }, + {2888,2888, 0, 0, 18926, 426, 0, 0 }, + {2889,2889, 0, 0, 18520, 73, 0, 0 }, + {2890,2890, 0, 0, 18473, 73, 0, 0 }, + {2891,2892, 0, 4, 40000, 93, 0, 0 }, + {2893,2893, 0, 0, 8006, 133, 0, 0 }, + {2894,2894, 0, 0, 18533, 66, 0, 0 }, + {2895,2895, 0, 0, 14786, 4966, 0, 0 }, + {2896,2897, 0, 4, 40000, 80, 0, 0 }, + {2898,2899, 0, 4, 40000, 73, 0, 0 }, + {2353,2900, 0, 4, 18420, 80, 0, 0 }, + {2901,2901, 0, 0, 40000, 0, 0, 0 }, + {2902,2903, 0, 4, 40000, 100, 0, 0 }, + {2904,2905, 0, 4, 40000, 93, 0, 0 }, + {2906,2907, 0, 4, 40000, 73, 0, 0 }, + {2908,2909, 0, 4, 10706, 160, 0, 0 }, + {2910,2911, 0, 4, 40000, 73, 0, 0 }, + {2912,2912, 0, 0, 40000, 40, 0, 0 }, + {2913,2914, 0, 4, 8713, 446, 0, 0 }, + {2915,2916, 0, 4, 14633, 653, 0, 0 }, + {2917,2918, 0, 4, 9166, 426, 0, 0 }, + {2919,2920, 0, 4, 9213, 240, 0, 0 }, + {2921,2922, 0, 4, 8700, 420, 0, 0 }, + {2923,2924, 0, 4, 2200, 346, 0, 0 }, + {2925,2926, 0, 4, 2360, 426, 0, 0 }, + {2927,2928, 0, 4, 2340, 233, 0, 0 }, + {2929,2929, 0, 0, 40000, 140, 0, 0 }, + {2930,2931, 0, 4, 40000, 100, 0, 0 }, + {2932,2933, 0, 4, 40000, 73, 0, 0 }, + {2934,2935, 0, 4, 40000, 80, 0, 0 }, + {2936,2937, 0, 4, 40000, 80, 0, 0 }, + {2938,2939, 0, 4, 40000, 246, 0, 0 }, + {2940,2940, 0, 0, 553, 446, 0, 0 }, + {2941,2941, 0, 0, 40000, 193, 0, 0 }, + {2942,2943, 0, 4, 1200, 406, 0, 0 }, + {2944,2944, 0, 0, 7026, 1553, 0, 0 }, + {2945,2945, 0, 0, 3426, 360, 0, 0 }, + {2946,2947, 0, 4, 7200, 646, 0, 0 }, + {2948,2948, 0, 0, 40000, 386, 0, 0 }, + {2949,2949, 0, 0, 1953, 726, 0, 0 }, + {2950,2951, 0, 4, 14606, 106, 0, 0 }, + {2952,2953, 0, 4, 40000, 1566, 0, 0 }, + {2954,2954, 60, 2, 6, 0, 0, 0 }, + {2955,2956, 0, 4, 40000, 240, 0, 0 }, + {2957,2958, 0, 4, 40000, 80, 0, 0 }, + {2959,2960, 0, 4, 40000, 113, 0, 0 }, + {2961,2962, 0, 4, 40000, 240, 0, 0 }, + {2963,2963, 0, 0, 8506, 680, 0, 0 }, + {2964,2964, 0, 0, 40000, 1593, 0, 0 }, + {2436,2436, 49, 0, 1873, 633, 0, 0 }, + {2357,2357, 61, 0, 113, 20, 0, 0 }, + {2357,2357, 56, 0, 113, 26, 0, 0 }, + {2357,2357, 58, 0, 113, 26, 0, 0 }, + {2357,2357, 49, 0, 126, 26, 0, 0 }, + {2357,2357, 44, 0, 126, 26, 0, 0 }, + {2965,2965, 0, 0, 40000, 380, 0, 0 }, + {2966,2966, 0, 0, 4440, 66, 0, 0 }, + {2967,2967, 0, 0, 8133, 1433, 0, 0 }, + {2968,2968, 0, 0, 40000, 126, 0, 0 }, + {2969,2969, 0, 0, 40000, 0, 0, 0 }, + {2970,2970, 84, 0, 160, 26, 0, 0 }, + {2971,2971, 72, 0, 440, 180, 0, 0 }, + {2972,2972, 0, 0, 8313, 580, 0, 0 }, + {2973,2973, 0, 0, 40000, 160, 0, 0 }, + {2974,2974, 0, 0, 40000, 3000, 0, 0 }, + {2975,2975, 0, 0, 8300, 493, 0, 0 }, + {2976,2976, 0, 0, 973, 673, 0, 0 }, + {2977,2977, 0, 0, 40000, 73, 0, 0 }, + {2978,2978, 0, 0, 40000, 133, 0, 0 }, + {2979,2979, 0, 0, 40000, 140, 0, 0 }, + {2980,2980, 0, 0, 40000, 346, 0, 0 }, + {2981,2981, 0, 0, 40000, 1006, 0, 0 }, + {2982,2982, 0, 0, 40000, 966, 0, 0 }, + {2983,2983, 0, 0, 40000, 0, 0, 0 }, + {2984,2984, 0, 0, 40000, 0, 0, 0 }, + {2985,2985, 0, 0, 40000, 66, 0, 0 }, + {2986,2986, 0, 0, 40000, 66, 0, 0 }, + {2987,2987, 0, 0, 40000, 46, 0, 0 }, + {2988,2988, 0, 0, 40000, 533, 0, 0 }, + {2989,2989, 0, 0, 2400, 780, 0, 0 }, + {2990,2990, 0, 0, 820, 66, 0, 0 }, + {2991,2991, 0, 0, 40000, 240, 0, 0 }, + {2992,2992, 0, 0, 40000, 220, 0, 0 }, + {2993,2993, 0, 0, 40000, 0, 0, 0 }, + {2994,2994, 0, 0, 15100, 73, 0, 0 }, + {2995,2995, 0, 0, 40000, 200, 0, 0 }, + {2996,2996, 0, 0, 2426, 93, 0, 0 }, + {2997,2997, 0, 0, 4640, 1553, 0, 0 }, + {2998,2998, 0, 0, 40000, 73, 0, 0 }, + {2999,2999, 0, 0, 40000, 73, 0, 0 }, + {3000,3000, 0, 0, 1133, 633, 0, 0 }, + {3001,3001, 0, 0, 40000, 0, 0, 0 }, + {3002,3002, 0, 0, 40000, 1006, 0, 0 }, + {3003,3003, 0, 0, 4653, 653, 0, 0 }, + {3004,3004, 0, 0, 40000, 1000, 0, 0 }, + {3005,3005, 0, 0, 40000, 53, 0, 0 }, + {3006,3006, 0, 0, 40000, 60, 0, 0 }, + {3007,3007, 0, 0, 40000, 0, 0, 0 }, + { 350, 350, 0, 0, 513, 200, 0, 0 }, + {3008,3008, 0, 0, 213, 106, 0, 0 }, + {3009,3009, 0, 0, 280, 126, 0, 0 }, + {3010,3010, 0, 0, 1193, 426, 0, 0 }, + {3011,3011, 0, 0, 14653, 4906, 0, 0 }, + {3012,3012, 0, 0, 1040, 326, 0, 0 }, + {3013,3013, 0, 0, 5740, 2326, 0, 0 }, + {3014,3014, 0, 0, 40000, 73, 0, 0 }, + {3015,3015, 0, 0, 40000, 240, 0, 0 }, + { 350, 350, 36, 0, 380, 153, 0, 0 }, + { 369, 369, 37, 0, 213, 66, 0, 0 }, + {3008,3008, 38, 0, 213, 106, 0, 0 }, + { 369, 369, 24, 0, 193, 13, 0, 0 }, + {3008,3008, 32, 0, 206, 106, 0, 0 }, + { 369, 369, 48, 0, 186, 20, 0, 0 }, + {3009,3009, 42, 0, 220, 106, 0, 0 }, + { 369, 369, 50, 0, 186, 73, 0, 0 }, + { 369, 369, 52, 0, 186, 73, 0, 0 }, + { 369, 369, 54, 0, 186, 33, 0, 0 }, + { 369, 369, 55, 0, 186, 33, 0, 0 }, + { 369, 369, 57, 0, 180, 33, 0, 0 }, + {3010,3010, 51, 0, 966, 353, 0, 0 }, + { 144, 144, 61, 0, 213, 126, 0, 0 }, + {3016,3016, 0, 0, 8340, 520, 0, 0 }, + {3016,3016, 63, 0, 6106, 373, 0, 0 }, + {3016,3016, 64, 0, 6073, 380, 0, 0 }, + {3017,3017, 40, 0, 206, 100, 0, 0 }, + {3017,3017, 70, 0, 160, 93, 0, 0 }, + {3018,3018, 0, 0, 40000, 73, 0, 0 }, + {3019,3019, 0, 0, 40000, 73, 0, 0 }, + {3020,3020, 0, 0, 40000, 73, 0, 0 }, + {3021,3021, 0, 0, 40000, 73, 0, 0 }, + {3022,3022, 38, 0, 246, 33, 0, 0 }, + {2441,2441, 57, 0, 286, 126, 0, 0 }, + {3023,3023, 63, 0, 146, 126, 0, 0 }, + {3024,3024, 74, 0, 280, 73, 0, 0 }, + {3025,3025, 74, 0, 453, 100, 0, 0 }, + {3026,3026, 60, 0, 666, 33, 0, 0 }, + {1593,1594, 35, 4, 1220, 413, 0, 0 }, + {1564,1565, 35, 4, 700, 260, 0, 0 }, + { 248,3027, 0, 4, 40000, 126, 0, 0 }, + {1445,3028, 0, 4, 9920, 326, 0, 0 }, + {1447,3029, 0, 4, 10133, 26, 0, 0 }, + {1452,3030, 0, 4, 9213, 240, 0, 0 }, + {1544,3031, 0, 4, 293, 86, 0, 0 }, + {1546,3032, 0, 4, 40000, 153, 0, 0 }, + { 398, 399, 35, 4, 1906, 226, 0, 0 }, + {1550,3033, 35, 4, 66, 26, 0, 0 }, + {1556,1557, 35, 4, 1126, 426, 0, 0 }, + {1558,1559, 35, 4, 1126, 413, 0, 0 }, + {1570,1571, 35, 4, 493, 100, 0, 0 }, + {1608,1609, 35, 4, 2286, 413, 0, 0 }, + {1595,1596, 35, 4, 220, 260, 0, 0 }, + { 159,1597, 35, 4, 220, 273, 0, 0 }, + {1610,1611, 35, 4, 220, 260, 0, 0 }, + { 397,1588, 35, 4, 253, 86, 0, 0 }, + {1606,1607, 35, 4, 140, 60, 0, 0 }, + { 145,1576, 35, 4, 160, 120, 0, 0 }, + {1612,1613, 35, 4, 413, 440, 0, 0 }, + {1577,1578, 35, 4, 373, 393, 0, 0 }, + {1614,1615, 35, 4, 706, 760, 0, 0 }, + {1550,1551, 35, 4, 620, 240, 0, 0 }, + { 364, 365, 35, 4, 146, 26, 0, 0 }, + { 129,1549, 35, 4, 200, 100, 0, 0 }, + { 132,1552, 35, 4, 213, 106, 0, 0 }, + {1553,1554, 35, 4, 206, 73, 0, 0 }, + { 129,1548, 35, 4, 200, 100, 0, 0 }, + { 134,1555, 35, 4, 2233, 680, 0, 0 }, + {1560,1561, 35, 4, 353, 146, 0, 0 }, + {1562,1563, 35, 4, 2013, 426, 0, 0 }, + {1572,1573, 35, 4, 346, 153, 0, 0 }, + {1574,1575, 35, 4, 360, 246, 0, 0 }, + {1581,1582, 35, 4, 620, 246, 0, 0 }, + { 149,1583, 35, 4, 326, 33, 0, 0 }, + {1584,1585, 35, 4, 200, 100, 0, 0 }, + {1591,1592, 35, 4, 1206, 413, 0, 0 }, + {1579,1580, 35, 4, 273, 233, 0, 0 }, + {1586,1587, 35, 4, 200, 100, 0, 0 }, + {1589,1590, 35, 4, 120, 86, 0, 0 }, + {1600,1601, 35, 4, 1246, 373, 0, 0 }, + {1602,1603, 35, 4, 700, 273, 0, 0 }, + {1604,1605, 35, 4, 2660, 926, 0, 0 }, + {1598,1599, 35, 4, 1300, 400, 0, 0 }, + { 374, 375, 35, 4, 1186, 426, 0, 0 }, + {1566,1567, 35, 4, 2333, 813, 0, 0 }, + {2306,2307, 35, 4, 2360, 906, 0, 0 }, + {3034, 339, 35, 6, 6, 0, 0, 0 }, + {2305,1548, 35, 4, 200, 100, 0, 0 }, + {1595,1595, 35, 0, 220, 273, 0, 0 }, + {2303,2304, 35, 4, 146, 26, 0, 0 }, + {1560,2308, 35, 4, 353, 233, 0, 0 }, + {2309,2310, 35, 4, 2013, 433, 0, 0 }, + {1568,1568, 35, 0, 1280, 453, 0, 0 }, + {2311,2312, 35, 4, 346, 146, 0, 0 }, + {2313,2314, 35, 4, 633, 240, 0, 0 }, + {2315,2315, 35, 0, 106, 46, 0, 0 }, + {2316,2316, 35, 0, 506, 453, 0, 0 }, + {1612,1612, 35, 0, 526, 400, 0, 0 }, + {2317,2317, 35, 0, 640, 253, 0, 0 }, + {2318,2318, 35, 0, 326, 20, 0, 0 }, + {2319,2319, 35, 0, 453, 446, 0, 0 }, + {2320,2320, 35, 0, 466, 453, 0, 0 }, + {2321,2321, 35, 0, 120, 26, 0, 0 }, + {2322,2322, 35, 0, 1220, 406, 0, 0 }, + {2323,2323, 35, 0, 2360, 786, 0, 0 }, + {2324, 392, 35, 4, 180, 46, 0, 0 }, + {2324, 393, 35, 4, 460, 66, 0, 0 }, + {2325,2326, 35, 4, 533, 133, 0, 0 }, + {2327,2328, 35, 4, 1273, 406, 0, 0 }, + {2329,2330, 35, 4, 613, 773, 0, 0 }, + {2331,2332, 35, 4, 2340, 780, 0, 0 }, + {3035,3036, 35, 4, 1193, 433, 0, 0 }, + {3037,3038, 35, 4, 1840, 340, 0, 0 }, + {1564,1564, 35, 0, 713, 273, 0, 0 }, + {3039,3039, 0, 0, 40000, 0, 0, 0 }, + {3040,3040, 0, 0, 6100, 146, 0, 0 }, + {3041,3041, 0, 0, 2386, 26, 0, 0 }, + {3042,3042, 0, 0, 4320, 80, 0, 0 }, + {3043,3043, 0, 0, 3433, 313, 0, 0 }, + {3044,3044, 0, 0, 6620, 2446, 0, 0 }, + {3045,3045, 0, 0, 3726, 1253, 0, 0 }, + {3046,3046, 0, 0, 40000, 133, 0, 0 }, + {3047,3047, 0, 0, 4566, 1253, 0, 0 }, + {3048,3048, 0, 0, 40000, 813, 0, 0 }, + {3049,3049, 0, 0, 18513, 1560, 0, 0 }, + {3050,3050, 0, 0, 2186, 426, 0, 0 }, + {3051,3051, 0, 0, 1186, 420, 0, 0 }, + {3052,3052, 0, 0, 766, 420, 0, 0 }, + {3053,3053, 0, 0, 14513, 4713, 0, 0 }, + {3054,3054, 0, 0, 15493, 1580, 0, 0 }, + {3055,3055, 0, 0, 40000, 66, 0, 0 }, + {3056,3056, 0, 0, 40000, 60, 0, 0 }, + {3057,3057, 0, 0, 4740, 100, 0, 0 }, + {3058,3058, 0, 0, 40000, 66, 0, 0 }, + {3059,3059, 0, 0, 40000, 73, 0, 0 }, + {3060,3060, 0, 0, 40000, 73, 0, 0 }, + {3061,3061, 0, 0, 40000, 0, 0, 0 }, + {3062,3062, 0, 0, 8373, 633, 0, 0 }, + {3063,3063, 0, 0, 7560, 133, 0, 0 }, + {3064,3064, 0, 0, 40000, 0, 0, 0 }, + {3065,3065, 0, 0, 40000, 86, 0, 0 }, + {3066,3066, 0, 0, 340, 140, 0, 0 }, + {3067,3067, 0, 0, 40000, 0, 0, 0 }, + {3068,3068, 0, 0, 40000, 166, 0, 0 }, + {3069,3069, 0, 0, 4280, 1466, 0, 0 }, + {3070,3070, 0, 0, 2193, 73, 0, 0 }, + {3071,3071, 0, 0, 4846, 100, 0, 0 }, + {3072,3072, 0, 0, 12740, 93, 0, 0 }, + {3073,3073, 0, 0, 6953, 200, 0, 0 }, + {3074,3074, 0, 0, 13780, 73, 0, 0 }, + {3075,3075, 0, 0, 40000, 73, 0, 0 }, + {3076,3076, 0, 0, 5860, 600, 0, 0 }, + {3077,3077, 0, 0, 2206, 73, 0, 0 }, + {3078,3078, 0, 0, 40000, 140, 0, 0 }, + {3079,3079, 0, 0, 40000, 53, 0, 0 }, + {3080,3080, 0, 0, 40000, 120, 0, 0 }, + {3081,3081, 0, 0, 40000, 140, 0, 0 }, + {3082,3082, 0, 0, 40000, 126, 0, 0 }, + {3083,3083, 0, 0, 360, 140, 0, 0 }, + {3084,3084, 0, 0, 8880, 1373, 0, 0 }, + {3085,3085, 0, 0, 593, 73, 0, 0 }, + {3086,3086, 0, 0, 40000, 193, 0, 0 }, + {3087,3087, 0, 0, 40000, 200, 0, 0 }, + {3088,3088, 0, 0, 40000, 160, 0, 0 }, + {3089,3089, 0, 0, 40000, 200, 0, 0 }, + {3090,3090, 0, 0, 40000, 53, 0, 0 }, + {3091,3091, 0, 0, 40000, 73, 0, 0 }, + {3092,3092, 0, 0, 40000, 73, 0, 0 }, + {3093,3093, 0, 0, 760, 213, 0, 0 }, + {3094,3094, 0, 0, 40000, 133, 0, 0 }, + {3095,3095, 0, 0, 40000, 220, 0, 0 }, + {3096,3096, 0, 0, 40000, 100, 0, 0 }, + {3097,3097, 0, 0, 40000, 73, 0, 0 }, + {3098,3098, 0, 0, 40000, 140, 0, 0 }, + {3099,3099, 0, 0, 40000, 140, 0, 0 }, + {3100,3100, 0, 0, 40000, 140, 0, 0 }, + {3101,3101, 0, 0, 40000, 73, 0, 0 }, + {3102,3102, 0, 0, 40000, 73, 0, 0 }, + {3103,3103, 0, 0, 40000, 73, 0, 0 }, + {3104,3104, 0, 0, 40000, 73, 0, 0 }, + {3105,3105, 0, 0, 40000, 66, 0, 0 }, + {3106,3106, 0, 0, 40000, 66, 0, 0 }, + {3107,3107, 0, 0, 40000, 73, 0, 0 }, + {3108,3108, 0, 0, 40000, 73, 0, 0 }, + {3109,3109, 0, 0, 40000, 73, 0, 0 }, + {3110,3110, 0, 0, 40000, 73, 0, 0 }, + {3111,3111, 0, 0, 40000, 86, 0, 0 }, + {3112,3112, 0, 0, 5393, 100, 0, 0 }, + {3113,3113, 0, 0, 40000, 60, 0, 0 }, + {3114,3114, 0, 0, 18500, 73, 0, 0 }, + {3115,3115, 0, 0, 40000, 93, 0, 0 }, + {3116,3116, 0, 0, 40000, 86, 0, 0 }, + {3117,3117, 0, 0, 40000, 173, 0, 0 }, + {3118,3118, 0, 0, 40000, 1353, 0, 0 }, + {3119,3119, 0, 0, 17506, 73, 0, 0 }, + {3120,3120, 0, 0, 40000, 100, 0, 0 }, + {3121,3121, 0, 0, 40000, 73, 0, 0 }, + {3122,3122, 0, 0, 5620, 193, 0, 0 }, + {3123,3123, 0, 0, 3700, 80, 0, 0 }, + {3124,3124, 0, 0, 40000, 66, 0, 0 }, + {3125,3125, 0, 0, 2740, 80, 0, 0 }, + {3126,3126, 0, 0, 8333, 173, 0, 0 }, + {3127,3127, 0, 0, 2226, 466, 0, 0 }, + {3128,3128, 0, 0, 340, 146, 0, 0 }, + {3129,3129, 0, 0, 19980, 6280, 0, 0 }, + {3130,3130, 0, 0, 353, 73, 0, 0 }, + {3131,3131, 35, 0, 566, 233, 0, 0 }, + {3132,3132, 35, 0, 226, 46, 0, 0 }, + {3133,3133, 35, 0, 40000, 100, 0, 0 }, + {3134,3134, 35, 0, 40000, 100, 0, 0 }, + {3135,3135, 35, 0, 360, 146, 0, 0 }, + {3061,3061, 35, 0, 40000, 0, 0, 0 }, + {3136,3136, 35, 0, 366, 20, 0, 0 }, + { 739, 739, 35, 0, 246, 20, 0, 0 }, + {3137,3137, 35, 0, 333, 33, 0, 0 }, + {3138,3138, 35, 0, 420, 166, 0, 0 }, + {3139,3139, 35, 0, 626, 240, 0, 0 }, + {3140,3140, 35, 0, 233, 100, 0, 0 }, + {3141,3141, 35, 0, 1166, 440, 0, 0 }, + {3142,3142, 35, 0, 166, 66, 0, 0 }, + {3143,3143, 35, 0, 1166, 440, 0, 0 }, + {3144,3144, 35, 0, 813, 100, 0, 0 }, + {3145,3145, 35, 0, 1040, 440, 0, 0 }, + {3146,3146, 35, 0, 40000, 0, 0, 0 }, + {3147,3147, 35, 0, 40000, 0, 0, 0 }, + {3148,3148, 35, 0, 180, 40, 0, 0 }, + {3149,3149, 35, 0, 40000, 0, 0, 0 }, + {3150,3150, 0, 0, 40000, 0, 0, 0 }, + {3151,3151, 0, 0, 4900, 240, 0, 0 }, + {3152,3152, 0, 0, 3480, 80, 0, 0 }, + {3153,3153, 0, 0, 3586, 86, 0, 0 }, + {3154,3154, 0, 0, 4626, 633, 0, 0 }, + {3155,3155, 0, 0, 4293, 2286, 0, 0 }, + {3156,3156, 0, 0, 13653, 4720, 0, 0 }, + {3157,3157, 0, 0, 1206, 426, 0, 0 }, + {3158,3158, 0, 0, 653, 426, 0, 0 }, + {3159,3159, 0, 0, 40000, 0, 0, 0 }, + {3160,3160, 0, 0, 4633, 633, 0, 0 }, + {3161,3161, 0, 0, 40000, 73, 0, 0 }, + {3162,3162, 0, 0, 40000, 60, 0, 0 }, + {3163,3163, 0, 0, 40000, 146, 0, 0 }, + {3164,3164, 0, 0, 40000, 73, 0, 0 }, + {3165,3165, 0, 0, 40000, 73, 0, 0 }, + {3166,3166, 0, 0, 40000, 0, 0, 0 }, + {3167,3167, 0, 0, 40000, 66, 0, 0 }, + {3168,3168, 0, 0, 3680, 1180, 0, 0 }, + {3169,3169, 0, 0, 2406, 846, 0, 0 }, + {3170,3170, 0, 0, 1560, 73, 0, 0 }, + {3171,3171, 0, 0, 1946, 226, 0, 0 }, + {3172,3172, 0, 0, 4333, 13, 0, 0 }, + {3173,3173, 0, 0, 40000, 0, 0, 0 }, + {3174,3174, 0, 0, 40000, 0, 0, 0 }, + {3175,3175, 0, 0, 40000, 66, 0, 0 }, + {3176,3176, 0, 0, 40000, 180, 0, 0 }, + {3177,3177, 0, 0, 15380, 80, 0, 0 }, + {3178,3178, 0, 0, 18213, 73, 0, 0 }, + {3179,3179, 0, 0, 1706, 0, 0, 0 }, + {3180,3180, 0, 0, 5733, 1266, 0, 0 }, + {3181,3181, 0, 0, 40000, 0, 0, 0 }, + {3182,3182, 0, 0, 40000, 366, 0, 0 }, + {3183,3183, 0, 0, 40000, 66, 0, 0 }, + {3184,3184, 0, 0, 4786, 73, 0, 0 }, + {3185,3185, 0, 0, 5660, 720, 0, 0 }, + {3186,3186, 0, 0, 1293, 406, 0, 0 }, + {3187,3187, 0, 0, 40000, 0, 0, 0 }, + {3188,3188, 0, 0, 2686, 233, 0, 0 }, + {3189,3189, 0, 0, 40000, 0, 0, 0 }, + {3190,3190, 0, 0, 40000, 73, 0, 0 }, + {3191,3191, 0, 0, 40000, 0, 0, 0 }, + {3192,3192, 0, 0, 40000, 73, 0, 0 }, + {3193,3193, 0, 0, 40000, 0, 0, 0 }, + {3194,3194, 0, 0, 3920, 73, 0, 0 }, + {3195,3195, 0, 0, 40000, 73, 0, 0 }, + {3196,3196, 0, 0, 40000, 66, 0, 0 }, + {3197,3197, 0, 0, 40000, 80, 0, 0 }, + {3198,3198, 0, 0, 40000, 86, 0, 0 }, + {3199,3199, 0, 0, 40000, 60, 0, 0 }, + {3200,3200, 0, 0, 40000, 0, 0, 0 }, + {3201,3201, 0, 0, 40000, 353, 0, 0 }, + {3202,3202, 0, 0, 3920, 73, 0, 0 }, + {3203,3203, 0, 0, 5833, 813, 0, 0 }, + {3204,3204, 0, 0, 40000, 60, 0, 0 }, + {3205,3205, 0, 0, 40000, 73, 0, 0 }, + {3206,3206, 0, 0, 1400, 406, 0, 0 }, + {3207,3207, 0, 0, 40000, 66, 0, 0 }, + {3208,3208, 0, 0, 9066, 2220, 0, 0 }, + {3209,3209, 0, 0, 1473, 773, 0, 0 }, + {3210,3210, 0, 0, 40000, 120, 0, 0 }, + {3211,3211, 0, 0, 40000, 306, 0, 0 }, + {3212,3212, 0, 0, 9306, 3013, 0, 0 }, + {3213,3213, 0, 0, 40000, 60, 0, 0 }, + {3214,3214, 0, 0, 40000, 73, 0, 0 }, + {3215,3215, 0, 0, 40000, 73, 0, 0 }, + {3216,3216, 0, 0, 40000, 453, 0, 0 }, + {3217,3217, 0, 0, 40000, 3460, 0, 0 }, + {3218,3218, 0, 0, 40000, 453, 0, 0 }, + {3219,3219, 0, 0, 40000, 40, 0, 0 }, + {3220,3220, 0, 0, 40000, 3926, 0, 0 }, + {3221,3221, 0, 0, 40000, 4506, 0, 0 }, + {3222,3222, 0, 0, 4646, 646, 0, 0 }, + {3223,3223, 0, 0, 773, 100, 0, 0 }, + {3224,3224, 0, 0, 40000, 73, 0, 0 }, + {3225,3225, 0, 0, 40000, 173, 0, 0 }, + {3226,3226, 0, 0, 1606, 653, 0, 0 }, + {3227,3227, 0, 0, 2353, 806, 0, 0 }, + {3228,3228, 0, 0, 980, 360, 0, 0 }, + {3229,3229, 0, 0, 1193, 413, 0, 0 }, + { 499, 499, 0, 0, 266, 160, 0, 0 }, + {3230,3230, 0, 0, 973, 360, 0, 0 }, + {3231,3231, 0, 0, 273, 53, 0, 0 }, + {3232,3232, 0, 0, 726, 220, 0, 0 }, + {3233,3233, 0, 0, 19933, 6093, 0, 0 }, + {3234,3234, 0, 0, 40000, 0, 0, 0 }, + { 403, 403, 0, 0, 40000, 73, 0, 0 }, + {3235,3235, 0, 0, 4966, 233, 0, 0 }, + {3236,3236, 0, 0, 4946, 240, 0, 0 }, + {3237,3237, 0, 0, 4946, 233, 0, 0 }, + {3238,3238, 0, 0, 4640, 1613, 0, 0 }, + {3239,3239, 0, 0, 2360, 806, 0, 0 }, + {3240,3240, 0, 0, 4466, 200, 0, 0 }, + {3241,3241, 0, 0, 40000, 73, 0, 0 }, + {3242,3242, 0, 0, 40000, 73, 0, 0 }, + {3243,3243, 0, 0, 40000, 73, 0, 0 }, + {3244,3244, 0, 0, 40000, 73, 0, 0 }, + {3245,3245, 0, 0, 40000, 240, 0, 0 }, + {3246,3246, 0, 0, 40000, 226, 0, 0 }, + {3247,3247, 0, 0, 40000, 233, 0, 0 }, + {3248,3248, 0, 0, 40000, 240, 0, 0 }, + {3249,3249, 0, 0, 4306, 1253, 0, 0 }, + {3250,3250, 0, 0, 3873, 1206, 0, 0 }, + {3251,3251, 0, 0, 4640, 633, 0, 0 }, + {3252,3252, 0, 0, 1233, 80, 0, 0 }, + {3253,3253, 0, 0, 1233, 26, 0, 0 }, + {3254,3254, 0, 0, 1233, 26, 0, 0 }, + {3255,3255, 0, 0, 4573, 1253, 0, 0 }, + {3256,3256, 0, 0, 3793, 1240, 0, 0 }, + {3257,3257, 0, 0, 40000, 73, 0, 0 }, + {3258,3258, 0, 0, 40000, 73, 0, 0 }, + {3259,3259, 0, 0, 40000, 140, 0, 0 }, + {3260,3260, 0, 0, 40000, 146, 0, 0 }, + {3261,3261, 0, 0, 40000, 80, 0, 0 }, + {3262,3262, 0, 0, 5953, 200, 0, 0 }, + {3263,3263, 0, 0, 5926, 200, 0, 0 }, + {3264,3264, 0, 0, 5866, 26, 0, 0 }, + {3265,3265, 0, 0, 18573, 6153, 0, 0 }, + {3266,3266, 0, 0, 40000, 2093, 0, 0 }, + {3267,3267, 0, 0, 40000, 73, 0, 0 }, + {3268,3268, 0, 0, 18626, 1553, 0, 0 }, + {3269,3269, 0, 0, 40000, 1820, 0, 0 }, + {3270,3270, 0, 0, 40000, 500, 0, 0 }, + {3271,3271, 0, 0, 18206, 5900, 0, 0 }, + {3272,3272, 0, 0, 14200, 93, 0, 0 }, + {3273,3273, 0, 0, 40000, 2873, 0, 0 }, + {3274,3274, 0, 0, 14960, 4913, 0, 0 }, + {3275,3275, 0, 0, 40000, 86, 0, 0 }, + {3276,3276, 0, 0, 40000, 826, 0, 0 }, + {3277,3277, 0, 0, 40000, 200, 0, 0 }, + {3278,3278, 0, 0, 40000, 340, 0, 0 }, + {3279,3279, 0, 0, 13220, 2500, 0, 0 }, + {3280,3280, 0, 0, 40000, 100, 0, 0 }, + {3281,3281, 0, 0, 40000, 1026, 0, 0 }, + {3282,3282, 0, 0, 40000, 366, 0, 0 }, + {3283,3283, 0, 0, 40000, 386, 0, 0 }, + {3284,3284, 0, 0, 40000, 0, 0, 0 }, + {3285,3285, 0, 0, 40000, 0, 0, 0 }, + {3286,3286, 0, 0, 40000, 140, 0, 0 }, + {3287,3287, 0, 0, 40000, 53, 0, 0 }, + {3288,3288, 0, 0, 40000, 120, 0, 0 }, + {3289,3289, 0, 0, 8866, 1366, 0, 0 }, + {3290,3290, 0, 0, 4193, 1400, 0, 0 }, + {3291,3291, 0, 0, 8353, 673, 0, 0 }, + {3292,3292, 0, 0, 8353, 673, 0, 0 }, + {3293,3293, 0, 0, 8400, 593, 0, 0 }, + {3294,3294, 0, 0, 8440, 666, 0, 0 }, + {3295,3295, 0, 0, 9600, 1580, 0, 0 }, + {3296,3296, 0, 0, 40000, 46, 0, 0 }, + {3297,3297, 0, 0, 40000, 0, 0, 0 }, + {3298,3298, 0, 0, 1653, 93, 0, 0 }, + {3299,3299, 0, 0, 2706, 73, 0, 0 }, + {3300,3300, 0, 0, 11680, 26, 0, 0 }, + {3301,3301, 0, 0, 6500, 340, 0, 0 }, + {3302,3302, 0, 0, 40000, 0, 0, 0 }, + {3303,3303, 0, 0, 40000, 0, 0, 0 }, + {3304,3304, 0, 0, 40000, 73, 0, 0 }, + {3305,3305, 0, 0, 40000, 73, 0, 0 }, + {3306,3306, 0, 0, 40000, 73, 0, 0 }, + {3307,3307, 0, 0, 40000, 73, 0, 0 }, + {3308,3308, 0, 0, 40000, 73, 0, 0 }, + {3309,3309, 0, 0, 40000, 73, 0, 0 }, + {3310,3310, 0, 0, 40000, 73, 0, 0 }, + {3311,3311, 0, 0, 40000, 73, 0, 0 }, + {3312,3312, 0, 0, 40000, 73, 0, 0 }, + {3313,3313, 0, 0, 40000, 133, 0, 0 }, + {3314,3314, 0, 0, 40000, 126, 0, 0 }, + {3315,3315, 0, 0, 40000, 73, 0, 0 }, + {3316,3316, 0, 0, 40000, 73, 0, 0 }, + {3317,3317, 0, 0, 40000, 73, 0, 0 }, + {3318,3318, 0, 0, 40000, 200, 0, 0 }, + {3319,3319, 0, 0, 40000, 133, 0, 0 }, + {3320,3320, 0, 0, 40000, 0, 0, 0 }, + {3321,3321, 0, 0, 40000, 240, 0, 0 }, + {3322,3322, 0, 0, 40000, 220, 0, 0 }, + {3323,3323, 0, 0, 40000, 226, 0, 0 }, + {3324,3324, 0, 0, 40000, 100, 0, 0 }, + {3325,3325, 0, 0, 40000, 140, 0, 0 }, + {3326,3326, 0, 0, 40000, 0, 0, 0 }, + {3327,3327, 0, 0, 40000, 426, 0, 0 }, + {3328,3328, 0, 0, 40000, 426, 0, 0 }, + {3329,3329, 0, 0, 3680, 1220, 0, 0 }, + {3330,3330, 0, 0, 40000, 533, 0, 0 }, + {3331,3331, 0, 0, 40000, 813, 0, 0 }, + {3332,3332, 0, 0, 14506, 4706, 0, 0 }, + {3333,3333, 0, 0, 766, 420, 0, 0 }, + {3334,3334, 0, 0, 40000, 1566, 0, 0 }, + {3335,3335, 0, 0, 40000, 120, 0, 0 }, + {3336,3336, 0, 0, 40000, 2380, 0, 0 }, + {3337,3337, 0, 0, 5666, 300, 0, 0 }, + {3338,3338, 0, 0, 40000, 73, 0, 0 }, + {3339,3339, 0, 0, 40000, 2513, 0, 0 }, + {3340,3340, 0, 0, 1260, 826, 0, 0 }, + {3341,3341, 0, 0, 2420, 413, 0, 0 }, + {3342,3342, 0, 0, 626, 240, 0, 0 }, + {3343,3343, 0, 0, 273, 60, 0, 0 }, + {3344,3344, 0, 0, 540, 20, 0, 0 }, + {3345,3345, 0, 0, 540, 20, 0, 0 }, + {3346,3346, 0, 0, 540, 20, 0, 0 }, + {3347,3347, 0, 0, 1153, 760, 0, 0 }, + {3348,3348, 0, 0, 40000, 100, 0, 0 }, + {3349,3349, 0, 0, 7326, 2380, 0, 0 }, + {3350,3350, 0, 0, 40000, 4426, 0, 0 }, + {3351,3351, 0, 0, 7413, 2493, 0, 0 }, + {3352,3352, 0, 0, 253, 20, 0, 0 }, + {3353,3353, 0, 0, 246, 33, 0, 0 }, + {3354,3354, 0, 0, 286, 13, 0, 0 }, + {3355,3355, 0, 0, 953, 13, 0, 0 }, + {3356,3356, 0, 0, 293, 20, 0, 0 }, + { 142, 142, 20, 0, 1893, 620, 0, 0 }, + {3357,1451, 0, 4, 2340, 780, 0, 0 }, + {3358,3359, 0, 4, 9206, 240, 0, 0 }, + {3360,1455, 0, 4, 40000, 0, 0, 0 }, + {3361,1463, 0, 4, 40000, 266, 0, 0 }, + { 225,3362, 0, 4, 7993, 100, 0, 0 }, + {3363,1545, 0, 4, 293, 86, 0, 0 }, + {3364,1547, 0, 4, 40000, 180, 0, 0 }, + {3365,3366, 39, 4, 66, 26, 0, 0 }, + {3367, 368, 58, 4, 173, 93, 0, 0 }, + {3368,1551, 48, 4, 520, 200, 0, 0 }, + {3368,3033, 49, 4, 53, 26, 0, 0 }, + {3368,3033, 51, 4, 53, 26, 0, 0 }, + {3368,3033, 54, 4, 60, 26, 0, 0 }, + {3368,3033, 57, 4, 60, 26, 0, 0 }, + {3368,3033, 60, 4, 60, 26, 0, 0 }, + {3369,3370, 70, 4, 773, 306, 0, 0 }, + {1564,1565, 80, 4, 220, 106, 0, 0 }, + {3371,1571, 44, 4, 413, 93, 0, 0 }, + {3372,3372, 0, 0, 8366, 666, 0, 0 }, + {3373,3373, 0, 0, 8366, 666, 0, 0 }, + {3374,3374, 0, 0, 3773, 73, 0, 0 }, + {3375,3375, 0, 0, 8366, 666, 0, 0 }, + {3376,3376, 0, 0, 4693, 26, 0, 0 }, + {3377,3377, 0, 0, 7400, 80, 0, 0 }, + {3378,3378, 0, 0, 3586, 80, 0, 0 }, + {3379,3379, 0, 0, 8366, 666, 0, 0 }, + {3380,3380, 0, 0, 3786, 1240, 0, 0 }, + {3381,3381, 0, 0, 9013, 1466, 0, 0 }, + {3382,3382, 0, 0, 1200, 73, 0, 0 }, + {3383,3383, 0, 0, 8146, 1446, 0, 0 }, + {3384,3384, 0, 0, 3660, 1206, 0, 0 }, + {3385,3385, 0, 0, 200, 100, 0, 0 }, + {3386,3386, 0, 0, 40000, 0, 0, 0 }, + {3387,3387, 0, 0, 1213, 426, 0, 0 }, + {3388,3388, 0, 0, 40000, 2573, 0, 0 }, + {3389,3389, 0, 0, 40000, 3446, 0, 0 }, + {3390,3390, 0, 0, 40000, 333, 0, 0 }, + {3391,3391, 0, 0, 40000, 73, 0, 0 }, + {3392,3392, 0, 0, 40000, 93, 0, 0 }, + {3393,3393, 0, 0, 40000, 73, 0, 0 }, + {3394,3394, 0, 0, 40000, 73, 0, 0 }, + {3395,3395, 0, 0, 40000, 73, 0, 0 }, + {3396,3396, 0, 0, 2193, 413, 0, 0 }, + {3397,3397, 0, 0, 14606, 2886, 0, 0 }, + {3398,3398, 0, 0, 10626, 4520, 0, 0 }, + {3399,3399, 0, 0, 2413, 100, 0, 0 }, + {3400,3400, 0, 0, 3593, 1140, 0, 0 }, + {3401,3401, 0, 0, 40000, 146, 0, 0 }, + {3402,3402, 0, 0, 40000, 86, 0, 0 }, + {3403,3403, 0, 0, 40000, 86, 0, 0 }, + {3404,3404, 0, 0, 9366, 106, 0, 0 }, + {3405,3405, 0, 0, 40000, 73, 0, 0 }, + {3406,3406, 0, 0, 40000, 0, 0, 0 }, + {3407,3407, 0, 0, 40000, 0, 0, 0 }, + {3408,3408, 0, 0, 1626, 400, 0, 0 }, + {3409,3409, 0, 0, 4473, 2933, 0, 0 }, + {3410,3410, 0, 0, 40000, 66, 0, 0 }, + {3411,3411, 0, 0, 40000, 0, 0, 0 }, + {3412,3412, 0, 0, 40000, 253, 0, 0 }, + {3413,3413, 0, 0, 40000, 233, 0, 0 }, + {3414,3414, 0, 0, 40000, 346, 0, 0 }, + {3415,3415, 0, 0, 1966, 26, 0, 0 }, + {3416,3416, 0, 0, 40000, 366, 0, 0 }, + {3417,3417, 0, 0, 2266, 386, 0, 0 }, + {3418,3418, 0, 0, 40000, 0, 0, 0 }, + {3419,3419, 0, 0, 2313, 766, 0, 0 }, + {3420,3420, 0, 0, 40000, 340, 0, 0 }, + {3421,3421, 0, 0, 40000, 346, 0, 0 }, + {3422,3422, 0, 0, 40000, 340, 0, 0 }, + {3423,3423, 0, 0, 40000, 353, 0, 0 }, + {3424,3424, 0, 0, 40000, 353, 0, 0 }, + {3425,3425, 0, 0, 40000, 226, 0, 0 }, + {3426,3426, 0, 0, 40000, 73, 0, 0 }, + {3427,3427, 0, 0, 940, 253, 0, 0 }, + {3428,3428, 0, 0, 40000, 73, 0, 0 }, + {3429,3429, 0, 0, 40000, 80, 0, 0 }, + {3430,3430, 0, 0, 40000, 240, 0, 0 }, + {3431,3431, 0, 0, 40000, 80, 0, 0 }, + {3432,3432, 0, 0, 40000, 73, 0, 0 }, + {3433,3433, 0, 0, 40000, 73, 0, 0 }, + {3434,3434, 0, 0, 40000, 73, 0, 0 }, + {3435,3435, 0, 0, 40000, 73, 0, 0 }, + {3436,3436, 0, 0, 40000, 73, 0, 0 }, + {3437,3437, 0, 0, 40000, 73, 0, 0 }, + {3438,3438, 0, 0, 40000, 73, 0, 0 }, + {3439,3439, 0, 0, 40000, 73, 0, 0 }, + {3440,3440, 0, 0, 40000, 73, 0, 0 }, + {3441,3441, 0, 0, 40000, 73, 0, 0 }, + {3442,3442, 0, 0, 40000, 66, 0, 0 }, + {3443,3443, 0, 0, 40000, 73, 0, 0 }, + {3444,3444, 0, 0, 40000, 80, 0, 0 }, + {3445,3445, 0, 0, 40000, 66, 0, 0 }, + {3446,3446, 0, 0, 40000, 66, 0, 0 }, + {3447,3447, 0, 0, 40000, 66, 0, 0 }, + {3448,3448, 0, 0, 40000, 66, 0, 0 }, + {3449,3449, 0, 0, 40000, 80, 0, 0 }, + {3450,3450, 0, 0, 40000, 353, 0, 0 }, + {3451,3451, 0, 0, 40000, 0, 0, 0 }, + {3452,3452, 0, 0, 18440, 100, 0, 0 }, + {3453,3453, 0, 0, 18086, 100, 0, 0 }, + {3454,3454, 0, 0, 266, 66, 0, 0 }, + {3455,3455, 0, 0, 40000, 80, 0, 0 }, + {3456,3456, 0, 0, 40000, 100, 0, 0 }, + {3457,3457, 0, 0, 40000, 80, 0, 0 }, + {3458,3458, 0, 0, 40000, 120, 0, 0 }, + {3459,3459, 0, 0, 40000, 93, 0, 0 }, + {3460,3460, 0, 0, 40000, 233, 0, 0 }, + {3461,3461, 0, 0, 40000, 0, 0, 0 }, + {3462,3462, 0, 0, 40000, 86, 0, 0 }, + {3463,3463, 0, 0, 40000, 820, 0, 0 }, + {3464,3464, 0, 0, 40000, 4986, 0, 0 }, + {3465,3465, 0, 0, 40000, 146, 0, 0 }, + {3466,3466, 0, 0, 40000, 100, 0, 0 }, + {3467,3467, 0, 0, 40000, 3346, 0, 0 }, + {3468,3468, 0, 0, 40000, 660, 0, 0 }, + {3469,3469, 0, 0, 40000, 366, 0, 0 }, + {3470,3470, 0, 0, 40000, 1480, 0, 0 }, + {3471,3471, 0, 0, 40000, 646, 0, 0 }, + {3472,3472, 0, 0, 40000, 2673, 0, 0 }, + {3473,3473, 0, 0, 40000, 2500, 0, 0 }, + {3474,3474, 0, 0, 40000, 2513, 0, 0 }, + {3475,3475, 0, 0, 40000, 66, 0, 0 }, + {3476,3476, 0, 0, 9600, 1580, 0, 0 }, + {3477,3477, 0, 0, 40000, 46, 0, 0 }, + {3478,3478, 0, 0, 10673, 100, 0, 0 }, + {3479,3479, 0, 0, 2333, 800, 0, 0 }, + {3480,3480, 0, 0, 3673, 1200, 0, 0 }, + {3481,3481, 0, 0, 40000, 73, 0, 0 }, + {3482,3482, 0, 0, 40000, 146, 0, 0 }, + {3483,3483, 0, 0, 40000, 73, 0, 0 }, + {3484,3484, 0, 0, 2266, 726, 0, 0 }, + {3485,3485, 0, 0, 333, 140, 0, 0 }, + {3486,3486, 0, 0, 2286, 746, 0, 0 }, + {3487,3487, 0, 0, 293, 126, 0, 0 }, + {3488,3488, 0, 0, 3700, 1213, 0, 0 }, + {3489,3489, 0, 0, 3773, 1186, 0, 0 }, + {3490,3490, 0, 0, 3646, 1200, 0, 0 }, + {3491,3491, 0, 0, 3020, 73, 0, 0 }, + {3492,3492, 0, 0, 786, 273, 0, 0 }, + {3493,3493, 0, 0, 40000, 146, 0, 0 }, + {3494,3494, 0, 0, 40000, 3093, 0, 0 }, + {3495,3495, 0, 0, 273, 60, 0, 0 }, + {3496,3496, 0, 0, 40000, 73, 0, 0 }, + {3497,3497, 0, 0, 40000, 73, 0, 0 }, + {3498,3498, 0, 0, 40000, 3093, 0, 0 }, + {3499,3499, 0, 0, 40000, 240, 0, 0 }, + {3500,3500, 0, 2, 6, 0, 0, 0 }, + { 739, 739, 46, 0, 220, 33, 0, 0 }, + {3501,3501, 47, 0, 973, 93, 0, 0 }, + {3502,3502, 64, 0, 126, 66, 0, 0 }, + {3503,3503, 40, 0, 340, 146, 0, 0 }, + {3504,3504, 48, 0, 100, 26, 0, 0 }, + {3505,3505, 48, 0, 286, 133, 0, 0 }, + {3506,3506, 46, 0, 466, 166, 0, 0 }, + {3507,3507,111, 0, 226, 113, 0, 0 }, + {3508,3508, 49, 0, 473, 166, 0, 0 }, + {3509,3509, 56, 0, 126, 40, 0, 0 }, + {3510,3510, 52, 0, 520, 206, 0, 0 }, + {3511,3511, 96, 0, 1346, 473, 0, 0 }, + {3510,3510, 54, 0, 513, 206, 0, 0 }, + {3512,3512, 57, 0, 973, 266, 0, 0 }, + {3513,3513, 82, 0, 1580, 553, 0, 0 }, + {3510,3510, 60, 0, 506, 200, 0, 0 }, + {3514,3514, 60, 0, 1886, 646, 0, 0 }, + {3515,3515, 92, 0, 1026, 520, 0, 0 }, + {3516,3516, 60, 0, 180, 93, 0, 0 }, + {3517,3517, 58, 0, 213, 213, 0, 0 }, + {3518,3518, 22, 0, 2300, 766, 0, 0 }, + {3519,3519, 60, 0, 1873, 653, 0, 0 }, + {3520,3520, 72, 0, 260, 93, 0, 0 }, + {3521,3521, 77, 0, 253, 93, 0, 0 }, + {3522,3522, 70, 0, 206, 93, 0, 0 }, + {3523,3523, 75, 0, 173, 93, 0, 0 }, + {3524,3524, 69, 0, 406, 113, 0, 0 }, + {3525,3525, 59, 0, 380, 160, 0, 0 }, + {3526,3526, 48, 0, 373, 40, 0, 0 }, + {3527,3527, 89, 0, 433, 180, 0, 0 }, + {3528,3528, 84, 0, 813, 180, 0, 0 }, + {3529,3529, 33, 0, 240, 53, 0, 0 }, + {3530,3530, 55, 0, 220, 86, 0, 0 }, + {3531,3531, 58, 0, 526, 200, 0, 0 }, + {3532,3532, 52, 0, 526, 193, 0, 0 }, + {3533,3533, 57, 0, 166, 80, 0, 0 }, + {3534,3534, 57, 0, 240, 100, 0, 0 }, + {3535,3535, 85, 0, 220, 113, 0, 0 }, + {3536,3536, 68, 0, 173, 93, 0, 0 }, + {3536,3536, 61, 0, 220, 113, 0, 0 }, + {3537,3537, 64, 0, 346, 53, 0, 0 }, + {3538,3538, 44, 0, 1080, 346, 0, 0 }, + {3539,3539,100, 0, 193, 20, 0, 0 }, + {3540,3540,100, 0, 793, 26, 0, 0 }, + {3541,3541, 0, 0, 14166, 320, 0, 0 }, + {3542,3542, 0, 0, 3873, 1613, 0, 0 }, + {3543,3543, 0, 0, 3586, 86, 0, 0 }, + {3544,3544, 0, 0, 7406, 2486, 0, 0 }, + {3545,3545, 0, 0, 4640, 1560, 0, 0 }, + {3546,3546, 0, 0, 446, 440, 0, 0 }, + {3547,3547, 0, 0, 9253, 3100, 0, 0 }, + {3548,3548, 0, 0, 4646, 646, 0, 0 }, + {3549,3549, 0, 0, 40000, 66, 0, 0 }, + {3550,3550, 0, 0, 40000, 73, 0, 0 }, + {3551,3551, 0, 0, 40000, 113, 0, 0 }, + {3552,3552, 0, 0, 40000, 73, 0, 0 }, + {3553,3553, 0, 0, 40000, 73, 0, 0 }, + {3554,3554, 0, 0, 40000, 0, 0, 0 }, + {3555,3555, 0, 0, 40000, 60, 0, 0 }, + {3556,3556, 0, 0, 3673, 1206, 0, 0 }, + {3557,3557, 0, 0, 3706, 1293, 0, 0 }, + {3558,3558, 0, 0, 5693, 1126, 0, 0 }, + {3559,3559, 0, 0, 2406, 846, 0, 0 }, + {3560,3560, 0, 0, 40000, 66, 0, 0 }, + {3561,3561, 0, 0, 40000, 73, 0, 0 }, + {3562,3562, 0, 0, 4333, 13, 0, 0 }, + {3563,3563, 0, 0, 3700, 66, 0, 0 }, + {3564,3564, 0, 0, 40000, 0, 0, 0 }, + {3565,3565, 0, 0, 3713, 1260, 0, 0 }, + {3566,3566, 0, 0, 1140, 126, 0, 0 }, + {3567,3567, 0, 0, 40000, 186, 0, 0 }, + {3568,3568, 0, 0, 40000, 0, 0, 0 }, + {3569,3569, 0, 0, 14400, 6, 0, 0 }, + {3570,3570, 0, 0, 14580, 66, 0, 0 }, + {3571,3571, 0, 0, 40000, 73, 0, 0 }, + {3572,3572, 0, 0, 40000, 353, 0, 0 }, + {3573,3573, 0, 0, 40000, 0, 0, 0 }, + {3574,3574, 0, 0, 40000, 173, 0, 0 }, + {3575,3575, 0, 0, 1833, 600, 0, 0 }, + {3576,3576, 0, 0, 40000, 0, 0, 0 }, + {3577,3577, 0, 0, 40000, 206, 0, 0 }, + {3578,3578, 0, 0, 40000, 46, 0, 0 }, + {3579,3579, 0, 0, 40000, 73, 0, 0 }, + {3580,3580, 0, 0, 9166, 2900, 0, 0 }, + {3581,3581, 0, 0, 5640, 680, 0, 0 }, + {3582,3582, 0, 0, 640, 220, 0, 0 }, + {3583,3583, 0, 0, 40000, 53, 0, 0 }, + {3584,3584, 0, 0, 40000, 26, 0, 0 }, + {3585,3585, 0, 0, 40000, 0, 0, 0 }, + {3586,3586, 0, 0, 40000, 66, 0, 0 }, + {3587,3587, 0, 0, 40000, 60, 0, 0 }, + {3588,3588, 0, 0, 40000, 0, 0, 0 }, + {3589,3589, 0, 0, 40000, 73, 0, 0 }, + {3590,3590, 0, 0, 40000, 0, 0, 0 }, + {3591,3591, 0, 0, 40000, 0, 0, 0 }, + {3592,3592, 0, 0, 3780, 73, 0, 0 }, + {3593,3593, 0, 0, 40000, 0, 0, 0 }, + {3594,3594, 0, 0, 3786, 73, 0, 0 }, + {3595,3595, 0, 0, 40000, 73, 0, 0 }, + {3596,3596, 0, 0, 40000, 66, 0, 0 }, + {3597,3597, 0, 0, 40000, 73, 0, 0 }, + {3598,3598, 0, 0, 40000, 53, 0, 0 }, + {3599,3599, 0, 0, 40000, 426, 0, 0 }, + {3600,3600, 0, 0, 40000, 133, 0, 0 }, + {3601,3601, 0, 0, 40000, 66, 0, 0 }, + {3602,3602, 0, 0, 40000, 433, 0, 0 }, + {3603,3603, 0, 0, 393, 126, 0, 0 }, + {3604,3604, 0, 0, 40000, 66, 0, 0 }, + {3605,3605, 0, 0, 40000, 353, 0, 0 }, + {3606,3606, 0, 0, 3813, 73, 0, 0 }, + {3607,3607, 0, 0, 5793, 780, 0, 0 }, + {3608,3608, 0, 0, 40000, 73, 0, 0 }, + {3609,3609, 0, 0, 40000, 86, 0, 0 }, + {3610,3610, 0, 0, 820, 206, 0, 0 }, + {3611,3611, 0, 0, 40000, 66, 0, 0 }, + {3612,3612, 0, 0, 40000, 200, 0, 0 }, + {3613,3613, 0, 0, 18186, 720, 0, 0 }, + {3614,3614, 0, 0, 40000, 0, 0, 0 }, + {3615,3615, 0, 0, 40000, 493, 0, 0 }, + {3616,3616, 0, 0, 40000, 306, 0, 0 }, + {3617,3617, 0, 0, 2166, 600, 0, 0 }, + {3618,3618, 0, 0, 40000, 73, 0, 0 }, + {3619,3619, 0, 0, 40000, 3073, 0, 0 }, + {3620,3620, 0, 0, 2333, 413, 0, 0 }, + {3621,3621, 0, 0, 14880, 73, 0, 0 }, + {3622,3622, 0, 0, 40000, 66, 0, 0 }, + {3623,3623, 0, 0, 40000, 73, 0, 0 }, + {3624,3624, 0, 0, 40000, 1873, 0, 0 }, + {3625,3625, 0, 0, 40000, 446, 0, 0 }, + {3626,3626, 0, 0, 40000, 3126, 0, 0 }, + {3627,3627, 0, 0, 18446, 6140, 0, 0 }, + {3628,3628, 0, 0, 1113, 240, 0, 0 }, + {3629,3629, 0, 0, 40000, 3600, 0, 0 }, + {3630,3630, 0, 0, 40000, 4726, 0, 0 }, + {3631,3631, 0, 0, 40000, 0, 0, 0 }, + {3632,3632, 0, 0, 2893, 606, 0, 0 }, + {3633,3633, 0, 0, 40000, 0, 0, 0 }, + {3634,3634, 0, 0, 40000, 0, 0, 0 }, + {3635,3635, 0, 0, 40000, 173, 0, 0 }, + {3636,3636, 0, 0, 40000, 60, 0, 0 }, + {3637,3637, 0, 0, 40000, 0, 0, 0 }, + {3638,3638, 0, 0, 986, 326, 0, 0 }, + {3639,3639, 0, 0, 1873, 646, 0, 0 }, + {3640,3640, 0, 0, 200, 260, 0, 0 }, + {3641,3641, 0, 0, 1180, 393, 0, 0 }, + {3642,3642, 0, 0, 266, 160, 0, 0 }, + {3643,3643, 0, 0, 313, 126, 0, 0 }, + {3644,3644, 0, 0, 406, 253, 0, 0 }, + {3645,3645, 0, 0, 1013, 813, 0, 0 }, + {3646,3646, 0, 0, 273, 53, 0, 0 }, + {3647,3647, 0, 0, 720, 213, 0, 0 }, + {3648,3648, 0, 0, 386, 120, 0, 0 }, + {3649,3649, 0, 0, 40000, 766, 0, 0 }, + {3650,3650, 0, 0, 40000, 66, 0, 0 }, + {3651,3651, 0, 0, 40000, 73, 0, 0 }, + {3652,3652, 0, 0, 1186, 426, 0, 0 }, + {3653,3653, 0, 0, 16720, 240, 0, 0 }, + {3654,3654, 0, 0, 8026, 246, 0, 0 }, + {3655,3655, 0, 0, 18186, 140, 0, 0 }, + {3656,3656, 0, 0, 14566, 200, 0, 0 }, + {3657,3657, 0, 0, 7973, 20, 0, 0 }, + {3658,3658, 0, 0, 4446, 86, 0, 0 }, + {3659,3659, 0, 0, 4473, 100, 0, 0 }, + {3660,3660, 0, 0, 8646, 153, 0, 0 }, + {3661,3661, 0, 0, 3726, 660, 0, 0 }, + {3662,3662, 0, 0, 1893, 653, 0, 0 }, + {3663,3663, 0, 0, 1933, 760, 0, 0 }, + {3664,3664, 0, 0, 9160, 240, 0, 0 }, + {3665,3665, 0, 0, 1133, 100, 0, 0 }, + {3666,3666, 0, 0, 633, 233, 0, 0 }, + {3667,3667, 0, 0, 9153, 3060, 0, 0 }, + {3668,3668, 0, 0, 2166, 406, 0, 0 }, + {3669,3669, 0, 0, 40000, 66, 0, 0 }, + {3670,3670, 0, 0, 40000, 73, 0, 0 }, + {3671,3671, 0, 0, 40000, 73, 0, 0 }, + {3672,3672, 0, 0, 40000, 73, 0, 0 }, + {3673,3673, 0, 0, 40000, 346, 0, 0 }, + {3674,3674, 0, 0, 40000, 353, 0, 0 }, + {3675,3675, 0, 0, 40000, 200, 0, 0 }, + {3676,3676, 0, 0, 40000, 320, 0, 0 }, + {3677,3677, 0, 0, 4646, 100, 0, 0 }, + {3678,3678, 0, 0, 4426, 133, 0, 0 }, + {3679,3679, 0, 0, 4633, 100, 0, 0 }, + {3680,3680, 0, 0, 2266, 133, 0, 0 }, + {3681,3681, 0, 0, 2346, 53, 0, 0 }, + {3682,3682, 0, 0, 40000, 66, 50, 0 }, + {3683,3683, 0, 0, 9686, 173, 20, 0 }, + {3684,3684, 0, 0, 14300, 66, 0, 0 }, + {3685,3685, 0, 0, 40000, 0, 0, 0 }, + {3686,3686, 0, 0, 40000, 0, 0, 0 }, + {3687,3687, 0, 0, 40000, 0, 0, 0 }, + {3688,3688, 0, 0, 8613, 73, 0, 0 }, + {3689,3689, 0, 0, 40000, 0, 0, 0 }, + {3690,3690, 0, 0, 40000, 0, 0, 0 }, + {3691,3691, 0, 0, 40000, 0, 0, 0 }, + {3692,3692, 0, 0, 40000, 0, 0, 0 }, + {3693,3693, 0, 0, 40000, 393, 0, 0 }, + {3694,3694, 0, 0, 40000, 126, 0, 0 }, + {3695,3695, 0, 0, 40000, 120, 0, 0 }, + {3696,3696, 0, 0, 40000, 0, 0, 0 }, + {3697,3697, 0, 0, 40000, 226, 0, 0 }, + {3698,3698, 0, 0, 7420, 1186, 0, 0 }, + {3699,3699, 0, 0, 3280, 1726, 0, 0 }, + {3700,3700, 0, 0, 3680, 1220, 0, 0 }, + {3701,3701, 0, 0, 40000, 480, 0, 0 }, + {3702,3702, 0, 0, 40000, 306, 0, 0 }, + {3703,3703, 0, 0, 40000, 433, 0, 0 }, + {3704,3704, 0, 0, 40000, 133, 0, 0 }, + {3705,3705, 0, 0, 40000, 0, 0, 0 }, + {3706,3706, 0, 0, 40000, 0, 0, 0 }, + {3707,3707, 0, 0, 1166, 380, 0, 0 }, + {3708,3708, 0, 0, 40000, 140, 0, 0 }, + {3709,3709, 0, 0, 40000, 126, 0, 0 }, + {3710,3710, 0, 0, 40000, 100, 0, 0 }, + {3711,3711, 0, 0, 40000, 66, 0, 0 }, + {3712,3712, 0, 0, 40000, 226, 0, 0 }, + {3713,3713, 0, 0, 40000, 133, 0, 0 }, + {3714,3714, 0, 0, 40000, 73, 0, 0 }, + {3715,3715, 0, 0, 40000, 226, 0, 0 }, + {3716,3716, 0, 0, 40000, 100, 0, 0 }, + {3717,3717, 0, 0, 40000, 80, 0, 0 }, + {3718,3718, 0, 0, 40000, 100, 0, 0 }, + {3719,3719, 0, 0, 40000, 73, 0, 0 }, + {3720,3720, 0, 0, 40000, 73, 0, 0 }, + {3721,3721, 0, 0, 40000, 73, 0, 0 }, + {3722,3722, 0, 0, 40000, 126, 0, 0 }, + {3723,3723, 0, 0, 40000, 80, 0, 0 }, + {3724,3724, 0, 0, 40000, 73, 0, 0 }, + {3725,3725, 0, 0, 40000, 86, 0, 0 }, + {3726,3726, 0, 0, 40000, 100, 0, 0 }, + {3727,3727, 0, 0, 40000, 0, 0, 0 }, + {3728,3728, 0, 0, 40000, 126, 0, 0 }, + {3729,3729, 0, 0, 40000, 133, 0, 0 }, + {3730,3730, 0, 0, 40000, 140, 0, 0 }, + {3731,3731, 0, 0, 40000, 73, 0, 0 }, + {3732,3732, 0, 0, 40000, 60, 0, 0 }, + {3733,3733, 0, 0, 40000, 93, 0, 0 }, + {3734,3734, 0, 0, 40000, 80, 0, 0 }, + {3735,3735, 0, 0, 40000, 66, 0, 0 }, + {3736,3736, 0, 0, 40000, 0, 0, 0 }, + {3737,3737, 0, 0, 40000, 220, 0, 0 }, + {3738,3738, 0, 0, 40000, 80, 0, 0 }, + {3739,3739, 0, 0, 40000, 400, 0, 0 }, + {3740,3740, 0, 0, 40000, 1373, 0, 0 }, + {3741,3741, 0, 0, 40000, 86, 0, 0 }, + {3742,3742, 0, 0, 40000, 1313, 0, 0 }, + {3743,3743, 0, 0, 40000, 0, -50, 0 }, + {3744,3744, 0, 0, 11486, 593, 0, 0 }, + {3745,3745, 0, 0, 40000, 1246, 0, 0 }, + {3746,3746, 0, 0, 40000, 140, -20, 0 }, + {3747,3747, 0, 0, 14386, 2680, 0, 0 }, + {3748,3748, 0, 0, 40000, 653, 0, 0 }, + {3749,3749, 0, 0, 2286, 713, 0, 0 }, + {3750,3750, 0, 0, 40000, 253, 0, 0 }, + {3751,3751, 0, 0, 6933, 406, 0, 0 }, + {3752,3752, 0, 0, 40000, 1313, 0, 0 }, + {3753,3753, 0, 0, 40000, 1440, 0, 0 }, + {3754,3754, 0, 0, 40000, 73, 0, 0 }, + {3755,3755, 0, 0, 11100, 420, 0, 0 }, + {3756,3756, 0, 0, 6493, 320, 0, 0 }, + {3757,3757, 0, 0, 3486, 126, 0, 0 }, + {3758,3758, 0, 0, 6620, 2133, 0, 0 }, + {3759,3759, 0, 0, 1180, 413, 0, 0 }, + {3760,3760, 0, 0, 40000, 73, 0, 0 }, + {3761,3761, 0, 0, 40000, 73, 0, 0 }, + {3762,3762, 0, 0, 40000, 66, 0, 0 }, + {3763,3763, 0, 0, 4580, 413, 0, 0 }, + {3764,3764, 0, 0, 340, 146, 0, 0 }, + {3765,3765, 0, 0, 1166, 400, 0, 0 }, + {3766,3766, 0, 0, 1346, 660, 0, 0 }, + {3767,3767, 0, 0, 1260, 393, 0, 0 }, + {3768,3768, 0, 0, 3646, 1186, 0, 0 }, + {3769,3769, 0, 0, 2713, 400, 0, 0 }, + {3770,3770, 0, 0, 1780, 73, 0, 0 }, + {3771,3771, 0, 0, 800, 213, 0, 0 }, + {3772,3772, 0, 0, 660, 173, 0, 0 }, + {3773,3773, 0, 0, 12146, 73, 0, 0 }, + {3774,3774, 0, 0, 273, 60, 0, 0 }, + {3775,3775, 0, 0, 40000, 73, 0, 0 }, + {3776,3776, 0, 0, 380, 53, 0, 0 }, + {3777,3777, 0, 0, 40000, 200, 0, 0 }, + {3778,3778, 0, 0, 586, 20, 0, 0 }, + {3779,3779, 0, 2, 6, 0, 40, 0 }, + { 738, 738, 44, 0, 840, 340, 33, 0 }, + {3780,3780, 36, 0, 7366, 140, 33, 0 }, + {3781,3781, 32, 0, 100, 40, 40, 0 }, + {2030,2030, 60, 0, 293, 126, 50, 0 }, + {3782,3782, 24, 0, 100, 0, 40, 0 }, + {3783,3783, 60, 0, 126, 73, 50, 0 }, + {3784,3784, 44, 0, 393, 93, 50, 0 }, + { 132, 132, 44, 0, 173, 100, 40, 0 }, + {3785,3785, 47, 0, 393, 93, 50, 0 }, + { 152, 152, 44, 0, 213, 86, 40, 0 }, + {3784,3784, 50, 0, 393, 93, 50, 0 }, + { 139, 139, 44, 0, 293, 100, 40, 0 }, + {3784,3784, 54, 0, 393, 93, 50, 0 }, + {3784,3784, 57, 0, 393, 93, 50, 0 }, + {3786,3786, 60, 0, 1900, 666, 40, 0 }, + {3784,3784, 60, 0, 393, 93, 50, 0 }, + {3514,3514, 60, 0, 1886, 646, 30, 0 }, + {3787,3787, 60, 0, 1900, 666, 45, 0 }, + {3788,3788, 60, 0, 1886, 666, 30, 0 }, + {3516,3516, 60, 0, 180, 93, 40, 0 }, + {3789,3789, 60, 0, 1866, 653, 40, 0 }, + {3517,3517, 58, 0, 213, 213, 40, 0 }, + {3790,3790, 60, 0, 1873, 633, 40, 0 }, + {3791,3791, 44, 0, 946, 333, 40, 0 }, + {3519,3519, 60, 0, 1873, 653, 20, 0 }, + {2037,2037, 44, 0, 213, 126, 40, 0 }, + { 144, 144, 44, 0, 213, 126, 40, 0 }, + {2038,2038, 44, 0, 106, 46, 40, 0 }, + {3792,3792, 44, 0, 380, 360, 40, 0 }, + {3793,3793, 44, 0, 520, 206, 40, 0 }, + {3794,3794, 45, 0, 273, 100, 40, 0 }, + {3795,3795, 33, 0, 326, 106, 40, 0 }, + {3796,3796, 56, 0, 506, 200, 40, 0 }, + {3796,3796, 51, 0, 506, 200, 40, 0 }, + {3530,3530, 55, 0, 220, 86, 40, 0 }, + {3797,3797, 44, 0, 126, 66, 40, 0 }, + {3798,3798, 44, 0, 553, 186, 40, 0 }, + {3533,3533, 57, 0, 166, 80, 40, 0 }, + {3534,3534, 56, 0, 240, 100, 40, 0 }, + { 158, 158, 68, 0, 126, 140, 40, 0 }, + {3799,3799, 51, 0, 513, 206, 40, 0 }, + {3800,3800, 46, 0, 506, 200, 40, 0 }, + {3537,3537, 64, 0, 346, 53, 40, 0 }, + {3538,3538, 44, 0, 1080, 346, 40, 0 }, + {3801,3801, 44, 0, 513, 206, 40, 0 }, + {3802,3802, 44, 0, 3720, 1260, 40, 0 }, + { 152, 152, 45, 0, 220, 80, 20, 0 }, + {3683,3683, 0, 0, 9686, 173, 50, 0 }, + {3803,3803, 0, 0, 40000, 86, 20, 0 }, + {3804,3804, 0, 0, 40000, 226, 0, 0 }, + {3712,3712, 0, 0, 40000, 226, 50, 0 }, + {3805,3805, 0, 0, 40000, 73, 0, 0 }, + {3806,3806, 0, 0, 40000, 73, 0, 0 }, + {3807,3807, 0, 0, 40000, 93, 0, 0 }, + {3808,3808, 0, 0, 4653, 660, -18, 0 }, + {3809,3809, 0, 0, 6686, 2246, 10, 0 }, + {3810,3810, 0, 0, 1180, 413, 0, 0 }, + {3811,3811, 0, 0, 966, 293, 30, 0 }, + {3812,3812, 0, 0, 1780, 66, 0, 0 }, + {3780,3780, 45, 0, 5900, 113, 20, 0 }, + {3061,3061, 45, 0, 40000, 0, 40, 0 }, + {3813,3813, 60, 0, 126, 226, 60, 0 }, + {3781,3781, 60, 0, 93, 33, 60, 0 }, + {3814,3814, 44, 0, 393, 86, 50, 0 }, + {3815,3815, 57, 0, 166, 80, 127, 0 }, + {3816,3816, 56, 0, 240, 100, 127, 0 }, + {3817,3817, 60, 0, 113, 26, 60, 0 }, + {3818,3818, 60, 0, 113, 26, 60, 0 }, + {3061,3061, 45, 0, 40000, 0, 20, 0 }, + {3517,3517, 45, 0, 213, 213, 20, 0 }, + {3819,3819, 0, 0, 4033, 100, 0, 0 }, + {3820,3820, 0, 0, 5200, 873, 0, 0 }, + {3821,3821, 0, 0, 40000, 0, 0, 0 }, + {3822,3822, 0, 0, 10493, 160, 0, 0 }, + {3823,3823, 0, 0, 40000, 740, 0, 0 }, + {3824,3825, 0, 1, 40000, 366, 0, 0.078125 }, + {3826,3826, 0, 0, 40000, 5100, 0, 0 }, + {3827,3827, 0, 0, 40000, 766, 0, 0 }, + {3828,1172, 0, 1, 40000, 780, 0, 0.15625 }, + {3829,3829, 0, 0, 40000, 60, 0, 0 }, + {3830,3830, 0, 0, 566, 133, 0, 0 }, + {3831,3831, 32, 0, 146, 33, 0, 0 }, + {3832,3832, 36, 0, 273, 140, 0, 0 }, + {3833,3833, 88, 0, 340, 120, 0, 0 }, + {3834,3834, 0, 0, 9006, 240, 0, 0 }, + {3835,3835, 0, 0, 9206, 246, 0, 0 }, + {3836,3836, 0, 0, 9246, 386, 0, 0 }, + {3837,3837, 0, 0, 9440, 220, 0, 0 }, + {3838,3838, 0, 0, 8900, 133, 0, 0 }, + {3839,3839, 0, 0, 9400, 253, 0, 0 }, + {3840,3840, 0, 0, 4613, 420, 0, 0 }, + {3841,3841, 0, 0, 9233, 426, 0, 0 }, + {3842,3842, 0, 0, 40000, 526, 0, 0 }, + {3843,3843, 0, 0, 40000, 640, 0, 0 }, + {3844,3844, 0, 0, 40000, 666, 0, 0 }, + {3845,3845, 0, 0, 40000, 1053, 0, 0 }, + {3846,3846, 0, 0, 40000, 173, 0, 0 }, + {3847,3847, 0, 0, 40000, 246, 0, 0 }, + {3848,3848, 0, 0, 40000, 226, 0, 0 }, + {3849,3849, 0, 0, 4073, 233, 0, 0 }, + {3850,3850, 0, 0, 14286, 326, 0, 0 }, + {3851,3851, 0, 0, 9233, 146, 0, 0 }, + {3852,3852, 0, 0, 4480, 133, 0, 0 }, + {3853,3853, 0, 0, 40000, 53, 0, 0 }, + {3854,3854, 0, 0, 40000, 126, 0, 0 }, + {3855,3855, 0, 0, 40000, 126, 0, 0 }, + {3856,3856, 0, 0, 18226, 146, 0, 0 }, + {3857,3857, 0, 0, 40000, 326, 0, 0 }, + {3858,3858, 0, 0, 40000, 0, 0, 0 }, + {3859,3859, 0, 0, 40000, 300, 0, 0 }, + {3860,3860, 0, 0, 40000, 0, 0, 0 }, + {3861,3861, 0, 0, 40000, 0, 0, 0 }, + {3862,3862, 0, 0, 40000, 0, 0, 0 }, + {3863,3863, 0, 0, 40000, 140, 0, 0 }, + {3864,3864, 0, 0, 40000, 153, 0, 0 }, + {3865,3865, 0, 0, 40000, 233, 0, 0 }, + {3866,3866, 0, 0, 40000, 186, 0, 0 }, + {3867,3867, 0, 0, 40000, 413, 0, 0 }, + {3868,3868, 0, 0, 40000, 373, 0, 0 }, + {3869,3869, 0, 0, 1246, 440, 0, 0 }, + {3870,3870, 0, 0, 4620, 1513, 0, 0 }, + {3871,3871, 0, 0, 40000, 433, 0, 0 }, + {3872,3872, 0, 0, 40000, 453, 0, 0 }, + {3873,3873, 0, 0, 40000, 1440, 0, 0 }, + {3874,3874, 0, 0, 40000, 480, 0, 0 }, + {3875,3875, 0, 0, 40000, 1360, 0, 0 }, + {3876,3876, 0, 0, 40000, 0, 0, 0 }, + {3877,3877, 0, 0, 40000, 353, 0, 0 }, + {3878,3878, 0, 0, 40000, 86, 0, 0 }, + {3879,3879, 0, 0, 40000, 126, 0, 0 }, + {3880,3880, 0, 0, 40000, 73, 0, 0 }, + {3881,3881, 0, 0, 40000, 80, 0, 0 }, + {3882,3882, 0, 0, 40000, 246, 0, 0 }, + {3883,3883, 0, 0, 40000, 93, 0, 0 }, + {3884,3884, 0, 0, 40000, 120, 0, 0 }, + {3885,3885, 0, 0, 40000, 180, 0, 0 }, + {3886,3886, 0, 0, 40000, 133, 0, 0 }, + {3887,3887, 0, 0, 40000, 133, 0, 0 }, + {3888,3888, 0, 0, 40000, 153, 0, 0 }, + {3889,3889, 0, 0, 40000, 93, 0, 0 }, + {3890,3890, 0, 0, 40000, 140, 0, 0 }, + {3891,3891, 0, 0, 40000, 100, 0, 0 }, + {3892,3892, 0, 0, 40000, 146, 0, 0 }, + {3893,3893, 0, 0, 40000, 126, 0, 0 }, + {3894,3894, 0, 0, 40000, 160, 0, 0 }, + {3895,3895, 0, 0, 40000, 226, 0, 0 }, + {3896,3896, 0, 0, 40000, 140, 0, 0 }, + {3897,3897, 0, 0, 40000, 200, 0, 0 }, + {3898,3898, 0, 0, 40000, 66, 0, 0 }, + {3899,3899, 0, 0, 40000, 446, 0, 0 }, + {3900,3900, 0, 0, 40000, 140, 0, 0 }, + {3901,3901, 0, 0, 40000, 400, 0, 0 }, + {3902,3902, 0, 0, 40000, 373, 0, 0 }, + {3903,3903, 0, 0, 40000, 1306, 0, 0 }, + {3904,3904, 0, 0, 40000, 186, 0, 0 }, + {3905,3905, 0, 0, 40000, 640, 0, 0 }, + {3906,3906, 0, 0, 40000, 346, 0, 0 }, + {3907,3907, 0, 0, 40000, 140, 0, 0 }, + {3908,3908, 0, 0, 40000, 253, 0, 0 }, + {3909,3909, 0, 0, 8980, 746, 0, 0 }, + {3910,3910, 0, 0, 40000, 1266, 0, 0 }, + {3911,3911, 0, 0, 40000, 1306, 0, 0 }, + {3912,3912, 0, 0, 7226, 593, 0, 0 }, + {3913,3913, 0, 0, 40000, 140, 0, 0 }, + {3914,3914, 0, 0, 40000, 220, 0, 0 }, + {3915,3915, 0, 0, 40000, 146, 0, 0 }, + {3916,3916, 0, 0, 4606, 1506, 0, 0 }, + {3917,3917, 0, 0, 40000, 80, 0, 0 }, + {3918,3918, 0, 0, 40000, 0, 0, 0 }, + {3919,3919, 0, 0, 40000, 0, 0, 0 }, + {3920,3920, 0, 0, 613, 226, 0, 0 }, + {3921,3921, 0, 0, 9073, 2946, 0, 0 }, + {3922,3922, 0, 0, 40000, 73, 0, 0 }, + {3923,3923, 0, 0, 3726, 200, 0, 0 }, + {3924,3924, 0, 0, 3680, 373, 0, 0 }, + {3925,3925, 0, 0, 7113, 186, 0, 0 }, + {3926,3926, 0, 0, 2406, 106, 0, 0 }, + {3927,3927, 0, 0, 40000, 0, 0, 0 }, + {3928,3928, 0, 0, 40000, 253, 0, 0 }, + {3929,3929, 0, 0, 40000, 0, 0, 0 }, + {3930,3930, 0, 0, 40000, 80, 0, 0 }, + {3931,3931, 0, 0, 40000, 86, 0, 0 }, + {3932,3932, 0, 0, 18186, 740, 0, 0 }, + {3933,3933, 0, 0, 18426, 813, 0, 0 }, + { 523, 523, 0, 0, 200, 260, 0, 0 }, + {3934,3934, 0, 0, 340, 146, 0, 0 }, + {3935,3935, 0, 0, 366, 260, 0, 0 }, + {3936,3936, 48, 0, 126, 26, 0, 0 }, + {3937,3937, 27, 0, 200, 106, 0, 0 }, + {3938,3938, 40, 0, 1073, 800, 0, 0 }, + {3939,3939, 48, 0, 100, 33, 0, 0 }, + {3938,3938, 45, 0, 933, 666, 0, 0 }, + {3940,3940, 48, 0, 140, 333, 0, 0 }, + {3938,3938, 47, 0, 933, 666, 0, 0 }, + {3941,3941, 48, 0, 1840, 1373, 0, 0 }, + {3938,3938, 49, 0, 953, 686, 0, 0 }, + {3938,3938, 53, 0, 906, 686, 0, 0 }, + {3938,3938, 56, 0, 913, 693, 0, 0 }, + { 129, 129, 52, 0, 293, 126, 0, 0 }, + { 130, 130, 48, 0, 173, 93, 0, 0 }, + { 129, 129, 58, 0, 286, 126, 0, 0 }, + { 132, 132, 47, 0, 173, 100, 0, 0 }, + { 492, 492, 43, 0, 820, 306, 0, 0 }, + { 132, 132, 49, 0, 173, 93, 0, 0 }, + { 132, 132, 51, 0, 173, 93, 0, 0 }, + { 132, 132, 54, 0, 173, 93, 0, 0 }, + { 132, 132, 57, 0, 146, 86, 0, 0 }, + { 492, 492, 72, 0, 706, 300, 0, 0 }, + { 137, 137, 76, 0, 1900, 666, 0, 0 }, + { 138, 138, 84, 0, 740, 300, 0, 0 }, + { 139, 139, 36, 0, 353, 146, 0, 0 }, + { 140, 140, 76, 0, 1886, 680, 0, 0 }, + { 141, 141, 84, 0, 220, 113, 0, 0 }, + { 135, 135, 83, 0, 1353, 480, 0, 0 }, + { 142, 142, 84, 0, 386, 160, 0, 0 }, + {3942,3942, 24, 0, 2313, 780, 0, 0 }, + { 137, 137, 77, 0, 1893, 660, 0, 0 }, + { 144, 144, 60, 0, 213, 126, 0, 0 }, + { 145, 145, 65, 0, 180, 146, 0, 0 }, + { 146, 146, 59, 0, 173, 93, 0, 0 }, + { 147, 147, 51, 0, 266, 213, 0, 0 }, + { 148, 148, 45, 0, 513, 200, 0, 0 }, + { 149, 149, 71, 0, 246, 26, 0, 0 }, + { 150, 150, 60, 0, 500, 193, 0, 0 }, + { 151, 151, 58, 0, 513, 200, 0, 0 }, + { 152, 152, 53, 0, 220, 86, 0, 0 }, + { 153, 153, 64, 0, 113, 40, 0, 0 }, + { 154, 154, 71, 0, 840, 300, 0, 0 }, + { 156, 156, 61, 0, 166, 80, 0, 0 }, + { 158, 158, 48, 0, 173, 213, 0, 0 }, + { 159, 159, 69, 0, 126, 140, 0, 0 }, + { 160, 160, 68, 0, 126, 140, 0, 0 }, + { 161, 161, 63, 0, 326, 113, 0, 0 }, + { 162, 162, 74, 0, 860, 286, 0, 0 }, + { 163, 163, 60, 0, 386, 160, 0, 0 }, + { 164, 164, 80, 0, 1106, 273, 0, 0 }, + { 165, 165, 64, 0, 126, 66, 0, 0 }, + { 166, 166, 69, 0, 386, 80, 0, 0 }, + { 167, 167, 73, 0, 546, 306, 0, 0 }, + { 168, 168, 75, 0, 126, 140, 0, 0 }, + { 169, 169, 68, 0, 340, 320, 0, 0 }, + { 131, 131, 48, 0, 520, 200, 0, 0 }, + {3061,3061, 53, 0, 40000, 0, 0, 0 }, + {3943,3944, 0, 4, 2133, 333, 0, 0 }, + {3945,3946, 0, 4, 8966, 393, 0, 0 }, + { 174,3947, 0, 4, 6946, 320, 0, 0 }, + {3948,3949, 0, 4, 9320, 133, 0, 0 }, + { 9,3950, 0, 4, 1606, 426, 0, 0 }, + {3951,3952, 0, 4, 18373, 240, 0, 0 }, + {3953,3954, 0, 1, 7440, 1100, 0, 0.0625 }, + { 15,3955, 0, 4, 5640, 1986, 0, 0 }, + {3956,3957, 0, 4, 40000, 100, 0, 0 }, + {3958,3959, 0, 4, 40000, 73, 0, 0 }, + {3960,3961, 0, 4, 40000, 73, 0, 0 }, + {3962,3963, 0, 4, 40000, 73, 0, 0 }, + {3964,3965, 0, 4, 18186, 153, 0, 0 }, + {3966,3967, 0, 4, 18453, 153, 0, 0 }, + { 31,3968, 0, 4, 40000, 0, 0, 0 }, + {3969,3970, 0, 4, 17886, 100, 0, 0 }, + {3971,3972, 0, 4, 40000, 66, 0, 0 }, + {3973,3972, 0, 4, 40000, 66, 0, 0 }, + {3974,3975, 0, 4, 40000, 46, 0, 0 }, + {3976,3977, 0, 4, 18553, 106, 0, 0 }, + {3978,3977, 0, 4, 18460, 106, 0, 0 }, + {3979,3980, 0, 4, 9366, 106, 0, 0 }, + {3981,3982, 0, 4, 9073, 226, 0, 0 }, + {3983,3984, 0, 4, 40000, 140, 0, 0 }, + {3985,3986, 0, 4, 40000, 800, 0, 0 }, + { 54,3987, 0, 4, 2513, 706, 0, 0 }, + {3988,3989, 0, 4, 40000, 86, 0, 0 }, + {3990,3990, 0, 0, 40000, 126, 0, 0 }, + {3991,3992, 0, 4, 40000, 233, 0, 0 }, + {3993, 253, 0, 4, 40000, 66, 0, 0 }, + {3994,3995, 0, 4, 40000, 0, 0, 0 }, + {3996,3997, 0, 4, 40000, 126, 0, 0 }, + {3998,3999, 0, 4, 40000, 80, 0, 0 }, + {4000,4001, 0, 4, 40000, 73, 0, 0 }, + {4002,4003, 0, 1, 40000, 86, 0, 0.046875 }, + {4004,4005, 0, 4, 40000, 86, 0, 0 }, + {1503,4006, 0, 4, 40000, 93, 0, 0 }, + { 88,4007, 0, 4, 40000, 1220, 0, 0 }, + {3743,4008, 0, 4, 7646, 1260, 0, 0 }, + { 92,4009, 0, 4, 40000, 186, 0, 0 }, + { 93,4010, 0, 4, 40000, 813, 0, 0 }, + { 94,4011, 0, 4, 7660, 1260, 0, 0 }, + { 96,4012, 0, 4, 40000, 2460, 0, 0 }, + {4013,4014, 0, 4, 40000, 420, 0, 0 }, + { 103,4015, 0, 4, 3673, 1240, 0, 0 }, + {4016,4017, 0, 1, 6286, 380, 0, 0 }, + {4018,4019, 0, 1, 2220, 426, 0, 0.03125 }, + { 107,4020, 0, 4, 2086, 760, 0, 0 }, + {4021,4022, 0, 1, 40000, 100, 0, 0.0625 }, + { 110,4023, 0, 4, 40000, 100, 0, 0 }, + { 111,4024, 0, 4, 2300, 820, 0, 0 }, + {4025,4026, 0, 4, 1013, 326, 0, 0 }, + {4027,4028, 0, 1, 1220, 393, 0, 0.03125 }, + { 115,4029, 0, 4, 1813, 646, 0, 0 }, + {4030,4031, 0, 1, 566, 146, 0, 0 }, + { 118,4032, 0, 4, 1553, 53, 0, 0 }, + {4033,4033, 0, 0, 613, 60, 0, 0 }, + { 120,4034, 0, 4, 2126, 1166, 0, 0 }, + {4035,4036, 0, 1, 11880, 2993, 0, 0 }, + { 123,4037, 0, 4, 7080, 2473, 0, 0 }, + { 124,4038, 0, 4, 40000, 1126, 0, 0 }, + { 125,4039, 0, 4, 40000, 1546, 0, 0 }, + {4040,4040, 34, 0, 133, 40, 0, 0 }, + {4041,4041, 28, 0, 193, 46, 0, 0 }, + {4042,4043, 39, 1, 553, 126, 0, 0 }, + {4042,4043, 33, 1, 553, 126, 0, 0 }, + {4044,4045, 63, 4, 166, 93, 0, 0 }, + {4046,4046, 15, 0, 113, 66, 0, 0 }, + {4047,4047, 36, 0, 106, 53, 0, 0 }, + {4047,4048, 36, 1, 480, 173, 0, 0.40625 }, + {4049,4049, 35, 0, 706, 266, 0, 0 }, + {4050,4051, 38, 1, 273, 106, 0, 0 }, + {4052,4053, 38, 1, 366, 133, 0, 0 }, + {4054,4055, 48, 1, 280, 133, 0, -1.90625 }, + {4056,4056, 48, 0, 180, 86, 0, 0 }, + {4057,4058, 48, 1, 953, 346, 0, -1.90625 }, + {4059,4059, 61, 1, 3200, 540, 0, 0.09375 }, + {3369,1557, 70, 4, 766, 306, 0, 0 }, + {4060,4061, 79, 1, 1306, 513, 0, 0.078125 }, + {4062,4062, 62, 0, 5200, 466, 0, 0 }, + {4063,4064, 67, 1, 2153, 1080, 0, 0.078125 }, + {4065,4065, 62, 1, 3226, 573, 0, 0.09375 }, + {4066,4067, 54, 1, 286, 133, 0, 0 }, + {4066,4068, 48, 1, 286, 126, 0, 0 }, + {1589,1589, 71, 0, 106, 46, 0, 0 }, + { 389, 389, 42, 0, 266, 73, 0, 0 }, + {4069,4070, 60, 1, 120, 80, 0, 0 }, + {4070,4071, 60, 1, 380, 80, 0, 0 }, + {4072,4072, 73, 0, 166, 33, 0, 0 }, + {4073,4074, 68, 1, 153, 40, 0, 0 }, + {4075,4076, 18, 1, 200, 80, 0, 0 }, + {4077,4078, 18, 1, 253, 73, 0, 0 }, + {4079,4080, 64, 4, 1346, 33, 0, 0 }, + {4081,4082, 64, 1, 373, 73, 0, 0.03125 }, + {4083,4083, 67, 0, 106, 26, 0, 0 }, + { 844, 844,244, 2, 6, 0, 0, 0 }, + { 855, 855,244, 2, 6, 0, 0, 0 }, + { 880, 880,232, 0, 253, 80, 0, 0 }, + { 882, 882,220, 0, 40000, 266, 0, 0 }, + { 887, 887, 35, 0, 133, 46, 0, 0 }, + { 884, 884, 35, 0, 233, 80, 0, 0 }, + { 885, 885, 35, 0, 226, 86, 0, 0 }, + { 886, 886, 35, 0, 113, 53, 0, 0 }, + { 361, 361, 35, 0, 286, 73, 0, 0 }, + { 767, 767, 35, 0, 3020, 786, 0, 0 }, + { 888, 888, 35, 0, 246, 53, 0, 0 }, + {2141,2141, 35, 0, 186, 73, 0, 0 }, + { 891, 891, 35, 0, 713, 266, 0, 0 }, + {2142,2142, 35, 0, 200, 100, 0, 0 }, + {2143,2143, 35, 0, 220, 80, 0, 0 }, + {2144,2144, 35, 0, 2393, 100, 0, 0 }, + {2145,2145, 35, 0, 1980, 813, 0, 0 }, + { 376, 376, 35, 0, 1880, 840, 0, 0 }, + { 895, 895, 35, 0, 366, 140, 0, 0 }, + {2146,2146, 35, 0, 346, 106, 0, 0 }, + { 382, 382, 35, 0, 1073, 113, 0, 0 }, + {2147,2147, 35, 0, 106, 80, 0, 0 }, + { 898, 898, 35, 0, 206, 153, 0, 0 }, + { 899, 899, 35, 0, 633, 240, 0, 0 }, + { 900, 900, 35, 0, 620, 240, 0, 0 }, + { 871, 871, 35, 0, 380, 73, 0, 0 }, + { 388, 388, 35, 0, 286, 80, 0, 0 }, + { 901, 901, 35, 0, 260, 26, 0, 0 }, + { 902, 902, 35, 0, 1093, 73, 0, 0 }, + { 903, 903, 35, 0, 126, 73, 0, 0 }, + {3500,3500, 35, 2, 6, 0, 0, 0 }, + {4084,4084, 0, 0, 14166, 320, 0, 0 }, + {4085,4085, 0, 0, 7413, 653, 0, 0 }, + {4086,4086, 0, 0, 40000, 146, 0, 0 }, + {4087,4087, 0, 0, 40000, 113, 0, 0 }, + {4088,4088, 0, 0, 16773, 193, 0, 0 }, + {4089,4089, 0, 0, 40000, 73, 0, 0 }, + {4090,4090, 0, 0, 40000, 0, 0, 0 }, + {4091,4091, 0, 0, 966, 373, 0, 0 }, + {4092,4092, 0, 0, 40000, 80, 0, 0 }, + {4093,4093, 0, 0, 40000, 80, 0, 0 }, + {4094,4094, 0, 0, 18473, 93, 0, 0 }, + {4095,4095, 0, 0, 40000, 60, 0, 0 }, + {4096,4096, 0, 0, 40000, 73, 0, 0 }, + {4097,4097, 0, 0, 40000, 0, 0, 0 }, + {4098,4098, 0, 0, 40000, 213, 0, 0 }, + {4099,4099, 0, 0, 40000, 66, 0, 0 }, + {4100,4100, 0, 0, 1413, 1026, 0, 0 }, + {4101,4101, 0, 0, 506, 200, 0, 0 }, + {4102,4102, 0, 0, 3793, 1106, 0, 0 }, + {4103,4103, 0, 0, 40000, 220, 0, 0 }, + {4104,4104, 0, 0, 40000, 46, 0, 0 }, + {4105,4105, 0, 0, 40000, 0, 0, 0 }, + {4106,4106, 0, 0, 40000, 60, 0, 0 }, + {4107,4107, 0, 0, 40000, 0, 0, 0 }, + {4108,4108, 0, 0, 40000, 33, 0, 0 }, + {4109,4109, 0, 0, 40000, 0, 0, 0 }, + {4110,4110, 0, 0, 40000, 146, 0, 0 }, + {4111,4111, 0, 0, 40000, 66, 0, 0 }, + {4112,4112, 0, 0, 40000, 353, 0, 0 }, + {4113,4113, 0, 0, 40000, 66, 0, 0 }, + {4114,4114, 0, 0, 40000, 53, 0, 0 }, + {4115,4115, 0, 0, 40000, 73, 0, 0 }, + {4116,4116, 0, 0, 40000, 66, 0, 0 }, + {4117,4117, 0, 0, 40000, 926, 0, 0 }, + {4118,4118, 0, 0, 2833, 200, 0, 0 }, + { 127, 127, 36, 0, 386, 166, 0, 0 }, + {4119,4119, 36, 0, 100, 33, 0, 0 }, + {2030,2030, 36, 0, 346, 140, 0, 0 }, + {3782,3782, 48, 0, 93, 0, 0, 0 }, + {3783,3783, 36, 0, 146, 86, 0, 0 }, + {4120,4120, 48, 0, 1886, 653, 0, 0 }, + { 132, 132, 69, 0, 126, 66, 0, 0 }, + {4120,4120, 52, 0, 1853, 626, 0, 0 }, + { 152, 152, 48, 0, 220, 86, 0, 0 }, + {4120,4120, 55, 0, 1886, 640, 0, 0 }, + { 139, 139, 57, 0, 293, 133, 0, 0 }, + {4120,4120, 58, 0, 1860, 633, 0, 0 }, + {4120,4120, 60, 0, 1886, 633, 0, 0 }, + {4121,4121, 62, 0, 2660, 900, 0, 0 }, + {4120,4120, 63, 0, 1880, 646, 0, 0 }, + { 134, 134, 70, 0, 966, 360, 0, 0 }, + {4122,4122, 70, 0, 973, 346, 0, 0 }, + {4123,4123, 53, 0, 1866, 640, 0, 0 }, + {3516,3516, 48, 0, 180, 93, 0, 0 }, + {4124,4124, 84, 0, 1360, 473, 0, 0 }, + {4125,4125, 43, 0, 513, 206, 0, 0 }, + {4126,4126, 56, 0, 1353, 480, 0, 0 }, + {3791,3791, 24, 0, 1866, 613, 0, 0 }, + { 134, 134, 65, 0, 1346, 486, 0, 0 }, + { 146, 146, 48, 0, 173, 93, 0, 0 }, + { 146, 146, 54, 0, 173, 93, 0, 0 }, + {4127,4127, 42, 0, 246, 140, 0, 0 }, + {4127,4127, 39, 0, 240, 133, 0, 0 }, + {3816,3816, 52, 0, 306, 113, 0, 0 }, + {4128,4128, 52, 0, 413, 86, 0, 0 }, + { 158, 158, 60, 0, 146, 166, 0, 0 }, + { 158, 158, 66, 0, 146, 166, 0, 0 }, + { 158, 158, 59, 0, 146, 166, 0, 0 }, + {3538,3538, 91, 0, 773, 233, 0, 0 }, + {3547,3547,109, 0, 5300, 1786, 0, 0 }, + {4129,4129, 79, 0, 560, 313, 0, 0 }, + {4130,4130, 0, 0, 10646, 73, 0, 0 }, + {4131,4132, 0, 1, 14166, 586, 0, 0.03125 }, + {4133,4134, 0, 1, 15553, 546, 0, 0.03125 }, + {4135,4136, 0, 1, 11746, 320, 0, 0.046875 }, + {4137,4138, 0, 1, 14706, 646, 0, 0.15625 }, + {4139,4140, 0, 1, 7320, 100, 0, 0.046875 }, + {4141,4142, 0, 1, 40000, 0, 0, 0.0625 }, + {4143,4144, 0, 1, 13660, 260, 0, 0 }, + {4145,4146, 0, 1, 15026, 133, 0, 0 }, + {4147,4148, 0, 1, 40000, 0, 0, 2.5e-05 }, + {4149,4150, 0, 1, 4980, 3400, 0, 0 }, + {4151,4152, 0, 1, 7840, 2660, 0, 0.046875 }, + {4153,4154, 0, 1, 8326, 180, 0, 0 }, + {4155,4156, 0, 1, 1093, 140, 0, 0 }, + {4157,4158, 0, 1, 2280, 400, 0, 0 }, + {4159,4160, 0, 1, 4553, 1486, 0, 0.03125 }, + {4161,4161, 0, 1, 40000, 0, 0, 0.03125 }, + {4162,4163, 0, 1, 40000, 60, 0, 0.15625 }, + {4164,4165, 0, 1, 40000, 93, 0, 0.078125 }, + {4166,4167, 0, 1, 40000, 86, 0, 0.15625 }, + {4168,4169, 0, 1, 40000, 520, 0, 0.03125 }, + {4170,4171, 0, 1, 40000, 140, 0, 0.0625 }, + {4172,4173, 0, 1, 40000, 133, 0, 0.140625 }, + {4174,4175, 0, 1, 40000, 73, 0, 0 }, + {4176,4177, 0, 1, 40000, 346, 0, 0.109375 }, + {4178,4179, 0, 1, 3693, 86, 0, 0 }, + {4180,4181, 0, 1, 6586, 460, 0, 2.5e-05 }, + {4182,4183, 0, 1, 4320, 93, 0, 0 }, + {4184,4185, 0, 1, 7346, 126, 0, 0.046875 }, + {4186,4187, 0, 1, 3633, 260, 0, 0 }, + {4188,4189, 0, 1, 40000, 126, 0, -1.95312 }, + {4190,4191, 0, 1, 40000, 126, 0, -1.9375 }, + {4192,4193, 0, 1, 40000, 46, 0, 0.234375 }, + {4194,4195, 0, 1, 40000, 0, 0, 0.03125 }, + {4196,4197, 0, 1, 10320, 86, 0, 0 }, + {4198,4199, 0, 1, 12933, 133, 0, 0 }, + {4200,4201, 0, 1, 11820, 240, 0, 0.046875 }, + {4202,4203, 0, 1, 3966, 166, 0, 0 }, + {4204,4205, 0, 1, 40000, 0, 0, 0 }, + {4206,4206, 0, 0, 2666, 160, 0, 0 }, + {4207,4208, 0, 1, 15046, 93, 0, 0.078125 }, + {4209,4210, 0, 1, 40000, 100, 0, 0 }, + {4211,4211, 0, 0, 40000, 260, 0, 0 }, + {4212,4213, 0, 1, 40000, 126, 0, 2.5e-05 }, + {4214,4214, 0, 0, 40000, 233, 0, 0 }, + {4215,4216, 0, 1, 40000, 440, 0, 0.078125 }, + {4217,4218, 0, 1, 2160, 606, 0, 0.109375 }, + {4219,4220, 0, 1, 14753, 2400, 0, 0.03125 }, + {4221,4222, 0, 1, 7680, 646, 0, 0.03125 }, + {4223,4224, 0, 1, 40000, 446, 0, 0.0625 }, + {4225,4226, 0, 1, 40000, 866, 0, -0.0625 }, + {4227,4227, 0, 1, 40000, 1220, 0, 0.078125 }, + {4228,4228, 0, 1, 40000, 1960, 0, 0.0625 }, + {4229,4230, 0, 1, 40000, 433, 0, 0.125 }, + {4231,4232, 0, 1, 40000, 140, 0, 0.140625 }, + {4233,4234, 0, 1, 40000, 806, 0, 0.109375 }, + {4235,4236, 0, 1, 2040, 486, 0, 0.125 }, + {4237,4238, 0, 1, 40000, 86, 0, 0 }, + {4239,4240, 0, 1, 40000, 80, 0, 0.03125 }, + {4241,4241, 0, 0, 40000, 73, 0, 0 }, + {4242,4243, 0, 1, 40000, 400, 0, 0.0625 }, + {4244,4245, 0, 1, 40000, 120, 0, 0.0625 }, + {4246,4247, 0, 1, 40000, 0, 0, 0.09375 }, + {4248,4248, 0, 1, 40000, 0, 0, 0.125 }, + {4249,4250, 0, 1, 40000, 186, 0, 0 }, + {4251,4251, 0, 0, 40000, 166, 0, 0 }, + {4252,4252, 0, 0, 40000, 73, 0, 0 }, + {4253,4253, 0, 0, 40000, 60, 0, 0 }, + {4254,4255, 0, 1, 40000, 140, 0, 0 }, + {4256,4256, 0, 0, 40000, 140, 0, 0 }, + {4257,4257, 0, 0, 40000, 66, 0, 0 }, + {4258,4259, 0, 1, 40000, 133, 0, 0 }, + {4260,4260, 0, 0, 40000, 86, 0, 0 }, + {4261,4261, 0, 0, 40000, 73, 0, 0 }, + {4262,4262, 0, 0, 40000, 106, 0, 0 }, + {4263,4264, 0, 1, 40000, 186, 0, 0.03125 }, + {4265,4266, 0, 1, 40000, 0, 0, 0.03125 }, + {4267,4267, 0, 0, 40000, 300, 0, 0 }, + {4268,4268, 0, 0, 40000, 66, 0, 0 }, + {4269,4270, 0, 1, 40000, 73, 0, 0.125 }, + {4271,4272, 0, 1, 40000, 86, 0, 0.109375 }, + {4273,4274, 0, 1, 40000, 146, 0, 0.109375 }, + {4275,4276, 0, 1, 40000, 66, 0, -0.03125 }, + {4277,4277, 0, 0, 40000, 60, 0, 0 }, + {4278,4279, 0, 1, 40000, 213, 0, 0.15625 }, + {4280,4281, 0, 1, 40000, 66, 0, 0.125 }, + {4282,4283, 0, 1, 40000, 100, 0, 0.03125 }, + {4284,4285, 0, 1, 40000, 1513, 0, 0.078125 }, + {4286,4287, 0, 1, 40000, 353, 0, 0.109375 }, + {4288,4289, 0, 1, 40000, 133, 0, 0.078125 }, + {4290,4291, 0, 1, 40000, 746, 0, 0.140625 }, + {4292,4293, 0, 1, 40000, 0, 0, 0.109375 }, + {4294,4295, 0, 1, 5033, 1606, 0, 0.0625 }, + {4296,4297, 0, 1, 40000, 1146, 0, 0.09375 }, + {4298,4299, 0, 1, 40000, 1586, 0, 0.109375 }, + {4300,4301, 0, 1, 40000, 0, 0, 0.09375 }, + {4302,4303, 0, 1, 40000, 1006, 0, 0.125 }, + {4304,4304, 0, 1, 2680, 793, 0, 0.109375 }, + {4305,4306, 0, 1, 40000, 0, 0, -0.046875 }, + {4307,4308, 0, 1, 9000, 3186, 0, 0.125 }, + {4309,4310, 0, 1, 40000, 1073, 0, -0.078125 }, + {4311,4312, 0, 1, 40000, 2093, 0, 0.140625 }, + {4313,4314, 0, 1, 40000, 0, 0, 0.078125 }, + {4315,4316, 0, 1, 9580, 713, 0, 0.03125 }, + {4317,4317, 0, 0, 1166, 760, 0, 0 }, + {4318,4319, 0, 1, 1186, 240, 0, 0 }, + {4320,4320, 0, 0, 40000, 160, 0, 0 }, + {4321,4321, 0, 0, 40000, 120, 0, 0 }, + {4322,4322, 0, 0, 8673, 2413, 0, 0 }, + {4323,4323, 0, 0, 393, 126, 0, 0 }, + {4324,4324, 0, 0, 246, 93, 0, 0 }, + {4325,4326, 0, 1, 1953, 393, 0, 0 }, + {4327,4328, 0, 1, 4220, 133, 0, 0 }, + {4329,4330, 0, 1, 2873, 73, 0, 0.109375 }, + {4331,4332, 0, 1, 40000, 186, 0, 0 }, + {4333,4333, 0, 0, 1573, 86, 0, 0 }, + {4334,4335, 0, 1, 40000, 793, 0, 0 }, + {4336,4337, 0, 1, 40000, 173, 0, 0 }, + {4338,4339, 0, 1, 40000, 793, 0, 0 }, + {4340,4340, 0, 0, 606, 133, 0, 0 }, + {4044,4341, 63, 1, 160, 80, 0, 0 }, + {4342,4343, 25, 1, 313, 153, 0, 0 }, + {4344,4343, 25, 1, 206, 100, 0, 0 }, + {4345,4346, 61, 1, 153, 93, 0, 0 }, + {4347,4348, 38, 1, 340, 133, 0, 0 }, + {4349,4350, 37, 1, 206, 93, 0, 0 }, + {4351,4352, 15, 1, 346, 153, 10, 0 }, + {4353,4354,100, 1, 146, 80, 0, 0.140625 }, + {4355,4356, 19, 1, 553, 200, 10, 0 }, + {4357,4358, 15, 1, 333, 153, 20, 0 }, + {4359,4360, 12, 1, 340, 146, 20, 0 }, + {4361,4362, 11, 1, 346, 146, 20, 0 }, + {4363,4364, 61, 1, 2706, 1033, 0, 0.09375 }, + {4365,4362, 8, 1, 340, 146, 20, 0 }, + {4366,4367, 91, 1, 1166, 366, 0, -0.046875 }, + {4368,4368, 70, 0, 966, 346, 0, 0 }, + {4369,4370, 80, 1, 300, 93, 0, 0.125 }, + {4371,4371, 58, 0, 206, 53, 0, 0 }, + {4372,4364, 62, 1, 2333, 820, 0, 0.09375 }, + {4373,4374, 31, 1, 773, 200, 0, 0 }, + {4375,4367, 91, 1, 1160, 360, 0, -0.03125 }, + {4376,4377, 41, 1, 373, 113, 0, 0 }, + {4378,4379, 35, 1, 406, 126, 0, 0 }, + {4380,4381, 29, 1, 146, 106, 0, 0 }, + {4382,4383, 41, 1, 400, 126, 0, 0 }, + {4382,4383, 37, 1, 400, 126, 0, 0 }, + {4384,4385, 77, 1, 193, 93, 0, 0 }, + {4386,4387, 72, 1, 200, 93, 0, 0 }, + {4388,4388, 40, 0, 513, 0, 0, 0 }, + {4389,4389, 38, 0, 200, 20, 0, 0 }, + {4390,4390, 36, 0, 620, 20, 0, 0 }, + {4391,4391, 90, 0, 193, 20, 0, 0 }, + {4392,4392, 90, 0, 793, 40, 0, 0 }, + {4393,4394, 80, 1, 406, 153, 0, 0.03125 }, + {4395,4396, 64, 1, 1866, 606, 0, 0 }, + {4397,4398, 50, 1, 173, 126, 0, 0 }, + {4399,4399, 36, 0, 4646, 1606, 0, 0 }, + {4400,4400, 0, 0, 40000, 86, 0, 0 }, + {4401,4401, 0, 0, 40000, 73, 0, 0 }, + {4402,4402, 0, 0, 2433, 700, 0, 0 }, + {4403,4403, 0, 0, 1233, 26, 0, 0 }, + {4404,4404, 0, 0, 40000, 66, 0, 0 }, + {4405,4405, 0, 0, 40000, 60, 0, 0 }, + {4406,4406, 0, 0, 40000, 60, 0, 0 }, + {4407,4407, 0, 0, 40000, 66, 0, 0 }, + {4408,4408, 0, 0, 40000, 66, 0, 0 }, + {4409,4409, 0, 0, 40000, 0, 0, 0 }, + {4409,4409, 73, 0, 40000, 0, 0, 0 }, + {4410,4410, 0, 0, 40000, 60, 0, 0 }, + {4411,4411, 0, 0, 40000, 60, 0, 0 }, + {4412,4412, 0, 0, 7326, 2486, 0, 0 }, + {4413,4413, 0, 0, 4886, 1586, 0, 0 }, + {4414,4414, 0, 0, 646, 20, 0, 0 }, + {4415,4415, 0, 0, 253, 20, 0, 0 }, + {4415,4415, 12, 0, 253, 20, 0, 0 }, + {4416,4416, 0, 0, 640, 100, 0, 0 }, + {4416,4416, 1, 0, 640, 106, 0, 0 }, + {4417,4417, 0, 0, 133, 106, 0, 0 }, + {4417,4417, 23, 0, 133, 106, 0, 0 }, + {4418,4418, 0, 0, 653, 100, 0, 0 }, + {4419,4419, 0, 0, 4166, 1546, 0, 0 }, + {4420,4420, 0, 0, 40000, 73, 0, 0 }, + {4421,4421, 0, 0, 40000, 60, 0, 0 }, + {4422,4422, 0, 0, 40000, 53, 0, 0 }, + {4423,4423, 0, 0, 40000, 0, 0, 0 }, + {4424,4424, 0, 0, 246, 20, 0, 0 }, + {4425,4425, 0, 2, 6, 0, 0, 0 }, + {4426,4426, 0, 0, 4946, 233, 0, 0 }, + {4427,4427, 0, 0, 4946, 233, 0, 0 }, + {4428,4428, 0, 0, 4953, 240, 0, 0 }, + {4429,4429, 0, 0, 4946, 233, 0, 0 }, + {4430,4430, 0, 0, 18233, 46, 0, 0 }, + {4431,4431, 0, 0, 2386, 26, 0, 0 }, + {4432,4432, 0, 0, 4640, 633, 0, 0 }, + {4433,4433, 0, 0, 18466, 100, 0, 0 }, + {4434,4434, 0, 0, 18440, 66, 0, -2 }, + {4435,4435, 0, 0, 18440, 6140, 0, -2 }, + {4436,4436, 0, 0, 1206, 433, 0, -2 }, + {4437,4437, 0, 0, 4626, 240, 0, 0 }, + {4438,4438, 0, 0, 726, 400, 0, 0 }, + {4439,4439, 0, 0, 5866, 73, 0, 0 }, + {4440,4440, 0, 0, 40000, 73, 0, 0 }, + {4441,4441, 0, 0, 40000, 73, 0, 0 }, + {4442,4442, 0, 0, 40000, 73, 0, 0 }, + {4443,4443, 0, 0, 40000, 73, 0, 0 }, + {4444,4444, 0, 0, 6500, 346, 0, 0 }, + {4445,4445, 0, 0, 6506, 346, 0, 0 }, + {4446,4446, 0, 0, 40000, 66, 0, -2 }, + {4447,4447, 0, 0, 40000, 66, 0, -2 }, + {4448,4448, 0, 0, 40000, 0, 0, 0 }, + {4449,4449, 0, 0, 40000, 46, 0, 0 }, + {4450,4450, 0, 0, 40000, 0, 0, 0 }, + {4450,4450, 0, 0, 40000, 0, 0, -2 }, + {4451,4451, 0, 0, 2386, 26, 0, 0 }, + {4452,4452, 0, 0, 40000, 73, 0, -2 }, + {4453,4453, 0, 0, 5866, 26, 0, -2 }, + {4454,4454, 0, 0, 40000, 133, 0, 0 }, + {4455,4455, 0, 0, 40000, 133, 0, 0 }, + {4456,4456, 0, 0, 40000, 126, 0, 0 }, + {4457,4457, 0, 0, 253, 20, 0, 0 }, + {4458,4458, 0, 0, 8866, 1366, 0, 0 }, + {4459,4459, 0, 0, 1040, 766, 0, 0 }, + {4460,4460, 0, 0, 40000, 146, 0, -2 }, + {4461,4461, 0, 0, 40000, 153, 0, -2 }, + {4462,4462, 0, 0, 40000, 466, 0, -2 }, + {4463,4463, 0, 0, 40000, 66, 0, 0 }, + {4464,4464, 0, 0, 2333, 566, 0, 0 }, + {4465,4465, 0, 0, 40000, 140, 0, -2 }, + {4466,4466, 0, 0, 40000, 100, 0, -2 }, + {4467,4467, 0, 0, 40000, 226, 0, -2 }, + {4468,4468, 0, 0, 40000, 0, 0, 0 }, + {3712,3712, 0, 0, 40000, 226, 0, -2 }, + {4469,4469, 0, 0, 40000, 140, 0, -2 }, + {4470,4470, 0, 0, 40000, 66, 0, 0 }, + {4471,4471, 0, 0, 40000, 73, 0, 0 }, + {4472,4472, 0, 0, 40000, 73, 0, 0 }, + {4473,4473, 0, 0, 40000, 86, 0, -2 }, + {4474,4474, 0, 0, 40000, 80, 0, 0 }, + {4475,4475, 0, 0, 40000, 73, 0, -2 }, + {4476,4476, 0, 0, 40000, 80, 0, -2 }, + {4477,4477, 0, 0, 40000, 73, 0, -2 }, + {4478,4478, 0, 0, 40000, 73, 0, 0 }, + {4479,4479, 0, 0, 40000, 73, 0, 0 }, + {4480,4480, 0, 0, 40000, 93, 0, 0 }, + {4481,4481, 0, 0, 40000, 73, 0, 0 }, + {4482,4482, 0, 0, 11946, 13, 0, 0 }, + {4483,4483, 0, 0, 40000, 73, 0, 0 }, + {4453,4453, 0, 0, 5866, 26, 0, 0 }, + {4484,4484, 0, 0, 40000, 820, 0, 0 }, + {4485,4485, 0, 0, 2153, 873, 0, 0 }, + {1221,1221, 0, 0, 40000, 293, 0, 0.171875 }, + {4486,4486, 0, 0, 1620, 120, 0, 0 }, + {4487,4487, 0, 0, 15120, 93, 0, 0 }, + {4488,4488, 0, 0, 14613, 93, 0, 0 }, + {4489,4489, 0, 0, 2346, 793, 0, 0 }, + {4490,4490, 0, 0, 40000, 2380, 0, 0 }, + {4491,4491, 0, 0, 40000, 1280, 0, 0 }, + {4492,4492, 0, 0, 40000, 1460, 0, 0 }, + {4493,4493, 0, 0, 40000, 2513, 0, 0 }, + {4494,4494, 0, 0, 14840, 1266, 0, 0 }, + {4495,4495, 0, 0, 4513, 640, 0, 0 }, + {4496,4496, 0, 0, 4680, 806, 0, 0 }, + {4497,4497, 0, 0, 40000, 100, 0, 0 }, + {4498,4498, 0, 0, 40000, 66, 0, 0 }, + {4499,4499, 0, 0, 2420, 413, 0, 0 }, + {4500,4500, 0, 0, 406, 73, 0, -2 }, + {4501,4501, 0, 0, 1166, 400, 0, 0 }, + {4502,4502, 0, 0, 1213, 106, 0, 0 }, + {4503,4503, 0, 0, 273, 60, 0, -2 }, + {4504,4504, 0, 0, 40000, 2380, 0, 0 }, + {4505,4505, 0, 0, 40000, 440, 0, 0 }, + {1261,1261, 0, 0, 40000, 2960, 0, 0 }, + {4506,4506, 37, 0, 973, 73, 0, -2 }, + {4507,4507, 48, 0, 106, 26, 0, -2 }, + {4508,4508, 48, 0, 286, 133, 0, -2 }, + {4509,4509, 62, 0, 166, 60, 0, 0 }, + {4510,4510, 44, 0, 980, 360, 0, 0 }, + {4511,4511, 80, 0, 100, 33, 0, 0 }, + {4510,4510, 50, 0, 980, 346, 0, 0 }, + {4512,4512, 48, 0, 106, 46, 0, -2 }, + {4510,4510, 55, 0, 973, 360, 0, 0 }, + {4513,4513, 61, 0, 513, 20, 0, 0 }, + {4510,4510, 58, 0, 966, 353, 0, 0 }, + {4510,4510, 63, 0, 973, 353, 0, 0 }, + {4514,4514, 71, 0, 1366, 580, 0, 0 }, + {4510,4510, 72, 0, 820, 306, 0, 0 }, + {4515,4515, 70, 0, 1886, 666, 0, 0 }, + {4514,4514, 88, 0, 1353, 560, 0, 0 }, + {4516,4516, 76, 0, 1873, 653, 0, 0 }, + {4517,4517, 84, 0, 260, 113, 0, 0 }, + {4514,4514, 68, 0, 1366, 553, 0, 0 }, + {4518,4518, 72, 0, 153, 53, 0, 0 }, + {4519,4519, 28, 0, 1193, 413, 0, 0 }, + {4515,4515, 81, 0, 1353, 480, 0, 0 }, + {4520,4520, 58, 0, 246, 120, 0, -2 }, + {4520,4520, 55, 0, 246, 120, 0, -2 }, + {4520,4520, 44, 0, 246, 120, 0, -2 }, + {4520,4520, 49, 0, 246, 120, 0, -2 }, + {4520,4520, 40, 0, 286, 133, 0, -2 }, + {4521,4521, 55, 0, 740, 560, 0, -2 }, + {4521,4521, 48, 0, 893, 693, 0, -2 }, + {4522,4522, 52, 0, 513, 206, 0, 0 }, + {4522,4522, 45, 0, 513, 206, 0, 0 }, + {4523,4523, 48, 0, 173, 100, 0, -2 }, + {4524,4524, 48, 0, 120, 266, 0, -2 }, + {4525,4525, 48, 0, 253, 60, 0, -2 }, + {4500,4500, 73, 0, 160, 20, 0, -2 }, + {4500,4500, 68, 0, 160, 20, 0, -2 }, + {4500,4500, 63, 0, 193, 20, 0, -2 }, + {4526,4526,108, 0, 406, 26, 0, 0 }, + {4527,4527,108, 0, 740, 26, 0, 0 }, }; //Returns total number of generated banks int maxAdlBanks() -{ return 75; +{ + return 75; } -const char* const banknames[75] = +const char* const banknames[76] = { "AIL (Star Control 3, Albion, Empire 2, etc.)", "Bisqwit (selection of 4op and 2op)", @@ -9437,8 +9303,8 @@ const char* const banknames[75] = "TMB (Shadow Warrior)", "DMX (Raptor)", "OP3 (Modded GMOPL by Wohlstand)", - "SB (Jammey O'Connel's bank)", - "TMB (Default bank of Apgee Sound System)", + "SB (Jamie O'Connell's bank)", + "TMB (Default bank of Apogee Sound System)", "WOPL (4op bank by James Alan Nguyen and Wohlstand)", "TMB (Blood)", "TMB (Lee)", @@ -9446,6 +9312,7 @@ const char* const banknames[75] = "WOPL (DMXOPL3 bank by Sneakernets)", "EA (Cartooners)", "WOPL (Apogee IMF 90-ish)", + NULL }; const unsigned short banks[75][256] = { @@ -10404,68 +10271,68 @@ const unsigned short banks[75][256] = 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, }, { -3098,3205,3101,3129,3130,3131,3108,3132,3209,3210,3120,3133,3124,3134,3103,3135, -3136,3137,3113,3138,3139,3140,3141,3142,3143,3144,3145,3206,3121,3207,3208,3146, -3127,3147,3148,3211,3212,3149,3150,3151,3116,3106,3152,3110,3111,3213,3104,3126, -3105,3117,3153,3154,3102,3214,3155,3119,3215,3216,3115,3109,3217,3114,3112,3218, -3156,3219,3157,3158,3159,3107,3122,3128,3160,3222,3161,3162,3163,3164,3165,3166, -3167,3168,3169,3170,3171,3172,3173,3174,3223,3175,3176,3177,3118,3221,3220,3178, -3226,3241,3179,3180,3242,3181,3182,3183,3184,3185,3186,3187,3125,3188,3189,3190, -3191,3192,3193,3194,3123,3195,3196,3197,3198,3224,3199,3200,3201,3202,3203,3204, +1551,1552,1553,3101, 181, 182, 183,3102, 185,1556,1557,3103,1559, 188, 189,1560, +1561,1562, 193,1564,1565, 195, 196, 197, 198, 199, 200, 201, 202,1566,1567, 205, + 206, 207, 208,1568,1569,1570,1571, 211,1572,1573, 214, 215, 216, 217, 218, 219, + 220, 221,1574,1575,1576,1577,1578, 225,1579,1580,3100,1581,1582, 229, 230, 231, + 232, 233, 234, 235,1583, 237, 238, 239,1584,1585, 240,1586, 242, 243, 244,1587, + 246, 247,1588,1589, 249, 250, 251,1590, 252, 253, 254, 255,1591,1592,1593,1594, +1595, 259,1596,1597,1598,1599,1600,1601, 264, 265, 266, 267, 268, 269, 270, 271, +1602, 272, 273,1603,1604,1605, 276,1606,1607, 295, 295,1610,3104,3105, 295, 284, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, - 295, 295, 295, 127,3227,3244,3248,3268,3245,3243,3246,3227,3247,3227,3249,3227, -3227,3269,3227,3228,3270,3229,3250,3251,3100,3271,3258,3230,3252,3253,3237,3239, -3259,3254,3255,3256,3260,3235,3261,3257,3099,3265,3266,3232,3233,3233,3267,3262, -3263,3264,3236,3225,3231,3234,3238,3240, 295, 295, 295, 295, 295, 295, 295, 295, + 295, 295, 295, 127,3107,3122,3126, 316,3123,3121,3124,3107,3125,3107,3127,3107, +3107, 329,3107,3108,3143,3109,3128,3129,3099,3144,1634,3110,3130,3131,3117,3119, +3136,3132,3133,3134,3137,3115,3138,3135,3098, 343, 344,3112,3113,3113,3142,3139, +3140,3141,3116,3106,3111,3114,3118,3120, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, }, { -3098,3205,3101,3129,3130,3131,3108,3132,3209,3210,3120,3133,3124,3134,3103,3135, -3136,3137,3113,3138,3139,3140,3141,3142,3143,3144,3145,3206,3121,3207,3208,3146, -3127,3147,3148,3211,3212,3149,3150,3151,3116,3106,3152,3110,3111,3213,3104,3126, -3105,3117,3153,3154,3102,3214,3155,3119,3215,3216,3115,3109,3217,3114,3112,3218, -3156,3219,3157,3158,3159,3107,3122,3128,3160,3222,3161,3162,3163,3164,3165,3166, -3167,3168,3169,3170,3171,3172,3173,3174,3223,3175,3176,3177,3118,3221,3220,3178, -3226,3241,3179,3180,3242,3181,3182,3183,3184,3185,3186,3187,3125,3188,3189,3190, -3191,3192,3193,3194,3123,3195,3196,3197,3198,3224,3199,3200,3201,3202,3203,3204, +1551,1552,1553,3101, 181, 182, 183,3102, 185,1556,1557,3103,1559, 188, 189,1560, +1561,1562, 193,1564,1565, 195, 196, 197, 198, 199, 200, 201, 202,1566,1567, 205, + 206, 207, 208,1568,1569,1570,1571, 211,1572,1573, 214, 215, 216, 217, 218, 219, + 220, 221,1574,1575,1576,1577,1578, 225,1579,1580,3100,1581,1582, 229, 230, 231, + 232, 233, 234, 235,1583, 237, 238, 239,1584,1585, 240,1586, 242, 243, 244,1587, + 246, 247,1588,1589, 249, 250, 251,1590, 252, 253, 254, 255,1591,1592,1593,1594, +1595, 259,1596,1597,1598,1599,1600,1601, 264, 265, 266, 267, 268, 269, 270, 271, +1602, 272, 273,1603,1604,1605, 276,1606,1607, 295, 295,1610,3104,3105, 295, 284, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, - 295, 295, 295,3274,3274,3277,3275,3276,3275,3272,3272,3272,3272,3272,3272,3272, -3272,3272,3272,3272,3272,3272,3278,3279,3301,3269,3280,3269,3281,3282,3283,3284, -3285,3286,3287,3288,3289, 349,3290,3291,3292,3293,3294,3276,3276,3276,3295,3296, -3297,3298, 349,3299,3300,3276,3283,3284,3273, 295, 295, 295, 295, 295, 295, 295, + 295, 295, 295,2430,2430,3149,3147,3148,3147,3145,3145,3145,3145,3145,3145,3145, +3145,3145,3145,3145,3145,3145,3150,3151,3173, 329,3152, 329,3153,3154,3155,3156, +3157,3158,3159,3160,3161, 349,3162,3163,3164,3165,3166,3148,3148,3148,3167,3168, +3169,3170, 349,3171,3172,3148,3155,3156, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, }, { -3302,3303,3304,3305,3306,3307,3308,3309,3310,3311,3312,3313,3314,3315,3316,3317, -3318,3319,3320,3321,3322,3323,3324,3324,3325,3326,3327,3328,3329,3330,3331,3332, -3333,3334,3335,3336,3337,3338,3339,3340,3341,3342,3343,3344,3345,3346,3347,3348, -3349,3350,3351,3352,3353,3354,3355,3356,3357,3358,3359,3324,3360,3361,3362,3363, -3324,3364,3365,3366,3324,3367,3324,3368,3324,3369,3324,3370,3324,3371,3372,3373, -3374,3375,3376,3324,3377,3378,3324,3379,3380,3381,3382,3383,3324,3324,3384,3324, -3385,3324,3386,3324,3387,3388,3324,3389,3324,3324,3390,3324,3324,3324,3324,3324, -3324,3324,3324,3391,3324,3324,3324,3324,3324,3324,3392,3393,3324,3324,3324,3324, -3394,3395, 285,3396,3397, 287, 288,3398,3399,3399,3399,3399,3399,3399,3399,3399, -3399,3399,3399,3399,3399,3399,3399,3399,3399,3399,3399,3399,3399,3399,3399,3399, -3399,3399,3399,3400,3401,3399,3402,3399,3403,3404,3405,3406,3407,3408,3409,3410, -3410,3411,3408,3412,3399,3399,3413,3414,3399,3399,3399,3399,3399,3399,3399,3399, -3399,3399,3399,3399,3399,3399,3399,3399,3399,3399,3399,3399,3399,3399,3399,3399, -3399,3399,3399,3399,3399,3399,3399,3399,3399,3399,3399,3399,3399,3399,3399,3399, -3399,3399,3399,3399,3399,3399,3399,3399,3399,3399,3399,3399,3399,3399,3399,3399, -3399,3399,3399,3399,3399,3399,3399,3399,3399,3399,3399,3399,3399,3399,3399,3399, +3174,3175,3176,3177,3178,3179,3180,3181,3182,3183,3184,3185,3186,3187,3188,3189, +3190,3191,3192,3193,3194,3195,3196,3196,3197,3198,3199,3200,3201,3202,3203,3204, +3205,3206,3207,3208,3209,3210,3211,3212,3213,3214,3215,3216,3217,3218,3219,3220, +3221,3222,3223,3224,3225,3226,3227,3228,3229,3230,3231,3196,3232,3233,3234,3235, +3196,3236,3237,3238,3196,3239,3196,3240,3196,3241,3196,3242,3196,3243,3244,3245, +3246,3247,3248,3196,3249,3250,3196,3251,3252,3253,3254,3255,3196,3196,3256,3196, +3257,3196,3258,3196,3259,3260,3196,3261,3196,3196,3262,3196,3196,3196,3196,3196, +3196,3196,3196,3263,3196,3196,3196,3196,3196,3196,3264,3265,3196,3196,3196,3196, +3266,3267, 285,3268,3269, 287, 288,3270,3271,3271,3271,3271,3271,3271,3271,3271, +3271,3271,3271,3271,3271,3271,3271,3271,3271,3271,3271,3271,3271,3271,3271,3271, +3271,3271,3271,3272,3273,3271,3274,3271,3275,3276,3277,3278,3279,3280,3281,3282, +3282,3283,3280,3284,3271,3271,3285,3286,3271,3271,3271,3271,3271,3271,3271,3271, +3271,3271,3271,3271,3271,3271,3271,3271,3271,3271,3271,3271,3271,3271,3271,3271, +3271,3271,3271,3271,3271,3271,3271,3271,3271,3271,3271,3271,3271,3271,3271,3271, +3271,3271,3271,3271,3271,3271,3271,3271,3271,3271,3271,3271,3271,3271,3271,3271, +3271,3271,3271,3271,3271,3271,3271,3271,3271,3271,3271,3271,3271,3271,3271,3271, }, { -3415,3416, 466,3417, 713,3324,3418, 467,3419, 632,3420,3421,3422,3423,3424,3425, -3426, 598,3427,3428,3429,3430,3431,3432,1512,3433,3422,3434, 759, 437, 437,3435, -3436,3437, 439,3438, 440,3439, 442, 441,3440,3441,3442,3443,2306,3444,3445, 582, -3446,3447, 452,3448,3449,3418,3450,3451,3452, 778,3453,2312, 779,3454,3455,3456, -3457, 769,3458,3459,3460,3461, 663, 664,3462,2316,3463,3464,3465,1299,3466,3467, -3468,3469,3470,3471,3472,3473,3474, 442,3475,3476,3477,3432,3478,1669,3479,3480, -3481,3482,3483,3484,3481, 261,3485,3486,1919,2420,3487,3488, 676,3489,3490,3491, -3492,3493,3494,3495, 677,3496, 677,3497,3498, 677,3499, 455,3500,2421,3501, 465, +3287,3288, 466,3289, 713,3196,3290, 467,3291, 632,3292,3293,3294,3295,3296,3297, +3298, 598,3299,3300,3301,3302,3303,3304,1512,3305,3294,3306, 759, 437, 437,3307, +3308,3309, 439,3310, 440,3311, 442, 441,3312,3313,3314,3315,2306,3316,3317, 582, +3318,3319, 452,3320,3321,3290,3322,3323,3324, 778,3325,2312, 779,3326,3327,3328, +3329, 769,3330,3331,3332,3333, 663, 664,3334,2316,3335,3336,3337,1299,3338,3339, +3340,3341,3342,3343,3344,3345,3346, 442,3347,3348,3349,3304,3350,1669,3351,3352, +3353,3354,3355,3356,3353, 261,3357,3358,1919,2420,3359,3360, 676,3361,3362,3363, +3364,3365,3366,3367, 677,3368, 677,3369,3370, 677,3371, 455,3372,2421,3373, 465, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, @@ -10476,14 +10343,14 @@ const unsigned short banks[75][256] = 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, }, { -3502,3502,3503,3504,3505,3506,3506,3507,3508,3509,3510,3511,3512,3513,3514,3515, -3516,3517,3518,3519,3520,3521,3522,3523,3524,3525,3526,3527,3528,3529,3530,3531, -3532,3533,3534,3535,3536,3537,3538,3539,3540,3541,3542,3543,3544,3545,3546,3547, -3548,3549,3550,3551,3552,3553,3554,3555,3553,3556,3557,3558,3559,3560,3561,3562, -3563,3564,3565,3566,3567,3568,3569,3570,3571,3572,3573,3574,3463,3575,3576,3577, -3578,3579,3580,3581,3582,3583,3584,3585,3586,3587,3588,3589,3590,3590,3591,3592, -3593,3594,3595,3596,3597,3598,3599,3600,3601,3602,3603,3604,3605,3606,3607,3608, -3609,3610,3609,3611,3612,3613,3614,3615,3616,3617,3618,3619,3620,3621,3622,3623, +3374,3374,3375,3376,3377,3378,3378,3379,3380,3381,3382,3383,3384,3385,3386,3387, +3388,3389,3390,3391,3392,3393,3394,3395,3396,3397,3398,3399,3400,3401,3402,3403, +3404,3405,3406,3407,3408,3409,3410,3411,3412,3413,3414,3415,3416,3417,3418,3419, +3420,3421,3422,3423,3424,3425,3426,3427,3425,3428,3429,3430,3431,3432,3433,3434, +3435,3436,3437,3438,3439,3440,3441,3442,3443,3444,3445,3446,3335,3447,3448,3449, +3450,3451,3452,3453,3454,3455,3456,3457,3458,3459,3460,3461,3462,3462,3463,3464, +3465,3466,3467,3468,3469,3470,3471,3472,3473,3474,3475,3476,3477,3478,3479,3480, +3481,3482,3481,3483,3484,3485,3486,3487,3488,3489,3490,3491,3492,3493,3494,3495, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, @@ -10505,114 +10372,114 @@ const unsigned short banks[75][256] = 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 127, 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, - 139, 140, 141, 142, 143, 144, 145, 146, 147, 148,3624, 150, 151, 152, 153, 154, + 139, 140, 141, 142, 143, 144, 145, 146, 147, 148,3496, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, 176, 177, 178, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, }, { -1551,1552,1553,1554, 181, 182, 183,1555, 185,1556,3625,3626,3627, 188, 189,1560, -1561,1562,1563,3628,1565, 195, 196, 197, 198, 199, 200, 201, 202,1566,1567, 205, - 206, 207, 208,1568,1569,1570,1571,3629,1572,1573, 214, 215, 216, 217, 218, 219, +1551,1552,1553,1554, 181, 182, 183,1555, 185,1556,3497,3498,3499, 188, 189,1560, +1561,1562,1563,3500,1565, 195, 196, 197, 198, 199, 200, 201, 202,1566,1567, 205, + 206, 207, 208,1568,1569,1570,1571,3501,1572,1573, 214, 215, 216, 217, 218, 219, 220, 221,1574,1575,1576,1577,1578, 225,1579,1580, 226,1581,1582, 229, 230, 231, 232, 233, 234, 235,1583, 237, 238, 239,1584,1585, 240,1586, 242, 243, 244,1587, 246, 247,1588,1589, 249, 250, 251,1590, 252, 253, 254, 255,1591,1592,1593,1594, 1595, 259,1596,1597,1598,1599,1600,1601, 264, 265, 266, 267, 268, 269, 270, 271, -1602, 272, 273,1603,1604,1605, 276,1606,1607, 295, 295,1610,3630,3631, 295, 284, +1602, 272, 273,1603,1604,1605, 276,1606,1607, 295, 295,1610,3502,3503, 295, 284, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, - 295, 295, 295, 127,3632,1613,1614,3633,1616,3634,1618,3635,1620,3636,1622,3637, -3638,1625,3639,3640,1628,1629,1630,1631,3641,1633,1634,3642,1636,1637,1638,1639, + 295, 295, 295, 127,3504,1613,1614,3505,1616,3506,1618,3507,1620,3508,1622,3509, +3510,1625,3511,3512,1628,1629,1630,1631,3513,1633,1634,3514,1636,1637,1638,1639, 1640,1641,1642,1643,1644,1645,1646,1647,1648,1649,1650,1651,1652,1653,1654,1655, 1656,1657,1658,1659,1660,1661,1662,1663, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, }, { -3643,3644,3645,3646,3647,3648,3649,3650,3651,3652,3653,3654,3655,3656,3657,3658, -3659,3660,3661,3662,3663,3664,3665,3666,3667,3668,3669,3670,3671,3672,3673,3674, -3675,3676,3677,3678,3679,3680,3681,3682,3683,3684,3685,3686,3687,3688,3689,3690, -3691,3692,3693,3694,3695,3696,3697,3698,3699,3700,3701,3702,3703,3704,3705,3706, -3707,3708,3709,3710,3711,3712,3713,3714,3715,3716,3717,3718,3719,3720,3721,3722, -3723,3724,3725,3726,3727,3728,3729,3730,3731,3732,3733,3734,3735,3736,3737,3738, -3739,3740,3741,3742,3743,3744,3745,3746,3747,3748,3749,3750,3751,3752,3753,3754, -3755,3756,3757,3758,3759,3760,3761,3762,3763,3764,3765,3766,3767,3768,3769,3770, +3515,3516,3517,3518,3519,3520,3521,3522,3523,3524,3525,3526,3527,3528,3529,3530, +3531,3532,3533,3534,3535,3536,3537,3538,3539,3540,3541,3542,3543,3544,3545,3546, +3547,3548,3549,3550,3551,3552,3553,3554,3555,3556,3557,3558,3559,3560,3561,3562, +3563,3564,3565,3566,3567,3568,3569,3570,3571,3572,3573,3574,3575,3576,3577,3578, +3579,3580,3581,3582,3583,3584,3585,3586,3587,3588,3589,3590,3591,3592,3593,3594, +3595,3596,3597,3598,3599,3600,3601,3602,3603,3604,3605,3606,3607,3608,3609,3610, +3611,3612,3613,3614,3615,3616,3617,3618,3619,3620,3621,3622,3623,3624,3625,3626, +3627,3628,3629,3630,3631,3632,3633,3634,3635,3636,3637,3638,3639,3640,3641,3642, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, - 295, 295, 295,3772,3773,3774,3775,3776,3777,3778,3779,3780,3781,3782,3783,3784, -3785,3786,3787,3788,3786,3789,3790,3786,3791,3786,3792,3793,3794,3795,3796,3797, -3798,3799,3800,3801,3802,3803,3804,3805,3806,3807,3808,3809,3810,3811,3812,3813, -3814,3815, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + 295, 295, 295,3644,3645,3646,3647,3648,3649,3650,3651,3652,3653,3654,3655,3656, +3657,3658,3659,3660,3658,3661,3662,3658,3663,3658,3664,3665,3666,3667,3668,3669, +3670,3671,3672,3673,3674,3675,3676,3677,3678,3679,3680,3681,3682,3683,3684,3685, +3686,3687, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, }, { -3415,1492,3816,3417, 713,3817,3818, 730,1914,3819,1533,3421,3820,3821,3822,3823, -3426,3824,3825,3826,3827,3828,3829,3830,3831,3832,3833,3834, 759, 437,3835,3836, -3436,3837,3838,3839,3840,3841, 442,3842,3843,3844,3845,3846,3847,3848,3849,3850, -3851,2306,3852,3853,3854,3855,3856,3857,3858,3859,3860,3861,3862,3863,3864,3865, -3866,3867,3868,3869,3870,3871,3872,3873,3874,3875,3876,3877,3878,3879,3880,3881, -3882,3883,3884,3885,3886,3887,3888,3889,3890,3891,3892,3893,3894,3895,3896,3897, -3898,3899,3900,3901,3902,3903,3904,3905,3906,3907,3908,3909,3910,3911,3912,3913, -3914,3915,3916,3917,3918,3919,3920,3921,3922,3923,3499,3924,3925,2421,3926,3927, +3287,1492,3688,3289, 713,3689,3690, 730,1914,3691,1533,3293,3692,3693,3694,3695, +3298,3696,3697,3698,3699,3700,3701,3702,3703,3704,3705,3706, 759, 437,3707,3708, +3308,3709,3710,3711,3712,3713, 442,3714,3715,3716,3717,3718,3719,3720,3721,3722, +3723,2306,3724,3725,3726,3727,3728,3729,3730,3731,3732,3733,3734,3735,3736,3737, +3738,3739,3740,3741,3742,3743,3744,3745,3746,3747,3748,3749,3750,3751,3752,3753, +3754,3755,3756,3757,3758,3759,3760,3761,3762,3763,3764,3765,3766,3767,3768,3769, +3770,3771,3772,3773,3774,3775,3776,3777,3778,3779,3780,3781,3782,3783,3784,3785, +3786,3787,3788,3789,3790,3791,3792,3793,3794,3795,3371,3796,3797,2421,3798,3799, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, - 295, 295, 295,3772,3773,3774,3775,3776,3777,3778,3779,3780,3781,3782,3783,3784, -3785,3786,3787,3788,3786,3789,3790,3786,3791,3786,3792,3793,3794,3795,3796,3797, -3798,3799,3800,3801,3802,3803,3804,3805,3806,3807,3808,3809,3810,3811,3812,3813, -3814,3815, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + 295, 295, 295,3644,3645,3646,3647,3648,3649,3650,3651,3652,3653,3654,3655,3656, +3657,3658,3659,3660,3658,3661,3662,3658,3663,3658,3664,3665,3666,3667,3668,3669, +3670,3671,3672,3673,3674,3675,3676,3677,3678,3679,3680,3681,3682,3683,3684,3685, +3686,3687, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, }, { -3928,3929,3930,3931,3932,3933,3934,3935,3936,3937,3938,3939,3940,3941,3942,3943, -3944,3945,3946,3947,3948,3949,3950,3951,3952,3953,3954,3955,3956,3957,3958,3959, -3960,3961,3962,3963,3964,3965,3966,3967,3968,3969,3970,3971,3972,3973,3974,3975, -3968,3976,3977,3978,3979,3980,3981,3982,3983,3984,3985,3986,3987,3988,3989,3990, -3991,3992,3992,3993,3994,3995,3996,3997,3998,3999,4000,4001,4002,4003,4004,4005, -4006,4007,4008,4009,4010,4011,4012,4013,4014,4015,4016,4017,4018,4019,4020,4021, -4022,4023,4024,4025,4026,4027,4028,4029,4030,4031,4032,4033,4034,4035,4036,4037, -4038,4039,4040,4041,4042,4043,4044,4045,4046,4047,4048,4049,4050,4051,4052,4053, +3800,3801,3802,3803,3804,3805,3806,3807,3808,3809,3810,3811,3812,3813,3814,3815, +3816,3817,3818,3819,3820,3821,3822,3823,3824,3825,3826,3827,3828,3829,3830,3831, +3832,3833,3834,3835,3836,3837,3838,3839,3840,3841,3842,3843,3844,3845,3846,3847, +3840,3848,3849,3850,3851,3852,3853,3854,3855,3856,3857,3858,3859,3860,3861,3862, +3863,3864,3864,3865,3866,3867,3868,3869,3870,3871,3872,3873,3874,3875,3876,3877, +3878,3879,3880,3881,3882,3883,3884,3885,3886,3887,3888,3889,3890,3891,3892,3893, +3894,3895,3896,3897,3898,3899,3900,3901,3902,3903,3904,3905,3906,3907,3908,3909, +3910,3911,3912,3913,3914,3915,3916,3917,3918,3919,3920,3921,3922,3923,3924,3925, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, - 295, 295, 295,4055,4056,4057,4058,4059,4060,4061,4062,4063,4064,4065,4066,4067, -4068,4069,4070,3788,4071,4072,3790,4073,3791,4074,4075,3793,4076,4077,4078,4079, -4080,4081,4082,4083,4084,4064,3804,4085,4086,3807,4087,4088,4089,4090,3812,3813, -4091,4092,4093, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + 295, 295, 295,3927,3928,3929,3930,3931,3932,3933,3934,3935,3936,3937,3938,3939, +3940,3941,3942,3943,3944,3945,3946,3947,3948,3949,3950,3951,3952,3953,3954,3955, +3956,3957,3958,3959,3960,3936,3961,3962,3963,3964,3965,3966,3967,3968,3969,3970, +3971,3972,3973, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, }, { -3928,3929,3930,3931,3932,3933,3934,3935,3936,3937,3938,3939,3940,3941,3942,3943, -3944,3945,3946,3947,3948,3949,3950,3951,3952,3953,3954,3955,3956,3958,4094,3959, -3960,3961,3962,3963,3964,3965,3966,3967,3968,3969,3970,3971,4095,3973,3974,3975, -3968,3976,3977,3978,3979,3980,3981,3982,3983,3984,3985,3986,3987,3988,4096,3990, -3991,3992,3992,3993,3994,3995,3996,3997,3998,3999,4000,4001,4002,4003,4004,4005, -4097,4007,4098,4009,4010,4011,4012,4013,4014,4015,4016,4017,4018,4019,4020,4021, -4022,4023,4024,4025,4026,4027,4028,4029,4030,4031,4099,4100,4101,4035,4036,4037, -4038,4039,4040,4041,4102,4043,4044,4103,4046,4047,4048,4049,4050,4051,4052,4053, -4104,4104,4104,4104,4104,4104,4104,4104,4104,4104,4104,4104,4104,4104,4104,4104, -4104,4104,4104,4104,4104,4104,4104,4104,4104,4104,4104,4105,4105,4105,4105,4106, -4105,4105,4105,4055,4056,4107,4058,4059,4060,4108,4062,4063,4064,4065,4066,4067, -4068,4069,4070,3788,4071,4072,3790,4073,3791,4074,4075,3793,4076,4077,4078,4079, -4080,4081,4082,4083,4084,4064,3804,4085,4086,4109,4110,4088,4111,4112,3812,3813, -4091,4092,4093,4105,4105,4105,4105,4105,4105,4104,4113,4113,4113,4113,4113,4113, -4113,4113,4113,4113,4113,4113,4113,4113,4113,4113,4113,4113,4113,4113,4113,4113, -4113,4113,4113,4113,4113,4113,4113,4113,4113,4113,4113,4113,4113,4113,4113,4113, +3800,3801,3802,3803,3804,3805,3806,3807,3808,3809,3810,3811,3812,3813,3814,3815, +3816,3817,3818,3819,3820,3821,3822,3823,3824,3825,3826,3827,3828,3974,3975,3831, +3832,3833,3834,3835,3836,3837,3838,3839,3840,3841,3842,3843,3976,3845,3846,3847, +3840,3848,3849,3850,3851,3852,3853,3854,3855,3856,3857,3858,3977,3860,3978,3862, +3863,3864,3864,3865,3866,3867,3868,3869,3870,3871,3872,3873,3874,3875,3876,3877, +3979,3879,3980,3881,3882,3883,3884,3885,3886,3887,3888,3889,3890,3891,3892,3893, +3894,3895,3896,3897,3898,3899,3900,3901,3902,3903,3981,3982,3983,3907,3908,3909, +3910,3911,3912,3913,3984,3915,3916,3985,3918,3919,3920,3921,3922,3923,3924,3925, +3986,3986,3986,3986,3986,3986,3986,3986,3986,3986,3986,3986,3986,3986,3986,3986, +3986,3986,3986,3986,3986,3986,3986,3986,3986,3986,3986,3987,3987,3987,3987,3988, +3987,3987,3987,3927,3928,3989,3930,3931,3932,3990,3934,3935,3936,3937,3938,3939, +3940,3941,3942,3943,3944,3945,3946,3947,3948,3949,3950,3951,3952,3953,3954,3955, +3956,3957,3958,3959,3960,3936,3961,3962,3963,3991,3992,3966,3993,3994,3969,3970, +3971,3972,3973,3987,3987,3987,3987,3987,3995,3986,3996,3996,3996,3996,3996,3996, +3996,3996,3996,3996,3996,3996,3996,3996,3996,3996,3996,3996,3996,3996,3996,3996, +3996,3996,3996,3996,3996,3996,3996,3996,3996,3996,3996,3996,3996,3996,3996,3996, }, { 1222,1223,1224,1225,1226,1227,1228,1229,1230,1231,1232,1233,1234,1235,1236,1237, -1238,1239,1240,1241,1242,1243,1244,1245,4114,4115,4116,1249,1250,1376,1377,1253, -1378,1255,1256,4117,1380,1259,1260,1261,1262,1263,1264,1265,1266,1267,1268,4118, -1270,4119,4120,4121,4122,1275,1276,1277,1278,1279,1381,1281,1282,1283,1284,1285, +1238,1239,1240,1241,1242,1243,1244,1245,3997,3998,3999,1249,1250,1376,1377,1253, +1378,1255,1256,4000,1380,1259,1260,1261,1262,1263,1264,1265,1266,1267,1268,4001, +1270,4002,4003,4004,4005,1275,1276,1277,1278,1279,1381,1281,1282,1283,1284,1285, 1286,1287,1288,1289,1290,1291,1292,1293,1294,1295,1296,1297,1298,1299,1300,1301, -1302,1303,1304,1305,4123,1307,1308,1309,1309,1310,1311,1312,1313,1314,1315,1316, +1302,1303,1304,1305,4006,1307,1308,1309,1309,1310,1311,1312,1313,1314,1315,1316, 1317,1318,1319,1320,1321,1322,1323,1324,1325,1326,1327,1328,1329,1330,1331,1332, -1333,1334,1335,1336,1337,4124,1339,1340,1341,1342,1343,1344,1345,1346,1347,1348, +1333,1334,1335,1336,1337,4007,1339,1340,1341,1342,1343,1344,1345,1346,1347,1348, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, - 295, 295, 295, 312,1349,1350,4125,1352,4126,1354,4127,1355, 320,1356, 321,1357, + 295, 295, 295, 312,1349,1350,4008,1352,4009,1354,4010,1355, 320,1356, 321,1357, 1358,1359,1360,1361,1362,1363,1364,1383,1366,1359,1384,1361, 332, 333, 334, 335, 336,1368,1369, 338, 339, 320,1370, 295, 295, 295, 295,1372,1373,1374,1375, 295, 347, 348, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, @@ -10620,38 +10487,38 @@ const unsigned short banks[75][256] = 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, }, { -4128,4129,4130,4131,4132,4133, 6, 7,4134, 9, 10,4135, 12, 13, 14, 15, -4136,4137,4138,4139, 20,4140,4141,4142,4143,4144,4145,4146,4147,4148,4149,4150, - 32,4151,4152,4153,4154,4155,4156,4157,4158,4159,4160,4161,4162,4163,4164, 46, -4165,4166,4167,4168,4169,4170,4171, 54,4172,4173,4174,4175,4176,4177,4178,4179, -4180,4181,4182, 66,4183,4184, 69,4185,4186,4187,4188, 74, 75, 76, 77, 78, -4189,4190,4191, 82,4192,4193, 85,4194,4195, 88,4196,4197,4198,4199,4200,4201, - 95, 96, 97,4202,4203,4204,4205, 102, 103,4206, 105, 106, 107,4207,4208,4209, -4210, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121,4211,4212,4213, 125,4214, +4011,4012,4013,4014,4015,4016, 6, 7,4017, 9, 10,4018, 12, 13, 14, 15, +4019,4020,4021,4022, 20,4023,4024,4025,4026,4027,4028,4029,4030,4031,4032,4033, + 32,4034,4035,4036,4037,4038,4039,4040,4041,4042,4043,4044,4045,4046,4047, 46, +4048,4049,4050,4051,4052,4053,4054, 54,4055,4056,4057,4058,4059,4060,4061,4062, +4063,4064,4065, 66,4066,4067, 69,4068,4069,4070,4071, 74, 75, 76, 77, 78, +4072,4073,4074, 82,4075,4076, 85,4077,4078, 88,4079,4080,4081,4082,4083,4084, + 95, 96, 97,4085,4086,4087,4088, 102, 103,4089, 105, 106, 107,4090,4091,4092, +4093, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121,4094,4095,4096, 125,4097, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, - 295, 295, 295,3772,3773,3774,3775,3776,3777,3778,3779,3780,3781,3782,3783,3784, -3785,3786,3787,3788,3786,3789,3790,3786,3791,3786,3792,3793,3794,3795,3796,3797, -3798,3799,3800,3801,3802,3803,3804,3805,3806,3807,3808,3809,3810,3811,3812,3813, -3814,3815, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + 295, 295, 295,3644,3645,3646,3647,3648,3649,3650,3651,3652,3653,3654,3655,3656, +3657,3658,3659,3660,3658,3661,3662,3658,3663,3658,3664,3665,3666,3667,3668,3669, +3670,3671,3672,3673,3674,3675,3676,3677,3678,3679,3680,3681,3682,3683,3684,3685, +3686,3687, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, }, { -3415,3416, 466,3417, 713,3324,3418, 467,3419, 632,3420,3421,3422,3423,4215,3425, -3426, 598,4216,3428,3429,3430,3431,3432,4217,4218,4219,4220, 759, 437, 438,3435, -3436,3437, 439,3438, 440,4221, 442,4222,3440,3441,3442,3443,2306,4223,3445, 582, -3446,3447, 452,3448,3449,4224,3450,3451,3452, 778,3453,2312, 779,3454,3455,3456, -3457, 769,3458,3459,3460,3461, 663, 664,3462,2316,3463,3464,3465,1299,3466,3467, -3468,3469,4225,3471,3472,3473,4226, 442,3475,3476,3477,3432,3478,1669,3479,3480, -3481,3482,3483,3484,4227, 261,3485,3486,1919,2420,3487,3488, 676,3489,3490,3491, -3492,4228,3494,3495,4229,4230, 680,3497,3498, 462,3499, 455,3500,2421,3501, 465, +3287,3288, 466,3289, 713,3196,3290, 467,3291, 632,3292,3293,3294,3295,4098,3297, +3298, 598,4099,3300,3301,3302,3303,3304,4100,4101,4102,4103, 759, 437, 438,3307, +3308,3309, 439,3310, 440,4104, 442,4105,3312,3313,3314,3315,2306,4106,3317, 582, +3318,3319, 452,3320,3321,4107,3322,3323,3324, 778,3325,2312, 779,3326,3327,3328, +3329, 769,3330,3331,3332,3333, 663, 664,3334,2316,3335,3336,3337,1299,3338,3339, +3340,3341,4108,3343,3344,3345,4109, 442,3347,3348,3349,3304,3350,1669,3351,3352, +3353,3354,3355,3356,4110, 261,3357,3358,1919,2420,3359,3360, 676,3361,3362,3363, +3364,4111,3366,3367,4112,4113, 680,3369,3370, 462,3371, 455,3372,2421,3373, 465, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, - 295, 295, 295,3772,3773,4231,3777,3776,4232,4233,4234,4235,4236,4237,4238,4239, -4240,3786,4241,3788,3786,3789,3790,3786,3791,3786,3792,2617,3794,3795,3796,3797, -3798,3799,3800,3801,3802, 160,3804,3805,3806,3807,3808,3809,3810,3811,3812,3813, -3814,3815, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + 295, 295, 295,3644,3645,4114,3649,3648,4115,4116,4117,4118,4119,4120,4121,4122, +4123,3658,4124,3660,3658,3661,3662,3658,3663,3658,3664,2617,3666,3667,3668,3669, +3670,3671,3672,3673,3674, 160,3676,3677,3678,3679,3680,3681,3682,3683,3684,3685, +3686,3687, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, }, @@ -10666,28 +10533,28 @@ const unsigned short banks[75][256] = 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, - 127, 127, 127, 127,4242,4243,4244, 141,4245,4246,4247,4246,4248,4246,4249,4250, -4251,1954,4252,4253,4254,4255,4256,4257,4258,4259,4260,4261,4262,4263,4264,4265, -4266,4267,4268,4269,4270,4271, 163,4272, 625,4273,4274,4275,4276,4277,4278,4279, -4280,4281,4282,4283,4284,4285,4286, 127, 127, 127, 127, 127, 127, 127, 127, 127, + 127, 127, 127, 127,4125,4126,4127, 141,4128,4129,4130,4129,4131,4129,4132,4133, +4134,1954,4135,4136,4137,4138,4139,4140,4141,4142,4143,4144,4145,4146,4147,4148, +4149,4150,4151,4152,4153,4154, 163,4155, 625,4156,4157,4158,4159,4160,4161,4162, +4163,4164,4165,4166,4167,4168,4169, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, }, { -4287,4288,1553,1554, 181,4289,4290,1555, 185,4291,3625,4292,1559, 188,4293,4294, -4295,1562,4296,4297,1565, 195,4298, 197,4299,4300, 200, 201, 28,1566,1567,4301, -4302,4303,4304,4305,4306,4307,4308,4309,1572,1573, 214, 215, 216, 217, 218, 219, - 220,4310,4311,1575,1576,1577,1578,4312,1579,1580,4313,1581,4314,4315,4316,4317, - 232, 233, 234, 235,4318, 237, 238,4319,1584,1585, 240,1586, 242, 243, 244,1587, - 246,4320,4321,1589, 249, 250, 251,1590, 252,4322, 254, 255,4323,4324,4325,4326, -1595,4327,1596,1597,1598,1599,1600,1601,4328,4329,4330, 267,4331,4332, 270,4333, -4334,4335, 273,1603,4336,1605, 276,4337,4338,4339,4340,1610,4341,4342,4343, 126, - 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, - 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, - 295, 295, 295,4344,1470,1613,4345,1615,4346,1475,4347,1477,4348,1479,4349,1481, -1482,4350,1484,4351,4352,4353,1630,4354,2291,4355,1634,3642,1636,1637,1638,1639, -1640,4356,4357,1643,1644,1645,1646,4358, 342,4359,4360,1651,1652,1653,4361,4362, -1656,4363,1658,1659,1660,1661,1662,1663, 295, 295, 295, 295, 295, 295, 295, 295, +4170,4171,1553,1554, 181,4172,4173,1555, 185,4174,3497,4175,1559, 188,4176,4177, +4178,1562,4179,4180,1565, 195,4181, 197,4182,4183, 200, 201, 28,1566,1567,4184, +4185,4186,4187,4188,4189,4190,4191,4192,1572,1573, 214, 215, 216, 217, 218, 219, + 220,4193,4194,1575,1576,3852,1578,4195,1579,1580,4196,4197,4198,4199,4200,4201, + 232, 233, 234, 235,4202, 237, 238,4203,1584,1585, 240,1586,4204, 243, 244,1587, + 246,4205,4206,1589, 249, 250, 251,1590, 252,4207, 254,3889,4208,4209,4210,4211, +1595,4212,1596,1597,4213,1599,1600,1601,4214,4215,4216, 267,4217,4218, 270,4219, +4220,4221,4222,1603,4223,4224, 276,4225,4226,4227,4228,1610,4229,4230,4231, 126, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295,1662,1663, 295, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295,4232,4233,4234,4235,4236, +4237,4238,4239,4240,1470,1613,4241,1615,4242,1475,4243,1477,4244,1479,4245,1481, +1482,4246,1484,4247,4248,4249,1630,4250,2291,4251,1634,3514,1636,1637,1638,1639, +1640,4252,4253,1643,1644, 320,4254,4255, 342,4256,4257,4258,4259,1374,4260,4261, +1656,4262,4263,1659,1660,4264,1662,1663, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, }, @@ -10699,9 +10566,9 @@ const unsigned short banks[75][256] = 2224, 909,2225, 911, 912,2226,2227, 915, 916,2228,2229, 919, 920, 921, 922, 923, 2230, 925, 926, 927, 928, 929, 295, 931, 932, 933, 934, 935,2232,2233, 938,2234, 940, 295, 942, 943, 944, 945, 946, 947, 948, 949, 950, 951, 952, 953, 954, 955, - 956, 957, 958, 959, 960, 961, 962, 963, 964, 965, 966,4366, 968,4367, 295, 295, -4368,4369,4370,4371,4372,4373,4374, 978,4375,4376,4377,4378,4379,4380,4381,4382, -4383,4384,4385,4386,4387,4388,4389,4390,4391,4392,4393, 975, 971, 972, 973, 974, + 956, 957, 958, 959, 960, 961, 962, 963, 964, 965, 966,4267, 968,4268, 295, 295, +4269,4270,4271,4272,4273,4274,4275, 978,4276,4277,4278,4279,4280,4281,4282,4283, +4284,4285,4286,4287,4288,4289,4290,4291,4292,4293,4294, 975, 971, 972, 973, 974, 975, 310, 976, 977, 978, 974,2239, 980,2240, 982,2241, 984,2242, 985,2243, 987, 988,2244, 990, 325,2244, 325, 991,2244,2245,2246, 330, 993,2247,2248,2249, 997, 998,2250,2251,1001,1002,1003, 340,1004,1005,1006,1007,1008,1009, 310,1010,1011, @@ -10710,62 +10577,62 @@ const unsigned short banks[75][256] = 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, }, { -3415,4395,3324,3324,4396,3324,3324,3324,3324,3324,3324, 378,3324,3324,3822,3324, -3324,4397,3324,4398,4399,4400,3324,3324,3324, 370,3324,4401,4402,4403,4404,4405, -3324,4406,4407,4408,3438,3438,4409, 442,4410,3324,3324,4411,4412,4411, 676,4413, -4414,3324,3324,4415, 51,3324,3324,3324,4416,3452,4417,3324, 59,4418,4416, 62, -3324,4419,4419,4420, 67,3324, 664,4421,3324,2316,3324,4422, 70,3324,4423, 78, -4424,4425, 81,3324,3324,3324,3324,3324,3324,3324,3324, 90,3324,4426, 93,4427, -3324,3324,3324,3324,3324,3324,3324,4428,3324,3324,3324,3324,3324,3324,3324,3324, -3324,3324,3324,3324,3324,4229,3324,4429,3324,3324,3324,3324,3324,3324,3324,3324, -3324,3324,3324,3324,3324,3324,3324,3324,3324,3324,3324,3324,3324,3324,3324,3324, -3324,3324,3324,3324,3324,3324,3324,3324,3324,3324,3324,3324,3324,3324,3324,3324, -3324,3324,3324,4430,4430,4431,4432,4433,4434,4435,4436,4437,4438,4439,4440,4441, -4442,4443,4444,4445,4446,4447,4448,4449,4450,4451,4452,4453,3324,3324,4454, 154, -4455,4456,4457,3324,3324, 160,3324,3324,3324,4458,4459,4460,4461,4462,4463,3324, -3324,4464, 160,4465,3324,3324,3324,3324,3324,3324,3324,3324,3324,3324,3324,3324, -3324,3324,3324,3324,3324,3324,3324,3324,3324,3324,3324,3324,3324,3324,3324,3324, -3324,3324,3324,3324,3324,3324,3324,3324,3324,3324,3324,3324,3324,3324,3324,3324, +3287,4296,3196,3196,4297,3196,3196,3196,3196,3196,3196, 378,3196,3196,3694,3196, +3196,4298,3196,4299,4300,4301,3196,3196,3196, 370,3196,4302,4303,4304,4305,4306, +3196,4307,4308,4309,3310,3310,4310, 442,4311,3196,3196,4312,4313,4312, 676,4314, +4315,3196,3196,4316, 51,3196,3196,3196,4317,3324,4318,3196, 59,4319,4317, 62, +3196,4320,4320,4321, 67,3196, 664,4322,3196,2316,3196,4323, 70,3196,4324, 78, +4325,4326, 81,3196,3196,3196,3196,3196,3196,3196,3196, 90,3196,4327, 93,4328, +3196,3196,3196,3196,3196,3196,3196,4329,3196,3196,3196,3196,3196,3196,3196,3196, +3196,3196,3196,3196,3196,4112,3196,4330,3196,3196,3196,3196,3196,3196,3196,3196, +3196,3196,3196,3196,3196,3196,3196,3196,3196,3196,3196,3196,3196,3196,3196,3196, +3196,3196,3196,3196,3196,3196,3196,3196,3196,3196,3196,3196,3196,3196,3196,3196, +3196,3196,3196,4331,4331,4332,4333,4334,4335,4336,4337,4338,4339,4340,4341,4342, +4343,4344,4345,4346,4347,4348,4349,4350,4351,4352,4353,4354,3196,3196,4355, 154, +4356,4357,4358,3196,3196, 160,3196,3196,3196,4359,4360,4361,4362,4363,4364,3196, +3196,4365, 160,4366,3196,3196,3196,3196,3196,3196,3196,3196,3196,3196,3196,3196, +3196,3196,3196,3196,3196,3196,3196,3196,3196,3196,3196,3196,3196,3196,3196,3196, +3196,3196,3196,3196,3196,3196,3196,3196,3196,3196,3196,3196,3196,3196,3196,3196, }, { -3928,3929,3930,3931,3932,3933,3934,3935,3936,3937,3938,3939,3940,3941,3942,3943, -3944,3945,3946,3947,3948,3949,3950,3951,3952,3953,3954,3955,3956,3957,3958,3959, -3960,3961,3962,3963,3964,3965,3966,3967,3968,3969,3970,3971,3972,3973,3974,3975, -3968,3976,3977,3978,3979,3980,3981,3982,3983,3984,3985,3986,3987,3988,3989,3990, -3991,3992,3992,3993,3994,3995,3996,3997,3998,3999,4000,4001,4002,4003,4004,4005, -4006,4007,4008,4009,4010,4011,4012,4013,4014,4015,4016,4017,4018,4019,4020,4021, -4022,4023,4024,4025,4026,4027,4028,4029,4030,4031,4032,4033,4034,4035,4036,4037, -4038,4039,4040,4041,4042,4043,4044,4045,4046,4047,4466,4049,4050,4051,4052,4053, -4104,4104,4104,4104,4104,4104,4104,4104,4104,4104,4104,4104,4104,4104,4104,4104, -4104,4104,4104,4104,4104,4104,4104,4104,4104,4104,4104,4105,4105,4105,4105,4105, -4105,4105,4105,4055,4056,4057,4058,4059,4060,4061,4062,4063,4064,4065,4066,4067, -4068,4069,4070,3788,4071,4072,3790,4073,3791,4074,4075,3793,4076,4077,4078,4079, -4080,4081,4082,4083,4084,4064,3804,4085,4086,3807,4087,4088,4089,4090,3812,3813, -4091,4092,4093,4105,4105,4105,4105,4105,4105,4104,4113,4113,4113,4113,4113,4113, -4113,4113,4113,4113,4113,4113,4113,4113,4113,4113,4113,4113,4113,4113,4113,4113, -4113,4113,4113,4113,4113,4113,4113,4113,4113,4113,4113,4113,4113,4113,4113,4113, +3800,3801,3802,3803,3804,3805,3806,3807,3808,3809,3810,3811,3812,3813,3814,3815, +3816,3817,3818,3819,3820,3821,3822,3823,3824,3825,3826,3827,3828,3829,3830,3831, +3832,3833,3834,3835,3836,3837,3838,3839,3840,3841,3842,3843,3844,3845,3846,3847, +3840,3848,3849,3850,3851,3852,3853,3854,3855,3856,3857,3858,3859,3860,3861,3862, +3863,3864,3864,3865,3866,3867,3868,3869,3870,3871,3872,3873,3874,3875,3876,3877, +3878,3879,3880,3881,3882,3883,3884,3885,3886,3887,3888,3889,3890,3891,3892,3893, +3894,3895,3896,3897,3898,3899,3900,3901,3902,3903,3904,3905,3906,3907,3908,3909, +3910,3911,3912,3913,3914,3915,3916,3917,3918,3919,4367,3921,3922,3923,3924,3925, +3986,3986,3986,3986,3986,3986,3986,3986,3986,3986,3986,3986,3986,3986,3986,3986, +3986,3986,3986,3986,3986,3986,3986,3986,3986,3986,3986,3987,3987,3987,3987,3987, +3987,3987,3987,3927,3928,3929,3930,3931,3932,3933,3934,3935,3936,3937,3938,3939, +3940,3941,3942,3943,3944,3945,3946,3947,3948,3949,3950,3951,3952,3953,3954,3955, +3956,3957,3958,3959,3960,3936,3961,3962,3963,3964,3965,3966,3967,3968,3969,3970, +3971,3972,3973,3987,3987,3987,3987,3987,3995,3986,3996,3996,3996,3996,3996,3996, +3996,3996,3996,3996,3996,3996,3996,3996,3996,3996,3996,3996,3996,3996,3996,3996, +3996,3996,3996,3996,3996,3996,3996,3996,3996,3996,3996,3996,3996,3996,3996,3996, }, { -4467,4468,4469,4470,4471,4472,4473,4474,4475,4476,4477,4478,4479,4480,4481,4482, -4483,4484,4485,4486,4487,4488,4489,4490,4491,4492,4493,4494,4495,4496,4497,4498, -4499,4500,4501,4502,4503,4504,4505,4506,4507,4508,4509,4510,4511,4512,4513,4514, -4515,4516,4517,4518,4519,4520,4521,4522,4523,4524,4525,4526,4527,4528,4529,4530, -4531,4532,4533,4534,4535,4536,4537,4538,4539,4540,4541,4542,4543,4544,4545,4546, -4547,4548,4549,4550,4551,4552,4553,4554,4555,4556,4557,4558,4559,4560,4561,4562, -4563,4564,4565,4566,4567,4568,4569,4570,4571,4572,4573,4574,4575,4576,4577,4578, -4579,4580,4581,4582,4583,4584,4585,4586,4587,4588,4589,4590,4591,4592,4593,4594, +4368,4369,4370,4371,4372,4373,4374,4375,4376,4377,4378,4379,4380,4381,4382,4383, +4384,4385,4386,4387,4388,4389,4390,4391,4392,4393,4394,4395,4396,4397,4398,4399, +4400,4401,4402,4403,4404,4405,4406,4407,4408,4409,4410,4411,4412,4413,4414,4415, +4416,4417,4418,4419,4420,4421,4422,4423,4424,4425,4426,4197,4427,4428,4429,4430, +4431,4432,4433,4434,4435,4436,4437,4438,4439,4440,4441,4442,4204,4443,4444,4445, +4446,4447,4448,4449,4450,4451,4452,4453,4454,4455,4456,4457,4458,4459,4460,4461, +4462,4463,4464,4465,4466,4467,4468,4469,4470,4215,4216,4471,4472,4218,4473,4474, +4475,4476,4222,4477,4478,4224,4479,4480,4226,4481,4228,4482,4483,4484,4485,4486, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, - 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295,4595,4596,4597,4598,4599, -4600,4601,4602,4603,4604,4605,4606,4607,4346,4608,4609,4610,4611,4612,4349,4613, -4614,4615,4616,4617,4352,4618,4619,4354,4620,4621,4622,4623,4624,4625,4626,4627, -4628,4629,4630,4631,4632, 320,4633,4634,4635,4636,4637,4638,4639,1374,4640,4641, -4642,4643,4644,4645,4646,4647,4648,4649, 295, 295, 295, 295, 295, 295, 295, 295, + 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295,4232,4233,4234,4235,4487, +4237,4238,4239,4488,4489,4490,4491,4492,4242,4493,4494,4495,4244,4496,4245,4497, +4498,4499,4500,4501,4248,4502,4503,4250,4504,4505,4506,4507,4508,4509,4510,4511, +4512,4252,4253,4513,4514, 320,4515,4516,4517,4256,4257,4258,4259,1374,4260,4261, +4518,4519,4263,4520,4521,4264,4522,4523, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, }, { -4650,4651,4652,4653,4651,4654,4655,4656,4657,4658,4659,4661,4662,4663,4664,4665, -4666,4668,4670,4663,4672,4673,4674,4675,4676,4677,4678, 295, 28, 29, 30, 31, +4524,4525,4526,4527,4525,4528,4529,4530,4531,4532,4533,4535,4536,4537,4538,4539, +4540,4542,4544,4537,4546,4547,4548,4549,4550,4551,4552, 295, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 33, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, @@ -10774,28 +10641,28 @@ const unsigned short banks[75][256] = 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, - 295, 295, 295, 127,4667, 128,4669, 130, 131, 132,4671, 134, 135, 136, 137, 138, - 139, 140, 141, 142, 143, 144,4660, 146, 147, 148, 149, 150, 151, 152, 153, 154, + 295, 295, 295, 127,4541, 128,4543, 130, 131, 132,4545, 134, 135, 136, 137, 138, + 139, 140, 141, 142, 143, 144,4534, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, }, { -4680,4681,4682,4683,4684,4685,4686,4687,4688,4689,4690,4691,4692,3423,4689,4693, -3426,4694,4695,3426,4696,4697,4697,4697, 868, 869, 870,4698,4699,4700,4701,4702, -4703,4704,4705,4705,4706,4703,4707,4708,3970,4709,4710,4710,4711,4712,4713,4714, -4715,4716,4717,4717,4718,4224,3450,4719,4720,4721,4722,4723,4724,4720,4720,4725, -3457, 769,3458,3459,4726,4727,4728,4729,4730,4731,4732,4733,4734,4731,1300,4735, -4736,4737,4731,4696,4738,4720,4739,4740,3475,4741,4742,4743,4744,1314,4745,4746, -4747,4748,4749,4742,4742,1322,4750,4751,4752,4753,4754,4753,3423,4755,3970,4756, -4757,1334,4757,4758,4759,4760, 792,4761,1341,1342,1343,1344,4762,4763,4764,1348, +4554,4555,4556,4557,4558,4559,4560,4561,4562,4563,4564,4565,4566,3295,4563,4567, +3298,4568,4569,3298,4570,4571,4571,4571, 868, 869, 870,4572,4573,4574,4575,4576, +4577,4578,4579,4579,4580,4577,4581,4582,3842,4583,4584,4584,4585,4586,4587,4588, +4589,4590,4591,4591,4592,4107,3322,4593,4594,4595,4596,4597,4598,4594,4594,4599, +3329, 769,3330,3331,4600,4601,4602,4603,4604,4605,4606,4607,4608,4605,1300,4609, +4610,4611,4605,4570,4612,4594,4613,4614,3347,4615,4616,4617,4618,1314,4619,4620, +4621,4622,4623,4616,4616,1322,4624,4625,4626,4627,4628,4627,3295,4629,3842,4630, +4631,1334,4631,4632,4633,4634, 792,4635,1341,1342,1343,1344,4636,4637,4638,1348, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, - 295, 295, 295,4765,4765,4766,4767,4766,4768,4769,4770,4771,4772,4773,4774,4775, -4776,4777,4778,4779,4780,4781,4782,4783,4784,4777,4785,4786,4787,4788,4789,4790, -4791,4792,4793,4794,4795,4796,4797,4794,4795,4796,4798,4799,4800,4801,1375, 295, -4802,4803, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, + 295, 295, 295,4639,4639,4640,4641,4640,4642,4643,4644,4645,4646,4647,4648,4649, +4650,4651,4652,4653,4654,4655,4656,4657,4658,4651,4659,4660,4661,4662,4663,4664, +4665,4666,4667,4668,4669,4670,4671,4668,4669,4670,4672,4673,4674,4675,1375, 295, +4676,4677, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, 295, }, @@ -10869,8 +10736,8 @@ const AdlBankSetup adlbanksetup[75] = {3, 0, 0, 0, 0}, //Bank 63, TMB (Shadow Warrior) {2, 0, 0, 0, 0}, //Bank 64, DMX (Raptor) {3, 0, 0, 0, 0}, //Bank 65, OP3 (Modded GMOPL by Wohlstand) - {3, 0, 0, 0, 0}, //Bank 66, SB (Jammey O'Connel's bank) - {3, 0, 0, 0, 0}, //Bank 67, TMB (Default bank of Apgee Sound System) + {3, 0, 0, 0, 0}, //Bank 66, SB (Jamie O'Connell's bank) + {3, 0, 0, 0, 0}, //Bank 67, TMB (Default bank of Apogee Sound System) {0, 1, 1, 0, 0}, //Bank 68, WOPL (4op bank by James Alan Nguyen and Wohlstand) {3, 0, 0, 0, 0}, //Bank 69, TMB (Blood) {3, 0, 0, 0, 0}, //Bank 70, TMB (Lee) diff --git a/src/sound/adlmidi/adldata.hh b/src/sound/adlmidi/adldata.hh index 104db82cd..93d4144e0 100644 --- a/src/sound/adlmidi/adldata.hh +++ b/src/sound/adlmidi/adldata.hh @@ -26,6 +26,7 @@ #include #include +#include #pragma pack(push, 1) #define ADLDATA_BYTE_COMPARABLE(T) \ @@ -34,32 +35,32 @@ inline bool operator!=(const T &a, const T &b) \ { return !operator==(a, b); } -extern const struct adldata +struct adldata { uint32_t modulator_E862, carrier_E862; // See below uint8_t modulator_40, carrier_40; // KSL/attenuation settings uint8_t feedconn; // Feedback/connection bits for the channel int8_t finetune; -} adl[]; +}; ADLDATA_BYTE_COMPARABLE(struct adldata) -enum { adlDefaultNumber = 189 }; -extern const struct adlinsdata +struct adlinsdata { enum { Flag_Pseudo4op = 0x01, Flag_NoSound = 0x02, Flag_Real4op = 0x04 }; + enum { Flag_RM_BassDrum = 0x08, Flag_RM_Snare = 0x10, Flag_RM_TomTom = 0x18, + Flag_RM_Cymbal = 0x20, Flag_RM_HiHat = 0x28, Mask_RhythmMode = 0x38 }; + uint16_t adlno1, adlno2; uint8_t tone; uint8_t flags; uint16_t ms_sound_kon; // Number of milliseconds it produces sound; uint16_t ms_sound_koff; + int8_t midi_velocity_offset; double voice2_fine_tune; -} adlins[]; +}; ADLDATA_BYTE_COMPARABLE(struct adlinsdata) -int maxAdlBanks(); -extern const unsigned short banks[][256]; -extern const char* const banknames[]; enum { adlNoteOnMaxTime = 40000 }; @@ -73,9 +74,9 @@ struct adlinsdata2 uint8_t flags; uint16_t ms_sound_kon; // Number of milliseconds it produces sound; uint16_t ms_sound_koff; + int8_t midi_velocity_offset; double voice2_fine_tune; - adlinsdata2() {} - explicit adlinsdata2(const adlinsdata &d); + static adlinsdata2 from_adldata(const adlinsdata &d); }; ADLDATA_BYTE_COMPARABLE(struct adlinsdata2) @@ -85,25 +86,43 @@ ADLDATA_BYTE_COMPARABLE(struct adlinsdata2) /** * @brief Bank global setup */ -extern const struct AdlBankSetup +struct AdlBankSetup { int volumeModel; bool deepTremolo; bool deepVibrato; bool adLibPercussions; bool scaleModulators; -} adlbanksetup[]; +}; + +#ifndef DISABLE_EMBEDDED_BANKS +int maxAdlBanks(); +extern const adldata adl[]; +extern const adlinsdata adlins[]; +extern const unsigned short banks[][256]; +extern const char* const banknames[]; +extern const AdlBankSetup adlbanksetup[]; +#endif /** * @brief Conversion of storage formats */ -inline adlinsdata2::adlinsdata2(const adlinsdata &d) - : tone(d.tone), flags(d.flags), - ms_sound_kon(d.ms_sound_kon), ms_sound_koff(d.ms_sound_koff), - voice2_fine_tune(d.voice2_fine_tune) +inline adlinsdata2 adlinsdata2::from_adldata(const adlinsdata &d) { - adl[0] = ::adl[d.adlno1]; - adl[1] = ::adl[d.adlno2]; + adlinsdata2 ins; + ins.tone = d.tone; + ins.flags = d.flags; + ins.ms_sound_kon = d.ms_sound_kon; + ins.ms_sound_koff = d.ms_sound_koff; + ins.midi_velocity_offset = d.midi_velocity_offset; + ins.voice2_fine_tune = d.voice2_fine_tune; +#ifdef DISABLE_EMBEDDED_BANKS + std::memset(ins.adl, 0, sizeof(adldata) * 2); +#else + ins.adl[0] = ::adl[d.adlno1]; + ins.adl[1] = ::adl[d.adlno2]; +#endif + return ins; } /** diff --git a/src/sound/adlmidi/adlmidi.cpp b/src/sound/adlmidi/adlmidi.cpp index 9210b5be3..12d0e68e2 100644 --- a/src/sound/adlmidi/adlmidi.cpp +++ b/src/sound/adlmidi/adlmidi.cpp @@ -23,13 +23,9 @@ #include "adlmidi_private.hpp" -#ifdef ADLMIDI_HW_OPL -#define MaxCards 1 -#define MaxCards_STR "1" //Why not just "#MaxCards" ? Watcom fails to pass this with "syntax error" :-P -#else -#define MaxCards 100 -#define MaxCards_STR "100" -#endif +/* Unify MIDI player casting and interface between ADLMIDI and OPNMIDI */ +#define GET_MIDI_PLAYER(device) reinterpret_cast((device)->adl_midiPlayer) +typedef MIDIplay MidiPlayer; static ADL_Version adl_version = { ADLMIDI_VERSION_MAJOR, @@ -56,7 +52,7 @@ ADLMIDI_EXPORT struct ADL_MIDIPlayer *adl_init(long sample_rate) return NULL; } - MIDIplay *player = new MIDIplay(static_cast(sample_rate)); + MIDIplay *player = new(std::nothrow) MIDIplay(static_cast(sample_rate)); if(!player) { free(midi_device); @@ -64,59 +60,109 @@ ADLMIDI_EXPORT struct ADL_MIDIPlayer *adl_init(long sample_rate) return NULL; } midi_device->adl_midiPlayer = player; - adlRefreshNumCards(midi_device); + adlCalculateFourOpChannels(player); return midi_device; } -ADLMIDI_EXPORT int adl_setNumChips(ADL_MIDIPlayer *device, int numCards) +ADLMIDI_EXPORT void adl_close(struct ADL_MIDIPlayer *device) +{ + if(!device) + return; + MidiPlayer *play = GET_MIDI_PLAYER(device); + assert(play); + delete play; + device->adl_midiPlayer = NULL; + free(device); + device = NULL; +} + +ADLMIDI_EXPORT int adl_setDeviceIdentifier(ADL_MIDIPlayer *device, unsigned id) +{ + if(!device || id > 0x0f) + return -1; + MidiPlayer *play = GET_MIDI_PLAYER(device); + assert(play); + play->setDeviceId(static_cast(id)); + return 0; +} + +ADLMIDI_EXPORT int adl_setNumChips(ADL_MIDIPlayer *device, int numChips) { if(device == NULL) return -2; - MIDIplay *play = reinterpret_cast(device->adl_midiPlayer); + MidiPlayer *play = GET_MIDI_PLAYER(device); + assert(play); #ifdef ADLMIDI_HW_OPL - (void)numCards; - play->m_setup.NumCards = 1; + ADL_UNUSED(numChips); + play->m_setup.numChips = 1; #else - play->m_setup.NumCards = static_cast(numCards); + play->m_setup.numChips = static_cast(numChips); #endif - if(play->m_setup.NumCards < 1 || play->m_setup.NumCards > MaxCards) + if(play->m_setup.numChips < 1 || play->m_setup.numChips > ADL_MAX_CHIPS) { - play->setErrorString("number of chips may only be 1.." MaxCards_STR ".\n"); + play->setErrorString("number of chips may only be 1.." ADL_MAX_CHIPS_STR ".\n"); return -1; } - play->opl.NumCards = play->m_setup.NumCards; - adl_reset(device); + int maxFourOps = static_cast(play->m_setup.numChips * 6); - return adlRefreshNumCards(device); + if(play->m_setup.numFourOps > maxFourOps) + play->m_setup.numFourOps = maxFourOps; + else if(play->m_setup.numFourOps < -1) + play->m_setup.numFourOps = -1; + + if(!play->m_synth.setupLocked()) + { + play->m_synth.m_numChips = play->m_setup.numChips; + if(play->m_setup.numFourOps < 0) + adlCalculateFourOpChannels(play, true); + else + play->m_synth.m_numFourOps = static_cast(play->m_setup.numFourOps); + play->partialReset(); + return 0; + } + + return 0; } ADLMIDI_EXPORT int adl_getNumChips(struct ADL_MIDIPlayer *device) { if(device == NULL) return -2; - MIDIplay *play = reinterpret_cast(device->adl_midiPlayer); - if(play) - return (int)play->m_setup.NumCards; - return -2; + MidiPlayer *play = GET_MIDI_PLAYER(device); + assert(play); + return (int)play->m_setup.numChips; +} + +ADLMIDI_EXPORT int adl_getNumChipsObtained(struct ADL_MIDIPlayer *device) +{ + if(device == NULL) + return -2; + MidiPlayer *play = GET_MIDI_PLAYER(device); + assert(play); + return (int)play->m_synth.m_numChips; } ADLMIDI_EXPORT int adl_setBank(ADL_MIDIPlayer *device, int bank) { - #ifdef DISABLE_EMBEDDED_BANKS - ADL_UNUSED(device); +#ifdef DISABLE_EMBEDDED_BANKS ADL_UNUSED(bank); - ADLMIDI_ErrorString = "This build of libADLMIDI has no embedded banks. Please load bank by using of adl_openBankFile() or adl_openBankData() functions instead of adl_setBank()"; + MidiPlayer *play = GET_MIDI_PLAYER(device); + assert(play); + play->setErrorString("This build of libADLMIDI has no embedded banks. " + "Please load banks by using adl_openBankFile() or " + "adl_openBankData() functions instead of adl_setBank()."); return -1; - #else +#else const uint32_t NumBanks = static_cast(maxAdlBanks()); int32_t bankno = bank; if(bankno < 0) bankno = 0; - MIDIplay *play = reinterpret_cast(device->adl_midiPlayer); + MidiPlayer *play = GET_MIDI_PLAYER(device); + assert(play); if(static_cast(bankno) >= NumBanks) { char errBuf[150]; @@ -125,30 +171,39 @@ ADLMIDI_EXPORT int adl_setBank(ADL_MIDIPlayer *device, int bank) return -1; } - play->m_setup.AdlBank = static_cast(bankno); - play->opl.setEmbeddedBank(play->m_setup.AdlBank); + play->m_setup.bankId = static_cast(bankno); + play->m_synth.setEmbeddedBank(play->m_setup.bankId); play->applySetup(); - return adlRefreshNumCards(device); - #endif + return 0; +#endif } ADLMIDI_EXPORT int adl_getBanksCount() { +#ifndef DISABLE_EMBEDDED_BANKS return maxAdlBanks(); +#else + return 0; +#endif } ADLMIDI_EXPORT const char *const *adl_getBankNames() { +#ifndef DISABLE_EMBEDDED_BANKS return banknames; +#else + return NULL; +#endif } ADLMIDI_EXPORT int adl_reserveBanks(ADL_MIDIPlayer *device, unsigned banks) { if(!device) return -1; - MIDIplay *play = reinterpret_cast(device->adl_midiPlayer); - OPL3::BankMap &map = play->opl.dynamic_banks; + MidiPlayer *play = GET_MIDI_PLAYER(device); + assert(play); + OPL3::BankMap &map = play->m_synth.m_insBanks; map.reserve(banks); return (int)map.capacity(); } @@ -161,10 +216,11 @@ ADLMIDI_EXPORT int adl_getBank(ADL_MIDIPlayer *device, const ADL_BankId *idp, in ADL_BankId id = *idp; if(id.lsb > 127 || id.msb > 127 || id.percussive > 1) return -1; - unsigned idnumber = (id.msb << 8) | id.lsb | (id.percussive ? OPL3::PercussionTag : 0); + size_t idnumber = ((id.msb << 8) | id.lsb | (id.percussive ? size_t(OPL3::PercussionTag) : 0)); - MIDIplay *play = reinterpret_cast(device->adl_midiPlayer); - OPL3::BankMap &map = play->opl.dynamic_banks; + MidiPlayer *play = GET_MIDI_PLAYER(device); + assert(play); + OPL3::BankMap &map = play->m_synth.m_insBanks; OPL3::BankMap::iterator it; if(!(flags & ADLMIDI_Bank_Create)) @@ -175,7 +231,7 @@ ADLMIDI_EXPORT int adl_getBank(ADL_MIDIPlayer *device, const ADL_BankId *idp, in } else { - std::pair value; + std::pair value; value.first = idnumber; memset(&value.second, 0, sizeof(value.second)); for (unsigned i = 0; i < 128; ++i) @@ -203,7 +259,7 @@ ADLMIDI_EXPORT int adl_getBankId(ADL_MIDIPlayer *device, const ADL_Bank *bank, A return -1; OPL3::BankMap::iterator it = OPL3::BankMap::iterator::from_ptrs(bank->pointer); - unsigned idnumber = it->first; + OPL3::BankMap::key_type idnumber = it->first; id->msb = (idnumber >> 8) & 127; id->lsb = idnumber & 127; id->percussive = (idnumber & OPL3::PercussionTag) ? 1 : 0; @@ -215,8 +271,9 @@ ADLMIDI_EXPORT int adl_removeBank(ADL_MIDIPlayer *device, ADL_Bank *bank) if(!device || !bank) return -1; - MIDIplay *play = reinterpret_cast(device->adl_midiPlayer); - OPL3::BankMap &map = play->opl.dynamic_banks; + MidiPlayer *play = GET_MIDI_PLAYER(device); + assert(play); + OPL3::BankMap &map = play->m_synth.m_insBanks; OPL3::BankMap::iterator it = OPL3::BankMap::iterator::from_ptrs(bank->pointer); size_t size = map.size(); map.erase(it); @@ -228,8 +285,9 @@ ADLMIDI_EXPORT int adl_getFirstBank(ADL_MIDIPlayer *device, ADL_Bank *bank) if(!device) return -1; - MIDIplay *play = reinterpret_cast(device->adl_midiPlayer); - OPL3::BankMap &map = play->opl.dynamic_banks; + MidiPlayer *play = GET_MIDI_PLAYER(device); + assert(play); + OPL3::BankMap &map = play->m_synth.m_insBanks; OPL3::BankMap::iterator it = map.begin(); if(it == map.end()) @@ -244,8 +302,9 @@ ADLMIDI_EXPORT int adl_getNextBank(ADL_MIDIPlayer *device, ADL_Bank *bank) if(!device) return -1; - MIDIplay *play = reinterpret_cast(device->adl_midiPlayer); - OPL3::BankMap &map = play->opl.dynamic_banks; + MidiPlayer *play = GET_MIDI_PLAYER(device); + assert(play); + OPL3::BankMap &map = play->m_synth.m_insBanks; OPL3::BankMap::iterator it = OPL3::BankMap::iterator::from_ptrs(bank->pointer); if(++it == map.end()) @@ -258,7 +317,7 @@ ADLMIDI_EXPORT int adl_getNextBank(ADL_MIDIPlayer *device, ADL_Bank *bank) ADLMIDI_EXPORT int adl_getInstrument(ADL_MIDIPlayer *device, const ADL_Bank *bank, unsigned index, ADL_Instrument *ins) { if(!device || !bank || index > 127 || !ins) - return 1; + return -1; OPL3::BankMap::iterator it = OPL3::BankMap::iterator::from_ptrs(bank->pointer); cvt_FMIns_to_ADLI(*ins, it->second.ins[index]); @@ -269,130 +328,248 @@ ADLMIDI_EXPORT int adl_getInstrument(ADL_MIDIPlayer *device, const ADL_Bank *ban ADLMIDI_EXPORT int adl_setInstrument(ADL_MIDIPlayer *device, ADL_Bank *bank, unsigned index, const ADL_Instrument *ins) { if(!device || !bank || index > 127 || !ins) - return 1; + return -1; if(ins->version != 0) - return 1; + return -1; OPL3::BankMap::iterator it = OPL3::BankMap::iterator::from_ptrs(bank->pointer); cvt_ADLI_to_FMIns(it->second.ins[index], *ins); return 0; } +ADLMIDI_EXPORT int adl_loadEmbeddedBank(struct ADL_MIDIPlayer *device, ADL_Bank *bank, int num) +{ + if(!device) + return -1; + +#ifdef DISABLE_EMBEDDED_BANKS + ADL_UNUSED(bank); + ADL_UNUSED(num); + MidiPlayer *play = GET_MIDI_PLAYER(device); + assert(play); + play->setErrorString("This build of libADLMIDI has no embedded banks. " + "Please load banks by using adl_openBankFile() or " + "adl_openBankData() functions instead of adl_loadEmbeddedBank()."); + return -1; +#else + if(num < 0 || num >= maxAdlBanks()) + return -1; + + OPL3::BankMap::iterator it = OPL3::BankMap::iterator::from_ptrs(bank->pointer); + size_t id = it->first; + + for (unsigned i = 0; i < 128; ++i) { + size_t insno = i + ((id & OPL3::PercussionTag) ? 128 : 0); + size_t adlmeta = ::banks[num][insno]; + it->second.ins[i] = adlinsdata2::from_adldata(::adlins[adlmeta]); + } + return 0; +#endif +} + ADLMIDI_EXPORT int adl_setNumFourOpsChn(ADL_MIDIPlayer *device, int ops4) { if(!device) return -1; - MIDIplay *play = reinterpret_cast(device->adl_midiPlayer); - if((unsigned int)ops4 > 6 * play->m_setup.NumCards) + + MidiPlayer *play = GET_MIDI_PLAYER(device); + assert(play); + if(ops4 > 6 * static_cast(play->m_setup.numChips)) { char errBuff[250]; - snprintf(errBuff, 250, "number of four-op channels may only be 0..%u when %u OPL3 cards are used.\n", (6 * (play->m_setup.NumCards)), play->m_setup.NumCards); + snprintf(errBuff, 250, "number of four-op channels may only be 0..%u when %u OPL3 cards are used.\n", (6 * (play->m_setup.numChips)), play->m_setup.numChips); play->setErrorString(errBuff); return -1; } - play->m_setup.NumFourOps = static_cast(ops4); - play->opl.NumFourOps = play->m_setup.NumFourOps; + play->m_setup.numFourOps = ops4; + if(!play->m_synth.setupLocked()) + { + if(play->m_setup.numFourOps < 0) + adlCalculateFourOpChannels(play, true); + else + play->m_synth.m_numFourOps = static_cast(play->m_setup.numFourOps); + play->m_synth.updateChannelCategories(); + } - return 0; //adlRefreshNumCards(device); + return 0; } ADLMIDI_EXPORT int adl_getNumFourOpsChn(struct ADL_MIDIPlayer *device) { if(!device) - return -1; - MIDIplay *play = reinterpret_cast(device->adl_midiPlayer); - if(play) - return (int)play->m_setup.NumFourOps; - return -1; + return -2; + MidiPlayer *play = GET_MIDI_PLAYER(device); + assert(play); + return play->m_setup.numFourOps; } +ADLMIDI_EXPORT int adl_getNumFourOpsChnObtained(struct ADL_MIDIPlayer *device) +{ + if(!device) + return -2; + MidiPlayer *play = GET_MIDI_PLAYER(device); + assert(play); + return (int)play->m_synth.m_numFourOps; +} + + ADLMIDI_EXPORT void adl_setPercMode(ADL_MIDIPlayer *device, int percmod) { if(!device) return; - MIDIplay *play = reinterpret_cast(device->adl_midiPlayer); - play->m_setup.AdlPercussionMode = percmod; - play->opl.AdlPercussionMode = play->m_setup.AdlPercussionMode < 0 ? - play->opl.dynamic_bank_setup.adLibPercussions : - (play->m_setup.AdlPercussionMode != 0); - play->opl.updateFlags(); + MidiPlayer *play = GET_MIDI_PLAYER(device); + assert(play); + play->m_setup.rhythmMode = percmod; + if(!play->m_synth.setupLocked()) + { + play->m_synth.m_rhythmMode = play->m_setup.rhythmMode < 0 ? + (play->m_synth.m_insBankSetup.adLibPercussions) : + (play->m_setup.rhythmMode != 0); + play->m_synth.updateChannelCategories(); + } } ADLMIDI_EXPORT void adl_setHVibrato(ADL_MIDIPlayer *device, int hvibro) { if(!device) return; - MIDIplay *play = reinterpret_cast(device->adl_midiPlayer); - play->m_setup.HighVibratoMode = hvibro; - play->opl.HighVibratoMode = play->m_setup.HighVibratoMode < 0 ? - play->opl.dynamic_bank_setup.deepVibrato : - (play->m_setup.HighVibratoMode != 0); - play->opl.updateDeepFlags(); + MidiPlayer *play = GET_MIDI_PLAYER(device); + assert(play); + play->m_setup.deepVibratoMode = hvibro; + if(!play->m_synth.setupLocked()) + { + play->m_synth.m_deepVibratoMode = play->m_setup.deepVibratoMode < 0 ? + play->m_synth.m_insBankSetup.deepVibrato : + (play->m_setup.deepVibratoMode != 0); + play->m_synth.commitDeepFlags(); + } +} + +ADLMIDI_EXPORT int adl_getHVibrato(struct ADL_MIDIPlayer *device) +{ + if(!device) return -1; + MidiPlayer *play = GET_MIDI_PLAYER(device); + assert(play); + return play->m_synth.m_deepVibratoMode; } ADLMIDI_EXPORT void adl_setHTremolo(ADL_MIDIPlayer *device, int htremo) { if(!device) return; - MIDIplay *play = reinterpret_cast(device->adl_midiPlayer); - play->m_setup.HighTremoloMode = htremo; - play->opl.HighTremoloMode = play->m_setup.HighTremoloMode < 0 ? - play->opl.dynamic_bank_setup.deepTremolo : - (play->m_setup.HighTremoloMode != 0); - play->opl.updateDeepFlags(); + MidiPlayer *play = GET_MIDI_PLAYER(device); + assert(play); + play->m_setup.deepTremoloMode = htremo; + if(!play->m_synth.setupLocked()) + { + play->m_synth.m_deepTremoloMode = play->m_setup.deepTremoloMode < 0 ? + play->m_synth.m_insBankSetup.deepTremolo : + (play->m_setup.deepTremoloMode != 0); + play->m_synth.commitDeepFlags(); + } +} + +ADLMIDI_EXPORT int adl_getHTremolo(struct ADL_MIDIPlayer *device) +{ + if(!device) return -1; + MidiPlayer *play = GET_MIDI_PLAYER(device); + assert(play); + return play->m_synth.m_deepTremoloMode; } ADLMIDI_EXPORT void adl_setScaleModulators(ADL_MIDIPlayer *device, int smod) { - if(!device) return; - MIDIplay *play = reinterpret_cast(device->adl_midiPlayer); - play->m_setup.ScaleModulators = smod; - play->opl.ScaleModulators = play->m_setup.ScaleModulators < 0 ? - play->opl.dynamic_bank_setup.scaleModulators : - (play->m_setup.ScaleModulators != 0); + if(!device) + return; + MidiPlayer *play = GET_MIDI_PLAYER(device); + assert(play); + play->m_setup.scaleModulators = smod; + if(!play->m_synth.setupLocked()) + { + play->m_synth.m_scaleModulators = play->m_setup.scaleModulators < 0 ? + play->m_synth.m_insBankSetup.scaleModulators : + (play->m_setup.scaleModulators != 0); + } } ADLMIDI_EXPORT void adl_setFullRangeBrightness(struct ADL_MIDIPlayer *device, int fr_brightness) { - if(!device) return; - MIDIplay *play = reinterpret_cast(device->adl_midiPlayer); + if(!device) + return; + MidiPlayer *play = GET_MIDI_PLAYER(device); + assert(play); play->m_setup.fullRangeBrightnessCC74 = (fr_brightness != 0); } ADLMIDI_EXPORT void adl_setLoopEnabled(ADL_MIDIPlayer *device, int loopEn) { - if(!device) return; - MIDIplay *play = reinterpret_cast(device->adl_midiPlayer); - play->m_setup.loopingIsEnabled = (loopEn != 0); + if(!device) + return; + MidiPlayer *play = GET_MIDI_PLAYER(device); + assert(play); +#ifndef ADLMIDI_DISABLE_MIDI_SEQUENCER + play->m_sequencer.setLoopEnabled(loopEn != 0); +#else + ADL_UNUSED(loopEn); +#endif +} + +ADLMIDI_EXPORT void adl_setSoftPanEnabled(ADL_MIDIPlayer *device, int softPanEn) +{ + if(!device) + return; + MidiPlayer *play = GET_MIDI_PLAYER(device); + assert(play); + play->m_synth.m_softPanning = (softPanEn != 0); } /* !!!DEPRECATED!!! */ ADLMIDI_EXPORT void adl_setLogarithmicVolumes(struct ADL_MIDIPlayer *device, int logvol) { - if(!device) return; - MIDIplay *play = reinterpret_cast(device->adl_midiPlayer); - play->m_setup.LogarithmicVolumes = (logvol != 0); - if(play->m_setup.LogarithmicVolumes) - play->opl.ChangeVolumeRangesModel(ADLMIDI_VolumeModel_NativeOPL3); - else - play->opl.ChangeVolumeRangesModel(static_cast(play->opl.m_volumeScale)); + if(!device) + return; + MidiPlayer *play = GET_MIDI_PLAYER(device); + assert(play); + play->m_setup.logarithmicVolumes = (logvol != 0); + if(!play->m_synth.setupLocked()) + { + if(play->m_setup.logarithmicVolumes) + play->m_synth.setVolumeScaleModel(ADLMIDI_VolumeModel_NativeOPL3); + else + play->m_synth.setVolumeScaleModel(static_cast(play->m_synth.m_volumeScale)); + } } ADLMIDI_EXPORT void adl_setVolumeRangeModel(struct ADL_MIDIPlayer *device, int volumeModel) { - if(!device) return; - MIDIplay *play = reinterpret_cast(device->adl_midiPlayer); - play->m_setup.VolumeModel = volumeModel; - if(play->m_setup.VolumeModel == ADLMIDI_VolumeModel_AUTO)//Use bank default volume model - play->opl.m_volumeScale = (OPL3::VolumesScale)play->opl.dynamic_bank_setup.volumeModel; - else - play->opl.ChangeVolumeRangesModel(static_cast(volumeModel)); + if(!device) + return; + MidiPlayer *play = GET_MIDI_PLAYER(device); + assert(play); + play->m_setup.volumeScaleModel = volumeModel; + if(!play->m_synth.setupLocked()) + { + if(play->m_setup.volumeScaleModel == ADLMIDI_VolumeModel_AUTO)//Use bank default volume model + play->m_synth.m_volumeScale = (OPL3::VolumesScale)play->m_synth.m_insBankSetup.volumeModel; + else + play->m_synth.setVolumeScaleModel(static_cast(volumeModel)); + } +} + +ADLMIDI_EXPORT int adl_getVolumeRangeModel(struct ADL_MIDIPlayer *device) +{ + if(!device) + return -1; + MidiPlayer *play = GET_MIDI_PLAYER(device); + assert(play); + return play->m_synth.getVolumeScaleModel(); } ADLMIDI_EXPORT int adl_openBankFile(struct ADL_MIDIPlayer *device, const char *filePath) { - if(device && device->adl_midiPlayer) + if(device) { - MIDIplay *play = reinterpret_cast(device->adl_midiPlayer); + MidiPlayer *play = GET_MIDI_PLAYER(device); + assert(play); play->m_setup.tick_skip_samples_delay = 0; if(!play->LoadBank(filePath)) { @@ -401,7 +578,8 @@ ADLMIDI_EXPORT int adl_openBankFile(struct ADL_MIDIPlayer *device, const char *f play->setErrorString("ADL MIDI: Can't load file"); return -1; } - else return adlRefreshNumCards(device); + else + return adlCalculateFourOpChannels(play, true); } ADLMIDI_ErrorString = "Can't load file: ADLMIDI is not initialized"; @@ -410,9 +588,10 @@ ADLMIDI_EXPORT int adl_openBankFile(struct ADL_MIDIPlayer *device, const char *f ADLMIDI_EXPORT int adl_openBankData(struct ADL_MIDIPlayer *device, const void *mem, unsigned long size) { - if(device && device->adl_midiPlayer) + if(device) { - MIDIplay *play = reinterpret_cast(device->adl_midiPlayer); + MidiPlayer *play = GET_MIDI_PLAYER(device); + assert(play); play->m_setup.tick_skip_samples_delay = 0; if(!play->LoadBank(mem, static_cast(size))) { @@ -421,7 +600,8 @@ ADLMIDI_EXPORT int adl_openBankData(struct ADL_MIDIPlayer *device, const void *m play->setErrorString("ADL MIDI: Can't load data from memory"); return -1; } - else return adlRefreshNumCards(device); + else + return adlCalculateFourOpChannels(play, true); } ADLMIDI_ErrorString = "Can't load file: ADL MIDI is not initialized"; @@ -430,9 +610,10 @@ ADLMIDI_EXPORT int adl_openBankData(struct ADL_MIDIPlayer *device, const void *m ADLMIDI_EXPORT int adl_openFile(ADL_MIDIPlayer *device, const char *filePath) { - if(device && device->adl_midiPlayer) + if(device) { - MIDIplay *play = reinterpret_cast(device->adl_midiPlayer); + MidiPlayer *play = GET_MIDI_PLAYER(device); + assert(play); #ifndef ADLMIDI_DISABLE_MIDI_SEQUENCER play->m_setup.tick_skip_samples_delay = 0; if(!play->LoadMIDI(filePath)) @@ -444,7 +625,7 @@ ADLMIDI_EXPORT int adl_openFile(ADL_MIDIPlayer *device, const char *filePath) } else return 0; #else - (void)filePath; + ADL_UNUSED(filePath); play->setErrorString("ADLMIDI: MIDI Sequencer is not supported in this build of library!"); return -1; #endif //ADLMIDI_DISABLE_MIDI_SEQUENCER @@ -456,9 +637,10 @@ ADLMIDI_EXPORT int adl_openFile(ADL_MIDIPlayer *device, const char *filePath) ADLMIDI_EXPORT int adl_openData(ADL_MIDIPlayer *device, const void *mem, unsigned long size) { - if(device && device->adl_midiPlayer) + if(device) { - MIDIplay *play = reinterpret_cast(device->adl_midiPlayer); + MidiPlayer *play = GET_MIDI_PLAYER(device); + assert(play); #ifndef ADLMIDI_DISABLE_MIDI_SEQUENCER play->m_setup.tick_skip_samples_delay = 0; if(!play->LoadMIDI(mem, static_cast(size))) @@ -470,7 +652,8 @@ ADLMIDI_EXPORT int adl_openData(ADL_MIDIPlayer *device, const void *mem, unsigne } else return 0; #else - (void)mem;(void)size; + ADL_UNUSED(mem); + ADL_UNUSED(size); play->setErrorString("ADLMIDI: MIDI Sequencer is not supported in this build of library!"); return -1; #endif //ADLMIDI_DISABLE_MIDI_SEQUENCER @@ -489,13 +672,14 @@ ADLMIDI_EXPORT const char *adl_chipEmulatorName(struct ADL_MIDIPlayer *device) { if(device) { - #ifndef ADLMIDI_HW_OPL - MIDIplay *play = reinterpret_cast(device->adl_midiPlayer); - if(play && !play->opl.cardsOP2.empty()) - return play->opl.cardsOP2[0]->emulatorName(); - #else +#ifndef ADLMIDI_HW_OPL + MidiPlayer *play = GET_MIDI_PLAYER(device); + assert(play); + if(!play->m_synth.m_chips.empty()) + return play->m_synth.m_chips[0]->emulatorName(); +#else return "Hardware OPL3 chip on 0x330"; - #endif +#endif } return "Unknown"; } @@ -504,11 +688,12 @@ ADLMIDI_EXPORT int adl_switchEmulator(struct ADL_MIDIPlayer *device, int emulato { if(device) { - MIDIplay *play = reinterpret_cast(device->adl_midiPlayer); - if(play && (emulator >= 0) && (emulator < ADLMIDI_EMU_end)) + MidiPlayer *play = GET_MIDI_PLAYER(device); + assert(play); + if(adl_isEmulatorAvailable(emulator)) { play->m_setup.emulator = emulator; - adl_reset(device); + play->partialReset(); return 0; } play->setErrorString("OPL3 MIDI: Unknown emulation core!"); @@ -521,13 +706,12 @@ ADLMIDI_EXPORT int adl_setRunAtPcmRate(ADL_MIDIPlayer *device, int enabled) { if(device) { - MIDIplay *play = reinterpret_cast(device->adl_midiPlayer); - if(play) - { - play->m_setup.runAtPcmRate = (enabled != 0); - adl_reset(device); - return 0; - } + MidiPlayer *play = GET_MIDI_PLAYER(device); + assert(play); + play->m_setup.runAtPcmRate = (enabled != 0); + if(!play->m_synth.setupLocked()) + play->partialReset(); + return 0; } return -1; } @@ -556,164 +740,193 @@ ADLMIDI_EXPORT const char *adl_errorInfo(struct ADL_MIDIPlayer *device) { if(!device) return adl_errorString(); - MIDIplay *play = reinterpret_cast(device->adl_midiPlayer); + MidiPlayer *play = GET_MIDI_PLAYER(device); if(!play) return adl_errorString(); return play->getErrorString().c_str(); } -ADLMIDI_EXPORT const char *adl_getMusicTitle(struct ADL_MIDIPlayer *device) -{ - if(!device) - return ""; - MIDIplay *play = reinterpret_cast(device->adl_midiPlayer); - if(!play) - return ""; - #ifndef ADLMIDI_DISABLE_MIDI_SEQUENCER - return play->musTitle.c_str(); - #else - return ""; - #endif -} - -ADLMIDI_EXPORT void adl_close(struct ADL_MIDIPlayer *device) -{ - if(device->adl_midiPlayer) - delete reinterpret_cast(device->adl_midiPlayer); - device->adl_midiPlayer = NULL; - free(device); - device = NULL; -} - ADLMIDI_EXPORT void adl_reset(struct ADL_MIDIPlayer *device) { if(!device) return; - MIDIplay *play = reinterpret_cast(device->adl_midiPlayer); - play->m_setup.tick_skip_samples_delay = 0; - play->opl.runAtPcmRate = play->m_setup.runAtPcmRate; - play->opl.Reset(play->m_setup.emulator, play->m_setup.PCM_RATE, play); - play->ch.clear(); - play->ch.resize((size_t)play->opl.NumChannels); + MidiPlayer *play = GET_MIDI_PLAYER(device); + assert(play); + play->partialReset(); + play->resetMIDI(); } ADLMIDI_EXPORT double adl_totalTimeLength(struct ADL_MIDIPlayer *device) { +#ifndef ADLMIDI_DISABLE_MIDI_SEQUENCER if(!device) return -1.0; - #ifndef ADLMIDI_DISABLE_MIDI_SEQUENCER - return reinterpret_cast(device->adl_midiPlayer)->timeLength(); - #else + MidiPlayer *play = GET_MIDI_PLAYER(device); + assert(play); + return play->m_sequencer.timeLength(); +#else + ADL_UNUSED(device); return -1.0; - #endif +#endif } ADLMIDI_EXPORT double adl_loopStartTime(struct ADL_MIDIPlayer *device) { +#ifndef ADLMIDI_DISABLE_MIDI_SEQUENCER if(!device) return -1.0; - #ifndef ADLMIDI_DISABLE_MIDI_SEQUENCER - return reinterpret_cast(device->adl_midiPlayer)->getLoopStart(); - #else + MidiPlayer *play = GET_MIDI_PLAYER(device); + assert(play); + return play->m_sequencer.getLoopStart(); +#else + ADL_UNUSED(device); return -1.0; - #endif +#endif } ADLMIDI_EXPORT double adl_loopEndTime(struct ADL_MIDIPlayer *device) { +#ifndef ADLMIDI_DISABLE_MIDI_SEQUENCER if(!device) return -1.0; - #ifndef ADLMIDI_DISABLE_MIDI_SEQUENCER - return reinterpret_cast(device->adl_midiPlayer)->getLoopEnd(); - #else + MidiPlayer *play = GET_MIDI_PLAYER(device); + assert(play); + return play->m_sequencer.getLoopEnd(); +#else + ADL_UNUSED(device); return -1.0; - #endif +#endif } ADLMIDI_EXPORT double adl_positionTell(struct ADL_MIDIPlayer *device) { +#ifndef ADLMIDI_DISABLE_MIDI_SEQUENCER if(!device) return -1.0; - #ifndef ADLMIDI_DISABLE_MIDI_SEQUENCER - return reinterpret_cast(device->adl_midiPlayer)->tell(); - #else + MidiPlayer *play = GET_MIDI_PLAYER(device); + assert(play); + return play->m_sequencer.tell(); +#else + ADL_UNUSED(device); return -1.0; - #endif +#endif } ADLMIDI_EXPORT void adl_positionSeek(struct ADL_MIDIPlayer *device, double seconds) { +#ifndef ADLMIDI_DISABLE_MIDI_SEQUENCER + if(seconds < 0.0) + return;//Seeking negative position is forbidden! :-P if(!device) return; - #ifndef ADLMIDI_DISABLE_MIDI_SEQUENCER - reinterpret_cast(device->adl_midiPlayer)->seek(seconds); - #endif + MidiPlayer *play = GET_MIDI_PLAYER(device); + assert(play); + play->realTime_panic(); + play->m_setup.delay = play->m_sequencer.seek(seconds, play->m_setup.mindelay); + play->m_setup.carry = 0.0; +#else + ADL_UNUSED(device); + ADL_UNUSED(seconds); +#endif } ADLMIDI_EXPORT void adl_positionRewind(struct ADL_MIDIPlayer *device) { +#ifndef ADLMIDI_DISABLE_MIDI_SEQUENCER if(!device) return; - #ifndef ADLMIDI_DISABLE_MIDI_SEQUENCER - reinterpret_cast(device->adl_midiPlayer)->rewind(); - #endif + MidiPlayer *play = GET_MIDI_PLAYER(device); + assert(play); + play->realTime_panic(); + play->m_sequencer.rewind(); +#else + ADL_UNUSED(device); +#endif } ADLMIDI_EXPORT void adl_setTempo(struct ADL_MIDIPlayer *device, double tempo) { +#ifndef ADLMIDI_DISABLE_MIDI_SEQUENCER if(!device || (tempo <= 0.0)) return; - #ifndef ADLMIDI_DISABLE_MIDI_SEQUENCER - reinterpret_cast(device->adl_midiPlayer)->setTempo(tempo); - #endif + MidiPlayer *play = GET_MIDI_PLAYER(device); + assert(play); + play->m_sequencer.setTempo(tempo); +#else + ADL_UNUSED(device); + ADL_UNUSED(tempo); +#endif +} + + +ADLMIDI_EXPORT int adl_describeChannels(struct ADL_MIDIPlayer *device, char *str, char *attr, size_t size) +{ + if(!device) + return -1; + MidiPlayer *play = GET_MIDI_PLAYER(device); + assert(play); + play->describeChannels(str, attr, size); + return 0; } ADLMIDI_EXPORT const char *adl_metaMusicTitle(struct ADL_MIDIPlayer *device) { +#ifndef ADLMIDI_DISABLE_MIDI_SEQUENCER if(!device) return ""; - #ifndef ADLMIDI_DISABLE_MIDI_SEQUENCER - return reinterpret_cast(device->adl_midiPlayer)->musTitle.c_str(); - #else + MidiPlayer *play = GET_MIDI_PLAYER(device); + assert(play); + return play->m_sequencer.getMusicTitle().c_str(); +#else + ADL_UNUSED(device); return ""; - #endif +#endif } ADLMIDI_EXPORT const char *adl_metaMusicCopyright(struct ADL_MIDIPlayer *device) { +#ifndef ADLMIDI_DISABLE_MIDI_SEQUENCER if(!device) return ""; - #ifndef ADLMIDI_DISABLE_MIDI_SEQUENCER - return reinterpret_cast(device->adl_midiPlayer)->musCopyright.c_str(); - #else + MidiPlayer *play = GET_MIDI_PLAYER(device); + assert(play); + return play->m_sequencer.getMusicCopyright().c_str(); +#else + ADL_UNUSED(device); return ""; - #endif +#endif } ADLMIDI_EXPORT size_t adl_metaTrackTitleCount(struct ADL_MIDIPlayer *device) { +#ifndef ADLMIDI_DISABLE_MIDI_SEQUENCER if(!device) return 0; -#ifndef ADLMIDI_DISABLE_MIDI_SEQUENCER - return reinterpret_cast(device->adl_midiPlayer)->musTrackTitles.size(); + MidiPlayer *play = GET_MIDI_PLAYER(device); + assert(play); + return play->m_sequencer.getTrackTitles().size(); #else + ADL_UNUSED(device); return 0; #endif } ADLMIDI_EXPORT const char *adl_metaTrackTitle(struct ADL_MIDIPlayer *device, size_t index) { - if(!device) - return 0; #ifndef ADLMIDI_DISABLE_MIDI_SEQUENCER - MIDIplay *play = reinterpret_cast(device->adl_midiPlayer); - if(index >= play->musTrackTitles.size()) + if(!device) + return ""; + MidiPlayer *play = GET_MIDI_PLAYER(device); + assert(play); + const std::vector &titles = play->m_sequencer.getTrackTitles(); + if(index >= titles.size()) return "INVALID"; - return play->musTrackTitles[index].c_str(); + return titles[index].c_str(); #else - (void)device; (void)index; + ADL_UNUSED(device); + ADL_UNUSED(index); return "NOT SUPPORTED"; #endif } @@ -721,11 +934,14 @@ ADLMIDI_EXPORT const char *adl_metaTrackTitle(struct ADL_MIDIPlayer *device, siz ADLMIDI_EXPORT size_t adl_metaMarkerCount(struct ADL_MIDIPlayer *device) { +#ifndef ADLMIDI_DISABLE_MIDI_SEQUENCER if(!device) return 0; -#ifndef ADLMIDI_DISABLE_MIDI_SEQUENCER - return reinterpret_cast(device->adl_midiPlayer)->musMarkers.size(); + MidiPlayer *play = GET_MIDI_PLAYER(device); + assert(play); + return play->m_sequencer.getMarkers().size(); #else + ADL_UNUSED(device); return 0; #endif } @@ -733,38 +949,56 @@ ADLMIDI_EXPORT size_t adl_metaMarkerCount(struct ADL_MIDIPlayer *device) ADLMIDI_EXPORT Adl_MarkerEntry adl_metaMarker(struct ADL_MIDIPlayer *device, size_t index) { struct Adl_MarkerEntry marker; - #ifndef ADLMIDI_DISABLE_MIDI_SEQUENCER - MIDIplay *play = reinterpret_cast(device->adl_midiPlayer); - if(!device || !play || (index >= play->musMarkers.size())) + +#ifndef ADLMIDI_DISABLE_MIDI_SEQUENCER + if(!device) { marker.label = "INVALID"; marker.pos_time = 0.0; marker.pos_ticks = 0; return marker; } - else + + MidiPlayer *play = GET_MIDI_PLAYER(device); + assert(play); + + const std::vector &markers = play->m_sequencer.getMarkers(); + if(index >= markers.size()) { - MIDIplay::MIDI_MarkerEntry &mk = play->musMarkers[index]; - marker.label = mk.label.c_str(); - marker.pos_time = mk.pos_time; - marker.pos_ticks = (unsigned long)mk.pos_ticks; + marker.label = "INVALID"; + marker.pos_time = 0.0; + marker.pos_ticks = 0; + return marker; } - #else - (void)device; (void)index; + + const MidiSequencer::MIDI_MarkerEntry &mk = markers[index]; + marker.label = mk.label.c_str(); + marker.pos_time = mk.pos_time; + marker.pos_ticks = (unsigned long)mk.pos_ticks; +#else + ADL_UNUSED(device); + ADL_UNUSED(index); marker.label = "NOT SUPPORTED"; marker.pos_time = 0.0; marker.pos_ticks = 0; - #endif +#endif return marker; } ADLMIDI_EXPORT void adl_setRawEventHook(struct ADL_MIDIPlayer *device, ADL_RawEventHook rawEventHook, void *userData) { +#ifndef ADLMIDI_DISABLE_MIDI_SEQUENCER if(!device) return; - MIDIplay *play = reinterpret_cast(device->adl_midiPlayer); - play->hooks.onEvent = rawEventHook; - play->hooks.onEvent_userData = userData; + MidiPlayer *play = GET_MIDI_PLAYER(device); + assert(play); + play->m_sequencerInterface.onEvent = rawEventHook; + play->m_sequencerInterface.onEvent_userData = userData; +#else + ADL_UNUSED(device); + ADL_UNUSED(rawEventHook); + ADL_UNUSED(userData); +#endif } /* Set note hook */ @@ -772,7 +1006,8 @@ ADLMIDI_EXPORT void adl_setNoteHook(struct ADL_MIDIPlayer *device, ADL_NoteHook { if(!device) return; - MIDIplay *play = reinterpret_cast(device->adl_midiPlayer); + MidiPlayer *play = GET_MIDI_PLAYER(device); + assert(play); play->hooks.onNote = noteHook; play->hooks.onNote_userData = userData; } @@ -782,12 +1017,19 @@ ADLMIDI_EXPORT void adl_setDebugMessageHook(struct ADL_MIDIPlayer *device, ADL_D { if(!device) return; - MIDIplay *play = reinterpret_cast(device->adl_midiPlayer); + MidiPlayer *play = GET_MIDI_PLAYER(device); + assert(play); play->hooks.onDebugMessage = debugMessageHook; play->hooks.onDebugMessage_userData = userData; +#ifndef ADLMIDI_DISABLE_MIDI_SEQUENCER + play->m_sequencerInterface.onDebugMessage = debugMessageHook; + play->m_sequencerInterface.onDebugMessage_userData = userData; +#endif } #ifndef ADLMIDI_HW_OPL + +# ifndef __WATCOMC__ template static void CopySamplesRaw(ADL_UInt8 *dstLeft, ADL_UInt8 *dstRight, const int32_t *src, size_t frameCount, unsigned sampleOffset) @@ -804,8 +1046,8 @@ static void CopySamplesTransformed(ADL_UInt8 *dstLeft, ADL_UInt8 *dstRight, cons Ret(&transform)(int32_t)) { for(size_t i = 0; i < frameCount; ++i) { - *(Dst *)(dstLeft + (i * sampleOffset)) = (Dst)transform(src[2 * i]); - *(Dst *)(dstRight + (i * sampleOffset)) = (Dst)transform(src[(2 * i) + 1]); + *(Dst *)(dstLeft + (i * sampleOffset)) = static_cast(transform(src[2 * i])); + *(Dst *)(dstRight + (i * sampleOffset)) = static_cast(transform(src[(2 * i) + 1])); } } @@ -919,7 +1161,61 @@ static int SendStereoAudio(int samples_requested, return 0; } -#endif +# else // __WATCOMC__ + +/* + Workaround for OpenWattcom where templates are declared above are causing compiler to be crashed +*/ +static void CopySamplesTransformed(ADL_UInt8 *dstLeft, ADL_UInt8 *dstRight, const int32_t *src, + size_t frameCount, unsigned sampleOffset, + int32_t(&transform)(int32_t)) +{ + for(size_t i = 0; i < frameCount; ++i) { + *(int16_t *)(dstLeft + (i * sampleOffset)) = (int16_t)transform(src[2 * i]); + *(int16_t *)(dstRight + (i * sampleOffset)) = (int16_t)transform(src[(2 * i) + 1]); + } +} + +static int SendStereoAudio(int samples_requested, + ssize_t in_size, + int32_t *_in, + ssize_t out_pos, + ADL_UInt8 *left, + ADL_UInt8 *right, + const ADLMIDI_AudioFormat *format) +{ + if(!in_size) + return 0; + size_t outputOffset = static_cast(out_pos); + size_t inSamples = static_cast(in_size * 2); + size_t maxSamples = static_cast(samples_requested) - outputOffset; + size_t toCopy = std::min(maxSamples, inSamples); + + ADLMIDI_SampleType sampleType = format->type; + const unsigned containerSize = format->containerSize; + const unsigned sampleOffset = format->sampleOffset; + + left += (outputOffset / 2) * sampleOffset; + right += (outputOffset / 2) * sampleOffset; + + if(sampleType == ADLMIDI_SampleType_U16) + { + switch(containerSize) { + case sizeof(int16_t): + CopySamplesTransformed(left, right, _in, toCopy / 2, sampleOffset, adl_cvtS16); + break; + default: + return -1; + } + } + else + return -1; + return 0; +} +# endif // __WATCOM__ + +#endif // ADLMIDI_HW_OPL + ADLMIDI_EXPORT int adl_play(struct ADL_MIDIPlayer *device, int sampleCount, short *out) { @@ -930,23 +1226,25 @@ ADLMIDI_EXPORT int adl_playFormat(ADL_MIDIPlayer *device, int sampleCount, ADL_UInt8 *out_left, ADL_UInt8 *out_right, const ADLMIDI_AudioFormat *format) { - #ifndef ADLMIDI_DISABLE_MIDI_SEQUENCER - #ifdef ADLMIDI_HW_OPL - (void)device; - (void)sampleCount; - (void)out_left; - (void)out_right; - (void)format; +#if defined(ADLMIDI_DISABLE_MIDI_SEQUENCER) || defined(ADLMIDI_HW_OPL) + ADL_UNUSED(device); + ADL_UNUSED(sampleCount); + ADL_UNUSED(out_left); + ADL_UNUSED(out_right); + ADL_UNUSED(format); return 0; - #else +#endif + +#if !defined(ADLMIDI_DISABLE_MIDI_SEQUENCER) && !defined(ADLMIDI_HW_OPL) sampleCount -= sampleCount % 2; //Avoid even sample requests if(sampleCount < 0) return 0; if(!device) return 0; - MIDIplay *player = reinterpret_cast(device->adl_midiPlayer); - MIDIplay::Setup &setup = player->m_setup; + MidiPlayer *player = GET_MIDI_PLAYER(device); + assert(player); + MidiPlayer::Setup &setup = player->m_setup; ssize_t gotten_len = 0; ssize_t n_periodCountStereo = 512; @@ -966,16 +1264,16 @@ ADLMIDI_EXPORT int adl_playFormat(ADL_MIDIPlayer *device, int sampleCount, else { setup.delay -= eat_delay; - setup.carry += setup.PCM_RATE * eat_delay; + setup.carry += double(setup.PCM_RATE) * eat_delay; n_periodCountStereo = static_cast(setup.carry); - setup.carry -= n_periodCountStereo; + setup.carry -= double(n_periodCountStereo); } //if(setup.SkipForward > 0) // setup.SkipForward -= 1; //else { - if((player->atEnd) && (setup.delay <= 0.0)) + if((player->m_sequencer.positionAtEnd()) && (setup.delay <= 0.0)) break;//Stop to fetch samples at reaching the song end with disabled loop ssize_t leftSamples = left / 2; @@ -990,18 +1288,18 @@ ADLMIDI_EXPORT int adl_playFormat(ADL_MIDIPlayer *device, int sampleCount, ssize_t in_generatedPhys = in_generatedStereo * 2; //! Unsigned total sample count //fill buffer with zeros - int32_t *out_buf = player->outBuf; + int32_t *out_buf = player->m_outBuf; std::memset(out_buf, 0, static_cast(in_generatedPhys) * sizeof(out_buf[0])); - unsigned int chips = player->opl.NumCards; + unsigned int chips = player->m_synth.m_numChips; if(chips == 1) { - player->opl.cardsOP2[0]->generate32(out_buf, (size_t)in_generatedStereo); + player->m_synth.m_chips[0]->generate32(out_buf, (size_t)in_generatedStereo); } else if(n_periodCountStereo > 0) { /* Generate data from every chip and mix result */ for(size_t card = 0; card < chips; ++card) - player->opl.cardsOP2[card]->generateAndMix32(out_buf, (size_t)in_generatedStereo); + player->m_synth.m_chips[card]->generateAndMix32(out_buf, (size_t)in_generatedStereo); } /* Process it */ @@ -1024,10 +1322,7 @@ ADLMIDI_EXPORT int adl_playFormat(ADL_MIDIPlayer *device, int sampleCount, } return static_cast(gotten_len); - #endif - #else - return 0; - #endif //ADLMIDI_DISABLE_MIDI_SEQUENCER +#endif //ADLMIDI_DISABLE_MIDI_SEQUENCER } @@ -1040,22 +1335,23 @@ ADLMIDI_EXPORT int adl_generateFormat(struct ADL_MIDIPlayer *device, int sampleC ADL_UInt8 *out_left, ADL_UInt8 *out_right, const ADLMIDI_AudioFormat *format) { - #ifdef ADLMIDI_HW_OPL - (void)device; - (void)sampleCount; - (void)out_left; - (void)out_right; - (void)format; +#ifdef ADLMIDI_HW_OPL + ADL_UNUSED(device); + ADL_UNUSED(sampleCount); + ADL_UNUSED(out_left); + ADL_UNUSED(out_right); + ADL_UNUSED(format); return 0; - #else +#else sampleCount -= sampleCount % 2; //Avoid even sample requests if(sampleCount < 0) return 0; if(!device) return 0; - MIDIplay *player = reinterpret_cast(device->adl_midiPlayer); - MIDIplay::Setup &setup = player->m_setup; + MidiPlayer *player = GET_MIDI_PLAYER(device); + assert(player); + MidiPlayer::Setup &setup = player->m_setup; ssize_t gotten_len = 0; ssize_t n_periodCountStereo = 512; @@ -1068,9 +1364,9 @@ ADLMIDI_EXPORT int adl_generateFormat(struct ADL_MIDIPlayer *device, int sampleC {//... const double eat_delay = delay < setup.maxdelay ? delay : setup.maxdelay; delay -= eat_delay; - setup.carry += setup.PCM_RATE * eat_delay; + setup.carry += double(setup.PCM_RATE) * eat_delay; n_periodCountStereo = static_cast(setup.carry); - setup.carry -= n_periodCountStereo; + setup.carry -= double(n_periodCountStereo); { ssize_t leftSamples = left / 2; @@ -1082,16 +1378,16 @@ ADLMIDI_EXPORT int adl_generateFormat(struct ADL_MIDIPlayer *device, int sampleC ssize_t in_generatedPhys = in_generatedStereo * 2; //! Unsigned total sample count //fill buffer with zeros - int32_t *out_buf = player->outBuf; + int32_t *out_buf = player->m_outBuf; std::memset(out_buf, 0, static_cast(in_generatedPhys) * sizeof(out_buf[0])); - unsigned int chips = player->opl.NumCards; + unsigned int chips = player->m_synth.m_numChips; if(chips == 1) - player->opl.cardsOP2[0]->generate32(out_buf, (size_t)in_generatedStereo); + player->m_synth.m_chips[0]->generate32(out_buf, (size_t)in_generatedStereo); else if(n_periodCountStereo > 0) { /* Generate data from every chip and mix result */ for(unsigned card = 0; card < chips; ++card) - player->opl.cardsOP2[card]->generateAndMix32(out_buf, (size_t)in_generatedStereo); + player->m_synth.m_chips[card]->generateAndMix32(out_buf, (size_t)in_generatedStereo); } /* Process it */ if(SendStereoAudio(sampleCount, in_generatedStereo, out_buf, gotten_len, out_left, out_right, format) == -1) @@ -1101,25 +1397,26 @@ ADLMIDI_EXPORT int adl_generateFormat(struct ADL_MIDIPlayer *device, int sampleC gotten_len += (in_generatedPhys) /* - setup.stored_samples*/; } - player->TickIteratos(eat_delay); + player->TickIterators(eat_delay); }//... } return static_cast(gotten_len); - #endif +#endif } -ADLMIDI_EXPORT double adl_tickEvents(struct ADL_MIDIPlayer *device, double seconds, double granuality) +ADLMIDI_EXPORT double adl_tickEvents(struct ADL_MIDIPlayer *device, double seconds, double granulality) { #ifndef ADLMIDI_DISABLE_MIDI_SEQUENCER if(!device) return -1.0; - MIDIplay *player = reinterpret_cast(device->adl_midiPlayer); - if(!player) - return -1.0; - return player->Tick(seconds, granuality); + MidiPlayer *play = GET_MIDI_PLAYER(device); + assert(play); + return play->Tick(seconds, granulality); #else - (void)seconds; (void)granuality; + ADL_UNUSED(device); + ADL_UNUSED(seconds); + ADL_UNUSED(granulality); return -1.0; #endif } @@ -1129,141 +1426,210 @@ ADLMIDI_EXPORT int adl_atEnd(struct ADL_MIDIPlayer *device) #ifndef ADLMIDI_DISABLE_MIDI_SEQUENCER if(!device) return 1; - MIDIplay *player = reinterpret_cast(device->adl_midiPlayer); - if(!player) - return 1; - return (int)player->atEnd; + MidiPlayer *play = GET_MIDI_PLAYER(device); + assert(play); + return (int)play->m_sequencer.positionAtEnd(); #else + ADL_UNUSED(device); return 1; #endif } +ADLMIDI_EXPORT size_t adl_trackCount(struct ADL_MIDIPlayer *device) +{ +#ifndef ADLMIDI_DISABLE_MIDI_SEQUENCER + if(!device) + return 0; + MidiPlayer *play = GET_MIDI_PLAYER(device); + assert(play); + return play->m_sequencer.getTrackCount(); +#else + ADL_UNUSED(device); + return 0; +#endif +} + +ADLMIDI_EXPORT int adl_setTrackOptions(struct ADL_MIDIPlayer *device, size_t trackNumber, unsigned trackOptions) +{ +#ifndef ADLMIDI_DISABLE_MIDI_SEQUENCER + if(!device) + return -1; + MidiPlayer *play = GET_MIDI_PLAYER(device); + assert(play); + MidiSequencer &seq = play->m_sequencer; + + unsigned enableFlag = trackOptions & 3; + trackOptions &= ~3u; + + // handle on/off/solo + switch(enableFlag) + { + default: + break; + case ADLMIDI_TrackOption_On: + case ADLMIDI_TrackOption_Off: + if(!seq.setTrackEnabled(trackNumber, enableFlag == ADLMIDI_TrackOption_On)) + return -1; + break; + case ADLMIDI_TrackOption_Solo: + seq.setSoloTrack(trackNumber); + break; + } + + // handle others... + if(trackOptions != 0) + return -1; + + return 0; + +#else + ADL_UNUSED(device); + ADL_UNUSED(trackNumber); + ADL_UNUSED(trackOptions); + return -1; +#endif +} + +ADLMIDI_EXPORT int adl_setTriggerHandler(struct ADL_MIDIPlayer *device, ADL_TriggerHandler handler, void *userData) +{ +#ifndef ADLMIDI_DISABLE_MIDI_SEQUENCER + if(!device) + return -1; + MidiPlayer *play = GET_MIDI_PLAYER(device); + assert(play); + MidiSequencer &seq = play->m_sequencer; + seq.setTriggerHandler(handler, userData); + return 0; +#else + ADL_UNUSED(device); + ADL_UNUSED(handler); + ADL_UNUSED(userData); + return -1; +#endif +} + ADLMIDI_EXPORT void adl_panic(struct ADL_MIDIPlayer *device) { if(!device) return; - MIDIplay *player = reinterpret_cast(device->adl_midiPlayer); - if(!player) - return; - player->realTime_panic(); + MidiPlayer *play = GET_MIDI_PLAYER(device); + assert(play); + play->realTime_panic(); } ADLMIDI_EXPORT void adl_rt_resetState(struct ADL_MIDIPlayer *device) { if(!device) return; - MIDIplay *player = reinterpret_cast(device->adl_midiPlayer); - if(!player) - return; - player->realTime_ResetState(); + MidiPlayer *play = GET_MIDI_PLAYER(device); + assert(play); + play->realTime_ResetState(); } ADLMIDI_EXPORT int adl_rt_noteOn(struct ADL_MIDIPlayer *device, ADL_UInt8 channel, ADL_UInt8 note, ADL_UInt8 velocity) { if(!device) return 0; - MIDIplay *player = reinterpret_cast(device->adl_midiPlayer); - if(!player) - return 0; - return (int)player->realTime_NoteOn(channel, note, velocity); + MidiPlayer *play = GET_MIDI_PLAYER(device); + assert(play); + return (int)play->realTime_NoteOn(channel, note, velocity); } ADLMIDI_EXPORT void adl_rt_noteOff(struct ADL_MIDIPlayer *device, ADL_UInt8 channel, ADL_UInt8 note) { if(!device) return; - MIDIplay *player = reinterpret_cast(device->adl_midiPlayer); - if(!player) - return; - player->realTime_NoteOff(channel, note); + MidiPlayer *play = GET_MIDI_PLAYER(device); + assert(play); + play->realTime_NoteOff(channel, note); } ADLMIDI_EXPORT void adl_rt_noteAfterTouch(struct ADL_MIDIPlayer *device, ADL_UInt8 channel, ADL_UInt8 note, ADL_UInt8 atVal) { if(!device) return; - MIDIplay *player = reinterpret_cast(device->adl_midiPlayer); - if(!player) - return; - player->realTime_NoteAfterTouch(channel, note, atVal); + MidiPlayer *play = GET_MIDI_PLAYER(device); + assert(play); + play->realTime_NoteAfterTouch(channel, note, atVal); } ADLMIDI_EXPORT void adl_rt_channelAfterTouch(struct ADL_MIDIPlayer *device, ADL_UInt8 channel, ADL_UInt8 atVal) { if(!device) return; - MIDIplay *player = reinterpret_cast(device->adl_midiPlayer); - if(!player) - return; - player->realTime_ChannelAfterTouch(channel, atVal); + MidiPlayer *play = GET_MIDI_PLAYER(device); + assert(play); + play->realTime_ChannelAfterTouch(channel, atVal); } ADLMIDI_EXPORT void adl_rt_controllerChange(struct ADL_MIDIPlayer *device, ADL_UInt8 channel, ADL_UInt8 type, ADL_UInt8 value) { if(!device) return; - MIDIplay *player = reinterpret_cast(device->adl_midiPlayer); - if(!player) - return; - player->realTime_Controller(channel, type, value); + MidiPlayer *play = GET_MIDI_PLAYER(device); + assert(play); + play->realTime_Controller(channel, type, value); } ADLMIDI_EXPORT void adl_rt_patchChange(struct ADL_MIDIPlayer *device, ADL_UInt8 channel, ADL_UInt8 patch) { if(!device) return; - MIDIplay *player = reinterpret_cast(device->adl_midiPlayer); - if(!player) - return; - player->realTime_PatchChange(channel, patch); + MidiPlayer *play = GET_MIDI_PLAYER(device); + assert(play); + play->realTime_PatchChange(channel, patch); } ADLMIDI_EXPORT void adl_rt_pitchBend(struct ADL_MIDIPlayer *device, ADL_UInt8 channel, ADL_UInt16 pitch) { if(!device) return; - MIDIplay *player = reinterpret_cast(device->adl_midiPlayer); - if(!player) - return; - player->realTime_PitchBend(channel, pitch); + MidiPlayer *play = GET_MIDI_PLAYER(device); + assert(play); + play->realTime_PitchBend(channel, pitch); } ADLMIDI_EXPORT void adl_rt_pitchBendML(struct ADL_MIDIPlayer *device, ADL_UInt8 channel, ADL_UInt8 msb, ADL_UInt8 lsb) { if(!device) return; - MIDIplay *player = reinterpret_cast(device->adl_midiPlayer); - if(!player) - return; - player->realTime_PitchBend(channel, msb, lsb); + MidiPlayer *play = GET_MIDI_PLAYER(device); + assert(play); + play->realTime_PitchBend(channel, msb, lsb); } ADLMIDI_EXPORT void adl_rt_bankChangeLSB(struct ADL_MIDIPlayer *device, ADL_UInt8 channel, ADL_UInt8 lsb) { if(!device) return; - MIDIplay *player = reinterpret_cast(device->adl_midiPlayer); - if(!player) - return; - player->realTime_BankChangeLSB(channel, lsb); + MidiPlayer *play = GET_MIDI_PLAYER(device); + assert(play); + play->realTime_BankChangeLSB(channel, lsb); } ADLMIDI_EXPORT void adl_rt_bankChangeMSB(struct ADL_MIDIPlayer *device, ADL_UInt8 channel, ADL_UInt8 msb) { if(!device) return; - MIDIplay *player = reinterpret_cast(device->adl_midiPlayer); - if(!player) - return; - player->realTime_BankChangeMSB(channel, msb); + MidiPlayer *play = GET_MIDI_PLAYER(device); + assert(play); + play->realTime_BankChangeMSB(channel, msb); } ADLMIDI_EXPORT void adl_rt_bankChange(struct ADL_MIDIPlayer *device, ADL_UInt8 channel, ADL_SInt16 bank) { if(!device) return; - MIDIplay *player = reinterpret_cast(device->adl_midiPlayer); - if(!player) - return; - player->realTime_BankChange(channel, (uint16_t)bank); + MidiPlayer *play = GET_MIDI_PLAYER(device); + assert(play); + play->realTime_BankChange(channel, (uint16_t)bank); +} + +ADLMIDI_EXPORT int adl_rt_systemExclusive(struct ADL_MIDIPlayer *device, const ADL_UInt8 *msg, size_t size) +{ + if(!device) + return -1; + MidiPlayer *play = GET_MIDI_PLAYER(device); + assert(play); + return play->realTime_SysEx(msg, size); } diff --git a/src/sound/adlmidi/adlmidi.h b/src/sound/adlmidi/adlmidi.h index 51cd968d0..352458773 100644 --- a/src/sound/adlmidi/adlmidi.h +++ b/src/sound/adlmidi/adlmidi.h @@ -29,8 +29,8 @@ extern "C" { #endif #define ADLMIDI_VERSION_MAJOR 1 -#define ADLMIDI_VERSION_MINOR 3 -#define ADLMIDI_VERSION_PATCHLEVEL 3 +#define ADLMIDI_VERSION_MINOR 4 +#define ADLMIDI_VERSION_PATCHLEVEL 0 #define ADLMIDI_TOSTR_I(s) #s #define ADLMIDI_TOSTR(s) ADLMIDI_TOSTR_I(s) @@ -55,392 +55,1187 @@ typedef char ADL_SInt8; typedef short ADL_SInt16; #endif +/* == Deprecated function markers == */ + +#if defined(_MSC_VER) /* MSVC */ +# if _MSC_VER >= 1500 /* MSVC 2008 */ + /*! Indicates that the following function is deprecated. */ +# define ADLMIDI_DEPRECATED(message) __declspec(deprecated(message)) +# endif +#endif /* defined(_MSC_VER) */ + +#ifdef __clang__ +# if __has_extension(attribute_deprecated_with_message) +# define ADLMIDI_DEPRECATED(message) __attribute__((deprecated(message))) +# endif +#elif defined __GNUC__ /* not clang (gcc comes later since clang emulates gcc) */ +# if (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 5)) +# define ADLMIDI_DEPRECATED(message) __attribute__((deprecated(message))) +# elif (__GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 1)) +# define ADLMIDI_DEPRECATED(message) __attribute__((__deprecated__)) +# endif /* GNUC version */ +#endif /* __clang__ || __GNUC__ */ + +#if !defined(ADLMIDI_DEPRECATED) +# define ADLMIDI_DEPRECATED(message) +#endif /* if !defined(ADLMIDI_DEPRECATED) */ + + +#ifdef ADLMIDI_BUILD +# ifndef ADLMIDI_DECLSPEC +# if defined (_WIN32) && defined(ADLMIDI_BUILD_DLL) +# define ADLMIDI_DECLSPEC __declspec(dllexport) +# else +# define ADLMIDI_DECLSPEC +# endif +# endif +#else +# define ADLMIDI_DECLSPEC +#endif + + +/** + * @brief Volume scaling models + */ enum ADLMIDI_VolumeModels { + /*! Automatical choice by the specific bank */ ADLMIDI_VolumeModel_AUTO = 0, + /*! Linearized scaling model, most standard */ ADLMIDI_VolumeModel_Generic = 1, + /*! Native OPL3's logarithmic volume scale */ ADLMIDI_VolumeModel_NativeOPL3 = 2, + /*! Native OPL3's logarithmic volume scale. Alias. */ ADLMIDI_VolumeModel_CMF = ADLMIDI_VolumeModel_NativeOPL3, + /*! Logarithmic volume scale, using volume map table. Used in DMX. */ ADLMIDI_VolumeModel_DMX = 3, + /*! Logarithmic volume scale, used in Apogee Sound System. */ ADLMIDI_VolumeModel_APOGEE = 4, + /*! Aproximated and shorted volume map table. Similar to general, but has less granularity. */ ADLMIDI_VolumeModel_9X = 5 }; +/** + * @brief Sound output format + */ enum ADLMIDI_SampleType { - ADLMIDI_SampleType_S16 = 0, /* signed PCM 16-bit */ - ADLMIDI_SampleType_S8, /* signed PCM 8-bit */ - ADLMIDI_SampleType_F32, /* float 32-bit */ - ADLMIDI_SampleType_F64, /* float 64-bit */ - ADLMIDI_SampleType_S24, /* signed PCM 24-bit */ - ADLMIDI_SampleType_S32, /* signed PCM 32-bit */ - ADLMIDI_SampleType_U8, /* unsigned PCM 8-bit */ - ADLMIDI_SampleType_U16, /* unsigned PCM 16-bit */ - ADLMIDI_SampleType_U24, /* unsigned PCM 24-bit */ - ADLMIDI_SampleType_U32, /* unsigned PCM 32-bit */ - ADLMIDI_SampleType_Count, + /*! signed PCM 16-bit */ + ADLMIDI_SampleType_S16 = 0, + /*! signed PCM 8-bit */ + ADLMIDI_SampleType_S8, + /*! float 32-bit */ + ADLMIDI_SampleType_F32, + /*! float 64-bit */ + ADLMIDI_SampleType_F64, + /*! signed PCM 24-bit */ + ADLMIDI_SampleType_S24, + /*! signed PCM 32-bit */ + ADLMIDI_SampleType_S32, + /*! unsigned PCM 8-bit */ + ADLMIDI_SampleType_U8, + /*! unsigned PCM 16-bit */ + ADLMIDI_SampleType_U16, + /*! unsigned PCM 24-bit */ + ADLMIDI_SampleType_U24, + /*! unsigned PCM 32-bit */ + ADLMIDI_SampleType_U32, + /*! Count of available sample format types */ + ADLMIDI_SampleType_Count }; +/** + * @brief Sound output format context + */ struct ADLMIDI_AudioFormat { - enum ADLMIDI_SampleType type; /* type of sample */ - unsigned containerSize; /* size in bytes of the storage type */ - unsigned sampleOffset; /* distance in bytes between consecutive samples */ + /*! type of sample */ + enum ADLMIDI_SampleType type; + /*! size in bytes of the storage type */ + unsigned containerSize; + /*! distance in bytes between consecutive samples */ + unsigned sampleOffset; }; +/** + * @brief Instance of the library + */ struct ADL_MIDIPlayer { + /*! Private context descriptor */ void *adl_midiPlayer; }; /* DEPRECATED */ #define adl_setNumCards adl_setNumChips -/* Sets number of emulated chips (from 1 to 100). Emulation of multiple chips exchanges polyphony limits*/ -extern int adl_setNumChips(struct ADL_MIDIPlayer *device, int numCards); +/** + * @brief Sets number of emulated chips (from 1 to 100). Emulation of multiple chips extends polyphony limits + * @param device Instance of the library + * @param numChips Count of virtual chips to emulate + * @return 0 on success, <0 when any error has occurred + */ +extern ADLMIDI_DECLSPEC int adl_setNumChips(struct ADL_MIDIPlayer *device, int numChips); -/* Get current number of emulated chips */ -extern int adl_getNumChips(struct ADL_MIDIPlayer *device); +/** + * @brief Get current number of emulated chips + * @param device Instance of the library + * @return Count of working chip emulators + */ +extern ADLMIDI_DECLSPEC int adl_getNumChips(struct ADL_MIDIPlayer *device); -/* Sets a number of the patches bank from 0 to N banks. Is recommended to call adl_reset() to apply changes to already-loaded file player or real-time. */ -extern int adl_setBank(struct ADL_MIDIPlayer *device, int bank); +/** + * @brief Get obtained number of emulated chips + * @param device Instance of the library + * @return Count of working chip emulators + */ +extern ADLMIDI_DECLSPEC int adl_getNumChipsObtained(struct ADL_MIDIPlayer *device); -/* Returns total number of available banks */ -extern int adl_getBanksCount(); +/** + * @brief Sets a number of the patches bank from 0 to N banks. + * + * Is recommended to call adl_reset() to apply changes to already-loaded file player or real-time. + * + * @param device Instance of the library + * @param bank Number of embedded bank + * @return 0 on success, <0 when any error has occurred + */ +extern ADLMIDI_DECLSPEC int adl_setBank(struct ADL_MIDIPlayer *device, int bank); -/* Returns pointer to array of names of every bank */ -extern const char *const *adl_getBankNames(); +/** + * @brief Returns total number of available banks + * @return Total number of available embedded banks + */ +extern ADLMIDI_DECLSPEC int adl_getBanksCount(); -/* Reference to dynamic bank */ +/** + * @brief Returns pointer to array of names of every bank + * @return Array of strings containing the name of every embedded bank + */ +extern ADLMIDI_DECLSPEC const char *const *adl_getBankNames(); + +/** + * @brief Reference to dynamic bank + */ typedef struct ADL_Bank { void *pointer[3]; } ADL_Bank; -/* Identifier of dynamic bank */ +/** + * @brief Identifier of dynamic bank + */ typedef struct ADL_BankId { - ADL_UInt8 percussive, msb, lsb; + /*! 0 if bank is melodic set, or 1 if bank is a percussion set */ + ADL_UInt8 percussive; + /*! Assign to MSB bank number */ + ADL_UInt8 msb; + /*! Assign to LSB bank number */ + ADL_UInt8 lsb; } ADL_BankId; -/* Flags for dynamic bank access */ +/** + * @brief Flags for dynamic bank access + */ enum ADL_BankAccessFlags { - ADLMIDI_Bank_Create = 1, /* create bank, allocating memory as needed */ - ADLMIDI_Bank_CreateRt = 1|2, /* create bank, never allocating memory */ + /*! create bank, allocating memory as needed */ + ADLMIDI_Bank_Create = 1, + /*! create bank, never allocating memory */ + ADLMIDI_Bank_CreateRt = 1|2 }; typedef struct ADL_Instrument ADL_Instrument; -#if defined(ADLMIDI_UNSTABLE_API) -/* Preallocates a minimum number of bank slots. Returns the actual capacity. */ -extern int adl_reserveBanks(struct ADL_MIDIPlayer *device, unsigned banks); -/* Gets the bank designated by the identifier, optionally creating if it does not exist. */ -extern int adl_getBank(struct ADL_MIDIPlayer *device, const ADL_BankId *id, int flags, ADL_Bank *bank); -/* Gets the identifier of a bank. */ -extern int adl_getBankId(struct ADL_MIDIPlayer *device, const ADL_Bank *bank, ADL_BankId *id); -/* Removes a bank. */ -extern int adl_removeBank(struct ADL_MIDIPlayer *device, ADL_Bank *bank); -/* Gets the first bank. */ -extern int adl_getFirstBank(struct ADL_MIDIPlayer *device, ADL_Bank *bank); -/* Iterates to the next bank. */ -extern int adl_getNextBank(struct ADL_MIDIPlayer *device, ADL_Bank *bank); -/* Gets the nth intrument in the bank [0..127]. */ -extern int adl_getInstrument(struct ADL_MIDIPlayer *device, const ADL_Bank *bank, unsigned index, ADL_Instrument *ins); -/* Sets the nth intrument in the bank [0..127]. */ -extern int adl_setInstrument(struct ADL_MIDIPlayer *device, ADL_Bank *bank, unsigned index, const ADL_Instrument *ins); -#endif /* defined(ADLMIDI_UNSTABLE_API) */ - -/*Sets number of 4-operator channels between all chips. - By default, it is automatically re-calculating every bank change. - If you want to specify custom number of four operator channels, - please call this function after bank change (adl_setBank() or adl_openBank()), - otherwise, value will be overwritten by auto-calculated.*/ -extern int adl_setNumFourOpsChn(struct ADL_MIDIPlayer *device, int ops4); - -/*Get current total count of 4-operator channels between all chips*/ -extern int adl_getNumFourOpsChn(struct ADL_MIDIPlayer *device); - -/*Override Enable(1) or Disable(0) AdLib percussion mode. -1 - use bank default AdLib percussion mode*/ -extern void adl_setPercMode(struct ADL_MIDIPlayer *device, int percmod); - -/*Override Enable(1) or Disable(0) deep vibrato state. -1 - use bank default vibrato state*/ -extern void adl_setHVibrato(struct ADL_MIDIPlayer *device, int hvibro); - -/*Override Enable(1) or Disable(0) deep tremolo state. -1 - use bank default tremolo state*/ -extern void adl_setHTremolo(struct ADL_MIDIPlayer *device, int htremo); - -/*Override Enable(1) or Disable(0) scaling of modulator volumes. -1 - use bank default scaling of modulator volumes*/ -extern void adl_setScaleModulators(struct ADL_MIDIPlayer *device, int smod); - -/*Enable(1) or Disable(0) full-range brightness (MIDI CC74 used in XG music to filter result sounding) scaling. - By default, brightness affects sound between 0 and 64. - When this option is enabled, the range will use a full range from 0 up to 127. -*/ -extern void adl_setFullRangeBrightness(struct ADL_MIDIPlayer *device, int fr_brightness); - -/*Enable or disable built-in loop (built-in loop supports 'loopStart' and 'loopEnd' tags to loop specific part)*/ -extern void adl_setLoopEnabled(struct ADL_MIDIPlayer *device, int loopEn); - -/* !!!DEPRECATED!!! Enable or disable Logariphmic volume changer */ -extern void adl_setLogarithmicVolumes(struct ADL_MIDIPlayer *device, int logvol); - -/*Set different volume range model */ -extern void adl_setVolumeRangeModel(struct ADL_MIDIPlayer *device, int volumeModel); - -/*Load WOPL bank file from File System. Is recommended to call adl_reset() to apply changes to already-loaded file player or real-time.*/ -extern int adl_openBankFile(struct ADL_MIDIPlayer *device, const char *filePath); - -/*Load WOPL bank file from memory data*/ -extern int adl_openBankData(struct ADL_MIDIPlayer *device, const void *mem, unsigned long size); -/* DEPRECATED */ -extern const char *adl_emulatorName(); -/*Returns chip emulator name string*/ -extern const char *adl_chipEmulatorName(struct ADL_MIDIPlayer *device); +/* ======== Setup ======== */ +/** + * @brief Preallocates a minimum number of bank slots. Returns the actual capacity + * @param device Instance of the library + * @param banks Count of bank slots to pre-allocate. + * @return actual capacity of reserved bank slots. + */ +extern ADLMIDI_DECLSPEC int adl_reserveBanks(struct ADL_MIDIPlayer *device, unsigned banks); +/** + * @brief Gets the bank designated by the identifier, optionally creating if it does not exist + * @param device Instance of the library + * @param id Identifier of dynamic bank + * @param flags Flags for dynamic bank access (ADL_BankAccessFlags) + * @param bank Reference to dynamic bank + * @return 0 on success, <0 when any error has occurred + */ +extern ADLMIDI_DECLSPEC int adl_getBank(struct ADL_MIDIPlayer *device, const ADL_BankId *id, int flags, ADL_Bank *bank); +/** + * @brief Gets the identifier of a bank + * @param device Instance of the library + * @param bank Reference to dynamic bank. + * @param id Identifier of dynamic bank + * @return 0 on success, <0 when any error has occurred + */ +extern ADLMIDI_DECLSPEC int adl_getBankId(struct ADL_MIDIPlayer *device, const ADL_Bank *bank, ADL_BankId *id); +/** + * @brief Removes a bank + * @param device Instance of the library + * @param bank Reference to dynamic bank + * @return 0 on success, <0 when any error has occurred + */ +extern ADLMIDI_DECLSPEC int adl_removeBank(struct ADL_MIDIPlayer *device, ADL_Bank *bank); +/** + * @brief Gets the first bank + * @param device Instance of the library + * @param bank Reference to dynamic bank + * @return 0 on success, <0 when any error has occurred + */ +extern ADLMIDI_DECLSPEC int adl_getFirstBank(struct ADL_MIDIPlayer *device, ADL_Bank *bank); +/** + * @brief Iterates to the next bank + * @param device Instance of the library + * @param bank Reference to dynamic bank + * @return 0 on success, <0 when any error has occurred or end has been reached. + */ +extern ADLMIDI_DECLSPEC int adl_getNextBank(struct ADL_MIDIPlayer *device, ADL_Bank *bank); +/** + * @brief Gets the nth intrument in the bank [0..127] + * @param device Instance of the library + * @param bank Reference to dynamic bank + * @param index Index of the instrument + * @param ins Instrument entry + * @return 0 on success, <0 when any error has occurred + */ +extern ADLMIDI_DECLSPEC int adl_getInstrument(struct ADL_MIDIPlayer *device, const ADL_Bank *bank, unsigned index, ADL_Instrument *ins); +/** + * @brief Sets the nth intrument in the bank [0..127] + * @param device Instance of the library + * @param bank Reference to dynamic bank + * @param index Index of the instrument + * @param ins Instrument structure pointer + * @return 0 on success, <0 when any error has occurred + * + * This function allows to override an instrument on the fly + */ +extern ADLMIDI_DECLSPEC int adl_setInstrument(struct ADL_MIDIPlayer *device, ADL_Bank *bank, unsigned index, const ADL_Instrument *ins); +/** + * @brief Loads the melodic or percussive part of the nth embedded bank + * @param device Instance of the library + * @param bank Reference to dynamic bank + * @param num Number of embedded bank to load into the current bank array + * @return 0 on success, <0 when any error has occurred + */ +extern ADLMIDI_DECLSPEC int adl_loadEmbeddedBank(struct ADL_MIDIPlayer *device, ADL_Bank *bank, int num); + + + +/** + * @brief Sets number of 4-operator channels between all chips + * + * By default, it is automatically re-calculating every bank change. + * If you want to specify custom number of four operator channels, + * please call this function after bank change (adl_setBank() or adl_openBank()), + * otherwise, value will be overwritten by auto-calculated. + * If the count is specified as -1, an auto-calculated amount is used instead. + * + * @param device Instance of the library + * @param ops4 Count of four-op channels to allocate between all emulating chips + * @return 0 on success, <0 when any error has occurred + */ +extern ADLMIDI_DECLSPEC int adl_setNumFourOpsChn(struct ADL_MIDIPlayer *device, int ops4); + +/** + * @brief Get current total count of 4-operator channels between all chips + * @param device Instance of the library + * @return 0 on success, <-1 when any error has occurred, but, -1 - "auto" + */ +extern ADLMIDI_DECLSPEC int adl_getNumFourOpsChn(struct ADL_MIDIPlayer *device); + +/** + * @brief Get obtained total count of 4-operator channels between all chips + * @param device Instance of the library + * @return 0 on success, <0 when any error has occurred + */ +extern ADLMIDI_DECLSPEC int adl_getNumFourOpsChnObtained(struct ADL_MIDIPlayer *device); + +/** + * @brief Override Enable(1) or Disable(0) AdLib percussion mode. -1 - use bank default AdLib percussion mode + * + * This function forces rhythm-mode on any bank. The result will work glitchy. + * + * @param device Instance of the library + * @param percmod 0 - disabled, 1 - enabled + */ +extern ADLMIDI_DECLSPEC void adl_setPercMode(struct ADL_MIDIPlayer *device, int percmod); + +/** + * @brief Override Enable(1) or Disable(0) deep vibrato state. -1 - use bank default vibrato state + * @param device Instance of the library + * @param hvibro 0 - disabled, 1 - enabled + */ +extern ADLMIDI_DECLSPEC void adl_setHVibrato(struct ADL_MIDIPlayer *device, int hvibro); + +/** + * @brief Get the deep vibrato state. + * @param device Instance of the library + * @return deep vibrato state on success, <0 when any error has occurred + */ +extern ADLMIDI_DECLSPEC int adl_getHVibrato(struct ADL_MIDIPlayer *device); + +/** + * @brief Override Enable(1) or Disable(0) deep tremolo state. -1 - use bank default tremolo state + * @param device Instance of the library + * @param htremo 0 - disabled, 1 - enabled + */ +extern ADLMIDI_DECLSPEC void adl_setHTremolo(struct ADL_MIDIPlayer *device, int htremo); + +/** + * @brief Get the deep tremolo state. + * @param device Instance of the library + * @return deep tremolo state on success, <0 when any error has occurred + */ +extern ADLMIDI_DECLSPEC int adl_getHTremolo(struct ADL_MIDIPlayer *device); + +/** + * @brief Override Enable(1) or Disable(0) scaling of modulator volumes. -1 - use bank default scaling of modulator volumes + * @param device Instance of the library + * @param smod 0 - disabled, 1 - enabled + */ +extern ADLMIDI_DECLSPEC void adl_setScaleModulators(struct ADL_MIDIPlayer *device, int smod); + +/** + * @brief Enable(1) or Disable(0) full-range brightness (MIDI CC74 used in XG music to filter result sounding) scaling + * + * By default, brightness affects sound between 0 and 64. + * When this option is enabled, the brightness will use full range from 0 up to 127. + * + * @param device Instance of the library + * @param fr_brightness 0 - disabled, 1 - enabled + */ +extern ADLMIDI_DECLSPEC void adl_setFullRangeBrightness(struct ADL_MIDIPlayer *device, int fr_brightness); + +/** + * @brief Enable or disable built-in loop (built-in loop supports 'loopStart' and 'loopEnd' tags to loop specific part) + * @param device Instance of the library + * @param loopEn 0 - disabled, 1 - enabled + */ +extern ADLMIDI_DECLSPEC void adl_setLoopEnabled(struct ADL_MIDIPlayer *device, int loopEn); + +/** + * @brief Enable or disable soft panning with chip emulators + * @param device Instance of the library + * @param softPanEn 0 - disabled, 1 - enabled + */ +extern ADLMIDI_DECLSPEC void adl_setSoftPanEnabled(struct ADL_MIDIPlayer *device, int softPanEn); + +/** + * @brief [DEPRECATED] Enable or disable Logarithmic volume changer + * + * This function is deprecated. Suggested replacement: `adl_setVolumeRangeModel` with `ADLMIDI_VolumeModel_NativeOPL3` volume model value; + */ +ADLMIDI_DEPRECATED("Use `adl_setVolumeRangeModel(device, ADLMIDI_VolumeModel_NativeOPL3)` instead") +extern ADLMIDI_DECLSPEC void adl_setLogarithmicVolumes(struct ADL_MIDIPlayer *device, int logvol); + +/** + * @brief Set different volume range model + * @param device Instance of the library + * @param volumeModel Volume model type (#ADLMIDI_VolumeModels) + */ +extern ADLMIDI_DECLSPEC void adl_setVolumeRangeModel(struct ADL_MIDIPlayer *device, int volumeModel); + +/** + * @brief Get the volume range model + * @param device Instance of the library + * @return volume model on success, <0 when any error has occurred + */ +extern ADLMIDI_DECLSPEC int adl_getVolumeRangeModel(struct ADL_MIDIPlayer *device); + +/** + * @brief Load WOPL bank file from File System + * + * Is recommended to call adl_reset() to apply changes to already-loaded file player or real-time. + * + * @param device Instance of the library + * @param filePath Absolute or relative path to the WOPL bank file. UTF8 encoding is required, even on Windows. + * @return 0 on success, <0 when any error has occurred + */ +extern ADLMIDI_DECLSPEC int adl_openBankFile(struct ADL_MIDIPlayer *device, const char *filePath); + +/** + * @brief Load WOPL bank file from memory data + * + * Is recommended to call adl_reset() to apply changes to already-loaded file player or real-time. + * + * @param device Instance of the library + * @param mem Pointer to memory block where is raw data of WOPL bank file is stored + * @param size Size of given memory block + * @return 0 on success, <0 when any error has occurred + */ +extern ADLMIDI_DECLSPEC int adl_openBankData(struct ADL_MIDIPlayer *device, const void *mem, unsigned long size); + + +/** + * @brief [DEPRECATED] Dummy function + * + * This function is deprecated. Suggested replacement: `adl_chipEmulatorName` + * + * @return A string that contains a notice to use `adl_chipEmulatorName` instead of this function. + */ +ADLMIDI_DEPRECATED("Use `adl_chipEmulatorName(device)` instead") +extern ADLMIDI_DECLSPEC const char *adl_emulatorName(); + +/** + * @brief Returns chip emulator name string + * @param device Instance of the library + * @return Understandable name of current OPL3 emulator + */ +extern ADLMIDI_DECLSPEC const char *adl_chipEmulatorName(struct ADL_MIDIPlayer *device); + +/** + * @brief List of available OPL3 emulators + */ enum ADL_Emulator { + /*! Nuked OPL3 v. 1.8 */ ADLMIDI_EMU_NUKED = 0, + /*! Nuked OPL3 v. 1.7.4 */ ADLMIDI_EMU_NUKED_174, + /*! DosBox */ ADLMIDI_EMU_DOSBOX, + /*! Count instrument on the level */ ADLMIDI_EMU_end }; -/* Switch the emulation core */ -extern int adl_switchEmulator(struct ADL_MIDIPlayer *device, int emulator); - +/** + * @brief Switch the emulation core + * @param device Instance of the library + * @param emulator Type of emulator (#ADL_Emulator) + * @return 0 on success, <0 when any error has occurred + */ +extern ADLMIDI_DECLSPEC int adl_switchEmulator(struct ADL_MIDIPlayer *device, int emulator); +/** + * @brief Library version context + */ typedef struct { ADL_UInt16 major; ADL_UInt16 minor; ADL_UInt16 patch; } ADL_Version; -/*Run emulator with PCM rate to reduce CPU usage on slow devices. May decrease sounding accuracy.*/ -extern int adl_setRunAtPcmRate(struct ADL_MIDIPlayer *device, int enabled); +/** + * @brief Run emulator with PCM rate to reduce CPU usage on slow devices. + * + * May decrease sounding accuracy on some chip emulators. + * + * @param device Instance of the library + * @param enabled 0 - disabled, 1 - enabled + * @return 0 on success, <0 when any error has occurred + */ +extern ADLMIDI_DECLSPEC int adl_setRunAtPcmRate(struct ADL_MIDIPlayer *device, int enabled); -/*Returns string which contains a version number*/ -extern const char *adl_linkedLibraryVersion(); +/** + * @brief Set 4-bit device identifier. Used by the SysEx processor. + * @param device Instance of the library + * @param id 4-bit device identifier + * @return 0 on success, <0 when any error has occurred + */ +extern ADLMIDI_DECLSPEC int adl_setDeviceIdentifier(struct ADL_MIDIPlayer *device, unsigned id); -/*Returns structure which contains a version number of library */ -extern const ADL_Version *adl_linkedVersion(); +/** + * @section Information + */ -/*Returns string which contains last error message of initialization*/ -extern const char *adl_errorString(); +/** + * @brief Returns string which contains a version number + * @return String which contains a version of the library + */ +extern ADLMIDI_DECLSPEC const char *adl_linkedLibraryVersion(); -/*Returns string which contains last error message on specific device*/ -extern const char *adl_errorInfo(struct ADL_MIDIPlayer *device); +/** + * @brief Returns structure which contains a version number of library + * @return Library version context structure which contains version number of the library + */ +extern ADLMIDI_DECLSPEC const ADL_Version *adl_linkedVersion(); -/*Initialize ADLMIDI Player device*/ -extern struct ADL_MIDIPlayer *adl_init(long sample_rate); -/*Load MIDI file from File System*/ -extern int adl_openFile(struct ADL_MIDIPlayer *device, const char *filePath); +/* ======== Error Info ======== */ -/*Load MIDI file from memory data*/ -extern int adl_openData(struct ADL_MIDIPlayer *device, const void *mem, unsigned long size); +/** + * @brief Returns string which contains last error message of initialization + * + * Don't use this function to get info on any function except of `adl_init`! + * Use `adl_errorInfo()` to get error information while workflow + * + * @return String with error message related to library initialization + */ +extern ADLMIDI_DECLSPEC const char *adl_errorString(); -/*Resets MIDI player*/ -extern void adl_reset(struct ADL_MIDIPlayer *device); - -/*Get total time length of current song*/ -extern double adl_totalTimeLength(struct ADL_MIDIPlayer *device); - -/*Get loop start time if presented. -1 means MIDI file has no loop points */ -extern double adl_loopStartTime(struct ADL_MIDIPlayer *device); - -/*Get loop end time if presented. -1 means MIDI file has no loop points */ -extern double adl_loopEndTime(struct ADL_MIDIPlayer *device); - -/*Get current time position in seconds*/ -extern double adl_positionTell(struct ADL_MIDIPlayer *device); - -/*Jump to absolute time position in seconds*/ -extern void adl_positionSeek(struct ADL_MIDIPlayer *device, double seconds); - -/*Reset MIDI track position to begin */ -extern void adl_positionRewind(struct ADL_MIDIPlayer *device); - -/*Set tempo multiplier: 1.0 - original tempo, >1 - play faster, <1 - play slower */ -extern void adl_setTempo(struct ADL_MIDIPlayer *device, double tempo); - -/*Close and delete ADLMIDI device*/ -extern void adl_close(struct ADL_MIDIPlayer *device); +/** + * @brief Returns string which contains last error message on specific device + * @param device Instance of the library + * @return String with error message related to last function call returned non-zero value. + */ +extern ADLMIDI_DECLSPEC const char *adl_errorInfo(struct ADL_MIDIPlayer *device); -/**META**/ +/* ======== Initialization ======== */ -/*Returns string which contains a music title*/ -extern const char *adl_metaMusicTitle(struct ADL_MIDIPlayer *device); +/** + * @brief Initialize ADLMIDI Player device + * + * Tip 1: You can initialize multiple instances and run them in parallel + * Tip 2: Library is NOT thread-safe, therefore don't use same instance in different threads or use mutexes + * Tip 3: Changing of sample rate on the fly is not supported. Re-create the instance again. + * + * @param sample_rate Output sample rate + * @return Instance of the library. If NULL was returned, check the `adl_errorString` message for more info. + */ +extern ADLMIDI_DECLSPEC struct ADL_MIDIPlayer *adl_init(long sample_rate); -/*Returns string which contains a copyright string*/ -extern const char *adl_metaMusicCopyright(struct ADL_MIDIPlayer *device); +/** + * @brief Close and delete ADLMIDI device + * @param device Instance of the library + */ +extern ADLMIDI_DECLSPEC void adl_close(struct ADL_MIDIPlayer *device); -/*Returns count of available track titles: NOTE: there are CAN'T be associated with channel in any of event or note hooks */ -extern size_t adl_metaTrackTitleCount(struct ADL_MIDIPlayer *device); -/*Get track title by index*/ -extern const char *adl_metaTrackTitle(struct ADL_MIDIPlayer *device, size_t index); +/* ======== MIDI Sequencer ======== */ + +/** + * @brief Load MIDI (or any other supported format) file from File System + * + * Available when library is built with built-in MIDI Sequencer support. + * + * @param device Instance of the library + * @param filePath Absolute or relative path to the music file. UTF8 encoding is required, even on Windows. + * @return 0 on success, <0 when any error has occurred + */ +extern ADLMIDI_DECLSPEC int adl_openFile(struct ADL_MIDIPlayer *device, const char *filePath); + +/** + * @brief Load MIDI (or any other supported format) file from memory data + * + * Available when library is built with built-in MIDI Sequencer support. + * + * @param device Instance of the library + * @param mem Pointer to memory block where is raw data of music file is stored + * @param size Size of given memory block + * @return 0 on success, <0 when any error has occurred + */ +extern ADLMIDI_DECLSPEC int adl_openData(struct ADL_MIDIPlayer *device, const void *mem, unsigned long size); + +/** + * @brief Resets MIDI player (per-channel setup) into initial state + * @param device Instance of the library + */ +extern ADLMIDI_DECLSPEC void adl_reset(struct ADL_MIDIPlayer *device); + +/** + * @brief Get total time length of current song + * + * Available when library is built with built-in MIDI Sequencer support. + * + * @param device Instance of the library + * @return Total song length in seconds + */ +extern ADLMIDI_DECLSPEC double adl_totalTimeLength(struct ADL_MIDIPlayer *device); + +/** + * @brief Get loop start time if presented. + * + * Available when library is built with built-in MIDI Sequencer support. + * + * @param device Instance of the library + * @return Time position in seconds of loop start point, or -1 when file has no loop points + */ +extern ADLMIDI_DECLSPEC double adl_loopStartTime(struct ADL_MIDIPlayer *device); + +/** + * @brief Get loop endtime if presented. + * + * Available when library is built with built-in MIDI Sequencer support. + * + * @param device Instance of the library + * @return Time position in seconds of loop end point, or -1 when file has no loop points + */ +extern ADLMIDI_DECLSPEC double adl_loopEndTime(struct ADL_MIDIPlayer *device); + +/** + * @brief Get current time position in seconds + * + * Available when library is built with built-in MIDI Sequencer support. + * + * @param device Instance of the library + * @return Current time position in seconds + */ +extern ADLMIDI_DECLSPEC double adl_positionTell(struct ADL_MIDIPlayer *device); + +/** + * @brief Jump to absolute time position in seconds + * + * Available when library is built with built-in MIDI Sequencer support. + * + * @param device Instance of the library + * @param seconds Destination time position in seconds to seek + */ +extern ADLMIDI_DECLSPEC void adl_positionSeek(struct ADL_MIDIPlayer *device, double seconds); + +/** + * @brief Reset MIDI track position to begin + * + * Available when library is built with built-in MIDI Sequencer support. + * + * @param device Instance of the library + */ +extern ADLMIDI_DECLSPEC void adl_positionRewind(struct ADL_MIDIPlayer *device); + +/** + * @brief Set tempo multiplier + * + * Available when library is built with built-in MIDI Sequencer support. + * + * @param device Instance of the library + * @param tempo Tempo multiplier value: 1.0 - original tempo, >1 - play faster, <1 - play slower + */ +extern ADLMIDI_DECLSPEC void adl_setTempo(struct ADL_MIDIPlayer *device, double tempo); + +/** + * @brief Returns 1 if music position has reached end + * @param device Instance of the library + * @return 1 when end of sing has been reached, otherwise, 0 will be returned. <0 is returned on any error + */ +extern ADLMIDI_DECLSPEC int adl_atEnd(struct ADL_MIDIPlayer *device); + +/** + * @brief Returns the number of tracks of the current sequence + * @param device Instance of the library + * @return Count of tracks in the current sequence + */ +extern ADLMIDI_DECLSPEC size_t adl_trackCount(struct ADL_MIDIPlayer *device); + +/** + * @brief Track options + */ +enum ADLMIDI_TrackOptions +{ + /*! Enabled track */ + ADLMIDI_TrackOption_On = 1, + /*! Disabled track */ + ADLMIDI_TrackOption_Off = 2, + /*! Solo track */ + ADLMIDI_TrackOption_Solo = 3 +}; + +/** + * @brief Sets options on a track of the current sequence + * @param device Instance of the library + * @param trackNumber Identifier of the designated track. + * @return 0 on success, <0 when any error has occurred + */ +extern ADLMIDI_DECLSPEC int adl_setTrackOptions(struct ADL_MIDIPlayer *device, size_t trackNumber, unsigned trackOptions); + +/** + * @brief Handler of callback trigger events + * @param userData Pointer to user data (usually, context of something) + * @param trigger Value of the event which triggered this callback. + * @param track Identifier of the track which triggered this callback. + */ +typedef void (*ADL_TriggerHandler)(void *userData, unsigned trigger, size_t track); + +/** + * @brief Defines a handler for callback trigger events + * @param device Instance of the library + * @param handler Handler to invoke from the sequencer when triggered, or NULL. + * @param userData Instance of the library + * @return 0 on success, <0 when any error has occurred + */ +extern ADLMIDI_DECLSPEC int adl_setTriggerHandler(struct ADL_MIDIPlayer *device, ADL_TriggerHandler handler, void *userData); + + + + +/* ======== Meta-Tags ======== */ + +/** + * @brief Returns string which contains a music title + * @param device Instance of the library + * @return A string that contains music title + */ +extern ADLMIDI_DECLSPEC const char *adl_metaMusicTitle(struct ADL_MIDIPlayer *device); + +/** + * @brief Returns string which contains a copyright string* + * @param device Instance of the library + * @return A string that contains copyright notice, otherwise NULL + */ +extern ADLMIDI_DECLSPEC const char *adl_metaMusicCopyright(struct ADL_MIDIPlayer *device); + +/** + * @brief Returns count of available track titles + * + * NOTE: There are CAN'T be associated with channel in any of event or note hooks + * + * @param device Instance of the library + * @return Count of available MIDI tracks, otherwise NULL + */ +extern ADLMIDI_DECLSPEC size_t adl_metaTrackTitleCount(struct ADL_MIDIPlayer *device); + +/** + * @brief Get track title by index + * @param device Instance of the library + * @param index Index of the track to retreive the title + * @return A string that contains track title, otherwise NULL. + */ +extern ADLMIDI_DECLSPEC const char *adl_metaTrackTitle(struct ADL_MIDIPlayer *device, size_t index); + +/** + * @brief MIDI Marker structure + */ struct Adl_MarkerEntry { + /*! MIDI Marker title */ const char *label; + /*! Absolute time position of the marker in seconds */ double pos_time; + /*! Absolute time position of the marker in MIDI ticks */ unsigned long pos_ticks; }; -/*Returns count of available markers*/ -extern size_t adl_metaMarkerCount(struct ADL_MIDIPlayer *device); +/** + * @brief Returns count of available markers + * @param device Instance of the library + * @return Count of available MIDI markers + */ +extern ADLMIDI_DECLSPEC size_t adl_metaMarkerCount(struct ADL_MIDIPlayer *device); -/*Returns the marker entry*/ -extern struct Adl_MarkerEntry adl_metaMarker(struct ADL_MIDIPlayer *device, size_t index); +/** + * @brief Returns the marker entry + * @param device Instance of the library + * @param index Index of the marker to retreive it. + * @return MIDI Marker description structure. + */ +extern ADLMIDI_DECLSPEC struct Adl_MarkerEntry adl_metaMarker(struct ADL_MIDIPlayer *device, size_t index); -/*Take a sample buffer and iterate MIDI timers */ -extern int adl_play(struct ADL_MIDIPlayer *device, int sampleCount, short *out); -/*Take a sample buffer and iterate MIDI timers */ -extern int adl_playFormat(struct ADL_MIDIPlayer *device, int sampleCount, ADL_UInt8 *left, ADL_UInt8 *right, const struct ADLMIDI_AudioFormat *format); -/*Generate audio output from chip emulators without iteration of MIDI timers.*/ -extern int adl_generate(struct ADL_MIDIPlayer *device, int sampleCount, short *out); +/* ======== Audio output Generation ======== */ -/*Generate audio output from chip emulators without iteration of MIDI timers.*/ -extern int adl_generateFormat(struct ADL_MIDIPlayer *device, int sampleCount, ADL_UInt8 *left, ADL_UInt8 *right, const struct ADLMIDI_AudioFormat *format); +/** + * @brief Generate PCM signed 16-bit stereo audio output and iterate MIDI timers + * + * Use this function when you are playing MIDI file loaded by `adl_openFile` or by `adl_openData` + * with using of built-in MIDI sequencer. + * + * Don't use count of frames, use instead count of samples. One frame is two samples. + * So, for example, if you want to take 10 frames, you must to request amount of 20 samples! + * + * Available when library is built with built-in MIDI Sequencer support. + * + * @param device Instance of the library + * @param sampleCount Count of samples (not frames!) + * @param out Pointer to output with 16-bit stereo PCM output + * @return Count of given samples, otherwise, 0 or when catching an error while playing + */ +extern ADLMIDI_DECLSPEC int adl_play(struct ADL_MIDIPlayer *device, int sampleCount, short *out); + +/** + * @brief Generate PCM stereo audio output in sample format declared by given context and iterate MIDI timers + * + * Use this function when you are playing MIDI file loaded by `adl_openFile` or by `adl_openData` + * with using of built-in MIDI sequencer. + * + * Don't use count of frames, use instead count of samples. One frame is two samples. + * So, for example, if you want to take 10 frames, you must to request amount of 20 samples! + * + * Available when library is built with built-in MIDI Sequencer support. + * + * @param device Instance of the library + * @param sampleCount Count of samples (not frames!) + * @param left Left channel buffer output (Must be casted into bytes array) + * @param right Right channel buffer output (Must be casted into bytes array) + * @param format Destination PCM format format context + * @return Count of given samples, otherwise, 0 or when catching an error while playing + */ +extern ADLMIDI_DECLSPEC int adl_playFormat(struct ADL_MIDIPlayer *device, int sampleCount, ADL_UInt8 *left, ADL_UInt8 *right, const struct ADLMIDI_AudioFormat *format); + +/** + * @brief Generate PCM signed 16-bit stereo audio output without iteration of MIDI timers + * + * Use this function when you are using library as Real-Time MIDI synthesizer or with + * an external MIDI sequencer. You must to request the amount of samples which is equal + * to the delta between of MIDI event rows. One MIDI row is a group of MIDI events + * are having zero delta/delay between each other. When you are receiving events in + * real time, request the minimal possible delay value. + * + * Don't use count of frames, use instead count of samples. One frame is two samples. + * So, for example, if you want to take 10 frames, you must to request amount of 20 samples! + * + * @param device Instance of the library + * @param sampleCount + * @param out Pointer to output with 16-bit stereo PCM output + * @return Count of given samples, otherwise, 0 or when catching an error while playing + */ +extern ADLMIDI_DECLSPEC int adl_generate(struct ADL_MIDIPlayer *device, int sampleCount, short *out); + +/** + * @brief Generate PCM stereo audio output in sample format declared by given context without iteration of MIDI timers + * + * Use this function when you are using library as Real-Time MIDI synthesizer or with + * an external MIDI sequencer. You must to request the amount of samples which is equal + * to the delta between of MIDI event rows. One MIDI row is a group of MIDI events + * are having zero delta/delay between each other. When you are receiving events in + * real time, request the minimal possible delay value. + * + * Don't use count of frames, use instead count of samples. One frame is two samples. + * So, for example, if you want to take 10 frames, you must to request amount of 20 samples! + * + * @param device Instance of the library + * @param sampleCount + * @param left Left channel buffer output (Must be casted into bytes array) + * @param right Right channel buffer output (Must be casted into bytes array) + * @param format Destination PCM format format context + * @return Count of given samples, otherwise, 0 or when catching an error while playing + */ +extern ADLMIDI_DECLSPEC int adl_generateFormat(struct ADL_MIDIPlayer *device, int sampleCount, ADL_UInt8 *left, ADL_UInt8 *right, const struct ADLMIDI_AudioFormat *format); /** * @brief Periodic tick handler. - * @param device - * @param seconds seconds since last call - * @param granularity don't expect intervals smaller than this, in seconds - * @return desired number of seconds until next call * - * Use it for Hardware OPL3 mode or when you want to process events differently from adl_play() function. - * DON'T USE IT TOGETHER WITH adl_play()!!! + * Notice: The function is provided to use it with Hardware OPL3 mode or for the purpose to iterate + * MIDI playback without of sound generation. + * + * DON'T USE IT TOGETHER WITH adl_play() and adl_playFormat() calls + * as there are all using this function internally!!! + * + * @param device Instance of the library + * @param seconds Previous delay. On a first moment, pass the `0.0` + * @param granulality Minimal size of one MIDI tick in seconds. + * @return desired number of seconds until next call. Pass this value into `seconds` field in next time */ -extern double adl_tickEvents(struct ADL_MIDIPlayer *device, double seconds, double granuality); - -/*Returns 1 if music position has reached end*/ -extern int adl_atEnd(struct ADL_MIDIPlayer *device); - -/**RealTime**/ - -/*Force Off all notes on all channels*/ -extern void adl_panic(struct ADL_MIDIPlayer *device); - -/*Reset states of all controllers on all MIDI channels*/ -extern void adl_rt_resetState(struct ADL_MIDIPlayer *device); - -/*Turn specific MIDI note ON*/ -extern int adl_rt_noteOn(struct ADL_MIDIPlayer *device, ADL_UInt8 channel, ADL_UInt8 note, ADL_UInt8 velocity); - -/*Turn specific MIDI note OFF*/ -extern void adl_rt_noteOff(struct ADL_MIDIPlayer *device, ADL_UInt8 channel, ADL_UInt8 note); - -/*Set note after-touch*/ -extern void adl_rt_noteAfterTouch(struct ADL_MIDIPlayer *device, ADL_UInt8 channel, ADL_UInt8 note, ADL_UInt8 atVal); -/*Set channel after-touch*/ -extern void adl_rt_channelAfterTouch(struct ADL_MIDIPlayer *device, ADL_UInt8 channel, ADL_UInt8 atVal); - -/*Apply controller change*/ -extern void adl_rt_controllerChange(struct ADL_MIDIPlayer *device, ADL_UInt8 channel, ADL_UInt8 type, ADL_UInt8 value); - -/*Apply patch change*/ -extern void adl_rt_patchChange(struct ADL_MIDIPlayer *device, ADL_UInt8 channel, ADL_UInt8 patch); - -/*Apply pitch bend change*/ -extern void adl_rt_pitchBend(struct ADL_MIDIPlayer *device, ADL_UInt8 channel, ADL_UInt16 pitch); -/*Apply pitch bend change*/ -extern void adl_rt_pitchBendML(struct ADL_MIDIPlayer *device, ADL_UInt8 channel, ADL_UInt8 msb, ADL_UInt8 lsb); - -/*Change LSB of the bank*/ -extern void adl_rt_bankChangeLSB(struct ADL_MIDIPlayer *device, ADL_UInt8 channel, ADL_UInt8 lsb); -/*Change MSB of the bank*/ -extern void adl_rt_bankChangeMSB(struct ADL_MIDIPlayer *device, ADL_UInt8 channel, ADL_UInt8 msb); -/*Change bank by absolute signed value*/ -extern void adl_rt_bankChange(struct ADL_MIDIPlayer *device, ADL_UInt8 channel, ADL_SInt16 bank); +extern ADLMIDI_DECLSPEC double adl_tickEvents(struct ADL_MIDIPlayer *device, double seconds, double granulality); -/**Hooks**/ + +/* ======== Real-Time MIDI ======== */ + +/** + * @brief Force Off all notes on all channels + * @param device Instance of the library + */ +extern ADLMIDI_DECLSPEC void adl_panic(struct ADL_MIDIPlayer *device); + +/** + * @brief Reset states of all controllers on all MIDI channels + * @param device Instance of the library + */ +extern ADLMIDI_DECLSPEC void adl_rt_resetState(struct ADL_MIDIPlayer *device); + +/** + * @brief Turn specific MIDI note ON + * @param device Instance of the library + * @param channel Target MIDI channel [Between 0 and 16] + * @param note Note number to on [Between 0 and 127] + * @param velocity Velocity level [Between 0 and 127] + * @return 1 when note was successfully started, 0 when note was rejected by any reason. + */ +extern ADLMIDI_DECLSPEC int adl_rt_noteOn(struct ADL_MIDIPlayer *device, ADL_UInt8 channel, ADL_UInt8 note, ADL_UInt8 velocity); + +/** + * @brief Turn specific MIDI note OFF + * @param device Instance of the library + * @param channel Target MIDI channel [Between 0 and 16] + * @param note Note number to off [Between 0 and 127] + */ +extern ADLMIDI_DECLSPEC void adl_rt_noteOff(struct ADL_MIDIPlayer *device, ADL_UInt8 channel, ADL_UInt8 note); + +/** + * @brief Set note after-touch + * @param device Instance of the library + * @param channel Target MIDI channel [Between 0 and 16] + * @param note Note number to affect by aftertouch event [Between 0 and 127] + * @param atVal After-Touch value [Between 0 and 127] + */ +extern ADLMIDI_DECLSPEC void adl_rt_noteAfterTouch(struct ADL_MIDIPlayer *device, ADL_UInt8 channel, ADL_UInt8 note, ADL_UInt8 atVal); + +/** + * @brief Set channel after-touch + * @param device Instance of the library + * @param channel Target MIDI channel [Between 0 and 16] + * @param atVal After-Touch level [Between 0 and 127] + */ +extern ADLMIDI_DECLSPEC void adl_rt_channelAfterTouch(struct ADL_MIDIPlayer *device, ADL_UInt8 channel, ADL_UInt8 atVal); + +/** + * @brief Apply controller change + * @param device Instance of the library + * @param channel Target MIDI channel [Between 0 and 16] + * @param type Type of the controller [Between 0 and 255] + * @param value Value of the controller event [Between 0 and 127] + */ +extern ADLMIDI_DECLSPEC void adl_rt_controllerChange(struct ADL_MIDIPlayer *device, ADL_UInt8 channel, ADL_UInt8 type, ADL_UInt8 value); + +/** + * @brief Apply patch change + * @param device Instance of the library + * @param channel Target MIDI channel [Between 0 and 16] + * @param patch Patch number [Between 0 and 127] + */ +extern ADLMIDI_DECLSPEC void adl_rt_patchChange(struct ADL_MIDIPlayer *device, ADL_UInt8 channel, ADL_UInt8 patch); + +/** + * @brief Apply pitch bend change + * @param device Instance of the library + * @param channel Target MIDI channel [Between 0 and 16] + * @param pitch 24-bit pitch bend value + */ +extern ADLMIDI_DECLSPEC void adl_rt_pitchBend(struct ADL_MIDIPlayer *device, ADL_UInt8 channel, ADL_UInt16 pitch); + +/** + * @brief Apply pitch bend change + * @param device Instance of the library + * @param channel Target MIDI channel [Between 0 and 16] + * @param msb MSB part of 24-bit pitch bend value + * @param lsb LSB part of 24-bit pitch bend value + */ +extern ADLMIDI_DECLSPEC void adl_rt_pitchBendML(struct ADL_MIDIPlayer *device, ADL_UInt8 channel, ADL_UInt8 msb, ADL_UInt8 lsb); + +/** + * @brief Change LSB of the bank number (Alias to CC-32 event) + * @param device Instance of the library + * @param channel Target MIDI channel [Between 0 and 16] + * @param lsb LSB value of the MIDI bank number + */ +extern ADLMIDI_DECLSPEC void adl_rt_bankChangeLSB(struct ADL_MIDIPlayer *device, ADL_UInt8 channel, ADL_UInt8 lsb); + +/** + * @brief Change MSB of the bank (Alias to CC-0 event) + * @param device Instance of the library + * @param channel Target MIDI channel [Between 0 and 16] + * @param msb MSB value of the MIDI bank number + */ +extern ADLMIDI_DECLSPEC void adl_rt_bankChangeMSB(struct ADL_MIDIPlayer *device, ADL_UInt8 channel, ADL_UInt8 msb); + +/** + * @brief Change bank by absolute signed value + * @param device Instance of the library + * @param channel Target MIDI channel [Between 0 and 16] + * @param bank Bank number as concoctated signed 16-bit value of MSB and LSB parts. + */ + +extern ADLMIDI_DECLSPEC void adl_rt_bankChange(struct ADL_MIDIPlayer *device, ADL_UInt8 channel, ADL_SInt16 bank); + +/** + * @brief Perform a system exclusive message + * @param device Instance of the library + * @param msg Raw SysEx message buffer (must begin with 0xF0 and end with 0xF7) + * @param size Size of given SysEx message buffer + * @return 1 when SysEx message was successfully processed, 0 when SysEx message was rejected by any reason + */ +extern ADLMIDI_DECLSPEC int adl_rt_systemExclusive(struct ADL_MIDIPlayer *device, const ADL_UInt8 *msg, size_t size); + + + + +/* ======== Hooks and debugging ======== */ + +/** + * @brief Raw event callback + * @param userdata Pointer to user data (usually, context of someting) + * @param type MIDI event type + * @param subtype MIDI event sub-type (special events only) + * @param channel MIDI channel + * @param data Raw event data + * @param len Length of event data + */ typedef void (*ADL_RawEventHook)(void *userdata, ADL_UInt8 type, ADL_UInt8 subtype, ADL_UInt8 channel, const ADL_UInt8 *data, size_t len); + +/** + * @brief Note on/off callback + * @param userdata Pointer to user data (usually, context of someting) + * @param adlchn Chip channel where note was played + * @param note Note number [between 0 and 127] + * @param pressure Velocity level, or -1 when it's note off event + * @param bend Pitch bend offset value + */ typedef void (*ADL_NoteHook)(void *userdata, int adlchn, int note, int ins, int pressure, double bend); + +/** + * @brief Debug messages callback + * @param userdata Pointer to user data (usually, context of someting) + * @param fmt Format strign output (in context of `printf()` standard function) + */ typedef void (*ADL_DebugMessageHook)(void *userdata, const char *fmt, ...); -/* Set raw MIDI event hook */ -extern void adl_setRawEventHook(struct ADL_MIDIPlayer *device, ADL_RawEventHook rawEventHook, void *userData); +/** + * @brief Set raw MIDI event hook + * @param device Instance of the library + * @param rawEventHook Pointer to the callback function which will be called on every MIDI event + * @param userData Pointer to user data which will be passed through the callback. + */ +extern ADLMIDI_DECLSPEC void adl_setRawEventHook(struct ADL_MIDIPlayer *device, ADL_RawEventHook rawEventHook, void *userData); -/* Set note hook */ -extern void adl_setNoteHook(struct ADL_MIDIPlayer *device, ADL_NoteHook noteHook, void *userData); +/** + * @brief Set note hook + * @param device Instance of the library + * @param noteHook Pointer to the callback function which will be called on every noteOn MIDI event + * @param userData Pointer to user data which will be passed through the callback. + */ +extern ADLMIDI_DECLSPEC void adl_setNoteHook(struct ADL_MIDIPlayer *device, ADL_NoteHook noteHook, void *userData); -/* Set debug message hook */ -extern void adl_setDebugMessageHook(struct ADL_MIDIPlayer *device, ADL_DebugMessageHook debugMessageHook, void *userData); +/** + * @brief Set debug message hook + * @param device Instance of the library + * @param debugMessageHook Pointer to the callback function which will be called on every debug message + * @param userData Pointer to user data which will be passed through the callback. + */ +extern ADLMIDI_DECLSPEC void adl_setDebugMessageHook(struct ADL_MIDIPlayer *device, ADL_DebugMessageHook debugMessageHook, void *userData); + +/** + * @brief Get a textual description of the channel state. For display only. + * @param device Instance of the library + * @param text Destination char buffer for channel usage state. Every entry is assigned to the chip channel. + * @param attr Destination char buffer for additional attributes like MIDI channel number that uses this chip channel. + * @param size Size of given buffers (both text and attr are must have same size!) + * @return 0 on success, <0 when any error has occurred + * + * Every character in the `text` buffer means the type of usage: + * ``` + * `-` - channel is unused (free) + * `+` - channel is used by two-operator voice + * `#` - channel is used by four-operator voice + * `@` - channel is used to play automatic arpeggio on chip channels overflow + * `r` - rhythm-mode channel note + * ``` + * + * The `attr` field receives the MIDI channel from which the chip channel is used. + * To get the valid MIDI channel you will need to apply the & 0x0F mask to every value. + */ +extern ADLMIDI_DECLSPEC int adl_describeChannels(struct ADL_MIDIPlayer *device, char *text, char *attr, size_t size); -/**Instrument structures**/ + +/* ======== Instrument structures ======== */ + +/** + * @brief Version of the instrument data format + */ enum { - ADLMIDI_InstrumentVersion = 0, + ADLMIDI_InstrumentVersion = 0 }; +/** + * @brief Instrument flags + */ typedef enum ADL_InstrumentFlags { - /* Is two-operator single-voice instrument (no flags) */ + /*! Is two-operator single-voice instrument (no flags) */ ADLMIDI_Ins_2op = 0x00, - /* Is true four-operator instrument */ + /*! Is true four-operator instrument */ ADLMIDI_Ins_4op = 0x01, - /* Is pseudo four-operator (two 2-operator voices) instrument */ + /*! Is pseudo four-operator (two 2-operator voices) instrument */ ADLMIDI_Ins_Pseudo4op = 0x02, - /* Is a blank instrument entry */ + /*! Is a blank instrument entry */ ADLMIDI_Ins_IsBlank = 0x04, - /* Mask of the flags range */ - ADLMIDI_Ins_ALL_MASK = 0x07, + + /*! RythmMode flags mask */ + ADLMIDI_Ins_RhythmModeMask = 0x38, + + /*! Mask of the flags range */ + ADLMIDI_Ins_ALL_MASK = 0x07 } ADL_InstrumentFlags; +/** + * @brief Rhythm-mode drum type + */ +typedef enum ADL_RhythmMode +{ + /*! RythmMode: BassDrum */ + ADLMIDI_RM_BassDrum = 0x08, + /*! RythmMode: Snare */ + ADLMIDI_RM_Snare = 0x10, + /*! RythmMode: TomTom */ + ADLMIDI_RM_TomTom = 0x18, + /*! RythmMode: Cymbal */ + ADLMIDI_RM_Cymbal = 0x20, + /*! RythmMode: HiHat */ + ADLMIDI_RM_HiHat = 0x28 +} ADL_RhythmMode; + + +/** + * @brief Operator structure, part of Instrument structure + */ typedef struct ADL_Operator { - /* AM/Vib/Env/Ksr/FMult characteristics */ + /*! AM/Vib/Env/Ksr/FMult characteristics */ ADL_UInt8 avekf_20; - /* Key Scale Level / Total level register data */ + /*! Key Scale Level / Total level register data */ ADL_UInt8 ksl_l_40; - /* Attack / Decay */ + /*! Attack / Decay */ ADL_UInt8 atdec_60; - /* Systain and Release register data */ + /*! Systain and Release register data */ ADL_UInt8 susrel_80; - /* Wave form */ + /*! Wave form */ ADL_UInt8 waveform_E0; } ADL_Operator; +/** + * @brief Instrument structure + */ typedef struct ADL_Instrument { - /* Version of the instrument object */ + /*! Version of the instrument object */ int version; - /* MIDI note key (half-tone) offset for an instrument (or a first voice in pseudo-4-op mode) */ + /*! MIDI note key (half-tone) offset for an instrument (or a first voice in pseudo-4-op mode) */ ADL_SInt16 note_offset1; - /* MIDI note key (half-tone) offset for a second voice in pseudo-4-op mode */ + /*! MIDI note key (half-tone) offset for a second voice in pseudo-4-op mode */ ADL_SInt16 note_offset2; - /* MIDI note velocity offset (taken from Apogee TMB format) */ + /*! MIDI note velocity offset (taken from Apogee TMB format) */ ADL_SInt8 midi_velocity_offset; - /* Second voice detune level (taken from DMX OP2) */ + /*! Second voice detune level (taken from DMX OP2) */ ADL_SInt8 second_voice_detune; - /* Percussion MIDI base tone number at which this drum will be played */ + /*! Percussion MIDI base tone number at which this drum will be played */ ADL_UInt8 percussion_key_number; - /* Enum ADL_InstrumentFlags */ + /** + * @var inst_flags + * @brief Instrument flags + * + * Enums: #ADL_InstrumentFlags and #ADL_RhythmMode + * + * Bitwise flags bit map: + * ``` + * [0EEEDCBA] + * A) 0x00 - 2-operator mode + * B) 0x01 - 4-operator mode + * C) 0x02 - pseudo-4-operator (two 2-operator voices) mode + * D) 0x04 - is 'blank' instrument (instrument which has no sound) + * E) 0x38 - Reserved for rhythm-mode percussion type number (three bits number) + * -> 0x00 - Melodic or Generic drum (rhythm-mode is disabled) + * -> 0x08 - is Bass drum + * -> 0x10 - is Snare + * -> 0x18 - is Tom-tom + * -> 0x20 - is Cymbal + * -> 0x28 - is Hi-hat + * 0) Reserved / Unused + * ``` + */ ADL_UInt8 inst_flags; - /* Feedback&Connection register for first and second operators */ + /*! Feedback&Connection register for first and second operators */ ADL_UInt8 fb_conn1_C0; - /* Feedback&Connection register for third and fourth operators */ + /*! Feedback&Connection register for third and fourth operators */ ADL_UInt8 fb_conn2_C0; - /* Operators register data */ + /*! Operators register data */ ADL_Operator operators[4]; - /* Millisecond delay of sounding while key is on */ + /*! Millisecond delay of sounding while key is on */ ADL_UInt16 delay_on_ms; - /* Millisecond delay of sounding after key off */ + /*! Millisecond delay of sounding after key off */ ADL_UInt16 delay_off_ms; } ADL_Instrument; diff --git a/src/sound/adlmidi/adlmidi.hpp b/src/sound/adlmidi/adlmidi.hpp index 6d01b8d33..f2cd59d27 100644 --- a/src/sound/adlmidi/adlmidi.hpp +++ b/src/sound/adlmidi/adlmidi.hpp @@ -24,9 +24,11 @@ #ifndef ADLMIDI_HPP #define ADLMIDI_HPP +#include "adlmidi.h" + struct ADL_MIDIPlayer; -class AdlInstrumentTester +class ADLMIDI_DECLSPEC AdlInstrumentTester { struct Impl; Impl *P; diff --git a/src/sound/adlmidi/adlmidi_bankmap.h b/src/sound/adlmidi/adlmidi_bankmap.h index e4534cd6e..5d747d1b3 100644 --- a/src/sound/adlmidi/adlmidi_bankmap.h +++ b/src/sound/adlmidi/adlmidi_bankmap.h @@ -40,7 +40,7 @@ template class BasicBankMap { public: - typedef uint16_t key_type; /* the bank identifier */ + typedef size_t key_type; /* the bank identifier */ typedef T mapped_type; typedef std::pair value_type; @@ -74,7 +74,7 @@ private: enum { hash_bits = 8, /* worst case # of collisions: 128^2/2^hash_bits */ - hash_buckets = 1 << hash_bits, + hash_buckets = 1 << hash_bits }; public: diff --git a/src/sound/adlmidi/adlmidi_bankmap.tcc b/src/sound/adlmidi/adlmidi_bankmap.tcc index 76e70016c..90d88949b 100644 --- a/src/sound/adlmidi/adlmidi_bankmap.tcc +++ b/src/sound/adlmidi/adlmidi_bankmap.tcc @@ -37,7 +37,7 @@ template inline size_t BasicBankMap::hash(key_type key) { // disregard the 0 high bit in LSB - key = (key & 127) | ((key >> 8) << 7); + key = key_type(key & 127) | key_type((key >> 8) << 7); // take low part as hash value return key & (hash_buckets - 1); } diff --git a/src/sound/adlmidi/adlmidi_cvt.hpp b/src/sound/adlmidi/adlmidi_cvt.hpp new file mode 100644 index 000000000..449fe2ffd --- /dev/null +++ b/src/sound/adlmidi/adlmidi_cvt.hpp @@ -0,0 +1,124 @@ +/* + * libADLMIDI is a free MIDI to WAV conversion library with OPL3 emulation + * + * Original ADLMIDI code: Copyright (c) 2010-2014 Joel Yliluoma + * ADLMIDI Library API: Copyright (c) 2015-2018 Vitaly Novichkov + * + * Library is based on the ADLMIDI, a MIDI player for Linux and Windows with OPL3 emulation: + * http://iki.fi/bisqwit/source/adlmidi.html + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "adldata.hh" +#include "wopl/wopl_file.h" +#include + +template +static void cvt_generic_to_FMIns(adlinsdata2 &ins, const WOPLI &in) +{ + ins.voice2_fine_tune = 0.0; + int8_t voice2_fine_tune = in.second_voice_detune; + if(voice2_fine_tune != 0) + { + if(voice2_fine_tune == 1) + ins.voice2_fine_tune = 0.000025; + else if(voice2_fine_tune == -1) + ins.voice2_fine_tune = -0.000025; + else + ins.voice2_fine_tune = voice2_fine_tune * (15.625 / 1000.0); + } + + ins.midi_velocity_offset = in.midi_velocity_offset; + ins.tone = in.percussion_key_number; + ins.flags = (in.inst_flags & WOPL_Ins_4op) && (in.inst_flags & WOPL_Ins_Pseudo4op) ? adlinsdata::Flag_Pseudo4op : 0; + ins.flags|= (in.inst_flags & WOPL_Ins_4op) && ((in.inst_flags & WOPL_Ins_Pseudo4op) == 0) ? adlinsdata::Flag_Real4op : 0; + ins.flags|= (in.inst_flags & WOPL_Ins_IsBlank) ? adlinsdata::Flag_NoSound : 0; + ins.flags|= in.inst_flags & WOPL_RhythmModeMask; + + for(size_t op = 0, slt = 0; op < 4; op++, slt++) + { + ins.adl[slt].carrier_E862 = + ((static_cast(in.operators[op].waveform_E0) << 24) & 0xFF000000) //WaveForm + | ((static_cast(in.operators[op].susrel_80) << 16) & 0x00FF0000) //SusRel + | ((static_cast(in.operators[op].atdec_60) << 8) & 0x0000FF00) //AtDec + | ((static_cast(in.operators[op].avekf_20) << 0) & 0x000000FF); //AVEKM + ins.adl[slt].carrier_40 = in.operators[op].ksl_l_40;//KSLL + + op++; + ins.adl[slt].modulator_E862 = + ((static_cast(in.operators[op].waveform_E0) << 24) & 0xFF000000) //WaveForm + | ((static_cast(in.operators[op].susrel_80) << 16) & 0x00FF0000) //SusRel + | ((static_cast(in.operators[op].atdec_60) << 8) & 0x0000FF00) //AtDec + | ((static_cast(in.operators[op].avekf_20) << 0) & 0x000000FF); //AVEKM + ins.adl[slt].modulator_40 = in.operators[op].ksl_l_40;//KSLL + } + + ins.adl[0].finetune = static_cast(in.note_offset1); + ins.adl[0].feedconn = in.fb_conn1_C0; + ins.adl[1].finetune = static_cast(in.note_offset2); + ins.adl[1].feedconn = in.fb_conn2_C0; + + ins.ms_sound_kon = in.delay_on_ms; + ins.ms_sound_koff = in.delay_off_ms; +} + +template +static void cvt_FMIns_to_generic(WOPLI &ins, const adlinsdata2 &in) +{ + ins.second_voice_detune = 0; + double voice2_fine_tune = in.voice2_fine_tune; + if(voice2_fine_tune != 0) + { + if(voice2_fine_tune > 0 && voice2_fine_tune <= 0.000025) + ins.second_voice_detune = 1; + else if(voice2_fine_tune < 0 && voice2_fine_tune >= -0.000025) + ins.second_voice_detune = -1; + else + { + long value = static_cast(round(voice2_fine_tune * (1000.0 / 15.625))); + value = (value < -128) ? -128 : value; + value = (value > +127) ? +127 : value; + ins.second_voice_detune = static_cast(value); + } + } + + ins.midi_velocity_offset = in.midi_velocity_offset; + ins.percussion_key_number = in.tone; + ins.inst_flags = (in.flags & (adlinsdata::Flag_Pseudo4op|adlinsdata::Flag_Real4op)) ? WOPL_Ins_4op : 0; + ins.inst_flags|= (in.flags & adlinsdata::Flag_Pseudo4op) ? WOPL_Ins_Pseudo4op : 0; + ins.inst_flags|= (in.flags & adlinsdata::Flag_NoSound) ? WOPL_Ins_IsBlank : 0; + ins.inst_flags |= in.flags & adlinsdata::Mask_RhythmMode; + + for(size_t op = 0; op < 4; op++) + { + const adldata &in2op = in.adl[(op < 2) ? 0 : 1]; + uint32_t regE862 = ((op & 1) == 0) ? in2op.carrier_E862 : in2op.modulator_E862; + uint8_t reg40 = ((op & 1) == 0) ? in2op.carrier_40 : in2op.modulator_40; + + ins.operators[op].waveform_E0 = static_cast(regE862 >> 24); + ins.operators[op].susrel_80 = static_cast(regE862 >> 16); + ins.operators[op].atdec_60 = static_cast(regE862 >> 8); + ins.operators[op].avekf_20 = static_cast(regE862 >> 0); + ins.operators[op].ksl_l_40 = reg40; + } + + ins.note_offset1 = in.adl[0].finetune; + ins.fb_conn1_C0 = in.adl[0].feedconn; + ins.note_offset2 = in.adl[1].finetune; + ins.fb_conn2_C0 = in.adl[1].feedconn; + + ins.delay_on_ms = in.ms_sound_kon; + ins.delay_off_ms = in.ms_sound_koff; +} diff --git a/src/sound/adlmidi/adlmidi_load.cpp b/src/sound/adlmidi/adlmidi_load.cpp index 7f69e259f..07ad87573 100644 --- a/src/sound/adlmidi/adlmidi_load.cpp +++ b/src/sound/adlmidi/adlmidi_load.cpp @@ -1,4 +1,4 @@ -/* +/* * libADLMIDI is a free MIDI to WAV conversion library with OPL3 emulation * * Original ADLMIDI code: Copyright (c) 2010-2014 Joel Yliluoma @@ -22,168 +22,23 @@ */ #include "adlmidi_private.hpp" +#include "adlmidi_cvt.hpp" #include "wopl/wopl_file.h" -#ifndef ADLMIDI_DISABLE_MIDI_SEQUENCER -# ifndef ADLMIDI_DISABLE_MUS_SUPPORT -# include "adlmidi_mus2mid.h" -# endif//MUS -# ifndef ADLMIDI_DISABLE_XMI_SUPPORT -# include "adlmidi_xmi2mid.h" -# endif//XMI -#endif //ADLMIDI_DISABLE_MIDI_SEQUENCER - -#ifndef ADLMIDI_DISABLE_MIDI_SEQUENCER -uint64_t MIDIplay::ReadBEint(const void *buffer, size_t nbytes) -{ - uint64_t result = 0; - const unsigned char *data = reinterpret_cast(buffer); - - for(unsigned n = 0; n < nbytes; ++n) - result = (result << 8) + data[n]; - - return result; -} - -uint64_t MIDIplay::ReadLEint(const void *buffer, size_t nbytes) -{ - uint64_t result = 0; - const unsigned char *data = reinterpret_cast(buffer); - - for(unsigned n = 0; n < nbytes; ++n) - result = result + static_cast(data[n] << (n * 8)); - - return result; -} - -#endif - bool MIDIplay::LoadBank(const std::string &filename) { - fileReader file; + FileAndMemReader file; file.openFile(filename.c_str()); return LoadBank(file); } bool MIDIplay::LoadBank(const void *data, size_t size) { - fileReader file; + FileAndMemReader file; file.openData(data, size); return LoadBank(file); } -template -static void cvt_generic_to_FMIns(adlinsdata2 &ins, const WOPLI &in) -{ - ins.voice2_fine_tune = 0.0; - int8_t voice2_fine_tune = in.second_voice_detune; - if(voice2_fine_tune != 0) - { - if(voice2_fine_tune == 1) - ins.voice2_fine_tune = 0.000025; - else if(voice2_fine_tune == -1) - ins.voice2_fine_tune = -0.000025; - else - ins.voice2_fine_tune = voice2_fine_tune * (15.625 / 1000.0); - } - - ins.tone = in.percussion_key_number; - ins.flags = (in.inst_flags & WOPL_Ins_4op) && (in.inst_flags & WOPL_Ins_Pseudo4op) ? adlinsdata::Flag_Pseudo4op : 0; - ins.flags|= (in.inst_flags & WOPL_Ins_4op) && ((in.inst_flags & WOPL_Ins_Pseudo4op) == 0) ? adlinsdata::Flag_Real4op : 0; - ins.flags|= (in.inst_flags & WOPL_Ins_IsBlank) ? adlinsdata::Flag_NoSound : 0; - - bool fourOps = (in.inst_flags & WOPL_Ins_4op) || (in.inst_flags & WOPL_Ins_Pseudo4op); - for(size_t op = 0, slt = 0; op < static_cast(fourOps ? 4 : 2); op++, slt++) - { - ins.adl[slt].carrier_E862 = - ((static_cast(in.operators[op].waveform_E0) << 24) & 0xFF000000) //WaveForm - | ((static_cast(in.operators[op].susrel_80) << 16) & 0x00FF0000) //SusRel - | ((static_cast(in.operators[op].atdec_60) << 8) & 0x0000FF00) //AtDec - | ((static_cast(in.operators[op].avekf_20) << 0) & 0x000000FF); //AVEKM - ins.adl[slt].carrier_40 = in.operators[op].ksl_l_40;//KSLL - - op++; - ins.adl[slt].modulator_E862 = - ((static_cast(in.operators[op].waveform_E0) << 24) & 0xFF000000) //WaveForm - | ((static_cast(in.operators[op].susrel_80) << 16) & 0x00FF0000) //SusRel - | ((static_cast(in.operators[op].atdec_60) << 8) & 0x0000FF00) //AtDec - | ((static_cast(in.operators[op].avekf_20) << 0) & 0x000000FF); //AVEKM - ins.adl[slt].modulator_40 = in.operators[op].ksl_l_40;//KSLL - } - - ins.adl[0].finetune = static_cast(in.note_offset1); - ins.adl[0].feedconn = in.fb_conn1_C0; - if(!fourOps) - ins.adl[1] = ins.adl[0]; - else - { - ins.adl[1].finetune = static_cast(in.note_offset2); - ins.adl[1].feedconn = in.fb_conn2_C0; - } - - ins.ms_sound_kon = in.delay_on_ms; - ins.ms_sound_koff = in.delay_off_ms; -} - -template -static void cvt_FMIns_to_generic(WOPLI &ins, const adlinsdata2 &in) -{ - ins.second_voice_detune = 0; - double voice2_fine_tune = in.voice2_fine_tune; - if(voice2_fine_tune != 0) - { - if(voice2_fine_tune > 0 && voice2_fine_tune <= 0.000025) - ins.second_voice_detune = 1; - else if(voice2_fine_tune < 0 && voice2_fine_tune >= -0.000025) - ins.second_voice_detune = -1; - else - { - long value = lround(voice2_fine_tune * (1000.0 / 15.625)); - value = (value < -128) ? -128 : value; - value = (value > +127) ? +127 : value; - ins.second_voice_detune = static_cast(value); - } - } - - ins.percussion_key_number = in.tone; - bool fourOps = (in.flags & adlinsdata::Flag_Pseudo4op) || in.adl[0] != in.adl[1]; - ins.inst_flags = fourOps ? WOPL_Ins_4op : 0; - ins.inst_flags|= (in.flags & adlinsdata::Flag_Pseudo4op) ? WOPL_Ins_Pseudo4op : 0; - ins.inst_flags|= (in.flags & adlinsdata::Flag_NoSound) ? WOPL_Ins_IsBlank : 0; - - for(size_t op = 0, slt = 0; op < static_cast(fourOps ? 4 : 2); op++, slt++) - { - ins.operators[op].waveform_E0 = static_cast(in.adl[slt].carrier_E862 >> 24); - ins.operators[op].susrel_80 = static_cast(in.adl[slt].carrier_E862 >> 16); - ins.operators[op].atdec_60 = static_cast(in.adl[slt].carrier_E862 >> 8); - ins.operators[op].avekf_20 = static_cast(in.adl[slt].carrier_E862 >> 0); - ins.operators[op].ksl_l_40 = in.adl[slt].carrier_40; - - op++; - ins.operators[op].waveform_E0 = static_cast(in.adl[slt].carrier_E862 >> 24); - ins.operators[op].susrel_80 = static_cast(in.adl[slt].carrier_E862 >> 16); - ins.operators[op].atdec_60 = static_cast(in.adl[slt].carrier_E862 >> 8); - ins.operators[op].avekf_20 = static_cast(in.adl[slt].carrier_E862 >> 0); - ins.operators[op].ksl_l_40 = in.adl[slt].carrier_40; - } - - ins.note_offset1 = in.adl[0].finetune; - ins.fb_conn1_C0 = in.adl[0].feedconn; - if(!fourOps) - { - ins.operators[2] = ins.operators[0]; - ins.operators[3] = ins.operators[1]; - } - else - { - ins.note_offset2 = in.adl[1].finetune; - ins.fb_conn2_C0 = in.adl[1].feedconn; - } - - ins.delay_on_ms = in.ms_sound_kon; - ins.delay_off_ms = in.ms_sound_koff; -} - void cvt_ADLI_to_FMIns(adlinsdata2 &ins, const ADL_Instrument &in) { return cvt_generic_to_FMIns(ins, in); @@ -194,7 +49,7 @@ void cvt_FMIns_to_ADLI(ADL_Instrument &ins, const adlinsdata2 &in) cvt_FMIns_to_generic(ins, in); } -bool MIDIplay::LoadBank(MIDIplay::fileReader &fr) +bool MIDIplay::LoadBank(FileAndMemReader &fr) { int err = 0; WOPLFile *wopl = NULL; @@ -207,9 +62,8 @@ bool MIDIplay::LoadBank(MIDIplay::fileReader &fr) } // Read complete bank file into the memory - fr.seek(0, SEEK_END); - fsize = fr.tell(); - fr.seek(0, SEEK_SET); + fsize = fr.fileSize(); + fr.seek(0, FileAndMemReader::SET); // Allocate necessary memory block raw_file_data = (char*)malloc(fsize); if(!raw_file_data) @@ -250,29 +104,28 @@ bool MIDIplay::LoadBank(MIDIplay::fileReader &fr) } } - opl.dynamic_bank_setup.adLibPercussions = false; - opl.dynamic_bank_setup.scaleModulators = false; - opl.dynamic_bank_setup.deepTremolo = (wopl->opl_flags & WOPL_FLAG_DEEP_TREMOLO) != 0; - opl.dynamic_bank_setup.deepVibrato = (wopl->opl_flags & WOPL_FLAG_DEEP_VIBRATO) != 0; - opl.dynamic_bank_setup.volumeModel = wopl->volume_model; - m_setup.HighTremoloMode = -1; - m_setup.HighVibratoMode = -1; - m_setup.VolumeModel = ADLMIDI_VolumeModel_AUTO; + m_synth.m_insBankSetup.adLibPercussions = false; + m_synth.m_insBankSetup.scaleModulators = false; + m_synth.m_insBankSetup.deepTremolo = (wopl->opl_flags & WOPL_FLAG_DEEP_TREMOLO) != 0; + m_synth.m_insBankSetup.deepVibrato = (wopl->opl_flags & WOPL_FLAG_DEEP_VIBRATO) != 0; + m_synth.m_insBankSetup.volumeModel = wopl->volume_model; + m_setup.deepTremoloMode = -1; + m_setup.deepVibratoMode = -1; + m_setup.volumeScaleModel = ADLMIDI_VolumeModel_AUTO; - opl.setEmbeddedBank(m_setup.AdlBank); + m_synth.setEmbeddedBank(m_setup.bankId); uint16_t slots_counts[2] = {wopl->banks_count_melodic, wopl->banks_count_percussion}; WOPLBank *slots_src_ins[2] = { wopl->banks_melodic, wopl->banks_percussive }; - for(unsigned ss = 0; ss < 2; ss++) + for(size_t ss = 0; ss < 2; ss++) { - for(unsigned i = 0; i < slots_counts[ss]; i++) + for(size_t i = 0; i < slots_counts[ss]; i++) { - unsigned bankno = - (slots_src_ins[ss][i].bank_midi_msb * 256) + - slots_src_ins[ss][i].bank_midi_lsb + - (ss ? OPL3::PercussionTag : 0); - OPL3::Bank &bank = opl.dynamic_banks[bankno]; + size_t bankno = (slots_src_ins[ss][i].bank_midi_msb * 256) + + (slots_src_ins[ss][i].bank_midi_lsb) + + (ss ? size_t(OPL3::PercussionTag) : 0); + OPL3::Bank &bank = m_synth.m_insBanks[bankno]; for(int j = 0; j < 128; j++) { adlinsdata2 &ins = bank.ins[j]; @@ -283,7 +136,7 @@ bool MIDIplay::LoadBank(MIDIplay::fileReader &fr) } } - opl.AdlBank = ~0u; // Use dynamic banks! + m_synth.m_embeddedBank = OPL3::CustomBankTag; // Use dynamic banks! //Percussion offset is count of instruments multipled to count of melodic banks applySetup(); @@ -293,189 +146,45 @@ bool MIDIplay::LoadBank(MIDIplay::fileReader &fr) } #ifndef ADLMIDI_DISABLE_MIDI_SEQUENCER -bool MIDIplay::LoadMIDI(const std::string &filename) -{ - fileReader file; - file.openFile(filename.c_str()); - if(!LoadMIDI(file)) - return false; - return true; -} -bool MIDIplay::LoadMIDI(const void *data, size_t size) +bool MIDIplay::LoadMIDI_pre() { - fileReader file; - file.openData(data, size); - return LoadMIDI(file); -} - -bool MIDIplay::LoadMIDI(MIDIplay::fileReader &fr) -{ - size_t fsize; - ADL_UNUSED(fsize); - //! Temp buffer for conversion - AdlMIDI_CPtr cvt_buf; - errorString.clear(); - - #ifdef DISABLE_EMBEDDED_BANKS - if((opl.AdlBank != ~0u) || opl.dynamic_banks.empty()) +#ifdef DISABLE_EMBEDDED_BANKS + if((m_synth.m_embeddedBank != OPL3::CustomBankTag) || m_synth.m_insBanks.empty()) { errorStringOut = "Bank is not set! Please load any instruments bank by using of adl_openBankFile() or adl_openBankData() functions!"; return false; } - #endif - - if(!fr.isValid()) - { - errorStringOut = "Invalid data stream!\n"; - #ifndef _WIN32 - errorStringOut += std::strerror(errno); - #endif - return false; - } - +#endif /**** Set all properties BEFORE starting of actial file reading! ****/ + resetMIDI(); applySetup(); - atEnd = false; - loopStart = true; - invalidLoop = false; + return true; +} - bool is_GMF = false; // GMD/MUS files (ScummVM) - //bool is_MUS = false; // MUS/DMX files (Doom) - bool is_IMF = false; // IMF - bool is_CMF = false; // Creative Music format (CMF/CTMF) - bool is_RSXX = false; // RSXX, such as Cartooners - - const size_t HeaderSize = 4 + 4 + 2 + 2 + 2; // 14 - char HeaderBuf[HeaderSize] = ""; - size_t DeltaTicks = 192, TrackCount = 1; - -riffskip: - fsize = fr.read(HeaderBuf, 1, HeaderSize); - - if(std::memcmp(HeaderBuf, "RIFF", 4) == 0) +bool MIDIplay::LoadMIDI_post() +{ + MidiSequencer::FileFormat format = m_sequencer.getFormat(); + if(format == MidiSequencer::Format_CMF) { - fr.seek(6l, SEEK_CUR); - goto riffskip; - } + const std::vector &instruments = m_sequencer.getRawCmfInstruments(); + m_synth.m_insBanks.clear();//Clean up old banks - if(std::memcmp(HeaderBuf, "GMF\x1", 4) == 0) - { - // GMD/MUS files (ScummVM) - fr.seek(7 - static_cast(HeaderSize), SEEK_CUR); - is_GMF = true; - } - #ifndef ADLMIDI_DISABLE_MUS_SUPPORT - else if(std::memcmp(HeaderBuf, "MUS\x1A", 4) == 0) - { - // MUS/DMX files (Doom) - fr.seek(0, SEEK_END); - size_t mus_len = fr.tell(); - fr.seek(0, SEEK_SET); - uint8_t *mus = (uint8_t *)malloc(mus_len); - if(!mus) + uint16_t ins_count = static_cast(instruments.size()); + for(uint16_t i = 0; i < ins_count; ++i) { - errorStringOut = "Out of memory!"; - return false; - } - fr.read(mus, 1, mus_len); - //Close source stream - fr.close(); - - uint8_t *mid = NULL; - uint32_t mid_len = 0; - int m2mret = AdlMidi_mus2midi(mus, static_cast(mus_len), - &mid, &mid_len, 0); - if(mus) free(mus); - if(m2mret < 0) - { - errorStringOut = "Invalid MUS/DMX data format!"; - return false; - } - cvt_buf.reset(mid); - //Open converted MIDI file - fr.openData(mid, static_cast(mid_len)); - //Re-Read header again! - goto riffskip; - } - #endif //ADLMIDI_DISABLE_MUS_SUPPORT - #ifndef ADLMIDI_DISABLE_XMI_SUPPORT - else if(std::memcmp(HeaderBuf, "FORM", 4) == 0) - { - if(std::memcmp(HeaderBuf + 8, "XDIR", 4) != 0) - { - fr.close(); - errorStringOut = fr._fileName + ": Invalid format\n"; - return false; - } - - fr.seek(0, SEEK_END); - size_t mus_len = fr.tell(); - fr.seek(0, SEEK_SET); - uint8_t *mus = (uint8_t*)malloc(mus_len); - if(!mus) - { - errorStringOut = "Out of memory!"; - return false; - } - fr.read(mus, 1, mus_len); - //Close source stream - fr.close(); - - uint8_t *mid = NULL; - uint32_t mid_len = 0; - int m2mret = AdlMidi_xmi2midi(mus, static_cast(mus_len), - &mid, &mid_len, XMIDI_CONVERT_NOCONVERSION); - if(mus) free(mus); - if(m2mret < 0) - { - errorStringOut = "Invalid XMI data format!"; - return false; - } - cvt_buf.reset(mid); - //Open converted MIDI file - fr.openData(mid, static_cast(mid_len)); - //Re-Read header again! - goto riffskip; - } - #endif //ADLMIDI_DISABLE_XMI_SUPPORT - else if(std::memcmp(HeaderBuf, "CTMF", 4) == 0) - { - opl.dynamic_banks.clear(); - // Creative Music Format (CMF). - // When playing CTMF files, use the following commandline: - // adlmidi song8.ctmf -p -v 1 1 0 - // i.e. enable percussion mode, deeper vibrato, and use only 1 card. - is_CMF = true; - //unsigned version = ReadLEint(HeaderBuf+4, 2); - uint64_t ins_start = ReadLEint(HeaderBuf + 6, 2); - uint64_t mus_start = ReadLEint(HeaderBuf + 8, 2); - //unsigned deltas = ReadLEint(HeaderBuf+10, 2); - uint64_t ticks = ReadLEint(HeaderBuf + 12, 2); - // Read title, author, remarks start offsets in file - fr.read(HeaderBuf, 1, 6); - //unsigned long notes_starts[3] = {ReadLEint(HeaderBuf+0,2),ReadLEint(HeaderBuf+0,4),ReadLEint(HeaderBuf+0,6)}; - fr.seek(16, SEEK_CUR); // Skip the channels-in-use table - fr.read(HeaderBuf, 1, 4); - uint64_t ins_count = ReadLEint(HeaderBuf + 0, 2); //, basictempo = ReadLEint(HeaderBuf+2, 2); - fr.seek(static_cast(ins_start), SEEK_SET); - - //std::printf("%u instruments\n", ins_count); - for(unsigned i = 0; i < ins_count; ++i) - { - unsigned bank = i / 256; - bank = (bank & 127) + ((bank >> 7) << 8); + const uint8_t *InsData = instruments[i].data; + size_t bank = i / 256; + bank = ((bank & 127) + ((bank >> 7) << 8)); if(bank > 127 + (127 << 8)) break; - bank += (i % 256 < 128) ? 0 : OPL3::PercussionTag; + bank += (i % 256 < 128) ? 0 : size_t(OPL3::PercussionTag); - unsigned char InsData[16]; - fr.read(InsData, 1, 16); /*std::printf("Ins %3u: %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X\n", i, InsData[0],InsData[1],InsData[2],InsData[3], InsData[4],InsData[5],InsData[6],InsData[7], InsData[8],InsData[9],InsData[10],InsData[11], InsData[12],InsData[13],InsData[14],InsData[15]);*/ - adlinsdata2 &adlins = opl.dynamic_banks[bank].ins[i % 128]; + adlinsdata2 &adlins = m_synth.m_insBanks[bank].ins[i % 128]; adldata adl; adl.modulator_E862 = ((static_cast(InsData[8] & 0x07) << 24) & 0xFF000000) //WaveForm @@ -500,224 +209,79 @@ riffskip: adlins.voice2_fine_tune = 0.0; } - fr.seeku(mus_start, SEEK_SET); - TrackCount = 1; - DeltaTicks = (size_t)ticks; - opl.AdlBank = ~0u; // Ignore AdlBank number, use dynamic banks instead + m_synth.m_embeddedBank = OPL3::CustomBankTag; // Ignore AdlBank number, use dynamic banks instead //std::printf("CMF deltas %u ticks %u, basictempo = %u\n", deltas, ticks, basictempo); - opl.AdlPercussionMode = true; - opl.m_musicMode = OPL3::MODE_CMF; - opl.m_volumeScale = OPL3::VOLUME_NATIVE; + m_synth.m_rhythmMode = true; + m_synth.m_musicMode = OPL3::MODE_CMF; + m_synth.m_volumeScale = OPL3::VOLUME_NATIVE; + + m_synth.m_numChips = 1; + m_synth.m_numFourOps = 0; + } + else if(format == MidiSequencer::Format_RSXX) + { + //opl.CartoonersVolumes = true; + m_synth.m_musicMode = OPL3::MODE_RSXX; + m_synth.m_volumeScale = OPL3::VOLUME_NATIVE; + + m_synth.m_numChips = 1; + m_synth.m_numFourOps = 0; + } + else if(format == MidiSequencer::Format_IMF) + { + //std::fprintf(stderr, "Done reading IMF file\n"); + m_synth.m_numFourOps = 0; //Don't use 4-operator channels for IMF playing! + m_synth.m_musicMode = OPL3::MODE_IMF; + + m_synth.m_numChips = 1; + m_synth.m_numFourOps = 0; } else { - // Try to identify RSXX format - if(HeaderBuf[0] == 0x7D) - { - fr.seek(0x6D, SEEK_SET); - fr.read(HeaderBuf, 6, 1); - if(std::memcmp(HeaderBuf, "rsxx}u", 6) == 0) - { - is_RSXX = true; - fr.seek(0x7D, SEEK_SET); - TrackCount = 1; - DeltaTicks = 60; - //opl.CartoonersVolumes = true; - opl.m_musicMode = OPL3::MODE_RSXX; - opl.m_volumeScale = OPL3::VOLUME_NATIVE; - } - } - - // Try parsing as an IMF file - if(!is_RSXX) - { - do - { - uint8_t raw[4]; - size_t end = static_cast(HeaderBuf[0]) + 256 * static_cast(HeaderBuf[1]); - - if(!end || (end & 3)) - break; - - size_t backup_pos = fr.tell(); - int64_t sum1 = 0, sum2 = 0; - fr.seek(2, SEEK_SET); - - for(unsigned n = 0; n < 42; ++n) - { - if(fr.read(raw, 1, 4) != 4) - break; - int64_t value1 = raw[0]; - value1 += raw[1] << 8; - sum1 += value1; - int64_t value2 = raw[2]; - value2 += raw[3] << 8; - sum2 += value2; - } - - fr.seek(static_cast(backup_pos), SEEK_SET); - - if(sum1 > sum2) - { - is_IMF = true; - DeltaTicks = 1; - } - } while(false); - } - - if(!is_IMF && !is_RSXX) - { - if(std::memcmp(HeaderBuf, "MThd\0\0\0\6", 8) != 0) - { - fr.close(); - errorStringOut = fr._fileName + ": Invalid format, Header signature is unknown!\n"; - return false; - } - - /*size_t Fmt = ReadBEint(HeaderBuf + 8, 2);*/ - TrackCount = (size_t)ReadBEint(HeaderBuf + 10, 2); - DeltaTicks = (size_t)ReadBEint(HeaderBuf + 12, 2); - } + m_synth.m_numChips = m_setup.numChips; + if(m_setup.numFourOps < 0) + adlCalculateFourOpChannels(this, true); } - TrackData.clear(); - TrackData.resize(TrackCount, std::vector()); - InvDeltaTicks = fraction(1, 1000000l * static_cast(DeltaTicks)); - if(is_CMF || is_RSXX) - Tempo = fraction(1, static_cast(DeltaTicks)); - else - Tempo = fraction(1, static_cast(DeltaTicks) * 2); - static const unsigned char EndTag[4] = {0xFF, 0x2F, 0x00, 0x00}; - size_t totalGotten = 0; - - for(size_t tk = 0; tk < TrackCount; ++tk) - { - // Read track header - size_t TrackLength; - - if(is_IMF) - { - //std::fprintf(stderr, "Reading IMF file...\n"); - size_t end = static_cast(HeaderBuf[0]) + 256 * static_cast(HeaderBuf[1]); - unsigned IMF_tempo = 1428; - static const unsigned char imf_tempo[] = {0x0,//Zero delay! - MidiEvent::T_SPECIAL, MidiEvent::ST_TEMPOCHANGE, 0x4, - static_cast(IMF_tempo >> 24), - static_cast(IMF_tempo >> 16), - static_cast(IMF_tempo >> 8), - static_cast(IMF_tempo) - }; - TrackData[tk].insert(TrackData[tk].end(), imf_tempo, imf_tempo + sizeof(imf_tempo)); - TrackData[tk].push_back(0x00); - fr.seek(2, SEEK_SET); - - while(fr.tell() < end && !fr.eof()) - { - uint8_t special_event_buf[5]; - uint8_t raw[4]; - special_event_buf[0] = MidiEvent::T_SPECIAL; - special_event_buf[1] = MidiEvent::ST_RAWOPL; - special_event_buf[2] = 0x02; - if(fr.read(raw, 1, 4) != 4) - break; - special_event_buf[3] = raw[0]; // port index - special_event_buf[4] = raw[1]; // port value - uint32_t delay = static_cast(raw[2]); - delay += 256 * static_cast(raw[3]); - totalGotten += 4; - //if(special_event_buf[3] <= 8) continue; - //fprintf(stderr, "Put %02X <- %02X, plus %04X delay\n", special_event_buf[3],special_event_buf[4], delay); - TrackData[tk].insert(TrackData[tk].end(), special_event_buf, special_event_buf + 5); - //if(delay>>21) TrackData[tk].push_back( 0x80 | ((delay>>21) & 0x7F ) ); - if(delay >> 14) - TrackData[tk].push_back(0x80 | ((delay >> 14) & 0x7F)); - if(delay >> 7) - TrackData[tk].push_back(0x80 | ((delay >> 7) & 0x7F)); - TrackData[tk].push_back(((delay >> 0) & 0x7F)); - } - - TrackData[tk].insert(TrackData[tk].end(), EndTag + 0, EndTag + 4); - //CurrentPosition.track[tk].delay = 0; - //CurrentPosition.began = true; - //std::fprintf(stderr, "Done reading IMF file\n"); - opl.NumFourOps = 0; //Don't use 4-operator channels for IMF playing! - opl.m_musicMode = OPL3::MODE_IMF; - } - else - { - // Take the rest of the file - if(is_GMF || is_CMF || is_RSXX) - { - size_t pos = fr.tell(); - fr.seek(0, SEEK_END); - TrackLength = fr.tell() - pos; - fr.seek(static_cast(pos), SEEK_SET); - } - //else if(is_MUS) // Read TrackLength from file position 4 - //{ - // size_t pos = fr.tell(); - // fr.seek(4, SEEK_SET); - // TrackLength = static_cast(fr.getc()); - // TrackLength += static_cast(fr.getc() << 8); - // fr.seek(static_cast(pos), SEEK_SET); - //} - else - { - fsize = fr.read(HeaderBuf, 1, 8); - if(std::memcmp(HeaderBuf, "MTrk", 4) != 0) - { - fr.close(); - errorStringOut = fr._fileName + ": Invalid format, MTrk signature is not found!\n"; - return false; - } - TrackLength = (size_t)ReadBEint(HeaderBuf + 4, 4); - } - - // Read track data - TrackData[tk].resize(TrackLength); - fsize = fr.read(&TrackData[tk][0], 1, TrackLength); - totalGotten += fsize; - - if(is_GMF/*|| is_MUS*/) // Note: CMF does include the track end tag. - TrackData[tk].insert(TrackData[tk].end(), EndTag + 0, EndTag + 4); - if(is_RSXX)//Finalize raw track data with a zero - TrackData[tk].push_back(0); - - //bool ok = false; - //// Read next event time - //uint64_t tkDelay = ReadVarLenEx(tk, ok); - //if(ok) - // CurrentPosition.track[tk].delay = tkDelay; - //else - //{ - // std::stringstream msg; - // msg << fr._fileName << ": invalid variable length in the track " << tk << "! (error code " << tkDelay << ")"; - // ADLMIDI_ErrorString = msg.str(); - // return false; - //} - } - } - - for(size_t tk = 0; tk < TrackCount; ++tk) - totalGotten += TrackData[tk].size(); - - if(totalGotten == 0) - { - errorStringOut = fr._fileName + ": Empty track data"; - return false; - } - - //Build new MIDI events table - if(!buildTrackData()) - { - errorStringOut = fr._fileName + ": MIDI data parsing error has occouped!\n" + errorString; - return false; - } - - opl.Reset(m_setup.emulator, m_setup.PCM_RATE, this); // Reset OPL3 chip + m_setup.tick_skip_samples_delay = 0; + m_synth.reset(m_setup.emulator, m_setup.PCM_RATE, this); // Reset OPL3 chip //opl.Reset(); // ...twice (just in case someone misprogrammed OPL3 previously) - ch.clear(); - ch.resize(opl.NumChannels); + m_chipChannels.clear(); + m_chipChannels.resize(m_synth.m_numChannels); + return true; } -#endif //ADLMIDI_DISABLE_MIDI_SEQUENCER + +bool MIDIplay::LoadMIDI(const std::string &filename) +{ + FileAndMemReader file; + file.openFile(filename.c_str()); + if(!LoadMIDI_pre()) + return false; + if(!m_sequencer.loadMIDI(file)) + { + errorStringOut = m_sequencer.getErrorString(); + return false; + } + if(!LoadMIDI_post()) + return false; + return true; +} + +bool MIDIplay::LoadMIDI(const void *data, size_t size) +{ + FileAndMemReader file; + file.openData(data, size); + if(!LoadMIDI_pre()) + return false; + if(!m_sequencer.loadMIDI(file)) + { + errorStringOut = m_sequencer.getErrorString(); + return false; + } + if(!LoadMIDI_post()) + return false; + return true; +} + +#endif /* ADLMIDI_DISABLE_MIDI_SEQUENCER */ diff --git a/src/sound/adlmidi/adlmidi_midiplay.cpp b/src/sound/adlmidi/adlmidi_midiplay.cpp index 8996d67d0..1e1da0789 100644 --- a/src/sound/adlmidi/adlmidi_midiplay.cpp +++ b/src/sound/adlmidi/adlmidi_midiplay.cpp @@ -25,7 +25,7 @@ // Mapping from MIDI volume level to OPL level value. -static const uint8_t DMX_volume_mapping_table[128] = +static const uint_fast32_t DMX_volume_mapping_table[128] = { 0, 1, 3, 5, 6, 8, 10, 11, 13, 14, 16, 17, 19, 20, 22, 23, @@ -45,7 +45,7 @@ static const uint8_t DMX_volume_mapping_table[128] = 124, 124, 125, 125, 126, 126, 127, 127, }; -static const uint8_t W9X_volume_mapping_table[32] = +static const uint_fast32_t W9X_volume_mapping_table[32] = { 63, 63, 40, 36, 32, 28, 23, 21, 19, 17, 15, 14, 13, 12, 11, 10, @@ -93,877 +93,179 @@ static const uint8_t PercussionMap[256] = "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"; +enum { MasterVolumeDefault = 127 }; + inline bool isXgPercChannel(uint8_t msb, uint8_t lsb) { return (msb == 0x7E || msb == 0x7F) && (lsb == 0); } -void MIDIplay::AdlChannel::AddAge(int64_t ms) +void MIDIplay::AdlChannel::addAge(int64_t us) { - const int64_t neg = static_cast(-0x1FFFFFFFl); + const int64_t neg = 1000 * static_cast(-0x1FFFFFFFll); if(users_empty()) - koff_time_until_neglible = std::max(int64_t(koff_time_until_neglible - ms), neg); + { + koff_time_until_neglible_us = std::max(koff_time_until_neglible_us - us, neg); + if(koff_time_until_neglible_us < 0) + koff_time_until_neglible_us = 0; + } else { - koff_time_until_neglible = 0; + koff_time_until_neglible_us = 0; for(LocationData *i = users_first; i; i = i->next) { if(!i->fixed_sustain) - i->kon_time_until_neglible = std::max(i->kon_time_until_neglible - ms, neg); - i->vibdelay += ms; + i->kon_time_until_neglible_us = std::max(i->kon_time_until_neglible_us - us, neg); + i->vibdelay_us += us; } } } -#ifndef ADLMIDI_DISABLE_MIDI_SEQUENCER - -MIDIplay::MidiEvent::MidiEvent() : - type(T_UNKNOWN), - subtype(T_UNKNOWN), - channel(0), - isValid(1), - absPosition(0) -{} - - -MIDIplay::MidiTrackRow::MidiTrackRow() : - time(0.0), - delay(0), - absPos(0), - timeDelay(0.0) -{} - -void MIDIplay::MidiTrackRow::reset() -{ - time = 0.0; - delay = 0; - absPos = 0; - timeDelay = 0.0; - events.clear(); -} - -void MIDIplay::MidiTrackRow::sortEvents(bool *noteStates) -{ - typedef std::vector EvtArr; - EvtArr metas; - EvtArr noteOffs; - EvtArr controllers; - EvtArr anyOther; - - metas.reserve(events.size()); - noteOffs.reserve(events.size()); - controllers.reserve(events.size()); - anyOther.reserve(events.size()); - - for(size_t i = 0; i < events.size(); i++) - { - if(events[i].type == MidiEvent::T_NOTEOFF) - noteOffs.push_back(events[i]); - else if((events[i].type == MidiEvent::T_CTRLCHANGE) - || (events[i].type == MidiEvent::T_PATCHCHANGE) - || (events[i].type == MidiEvent::T_WHEEL) - || (events[i].type == MidiEvent::T_CHANAFTTOUCH)) - { - controllers.push_back(events[i]); - } - else if((events[i].type == MidiEvent::T_SPECIAL) && (events[i].subtype == MidiEvent::ST_MARKER)) - metas.push_back(events[i]); - else - anyOther.push_back(events[i]); - } - - /* - * If Note-Off and it's Note-On is on the same row - move this damned note off down! - */ - if(noteStates) - { - std::set markAsOn; - for(size_t i = 0; i < anyOther.size(); i++) - { - const MidiEvent e = anyOther[i]; - if(e.type == MidiEvent::T_NOTEON) - { - const size_t note_i = (e.channel * 255) + (e.data[0] & 0x7F); - //Check, was previously note is on or off - bool wasOn = noteStates[note_i]; - markAsOn.insert(note_i); - // Detect zero-length notes are following previously pressed note - int noteOffsOnSameNote = 0; - for(EvtArr::iterator j = noteOffs.begin(); j != noteOffs.end();) - { - //If note was off, and note-off on same row with note-on - move it down! - if( - ((*j).channel == e.channel) && - ((*j).data[0] == e.data[0]) - ) - { - //If note is already off OR more than one note-off on same row and same note - if(!wasOn || (noteOffsOnSameNote != 0)) - { - anyOther.push_back(*j); - j = noteOffs.erase(j); - markAsOn.erase(note_i); - continue; - } - else - { - //When same row has many note-offs on same row - //that means a zero-length note follows previous note - //it must be shuted down - noteOffsOnSameNote++; - } - } - j++; - } - } - } - - //Mark other notes as released - for(EvtArr::iterator j = noteOffs.begin(); j != noteOffs.end(); j++) - { - size_t note_i = (j->channel * 255) + (j->data[0] & 0x7F); - noteStates[note_i] = false; - } - - for(std::set::iterator j = markAsOn.begin(); j != markAsOn.end(); j++) - noteStates[*j] = true; - } - /***********************************************************************************/ - - events.clear(); - events.insert(events.end(), noteOffs.begin(), noteOffs.end()); - events.insert(events.end(), metas.begin(), metas.end()); - events.insert(events.end(), controllers.begin(), controllers.end()); - events.insert(events.end(), anyOther.begin(), anyOther.end()); -} -#endif //ADLMIDI_DISABLE_MIDI_SEQUENCER - -#ifndef ADLMIDI_DISABLE_MIDI_SEQUENCER -bool MIDIplay::buildTrackData() -{ - fullSongTimeLength = 0.0; - loopStartTime = -1.0; - loopEndTime = -1.0; - musTitle.clear(); - musCopyright.clear(); - musTrackTitles.clear(); - musMarkers.clear(); - caugh_missing_instruments.clear(); - caugh_missing_banks_melodic.clear(); - caugh_missing_banks_percussion.clear(); - trackDataNew.clear(); - const size_t trackCount = TrackData.size(); - trackDataNew.resize(trackCount, MidiTrackQueue()); - - invalidLoop = false; - bool gotLoopStart = false, gotLoopEnd = false, gotLoopEventInThisRow = false; - //! Tick position of loop start tag - uint64_t loopStartTicks = 0; - //! Tick position of loop end tag - uint64_t loopEndTicks = 0; - //! Full length of song in ticks - uint64_t ticksSongLength = 0; - //! Cache for error message strign - char error[150]; - - CurrentPositionNew.track.clear(); - CurrentPositionNew.track.resize(trackCount); - - //! Caches note on/off states. - bool noteStates[16 * 255]; - /* This is required to carefully detect zero-length notes * - * and avoid a move of "note-off" event over "note-on" while sort. * - * Otherwise, after sort those notes will play infinite sound */ - - //Tempo change events - std::vector tempos; - - /* - * TODO: Make this be safer for memory in case of broken input data - * which may cause going away of available track data (and then give a crash!) - * - * POST: Check this more carefully for possible vulnuabilities are can crash this - */ - for(size_t tk = 0; tk < trackCount; ++tk) - { - uint64_t abs_position = 0; - int status = 0; - MidiEvent event; - bool ok = false; - uint8_t *end = TrackData[tk].data() + TrackData[tk].size(); - uint8_t *trackPtr = TrackData[tk].data(); - std::memset(noteStates, 0, sizeof(noteStates)); - - //Time delay that follows the first event in the track - { - MidiTrackRow evtPos; - if(opl.m_musicMode == OPL3::MODE_RSXX) - ok = true; - else - evtPos.delay = ReadVarLenEx(&trackPtr, end, ok); - if(!ok) - { - int len = snprintf(error, 150, "buildTrackData: Can't read variable-length value at begin of track %d.\n", (int)tk); - if((len > 0) && (len < 150)) - errorString += std::string(error, (size_t)len); - return false; - } - - //HACK: Begin every track with "Reset all controllers" event to avoid controllers state break came from end of song - for(uint8_t chan = 0; chan < 16; chan++) - { - MidiEvent event; - event.type = MidiEvent::T_CTRLCHANGE; - event.channel = chan; - event.data.push_back(121); - event.data.push_back(0); - evtPos.events.push_back(event); - } - - evtPos.absPos = abs_position; - abs_position += evtPos.delay; - trackDataNew[tk].push_back(evtPos); - } - - MidiTrackRow evtPos; - do - { - event = parseEvent(&trackPtr, end, status); - if(!event.isValid) - { - int len = snprintf(error, 150, "buildTrackData: Fail to parse event in the track %d.\n", (int)tk); - if((len > 0) && (len < 150)) - errorString += std::string(error, (size_t)len); - return false; - } - - evtPos.events.push_back(event); - if(event.type == MidiEvent::T_SPECIAL) - { - if(event.subtype == MidiEvent::ST_TEMPOCHANGE) - { - event.absPosition = abs_position; - tempos.push_back(event); - } - else if(!invalidLoop && (event.subtype == MidiEvent::ST_LOOPSTART)) - { - /* - * loopStart is invalid when: - * - starts together with loopEnd - * - appears more than one time in same MIDI file - */ - if(gotLoopStart || gotLoopEventInThisRow) - invalidLoop = true; - else - { - gotLoopStart = true; - loopStartTicks = abs_position; - } - //In this row we got loop event, register this! - gotLoopEventInThisRow = true; - } - else if(!invalidLoop && (event.subtype == MidiEvent::ST_LOOPEND)) - { - /* - * loopEnd is invalid when: - * - starts before loopStart - * - starts together with loopStart - * - appars more than one time in same MIDI file - */ - if(gotLoopEnd || gotLoopEventInThisRow) - invalidLoop = true; - else - { - gotLoopEnd = true; - loopEndTicks = abs_position; - } - //In this row we got loop event, register this! - gotLoopEventInThisRow = true; - } - } - - if(event.subtype != MidiEvent::ST_ENDTRACK)//Don't try to read delta after EndOfTrack event! - { - evtPos.delay = ReadVarLenEx(&trackPtr, end, ok); - if(!ok) - { - /* End of track has been reached! However, there is no EOT event presented */ - event.type = MidiEvent::T_SPECIAL; - event.subtype = MidiEvent::ST_ENDTRACK; - } - } - - if((evtPos.delay > 0) || (event.subtype == MidiEvent::ST_ENDTRACK)) - { - evtPos.absPos = abs_position; - abs_position += evtPos.delay; - evtPos.sortEvents(noteStates); - trackDataNew[tk].push_back(evtPos); - evtPos.reset(); - gotLoopEventInThisRow = false; - } - } - while((trackPtr <= end) && (event.subtype != MidiEvent::ST_ENDTRACK)); - - if(ticksSongLength < abs_position) - ticksSongLength = abs_position; - //Set the chain of events begin - if(trackDataNew[tk].size() > 0) - CurrentPositionNew.track[tk].pos = trackDataNew[tk].begin(); - } - - if(gotLoopStart && !gotLoopEnd) - { - gotLoopEnd = true; - loopEndTicks = ticksSongLength; - } - - //loopStart must be located before loopEnd! - if(loopStartTicks >= loopEndTicks) - invalidLoop = true; - - /********************************************************************************/ - //Calculate time basing on collected tempo events - /********************************************************************************/ - for(size_t tk = 0; tk < trackCount; ++tk) - { - fraction currentTempo = Tempo; - double time = 0.0; - uint64_t abs_position = 0; - size_t tempo_change_index = 0; - MidiTrackQueue &track = trackDataNew[tk]; - if(track.empty()) - continue;//Empty track is useless! - -#ifdef DEBUG_TIME_CALCULATION - std::fprintf(stdout, "\n============Track %" PRIuPTR "=============\n", tk); - std::fflush(stdout); -#endif - - MidiTrackRow *posPrev = &(*(track.begin()));//First element - for(MidiTrackQueue::iterator it = track.begin(); it != track.end(); it++) - { -#ifdef DEBUG_TIME_CALCULATION - bool tempoChanged = false; -#endif - MidiTrackRow &pos = *it; - if((posPrev != &pos) && //Skip first event - (!tempos.empty()) && //Only when in-track tempo events are available - (tempo_change_index < tempos.size()) - ) - { - // If tempo event is going between of current and previous event - if(tempos[tempo_change_index].absPosition <= pos.absPos) - { - //Stop points: begin point and tempo change points are before end point - std::vector points; - fraction t; - TempoChangePoint firstPoint = {posPrev->absPos, currentTempo}; - points.push_back(firstPoint); - - //Collect tempo change points between previous and current events - do - { - TempoChangePoint tempoMarker; - MidiEvent &tempoPoint = tempos[tempo_change_index]; - tempoMarker.absPos = tempoPoint.absPosition; - tempoMarker.tempo = InvDeltaTicks * fraction(ReadBEint(tempoPoint.data.data(), tempoPoint.data.size())); - points.push_back(tempoMarker); - tempo_change_index++; - } - while((tempo_change_index < tempos.size()) && - (tempos[tempo_change_index].absPosition <= pos.absPos)); - - // Re-calculate time delay of previous event - time -= posPrev->timeDelay; - posPrev->timeDelay = 0.0; - - for(size_t i = 0, j = 1; j < points.size(); i++, j++) - { - /* If one or more tempo events are appears between of two events, - * calculate delays between each tempo point, begin and end */ - uint64_t midDelay = 0; - //Delay between points - midDelay = points[j].absPos - points[i].absPos; - //Time delay between points - t = midDelay * currentTempo; - posPrev->timeDelay += t.value(); - - //Apply next tempo - currentTempo = points[j].tempo; -#ifdef DEBUG_TIME_CALCULATION - tempoChanged = true; -#endif - } - //Then calculate time between last tempo change point and end point - TempoChangePoint tailTempo = points.back(); - uint64_t postDelay = pos.absPos - tailTempo.absPos; - t = postDelay * currentTempo; - posPrev->timeDelay += t.value(); - - //Store Common time delay - posPrev->time = time; - time += posPrev->timeDelay; - } - } - - fraction t = pos.delay * currentTempo; - pos.timeDelay = t.value(); - pos.time = time; - time += pos.timeDelay; - - //Capture markers after time value calculation - for(size_t i = 0; i < pos.events.size(); i++) - { - MidiEvent &e = pos.events[i]; - if((e.type == MidiEvent::T_SPECIAL) && (e.subtype == MidiEvent::ST_MARKER)) - { - MIDI_MarkerEntry marker; - marker.label = std::string((char *)e.data.data(), e.data.size()); - marker.pos_ticks = pos.absPos; - marker.pos_time = pos.time; - musMarkers.push_back(marker); - } - } - - //Capture loop points time positions - if(!invalidLoop) - { - // Set loop points times - if(loopStartTicks == pos.absPos) - loopStartTime = pos.time; - else if(loopEndTicks == pos.absPos) - loopEndTime = pos.time; - } - -#ifdef DEBUG_TIME_CALCULATION - std::fprintf(stdout, "= %10" PRId64 " = %10f%s\n", pos.absPos, pos.time, tempoChanged ? " <----TEMPO CHANGED" : ""); - std::fflush(stdout); -#endif - - abs_position += pos.delay; - posPrev = &pos; - } - - if(time > fullSongTimeLength) - fullSongTimeLength = time; - } - - fullSongTimeLength += postSongWaitDelay; - //Set begin of the music - trackBeginPositionNew = CurrentPositionNew; - //Initial loop position will begin at begin of track until passing of the loop point - LoopBeginPositionNew = CurrentPositionNew; - - /********************************************************************************/ - //Resolve "hell of all times" of too short drum notes: - //move too short percussion note-offs far far away as possible - /********************************************************************************/ -#if 1 //Use this to record WAVEs for comparison before/after implementing of this - if(opl.m_musicMode == OPL3::MODE_MIDI)//Percussion fix is needed for MIDI only, not for IMF/RSXX or CMF - { - //! Minimal real time in seconds -#define DRUM_NOTE_MIN_TIME 0.03 - //! Minimal ticks count -#define DRUM_NOTE_MIN_TICKS 15 - struct NoteState - { - double delay; - uint64_t delayTicks; - bool isOn; - char ___pad[7]; - } drNotes[255]; - uint16_t banks[16]; - - for(size_t tk = 0; tk < trackCount; ++tk) - { - std::memset(drNotes, 0, sizeof(drNotes)); - std::memset(banks, 0, sizeof(banks)); - MidiTrackQueue &track = trackDataNew[tk]; - if(track.empty()) - continue;//Empty track is useless! - - for(MidiTrackQueue::iterator it = track.begin(); it != track.end(); it++) - { - MidiTrackRow &pos = *it; - - for(ssize_t e = 0; e < (ssize_t)pos.events.size(); e++) - { - MidiEvent *et = &pos.events[(size_t)e]; - - /* Set MSB/LSB bank */ - if(et->type == MidiEvent::T_CTRLCHANGE) - { - uint8_t ctrlno = et->data[0]; - uint8_t value = et->data[1]; - switch(ctrlno) - { - case 0: // Set bank msb (GM bank) - banks[et->channel] = uint16_t(uint16_t(value) << 8) | uint16_t(banks[et->channel] & 0x00FF); - break; - case 32: // Set bank lsb (XG bank) - banks[et->channel] = (banks[et->channel] & 0xFF00) | (uint16_t(value) & 0x00FF); - break; - } - continue; - } - - bool percussion = (et->channel == 9) || - banks[et->channel] == 0x7E00 || //XG SFX1/SFX2 channel (16128 signed decimal) - banks[et->channel] == 0x7F00; //XG Percussion channel (16256 signed decimal) - if(!percussion) - continue; - - if(et->type == MidiEvent::T_NOTEON) - { - uint8_t note = et->data[0] & 0x7F; - NoteState &ns = drNotes[note]; - ns.isOn = true; - ns.delay = 0.0; - ns.delayTicks = 0; - } - else if(et->type == MidiEvent::T_NOTEOFF) - { - uint8_t note = et->data[0] & 0x7F; - NoteState &ns = drNotes[note]; - if(ns.isOn) - { - ns.isOn = false; - if(ns.delayTicks < DRUM_NOTE_MIN_TICKS || ns.delay < DRUM_NOTE_MIN_TIME)//If note is too short - { - //Move it into next event position if that possible - for(MidiTrackQueue::iterator itNext = it; - itNext != track.end(); - itNext++) - { - MidiTrackRow &posN = *itNext; - if(ns.delayTicks > DRUM_NOTE_MIN_TICKS && ns.delay > DRUM_NOTE_MIN_TIME) - { - //Put note-off into begin of next event list - posN.events.insert(posN.events.begin(), pos.events[(size_t)e]); - //Renive this event from a current row - pos.events.erase(pos.events.begin() + (int)e); - e--; - break; - } - ns.delay += posN.timeDelay; - ns.delayTicks += posN.delay; - } - } - ns.delay = 0.0; - ns.delayTicks = 0; - } - } - } - - //Append time delays to sustaining notes - for(size_t no = 0; no < 128; no++) - { - NoteState &ns = drNotes[no]; - if(ns.isOn) - { - ns.delay += pos.timeDelay; - ns.delayTicks += pos.delay; - } - } - } - } -#undef DRUM_NOTE_MIN_TIME -#undef DRUM_NOTE_MIN_TICKS - } -#endif - - return true; -} -#endif - - MIDIplay::MIDIplay(unsigned long sampleRate): - cmf_percussion_mode(false), - m_arpeggioCounter(0), - m_audioTickCounter(0) -#ifndef ADLMIDI_DISABLE_MIDI_SEQUENCER - , fullSongTimeLength(0.0), - postSongWaitDelay(1.0), - loopStartTime(-1.0), - loopEndTime(-1.0), - tempoMultiplier(1.0), - atEnd(false), - loopStart(false), - loopEnd(false), - invalidLoop(false) + m_cmfPercussionMode(false), + m_masterVolume(MasterVolumeDefault), + m_sysExDeviceId(0), + m_synthMode(Mode_XG), + m_arpeggioCounter(0) +#if defined(ADLMIDI_AUDIO_TICK_HANDLER) + , m_audioTickCounter(0) #endif { - devices.clear(); + m_midiDevices.clear(); - m_setup.emulator = ADLMIDI_EMU_NUKED; + m_setup.emulator = adl_getLowestEmulator(); m_setup.runAtPcmRate = false; m_setup.PCM_RATE = sampleRate; m_setup.mindelay = 1.0 / (double)m_setup.PCM_RATE; m_setup.maxdelay = 512.0 / (double)m_setup.PCM_RATE; - m_setup.AdlBank = 0; - m_setup.NumFourOps = 7; - m_setup.NumCards = 2; - m_setup.HighTremoloMode = -1; - m_setup.HighVibratoMode = -1; - m_setup.AdlPercussionMode = -1; - m_setup.LogarithmicVolumes = false; - m_setup.VolumeModel = ADLMIDI_VolumeModel_AUTO; + m_setup.bankId = 0; + m_setup.numFourOps = -1; + m_setup.numChips = 2; + m_setup.deepTremoloMode = -1; + m_setup.deepVibratoMode = -1; + m_setup.rhythmMode = -1; + m_setup.logarithmicVolumes = false; + m_setup.volumeScaleModel = ADLMIDI_VolumeModel_AUTO; //m_setup.SkipForward = 0; - m_setup.loopingIsEnabled = false; - m_setup.ScaleModulators = -1; + m_setup.scaleModulators = -1; m_setup.fullRangeBrightnessCC74 = false; m_setup.delay = 0.0; m_setup.carry = 0.0; m_setup.tick_skip_samples_delay = 0; +#ifndef ADLMIDI_DISABLE_MIDI_SEQUENCER + initSequencerInterface(); +#endif + resetMIDI(); applySetup(); - ChooseDevice("none"); realTime_ResetState(); } void MIDIplay::applySetup() { + m_synth.m_musicMode = OPL3::MODE_MIDI; + m_setup.tick_skip_samples_delay = 0; - opl.runAtPcmRate = m_setup.runAtPcmRate; + m_synth.m_runAtPcmRate = m_setup.runAtPcmRate; - if(opl.AdlBank != ~0u) - opl.dynamic_bank_setup = adlbanksetup[m_setup.AdlBank]; +#ifndef DISABLE_EMBEDDED_BANKS + if(m_synth.m_embeddedBank != OPL3::CustomBankTag) + m_synth.m_insBankSetup = adlbanksetup[m_setup.bankId]; +#endif - opl.HighTremoloMode = m_setup.HighTremoloMode < 0 ? - opl.dynamic_bank_setup.deepTremolo : - (m_setup.HighTremoloMode != 0); - opl.HighVibratoMode = m_setup.HighVibratoMode < 0 ? - opl.dynamic_bank_setup.deepVibrato : - (m_setup.HighVibratoMode != 0); - opl.AdlPercussionMode = m_setup.AdlPercussionMode < 0 ? - opl.dynamic_bank_setup.adLibPercussions : - (m_setup.AdlPercussionMode != 0); - opl.ScaleModulators = m_setup.ScaleModulators < 0 ? - opl.dynamic_bank_setup.scaleModulators : - (m_setup.ScaleModulators != 0); - if(m_setup.LogarithmicVolumes) - opl.ChangeVolumeRangesModel(ADLMIDI_VolumeModel_NativeOPL3); - opl.m_musicMode = OPL3::MODE_MIDI; - opl.ChangeVolumeRangesModel(static_cast(m_setup.VolumeModel)); - if(m_setup.VolumeModel == ADLMIDI_VolumeModel_AUTO)//Use bank default volume model - opl.m_volumeScale = (OPL3::VolumesScale)opl.dynamic_bank_setup.volumeModel; + m_synth.m_deepTremoloMode = m_setup.deepTremoloMode < 0 ? + m_synth.m_insBankSetup.deepTremolo : + (m_setup.deepTremoloMode != 0); + m_synth.m_deepVibratoMode = m_setup.deepVibratoMode < 0 ? + m_synth.m_insBankSetup.deepVibrato : + (m_setup.deepVibratoMode != 0); + m_synth.m_rhythmMode = m_setup.rhythmMode < 0 ? + m_synth.m_insBankSetup.adLibPercussions : + (m_setup.rhythmMode != 0); + m_synth.m_scaleModulators = m_setup.scaleModulators < 0 ? + m_synth.m_insBankSetup.scaleModulators : + (m_setup.scaleModulators != 0); - opl.NumCards = m_setup.NumCards; - opl.NumFourOps = m_setup.NumFourOps; - cmf_percussion_mode = false; + if(m_setup.logarithmicVolumes) + m_synth.setVolumeScaleModel(ADLMIDI_VolumeModel_NativeOPL3); + else + m_synth.setVolumeScaleModel(static_cast(m_setup.volumeScaleModel)); - opl.Reset(m_setup.emulator, m_setup.PCM_RATE, this); - ch.clear(); - ch.resize(opl.NumChannels); + if(m_setup.volumeScaleModel == ADLMIDI_VolumeModel_AUTO)//Use bank default volume model + m_synth.m_volumeScale = (OPL3::VolumesScale)m_synth.m_insBankSetup.volumeModel; + + m_synth.m_numChips = m_setup.numChips; + m_cmfPercussionMode = false; + + if(m_setup.numFourOps >= 0) + m_synth.m_numFourOps = m_setup.numFourOps; + else + adlCalculateFourOpChannels(this, true); + + m_synth.reset(m_setup.emulator, m_setup.PCM_RATE, this); + m_chipChannels.clear(); + m_chipChannels.resize(m_synth.m_numChannels); // Reset the arpeggio counter m_arpeggioCounter = 0; } -#ifndef ADLMIDI_DISABLE_MIDI_SEQUENCER -uint64_t MIDIplay::ReadVarLen(uint8_t **ptr) +void MIDIplay::partialReset() { - uint64_t result = 0; - for(;;) - { - uint8_t byte = *((*ptr)++); - result = (result << 7) + (byte & 0x7F); - if(!(byte & 0x80)) - break; - } - return result; + realTime_panic(); + m_setup.tick_skip_samples_delay = 0; + m_synth.m_runAtPcmRate = m_setup.runAtPcmRate; + m_synth.reset(m_setup.emulator, m_setup.PCM_RATE, this); + m_chipChannels.clear(); + m_chipChannels.resize((size_t)m_synth.m_numChannels); } -uint64_t MIDIplay::ReadVarLenEx(uint8_t **ptr, uint8_t *end, bool &ok) +void MIDIplay::resetMIDI() { - uint64_t result = 0; - ok = false; + m_masterVolume = MasterVolumeDefault; + m_sysExDeviceId = 0; + m_synthMode = Mode_XG; + m_arpeggioCounter = 0; - for(;;) - { - if(*ptr >= end) - return 2; - unsigned char byte = *((*ptr)++); - result = (result << 7) + (byte & 0x7F); - if(!(byte & 0x80)) - break; - } + m_midiChannels.clear(); + m_midiChannels.resize(16, MIDIchannel()); - ok = true; - return result; + caugh_missing_instruments.clear(); + caugh_missing_banks_melodic.clear(); + caugh_missing_banks_percussion.clear(); } -double MIDIplay::Tick(double s, double granularity) +void MIDIplay::TickIterators(double s) { - s *= tempoMultiplier; -#ifdef ENABLE_BEGIN_SILENCE_SKIPPING - if(CurrentPositionNew.began) + for(uint16_t c = 0; c < m_synth.m_numChannels; ++c) + m_chipChannels[c].addAge(static_cast(s * 1e6)); + updateVibrato(s); + updateArpeggio(s); +#if !defined(ADLMIDI_AUDIO_TICK_HANDLER) + updateGlide(s); #endif - CurrentPositionNew.wait -= s; - CurrentPositionNew.absTimePosition += s; - - int antiFreezeCounter = 10000;//Limit 10000 loops to avoid freezing - while((CurrentPositionNew.wait <= granularity * 0.5) && (antiFreezeCounter > 0)) - { - //std::fprintf(stderr, "wait = %g...\n", CurrentPosition.wait); - if(!ProcessEventsNew()) - break; - if(CurrentPositionNew.wait <= 0.0) - antiFreezeCounter--; - } - - if(antiFreezeCounter <= 0) - CurrentPositionNew.wait += 1.0;/* Add extra 1 second when over 10000 events - with zero delay are been detected */ - - for(uint16_t c = 0; c < opl.NumChannels; ++c) - ch[c].AddAge(static_cast(s * 1000.0)); - - UpdateVibrato(s); - UpdateArpeggio(s); - - if(CurrentPositionNew.wait < 0.0)//Avoid negative delay value! - return 0.0; - - return CurrentPositionNew.wait; } -#endif /* ADLMIDI_DISABLE_MIDI_SEQUENCER */ - -void MIDIplay::TickIteratos(double s) -{ - for(uint16_t c = 0; c < opl.NumChannels; ++c) - ch[c].AddAge(static_cast(s * 1000.0)); - UpdateVibrato(s); - UpdateArpeggio(s); -} - -#ifndef ADLMIDI_DISABLE_MIDI_SEQUENCER - -void MIDIplay::seek(double seconds) -{ - if(seconds < 0.0) - return;//Seeking negative position is forbidden! :-P - const double granularity = m_setup.mindelay, - granualityHalf = granularity * 0.5, - s = seconds;//m_setup.delay < m_setup.maxdelay ? m_setup.delay : m_setup.maxdelay; - - /* Attempt to go away out of song end must rewind position to begin */ - if(seconds > fullSongTimeLength) - { - rewind(); - return; - } - - bool loopFlagState = m_setup.loopingIsEnabled; - // Turn loop pooints off because it causes wrong position rememberin on a quick seek - m_setup.loopingIsEnabled = false; - - /* - * Seeking search is similar to regular ticking, except of next things: - * - We don't processsing arpeggio and vibrato - * - To keep correctness of the state after seek, begin every search from begin - * - All sustaining notes must be killed - * - Ignore Note-On events - */ - rewind(); - - /* - * Set "loop Start" to false to prevent overwrite of loopStart position with - * seek destinition position - * - * TODO: Detect & set loopStart position on load time to don't break loop while seeking - */ - loopStart = false; - - while((CurrentPositionNew.absTimePosition < seconds) && - (CurrentPositionNew.absTimePosition < fullSongTimeLength)) - { - CurrentPositionNew.wait -= s; - CurrentPositionNew.absTimePosition += s; - int antiFreezeCounter = 10000;//Limit 10000 loops to avoid freezing - double dstWait = CurrentPositionNew.wait + granualityHalf; - while((CurrentPositionNew.wait <= granualityHalf)/*&& (antiFreezeCounter > 0)*/) - { - //std::fprintf(stderr, "wait = %g...\n", CurrentPosition.wait); - if(!ProcessEventsNew(true)) - break; - //Avoid freeze because of no waiting increasing in more than 10000 cycles - if(CurrentPositionNew.wait <= dstWait) - antiFreezeCounter--; - else - { - dstWait = CurrentPositionNew.wait + granualityHalf; - antiFreezeCounter = 10000; - } - } - if(antiFreezeCounter <= 0) - CurrentPositionNew.wait += 1.0;/* Add extra 1 second when over 10000 events - with zero delay are been detected */ - } - - if(CurrentPositionNew.wait < 0.0) - CurrentPositionNew.wait = 0.0; - - m_setup.loopingIsEnabled = loopFlagState; - m_setup.delay = CurrentPositionNew.wait; - m_setup.carry = 0.0; -} - -double MIDIplay::tell() -{ - return CurrentPositionNew.absTimePosition; -} - -double MIDIplay::timeLength() -{ - return fullSongTimeLength; -} - -double MIDIplay::getLoopStart() -{ - return loopStartTime; -} - -double MIDIplay::getLoopEnd() -{ - return loopEndTime; -} - -void MIDIplay::rewind() -{ - Panic(); - KillSustainingNotes(-1, -1); - CurrentPositionNew = trackBeginPositionNew; - atEnd = false; - loopStart = true; - loopEnd = false; - //invalidLoop = false;//No more needed here as this flag is set on load time -} - -void MIDIplay::setTempo(double tempo) -{ - tempoMultiplier = tempo; -} -#endif /* ADLMIDI_DISABLE_MIDI_SEQUENCER */ void MIDIplay::realTime_ResetState() { - for(size_t ch = 0; ch < Ch.size(); ch++) + for(size_t ch = 0; ch < m_midiChannels.size(); ch++) { - MIDIchannel &chan = Ch[ch]; + MIDIchannel &chan = m_midiChannels[ch]; chan.resetAllControllers(); - chan.volume = (opl.m_musicMode == OPL3::MODE_RSXX) ? 127 : 100; + chan.volume = (m_synth.m_musicMode == OPL3::MODE_RSXX) ? 127 : 100; chan.vibpos = 0.0; chan.lastlrpn = 0; chan.lastmrpn = 0; chan.nrpn = false; - NoteUpdate_All(uint16_t(ch), Upd_All); - NoteUpdate_All(uint16_t(ch), Upd_Off); + if((m_synthMode & Mode_GS) != 0)// Reset custom drum channels on GS + chan.is_xg_percussion = false; + noteUpdateAll(uint16_t(ch), Upd_All); + noteUpdateAll(uint16_t(ch), Upd_Off); } + m_masterVolume = MasterVolumeDefault; } bool MIDIplay::realTime_NoteOn(uint8_t channel, uint8_t note, uint8_t velocity) @@ -971,20 +273,23 @@ bool MIDIplay::realTime_NoteOn(uint8_t channel, uint8_t note, uint8_t velocity) if(note >= 128) note = 127; - if((opl.m_musicMode == OPL3::MODE_RSXX) && (velocity != 0)) + if((m_synth.m_musicMode == OPL3::MODE_RSXX) && (velocity != 0)) { // Check if this is just a note after-touch - MIDIchannel::activenoteiterator i = Ch[channel].activenotes_find(note); + MIDIchannel::activenoteiterator i = m_midiChannels[channel].activenotes_find(note); if(i) { + const int veloffset = i->ains->midi_velocity_offset; + velocity = (uint8_t)std::min(127, std::max(1, (int)velocity + veloffset)); i->vol = velocity; - NoteUpdate(channel, i, Upd_Volume); + noteUpdate(channel, i, Upd_Volume); return false; } } - channel = channel % 16; - NoteOff(channel, note); + if(static_cast(channel) > m_midiChannels.size()) + channel = channel % 16; + noteOff(channel, note); // On Note on, Keyoff the note first, just in case keyoff // was omitted; this fixes Dance of sugar-plum fairy // by Microsoft. Now that we've done a Keyoff, @@ -993,89 +298,108 @@ bool MIDIplay::realTime_NoteOn(uint8_t channel, uint8_t note, uint8_t velocity) if(velocity == 0) return false; - MIDIchannel &midiChan = Ch[channel]; + MIDIchannel &midiChan = m_midiChannels[channel]; size_t midiins = midiChan.patch; - bool isPercussion = (channel % 16 == 9); - bool isXgPercussion = false; + bool isPercussion = (channel % 16 == 9) || midiChan.is_xg_percussion; - uint16_t bank = 0; - if(midiChan.bank_msb || midiChan.bank_lsb) - { - bank = (uint16_t(midiChan.bank_msb) * 256) + uint16_t(midiChan.bank_lsb); - //0x7E00 - XG SFX1/SFX2 channel (16128 signed decimal) - //0x7F00 - XG Percussion channel (16256 signed decimal) - if(bank == 0x7E00 || bank == 0x7F00) - { - //Let XG SFX1/SFX2 bank will have LSB==1 (128...255 range in WOPN file) - //Let XG Percussion bank will use (0...127 range in WOPN file) - bank = (uint16_t)midiins + ((bank == 0x7E00) ? 128 : 0); // MIDI instrument defines the patch - midiins = note; // Percussion instrument - isXgPercussion = true; - isPercussion = false; - } - } + size_t bank = (midiChan.bank_msb * 256) + midiChan.bank_lsb; if(isPercussion) { - bank = (uint16_t)midiins; // MIDI instrument defines the patch + // == XG bank numbers == + // 0x7E00 - XG "SFX Kits" SFX1/SFX2 channel (16128 signed decimal) + // 0x7F00 - XG "Drum Kits" Percussion channel (16256 signed decimal) + + // MIDI instrument defines the patch: + if((m_synthMode & Mode_XG) != 0) + { + // Let XG SFX1/SFX2 bank will go in 128...255 range of LSB in WOPN file) + // Let XG Percussion bank will use (0...127 LSB range in WOPN file) + + // Choose: SFX or Drum Kits + bank = midiins + ((bank == 0x7E00) ? 128 : 0); + } + else + { + bank = midiins; + } midiins = note; // Percussion instrument } - if(isPercussion || isXgPercussion) + + if(isPercussion) bank += OPL3::PercussionTag; - const adlinsdata2 *ains = &OPL3::emptyInstrument; + const adlinsdata2 *ains = &OPL3::m_emptyInstrument; //Set bank bank const OPL3::Bank *bnk = NULL; - if((bank & ~(uint16_t)OPL3::PercussionTag) > 0) + bool caughtMissingBank = false; + if((bank & ~static_cast(OPL3::PercussionTag)) > 0) { - OPL3::BankMap::iterator b = opl.dynamic_banks.find(bank); - if(b != opl.dynamic_banks.end()) + OPL3::BankMap::iterator b = m_synth.m_insBanks.find(bank); + if(b != m_synth.m_insBanks.end()) bnk = &b->second; - if(bnk) ains = &bnk->ins[midiins]; - else if(hooks.onDebugMessage) + else + caughtMissingBank = true; + } + + //Or fall back to bank ignoring LSB (GS) + if((ains->flags & adlinsdata::Flag_NoSound) && ((m_synthMode & Mode_GS) != 0)) + { + size_t fallback = bank & ~(size_t)0x7F; + if(fallback != bank) { - std::set &missing = (isPercussion || isXgPercussion) ? - caugh_missing_banks_percussion : caugh_missing_banks_melodic; - const char *text = (isPercussion || isXgPercussion) ? - "percussion" : "melodic"; - if(missing.insert(bank).second) - hooks.onDebugMessage(hooks.onDebugMessage_userData, "[%i] Playing missing %s MIDI bank %i (patch %i)", channel, text, bank, midiins); + OPL3::BankMap::iterator b = m_synth.m_insBanks.find(fallback); + caughtMissingBank = false; + if(b != m_synth.m_insBanks.end()) + bnk = &b->second; + if(bnk) + ains = &bnk->ins[midiins]; + else + caughtMissingBank = true; } } - //Or fall back to first bank - if(ains->flags & adlinsdata::Flag_NoSound) - { - OPL3::BankMap::iterator b = opl.dynamic_banks.find(bank & OPL3::PercussionTag); - if(b != opl.dynamic_banks.end()) - bnk = &b->second; + if(caughtMissingBank && hooks.onDebugMessage) + { + std::set &missing = (isPercussion) ? + caugh_missing_banks_percussion : caugh_missing_banks_melodic; + const char *text = (isPercussion) ? + "percussion" : "melodic"; + if(missing.insert(bank).second) + { + hooks.onDebugMessage(hooks.onDebugMessage_userData, + "[%i] Playing missing %s MIDI bank %i (patch %i)", + channel, text, (bank & ~static_cast(OPL3::PercussionTag)), midiins); + } + } + + //Or fall back to first bank + if((ains->flags & adlinsdata::Flag_NoSound) != 0) + { + OPL3::BankMap::iterator b = m_synth.m_insBanks.find(bank & OPL3::PercussionTag); + if(b != m_synth.m_insBanks.end()) + bnk = &b->second; if(bnk) ains = &bnk->ins[midiins]; } - /* - if(MidCh%16 == 9 || (midiins != 32 && midiins != 46 && midiins != 48 && midiins != 50)) - break; // HACK - if(midiins == 46) vol = (vol*7)/10; // HACK - if(midiins == 48 || midiins == 50) vol /= 4; // HACK - */ - //if(midiins == 56) vol = vol*6/10; // HACK - //int meta = banks[opl.AdlBank][midiins]; + const int veloffset = ains->midi_velocity_offset; + velocity = (uint8_t)std::min(127, std::max(1, (int)velocity + veloffset)); - int16_t tone = note; - - if(!isPercussion && !isXgPercussion && (bank > 0)) // For non-zero banks + int32_t tone = note; + if(!isPercussion && (bank > 0)) // For non-zero banks { if(ains->flags & adlinsdata::Flag_NoSound) { - if(hooks.onDebugMessage) + if(hooks.onDebugMessage && caugh_missing_instruments.insert(static_cast(midiins)).second) { - if(caugh_missing_instruments.insert(static_cast(midiins)).second) - hooks.onDebugMessage(hooks.onDebugMessage_userData, "[%i] Caugh a blank instrument %i (offset %i) in the MIDI bank %u", channel, Ch[channel].patch, midiins, bank); + hooks.onDebugMessage(hooks.onDebugMessage_userData, + "[%i] Caught a blank instrument %i (offset %i) in the MIDI bank %u", + channel, m_midiChannels[channel].patch, midiins, bank); } bank = 0; midiins = midiChan.patch; @@ -1094,12 +418,13 @@ bool MIDIplay::realTime_NoteOn(uint8_t channel, uint8_t note, uint8_t velocity) } //uint16_t i[2] = { ains->adlno1, ains->adlno2 }; + bool is_2op = !(ains->flags & (adlinsdata::Flag_Pseudo4op|adlinsdata::Flag_Real4op)); bool pseudo_4op = ains->flags & adlinsdata::Flag_Pseudo4op; #ifndef __WATCOMC__ MIDIchannel::NoteInfo::Phys voices[MIDIchannel::NoteInfo::MaxNumPhysChans] = { {0, ains->adl[0], false}, - {0, ains->adl[1], pseudo_4op} + {0, (!is_2op) ? ains->adl[1] : ains->adl[0], pseudo_4op} }; #else /* Unfortunately, WatCom can't brace-initialize structure that incluses structure fields */ MIDIchannel::NoteInfo::Phys voices[MIDIchannel::NoteInfo::MaxNumPhysChans]; @@ -1107,20 +432,34 @@ bool MIDIplay::realTime_NoteOn(uint8_t channel, uint8_t note, uint8_t velocity) voices[0].ains = ains->adl[0]; voices[0].pseudo4op = false; voices[1].chip_chan = 0; - voices[1].ains = ains->adl[1]; + voices[1].ains = (!is_2op) ? ains->adl[1] : ains->adl[0]; voices[1].pseudo4op = pseudo_4op; #endif /* __WATCOMC__ */ - if((opl.AdlPercussionMode == 1) && PercussionMap[midiins & 0xFF]) + if((m_synth.m_rhythmMode == 1) && PercussionMap[midiins & 0xFF]) voices[1] = voices[0];//i[1] = i[0]; + bool isBlankNote = (ains->flags & adlinsdata::Flag_NoSound) != 0; + if(hooks.onDebugMessage) { - if((ains->flags & adlinsdata::Flag_NoSound) && - caugh_missing_instruments.insert(static_cast(midiins)).second) + if(isBlankNote && caugh_missing_instruments.insert(static_cast(midiins)).second) hooks.onDebugMessage(hooks.onDebugMessage_userData, "[%i] Playing missing instrument %i", channel, midiins); } + if(isBlankNote) + { + // Don't even try to play the blank instrument! But, insert the dummy note. + std::pair + dummy = midiChan.activenotes_insert(note); + dummy.first->isBlank = true; + dummy.first->ains = NULL; + dummy.first->chip_channels_count = 0; + // Record the last note on MIDI channel as source of portamento + midiChan.portamentoSource = static_cast(note); + return false; + } + // Allocate AdLib channel (the physical sound channel for the note) int32_t adlchannel[MIDIchannel::NoteInfo::MaxNumPhysChans] = { -1, -1 }; @@ -1137,25 +476,25 @@ bool MIDIplay::realTime_NoteOn(uint8_t channel, uint8_t note, uint8_t velocity) int32_t c = -1; int32_t bs = -0x7FFFFFFFl; - for(size_t a = 0; a < (size_t)opl.NumChannels; ++a) + for(size_t a = 0; a < (size_t)m_synth.m_numChannels; ++a) { if(ccount == 1 && static_cast(a) == adlchannel[0]) continue; // ^ Don't use the same channel for primary&secondary - if(voices[0].ains == voices[1].ains || pseudo_4op/*i[0] == i[1] || pseudo_4op*/) + if(is_2op || pseudo_4op) { // Only use regular channels - uint8_t expected_mode = 0; + uint32_t expected_mode = 0; - if(opl.AdlPercussionMode == 1) + if(m_synth.m_rhythmMode) { - if(cmf_percussion_mode) + if(m_cmfPercussionMode) expected_mode = channel < 11 ? 0 : (3 + channel - 11); // CMF else expected_mode = PercussionMap[midiins & 0xFF]; } - if(opl.four_op_category[a] != expected_mode) + if(m_synth.m_channelCategory[a] != expected_mode) continue; } else @@ -1163,7 +502,7 @@ bool MIDIplay::realTime_NoteOn(uint8_t channel, uint8_t note, uint8_t velocity) if(ccount == 0) { // Only use four-op master channels - if(opl.four_op_category[a] != 1) + if(m_synth.m_channelCategory[a] != OPL3::ChanCat_4op_Master) continue; } else @@ -1174,7 +513,7 @@ bool MIDIplay::realTime_NoteOn(uint8_t channel, uint8_t note, uint8_t velocity) } } - int64_t s = CalculateAdlChannelGoodness(a, voices[ccount], channel); + int64_t s = calculateChipChannelGoodness(a, voices[ccount]); if(s > bs) { bs = (int32_t)s; // Best candidate wins @@ -1191,7 +530,7 @@ bool MIDIplay::realTime_NoteOn(uint8_t channel, uint8_t note, uint8_t velocity) continue; // Could not play this note. Ignore it. } - PrepareAdlChannelForNewNote(static_cast(c), voices[ccount]); + prepareChipChannelForNewNote(static_cast(c), voices[ccount]); adlchannel[ccount] = c; } @@ -1204,16 +543,39 @@ bool MIDIplay::realTime_NoteOn(uint8_t channel, uint8_t note, uint8_t velocity) //if(hooks.onDebugMessage) // hooks.onDebugMessage(hooks.onDebugMessage_userData, "i1=%d:%d, i2=%d:%d", i[0],adlchannel[0], i[1],adlchannel[1]); + if(midiChan.softPedal) // Apply Soft Pedal level reducing + velocity = static_cast(std::floor(static_cast(velocity) * 0.8f)); + // Allocate active note for MIDI channel std::pair ir = midiChan.activenotes_insert(note); ir.first->vol = velocity; ir.first->vibrato = midiChan.noteAftertouch[note]; - ir.first->tone = tone; + ir.first->noteTone = static_cast(tone); + ir.first->currentTone = tone; + ir.first->glideRate = HUGE_VAL; ir.first->midiins = midiins; + ir.first->isPercussion = isPercussion; + ir.first->isBlank = isBlankNote; ir.first->ains = ains; ir.first->chip_channels_count = 0; + int8_t currentPortamentoSource = midiChan.portamentoSource; + double currentPortamentoRate = midiChan.portamentoRate; + bool portamentoEnable = + midiChan.portamentoEnable && currentPortamentoRate != HUGE_VAL && !isPercussion; + // Record the last note on MIDI channel as source of portamento + midiChan.portamentoSource = static_cast(note); + // midiChan.portamentoSource = portamentoEnable ? (int8_t)note : (int8_t)-1; + + // Enable gliding on portamento note + if (portamentoEnable && currentPortamentoSource >= 0) + { + ir.first->currentTone = currentPortamentoSource; + ir.first->glideRate = currentPortamentoRate; + ++midiChan.gliding_note_count; + } + for(unsigned ccount = 0; ccount < MIDIchannel::NoteInfo::MaxNumPhysChans; ++ccount) { int32_t c = adlchannel[ccount]; @@ -1222,21 +584,34 @@ bool MIDIplay::realTime_NoteOn(uint8_t channel, uint8_t note, uint8_t velocity) uint16_t chipChan = static_cast(adlchannel[ccount]); ir.first->phys_ensure_find_or_create(chipChan)->assign(voices[ccount]); } - NoteUpdate(channel, ir.first, Upd_All | Upd_Patch); + + noteUpdate(channel, ir.first, Upd_All | Upd_Patch); + + for(unsigned ccount = 0; ccount < MIDIchannel::NoteInfo::MaxNumPhysChans; ++ccount) + { + int32_t c = adlchannel[ccount]; + if(c < 0) + continue; + m_chipChannels[c].recent_ins = voices[ccount]; + m_chipChannels[c].addAge(0); + } + return true; } void MIDIplay::realTime_NoteOff(uint8_t channel, uint8_t note) { - channel = channel % 16; - NoteOff(channel, note); + if(static_cast(channel) > m_midiChannels.size()) + channel = channel % 16; + noteOff(channel, note); } void MIDIplay::realTime_NoteAfterTouch(uint8_t channel, uint8_t note, uint8_t atVal) { - channel = channel % 16; - MIDIchannel &chan = Ch[channel]; - MIDIchannel::activenoteiterator i = Ch[channel].activenotes_find(note); + if(static_cast(channel) > m_midiChannels.size()) + channel = channel % 16; + MIDIchannel &chan = m_midiChannels[channel]; + MIDIchannel::activenoteiterator i = m_midiChannels[channel].activenotes_find(note); if(i) { i->vibrato = atVal; @@ -1255,87 +630,100 @@ void MIDIplay::realTime_NoteAfterTouch(uint8_t channel, uint8_t note, uint8_t at void MIDIplay::realTime_ChannelAfterTouch(uint8_t channel, uint8_t atVal) { - channel = channel % 16; - Ch[channel].aftertouch = atVal; + if(static_cast(channel) > m_midiChannels.size()) + channel = channel % 16; + m_midiChannels[channel].aftertouch = atVal; } void MIDIplay::realTime_Controller(uint8_t channel, uint8_t type, uint8_t value) { - channel = channel % 16; + if(static_cast(channel) > m_midiChannels.size()) + channel = channel % 16; switch(type) { case 1: // Adjust vibrato //UI.PrintLn("%u:vibrato %d", MidCh,value); - Ch[channel].vibrato = value; + m_midiChannels[channel].vibrato = value; break; case 0: // Set bank msb (GM bank) - Ch[channel].bank_msb = value; - Ch[channel].is_xg_percussion = isXgPercChannel(Ch[channel].bank_msb, Ch[channel].bank_lsb); + m_midiChannels[channel].bank_msb = value; + if((m_synthMode & Mode_GS) == 0)// Don't use XG drums on GS synth mode + m_midiChannels[channel].is_xg_percussion = isXgPercChannel(m_midiChannels[channel].bank_msb, m_midiChannels[channel].bank_lsb); break; case 32: // Set bank lsb (XG bank) - Ch[channel].bank_lsb = value; - Ch[channel].is_xg_percussion = isXgPercChannel(Ch[channel].bank_msb, Ch[channel].bank_lsb); + m_midiChannels[channel].bank_lsb = value; + if((m_synthMode & Mode_GS) == 0)// Don't use XG drums on GS synth mode + m_midiChannels[channel].is_xg_percussion = isXgPercChannel(m_midiChannels[channel].bank_msb, m_midiChannels[channel].bank_lsb); break; case 5: // Set portamento msb - Ch[channel].portamento = static_cast((Ch[channel].portamento & 0x7F) | (value << 7)); - //UpdatePortamento(MidCh); + m_midiChannels[channel].portamento = static_cast((m_midiChannels[channel].portamento & 0x007F) | (value << 7)); + updatePortamento(channel); break; case 37: // Set portamento lsb - Ch[channel].portamento = (Ch[channel].portamento & 0x3F80) | (value); - //UpdatePortamento(MidCh); + m_midiChannels[channel].portamento = static_cast((m_midiChannels[channel].portamento & 0x3F80) | (value)); + updatePortamento(channel); break; case 65: // Enable/disable portamento - // value >= 64 ? enabled : disabled - //UpdatePortamento(MidCh); + m_midiChannels[channel].portamentoEnable = value >= 64; + updatePortamento(channel); break; case 7: // Change volume - Ch[channel].volume = value; - NoteUpdate_All(channel, Upd_Volume); + m_midiChannels[channel].volume = value; + noteUpdateAll(channel, Upd_Volume); break; case 74: // Change brightness - Ch[channel].brightness = value; - NoteUpdate_All(channel, Upd_Volume); + m_midiChannels[channel].brightness = value; + noteUpdateAll(channel, Upd_Volume); break; case 64: // Enable/disable sustain - Ch[channel].sustain = value; - if(!value) KillSustainingNotes(channel); + m_midiChannels[channel].sustain = (value >= 64); + if(!m_midiChannels[channel].sustain) + killSustainingNotes(channel, -1, AdlChannel::LocationData::Sustain_Pedal); + break; + + case 66: // Enable/disable sostenuto + if(value >= 64) //Find notes and mark them as sostenutoed + markSostenutoNotes(channel); + else + killSustainingNotes(channel, -1, AdlChannel::LocationData::Sustain_Sostenuto); + break; + + case 67: // Enable/disable soft-pedal + m_midiChannels[channel].softPedal = (value >= 64); break; case 11: // Change expression (another volume factor) - Ch[channel].expression = value; - NoteUpdate_All(channel, Upd_Volume); + m_midiChannels[channel].expression = value; + noteUpdateAll(channel, Upd_Volume); break; case 10: // Change panning - Ch[channel].panning = 0x00; - if(value < 64 + 32) Ch[channel].panning |= OPL_PANNING_LEFT; - if(value >= 64 - 32) Ch[channel].panning |= OPL_PANNING_RIGHT; + m_midiChannels[channel].panning = value; - NoteUpdate_All(channel, Upd_Pan); + noteUpdateAll(channel, Upd_Pan); break; case 121: // Reset all controllers - Ch[channel].resetAllControllers(); - //UpdatePortamento(MidCh); - NoteUpdate_All(channel, Upd_Pan + Upd_Volume + Upd_Pitch); + m_midiChannels[channel].resetAllControllers(); + noteUpdateAll(channel, Upd_Pan + Upd_Volume + Upd_Pitch); // Kill all sustained notes - KillSustainingNotes(channel); + killSustainingNotes(channel, -1, AdlChannel::LocationData::Sustain_ANY); break; case 120: // All sounds off - NoteUpdate_All(channel, Upt_OffMute); + noteUpdateAll(channel, Upd_OffMute); break; case 123: // All notes off - NoteUpdate_All(channel, Upd_Off); + noteUpdateAll(channel, Upd_Off); break; case 91: @@ -1354,38 +742,39 @@ void MIDIplay::realTime_Controller(uint8_t channel, uint8_t type, uint8_t value) break; // Phaser effect depth. We don't do. case 98: - Ch[channel].lastlrpn = value; - Ch[channel].nrpn = true; + m_midiChannels[channel].lastlrpn = value; + m_midiChannels[channel].nrpn = true; break; case 99: - Ch[channel].lastmrpn = value; - Ch[channel].nrpn = true; + m_midiChannels[channel].lastmrpn = value; + m_midiChannels[channel].nrpn = true; break; case 100: - Ch[channel].lastlrpn = value; - Ch[channel].nrpn = false; + m_midiChannels[channel].lastlrpn = value; + m_midiChannels[channel].nrpn = false; break; case 101: - Ch[channel].lastmrpn = value; - Ch[channel].nrpn = false; + m_midiChannels[channel].lastmrpn = value; + m_midiChannels[channel].nrpn = false; break; case 113: break; // Related to pitch-bender, used by missimp.mid in Duke3D case 6: - SetRPN(channel, value, true); + setRPN(channel, value, true); break; case 38: - SetRPN(channel, value, false); + setRPN(channel, value, false); break; case 103: - cmf_percussion_mode = (value != 0); + if(m_synth.m_musicMode == OPL3::MODE_CMF) + m_cmfPercussionMode = (value != 0); break; // CMF (ctrl 0x67) rhythm mode default: @@ -1396,71 +785,338 @@ void MIDIplay::realTime_Controller(uint8_t channel, uint8_t type, uint8_t value) void MIDIplay::realTime_PatchChange(uint8_t channel, uint8_t patch) { - channel = channel % 16; - Ch[channel].patch = patch; + if(static_cast(channel) > m_midiChannels.size()) + channel = channel % 16; + m_midiChannels[channel].patch = patch; } void MIDIplay::realTime_PitchBend(uint8_t channel, uint16_t pitch) { - channel = channel % 16; - Ch[channel].bend = int(pitch) - 8192; - NoteUpdate_All(channel, Upd_Pitch); + if(static_cast(channel) > m_midiChannels.size()) + channel = channel % 16; + m_midiChannels[channel].bend = int(pitch) - 8192; + noteUpdateAll(channel, Upd_Pitch); } void MIDIplay::realTime_PitchBend(uint8_t channel, uint8_t msb, uint8_t lsb) { - channel = channel % 16; - Ch[channel].bend = int(lsb) + int(msb) * 128 - 8192; - NoteUpdate_All(channel, Upd_Pitch); + if(static_cast(channel) > m_midiChannels.size()) + channel = channel % 16; + m_midiChannels[channel].bend = int(lsb) + int(msb) * 128 - 8192; + noteUpdateAll(channel, Upd_Pitch); } void MIDIplay::realTime_BankChangeLSB(uint8_t channel, uint8_t lsb) { - channel = channel % 16; - Ch[channel].bank_lsb = lsb; + if(static_cast(channel) > m_midiChannels.size()) + channel = channel % 16; + m_midiChannels[channel].bank_lsb = lsb; } void MIDIplay::realTime_BankChangeMSB(uint8_t channel, uint8_t msb) { - channel = channel % 16; - Ch[channel].bank_msb = msb; + if(static_cast(channel) > m_midiChannels.size()) + channel = channel % 16; + m_midiChannels[channel].bank_msb = msb; } void MIDIplay::realTime_BankChange(uint8_t channel, uint16_t bank) { - channel = channel % 16; - Ch[channel].bank_lsb = uint8_t(bank & 0xFF); - Ch[channel].bank_msb = uint8_t((bank >> 8) & 0xFF); + if(static_cast(channel) > m_midiChannels.size()) + channel = channel % 16; + m_midiChannels[channel].bank_lsb = uint8_t(bank & 0xFF); + m_midiChannels[channel].bank_msb = uint8_t((bank >> 8) & 0xFF); +} + +void MIDIplay::setDeviceId(uint8_t id) +{ + m_sysExDeviceId = id; +} + +bool MIDIplay::realTime_SysEx(const uint8_t *msg, size_t size) +{ + if(size < 4 || msg[0] != 0xF0 || msg[size - 1] != 0xF7) + return false; + + unsigned manufacturer = msg[1]; + unsigned dev = msg[2]; + msg += 3; + size -= 4; + + switch(manufacturer) + { + default: + break; + case Manufacturer_UniversalNonRealtime: + case Manufacturer_UniversalRealtime: + return doUniversalSysEx( + dev, manufacturer == Manufacturer_UniversalRealtime, msg, size); + case Manufacturer_Roland: + return doRolandSysEx(dev, msg, size); + case Manufacturer_Yamaha: + return doYamahaSysEx(dev, msg, size); + } + + return false; +} + +bool MIDIplay::doUniversalSysEx(unsigned dev, bool realtime, const uint8_t *data, size_t size) +{ + bool devicematch = dev == 0x7F || dev == m_sysExDeviceId; + if(size < 2 || !devicematch) + return false; + + unsigned address = + (((unsigned)data[0] & 0x7F) << 8) | + (((unsigned)data[1] & 0x7F)); + data += 2; + size -= 2; + + switch(((unsigned)realtime << 16) | address) + { + case (0 << 16) | 0x0901: // GM System On + if(hooks.onDebugMessage) + hooks.onDebugMessage(hooks.onDebugMessage_userData, "SysEx: GM System On"); + m_synthMode = Mode_GM; + realTime_ResetState(); + return true; + case (0 << 16) | 0x0902: // GM System Off + if(hooks.onDebugMessage) + hooks.onDebugMessage(hooks.onDebugMessage_userData, "SysEx: GM System Off"); + m_synthMode = Mode_XG;//TODO: TEMPORARY, make something RIGHT + realTime_ResetState(); + return true; + case (1 << 16) | 0x0401: // MIDI Master Volume + if(size != 2) + break; + unsigned volume = + (((unsigned)data[0] & 0x7F)) | + (((unsigned)data[1] & 0x7F) << 7); + m_masterVolume = static_cast(volume >> 7); + for(size_t ch = 0; ch < m_midiChannels.size(); ch++) + noteUpdateAll(uint16_t(ch), Upd_Volume); + return true; + } + + return false; +} + +bool MIDIplay::doRolandSysEx(unsigned dev, const uint8_t *data, size_t size) +{ + bool devicematch = dev == 0x7F || (dev & 0x0F) == m_sysExDeviceId; + if(size < 6 || !devicematch) + return false; + + unsigned model = data[0] & 0x7F; + unsigned mode = data[1] & 0x7F; + unsigned checksum = data[size - 1] & 0x7F; + data += 2; + size -= 3; + +#if !defined(ADLMIDI_SKIP_ROLAND_CHECKSUM) + { + unsigned checkvalue = 0; + for(size_t i = 0; i < size; ++i) + checkvalue += data[i] & 0x7F; + checkvalue = (128 - (checkvalue & 127)) & 127; + if(checkvalue != checksum) + { + if(hooks.onDebugMessage) + hooks.onDebugMessage(hooks.onDebugMessage_userData, "SysEx: Caught invalid roland SysEx message!"); + return false; + } + } +#endif + + unsigned address = + (((unsigned)data[0] & 0x7F) << 16) | + (((unsigned)data[1] & 0x7F) << 8) | + (((unsigned)data[2] & 0x7F)); + unsigned target_channel = 0; + + /* F0 41 10 42 12 40 00 7F 00 41 F7 */ + + if((address & 0xFFF0FF) == 0x401015) // Turn channel 1 into percussion + { + address = 0x401015; + target_channel = data[1] & 0x0F; + } + + data += 3; + size -= 3; + + if(mode != RolandMode_Send) // don't have MIDI-Out reply ability + return false; + + // Mode Set + // F0 {41 10 42 12} {40 00 7F} {00 41} F7 + + // Custom drum channels + // F0 {41 10 42 12} {40 1 15} { } F7 + + switch((model << 24) | address) + { + case (RolandModel_GS << 24) | 0x00007F: // System Mode Set + { + if(size != 1 || (dev & 0xF0) != 0x10) + break; + unsigned mode = data[0] & 0x7F; + ADL_UNUSED(mode);//TODO: Hook this correctly! + if(hooks.onDebugMessage) + hooks.onDebugMessage(hooks.onDebugMessage_userData, "SysEx: Caught Roland System Mode Set: %02X", mode); + m_synthMode = Mode_GS; + realTime_ResetState(); + return true; + } + case (RolandModel_GS << 24) | 0x40007F: // Mode Set + { + if(size != 1 || (dev & 0xF0) != 0x10) + break; + unsigned value = data[0] & 0x7F; + ADL_UNUSED(value);//TODO: Hook this correctly! + if(hooks.onDebugMessage) + hooks.onDebugMessage(hooks.onDebugMessage_userData, "SysEx: Caught Roland Mode Set: %02X", value); + m_synthMode = Mode_GS; + realTime_ResetState(); + return true; + } + case (RolandModel_GS << 24) | 0x401015: // Percussion channel + { + if(size != 1 || (dev & 0xF0) != 0x10) + break; + if(m_midiChannels.size() < 16) + break; + unsigned value = data[0] & 0x7F; + const uint8_t channels_map[16] = + { + 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 10, 11, 12, 13, 14, 15 + }; + if(hooks.onDebugMessage) + hooks.onDebugMessage(hooks.onDebugMessage_userData, + "SysEx: Caught Roland Percussion set: %02X on channel %u (from %X)", + value, channels_map[target_channel], target_channel); + m_midiChannels[channels_map[target_channel]].is_xg_percussion = ((value == 0x01)) || ((value == 0x02)); + return true; + } + } + + return false; +} + +bool MIDIplay::doYamahaSysEx(unsigned dev, const uint8_t *data, size_t size) +{ + bool devicematch = dev == 0x7F || (dev & 0x0F) == m_sysExDeviceId; + if(size < 1 || !devicematch) + return false; + + unsigned model = data[0] & 0x7F; + ++data; + --size; + + switch((model << 8) | (dev & 0xF0)) + { + case (YamahaModel_XG << 8) | 0x10: // parameter change + { + if(size < 3) + break; + + unsigned address = + (((unsigned)data[0] & 0x7F) << 16) | + (((unsigned)data[1] & 0x7F) << 8) | + (((unsigned)data[2] & 0x7F)); + data += 3; + size -= 3; + + switch(address) + { + case 0x00007E: // XG System On + if(size != 1) + break; + unsigned value = data[0] & 0x7F; + ADL_UNUSED(value);//TODO: Hook this correctly! + if(hooks.onDebugMessage) + hooks.onDebugMessage(hooks.onDebugMessage_userData, "SysEx: Caught Yamaha XG System On: %02X", value); + m_synthMode = Mode_XG; + realTime_ResetState(); + return true; + } + + break; + } + } + + return false; } void MIDIplay::realTime_panic() { - Panic(); - KillSustainingNotes(-1, -1); + panic(); + killSustainingNotes(-1, -1, AdlChannel::LocationData::Sustain_ANY); } -void MIDIplay::AudioTick(uint32_t chipId, uint32_t /*rate*/) +void MIDIplay::realTime_deviceSwitch(size_t track, const char *data, size_t length) +{ + const std::string indata(data, length); + m_currentMidiDevice[track] = chooseDevice(indata); +} + +size_t MIDIplay::realTime_currentDevice(size_t track) +{ + if(m_currentMidiDevice.empty()) + return 0; + return m_currentMidiDevice[track]; +} + +void MIDIplay::realTime_rawOPL(uint8_t reg, uint8_t value) +{ + if((reg & 0xF0) == 0xC0) + value |= 0x30; + //std::printf("OPL poke %02X, %02X\n", reg, value); + //std::fflush(stdout); + m_synth.writeReg(0, reg, value); +} + +#if defined(ADLMIDI_AUDIO_TICK_HANDLER) +void MIDIplay::AudioTick(uint32_t chipId, uint32_t rate) { if(chipId != 0) // do first chip ticks only return; - /*uint32_t tickNumber = */m_audioTickCounter++; -} + uint32_t tickNumber = m_audioTickCounter++; + double timeDelta = 1.0 / rate; -void MIDIplay::NoteUpdate(uint16_t MidCh, + enum { portamentoInterval = 32 }; // for efficiency, set rate limit on pitch updates + + if(tickNumber % portamentoInterval == 0) + { + double portamentoDelta = timeDelta * portamentoInterval; + updateGlide(portamentoDelta); + } +} +#endif + +void MIDIplay::noteUpdate(size_t midCh, MIDIplay::MIDIchannel::activenoteiterator i, unsigned props_mask, int32_t select_adlchn) { MIDIchannel::NoteInfo &info = *i; - const int16_t tone = info.tone; + const int16_t noteTone = info.noteTone; + const double currentTone = info.currentTone; const uint8_t vol = info.vol; const int midiins = static_cast(info.midiins); const adlinsdata2 &ains = *info.ains; AdlChannel::Location my_loc; - my_loc.MidCh = MidCh; + my_loc.MidCh = static_cast(midCh); my_loc.note = info.note; + if(info.isBlank) + { + if(props_mask & Upd_Off) + m_midiChannels[midCh].activenotes_erase(i); + return; + } + for(unsigned ccount = 0, ctotal = info.chip_channels_count; ccount < ctotal; ccount++) { const MIDIchannel::NoteInfo::Phys &ins = info.chip_channels[ccount]; @@ -1470,14 +1126,14 @@ void MIDIplay::NoteUpdate(uint16_t MidCh, if(props_mask & Upd_Patch) { - opl.Patch(c, ins.ains); - AdlChannel::LocationData *d = ch[c].users_find_or_create(my_loc); + m_synth.setPatch(c, ins.ains); + AdlChannel::LocationData *d = m_chipChannels[c].users_find_or_create(my_loc); if(d) // inserts if necessary { - d->sustained = false; - d->vibdelay = 0; + d->sustained = AdlChannel::LocationData::Sustain_None; + d->vibdelay_us = 0; d->fixed_sustain = (ains.ms_sound_kon == static_cast(adlNoteOnMaxTime)); - d->kon_time_until_neglible = ains.ms_sound_kon; + d->kon_time_until_neglible_us = 1000 * ains.ms_sound_kon; d->ins = ins; } } @@ -1486,34 +1142,35 @@ void MIDIplay::NoteUpdate(uint16_t MidCh, for(unsigned ccount = 0; ccount < info.chip_channels_count; ccount++) { const MIDIchannel::NoteInfo::Phys &ins = info.chip_channels[ccount]; - uint16_t c = ins.chip_chan; + uint16_t c = ins.chip_chan; + uint16_t c_slave = info.chip_channels[1].chip_chan; if(select_adlchn >= 0 && c != select_adlchn) continue; if(props_mask & Upd_Off) // note off { - if(Ch[MidCh].sustain == 0) + if(!m_midiChannels[midCh].sustain) { - AdlChannel::LocationData *k = ch[c].users_find(my_loc); - - if(k) - ch[c].users_erase(k); + AdlChannel::LocationData *k = m_chipChannels[c].users_find(my_loc); + bool do_erase_user = (k && ((k->sustained & AdlChannel::LocationData::Sustain_Sostenuto) == 0)); + if(do_erase_user) + m_chipChannels[c].users_erase(k); if(hooks.onNote) - hooks.onNote(hooks.onNote_userData, c, tone, midiins, 0, 0.0); + hooks.onNote(hooks.onNote_userData, c, noteTone, midiins, 0, 0.0); - if(ch[c].users_empty()) + if(do_erase_user && m_chipChannels[c].users_empty()) { - opl.NoteOff(c); + m_synth.noteOff(c); if(props_mask & Upd_Mute) // Mute the note { - opl.Touch_Real(c, 0); - ch[c].koff_time_until_neglible = 0; + m_synth.touchNote(c, 0); + m_chipChannels[c].koff_time_until_neglible_us = 0; } else { - ch[c].koff_time_until_neglible = ains.ms_sound_koff; + m_chipChannels[c].koff_time_until_neglible_us = 1000 * int64_t(ains.ms_sound_koff); } } } @@ -1521,11 +1178,11 @@ void MIDIplay::NoteUpdate(uint16_t MidCh, { // Sustain: Forget about the note, but don't key it off. // Also will avoid overwriting it very soon. - AdlChannel::LocationData *d = ch[c].users_find_or_create(my_loc); + AdlChannel::LocationData *d = m_chipChannels[c].users_find_or_create(my_loc); if(d) - d->sustained = true; // note: not erased! + d->sustained |= AdlChannel::LocationData::Sustain_Pedal; // note: not erased! if(hooks.onNote) - hooks.onNote(hooks.onNote_userData, c, tone, midiins, -1, 0.0); + hooks.onNote(hooks.onNote_userData, c, noteTone, midiins, -1, 0.0); } info.phys_erase_at(&ins); // decrements channel count @@ -1534,13 +1191,13 @@ void MIDIplay::NoteUpdate(uint16_t MidCh, } if(props_mask & Upd_Pan) - opl.Pan(c, Ch[MidCh].panning); + m_synth.setPan(c, m_midiChannels[midCh].panning); if(props_mask & Upd_Volume) { - uint32_t volume; - bool is_percussion = (MidCh == 9) || Ch[MidCh].is_xg_percussion; - uint8_t brightness = is_percussion ? 127 : Ch[MidCh].brightness; + uint_fast32_t volume; + bool is_percussion = (midCh == 9) || m_midiChannels[midCh].is_xg_percussion; + uint_fast32_t brightness = is_percussion ? 127 : m_midiChannels[midCh].brightness; if(!m_setup.fullRangeBrightnessCC74) { @@ -1551,12 +1208,12 @@ void MIDIplay::NoteUpdate(uint16_t MidCh, brightness *= 2; } - switch(opl.m_volumeScale) + switch(m_synth.m_volumeScale) { - + default: case OPL3::VOLUME_Generic: { - volume = vol * Ch[MidCh].volume * Ch[MidCh].expression; + volume = vol * m_masterVolume * m_midiChannels[midCh].volume * m_midiChannels[midCh].expression; /* If the channel has arpeggio, the effective volume of * *this* instrument is actually lower due to timesharing. @@ -1567,77 +1224,73 @@ void MIDIplay::NoteUpdate(uint16_t MidCh, */ //volume = (int)(volume * std::sqrt( (double) ch[c].users.size() )); - // The formula below: SOLVE(V=127^3 * 2^( (A-63.49999) / 8), A) - volume = volume > 8725 ? static_cast(std::log(static_cast(volume)) * 11.541561 + (0.5 - 104.22845)) : 0; - // The incorrect formula below: SOLVE(V=127^3 * (2^(A/63)-1), A) - //opl.Touch_Real(c, volume>11210 ? 91.61112 * std::log(4.8819E-7*volume + 1.0)+0.5 : 0); - - opl.Touch_Real(c, volume, brightness); - //opl.Touch(c, volume); + // The formula below: SOLVE(V=127^4 * 2^( (A-63.49999) / 8), A) + volume = volume > (8725 * 127) ? static_cast(std::log(static_cast(volume)) * 11.541560327111707 - 1.601379199767093e+02) : 0; + // The incorrect formula below: SOLVE(V=127^4 * (2^(A/63)-1), A) + //opl.Touch_Real(c, volume>(11210*127) ? 91.61112 * std::log((4.8819E-7/127)*volume + 1.0)+0.5 : 0); } break; case OPL3::VOLUME_NATIVE: { - volume = vol * Ch[MidCh].volume * Ch[MidCh].expression; - volume = volume * 127 / (127 * 127 * 127) / 2; - opl.Touch_Real(c, volume, brightness); + volume = vol * m_midiChannels[midCh].volume * m_midiChannels[midCh].expression; + // volume = volume * m_masterVolume / (127 * 127 * 127) / 2; + volume = (volume * m_masterVolume) / 4096766; } break; case OPL3::VOLUME_DMX: { - volume = 2 * ((Ch[MidCh].volume * Ch[MidCh].expression) * 127 / 16129) + 1; + volume = 2 * (m_midiChannels[midCh].volume * m_midiChannels[midCh].expression * m_masterVolume / 16129) + 1; //volume = 2 * (Ch[MidCh].volume) + 1; volume = (DMX_volume_mapping_table[(vol < 128) ? vol : 127] * volume) >> 9; - opl.Touch_Real(c, volume, brightness); } break; case OPL3::VOLUME_APOGEE: { - volume = ((Ch[MidCh].volume * Ch[MidCh].expression) * 127 / 16129); + volume = (m_midiChannels[midCh].volume * m_midiChannels[midCh].expression * m_masterVolume / 16129); volume = ((64 * (vol + 0x80)) * volume) >> 15; //volume = ((63 * (vol + 0x80)) * Ch[MidCh].volume) >> 15; - opl.Touch_Real(c, volume, brightness); } break; case OPL3::VOLUME_9X: { - //volume = 63 - W9X_volume_mapping_table[(((vol * Ch[MidCh].volume /** Ch[MidCh].expression*/) * 127 / 16129 /*2048383*/) >> 2)]; - volume = 63 - W9X_volume_mapping_table[(((vol * Ch[MidCh].volume * Ch[MidCh].expression) * 127 / 2048383) >> 2)]; + //volume = 63 - W9X_volume_mapping_table[(((vol * Ch[MidCh].volume /** Ch[MidCh].expression*/) * m_masterVolume / 16129 /*2048383*/) >> 2)]; + volume = 63 - W9X_volume_mapping_table[((vol * m_midiChannels[midCh].volume * m_midiChannels[midCh].expression * m_masterVolume / 2048383) >> 2)]; //volume = W9X_volume_mapping_table[vol >> 2] + volume; - opl.Touch_Real(c, volume, brightness); } break; } + m_synth.touchNote(c, static_cast(volume), static_cast(brightness)); + /* DEBUG ONLY!!! - static uint32_t max = 0; + static uint32_t max = 0; - if(volume == 0) - max = 0; + if(volume == 0) + max = 0; - if(volume > max) - max = volume; + if(volume > max) + max = volume; - printf("%d\n", max); - fflush(stdout); - */ + printf("%d\n", max); + fflush(stdout); + */ } if(props_mask & Upd_Pitch) { - AdlChannel::LocationData *d = ch[c].users_find(my_loc); + AdlChannel::LocationData *d = m_chipChannels[c].users_find(my_loc); // Don't bend a sustained note - if(!d || !d->sustained) + if(!d || (d->sustained == AdlChannel::LocationData::Sustain_None)) { - double midibend = Ch[MidCh].bend * Ch[MidCh].bendsense; + double midibend = m_midiChannels[midCh].bend * m_midiChannels[midCh].bendsense; double bend = midibend + ins.ains.finetune; double phase = 0.0; - uint8_t vibrato = std::max(Ch[MidCh].vibrato, Ch[MidCh].aftertouch); + uint8_t vibrato = std::max(m_midiChannels[midCh].vibrato, m_midiChannels[midCh].aftertouch); vibrato = std::max(vibrato, i->vibrato); if((ains.flags & adlinsdata::Flag_Pseudo4op) && ins.pseudo4op) @@ -1645,359 +1298,36 @@ void MIDIplay::NoteUpdate(uint16_t MidCh, phase = ains.voice2_fine_tune;//0.125; // Detune the note slightly (this is what Doom does) } - if(vibrato && (!d || d->vibdelay >= Ch[MidCh].vibdelay)) - bend += static_cast(vibrato) * Ch[MidCh].vibdepth * std::sin(Ch[MidCh].vibpos); + if(vibrato && (!d || d->vibdelay_us >= m_midiChannels[midCh].vibdelay_us)) + bend += static_cast(vibrato) * m_midiChannels[midCh].vibdepth * std::sin(m_midiChannels[midCh].vibpos); -#ifdef ADLMIDI_USE_DOSBOX_OPL -# define BEND_COEFFICIENT 172.00093 -#else -# define BEND_COEFFICIENT 172.4387 -#endif - opl.NoteOn(c, BEND_COEFFICIENT * std::exp(0.057762265 * (static_cast(tone) + bend + phase))); +#define BEND_COEFFICIENT 172.4387 + m_synth.noteOn(c, c_slave, BEND_COEFFICIENT * std::exp(0.057762265 * (currentTone + bend + phase))); #undef BEND_COEFFICIENT if(hooks.onNote) - hooks.onNote(hooks.onNote_userData, c, tone, midiins, vol, midibend); + hooks.onNote(hooks.onNote_userData, c, noteTone, midiins, vol, midibend); } } } if(info.chip_channels_count == 0) - Ch[MidCh].activenotes_erase(i); + { + if(i->glideRate != HUGE_VAL) + --m_midiChannels[midCh].gliding_note_count; + m_midiChannels[midCh].activenotes_erase(i); + } } -#ifndef ADLMIDI_DISABLE_MIDI_SEQUENCER -bool MIDIplay::ProcessEventsNew(bool isSeek) +void MIDIplay::noteUpdateAll(size_t midCh, unsigned props_mask) { - if(CurrentPositionNew.track.size() == 0) - atEnd = true;//No MIDI track data to play - if(atEnd) - return false;//No more events in the queue - - loopEnd = false; - const size_t TrackCount = CurrentPositionNew.track.size(); - const PositionNew RowBeginPosition(CurrentPositionNew); - -#ifdef DEBUG_TIME_CALCULATION - double maxTime = 0.0; -#endif - - for(size_t tk = 0; tk < TrackCount; ++tk) + for(MIDIchannel::activenoteiterator + i = m_midiChannels[midCh].activenotes_begin(); i;) { - PositionNew::TrackInfo &track = CurrentPositionNew.track[tk]; - if((track.status >= 0) && (track.delay <= 0)) - { - //Check is an end of track has been reached - if(track.pos == trackDataNew[tk].end()) - { - track.status = -1; - break; - } - - // Handle event - for(size_t i = 0; i < track.pos->events.size(); i++) - { - const MidiEvent &evt = track.pos->events[i]; -#ifdef ENABLE_BEGIN_SILENCE_SKIPPING - if(!CurrentPositionNew.began && (evt.type == MidiEvent::T_NOTEON)) - CurrentPositionNew.began = true; -#endif - if(isSeek && (evt.type == MidiEvent::T_NOTEON)) - continue; - HandleEvent(tk, evt, track.status); - if(loopEnd) - break;//Stop event handling on catching loopEnd event! - } - -#ifdef DEBUG_TIME_CALCULATION - if(maxTime < track.pos->time) - maxTime = track.pos->time; -#endif - // Read next event time (unless the track just ended) - if(track.status >= 0) - { - track.delay += track.pos->delay; - track.pos++; - } - } + MIDIchannel::activenoteiterator j(i++); + noteUpdate(midCh, j, props_mask); } - -#ifdef DEBUG_TIME_CALCULATION - std::fprintf(stdout, " \r"); - std::fprintf(stdout, "Time: %10f; Audio: %10f\r", maxTime, CurrentPositionNew.absTimePosition); - std::fflush(stdout); -#endif - - // Find shortest delay from all track - uint64_t shortest = 0; - bool shortest_no = true; - - for(size_t tk = 0; tk < TrackCount; ++tk) - { - PositionNew::TrackInfo &track = CurrentPositionNew.track[tk]; - if((track.status >= 0) && (shortest_no || track.delay < shortest)) - { - shortest = track.delay; - shortest_no = false; - } - } - - //if(shortest > 0) UI.PrintLn("shortest: %ld", shortest); - - // Schedule the next playevent to be processed after that delay - for(size_t tk = 0; tk < TrackCount; ++tk) - CurrentPositionNew.track[tk].delay -= shortest; - - fraction t = shortest * Tempo; - -#ifdef ENABLE_BEGIN_SILENCE_SKIPPING - if(CurrentPositionNew.began) -#endif - CurrentPositionNew.wait += t.value(); - - //if(shortest > 0) UI.PrintLn("Delay %ld (%g)", shortest, (double)t.valuel()); - if(loopStart) - { - LoopBeginPositionNew = RowBeginPosition; - loopStart = false; - } - - if(shortest_no || loopEnd) - { - //Loop if song end or loop end point has reached - loopEnd = false; - shortest = 0; - if(!m_setup.loopingIsEnabled) - { - atEnd = true; //Don't handle events anymore - CurrentPositionNew.wait += postSongWaitDelay;//One second delay until stop playing - return true;//We have caugh end here! - } - CurrentPositionNew = LoopBeginPositionNew; - } - - return true;//Has events in queue } -MIDIplay::MidiEvent MIDIplay::parseEvent(uint8_t **pptr, uint8_t *end, int &status) -{ - uint8_t *&ptr = *pptr; - MIDIplay::MidiEvent evt; - - if(ptr + 1 > end) - { - //When track doesn't ends on the middle of event data, it's must be fine - evt.type = MidiEvent::T_SPECIAL; - evt.subtype = MidiEvent::ST_ENDTRACK; - return evt; - } - - unsigned char byte = *(ptr++); - bool ok = false; - - if(byte == MidiEvent::T_SYSEX || byte == MidiEvent::T_SYSEX2)// Ignore SysEx - { - uint64_t length = ReadVarLenEx(pptr, end, ok); - if(!ok || (ptr + length > end)) - { - errorString += "parseEvent: Can't read SysEx event - Unexpected end of track data.\n"; - evt.isValid = 0; - return evt; - } - ptr += (size_t)length; - return evt; - } - - if(byte == MidiEvent::T_SPECIAL) - { - // Special event FF - uint8_t evtype = *(ptr++); - uint64_t length = ReadVarLenEx(pptr, end, ok); - if(!ok || (ptr + length > end)) - { - errorString += "parseEvent: Can't read Special event - Unexpected end of track data.\n"; - evt.isValid = 0; - return evt; - } - std::string data(length ? (const char *)ptr : 0, (size_t)length); - ptr += (size_t)length; - - evt.type = byte; - evt.subtype = evtype; - evt.data.insert(evt.data.begin(), data.begin(), data.end()); - -#if 0 /* Print all tempo events */ - if(evt.subtype == MidiEvent::ST_TEMPOCHANGE) - { - if(hooks.onDebugMessage) - hooks.onDebugMessage(hooks.onDebugMessage_userData, "Temp Change: %02X%02X%02X", evt.data[0], evt.data[1], evt.data[2]); - } -#endif - - /* TODO: Store those meta-strings separately and give ability to read them - * by external functions (to display song title and copyright in the player) */ - if(evt.subtype == MidiEvent::ST_COPYRIGHT) - { - if(musCopyright.empty()) - { - musCopyright = std::string((const char *)evt.data.data(), evt.data.size()); - if(hooks.onDebugMessage) - hooks.onDebugMessage(hooks.onDebugMessage_userData, "Music copyright: %s", musCopyright.c_str()); - } - else if(hooks.onDebugMessage) - { - std::string str((const char *)evt.data.data(), evt.data.size()); - hooks.onDebugMessage(hooks.onDebugMessage_userData, "Extra copyright event: %s", str.c_str()); - } - } - else if(evt.subtype == MidiEvent::ST_SQTRKTITLE) - { - if(musTitle.empty()) - { - musTitle = std::string((const char *)evt.data.data(), evt.data.size()); - if(hooks.onDebugMessage) - hooks.onDebugMessage(hooks.onDebugMessage_userData, "Music title: %s", musTitle.c_str()); - } - else if(hooks.onDebugMessage) - { - //TODO: Store track titles and associate them with each track and make API to retreive them - std::string str((const char *)evt.data.data(), evt.data.size()); - musTrackTitles.push_back(str); - hooks.onDebugMessage(hooks.onDebugMessage_userData, "Track title: %s", str.c_str()); - } - } - else if(evt.subtype == MidiEvent::ST_INSTRTITLE) - { - if(hooks.onDebugMessage) - { - std::string str((const char *)evt.data.data(), evt.data.size()); - hooks.onDebugMessage(hooks.onDebugMessage_userData, "Instrument: %s", str.c_str()); - } - } - else if(evt.subtype == MidiEvent::ST_MARKER) - { - //To lower - for(size_t i = 0; i < data.size(); i++) - { - if(data[i] <= 'Z' && data[i] >= 'A') - data[i] = data[i] - ('Z' - 'z'); - } - - if(data == "loopstart") - { - //Return a custom Loop Start event instead of Marker - evt.subtype = MidiEvent::ST_LOOPSTART; - evt.data.clear();//Data is not needed - return evt; - } - - if(data == "loopend") - { - //Return a custom Loop End event instead of Marker - evt.subtype = MidiEvent::ST_LOOPEND; - evt.data.clear();//Data is not needed - return evt; - } - } - - if(evtype == MidiEvent::ST_ENDTRACK) - status = -1;//Finalize track - - return evt; - } - - // Any normal event (80..EF) - if(byte < 0x80) - { - byte = static_cast(status | 0x80); - ptr--; - } - - //Sys Com Song Select(Song #) [0-127] - if(byte == MidiEvent::T_SYSCOMSNGSEL) - { - if(ptr + 1 > end) - { - errorString += "parseEvent: Can't read System Command Song Select event - Unexpected end of track data.\n"; - evt.isValid = 0; - return evt; - } - evt.type = byte; - evt.data.push_back(*(ptr++)); - return evt; - } - - //Sys Com Song Position Pntr [LSB, MSB] - if(byte == MidiEvent::T_SYSCOMSPOSPTR) - { - if(ptr + 2 > end) - { - errorString += "parseEvent: Can't read System Command Position Pointer event - Unexpected end of track data.\n"; - evt.isValid = 0; - return evt; - } - evt.type = byte; - evt.data.push_back(*(ptr++)); - evt.data.push_back(*(ptr++)); - return evt; - } - - uint8_t midCh = byte & 0x0F, evType = (byte >> 4) & 0x0F; - status = byte; - evt.channel = midCh; - evt.type = evType; - - switch(evType) - { - case MidiEvent::T_NOTEOFF://2 byte length - case MidiEvent::T_NOTEON: - case MidiEvent::T_NOTETOUCH: - case MidiEvent::T_CTRLCHANGE: - case MidiEvent::T_WHEEL: - if(ptr + 2 > end) - { - errorString += "parseEvent: Can't read regular 2-byte event - Unexpected end of track data.\n"; - evt.isValid = 0; - return evt; - } - - evt.data.push_back(*(ptr++)); - evt.data.push_back(*(ptr++)); - - /* TODO: Implement conversion of RSXX's note volumes out of synthesizer */ - /*if((opl.m_musicMode == OPL3::MODE_RSXX) && (evType == MidiEvent::T_NOTEON) && (evt.data[1] != 0)) - { - //NOT WORKING YET - evt.type = MidiEvent::T_NOTETOUCH; - } - else */if((evType == MidiEvent::T_NOTEON) && (evt.data[1] == 0)) - { - evt.type = MidiEvent::T_NOTEOFF; // Note ON with zero velocity is Note OFF! - } //111'th loopStart controller (RPG Maker and others) - else if((evType == MidiEvent::T_CTRLCHANGE) && (evt.data[0] == 111)) - { - //Change event type to custom Loop Start event and clear data - evt.type = MidiEvent::T_SPECIAL; - evt.subtype = MidiEvent::ST_LOOPSTART; - evt.data.clear(); - } - - return evt; - case MidiEvent::T_PATCHCHANGE://1 byte length - case MidiEvent::T_CHANAFTTOUCH: - if(ptr + 1 > end) - { - errorString += "parseEvent: Can't read regular 1-byte event - Unexpected end of track data.\n"; - evt.isValid = 0; - return evt; - } - evt.data.push_back(*(ptr++)); - return evt; - } - - return evt; -} -#endif /* ADLMIDI_DISABLE_MIDI_SEQUENCER */ - const std::string &MIDIplay::getErrorString() { return errorStringOut; @@ -2008,182 +1338,33 @@ void MIDIplay::setErrorString(const std::string &err) errorStringOut = err; } -#ifndef ADLMIDI_DISABLE_MIDI_SEQUENCER -void MIDIplay::HandleEvent(size_t tk, const MIDIplay::MidiEvent &evt, int &status) +int64_t MIDIplay::calculateChipChannelGoodness(size_t c, const MIDIchannel::NoteInfo::Phys &ins) const { - if(hooks.onEvent) + const AdlChannel &chan = m_chipChannels[c]; + int64_t koff_ms = chan.koff_time_until_neglible_us / 1000; + int64_t s = -koff_ms; + + // Rate channel with a releasing note + if(s < 0 && chan.users_empty()) { - hooks.onEvent(hooks.onEvent_userData, - evt.type, - evt.subtype, - evt.channel, - evt.data.data(), - evt.data.size()); + s -= 40000; + // If it's same instrument, better chance to get it when no free channels + if(chan.recent_ins == ins) + s = (m_synth.m_musicMode == OPL3::MODE_CMF) ? 0 : -koff_ms; + return s; } - if(evt.type == MidiEvent::T_SYSEX || evt.type == MidiEvent::T_SYSEX2) // Ignore SysEx - { - //std::string data( length?(const char*) &TrackData[tk][CurrentPosition.track[tk].ptr]:0, length ); - //UI.PrintLn("SysEx %02X: %u bytes", byte, length/*, data.c_str()*/); - return; - } - - if(evt.type == MidiEvent::T_SPECIAL) - { - // Special event FF - uint8_t evtype = evt.subtype; - uint64_t length = (uint64_t)evt.data.size(); - std::string data(length ? (const char *)evt.data.data() : 0, (size_t)length); - - if(evtype == MidiEvent::ST_ENDTRACK)//End Of Track - { - status = -1; - return; - } - - if(evtype == MidiEvent::ST_TEMPOCHANGE)//Tempo change - { - Tempo = InvDeltaTicks * fraction(ReadBEint(evt.data.data(), evt.data.size())); - return; - } - - if(evtype == MidiEvent::ST_MARKER)//Meta event - { - //Do nothing! :-P - return; - } - - if(evtype == MidiEvent::ST_DEVICESWITCH) - { - current_device[tk] = ChooseDevice(data); - return; - } - - //if(evtype >= 1 && evtype <= 6) - // UI.PrintLn("Meta %d: %s", evtype, data.c_str()); - - //Turn on Loop handling when loop is enabled - if(m_setup.loopingIsEnabled && !invalidLoop) - { - if(evtype == MidiEvent::ST_LOOPSTART) // Special non-spec ADLMIDI special for IMF playback: Direct poke to AdLib - { - loopStart = true; - return; - } - - if(evtype == MidiEvent::ST_LOOPEND) // Special non-spec ADLMIDI special for IMF playback: Direct poke to AdLib - { - loopEnd = true; - return; - } - } - - if(evtype == MidiEvent::ST_RAWOPL) // Special non-spec ADLMIDI special for IMF playback: Direct poke to AdLib - { - uint8_t i = static_cast(data[0]), v = static_cast(data[1]); - if((i & 0xF0) == 0xC0) - v |= 0x30; - //std::printf("OPL poke %02X, %02X\n", i, v); - //std::fflush(stdout); - opl.Poke(0, i, v); - return; - } - - return; - } - - // Any normal event (80..EF) - // if(evt.type < 0x80) - // { - // byte = static_cast(CurrentPosition.track[tk].status | 0x80); - // CurrentPosition.track[tk].ptr--; - // } - - if(evt.type == MidiEvent::T_SYSCOMSNGSEL || - evt.type == MidiEvent::T_SYSCOMSPOSPTR) - return; - - /*UI.PrintLn("@%X Track %u: %02X %02X", - CurrentPosition.track[tk].ptr-1, (unsigned)tk, byte, - TrackData[tk][CurrentPosition.track[tk].ptr]);*/ - uint8_t midCh = evt.channel;//byte & 0x0F, EvType = byte >> 4; - midCh += (uint8_t)current_device[tk]; - status = evt.type; - - switch(evt.type) - { - case MidiEvent::T_NOTEOFF: // Note off - { - uint8_t note = evt.data[0]; - realTime_NoteOff(midCh, note); - break; - } - - case MidiEvent::T_NOTEON: // Note on - { - uint8_t note = evt.data[0]; - uint8_t vol = evt.data[1]; - /*if(*/ realTime_NoteOn(midCh, note, vol); /*)*/ - //CurrentPosition.began = true; - break; - } - - case MidiEvent::T_NOTETOUCH: // Note touch - { - uint8_t note = evt.data[0]; - uint8_t vol = evt.data[1]; - realTime_NoteAfterTouch(midCh, note, vol); - break; - } - - case MidiEvent::T_CTRLCHANGE: // Controller change - { - uint8_t ctrlno = evt.data[0]; - uint8_t value = evt.data[1]; - realTime_Controller(midCh, ctrlno, value); - break; - } - - case MidiEvent::T_PATCHCHANGE: // Patch change - realTime_PatchChange(midCh, evt.data[0]); - break; - - case MidiEvent::T_CHANAFTTOUCH: // Channel after-touch - { - // TODO: Verify, is this correct action? - uint8_t vol = evt.data[0]; - realTime_ChannelAfterTouch(midCh, vol); - break; - } - - case MidiEvent::T_WHEEL: // Wheel/pitch bend - { - uint8_t a = evt.data[0]; - uint8_t b = evt.data[1]; - realTime_PitchBend(midCh, b, a); - break; - } - } -} -#endif /* ADLMIDI_DISABLE_MIDI_SEQUENCER */ - -int64_t MIDIplay::CalculateAdlChannelGoodness(size_t c, const MIDIchannel::NoteInfo::Phys &ins, uint16_t) const -{ - int64_t s = -ch[c].koff_time_until_neglible; - // Same midi-instrument = some stability - //if(c == MidCh) s += 4; - for(AdlChannel::LocationData *j = ch[c].users_first; j; j = j->next) + for(AdlChannel::LocationData *j = chan.users_first; j; j = j->next) { - s -= 4000; + s -= 4000000; - if(!j->sustained) - s -= j->kon_time_until_neglible; - else - s -= (j->kon_time_until_neglible / 2); + int64_t kon_ms = j->kon_time_until_neglible_us / 1000; + s -= (j->sustained == AdlChannel::LocationData::Sustain_None) ? + kon_ms : (kon_ms / 2); MIDIchannel::activenoteiterator - k = const_cast(Ch[j->loc.MidCh]).activenotes_find(j->loc.note); + k = const_cast(m_midiChannels[j->loc.MidCh]).activenotes_find(j->loc.note); if(k) { @@ -2192,13 +1373,13 @@ int64_t MIDIplay::CalculateAdlChannelGoodness(size_t c, const MIDIchannel::NoteI { s += 300; // Arpeggio candidate = even better - if(j->vibdelay < 70 - || j->kon_time_until_neglible > 20000) - s += 0; + if(j->vibdelay_us < 70000 + || j->kon_time_until_neglible_us > 20000000) + s += 10; } // Percussion is inferior to melody - s += 50 * (int64_t)(k->midiins / 128); + s += k->isPercussion ? 50 : 0; /* if(k->second.midiins >= 25 && k->second.midiins < 40 @@ -2214,17 +1395,17 @@ int64_t MIDIplay::CalculateAdlChannelGoodness(size_t c, const MIDIchannel::NoteI // increase the score slightly. unsigned n_evacuation_stations = 0; - for(size_t c2 = 0; c2 < static_cast(opl.NumChannels); ++c2) + for(size_t c2 = 0; c2 < static_cast(m_synth.m_numChannels); ++c2) { if(c2 == c) continue; - if(opl.four_op_category[c2] - != opl.four_op_category[c]) continue; + if(m_synth.m_channelCategory[c2] + != m_synth.m_channelCategory[c]) continue; - for(AdlChannel::LocationData *m = ch[c2].users_first; m; m = m->next) + for(AdlChannel::LocationData *m = m_chipChannels[c2].users_first; m; m = m->next) { - if(m->sustained) continue; - if(m->vibdelay >= 200) continue; + if(m->sustained != AdlChannel::LocationData::Sustain_None) continue; + if(m->vibdelay_us >= 200000) continue; if(m->ins != j->ins) continue; n_evacuation_stations += 1; } @@ -2237,26 +1418,26 @@ int64_t MIDIplay::CalculateAdlChannelGoodness(size_t c, const MIDIchannel::NoteI } -void MIDIplay::PrepareAdlChannelForNewNote(size_t c, const MIDIchannel::NoteInfo::Phys &ins) +void MIDIplay::prepareChipChannelForNewNote(size_t c, const MIDIchannel::NoteInfo::Phys &ins) { - if(ch[c].users_empty()) return; // Nothing to do + if(m_chipChannels[c].users_empty()) return; // Nothing to do //bool doing_arpeggio = false; - for(AdlChannel::LocationData *jnext = ch[c].users_first; jnext;) + for(AdlChannel::LocationData *jnext = m_chipChannels[c].users_first; jnext;) { AdlChannel::LocationData *j = jnext; jnext = jnext->next; - if(!j->sustained) + if(j->sustained == AdlChannel::LocationData::Sustain_None) { // Collision: Kill old note, // UNLESS we're going to do arpeggio MIDIchannel::activenoteiterator i - (Ch[j->loc.MidCh].activenotes_ensure_find(j->loc.note)); + (m_midiChannels[j->loc.MidCh].activenotes_ensure_find(j->loc.note)); // Check if we can do arpeggio. - if((j->vibdelay < 70 - || j->kon_time_until_neglible > 20000) + if((j->vibdelay_us < 70000 + || j->kon_time_until_neglible_us > 20000000) && j->ins == ins) { // Do arpeggio together with this note. @@ -2264,7 +1445,7 @@ void MIDIplay::PrepareAdlChannelForNewNote(size_t c, const MIDIchannel::NoteInfo continue; } - KillOrEvacuate(c, j, i); + killOrEvacuate(c, j, i); // ^ will also erase j from ch[c].users. } } @@ -2272,62 +1453,64 @@ void MIDIplay::PrepareAdlChannelForNewNote(size_t c, const MIDIchannel::NoteInfo // Kill all sustained notes on this channel // Don't keep them for arpeggio, because arpeggio requires // an intact "activenotes" record. This is a design flaw. - KillSustainingNotes(-1, static_cast(c)); + killSustainingNotes(-1, static_cast(c), AdlChannel::LocationData::Sustain_ANY); // Keyoff the channel so that it can be retriggered, // unless the new note will be introduced as just an arpeggio. - if(ch[c].users_empty()) - opl.NoteOff(c); + if(m_chipChannels[c].users_empty()) + m_synth.noteOff(c); } -void MIDIplay::KillOrEvacuate(size_t from_channel, +void MIDIplay::killOrEvacuate(size_t from_channel, AdlChannel::LocationData *j, MIDIplay::MIDIchannel::activenoteiterator i) { + uint32_t maxChannels = ADL_MAX_CHIPS * 18; + // Before killing the note, check if it can be // evacuated to another channel as an arpeggio // instrument. This helps if e.g. all channels // are full of strings and we want to do percussion. // FIXME: This does not care about four-op entanglements. - for(uint32_t c = 0; c < opl.NumChannels; ++c) + for(uint32_t c = 0; c < m_synth.m_numChannels; ++c) { uint16_t cs = static_cast(c); - if(c > std::numeric_limits::max()) + if(c >= maxChannels) break; if(c == from_channel) continue; - if(opl.four_op_category[c] != opl.four_op_category[from_channel]) + if(m_synth.m_channelCategory[c] != m_synth.m_channelCategory[from_channel]) continue; - AdlChannel &adlch = ch[c]; + AdlChannel &adlch = m_chipChannels[c]; if(adlch.users_size == AdlChannel::users_max) continue; // no room for more arpeggio on channel for(AdlChannel::LocationData *m = adlch.users_first; m; m = m->next) { - if(m->vibdelay >= 200 - && m->kon_time_until_neglible < 10000) continue; + if(m->vibdelay_us >= 200000 + && m->kon_time_until_neglible_us < 10000000) continue; if(m->ins != j->ins) continue; if(hooks.onNote) { hooks.onNote(hooks.onNote_userData, (int)from_channel, - i->tone, + i->noteTone, static_cast(i->midiins), 0, 0.0); hooks.onNote(hooks.onNote_userData, (int)c, - i->tone, + i->noteTone, static_cast(i->midiins), i->vol, 0.0); } i->phys_erase(static_cast(from_channel)); i->phys_ensure_find_or_create(cs)->assign(j->ins); - if(!ch[cs].users_insert(*j)) + if(!m_chipChannels[cs].users_insert(*j)) assert(false); - ch[from_channel].users_erase(j); + m_chipChannels[from_channel].users_erase(j); return; } } @@ -2340,24 +1523,24 @@ void MIDIplay::KillOrEvacuate(size_t from_channel, ins );*/ // Kill it - NoteUpdate(j->loc.MidCh, + noteUpdate(j->loc.MidCh, i, Upd_Off, static_cast(from_channel)); } -void MIDIplay::Panic() +void MIDIplay::panic() { - for(uint8_t chan = 0; chan < Ch.size(); chan++) + for(uint8_t chan = 0; chan < m_midiChannels.size(); chan++) { for(uint8_t note = 0; note < 128; note++) realTime_NoteOff(chan, note); } } -void MIDIplay::KillSustainingNotes(int32_t MidCh, int32_t this_adlchn) +void MIDIplay::killSustainingNotes(int32_t midCh, int32_t this_adlchn, uint32_t sustain_type) { - uint32_t first = 0, last = opl.NumChannels; + uint32_t first = 0, last = m_synth.m_numChannels; if(this_adlchn >= 0) { @@ -2365,57 +1548,87 @@ void MIDIplay::KillSustainingNotes(int32_t MidCh, int32_t this_adlchn) last = first + 1; } - for(unsigned c = first; c < last; ++c) + for(uint32_t c = first; c < last; ++c) { - if(ch[c].users_empty()) continue; // Nothing to do + if(m_chipChannels[c].users_empty()) + continue; // Nothing to do - for(AdlChannel::LocationData *jnext = ch[c].users_first; jnext;) + for(AdlChannel::LocationData *jnext = m_chipChannels[c].users_first; jnext;) { AdlChannel::LocationData *j = jnext; jnext = jnext->next; - if((MidCh < 0 || j->loc.MidCh == MidCh) - && j->sustained) + if((midCh < 0 || j->loc.MidCh == midCh) + && ((j->sustained & sustain_type) != 0)) { int midiins = '?'; if(hooks.onNote) hooks.onNote(hooks.onNote_userData, (int)c, j->loc.note, midiins, 0, 0.0); - ch[c].users_erase(j); + j->sustained &= ~sustain_type; + if(j->sustained == AdlChannel::LocationData::Sustain_None) + m_chipChannels[c].users_erase(j);//Remove only when note is clean from any holders } } // Keyoff the channel, if there are no users left. - if(ch[c].users_empty()) - opl.NoteOff(c); + if(m_chipChannels[c].users_empty()) + m_synth.noteOff(c); } } -void MIDIplay::SetRPN(unsigned MidCh, unsigned value, bool MSB) +void MIDIplay::markSostenutoNotes(int32_t midCh) { - bool nrpn = Ch[MidCh].nrpn; - unsigned addr = Ch[MidCh].lastmrpn * 0x100 + Ch[MidCh].lastlrpn; + uint32_t first = 0, last = m_synth.m_numChannels; + for(uint32_t c = first; c < last; ++c) + { + if(m_chipChannels[c].users_empty()) + continue; // Nothing to do + + for(AdlChannel::LocationData *jnext = m_chipChannels[c].users_first; jnext;) + { + AdlChannel::LocationData *j = jnext; + jnext = jnext->next; + if((j->loc.MidCh == midCh) && (j->sustained == AdlChannel::LocationData::Sustain_None)) + j->sustained |= AdlChannel::LocationData::Sustain_Sostenuto; + } + } +} + +void MIDIplay::setRPN(size_t midCh, unsigned value, bool MSB) +{ + bool nrpn = m_midiChannels[midCh].nrpn; + unsigned addr = m_midiChannels[midCh].lastmrpn * 0x100 + m_midiChannels[midCh].lastlrpn; switch(addr + nrpn * 0x10000 + MSB * 0x20000) { case 0x0000 + 0*0x10000 + 1*0x20000: // Pitch-bender sensitivity - Ch[MidCh].bendsense_msb = value; - Ch[MidCh].updateBendSensitivity(); + m_midiChannels[midCh].bendsense_msb = value; + m_midiChannels[midCh].updateBendSensitivity(); break; case 0x0000 + 0*0x10000 + 0*0x20000: // Pitch-bender sensitivity LSB - Ch[MidCh].bendsense_lsb = value; - Ch[MidCh].updateBendSensitivity(); + m_midiChannels[midCh].bendsense_lsb = value; + m_midiChannels[midCh].updateBendSensitivity(); break; - case 0x0108 + 1*0x10000 + 1*0x20000: // Vibrato speed - if(value == 64) Ch[MidCh].vibspeed = 1.0; - else if(value < 100) Ch[MidCh].vibspeed = 1.0 / (1.6e-2 * (value ? value : 1)); - else Ch[MidCh].vibspeed = 1.0 / (0.051153846 * value - 3.4965385); - Ch[MidCh].vibspeed *= 2 * 3.141592653 * 5.0; + case 0x0108 + 1*0x10000 + 1*0x20000: + if((m_synthMode & Mode_XG) != 0) // Vibrato speed + { + if(value == 64) m_midiChannels[midCh].vibspeed = 1.0; + else if(value < 100) m_midiChannels[midCh].vibspeed = 1.0 / (1.6e-2 * (value ? value : 1)); + else m_midiChannels[midCh].vibspeed = 1.0 / (0.051153846 * value - 3.4965385); + m_midiChannels[midCh].vibspeed *= 2 * 3.141592653 * 5.0; + } break; - case 0x0109 + 1*0x10000 + 1*0x20000: // Vibrato depth - Ch[MidCh].vibdepth = ((value - 64) * 0.15) * 0.01; + case 0x0109 + 1*0x10000 + 1*0x20000: + if((m_synthMode & Mode_XG) != 0) // Vibrato depth + { + m_midiChannels[midCh].vibdepth = (((int)value - 64) * 0.15) * 0.01; + } break; - case 0x010A + 1*0x10000 + 1*0x20000: // Vibrato delay in millisecons - Ch[MidCh].vibdelay = value ? int64_t(0.2092 * std::exp(0.0795 * (double)value)) : 0; + case 0x010A + 1*0x10000 + 1*0x20000: + if((m_synthMode & Mode_XG) != 0) // Vibrato delay in millisecons + { + m_midiChannels[midCh].vibdelay_us = value ? int64_t(209.2 * std::exp(0.0795 * (double)value)) : 0; + } break; default:/* UI.PrintLn("%s %04X <- %d (%cSB) (ch %u)", "NRPN"+!nrpn, addr, value, "LM"[MSB], MidCh);*/ @@ -2423,67 +1636,53 @@ void MIDIplay::SetRPN(unsigned MidCh, unsigned value, bool MSB) } } -//void MIDIplay::UpdatePortamento(unsigned MidCh) -//{ -// // mt = 2^(portamento/2048) * (1.0 / 5000.0) -// /* -// double mt = std::exp(0.00033845077 * Ch[MidCh].portamento); -// NoteUpdate_All(MidCh, Upd_Pitch); -// */ -// //UI.PrintLn("Portamento %u: %u (unimplemented)", MidCh, Ch[MidCh].portamento); -//} - -void MIDIplay::NoteUpdate_All(uint16_t MidCh, unsigned props_mask) +void MIDIplay::updatePortamento(size_t midCh) { - for(MIDIchannel::activenoteiterator - i = Ch[MidCh].activenotes_begin(); i;) - { - MIDIchannel::activenoteiterator j(i++); - NoteUpdate(MidCh, j, props_mask); - } + double rate = HUGE_VAL; + uint16_t midival = m_midiChannels[midCh].portamento; + if(m_midiChannels[midCh].portamentoEnable && midival > 0) + rate = 350.0 * std::pow(2.0, -0.062 * (1.0 / 128) * midival); + m_midiChannels[midCh].portamentoRate = rate; } -void MIDIplay::NoteOff(uint16_t MidCh, uint8_t note) + +void MIDIplay::noteOff(size_t midCh, uint8_t note) { MIDIchannel::activenoteiterator - i = Ch[MidCh].activenotes_find(note); - + i = m_midiChannels[midCh].activenotes_find(note); if(i) - NoteUpdate(MidCh, i, Upd_Off); + noteUpdate(midCh, i, Upd_Off); } -void MIDIplay::UpdateVibrato(double amount) +void MIDIplay::updateVibrato(double amount) { - for(size_t a = 0, b = Ch.size(); a < b; ++a) + for(size_t a = 0, b = m_midiChannels.size(); a < b; ++a) { - if(Ch[a].hasVibrato() && !Ch[a].activenotes_empty()) + if(m_midiChannels[a].hasVibrato() && !m_midiChannels[a].activenotes_empty()) { - NoteUpdate_All(static_cast(a), Upd_Pitch); - Ch[a].vibpos += amount * Ch[a].vibspeed; + noteUpdateAll(static_cast(a), Upd_Pitch); + m_midiChannels[a].vibpos += amount * m_midiChannels[a].vibspeed; } else - Ch[a].vibpos = 0.0; + m_midiChannels[a].vibpos = 0.0; } } - - - -uint64_t MIDIplay::ChooseDevice(const std::string &name) +size_t MIDIplay::chooseDevice(const std::string &name) { - std::map::iterator i = devices.find(name); + std::map::iterator i = m_midiDevices.find(name); - if(i != devices.end()) + if(i != m_midiDevices.end()) return i->second; - size_t n = devices.size() * 16; - devices.insert(std::make_pair(name, n)); - Ch.resize(n + 16); + size_t n = m_midiDevices.size() * 16; + m_midiDevices.insert(std::make_pair(name, n)); + m_midiChannels.resize(n + 16); return n; } -void MIDIplay::UpdateArpeggio(double) // amount = amount of time passed +void MIDIplay::updateArpeggio(double) // amount = amount of time passed { // If there is an adlib channel that has multiple notes // simulated on the same channel, arpeggio them. @@ -2509,17 +1708,17 @@ void MIDIplay::UpdateArpeggio(double) // amount = amount of time passed ++m_arpeggioCounter; - for(uint32_t c = 0; c < opl.NumChannels; ++c) + for(uint32_t c = 0; c < m_synth.m_numChannels; ++c) { retry_arpeggio: if(c > uint32_t(std::numeric_limits::max())) break; - size_t n_users = ch[c].users_size; + size_t n_users = m_chipChannels[c].users_size; if(n_users > 1) { - AdlChannel::LocationData *i = ch[c].users_first; + AdlChannel::LocationData *i = m_chipChannels[c].users_first; size_t rate_reduction = 3; if(n_users >= 3) @@ -2532,21 +1731,21 @@ retry_arpeggio: n = 0; n < count; ++n) i = i->next; - if(i->sustained == false) + if(i->sustained == AdlChannel::LocationData::Sustain_None) { - if(i->kon_time_until_neglible <= 0l) + if(i->kon_time_until_neglible_us <= 0) { - NoteUpdate( + noteUpdate( i->loc.MidCh, - Ch[ i->loc.MidCh ].activenotes_ensure_find(i->loc.note), + m_midiChannels[ i->loc.MidCh ].activenotes_ensure_find(i->loc.note), Upd_Off, static_cast(c)); goto retry_arpeggio; } - NoteUpdate( + noteUpdate( i->loc.MidCh, - Ch[ i->loc.MidCh ].activenotes_ensure_find(i->loc.note), + m_midiChannels[ i->loc.MidCh ].activenotes_ensure_find(i->loc.note), Upd_Pitch | Upd_Volume | Upd_Pan, static_cast(c)); } @@ -2554,6 +1753,88 @@ retry_arpeggio: } } +void MIDIplay::updateGlide(double amount) +{ + size_t num_channels = m_midiChannels.size(); + + for(size_t channel = 0; channel < num_channels; ++channel) + { + MIDIchannel &midiChan = m_midiChannels[channel]; + if(midiChan.gliding_note_count == 0) + continue; + + for(MIDIchannel::activenoteiterator it = midiChan.activenotes_begin(); + it; ++it) + { + double finalTone = it->noteTone; + double previousTone = it->currentTone; + + bool directionUp = previousTone < finalTone; + double toneIncr = amount * (directionUp ? +it->glideRate : -it->glideRate); + + double currentTone = previousTone + toneIncr; + bool glideFinished = !(directionUp ? (currentTone < finalTone) : (currentTone > finalTone)); + currentTone = glideFinished ? finalTone : currentTone; + + if(currentTone != previousTone) + { + it->currentTone = currentTone; + noteUpdate(static_cast(channel), it, Upd_Pitch); + } + } + } +} + +void MIDIplay::describeChannels(char *str, char *attr, size_t size) +{ + if (!str || size <= 0) + return; + + OPL3 &synth = m_synth; + uint32_t numChannels = synth.m_numChannels; + + uint32_t index = 0; + while(index < numChannels && index < size - 1) + { + const AdlChannel &adlChannel = m_chipChannels[index]; + + AdlChannel::LocationData *loc = adlChannel.users_first; + if(!loc) // off + { + str[index] = '-'; + } + else if(loc->next) // arpeggio + { + str[index] = '@'; + } + else // on + { + switch(synth.m_channelCategory[index]) + { + case OPL3::ChanCat_Regular: + str[index] = '+'; + break; + case OPL3::ChanCat_4op_Master: + case OPL3::ChanCat_4op_Slave: + str[index] = '#'; + break; + default: // rhythm-mode percussion + str[index] = 'r'; + break; + } + } + + uint8_t attribute = 0; + if (loc) // 4-bit color index of MIDI channel + attribute |= (uint8_t)(loc->loc.MidCh & 0xF); + + attr[index] = (char)attribute; + ++index; + } + + str[index] = 0; + attr[index] = 0; +} #ifndef ADLMIDI_DISABLE_CPP_EXTRAS @@ -2569,11 +1850,15 @@ struct AdlInstrumentTester::Impl ADLMIDI_EXPORT AdlInstrumentTester::AdlInstrumentTester(ADL_MIDIPlayer *device) : P(new Impl) { +#ifndef DISABLE_EMBEDDED_BANKS MIDIplay *play = reinterpret_cast(device->adl_midiPlayer); P->cur_gm = 0; P->ins_idx = 0; P->play = play; - P->opl = play ? &play->opl : NULL; + P->opl = play ? &play->m_synth : NULL; +#else + ADL_UNUSED(device); +#endif } ADLMIDI_EXPORT AdlInstrumentTester::~AdlInstrumentTester() @@ -2583,6 +1868,7 @@ ADLMIDI_EXPORT AdlInstrumentTester::~AdlInstrumentTester() ADLMIDI_EXPORT void AdlInstrumentTester::FindAdlList() { +#ifndef DISABLE_EMBEDDED_BANKS const unsigned NumBanks = (unsigned)adl_getBanksCount(); std::set adl_ins_set; for(unsigned bankno = 0; bankno < NumBanks; ++bankno) @@ -2590,32 +1876,39 @@ ADLMIDI_EXPORT void AdlInstrumentTester::FindAdlList() P->adl_ins_list.assign(adl_ins_set.begin(), adl_ins_set.end()); P->ins_idx = 0; NextAdl(0); - P->opl->Silence(); + P->opl->silenceAll(); +#endif } ADLMIDI_EXPORT void AdlInstrumentTester::Touch(unsigned c, unsigned volume) // Volume maxes at 127*127*127 { +#ifndef DISABLE_EMBEDDED_BANKS OPL3 *opl = P->opl; if(opl->m_volumeScale == OPL3::VOLUME_NATIVE) - opl->Touch_Real(c, volume * 127 / (127 * 127 * 127) / 2); + opl->touchNote(c, static_cast(volume * 127 / (127 * 127 * 127) / 2)); else { // The formula below: SOLVE(V=127^3 * 2^( (A-63.49999) / 8), A) - opl->Touch_Real(c, volume > 8725 ? static_cast(std::log((double)volume) * 11.541561 + (0.5 - 104.22845)) : 0); + opl->touchNote(c, static_cast(volume > 8725 ? static_cast(std::log((double)volume) * 11.541561 + (0.5 - 104.22845)) : 0)); // The incorrect formula below: SOLVE(V=127^3 * (2^(A/63)-1), A) //Touch_Real(c, volume>11210 ? 91.61112 * std::log(4.8819E-7*volume + 1.0)+0.5 : 0); } +#else + ADL_UNUSED(c); + ADL_UNUSED(volume); +#endif } ADLMIDI_EXPORT void AdlInstrumentTester::DoNote(int note) { +#ifndef DISABLE_EMBEDDED_BANKS MIDIplay *play = P->play; OPL3 *opl = P->opl; if(P->adl_ins_list.empty()) FindAdlList(); const unsigned meta = P->adl_ins_list[P->ins_idx]; - const adlinsdata2 ains(adlins[meta]); + const adlinsdata2 ains = adlinsdata2::from_adldata(::adlins[meta]); int tone = (P->cur_gm & 128) ? (P->cur_gm & 127) : (note + 50); if(ains.tone) @@ -2630,7 +1923,7 @@ ADLMIDI_EXPORT void AdlInstrumentTester::DoNote(int note) } double hertz = 172.00093 * std::exp(0.057762265 * (tone + 0.0)); int32_t adlchannel[2] = { 0, 3 }; - if(ains.adl[0] == ains.adl[1]) + if((ains.flags & (adlinsdata::Flag_Pseudo4op|adlinsdata::Flag_Real4op)) == 0) { adlchannel[1] = -1; adlchannel[0] = 6; // single-op @@ -2649,31 +1942,39 @@ ADLMIDI_EXPORT void AdlInstrumentTester::DoNote(int note) } } - opl->NoteOff(0); - opl->NoteOff(3); - opl->NoteOff(6); + opl->noteOff(0); + opl->noteOff(3); + opl->noteOff(6); for(unsigned c = 0; c < 2; ++c) { if(adlchannel[c] < 0) continue; - opl->Patch((uint16_t)adlchannel[c], ains.adl[c]); - opl->Touch_Real((uint16_t)adlchannel[c], 127 * 127 * 100); - opl->Pan((uint16_t)adlchannel[c], 0x30); - opl->NoteOn((uint16_t)adlchannel[c], hertz); + opl->setPatch(static_cast(adlchannel[c]), ains.adl[c]); + opl->touchNote(static_cast(adlchannel[c]), 63); + opl->setPan(static_cast(adlchannel[c]), 0x30); + opl->noteOn(static_cast(adlchannel[c]), static_cast(adlchannel[1]), hertz); } +#else + ADL_UNUSED(note); +#endif } ADLMIDI_EXPORT void AdlInstrumentTester::NextGM(int offset) { +#ifndef DISABLE_EMBEDDED_BANKS P->cur_gm = (P->cur_gm + 256 + (uint32_t)offset) & 0xFF; FindAdlList(); +#else + ADL_UNUSED(offset); +#endif } ADLMIDI_EXPORT void AdlInstrumentTester::NextAdl(int offset) { +#ifndef DISABLE_EMBEDDED_BANKS //OPL3 *opl = P->opl; if(P->adl_ins_list.empty()) FindAdlList(); const unsigned NumBanks = (unsigned)adl_getBanksCount(); - P->ins_idx = (uint32_t)((int32_t)P->ins_idx + (int32_t)P->adl_ins_list.size() + offset) % P->adl_ins_list.size(); + P->ins_idx = (uint32_t)((int32_t)P->ins_idx + (int32_t)P->adl_ins_list.size() + offset) % (int32_t)P->adl_ins_list.size(); #if 0 UI.Color(15); @@ -2686,10 +1987,10 @@ ADLMIDI_EXPORT void AdlInstrumentTester::NextAdl(int offset) std::fflush(stderr); #endif - for(unsigned a = 0, n = (unsigned)P->adl_ins_list.size(); a < n; ++a) + for(size_t a = 0, n = P->adl_ins_list.size(); a < n; ++a) { const unsigned i = P->adl_ins_list[a]; - const adlinsdata2 ains(adlins[i]); + const adlinsdata2 ains = adlinsdata2::from_adldata(::adlins[i]); char ToneIndication[8] = " "; if(ains.tone) @@ -2704,7 +2005,7 @@ ADLMIDI_EXPORT void AdlInstrumentTester::NextAdl(int offset) } std::printf("%s%s%s%u\t", ToneIndication, - ains.adl[0] != ains.adl[1] ? "[2]" : " ", + (ains.flags & (adlinsdata::Flag_Pseudo4op|adlinsdata::Flag_Real4op)) ? "[2]" : " ", (P->ins_idx == a) ? "->" : "\t", i ); @@ -2715,10 +2016,14 @@ ADLMIDI_EXPORT void AdlInstrumentTester::NextAdl(int offset) std::printf("\n"); } +#else + ADL_UNUSED(offset); +#endif } ADLMIDI_EXPORT bool AdlInstrumentTester::HandleInputChar(char ch) { +#ifndef DISABLE_EMBEDDED_BANKS static const char notes[] = "zsxdcvgbhnjmq2w3er5t6y7ui9o0p"; // c'd'ef'g'a'bC'D'EF'G'A'Bc'd'e switch(ch) @@ -2753,6 +2058,9 @@ ADLMIDI_EXPORT bool AdlInstrumentTester::HandleInputChar(char ch) if(p && *p) DoNote((int)(p - notes) - 12); } +#else + ADL_UNUSED(ch); +#endif return true; } diff --git a/src/sound/adlmidi/adlmidi_opl3.cpp b/src/sound/adlmidi/adlmidi_opl3.cpp index 5d15d002c..f3672d3a2 100644 --- a/src/sound/adlmidi/adlmidi_opl3.cpp +++ b/src/sound/adlmidi/adlmidi_opl3.cpp @@ -22,6 +22,8 @@ */ #include "adlmidi_private.hpp" +#include +#include #ifdef ADLMIDI_HW_OPL static const unsigned OPLBase = 0x388; @@ -42,32 +44,48 @@ static const unsigned OPLBase = 0x388; # endif #endif -#ifdef DISABLE_EMBEDDED_BANKS -/* - Dummy data which replaces adldata.cpp banks database -*/ +static const unsigned adl_emulatorSupport = 0 +#ifndef ADLMIDI_HW_OPL +# ifndef ADLMIDI_DISABLE_NUKED_EMULATOR + | (1u << ADLMIDI_EMU_NUKED) | (1u << ADLMIDI_EMU_NUKED_174) +# endif -const struct adldata adl[] = -{ - {0, 0, (unsigned char)'\0', (unsigned char)'\0', (unsigned char)'\0', 0} -}; +# ifndef ADLMIDI_DISABLE_DOSBOX_EMULATOR + | (1u << ADLMIDI_EMU_DOSBOX) +# endif +#endif +; -const struct adlinsdata adlins[] = +//! Check emulator availability +bool adl_isEmulatorAvailable(int emulator) { - {0, 0, 0, 0, 0, 0, 0.0} -}; - -int maxAdlBanks() -{ - return 0; + return (adl_emulatorSupport & (1u << (unsigned)emulator)) != 0; } -const unsigned short banks[][256] = {{0}}; -const char *const banknames[] = {""}; -const AdlBankSetup adlbanksetup[] = {{0, 1, 1, 0, 0}}; -#endif +//! Find highest emulator +int adl_getHighestEmulator() +{ + int emu = -1; + for(unsigned m = adl_emulatorSupport; m > 0; m >>= 1) + ++emu; + return emu; +} -static const unsigned short Operators[23 * 2] = +//! Find lowest emulator +int adl_getLowestEmulator() +{ + int emu = -1; + unsigned m = adl_emulatorSupport; + if(m > 0) + { + for(emu = 0; (m & 1) == 0; m >>= 1) + ++emu; + } + return emu; +} + +//! Per-channel and per-operator registers map +static const uint16_t g_operatorsMap[23 * 2] = { // Channels 0-2 0x000, 0x003, 0x001, 0x004, 0x002, 0x005, // operators 0, 3, 1, 4, 2, 5 @@ -91,7 +109,8 @@ static const unsigned short Operators[23 * 2] = 0x011, 0xFFF }; // operator 13 -static const unsigned short Channels[23] = +//! Channel map to regoster offsets +static const uint16_t g_channelsMap[23] = { 0x000, 0x001, 0x002, 0x003, 0x004, 0x005, 0x006, 0x007, 0x008, // 0..8 0x100, 0x101, 0x102, 0x103, 0x104, 0x105, 0x106, 0x107, 0x108, // 9..17 (secondary set) @@ -129,29 +148,6 @@ static const unsigned short Channels[23] = Ports: ??? */ -void OPL3::setEmbeddedBank(unsigned int bank) -{ - AdlBank = bank; - //Embedded banks are supports 128:128 GM set only - dynamic_banks.clear(); - - if(bank >= static_cast(maxAdlBanks())) - return; - - Bank *bank_pair[2] = - { - &dynamic_banks[0], - &dynamic_banks[PercussionTag] - }; - - for(unsigned i = 0; i < 256; ++i) - { - size_t meta = banks[bank][i]; - adlinsdata2 &ins = bank_pair[i / 128]->ins[i % 128]; - ins = adlinsdata2(adlins[meta]); - } -} - static adlinsdata2 makeEmptyInstrument() { adlinsdata2 ins; @@ -160,126 +156,239 @@ static adlinsdata2 makeEmptyInstrument() return ins; } -const adlinsdata2 OPL3::emptyInstrument = makeEmptyInstrument(); +const adlinsdata2 OPL3::m_emptyInstrument = makeEmptyInstrument(); OPL3::OPL3() : - NumCards(1), - NumFourOps(0), - HighTremoloMode(false), - HighVibratoMode(false), - AdlPercussionMode(false), + m_numChips(1), + m_numFourOps(0), + m_deepTremoloMode(false), + m_deepVibratoMode(false), + m_rhythmMode(false), + m_softPanning(false), m_musicMode(MODE_MIDI), m_volumeScale(VOLUME_Generic) { + m_insBankSetup.volumeModel = OPL3::VOLUME_Generic; + m_insBankSetup.deepTremolo = false; + m_insBankSetup.deepVibrato = false; + m_insBankSetup.adLibPercussions = false; + m_insBankSetup.scaleModulators = false; + #ifdef DISABLE_EMBEDDED_BANKS - AdlBank = ~0u; + m_embeddedBank = CustomBankTag; #else setEmbeddedBank(0); #endif } -void OPL3::Poke(size_t card, uint16_t index, uint8_t value) +bool OPL3::setupLocked() { - #ifdef ADLMIDI_HW_OPL - (void)card; - unsigned o = index >> 8; + return (m_musicMode == MODE_CMF || + m_musicMode == MODE_IMF || + m_musicMode == MODE_RSXX); +} + +void OPL3::setEmbeddedBank(uint32_t bank) +{ +#ifndef DISABLE_EMBEDDED_BANKS + m_embeddedBank = bank; + //Embedded banks are supports 128:128 GM set only + m_insBanks.clear(); + + if(bank >= static_cast(maxAdlBanks())) + return; + + Bank *bank_pair[2] = + { + &m_insBanks[0], + &m_insBanks[PercussionTag] + }; + + for(unsigned i = 0; i < 256; ++i) + { + size_t meta = banks[bank][i]; + adlinsdata2 &ins = bank_pair[i / 128]->ins[i % 128]; + ins = adlinsdata2::from_adldata(::adlins[meta]); + } +#else + ADL_UNUSED(bank); +#endif +} + +void OPL3::writeReg(size_t chip, uint16_t address, uint8_t value) +{ +#ifdef ADLMIDI_HW_OPL + ADL_UNUSED(chip); + unsigned o = address >> 8; unsigned port = OPLBase + o * 2; #ifdef __DJGPP__ - outportb(port, index); + outportb(port, address); for(unsigned c = 0; c < 6; ++c) inportb(port); outportb(port + 1, value); for(unsigned c = 0; c < 35; ++c) inportb(port); #endif #ifdef __WATCOMC__ - outp(port, index); + outp(port, address); for(uint16_t c = 0; c < 6; ++c) inp(port); outp(port + 1, value); for(uint16_t c = 0; c < 35; ++c) inp(port); #endif//__WATCOMC__ - #else - cardsOP2[card]->writeReg(index, value); - #endif +#else//ADLMIDI_HW_OPL + m_chips[chip]->writeReg(address, value); +#endif +} + +void OPL3::writeRegI(size_t chip, uint32_t address, uint32_t value) +{ +#ifdef ADLMIDI_HW_OPL + writeReg(chip, static_cast(address), static_cast(value)); +#else//ADLMIDI_HW_OPL + m_chips[chip]->writeReg(static_cast(address), static_cast(value)); +#endif +} + +void OPL3::writePan(size_t chip, uint32_t address, uint32_t value) +{ +#ifndef ADLMIDI_HW_OPL + m_chips[chip]->writePan(static_cast(address), static_cast(value)); +#else + ADL_UNUSED(chip); + ADL_UNUSED(address); + ADL_UNUSED(value); +#endif } -void OPL3::NoteOff(size_t c) +void OPL3::noteOff(size_t c) { - size_t card = c / 23, cc = c % 23; + size_t chip = c / 23, cc = c % 23; if(cc >= 18) { - regBD[card] &= ~(0x10 >> (cc - 18)); - Poke(card, 0xBD, regBD[card]); + m_regBD[chip] &= ~(0x10 >> (cc - 18)); + writeRegI(chip, 0xBD, m_regBD[chip]); return; } - Poke(card, 0xB0 + Channels[cc], pit[c] & 0xDF); + writeRegI(chip, 0xB0 + g_channelsMap[cc], m_keyBlockFNumCache[c] & 0xDF); } -void OPL3::NoteOn(unsigned c, double hertz) // Hertz range: 0..131071 +void OPL3::noteOn(size_t c1, size_t c2, double hertz) // Hertz range: 0..131071 { - unsigned card = c / 23, cc = c % 23; - unsigned x = 0x2000; + size_t chip = c1 / 23, cc1 = c1 % 23, cc2 = c2 % 23; + uint32_t octave = 0, ftone = 0, mul_offset = 0; - if(hertz < 0 || hertz > 131071) // Avoid infinite loop + if(hertz < 0) return; - while(hertz >= 1023.5) + //Basic range until max of octaves reaching + while((hertz >= 1023.5) && (octave < 0x1C00)) { hertz /= 2.0; // Calculate octave - x += 0x400; + octave += 0x400; + } + //Extended range, rely on frequency multiplication increment + while(hertz >= 1022.75) + { + hertz /= 2.0; // Calculate octave + mul_offset++; } - x += static_cast(hertz + 0.5); - unsigned chn = Channels[cc]; + ftone = octave + static_cast(hertz + 0.5); + uint32_t chn = g_channelsMap[cc1]; + const adldata &patch1 = m_insCache[c1]; + const adldata &patch2 = m_insCache[c2 < m_insCache.size() ? c2 : 0]; - if(cc >= 18) + if(cc1 < 18) { - regBD[card] |= (0x10 >> (cc - 18)); - Poke(card, 0x0BD, regBD[card]); - x &= ~0x2000u; - //x |= 0x800; // for test + ftone += 0x2000u; /* Key-ON [KON] */ + + const bool natural_4op = (m_channelCategory[c1] == ChanCat_4op_Master); + const size_t opsCount = natural_4op ? 4 : 2; + const uint16_t op_addr[4] = + { + g_operatorsMap[cc1 * 2 + 0], g_operatorsMap[cc1 * 2 + 1], + g_operatorsMap[cc2 * 2 + 0], g_operatorsMap[cc2 * 2 + 1] + }; + const uint32_t ops[4] = + { + patch1.modulator_E862 & 0xFF, + patch1.carrier_E862 & 0xFF, + patch2.modulator_E862 & 0xFF, + patch2.carrier_E862 & 0xFF + }; + + for(size_t op = 0; op < opsCount; op++) + { + if((op > 0) && (op_addr[op] == 0xFFF)) + break; + if(mul_offset > 0) + { + uint32_t dt = ops[op] & 0xF0; + uint32_t mul = ops[op] & 0x0F; + if((mul + mul_offset) > 0x0F) + { + mul_offset = 0; + mul = 0x0F; + } + writeRegI(chip, 0x20 + op_addr[op], (dt | (mul + mul_offset)) & 0xFF); + } + else + { + writeRegI(chip, 0x20 + op_addr[op], ops[op] & 0xFF); + } + } } if(chn != 0xFFF) { - Poke(card, 0xA0 + chn, x & 0xFF); - Poke(card, 0xB0 + chn, pit[c] = static_cast(x >> 8)); + writeRegI(chip , 0xA0 + chn, (ftone & 0xFF)); + writeRegI(chip , 0xB0 + chn, (ftone >> 8)); + m_keyBlockFNumCache[c1] = (ftone >> 8); + } + + if(cc1 >= 18) + { + m_regBD[chip ] |= (0x10 >> (cc1 - 18)); + writeRegI(chip , 0x0BD, m_regBD[chip ]); + //x |= 0x800; // for test } } -void OPL3::Touch_Real(unsigned c, unsigned volume, uint8_t brightness) +void OPL3::touchNote(size_t c, uint8_t volume, uint8_t brightness) { if(volume > 63) volume = 63; - size_t card = c / 23, cc = c % 23; - const adldata &adli = ins[c]; - uint16_t o1 = Operators[cc * 2 + 0]; - uint16_t o2 = Operators[cc * 2 + 1]; + size_t chip = c / 23, cc = c % 23; + const adldata &adli = m_insCache[c]; + uint16_t o1 = g_operatorsMap[cc * 2 + 0]; + uint16_t o2 = g_operatorsMap[cc * 2 + 1]; uint8_t x = adli.modulator_40, y = adli.carrier_40; - uint16_t mode = 1; // 2-op AM + uint32_t mode = 1; // 2-op AM - if(four_op_category[c] == 0 || four_op_category[c] == 3) + if(m_channelCategory[c] == ChanCat_Regular || + m_channelCategory[c] == ChanCat_Rhythm_Bass) { mode = adli.feedconn & 1; // 2-op FM or 2-op AM } - else if(four_op_category[c] == 1 || four_op_category[c] == 2) + else if(m_channelCategory[c] == ChanCat_4op_Master || + m_channelCategory[c] == ChanCat_4op_Slave) { const adldata *i0, *i1; - if(four_op_category[c] == 1) + if(m_channelCategory[c] == ChanCat_4op_Master) { i0 = &adli; - i1 = &ins[c + 3]; + i1 = &m_insCache[c + 3]; mode = 2; // 4-op xx-xx ops 1&2 } else { - i0 = &ins[c - 3]; + i0 = &m_insCache[c - 3]; i1 = &adli; mode = 6; // 4-op xx-xx ops 3&4 } @@ -303,14 +412,14 @@ void OPL3::Touch_Real(unsigned c, unsigned volume, uint8_t brightness) if(m_musicMode == MODE_RSXX) { - Poke(card, 0x40 + o1, x); + writeRegI(chip, 0x40 + o1, x); if(o2 != 0xFFF) - Poke(card, 0x40 + o2, y - volume / 2); + writeRegI(chip, 0x40 + o2, y - volume / 2); } else { - bool do_modulator = do_ops[ mode ][ 0 ] || ScaleModulators; - bool do_carrier = do_ops[ mode ][ 1 ] || ScaleModulators; + bool do_modulator = do_ops[ mode ][ 0 ] || m_scaleModulators; + bool do_carrier = do_ops[ mode ][ 1 ] || m_scaleModulators; uint32_t modulator = do_modulator ? (x | 63) - volume + volume * (x & 63) / 63 : x; uint32_t carrier = do_carrier ? (y | 63) - volume + volume * (y & 63) / 63 : y; @@ -324,9 +433,9 @@ void OPL3::Touch_Real(unsigned c, unsigned volume, uint8_t brightness) carrier = (carrier | 63) - brightness + brightness * (carrier & 63) / 63; } - Poke(card, 0x40 + o1, modulator); + writeRegI(chip, 0x40 + o1, modulator); if(o2 != 0xFFF) - Poke(card, 0x40 + o2, carrier); + writeRegI(chip, 0x40 + o2, carrier); } // Correct formula (ST3, AdPlug): @@ -351,70 +460,99 @@ void OPL3::Touch(unsigned c, unsigned volume) // Volume maxes at 127*127*127 } }*/ -void OPL3::Patch(uint16_t c, const adldata &adli) +void OPL3::setPatch(size_t c, const adldata &instrument) { - uint16_t card = c / 23, cc = c % 23; + size_t chip = c / 23, cc = c % 23; static const uint8_t data[4] = {0x20, 0x60, 0x80, 0xE0}; - ins[c] = adli; - uint16_t o1 = Operators[cc * 2 + 0]; - uint16_t o2 = Operators[cc * 2 + 1]; - unsigned x = adli.modulator_E862, y = adli.carrier_E862; + m_insCache[c] = instrument; + uint16_t o1 = g_operatorsMap[cc * 2 + 0]; + uint16_t o2 = g_operatorsMap[cc * 2 + 1]; + unsigned x = instrument.modulator_E862, y = instrument.carrier_E862; - for(unsigned a = 0; a < 4; ++a, x >>= 8, y >>= 8) + for(size_t a = 0; a < 4; ++a, x >>= 8, y >>= 8) { - Poke(card, data[a] + o1, x & 0xFF); + writeRegI(chip, data[a] + o1, x & 0xFF); if(o2 != 0xFFF) - Poke(card, data[a] + o2, y & 0xFF); + writeRegI(chip, data[a] + o2, y & 0xFF); } } -void OPL3::Pan(unsigned c, unsigned value) +void OPL3::setPan(size_t c, uint8_t value) { - unsigned card = c / 23, cc = c % 23; - - if(Channels[cc] != 0xFFF) - Poke(card, 0xC0 + Channels[cc], ins[c].feedconn | value); -} - -void OPL3::Silence() // Silence all OPL channels. -{ - for(unsigned c = 0; c < NumChannels; ++c) + size_t chip = c / 23, cc = c % 23; + if(g_channelsMap[cc] != 0xFFF) { - NoteOff(c); - Touch_Real(c, 0); - } -} - -void OPL3::updateFlags() -{ - unsigned fours = NumFourOps; - - for(unsigned card = 0; card < NumCards; ++card) - { - Poke(card, 0x0BD, regBD[card] = (HighTremoloMode * 0x80 - + HighVibratoMode * 0x40 - + AdlPercussionMode * 0x20)); - unsigned fours_this_card = std::min(fours, 6u); - Poke(card, 0x104, (1 << fours_this_card) - 1); - fours -= fours_this_card; - } - - // Mark all channels that are reserved for four-operator function - if(AdlPercussionMode == 1) - for(unsigned a = 0; a < NumCards; ++a) +#ifndef ADLMIDI_HW_OPL + if (m_softPanning) { - for(unsigned b = 0; b < 5; ++b) - four_op_category[a * 23 + 18 + b] = static_cast(b + 3); - for(unsigned b = 0; b < 3; ++b) - four_op_category[a * 23 + 6 + b] = 8; + writePan(chip, g_channelsMap[cc], value); + writeRegI(chip, 0xC0 + g_channelsMap[cc], m_insCache[c].feedconn | OPL_PANNING_BOTH); } + else + { +#endif + int panning = 0; + if(value < 64 + 32) panning |= OPL_PANNING_LEFT; + if(value >= 64 - 32) panning |= OPL_PANNING_RIGHT; + writePan(chip, g_channelsMap[cc], 64); + writeRegI(chip, 0xC0 + g_channelsMap[cc], m_insCache[c].feedconn | panning); +#ifndef ADLMIDI_HW_OPL + } +#endif + } +} - unsigned nextfour = 0; - - for(unsigned a = 0; a < NumFourOps; ++a) +void OPL3::silenceAll() // Silence all OPL channels. +{ + for(size_t c = 0; c < m_numChannels; ++c) { - four_op_category[nextfour ] = 1; - four_op_category[nextfour + 3] = 2; + noteOff(c); + touchNote(c, 0); + } +} + +void OPL3::updateChannelCategories() +{ + const uint32_t fours = m_numFourOps; + + for(uint32_t chip = 0, fours_left = fours; chip < m_numChips; ++chip) + { + m_regBD[chip] = (m_deepTremoloMode * 0x80 + m_deepVibratoMode * 0x40 + m_rhythmMode * 0x20); + writeRegI(chip, 0x0BD, m_regBD[chip]); + uint32_t fours_this_chip = std::min(fours_left, static_cast(6u)); + writeRegI(chip, 0x104, (1 << fours_this_chip) - 1); + fours_left -= fours_this_chip; + } + + if(!m_rhythmMode) + { + for(size_t a = 0, n = m_numChips; a < n; ++a) + { + for(size_t b = 0; b < 23; ++b) + { + m_channelCategory[a * 23 + b] = + (b >= 18) ? ChanCat_Rhythm_Slave : ChanCat_Regular; + } + } + } + else + { + for(size_t a = 0, n = m_numChips; a < n; ++a) + { + for(size_t b = 0; b < 23; ++b) + { + m_channelCategory[a * 23 + b] = + (b >= 18) ? static_cast(ChanCat_Rhythm_Bass + (b - 18)) : + (b >= 6 && b < 9) ? ChanCat_Rhythm_Slave : ChanCat_Regular; + } + } + } + + uint32_t nextfour = 0; + for(uint32_t a = 0; a < fours; ++a) + { + m_channelCategory[nextfour] = ChanCat_4op_Master; + m_channelCategory[nextfour + 3] = ChanCat_4op_Slave; switch(a % 6) { @@ -434,19 +572,45 @@ void OPL3::updateFlags() break; } } + +/**/ +/* + In two-op mode, channels 0..8 go as follows: + Op1[port] Op2[port] + Channel 0: 00 00 03 03 + Channel 1: 01 01 04 04 + Channel 2: 02 02 05 05 + Channel 3: 06 08 09 0B + Channel 4: 07 09 10 0C + Channel 5: 08 0A 11 0D + Channel 6: 12 10 15 13 + Channel 7: 13 11 16 14 + Channel 8: 14 12 17 15 + In four-op mode, channels 0..8 go as follows: + Op1[port] Op2[port] Op3[port] Op4[port] + Channel 0: 00 00 03 03 06 08 09 0B + Channel 1: 01 01 04 04 07 09 10 0C + Channel 2: 02 02 05 05 08 0A 11 0D + Channel 3: CHANNEL 0 SLAVE + Channel 4: CHANNEL 1 SLAVE + Channel 5: CHANNEL 2 SLAVE + Channel 6: 12 10 15 13 + Channel 7: 13 11 16 14 + Channel 8: 14 12 17 15 + Same goes principally for channels 9-17 respectively. + */ } -void OPL3::updateDeepFlags() +void OPL3::commitDeepFlags() { - for(unsigned card = 0; card < NumCards; ++card) + for(size_t chip = 0; chip < m_numChips; ++chip) { - Poke(card, 0x0BD, regBD[card] = (HighTremoloMode * 0x80 - + HighVibratoMode * 0x40 - + AdlPercussionMode * 0x20)); + m_regBD[chip] = (m_deepTremoloMode * 0x80 + m_deepVibratoMode * 0x40 + m_rhythmMode * 0x20); + writeRegI(chip, 0x0BD, m_regBD[chip]); } } -void OPL3::ChangeVolumeRangesModel(ADLMIDI_VolumeModels volumeModel) +void OPL3::setVolumeScaleModel(ADLMIDI_VolumeModels volumeModel) { switch(volumeModel) { @@ -475,19 +639,37 @@ void OPL3::ChangeVolumeRangesModel(ADLMIDI_VolumeModels volumeModel) } } -#ifndef ADLMIDI_HW_OPL -void OPL3::ClearChips() +ADLMIDI_VolumeModels OPL3::getVolumeScaleModel() { - for(size_t i = 0; i < cardsOP2.size(); i++) - cardsOP2[i].reset(NULL); - cardsOP2.clear(); + switch(m_volumeScale) + { + default: + case OPL3::VOLUME_Generic: + return ADLMIDI_VolumeModel_Generic; + case OPL3::VOLUME_NATIVE: + return ADLMIDI_VolumeModel_NativeOPL3; + case OPL3::VOLUME_DMX: + return ADLMIDI_VolumeModel_DMX; + case OPL3::VOLUME_APOGEE: + return ADLMIDI_VolumeModel_APOGEE; + case OPL3::VOLUME_9X: + return ADLMIDI_VolumeModel_9X; + } +} + +#ifndef ADLMIDI_HW_OPL +void OPL3::clearChips() +{ + for(size_t i = 0; i < m_chips.size(); i++) + m_chips[i].reset(NULL); + m_chips.clear(); } #endif -void OPL3::Reset(int emulator, unsigned long PCM_RATE, void *audioTickHandler) +void OPL3::reset(int emulator, unsigned long PCM_RATE, void *audioTickHandler) { #ifndef ADLMIDI_HW_OPL - ClearChips(); + clearChips(); #else (void)emulator; (void)PCM_RATE; @@ -495,24 +677,27 @@ void OPL3::Reset(int emulator, unsigned long PCM_RATE, void *audioTickHandler) #if !defined(ADLMIDI_AUDIO_TICK_HANDLER) (void)audioTickHandler; #endif - ins.clear(); - pit.clear(); - regBD.clear(); + m_insCache.clear(); + m_keyBlockFNumCache.clear(); + m_regBD.clear(); #ifndef ADLMIDI_HW_OPL - cardsOP2.resize(NumCards, AdlMIDI_SPtr()); + m_chips.resize(m_numChips, AdlMIDI_SPtr()); #endif - NumChannels = NumCards * 23; - ins.resize(NumChannels, adl[adlDefaultNumber]); - pit.resize(NumChannels, 0); - regBD.resize(NumCards, 0); - four_op_category.resize(NumChannels, 0); + const struct adldata defaultInsCache = { 0x1557403,0x005B381, 0x49,0x80, 0x4, +0 }; + m_numChannels = m_numChips * 23; + m_insCache.resize(m_numChannels, defaultInsCache); + m_keyBlockFNumCache.resize(m_numChannels, 0); + m_regBD.resize(m_numChips, 0); + m_channelCategory.resize(m_numChannels, 0); - for(unsigned p = 0, a = 0; a < NumCards; ++a) + for(size_t p = 0, a = 0; a < m_numChips; ++a) { - for(unsigned b = 0; b < 18; ++b) four_op_category[p++] = 0; - for(unsigned b = 0; b < 5; ++b) four_op_category[p++] = 8; + for(size_t b = 0; b < 18; ++b) + m_channelCategory[p++] = 0; + for(size_t b = 0; b < 5; ++b) + m_channelCategory[p++] = ChanCat_Rhythm_Slave; } static const uint16_t data[] = @@ -521,15 +706,17 @@ void OPL3::Reset(int emulator, unsigned long PCM_RATE, void *audioTickHandler) 0x105, 0, 0x105, 1, 0x105, 0, // Pulse OPL3 enable 0x001, 32, 0x105, 1 // Enable wave, OPL3 extensions }; - unsigned fours = NumFourOps; +// size_t fours = m_numFourOps; - for(size_t i = 0; i < NumCards; ++i) + for(size_t i = 0; i < m_numChips; ++i) { #ifndef ADLMIDI_HW_OPL OPLChipBase *chip; switch(emulator) { default: + assert(false); + abort(); #ifndef ADLMIDI_DISABLE_NUKED_EMULATOR case ADLMIDI_EMU_NUKED: /* Latest Nuked OPL3 */ chip = new NukedOPL3; @@ -544,91 +731,23 @@ void OPL3::Reset(int emulator, unsigned long PCM_RATE, void *audioTickHandler) break; #endif } - cardsOP2[i].reset(chip); + m_chips[i].reset(chip); chip->setChipId((uint32_t)i); chip->setRate((uint32_t)PCM_RATE); - if(runAtPcmRate) + if(m_runAtPcmRate) chip->setRunningAtPcmRate(true); # if defined(ADLMIDI_AUDIO_TICK_HANDLER) chip->setAudioTickHandlerInstance(audioTickHandler); # endif #endif // ADLMIDI_HW_OPL - for(unsigned a = 0; a < 18; ++a) Poke(i, 0xB0 + Channels[a], 0x00); - for(unsigned a = 0; a < sizeof(data) / sizeof(*data); a += 2) - Poke(i, data[a], static_cast(data[a + 1])); - Poke(i, 0x0BD, regBD[i] = (HighTremoloMode * 0x80 - + HighVibratoMode * 0x40 - + AdlPercussionMode * 0x20)); - unsigned fours_this_card = std::min(fours, 6u); - Poke(i, 0x104, (1 << fours_this_card) - 1); - //fprintf(stderr, "Card %u: %u four-ops.\n", card, fours_this_card); - fours -= fours_this_card; + /* Clean-up channels from any playing junk sounds */ + for(size_t a = 0; a < 18; ++a) + writeRegI(i, 0xB0 + g_channelsMap[a], 0x00); + for(size_t a = 0; a < sizeof(data) / sizeof(*data); a += 2) + writeRegI(i, data[a], (data[a + 1])); } - // Mark all channels that are reserved for four-operator function - if(AdlPercussionMode == 1) - { - for(unsigned a = 0; a < NumCards; ++a) - { - for(unsigned b = 0; b < 5; ++b) four_op_category[a * 23 + 18 + b] = static_cast(b + 3); - for(unsigned b = 0; b < 3; ++b) four_op_category[a * 23 + 6 + b] = 8; - } - } - unsigned nextfour = 0; - - for(unsigned a = 0; a < NumFourOps; ++a) - { - four_op_category[nextfour ] = 1; - four_op_category[nextfour + 3] = 2; - - switch(a % 6) - { - case 0: - case 1: - nextfour += 1; - break; - - case 2: - nextfour += 9 - 2; - break; - - case 3: - case 4: - nextfour += 1; - break; - - case 5: - nextfour += 23 - 9 - 2; - break; - } - } - - /**/ - /* - In two-op mode, channels 0..8 go as follows: - Op1[port] Op2[port] - Channel 0: 00 00 03 03 - Channel 1: 01 01 04 04 - Channel 2: 02 02 05 05 - Channel 3: 06 08 09 0B - Channel 4: 07 09 10 0C - Channel 5: 08 0A 11 0D - Channel 6: 12 10 15 13 - Channel 7: 13 11 16 14 - Channel 8: 14 12 17 15 - In four-op mode, channels 0..8 go as follows: - Op1[port] Op2[port] Op3[port] Op4[port] - Channel 0: 00 00 03 03 06 08 09 0B - Channel 1: 01 01 04 04 07 09 10 0C - Channel 2: 02 02 05 05 08 0A 11 0D - Channel 3: CHANNEL 0 SLAVE - Channel 4: CHANNEL 1 SLAVE - Channel 5: CHANNEL 2 SLAVE - Channel 6: 12 10 15 13 - Channel 7: 13 11 16 14 - Channel 8: 14 12 17 15 - Same goes principally for channels 9-17 respectively. - */ - Silence(); + updateChannelCategories(); + silenceAll(); } diff --git a/src/sound/adlmidi/adlmidi_private.cpp b/src/sound/adlmidi/adlmidi_private.cpp index 1ee7c4a33..4e8e488af 100644 --- a/src/sound/adlmidi/adlmidi_private.cpp +++ b/src/sound/adlmidi/adlmidi_private.cpp @@ -27,53 +27,58 @@ std::string ADLMIDI_ErrorString; // Generator callback on audio rate ticks +#if defined(ADLMIDI_AUDIO_TICK_HANDLER) void adl_audioTickHandler(void *instance, uint32_t chipId, uint32_t rate) { reinterpret_cast(instance)->AudioTick(chipId, rate); } +#endif -int adlRefreshNumCards(ADL_MIDIPlayer *device) +int adlCalculateFourOpChannels(MIDIplay *play, bool silent) { - unsigned n_fourop[2] = {0, 0}, n_total[2] = {0, 0}; - MIDIplay *play = reinterpret_cast(device->adl_midiPlayer); + size_t n_fourop[2] = {0, 0}, n_total[2] = {0, 0}; //Automatically calculate how much 4-operator channels is necessary - if(play->opl.AdlBank == ~0u) +#ifndef DISABLE_EMBEDDED_BANKS + if(play->m_synth.m_embeddedBank == OPL3::CustomBankTag) +#endif { //For custom bank - OPL3::BankMap::iterator it = play->opl.dynamic_banks.begin(); - OPL3::BankMap::iterator end = play->opl.dynamic_banks.end(); + OPL3::BankMap::iterator it = play->m_synth.m_insBanks.begin(); + OPL3::BankMap::iterator end = play->m_synth.m_insBanks.end(); for(; it != end; ++it) { - uint16_t bank = it->first; - unsigned div = (bank & OPL3::PercussionTag) ? 1 : 0; - for(unsigned i = 0; i < 128; ++i) + size_t bank = it->first; + size_t div = (bank & OPL3::PercussionTag) ? 1 : 0; + for(size_t i = 0; i < 128; ++i) { adlinsdata2 &ins = it->second.ins[i]; if(ins.flags & adlinsdata::Flag_NoSound) continue; - if((ins.adl[0] != ins.adl[1]) && ((ins.flags & adlinsdata::Flag_Pseudo4op) == 0)) + if((ins.flags & adlinsdata::Flag_Real4op) != 0) ++n_fourop[div]; ++n_total[div]; } } } +#ifndef DISABLE_EMBEDDED_BANKS else { //For embedded bank - for(unsigned a = 0; a < 256; ++a) + for(size_t a = 0; a < 256; ++a) { - unsigned insno = banks[play->m_setup.AdlBank][a]; + size_t insno = banks[play->m_setup.bankId][a]; if(insno == 198) continue; ++n_total[a / 128]; - adlinsdata2 ins(adlins[insno]); - if(ins.flags & adlinsdata::Flag_Real4op) + adlinsdata2 ins = adlinsdata2::from_adldata(::adlins[insno]); + if((ins.flags & adlinsdata::Flag_Real4op) != 0) ++n_fourop[a / 128]; } } +#endif - unsigned numFourOps = 0; + size_t numFourOps = 0; // All 2ops (no 4ops) if((n_fourop[0] == 0) && (n_fourop[1] == 0)) @@ -94,7 +99,10 @@ int adlRefreshNumCards(ADL_MIDIPlayer *device) : (play->m_setup.NumCards == 1 ? 1 : play->m_setup.NumCards * 4); */ - play->opl.NumFourOps = play->m_setup.NumFourOps = (numFourOps * play->m_setup.NumCards); + play->m_synth.m_numFourOps = static_cast(numFourOps * play->m_synth.m_numChips); + // Update channel categories and set up four-operator channels + if(!silent) + play->m_synth.updateChannelCategories(); return 0; } diff --git a/src/sound/adlmidi/adlmidi_private.hpp b/src/sound/adlmidi/adlmidi_private.hpp index 0e63b5a61..59ba555d9 100644 --- a/src/sound/adlmidi/adlmidi_private.hpp +++ b/src/sound/adlmidi/adlmidi_private.hpp @@ -28,18 +28,16 @@ #ifndef ADLMIDI_EXPORT # if defined (_WIN32) && defined(ADLMIDI_BUILD_DLL) # define ADLMIDI_EXPORT __declspec(dllexport) -# elif defined (LIBADLMIDI_VISIBILITY) +# elif defined (LIBADLMIDI_VISIBILITY) && defined (__GNUC__) # define ADLMIDI_EXPORT __attribute__((visibility ("default"))) # else # define ADLMIDI_EXPORT # endif #endif -// Require declarations of unstable API for extern "C" -#define ADLMIDI_UNSTABLE_API #ifdef _WIN32 -#define NOMINMAX +#define NOMINMAX 1 #endif #if defined(_WIN32) && !defined(__WATCOMC__) @@ -51,7 +49,7 @@ typedef __int64 ssize_t; # else typedef __int32 ssize_t; # endif -# define NOMINMAX //Don't override std::min and std::max +# define NOMINMAX 1 //Don't override std::min and std::max # else # ifdef _WIN64 typedef int64_t ssize_t; @@ -84,6 +82,7 @@ typedef int32_t ssize_t; //#else #include #include +#include // nothrow //#endif #include #include @@ -131,16 +130,28 @@ typedef int32_t ssize_t; #define INT32_MAX 0x7fffffff #endif -#include "fraction.hpp" +#include "file_reader.hpp" + +#ifndef ADLMIDI_DISABLE_MIDI_SEQUENCER +// Rename class to avoid ABI collisions +#define BW_MidiSequencer AdlMidiSequencer +#include "midi_sequencer.hpp" +typedef BW_MidiSequencer MidiSequencer; +#endif//ADLMIDI_DISABLE_MIDI_SEQUENCER + #ifndef ADLMIDI_HW_OPL #include "chips/opl_chip_base.h" #endif #include "adldata.hh" + +#define ADLMIDI_BUILD #include "adlmidi.h" //Main API + #ifndef ADLMIDI_DISABLE_CPP_EXTRAS #include "adlmidi.hpp" //Extra C++ API #endif + #include "adlmidi_ptr.hpp" #include "adlmidi_bankmap.h" @@ -150,6 +161,14 @@ typedef int32_t ssize_t; #define OPL_PANNING_RIGHT 0x20 #define OPL_PANNING_BOTH 0x30 +#ifdef ADLMIDI_HW_OPL +#define ADL_MAX_CHIPS 1 +#define ADL_MAX_CHIPS_STR "1" //Why not just "#MaxCards" ? Watcom fails to pass this with "syntax error" :-P +#else +#define ADL_MAX_CHIPS 100 +#define ADL_MAX_CHIPS_STR "100" +#endif + extern std::string ADLMIDI_ErrorString; /* @@ -158,7 +177,7 @@ extern std::string ADLMIDI_ErrorString; template inline Real adl_cvtReal(int32_t x) { - return x * ((Real)1 / INT16_MAX); + return static_cast(x) * (static_cast(1) / static_cast(INT16_MAX)); } inline int32_t adl_cvtS16(int32_t x) @@ -199,104 +218,271 @@ inline int32_t adl_cvtU32(int32_t x) return (uint32_t)adl_cvtS32(x) - (uint32_t)INT32_MIN; } -class MIDIplay; struct ADL_MIDIPlayer; +class MIDIplay; +/** + * @brief OPL3 Chip management class + */ class OPL3 { -public: friend class MIDIplay; friend class AdlInstrumentTester; - uint32_t NumChannels; - char ____padding[4]; - ADL_MIDIPlayer *_parent; -#ifndef ADLMIDI_HW_OPL - std::vector > cardsOP2; -#endif -private: - std::vector ins; // patch data, cached, needed by Touch() - std::vector pit; // value poked to B0, cached, needed by NoteOff)( - std::vector regBD; - - friend int adlRefreshNumCards(ADL_MIDIPlayer *device); + friend int adlCalculateFourOpChannels(MIDIplay *play, bool silent); public: + enum + { + PercussionTag = 1 << 15, + CustomBankTag = 0xFFFFFFFF + }; + + //! Total number of chip channels between all running emulators + uint32_t m_numChannels; + //! Just a padding. Reserved. + char _padding[4]; +#ifndef ADLMIDI_HW_OPL + //! Running chip emulators + std::vector > m_chips; +#endif + +private: + //! Cached patch data, needed by Touch() + std::vector m_insCache; + //! Value written to B0, cached, needed by NoteOff. + /*! Contains Key on/off state, octave block and frequency number values + */ + std::vector m_keyBlockFNumCache; + //! Cached BD registry value (flags register: DeepTremolo, DeepVibrato, and RhythmMode) + std::vector m_regBD; + +public: + /** + * @brief MIDI bank entry + */ struct Bank { + //! MIDI Bank instruments adlinsdata2 ins[128]; }; typedef BasicBankMap BankMap; - BankMap dynamic_banks; - AdlBankSetup dynamic_bank_setup; -public: - void setEmbeddedBank(unsigned int bank); - static const adlinsdata2 emptyInstrument; - enum { PercussionTag = 1 << 15 }; + //! MIDI bank instruments data + BankMap m_insBanks; + //! MIDI bank-wide setup + AdlBankSetup m_insBankSetup; +public: + //! Blank instrument template + static const adlinsdata2 m_emptyInstrument; //! Total number of running concurrent emulated chips - unsigned int NumCards; - //! Currently running embedded bank number. "~0" means usign of the custom bank. - unsigned int AdlBank; + uint32_t m_numChips; + //! Currently running embedded bank number. "CustomBankTag" means usign of the custom bank. + uint32_t m_embeddedBank; //! Total number of needed four-operator channels in all running chips - unsigned int NumFourOps; + uint32_t m_numFourOps; //! Turn global Deep Tremolo mode on - bool HighTremoloMode; + bool m_deepTremoloMode; //! Turn global Deep Vibrato mode on - bool HighVibratoMode; - //! Use AdLib percussion mode - bool AdlPercussionMode; + bool m_deepVibratoMode; + //! Use Rhythm Mode percussions + bool m_rhythmMode; //! Carriers-only are scaled by default by volume level. This flag will tell to scale modulators too. - bool ScaleModulators; + bool m_scaleModulators; //! Run emulator at PCM rate if that possible. Reduces sounding accuracy, but decreases CPU usage on lower rates. - bool runAtPcmRate; - // ! Required to play CMF files. Can be turned on by using of "CMF" volume model - //bool LogarithmicVolumes; //[REPLACED WITH "m_volumeScale == VOLUME_NATIVE", DEPRECATED!!!] - // ! Required to play EA-MUS files [REPLACED WITH "m_musicMode", DEPRECATED!!!] - //bool CartoonersVolumes; + bool m_runAtPcmRate; + //! Enable soft panning + bool m_softPanning; + + //! Just a padding. Reserved. + char _padding2[3]; + + /** + * @brief Music playing mode + */ enum MusicMode { + //! MIDI mode MODE_MIDI, + //! Id-Software Music mode MODE_IMF, + //! Creative Music Files mode MODE_CMF, + //! EA-MUS (a.k.a. RSXX) mode MODE_RSXX } m_musicMode; - //! Just a padding. Reserved. - char ___padding2[3]; - //! Volume models enum + + /** + * @brief Volume models enum + */ enum VolumesScale { + //! Generic volume model (linearization of logarithmic scale) VOLUME_Generic, + //! OPL3 native logarithmic scale VOLUME_NATIVE, + //! DMX volume scale logarithmic table VOLUME_DMX, + //! Apoge Sound System volume scaling model VOLUME_APOGEE, + //! Windows 9x driver volume scale table VOLUME_9X } m_volumeScale; + //! Reserved + char _padding3[8]; + + /** + * @brief Channel categiry enumeration + */ + enum ChanCat + { + //! Regular melodic/percussion channel + ChanCat_Regular = 0, + //! Four-op master + ChanCat_4op_Master = 1, + //! Four-op slave + ChanCat_4op_Slave = 2, + //! Rhythm-mode Bass drum + ChanCat_Rhythm_Bass = 3, + //! Rhythm-mode Snare drum + ChanCat_Rhythm_Snare = 4, + //! Rhythm-mode Tom-Tom + ChanCat_Rhythm_Tom = 5, + //! Rhythm-mode Cymbal + ChanCat_Rhythm_Cymbal = 6, + //! Rhythm-mode Hi-Hat + ChanCat_Rhythm_HiHat = 7, + //! Rhythm-mode Slave channel + ChanCat_Rhythm_Slave = 8 + }; + + //! Category of the channel + /*! 1 = quad-master, 2 = quad-slave, 0 = regular + 3 = percussion BassDrum + 4 = percussion Snare + 5 = percussion Tom + 6 = percussion Crash cymbal + 7 = percussion Hihat + 8 = percussion slave + */ + std::vector m_channelCategory; + + + /** + * @brief C.O. Constructor + */ OPL3(); - char ____padding3[8]; - std::vector four_op_category; // 1 = quad-master, 2 = quad-slave, 0 = regular - // 3 = percussion BassDrum - // 4 = percussion Snare - // 5 = percussion Tom - // 6 = percussion Crash cymbal - // 7 = percussion Hihat - // 8 = percussion slave - void Poke(size_t card, uint16_t index, uint8_t value); + /** + * @brief Checks are setup locked to be changed on the fly or not + * @return true when setup on the fly is locked + */ + bool setupLocked(); - void NoteOff(size_t c); - void NoteOn(unsigned c, double hertz); - void Touch_Real(unsigned c, unsigned volume, uint8_t brightness = 127); - //void Touch(unsigned c, unsigned volume) + /** + * @brief Choose one of embedded banks + * @param bank ID of the bank + */ + void setEmbeddedBank(uint32_t bank); + + /** + * @brief Write data to OPL3 chip register + * @param chip Index of emulated chip. In hardware OPL3 builds, this parameter is ignored + * @param address Register address to write + * @param value Value to write + */ + void writeReg(size_t chip, uint16_t address, uint8_t value); + + /** + * @brief Write data to OPL3 chip register + * @param chip Index of emulated chip. In hardware OPL3 builds, this parameter is ignored + * @param address Register address to write + * @param value Value to write + */ + void writeRegI(size_t chip, uint32_t address, uint32_t value); + + /** + * @brief Write to soft panning control of OPL3 chip emulator + * @param chip Index of emulated chip. + * @param address Register of channel to write + * @param value Value to write + */ + void writePan(size_t chip, uint32_t address, uint32_t value); + + /** + * @brief Off the note in specified chip channel + * @param c Channel of chip (Emulated chip choosing by next formula: [c = ch + (chipId * 23)]) + */ + void noteOff(size_t c); + + /** + * @brief On the note in specified chip channel with specified frequency of the tone + * @param c1 Channel of chip [or master 4-op channel] (Emulated chip choosing by next formula: [c = ch + (chipId * 23)]) + * @param c2 Slave 4-op channel of chip, unused for 2op (Emulated chip choosing by next formula: [c = ch + (chipId * 23)]) + * @param hertz Frequency of the tone in hertzes + */ + void noteOn(size_t c1, size_t c2, double hertz); + + /** + * @brief Change setup of instrument in specified chip channel + * @param c Channel of chip (Emulated chip choosing by next formula: [c = ch + (chipId * 23)]) + * @param volume Volume level (from 0 to 63) + * @param brightness CC74 Brightness level (from 0 to 127) + */ + void touchNote(size_t c, uint8_t volume, uint8_t brightness = 127); + + /** + * @brief Set the instrument into specified chip channel + * @param c Channel of chip (Emulated chip choosing by next formula: [c = ch + (chipId * 23)]) + * @param instrument Instrument data to set into the chip channel + */ + void setPatch(size_t c, const adldata &instrument); + + /** + * @brief Set panpot position + * @param c Channel of chip (Emulated chip choosing by next formula: [c = ch + (chipId * 23)]) + * @param value 3-bit panpot value + */ + void setPan(size_t c, uint8_t value); + + /** + * @brief Shut up all chip channels + */ + void silenceAll(); + + /** + * @brief Commit updated flag states to chip registers + */ + void updateChannelCategories(); + + /** + * @brief commit deepTremolo and deepVibrato flags + */ + void commitDeepFlags(); + + /** + * @brief Set the volume scaling model + * @param volumeModel Type of volume scale model scale + */ + void setVolumeScaleModel(ADLMIDI_VolumeModels volumeModel); + + /** + * @brief Get the volume scaling model + */ + ADLMIDI_VolumeModels getVolumeScaleModel(); - void Patch(uint16_t c, const adldata &adli); - void Pan(unsigned c, unsigned value); - void Silence(); - void updateFlags(); - void updateDeepFlags(); - void ChangeVolumeRangesModel(ADLMIDI_VolumeModels volumeModel); #ifndef ADLMIDI_HW_OPL - void ClearChips(); + /** + * @brief Clean up all running emulated chip instances + */ + void clearChips(); #endif - void Reset(int emulator, unsigned long PCM_RATE, void *audioTickHandler); + + /** + * @brief Reset chip properties and initialize them + * @param emulator Type of chip emulator + * @param PCM_RATE Output sample rate to generate on output + * @param audioTickHandler PCM-accurate clock hook + */ + void reset(int emulator, unsigned long PCM_RATE, void *audioTickHandler); }; @@ -306,17 +492,11 @@ public: struct MIDIEventHooks { MIDIEventHooks() : - onEvent(NULL), - onEvent_userData(NULL), onNote(NULL), onNote_userData(NULL), onDebugMessage(NULL), onDebugMessage_userData(NULL) {} - //! Raw MIDI event hook - typedef void (*RawEventHook)(void *userdata, uint8_t type, uint8_t subtype, uint8_t channel, const uint8_t *data, size_t len); - RawEventHook onEvent; - void *onEvent_userData; //! Note on/off hooks typedef void (*NoteHook)(void *userdata, int adlchn, int note, int ins, int pressure, double bend); @@ -334,207 +514,121 @@ class MIDIplay { friend void adl_reset(struct ADL_MIDIPlayer*); public: - MIDIplay(unsigned long sampleRate = 22050); + explicit MIDIplay(unsigned long sampleRate = 22050); ~MIDIplay() {} void applySetup(); + void partialReset(); + void resetMIDI(); + /**********************Internal structures and classes**********************/ /** - * @brief A little class gives able to read filedata from disk and also from a memory segment + * @brief Persistent settings for each MIDI channel */ - class fileReader - { - public: - enum relTo - { - SET = 0, - CUR = 1, - END = 2 - }; - - fileReader() - { - fp = NULL; - mp = NULL; - mp_size = 0; - mp_tell = 0; - } - ~fileReader() - { - close(); - } - - void openFile(const char *path) - { - #if !defined(_WIN32) || defined(__WATCOMC__) - fp = std::fopen(path, "rb"); - #else - wchar_t widePath[MAX_PATH]; - int size = MultiByteToWideChar(CP_UTF8, 0, path, (int)std::strlen(path), widePath, MAX_PATH); - widePath[size] = '\0'; - fp = _wfopen(widePath, L"rb"); - #endif - _fileName = path; - mp = NULL; - mp_size = 0; - mp_tell = 0; - } - - void openData(const void *mem, size_t lenght) - { - fp = NULL; - mp = mem; - mp_size = lenght; - mp_tell = 0; - } - - void seek(long pos, int rel_to) - { - if(fp) - std::fseek(fp, pos, rel_to); - else - { - switch(rel_to) - { - case SET: - mp_tell = static_cast(pos); - break; - - case END: - mp_tell = mp_size - static_cast(pos); - break; - - case CUR: - mp_tell = mp_tell + static_cast(pos); - break; - } - - if(mp_tell > mp_size) - mp_tell = mp_size; - } - } - - inline void seeku(uint64_t pos, int rel_to) - { - seek(static_cast(pos), rel_to); - } - - size_t read(void *buf, size_t num, size_t size) - { - if(fp) - return std::fread(buf, num, size, fp); - else - { - size_t pos = 0; - size_t maxSize = static_cast(size * num); - - while((pos < maxSize) && (mp_tell < mp_size)) - { - reinterpret_cast(buf)[pos] = reinterpret_cast(mp)[mp_tell]; - mp_tell++; - pos++; - } - - return pos; - } - } - - int getc() - { - if(fp) - return std::getc(fp); - else - { - if(mp_tell >= mp_size) return -1; - int x = reinterpret_cast(mp)[mp_tell]; - mp_tell++; - return x; - } - } - - size_t tell() - { - if(fp) - return static_cast(std::ftell(fp)); - else - return mp_tell; - } - - void close() - { - if(fp) std::fclose(fp); - - fp = NULL; - mp = NULL; - mp_size = 0; - mp_tell = 0; - } - - bool isValid() - { - return (fp) || (mp); - } - - bool eof() - { - if(fp) - return (std::feof(fp) != 0); - else - return mp_tell >= mp_size; - } - std::string _fileName; - std::FILE *fp; - const void *mp; - size_t mp_size; - size_t mp_tell; - }; - - // Persistent settings for each MIDI channel struct MIDIchannel { - uint16_t portamento; - uint8_t bank_lsb, bank_msb; + //! LSB Bank number + uint8_t bank_lsb, + //! MSB Bank number + bank_msb; + //! Current patch number uint8_t patch; - uint8_t volume, expression; - uint8_t panning, vibrato, aftertouch, sustain; + //! Volume level + uint8_t volume, + //! Expression level + expression; + //! Panning level + uint8_t panning, + //! Vibrato level + vibrato, + //! Channel aftertouch level + aftertouch; + //! Portamento time + uint16_t portamento; + //! Is Pedal sustain active + bool sustain; + //! Is Soft pedal active + bool softPedal; + //! Is portamento enabled + bool portamentoEnable; + //! Source note number used by portamento + int8_t portamentoSource; // note number or -1 + //! Portamento rate + double portamentoRate; //! Per note Aftertouch values uint8_t noteAftertouch[128]; //! Is note aftertouch has any non-zero value bool noteAfterTouchInUse; - char ____padding[6]; + //! Reserved + char _padding[6]; + //! Pitch bend value int bend; + //! Pitch bend sensitivity double bendsense; - int bendsense_lsb, bendsense_msb; - double vibpos, vibspeed, vibdepth; - int64_t vibdelay; - uint8_t lastlrpn, lastmrpn; + //! Pitch bend sensitivity LSB value + int bendsense_lsb, + //! Pitch bend sensitivity MSB value + bendsense_msb; + //! Vibrato position value + double vibpos, + //! Vibrato speed value + vibspeed, + //! Vibrato depth value + vibdepth; + //! Vibrato delay time + int64_t vibdelay_us; + //! Last LSB part of RPN value received + uint8_t lastlrpn, + //! Last MSB poart of RPN value received + lastmrpn; + //! Interpret RPN value as NRPN bool nrpn; + //! Brightness level uint8_t brightness; + + //! Is melodic channel turned into percussion bool is_xg_percussion; + + /** + * @brief Per-Note information + */ struct NoteInfo { + //! Note number uint8_t note; + //! Is note active bool active; - // Current pressure + //! Current pressure uint8_t vol; - // Note vibrato (a part of Note Aftertouch feature) + //! Note vibrato (a part of Note Aftertouch feature) uint8_t vibrato; - // Tone selected on noteon: - int16_t tone; - char ____padding2[4]; - // Patch selected on noteon; index to bank.ins[] + //! Tone selected on noteon: + int16_t noteTone; + //! Current tone (!= noteTone if gliding note) + double currentTone; + //! Gliding rate + double glideRate; + //! Patch selected on noteon; index to bank.ins[] size_t midiins; - // Patch selected + //! Is note the percussion instrument + bool isPercussion; + //! Note that plays missing instrument. Doesn't using any chip channels + bool isBlank; + //! Patch selected const adlinsdata2 *ains; enum { MaxNumPhysChans = 2, - MaxNumPhysItemCount = MaxNumPhysChans, + MaxNumPhysItemCount = MaxNumPhysChans }; + + /** + * @brief Reference to currently using chip channel + */ struct Phys { //! Destination chip channel @@ -558,11 +652,12 @@ public: return !operator==(oth); } }; + //! List of OPL3 channels it is currently occupying. Phys chip_channels[MaxNumPhysItemCount]; //! Count of used channels. unsigned chip_channels_count; - // + Phys *phys_find(unsigned chip_chan) { Phys *ph = NULL; @@ -603,20 +698,26 @@ public: phys_erase_at(ph); } }; - char ____padding2[5]; + + //! Reserved + char _padding2[5]; + //! Count of gliding notes in this channel + unsigned gliding_note_count; + + //! Active notes in the channel NoteInfo activenotes[128]; struct activenoteiterator { - explicit activenoteiterator(NoteInfo *info = 0) + explicit activenoteiterator(NoteInfo *info = NULL) : ptr(info) {} activenoteiterator &operator++() { if(ptr->note == 127) - ptr = 0; + ptr = NULL; else for(++ptr; ptr && !ptr->active;) - ptr = (ptr->note == 127) ? 0 : (ptr + 1); + ptr = (ptr->note == 127) ? NULL : (ptr + 1); return *this; } activenoteiterator operator++(int) @@ -649,7 +750,7 @@ public: { assert(note < 128); return activenoteiterator( - activenotes[note].active ? &activenotes[note] : 0); + activenotes[note].active ? &activenotes[note] : NULL); } activenoteiterator activenotes_ensure_find(uint8_t note) @@ -687,6 +788,9 @@ public: } } + /** + * @brief Reset channel into initial state + */ void reset() { resetAllControllers(); @@ -699,6 +803,10 @@ public: nrpn = false; is_xg_percussion = false; } + + /** + * @brief Reset all MIDI controllers into initial state + */ void resetAllControllers() { bend = 0; @@ -707,35 +815,52 @@ public: updateBendSensitivity(); volume = 100; expression = 127; - sustain = 0; + sustain = false; + softPedal = false; vibrato = 0; aftertouch = 0; std::memset(noteAftertouch, 0, 128); noteAfterTouchInUse = false; vibspeed = 2 * 3.141592653 * 5.0; vibdepth = 0.5 / 127; - vibdelay = 0; - panning = OPL_PANNING_BOTH; + vibdelay_us = 0; + panning = 64; portamento = 0; + portamentoEnable = false; + portamentoSource = -1; + portamentoRate = HUGE_VAL; brightness = 127; } + + /** + * @brief Has channel vibrato to process + * @return + */ bool hasVibrato() { return (vibrato > 0) || (aftertouch > 0) || noteAfterTouchInUse; } + + /** + * @brief Commit pitch bend sensitivity value from MSB and LSB + */ void updateBendSensitivity() { int cent = bendsense_msb * 128 + bendsense_lsb; bendsense = cent * (1.0 / (128 * 8192)); } + MIDIchannel() { activenotes_clear(); + gliding_note_count = 0; reset(); } }; - // Additional information about OPL3 channels + /** + * @brief Additional information about OPL3 channels + */ struct AdlChannel { struct Location @@ -751,18 +876,27 @@ public: { LocationData *prev, *next; Location loc; - bool sustained; - char ____padding[7]; + enum { + Sustain_None = 0x00, + Sustain_Pedal = 0x01, + Sustain_Sostenuto = 0x02, + Sustain_ANY = Sustain_Pedal | Sustain_Sostenuto + }; + uint32_t sustained; + char _padding[6]; MIDIchannel::NoteInfo::Phys ins; // a copy of that in phys[] //! Has fixed sustain, don't iterate "on" timeout bool fixed_sustain; //! Timeout until note will be allowed to be killed by channel manager while it is on - int64_t kon_time_until_neglible; - int64_t vibdelay; + int64_t kon_time_until_neglible_us; + int64_t vibdelay_us; }; - // If the channel is keyoff'd - int64_t koff_time_until_neglible; + //! Time left until sounding will be muted after key off + int64_t koff_time_until_neglible_us; + + //! Recently passed instrument, improves a goodness of released but busy channel when matching + MIDIchannel::NoteInfo::Phys recent_ins; enum { users_max = 128 }; LocationData *users_first, *users_free_cells; @@ -779,12 +913,13 @@ public: void users_assign(const LocationData *users, size_t count); // For channel allocation: - AdlChannel(): koff_time_until_neglible(0) + AdlChannel(): koff_time_until_neglible_us(0) { users_clear(); + std::memset(&recent_ins, 0, sizeof(MIDIchannel::NoteInfo::Phys)); } - AdlChannel(const AdlChannel &oth): koff_time_until_neglible(oth.koff_time_until_neglible) + AdlChannel(const AdlChannel &oth): koff_time_until_neglible_us(oth.koff_time_until_neglible_us) { if(oth.users_first) { @@ -797,156 +932,49 @@ public: AdlChannel &operator=(const AdlChannel &oth) { - koff_time_until_neglible = oth.koff_time_until_neglible; + koff_time_until_neglible_us = oth.koff_time_until_neglible_us; users_assign(oth.users_first, oth.users_size); return *this; } - void AddAge(int64_t ms); + /** + * @brief Increases age of active note in microseconds time + * @param us Amount time in microseconds + */ + void addAge(int64_t us); }; #ifndef ADLMIDI_DISABLE_MIDI_SEQUENCER /** - * @brief MIDI Event utility container + * @brief MIDI files player sequencer */ - class MidiEvent - { - public: - MidiEvent(); - - enum Types - { - T_UNKNOWN = 0x00, - T_NOTEOFF = 0x08,//size == 2 - T_NOTEON = 0x09,//size == 2 - T_NOTETOUCH = 0x0A,//size == 2 - T_CTRLCHANGE = 0x0B,//size == 2 - T_PATCHCHANGE = 0x0C,//size == 1 - T_CHANAFTTOUCH = 0x0D,//size == 1 - T_WHEEL = 0x0E,//size == 2 - - T_SYSEX = 0xF0,//size == len - T_SYSCOMSPOSPTR = 0xF2,//size == 2 - T_SYSCOMSNGSEL = 0xF3,//size == 1 - T_SYSEX2 = 0xF7,//size == len - T_SPECIAL = 0xFF - }; - enum SubTypes - { - ST_SEQNUMBER = 0x00,//size == 2 - ST_TEXT = 0x01,//size == len - ST_COPYRIGHT = 0x02,//size == len - ST_SQTRKTITLE = 0x03,//size == len - ST_INSTRTITLE = 0x04,//size == len - ST_LYRICS = 0x05,//size == len - ST_MARKER = 0x06,//size == len - ST_CUEPOINT = 0x07,//size == len - ST_DEVICESWITCH = 0x09,//size == len - ST_MIDICHPREFIX = 0x20,//size == 1 - - ST_ENDTRACK = 0x2F,//size == 0 - ST_TEMPOCHANGE = 0x51,//size == 3 - ST_SMPTEOFFSET = 0x54,//size == 5 - ST_TIMESIGNATURE = 0x55, //size == 4 - ST_KEYSIGNATURE = 0x59,//size == 2 - ST_SEQUENCERSPEC = 0x7F, //size == len - - /* Non-standard, internal ADLMIDI usage only */ - ST_LOOPSTART = 0xE1,//size == 0 - ST_LOOPEND = 0xE2,//size == 0 - ST_RAWOPL = 0xE3//size == 0 - }; - //! Main type of event - uint8_t type; - //! Sub-type of the event - uint8_t subtype; - //! Targeted MIDI channel - uint8_t channel; - //! Is valid event - uint8_t isValid; - //! Reserved 5 bytes padding - uint8_t __padding[4]; - //! Absolute tick position (Used for the tempo calculation only) - uint64_t absPosition; - //! Raw data of this event - std::vector data; - }; + MidiSequencer m_sequencer; /** - * @brief A track position event contains a chain of MIDI events until next delay value - * - * Created with purpose to sort events by type in the same position - * (for example, to keep controllers always first than note on events or lower than note-off events) + * @brief Interface between MIDI sequencer and this library */ - class MidiTrackRow - { - public: - MidiTrackRow(); - void reset(); - //! Absolute time position in seconds - double time; - //! Delay to next event in ticks - uint64_t delay; - //! Absolute position in ticks - uint64_t absPos; - //! Delay to next event in seconds - double timeDelay; - std::vector events; - /** - * @brief Sort events in this position - */ - void sortEvents(bool *noteStates = NULL); - }; + BW_MidiRtInterface m_sequencerInterface; /** - * @brief Tempo change point entry. Used in the MIDI data building function only. + * @brief Initialize MIDI sequencer interface */ - struct TempoChangePoint - { - uint64_t absPos; - fraction tempo; - }; - //P.S. I declared it here instead of local in-function because C++99 can't process templates with locally-declared structures - - typedef std::list MidiTrackQueue; - - // Information about each track - struct PositionNew - { - bool began; - char padding[7]; - double wait; - double absTimePosition; - struct TrackInfo - { - size_t ptr; - uint64_t delay; - int status; - char padding2[4]; - MidiTrackQueue::iterator pos; - TrackInfo(): ptr(0), delay(0), status(0) {} - }; - std::vector track; - PositionNew(): began(false), wait(0.0), absTimePosition(0.0), track() - {} - }; -#endif//ADLMIDI_DISABLE_MIDI_SEQUENCER + void initSequencerInterface(); +#endif //ADLMIDI_DISABLE_MIDI_SEQUENCER struct Setup { int emulator; bool runAtPcmRate; - unsigned int AdlBank; - unsigned int NumFourOps; - unsigned int NumCards; - int HighTremoloMode; - int HighVibratoMode; - int AdlPercussionMode; - bool LogarithmicVolumes; - int VolumeModel; + unsigned int bankId; + int numFourOps; + unsigned int numChips; + int deepTremoloMode; + int deepVibratoMode; + int rhythmMode; + bool logarithmicVolumes; + int volumeScaleModel; //unsigned int SkipForward; - bool loopingIsEnabled; - int ScaleModulators; + int scaleModulators; bool fullRangeBrightnessCC74; double delay; @@ -964,148 +992,140 @@ public: unsigned long PCM_RATE; }; + /** + * @brief MIDI Marker entry + */ struct MIDI_MarkerEntry { + //! Label of marker std::string label; + //! Absolute position in seconds double pos_time; + //! Absolute position in ticks in the track uint64_t pos_ticks; }; - std::vector Ch; - bool cmf_percussion_mode; + //! Available MIDI Channels + std::vector m_midiChannels; + //! CMF Rhythm mode + bool m_cmfPercussionMode; + + //! Master volume, controlled via SysEx + uint8_t m_masterVolume; + + //! SysEx device ID + uint8_t m_sysExDeviceId; + + /** + * @brief MIDI Synthesizer mode + */ + enum SynthMode + { + Mode_GM = 0x00, + Mode_GS = 0x01, + Mode_XG = 0x02, + Mode_GM2 = 0x04 + }; + //! MIDI Synthesizer mode + uint32_t m_synthMode; + + //! Installed function hooks MIDIEventHooks hooks; private: - std::map devices; - std::map current_device; + //! Per-track MIDI devices map + std::map m_midiDevices; + //! Current MIDI device per track + std::map m_currentMidiDevice; - //Padding to fix CLanc code model's warning - char ____padding[7]; + //! Padding to fix CLanc code model's warning + char _padding[7]; - std::vector ch; + //! Chip channels map + std::vector m_chipChannels; //! Counter of arpeggio processing size_t m_arpeggioCounter; +#if defined(ADLMIDI_AUDIO_TICK_HANDLER) //! Audio tick counter uint32_t m_audioTickCounter; - -#ifndef ADLMIDI_DISABLE_MIDI_SEQUENCER - std::vector > TrackData; - - PositionNew CurrentPositionNew, LoopBeginPositionNew, trackBeginPositionNew; - - //! Full song length in seconds - double fullSongTimeLength; - //! Delay after song playd before rejecting the output stream requests - double postSongWaitDelay; - - //! Loop start time - double loopStartTime; - //! Loop end time - double loopEndTime; #endif - //! Local error string - std::string errorString; + //! Local error string std::string errorStringOut; -#ifndef ADLMIDI_DISABLE_MIDI_SEQUENCER - //! Pre-processed track data storage - std::vector trackDataNew; -#endif - //! Missing instruments catches - std::set caugh_missing_instruments; + std::set caugh_missing_instruments; //! Missing melodic banks catches - std::set caugh_missing_banks_melodic; + std::set caugh_missing_banks_melodic; //! Missing percussion banks catches - std::set caugh_missing_banks_percussion; - -#ifndef ADLMIDI_DISABLE_MIDI_SEQUENCER - /** - * @brief Build MIDI track data from the raw track data storage - * @return true if everything successfully processed, or false on any error - */ - bool buildTrackData(); - - /** - * @brief Parse one event from raw MIDI track stream - * @param [_inout] ptr pointer to pointer to current position on the raw data track - * @param [_in] end address to end of raw track data, needed to validate position and size - * @param [_inout] status status of the track processing - * @return Parsed MIDI event entry - */ - MidiEvent parseEvent(uint8_t **ptr, uint8_t *end, int &status); -#endif//ADLMIDI_DISABLE_MIDI_SEQUENCER + std::set caugh_missing_banks_percussion; public: const std::string &getErrorString(); void setErrorString(const std::string &err); -#ifndef ADLMIDI_DISABLE_MIDI_SEQUENCER - std::string musTitle; - std::string musCopyright; - std::vector musTrackTitles; - std::vector musMarkers; + //! OPL3 Chip manager + OPL3 m_synth; - fraction InvDeltaTicks, Tempo; - //! Tempo multiplier - double tempoMultiplier; - bool atEnd, - loopStart, - loopEnd, - invalidLoop; /*Loop points are invalid (loopStart after loopEnd or loopStart and loopEnd are on same place)*/ - char ____padding2[2]; -#endif - OPL3 opl; - - int32_t outBuf[1024]; + //! Generator output buffer + int32_t m_outBuf[1024]; + //! Synthesizer setup Setup m_setup; -#ifndef ADLMIDI_DISABLE_MIDI_SEQUENCER /** - * @brief Utility function to read Big-Endian integer from raw binary data - * @param buffer Pointer to raw binary buffer - * @param nbytes Count of bytes to parse integer - * @return Extracted unsigned integer + * @brief Load custom bank from file + * @param filename Path to bank file + * @return true on succes */ - static uint64_t ReadBEint(const void *buffer, size_t nbytes); - - /** - * @brief Utility function to read Little-Endian integer from raw binary data - * @param buffer Pointer to raw binary buffer - * @param nbytes Count of bytes to parse integer - * @return Extracted unsigned integer - */ - static uint64_t ReadLEint(const void *buffer, size_t nbytes); - - /** - * @brief Standard MIDI Variable-Length numeric value parser without of validation - * @param [_inout] ptr Pointer to memory block that contains begin of variable-length value - * @return Unsigned integer that conains parsed variable-length value - */ - uint64_t ReadVarLen(uint8_t **ptr); - /** - * @brief Secure Standard MIDI Variable-Length numeric value parser with anti-out-of-range protection - * @param [_inout] ptr Pointer to memory block that contains begin of variable-length value, will be iterated forward - * @param [_in end Pointer to end of memory block where variable-length value is stored (after end of track) - * @param [_out] ok Reference to boolean which takes result of variable-length value parsing - * @return Unsigned integer that conains parsed variable-length value - */ - uint64_t ReadVarLenEx(uint8_t **ptr, uint8_t *end, bool &ok); -#endif - bool LoadBank(const std::string &filename); + + /** + * @brief Load custom bank from memory block + * @param data Pointer to memory block where raw bank file is stored + * @param size Size of given memory block + * @return true on succes + */ bool LoadBank(const void *data, size_t size); - bool LoadBank(fileReader &fr); + + /** + * @brief Load custom bank from opened FileAndMemReader class + * @param fr Instance with opened file + * @return true on succes + */ + bool LoadBank(FileAndMemReader &fr); #ifndef ADLMIDI_DISABLE_MIDI_SEQUENCER + /** + * @brief MIDI file loading pre-process + * @return true on success, false on failure + */ + bool LoadMIDI_pre(); + + /** + * @brief MIDI file loading post-process + * @return true on success, false on failure + */ + bool LoadMIDI_post(); + + /** + * @brief Load music file from a file + * @param filename Path to music file + * @return true on success, false on failure + */ + bool LoadMIDI(const std::string &filename); + + /** + * @brief Load music file from the memory block + * @param data pointer to the memory block + * @param size size of memory block + * @return true on success, false on failure + */ bool LoadMIDI(const void *data, size_t size); - bool LoadMIDI(fileReader &fr); /** * @brief Periodic tick handler. @@ -1114,83 +1134,212 @@ public: * @return desired number of seconds until next call */ double Tick(double s, double granularity); -#endif +#endif //ADLMIDI_DISABLE_MIDI_SEQUENCER /** * @brief Process extra iterators like vibrato or arpeggio * @param s seconds since last call */ - void TickIteratos(double s); + void TickIterators(double s); -#ifndef ADLMIDI_DISABLE_MIDI_SEQUENCER - /** - * @brief Change current position to specified time position in seconds - * @param seconds Absolute time position in seconds - */ - void seek(double seconds); - - /** - * @brief Gives current time position in seconds - * @return Current time position in seconds - */ - double tell(); - - /** - * @brief Gives time length of current song in seconds - * @return Time length of current song in seconds - */ - double timeLength(); - - /** - * @brief Gives loop start time position in seconds - * @return Loop start time position in seconds or -1 if song has no loop points - */ - double getLoopStart(); - - /** - * @brief Gives loop end time position in seconds - * @return Loop end time position in seconds or -1 if song has no loop points - */ - double getLoopEnd(); - - /** - * @brief Return to begin of current song - */ - void rewind(); - - /** - * @brief Set tempo multiplier - * @param tempo Tempo multiplier: 1.0 - original tempo. >1 - faster, <1 - slower - */ - void setTempo(double tempo); -#endif//ADLMIDI_DISABLE_MIDI_SEQUENCER /* RealTime event triggers */ + /** + * @brief Reset state of all channels + */ void realTime_ResetState(); + /** + * @brief Note On event + * @param channel MIDI channel + * @param note Note key (from 0 to 127) + * @param velocity Velocity level (from 0 to 127) + * @return true if Note On event was accepted + */ bool realTime_NoteOn(uint8_t channel, uint8_t note, uint8_t velocity); + + /** + * @brief Note Off event + * @param channel MIDI channel + * @param note Note key (from 0 to 127) + */ void realTime_NoteOff(uint8_t channel, uint8_t note); + /** + * @brief Note aftertouch event + * @param channel MIDI channel + * @param note Note key (from 0 to 127) + * @param atVal After-Touch level (from 0 to 127) + */ void realTime_NoteAfterTouch(uint8_t channel, uint8_t note, uint8_t atVal); + + /** + * @brief Channel aftertouch event + * @param channel MIDI channel + * @param atVal After-Touch level (from 0 to 127) + */ void realTime_ChannelAfterTouch(uint8_t channel, uint8_t atVal); + /** + * @brief Controller Change event + * @param channel MIDI channel + * @param type Type of controller + * @param value Value of the controller (from 0 to 127) + */ void realTime_Controller(uint8_t channel, uint8_t type, uint8_t value); + /** + * @brief Patch change + * @param channel MIDI channel + * @param patch Patch Number (from 0 to 127) + */ void realTime_PatchChange(uint8_t channel, uint8_t patch); + /** + * @brief Pitch bend change + * @param channel MIDI channel + * @param pitch Concoctated raw pitch value + */ void realTime_PitchBend(uint8_t channel, uint16_t pitch); + + /** + * @brief Pitch bend change + * @param channel MIDI channel + * @param msb MSB of pitch value + * @param lsb LSB of pitch value + */ void realTime_PitchBend(uint8_t channel, uint8_t msb, uint8_t lsb); + /** + * @brief LSB Bank Change CC + * @param channel MIDI channel + * @param lsb LSB value of bank number + */ void realTime_BankChangeLSB(uint8_t channel, uint8_t lsb); + + /** + * @brief MSB Bank Change CC + * @param channel MIDI channel + * @param msb MSB value of bank number + */ void realTime_BankChangeMSB(uint8_t channel, uint8_t msb); + + /** + * @brief Bank Change (united value) + * @param channel MIDI channel + * @param bank Bank number value + */ void realTime_BankChange(uint8_t channel, uint16_t bank); + /** + * @brief Sets the Device identifier + * @param id 7-bit Device identifier + */ + void setDeviceId(uint8_t id); + + /** + * @brief System Exclusive message + * @param msg Raw SysEx Message + * @param size Length of SysEx message + * @return true if message was passed successfully. False on any errors + */ + bool realTime_SysEx(const uint8_t *msg, size_t size); + + /** + * @brief Turn off all notes and mute the sound of releasing notes + */ void realTime_panic(); + /** + * @brief Device switch (to extend 16-channels limit of MIDI standard) + * @param track MIDI track index + * @param data Device name + * @param length Length of device name string + */ + void realTime_deviceSwitch(size_t track, const char *data, size_t length); + + /** + * @brief Currently selected device index + * @param track MIDI track index + * @return Multiple 16 value + */ + size_t realTime_currentDevice(size_t track); + + /** + * @brief Send raw OPL chip command + * @param reg OPL Register + * @param value Value to write + */ + void realTime_rawOPL(uint8_t reg, uint8_t value); + +#if defined(ADLMIDI_AUDIO_TICK_HANDLER) // Audio rate tick handler void AudioTick(uint32_t chipId, uint32_t rate); +#endif private: + /** + * @brief Hardware manufacturer (Used for SysEx) + */ + enum + { + Manufacturer_Roland = 0x41, + Manufacturer_Yamaha = 0x43, + Manufacturer_UniversalNonRealtime = 0x7E, + Manufacturer_UniversalRealtime = 0x7F + }; + + /** + * @brief Roland Mode (Used for SysEx) + */ + enum + { + RolandMode_Request = 0x11, + RolandMode_Send = 0x12 + }; + + /** + * @brief Device model (Used for SysEx) + */ + enum + { + RolandModel_GS = 0x42, + RolandModel_SC55 = 0x45, + YamahaModel_XG = 0x4C + }; + + /** + * @brief Process generic SysEx events + * @param dev Device ID + * @param realtime Is real-time event + * @param data Raw SysEx data + * @param size Size of given SysEx data + * @return true when event was successfully handled + */ + bool doUniversalSysEx(unsigned dev, bool realtime, const uint8_t *data, size_t size); + + /** + * @brief Process events specific to Roland devices + * @param dev Device ID + * @param data Raw SysEx data + * @param size Size of given SysEx data + * @return true when event was successfully handled + */ + bool doRolandSysEx(unsigned dev, const uint8_t *data, size_t size); + + /** + * @brief Process events specific to Yamaha devices + * @param dev Device ID + * @param data Raw SysEx data + * @param size Size of given SysEx data + * @return true when event was successfully handled + */ + bool doYamahaSysEx(unsigned dev, const uint8_t *data, size_t size); + +private: + /** + * @brief Note Update properties + */ enum { Upd_Patch = 0x1, @@ -1200,43 +1349,129 @@ private: Upd_All = Upd_Pan + Upd_Volume + Upd_Pitch, Upd_Off = 0x20, Upd_Mute = 0x40, - Upt_OffMute = Upd_Off + Upd_Mute + Upd_OffMute = Upd_Off + Upd_Mute }; - void NoteUpdate(uint16_t MidCh, + /** + * @brief Update active note + * @param MidCh MIDI Channel where note is processing + * @param i Iterator that points to active note in the MIDI channel + * @param props_mask Properties to update + * @param select_adlchn Specify chip channel, or -1 - all chip channels used by the note + */ + void noteUpdate(size_t midCh, MIDIchannel::activenoteiterator i, unsigned props_mask, int32_t select_adlchn = -1); -#ifndef ADLMIDI_DISABLE_MIDI_SEQUENCER - bool ProcessEventsNew(bool isSeek = false); - void HandleEvent(size_t tk, const MidiEvent &evt, int &status); -#endif + /** + * @brief Update all notes in specified MIDI channel + * @param midCh MIDI channel to update all notes in it + * @param props_mask Properties to update + */ + void noteUpdateAll(size_t midCh, unsigned props_mask); - // Determine how good a candidate this adlchannel - // would be for playing a note from this instrument. - int64_t CalculateAdlChannelGoodness(size_t c, const MIDIchannel::NoteInfo::Phys &ins, uint16_t /*MidCh*/) const; + /** + * @brief Determine how good a candidate this adlchannel would be for playing a note from this instrument. + * @param c Wanted chip channel + * @param ins Instrument wanted to be used in this channel + * @return Calculated coodness points + */ + int64_t calculateChipChannelGoodness(size_t c, const MIDIchannel::NoteInfo::Phys &ins) const; - // A new note will be played on this channel using this instrument. - // Kill existing notes on this channel (or don't, if we do arpeggio) - void PrepareAdlChannelForNewNote(size_t c, const MIDIchannel::NoteInfo::Phys &ins); + /** + * @brief A new note will be played on this channel using this instrument. + * @param c Wanted chip channel + * @param ins Instrument wanted to be used in this channel + * Kill existing notes on this channel (or don't, if we do arpeggio) + */ + void prepareChipChannelForNewNote(size_t c, const MIDIchannel::NoteInfo::Phys &ins); - void KillOrEvacuate( + /** + * @brief Kills note that uses wanted channel. When arpeggio is possible, note is evaluating to another channel + * @param from_channel Wanted chip channel + * @param j Chip channel instance + * @param i MIDI Channel active note instance + */ + void killOrEvacuate( size_t from_channel, AdlChannel::LocationData *j, MIDIchannel::activenoteiterator i); - void Panic(); - void KillSustainingNotes(int32_t MidCh = -1, int32_t this_adlchn = -1); - void SetRPN(unsigned MidCh, unsigned value, bool MSB); - //void UpdatePortamento(unsigned MidCh) - void NoteUpdate_All(uint16_t MidCh, unsigned props_mask); - void NoteOff(uint16_t MidCh, uint8_t note); - void UpdateVibrato(double amount); - void UpdateArpeggio(double /*amount*/); + /** + * @brief Off all notes and silence sound + */ + void panic(); + + /** + * @brief Kill note, sustaining by pedal or sostenuto + * @param MidCh MIDI channel, -1 - all MIDI channels + * @param this_adlchn Chip channel, -1 - all chip channels + * @param sustain_type Type of systain to process + */ + void killSustainingNotes(int32_t midCh = -1, + int32_t this_adlchn = -1, + uint32_t sustain_type = AdlChannel::LocationData::Sustain_ANY); + /** + * @brief Find active notes and mark them as sostenuto-sustained + * @param MidCh MIDI channel, -1 - all MIDI channels + */ + void markSostenutoNotes(int32_t midCh = -1); + + /** + * @brief Set RPN event value + * @param MidCh MIDI channel + * @param value 1 byte part of RPN value + * @param MSB is MSB or LSB part of value + */ + void setRPN(size_t midCh, unsigned value, bool MSB); + + /** + * @brief Update portamento setup in MIDI channel + * @param midCh MIDI channel where portamento needed to be updated + */ + void updatePortamento(size_t midCh); + + /** + * @brief Off the note + * @param midCh MIDI channel + * @param note Note to off + */ + void noteOff(size_t midCh, uint8_t note); + + /** + * @brief Update processing of vibrato to amount of seconds + * @param amount Amount value in seconds + */ + void updateVibrato(double amount); + + /** + * @brief Update auto-arpeggio + * @param amount Amount value in seconds [UNUSED] + */ + void updateArpeggio(double /*amount*/); + + /** + * @brief Update Portamento gliding to amount of seconds + * @param amount Amount value in seconds + */ + void updateGlide(double amount); public: - uint64_t ChooseDevice(const std::string &name); + /** + * @brief Checks was device name used or not + * @param name Name of MIDI device + * @return Offset of the MIDI Channels, multiple to 16 + */ + size_t chooseDevice(const std::string &name); + + /** + * @brief Gets a textual description of the state of chip channels + * @param text character pointer for text + * @param attr character pointer for text attributes + * @param size number of characters available to write + */ + void describeChannels(char *text, char *attr, size_t size); }; // I think, this is useless inside of Library @@ -1258,8 +1493,35 @@ struct FourChars }; */ +#if defined(ADLMIDI_AUDIO_TICK_HANDLER) extern void adl_audioTickHandler(void *instance, uint32_t chipId, uint32_t rate); -extern int adlRefreshNumCards(ADL_MIDIPlayer *device); +#endif +/** + * @brief Automatically calculate and enable necessary count of 4-op channels on emulated chips + * @param device Library context + * @param silent Don't re-count channel categories + * @return Always 0 + */ +extern int adlCalculateFourOpChannels(MIDIplay *play, bool silent = false); + +/** + * @brief Check emulator availability + * @param emulator Emulator ID (ADL_Emulator) + * @return true when emulator is available + */ +extern bool adl_isEmulatorAvailable(int emulator); + +/** + * @brief Find highest emulator + * @return The ADL_Emulator enum value which contains ID of highest emulator + */ +extern int adl_getHighestEmulator(); + +/** + * @brief Find lowest emulator + * @return The ADL_Emulator enum value which contains ID of lowest emulator + */ +extern int adl_getLowestEmulator(); #endif // ADLMIDI_PRIVATE_HPP diff --git a/src/sound/adlmidi/chips/dosbox/dbopl.cpp b/src/sound/adlmidi/chips/dosbox/dbopl.cpp index 7d78c5f05..193120397 100644 --- a/src/sound/adlmidi/chips/dosbox/dbopl.cpp +++ b/src/sound/adlmidi/chips/dosbox/dbopl.cpp @@ -37,6 +37,8 @@ #include #include #include +#include +#include #include "dbopl.h" #if defined(__GNUC__) && __GNUC__ > 3 @@ -70,6 +72,37 @@ #define PI 3.14159265358979323846 #endif +struct NoCopy { + NoCopy() {} +private: + NoCopy(const NoCopy &); + NoCopy &operator=(const NoCopy &); +}; +#if !defined(_WIN32) +#include +struct Mutex : NoCopy { + Mutex() { pthread_mutex_init(&m, NULL);} + ~Mutex() { pthread_mutex_destroy(&m); } + void lock() { pthread_mutex_lock(&m); } + void unlock() { pthread_mutex_unlock(&m); } + pthread_mutex_t m; +}; +#else +#include +struct Mutex : NoCopy { + Mutex() { InitializeCriticalSection(&m); } + ~Mutex() { DeleteCriticalSection(&m); } + void lock() { EnterCriticalSection(&m); } + void unlock() { LeaveCriticalSection(&m); } + CRITICAL_SECTION m; +}; +#endif +struct MutexHolder : NoCopy { + explicit MutexHolder(Mutex &m) : m(m) { m.lock(); } + ~MutexHolder() { m.unlock(); } + Mutex &m; +}; + namespace DBOPL { #define OPLRATE ((double)(14318180.0 / 288.0)) @@ -219,6 +252,29 @@ static const Bit8u KslShiftTable[4] = { 31,1,2,0 }; +// Pan law table +static const Bit16u PanLawTable[] = +{ + 65535, 65529, 65514, 65489, 65454, 65409, 65354, 65289, + 65214, 65129, 65034, 64929, 64814, 64689, 64554, 64410, + 64255, 64091, 63917, 63733, 63540, 63336, 63123, 62901, + 62668, 62426, 62175, 61914, 61644, 61364, 61075, 60776, + 60468, 60151, 59825, 59489, 59145, 58791, 58428, 58057, + 57676, 57287, 56889, 56482, 56067, 55643, 55211, 54770, + 54320, 53863, 53397, 52923, 52441, 51951, 51453, 50947, + 50433, 49912, 49383, 48846, 48302, 47750, 47191, + 46340, /* Center left */ + 46340, /* Center right */ + 45472, 44885, 44291, 43690, 43083, 42469, 41848, 41221, + 40588, 39948, 39303, 38651, 37994, 37330, 36661, 35986, + 35306, 34621, 33930, 33234, 32533, 31827, 31116, 30400, + 29680, 28955, 28225, 27492, 26754, 26012, 25266, 24516, + 23762, 23005, 22244, 21480, 20713, 19942, 19169, 18392, + 17613, 16831, 16046, 15259, 14469, 13678, 12884, 12088, + 11291, 10492, 9691, 8888, 8085, 7280, 6473, 5666, + 4858, 4050, 3240, 2431, 1620, 810, 0 +}; + //Generate a table index and table shift value using input value from a selected rate static void EnvelopeSelect( Bit8u val, Bit8u& index, Bit8u& shift ) { if ( val < 13 * 4 ) { //Rate 0 - 12 @@ -442,6 +498,7 @@ Bits Operator::TemplateVolume( ) { return vol; } //In sustain phase, but not sustaining, do regular release + /* fall through */ case RELEASE: vol += RateForward( releaseAdd );; if ( GCC_UNLIKELY(vol >= ENV_MAX) ) { @@ -757,6 +814,11 @@ void Channel::WriteC0(const Chip* chip, Bit8u val) { UpdateSynth(chip); } +void Channel::WritePan(Bit8u val) { + panLeft = PanLawTable[val & 0x7F]; + panRight = PanLawTable[0x7F - (val & 0x7F)]; +} + void Channel::UpdateSynth( const Chip* chip ) { //Select the new synth mode if ( chip->opl3Active ) { @@ -971,8 +1033,8 @@ Channel* Channel::BlockTemplate( Chip* chip, Bit32u samples, Bit32s* output ) { case sm3AMFM: case sm3FMAM: case sm3AMAM: - output[ i * 2 + 0 ] += sample & maskLeft; - output[ i * 2 + 1 ] += sample & maskRight; + output[ i * 2 + 0 ] += (sample * panLeft / 65535) & maskLeft; + output[ i * 2 + 1 ] += (sample * panRight / 65535) & maskRight; break; default: break; @@ -1265,21 +1327,56 @@ void Chip::GenerateBlock3_Mix( Bitu total, Bit32s* output ) { } } -void Chip::Setup( Bit32u rate ) { +struct CacheEntry { + Bit32u rate; + Bit32u freqMul[16]; + Bit32u linearRates[76]; + Bit32u attackRates[76]; +}; +struct Cache : NoCopy { + ~Cache(); + Mutex mutex; + std::vector entries; +}; + +static Cache cache; + +Cache::~Cache() +{ + for ( size_t i = 0, n = entries.size(); i < n; ++i ) + delete entries[i]; +} + +static const CacheEntry *CacheLookupRateDependent( Bit32u rate ) +{ + for ( size_t i = 0, n = cache.entries.size(); i < n; ++i ) { + const CacheEntry *entry = cache.entries[i]; + if (entry->rate == rate) + return entry; + } + return NULL; +} + +static const CacheEntry &ComputeRateDependent( Bit32u rate ) +{ + { + MutexHolder lock( cache.mutex ); + if (const CacheEntry *entry = CacheLookupRateDependent( rate )) + return *entry; + } + double original = OPLRATE; -// double original = rate; double scale = original / (double)rate; - //Noise counter is run at the same precision as general waves - noiseAdd = (Bit32u)( 0.5 + scale * ( 1 << LFO_SH ) ); - noiseCounter = 0; - noiseValue = 1; //Make sure it triggers the noise xor the first time - //The low frequency oscillation counter - //Every time his overflows vibrato and tremoloindex are increased - lfoAdd = (Bit32u)( 0.5 + scale * ( 1 << LFO_SH ) ); - lfoCounter = 0; - vibratoIndex = 0; - tremoloIndex = 0; +#if __cplusplus >= 201103L + std::unique_ptr entry(new CacheEntry); +#else + std::auto_ptr entry(new CacheEntry); +#endif + entry->rate = rate; + Bit32u *freqMul = entry->freqMul; + Bit32u *linearRates = entry->linearRates; + Bit32u *attackRates = entry->attackRates; //With higher octave this gets shifted up //-1 since the freqCreateTable = *2 @@ -1301,6 +1398,7 @@ void Chip::Setup( Bit32u rate ) { EnvelopeSelect( i, index, shift ); linearRates[i] = (Bit32u)( scale * (EnvelopeIncreaseTable[ index ] << ( RATE_SH + ENV_EXTRA - shift - 3 ))); } + // Bit32s attackDiffs[62]; //Generate the best matching attack rate for ( Bit8u i = 0; i < 62; i++ ) { @@ -1353,6 +1451,36 @@ void Chip::Setup( Bit32u rate ) { //This should provide instant volume maximizing attackRates[i] = 8 << RATE_SH; } + + MutexHolder lock( cache.mutex ); + if (const CacheEntry *entry = CacheLookupRateDependent( rate )) + return *entry; + + cache.entries.push_back(entry.get()); + return *entry.release(); +} + +void Chip::Setup( Bit32u rate ) { + double original = OPLRATE; +// double original = rate; + double scale = original / (double)rate; + + //Noise counter is run at the same precision as general waves + noiseAdd = (Bit32u)( 0.5 + scale * ( 1 << LFO_SH ) ); + noiseCounter = 0; + noiseValue = 1; //Make sure it triggers the noise xor the first time + //The low frequency oscillation counter + //Every time his overflows vibrato and tremoloindex are increased + lfoAdd = (Bit32u)( 0.5 + scale * ( 1 << LFO_SH ) ); + lfoCounter = 0; + vibratoIndex = 0; + tremoloIndex = 0; + + const CacheEntry &entry = ComputeRateDependent( rate ); + freqMul = entry.freqMul; + linearRates = entry.linearRates; + attackRates = entry.attackRates; + //Setup the channels with the correct four op flags //Channels are accessed through a table so they appear linear here chan[ 0].fourMask = 0x00 | ( 1 << 0 ); @@ -1388,6 +1516,10 @@ void Chip::Setup( Bit32u rate ) { WriteReg( i, 0xff ); WriteReg( i, 0x0 ); } + + for ( int i = 0; i < 18; i++ ) { + chan[i].WritePan( 0x40 ); + } } static bool doneTables = false; @@ -1614,5 +1746,14 @@ void Handler::Init( Bitu rate ) { chip.Setup( static_cast(rate) ); } +void Handler::WritePan( Bit32u reg, Bit8u val ) +{ + Bitu index; + index = ((reg >> 4) & 0x10) | (reg & 0xf); + if (ChanOffsetTable[index]) { + Channel* regChan = (Channel*)(((char *)&chip) + ChanOffsetTable[index]); + regChan->WritePan(val); + } +} } //Namespace DBOPL diff --git a/src/sound/adlmidi/chips/dosbox/dbopl.h b/src/sound/adlmidi/chips/dosbox/dbopl.h index 73c0aa9a1..89d2019c9 100644 --- a/src/sound/adlmidi/chips/dosbox/dbopl.h +++ b/src/sound/adlmidi/chips/dosbox/dbopl.h @@ -75,13 +75,13 @@ typedef enum { sm3AMAM, sm6Start, sm2Percussion, - sm3Percussion, + sm3Percussion } SynthMode; //Shifts for the values contained in chandata variable enum { SHIFT_KSLBASE = 16, - SHIFT_KEYCODE = 24, + SHIFT_KEYCODE = 24 }; struct Operator { @@ -91,7 +91,7 @@ public: MASK_KSR = 0x10, MASK_SUSTAIN = 0x20, MASK_VIBRATO = 0x40, - MASK_TREMOLO = 0x80, + MASK_TREMOLO = 0x80 }; typedef enum { @@ -99,7 +99,7 @@ public: RELEASE, SUSTAIN, DECAY, - ATTACK, + ATTACK } State; VolumeHandler volHandler; @@ -192,6 +192,9 @@ struct Channel { Bit8s maskLeft; //Sign extended values for both channel's panning Bit8s maskRight; + Bit16u panLeft; // Extended behavior, scale values for soft panning + Bit16u panRight; + //Forward the channel data to the operators of the channel void SetChanData( const Chip* chip, Bit32u data ); //Change in the chandata, check for new values and if we have to forward to operators @@ -201,6 +204,8 @@ struct Channel { void WriteB0( const Chip* chip, Bit8u val ); void WriteC0( const Chip* chip, Bit8u val ); + void WritePan( Bit8u val ); + //call this for the first channel template< bool opl3Mode > void GeneratePercussion( Chip* chip, Bit32s* output ); @@ -222,11 +227,11 @@ struct Chip { Bit32u noiseValue; //Frequency scales for the different multiplications - Bit32u freqMul[16]; + const Bit32u *freqMul/*[16]*/; //Rates for decay and release for rate of this chip - Bit32u linearRates[76]; + const Bit32u *linearRates/*[76]*/; //Best match attack rates for the rate of this chip - Bit32u attackRates[76]; + const Bit32u *attackRates/*[76]*/; //18 channels with 2 operators each Channel chan[18]; @@ -271,6 +276,7 @@ struct Chip { struct Handler { DBOPL::Chip chip; + void WritePan( Bit32u port, Bit8u val ); Bit32u WriteAddr( Bit32u port, Bit8u val ); void WriteReg( Bit32u addr, Bit8u val ); void GenerateArr(Bit32s *out, Bitu *samples); diff --git a/src/sound/adlmidi/chips/dosbox_opl3.cpp b/src/sound/adlmidi/chips/dosbox_opl3.cpp index 30fa38ea5..0b9501fdc 100644 --- a/src/sound/adlmidi/chips/dosbox_opl3.cpp +++ b/src/sound/adlmidi/chips/dosbox_opl3.cpp @@ -1,3 +1,23 @@ +/* + * Interfaces over Yamaha OPL3 (YMF262) chip emulators + * + * Copyright (C) 2017-2018 Vitaly Novichkov (Wohlstand) + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + #include "dosbox_opl3.h" #include "dosbox/dbopl.h" #include @@ -41,6 +61,12 @@ void DosBoxOPL3::writeReg(uint16_t addr, uint8_t data) chip_r->WriteReg(static_cast(addr), data); } +void DosBoxOPL3::writePan(uint16_t addr, uint8_t data) +{ + DBOPL::Handler *chip_r = reinterpret_cast(m_chip); + chip_r->WritePan(static_cast(addr), data); +} + void DosBoxOPL3::nativeGenerateN(int16_t *output, size_t frames) { DBOPL::Handler *chip_r = reinterpret_cast(m_chip); @@ -50,5 +76,5 @@ void DosBoxOPL3::nativeGenerateN(int16_t *output, size_t frames) const char *DosBoxOPL3::emulatorName() { - return "DosBox 0.74-r4111 OPL3"; + return "DOSBox 0.74-r4111 OPL3"; } diff --git a/src/sound/adlmidi/chips/dosbox_opl3.h b/src/sound/adlmidi/chips/dosbox_opl3.h index 192802666..eb79300f1 100644 --- a/src/sound/adlmidi/chips/dosbox_opl3.h +++ b/src/sound/adlmidi/chips/dosbox_opl3.h @@ -1,3 +1,23 @@ +/* + * Interfaces over Yamaha OPL3 (YMF262) chip emulators + * + * Copyright (C) 2017-2018 Vitaly Novichkov (Wohlstand) + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + #ifndef DOSBOX_OPL3_H #define DOSBOX_OPL3_H @@ -14,6 +34,7 @@ public: void setRate(uint32_t rate) override; void reset() override; void writeReg(uint16_t addr, uint8_t data) override; + void writePan(uint16_t addr, uint8_t data) override; void nativePreGenerate() override {} void nativePostGenerate() override {} void nativeGenerateN(int16_t *output, size_t frames) override; diff --git a/src/sound/adlmidi/chips/nuked/nukedopl3.c b/src/sound/adlmidi/chips/nuked/nukedopl3.c index 87d321257..267e67ae4 100644 --- a/src/sound/adlmidi/chips/nuked/nukedopl3.c +++ b/src/sound/adlmidi/chips/nuked/nukedopl3.c @@ -1,16 +1,19 @@ /* * Copyright (C) 2013-2018 Alexey Khokholov (Nuke.YKT) * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. * - * This program is distributed in the hope that it will be useful, + * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * Nuked OPL3 emulator. * Thanks: @@ -174,6 +177,32 @@ static const Bit8u ch_slot[18] = { 0, 1, 2, 6, 7, 8, 12, 13, 14, 18, 19, 20, 24, 25, 26, 30, 31, 32 }; +/* + * Pan law table + */ + +static const Bit16u panlawtable[] = +{ + 65535, 65529, 65514, 65489, 65454, 65409, 65354, 65289, + 65214, 65129, 65034, 64929, 64814, 64689, 64554, 64410, + 64255, 64091, 63917, 63733, 63540, 63336, 63123, 62901, + 62668, 62426, 62175, 61914, 61644, 61364, 61075, 60776, + 60468, 60151, 59825, 59489, 59145, 58791, 58428, 58057, + 57676, 57287, 56889, 56482, 56067, 55643, 55211, 54770, + 54320, 53863, 53397, 52923, 52441, 51951, 51453, 50947, + 50433, 49912, 49383, 48846, 48302, 47750, 47191, + 46340, /* Center left */ + 46340, /* Center right */ + 45472, 44885, 44291, 43690, 43083, 42469, 41848, 41221, + 40588, 39948, 39303, 38651, 37994, 37330, 36661, 35986, + 35306, 34621, 33930, 33234, 32533, 31827, 31116, 30400, + 29680, 28955, 28225, 27492, 26754, 26012, 25266, 24516, + 23762, 23005, 22244, 21480, 20713, 19942, 19169, 18392, + 17613, 16831, 16046, 15259, 14469, 13678, 12884, 12088, + 11291, 10492, 9691, 8888, 8085, 7280, 6473, 5666, + 4858, 4050, 3240, 2431, 1620, 810, 0 +}; + /* * Envelope generator */ @@ -1068,7 +1097,7 @@ void OPL3_Generate(opl3_chip *chip, Bit16s *buf) { accm += *chip->channel[ii].out[jj]; } - chip->mixbuff[0] += (Bit16s)(accm & chip->channel[ii].cha); + chip->mixbuff[0] += (Bit16s)((accm * chip->channel[ii].chl / 65535) & chip->channel[ii].cha); } for (ii = 15; ii < 18; ii++) @@ -1097,7 +1126,7 @@ void OPL3_Generate(opl3_chip *chip, Bit16s *buf) { accm += *chip->channel[ii].out[jj]; } - chip->mixbuff[1] += (Bit16s)(accm & chip->channel[ii].chb); + chip->mixbuff[1] += (Bit16s)((accm * chip->channel[ii].chr / 65535) & chip->channel[ii].chb); } for (ii = 33; ii < 36; ii++) @@ -1229,6 +1258,8 @@ void OPL3_Reset(opl3_chip *chip, Bit32u samplerate) chip->channel[channum].chtype = ch_2op; chip->channel[channum].cha = 0xffff; chip->channel[channum].chb = 0xffff; + chip->channel[channum].chl = 46340; + chip->channel[channum].chr = 46340; chip->channel[channum].ch_num = channum; OPL3_ChannelSetupAlg(&chip->channel[channum]); } @@ -1238,6 +1269,19 @@ void OPL3_Reset(opl3_chip *chip, Bit32u samplerate) chip->vibshift = 1; } +static void OPL3_ChannelWritePan(opl3_channel *channel, Bit8u data) +{ + channel->chl = panlawtable[data & 0x7F]; + channel->chr = panlawtable[0x7F - (data & 0x7F)]; +} + +void OPL3_WritePan(opl3_chip *chip, Bit16u reg, Bit8u v) +{ + Bit8u high = (reg >> 8) & 0x01; + Bit8u regm = reg & 0xff; + OPL3_ChannelWritePan(&chip->channel[9 * high + (regm & 0x0f)], v); +} + void OPL3_WriteReg(opl3_chip *chip, Bit16u reg, Bit8u v) { Bit8u high = (reg >> 8) & 0x01; diff --git a/src/sound/adlmidi/chips/nuked/nukedopl3.h b/src/sound/adlmidi/chips/nuked/nukedopl3.h index d57cf5fb5..268e8de64 100644 --- a/src/sound/adlmidi/chips/nuked/nukedopl3.h +++ b/src/sound/adlmidi/chips/nuked/nukedopl3.h @@ -1,16 +1,19 @@ /* * Copyright (C) 2013-2018 Alexey Khokholov (Nuke.YKT) * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. * - * This program is distributed in the hope that it will be useful, + * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * Nuked OPL3 emulator. * Thanks: @@ -98,6 +101,7 @@ struct _opl3_channel { Bit8u alg; Bit8u ksv; Bit16u cha, chb; + Bit16u chl, chr; Bit8u ch_num; }; @@ -150,6 +154,7 @@ void OPL3_GenerateResampled(opl3_chip *chip, Bit16s *buf); void OPL3_Reset(opl3_chip *chip, Bit32u samplerate); void OPL3_WriteReg(opl3_chip *chip, Bit16u reg, Bit8u v); void OPL3_WriteRegBuffered(opl3_chip *chip, Bit16u reg, Bit8u v); +void OPL3_WritePan(opl3_chip *chip, Bit16u reg, Bit8u v); void OPL3_GenerateStream(opl3_chip *chip, Bit16s *sndptr, Bit32u numsamples); void OPL3_GenerateStreamMix(opl3_chip *chip, Bit16s *sndptr, Bit32u numsamples); diff --git a/src/sound/adlmidi/chips/nuked/nukedopl3_174.c b/src/sound/adlmidi/chips/nuked/nukedopl3_174.c index 99eab16dc..8f818d4ee 100644 --- a/src/sound/adlmidi/chips/nuked/nukedopl3_174.c +++ b/src/sound/adlmidi/chips/nuked/nukedopl3_174.c @@ -229,6 +229,32 @@ static const Bit8u ch_slot[18] = { 0, 1, 2, 6, 7, 8, 12, 13, 14, 18, 19, 20, 24, 25, 26, 30, 31, 32 }; +/* + * Pan law table + */ + +static const Bit16u panlawtable[] = +{ + 65535, 65529, 65514, 65489, 65454, 65409, 65354, 65289, + 65214, 65129, 65034, 64929, 64814, 64689, 64554, 64410, + 64255, 64091, 63917, 63733, 63540, 63336, 63123, 62901, + 62668, 62426, 62175, 61914, 61644, 61364, 61075, 60776, + 60468, 60151, 59825, 59489, 59145, 58791, 58428, 58057, + 57676, 57287, 56889, 56482, 56067, 55643, 55211, 54770, + 54320, 53863, 53397, 52923, 52441, 51951, 51453, 50947, + 50433, 49912, 49383, 48846, 48302, 47750, 47191, + 46340, /* Center left */ + 46340, /* Center right */ + 45472, 44885, 44291, 43690, 43083, 42469, 41848, 41221, + 40588, 39948, 39303, 38651, 37994, 37330, 36661, 35986, + 35306, 34621, 33930, 33234, 32533, 31827, 31116, 30400, + 29680, 28955, 28225, 27492, 26754, 26012, 25266, 24516, + 23762, 23005, 22244, 21480, 20713, 19942, 19169, 18392, + 17613, 16831, 16046, 15259, 14469, 13678, 12884, 12088, + 11291, 10492, 9691, 8888, 8085, 7280, 6473, 5666, + 4858, 4050, 3240, 2431, 1620, 810, 0 +}; + /* * Envelope generator */ @@ -1082,7 +1108,7 @@ void OPL3v17_Generate(opl3_chip *chip, Bit16s *buf) { accm += *chip->channel[ii].out[jj]; } - chip->mixbuff[0] += (Bit16s)(accm & chip->channel[ii].cha); + chip->mixbuff[0] += (Bit16s)((accm * chip->channel[ii].chl / 65535) & chip->channel[ii].cha); } for (ii = 15; ii < 18; ii++) @@ -1121,7 +1147,7 @@ void OPL3v17_Generate(opl3_chip *chip, Bit16s *buf) { accm += *chip->channel[ii].out[jj]; } - chip->mixbuff[1] += (Bit16s)(accm & chip->channel[ii].chb); + chip->mixbuff[1] += (Bit16s)((accm * chip->channel[ii].chr / 65535) & chip->channel[ii].chb); } for (ii = 33; ii < 36; ii++) @@ -1220,8 +1246,10 @@ void OPL3v17_Reset(opl3_chip *chip, Bit32u samplerate) chip->channel[channum].out[2] = &chip->zeromod; chip->channel[channum].out[3] = &chip->zeromod; chip->channel[channum].chtype = ch_2op; - chip->channel[channum].cha = ~0; - chip->channel[channum].chb = ~0; + chip->channel[channum].cha = 0xffff; + chip->channel[channum].chb = 0xffff; + chip->channel[channum].chl = 46340; + chip->channel[channum].chr = 46340; OPL3_ChannelSetupAlg(&chip->channel[channum]); } chip->noise = 0x306600; @@ -1230,6 +1258,19 @@ void OPL3v17_Reset(opl3_chip *chip, Bit32u samplerate) chip->vibshift = 1; } +static void OPL3v17_ChannelWritePan(opl3_channel *channel, Bit8u data) +{ + channel->chl = panlawtable[data & 0x7F]; + channel->chr = panlawtable[0x7F - (data & 0x7F)]; +} + +void OPL3v17_WritePan(opl3_chip *chip, Bit16u reg, Bit8u v) +{ + Bit8u high = (reg >> 8) & 0x01; + Bit8u regm = reg & 0xff; + OPL3v17_ChannelWritePan(&chip->channel[9 * high + (regm & 0x0f)], v); +} + void OPL3v17_WriteReg(opl3_chip *chip, Bit16u reg, Bit8u v) { Bit8u high = (reg >> 8) & 0x01; diff --git a/src/sound/adlmidi/chips/nuked/nukedopl3_174.h b/src/sound/adlmidi/chips/nuked/nukedopl3_174.h index 240802f4f..cd185620c 100644 --- a/src/sound/adlmidi/chips/nuked/nukedopl3_174.h +++ b/src/sound/adlmidi/chips/nuked/nukedopl3_174.h @@ -103,6 +103,7 @@ struct _opl3_channel { Bit8u alg; Bit8u ksv; Bit16u cha, chb; + Bit16u chl, chr; }; typedef struct _opl3_writebuf { @@ -142,6 +143,7 @@ struct _opl3_chip { void OPL3v17_Generate(opl3_chip *chip, Bit16s *buf); void OPL3v17_GenerateResampled(opl3_chip *chip, Bit16s *buf); void OPL3v17_Reset(opl3_chip *chip, Bit32u samplerate); +void OPL3v17_WritePan(opl3_chip *chip, Bit16u reg, Bit8u v); void OPL3v17_WriteReg(opl3_chip *chip, Bit16u reg, Bit8u v); void OPL3v17_WriteRegBuffered(opl3_chip *chip, Bit16u reg, Bit8u v); void OPL3v17_GenerateStream(opl3_chip *chip, Bit16s *sndptr, Bit32u numsamples); diff --git a/src/sound/adlmidi/chips/nuked_opl3.cpp b/src/sound/adlmidi/chips/nuked_opl3.cpp index 48e5c1736..bbf4a2518 100644 --- a/src/sound/adlmidi/chips/nuked_opl3.cpp +++ b/src/sound/adlmidi/chips/nuked_opl3.cpp @@ -1,3 +1,23 @@ +/* + * Interfaces over Yamaha OPL3 (YMF262) chip emulators + * + * Copyright (C) 2017-2018 Vitaly Novichkov (Wohlstand) + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + #include "nuked_opl3.h" #include "nuked/nukedopl3.h" #include @@ -37,6 +57,12 @@ void NukedOPL3::writeReg(uint16_t addr, uint8_t data) OPL3_WriteRegBuffered(chip_r, addr, data); } +void NukedOPL3::writePan(uint16_t addr, uint8_t data) +{ + opl3_chip *chip_r = reinterpret_cast(m_chip); + OPL3_WritePan(chip_r, addr, data); +} + void NukedOPL3::nativeGenerate(int16_t *frame) { opl3_chip *chip_r = reinterpret_cast(m_chip); diff --git a/src/sound/adlmidi/chips/nuked_opl3.h b/src/sound/adlmidi/chips/nuked_opl3.h index 1b34e9ab5..33baf549a 100644 --- a/src/sound/adlmidi/chips/nuked_opl3.h +++ b/src/sound/adlmidi/chips/nuked_opl3.h @@ -1,3 +1,23 @@ +/* + * Interfaces over Yamaha OPL3 (YMF262) chip emulators + * + * Copyright (C) 2017-2018 Vitaly Novichkov (Wohlstand) + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + #ifndef NUKED_OPL3_H #define NUKED_OPL3_H @@ -14,6 +34,7 @@ public: void setRate(uint32_t rate) override; void reset() override; void writeReg(uint16_t addr, uint8_t data) override; + void writePan(uint16_t addr, uint8_t data) override; void nativePreGenerate() override {} void nativePostGenerate() override {} void nativeGenerate(int16_t *frame) override; diff --git a/src/sound/adlmidi/chips/nuked_opl3_v174.cpp b/src/sound/adlmidi/chips/nuked_opl3_v174.cpp index e24b2e706..6bb06c280 100644 --- a/src/sound/adlmidi/chips/nuked_opl3_v174.cpp +++ b/src/sound/adlmidi/chips/nuked_opl3_v174.cpp @@ -1,3 +1,23 @@ +/* + * Interfaces over Yamaha OPL3 (YMF262) chip emulators + * + * Copyright (C) 2017-2018 Vitaly Novichkov (Wohlstand) + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + #include "nuked_opl3_v174.h" #include "nuked/nukedopl3_174.h" #include @@ -37,6 +57,12 @@ void NukedOPL3v174::writeReg(uint16_t addr, uint8_t data) OPL3v17_WriteReg(chip_r, addr, data); } +void NukedOPL3v174::writePan(uint16_t addr, uint8_t data) +{ + opl3_chip *chip_r = reinterpret_cast(m_chip); + OPL3v17_WritePan(chip_r, addr, data); +} + void NukedOPL3v174::nativeGenerate(int16_t *frame) { opl3_chip *chip_r = reinterpret_cast(m_chip); diff --git a/src/sound/adlmidi/chips/nuked_opl3_v174.h b/src/sound/adlmidi/chips/nuked_opl3_v174.h index f14221fe0..9eaeb192a 100644 --- a/src/sound/adlmidi/chips/nuked_opl3_v174.h +++ b/src/sound/adlmidi/chips/nuked_opl3_v174.h @@ -1,3 +1,23 @@ +/* + * Interfaces over Yamaha OPL3 (YMF262) chip emulators + * + * Copyright (C) 2017-2018 Vitaly Novichkov (Wohlstand) + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + #ifndef NUKED_OPL3174_H #define NUKED_OPL3174_H @@ -14,6 +34,7 @@ public: void setRate(uint32_t rate) override; void reset() override; void writeReg(uint16_t addr, uint8_t data) override; + void writePan(uint16_t addr, uint8_t data) override; void nativePreGenerate() override {} void nativePostGenerate() override {} void nativeGenerate(int16_t *frame) override; diff --git a/src/sound/adlmidi/chips/opl_chip_base.h b/src/sound/adlmidi/chips/opl_chip_base.h index 879d6dad8..723bbc9bc 100644 --- a/src/sound/adlmidi/chips/opl_chip_base.h +++ b/src/sound/adlmidi/chips/opl_chip_base.h @@ -1,3 +1,21 @@ +/* + * Copyright (C) 2017-2018 Vitaly Novichkov (Wohlstand) + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + #ifndef ONP_CHIP_BASE_H #define ONP_CHIP_BASE_H @@ -43,6 +61,9 @@ public: virtual void reset() = 0; virtual void writeReg(uint16_t addr, uint8_t data) = 0; + // extended + virtual void writePan(uint16_t addr, uint8_t data) { (void)addr; (void)data; } + virtual void nativePreGenerate() = 0; virtual void nativePostGenerate() = 0; virtual void nativeGenerate(int16_t *frame) = 0; diff --git a/src/sound/adlmidi/chips/opl_chip_base.tcc b/src/sound/adlmidi/chips/opl_chip_base.tcc index 48ad103fa..a64aa7c19 100644 --- a/src/sound/adlmidi/chips/opl_chip_base.tcc +++ b/src/sound/adlmidi/chips/opl_chip_base.tcc @@ -236,8 +236,8 @@ void OPLChipBaseT::resampledGenerate(int32_t *output) rsm->out_count = 1; rsm->out_data = f_out; } - output[0] = std::lround(f_out[0]); - output[1] = std::lround(f_out[1]); + output[0] = static_cast(std::lround(f_out[0])); + output[1] = static_cast(std::lround(f_out[1])); } #else template diff --git a/src/sound/adlmidi/file_reader.hpp b/src/sound/adlmidi/file_reader.hpp new file mode 100644 index 000000000..7d13262a1 --- /dev/null +++ b/src/sound/adlmidi/file_reader.hpp @@ -0,0 +1,300 @@ +/* + * FileAndMemoryReader - a tiny helper to utify file reading from a disk and memory block + * + * Copyright (c) 2015-2018 Vitaly Novichkov + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +#pragma once +#ifndef FILE_AND_MEM_READER_HHHH +#define FILE_AND_MEM_READER_HHHH + +#include // std::string +#include // std::fopen, std::fread, std::fseek, std::ftell, std::fclose, std::feof +#include // uint*_t +#include // size_t and friends +#ifdef _WIN32 +#define NOMINMAX 1 +#include // std::strlen +#include // MultiByteToWideChar +#endif + +/** + * @brief A little class gives able to read filedata from disk and also from a memory segment + */ +class FileAndMemReader +{ + //! Currently loaded filename (empty for a memory blocks) + std::string m_file_name; + //! File reader descriptor + std::FILE *m_fp; + + //! Memory pointer descriptor + const void *m_mp; + //! Size of memory block + size_t m_mp_size; + //! Cursor position in the memory block + size_t m_mp_tell; + +public: + /** + * @brief Relation direction + */ + enum relTo + { + //! At begin position + SET = SEEK_SET, + //! At current position + CUR = SEEK_CUR, + //! At end position + END = SEEK_END + }; + + /** + * @brief C.O.: It's a constructor! + */ + FileAndMemReader() : + m_fp(NULL), + m_mp(NULL), + m_mp_size(0), + m_mp_tell(0) + {} + + /** + * @brief C.O.: It's a destructor! + */ + ~FileAndMemReader() + { + close(); + } + + /** + * @brief Open file from a disk + * @param path Path to the file in UTF-8 (even on Windows!) + */ + void openFile(const char *path) + { + if(m_fp) + this->close();//Close previously opened file first! +#if !defined(_WIN32) || defined(__WATCOMC__) + m_fp = std::fopen(path, "rb"); +#else + wchar_t widePath[MAX_PATH]; + int size = MultiByteToWideChar(CP_UTF8, 0, path, static_cast(std::strlen(path)), widePath, MAX_PATH); + widePath[size] = '\0'; + m_fp = _wfopen(widePath, L"rb"); +#endif + m_file_name = path; + m_mp = NULL; + m_mp_size = 0; + m_mp_tell = 0; + } + + /** + * @brief Open file from memory block + * @param mem Pointer to the memory block + * @param lenght Size of given block + */ + void openData(const void *mem, size_t lenght) + { + if(m_fp) + this->close();//Close previously opened file first! + m_fp = NULL; + m_mp = mem; + m_mp_size = lenght; + m_mp_tell = 0; + } + + /** + * @brief Seek to given position + * @param pos Offset or position + * @param rel_to Relation (at begin, at current, or at end) + */ + void seek(long pos, int rel_to) + { + if(!this->isValid()) + return; + + if(m_fp)//If a file + { + std::fseek(m_fp, pos, rel_to); + } + else//If a memory block + { + switch(rel_to) + { + case SET: + m_mp_tell = static_cast(pos); + break; + + case END: + m_mp_tell = m_mp_size - static_cast(pos); + break; + + case CUR: + m_mp_tell = m_mp_tell + static_cast(pos); + break; + } + + if(m_mp_tell > m_mp_size) + m_mp_tell = m_mp_size; + } + } + + /** + * @brief Seek to given position (unsigned integer 64 as relation. Negative values not supported) + * @param pos Offset or position + * @param rel_to Relation (at begin, at current, or at end) + */ + inline void seeku(uint64_t pos, int rel_to) + { + this->seek(static_cast(pos), rel_to); + } + + /** + * @brief Read the buffer from a file + * @param buf Pointer to the destination memory block + * @param num Number of elements + * @param size Size of one element + * @return Size + */ + size_t read(void *buf, size_t num, size_t size) + { + if(!this->isValid()) + return 0; + if(m_fp) + return std::fread(buf, num, size, m_fp); + else + { + size_t pos = 0; + size_t maxSize = static_cast(size * num); + + while((pos < maxSize) && (m_mp_tell < m_mp_size)) + { + reinterpret_cast(buf)[pos] = reinterpret_cast(m_mp)[m_mp_tell]; + m_mp_tell++; + pos++; + } + + return pos / num; + } + } + + /** + * @brief Get one byte and seek forward + * @return Readed byte or EOF (a.k.a. -1) + */ + int getc() + { + if(!this->isValid()) + return -1; + if(m_fp)//If a file + { + return std::getc(m_fp); + } + else //If a memory block + { + if(m_mp_tell >= m_mp_size) + return -1; + int x = reinterpret_cast(m_mp)[m_mp_tell]; + m_mp_tell++; + return x; + } + } + + /** + * @brief Returns current offset of cursor in a file + * @return Offset position + */ + size_t tell() + { + if(!this->isValid()) + return 0; + if(m_fp)//If a file + return static_cast(std::ftell(m_fp)); + else//If a memory block + return m_mp_tell; + } + + /** + * @brief Close the file + */ + void close() + { + if(m_fp) + std::fclose(m_fp); + + m_fp = NULL; + m_mp = NULL; + m_mp_size = 0; + m_mp_tell = 0; + } + + /** + * @brief Is file instance valid + * @return true if vaild + */ + bool isValid() + { + return (m_fp) || (m_mp); + } + + /** + * @brief Is End Of File? + * @return true if end of file was reached + */ + bool eof() + { + if(!this->isValid()) + return true; + if(m_fp) + return (std::feof(m_fp) != 0); + else + return m_mp_tell >= m_mp_size; + } + + /** + * @brief Get a current file name + * @return File name of currently loaded file + */ + const std::string &fileName() + { + return m_file_name; + } + + /** + * @brief Retrieve file size + * @return Size of file in bytes + */ + size_t fileSize() + { + if(!this->isValid()) + return 0; + if(!m_fp) + return m_mp_size; //Size of memory block is well known + size_t old_pos = this->tell(); + seek(0l, FileAndMemReader::END); + size_t file_size = this->tell(); + seek(static_cast(old_pos), FileAndMemReader::SET); + return file_size; + } +}; + +#endif /* FILE_AND_MEM_READER_HHHH */ diff --git a/src/sound/adlmidi/fraction.hpp b/src/sound/adlmidi/fraction.hpp deleted file mode 100644 index 1c0a38dde..000000000 --- a/src/sound/adlmidi/fraction.hpp +++ /dev/null @@ -1,215 +0,0 @@ -#ifndef bqw_fraction_h -#define bqw_fraction_h - -#include -#include - - -/* Fraction number handling. - * Copyright (C) 1992,2001 Bisqwit (http://iki.fi/bisqwit/) - */ - -template -class fraction -{ - inttype num1, num2; - typedef fraction self; - void Optim(); - - #if 1 - inline void Debug(char, const self &) { } - #else - inline void Debug(char op, const self &b) - { - cerr << nom() << '/' << denom() << ' ' << op - << ' ' << b.nom() << '/' << denom() - << ":\n"; - } - #endif -public: - void set(inttype n, inttype d) { num1=n; num2=d; Optim(); } - - fraction() : num1(0), num2(1) { } - fraction(inttype value) : num1(value), num2(1) { } - fraction(inttype n, inttype d) : num1(n), num2(d) { } - fraction(int value) : num1(value), num2(1) { } - template - explicit fraction(const floattype value) { operator= (value); } - inline double value() const {return nom() / (double)denom(); } - inline long double valuel() const {return nom() / (long double)denom(); } - self &operator+= (const inttype &value) { num1+=value*denom(); Optim(); return *this; } - self &operator-= (const inttype &value) { num1-=value*denom(); Optim(); return *this; } - self &operator*= (const inttype &value) { num1*=value; Optim(); return *this; } - self &operator/= (const inttype &value) { num2*=value; Optim(); return *this; } - self &operator+= (const self &b); - self &operator-= (const self &b); - self &operator*= (const self &b) { Debug('*',b);num1*=b.nom(); num2*=b.denom(); Optim(); return *this; } - self &operator/= (const self &b) { Debug('/',b);num1*=b.denom(); num2*=b.nom(); Optim(); return *this; } - self operator- () const { return self(-num1, num2); } - -#define fraction_blah_func(op1, op2) \ - self operator op1 (const self &b) const { self tmp(*this); tmp op2 b; return tmp; } - - fraction_blah_func( +, += ) - fraction_blah_func( -, -= ) - fraction_blah_func( /, /= ) - fraction_blah_func( *, *= ) - -#undef fraction_blah_func -#define fraction_blah_func(op) \ - bool operator op(const self &b) const { return value() op b.value(); } \ - bool operator op(inttype b) const { return value() op b; } - - fraction_blah_func( < ) - fraction_blah_func( > ) - fraction_blah_func( <= ) - fraction_blah_func( >= ) - -#undef fraction_blah_func - - const inttype &nom() const { return num1; } - const inttype &denom() const { return num2; } - inline bool operator == (inttype b) const { return denom() == 1 && nom() == b; } - inline bool operator != (inttype b) const { return denom() != 1 || nom() != b; } - inline bool operator == (const self &b) const { return denom()==b.denom() && nom()==b.nom(); } - inline bool operator != (const self &b) const { return denom()!=b.denom() || nom()!=b.nom(); } - //operator bool () const { return nom() != 0; } - inline bool negative() const { return (nom() < 0) ^ (denom() < 0); } - - self &operator= (const inttype value) { num2=1; num1=value; return *this; } - //self &operator= (int value) { num2=1; num1=value; return *this; } - - self &operator= (double orig) { return *this = (long double)orig; } - self &operator= (long double orig); -}; - -#ifdef _MSC_VER -#pragma warning(disable:4146) -#endif - -template -void fraction::Optim() -{ - /* Euclidean algorithm */ - inttype n1, n2, nn1, nn2; - - nn1 = std::numeric_limits::is_signed ? (num1 >= 0 ? num1 : -num1) : num1; - nn2 = std::numeric_limits::is_signed ? (num2 >= 0 ? num2 : -num2) : num2; - - if(nn1 < nn2) - n1 = num1, n2 = num2; - else - n1 = num2, n2 = num1; - - if(!num1) { num2 = 1; return; } - for(;;) - { - //fprintf(stderr, "%d/%d: n1=%d,n2=%d\n", nom(),denom(),n1,n2); - inttype tmp = n2 % n1; - if(!tmp)break; - n2 = n1; - n1 = tmp; - } - num1 /= n1; - num2 /= n1; - //fprintf(stderr, "result: %d/%d\n\n", nom(), denom()); -} - -#ifdef _MSC_VER -#pragma warning(default:4146) -#endif - -template -inline const fraction abs(const fraction &f) -{ - return fraction(abs(f.nom()), abs(f.denom())); -} - -#define fraction_blah_func(op) \ - template \ - fraction operator op \ - (const inttype bla, const fraction &b) \ - { return fraction (bla) op b; } -fraction_blah_func( + ) -fraction_blah_func( - ) -fraction_blah_func( * ) -fraction_blah_func( / ) -#undef fraction_blah_func - -#define fraction_blah_func(op1, op2) \ - template \ - fraction &fraction::operator op2 (const fraction &b) \ - { \ - inttype newnom = nom()*b.denom() op1 denom()*b.nom(); \ - num2 *= b.denom(); \ - num1 = newnom; \ - Optim(); \ - return *this; \ - } -fraction_blah_func( +, += ) -fraction_blah_func( -, -= ) -#undef fraction_blah_func - -template -fraction &fraction::operator= (long double orig) -{ - if(orig == 0.0) - { - set(0, 0); - return *this; - } - - inttype cf[25]; - for(int maxdepth=1; maxdepth<25; ++maxdepth) - { - inttype u,v; - long double virhe, a=orig; - int i, viim; - - for(i = 0; i < maxdepth; ++i) - { - cf[i] = (inttype)a; - if(cf[i]-1 > cf[i])break; - a = 1.0 / (a - cf[i]); - } - - for(viim=i-1; i < maxdepth; ++i) - cf[i] = 0; - - u = cf[viim]; - v = 1; - for(i = viim-1; i >= 0; --i) - { - inttype w = cf[i] * u + v; - v = u; - u = w; - } - - virhe = (orig - (u / (long double)v)) / orig; - - set(u, v); - //if(verbose > 4) - // cerr << "Guess: " << *this << " - error = " << virhe*100 << "%\n"; - - if(virhe < 1e-8 && virhe > -1e-8)break; - } - - //if(verbose > 4) - //{ - // cerr << "Fraction=" << orig << ": " << *this << endl; - //} - - return *this; -} - - -/* -template -ostream &operator << (ostream &dest, const fraction &m) -{ - if(m.denom() == (inttype)1) return dest << m.nom(); - return dest << m.nom() << '/' << m.denom(); -} -*/ - -#endif diff --git a/src/sound/adlmidi/wopl/wopl_file.c b/src/sound/adlmidi/wopl/wopl_file.c index 25b75be53..f018cbce9 100644 --- a/src/sound/adlmidi/wopl/wopl_file.c +++ b/src/sound/adlmidi/wopl/wopl_file.c @@ -353,6 +353,8 @@ int WOPL_LoadInstFromMem(WOPIFile *file, void *mem, size_t length) GO_FORWARD(2); } + file->version = version; + {/* is drum flag */ if(length < 1) return WOPL_ERR_UNEXPECTED_ENDING; @@ -434,11 +436,13 @@ size_t WOPL_CalculateInstFileSize(WOPIFile *file, uint16_t version) * is percussive instrument */ - if(version >= 3) - ins_size = WOPL_INST_SIZE_V3; + if(version > 2) + /* Skip sounding delays are not part of single-instrument file + * two sizes of uint16_t will be subtracted */ + ins_size = WOPL_INST_SIZE_V3 - (sizeof(uint16_t) * 2); else ins_size = WOPL_INST_SIZE_V2; - final_size += ins_size * 128; + final_size += ins_size; return final_size; } diff --git a/src/sound/adlmidi/wopl/wopl_file.h b/src/sound/adlmidi/wopl/wopl_file.h index 0e3884243..fa270b6f8 100644 --- a/src/sound/adlmidi/wopl/wopl_file.h +++ b/src/sound/adlmidi/wopl/wopl_file.h @@ -71,13 +71,13 @@ typedef enum WOPL_InstrumentFlags WOPL_Ins_IsBlank = 0x04, /* RythmMode flags mask */ - WOPL_RythmModeMask = 0x38, + WOPL_RhythmModeMask = 0x38, /* Mask of the flags range */ WOPL_Ins_ALL_MASK = 0x07 } WOPL_InstrumentFlags; -typedef enum WOPL_RythmMode +typedef enum WOPL_RhythmMode { /* RythmMode: BassDrum */ WOPL_RM_BassDrum = 0x08, @@ -86,10 +86,13 @@ typedef enum WOPL_RythmMode /* RythmMode: TomTom */ WOPL_RM_TomTom = 0x18, /* RythmMode: Cymbell */ - WOPL_RM_Cymball = 0x20, + WOPL_RM_Cymbal = 0x20, /* RythmMode: HiHat */ WOPL_RM_HiHat = 0x28 -} WOPL_RythmMode; +} WOPL_RhythmMode; + +/* DEPRECATED: It has typo. Don't use it! */ +typedef WOPL_RhythmMode WOPL_RythmMode; /* Error codes */ typedef enum WOPL_ErrorCodes diff --git a/src/sound/i_musicinterns.h b/src/sound/i_musicinterns.h index cccc12241..c638afbe7 100644 --- a/src/sound/i_musicinterns.h +++ b/src/sound/i_musicinterns.h @@ -331,6 +331,9 @@ protected: void HandleEvent(int status, int parm1, int parm2); void HandleLongEvent(const uint8_t *data, int len); void ComputeOutput(float *buffer, int len); + +private: + int LoadCustomBank(const char *bankfile); }; @@ -341,14 +344,17 @@ public: OPNMIDIDevice(const char *args); ~OPNMIDIDevice(); + int Open(MidiCallback, void *userdata); int GetDeviceType() const override { return MDEV_OPN; } protected: - void HandleEvent(int status, int parm1, int parm2); void HandleLongEvent(const uint8_t *data, int len); void ComputeOutput(float *buffer, int len); + +private: + int LoadCustomBank(const char *bankfile); }; diff --git a/src/sound/i_soundfont.cpp b/src/sound/i_soundfont.cpp index eb2027215..a591eac97 100644 --- a/src/sound/i_soundfont.cpp +++ b/src/sound/i_soundfont.cpp @@ -302,6 +302,16 @@ void FSoundFontManager::ProcessOneFile(const FString &fn) FSoundFontInfo sft = { fb, fbe, fn, SF_SF2 }; soundfonts.Push(sft); } + if (!memcmp(head, "WOPL3-BANK\0", 11)) + { + FSoundFontInfo sft = { fb, fbe, fn, SF_WOPL }; + soundfonts.Push(sft); + } + if (!memcmp(head, "WOPN2-BANK\0", 11) || !memcmp(head, "WOPN2-B2NK\0", 11)) + { + FSoundFontInfo sft = { fb, fbe, fn, SF_WOPN }; + soundfonts.Push(sft); + } else if (!memcmp(head, "PK", 2)) { auto zip = FResourceFile::OpenResourceFile(fn, true); @@ -333,19 +343,18 @@ void FSoundFontManager::CollectSoundfonts() { findstate_t c_file; void *file; - - + if (GameConfig != NULL && GameConfig->SetSection ("SoundfontSearch.Directories")) { const char *key; const char *value; - + while (GameConfig->NextInSection (key, value)) { if (stricmp (key, "Path") == 0) { FString dir; - + dir = NicePath(value); FixPathSeperator(dir); if (dir.IsNotEmpty()) diff --git a/src/sound/i_soundfont.h b/src/sound/i_soundfont.h index 5ef60102c..681558a86 100644 --- a/src/sound/i_soundfont.h +++ b/src/sound/i_soundfont.h @@ -7,7 +7,9 @@ enum { SF_SF2 = 1, - SF_GUS = 2 + SF_GUS = 2, + SF_WOPL = 4, + SF_WOPN = 8 }; struct FSoundFontInfo diff --git a/src/sound/mididevices/music_adlmidi_mididevice.cpp b/src/sound/mididevices/music_adlmidi_mididevice.cpp index 7cece2ec6..061f6abab 100644 --- a/src/sound/mididevices/music_adlmidi_mididevice.cpp +++ b/src/sound/mididevices/music_adlmidi_mididevice.cpp @@ -36,6 +36,7 @@ #include "i_musicinterns.h" #include "adlmidi/adlmidi.h" +#include "i_soundfont.h" enum { @@ -72,6 +73,15 @@ CUSTOM_CVAR(Bool, adl_run_at_pcm_rate, 0, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) } } +CUSTOM_CVAR(Bool, adl_fullpan, 1, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) +{ + if (currSong != nullptr && currSong->GetDeviceType() == MDEV_ADL) + { + MIDIDeviceChanged(-1, true); + } +} + + CUSTOM_CVAR(Int, adl_bank, 14, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) { if (currSong != nullptr && currSong->GetDeviceType() == MDEV_ADL) @@ -118,18 +128,11 @@ ADLMIDIDevice::ADLMIDIDevice(const char *args) { adl_switchEmulator(Renderer, (int)adl_emulator_id); adl_setRunAtPcmRate(Renderer, (int)adl_run_at_pcm_rate); -// todo: Implement handling of external or in-resources WOPL bank files and load -/* - if(adl_use_custom_bank) - { - adl_openBankFile(Renderer, (char*)adl_bank_file); - adl_openBankData(Renderer, (char*)adl_bank, (unsigned long)size); - } - else -*/ + if(!LoadCustomBank(adl_custom_bank)) adl_setBank(Renderer, (int)adl_bank); adl_setNumChips(Renderer, (int)adl_chips_count); adl_setVolumeRangeModel(Renderer, (int)adl_volume_model); + adl_setSoftPanEnabled(Renderer, (int)adl_fullpan); } } @@ -148,6 +151,27 @@ ADLMIDIDevice::~ADLMIDIDevice() } } +//========================================================================== +// +// ADLMIDIDevice :: LoadCustomBank +// +// Loads a custom WOPL bank for libADLMIDI. Returns 1 when bank has been +// loaded, otherwise, returns 0 when custom banks are disabled or failed +// +//========================================================================== + +int ADLMIDIDevice::LoadCustomBank(const char *bankfile) +{ + if(!adl_use_custom_bank) + return 0; + auto info = sfmanager.FindSoundFont(bankfile, SF_WOPL); + if(info == nullptr) + return 0; + bankfile = info->mFilename.GetChars(); + return (adl_openBankFile(Renderer, bankfile) == 0); +} + + //========================================================================== // // ADLMIDIDevice :: Open diff --git a/src/sound/mididevices/music_opnmidi_mididevice.cpp b/src/sound/mididevices/music_opnmidi_mididevice.cpp index de27644c3..d9f5d6124 100644 --- a/src/sound/mididevices/music_opnmidi_mididevice.cpp +++ b/src/sound/mididevices/music_opnmidi_mididevice.cpp @@ -38,6 +38,7 @@ #include "w_wad.h" #include "i_system.h" #include "opnmidi/opnmidi.h" +#include "i_soundfont.h" enum { @@ -74,6 +75,14 @@ CUSTOM_CVAR(Bool, opn_run_at_pcm_rate, 0, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) } } +CUSTOM_CVAR(Bool, opn_fullpan, 1, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) +{ + if (currSong != nullptr && currSong->GetDeviceType() == MDEV_OPN) + { + MIDIDeviceChanged(-1, true); + } +} + CUSTOM_CVAR(Bool, opn_use_custom_bank, 0, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) { if (currSong != nullptr && currSong->GetDeviceType() == MDEV_OPN) @@ -102,25 +111,21 @@ OPNMIDIDevice::OPNMIDIDevice(const char *args) Renderer = opn2_init(44100); // todo: make it configurable if (Renderer != nullptr) { + if (!LoadCustomBank(opn_custom_bank)) + { + int lump = Wads.CheckNumForFullName("xg.wopn"); + if (lump < 0) + { + I_Error("No OPN bank found"); + } + FMemLump data = Wads.ReadLump(lump); + opn2_openBankData(Renderer, data.GetMem(), (long)data.GetSize()); + } + opn2_switchEmulator(Renderer, (int)opn_emulator_id); opn2_setRunAtPcmRate(Renderer, (int)opn_run_at_pcm_rate); -// todo: Implement handling of external or in-resources WOPN bank files and load -/* - if(opn_use_custom_bank) - { - opn2_openBankFile(Renderer, (char*)opn_bank_file); - opn2_openBankData(Renderer, (char*)opn_bank, (long)size); - } - else - */ - int lump = Wads.CheckNumForFullName("xg.wopn"); - if (lump < 0) - { - I_Error("No OPN bank found"); - } - FMemLump data = Wads.ReadLump(lump); - opn2_openBankData(Renderer, data.GetMem(), (long)data.GetSize()); opn2_setNumChips(Renderer, opn_chips_count); + opn2_setSoftPanEnabled(Renderer, (int)opn_fullpan); } } @@ -139,6 +144,27 @@ OPNMIDIDevice::~OPNMIDIDevice() } } +//========================================================================== +// +// OPNMIDIDevice :: LoadCustomBank +// +// Loads a custom WOPN bank for libOPNMIDI. Returns 1 when bank has been +// loaded, otherwise, returns 0 when custom banks are disabled or failed +// +//========================================================================== + + +int OPNMIDIDevice::LoadCustomBank(const char *bankfile) +{ + if(!opn_use_custom_bank) + return 0; + auto info = sfmanager.FindSoundFont(bankfile, SF_WOPN); + if(info == nullptr) + return 0; + bankfile = info->mFilename.GetChars(); + return (opn2_openBankFile(Renderer, bankfile) == 0); +} + //========================================================================== // // OPNMIDIDevice :: Open diff --git a/src/sound/opnmidi/chips/gens/Ym2612_Emu.cpp b/src/sound/opnmidi/chips/gens/Ym2612_Emu.cpp index 18285bf84..9bdc0d1d4 100644 --- a/src/sound/opnmidi/chips/gens/Ym2612_Emu.cpp +++ b/src/sound/opnmidi/chips/gens/Ym2612_Emu.cpp @@ -1,4 +1,4 @@ -// Game_Music_Emu 0.6.0. http://www.slack.net/~ant/ +// Game_Music_Emu 0.6.0. http://www.slack.net/~ant/ // Based on Gens 2.10 ym2612.c @@ -96,6 +96,8 @@ struct channel_t int KC[4]; // Key Code = valeur fonction de la frequence (voir KSR pour les slots, KSR = KC >> KSR_S) slot_t SLOT[4]; // four slot.operators = les 4 slots de la voie int FFlag; // Frequency step recalculation flag + int PANVolumeL; // Left PCM output channel volume + int PANVolumeR; // Right PCM output channel volume }; struct state_t @@ -253,6 +255,32 @@ static const unsigned char LFO_FMS_TAB [8] = LFO_FMS_BASE * 12, LFO_FMS_BASE * 24 }; + +/* + * Pan law table + */ +static const unsigned short panlawtable[] = +{ + 65535, 65529, 65514, 65489, 65454, 65409, 65354, 65289, + 65214, 65129, 65034, 64929, 64814, 64689, 64554, 64410, + 64255, 64091, 63917, 63733, 63540, 63336, 63123, 62901, + 62668, 62426, 62175, 61914, 61644, 61364, 61075, 60776, + 60468, 60151, 59825, 59489, 59145, 58791, 58428, 58057, + 57676, 57287, 56889, 56482, 56067, 55643, 55211, 54770, + 54320, 53863, 53397, 52923, 52441, 51951, 51453, 50947, + 50433, 49912, 49383, 48846, 48302, 47750, 47191, + 46340, /* Center left */ + 46340, /* Center right */ + 45472, 44885, 44291, 43690, 43083, 42469, 41848, 41221, + 40588, 39948, 39303, 38651, 37994, 37330, 36661, 35986, + 35306, 34621, 33930, 33234, 32533, 31827, 31116, 30400, + 29680, 28955, 28225, 27492, 26754, 26012, 25266, 24516, + 23762, 23005, 22244, 21480, 20713, 19942, 19169, 18392, + 17613, 16831, 16046, 15259, 14469, 13678, 12884, 12088, + 11291, 10492, 9691, 8888, 8085, 7280, 6473, 5666, + 4858, 4050, 3240, 2431, 1620, 810, 0 +}; + inline void YM2612_Special_Update() { } struct Ym2612_Impl @@ -273,6 +301,7 @@ struct Ym2612_Impl void reset(); void write0( int addr, int data ); void write1( int addr, int data ); + void write_pan(int channel, int data ); void run_timer( int ); void run( int pair_count, Ym2612_Emu::sample_t* ); }; @@ -880,6 +909,12 @@ inline void Ym2612_Impl::write1( int opn_addr, int data ) } } +void Ym2612_Impl::write_pan( int channel, int data ) +{ + YM2612.CHANNEL[channel].PANVolumeL = panlawtable[data & 0x7F]; + YM2612.CHANNEL[channel].PANVolumeR = panlawtable[0x7F - (data & 0x7F)]; +} + void Ym2612_Emu::reset() { impl->reset(); @@ -910,6 +945,9 @@ void Ym2612_Impl::reset() ch.FMS = 0; ch.AMS = 0; + ch.PANVolumeL = 46340; + ch.PANVolumeR = 46340; + for ( int j = 0 ;j < 4 ; j++ ) { ch.S0_OUT [j] = 0; @@ -959,6 +997,11 @@ void Ym2612_Emu::write1( int addr, int data ) impl->write1( addr, data ); } +void Ym2612_Emu::write_pan(int channel, int data) +{ + impl->write_pan( channel, data ); +} + void Ym2612_Emu::mute_voices( int mask ) { impl->mute_mask = mask; } static void update_envelope_( slot_t* sl ) @@ -1166,10 +1209,10 @@ void ym2612_update_chan::func( tables_t& g, channel_t& ch, in1 += (ch.SLOT [S1].Finc * freq_LFO) >> (LFO_FMS_LBITS - 1); in2 += (ch.SLOT [S2].Finc * freq_LFO) >> (LFO_FMS_LBITS - 1); in3 += (ch.SLOT [S3].Finc * freq_LFO) >> (LFO_FMS_LBITS - 1); - - int t0 = buf [0] + (CH_OUTd & ch.LEFT); - int t1 = buf [1] + (CH_OUTd & ch.RIGHT); - + + int t0 = buf [0] + ((CH_OUTd * ch.PANVolumeL / 65535) & ch.LEFT); + int t1 = buf [1] + ((CH_OUTd * ch.PANVolumeR / 65535) & ch.RIGHT); + update_envelope( ch.SLOT [0] ); update_envelope( ch.SLOT [1] ); update_envelope( ch.SLOT [2] ); diff --git a/src/sound/opnmidi/chips/gens/Ym2612_Emu.h b/src/sound/opnmidi/chips/gens/Ym2612_Emu.h index efce1e2d1..f483d1bb3 100644 --- a/src/sound/opnmidi/chips/gens/Ym2612_Emu.h +++ b/src/sound/opnmidi/chips/gens/Ym2612_Emu.h @@ -29,6 +29,9 @@ public: // Write addr to register 2 then data to register 3 void write1( int addr, int data ); + // Write pan level channel data + void write_pan( int channel, int data ); + // Run and add pair_count samples into current output buffer contents typedef short sample_t; enum { out_chan_count = 2 }; // stereo diff --git a/src/sound/opnmidi/chips/gens_opn2.cpp b/src/sound/opnmidi/chips/gens_opn2.cpp index 3e8977c77..a43e57ebc 100644 --- a/src/sound/opnmidi/chips/gens_opn2.cpp +++ b/src/sound/opnmidi/chips/gens_opn2.cpp @@ -1,3 +1,23 @@ +/* + * Interfaces over Yamaha OPN2 (YM2612) chip emulators + * + * Copyright (C) 2017-2018 Vitaly Novichkov (Wohlstand) + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + #include "gens_opn2.h" #include @@ -40,6 +60,11 @@ void GensOPN2::writeReg(uint32_t port, uint16_t addr, uint8_t data) } } +void GensOPN2::writePan(uint16_t chan, uint8_t data) +{ + chip->write_pan(static_cast(chan), static_cast(data)); +} + void GensOPN2::nativeGenerateN(int16_t *output, size_t frames) { std::memset(output, 0, frames * sizeof(int16_t) * 2); diff --git a/src/sound/opnmidi/chips/gens_opn2.h b/src/sound/opnmidi/chips/gens_opn2.h index 158ffc69b..240a6208c 100644 --- a/src/sound/opnmidi/chips/gens_opn2.h +++ b/src/sound/opnmidi/chips/gens_opn2.h @@ -1,3 +1,23 @@ +/* + * Interfaces over Yamaha OPN2 (YM2612) chip emulators + * + * Copyright (C) 2017-2018 Vitaly Novichkov (Wohlstand) + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + #ifndef GENS_OPN2_H #define GENS_OPN2_H @@ -15,6 +35,7 @@ public: void setRate(uint32_t rate, uint32_t clock) override; void reset() override; void writeReg(uint32_t port, uint16_t addr, uint8_t data) override; + void writePan(uint16_t chan, uint8_t data) override; void nativePreGenerate() override {} void nativePostGenerate() override {} void nativeGenerateN(int16_t *output, size_t frames) override; diff --git a/src/sound/opnmidi/chips/mame/mame_ym2612fm.c b/src/sound/opnmidi/chips/mame/mame_ym2612fm.c index 0628a878a..46d1b012f 100644 --- a/src/sound/opnmidi/chips/mame/mame_ym2612fm.c +++ b/src/sound/opnmidi/chips/mame/mame_ym2612fm.c @@ -136,6 +136,7 @@ #include #include /* for memset */ #include /* for NULL */ +#include #include #include "mamedef.h" #include "mame_ym2612fm.h" @@ -543,6 +544,33 @@ static FILE *sample[1]; #endif +/* + * Pan law table + */ + +static const UINT16 panlawtable[] = +{ + 65535, 65529, 65514, 65489, 65454, 65409, 65354, 65289, + 65214, 65129, 65034, 64929, 64814, 64689, 64554, 64410, + 64255, 64091, 63917, 63733, 63540, 63336, 63123, 62901, + 62668, 62426, 62175, 61914, 61644, 61364, 61075, 60776, + 60468, 60151, 59825, 59489, 59145, 58791, 58428, 58057, + 57676, 57287, 56889, 56482, 56067, 55643, 55211, 54770, + 54320, 53863, 53397, 52923, 52441, 51951, 51453, 50947, + 50433, 49912, 49383, 48846, 48302, 47750, 47191, + 46340, /* Center left */ + 46340, /* Center right */ + 45472, 44885, 44291, 43690, 43083, 42469, 41848, 41221, + 40588, 39948, 39303, 38651, 37994, 37330, 36661, 35986, + 35306, 34621, 33930, 33234, 32533, 31827, 31116, 30400, + 29680, 28955, 28225, 27492, 26754, 26012, 25266, 24516, + 23762, 23005, 22244, 21480, 20713, 19942, 19169, 18392, + 17613, 16831, 16046, 15259, 14469, 13678, 12884, 12088, + 11291, 10492, 9691, 8888, 8085, 7280, 6473, 5666, + 4858, 4050, 3240, 2431, 1620, 810, 0 +}; + + /* struct describing a single operator (SLOT) */ typedef struct { @@ -608,6 +636,9 @@ typedef struct UINT8 kcode; /* key code: */ UINT32 block_fnum; /* current blk/fnum value for this slot (can be different betweeen slots of one channel in 3slot mode) */ UINT8 Muted; + + INT32 pan_volume_l; + INT32 pan_volume_r; } FM_CH; @@ -2457,34 +2488,40 @@ void ym2612_generate_one_native(void *chip, FMSAMPLE buffer[]) if (out_fm[5] > 8192) out_fm[5] = 8192; else if (out_fm[5] < -8192) out_fm[5] = -8192; +#define PANLAW_L(ch, chpan) (((out_fm[ch]>>0) * cch[ch].pan_volume_l / 65535) & OPN->pan[chpan]); +#define PANLAW_R(ch, chpan) (((out_fm[ch]>>0) * cch[ch].pan_volume_r / 65535) & OPN->pan[chpan]); + /* 6-channels mixing */ - lt = ((out_fm[0]>>0) & OPN->pan[0]); - rt = ((out_fm[0]>>0) & OPN->pan[1]); - lt += ((out_fm[1]>>0) & OPN->pan[2]); - rt += ((out_fm[1]>>0) & OPN->pan[3]); - lt += ((out_fm[2]>>0) & OPN->pan[4]); - rt += ((out_fm[2]>>0) & OPN->pan[5]); - lt += ((out_fm[3]>>0) & OPN->pan[6]); - rt += ((out_fm[3]>>0) & OPN->pan[7]); + lt = PANLAW_L(0, 0); + rt = PANLAW_R(0, 1); + lt += PANLAW_L(1, 2); + rt += PANLAW_R(1, 3); + lt += PANLAW_L(2, 4); + rt += PANLAW_R(2, 5); + lt += PANLAW_L(3, 6); + rt += PANLAW_R(3, 7); if (! F2612->dac_test) { - lt += ((out_fm[4]>>0) & OPN->pan[8]); - rt += ((out_fm[4]>>0) & OPN->pan[9]); + lt += PANLAW_L(4, 8); + rt += PANLAW_R(4, 9); } else { lt += dacout; - lt += dacout; + rt += dacout; } - lt += ((out_fm[5]>>0) & OPN->pan[10]); - rt += ((out_fm[5]>>0) & OPN->pan[11]); + lt += PANLAW_L(5, 10); + rt += PANLAW_R(5, 11); + +#undef PANLAW_L +#undef PANLAW_R /* Limit( lt, MAXOUT, MINOUT ); */ /* Limit( rt, MAXOUT, MINOUT ); */ - #ifdef SAVE_SAMPLE - SAVE_ALL_CHANNELS - #endif +#ifdef SAVE_SAMPLE + SAVE_ALL_CHANNELS +#endif /* buffering */ if (F2612->WaveOutMode & 0x01) @@ -2587,6 +2624,7 @@ void * ym2612_init(void *param, int clock, int rate, FM_TIMERHANDLER timer_handler,FM_IRQHANDLER IRQHandler) { YM2612 *F2612; + int i = 0; if (clock <= 0 || rate <= 0) return NULL; /* Forbid zero clock and sample rate */ @@ -2624,6 +2662,13 @@ void * ym2612_init(void *param, int clock, int rate, F2612->WaveOutMode = 0x01; else F2612->WaveOutMode = 0x03; + + for (i = 0; i < 6; i++) + { + F2612->CH[i].pan_volume_l = 46340; + F2612->CH[i].pan_volume_r = 46340; + } + /*hFile = fopen("YM2612.log", "wt"); fprintf(hFile, "Clock: %d, Sample Rate: %d\n", clock, rate); fprintf(hFile, "Sample\tCh 0\tCh 1\tCh 2\tCh 3\tCh 4\tCh 5\n"); @@ -2786,6 +2831,14 @@ int ym2612_write(void *chip, int a, UINT8 v) return F2612->OPN.ST.irq; } +void ym2612_write_pan(void *chip, int c, unsigned char v) +{ + YM2612 *F2612 = (YM2612 *)chip; + assert((c >= 0) && (c < 6)); + F2612->CH[c].pan_volume_l = panlawtable[v & 0x7F]; + F2612->CH[c].pan_volume_r = panlawtable[0x7F - (v & 0x7F)]; +} + UINT8 ym2612_read(void *chip,int a) { YM2612 *F2612 = (YM2612 *)chip; diff --git a/src/sound/opnmidi/chips/mame/mame_ym2612fm.h b/src/sound/opnmidi/chips/mame/mame_ym2612fm.h index de05236ef..5b209bb53 100644 --- a/src/sound/opnmidi/chips/mame/mame_ym2612fm.h +++ b/src/sound/opnmidi/chips/mame/mame_ym2612fm.h @@ -148,8 +148,9 @@ void ym2612_generate_one_native(void *chip, FMSAMPLE buffer[2]); /* void ym2612_post_generate(void *chip, int length); */ -int ym2612_write(void *chip, int a,unsigned char v); -unsigned char ym2612_read(void *chip,int a); +int ym2612_write(void *chip, int a, unsigned char v); +void ym2612_write_pan(void *chip, int c, unsigned char v); +unsigned char ym2612_read(void *chip, int a); int ym2612_timer_over(void *chip, int c ); void ym2612_postload(void *chip); diff --git a/src/sound/opnmidi/chips/mame_opn2.cpp b/src/sound/opnmidi/chips/mame_opn2.cpp index e9533131f..611988931 100644 --- a/src/sound/opnmidi/chips/mame_opn2.cpp +++ b/src/sound/opnmidi/chips/mame_opn2.cpp @@ -1,3 +1,23 @@ +/* + * Interfaces over Yamaha OPN2 (YM2612) chip emulators + * + * Copyright (C) 2017-2018 Vitaly Novichkov (Wohlstand) + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + #include "mame_opn2.h" #include "mame/mame_ym2612fm.h" #include @@ -36,6 +56,11 @@ void MameOPN2::writeReg(uint32_t port, uint16_t addr, uint8_t data) ym2612_write(chip, 1 + (int)(port) * 2, data); } +void MameOPN2::writePan(uint16_t chan, uint8_t data) +{ + ym2612_write_pan(chip, (int)chan, data); +} + void MameOPN2::nativePreGenerate() { void *chip = this->chip; diff --git a/src/sound/opnmidi/chips/mame_opn2.h b/src/sound/opnmidi/chips/mame_opn2.h index 832602476..2fe5e0374 100644 --- a/src/sound/opnmidi/chips/mame_opn2.h +++ b/src/sound/opnmidi/chips/mame_opn2.h @@ -1,3 +1,23 @@ +/* + * Interfaces over Yamaha OPN2 (YM2612) chip emulators + * + * Copyright (C) 2017-2018 Vitaly Novichkov (Wohlstand) + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + #ifndef MAME_OPN2_H #define MAME_OPN2_H @@ -14,6 +34,7 @@ public: void setRate(uint32_t rate, uint32_t clock) override; void reset() override; void writeReg(uint32_t port, uint16_t addr, uint8_t data) override; + void writePan(uint16_t chan, uint8_t data) override; void nativePreGenerate() override; void nativePostGenerate() override {} void nativeGenerate(int16_t *frame) override; diff --git a/src/sound/opnmidi/chips/nuked/ym3438.c b/src/sound/opnmidi/chips/nuked/ym3438.c index 019ca4cde..8c170b768 100644 --- a/src/sound/opnmidi/chips/nuked/ym3438.c +++ b/src/sound/opnmidi/chips/nuked/ym3438.c @@ -1,19 +1,19 @@ /* * Copyright (C) 2017 Alexey Khokholov (Nuke.YKT) * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. * - * This program is distributed in the hope that it will be useful, + * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * * Nuked OPN2(Yamaha YM3438) emulator. @@ -216,6 +216,32 @@ static const Bit32u fm_algorithm[4][6][8] = { } }; +/* + * Pan law table + */ + +static const Bit16u panlawtable[] = +{ + 65535, 65529, 65514, 65489, 65454, 65409, 65354, 65289, + 65214, 65129, 65034, 64929, 64814, 64689, 64554, 64410, + 64255, 64091, 63917, 63733, 63540, 63336, 63123, 62901, + 62668, 62426, 62175, 61914, 61644, 61364, 61075, 60776, + 60468, 60151, 59825, 59489, 59145, 58791, 58428, 58057, + 57676, 57287, 56889, 56482, 56067, 55643, 55211, 54770, + 54320, 53863, 53397, 52923, 52441, 51951, 51453, 50947, + 50433, 49912, 49383, 48846, 48302, 47750, 47191, + 46340, /* Center left */ + 46340, /* Center right */ + 45472, 44885, 44291, 43690, 43083, 42469, 41848, 41221, + 40588, 39948, 39303, 38651, 37994, 37330, 36661, 35986, + 35306, 34621, 33930, 33234, 32533, 31827, 31116, 30400, + 29680, 28955, 28225, 27492, 26754, 26012, 25266, 24516, + 23762, 23005, 22244, 21480, 20713, 19942, 19169, 18392, + 17613, 16831, 16046, 15259, 14469, 13678, 12884, 12088, + 11291, 10492, 9691, 8888, 8085, 7280, 6473, 5666, + 4858, 4050, 3240, 2431, 1620, 810, 0 +}; + static Bit32u chip_type = ym3438_type_discrete; void OPN2_DoIO(ym3438_t *chip) @@ -1204,6 +1230,8 @@ void OPN2_Reset(ym3438_t *chip, Bit32u rate, Bit32u clock) { chip->pan_l[i] = 1; chip->pan_r[i] = 1; + chip->pan_volume_l[i] = 46340; + chip->pan_volume_r[i] = 46340; } if (rate != 0) @@ -1426,6 +1454,13 @@ Bit8u OPN2_Read(ym3438_t *chip, Bit32u port) return 0; } + +void OPN2_WritePan(ym3438_t *chip, Bit32u channel, Bit8u data) +{ + chip->pan_volume_l[channel] = panlawtable[data & 0x7F]; + chip->pan_volume_r[channel] = panlawtable[0x7F - (data & 0x7F)]; +} + void OPN2_WriteBuffered(ym3438_t *chip, Bit32u port, Bit8u data) { Bit64u time1, time2; @@ -1466,6 +1501,7 @@ void OPN2_Generate(ym3438_t *chip, Bit16s *buf) Bit32u i; Bit16s buffer[2]; Bit32u mute; + Bit32s channel = -1; buf[0] = 0; buf[1] = 0; @@ -1476,21 +1512,27 @@ void OPN2_Generate(ym3438_t *chip, Bit16s *buf) { case 0: /* Ch 2 */ mute = chip->mute[1]; + channel = 1; break; case 1: /* Ch 6, DAC */ mute = chip->mute[5 + chip->dacen]; + channel = 5; break; case 2: /* Ch 4 */ mute = chip->mute[3]; + channel = 3; break; case 3: /* Ch 1 */ mute = chip->mute[0]; + channel = 0; break; case 4: /* Ch 5 */ mute = chip->mute[4]; + channel = 4; break; case 5: /* Ch 3 */ mute = chip->mute[2]; + channel = 2; break; default: mute = 0; @@ -1499,6 +1541,11 @@ void OPN2_Generate(ym3438_t *chip, Bit16s *buf) OPN2_Clock(chip, buffer); if (!mute) { + if (channel >= 0) + { + buffer[0] = buffer[0] * chip->pan_volume_l[channel] / 65535; + buffer[1] = buffer[1] * chip->pan_volume_r[channel] / 65535; + } buf[0] += buffer[0]; buf[1] += buffer[1]; } diff --git a/src/sound/opnmidi/chips/nuked/ym3438.h b/src/sound/opnmidi/chips/nuked/ym3438.h index c277f087b..2a3b5bf3d 100644 --- a/src/sound/opnmidi/chips/nuked/ym3438.h +++ b/src/sound/opnmidi/chips/nuked/ym3438.h @@ -1,19 +1,19 @@ /* * Copyright (C) 2017 Alexey Khokholov (Nuke.YKT) * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. * - * This program is distributed in the hope that it will be useful, + * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * * Nuked OPN2(Yamaha YM3438) emulator. @@ -213,6 +213,9 @@ typedef struct Bit32s oldsamples[2]; Bit32s samples[2]; + Bit32u pan_volume_l[6]; + Bit32u pan_volume_r[6]; + Bit64u writebuf_samplecnt; Bit32u writebuf_cur; Bit32u writebuf_last; @@ -231,6 +234,7 @@ Bit32u OPN2_ReadIRQPin(ym3438_t *chip); Bit8u OPN2_Read(ym3438_t *chip, Bit32u port); /*EXTRA*/ +void OPN2_WritePan(ym3438_t *chip, Bit32u channel, Bit8u data); void OPN2_WriteBuffered(ym3438_t *chip, Bit32u port, Bit8u data); void OPN2_Generate(ym3438_t *chip, Bit16s *buf); void OPN2_GenerateResampled(ym3438_t *chip, Bit16s *buf); diff --git a/src/sound/opnmidi/chips/nuked_opn2.cpp b/src/sound/opnmidi/chips/nuked_opn2.cpp index e452ccf0f..2c2ec46f1 100644 --- a/src/sound/opnmidi/chips/nuked_opn2.cpp +++ b/src/sound/opnmidi/chips/nuked_opn2.cpp @@ -1,3 +1,23 @@ +/* + * Interfaces over Yamaha OPN2 (YM2612) chip emulators + * + * Copyright (C) 2017-2018 Vitaly Novichkov (Wohlstand) + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + #include "nuked_opn2.h" #include "nuked/ym3438.h" #include @@ -37,6 +57,12 @@ void NukedOPN2::writeReg(uint32_t port, uint16_t addr, uint8_t data) //qDebug() << QString("%1: 0x%2 => 0x%3").arg(port).arg(addr, 2, 16, QChar('0')).arg(data, 2, 16, QChar('0')); } +void NukedOPN2::writePan(uint16_t chan, uint8_t data) +{ + ym3438_t *chip_r = reinterpret_cast(chip); + OPN2_WritePan(chip_r, (Bit32u)chan, data); +} + void NukedOPN2::nativeGenerate(int16_t *frame) { ym3438_t *chip_r = reinterpret_cast(chip); diff --git a/src/sound/opnmidi/chips/nuked_opn2.h b/src/sound/opnmidi/chips/nuked_opn2.h index 9eb239f48..ff5d25581 100644 --- a/src/sound/opnmidi/chips/nuked_opn2.h +++ b/src/sound/opnmidi/chips/nuked_opn2.h @@ -1,3 +1,23 @@ +/* + * Interfaces over Yamaha OPN2 (YM2612) chip emulators + * + * Copyright (C) 2017-2018 Vitaly Novichkov (Wohlstand) + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + #ifndef NUKED_OPN2_H #define NUKED_OPN2_H @@ -14,6 +34,7 @@ public: void setRate(uint32_t rate, uint32_t clock) override; void reset() override; void writeReg(uint32_t port, uint16_t addr, uint8_t data) override; + void writePan(uint16_t chan, uint8_t data) override; void nativePreGenerate() override {} void nativePostGenerate() override {} void nativeGenerate(int16_t *frame) override; diff --git a/src/sound/opnmidi/chips/opn_chip_base.h b/src/sound/opnmidi/chips/opn_chip_base.h index 5c532c920..fb02c32a4 100644 --- a/src/sound/opnmidi/chips/opn_chip_base.h +++ b/src/sound/opnmidi/chips/opn_chip_base.h @@ -1,3 +1,23 @@ +/* + * Interfaces over Yamaha OPN2 (YM2612) chip emulators + * + * Copyright (C) 2017-2018 Vitaly Novichkov (Wohlstand) + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + #ifndef ONP_CHIP_BASE_H #define ONP_CHIP_BASE_H @@ -13,25 +33,40 @@ class VResampler; #endif +#if defined(OPNMIDI_AUDIO_TICK_HANDLER) +extern void opn2_audioTickHandler(void *instance, uint32_t chipId, uint32_t rate); +#endif + class OPNChipBase { public: enum { nativeRate = 53267 }; protected: + uint32_t m_id; uint32_t m_rate; uint32_t m_clock; public: OPNChipBase(); virtual ~OPNChipBase(); + uint32_t chipId() const { return m_id; } + void setChipId(uint32_t id) { m_id = id; } + virtual bool canRunAtPcmRate() const = 0; virtual bool isRunningAtPcmRate() const = 0; virtual bool setRunningAtPcmRate(bool r) = 0; +#if defined(OPNMIDI_AUDIO_TICK_HANDLER) + virtual void setAudioTickHandlerInstance(void *instance) = 0; +#endif virtual void setRate(uint32_t rate, uint32_t clock) = 0; + virtual uint32_t effectiveRate() const = 0; virtual void reset() = 0; virtual void writeReg(uint32_t port, uint16_t addr, uint8_t data) = 0; + // extended + virtual void writePan(uint16_t addr, uint8_t data) { (void)addr; (void)data; } + virtual void nativePreGenerate() = 0; virtual void nativePostGenerate() = 0; virtual void nativeGenerate(int16_t *frame) = 0; @@ -58,8 +93,12 @@ public: bool isRunningAtPcmRate() const override; bool setRunningAtPcmRate(bool r) override; +#if defined(OPNMIDI_AUDIO_TICK_HANDLER) + void setAudioTickHandlerInstance(void *instance); +#endif virtual void setRate(uint32_t rate, uint32_t clock) override; + uint32_t effectiveRate() const override; virtual void reset() override; void generate(int16_t *output, size_t frames) override; void generateAndMix(int16_t *output, size_t frames) override; @@ -67,6 +106,10 @@ public: void generateAndMix32(int32_t *output, size_t frames) override; private: bool m_runningAtPcmRate; +#if defined(OPNMIDI_AUDIO_TICK_HANDLER) + void *m_audioTickHandlerInstance; +#endif + void nativeTick(int16_t *frame); void setupResampler(uint32_t rate); void resetResampler(); void resampledGenerate(int32_t *output); diff --git a/src/sound/opnmidi/chips/opn_chip_base.tcc b/src/sound/opnmidi/chips/opn_chip_base.tcc index 4915e802d..1302ea5bf 100644 --- a/src/sound/opnmidi/chips/opn_chip_base.tcc +++ b/src/sound/opnmidi/chips/opn_chip_base.tcc @@ -20,6 +20,7 @@ /* OPNChipBase */ inline OPNChipBase::OPNChipBase() : + m_id(0), m_rate(44100), m_clock(7670454) { @@ -35,6 +36,10 @@ template OPNChipBaseT::OPNChipBaseT() : OPNChipBase(), m_runningAtPcmRate(false) +#if defined(OPNMIDI_AUDIO_TICK_HANDLER) + , + m_audioTickHandlerInstance(NULL) +#endif { #if defined(OPNMIDI_ENABLE_HQ_RESAMPLER) m_resampler = new VResampler; @@ -69,6 +74,14 @@ bool OPNChipBaseT::setRunningAtPcmRate(bool r) return true; } +#if defined(OPNMIDI_AUDIO_TICK_HANDLER) +template +void OPNChipBaseT::setAudioTickHandlerInstance(void *instance) +{ + m_audioTickHandlerInstance = instance; +} +#endif + template void OPNChipBaseT::setRate(uint32_t rate, uint32_t clock) { @@ -81,6 +94,12 @@ void OPNChipBaseT::setRate(uint32_t rate, uint32_t clock) resetResampler(); } +template +uint32_t OPNChipBaseT::effectiveRate() const +{ + return m_runningAtPcmRate ? m_rate : (uint32_t)nativeRate; +} + template void OPNChipBaseT::reset() { @@ -152,6 +171,15 @@ void OPNChipBaseT::generateAndMix32(int32_t *output, size_t frames) static_cast(this)->nativePostGenerate(); } +template +void OPNChipBaseT::nativeTick(int16_t *frame) +{ +#if defined(OPNMIDI_AUDIO_TICK_HANDLER) + opn2_audioTickHandler(m_audioTickHandlerInstance, m_id, effectiveRate()); +#endif + static_cast(this)->nativeGenerate(frame); +} + template void OPNChipBaseT::setupResampler(uint32_t rate) { @@ -184,7 +212,7 @@ void OPNChipBaseT::resampledGenerate(int32_t *output) if(UNLIKELY(m_runningAtPcmRate)) { int16_t in[2]; - static_cast(this)->nativeGenerate(in); + static_cast(this)->nativeTick(in); output[0] = (int32_t)in[0] * T::resamplerPreAmplify / T::resamplerPostAttenuate; output[1] = (int32_t)in[1] * T::resamplerPreAmplify / T::resamplerPostAttenuate; return; @@ -202,7 +230,7 @@ void OPNChipBaseT::resampledGenerate(int32_t *output) while(rsm->process(), rsm->out_count != 0) { int16_t in[2]; - static_cast(this)->nativeGenerate(in); + static_cast(this)->nativeTick(in); f_in[0] = scale * (float)in[0]; f_in[1] = scale * (float)in[1]; rsm->inp_count = 1; @@ -210,8 +238,8 @@ void OPNChipBaseT::resampledGenerate(int32_t *output) rsm->out_count = 1; rsm->out_data = f_out; } - output[0] = std::lround(f_out[0]); - output[1] = std::lround(f_out[1]); + output[0] = static_cast(std::lround(f_out[0])); + output[1] = static_cast(std::lround(f_out[1])); } #else template @@ -220,7 +248,7 @@ void OPNChipBaseT::resampledGenerate(int32_t *output) if(UNLIKELY(m_runningAtPcmRate)) { int16_t in[2]; - static_cast(this)->nativeGenerate(in); + static_cast(this)->nativeTick(in); output[0] = (int32_t)in[0] * T::resamplerPreAmplify / T::resamplerPostAttenuate; output[1] = (int32_t)in[1] * T::resamplerPreAmplify / T::resamplerPostAttenuate; return; @@ -233,7 +261,7 @@ void OPNChipBaseT::resampledGenerate(int32_t *output) m_oldsamples[0] = m_samples[0]; m_oldsamples[1] = m_samples[1]; int16_t buffer[2]; - static_cast(this)->nativeGenerate(buffer); + static_cast(this)->nativeTick(buffer); m_samples[0] = buffer[0] * T::resamplerPreAmplify; m_samples[1] = buffer[1] * T::resamplerPreAmplify; samplecnt -= rateratio; diff --git a/src/sound/opnmidi/file_reader.hpp b/src/sound/opnmidi/file_reader.hpp new file mode 100644 index 000000000..7d13262a1 --- /dev/null +++ b/src/sound/opnmidi/file_reader.hpp @@ -0,0 +1,300 @@ +/* + * FileAndMemoryReader - a tiny helper to utify file reading from a disk and memory block + * + * Copyright (c) 2015-2018 Vitaly Novichkov + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +#pragma once +#ifndef FILE_AND_MEM_READER_HHHH +#define FILE_AND_MEM_READER_HHHH + +#include // std::string +#include // std::fopen, std::fread, std::fseek, std::ftell, std::fclose, std::feof +#include // uint*_t +#include // size_t and friends +#ifdef _WIN32 +#define NOMINMAX 1 +#include // std::strlen +#include // MultiByteToWideChar +#endif + +/** + * @brief A little class gives able to read filedata from disk and also from a memory segment + */ +class FileAndMemReader +{ + //! Currently loaded filename (empty for a memory blocks) + std::string m_file_name; + //! File reader descriptor + std::FILE *m_fp; + + //! Memory pointer descriptor + const void *m_mp; + //! Size of memory block + size_t m_mp_size; + //! Cursor position in the memory block + size_t m_mp_tell; + +public: + /** + * @brief Relation direction + */ + enum relTo + { + //! At begin position + SET = SEEK_SET, + //! At current position + CUR = SEEK_CUR, + //! At end position + END = SEEK_END + }; + + /** + * @brief C.O.: It's a constructor! + */ + FileAndMemReader() : + m_fp(NULL), + m_mp(NULL), + m_mp_size(0), + m_mp_tell(0) + {} + + /** + * @brief C.O.: It's a destructor! + */ + ~FileAndMemReader() + { + close(); + } + + /** + * @brief Open file from a disk + * @param path Path to the file in UTF-8 (even on Windows!) + */ + void openFile(const char *path) + { + if(m_fp) + this->close();//Close previously opened file first! +#if !defined(_WIN32) || defined(__WATCOMC__) + m_fp = std::fopen(path, "rb"); +#else + wchar_t widePath[MAX_PATH]; + int size = MultiByteToWideChar(CP_UTF8, 0, path, static_cast(std::strlen(path)), widePath, MAX_PATH); + widePath[size] = '\0'; + m_fp = _wfopen(widePath, L"rb"); +#endif + m_file_name = path; + m_mp = NULL; + m_mp_size = 0; + m_mp_tell = 0; + } + + /** + * @brief Open file from memory block + * @param mem Pointer to the memory block + * @param lenght Size of given block + */ + void openData(const void *mem, size_t lenght) + { + if(m_fp) + this->close();//Close previously opened file first! + m_fp = NULL; + m_mp = mem; + m_mp_size = lenght; + m_mp_tell = 0; + } + + /** + * @brief Seek to given position + * @param pos Offset or position + * @param rel_to Relation (at begin, at current, or at end) + */ + void seek(long pos, int rel_to) + { + if(!this->isValid()) + return; + + if(m_fp)//If a file + { + std::fseek(m_fp, pos, rel_to); + } + else//If a memory block + { + switch(rel_to) + { + case SET: + m_mp_tell = static_cast(pos); + break; + + case END: + m_mp_tell = m_mp_size - static_cast(pos); + break; + + case CUR: + m_mp_tell = m_mp_tell + static_cast(pos); + break; + } + + if(m_mp_tell > m_mp_size) + m_mp_tell = m_mp_size; + } + } + + /** + * @brief Seek to given position (unsigned integer 64 as relation. Negative values not supported) + * @param pos Offset or position + * @param rel_to Relation (at begin, at current, or at end) + */ + inline void seeku(uint64_t pos, int rel_to) + { + this->seek(static_cast(pos), rel_to); + } + + /** + * @brief Read the buffer from a file + * @param buf Pointer to the destination memory block + * @param num Number of elements + * @param size Size of one element + * @return Size + */ + size_t read(void *buf, size_t num, size_t size) + { + if(!this->isValid()) + return 0; + if(m_fp) + return std::fread(buf, num, size, m_fp); + else + { + size_t pos = 0; + size_t maxSize = static_cast(size * num); + + while((pos < maxSize) && (m_mp_tell < m_mp_size)) + { + reinterpret_cast(buf)[pos] = reinterpret_cast(m_mp)[m_mp_tell]; + m_mp_tell++; + pos++; + } + + return pos / num; + } + } + + /** + * @brief Get one byte and seek forward + * @return Readed byte or EOF (a.k.a. -1) + */ + int getc() + { + if(!this->isValid()) + return -1; + if(m_fp)//If a file + { + return std::getc(m_fp); + } + else //If a memory block + { + if(m_mp_tell >= m_mp_size) + return -1; + int x = reinterpret_cast(m_mp)[m_mp_tell]; + m_mp_tell++; + return x; + } + } + + /** + * @brief Returns current offset of cursor in a file + * @return Offset position + */ + size_t tell() + { + if(!this->isValid()) + return 0; + if(m_fp)//If a file + return static_cast(std::ftell(m_fp)); + else//If a memory block + return m_mp_tell; + } + + /** + * @brief Close the file + */ + void close() + { + if(m_fp) + std::fclose(m_fp); + + m_fp = NULL; + m_mp = NULL; + m_mp_size = 0; + m_mp_tell = 0; + } + + /** + * @brief Is file instance valid + * @return true if vaild + */ + bool isValid() + { + return (m_fp) || (m_mp); + } + + /** + * @brief Is End Of File? + * @return true if end of file was reached + */ + bool eof() + { + if(!this->isValid()) + return true; + if(m_fp) + return (std::feof(m_fp) != 0); + else + return m_mp_tell >= m_mp_size; + } + + /** + * @brief Get a current file name + * @return File name of currently loaded file + */ + const std::string &fileName() + { + return m_file_name; + } + + /** + * @brief Retrieve file size + * @return Size of file in bytes + */ + size_t fileSize() + { + if(!this->isValid()) + return 0; + if(!m_fp) + return m_mp_size; //Size of memory block is well known + size_t old_pos = this->tell(); + seek(0l, FileAndMemReader::END); + size_t file_size = this->tell(); + seek(static_cast(old_pos), FileAndMemReader::SET); + return file_size; + } +}; + +#endif /* FILE_AND_MEM_READER_HHHH */ diff --git a/src/sound/opnmidi/fraction.hpp b/src/sound/opnmidi/fraction.hpp deleted file mode 100644 index 7aab95bd2..000000000 --- a/src/sound/opnmidi/fraction.hpp +++ /dev/null @@ -1,214 +0,0 @@ -#ifndef bqw_fraction_h -#define bqw_fraction_h - -#include -#include - -/* Fraction number handling. - * Copyright (C) 1992,2001 Bisqwit (http://iki.fi/bisqwit/) - */ - -template -class fraction -{ - inttype num1, num2; - typedef fraction self; - void Optim(); - - #if 1 - inline void Debug(char, const self &) { } - #else - inline void Debug(char op, const self &b) - { - cerr << nom() << '/' << denom() << ' ' << op - << ' ' << b.nom() << '/' << denom() - << ":\n"; - } - #endif -public: - void set(inttype n, inttype d) { num1=n; num2=d; Optim(); } - - fraction() : num1(0), num2(1) { } - fraction(inttype value) : num1(value), num2(1) { } - fraction(inttype n, inttype d) : num1(n), num2(d) { } - fraction(int value) : num1(value), num2(1) { } - template - explicit fraction(const floattype value) { operator= (value); } - inline double value() const {return nom() / (double)denom(); } - inline long double valuel() const {return nom() / (long double)denom(); } - self &operator+= (const inttype &value) { num1+=value*denom(); Optim(); return *this; } - self &operator-= (const inttype &value) { num1-=value*denom(); Optim(); return *this; } - self &operator*= (const inttype &value) { num1*=value; Optim(); return *this; } - self &operator/= (const inttype &value) { num2*=value; Optim(); return *this; } - self &operator+= (const self &b); - self &operator-= (const self &b); - self &operator*= (const self &b) { Debug('*',b);num1*=b.nom(); num2*=b.denom(); Optim(); return *this; } - self &operator/= (const self &b) { Debug('/',b);num1*=b.denom(); num2*=b.nom(); Optim(); return *this; } - self operator- () const { return self(-num1, num2); } - -#define fraction_blah_func(op1, op2) \ - self operator op1 (const self &b) const { self tmp(*this); tmp op2 b; return tmp; } - - fraction_blah_func( +, += ) - fraction_blah_func( -, -= ) - fraction_blah_func( /, /= ) - fraction_blah_func( *, *= ) - -#undef fraction_blah_func -#define fraction_blah_func(op) \ - bool operator op(const self &b) const { return value() op b.value(); } \ - bool operator op(inttype b) const { return value() op b; } - - fraction_blah_func( < ) - fraction_blah_func( > ) - fraction_blah_func( <= ) - fraction_blah_func( >= ) - -#undef fraction_blah_func - - const inttype &nom() const { return num1; } - const inttype &denom() const { return num2; } - inline bool operator == (inttype b) const { return denom() == 1 && nom() == b; } - inline bool operator != (inttype b) const { return denom() != 1 || nom() != b; } - inline bool operator == (const self &b) const { return denom()==b.denom() && nom()==b.nom(); } - inline bool operator != (const self &b) const { return denom()!=b.denom() || nom()!=b.nom(); } - //operator bool () const { return nom() != 0; } - inline bool negative() const { return (nom() < 0) ^ (denom() < 0); } - - self &operator= (const inttype value) { num2=1; num1=value; return *this; } - //self &operator= (int value) { num2=1; num1=value; return *this; } - - self &operator= (double orig) { return *this = (long double)orig; } - self &operator= (long double orig); -}; - -#ifdef _MSC_VER -#pragma warning(disable:4146) -#endif - -template -void fraction::Optim() -{ - /* Euclidean algorithm */ - inttype n1, n2, nn1, nn2; - - nn1 = std::numeric_limits::is_signed ? (num1 >= 0 ? num1 : -num1) : num1; - nn2 = std::numeric_limits::is_signed ? (num2 >= 0 ? num2 : -num2) : num2; - - if(nn1 < nn2) - n1 = num1, n2 = num2; - else - n1 = num2, n2 = num1; - - if(!num1) { num2 = 1; return; } - for(;;) - { - //fprintf(stderr, "%d/%d: n1=%d,n2=%d\n", nom(),denom(),n1,n2); - inttype tmp = n2 % n1; - if(!tmp)break; - n2 = n1; - n1 = tmp; - } - num1 /= n1; - num2 /= n1; - //fprintf(stderr, "result: %d/%d\n\n", nom(), denom()); -} - -#ifdef _MSC_VER -#pragma warning(default:4146) -#endif - -template -inline const fraction abs(const fraction &f) -{ - return fraction(abs(f.nom()), abs(f.denom())); -} - -#define fraction_blah_func(op) \ - template \ - fraction operator op \ - (const inttype bla, const fraction &b) \ - { return fraction (bla) op b; } -fraction_blah_func( + ) -fraction_blah_func( - ) -fraction_blah_func( * ) -fraction_blah_func( / ) -#undef fraction_blah_func - -#define fraction_blah_func(op1, op2) \ - template \ - fraction &fraction::operator op2 (const fraction &b) \ - { \ - inttype newnom = nom()*b.denom() op1 denom()*b.nom(); \ - num2 *= b.denom(); \ - num1 = newnom; \ - Optim(); \ - return *this; \ - } -fraction_blah_func( +, += ) -fraction_blah_func( -, -= ) -#undef fraction_blah_func - -template -fraction &fraction::operator= (long double orig) -{ - if(orig == 0.0) - { - set(0, 0); - return *this; - } - - inttype cf[25]; - for(int maxdepth=1; maxdepth<25; ++maxdepth) - { - inttype u,v; - long double virhe, a=orig; - int i, viim; - - for(i = 0; i < maxdepth; ++i) - { - cf[i] = (inttype)a; - if(cf[i]-1 > cf[i])break; - a = 1.0 / (a - cf[i]); - } - - for(viim=i-1; i < maxdepth; ++i) - cf[i] = 0; - - u = cf[viim]; - v = 1; - for(i = viim-1; i >= 0; --i) - { - inttype w = cf[i] * u + v; - v = u; - u = w; - } - - virhe = (orig - (u / (long double)v)) / orig; - - set(u, v); - //if(verbose > 4) - // cerr << "Guess: " << *this << " - error = " << virhe*100 << "%\n"; - - if(virhe < 1e-8 && virhe > -1e-8)break; - } - - //if(verbose > 4) - //{ - // cerr << "Fraction=" << orig << ": " << *this << endl; - //} - - return *this; -} - - -/* -template -ostream &operator << (ostream &dest, const fraction &m) -{ - if(m.denom() == (inttype)1) return dest << m.nom(); - return dest << m.nom() << '/' << m.denom(); -} -*/ - -#endif diff --git a/src/sound/opnmidi/opnbank.h b/src/sound/opnmidi/opnbank.h index 521616ae1..0e005a3db 100644 --- a/src/sound/opnmidi/opnbank.h +++ b/src/sound/opnmidi/opnbank.h @@ -110,6 +110,7 @@ struct opnInstMeta2 uint16_t ms_sound_kon; // Number of milliseconds it produces sound; uint16_t ms_sound_koff; double fine_tune; + int8_t midi_velocity_offset; #if 0 opnInstMeta2() {} explicit opnInstMeta2(const opnInstMeta &d); @@ -120,6 +121,16 @@ OPNDATA_BYTE_COMPARABLE(struct opnInstMeta2) #undef OPNDATA_BYTE_COMPARABLE #pragma pack(pop) +/** + * @brief Bank global setup + */ +struct OpnBankSetup +{ + int volumeModel; + int lfoEnable; + int lfoFrequency; +}; + #if 0 /** * @brief Conversion of storage formats @@ -127,11 +138,21 @@ OPNDATA_BYTE_COMPARABLE(struct opnInstMeta2) inline opnInstMeta2::opnInstMeta2(const opnInstMeta &d) : tone(d.tone), flags(d.flags), ms_sound_kon(d.ms_sound_kon), ms_sound_koff(d.ms_sound_koff), - fine_tune(d.fine_tune) + fine_tune(d.fine_tune), midi_velocity_offset(d.midi_velocity_offset) { opn[0] = ::opn[d.opnno1]; opn[1] = ::opn[d.opnno2]; } #endif +/** + * @brief Convert external instrument to internal instrument + */ +void cvt_OPNI_to_FMIns(opnInstMeta2 &dst, const struct OPN2_Instrument &src); + +/** + * @brief Convert internal instrument to external instrument + */ +void cvt_FMIns_to_OPNI(struct OPN2_Instrument &dst, const opnInstMeta2 &src); + #endif // OPNMIDI_OPNBANK_H diff --git a/src/sound/opnmidi/opnmidi.cpp b/src/sound/opnmidi/opnmidi.cpp index eab98eee7..2f35e2a05 100644 --- a/src/sound/opnmidi/opnmidi.cpp +++ b/src/sound/opnmidi/opnmidi.cpp @@ -23,8 +23,9 @@ #include "opnmidi_private.hpp" -#define MaxCards 100 -#define MaxCards_STR "100" +/* Unify MIDI player casting and interface between ADLMIDI and OPNMIDI */ +#define GET_MIDI_PLAYER(device) reinterpret_cast((device)->opn2_midiPlayer) +typedef OPNMIDIplay MidiPlayer; static OPN2_Version opn2_version = { OPNMIDI_VERSION_MAJOR, @@ -51,7 +52,7 @@ OPNMIDI_EXPORT struct OPN2_MIDIPlayer *opn2_init(long sample_rate) return NULL; } - OPNMIDIplay *player = new OPNMIDIplay(static_cast(sample_rate)); + OPNMIDIplay *player = new(std::nothrow) OPNMIDIplay(static_cast(sample_rate)); if(!player) { free(midi_device); @@ -59,44 +60,208 @@ OPNMIDI_EXPORT struct OPN2_MIDIPlayer *opn2_init(long sample_rate) return NULL; } midi_device->opn2_midiPlayer = player; - opn2RefreshNumCards(midi_device); return midi_device; } +OPNMIDI_EXPORT int opn2_setDeviceIdentifier(OPN2_MIDIPlayer *device, unsigned id) +{ + if(!device || id > 0x0f) + return -1; + MidiPlayer *play = GET_MIDI_PLAYER(device); + assert(play); + play->setDeviceId(static_cast(id)); + return 0; +} + OPNMIDI_EXPORT int opn2_setNumChips(OPN2_MIDIPlayer *device, int numCards) { if(device == NULL) return -2; - OPNMIDIplay *play = reinterpret_cast(device->opn2_midiPlayer); - play->m_setup.NumCards = static_cast(numCards); - if(play->m_setup.NumCards < 1 || play->m_setup.NumCards > MaxCards) + MidiPlayer *play = GET_MIDI_PLAYER(device); + assert(play); + play->m_setup.numChips = static_cast(numCards); + if(play->m_setup.numChips < 1 || play->m_setup.numChips > OPN_MAX_CHIPS) { - play->setErrorString("number of chips may only be 1.." MaxCards_STR ".\n"); + play->setErrorString("number of chips may only be 1.." OPN_MAX_CHIPS_STR ".\n"); return -1; } - play->opn.NumCards = play->m_setup.NumCards; - opn2_reset(device); + if(!play->m_synth.setupLocked()) + { + play->m_synth.m_numChips = play->m_setup.numChips; + play->partialReset(); + } - return opn2RefreshNumCards(device); + return 0; } OPNMIDI_EXPORT int opn2_getNumChips(struct OPN2_MIDIPlayer *device) { if(device == NULL) return -2; - OPNMIDIplay *play = reinterpret_cast(device->opn2_midiPlayer); - if(play) - return (int)play->m_setup.NumCards; - return -2; + MidiPlayer *play = GET_MIDI_PLAYER(device); + assert(play); + return (int)play->m_setup.numChips; +} + +OPNMIDI_EXPORT int opn2_getNumChipsObtained(struct OPN2_MIDIPlayer *device) +{ + if(device == NULL) + return -2; + MidiPlayer *play = GET_MIDI_PLAYER(device); + assert(play); + return (int)play->m_synth.m_numChips; +} + + +OPNMIDI_EXPORT int opn2_reserveBanks(OPN2_MIDIPlayer *device, unsigned banks) +{ + if(!device) + return -1; + MidiPlayer *play = GET_MIDI_PLAYER(device); + assert(play); + OPN2::BankMap &map = play->m_synth.m_insBanks; + map.reserve(banks); + return (int)map.capacity(); +} + +OPNMIDI_EXPORT int opn2_getBank(OPN2_MIDIPlayer *device, const OPN2_BankId *idp, int flags, OPN2_Bank *bank) +{ + if(!device || !idp || !bank) + return -1; + + OPN2_BankId id = *idp; + if(id.lsb > 127 || id.msb > 127 || id.percussive > 1) + return -1; + size_t idnumber = ((id.msb << 8) | id.lsb | (id.percussive ? size_t(OPN2::PercussionTag) : 0)); + + MidiPlayer *play = GET_MIDI_PLAYER(device); + assert(play); + OPN2::BankMap &map = play->m_synth.m_insBanks; + + OPN2::BankMap::iterator it; + if(!(flags & OPNMIDI_Bank_Create)) + { + it = map.find(idnumber); + if(it == map.end()) + return -1; + } + else + { + std::pair value; + value.first = idnumber; + memset(&value.second, 0, sizeof(value.second)); + for (unsigned i = 0; i < 128; ++i) + value.second.ins[i].flags = opnInstMeta::Flag_NoSound; + + std::pair ir; + if(flags & OPNMIDI_Bank_CreateRt) + { + ir = map.insert(value, OPN2::BankMap::do_not_expand_t()); + if(ir.first == map.end()) + return -1; + } + else + ir = map.insert(value); + it = ir.first; + } + + it.to_ptrs(bank->pointer); + return 0; +} + +OPNMIDI_EXPORT int opn2_getBankId(OPN2_MIDIPlayer *device, const OPN2_Bank *bank, OPN2_BankId *id) +{ + if(!device || !bank) + return -1; + + OPN2::BankMap::iterator it = OPN2::BankMap::iterator::from_ptrs(bank->pointer); + OPN2::BankMap::key_type idnumber = it->first; + id->msb = (idnumber >> 8) & 127; + id->lsb = idnumber & 127; + id->percussive = (idnumber & OPN2::PercussionTag) ? 1 : 0; + return 0; +} + +OPNMIDI_EXPORT int opn2_removeBank(OPN2_MIDIPlayer *device, OPN2_Bank *bank) +{ + if(!device || !bank) + return -1; + + MidiPlayer *play = GET_MIDI_PLAYER(device); + assert(play); + OPN2::BankMap &map = play->m_synth.m_insBanks; + OPN2::BankMap::iterator it = OPN2::BankMap::iterator::from_ptrs(bank->pointer); + size_t size = map.size(); + map.erase(it); + return (map.size() != size) ? 0 : -1; +} + +OPNMIDI_EXPORT int opn2_getFirstBank(OPN2_MIDIPlayer *device, OPN2_Bank *bank) +{ + if(!device) + return -1; + + MidiPlayer *play = GET_MIDI_PLAYER(device); + assert(play); + OPN2::BankMap &map = play->m_synth.m_insBanks; + + OPN2::BankMap::iterator it = map.begin(); + if(it == map.end()) + return -1; + + it.to_ptrs(bank->pointer); + return 0; +} + +OPNMIDI_EXPORT int opn2_getNextBank(OPN2_MIDIPlayer *device, OPN2_Bank *bank) +{ + if(!device) + return -1; + + MidiPlayer *play = GET_MIDI_PLAYER(device); + assert(play); + OPN2::BankMap &map = play->m_synth.m_insBanks; + + OPN2::BankMap::iterator it = OPN2::BankMap::iterator::from_ptrs(bank->pointer); + if(++it == map.end()) + return -1; + + it.to_ptrs(bank->pointer); + return 0; +} + +OPNMIDI_EXPORT int opn2_getInstrument(OPN2_MIDIPlayer *device, const OPN2_Bank *bank, unsigned index, OPN2_Instrument *ins) +{ + if(!device || !bank || index > 127 || !ins) + return -1; + + OPN2::BankMap::iterator it = OPN2::BankMap::iterator::from_ptrs(bank->pointer); + cvt_FMIns_to_OPNI(*ins, it->second.ins[index]); + ins->version = 0; + return 0; +} + +OPNMIDI_EXPORT int opn2_setInstrument(OPN2_MIDIPlayer *device, OPN2_Bank *bank, unsigned index, const OPN2_Instrument *ins) +{ + if(!device || !bank || index > 127 || !ins) + return -1; + + if(ins->version != 0) + return -1; + + OPN2::BankMap::iterator it = OPN2::BankMap::iterator::from_ptrs(bank->pointer); + cvt_OPNI_to_FMIns(it->second.ins[index], *ins); + return 0; } OPNMIDI_EXPORT int opn2_openBankFile(OPN2_MIDIPlayer *device, const char *filePath) { - if(device && device->opn2_midiPlayer) + if(device) { - OPNMIDIplay *play = reinterpret_cast(device->opn2_midiPlayer); + MidiPlayer *play = GET_MIDI_PLAYER(device); + assert(play); play->m_setup.tick_skip_samples_delay = 0; if(!play->LoadBank(filePath)) { @@ -105,7 +270,8 @@ OPNMIDI_EXPORT int opn2_openBankFile(OPN2_MIDIPlayer *device, const char *filePa play->setErrorString("OPN2 MIDI: Can't load file"); return -1; } - else return opn2RefreshNumCards(device); + else + return 0; } OPN2MIDI_ErrorString = "Can't load file: OPN2 MIDI is not initialized"; return -1; @@ -113,9 +279,10 @@ OPNMIDI_EXPORT int opn2_openBankFile(OPN2_MIDIPlayer *device, const char *filePa OPNMIDI_EXPORT int opn2_openBankData(OPN2_MIDIPlayer *device, const void *mem, long size) { - if(device && device->opn2_midiPlayer) + if(device) { - OPNMIDIplay *play = reinterpret_cast(device->opn2_midiPlayer); + MidiPlayer *play = GET_MIDI_PLAYER(device); + assert(play); play->m_setup.tick_skip_samples_delay = 0; if(!play->LoadBank(mem, static_cast(size))) { @@ -131,53 +298,135 @@ OPNMIDI_EXPORT int opn2_openBankData(OPN2_MIDIPlayer *device, const void *mem, l return -1; } -OPNMIDI_EXPORT void opn2_setScaleModulators(OPN2_MIDIPlayer *device, int smod) +OPNMIDI_EXPORT void opn2_setLfoEnabled(struct OPN2_MIDIPlayer *device, int lfoEnable) { if(!device) return; - OPNMIDIplay *play = reinterpret_cast(device->opn2_midiPlayer); + MidiPlayer *play = GET_MIDI_PLAYER(device); + assert(play); + play->m_setup.lfoEnable = lfoEnable; + play->m_synth.m_lfoEnable = (lfoEnable < 0 ? + play->m_synth.m_insBankSetup.lfoEnable : + play->m_setup.lfoEnable) != 0; + play->m_synth.commitLFOSetup(); +} + +OPNMIDI_EXPORT int opn2_getLfoEnabled(struct OPN2_MIDIPlayer *device) +{ + if(!device) return -1; + MidiPlayer *play = GET_MIDI_PLAYER(device); + assert(play); + return play->m_synth.m_lfoEnable; +} + +OPNMIDI_EXPORT void opn2_setLfoFrequency(struct OPN2_MIDIPlayer *device, int lfoFrequency) +{ + if(!device) return; + MidiPlayer *play = GET_MIDI_PLAYER(device); + assert(play); + play->m_setup.lfoFrequency = lfoFrequency; + play->m_synth.m_lfoFrequency = lfoFrequency < 0 ? + play->m_synth.m_insBankSetup.lfoFrequency : + (uint8_t)play->m_setup.lfoFrequency; + play->m_synth.commitLFOSetup(); +} + +OPNMIDI_EXPORT int opn2_getLfoFrequency(struct OPN2_MIDIPlayer *device) +{ + if(!device) return -1; + MidiPlayer *play = GET_MIDI_PLAYER(device); + assert(play); + return play->m_synth.m_lfoFrequency; +} + +OPNMIDI_EXPORT void opn2_setScaleModulators(OPN2_MIDIPlayer *device, int smod) +{ + if(!device) + return; + MidiPlayer *play = GET_MIDI_PLAYER(device); + assert(play); play->m_setup.ScaleModulators = smod; - play->opn.ScaleModulators = (play->m_setup.ScaleModulators != 0); + play->m_synth.m_scaleModulators = (play->m_setup.ScaleModulators != 0); } OPNMIDI_EXPORT void opn2_setFullRangeBrightness(struct OPN2_MIDIPlayer *device, int fr_brightness) { - if(!device) return; - OPNMIDIplay *play = reinterpret_cast(device->opn2_midiPlayer); + if(!device) + return; + MidiPlayer *play = GET_MIDI_PLAYER(device); + assert(play); play->m_setup.fullRangeBrightnessCC74 = (fr_brightness != 0); } OPNMIDI_EXPORT void opn2_setLoopEnabled(OPN2_MIDIPlayer *device, int loopEn) { - if(!device) return; - OPNMIDIplay *play = reinterpret_cast(device->opn2_midiPlayer); - play->m_setup.loopingIsEnabled = (loopEn != 0); + if(!device) + return; + MidiPlayer *play = GET_MIDI_PLAYER(device); + assert(play); +#ifndef OPNMIDI_DISABLE_MIDI_SEQUENCER + play->m_sequencer.setLoopEnabled(loopEn != 0); +#else + ADL_UNUSED(loopEn); +#endif +} + +OPNMIDI_EXPORT void opn2_setSoftPanEnabled(OPN2_MIDIPlayer *device, int softPanEn) +{ + if(!device) + return; + MidiPlayer *play = GET_MIDI_PLAYER(device); + assert(play); + play->m_synth.m_softPanning = (softPanEn != 0); } /* !!!DEPRECATED!!! */ OPNMIDI_EXPORT void opn2_setLogarithmicVolumes(struct OPN2_MIDIPlayer *device, int logvol) { - if(!device) return; - OPNMIDIplay *play = reinterpret_cast(device->opn2_midiPlayer); + if(!device) + return; + MidiPlayer *play = GET_MIDI_PLAYER(device); + assert(play); play->m_setup.LogarithmicVolumes = static_cast(logvol); - if(play->m_setup.LogarithmicVolumes != 0) - play->opn.ChangeVolumeRangesModel(OPNMIDI_VolumeModel_CMF); - else - play->opn.ChangeVolumeRangesModel(static_cast(play->m_setup.VolumeModel)); + if(!play->m_synth.setupLocked()) + { + if(play->m_setup.LogarithmicVolumes != 0) + play->m_synth.setVolumeScaleModel(OPNMIDI_VolumeModel_NativeOPN2); + else + play->m_synth.setVolumeScaleModel(static_cast(play->m_setup.VolumeModel)); + } } OPNMIDI_EXPORT void opn2_setVolumeRangeModel(OPN2_MIDIPlayer *device, int volumeModel) { - if(!device) return; - OPNMIDIplay *play = reinterpret_cast(device->opn2_midiPlayer); + if(!device) + return; + MidiPlayer *play = GET_MIDI_PLAYER(device); + assert(play); play->m_setup.VolumeModel = volumeModel; - play->opn.ChangeVolumeRangesModel(static_cast(volumeModel)); + if(!play->m_synth.setupLocked()) + { + if(play->m_setup.VolumeModel == OPNMIDI_VolumeModel_AUTO)//Use bank default volume model + play->m_synth.m_volumeScale = (OPN2::VolumesScale)play->m_synth.m_insBankSetup.volumeModel; + else + play->m_synth.setVolumeScaleModel(static_cast(volumeModel)); + } +} + +OPNMIDI_EXPORT int opn2_getVolumeRangeModel(struct OPN2_MIDIPlayer *device) +{ + if(!device) + return -1; + MidiPlayer *play = GET_MIDI_PLAYER(device); + assert(play); + return play->m_synth.getVolumeScaleModel(); } OPNMIDI_EXPORT int opn2_openFile(OPN2_MIDIPlayer *device, const char *filePath) { - if(device && device->opn2_midiPlayer) + if(device) { - OPNMIDIplay *play = reinterpret_cast(device->opn2_midiPlayer); + MidiPlayer *play = GET_MIDI_PLAYER(device); + assert(play); #ifndef OPNMIDI_DISABLE_MIDI_SEQUENCER play->m_setup.tick_skip_samples_delay = 0; if(!play->LoadMIDI(filePath)) @@ -189,7 +438,7 @@ OPNMIDI_EXPORT int opn2_openFile(OPN2_MIDIPlayer *device, const char *filePath) } else return 0; #else - (void)filePath; + ADL_UNUSED(filePath); play->setErrorString("OPNMIDI: MIDI Sequencer is not supported in this build of library!"); return -1; #endif @@ -201,9 +450,10 @@ OPNMIDI_EXPORT int opn2_openFile(OPN2_MIDIPlayer *device, const char *filePath) OPNMIDI_EXPORT int opn2_openData(OPN2_MIDIPlayer *device, const void *mem, unsigned long size) { - if(device && device->opn2_midiPlayer) + if(device) { - OPNMIDIplay *play = reinterpret_cast(device->opn2_midiPlayer); + MidiPlayer *play = GET_MIDI_PLAYER(device); + assert(play); #ifndef OPNMIDI_DISABLE_MIDI_SEQUENCER play->m_setup.tick_skip_samples_delay = 0; if(!play->LoadMIDI(mem, static_cast(size))) @@ -215,7 +465,8 @@ OPNMIDI_EXPORT int opn2_openData(OPN2_MIDIPlayer *device, const void *mem, unsig } else return 0; #else - (void)mem;(void)size; + ADL_UNUSED(mem); + ADL_UNUSED(size); play->setErrorString("OPNMIDI: MIDI Sequencer is not supported in this build of library!"); return -1; #endif @@ -234,9 +485,10 @@ OPNMIDI_EXPORT const char *opn2_chipEmulatorName(struct OPN2_MIDIPlayer *device) { if(device) { - OPNMIDIplay *play = reinterpret_cast(device->opn2_midiPlayer); - if(play && !play->opn.cardsOP2.empty()) - return play->opn.cardsOP2[0]->emulatorName(); + MidiPlayer *play = GET_MIDI_PLAYER(device); + assert(play); + if(!play->m_synth.m_chips.empty()) + return play->m_synth.m_chips[0]->emulatorName(); } return "Unknown"; } @@ -245,11 +497,12 @@ OPNMIDI_EXPORT int opn2_switchEmulator(struct OPN2_MIDIPlayer *device, int emula { if(device) { - OPNMIDIplay *play = reinterpret_cast(device->opn2_midiPlayer); - if(play && (emulator >= 0) && (emulator < OPNMIDI_EMU_end)) + MidiPlayer *play = GET_MIDI_PLAYER(device); + assert(play); + if(opn2_isEmulatorAvailable(emulator)) { play->m_setup.emulator = emulator; - opn2_reset(device); + play->partialReset(); return 0; } play->setErrorString("OPN2 MIDI: Unknown emulation core!"); @@ -262,13 +515,12 @@ OPNMIDI_EXPORT int opn2_setRunAtPcmRate(OPN2_MIDIPlayer *device, int enabled) { if(device) { - OPNMIDIplay *play = reinterpret_cast(device->opn2_midiPlayer); - if(play) - { - play->m_setup.runAtPcmRate = (enabled != 0); - opn2_reset(device); - return 0; - } + MidiPlayer *play = GET_MIDI_PLAYER(device); + assert(play); + play->m_setup.runAtPcmRate = (enabled != 0); + if(!play->m_synth.setupLocked()) + play->partialReset(); + return 0; } return -1; } @@ -297,30 +549,19 @@ OPNMIDI_EXPORT const char *opn2_errorInfo(struct OPN2_MIDIPlayer *device) { if(!device) return opn2_errorString(); - OPNMIDIplay *play = reinterpret_cast(device->opn2_midiPlayer); + MidiPlayer *play = GET_MIDI_PLAYER(device); if(!play) return opn2_errorString(); return play->getErrorString().c_str(); } -OPNMIDI_EXPORT const char *opn2_getMusicTitle(struct OPN2_MIDIPlayer *device) -{ - if(!device) - return ""; -#ifndef OPNMIDI_DISABLE_MIDI_SEQUENCER - OPNMIDIplay *play = reinterpret_cast(device->opn2_midiPlayer); - if(!play) - return ""; - return play->musTitle.c_str(); -#else - return ""; -#endif -} - OPNMIDI_EXPORT void opn2_close(OPN2_MIDIPlayer *device) { - if(device->opn2_midiPlayer) - delete reinterpret_cast(device->opn2_midiPlayer); + if(!device) + return; + MidiPlayer *play = GET_MIDI_PLAYER(device); + assert(play); + delete play; device->opn2_midiPlayer = NULL; free(device); device = NULL; @@ -330,154 +571,183 @@ OPNMIDI_EXPORT void opn2_reset(OPN2_MIDIPlayer *device) { if(!device) return; - OPNMIDIplay *play = reinterpret_cast(device->opn2_midiPlayer); - play->m_setup.tick_skip_samples_delay = 0; - play->opn.runAtPcmRate = play->m_setup.runAtPcmRate; - play->opn.Reset(play->m_setup.emulator, play->m_setup.PCM_RATE); - play->ch.clear(); - play->ch.resize(play->opn.NumChannels); + MidiPlayer *play = GET_MIDI_PLAYER(device); + assert(play); + play->partialReset(); + play->resetMIDI(); } OPNMIDI_EXPORT double opn2_totalTimeLength(struct OPN2_MIDIPlayer *device) { +#ifndef OPNMIDI_DISABLE_MIDI_SEQUENCER if(!device) return -1.0; -#ifndef OPNMIDI_DISABLE_MIDI_SEQUENCER - OPNMIDIplay *play = reinterpret_cast(device->opn2_midiPlayer); - if(play) - return play->timeLength(); - else + MidiPlayer *play = GET_MIDI_PLAYER(device); + assert(play); + return play->m_sequencer.timeLength(); +#else + ADL_UNUSED(device); + return -1.0; #endif - return -1.0; } OPNMIDI_EXPORT double opn2_loopStartTime(struct OPN2_MIDIPlayer *device) { +#ifndef OPNMIDI_DISABLE_MIDI_SEQUENCER if(!device) return -1.0; -#ifndef OPNMIDI_DISABLE_MIDI_SEQUENCER - OPNMIDIplay *play = reinterpret_cast(device->opn2_midiPlayer); - if(play) - return play->getLoopStart(); - else + MidiPlayer *play = GET_MIDI_PLAYER(device); + assert(play); + return play->m_sequencer.getLoopStart(); +#else + ADL_UNUSED(device); + return -1.0; #endif - return -1.0; } OPNMIDI_EXPORT double opn2_loopEndTime(struct OPN2_MIDIPlayer *device) { +#ifndef OPNMIDI_DISABLE_MIDI_SEQUENCER if(!device) return -1.0; -#ifndef OPNMIDI_DISABLE_MIDI_SEQUENCER - OPNMIDIplay *play = reinterpret_cast(device->opn2_midiPlayer); - if(play) - return play->getLoopEnd(); - else + MidiPlayer *play = GET_MIDI_PLAYER(device); + assert(play); + return play->m_sequencer.getLoopEnd(); +#else + ADL_UNUSED(device); + return -1.0; #endif - return -1.0; } OPNMIDI_EXPORT double opn2_positionTell(struct OPN2_MIDIPlayer *device) { +#ifndef OPNMIDI_DISABLE_MIDI_SEQUENCER if(!device) return -1.0; -#ifndef OPNMIDI_DISABLE_MIDI_SEQUENCER - OPNMIDIplay *play = reinterpret_cast(device->opn2_midiPlayer); - if(play) - return play->tell(); - else + MidiPlayer *play = GET_MIDI_PLAYER(device); + assert(play); + return play->m_sequencer.tell(); +#else + ADL_UNUSED(device); + return -1.0; #endif - return -1.0; } OPNMIDI_EXPORT void opn2_positionSeek(struct OPN2_MIDIPlayer *device, double seconds) { +#ifndef OPNMIDI_DISABLE_MIDI_SEQUENCER + if(seconds < 0.0) + return;//Seeking negative position is forbidden! :-P if(!device) return; -#ifndef OPNMIDI_DISABLE_MIDI_SEQUENCER - OPNMIDIplay *play = reinterpret_cast(device->opn2_midiPlayer); - if(play) - play->seek(seconds); + MidiPlayer *play = GET_MIDI_PLAYER(device); + assert(play); + play->realTime_panic(); + play->m_setup.delay = play->m_sequencer.seek(seconds, play->m_setup.mindelay); + play->m_setup.carry = 0.0; #else - (void)seconds; + ADL_UNUSED(device); + ADL_UNUSED(seconds); #endif } OPNMIDI_EXPORT void opn2_positionRewind(struct OPN2_MIDIPlayer *device) { +#ifndef OPNMIDI_DISABLE_MIDI_SEQUENCER if(!device) return; -#ifndef OPNMIDI_DISABLE_MIDI_SEQUENCER - OPNMIDIplay *play = reinterpret_cast(device->opn2_midiPlayer); - if(play) - play->rewind(); + MidiPlayer *play = GET_MIDI_PLAYER(device); + assert(play); + play->realTime_panic(); + play->m_sequencer.rewind(); +#else + ADL_UNUSED(device); #endif } OPNMIDI_EXPORT void opn2_setTempo(struct OPN2_MIDIPlayer *device, double tempo) { +#ifndef OPNMIDI_DISABLE_MIDI_SEQUENCER if(!device || (tempo <= 0.0)) return; -#ifndef OPNMIDI_DISABLE_MIDI_SEQUENCER - OPNMIDIplay *play = reinterpret_cast(device->opn2_midiPlayer); - if(play) - play->setTempo(tempo); + MidiPlayer *play = GET_MIDI_PLAYER(device); + assert(play); + play->m_sequencer.setTempo(tempo); +#else + ADL_UNUSED(device); + ADL_UNUSED(tempo); #endif } +OPNMIDI_EXPORT int opn2_describeChannels(struct OPN2_MIDIPlayer *device, char *str, char *attr, size_t size) +{ + if(!device) + return -1; + MidiPlayer *play = GET_MIDI_PLAYER(device); + assert(play); + play->describeChannels(str, attr, size); + return 0; +} + OPNMIDI_EXPORT const char *opn2_metaMusicTitle(struct OPN2_MIDIPlayer *device) { +#ifndef OPNMIDI_DISABLE_MIDI_SEQUENCER if(!device) return ""; -#ifndef OPNMIDI_DISABLE_MIDI_SEQUENCER - OPNMIDIplay *play = reinterpret_cast(device->opn2_midiPlayer); - if(play) - return play->musTitle.c_str(); - else + MidiPlayer *play = GET_MIDI_PLAYER(device); + assert(play); + return play->m_sequencer.getMusicTitle().c_str(); +#else + ADL_UNUSED(device); + return ""; #endif - return ""; } OPNMIDI_EXPORT const char *opn2_metaMusicCopyright(struct OPN2_MIDIPlayer *device) { +#ifndef OPNMIDI_DISABLE_MIDI_SEQUENCER if(!device) return ""; -#ifndef OPNMIDI_DISABLE_MIDI_SEQUENCER - OPNMIDIplay *play = reinterpret_cast(device->opn2_midiPlayer); - if(play) - return play->musCopyright.c_str(); - else + MidiPlayer *play = GET_MIDI_PLAYER(device); + assert(play); + return play->m_sequencer.getMusicCopyright().c_str(); +#else + ADL_UNUSED(device); + return 0; #endif - return ""; } OPNMIDI_EXPORT size_t opn2_metaTrackTitleCount(struct OPN2_MIDIPlayer *device) { +#ifndef OPNMIDI_DISABLE_MIDI_SEQUENCER if(!device) return 0; -#ifndef OPNMIDI_DISABLE_MIDI_SEQUENCER - OPNMIDIplay *play = reinterpret_cast(device->opn2_midiPlayer); - if(play) - return play->musTrackTitles.size(); - else + MidiPlayer *play = GET_MIDI_PLAYER(device); + assert(play); + return play->m_sequencer.getTrackTitles().size(); +#else + ADL_UNUSED(device); + return 0; #endif - return 0; } OPNMIDI_EXPORT const char *opn2_metaTrackTitle(struct OPN2_MIDIPlayer *device, size_t index) { - if(!device) - return 0; #ifndef OPNMIDI_DISABLE_MIDI_SEQUENCER - OPNMIDIplay *play = reinterpret_cast(device->opn2_midiPlayer); - if(index >= play->musTrackTitles.size()) + if(!device) + return ""; + MidiPlayer *play = GET_MIDI_PLAYER(device); + assert(play); + const std::vector &titles = play->m_sequencer.getTrackTitles(); + if(index >= titles.size()) return "INVALID"; - return play->musTrackTitles[index].c_str(); + return titles[index].c_str(); #else - (void)index; + ADL_UNUSED(device); + ADL_UNUSED(index); return "NOT SUPPORTED"; #endif } @@ -485,36 +755,47 @@ OPNMIDI_EXPORT const char *opn2_metaTrackTitle(struct OPN2_MIDIPlayer *device, s OPNMIDI_EXPORT size_t opn2_metaMarkerCount(struct OPN2_MIDIPlayer *device) { +#ifndef OPNMIDI_DISABLE_MIDI_SEQUENCER if(!device) return 0; -#ifndef OPNMIDI_DISABLE_MIDI_SEQUENCER - OPNMIDIplay *play = reinterpret_cast(device->opn2_midiPlayer); - if(play) - return play->musMarkers.size(); - else + MidiPlayer *play = GET_MIDI_PLAYER(device); + assert(play); + return play->m_sequencer.getMarkers().size(); +#else + ADL_UNUSED(device); + return 0; #endif - return 0; } OPNMIDI_EXPORT Opn2_MarkerEntry opn2_metaMarker(struct OPN2_MIDIPlayer *device, size_t index) { struct Opn2_MarkerEntry marker; + #ifndef OPNMIDI_DISABLE_MIDI_SEQUENCER - OPNMIDIplay *play = reinterpret_cast(device->opn2_midiPlayer); - if(!device || !play || (index >= play->musMarkers.size())) + if(!device) { marker.label = "INVALID"; marker.pos_time = 0.0; marker.pos_ticks = 0; return marker; } - else + + MidiPlayer *play = GET_MIDI_PLAYER(device); + assert(play); + + const std::vector &markers = play->m_sequencer.getMarkers(); + if(index >= markers.size()) { - OPNMIDIplay::MIDI_MarkerEntry &mk = play->musMarkers[index]; - marker.label = mk.label.c_str(); - marker.pos_time = mk.pos_time; - marker.pos_ticks = (unsigned long)mk.pos_ticks; + marker.label = "INVALID"; + marker.pos_time = 0.0; + marker.pos_ticks = 0; + return marker; } + + const MidiSequencer::MIDI_MarkerEntry &mk = markers[index]; + marker.label = mk.label.c_str(); + marker.pos_time = mk.pos_time; + marker.pos_ticks = (unsigned long)mk.pos_ticks; #else (void)device; (void)index; marker.label = "NOT SUPPORTED"; @@ -526,11 +807,18 @@ OPNMIDI_EXPORT Opn2_MarkerEntry opn2_metaMarker(struct OPN2_MIDIPlayer *device, OPNMIDI_EXPORT void opn2_setRawEventHook(struct OPN2_MIDIPlayer *device, OPN2_RawEventHook rawEventHook, void *userData) { +#ifndef OPNMIDI_DISABLE_MIDI_SEQUENCER if(!device) return; - OPNMIDIplay *play = reinterpret_cast(device->opn2_midiPlayer); - play->hooks.onEvent = rawEventHook; - play->hooks.onEvent_userData = userData; + MidiPlayer *play = GET_MIDI_PLAYER(device); + assert(play); + play->m_sequencerInterface.onEvent = rawEventHook; + play->m_sequencerInterface.onEvent_userData = userData; +#else + ADL_UNUSED(device); + ADL_UNUSED(rawEventHook); + ADL_UNUSED(userData); +#endif } /* Set note hook */ @@ -538,7 +826,8 @@ OPNMIDI_EXPORT void opn2_setNoteHook(struct OPN2_MIDIPlayer *device, OPN2_NoteHo { if(!device) return; - OPNMIDIplay *play = reinterpret_cast(device->opn2_midiPlayer); + MidiPlayer *play = GET_MIDI_PLAYER(device); + assert(play); play->hooks.onNote = noteHook; play->hooks.onNote_userData = userData; } @@ -548,9 +837,14 @@ OPNMIDI_EXPORT void opn2_setDebugMessageHook(struct OPN2_MIDIPlayer *device, OPN { if(!device) return; - OPNMIDIplay *play = reinterpret_cast(device->opn2_midiPlayer); + MidiPlayer *play = GET_MIDI_PLAYER(device); + assert(play); play->hooks.onDebugMessage = debugMessageHook; play->hooks.onDebugMessage_userData = userData; +#ifndef OPNMIDI_DISABLE_MIDI_SEQUENCER + play->m_sequencerInterface.onDebugMessage = debugMessageHook; + play->m_sequencerInterface.onDebugMessage_userData = userData; +#endif } @@ -570,8 +864,8 @@ static void CopySamplesTransformed(OPN2_UInt8 *dstLeft, OPN2_UInt8 *dstRight, co Ret(&transform)(int32_t)) { for(size_t i = 0; i < frameCount; ++i) { - *(Dst *)(dstLeft + (i * sampleOffset)) = transform(src[2 * i]); - *(Dst *)(dstRight + (i * sampleOffset)) = transform(src[(2 * i) + 1]); + *(Dst *)(dstLeft + (i * sampleOffset)) = static_cast(transform(src[2 * i])); + *(Dst *)(dstRight + (i * sampleOffset)) = static_cast(transform(src[(2 * i) + 1])); } } @@ -688,6 +982,15 @@ OPNMIDI_EXPORT int opn2_playFormat(OPN2_MIDIPlayer *device, int sampleCount, OPN2_UInt8 *out_left, OPN2_UInt8 *out_right, const OPNMIDI_AudioFormat *format) { +#if defined(OPNMIDI_DISABLE_MIDI_SEQUENCER) + ADL_UNUSED(device); + ADL_UNUSED(sampleCount); + ADL_UNUSED(out_left); + ADL_UNUSED(out_right); + ADL_UNUSED(format); + return 0; +#endif + #ifndef OPNMIDI_DISABLE_MIDI_SEQUENCER sampleCount -= sampleCount % 2; //Avoid even sample requests if(sampleCount < 0) @@ -695,8 +998,9 @@ OPNMIDI_EXPORT int opn2_playFormat(OPN2_MIDIPlayer *device, int sampleCount, if(!device) return 0; - OPNMIDIplay * player = (reinterpret_cast(device->opn2_midiPlayer)); - OPNMIDIplay::Setup &setup = player->m_setup; + MidiPlayer *player = GET_MIDI_PLAYER(device); + assert(player); + MidiPlayer::Setup &setup = player->m_setup; ssize_t gotten_len = 0; ssize_t n_periodCountStereo = 512; @@ -716,16 +1020,16 @@ OPNMIDI_EXPORT int opn2_playFormat(OPN2_MIDIPlayer *device, int sampleCount, else { setup.delay -= eat_delay; - setup.carry += setup.PCM_RATE * eat_delay; + setup.carry += double(setup.PCM_RATE) * eat_delay; n_periodCountStereo = static_cast(setup.carry); - setup.carry -= n_periodCountStereo; + setup.carry -= double(n_periodCountStereo); } //if(setup.SkipForward > 0) // setup.SkipForward -= 1; //else { - if((player->atEnd) && (setup.delay <= 0.0)) + if((player->m_sequencer.positionAtEnd()) && (setup.delay <= 0.0)) break;//Stop to fetch samples at reaching the song end with disabled loop ssize_t leftSamples = left / 2; @@ -740,16 +1044,16 @@ OPNMIDI_EXPORT int opn2_playFormat(OPN2_MIDIPlayer *device, int sampleCount, ssize_t in_generatedPhys = in_generatedStereo * 2; //! Unsigned total sample count //fill buffer with zeros - int32_t *out_buf = player->outBuf; + int32_t *out_buf = player->m_outBuf; std::memset(out_buf, 0, static_cast(in_generatedPhys) * sizeof(out_buf[0])); - unsigned int chips = player->opn.NumCards; + unsigned int chips = player->m_synth.m_numChips; if(chips == 1) - player->opn.cardsOP2[0]->generate32(out_buf, (size_t)in_generatedStereo); + player->m_synth.m_chips[0]->generate32(out_buf, (size_t)in_generatedStereo); else/* if(n_periodCountStereo > 0)*/ { /* Generate data from every chip and mix result */ for(size_t card = 0; card < chips; ++card) - player->opn.cardsOP2[card]->generateAndMix32(out_buf, (size_t)in_generatedStereo); + player->m_synth.m_chips[card]->generateAndMix32(out_buf, (size_t)in_generatedStereo); } /* Process it */ if(SendStereoAudio(sampleCount, in_generatedStereo, out_buf, gotten_len, out_left, out_right, format) == -1) @@ -769,8 +1073,6 @@ OPNMIDI_EXPORT int opn2_playFormat(OPN2_MIDIPlayer *device, int sampleCount, } return static_cast(gotten_len); -#else - return 0; #endif //OPNMIDI_DISABLE_MIDI_SEQUENCER } @@ -790,8 +1092,9 @@ OPNMIDI_EXPORT int opn2_generateFormat(struct OPN2_MIDIPlayer *device, int sampl if(!device) return 0; - OPNMIDIplay * player = (reinterpret_cast(device->opn2_midiPlayer)); - OPNMIDIplay::Setup &setup = player->m_setup; + MidiPlayer *player = GET_MIDI_PLAYER(device); + assert(player); + MidiPlayer::Setup &setup = player->m_setup; ssize_t gotten_len = 0; ssize_t n_periodCountStereo = 512; @@ -804,9 +1107,9 @@ OPNMIDI_EXPORT int opn2_generateFormat(struct OPN2_MIDIPlayer *device, int sampl {// const double eat_delay = delay < setup.maxdelay ? delay : setup.maxdelay; delay -= eat_delay; - setup.carry += setup.PCM_RATE * eat_delay; + setup.carry += double(setup.PCM_RATE) * eat_delay; n_periodCountStereo = static_cast(setup.carry); - setup.carry -= n_periodCountStereo; + setup.carry -= double(n_periodCountStereo); { ssize_t leftSamples = left / 2; @@ -818,16 +1121,16 @@ OPNMIDI_EXPORT int opn2_generateFormat(struct OPN2_MIDIPlayer *device, int sampl ssize_t in_generatedPhys = in_generatedStereo * 2; //! Unsigned total sample count //fill buffer with zeros - int32_t *out_buf = player->outBuf; + int32_t *out_buf = player->m_outBuf; std::memset(out_buf, 0, static_cast(in_generatedPhys) * sizeof(out_buf[0])); - unsigned int chips = player->opn.NumCards; + unsigned int chips = player->m_synth.m_numChips; if(chips == 1) - player->opn.cardsOP2[0]->generate32(out_buf, (size_t)in_generatedStereo); + player->m_synth.m_chips[0]->generate32(out_buf, (size_t)in_generatedStereo); else/* if(n_periodCountStereo > 0)*/ { /* Generate data from every chip and mix result */ for(size_t card = 0; card < chips; ++card) - player->opn.cardsOP2[card]->generateAndMix32(out_buf, (size_t)in_generatedStereo); + player->m_synth.m_chips[card]->generateAndMix32(out_buf, (size_t)in_generatedStereo); } /* Process it */ if(SendStereoAudio(sampleCount, in_generatedStereo, out_buf, gotten_len, out_left, out_right, format) == -1) @@ -837,8 +1140,8 @@ OPNMIDI_EXPORT int opn2_generateFormat(struct OPN2_MIDIPlayer *device, int sampl gotten_len += (in_generatedPhys) /* - setup.stored_samples*/; } - player->TickIteratos(eat_delay); - }// + player->TickIterators(eat_delay); + }//... } return static_cast(gotten_len); @@ -846,159 +1149,211 @@ OPNMIDI_EXPORT int opn2_generateFormat(struct OPN2_MIDIPlayer *device, int sampl OPNMIDI_EXPORT double opn2_tickEvents(struct OPN2_MIDIPlayer *device, double seconds, double granuality) { +#ifndef OPNMIDI_DISABLE_MIDI_SEQUENCER if(!device) return -1.0; -#ifndef OPNMIDI_DISABLE_MIDI_SEQUENCER - OPNMIDIplay *player = reinterpret_cast(device->opn2_midiPlayer); - if(!player) - return -1.0; - return player->Tick(seconds, granuality); + MidiPlayer *play = GET_MIDI_PLAYER(device); + assert(play); + return play->Tick(seconds, granuality); #else - (void)seconds; (void)granuality; + ADL_UNUSED(device); + ADL_UNUSED(seconds); + ADL_UNUSED(granuality); return -1.0; #endif } OPNMIDI_EXPORT int opn2_atEnd(struct OPN2_MIDIPlayer *device) { +#ifndef OPNMIDI_DISABLE_MIDI_SEQUENCER if(!device) return 1; -#ifndef OPNMIDI_DISABLE_MIDI_SEQUENCER - OPNMIDIplay *player = reinterpret_cast(device->opn2_midiPlayer); - if(!player) - return 1; - return (int)player->atEnd; + MidiPlayer *play = GET_MIDI_PLAYER(device); + assert(play); + return (int)play->m_sequencer.positionAtEnd(); #else + ADL_UNUSED(device); return 1; #endif } +OPNMIDI_EXPORT size_t opn2_trackCount(struct OPN2_MIDIPlayer *device) +{ +#ifndef OPNMIDI_DISABLE_MIDI_SEQUENCER + if(!device) + return 0; + MidiPlayer *play = GET_MIDI_PLAYER(device); + assert(play); + return play->m_sequencer.getTrackCount(); +#else + ADL_UNUSED(device); + return 0; +#endif +} + +OPNMIDI_EXPORT int opn2_setTrackOptions(struct OPN2_MIDIPlayer *device, size_t trackNumber, unsigned trackOptions) +{ +#ifndef OPNMIDI_DISABLE_MIDI_SEQUENCER + if(!device) + return -1; + MidiPlayer *play = GET_MIDI_PLAYER(device); + assert(play); + MidiSequencer &seq = play->m_sequencer; + + unsigned enableFlag = trackOptions & 3; + trackOptions &= ~3u; + + // handle on/off/solo + switch(enableFlag) + { + default: + break; + case OPNMIDI_TrackOption_On: + case OPNMIDI_TrackOption_Off: + if(!seq.setTrackEnabled(trackNumber, enableFlag == OPNMIDI_TrackOption_On)) + return -1; + break; + case OPNMIDI_TrackOption_Solo: + seq.setSoloTrack(trackNumber); + break; + } + + // handle others... + if(trackOptions != 0) + return -1; + + return 0; + +#else + ADL_UNUSED(device); + ADL_UNUSED(trackNumber); + ADL_UNUSED(trackOptions); + return -1; +#endif +} + OPNMIDI_EXPORT void opn2_panic(struct OPN2_MIDIPlayer *device) { if(!device) return; - OPNMIDIplay *player = reinterpret_cast(device->opn2_midiPlayer); - if(!player) - return; - player->realTime_panic(); + MidiPlayer *play = GET_MIDI_PLAYER(device); + assert(play); + play->realTime_panic(); } OPNMIDI_EXPORT void opn2_rt_resetState(struct OPN2_MIDIPlayer *device) { if(!device) return; - OPNMIDIplay *player = reinterpret_cast(device->opn2_midiPlayer); - if(!player) - return; - player->realTime_ResetState(); + MidiPlayer *play = GET_MIDI_PLAYER(device); + assert(play); + play->realTime_ResetState(); } OPNMIDI_EXPORT int opn2_rt_noteOn(struct OPN2_MIDIPlayer *device, OPN2_UInt8 channel, OPN2_UInt8 note, OPN2_UInt8 velocity) { if(!device) return 0; - OPNMIDIplay *player = reinterpret_cast(device->opn2_midiPlayer); - if(!player) - return 0; - return (int)player->realTime_NoteOn(channel, note, velocity); + MidiPlayer *play = GET_MIDI_PLAYER(device); + assert(play); + return (int)play->realTime_NoteOn(channel, note, velocity); } OPNMIDI_EXPORT void opn2_rt_noteOff(struct OPN2_MIDIPlayer *device, OPN2_UInt8 channel, OPN2_UInt8 note) { if(!device) return; - OPNMIDIplay *player = reinterpret_cast(device->opn2_midiPlayer); - if(!player) - return; - player->realTime_NoteOff(channel, note); + MidiPlayer *play = GET_MIDI_PLAYER(device); + assert(play); + play->realTime_NoteOff(channel, note); } OPNMIDI_EXPORT void opn2_rt_noteAfterTouch(struct OPN2_MIDIPlayer *device, OPN2_UInt8 channel, OPN2_UInt8 note, OPN2_UInt8 atVal) { if(!device) return; - OPNMIDIplay *player = reinterpret_cast(device->opn2_midiPlayer); - if(!player) - return; - player->realTime_NoteAfterTouch(channel, note, atVal); + MidiPlayer *play = GET_MIDI_PLAYER(device); + assert(play); + play->realTime_NoteAfterTouch(channel, note, atVal); } OPNMIDI_EXPORT void opn2_rt_channelAfterTouch(struct OPN2_MIDIPlayer *device, OPN2_UInt8 channel, OPN2_UInt8 atVal) { if(!device) return; - OPNMIDIplay *player = reinterpret_cast(device->opn2_midiPlayer); - if(!player) - return; - player->realTime_ChannelAfterTouch(channel, atVal); + MidiPlayer *play = GET_MIDI_PLAYER(device); + assert(play); + play->realTime_ChannelAfterTouch(channel, atVal); } OPNMIDI_EXPORT void opn2_rt_controllerChange(struct OPN2_MIDIPlayer *device, OPN2_UInt8 channel, OPN2_UInt8 type, OPN2_UInt8 value) { if(!device) return; - OPNMIDIplay *player = reinterpret_cast(device->opn2_midiPlayer); - if(!player) - return; - player->realTime_Controller(channel, type, value); + MidiPlayer *play = GET_MIDI_PLAYER(device); + assert(play); + play->realTime_Controller(channel, type, value); } OPNMIDI_EXPORT void opn2_rt_patchChange(struct OPN2_MIDIPlayer *device, OPN2_UInt8 channel, OPN2_UInt8 patch) { if(!device) return; - OPNMIDIplay *player = reinterpret_cast(device->opn2_midiPlayer); - if(!player) - return; - player->realTime_PatchChange(channel, patch); + MidiPlayer *play = GET_MIDI_PLAYER(device); + assert(play); + play->realTime_PatchChange(channel, patch); } OPNMIDI_EXPORT void opn2_rt_pitchBend(struct OPN2_MIDIPlayer *device, OPN2_UInt8 channel, OPN2_UInt16 pitch) { if(!device) return; - OPNMIDIplay *player = reinterpret_cast(device->opn2_midiPlayer); - if(!player) - return; - player->realTime_PitchBend(channel, pitch); + MidiPlayer *play = GET_MIDI_PLAYER(device); + assert(play); + play->realTime_PitchBend(channel, pitch); } OPNMIDI_EXPORT void opn2_rt_pitchBendML(struct OPN2_MIDIPlayer *device, OPN2_UInt8 channel, OPN2_UInt8 msb, OPN2_UInt8 lsb) { if(!device) return; - OPNMIDIplay *player = reinterpret_cast(device->opn2_midiPlayer); - if(!player) - return; - player->realTime_PitchBend(channel, msb, lsb); + MidiPlayer *play = GET_MIDI_PLAYER(device); + assert(play); + play->realTime_PitchBend(channel, msb, lsb); } OPNMIDI_EXPORT void opn2_rt_bankChangeLSB(struct OPN2_MIDIPlayer *device, OPN2_UInt8 channel, OPN2_UInt8 lsb) { if(!device) return; - OPNMIDIplay *player = reinterpret_cast(device->opn2_midiPlayer); - if(!player) - return; - player->realTime_BankChangeLSB(channel, lsb); + MidiPlayer *play = GET_MIDI_PLAYER(device); + assert(play); + play->realTime_BankChangeLSB(channel, lsb); } OPNMIDI_EXPORT void opn2_rt_bankChangeMSB(struct OPN2_MIDIPlayer *device, OPN2_UInt8 channel, OPN2_UInt8 msb) { if(!device) return; - OPNMIDIplay *player = reinterpret_cast(device->opn2_midiPlayer); - if(!player) - return; - player->realTime_BankChangeMSB(channel, msb); + MidiPlayer *play = GET_MIDI_PLAYER(device); + assert(play); + play->realTime_BankChangeMSB(channel, msb); } OPNMIDI_EXPORT void opn2_rt_bankChange(struct OPN2_MIDIPlayer *device, OPN2_UInt8 channel, OPN2_SInt16 bank) { if(!device) return; - OPNMIDIplay *player = reinterpret_cast(device->opn2_midiPlayer); - if(!player) - return; - player->realTime_BankChange(channel, (uint16_t)bank); + MidiPlayer *play = GET_MIDI_PLAYER(device); + assert(play); + play->realTime_BankChange(channel, (uint16_t)bank); +} + +OPNMIDI_EXPORT int opn2_rt_systemExclusive(struct OPN2_MIDIPlayer *device, const OPN2_UInt8 *msg, size_t size) +{ + if(!device) + return -1; + MidiPlayer *play = GET_MIDI_PLAYER(device); + assert(play); + return play->realTime_SysEx(msg, size); } diff --git a/src/sound/opnmidi/opnmidi.h b/src/sound/opnmidi/opnmidi.h index 29ac58f86..5f2cd78e5 100644 --- a/src/sound/opnmidi/opnmidi.h +++ b/src/sound/opnmidi/opnmidi.h @@ -29,7 +29,7 @@ extern "C" { #endif #define OPNMIDI_VERSION_MAJOR 1 -#define OPNMIDI_VERSION_MINOR 3 +#define OPNMIDI_VERSION_MINOR 4 #define OPNMIDI_VERSION_PATCHLEVEL 0 #define OPNMIDI_TOSTR_I(s) #s @@ -54,195 +54,727 @@ typedef char OPN2_SInt8; typedef short OPN2_SInt16; #endif + +/* == Deprecated function markers == */ + +#if defined(_MSC_VER) /* MSVC */ +# if _MSC_VER >= 1500 /* MSVC 2008 */ + /*! Indicates that the following function is deprecated. */ +# define OPNMIDI_DEPRECATED(message) __declspec(deprecated(message)) +# endif +#endif /* defined(_MSC_VER) */ + +#ifdef __clang__ +# if __has_extension(attribute_deprecated_with_message) +# define OPNMIDI_DEPRECATED(message) __attribute__((deprecated(message))) +# endif +#elif defined __GNUC__ /* not clang (gcc comes later since clang emulates gcc) */ +# if (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 5)) +# define OPNMIDI_DEPRECATED(message) __attribute__((deprecated(message))) +# elif (__GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 1)) +# define OPNMIDI_DEPRECATED(message) __attribute__((__deprecated__)) +# endif /* GNUC version */ +#endif /* __clang__ || __GNUC__ */ + +#if !defined(OPNMIDI_DEPRECATED) +# define OPNMIDI_DEPRECATED(message) +#endif /* if !defined(OPNMIDI_DEPRECATED) */ + + +#ifdef OPNMIDI_BUILD +# ifndef OPNMIDI_DECLSPEC +# if defined (_WIN32) && defined(OPNMIDI_BUILD_DLL) +# define OPNMIDI_DECLSPEC __declspec(dllexport) +# else +# define OPNMIDI_DECLSPEC +# endif +# endif +#else +# define OPNMIDI_DECLSPEC +#endif + +/** + * @brief Volume scaling models + */ enum OPNMIDI_VolumeModels { + /*! Automatical choice by the specific bank */ OPNMIDI_VolumeModel_AUTO = 0, + /*! Linearized scaling model, most standard */ OPNMIDI_VolumeModel_Generic, - OPNMIDI_VolumeModel_CMF, + /*! Native OPN2's logarithmic volume scale */ + OPNMIDI_VolumeModel_NativeOPN2, + /*! Logarithmic volume scale, using volume map table. Used in DMX. */ OPNMIDI_VolumeModel_DMX, + /*! Logarithmic volume scale, used in Apogee Sound System. */ OPNMIDI_VolumeModel_APOGEE, + /*! Aproximated and shorted volume map table. Similar to general, but has less granularity. */ OPNMIDI_VolumeModel_9X }; +/** + * @brief Sound output format + */ enum OPNMIDI_SampleType { - OPNMIDI_SampleType_S16 = 0, /* signed PCM 16-bit */ - OPNMIDI_SampleType_S8, /* signed PCM 8-bit */ - OPNMIDI_SampleType_F32, /* float 32-bit */ - OPNMIDI_SampleType_F64, /* float 64-bit */ - OPNMIDI_SampleType_S24, /* signed PCM 24-bit */ - OPNMIDI_SampleType_S32, /* signed PCM 32-bit */ - OPNMIDI_SampleType_U8, /* unsigned PCM 8-bit */ - OPNMIDI_SampleType_U16, /* unsigned PCM 16-bit */ - OPNMIDI_SampleType_U24, /* unsigned PCM 24-bit */ - OPNMIDI_SampleType_U32, /* unsigned PCM 32-bit */ + /*! signed PCM 16-bit */ + OPNMIDI_SampleType_S16 = 0, + /*! signed PCM 8-bit */ + OPNMIDI_SampleType_S8, + /*! float 32-bit */ + OPNMIDI_SampleType_F32, + /*! float 64-bit */ + OPNMIDI_SampleType_F64, + /*! signed PCM 24-bit */ + OPNMIDI_SampleType_S24, + /*! signed PCM 32-bit */ + OPNMIDI_SampleType_S32, + /*! unsigned PCM 8-bit */ + OPNMIDI_SampleType_U8, + /*! unsigned PCM 16-bit */ + OPNMIDI_SampleType_U16, + /*! unsigned PCM 24-bit */ + OPNMIDI_SampleType_U24, + /*! unsigned PCM 32-bit */ + OPNMIDI_SampleType_U32, + /*! Count of available sample format types */ OPNMIDI_SampleType_Count, }; +/** + * @brief Sound output format context + */ struct OPNMIDI_AudioFormat { - enum OPNMIDI_SampleType type; /* type of sample */ - unsigned containerSize; /* size in bytes of the storage type */ - unsigned sampleOffset; /* distance in bytes between consecutive samples */ + /*! type of sample */ + enum OPNMIDI_SampleType type; + /*! size in bytes of the storage type */ + unsigned containerSize; + /*! distance in bytes between consecutive samples */ + unsigned sampleOffset; }; +/** + * @brief Instance of the library + */ struct OPN2_MIDIPlayer { + /*! Private context descriptor */ void *opn2_midiPlayer; }; /* DEPRECATED */ #define opn2_setNumCards opn2_setNumChips -/* Sets number of emulated sound cards (from 1 to 100). Emulation of multiple sound cards exchanges polyphony limits*/ -extern int opn2_setNumChips(struct OPN2_MIDIPlayer *device, int numCards); +/** + * @brief Sets number of emulated chips (from 1 to 100). Emulation of multiple chips extends polyphony limits + * @param device Instance of the library + * @param numChips Count of virtual chips to emulate + * @return 0 on success, <0 when any error has occurred + */ +extern OPNMIDI_DECLSPEC int opn2_setNumChips(struct OPN2_MIDIPlayer *device, int numCards); -/* Get current number of emulated chips */ -extern int opn2_getNumChips(struct OPN2_MIDIPlayer *device); +/** + * @brief Get current number of emulated chips + * @param device Instance of the library + * @return Count of working chip emulators + */ +extern OPNMIDI_DECLSPEC int opn2_getNumChips(struct OPN2_MIDIPlayer *device); -/*Enable or disable Enables scaling of modulator volumes*/ -extern void opn2_setScaleModulators(struct OPN2_MIDIPlayer *device, int smod); +/** + * @brief Get obtained number of emulated chips + * @param device Instance of the library + * @return Count of working chip emulators + */ +extern OPNMIDI_DECLSPEC int opn2_getNumChipsObtained(struct OPN2_MIDIPlayer *device); -/*Enable(1) or Disable(0) full-range brightness (MIDI CC74 used in XG music to filter result sounding) scaling. - By default, brightness affects sound between 0 and 64. - When this option is enabled, the range will use a full range from 0 up to 127. -*/ -extern void opn2_setFullRangeBrightness(struct OPN2_MIDIPlayer *device, int fr_brightness); +/** + * @brief Reference to dynamic bank + */ +typedef struct OPN2_Bank +{ + void *pointer[3]; +} OPN2_Bank; -/*Enable or disable built-in loop (built-in loop supports 'loopStart' and 'loopEnd' tags to loop specific part)*/ -extern void opn2_setLoopEnabled(struct OPN2_MIDIPlayer *device, int loopEn); +/** + * @brief Identifier of dynamic bank + */ +typedef struct OPN2_BankId +{ + /*! 0 if bank is melodic set, or 1 if bank is a percussion set */ + OPN2_UInt8 percussive; + /*! Assign to MSB bank number */ + OPN2_UInt8 msb; + /*! Assign to LSB bank number */ + OPN2_UInt8 lsb; +} OPN2_BankId; -/* !!!DEPRECATED!!! */ -extern void opn2_setLogarithmicVolumes(struct OPN2_MIDIPlayer *device, int logvol); +/** + * @brief Flags for dynamic bank access + */ +enum OPN2_BankAccessFlags +{ + /*! create bank, allocating memory as needed */ + OPNMIDI_Bank_Create = 1, + /*! create bank, never allocating memory */ + OPNMIDI_Bank_CreateRt = 1|2 +}; -/*Set different volume range model */ -extern void opn2_setVolumeRangeModel(struct OPN2_MIDIPlayer *device, int volumeModel); - -/*Load WOPN bank file from File System. Is recommended to call adl_reset() to apply changes to already-loaded file player or real-time.*/ -extern int opn2_openBankFile(struct OPN2_MIDIPlayer *device, const char *filePath); - -/*Load WOPN bank file from memory data*/ -extern int opn2_openBankData(struct OPN2_MIDIPlayer *device, const void *mem, long size); +typedef struct OPN2_Instrument OPN2_Instrument; -/* DEPRECATED */ -extern const char *opn2_emulatorName(); -/*Returns chip emulator name string*/ -extern const char *opn2_chipEmulatorName(struct OPN2_MIDIPlayer *device); +/* ======== Setup ======== */ + +#ifdef OPNMIDI_UNSTABLE_API + +/** + * @brief Preallocates a minimum number of bank slots. Returns the actual capacity + * @param device Instance of the library + * @param banks Count of bank slots to pre-allocate. + * @return actual capacity of reserved bank slots. + */ +extern OPNMIDI_DECLSPEC int opn2_reserveBanks(struct OPN2_MIDIPlayer *device, unsigned banks); +/** + * @brief Gets the bank designated by the identifier, optionally creating if it does not exist + * @param device Instance of the library + * @param id Identifier of dynamic bank + * @param flags Flags for dynamic bank access (OPN2_BankAccessFlags) + * @param bank Reference to dynamic bank + * @return 0 on success, <0 when any error has occurred + */ +extern OPNMIDI_DECLSPEC int opn2_getBank(struct OPN2_MIDIPlayer *device, const OPN2_BankId *id, int flags, OPN2_Bank *bank); +/** + * @brief Gets the identifier of a bank + * @param device Instance of the library + * @param bank Reference to dynamic bank. + * @param id Identifier of dynamic bank + * @return 0 on success, <0 when any error has occurred + */ +extern OPNMIDI_DECLSPEC int opn2_getBankId(struct OPN2_MIDIPlayer *device, const OPN2_Bank *bank, OPN2_BankId *id); +/** + * @brief Removes a bank + * @param device Instance of the library + * @param bank Reference to dynamic bank + * @return 0 on success, <0 when any error has occurred + */ +extern OPNMIDI_DECLSPEC int opn2_removeBank(struct OPN2_MIDIPlayer *device, OPN2_Bank *bank); +/** + * @brief Gets the first bank + * @param device Instance of the library + * @param bank Reference to dynamic bank + * @return 0 on success, <0 when any error has occurred + */ +extern OPNMIDI_DECLSPEC int opn2_getFirstBank(struct OPN2_MIDIPlayer *device, OPN2_Bank *bank); +/** + * @brief Iterates to the next bank + * @param device Instance of the library + * @param bank Reference to dynamic bank + * @return 0 on success, <0 when any error has occurred or end has been reached. + */ +extern OPNMIDI_DECLSPEC int opn2_getNextBank(struct OPN2_MIDIPlayer *device, OPN2_Bank *bank); +/** + * @brief Gets the nth intrument in the bank [0..127] + * @param device Instance of the library + * @param bank Reference to dynamic bank + * @param index Index of the instrument + * @param ins Instrument entry + * @return 0 on success, <0 when any error has occurred + */ +extern OPNMIDI_DECLSPEC int opn2_getInstrument(struct OPN2_MIDIPlayer *device, const OPN2_Bank *bank, unsigned index, OPN2_Instrument *ins); +/** + * @brief Sets the nth intrument in the bank [0..127] + * @param device Instance of the library + * @param bank Reference to dynamic bank + * @param index Index of the instrument + * @param ins Instrument structure pointer + * @return 0 on success, <0 when any error has occurred + * + * This function allows to override an instrument on the fly + */ +extern OPNMIDI_DECLSPEC int opn2_setInstrument(struct OPN2_MIDIPlayer *device, OPN2_Bank *bank, unsigned index, const OPN2_Instrument *ins); + +#endif /* OPNMIDI_UNSTABLE_API */ + + + +/*Override Enable(1) or Disable(0) LFO. -1 - use bank default state*/ +extern OPNMIDI_DECLSPEC void opn2_setLfoEnabled(struct OPN2_MIDIPlayer *device, int lfoEnable); + +/*Get the LFO state*/ +extern OPNMIDI_DECLSPEC int opn2_getLfoEnabled(struct OPN2_MIDIPlayer *device); + +/*Override LFO frequency. -1 - use bank default state*/ +extern OPNMIDI_DECLSPEC void opn2_setLfoFrequency(struct OPN2_MIDIPlayer *device, int lfoFrequency); + +/*Get the LFO frequency*/ +extern OPNMIDI_DECLSPEC int opn2_getLfoFrequency(struct OPN2_MIDIPlayer *device); + +/** + * @brief Override Enable(1) or Disable(0) scaling of modulator volumes. -1 - use bank default scaling of modulator volumes + * @param device Instance of the library + * @param smod 0 - disabled, 1 - enabled + */ +extern OPNMIDI_DECLSPEC void opn2_setScaleModulators(struct OPN2_MIDIPlayer *device, int smod); + +/** + * @brief Enable(1) or Disable(0) full-range brightness (MIDI CC74 used in XG music to filter result sounding) scaling + * + * By default, brightness affects sound between 0 and 64. + * When this option is enabled, the brightness will use full range from 0 up to 127. + * + * @param device Instance of the library + * @param fr_brightness 0 - disabled, 1 - enabled + */ +extern OPNMIDI_DECLSPEC void opn2_setFullRangeBrightness(struct OPN2_MIDIPlayer *device, int fr_brightness); + +/** + * @brief Enable or disable built-in loop (built-in loop supports 'loopStart' and 'loopEnd' tags to loop specific part) + * @param device Instance of the library + * @param loopEn 0 - disabled, 1 - enabled + */ +extern OPNMIDI_DECLSPEC void opn2_setLoopEnabled(struct OPN2_MIDIPlayer *device, int loopEn); + +/** + * @brief Enable or disable soft panning with chip emulators + * @param device Instance of the library + * @param softPanEn 0 - disabled, 1 - enabled + */ +extern OPNMIDI_DECLSPEC void opn2_setSoftPanEnabled(struct OPN2_MIDIPlayer *device, int softPanEn); + +/** + * @brief [DEPRECATED] Enable or disable Logarithmic volume changer + * + * This function is deprecated. Suggested replacement: `opn2_setVolumeRangeModel` with `OPNMIDI_VolumeModel_NativeOPN2` volume model value; + */ +OPNMIDI_DEPRECATED("Use `opn2_setVolumeRangeModel(device, OPNMIDI_VolumeModel_NativeOPN2)` instead") +extern OPNMIDI_DECLSPEC void opn2_setLogarithmicVolumes(struct OPN2_MIDIPlayer *device, int logvol); + +/** + * @brief Set different volume range model + * @param device Instance of the library + * @param volumeModel Volume model type (#OPNMIDI_VolumeModels) + */ +extern OPNMIDI_DECLSPEC void opn2_setVolumeRangeModel(struct OPN2_MIDIPlayer *device, int volumeModel); + +/** + * @brief Get the volume range model + * @param device Instance of the library + * @return volume model on success, <0 when any error has occurred + */ +extern OPNMIDI_DECLSPEC int opn2_getVolumeRangeModel(struct OPN2_MIDIPlayer *device); + +/** + * @brief Load WOPN bank file from File System + * + * Is recommended to call adl_reset() to apply changes to already-loaded file player or real-time. + * + * @param device Instance of the library + * @param filePath Absolute or relative path to the WOPL bank file. UTF8 encoding is required, even on Windows. + * @return 0 on success, <0 when any error has occurred + */ +extern OPNMIDI_DECLSPEC int opn2_openBankFile(struct OPN2_MIDIPlayer *device, const char *filePath); + +/** + * @brief Load WOPN bank file from memory data + * + * Is recommended to call adl_reset() to apply changes to already-loaded file player or real-time. + * + * @param device Instance of the library + * @param mem Pointer to memory block where is raw data of WOPL bank file is stored + * @param size Size of given memory block + * @return 0 on success, <0 when any error has occurred + */ +extern OPNMIDI_DECLSPEC int opn2_openBankData(struct OPN2_MIDIPlayer *device, const void *mem, long size); + + +/** + * @brief [DEPRECATED] Dummy function + * + * This function is deprecated. Suggested replacement: `opn2_chipEmulatorName` + * + * @return A string that contains a notice to use `opn2_chipEmulatorName` instead of this function. + */ +OPNMIDI_DEPRECATED("Use `adl_chipEmulatorName(device)` instead") +extern OPNMIDI_DECLSPEC const char *opn2_emulatorName(); + +/** + * @brief Returns chip emulator name string + * @param device Instance of the library + * @return Understandable name of current OPN2 emulator + */ +extern OPNMIDI_DECLSPEC const char *opn2_chipEmulatorName(struct OPN2_MIDIPlayer *device); + +/** + * @brief List of available OPN2 emulators + */ enum Opn2_Emulator { + /*! Mame YM2612 */ OPNMIDI_EMU_MAME = 0, + /*! Nuked OPN2 */ OPNMIDI_EMU_NUKED, + /*! GENS */ OPNMIDI_EMU_GENS, + /*! Genesis Plus GX (a fork of Mame YM2612) */ OPNMIDI_EMU_GX, + /*! Count instrument on the level */ OPNMIDI_EMU_end }; -/* Switch the emulation core */ -extern int opn2_switchEmulator(struct OPN2_MIDIPlayer *device, int emulator); +/** + * @brief Switch the emulation core + * @param device Instance of the library + * @param emulator Type of emulator (#Opn2_Emulator) + * @return 0 on success, <0 when any error has occurred + */ +extern OPNMIDI_DECLSPEC int opn2_switchEmulator(struct OPN2_MIDIPlayer *device, int emulator); +/** + * @brief Library version context + */ typedef struct { OPN2_UInt16 major; OPN2_UInt16 minor; OPN2_UInt16 patch; } OPN2_Version; -/*Run emulator with PCM rate to reduce CPU usage on slow devices. May decrease sounding accuracy.*/ -extern int opn2_setRunAtPcmRate(struct OPN2_MIDIPlayer *device, int enabled); +/** + * @brief Run emulator with PCM rate to reduce CPU usage on slow devices. + * + * May decrease sounding accuracy on some chip emulators. + * + * @param device Instance of the library + * @param enabled 0 - disabled, 1 - enabled + * @return 0 on success, <0 when any error has occurred + */ +extern OPNMIDI_DECLSPEC int opn2_setRunAtPcmRate(struct OPN2_MIDIPlayer *device, int enabled); -/*Returns string which contains a version number*/ -extern const char *opn2_linkedLibraryVersion(); +/** + * @brief Set 4-bit device identifier. Used by the SysEx processor. + * @param device Instance of the library + * @param id 4-bit device identifier + * @return 0 on success, <0 when any error has occurred + */ +extern OPNMIDI_DECLSPEC int opn2_setDeviceIdentifier(struct OPN2_MIDIPlayer *device, unsigned id); -/*Returns structure which contains a version number of library */ -extern const OPN2_Version *opn2_linkedVersion(); -/*Returns string which contains last error message*/ -extern const char *opn2_errorString(); +/** + * @section Information + */ -/*Returns string which contains last error message on specific device*/ -extern const char *opn2_errorInfo(struct OPN2_MIDIPlayer *device); +/** + * @brief Returns string which contains a version number + * @return String which contains a version of the library + */ +extern OPNMIDI_DECLSPEC const char *opn2_linkedLibraryVersion(); -/*Initialize ADLMIDI Player device*/ -extern struct OPN2_MIDIPlayer *opn2_init(long sample_rate); +/** + * @brief Returns structure which contains a version number of library + * @return Library version context structure which contains version number of the library + */ +extern OPNMIDI_DECLSPEC const OPN2_Version *opn2_linkedVersion(); -/*Load MIDI file from File System*/ -extern int opn2_openFile(struct OPN2_MIDIPlayer *device, const char *filePath); +/** + * @brief Returns string which contains last error message of initialization + * + * Don't use this function to get info on any function except of `opn2_init`! + * Use `opn2_errorInfo()` to get error information while workflow + * + * @return String with error message related to library initialization + */ +extern OPNMIDI_DECLSPEC const char *opn2_errorString(); -/*Load MIDI file from memory data*/ -extern int opn2_openData(struct OPN2_MIDIPlayer *device, const void *mem, unsigned long size); +/** + * @brief Returns string which contains last error message on specific device + * @param device Instance of the library + * @return String with error message related to last function call returned non-zero value. + */ +extern OPNMIDI_DECLSPEC const char *opn2_errorInfo(struct OPN2_MIDIPlayer *device); -/*Resets MIDI player*/ -extern void opn2_reset(struct OPN2_MIDIPlayer *device); -/*Get total time length of current song*/ -extern double opn2_totalTimeLength(struct OPN2_MIDIPlayer *device); +/* ======== Initialization ======== */ -/*Get loop start time if presented. -1 means MIDI file has no loop points */ -extern double opn2_loopStartTime(struct OPN2_MIDIPlayer *device); +/** + * @brief Initialize OPNMIDI Player device + * + * Tip 1: You can initialize multiple instances and run them in parallel + * Tip 2: Library is NOT thread-safe, therefore don't use same instance in different threads or use mutexes + * Tip 3: Changing of sample rate on the fly is not supported. Re-create the instance again. + * + * @param sample_rate Output sample rate + * @return Instance of the library. If NULL was returned, check the `adl_errorString` message for more info. + */ +extern OPNMIDI_DECLSPEC struct OPN2_MIDIPlayer *opn2_init(long sample_rate); -/*Get loop end time if presented. -1 means MIDI file has no loop points */ -extern double opn2_loopEndTime(struct OPN2_MIDIPlayer *device); +/** + * @brief Close and delete OPNMIDI device + * @param device Instance of the library + */ +extern OPNMIDI_DECLSPEC void opn2_close(struct OPN2_MIDIPlayer *device); -/*Get current time position in seconds*/ -extern double opn2_positionTell(struct OPN2_MIDIPlayer *device); -/*Jump to absolute time position in seconds*/ -extern void opn2_positionSeek(struct OPN2_MIDIPlayer *device, double seconds); +/* ======== MIDI Sequencer ======== */ -/*Reset MIDI track position to begin */ -extern void opn2_positionRewind(struct OPN2_MIDIPlayer *device); +/** + * @brief Load MIDI (or any other supported format) file from File System + * + * Available when library is built with built-in MIDI Sequencer support. + * + * @param device Instance of the library + * @param filePath Absolute or relative path to the music file. UTF8 encoding is required, even on Windows. + * @return 0 on success, <0 when any error has occurred + */ +extern OPNMIDI_DECLSPEC int opn2_openFile(struct OPN2_MIDIPlayer *device, const char *filePath); -/*Set tempo multiplier: 1.0 - original tempo, >1 - play faster, <1 - play slower */ -extern void opn2_setTempo(struct OPN2_MIDIPlayer *device, double tempo); +/** + * @brief Load MIDI (or any other supported format) file from memory data + * + * Available when library is built with built-in MIDI Sequencer support. + * + * @param device Instance of the library + * @param mem Pointer to memory block where is raw data of music file is stored + * @param size Size of given memory block + * @return 0 on success, <0 when any error has occurred + */ +extern OPNMIDI_DECLSPEC int opn2_openData(struct OPN2_MIDIPlayer *device, const void *mem, unsigned long size); -/*Close and delete OPNMIDI device*/ -extern void opn2_close(struct OPN2_MIDIPlayer *device); +/** + * @brief Resets MIDI player (per-channel setup) into initial state + * @param device Instance of the library + */ +extern OPNMIDI_DECLSPEC void opn2_reset(struct OPN2_MIDIPlayer *device); + +/** + * @brief Get total time length of current song + * + * Available when library is built with built-in MIDI Sequencer support. + * + * @param device Instance of the library + * @return Total song length in seconds + */ +extern OPNMIDI_DECLSPEC double opn2_totalTimeLength(struct OPN2_MIDIPlayer *device); + +/** + * @brief Get loop start time if presented. + * + * Available when library is built with built-in MIDI Sequencer support. + * + * @param device Instance of the library + * @return Time position in seconds of loop start point, or -1 when file has no loop points + */ +extern OPNMIDI_DECLSPEC double opn2_loopStartTime(struct OPN2_MIDIPlayer *device); + +/** + * @brief Get loop endtime if presented. + * + * Available when library is built with built-in MIDI Sequencer support. + * + * @param device Instance of the library + * @return Time position in seconds of loop end point, or -1 when file has no loop points + */ +extern OPNMIDI_DECLSPEC double opn2_loopEndTime(struct OPN2_MIDIPlayer *device); + +/** + * @brief Get current time position in seconds + * + * Available when library is built with built-in MIDI Sequencer support. + * + * @param device Instance of the library + * @return Current time position in seconds + */ +extern OPNMIDI_DECLSPEC double opn2_positionTell(struct OPN2_MIDIPlayer *device); + +/** + * @brief Jump to absolute time position in seconds + * + * Available when library is built with built-in MIDI Sequencer support. + * + * @param device Instance of the library + * @param seconds Destination time position in seconds to seek + */ +extern OPNMIDI_DECLSPEC void opn2_positionSeek(struct OPN2_MIDIPlayer *device, double seconds); + +/** + * @brief Reset MIDI track position to begin + * + * Available when library is built with built-in MIDI Sequencer support. + * + * @param device Instance of the library + */ +extern OPNMIDI_DECLSPEC void opn2_positionRewind(struct OPN2_MIDIPlayer *device); + +/** + * @brief Set tempo multiplier + * + * Available when library is built with built-in MIDI Sequencer support. + * + * @param device Instance of the library + * @param tempo Tempo multiplier value: 1.0 - original tempo, >1 - play faster, <1 - play slower + */ +extern OPNMIDI_DECLSPEC void opn2_setTempo(struct OPN2_MIDIPlayer *device, double tempo); + +/** + * @brief Returns 1 if music position has reached end + * @param device Instance of the library + * @return 1 when end of sing has been reached, otherwise, 0 will be returned. <0 is returned on any error + */ +extern OPNMIDI_DECLSPEC int opn2_atEnd(struct OPN2_MIDIPlayer *device); + +/** + * @brief Returns the number of tracks of the current sequence + * @param device Instance of the library + * @return Count of tracks in the current sequence + */ +extern OPNMIDI_DECLSPEC size_t opn2_trackCount(struct OPN2_MIDIPlayer *device); -/**META**/ +/* ======== Meta-Tags ======== */ -/*Returns string which contains a music title*/ -extern const char *opn2_metaMusicTitle(struct OPN2_MIDIPlayer *device); +/** + * @brief Returns string which contains a music title + * @param device Instance of the library + * @return A string that contains music title + */ +extern OPNMIDI_DECLSPEC const char *opn2_metaMusicTitle(struct OPN2_MIDIPlayer *device); -/*Returns string which contains a copyright string*/ -extern const char *opn2_metaMusicCopyright(struct OPN2_MIDIPlayer *device); +/** + * @brief Returns string which contains a copyright string* + * @param device Instance of the library + * @return A string that contains copyright notice, otherwise NULL + */ +extern OPNMIDI_DECLSPEC const char *opn2_metaMusicCopyright(struct OPN2_MIDIPlayer *device); -/*Returns count of available track titles: NOTE: there are CAN'T be associated with channel in any of event or note hooks */ -extern size_t opn2_metaTrackTitleCount(struct OPN2_MIDIPlayer *device); +/** + * @brief Returns count of available track titles + * + * NOTE: There are CAN'T be associated with channel in any of event or note hooks + * + * @param device Instance of the library + * @return Count of available MIDI tracks, otherwise NULL + */ +extern OPNMIDI_DECLSPEC size_t opn2_metaTrackTitleCount(struct OPN2_MIDIPlayer *device); -/*Get track title by index*/ -extern const char *opn2_metaTrackTitle(struct OPN2_MIDIPlayer *device, size_t index); +/** + * @brief Get track title by index + * @param device Instance of the library + * @param index Index of the track to retreive the title + * @return A string that contains track title, otherwise NULL. + */ +extern OPNMIDI_DECLSPEC const char *opn2_metaTrackTitle(struct OPN2_MIDIPlayer *device, size_t index); +/** + * @brief MIDI Marker structure + */ struct Opn2_MarkerEntry { + /*! MIDI Marker title */ const char *label; + /*! Absolute time position of the marker in seconds */ double pos_time; + /*! Absolute time position of the marker in MIDI ticks */ unsigned long pos_ticks; }; -/*Returns count of available markers*/ -extern size_t opn2_metaMarkerCount(struct OPN2_MIDIPlayer *device); +/** + * @brief Returns count of available markers + * @param device Instance of the library + * @return Count of available MIDI markers + */ +extern OPNMIDI_DECLSPEC size_t opn2_metaMarkerCount(struct OPN2_MIDIPlayer *device); -/*Returns the marker entry*/ -extern struct Opn2_MarkerEntry opn2_metaMarker(struct OPN2_MIDIPlayer *device, size_t index); +/** + * @brief Returns the marker entry + * @param device Instance of the library + * @param index Index of the marker to retreive it. + * @return MIDI Marker description structure. + */ +extern OPNMIDI_DECLSPEC struct Opn2_MarkerEntry opn2_metaMarker(struct OPN2_MIDIPlayer *device, size_t index); -/*Take a sample buffer and iterate MIDI timers */ -extern int opn2_play(struct OPN2_MIDIPlayer *device, int sampleCount, short *out); +/* ======== Audio output Generation ======== */ -/*Take a sample buffer and iterate MIDI timers */ -extern int opn2_playFormat(struct OPN2_MIDIPlayer *device, int sampleCount, OPN2_UInt8 *left, OPN2_UInt8 *right, const struct OPNMIDI_AudioFormat *format); +/** + * @brief Generate PCM signed 16-bit stereo audio output and iterate MIDI timers + * + * Use this function when you are playing MIDI file loaded by `adl_openFile` or by `adl_openData` + * with using of built-in MIDI sequencer. + * + * Don't use count of frames, use instead count of samples. One frame is two samples. + * So, for example, if you want to take 10 frames, you must to request amount of 20 samples! + * + * Available when library is built with built-in MIDI Sequencer support. + * + * @param device Instance of the library + * @param sampleCount Count of samples (not frames!) + * @param out Pointer to output with 16-bit stereo PCM output + * @return Count of given samples, otherwise, 0 or when catching an error while playing + */ +extern OPNMIDI_DECLSPEC int opn2_play(struct OPN2_MIDIPlayer *device, int sampleCount, short *out); -/*Generate audio output from chip emulators without iteration of MIDI timers.*/ -extern int opn2_generate(struct OPN2_MIDIPlayer *device, int sampleCount, short *out); +/** + * @brief Generate PCM stereo audio output in sample format declared by given context and iterate MIDI timers + * + * Use this function when you are playing MIDI file loaded by `adl_openFile` or by `adl_openData` + * with using of built-in MIDI sequencer. + * + * Don't use count of frames, use instead count of samples. One frame is two samples. + * So, for example, if you want to take 10 frames, you must to request amount of 20 samples! + * + * Available when library is built with built-in MIDI Sequencer support. + * + * @param device Instance of the library + * @param sampleCount Count of samples (not frames!) + * @param left Left channel buffer output (Must be casted into bytes array) + * @param right Right channel buffer output (Must be casted into bytes array) + * @param format Destination PCM format format context + * @return Count of given samples, otherwise, 0 or when catching an error while playing + */ +extern OPNMIDI_DECLSPEC int opn2_playFormat(struct OPN2_MIDIPlayer *device, int sampleCount, OPN2_UInt8 *left, OPN2_UInt8 *right, const struct OPNMIDI_AudioFormat *format); -/*Generate audio output from chip emulators without iteration of MIDI timers.*/ -extern int opn2_generateFormat(struct OPN2_MIDIPlayer *device, int sampleCount, OPN2_UInt8 *left, OPN2_UInt8 *right, const struct OPNMIDI_AudioFormat *format); +/** + * @brief Generate PCM signed 16-bit stereo audio output without iteration of MIDI timers + * + * Use this function when you are using library as Real-Time MIDI synthesizer or with + * an external MIDI sequencer. You must to request the amount of samples which is equal + * to the delta between of MIDI event rows. One MIDI row is a group of MIDI events + * are having zero delta/delay between each other. When you are receiving events in + * real time, request the minimal possible delay value. + * + * Don't use count of frames, use instead count of samples. One frame is two samples. + * So, for example, if you want to take 10 frames, you must to request amount of 20 samples! + * + * @param device Instance of the library + * @param sampleCount + * @param out Pointer to output with 16-bit stereo PCM output + * @return Count of given samples, otherwise, 0 or when catching an error while playing + */ +extern OPNMIDI_DECLSPEC int opn2_generate(struct OPN2_MIDIPlayer *device, int sampleCount, short *out); + +/** + * @brief Generate PCM stereo audio output in sample format declared by given context without iteration of MIDI timers + * + * Use this function when you are using library as Real-Time MIDI synthesizer or with + * an external MIDI sequencer. You must to request the amount of samples which is equal + * to the delta between of MIDI event rows. One MIDI row is a group of MIDI events + * are having zero delta/delay between each other. When you are receiving events in + * real time, request the minimal possible delay value. + * + * Don't use count of frames, use instead count of samples. One frame is two samples. + * So, for example, if you want to take 10 frames, you must to request amount of 20 samples! + * + * @param device Instance of the library + * @param sampleCount + * @param left Left channel buffer output (Must be casted into bytes array) + * @param right Right channel buffer output (Must be casted into bytes array) + * @param format Destination PCM format format context + * @return Count of given samples, otherwise, 0 or when catching an error while playing + */ +extern OPNMIDI_DECLSPEC int opn2_generateFormat(struct OPN2_MIDIPlayer *device, int sampleCount, OPN2_UInt8 *left, OPN2_UInt8 *right, const struct OPNMIDI_AudioFormat *format); /** * @brief Periodic tick handler. @@ -254,63 +786,291 @@ extern int opn2_generateFormat(struct OPN2_MIDIPlayer *device, int sampleCount, * Use it for Hardware OPL3 mode or when you want to process events differently from opn2_play() function. * DON'T USE IT TOGETHER WITH opn2_play()!!! */ -extern double opn2_tickEvents(struct OPN2_MIDIPlayer *device, double seconds, double granuality); +extern OPNMIDI_DECLSPEC double opn2_tickEvents(struct OPN2_MIDIPlayer *device, double seconds, double granuality); -/*Returns 1 if music position has reached end*/ -extern int opn2_atEnd(struct OPN2_MIDIPlayer *device); +/** + * @brief Track options + */ +enum OPNMIDI_TrackOptions +{ + /*! Enabled track */ + OPNMIDI_TrackOption_On = 1, + /*! Disabled track */ + OPNMIDI_TrackOption_Off = 2, + /*! Solo track */ + OPNMIDI_TrackOption_Solo = 3, +}; -/**RealTime**/ - -/*Force Off all notes on all channels*/ -extern void opn2_panic(struct OPN2_MIDIPlayer *device); - -/*Reset states of all controllers on all MIDI channels*/ -extern void opn2_rt_resetState(struct OPN2_MIDIPlayer *device); - -/*Turn specific MIDI note ON*/ -extern int opn2_rt_noteOn(struct OPN2_MIDIPlayer *device, OPN2_UInt8 channel, OPN2_UInt8 note, OPN2_UInt8 velocity); - -/*Turn specific MIDI note OFF*/ -extern void opn2_rt_noteOff(struct OPN2_MIDIPlayer *device, OPN2_UInt8 channel, OPN2_UInt8 note); - -/*Set note after-touch*/ -extern void opn2_rt_noteAfterTouch(struct OPN2_MIDIPlayer *device, OPN2_UInt8 channel, OPN2_UInt8 note, OPN2_UInt8 atVal); -/*Set channel after-touch*/ -extern void opn2_rt_channelAfterTouch(struct OPN2_MIDIPlayer *device, OPN2_UInt8 channel, OPN2_UInt8 atVal); - -/*Apply controller change*/ -extern void opn2_rt_controllerChange(struct OPN2_MIDIPlayer *device, OPN2_UInt8 channel, OPN2_UInt8 type, OPN2_UInt8 value); - -/*Apply patch change*/ -extern void opn2_rt_patchChange(struct OPN2_MIDIPlayer *device, OPN2_UInt8 channel, OPN2_UInt8 patch); - -/*Apply pitch bend change*/ -extern void opn2_rt_pitchBend(struct OPN2_MIDIPlayer *device, OPN2_UInt8 channel, OPN2_UInt16 pitch); -/*Apply pitch bend change*/ -extern void opn2_rt_pitchBendML(struct OPN2_MIDIPlayer *device, OPN2_UInt8 channel, OPN2_UInt8 msb, OPN2_UInt8 lsb); - -/*Change LSB of the bank*/ -extern void opn2_rt_bankChangeLSB(struct OPN2_MIDIPlayer *device, OPN2_UInt8 channel, OPN2_UInt8 lsb); -/*Change MSB of the bank*/ -extern void opn2_rt_bankChangeMSB(struct OPN2_MIDIPlayer *device, OPN2_UInt8 channel, OPN2_UInt8 msb); -/*Change bank by absolute signed value*/ -extern void opn2_rt_bankChange(struct OPN2_MIDIPlayer *device, OPN2_UInt8 channel, OPN2_SInt16 bank); +/** + * @brief Sets options on a track of the current sequence + * @param device Instance of the library + * @param trackNumber Identifier of the designated track. + * @return 0 on success, <0 when any error has occurred + */ +extern OPNMIDI_DECLSPEC int opn2_setTrackOptions(struct OPN2_MIDIPlayer *device, size_t trackNumber, unsigned trackOptions); -/**Hooks**/ + +/* ======== Real-Time MIDI ======== */ + +/** + * @brief Force Off all notes on all channels + * @param device Instance of the library + */ +extern OPNMIDI_DECLSPEC void opn2_panic(struct OPN2_MIDIPlayer *device); + +/** + * @brief Reset states of all controllers on all MIDI channels + * @param device Instance of the library + */ +extern OPNMIDI_DECLSPEC void opn2_rt_resetState(struct OPN2_MIDIPlayer *device); + +/** + * @brief Turn specific MIDI note ON + * @param device Instance of the library + * @param channel Target MIDI channel [Between 0 and 16] + * @param note Note number to on [Between 0 and 127] + * @param velocity Velocity level [Between 0 and 127] + * @return 1 when note was successfully started, 0 when note was rejected by any reason. + */ +extern OPNMIDI_DECLSPEC int opn2_rt_noteOn(struct OPN2_MIDIPlayer *device, OPN2_UInt8 channel, OPN2_UInt8 note, OPN2_UInt8 velocity); + +/** + * @brief Turn specific MIDI note OFF + * @param device Instance of the library + * @param channel Target MIDI channel [Between 0 and 16] + * @param note Note number to off [Between 0 and 127] + */ +extern OPNMIDI_DECLSPEC void opn2_rt_noteOff(struct OPN2_MIDIPlayer *device, OPN2_UInt8 channel, OPN2_UInt8 note); + +/** + * @brief Set note after-touch + * @param device Instance of the library + * @param channel Target MIDI channel [Between 0 and 16] + * @param note Note number to affect by aftertouch event [Between 0 and 127] + * @param atVal After-Touch value [Between 0 and 127] + */ +extern OPNMIDI_DECLSPEC void opn2_rt_noteAfterTouch(struct OPN2_MIDIPlayer *device, OPN2_UInt8 channel, OPN2_UInt8 note, OPN2_UInt8 atVal); + +/** + * @brief Set channel after-touch + * @param device Instance of the library + * @param channel Target MIDI channel [Between 0 and 16] + * @param atVal After-Touch level [Between 0 and 127] + */ +extern OPNMIDI_DECLSPEC void opn2_rt_channelAfterTouch(struct OPN2_MIDIPlayer *device, OPN2_UInt8 channel, OPN2_UInt8 atVal); + +/** + * @brief Apply controller change + * @param device Instance of the library + * @param channel Target MIDI channel [Between 0 and 16] + * @param type Type of the controller [Between 0 and 255] + * @param value Value of the controller event [Between 0 and 127] + */ +extern OPNMIDI_DECLSPEC void opn2_rt_controllerChange(struct OPN2_MIDIPlayer *device, OPN2_UInt8 channel, OPN2_UInt8 type, OPN2_UInt8 value); + +/** + * @brief Apply patch change + * @param device Instance of the library + * @param channel Target MIDI channel [Between 0 and 16] + * @param patch Patch number [Between 0 and 127] + */ +extern OPNMIDI_DECLSPEC void opn2_rt_patchChange(struct OPN2_MIDIPlayer *device, OPN2_UInt8 channel, OPN2_UInt8 patch); + +/** + * @brief Apply pitch bend change + * @param device Instance of the library + * @param channel Target MIDI channel [Between 0 and 16] + * @param pitch 24-bit pitch bend value + */ +extern OPNMIDI_DECLSPEC void opn2_rt_pitchBend(struct OPN2_MIDIPlayer *device, OPN2_UInt8 channel, OPN2_UInt16 pitch); + +/** + * @brief Apply pitch bend change + * @param device Instance of the library + * @param channel Target MIDI channel [Between 0 and 16] + * @param msb MSB part of 24-bit pitch bend value + * @param lsb LSB part of 24-bit pitch bend value + */ +extern OPNMIDI_DECLSPEC void opn2_rt_pitchBendML(struct OPN2_MIDIPlayer *device, OPN2_UInt8 channel, OPN2_UInt8 msb, OPN2_UInt8 lsb); + +/** + * @brief Change LSB of the bank number (Alias to CC-32 event) + * @param device Instance of the library + * @param channel Target MIDI channel [Between 0 and 16] + * @param lsb LSB value of the MIDI bank number + */ +extern OPNMIDI_DECLSPEC void opn2_rt_bankChangeLSB(struct OPN2_MIDIPlayer *device, OPN2_UInt8 channel, OPN2_UInt8 lsb); + +/** + * @brief Change MSB of the bank (Alias to CC-0 event) + * @param device Instance of the library + * @param channel Target MIDI channel [Between 0 and 16] + * @param msb MSB value of the MIDI bank number + */ +extern OPNMIDI_DECLSPEC void opn2_rt_bankChangeMSB(struct OPN2_MIDIPlayer *device, OPN2_UInt8 channel, OPN2_UInt8 msb); + +/** + * @brief Change bank by absolute signed value + * @param device Instance of the library + * @param channel Target MIDI channel [Between 0 and 16] + * @param bank Bank number as concoctated signed 16-bit value of MSB and LSB parts. + */ +extern OPNMIDI_DECLSPEC void opn2_rt_bankChange(struct OPN2_MIDIPlayer *device, OPN2_UInt8 channel, OPN2_SInt16 bank); + +/** + * @brief Perform a system exclusive message + * @param device Instance of the library + * @param msg Raw SysEx message buffer (must begin with 0xF0 and end with 0xF7) + * @param size Size of given SysEx message buffer + * @return 1 when SysEx message was successfully processed, 0 when SysEx message was rejected by any reason + */ +extern OPNMIDI_DECLSPEC int opn2_rt_systemExclusive(struct OPN2_MIDIPlayer *device, const OPN2_UInt8 *msg, size_t size); + +/* ======== Hooks and debugging ======== */ + +/** + * @brief Raw event callback + * @param userdata Pointer to user data (usually, context of someting) + * @param type MIDI event type + * @param subtype MIDI event sub-type (special events only) + * @param channel MIDI channel + * @param data Raw event data + * @param len Length of event data + */ typedef void (*OPN2_RawEventHook)(void *userdata, OPN2_UInt8 type, OPN2_UInt8 subtype, OPN2_UInt8 channel, const OPN2_UInt8 *data, size_t len); +/** + * @brief Note on/off callback + * @param userdata Pointer to user data (usually, context of someting) + * @param adlchn Chip channel where note was played + * @param note Note number [between 0 and 127] + * @param pressure Velocity level, or -1 when it's note off event + * @param bend Pitch bend offset value + */ typedef void (*OPN2_NoteHook)(void *userdata, int adlchn, int note, int ins, int pressure, double bend); + +/** + * @brief Debug messages callback + * @param userdata Pointer to user data (usually, context of someting) + * @param fmt Format strign output (in context of `printf()` standard function) + */ typedef void (*OPN2_DebugMessageHook)(void *userdata, const char *fmt, ...); -/* Set raw MIDI event hook */ -extern void opn2_setRawEventHook(struct OPN2_MIDIPlayer *device, OPN2_RawEventHook rawEventHook, void *userData); +/** + * @brief Set raw MIDI event hook + * @param device Instance of the library + * @param rawEventHook Pointer to the callback function which will be called on every MIDI event + * @param userData Pointer to user data which will be passed through the callback. + */ +extern OPNMIDI_DECLSPEC void opn2_setRawEventHook(struct OPN2_MIDIPlayer *device, OPN2_RawEventHook rawEventHook, void *userData); -/* Set note hook */ -extern void opn2_setNoteHook(struct OPN2_MIDIPlayer *device, OPN2_NoteHook noteHook, void *userData); +/** + * @brief Set note hook + * @param device Instance of the library + * @param noteHook Pointer to the callback function which will be called on every noteOn MIDI event + * @param userData Pointer to user data which will be passed through the callback. + */ +extern OPNMIDI_DECLSPEC void opn2_setNoteHook(struct OPN2_MIDIPlayer *device, OPN2_NoteHook noteHook, void *userData); -/* Set debug message hook */ -extern void opn2_setDebugMessageHook(struct OPN2_MIDIPlayer *device, OPN2_DebugMessageHook debugMessageHook, void *userData); +/** + * @brief Set debug message hook + * @param device Instance of the library + * @param debugMessageHook Pointer to the callback function which will be called on every debug message + * @param userData Pointer to user data which will be passed through the callback. + */ +extern OPNMIDI_DECLSPEC void opn2_setDebugMessageHook(struct OPN2_MIDIPlayer *device, OPN2_DebugMessageHook debugMessageHook, void *userData); + + +/** + * @brief Get a textual description of the channel state. For display only. + * @param device Instance of the library + * @param text Destination char buffer for channel usage state. Every entry is assigned to the chip channel. + * @param attr Destination char buffer for additional attributes like MIDI channel number that uses this chip channel. + * @param size Size of given buffers (both text and attr are must have same size!) + * @return 0 on success, <0 when any error has occurred + * + * Every character in the `text` buffer means the type of usage: + * ``` + * `-` - channel is unused (free) + * `+` - channel is used by regular voice + * `@` - channel is used to play automatic arpeggio on chip channels overflow + * ``` + * + * The `attr` field receives the MIDI channel from which the chip channel is used. + * To get the valid MIDI channel you will need to apply the & 0x0F mask to every value. + */ +extern OPNMIDI_DECLSPEC int opn2_describeChannels(struct OPN2_MIDIPlayer *device, char *text, char *attr, size_t size); + + + + +/* ======== Instrument structures ======== */ + +/** + * @brief Version of the instrument data format + */ +enum +{ + OPNMIDI_InstrumentVersion = 0 +}; + +/** + * @brief Instrument flags + */ +typedef enum OPN2_InstrumentFlags +{ + OPNMIDI_Ins_Pseudo8op = 0x01, /*Reserved for future use, not implemented yet*/ + OPNMIDI_Ins_IsBlank = 0x02 +} OPN2_InstrumentFlags; + +/** + * @brief Operator structure, part of Instrument structure + */ +typedef struct OPN2_Operator +{ + /* Detune and frequency multiplication register data */ + OPN2_UInt8 dtfm_30; + /* Total level register data */ + OPN2_UInt8 level_40; + /* Rate scale and attack register data */ + OPN2_UInt8 rsatk_50; + /* Amplitude modulation enable and Decay-1 register data */ + OPN2_UInt8 amdecay1_60; + /* Decay-2 register data */ + OPN2_UInt8 decay2_70; + /* Sustain and Release register data */ + OPN2_UInt8 susrel_80; + /* SSG-EG register data */ + OPN2_UInt8 ssgeg_90; +} OPN2_Operator; + +/** + * @brief Instrument structure + */ +typedef struct OPN2_Instrument +{ + /*! Version of the instrument object */ + int version; + /* MIDI note key (half-tone) offset for an instrument (or a first voice in pseudo-4-op mode) */ + OPN2_SInt16 note_offset; + /* Reserved */ + OPN2_SInt8 midi_velocity_offset; + /* Percussion MIDI base tone number at which this drum will be played */ + OPN2_UInt8 percussion_key_number; + /* Instrument flags */ + OPN2_UInt8 inst_flags; + /* Feedback and Algorithm register data */ + OPN2_UInt8 fbalg; + /* LFO Sensitivity register data */ + OPN2_UInt8 lfosens; + /* Operators register data */ + OPN2_Operator operators[4]; + /* Millisecond delay of sounding while key is on */ + OPN2_UInt16 delay_on_ms; + /* Millisecond delay of sounding after key off */ + OPN2_UInt16 delay_off_ms; +} OPN2_Instrument; #ifdef __cplusplus } diff --git a/src/sound/opnmidi/opnmidi_bankmap.h b/src/sound/opnmidi/opnmidi_bankmap.h index eb05566bc..98293b899 100644 --- a/src/sound/opnmidi/opnmidi_bankmap.h +++ b/src/sound/opnmidi/opnmidi_bankmap.h @@ -40,7 +40,7 @@ template class BasicBankMap { public: - typedef uint16_t key_type; /* the bank identifier */ + typedef size_t key_type; /* the bank identifier */ typedef T mapped_type; typedef std::pair value_type; diff --git a/src/sound/opnmidi/opnmidi_cvt.hpp b/src/sound/opnmidi/opnmidi_cvt.hpp new file mode 100644 index 000000000..b565b9bc4 --- /dev/null +++ b/src/sound/opnmidi/opnmidi_cvt.hpp @@ -0,0 +1,82 @@ +/* + * libOPNMIDI is a free MIDI to WAV conversion library with OPN2 (YM2612) emulation + * + * MIDI parser and player (Original code from ADLMIDI): Copyright (c) 2010-2014 Joel Yliluoma + * ADLMIDI Library API: Copyright (c) 2015-2018 Vitaly Novichkov + * + * Library is based on the ADLMIDI, a MIDI player for Linux and Windows with OPL3 emulation: + * http://iki.fi/bisqwit/source/adlmidi.html + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "opnbank.h" + +template +static void cvt_generic_to_FMIns(opnInstMeta2 &ins, const WOPNI &in) +{ + ins.tone = in.percussion_key_number; + ins.flags = in.inst_flags; + /* Junk, delete later */ + ins.fine_tune = 0.0; + /* Junk, delete later */ + + ins.opn[0].fbalg = in.fbalg; + ins.opn[0].lfosens = in.lfosens; + ins.opn[0].finetune = in.note_offset; + ins.midi_velocity_offset = in.midi_velocity_offset; + + for(size_t op = 0; op < 4; op++) + { + ins.opn[0].OPS[op].data[0] = in.operators[op].dtfm_30; + ins.opn[0].OPS[op].data[1] = in.operators[op].level_40; + ins.opn[0].OPS[op].data[2] = in.operators[op].rsatk_50; + ins.opn[0].OPS[op].data[3] = in.operators[op].amdecay1_60; + ins.opn[0].OPS[op].data[4] = in.operators[op].decay2_70; + ins.opn[0].OPS[op].data[5] = in.operators[op].susrel_80; + ins.opn[0].OPS[op].data[6] = in.operators[op].ssgeg_90; + } + + ins.opn[1] = ins.opn[0]; + + ins.ms_sound_kon = in.delay_on_ms; + ins.ms_sound_koff = in.delay_off_ms; +} + +template +static void cvt_FMIns_to_generic(WOPNI &ins, const opnInstMeta2 &in) +{ + ins.percussion_key_number = in.tone; + ins.inst_flags = in.flags; + + ins.fbalg = in.opn[0].fbalg; + ins.lfosens = in.opn[0].lfosens; + ins.note_offset = in.opn[0].finetune; + + ins.midi_velocity_offset = in.midi_velocity_offset; + + for(size_t op = 0; op < 4; op++) + { + ins.operators[op].dtfm_30 = in.opn[0].OPS[op].data[0]; + ins.operators[op].level_40 = in.opn[0].OPS[op].data[1]; + ins.operators[op].rsatk_50 = in.opn[0].OPS[op].data[2]; + ins.operators[op].amdecay1_60 = in.opn[0].OPS[op].data[3]; + ins.operators[op].decay2_70 = in.opn[0].OPS[op].data[4]; + ins.operators[op].susrel_80 = in.opn[0].OPS[op].data[5]; + ins.operators[op].ssgeg_90 = in.opn[0].OPS[op].data[6]; + } + + ins.delay_on_ms = in.ms_sound_kon; + ins.delay_off_ms = in.ms_sound_koff; +} diff --git a/src/sound/opnmidi/opnmidi_load.cpp b/src/sound/opnmidi/opnmidi_load.cpp index c52b8093c..1fd8a3332 100644 --- a/src/sound/opnmidi/opnmidi_load.cpp +++ b/src/sound/opnmidi/opnmidi_load.cpp @@ -22,542 +22,203 @@ */ #include "opnmidi_private.hpp" - -#ifndef OPNMIDI_DISABLE_MIDI_SEQUENCER -# ifndef OPNMIDI_DISABLE_MUS_SUPPORT -# include "opnmidi_mus2mid.h" -# endif -# ifndef OPNMIDI_DISABLE_XMI_SUPPORT -# include "opnmidi_xmi2mid.h" -# endif -#endif //OPNMIDI_DISABLE_MIDI_SEQUENCER - -uint64_t OPNMIDIplay::ReadBEint(const void *buffer, size_t nbytes) -{ - uint64_t result = 0; - const unsigned char *data = reinterpret_cast(buffer); - - for(unsigned n = 0; n < nbytes; ++n) - result = (result << 8) + data[n]; - - return result; -} - -uint64_t OPNMIDIplay::ReadLEint(const void *buffer, size_t nbytes) -{ - uint64_t result = 0; - const unsigned char *data = reinterpret_cast(buffer); - - for(unsigned n = 0; n < nbytes; ++n) - result = result + static_cast(data[n] << (n * 8)); - - return result; -} - -//uint64_t OPNMIDIplay::ReadVarLenEx(size_t tk, bool &ok) -//{ -// uint64_t result = 0; -// ok = false; - -// for(;;) -// { -// if(tk >= TrackData.size()) -// return 1; - -// if(tk >= CurrentPosition.track.size()) -// return 2; - -// size_t ptr = CurrentPosition.track[tk].ptr; - -// if(ptr >= TrackData[tk].size()) -// return 3; - -// unsigned char byte = TrackData[tk][CurrentPosition.track[tk].ptr++]; -// result = (result << 7) + (byte & 0x7F); - -// if(!(byte & 0x80)) break; -// } - -// ok = true; -// return result; -//} +#include "opnmidi_cvt.hpp" +#include "wopn/wopn_file.h" bool OPNMIDIplay::LoadBank(const std::string &filename) { - fileReader file; + FileAndMemReader file; file.openFile(filename.c_str()); return LoadBank(file); } bool OPNMIDIplay::LoadBank(const void *data, size_t size) { - fileReader file; + FileAndMemReader file; file.openData(data, (size_t)size); return LoadBank(file); } -size_t readU16BE(OPNMIDIplay::fileReader &fr, uint16_t &out) +void cvt_OPNI_to_FMIns(opnInstMeta2 &ins, const OPN2_Instrument &in) { - uint8_t arr[2]; - size_t ret = fr.read(arr, 1, 2); - out = arr[1]; - out |= ((arr[0] << 8) & 0xFF00); - return ret; + return cvt_generic_to_FMIns(ins, in); } -size_t readS16BE(OPNMIDIplay::fileReader &fr, int16_t &out) +void cvt_FMIns_to_OPNI(OPN2_Instrument &ins, const opnInstMeta2 &in) { - uint8_t arr[2]; - size_t ret = fr.read(arr, 1, 2); - out = *reinterpret_cast(&arr[0]); - out *= 1 << 8; - out |= arr[1]; - return ret; + cvt_FMIns_to_generic(ins, in); } -int16_t toSint16BE(uint8_t *arr) -{ - int16_t num = *reinterpret_cast(&arr[0]); - num *= 1 << 8; - num |= arr[1]; - return num; -} - -static uint16_t toUint16LE(const uint8_t *arr) -{ - uint16_t num = arr[0]; - num |= ((arr[1] << 8) & 0xFF00); - return num; -} - -static uint16_t toUint16BE(const uint8_t *arr) -{ - uint16_t num = arr[1]; - num |= ((arr[0] << 8) & 0xFF00); - return num; -} - - -static const char *wopn2_magic1 = "WOPN2-BANK\0"; -static const char *wopn2_magic2 = "WOPN2-B2NK\0"; - -#define WOPL_INST_SIZE_V1 65 -#define WOPL_INST_SIZE_V2 69 - -static const uint16_t latest_version = 2; - -bool OPNMIDIplay::LoadBank(OPNMIDIplay::fileReader &fr) +bool OPNMIDIplay::LoadBank(FileAndMemReader &fr) { + int err = 0; + WOPNFile *wopn = NULL; + char *raw_file_data = NULL; size_t fsize; - ADL_UNUSED(fsize); if(!fr.isValid()) { - errorStringOut = "Can't load bank file: Invalid data stream!"; + errorStringOut = "Custom bank: Invalid data stream!"; return false; } - char magic[32]; - std::memset(magic, 0, 32); - uint16_t version = 1; - - uint16_t count_melodic_banks = 1; - uint16_t count_percussive_banks = 1; - - if(fr.read(magic, 1, 11) != 11) + // Read complete bank file into the memory + fsize = fr.fileSize(); + fr.seek(0, FileAndMemReader::SET); + // Allocate necessary memory block + raw_file_data = (char*)malloc(fsize); + if(!raw_file_data) { - errorStringOut = "Can't load bank file: Can't read magic number!"; + errorStringOut = "Custom bank: Out of memory before of read!"; return false; } + fr.read(raw_file_data, 1, fsize); - bool is1 = std::strncmp(magic, wopn2_magic1, 11) == 0; - bool is2 = std::strncmp(magic, wopn2_magic2, 11) == 0; + // Parse bank file from the memory + wopn = WOPN_LoadBankFromMem((void*)raw_file_data, fsize, &err); + //Free the buffer no more needed + free(raw_file_data); - if(!is1 && !is2) + // Check for any erros + if(!wopn) { - errorStringOut = "Can't load bank file: Invalid magic number!"; - return false; - } - - if(is2) - { - uint8_t ver[2]; - if(fr.read(ver, 1, 2) != 2) + switch(err) { - errorStringOut = "Can't load bank file: Can't read version number!"; + case WOPN_ERR_BAD_MAGIC: + errorStringOut = "Custom bank: Invalid magic!"; return false; - } - version = toUint16LE(ver); - if(version < 2 || version > latest_version) - { - errorStringOut = "Can't load bank file: unsupported WOPN version!"; + case WOPN_ERR_UNEXPECTED_ENDING: + errorStringOut = "Custom bank: Unexpected ending!"; + return false; + case WOPN_ERR_INVALID_BANKS_COUNT: + errorStringOut = "Custom bank: Invalid banks count!"; + return false; + case WOPN_ERR_NEWER_VERSION: + errorStringOut = "Custom bank: Version is newer than supported by this library!"; + return false; + case WOPN_ERR_OUT_OF_MEMORY: + errorStringOut = "Custom bank: Out of memory!"; + return false; + default: + errorStringOut = "Custom bank: Unknown error!"; return false; } } - opn.cleanInstrumentBanks(); - if((readU16BE(fr, count_melodic_banks) != 2) || (readU16BE(fr, count_percussive_banks) != 2)) + m_synth.m_insBankSetup.volumeModel = wopn->volume_model; + m_synth.m_insBankSetup.lfoEnable = (wopn->lfo_freq & 8) != 0; + m_synth.m_insBankSetup.lfoFrequency = wopn->lfo_freq & 7; + m_setup.VolumeModel = OPNMIDI_VolumeModel_AUTO; + m_setup.lfoEnable = -1; + m_setup.lfoFrequency = -1; + + m_synth.m_insBanks.clear(); + + uint16_t slots_counts[2] = {wopn->banks_count_melodic, wopn->banks_count_percussion}; + WOPNBank *slots_src_ins[2] = { wopn->banks_melodic, wopn->banks_percussive }; + + for(size_t ss = 0; ss < 2; ss++) { - errorStringOut = "Can't load bank file: Can't read count of banks!"; - return false; - } - - if((count_melodic_banks < 1) || (count_percussive_banks < 1)) - { - errorStringOut = "Custom bank: Too few banks in this file!"; - return false; - } - - if(fr.read(&opn.regLFO, 1, 1) != 1) - { - errorStringOut = "Can't load bank file: Can't read LFO registry state!"; - return false; - } - - opn.cleanInstrumentBanks(); - - std::vector banks; - banks.reserve(count_melodic_banks + count_percussive_banks); - - if(version >= 2)//Read bank meta-entries - { - for(uint16_t i = 0; i < count_melodic_banks; i++) + for(size_t i = 0; i < slots_counts[ss]; i++) { - uint8_t bank_meta[34]; - if(fr.read(bank_meta, 1, 34) != 34) + size_t bankno = (slots_src_ins[ss][i].bank_midi_msb * 256) + + (slots_src_ins[ss][i].bank_midi_lsb) + + (ss ? size_t(OPN2::PercussionTag) : 0); + OPN2::Bank &bank = m_synth.m_insBanks[bankno]; + for(int j = 0; j < 128; j++) { - opn.cleanInstrumentBanks(); - errorStringOut = "Custom bank: Fail to read melodic bank meta-data!"; - return false; + opnInstMeta2 &ins = bank.ins[j]; + std::memset(&ins, 0, sizeof(opnInstMeta2)); + WOPNInstrument &inIns = slots_src_ins[ss][i].ins[j]; + cvt_generic_to_FMIns(ins, inIns); } - uint16_t bankno = uint16_t(bank_meta[33]) * 256 + uint16_t(bank_meta[32]); - OPN2::Bank &bank = opn.dynamic_banks[bankno]; - //strncpy(bank.name, char_p(bank_meta), 32); - banks.push_back(&bank); } - - for(uint16_t i = 0; i < count_percussive_banks; i++) - { - uint8_t bank_meta[34]; - if(fr.read(bank_meta, 1, 34) != 34) - { - opn.cleanInstrumentBanks(); - errorStringOut = "Custom bank: Fail to read percussion bank meta-data!"; - return false; - } - uint16_t bankno = uint16_t(bank_meta[33]) * 256 + uint16_t(bank_meta[32]) + OPN2::PercussionTag; - OPN2::Bank &bank = opn.dynamic_banks[bankno]; - //strncpy(bank.name, char_p(bank_meta), 32); - banks.push_back(&bank); - } - } - - size_t total = 128 * opn.dynamic_banks.size(); - - for(size_t i = 0; i < total; i++) - { - opnInstMeta2 &meta = banks[i / 128]->ins[i % 128]; - opnInstData &data = meta.opn[0]; - uint8_t idata[WOPL_INST_SIZE_V2]; - - size_t readSize = version >= 2 ? WOPL_INST_SIZE_V2 : WOPL_INST_SIZE_V1; - if(fr.read(idata, 1, readSize) != readSize) - { - opn.cleanInstrumentBanks(); - errorStringOut = "Can't load bank file: Failed to read instrument data"; - return false; - } - data.finetune = toSint16BE(idata + 32); - //Percussion instrument note number or a "fixed note sound" - meta.tone = idata[34]; - data.fbalg = idata[35]; - data.lfosens = idata[36]; - for(size_t op = 0; op < 4; op++) - { - size_t off = 37 + op * 7; - std::memcpy(data.OPS[op].data, idata + off, 7); - } - - meta.flags = 0; - if(version >= 2) - { - meta.ms_sound_kon = toUint16BE(idata + 65); - meta.ms_sound_koff = toUint16BE(idata + 67); - if((meta.ms_sound_kon == 0) && (meta.ms_sound_koff == 0)) - meta.flags |= opnInstMeta::Flag_NoSound; - } - else - { - meta.ms_sound_kon = 1000; - meta.ms_sound_koff = 500; - } - - meta.opn[1] = meta.opn[0]; - - /* Junk, delete later */ - meta.fine_tune = 0.0; - /* Junk, delete later */ } applySetup(); + WOPN_Free(wopn); + return true; } #ifndef OPNMIDI_DISABLE_MIDI_SEQUENCER + +bool OPNMIDIplay::LoadMIDI_pre() +{ + if(m_synth.m_insBanks.empty()) + { + errorStringOut = "Bank is not set! Please load any instruments bank by using of adl_openBankFile() or adl_openBankData() functions!"; + return false; + } + + /**** Set all properties BEFORE starting of actial file reading! ****/ + resetMIDI(); + applySetup(); + + return true; +} + +bool OPNMIDIplay::LoadMIDI_post() +{ + MidiSequencer::FileFormat format = m_sequencer.getFormat(); + if(format == MidiSequencer::Format_CMF) + { + errorStringOut = "OPNMIDI doesn't supports CMF, use ADLMIDI to play this file!"; + /* As joke, why not to try implemented the converter of patches from OPL3 into OPN2? */ + return false; + } + else if(format == MidiSequencer::Format_RSXX) + { + m_synth.m_musicMode = OPN2::MODE_RSXX; + m_synth.m_volumeScale = OPN2::VOLUME_Generic; + m_synth.m_numChips = 2; + } + else if(format == MidiSequencer::Format_IMF) + { + errorStringOut = "OPNMIDI doesn't supports IMF, use ADLMIDI to play this file!"; + /* Same as for CMF */ + return false; + } + + m_setup.tick_skip_samples_delay = 0; + m_synth.reset(m_setup.emulator, m_setup.PCM_RATE, this); // Reset OPN2 chip + m_chipChannels.clear(); + m_chipChannels.resize(m_synth.m_numChannels); + + return true; +} + + bool OPNMIDIplay::LoadMIDI(const std::string &filename) { - fileReader file; + FileAndMemReader file; file.openFile(filename.c_str()); - if(!LoadMIDI(file)) + if(!LoadMIDI_pre()) + return false; + if(!m_sequencer.loadMIDI(file)) + { + errorStringOut = m_sequencer.getErrorString(); + return false; + } + if(!LoadMIDI_post()) return false; return true; } bool OPNMIDIplay::LoadMIDI(const void *data, size_t size) { - fileReader file; + FileAndMemReader file; file.openData(data, size); - return LoadMIDI(file); -} - -bool OPNMIDIplay::LoadMIDI(OPNMIDIplay::fileReader &fr) -{ - size_t fsize; - ADL_UNUSED(fsize); - //! Temp buffer for conversion - AdlMIDI_CPtr cvt_buf; - errorString.clear(); - - if(opn.dynamic_banks.empty()) + if(!LoadMIDI_pre()) + return false; + if(!m_sequencer.loadMIDI(file)) { - errorStringOut = "Bank is not set! Please load any instruments bank by using of adl_openBankFile() or adl_openBankData() functions!"; + errorStringOut = m_sequencer.getErrorString(); return false; } - - if(!fr.isValid()) - { - errorStringOut = "Invalid data stream!\n"; - #ifndef _WIN32 - errorStringOut += std::strerror(errno); - #endif + if(!LoadMIDI_post()) return false; - } - - /**** Set all properties BEFORE starting of actial file reading! ****/ - applySetup(); - - atEnd = false; - loopStart = true; - invalidLoop = false; - - bool is_GMF = false; // GMD/MUS files (ScummVM) - bool is_RSXX = false; // RSXX, such as Cartooners - - const size_t HeaderSize = 4 + 4 + 2 + 2 + 2; // 14 - char HeaderBuf[HeaderSize] = ""; - size_t DeltaTicks = 192, TrackCount = 1; - -riffskip: - fsize = fr.read(HeaderBuf, 1, HeaderSize); - - if(std::memcmp(HeaderBuf, "RIFF", 4) == 0) - { - fr.seek(6l, SEEK_CUR); - goto riffskip; - } - - if(std::memcmp(HeaderBuf, "GMF\x1", 4) == 0) - { - // GMD/MUS files (ScummVM) - fr.seek(7 - static_cast(HeaderSize), SEEK_CUR); - is_GMF = true; - } - - #ifndef OPNMIDI_DISABLE_MUS_SUPPORT - else if(std::memcmp(HeaderBuf, "MUS\x1A", 4) == 0) - { - // MUS/DMX files (Doom) - fr.seek(0, SEEK_END); - size_t mus_len = fr.tell(); - fr.seek(0, SEEK_SET); - uint8_t *mus = (uint8_t *)malloc(mus_len); - if(!mus) - { - errorStringOut = "Out of memory!"; - return false; - } - fr.read(mus, 1, mus_len); - //Close source stream - fr.close(); - - uint8_t *mid = NULL; - uint32_t mid_len = 0; - int m2mret = OpnMidi_mus2midi(mus, static_cast(mus_len), - &mid, &mid_len, 0); - if(mus) free(mus); - if(m2mret < 0) - { - errorStringOut = "Invalid MUS/DMX data format!"; - return false; - } - cvt_buf.reset(mid); - //Open converted MIDI file - fr.openData(mid, static_cast(mid_len)); - //Re-Read header again! - goto riffskip; - } - #endif //OPNMIDI_DISABLE_MUS_SUPPORT - - #ifndef OPNMIDI_DISABLE_XMI_SUPPORT - else if(std::memcmp(HeaderBuf, "FORM", 4) == 0) - { - if(std::memcmp(HeaderBuf + 8, "XDIR", 4) != 0) - { - fr.close(); - errorStringOut = fr._fileName + ": Invalid format\n"; - return false; - } - - fr.seek(0, SEEK_END); - size_t mus_len = fr.tell(); - fr.seek(0, SEEK_SET); - uint8_t *mus = (uint8_t*)malloc(mus_len); - if(!mus) - { - errorStringOut = "Out of memory!"; - return false; - } - fr.read(mus, 1, mus_len); - //Close source stream - fr.close(); - - uint8_t *mid = NULL; - uint32_t mid_len = 0; - int m2mret = OpnMidi_xmi2midi(mus, static_cast(mus_len), - &mid, &mid_len, XMIDI_CONVERT_NOCONVERSION); - if(mus) free(mus); - if(m2mret < 0) - { - errorStringOut = "Invalid XMI data format!"; - return false; - } - cvt_buf.reset(mid); - //Open converted MIDI file - fr.openData(mid, static_cast(mid_len)); - //Re-Read header again! - goto riffskip; - } - #endif //OPNMIDI_DISABLE_XMI_SUPPORT - - else - { - // Try to identify RSXX format - if(HeaderBuf[0] == 0x7D) - { - fr.seek(0x6D, SEEK_SET); - fr.read(HeaderBuf, 6, 1); - if(std::memcmp(HeaderBuf, "rsxx}u", 6) == 0) - { - is_RSXX = true; - fr.seek(0x7D, SEEK_SET); - TrackCount = 1; - DeltaTicks = 60; - //opl.CartoonersVolumes = true; - opn.m_musicMode = OPN2::MODE_RSXX; - opn.m_volumeScale = OPN2::VOLUME_CMF; - } - } - - if(!is_RSXX) - { - if(std::memcmp(HeaderBuf, "MThd\0\0\0\6", 8) != 0) - { - fr.close(); - errorStringOut = fr._fileName + ": Invalid format, Header signature is unknown!\n"; - return false; - } - - /*size_t Fmt = ReadBEint(HeaderBuf + 8, 2);*/ - TrackCount = (size_t)ReadBEint(HeaderBuf + 10, 2); - DeltaTicks = (size_t)ReadBEint(HeaderBuf + 12, 2); - } - } - - TrackData.clear(); - TrackData.resize(TrackCount, std::vector()); - InvDeltaTicks = fraction(1, 1000000l * static_cast(DeltaTicks)); - Tempo = fraction(1, static_cast(DeltaTicks) * 2); - static const unsigned char EndTag[4] = {0xFF, 0x2F, 0x00, 0x00}; - size_t totalGotten = 0; - - for(size_t tk = 0; tk < TrackCount; ++tk) - { - // Read track header - size_t TrackLength; - { - if(is_GMF || is_RSXX) // Take the rest of the file - { - size_t pos = fr.tell(); - fr.seek(0, SEEK_END); - TrackLength = fr.tell() - pos; - fr.seek(static_cast(pos), SEEK_SET); - } - else - { - fsize = fr.read(HeaderBuf, 1, 8); - if(std::memcmp(HeaderBuf, "MTrk", 4) != 0) - { - fr.close(); - errorStringOut = fr._fileName + ": Invalid format, MTrk signature is not found!\n"; - return false; - } - TrackLength = (size_t)ReadBEint(HeaderBuf + 4, 4); - } - - // Read track data - TrackData[tk].resize(TrackLength); - fsize = fr.read(&TrackData[tk][0], 1, TrackLength); - totalGotten += fsize; - - if(is_GMF/*|| is_MUS*/) // Note: CMF does include the track end tag. - TrackData[tk].insert(TrackData[tk].end(), EndTag + 0, EndTag + 4); - if(is_RSXX)//Finalize raw track data with a zero - TrackData[tk].push_back(0); - - //bool ok = false; - //// Read next event time - //uint64_t tkDelay = ReadVarLenEx(tk, ok); - //if(ok) - // CurrentPosition.track[tk].delay = tkDelay; - //else - //{ - // std::stringstream msg; - // msg << fr._fileName << ": invalid variable length in the track " << tk << "! (error code " << tkDelay << ")"; - // OPN2MIDI_ErrorString = msg.str(); - // return false; - //} - } - } - - for(size_t tk = 0; tk < TrackCount; ++tk) - totalGotten += TrackData[tk].size(); - - if(totalGotten == 0) - { - errorStringOut = fr._fileName + ": Empty track data"; - return false; - } - - //Build new MIDI events table - if(!buildTrackData()) - { - errorStringOut = fr._fileName + ": MIDI data parsing error has occouped!\n" + errorString; - return false; - } - - opn.Reset(m_setup.emulator, m_setup.PCM_RATE); // Reset OPN2 chip - ch.clear(); - ch.resize(opn.NumChannels); return true; } + #endif //OPNMIDI_DISABLE_MIDI_SEQUENCER diff --git a/src/sound/opnmidi/opnmidi_midiplay.cpp b/src/sound/opnmidi/opnmidi_midiplay.cpp index 5a93bb36e..6e9f3bc0e 100644 --- a/src/sound/opnmidi/opnmidi_midiplay.cpp +++ b/src/sound/opnmidi/opnmidi_midiplay.cpp @@ -25,7 +25,7 @@ // Mapping from MIDI volume level to OPL level value. -static const uint32_t DMX_volume_mapping_table[] = +static const uint_fast32_t DMX_volume_mapping_table[128] = { 0, 1, 3, 5, 6, 8, 10, 11, 13, 14, 16, 17, 19, 20, 22, 23, @@ -43,26 +43,9 @@ static const uint32_t DMX_volume_mapping_table[] = 116, 117, 117, 118, 118, 119, 119, 120, 120, 121, 121, 122, 122, 123, 123, 123, 124, 124, 125, 125, 126, 126, 127, 127, - //Protection entries to avoid crash if value more than 127 - 127, 127, 127, 127, 127, 127, 127, 127, - 127, 127, 127, 127, 127, 127, 127, 127, - 127, 127, 127, 127, 127, 127, 127, 127, - 127, 127, 127, 127, 127, 127, 127, 127, - 127, 127, 127, 127, 127, 127, 127, 127, - 127, 127, 127, 127, 127, 127, 127, 127, - 127, 127, 127, 127, 127, 127, 127, 127, - 127, 127, 127, 127, 127, 127, 127, 127, - 127, 127, 127, 127, 127, 127, 127, 127, - 127, 127, 127, 127, 127, 127, 127, 127, - 127, 127, 127, 127, 127, 127, 127, 127, - 127, 127, 127, 127, 127, 127, 127, 127, - 127, 127, 127, 127, 127, 127, 127, 127, - 127, 127, 127, 127, 127, 127, 127, 127, - 127, 127, 127, 127, 127, 127, 127, 127, - 127, 127, 127, 127, 127, 127, 127, 127, }; -static const uint8_t W9X_volume_mapping_table[32] = +static const uint_fast32_t W9X_volume_mapping_table[32] = { 63, 63, 40, 36, 32, 28, 23, 21, 19, 17, 15, 14, 13, 12, 11, 10, @@ -70,609 +53,46 @@ static const uint8_t W9X_volume_mapping_table[32] = 3, 3, 2, 2, 1, 1, 0, 0 }; +enum { MasterVolumeDefault = 127 }; + inline bool isXgPercChannel(uint8_t msb, uint8_t lsb) { return (msb == 0x7E || msb == 0x7F) && (lsb == 0); } -void OPNMIDIplay::OpnChannel::AddAge(int64_t ms) +void OPNMIDIplay::OpnChannel::addAge(int64_t us) { - const int64_t neg = static_cast(-0x1FFFFFFFl); + const int64_t neg = 1000 * static_cast(-0x1FFFFFFFl); if(users_empty()) - koff_time_until_neglible = - std::max(int64_t(koff_time_until_neglible - ms), neg); + { + koff_time_until_neglible_us = std::max(koff_time_until_neglible_us - us, neg); + if(koff_time_until_neglible_us < 0) + koff_time_until_neglible_us = 0; + } else { - koff_time_until_neglible = 0; - + koff_time_until_neglible_us = 0; for(LocationData *i = users_first; i; i = i->next) { if(!i->fixed_sustain) - i->kon_time_until_neglible = std::max(i->kon_time_until_neglible - ms, neg); - i->vibdelay += ms; + i->kon_time_until_neglible_us = std::max(i->kon_time_until_neglible_us - us, neg); + i->vibdelay_us += us; } } } -#ifndef OPNMIDI_DISABLE_MIDI_SEQUENCER - -OPNMIDIplay::MidiEvent::MidiEvent() : - type(T_UNKNOWN), - subtype(T_UNKNOWN), - channel(0), - isValid(1), - absPosition(0) -{} - - -OPNMIDIplay::MidiTrackRow::MidiTrackRow() : - time(0.0), - delay(0), - absPos(0), - timeDelay(0.0) -{} - -void OPNMIDIplay::MidiTrackRow::reset() -{ - time = 0.0; - delay = 0; - absPos = 0; - timeDelay = 0.0; - events.clear(); -} - -void OPNMIDIplay::MidiTrackRow::sortEvents(bool *noteStates) -{ - typedef std::vector EvtArr; - EvtArr metas; - EvtArr noteOffs; - EvtArr controllers; - EvtArr anyOther; - - metas.reserve(events.size()); - noteOffs.reserve(events.size()); - controllers.reserve(events.size()); - anyOther.reserve(events.size()); - - for(size_t i = 0; i < events.size(); i++) - { - if(events[i].type == MidiEvent::T_NOTEOFF) - noteOffs.push_back(events[i]); - else if((events[i].type == MidiEvent::T_CTRLCHANGE) - || (events[i].type == MidiEvent::T_PATCHCHANGE) - || (events[i].type == MidiEvent::T_WHEEL) - || (events[i].type == MidiEvent::T_CHANAFTTOUCH)) - { - controllers.push_back(events[i]); - } - else if((events[i].type == MidiEvent::T_SPECIAL) && (events[i].subtype == MidiEvent::ST_MARKER)) - metas.push_back(events[i]); - else - anyOther.push_back(events[i]); - } - - /* - * If Note-Off and it's Note-On is on the same row - move this damned note off down! - */ - if(noteStates) - { - std::set markAsOn; - for(size_t i = 0; i < anyOther.size(); i++) - { - const MidiEvent e = anyOther[i]; - if(e.type == MidiEvent::T_NOTEON) - { - const size_t note_i = (e.channel * 255) + (e.data[0] & 0x7F); - //Check, was previously note is on or off - bool wasOn = noteStates[note_i]; - markAsOn.insert(note_i); - // Detect zero-length notes are following previously pressed note - int noteOffsOnSameNote = 0; - for(EvtArr::iterator j = noteOffs.begin(); j != noteOffs.end();) - { - //If note was off, and note-off on same row with note-on - move it down! - if( - ((*j).channel == e.channel) && - ((*j).data[0] == e.data[0]) - ) - { - //If note is already off OR more than one note-off on same row and same note - if(!wasOn || (noteOffsOnSameNote != 0)) - { - anyOther.push_back(*j); - j = noteOffs.erase(j); - markAsOn.erase(note_i); - continue; - } else { - //When same row has many note-offs on same row - //that means a zero-length note follows previous note - //it must be shuted down - noteOffsOnSameNote++; - } - } - j++; - } - } - } - - //Mark other notes as released - for(EvtArr::iterator j = noteOffs.begin(); j != noteOffs.end(); j++) - { - size_t note_i = (j->channel * 255) + (j->data[0] & 0x7F); - noteStates[note_i] = false; - } - - for(std::set::iterator j = markAsOn.begin(); j != markAsOn.end(); j++) - noteStates[*j] = true; - - } - /***********************************************************************************/ - - events.clear(); - events.insert(events.end(), noteOffs.begin(), noteOffs.end()); - events.insert(events.end(), metas.begin(), metas.end()); - events.insert(events.end(), controllers.begin(), controllers.end()); - events.insert(events.end(), anyOther.begin(), anyOther.end()); -} -#endif //OPNMIDI_DISABLE_MIDI_SEQUENCER - -#ifndef OPNMIDI_DISABLE_MIDI_SEQUENCER -bool OPNMIDIplay::buildTrackData() -{ - fullSongTimeLength = 0.0; - loopStartTime = -1.0; - loopEndTime = -1.0; - musTitle.clear(); - musCopyright.clear(); - musTrackTitles.clear(); - musMarkers.clear(); - caugh_missing_instruments.clear(); - caugh_missing_banks_melodic.clear(); - caugh_missing_banks_percussion.clear(); - trackDataNew.clear(); - const size_t trackCount = TrackData.size(); - trackDataNew.resize(trackCount, MidiTrackQueue()); - - invalidLoop = false; - bool gotLoopStart = false, gotLoopEnd = false, gotLoopEventInThisRow = false; - //! Tick position of loop start tag - uint64_t loopStartTicks = 0; - //! Tick position of loop end tag - uint64_t loopEndTicks = 0; - //! Full length of song in ticks - uint64_t ticksSongLength = 0; - //! Cache for error message strign - char error[150]; - - CurrentPositionNew.track.clear(); - CurrentPositionNew.track.resize(trackCount); - - //! Caches note on/off states. - bool noteStates[16 * 255]; - /* This is required to carefully detect zero-length notes * - * and avoid a move of "note-off" event over "note-on" while sort. * - * Otherwise, after sort those notes will play infinite sound */ - - //Tempo change events - std::vector tempos; - - /* - * TODO: Make this be safer for memory in case of broken input data - * which may cause going away of available track data (and then give a crash!) - * - * POST: Check this more carefully for possible vulnuabilities are can crash this - */ - for(size_t tk = 0; tk < trackCount; ++tk) - { - uint64_t abs_position = 0; - int status = 0; - MidiEvent event; - bool ok = false; - uint8_t *end = TrackData[tk].data() + TrackData[tk].size(); - uint8_t *trackPtr = TrackData[tk].data(); - std::memset(noteStates, 0, sizeof(noteStates)); - - //Time delay that follows the first event in the track - { - MidiTrackRow evtPos; - if(opn.m_musicMode == OPN2::MODE_RSXX) - ok = true; - else - evtPos.delay = ReadVarLenEx(&trackPtr, end, ok); - if(!ok) - { - int len = snprintf(error, 150, "buildTrackData: Can't read variable-length value at begin of track %d.\n", (int)tk); - if((len > 0) && (len < 150)) - errorString += std::string(error, (size_t)len); - return false; - } - - //HACK: Begin every track with "Reset all controllers" event to avoid controllers state break came from end of song - for(uint8_t chan = 0; chan < 16; chan++) - { - MidiEvent event; - event.type = MidiEvent::T_CTRLCHANGE; - event.channel = chan; - event.data.push_back(121); - event.data.push_back(0); - evtPos.events.push_back(event); - } - - evtPos.absPos = abs_position; - abs_position += evtPos.delay; - trackDataNew[tk].push_back(evtPos); - } - - MidiTrackRow evtPos; - do - { - event = parseEvent(&trackPtr, end, status); - if(!event.isValid) - { - int len = snprintf(error, 150, "buildTrackData: Fail to parse event in the track %d.\n", (int)tk); - if((len > 0) && (len < 150)) - errorString += std::string(error, (size_t)len); - return false; - } - - evtPos.events.push_back(event); - if(event.type == MidiEvent::T_SPECIAL) - { - if(event.subtype == MidiEvent::ST_TEMPOCHANGE) - { - event.absPosition = abs_position; - tempos.push_back(event); - } - else if(!invalidLoop && (event.subtype == MidiEvent::ST_LOOPSTART)) - { - /* - * loopStart is invalid when: - * - starts together with loopEnd - * - appears more than one time in same MIDI file - */ - if(gotLoopStart || gotLoopEventInThisRow) - invalidLoop = true; - else - { - gotLoopStart = true; - loopStartTicks = abs_position; - } - //In this row we got loop event, register this! - gotLoopEventInThisRow = true; - } - else if(!invalidLoop && (event.subtype == MidiEvent::ST_LOOPEND)) - { - /* - * loopEnd is invalid when: - * - starts before loopStart - * - starts together with loopStart - * - appars more than one time in same MIDI file - */ - if(gotLoopEnd || gotLoopEventInThisRow) - invalidLoop = true; - else - { - gotLoopEnd = true; - loopEndTicks = abs_position; - } - //In this row we got loop event, register this! - gotLoopEventInThisRow = true; - } - } - - if(event.subtype != MidiEvent::ST_ENDTRACK)//Don't try to read delta after EndOfTrack event! - { - evtPos.delay = ReadVarLenEx(&trackPtr, end, ok); - if(!ok) - { - /* End of track has been reached! However, there is no EOT event presented */ - event.type = MidiEvent::T_SPECIAL; - event.subtype = MidiEvent::ST_ENDTRACK; - } - } - - if((evtPos.delay > 0) || (event.subtype == MidiEvent::ST_ENDTRACK)) - { - evtPos.absPos = abs_position; - abs_position += evtPos.delay; - evtPos.sortEvents(noteStates); - trackDataNew[tk].push_back(evtPos); - evtPos.reset(); - gotLoopEventInThisRow = false; - } - } - while((trackPtr <= end) && (event.subtype != MidiEvent::ST_ENDTRACK)); - - if(ticksSongLength < abs_position) - ticksSongLength = abs_position; - //Set the chain of events begin - if(trackDataNew[tk].size() > 0) - CurrentPositionNew.track[tk].pos = trackDataNew[tk].begin(); - } - - if(gotLoopStart && !gotLoopEnd) - { - gotLoopEnd = true; - loopEndTicks = ticksSongLength; - } - - //loopStart must be located before loopEnd! - if(loopStartTicks >= loopEndTicks) - invalidLoop = true; - - /********************************************************************************/ - //Calculate time basing on collected tempo events - /********************************************************************************/ - for(size_t tk = 0; tk < trackCount; ++tk) - { - fraction currentTempo = Tempo; - double time = 0.0; - uint64_t abs_position = 0; - size_t tempo_change_index = 0; - MidiTrackQueue &track = trackDataNew[tk]; - if(track.empty()) - continue;//Empty track is useless! - - #ifdef DEBUG_TIME_CALCULATION - std::fprintf(stdout, "\n============Track %" PRIuPTR "=============\n", tk); - std::fflush(stdout); - #endif - - MidiTrackRow *posPrev = &(*(track.begin()));//First element - for(MidiTrackQueue::iterator it = track.begin(); it != track.end(); it++) - { - #ifdef DEBUG_TIME_CALCULATION - bool tempoChanged = false; - #endif - MidiTrackRow &pos = *it; - if((posPrev != &pos) && //Skip first event - (!tempos.empty()) && //Only when in-track tempo events are available - (tempo_change_index < tempos.size()) - ) - { - // If tempo event is going between of current and previous event - if(tempos[tempo_change_index].absPosition <= pos.absPos) - { - //Stop points: begin point and tempo change points are before end point - std::vector points; - fraction t; - TempoChangePoint firstPoint = {posPrev->absPos, currentTempo}; - points.push_back(firstPoint); - - //Collect tempo change points between previous and current events - do - { - TempoChangePoint tempoMarker; - MidiEvent &tempoPoint = tempos[tempo_change_index]; - tempoMarker.absPos = tempoPoint.absPosition; - tempoMarker.tempo = InvDeltaTicks * fraction(ReadBEint(tempoPoint.data.data(), tempoPoint.data.size())); - points.push_back(tempoMarker); - tempo_change_index++; - } - while((tempo_change_index < tempos.size()) && - (tempos[tempo_change_index].absPosition <= pos.absPos)); - - // Re-calculate time delay of previous event - time -= posPrev->timeDelay; - posPrev->timeDelay = 0.0; - - for(size_t i = 0, j = 1; j < points.size(); i++, j++) - { - /* If one or more tempo events are appears between of two events, - * calculate delays between each tempo point, begin and end */ - uint64_t midDelay = 0; - //Delay between points - midDelay = points[j].absPos - points[i].absPos; - //Time delay between points - t = midDelay * currentTempo; - posPrev->timeDelay += t.value(); - - //Apply next tempo - currentTempo = points[j].tempo; - #ifdef DEBUG_TIME_CALCULATION - tempoChanged = true; - #endif - } - //Then calculate time between last tempo change point and end point - TempoChangePoint tailTempo = points.back(); - uint64_t postDelay = pos.absPos - tailTempo.absPos; - t = postDelay * currentTempo; - posPrev->timeDelay += t.value(); - - //Store Common time delay - posPrev->time = time; - time += posPrev->timeDelay; - } - } - - fraction t = pos.delay * currentTempo; - pos.timeDelay = t.value(); - pos.time = time; - time += pos.timeDelay; - - //Capture markers after time value calculation - for(size_t i = 0; i < pos.events.size(); i++) - { - MidiEvent &e = pos.events[i]; - if((e.type == MidiEvent::T_SPECIAL) && (e.subtype == MidiEvent::ST_MARKER)) - { - MIDI_MarkerEntry marker; - marker.label = std::string((char *)e.data.data(), e.data.size()); - marker.pos_ticks = pos.absPos; - marker.pos_time = pos.time; - musMarkers.push_back(marker); - } - } - - //Capture loop points time positions - if(!invalidLoop) - { - // Set loop points times - if(loopStartTicks == pos.absPos) - loopStartTime = pos.time; - else if(loopEndTicks == pos.absPos) - loopEndTime = pos.time; - } - - #ifdef DEBUG_TIME_CALCULATION - std::fprintf(stdout, "= %10" PRId64 " = %10f%s\n", pos.absPos, pos.time, tempoChanged ? " <----TEMPO CHANGED" : ""); - std::fflush(stdout); - #endif - - abs_position += pos.delay; - posPrev = &pos; - } - - if(time > fullSongTimeLength) - fullSongTimeLength = time; - } - - fullSongTimeLength += postSongWaitDelay; - //Set begin of the music - trackBeginPositionNew = CurrentPositionNew; - //Initial loop position will begin at begin of track until passing of the loop point - LoopBeginPositionNew = CurrentPositionNew; - - /********************************************************************************/ - //Resolve "hell of all times" of too short drum notes: - //move too short percussion note-offs far far away as possible - /********************************************************************************/ - #if 1 //Use this to record WAVEs for comparison before/after implementing of this - if(opn.m_musicMode == OPN2::MODE_MIDI)//Percussion fix is needed for MIDI only, not for IMF/RSXX or CMF - { - //! Minimal real time in seconds -#define DRUM_NOTE_MIN_TIME 0.03 - //! Minimal ticks count -#define DRUM_NOTE_MIN_TICKS 15 - struct NoteState - { - double delay; - uint64_t delayTicks; - bool isOn; - char ___pad[7]; - } drNotes[255]; - uint16_t banks[16]; - - for(size_t tk = 0; tk < trackCount; ++tk) - { - std::memset(drNotes, 0, sizeof(drNotes)); - std::memset(banks, 0, sizeof(banks)); - MidiTrackQueue &track = trackDataNew[tk]; - if(track.empty()) - continue;//Empty track is useless! - - for(MidiTrackQueue::iterator it = track.begin(); it != track.end(); it++) - { - MidiTrackRow &pos = *it; - - for(ssize_t e = 0; e < (ssize_t)pos.events.size(); e++) - { - MidiEvent *et = &pos.events[(size_t)e]; - - /* Set MSB/LSB bank */ - if(et->type == MidiEvent::T_CTRLCHANGE) - { - uint8_t ctrlno = et->data[0]; - uint8_t value = et->data[1]; - switch(ctrlno) - { - case 0: // Set bank msb (GM bank) - banks[et->channel] = uint16_t(uint16_t(value) << 8) | uint16_t(banks[et->channel] & 0x00FF); - break; - case 32: // Set bank lsb (XG bank) - banks[et->channel] = (banks[et->channel] & 0xFF00) | (uint16_t(value) & 0x00FF); - break; - } - continue; - } - - bool percussion = (et->channel == 9) || - banks[et->channel] == 0x7E00 || //XG SFX1/SFX2 channel (16128 signed decimal) - banks[et->channel] == 0x7F00; //XG Percussion channel (16256 signed decimal) - if(!percussion) - continue; - - if(et->type == MidiEvent::T_NOTEON) - { - uint8_t note = et->data[0] & 0x7F; - NoteState &ns = drNotes[note]; - ns.isOn = true; - ns.delay = 0.0; - ns.delayTicks = 0; - } - else if(et->type == MidiEvent::T_NOTEOFF) - { - uint8_t note = et->data[0] & 0x7F; - NoteState &ns = drNotes[note]; - if(ns.isOn) - { - ns.isOn = false; - if(ns.delayTicks < DRUM_NOTE_MIN_TICKS || ns.delay < DRUM_NOTE_MIN_TIME)//If note is too short - { - //Move it into next event position if that possible - for(MidiTrackQueue::iterator itNext = it; - itNext != track.end(); - itNext++) - { - MidiTrackRow &posN = *itNext; - if(ns.delayTicks > DRUM_NOTE_MIN_TICKS && ns.delay > DRUM_NOTE_MIN_TIME) - { - //Put note-off into begin of next event list - posN.events.insert(posN.events.begin(), pos.events[(size_t)e]); - //Renive this event from a current row - pos.events.erase(pos.events.begin() + (int)e); - e--; - break; - } - ns.delay += posN.timeDelay; - ns.delayTicks += posN.delay; - } - } - ns.delay = 0.0; - ns.delayTicks = 0; - } - } - } - - //Append time delays to sustaining notes - for(size_t no = 0; no < 128; no++) - { - NoteState &ns = drNotes[no]; - if(ns.isOn) - { - ns.delay += pos.timeDelay; - ns.delayTicks += pos.delay; - } - } - } - } -#undef DRUM_NOTE_MIN_TIME -#undef DRUM_NOTE_MIN_TICKS - } - #endif - - return true; -} -#endif //OPNMIDI_DISABLE_MIDI_SEQUENCER - - OPNMIDIplay::OPNMIDIplay(unsigned long sampleRate) : + m_masterVolume(MasterVolumeDefault), + m_sysExDeviceId(0), + m_synthMode(Mode_XG), m_arpeggioCounter(0) -#ifndef OPNMIDI_DISABLE_MIDI_SEQUENCER - , fullSongTimeLength(0.0), - postSongWaitDelay(1.0), - loopStartTime(-1.0), - loopEndTime(-1.0), - tempoMultiplier(1.0), - atEnd(false), - loopStart(false), - loopEnd(false), - invalidLoop(false) +#if defined(ADLMIDI_AUDIO_TICK_HANDLER) + , m_audioTickCounter(0) #endif { - devices.clear(); + m_midiDevices.clear(); - m_setup.emulator = OPNMIDI_EMU_MAME; + m_setup.emulator = opn2_getLowestEmulator(); m_setup.runAtPcmRate = false; m_setup.PCM_RATE = sampleRate; @@ -680,245 +100,117 @@ OPNMIDIplay::OPNMIDIplay(unsigned long sampleRate) : m_setup.maxdelay = 512.0 / (double)m_setup.PCM_RATE; m_setup.OpnBank = 0; - m_setup.NumCards = 2; + m_setup.numChips = 2; m_setup.LogarithmicVolumes = false; m_setup.VolumeModel = OPNMIDI_VolumeModel_AUTO; + m_setup.lfoEnable = -1; + m_setup.lfoFrequency = -1; //m_setup.SkipForward = 0; - m_setup.loopingIsEnabled = false; m_setup.ScaleModulators = 0; m_setup.fullRangeBrightnessCC74 = false; m_setup.delay = 0.0; m_setup.carry = 0.0; m_setup.tick_skip_samples_delay = 0; +#ifndef OPNMIDI_DISABLE_MIDI_SEQUENCER + initSequencerInterface(); +#endif + resetMIDI(); applySetup(); - ChooseDevice("none"); realTime_ResetState(); } void OPNMIDIplay::applySetup() { + m_synth.m_musicMode = OPN2::MODE_MIDI; + m_setup.tick_skip_samples_delay = 0; - opn.ScaleModulators = (m_setup.ScaleModulators != 0); - opn.runAtPcmRate = m_setup.runAtPcmRate; - opn.m_musicMode = OPN2::MODE_MIDI; + m_synth.m_runAtPcmRate = m_setup.runAtPcmRate; + + m_synth.m_scaleModulators = (m_setup.ScaleModulators != 0); + if(m_setup.LogarithmicVolumes != 0) - opn.ChangeVolumeRangesModel(OPNMIDI_VolumeModel_CMF); + m_synth.setVolumeScaleModel(OPNMIDI_VolumeModel_NativeOPN2); else - opn.ChangeVolumeRangesModel(static_cast(m_setup.VolumeModel)); + m_synth.setVolumeScaleModel(static_cast(m_setup.VolumeModel)); + if(m_setup.VolumeModel == OPNMIDI_VolumeModel_AUTO) - opn.m_volumeScale = OPN2::VOLUME_Generic; + m_synth.m_volumeScale = (OPN2::VolumesScale)m_synth.m_insBankSetup.volumeModel; - opn.NumCards = m_setup.NumCards; + m_synth.m_numChips = m_setup.numChips; - opn.Reset(m_setup.emulator, m_setup.PCM_RATE); - ch.clear(); - ch.resize(opn.NumChannels, OpnChannel()); + if(m_setup.lfoEnable < 0) + m_synth.m_lfoEnable = (m_synth.m_insBankSetup.lfoEnable != 0); + else + m_synth.m_lfoEnable = (m_setup.lfoEnable != 0); + + if(m_setup.lfoFrequency < 0) + m_synth.m_lfoFrequency = m_synth.m_insBankSetup.lfoFrequency; + else + m_synth.m_lfoFrequency = m_setup.lfoFrequency; + + m_synth.reset(m_setup.emulator, m_setup.PCM_RATE, this); + m_chipChannels.clear(); + m_chipChannels.resize(m_synth.m_numChannels, OpnChannel()); // Reset the arpeggio counter m_arpeggioCounter = 0; } -uint64_t OPNMIDIplay::ReadVarLen(uint8_t **ptr) +void OPNMIDIplay::partialReset() { - uint64_t result = 0; - for(;;) - { - uint8_t byte = *((*ptr)++); - result = (result << 7) + (byte & 0x7F); - if(!(byte & 0x80)) - break; - } - return result; + realTime_panic(); + m_setup.tick_skip_samples_delay = 0; + m_synth.m_runAtPcmRate = m_setup.runAtPcmRate; + m_synth.reset(m_setup.emulator, m_setup.PCM_RATE, this); + m_chipChannels.clear(); + m_chipChannels.resize(m_synth.m_numChannels); } -uint64_t OPNMIDIplay::ReadVarLenEx(uint8_t **ptr, uint8_t *end, bool &ok) +void OPNMIDIplay::resetMIDI() { - uint64_t result = 0; - ok = false; + m_masterVolume = MasterVolumeDefault; + m_sysExDeviceId = 0; + m_synthMode = Mode_XG; + m_arpeggioCounter = 0; - for(;;) - { - if(*ptr >= end) - return 2; - unsigned char byte = *((*ptr)++); - result = (result << 7) + (byte & 0x7F); - if(!(byte & 0x80)) - break; - } + m_midiChannels.clear(); + m_midiChannels.resize(16, MIDIchannel()); - ok = true; - return result; + caugh_missing_instruments.clear(); + caugh_missing_banks_melodic.clear(); + caugh_missing_banks_percussion.clear(); } -#ifndef OPNMIDI_DISABLE_MIDI_SEQUENCER -double OPNMIDIplay::Tick(double s, double granularity) +void OPNMIDIplay::TickIterators(double s) { - s *= tempoMultiplier; - #ifdef ENABLE_BEGIN_SILENCE_SKIPPING - if(CurrentPositionNew.began) - #endif - CurrentPositionNew.wait -= s; - CurrentPositionNew.absTimePosition += s; - - int antiFreezeCounter = 10000;//Limit 10000 loops to avoid freezing - while((CurrentPositionNew.wait <= granularity * 0.5) && (antiFreezeCounter > 0)) - { - //std::fprintf(stderr, "wait = %g...\n", CurrentPosition.wait); - if(!ProcessEventsNew()) - break; - if(CurrentPositionNew.wait <= 0.0) - antiFreezeCounter--; - } - - if(antiFreezeCounter <= 0) - CurrentPositionNew.wait += 1.0;/* Add extra 1 second when over 10000 events - with zero delay are been detected */ - - for(uint16_t c = 0; c < opn.NumChannels; ++c) - ch[c].AddAge(static_cast(s * 1000.0)); - - UpdateVibrato(s); - UpdateArpeggio(s); - - if(CurrentPositionNew.wait < 0.0)//Avoid negative delay value! - return 0.0; - - return CurrentPositionNew.wait; + for(uint16_t c = 0; c < m_synth.m_numChannels; ++c) + m_chipChannels[c].addAge(static_cast(s * 1e6)); + updateVibrato(s); + updateArpeggio(s); +#if !defined(ADLMIDI_AUDIO_TICK_HANDLER) + updateGlide(s); +#endif } -#endif //OPNMIDI_DISABLE_MIDI_SEQUENCER - -void OPNMIDIplay::TickIteratos(double s) -{ - for(uint16_t c = 0; c < opn.NumChannels; ++c) - ch[c].AddAge(static_cast(s * 1000.0)); - UpdateVibrato(s); - UpdateArpeggio(s); -} - -#ifndef OPNMIDI_DISABLE_MIDI_SEQUENCER -void OPNMIDIplay::seek(double seconds) -{ - if(seconds < 0.0) - return;//Seeking negative position is forbidden! :-P - const double granularity = m_setup.mindelay, - granualityHalf = granularity * 0.5, - s = seconds;//m_setup.delay < m_setup.maxdelay ? m_setup.delay : m_setup.maxdelay; - - /* Attempt to go away out of song end must rewind position to begin */ - if(seconds > fullSongTimeLength) - { - rewind(); - return; - } - - bool loopFlagState = m_setup.loopingIsEnabled; - // Turn loop pooints off because it causes wrong position rememberin on a quick seek - m_setup.loopingIsEnabled = false; - - /* - * Seeking search is similar to regular ticking, except of next things: - * - We don't processsing arpeggio and vibrato - * - To keep correctness of the state after seek, begin every search from begin - * - All sustaining notes must be killed - * - Ignore Note-On events - */ - rewind(); - - /* - * Set "loop Start" to false to prevent overwrite of loopStart position with - * seek destinition position - * - * TODO: Detect & set loopStart position on load time to don't break loop while seeking - */ - loopStart = false; - - while((CurrentPositionNew.absTimePosition < seconds) && - (CurrentPositionNew.absTimePosition < fullSongTimeLength)) - { - CurrentPositionNew.wait -= s; - CurrentPositionNew.absTimePosition += s; - int antiFreezeCounter = 10000;//Limit 10000 loops to avoid freezing - double dstWait = CurrentPositionNew.wait + granualityHalf; - while((CurrentPositionNew.wait <= granualityHalf)/*&& (antiFreezeCounter > 0)*/) - { - //std::fprintf(stderr, "wait = %g...\n", CurrentPosition.wait); - if(!ProcessEventsNew(true)) - break; - //Avoid freeze because of no waiting increasing in more than 10000 cycles - if(CurrentPositionNew.wait <= dstWait) - antiFreezeCounter--; - else - { - dstWait = CurrentPositionNew.wait + granualityHalf; - antiFreezeCounter = 10000; - } - } - if(antiFreezeCounter <= 0) - CurrentPositionNew.wait += 1.0;/* Add extra 1 second when over 10000 events - with zero delay are been detected */ - } - - if(CurrentPositionNew.wait < 0.0) - CurrentPositionNew.wait = 0.0; - - m_setup.loopingIsEnabled = loopFlagState; - m_setup.delay = CurrentPositionNew.wait; - m_setup.carry = 0.0; -} - -double OPNMIDIplay::tell() -{ - return CurrentPositionNew.absTimePosition; -} - -double OPNMIDIplay::timeLength() -{ - return fullSongTimeLength; -} - -double OPNMIDIplay::getLoopStart() -{ - return loopStartTime; -} - -double OPNMIDIplay::getLoopEnd() -{ - return loopEndTime; -} - -void OPNMIDIplay::rewind() -{ - Panic(); - KillSustainingNotes(-1, -1); - CurrentPositionNew = trackBeginPositionNew; - atEnd = false; - loopStart = true; - loopEnd = false; - //invalidLoop = false;//No more needed here as this flag is set on load time -} - -void OPNMIDIplay::setTempo(double tempo) -{ - tempoMultiplier = tempo; -} -#endif //OPNMIDI_DISABLE_MIDI_SEQUENCER void OPNMIDIplay::realTime_ResetState() { - for(size_t ch = 0; ch < Ch.size(); ch++) + for(size_t ch = 0; ch < m_midiChannels.size(); ch++) { - MIDIchannel &chan = Ch[ch]; + MIDIchannel &chan = m_midiChannels[ch]; chan.resetAllControllers(); - chan.volume = (opn.m_musicMode == OPN2::MODE_RSXX) ? 127 : 100; + chan.volume = (m_synth.m_musicMode == OPN2::MODE_RSXX) ? 127 : 100; chan.vibpos = 0.0; chan.lastlrpn = 0; chan.lastmrpn = 0; chan.nrpn = false; - NoteUpdate_All(uint16_t(ch), Upd_All); - NoteUpdate_All(uint16_t(ch), Upd_Off); + if((m_synthMode & Mode_GS) != 0)// Reset custom drum channels on GS + chan.is_xg_percussion = false; + noteUpdateAll(uint16_t(ch), Upd_All); + noteUpdateAll(uint16_t(ch), Upd_Off); } + m_masterVolume = MasterVolumeDefault; } bool OPNMIDIplay::realTime_NoteOn(uint8_t channel, uint8_t note, uint8_t velocity) @@ -926,20 +218,23 @@ bool OPNMIDIplay::realTime_NoteOn(uint8_t channel, uint8_t note, uint8_t velocit if(note >= 127) note = 127; - if((opn.m_musicMode == OPN2::MODE_RSXX) && (velocity != 0)) + if((m_synth.m_musicMode == OPN2::MODE_RSXX) && (velocity != 0)) { // Check if this is just a note after-touch - MIDIchannel::activenoteiterator i = Ch[channel].activenotes_find(note); + MIDIchannel::activenoteiterator i = m_midiChannels[channel].activenotes_find(note); if(i) { + const int veloffset = i->ains->midi_velocity_offset; + velocity = (uint8_t)std::min(127, std::max(1, (int)velocity + veloffset)); i->vol = velocity; - NoteUpdate(channel, i, Upd_Volume); + noteUpdate(channel, i, Upd_Volume); return false; } } - channel = channel % 16; - NoteOff(channel, note); + if(static_cast(channel) > m_midiChannels.size()) + channel = channel % 16; + noteOff(channel, note); // On Note on, Keyoff the note first, just in case keyoff // was omitted; this fixes Dance of sugar-plum fairy // by Microsoft. Now that we've done a Keyoff, @@ -948,54 +243,62 @@ bool OPNMIDIplay::realTime_NoteOn(uint8_t channel, uint8_t note, uint8_t velocit if(velocity == 0) return false; - MIDIchannel &midiChan = Ch[channel]; + MIDIchannel &midiChan = m_midiChannels[channel]; size_t midiins = midiChan.patch; - bool isPercussion = (channel % 16 == 9); - bool isXgPercussion = false; + bool isPercussion = (channel % 16 == 9) || midiChan.is_xg_percussion; - uint16_t bank = 0; + size_t bank = 0; if(midiChan.bank_msb || midiChan.bank_lsb) { - bank = (uint16_t(midiChan.bank_msb) * 256) + uint16_t(midiChan.bank_lsb); - //0x7E00 - XG SFX1/SFX2 channel (16128 signed decimal) - //0x7F00 - XG Percussion channel (16256 signed decimal) - if(bank == 0x7E00 || bank == 0x7F00) - { - //Let XG SFX1/SFX2 bank will have LSB==1 (128...255 range in WOPN file) - //Let XG Percussion bank will use (0...127 range in WOPN file) - bank = (uint16_t)midiins + ((bank == 0x7E00) ? 128 : 0); // MIDI instrument defines the patch - midiins = note; // Percussion instrument - isXgPercussion = true; - isPercussion = false; - } + if((m_synthMode & Mode_GS) != 0) //in GS mode ignore LSB + bank = (midiChan.bank_msb * 256); + else + bank = (midiChan.bank_msb * 256) + midiChan.bank_lsb; } if(isPercussion) { - bank = (uint16_t)midiins; // MIDI instrument defines the patch + // == XG bank numbers == + // 0x7E00 - XG "SFX Kits" SFX1/SFX2 channel (16128 signed decimal) + // 0x7F00 - XG "Drum Kits" Percussion channel (16256 signed decimal) + + // MIDI instrument defines the patch: + if((m_synthMode & Mode_XG) != 0) + { + // Let XG SFX1/SFX2 bank will go in 128...255 range of LSB in WOPN file) + // Let XG Percussion bank will use (0...127 LSB range in WOPN file) + + // Choose: SFX or Drum Kits + bank = midiins + ((bank == 0x7E00) ? 128 : 0); + } + else + { + bank = midiins; + } midiins = note; // Percussion instrument } - if(isPercussion || isXgPercussion) + + if(isPercussion) bank += OPN2::PercussionTag; - const opnInstMeta2 *ains = &OPN2::emptyInstrument; + const opnInstMeta2 *ains = &OPN2::m_emptyInstrument; //Set bank bank const OPN2::Bank *bnk = NULL; if((bank & ~(uint16_t)OPN2::PercussionTag) > 0) { - OPN2::BankMap::iterator b = opn.dynamic_banks.find(bank); - if(b != opn.dynamic_banks.end()) + OPN2::BankMap::iterator b = m_synth.m_insBanks.find(bank); + if(b != m_synth.m_insBanks.end()) bnk = &b->second; if(bnk) ains = &bnk->ins[midiins]; else if(hooks.onDebugMessage) { - std::set &missing = (isPercussion || isXgPercussion) ? + std::set &missing = (isPercussion) ? caugh_missing_banks_percussion : caugh_missing_banks_melodic; - const char *text = (isPercussion || isXgPercussion) ? + const char *text = (isPercussion) ? "percussion" : "melodic"; if(missing.insert(bank).second) hooks.onDebugMessage(hooks.onDebugMessage_userData, "[%i] Playing missing %s MIDI bank %i (patch %i)", channel, text, bank, midiins); @@ -1004,25 +307,19 @@ bool OPNMIDIplay::realTime_NoteOn(uint8_t channel, uint8_t note, uint8_t velocit //Or fall back to first bank if(ains->flags & opnInstMeta::Flag_NoSound) { - OPN2::BankMap::iterator b = opn.dynamic_banks.find(bank & OPN2::PercussionTag); - if(b != opn.dynamic_banks.end()) + OPN2::BankMap::iterator b = m_synth.m_insBanks.find(bank & OPN2::PercussionTag); + if(b != m_synth.m_insBanks.end()) bnk = &b->second; if(bnk) ains = &bnk->ins[midiins]; } - /* - if(MidCh%16 == 9 || (midiins != 32 && midiins != 46 && midiins != 48 && midiins != 50)) - break; // HACK - if(midiins == 46) vol = (vol*7)/10; // HACK - if(midiins == 48 || midiins == 50) vol /= 4; // HACK - */ - //if(midiins == 56) vol = vol*6/10; // HACK + const int veloffset = ains->midi_velocity_offset; + velocity = (uint8_t)std::min(127, std::max(1, (int)velocity + veloffset)); - int16_t tone = note; - - if(!isPercussion && !isXgPercussion && (bank > 0)) // For non-zero banks + int32_t tone = note; + if(!isPercussion && (bank > 0)) // For non-zero banks { if(ains->flags & opnInstMeta::Flag_NoSound) { @@ -1030,7 +327,7 @@ bool OPNMIDIplay::realTime_NoteOn(uint8_t channel, uint8_t note, uint8_t velocit { if(!caugh_missing_instruments.count(static_cast(midiins))) { - hooks.onDebugMessage(hooks.onDebugMessage_userData, "[%i] Caugh a blank instrument %i (offset %i) in the MIDI bank %u", channel, midiChan.patch, midiins, bank); + hooks.onDebugMessage(hooks.onDebugMessage_userData, "[%i] Caught a blank instrument %i (offset %i) in the MIDI bank %u", channel, midiChan.patch, midiins, bank); caugh_missing_instruments.insert(static_cast(midiins)); } } @@ -1057,15 +354,30 @@ bool OPNMIDIplay::realTime_NoteOn(uint8_t channel, uint8_t note, uint8_t velocit //bool pseudo_4op = ains.flags & opnInstMeta::Flag_Pseudo8op; //if((opn.AdlPercussionMode == 1) && PercussionMap[midiins & 0xFF]) i[1] = i[0]; + bool isBlankNote = (ains->flags & opnInstMeta::Flag_NoSound) != 0; + if(hooks.onDebugMessage) { - if(!caugh_missing_instruments.count(static_cast(midiins)) && (ains->flags & opnInstMeta::Flag_NoSound)) + if(!caugh_missing_instruments.count(static_cast(midiins)) && isBlankNote) { hooks.onDebugMessage(hooks.onDebugMessage_userData, "[%i] Playing missing instrument %i", channel, midiins); caugh_missing_instruments.insert(static_cast(midiins)); } } + if(isBlankNote) + { + // Don't even try to play the blank instrument! But, insert the dummy note. + std::pair + dummy = midiChan.activenotes_insert(note); + dummy.first->isBlank = true; + dummy.first->ains = NULL; + dummy.first->chip_channels_count = 0; + // Record the last note on MIDI channel as source of portamento + midiChan.portamentoSource = static_cast(note); + return false; + } + // Allocate AdLib channel (the physical sound channel for the note) int32_t adlchannel[MIDIchannel::NoteInfo::MaxNumPhysChans] = { -1, -1 }; @@ -1082,7 +394,7 @@ bool OPNMIDIplay::realTime_NoteOn(uint8_t channel, uint8_t note, uint8_t velocit int32_t c = -1; int32_t bs = -0x7FFFFFFFl; - for(size_t a = 0; a < (size_t)opn.NumChannels; ++a) + for(size_t a = 0; a < (size_t)m_synth.m_numChannels; ++a) { if(ccount == 1 && static_cast(a) == adlchannel[0]) continue; // ^ Don't use the same channel for primary&secondary @@ -1101,7 +413,7 @@ bool OPNMIDIplay::realTime_NoteOn(uint8_t channel, uint8_t note, uint8_t velocit // if(opn.four_op_category[a] != expected_mode) // continue; //} - int64_t s = CalculateAdlChannelGoodness(a, voices[ccount], channel); + int64_t s = calculateChipChannelGoodness(a, voices[ccount]); if(s > bs) { bs = (int32_t)s; // Best candidate wins @@ -1118,7 +430,7 @@ bool OPNMIDIplay::realTime_NoteOn(uint8_t channel, uint8_t note, uint8_t velocit continue; // Could not play this note. Ignore it. } - PrepareAdlChannelForNewNote(static_cast(c), voices[ccount]); + prepareChipChannelForNewNote(static_cast(c), voices[ccount]); adlchannel[ccount] = c; } @@ -1131,16 +443,39 @@ bool OPNMIDIplay::realTime_NoteOn(uint8_t channel, uint8_t note, uint8_t velocit //if(hooks.onDebugMessage) // hooks.onDebugMessage(hooks.onDebugMessage_userData, "i1=%d:%d, i2=%d:%d", i[0],adlchannel[0], i[1],adlchannel[1]); + if(midiChan.softPedal) // Apply Soft Pedal level reducing + velocity = static_cast(std::floor(static_cast(velocity) * 0.8f)); + // Allocate active note for MIDI channel std::pair ir = midiChan.activenotes_insert(note); ir.first->vol = velocity; ir.first->vibrato = midiChan.noteAftertouch[note]; - ir.first->tone = tone; + ir.first->noteTone = static_cast(tone); + ir.first->currentTone = tone; + ir.first->glideRate = HUGE_VAL; ir.first->midiins = midiins; + ir.first->isPercussion = isPercussion; + ir.first->isBlank = isBlankNote; ir.first->ains = ains; ir.first->chip_channels_count = 0; + int8_t currentPortamentoSource = midiChan.portamentoSource; + double currentPortamentoRate = midiChan.portamentoRate; + bool portamentoEnable = + midiChan.portamentoEnable && currentPortamentoRate != HUGE_VAL && !isPercussion; + // Record the last note on MIDI channel as source of portamento + midiChan.portamentoSource = static_cast(note); + // midiChan.portamentoSource = portamentoEnable ? (int8_t)note : (int8_t)-1; + + // Enable gliding on portamento note + if (portamentoEnable && currentPortamentoSource >= 0) + { + ir.first->currentTone = currentPortamentoSource; + ir.first->glideRate = currentPortamentoRate; + ++midiChan.gliding_note_count; + } + for(unsigned ccount = 0; ccount < MIDIchannel::NoteInfo::MaxNumPhysChans; ++ccount) { int32_t c = adlchannel[ccount]; @@ -1149,21 +484,34 @@ bool OPNMIDIplay::realTime_NoteOn(uint8_t channel, uint8_t note, uint8_t velocit uint16_t chipChan = static_cast(adlchannel[ccount]); ir.first->phys_ensure_find_or_create(chipChan)->assign(voices[ccount]); } - NoteUpdate(channel, ir.first, Upd_All | Upd_Patch); + + noteUpdate(channel, ir.first, Upd_All | Upd_Patch); + + for(unsigned ccount = 0; ccount < MIDIchannel::NoteInfo::MaxNumPhysChans; ++ccount) + { + int32_t c = adlchannel[ccount]; + if(c < 0) + continue; + m_chipChannels[c].recent_ins = voices[ccount]; + m_chipChannels[c].addAge(0); + } + return true; } void OPNMIDIplay::realTime_NoteOff(uint8_t channel, uint8_t note) { - channel = channel % 16; - NoteOff(channel, note); + if(static_cast(channel) > m_midiChannels.size()) + channel = channel % 16; + noteOff(channel, note); } void OPNMIDIplay::realTime_NoteAfterTouch(uint8_t channel, uint8_t note, uint8_t atVal) { - channel = channel % 16; - MIDIchannel &chan = Ch[channel]; - MIDIchannel::activenoteiterator i = Ch[channel].activenotes_find(note); + if(static_cast(channel) > m_midiChannels.size()) + channel = channel % 16; + MIDIchannel &chan = m_midiChannels[channel]; + MIDIchannel::activenoteiterator i = m_midiChannels[channel].activenotes_find(note); if(i) { i->vibrato = atVal; @@ -1182,86 +530,99 @@ void OPNMIDIplay::realTime_NoteAfterTouch(uint8_t channel, uint8_t note, uint8_t void OPNMIDIplay::realTime_ChannelAfterTouch(uint8_t channel, uint8_t atVal) { - channel = channel % 16; - Ch[channel].aftertouch = atVal; + if(static_cast(channel) > m_midiChannels.size()) + channel = channel % 16; + m_midiChannels[channel].aftertouch = atVal; } void OPNMIDIplay::realTime_Controller(uint8_t channel, uint8_t type, uint8_t value) { - channel = channel % 16; + if(static_cast(channel) > m_midiChannels.size()) + channel = channel % 16; switch(type) { case 1: // Adjust vibrato //UI.PrintLn("%u:vibrato %d", MidCh,value); - Ch[channel].vibrato = value; + m_midiChannels[channel].vibrato = value; break; case 0: // Set bank msb (GM bank) - Ch[channel].bank_msb = value; - Ch[channel].is_xg_percussion = isXgPercChannel(Ch[channel].bank_msb, Ch[channel].bank_lsb); + m_midiChannels[channel].bank_msb = value; + if((m_synthMode & Mode_GS) == 0)// Don't use XG drums on GS synth mode + m_midiChannels[channel].is_xg_percussion = isXgPercChannel(m_midiChannels[channel].bank_msb, m_midiChannels[channel].bank_lsb); break; case 32: // Set bank lsb (XG bank) - Ch[channel].bank_lsb = value; - Ch[channel].is_xg_percussion = isXgPercChannel(Ch[channel].bank_msb, Ch[channel].bank_lsb); + m_midiChannels[channel].bank_lsb = value; + if((m_synthMode & Mode_GS) == 0)// Don't use XG drums on GS synth mode + m_midiChannels[channel].is_xg_percussion = isXgPercChannel(m_midiChannels[channel].bank_msb, m_midiChannels[channel].bank_lsb); break; case 5: // Set portamento msb - Ch[channel].portamento = static_cast((Ch[channel].portamento & 0x7F) | (value << 7)); - //UpdatePortamento(MidCh); + m_midiChannels[channel].portamento = static_cast((m_midiChannels[channel].portamento & 0x007F) | (value << 7)); + updatePortamento(channel); break; case 37: // Set portamento lsb - Ch[channel].portamento = (Ch[channel].portamento & 0x3F80) | (value); - //UpdatePortamento(MidCh); + m_midiChannels[channel].portamento = static_cast((m_midiChannels[channel].portamento & 0x3F80) | (value)); + updatePortamento(channel); break; case 65: // Enable/disable portamento - // value >= 64 ? enabled : disabled - //UpdatePortamento(MidCh); + m_midiChannels[channel].portamentoEnable = value >= 64; + updatePortamento(channel); break; case 7: // Change volume - Ch[channel].volume = value; - NoteUpdate_All(channel, Upd_Volume); + m_midiChannels[channel].volume = value; + noteUpdateAll(channel, Upd_Volume); break; case 74: // Change brightness - Ch[channel].brightness = value; - NoteUpdate_All(channel, Upd_Volume); + m_midiChannels[channel].brightness = value; + noteUpdateAll(channel, Upd_Volume); break; case 64: // Enable/disable sustain - Ch[channel].sustain = value; - if(!value) KillSustainingNotes(channel); + m_midiChannels[channel].sustain = (value >= 64); + if(!m_midiChannels[channel].sustain) + killSustainingNotes(channel, -1, OpnChannel::LocationData::Sustain_Pedal); + break; + + case 66: // Enable/disable sostenuto + if(value >= 64) //Find notes and mark them as sostenutoed + markSostenutoNotes(channel); + else + killSustainingNotes(channel, -1, OpnChannel::LocationData::Sustain_Sostenuto); + break; + + case 67: // Enable/disable soft-pedal + m_midiChannels[channel].softPedal = (value >= 64); break; case 11: // Change expression (another volume factor) - Ch[channel].expression = value; - NoteUpdate_All(channel, Upd_Volume); + m_midiChannels[channel].expression = value; + noteUpdateAll(channel, Upd_Volume); break; case 10: // Change panning - Ch[channel].panning = 0x00; - if(value < 64 + 32) Ch[channel].panning |= OPN_PANNING_LEFT; - if(value >= 64 - 32) Ch[channel].panning |= OPN_PANNING_RIGHT; - NoteUpdate_All(channel, Upd_Pan); + m_midiChannels[channel].panning = value; + noteUpdateAll(channel, Upd_Pan); break; case 121: // Reset all controllers - Ch[channel].resetAllControllers(); - //UpdatePortamento(MidCh); - NoteUpdate_All(channel, Upd_Pan + Upd_Volume + Upd_Pitch); + m_midiChannels[channel].resetAllControllers(); + noteUpdateAll(channel, Upd_Pan + Upd_Volume + Upd_Pitch); // Kill all sustained notes - KillSustainingNotes(channel); + killSustainingNotes(channel, -1, OpnChannel::LocationData::Sustain_ANY); break; case 120: // All sounds off - NoteUpdate_All(channel, Upd_OffMute); + noteUpdateAll(channel, Upd_OffMute); break; case 123: // All notes off - NoteUpdate_All(channel, Upd_Off); + noteUpdateAll(channel, Upd_Off); break; case 91: @@ -1280,34 +641,34 @@ void OPNMIDIplay::realTime_Controller(uint8_t channel, uint8_t type, uint8_t val break; // Phaser effect depth. We don't do. case 98: - Ch[channel].lastlrpn = value; - Ch[channel].nrpn = true; + m_midiChannels[channel].lastlrpn = value; + m_midiChannels[channel].nrpn = true; break; case 99: - Ch[channel].lastmrpn = value; - Ch[channel].nrpn = true; + m_midiChannels[channel].lastmrpn = value; + m_midiChannels[channel].nrpn = true; break; case 100: - Ch[channel].lastlrpn = value; - Ch[channel].nrpn = false; + m_midiChannels[channel].lastlrpn = value; + m_midiChannels[channel].nrpn = false; break; case 101: - Ch[channel].lastmrpn = value; - Ch[channel].nrpn = false; + m_midiChannels[channel].lastmrpn = value; + m_midiChannels[channel].nrpn = false; break; case 113: break; // Related to pitch-bender, used by missimp.mid in Duke3D case 6: - SetRPN(channel, value, true); + setRPN(channel, value, true); break; case 38: - SetRPN(channel, value, false); + setRPN(channel, value, false); break; //case 103: @@ -1322,64 +683,329 @@ void OPNMIDIplay::realTime_Controller(uint8_t channel, uint8_t type, uint8_t val void OPNMIDIplay::realTime_PatchChange(uint8_t channel, uint8_t patch) { - channel = channel % 16; - Ch[channel].patch = patch; + if(static_cast(channel) > m_midiChannels.size()) + channel = channel % 16; + m_midiChannels[channel].patch = patch; } void OPNMIDIplay::realTime_PitchBend(uint8_t channel, uint16_t pitch) { - channel = channel % 16; - Ch[channel].bend = int(pitch) - 8192; - NoteUpdate_All(channel, Upd_Pitch); + if(static_cast(channel) > m_midiChannels.size()) + channel = channel % 16; + m_midiChannels[channel].bend = int(pitch) - 8192; + noteUpdateAll(channel, Upd_Pitch); } void OPNMIDIplay::realTime_PitchBend(uint8_t channel, uint8_t msb, uint8_t lsb) { - channel = channel % 16; - Ch[channel].bend = int(lsb) + int(msb) * 128 - 8192; - NoteUpdate_All(channel, Upd_Pitch); + if(static_cast(channel) > m_midiChannels.size()) + channel = channel % 16; + m_midiChannels[channel].bend = int(lsb) + int(msb) * 128 - 8192; + noteUpdateAll(channel, Upd_Pitch); } void OPNMIDIplay::realTime_BankChangeLSB(uint8_t channel, uint8_t lsb) { - channel = channel % 16; - Ch[channel].bank_lsb = lsb; + if(static_cast(channel) > m_midiChannels.size()) + channel = channel % 16; + m_midiChannels[channel].bank_lsb = lsb; } void OPNMIDIplay::realTime_BankChangeMSB(uint8_t channel, uint8_t msb) { - channel = channel % 16; - Ch[channel].bank_msb = msb; + if(static_cast(channel) > m_midiChannels.size()) + channel = channel % 16; + m_midiChannels[channel].bank_msb = msb; } void OPNMIDIplay::realTime_BankChange(uint8_t channel, uint16_t bank) { - channel = channel % 16; - Ch[channel].bank_lsb = uint8_t(bank & 0xFF); - Ch[channel].bank_msb = uint8_t((bank >> 8) & 0xFF); + if(static_cast(channel) > m_midiChannels.size()) + channel = channel % 16; + m_midiChannels[channel].bank_lsb = uint8_t(bank & 0xFF); + m_midiChannels[channel].bank_msb = uint8_t((bank >> 8) & 0xFF); +} + +void OPNMIDIplay::setDeviceId(uint8_t id) +{ + m_sysExDeviceId = id; +} + +bool OPNMIDIplay::realTime_SysEx(const uint8_t *msg, size_t size) +{ + if(size < 4 || msg[0] != 0xF0 || msg[size - 1] != 0xF7) + return false; + + unsigned manufacturer = msg[1]; + unsigned dev = msg[2]; + msg += 3; + size -= 4; + + switch(manufacturer) + { + default: + break; + case Manufacturer_UniversalNonRealtime: + case Manufacturer_UniversalRealtime: + return doUniversalSysEx( + dev, manufacturer == Manufacturer_UniversalRealtime, msg, size); + case Manufacturer_Roland: + return doRolandSysEx(dev, msg, size); + case Manufacturer_Yamaha: + return doYamahaSysEx(dev, msg, size); + } + + return false; +} + +bool OPNMIDIplay::doUniversalSysEx(unsigned dev, bool realtime, const uint8_t *data, size_t size) +{ + bool devicematch = dev == 0x7F || dev == m_sysExDeviceId; + if(size < 2 || !devicematch) + return false; + + unsigned address = + (((unsigned)data[0] & 0x7F) << 8) | + (((unsigned)data[1] & 0x7F)); + data += 2; + size -= 2; + + switch(((unsigned)realtime << 16) | address) + { + case (0 << 16) | 0x0901: // GM System On + if(hooks.onDebugMessage) + hooks.onDebugMessage(hooks.onDebugMessage_userData, "SysEx: GM System On"); + m_synthMode = Mode_GM; + realTime_ResetState(); + return true; + case (0 << 16) | 0x0902: // GM System Off + if(hooks.onDebugMessage) + hooks.onDebugMessage(hooks.onDebugMessage_userData, "SysEx: GM System Off"); + m_synthMode = Mode_XG;//TODO: TEMPORARY, make something RIGHT + realTime_ResetState(); + return true; + case (1 << 16) | 0x0401: // MIDI Master Volume + if(size != 2) + break; + unsigned volume = + (((unsigned)data[0] & 0x7F)) | + (((unsigned)data[1] & 0x7F) << 7); + m_masterVolume = static_cast(volume >> 7); + for(size_t ch = 0; ch < m_midiChannels.size(); ch++) + noteUpdateAll(uint16_t(ch), Upd_Volume); + return true; + } + + return false; +} + +bool OPNMIDIplay::doRolandSysEx(unsigned dev, const uint8_t *data, size_t size) +{ + bool devicematch = dev == 0x7F || (dev & 0x0F) == m_sysExDeviceId; + if(size < 6 || !devicematch) + return false; + + unsigned model = data[0] & 0x7F; + unsigned mode = data[1] & 0x7F; + unsigned checksum = data[size - 1] & 0x7F; + data += 2; + size -= 3; + +#if !defined(OPNMIDI_SKIP_ROLAND_CHECKSUM) + { + unsigned checkvalue = 0; + for(size_t i = 0; i < size; ++i) + checkvalue += data[i] & 0x7F; + checkvalue = (128 - (checkvalue & 127)) & 127; + if(checkvalue != checksum) + { + if(hooks.onDebugMessage) + hooks.onDebugMessage(hooks.onDebugMessage_userData, "SysEx: Caught invalid roland SysEx message!"); + return false; + } + } +#endif + + unsigned address = + (((unsigned)data[0] & 0x7F) << 16) | + (((unsigned)data[1] & 0x7F) << 8) | + (((unsigned)data[2] & 0x7F)); + unsigned target_channel = 0; + + /* F0 41 10 42 12 40 00 7F 00 41 F7 */ + + if((address & 0xFFF0FF) == 0x401015) // Turn channel 1 into percussion + { + address = 0x401015; + target_channel = data[1] & 0x0F; + } + + data += 3; + size -= 3; + + if(mode != RolandMode_Send) // don't have MIDI-Out reply ability + return false; + + // Mode Set + // F0 {41 10 42 12} {40 00 7F} {00 41} F7 + + // Custom drum channels + // F0 {41 10 42 12} {40 1 15} { } F7 + + switch((model << 24) | address) + { + case (RolandModel_GS << 24) | 0x00007F: // System Mode Set + { + if(size != 1 || (dev & 0xF0) != 0x10) + break; + unsigned mode = data[0] & 0x7F; + ADL_UNUSED(mode);//TODO: Hook this correctly! + if(hooks.onDebugMessage) + hooks.onDebugMessage(hooks.onDebugMessage_userData, "SysEx: Caught Roland System Mode Set: %02X", mode); + m_synthMode = Mode_GS; + realTime_ResetState(); + return true; + } + case (RolandModel_GS << 24) | 0x40007F: // Mode Set + { + if(size != 1 || (dev & 0xF0) != 0x10) + break; + unsigned value = data[0] & 0x7F; + ADL_UNUSED(value);//TODO: Hook this correctly! + if(hooks.onDebugMessage) + hooks.onDebugMessage(hooks.onDebugMessage_userData, "SysEx: Caught Roland Mode Set: %02X", value); + m_synthMode = Mode_GS; + realTime_ResetState(); + return true; + } + case (RolandModel_GS << 24) | 0x401015: // Percussion channel + { + if(size != 1 || (dev & 0xF0) != 0x10) + break; + if(m_midiChannels.size() < 16) + break; + unsigned value = data[0] & 0x7F; + const uint8_t channels_map[16] = + { + 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 10, 11, 12, 13, 14, 15 + }; + if(hooks.onDebugMessage) + hooks.onDebugMessage(hooks.onDebugMessage_userData, + "SysEx: Caught Roland Percussion set: %02X on channel %u (from %X)", + value, channels_map[target_channel], target_channel); + m_midiChannels[channels_map[target_channel]].is_xg_percussion = ((value == 0x01)) || ((value == 0x02)); + return true; + } + } + + return false; +} + +bool OPNMIDIplay::doYamahaSysEx(unsigned dev, const uint8_t *data, size_t size) +{ + bool devicematch = dev == 0x7F || (dev & 0x0F) == m_sysExDeviceId; + if(size < 1 || !devicematch) + return false; + + unsigned model = data[0] & 0x7F; + ++data; + --size; + + switch((model << 8) | (dev & 0xF0)) + { + case (YamahaModel_XG << 8) | 0x10: // parameter change + { + if(size < 3) + break; + + unsigned address = + (((unsigned)data[0] & 0x7F) << 16) | + (((unsigned)data[1] & 0x7F) << 8) | + (((unsigned)data[2] & 0x7F)); + data += 3; + size -= 3; + + switch(address) + { + case 0x00007E: // XG System On + if(size != 1) + break; + unsigned value = data[0] & 0x7F; + ADL_UNUSED(value);//TODO: Hook this correctly! + if(hooks.onDebugMessage) + hooks.onDebugMessage(hooks.onDebugMessage_userData, "SysEx: Caught Yamaha XG System On: %02X", value); + m_synthMode = Mode_XG; + realTime_ResetState(); + return true; + } + + break; + } + } + + return false; } void OPNMIDIplay::realTime_panic() { - Panic(); - KillSustainingNotes(-1, -1); + panic(); + killSustainingNotes(-1, -1, OpnChannel::LocationData::Sustain_ANY); } +void OPNMIDIplay::realTime_deviceSwitch(size_t track, const char *data, size_t length) +{ + const std::string indata(data, length); + m_currentMidiDevice[track] = chooseDevice(indata); +} -void OPNMIDIplay::NoteUpdate(uint16_t MidCh, +size_t OPNMIDIplay::realTime_currentDevice(size_t track) +{ + if(m_currentMidiDevice.empty()) + return 0; + return m_currentMidiDevice[track]; +} + +#if defined(ADLMIDI_AUDIO_TICK_HANDLER) +void OPNMIDIplay::AudioTick(uint32_t chipId, uint32_t rate) +{ + if(chipId != 0) // do first chip ticks only + return; + + uint32_t tickNumber = m_audioTickCounter++; + double timeDelta = 1.0 / rate; + + enum { portamentoInterval = 32 }; // for efficiency, set rate limit on pitch updates + + if(tickNumber % portamentoInterval == 0) + { + double portamentoDelta = timeDelta * portamentoInterval; + UpdateGlide(portamentoDelta); + } +} +#endif + +void OPNMIDIplay::noteUpdate(size_t midCh, OPNMIDIplay::MIDIchannel::activenoteiterator i, unsigned props_mask, int32_t select_adlchn) { MIDIchannel::NoteInfo &info = *i; - const int16_t tone = info.tone; + const int16_t noteTone = info.noteTone; + const double currentTone = info.currentTone; const uint8_t vol = info.vol; const size_t midiins = info.midiins; const opnInstMeta2 &ains = *info.ains; OpnChannel::Location my_loc; - my_loc.MidCh = MidCh; + my_loc.MidCh = static_cast(midCh); my_loc.note = info.note; + if(info.isBlank) + { + if(props_mask & Upd_Off) + m_midiChannels[midCh].activenotes_erase(i); + return; + } + for(unsigned ccount = 0, ctotal = info.chip_channels_count; ccount < ctotal; ccount++) { const MIDIchannel::NoteInfo::Phys &ins = info.chip_channels[ccount]; @@ -1389,13 +1015,13 @@ void OPNMIDIplay::NoteUpdate(uint16_t MidCh, if(props_mask & Upd_Patch) { - opn.Patch(c, ins.ains); - OpnChannel::LocationData *d = ch[c].users_find_or_create(my_loc); + m_synth.setPatch(c, ins.ains); + OpnChannel::LocationData *d = m_chipChannels[c].users_find_or_create(my_loc); if(d) { // inserts if necessary - d->sustained = false; - d->vibdelay = 0; + d->sustained = OpnChannel::LocationData::Sustain_None; + d->vibdelay_us = 0; d->fixed_sustain = (ains.ms_sound_kon == static_cast(opnNoteOnMaxTime)); - d->kon_time_until_neglible = ains.ms_sound_kon; + d->kon_time_until_neglible_us = 1000 * ains.ms_sound_kon; d->ins = ins; } } @@ -1411,25 +1037,27 @@ void OPNMIDIplay::NoteUpdate(uint16_t MidCh, if(props_mask & Upd_Off) // note off { - if(Ch[MidCh].sustain == 0) + if(m_midiChannels[midCh].sustain == 0) { - OpnChannel::LocationData *k = ch[c].users_find(my_loc); - - if(k) - ch[c].users_erase(k); + OpnChannel::LocationData *k = m_chipChannels[c].users_find(my_loc); + bool do_erase_user = (k && ((k->sustained & OpnChannel::LocationData::Sustain_Sostenuto) == 0)); + if(do_erase_user) + m_chipChannels[c].users_erase(k); if(hooks.onNote) - hooks.onNote(hooks.onNote_userData, c, tone, (int)midiins, 0, 0.0); + hooks.onNote(hooks.onNote_userData, c, noteTone, (int)midiins, 0, 0.0); - if(ch[c].users_empty()) + if(do_erase_user && m_chipChannels[c].users_empty()) { - opn.NoteOff(c); + m_synth.noteOff(c); if(props_mask & Upd_Mute) // Mute the note { - opn.Touch_Real(c, 0); - ch[c].koff_time_until_neglible = 0; - } else { - ch[c].koff_time_until_neglible = ains.ms_sound_koff; + m_synth.touchNote(c, 0); + m_chipChannels[c].koff_time_until_neglible_us = 0; + } + else + { + m_chipChannels[c].koff_time_until_neglible_us = 1000 * int64_t(ains.ms_sound_koff); } } } @@ -1437,11 +1065,11 @@ void OPNMIDIplay::NoteUpdate(uint16_t MidCh, { // Sustain: Forget about the note, but don't key it off. // Also will avoid overwriting it very soon. - OpnChannel::LocationData *d = ch[c].users_find_or_create(my_loc); + OpnChannel::LocationData *d = m_chipChannels[c].users_find_or_create(my_loc); if(d) - d->sustained = true; // note: not erased! + d->sustained |= OpnChannel::LocationData::Sustain_Pedal; // note: not erased! if(hooks.onNote) - hooks.onNote(hooks.onNote_userData, c, tone, (int)midiins, -1, 0.0); + hooks.onNote(hooks.onNote_userData, c, noteTone, (int)midiins, -1, 0.0); } info.phys_erase_at(&ins); // decrements channel count @@ -1450,13 +1078,13 @@ void OPNMIDIplay::NoteUpdate(uint16_t MidCh, } if(props_mask & Upd_Pan) - opn.Pan(c, Ch[MidCh].panning); + m_synth.setPan(c, m_midiChannels[midCh].panning); if(props_mask & Upd_Volume) { - uint32_t volume; - bool is_percussion = (MidCh == 9) || Ch[MidCh].is_xg_percussion; - uint8_t brightness = is_percussion ? 127 : Ch[MidCh].brightness; + uint_fast32_t volume; + bool is_percussion = (midCh == 9) || m_midiChannels[midCh].is_xg_percussion; + uint_fast32_t brightness = is_percussion ? 127 : m_midiChannels[midCh].brightness; if(!m_setup.fullRangeBrightnessCC74) { @@ -1467,11 +1095,12 @@ void OPNMIDIplay::NoteUpdate(uint16_t MidCh, brightness *= 2; } - switch(opn.m_volumeScale) + switch(m_synth.m_volumeScale) { + default: case OPN2::VOLUME_Generic: { - volume = vol * Ch[MidCh].volume * Ch[MidCh].expression; + volume = vol * m_masterVolume * m_midiChannels[midCh].volume * m_midiChannels[midCh].expression; /* If the channel has arpeggio, the effective volume of * *this* instrument is actually lower due to timesharing. * To compensate, add extra volume that corresponds to the @@ -1482,77 +1111,78 @@ void OPNMIDIplay::NoteUpdate(uint16_t MidCh, //volume = (int)(volume * std::sqrt( (double) ch[c].users.size() )); // The formula below: SOLVE(V=127^3 * 2^( (A-63.49999) / 8), A) - volume = volume > 8725 ? static_cast((std::log(static_cast(volume)) * (11.541561) + (0.5 - 104.22845)) * 2.0) : 0; + volume = volume > (8725 * 127) ? static_cast((std::log(static_cast(volume)) * 11.541560327111707 - 1.601379199767093e+02) * 2.0) : 0; // The incorrect formula below: SOLVE(V=127^3 * (2^(A/63)-1), A) //opl.Touch_Real(c, volume>11210 ? 91.61112 * std::log(4.8819E-7*volume + 1.0)+0.5 : 0); - - opn.Touch_Real(c, volume, brightness); } break; - case OPN2::VOLUME_CMF: + case OPN2::VOLUME_NATIVE: { - volume = vol * Ch[MidCh].volume * Ch[MidCh].expression; - volume = volume * 127 / (2048383/*127 * 127 * 127*/); - opn.Touch_Real(c, volume, brightness); + volume = vol * m_midiChannels[midCh].volume * m_midiChannels[midCh].expression; + //volume = volume * m_masterVolume / (127 * 127 * 127) / 2; + volume = (volume * m_masterVolume) / 4096766; } break; case OPN2::VOLUME_DMX: { - volume = 2 * ((Ch[MidCh].volume * Ch[MidCh].expression) * 127 / 16129) + 1; + volume = 2 * (m_midiChannels[midCh].volume * m_midiChannels[midCh].expression * m_masterVolume / 16129) + 1; //volume = 2 * (Ch[MidCh].volume) + 1; - volume = (DMX_volume_mapping_table[vol] * volume) >> 9; - opn.Touch_Real(c, volume, brightness); + volume = (DMX_volume_mapping_table[(vol < 128) ? vol : 127] * volume) >> 9; + if(volume > 0) + volume += 64;//OPN has 0~127 range. As 0...63 is almost full silence, but at 64 to 127 is very closed to OPL3, just add 64. } break; case OPN2::VOLUME_APOGEE: { - volume = ((Ch[MidCh].volume * Ch[MidCh].expression) * 127 / 16129); + volume = (m_midiChannels[midCh].volume * m_midiChannels[midCh].expression * m_masterVolume / 16129); volume = ((64 * (vol + 0x80)) * volume) >> 15; //volume = ((63 * (vol + 0x80)) * Ch[MidCh].volume) >> 15; - volume *= 2;//OPN has 0~127 range - opn.Touch_Real(c, volume, brightness); + if(volume > 0) + volume += 64;//OPN has 0~127 range. As 0...63 is almost full silence, but at 64 to 127 is very closed to OPL3, just add 64. } break; case OPN2::VOLUME_9X: { //volume = 63 - W9X_volume_mapping_table[(((vol * Ch[MidCh].volume /** Ch[MidCh].expression*/) * 127 / 16129 /*2048383*/) >> 2)]; - volume = 63 - W9X_volume_mapping_table[(((vol * Ch[MidCh].volume * Ch[MidCh].expression) * 127 / 2048383) >> 2)]; + volume = 63 - W9X_volume_mapping_table[((vol * m_midiChannels[midCh].volume * m_midiChannels[midCh].expression * m_masterVolume / 2048383) >> 2)]; //volume = W9X_volume_mapping_table[vol >> 2] + volume; - volume *= 2;//OPN has 0~127 range - opn.Touch_Real(c, volume, brightness); + if(volume > 0) + volume += 64;//OPN has 0~127 range. As 0...63 is almost full silence, but at 64 to 127 is very closed to OPL3, just add 64. } break; } + m_synth.touchNote(c, static_cast(volume), static_cast(brightness)); + /* DEBUG ONLY!!! - static uint32_t max = 0; + static uint32_t max = 0; - if(volume == 0) - max = 0; + if(volume == 0) + max = 0; - if(volume > max) - max = volume; + if(volume > max) + max = volume; - printf("%d\n", max); - fflush(stdout); - */ + printf("%d\n", max); + fflush(stdout); + */ } if(props_mask & Upd_Pitch) { - OpnChannel::LocationData *d = ch[c].users_find(my_loc); + OpnChannel::LocationData *d = m_chipChannels[c].users_find(my_loc); // Don't bend a sustained note - if(!d || !d->sustained) + if(!d || (d->sustained == OpnChannel::LocationData::Sustain_None)) { - double midibend = Ch[MidCh].bend * Ch[MidCh].bendsense; + double midibend = m_midiChannels[midCh].bend * m_midiChannels[midCh].bendsense; double bend = midibend + ins.ains.finetune; double phase = 0.0; - uint8_t vibrato = std::max(Ch[MidCh].vibrato, Ch[MidCh].aftertouch); + uint8_t vibrato = std::max(m_midiChannels[midCh].vibrato, m_midiChannels[midCh].aftertouch); vibrato = std::max(vibrato, i->vibrato); if((ains.flags & opnInstMeta::Flag_Pseudo8op) && ins.ains == ains.opn[1]) @@ -1560,349 +1190,35 @@ void OPNMIDIplay::NoteUpdate(uint16_t MidCh, phase = ains.fine_tune;//0.125; // Detune the note slightly (this is what Doom does) } - if(vibrato && (!d || d->vibdelay >= Ch[MidCh].vibdelay)) - bend += static_cast(vibrato) * Ch[MidCh].vibdepth * std::sin(Ch[MidCh].vibpos); + if(vibrato && (!d || d->vibdelay_us >= m_midiChannels[midCh].vibdelay_us)) + bend += static_cast(vibrato) * m_midiChannels[midCh].vibdepth * std::sin(m_midiChannels[midCh].vibpos); #define BEND_COEFFICIENT 321.88557 - opn.NoteOn(c, BEND_COEFFICIENT * std::exp(0.057762265 * (static_cast(tone) + bend + phase))); + m_synth.noteOn(c, BEND_COEFFICIENT * std::exp(0.057762265 * (currentTone + bend + phase))); #undef BEND_COEFFICIENT if(hooks.onNote) - hooks.onNote(hooks.onNote_userData, c, tone, (int)midiins, vol, midibend); + hooks.onNote(hooks.onNote_userData, c, noteTone, (int)midiins, vol, midibend); } } } if(info.chip_channels_count == 0) - Ch[MidCh].activenotes_erase(i); + { + if(i->glideRate != HUGE_VAL) + --m_midiChannels[midCh].gliding_note_count; + m_midiChannels[midCh].activenotes_erase(i); + } } -#ifndef OPNMIDI_DISABLE_MIDI_SEQUENCER -bool OPNMIDIplay::ProcessEventsNew(bool isSeek) +void OPNMIDIplay::noteUpdateAll(size_t midCh, unsigned props_mask) { - if(CurrentPositionNew.track.size() == 0) - atEnd = true;//No MIDI track data to play - if(atEnd) - return false;//No more events in the queue - - loopEnd = false; - const size_t TrackCount = CurrentPositionNew.track.size(); - const PositionNew RowBeginPosition(CurrentPositionNew); - - #ifdef DEBUG_TIME_CALCULATION - double maxTime = 0.0; - #endif - - for(size_t tk = 0; tk < TrackCount; ++tk) + for(MIDIchannel::activenoteiterator + i = m_midiChannels[midCh].activenotes_begin(); i;) { - PositionNew::TrackInfo &track = CurrentPositionNew.track[tk]; - if((track.status >= 0) && (track.delay <= 0)) - { - //Check is an end of track has been reached - if(track.pos == trackDataNew[tk].end()) - { - track.status = -1; - break; - } - - // Handle event - for(size_t i = 0; i < track.pos->events.size(); i++) - { - const MidiEvent &evt = track.pos->events[i]; - #ifdef ENABLE_BEGIN_SILENCE_SKIPPING - if(!CurrentPositionNew.began && (evt.type == MidiEvent::T_NOTEON)) - CurrentPositionNew.began = true; - #endif - if(isSeek && (evt.type == MidiEvent::T_NOTEON)) - continue; - HandleEvent(tk, evt, track.status); - if(loopEnd) - break;//Stop event handling on catching loopEnd event! - } - - #ifdef DEBUG_TIME_CALCULATION - if(maxTime < track.pos->time) - maxTime = track.pos->time; - #endif - // Read next event time (unless the track just ended) - if(track.status >= 0) - { - track.delay += track.pos->delay; - track.pos++; - } - } + MIDIchannel::activenoteiterator j(i++); + noteUpdate(midCh, j, props_mask); } - - #ifdef DEBUG_TIME_CALCULATION - std::fprintf(stdout, " \r"); - std::fprintf(stdout, "Time: %10f; Audio: %10f\r", maxTime, CurrentPositionNew.absTimePosition); - std::fflush(stdout); - #endif - - // Find shortest delay from all track - uint64_t shortest = 0; - bool shortest_no = true; - - for(size_t tk = 0; tk < TrackCount; ++tk) - { - PositionNew::TrackInfo &track = CurrentPositionNew.track[tk]; - if((track.status >= 0) && (shortest_no || track.delay < shortest)) - { - shortest = track.delay; - shortest_no = false; - } - } - - //if(shortest > 0) UI.PrintLn("shortest: %ld", shortest); - - // Schedule the next playevent to be processed after that delay - for(size_t tk = 0; tk < TrackCount; ++tk) - CurrentPositionNew.track[tk].delay -= shortest; - - fraction t = shortest * Tempo; - - #ifdef ENABLE_BEGIN_SILENCE_SKIPPING - if(CurrentPositionNew.began) - #endif - CurrentPositionNew.wait += t.value(); - - //if(shortest > 0) UI.PrintLn("Delay %ld (%g)", shortest, (double)t.valuel()); - if(loopStart) - { - LoopBeginPositionNew = RowBeginPosition; - loopStart = false; - } - - if(shortest_no || loopEnd) - { - //Loop if song end or loop end point has reached - loopEnd = false; - shortest = 0; - if(!m_setup.loopingIsEnabled) - { - atEnd = true; //Don't handle events anymore - CurrentPositionNew.wait += postSongWaitDelay;//One second delay until stop playing - return true;//We have caugh end here! - } - CurrentPositionNew = LoopBeginPositionNew; - } - - return true;//Has events in queue } -#endif //OPNMIDI_DISABLE_MIDI_SEQUENCER - -#ifndef OPNMIDI_DISABLE_MIDI_SEQUENCER -OPNMIDIplay::MidiEvent OPNMIDIplay::parseEvent(uint8_t **pptr, uint8_t *end, int &status) -{ - uint8_t *&ptr = *pptr; - OPNMIDIplay::MidiEvent evt; - - if(ptr + 1 > end) - { - //When track doesn't ends on the middle of event data, it's must be fine - evt.type = MidiEvent::T_SPECIAL; - evt.subtype = MidiEvent::ST_ENDTRACK; - return evt; - } - - unsigned char byte = *(ptr++); - bool ok = false; - - if(byte == MidiEvent::T_SYSEX || byte == MidiEvent::T_SYSEX2)// Ignore SysEx - { - uint64_t length = ReadVarLenEx(pptr, end, ok); - if(!ok || (ptr + length > end)) - { - errorString += "parseEvent: Can't read SysEx event - Unexpected end of track data.\n"; - evt.isValid = 0; - return evt; - } - ptr += (size_t)length; - return evt; - } - - if(byte == MidiEvent::T_SPECIAL) - { - // Special event FF - uint8_t evtype = *(ptr++); - uint64_t length = ReadVarLenEx(pptr, end, ok); - if(!ok || (ptr + length > end)) - { - errorString += "parseEvent: Can't read Special event - Unexpected end of track data.\n"; - evt.isValid = 0; - return evt; - } - std::string data(length ? (const char *)ptr : 0, (size_t)length); - ptr += (size_t)length; - - evt.type = byte; - evt.subtype = evtype; - evt.data.insert(evt.data.begin(), data.begin(), data.end()); - -#if 0 /* Print all tempo events */ - if(evt.subtype == MidiEvent::ST_TEMPOCHANGE) - { - if(hooks.onDebugMessage) - hooks.onDebugMessage(hooks.onDebugMessage_userData, "Temp Change: %02X%02X%02X", evt.data[0], evt.data[1], evt.data[2]); - } -#endif - - /* TODO: Store those meta-strings separately and give ability to read them - * by external functions (to display song title and copyright in the player) */ - if(evt.subtype == MidiEvent::ST_COPYRIGHT) - { - if(musCopyright.empty()) - { - musCopyright = std::string((const char *)evt.data.data(), evt.data.size()); - if(hooks.onDebugMessage) - hooks.onDebugMessage(hooks.onDebugMessage_userData, "Music copyright: %s", musCopyright.c_str()); - } - else if(hooks.onDebugMessage) - { - std::string str((const char *)evt.data.data(), evt.data.size()); - hooks.onDebugMessage(hooks.onDebugMessage_userData, "Extra copyright event: %s", str.c_str()); - } - } - else if(evt.subtype == MidiEvent::ST_SQTRKTITLE) - { - if(musTitle.empty()) - { - musTitle = std::string((const char *)evt.data.data(), evt.data.size()); - if(hooks.onDebugMessage) - hooks.onDebugMessage(hooks.onDebugMessage_userData, "Music title: %s", musTitle.c_str()); - } - else if(hooks.onDebugMessage) - { - //TODO: Store track titles and associate them with each track and make API to retreive them - std::string str((const char *)evt.data.data(), evt.data.size()); - musTrackTitles.push_back(str); - hooks.onDebugMessage(hooks.onDebugMessage_userData, "Track title: %s", str.c_str()); - } - } - else if(evt.subtype == MidiEvent::ST_INSTRTITLE) - { - if(hooks.onDebugMessage) - { - std::string str((const char *)evt.data.data(), evt.data.size()); - hooks.onDebugMessage(hooks.onDebugMessage_userData, "Instrument: %s", str.c_str()); - } - } - else if(evt.subtype == MidiEvent::ST_MARKER) - { - //To lower - for(size_t i = 0; i < data.size(); i++) - { - if(data[i] <= 'Z' && data[i] >= 'A') - data[i] = data[i] - ('Z' - 'z'); - } - - if(data == "loopstart") - { - //Return a custom Loop Start event instead of Marker - evt.subtype = MidiEvent::ST_LOOPSTART; - evt.data.clear();//Data is not needed - return evt; - } - - if(data == "loopend") - { - //Return a custom Loop End event instead of Marker - evt.subtype = MidiEvent::ST_LOOPEND; - evt.data.clear();//Data is not needed - return evt; - } - } - - if(evtype == MidiEvent::ST_ENDTRACK) - status = -1;//Finalize track - - return evt; - } - - // Any normal event (80..EF) - if(byte < 0x80) - { - byte = static_cast(status | 0x80); - ptr--; - } - - //Sys Com Song Select(Song #) [0-127] - if(byte == MidiEvent::T_SYSCOMSNGSEL) - { - if(ptr + 1 > end) - { - errorString += "parseEvent: Can't read System Command Song Select event - Unexpected end of track data.\n"; - evt.isValid = 0; - return evt; - } - evt.type = byte; - evt.data.push_back(*(ptr++)); - return evt; - } - - //Sys Com Song Position Pntr [LSB, MSB] - if(byte == MidiEvent::T_SYSCOMSPOSPTR) - { - if(ptr + 2 > end) - { - errorString += "parseEvent: Can't read System Command Position Pointer event - Unexpected end of track data.\n"; - evt.isValid = 0; - return evt; - } - evt.type = byte; - evt.data.push_back(*(ptr++)); - evt.data.push_back(*(ptr++)); - return evt; - } - - uint8_t midCh = byte & 0x0F, evType = (byte >> 4) & 0x0F; - status = byte; - evt.channel = midCh; - evt.type = evType; - - switch(evType) - { - case MidiEvent::T_NOTEOFF://2 byte length - case MidiEvent::T_NOTEON: - case MidiEvent::T_NOTETOUCH: - case MidiEvent::T_CTRLCHANGE: - case MidiEvent::T_WHEEL: - if(ptr + 2 > end) - { - errorString += "parseEvent: Can't read regular 2-byte event - Unexpected end of track data.\n"; - evt.isValid = 0; - return evt; - } - - evt.data.push_back(*(ptr++)); - evt.data.push_back(*(ptr++)); - - if((evType == MidiEvent::T_NOTEON) && (evt.data[1] == 0)) - evt.type = MidiEvent::T_NOTEOFF; // Note ON with zero velocity is Note OFF! - //111'th loopStart controller (RPG Maker and others) - else if((evType == MidiEvent::T_CTRLCHANGE) && (evt.data[0] == 111)) - { - //Change event type to custom Loop Start event and clear data - evt.type = MidiEvent::T_SPECIAL; - evt.subtype = MidiEvent::ST_LOOPSTART; - evt.data.clear(); - } - - return evt; - case MidiEvent::T_PATCHCHANGE://1 byte length - case MidiEvent::T_CHANAFTTOUCH: - if(ptr + 1 > end) - { - errorString += "parseEvent: Can't read regular 1-byte event - Unexpected end of track data.\n"; - evt.isValid = 0; - return evt; - } - evt.data.push_back(*(ptr++)); - return evt; - } - - return evt; -} -#endif //OPNMIDI_DISABLE_MIDI_SEQUENCER const std::string &OPNMIDIplay::getErrorString() { @@ -1914,182 +1230,33 @@ void OPNMIDIplay::setErrorString(const std::string &err) errorStringOut = err; } -#ifndef OPNMIDI_DISABLE_MIDI_SEQUENCER -void OPNMIDIplay::HandleEvent(size_t tk, const OPNMIDIplay::MidiEvent &evt, int &status) +int64_t OPNMIDIplay::calculateChipChannelGoodness(size_t c, const MIDIchannel::NoteInfo::Phys &ins) const { - if(hooks.onEvent) + const OpnChannel &chan = m_chipChannels[c]; + int64_t koff_ms = chan.koff_time_until_neglible_us / 1000; + int64_t s = -koff_ms; + + // Rate channel with a releasing note + if(s < 0 && chan.users_empty()) { - hooks.onEvent(hooks.onEvent_userData, - evt.type, - evt.subtype, - evt.channel, - evt.data.data(), - evt.data.size()); + s -= 40000; + // If it's same instrument, better chance to get it when no free channels + if(chan.recent_ins == ins) + s = (m_synth.m_musicMode == OPN2::MODE_CMF) ? 0 : -koff_ms; + return s; } - if(evt.type == MidiEvent::T_SYSEX || evt.type == MidiEvent::T_SYSEX2) // Ignore SysEx - { - //std::string data( length?(const char*) &TrackData[tk][CurrentPosition.track[tk].ptr]:0, length ); - //UI.PrintLn("SysEx %02X: %u bytes", byte, length/*, data.c_str()*/); - return; - } - - if(evt.type == MidiEvent::T_SPECIAL) - { - // Special event FF - uint8_t evtype = evt.subtype; - uint64_t length = (uint64_t)evt.data.size(); - std::string data(length ? (const char *)evt.data.data() : 0, (size_t)length); - - if(evtype == MidiEvent::ST_ENDTRACK)//End Of Track - { - status = -1; - return; - } - - if(evtype == MidiEvent::ST_TEMPOCHANGE)//Tempo change - { - Tempo = InvDeltaTicks * fraction(ReadBEint(evt.data.data(), evt.data.size())); - return; - } - - if(evtype == MidiEvent::ST_MARKER)//Meta event - { - //Do nothing! :-P - return; - } - - if(evtype == MidiEvent::ST_DEVICESWITCH) - { - current_device[tk] = ChooseDevice(data); - return; - } - - //if(evtype >= 1 && evtype <= 6) - // UI.PrintLn("Meta %d: %s", evtype, data.c_str()); - - //Turn on Loop handling when loop is enabled - if(m_setup.loopingIsEnabled && !invalidLoop) - { - if(evtype == MidiEvent::ST_LOOPSTART) // Special non-spec ADLMIDI special for IMF playback: Direct poke to AdLib - { - loopStart = true; - return; - } - - if(evtype == MidiEvent::ST_LOOPEND) // Special non-spec ADLMIDI special for IMF playback: Direct poke to AdLib - { - loopEnd = true; - return; - } - } - - //if(evtype == MidiEvent::ST_RAWOPL) // Special non-spec ADLMIDI special for IMF playback: Direct poke to AdLib - //{ - // uint8_t i = static_cast(data[0]), v = static_cast(data[1]); - // if((i & 0xF0) == 0xC0) - // v |= 0x30; - // //std::printf("OPL poke %02X, %02X\n", i, v); - // //std::fflush(stdout); - // opl.PokeN(0, i, v); - // return; - //} - - return; - } - - // Any normal event (80..EF) - // if(evt.type < 0x80) - // { - // byte = static_cast(CurrentPosition.track[tk].status | 0x80); - // CurrentPosition.track[tk].ptr--; - // } - - if(evt.type == MidiEvent::T_SYSCOMSNGSEL || - evt.type == MidiEvent::T_SYSCOMSPOSPTR) - return; - - /*UI.PrintLn("@%X Track %u: %02X %02X", - CurrentPosition.track[tk].ptr-1, (unsigned)tk, byte, - TrackData[tk][CurrentPosition.track[tk].ptr]);*/ - uint8_t midCh = evt.channel;//byte & 0x0F, EvType = byte >> 4; - midCh += (uint8_t)current_device[tk]; - status = evt.type; - - switch(evt.type) - { - case MidiEvent::T_NOTEOFF: // Note off - { - uint8_t note = evt.data[0]; - realTime_NoteOff(midCh, note); - break; - } - - case MidiEvent::T_NOTEON: // Note on - { - uint8_t note = evt.data[0]; - uint8_t vol = evt.data[1]; - /*if(*/ realTime_NoteOn(midCh, note, vol); /*)*/ - //CurrentPosition.began = true; - break; - } - - case MidiEvent::T_NOTETOUCH: // Note touch - { - uint8_t note = evt.data[0]; - uint8_t vol = evt.data[1]; - realTime_NoteAfterTouch(midCh, note, vol); - break; - } - - case MidiEvent::T_CTRLCHANGE: // Controller change - { - uint8_t ctrlno = evt.data[0]; - uint8_t value = evt.data[1]; - realTime_Controller(midCh, ctrlno, value); - break; - } - - case MidiEvent::T_PATCHCHANGE: // Patch change - realTime_PatchChange(midCh, evt.data[0]); - break; - - case MidiEvent::T_CHANAFTTOUCH: // Channel after-touch - { - // TODO: Verify, is this correct action? - uint8_t vol = evt.data[0]; - realTime_ChannelAfterTouch(midCh, vol); - break; - } - - case MidiEvent::T_WHEEL: // Wheel/pitch bend - { - uint8_t a = evt.data[0]; - uint8_t b = evt.data[1]; - realTime_PitchBend(midCh, b, a); - break; - } - } -} -#endif //OPNMIDI_DISABLE_MIDI_SEQUENCER - -int64_t OPNMIDIplay::CalculateAdlChannelGoodness(size_t c, const MIDIchannel::NoteInfo::Phys &ins, uint16_t) const -{ - int64_t s = -ch[c].koff_time_until_neglible; - // Same midi-instrument = some stability - //if(c == MidCh) s += 4; - for (OpnChannel::LocationData *j = ch[c].users_first; j; j = j->next) + for(OpnChannel::LocationData *j = chan.users_first; j; j = j->next) { - s -= 4000; + s -= 4000000; - if(!j->sustained) - s -= j->kon_time_until_neglible; - else - s -= (j->kon_time_until_neglible / 2); + int64_t kon_ms = j->kon_time_until_neglible_us / 1000; + s -= (j->sustained == OpnChannel::LocationData::Sustain_None) ? + kon_ms : (kon_ms / 2); MIDIchannel::activenoteiterator - k = const_cast(Ch[j->loc.MidCh]).activenotes_find(j->loc.note); + k = const_cast(m_midiChannels[j->loc.MidCh]).activenotes_find(j->loc.note); if(k) { @@ -2098,21 +1265,13 @@ int64_t OPNMIDIplay::CalculateAdlChannelGoodness(size_t c, const MIDIchannel::No { s += 300; // Arpeggio candidate = even better - if(j->vibdelay < 70 - || j->kon_time_until_neglible > 20000) - s += 0; + if(j->vibdelay_us < 70000 + || j->kon_time_until_neglible_us > 20000000) + s += 10; } // Percussion is inferior to melody - s += 50 * (int64_t)(k->midiins / 128); - /* - if(k->second.midiins >= 25 - && k->second.midiins < 40 - && j->second.ins != ins) - { - s -= 14000; // HACK: Don't clobber the bass or the guitar - } - */ + s += k->isPercussion ? 50 : 0; } // If there is another channel to which this note @@ -2129,8 +1288,8 @@ int64_t OPNMIDIplay::CalculateAdlChannelGoodness(size_t c, const MIDIchannel::No // for(OpnChannel::LocationData *m = ch[c2].users_first; m; m = m->next) // { -// if(m->sustained) continue; -// if(m->vibdelay >= 200) continue; +// if(m->sustained != OpnChannel::LocationData::Sustain_None) continue; +// if(m->vibdelay >= 200000) continue; // if(m->ins != j->second.ins) continue; // n_evacuation_stations += 1; // } @@ -2143,12 +1302,12 @@ int64_t OPNMIDIplay::CalculateAdlChannelGoodness(size_t c, const MIDIchannel::No } -void OPNMIDIplay::PrepareAdlChannelForNewNote(size_t c, const MIDIchannel::NoteInfo::Phys &ins) +void OPNMIDIplay::prepareChipChannelForNewNote(size_t c, const MIDIchannel::NoteInfo::Phys &ins) { - if(ch[c].users_empty()) return; // Nothing to do + if(m_chipChannels[c].users_empty()) return; // Nothing to do //bool doing_arpeggio = false; - for(OpnChannel::LocationData *jnext = ch[c].users_first; jnext;) + for(OpnChannel::LocationData *jnext = m_chipChannels[c].users_first; jnext;) { OpnChannel::LocationData *j = jnext; jnext = jnext->next; @@ -2158,11 +1317,11 @@ void OPNMIDIplay::PrepareAdlChannelForNewNote(size_t c, const MIDIchannel::NoteI // Collision: Kill old note, // UNLESS we're going to do arpeggio MIDIchannel::activenoteiterator i - (Ch[j->loc.MidCh].activenotes_ensure_find(j->loc.note)); + (m_midiChannels[j->loc.MidCh].activenotes_ensure_find(j->loc.note)); // Check if we can do arpeggio. - if((j->vibdelay < 70 - || j->kon_time_until_neglible > 20000) + if((j->vibdelay_us < 70000 + || j->kon_time_until_neglible_us > 20000000) && j->ins == ins) { // Do arpeggio together with this note. @@ -2170,7 +1329,7 @@ void OPNMIDIplay::PrepareAdlChannelForNewNote(size_t c, const MIDIchannel::NoteI continue; } - KillOrEvacuate(c, j, i); + killOrEvacuate(c, j, i); // ^ will also erase j from ch[c].users. } } @@ -2178,62 +1337,64 @@ void OPNMIDIplay::PrepareAdlChannelForNewNote(size_t c, const MIDIchannel::NoteI // Kill all sustained notes on this channel // Don't keep them for arpeggio, because arpeggio requires // an intact "activenotes" record. This is a design flaw. - KillSustainingNotes(-1, static_cast(c)); + killSustainingNotes(-1, static_cast(c), OpnChannel::LocationData::Sustain_ANY); // Keyoff the channel so that it can be retriggered, // unless the new note will be introduced as just an arpeggio. - if(ch[c].users_empty()) - opn.NoteOff(c); + if(m_chipChannels[c].users_empty()) + m_synth.noteOff(c); } -void OPNMIDIplay::KillOrEvacuate(size_t from_channel, +void OPNMIDIplay::killOrEvacuate(size_t from_channel, OpnChannel::LocationData *j, OPNMIDIplay::MIDIchannel::activenoteiterator i) { + uint32_t maxChannels = OPN_MAX_CHIPS * 6; + // Before killing the note, check if it can be // evacuated to another channel as an arpeggio // instrument. This helps if e.g. all channels // are full of strings and we want to do percussion. // FIXME: This does not care about four-op entanglements. - for(uint32_t c = 0; c < opn.NumChannels; ++c) + for(uint32_t c = 0; c < m_synth.m_numChannels; ++c) { uint16_t cs = static_cast(c); - if(c > std::numeric_limits::max()) + if(c >= maxChannels) break; if(c == from_channel) continue; //if(opn.four_op_category[c] != opn.four_op_category[from_channel]) // continue; - OpnChannel &adlch = ch[c]; + OpnChannel &adlch = m_chipChannels[c]; if(adlch.users_size == OpnChannel::users_max) continue; // no room for more arpeggio on channel for(OpnChannel::LocationData *m = adlch.users_first; m; m = m->next) { - if(m->vibdelay >= 200 - && m->kon_time_until_neglible < 10000) continue; + if(m->vibdelay_us >= 200000 + && m->kon_time_until_neglible_us < 10000000) continue; if(m->ins != j->ins) continue; if(hooks.onNote) { hooks.onNote(hooks.onNote_userData, (int)from_channel, - i->tone, + i->noteTone, (int)i->midiins, 0, 0.0); hooks.onNote(hooks.onNote_userData, (int)c, - i->tone, + i->noteTone, (int)i->midiins, i->vol, 0.0); } i->phys_erase(static_cast(from_channel)); i->phys_ensure_find_or_create(cs)->assign(j->ins); - if(!ch[cs].users_insert(*j)) + if(!m_chipChannels[cs].users_insert(*j)) assert(false); - ch[from_channel].users_erase(j); + m_chipChannels[from_channel].users_erase(j); return; } } @@ -2246,24 +1407,24 @@ void OPNMIDIplay::KillOrEvacuate(size_t from_channel, ins );*/ // Kill it - NoteUpdate(j->loc.MidCh, + noteUpdate(j->loc.MidCh, i, Upd_Off, static_cast(from_channel)); } -void OPNMIDIplay::Panic() +void OPNMIDIplay::panic() { - for(uint8_t chan = 0; chan < Ch.size(); chan++) + for(uint8_t chan = 0; chan < m_midiChannels.size(); chan++) { for(uint8_t note = 0; note < 128; note++) realTime_NoteOff(chan, note); } } -void OPNMIDIplay::KillSustainingNotes(int32_t MidCh, int32_t this_adlchn) +void OPNMIDIplay::killSustainingNotes(int32_t midCh, int32_t this_adlchn, uint32_t sustain_type) { - uint32_t first = 0, last = opn.NumChannels; + uint32_t first = 0, last = m_synth.m_numChannels; if(this_adlchn >= 0) { @@ -2271,57 +1432,87 @@ void OPNMIDIplay::KillSustainingNotes(int32_t MidCh, int32_t this_adlchn) last = first + 1; } - for(unsigned c = first; c < last; ++c) + for(uint32_t c = first; c < last; ++c) { - if(ch[c].users_empty()) continue; // Nothing to do + if(m_chipChannels[c].users_empty()) + continue; // Nothing to do - for(OpnChannel::LocationData *jnext = ch[c].users_first; jnext;) + for(OpnChannel::LocationData *jnext = m_chipChannels[c].users_first; jnext;) { OpnChannel::LocationData *j = jnext; jnext = jnext->next; - if((MidCh < 0 || j->loc.MidCh == MidCh) - && j->sustained) + if((midCh < 0 || j->loc.MidCh == midCh) + && ((j->sustained & sustain_type) != 0)) { int midiins = '?'; if(hooks.onNote) hooks.onNote(hooks.onNote_userData, (int)c, j->loc.note, midiins, 0, 0.0); - ch[c].users_erase(j); + j->sustained &= ~sustain_type; + if(j->sustained == OpnChannel::LocationData::Sustain_None) + m_chipChannels[c].users_erase(j);//Remove only when note is clean from any holders } } // Keyoff the channel, if there are no users left. - if(ch[c].users_empty()) - opn.NoteOff(c); + if(m_chipChannels[c].users_empty()) + m_synth.noteOff(c); } } -void OPNMIDIplay::SetRPN(unsigned MidCh, unsigned value, bool MSB) +void OPNMIDIplay::markSostenutoNotes(int32_t midCh) { - bool nrpn = Ch[MidCh].nrpn; - unsigned addr = Ch[MidCh].lastmrpn * 0x100 + Ch[MidCh].lastlrpn; + uint32_t first = 0, last = m_synth.m_numChannels; + for(uint32_t c = first; c < last; ++c) + { + if(m_chipChannels[c].users_empty()) + continue; // Nothing to do + + for(OpnChannel::LocationData *jnext = m_chipChannels[c].users_first; jnext;) + { + OpnChannel::LocationData *j = jnext; + jnext = jnext->next; + if((j->loc.MidCh == midCh) && (j->sustained == OpnChannel::LocationData::Sustain_None)) + j->sustained |= OpnChannel::LocationData::Sustain_Sostenuto; + } + } +} + +void OPNMIDIplay::setRPN(size_t midCh, unsigned value, bool MSB) +{ + bool nrpn = m_midiChannels[midCh].nrpn; + unsigned addr = m_midiChannels[midCh].lastmrpn * 0x100 + m_midiChannels[midCh].lastlrpn; switch(addr + nrpn * 0x10000 + MSB * 0x20000) { case 0x0000 + 0*0x10000 + 1*0x20000: // Pitch-bender sensitivity - Ch[MidCh].bendsense_msb = value; - Ch[MidCh].updateBendSensitivity(); + m_midiChannels[midCh].bendsense_msb = value; + m_midiChannels[midCh].updateBendSensitivity(); break; case 0x0000 + 0*0x10000 + 0*0x20000: // Pitch-bender sensitivity LSB - Ch[MidCh].bendsense_lsb = value; - Ch[MidCh].updateBendSensitivity(); + m_midiChannels[midCh].bendsense_lsb = value; + m_midiChannels[midCh].updateBendSensitivity(); break; case 0x0108 + 1*0x10000 + 1*0x20000: // Vibrato speed - if(value == 64) Ch[MidCh].vibspeed = 1.0; - else if(value < 100) Ch[MidCh].vibspeed = 1.0 / (1.6e-2 * (value ? value : 1)); - else Ch[MidCh].vibspeed = 1.0 / (0.051153846 * value - 3.4965385); - Ch[MidCh].vibspeed *= 2 * 3.141592653 * 5.0; + if((m_synthMode & Mode_XG) != 0) // Vibrato speed + { + if(value == 64) m_midiChannels[midCh].vibspeed = 1.0; + else if(value < 100) m_midiChannels[midCh].vibspeed = 1.0 / (1.6e-2 * (value ? value : 1)); + else m_midiChannels[midCh].vibspeed = 1.0 / (0.051153846 * value - 3.4965385); + m_midiChannels[midCh].vibspeed *= 2 * 3.141592653 * 5.0; + } break; - case 0x0109 + 1*0x10000 + 1*0x20000: // Vibrato depth - Ch[MidCh].vibdepth = ((value - 64) * 0.15) * 0.01; + case 0x0109 + 1*0x10000 + 1*0x20000: + if((m_synthMode & Mode_XG) != 0) // Vibrato depth + { + m_midiChannels[midCh].vibdepth = (((int)value - 64) * 0.15) * 0.01; + } break; - case 0x010A + 1*0x10000 + 1*0x20000: // Vibrato delay in millisecons - Ch[MidCh].vibdelay = value ? int64_t(0.2092 * std::exp(0.0795 * (double)value)) : 0; + case 0x010A + 1*0x10000 + 1*0x20000: + if((m_synthMode & Mode_XG) != 0) // Vibrato delay in millisecons + { + m_midiChannels[midCh].vibdelay_us = value ? int64_t(209.2 * std::exp(0.0795 * (double)value)) : 0; + } break; default:/* UI.PrintLn("%s %04X <- %d (%cSB) (ch %u)", "NRPN"+!nrpn, addr, value, "LM"[MSB], MidCh);*/ @@ -2329,67 +1520,56 @@ void OPNMIDIplay::SetRPN(unsigned MidCh, unsigned value, bool MSB) } } -//void MIDIplay::UpdatePortamento(unsigned MidCh) -//{ -// // mt = 2^(portamento/2048) * (1.0 / 5000.0) -// /* -// double mt = std::exp(0.00033845077 * Ch[MidCh].portamento); -// NoteUpdate_All(MidCh, Upd_Pitch); -// */ -// //UI.PrintLn("Portamento %u: %u (unimplemented)", MidCh, Ch[MidCh].portamento); -//} - -void OPNMIDIplay::NoteUpdate_All(uint16_t MidCh, unsigned props_mask) +void OPNMIDIplay::updatePortamento(size_t midCh) { - for(MIDIchannel::activenoteiterator - i = Ch[MidCh].activenotes_begin(); i;) - { - MIDIchannel::activenoteiterator j(i++); - NoteUpdate(MidCh, j, props_mask); - } + double rate = HUGE_VAL; + uint16_t midival = m_midiChannels[midCh].portamento; + if(m_midiChannels[midCh].portamentoEnable && midival > 0) + rate = 350.0 * std::pow(2.0, -0.062 * (1.0 / 128) * midival); + m_midiChannels[midCh].portamentoRate = rate; } -void OPNMIDIplay::NoteOff(uint16_t MidCh, uint8_t note) +void OPNMIDIplay::noteOff(size_t midCh, uint8_t note) { MIDIchannel::activenoteiterator - i = Ch[MidCh].activenotes_find(note); + i = m_midiChannels[midCh].activenotes_find(note); if(i) - NoteUpdate(MidCh, i, Upd_Off); + noteUpdate(midCh, i, Upd_Off); } -void OPNMIDIplay::UpdateVibrato(double amount) +void OPNMIDIplay::updateVibrato(double amount) { - for(size_t a = 0, b = Ch.size(); a < b; ++a) + for(size_t a = 0, b = m_midiChannels.size(); a < b; ++a) { - if(Ch[a].hasVibrato() && !Ch[a].activenotes_empty()) + if(m_midiChannels[a].hasVibrato() && !m_midiChannels[a].activenotes_empty()) { - NoteUpdate_All(static_cast(a), Upd_Pitch); - Ch[a].vibpos += amount * Ch[a].vibspeed; + noteUpdateAll(static_cast(a), Upd_Pitch); + m_midiChannels[a].vibpos += amount * m_midiChannels[a].vibspeed; } else - Ch[a].vibpos = 0.0; + m_midiChannels[a].vibpos = 0.0; } } -uint64_t OPNMIDIplay::ChooseDevice(const std::string &name) +size_t OPNMIDIplay::chooseDevice(const std::string &name) { - std::map::iterator i = devices.find(name); + std::map::iterator i = m_midiDevices.find(name); - if(i != devices.end()) + if(i != m_midiDevices.end()) return i->second; - size_t n = devices.size() * 16; - devices.insert(std::make_pair(name, n)); - Ch.resize(n + 16); + size_t n = m_midiDevices.size() * 16; + m_midiDevices.insert(std::make_pair(name, n)); + m_midiChannels.resize(n + 16); return n; } -void OPNMIDIplay::UpdateArpeggio(double) // amount = amount of time passed +void OPNMIDIplay::updateArpeggio(double) // amount = amount of time passed { // If there is an adlib channel that has multiple notes // simulated on the same channel, arpeggio them. @@ -2415,17 +1595,17 @@ void OPNMIDIplay::UpdateArpeggio(double) // amount = amount of time passed ++m_arpeggioCounter; - for(uint32_t c = 0; c < opn.NumChannels; ++c) + for(uint32_t c = 0; c < m_synth.m_numChannels; ++c) { retry_arpeggio: if(c > uint32_t(std::numeric_limits::max())) break; - size_t n_users = ch[c].users_size; + size_t n_users = m_chipChannels[c].users_size; if(n_users > 1) { - OpnChannel::LocationData *i = ch[c].users_first; + OpnChannel::LocationData *i = m_chipChannels[c].users_first; size_t rate_reduction = 3; if(n_users >= 3) @@ -2438,21 +1618,21 @@ retry_arpeggio: n = 0; n < count; ++n) i = i->next; - if(i->sustained == false) + if(i->sustained == OpnChannel::LocationData::Sustain_None) { - if(i->kon_time_until_neglible <= 0l) + if(i->kon_time_until_neglible_us <= 0) { - NoteUpdate( + noteUpdate( i->loc.MidCh, - Ch[ i->loc.MidCh ].activenotes_ensure_find(i->loc.note), + m_midiChannels[ i->loc.MidCh ].activenotes_ensure_find(i->loc.note), Upd_Off, static_cast(c)); goto retry_arpeggio; } - NoteUpdate( + noteUpdate( i->loc.MidCh, - Ch[ i->loc.MidCh ].activenotes_ensure_find(i->loc.note), + m_midiChannels[ i->loc.MidCh ].activenotes_ensure_find(i->loc.note), Upd_Pitch | Upd_Volume | Upd_Pan, static_cast(c)); } @@ -2460,6 +1640,76 @@ retry_arpeggio: } } +void OPNMIDIplay::updateGlide(double amount) +{ + size_t num_channels = m_midiChannels.size(); + + for(size_t channel = 0; channel < num_channels; ++channel) + { + MIDIchannel &midiChan = m_midiChannels[channel]; + if(midiChan.gliding_note_count == 0) + continue; + + for(MIDIchannel::activenoteiterator it = midiChan.activenotes_begin(); + it; ++it) + { + double finalTone = it->noteTone; + double previousTone = it->currentTone; + + bool directionUp = previousTone < finalTone; + double toneIncr = amount * (directionUp ? +it->glideRate : -it->glideRate); + + double currentTone = previousTone + toneIncr; + bool glideFinished = !(directionUp ? (currentTone < finalTone) : (currentTone > finalTone)); + currentTone = glideFinished ? finalTone : currentTone; + + if(currentTone != previousTone) + { + it->currentTone = currentTone; + noteUpdate(static_cast(channel), it, Upd_Pitch); + } + } + } +} + +void OPNMIDIplay::describeChannels(char *str, char *attr, size_t size) +{ + if (!str || size <= 0) + return; + + OPN2 &synth = m_synth; + uint32_t numChannels = synth.m_numChannels; + + uint32_t index = 0; + while(index < numChannels && index < size - 1) + { + const OpnChannel &adlChannel = m_chipChannels[index]; + + OpnChannel::LocationData *loc = adlChannel.users_first; + if(!loc) // off + { + str[index] = '-'; + } + else if(loc->next) // arpeggio + { + str[index] = '@'; + } + else // on + { + str[index] = '+'; + } + + uint8_t attribute = 0; + if (loc) // 4-bit color index of MIDI channel + attribute |= (uint8_t)(loc->loc.MidCh & 0xF); + + attr[index] = (char)attribute; + ++index; + } + + str[index] = 0; + attr[index] = 0; +} /* TODO */ diff --git a/src/sound/opnmidi/opnmidi_opn2.cpp b/src/sound/opnmidi/opnmidi_opn2.cpp index 392294802..a5a60d226 100644 --- a/src/sound/opnmidi/opnmidi_opn2.cpp +++ b/src/sound/opnmidi/opnmidi_opn2.cpp @@ -48,23 +48,60 @@ #include "chips/gx_opn2.h" #endif +static const unsigned opn2_emulatorSupport = 0 +#ifndef OPNMIDI_DISABLE_NUKED_EMULATOR + | (1u << OPNMIDI_EMU_NUKED) +#endif +#ifndef OPNMIDI_DISABLE_MAME_EMULATOR + | (1u << OPNMIDI_EMU_MAME) +#endif +#ifndef OPNMIDI_DISABLE_GENS_EMULATOR + | (1u << OPNMIDI_EMU_GENS) +#endif +#ifndef OPNMIDI_DISABLE_GX_EMULATOR + | (1u << OPNMIDI_EMU_GX) +#endif +; -static const uint8_t NoteChannels[6] = { 0, 1, 2, 4, 5, 6 }; - -static inline void getOpnChannel(uint32_t in_channel, - unsigned &out_card, - uint8_t &out_port, - uint8_t &out_ch) +//! Check emulator availability +bool opn2_isEmulatorAvailable(int emulator) { - out_card = in_channel / 6; - uint8_t ch4 = in_channel % 6; - out_port = ((ch4 < 3) ? 0 : 1); - out_ch = ch4 % 3; + return (opn2_emulatorSupport & (1u << (unsigned)emulator)) != 0; } -void OPN2::cleanInstrumentBanks() +//! Find highest emulator +int opn2_getHighestEmulator() { - dynamic_banks.clear(); + int emu = -1; + for(unsigned m = opn2_emulatorSupport; m > 0; m >>= 1) + ++emu; + return emu; +} + +//! Find lowest emulator +int opn2_getLowestEmulator() +{ + int emu = -1; + unsigned m = opn2_emulatorSupport; + if(m > 0) + { + for(emu = 0; (m & 1) == 0; m >>= 1) + ++emu; + } + return emu; +} + +static const uint32_t g_noteChannelsMap[6] = { 0, 1, 2, 4, 5, 6 }; + +static inline void getOpnChannel(size_t in_channel, + size_t &out_chip, + uint8_t &out_port, + uint32_t &out_ch) +{ + out_chip = in_channel / 6; + size_t ch4 = in_channel % 6; + out_port = ((ch4 < 3) ? 0 : 1); + out_ch = static_cast(ch4 % 3); } static opnInstMeta2 makeEmptyInstrument() @@ -75,71 +112,128 @@ static opnInstMeta2 makeEmptyInstrument() return ins; } -const opnInstMeta2 OPN2::emptyInstrument = makeEmptyInstrument(); +const opnInstMeta2 OPN2::m_emptyInstrument = makeEmptyInstrument(); OPN2::OPN2() : - regLFO(0), - NumCards(1), + m_regLFOSetup(0), + m_numChips(1), + m_scaleModulators(false), + m_runAtPcmRate(false), + m_softPanning(false), m_musicMode(MODE_MIDI), - m_volumeScale(VOLUME_Generic) + m_volumeScale(VOLUME_Generic), + m_lfoEnable(false), + m_lfoFrequency(0) { + m_insBankSetup.volumeModel = OPN2::VOLUME_Generic; + m_insBankSetup.lfoEnable = false; + m_insBankSetup.lfoFrequency = 0; + // Initialize blank instruments banks - cleanInstrumentBanks(); + m_insBanks.clear(); } OPN2::~OPN2() { - ClearChips(); + clearChips(); } -void OPN2::PokeO(size_t card, uint8_t port, uint8_t index, uint8_t value) +bool OPN2::setupLocked() { - cardsOP2[card]->writeReg(port, index, value); + return (m_musicMode == MODE_CMF || + m_musicMode == MODE_IMF || + m_musicMode == MODE_RSXX); } -void OPN2::NoteOff(size_t c) +void OPN2::writeReg(size_t chip, uint8_t port, uint8_t index, uint8_t value) { - unsigned card; - uint8_t port, cc; - uint8_t ch4 = c % 6; - getOpnChannel(uint16_t(c), card, port, cc); - PokeO(card, 0, 0x28, NoteChannels[ch4]); + m_chips[chip]->writeReg(port, index, value); } -void OPN2::NoteOn(unsigned c, double hertz) // Hertz range: 0..131071 +void OPN2::writeRegI(size_t chip, uint8_t port, uint32_t index, uint32_t value) { - unsigned card; - uint8_t port, cc; - uint8_t ch4 = c % 6; - getOpnChannel(uint16_t(c), card, port, cc); + m_chips[chip]->writeReg(port, static_cast(index), static_cast(value)); +} - uint16_t x2 = 0x0000; +void OPN2::writePan(size_t chip, uint32_t index, uint32_t value) +{ + m_chips[chip]->writePan(static_cast(index), static_cast(value)); +} - if(hertz < 0 || hertz > 262143) // Avoid infinite loop +void OPN2::noteOff(size_t c) +{ + size_t chip; + uint8_t port; + uint32_t cc; + size_t ch4 = c % 6; + getOpnChannel(c, chip, port, cc); + writeRegI(chip, 0, 0x28, g_noteChannelsMap[ch4]); +} + +void OPN2::noteOn(size_t c, double hertz) // Hertz range: 0..131071 +{ + size_t chip; + uint8_t port; + uint32_t cc; + size_t ch4 = c % 6; + getOpnChannel(c, chip, port, cc); + + if(hertz < 0) // Avoid infinite loop return; - while((hertz >= 1023.75) && (x2 < 0x3800)) + uint32_t octave = 0, ftone = 0, mul_offset = 0; + const opnInstData &adli = m_insCache[c]; + + //Basic range until max of octaves reaching + while((hertz >= 1023.75) && (octave < 0x3800)) { hertz /= 2.0; // Calculate octave - x2 += 0x800; + octave += 0x800; + } + //Extended range, rely on frequency multiplication increment + while(hertz >= 2036.75) + { + hertz /= 2.0; // Calculate octave + mul_offset++; + } + ftone = octave + static_cast(hertz + 0.5); + + for(size_t op = 0; op < 4; op++) + { + uint32_t reg = adli.OPS[op].data[0]; + uint16_t address = static_cast(0x30 + (op * 4) + cc); + if(mul_offset > 0) // Increase frequency multiplication value + { + uint32_t dt = reg & 0xF0; + uint32_t mul = reg & 0x0F; + if((mul + mul_offset) > 0x0F) + { + mul_offset = 0; + mul = 0x0F; + } + writeRegI(chip, port, address, uint8_t(dt | (mul + mul_offset))); + } + else + { + writeRegI(chip, port, address, uint8_t(reg)); + } } - x2 += static_cast(hertz + 0.5); - PokeO(card, port, 0xA4 + cc, (x2>>8) & 0xFF);//Set frequency and octave - PokeO(card, port, 0xA0 + cc, x2 & 0xFF); - PokeO(card, 0, 0x28, 0xF0 + NoteChannels[ch4]); - pit[c] = static_cast(x2 >> 8); + writeRegI(chip, port, 0xA4 + cc, (ftone>>8) & 0xFF);//Set frequency and octave + writeRegI(chip, port, 0xA0 + cc, ftone & 0xFF); + writeRegI(chip, 0, 0x28, 0xF0 + g_noteChannelsMap[ch4]); } -void OPN2::Touch_Real(unsigned c, unsigned volume, uint8_t brightness) +void OPN2::touchNote(size_t c, uint8_t volume, uint8_t brightness) { if(volume > 127) volume = 127; - unsigned card; - uint8_t port, cc; - getOpnChannel(c, card, port, cc); + size_t chip; + uint8_t port; + uint32_t cc; + getOpnChannel(c, chip, port, cc); - const opnInstData &adli = ins[c]; + const opnInstData &adli = m_insCache[c]; uint8_t op_vol[4] = { @@ -170,16 +264,16 @@ void OPN2::Touch_Real(unsigned c, unsigned volume, uint8_t brightness) uint8_t alg = adli.fbalg & 0x07; for(uint8_t op = 0; op < 4; op++) { - bool do_op = alg_do[alg][op] || ScaleModulators; - uint8_t x = op_vol[op]; - uint8_t vol_res = do_op ? uint8_t(127 - (volume * (127 - (x&127)))/127) : x; + bool do_op = alg_do[alg][op] || m_scaleModulators; + uint32_t x = op_vol[op]; + uint32_t vol_res = do_op ? (127 - (static_cast(volume) * (127 - (x & 127)))/127) : x; if(brightness != 127) { - brightness = static_cast(::round(127.0 * ::sqrt((static_cast(brightness)) * (1.0 / 127.0)))); + brightness = static_cast(::round(127.0 * ::sqrt((static_cast(brightness)) * (1.0 / 127.0)))); if(!do_op) - vol_res = uint8_t(127 - (brightness * (127 - (uint32_t(vol_res) & 127))) / 127); + vol_res = (127 - (brightness * (127 - (static_cast(vol_res) & 127))) / 127); } - PokeO(card, port, 0x40 + cc + (4 * op), vol_res); + writeRegI(chip, port, 0x40 + cc + (4 * op), vol_res); } // Correct formula (ST3, AdPlug): // 63-((63-(instrvol))/63)*chanvol @@ -189,57 +283,68 @@ void OPN2::Touch_Real(unsigned c, unsigned volume, uint8_t brightness) // 63 + chanvol * (instrvol / 63.0 - 1) } -void OPN2::Patch(uint16_t c, const opnInstData &adli) +void OPN2::setPatch(size_t c, const opnInstData &instrument) { - unsigned card; - uint8_t port, cc; - getOpnChannel(uint16_t(c), card, port, cc); - ins[c] = adli; - #if 1 //Reg1-Op1, Reg1-Op2, Reg1-Op3, Reg1-Op4,.... + size_t chip; + uint8_t port; + uint32_t cc; + getOpnChannel(c, chip, port, cc); + m_insCache[c] = instrument; for(uint8_t d = 0; d < 7; d++) { for(uint8_t op = 0; op < 4; op++) - PokeO(card, port, 0x30 + (0x10 * d) + (op * 4) + cc, adli.OPS[op].data[d]); + writeRegI(chip, port, 0x30 + (0x10 * d) + (op * 4) + cc, instrument.OPS[op].data[d]); } - #else //Reg1-Op1, Reg2-Op1, Reg3-Op1, Reg4-Op1,.... - for(uint8_t op = 0; op < 4; op++) - { - PokeO(card, port, 0x30 + (op * 4) + cc, adli.OPS[op].data[0]); - PokeO(card, port, 0x40 + (op * 4) + cc, adli.OPS[op].data[1]); - PokeO(card, port, 0x50 + (op * 4) + cc, adli.OPS[op].data[2]); - PokeO(card, port, 0x60 + (op * 4) + cc, adli.OPS[op].data[3]); - PokeO(card, port, 0x70 + (op * 4) + cc, adli.OPS[op].data[4]); - PokeO(card, port, 0x80 + (op * 4) + cc, adli.OPS[op].data[5]); - PokeO(card, port, 0x90 + (op * 4) + cc, adli.OPS[op].data[6]); - } - #endif - PokeO(card, port, 0xB0 + cc, adli.fbalg);//Feedback/Algorithm - regBD[c] = (regBD[c] & 0xC0) | (adli.lfosens & 0x3F); - PokeO(card, port, 0xB4 + cc, regBD[c]);//Panorame and LFO bits + writeRegI(chip, port, 0xB0 + cc, instrument.fbalg);//Feedback/Algorithm + m_regLFOSens[c] = (m_regLFOSens[c] & 0xC0) | (instrument.lfosens & 0x3F); + writeRegI(chip, port, 0xB4 + cc, m_regLFOSens[c]);//Panorame and LFO bits } -void OPN2::Pan(unsigned c, unsigned value) +void OPN2::setPan(size_t c, uint8_t value) { - unsigned card; - uint8_t port, cc; - getOpnChannel(uint16_t(c), card, port, cc); - const opnInstData &adli = ins[c]; - uint8_t val = (value & 0xC0) | (adli.lfosens & 0x3F); - regBD[c] = val; - PokeO(card, port, 0xB4 + cc, val); + size_t chip; + uint8_t port; + uint32_t cc; + getOpnChannel(c, chip, port, cc); + const opnInstData &adli = m_insCache[c]; + uint8_t val = 0; + if(m_softPanning) + { + val = (OPN_PANNING_BOTH & 0xC0) | (adli.lfosens & 0x3F); + writePan(chip, c % 6, value); + writeRegI(chip, port, 0xB4 + cc, val); + } + else + { + int panning = 0; + if(value < 64 + 32) panning |= OPN_PANNING_LEFT; + if(value >= 64 - 32) panning |= OPN_PANNING_RIGHT; + val = (panning & 0xC0) | (adli.lfosens & 0x3F); + writePan(chip, c % 6, 64); + writeRegI(chip, port, 0xB4 + cc, val); + } + m_regLFOSens[c] = val; } -void OPN2::Silence() // Silence all OPL channels. +void OPN2::silenceAll() // Silence all OPL channels. { - for(unsigned c = 0; c < NumChannels; ++c) + for(size_t c = 0; c < m_numChannels; ++c) { - NoteOff(c); - Touch_Real(c, 0); + noteOff(c); + touchNote(c, 0); } } -void OPN2::ChangeVolumeRangesModel(OPNMIDI_VolumeModels volumeModel) +void OPN2::commitLFOSetup() +{ + uint8_t regLFOSetup = (m_lfoEnable ? 8 : 0) | (m_lfoFrequency & 7); + m_regLFOSetup = regLFOSetup; + for(size_t chip = 0; chip < m_numChips; ++chip) + writeReg(chip, 0, 0x22, regLFOSetup); +} + +void OPN2::setVolumeScaleModel(OPNMIDI_VolumeModels volumeModel) { switch(volumeModel) { @@ -250,8 +355,8 @@ void OPN2::ChangeVolumeRangesModel(OPNMIDI_VolumeModels volumeModel) m_volumeScale = OPN2::VOLUME_Generic; break; - case OPNMIDI_VolumeModel_CMF: - m_volumeScale = OPN2::VOLUME_CMF; + case OPNMIDI_VolumeModel_NativeOPN2: + m_volumeScale = OPN2::VOLUME_NATIVE; break; case OPNMIDI_VolumeModel_DMX: @@ -268,70 +373,101 @@ void OPN2::ChangeVolumeRangesModel(OPNMIDI_VolumeModels volumeModel) } } -void OPN2::ClearChips() +OPNMIDI_VolumeModels OPN2::getVolumeScaleModel() { - for(size_t i = 0; i < cardsOP2.size(); i++) - cardsOP2[i].reset(NULL); - cardsOP2.clear(); + switch(m_volumeScale) + { + default: + case OPN2::VOLUME_Generic: + return OPNMIDI_VolumeModel_Generic; + case OPN2::VOLUME_NATIVE: + return OPNMIDI_VolumeModel_NativeOPN2; + case OPN2::VOLUME_DMX: + return OPNMIDI_VolumeModel_DMX; + case OPN2::VOLUME_APOGEE: + return OPNMIDI_VolumeModel_APOGEE; + case OPN2::VOLUME_9X: + return OPNMIDI_VolumeModel_9X; + } } -void OPN2::Reset(int emulator, unsigned long PCM_RATE) +void OPN2::clearChips() { - ClearChips(); - ins.clear(); - pit.clear(); - regBD.clear(); - cardsOP2.resize(NumCards, AdlMIDI_SPtr()); + for(size_t i = 0; i < m_chips.size(); i++) + m_chips[i].reset(NULL); + m_chips.clear(); +} - for(size_t i = 0; i < cardsOP2.size(); i++) +void OPN2::reset(int emulator, unsigned long PCM_RATE, void *audioTickHandler) +{ +#if !defined(ADLMIDI_AUDIO_TICK_HANDLER) + ADL_UNUSED(audioTickHandler); +#endif + clearChips(); + m_insCache.clear(); + m_regLFOSens.clear(); + m_chips.resize(m_numChips, AdlMIDI_SPtr()); + + for(size_t i = 0; i < m_chips.size(); i++) { + OPNChipBase *chip; + switch(emulator) { default: + assert(false); + abort(); #ifndef OPNMIDI_DISABLE_MAME_EMULATOR case OPNMIDI_EMU_MAME: - cardsOP2[i].reset(new MameOPN2()); + chip = new MameOPN2; break; #endif #ifndef OPNMIDI_DISABLE_NUKED_EMULATOR case OPNMIDI_EMU_NUKED: - cardsOP2[i].reset(new NukedOPN2()); + chip = new NukedOPN2; break; #endif #ifndef OPNMIDI_DISABLE_GENS_EMULATOR case OPNMIDI_EMU_GENS: - cardsOP2[i].reset(new GensOPN2()); + chip = new GensOPN2; break; #endif #ifndef OPNMIDI_DISABLE_GX_EMULATOR case OPNMIDI_EMU_GX: - cardsOP2[i].reset(new GXOPN2()); + chip = new GXOPN2; break; #endif } - cardsOP2[i]->setRate((uint32_t)PCM_RATE, 7670454); - if(runAtPcmRate) - cardsOP2[i]->setRunningAtPcmRate(true); + m_chips[i].reset(chip); + chip->setChipId((uint32_t)i); + chip->setRate((uint32_t)PCM_RATE, 7670454); + if(m_runAtPcmRate) + chip->setRunningAtPcmRate(true); +#if defined(ADLMIDI_AUDIO_TICK_HANDLER) + chip->setAudioTickHandlerInstance(audioTickHandler); +#endif } - NumChannels = NumCards * 6; - ins.resize(NumChannels, emptyInstrument.opn[0]); - pit.resize(NumChannels, 0); - regBD.resize(NumChannels, 0); + m_numChannels = m_numChips * 6; + m_insCache.resize(m_numChannels, m_emptyInstrument.opn[0]); + m_regLFOSens.resize(m_numChannels, 0); - for(unsigned card = 0; card < NumCards; ++card) + uint8_t regLFOSetup = (m_lfoEnable ? 8 : 0) | (m_lfoFrequency & 7); + m_regLFOSetup = regLFOSetup; + + for(size_t card = 0; card < m_numChips; ++card) { - PokeO(card, 0, 0x22, regLFO);//push current LFO state - PokeO(card, 0, 0x27, 0x00); //set Channel 3 normal mode - PokeO(card, 0, 0x2B, 0x00); //Disable DAC + writeReg(card, 0, 0x22, regLFOSetup);//push current LFO state + writeReg(card, 0, 0x27, 0x00); //set Channel 3 normal mode + writeReg(card, 0, 0x2B, 0x00); //Disable DAC //Shut up all channels - PokeO(card, 0, 0x28, 0x00 ); //Note Off 0 channel - PokeO(card, 0, 0x28, 0x01 ); //Note Off 1 channel - PokeO(card, 0, 0x28, 0x02 ); //Note Off 2 channel - PokeO(card, 0, 0x28, 0x04 ); //Note Off 3 channel - PokeO(card, 0, 0x28, 0x05 ); //Note Off 4 channel - PokeO(card, 0, 0x28, 0x06 ); //Note Off 5 channel + writeReg(card, 0, 0x28, 0x00 ); //Note Off 0 channel + writeReg(card, 0, 0x28, 0x01 ); //Note Off 1 channel + writeReg(card, 0, 0x28, 0x02 ); //Note Off 2 channel + writeReg(card, 0, 0x28, 0x04 ); //Note Off 3 channel + writeReg(card, 0, 0x28, 0x05 ); //Note Off 4 channel + writeReg(card, 0, 0x28, 0x06 ); //Note Off 5 channel } - Silence(); + silenceAll(); } diff --git a/src/sound/opnmidi/opnmidi_private.cpp b/src/sound/opnmidi/opnmidi_private.cpp index 1faced79d..47c2c87ac 100644 --- a/src/sound/opnmidi/opnmidi_private.cpp +++ b/src/sound/opnmidi/opnmidi_private.cpp @@ -25,31 +25,12 @@ std::string OPN2MIDI_ErrorString; -int opn2RefreshNumCards(OPN2_MIDIPlayer * /*device*/) -{ -// OPNMIDIplay *play = reinterpret_cast(device->opn2_midiPlayer); - //OPN uses 4-op instruments only -// unsigned n_fourop[2] = {0, 0}, n_total[2] = {0, 0}; -// for(unsigned a = 0; a < 256; ++a) -// { -// unsigned insno = banks[device->OpnBank][a]; -// if(insno == 198) continue; -// ++n_total[a / 128]; -// if(adlins[insno].adlno1 != adlins[insno].adlno2) -// ++n_fourop[a / 128]; -// } +// Generator callback on audio rate ticks -// device->NumFourOps = -// (n_fourop[0] >= n_total[0] * 7 / 8) ? device->NumCards * 6 -// : (n_fourop[0] < n_total[0] * 1 / 8) ? 0 -// : (device->NumCards == 1 ? 1 : device->NumCards * 4); -// reinterpret_cast(device->opn2_midiPlayer)->opn.NumFourOps = device->NumFourOps; -// if(n_fourop[0] >= n_total[0] * 15 / 16 && device->NumFourOps == 0) -// { -// OPN2MIDI_ErrorString = "ERROR: You have selected a bank that consists almost exclusively of four-op patches.\n" -// " The results (silence + much cpu load) would be probably\n" -// " not what you want, therefore ignoring the request.\n"; -// return -1; -// } - return 0; +#if defined(ADLMIDI_AUDIO_TICK_HANDLER) +void opn2_audioTickHandler(void *instance, uint32_t chipId, uint32_t rate) +{ + reinterpret_cast(instance)->AudioTick(chipId, rate); } +#endif + diff --git a/src/sound/opnmidi/opnmidi_private.hpp b/src/sound/opnmidi/opnmidi_private.hpp index ed10dcd10..353cc3d03 100644 --- a/src/sound/opnmidi/opnmidi_private.hpp +++ b/src/sound/opnmidi/opnmidi_private.hpp @@ -24,11 +24,13 @@ #ifndef ADLMIDI_PRIVATE_HPP #define ADLMIDI_PRIVATE_HPP +#define OPNMIDI_UNSTABLE_API + // Setup compiler defines useful for exporting required public API symbols in gme.cpp #ifndef OPNMIDI_EXPORT -# if defined (_WIN32) && defined(ADLMIDI_BUILD_DLL) +# if defined (_WIN32) && defined(OPNMIDI_BUILD_DLL) # define OPNMIDI_EXPORT __declspec(dllexport) -# elif defined (LIBADLMIDI_VISIBILITY) +# elif defined (LIBOPNMIDI_VISIBILITY) && defined (__GNUC__) # define OPNMIDI_EXPORT __attribute__((visibility ("default"))) # else # define OPNMIDI_EXPORT @@ -36,28 +38,35 @@ #endif #ifdef _WIN32 -# undef NO_OLDNAMES +#define NOMINMAX 1 +#endif +#ifdef _WIN32 +# undef NO_OLDNAMES +# include # ifdef _MSC_VER # ifdef _WIN64 typedef __int64 ssize_t; # else typedef __int32 ssize_t; # endif -# define NOMINMAX //Don't override std::min and std::max +# define NOMINMAX 1 //Don't override std::min and std::max +# else +# ifdef _WIN64 +typedef int64_t ssize_t; +# else +typedef int32_t ssize_t; +# endif # endif # include #endif -#ifdef USE_LEGACY_EMULATOR // Kept for a backward compatibility -#define OPNMIDI_USE_LEGACY_EMULATOR -#endif - #include #include #include #include #include +#include // nothrow #include #include #include @@ -67,6 +76,7 @@ typedef __int32 ssize_t; #include // vector #include // deque #include // exp, log, ceil +#include #include #include #include // numeric_limit @@ -100,11 +110,22 @@ typedef __int32 ssize_t; #define INT32_MAX 0x7fffffff #endif -#include "fraction.hpp" +#include "file_reader.hpp" + +#ifndef OPNMIDI_DISABLE_MIDI_SEQUENCER +// Rename class to avoid ABI collisions +#define BW_MidiSequencer OpnMidiSequencer +#include "midi_sequencer.hpp" +typedef BW_MidiSequencer MidiSequencer; +#endif//OPNMIDI_DISABLE_MIDI_SEQUENCER + #include "chips/opn_chip_base.h" #include "opnbank.h" -#include "opnmidi.h" + +#define OPNMIDI_BUILD +#include "opnmidi.h" //Main API + #include "opnmidi_ptr.hpp" #include "opnmidi_bankmap.h" @@ -114,6 +135,9 @@ typedef __int32 ssize_t; #define OPN_PANNING_RIGHT 0x40 #define OPN_PANNING_BOTH 0xC0 +#define OPN_MAX_CHIPS 100 +#define OPN_MAX_CHIPS_STR "100" + extern std::string OPN2MIDI_ErrorString; /* @@ -122,7 +146,7 @@ extern std::string OPN2MIDI_ErrorString; template inline Real opn2_cvtReal(int32_t x) { - return x * ((Real)1 / INT16_MAX); + return static_cast(x) * (static_cast(1) / static_cast(INT16_MAX)); } inline int32_t opn2_cvtS16(int32_t x) { @@ -162,80 +186,212 @@ inline int32_t opn2_cvtU32(int32_t x) } class OPNMIDIplay; +/** + * @brief OPN2 Chip management class + */ class OPN2 { -public: friend class OPNMIDIplay; - uint32_t NumChannels; - char ____padding[4]; - std::vector > cardsOP2; -private: - std::vector ins; // patch data, cached, needed by Touch() - std::vector pit; // value poked to B0, cached, needed by NoteOff)( - std::vector regBD; - uint8_t regLFO; - - void cleanInstrumentBanks(); public: + enum { PercussionTag = 1 << 15 }; + + //! Total number of chip channels between all running emulators + uint32_t m_numChannels; + //! Just a padding. Reserved. + char _padding[4]; + //! Running chip emulators + std::vector > m_chips; +private: + //! Cached patch data, needed by Touch() + std::vector m_insCache; + //! Cached per-channel LFO sensitivity flags + std::vector m_regLFOSens; + //! LFO setup registry cache + uint8_t m_regLFOSetup; + +public: + /** + * @brief MIDI bank entry + */ struct Bank { + //! MIDI Bank instruments opnInstMeta2 ins[128]; }; typedef BasicBankMap BankMap; - BankMap dynamic_banks; + //! MIDI bank instruments data + BankMap m_insBanks; + //! MIDI bank-wide setup + OpnBankSetup m_insBankSetup; + public: - static const opnInstMeta2 emptyInstrument; - enum { PercussionTag = 1 << 15 }; + //! Blank instrument template + static const opnInstMeta2 m_emptyInstrument; //! Total number of running concurrent emulated chips - unsigned int NumCards; + uint32_t m_numChips; //! Carriers-only are scaled by default by volume level. This flag will tell to scale modulators too. - bool ScaleModulators; + bool m_scaleModulators; //! Run emulator at PCM rate if that possible. Reduces sounding accuracy, but decreases CPU usage on lower rates. - bool runAtPcmRate; + bool m_runAtPcmRate; + //! Enable soft panning + bool m_softPanning; - char ___padding2[3]; + //! Just a padding. Reserved. + char _padding2[3]; + /** + * @brief Music playing mode + */ enum MusicMode { + //! MIDI mode MODE_MIDI, - //MODE_IMF, OPN2 chip is not able to interpret OPL's except of a creepy and ugly conversion :-P - //MODE_CMF, CMF also is not supported :-P + //! Id-Software Music mode + MODE_IMF, + //! Creative Music Files mode + MODE_CMF, + //! EA-MUS (a.k.a. RSXX) mode MODE_RSXX } m_musicMode; + /** + * @brief Volume models enum + */ enum VolumesScale { + //! Generic volume model (linearization of logarithmic scale) VOLUME_Generic, - VOLUME_CMF, + //! OPN2 native logarithmic scale + VOLUME_NATIVE, + //! DMX volume scale logarithmic table VOLUME_DMX, + //! Apoge Sound System volume scaling model VOLUME_APOGEE, + //! Windows 9x driver volume scale table VOLUME_9X } m_volumeScale; + //! Reserved + bool m_lfoEnable; + uint8_t m_lfoFrequency; + + //! Category of the channel + /*! 1 = DAC, 0 = regular + */ + std::vector m_channelCategory; + + + /** + * @brief C.O. Constructor + */ OPN2(); + + /** + * @brief C.O. Destructor + */ ~OPN2(); - char ____padding3[8]; - std::vector four_op_category; // 1 = quad-master, 2 = quad-slave, 0 = regular - // 3 = percussion BassDrum - // 4 = percussion Snare - // 5 = percussion Tom - // 6 = percussion Crash cymbal - // 7 = percussion Hihat - // 8 = percussion slave - void PokeO(size_t card, uint8_t port, uint8_t index, uint8_t value); + /** + * @brief Checks are setup locked to be changed on the fly or not + * @return true when setup on the fly is locked + */ + bool setupLocked(); - void NoteOff(size_t c); - void NoteOn(unsigned c, double hertz); - void Touch_Real(unsigned c, unsigned volume, uint8_t brightness = 127); + /** + * @brief Write data to OPN2 chip register + * @param chip Index of emulated chip. In hardware OPN2 builds, this parameter is ignored + * @param port Port of the chip to write + * @param index Register address to write + * @param value Value to write + */ + void writeReg(size_t chip, uint8_t port, uint8_t index, uint8_t value); - void Patch(uint16_t c, const opnInstData &adli); - void Pan(unsigned c, unsigned value); - void Silence(); - void ChangeVolumeRangesModel(OPNMIDI_VolumeModels volumeModel); - void ClearChips(); - void Reset(int emulator, unsigned long PCM_RATE); + /** + * @brief Write data to OPN2 chip register + * @param chip Index of emulated chip. In hardware OPN2 builds, this parameter is ignored + * @param port Port of the chip to write + * @param index Register address to write + * @param value Value to write + */ + void writeRegI(size_t chip, uint8_t port, uint32_t index, uint32_t value); + + /** + * @brief Write to soft panning control of OPN2 chip emulator + * @param chip Index of emulated chip. + * @param address Register of channel to write + * @param value Value to write + */ + void writePan(size_t chip, uint32_t index, uint32_t value); + + /** + * @brief Off the note in specified chip channel + * @param c Channel of chip (Emulated chip choosing by next formula: [c = ch + (chipId * 23)]) + */ + void noteOff(size_t c); + + /** + * @brief On the note in specified chip channel with specified frequency of the tone + * @param c Channel of chip (Emulated chip choosing by next formula: [c = ch + (chipId * 23)]) + * @param hertz Frequency of the tone in hertzes + */ + void noteOn(size_t c, double hertz); + + /** + * @brief Change setup of instrument in specified chip channel + * @param c Channel of chip (Emulated chip choosing by next formula: [c = ch + (chipId * 23)]) + * @param volume Volume level (from 0 to 127) + * @param brightness CC74 Brightness level (from 0 to 127) + */ + void touchNote(size_t c, uint8_t volume, uint8_t brightness = 127); + + /** + * @brief Set the instrument into specified chip channel + * @param c Channel of chip (Emulated chip choosing by next formula: [c = ch + (chipId * 23)]) + * @param instrument Instrument data to set into the chip channel + */ + void setPatch(size_t c, const opnInstData &instrument); + + /** + * @brief Set panpot position + * @param c Channel of chip (Emulated chip choosing by next formula: [c = ch + (chipId * 23)]) + * @param value 3-bit panpot value + */ + void setPan(size_t c, uint8_t value); + + /** + * @brief Shut up all chip channels + */ + void silenceAll(); + + /** + * @brief commit LFO enable and frequency + */ + void commitLFOSetup(); + + /** + * @brief Set the volume scaling model + * @param volumeModel Type of volume scale model scale + */ + void setVolumeScaleModel(OPNMIDI_VolumeModels volumeModel); + + /** + * @brief Get the volume scaling model + */ + OPNMIDI_VolumeModels getVolumeScaleModel(); + + /** + * @brief Clean up all running emulated chip instances + */ + void clearChips(); + + /** + * @brief Reset chip properties and initialize them + * @param emulator Type of chip emulator + * @param PCM_RATE Output sample rate to generate on output + * @param audioTickHandler PCM-accurate clock hook + */ + void reset(int emulator, unsigned long PCM_RATE, void *audioTickHandler); }; @@ -245,17 +401,11 @@ public: struct MIDIEventHooks { MIDIEventHooks() : - onEvent(NULL), - onEvent_userData(NULL), onNote(NULL), onNote_userData(NULL), onDebugMessage(NULL), onDebugMessage_userData(NULL) {} - //! Raw MIDI event hook - typedef void (*RawEventHook)(void *userdata, uint8_t type, uint8_t subtype, uint8_t channel, const uint8_t *data, size_t len); - RawEventHook onEvent; - void *onEvent_userData; //! Note on/off hooks typedef void (*NoteHook)(void *userdata, int adlchn, int note, int ins, int pressure, double bend); @@ -273,208 +423,121 @@ class OPNMIDIplay { friend void opn2_reset(struct OPN2_MIDIPlayer*); public: - OPNMIDIplay(unsigned long sampleRate = 22050); + explicit OPNMIDIplay(unsigned long sampleRate = 22050); ~OPNMIDIplay() {} void applySetup(); + void partialReset(); + void resetMIDI(); + /**********************Internal structures and classes**********************/ /** - * @brief A little class gives able to read filedata from disk and also from a memory segment + * @brief Persistent settings for each MIDI channel */ - class fileReader - { - public: - enum relTo - { - SET = 0, - CUR = 1, - END = 2 - }; - - fileReader() - { - fp = NULL; - mp = NULL; - mp_size = 0; - mp_tell = 0; - } - ~fileReader() - { - close(); - } - - void openFile(const char *path) - { - #ifndef _WIN32 - fp = std::fopen(path, "rb"); - #else - wchar_t widePath[MAX_PATH]; - int size = MultiByteToWideChar(CP_UTF8, 0, path, (int)std::strlen(path), widePath, MAX_PATH); - widePath[size] = '\0'; - fp = _wfopen(widePath, L"rb"); - #endif - _fileName = path; - mp = NULL; - mp_size = 0; - mp_tell = 0; - } - - void openData(const void *mem, size_t lenght) - { - fp = NULL; - mp = mem; - mp_size = lenght; - mp_tell = 0; - } - - void seek(long pos, int rel_to) - { - if(fp) - std::fseek(fp, pos, rel_to); - else - { - switch(rel_to) - { - case SET: - mp_tell = static_cast(pos); - break; - - case END: - mp_tell = mp_size - static_cast(pos); - break; - - case CUR: - mp_tell = mp_tell + static_cast(pos); - break; - } - - if(mp_tell > mp_size) - mp_tell = mp_size; - } - } - - inline void seeku(uint64_t pos, int rel_to) - { - seek(static_cast(pos), rel_to); - } - - size_t read(void *buf, size_t num, size_t size) - { - if(fp) - return std::fread(buf, num, size, fp); - else - { - size_t pos = 0; - size_t maxSize = static_cast(size * num); - - while((pos < maxSize) && (mp_tell < mp_size)) - { - reinterpret_cast(buf)[pos] = reinterpret_cast(mp)[mp_tell]; - mp_tell++; - pos++; - } - - return pos; - } - } - - int getc() - { - if(fp) - return std::getc(fp); - else - { - if(mp_tell >= mp_size) return -1; - int x = reinterpret_cast(mp)[mp_tell]; - mp_tell++; - return x; - } - } - - size_t tell() - { - if(fp) - return static_cast(std::ftell(fp)); - else - return mp_tell; - } - - void close() - { - if(fp) std::fclose(fp); - - fp = NULL; - mp = NULL; - mp_size = 0; - mp_tell = 0; - } - - bool isValid() - { - return (fp) || (mp); - } - - bool eof() - { - if(fp) - return (std::feof(fp) != 0); - else - return mp_tell >= mp_size; - } - std::string _fileName; - std::FILE *fp; - const void *mp; - size_t mp_size; - size_t mp_tell; - }; - - // Persistent settings for each MIDI channel struct MIDIchannel { - uint16_t portamento; - uint8_t bank_lsb, bank_msb; + //! LSB Bank number + uint8_t bank_lsb, + //! MSB Bank number + bank_msb; + //! Current patch number uint8_t patch; - uint8_t volume, expression; - uint8_t panning, vibrato, aftertouch, sustain; + //! Volume level + uint8_t volume, + //! Expression level + expression; + //! Panning level + uint8_t panning, + //! Vibrato level + vibrato, + //! Channel aftertouch level + aftertouch; + //! Portamento time + uint16_t portamento; + //! Is Pedal sustain active + bool sustain; + //! Is Soft pedal active + bool softPedal; + //! Is portamento enabled + bool portamentoEnable; + //! Source note number used by portamento + int8_t portamentoSource; // note number or -1 + //! Portamento rate + double portamentoRate; //! Per note Aftertouch values uint8_t noteAftertouch[128]; //! Is note aftertouch has any non-zero value bool noteAfterTouchInUse; - char ____padding[6]; + //! Reserved + char _padding[6]; + //! Pitch bend value int bend; + //! Pitch bend sensitivity double bendsense; - int bendsense_lsb, bendsense_msb; - double vibpos, vibspeed, vibdepth; - int64_t vibdelay; - uint8_t lastlrpn, lastmrpn; + //! Pitch bend sensitivity LSB value + int bendsense_lsb, + //! Pitch bend sensitivity MSB value + bendsense_msb; + //! Vibrato position value + double vibpos, + //! Vibrato speed value + vibspeed, + //! Vibrato depth value + vibdepth; + //! Vibrato delay time + int64_t vibdelay_us; + //! Last LSB part of RPN value received + uint8_t lastlrpn, + //! Last MSB poart of RPN value received + lastmrpn; + //! Interpret RPN value as NRPN bool nrpn; + //! Brightness level uint8_t brightness; + + //! Is melodic channel turned into percussion bool is_xg_percussion; + + /** + * @brief Per-Note information + */ struct NoteInfo { + //! Note number uint8_t note; + //! Is note active bool active; - // Current pressure + //! Current pressure uint8_t vol; - // Note vibrato (a part of Note Aftertouch feature) + //! Note vibrato (a part of Note Aftertouch feature) uint8_t vibrato; - char ____padding[1]; - // Tone selected on noteon: - int16_t tone; - char ____padding2[10]; - // Patch selected on noteon; index to banks[AdlBank][] + //! Tone selected on noteon: + int16_t noteTone; + //! Current tone (!= noteTone if gliding note) + double currentTone; + //! Gliding rate + double glideRate; + //! Patch selected on noteon; index to bank.ins[] size_t midiins; - // Patch selected + //! Is note the percussion instrument + bool isPercussion; + //! Note that plays missing instrument. Doesn't using any chip channels + bool isBlank; + //! Patch selected const opnInstMeta2 *ains; enum { MaxNumPhysChans = 2, MaxNumPhysItemCount = MaxNumPhysChans, }; + + /** + * @brief Reference to currently using chip channel + */ struct Phys { //! Destination chip channel @@ -495,11 +558,12 @@ public: return !operator==(oth); } }; - // List of OPN2 channels it is currently occupying. + + //! List of OPN2 channels it is currently occupying. Phys chip_channels[MaxNumPhysItemCount]; //! Count of used channels. unsigned chip_channels_count; - // + Phys *phys_find(unsigned chip_chan) { Phys *ph = NULL; @@ -508,18 +572,18 @@ public: ph = &chip_channels[i]; return ph; } - Phys *phys_find_or_create(unsigned chip_chan) + Phys *phys_find_or_create(uint16_t chip_chan) { Phys *ph = phys_find(chip_chan); if(!ph) { if(chip_channels_count < MaxNumPhysItemCount) { ph = &chip_channels[chip_channels_count++]; - ph->chip_chan = (uint16_t)chip_chan; + ph->chip_chan = chip_chan; } } return ph; } - Phys *phys_ensure_find_or_create(unsigned chip_chan) + Phys *phys_ensure_find_or_create(uint16_t chip_chan) { Phys *ph = phys_find_or_create(chip_chan); assert(ph); @@ -540,7 +604,13 @@ public: phys_erase_at(ph); } }; - char ____padding2[5]; + + //! Reserved + char _padding2[5]; + //! Count of gliding notes in this channel + unsigned gliding_note_count; + + //! Active notes in the channel NoteInfo activenotes[128]; struct activenoteiterator @@ -624,6 +694,9 @@ public: } } + /** + * @brief Reset channel into initial state + */ void reset() { resetAllControllers(); @@ -636,6 +709,10 @@ public: nrpn = false; is_xg_percussion = false; } + + /** + * @brief Reset all MIDI controllers into initial state + */ void resetAllControllers() { bend = 0; @@ -644,35 +721,52 @@ public: updateBendSensitivity(); volume = 100; expression = 127; - sustain = 0; + sustain = false; + softPedal = false; vibrato = 0; aftertouch = 0; std::memset(noteAftertouch, 0, 128); noteAfterTouchInUse = false; vibspeed = 2 * 3.141592653 * 5.0; vibdepth = 0.5 / 127; - vibdelay = 0; - panning = OPN_PANNING_BOTH; + vibdelay_us = 0; + panning = 64; portamento = 0; + portamentoEnable = false; + portamentoSource = -1; + portamentoRate = HUGE_VAL; brightness = 127; } + + /** + * @brief Has channel vibrato to process + * @return + */ bool hasVibrato() { return (vibrato > 0) || (aftertouch > 0) || noteAfterTouchInUse; } + + /** + * @brief Commit pitch bend sensitivity value from MSB and LSB + */ void updateBendSensitivity() { int cent = bendsense_msb * 128 + bendsense_lsb; bendsense = cent * (1.0 / (128 * 8192)); } + MIDIchannel() { activenotes_clear(); + gliding_note_count = 0; reset(); } }; - // Additional information about OPN channels + /** + * @brief Additional information about OPN2 channels + */ struct OpnChannel { struct Location @@ -683,24 +777,33 @@ public: { return MidCh == l.MidCh && note == l.note; } bool operator!=(const Location &l) const { return !operator==(l); } - char ____padding[1]; + char _padding[1]; }; struct LocationData { LocationData *prev, *next; Location loc; - bool sustained; - char ____padding[3]; + enum { + Sustain_None = 0x00, + Sustain_Pedal = 0x01, + Sustain_Sostenuto = 0x02, + Sustain_ANY = Sustain_Pedal | Sustain_Sostenuto, + }; + uint32_t sustained; + char _padding[3]; MIDIchannel::NoteInfo::Phys ins; // a copy of that in phys[] //! Has fixed sustain, don't iterate "on" timeout bool fixed_sustain; //! Timeout until note will be allowed to be killed by channel manager while it is on - int64_t kon_time_until_neglible; - int64_t vibdelay; + int64_t kon_time_until_neglible_us; + int64_t vibdelay_us; }; - // If the channel is keyoff'd - int64_t koff_time_until_neglible; + //! Time left until sounding will be muted after key off + int64_t koff_time_until_neglible_us; + + //! Recently passed instrument, improves a goodness of released but busy channel when matching + MIDIchannel::NoteInfo::Phys recent_ins; enum { users_max = 128 }; LocationData *users_first, *users_free_cells; @@ -717,12 +820,13 @@ public: void users_assign(const LocationData *users, size_t count); // For channel allocation: - OpnChannel(): koff_time_until_neglible(0) + OpnChannel(): koff_time_until_neglible_us(0) { users_clear(); + std::memset(&recent_ins, 0, sizeof(MIDIchannel::NoteInfo::Phys)); } - OpnChannel(const OpnChannel &oth): koff_time_until_neglible(oth.koff_time_until_neglible) + OpnChannel(const OpnChannel &oth): koff_time_until_neglible_us(oth.koff_time_until_neglible_us) { if(oth.users_first) { @@ -735,139 +839,33 @@ public: OpnChannel &operator=(const OpnChannel &oth) { - koff_time_until_neglible = oth.koff_time_until_neglible; + koff_time_until_neglible_us = oth.koff_time_until_neglible_us; users_assign(oth.users_first, oth.users_size); return *this; } - void AddAge(int64_t ms); + /** + * @brief Increases age of active note in microseconds time + * @param us Amount time in microseconds + */ + void addAge(int64_t us); }; #ifndef OPNMIDI_DISABLE_MIDI_SEQUENCER /** - * @brief MIDI Event utility container + * @brief MIDI files player sequencer */ - class MidiEvent - { - public: - MidiEvent(); - - enum Types - { - T_UNKNOWN = 0x00, - T_NOTEOFF = 0x08,//size == 2 - T_NOTEON = 0x09,//size == 2 - T_NOTETOUCH = 0x0A,//size == 2 - T_CTRLCHANGE = 0x0B,//size == 2 - T_PATCHCHANGE = 0x0C,//size == 1 - T_CHANAFTTOUCH = 0x0D,//size == 1 - T_WHEEL = 0x0E,//size == 2 - - T_SYSEX = 0xF0,//size == len - T_SYSCOMSPOSPTR = 0xF2,//size == 2 - T_SYSCOMSNGSEL = 0xF3,//size == 1 - T_SYSEX2 = 0xF7,//size == len - T_SPECIAL = 0xFF - }; - enum SubTypes - { - ST_SEQNUMBER = 0x00,//size == 2 - ST_TEXT = 0x01,//size == len - ST_COPYRIGHT = 0x02,//size == len - ST_SQTRKTITLE = 0x03,//size == len - ST_INSTRTITLE = 0x04,//size == len - ST_LYRICS = 0x05,//size == len - ST_MARKER = 0x06,//size == len - ST_CUEPOINT = 0x07,//size == len - ST_DEVICESWITCH = 0x09,//size == len - ST_MIDICHPREFIX = 0x20,//size == 1 - - ST_ENDTRACK = 0x2F,//size == 0 - ST_TEMPOCHANGE = 0x51,//size == 3 - ST_SMPTEOFFSET = 0x54,//size == 5 - ST_TIMESIGNATURE = 0x55, //size == 4 - ST_KEYSIGNATURE = 0x59,//size == 2 - ST_SEQUENCERSPEC = 0x7F, //size == len - - /* Non-standard, internal ADLMIDI usage only */ - ST_LOOPSTART = 0xE1,//size == 0 - ST_LOOPEND = 0xE2,//size == 0 - ST_RAWOPL = 0xE3//size == 0 - }; - //! Main type of event - uint8_t type; - //! Sub-type of the event - uint8_t subtype; - //! Targeted MIDI channel - uint8_t channel; - //! Is valid event - uint8_t isValid; - //! Reserved 5 bytes padding - uint8_t __padding[4]; - //! Absolute tick position (Used for the tempo calculation only) - uint64_t absPosition; - //! Raw data of this event - std::vector data; - }; + MidiSequencer m_sequencer; /** - * @brief A track position event contains a chain of MIDI events until next delay value - * - * Created with purpose to sort events by type in the same position - * (for example, to keep controllers always first than note on events or lower than note-off events) + * @brief Interface between MIDI sequencer and this library */ - class MidiTrackRow - { - public: - MidiTrackRow(); - void reset(); - //! Absolute time position in seconds - double time; - //! Delay to next event in ticks - uint64_t delay; - //! Absolute position in ticks - uint64_t absPos; - //! Delay to next event in seconds - double timeDelay; - std::vector events; - /** - * @brief Sort events in this position - */ - void sortEvents(bool *noteStates = NULL); - }; + BW_MidiRtInterface m_sequencerInterface; /** - * @brief Tempo change point entry. Used in the MIDI data building function only. + * @brief Initialize MIDI sequencer interface */ - struct TempoChangePoint - { - uint64_t absPos; - fraction tempo; - }; - //P.S. I declared it here instead of local in-function because C++99 can't process templates with locally-declared structures - - typedef std::list MidiTrackQueue; - - // Information about each track - struct PositionNew - { - bool began; - char padding[7]; - double wait; - double absTimePosition; - struct TrackInfo - { - size_t ptr; - uint64_t delay; - int status; - char padding2[4]; - MidiTrackQueue::iterator pos; - TrackInfo(): ptr(0), delay(0), status(0) {} - }; - std::vector track; - PositionNew(): began(false), wait(0.0), absTimePosition(0.0), track() - {} - }; + void initSequencerInterface(); #endif //OPNMIDI_DISABLE_MIDI_SEQUENCER struct Setup @@ -875,11 +873,12 @@ public: int emulator; bool runAtPcmRate; unsigned int OpnBank; - unsigned int NumCards; + unsigned int numChips; unsigned int LogarithmicVolumes; int VolumeModel; + int lfoEnable; + int lfoFrequency; //unsigned int SkipForward; - bool loopingIsEnabled; int ScaleModulators; bool fullRangeBrightnessCC74; @@ -898,127 +897,134 @@ public: unsigned long PCM_RATE; }; + /** + * @brief MIDI Marker entry + */ struct MIDI_MarkerEntry { + //! Label of marker std::string label; + //! Absolute position in seconds double pos_time; + //! Absolute position in ticks in the track uint64_t pos_ticks; }; - std::vector Ch; - //bool cmf_percussion_mode; + //! Available MIDI Channels + std::vector m_midiChannels; + //! Master volume, controlled via SysEx + uint8_t m_masterVolume; + + //! SysEx device ID + uint8_t m_sysExDeviceId; + + /** + * @brief MIDI Synthesizer mode + */ + enum SynthMode + { + Mode_GM = 0x00, + Mode_GS = 0x01, + Mode_XG = 0x02, + Mode_GM2 = 0x04, + }; + //! MIDI Synthesizer mode + uint32_t m_synthMode; + + //! Installed function hooks MIDIEventHooks hooks; private: - std::map devices; - std::map current_device; + //! Per-track MIDI devices map + std::map m_midiDevices; + //! Current MIDI device per track + std::map m_currentMidiDevice; - std::vector ch; + //! Chip channels map + std::vector m_chipChannels; //! Counter of arpeggio processing size_t m_arpeggioCounter; -#ifndef OPNMIDI_DISABLE_MIDI_SEQUENCER - std::vector > TrackData; - - PositionNew CurrentPositionNew, LoopBeginPositionNew, trackBeginPositionNew; - - //! Full song length in seconds - double fullSongTimeLength; - //! Delay after song playd before rejecting the output stream requests - double postSongWaitDelay; - - //! Loop start time - double loopStartTime; - //! Loop end time - double loopEndTime; +#if defined(ADLMIDI_AUDIO_TICK_HANDLER) + //! Audio tick counter + uint32_t m_audioTickCounter; #endif - //! Local error string - std::string errorString; + //! Local error string std::string errorStringOut; -#ifndef OPNMIDI_DISABLE_MIDI_SEQUENCER - //! Pre-processed track data storage - std::vector trackDataNew; -#endif - //! Missing instruments catches - std::set caugh_missing_instruments; + std::set caugh_missing_instruments; //! Missing melodic banks catches - std::set caugh_missing_banks_melodic; + std::set caugh_missing_banks_melodic; //! Missing percussion banks catches - std::set caugh_missing_banks_percussion; - -#ifndef OPNMIDI_DISABLE_MIDI_SEQUENCER - /** - * @brief Build MIDI track data from the raw track data storage - * @return true if everything successfully processed, or false on any error - */ - bool buildTrackData(); - - /** - * @brief Parse one event from raw MIDI track stream - * @param [_inout] ptr pointer to pointer to current position on the raw data track - * @param [_in] end address to end of raw track data, needed to validate position and size - * @param [_inout] status status of the track processing - * @return Parsed MIDI event entry - */ - MidiEvent parseEvent(uint8_t **ptr, uint8_t *end, int &status); -#endif + std::set caugh_missing_banks_percussion; public: const std::string &getErrorString(); void setErrorString(const std::string &err); -#ifndef OPNMIDI_DISABLE_MIDI_SEQUENCER - std::string musTitle; - std::string musCopyright; - std::vector musTrackTitles; - std::vector musMarkers; + //! OPN2 Chip manager + OPN2 m_synth; - fraction InvDeltaTicks, Tempo; - //! Tempo multiplier - double tempoMultiplier; - bool atEnd, - loopStart, - loopEnd, - invalidLoop; /*Loop points are invalid (loopStart after loopEnd or loopStart and loopEnd are on same place)*/ - char ____padding2[2]; -#endif - OPN2 opn; - - int32_t outBuf[1024]; + //! Generator output buffer + int32_t m_outBuf[1024]; + //! Synthesizer setup Setup m_setup; - static uint64_t ReadBEint(const void *buffer, size_t nbytes); - static uint64_t ReadLEint(const void *buffer, size_t nbytes); - /** - * @brief Standard MIDI Variable-Length numeric value parser without of validation - * @param [_inout] ptr Pointer to memory block that contains begin of variable-length value - * @return Unsigned integer that conains parsed variable-length value + * @brief Load bank from file + * @param filename Path to bank file + * @return true on succes */ - uint64_t ReadVarLen(uint8_t **ptr); - /** - * @brief Secure Standard MIDI Variable-Length numeric value parser with anti-out-of-range protection - * @param [_inout] ptr Pointer to memory block that contains begin of variable-length value, will be iterated forward - * @param [_in end Pointer to end of memory block where variable-length value is stored (after end of track) - * @param [_out] ok Reference to boolean which takes result of variable-length value parsing - * @return Unsigned integer that conains parsed variable-length value - */ - uint64_t ReadVarLenEx(uint8_t **ptr, uint8_t *end, bool &ok); - bool LoadBank(const std::string &filename); + + /** + * @brief Load bank from memory block + * @param data Pointer to memory block where raw bank file is stored + * @param size Size of given memory block + * @return true on succes + */ bool LoadBank(const void *data, size_t size); - bool LoadBank(fileReader &fr); + + /** + * @brief Load bank from opened FileAndMemReader class + * @param fr Instance with opened file + * @return true on succes + */ + bool LoadBank(FileAndMemReader &fr); #ifndef OPNMIDI_DISABLE_MIDI_SEQUENCER + /** + * @brief MIDI file loading pre-process + * @return true on success, false on failure + */ + bool LoadMIDI_pre(); + + /** + * @brief MIDI file loading post-process + * @return true on success, false on failure + */ + bool LoadMIDI_post(); + + /** + * @brief Load music file from a file + * @param filename Path to music file + * @return true on success, false on failure + */ + bool LoadMIDI(const std::string &filename); + + /** + * @brief Load music file from the memory block + * @param data pointer to the memory block + * @param size size of memory block + * @return true on success, false on failure + */ bool LoadMIDI(const void *data, size_t size); - bool LoadMIDI(fileReader &fr); /** * @brief Periodic tick handler. @@ -1027,80 +1033,205 @@ public: * @return desired number of seconds until next call */ double Tick(double s, double granularity); -#endif +#endif //OPNMIDI_DISABLE_MIDI_SEQUENCER /** * @brief Process extra iterators like vibrato or arpeggio * @param s seconds since last call */ - void TickIteratos(double s); + void TickIterators(double s); -#ifndef OPNMIDI_DISABLE_MIDI_SEQUENCER - /** - * @brief Change current position to specified time position in seconds - * @param seconds Absolute time position in seconds - */ - void seek(double seconds); - - /** - * @brief Gives current time position in seconds - * @return Current time position in seconds - */ - double tell(); - - /** - * @brief Gives time length of current song in seconds - * @return Time length of current song in seconds - */ - double timeLength(); - - /** - * @brief Gives loop start time position in seconds - * @return Loop start time position in seconds or -1 if song has no loop points - */ - double getLoopStart(); - - /** - * @brief Gives loop end time position in seconds - * @return Loop end time position in seconds or -1 if song has no loop points - */ - double getLoopEnd(); - - /** - * @brief Return to begin of current song - */ - void rewind(); - - /** - * @brief Set tempo multiplier - * @param tempo Tempo multiplier: 1.0 - original tempo. >1 - faster, <1 - slower - */ - void setTempo(double tempo); -#endif /* RealTime event triggers */ + /** + * @brief Reset state of all channels + */ void realTime_ResetState(); + /** + * @brief Note On event + * @param channel MIDI channel + * @param note Note key (from 0 to 127) + * @param velocity Velocity level (from 0 to 127) + * @return true if Note On event was accepted + */ bool realTime_NoteOn(uint8_t channel, uint8_t note, uint8_t velocity); + + /** + * @brief Note Off event + * @param channel MIDI channel + * @param note Note key (from 0 to 127) + */ void realTime_NoteOff(uint8_t channel, uint8_t note); + /** + * @brief Note aftertouch event + * @param channel MIDI channel + * @param note Note key (from 0 to 127) + * @param atVal After-Touch level (from 0 to 127) + */ void realTime_NoteAfterTouch(uint8_t channel, uint8_t note, uint8_t atVal); + + /** + * @brief Channel aftertouch event + * @param channel MIDI channel + * @param atVal After-Touch level (from 0 to 127) + */ void realTime_ChannelAfterTouch(uint8_t channel, uint8_t atVal); + /** + * @brief Controller Change event + * @param channel MIDI channel + * @param type Type of controller + * @param value Value of the controller (from 0 to 127) + */ void realTime_Controller(uint8_t channel, uint8_t type, uint8_t value); + /** + * @brief Patch change + * @param channel MIDI channel + * @param patch Patch Number (from 0 to 127) + */ void realTime_PatchChange(uint8_t channel, uint8_t patch); + /** + * @brief Pitch bend change + * @param channel MIDI channel + * @param pitch Concoctated raw pitch value + */ void realTime_PitchBend(uint8_t channel, uint16_t pitch); + + /** + * @brief Pitch bend change + * @param channel MIDI channel + * @param msb MSB of pitch value + * @param lsb LSB of pitch value + */ void realTime_PitchBend(uint8_t channel, uint8_t msb, uint8_t lsb); + /** + * @brief LSB Bank Change CC + * @param channel MIDI channel + * @param lsb LSB value of bank number + */ void realTime_BankChangeLSB(uint8_t channel, uint8_t lsb); + + /** + * @brief MSB Bank Change CC + * @param channel MIDI channel + * @param lsb MSB value of bank number + */ void realTime_BankChangeMSB(uint8_t channel, uint8_t msb); + + /** + * @brief Bank Change (united value) + * @param channel MIDI channel + * @param bank Bank number value + */ void realTime_BankChange(uint8_t channel, uint16_t bank); + /** + * @brief Sets the Device identifier + * @param id 7-bit Device identifier + */ + void setDeviceId(uint8_t id); + + /** + * @brief System Exclusive message + * @param msg Raw SysEx Message + * @param size Length of SysEx message + * @return true if message was passed successfully. False on any errors + */ + bool realTime_SysEx(const uint8_t *msg, size_t size); + + /** + * @brief Turn off all notes and mute the sound of releasing notes + */ void realTime_panic(); + /** + * @brief Device switch (to extend 16-channels limit of MIDI standard) + * @param track MIDI track index + * @param data Device name + * @param length Length of device name string + */ + void realTime_deviceSwitch(size_t track, const char *data, size_t length); + + /** + * @brief Currently selected device index + * @param track MIDI track index + * @return Multiple 16 value + */ + size_t realTime_currentDevice(size_t track); + +#if defined(ADLMIDI_AUDIO_TICK_HANDLER) + // Audio rate tick handler + void AudioTick(uint32_t chipId, uint32_t rate); +#endif + private: + /** + * @brief Hardware manufacturer (Used for SysEx) + */ + enum + { + Manufacturer_Roland = 0x41, + Manufacturer_Yamaha = 0x43, + Manufacturer_UniversalNonRealtime = 0x7E, + Manufacturer_UniversalRealtime = 0x7F + }; + + /** + * @brief Roland Mode (Used for SysEx) + */ + enum + { + RolandMode_Request = 0x11, + RolandMode_Send = 0x12 + }; + + /** + * @brief Device model (Used for SysEx) + */ + enum + { + RolandModel_GS = 0x42, + RolandModel_SC55 = 0x45, + YamahaModel_XG = 0x4C + }; + + /** + * @brief Process generic SysEx events + * @param dev Device ID + * @param realtime Is real-time event + * @param data Raw SysEx data + * @param size Size of given SysEx data + * @return true when event was successfully handled + */ + bool doUniversalSysEx(unsigned dev, bool realtime, const uint8_t *data, size_t size); + + /** + * @brief Process events specific to Roland devices + * @param dev Device ID + * @param data Raw SysEx data + * @param size Size of given SysEx data + * @return true when event was successfully handled + */ + bool doRolandSysEx(unsigned dev, const uint8_t *data, size_t size); + + /** + * @brief Process events specific to Yamaha devices + * @param dev Device ID + * @param data Raw SysEx data + * @param size Size of given SysEx data + * @return true when event was successfully handled + */ + bool doYamahaSysEx(unsigned dev, const uint8_t *data, size_t size); + +private: + /** + * @brief Note Update properties + */ enum { Upd_Patch = 0x1, @@ -1113,42 +1244,144 @@ private: Upd_OffMute = Upd_Off + Upd_Mute }; - void NoteUpdate(uint16_t MidCh, + /** + * @brief Update active note + * @param MidCh MIDI Channel where note is processing + * @param i Iterator that points to active note in the MIDI channel + * @param props_mask Properties to update + * @param select_adlchn Specify chip channel, or -1 - all chip channels used by the note + */ + void noteUpdate(size_t midCh, MIDIchannel::activenoteiterator i, unsigned props_mask, int32_t select_adlchn = -1); -#ifndef OPNMIDI_DISABLE_MIDI_SEQUENCER - bool ProcessEventsNew(bool isSeek = false); - void HandleEvent(size_t tk, const MidiEvent &evt, int &status); -#endif + void noteUpdateAll(size_t midCh, unsigned props_mask); - // Determine how good a candidate this adlchannel - // would be for playing a note from this instrument. - int64_t CalculateAdlChannelGoodness(size_t c, const MIDIchannel::NoteInfo::Phys &ins, uint16_t /*MidCh*/) const; + /** + * @brief Determine how good a candidate this adlchannel would be for playing a note from this instrument. + * @param c Wanted chip channel + * @param ins Instrument wanted to be used in this channel + * @return Calculated coodness points + */ + int64_t calculateChipChannelGoodness(size_t c, const MIDIchannel::NoteInfo::Phys &ins) const; - // A new note will be played on this channel using this instrument. - // Kill existing notes on this channel (or don't, if we do arpeggio) - void PrepareAdlChannelForNewNote(size_t c, const MIDIchannel::NoteInfo::Phys &ins); + /** + * @brief A new note will be played on this channel using this instrument. + * @param c Wanted chip channel + * @param ins Instrument wanted to be used in this channel + * Kill existing notes on this channel (or don't, if we do arpeggio) + */ + void prepareChipChannelForNewNote(size_t c, const MIDIchannel::NoteInfo::Phys &ins); - void KillOrEvacuate( + /** + * @brief Kills note that uses wanted channel. When arpeggio is possible, note is evaluating to another channel + * @param from_channel Wanted chip channel + * @param j Chip channel instance + * @param i MIDI Channel active note instance + */ + void killOrEvacuate( size_t from_channel, OpnChannel::LocationData *j, MIDIchannel::activenoteiterator i); - void Panic(); - void KillSustainingNotes(int32_t MidCh = -1, int32_t this_adlchn = -1); - void SetRPN(unsigned MidCh, unsigned value, bool MSB); - //void UpdatePortamento(unsigned MidCh) - void NoteUpdate_All(uint16_t MidCh, unsigned props_mask); - void NoteOff(uint16_t MidCh, uint8_t note); - void UpdateVibrato(double amount); - void UpdateArpeggio(double /*amount*/); + + /** + * @brief Off all notes and silence sound + */ + void panic(); + + /** + * @brief Kill note, sustaining by pedal or sostenuto + * @param MidCh MIDI channel, -1 - all MIDI channels + * @param this_adlchn Chip channel, -1 - all chip channels + * @param sustain_type Type of systain to process + */ + void killSustainingNotes(int32_t midCh = -1, + int32_t this_adlchn = -1, + uint32_t sustain_type = OpnChannel::LocationData::Sustain_ANY); + /** + * @brief Find active notes and mark them as sostenuto-sustained + * @param MidCh MIDI channel, -1 - all MIDI channels + */ + void markSostenutoNotes(int32_t midCh = -1); + + /** + * @brief Set RPN event value + * @param MidCh MIDI channel + * @param value 1 byte part of RPN value + * @param MSB is MSB or LSB part of value + */ + void setRPN(size_t midCh, unsigned value, bool MSB); + + /** + * @brief Update portamento setup in MIDI channel + * @param midCh MIDI channel where portamento needed to be updated + */ + void updatePortamento(size_t midCh); + + /** + * @brief Off the note + * @param midCh MIDI channel + * @param note Note to off + */ + void noteOff(size_t midCh, uint8_t note); + + /** + * @brief Update processing of vibrato to amount of seconds + * @param amount Amount value in seconds + */ + void updateVibrato(double amount); + + /** + * @brief Update auto-arpeggio + * @param amount Amount value in seconds [UNUSED] + */ + void updateArpeggio(double /*amount*/); + + /** + * @brief Update Portamento gliding to amount of seconds + * @param amount Amount value in seconds + */ + void updateGlide(double amount); public: - uint64_t ChooseDevice(const std::string &name); + /** + * @brief Checks was device name used or not + * @param name Name of MIDI device + * @return Offset of the MIDI Channels, multiple to 16 + */ + size_t chooseDevice(const std::string &name); + + /** + * @brief Gets a textual description of the state of chip channels + * @param text character pointer for text + * @param attr character pointer for text attributes + * @param size number of characters available to write + */ + void describeChannels(char *text, char *attr, size_t size); }; -extern int opn2RefreshNumCards(OPN2_MIDIPlayer *device); +#if defined(ADLMIDI_AUDIO_TICK_HANDLER) +extern void opn2_audioTickHandler(void *instance, uint32_t chipId, uint32_t rate); +#endif +/** + * @brief Check emulator availability + * @param emulator Emulator ID (Opn2_Emulator) + * @return true when emulator is available + */ +extern bool opn2_isEmulatorAvailable(int emulator); + +/** + * @brief Find highest emulator + * @return The Opn2_Emulator enum value which contains ID of highest emulator + */ +extern int opn2_getHighestEmulator(); + +/** + * @brief Find lowest emulator + * @return The Opn2_Emulator enum value which contains ID of lowest emulator + */ +extern int opn2_getLowestEmulator(); #endif // ADLMIDI_PRIVATE_HPP diff --git a/src/sound/opnmidi/wopn/wopn_file.c b/src/sound/opnmidi/wopn/wopn_file.c new file mode 100644 index 000000000..f2bea78e9 --- /dev/null +++ b/src/sound/opnmidi/wopn/wopn_file.c @@ -0,0 +1,622 @@ +/* + * Wohlstand's OPN2 Bank File - a bank format to store OPN2 timbre data and setup + * + * Copyright (c) 2018 Vitaly Novichkov + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +#include "wopn_file.h" +#include +#include + +static const char *wopn2_magic1 = "WOPN2-BANK\0"; +static const char *wopn2_magic2 = "WOPN2-B2NK\0"; +static const char *opni_magic1 = "WOPN2-INST\0"; +static const char *opni_magic2 = "WOPN2-IN2T\0"; + +static const uint16_t wopn_latest_version = 2; + +enum +{ + WOPN_INST_SIZE_V1 = 65, + WOPN_INST_SIZE_V2 = 69 +}; + +static uint16_t toUint16LE(const uint8_t *arr) +{ + uint16_t num = arr[0]; + num |= ((arr[1] << 8) & 0xFF00); + return num; +} + +static uint16_t toUint16BE(const uint8_t *arr) +{ + uint16_t num = arr[1]; + num |= ((arr[0] << 8) & 0xFF00); + return num; +} + +static int16_t toSint16BE(const uint8_t *arr) +{ + int16_t num = *(const int8_t *)(&arr[0]); + num *= 1 << 8; + num |= arr[1]; + return num; +} + +static void fromUint16LE(uint16_t in, uint8_t *arr) +{ + arr[0] = in & 0x00FF; + arr[1] = (in >> 8) & 0x00FF; +} + +static void fromUint16BE(uint16_t in, uint8_t *arr) +{ + arr[1] = in & 0x00FF; + arr[0] = (in >> 8) & 0x00FF; +} + +static void fromSint16BE(int16_t in, uint8_t *arr) +{ + arr[1] = in & 0x00FF; + arr[0] = ((uint16_t)in >> 8) & 0x00FF; +} + + +WOPNFile *WOPN_Init(uint16_t melodic_banks, uint16_t percussive_banks) +{ + WOPNFile *file = NULL; + if(melodic_banks == 0) + return NULL; + if(percussive_banks == 0) + return NULL; + file = (WOPNFile*)calloc(1, sizeof(WOPNFile)); + if(!file) + return NULL; + file->banks_count_melodic = melodic_banks; + file->banks_count_percussion = percussive_banks; + file->banks_melodic = (WOPNBank*)calloc(1, sizeof(WOPNBank) * melodic_banks ); + file->banks_percussive = (WOPNBank*)calloc(1, sizeof(WOPNBank) * percussive_banks ); + return file; +} + +void WOPN_Free(WOPNFile *file) +{ + if(file) + { + if(file->banks_melodic) + free(file->banks_melodic); + if(file->banks_percussive) + free(file->banks_percussive); + free(file); + } +} + +int WOPN_BanksCmp(const WOPNFile *bank1, const WOPNFile *bank2) +{ + int res = 1; + + res &= (bank1->version == bank2->version); + res &= (bank1->lfo_freq == bank2->lfo_freq); + res &= (bank1->volume_model == bank2->volume_model); + res &= (bank1->banks_count_melodic == bank2->banks_count_melodic); + res &= (bank1->banks_count_percussion == bank2->banks_count_percussion); + + if(res) + { + int i; + for(i = 0; i < bank1->banks_count_melodic; i++) + res &= (memcmp(&bank1->banks_melodic[i], &bank2->banks_melodic[i], sizeof(WOPNBank)) == 0); + if(res) + { + for(i = 0; i < bank1->banks_count_percussion; i++) + res &= (memcmp(&bank1->banks_percussive[i], &bank2->banks_percussive[i], sizeof(WOPNBank)) == 0); + } + } + + return res; +} + +static void WOPN_parseInstrument(WOPNInstrument *ins, uint8_t *cursor, uint16_t version, uint8_t has_sounding_delays) +{ + int l; + strncpy(ins->inst_name, (const char*)cursor, 32); + ins->inst_name[32] = '\0'; + ins->note_offset = toSint16BE(cursor + 32); + ins->midi_velocity_offset = 0; /* TODO: for future version > 2 */ + ins->percussion_key_number = cursor[34]; + ins->inst_flags = 0; /* TODO: for future version > 2 */ + ins->fbalg = cursor[35]; + ins->lfosens = cursor[36]; + for(l = 0; l < 4; l++) + { + size_t off = 37 + (size_t)(l) * 7; + ins->operators[l].dtfm_30 = cursor[off + 0]; + ins->operators[l].level_40 = cursor[off + 1]; + ins->operators[l].rsatk_50 = cursor[off + 2]; + ins->operators[l].amdecay1_60 = cursor[off + 3]; + ins->operators[l].decay2_70 = cursor[off + 4]; + ins->operators[l].susrel_80 = cursor[off + 5]; + ins->operators[l].ssgeg_90 = cursor[off + 6]; + } + if((version >= 2) && has_sounding_delays) + { + ins->delay_on_ms = toUint16BE(cursor + 65); + ins->delay_off_ms = toUint16BE(cursor + 67); + + /* Null delays indicate the blank instrument in version 2 */ + if((version < 3) && ins->delay_on_ms == 0 && ins->delay_off_ms == 0) + ins->inst_flags |= WOPN_Ins_IsBlank; + } +} + +static void WOPN_writeInstrument(WOPNInstrument *ins, uint8_t *cursor, uint16_t version, uint8_t has_sounding_delays) +{ + int l; + strncpy((char*)cursor, ins->inst_name, 32); + fromSint16BE(ins->note_offset, cursor + 32); + cursor[34] = ins->percussion_key_number; + cursor[35] = ins->fbalg; + cursor[36] = ins->lfosens; + for(l = 0; l < 4; l++) + { + size_t off = 37 + (size_t)(l) * 7; + cursor[off + 0] = ins->operators[l].dtfm_30; + cursor[off + 1] = ins->operators[l].level_40; + cursor[off + 2] = ins->operators[l].rsatk_50; + cursor[off + 3] = ins->operators[l].amdecay1_60; + cursor[off + 4] = ins->operators[l].decay2_70; + cursor[off + 5] = ins->operators[l].susrel_80; + cursor[off + 6] = ins->operators[l].ssgeg_90; + } + if((version >= 2) && has_sounding_delays) + { + if((version < 3) && (ins->inst_flags & WOPN_Ins_IsBlank) != 0) + { + /* Null delays indicate the blank instrument in version 2 */ + fromUint16BE(0, cursor + 65); + fromUint16BE(0, cursor + 67); + } + else + { + fromUint16BE(ins->delay_on_ms, cursor + 65); + fromUint16BE(ins->delay_off_ms, cursor + 67); + } + } +} + +WOPNFile *WOPN_LoadBankFromMem(void *mem, size_t length, int *error) +{ + WOPNFile *outFile = NULL; + uint16_t i = 0, j = 0, k = 0; + uint16_t version = 0; + uint16_t count_melodic_banks = 1; + uint16_t count_percussive_banks = 1; + uint8_t *cursor = (uint8_t *)mem; + + WOPNBank *bankslots[2]; + uint16_t bankslots_sizes[2]; + +#define SET_ERROR(err) \ +{\ + WOPN_Free(outFile);\ + if(error)\ + {\ + *error = err;\ + }\ +} + +#define GO_FORWARD(bytes) { cursor += bytes; length -= bytes; } + + if(!cursor) + { + SET_ERROR(WOPN_ERR_NULL_POINTER); + return NULL; + } + + {/* Magic number */ + if(length < 11) + { + SET_ERROR(WOPN_ERR_UNEXPECTED_ENDING); + return NULL; + } + if(memcmp(cursor, wopn2_magic1, 11) == 0) + { + version = 1; + } + else if(memcmp(cursor, wopn2_magic2, 11) != 0) + { + SET_ERROR(WOPN_ERR_BAD_MAGIC); + return NULL; + } + GO_FORWARD(11); + } + + if (version == 0) + {/* Version code */ + if(length < 2) + { + SET_ERROR(WOPN_ERR_UNEXPECTED_ENDING); + return NULL; + } + version = toUint16LE(cursor); + if(version > wopn_latest_version) + { + SET_ERROR(WOPN_ERR_NEWER_VERSION); + return NULL; + } + GO_FORWARD(2); + } + + {/* Header of WOPN */ + uint8_t head[5]; + if(length < 5) + { + SET_ERROR(WOPN_ERR_UNEXPECTED_ENDING); + return NULL; + } + memcpy(head, cursor, 5); + count_melodic_banks = toUint16BE(head); + count_percussive_banks = toUint16BE(head + 2); + GO_FORWARD(5); + + outFile = WOPN_Init(count_melodic_banks, count_percussive_banks); + if(!outFile) + { + SET_ERROR(WOPN_ERR_OUT_OF_MEMORY); + return NULL; + } + + outFile->version = version; + outFile->lfo_freq = head[4]; + outFile->volume_model = 0; + } + + bankslots_sizes[0] = count_melodic_banks; + bankslots[0] = outFile->banks_melodic; + bankslots_sizes[1] = count_percussive_banks; + bankslots[1] = outFile->banks_percussive; + + if(version >= 2) /* Bank names and LSB/MSB titles */ + { + for(i = 0; i < 2; i++) + { + for(j = 0; j < bankslots_sizes[i]; j++) + { + if(length < 34) + { + SET_ERROR(WOPN_ERR_UNEXPECTED_ENDING); + return NULL; + } + strncpy(bankslots[i][j].bank_name, (const char*)cursor, 32); + bankslots[i][j].bank_name[32] = '\0'; + bankslots[i][j].bank_midi_lsb = cursor[32]; + bankslots[i][j].bank_midi_msb = cursor[33]; + GO_FORWARD(34); + } + } + } + + {/* Read instruments data */ + uint16_t insSize = 0; + if(version > 1) + insSize = WOPN_INST_SIZE_V2; + else + insSize = WOPN_INST_SIZE_V1; + for(i = 0; i < 2; i++) + { + if(length < (insSize * 128) * (size_t)bankslots_sizes[i]) + { + SET_ERROR(WOPN_ERR_UNEXPECTED_ENDING); + return NULL; + } + + for(j = 0; j < bankslots_sizes[i]; j++) + { + for(k = 0; k < 128; k++) + { + WOPNInstrument *ins = &bankslots[i][j].ins[k]; + WOPN_parseInstrument(ins, cursor, version, 1); + GO_FORWARD(insSize); + } + } + } + } + +#undef GO_FORWARD +#undef SET_ERROR + + return outFile; +} + +int WOPN_LoadInstFromMem(OPNIFile *file, void *mem, size_t length) +{ + uint16_t version = 0; + uint8_t *cursor = (uint8_t *)mem; + uint16_t ins_size; + + if(!cursor) + return WOPN_ERR_NULL_POINTER; + +#define GO_FORWARD(bytes) { cursor += bytes; length -= bytes; } + + {/* Magic number */ + if(length < 11) + return WOPN_ERR_UNEXPECTED_ENDING; + if(memcmp(cursor, opni_magic1, 11) == 0) + version = 1; + else if(memcmp(cursor, opni_magic2, 11) != 0) + return WOPN_ERR_BAD_MAGIC; + GO_FORWARD(11); + } + + if (version == 0) {/* Version code */ + if(length < 2) + return WOPN_ERR_UNEXPECTED_ENDING; + version = toUint16LE(cursor); + if(version > wopn_latest_version) + return WOPN_ERR_NEWER_VERSION; + GO_FORWARD(2); + } + + file->version = version; + + {/* is drum flag */ + if(length < 1) + return WOPN_ERR_UNEXPECTED_ENDING; + file->is_drum = *cursor; + GO_FORWARD(1); + } + + if(version > 1) + /* Skip sounding delays are not part of single-instrument file + * two sizes of uint16_t will be subtracted */ + ins_size = WOPN_INST_SIZE_V2 - (sizeof(uint16_t) * 2); + else + ins_size = WOPN_INST_SIZE_V1; + + if(length < ins_size) + return WOPN_ERR_UNEXPECTED_ENDING; + + WOPN_parseInstrument(&file->inst, cursor, version, 0); + GO_FORWARD(ins_size); + + return WOPN_ERR_OK; +#undef GO_FORWARD +} + +size_t WOPN_CalculateBankFileSize(WOPNFile *file, uint16_t version) +{ + size_t final_size = 0; + size_t ins_size = 0; + + if(version == 0) + version = wopn_latest_version; + + if(!file) + return 0; + final_size += 11 + 2 + 2 + 2 + 1; + /* + * Magic number, + * Version, + * Count of melodic banks, + * Count of percussive banks, + * Chip specific flags + */ + + if(version >= 2) + { + /* Melodic banks meta-data */ + final_size += (32 + 1 + 1) * file->banks_count_melodic; + /* Percussive banks meta-data */ + final_size += (32 + 1 + 1) * file->banks_count_percussion; + } + + if(version >= 2) + ins_size = WOPN_INST_SIZE_V2; + else + ins_size = WOPN_INST_SIZE_V1; + /* Melodic instruments */ + final_size += (ins_size * 128) * file->banks_count_melodic; + /* Percussive instruments */ + final_size += (ins_size * 128) * file->banks_count_percussion; + + return final_size; +} + +size_t WOPN_CalculateInstFileSize(OPNIFile *file, uint16_t version) +{ + size_t final_size = 0; + size_t ins_size = 0; + + if(version == 0) + version = wopn_latest_version; + + if(!file) + return 0; + final_size += 11 + 1; + /* + * Magic number, + * is percussive instrument + */ + + /* Version */ + if (version > 1) + final_size += 2; + + if(version > 1) + /* Skip sounding delays are not part of single-instrument file + * two sizes of uint16_t will be subtracted */ + ins_size = WOPN_INST_SIZE_V2 - (sizeof(uint16_t) * 2); + else + ins_size = WOPN_INST_SIZE_V1; + final_size += ins_size; + + return final_size; +} + +int WOPN_SaveBankToMem(WOPNFile *file, void *dest_mem, size_t length, uint16_t version, uint16_t force_gm) +{ + uint8_t *cursor = (uint8_t *)dest_mem; + uint16_t ins_size = 0; + uint16_t i, j, k; + uint16_t banks_melodic = force_gm ? 1 : file->banks_count_melodic; + uint16_t banks_percussive = force_gm ? 1 : file->banks_count_percussion; + + WOPNBank *bankslots[2]; + uint16_t bankslots_sizes[2]; + + if(version == 0) + version = wopn_latest_version; + +#define GO_FORWARD(bytes) { cursor += bytes; length -= bytes; } + + if(length < 11) + return WOPN_ERR_UNEXPECTED_ENDING; + if(version > 1) + memcpy(cursor, wopn2_magic2, 11); + else + memcpy(cursor, wopn2_magic1, 11); + GO_FORWARD(11); + + if(version > 1) + { + if(length < 2) + return WOPN_ERR_UNEXPECTED_ENDING; + fromUint16LE(version, cursor); + GO_FORWARD(2); + } + + if(length < 2) + return WOPN_ERR_UNEXPECTED_ENDING; + fromUint16BE(banks_melodic, cursor); + GO_FORWARD(2); + + if(length < 2) + return WOPN_ERR_UNEXPECTED_ENDING; + fromUint16BE(banks_percussive, cursor); + GO_FORWARD(2); + + if(length < 1) + return WOPN_ERR_UNEXPECTED_ENDING; + cursor[0] = file->lfo_freq; + GO_FORWARD(1); + + bankslots[0] = file->banks_melodic; + bankslots_sizes[0] = banks_melodic; + bankslots[1] = file->banks_percussive; + bankslots_sizes[1] = banks_percussive; + + if(version >= 2) + { + for(i = 0; i < 2; i++) + { + for(j = 0; j < bankslots_sizes[i]; j++) + { + if(length < 34) + return WOPN_ERR_UNEXPECTED_ENDING; + strncpy((char*)cursor, bankslots[i][j].bank_name, 32); + cursor[32] = bankslots[i][j].bank_midi_lsb; + cursor[33] = bankslots[i][j].bank_midi_msb; + GO_FORWARD(34); + } + } + } + + {/* Write instruments data */ + if(version >= 2) + ins_size = WOPN_INST_SIZE_V2; + else + ins_size = WOPN_INST_SIZE_V1; + for(i = 0; i < 2; i++) + { + if(length < (ins_size * 128) * (size_t)bankslots_sizes[i]) + return WOPN_ERR_UNEXPECTED_ENDING; + + for(j = 0; j < bankslots_sizes[i]; j++) + { + for(k = 0; k < 128; k++) + { + WOPNInstrument *ins = &bankslots[i][j].ins[k]; + WOPN_writeInstrument(ins, cursor, version, 1); + GO_FORWARD(ins_size); + } + } + } + } + + return WOPN_ERR_OK; +#undef GO_FORWARD +} + +int WOPN_SaveInstToMem(OPNIFile *file, void *dest_mem, size_t length, uint16_t version) +{ + uint8_t *cursor = (uint8_t *)dest_mem; + uint16_t ins_size; + + if(!cursor) + return WOPN_ERR_NULL_POINTER; + + if(version == 0) + version = wopn_latest_version; + +#define GO_FORWARD(bytes) { cursor += bytes; length -= bytes; } + + {/* Magic number */ + if(length < 11) + return WOPN_ERR_UNEXPECTED_ENDING; + if(version > 1) + memcpy(cursor, opni_magic2, 11); + else + memcpy(cursor, opni_magic1, 11); + GO_FORWARD(11); + } + + if (version > 1) + {/* Version code */ + if(length < 2) + return WOPN_ERR_UNEXPECTED_ENDING; + fromUint16LE(version, cursor); + GO_FORWARD(2); + } + + {/* is drum flag */ + if(length < 1) + return WOPN_ERR_UNEXPECTED_ENDING; + *cursor = file->is_drum; + GO_FORWARD(1); + } + + if(version > 1) + /* Skip sounding delays are not part of single-instrument file + * two sizes of uint16_t will be subtracted */ + ins_size = WOPN_INST_SIZE_V2 - (sizeof(uint16_t) * 2); + else + ins_size = WOPN_INST_SIZE_V1; + + if(length < ins_size) + return WOPN_ERR_UNEXPECTED_ENDING; + + WOPN_writeInstrument(&file->inst, cursor, version, 0); + GO_FORWARD(ins_size); + + return WOPN_ERR_OK; +#undef GO_FORWARD +} diff --git a/src/sound/opnmidi/wopn/wopn_file.h b/src/sound/opnmidi/wopn/wopn_file.h new file mode 100644 index 000000000..cd0d6b4d4 --- /dev/null +++ b/src/sound/opnmidi/wopn/wopn_file.h @@ -0,0 +1,250 @@ +/* + * Wohlstand's OPN2 Bank File - a bank format to store OPN2 timbre data and setup + * + * Copyright (c) 2018 Vitaly Novichkov + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +#ifndef WOPN_FILE_H +#define WOPN_FILE_H + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#if !defined(__STDC_VERSION__) || (defined(__STDC_VERSION__) && (__STDC_VERSION__ < 199901L)) \ + || defined(__STRICT_ANSI__) || !defined(__cplusplus) +typedef signed char int8_t; +typedef unsigned char uint8_t; +typedef signed short int int16_t; +typedef unsigned short int uint16_t; +#endif + +/* Volume scaling model implemented in the libOPNMIDI */ +typedef enum WOPN_VolumeModel +{ + WOPN_VM_Generic = 0 +} WOPN_VolumeModel; + +typedef enum WOPN_InstrumentFlags +{ + /* Is pseudo eight-operator (two 4-operator voices) instrument */ + WOPN_Ins_Pseudo8op = 0x01, + /* Is a blank instrument entry */ + WOPN_Ins_IsBlank = 0x02, + + /* Mask of the flags range */ + WOPN_Ins_ALL_MASK = 0x03 +} WOPN_InstrumentFlags; + +/* Error codes */ +typedef enum WOPN_ErrorCodes +{ + WOPN_ERR_OK = 0, + /* Magic number is not maching */ + WOPN_ERR_BAD_MAGIC, + /* Too short file */ + WOPN_ERR_UNEXPECTED_ENDING, + /* Zero banks count */ + WOPN_ERR_INVALID_BANKS_COUNT, + /* Version of file is newer than supported by current version of library */ + WOPN_ERR_NEWER_VERSION, + /* Out of memory */ + WOPN_ERR_OUT_OF_MEMORY, + /* Given null pointer memory data */ + WOPN_ERR_NULL_POINTER +} WOPN_ErrorCodes; + +/* OPN2 Oerators data */ +typedef struct WOPNOperator +{ + /* Detune and frequency multiplication register data */ + uint8_t dtfm_30; + /* Total level register data */ + uint8_t level_40; + /* Rate scale and attack register data */ + uint8_t rsatk_50; + /* Amplitude modulation enable and Decay-1 register data */ + uint8_t amdecay1_60; + /* Decay-2 register data */ + uint8_t decay2_70; + /* Sustain and Release register data */ + uint8_t susrel_80; + /* SSG-EG register data */ + uint8_t ssgeg_90; +} WOPNOperator; + +/* Instrument entry */ +typedef struct WOPNInstrument +{ + /* Title of the instrument */ + char inst_name[34]; + /* MIDI note key (half-tone) offset for an instrument (or a first voice in pseudo-4-op mode) */ + int16_t note_offset; + /* Reserved */ + int8_t midi_velocity_offset; + /* Percussion MIDI base tone number at which this drum will be played */ + uint8_t percussion_key_number; + /* Enum WOPN_InstrumentFlags */ + uint8_t inst_flags; + /* Feedback and Algorithm register data */ + uint8_t fbalg; + /* LFO Sensitivity register data */ + uint8_t lfosens; + /* Operators register data */ + WOPNOperator operators[4]; + /* Millisecond delay of sounding while key is on */ + uint16_t delay_on_ms; + /* Millisecond delay of sounding after key off */ + uint16_t delay_off_ms; +} WOPNInstrument; + +/* Bank entry */ +typedef struct WOPNBank +{ + /* Name of bank */ + char bank_name[33]; + /* MIDI Bank LSB code */ + uint8_t bank_midi_lsb; + /* MIDI Bank MSB code */ + uint8_t bank_midi_msb; + /* Instruments data of this bank */ + WOPNInstrument ins[128]; +} WOPNBank; + +/* Instrument data file */ +typedef struct OPNIFile +{ + /* Version of instrument file */ + uint16_t version; + /* Is this a percussion instrument */ + uint8_t is_drum; + /* Instrument data */ + WOPNInstrument inst; +} OPNIFile; + +/* Bank data file */ +typedef struct WOPNFile +{ + /* Version of bank file */ + uint16_t version; + /* Count of melodic banks in this file */ + uint16_t banks_count_melodic; + /* Count of percussion banks in this file */ + uint16_t banks_count_percussion; + /* Chip global LFO enable flag and frequency register data */ + uint8_t lfo_freq; + /* Reserved (Enum WOPN_VolumeModel) */ + uint8_t volume_model; + /* dynamically allocated data Melodic banks array */ + WOPNBank *banks_melodic; + /* dynamically allocated data Percussive banks array */ + WOPNBank *banks_percussive; +} WOPNFile; + + +/** + * @brief Initialize blank WOPN data structure with allocated bank data + * @param melodic_banks Count of melodic banks + * @param percussive_banks Count of percussive banks + * @return pointer to heap-allocated WOPN data structure or NULL when out of memory or incorrectly given banks counts + */ +extern WOPNFile *WOPN_Init(uint16_t melodic_banks, uint16_t percussive_banks); + +/** + * @brief Clean up WOPN data file (all allocated bank arrays will be fried too) + * @param file pointer to heap-allocated WOPN data structure + */ +extern void WOPN_Free(WOPNFile *file); + +/** + * @brief Compare two bank entries + * @param bank1 First bank + * @param bank2 Second bank + * @return 1 if banks are equal or 0 if there are different + */ +extern int WOPN_BanksCmp(const WOPNFile *bank1, const WOPNFile *bank2); + + +/** + * @brief Load WOPN bank file from the memory. + * WOPN data structure will be allocated. (don't forget to clear it with WOPN_Free() after use!) + * @param mem Pointer to memory block contains raw WOPN bank file data + * @param length Length of given memory block + * @param error pointer to integer to return an error code. Pass NULL if you don't want to use error codes. + * @return Heap-allocated WOPN file data structure or NULL if any error has occouped + */ +extern WOPNFile *WOPN_LoadBankFromMem(void *mem, size_t length, int *error); + +/** + * @brief Load WOPI instrument file from the memory. + * You must allocate OPNIFile structure by yourself and give the pointer to it. + * @param file Pointer to destinition OPNIFile structure to fill it with parsed data. + * @param mem Pointer to memory block contains raw WOPI instrument file data + * @param length Length of given memory block + * @return 0 if no errors occouped, or an error code of WOPN_ErrorCodes enumeration + */ +extern int WOPN_LoadInstFromMem(OPNIFile *file, void *mem, size_t length); + +/** + * @brief Calculate the size of the output memory block + * @param file Heap-allocated WOPN file data structure + * @param version Destinition version of the file + * @return Size of the raw WOPN file data + */ +extern size_t WOPN_CalculateBankFileSize(WOPNFile *file, uint16_t version); + +/** + * @brief Calculate the size of the output memory block + * @param file Pointer to WOPI file data structure + * @param version Destinition version of the file + * @return Size of the raw WOPI file data + */ +extern size_t WOPN_CalculateInstFileSize(OPNIFile *file, uint16_t version); + +/** + * @brief Write raw WOPN into given memory block + * @param file Heap-allocated WOPN file data structure + * @param dest_mem Destinition memory block pointer + * @param length Length of destinition memory block + * @param version Wanted WOPN version + * @param force_gm Force GM set in saved bank file + * @return Error code or 0 on success + */ +extern int WOPN_SaveBankToMem(WOPNFile *file, void *dest_mem, size_t length, uint16_t version, uint16_t force_gm); + +/** + * @brief Write raw WOPI into given memory block + * @param file Pointer to WOPI file data structure + * @param dest_mem Destinition memory block pointer + * @param length Length of destinition memory block + * @param version Wanted WOPI version + * @return Error code or 0 on success + */ +extern int WOPN_SaveInstToMem(OPNIFile *file, void *dest_mem, size_t length, uint16_t version); + +#ifdef __cplusplus +} +#endif + +#endif /* WOPN_FILE_H */ diff --git a/wadsrc/static/language.enu b/wadsrc/static/language.enu index b444f27db..38a1962e8 100644 --- a/wadsrc/static/language.enu +++ b/wadsrc/static/language.enu @@ -2224,6 +2224,10 @@ ADVSNDMNU_RUNPCMRATE = "Run emulator at PCM rate"; ADVSNDMNU_ADLNUMCHIPS = "Number of emulated OPL chips"; ADVSNDMNU_VLMODEL = "Volume model"; ADVSNDMNU_OPNNUMCHIPS = "Number of emulated OPN chips"; +ADVSNDMNU_ADLCUSTOMBANK = "Use custom WOPL bank"; +ADVSNDMNU_OPLBANKFILE = "WOPL Bank file"; +ADVSNDMNU_OPNCUSTOMBANK = "Use custom WOPN bank"; +ADVSNDMNU_OPNBANKFILE = "WOPN Bank file"; // ADLMIDI's emulation cores diff --git a/wadsrc/static/menudef.txt b/wadsrc/static/menudef.txt index 931ba51a1..0bd45a7d7 100644 --- a/wadsrc/static/menudef.txt +++ b/wadsrc/static/menudef.txt @@ -1691,6 +1691,17 @@ OptionMenu ADLBankMenu protected Title "$ADVSNDMNU_OPLBANK" } +OptionMenu ADLMIDICustomBanksMenu protected +{ + Title "$ADVSNDMNU_OPLBANKFILE" +} + +OptionMenu OPNMIDICustomBanksMenu protected +{ + Title "$ADVSNDMNU_OPNBANKFILE" +} + + /*======================================= * * Module Replayer Options Menu @@ -1840,11 +1851,16 @@ OptionMenu ModReplayerOptions protected OptionMenu ADLOptions protected { Title "$ADVSNDMNU_ADLMIDI" - LabeledSubmenu "$ADVSNDMNU_OPLBANK", "adl_bank", "ADLBankMenu" Option "$ADVSNDMNU_OPLCORES", "adl_emulator_id", "ADLOplCores" Option "$ADVSNDMNU_RUNPCMRATE", "adl_run_at_pcm_rate", "OnOff" Slider "$ADVSNDMNU_ADLNUMCHIPS", "adl_chips_count", 1, 32, 1, 0 + Option "$ADVSNDMNU_OPLFULLPAN", "adl_fullpan", "OnOff" Option "$ADVSNDMNU_VLMODEL", "adl_volume_model", "AdlVolumeModels" + StaticText "" + LabeledSubmenu "$ADVSNDMNU_OPLBANK", "adl_bank", "ADLBankMenu" + StaticText "" + Option "$ADVSNDMNU_ADLCUSTOMBANK", "adl_use_custom_bank", "OnOff" + LabeledSubmenu "$ADVSNDMNU_OPLBANKFILE", "adl_custom_bank", "ADLMIDICustomBanksMenu" } OptionMenu OPNOptions protected @@ -1853,6 +1869,10 @@ OptionMenu ModReplayerOptions protected Option "$ADVSNDMNU_OPNCORES", "opn_emulator_id", "OpnCores" Option "$ADVSNDMNU_RUNPCMRATE", "opn_run_at_pcm_rate", "OnOff" Slider "$ADVSNDMNU_OPNNUMCHIPS", "opn_chips_count", 1, 32, 1, 0 + Option "$ADVSNDMNU_OPLFULLPAN", "opn_fullpan", "OnOff" + StaticText "" + Option "$ADVSNDMNU_OPNCUSTOMBANK", "opn_use_custom_bank", "OnOff" + LabeledSubmenu "$ADVSNDMNU_OPNBANKFILE", "opn_custom_bank", "OPNMIDICustomBanksMenu" } /*======================================= @@ -1944,7 +1964,7 @@ OptionMenu VideoModeMenu protected OptionMenu CustomResolutionMenu protected { Title "$VIDMNU_RESPRESETTTL" - + StaticText "$VIDMNU_RESPRESETHEAD" StaticText "" StaticText "$VIDMNU_ASPECT43" @@ -2250,7 +2270,7 @@ OptionMenu "GLTextureGLOptions" protected Option "$GLTEXMNU_TEXFILTER", gl_texture_filter, "FilterModes" Option "$GLTEXMNU_ANISOTROPIC", gl_texture_filter_anisotropic, "Anisotropy" Option "$GLTEXMNU_ENABLEHIRES", gl_texture_usehires, "YesNo" - + ifOption(MMX) { Option "$GLTEXMNU_HQRESIZE", gl_texture_hqresize, "HqResizeModes" diff --git a/wadsrc/static/xg.wopn b/wadsrc/static/xg.wopn index ccb34797763eb46530ea50425cc2f9c52947b069..c5d63367ef438d1a5974b93401aef169b5951f64 100644 GIT binary patch delta 1094 zcmcJOQAkr!7{~8-?!68snwE)MQ?Azty+~_@k`)%*ORWc+n^Pi*T650j+RZjoA1aLk zebjGSrkFk?LRP||+(e2X=*a~^1f~xOK3gw7cdiwLrV`Q1Ip;g)JBR=O{JyVih3{J7 zr{{t2U+8=A6SFX476aVj1b=`Y7nXDE9B1Mn>mA4OoFnTr#|bPFIWCPw>r>^NxTr`q zdbqQ;GZJe?_pV&(x7f@uOS_BiW>cYj*aB7@JKM`f#ic_Mt=0#h6=Wm+re&LSEpad!7jS_{7u0lemHzUJJTvDG@tR@M>Vg zGu~91#GcUGvBtX(kJN?C*@{#jx*Vtt`*7T9!MAlyM7&2!>sYw60XNJ$VNS0f=lsB%tFId@(jCi&mCYW+c(%ph#c>Uq4WZcngIg4fYx^gkLE74heYlVerp1uyiR0th z&{xe~vtj;-3={O^$T9w(tggP)(wkdC{+5c1#s0DqCOZ%7XMzT|84YHUPxQi598WT8 z31in-RVKf#vq;LcdbS+a-TYl?O?B*0T00Wj&>C2^kb*VL_^f7#r=s}t@jI6eTbEtK(_IpGlYHf24*G{^b^CI%n zUB)w)+NgG+M2%98=+;Fg1YNlxkRbCW@S-d2Kj=Nni_l86oA=lA@bLYlh5#I2Yw8HUmk6TA=fC|28pE*ek3;x!H)KjH8+If?3N zo^=jpV&Dmxp*2*-jq-BnDkj=aT>o)+i~jZg8hLp$AmR<6(mDJcN*NAbr8yn{7Yj^)@S>NElmSCfx+d!B2P!&fm>#GbC4r1m>TzpWXXS_RIU* zWJ`Gw8$rsq!D{{{F<>7PHCdi@gi}#r^M|I!;!M^r-L}H4tqClJ<~kLJ;};j!st|@u zfnyL!>Qs{CYJpLaVsFRmkmv~!iwX+5WfR1M4jRr(#{bL$+8@=_Q9(+@!%kAo49AKb z`ST0Kv($fCU7r`VslG&{@6?UfNM{?7ZG_-LT%oog;zgh;IXnlYDxp@1lNM7>-PMgc tiz+8OUw0`(vD?NfXLo3<9SLn246uKx;eODq2z#22)xAu|ZcTW#KLNMNam4@t From c3894ee348b9e80525eee22e5a28eefb593f9eb4 Mon Sep 17 00:00:00 2001 From: Marisa Kirisame Date: Tue, 18 Sep 2018 01:14:39 +0200 Subject: [PATCH 33/40] Exports various resurrection-related functions to ZScript. --- src/p_actionfunctions.cpp | 25 ++++++++++++++++++++++++- src/p_enemy.cpp | 6 ++++++ src/p_mobj.cpp | 7 +++++++ wadsrc/static/zscript/actor.txt | 4 ++++ 4 files changed, 41 insertions(+), 1 deletion(-) diff --git a/src/p_actionfunctions.cpp b/src/p_actionfunctions.cpp index a7a12f9f1..3c0d35153 100644 --- a/src/p_actionfunctions.cpp +++ b/src/p_actionfunctions.cpp @@ -4551,7 +4551,30 @@ DEFINE_ACTION_FUNCTION(AActor, A_RaiseSiblings) } return 0; } - + +//=========================================================================== +// +// A_RaiseSelf +// +//=========================================================================== +DEFINE_ACTION_FUNCTION(AActor, A_RaiseSelf) +{ + PARAM_SELF_PROLOGUE(AActor); + PARAM_INT_DEF(flags); + ACTION_RETURN_BOOL(P_Thing_Raise(self, NULL, (flags & RF_NOCHECKPOSITION))); +} + +//=========================================================================== +// +// CanRaise +// +//=========================================================================== +DEFINE_ACTION_FUNCTION(AActor, CanRaise) +{ + PARAM_SELF_PROLOGUE(AActor); + ACTION_RETURN_BOOL(P_Thing_CanRaise(self)); +} + //=========================================================================== // // A_MonsterRefire diff --git a/src/p_enemy.cpp b/src/p_enemy.cpp index 9298495eb..67aa64eef 100644 --- a/src/p_enemy.cpp +++ b/src/p_enemy.cpp @@ -2971,6 +2971,12 @@ DEFINE_ACTION_FUNCTION(AActor, A_ExtChase) return 0; } +DEFINE_ACTION_FUNCTION(AActor, A_CheckForResurrection) +{ + PARAM_SELF_PROLOGUE(AActor); + ACTION_RETURN_BOOL(P_CheckForResurrection(self, false)); +} + // for internal use void A_Chase(AActor *self) { diff --git a/src/p_mobj.cpp b/src/p_mobj.cpp index e0ebaea92..bd8da579f 100644 --- a/src/p_mobj.cpp +++ b/src/p_mobj.cpp @@ -7785,6 +7785,13 @@ void AActor::Revive() E_WorldThingRevived(this); } +DEFINE_ACTION_FUNCTION(AActor, Revive) +{ + PARAM_SELF_PROLOGUE(AActor); + self->Revive(); + return 0; +} + int AActor::GetGibHealth() const { IFVIRTUAL(AActor, GetGibHealth) diff --git a/wadsrc/static/zscript/actor.txt b/wadsrc/static/zscript/actor.txt index 174722d00..2ce2e7663 100644 --- a/wadsrc/static/zscript/actor.txt +++ b/wadsrc/static/zscript/actor.txt @@ -1018,6 +1018,7 @@ class Actor : Thinker native native void A_Chase(statelabel melee = null, statelabel missile = null, int flags = 0); native void A_Scream(); native void A_VileChase(); + native bool A_CheckForResurrection(); native void A_BossDeath(); native void A_Detonate(); bool A_CallSpecial(int special, int arg1=0, int arg2=0, int arg3=0, int arg4=0, int arg5=0) @@ -1075,6 +1076,9 @@ class Actor : Thinker native native void A_RaiseMaster(int flags = 0); native void A_RaiseChildren(int flags = 0); native void A_RaiseSiblings(int flags = 0); + native bool A_RaiseSelf(int flags = 0); + native bool CanRaise(); + native void Revive(); action native bool, Actor A_ThrowGrenade(class itemtype, double zheight = 0, double xyvel = 0, double zvel = 0, bool useammo = true); native void A_Weave(int xspeed, int yspeed, double xdist, double ydist); native bool A_Morph(class type, int duration = 0, int flags = 0, class enter_flash = null, class exit_flash = null); From cd520a6528c9adb204451685144ca93fc5ed29ac Mon Sep 17 00:00:00 2001 From: Magnus Norddahl Date: Thu, 4 Oct 2018 15:33:07 +0200 Subject: [PATCH 34/40] - fix the nearest shadow map filter to have no shadow acne and replace linear with nearest in the menu --- wadsrc/static/language.enu | 2 + wadsrc/static/language.fr | 2 + wadsrc/static/menudef.txt | 4 +- wadsrc/static/shaders/glsl/main.fp | 76 ++++++++++++++++++++---------- 4 files changed, 58 insertions(+), 26 deletions(-) diff --git a/wadsrc/static/language.enu b/wadsrc/static/language.enu index 065d547be..a77f60515 100644 --- a/wadsrc/static/language.enu +++ b/wadsrc/static/language.enu @@ -2437,6 +2437,8 @@ OPTVAL_SOUNDSYSTEM = "Sound System"; OPTVAL_FOO_DUMB = "foo_dumb"; OPTVAL_ALIASING = "Aliasing"; OPTVAL_LINEAR = "Linear"; +OPTVAL_NEAREST = "Nearest"; +OPTVAL_PCF = "PCF"; OPTVAL_CUBIC = "Cubic"; OPTVAL_BLEP = "Band-limited step"; OPTVAL_LINEARSLOW = "Linear (Slower)"; diff --git a/wadsrc/static/language.fr b/wadsrc/static/language.fr index 181c9e0db..c43a3c222 100644 --- a/wadsrc/static/language.fr +++ b/wadsrc/static/language.fr @@ -2418,6 +2418,8 @@ OPTVAL_OLD = "Ancien"; OPTVAL_DEFAULT = "Défaut"; OPTVAL_SOUNDSYSTEM = "Système Sonore"; OPTVAL_LINEAR = "Linéaire"; +OPTVAL_NEAREST = "Nearest"; +OPTVAL_PCF = "PCF"; OPTVAL_CUBIC = "Cubique"; OPTVAL_BLEP = "Step limité sur bande"; OPTVAL_LINEARSLOW = "Linéaire (Lent)"; diff --git a/wadsrc/static/menudef.txt b/wadsrc/static/menudef.txt index 9e3f801cc..52143d2cc 100644 --- a/wadsrc/static/menudef.txt +++ b/wadsrc/static/menudef.txt @@ -2227,8 +2227,8 @@ OptionValue ShadowMapQuality OptionValue ShadowMapFilter { - 0, "$OPTVAL_LINEAR" - 1, "PCF" + 0, "$OPTVAL_NEAREST" + 1, "$OPTVAL_PCF" } diff --git a/wadsrc/static/shaders/glsl/main.fp b/wadsrc/static/shaders/glsl/main.fp index 219e01581..c8ad439cb 100644 --- a/wadsrc/static/shaders/glsl/main.fp +++ b/wadsrc/static/shaders/glsl/main.fp @@ -160,21 +160,21 @@ float R_DoomLightingEquation(float light) float shadowDirToU(vec2 dir) { - if (abs(dir.x) > abs(dir.y)) + if (abs(dir.y) > abs(dir.x)) { - float v = dir.y / dir.x * 0.125; - if (dir.x >= 0.0) - return (0.25 + 0.125) - v; + float x = dir.x / dir.y * 0.125; + if (dir.y >= 0.0) + return 0.125 + x; else - return (0.75 + 0.125) - v; + return (0.50 + 0.125) + x; } else { - float v = dir.x / dir.y * 0.125; - if (dir.y >= 0.0) - return 0.125 + v; + float y = dir.y / dir.x * 0.125; + if (dir.x >= 0.0) + return (0.25 + 0.125) - y; else - return (0.50 + 0.125) + v; + return (0.75 + 0.125) - y; } } @@ -182,7 +182,7 @@ float sampleShadowmap(vec2 dir, float v) { float u = shadowDirToU(dir); float dist2 = dot(dir, dir); - return texture(ShadowMap, vec2(u, v)).x > dist2 ? 1.0 : 0.0; + return step(dist2, texture(ShadowMap, vec2(u, v)).x); } float sampleShadowmapLinear(vec2 dir, float v) @@ -211,6 +211,40 @@ float sampleShadowmapLinear(vec2 dir, float v) return mix(step(dist2, depth0), step(dist2, depth1), t); } +vec2 shadowmapAdjustedRay(vec4 lightpos) +{ + vec3 planePoint = pixelpos.xyz - lightpos.xyz; + + if (dot(planePoint.xz, planePoint.xz) < 1.0) + return planePoint.xz * 0.5; + + vec3 ray = normalize(planePoint); + + vec2 isize = textureSize(ShadowMap, 0); + float scale = float(isize.x) * 0.25; + + // Snap to shadow map texel grid + if (abs(ray.z) > abs(ray.x)) + { + ray.y = ray.y / abs(ray.z); + ray.x = ray.x / abs(ray.z); + ray.x = (floor((ray.x + 1.0) * 0.5 * scale) + 0.5) / scale * 2.0 - 1.0; + ray.z = sign(ray.z); + } + else + { + ray.y = ray.y / abs(ray.x); + ray.z = ray.z / abs(ray.x); + ray.z = (floor((ray.z + 1.0) * 0.5 * scale) + 0.5) / scale * 2.0 - 1.0; + ray.x = sign(ray.x); + } + + float bias = 1.0; + float negD = dot(vWorldNormal.xyz, planePoint); + float t = negD / dot(vWorldNormal.xyz, ray) - bias; + return ray.xz * t; +} + //=========================================================================== // // Check if light is in shadow using Percentage Closer Filtering (PCF) @@ -224,22 +258,20 @@ float shadowmapAttenuation(vec4 lightpos, float shadowIndex) float v = (shadowIndex + 0.5) / 1024.0; - vec2 ray = pixelpos.xz - lightpos.xz; - float length = length(ray); - if (length < 3.0) - return 1.0; - - vec2 dir = ray / length; + vec2 ray = shadowmapAdjustedRay(lightpos); if (uShadowmapFilter <= 0) { - ray -= dir * 2.0; // Shadow acne margin - return sampleShadowmapLinear(ray, v); + return sampleShadowmap(ray, v); + //return sampleShadowmapLinear(ray, v); } else { - ray -= dir * 2.0; // Shadow acne margin - dir = dir * min(length / 50.0, 1.0); // avoid sampling behind light + float length = length(ray); + if (length < 3.0) + return 1.0; + + vec2 dir = ray / length * min(length / 50.0, 1.0); // avoid sampling behind light vec2 normal = vec2(-dir.y, dir.x); vec2 bias = dir * 10.0; @@ -253,10 +285,6 @@ float shadowmapAttenuation(vec4 lightpos, float shadowIndex) } return sum / uShadowmapFilter; } -#if 0 // nearest shadow filter (not used) - ray -= dir * 6.0; // Shadow acne margin - return sampleShadowmap(ray, v); -#endif } float shadowAttenuation(vec4 lightpos, float lightcolorA) From 25ac526936008daf45c8368568079d222ebf523c Mon Sep 17 00:00:00 2001 From: Marisa Kirisame Date: Thu, 4 Oct 2018 23:16:43 +0200 Subject: [PATCH 35/40] Computed facet normals for UE1 models were not normalized when they were supposed to. --- src/r_data/models/models_ue1.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/r_data/models/models_ue1.cpp b/src/r_data/models/models_ue1.cpp index 838be2af7..bd4f54e3d 100644 --- a/src/r_data/models/models_ue1.cpp +++ b/src/r_data/models/models_ue1.cpp @@ -129,7 +129,7 @@ void FUE1Model::LoadGeometry() FVector3 dir[2]; dir[0] = verts[Poly.V[1]+numVerts*j].Pos-verts[Poly.V[0]+numVerts*j].Pos; dir[1] = verts[Poly.V[2]+numVerts*j].Pos-verts[Poly.V[0]+numVerts*j].Pos; - Poly.Normals.Push(dir[0]^dir[1]); + Poly.Normals.Push((dir[0]^dir[1]).Unit()); } // push polys.Push(Poly); From e7f19b01cb026e0a45b3c7354cc898585a182109 Mon Sep 17 00:00:00 2001 From: Rachael Alexanderson Date: Wed, 3 Oct 2018 09:36:01 -0400 Subject: [PATCH 36/40] - added normal5x and normal6x --- src/textures/hires/hqresize.cpp | 6 +++++- wadsrc/static/language.enu | 2 ++ wadsrc/static/menudef.txt | 2 ++ wadsrc/static/zscript/menu/optionmenu.txt | 2 ++ 4 files changed, 11 insertions(+), 1 deletion(-) diff --git a/src/textures/hires/hqresize.cpp b/src/textures/hires/hqresize.cpp index 6bcb2d805..01f310ed2 100644 --- a/src/textures/hires/hqresize.cpp +++ b/src/textures/hires/hqresize.cpp @@ -47,7 +47,7 @@ CUSTOM_CVAR(Int, gl_texture_hqresize, 0, CVAR_ARCHIVE | CVAR_GLOBALCONFIG | CVAR_NOINITCALL) { - if (self < 0 || self > 22) + if (self < 0 || self > 24) { self = 0; } @@ -439,6 +439,10 @@ unsigned char *FTexture::CreateUpsampledTextureBuffer (unsigned char *inputBuffe return normalNxHelper( &normalNx, 3, inputBuffer, inWidth, inHeight, outWidth, outHeight ); case 22: return normalNxHelper( &normalNx, 4, inputBuffer, inWidth, inHeight, outWidth, outHeight ); + case 23: + return normalNxHelper( &normalNx, 5, inputBuffer, inWidth, inHeight, outWidth, outHeight ); + case 24: + return normalNxHelper( &normalNx, 6, inputBuffer, inWidth, inHeight, outWidth, outHeight ); } } return inputBuffer; diff --git a/wadsrc/static/language.enu b/wadsrc/static/language.enu index 4f5e1dd13..6ce6fcf23 100644 --- a/wadsrc/static/language.enu +++ b/wadsrc/static/language.enu @@ -2881,6 +2881,8 @@ OPTVAL_SCALE4X = "Scale4x"; OPTVAL_NORMAL2X = "Normal2x"; OPTVAL_NORMAL3X = "Normal3x"; OPTVAL_NORMAL4X = "Normal4x"; +OPTVAL_NORMAL5X = "Normal5x"; +OPTVAL_NORMAL6X = "Normal6x"; OPTVAL_HQ2X = "hq2x"; OPTVAL_HQ3X = "hq3x"; OPTVAL_HQ4X = "hq4x"; diff --git a/wadsrc/static/menudef.txt b/wadsrc/static/menudef.txt index a40c8b2df..f210af477 100644 --- a/wadsrc/static/menudef.txt +++ b/wadsrc/static/menudef.txt @@ -2188,6 +2188,8 @@ OptionValue "HqResizeModes" 20, "$OPTVAL_NORMAL2X" 21, "$OPTVAL_NORMAL3X" 22, "$OPTVAL_NORMAL4X" + 23, "$OPTVAL_NORMAL5X" + 24, "$OPTVAL_NORMAL6X" } OptionValue "HqResizeModesNoMMX" diff --git a/wadsrc/static/zscript/menu/optionmenu.txt b/wadsrc/static/zscript/menu/optionmenu.txt index ede7c0217..3e1853735 100644 --- a/wadsrc/static/zscript/menu/optionmenu.txt +++ b/wadsrc/static/zscript/menu/optionmenu.txt @@ -595,10 +595,12 @@ class GLTextureGLOptions : OptionMenu break; case 16: case 18: + case 23: multiplier = 25; break; case 17: case 19: + case 24: multiplier = 36; break; } From 6135e867a99e2d9e90815067ce9aee7d702bdcb8 Mon Sep 17 00:00:00 2001 From: Magnus Norddahl Date: Fri, 5 Oct 2018 04:36:11 +0200 Subject: [PATCH 37/40] - remove shadow acne from PCF shadowmap filter and add three quality levels --- wadsrc/static/language.enu | 4 +- wadsrc/static/language.fr | 4 +- wadsrc/static/menudef.txt | 4 +- wadsrc/static/shaders/glsl/main.fp | 128 ++++++++++++++--------------- 4 files changed, 72 insertions(+), 68 deletions(-) diff --git a/wadsrc/static/language.enu b/wadsrc/static/language.enu index 4f5e1dd13..ab957c322 100644 --- a/wadsrc/static/language.enu +++ b/wadsrc/static/language.enu @@ -2442,7 +2442,9 @@ OPTVAL_FOO_DUMB = "foo_dumb"; OPTVAL_ALIASING = "Aliasing"; OPTVAL_LINEAR = "Linear"; OPTVAL_NEAREST = "Nearest"; -OPTVAL_PCF = "PCF"; +OPTVAL_PCF_LOW = "PCF (Low)"; +OPTVAL_PCF_MEDIUM = "PCF (Medium)"; +OPTVAL_PCF_HIGH = "PCF (High)"; OPTVAL_CUBIC = "Cubic"; OPTVAL_BLEP = "Band-limited step"; OPTVAL_LINEARSLOW = "Linear (Slower)"; diff --git a/wadsrc/static/language.fr b/wadsrc/static/language.fr index c43a3c222..22cd852d8 100644 --- a/wadsrc/static/language.fr +++ b/wadsrc/static/language.fr @@ -2419,7 +2419,9 @@ OPTVAL_DEFAULT = "Défaut"; OPTVAL_SOUNDSYSTEM = "Système Sonore"; OPTVAL_LINEAR = "Linéaire"; OPTVAL_NEAREST = "Nearest"; -OPTVAL_PCF = "PCF"; +OPTVAL_PCF_LOW = "PCF (Low)"; +OPTVAL_PCF_MEDIUM = "PCF (Medium)"; +OPTVAL_PCF_HIGH = "PCF (High)"; OPTVAL_CUBIC = "Cubique"; OPTVAL_BLEP = "Step limité sur bande"; OPTVAL_LINEARSLOW = "Linéaire (Lent)"; diff --git a/wadsrc/static/menudef.txt b/wadsrc/static/menudef.txt index a40c8b2df..c2e084c3d 100644 --- a/wadsrc/static/menudef.txt +++ b/wadsrc/static/menudef.txt @@ -2259,7 +2259,9 @@ OptionValue ShadowMapQuality OptionValue ShadowMapFilter { 0, "$OPTVAL_NEAREST" - 1, "$OPTVAL_PCF" + 1, "$OPTVAL_PCF_LOW" + 2, "$OPTVAL_PCF_MEDIUM" + 3, "$OPTVAL_PCF_HIGH" } diff --git a/wadsrc/static/shaders/glsl/main.fp b/wadsrc/static/shaders/glsl/main.fp index c8ad439cb..6bc34d275 100644 --- a/wadsrc/static/shaders/glsl/main.fp +++ b/wadsrc/static/shaders/glsl/main.fp @@ -178,47 +178,26 @@ float shadowDirToU(vec2 dir) } } -float sampleShadowmap(vec2 dir, float v) +vec2 shadowUToDir(float u) { - float u = shadowDirToU(dir); - float dist2 = dot(dir, dir); - return step(dist2, texture(ShadowMap, vec2(u, v)).x); + u *= 4.0; + vec2 raydir; + switch (int(u)) + { + case 0: raydir = vec2(u * 2.0 - 1.0, 1.0); break; + case 1: raydir = vec2(1.0, 1.0 - (u - 1.0) * 2.0); break; + case 2: raydir = vec2(1.0 - (u - 2.0) * 2.0, -1.0); break; + case 3: raydir = vec2(-1.0, (u - 3.0) * 2.0 - 1.0); break; + } + return raydir; } -float sampleShadowmapLinear(vec2 dir, float v) +float sampleShadowmap(vec3 planePoint, float v) { - float u = shadowDirToU(dir); - float dist2 = dot(dir, dir); + float bias = 1.0; + float negD = dot(vWorldNormal.xyz, planePoint); - vec2 isize = textureSize(ShadowMap, 0); - vec2 size = vec2(isize); - - vec2 fetchPos = vec2(u, v) * size - vec2(0.5, 0.0); - if (fetchPos.x < 0.0) - fetchPos.x += size.x; - - ivec2 ifetchPos = ivec2(fetchPos); - int y = ifetchPos.y; - - float t = fract(fetchPos.x); - int x0 = ifetchPos.x; - int x1 = ifetchPos.x + 1; - if (x1 == isize.x) - x1 = 0; - - float depth0 = texelFetch(ShadowMap, ivec2(x0, y), 0).x; - float depth1 = texelFetch(ShadowMap, ivec2(x1, y), 0).x; - return mix(step(dist2, depth0), step(dist2, depth1), t); -} - -vec2 shadowmapAdjustedRay(vec4 lightpos) -{ - vec3 planePoint = pixelpos.xyz - lightpos.xyz; - - if (dot(planePoint.xz, planePoint.xz) < 1.0) - return planePoint.xz * 0.5; - - vec3 ray = normalize(planePoint); + vec3 ray = planePoint; vec2 isize = textureSize(ShadowMap, 0); float scale = float(isize.x) * 0.25; @@ -239,51 +218,70 @@ vec2 shadowmapAdjustedRay(vec4 lightpos) ray.x = sign(ray.x); } - float bias = 1.0; - float negD = dot(vWorldNormal.xyz, planePoint); float t = negD / dot(vWorldNormal.xyz, ray) - bias; - return ray.xz * t; + vec2 dir = ray.xz * t; + + float u = shadowDirToU(dir); + float dist2 = dot(dir, dir); + return step(dist2, texture(ShadowMap, vec2(u, v)).x); } -//=========================================================================== -// -// Check if light is in shadow using Percentage Closer Filtering (PCF) -// -//=========================================================================== +float sampleShadowmapPCF(vec3 planePoint, float v) +{ + float bias = 1.0; + float negD = dot(vWorldNormal.xyz, planePoint); + + vec3 ray = planePoint; + + if (abs(ray.z) > abs(ray.x)) + ray.y = ray.y / abs(ray.z); + else + ray.y = ray.y / abs(ray.x); + + vec2 isize = textureSize(ShadowMap, 0); + float scale = float(isize.x); + float texelPos = floor(shadowDirToU(ray.xz) * scale); + + float sum = 0.0; + float step_count = uShadowmapFilter; + + texelPos -= step_count + 0.5; + for (float x = -step_count; x <= step_count; x++) + { + float u = texelPos / scale; + vec2 dir = shadowUToDir(u); + + ray.x = dir.x; + ray.z = dir.y; + float t = negD / dot(vWorldNormal.xyz, ray) - bias; + dir = ray.xz * t; + + float dist2 = dot(dir, dir); + sum += step(dist2, texture(ShadowMap, vec2(u, v)).x); + texelPos++; + } + return sum / (uShadowmapFilter * 2.0 + 1.0); +} float shadowmapAttenuation(vec4 lightpos, float shadowIndex) { if (shadowIndex >= 1024.0) return 1.0; // No shadowmap available for this light - float v = (shadowIndex + 0.5) / 1024.0; + vec3 planePoint = pixelpos.xyz - lightpos.xyz; - vec2 ray = shadowmapAdjustedRay(lightpos); + if (dot(planePoint.xz, planePoint.xz) < 1.0) + return 1.0; // Light is too close + + float v = (shadowIndex + 0.5) / 1024.0; if (uShadowmapFilter <= 0) { - return sampleShadowmap(ray, v); - //return sampleShadowmapLinear(ray, v); + return sampleShadowmap(planePoint, v); } else { - float length = length(ray); - if (length < 3.0) - return 1.0; - - vec2 dir = ray / length * min(length / 50.0, 1.0); // avoid sampling behind light - - vec2 normal = vec2(-dir.y, dir.x); - vec2 bias = dir * 10.0; - - float sum = 0.0; - float step_count = ((uShadowmapFilter - 1) / 2.); - - for (float x = -step_count; x <= step_count; x++) - { - sum += sampleShadowmap(ray + normal * x /*- bias * abs(x)*/, v); - } - return sum / uShadowmapFilter; + return sampleShadowmapPCF(planePoint, v); } } From 9cffc291349a927f4d54ecf6aaaac7937d6f499b Mon Sep 17 00:00:00 2001 From: "alexey.lysiuk" Date: Fri, 5 Oct 2018 15:22:21 +0300 Subject: [PATCH 38/40] - fixed stuck memory usage warning in options menu https://forum.zdoom.org/viewtopic.php?t=62186 --- wadsrc/static/zscript/menu/optionmenu.txt | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/wadsrc/static/zscript/menu/optionmenu.txt b/wadsrc/static/zscript/menu/optionmenu.txt index 3e1853735..f73035ab0 100644 --- a/wadsrc/static/zscript/menu/optionmenu.txt +++ b/wadsrc/static/zscript/menu/optionmenu.txt @@ -540,14 +540,19 @@ class CompatibilityMenu : OptionMenu class GLTextureGLOptions : OptionMenu { private int mWarningIndex; + private string mWarningLabel; override void Init(Menu parent, OptionMenuDescriptor desc) { super.Init(parent, desc); + // Find index of warning item placeholder + mWarningIndex = -1; + mWarningLabel = "!HQRESIZE_WARNING!"; + for (int i=0; i < mDesc.mItems.Size(); ++i) { - if (mDesc.mItems[i].mLabel == "!HQRESIZE_WARNING!") + if (mDesc.mItems[i].mLabel == mWarningLabel) { mWarningIndex = i; break; @@ -555,11 +560,22 @@ class GLTextureGLOptions : OptionMenu } } + override void OnDestroy() + { + // Restore warning item placeholder + if (mWarningIndex >= 0) + { + mDesc.mItems[mWarningIndex].mLabel = mWarningLabel; + } + + Super.OnDestroy(); + } + override void Ticker() { Super.Ticker(); - if (mWarningIndex > 0) + if (mWarningIndex >= 0) { string message; From 8ea74770fde01ed6d8d7dd50160b213a54e5f043 Mon Sep 17 00:00:00 2001 From: Magnus Norddahl Date: Fri, 5 Oct 2018 21:30:16 +0200 Subject: [PATCH 39/40] - fix artifact in PCF filter when the texture coordinate wraps --- wadsrc/static/shaders/glsl/main.fp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/wadsrc/static/shaders/glsl/main.fp b/wadsrc/static/shaders/glsl/main.fp index 6bc34d275..228d7b02d 100644 --- a/wadsrc/static/shaders/glsl/main.fp +++ b/wadsrc/static/shaders/glsl/main.fp @@ -248,7 +248,7 @@ float sampleShadowmapPCF(vec3 planePoint, float v) texelPos -= step_count + 0.5; for (float x = -step_count; x <= step_count; x++) { - float u = texelPos / scale; + float u = fract(texelPos / scale); vec2 dir = shadowUToDir(u); ray.x = dir.x; From 605d9ecdabbb89ad14a3e92c07147777cf67e683 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Sat, 6 Oct 2018 23:50:12 +0200 Subject: [PATCH 40/40] - removed dynamic lights from Hexen's Mana pickups. They barely glow, yet were casting relatively strong lights. --- wadsrc_lights/static/filter/hexen/gldefs.txt | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/wadsrc_lights/static/filter/hexen/gldefs.txt b/wadsrc_lights/static/filter/hexen/gldefs.txt index cc9513d97..fb6cb49c3 100644 --- a/wadsrc_lights/static/filter/hexen/gldefs.txt +++ b/wadsrc_lights/static/filter/hexen/gldefs.txt @@ -1545,11 +1545,6 @@ pointlight MANA1 offset 0 36 0 } -object Mana1 -{ - frame MAN1 { light MANA1 } -} - // Green mana pointlight MANA2 { @@ -1559,11 +1554,6 @@ pointlight MANA2 offset 0 36 0 } -object Mana2 -{ - frame MAN2 { light MANA2 } -} - // Combined mana pointlight MANA3 {