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