1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212 |
- /*
- * DLSBank.cpp
- * -----------
- * Purpose: Sound bank loading.
- * Notes : Supported sound bank types: DLS (including embedded DLS in MSS & RMI), SF2, SF3 / SF4 (modified SF2 with compressed samples)
- * Authors: Olivier Lapicque
- * OpenMPT Devs
- * The OpenMPT source code is released under the BSD license. Read LICENSE for more details.
- */
- #include "stdafx.h"
- #include "Sndfile.h"
- #ifdef MODPLUG_TRACKER
- #include "../mptrack/Mptrack.h"
- #include "../common/mptFileIO.h"
- #endif
- #include "Dlsbank.h"
- #include "Loaders.h"
- #include "SampleCopy.h"
- #include "../common/mptStringBuffer.h"
- #include "../common/FileReader.h"
- #include "openmpt/base/Endian.hpp"
- #include "SampleIO.h"
- #include "mpt/io/base.hpp"
- #include "mpt/io/io.hpp"
- #include "mpt/io/io_stdstream.hpp"
- OPENMPT_NAMESPACE_BEGIN
- #ifdef MODPLUG_TRACKER
- #ifdef MPT_ALL_LOGGING
- #define DLSBANK_LOG
- #define DLSINSTR_LOG
- #endif
- #define F_RGN_OPTION_SELFNONEXCLUSIVE 0x0001
- // Region Flags
- enum RegionFlags
- {
- DLSREGION_KEYGROUPMASK = 0x0F,
- DLSREGION_OVERRIDEWSMP = 0x10,
- DLSREGION_PINGPONGLOOP = 0x20,
- DLSREGION_SAMPLELOOP = 0x40,
- DLSREGION_SELFNONEXCLUSIVE = 0x80,
- DLSREGION_SUSTAINLOOP = 0x100,
- };
- ///////////////////////////////////////////////////////////////////////////
- // Articulation connection graph definitions
- enum ConnectionSource : uint16
- {
- // Generic Sources
- CONN_SRC_NONE = 0x0000,
- CONN_SRC_LFO = 0x0001,
- CONN_SRC_KEYONVELOCITY = 0x0002,
- CONN_SRC_KEYNUMBER = 0x0003,
- CONN_SRC_EG1 = 0x0004,
- CONN_SRC_EG2 = 0x0005,
- CONN_SRC_PITCHWHEEL = 0x0006,
- CONN_SRC_POLYPRESSURE = 0x0007,
- CONN_SRC_CHANNELPRESSURE = 0x0008,
- CONN_SRC_VIBRATO = 0x0009,
- // Midi Controllers 0-127
- CONN_SRC_CC1 = 0x0081,
- CONN_SRC_CC7 = 0x0087,
- CONN_SRC_CC10 = 0x008a,
- CONN_SRC_CC11 = 0x008b,
- CONN_SRC_CC91 = 0x00db,
- CONN_SRC_CC93 = 0x00dd,
- CONN_SRC_RPN0 = 0x0100,
- CONN_SRC_RPN1 = 0x0101,
- CONN_SRC_RPN2 = 0x0102,
- };
- enum ConnectionDestination : uint16
- {
- // Generic Destinations
- CONN_DST_NONE = 0x0000,
- CONN_DST_ATTENUATION = 0x0001,
- CONN_DST_RESERVED = 0x0002,
- CONN_DST_PITCH = 0x0003,
- CONN_DST_PAN = 0x0004,
- // LFO Destinations
- CONN_DST_LFO_FREQUENCY = 0x0104,
- CONN_DST_LFO_STARTDELAY = 0x0105,
- CONN_DST_KEYNUMBER = 0x0005,
- // EG1 Destinations
- CONN_DST_EG1_ATTACKTIME = 0x0206,
- CONN_DST_EG1_DECAYTIME = 0x0207,
- CONN_DST_EG1_RESERVED = 0x0208,
- CONN_DST_EG1_RELEASETIME = 0x0209,
- CONN_DST_EG1_SUSTAINLEVEL = 0x020a,
- CONN_DST_EG1_DELAYTIME = 0x020b,
- CONN_DST_EG1_HOLDTIME = 0x020c,
- CONN_DST_EG1_SHUTDOWNTIME = 0x020d,
- // EG2 Destinations
- CONN_DST_EG2_ATTACKTIME = 0x030a,
- CONN_DST_EG2_DECAYTIME = 0x030b,
- CONN_DST_EG2_RESERVED = 0x030c,
- CONN_DST_EG2_RELEASETIME = 0x030d,
- CONN_DST_EG2_SUSTAINLEVEL = 0x030e,
- CONN_DST_EG2_DELAYTIME = 0x030f,
- CONN_DST_EG2_HOLDTIME = 0x0310,
- CONN_TRN_NONE = 0x0000,
- CONN_TRN_CONCAVE = 0x0001,
- };
- //////////////////////////////////////////////////////////
- // Supported DLS1 Articulations
- // [4-bit transform][12-bit dest][8-bit control][8-bit source] = 32-bit ID
- constexpr uint32 DLSArt(uint8 src, uint8 ctl, uint16 dst)
- {
- return (dst << 16u) | (ctl << 8u) | src;
- }
- enum DLSArt : uint32
- {
- // Vibrato / Tremolo
- ART_LFO_FREQUENCY = DLSArt(CONN_SRC_NONE, CONN_SRC_NONE, CONN_DST_LFO_FREQUENCY),
- ART_LFO_STARTDELAY = DLSArt(CONN_SRC_NONE, CONN_SRC_NONE, CONN_DST_LFO_STARTDELAY),
- ART_LFO_ATTENUATION = DLSArt(CONN_SRC_LFO, CONN_SRC_NONE, CONN_DST_ATTENUATION),
- ART_LFO_PITCH = DLSArt(CONN_SRC_LFO, CONN_SRC_NONE, CONN_DST_PITCH),
- ART_LFO_MODWTOATTN = DLSArt(CONN_SRC_LFO, CONN_SRC_CC1, CONN_DST_ATTENUATION),
- ART_LFO_MODWTOPITCH = DLSArt(CONN_SRC_LFO, CONN_SRC_CC1, CONN_DST_PITCH),
- // Volume Envelope
- ART_VOL_EG_ATTACKTIME = DLSArt(CONN_SRC_NONE, CONN_SRC_NONE, CONN_DST_EG1_ATTACKTIME),
- ART_VOL_EG_DECAYTIME = DLSArt(CONN_SRC_NONE, CONN_SRC_NONE, CONN_DST_EG1_DECAYTIME),
- ART_VOL_EG_SUSTAINLEVEL = DLSArt(CONN_SRC_NONE, CONN_SRC_NONE, CONN_DST_EG1_SUSTAINLEVEL),
- ART_VOL_EG_RELEASETIME = DLSArt(CONN_SRC_NONE, CONN_SRC_NONE, CONN_DST_EG1_RELEASETIME),
- ART_VOL_EG_DELAYTIME = DLSArt(CONN_SRC_NONE, CONN_SRC_NONE, CONN_DST_EG1_DELAYTIME),
- ART_VOL_EG_HOLDTIME = DLSArt(CONN_SRC_NONE, CONN_SRC_NONE, CONN_DST_EG1_HOLDTIME),
- ART_VOL_EG_SHUTDOWNTIME = DLSArt(CONN_SRC_NONE, CONN_SRC_NONE, CONN_DST_EG1_SHUTDOWNTIME),
- ART_VOL_EG_VELTOATTACK = DLSArt(CONN_SRC_KEYONVELOCITY, CONN_SRC_NONE, CONN_DST_EG1_ATTACKTIME),
- ART_VOL_EG_KEYTODECAY = DLSArt(CONN_SRC_KEYNUMBER, CONN_SRC_NONE, CONN_DST_EG1_DECAYTIME),
- // Pitch Envelope
- ART_PITCH_EG_ATTACKTIME = DLSArt(CONN_SRC_NONE, CONN_SRC_NONE, CONN_DST_EG2_ATTACKTIME),
- ART_PITCH_EG_DECAYTIME = DLSArt(CONN_SRC_NONE, CONN_SRC_NONE, CONN_DST_EG2_DECAYTIME),
- ART_PITCH_EG_SUSTAINLEVEL = DLSArt(CONN_SRC_NONE, CONN_SRC_NONE, CONN_DST_EG2_SUSTAINLEVEL),
- ART_PITCH_EG_RELEASETIME = DLSArt(CONN_SRC_NONE, CONN_SRC_NONE, CONN_DST_EG2_RELEASETIME),
- ART_PITCH_EG_VELTOATTACK = DLSArt(CONN_SRC_KEYONVELOCITY, CONN_SRC_NONE, CONN_DST_EG2_ATTACKTIME),
- ART_PITCH_EG_KEYTODECAY = DLSArt(CONN_SRC_KEYNUMBER, CONN_SRC_NONE, CONN_DST_EG2_DECAYTIME),
- // Default Pan
- ART_DEFAULTPAN = DLSArt(CONN_SRC_NONE, CONN_SRC_NONE, CONN_DST_PAN),
- };
- //////////////////////////////////////////////////////////
- // DLS IFF Chunk IDs
- enum IFFChunkID : uint32
- {
- // Standard IFF chunks IDs
- IFFID_FORM = MagicLE("FORM"),
- IFFID_RIFF = MagicLE("RIFF"),
- IFFID_LIST = MagicLE("LIST"),
- IFFID_INFO = MagicLE("INFO"),
- // IFF Info fields
- IFFID_ICOP = MagicLE("ICOP"),
- IFFID_INAM = MagicLE("INAM"),
- IFFID_ICMT = MagicLE("ICMT"),
- IFFID_IENG = MagicLE("IENG"),
- IFFID_ISFT = MagicLE("ISFT"),
- IFFID_ISBJ = MagicLE("ISBJ"),
- // Wave IFF chunks IDs
- IFFID_wave = MagicLE("wave"),
- IFFID_wsmp = MagicLE("wsmp"),
- IFFID_XDLS = MagicLE("XDLS"),
- IFFID_DLS = MagicLE("DLS "),
- IFFID_MLS = MagicLE("MLS "),
- IFFID_RMID = MagicLE("RMID"),
- IFFID_colh = MagicLE("colh"),
- IFFID_ins = MagicLE("ins "),
- IFFID_insh = MagicLE("insh"),
- IFFID_ptbl = MagicLE("ptbl"),
- IFFID_wvpl = MagicLE("wvpl"),
- IFFID_rgn = MagicLE("rgn "),
- IFFID_rgn2 = MagicLE("rgn2"),
- IFFID_rgnh = MagicLE("rgnh"),
- IFFID_wlnk = MagicLE("wlnk"),
- IFFID_art1 = MagicLE("art1"),
- IFFID_art2 = MagicLE("art2"),
- };
- //////////////////////////////////////////////////////////
- // DLS Structures definitions
- struct IFFCHUNK
- {
- uint32le id;
- uint32le len;
- };
- MPT_BINARY_STRUCT(IFFCHUNK, 8)
- struct RIFFChunkID
- {
- uint32le id_RIFF;
- uint32le riff_len;
- uint32le id_DLS;
- };
- MPT_BINARY_STRUCT(RIFFChunkID, 12)
- struct LISTChunk
- {
- uint32le id;
- uint32le len;
- uint32le listid;
- };
- MPT_BINARY_STRUCT(LISTChunk, 12)
- struct DLSRgnRange
- {
- uint16le usLow;
- uint16le usHigh;
- };
- MPT_BINARY_STRUCT(DLSRgnRange, 4)
- struct VERSChunk
- {
- uint32le id;
- uint32le len;
- uint16le version[4];
- };
- MPT_BINARY_STRUCT(VERSChunk, 16)
- struct PTBLChunk
- {
- uint32le cbSize;
- uint32le cCues;
- };
- MPT_BINARY_STRUCT(PTBLChunk, 8)
- struct INSHChunk
- {
- uint32le cRegions;
- uint32le ulBank;
- uint32le ulInstrument;
- };
- MPT_BINARY_STRUCT(INSHChunk, 12)
- struct RGNHChunk
- {
- DLSRgnRange RangeKey;
- DLSRgnRange RangeVelocity;
- uint16le fusOptions;
- uint16le usKeyGroup;
- };
- MPT_BINARY_STRUCT(RGNHChunk, 12)
- struct WLNKChunk
- {
- uint16le fusOptions;
- uint16le usPhaseGroup;
- uint32le ulChannel;
- uint32le ulTableIndex;
- };
- MPT_BINARY_STRUCT(WLNKChunk, 12)
- struct ART1Chunk
- {
- uint32le cbSize;
- uint32le cConnectionBlocks;
- };
- MPT_BINARY_STRUCT(ART1Chunk, 8)
- struct ConnectionBlock
- {
- uint16le usSource;
- uint16le usControl;
- uint16le usDestination;
- uint16le usTransform;
- int32le lScale;
- };
- MPT_BINARY_STRUCT(ConnectionBlock, 12)
- struct WSMPChunk
- {
- uint32le cbSize;
- uint16le usUnityNote;
- int16le sFineTune;
- int32le lAttenuation;
- uint32le fulOptions;
- uint32le cSampleLoops;
- };
- MPT_BINARY_STRUCT(WSMPChunk, 20)
- struct WSMPSampleLoop
- {
- uint32le cbSize;
- uint32le ulLoopType;
- uint32le ulLoopStart;
- uint32le ulLoopLength;
- };
- MPT_BINARY_STRUCT(WSMPSampleLoop, 16)
- /////////////////////////////////////////////////////////////////////
- // SF2 IFF Chunk IDs
- enum SF2ChunkID : uint32
- {
- IFFID_ifil = MagicLE("ifil"),
- IFFID_sfbk = MagicLE("sfbk"),
- IFFID_sfpk = MagicLE("sfpk"), // SF2Pack compressed soundfont
- IFFID_sdta = MagicLE("sdta"),
- IFFID_pdta = MagicLE("pdta"),
- IFFID_phdr = MagicLE("phdr"),
- IFFID_pbag = MagicLE("pbag"),
- IFFID_pgen = MagicLE("pgen"),
- IFFID_inst = MagicLE("inst"),
- IFFID_ibag = MagicLE("ibag"),
- IFFID_igen = MagicLE("igen"),
- IFFID_shdr = MagicLE("shdr"),
- };
- ///////////////////////////////////////////
- // SF2 Generators IDs
- enum SF2Generators : uint16
- {
- SF2_GEN_START_LOOP_FINE = 2,
- SF2_GEN_END_LOOP_FINE = 3,
- SF2_GEN_MODENVTOFILTERFC = 11,
- SF2_GEN_PAN = 17,
- SF2_GEN_DECAYMODENV = 28,
- SF2_GEN_ATTACKVOLENV = 34,
- SF2_GEN_HOLDVOLENV = 34,
- SF2_GEN_DECAYVOLENV = 36,
- SF2_GEN_SUSTAINVOLENV = 37,
- SF2_GEN_RELEASEVOLENV = 38,
- SF2_GEN_INSTRUMENT = 41,
- SF2_GEN_KEYRANGE = 43,
- SF2_GEN_START_LOOP_COARSE = 45,
- SF2_GEN_ATTENUATION = 48,
- SF2_GEN_END_LOOP_COARSE = 50,
- SF2_GEN_COARSETUNE = 51,
- SF2_GEN_FINETUNE = 52,
- SF2_GEN_SAMPLEID = 53,
- SF2_GEN_SAMPLEMODES = 54,
- SF2_GEN_SCALE_TUNING = 56,
- SF2_GEN_KEYGROUP = 57,
- SF2_GEN_UNITYNOTE = 58,
- };
- /////////////////////////////////////////////////////////////////////
- // SF2 Structures Definitions
- struct SFPresetHeader
- {
- char achPresetName[20];
- uint16le wPreset;
- uint16le wBank;
- uint16le wPresetBagNdx;
- uint32le dwLibrary;
- uint32le dwGenre;
- uint32le dwMorphology;
- };
- MPT_BINARY_STRUCT(SFPresetHeader, 38)
- struct SFPresetBag
- {
- uint16le wGenNdx;
- uint16le wModNdx;
- };
- MPT_BINARY_STRUCT(SFPresetBag, 4)
- struct SFGenList
- {
- uint16le sfGenOper;
- uint16le genAmount;
- };
- MPT_BINARY_STRUCT(SFGenList, 4)
- struct SFInst
- {
- char achInstName[20];
- uint16le wInstBagNdx;
- };
- MPT_BINARY_STRUCT(SFInst, 22)
- struct SFInstBag
- {
- uint16le wGenNdx;
- uint16le wModNdx;
- };
- MPT_BINARY_STRUCT(SFInstBag, 4)
- struct SFInstGenList
- {
- uint16le sfGenOper;
- uint16le genAmount;
- };
- MPT_BINARY_STRUCT(SFInstGenList, 4)
- struct SFSample
- {
- char achSampleName[20];
- uint32le dwStart;
- uint32le dwEnd;
- uint32le dwStartloop;
- uint32le dwEndloop;
- uint32le dwSampleRate;
- uint8le byOriginalPitch;
- int8le chPitchCorrection;
- uint16le wSampleLink;
- uint16le sfSampleType;
- };
- MPT_BINARY_STRUCT(SFSample, 46)
- // End of structures definitions
- /////////////////////////////////////////////////////////////////////
- struct SF2LoaderInfo
- {
- FileReader presetBags;
- FileReader presetGens;
- FileReader insts;
- FileReader instBags;
- FileReader instGens;
- };
- /////////////////////////////////////////////////////////////////////
- // Unit conversion
- static uint8 DLSSustainLevelToLinear(int32 sustain)
- {
- // 0.1% units
- if(sustain >= 0)
- {
- int32 l = sustain / (1000 * 512);
- if(l >= 0 && l <= 128)
- return static_cast<uint8>(l);
- }
- return 128;
- }
- static uint8 SF2SustainLevelToLinear(int32 sustain)
- {
- // 0.1% units
- int32 l = 128 * (1000 - Clamp(sustain, 0, 1000)) / 1000;
- return static_cast<uint8>(l);
- }
- int32 CDLSBank::DLS32BitTimeCentsToMilliseconds(int32 lTimeCents)
- {
- // tc = log2(time[secs]) * 1200*65536
- // time[secs] = 2^(tc/(1200*65536))
- if ((uint32)lTimeCents == 0x80000000) return 0;
- double fmsecs = 1000.0 * std::pow(2.0, ((double)lTimeCents)/(1200.0*65536.0));
- if (fmsecs < -32767) return -32767;
- if (fmsecs > 32767) return 32767;
- return (int32)fmsecs;
- }
- // 0dB = 0x10000
- int32 CDLSBank::DLS32BitRelativeGainToLinear(int32 lCentibels)
- {
- // v = 10^(cb/(200*65536)) * V
- return (int32)(65536.0 * std::pow(10.0, ((double)lCentibels)/(200*65536.0)) );
- }
- int32 CDLSBank::DLS32BitRelativeLinearToGain(int32 lGain)
- {
- // cb = log10(v/V) * 200 * 65536
- if (lGain <= 0) return -960 * 65536;
- return (int32)(200 * 65536.0 * std::log10(((double)lGain) / 65536.0));
- }
- int32 CDLSBank::DLSMidiVolumeToLinear(uint32 nMidiVolume)
- {
- return (nMidiVolume * nMidiVolume << 16) / (127*127);
- }
- /////////////////////////////////////////////////////////////////////
- // Implementation
- CDLSBank::CDLSBank()
- {
- m_nMaxWaveLink = 0;
- m_nType = SOUNDBANK_TYPE_INVALID;
- }
- bool CDLSBank::IsDLSBank(const mpt::PathString &filename)
- {
- RIFFChunkID riff;
- if(filename.empty()) return false;
- mpt::ifstream f(filename, std::ios::binary);
- if(!f)
- {
- return false;
- }
- MemsetZero(riff);
- mpt::IO::Read(f, riff);
- // Check for embedded DLS sections
- if(riff.id_RIFF == IFFID_FORM)
- {
- // Miles Sound System
- do
- {
- uint32 len = mpt::bit_cast<uint32be>(riff.riff_len);
- if (len <= 4) break;
- if (riff.id_DLS == IFFID_XDLS)
- {
- mpt::IO::Read(f, riff);
- break;
- }
- if((len % 2u) != 0)
- len++;
- if (!mpt::IO::SeekRelative(f, len-4)) break;
- } while (mpt::IO::Read(f, riff));
- } else if(riff.id_RIFF == IFFID_RIFF && riff.id_DLS == IFFID_RMID)
- {
- for (;;)
- {
- if(!mpt::IO::Read(f, riff))
- break;
- if (riff.id_DLS == IFFID_DLS)
- break; // found it
- int len = riff.riff_len;
- if((len % 2u) != 0)
- len++;
- if ((len <= 4) || !mpt::IO::SeekRelative(f, len-4)) break;
- }
- }
- return ((riff.id_RIFF == IFFID_RIFF)
- && ((riff.id_DLS == IFFID_DLS) || (riff.id_DLS == IFFID_MLS) || (riff.id_DLS == IFFID_sfbk))
- && (riff.riff_len >= 256));
- }
- ///////////////////////////////////////////////////////////////
- // Find an instrument based on the given parameters
- const DLSINSTRUMENT *CDLSBank::FindInstrument(bool isDrum, uint32 bank, uint32 program, uint32 key, uint32 *pInsNo) const
- {
- // This helps finding the "more correct" instrument if we search for an instrument in any bank, and the higher-bank instruments appear first in the file
- // Fixes issues when loading GeneralUser GS into OpenMPT's MIDI library.
- std::vector<std::reference_wrapper<const DLSINSTRUMENT>> sortedInstr{m_Instruments.begin(), m_Instruments.end()};
- if(bank >= 0x4000 || program >= 0x80)
- {
- std::sort(sortedInstr.begin(), sortedInstr.end(), [](const DLSINSTRUMENT &l, const DLSINSTRUMENT &r)
- { return std::tie(l.ulBank, l.ulInstrument) < std::tie(r.ulBank, r.ulInstrument); });
- }
- for(const DLSINSTRUMENT &dlsIns : sortedInstr)
- {
- uint32 insbank = ((dlsIns.ulBank & 0x7F00) >> 1) | (dlsIns.ulBank & 0x7F);
- if((bank >= 0x4000) || (insbank == bank))
- {
- if(isDrum && (dlsIns.ulBank & F_INSTRUMENT_DRUMS))
- {
- if((program >= 0x80) || (program == (dlsIns.ulInstrument & 0x7F)))
- {
- for(const auto ®ion : dlsIns.Regions)
- {
- if(region.IsDummy())
- continue;
- if((!key || key >= 0x80)
- || (key >= region.uKeyMin && key <= region.uKeyMax))
- {
- if(pInsNo)
- *pInsNo = static_cast<uint32>(std::distance(m_Instruments.data(), &dlsIns));
- // cppcheck false-positive
- // cppcheck-suppress returnDanglingLifetime
- return &dlsIns;
- }
- }
- }
- } else if(!isDrum && !(dlsIns.ulBank & F_INSTRUMENT_DRUMS))
- {
- if((program >= 0x80) || (program == (dlsIns.ulInstrument & 0x7F)))
- {
- if(pInsNo)
- *pInsNo = static_cast<uint32>(std::distance(m_Instruments.data(), &dlsIns));
- // cppcheck false-positive
- // cppcheck-suppress returnDanglingLifetime
- return &dlsIns;
- }
- }
- }
- }
- return nullptr;
- }
- bool CDLSBank::FindAndExtract(CSoundFile &sndFile, const INSTRUMENTINDEX ins, const bool isDrum) const
- {
- ModInstrument *pIns = sndFile.Instruments[ins];
- if(pIns == nullptr)
- return false;
- uint32 dlsIns = 0, drumRgn = 0;
- const uint32 program = (pIns->nMidiProgram != 0) ? pIns->nMidiProgram - 1 : 0;
- const uint32 key = isDrum ? (pIns->nMidiDrumKey & 0x7F) : 0xFF;
- if(FindInstrument(isDrum, (pIns->wMidiBank - 1) & 0x3FFF, program, key, &dlsIns)
- || FindInstrument(isDrum, (pIns->wMidiBank - 1) & 0x3F80, program, key, &dlsIns)
- || FindInstrument(isDrum, 0xFFFF, isDrum ? 0xFF : program, key, &dlsIns))
- {
- if(key < 0x80) drumRgn = GetRegionFromKey(dlsIns, key);
- if(ExtractInstrument(sndFile, ins, dlsIns, drumRgn))
- {
- pIns = sndFile.Instruments[ins]; // Reset pointer because ExtractInstrument may delete the previous value.
- if((key >= 24) && (key < 24 + std::size(szMidiPercussionNames)))
- {
- pIns->name = szMidiPercussionNames[key - 24];
- }
- return true;
- }
- }
- return false;
- }
- ///////////////////////////////////////////////////////////////
- // Update DLS instrument definition from an IFF chunk
- bool CDLSBank::UpdateInstrumentDefinition(DLSINSTRUMENT *pDlsIns, FileReader chunk)
- {
- IFFCHUNK header;
- chunk.ReadStruct(header);
- if(!header.len || !chunk.CanRead(header.len))
- return false;
- if(header.id == IFFID_LIST)
- {
- uint32 listid = chunk.ReadUint32LE();
- while(chunk.CanRead(sizeof(IFFCHUNK)))
- {
- IFFCHUNK subHeader;
- chunk.ReadStruct(subHeader);
- chunk.SkipBack(sizeof(IFFCHUNK));
- FileReader subData = chunk.ReadChunk(subHeader.len + sizeof(IFFCHUNK));
- if(subHeader.len & 1)
- {
- chunk.Skip(1);
- }
- UpdateInstrumentDefinition(pDlsIns, subData);
- }
- switch(listid)
- {
- case IFFID_rgn: // Level 1 region
- case IFFID_rgn2: // Level 2 region
- pDlsIns->Regions.push_back({});
- break;
- }
- } else
- {
- switch(header.id)
- {
- case IFFID_insh:
- {
- INSHChunk insh;
- chunk.ReadStruct(insh);
- pDlsIns->ulBank = insh.ulBank;
- pDlsIns->ulInstrument = insh.ulInstrument;
- //Log("%3d regions, bank 0x%04X instrument %3d\n", insh.cRegions, pDlsIns->ulBank, pDlsIns->ulInstrument);
- break;
- }
- case IFFID_rgnh:
- if(!pDlsIns->Regions.empty())
- {
- RGNHChunk rgnh;
- chunk.ReadStruct(rgnh);
- DLSREGION ®ion = pDlsIns->Regions.back();
- region.uKeyMin = (uint8)rgnh.RangeKey.usLow;
- region.uKeyMax = (uint8)rgnh.RangeKey.usHigh;
- region.fuOptions = (uint8)(rgnh.usKeyGroup & DLSREGION_KEYGROUPMASK);
- if(rgnh.fusOptions & F_RGN_OPTION_SELFNONEXCLUSIVE)
- region.fuOptions |= DLSREGION_SELFNONEXCLUSIVE;
- //Log(" Region %d: fusOptions=0x%02X usKeyGroup=0x%04X ", pDlsIns->nRegions, rgnh.fusOptions, rgnh.usKeyGroup);
- //Log("KeyRange[%3d,%3d] ", rgnh.RangeKey.usLow, rgnh.RangeKey.usHigh);
- }
- break;
- case IFFID_wlnk:
- if (!pDlsIns->Regions.empty())
- {
- WLNKChunk wlnk;
- chunk.ReadStruct(wlnk);
- DLSREGION ®ion = pDlsIns->Regions.back();
- region.nWaveLink = (uint16)wlnk.ulTableIndex;
- if((region.nWaveLink < Util::MaxValueOfType(region.nWaveLink)) && (region.nWaveLink >= m_nMaxWaveLink))
- m_nMaxWaveLink = region.nWaveLink + 1;
- //Log(" WaveLink %d: fusOptions=0x%02X usPhaseGroup=0x%04X ", pDlsIns->nRegions, wlnk.fusOptions, wlnk.usPhaseGroup);
- //Log("ulChannel=%d ulTableIndex=%4d\n", wlnk.ulChannel, wlnk.ulTableIndex);
- }
- break;
- case IFFID_wsmp:
- if(!pDlsIns->Regions.empty())
- {
- DLSREGION ®ion = pDlsIns->Regions.back();
- WSMPChunk wsmp;
- chunk.ReadStruct(wsmp);
- region.fuOptions |= DLSREGION_OVERRIDEWSMP;
- region.uUnityNote = (uint8)wsmp.usUnityNote;
- region.sFineTune = wsmp.sFineTune;
- int32 lVolume = DLS32BitRelativeGainToLinear(wsmp.lAttenuation) / 256;
- if (lVolume > 256) lVolume = 256;
- if (lVolume < 4) lVolume = 4;
- region.usVolume = (uint16)lVolume;
- //Log(" WaveSample %d: usUnityNote=%2d sFineTune=%3d ", pDlsEnv->nRegions, p->usUnityNote, p->sFineTune);
- //Log("fulOptions=0x%04X loops=%d\n", p->fulOptions, p->cSampleLoops);
- if((wsmp.cSampleLoops) && (wsmp.cbSize + sizeof(WSMPSampleLoop) <= header.len))
- {
- WSMPSampleLoop loop;
- chunk.Seek(sizeof(IFFCHUNK) + wsmp.cbSize);
- chunk.ReadStruct(loop);
- //Log("looptype=%2d loopstart=%5d loopend=%5d\n", ploop->ulLoopType, ploop->ulLoopStart, ploop->ulLoopLength);
- if(loop.ulLoopLength > 3)
- {
- region.fuOptions |= DLSREGION_SAMPLELOOP;
- //if(loop.ulLoopType) region.fuOptions |= DLSREGION_PINGPONGLOOP;
- region.ulLoopStart = loop.ulLoopStart;
- region.ulLoopEnd = loop.ulLoopStart + loop.ulLoopLength;
- }
- }
- }
- break;
- case IFFID_art1:
- case IFFID_art2:
- {
- ART1Chunk art1;
- chunk.ReadStruct(art1);
- if(!(pDlsIns->ulBank & F_INSTRUMENT_DRUMS))
- {
- pDlsIns->nMelodicEnv = static_cast<uint32>(m_Envelopes.size() + 1);
- }
- if(art1.cbSize + art1.cConnectionBlocks * sizeof(ConnectionBlock) > header.len)
- break;
- DLSENVELOPE dlsEnv;
- MemsetZero(dlsEnv);
- dlsEnv.nDefPan = 128;
- dlsEnv.nVolSustainLevel = 128;
- //Log(" art1 (%3d bytes): cbSize=%d cConnectionBlocks=%d\n", p->len, p->cbSize, p->cConnectionBlocks);
- chunk.Seek(sizeof(IFFCHUNK) + art1.cbSize);
- for (uint32 iblk = 0; iblk < art1.cConnectionBlocks; iblk++)
- {
- ConnectionBlock blk;
- chunk.ReadStruct(blk);
- // [4-bit transform][12-bit dest][8-bit control][8-bit source] = 32-bit ID
- uint32 dwArticulation = blk.usTransform;
- dwArticulation = (dwArticulation << 12) | (blk.usDestination & 0x0FFF);
- dwArticulation = (dwArticulation << 8) | (blk.usControl & 0x00FF);
- dwArticulation = (dwArticulation << 8) | (blk.usSource & 0x00FF);
- switch(dwArticulation)
- {
- case ART_DEFAULTPAN:
- {
- int32 pan = 128 + blk.lScale / (65536000/128);
- dlsEnv.nDefPan = mpt::saturate_cast<uint8>(pan);
- }
- break;
- case ART_VOL_EG_ATTACKTIME:
- // 32-bit time cents units. range = [0s, 20s]
- dlsEnv.wVolAttack = 0;
- if(blk.lScale > -0x40000000)
- {
- int32 l = blk.lScale - 78743200; // maximum velocity
- if (l > 0) l = 0;
- int32 attacktime = DLS32BitTimeCentsToMilliseconds(l);
- if (attacktime < 0) attacktime = 0;
- if (attacktime > 20000) attacktime = 20000;
- if (attacktime >= 20) dlsEnv.wVolAttack = (uint16)(attacktime / 20);
- //Log("%3d: Envelope Attack Time set to %d (%d time cents)\n", (uint32)(dlsEnv.ulInstrument & 0x7F)|((dlsEnv.ulBank >> 16) & 0x8000), attacktime, pblk->lScale);
- }
- break;
- case ART_VOL_EG_DECAYTIME:
- // 32-bit time cents units. range = [0s, 20s]
- dlsEnv.wVolDecay = 0;
- if(blk.lScale > -0x40000000)
- {
- int32 decaytime = DLS32BitTimeCentsToMilliseconds(blk.lScale);
- if (decaytime > 20000) decaytime = 20000;
- if (decaytime >= 20) dlsEnv.wVolDecay = (uint16)(decaytime / 20);
- //Log("%3d: Envelope Decay Time set to %d (%d time cents)\n", (uint32)(dlsEnv.ulInstrument & 0x7F)|((dlsEnv.ulBank >> 16) & 0x8000), decaytime, pblk->lScale);
- }
- break;
- case ART_VOL_EG_RELEASETIME:
- // 32-bit time cents units. range = [0s, 20s]
- dlsEnv.wVolRelease = 0;
- if(blk.lScale > -0x40000000)
- {
- int32 releasetime = DLS32BitTimeCentsToMilliseconds(blk.lScale);
- if (releasetime > 20000) releasetime = 20000;
- if (releasetime >= 20) dlsEnv.wVolRelease = (uint16)(releasetime / 20);
- //Log("%3d: Envelope Release Time set to %d (%d time cents)\n", (uint32)(dlsEnv.ulInstrument & 0x7F)|((dlsEnv.ulBank >> 16) & 0x8000), dlsEnv.wVolRelease, pblk->lScale);
- }
- break;
- case ART_VOL_EG_SUSTAINLEVEL:
- // 0.1% units
- if(blk.lScale >= 0)
- {
- dlsEnv.nVolSustainLevel = DLSSustainLevelToLinear(blk.lScale);
- }
- break;
- //default:
- // Log(" Articulation = 0x%08X value=%d\n", dwArticulation, blk.lScale);
- }
- }
- m_Envelopes.push_back(dlsEnv);
- }
- break;
- case IFFID_INAM:
- chunk.ReadString<mpt::String::spacePadded>(pDlsIns->szName, header.len);
- break;
- #if 0
- default:
- {
- char sid[5];
- memcpy(sid, &header.id, 4);
- sid[4] = 0;
- Log(" \"%s\": %d bytes\n", (uint32)sid, header.len.get());
- }
- #endif
- }
- }
- return true;
- }
- ///////////////////////////////////////////////////////////////
- // Converts SF2 chunks to DLS
- bool CDLSBank::UpdateSF2PresetData(SF2LoaderInfo &sf2info, const IFFCHUNK &header, FileReader &chunk)
- {
- if (!chunk.IsValid()) return false;
- switch(header.id)
- {
- case IFFID_phdr:
- if(m_Instruments.empty())
- {
- uint32 numIns = static_cast<uint32>(chunk.GetLength() / sizeof(SFPresetHeader));
- if(numIns <= 1)
- break;
- // The terminal sfPresetHeader record should never be accessed, and exists only to provide a terminal wPresetBagNdx with which to determine the number of zones in the last preset.
- numIns--;
- m_Instruments.resize(numIns);
- #ifdef DLSBANK_LOG
- MPT_LOG_GLOBAL(LogDebug, "DLSBank", MPT_UFORMAT("phdr: {} instruments")(m_Instruments.size()));
- #endif
- SFPresetHeader psfh;
- chunk.ReadStruct(psfh);
- for(auto &dlsIns : m_Instruments)
- {
- mpt::String::WriteAutoBuf(dlsIns.szName) = mpt::String::ReadAutoBuf(psfh.achPresetName);
- dlsIns.ulInstrument = psfh.wPreset & 0x7F;
- dlsIns.ulBank = (psfh.wBank >= 128) ? F_INSTRUMENT_DRUMS : (psfh.wBank << 8);
- dlsIns.wPresetBagNdx = psfh.wPresetBagNdx;
- dlsIns.wPresetBagNum = 1;
- chunk.ReadStruct(psfh);
- if (psfh.wPresetBagNdx > dlsIns.wPresetBagNdx) dlsIns.wPresetBagNum = static_cast<uint16>(psfh.wPresetBagNdx - dlsIns.wPresetBagNdx);
- }
- }
- break;
- case IFFID_pbag:
- if(!m_Instruments.empty() && chunk.CanRead(sizeof(SFPresetBag)))
- {
- sf2info.presetBags = chunk.GetChunk(chunk.BytesLeft());
- }
- #ifdef DLSINSTR_LOG
- else MPT_LOG_GLOBAL(LogDebug, "DLSINSTR", U_("pbag: no instruments!"));
- #endif
- break;
- case IFFID_pgen:
- if(!m_Instruments.empty() && chunk.CanRead(sizeof(SFGenList)))
- {
- sf2info.presetGens = chunk.GetChunk(chunk.BytesLeft());
- }
- #ifdef DLSINSTR_LOG
- else MPT_LOG_GLOBAL(LogDebug, "DLSINSTR", U_("pgen: no instruments!"));
- #endif
- break;
- case IFFID_inst:
- if(!m_Instruments.empty() && chunk.CanRead(sizeof(SFInst)))
- {
- sf2info.insts = chunk.GetChunk(chunk.BytesLeft());
- }
- break;
- case IFFID_ibag:
- if(!m_Instruments.empty() && chunk.CanRead(sizeof(SFInstBag)))
- {
- sf2info.instBags = chunk.GetChunk(chunk.BytesLeft());
- }
- break;
- case IFFID_igen:
- if(!m_Instruments.empty() && chunk.CanRead(sizeof(SFInstGenList)))
- {
- sf2info.instGens = chunk.GetChunk(chunk.BytesLeft());
- }
- break;
- case IFFID_shdr:
- if (m_SamplesEx.empty())
- {
- uint32 numSmp = static_cast<uint32>(chunk.GetLength() / sizeof(SFSample));
- if (numSmp < 1) break;
- m_SamplesEx.resize(numSmp);
- m_WaveForms.resize(numSmp);
- #ifdef DLSINSTR_LOG
- MPT_LOG_GLOBAL(LogDebug, "DLSINSTR", MPT_UFORMAT("shdr: {} samples")(m_SamplesEx.size()));
- #endif
- for (uint32 i = 0; i < numSmp; i++)
- {
- SFSample p;
- chunk.ReadStruct(p);
- DLSSAMPLEEX &dlsSmp = m_SamplesEx[i];
- mpt::String::WriteAutoBuf(dlsSmp.szName) = mpt::String::ReadAutoBuf(p.achSampleName);
- dlsSmp.dwLen = 0;
- dlsSmp.dwSampleRate = p.dwSampleRate;
- dlsSmp.byOriginalPitch = p.byOriginalPitch;
- dlsSmp.chPitchCorrection = static_cast<int8>(Util::muldivr(p.chPitchCorrection, 128, 100));
- // cognitone's sf2convert tool doesn't set the correct sample flags (0x01 / 0x02 instead of 0x10/ 0x20).
- // For SF3, we ignore this and go by https://github.com/FluidSynth/fluidsynth/wiki/SoundFont3Format instead
- // As cognitone's tool is the only tool writing SF4 files, we always assume compressed samples with SF4 files if bits 0/1 are set.
- uint16 sampleType = p.sfSampleType;
- if(m_sf2version >= 0x4'0000 && m_sf2version <= 0x4'FFFF && (sampleType & 0x03))
- sampleType = (sampleType & 0xFFFC) | 0x10;
- dlsSmp.compressed = (sampleType & 0x10);
- if(((sampleType & 0x7FCF) <= 4) && (p.dwEnd >= p.dwStart + 4))
- {
- m_WaveForms[i] = p.dwStart;
- dlsSmp.dwLen = (p.dwEnd - p.dwStart);
- if(!dlsSmp.compressed)
- {
- m_WaveForms[i] *= 2;
- dlsSmp.dwLen *= 2;
- if((p.dwEndloop > p.dwStartloop + 7) && (p.dwStartloop >= p.dwStart))
- {
- dlsSmp.dwStartloop = p.dwStartloop - p.dwStart;
- dlsSmp.dwEndloop = p.dwEndloop - p.dwStart;
- }
- } else
- {
- if(p.dwEndloop > p.dwStartloop + 7)
- {
- dlsSmp.dwStartloop = p.dwStartloop;
- dlsSmp.dwEndloop = p.dwEndloop;
- }
- }
- }
- }
- }
- break;
- #ifdef DLSINSTR_LOG
- default:
- {
- char sdbg[5];
- memcpy(sdbg, &header.id, 4);
- sdbg[4] = 0;
- MPT_LOG_GLOBAL(LogDebug, "DLSINSTR", MPT_UFORMAT("Unsupported SF2 chunk: {} ({} bytes)")(mpt::ToUnicode(mpt::Charset::ASCII, mpt::String::ReadAutoBuf(sdbg)), header.len.get()));
- }
- #endif
- }
- return true;
- }
- static int16 SF2TimeToDLS(int16 amount)
- {
- int32 time = CDLSBank::DLS32BitTimeCentsToMilliseconds(static_cast<int32>(amount) << 16);
- return static_cast<int16>(Clamp(time, 20, 20000) / 20);
- }
- // Convert all instruments to the DLS format
- bool CDLSBank::ConvertSF2ToDLS(SF2LoaderInfo &sf2info)
- {
- if (m_Instruments.empty() || m_SamplesEx.empty())
- return false;
- const uint32 numInsts = static_cast<uint32>(sf2info.insts.GetLength() / sizeof(SFInst));
- const uint32 numInstBags = static_cast<uint32>(sf2info.instBags.GetLength() / sizeof(SFInstBag));
- std::vector<std::pair<uint16, uint16>> instruments; // instrument, key range
- std::vector<SFGenList> generators;
- std::vector<SFInstGenList> instrGenerators;
- for(auto &dlsIns : m_Instruments)
- {
- instruments.clear();
- DLSENVELOPE dlsEnv;
- int32 instrAttenuation = 0;
- int16 instrFinetune = 0;
- // Default Envelope Values
- dlsEnv.wVolAttack = 0;
- dlsEnv.wVolDecay = 0;
- dlsEnv.wVolRelease = 0;
- dlsEnv.nVolSustainLevel = 128;
- dlsEnv.nDefPan = 128;
- // Load Preset Bags
- sf2info.presetBags.Seek(dlsIns.wPresetBagNdx * sizeof(SFPresetBag));
- for(uint32 ipbagcnt = 0; ipbagcnt < dlsIns.wPresetBagNum; ipbagcnt++)
- {
- // Load generators for each preset bag
- SFPresetBag bag[2];
- if(!sf2info.presetBags.ReadArray(bag))
- break;
- sf2info.presetBags.SkipBack(sizeof(SFPresetBag));
- sf2info.presetGens.Seek(bag[0].wGenNdx * sizeof(SFGenList));
- uint16 keyRange = 0xFFFF;
- if(!sf2info.presetGens.ReadVector(generators, bag[1].wGenNdx - bag[0].wGenNdx))
- continue;
- for(const auto &gen : generators)
- {
- const int16 value = static_cast<int16>(gen.genAmount);
- switch(gen.sfGenOper)
- {
- case SF2_GEN_ATTACKVOLENV:
- dlsEnv.wVolAttack = SF2TimeToDLS(gen.genAmount);
- break;
- case SF2_GEN_DECAYVOLENV:
- dlsEnv.wVolDecay = SF2TimeToDLS(gen.genAmount);
- break;
- case SF2_GEN_SUSTAINVOLENV:
- // 0.1% units
- if(gen.genAmount >= 0)
- {
- dlsEnv.nVolSustainLevel = SF2SustainLevelToLinear(gen.genAmount);
- }
- break;
- case SF2_GEN_RELEASEVOLENV:
- dlsEnv.wVolRelease = SF2TimeToDLS(gen.genAmount);
- break;
- case SF2_GEN_INSTRUMENT:
- if(const auto instr = std::make_pair(gen.genAmount.get(), keyRange); !mpt::contains(instruments, instr))
- instruments.push_back(instr);
- keyRange = 0xFFFF;
- break;
- case SF2_GEN_KEYRANGE:
- keyRange = gen.genAmount;
- break;
- case SF2_GEN_ATTENUATION:
- instrAttenuation = -value;
- break;
- case SF2_GEN_COARSETUNE:
- instrFinetune += value * 128;
- break;
- case SF2_GEN_FINETUNE:
- instrFinetune += static_cast<int16>(Util::muldiv(static_cast<int8>(value), 128, 100));
- break;
- #if 0
- default:
- Log("Ins %3d: bag %3d gen %3d: ", nIns, ipbagndx, ipgenndx);
- Log("genoper=%d amount=0x%04X ", gen.sfGenOper, gen.genAmount);
- Log((pSmp->ulBank & F_INSTRUMENT_DRUMS) ? "(drum)\n" : "\n");
- #endif
- }
- }
- }
- // Envelope
- if (!(dlsIns.ulBank & F_INSTRUMENT_DRUMS))
- {
- m_Envelopes.push_back(dlsEnv);
- dlsIns.nMelodicEnv = static_cast<uint32>(m_Envelopes.size());
- }
- // Load Instrument Bags
- dlsIns.Regions.clear();
- for(const auto & [nInstrNdx, keyRange] : instruments)
- {
- if(nInstrNdx >= numInsts)
- continue;
- sf2info.insts.Seek(nInstrNdx * sizeof(SFInst));
- SFInst insts[2];
- sf2info.insts.ReadArray(insts);
- const uint32 numRegions = insts[1].wInstBagNdx - insts[0].wInstBagNdx;
- dlsIns.Regions.reserve(dlsIns.Regions.size() + numRegions);
- //Log("\nIns %3d, %2d regions:\n", nIns, pSmp->nRegions);
- DLSREGION globalZone{};
- globalZone.uUnityNote = 0xFF; // 0xFF means undefined -> use sample root note
- globalZone.tuning = 100;
- globalZone.sFineTune = instrFinetune;
- globalZone.nWaveLink = Util::MaxValueOfType(globalZone.nWaveLink);
- if(keyRange != 0xFFFF)
- {
- globalZone.uKeyMin = static_cast<uint8>(keyRange & 0xFF);
- globalZone.uKeyMax = static_cast<uint8>(keyRange >> 8);
- if(globalZone.uKeyMin > globalZone.uKeyMax)
- std::swap(globalZone.uKeyMin, globalZone.uKeyMax);
- } else
- {
- globalZone.uKeyMin = 0;
- globalZone.uKeyMax = 127;
- }
- for(uint32 nRgn = 0; nRgn < numRegions; nRgn++)
- {
- uint32 ibagcnt = insts[0].wInstBagNdx + nRgn;
- if(ibagcnt >= numInstBags)
- break;
- // Create a new envelope for drums
- DLSENVELOPE *pDlsEnv = &dlsEnv;
- if(!(dlsIns.ulBank & F_INSTRUMENT_DRUMS) && dlsIns.nMelodicEnv > 0 && dlsIns.nMelodicEnv <= m_Envelopes.size())
- {
- pDlsEnv = &m_Envelopes[dlsIns.nMelodicEnv - 1];
- }
- DLSREGION rgn = globalZone;
- // Region Default Values
- int32 regionAttn = 0;
- // Load Generators
- sf2info.instBags.Seek(ibagcnt * sizeof(SFInstBag));
- SFInstBag bags[2];
- sf2info.instBags.ReadArray(bags);
- sf2info.instGens.Seek(bags[0].wGenNdx * sizeof(SFInstGenList));
- uint16 lastOp = SF2_GEN_SAMPLEID;
- int32 loopStart = 0, loopEnd = 0;
- if(!sf2info.instGens.ReadVector(instrGenerators, bags[1].wGenNdx - bags[0].wGenNdx))
- break;
- for(const auto &gen : instrGenerators)
- {
- uint16 value = gen.genAmount;
- lastOp = gen.sfGenOper;
- switch(gen.sfGenOper)
- {
- case SF2_GEN_KEYRANGE:
- {
- uint8 keyMin = static_cast<uint8>(value & 0xFF);
- uint8 keyMax = static_cast<uint8>(value >> 8);
- if(keyMin > keyMax)
- std::swap(keyMin, keyMax);
- rgn.uKeyMin = std::max(rgn.uKeyMin, keyMin);
- rgn.uKeyMax = std::min(rgn.uKeyMax, keyMax);
- // There was no overlap between instrument region and preset region - skip it
- if(rgn.uKeyMin > rgn.uKeyMax)
- rgn.uKeyMin = rgn.uKeyMax = 0xFF;
- }
- break;
- case SF2_GEN_UNITYNOTE:
- if (value < 128) rgn.uUnityNote = static_cast<uint8>(value);
- break;
- case SF2_GEN_ATTACKVOLENV:
- pDlsEnv->wVolAttack = SF2TimeToDLS(gen.genAmount);
- break;
- case SF2_GEN_DECAYVOLENV:
- pDlsEnv->wVolDecay = SF2TimeToDLS(gen.genAmount);
- break;
- case SF2_GEN_SUSTAINVOLENV:
- // 0.1% units
- if(gen.genAmount >= 0)
- {
- pDlsEnv->nVolSustainLevel = SF2SustainLevelToLinear(gen.genAmount);
- }
- break;
- case SF2_GEN_RELEASEVOLENV:
- pDlsEnv->wVolRelease = SF2TimeToDLS(gen.genAmount);
- break;
- case SF2_GEN_PAN:
- {
- int32 pan = static_cast<int16>(value);
- pan = std::clamp(Util::muldivr(pan + 500, 256, 1000), 0, 256);
- rgn.panning = static_cast<int16>(pan);
- pDlsEnv->nDefPan = mpt::saturate_cast<uint8>(pan);
- }
- break;
- case SF2_GEN_ATTENUATION:
- regionAttn = -static_cast<int16>(value);
- break;
- case SF2_GEN_SAMPLEID:
- if (value < m_SamplesEx.size())
- {
- rgn.nWaveLink = value;
- rgn.ulLoopStart = mpt::saturate_cast<uint32>(m_SamplesEx[value].dwStartloop + loopStart);
- rgn.ulLoopEnd = mpt::saturate_cast<uint32>(m_SamplesEx[value].dwEndloop + loopEnd);
- }
- break;
- case SF2_GEN_SAMPLEMODES:
- value &= 3;
- rgn.fuOptions &= uint16(~(DLSREGION_SAMPLELOOP|DLSREGION_PINGPONGLOOP|DLSREGION_SUSTAINLOOP));
- if(value == 1)
- rgn.fuOptions |= DLSREGION_SAMPLELOOP;
- else if(value == 2)
- rgn.fuOptions |= DLSREGION_SAMPLELOOP | DLSREGION_PINGPONGLOOP;
- else if(value == 3)
- rgn.fuOptions |= DLSREGION_SAMPLELOOP | DLSREGION_SUSTAINLOOP;
- rgn.fuOptions |= DLSREGION_OVERRIDEWSMP;
- break;
- case SF2_GEN_KEYGROUP:
- rgn.fuOptions |= (value & DLSREGION_KEYGROUPMASK);
- break;
- case SF2_GEN_COARSETUNE:
- rgn.sFineTune += static_cast<int16>(value) * 128;
- break;
- case SF2_GEN_FINETUNE:
- rgn.sFineTune += static_cast<int16>(Util::muldiv(static_cast<int8>(value), 128, 100));
- break;
- case SF2_GEN_SCALE_TUNING:
- rgn.tuning = mpt::saturate_cast<uint8>(value);
- break;
- case SF2_GEN_START_LOOP_FINE:
- loopStart += static_cast<int16>(value);
- break;
- case SF2_GEN_END_LOOP_FINE:
- loopEnd += static_cast<int16>(value);
- break;
- case SF2_GEN_START_LOOP_COARSE:
- loopStart += static_cast<int16>(value) * 32768;
- break;
- case SF2_GEN_END_LOOP_COARSE:
- loopEnd += static_cast<int16>(value) * 32768;
- break;
- //default:
- // Log(" gen=%d value=%04X\n", pgen->sfGenOper, pgen->genAmount);
- }
- }
- int32 linearVol = DLS32BitRelativeGainToLinear(((instrAttenuation + regionAttn) * 65536) / 10) / 256;
- Limit(linearVol, 16, 256);
- rgn.usVolume = static_cast<uint16>(linearVol);
- if(lastOp != SF2_GEN_SAMPLEID && nRgn == 0)
- globalZone = rgn;
- else if(!rgn.IsDummy())
- dlsIns.Regions.push_back(rgn);
- //Log("\n");
- }
- }
- }
- return true;
- }
- ///////////////////////////////////////////////////////////////
- // Open: opens a DLS bank
- bool CDLSBank::Open(const mpt::PathString &filename)
- {
- if(filename.empty()) return false;
- m_szFileName = filename;
- InputFile f(filename, SettingCacheCompleteFileBeforeLoading());
- if(!f.IsValid()) return false;
- return Open(GetFileReader(f));
- }
- bool CDLSBank::Open(FileReader file)
- {
- uint32 nInsDef;
- if(file.GetOptionalFileName())
- m_szFileName = file.GetOptionalFileName().value();
- file.Rewind();
- size_t dwMemLength = file.GetLength();
- size_t dwMemPos = 0;
- if(!file.CanRead(256))
- {
- return false;
- }
- RIFFChunkID riff;
- file.ReadStruct(riff);
- // Check DLS sections embedded in RMI midi files
- if(riff.id_RIFF == IFFID_RIFF && riff.id_DLS == IFFID_RMID)
- {
- while(file.ReadStruct(riff))
- {
- if(riff.id_RIFF == IFFID_RIFF && riff.id_DLS == IFFID_DLS)
- {
- file.SkipBack(sizeof(riff));
- break;
- }
- uint32 len = riff.riff_len;
- if((len % 2u) != 0)
- len++;
- file.SkipBack(4);
- file.Skip(len);
- }
- }
- // Check XDLS sections embedded in big endian IFF files (Miles Sound System)
- if (riff.id_RIFF == IFFID_FORM)
- {
- do
- {
- if(riff.id_DLS == IFFID_XDLS)
- {
- file.ReadStruct(riff);
- break;
- }
- uint32 len = mpt::bit_cast<uint32be>(riff.riff_len);
- if((len % 2u) != 0)
- len++;
- file.SkipBack(4);
- file.Skip(len);
- } while(file.ReadStruct(riff));
- }
- if (riff.id_RIFF != IFFID_RIFF
- || (riff.id_DLS != IFFID_DLS && riff.id_DLS != IFFID_MLS && riff.id_DLS != IFFID_sfbk)
- || !file.CanRead(riff.riff_len - 4))
- {
- #ifdef DLSBANK_LOG
- MPT_LOG_GLOBAL(LogDebug, "DLSBANK", U_("Invalid DLS bank!"));
- #endif
- return false;
- }
- SF2LoaderInfo sf2info;
- m_nType = (riff.id_DLS == IFFID_sfbk) ? SOUNDBANK_TYPE_SF2 : SOUNDBANK_TYPE_DLS;
- m_dwWavePoolOffset = 0;
- m_sf2version = 0;
- m_Instruments.clear();
- m_WaveForms.clear();
- m_Envelopes.clear();
- nInsDef = 0;
- if (dwMemLength > 8 + riff.riff_len + dwMemPos) dwMemLength = 8 + riff.riff_len + dwMemPos;
- bool applyPaddingToSampleChunk = true;
- while(file.CanRead(sizeof(IFFCHUNK)))
- {
- IFFCHUNK chunkHeader;
- file.ReadStruct(chunkHeader);
- dwMemPos = file.GetPosition();
- FileReader chunk = file.ReadChunk(chunkHeader.len);
-
- bool applyPadding = (chunkHeader.len % 2u) != 0;
- if(!chunk.LengthIsAtLeast(chunkHeader.len))
- break;
- switch(chunkHeader.id)
- {
- // DLS 1.0: Instruments Collection Header
- case IFFID_colh:
- #ifdef DLSBANK_LOG
- MPT_LOG_GLOBAL(LogDebug, "DLSBANK", MPT_UFORMAT("colh ({} bytes)")(chunkHeader.len.get()));
- #endif
- if (m_Instruments.empty())
- {
- m_Instruments.resize(chunk.ReadUint32LE());
- #ifdef DLSBANK_LOG
- MPT_LOG_GLOBAL(LogDebug, "DLSBANK", MPT_UFORMAT(" {} instruments")(m_Instruments.size()));
- #endif
- }
- break;
- // DLS 1.0: Instruments Pointers Table
- case IFFID_ptbl:
- #ifdef DLSBANK_LOG
- MPT_LOG_GLOBAL(LogDebug, "DLSBANK", MPT_UFORMAT("ptbl ({} bytes)")(chunkHeader.len.get()));
- #endif
- if (m_WaveForms.empty())
- {
- PTBLChunk ptbl;
- chunk.ReadStruct(ptbl);
- chunk.Skip(ptbl.cbSize - 8);
- uint32 cues = std::min(ptbl.cCues.get(), mpt::saturate_cast<uint32>(chunk.BytesLeft() / sizeof(uint32)));
- m_WaveForms.reserve(cues);
- for(uint32 i = 0; i < cues; i++)
- {
- m_WaveForms.push_back(chunk.ReadUint32LE());
- }
- #ifdef DLSBANK_LOG
- MPT_LOG_GLOBAL(LogDebug, "DLSBANK", MPT_UFORMAT(" {} waveforms")(m_WaveForms.size()));
- #endif
- }
- break;
- // DLS 1.0: LIST section
- case IFFID_LIST:
- #ifdef DLSBANK_LOG
- MPT_LOG_GLOBAL(LogDebug, "DLSBANK", U_("LIST"));
- #endif
- {
- uint32 listid = chunk.ReadUint32LE();
- if (((listid == IFFID_wvpl) && (m_nType & SOUNDBANK_TYPE_DLS))
- || ((listid == IFFID_sdta) && (m_nType & SOUNDBANK_TYPE_SF2)))
- {
- m_dwWavePoolOffset = dwMemPos + 4;
- #ifdef DLSBANK_LOG
- MPT_LOG_GLOBAL(LogDebug, "DLSBANK", MPT_UFORMAT("Wave Pool offset: {}")(m_dwWavePoolOffset));
- #endif
- if(!applyPaddingToSampleChunk)
- applyPadding = false;
- break;
- }
- while (chunk.CanRead(12))
- {
- IFFCHUNK listHeader;
- chunk.ReadStruct(listHeader);
- if(!chunk.CanRead(listHeader.len))
- break;
- FileReader subData = chunk.GetChunkAt(chunk.GetPosition() - sizeof(IFFCHUNK), listHeader.len + 8);
- FileReader listChunk = chunk.ReadChunk(listHeader.len);
- if(listHeader.len % 2u)
- chunk.Skip(1);
- // DLS Instrument Headers
- if (listHeader.id == IFFID_LIST && (m_nType & SOUNDBANK_TYPE_DLS))
- {
- uint32 subID = listChunk.ReadUint32LE();
- if ((subID == IFFID_ins) && (nInsDef < m_Instruments.size()))
- {
- DLSINSTRUMENT &dlsIns = m_Instruments[nInsDef];
- //Log("Instrument %d:\n", nInsDef);
- dlsIns.Regions.push_back({});
- UpdateInstrumentDefinition(&dlsIns, subData);
- nInsDef++;
- }
- } else
- // DLS/SF2 Bank Information
- if (listid == IFFID_INFO && listHeader.len)
- {
- switch(listHeader.id)
- {
- case IFFID_ifil:
- m_sf2version = listChunk.ReadUint16LE() << 16;
- m_sf2version |= listChunk.ReadUint16LE();
- if(m_sf2version >= 0x3'0000 && m_sf2version <= 0x4'FFFF)
- {
- // "SF3" / "SF4" with compressed samples. The padding of the sample chunk is now optional (probably because it was simply forgotten to be added)
- applyPaddingToSampleChunk = false;
- }
- listChunk.Skip(2);
- break;
- case IFFID_INAM:
- listChunk.ReadString<mpt::String::maybeNullTerminated>(m_BankInfo.szBankName, listChunk.BytesLeft());
- break;
- case IFFID_IENG:
- listChunk.ReadString<mpt::String::maybeNullTerminated>(m_BankInfo.szEngineer, listChunk.BytesLeft());
- break;
- case IFFID_ICOP:
- listChunk.ReadString<mpt::String::maybeNullTerminated>(m_BankInfo.szCopyRight, listChunk.BytesLeft());
- break;
- case IFFID_ICMT:
- listChunk.ReadString<mpt::String::maybeNullTerminated>(m_BankInfo.szComments, listChunk.BytesLeft());
- break;
- case IFFID_ISFT:
- listChunk.ReadString<mpt::String::maybeNullTerminated>(m_BankInfo.szSoftware, listChunk.BytesLeft());
- break;
- case IFFID_ISBJ:
- listChunk.ReadString<mpt::String::maybeNullTerminated>(m_BankInfo.szDescription, listChunk.BytesLeft());
- break;
- }
- } else
- if ((listid == IFFID_pdta) && (m_nType & SOUNDBANK_TYPE_SF2))
- {
- UpdateSF2PresetData(sf2info, listHeader, listChunk);
- }
- }
- }
- break;
- #ifdef DLSBANK_LOG
- default:
- {
- char sdbg[5];
- memcpy(sdbg, &chunkHeader.id, 4);
- sdbg[4] = 0;
- MPT_LOG_GLOBAL(LogDebug, "DLSBANK", MPT_UFORMAT("Unsupported chunk: {} ({} bytes)")(mpt::ToUnicode(mpt::Charset::ASCII, mpt::String::ReadAutoBuf(sdbg)), chunkHeader.len.get()));
- }
- break;
- #endif
- }
- if(applyPadding)
- file.Skip(1);
- }
- // Build the ptbl is not present in file
- if ((m_WaveForms.empty()) && (m_dwWavePoolOffset) && (m_nType & SOUNDBANK_TYPE_DLS) && (m_nMaxWaveLink > 0))
- {
- #ifdef DLSBANK_LOG
- MPT_LOG_GLOBAL(LogDebug, "DLSBANK", MPT_UFORMAT("ptbl not present: building table ({} wavelinks)...")(m_nMaxWaveLink));
- #endif
- m_WaveForms.reserve(m_nMaxWaveLink);
- file.Seek(m_dwWavePoolOffset);
- while(m_WaveForms.size() < m_nMaxWaveLink && file.CanRead(sizeof(IFFCHUNK)))
- {
- IFFCHUNK chunk;
- file.ReadStruct(chunk);
- if (chunk.id == IFFID_LIST)
- m_WaveForms.push_back(file.GetPosition() - m_dwWavePoolOffset - sizeof(IFFCHUNK));
- file.Skip(chunk.len);
- }
- #ifdef DLSBANK_LOG
- MPT_LOG_GLOBAL(LogDebug, "DLSBANK", MPT_UFORMAT("Found {} waveforms")(m_WaveForms.size()));
- #endif
- }
- // Convert the SF2 data to DLS
- if ((m_nType & SOUNDBANK_TYPE_SF2) && !m_SamplesEx.empty() && !m_Instruments.empty())
- {
- ConvertSF2ToDLS(sf2info);
- }
- #ifdef DLSBANK_LOG
- MPT_LOG_GLOBAL(LogDebug, "DLSBANK", U_("DLS bank closed"));
- #endif
- return true;
- }
- ////////////////////////////////////////////////////////////////////////////////////////
- // Extracts the Waveforms from a DLS/SF2 bank
- uint32 CDLSBank::GetRegionFromKey(uint32 nIns, uint32 nKey) const
- {
- if(nIns >= m_Instruments.size())
- return 0;
- const DLSINSTRUMENT &dlsIns = m_Instruments[nIns];
- for(uint32 rgn = 0; rgn < static_cast<uint32>(dlsIns.Regions.size()); rgn++)
- {
- const auto ®ion = dlsIns.Regions[rgn];
- if(nKey < region.uKeyMin || nKey > region.uKeyMax)
- continue;
- if(region.nWaveLink == Util::MaxValueOfType(region.nWaveLink))
- continue;
- return rgn;
- }
- return 0;
- }
- bool CDLSBank::ExtractWaveForm(uint32 nIns, uint32 nRgn, std::vector<uint8> &waveData, uint32 &length) const
- {
- waveData.clear();
- length = 0;
- if (nIns >= m_Instruments.size() || !m_dwWavePoolOffset)
- {
- #ifdef DLSBANK_LOG
- MPT_LOG_GLOBAL(LogDebug, "DLSBANK", MPT_UFORMAT("ExtractWaveForm({}) failed: m_Instruments.size()={} m_dwWavePoolOffset={} m_WaveForms.size()={}")(nIns, m_Instruments.size(), m_dwWavePoolOffset, m_WaveForms.size()));
- #endif
- return false;
- }
- const DLSINSTRUMENT &dlsIns = m_Instruments[nIns];
- if(nRgn >= dlsIns.Regions.size())
- {
- #ifdef DLSBANK_LOG
- MPT_LOG_GLOBAL(LogDebug, "DLSBANK", MPT_UFORMAT("invalid waveform region: nIns={} nRgn={} pSmp->nRegions={}")(nIns, nRgn, dlsIns.Regions.size()));
- #endif
- return false;
- }
- uint32 nWaveLink = dlsIns.Regions[nRgn].nWaveLink;
- if(nWaveLink >= m_WaveForms.size())
- {
- #ifdef DLSBANK_LOG
- MPT_LOG_GLOBAL(LogDebug, "DLSBANK", MPT_UFORMAT("Invalid wavelink id: nWaveLink={} nWaveForms={}")(nWaveLink, m_WaveForms.size()));
- #endif
- return false;
- }
- mpt::ifstream f(m_szFileName, std::ios::binary);
- if(!f)
- {
- return false;
- }
- mpt::IO::Offset sampleOffset = mpt::saturate_cast<mpt::IO::Offset>(m_WaveForms[nWaveLink] + m_dwWavePoolOffset);
- if(mpt::IO::SeekAbsolute(f, sampleOffset))
- {
- if (m_nType & SOUNDBANK_TYPE_SF2)
- {
- if (m_SamplesEx[nWaveLink].dwLen)
- {
- if (mpt::IO::SeekRelative(f, 8))
- {
- length = m_SamplesEx[nWaveLink].dwLen;
- try
- {
- waveData.assign(length + 8, 0);
- mpt::IO::ReadRaw(f, waveData.data(), length);
- } catch(mpt::out_of_memory e)
- {
- mpt::delete_out_of_memory(e);
- }
- }
- }
- } else
- {
- LISTChunk chunk;
- if(mpt::IO::Read(f, chunk))
- {
- if((chunk.id == IFFID_LIST) && (chunk.listid == IFFID_wave) && (chunk.len > 4))
- {
- length = chunk.len + 8;
- try
- {
- waveData.assign(chunk.len + sizeof(IFFCHUNK), 0);
- memcpy(waveData.data(), &chunk, sizeof(chunk));
- mpt::IO::ReadRaw(f, &waveData[sizeof(chunk)], length - sizeof(chunk));
- } catch(mpt::out_of_memory e)
- {
- mpt::delete_out_of_memory(e);
- }
- }
- }
- }
- }
- return !waveData.empty();
- }
- bool CDLSBank::ExtractSample(CSoundFile &sndFile, SAMPLEINDEX nSample, uint32 nIns, uint32 nRgn, int transpose) const
- {
- std::vector<uint8> pWaveForm;
- uint32 dwLen = 0;
- bool ok, hasWaveform;
- if(nIns >= m_Instruments.size())
- return false;
- const DLSINSTRUMENT &dlsIns = m_Instruments[nIns];
- if(nRgn >= dlsIns.Regions.size())
- return false;
- if(!ExtractWaveForm(nIns, nRgn, pWaveForm, dwLen))
- return false;
- if(dwLen < 16)
- return false;
- ok = false;
- FileReader wsmpChunk;
- if (m_nType & SOUNDBANK_TYPE_SF2)
- {
- sndFile.DestroySample(nSample);
- uint32 nWaveLink = dlsIns.Regions[nRgn].nWaveLink;
- ModSample &sample = sndFile.GetSample(nSample);
- if (sndFile.m_nSamples < nSample) sndFile.m_nSamples = nSample;
- if (nWaveLink < m_SamplesEx.size())
- {
- const DLSSAMPLEEX &p = m_SamplesEx[nWaveLink];
- #ifdef DLSINSTR_LOG
- MPT_LOG_GLOBAL(LogDebug, "DLSINSTR", MPT_UFORMAT(" SF2 WaveLink #{}: {}Hz")(nWaveLink, p.dwSampleRate));
- #endif
- sample.Initialize();
- FileReader chunk{mpt::as_span(pWaveForm.data(), dwLen)};
- if(!p.compressed || !sndFile.ReadSampleFromFile(nSample, chunk, false, false))
- {
- sample.nLength = dwLen / 2;
- SampleIO(
- SampleIO::_16bit,
- SampleIO::mono,
- SampleIO::littleEndian,
- SampleIO::signedPCM)
- .ReadSample(sample, chunk);
- }
- sample.nLoopStart = dlsIns.Regions[nRgn].ulLoopStart;
- sample.nLoopEnd = dlsIns.Regions[nRgn].ulLoopEnd;
- sample.nC5Speed = p.dwSampleRate;
- sample.RelativeTone = p.byOriginalPitch;
- sample.nFineTune = p.chPitchCorrection;
- if(p.szName[0])
- sndFile.m_szNames[nSample] = mpt::String::ReadAutoBuf(p.szName);
- else if(dlsIns.szName[0])
- sndFile.m_szNames[nSample] = mpt::String::ReadAutoBuf(dlsIns.szName);
- }
- hasWaveform = sample.HasSampleData();
- } else
- {
- FileReader file(mpt::as_span(pWaveForm.data(), dwLen));
- hasWaveform = sndFile.ReadWAVSample(nSample, file, false, &wsmpChunk);
- if(dlsIns.szName[0])
- sndFile.m_szNames[nSample] = mpt::String::ReadAutoBuf(dlsIns.szName);
- }
- if (hasWaveform)
- {
- ModSample &sample = sndFile.GetSample(nSample);
- const DLSREGION &rgn = dlsIns.Regions[nRgn];
- sample.uFlags.reset(CHN_LOOP | CHN_PINGPONGLOOP | CHN_SUSTAINLOOP | CHN_PINGPONGSUSTAIN);
- if (rgn.fuOptions & DLSREGION_SAMPLELOOP) sample.uFlags.set(CHN_LOOP);
- if (rgn.fuOptions & DLSREGION_SUSTAINLOOP) sample.uFlags.set(CHN_SUSTAINLOOP);
- if (rgn.fuOptions & DLSREGION_PINGPONGLOOP) sample.uFlags.set(CHN_PINGPONGLOOP);
- if (sample.uFlags[CHN_LOOP | CHN_SUSTAINLOOP])
- {
- if (rgn.ulLoopEnd > rgn.ulLoopStart)
- {
- if (sample.uFlags[CHN_SUSTAINLOOP])
- {
- sample.nSustainStart = rgn.ulLoopStart;
- sample.nSustainEnd = rgn.ulLoopEnd;
- } else
- {
- sample.nLoopStart = rgn.ulLoopStart;
- sample.nLoopEnd = rgn.ulLoopEnd;
- }
- } else
- {
- sample.uFlags.reset(CHN_LOOP|CHN_SUSTAINLOOP);
- }
- }
- // WSMP chunk
- {
- uint32 usUnityNote = rgn.uUnityNote;
- int sFineTune = rgn.sFineTune;
- int lVolume = rgn.usVolume;
- WSMPChunk wsmp;
- if(!(rgn.fuOptions & DLSREGION_OVERRIDEWSMP) && wsmpChunk.IsValid() && wsmpChunk.Skip(sizeof(IFFCHUNK)) && wsmpChunk.ReadStructPartial(wsmp))
- {
- usUnityNote = wsmp.usUnityNote;
- sFineTune = wsmp.sFineTune;
- lVolume = DLS32BitRelativeGainToLinear(wsmp.lAttenuation) / 256;
- if(wsmp.cSampleLoops)
- {
- WSMPSampleLoop loop;
- wsmpChunk.Seek(sizeof(IFFCHUNK) + wsmp.cbSize);
- wsmpChunk.ReadStruct(loop);
- if(loop.ulLoopLength > 3)
- {
- sample.uFlags.set(CHN_LOOP);
- //if (loop.ulLoopType) sample.uFlags |= CHN_PINGPONGLOOP;
- sample.nLoopStart = loop.ulLoopStart;
- sample.nLoopEnd = loop.ulLoopStart + loop.ulLoopLength;
- }
- }
- } else if (m_nType & SOUNDBANK_TYPE_SF2)
- {
- usUnityNote = (usUnityNote < 0x80) ? usUnityNote : sample.RelativeTone;
- sFineTune += sample.nFineTune;
- }
- #ifdef DLSINSTR_LOG
- MPT_LOG_GLOBAL(LogDebug, "DLSINSTR", MPT_UFORMAT("WSMP: usUnityNote={}.{}, {}Hz (transp={})")(usUnityNote, sFineTune, sample.nC5Speed, transpose));
- #endif
- if (usUnityNote > 0x7F) usUnityNote = 60;
- int steps = (60 + transpose - usUnityNote) * 128 + sFineTune;
- sample.Transpose(steps * (1.0 / (12.0 * 128.0)));
- sample.RelativeTone = 0;
- Limit(lVolume, 16, 256);
- sample.nGlobalVol = (uint8)(lVolume / 4); // 0-64
- }
- sample.nPan = GetPanning(nIns, nRgn);
- sample.Convert(MOD_TYPE_IT, sndFile.GetType());
- sample.PrecomputeLoops(sndFile, false);
- ok = true;
- }
- return ok;
- }
- static uint16 ScaleEnvelope(uint32 time, float tempoScale)
- {
- return std::max(mpt::saturate_round<uint16>(time * tempoScale), uint16(1));
- }
- bool CDLSBank::ExtractInstrument(CSoundFile &sndFile, INSTRUMENTINDEX nInstr, uint32 nIns, uint32 nDrumRgn) const
- {
- uint32 minRegion, maxRegion, nEnv;
- if (nIns >= m_Instruments.size())
- return false;
- const DLSINSTRUMENT &dlsIns = m_Instruments[nIns];
- const bool isDrum = (dlsIns.ulBank & F_INSTRUMENT_DRUMS) && nDrumRgn != uint32_max;
- if(isDrum)
- {
- if(nDrumRgn >= dlsIns.Regions.size())
- return false;
- minRegion = nDrumRgn;
- maxRegion = nDrumRgn + 1;
- nEnv = dlsIns.Regions[nDrumRgn].uPercEnv;
- } else
- {
- if(dlsIns.Regions.empty())
- return false;
- minRegion = 0;
- maxRegion = static_cast<uint32>(dlsIns.Regions.size());
- nEnv = dlsIns.nMelodicEnv;
- }
- #ifdef DLSINSTR_LOG
- MPT_LOG_GLOBAL(LogDebug, "DLSINSTR", MPT_UFORMAT("DLS Instrument #{}: {}")(nIns, mpt::ToUnicode(mpt::Charset::ASCII, mpt::String::ReadAutoBuf(dlsIns.szName))));
- MPT_LOG_GLOBAL(LogDebug, "DLSINSTR", MPT_UFORMAT(" Bank=0x{} Instrument=0x{}")(mpt::ufmt::HEX0<4>(dlsIns.ulBank), mpt::ufmt::HEX0<4>(dlsIns.ulInstrument)));
- MPT_LOG_GLOBAL(LogDebug, "DLSINSTR", MPT_UFORMAT(" {} regions, nMelodicEnv={}")(dlsIns.Regions.size(), dlsIns.nMelodicEnv));
- for (uint32 iDbg=0; iDbg<dlsIns.Regions.size(); iDbg++)
- {
- const DLSREGION *prgn = &dlsIns.Regions[iDbg];
- MPT_LOG_GLOBAL(LogDebug, "DLSINSTR", MPT_UFORMAT(" Region {}:")(iDbg));
- MPT_LOG_GLOBAL(LogDebug, "DLSINSTR", MPT_UFORMAT(" WaveLink = {} (loop [{}, {}])")(prgn->nWaveLink, prgn->ulLoopStart, prgn->ulLoopEnd));
- MPT_LOG_GLOBAL(LogDebug, "DLSINSTR", MPT_UFORMAT(" Key Range: [{}, {}]")(prgn->uKeyMin, prgn->uKeyMax));
- MPT_LOG_GLOBAL(LogDebug, "DLSINSTR", MPT_UFORMAT(" fuOptions = 0x{}")(mpt::ufmt::HEX0<4>(prgn->fuOptions)));
- MPT_LOG_GLOBAL(LogDebug, "DLSINSTR", MPT_UFORMAT(" usVolume = {}, Unity Note = {}")(prgn->usVolume, prgn->uUnityNote));
- }
- #endif
- ModInstrument *pIns = new (std::nothrow) ModInstrument();
- if(pIns == nullptr)
- {
- return false;
- }
- if(sndFile.Instruments[nInstr])
- {
- sndFile.DestroyInstrument(nInstr, deleteAssociatedSamples);
- }
- // Initializes Instrument
- if(isDrum)
- {
- uint32 key = dlsIns.Regions[nDrumRgn].uKeyMin;
- if((key >= 24) && (key <= 84))
- {
- std::string s = szMidiPercussionNames[key-24];
- if(!mpt::String::ReadAutoBuf(dlsIns.szName).empty())
- {
- s += MPT_AFORMAT(" ({})")(mpt::trim_right<std::string>(mpt::String::ReadAutoBuf(dlsIns.szName)));
- }
- pIns->name = s;
- } else
- {
- pIns->name = mpt::String::ReadAutoBuf(dlsIns.szName);
- }
- } else
- {
- pIns->name = mpt::String::ReadAutoBuf(dlsIns.szName);
- }
- int transpose = 0;
- if(isDrum)
- {
- for(uint32 iNoteMap = 0; iNoteMap < NOTE_MAX; iNoteMap++)
- {
- if(sndFile.GetType() & (MOD_TYPE_IT | MOD_TYPE_MID | MOD_TYPE_MPT))
- {
- // Format has instrument note mapping
- if(dlsIns.Regions[nDrumRgn].tuning == 0)
- pIns->NoteMap[iNoteMap] = NOTE_MIDDLEC;
- else if (iNoteMap < dlsIns.Regions[nDrumRgn].uKeyMin)
- pIns->NoteMap[iNoteMap] = (uint8)(dlsIns.Regions[nDrumRgn].uKeyMin + NOTE_MIN);
- else if(iNoteMap > dlsIns.Regions[nDrumRgn].uKeyMax)
- pIns->NoteMap[iNoteMap] = (uint8)(dlsIns.Regions[nDrumRgn].uKeyMax + NOTE_MIN);
- } else
- {
- if(iNoteMap == dlsIns.Regions[nDrumRgn].uKeyMin)
- {
- transpose = (dlsIns.Regions[nDrumRgn].uKeyMin + (dlsIns.Regions[nDrumRgn].uKeyMax - dlsIns.Regions[nDrumRgn].uKeyMin) / 2) - 60;
- }
- }
- }
- }
- pIns->nFadeOut = 1024;
- pIns->nMidiProgram = (uint8)(dlsIns.ulInstrument & 0x7F) + 1;
- pIns->nMidiChannel = (uint8)(isDrum ? 10 : 0);
- pIns->wMidiBank = (uint16)(((dlsIns.ulBank & 0x7F00) >> 1) | (dlsIns.ulBank & 0x7F));
- pIns->nNNA = NewNoteAction::NoteOff;
- pIns->nDCT = DuplicateCheckType::Note;
- pIns->nDNA = DuplicateNoteAction::NoteFade;
- sndFile.Instruments[nInstr] = pIns;
- uint32 nLoadedSmp = 0;
- SAMPLEINDEX nextSample = 0;
- // Extract Samples
- std::vector<SAMPLEINDEX> RgnToSmp(dlsIns.Regions.size());
- std::set<uint16> extractedSamples;
- for(uint32 nRgn = minRegion; nRgn < maxRegion; nRgn++)
- {
- bool duplicateRegion = false;
- SAMPLEINDEX nSmp = 0;
- const DLSREGION &rgn = dlsIns.Regions[nRgn];
- if(rgn.IsDummy())
- continue;
- // Elimitate Duplicate Regions
- uint32 dupRegion;
- for(dupRegion = minRegion; dupRegion < nRgn; dupRegion++)
- {
- const DLSREGION &rgn2 = dlsIns.Regions[dupRegion];
- if(RgnToSmp[dupRegion] == 0 || rgn2.IsDummy())
- continue;
- // No need to extract the same sample data twice
- const bool sameSample = (rgn2.nWaveLink == rgn.nWaveLink) && (rgn2.ulLoopEnd == rgn.ulLoopEnd) && (rgn2.ulLoopStart == rgn.ulLoopStart) && extractedSamples.count(rgn.nWaveLink);
- // Candidate for stereo sample creation
- const bool sameKeyRange = (rgn2.uKeyMin == rgn.uKeyMin) && (rgn2.uKeyMax == rgn.uKeyMax);
- if(sameSample || sameKeyRange)
- {
- duplicateRegion = true;
- if(!sameKeyRange)
- nSmp = RgnToSmp[dupRegion];
- break;
- }
- }
- // Create a new sample
- if (!duplicateRegion)
- {
- uint32 nmaxsmp = (m_nType & MOD_TYPE_XM) ? 16 : (NOTE_MAX - NOTE_MIN + 1);
- if (nLoadedSmp >= nmaxsmp)
- {
- nSmp = RgnToSmp[nRgn - 1];
- } else
- {
- nextSample = sndFile.GetNextFreeSample(nInstr, nextSample + 1);
- if (nextSample == SAMPLEINDEX_INVALID) break;
- if (nextSample > sndFile.GetNumSamples()) sndFile.m_nSamples = nextSample;
- nSmp = nextSample;
- nLoadedSmp++;
- }
- }
- RgnToSmp[nRgn] = nSmp;
- // Map all notes to the right sample
- if(nSmp)
- {
- for(uint8 key = 0; key < NOTE_MAX; key++)
- {
- if(isDrum || (key >= rgn.uKeyMin && key <= rgn.uKeyMax))
- {
- pIns->Keyboard[key] = nSmp;
- }
- }
- // Load the sample
- if(!duplicateRegion || !sndFile.GetSample(nSmp).HasSampleData())
- {
- ExtractSample(sndFile, nSmp, nIns, nRgn, transpose);
- extractedSamples.insert(rgn.nWaveLink);
- }
- } else if(duplicateRegion && sndFile.GetSample(RgnToSmp[dupRegion]).GetNumChannels() == 1)
- {
- // Try to combine stereo samples
- const uint16 pan1 = GetPanning(nIns, nRgn), pan2 = GetPanning(nIns, dupRegion);
- if((pan1 < 16 && pan2 >= 240) || (pan2 < 16 && pan1 >= 240))
- {
- ModSample &sample = sndFile.GetSample(RgnToSmp[dupRegion]);
- ModSample sampleCopy = sample;
- sampleCopy.pData.pSample = nullptr;
- sampleCopy.uFlags.set(CHN_16BIT | CHN_STEREO);
- if(!sampleCopy.AllocateSample())
- continue;
- const uint8 offsetOrig = (pan1 < pan2) ? 1 : 0;
- const uint8 offsetNew = (pan1 < pan2) ? 0 : 1;
- std::vector<uint8> pWaveForm;
- uint32 dwLen = 0;
- if(!ExtractWaveForm(nIns, nRgn, pWaveForm, dwLen))
- continue;
- extractedSamples.insert(rgn.nWaveLink);
- // First copy over original channel
- auto pDest = sampleCopy.sample16() + offsetOrig;
- if(sample.uFlags[CHN_16BIT])
- CopySample<SC::ConversionChain<SC::Convert<int16, int16>, SC::DecodeIdentity<int16>>>(pDest, sample.nLength, 2, sample.sample16(), sample.GetSampleSizeInBytes(), 1);
- else
- CopySample<SC::ConversionChain<SC::Convert<int16, int8>, SC::DecodeIdentity<int8>>>(pDest, sample.nLength, 2, sample.sample8(), sample.GetSampleSizeInBytes(), 1);
- sample.FreeSample();
- // Now read the other channel
- if(m_SamplesEx[m_Instruments[nIns].Regions[nRgn].nWaveLink].compressed)
- {
- FileReader file{mpt::as_span(pWaveForm)};
- if(sndFile.ReadSampleFromFile(nSmp, file, false, false))
- {
- pDest = sampleCopy.sample16() + offsetNew;
- const SmpLength copyLength = std::min(sample.nLength, sampleCopy.nLength);
- if(sample.uFlags[CHN_16BIT])
- CopySample<SC::ConversionChain<SC::Convert<int16, int16>, SC::DecodeIdentity<int16>>>(pDest, copyLength, 2, sample.sample16(), sample.GetSampleSizeInBytes(), sample.GetNumChannels());
- else
- CopySample<SC::ConversionChain<SC::Convert<int16, int8>, SC::DecodeIdentity<int8>>>(pDest, copyLength, 2, sample.sample8(), sample.GetSampleSizeInBytes(), sample.GetNumChannels());
- }
- } else
- {
- SmpLength len = std::min(dwLen / 2u, sampleCopy.nLength);
- const int16 *src = reinterpret_cast<int16 *>(pWaveForm.data());
- int16 *dst = sampleCopy.sample16() + offsetNew;
- CopySample<SC::ConversionChain<SC::Convert<int16, int16>, SC::DecodeIdentity<int16>>>(dst, len, 2, src, pWaveForm.size(), 1);
- }
- sample.FreeSample();
- sample = sampleCopy;
- }
- }
- }
- float tempoScale = 1.0f;
- if(sndFile.m_nTempoMode == TempoMode::Modern)
- {
- uint32 ticksPerBeat = sndFile.m_nDefaultRowsPerBeat * sndFile.m_nDefaultSpeed;
- if(ticksPerBeat != 0)
- tempoScale = ticksPerBeat / 24.0f;
- }
- // Initializes Envelope
- if ((nEnv) && (nEnv <= m_Envelopes.size()))
- {
- const DLSENVELOPE &part = m_Envelopes[nEnv - 1];
- // Volume Envelope
- if ((part.wVolAttack) || (part.wVolDecay < 20*50) || (part.nVolSustainLevel) || (part.wVolRelease < 20*50))
- {
- pIns->VolEnv.dwFlags.set(ENV_ENABLED);
- // Delay section
- // -> DLS level 2
- // Attack section
- pIns->VolEnv.clear();
- if (part.wVolAttack)
- {
- pIns->VolEnv.push_back(0, (uint8)(ENVELOPE_MAX / (part.wVolAttack / 2 + 2) + 8)); // /-----
- pIns->VolEnv.push_back(ScaleEnvelope(part.wVolAttack, tempoScale), ENVELOPE_MAX); // |
- } else
- {
- pIns->VolEnv.push_back(0, ENVELOPE_MAX);
- }
- // Hold section
- // -> DLS Level 2
- // Sustain Level
- if (part.nVolSustainLevel > 0)
- {
- if (part.nVolSustainLevel < 128)
- {
- uint16 lStartTime = pIns->VolEnv.back().tick;
- int32 lSusLevel = - DLS32BitRelativeLinearToGain(part.nVolSustainLevel << 9) / 65536;
- int32 lDecayTime = 1;
- if (lSusLevel > 0)
- {
- lDecayTime = (lSusLevel * (int32)part.wVolDecay) / 960;
- for (uint32 i=0; i<7; i++)
- {
- int32 lFactor = 128 - (1 << i);
- if (lFactor <= part.nVolSustainLevel) break;
- int32 lev = - DLS32BitRelativeLinearToGain(lFactor << 9) / 65536;
- if (lev > 0)
- {
- int32 ltime = (lev * (int32)part.wVolDecay) / 960;
- if ((ltime > 1) && (ltime < lDecayTime))
- {
- uint16 tick = lStartTime + ScaleEnvelope(ltime, tempoScale);
- if(tick > pIns->VolEnv.back().tick)
- {
- pIns->VolEnv.push_back(tick, (uint8)(lFactor / 2));
- }
- }
- }
- }
- }
- uint16 decayEnd = lStartTime + ScaleEnvelope(lDecayTime, tempoScale);
- if (decayEnd > pIns->VolEnv.back().tick)
- {
- pIns->VolEnv.push_back(decayEnd, (uint8)((part.nVolSustainLevel+1) / 2));
- }
- }
- pIns->VolEnv.dwFlags.set(ENV_SUSTAIN);
- } else
- {
- pIns->VolEnv.dwFlags.set(ENV_SUSTAIN);
- pIns->VolEnv.push_back(pIns->VolEnv.back().tick + 1u, pIns->VolEnv.back().value);
- }
- pIns->VolEnv.nSustainStart = pIns->VolEnv.nSustainEnd = (uint8)(pIns->VolEnv.size() - 1);
- // Release section
- if ((part.wVolRelease) && (pIns->VolEnv.back().value > 1))
- {
- int32 lReleaseTime = part.wVolRelease;
- uint16 lStartTime = pIns->VolEnv.back().tick;
- int32 lStartFactor = pIns->VolEnv.back().value;
- int32 lSusLevel = - DLS32BitRelativeLinearToGain(lStartFactor << 10) / 65536;
- int32 lDecayEndTime = (lReleaseTime * lSusLevel) / 960;
- lReleaseTime -= lDecayEndTime;
- if(pIns->VolEnv.nSustainEnd > 0)
- pIns->VolEnv.nReleaseNode = pIns->VolEnv.nSustainEnd;
- for (uint32 i=0; i<5; i++)
- {
- int32 lFactor = 1 + ((lStartFactor * 3) >> (i+2));
- if ((lFactor <= 1) || (lFactor >= lStartFactor)) continue;
- int32 lev = - DLS32BitRelativeLinearToGain(lFactor << 10) / 65536;
- if (lev > 0)
- {
- int32 ltime = (((int32)part.wVolRelease * lev) / 960) - lDecayEndTime;
- if ((ltime > 1) && (ltime < lReleaseTime))
- {
- uint16 tick = lStartTime + ScaleEnvelope(ltime, tempoScale);
- if(tick > pIns->VolEnv.back().tick)
- {
- pIns->VolEnv.push_back(tick, (uint8)lFactor);
- }
- }
- }
- }
- if (lReleaseTime < 1) lReleaseTime = 1;
- auto releaseTicks = ScaleEnvelope(lReleaseTime, tempoScale);
- pIns->VolEnv.push_back(lStartTime + releaseTicks, ENVELOPE_MIN);
- if(releaseTicks > 0)
- {
- pIns->nFadeOut = 32768 / releaseTicks;
- }
- } else
- {
- pIns->VolEnv.push_back(pIns->VolEnv.back().tick + 1u, ENVELOPE_MIN);
- }
- }
- }
- if(isDrum)
- {
- // Create a default envelope for drums
- pIns->VolEnv.dwFlags.reset(ENV_SUSTAIN);
- if(!pIns->VolEnv.dwFlags[ENV_ENABLED])
- {
- pIns->VolEnv.dwFlags.set(ENV_ENABLED);
- pIns->VolEnv.resize(4);
- pIns->VolEnv[0] = EnvelopeNode(0, ENVELOPE_MAX);
- pIns->VolEnv[1] = EnvelopeNode(ScaleEnvelope(5, tempoScale), ENVELOPE_MAX);
- pIns->VolEnv[2] = EnvelopeNode(pIns->VolEnv[1].tick * 2u, ENVELOPE_MID);
- pIns->VolEnv[3] = EnvelopeNode(pIns->VolEnv[2].tick * 2u, ENVELOPE_MIN); // 1 second max. for drums
- }
- }
- pIns->Sanitize(MOD_TYPE_MPT);
- pIns->Convert(MOD_TYPE_MPT, sndFile.GetType());
- return true;
- }
- const char *CDLSBank::GetRegionName(uint32 nIns, uint32 nRgn) const
- {
- if(nIns >= m_Instruments.size())
- return nullptr;
- const DLSINSTRUMENT &dlsIns = m_Instruments[nIns];
- if(nRgn >= dlsIns.Regions.size())
- return nullptr;
- if (m_nType & SOUNDBANK_TYPE_SF2)
- {
- uint32 nWaveLink = dlsIns.Regions[nRgn].nWaveLink;
- if (nWaveLink < m_SamplesEx.size())
- {
- return m_SamplesEx[nWaveLink].szName;
- }
- }
- return nullptr;
- }
- uint16 CDLSBank::GetPanning(uint32 ins, uint32 region) const
- {
- const DLSINSTRUMENT &dlsIns = m_Instruments[ins];
- if(region >= std::size(dlsIns.Regions))
- return 128;
- const DLSREGION &rgn = dlsIns.Regions[region];
- if(rgn.panning >= 0)
- return static_cast<uint16>(rgn.panning);
- if(dlsIns.ulBank & F_INSTRUMENT_DRUMS)
- {
- if(rgn.uPercEnv > 0 && rgn.uPercEnv <= m_Envelopes.size())
- {
- return m_Envelopes[rgn.uPercEnv - 1].nDefPan;
- }
- } else
- {
- if(dlsIns.nMelodicEnv > 0 && dlsIns.nMelodicEnv <= m_Envelopes.size())
- {
- return m_Envelopes[dlsIns.nMelodicEnv - 1].nDefPan;
- }
- }
- return 128;
- }
- #else // !MODPLUG_TRACKER
- MPT_MSVC_WORKAROUND_LNK4221(Dlsbank)
- #endif // MODPLUG_TRACKER
- OPENMPT_NAMESPACE_END
|