diff options
author | James Cowgill <jcowgill@debian.org> | 2016-10-03 10:03:42 +0100 |
---|---|---|
committer | James Cowgill <jcowgill@debian.org> | 2016-10-03 10:03:42 +0100 |
commit | 8afe53282f39c89e2425dbf1afd438719e1aedeb (patch) | |
tree | bdf8aadb1d7e65ea1c12a7813a3334b0e70a8a41 /soundlib/ModSequence.h |
New upstream version 0.2.7025~beta20.1
Diffstat (limited to 'soundlib/ModSequence.h')
-rw-r--r-- | soundlib/ModSequence.h | 252 |
1 files changed, 252 insertions, 0 deletions
diff --git a/soundlib/ModSequence.h b/soundlib/ModSequence.h new file mode 100644 index 0000000..f504ff7 --- /dev/null +++ b/soundlib/ModSequence.h @@ -0,0 +1,252 @@ +/* + * ModSequence.h + * ------------- + * Purpose: Order and sequence handling. + * Notes : (currently none) + * Authors: OpenMPT Devs + * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. + */ + + +#pragma once + +#include <algorithm> +#include <vector> + +OPENMPT_NAMESPACE_BEGIN + +class CSoundFile; +class ModSequenceSet; +class FileReader; + +class ModSequence +//=============== +{ +public: + friend class ModSequenceSet; + typedef PATTERNINDEX* iterator; + typedef const PATTERNINDEX* const_iterator; + + friend void WriteModSequence(std::ostream& oStrm, const ModSequence& seq); + friend void ReadModSequence(std::istream& iStrm, ModSequence& seq, const size_t); + + virtual ~ModSequence() {if (m_bDeletableArray) delete[] m_pArray;} + ModSequence(const ModSequence&); + ModSequence(CSoundFile& rSf, ORDERINDEX nSize); + ModSequence(CSoundFile& rSf, PATTERNINDEX* pArray, ORDERINDEX nSize, ORDERINDEX nCapacity, ORDERINDEX restartPos, bool bDeletableArray); + + // Initialize default sized sequence. + void Init(); + + PATTERNINDEX& operator[](const size_t i) {MPT_ASSERT(i < m_nSize); return m_pArray[i];} + const PATTERNINDEX& operator[](const size_t i) const {MPT_ASSERT(i < m_nSize); return m_pArray[i];} + PATTERNINDEX& At(const size_t i) {return (*this)[i];} + const PATTERNINDEX& At(const size_t i) const {return (*this)[i];} + + PATTERNINDEX& Last() {MPT_ASSERT(m_nSize > 0); return m_pArray[m_nSize-1];} + const PATTERNINDEX& Last() const {MPT_ASSERT(m_nSize > 0); return m_pArray[m_nSize-1];} + + // Returns last accessible index, i.e. GetLength() - 1. Behaviour is undefined if length is zero. + ORDERINDEX GetLastIndex() const {return m_nSize - 1;} + + void Append() {Append(GetInvalidPatIndex());} // Appends InvalidPatIndex. + void Append(PATTERNINDEX nPat); // Appends given patindex. + + // Inserts nCount orders starting from nPos using nFill as the pattern index for all inserted orders. + // Sequence will automatically grow if needed and if it can't grow enough, some tail + // orders will be discarded. + // Return: Number of orders inserted. + ORDERINDEX Insert(ORDERINDEX nPos, ORDERINDEX nCount) { return Insert(nPos, nCount, GetInvalidPatIndex()); } + ORDERINDEX Insert(ORDERINDEX nPos, ORDERINDEX nCount, PATTERNINDEX nFill); + + // Removes orders from range [nPosBegin, nPosEnd]. + void Remove(ORDERINDEX nPosBegin, ORDERINDEX nPosEnd); + + // Remove all references to a given pattern index from the order list. Jump commands are updated accordingly. + void RemovePattern(PATTERNINDEX which); + + // Check if pattern at sequence position ord is valid. + bool IsValidPat(ORDERINDEX ord); + + void clear(); + void resize(ORDERINDEX nNewSize) {resize(nNewSize, GetInvalidPatIndex());} + void resize(ORDERINDEX nNewSize, PATTERNINDEX nFill); + + // Replaces all occurences of nOld with nNew. + void Replace(PATTERNINDEX nOld, PATTERNINDEX nNew) {if (nOld != nNew) std::replace(begin(), end(), nOld, nNew);} + + void AdjustToNewModType(const MODTYPE oldtype); + + ORDERINDEX size() const {return GetLength();} + ORDERINDEX GetLength() const {return m_nSize;} + + // Returns length of sequence without counting trailing '---' items. + ORDERINDEX GetLengthTailTrimmed() const; + + // Returns length of sequence stopping counting on first '---' (or at the end of sequence). + ORDERINDEX GetLengthFirstEmpty() const; + + // Returns the internal representation of a stop '---' index + static PATTERNINDEX GetInvalidPatIndex() { return uint16_max; } + // Returns the internal representation of an ignore '+++' index + static PATTERNINDEX GetIgnoreIndex() {return uint16_max - 1; } + + // Returns the previous/next order ignoring skip indices(+++). + // If no previous/next order exists, return first/last order, and zero + // when orderlist is empty. + ORDERINDEX GetPreviousOrderIgnoringSkips(const ORDERINDEX start) const; + ORDERINDEX GetNextOrderIgnoringSkips(const ORDERINDEX start) const; + + // Find an order item that contains a given pattern number. + ORDERINDEX FindOrder(PATTERNINDEX nPat, ORDERINDEX startFromOrder = 0, bool searchForward = true) const; + + // Ensures that the pattern at the specified order position is used only once (across all sequences). + // If another usage is found, the pattern is replaced by a copy and the new index is returned. + PATTERNINDEX EnsureUnique(ORDERINDEX ord); + + ModSequence& operator=(const ModSequence& seq); + + // Write order items as bytes. '---' is written as stopIndex, '+++' is written as ignoreIndex + size_t WriteAsByte(FILE* f, const ORDERINDEX count, uint8 stopIndex = 0xFF, uint8 ignoreIndex = 0xFE) const; + // Read order items as bytes from a file. 'howMany' bytes are read from the file, optionally only the first 'readEntries' of them are actually processed. + // 'stopIndex' is treated as '---', 'ignoreIndex' is treated as '+++'. If the format doesn't support such indices, just pass a parameter that does not fit into a byte. + bool ReadAsByte(FileReader &file, size_t howMany, size_t readEntries = ORDERINDEX_MAX, uint16 stopIndex = ORDERINDEX_INVALID, uint16 ignoreIndex = ORDERINDEX_INVALID); + template<typename T, size_t arraySize> + // Read order items from an array. Only the first 'howMany' entries are actually processed. + // 'stopIndex' is treated as '---', 'ignoreIndex' is treated as '+++'. If the format doesn't support such indices, just pass ORDERINDEX_INVALID. + bool ReadFromArray(const T (&orders)[arraySize], size_t howMany = arraySize, uint16 stopIndex = ORDERINDEX_INVALID, uint16 ignoreIndex = ORDERINDEX_INVALID); + + // Deprecated function used for MPTm files created with OpenMPT 1.17.02.46 - 1.17.02.48. + bool Deserialize(FileReader &file); + + // Returns true if the IT orderlist datafield is not sufficient to store orderlist information. + bool NeedsExtraDatafield() const; + +#ifdef MODPLUG_TRACKER + // Check if a playback position is currently locked (inaccessible) + bool IsPositionLocked(ORDERINDEX position) const; +#endif // MODPLUG_TRACKER + + // Sequence name setter / getter + inline void SetName(const std::string &newName) { m_sName = newName;} + inline std::string GetName() const { return m_sName; } + + // Restart position setter / getter + inline void SetRestartPos(ORDERINDEX restartPos) { m_restartPos = restartPos; } + inline ORDERINDEX GetRestartPos() const { return m_restartPos; } + + +protected: + iterator begin() {return m_pArray;} + const_iterator begin() const {return m_pArray;} + iterator end() {return m_pArray + m_nSize;} + const_iterator end() const {return m_pArray + m_nSize;} + +protected: + std::string m_sName; // Sequence name. + + CSoundFile &m_sndFile; // Pointer to associated CSoundFile. + PATTERNINDEX *m_pArray; // Pointer to sequence array. + ORDERINDEX m_nSize; // Sequence length. + ORDERINDEX m_nCapacity; // Capacity in m_pArray. + ORDERINDEX m_restartPos; // Restart position when playback of this order ended + bool m_bDeletableArray; // True if m_pArray points the deletable(with delete[]) array. +}; + + +template<typename T, size_t arraySize> +bool ModSequence::ReadFromArray(const T (&orders)[arraySize], size_t howMany, uint16 stopIndex, uint16 ignoreIndex) +//----------------------------------------------------------------------------------------------------------------- +{ + STATIC_ASSERT(arraySize < ORDERINDEX_MAX); + LimitMax(howMany, arraySize); + ORDERINDEX readEntries = static_cast<ORDERINDEX>(howMany); + + if(GetLength() < readEntries) + { + resize(readEntries); + } + + for(int i = 0; i < readEntries; i++) + { + PATTERNINDEX pat = static_cast<PATTERNINDEX>(orders[i]); + if(pat == stopIndex) pat = GetInvalidPatIndex(); + if(pat == ignoreIndex) pat = GetIgnoreIndex(); + (*this)[i] = pat; + } + return true; +} + +//======================================= +class ModSequenceSet : public ModSequence +//======================================= +{ + friend void WriteModSequenceOld(std::ostream& oStrm, const ModSequenceSet& seq); + friend void ReadModSequenceOld(std::istream& iStrm, ModSequenceSet& seq, const size_t); + friend void WriteModSequences(std::ostream& oStrm, const ModSequenceSet& seq); + friend void ReadModSequences(std::istream& iStrm, ModSequenceSet& seq, const size_t); + friend void ReadModSequence(std::istream& iStrm, ModSequence& seq, const size_t); + +public: + ModSequenceSet(CSoundFile& sndFile); + + const ModSequence& GetSequence() {return GetSequence(GetCurrentSequenceIndex());} + const ModSequence& GetSequence(SEQUENCEINDEX nSeq) const; + ModSequence& GetSequence(SEQUENCEINDEX nSeq); + SEQUENCEINDEX GetNumSequences() const {return static_cast<SEQUENCEINDEX>(m_Sequences.size());} + void SetSequence(SEQUENCEINDEX); // Sets working sequence. + SEQUENCEINDEX AddSequence(bool bDuplicate = true); // Adds new sequence. If bDuplicate is true, new sequence is a duplicate of the old one. Returns the ID of the new sequence. + void RemoveSequence() {RemoveSequence(GetCurrentSequenceIndex());} + void RemoveSequence(SEQUENCEINDEX); // Removes given sequence + SEQUENCEINDEX GetCurrentSequenceIndex() const {return m_nCurrentSeq;} + + ModSequenceSet& operator=(const ModSequence& seq) {ModSequence::operator=(seq); return *this;} + +#ifdef MODPLUG_TRACKER + // Adjust sequence when converting between module formats + void OnModTypeChanged(const MODTYPE oldtype); + // If there are subsongs (separated by "---" or "+++" patterns) in the module, + // asks user whether to convert these into multiple sequences (given that the + // modformat supports multiple sequences). + // Returns true if sequences were modified, false otherwise. + bool ConvertSubsongsToMultipleSequences(); + + // Convert the sequence's restart position information to a pattern command. + bool RestartPosToPattern(SEQUENCEINDEX seq); + // Merges multiple sequences into one and destroys all other sequences. + // Returns false if there were no sequences to merge, true otherwise. + bool MergeSequences(); +#endif // MODPLUG_TRACKER + + static const ORDERINDEX s_nCacheSize; + +private: + void CopyCacheToStorage(); + void CopyStorageToCache(); + + // Array size should be s_nCacheSize. + // s_nCacheSize is defined out of line, because taking references to + // static const members is problematic for some clang and GCC versions + // otherwise. + // Defining s_nCacheSize out of line makes it unavailable for declaring + // an array. + PATTERNINDEX m_Cache[MAX_ORDERS]; // Local cache array. + std::vector<ModSequence> m_Sequences; // Array of sequences. + SEQUENCEINDEX m_nCurrentSeq; // Index of current sequence. +}; + + +const char FileIdSequences[] = "mptSeqC"; +const char FileIdSequence[] = "mptSeq"; + +void WriteModSequences(std::ostream& oStrm, const ModSequenceSet& seq); +void ReadModSequences(std::istream& iStrm, ModSequenceSet& seq, const size_t nSize = 0); + +void WriteModSequence(std::ostream& oStrm, const ModSequence& seq); +void ReadModSequence(std::istream& iStrm, ModSequence& seq, const size_t); + +void WriteModSequenceOld(std::ostream& oStrm, const ModSequenceSet& seq); +void ReadModSequenceOld(std::istream& iStrm, ModSequenceSet& seq, const size_t); + + +OPENMPT_NAMESPACE_END |