diff options
| -rw-r--r-- | sound/mods/module.cpp | 111 | ||||
| -rw-r--r-- | sound/mods/module.h | 6 | ||||
| -rw-r--r-- | sound/mods/protracker.cpp | 263 | 
3 files changed, 281 insertions, 99 deletions
| diff --git a/sound/mods/module.cpp b/sound/mods/module.cpp index 088522bc3a..fdd20cc01f 100644 --- a/sound/mods/module.cpp +++ b/sound/mods/module.cpp @@ -29,6 +29,88 @@  namespace Modules { +const int16 Module::periods[16][60] = { +	{1712, 1616, 1524, 1440, 1356, 1280, 1208, 1140, 1076, 1016, 960 , 906, +	 856 , 808 , 762 , 720 , 678 , 640 , 604 , 570 , 538 , 508 , 480 , 453, +	 428 , 404 , 381 , 360 , 339 , 320 , 302 , 285 , 269 , 254 , 240 , 226, +	 214 , 202 , 190 , 180 , 170 , 160 , 151 , 143 , 135 , 127 , 120 , 113, +	 107 , 101 , 95  , 90  , 85  , 80  , 75  , 71  , 67  , 63  , 60  , 56 }, +	{1700, 1604, 1514, 1430, 1348, 1274, 1202, 1134, 1070, 1010, 954 , 900, +	 850 , 802 , 757 , 715 , 674 , 637 , 601 , 567 , 535 , 505 , 477 , 450, +	 425 , 401 , 379 , 357 , 337 , 318 , 300 , 284 , 268 , 253 , 239 , 225, +	 213 , 201 , 189 , 179 , 169 , 159 , 150 , 142 , 134 , 126 , 119 , 113, +	 106 , 100 , 94  , 89  , 84  , 79  , 75  , 71  , 67  , 63  , 59  , 56 }, +	{1688, 1592, 1504, 1418, 1340, 1264, 1194, 1126, 1064, 1004, 948 , 894, +	 844 , 796 , 752 , 709 , 670 , 632 , 597 , 563 , 532 , 502 , 474 , 447, +	 422 , 398 , 376 , 355 , 335 , 316 , 298 , 282 , 266 , 251 , 237 , 224, +	 211 , 199 , 188 , 177 , 167 , 158 , 149 , 141 , 133 , 125 , 118 , 112, +	 105 , 99  , 94  , 88  , 83  , 79  , 74  , 70  , 66  , 62  , 59  , 56 }, +	{1676, 1582, 1492, 1408, 1330, 1256, 1184, 1118, 1056, 996 , 940 , 888, +	 838 , 791 , 746 , 704 , 665 , 628 , 592 , 559 , 528 , 498 , 470 , 444, +	 419 , 395 , 373 , 352 , 332 , 314 , 296 , 280 , 264 , 249 , 235 , 222, +	 209 , 198 , 187 , 176 , 166 , 157 , 148 , 140 , 132 , 125 , 118 , 111, +	 104 , 99  , 93  , 88  , 83  , 78  , 74  , 70  , 66  , 62  , 59  , 55 }, +	{1664, 1570, 1482, 1398, 1320, 1246, 1176, 1110, 1048, 990 , 934 , 882, +	 832 , 785 , 741 , 699 , 660 , 623 , 588 , 555 , 524 , 495 , 467 , 441, +	 416 , 392 , 370 , 350 , 330 , 312 , 294 , 278 , 262 , 247 , 233 , 220, +	 208 , 196 , 185 , 175 , 165 , 156 , 147 , 139 , 131 , 124 , 117 , 110, +	 104 , 98  , 92  , 87  , 82  , 78  , 73  , 69  , 65  , 62  , 58  , 55 }, +	{1652, 1558, 1472, 1388, 1310, 1238, 1168, 1102, 1040, 982 , 926 , 874, +	 826 , 779 , 736 , 694 , 655 , 619 , 584 , 551 , 520 , 491 , 463 , 437, +	 413 , 390 , 368 , 347 , 328 , 309 , 292 , 276 , 260 , 245 , 232 , 219, +	 206 , 195 , 184 , 174 , 164 , 155 , 146 , 138 , 130 , 123 , 116 , 109, +	 103 , 97  , 92  , 87  , 82  , 77  , 73  , 69  , 65  , 61  , 58  , 54 }, +	{1640, 1548, 1460, 1378, 1302, 1228, 1160, 1094, 1032, 974 , 920 , 868, +	 820 , 774 , 730 , 689 , 651 , 614 , 580 , 547 , 516 , 487 , 460 , 434, +	 410 , 387 , 365 , 345 , 325 , 307 , 290 , 274 , 258 , 244 , 230 , 217, +	 205 , 193 , 183 , 172 , 163 , 154 , 145 , 137 , 129 , 122 , 115 , 109, +	 102 , 96  , 91  , 86  , 81  , 77  , 72  , 68  , 64  , 61  , 57  , 54 }, +	{1628, 1536, 1450, 1368, 1292, 1220, 1150, 1086, 1026, 968 , 914 , 862, +	 814 , 768 , 725 , 684 , 646 , 610 , 575 , 543 , 513 , 484 , 457 , 431, +	 407 , 384 , 363 , 342 , 323 , 305 , 288 , 272 , 256 , 242 , 228 , 216, +	 204 , 192 , 181 , 171 , 161 , 152 , 144 , 136 , 128 , 121 , 114 , 108, +	 102 , 96  , 90  , 85  , 80  , 76  , 72  , 68  , 64  , 60  , 57  , 54 }, +	{1814, 1712, 1616, 1524, 1440, 1356, 1280, 1208, 1140, 1076, 1016, 960, +	 907 , 856 , 808 , 762 , 720 , 678 , 640 , 604 , 570 , 538 , 508 , 480, +	 453 , 428 , 404 , 381 , 360 , 339 , 320 , 302 , 285 , 269 , 254 , 240, +	 226 , 214 , 202 , 190 , 180 , 170 , 160 , 151 , 143 , 135 , 127 , 120, +	 113 , 107 , 101 , 95  , 90  , 85  , 80  , 75  , 71  , 67  , 63  , 60 }, +	{1800, 1700, 1604, 1514, 1430, 1350, 1272, 1202, 1134, 1070, 1010, 954, +	 900 , 850 , 802 , 757 , 715 , 675 , 636 , 601 , 567 , 535 , 505 , 477, +	 450 , 425 , 401 , 379 , 357 , 337 , 318 , 300 , 284 , 268 , 253 , 238, +	 225 , 212 , 200 , 189 , 179 , 169 , 159 , 150 , 142 , 134 , 126 , 119, +	 112 , 106 , 100 , 94  , 89  , 84  , 79  , 75  , 71  , 67  , 63  , 59 }, +	{1788, 1688, 1592, 1504, 1418, 1340, 1264, 1194, 1126, 1064, 1004, 948, +	 894 , 844 , 796 , 752 , 709 , 670 , 632 , 597 , 563 , 532 , 502 , 474, +	 447 , 422 , 398 , 376 , 355 , 335 , 316 , 298 , 282 , 266 , 251 , 237, +	 223 , 211 , 199 , 188 , 177 , 167 , 158 , 149 , 141 , 133 , 125 , 118, +	 111 , 105 , 99  , 94  , 88  , 83  , 79  , 74  , 70  , 66  , 62  , 59 }, +	{1774, 1676, 1582, 1492, 1408, 1330, 1256, 1184, 1118, 1056, 996 , 940, +	 887 , 838 , 791 , 746 , 704 , 665 , 628 , 592 , 559 , 528 , 498 , 470, +	 444 , 419 , 395 , 373 , 352 , 332 , 314 , 296 , 280 , 264 , 249 , 235, +	 222 , 209 , 198 , 187 , 176 , 166 , 157 , 148 , 140 , 132 , 125 , 118, +	 111 , 104 , 99  , 93  , 88  , 83  , 78  , 74  , 70  , 66  , 62  , 59 }, +	{1762, 1664, 1570, 1482, 1398, 1320, 1246, 1176, 1110, 1048, 988 , 934, +	 881 , 832 , 785 , 741 , 699 , 660 , 623 , 588 , 555 , 524 , 494 , 467, +	 441 , 416 , 392 , 370 , 350 , 330 , 312 , 294 , 278 , 262 , 247 , 233, +	 220 , 208 , 196 , 185 , 175 , 165 , 156 , 147 , 139 , 131 , 123 , 117, +	 110 , 104 , 98  , 92  , 87  , 82  , 78  , 73  , 69  , 65  , 61  , 58 }, +	{1750, 1652, 1558, 1472, 1388, 1310, 1238, 1168, 1102, 1040, 982 , 926, +	 875 , 826 , 779 , 736 , 694 , 655 , 619 , 584 , 551 , 520 , 491 , 463, +	 437 , 413 , 390 , 368 , 347 , 328 , 309 , 292 , 276 , 260 , 245 , 232, +	 219 , 206 , 195 , 184 , 174 , 164 , 155 , 146 , 138 , 130 , 123 , 116, +	 109 , 103 , 97  , 92  , 87  , 82  , 77  , 73  , 69  , 65  , 61  , 58 }, +	{1736, 1640, 1548, 1460, 1378, 1302, 1228, 1160, 1094, 1032, 974 , 920, +	 868 , 820 , 774 , 730 , 689 , 651 , 614 , 580 , 547 , 516 , 487 , 460, +	 434 , 410 , 387 , 365 , 345 , 325 , 307 , 290 , 274 , 258 , 244 , 230, +	 217 , 205 , 193 , 183 , 172 , 163 , 154 , 145 , 137 , 129 , 122 , 115, +	 108 , 102 , 96  , 91  , 86  , 81  , 77  , 72  , 68  , 64  , 61  , 57 }, +	{1724, 1628, 1536, 1450, 1368, 1292, 1220, 1150, 1086, 1026, 968 , 914, +	 862 , 814 , 768 , 725 , 684 , 646 , 610 , 575 , 543 , 513 , 484 , 457, +	 431 , 407 , 384 , 363 , 342 , 323 , 305 , 288 , 272 , 256 , 242 , 228, +	 216 , 203 , 192 , 181 , 171 , 161 , 152 , 144 , 136 , 128 , 121 , 114, +	 108 , 101 , 96  , 90  , 85  , 80  , 76  , 72  , 68  , 64  , 60  , 57 }}; +  bool Module::load(Common::ReadStream &st) {  	st.read(songname, 20);  	songname[20] = '\0'; @@ -70,6 +152,7 @@ bool Module::load(Common::ReadStream &st) {  				pattern[i][j][k].sample = (note & 0xf0000000) >> 24 | (note & 0x0000f000) >> 12;  				pattern[i][j][k].period = (note >> 16) & 0xfff;  				pattern[i][j][k].effect = note & 0xfff; +				pattern[i][j][k].note = periodToNote((note >> 16) & 0xfff);  			}  		}  	} @@ -103,4 +186,32 @@ Module::~Module() {  	}  } +byte Module::periodToNote(int16 period, byte finetune) { +	int16 diff1; +	int16 diff2; + +	diff1 = ABS(periods[finetune][0] - period); +	if (diff1 == 0) +		return 0; + +	for (int i = 1; i < 60; i++) { +		diff2 = ABS(periods[finetune][i] - period); +		if (diff2 == 0) +			return i; +		else if (diff2 > diff1) +			return i-1; +		diff1 = diff2; +	} +	return 59; +} + +int16 Module::noteToPeriod(byte note, byte finetune) { +	if (finetune > 15) +		finetune = 15; +	if (note > 59) +		note = 59; + +	return periods[finetune][note]; +} +  } // End of namespace Modules diff --git a/sound/mods/module.h b/sound/mods/module.h index b62a8f47cb..c302d490b1 100644 --- a/sound/mods/module.h +++ b/sound/mods/module.h @@ -32,6 +32,7 @@ namespace Modules {  struct note_t {  	byte sample; +	byte note;  	uint16 period;  	uint16 effect;  }; @@ -67,6 +68,11 @@ public:  	~Module();  	bool load(Common::ReadStream &stream); +	byte static periodToNote(int16 period, byte finetune = 0); +	int16 static noteToPeriod(byte note, byte finetune = 0); + +private: +	static const int16 periods[16][60];  };  } // End of namespace Modules diff --git a/sound/mods/protracker.cpp b/sound/mods/protracker.cpp index 31f2264f8a..0274551c7a 100644 --- a/sound/mods/protracker.cpp +++ b/sound/mods/protracker.cpp @@ -41,8 +41,6 @@ private:  	int _row;  	int _pos; -	int _patternDelay; -  	int _speed;  	int _bpm; @@ -59,12 +57,22 @@ private:  	int _patternLoopCount;  	int _patternLoopRow; +	// For effect 0xEE - Pattern Delay +	byte _patternDelay; + +	static const int16 sinetable[]; +  	struct {  		byte sample;  		uint16 period;  		double offset;  		byte vol; +		byte finetune; + +		// For effect 0x0 - Arpeggio +		bool arpeggio; +		byte arpeggioNotes[3];  		// For effect 0x3 - Porta to note  		uint16 portaToNote; @@ -75,6 +83,10 @@ private:  		byte vibratoPos;  		byte vibratoSpeed;  		byte vibratoDepth; + +		// For effect 0xED - Delay sample +		byte delaySample; +		byte delaySampleTick;  	} _track[4];  public: @@ -84,11 +96,56 @@ private:  	void startPlay() { _playing = true; _end = false; }  	void interrupt(); +	void doPorta(int track) { +		if (_track[track].portaToNote && _track[track].portaToNoteSpeed) { +			if (_track[track].period < _track[track].portaToNote) { +				_track[track].period += _track[track].portaToNoteSpeed; +				if (_track[track].period > _track[track].portaToNote) +					_track[track].period = _track[track].portaToNote; +			} else if (_track[track].period > _track[track].portaToNote) { +				_track[track].period -= _track[track].portaToNoteSpeed; +				if (_track[track].period < _track[track].portaToNote) +					_track[track].period = _track[track].portaToNote; +			} +		} +	} +	void doVibrato(int track) { +		_track[track].vibrato = +				(_track[track].vibratoDepth * sinetable[_track[track].vibratoPos]) / 128; +		_track[track].vibratoPos += _track[track].vibratoSpeed; +		_track[track].vibratoPos %= 64; +	} +	void doVolSlide(int track, byte ex, byte ey) { +		int vol = _track[track].vol; +		if (ex == 0) +			vol -= ey; +		else if (ey == 0) +			vol += ex; + +		if (vol < 0) +			vol = 0; +		else if (vol > 64) +			vol = 64; + +		_track[track].vol = vol; +	} +  	void updateRow();  	void updateEffects();  }; +const int16 ProtrackerStream::sinetable[64] = { +		 0,   24,   49,   74,   97,  120,  141,  161, +	 180,  197,  212,  224,  235,  244,  250,  253, +	 255,  253,  250,  244,  235,  224,  212,  197, +	 180,  161,  141,  120,   97,   74,   49,   24, +		 0,  -24,  -49,  -74,  -97, -120, -141, -161, +	-180, -197, -212, -224, -235, -244, -250, -253, +	-255, -253, -250, -244, -235, -224, -212, -197, +	-180, -161, -141, -120,  -97,  -74,  -49,  -24 +}; +  ProtrackerStream::ProtrackerStream(Common::ReadStream *stream, int rate, bool stereo) :  		Paula(stereo, rate, rate/50) {  	_module = new Module(); @@ -111,6 +168,8 @@ ProtrackerStream::ProtrackerStream(Common::ReadStream *stream, int rate, bool st  		_track[t].period = 0;  		_track[t].offset = 0.0;  		_track[t].vibrato = 0; +		_track[t].delaySampleTick = 0; +		_track[t].arpeggio = false;  	}	  	startPlay(); @@ -118,7 +177,9 @@ ProtrackerStream::ProtrackerStream(Common::ReadStream *stream, int rate, bool st  void ProtrackerStream::updateRow() {  	for (int track = 0; track < 4; track++) { +		_track[track].arpeggio = false;  		_track[track].vibrato = 0; +		_track[track].delaySampleTick = 0;  		note_t note =  		    _module->pattern[_module->songpos[_pos]][_row][track]; @@ -129,21 +190,36 @@ void ProtrackerStream::updateRow() {  				_track[track].vibratoPos = 0;  			}  			_track[track].sample = note.sample; +			_track[track].finetune = _module->sample[note.sample - 1].finetune;  			_track[track].vol = _module->sample[note.sample - 1].vol;  		} +  		if (note.period) {  			if (effect != 3 && effect != 5) { -				_track[track].period = note.period; +				if (_track[track].finetune) +					_track[track].period = _module->noteToPeriod(note.note, _track[track].finetune); +				else +					_track[track].period = note.period;  				_track[track].offset = 0.0;  			}  		}  		int exy = note.effect & 0xff; +		if (exy);  		int ex = (note.effect >> 4) & 0xf;  		int ey = note.effect & 0xf; +		int vol;  		switch (effect) {  		case 0x0: +			if (ex || ey) { +				_track[track].arpeggio = true; +				if (note.period) { +					_track[track].arpeggioNotes[0] = note.note; +					_track[track].arpeggioNotes[1] = note.note + ex; +					_track[track].arpeggioNotes[2] = note.note + ey; +				} +			}  			break;  		case 0x1:  			break; @@ -152,7 +228,6 @@ void ProtrackerStream::updateRow() {  		case 0x3:  			if (note.period)  				_track[track].portaToNote = note.period; -  			if (exy)  				_track[track].portaToNoteSpeed = exy;  			break; @@ -163,6 +238,18 @@ void ProtrackerStream::updateRow() {  			}  			break;  		case 0x5: +			doPorta(track); +			doVolSlide(track, ex, ey); +			break; +		case 0x6: +			doVibrato(track); +			doVolSlide(track, ex, ey); +			break; +		case 0x9: // Set sample offset +			if (exy) { +				_track[track].offset = exy * 256; +				_voice[track].offset = _track[track].offset; +			}  			break;  		case 0xA:  			break; @@ -180,6 +267,24 @@ void ProtrackerStream::updateRow() {  		case 0xE:  			switch (ex) { +			case 0x0: // Switch filters off +				break; +			case 0x1: // Fine slide up +				_track[track].period -= exy; +				break; +			case 0x2: // Fine slide down +				_track[track].period += exy; +				break; +			case 0x5: // Set finetune +				_track[track].finetune = ey; +				_module->sample[_track[track].sample].finetune = ey; +				if (note.period) { +					if (ey) +						_track[track].period = _module->noteToPeriod(note.note, ey); +					else +						_track[track].period = note.period; +				} +				break;  			case 0x6:  				if (ey == 0) {  					_patternLoopRow = _row; @@ -193,6 +298,27 @@ void ProtrackerStream::updateRow() {  				break;  			case 0x9:  				break;	// Retrigger note +			case 0xA: // Fine volume slide up +				vol = _track[track].vol + ey; +				if (vol > 64) +					vol = 64; +				_track[track].vol = vol; +				break; +			case 0xB: // Fine volume slide down +				vol = _track[track].vol - ey; +				if (vol < 0) +					vol = 0; +				_track[track].vol = vol; +				break; +			case 0xD: // Delay sample +				_track[track].delaySampleTick = ey; +				_track[track].delaySample = _track[track].sample; +				_track[track].sample = 0; +				_track[track].vol = 0; +				break; +			case 0xE: // Pattern delay +				_patternDelay = ey; +				break;  			default:  				warning("Unimplemented effect %X\n", note.effect);  			} @@ -213,18 +339,6 @@ void ProtrackerStream::updateRow() {  }  void ProtrackerStream::updateEffects() { - -	static const int16 sinetable[64] = { -		   0,   24,   49,   74,   97,  120,  141,  161, -		 180,  197,  212,  224,  235,  244,  250,  253, -		 255,  253,  250,  244,  235,  224,  212,  197, -		 180,  161,  141,  120,   97,   74,   49,   24, -		   0,  -24,  -49,  -74,  -97, -120, -141, -161, -		-180, -197, -212, -224, -235, -244, -250, -253, -		-255, -253, -250, -244, -235, -224, -212, -197, -		-180, -161, -141, -120,  -97,  -74,  -49,  -24 -	}; -  	for (int track = 0; track < 4; track++) {  		_track[track].vibrato = 0; @@ -237,9 +351,18 @@ void ProtrackerStream::updateEffects() {  		int ex = (note.effect >> 4) & 0xf;  		int ey = (note.effect) & 0xf; -		int vol;  		switch (effect) {  		case 0x0: +			if (ex || ey) { +				if (_tick == 1) +					_track[track].period = +						_module->noteToPeriod(_track[track].arpeggioNotes[0], +								_track[track].finetune); +				else +					_track[track].period = +						_module->noteToPeriod(_track[track].arpeggioNotes[_tick % 3], +								_track[track].finetune); +			}  			break;  		case 0x1:  			_track[track].period -= exy; @@ -248,89 +371,21 @@ void ProtrackerStream::updateEffects() {  			_track[track].period += exy;  			break;  		case 0x3: -			if (_track[track].portaToNote && _track[track].portaToNoteSpeed) { -				if (_track[track].period < _track[track].portaToNote) { -					_track[track].period += _track[track].portaToNoteSpeed; -					if (_track[track].period > _track[track].portaToNote) -						_track[track].period = _track[track].portaToNote; -				} else if (_track[track].period > _track[track].portaToNote) { -					_track[track].period -= _track[track].portaToNoteSpeed; -					if (_track[track].period < _track[track].portaToNote) -						_track[track].period = _track[track].portaToNote; -				} -			} +			doPorta(track);  			break;  		case 0x4: -			_track[track].vibrato = -			    (_track[track].vibratoDepth * sinetable[_track[track].vibratoPos]) / 128; -			_track[track].vibratoPos += _track[track].vibratoSpeed; -			_track[track].vibratoPos %= 64; +			doVibrato(track);  			break; -  		case 0x5: -			if (_track[track].portaToNote -			    && _track[track].portaToNoteSpeed) { -				if (_track[track].period < _track[track].portaToNote) { -					_track[track].period += _track[track].portaToNoteSpeed; -					if (_track[track].period > _track[track].portaToNote) -						_track[track].period = _track[track].portaToNote; -				} else if (_track[track].period > _track[track].portaToNote) { -					_track[track].period -= _track[track].portaToNoteSpeed; -					if (_track[track].period < _track[track].portaToNote) -						_track[track].period = _track[track].portaToNote; -				} -			} - -			vol = _track[track].vol; -			if (ex == 0) -				vol -= ey; -			else if (ey == 0) -				vol += ex; - -			if (vol < 0) -				vol = 0; -			else if (vol > 64) -				vol = 64; - -			_track[track].vol = vol; - +			doPorta(track); +			doVolSlide(track, ex, ey);  			break; -  		case 0x6: -			_track[track].vibrato = -			    (_track[track].vibratoDepth * sinetable[_track[track].vibratoPos]) / 128; -			_track[track].vibratoPos += _track[track].vibratoSpeed; -			_track[track].vibratoPos %= 64; - -			vol = _track[track].vol; -			if (ex == 0) -				vol -= ey; -			else if (ey == 0) -				vol += ex; - -			if (vol < 0) -				vol = 0; -			else if (vol > 64) -				vol = 64; - -			_track[track].vol = vol; - +			doVibrato(track); +			doVolSlide(track, ex, ey);  			break; -  		case 0xA: -			vol = _track[track].vol; -			if (ex == 0) -				vol -= ey; -			else if (ey == 0) -				vol += ex; - -			if (vol < 0) -				vol = 0; -			else if (vol > 64) -				vol = 64; - -			_track[track].vol = vol; - +			doVolSlide(track, ex, ey);  			break;  		case 0xE: @@ -341,6 +396,14 @@ void ProtrackerStream::updateEffects() {  				if (ey && _tick % ey == 0)  					_track[track].offset = 0.0;  				break; +			case 0xD: // Delay sample +				if (_tick == _track[track].delaySampleTick) { +					_track[track].sample = _track[track].delaySample; +					_track[track].offset = 0.0; +					if (_track[track].sample) +						_track[track].vol = _module->sample[_track[track].sample - 1].vol; +				} +				break;  			}  			break;  		} @@ -354,6 +417,10 @@ void ProtrackerStream::interrupt(void) {  		_track[track].offset = _voice[track].offset;  	if (_tick == 0) { +		if (_track[track].arpeggio) { +			_track[track].period = _module->noteToPeriod(_track[track].arpeggioNotes[0], +					_track[track].finetune); +		}  		if (_hasJumpToPattern) {  			_hasJumpToPattern = false;  			_pos = _jumpToPattern; @@ -373,17 +440,15 @@ void ProtrackerStream::interrupt(void) {  			_patternLoopRow = 0;  		} -		if (_patternDelay == 0) { -			updateRow(); -		} else { -			_patternDelay--; -		} +		updateRow();  	} else  		updateEffects(); -	_tick = (_tick + 1) % _speed; -	if (_tick == 0) +	_tick = (_tick + 1) % (_speed + _patternDelay * _speed); +	if (_tick == 0) {  		_row++; +		_patternDelay = 0; +	}  	for (track = 0; track < 4; track++) {  		_voice[track].period = _track[track].period + _track[track].vibrato; | 
