1 /*
2  *  tracker/ModuleEditor.h
3  *
4  *  Copyright 2009 Peter Barth
5  *
6  *  This file is part of Milkytracker.
7  *
8  *  Milkytracker is free software: you can redistribute it and/or modify
9  *  it under the terms of the GNU General Public License as published by
10  *  the Free Software Foundation, either version 3 of the License, or
11  *  (at your option) any later version.
12  *
13  *  Milkytracker is distributed in the hope that it will be useful,
14  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
15  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  *  GNU General Public License for more details.
17  *
18  *  You should have received a copy of the GNU General Public License
19  *  along with Milkytracker.  If not, see <http://www.gnu.org/licenses/>.
20  *
21  */
22 
23 #ifndef MODULEDITOR__H
24 #define MODULEDITOR__H
25 
26 #include "MilkyPlay.h"
27 #include "BasicTypes.h"
28 #include "PatternEditorTools.h"
29 #include "SongLengthEstimator.h"
30 
31 class XIInstrument;
32 class PatternEditor;
33 class SampleEditor;
34 class EnvelopeEditor;
35 class ModuleServices;
36 class PlayerCriticalSection;
37 
38 class ModuleEditor
39 {
40 public:
41 	enum
42 	{
43 		// FT2 noterange
44 		MAX_NOTE = 96,
45 		// FT2 maximum of instruments
46 		MAX_INSTRUMENTS = 255,
47 		// FT2 maximum of patterns
48 		MAX_PATTERNS = 256,
49 		// FT2 length of module title
50 		MAX_TITLETEXT = 20,
51 		// FT2 length of instrument text
52 		MAX_INSTEXT = 22,
53 		// FT2 length of sample text
54 		MAX_SMPTEXT = 22,
55 	};
56 
57 	// "proxy" instrument type
58 	struct TEditorInstrument
59 	{
60 		TXMInstrument* instrument;
61 
62 		// since TXMInstruments holds only references to the *used* samples
63 		// we need another list of samples here in case the user wants to
64 		// add a new sample that isn't used yet
65 
66 		// fasttracker 2 can only handle 16 samples
67 		mp_sint32 numUsedSamples;
68 		mp_sint32 usedSamples[16];
69 
70 		// FT2 can only handle 96 notes
71 		mp_ubyte nbu[MAX_NOTE];
72 
73 		// fasttracker can only handle envelopes per instrument,
74 		// no envelopes per sample
75 		// store default envelope here
76 		mp_sint32 volumeEnvelope;
77 		mp_sint32 panningEnvelope;
78 
79 		// default volume fadeout
80 		mp_uword volfade;
81 		// default autovibrato stuff
82 		mp_ubyte vibtype, vibsweep, vibdepth, vibrate;
83 	};
84 
85 	enum ModSaveTypes
86 	{
87 		ModSaveTypeDefault = -1,
88 		ModSaveTypeXM,
89 		ModSaveTypeMOD
90 	};
91 
92 private:
93 	XModule* module;
94 	PatternEditor* patternEditor;
95 	SampleEditor* sampleEditor;
96 	EnvelopeEditor* envelopeEditor;
97 	class ChangesListener* changesListener;
98 	ModuleServices* moduleServices;
99 	PlayerCriticalSection* playerCriticalSection;
100 
101 	bool changed;
102 
103 	PPSystemString moduleFileName;
104 	PPSystemString sampleFileName;
105 	PPSystemString instrumentFileName;
106 	ModSaveTypes eSaveType;
107 
108 	void enterCriticalSection();
109 	void leaveCriticalSection();
110 
111 	void adjustExtension(bool hasExtension = true);
112 
113 	TEditorInstrument* instruments;
114 
115 	// convert MilkyPlay instrument at index i to our more XM-style instrument
116 	void convertInstrument(mp_sint32 i);
117 
118 	// create a mapping which helps to treat MilkyPlay style instruments like XM ones
119 	void buildInstrumentTable();
120 
121 	// make sure everything is fine
122 	void validateInstruments();
123 
124 	// insert an XIInstrument at position index
125 	bool insertXIInstrument(mp_sint32 index, const XIInstrument* ins);
126 
127 	XIInstrument* extractXIInstrument(mp_sint32 index);
128 
129 	bool allocatePattern(TXMPattern* pattern);
130 
131 	mp_sint32 lastRequestedPatternIndex;
132 
133 	mp_sint32 currentOrderIndex;
134 	mp_sint32 currentPatternIndex;
135 	mp_sint32 currentInstrumentIndex;
136 	mp_sint32 currentSampleIndex;
137 	PatternEditorTools::Position currentCursorPosition;
138 
139 	pp_int32 enumerationIndex;
140 
141 public:
142 	ModuleEditor();
143 	~ModuleEditor();
144 
getModule()145 	XModule* getModule() { return module; }
getPatternEditor()146 	PatternEditor* getPatternEditor() { return patternEditor; }
getSampleEditor()147 	SampleEditor* getSampleEditor() { return sampleEditor; }
getEnvelopeEditor()148 	EnvelopeEditor* getEnvelopeEditor() { return envelopeEditor; }
getModuleServices()149 	ModuleServices* getModuleServices() { return moduleServices; }
150 
attachPlayerCriticalSection(PlayerCriticalSection * playerCriticalSection)151 	void attachPlayerCriticalSection(PlayerCriticalSection* playerCriticalSection) { this->playerCriticalSection = playerCriticalSection; }
152 
153 	PPSystemString getModuleFileNameFull(ModSaveTypes extension = ModSaveTypeDefault);
154 	PPSystemString getModuleFileName(ModSaveTypes extension = ModSaveTypeDefault);
155 
getSaveType()156 	ModSaveTypes getSaveType() const { return eSaveType; }
157 
setCurrentOrderIndex(mp_sint32 index)158 	void setCurrentOrderIndex(mp_sint32 index) { currentOrderIndex = index; }
getCurrentOrderIndex()159 	mp_sint32 getCurrentOrderIndex() const { return currentOrderIndex; }
160 
setCurrentPatternIndex(mp_sint32 index)161 	void setCurrentPatternIndex(mp_sint32 index) { currentPatternIndex = index; }
getCurrentPatternIndex()162 	mp_sint32 getCurrentPatternIndex() const { return currentPatternIndex; }
163 
setCurrentInstrumentIndex(mp_sint32 index)164 	void setCurrentInstrumentIndex(mp_sint32 index) { currentInstrumentIndex = index; }
getCurrentInstrumentIndex()165 	mp_sint32 getCurrentInstrumentIndex() const { return currentInstrumentIndex; }
166 
setCurrentSampleIndex(mp_sint32 index)167 	void setCurrentSampleIndex(mp_sint32 index) { currentSampleIndex = index; }
getCurrentSampleIndex()168 	mp_sint32 getCurrentSampleIndex() const { return currentSampleIndex; }
169 
setCurrentCursorPosition(const PatternEditorTools::Position & currentCursorPosition)170 	void setCurrentCursorPosition(const PatternEditorTools::Position& currentCursorPosition) { this->currentCursorPosition = currentCursorPosition; }
getCurrentCursorPosition()171 	const PatternEditorTools::Position& getCurrentCursorPosition() { return currentCursorPosition; }
172 
setChanged()173 	void setChanged() { changed = true; }
hasChanged()174 	bool hasChanged() const { return changed; }
175 
176 	void reloadCurrentPattern();
177 	void reloadSample(mp_sint32 insIndex, mp_sint32 smpIndex);
178 	void reloadEnvelope(mp_sint32 insIndex, mp_sint32 smpIndex, mp_sint32 type);
179 
180 	// --------- Access to module information/module data ---------------------------
181 	void setNumChannels(mp_uint32 channels);
getNumChannels()182 	mp_uint32 getNumChannels() const { return module->header.channum; }
183 
184 	void setTitle(const char* name, mp_uint32 length);
185 	void getTitle(char* name, mp_uint32 length) const;
186 
187 	void setNumOrders(mp_sint32 numOrders);
getNumOrders()188 	mp_sint32 getNumOrders() const { return module->header.ordnum; }
189 
getNumInstruments()190 	mp_sint32 getNumInstruments() const { return module->header.insnum; }
getNumSamples(mp_sint32 insIndex)191 	mp_sint32 getNumSamples(mp_sint32 insIndex) const { return instruments[insIndex].numUsedSamples; }
192 
193 	enum Frequencies
194 	{
195 		FrequencyAmiga = 0,
196 		FrequencyLinear = 1
197 	};
198 	void setFrequency(Frequencies frequency);
getFrequency()199 	Frequencies getFrequency() const { return (Frequencies)(module->header.freqtab & 1); }
200 
getSongBPM()201 	mp_sint32 getSongBPM() const { return module->header.speed; }
getSongTickSpeed()202 	mp_sint32 getSongTickSpeed() const { return module->header.tempo; }
203 
204 	// Check if song is protracker incompatible
205 	// 0 = PTK compatible
206 	// 1 = Song contains more than 31 instruments
207 	// 2 = Song uses linear frequencies
208 	// 3 = Song contains incompatible samples
209 	// 4 = Song contains FT2-style instruments
210 	// 5 = Incompatible pattern data
getPTIncompatibilityCode()211 	mp_uint32 getPTIncompatibilityCode() const { return module->isPTCompatible(); }
212 
213 private:
getSampleInfoInternal(mp_sint32 insIndex,mp_sint32 smpIndex)214 	TXMSample* getSampleInfoInternal(mp_sint32 insIndex, mp_sint32 smpIndex) const { return &module->smp[instruments[insIndex].usedSamples[smpIndex]]; }
215 
216 public:
getSampleInfo(mp_sint32 insIndex,mp_sint32 smpIndex)217 	TXMSample* getSampleInfo(mp_sint32 insIndex, mp_sint32 smpIndex) { return &module->smp[instruments[insIndex].usedSamples[smpIndex]]; }
218 
219 	void setSampleName(mp_sint32 insIndex, mp_sint32 smpIndex, const char* name, mp_uint32 length);
220 	void getSampleName(mp_sint32 insIndex, mp_sint32 smpIndex, char* name, mp_uint32 length) const;
221 	void setCurrentSampleName(const char* name, mp_uint32 length);
222 
223 private:
224 	TXMSample* getFirstSampleInfo();
225 	TXMSample* getNextSampleInfo();
226 
227 public:
getInstrumentInfo(mp_sint32 insIndex)228 	TEditorInstrument* getInstrumentInfo(mp_sint32 insIndex) { return &instruments[insIndex]; }
229 
230 	void setInstrumentName(mp_sint32 insIndex, const char* name, mp_uint32 length);
231 	void getInstrumentName(mp_sint32 insIndex, char* name, mp_uint32 length) const;
232 
233 	TEnvelope* getEnvelope(mp_sint32 insIndex, mp_sint32 smpIndex, mp_sint32 type);
234 
235 	bool createNewSong(mp_uword numChannels = 8);
236 	void createEmptySong(bool clearPatterns = true, bool clearInstruments = true, mp_sint32 numChannels = 8);
237 	bool isEmpty() const;
238 
239 	bool openSong(const SYSCHAR* fileName, const SYSCHAR* preferredFileName = NULL);
240 	bool saveSong(const SYSCHAR* fileName, ModSaveTypes saveType = ModSaveTypeXM);
241 	mp_sint32 saveBackup(const SYSCHAR* fileName);
242 
243 	void increaseSongLength();
244 	void decreaseSongLength();
245 
getRepeatPos()246 	mp_sint32 getRepeatPos() const { return module->header.restart; }
247 
248 	void increaseRepeatPos();
249 	void decreaseRepeatPos();
250 
251 	// insert new position into orderlist
252 	bool insertNewOrderPosition(mp_sint32 index);
253 	// delete position from orderlist
254 	void deleteOrderPosition(mp_sint32 index);
255 	// duplicate current position and add one
256 	bool seqCurrentOrderPosition(mp_sint32 index, bool clone = false);
257 
258 	// get pattern index at order position
259 	mp_sint32 getOrderPosition(mp_sint32 index) const;
260 
261 	// inrease pattern number in the orderlist
262 	void increaseOrderPosition(mp_sint32 index);
263 	// decrease pattern number in the orderlist
264 	void decreaseOrderPosition(mp_sint32 index);
265 
266 	bool isEditingOrderPosition(mp_sint32 index) const;
267 
268 	// throw away trailing empty patterns
269 	// handle with care, they might be currently playing
270 	void cleanUnusedPatterns();
271 
272 	// get pattern, if not allocated yet, allocate one
273 	TXMPattern* getPattern(mp_sint32 index, bool cleanUnusedPatterns = false);
274 
275 	// allocate new sample within given instrument (obsolete)
276 	//mp_sint32 allocateSample(mp_sint32 index);
277 
278 	// free last sample within given instrument (obsolete)
279 	void freeSample(mp_sint32 index);
280 
281 	// deallocate memory used by sample
282 	void clearSample(mp_sint32 smpIndex);
283 	void clearSample(mp_sint32 insIndex, mp_sint32 smpIndex);
284 
285 	// load sample
286 	bool loadSample(const SYSCHAR* fileName,
287 					mp_sint32 insIndex,
288 					mp_sint32 smpIndex,
289 					mp_sint32 channelIndex,
290 					const SYSCHAR* preferredFileName = NULL);
291 
292 	// retrieve number of channels contained in a sample on disk
293 	mp_sint32 getNumSampleChannels(const SYSCHAR* fileName);
294 
295 	// get name of channel
296 	const char* getNameOfSampleChannel(const SYSCHAR* fileName, mp_sint32 index);
297 
298 	// save sample
299 	enum SampleFormatTypes
300 	{
301 		SampleFormatTypeWAV,
302 		SampleFormatTypeIFF
303 	};
304 
305 	bool saveSample(const SYSCHAR* fileName, mp_sint32 insIndex, mp_sint32 smpIndex, SampleFormatTypes format);
306 
307 	// create sample filename from sample name information
308 	const PPSystemString& getSampleFileName(mp_sint32 insIndex, mp_sint32 smpIndex);
309 
310 	// postprocessing of samples, when changes are made
311 	void finishSamples();
312 
313 	// allocate one more instrument
314 	mp_sint32 allocateInstrument();
315 	// free last instrument
316 	void freeInstrument();
317 
318 	// load instrument
319 	bool loadInstrument(const SYSCHAR* fileName, mp_sint32 index);
320 	// save instrument
321 	bool saveInstrument(const SYSCHAR* fileName, mp_sint32 index);
322 
323 	// zap (clear) instrument
324 	bool zapInstrument(mp_sint32 index);
325 
326 	// create instrument filename from instrument name information
327 	const PPSystemString& getInstrumentFileName(mp_sint32 index);
328 
329 	bool copyInstrument(ModuleEditor& dstModule, mp_sint32 dstIndex,
330 						ModuleEditor& srcModule, mp_sint32 srcIndex);
331 	bool swapInstruments(ModuleEditor& dstModule, mp_sint32 dstIndex,
332 						 ModuleEditor& srcModule, mp_sint32 srcIndex);
333 
334 	bool copySample(ModuleEditor& dstModule, mp_sint32 dstInsIndex, mp_sint32 dstIndex,
335 					ModuleEditor& srcModule, mp_sint32 srcInsIndex, mp_sint32 srcIndex);
336 	bool swapSamples(ModuleEditor& dstModule, mp_sint32 dstInsIndex, mp_sint32 dstIndex,
337 					 ModuleEditor& srcModule, mp_sint32 srcInsIndex, mp_sint32 srcIndex);
338 
339 	// get note->sample LUT
340 	const mp_ubyte* getSampleTable(mp_sint32 index);
341 
342 	// update note->sample LUT
343 	void updateSampleTable(mp_sint32 index, const mp_ubyte* nbu);
344 
345 	// update instrument autovibrato / volume fadeout stuff
346 	void updateInstrumentData(mp_sint32 index);
347 
348 	// remap instruments in entire song
349 	pp_int32 insRemapSong(pp_int32 oldIns, pp_int32 newIns);
350 
351 	// transpose notes in entire song
352 	pp_int32 noteTransposeSong(const PatternEditorTools::TransposeParameters& transposeParameters, bool evaluate = false);
353 
354 	// panning effect conversion
355 	enum PanConversionTypes
356 	{
357 		PanConversionTypeConvert_E8x,
358 		PanConversionTypeConvert_80x,
359 		PanConversionTypeRemove_E8x,
360 		PanConversionTypeRemove_8xx
361 	};
362 
363 	pp_int32 panConvertSong(PanConversionTypes type);
364 
365 	// Optimizing features operating on the entire song
366 	// --------------------------------------------------------
367 	// remove unused patterns, remapping is always done
368 	pp_int32 removeUnusedPatterns(bool evaluate);
369 	// remove unused instruments, remapping is optional
370 	pp_int32 removeUnusedInstruments(bool evaluate, bool remap);
371 	// remove unused samples, no remapping is performed
372 	pp_int32 removeUnusedSamples(bool evaluate);
373 
374 	pp_int32 relocateCommands(const PatternEditorTools::RelocateParameters& relocateParameters, bool evaluate);
375 	pp_int32 zeroOperands(const PatternEditorTools::OperandOptimizeParameters& optimizeParameters, bool evaluate);
376 	pp_int32 fillOperands(const PatternEditorTools::OperandOptimizeParameters& optimizeParameters, bool evaluate);
377 
378 	void optimizeSamples(bool convertTo8Bit, bool minimize,
379 						 mp_sint32& numConvertedSamples, mp_sint32& numMinimizedSamples,
380 						 bool evaluate);
381 
382 public:
383 	static void insertText(char* dst, const char* src, mp_sint32 max);
384 
385 	static PPSystemString getTempFilename();
386 
387 	friend class ChangesListener;
388 };
389 
390 #endif
391