1 /*
2  * Copyright (c) 2009, The MilkyTracker Team.
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions are met:
7  *
8  * - Redistributions of source code must retain the above copyright notice,
9  *   this list of conditions and the following disclaimer.
10  * - Redistributions in binary form must reproduce the above copyright
11  *   notice, this list of conditions and the following disclaimer in the
12  *   documentation and/or other materials provided with the distribution.
13  * - Neither the name of the <ORGANIZATION> nor the names of its contributors
14  *   may be used to endorse or promote products derived from this software
15  *   without specific prior written permission.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
18  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
21  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
22  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
25  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
26  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27  * POSSIBILITY OF SUCH DAMAGE.
28  */
29 
30 /*
31  *  PlayerBase.h
32  *  MilkyPlay
33  *
34  *  Created by Peter Barth on Thu Jan 20 2005.
35  *
36  */
37 
38 #ifndef __PLAYERBASE_H__
39 #define __PLAYERBASE_H__
40 
41 #include "ChannelMixer.h"
42 
43 class XModule;
44 
45 struct TPlayerChannelInfo
46 {
47 	mp_ubyte note;
48 	mp_ubyte instrument;
49 	mp_ubyte volume;
50 	mp_ubyte panning;
51 	mp_ubyte numeffects;
52 	mp_ubyte effects[MP_NUMEFFECTS];
53 	mp_ubyte operands[MP_NUMEFFECTS];
54 
clearTPlayerChannelInfo55 	void clear()
56 	{
57 		memset(this, 0, sizeof(TPlayerChannelInfo));
58 	}
59 };
60 
61 class PlayModeSettings
62 {
63 public:
64 	enum PlayModes
65 	{
66 		PlayMode_Auto,
67 		PlayMode_ProTracker2,
68 		PlayMode_ProTracker3,
69 		PlayMode_ScreamTracker3,
70 		PlayMode_FastTracker2,
71 		PlayMode_ImpulseTracker
72 	};
73 
74 	enum PlayModeOptions
75 	{
76 		PlayModeOptionFirst = 0,
77 		PlayModeOptionPanning8xx = 0,
78 		PlayModeOptionPanningE8x = 1,
79 		// Only affects PTK playback mode
80 		PlayModeOptionForcePTPitchLimit = 2,
81 		PlayModeOptionLast
82 	};
83 
84 protected:
85 	bool			options[PlayModeOptionLast];
86 
87 	PlayModes		playMode;
88 };
89 
90 class PlayerBase : public ChannelMixer, public PlayModeSettings
91 {
92 private:
93 	struct TimeRecord
94 	{
95 		enum BITPOSITIONS
96 		{
97 			BITPOS_POS = 0,
98 			BITPOS_ROW = 8,
99 			BITPOS_TEMPO = 16,
100 			BITPOS_SPEED = 24,
101 
102 			BITPOS_MAINVOL = 0,
103 			BITPOS_TICKER = 8
104 		};
105 
106 		mp_uint32 posRowTempoSpeed;
107 		mp_sint32 mainVolumeTicker;
108 
TimeRecordTimeRecord109 		TimeRecord()
110 		{
111 		}
112 
TimeRecordTimeRecord113 		TimeRecord(mp_uint32 pos, mp_uint32 row, mp_uint32 tempo, mp_uint32 speed, mp_sint32 mainVol, mp_sint32 ticker) :
114 			posRowTempoSpeed((pos << BITPOS_POS) +
115 							 (row << BITPOS_ROW) +
116 							 (tempo << BITPOS_TEMPO) +
117 							 (speed << BITPOS_SPEED)),
118 			mainVolumeTicker((mainVol << BITPOS_MAINVOL) +
119 							 (ticker << BITPOS_TICKER))
120 		{
121 		}
122 	};
123 
124 	TimeRecord*		timeRecord;
125 
reallocTimeRecord()126 	void reallocTimeRecord()
127 	{
128 		delete[] timeRecord;
129 		timeRecord = new TimeRecord[getNumBeatPackets()+1];
130 
131 		updateTimeRecord();
132 	}
133 
updateTimeRecord()134 	void updateTimeRecord()
135 	{
136 		for (mp_uint32 i = 0; i < getNumBeatPackets()+1; i++)
137 		{
138 			timeRecord[i] = TimeRecord(poscnt,
139 									   rowcnt,
140 									   bpm,
141 									   tickSpeed,
142 									   mainVolume,
143 									   ticker);
144 		}
145 	}
146 
147 public:
148 	enum PlayerTypes
149 	{
150 		PlayerType_Generic,		// generic module player, can play most of the Protracker style formats
151 		PlayerType_FAR,			// Farandole composer player
152 		PlayerType_IT,			// Supposed to be a compatible IT replayer
153 		PlayerType_INVALID = -1	// NULL player :D
154 	};
155 
156 protected:
157 	XModule*		module;
158 
159 	bool			paused;					// Player is paused
160 	bool			halted;					// Playing has been stopped (song is over)
161 	bool			repeat;					// Player will repeat song
162 	bool			idle;					// Player is mixing, but not processing song
163 	bool			playOneRowOnly;			// Player will only play one row and not advance to the next row (used for milkytracker)
164 	bool			resetOnStopFlag;
165 	bool			resetMainVolumeOnStartPlayFlag;
166 
167 	mp_sint32		initialNumChannels;		// Fixed number of channels, can be set manually in StartPlaying
168 											// otherwise it will be module->header.channum
169 
170 	mp_sint32		mainVolume;				// current song's main volume
171 
172 	mp_sint32		tickSpeed;				// our tickspeed
173 	mp_sint32		baseBpm;				// Support digibooster REAL BPM value
174 	mp_sint32		bpm;					// BPM speed
175 	mp_sint32		ticker;					// runs from 0 to tickspeed-1
176 
177 	mp_sint32		rowcnt;					// counts through each row in a pattern
178 	mp_sint32		poscnt;					// counts through each index the pattern index table
179 	mp_int64		synccnt;				// will increment 250 times per mixed second
180 	mp_sint32		lastUnvisitedPos;		// the last order we visited before a new order has been set
181 
182 	mp_uint32		adder, BPMCounter;
183 
184 	mp_sint32		patternIndexToPlay;		// Play special pattern, -1 = Play entire song
185 
186 	mp_sint32		kick();
187 
allocateStructures()188 	virtual mp_sint32 allocateStructures() { return 0; }
189 
clearEffectMemory()190 	virtual void clearEffectMemory() { }
191 
192 public:
193 	PlayerBase(mp_uint32 frequency);
194 
195 	virtual ~PlayerBase();
196 
197 	virtual mp_sint32 adjustFrequency(mp_uint32 frequency);
198 	virtual mp_sint32 setBufferSize(mp_uint32 bufferSize);
199 
setPlayMode(PlayModes mode)200 	void setPlayMode(PlayModes mode) { playMode = mode; }
201 
getPlayMode()202 	PlayModes getPlayMode() const { return playMode; }
203 
enable(PlayModeOptions option,bool b)204 	void			enable(PlayModeOptions option, bool b)
205 	{
206 		ASSERT(option>=PlayModeOptionFirst && option<PlayModeOptionLast);
207 		options[option] = b;
208 	}
209 
isEnabled(PlayModeOptions option)210 	bool			isEnabled(PlayModeOptions option) const
211 	{
212 		ASSERT(option>=PlayModeOptionFirst && option<PlayModeOptionLast);
213 		return options[option];
214 	}
215 
216 	// runtime type identification
217 	virtual PlayerTypes getType() const = 0;
218 
219 	// virtual from mixer class, perform playing here
220 	virtual void timerHandler(mp_sint32 currentBeatPacket);
221 
222 	virtual void restart(mp_uint32 startPosition = 0,
223 						 mp_uint32 startRow = 0,
224 						 bool resetMixer = true,
225 						 const mp_ubyte* customPanningTable = NULL,
226 						 bool playOneRowOnly = false);
227 
reset()228 	virtual void reset() {}
229 
resetAllSpeed()230 	virtual void resetAllSpeed() {}
231 
232 	virtual mp_sint32   startPlaying(XModule* module,
233 									 bool repeat = false,
234 									 mp_uint32 startPosition = 0,
235 									 mp_uint32 startRow = 0,
236 									 mp_sint32 numChannels = -1,
237 									 const mp_ubyte* customPanningTable = NULL,
238 									 bool idle = false,
239 									 mp_sint32 patternIndex = -1,
240 									 bool playOneRowOnly = false);
241 
setPatternToPlay(mp_sint32 patternIndex)242 	void			setPatternToPlay(mp_sint32 patternIndex) { patternIndexToPlay = patternIndex; }
getPatternToPlay()243 	mp_sint32       getPatternToPlay() const { return patternIndexToPlay; }
244 
245 	mp_sint32		stopPlaying();
246 
hasSongHalted()247 	bool			hasSongHalted() const { return halted; }
248 
setIdle(bool idle)249 	void			setIdle(bool idle) { this->idle = idle; }
isIdle()250 	bool			isIdle() const { return idle; }
251 
setRepeat(bool repeat)252 	virtual void	setRepeat(bool repeat) { this->repeat = repeat; }
isRepeating()253 	bool			isRepeating() const { return repeat; }
254 
255 	mp_sint32		pausePlaying();
256 	mp_sint32		resumePlaying(bool unpause = true);
257 
isPaused()258 	bool			isPaused() const { return paused; }
259 
260 	// Set song main volume
setSongMainVolume(mp_ubyte volume)261 	void			setSongMainVolume(mp_ubyte volume) { mainVolume = volume; }
262 	mp_sint32		getSongMainVolume(mp_uint32 i = 0) const
263 	{
264 		return (timeRecord[i].mainVolumeTicker >> TimeRecord::BITPOS_MAINVOL) & 255;
265 	}
266 
267 	// Reset sound mixer on song stop
resetOnStop(bool b)268 	void			resetOnStop(bool b) { resetOnStopFlag = b; }
269 	// Reset main volume when song is started
resetMainVolumeOnStartPlay(bool b)270 	void			resetMainVolumeOnStartPlay(bool b) { resetMainVolumeOnStartPlayFlag = b; }
271 
272 	virtual mp_sint32		getOrder(mp_uint32 i = 0) const
273 	{
274 		return (timeRecord[i].posRowTempoSpeed >> TimeRecord::BITPOS_POS) & 255;
275 	}
276 
277 	virtual mp_sint32		getRow(mp_uint32 i = 0) const
278 	{
279 		return (timeRecord[i].posRowTempoSpeed >> TimeRecord::BITPOS_ROW) & 255;
280 	}
281 
282 	virtual void		getPosition(mp_sint32& order, mp_sint32& row, mp_uint32 i = 0) const
283 	{
284 		order = (timeRecord[i].posRowTempoSpeed >> TimeRecord::BITPOS_POS) & 255;
285 		row = (timeRecord[i].posRowTempoSpeed >> TimeRecord::BITPOS_ROW) & 255;
286 	}
287 
getLastUnvisitedPosition()288 	virtual mp_sint32	getLastUnvisitedPosition() const { return lastUnvisitedPos; }
289 
290 	virtual void		getPosition(mp_sint32& order, mp_sint32& row, mp_sint32& ticker, mp_uint32 i = 0) const
291 	{
292 		order = (timeRecord[i].posRowTempoSpeed >> TimeRecord::BITPOS_POS) & 255;
293 		row = (timeRecord[i].posRowTempoSpeed >> TimeRecord::BITPOS_ROW) & 255;
294 		ticker = (timeRecord[i].mainVolumeTicker >> TimeRecord::BITPOS_TICKER) & 255;
295 	}
296 
getSyncCount()297 	virtual mp_int64		getSyncCount() const { return synccnt; }
298 
299 	virtual void			nextPattern();
300 	virtual void			lastPattern();
301 	virtual void			setPatternPos(mp_uint32 pos, mp_uint32 row = 0, bool resetChannels = true, bool resetFXMemory = true);
302 
setTempo(mp_sint32 tempo)303 	virtual void			setTempo(mp_sint32 tempo)
304 	{
305 		bpm = tempo;
306 		updateTimeRecord();
307 	}
308 
setSpeed(mp_sint32 speed)309 	virtual void			setSpeed(mp_sint32 speed)
310 	{
311 		if (speed != tickSpeed)
312 		{
313 			tickSpeed = speed;
314 			if (ticker >= speed)
315 				ticker = 0;
316 			updateTimeRecord();
317 		}
318 	}
319 
320 	virtual mp_sint32		getTempo(mp_uint32 i = 0) const
321 	{
322 		if (isPlaying() && !isIdle())
323 		{
324 			return (timeRecord[i].posRowTempoSpeed >> TimeRecord::BITPOS_TEMPO) & 255;
325 		}
326 		return bpm;
327 	}
328 
329 	virtual mp_sint32		getSpeed(mp_uint32 i = 0) const
330 	{
331 		if (isPlaying() && !isIdle())
332 		{
333 			return (timeRecord[i].posRowTempoSpeed >> TimeRecord::BITPOS_SPEED) & 255;;
334 		}
335 		return tickSpeed;
336 	}
337 
grabChannelInfo(mp_sint32 chn,TPlayerChannelInfo & channelInfo)338 	virtual bool			grabChannelInfo(mp_sint32 chn, TPlayerChannelInfo& channelInfo) const
339 	{
340 		channelInfo.clear();
341 		return false;
342 	}
343 
344 	// milkytracker
345 	virtual void			playNote(mp_ubyte chn,
346 									 mp_sint32 note, mp_sint32 ins, mp_sint32 vol = -1) {}
347 
setPanning(mp_ubyte chn,mp_ubyte pan)348 	virtual void			setPanning(mp_ubyte chn, mp_ubyte pan) {}
349 };
350 
351 #endif
352