From: <man...@us...> - 2013-07-05 16:56:08
|
Revision: 2502 http://sourceforge.net/p/modplug/code/2502 Author: manxorist Date: 2013-07-05 16:55:57 +0000 (Fri, 05 Jul 2013) Log Message: ----------- [Ref] Rename the last 4 remaining files in soundlib/ with had .CPP extension to lowercase .cpp. Modified Paths: -------------- trunk/OpenMPT/libopenmpt/libopenmpt.vcxproj trunk/OpenMPT/libopenmpt/libopenmpt.vcxproj.filters trunk/OpenMPT/openmpt123/Makefile Added Paths: ----------- trunk/OpenMPT/soundlib/Load_amf.cpp trunk/OpenMPT/soundlib/Load_dbm.cpp trunk/OpenMPT/soundlib/Load_dmf.cpp trunk/OpenMPT/soundlib/Load_dsm.cpp Removed Paths: ------------- trunk/OpenMPT/soundlib/LOAD_AMF.CPP trunk/OpenMPT/soundlib/LOAD_DBM.CPP trunk/OpenMPT/soundlib/LOAD_DMF.CPP trunk/OpenMPT/soundlib/LOAD_DSM.CPP Modified: trunk/OpenMPT/libopenmpt/libopenmpt.vcxproj =================================================================== --- trunk/OpenMPT/libopenmpt/libopenmpt.vcxproj 2013-07-05 12:13:18 UTC (rev 2501) +++ trunk/OpenMPT/libopenmpt/libopenmpt.vcxproj 2013-07-05 16:55:57 UTC (rev 2502) @@ -394,12 +394,12 @@ <ClCompile Include="..\soundlib\ITCompression.cpp" /> <ClCompile Include="..\soundlib\ITTools.cpp" /> <ClCompile Include="..\soundlib\Load_669.cpp" /> - <ClCompile Include="..\soundlib\LOAD_AMF.CPP" /> + <ClCompile Include="..\soundlib\Load_amf.cpp" /> <ClCompile Include="..\soundlib\Load_ams.cpp" /> - <ClCompile Include="..\soundlib\LOAD_DBM.CPP" /> + <ClCompile Include="..\soundlib\Load_dbm.cpp" /> <ClCompile Include="..\soundlib\Load_digi.cpp" /> - <ClCompile Include="..\soundlib\LOAD_DMF.CPP" /> - <ClCompile Include="..\soundlib\LOAD_DSM.CPP" /> + <ClCompile Include="..\soundlib\Load_dmf.cpp" /> + <ClCompile Include="..\soundlib\Load_dsm.cpp" /> <ClCompile Include="..\soundlib\Load_far.cpp" /> <ClCompile Include="..\soundlib\Load_gdm.cpp" /> <ClCompile Include="..\soundlib\Load_imf.cpp" /> Modified: trunk/OpenMPT/libopenmpt/libopenmpt.vcxproj.filters =================================================================== --- trunk/OpenMPT/libopenmpt/libopenmpt.vcxproj.filters 2013-07-05 12:13:18 UTC (rev 2501) +++ trunk/OpenMPT/libopenmpt/libopenmpt.vcxproj.filters 2013-07-05 16:55:57 UTC (rev 2502) @@ -277,19 +277,19 @@ <ClCompile Include="..\soundlib\XMTools.cpp"> <Filter>Source Files\soundlib</Filter> </ClCompile> - <ClCompile Include="..\soundlib\LOAD_AMF.CPP"> + <ClCompile Include="..\soundlib\Load_amf.cpp"> <Filter>Source Files\soundlib</Filter> </ClCompile> <ClCompile Include="..\soundlib\Load_ams.cpp"> <Filter>Source Files\soundlib</Filter> </ClCompile> - <ClCompile Include="..\soundlib\LOAD_DBM.CPP"> + <ClCompile Include="..\soundlib\Load_dbm.cpp"> <Filter>Source Files\soundlib</Filter> </ClCompile> - <ClCompile Include="..\soundlib\LOAD_DMF.CPP"> + <ClCompile Include="..\soundlib\Load_dmf.cpp"> <Filter>Source Files\soundlib</Filter> </ClCompile> - <ClCompile Include="..\soundlib\LOAD_DSM.CPP"> + <ClCompile Include="..\soundlib\Load_dsm.cpp"> <Filter>Source Files\soundlib</Filter> </ClCompile> <ClCompile Include="..\soundlib\Load_far.cpp"> Modified: trunk/OpenMPT/openmpt123/Makefile =================================================================== --- trunk/OpenMPT/openmpt123/Makefile 2013-07-05 12:13:18 UTC (rev 2501) +++ trunk/OpenMPT/openmpt123/Makefile 2013-07-05 16:55:57 UTC (rev 2502) @@ -115,11 +115,6 @@ $(INFO) [LD ] $@ $(SILENT)$(LINK.cc) $^ $(LOADLIBES) $(LDLIBS) -o $@ -%.o: %.CPP - $(INFO) [CXX] $< - $(VERYSILENT)$(CXX) $(CXXFLAGS) $(CPPFLAGS) $(TARGET_ARCH) -M -MT$@ $< > $*.d - $(SILENT)@$(COMPILE.cc) $(OUTPUT_OPTION) $< - %.o: %.cpp $(INFO) [CXX] $< $(VERYSILENT)$(CXX) $(CXXFLAGS) $(CPPFLAGS) $(TARGET_ARCH) -M -MT$@ $< > $*.d @@ -141,13 +136,11 @@ COMMON_CXX_SOURCES += \ $(wildcard ../common/*.cpp) \ - + SOUNDLIB_CXX_SOURCES += \ $(COMMON_CXX_SOURCES) \ $(wildcard ../soundlib/*.cpp) \ -SOUNDLIB_CXX_SOURCES_UPPERCASE += $(wildcard ../soundlib/*.CPP) -SOUNDLIB_CXX_SOURCES += $(SOUNDLIB_CXX_SOURCES_UPPERCASE:.CPP=.cpp) LIBOPENMPT_CXX_SOURCES += \ Deleted: trunk/OpenMPT/soundlib/LOAD_AMF.CPP =================================================================== --- trunk/OpenMPT/soundlib/LOAD_AMF.CPP 2013-07-05 12:13:18 UTC (rev 2501) +++ trunk/OpenMPT/soundlib/LOAD_AMF.CPP 2013-07-05 16:55:57 UTC (rev 2502) @@ -1,593 +0,0 @@ -/* - * Load_amf.cpp - * ------------ - * Purpose: AMF module loader - * Notes : There are two types of AMF files, the ASYLUM Music Format (used in Crusader: No Remorse and Crusader: No Regret) - * and Advanced Music Format (DSMI / Digital Sound And Music Interface, used in various games such as Pinball World). - * Both module types are handled here. - * Authors: Olivier Lapicque - * OpenMPT Devs - * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. - */ - - -#include "stdafx.h" -#include "Loaders.h" - - -#ifdef NEEDS_PRAGMA_PACK -#pragma pack(push, 1) -#endif - -// ASYLUM AMF File Header -struct PACKED AsylumFileHeader -{ - char signature[32]; - uint8 defaultSpeed; - uint8 defaultTempo; - uint8 numSamples; - uint8 numPatterns; - uint8 numOrders; - uint8 restartPos; -}; - -STATIC_ASSERT(sizeof(AsylumFileHeader) == 38); - - -// ASYLUM AMF Sample Header -struct PACKED AsylumSampleHeader -{ - char name[22]; - uint8 finetune; - uint8 defaultVolume; - int8 transpose; - uint32 length; - uint32 loopStart; - uint32 loopLength; - - // Convert all multi-byte numeric values to current platform's endianness or vice versa. - void ConvertEndianness() - { - SwapBytesLE(length); - SwapBytesLE(loopStart); - SwapBytesLE(loopLength); - } - - // Convert an AMF sample header to OpenMPT's internal sample header. - void ConvertToMPT(ModSample &mptSmp) const - { - mptSmp.Initialize(); - mptSmp.nFineTune = MOD2XMFineTune(finetune); - mptSmp.nVolume = std::min(defaultVolume, uint8(64)) * 4u; - mptSmp.RelativeTone = transpose; - mptSmp.nLength = length; - - if(loopLength > 2 && loopStart + loopLength <= length) - { - mptSmp.uFlags.set(CHN_LOOP); - mptSmp.nLoopStart = loopStart; - mptSmp.nLoopEnd = loopStart + loopLength; - } - } -}; - -STATIC_ASSERT(sizeof(AsylumSampleHeader) == 37); - - -// DSMI AMF File Header -struct PACKED AMFFileHeader -{ - char amf[3]; - uint8 version; - char title[32]; - uint8 numSamples; - uint8 numOrders; - uint16 numTracks; - uint8 numChannels; - - // Convert all multi-byte numeric values to current platform's endianness or vice versa. - void ConvertEndianness() - { - SwapBytesLE(numTracks); - } -}; - -STATIC_ASSERT(sizeof(AMFFileHeader) == 41); - - -#ifdef NEEDS_PRAGMA_PACK -#pragma pack(pop) -#endif - - -bool CSoundFile::ReadAMF_Asylum(FileReader &file, ModLoadingFlags loadFlags) -//-------------------------------------------------------------------------- -{ - file.Rewind(); - - AsylumFileHeader fileHeader; - if(!file.Read(fileHeader) - || strncmp(fileHeader.signature, "ASYLUM Music Format V1.0", 25) - || fileHeader.numSamples > 64 - || !file.CanRead(256 + 64 * sizeof(AsylumSampleHeader) + 64 * 4 * 8 * fileHeader.numPatterns)) - { - return false; - } else if(loadFlags == onlyVerifyHeader) - { - return true; - } - - InitializeGlobals(); - InitializeChannels(); - m_nType = MOD_TYPE_AMF0; - m_nChannels = 8; - m_nDefaultSpeed = fileHeader.defaultSpeed; - m_nDefaultTempo = fileHeader.defaultTempo; - m_nSamples = fileHeader.numSamples; - if(fileHeader.restartPos < fileHeader.numOrders) - { - m_nRestartPos = fileHeader.restartPos; - } - songName = ""; - - Order.ReadAsByte(file, 256, fileHeader.numOrders); - - // Read Sample Headers - for(SAMPLEINDEX smp = 1; smp <= GetNumSamples(); smp++) - { - AsylumSampleHeader sampleHeader; - file.ReadConvertEndianness(sampleHeader); - sampleHeader.ConvertToMPT(Samples[smp]); - mpt::String::Read<mpt::String::maybeNullTerminated>(m_szNames[smp], sampleHeader.name); - } - - file.Skip((64 - fileHeader.numSamples) * sizeof(AsylumSampleHeader)); - - // Read Patterns - for(PATTERNINDEX pat = 0; pat < fileHeader.numPatterns; pat++) - { - if(!(loadFlags & loadPatternData) || Patterns.Insert(pat, 64)) - { - file.Skip(64 * 4 * 8); - continue; - } - - ModCommand *p = Patterns[pat]; - for(size_t i = 0; i < 8 * 64; i++, p++) - { - uint8 data[4]; - file.ReadArray(data); - - p->note = NOTE_NONE; - if(data[0] && data[0] + 12 + NOTE_MIN <= NOTE_MAX) - { - p->note = data[0] + 12 + NOTE_MIN; - } - p->instr = data[1]; - p->command = data[2]; - p->param = data[3]; - ConvertModCommand(*p); - } - } - - if(loadFlags & loadSampleData) - { - // Read Sample Data - const SampleIO sampleIO( - SampleIO::_8bit, - SampleIO::mono, - SampleIO::littleEndian, - SampleIO::signedPCM); - - for(SAMPLEINDEX smp = 1; smp <= GetNumSamples(); smp++) - { - sampleIO.ReadSample(Samples[smp], file); - } - } - - return true; -} - - -// Read a single AMF track (channel) into a pattern. -void AMFReadPattern(CPattern &pattern, CHANNELINDEX chn, FileReader &fileChunk) -//----------------------------------------------------------------------------- -{ - fileChunk.Rewind(); - ModCommand::INSTR lastInstr = 0; - while(fileChunk.AreBytesLeft()) - { - const uint8 row = fileChunk.ReadUint8(); - const uint8 command = fileChunk.ReadUint8(); - const uint8 value = fileChunk.ReadUint8(); - if(row >= pattern.GetNumRows()) - { - break; - } - - ModCommand &m = *pattern.GetpModCommand(row, chn); - if(command < 0x7F) - { - // Note + Volume - if(command == 0 && value == 0) - { - m.note = NOTE_NOTECUT; - } else - { - m.note = command + NOTE_MIN; - if(value != 0xFF) - { - if(!m.instr) m.instr = lastInstr; - m.volcmd = VOLCMD_VOLUME; - m.vol = value; - } - } - } else if(command == 0x7F) - { - // Duplicate row - int8 rowDelta = static_cast<int8>(value); - int16 copyRow = static_cast<int16>(row) + rowDelta; - if(copyRow >= 0 && copyRow < static_cast<int16>(pattern.GetNumRows())) - { - m = *pattern.GetpModCommand(copyRow, chn); - } - } else if(command == 0x80) - { - // Instrument - m.instr = value + 1; - lastInstr = m.instr; - } else - { - // Effect - static const ModCommand::COMMAND effTrans[] = - { - CMD_NONE, CMD_SPEED, CMD_VOLUMESLIDE, CMD_VOLUME, - CMD_PORTAMENTOUP, CMD_NONE, CMD_TONEPORTAMENTO, CMD_TREMOR, - CMD_ARPEGGIO, CMD_VIBRATO, CMD_TONEPORTAVOL, CMD_VIBRATOVOL, - CMD_PATTERNBREAK, CMD_POSITIONJUMP, CMD_NONE, CMD_RETRIG, - CMD_OFFSET, CMD_VOLUMESLIDE, CMD_PORTAMENTOUP, CMD_S3MCMDEX, - CMD_S3MCMDEX, CMD_TEMPO, CMD_PORTAMENTOUP, CMD_PANNING8, - }; - - uint8 cmd = (command & 0x7F); - uint8 param = value; - - if(cmd < CountOf(effTrans)) - { - cmd = effTrans[cmd]; - } else - { - cmd = CMD_NONE; - } - - // Fix some commands... - switch(command & 0x7F) - { - // 02: Volume Slide - // 0A: Tone Porta + Vol Slide - // 0B: Vibrato + Vol Slide - case 0x02: - case 0x0A: - case 0x0B: - if(param & 0x80) - param = (-static_cast<int8>(param)) & 0x0F; - else - param = (param & 0x0F) << 4; - break; - - // 03: Volume - case 0x03: - param = std::min(param, uint8(64)); - if(m.volcmd == VOLCMD_NONE || m.volcmd == VOLCMD_VOLUME) - { - m.volcmd = VOLCMD_VOLUME; - m.vol = param; - cmd = CMD_NONE; - } - break; - - // 04: Porta Up/Down - case 0x04: - if(param & 0x80) - param = (-static_cast<int8>(param)) & 0x7F; - else - cmd = CMD_PORTAMENTODOWN; - break; - - // 11: Fine Volume Slide - case 0x11: - if(param) - { - if(param & 0x80) - param = 0xF0 | ((-static_cast<int8>(param)) & 0x0F); - else - param = 0x0F | ((param & 0x0F) << 4); - } else - { - cmd = CMD_NONE; - } - break; - - // 12: Fine Portamento - // 16: Extra Fine Portamento - case 0x12: - case 0x16: - if(param) - { - cmd = static_cast<uint8>((param & 0x80) ? CMD_PORTAMENTOUP : CMD_PORTAMENTODOWN); - if(param & 0x80) - { - param = ((-static_cast<int8>(param)) & 0x0F); - } - param |= (command == 0x16) ? 0xE0 : 0xF0; - } else - { - cmd = CMD_NONE; - } - break; - - // 13: Note Delay - case 0x13: - param = 0xD0 | (param & 0x0F); - break; - - // 14: Note Cut - case 0x14: - param = 0xC0 | (param & 0x0F); - break; - - // 17: Panning - case 0x17: - param = (param + 64) & 0x7F; - if(m.command != CMD_NONE) - { - if(m.volcmd == VOLCMD_NONE || m.volcmd == VOLCMD_PANNING) - { - m.volcmd = VOLCMD_PANNING; - m.vol = param / 2; - } - cmd = CMD_NONE; - } - break; - } - - if(cmd != CMD_NONE) - { - m.command = cmd; - m.param = param; - } - } - } -} - - -bool CSoundFile::ReadAMF_DSMI(FileReader &file, ModLoadingFlags loadFlags) -//------------------------------------------------------------------------ -{ - file.Rewind(); - - AMFFileHeader fileHeader; - if(!file.ReadConvertEndianness(fileHeader) - || memcmp(fileHeader.amf, "AMF", 3) - || fileHeader.version < 8 || fileHeader.version > 14 - || ((fileHeader.numChannels < 1 || fileHeader.numChannels > 32) && fileHeader.version >= 10)) - { - return false; - } else if(loadFlags == onlyVerifyHeader) - { - return true; - } - - InitializeGlobals(); - InitializeChannels(); - - m_nType = MOD_TYPE_AMF; - m_nChannels = fileHeader.numChannels; - m_nSamples = fileHeader.numSamples; - - mpt::String::Read<mpt::String::maybeNullTerminated>(songName, fileHeader.title); - - if(fileHeader.version < 10) - { - // Old format revisions are fixed to 4 channels - fileHeader.numChannels = 4; - file.SkipBack(1); - SetupMODPanning(true); - } - - // Setup Channel Pan Positions - if(fileHeader.version >= 11) - { - const CHANNELINDEX readChannels = fileHeader.version >= 12 ? 32 : 16; - for(CHANNELINDEX chn = 0; chn < readChannels; chn++) - { - int16 pan = (file.ReadInt8() + 64) * 2; - if(pan < 0) pan = 0; - if(pan > 256) - { - pan = 128; - ChnSettings[chn].dwFlags = CHN_SURROUND; - } - ChnSettings[chn].nPan = static_cast<uint16>(pan); - } - } else if(fileHeader.version == 10) - { - uint8 panPos[16]; - file.ReadArray(panPos); - for(CHANNELINDEX chn = 0; chn < 16; chn++) - { - ChnSettings[chn].nPan = (panPos[chn] & 1) ? 0x40 : 0xC0; - } - } - // To check: Was the channel table introduced in revision 1.0 or 0.9? I only have 0.8 files, in which it is missing... - ASSERT(fileHeader.version != 9); - - // Get Tempo/Speed - if(fileHeader.version >= 13) - { - m_nDefaultTempo = file.ReadUint8(); - m_nDefaultSpeed = file.ReadUint8(); - if(m_nDefaultTempo < 32) - { - m_nDefaultTempo = 125; - } - } else - { - m_nDefaultTempo = 125; - m_nDefaultSpeed = 6; - } - - // Setup Order List - Order.resize(fileHeader.numOrders); - std::vector<ROWINDEX> patternLength(fileHeader.numOrders, 64); - const FileReader::off_t trackStartPos = file.GetPosition() + (fileHeader.version >= 14 ? 2 : 0); - - for(ORDERINDEX ord = 0; ord < fileHeader.numOrders; ord++) - { - Order[ord] = ord; - if(fileHeader.version >= 14) - { - patternLength[ord] = file.ReadUint16LE(); - } - // Track positions will be read as needed. - file.Skip(m_nChannels * 2); - } - - // Read Sample Headers - std::vector<uint32> samplePos(GetNumSamples(), 0); - uint32 maxSamplePos = 0; - - for(SAMPLEINDEX smp = 1; smp <= GetNumSamples(); smp++) - { - ModSample &sample = Samples[smp]; - sample.Initialize(); - - uint8 type = file.ReadUint8(); - file.ReadString<mpt::String::maybeNullTerminated>(m_szNames[smp], 32); - file.ReadString<mpt::String::nullTerminated>(sample.filename, 13); - samplePos[smp - 1] = file.ReadUint32LE(); - if(fileHeader.version < 10) - { - sample.nLength = file.ReadUint16LE(); - } else - { - sample.nLength = file.ReadUint32LE(); - } - - sample.nC5Speed = file.ReadUint16LE(); - sample.nVolume = std::min(file.ReadUint8(), uint8(64)) * 4u; - - if(fileHeader.version < 10) - { - // Various sources (Miodrag Vallat's amf.txt, old ModPlug code) suggest that the loop information - // format revision 1.0 should only consist of a 16-bit value for the loop start (loop end would - // automatically equal sample length), but the only v1.0 files I have ("the tribal zone" and - // "the way its gonna b" by Maelcum) do not confirm this - the sample headers are laid out exactly - // as in the newer revisions in these two files. Even in format revision 0.8 (output by MOD2AMF v1.02) - // There are loop start and loop end values (although they are 16-Bit). Maybe this only applies to - // even older revision of the format? - sample.nLoopStart = file.ReadUint16LE(); - sample.nLoopEnd = file.ReadUint16LE(); - } else - { - sample.nLoopStart = file.ReadUint32LE(); - sample.nLoopEnd = file.ReadUint32LE(); - } - - // Length of v1.0+ sample header: 65 bytes - // Length of old sample header: 59 bytes - - if(type != 0) - { - if(sample.nLoopEnd > sample.nLoopStart + 2 && sample.nLoopEnd <= sample.nLength) - { - sample.uFlags.set(CHN_LOOP); - } else - { - sample.nLoopStart = sample.nLoopEnd = 0; - } - - maxSamplePos = std::max(maxSamplePos, samplePos[smp - 1]); - } - } - - // Read Track Mapping Table - std::vector<uint16> trackMap; - file.ReadVectorLE(trackMap, fileHeader.numTracks); - uint16 trackCount = 0; - for(std::vector<uint16>::const_iterator i = trackMap.begin(); i != trackMap.end(); i++) - { - trackCount = std::max(trackCount, *i); - } - - // Store Tracks Positions - std::vector<FileReader> trackData(trackCount); - for(uint16 i = 0; i < trackCount; i++) - { - // Track size is a 24-Bit value describing the number of byte triplets in this track. - uint32 trackSize = file.ReadUint16LE() | (file.ReadUint8() << 16); - trackData[i] = file.GetChunk(trackSize * 3); - } - - if(loadFlags & loadSampleData) - { - // Read Sample Data - const SampleIO sampleIO( - SampleIO::_8bit, - SampleIO::mono, - SampleIO::littleEndian, - SampleIO::unsignedPCM); - - // Why is all of this sample loading business so dumb in AMF? - // Surely there must be some great idea behind it which isn't handled here (re-using the same sample data for different sample slots maybe?) - for(uint32 seekPos = 1; seekPos <= maxSamplePos; seekPos++) - { - for(SAMPLEINDEX smp = 0; smp < GetNumSamples(); smp++) - { - if(seekPos == samplePos[smp]) - { - sampleIO.ReadSample(Samples[smp + 1], file); - break; - } - } - if(file.NoBytesLeft()) - { - break; - } - } - } - - if(!(loadFlags & loadPatternData)) - { - return true; - } - - // Create the patterns from the list of tracks - for(PATTERNINDEX pat = 0; pat < fileHeader.numOrders; pat++) - { - if(Patterns.Insert(pat, patternLength[pat])) - { - continue; - } - - // Get table with per-channel track assignments - file.Seek(trackStartPos + pat * (GetNumChannels() * 2 + (fileHeader.version >= 14 ? 2 : 0))); - std::vector<uint16> tracks; - file.ReadVectorLE(tracks, GetNumChannels()); - - for(CHANNELINDEX chn = 0; chn < GetNumChannels(); chn++) - { - if(tracks[chn] > 0 && tracks[chn] <= fileHeader.numTracks) - { - uint16 realTrack = trackMap[tracks[chn] - 1]; - if(realTrack > 0 && realTrack <= trackCount) - { - realTrack--; - AMFReadPattern(Patterns[pat], chn, trackData[realTrack]); - } - } - } - } - - return true; -} Deleted: trunk/OpenMPT/soundlib/LOAD_DBM.CPP =================================================================== --- trunk/OpenMPT/soundlib/LOAD_DBM.CPP 2013-07-05 12:13:18 UTC (rev 2501) +++ trunk/OpenMPT/soundlib/LOAD_DBM.CPP 2013-07-05 16:55:57 UTC (rev 2502) @@ -1,506 +0,0 @@ -/* - * Load_dbm.cpp - * ------------ - * Purpose: DigiBooster Pro module Loader (DBM) - * Notes : This loader doesn't handle multiple songs. - * Authors: Olivier Lapicque - * Adam Goode (endian and char fixes for PPC) - * OpenMPT Devs - * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. - */ - - -#include "stdafx.h" -#include "Loaders.h" - -#define DBM_FILE_MAGIC 0x304d4244 -#define DBM_ID_NAME 0x454d414e -#define DBM_NAMELEN 0x2c000000 -#define DBM_ID_INFO 0x4f464e49 -#define DBM_INFOLEN 0x0a000000 -#define DBM_ID_SONG 0x474e4f53 -#define DBM_ID_INST 0x54534e49 -#define DBM_ID_VENV 0x564e4556 -#define DBM_ID_PATT 0x54544150 -#define DBM_ID_SMPL 0x4c504d53 - -#ifdef NEEDS_PRAGMA_PACK -#pragma pack(push, 1) -#endif - -struct PACKED DBMFileHeader -{ - uint32 dbm_id; // "DBM0" = 0x304d4244 - uint8 trkVerHi; // Tracker version: 02.15 - uint8 trkVerLo; - uint16 reserved; - uint32 name_id; // "NAME" = 0x454d414e - uint32 name_len; // name length: always 44 - char songname[44]; - uint32 info_id; // "INFO" = 0x4f464e49 - uint32 info_len; // 0x0a000000 - uint16 instruments; - uint16 samples; - uint16 songs; - uint16 patterns; - uint16 channels; - uint32 song_id; // "SONG" = 0x474e4f53 - uint32 song_len; - char songname2[44]; - uint16 orders; -// uint16 orderlist[0]; // orderlist[orders] in words -}; - -STATIC_ASSERT(sizeof(DBMFileHeader) == 132); - -struct PACKED DBMInstrument -{ - char name[30]; - uint16 sampleno; - uint16 volume; - uint32 finetune; - uint32 loopstart; - uint32 looplen; - uint16 panning; - uint16 flags; -}; - -STATIC_ASSERT(sizeof(DBMInstrument) == 50); - -struct PACKED DBMEnvelope -{ - uint16 instrument; - uint8 flags; - uint8 numpoints; - uint8 sustain1; - uint8 loopbegin; - uint8 loopend; - uint8 sustain2; - uint16 volenv[2 * 32]; -}; - -STATIC_ASSERT(sizeof(DBMEnvelope) == 136); - -struct PACKED DBMPattern -{ - uint16 rows; - uint32 packedsize; - uint8 patterndata[2]; // [packedsize] -}; - -STATIC_ASSERT(sizeof(DBMPattern) == 8); - -struct PACKED DBMSample -{ - uint32 flags; - uint32 samplesize; - uint8 sampledata[2]; // [samplesize] -}; - -STATIC_ASSERT(sizeof(DBMSample) == 10); - -#ifdef NEEDS_PRAGMA_PACK -#pragma pack(pop) -#endif - - -static const ModCommand::COMMAND dbmEffects[23] = -{ - CMD_ARPEGGIO, CMD_PORTAMENTOUP, CMD_PORTAMENTODOWN, CMD_TONEPORTAMENTO, - CMD_VIBRATO, CMD_TONEPORTAVOL, CMD_VIBRATOVOL, CMD_TREMOLO, - CMD_PANNING8, CMD_OFFSET, CMD_VOLUMESLIDE, CMD_POSITIONJUMP, - CMD_VOLUME, CMD_PATTERNBREAK, CMD_MODCMDEX, CMD_TEMPO, - CMD_GLOBALVOLUME, CMD_GLOBALVOLSLIDE, CMD_KEYOFF, CMD_SETENVPOSITION, - CMD_CHANNELVOLUME, CMD_CHANNELVOLSLIDE, CMD_PANNINGSLIDE, -}; - - -void ConvertDBMEffect(uint8 &command, uint8 ¶m) -//------------------------------------------------- -{ - if(command < CountOf(dbmEffects)) - command = dbmEffects[command]; - else - command = CMD_NONE; - - switch (command) - { - case CMD_ARPEGGIO: - if(param == 0) - command = CMD_NONE; - break; - - case CMD_VOLUMESLIDE: - if(param & 0xF0) - param &= 0xF0; - break; - - case CMD_GLOBALVOLUME: - if(param <= 64) - param *= 2; - else - param = 128; - break; - - case CMD_MODCMDEX: - switch(param & 0xF0) - { - case 0x00: // set filter - command = CMD_NONE; - break; - case 0x30: // play backwards - command = CMD_S3MCMDEX; - param = 0x9F; - break; - case 0x40: // turn off sound in channel - // TODO - break; - case 0x50: // turn on/off channel - // TODO is this correct? - if((param & 0x0F) <= 0x01) - { - command = CMD_CHANNELVOLUME; - param = (param == 0x50) ? 0x00 : 0x40; - } - break; - case 0x60: // set loop begin / loop - // TODO - break; - case 0x70: // set offset - // TODO - break; - case 0xF0: // turn on/off channel - // TODO - break; - default: - // Rest will be converted later from CMD_MODCMDEX to CMD_S3MCMDEX. - break; - } - break; - - case CMD_TEMPO: - if(param <= 0x1F) command = CMD_SPEED; - break; - - case CMD_KEYOFF: - if (param == 0) - { - // TODO key of at tick 0 - } - break; - - case CMD_OFFSET: - // TODO Sample offset slide - command = CMD_NONE; - break; - } -} - - -bool CSoundFile::ReadDBM(const BYTE *lpStream, const DWORD dwMemLength, ModLoadingFlags loadFlags) -//------------------------------------------------------------------------------------------------ -{ - const DBMFileHeader *pfh = (DBMFileHeader *)lpStream; - DWORD dwMemPos; - uint16 nOrders, nSamples, nInstruments, nPatterns; - - if ((!lpStream) || (dwMemLength <= sizeof(DBMFileHeader)) || (!pfh->channels) - || (pfh->dbm_id != DBM_FILE_MAGIC) || (!pfh->songs) || (pfh->song_id != DBM_ID_SONG) - || (pfh->name_id != DBM_ID_NAME) || (pfh->name_len != DBM_NAMELEN) - || (pfh->info_id != DBM_ID_INFO) || (pfh->info_len != DBM_INFOLEN)) return false; - dwMemPos = sizeof(DBMFileHeader); - nOrders = BigEndianW(pfh->orders); - if (dwMemPos + 2 * nOrders + 8*3 >= dwMemLength) - return false; - else if(loadFlags == onlyVerifyHeader) - return true; - - InitializeGlobals(); - InitializeChannels(); - - nInstruments = BigEndianW(pfh->instruments); - nSamples = BigEndianW(pfh->samples); - nPatterns = BigEndianW(pfh->patterns); - m_nType = MOD_TYPE_DBM; - m_nChannels = CLAMP(BigEndianW(pfh->channels), 1, MAX_BASECHANNELS); // note: MAX_BASECHANNELS is currently 127, but DBM supports up to 128 channels. - madeWithTracker = mpt::String::Format("Digi Booster %x.%x", pfh->trkVerHi, pfh->trkVerLo); - - if(pfh->songname[0]) - { - mpt::String::Read<mpt::String::maybeNullTerminated>(songName, pfh->songname); - } else - { - mpt::String::Read<mpt::String::maybeNullTerminated>(songName, pfh->songname2); - - } - - Order.resize(nOrders, Order.GetInvalidPatIndex()); - for (UINT iOrd=0; iOrd < nOrders; iOrd++) - { - if (iOrd >= MAX_ORDERS) break; - Order[iOrd] = (PATTERNINDEX)BigEndianW(*((WORD *)(lpStream + dwMemPos + iOrd * 2))); - } - dwMemPos += 2 * nOrders; - while (dwMemPos + 10 < dwMemLength) - { - uint32 chunk_id = LittleEndian(((uint32 *)(lpStream + dwMemPos))[0]); - uint32 chunk_size = BigEndian(((uint32 *)(lpStream + dwMemPos))[1]); - uint32 chunk_pos; - - dwMemPos += 8; - chunk_pos = dwMemPos; - if ((dwMemPos + chunk_size > dwMemLength) || (chunk_size > dwMemLength)) break; - dwMemPos += chunk_size; - // Instruments - if (chunk_id == DBM_ID_INST) - { - if (nInstruments >= MAX_INSTRUMENTS) nInstruments = MAX_INSTRUMENTS-1; - for(INSTRUMENTINDEX iIns = 0; iIns < nInstruments; iIns++) - { - ModSample *psmp; - ModInstrument *pIns; - DBMInstrument *pih; - uint16 nsmp; - - if (chunk_pos + sizeof(DBMInstrument) > dwMemPos) break; - - pih = (DBMInstrument *)(lpStream + chunk_pos); - nsmp = BigEndianW(pih->sampleno); - psmp = ((nsmp) && (nsmp < MAX_SAMPLES)) ? &Samples[nsmp] : nullptr; - - pIns = AllocateInstrument(iIns + 1, nsmp); - if(pIns == nullptr) - { - break; - } - - mpt::String::Read<mpt::String::maybeNullTerminated>(pIns->name, pih->name); - if (psmp) - { - mpt::String::Read<mpt::String::maybeNullTerminated>(m_szNames[nsmp], pih->name); - } - - pIns->nFadeOut = 1024; // ??? - pIns->nPan = BigEndianW(pih->panning); - if ((pIns->nPan) && (pIns->nPan < 256)) - pIns->dwFlags = INS_SETPANNING; - else - pIns->nPan = 128; - - // Sample Info - if(psmp) - { - uint16 sflags = BigEndianW(pih->flags); - psmp->nVolume = BigEndianW(pih->volume) * 4; - if(/*!psmp->nVolume ||*/ psmp->nVolume > 256) psmp->nVolume = 256; // XXX First condition looks like a typical "modplug-ism" - psmp->nGlobalVol = 64; - psmp->nC5Speed = BigEndian(pih->finetune); - - if(pih->looplen && (sflags & 3)) - { - psmp->nLoopStart = BigEndian(pih->loopstart); - psmp->nLoopEnd = psmp->nLoopStart + BigEndian(pih->looplen); - psmp->uFlags |= CHN_LOOP; - psmp->uFlags &= ~CHN_PINGPONGLOOP; - if(sflags & 2) psmp->uFlags |= CHN_PINGPONGLOOP; - } - } - chunk_pos += sizeof(DBMInstrument); - m_nInstruments = iIns + 1; - } - } else - // Volume Envelopes - if (chunk_id == DBM_ID_VENV) - { - UINT nEnvelopes = lpStream[chunk_pos+1]; - - chunk_pos += 2; - for (UINT iEnv=0; iEnv<nEnvelopes; iEnv++) - { - DBMEnvelope *peh; - UINT nins; - - if (chunk_pos + sizeof(DBMEnvelope) > dwMemPos) break; - peh = (DBMEnvelope *)(lpStream+chunk_pos); - nins = BigEndianW(peh->instrument); - if ((nins) && (nins < MAX_INSTRUMENTS) && (Instruments[nins]) && (peh->numpoints)) - { - ModInstrument *pIns = Instruments[nins]; - - pIns->VolEnv.dwFlags.set(ENV_ENABLED, (peh->flags & 1) != 0); - pIns->VolEnv.dwFlags.set(ENV_SUSTAIN, (peh->flags & 2) != 0); - pIns->VolEnv.dwFlags.set(ENV_LOOP, (peh->flags & 4) != 0); - pIns->VolEnv.nNodes = peh->numpoints + 1; - if (pIns->VolEnv.nNodes > MAX_ENVPOINTS) pIns->VolEnv.nNodes = MAX_ENVPOINTS; - pIns->VolEnv.nLoopStart = peh->loopbegin; - pIns->VolEnv.nLoopEnd = peh->loopend; - pIns->VolEnv.nSustainStart = pIns->VolEnv.nSustainEnd = peh->sustain1; - for(uint32 i=0; i<pIns->VolEnv.nNodes; i++) - { - pIns->VolEnv.Ticks[i] = BigEndianW(peh->volenv[i * 2]); - pIns->VolEnv.Values[i] = (BYTE)BigEndianW(peh->volenv[i * 2 + 1]); - } - } - chunk_pos += sizeof(DBMEnvelope); - } - } else - // Packed Pattern Data - if (chunk_id == DBM_ID_PATT && (loadFlags & loadPatternData)) - { - if (nPatterns > MAX_PATTERNS) nPatterns = MAX_PATTERNS; - for(PATTERNINDEX iPat = 0; iPat < nPatterns; iPat++) - { - DBMPattern *pph; - DWORD pksize; - UINT nRows; - - if (chunk_pos + sizeof(DBMPattern) > dwMemPos) break; - pph = (DBMPattern *)(lpStream+chunk_pos); - pksize = BigEndian(pph->packedsize); - if ((chunk_pos + pksize + 6 > dwMemPos) || (pksize > dwMemPos)) break; - nRows = BigEndianW(pph->rows); - if ((nRows >= 4) && (nRows <= 256)) - { - Patterns.Insert(iPat, nRows); - ModCommand *m = Patterns[iPat]; - if (m) - { - LPBYTE pkdata = (LPBYTE)&pph->patterndata; - UINT row = 0; - UINT i = 0; - - while ((i+3<pksize) && (row < nRows)) - { - UINT ch = pkdata[i++]; - - if (ch) - { - BYTE b = pkdata[i++]; - ch--; - if (ch < m_nChannels) - { - if (b & 0x01) - { - uint8 note = pkdata[i++]; - - if (note == 0x1F) note = NOTE_KEYOFF; else - if ((note) && (note < 0xFE)) - { - note = ((note >> 4) * 12) + (note & 0x0F) + 13; - } - m[ch].note = note; - } - if (b & 0x02) m[ch].instr = pkdata[i++]; - if (b & 0x3C) - { - uint8 cmd1 = CMD_NONE, cmd2 = CMD_NONE; - uint8 param1 = 0, param2 = 0; - if (b & 0x04) cmd2 = pkdata[i++]; - if (b & 0x08) param2 = pkdata[i++]; - if (b & 0x10) cmd1 = pkdata[i++]; - if (b & 0x20) param1 = pkdata[i++]; - ConvertDBMEffect(cmd1, param1); - ConvertDBMEffect(cmd2, param2); - - // this is the same conversion algorithm as in the ULT loader. maybe this should be merged at some point... - if (cmd2 == CMD_VOLUME || (cmd2 == CMD_NONE && cmd1 != CMD_VOLUME)) - { - std::swap(cmd1, cmd2); - std::swap(param1, param2); - } - - int n; - for (n = 0; n < 4; n++) - { - if(ModCommand::ConvertVolEffect(cmd1, param1, (n >> 1) != 0)) - { - n = 5; - break; - } - std::swap(cmd1, cmd2); - std::swap(param1, param2); - } - if (n < 5) - { - if (ModCommand::GetEffectWeight((ModCommand::COMMAND)cmd1) > ModCommand::GetEffectWeight((ModCommand::COMMAND)cmd2)) - { - std::swap(cmd1, cmd2); - std::swap(param1, param2); - } - cmd1 = CMD_NONE; - } - if (!cmd1) - param1 = 0; - if (!cmd2) - param2 = 0; - - m[ch].volcmd = cmd1; - m[ch].vol = param1; - m[ch].command = cmd2; - m[ch].param = param2; - m[ch].ExtendedMODtoS3MEffect(); - } - } else - { - if (b & 0x01) i++; - if (b & 0x02) i++; - if (b & 0x04) i++; - if (b & 0x08) i++; - if (b & 0x10) i++; - if (b & 0x20) i++; - } - } else - { - row++; - m += m_nChannels; - } - } - } - } - chunk_pos += 6 + pksize; - } - } else - // Reading Sample Data - if (chunk_id == DBM_ID_SMPL && (loadFlags & loadSampleData)) - { - if (nSamples >= MAX_SAMPLES) nSamples = MAX_SAMPLES-1; - m_nSamples = nSamples; - for (UINT iSmp=1; iSmp<=nSamples; iSmp++) - { - DBMSample *psh; - DWORD samplesize; - DWORD sampleflags; - - if (chunk_pos + sizeof(DBMSample) >= dwMemPos) break; - psh = (DBMSample *)(lpStream+chunk_pos); - chunk_pos += 8; - samplesize = BigEndian(psh->samplesize); - sampleflags = BigEndian(psh->flags); - - ModSample &sample = Samples[iSmp]; - sample.nLength = samplesize; - if(sampleflags & 2) - { - samplesize *= 2; - } - if(chunk_pos + samplesize > dwMemPos || samplesize > dwMemLength) - { - break; - } - - if(sampleflags & 3) - { - FileReader chunk(psh->sampledata, samplesize); - SampleIO( - (sampleflags & 2) ? SampleIO::_16bit : SampleIO::_8bit, - SampleIO::mono, - SampleIO::bigEndian, - SampleIO::signedPCM) - .ReadSample(sample, chunk); - } - chunk_pos += samplesize; - } - } - } - return true; -} Deleted: trunk/OpenMPT/soundlib/LOAD_DMF.CPP =================================================================== --- trunk/OpenMPT/soundlib/LOAD_DMF.CPP 2013-07-05 12:13:18 UTC (rev 2501) +++ trunk/OpenMPT/soundlib/LOAD_DMF.CPP 2013-07-05 16:55:57 UTC (rev 2502) @@ -1,1225 +0,0 @@ -/* - * load_dmf.cpp - * ------------ - * Purpose: DMF module loader (X-Tracker by D-LUSiON). - * Notes : If it wasn't already outdated when the tracker was released, this would be a rather interesting - * and in some parts even sophisticated format - effect columns are separated by effect type, an easy to - * understand BPM tempo mode, effect durations are always divided into a 256th row, vibrato effects are - * specified by period length and the same 8-Bit granularity is used for both volume and panning. - * Unluckily, this format does not offer any envelopes or multi-sample instruments, and bidi sample loops - * are missing as well, so it was already well behind FT2 and IT back then. - * Authors: Johannes Schultz (mostly based on DMF.TXT, DMF_EFFC.TXT, trial and error and some invaluable hints by Zatzen) - * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. - */ - - -#include "stdafx.h" -#include "Loaders.h" -#include "ChunkReader.h" -#ifdef MODPLUG_TRACKER -#include "../mptrack/Moddoc.h" -#endif // MODPLUG_TRACKER - -#ifdef NEEDS_PRAGMA_PACK -#pragma pack(push, 1) -#endif - -// DMF header -struct PACKED DMFFileHeader -{ - char signature[4]; // "DDMF" - uint8 version; // 1 - 7 are beta versions, 8 is the official thing, 10 is xtracker32 - char tracker[8]; // "XTRACKER" - char songname[30]; - char composer[20]; - uint8 creationDay; - uint8 creationMonth; - uint8 creationYear; -}; - -STATIC_ASSERT(sizeof(DMFFileHeader) == 66); - -struct PACKED DMFChunk -{ - // 32-Bit chunk identifiers - enum ChunkIdentifiers - { - idCMSG = 0x47534D43, // Song message - idSEQU = 0x55514553, // Order list - idPATT = 0x54544150, // Patterns - idSMPI = 0x49504D53, // Sample headers - idSMPD = 0x44504D53, // Sample data - idSMPJ = 0x4A504D53, // Sample jump table (XTrakcker 32 only) - idENDE = 0x45444E45, // Last four bytes of DMF file - idSETT = 0x9C219DE4, // Probably contains GUI settings - }; - - typedef ChunkIdentifiers id_type; - - uint32 id; - uint32 length; - - size_t GetLength() const - { - uint32 l = length; - return SwapBytesLE(l); - } - - id_type GetID() const - { - uint32 i = id; - return static_cast<id_type>(SwapBytesLE(i)); - } -}; - -STATIC_ASSERT(sizeof(DMFChunk) == 8); - -// Order list -struct PACKED DMFSequence -{ - uint16 loopStart; - uint16 loopEnd; - // order list follows here ... - - // Convert all multi-byte numeric values to current platform's endianness or vice versa. - void ConvertEndianness() - { - SwapBytesLE(loopStart); - SwapBytesLE(loopEnd); - } -}; - -STATIC_ASSERT(sizeof(DMFSequence) == 4); - -// Pattern header (global) -struct PACKED DMFPatterns -{ - uint16 numPatterns; // 1..1024 patterns - uint8 numTracks; // 1..32 channels - - // Convert all multi-byte numeric values to current platform's endianness or vice versa. - void ConvertEndianness() - { - SwapBytesLE(numPatterns); - } -}; - -STATIC_ASSERT(sizeof(DMFPatterns) == 3); - -// Pattern header (for each pattern) -struct PACKED DMFPatternHeader -{ - uint8 numTracks; // 1..32 channels - uint8 beat; // [hi|lo] -> hi = rows per beat, lo = reserved - uint16 numRows; - uint32 patternLength; - // patttern data follows here ... - - // Convert all multi-byte numeric values to current platform's endianness or vice versa. - void ConvertEndianness() - { - SwapBytesLE(numRows); - SwapBytesLE(patternLength); - } -}; - -STATIC_ASSERT(sizeof(DMFPatternHeader) == 8); - -// Sample header -struct PACKED DMFSampleHeader -{ - enum SampleFlags - { - // Sample flags - smpLoop = 0x01, - smp16Bit = 0x02, - smpCompMask = 0x0C, - smpComp1 = 0x04, // Compression type 1 - smpComp2 = 0x08, // Compression type 2 (unused) - smpComp3 = 0x0C, // Compression type 3 (dito) - smpLibrary = 0x80, // Sample is stored in a library - }; - - uint32 length; - uint32 loopStart; - uint32 loopEnd; - uint16 c3freq; // 1000..45000hz - uint8 volume; // 0 = ignore - uint8 flags; - - // Convert an DMFSampleHeader to OpenMPT's internal sample representation. - void ConvertToMPT(ModSample &mptSmp) const - { - mptSmp.Initialize(); - mptSmp.nLength = length; - mptSmp.nSustainEnd = loopEnd; - mptSmp.nSustainStart = loopStart; - if(mptSmp.nSustainEnd > 0) - { - mptSmp.nSustainEnd--; - } - mptSmp.SanitizeLoops(); - - mptSmp.nC5Speed = c3freq; - mptSmp.nGlobalVol = 64; - if(volume) - { - mptSmp.nVolume = volume + 1; - } else - { - mptSmp.nVolume = 256; - } - - if((flags & smpLoop) != 0 && mptSmp.nSustainEnd > mptSmp.nSustainStart) - { - mptSmp.uFlags.set(CHN_SUSTAINLOOP); - } - if((flags & smp16Bit) != 0) - { - mptSmp.uFlags.set(CHN_16BIT); - mptSmp.nLength /= 2; - mptSmp.nSustainStart /= 2; - mptSmp.nSustainEnd /= 2; - } - } - - // Convert all multi-byte numeric values to current platform's endianness or vice versa. - void ConvertEndianness() - { - SwapBytesLE(length); - SwapBytesLE(loopStart); - SwapBytesLE(loopEnd); - SwapBytesLE(c3freq); - } -}; - -STATIC_ASSERT(sizeof(DMFSampleHeader) == 16); - -// Sample header tail (between head and tail, there might be the library name of the sample, depending on the DMF version) -struct PACKED DMFSampleHeaderTail -{ - uint16 filler; - uint32 crc32; -}; - -STATIC_ASSERT(sizeof(DMFSampleHeaderTail) == 6); - -#ifdef NEEDS_PRAGMA_PACK -#pragma pack(pop) -#endif - -// Pattern translation memory -struct DMFPatternSettings -{ - struct ChannelState - { - ModCommand::NOTE noteBuffer; // Note buffer - ModCommand::NOTE lastNote; // Last played note on channel - uint8 vibratoType; // Last used vibrato type on channel - uint8 tremoloType; // Last used tremolo type on channel - uint8 highOffset; // Last used high offset on channel - bool playDir; // Sample play direction... false = forward (default) - - ChannelState() - { - noteBuffer = lastNote = NOTE_NONE; - vibratoType = 8; - tremoloType = 4; - highOffset = 0; - playDir = false; - } - }; - - std::vector<ChannelState> channels; // Memory for each channel's state - bool realBPMmode; // true = BPM mode - uint8 beat; // Rows per beat - uint8 tempoTicks; // Tick mode param - uint8 tempoBPM; // BPM mode param - uint8 internalTicks; // Ticks per row in final pattern - - DMFPatternSettings(CHANNELINDEX numChannels) : channels(numChannels) - { - realBPMmode = false; - beat = 0; - tempoTicks = 32; - tempoBPM = 120; - internalTicks = 6; - } -}; - - -// Convert portamento value (not very accurate due to X-Tracker's higher granularity, to say the least) -static uint8 DMFporta2MPT(uint8 val, const uint8 internalTicks, const bool hasFine) -//--------------------------------------------------------------------------------- -{ - if(val == 0) - return 0; - else if((val <= 0x0F || internalTicks < 2) && hasFine) - return (val | 0xF0); - else - return MAX(1, (val / (internalTicks - 1))); // no porta on first tick! -} - - -// Convert portamento / volume slide value (not very accurate due to X-Tracker's higher granularity, to say the least) -static uint8 DMFslide2MPT(uint8 val, const uint8 internalTicks, const bool up) -//---------------------------------------------------------------------------- -{ - val = MAX(1, val / 4); - const bool isFine = (val < 0x0F) || (internalTicks < 2); - if(!isFine) - val = MAX(1, (val + internalTicks - 2) / (internalTicks - 1)); // no slides on first tick! "+ internalTicks - 2" for rounding precision - - if(up) - return (isFine ? 0x0F : 0x00) | (val << 4); - else - return (isFine ? 0xF0 : 0x00) | (val & 0x0F); - -} - - -// Calculate tremor on/off param -static uint8 DMFtremor2MPT(uint8 val, const uint8 internalTicks) -//-------------------------------------------------------------- -{ - uint8 ontime = (val >> 4); - uint8 offtime = (val & 0x0F); - ontime = CLAMP(ontime * internalTicks / 15, 1, 15); - offtime = CLAMP(offtime * internalTicks / 15, 1, 15); - return (ontime << 4) | offtime; -} - - -// Calculate delay parameter for note cuts / delays -static uint8 DMFdelay2MPT(uint8 val, const uint8 internalTicks) -//------------------------------------------------------------- -{ - int newval = (int)val * (int)internalTicks / 255; - Limit(newval, 0, 15); - return (uint8)newval; -} - - -// Convert vibrato-style command parameters -static uint8 DMFvibrato2MPT(uint8 val, const uint8 internalTicks) -//--------------------------------------------------------------- -{ - // MPT: 1 vibrato period == 64 ticks... we have internalTicks ticks per row. - // X-Tracker: Period length specified in rows! - const int periodInTicks = MAX(1, (val >> 4)) * internalTicks; - const uint8 matchingPeriod = (uint8)CLAMP((128 / periodInTicks), 1, 15); - return (matchingPeriod << 4) | MAX(1, (val & 0x0F)); -} - - -// Try using effect memory (zero paramer) to give the effect swapper some optimization hints. -static void ApplyEffectMemory(const ModCommand *m, ROWINDEX row, CHANNELINDEX numChannels, uint8 effect, uint8 ¶m) -//-------------------------------------------------------------------------------------------------------------------- -{ - if(effect == CMD_NONE || param == 0) - { - return; - } - - const bool isTonePortaEffect = (effect == CMD_PORTAMENTOUP || effect == CMD_PORTAMENTODOWN || effect == CMD_TONEPORTAMENTO); - const bool isVolSlideEffect = (effect == CMD_VOLUMESLIDE || effect == CMD_TONEPORTAVOL || effect == CMD_VIBRATOVOL); - - while(row > 0) - { - m -= numChannels; - row--; - - // First, keep some extra rules in mind for portamento, where effect memory is shared between various commands. - bool isSame = (effect == m->command); - if(isTonePortaEffect && (m->command == CMD_PORTAMENTOUP || m->command == CMD_PORTAMENTODOWN || m->command == CMD_TONEPORTAMENTO)) - { - if(m->param < 0xE0) - { - // Avoid effect param for fine slides, or else we could accidentally put this command in the volume column, where fine slides won't work! - isSame = true; - } else - { - return; - } - } else if(isVolSlideEffect && (m->command == CMD_VOLUMESLIDE || m->command == CMD_TONEPORTAVOL || m->command == CMD_VIBRATOVOL)) - { - isSame = true; - } - if(isTonePortaEffect - && (m->volcmd == VOLCMD_PORTAUP || m->volcmd == VOLCMD_PORTADOWN || m->volcmd == VOLCMD_TONEPORTAMENTO) - && m->vol != 0) - { - // Uuh... Don't even try - return; - } else if(isVolSlideEffect - && (m->volcmd == VOLCMD_FINEVOLUP || m->volcmd == VOLCMD_FINEVOLDOWN || m->volcmd == VOLCMD_VOLSLIDEUP || m->volcmd == VOLCMD_VOLSLIDEDOWN) - && m->vol != 0) - { - // Same! - return; - } - - if(isSame) - { - if(param != m->param && m->param != 0) - { - // No way to optimize this - return; - } else if(param == m->param) - { - // Yay! - param = 0; - return; - } - } - } -} - - -static PATTERNINDEX ConvertDMFPattern(FileReader &file, DMFPatternSettings &settings, CSoundFile &sndFile) -//-------------------------------------------------------------------------------------------------------- -{ - // Pattern flags - enum PatternFlags - { - // Global Track - patGlobPack = 0x80, // Pack information for global track follows - patGlobMask = 0x3F, // Mask for global effects - // Note tracks - patCounter = 0x80, // Pack information for current channel follows - patInstr = 0x40, // Instrument number present - patNote = 0x20, // Note present - patVolume = 0x10, // Volume present - patInsEff = 0x08, // Instrument effect present - patNoteEff = 0x04, // Note effect present - patVolEff = 0x02, // Volume effect stored - }; - - file.Rewind(); - - DMFPatternHeader patHead; - file.ReadConvertEndianness(patHead); - - const ROWINDEX numRows = Clamp(ROWINDEX(patHead.numRows), ROWINDEX(1), MAX_PATTERN_ROWS); - const PATTERNINDEX pat = sndFile.Patterns.Insert(numRows); - if(pat == PATTERNINDEX_INVALID) - { - return pat; - } - - PatternRow m = sndFile.Patterns[pat].GetRow(0); - const CHANNELINDEX numChannels = std::min(sndFile.GetNumChannels(), CHANNELINDEX(patHead.numTracks)); - - // When breaking to a pattern with less channels that the previous pattern, - // all voices in the now unused channels are killed: - for(CHANNELINDEX chn = numChannels + 1; chn < sndFile.GetNumChannels(); chn++) - { - m[chn].note = NOTE_NOTECUT; - } - - // Initialize tempo stuff - settings.beat = (patHead.beat >> 4); - bool tempoChange = settings.realBPMmode; - uint8 writeDelay = 0; - - // Counters for channel packing (including global track) - std::vector<uint8> channelCounter(numChannels + 1, 0); - - for(ROWINDEX row = 0; row < numRows; row++) - { - // Global track info counter reached 0 => read global track data - if(channelCounter[0] == 0) - { - uint8 globalInfo = file.ReadUint8(); - // 0x80: Packing counter (if not present, counter stays at 0) - if((globalInfo & patGlobPack) != 0) - { - channelCounter[0] = file.ReadUint8(); - } - - globalInfo &= patGlobMask; - - uint8 globalData = 0; - if(globalInfo != 0) - { - globalData = file.ReadUint8(); - } - - switch(globalInfo) - { - case 1: // Set Tick Frame Speed - settings.realBPMmode = false; - settings.tempoTicks = std::max(uint8(1), globalData); // Tempo in 1/4 rows per second - settings.tempoBPM = 0; // Automatically updated by X-Tracker - tempoChange = true; - break; - case 2: // Set BPM Speed (real BPM mode) - if(globalData) // DATA = 0 doesn't do anything - { - settings.realBPMmode = true; - settings.tempoBPM = globalData; // Tempo in real BPM (depends on rows per beat) - if(settings.beat != 0) - { - settings.tempoTicks = (globalData * settings.beat * 15); // Automatically updated by X-Tracker - } - tempoChange = true; - } - break; - case 3: // Set Beat - settings.beat = (globalData >> 4); - if(settings.beat != 0) - { - // Tempo changes only if we're in real BPM mode - tempoChange = settings.realBPMmode; - } else - { - // If beat is 0, change to tick speed mode, but keep current tempo - settings.realBPMmode = false; - } - break; - case 4: // Tick Delay - writeDelay = globalData; - break; - case 5: // Set External Flag - break; - case 6: // Slide Speed Up - if(globalData > 0) - { - uint8 &tempoData = (settings.realBPMmode) ? settings.tempoBPM : settings.tempoTicks; - if(tempoData < 256 - globalData) - { - tempoData += globalData; - } else - { - tempoData = 255; - } - tempoChange = true; - } - break; - case 7: // Slide Speed Down - if(globalData > 0) - { - uint8 &tempoData = (settings.realBPMmode) ? settings.tempoBPM : settings.tempoTicks; - if(tempoData > 1 + globalData) - { - tempoData -= globalData; - } else - { - tempoData = 1; - } - tempoChange = true; - } - break; - } - } else - { - channelCounter[0]--; - } - - // These will eventually be written to the pattern - int speed = 0, tempo = 0; - - if(tempoChange) - { - // Can't do anything if we're in BPM mode and there's no rows per beat set... - if(!settings.realBPMmode || settings.beat) - { - // My approach to convert X-Tracker's "tick speed" (1/4 rows per second): - // Tempo * 6 / Speed = Beats per Minute - // => Tempo * 6 / (Speed * 60) = Beats per Second - // => Tempo * 24 / (Speed * 60) = Rows per Second (4 rows per beat at tempo 6) - // => Tempo = 60 * Rows per Second * Speed / 24 - // For some reason, using settings.tempoTicks + 1 gives more accurate results than just settings.tempoTicks... (same problem in the old libmodplug DMF loader) - // Original unoptimized formula: - //const int tickspeed = (tempoRealBPMmode) ? MAX(1, (tempoData * beat * 4) / 60) : tempoData; - const int tickspeed = (settings.realBPMmode) ? std::max(1, settings.tempoBPM * settings.beat * 2) : ((settings.tempoTicks + 1) * 30); - // Try to find matching speed - try higher speeds first, so that effects like arpeggio and tremor work better. - for(speed = 255; speed > 1; speed--) - { - // Original unoptimized formula: - // tempo = 30 * tickspeed * speed / 48; - tempo = tickspeed * speed / 48; - if(tempo >= 32 && tempo <= 255) - { - break; - } - } - Limit(tempo, 32, 255); - settings.internalTicks = (uint8)speed; - } else - { - tempoChange = false; - } - } - - m = sndFile.Patterns[pat].GetpModCommand(row, 1); // Reserve first channel for global effects - - for(CHANNELINDEX chn = 1; chn <= numChannels; chn++, m++) - { - // Track info counter reached 0 => read track data - if(channelCounter[chn] == 0) - { - const uint8 channelInfo = file.ReadUint8(); - //////////////////////////////////////////////////////////////// - // 0x80: Packing counter (if not present, counter stays at 0) - if((channelInfo & patCounter) != 0) - { - channelCounter[chn] = file.ReadUint8(); - } - - //////////////////////////////////////////////////////////////// - // 0x40: Instrument - bool slideNote = true; // If there is no instrument number next to a note, the note is not retriggered! - if((channelInfo & patInstr) != 0) - { - m->instr = file.ReadUint8(); - if(m->instr != 0) - { - slideNote = false; - } - } - - //////////////////////////////////////////////////////////////// - // 0x20: Note - if((channelInfo & patNote) != 0) - { - m->note = file.ReadUint8(); - if(m->note >= 1 && m->note <= 108) - { - m->note = static_cast<uint8>(Clamp(m->note + 24, NOTE_MIN, NOTE_MAX)); - settings.channels[chn].lastNote = m->note; - } else if(m->note >= 129 && m->note <= 236) - { - // "Buffer notes" for portamento (and other effects?) that are actually not played, but just "queued"... - m->note = static_cast<uint8>(Clamp((m->note & 0x7F) + 24, NOTE_MIN, NOTE_MAX)); - settings.channels[chn].noteBuffer = m->note; - m->note = NOTE_NONE; - } else if(m->note == 255) - { - m->note = NOTE_NOTECUT; - } - } - - // If there's just an instrument number, but no note, retrigger sample. - if(m->note == NOTE_NONE && m->instr > 0) - { - m->note = settings.channels[chn].lastNote; - m->instr = 0; - } - - if(m->IsNote()) - { - settings.channels[chn].playDir = false; - } - - uint8 effect1 = CMD_NONE, effect2 = CMD_NONE, effect3 = CMD_NONE; - uint8 effectParam1 = 0, effectParam2 = 0, effectParam3 = 0; - bool useMem2 = false, useMem3 = false; // Effect can use memory if necessary - - //////////////////////////////////////////////////////////////// - // 0x10: Volume - if((channelInfo & patVolume) != 0) - { - m->volcmd = VOLCMD_VOLUME; - m->vol = (file.ReadUint8() + 2) / 4; // Should be + 3 instead of + 2, but volume 1 is silent in X-Tracker. - } - - //////////////////////////////////////////////////////////////// - // 0x08: Instrument effect - if((channelInfo & patInsEff) != 0) - { - effect1 = file.ReadUint8(); - effectParam1 = file.ReadUint8(); - - switch(effect1) - { - case 1: // Stop Sample - m->note = NOTE_NOTECUT; - effect1 = CMD_NONE; - break; - case 2: // Stop Sample Loop - m->note = NOTE_KEYOFF; - effect1 = CMD_NONE; - break; - case 3: // Instrument Volume Override (aka "Restart") - m->note = settings.channels[chn].lastNote; - settings.channels[chn].playDir = false; - effect1 = CMD_NONE; - break; - case 4: // Sample Delay - effectParam1 = DMFdelay2MPT(effectParam1, settings.internalTicks); - if(effectParam1) - { - effect1 = CMD_S3MCMDEX; - effectParam1 = 0xD0 | (effectParam1); - } else - { - effect1 = CMD_NONE; - } - if(m->note == NOTE_NONE) - { - m->note = settings.channels[chn].lastNote; - settings.channels[chn].playDir = false; - } - break; - case 5: // Tremolo Retrig Sample (who invented those stupid effect names?) - effectParam1 = MAX(1, DMFdelay2MPT(effectParam1, settings.internalTicks)); - effect1 = CMD_RETRIG; - settings.channels[chn].playDir = false; - break; - case 6: // Offset - case 7: // Offset + 64k - case 8: // Offset + 128k - case 9: // Offset + 192k - // Put high offset on previous row - if(row > 0 && effect1 != settings.channels[chn].highOffset) - { - if(sndFile.Patterns[pat].WriteEffect(EffectWriter(CMD_S3MCMDEX, (0xA0 | (effect1 - 6))).Row(row - 1).Channel(chn).Retry(EffectWriter::rmTryPreviousRow))) - { - settings.channels[chn].highOffset = effect1; - } - } - effect1 = CMD_OFFSET; - if(m->note == NOTE_NONE) - { - // Offset without note does also work in DMF. - m->note = settings.channels[chn].lastNote; - } - settings.channels[chn].playDir = false; - break; - case 10: // Invert Sample play direction ("Tekkno Invert") - effect1 = CMD_S3MCMDEX; - if(settings.channels[chn].playDir == false) - effectParam1 = 0x9F; - else - effectParam1 = 0x9E; - settings.channels[chn].playDir = !settings.channels[chn].playDir; - break; - default: - effect1 = CMD_NONE; - break; - } - } - - //////////////////////////////////////////////////////////////// - // 0x04: Note effect - if((channelInfo & patNoteEff) != 0) - { - effect2 = file.ReadUint8(); - effectParam2 = file.ReadUint8(); - - switch(effect2) - { - case 1: // Note Finetune - effect2 = static_cast<ModCommand::COMMAND>(effectParam2 < 128 ? CMD_PORTAMENTOUP : CMD_PORTAMENTODOWN); - if(effectParam2 > 128) effectParam2 = 255 - effectParam2 + 1; - effectParam2 = 0xF0 | MIN(0x0F, effectParam2); // Well, this is not too accurate... - break; - case 2: // Note Delay (wtf is the difference to Sample Delay?) - effectParam2 = DMFdelay2MPT(effectParam2, settings.internalTicks); - if(effectParam2) - { - effect2 = CMD_S3MCMDEX; - effectParam2 = 0xD0 | (effectParam2); - } else - { - effect2 = CMD_NONE; - } - useMem2 = true; - break; - case 3: // Arpeggio - effect2 = CMD_ARPEGGIO; - useMem2 = true; - break; - case 4: // Portamento Up - case 5: // Portamento Down - effectParam2 = DMFporta2MPT(effectParam2, settings.internalTicks, true); - effect2 = static_cast<ModCommand::COMMAND>(effect2 == 4 ? CMD_PORTAMENTOUP : CMD_PORTAMENTODOWN); - useMem2 = true; - break; - case 6: // Portamento to Note - if(m->note == NOTE_NONE) - { - m->note = settings.channels[chn].noteBuffer; - } - effectParam2 = DMFporta2MPT(effectParam2, settings.internalTicks, false); - effect2 = CMD_TONEPORTAMENTO; - useMem2 = true; - break; - case 7: // Scratch to Note (neat! but we don't have such an effect...) - m->note = CLAMP(effectParam2 + 25, NOTE_MIN, NOTE_MAX); - effect2 = CMD_TONEPORTAMENTO; - effectParam2 = 0xFF; - useMem2 = true; - break; - case 8: // Vibrato Sine - case 9: // Vibrato Triangle (ramp down should be close enough) - case 10: // Vibrato Square - // Put vibrato type on previous row - if(row > 0 && effect2 != settings.channels[chn].vibratoType) - { - if(sndFile.Patterns[pat].WriteEffect(EffectWriter(CMD_S3MCMDEX, (0x30 | (effect2 - 8))).Row(row - 1).Channel(chn).Retry(EffectWriter::rmTryPreviousRow))) - { - settings.channels[chn].vibratoType = effect2; - } - } - effect2 = CMD_VIBRATO; - effectParam2 = DMFvibrato2MPT(effectParam2, settings.internalTicks); - useMem2 = true; - break; - case 11: // Note Tremolo - effectParam2 = DMFtremor2MPT(effectParam2, settings.internalTicks); - effect2 = CMD_TREMOR; - useMem2 = true; - break; - case 12: // Note Cut - effectParam2 = DMFdelay2MPT(effectParam2, settings.internalTicks); - if(effectParam2) - { - effect2 = CMD_S3MCMDEX; - effectParam2 = 0xC0 | (effectParam2); - } else - { - effect2 = CMD_NONE; - m->note = NOTE_NOTECUT; - } - useMem2 = true; - break; - default: - effect2 = CMD_NONE; - break; - } - } - - //////////////////////////////////////////////////////////////// - // 0x02: Volume effect - if((channelInfo & patVolEff) != 0) - { - effect3 = file.ReadUint8(); - effectParam3 = file.ReadUint8(); - - switch(effect3) - { - case 1: // Volume Slide Up - case 2: // Volume Slide Down - effectParam3 = DMFslide2MPT(effectParam3, settings.internalTicks, (effect3 == 1)); - effect3 = CMD_VOLUMESLIDE; - useMem3 = true; - break; - case 3: // Volume Tremolo (actually this is Tremor) - effectParam3 = DMFtremor2MPT(effectParam3, settings.internalTicks); - effect3 = CMD_TREMOR; - useMem3 = true; - break; - case 4: // Tremolo Sine - case 5: // Tremolo Triangle (ramp down should be close enough) - case 6: // Tremolo Square - // Put tremolo type on previous row - if(row > 0 && effect3 != settings.channels[chn].tremoloType) - { - if(sndFile.Patterns[pat].WriteEffect(EffectWriter(CMD_S3MCMDEX, (0x40 | (effect3 - 4))).Row(row - 1).Channel(chn).Retry(EffectWriter::rmTryPreviousRow))) - { - settings.channels[chn].tremoloType = effect3; - } - } - effect3 = CMD_TREMOLO; - effectParam3 = DMFvibrato2MPT(effectParam3, settings.internalTicks); - useMem3 = true; - break; - case 7: // Set Balance - effect3 = CMD_PANNING8; - break; - case 8: // Slide Balance Left - case 9: // Slide Balance Right - effectParam3 = DMFslide2MPT(effectParam3, settings.internalTicks, (effect3 == 8)); - effect3 = CMD_PANNINGSLIDE; - useMem3 = true; - break; - case 10: // Balance Vibrato Left/Right (always sine modulated) - effect3 = CMD_PANBRELLO; - effectParam3 = DMFvibrato2MPT(effectParam3, settings.internalTicks); - useMem3 = true; - break; - default: - effect3 = CMD_NONE; - break; - } - } - - // Let's see if we can help the effect swapper by reducing some effect parameters to "continue" parameters. - if(useMem2) - { - ApplyEffectMemory(m, row, sndFile.GetNumChannels(), effect2, effectParam2); - } - if(useMem3) - { - ApplyEffectMemory(m, row, sndFile.GetNumChannels(), effect3, effectParam3); - } - - // I guess this is close enough to "not retriggering the note" - ... [truncated message content] |