/** * Furnace Tracker - multi-system chiptune tracker * Copyright (C) 2021-2024 tildearrow and contributors * * 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 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, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #ifndef _SONG_H #define _SONG_H #include #include "../pch.h" #include "defines.h" #include "../ta-utils.h" #include "config.h" #include "orders.h" #include "instrument.h" #include "pattern.h" #include "wavetable.h" #include "sample.h" enum DivSystem { DIV_SYSTEM_NULL=0, DIV_SYSTEM_YMU759, DIV_SYSTEM_GENESIS, // ** COMPOUND SYSTEM - DO NOT USE! ** DIV_SYSTEM_GENESIS_EXT, // ** COMPOUND SYSTEM - DO NOT USE! ** DIV_SYSTEM_SMS, DIV_SYSTEM_SMS_OPLL, // ** COMPOUND SYSTEM - DO NOT USE! ** DIV_SYSTEM_GB, DIV_SYSTEM_PCE, DIV_SYSTEM_NES, DIV_SYSTEM_NES_VRC7, // ** COMPOUND SYSTEM - DO NOT USE! ** DIV_SYSTEM_NES_FDS, // ** COMPOUND SYSTEM - DO NOT USE! ** DIV_SYSTEM_C64_6581, DIV_SYSTEM_C64_8580, DIV_SYSTEM_ARCADE, // ** COMPOUND SYSTEM - DO NOT USE! ** DIV_SYSTEM_YM2610, DIV_SYSTEM_YM2610_EXT, DIV_SYSTEM_AY8910, DIV_SYSTEM_AMIGA, DIV_SYSTEM_YM2151, DIV_SYSTEM_YM2612, DIV_SYSTEM_TIA, DIV_SYSTEM_SAA1099, DIV_SYSTEM_AY8930, DIV_SYSTEM_VIC20, DIV_SYSTEM_PET, DIV_SYSTEM_SNES, DIV_SYSTEM_VRC6, DIV_SYSTEM_OPLL, DIV_SYSTEM_FDS, DIV_SYSTEM_MMC5, DIV_SYSTEM_N163, DIV_SYSTEM_YM2203, DIV_SYSTEM_YM2203_EXT, DIV_SYSTEM_YM2608, DIV_SYSTEM_YM2608_EXT, DIV_SYSTEM_OPL, DIV_SYSTEM_OPL2, DIV_SYSTEM_OPL3, DIV_SYSTEM_MULTIPCM, DIV_SYSTEM_PCSPKR, DIV_SYSTEM_POKEY, DIV_SYSTEM_RF5C68, DIV_SYSTEM_SWAN, DIV_SYSTEM_OPZ, DIV_SYSTEM_POKEMINI, DIV_SYSTEM_SEGAPCM, DIV_SYSTEM_VBOY, DIV_SYSTEM_VRC7, DIV_SYSTEM_YM2610B, DIV_SYSTEM_SFX_BEEPER, DIV_SYSTEM_SFX_BEEPER_QUADTONE, DIV_SYSTEM_YM2612_EXT, DIV_SYSTEM_SCC, DIV_SYSTEM_OPL_DRUMS, DIV_SYSTEM_OPL2_DRUMS, DIV_SYSTEM_OPL3_DRUMS, DIV_SYSTEM_YM2610_FULL, DIV_SYSTEM_YM2610_FULL_EXT, DIV_SYSTEM_OPLL_DRUMS, DIV_SYSTEM_LYNX, DIV_SYSTEM_QSOUND, DIV_SYSTEM_VERA, DIV_SYSTEM_YM2610B_EXT, DIV_SYSTEM_SEGAPCM_COMPAT, DIV_SYSTEM_X1_010, DIV_SYSTEM_BUBSYS_WSG, DIV_SYSTEM_OPL4, DIV_SYSTEM_OPL4_DRUMS, DIV_SYSTEM_ES5506, DIV_SYSTEM_Y8950, DIV_SYSTEM_Y8950_DRUMS, DIV_SYSTEM_SCC_PLUS, DIV_SYSTEM_SOUND_UNIT, DIV_SYSTEM_MSM6295, DIV_SYSTEM_MSM6258, DIV_SYSTEM_YMZ280B, DIV_SYSTEM_NAMCO, DIV_SYSTEM_NAMCO_15XX, DIV_SYSTEM_NAMCO_CUS30, DIV_SYSTEM_YM2612_DUALPCM, DIV_SYSTEM_YM2612_DUALPCM_EXT, DIV_SYSTEM_MSM5232, DIV_SYSTEM_T6W28, DIV_SYSTEM_K007232, DIV_SYSTEM_GA20, DIV_SYSTEM_PCM_DAC, DIV_SYSTEM_PONG, DIV_SYSTEM_DUMMY, DIV_SYSTEM_YM2612_CSM, DIV_SYSTEM_YM2610_CSM, DIV_SYSTEM_YM2610B_CSM, DIV_SYSTEM_YM2203_CSM, DIV_SYSTEM_YM2608_CSM, DIV_SYSTEM_SM8521, DIV_SYSTEM_PV1000, DIV_SYSTEM_K053260, DIV_SYSTEM_TED, DIV_SYSTEM_C140, DIV_SYSTEM_C219, DIV_SYSTEM_ESFM, DIV_SYSTEM_POWERNOISE, DIV_SYSTEM_DAVE, DIV_SYSTEM_NDS, DIV_SYSTEM_GBA_DMA, DIV_SYSTEM_GBA_MINMOD, DIV_SYSTEM_5E01, }; enum DivEffectType: unsigned short { DIV_EFFECT_NULL=0, DIV_EFFECT_DUMMY, DIV_EFFECT_EXTERNAL, DIV_EFFECT_VOLUME, DIV_EFFECT_FILTER }; struct DivGroovePattern { unsigned char val[16]; unsigned char len; DivGroovePattern(): len(1) { memset(val,6,16); } }; struct DivSubSong { String name, notes; unsigned char hilightA, hilightB; unsigned char timeBase, arpLen; DivGroovePattern speeds; short virtualTempoN, virtualTempoD; float hz; int patLen, ordersLen; DivOrders orders; DivChannelData pat[DIV_MAX_CHANS]; bool chanShow[DIV_MAX_CHANS]; bool chanShowChanOsc[DIV_MAX_CHANS]; unsigned char chanCollapse[DIV_MAX_CHANS]; String chanName[DIV_MAX_CHANS]; String chanShortName[DIV_MAX_CHANS]; void clearData(); void optimizePatterns(); void rearrangePatterns(); void sortOrders(); void makePatUnique(); DivSubSong(): hilightA(4), hilightB(16), timeBase(0), arpLen(1), virtualTempoN(150), virtualTempoD(150), hz(60.0), patLen(64), ordersLen(1) { for (int i=0; i entries; DivAssetDir(): name("New Directory") {} DivAssetDir(String n): name(n) {} }; struct DivEffectStorage { DivEffectType id; unsigned short slot, storageVer; float dryWet; unsigned char* storage; size_t storageLen; DivEffectStorage(): id(DIV_EFFECT_NULL), slot(0), storageVer(0), dryWet(1.0f), storage(NULL), storageLen(0) {} }; struct DivSong { unsigned short version; bool isDMF; // system DivSystem system[DIV_MAX_CHIPS]; unsigned char systemLen; float systemVol[DIV_MAX_CHIPS]; float systemPan[DIV_MAX_CHIPS]; float systemPanFR[DIV_MAX_CHIPS]; DivConfig systemFlags[DIV_MAX_CHIPS]; // song information String name, author, systemName; // legacy song information // those will be stored in .fur and mapped to VGM as: // category -> game name // writer -> ripper // createdDate -> year String carrier, composer, vendor, category, writer, arranger, copyright, manGroup, manInfo, createdDate, revisionDate; // more VGM specific stuff String nameJ, authorJ, categoryJ, systemNameJ; // other things String notes; // module details int insLen, waveLen, sampleLen; float masterVol; float tuning; // compatibility flags bool limitSlides; // linear pitch // 0: not linear // 1: only pitch changes (04xy/E5xx) linear // 2: full linear unsigned char linearPitch; unsigned char pitchSlideSpeed; // loop behavior // 0: reset on loop // 1: fake reset on loop // 2: don't do anything on loop unsigned char loopModality; // cut/delay effect behavior // 0: strict (don't allow value higher than or equal to speed) // 1: broken (don't allow value higher than speed) // 2: lax (allow value higher than speed) unsigned char delayBehavior; // 0B/0D treatment // 0: normal (0B/0D accepted) // 1: old Furnace (first one accepted) // 2: DefleMask (0D takes priority over 0B) unsigned char jumpTreatment; bool properNoiseLayout; bool waveDutyIsVol; bool resetMacroOnPorta; bool legacyVolumeSlides; bool compatibleArpeggio; bool noteOffResetsSlides; bool targetResetsSlides; bool arpNonPorta; bool algMacroBehavior; bool brokenShortcutSlides; bool ignoreDuplicateSlides; bool stopPortaOnNoteOff; bool continuousVibrato; bool brokenDACMode; bool oneTickCut; bool newInsTriggersInPorta; bool arp0Reset; bool brokenSpeedSel; bool noSlidesOnFirstTick; bool rowResetsArpPos; bool ignoreJumpAtEnd; bool buggyPortaAfterSlide; bool gbInsAffectsEnvelope; bool sharedExtStat; bool ignoreDACModeOutsideIntendedChannel; bool e1e2AlsoTakePriority; bool newSegaPCM; bool fbPortaPause; bool snDutyReset; bool pitchMacroIsLinear; bool oldOctaveBoundary; bool noOPN2Vol; bool newVolumeScaling; bool volMacroLinger; bool brokenOutVol; bool brokenOutVol2; bool e1e2StopOnSameNote; bool brokenPortaArp; bool snNoLowPeriods; bool disableSampleMacro; bool autoSystem; bool oldArpStrategy; bool patchbayAuto; bool brokenPortaLegato; bool brokenFMOff; bool preNoteNoEffect; bool oldDPCM; bool resetArpPhaseOnNewNote; bool ceilVolumeScaling; bool oldAlwaysSetVolume; bool resetEffectsOnRowChange; std::vector ins; std::vector wave; std::vector sample; std::vector subsong; std::vector patchbay; std::vector grooves; std::vector insDir; std::vector waveDir; std::vector sampleDir; std::vector effects; DivInstrument nullIns, nullInsOPLL, nullInsOPL, nullInsOPLDrums, nullInsQSound, nullInsESFM; DivWavetable nullWave; DivSample nullSample; /** * clear orders and patterns. */ void clearSongData(); /** * clear instruments. */ void clearInstruments(); /** * clear wavetables. */ void clearWavetables(); /** * clear samples. */ void clearSamples(); /** * unloads the song, freeing all memory associated with it. * use before destroying the object. */ void unload(); DivSong(): version(0), isDMF(false), systemLen(2), name(""), author(""), systemName(""), carrier(""), composer(""), vendor(""), category(""), writer(""), arranger(""), copyright(""), manGroup(""), manInfo(""), createdDate(""), revisionDate(""), insLen(0), waveLen(0), sampleLen(0), masterVol(1.0f), tuning(440.0f), limitSlides(false), linearPitch(2), pitchSlideSpeed(4), loopModality(2), delayBehavior(2), jumpTreatment(0), properNoiseLayout(true), waveDutyIsVol(false), resetMacroOnPorta(false), legacyVolumeSlides(false), compatibleArpeggio(false), noteOffResetsSlides(true), targetResetsSlides(true), arpNonPorta(false), algMacroBehavior(false), brokenShortcutSlides(false), ignoreDuplicateSlides(false), stopPortaOnNoteOff(false), continuousVibrato(false), brokenDACMode(false), oneTickCut(false), newInsTriggersInPorta(true), arp0Reset(true), brokenSpeedSel(false), noSlidesOnFirstTick(false), rowResetsArpPos(false), ignoreJumpAtEnd(false), buggyPortaAfterSlide(false), gbInsAffectsEnvelope(true), sharedExtStat(true), ignoreDACModeOutsideIntendedChannel(false), e1e2AlsoTakePriority(false), newSegaPCM(true), fbPortaPause(false), snDutyReset(false), pitchMacroIsLinear(true), oldOctaveBoundary(false), noOPN2Vol(false), newVolumeScaling(true), volMacroLinger(true), brokenOutVol(false), brokenOutVol2(false), e1e2StopOnSameNote(false), brokenPortaArp(false), snNoLowPeriods(false), disableSampleMacro(false), autoSystem(true), oldArpStrategy(false), patchbayAuto(true), brokenPortaLegato(false), brokenFMOff(false), preNoteNoEffect(false), oldDPCM(false), resetArpPhaseOnNewNote(false), ceilVolumeScaling(false), oldAlwaysSetVolume(false), resetEffectsOnRowChange(false) { for (int i=0; i