File: ModSequence.h

package info (click to toggle)
libopenmpt 0.8.4-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 9,844 kB
  • sloc: cpp: 129,441; sh: 4,695; ansic: 1,107; makefile: 712
file content (223 lines) | stat: -rw-r--r-- 10,267 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
/*
 * 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 "openmpt/all/BuildSettings.hpp"

#include "Snd_defs.h"

#include <algorithm>
#include <vector>

OPENMPT_NAMESPACE_BEGIN

class CPattern;
class CSoundFile;
class ModSequenceSet;

class ModSequence: public std::vector<PATTERNINDEX>
{
	friend class ModSequenceSet;

protected:
	mpt::ustring m_name;           // Sequence name
	CSoundFile &m_sndFile;         // Associated CSoundFile
	ORDERINDEX m_restartPos = 0;   // Restart position when playback of this order ended
	TEMPO m_defaultTempo{125, 0};  // Default tempo at start of sequence
	uint32 m_defaultSpeed = 6;     // Default ticks per row at start of sequence

public:
	ModSequence(CSoundFile &sndFile);
	ModSequence(ModSequence &&) noexcept = default;
	ModSequence(const ModSequence &) = default;
	ModSequence& operator=(const ModSequence &other);

	bool operator==(const ModSequence &other) const noexcept;
	bool operator!=(const ModSequence &other) const noexcept { return !(*this == other); }

	ORDERINDEX GetLength() const noexcept { return mpt::saturate_cast<ORDERINDEX>(size()); }
	// Returns last accessible index, i.e. GetLength() - 1, or 0 if the order list is empty.
	ORDERINDEX GetLastIndex() const noexcept { return static_cast<ORDERINDEX>(std::max(ORDERINDEX(1), GetLength()) - 1u); }
	// Returns length of sequence without counting trailing '---' items.
	ORDERINDEX GetLengthTailTrimmed() const noexcept;
	// Returns length of sequence stopping counting on first '---' (or at the end of sequence).
	ORDERINDEX GetLengthFirstEmpty() const noexcept;
	// Returns amount of patterns that can be added at the end of the order list before reaching the current format's limits.
	ORDERINDEX GetRemainingCapacity(ORDERINDEX startingFrom = ORDERINDEX_INVALID, bool enforceFormatLimits = true) const noexcept;

	// Replaces order list with 'newSize' copies of 'pat'.
	void assign(ORDERINDEX newSize, PATTERNINDEX pat);

	// Inserts 'count' orders starting from 'pos' using 'fill' 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 (up to 'count' many).
	ORDERINDEX insert(ORDERINDEX pos, ORDERINDEX count) { return insert(pos, count, PATTERNINDEX_INVALID, true); }
	ORDERINDEX insert(ORDERINDEX pos, ORDERINDEX count, PATTERNINDEX fill, bool enforceFormatLimits = true);
	ORDERINDEX insert(ORDERINDEX pos, const mpt::span<const PATTERNINDEX> orders, bool enforceFormatLimits = true);

	void push_back() { push_back(PATTERNINDEX_INVALID); }
	void push_back(PATTERNINDEX pat) { if(GetLength() < MAX_ORDERS) std::vector<PATTERNINDEX>::push_back(pat); }

	void resize(ORDERINDEX newSize) { resize(newSize, PATTERNINDEX_INVALID); }
	void resize(ORDERINDEX newSize, PATTERNINDEX pat) { std::vector<PATTERNINDEX>::resize(std::min(MAX_ORDERS, newSize), pat); }

	// Removes orders from range [posBegin, posEnd].
	void Remove(ORDERINDEX posBegin, ORDERINDEX posEnd) noexcept;

	// Remove all references to a given pattern index from the order list. Jump commands are updated accordingly.
	void RemovePattern(PATTERNINDEX pat);

	// Replaces all occurences of oldPat with newPat.
	void Replace(PATTERNINDEX oldPat, PATTERNINDEX newPat) noexcept { if(oldPat != newPat) std::replace(begin(), end(), oldPat, newPat); }

	// Removes any "---" patterns at the end of the list.
	void Shrink() { resize(GetLengthTailTrimmed()); }

	// Check if pattern at sequence position ord is valid.
	bool IsValidPat(ORDERINDEX ord) const noexcept;

	CPattern *PatternAt(ORDERINDEX ord) const noexcept;

	void AdjustToNewModType(const MODTYPE oldtype);

	// 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 noexcept;
	ORDERINDEX GetNextOrderIgnoringSkips(const ORDERINDEX start) const noexcept;

	// Returns the first item that contains a pattern that actually exists, ORDERINDEX_INVALID if no such item exists.
	ORDERINDEX GetFirstValidIndex() const noexcept;

	// Find an order item that contains a given pattern number.
	ORDERINDEX FindOrder(PATTERNINDEX pat, ORDERINDEX startSearchAt = 0, bool searchForward = true) const noexcept;

	// 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);

#ifndef MODPLUG_NO_FILESAVE
	// Write order items as bytes. '---' is written as stopIndex, '+++' is written as ignoreIndex
	size_t WriteAsByte(std::ostream &f, const ORDERINDEX count, uint8 stopIndex = 0xFF, uint8 ignoreIndex = 0xFE) const;
#endif // MODPLUG_NO_FILESAVE

	// Returns true if the IT orderlist datafield is not sufficient to store orderlist information.
	bool NeedsExtraDatafield() const noexcept;

#ifdef MODPLUG_TRACKER
	// Check if a playback position is currently locked (inaccessible)
	bool IsPositionLocked(ORDERINDEX position) const noexcept;
	// Check if this sequence has subsongs separated by invalid ("---" or non-existing) patterns
	bool HasSubsongs() const noexcept;
#endif // MODPLUG_TRACKER

	inline void SetName(mpt::ustring newName) noexcept { m_name = std::move(newName); }
	inline mpt::ustring GetName() const { return m_name; }

	inline void SetRestartPos(ORDERINDEX restartPos) noexcept { m_restartPos = restartPos; }
	inline ORDERINDEX GetRestartPos() const noexcept { return m_restartPos; }

	void SetDefaultTempo(TEMPO tempo) noexcept;
	inline void SetDefaultTempoInt(uint32 tempo) noexcept { SetDefaultTempo(TEMPO{mpt::saturate_cast<uint16>(tempo), 0}); }
	inline TEMPO GetDefaultTempo() const noexcept { return m_defaultTempo; }

	inline void SetDefaultSpeed(uint32 speed) noexcept { m_defaultSpeed = speed ? speed : 6; }
	inline uint32 GetDefaultSpeed() const noexcept { return m_defaultSpeed; }
};


class ModSequenceSet
{
	friend void ReadModSequenceOld(std::istream& iStrm, ModSequenceSet& seq, const size_t);
	friend void ReadModSequences(std::istream& iStrm, ModSequenceSet& seq, const size_t, mpt::Charset defaultCharset);

protected:
	std::vector<ModSequence> m_Sequences;  // Array of sequences
	CSoundFile &m_sndFile;
	SEQUENCEINDEX m_currentSeq = 0;  // Index of current sequence.

public:
	ModSequenceSet(CSoundFile &sndFile);
	ModSequenceSet(ModSequenceSet &&) noexcept = default;
	ModSequenceSet& operator=(const ModSequenceSet & other);

	// Remove all sequences and initialize default sequence
	void Initialize();

	// Get the working sequence
	ModSequence& operator() () { return m_Sequences[m_currentSeq]; }
	const ModSequence& operator() () const { return m_Sequences[m_currentSeq]; }
	// Get an arbitrary sequence
	ModSequence& operator() (SEQUENCEINDEX seq) { return m_Sequences[seq]; }
	const ModSequence& operator() (SEQUENCEINDEX seq) const { return m_Sequences[seq]; }

	SEQUENCEINDEX GetNumSequences() const noexcept { return static_cast<SEQUENCEINDEX>(m_Sequences.size()); }
	SEQUENCEINDEX GetCurrentSequenceIndex() const noexcept { return m_currentSeq; }

	// Sets working sequence.
	void SetSequence(SEQUENCEINDEX) noexcept;
	// Add new empty sequence.
	// Returns the ID of the new sequence, or SEQUENCEINDEX_INVALID on failure.
	SEQUENCEINDEX AddSequence();
	// Removes given sequence.
	void RemoveSequence(SEQUENCEINDEX);

#ifdef MODPLUG_TRACKER
	// Assigns a new set of sequences. The vector contents indicate which existing sequences to keep / duplicate or if a new sequences should be inserted (SEQUENCEINDEX_INVALID)
	// The function fails if the vector is empty or contains too many sequences.
	bool Rearrange(const std::vector<SEQUENCEINDEX> &newOrder);

	// Adjust sequence when converting between module formats
	void OnModTypeChanged(MODTYPE oldType);
	// Check if there is a single sequences that qualifies for subsong splitting
	bool CanSplitSubsongs() const noexcept;
	// If there are subsongs (separated by "---" 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 SplitSubsongsToMultipleSequences();

	// Convert the sequence's restart position and tempo information to a pattern command.
	bool WriteGlobalsToPattern(SEQUENCEINDEX seq, bool writeRestartPos, bool writeTempo);
	// 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

	std::vector<ModSequence>::iterator begin() noexcept { return m_Sequences.begin(); }
	std::vector<ModSequence>::const_iterator begin() const noexcept { return m_Sequences.begin(); }
	std::vector<ModSequence>::const_iterator cbegin() const noexcept { return m_Sequences.cbegin(); }
	std::vector<ModSequence>::iterator end() noexcept { return m_Sequences.end(); }
	std::vector<ModSequence>::const_iterator end() const noexcept { return m_Sequences.end(); }
	std::vector<ModSequence>::const_iterator cend() const noexcept { return m_Sequences.cend(); }
};


const char FileIdSequences[] = "mptSeqC";
const char FileIdSequence[] = "mptSeq";

#ifndef MODPLUG_NO_FILESAVE
void WriteModSequences(std::ostream& oStrm, const ModSequenceSet& seq);
#endif // MODPLUG_NO_FILESAVE
void ReadModSequences(std::istream& iStrm, ModSequenceSet& seq, const size_t nSize, mpt::Charset defaultCharset);

#ifndef MODPLUG_NO_FILESAVE
void WriteModSequence(std::ostream& oStrm, const ModSequence& seq);
#endif // MODPLUG_NO_FILESAVE
void ReadModSequence(std::istream& iStrm, ModSequence& seq, const size_t, mpt::Charset defaultCharset);

#ifndef MODPLUG_NO_FILESAVE
void WriteModSequenceOld(std::ostream& oStrm, const ModSequenceSet& seq);
#endif // MODPLUG_NO_FILESAVE
void ReadModSequenceOld(std::istream& iStrm, ModSequenceSet& seq, const size_t);


OPENMPT_NAMESPACE_END