pattern.h 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214
  1. /*
  2. * Pattern.h
  3. * ---------
  4. * Purpose: Module Pattern header class
  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. #pragma once
  10. #include "openmpt/all/BuildSettings.hpp"
  11. #include <vector>
  12. #include "modcommand.h"
  13. #include "Snd_defs.h"
  14. OPENMPT_NAMESPACE_BEGIN
  15. class CPatternContainer;
  16. class CSoundFile;
  17. class EffectWriter;
  18. typedef ModCommand* PatternRow;
  19. class CPattern
  20. {
  21. friend class CPatternContainer;
  22. public:
  23. CPattern& operator= (const CPattern &pat);
  24. bool operator== (const CPattern &other) const;
  25. bool operator!= (const CPattern &other) const { return !(*this == other); }
  26. public:
  27. ModCommand* GetpModCommand(const ROWINDEX r, const CHANNELINDEX c) { return &m_ModCommands[r * GetNumChannels() + c]; }
  28. const ModCommand* GetpModCommand(const ROWINDEX r, const CHANNELINDEX c) const { return &m_ModCommands[r * GetNumChannels() + c]; }
  29. ROWINDEX GetNumRows() const { return m_Rows; }
  30. ROWINDEX GetRowsPerBeat() const { return m_RowsPerBeat; } // pattern-specific rows per beat
  31. ROWINDEX GetRowsPerMeasure() const { return m_RowsPerMeasure; } // pattern-specific rows per measure
  32. bool GetOverrideSignature() const { return (m_RowsPerBeat + m_RowsPerMeasure > 0); } // override song time signature?
  33. // Returns true if pattern data can be accessed at given row, false otherwise.
  34. bool IsValidRow(const ROWINDEX row) const { return (row < GetNumRows()); }
  35. // Returns true if any pattern data is present.
  36. bool IsValid() const { return !m_ModCommands.empty(); }
  37. // Return PatternRow object which has operator[] defined so that ModCommand
  38. // at (iRow, iChn) can be accessed with GetRow(iRow)[iChn].
  39. PatternRow GetRow(const ROWINDEX row) { return GetpModCommand(row, 0); }
  40. PatternRow GetRow(const ROWINDEX row) const { return const_cast<ModCommand *>(GetpModCommand(row, 0)); }
  41. CHANNELINDEX GetNumChannels() const;
  42. // Add or remove rows from the pattern.
  43. bool Resize(const ROWINDEX newRowCount, bool enforceFormatLimits = true, bool resizeAtEnd = true);
  44. // Check if there is any note data on a given row.
  45. bool IsEmptyRow(ROWINDEX row) const;
  46. // Allocate new pattern memory and replace old pattern data.
  47. bool AllocatePattern(ROWINDEX rows);
  48. // Deallocate pattern data.
  49. void Deallocate();
  50. // Removes all modcommands from the pattern.
  51. void ClearCommands();
  52. // Returns associated soundfile.
  53. CSoundFile& GetSoundFile();
  54. const CSoundFile& GetSoundFile() const;
  55. const std::vector<ModCommand> &GetData() const { return m_ModCommands; }
  56. void SetData(std::vector<ModCommand> &&data) { MPT_ASSERT(data.size() == GetNumRows() * GetNumChannels()); m_ModCommands = std::move(data); }
  57. // Set pattern signature (rows per beat, rows per measure). Returns true on success.
  58. bool SetSignature(const ROWINDEX rowsPerBeat, const ROWINDEX rowsPerMeasure);
  59. void RemoveSignature() { m_RowsPerBeat = m_RowsPerMeasure = 0; }
  60. bool HasTempoSwing() const { return !m_tempoSwing.empty(); }
  61. const TempoSwing& GetTempoSwing() const { return m_tempoSwing; }
  62. void SetTempoSwing(const TempoSwing &swing) { m_tempoSwing = swing; m_tempoSwing.Normalize(); }
  63. void RemoveTempoSwing() { m_tempoSwing.clear(); }
  64. // Pattern name functions - bool functions return true on success.
  65. bool SetName(const std::string &newName);
  66. bool SetName(const char *newName, size_t maxChars);
  67. template<size_t bufferSize>
  68. bool SetName(const char (&buffer)[bufferSize])
  69. {
  70. return SetName(buffer, bufferSize);
  71. }
  72. std::string GetName() const { return m_PatternName; }
  73. #ifdef MODPLUG_TRACKER
  74. // Double number of rows
  75. bool Expand();
  76. // Halve number of rows
  77. bool Shrink();
  78. #endif // MODPLUG_TRACKER
  79. // Write some kind of effect data to the pattern
  80. bool WriteEffect(EffectWriter &settings);
  81. typedef std::vector<ModCommand>::iterator iterator;
  82. typedef std::vector<ModCommand>::const_iterator const_iterator;
  83. iterator begin() { return m_ModCommands.begin(); }
  84. const_iterator begin() const { return m_ModCommands.begin(); }
  85. const_iterator cbegin() const { return m_ModCommands.cbegin(); }
  86. iterator end() { return m_ModCommands.end(); }
  87. const_iterator end() const { return m_ModCommands.end(); }
  88. const_iterator cend() const { return m_ModCommands.cend(); }
  89. CPattern(CPatternContainer& patCont) : m_rPatternContainer(patCont) {}
  90. CPattern(const CPattern &) = default;
  91. CPattern(CPattern &&) noexcept = default;
  92. protected:
  93. ModCommand& GetModCommand(size_t i) { return m_ModCommands[i]; }
  94. //Returns modcommand from (floor[i/channelCount], i%channelCount)
  95. ModCommand& GetModCommand(ROWINDEX r, CHANNELINDEX c) { return m_ModCommands[r * GetNumChannels() + c]; }
  96. const ModCommand& GetModCommand(ROWINDEX r, CHANNELINDEX c) const { return m_ModCommands[r * GetNumChannels() + c]; }
  97. protected:
  98. std::vector<ModCommand> m_ModCommands;
  99. ROWINDEX m_Rows = 0;
  100. ROWINDEX m_RowsPerBeat = 0; // patterns-specific time signature. if != 0, this is implicitely set.
  101. ROWINDEX m_RowsPerMeasure = 0; // ditto
  102. TempoSwing m_tempoSwing;
  103. std::string m_PatternName;
  104. CPatternContainer& m_rPatternContainer;
  105. };
  106. const char FileIdPattern[] = "mptP";
  107. void ReadModPattern(std::istream& iStrm, CPattern& patc, const size_t nSize = 0);
  108. void WriteModPattern(std::ostream& oStrm, const CPattern& patc);
  109. // Class for conveniently writing an effect to the pattern.
  110. class EffectWriter
  111. {
  112. friend class CPattern;
  113. // Row advance mode
  114. enum RetryMode
  115. {
  116. rmIgnore, // If effect can't be written, abort.
  117. rmTryNextRow, // If effect can't be written, try next row.
  118. rmTryPreviousRow, // If effect can't be written, try previous row.
  119. };
  120. public:
  121. // Constructors with effect commands
  122. EffectWriter(EffectCommand cmd, ModCommand::PARAM param) : m_command(cmd), m_param(param), m_isVolEffect(false) { Init(); }
  123. EffectWriter(VolumeCommand cmd, ModCommand::VOL param) : m_volcmd(cmd), m_vol(param), m_isVolEffect(true) { Init(); }
  124. // Additional constructors:
  125. // Set row in which writing should start
  126. EffectWriter &Row(ROWINDEX row) { m_row = row; return *this; }
  127. // Set channel to which writing should be restricted to
  128. EffectWriter &Channel(CHANNELINDEX chn) { m_channel = chn; return *this; }
  129. // Allow multiple effects of the same kind to be written in the same row.
  130. EffectWriter &AllowMultiple() { m_allowMultiple = true; return *this; }
  131. // Set retry mode.
  132. EffectWriter &RetryNextRow() { m_retryMode = rmTryNextRow; return *this; }
  133. EffectWriter &RetryPreviousRow() { m_retryMode = rmTryPreviousRow; return *this; }
  134. protected:
  135. RetryMode m_retryMode;
  136. ROWINDEX m_row;
  137. CHANNELINDEX m_channel;
  138. union
  139. {
  140. EffectCommand m_command;
  141. VolumeCommand m_volcmd;
  142. };
  143. union
  144. {
  145. ModCommand::PARAM m_param;
  146. ModCommand::VOL m_vol;
  147. };
  148. bool m_retry : 1;
  149. bool m_allowMultiple : 1;
  150. bool m_isVolEffect : 1;
  151. // Common data initialisation
  152. void Init()
  153. {
  154. m_row = 0;
  155. m_channel = CHANNELINDEX_INVALID; // Any channel
  156. m_retryMode = rmIgnore; // If effect couldn't be written, abort.
  157. m_retry = true;
  158. m_allowMultiple = false; // Stop if same type of effect is encountered
  159. }
  160. };
  161. OPENMPT_NAMESPACE_END