1
0

Load_dbm.cpp 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711
  1. /*
  2. * Load_dbm.cpp
  3. * ------------
  4. * Purpose: DigiBooster Pro module Loader (DBM)
  5. * Notes : (currently none)
  6. * Authors: OpenMPT Devs
  7. * The OpenMPT source code is released under the BSD license. Read LICENSE for more details.
  8. */
  9. #include "stdafx.h"
  10. #include "Loaders.h"
  11. #include "../common/mptStringBuffer.h"
  12. #ifndef NO_PLUGINS
  13. #include "plugins/DigiBoosterEcho.h"
  14. #endif // NO_PLUGINS
  15. #ifdef LIBOPENMPT_BUILD
  16. #define MPT_DBM_USE_REAL_SUBSONGS
  17. #endif
  18. OPENMPT_NAMESPACE_BEGIN
  19. struct DBMFileHeader
  20. {
  21. char dbm0[4];
  22. uint8 trkVerHi;
  23. uint8 trkVerLo;
  24. char reserved[2];
  25. };
  26. MPT_BINARY_STRUCT(DBMFileHeader, 8)
  27. // IFF-style Chunk
  28. struct DBMChunk
  29. {
  30. // 32-Bit chunk identifiers
  31. enum ChunkIdentifiers
  32. {
  33. idNAME = MagicBE("NAME"),
  34. idINFO = MagicBE("INFO"),
  35. idSONG = MagicBE("SONG"),
  36. idINST = MagicBE("INST"),
  37. idVENV = MagicBE("VENV"),
  38. idPENV = MagicBE("PENV"),
  39. idPATT = MagicBE("PATT"),
  40. idPNAM = MagicBE("PNAM"),
  41. idSMPL = MagicBE("SMPL"),
  42. idDSPE = MagicBE("DSPE"),
  43. idMPEG = MagicBE("MPEG"),
  44. };
  45. uint32be id;
  46. uint32be length;
  47. size_t GetLength() const
  48. {
  49. return length;
  50. }
  51. ChunkIdentifiers GetID() const
  52. {
  53. return static_cast<ChunkIdentifiers>(id.get());
  54. }
  55. };
  56. MPT_BINARY_STRUCT(DBMChunk, 8)
  57. struct DBMInfoChunk
  58. {
  59. uint16be instruments;
  60. uint16be samples;
  61. uint16be songs;
  62. uint16be patterns;
  63. uint16be channels;
  64. };
  65. MPT_BINARY_STRUCT(DBMInfoChunk, 10)
  66. // Instrument header
  67. struct DBMInstrument
  68. {
  69. enum DBMInstrFlags
  70. {
  71. smpLoop = 0x01,
  72. smpPingPongLoop = 0x02,
  73. };
  74. char name[30];
  75. uint16be sample; // Sample reference
  76. uint16be volume; // 0...64
  77. uint32be sampleRate;
  78. uint32be loopStart;
  79. uint32be loopLength;
  80. int16be panning; // -128...128
  81. uint16be flags; // See DBMInstrFlags
  82. };
  83. MPT_BINARY_STRUCT(DBMInstrument, 50)
  84. // Volume or panning envelope
  85. struct DBMEnvelope
  86. {
  87. enum DBMEnvelopeFlags
  88. {
  89. envEnabled = 0x01,
  90. envSustain = 0x02,
  91. envLoop = 0x04,
  92. };
  93. uint16be instrument;
  94. uint8be flags; // See DBMEnvelopeFlags
  95. uint8be numSegments; // Number of envelope points - 1
  96. uint8be sustain1;
  97. uint8be loopBegin;
  98. uint8be loopEnd;
  99. uint8be sustain2; // Second sustain point
  100. uint16be data[2 * 32];
  101. };
  102. MPT_BINARY_STRUCT(DBMEnvelope, 136)
  103. // Note: Unlike in MOD, 1Fx, 2Fx, 5Fx / 5xF, 6Fx / 6xF and AFx / AxF are fine slides.
  104. static constexpr ModCommand::COMMAND dbmEffects[] =
  105. {
  106. CMD_ARPEGGIO, CMD_PORTAMENTOUP, CMD_PORTAMENTODOWN, CMD_TONEPORTAMENTO,
  107. CMD_VIBRATO, CMD_TONEPORTAVOL, CMD_VIBRATOVOL, CMD_TREMOLO,
  108. CMD_PANNING8, CMD_OFFSET, CMD_VOLUMESLIDE, CMD_POSITIONJUMP,
  109. CMD_VOLUME, CMD_PATTERNBREAK, CMD_MODCMDEX, CMD_TEMPO,
  110. CMD_GLOBALVOLUME, CMD_GLOBALVOLSLIDE, CMD_NONE, CMD_NONE,
  111. CMD_KEYOFF, CMD_SETENVPOSITION, CMD_NONE, CMD_NONE,
  112. CMD_NONE, CMD_PANNINGSLIDE, CMD_NONE, CMD_NONE,
  113. CMD_NONE, CMD_NONE, CMD_NONE,
  114. #ifndef NO_PLUGINS
  115. CMD_DBMECHO, // Toggle DSP
  116. CMD_MIDI, // Wxx Echo Delay
  117. CMD_MIDI, // Xxx Echo Feedback
  118. CMD_MIDI, // Yxx Echo Mix
  119. CMD_MIDI, // Zxx Echo Cross
  120. #endif // NO_PLUGINS
  121. };
  122. static void ConvertDBMEffect(uint8 &command, uint8 &param)
  123. {
  124. uint8 oldCmd = command;
  125. if(command < std::size(dbmEffects))
  126. command = dbmEffects[command];
  127. else
  128. command = CMD_NONE;
  129. switch(command)
  130. {
  131. case CMD_ARPEGGIO:
  132. if(param == 0)
  133. command = CMD_NONE;
  134. break;
  135. case CMD_PATTERNBREAK:
  136. param = ((param >> 4) * 10) + (param & 0x0F);
  137. break;
  138. #ifdef MODPLUG_TRACKER
  139. case CMD_VIBRATO:
  140. if(param & 0x0F)
  141. {
  142. // DBM vibrato is half as deep as most other trackers. Convert it to IT fine vibrato range if possible.
  143. uint8 depth = (param & 0x0F) * 2u;
  144. param &= 0xF0;
  145. if(depth < 16)
  146. command = CMD_FINEVIBRATO;
  147. else
  148. depth = (depth + 2u) / 4u;
  149. param |= depth;
  150. }
  151. break;
  152. #endif
  153. // Volume slide nibble priority - first nibble (slide up) has precedence.
  154. case CMD_VOLUMESLIDE:
  155. case CMD_TONEPORTAVOL:
  156. case CMD_VIBRATOVOL:
  157. if((param & 0xF0) != 0x00 && (param & 0xF0) != 0xF0 && (param & 0x0F) != 0x0F)
  158. param &= 0xF0;
  159. break;
  160. case CMD_GLOBALVOLUME:
  161. if(param <= 64)
  162. param *= 2;
  163. else
  164. param = 128;
  165. break;
  166. case CMD_MODCMDEX:
  167. switch(param & 0xF0)
  168. {
  169. case 0x30: // Play backwards
  170. command = CMD_S3MCMDEX;
  171. param = 0x9F;
  172. break;
  173. case 0x40: // Turn off sound in channel (volume / portamento commands after this can't pick up the note anymore)
  174. command = CMD_S3MCMDEX;
  175. param = 0xC0;
  176. break;
  177. case 0x50: // Turn on/off channel
  178. // TODO: Apparently this should also kill the playing note.
  179. if((param & 0x0F) <= 0x01)
  180. {
  181. command = CMD_CHANNELVOLUME;
  182. param = (param == 0x50) ? 0x00 : 0x40;
  183. }
  184. break;
  185. case 0x70: // Coarse offset
  186. command = CMD_S3MCMDEX;
  187. param = 0xA0 | (param & 0x0F);
  188. break;
  189. default:
  190. // Rest will be converted later from CMD_MODCMDEX to CMD_S3MCMDEX.
  191. break;
  192. }
  193. break;
  194. case CMD_TEMPO:
  195. if(param <= 0x1F) command = CMD_SPEED;
  196. break;
  197. case CMD_KEYOFF:
  198. if (param == 0)
  199. {
  200. // TODO key off at tick 0
  201. }
  202. break;
  203. case CMD_MIDI:
  204. // Encode echo parameters into fixed MIDI macros
  205. param = 128 + (oldCmd - 32) * 32 + param / 8;
  206. }
  207. }
  208. // Read a chunk of volume or panning envelopes
  209. static void ReadDBMEnvelopeChunk(FileReader chunk, EnvelopeType envType, CSoundFile &sndFile, bool scaleEnv)
  210. {
  211. uint16 numEnvs = chunk.ReadUint16BE();
  212. for(uint16 env = 0; env < numEnvs; env++)
  213. {
  214. DBMEnvelope dbmEnv;
  215. chunk.ReadStruct(dbmEnv);
  216. uint16 dbmIns = dbmEnv.instrument;
  217. if(dbmIns > 0 && dbmIns <= sndFile.GetNumInstruments() && (sndFile.Instruments[dbmIns] != nullptr))
  218. {
  219. ModInstrument *mptIns = sndFile.Instruments[dbmIns];
  220. InstrumentEnvelope &mptEnv = mptIns->GetEnvelope(envType);
  221. if(dbmEnv.numSegments)
  222. {
  223. if(dbmEnv.flags & DBMEnvelope::envEnabled) mptEnv.dwFlags.set(ENV_ENABLED);
  224. if(dbmEnv.flags & DBMEnvelope::envSustain) mptEnv.dwFlags.set(ENV_SUSTAIN);
  225. if(dbmEnv.flags & DBMEnvelope::envLoop) mptEnv.dwFlags.set(ENV_LOOP);
  226. }
  227. uint8 numPoints = std::min(dbmEnv.numSegments.get(), uint8(31)) + 1;
  228. mptEnv.resize(numPoints);
  229. mptEnv.nLoopStart = dbmEnv.loopBegin;
  230. mptEnv.nLoopEnd = dbmEnv.loopEnd;
  231. mptEnv.nSustainStart = mptEnv.nSustainEnd = dbmEnv.sustain1;
  232. for(uint8 i = 0; i < numPoints; i++)
  233. {
  234. mptEnv[i].tick = dbmEnv.data[i * 2];
  235. uint16 val = dbmEnv.data[i * 2 + 1];
  236. if(scaleEnv)
  237. {
  238. // Panning envelopes are -128...128 in DigiBooster Pro 3.x
  239. val = (val + 128) / 4;
  240. }
  241. LimitMax(val, uint16(64));
  242. mptEnv[i].value = static_cast<uint8>(val);
  243. }
  244. }
  245. }
  246. }
  247. static bool ValidateHeader(const DBMFileHeader &fileHeader)
  248. {
  249. if(std::memcmp(fileHeader.dbm0, "DBM0", 4)
  250. || fileHeader.trkVerHi > 3)
  251. {
  252. return false;
  253. }
  254. return true;
  255. }
  256. CSoundFile::ProbeResult CSoundFile::ProbeFileHeaderDBM(MemoryFileReader file, const uint64 *pfilesize)
  257. {
  258. DBMFileHeader fileHeader;
  259. if(!file.ReadStruct(fileHeader))
  260. {
  261. return ProbeWantMoreData;
  262. }
  263. if(!ValidateHeader(fileHeader))
  264. {
  265. return ProbeFailure;
  266. }
  267. MPT_UNREFERENCED_PARAMETER(pfilesize);
  268. return ProbeSuccess;
  269. }
  270. bool CSoundFile::ReadDBM(FileReader &file, ModLoadingFlags loadFlags)
  271. {
  272. file.Rewind();
  273. DBMFileHeader fileHeader;
  274. if(!file.ReadStruct(fileHeader))
  275. {
  276. return false;
  277. }
  278. if(!ValidateHeader(fileHeader))
  279. {
  280. return false;
  281. }
  282. if(loadFlags == onlyVerifyHeader)
  283. {
  284. return true;
  285. }
  286. ChunkReader chunkFile(file);
  287. auto chunks = chunkFile.ReadChunks<DBMChunk>(1);
  288. // Globals
  289. DBMInfoChunk infoData;
  290. if(!chunks.GetChunk(DBMChunk::idINFO).ReadStruct(infoData))
  291. {
  292. return false;
  293. }
  294. InitializeGlobals(MOD_TYPE_DBM);
  295. InitializeChannels();
  296. m_SongFlags = SONG_ITCOMPATGXX | SONG_ITOLDEFFECTS;
  297. 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.
  298. m_nInstruments = std::min(static_cast<INSTRUMENTINDEX>(infoData.instruments), static_cast<INSTRUMENTINDEX>(MAX_INSTRUMENTS - 1));
  299. m_nSamples = std::min(static_cast<SAMPLEINDEX>(infoData.samples), static_cast<SAMPLEINDEX>(MAX_SAMPLES - 1));
  300. m_playBehaviour.set(kSlidesAtSpeed1);
  301. m_playBehaviour.reset(kITVibratoTremoloPanbrello);
  302. m_playBehaviour.reset(kITArpeggio);
  303. m_modFormat.formatName = U_("DigiBooster Pro");
  304. m_modFormat.type = U_("dbm");
  305. m_modFormat.madeWithTracker = MPT_UFORMAT("DigiBooster Pro {}.{}")(mpt::ufmt::hex(fileHeader.trkVerHi), mpt::ufmt::hex(fileHeader.trkVerLo));
  306. m_modFormat.charset = mpt::Charset::Amiga_no_C1;
  307. // Name chunk
  308. FileReader nameChunk = chunks.GetChunk(DBMChunk::idNAME);
  309. nameChunk.ReadString<mpt::String::maybeNullTerminated>(m_songName, nameChunk.GetLength());
  310. // Song chunk
  311. FileReader songChunk = chunks.GetChunk(DBMChunk::idSONG);
  312. Order().clear();
  313. uint16 numSongs = infoData.songs;
  314. for(uint16 i = 0; i < numSongs && songChunk.CanRead(46); i++)
  315. {
  316. char name[44];
  317. songChunk.ReadString<mpt::String::maybeNullTerminated>(name, 44);
  318. if(m_songName.empty())
  319. {
  320. m_songName = name;
  321. }
  322. uint16 numOrders = songChunk.ReadUint16BE();
  323. #ifdef MPT_DBM_USE_REAL_SUBSONGS
  324. if(!Order().empty())
  325. {
  326. // Add a new sequence for this song
  327. if(Order.AddSequence() == SEQUENCEINDEX_INVALID)
  328. break;
  329. }
  330. Order().SetName(mpt::ToUnicode(mpt::Charset::Amiga_no_C1, name));
  331. ReadOrderFromFile<uint16be>(Order(), songChunk, numOrders);
  332. #else
  333. const ORDERINDEX startIndex = Order().GetLength();
  334. if(startIndex < MAX_ORDERS && songChunk.CanRead(numOrders * 2u))
  335. {
  336. LimitMax(numOrders, static_cast<ORDERINDEX>(MAX_ORDERS - startIndex - 1));
  337. Order().resize(startIndex + numOrders + 1);
  338. for(uint16 ord = 0; ord < numOrders; ord++)
  339. {
  340. Order()[startIndex + ord] = static_cast<PATTERNINDEX>(songChunk.ReadUint16BE());
  341. }
  342. }
  343. #endif // MPT_DBM_USE_REAL_SUBSONGS
  344. }
  345. #ifdef MPT_DBM_USE_REAL_SUBSONGS
  346. Order.SetSequence(0);
  347. #endif // MPT_DBM_USE_REAL_SUBSONGS
  348. // Read instruments
  349. if(FileReader instChunk = chunks.GetChunk(DBMChunk::idINST))
  350. {
  351. for(INSTRUMENTINDEX i = 1; i <= GetNumInstruments(); i++)
  352. {
  353. DBMInstrument instrHeader;
  354. instChunk.ReadStruct(instrHeader);
  355. ModInstrument *mptIns = AllocateInstrument(i, instrHeader.sample);
  356. if(mptIns == nullptr || instrHeader.sample >= MAX_SAMPLES)
  357. {
  358. continue;
  359. }
  360. mptIns->name = mpt::String::ReadBuf(mpt::String::maybeNullTerminated, instrHeader.name);
  361. m_szNames[instrHeader.sample] = mpt::String::ReadBuf(mpt::String::maybeNullTerminated, instrHeader.name);
  362. mptIns->nFadeOut = 0;
  363. mptIns->nPan = static_cast<uint16>(instrHeader.panning + 128);
  364. LimitMax(mptIns->nPan, uint32(256));
  365. mptIns->dwFlags.set(INS_SETPANNING);
  366. // Sample Info
  367. ModSample &mptSmp = Samples[instrHeader.sample];
  368. mptSmp.Initialize();
  369. mptSmp.nVolume = std::min(static_cast<uint16>(instrHeader.volume), uint16(64)) * 4u;
  370. mptSmp.nC5Speed = Util::muldivr(instrHeader.sampleRate, 8303, 8363);
  371. if(instrHeader.loopLength && (instrHeader.flags & (DBMInstrument::smpLoop | DBMInstrument::smpPingPongLoop)))
  372. {
  373. mptSmp.nLoopStart = instrHeader.loopStart;
  374. mptSmp.nLoopEnd = mptSmp.nLoopStart + instrHeader.loopLength;
  375. mptSmp.uFlags.set(CHN_LOOP);
  376. if(instrHeader.flags & DBMInstrument::smpPingPongLoop)
  377. mptSmp.uFlags.set(CHN_PINGPONGLOOP);
  378. }
  379. }
  380. // Read envelopes
  381. ReadDBMEnvelopeChunk(chunks.GetChunk(DBMChunk::idVENV), ENV_VOLUME, *this, false);
  382. ReadDBMEnvelopeChunk(chunks.GetChunk(DBMChunk::idPENV), ENV_PANNING, *this, fileHeader.trkVerHi > 2);
  383. // Note-Off cuts samples if there's no envelope.
  384. for(INSTRUMENTINDEX i = 1; i <= GetNumInstruments(); i++)
  385. {
  386. if(Instruments[i] != nullptr && !Instruments[i]->VolEnv.dwFlags[ENV_ENABLED])
  387. {
  388. Instruments[i]->nFadeOut = 32767;
  389. }
  390. }
  391. }
  392. // Patterns
  393. FileReader patternChunk = chunks.GetChunk(DBMChunk::idPATT);
  394. #ifndef NO_PLUGINS
  395. bool hasEchoEnable = false, hasEchoParams = false;
  396. #endif // NO_PLUGINS
  397. if(patternChunk.IsValid() && (loadFlags & loadPatternData))
  398. {
  399. FileReader patternNameChunk = chunks.GetChunk(DBMChunk::idPNAM);
  400. patternNameChunk.Skip(1); // Encoding, should be UTF-8 or ASCII
  401. Patterns.ResizeArray(infoData.patterns);
  402. std::vector<std::pair<EffectCommand, ModCommand::PARAM>> lostGlobalCommands;
  403. for(PATTERNINDEX pat = 0; pat < infoData.patterns; pat++)
  404. {
  405. uint16 numRows = patternChunk.ReadUint16BE();
  406. uint32 packedSize = patternChunk.ReadUint32BE();
  407. FileReader chunk = patternChunk.ReadChunk(packedSize);
  408. if(!Patterns.Insert(pat, numRows))
  409. continue;
  410. std::string patName;
  411. patternNameChunk.ReadSizedString<uint8be, mpt::String::maybeNullTerminated>(patName);
  412. Patterns[pat].SetName(patName);
  413. PatternRow patRow = Patterns[pat].GetRow(0);
  414. ROWINDEX row = 0;
  415. lostGlobalCommands.clear();
  416. while(chunk.CanRead(1))
  417. {
  418. const uint8 ch = chunk.ReadUint8();
  419. if(!ch)
  420. {
  421. // End Of Row
  422. for(const auto &cmd : lostGlobalCommands)
  423. {
  424. Patterns[pat].WriteEffect(EffectWriter(cmd.first, cmd.second).Row(row));
  425. }
  426. lostGlobalCommands.clear();
  427. if(++row >= numRows)
  428. break;
  429. patRow = Patterns[pat].GetRow(row);
  430. continue;
  431. }
  432. ModCommand dummy = ModCommand::Empty();
  433. ModCommand &m = ch <= GetNumChannels() ? patRow[ch - 1] : dummy;
  434. const uint8 b = chunk.ReadUint8();
  435. if(b & 0x01)
  436. {
  437. uint8 note = chunk.ReadUint8();
  438. if(note == 0x1F)
  439. m.note = NOTE_KEYOFF;
  440. else if(note > 0 && note < 0xFE)
  441. m.note = ((note >> 4) * 12) + (note & 0x0F) + 13;
  442. }
  443. if(b & 0x02)
  444. {
  445. m.instr = chunk.ReadUint8();
  446. }
  447. if(b & 0x3C)
  448. {
  449. uint8 cmd1 = 0, cmd2 = 0, param1 = 0, param2 = 0;
  450. if(b & 0x04) cmd2 = chunk.ReadUint8();
  451. if(b & 0x08) param2 = chunk.ReadUint8();
  452. if(b & 0x10) cmd1 = chunk.ReadUint8();
  453. if(b & 0x20) param1 = chunk.ReadUint8();
  454. ConvertDBMEffect(cmd1, param1);
  455. ConvertDBMEffect(cmd2, param2);
  456. if (cmd2 == CMD_VOLUME || (cmd2 == CMD_NONE && cmd1 != CMD_VOLUME))
  457. {
  458. std::swap(cmd1, cmd2);
  459. std::swap(param1, param2);
  460. }
  461. const auto lostCommand = ModCommand::TwoRegularCommandsToMPT(cmd1, param1, cmd2, param2);
  462. if(ModCommand::IsGlobalCommand(lostCommand.first, lostCommand.second))
  463. lostGlobalCommands.insert(lostGlobalCommands.begin(), lostCommand); // Insert at front so that the last command of same type "wins"
  464. m.volcmd = cmd1;
  465. m.vol = param1;
  466. m.command = cmd2;
  467. m.param = param2;
  468. #ifdef MODPLUG_TRACKER
  469. m.ExtendedMODtoS3MEffect();
  470. #endif // MODPLUG_TRACKER
  471. #ifndef NO_PLUGINS
  472. if(m.command == CMD_DBMECHO)
  473. hasEchoEnable = true;
  474. else if(m.command == CMD_MIDI)
  475. hasEchoParams = true;
  476. #endif // NO_PLUGINS
  477. }
  478. }
  479. }
  480. }
  481. #ifndef NO_PLUGINS
  482. // Echo DSP
  483. if(loadFlags & loadPluginData)
  484. {
  485. if(hasEchoEnable)
  486. {
  487. // If there are any Vxx effects to dynamically enable / disable echo, use the CHN_NOFX flag.
  488. for(CHANNELINDEX i = 0; i < m_nChannels; i++)
  489. {
  490. ChnSettings[i].nMixPlugin = 1;
  491. ChnSettings[i].dwFlags.set(CHN_NOFX);
  492. }
  493. }
  494. bool anyEnabled = hasEchoEnable;
  495. // 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
  496. uint8 settings[8] = { 0, 80, 0, 150, 0, 80, 0, 255 };
  497. if(FileReader dspChunk = chunks.GetChunk(DBMChunk::idDSPE))
  498. {
  499. uint16 maskLen = dspChunk.ReadUint16BE();
  500. for(uint16 i = 0; i < maskLen; i++)
  501. {
  502. bool enabled = (dspChunk.ReadUint8() == 0);
  503. if(i < m_nChannels)
  504. {
  505. if(hasEchoEnable)
  506. {
  507. // If there are any Vxx effects to dynamically enable / disable echo, use the CHN_NOFX flag.
  508. ChnSettings[i].dwFlags.set(CHN_NOFX, !enabled);
  509. } else if(enabled)
  510. {
  511. ChnSettings[i].nMixPlugin = 1;
  512. anyEnabled = true;
  513. }
  514. }
  515. }
  516. dspChunk.ReadArray(settings);
  517. }
  518. if(anyEnabled)
  519. {
  520. // Note: DigiBooster Pro 3 has a more versatile per-channel echo effect.
  521. // In this case, we'd have to create one plugin per channel.
  522. SNDMIXPLUGIN &plugin = m_MixPlugins[0];
  523. plugin.Destroy();
  524. memcpy(&plugin.Info.dwPluginId1, "DBM0", 4);
  525. memcpy(&plugin.Info.dwPluginId2, "Echo", 4);
  526. plugin.Info.routingFlags = SNDMIXPLUGININFO::irAutoSuspend;
  527. plugin.Info.mixMode = 0;
  528. plugin.Info.gain = 10;
  529. plugin.Info.reserved = 0;
  530. plugin.Info.dwOutputRouting = 0;
  531. std::fill(plugin.Info.dwReserved, plugin.Info.dwReserved + std::size(plugin.Info.dwReserved), 0);
  532. plugin.Info.szName = "Echo";
  533. plugin.Info.szLibraryName = "DigiBooster Pro Echo";
  534. plugin.pluginData.resize(sizeof(DigiBoosterEcho::PluginChunk));
  535. DigiBoosterEcho::PluginChunk chunk = DigiBoosterEcho::PluginChunk::Create(settings[1], settings[3], settings[5], settings[7]);
  536. new (plugin.pluginData.data()) DigiBoosterEcho::PluginChunk(chunk);
  537. }
  538. }
  539. // Encode echo parameters into fixed MIDI macros
  540. if(hasEchoParams)
  541. {
  542. for(uint32 i = 0; i < 32; i++)
  543. {
  544. uint32 param = (i * 127u) / 32u;
  545. m_MidiCfg.Zxx[i ] = MPT_AFORMAT("F0F080{}")(mpt::afmt::HEX0<2>(param));
  546. m_MidiCfg.Zxx[i + 32] = MPT_AFORMAT("F0F081{}")(mpt::afmt::HEX0<2>(param));
  547. m_MidiCfg.Zxx[i + 64] = MPT_AFORMAT("F0F082{}")(mpt::afmt::HEX0<2>(param));
  548. m_MidiCfg.Zxx[i + 96] = MPT_AFORMAT("F0F083{}")(mpt::afmt::HEX0<2>(param));
  549. }
  550. }
  551. #endif // NO_PLUGINS
  552. // Samples
  553. FileReader sampleChunk = chunks.GetChunk(DBMChunk::idSMPL);
  554. if(sampleChunk.IsValid() && (loadFlags & loadSampleData))
  555. {
  556. for(SAMPLEINDEX smp = 1; smp <= GetNumSamples(); smp++)
  557. {
  558. uint32 sampleFlags = sampleChunk.ReadUint32BE();
  559. uint32 sampleLength = sampleChunk.ReadUint32BE();
  560. if(sampleFlags & 7)
  561. {
  562. ModSample &sample = Samples[smp];
  563. sample.nLength = sampleLength;
  564. SampleIO(
  565. (sampleFlags & 4) ? SampleIO::_32bit : ((sampleFlags & 2) ? SampleIO::_16bit : SampleIO::_8bit),
  566. SampleIO::mono,
  567. SampleIO::bigEndian,
  568. SampleIO::signedPCM)
  569. .ReadSample(sample, sampleChunk);
  570. }
  571. }
  572. }
  573. #if defined(MPT_ENABLE_MP3_SAMPLES) && 0
  574. // Compressed samples - this does not quite work yet...
  575. FileReader mpegChunk = chunks.GetChunk(DBMChunk::idMPEG);
  576. if(mpegChunk.IsValid() && (loadFlags & loadSampleData))
  577. {
  578. for(SAMPLEINDEX smp = 1; smp <= GetNumSamples(); smp++)
  579. {
  580. Samples[smp].nLength = mpegChunk.ReadUint32BE();
  581. }
  582. mpegChunk.Skip(2); // 0x00 0x40
  583. // Read whole MPEG stream into one sample and then split it up.
  584. FileReader chunk = mpegChunk.GetChunk(mpegChunk.BytesLeft());
  585. if(ReadMP3Sample(0, chunk, true))
  586. {
  587. ModSample &srcSample = Samples[0];
  588. const std::byte *smpData = srcSample.sampleb();
  589. SmpLength predelay = Util::muldiv_unsigned(20116, srcSample.nC5Speed, 100000);
  590. LimitMax(predelay, srcSample.nLength);
  591. smpData += predelay * srcSample.GetBytesPerSample();
  592. srcSample.nLength -= predelay;
  593. for(SAMPLEINDEX smp = 1; smp <= GetNumSamples(); smp++)
  594. {
  595. ModSample &sample = Samples[smp];
  596. sample.uFlags.set(srcSample.uFlags);
  597. LimitMax(sample.nLength, srcSample.nLength);
  598. if(sample.nLength)
  599. {
  600. sample.AllocateSample();
  601. memcpy(sample.sampleb(), smpData, sample.GetSampleSizeInBytes());
  602. smpData += sample.GetSampleSizeInBytes();
  603. srcSample.nLength -= sample.nLength;
  604. SmpLength gap = Util::muldiv_unsigned(454, srcSample.nC5Speed, 10000);
  605. LimitMax(gap, srcSample.nLength);
  606. smpData += gap * srcSample.GetBytesPerSample();
  607. srcSample.nLength -= gap;
  608. }
  609. }
  610. srcSample.FreeSample();
  611. }
  612. }
  613. #endif // MPT_ENABLE_MP3_SAMPLES
  614. return true;
  615. }
  616. OPENMPT_NAMESPACE_END