1 /**
  2  * This file is part of the Web Enabled Audio and Sound Enhancement Library (aka the Weasel audio library) Copyright 2011 - 2013 Warren Willmey. It is covered by the GNU General Public License version 3 as published by the Free Software Foundation, you should have received a copy of the GNU General Public License along with this program. If not, see <http://www.gnu.org/licenses/>.
  3  */
  4 if( undefined == window.weasel ) window.weasel = {};
  5 
  6 // ---------------------------------------------------------------------------
  7 /** Create a Ultimate Soundtracker 1.21 module out of the provided data (which has already passed the module sniffer test).
  8  * 
  9  * @constructor
 10  * @param {Array|Uint8Array} aModuleData = The ultimate Soundtracker 1.21 module as a byte array that MUST have passed the module sniffer test.
 11  * @param {int} iPlaybackFrequency = The playback frequency in hertz to use (e.g. 44100 ).
 12  * @param {weasel.Sample.prototype.SampleScannerMode} iSampleScannerMode = Scan for IFF Header corruption residue?.
 13  * 
 14  * @author Warren Willmey 2011
 15  */
 16 weasel.UltimateSoundTracker121 = function( aModuleData, iPlaybackFrequency, iSampleScannerMode )
 17 {
 18 	// Needed for prototype Inheritance.
 19 	if( aModuleData === undefined || !(( aModuleData instanceof Array ) || ( window.Uint8Array && aModuleData instanceof Uint8Array )) )
 20 		return;
 21 
 22 	this.sModuleType = weasel.ModuleSniffer.prototype.SupportedModules.UltimateSoundTracker121;
 23 	this.aModuleData = aModuleData;
 24 	this.aSequenceTable = this.getSequenceTable();
 25 	this.iMaxPattern = 0;
 26 	this.iSongRestartSequence = 0;		// Ultimate Soundtracker always restarts its song at pattern 0. This is same for all other Soundtrackers with the exception of the Noisetracker series and its derivatives.
 27 	this.bUsePALClockConstant = true;
 28 	this.iPlaybackFrequency = iPlaybackFrequency;
 29 	this.fMasterVolume = 1.0;
 30 	this.bFilterOn = false;
 31 	this.iSongSpeed = 0;
 32 	this.bPatternBreak = false;			// DOC Soundtracker 2.0 & 2.2 "Pattern Break" Effect Command.
 33 	this.iSequencePositionJump = -1;	// DOC Soundtracker 2.0 & 2.2 "Sequence Position Jump" Effect Command.
 34 	this.iPatternBreakToRow = 0;		// Protracker's Pattern Break changes to a provided row number.
 35 	this.bNoisetracker20VibratoMode = false;	// Noisetracker 1.1 and Noisetracker 2.0 have a different Vibrato divider.
 36 	this.iRowDelay = 0;							// Protracker Pattern Row Delay command.
 37 	this.bProtrackerTremoloSawtoothBug = true;	// Protracker Tremolo has a bug when using the sawtooth waveform, turning off makes it behave as it was intended.
 38 	this.bProtrackerRowDelayQuirk = false;		// Protracker Quirk, Row Delay command (EEx) and Pattern Break (Dxx) causes row skip, so Pattern Breaks to Dxx +1.
 39 	this.fWaitForDMAMultiplier = 1.0;	// Protracker ED0 command causes software delays, some 6000 clock cycles for each ED0.
 40 	this.bProtracker3SampleOffsetMode = false;		// Set Sample Offset command (9xx) behaves differently in Protracker 3, they removed some of the Quirks.
 41 	this.iTimingOverride = this.TimingOverrides.PAL;
 42 
 43 
 44 	this.aInstruments = new Array( this.FormatInstrumentTotal() );
 45 
 46 	this.bSampleLoopOffsetInWords = undefined == this.bSampleLoopOffsetInWords ? false : this.bSampleLoopOffsetInWords;
 47 	this.bNoiseTrackerLoopQuirk   = undefined == this.bNoiseTrackerLoopQuirk   ? false : this.bNoiseTrackerLoopQuirk;
 48 	this.bUseFineTuning           = undefined == this.bUseFineTuning           ? false : this.bUseFineTuning;
 49 	this.bFSTClearSampleOffsetAfterUse = undefined == this.bFSTClearSampleOffsetAfterUse ? false : this.bFSTClearSampleOffsetAfterUse;
 50 
 51 	for( var iInstrumentCount = 0, iTotalInstruments = this.aInstruments.length; iInstrumentCount <= iTotalInstruments; iInstrumentCount++ )
 52 	{
 53 		this.aInstruments[ iInstrumentCount ] = new weasel.Instrument( this, iInstrumentCount, iSampleScannerMode, this.bSampleLoopOffsetInWords, this.bNoiseTrackerLoopQuirk, this.bUseFineTuning );
 54 	}
 55 
 56 	this.aPatterns = new Array( this.numberOfUniquePatternsInSong() );
 57 	this.extractPatterns();
 58 
 59 	this._extractSongSpeed();
 60 
 61 
 62 	var iNumberOfChannels = this.getNumberOfChannels();
 63 	this.aSoundChannels = new Array( this.getNumberOfChannels() );
 64 	for( var iChannel = this.aSoundChannels.length; --iChannel >= 0; )
 65 		this.aSoundChannels[ iChannel ] = new weasel.Channel( this, iPlaybackFrequency, 1.0 );
 66 	this.bStartProcessingPatterns = false;								// Force Module to fetch row 0 of first pattern, or instruments wont play.
 67 	this.iCurrentSequencePosition = 0;
 68 	this.iCurrentTick = 0;
 69 	this.iTickSpeed = weasel.FormatUltimateSoundTracker121.TicksPerRow;	// Ultimate Soundtracker modules do NOT change their tick speed.
 70 	this.iTotalPatternTicks = 0;
 71 	this.iCurrentPatternRowPosition = 0;
 72 	this.iSamplesPerTick = 0;
 73 	this.setSamplesPerTick();
 74 	this.iSamplesRemaining = this.iSamplesPerTick;
 75 
 76 	this.bSongEnded = false;
 77 	this.b7BitPanning = false;
 78 
 79 	// Try to reduce heap activity with pre-allocation of Filter state changes
 80 	// during play back. Its unlikely more that 4 elements could be used 
 81 	// (BPM speed of 220 (292Hz) and Tick speed of 2).
 82 	//
 83 	this.aFilterStateChange = [ 0|0, 0|0, 0|0, 0|0 ];
 84 	this.aFilterStateForMixer = [ false, false, false, false ];
 85 };
 86 
 87 // ---------------------------------------------------------------------------
 88 /** Override the default song speed and use the VBL timer instead.
 89  * @const
 90  * @enum {int}
 91  */
 92 weasel.UltimateSoundTracker121.prototype.TimingOverrides =	{ 
 93 	  UseBPM: 0
 94 	, PAL	: 1
 95 	, NTSC	: 2 };
 96 
 97 // ---------------------------------------------------------------------------
 98 /** PAL vertical refresh rate in Hertz.
 99  * @const
100  * @type {float}
101  */
102 weasel.UltimateSoundTracker121.prototype.PAL = 50.0;
103 
104 // ---------------------------------------------------------------------------
105 /** NTSC vertical refresh rate in Hertz.
106  * @const
107  * @type {float}
108  */
109 weasel.UltimateSoundTracker121.prototype.NTSC = 60.0 / 1.001;
110 
111 // ---------------------------------------------------------------------------
112 /** Get the number of instruments supported by the module format.
113  * 
114  * @return {int} The total number of instruments this format supports (15 for Ultimate Soundtracker etc).
115  */
116 weasel.UltimateSoundTracker121.prototype.FormatInstrumentTotal = function( )
117 {
118 	return weasel.FormatUltimateSoundTracker121.NumberOfInstruments;
119 };
120 
121 // ---------------------------------------------------------------------------
122 /** Get the Module Header Size used by the module format, it is also the starting offset for the pattern data.
123  * 
124  * @return {int} the Module Header Size used by the module format, it is also the starting offset for the pattern data.
125  */
126 weasel.UltimateSoundTracker121.prototype.FormatModuleHeaderSize = function( )
127 {
128 	return weasel.FormatUltimateSoundTracker121.ModuleHeaderSize;
129 };
130 
131 // ---------------------------------------------------------------------------
132 /** Get the number of channels in this module.
133  * 
134  * @return {int} The number of channels in this module.
135  */
136 weasel.UltimateSoundTracker121.prototype.getNumberOfChannels = function( )
137 {
138 	return weasel.FormatUltimateSoundTracker121.NumberOfChannels;
139 };
140 
141 
142 // ---------------------------------------------------------------------------
143 /** Get the pattern size in bytes of this module.
144  * 
145  * @return {int} The size of each pattern in bytes for this module type.
146  */
147 weasel.UltimateSoundTracker121.prototype.getPatternSizeInBytes = function( )
148 {
149 	return weasel.FormatUltimateSoundTracker121.PatternSize;
150 };
151 
152 // ---------------------------------------------------------------------------
153 /** Set the overriding master volume for this module.
154  * 
155  * @param {float} fVolume = The volume range ( 0.0 - 1.0 ) although a range of 
156  * 0.0 to 2.0 is accepted, the volume levels may not be clamped by the mixer causing clicks/pops etc.
157  */
158 weasel.UltimateSoundTracker121.prototype.setMasterVolume = function( fVolume )
159 {
160 	if( fVolume < 0 )
161 	{
162 		fVolume = 0;
163 	}
164 	
165 	if( fVolume > 2 )
166 	{
167 		fVolume = 2;
168 	}
169 
170 	this.fMasterVolume = fVolume;
171 	
172 	
173 	for( var iChannel = this.aSoundChannels.length; --iChannel >= 0; )
174 	{
175 		this.aSoundChannels[ iChannel ].setMasterVolume( fVolume );
176 	}
177 };
178 
179 // ---------------------------------------------------------------------------
180 /** Get the current master volume of the module.
181  * 
182  * @return {float} The current master volume.
183  */
184 weasel.UltimateSoundTracker121.prototype.getMasterVolume = function( )
185 {
186 	return this.fMasterVolume;
187 };
188 
189 // ---------------------------------------------------------------------------
190 /**
191  * Set the number of samples needed between each song "tick" is processed (when new note data & sound fx are process etc),
192  * the current samples remaining does not change, until the tick occurs.
193  * 
194  */
195 weasel.UltimateSoundTracker121.prototype.setSamplesPerTick = function( )
196 {
197 	this.iSamplesPerTick = ( this.iPlaybackFrequency / this.tickPlaybackRateInHz() ) | 0;
198 };
199 
200 // ---------------------------------------------------------------------------
201 /**
202  * Set new output play back frequency, which requires the resizing of various buffers etc.
203  * 
204  * @param {int} iPlaybackFrequency = new replay play back frequency.
205  * 
206  */
207 weasel.UltimateSoundTracker121.prototype.changePlaybackFrequency = function( iPlaybackFrequency )
208 {
209 	this.iPlaybackFrequency = iPlaybackFrequency;
210 	
211 	this.setSamplesPerTick();
212 
213 	for( var iChannel = this.aSoundChannels.length; --iChannel >= 0; )
214 		this.aSoundChannels[ iChannel ].changePlaybackFrequency( iPlaybackFrequency );
215 };
216 
217 // ---------------------------------------------------------------------------
218 /** Get the current moduel playback frequency.
219  * 
220  * @return {int} The current module playback frequency.
221  */
222 weasel.UltimateSoundTracker121.prototype.getPlaybackFrequency = function( )
223 {
224 	return this.iPlaybackFrequency;
225 };
226 
227 // ---------------------------------------------------------------------------
228 /**
229  * Get the name of the Ultimate Soundtracker 1.21 module as a String.
230  * 
231  * @return {String} = Name of Sound Tracker Module, always character escape this string before display.
232  */
233 weasel.UltimateSoundTracker121.prototype.getTitle = function( )
234 {
235 	var sTitle = '';
236 	
237 	try
238 	{
239 		// The first 20 bytes are the module title, which is padded with NULLs.
240 		//
241 		sTitle = weasel.Helper.getNullTerminatedString( this.aModuleData, weasel.FormatUltimateSoundTracker121.Title, weasel.FormatUltimateSoundTracker121.MaxTitleLength );
242 
243 	}catch( oException )
244 	{
245 	}
246 
247 	return sTitle;
248 };
249 
250 // ---------------------------------------------------------------------------
251 /**
252  * Get the length of the Ultimate Soundtracker 1.21 module in patterns.
253  * 
254  * @return {int} = Length of song in patterns.
255  */
256 weasel.UltimateSoundTracker121.prototype.getSongLengthInPatterns = function()
257 {
258 	try
259 	{
260 		return weasel.Helper.getByte( this.aModuleData, weasel.FormatUltimateSoundTracker121.SongLength );
261 	}catch ( oException )
262 	{
263 		return 0;
264 	}
265 };
266 
267 // ---------------------------------------------------------------------------
268 /**
269  * Get the song speed of the Ultimate Soundtracker 1.21 module.
270  * 
271  * @return {int} = Song speed from module, which is in BPM (although its really an approximation, to get an accurate BPM use: (((tickPlaybackRateInHz() * 60Seconds) / 6TicksPerRow) / 4RowsPerQuarterNote) ).
272  */
273 weasel.UltimateSoundTracker121.prototype.getSongSpeed = function()
274 {
275 	return this.iSongSpeed;
276 };
277 
278 // ---------------------------------------------------------------------------
279 /**
280  * Set the song speed.
281  * 
282  * @param {int} iModuleBPMSpeed = The song speed as set in the module file (BPM) [0-220] which is the limits set down in Ultimate Soundtracker 1.8.
283  */
284 weasel.UltimateSoundTracker121.prototype.setSongSpeed = function( iModuleBPMSpeed )
285 {
286 	this.iSongSpeed = iModuleBPMSpeed > 220 ? 220 : iModuleBPMSpeed < 0 ? 0 : iModuleBPMSpeed;
287 	this.setSamplesPerTick();
288 };
289 
290 // ---------------------------------------------------------------------------
291 /**
292  * Get the maximum pattern number in Ultimate Soundtracker 1.21 module (this includes patterns not played).
293  * 
294  * @return {int} = Maximum Pattern number.
295  */
296 weasel.UltimateSoundTracker121.prototype.findMaximumPatternNumber = function()
297 {
298 	if( 0 != this.iMaxPattern )
299 		return this.iMaxPattern;
300 
301 	try
302 	{
303 		for( var iSequenceNumber = 0, iNumberOfPatterns = weasel.FormatUltimateSoundTracker121.SequenceTableLength; iSequenceNumber < iNumberOfPatterns; iSequenceNumber++ )
304 		{
305 			var iPatternNumber = this.getPatternNumber( iSequenceNumber );
306 
307 			if( iPatternNumber > this.iMaxPattern )
308 			{
309 				this.iMaxPattern = iPatternNumber;
310 			}
311 		}
312 	}catch (oException ) {}
313 
314 	return this.iMaxPattern;
315 };
316 
317 // ---------------------------------------------------------------------------
318 /**
319  * Get the pattern sequence table in Ultimate Soundtracker 1.21 module.
320  * 
321  * @return {Array} = The Pattern Sequence Table.
322  */
323 weasel.UltimateSoundTracker121.prototype.getSequenceTable = function()
324 {
325 	if( null != this.aSequenceTable )
326 		return this.aSequenceTable;
327 
328 	var aPatternSequenceTable = new Array( weasel.FormatUltimateSoundTracker121.SequenceTableLength );
329 
330 	for( var iLength = aPatternSequenceTable.length; --iLength >= 0; )
331 		aPatternSequenceTable[ iLength ] = 0;
332 
333 		for( var iPatternSequenceTable = weasel.FormatUltimateSoundTracker121.PatternSequenceTable, iNumberOfPatterns = weasel.FormatUltimateSoundTracker121.SequenceTableLength, iPattern = 0; iPattern <  iNumberOfPatterns; iPattern++ )
334 		{
335 			try
336 			{
337 				var iPatternNumber = weasel.Helper.getByte( this.aModuleData, iPatternSequenceTable++ );
338 
339 				aPatternSequenceTable[ iPattern ] = iPatternNumber;
340 
341 			}catch (oException ) {}
342 		}
343 
344 	return aPatternSequenceTable;
345 };
346 
347 
348 // ---------------------------------------------------------------------------
349 /**
350  * Get the number of unique patterns in the Ultimate Soundtracker 1.21 module.
351  * 
352  * @return {int} = Number of unique number.
353  */
354 weasel.UltimateSoundTracker121.prototype.numberOfUniquePatternsInSong = function()
355 {
356 	var iMaxPatternNumber = this.findMaximumPatternNumber();
357 
358 	return iMaxPatternNumber +1;
359 };
360 
361 // ---------------------------------------------------------------------------
362 /**
363  * Get the instrument object.
364  * 
365  * @param {int} iInstrumentNumber = The instrument number (Range 1-15).
366  * 
367  * @return {weasel.Instrument} = The instrument object, or null if not found (instrument number out of range).
368  */
369 weasel.UltimateSoundTracker121.prototype.getInstrument = function( iInstrumentNumber )
370 {
371 	if( iInstrumentNumber >= 0 && iInstrumentNumber <= this.FormatInstrumentTotal() )
372 		return this.aInstruments[ iInstrumentNumber ];
373 
374 	return null;
375 };
376 
377 // ---------------------------------------------------------------------------
378 /**
379  * Get the Module Data.
380  * 
381  * @return {Array|Uint8Array} = Array containing the original module data.
382  */
383 weasel.UltimateSoundTracker121.prototype.getModuleData = function()
384 {
385 	return this.aModuleData;
386 };
387 
388 
389 // ---------------------------------------------------------------------------
390 /**
391  * Get the note and octave from the period value (which is stored in the pattern).
392  * 
393  * @param {int} iAmigaPeriod = The Period value of the note.
394  * 
395  * @return {String} = The note and octave (in the format note octave e.g. 'C-2' or 'G#1'), or '???' if not found.
396  * 
397  * @TODO Should we be returning the nearest note if not found??
398  */
399 weasel.UltimateSoundTracker121.prototype.getNoteFromPeriod = function( iAmigaPeriod )
400 {
401 	if( iAmigaPeriod < 113 || iAmigaPeriod > 856 )
402 		return '???';
403 
404 	if( iAmigaPeriod in weasel.FormatUltimateSoundTracker121.NoteTable )
405 	{
406 		return weasel.FormatUltimateSoundTracker121.NoteTable[ iAmigaPeriod ];
407 	}
408 
409 	// Should we be returning the nearest note if not found??
410 	//
411 	return '???';
412 };
413 
414 // ---------------------------------------------------------------------------
415 /**
416  * Get the pattern number from the sequence table at the given offset.
417  * 
418  * @param {int} iSequenceNumber = The offset into the sequence table of the wanted pattern number.
419  * 
420  * @return {int} = Pattern number.
421  */
422 weasel.UltimateSoundTracker121.prototype.getPatternNumber = function( iSequenceNumber )
423 {
424 	if( iSequenceNumber < 0 || iSequenceNumber >= weasel.FormatUltimateSoundTracker121.SequenceTableLength )
425 		return 0;
426 	
427 	var iPatternNumber = 0;
428 
429 	try
430 	{
431 		iPatternNumber = this.aSequenceTable[ iSequenceNumber ];
432 	}catch (oException )
433 	{}
434 
435 	return iPatternNumber;
436 };
437 
438 // ---------------------------------------------------------------------------
439 /**
440  * Extract all patterns within module data into Pattern objects.
441  */
442 weasel.UltimateSoundTracker121.prototype.extractPatterns = function( )
443 {
444 	{
445 		for( var iPattern = 0, iMaxPattern = this.numberOfUniquePatternsInSong(); iPattern < iMaxPattern; iPattern++ )
446 		{
447 			this.aPatterns[ iPattern ] = new weasel.Pattern( this, iPattern );
448 		}
449 	}
450 };
451 
452 // ---------------------------------------------------------------------------
453 /**
454  * Extract song speed from module.
455  * 
456  * @protected
457  */
458 weasel.UltimateSoundTracker121.prototype._extractSongSpeed = function( )
459 {
460 	try
461 	{
462 		var iSongSpeed = weasel.Helper.getByte( this.aModuleData, weasel.FormatUltimateSoundTracker121.SongSpeed );
463 		this.setSongSpeed( iSongSpeed );
464 	}catch ( oException )
465 	{
466 	}
467 };
468 
469 
470 // ---------------------------------------------------------------------------
471 /**
472  * Get Pattern object from the pattern number.
473  * 
474  * @param {int} iPatternNumber = The number of the pattern to fetch (NOT THE SEQUENCE NUMBER!).
475  * 
476  * @return Pattern = The request Pattern object or null if non-existent.
477  */
478 weasel.UltimateSoundTracker121.prototype.getPattern = function( iPatternNumber )
479 {
480 	if( iPatternNumber < 0 || iPatternNumber >= this.aPatterns.length )
481 		return null;
482 
483 	return this.aPatterns[ iPatternNumber ];
484 };
485 
486 // ---------------------------------------------------------------------------
487 /**
488  * Get the CIA Timer Clock Constant, which will be either PAL or NTSC CPU frequency divided by 10.
489  * 
490  * @return {float} = The CIA Timer Clock Constant.
491  */
492 weasel.UltimateSoundTracker121.prototype.getCIATimerConstant = function( )
493 {
494 	return this.bUsePALClockConstant == true ? (weasel.FormatUltimateSoundTracker121.ClockConstantPAL / 5) : (weasel.FormatUltimateSoundTracker121.ClockConstantNTSC / 5);
495 };
496 
497 
498 // ---------------------------------------------------------------------------
499 /**
500  * Get tick playback rate in hz, this is speed at which the module processes each tick
501  * (there are 6 ticks per row), used to set the (CIA) interrupt rate
502  * , Ultimate Soundtracker allows you to adjust the song speed to values other than
503  * the normal 50hz PAL vertical refresh synchronization. Notice that this value is 
504  * calculated from the  CIA Timer Clock Constant value and that the default song speed of 120 (bpm)
505  * does not result in a 50hz timer, instead its ~48.45hz (PAL). 120bpm is a established music speed,
506  * 125bpm is a traditional Amiga song speed as it allows the song to be synced with a 50hz PAL TV.
507  * 
508  * @return {float} = The playback rate in hz.
509  */
510 weasel.UltimateSoundTracker121.prototype.tickPlaybackRateInHz = function( )
511 {
512 	var fPlaybackRateInHz = this.getCIATimerConstant() / ((240 - this.getSongSpeed()) * 122 );
513 	
514 	if( this.iTimingOverride == this.TimingOverrides.PAL )
515 	{
516 			fPlaybackRateInHz = this.PAL;
517 	}
518 	else if( this.iTimingOverride == this.TimingOverrides.NTSC  )
519 	{
520 			fPlaybackRateInHz = this.NTSC;
521 	}
522 	
523 	return fPlaybackRateInHz;
524 };
525 
526 
527 // ---------------------------------------------------------------------------
528 /**
529  * Get length of song in milliseconds.
530  * 
531  * @return {float} = The length of the song in ms.
532  */
533 weasel.UltimateSoundTracker121.prototype.getLengthOfSongInMilliSeconds = function( )
534 {
535 	return this.getSongLengthInPatterns() * weasel.FormatUltimateSoundTracker121.TicksPerRow * weasel.FormatUltimateSoundTracker121.NumberOfRowsPerPattern / this.tickPlaybackRateInHz() * 1000.0;
536 
537 };
538 
539 // ---------------------------------------------------------------------------
540 /**
541  * Get song position in milliseconds.
542  * 
543  * @return {float} = The song position from its beginning in ms.
544  */
545 weasel.UltimateSoundTracker121.prototype.getSongPositionInMilliSeconds = function( )
546 {
547 	var iCurrentPosition = (this.iCurrentSequencePosition * weasel.FormatUltimateSoundTracker121.TicksPerRow * weasel.FormatUltimateSoundTracker121.NumberOfRowsPerPattern)
548 	+ ( this.iCurrentPatternRowPosition * weasel.FormatUltimateSoundTracker121.TicksPerRow )
549 	+ ( this.iCurrentTick );
550 
551 	return iCurrentPosition / this.tickPlaybackRateInHz() * 1000.0;
552 };
553 
554 
555 // ---------------------------------------------------------------------------
556 /** Some Soundtrackers have effects that need processing out of the normal order,
557  * such as the Note Portamento command in Noisetracker.
558  * 
559  * @param {weasel.Channel} oChannel = The Channel to process for effects.
560  * @param {int} iNotePeriod = The note period yet to be set for this oChannel object.
561  *
562  * @return {bool} =	false : the fetch row process should continue for this oChannel object, 
563  * 					true  : stop the fetch row process for this oChannel object but continue for the other channels..
564  * 
565  * @protected
566  */
567 weasel.UltimateSoundTracker121.prototype._exceptionPreprocessEffects = function( oChannel, iNotePeriod )
568 {
569 	return false;
570 };
571 
572 // ---------------------------------------------------------------------------
573 /** Process a channel's effects.
574  * 
575  * @param {weasel.Channel} oChannel = The Channel to process for effects.
576  * 
577  * @protected
578  */
579 weasel.UltimateSoundTracker121.prototype._processChannelEffect = function( oChannel )
580 {
581 	switch( oChannel.getEffectNumber() )
582 	{
583 		case weasel.FormatUltimateSoundTracker121.Effects.Arpeggio :
584 				oChannel.arpeggio( this.iCurrentTick, weasel.Channel.prototype.ArpeggioMode.UltimateSoundtracker );
585 			break;
586 
587 		case weasel.FormatUltimateSoundTracker121.Effects.Pitchbend :
588 				var iEffectParameter= oChannel.getEffectParameter();
589 				var iPitchBendDown	= (iEffectParameter >>> 4)	& 0x0f;
590 				var iPitchBendUp	=  iEffectParameter			& 0x0f;
591 				
592 				oChannel.pitchBend( this.iCurrentTick, iPitchBendDown, iPitchBendUp );
593 			break;
594 
595 		default :
596 			break;
597 	}
598 };
599 
600 // ---------------------------------------------------------------------------
601 /**
602  * Process the effects column for all channels.
603  */
604 weasel.UltimateSoundTracker121.prototype.processEffects = function( )
605 {
606 	if( 0 != this.iCurrentTick )
607 	{
608 		for( var iChannels = this.aSoundChannels.length, iChannel = -1; ++iChannel < iChannels; )
609 		{
610 			var oChannel = this.aSoundChannels[ iChannel ];
611 
612 			this._processChannelEffect( oChannel );
613 		}
614 	}
615 };
616 
617 // ---------------------------------------------------------------------------
618 /** Process a channel's effects that occur on Tick Zero, there aren't any for Ultimate Soundtracker.
619  * 
620  * @param {weasel.Channel} oChannel = The Channel to process for effects.
621  * 
622  * @protected
623  */
624 weasel.UltimateSoundTracker121.prototype._processChannelTick0Effect = function( oChannel )
625 {
626 };
627 
628 // ---------------------------------------------------------------------------
629 /**
630  * Process the effects column for all channels when current tick is zero (a new row has been fetched)
631  * Ultimate Soundtracker does not process effects on this tick, however other Soundtrackers might.
632  */
633 weasel.UltimateSoundTracker121.prototype.processTick0Effects = function( )
634 {
635 
636 };
637 
638 // ---------------------------------------------------------------------------
639 /** Protracker allows fining tuning of its samples, Ultimate Soundtracker and others
640  * do not.
641  *
642  * @param {weasel.Channel} oChannel = The channel object to start its pending sample.
643  * @param {int} iNotePeriod = The note period yet to be set for this oChannel object.
644  *
645  * @return {int} =	The note period corrected for fine tuning. 
646  * 
647  * @protected
648  */
649 weasel.UltimateSoundTracker121.prototype._fineTune = function( oChannel, iNotePeriod )
650 {
651 	return iNotePeriod;
652 };
653 
654 // ---------------------------------------------------------------------------
655 /** Apply instrument volume to channel immediately, this is because Ultimate soundtracker applies 
656  * the new volume level of the pending instrument whether a note is present or not,
657  * allowing you to change the volume of the sample without actually changing the
658  * instrument or note period.
659  * 
660  * @param {weasel.Channel} oChannel = The channel object to apply volume to immediately.
661  * 
662  * @protected
663  */
664 weasel.UltimateSoundTracker121.prototype._applyVolumeImmediately = function( oChannel )
665 {
666 	// Volume change is applied immediately even if there is no new note.
667 	//
668 	oChannel.setVolume( this.getInstrument( oChannel.getPendingInstrumentNumber() ).getVolume() );
669 
670 };
671 
672 // ---------------------------------------------------------------------------
673 /**
674  * Fetch the next pattern row into the channels for processing.
675  */
676 weasel.UltimateSoundTracker121.prototype.fetchRow = function( )
677 {
678 	for( var iChannel = 0, iTotalChannels = this.aSoundChannels.length; iChannel < iTotalChannels; iChannel++ )
679 	{
680 		var oChannel = this.aSoundChannels[ iChannel ];
681 		var oPatternCell = this.getCurrentPatternCell( iChannel );
682 
683 		this._applyPatternCell( oChannel, oPatternCell );
684 		this._processChannelTick0Effect( oChannel );
685 	}
686 
687 	for( var iChannel = 0, iTotalChannels = this.aSoundChannels.length; iChannel < iTotalChannels; iChannel++ )
688 	{
689 		var oChannel = this.aSoundChannels[ iChannel ];
690 
691 		if( true == oChannel.getNoNoteDelayQuirk() )
692 		{
693 			// Update ALL normal channels playback as one or more ED0 or E9x command was used on this row and so normal channels get delayed by the DMA Wait pause.
694 			//
695 			oChannel.setNoNoteDelayQuirk( false );
696 			oChannel.delaySampleStart( true, this.WaitForDMAToStop() * this.fWaitForDMAMultiplier );
697 		}
698 	}
699 };
700 
701 // ---------------------------------------------------------------------------
702 /** Apply the Channel's pattern cell to the channel.
703  * 
704  * @param {weasel.Channel} oChannel = The channel object to apply volume to immediately.
705  * @return {weasel.PatternCell} = The current PatternCell object for the provided channel number.
706  * 
707  * @protected
708  */
709 weasel.UltimateSoundTracker121.prototype._applyPatternCell = function( oChannel, oPatternCell )
710 {
711 	// Always set effect command and effect parameter.
712 	//
713 	oChannel.setEffectNumber( oPatternCell.getEffectNumber() );
714 	oChannel.setEffectParameter( oPatternCell.getEffectParameter() );
715 	oChannel.setCurrentPatternCell( oPatternCell );
716 
717 	var iInstrumentNumber = oPatternCell.getInstrumentNumber();
718 
719 	// Only change instrument number if set in pattern.
720 	//
721 	if( iInstrumentNumber > 0 )
722 	{
723 		oChannel.setPendingInstrumentNumber( iInstrumentNumber, null );
724 		
725 		// Volume change is applied immediately even if there is no new note.
726 		//
727 		this._applyVolumeImmediately( oChannel );
728 	}
729 
730 	var iNotePeriod = oPatternCell.getNotePeriod();
731 
732 	// Only change note if set in pattern.
733 	//
734 	if( 0 != iNotePeriod )
735 	{
736 		if( this._exceptionPreprocessEffects( oChannel, iNotePeriod ) )
737 		{
738 			return;
739 		}
740 
741 		// Noisetracker 1.1 Vibrato command.
742 		//
743 		oChannel.setVibratoTablePosition( 0 );
744 
745 		// If period value is already too low (like 2604hz) then do not change the instrument.
746 		// Due to the Amiga DMA not being given enough time to stop, so it does not play the new sample (continues with old sample).
747 		//
748 		if( oChannel.getNotePeriod() >= 1374 )
749 		{
750 			oChannel.setNotePeriod( iNotePeriod );
751 			oChannel.setLastSavedNotePeriod( iNotePeriod );
752 			oChannel.setShadowNotePeriod( iNotePeriod );
753 
754 			return;
755 		}
756 
757 		this.startPendingSample( oChannel, iNotePeriod, this.WaitForDMAToStop() );
758 	}
759 };
760 
761 // ---------------------------------------------------------------------------
762 /**
763  * Start pending sample on a channel at a given note period.
764  * 
765  * @param {weasel.Channel} oChannel = The channel object to start its pending sample.
766  * @param {int} iNotePeriod = The note period yet to be set for this oChannel object.
767  * @param {float} fWaitForDMAToStop = The value to use (in milliseconds) for the pause between samples, typically weasel.FormatUltimateSoundTracker121.WaitForDMAToStop but Noisetracker/Protracker use different values.
768  */
769 weasel.UltimateSoundTracker121.prototype.startPendingSample = function( oChannel, iNotePeriod, fWaitForDMAToStop )
770 {
771 	var iInstrumentNumber = oChannel.getPendingInstrumentNumber();
772 	oChannel.setNotePeriod( iNotePeriod );
773 	oChannel.setLastSavedNotePeriod( iNotePeriod );
774 	oChannel.setShadowNotePeriod( iNotePeriod );
775 	oChannel.setInstrumentNumber( iInstrumentNumber );
776 	oChannel.delaySampleStart( true, fWaitForDMAToStop );
777 
778 	var oInstrument = this.getInstrument( iInstrumentNumber );
779 	var oSample = oInstrument.getSample();
780 
781 	oSample.setLoopedYet( false );
782 	oChannel.setSample( oSample );
783 	oChannel.setSamplePosition( 0.0 );
784 	oChannel.setProtrackerInvertLoopOffset( oInstrument );
785 
786 	if( 0 != oChannel.playFromOffset() )
787 	{
788 		// Protracker Set SampleOffset Command.
789 		//
790 		var iOffset = oChannel.playFromOffset();
791 		var iLength = oSample.getLength();
792 		var iLoopEnd= oSample.getLoopStart() + oSample.getLoopLength();
793 
794 		if( this.bFSTClearSampleOffsetAfterUse )
795 		{
796 			// Fasttracker does not apply the SampleOffset to notes without the
797 			// 9xx command.
798 			// So clear after use.
799 			//
800 			oChannel.setPlayFromOffset( 0 );
801 		}
802 
803 		if( !oSample.isLooped() && iOffset > iLength )
804 		{
805 			iOffset = iLength;
806 		}
807 		else if( oSample.isLooped() && oInstrument.isNoisetrackerInstrument() && iOffset > iLoopEnd )
808 		{
809 			iOffset = iLoopEnd;
810 		}
811 
812 		oChannel.setSamplePosition( iOffset );
813 	}
814 
815 	if( oSample.isLooped() && !oInstrument.isNoisetrackerInstrument() )
816 	{
817 		oChannel.setSamplePosition( oSample.getLoopStart() );
818 	}
819 
820 	oChannel.setSampleAccumulator();
821 };
822 
823 // ---------------------------------------------------------------------------
824 /**
825  * Process this modules pattern data, fetch the next pattern row into the channels for processing etc.
826  */
827 weasel.UltimateSoundTracker121.prototype.processPattern = function( )
828 {
829 	if( this.bStartProcessingPatterns )
830 	{
831 		this.iCurrentTick++;
832 		this.iTotalPatternTicks++;
833 	}
834 	else
835 	{
836 		this.bStartProcessingPatterns = true;
837 	}
838 
839 	// Next row?
840 	//
841 	if( this.iCurrentTick >= this.iTickSpeed )
842 	{
843 		this.iCurrentTick = 0;
844 
845 		if( --this.iRowDelay <= 0 )
846 		{
847 			this.iRowDelay = 0;
848 			this.iCurrentPatternRowPosition += 1;
849 
850 			// Next Pattern?
851 			//
852 			if( this.getCurrentPatternRowPosition() >= weasel.FormatUltimateSoundTracker121.NumberOfRowsPerPattern || this.bPatternBreak )
853 			{
854 				if( this.bProtrackerRowDelayQuirk && this.bPatternBreak )
855 				{
856 					// Protracker Quirk, Row Delay command (EEx) and Pattern Break (Dxx) causes row skip, so Pattern Breaks to Dxx +1.
857 					//
858 					this.iPatternBreakToRow += 1;
859 
860 					if( this.iPatternBreakToRow >= weasel.FormatUltimateSoundTracker121.NumberOfRowsPerPattern )
861 					{
862 						// Stop from visiting Row 65..
863 						// Bump sequence position along too.
864 						//
865 						this.iPatternBreakToRow = 0;
866 						this.iCurrentSequencePosition += 1;
867 					}
868 				}
869 
870 				this.iCurrentPatternRowPosition = this.iPatternBreakToRow;
871 				this.iCurrentSequencePosition += 1;
872 
873 				if( -1 != this.iSequencePositionJump && this.bPatternBreak )
874 				{
875 					this.iCurrentSequencePosition = this.iSequencePositionJump;
876 				}
877 
878 				this.bPatternBreak = false;
879 				this.iPatternBreakToRow = 0;
880 				this.iSequencePositionJump = -1;
881 
882 				// End of song?
883 				//
884 				if( this.iCurrentSequencePosition >= this.getSongLengthInPatterns() )
885 				{
886 					this.iCurrentSequencePosition = this.iSongRestartSequence;
887 					this.bSongEnded = true;
888 				}
889 
890 				this._songSequenceChange();
891 			}
892 
893 			this.bProtrackerRowDelayQuirk = false;
894 		}
895 	}
896 
897 	this.fWaitForDMAMultiplier = 1.0;	// Protracker ED0 command causes software delays (some 6000 clock cycles for each ED0).
898 
899 	// Fetch current pattern row.
900 	//
901 	if( 0 == this.iCurrentTick )
902 	{
903 		if( this.iRowDelay <= 0 )
904 		{
905 			this.fetchRow();
906 		}
907 		else
908 		{
909 			this.processTick0Effects();
910 		}
911 	}
912 	else
913 	{
914 		this.processEffects();
915 	}
916 };
917 
918 // ---------------------------------------------------------------------------
919 /** 
920  * Song Sequence Position changed.
921  * 
922  * @protected
923  */
924 weasel.UltimateSoundTracker121.prototype._songSequenceChange = function( )
925 {
926 	this.iTotalPatternTicks = 0;
927 };
928 
929 // ---------------------------------------------------------------------------
930 /** 
931  * Restart song from beginning of module.
932  */
933 weasel.UltimateSoundTracker121.prototype.restartSong = function( )
934 {
935 	this.iCurrentTick = 0;
936 	this.iTotalPatternTicks = 0;
937 	this.iCurrentPatternRowPosition = 0;
938 	this.iCurrentSequencePosition = 0;
939 	this.bSongEnded = false;
940 	this.bPatternBreak = false;
941 	this.iPatternBreakToRow = 0;
942 	this.bStartProcessingPatterns = false;
943 	this.iSequencePositionJump = -1;
944 	this.iRowDelay = 0;
945 	this.fWaitForDMAMultiplier = 1.0;
946 	this.bProtrackerRowDelayQuirk = false;
947 
948 	for( var iChannel = this.aSoundChannels.length; --iChannel >= 0; )
949 	{
950 		this.aSoundChannels[ iChannel ].clearChannel();
951 	}
952 };
953 
954 // ---------------------------------------------------------------------------
955 /**
956  * Get the current tick speed.
957  * 
958  * @return {int} = The tick speed.
959  */
960 weasel.UltimateSoundTracker121.prototype.getTickSpeed = function( )
961 {
962 	return this.iTickSpeed;
963 };
964 
965 // ---------------------------------------------------------------------------
966 /**
967  * Get the requested audio channel object.
968  * 
969  * @param {int} iChannelNumber = The number of the channel to fetch.
970  * 
971  * @return {weasel.Channel} = The requested audio channel object.
972  */
973 weasel.UltimateSoundTracker121.prototype.getChannel = function( iChannelNumber )
974 {
975 	if( iChannelNumber < 0 || iChannelNumber >= this.aSoundChannels.length )
976 	{
977 		return null;
978 	}
979 
980 	return this.aSoundChannels[ iChannelNumber ];
981 };
982 
983 
984 // ---------------------------------------------------------------------------
985 /**
986  * Get the current row tick.
987  * 
988  * @return {int} = The current row tick (0-5 range, when a 0 occurs a new pattern row has been fetched).
989  */
990 weasel.UltimateSoundTracker121.prototype.getCurrentTick = function()
991 {
992 	return this.iCurrentTick;
993 };
994 
995 // ---------------------------------------------------------------------------
996 /**
997  * Get the current pattern cell object from the selected channel.
998  * 
999  * @param {int} iChannelNumber = The Channel containing the pattern cell to fetch.
1000  * 
1001  * @return {weasel.PatternCell} = The current PatternCell object for the provided channel number.
1002  */
1003 weasel.UltimateSoundTracker121.prototype.getCurrentPatternCell = function( iChannelNumber )
1004 {
1005 	if( iChannelNumber < 0 || iChannelNumber >= this.getNumberOfChannels() )
1006 		return null;
1007 
1008 	return this.getPattern( this.getPatternNumber( this.getCurrentSequencePosition() ) ).getColumn( iChannelNumber ).getCell( this.getCurrentPatternRowPosition() );
1009 };
1010 
1011 // ---------------------------------------------------------------------------
1012 /**
1013  * Play this module into the given AudioBuffer object.
1014  * 
1015  * @param {weasel.AudioBuffer} oAudioBuffer = The AudioBuffer object to render to.
1016  * @param {bool} bIgnoreFilter = Ignore using the Amiga Filter (only a very few modules actually use it).
1017  * @param {int} iSamples = [optional] The number of samples to make this frame, usually the remaining samples to fill in the oAudioBuffer object.
1018  */
1019 weasel.UltimateSoundTracker121.prototype.play = function( oAudioBuffer, bIgnoreFilter, iSamples )
1020 {
1021 	var iSamplesToFill = oAudioBuffer.samplesToFill();
1022 	var oChannel1 = this.getChannel( 0 );
1023 	var oChannel2 = this.getChannel( 1 );
1024 	var oChannel3 = this.getChannel( 2 );
1025 	var oChannel4 = this.getChannel( 3 );
1026 
1027 	if( undefined !== iSamples )
1028 	{
1029 		if( iSamples < iSamplesToFill && iSamples >= 0 )
1030 		{
1031 			iSamplesToFill = iSamples;
1032 		}
1033 	}
1034 
1035 	var bFilterStartingState = this.bFilterOn;
1036 	var bFilterStateChange = bFilterStartingState;
1037 	var iFilterStateChanges = 0;
1038 	var aFilterStateChange = this.aFilterStateChange;
1039 	var aFilterStateForMixer = this.aFilterStateForMixer;
1040 
1041 	var iSamplesToMix = iSamplesToFill;
1042 	var iStartingCircularBufferOffset = oChannel1.getCircularBufferPosition();
1043 
1044 	for( ; iSamplesToFill > 0; )
1045 	{
1046 		var iSamplesTillPatternProcess = this.iSamplesRemaining;
1047 	
1048 		if( iSamplesTillPatternProcess > iSamplesToFill )
1049 		{
1050 			iSamplesTillPatternProcess = iSamplesToFill;
1051 		}
1052 
1053 		oChannel1.make( iSamplesTillPatternProcess );
1054 		oChannel2.make( iSamplesTillPatternProcess );
1055 		oChannel3.make( iSamplesTillPatternProcess );
1056 		oChannel4.make( iSamplesTillPatternProcess );
1057 
1058 
1059 		this.iSamplesRemaining -= iSamplesTillPatternProcess;
1060 		iSamplesToFill -= iSamplesTillPatternProcess;
1061 
1062 		// Time to process Pattern data?
1063 		//
1064 		if( this.iSamplesRemaining <= 0 )
1065 		{
1066 			this.processPattern();
1067 			this.iSamplesRemaining = this.iSamplesPerTick;
1068 
1069 			if( bFilterStateChange != this.bFilterOn )
1070 			{
1071 				// Filter has been toggled on or off, record sample offset
1072 				// when this occurred for mixer.
1073 				//
1074 				bFilterStateChange = this.bFilterOn;
1075 				aFilterStateChange[ iFilterStateChanges ] = iSamplesTillPatternProcess;
1076 				aFilterStateForMixer[ iFilterStateChanges ] = bFilterStateChange;
1077 				iFilterStateChanges++;
1078 			}
1079 		}
1080 	}
1081 
1082 	if( iSamplesToMix > 0 )
1083 	{
1084 		var aSamples1 = oChannel1.getCircularAudioBuffer();
1085 		var aSamples2 = oChannel2.getCircularAudioBuffer();
1086 		var aSamples3 = oChannel3.getCircularAudioBuffer();
1087 		var aSamples4 = oChannel4.getCircularAudioBuffer();
1088 		
1089 		if( 0 == iFilterStateChanges || bIgnoreFilter )
1090 		{
1091 			// Filter state did not change, can mix channels in one go.
1092 			//
1093 			oAudioBuffer.mix( iSamplesToMix, aSamples1, aSamples2, aSamples3, aSamples4, iStartingCircularBufferOffset, bFilterStartingState && !bIgnoreFilter );
1094 		}
1095 		else
1096 		{
1097 			// Filters state changed, mix in chunks before and after change(s).
1098 			//
1099 			for( var iFilterChanges = 0; iFilterChanges < iFilterStateChanges; iFilterChanges++ )
1100 			{
1101 				var iSamplesTillChange = aFilterStateChange[ iFilterChanges ];
1102 
1103 				oAudioBuffer.mix( iSamplesTillChange, aSamples1, aSamples2, aSamples3, aSamples4, iStartingCircularBufferOffset, bFilterStartingState );
1104 
1105 				bFilterStartingState = aFilterStateForMixer[ iFilterChanges ];
1106 				iStartingCircularBufferOffset = (iStartingCircularBufferOffset + iSamplesTillChange) % aSamples1.length;
1107 				iSamplesToMix -= iSamplesTillChange;
1108 			}
1109 
1110 			if( iSamplesToMix > 0 )
1111 			{
1112 				oAudioBuffer.mix( iSamplesToMix, aSamples1, aSamples2, aSamples3, aSamples4, iStartingCircularBufferOffset, bFilterStartingState );
1113 			}
1114 		}
1115 	}
1116 };
1117 
1118 // ---------------------------------------------------------------------------
1119 /**
1120  * Use to see if playing module has finished (modules naturally loop back to beginning).
1121  * 
1122  * @return {bool} = TRUE means Song has ended , FALSE mean Song not finished. 
1123  */
1124 weasel.UltimateSoundTracker121.prototype.hasSongEnded = function( )
1125 {
1126 	return this.bSongEnded;
1127 };
1128 
1129 // ---------------------------------------------------------------------------
1130 /**
1131  * Get the current sequence position in the song.
1132  * 
1133  * @return {int} = The current sequence position. 
1134  */
1135 weasel.UltimateSoundTracker121.prototype.getCurrentSequencePosition = function( )
1136 {
1137 	return this.iCurrentSequencePosition;
1138 };
1139 
1140 // ---------------------------------------------------------------------------
1141 /**
1142  * Set the current sequence position in the song.
1143  * 
1144  * @param {int} iSequencePosition = The position in the sequence table you want.
1145  */
1146 weasel.UltimateSoundTracker121.prototype.setCurrentSequencePosition = function( iSequencePosition )
1147 {
1148 	var iSongLength = this.getSongLengthInPatterns();
1149 	
1150 	if( iSequencePosition >= this.getSongLengthInPatterns() )
1151 	{
1152 		iSequencePosition = iSongLength -1;
1153 	}
1154 
1155 	if( iSequencePosition < 0 )
1156 	{
1157 		iSequencePosition = 0;
1158 	}
1159 
1160 	this.iCurrentSequencePosition = iSequencePosition;
1161 };
1162 
1163 // ---------------------------------------------------------------------------
1164 /**
1165  * Get the Current Pattern Row Position in the song.
1166  * 
1167  * @return {int} = The Current Pattern Row Position. 
1168  */
1169 weasel.UltimateSoundTracker121.prototype.getCurrentPatternRowPosition = function( )
1170 {
1171 	return this.iCurrentPatternRowPosition;
1172 };
1173 
1174 
1175 // ---------------------------------------------------------------------------
1176 /**
1177  * Set Clock Constant to either PAL or NTSC.
1178  * 
1179  * @param {bool} bClockConstant = true : PAL, false : NTSC.
1180  */
1181 weasel.UltimateSoundTracker121.prototype.setClockConstant = function( bClockConstant )
1182 {
1183 	this.bUsePALClockConstant = bClockConstant == true ? true : false;
1184 	this.setSamplesPerTick();
1185 	
1186 	for( var iChannel = this.aSoundChannels.length; --iChannel >= 0; )
1187 		this.aSoundChannels[ iChannel ].setClockConstant( this.bUsePALClockConstant );
1188 };
1189 
1190 // ---------------------------------------------------------------------------
1191 /**
1192  * Set Clock Constant to either PAL or NTSC.
1193  * 
1194  * @param {weasel.UltimateSoundTracker121.TimingOverrides} iBPMPALNTSC = Use the BPM set in mod | PAL 50hz | NTSC ~59.94hz (actually 60/1.001).
1195  */
1196 weasel.UltimateSoundTracker121.prototype.timingOverride = function( iBPMPALNTSC )
1197 {
1198 	switch( iBPMPALNTSC )
1199 	{
1200 
1201 		case this.TimingOverrides.PAL :
1202 			this.iTimingOverride = this.TimingOverrides.PAL;
1203 			this.setClockConstant( true );
1204 		break;
1205 
1206 		case this.TimingOverrides.NTSC :
1207 			this.iTimingOverride = this.TimingOverrides.NTSC;
1208 			this.setClockConstant( false );
1209 		break;
1210 
1211 
1212 		case this.TimingOverrides.UseBPM :
1213 			this.iTimingOverride = this.TimingOverrides.UseBPM;
1214 			this.setSamplesPerTick();
1215 		default:
1216 		break;
1217 	};
1218 };
1219 
1220 // ---------------------------------------------------------------------------
1221 /**
1222  * Get the timing override.
1223  * 
1224  * @return {weasel.UltimateSoundTracker121.TimingOverrides} = The timing override.
1225  */
1226 weasel.UltimateSoundTracker121.prototype.getTimingOverride = function()
1227 {
1228 	return this.iTimingOverride;
1229 };
1230 
1231 // ---------------------------------------------------------------------------
1232 /**
1233  * Get the type of Soundtracker Module.
1234  * 
1235  * @return {String} = The type of module.
1236  */
1237 weasel.UltimateSoundTracker121.prototype.getModuleType = function()
1238 {
1239 	return this.sModuleType;
1240 };
1241 
1242 // ---------------------------------------------------------------------------
1243 /**
1244  * Get the mode of the Vibrato (Noisetracker 1.1 & 2.0 have a different divider, 
1245  * unfortunately its impossible to 100% id a Noisetracker 2.0 module so need to 
1246  * give the user the ability to switch between Vibrato Modes).
1247  * 
1248  * @return {bool} = True : Noisetracker 2.0 Vibrato mode, False : Noisetracker 1.1 Vibrato Mode.
1249  */
1250 weasel.UltimateSoundTracker121.prototype.getVibratoMode = function()
1251 {
1252 	return this.bNoisetracker20VibratoMode;
1253 };
1254 
1255 // ---------------------------------------------------------------------------
1256 /**
1257  * Set the mode of the Vibrato (Noisetracker 1.1 & 2.0 have a different divider, 
1258  * unfortunately its impossible to 100% id a Noisetracker 2.0 module so need to 
1259  * give the user the ability to switch between Vibrato Modes).
1260  * 
1261  * @param {bool} bNoisetracker20Mode = True : Noisetracker 2.0 Vibrato mode, False : Noisetracker 1.1 Vibrato Mode.
1262  */
1263 weasel.UltimateSoundTracker121.prototype.setVibratoMode = function( bNoisetracker20Mode )
1264 {
1265 	this.bNoisetracker20VibratoMode = bNoisetracker20Mode == true ? true : false;
1266 };
1267 
1268 // ---------------------------------------------------------------------------
1269 /**
1270  * The Protracker series has a bug when using the Ramp Down Sawtooth waveform
1271  * during the Tremolo command (the other waveform types are fine). The bug
1272  * is caused by using the Vibrato Table Position instead of the Tremolo Table
1273  * Position during construction of the sawtooth resulting in either a lop sided
1274  * sawooth (non centered) if the Vibrato command is not used or a triangle-ish waveform
1275  * that almost randomly changes direction if the Vibrato command has been used on
1276  * the same channel.
1277  * 
1278  * @param {bool} bBugEnabled = True : Bug enabled (normal behaviour), False : bug disable.
1279  */
1280 weasel.UltimateSoundTracker121.prototype.setProtrackerTremoloSawtoothBugMode = function( bBugEnabled )
1281 {
1282 	this.bProtrackerTremoloSawtoothBug = bBugEnabled == true ? true : false;
1283 };
1284 
1285 // ---------------------------------------------------------------------------
1286 /**
1287  * Get the mode of the Protracker Tremolo Sawtooth bug. 
1288  * 
1289  * @return {bool} = True : Bug enabled (normal behaviour), False : bug disable.
1290  */
1291 weasel.UltimateSoundTracker121.prototype.getProtrackerTremoloSawtoothBugMode = function()
1292 {
1293 	return this.bProtrackerTremoloSawtoothBug;
1294 };
1295 
1296 // ---------------------------------------------------------------------------
1297 /**
1298  * Get the constant used for waiting for the DMA to stop in milliseconds. 
1299  * 
1300  * @return {float} = The millisecond pause used by this module type.
1301  */
1302 weasel.UltimateSoundTracker121.prototype.WaitForDMAToStop = function()
1303 {
1304 	return weasel.FormatUltimateSoundTracker121.WaitForDMAToStop;
1305 };
1306 
1307 
1308 // ---------------------------------------------------------------------------
1309 /**
1310  * Protracker 3 has different Set Sample Offset command (9xx) behaviour, in that
1311  * it behaves as expected and only applies the Sample Offset before the sample
1312  * starts and not after, neither does it keep adding the Offset to the sample start
1313  * each call.
1314  * 
1315  * @param {bool} bEnabled = True : Enable Protracker 3 mode, False : Enable Protracker 1 & 2 mode.
1316  */
1317 weasel.UltimateSoundTracker121.prototype.setProtracker3SampleOffsetMode = function( bEnabled )
1318 {
1319 	this.bProtracker3SampleOffsetMode = bEnabled == true ? true : false;
1320 };
1321 
1322 // ---------------------------------------------------------------------------
1323 /**
1324  * Get the mode of the Protracker 3 mode, Set Sample Offset behaves differently from Protracker 1 & 2. 
1325  * 
1326  * @return {bool} = True : Bug enabled (normal behaviour), False : bug disable.
1327  */
1328 weasel.UltimateSoundTracker121.prototype.getProtracker3SampleOffsetMode = function()
1329 {
1330 	return this.bProtracker3SampleOffsetMode;
1331 };
1332 
1333 
1334 // ---------------------------------------------------------------------------
1335 /**
1336  * Some Fasttracker/Taketracker use a 7 Bit panning position value in the 0x8xx
1337  * command. The Module Sniffer is able to detect this and sets the module accordingly,
1338  * but will sometimes get it wrong as composers enter the wrong values (or reperpose
1339  * in the case of a few of Necros & Basehead FST modules for 3D stereo using additional channels on the GUS).
1340  * 
1341  * @param {bool} bEnabled = True : Enable 7 Bit Panning mode, False : Enable 8 Bit Panning mode.
1342  */
1343 weasel.UltimateSoundTracker121.prototype.setFST7BitPanningMode = function( b7BitPanning )
1344 {
1345 	if( !this.b7BitPanning && b7BitPanning )
1346 	{
1347 		// Switch current 8bit panning positions on channels to where they
1348 		// would be as if they were set in 7bit panning mode.
1349 		//
1350 		for( var iChannel = this.aSoundChannels.length; --iChannel >= 0; )
1351 		{
1352 			var oChannel = this.aSoundChannels[ iChannel ];
1353 			oChannel.setPanningPosition( ( oChannel.getPanningPosition() * 2 ) |0 );
1354 		}
1355 	}
1356 	else if( this.b7BitPanning && !b7BitPanning )
1357 	{
1358 		// Switch current 7bit panning positions on channels to where they
1359 		// would be as if they were set in 8bit panning mode.
1360 		//
1361 		for( var iChannel = this.aSoundChannels.length; --iChannel >= 0; )
1362 		{
1363 			var oChannel = this.aSoundChannels[ iChannel ];
1364 			oChannel.setPanningPosition( ( oChannel.getPanningPosition() / 2 ) |0 );
1365 		}
1366 	}
1367 
1368 	this.b7BitPanning = b7BitPanning == true ? true : false;
1369 };
1370 
1371 // ---------------------------------------------------------------------------
1372 /**
1373  * Get the Fasttracker/Taketracker 7 Bit Panning Mode. 
1374  * 
1375  * @return {bool} = True : 7 Bit Panning in use, FALSE : 8 Bit Panning in use.
1376  */
1377 weasel.UltimateSoundTracker121.prototype.getFSTPanningMode = function()
1378 {
1379 	return this.b7BitPanning;
1380 };
1381 
1382 
1383 //---------------------------------------------------------------------------
1384 /**
1385 * Noisetracker/Protracker loop quirks mode active (Taketracker/Fasttracker does not implement them). 
1386 * 
1387 * @return {bool} = True : yes, FALSE : no.
1388 */
1389 weasel.UltimateSoundTracker121.prototype.getNoiseTrackerLoopQuirkEnabled = function()
1390 {
1391 	return this.bNoiseTrackerLoopQuirk;
1392 };
1393