/** * Furnace Tracker - multi-system chiptune tracker * Copyright (C) 2021-2023 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 #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 }; 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]; unsigned char chanCollapse[DIV_MAX_CHANS]; String chanName[DIV_MAX_CHANS]; String chanShortName[DIV_MAX_CHANS]; void clearData(); void optimizePatterns(); void rearrangePatterns(); 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 { // version number used for saving the song. // Furnace will save using the latest possible version, // known version numbers: // - 26: v1.1.3 // - changes height of FDS wave to 6-bit (it was 4-bit before) // - 25: v1.1 // - adds pattern names (in a rather odd way) // - introduces SMS+OPLL system // - 24: v0.12/0.13/1.0 // - current format version // - changes pattern length from char to int, probably to allow for size 256 // - 23: ??? // - what happened here? // - 20: v11.1 (?) // - E5xx effect range is now ±1 semitone // - 19: v11 // - introduces Arcade system // - changes to the FM instrument format due to YMU759 being dropped // - 18: v10 // - radically changes STD instrument for Game Boy // - 17: v9 // - changes C64 volIsCutoff flag from int to char for unknown reasons // - 16: v8 (?) // - introduces C64 system // - 15: v7 (?) // - 14: v6 (?) // - introduces NES system // - changes macro and wave values from char to int // - 13: v5.1 // - introduces PC Engine system in later version (how?) // - stores highlight in file // - 12: v5 (?) // - introduces Game Boy system // - introduces wavetables // - 11: ??? // - introduces Sega Master System // - custom Hz support // - instrument type (FM/STD) present // - prior to this version the instrument type depended on the system // - 10: ??? // - introduces multiple effect columns // - 9: v3.9 // - introduces Genesis system // - introduces system number // - patterns now stored in current known format // - 8: ??? // - only used in the Medivo YMU cover // - 7: ??? // - only present in a later version of First.dmf // - pattern format changes: empty field is 0xFF instead of 0x80 // - instrument now stored in pattern // - 5: BETA 3 // - adds arpeggio tick // - 4: BETA 2 // - possibly adds instrument number (stored in channel)? // - cannot confirm as I don't have any version 4 modules // - 3: BETA 1 // - possibly the first version that could save // - basic format, no system number, 16 instruments, one speed, YMU759-only // - patterns were stored in a different format (chars instead of shorts) and no instrument // - if somebody manages to find a version 2 or even 1 module, please tell me as it will be worth more than a luxury vehicle 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; 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; 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) { for (int i=0; i