/* (tabstops=8) °°°°°°°°°°°°°°±±±±±±±±±±±±±²²²²²²²²²²²²²²²²²²²²²²²²±±±±±±±±±±±±±°°°°°°°°°°°°°° þ S3M Player Tutorial by FireLight þ Copyright (c) Brett Paterson 1994-95 þ þ Last updated 20/11/95 þ °°°°°°°°°°°°°°±±±±±±±±±±±±±²²²²²²²²²²²²²²²²²²²²²²²²±±±±±±±±±±±±±°°°°°°°°°°°°°° ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ³ °±² : SECTION 0: ²±° ³ ³ °±² Index ²±° ³ ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ Section 1 : INTRODUCTION 1.1 Notes. 1.2 Terminology. 1.3 Contacting FireLight and feedback. Section 2 : THE LOADER 2.1 Notes. 2.2 Verification. 2.3 Load Module Name. 2.4 Rest of the Main Header - default/miscellaneous info. 2.5 Load channel settings 2.5.1 The 32 bytes. 2.5.2 Remapping the channels. 2.6 Load order data. 2.7 Parapointers. 2.8 Default pan positions. 2.8.1 Mono. 2.9 Instruments. 2.10 Loading pattern data. 2.10.1 Now 5 bytes? 2.11 Loading sample data. Section 3 : EXTRA S3M FEATURES 3.1 Volume byte. 3.2 Panning. 3.3 New effects. Section 4 : MORE PERIODS 4.1 Goodbye fine tune!!! - C2SPD. 4.2 9 octaves. Section 5 : MISCELLANEOUS 5.1 The 10 steps in converting a MOD player to an S3M player. 5.2 Creating the ultimate internal format of your own. Section 6 : EFFECTS 6.01 Quick Reference Chart 6.1 Effect Axx (Set speed) 6.2 Effect Bxx (Jump to order) 6.3 Effect Cxx (Break pattern to row) 6.4 Effect Dxx (Volume slide) 6.5 Effect Exx (Portamento Down) 6.6 Effect Fxx (Portamento Up) 6.7 Effect Gxx (Tone portamento) 6.8 Effect Hxy (Vibrato) 6.9 Effect Ixy (Tremor) 6.10 Effect Jxy (Arpeggio) 6.11 Effect Kxy (Vibrato + Volume slide) 6.12 Effect Lxy (Porta + Volume slide) 6.13 Effect Oxy (Set sample offset) 6.14 Effect Qxy (Retrig (+volumeslide) note) 6.15 Effect Rxy (Tremolo) 6.16 Effect Uxy (Fine Vibrato) 6.17 Effect S0x (Set filter) 6.18 Effect S1x (Set glissando control) 6.19 Effect S2x (Set finetune) 6.20 Effect S3x (Set vibrato waveform) 6.21 Effect S4x (Set tremolo waveform) 6.22 Effect S8x (Set channel pan position) 6.23 Effect SAx (Stereo control) 6.24 Effect SBx (Pattern loop) 6.25 Effect SCx (Notecut) 6.26 Effect SDx (Notedelay) 6.27 Effect SEx (Patterndelay) 6.28 Effect SFx (Funkrepeat) 6.29 Effect Txx (Set Tempo) 6.30 Effect Vxx (Set global volume) Section 7 : APPENDIX 7.1 Notes 7.2 ScreamTracker 3 TECH.DOC (edited) ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ³ °±² : SECTION 1: ²±° ³ ³ °±² Introduction ²±° ³ ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ³ °±² 1.1 Notes ²±° ³ ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ Assumptions: ============ Everything that was assumed in FMODDOC.TXT, and that you have actually got a .MOD player working. I borrow from FMODDOC.TXT and even cut and paste some of the parts. Most of the theory and information about writing player code is found in FMODDOC.TXT, this document is more about the actual S3M FILE format. At this point I will just take the opportunity to say that coding a .MOD player is quite beneficial, even though I hear you scoff and say who supports .MOD any more? Well sorry to say but S3M in its playing theory borrows VERY, VERY heavily from protracker, and if you code a .MOD player, it takes VERY LITTLE effort to make that player an S3M player. My .MOD player took about 1 HOUR to convert to an S3M player after the loader, as the principles in playing are very similar. WRITE A MOD PLAYER FIRST THEN DO S3M, YOU WON'T REGRET IT. S3M uses 90% of the .MOD effects, and as S3M has about 90% of .MOD lurking in the background in other areas, I can't emphasize this enough. See section 5 on the 10 simple steps in converting a MOD player to an S3M player to see what I mean. Your player will probably play the effects more accurately too if you get it right under .MOD first, and then .S3M. ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ³ °±² 1.2 Terminology ²±° ³ ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ TYPE LENGTH Bits RANGE WATCOM/BORLAND/TURBO C ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ byte 1 8 0-255 unsigned char word 2 16 0-65,535 unsigned short dword 4 32 0-4,294,967,295 unsigned long ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ Throughout this text I use the terms BYTE,WORD, and DWORD, to make the document more general to all languages. In C you can use typedefs to achieve the use of byte,word,dword terminology, and in Pascal and asm the syntax is already suited to this anyway. ORDERS - orders are how the mod plays from 0 to length of song. PATTERNS - patterns are played in any ORDER, and are the physical information. TICK - I refer to a clock tick for the interrupt handler as a tick, some others use the term FRAME. I will be using the term tick throughout the whole document. ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ³ °±² 1.3 Contacting FireLight and feedback ²±° ³ ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ Contact is encouraged for any reason. After typing literally over 100 printed pages, I might have probably made some small mistakes here and there, but I have proof read this document many many times and spell checked it etc. email : firelght@suburbia.apana.org.au post : Brett Paterson, 48/a Parr st, Leongatha, 3953, Victoria, Australia. IRC : FireLight on #coders (#trax) ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ³ °±² : SECTION 2 : ²±° ³ ³ °±² The Loader ²±° ³ ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ³ °±² 2.1 Notes ²±° ³ ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ As S3M can be quite difficult to load for the inexperienced, I will explain as much as possible about the quirkiness of this format and how to overcome it. If you have already written a .MOD loader, you will have to change your storage variables around a little bit to compensate for S3M's new features. The main differences are: ========================= 1. 32 channels 2. 9 octaves 3. volume column 4. a few extra effects More about this will be discussed later on in the document, and see SECTION 5.1 on handling these differences. As in FMODDOC.TXT I will split most sections into the following 4 sections, to try and get optimal understanding of the matter. - EXPLANATION (describes what the section is on about, for understanding) - PSEUDOCODE (usually shows HOW to code it, but in English) - STORAGE ISSUE (helps on how to store the information loaded) - SUGGESTION (a helpful hint or suggestion to do after this step) NOTE: Remember to follow this document along side TECH.DOC's map out of an S3M file. (TECH.DOC is kept in the appendix at the end of this document). It will make things easier to visualise. I'm not here to re-write tech.doc but to explain how things work. ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ³ °±² 2.2 Verification ²±° ³ ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ Explanation: ============ Before we attempt to load a the S3M, we should check that it is in fact an S3M. Every S3M has a signature stored inside the file at offset 44 (2Ch), and this should always be checked first. PseudoCode: =========== - Seek to offset 44 (2Ch) in the file - read in 4 bytes - compare them to "SCRM" - if it is a match we have an S3M file (apparently) - otherwise exit and display error message. ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ³ °±² 2.3 Load Module Name ²±° ³ ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ Explanation: ============ This is a trivial part of the loader and just holds the Title or name of the mod. It is the very first 28 bytes of the MOD. PsuedoCode: =========== - Seek back to position 0, the start of the file - read in 28 bytes, store as MODULE_NAME. Storage Issue: ============== If your system originally was set up to hold a 20byte name for a module title, then extend this to 28 bytes. Suggestion: =========== As always this point is a good time to see if it worked by printing out what you read in and seeing if it is correct. Note: ===== From this point on I won't be going into as much detail or length for trivial matters like this, but I will still make sure it is understood with a brief explanation. ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ³ °±² 2.4 Rest of the Main Header - default/miscellaneous information ²±° ³ ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ Explanation: ============ With a .MOD loader, normally at this stage we would be now getting ready to load in the 31 lots of sample information, but in S3M's case we have a lot of other miscellaneous information we want to know first. These sorts of things are default panning positions, global volume, initial tempo etc, etc. This section covers these miscellaneous parts and explains what to do with them. Breaking away from the usual format I will display this section in more of a map format, with the file offset and bytes to read displayed along with a short description. So after you have read in the name of the module, the following information is stored in this order. "Size" is how much info to read in, in BYTES. Remember when reading in a WORD, you should read it straight to the address of the variable like so, 2 bytes at a time. i.e. fread(&songlength, 2, 1, handle); (excuse my C). Otherwise find out the proper value with (byte1*100h + byte2). ÄÄÄÄÄÄÄÄÂÄÄÄÄÄÂÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ Offset ³ Size³ Explanation. ÄÄÄÄÄÄÄÄÅÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ 0 ³ 28 ³ SONG NAME, you should already have loaded this. 28 (1Ch)³ 1 ³ 1Ah. This should be the hex value 1Ah. But sometimes it ³ ³ isn't. It's main purpose seems to be an end of file marker ³ ³ for using 'TYPE' under DOS. If you do a 'TYPE NAME.S3M' it ³ ³ will display the name of the song then go no further. 29 (1Dh)³ 1 ³ FILETYPE. This should be the FILETYPE. If it is 16, then it ³ ³ is an ST3 module. I don't know what else it might be unless ³ ³ it is for tracker identification (trackers that might write to ³ ³ S3M - heh as if!.. ³ ³ .. reading the file is bad enough, but writing it is horror!) 30 (1Eh)³ 2 ³ These 2 bytes are currently for EXPANSION and can be ignored. 32 (20h)³ 2 ³ SONG LENGTH, or number of orders which count from 0 to this. ³ ³ See the order SECTION 2.6 for more on this because this ³ ³ figure is not entirely accurate. 34 (22h)³ 2 ³ NUMBER OF INSTRUMENTS. ³ ³ This is a new concept if you have coded a .MOD player as it ³ ³ is usually always 31. Add a NUM_INSTRUMENTS type variable to ³ ³ your routines now, and when loading mods just set it to 31 all ³ ³ the time. For S3M this will hold the number of samples we ³ ³ will be loading later on. 36 (24h)³ 2 ³ NUMBER OF PATTERNS. This part is the number of physical ³ ³ patterns stored in the file, regardless of how many orders ³ ³ there are. This figure isn't always the best method of ³ ³ finding out how many patterns there are in the song, as it ³ ³ also includes marker patterns etc, which have no pattern data. ³ ³ See SECTION 2.6 to find out more about this. ³ ³ (Remember to store this figure though as you MUST use it to ³ ³ know how many parapointers to load later.) 38 (26h)³ 2 ³ FLAGS. This is the flags section. You need to bitwise AND ³ ³ it with the following values to see if the corresponding flags ³ ³ are set. Most of them are for Scream Tracker specific ³ ³ information so can just about be ignored if you like. ³ ³ if ((value AND 1) >0) : st2vibrato (not supported in st3.01) ³ ³ if ((value AND 2) >0) : st2tempo (not supported in st3.01) ³ ³ if ((value AND 4) >0) : amigaslides (not supported in st3.01) ³ ³ if ((value AND 32) >0) : enable filter/sfx (not supported) ³ ³ if ((value AND 8) >0) : 0vol optimizations: Automatically ³ ³ turn off looping notes whose volume ³ ³ is 0 for >2 note rows. Don't bother ³ ³ with this. ³ ³ if ((value AND 16) >0) : amiga limits: Disallow any notes ³ ³ that go beyond the amiga hardware ³ ³ limits (like amiga does). This means ³ ³ that sliding up stops at B-5 etc. ³ ³ This also affects some minor amiga ³ ³ compatibility issues. ³ ³ if ((value AND 64) >0) : st3.00 volumeslides. Normally ³ ³ volumeslide is NOT performed on first ³ ³ frame of each row (this is according ³ ³ to amiga playing). If this is set, ³ ³ volumeslide is performed ALSO on the ³ ³ first row. This is set by default if ³ ³ the CWT/V value is 1300h. ³ ³ if ((value AND 128) >0): special custom data in file. If ³ ³ this is set then you can use the ³ ³ 'Special' pointer at offset 3Eh. ³ ³ See more about this when describing ³ ³ this special pointer. 40 (28h)³ 2 ³ CWT/V. This means 'Created with tracker / version'. ³ ³ It just tells us the tracker version used to write the tune. ³ ³ The upper 4 bits are the tracker, and the bottom 12 bits are ³ ³ the version. So to get the version, bitwise AND it with FFFh, ³ ³ and to find the tracker value, SHIFT RIGHT the value 12 bits. ³ ³ These are the values that are possible from scream tracker. ³ ³ ST3.00 : 1300h (NOTE: volumeslides on EVERY frame) ³ ³ ST3.01 : 1301h ³ ³ ST3.03 : 1303h ³ ³ ST3.20 : 1320h ³ ³ The part where it said volumeslides on every frame, is ³ ³ explained further in the effects section, SECTION 6.7 42 (2Ah)³ 2 ³ This part tells us whether the file stores its samples in ³ ³ unsigned format, or signed format. Remember this for loading ³ ³ your samples in later, but you don't really have to bother as ³ ³ they're usually unsigned. 1 = signed samples, 2 = unsigned. 44 (2Ch)³ 4 ³ 'SCRM' Here we are again at the "SCRM" signature. check this ³ ³ again or ignore it as you like. 48 (30h)³ 1 ³ GLOBAL VOLUME. You must have a global volume variable if you ³ ³ want to support S3M fully, and you may already have it in ³ ³ your .MOD player if you have a master volume switch. ³ ³ Global volume is quite simple, it just means when setting ³ ³ volumes while your mod is playing, you need to multiply the ³ ³ volume to be set by your global volume, then divide it by 64. ³ ³ i.e. final volume = volume[track]*globalvol/64 (from FMOD1.06) 49 (31h)³ 1 ³ INITIAL SPEED. This is the default speed to start at when ³ ³ playing your mod. Usually with a .MOD player we always set ³ ³ the default speed to 6, but with S3M we set it to this value. ³ ³ Speed means the number of clock ticks between each row, see ³ ³ FMODDOC.TXT for more information on ticks/frames/speed etc. 50 (32h)³ 1 ³ INITIAL TEMPO. This is the default tempo to start at when ³ ³ playing your mod. Usually with a .MOD player we always set ³ ³ the default speed to 125 (7Dh), but with S3M we set it to ³ ³ this value. Tempo means the speed we have the interrupt ³ ³ handler ticking, and in ticks per second we use the formula ³ ³ HZ = (SPEED*2)/5 to set our timer. See FMODDOC.TXT for more ³ ³ on this. 51 (33h)³ 1 ³ MASTER VOLUME. The lower 7 bits of this are the master ³ ³ volume. How is the master volume different to the global ³ ³ volume you ask? Well the master volume only affects the ³ ³ SoundBlaster, and the amount of sample multiplication. ³ ³ See FSBDOC.TXT for more information on SoundBlaster Mixing. ³ ³ ³ ³ As this is the lower 7 bits, then high 8th bit is the ³ ³ stereo flag. To get this value shift the number right by ³ ³ 7 bits. If the bit is set (1), then we use stereo as normal, ³ ³ if it is not set (0), then we use mono. Mono is achieved by ³ ³ setting all panning values to the middle. ³ ³ See SECTION 2.8.1 for more on this. 52 (34h)³ 1 ³ ULTRACLICK REMOVAL. This is fairly ScreamTracker specific ³ ³ flag and should most definately be ignored. You should have ³ ³ your own Ultraclick removal system instead of PSi's method. ³ ³ The value divided by 2 is meant to be the guaranteed number ³ ³ of channels which are to be click free. Your player should ³ ³ have all channels click free though. (using interrupt driven ³ ³ ramping for example.) ³ ³ For more information on his method see the relevant section ³ ³ in the APPENDIX at the end of this document which contains ³ ³ TECH.DOC from ST3. 53 (35h)³ 1 ³ DEFAULT PANNING. This is the flag that tells us if we are ³ ³ using default panning or not. ³ ³ If the value = 252, then we use the default panning values ³ ³ stored later on in this file. They are described in Section ³ ³ 2.8 when we get to that position in the file. ³ ³ If it doesn't equal 252, then no attempt is made to load ³ ³ these panning values (most likely because they aren't there) 54 (36h)³ 8 ³ These 8 bytes are currently for EXPANSION and can be ignored. 62 (3Eh)³ 2 ³ SPECIAL. This is a pointer to special custom data (only if ³ ³ the special data flag was set at offset 38 (26h) in the file). ³ ³ As you have no idea what this data might be you can ignore it. ³ ³ It is mainly for your own use if you want to add some extra ³ ³ data in your own S3M writer I think. ÄÄÄÄÄÄÄÄÅÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ Once this is done we have all the information we have need to know about the state that an S3M should be in before we start to play it. Now all we need to do is load some channel settings, which is information specific to each channel (like panning, and if it is disabled/enabled). Suggestion: =========== Now would be a good time to see if you loaded in your values right and check which ones apply to you and which don't. A lot of them wont. ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ³ °±² 2.5 Load Channel Settings ²±° ³ ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ Explanation: ============ You were probably just asking then. WHERE WAS THE NUMBER OF CHANNELS VALUE?? Well this part will tell you that. This is about the biggest design flaw in S3M I think and is a really messy way to do things. It is called the channel settings. The next 32 bytes are the settings for each channel. It tells us if a channel is turned on, or off, and by counting all the channels that are turned ON. This tells us how many channels that are used in total. Say we have a 4 channel S3M. Normally you would expect it to save it like this. channels 0<ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ>31 0123-------------------------- but Scream Tracker 3 allows you to save a tune with channels enabled and disabled, in this way for example. (using hex notation) channels 0<ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ>31 --2----78-----E--------------- This way sees the first 2 channels as disabled, and then the 3rd channel enabled and so on. It is still a 4 channel S3M, but it uses channels 2,7,8 and 14 to do it with!! If you still don't understand this concept, then run ScreamTracker 3 and experiment with it until you do. So how do you find out how many channels there are in this tune? Well it should be fairly obvious from these diagrams, that you just add 1 to a counter every time you find a 'channel enabled' byte in the channel settings are of the S3M - what we are reading right now. So with our example, it would only increase the NUMBER_OF_CHANNELS variable when it hits channel 2, 7, 8 and 14, thus telling us there is 4 channels. The next section will explain how each of these 32 channel bytes work. ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ³ °±² 2.5.1 The 32 Bytes ²±° ³ ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ Explanation: ============ There are 32 bytes here. 1 byte per channel. Read a byte. If it is smaller than 16 then its respective channel is enabled. Otherwise it is disabled. TECH.DOC from ST3 seems to say bit 8 set means that the channel is enabled. This isn't true, it is only enabled if the byte is less than 16. It most likely means that if bit 8 was set the channel is DISABLED, not enabled. 255 = a channel is disabled, and so does any channel with bit 8 set. (i.e 128) Pseudocode: =========== for count = 0 to 31 { read a byte if (the byte is <16) CHANNELS = CHANNELS + 1 } But wait!: ========== But wait! there's more! This byte also holds information if the channel is panned to the left or the right. - If the byte is smaller than or equal to 7, then it is panned to the LEFT - if the byte is greater than 7 and smaller than 16, then it is panned to the RIGHT. This isn't panning information, it is information whether the channel is left orientated, or right orientated. Panning comes later. The usual default positions to pan channels to are 03h, and 0Ch. (from an 00h-0Fh range) Wait again! There's more!: ========================= As the channels can be all over the place, it doesn't suit our 1234----- type method. Let us now look at remapping the channels to suit our method. ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ³ °±² 2.5.2 Remapping the channels ²±° ³ ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ Let us look at our example again. channels 0<ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ>31 --2----78-----E--------------- Everything from here in the actual S3M file, including pattern data is going use these channel values, 2,7,8 and E instead of a normal 0,1,2,3. What we have to do is remap the channel values into a more linear format like 0123-------, so when S3M tells us it wants channel 2, it actually writes to our channel 0, and if it asks for channel 7, then it actually writes to our channel 1 etc. The reason for this is that it would be stupid to allocate enough memory to contain all channels up to 0Eh. It would be better to allocate only enough for the 4 channels. To do this we need to map the channels as we find them enabled like this. channel value in file ³ what we want it to be stored as ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ 2 ³ 0 7 ³ 1 8 ³ 2 14 ³ 3 The usual method is to declare a new 32 byte array called REMAP, for example, and start looking for enabled channels. (by going through all 32 bytes in the file) - If an enabled channel is found, then use the channel number as the index to your REMAP array, and store the current channel counter in it. ie. '0'. so REMAP[2] = 0. - If we find another channel, then use it as the index to the REMAP array, and give it the new incremented channel counter. i.e. 1. so REMAP[7]=1 - If we find another channel, then use it as the index to the REMAP array, and give it the new incremented channel counter. i.e. 1. so REMAP[8]=2 - If we find another channel, then use it as the index to the REMAP array, and give it the new incremented channel counter. i.e. 1. so REMAP[14]=3 NOTE: The other remap values that are not used (i.e all those EXCEPT 2,7,8 and 14) should have previously been mapped to some impossible value, i.e. 255, so we don't have any confusion later on. Now we can safely reference the first channel of our system by looking up the relevant remap value. So when an S3M file tells us it has something for channel 2, it writes to our channel 0. Pseudocode: =========== declare a local 32 byte remap array called REMAP. We are now at offset 64 (40h). CHANNELS=0 set all 32 REMAP values to 255 first. (255 is an impossible channel value) for count = 0 to 31 { read a BYTE if (the BYTE was < 16) then { remap[count] = CHANNELS if (the BYTE <= 7) then PAN_VALUE[CHANNELS] is panned to LEFT else PAN_VALUE[CHANNELS] is panned to RIGHT CHANNELS = CHANNELS + 1 } } Now we know how many channels are in the S3M, as we added 1 to CHANNELS every time it was set, and also have set the pan values for each channel. 0,1,2 & 3. ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ³ °±² 2.6 Load Order Data ²±° ³ ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ Explanation: ============ This part is fairly simple. You read your order list in now, reading SONG_LENGTH number of bytes, the length of the song in orders that we found out in the main header at the start. (see SECTION 2.4) Remember the order list is how the song plays the physical patterns, i.e., in what order it plays them in. See FMODDOC.TXT, and SECTION 2.5 for more information on orders. Earlier on I told you to ignore the NUMBER_OF_PATTERNS byte that was supplied in the main header (SECTION 2.4), because it wasn't reliable. Well this is the part where you actually find out the number of patterns for yourself, by scanning through the order list and noting down the highest pattern number. And that's it - the number of patterns. ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ³ You CAN just use the supplied figure, but you would be allocating extra ³ ³ memory for patterns that aren't ever used, or that don't even exist. ³ ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ Apart from the actual pattern number, an order byte can have these two values. 254 = Marker Pattern, ignored. 255 = End of Song Marker. Pattern markers are just to be ignored, and an end of song marker tells us it should be the end of the song. What does this mean? Well it means that even though our original SONGLENGTH variable we read in was correct, it may contain marker values and will have to be recalculated! i.e., an order list is as follows. ORDER 0 ³ 1 ³ 2 ³ 3 ³ 4 ³ 5 ³ 6 ³ 7 ÚÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄ¿ PATTERN ³000³001³002³002³254³254³003³004³ ÀÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÙ Now the header will tell us that the SONGLENGTH is 8 for this example. This is right (0 - 7 including 0 equals 8 orders) but you see there are 2 marker patterns (the ones stored as 254), and they shouldn't be counted. So this means there are only really *6* orders that should be counted. So now we need another variable to keep count of the actual songlength. Look at the pseudocode below and see that I have used the variable A. When it is finished reading the order bytes, then set the actual SONGLENGTH to A. We again have to do a form of remapping also, to REMOVE the marker patterns from the order list. It is similar as we did in the channel settings section, but isn't as complex though, it is just a matter of setting order A to order COUNT. A, will always be smaller or equal than COUNT, as A isn't incremented if it encounters a marker pattern, but COUNT is. Pseudocode: =========== Should now be at offset 96 (60h). A=0 for COUNT=0 to SONGLENGTH { <- songlength is the value we loaded 1st if (ORDER[COUNT]<254) { <- if it is not a marker of any sort ORDER[A] = ORDER[COUNT] <- this rewrites the order list A = A + 1 <- now increment the actual songlength if (ORDER[COUNT] > NUMBER_OF_PATTERNS) then NUMBER_OF_PATTERNS = ORDER[COUNT] <- find highest pat# } } SONG_LENGTH = A <- now we store the real song length Storage issue: ============== As it is possible to have 255 orders, an order array that is 256 or so bytes big would be a good idea. i.e. declare it as ORDER[256] You will be safe with 256 orders as most songs are about 30 orders long and never do they even approach the 256 mark. (Some reach the early 100's.) Suggestion: =========== Check your patterns/orders against a reliable player and see if you get the same figures. It is pretty important to get right as you could do something stupid like play past the end of your pattern data into garbage and it could crash your player, or it could end the song too soon, i.e. before it should stop. ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ³ °±² 2.7 Parapointers ²±° ³ ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ Explanation: ============ Before I explain how these work, you are probably wondering - what are they? Well in S3M patterns and instruments are not saved in a rigid format like .MOD, i.e. instruments, followed straight away by patterns. In S3M they can literally be anywhere in the file, but they are all pointed too by these 'parapointers'. You must load these parapointers temporarily to find out where each instrument and pattern is in the file. The section in the file we are at now, contains these parapointers. Instrument Parapointers: ======================== The first set of parapointers encountered in the file are the instrument parapointers. The number of parapointers is the same as the number of instruments (of course). Each parapointer is stored as 2 bytes (a WORD), so the length in bytes for the instrument parapointer field is NUMBER_OF_INSTRUMENTS * 2. Store these away for later, using a WORD per parapointer. See the storage issue below on information about storing the parapointers. Pattern Parapointers: ===================== After the instrument parapointers follow the pattern parapointers. They are again 2 bytes each, and the number of pattern parapointers is the same as the NUMBER_OF_PATTERNS byte read from the header, NOT any value you might have calculated after throwing out marker patterns etc. As an offset in the file: ========================= To use the parapointers as a relevant value for the offset in the file, multiply it by 16, or shift it left 4 bits, and that is where the start of the data for that particular instrument or pattern is! Storage issue: ============== For the instrument and pattern parapointers, you could just declare an array of WORDs that can hold them all, say 99 for instruments, and 256 for patterns. this would require a 355 WORD array. Another way to do it is dynamically declaring a pointer to store these parapointers, and allocate only enough memory to hold all the parapointers. This is pretty pedantic though, as 710 bytes is not a lot to allocate locally/temporarily. Conclusion: =========== So in effect the number of bytes to read for the parapointer section is. (NUMBER_OF_INSTRUMENTS + NUMBER_OF_PATTERNS) * 2 and, To use them shift them left 4 bits, or multiply it by 16. Remember again, these 2 values (NUMBER_OF_INSTRUMENTS and NUMBER_OF_PATTERNS) are the values read in from the header at the start, not your final figures that might have discarded marker patterns etc. ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ³ °±² 2.8 Default pan positions ²±° ³ ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ Once the parapointers have been loaded for later, there is the next section in S3M that contains the default panning. In the main header the default panning byte was read (d.p). If it was 252 (which is incidentally FC in hexadecimal - har har), then you are allowed to read 32 bytes of panning information for each channel. If it is not 252, then it is not read and the next section is jumped to. Reading the panning bytes: ========================== Its not just as simple as reading in the pan values, no, PSI decided to make the pan values the bottom 4 bits of each byte, while the top 4 bits could contain any garbage.. I'm still not sure why you would save a format like this. (shrug). Anyway. Just bitwise AND each byte with 0Fh (15), to get the bottom 4 bits. Of course this means the pan values are from 0 to 15, considering it supports only 16 position panning. ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ³ °±² 2.8.1 Mono ²±° ³ ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ At this point it is the perfect time to check if bit 8 from the master volume byte in main header was set. If it WASN'T (0), then the mono flag in ST3 was set and all the panning values should be set to the MIDDLE, regardless of any other panning information given before. If the bit WAS set (1) then it should be stereo as normal and no further action is taken. Basic ways to check this bit are: ================================= if ((mastervol AND 128) == 0) make song mono if ((mastervol SHR 7) == 0) make song mono ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ³ °±² 2.9 Instruments ²±° ³ ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ It is time to read in the instrument information. This is the first useful data that we will be reading in now. Remember SECTION 2.7 - parapointers, because this is where we first use them. Reading an instrument is performed in the following fashion. - Use a file SEEK function to skip to the value pointed to by the parapointer value * 16. So seek to the relevant offset given by offset = parapointer[instnumber] * 16. (instnumber is the number of the instrument you are loading) Now read the following information in this order once for every instrument. In the following table, the term 'Offset' is described as relevant to the given parapointer offset, and not the start of the file. They are displayed as the decimal value first, followed by the hexadecimal value. ÄÄÄÄÄÄÄÄÂÄÄÄÄÄÂÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ Offset ³ Size³ Explanation. ÄÄÄÄÄÄÄÄÅÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ +0 00h³ 1 ³ TYPE - can equal 1 for sample, 2 for adlib melody, and 3 for ³ ³ adlib drum. We are only concerned about digital music ³ ³ here and want it to be 1, or 'sample'. +1 01h³ 13 ³ DOS FILENAME - this is a 13 byte DOS filename and can be ³ ³ safely ignored. +13 0Dh³ 3 ³ *MEMSEG - this section is explained below due to complexity. ÄÄÄÄÄÄÄÄÅÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ * MEMSEG: Memseg is another sort of parapointer to the actual sample data. You use it to find out where to read in the sample data from. To get the real value you multiply it by 16 again as you did with normal parapointers. The only problem with it, is that it is stored as a 3 byte value. The first byte is the upper part of the value, while the next 2 bytes are the lower word of the value. To get the real offset in bytes use the following method. 1. read a BYTE into PART1 2. read a WORD into PART2 3. sample_pos = (PART1 SHL 16) + PART2 You could also use this method reading 1 byte at a time. 1. read a byte into PART1 2. read a byte into PART2 3. read a byte into PART3 4. sample_pos = (PART1 SHL 16) + (PART3 SHL 8) + PART2 Storage issue: ============== Just use an array of 99 words, one for each sample which should be perfectly adequate. Suggestion: =========== When it is time to load these samples (you could actually do it now if you like - but it is better to first see if there is enough memory for pattern data before waiting for a long sample load), remember to multiply it by 16 to get the real offset in the file, and seek to it for each sample. Now Continuing with the map of the instrument.. ÄÄÄÄÄÄÄÄÂÄÄÄÄÄÂÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ Offset ³ Size³ Explanation. ÄÄÄÄÄÄÄÄÅÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ +13 0Dh³ 3 ³ MEMSEG - covered this just before.. +16 10h³ 4 ³ SAMPLE LENGTH - This is stored as a 4 byte value but as ST3 ³ ³ only supported 64kb samples, the first 2 bytes are used and ³ ³ the second 2 bytes are discarded (high part). ³ ³ So read a word, this is the sample length, and skip a word ³ ³ as they are never used. +20 14h³ 2+2 ³ LOOP BEGIN - This is stored as a 4 byte value also, but ST3 ³ ³ only supports 64kb samples, so the first 2 bytes are used and ³ ³ the second 2 bytes are discarded (high part). ³ ³ So read a word, this is the sample length, and skip a word ³ ³ as they are never used. ³ ³ Even if a sample has loop points, it does not mean that it is ³ ³ a looping sample. The [F] flags byte below tells us if the ³ ³ sample loops or not. +24 18h³ 2+2 ³ LOOP END - This is stored as a 4 byte value also, but ST3 ³ ³ only supports 64kb samples, so the first 2 bytes are used and ³ ³ the second 2 bytes are discarded (high part). ³ ³ So read a word, this is the sample length, and skip a word ³ ³ as they are never used. ³ ³ Even if a sample has loop points, it doesn't mean that it is ³ ³ a looping sample. The [F] flags byte below tells us if the ³ ³ sample loops or not. +28 1Ch³ 1 ³ VOLUME - this is the default volume of the sample. Read 1 ³ ³ byte and store it as the sample's default volume. +29 1Dh³ 1 ³ x - skip this byte it is unused +30 1Eh³ 1 ³ [P] - PACKING SCHEME. All S3M's should have unpacked samples ³ ³ so this byte should normally be 0. No info is supplied on ³ ³ DP3's packing method anyway. (if it ever existed) ³ ³ 0 = unpacked ³ ³ 1 = DP30ADPCM packing +31 1Fh³ 1 ³ [F] - FLAGS - There is only 1 value that should be checked ³ ³ here and that is the LOOP flag. It is checked by bitwise ³ ³ AND'ing it with 1. If the result is 1 then it is looped, ³ ³ otherwise it is not. The full range of bits are shown below. ³ ³ Stereo and 16 bit samples are not supported by ST3, so don't ³ ³ take any notice of them. (you may want to support 16 bit tho) ³ ³ BYTE AND 1 = Loop on. ³ ³ BYTE AND 2 = Stereo sample (not supported) ³ ³ BYTE AND 4 = 16 Bit sample (not supported) +32 20h³ 2+2 ³ C2SPD - This is the Middle C rate in HZ of the sample. It is ³ ³ used instead of finetuning from .MOD, and is explained in more ³ ³ detail in SECTION 4.1. ³ ³ ST3 only uses the first WORD (first 2 bytes), to store the ³ ³ C2SPD in, so ignore the second WORD (3rd and 4th byte). +36 24h³ 12 ³ UNUSED bytes. ST3 may store values in here but they are ³ ³ totally irrelevant to us. Some of the values that ST3 stores ³ ³ here are things like where to position the sample in the GUS ³ ³ etc. Hopefully you will ignore this and put them wherever ³ ³ you like. See TECH.DOC for more on this useless information. +48 30h³ 28 ³ SAMPLE NAME - This is the name of the sample and should be ³ ³ stored if you are prepared to print it out for an interface. +76 4Ch³ 4 ³ 'SCRS' label. This is here mainly for verification. Check ³ ³ that these 4 bytes are indeed 'SCRS' if you feel the need. ÄÄÄÄÄÄÄÄÅÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ Storage Issue: ============== In this case it might be better to use a more dynamic system of instrument storage, instead of rigidly having an array of 99 instrument structures like was used in .MOD (31 in that case). Especially seeing as though S3M can actually have a variable number of instruments, using memory for instruments that are not used is wasteful. If you are considering expanding to bigger formats like XM, then it is fairly necessary considering XM can contain 256 instruments which are rather large in themselves. Make the instrument structure a pointer to a structure instead, and allocate memory dynamically for it as it comes along, and deallocate the memory when the song is stopped. Suggestion: =========== Make sure all your values are loaded in properly by printing them out one after the other, and comparing them with Scream Tracker 3 or any other reputable player. (I wouldn't compare the sample lengths to FMOD, as FMOD clips samples at their loop end points - but otherwise FMOD is fine) ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ³ °±² 2.10 Loading pattern data ²±° ³ ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ This is the "trickiest" part of loading S3M, although it isn't very hard if you set it out logically. Each pattern consists of 64 rows, and the number of channels is defined by the value you worked out previously. If you are using one big pattern data buffer, then is time to allocate the memory for all of your pattern data buffer. The size of the buffer must be determined first. ie. NUMBER_OF_PATTERNS * CHANNELS * 64 * 5. (5 if you use 5 bytes per note) See SECTION 2.10.1 for more information on why 5 it should be bytes per note. Otherwise if you are using separate pattern buffers, ie one buffer for each pattern, then you should be allocating each pattern as it comes along, using the same formula above but removing the NUMBER_OF_PATTERNS part. ie. CHANNELS * 64 * 5. See FMODDOC.TXT and SECTION 2.6 for more information on issues concerning pattern storage. Remember to only load and allocate memory for the number of patterns you worked out previously, skipping marker patterns. If you include marker patterns then you are allocating memory that is never used and is wasted. See SECTION 2.6 on more about skipping marker patterns etc. Reading a pattern: ================== At this point in the file you should be seeking to each pattern as it comes, using the pattern parapointers. If you stored the instrument parapointers and the pattern parapointers in the one parapointer array, then of course the pattern parapointers come after the instrument parapointers. So start checking the parapointers from the offset given by the NUMBER_OF_INSTRUMENTS+1, as this is where the pattern parapointers would begin. Then do the same by looping NUMBER_OF_PATTERNS number of times, seeking to each parapointer value multiplied by 16 each time. Now here is some information on how to read in 1 pattern. Repeat this process for as many times as there are patterns. Top of the pattern: =================== Read 2 bytes. This is the length of the packed pattern data. This doesn't usually need to be bothered with as you know when to stop after reading 64 rows worth of data. This value is usually in case you want to read in the whole packed pattern into memory first, and then unpack it from memory, not disk. Now repeat from here until you have reached row 64: =================================================== Read 1 byte. If this is 0 then the end of the row has been read, increment the row to the next one and repeat this step. If row 64 has been reached, then start again from the top. If row 64 still hasn't been reached and the byte is > 0 then continue below. AND the byte with 31. This is the channel the following information belongs too.... AND the byte with 32. If this is bigger than 0, then 2 more bytes follow to be read. The NOTE value, and the INSTRUMENT number. AND the byte with 64. If this is bigger than 0, then 1 more byte follows to be read. This is the VOLUME BYTE value. AND the byte with 128. If this is bigger than 0, then 2 more bytes follow to be read. The EFFECT number, and the EFFECT_PARAMETER. Channels Remapped: ================== Simple huh, but remember about the REMAPPED CHANNELS, if you have done so. Remember SECTION 2.5.2 and how S3M allows the song to be written using any channels you like from 1 to 32, but take the example - as long as only channels 4 and 15 are used, it is only a 2 channel song. Our method was to remap these used channels into a linear format that goes in order from 0,1,2,3 onwards. So from above, 4 would be remapped to 0, and 15 would be remapped to 1. This was achieved by using an array of remap values, and each literal used channel value contained the linear value of the channel. ie. REMAP[0] = 255 <-- impossible value REMAP[1] = 255 <-- impossible value REMAP[2] = 255 <-- impossible value REMAP[3] = 255 <-- impossible value REMAP[4] = 0 <-- CHANNEL 0 IN OUR INTERNAL FORMAT REMAP[5] = 255 <-- impossible value ... REMAP[15] = 1 <-- CHANNEL 1 IN OUR INTERNAL FORMAT REMAP[16] = 255 <-- impossible value ... REMAP[31] = 255 <-- impossible value Remember 255 is an impossible channel value so you are sure not to reference the channel if it contains such an impossible value as 255. This should now make sense to you now, and you should also have worked out that it is trivially simple to reference the correct channel. When the byte read in the S3M is AND'ed with 31 to get the channel number, set the channel number to: CHANNEL = REMAP[BYTE AND 31] From there on you just reference the 'CHANNEL' variable, and things will be sorted into your pattern the way they should be, with no fuss at all. Found 'CHANNEL' and it is bigger than 'NUMBER_OF_CHANNELS': =========================================================== If the channel value actually referenced a channel outside of our calculated range of channels, then there must have been some sort of bug in the S3M saving routine of the tracker, or notes could have been entered in unused channels. ie. If we have a 16 channel song, and the channel byte tells us it wants to load using channel 25, then we must ignore all packed information that comes next. One way to do this is to load the next set of data not into our pattern data, but into a dummy value. The note must still be unpacked (to move the file pointer forward), but it must be ignored. A packed note simplified: ========================= To recap: Read 1 byte: 0 = end of row AND 31=Channel Number AND 32=read 1 byte for note, read 1 byte for instrument AND 64=read 1 byte for volume column value AND 128=read 1 byte for effect number, 1 byte for effect param. If Channel Number > Number of channels, set note pointer to dummy note else set note pointer to correct position in pattern data. Pseudocode: =========== So here is some MORE example psuedocode for reading all patterns. It is based on my loader so should be more practical than trying to chew on all the above pseudocode. (ALLOCATE MEMORY): for the pattern data buffer, IF you are using one big pattern data block instead of separate pattern buffers for each pattern. FOR: count=0 to NUMBER_OF_PATTERNS { SEEK TO: parapointer[count] * 16 READ 2 BYTES: this is the size of the packed pattern. (ALLOCATE MEMORY): for 1 pattern IF you are using a separate buffer for each pattern, and not just one huge block. ROW = 0 WHILE: (ROW < 64) { READ 1 BYTE: into TEMP IF: (TEMP > 0) { CHANNEL = REMAP[TEMP AND 31] <- remember whatever S3M gives us will be converted to a normal value in order from 0,1,2 etc. IF: (CHANNEL < NUMBER_OF_CHANNELS) then set a pointer to relevant position in pattern data, according to PATTERN, CHANNEL and ROW. ELSE: set the pointer to a DUMMY position, so any information is read into nowhere. We don't want to know about it if the channel byte just read was bigger than the actual number of channels. if ((TEMP AND 32) > 0) { <-- a note is available READ 1 BYTE: into TEMP2 IF: (TEMP2 == 255) there is no note ELSE IF: (TEMP2 == 254) the note is a KEYOFF note ELSE: note = ((TEMP2 SHR 4)*12) + (TEMP2 AND 0Fh) READ 1 BYTE: store as INSTRUMENT_NUMBER } if ((TEMP AND 64) > 0) <-- a volume byte is avail READ 1 BYTE: store as the VOLUME_COLUMN if((TEMP AND 128) > 0) { <-- an effect is available READ 1 BYTE: store as the EFFECT_NUMBER READ 1 BYTE: store as the EFFECT_PARAMETER } } ELSE: ROW = ROW + 1 } } Suggestion: =========== There you are. I don't expect you to code this perfectly in the first try, but it isn't really too difficult. I think it is essential that you write a pattern viewing function, that displays pattern data in the same format a tracker would. ie. C-2 01 -- 000|--- 00 -- 000|--- 00 -- 000|--- 00 -- 000|--- 00 -- A06| --- 00 -- 000|--- 00 -- 000|--- 00 -- 000|--- 00 -- 000|--- 00 -- 000| --- 00 -- 000|D#2 01 -- 000|--- 00 -- 000|--- 00 -- 000|--- 00 -- 000| --- 00 -- 000|--- 00 -- 000|--- 00 -- 000|--- 00 -- 000|--- 00 -- 000| --- 00 30 000|--- 00 -- 000|G-2 01 -- HA2|--- 00 -- 000|--- 00 -- 000| --- 00 20 000|--- 00 -- 000|--- 00 -- H00|--- 00 -- 000|--- 00 -- 000| --- 00 10 000|--- 00 30 000|--- 00 -- H00|C-3 01 -- 000|--- 00 -- 000| --- 00 05 000|--- 00 20 000|--- 00 -- H00|--- 00 -- 000|--- 00 -- 000| A simple display like this will show that you have loaded in your pattern data perfectly, and that your player will run through it without problems. (If it doesn't fit on an 80 column screen then just view say 7 channels at a time, but at different offsets - ie. view from channels 4 to 11 instead of 0 to 6) If it DOES run into problems, then this display will EASILY and QUICKLY pinpoint them. Say if your player played a wrong note at order 10, row 22, then all you would have to do is bring up the pattern and find out why it is a problem. If you really want to know how to display your note value as a C-2 type value instead of a number, then just do it like this notetable[12][2] = { "C-", "C#","D-","D#","E-","F-","F#","G-","G#","A-","A#","B-" }; then print it like this : notetable[noteval MOD 12] (Modulus/Remainder) followed by : noteval/12 (octave) ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ³ °±² 2.10.1 Now 5 bytes? ²±° ³ ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ In MOD you could get away with using 4 bytes per note, or even 3 if you were clever, but S3M is a different story, because just about every aspect of the note is expanded, and needs more space. The note range is many octaves larger, there are many more instruments, there is an extra volume column, and there are more effects. Here is a table of the note contents, and their ranges and the size in bits needed to store the values. Description Range Size Needed ======================================== Note : 1-108 7 bits (108 = 12 notes * 9 octaves) Instrument : 1-99 7 bits Volume byte : 0-64 6 bits Effect Number : 0-26 5 bits (effects A to Z) Effect Parameter : 0-255 8 bits ======================================== Total : 33 bits. (4 bytes + 1 bit!) This is the least amount of space you could use, and you will probably want to expand to more instruments and effects, so 5 bytes is the right number of bytes to use. Try and take off that extra 1 bit in the above table and you will soon see that it is impossible. No field can be cut in half with it's range, which is effectively what you do when you take a bit away. Just use 1 byte for every aspect, and don't bother about using bit allocation as you might have in .MOD routines. (to squash info into less bytes) Then there is plenty of room for expansion. You can still fit more effects, and more instruments. If you are worried about the amount of memory your pattern data is taking, it's time to use pmode or internal pattern packing :) Suggestion: =========== A good method I use with a note is to make a structure containing this note information and call it a 'note structure'. When you are looking through pattern data, all you have to do is point this note structure at the correct position in the pattern buffer, and the buffer fills each field in the structure with the correct values. The method is more English than using buffer_offset + 1, buffer_offset + 2, etc., to get each value out of the note. (ie. using offsets to pick out effect number, volume byte, etc., like you might do in assembler more often than not.) ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ³ °±² 2.11 Loading sample data ²±° ³ ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ That's just about it! The next section is easy, it is just uploading the actual sample data to your soundcard, or loading each sample into memory for mixing. Loop NUMBER_OF_INSTRUMENT number of times, and remember to SEEK to the position given by the MEMSEG portion of the instrument data MULTIPLIED by 16. FOR (count = 0 to NUMBER_OF_INSTRUMENTS-1) { SEEK: to sample_memseg_value * 16 UPLOAD: SAMPLE_LENGTH number of bytes to sound card or memory (see below) } Unsigned data: ============== All sample data is unsigned in ST3, so if your sound routines use signed samples (GUS does for example), then you will have to XOR each byte with 128. Another way to do this is just to ADD 128 to each byte which gives the same result. We are just trying to get the negative bit off each byte and make it 0-255 instead of -128 to 127. If you also have a MOD loader, then chances are that you WILL have to XOR these samples as MOD uses signed values (as did your old music system) ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ³ °±² : SECTION 3: ²±° ³ ³ °±² Extra S3M Features ²±° ³ ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ³ °±² 3.1 Volume byte ²±° ³ ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ One exciting feature of S3M, is the fact that there is an extra number in each channel, that allows you to set the volume of the channel at any time. This is good for having volume changes, as well as performing other effects in the effect column, which would normally have been impossible with older formats. C-2 01 -- 000 (play sample 1 at C-2, using it's default volume of 64) --- 00 -- 000 --- 00 20 000 (now set the volume to 20h) --- 00 -- 000 --- 00 10 000 (now set the volume to 10h) --- 00 -- 000 --- 00 05 000 (now set the volume to 5h) --- 00 02 000 (now set the volume to 2h) --- 00 01 000 (now set the volume to 1h) --- 00 00 000 (now set the volume to 0h) To code this is trivially simple. All that needs to be done is to look up this volume byte on every note, and check if there is a valid value stored there. The volume byte is processed just before the effects, and just after the note/instrument values are processed. The only minor hitch is how to store the byte if it has no value. One way, and the method I use is to store a "no volume byte" value as an impossible value, such as 255. Therefore everytime I check if it is not 255, then a volume byte value must be present and it is set. The other way is using internal pattern compression, and have a bit set if a volume byte exists. I will not go into internal pattern compression as it is quite advanced and raises many problems. ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ³ °±² 3.2 Panning ²±° ³ ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ S3M basically supports 16 position panning. It is achieved by default panning, and the use of the S8x effect (Set panning position). ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ³ °±² 3.3 New effects ²±° ³ ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ These effects are new compared to those of the .MOD format. EEx - Extra Fine portamento down. (4 times finer than normal fine porta) FEx - Extra Fine portamento up. (4 times finer than normal fine porta) Ixy - Tremor. (turns volume on/off at certain rate) Uxy - Fine Vibrato. (4 times finer than normal vibrato) Vxx - Global Volume. (sets a multiplier for the volume of the whole song) These effects are from .MOD but are different. Internally they should be treated as different effects. Axy - Set Speed (allows 1-FFh instead of just 1-1Fh) Qxy - Retrig + Volume slide (never had the volume part of retrig in .MOD) Kxy - Vibrato + Volume Slide (the parameters given here are remembered) Lxy - Porta + Volume Slide (the parameters given here are remembered) Dxy - Volume slide (contains ability to fine volume slide up or down in the same command. Also has fast volume slides) See the effects SECTION 6 for more information on how to code for these new effects. ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ³ °±² : SECTION 4: ²±° ³ ³ °±² More Periods ²±° ³ ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ³ °±² 4.1 Goodbye fine tune!!! - C2SPD ²±° ³ ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ This is my favorite part about S3M, and is called the C2SPD. With .MOD, it used finetunes that altered the pitch finely, using values from -8 to +7. To adjust the frequency you had to look up a table which stored 8 finetune values between each semitone. In S3M, finetuning is gone and it is replaced with a more accurate and less hassle free system. Instead of a finetune, each sample is given a Middle C rate in HZ. i.e the normal middle C rate for a sample is 8363 hz. The Middle C rate is called the C2SPD, (or C-2 speed. Middle C is actually C-4 in S3M, so it should be called C4SPD, but the name was from the old Scream Tracker 2, and it never changed.) If a sample does need to be tuned, then the C2SPD rate of the sample can be altered, i.e. less than 8363 would drop the pitch of the sample, and more than 8363 would raise the pitch of the sample. When playing, the pitch/frequency is calculated runtime, by multiplying the period from the note table by 8363, then dividing it by the instrument's C2SPD value. This alters the pitch according to the C2SPD. So if the instrument's C2SPD was 8363, then there would be no change in the pitch. A new frequency table is used also, one that does not contain the finetune values like .MOD did, because we won't need them. But this new table has a lot more octaves, so one is included below for your enjoyment in SECTION 4.2. Here is the basic formula for calculating the real period according to the note value and the instrument's C2SPD. ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ³ current_period = 8363 * periodtab[note] / instrument's C2SPD ³ ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ TECH.DOC shows a different formula, but it is slower and more cumbersome to implement. This formula here shows the guts of the problem and is quite adequate. You can inspect TECH.DOC's version in the appendix if you wish. To get the rate in hz, then the following formula is used: ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ³ speed_hz = 14317056L / period ³ ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ To get the period value in amiga/MOD terms, then just divide the period by 4, or shift it right 2 bits. The reason ST3 periods are 4 times bigger are to allow for extra fine slides etc which are 4 times more accurate. You would now use the HZ figure to set the frequency on your sound card. ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ³ °±² 4.2 9 Octaves ²±° ³ ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ Not using finetunes means a new period table. Here is one I calculated myself and is perfectly reliable. It is just plain note values, without finetunes like .MOD used, as C2SPD takes care of these fine adjustments. Just using note values like this makes things a lot easier without having to worry about 8 fine tune values between each semitone. (By now this is probably well drilled into your head) C C# D D# E F F# G G# A A# B ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ 27392,25856,24384,23040,21696,20480,19328,18240,17216,16256,15360,14496, ³ 0 13696,12928,12192,11520,10848,10240, 9664, 9120, 8608, 8128, 7680, 7248, ³ 1 6848, 6464, 6096, 5760, 5424, 5120, 4832, 4560, 4304, 4064, 3840, 3624, ³ 2 3424, 3232, 3048, 2880, 2712, 2560, 2416, 2280, 2152, 2032, 1920, 1812, ³ 3 1712, 1616, 1524, 1440, 1356, 1280, 1208, 1140, 1076, 1016, 960, 906, ³ 4 856, 808, 762, 720, 678, 640, 604, 570, 538, 508, 480, 453, ³ 5 428, 404, 381, 360, 339, 320, 302, 285, 269, 254, 240, 226, ³ 6 214, 202, 190, 180, 170, 160, 151, 143, 135, 127, 120, 113, ³ 7 107, 101, 95, 90, 85, 80, 75, 71, 67, 63, 60, 56 ³ 8 These next 2 octaves below, are included if you want to support the entire FT2 octave range of .MOD. DOPE.MOD uses these period values for example, and are only used when loading .MOD, not for anything else, as S3M doesn't even use these octaves. (it does indeed stop at B-8) 53, 50, 47, 45, 42, 40, 37, 35, 33, 31, 30, 28, ³ 9 26, 25, 23, 22, 21, 20, 18, 17, 16, 15, 15, 14, ³ 10 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ³ °±² : SECTION 5: ²±° ³ ³ °±² Miscellaneous ²±° ³ ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ³ °±² 5.1 The 10 steps in converting a MOD player to an S3M player ²±° ³ ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ Explanation: ============ Ok, so you have a .MOD player working great, and now you want to expand to support S3M. There are only 10 major steps in achieving this, and are listed below. One thing I DO suggest, is to first make your player actually play .MOD like it is an S3M, without even writing an S3M loader, or supporting S3M in any way. It just sets up your player engine and then all you have to do is write an S3M loader, and voila, it should fit nearly perfectly. These are the steps you should take in order of priority. ÚÄ¿ ³1³ ÀÄÙExpand all channel array sizes from 4 or 8, to 32. 8 is if you were supporting 8CHN .MOD, it would have been 4 if you were only supporting 4 channel M.K. mods. i.e. volume[8] becomes volume[32] portaspeed[8] becomes portaspeed[32] etc. ÚÄ¿ ³2³ ÀÄÙImport/rip/calculate a new period table, i.e. the one from SECTION 4.2, S3M periods do not use finetune values, so some changes will have to be made. Step 7 covers this. Again see SECTION 4 for more information on the new period system. ÚÄ¿ ³3³ ÀÄÙRename your finetune variable in the sample information to C2SPD instead. Make sure it is a WORD size at it has to support values like 44100 hz ÚÄ¿ ³4³ ÀÄÙIn your MOD loader, when loading in the fine tune value, convert it to C2SPD. You do this by looking up what finetune matches what C2SPD. i.e. Like this. (this is a switch statement I use that is fed the value read from the file, and returns the C2SPD to match it) case 0 : return 8363; case 1 : return 8413; case 2 : return 8463; case 3 : return 8529; case 4 : return 8581; case 5 : return 8651; case 6 : return 8723; case 7 : return 8757; case 8 : return 7895; case 9 : return 7941; case 10 : return 7985; case 11 : return 8046; case 12 : return 8107; case 13 : return 8169; case 14 : return 8232; case 15 : return 8280; default : return 8363; This same macro/function/lookuptable should also be used for the Set Fine Tune effect. See SECTION 6.19 for more information on this. ÚÄ¿ ³5³ ÀÄÙWhen loading in the periods from the file when reading in MOD pattern data, there is no need to multiply by 8 any more to account for fine tunes, because we are not using them. All you do is search the NEW period table one by one until it matches, and give it a note value. BUT. As a MOD C-2 is the same pitch as an S3M type C-4, our new period table supports 2 new octaves BELOW the normal MOD range (ie a MOD C-0 is only a C-2 in S3M). This means you have to virtually start searching the table from 24, which is a C-0 in MOD, but a C-2 in S3M. When a value is found then 24 has to be subtracted from the position where it was found. Here is the new pseudocode from converting a .MOD period value to a note number, when reading it from the file. This pseudocode actually searches 9 octaves (12*9 = 108) and not 3, as some .MOD files allow this many. FT2 for example can save with this many octaves. PseudoCode: =========== This psuedocode searches from C-2 in our S3M table (which is same as C-0 in MOD), right through to 131, which is B-8 in .MOD, but B-10 in S3M.. There is no such thing as B-10 in S3M so these last 2 octaves are only used for loading extremely high octave mods (ie DOPE.MOD). note=NONOTE (NONOTE can be an impossible value like 255 etc.) for (count = 0 to 107) if (period >= PERIOD_TAB[count+24]) { NOTE = count; BREAK FROM LOOP Ä¿ } ³ } ³ <ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ As it is searching with 'count+24', when we actually find the value and assign it to NOTE, 'count' is used, giving a note value 24 less (2 octaves) than the position we stopped at. If that logic is hard to visualize, try something like this which achieves the EXACT same result: note=NONOTE for (count = 24 to 131) if (period >= PERIOD_TAB[count]) { NOTE = count-24; BREAK FROM LOOP Ä¿ } ³ } ³ <ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ ÚÄ¿ ³6³ ÀÄÙIn your new note information/internal pattern data format, you need a volume column byte. When loading mods, they don't have a volume byte, so assign the volume byte on EVERY note an impossible value, that you will scan for while it is playing. If it is encountered then it is ignored. I use 255 for example, and I know not to process the volume byte if it is this value. ÚÄ¿ ³7³ ÀÄÙIn your playing code, SCRAP the old method of finding the period, as you should not have any more references to finetuning. i.e. PERIOD = OLD_PERIOD_TAB[ NOTE + last instrument's FINETUNE ] Replace it with this: ===================== PERIOD = 8363 * PERIOD_TAB[NOTE] / instrument's C2SPD * POSSIBLE BUG - remember that if the tune references a sample that is not used, you may have the unused sample's C2SPD as 0. This will cause a DIVIDE ERROR, so make sure you set all 99 to something like 8363 previously, or just bypass the formula if the C2SPD does happen to be 0. See SECTION 4.1 for more information on C2SPD. ÚÄ¿ ³8³ ÀÄÙEffects that affect the frequency, like all the Portas, and Vibratos, need to have the parameters multiplied by 4 when being used, as S3M uses 4*bigger period values than MOD does. (for extra fine slides etc) i.e. Instead of effect 1xy - (porta up), being - subtract XY from frequency, it should be: - subtract (XY*4) from frequency. A shift left by 2 bits would be beneficial here instead of actually multiplying by 4. ÚÄ¿ ³9³ ÀÄÙArpeggio. Remove the *8 reference, as we don't need to add the parameter given multiplied by 8 fine tunes this time, as there are no finetunes. All we have to do is add the parameter given! (remember, the new period table only has note values, finetunes are dead!) ÚÄÄ¿ ³10³ ÀÄÄÙAdd support for the new S3M effects that are different from any MOD effects. These include Fine Vibrato, Extra Fine Portamentos, Tremor, Retrig+VolumeSlide, etc. See the effects section on how to implement each effect. That's just about it! After these 10 steps are completed you should have an S3M player based on your old mod player, but just as good! The support should be nearly 100% accurate if you followed all these steps properly. Suggestion: =========== The *tiny* things you might trip on are things like - ST3's vibrato is different to a Protracker Vibrato, because protracker sets the frequency on every tick 0, whilst ST3 doesn't. ST3's vibrato sounds a LOT nicer on long big slow vibratos, than protracker, which resets to the middle frequency on every tick 0 and makes it sound very grating and harsh, almost like an arpeggio. This is just an example if you want to be a real perfectionist. Also there is the matter of fast volume slides, which is encountered in SECTION 6.4 - Volume Slide. There are other minor issues like this that you will probably find out. ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ³ °±² 5.2 Creating the ultimate internal format of your own ²±° ³ ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ You don't have to stop at S3M's limitations. The fun part of developing a music system is to make something that is better. I would not be hard to have better support than S3M, by including some of the following features. It will still be downwards compatible with S3M, but exceeds it in capability. (like S3M is to MOD). This is just a starting point, as there are many more things you can put into a music format, as XM does. S3M actually supports some of the things in this list anyway. 32 channels 9 octave support 16 bit support 32 bit sample sizes (ie 0-4gig) 256 orders 256 patterns 256 instruments extended instruments (ie envelopes, split keyboard, relative note, bidi loop) I guess you could say this is XM :) ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ³ °±² : SECTION 6: ²±° ³ ³ °±² Effects ²±° ³ ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ Note: ===== Most of the effect information here has been cut and pasted directly from FMODDOC.TXT, as S3M effects are EXACTLY the same as .MOD effects, except for a few differences which are gone into in detail. All references to mod have been removed and changed to S3M, and it can be used as if it were a document made just for S3M. ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ³ °±² 6.01 Quick Reference Chart ²±° ³ ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ Key: ==== T0/IN/BOTH: T0 - means the effect is only processed on tick 0. IN - means the effect is processed on every other tick besides 0. BOTH - means the effect is processed on EVERY tick. REMEMBER?: Means the effect remembers its parameters from the last time if it was given any. Otherwise it just uses the parameters stored. These effects are usually processed on other ticks, and the parameters are stored on tick 0, if the effect occurs. ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ³ S3M MOD Description T0/IN REMEMBER?³ ³ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄij ³ A F Set Speed T0 N ³ ³ B B Pattern Jump T0 N ³ ³ C D Pattern Break T0 N ³ ³ D A/EA/EB Volume Slide/Fine Volume Slide up/down BOTH Y ³ ³ E 2/E2 Porta Down/Fine Porta Down/Xtra Fine Porta BOTH Y ³ ³ F 1/E1 Porta Up/Fine Porta Up/Extra Fine Porta Down BOTH Y ³ ³ G 3 Porta to note IN Y ³ ³ H 4 Vibrato IN Y ³ ³ I - Tremor BOTH Y ³ ³ J 0 Arpeggio IN N ³ ³ K 6 Vibrato+Volume Slide IN Y ³ ³ L 5 Porta+Volume Slide IN Y ³ ³ M - - ³ ³ N - - ³ ³ O 9 Sample Offset T0 Y ³ ³ P - - ³ ³ Q E9 Retrig + Volume Slide IN Y ³ ³ R 7 Tremolo IN Y ³ ³ S0 E0 Set Filter on/off T0 N ³ ³ S1 E3 Set Glissando on/off T0 N ³ ³ S2 E5 Set FineTune T0 N ³ ³ S3 E4 Set Vibrato Waveform T0 N ³ ³ S4 E6 Set Tremolo Waveform T0 N ³ ³ S5 ³ ³ S6 ³ ³ S7 ³ ³ S8 E8 Set Pan Position T0 N ³ ³ S9 ³ ³ SA - Stereo Control T0 N ³ ³ SB E6 Pattern Loop T0 Y ³ ³ SC EC Note Cut IN N ³ ³ SD ED Note Delay IN N ³ ³ SE EE Pattern Delay T0 N ³ ³ SF EF Funk Repeat (invert loop) T0 N ³ ³ T F Set Tempo T0 N ³ ³ U - Fine Vibrato IN Y ³ ³ V - Global Volume T0 N ³ ³ W ³ ³ X ³ ³ Y ³ ³ Z ³ ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ With S3M, the effects are listed as Axy, Bxy etc, but in the file they are actually stored as 1, 2 etc, respectively. So a 1 in the file = effect A a 2 in the file = effect B etc. ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ³ °±² 6.1 Effect Axx (Set Speed) ²±° ³ UPDATED: T0? [Y] : INBETWEEN? [N] ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ This effect sets the speed of the song. E.G: --- 00 -- A07 (Set the speed of the song to 7 ticks a row) Range: xx = 01h-FFh This is simply a case of setting the SPEED variable in your player to the value supplied. It is allowed a range of 1 to FFh, and differs to .MOD which only allows speeds of up to 1Fh. The default speed of a song is 6. ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ³ °±² 6.2 Effect Bxx (Pattern Jump) ²±° ³ UPDATED: T0? [Y] : INBETWEEN? [N] ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ This effect jumps to a specified channel (in hex) E.G: --- 00 -- B10 (Jump to order 10h, or 16 decimal) Range: xx = 00h-FFh This effect is fairly simple, after the row has been processed including it's effect, then the ORDER variable is set to the given parameter, and the song continues playing from row 0. Make sure you don't jump over the end of the song length, and if you do then set it to the first order. See FMODDOC.TXT for more peculiarities about this effect, especially when combined with pattern break. ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ³ °±² 6.3 Effect Cxx (Pattern Break) ²±° ³ UPDATED: T0? [Y] : INBETWEEN? [N] ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ This effect breaks to the next pattern starting at the specified row. E.G: --- 00 -- C32 (Break from this here and start at row 32 on the next pattern) Range: xx = 00h-63Fh (it is stored as hexadecimal but is read as decimal) This effect is similar to effect Bxy or pattern jump. You only jump to the next pattern though, and you start tracking again at the specified row. The row should not be bigger than 63, and if it is take it as 0. It works something like this: - increment order (only once, some mods have more than 1 pbreak on a row which could cause an increment order twice or more!) - set row to be x*10 + y. (we have to get the decimal value not the hex) * Remember that it is possible to put more than 1 pattern break on a row, but we don't want to increment the order every time, we only want it to increment the row only once. So *set a flag when a pattern break occurs*, and only increment the order if that flag has not been set. * Also set a flag when pattern jump occurs (Bxy), as if a pattern jump effect has been called then the order should not be incremented either. See FMODDOC.TXT for more information on using effect Bxy and Dxy on the same row. The pseudocode would look something like this: if (effect = C) then ROW = (parameter_x * 10) + parmeter_y - 1 if (ROW > 62) row = -1 if (BREAKFLAG = 0 and JUMPFLAG = 0) then ORDER = ORDER + 1 if (ORDER >= SONGLENGTH) ORDER = 0 BREAKFLAG = 1 See FMODDOC.TXT for more peculiarities about this effect, especially when combined with pattern jump. ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ³ °±² 6.4 Effect Dxy (Volume slide) ²±° ³ UPDATED: T0? [Y] : INBETWEEN? [Y] ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ This effect causes the volume of the track to slide up or down. The parameter for this effect is remembered for every track, so an effect D00 will use the last parameter given. E.G: A-2 01 -- D01 (slide the volume down 1 * (speed-1) units) --- 00 -- D10 (slide the volume up 1 * (speed-1) units) --- 00 -- DF1 (slide the volume down 1 unit (fine slide)) --- 00 -- D1F (slide the volume up 1 unit (fine slide)) --- 00 -- DFF (slide the volume up 0Fh units (fine slide)) --- 00 -- D00 (no parameter means use last one. In this case slide up 0Fh) Range: x = amount to slide volume up by or, (0h-Eh, F means fine slide down) y = amount to slide volume down by. (0h-Eh, F means fine slide up) - D0y = slide down by y * (speed -1) units - Dx0 = slide up by x * (speed -1) units - DFy = slide down by y units on tick 0. - DxF = slide up by x units on tick 0. When it says slide by (speed -1) units, this isn't always true, see below about fast volume slides. This is a case of sliding by (speed) number of units. Remember the parameters: ======================== Remember that this volume slide command remembers it's last parameters, so you need a new variable that stores 32 channels worth of volume slide parameters. You only store something in this variable if the parameter given is > 0. From there on you check this variable for it's parameters, NOT the parameter just passed from the pattern data. i.e. If the actual parameter is 00, then the variable will be used. If the actual parameter is >0, then the parameter will be stored, and again, the variable will be used. See the pseudocode/description below for expansion on this last remark. (LAST_VOLUME_SLIDE actually refers to the LAST_VOLUME_SLIDE[CHANNEL], or the current channel's value. This is because we store 32 different values and not just one for the whole song.) Psuedocode/description for TICK 0: ================================== - if there is a parameter then set the LAST_VOLUME_SLIDE variable for that channel to it. - if the lower 4 bits of LAST_VOLUME_SLIDE = 0Fh, add to the track's volume the value that is in the upper 4 bits. - else, if the upper 4 bits of LAST_VOLUME_SLIDE = 0Fh, subtract from the volume the value that is in lower 4 bits. - if the volume for the track is > 64, then the volume for the track = 64. - if the volume for the track is < 0, then the volume for the track = 0. (slide up gets checked first, or gets priority as DFF means slide up by 0Fh) Pseudocode/description for INBETWEEN TICKS: =========================================== - if the LAST_VOLUME_SLIDE variable's lower 4 bits = 0, then add what is in the upper 4 bits value to the volume. - if the LAST_VOLUME_SLIDE variable's upper 4 bits = 0, then subtract what is in the lower 4 bits from the volume. - if the volume for the track is > 64, then the volume for the track = 64. - if the volume for the track is < 0, then the volume for the track = 0. - Set the volume for the track. Fast Volume Slides: =================== There is a bug ST3 used to have, in which it used to volume slide on EVERY tick, and not just the other ticks beside tick 0 like it should. This is only for D0y and Dx0, not the fine slides. When loading your tune you should have checked for a few things in the S3M header. - Cwt/v = 1300h means that fast volume slides are used. - Flags AND 64 means if the flags section AND'ed with 64 is true, then it also uses fast volume slides. This flag is set automatically if Cwt/v was 1300h Now here you should have set a special flag variable in your player that tells you to slide on tick 0 also, instead of just the other effect ticks like normal. It would be a simple case of checking that this flag is set and performing the extra slide on tick 0. Don't leave it out as there are a lot of S3M's that use fast volume slides. ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ³ °±² 6.5 Effect Exy (Portamento Down) ²±° ³ UPDATED: T0? [Y] : INBETWEEN? [Y] ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ This effect slides the pitch of the sample playing downwards, specified by the parameter given. It also contains the capability of doing fine portamento down, and extra fine portamento down. If no parameter is given, then the last portamento command given is used. E.G: C-2 01 -- E01 (slide the pitch down by (1*(speed-1)*4) units) --- 00 -- E20 (slide the pitch down by (20*(speed-1)*4) units) --- 00 -- EF7 (fine slide the pitch down by (7*4) units) --- 00 -- EE1 (extra fine slide the pitch down by (1) unit) --- 00 -- E00 (continue with another extra fine slide of 1) Range: xy = amount to slide pitch down by (0h-FFh) As this command in fact contains 3 different commands, they must be looked at separately. In summary it works like this: ============================== If x = 0Eh, it means slide down y units on tick 0. It is an extra fine slide. If x = 0Fh, it means slide down y * 4 units on tick 0. This is a fine slide. otherwise slide down (y*4) units every tick, except tick 0. Recapping: Normal Slides: ============== This is when the parameter is from 00h to DFh. It works just like the amiga slide when it is like this, except S3M uses 4*normal periods, so 4 times the parameter must be added to the period. See SECTION 4.1 for more about the multiplying by 4, and C2SPD. This sort of slide is performed every tick except tick 0. Fine Slides: ============ This is like the amiga fine portamento down, and comes into effect if the x part of the parameter given is 0Fh. It means to only add (y*4) units to the period on tick 0, and no other ticks, giving a fine slide. This sort of slide is only performed once on tick 0. Extra Fine Slides: ================== This part of the effect happens when the x part of the parameter given is 0Eh. It means to only add (y) units to the period on tick 0, and no other ticks, giving an extra fine slide. It is finer than a normal fine slide as it does not have to be multiplied by 4. This sort of slide is only performed once on tick 0. Pseudocode for tick 0: ====================== if (parameter > 0) then, PORTA_SPEED[track] = parameter if ((PORTA_SPEED[track] SHR 4) == 0Fh) then, FREQ[track] = FREQ[track] + ((PORTA_SPEED[track] & 0Fh) *4) if ((PORTA_SPEED[track] SHR 4) == 0Eh) then, FREQ[track] = FREQ[track] + (PORTA_SPEED[track] & 0Fh) Pseudocode for other ticks: =========================== if (PORTA_SPEED[track] < 0E0h) then, FREQ[track] = FREQ[track] + (PORTA_SPEED[track] * 4) ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ³ °±² 6.6 Effect Fxx (Portamento Up) ²±° ³ UPDATED: T0? [Y] : INBETWEEN? [Y] ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ This effect slides the pitch of the sample being played upwards, specified by the parameter given. It also contains the capability of doing fine portamento up, and extra fine portamento up. If no parameter is given, then the last portamento command given is used. E.G: C-2 01 -- F01 (slide the pitch up by (1*(speed-1)*4) units) --- 00 -- F20 (slide the pitch up by (20*(speed-1)*4) units) --- 00 -- FF7 (fine slide the pitch up by (7*4) units) --- 00 -- FE1 (extra fine slide the pitch up by (1) unit) --- 00 -- F00 (continue with another extra fine slide of 1) Range: xy = amount to slide pitch up by (0h-FFh) As this command in fact contains 3 different commands, they must be looked at separately. In summary it works like this: ============================== If x = 0Eh, it means slide up y units on tick 0. It is an extra fine slide. If x = 0Fh, it means slide up y * 4 units on tick 0. This is a fine slide. otherwise slide up (y*4) units every tick, except tick 0. Recapping: Normal Slides: ============== This is when the parameter is from 00h to DFh. It works just like the amiga slide when it is like this, except S3M uses 4*normal periods, so 4 times the parameter must be subtracted from the period. See SECTION 4.1 for more about the multiplying by 4, and C2SPD. This sort of slide is performed every tick except tick 0. Fine Slides: ============ This is like the amiga fine portamento up, and comes into effect if the x part of the parameter given is 0Fh. It means to only subtract (y*4) units from the period on tick 0, and no other ticks, giving a fine slide. This sort of slide is only performed once on tick 0. Extra Fine Slides: ================== This part of the effect happens when the x part of the parameter given is 0Eh. It means to only subtract (y) units from the period on tick 0, and no other ticks, giving an extra fine slide. It is finer than a normal fine slide as it does not have to be multiplied by 4. This sort of slide is only performed once on tick 0. Pseudocode for tick 0: ====================== if (parameter > 0) then, PORTA_SPEED[track] = parameter if ((PORTA_SPEED[track] SHR 4) == 0Fh) then, FREQ[track] = FREQ[track] - ((PORTA_SPEED[track] & 0Fh) *4) if ((PORTA_SPEED[track] SHR 4) == 0Eh) then, FREQ[track] = FREQ[track] - (PORTA_SPEED[track] & 0Fh) Pseudocode for other ticks: =========================== if (PORTA_SPEED[track] < 0E0h) then, FREQ[track] = FREQ[track] - (PORTA_SPEED[track] * 4) ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ UPDATED: ³ °±² 6.7 Effect Gxx (Tone portamento) ²±° ³ T0? [N] : INBETWEEN? [Y] ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ This effect causes the pitch to slide towards the note specified. If there is no note specified it slides towards the last note specified in the Porta to Note effect. If there is no parameter then the last porta speed used for that channel is used again. E.G: C-2 01 -- 000 D-2 01 -- G01 (Set D-2 as the note to slide towards, and with a speed of 1, --- 00 -- G00 then keep it sliding to D-2 with speed 1) --- 00 -- G00 --- 00 -- G00 Range: xy = 00h-FFh This effect can be buggy at first, but not too hard. on TICK 0: - If there is an argument given to the effect, then you must record that as PORTA_SPEED[channel]. (You need to remember all 32 channels worth of porta information - I have them as an array globally) - If there is a note given, then you must store that as NOTE_TO_PORTA_TO[channel]. - But don't slide here, just like the other porta effects. - also, don't reset the note like you would normally if there was a frequency given (i.e. the D-2 in our example) On OTHER ticks: - Subtract or add 4*PORTA_SPEED to the frequency (the period), and set it. Subtract or add depending on if the current frequency is smaller or larger than NOTE_TO_PORTA_TO. ³ Remember in this effect, if there is a note we don't retrigger the note on ³ ³ tick 0 like we usually do, so you have to make a special exception not to ³ ³ play/trigger a sample if there is a porta to note in progress. ³ If you don't remember why you have to multiply the PORTA_SPEED by 4, then look up SECTION 4.1 on why the S3M period system is different to mod. ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ³ °±² 6.8 Effect Hxy (Vibrato) ²±° ³ UPDATED: T0? [N] : INBETWEEN? [Y] ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ This effect causes the pitch to waver up and down around the base note. If no parameter use the last vibrato parameters used for that channel. E.G: D-2 01 -- HA2 (Vibrato the note D-2 with speed of A, and depth of 2) --- 00 -- H00 (Keep vibrating at A2) --- 00 -- HB3 (now change to B3) --- 00 -- H00 (Continue vibrating at B3) Range: x = speed to vibrate at (0h-Fh) y = depth of vibrato (0h-Fh) This is simply a case of adjusting the frequency every tick (besides tick 0), according to a pre-defined table, like a sine table for example. (There are others besides sine wave, see SECTION 5.20 for more on wavecontrols) If the values in the sine table are added and subtracted to the current frequency, then it will appear to be oscillating up and down smoothly in a wave like fashion, giving us vibrato. First lets look at this sine table. Sine Table: =========== This is the sine table used by Protracker. If a player calls itself fully protracker compatible, it really should be using this table. 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 This is actually only half a sine wave, to see how we cope with this read further. Next we need a variable for each channel to tell us where we are in this table currently. It will be referred to from here as VIBRATO_POS. It should be a signed byte, that ranges from -32 to +31. Positioning vibrato pointer: ============================ There are 32 positions in this sine table. But as it is only HALF a sine wave, we need to use it twice, subtracting the values from the frequency first, then adding the values to the frequency. It gives 1 complete sine oscillation. The way to do this is to use the variable called VIBRATO_POS, which is a SIGNED value from -32 to +31. (64 values, including 0) VIBRATO_POS always starts at 0. NOT -32. It is also reset every tick 0 to the start, unless the wavecontrol tells us not to retrigger the vibrato position to the start. If the position variable is < 0, then SUBTRACT the sinetable value, using the absolute of the VIBRATO_POS as the index to the sine table. You can get the absolute of the value by just AND'ing it with 31. (as the extra bit we AND out, is the sign bit.) Otherwise if VIBRATO_POS is >= 0, add to the frequency. By doing this we should get one nice and smooth oscillation like so: At this point we add to the frequency (+)³ ³ **** ³ ³ *** *** Current 0³****..........***³***..........****..... -> time Frequency ³ *** *** ³ (-)³ **** ³ À32ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ0ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ>31 VIBRATO_POS Once we have adjusted the frequency accordingly, add the VIBRATO_SPEED value to the VIBRATO_POS, therefore incrementing the position by the speed given by the Hxy command. If the value is greater than 31, then subtract 64 from it so it starts in the negative again, and we get a continuous sine wave. i.e. If the position is 30, and the vibrato speed is 4, it will become 34, and then should be repositioned to -30. Calculating depth: ================== Because there is a depth parameter to vibrato, we also need to find out how large a value to add or subtract from the current frequency. To calculate the amount or depth of the vibrato, you multiply the sine value by the effect parameter Y, then you DIVIDE it by 128. Remember the divide by 128 (or shift right 7 bits) must be implemented or you'll have a HUGE vibrato. Now remember that with S3M the periods are 4 times the normal period, so it must be then multiplied by 4, or shifted left 2 bits. This allows us to have fine effects like fine vibrato, which only add/subtract 1 unit at a time, not 4. If this is starting to confuse you now, then just look down a page to the pseudocode section and it is simplified into 6 easy steps. Setting the frequency: ====================== - Firstly, you don't actually change the value of the channel's frequency, but you just temporarily set the frequency to this new delta affected value. 1. Work out the size of the delta (delta is how much to add or subtract) delta = sine_table[vibrato_pos[CHANNEL]] delta = delta * vibrato_depth[CHANNEL] delta = delta / 128 (or shift right 7 bits) delta = delta * 4 (4 times normal periods) 2. Now actually add or subtract this delta to the frequency. if (VIBRATO_POS[CHANNEL] < 0), then SetFrequency(freq[CHANNEL] + delta) else SetFrequency(freq[CHANNEL] - delta) Pseudocode: =========== Lets just simplify the above into 6 easy steps now. 1. Start VIBRATO_POS at 0. (on tick 0, and if wavecontrol tells us too, which it usually does.) now on the other ticks.. 2. Use the absolute value of VIBRATO_POS to look up the sine table. This can be achieved by AND'ing it with 31. (and probably faster than abs()) 3. Multiply the sine table value with VIBRATO_DEPTH, then divide it by 128. this is the delta value. Now multiply by 4 as usual. 3. If the VIBRATO_POS is a negative value, SUBTRACT the delta value from the frequency and set it. 4. If the VIBRATO_POS is a positive value (or 0), ADD the delta value to the frequency and set it. 5. Add the VIBRATO_SPEED to the VIBRATO_POS. 6. If the VIBRATO_POS > 31, then subtract 64 from VIBRATO_POS. Wavecontrol: ============ There are several options to vibrato, and they can be set by what is called wavecontrol. Firstly there are 4 methods of affecting the frequency. 1. sine wave 2. ramp down 3. square wave 4. random Next, there is the option of retriggering the VIBRATO_POS to the start every tick 0, or leaving it be. It defaults to 0, which means "sine wave, retrig waveform". TICK 0 : if (wavecontrol says retrig waveform), then VIBRATO_POS = 0 OTHER TICKS : use the sine wave, ramp down, square wave, or random method. See more in SECTION 5.20 about wavecontrol. Example code: ============= For those interested this is how mine works. It is 100% accurate. (freq[track] is the current frequency of that channel) void dovibrato(UBYTE track) { register UWORD delta; register UBYTE temp; temp = (vibpos[track] & 31); // temp will be the index switch(wavecon[track]&3){ case 0: delta = sintab[temp]; // look up sine table break; case 1: temp <<= 3; // ramp down if(vibpos[track]<0) temp=255-temp; delta=temp; break; case 2: delta = 255; // square break; case 3: delta = sintab[temp]; // random break; }; delta *=vibdep[track]; delta >>=7; delta <<=2; // using 4 * periods makes the delta 4 times bigger if (vibpos[track] >= 0) GUSSetFreq(track, GUSfreq(freq[track]+delta)); else GUSSetFreq(track, GUSfreq(freq[track]-delta)); vibpos[track]+= vibspe[track] ; if (vibpos[track] > 31) vibpos[track] -=64; } ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ³ °±² 6.9 Effect Ixy (Tremor) ²±° ³ UPDATED: T0? [Y] : INBETWEEN? [Y] ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ Tremor is a volume effect that turns the note on and off alternately according to the x and y parameter. It is turned on for (x+1) frames, and turned off for (y+1) frames. Contrary to what ST3's help files say, it IS x+1, and y+1, and not x and y like it says. I have listened to ST3 a hundred times with the tremor effect and this is the case. E.G: --- 00 -- A06 (set speed to 6) C-2 01 -- I22 (Turn note on for 3 ticks, and off for 3 ticks, then repeat) --- 00 -- 000 This would cause an even once on and once off every note, as ST3 does. Range: x = time in ticks, to play sample at normal volume (0h-Fh) y = time in ticks, to play sample at volume 0 (0h-Fh) Peculiarities: ============== This is a strange effect, in that nearly every player has a different version. Even the old MDP plays it totally different to the newer Scream Tracker 3 version. (I found out the reason for this though, and have the correct way to perform this effect.. read on) The newer Scream Tracker routines allow 00 to be given as the parameter, which causes the last Ixy parameters to be used. The older routines did not allow this and anything with 00 as the parameters, resulted in a very fast tremor. It also does not turn the volume on for x ticks and off for y ticks. It turns the volume on for (x+1) ticks, and off for (y+1) ticks. I have tested this thoroughly in the newest Scream tracker. Even a so called 'perfect' player like cmod plays the tremor effect badly. How to do it: ============= The effect is quite simply done by setting the volume of the channel to it's normal volume for x+1 ticks, and then setting the volume to 0 for y+1 ticks. The +1 is added when you gather up the parameters, as you don't want to be doing additions every time you want to reference the parameter. Tremor is processed on EVERY tick, so instead of duplicating code, it would probably be wise to make a tremor function that can be called from either the tick 0 processing code, or the other tick processing code. The difference with tick 0 section is that you must check the parameters, and store them in the tremor parameter variable. (also adding 1 to them) If the parameters are 00 then no action is taken to store any parameters, and the last ones are used. Variables: ========== You will need 2 sets of variables. - TREMOR_COUNT[32] (to keep count of the tremor position) - TREMOR_PARAMETERS[32] (to remember the last used parameters) Psuedocode: =========== Using a modulus with this effect stops the tremor counter going off into infinity. So TREMOR_COUNT = TREMOR_COUNT MOD ((X + 1) + (Y + 1)) Tick 0: ======= if (PARAMETER > 0) store parameters in variable. call tremor function Other ticks: ============ call tremor function Tremor function: ================ X = x part of TREMOR_PARAMETER + 1 Y = y part of TREMOR_PARAMETER + 1 TREMOR_COUNT[channel] = TREMOR_COUNT[channel] MOD (X + Y) if (TREMOR_COUNT[channel] < X) set channel's volume to normal volume else set channel's volume to 0 TREMOR_COUNT = TREMOR_COUNT + 1 <- increment counter Suggestion: =========== If you want to, you could probably have different versions of this for if the song is an old S3M, compared to a new one. Ie. the older version does not seem to remember it's parameters, whereas the newer version does. ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ³ °±² 6.10 Effect Jxy (Arpeggio) ²±° ³ UPDATED: T0? [N] : INBETWEEN? [Y] ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ This effect alternates the pitch rapidly to simulate a chord. It usually sounds very grating or harsh so it isn't used much except for chip tunes. E.G: C-2 01 -- J47 (Add to the pitch by 4 half tones then 7 half tones) Range: x = 1st semitone to add to note (0h-Fh) y = 2nd semitone to add to note (0h-Fh) So the effect 047 would generate a major arpeggiated chord, while effect 037 causes a minor arpeggiated chord. This is a tick based effect: Tick 0 set frequency to normal value Tick 1 add the x parameter to the frequency and set it, Tick 2 add the y parameter to the frequency and set it, .... go back and repeat steps from tick 0 until we reach the next row You notice if SPEED is 1, then there will be no arpeggiation because there are no ticks inbetween. If SPEED is 2, then only the x arg is taken into account. This effect is quite simply using the current note value and adding the arpeggio parameters to it, and using the result to look up the period table for a new frequency. Using the modulus operator is a good way to process this effect. if the modulus of tick mod 3 is 0, then the frequency is set to the normal value. If the result is 1, then the frequency is set to the period value of the current frequency + the x parameter. Then logically if the result is 2, the frequency is set to the period value of the current frequency + the y parameter. Pseudocode: =========== - get remainder/modulus of: tick divided by 3 (i.e. tick % 3) - if result = 0 set the frequency to the normal value - if result = 1 set the frequency to the normal value + (x# of semitones) - if result = 2 set the frequency to the normal value + (y# of semitones) ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ UPDATED: ³ °±² 6.11 Effect Kxy (Vibrato + Volume Slide) ²±° ³ T0? [N] : INBETWEEN? [Y] ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ This is a combination of Vibrato (Hxy), and volume slide (Dxy - but not the fine part of the slide). The parameter given does not affect the vibrato, only the volume slide part. If no parameter use the volume slide parameters used for that channel. E.G: C-1 01 -- HA2 (Start Vibrato with speed 0Ah, and depth 2. --- 00 -- K01 (From here on keep doing vibrato, but slide volume down 1 also) --- 00 -- K00 --- 00 -- K00 (continue sliding the volume down) Range: x = amount to slide volume up by or, (0h-Fh) y = amount to slide volume down by. (0h-Fh) This is exactly as it says. Do a Hxy first, then do a volume slide. The parameters given only refer to the volume slide though and do not affect the vibrato. The Vibrato is carried on from the past vibrato parameters. Fine slides are NOT supported in this effect. But it is not like the Protracker version either, because this S3M version remembers it's parameters, whereas .MOD doesn't. ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ UPDATED: ³ °±² 6.12 Effect Lxy (Porta + Volume Slide) ²±° ³ T0? [N] : INBETWEEN? [Y] ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ This is a combination of Porta to Note (Gxy), and volume slide (Dxy - but not the fine part of the slide). The parameter given does not affect the porta, only the volume slide. If no parameter use the last volume slide parameter used for that channel. E.G: C-1 01 000 D-1 01 G01 (start porta to note using speed of 3.) --- 00 L01 (from here on keep doing porta, but slide volume down 1 as well.) --- 00 L00 --- 00 L00 (continue sliding the volume down) Range: x = amount to slide volume up by or (0h-Fh) y = amount to slide volume down by. (0h-Fh) This is exactly what it means, just do a Gxy first, then do a volume slide. The parameters given only refer to the volume slide though and do not affect the porta. The porta is carried on using the parameters from the last porta to note. Fine slides are NOT supported in this effect. But it is not like the Protracker version either, because the S3M version remembers it's parameters, where .MOD doesn't. ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ³ °±² 6.13 Effect Oxy (Sample offset) ²±° ³ UPDATED: T0? [Y] : INBETWEEN? [N] ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ This effect causes the note to start playing at an offset into the sample, instead of just from the start. It is used so that the beginning of a sample is not played, but skipped. E.G: C-2 01 -- O42 (Start the note playing at 4200h bytes into the sample) Range: xy = 00h-FFh As seen in the example, the argument is the first 2 digits of a 4 digit number (in hex) that the offset should take place from. so SAMPLE_OFFSET = EFFECT_PARAMETER * 0100h What you do to enable this effect is when you tell your soundcard or mixing buffer the start of the sample, also add to it the value SAMPLE_OFFSET and then play it. Remember to check if the user set an offset that is larger than the sample! If it is, then the sample offset should equal the end of the sample. ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ UPDATED: ³ °±² 6.14 Effect Qxy (Retrig + Volume Slide) ²±° ³ T0? [N] : INBETWEEN? [Y] ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ This effect retriggers (plays) the current note every x ticks. It is downwards compatible with the MOD retrig effect, but has an additional capability of having a volume slide command included. E.G: --- 00 -- A06 (Speed is now 6) C-2 01 -- Q03 (Retrig the note every 3 ticks - retrig once) C-2 01 -- Q01 (Retrig the note every tick - at speed 6 this would retrig 5 times) C-2 01 -- Q31 (Retrig the note every tick, and subtract 3 from the volume --- 00 -- 000 before the sample is retriggered) C-2 01 01 QF1 (Retrig the note every tick, and double the volume every tick before the sample is retriggered - 2,4,8,16,32,64) Range: x = Volume slide command (0h - 0Fh) y = ticks between retriggers (0h-0Fh) If the x parameter of the retrig is 0, then it acts as the .MOD type retrig did. If x is any other value, then it is affected in the following ways. X ³ Affect on volume. ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ 0 ³ 0 (Nothing) 1 ³ -1 (Subtract 1 from volume) 2 ³ -2 (Subtract 2 from volume) 3 ³ -4 (Subtract 4 from volume) 4 ³ -8 (Subtract 8 from volume) 5 ³ -16 (Subtract 16 from volume) 6 ³ 2/3 (multiply volume by 2, then divide by 3) 7 ³ 1/2 (divide volume by 2) 8 ³ ? (Nothing) 9 ³ +1 (Add 1 to volume) A ³ +2 (Add 2 to volume) B ³ +4 (Add 4 to volume) C ³ +8 (Add 8 to volume) D ³ +16 (Add 16 to volume) E ³ 3/2 (Multiply volume by 3, then divide by 2) F ³ 2 (Multiply volume by 2) The volume change is done before the note is retriggered, and not after. Remember with retrig the best way to find out when to trigger the sample is to take a modulus of the current tick number, and the y parameter of the effect. If the result (the remainder) is 0, then it is time to retrigger the note. i.e. tick MOD 1 = 0 - This causes the retrigger to occur on every tick. tick MOD 2 = 0 - This causes the retrigger to occur every second tick. tick MOD y = 0 - This causes the retrigger to occur every y ticks. ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ³ °±² 6.15 Effect Rxy (Tremolo) ²±° ³ UPDATED: T0? [N] : INBETWEEN? [Y] ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ This effect causes the volume to oscillate up and down in a fluctuating style around the current volume, like vibrato but affecting volume not pitch. If no parameter use the last tremolo parameter used for that channel. E.G: C-2 01 -- R72 (Vibrate the volume up and down using speed 7 and a depth of 2) --- 00 -- R00 (continue with the tremolo at 7,2) Range: x = speed to vibrate volume at (0h-Fh) y = depth of tremolo (0h-Fh) - For a really DETAILED explanation, see SECTION 6.8 on vibrato. The only difference is that you divide by 64 not 128, and the volume is affected not the frequency. If you havent coded vibrato then don't attempt tremolo until you have. Vibrato is a lot more common in songs than tremolo anyway. - Seeing as this is a similar effect to vibrato, then we will use the same tables as it does. Again, the only difference with tremolo is that you divide the delta (or deviation) by 64 and not 128. You also have to check for if the volume goes over or under 0 and 64. This means if the biggest value in the sine table 255 is divided by 64, then the biggest deviation with depth parameter of 1 would only be 4, on its peak. - You're probably asking, what if the volume of the channel is already 64? You cant make the volume go any higher! Well in this case you would only hear the negative side of the tremolo, when the volume dips down and then back to full. Same for the vice versa case if the volume is set to 0. So you have to clip the volumes that go above 64 and below 0. - On TICK 0 the TREMOLO_POS should be reset to the start, unless it's wavecontrol is set that the waveform is NOT reset/retriggered. (identically to vibrato) - I (and most other players) only use 1 byte for vibrato and tremolo wavecontrol, as you only need 4 bits each. That's why it is shifted right to get the upper 4 bits of information in the code below. - Check out the vibrato section for more information on position pointers and sine tables. (SECTION 6.8) This is how mine works, and is 100% accurate. You are right if you think it is nearly identical to vibrato. See SECTION 6.20 for pseudocode. void dotremolo(UBYTE track) { UWORD delta, temp; temp = (trempos[track] & 31); // get absolute value switch((wavecon[track] >> 4) &3){ // check upper 4 bits of wavecntrl case 0: delta = sintab[temp]; // look up sine table break; case 1: temp <<= 3; // ramp down using position value if(vibpos[track]<0) temp=255-temp; delta=temp; break; case 2: delta = 255; // square wave delta is always same break; case 3: delta = sintab[temp]; // random - just use sine. break; }; delta *= tremdep[track]; delta >>= 6; // divide by only 64 this time if (trempos[track] >= 0) { if (volume[track]+delta > 64) delta = 64-volume[track]; GUSSetVolume(track, (volume[track]+delta)*globalvol/64); } else { if ((WORD)(volume[track]-delta) < 0) delta = volume[track]; GUSSetVolume(track, (volume[track]-delta)*globalvol/64); } trempos[track] += tremspe[track]; if (trempos[track] > 31) trempos[track] -=64; } ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ³ °±² 6.17 Effect S0x (Set filter) ²±° ³ UPDATED: T0? [Y] : INBETWEEN? [N] ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ This effect turns on or off the hardware filter (not applicable to most pc sound cards) E.G: --- 00 -- S01 (Turn the filter on) --- 00 -- S00 (Turn the filter off) Range: x = 0 to turn hardware filter off, 1 to turn it on (0-1) There isn't much to say about this effect, except for that it is a hardware function which was designed to turn on the amiga's filter. If you wanted to you could try implementing this effect in the SBPro's h/w filter. Or if you were really tricky you could have 2 copies of the samples, one filtered (using your own filter pass), and one unfiltered, and switch between them when this command is used.. if you are totally insane that is :) ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ³ °±² 6.18 Effect S1x (Set glissando) ²±° ³ UPDATED: T0? [Y] : INBETWEEN? [N] ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ This effect causes a change in the effect Gxy (porta to note). It toggles whether to do a smooth slide or whether to slide in jumps of semitones. E.G: --- 00 -- S11 (Turn on Glissando and have portas slide in semitones) --- 00 -- S10 (Turn off Glissando and have portas slide smoothly) Range: x = 0 to turn off glissando, 1 to turn it on (0-1) By default this value should be set as 0, or doing a smooth slide. When it is set to 0, porta to note (effect Gxy) is achieved by just adding or subtracting the desired porta value to or from the frequency. With glissando turned on it is a different story. The frequency is only set to the next highest or lowest semitone, according to which direction you are sliding. To implement this just keep a glissando flag and check it while doing your porta effect in your UpdateEffect function. To do it is cumbersome to say the least, and I'd rather leave it out because of speed reasons, unless there is a better way to achieve it that I don't know of. To find the nearest semitone, you are going to have to search through the entire period table until you find a value that is the next semitone to be reached in the portamento. The only ways I can think of now are: 1. Sequential Search. Just start at the start of the table and search through to the end. Here it is diagramatically (The reason is to compare it with the 2nd method) The vertical bar |, is used to denote the position currently being searched in the table. search 1. [|--------------] search 2. [-|-------------] search 3. [--|------------] search 4. [---|-----------] search 5. [----|----------] search 6. [-----|---------] search 7. [------|--------] search 8. [-------|-------] search 9. [--------|------] search 10. [---------|-----] search 11. [----------|----] Sequential search uses about N/2 comparisons for both successful and unsuccessful search (on the average) 2. Binary search. Start searching half way through the table, if the value is smaller than this middle value, then discard the upper half and try from the middle of the lower half. This is also the case for vice versa, and the procedure is repeated until you narrow it down to the actual answer. As you can see it only takes 4 searches rather than 11 in this case, which makes it a lot more efficient. search 1. [-------|-------] search 2. [---|---] search 3. [-|-] search 4. [|] Binary search never uses more than log N+1 comparisons for either successful or unsuccessful search. 3. Other searches include interpolation search, binary tree search etc, but I am not going to go any more into searching algorithms, and you may think that these more complicated methods are not worth the extra calculations anyway, even if the search time is shorter. If you are interested there are plenty of books on this topic, so go check out your local library. I used a reference in part of this section on searching algorithms, and credit must go to the following book. Footnote: Algorithms in C Robert Sedgewick. ISBN 0-201-51425-7 1990. ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ³ °±² 6.19 Effect S2x (Set finetune) ²±° ³ UPDATED: T0? [Y] : INBETWEEN? [N] ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ This effect sets the finetune on a selected instrument, it is a carry on from the .MOD format, and is implemented as such. E.G: --- 01 -- S5F (Set the finetune of instrument 1 to -1) Range: x = value of finetune to set (0h-0Fh) As S3M uses C2SPD, and not finetune, then we just have to lookup a table or equivalent, and convert the parameter passed, to a C2SPD value. PARAMETER C2SPD 0 7895 1 7941 2 7985 3 8046 4 8107 5 8169 6 8232 7 8280 8 8363 (No finetune) 9 8413 A 8463 B 8529 C 8581 D 8651 E 8723 F 8757 To implement it, just - check the instrument number - get the value from the effect parameter. - set the C2SPD for that instrument according to the parameter. ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ UPDATED: ³ °±² 6.20 Effect S3x (Set Vibrato waveform) ²±° ³ T0? [Y] : INBETWEEN? [N] ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ This effect set the waveform for the vibrato command to follow. E.G: --- 00 -- S32 (Select the squarewave function for the vibrato command) --- 00 -- S30 (Select the default sinewave for the vibrato command) Range: x = vibrato function to select (0-7) The following values of x select its corresponding vibrato function x=0 : Set sine wave (default) x=1 : Set Ramp Down |\|\|\ _ _ x=2 : Set Squarewave |_| |_| |_ x=3 : Set Random (anywhere) x=4 : don't retrig Sine waveform x=5 : don't retrig RampDown waveform x=6 : don't retrig Squarewave waveform x=7 : don't retrig random waveform Sine wave: ========== This is covered in great detail in the vibrato SECTION 6.8, just apply a sine wave to the frequency. Ramp down: ========== This is done in the following fashion. Multiply the absolute value of VIBRATO_POS by 8 (shift left 3 bits). if the real value of VIBRATO_POS is smaller than 0, then the value to use is the value multiplied by 8, subtracted from 255. otherwise it is just the value multiplied by 8. To make things clearer this is how it works. temp = VIBRATO_POS[track] AND'ed with 31 (to get absolute value) temp = temp * 8 if ( VIBRATO_POS[track] < 0 ) then, temp=255-temp delta = temp This gives a constant downwards ramp as the combination of adding and subtracting makes it a straight line from top to bottom every time. Square wave: ============ This is easily done by adding and subtracting VIB_DEPTH * 255, then it is divided by 128 as usual. It is added to the frequency if VIBRATO_POS is greater than or equal to 0. It is subtracted from the frequency if VIBRATO_POS is smaller than 0. Retrig waveform: ================ This means that you reset the VIBRATO_POS to position 0 every time a new note is processed (on tick 0 in other words). If you have set the wave control flag to 4 or more, then the VIBRATO_POS is not reset, and just continues from the previous position of where it was. Pseudocode: =========== This is how the start works, before you set the frequency or increment the VIBRATO_POS value. temp = VIBRATO_POS[track] AND'ed with 31 (gets absolute value first) if (VIBRATO_WAVECONTROL = 0) then { (sine wave) DELTA = SINE_TABLE[temp] } else if (VIBRATO_WAVECONTROL = 1) then { (ramp down) temp = temp * 8 if ( VIBRATO_POS[track] < 0 ) then, temp=255-temp DELTA = temp } else if (VIBRATO_WAVECONTROL = 2) then { (square wave) DELTA = 255 } else if (VIBRATO_WAVECONTROL = 3) then { DELTA = SINE_TABLE[temp] (random - just use sinewave) } DELTA = DELTA * VIBRATO_DEPTH[track] DELTA = DELTA / 128 DELTA = DELTA * 4 etc.. etc.. Now we know the delta to add or subtract to the frequency. For real examples of the first 3 waveforms look at the example in SECTION 6.8 of the vibrato section. Storage Issue: ============== As these values for vibrato and tremolo only require 4 bits each, the common method is to only use 1 byte, using the upper 4 bits for the tremolo wavecontrol, and the lower 4 bits for the vibrato wavecontrol. ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ UPDATED: ³ °±² 6.21 Effect S4x (Set Tremolo waveform) ²±° ³ T0? [Y] : INBETWEEN? [N] ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ This effect set the waveform for the tremolo command to follow, just like vibrato. E.G: --- 00 -- S42 (Select the squarewave function for the tremolo command) --- 00 -- S40 (Select the default sinewave for the tremolo command) Range: x = tremolo function to select (0-7) The following values of x select its corresponding tremolo function x=0 : Set sine wave (default) x=1 : Set Ramp Down |\|\|\ _ _ x=2 : Set Squarewave |_| |_| |_ x=3 : Set Random (anywhere) x=4 : don't retrig Sine waveform x=5 : don't retrig RampDown waveform x=6 : don't retrig Squarewave waveform x=7 : don't retrig random waveform see section 6.20 for more information on wavecontrol, and 6.8 for an example of how wavecontrol is implemented with tremolo (it is identical to vibrato). Storage Issue: ============== As these values for vibrato and tremolo only require 4 bits each, the common method is to only use 1 byte, using the upper 4 bits for the tremolo wavecontrol, and the lower 4 bits for the vibrato wavecontrol. ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ³ °±² 6.22 Effect S8x (Pan position) ²±° ³ UPDATED: T0? [Y] : INBETWEEN? [N] ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ This effect lets you do 16 position panning. E.G: --- 00 -- S80 (Set the channel's pan value to the far left) --- 00 -- S8F (Set the channel's pan value to the far right) Range: x=position to pan too (0h-0Fh) On tick 0, just read in the parameter and set the relative panning value for the channel. ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ³ °±² 6.23 Effect SAx (Stereo control) ²±° ³ UPDATED: T0? [Y] : INBETWEEN? [N] ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ This effect lets you do 16 position panning, but different to the S8 command. It is obsolete now also, but it is handy to support for some old tunes like PANIC.S3M, and STRSHINE.S3M uses it also I think. E.G: --- 00 -- SA0 (Set the channel's pan value to position 8) --- 00 -- SA7 (Set the channel's pan value to position 15, the far right) --- 00 -- SA8 (Set the channel's pan value to position 0, the far left) --- 00 -- SAF (Set the channel's pan value to position 7) Range: x=position to pan too (0h-0Fh) The consensus seems to be that it is similar to finetune, in that if the parameter is bigger than 7, then you subtract 8 from it, and if it is from 0-7, then you add 8 to it. Pseudocode: ============ if (eparmy > 7), then temp = eparmy -8 else temp = eparmy + 8 setpan(temp) ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ³ °±² 6.24 Effect SBx (Pattern loop) ²±° ³ UPDATED: T0? [Y] : INBETWEEN? [N] ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ This effect allows the user to loop a part of a pattern x number of times. E.G: C-2 01 -- SB0 (Set the loop start at this point) --- 00 -- 000 --- 00 -- SB4 (Loop back to the starting point 4 times) Range: x=marks loop starting point, or sets the number of times to loop to the starting point (0h-0Fh) Pseudocode: =========== This effect is done in the following fashion. If (parameter X = 0) note down the row number for that channel else { if (PATTERN_LOOP[TRACK] = 0) then set PATTERN_LOOP[TRACK] = x else subtract 1 from PATTERN_LOOP[TRACK] if PATTERN_LOOP[TRACK] > 0 row = stored row number. (if we are still looping then jump back) } Remember when declaring the PATTERN_LOOP variable to initialize it as 0. Jumping back should just be a matter of setting your row number to the stored pattern loop number, and once the row is finished it should start playing at the specified position again. This is how my function works, in the UPDATE_NOTE function, or handler for tick 0. case 0x6: if (eparmy == 0) patlooprow[track] = row; // store pos if param=0 else { if (patloopno[track] == 0) patloopno[track]=eparmy; else patloopno[track]--; if (patloopno[track]) row = patlooprow[track]-1; } Remember you keep separate patlooprow/patloopno variables for EACH channel, NOT just 1 of each for the whole song. ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ³ °±² 6.25 Effect SCx (Notecut) ²±° ³ UPDATED: T0? [N] : INBETWEEN? [Y] ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ This effect cuts the volume of the note to 0 after x amount of ticks. --- 00 -- A06 (SPEED is now 6) C-2 01 -- EC3 (Stop the note at tick 3, or half way between 2 notes) Range: x= number of ticks to wait before zeroing samples volume. (0h-Fh) This effect is ignored on tick 0, but on tick x when you are updating tick based effects, then just set the volume of the channel to 0. Of course if the user specified x as a number more than the speed of the song, then it would be ok because it would never get to tick x, and the effect is ignored. ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ³ °±² 6.26 Effect SDx (Notedelay) ²±° ³ UPDATED: T0? [N] : INBETWEEN? [Y] ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ This effect waits for x amount of ticks before it actually plays the sample. *Until the new sample comes out, the previous one continues playing normally* --- 00 -- A06 (now speed 6) C-2 01 -- SD4 (Delay playing this note for another 4 ticks) Range: x = number of ticks to wait before playing sample. (0h-Fh) This effect is ignored on tick 0, AND you must make sure you don't play the sample on tick 0. When you arrive at tick x then just play the sample as you would normally. Again if the user specified x as a number more than the speed of the song, then it would be ok because it would never get to tick x, and the effect is ignored. Delay note can be one of the most annoying effects to implement for a number of reasons. --- 00 -- A06 (speed is now 6) C-2 01 05 000 (volume is now 5) --- 00 -- 000 E-2 02 -- SD3 (Here is the tricky part, see below for why) This requires special handling, as you would normally : 1. play the note 2. set the channel's volume if there was an instrument number, 3. set the channel's frequency if there was a note value, You don't do any of these things UNTIL tick 3, so you have to ignore everything on tick 0 until you get to tick 3, and THEN play the note, set the volume, and set the frequency. Now for the NEXT complication (delays longer than the speed of the song) --- 00 -- A04 (speed is now 4) C-2 01 05 000 (volume is now 5) E-2 02 -- SD5 (here is a delay that will never happen, see below for more) --- 00 -- 000 (this next note is here for a reason) This looks innocent enough, but it is actually quite tricky. You might say that you would just never play the note, but what happens when you get to the next row after the delay? You would normally set the volume of the channel and it jumps from 5 to the volume of sample 2, which you previously stored but did not set because there was a delay note effect. This is very wrong, as we want the volume to stay at 05, and the original C-2 01 sample to keep playing. It requires a special condition not to remember the volume for the channel if an instrument is set, and a delay note effect's parameter is bigger than the SPEED of the song. The reason I emphasize this is you might have coded it so it delayed playing the note, but forgot about the volume/frequency parts of this command. In summary: IF THE NOTE DELAY IS INVALID THEN THE SONG SHOULD PLAY IF THERE WAS NO SUCH NOTE, OR COMMAND. ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ³ °±² 6.27 Effect SEx (Patterndelay) ²±° ³ UPDATED: T0? [Y] : INBETWEEN? [N] ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ This effect delays the pattern for the time it would take to play x number of notes. E.G: C-2 01 -- SE8 (Play the c-2 note then wait for 8 notes before.. C-2 01 -- 000 ... playing the next note) Range: x= number of notes to delay pattern for. (0h-Fh) To implement this effect you are going to have to modify your main interrupt handler (see SECTION 3.3 of FMODDOC.TXT): You are going to have to keep a counter that is subtracted every time your SPEED number of ticks is up, but don't play the note. You must still keep playing the effects though. It would look something like this. if (tick >= speed) { ... blah blah blah etc... if (patdelay = 0) then { increment row. playnote. } else patdelay = patdelay - 1 } else do_effects This just boils down to not playing the note or incrementing the row for x number of notes, until the pattern delay counter is 0. When it is 0 the tune should keep playing as if nothing had happened. ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ³ °±² 6.28 Effect SFx (Funkrepeat) ²±° ³ UPDATED: T0? [Y] : INBETWEEN? [N] ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ This effect inverts a sample loop or plays it backwards. E.G: C-2 01 -- SF4 (Play the loop in this sample backwards at speed 4) Range: x = speed to set invert loop at (0h-0Fh) This effect is not supported in any player or tracker. Don't bother with it. ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ³ °±² 6.29 Effect Txx (Set Tempo) ²±° ³ UPDATED: T0? [Y] : INBETWEEN? [N] ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ This effect changed the song's tempo, or BPM. E.G: --- 00 -- T7D (Change the BPM of the song to 125) Range: xx = bpm setting (20h - FFh) This requires a simple setting of the BPM variable to the parameter specified and a resetting of the handlers rate. i.e resetting the speed of the system timer. The default BPM for a song is 125, but in loading in an S3M file it supplies the default tempo. ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ³ °±² 6.16 Effect Uxy (Fine Vibrato) ²±° ³ UPDATED: T0? [N] : INBETWEEN? [Y] ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ This effect is the same as normal Vibrato, but is 4 times smaller (finer) than a normal vibrato. Great for the higher octaves. It remembers and uses the same parameters from the Vibrato effect (Hxy). E.G: D-2 01 -- U71 (Fine vibrato the note D-2 with speed of 7, and depth of 1) --- 00 -- U00 (Keep vibrating at 71) --- 00 -- UD4 (now change to speed of D, and depth of 4) --- 00 -- U00 (Continue vibrating at D4) --- 00 -- H00 (to demonstrate, this normal vibrato will now continue using the parameters given to the fine vibrato command.) Range: x = speed to vibrate at (0h-Fh) y = depth of vibrato (0h-Fh) This effect is virtually identical to the Vibrato effect, and to see how vibrato works, see SECTION 6.8. The difference is that the delta is 4 times smaller than a normal vibrato. With normal vibrato, you shift the delta right by 7 bits (to divide by 128), and then as it is using 4 times bigger periods, it is shifted left by 2 bits. With fine vibrato all that is needed is the shift right by 7, and leave out the shift left by 2. ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ³ °±² 6.30 Effect Vxx (Global volume) ²±° ³ UPDATED: T0? [Y] : INBETWEEN? [N] ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ This effect changes the song's global volume to the parameter specified. E.G: --- 00 -- V20 (Change the global volume of the song to 20h) Range: xx = value to set the global volume at (0 - 40h) In your player you should have a GLOBAL_VOLUME type variable. All that should be required when this effect is used, is to set this variable to the parameter passed. It comes into effect when the volume of a channel is set (which should be every row by the way). You just multiply the channel's volume by the global volume, then divide it by 64 (40h) to get the final volume. GUS type example: ================= SetVolume(VOLUME[channel] * GLOBAL_VOLUME / 64) SoundBlaster type example (from tech.doc): ========================================== for i=0 to number of channels output += volumetable[volume * GLOBAL_VOLUME / 64][sampledata]; next Remember the divide is always best done last with integer values, as if you divided first, and then multiplied, you would lose a great deal of accuracy. ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ³ °±² : SECTION 7 : ²±° ³ ³ °±² APPENDIX ²±° ³ ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ³ °±² 7.1 Notes ²±° ³ ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ Below in SECTION 7.2 is the TECH.DOC file taken from ST3. It has been edited to leave only the real necessary information. Stuff like adlib info and old stmik formats have been removed, leaving only the main header and sample header info, as well as the packed pattern info and mixing etc info. get TECH.DOC out of ST3 if you want the full text. There are mistakes in TECH.DOC, so I have amended them by including little notes. They are encapsulated inside a text box with "FL Note" written inside it. ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ³ °±² 7.2 ScreamTracker 3 TECH.DOC (edited) ²±° ³ ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ This document finally containts the OFFICIAL information on S3M format and much more. There might be some errors here, so if something seems weird, don't just blindly believe it. Think first if it could be just a typo or something. ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ³ FL Note. Yes there are, so i'll fix them :) ³ ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ ----------------------------------------------------------------------------- What is the S3M file format? What is the samplefile format? What is the adlib instrument format? The first table describes the S3M header. All other blocks are pointed to by pointers, so in theory they could be anywhere in the file. However, the practical standard order is: - header - instruments in order - patterns in order - samples in order Next the instrument header is described. It is stored to S3M for each instrument and also saved to the start of all samples saved from ST3. Same header is also used by Advance Digiplayer. The third part is the description of the packed pattern format. S3M Module header 0 1 2 3 4 5 6 7 8 9 A B C D E F ÚÄÄÄÂÄÄÄÂÄÄÄÂÄÄÄÂÄÄÄÂÄÄÄÂÄÄÄÂÄÄÄÂÄÄÄÂÄÄÄÂÄÄÄÂÄÄÄÂÄÄÄÂÄÄÄÂÄÄÄÂÄÄÄ¿ 0000: ³ Song name, max 28 chars (end with NUL (0)) ³ ÃÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄ´ 0010: ³ ³1Ah³Typ³ x ³ x ³ ÃÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄ´ 0020: ³OrdNum ³InsNum ³PatNum ³ Flags ³ Cwt/v ³ Ffi ³'S'³'C'³'R'³'M'³ ÃÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄ´ 0030: ³g.v³i.s³i.t³m.v³u.c³d.p³ x ³ x ³ x ³ x ³ x ³ x ³ x ³ x ³Special³ ÃÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄ´ 0040: ³Channel settings for 32 channels, 255=unused,+128=disabled ³ ÃÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄ´ 0050: ³ ³ ÃÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄ´ 0060: ³Orders; length=OrdNum (should be even) ³ ÃÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄ´ xxx1: ³Parapointers to instruments; length=InsNum*2 ³ ÃÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄ´ xxx2: ³Parapointers to patterns; length=PatNum*2 ³ ÃÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄ´ xxx3: ³Channel default pan positions ³ ÃÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄ´ xxx1=70h+orders xxx2=70h+orders+instruments*2 xxx3=70h+orders+instruments*2+patterns*2 ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ³ FL NOTE : Actually it should be like this: ³ ³ xxx1=60h+orders ³ ³ xxx2=60h+orders+(instruments*2) ³ ³ xxx3=60h+orders+(instruments*2)+(patterns*2) ³ ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ Parapointers to file offset Y is (Y-Offset of file header)/16. You could think of parapointers as segments relative to the start of the S3M file. Type = File type: 16=ST3 module Ordnum = Number of orders in file (should be even!) Insnum = Number of instruments in file Patnum = Number of patterns in file Cwt/v = Created with tracker / version: &0xfff=version, >>12=tracker ST3.00:0x1300 (NOTE: volumeslides on EVERY frame) ST3.01:0x1301 ST3.03:0x1303 ST3.20:0x1320 Ffi = File format information 1=[VERY OLD] signed samples 2=unsigned samples Flags = [ These are old flags for Ffv1. Not supported in ST3.01 | +1:st2vibrato | +2:st2tempo | +4:amigaslides | +32:enable filter/sfx with sb ] +8: 0vol optimizations Automatically turn off looping notes whose volume is zero for >2 note rows. +16: amiga limits Disallow any notes that go beond the amiga hardware limits (like amiga does). This means that sliding up stops at B-5 etc. Also affects some minor amiga compatibility issues. +64: st3.00 volumeslides Normally volumeslide is NOT performed on first frame of each row (this is according to amiga playing). If this is set, volumeslide is performed ALSO on the first row. This is set by default if the Cwt/v files is 0x1300 +128: special custom data in file (see below) Special = pointer to special custom data (not used by ST3.01) ExtHead = pointer to extended header data area (generally after) the main header) g.v = global volume (see next section) m.v = master volume (see next section) 7 lower bits bit 8: stereo(1) / mono(0) i.s = initial speed (command A) i.t = initial tempo (command T) u.c = ultra click removal. ST3 uses u.c gus channels to guarantee, that u.c/2 channels run without any clicks. If more channels are used, some clicks might appear. The number displayed in ST3 order page is u.c/2 ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ³FL NOTE : This should be ignored as it is a really wasteful dumb method to ³ ³ remove GUS clicks. Using ramps off interrupts is the best method³ ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ d.p = 252 when default channel pan positions are present in the end of the header (xxx3). If !=252 ST3 doesn't try to load channel pan settings. ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ³FL NOTE : with d.p, this isn't always true, it's usually 0 even when there ³ ³ is default panning. ³ ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ Channel settings (byte per channel): ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ bit 8 : channel enabled ³ <ÄÄ FL NOTE: only when NOT set ³ bit 0-7: channel type ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ 0..7 : Left Sample Channel 1-8 8..15 : Right Sample Channel 1-8 16..31 : Adlib channels (9 melody + 5 drums) Channel pan settings (byte per channel): bit 6-7 : reserved bit 5 : 1=default pan position specified, 0=use defaults: for mono 7, for stereo 3 or C. bit 0-3 : default pan position ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ³ FL Note : Default pan position is in bit 0-4, not bit 0-3 ³ ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ Global volume directly divides the volume numbers used. So if the module has a note with volume 48 and master volume is 32, the note will be played with volume 24. This affects both Gravis & SoundBlasters. Master volume only affects the SoundBlaster. It controls the amount of sample multiplication (see mixing section of this doc). The bigger the value the bigger the output volume (and thus quality) will be. However if the value is too big, the mixer may have to clip the output to fit the 8 bit output stream. The default value works pretty well. Note that in stereo, the mastermul is internally multiplied by 11/8 inside the player since there is generally more room in the output stream. Order list lists the order in which to play the patterns. 255=-- is the end of tune mark and 254=++ is just a marker that is skipped. ----------------------------------------------------------------------------- Digiplayer/ST3 samplefileformat 0 1 2 3 4 5 6 7 8 9 A B C D E F ÚÄÄÄÂÄÄÄÂÄÄÄÂÄÄÄÂÄÄÄÂÄÄÄÂÄÄÄÂÄÄÄÂÄÄÄÂÄÄÄÂÄÄÄÂÄÄÄÂÄÄÄÂÄÄÄÂÄÄÄÂÄÄÄ¿ 0000: ³[T]³ Dos filename (12345678.ABC) ³ MemSeg ³ ÃÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄ´ 0010: ³Length ³HI:leng³LoopBeg³HI:LBeg³LoopEnd³HI:Lend³Vol³ x ³[P]³[F]³ ÃÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄ´ 0020: ³C2Spd ³HI:C2sp³ x ³ x ³ x ³ x ³Int:Gp ³Int:512³Int:lastused ³ ÃÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄ´ 0030: ³ Sample name, 28 characters max... (incl. NUL) ³ ÃÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄ´ 0040: ³ ...sample name... ³'S'³'C'³'R'³'S'³ ÃÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄ´ xxxx: sampledata Length / LoopBegin / LoopEnd are all 32 bit parameters although ST3 only support file sizes up to 64,000 bytes. Files bigger than that are clipped to 64,000 bytes when loaded to ST3. NOTE that LoopEnd points to one byte AFTER the end of the sample, so LoopEnd=100 means that byte 99.9999 (fixed) is the last one played. C2Spd = Herz for middle C. ST3 only uses lower 16 bits. Vol = Default volume 0..64 Memseg = Pointer to sampledata Inside a sample or S3M, MemSeg tells the parapointer to the actual sampledata. In files all 24 bits are used. In memory the value points to the actual sample segment or Fxxx if sample is in EMS under handle xxx. In memory the first memseg byte is overwritten with 0 to create the dos filename terminator nul. Int:Gp = Internal: Address of sample in gravis memory /32 (only used while module in memory) Int:512= Internal: flags for soundblaster loop expansion (only used while module in memory) Int:las= Internal: last used position (only works with sb) (only used while module in memory) [T]ype 1=Sample, 2=adlib melody, 3+=adlib drum (see below for adlib structure) [F]lags, +1=loop on +2=stereo (after Length bytes for LEFT channel, another Length bytes for RIGHT channel) +4=16-bit sample (intel LO-HI byteorder) (+2/+4 not supported by ST3.01) [P]ack 0=unpacked, 1=DP30ADPCM packing (not used by ST3.01) ----------------------------------------------------------------------------- packed pattern format 0 1 2 3 4 5 6 7 8 9 A B C D E F ÚÄÄÄÂÄÄÄÂÄÄÄÂÄÄÄÂÄÄÄÂÄÄÄÂÄÄÄÂÄÄÄÂÄÄÄÂÄÄÄÂÄÄÄÂÄÄÄÂÄÄÄÂÄÄÄÂÄÄÄÂÄÄÄ¿ 0000: ³Length ³ packed data, see below... ÃÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄ´ Length = length of packed pattern Unpacked pattern is always 32 channels by 64 rows. Below is the unpacked format st uses for reference: Unpacked Internal memoryformat for patterns (not used in files): NOTE: each channel takes 320 bytes, rows for each channel are sequential, so one unpacked pattern takes 10K. byte 0 - Note; hi=oct, lo=note, 255=empty note, 254=key off (used with adlib, with samples stops smp) byte 1 - Instrument ;0=.. byte 2 - Volume ;255=.. byte 3 - Special command ;255=.. byte 4 - Command info ; Packed data consits of following entries: BYTE:what 0=end of row &31=channel &32=follows; BYTE:note, BYTE:instrument &64=follows; BYTE:volume &128=follows; BYTE:command, BYTE:info So to unpack, first read one byte. If it's zero, this row is done (64 rows in entire pattern). If nonzero, the channel this entry belongs to is in BYTE AND 31. Then if bit 32 is set, read NOTE and INSTRUMENT (2 bytes). Then if bit 64 is set read VOLUME (1 byte). Then if bit 128 is set read COMMAND and INFO (2 bytes). For information on commands / how st3 plays them, see the manual. ----------------------------------------------------------------------------- What is C2SPD? How to calculate the note frequencies like ST3? How does ST3 mix depending on master volume? Finetuning (C2SPD) is actually the frequency in herz for the note C4. Why is it C2SPD? Well, originally in ST2 the middle note was C2 and the name stuck. Later in ST3 the middle note was raised to C4 for more octaves... So actually C2SPD should be called C4SPD... Table for note frequencies used by ST3: note: C C# D D# E F F# G G# A A# B period: 1712,1616,1524,1440,1356,1280,1208,1140,1076,1016,0960,0907 middle octave is 4. 8363 * 16 * ( period(NOTE) >> octave(NOTE) ) note_st3period = -------------------------------------------- middle_c_finetunevalue(INSTRUMENT) note_amigaperiod = note_st3period / 4 note_herz=14317056 / note_st3period Note that ST3 uses period values that are 4 times larger than the amiga to allow for extra fine slides (which are 4 times finer than normal fine slides). How ST3 mixes: 1) volumetable is created in the following way: > volumetable[volume][sampledata]=volume*(sampledata-128)/64; NOTE: sampledata in memory is unsigned in ST3, so the -128 in the formula converts it so that the volumetable output is signed. 2) postprocessing table is created with this pseudocode: > z=mastervol&127; > if(z<0x10) z=0x10; > c=2048*16/z; > a=(2048-c)/2; > b=a+c; > { 0 , if x < a > posttable[x+1024] = { (x-a)*256/(b-a) , if a <= x < b > { 255 , if x > b 3) mixing the samples output=1024 for i=0 to number of channels output+=volumetable[volume*globalvolume/64][sampledata]; next realoutput=posttable[output] This is how the mixing is done in theory. In practice it's a bit different for speed reasons, but the result is the same. ----------------------------------------------------------------------------- That's it. If there are any more questions, that's too bad :-) If you have problems with the S3M format, try to contact somone who already supports it (there is quite a lot of support for the S3M already, so that shouldn't be too hard...) Good luck for reading / writing Scream Tracker files.