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 
  5 if( undefined == window.weasel ) window.weasel = {};
  6 
  7 // ---------------------------------------------------------------------------
  8 /** Create a Def Jam Soundtracker 3 module out of the provided data (which has already passed the module sniffer test).
  9  * Def Jam Soundtracker 3 is based upon TJC Soundtracker 2.
 10  * The following Soundtrackers have identical replays to Def Jam Soundtracker 3: 
 11  *    # Alpha Flight Soundtracker 4
 12  *    # DOC Soundtracker 3
 13  *    # DOC Soundtracker 4
 14  *    # DOC Soundtracker 6
 15  *    
 16  * The replay routines are identical with the exception that Il Scuro (Def Jam Soundtracker 3) added Set Tick Speed Effect Command.
 17  *
 18  * @constructor
 19  * @extends weasel.TJCSoundTracker2
 20  * 
 21  * @param {Array|Uint8Array} aModuleData = The Def Jam Soundtracker 3 module as a byte array that MUST have passed the module sniffer test.
 22  * @param {int} iPlaybackFrequency = The playback frequency in hertz to use (e.g. 44100 ).
 23  * @param {weasel.Sample.prototype.SampleScannerMode} iSampleScannerMode = Scan for IFF Header corruption residue?.
 24  * 
 25  * @author Warren Willmey 2012
 26  */
 27 weasel.DefJamSoundTracker3 = function( aModuleData, iPlaybackFrequency, iSampleScannerMode )
 28 {
 29 	this.parent = weasel.TJCSoundTracker2;
 30 	
 31 	// Needed for prototype Inheritance.
 32 	//
 33 	if( aModuleData === undefined || !(( aModuleData instanceof Array ) || ( window.Uint8Array && aModuleData instanceof Uint8Array )) )
 34 		return;
 35 
 36 	this.parent( aModuleData, iPlaybackFrequency, iSampleScannerMode );
 37 	this.sModuleType = weasel.ModuleSniffer.prototype.SupportedModules.DefJamSoundTracker3;
 38 
 39 	// Def Jam Soundtracker 3 does not honour the BPM Speed setting and
 40 	// only playback at 50Hz PAL.
 41 	//
 42 	this.timingOverride( this.TimingOverrides.PAL );
 43 
 44 	// Due to the Tick Speed command it is not so simple to:
 45 	// a) Work out the (time) length of the song.
 46 	// b) Work out the current (time) position.
 47 	//
 48 	this.iSongLengthInTicks = 0;
 49 	this.aSequenceTableAccumulatedTicks = new Array( this.getSongLengthInPatterns() );
 50 	this._computeSequenceTableTicks();
 51 };
 52 
 53 weasel.DefJamSoundTracker3.prototype = new weasel.TJCSoundTracker2;
 54 
 55 
 56 // ---------------------------------------------------------------------------
 57 /** Scan all used patterns in sequence order to work out the length of the song
 58  * in ticks, recording each of the sequences starting tick.
 59  * 
 60  * @protected
 61  */
 62 weasel.DefJamSoundTracker3.prototype._computeSequenceTableTicks = function( )
 63 {
 64 	for( var iLength = this.aSequenceTableAccumulatedTicks.length; --iLength >= 0; )
 65 	{
 66 		this.aSequenceTableAccumulatedTicks[ iLength ] = 0;
 67 	}
 68 
 69 	var iTickSpeed = weasel.FormatUltimateSoundTracker121.TicksPerRow;
 70 	var iTickTotal = 0;
 71 
 72 
 73 	for( var iSongSequenceLength = this.aSequenceTableAccumulatedTicks.length, iSongPosition = 0; iSongPosition < iSongSequenceLength; iSongPosition++ )
 74 	{
 75 		// Record the Total Ticks needed to get to the beginning of each pattern sequence.
 76 		//
 77 		this.aSequenceTableAccumulatedTicks[ iSongPosition ] = iTickTotal;
 78 
 79 		var iPatternNumber = this.getPatternNumber( iSongPosition );
 80 		var oPattern = this.getPattern( iPatternNumber );
 81 
 82 		for( var iRowsPerPattern = weasel.FormatUltimateSoundTracker121.NumberOfRowsPerPattern, iRow = 0; iRow < iRowsPerPattern; iRow++ )
 83 		{
 84 			// Order of channels important, there may be more than one Tick Speed effect in a single row.
 85 			//
 86 			for( var iChannels = this.aSoundChannels.length, iChannel = -1; ++iChannel < iChannels; )
 87 			{
 88 				var oColumn = oPattern.getColumn( iChannel );
 89 				var oPatternCell = oColumn.getCell( iRow );
 90 
 91 				if( weasel.FormatDefJamSoundTracker3.Effects.TickSpeed == oPatternCell.getEffectNumber() )
 92 				{
 93 					// Tick Speed has changed.
 94 					//
 95 					var iWantedTickSpeed = oPatternCell.getEffectParameter() & 0xf;
 96 
 97 					if( iWantedTickSpeed < 1 )
 98 						iWantedTickSpeed = 1;
 99 
100 					iTickSpeed = iWantedTickSpeed;
101 				}
102 			}
103 
104 			iTickTotal += iTickSpeed;
105 		}
106 	}
107 
108 	this.iSongLengthInTicks = iTickTotal;
109 };
110 
111 // ---------------------------------------------------------------------------
112 /**
113  * Get length of song in milliseconds.
114  * 
115  * @return {float} = The length of the song in ms.
116  * 
117  * @override
118  */
119 weasel.DefJamSoundTracker3.prototype.getLengthOfSongInMilliSeconds = function( )
120 {
121 	return this.iSongLengthInTicks * ( 1000.0 / this.tickPlaybackRateInHz());
122 
123 };
124 
125 // ---------------------------------------------------------------------------
126 /**
127  * Get song position in milliseconds.
128  * 
129  * @return {float} = The song position from its beginning in ms.
130  * 
131  * @override
132  */
133 weasel.DefJamSoundTracker3.prototype.getSongPositionInMilliSeconds = function( )
134 {
135 	return (this.aSequenceTableAccumulatedTicks[ this.getCurrentSequencePosition() ] + this.iTotalPatternTicks) * (1000.0 / this.tickPlaybackRateInHz());
136 };
137 
138 
139 // ---------------------------------------------------------------------------
140 /** Set the row tick speed (this Effect Command is added by Il Scuro
141  *  in Def Jams Soundtracker 3, so technically is not a Jungle Command Soundtracker 2 command ).
142  * 
143  * @param {int} iTickSpeed = The new row tick speed to use (1-15).
144  */
145 weasel.DefJamSoundTracker3.prototype.setTickSpeed = function( iTickSpeed )
146 {
147 	this.iTickSpeed = iTickSpeed < 1 ? 1 : iTickSpeed > 15 ? 15 : iTickSpeed;
148 };
149 
150 // ---------------------------------------------------------------------------
151 /** Process a channel's effects that occur on Tick Zero.
152  * 
153  * @param {weasel.Channel} oChannel = The Channel to process for effects.
154  * 
155  * @protected
156  * @override
157  */
158 weasel.DefJamSoundTracker3.prototype._processChannelTick0Effect = function( oChannel )
159 {
160 	switch( oChannel.getEffectNumber() )
161 	{
162 		case weasel.FormatTJCSoundTracker2.Effects.Arpeggio :
163 
164 				if( 0 == oChannel.getEffectParameter() )
165 				{
166 					// If Effect Command = 0 and Effect Data = 0 then clear
167 					// the AutoSlide command.
168 					//
169 					oChannel.setAutoSlide( false );
170 				}
171 
172 			break;
173 
174 		case weasel.FormatTJCSoundTracker2.Effects.Volume :
175 
176 				var iVolume = oChannel.getEffectParameter();
177 
178 				if( iVolume > 64 )
179 				{
180 					iVolume = 64;
181 				}
182 
183 				oChannel.setVolume( iVolume );
184 
185 			break;
186 
187 
188 		case weasel.FormatTJCSoundTracker2.Effects.AutoSlide :
189 
190 				var iSlideVolume = oChannel.getEffectParameter();
191 
192 				if( 0 == (iSlideVolume >>> 4) & 0xf )
193 				{
194 					// Slide volume down.
195 					//
196 					iSlideVolume = 0 - (iSlideVolume & 0xf);
197 				}
198 				else
199 				{
200 					// Slide Volume up.
201 					//
202 					iSlideVolume = (iSlideVolume >>> 4) & 0xf;
203 				}
204 
205 					oChannel.setAutoSlide( true );
206 					oChannel.setAutoSlideValue( iSlideVolume );
207 			break;
208 
209 		case weasel.FormatDefJamSoundTracker3.Effects.TickSpeed :
210 
211 				var iWantedTickSpeed = oChannel.getEffectParameter() & 0xf;
212 				this.setTickSpeed( iWantedTickSpeed );
213 			break;
214 
215 		default :
216 			break;
217 	}
218 };
219