123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711 |
- /*
- * Load_dbm.cpp
- * ------------
- * Purpose: DigiBooster Pro module Loader (DBM)
- * Notes : (currently none)
- * Authors: OpenMPT Devs
- * The OpenMPT source code is released under the BSD license. Read LICENSE for more details.
- */
- #include "stdafx.h"
- #include "Loaders.h"
- #include "../common/mptStringBuffer.h"
- #ifndef NO_PLUGINS
- #include "plugins/DigiBoosterEcho.h"
- #endif // NO_PLUGINS
- #ifdef LIBOPENMPT_BUILD
- #define MPT_DBM_USE_REAL_SUBSONGS
- #endif
- OPENMPT_NAMESPACE_BEGIN
- struct DBMFileHeader
- {
- char dbm0[4];
- uint8 trkVerHi;
- uint8 trkVerLo;
- char reserved[2];
- };
- MPT_BINARY_STRUCT(DBMFileHeader, 8)
- // IFF-style Chunk
- struct DBMChunk
- {
- // 32-Bit chunk identifiers
- enum ChunkIdentifiers
- {
- idNAME = MagicBE("NAME"),
- idINFO = MagicBE("INFO"),
- idSONG = MagicBE("SONG"),
- idINST = MagicBE("INST"),
- idVENV = MagicBE("VENV"),
- idPENV = MagicBE("PENV"),
- idPATT = MagicBE("PATT"),
- idPNAM = MagicBE("PNAM"),
- idSMPL = MagicBE("SMPL"),
- idDSPE = MagicBE("DSPE"),
- idMPEG = MagicBE("MPEG"),
- };
- uint32be id;
- uint32be length;
- size_t GetLength() const
- {
- return length;
- }
- ChunkIdentifiers GetID() const
- {
- return static_cast<ChunkIdentifiers>(id.get());
- }
- };
- MPT_BINARY_STRUCT(DBMChunk, 8)
- struct DBMInfoChunk
- {
- uint16be instruments;
- uint16be samples;
- uint16be songs;
- uint16be patterns;
- uint16be channels;
- };
- MPT_BINARY_STRUCT(DBMInfoChunk, 10)
- // Instrument header
- struct DBMInstrument
- {
- enum DBMInstrFlags
- {
- smpLoop = 0x01,
- smpPingPongLoop = 0x02,
- };
- char name[30];
- uint16be sample; // Sample reference
- uint16be volume; // 0...64
- uint32be sampleRate;
- uint32be loopStart;
- uint32be loopLength;
- int16be panning; // -128...128
- uint16be flags; // See DBMInstrFlags
- };
- MPT_BINARY_STRUCT(DBMInstrument, 50)
- // Volume or panning envelope
- struct DBMEnvelope
- {
- enum DBMEnvelopeFlags
- {
- envEnabled = 0x01,
- envSustain = 0x02,
- envLoop = 0x04,
- };
- uint16be instrument;
- uint8be flags; // See DBMEnvelopeFlags
- uint8be numSegments; // Number of envelope points - 1
- uint8be sustain1;
- uint8be loopBegin;
- uint8be loopEnd;
- uint8be sustain2; // Second sustain point
- uint16be data[2 * 32];
- };
- MPT_BINARY_STRUCT(DBMEnvelope, 136)
- // Note: Unlike in MOD, 1Fx, 2Fx, 5Fx / 5xF, 6Fx / 6xF and AFx / AxF are fine slides.
- static constexpr ModCommand::COMMAND dbmEffects[] =
- {
- 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_NONE, CMD_NONE,
- CMD_KEYOFF, CMD_SETENVPOSITION, CMD_NONE, CMD_NONE,
- CMD_NONE, CMD_PANNINGSLIDE, CMD_NONE, CMD_NONE,
- CMD_NONE, CMD_NONE, CMD_NONE,
- #ifndef NO_PLUGINS
- CMD_DBMECHO, // Toggle DSP
- CMD_MIDI, // Wxx Echo Delay
- CMD_MIDI, // Xxx Echo Feedback
- CMD_MIDI, // Yxx Echo Mix
- CMD_MIDI, // Zxx Echo Cross
- #endif // NO_PLUGINS
- };
- static void ConvertDBMEffect(uint8 &command, uint8 ¶m)
- {
- uint8 oldCmd = command;
- if(command < std::size(dbmEffects))
- command = dbmEffects[command];
- else
- command = CMD_NONE;
- switch(command)
- {
- case CMD_ARPEGGIO:
- if(param == 0)
- command = CMD_NONE;
- break;
- case CMD_PATTERNBREAK:
- param = ((param >> 4) * 10) + (param & 0x0F);
- break;
- #ifdef MODPLUG_TRACKER
- case CMD_VIBRATO:
- if(param & 0x0F)
- {
- // DBM vibrato is half as deep as most other trackers. Convert it to IT fine vibrato range if possible.
- uint8 depth = (param & 0x0F) * 2u;
- param &= 0xF0;
- if(depth < 16)
- command = CMD_FINEVIBRATO;
- else
- depth = (depth + 2u) / 4u;
- param |= depth;
- }
- break;
- #endif
- // Volume slide nibble priority - first nibble (slide up) has precedence.
- case CMD_VOLUMESLIDE:
- case CMD_TONEPORTAVOL:
- case CMD_VIBRATOVOL:
- if((param & 0xF0) != 0x00 && (param & 0xF0) != 0xF0 && (param & 0x0F) != 0x0F)
- param &= 0xF0;
- break;
- case CMD_GLOBALVOLUME:
- if(param <= 64)
- param *= 2;
- else
- param = 128;
- break;
- case CMD_MODCMDEX:
- switch(param & 0xF0)
- {
- case 0x30: // Play backwards
- command = CMD_S3MCMDEX;
- param = 0x9F;
- break;
- case 0x40: // Turn off sound in channel (volume / portamento commands after this can't pick up the note anymore)
- command = CMD_S3MCMDEX;
- param = 0xC0;
- break;
- case 0x50: // Turn on/off channel
- // TODO: Apparently this should also kill the playing note.
- if((param & 0x0F) <= 0x01)
- {
- command = CMD_CHANNELVOLUME;
- param = (param == 0x50) ? 0x00 : 0x40;
- }
- break;
- case 0x70: // Coarse offset
- command = CMD_S3MCMDEX;
- param = 0xA0 | (param & 0x0F);
- 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 off at tick 0
- }
- break;
- case CMD_MIDI:
- // Encode echo parameters into fixed MIDI macros
- param = 128 + (oldCmd - 32) * 32 + param / 8;
- }
- }
- // Read a chunk of volume or panning envelopes
- static void ReadDBMEnvelopeChunk(FileReader chunk, EnvelopeType envType, CSoundFile &sndFile, bool scaleEnv)
- {
- uint16 numEnvs = chunk.ReadUint16BE();
- for(uint16 env = 0; env < numEnvs; env++)
- {
- DBMEnvelope dbmEnv;
- chunk.ReadStruct(dbmEnv);
- uint16 dbmIns = dbmEnv.instrument;
- if(dbmIns > 0 && dbmIns <= sndFile.GetNumInstruments() && (sndFile.Instruments[dbmIns] != nullptr))
- {
- ModInstrument *mptIns = sndFile.Instruments[dbmIns];
- InstrumentEnvelope &mptEnv = mptIns->GetEnvelope(envType);
- if(dbmEnv.numSegments)
- {
- if(dbmEnv.flags & DBMEnvelope::envEnabled) mptEnv.dwFlags.set(ENV_ENABLED);
- if(dbmEnv.flags & DBMEnvelope::envSustain) mptEnv.dwFlags.set(ENV_SUSTAIN);
- if(dbmEnv.flags & DBMEnvelope::envLoop) mptEnv.dwFlags.set(ENV_LOOP);
- }
- uint8 numPoints = std::min(dbmEnv.numSegments.get(), uint8(31)) + 1;
- mptEnv.resize(numPoints);
- mptEnv.nLoopStart = dbmEnv.loopBegin;
- mptEnv.nLoopEnd = dbmEnv.loopEnd;
- mptEnv.nSustainStart = mptEnv.nSustainEnd = dbmEnv.sustain1;
- for(uint8 i = 0; i < numPoints; i++)
- {
- mptEnv[i].tick = dbmEnv.data[i * 2];
- uint16 val = dbmEnv.data[i * 2 + 1];
- if(scaleEnv)
- {
- // Panning envelopes are -128...128 in DigiBooster Pro 3.x
- val = (val + 128) / 4;
- }
- LimitMax(val, uint16(64));
- mptEnv[i].value = static_cast<uint8>(val);
- }
- }
- }
- }
- static bool ValidateHeader(const DBMFileHeader &fileHeader)
- {
- if(std::memcmp(fileHeader.dbm0, "DBM0", 4)
- || fileHeader.trkVerHi > 3)
- {
- return false;
- }
- return true;
- }
- CSoundFile::ProbeResult CSoundFile::ProbeFileHeaderDBM(MemoryFileReader file, const uint64 *pfilesize)
- {
- DBMFileHeader fileHeader;
- if(!file.ReadStruct(fileHeader))
- {
- return ProbeWantMoreData;
- }
- if(!ValidateHeader(fileHeader))
- {
- return ProbeFailure;
- }
- MPT_UNREFERENCED_PARAMETER(pfilesize);
- return ProbeSuccess;
- }
- bool CSoundFile::ReadDBM(FileReader &file, ModLoadingFlags loadFlags)
- {
- file.Rewind();
- DBMFileHeader fileHeader;
- if(!file.ReadStruct(fileHeader))
- {
- return false;
- }
- if(!ValidateHeader(fileHeader))
- {
- return false;
- }
- if(loadFlags == onlyVerifyHeader)
- {
- return true;
- }
- ChunkReader chunkFile(file);
- auto chunks = chunkFile.ReadChunks<DBMChunk>(1);
- // Globals
- DBMInfoChunk infoData;
- if(!chunks.GetChunk(DBMChunk::idINFO).ReadStruct(infoData))
- {
- return false;
- }
- InitializeGlobals(MOD_TYPE_DBM);
- InitializeChannels();
- m_SongFlags = SONG_ITCOMPATGXX | SONG_ITOLDEFFECTS;
- m_nChannels = Clamp<uint16, uint16>(infoData.channels, 1, MAX_BASECHANNELS); // note: MAX_BASECHANNELS is currently 127, but DBPro 2 supports up to 128 channels, DBPro 3 apparently up to 254.
- m_nInstruments = std::min(static_cast<INSTRUMENTINDEX>(infoData.instruments), static_cast<INSTRUMENTINDEX>(MAX_INSTRUMENTS - 1));
- m_nSamples = std::min(static_cast<SAMPLEINDEX>(infoData.samples), static_cast<SAMPLEINDEX>(MAX_SAMPLES - 1));
- m_playBehaviour.set(kSlidesAtSpeed1);
- m_playBehaviour.reset(kITVibratoTremoloPanbrello);
- m_playBehaviour.reset(kITArpeggio);
- m_modFormat.formatName = U_("DigiBooster Pro");
- m_modFormat.type = U_("dbm");
- m_modFormat.madeWithTracker = MPT_UFORMAT("DigiBooster Pro {}.{}")(mpt::ufmt::hex(fileHeader.trkVerHi), mpt::ufmt::hex(fileHeader.trkVerLo));
- m_modFormat.charset = mpt::Charset::Amiga_no_C1;
- // Name chunk
- FileReader nameChunk = chunks.GetChunk(DBMChunk::idNAME);
- nameChunk.ReadString<mpt::String::maybeNullTerminated>(m_songName, nameChunk.GetLength());
- // Song chunk
- FileReader songChunk = chunks.GetChunk(DBMChunk::idSONG);
- Order().clear();
- uint16 numSongs = infoData.songs;
- for(uint16 i = 0; i < numSongs && songChunk.CanRead(46); i++)
- {
- char name[44];
- songChunk.ReadString<mpt::String::maybeNullTerminated>(name, 44);
- if(m_songName.empty())
- {
- m_songName = name;
- }
- uint16 numOrders = songChunk.ReadUint16BE();
- #ifdef MPT_DBM_USE_REAL_SUBSONGS
- if(!Order().empty())
- {
- // Add a new sequence for this song
- if(Order.AddSequence() == SEQUENCEINDEX_INVALID)
- break;
- }
- Order().SetName(mpt::ToUnicode(mpt::Charset::Amiga_no_C1, name));
- ReadOrderFromFile<uint16be>(Order(), songChunk, numOrders);
- #else
- const ORDERINDEX startIndex = Order().GetLength();
- if(startIndex < MAX_ORDERS && songChunk.CanRead(numOrders * 2u))
- {
- LimitMax(numOrders, static_cast<ORDERINDEX>(MAX_ORDERS - startIndex - 1));
- Order().resize(startIndex + numOrders + 1);
- for(uint16 ord = 0; ord < numOrders; ord++)
- {
- Order()[startIndex + ord] = static_cast<PATTERNINDEX>(songChunk.ReadUint16BE());
- }
- }
- #endif // MPT_DBM_USE_REAL_SUBSONGS
- }
- #ifdef MPT_DBM_USE_REAL_SUBSONGS
- Order.SetSequence(0);
- #endif // MPT_DBM_USE_REAL_SUBSONGS
- // Read instruments
- if(FileReader instChunk = chunks.GetChunk(DBMChunk::idINST))
- {
- for(INSTRUMENTINDEX i = 1; i <= GetNumInstruments(); i++)
- {
- DBMInstrument instrHeader;
- instChunk.ReadStruct(instrHeader);
- ModInstrument *mptIns = AllocateInstrument(i, instrHeader.sample);
- if(mptIns == nullptr || instrHeader.sample >= MAX_SAMPLES)
- {
- continue;
- }
- mptIns->name = mpt::String::ReadBuf(mpt::String::maybeNullTerminated, instrHeader.name);
- m_szNames[instrHeader.sample] = mpt::String::ReadBuf(mpt::String::maybeNullTerminated, instrHeader.name);
- mptIns->nFadeOut = 0;
- mptIns->nPan = static_cast<uint16>(instrHeader.panning + 128);
- LimitMax(mptIns->nPan, uint32(256));
- mptIns->dwFlags.set(INS_SETPANNING);
- // Sample Info
- ModSample &mptSmp = Samples[instrHeader.sample];
- mptSmp.Initialize();
- mptSmp.nVolume = std::min(static_cast<uint16>(instrHeader.volume), uint16(64)) * 4u;
- mptSmp.nC5Speed = Util::muldivr(instrHeader.sampleRate, 8303, 8363);
- if(instrHeader.loopLength && (instrHeader.flags & (DBMInstrument::smpLoop | DBMInstrument::smpPingPongLoop)))
- {
- mptSmp.nLoopStart = instrHeader.loopStart;
- mptSmp.nLoopEnd = mptSmp.nLoopStart + instrHeader.loopLength;
- mptSmp.uFlags.set(CHN_LOOP);
- if(instrHeader.flags & DBMInstrument::smpPingPongLoop)
- mptSmp.uFlags.set(CHN_PINGPONGLOOP);
- }
- }
- // Read envelopes
- ReadDBMEnvelopeChunk(chunks.GetChunk(DBMChunk::idVENV), ENV_VOLUME, *this, false);
- ReadDBMEnvelopeChunk(chunks.GetChunk(DBMChunk::idPENV), ENV_PANNING, *this, fileHeader.trkVerHi > 2);
- // Note-Off cuts samples if there's no envelope.
- for(INSTRUMENTINDEX i = 1; i <= GetNumInstruments(); i++)
- {
- if(Instruments[i] != nullptr && !Instruments[i]->VolEnv.dwFlags[ENV_ENABLED])
- {
- Instruments[i]->nFadeOut = 32767;
- }
- }
- }
- // Patterns
- FileReader patternChunk = chunks.GetChunk(DBMChunk::idPATT);
- #ifndef NO_PLUGINS
- bool hasEchoEnable = false, hasEchoParams = false;
- #endif // NO_PLUGINS
- if(patternChunk.IsValid() && (loadFlags & loadPatternData))
- {
- FileReader patternNameChunk = chunks.GetChunk(DBMChunk::idPNAM);
- patternNameChunk.Skip(1); // Encoding, should be UTF-8 or ASCII
- Patterns.ResizeArray(infoData.patterns);
- std::vector<std::pair<EffectCommand, ModCommand::PARAM>> lostGlobalCommands;
- for(PATTERNINDEX pat = 0; pat < infoData.patterns; pat++)
- {
- uint16 numRows = patternChunk.ReadUint16BE();
- uint32 packedSize = patternChunk.ReadUint32BE();
- FileReader chunk = patternChunk.ReadChunk(packedSize);
- if(!Patterns.Insert(pat, numRows))
- continue;
- std::string patName;
- patternNameChunk.ReadSizedString<uint8be, mpt::String::maybeNullTerminated>(patName);
- Patterns[pat].SetName(patName);
- PatternRow patRow = Patterns[pat].GetRow(0);
- ROWINDEX row = 0;
- lostGlobalCommands.clear();
- while(chunk.CanRead(1))
- {
- const uint8 ch = chunk.ReadUint8();
- if(!ch)
- {
- // End Of Row
- for(const auto &cmd : lostGlobalCommands)
- {
- Patterns[pat].WriteEffect(EffectWriter(cmd.first, cmd.second).Row(row));
- }
- lostGlobalCommands.clear();
- if(++row >= numRows)
- break;
- patRow = Patterns[pat].GetRow(row);
- continue;
- }
- ModCommand dummy = ModCommand::Empty();
- ModCommand &m = ch <= GetNumChannels() ? patRow[ch - 1] : dummy;
- const uint8 b = chunk.ReadUint8();
- if(b & 0x01)
- {
- uint8 note = chunk.ReadUint8();
- if(note == 0x1F)
- m.note = NOTE_KEYOFF;
- else if(note > 0 && note < 0xFE)
- m.note = ((note >> 4) * 12) + (note & 0x0F) + 13;
- }
- if(b & 0x02)
- {
- m.instr = chunk.ReadUint8();
- }
- if(b & 0x3C)
- {
- uint8 cmd1 = 0, cmd2 = 0, param1 = 0, param2 = 0;
- if(b & 0x04) cmd2 = chunk.ReadUint8();
- if(b & 0x08) param2 = chunk.ReadUint8();
- if(b & 0x10) cmd1 = chunk.ReadUint8();
- if(b & 0x20) param1 = chunk.ReadUint8();
- ConvertDBMEffect(cmd1, param1);
- ConvertDBMEffect(cmd2, param2);
- if (cmd2 == CMD_VOLUME || (cmd2 == CMD_NONE && cmd1 != CMD_VOLUME))
- {
- std::swap(cmd1, cmd2);
- std::swap(param1, param2);
- }
- const auto lostCommand = ModCommand::TwoRegularCommandsToMPT(cmd1, param1, cmd2, param2);
- if(ModCommand::IsGlobalCommand(lostCommand.first, lostCommand.second))
- lostGlobalCommands.insert(lostGlobalCommands.begin(), lostCommand); // Insert at front so that the last command of same type "wins"
- m.volcmd = cmd1;
- m.vol = param1;
- m.command = cmd2;
- m.param = param2;
- #ifdef MODPLUG_TRACKER
- m.ExtendedMODtoS3MEffect();
- #endif // MODPLUG_TRACKER
- #ifndef NO_PLUGINS
- if(m.command == CMD_DBMECHO)
- hasEchoEnable = true;
- else if(m.command == CMD_MIDI)
- hasEchoParams = true;
- #endif // NO_PLUGINS
- }
- }
- }
- }
- #ifndef NO_PLUGINS
- // Echo DSP
- if(loadFlags & loadPluginData)
- {
- if(hasEchoEnable)
- {
- // If there are any Vxx effects to dynamically enable / disable echo, use the CHN_NOFX flag.
- for(CHANNELINDEX i = 0; i < m_nChannels; i++)
- {
- ChnSettings[i].nMixPlugin = 1;
- ChnSettings[i].dwFlags.set(CHN_NOFX);
- }
- }
- bool anyEnabled = hasEchoEnable;
- // DBP 3 Documentation says that the defaults are 64/128/128/255, but they appear to be 80/150/80/255 in DBP 2.21
- uint8 settings[8] = { 0, 80, 0, 150, 0, 80, 0, 255 };
- if(FileReader dspChunk = chunks.GetChunk(DBMChunk::idDSPE))
- {
- uint16 maskLen = dspChunk.ReadUint16BE();
- for(uint16 i = 0; i < maskLen; i++)
- {
- bool enabled = (dspChunk.ReadUint8() == 0);
- if(i < m_nChannels)
- {
- if(hasEchoEnable)
- {
- // If there are any Vxx effects to dynamically enable / disable echo, use the CHN_NOFX flag.
- ChnSettings[i].dwFlags.set(CHN_NOFX, !enabled);
- } else if(enabled)
- {
- ChnSettings[i].nMixPlugin = 1;
- anyEnabled = true;
- }
- }
- }
- dspChunk.ReadArray(settings);
- }
- if(anyEnabled)
- {
- // Note: DigiBooster Pro 3 has a more versatile per-channel echo effect.
- // In this case, we'd have to create one plugin per channel.
- SNDMIXPLUGIN &plugin = m_MixPlugins[0];
- plugin.Destroy();
- memcpy(&plugin.Info.dwPluginId1, "DBM0", 4);
- memcpy(&plugin.Info.dwPluginId2, "Echo", 4);
- plugin.Info.routingFlags = SNDMIXPLUGININFO::irAutoSuspend;
- plugin.Info.mixMode = 0;
- plugin.Info.gain = 10;
- plugin.Info.reserved = 0;
- plugin.Info.dwOutputRouting = 0;
- std::fill(plugin.Info.dwReserved, plugin.Info.dwReserved + std::size(plugin.Info.dwReserved), 0);
- plugin.Info.szName = "Echo";
- plugin.Info.szLibraryName = "DigiBooster Pro Echo";
- plugin.pluginData.resize(sizeof(DigiBoosterEcho::PluginChunk));
- DigiBoosterEcho::PluginChunk chunk = DigiBoosterEcho::PluginChunk::Create(settings[1], settings[3], settings[5], settings[7]);
- new (plugin.pluginData.data()) DigiBoosterEcho::PluginChunk(chunk);
- }
- }
- // Encode echo parameters into fixed MIDI macros
- if(hasEchoParams)
- {
- for(uint32 i = 0; i < 32; i++)
- {
- uint32 param = (i * 127u) / 32u;
- m_MidiCfg.Zxx[i ] = MPT_AFORMAT("F0F080{}")(mpt::afmt::HEX0<2>(param));
- m_MidiCfg.Zxx[i + 32] = MPT_AFORMAT("F0F081{}")(mpt::afmt::HEX0<2>(param));
- m_MidiCfg.Zxx[i + 64] = MPT_AFORMAT("F0F082{}")(mpt::afmt::HEX0<2>(param));
- m_MidiCfg.Zxx[i + 96] = MPT_AFORMAT("F0F083{}")(mpt::afmt::HEX0<2>(param));
- }
- }
- #endif // NO_PLUGINS
- // Samples
- FileReader sampleChunk = chunks.GetChunk(DBMChunk::idSMPL);
- if(sampleChunk.IsValid() && (loadFlags & loadSampleData))
- {
- for(SAMPLEINDEX smp = 1; smp <= GetNumSamples(); smp++)
- {
- uint32 sampleFlags = sampleChunk.ReadUint32BE();
- uint32 sampleLength = sampleChunk.ReadUint32BE();
- if(sampleFlags & 7)
- {
- ModSample &sample = Samples[smp];
- sample.nLength = sampleLength;
- SampleIO(
- (sampleFlags & 4) ? SampleIO::_32bit : ((sampleFlags & 2) ? SampleIO::_16bit : SampleIO::_8bit),
- SampleIO::mono,
- SampleIO::bigEndian,
- SampleIO::signedPCM)
- .ReadSample(sample, sampleChunk);
- }
- }
- }
- #if defined(MPT_ENABLE_MP3_SAMPLES) && 0
- // Compressed samples - this does not quite work yet...
- FileReader mpegChunk = chunks.GetChunk(DBMChunk::idMPEG);
- if(mpegChunk.IsValid() && (loadFlags & loadSampleData))
- {
- for(SAMPLEINDEX smp = 1; smp <= GetNumSamples(); smp++)
- {
- Samples[smp].nLength = mpegChunk.ReadUint32BE();
- }
- mpegChunk.Skip(2); // 0x00 0x40
- // Read whole MPEG stream into one sample and then split it up.
- FileReader chunk = mpegChunk.GetChunk(mpegChunk.BytesLeft());
- if(ReadMP3Sample(0, chunk, true))
- {
- ModSample &srcSample = Samples[0];
- const std::byte *smpData = srcSample.sampleb();
- SmpLength predelay = Util::muldiv_unsigned(20116, srcSample.nC5Speed, 100000);
- LimitMax(predelay, srcSample.nLength);
- smpData += predelay * srcSample.GetBytesPerSample();
- srcSample.nLength -= predelay;
- for(SAMPLEINDEX smp = 1; smp <= GetNumSamples(); smp++)
- {
- ModSample &sample = Samples[smp];
- sample.uFlags.set(srcSample.uFlags);
- LimitMax(sample.nLength, srcSample.nLength);
- if(sample.nLength)
- {
- sample.AllocateSample();
- memcpy(sample.sampleb(), smpData, sample.GetSampleSizeInBytes());
- smpData += sample.GetSampleSizeInBytes();
- srcSample.nLength -= sample.nLength;
- SmpLength gap = Util::muldiv_unsigned(454, srcSample.nC5Speed, 10000);
- LimitMax(gap, srcSample.nLength);
- smpData += gap * srcSample.GetBytesPerSample();
- srcSample.nLength -= gap;
- }
- }
- srcSample.FreeSample();
- }
- }
- #endif // MPT_ENABLE_MP3_SAMPLES
-
- return true;
- }
- OPENMPT_NAMESPACE_END
|