WorkoutGenSD Online

To load your script from disk, Click ->

You may also drag and drop your script file /text onto this box, or copy/paste contents into it, or just begin typing.

WorkoutGenSD Online is an online compiler for creating workouts for iFit compatible devices equipped with iFit SD card slots. Supported machines include treadmills, bikes, ellipticals, and incline trainers. The compiler uses a very simple, easy-to-learn scripting language. No programming experience required.

Keyword Quick Reference

treadmill (or tread) -- must be first line of script -- signifies these workouts are for treadmills
elliptical (or elliptic) -- must be first line of script -- signifies these workouts are for elliptical trainers
bike -- must be first line of script -- signifies these workouts are for bikes
incline -- must be first line of script -- signifies these workouts are for incline trainers
workout -- must be second line of script or follow an end keyword -- signifies beginning of workout
interval (or int) -- starts an interval
play -- plays a wav file
say -- used with play, generates code to create the wav file using text-to-speech
autoname (or auto) -- generates a wav filename automatically
midinterval (or mid) -- allows to create equipment change (e.g. speed or incline) in the middle of an interval
endprogram (or end) -- must be final line of each workout (unless you are also using experimental keywords)
exp -- used for experimenting with some unknown command types
remove -- used in experimental section to experiment with removing some commands

Compatible SD cards

Believe it or not, finding a compatible SD card will be the biggest challenge you will face, even bigger than learning the simple scripting language used by the compiler. Not just any SD card will work. In fact, most SD cards WILL NOT work. Here is a list of cards that have been reported to be compatible:

128MB Delkin Devices eFilm Multimedia MMC Card
1 GB Kodak SD 
128 MB LEXAR Media SD card
2GB Kodak High Speed SD Card 60x speed
512MB Kodak
2GB Kingston
1GB Mini SD Kingston (in Full size SD Adapter)
256MB Corsair SD Card
1GB HP SD 
1GB Generic microSD (in full size SD adapter)
2GB PNY
1GB Sandisk (NOT WORKING)
4GB Sandisk (NOT WORKING)
2GB Sandisk (NOT WORKING)
512MB Sandisk MiniSD (NOT WORKING)
64MB MMC+ cards (available from me -- see below)

Notice all of these are 2GB and under, so you almost certainly will need SD and NOT SDHC cards, but don't let this fool you into thinking SD versus SDHC is the only issue to overcome. Also, bear in mind just because my 512MB Kodak card works doesn't necessarily mean your 512MB Kodak card will work. Trial and error, perseverance and determination will be called for in this endeavor. If I can find a good supplier of compatible cards I might start selling them here, but until then I'm afraid you are on your own. If you find a card that works, drop me a line at mwganson at gmail and I will add your card to the list. A good source is eBay for used cards. If you can find an assortment of various brands and sizes that might be your best bet to hopefully get one of them in the lot that works with your machine. Also look at MMC cards, some of which are known to work.

One thing I have recently discovered when it comes to SD card compatibility. I had a couple identical cards. Both were working, but after I formatted one (in Windows) it stopped working with the treadmill. After further investigation I discovered a difference in the 2 cards and was able to get both working again. My solution was to boot into linux and run the gparted (gnu partition editor) utility. In gparted I selected the partition, and then in the Partition menu I selected Manage flags menu item. The lba checkbox was checked. I unchecked it (all checkboxes should be clear -- no flags set) and closed the dialog. After that the treadmill once again recognized the SD card and it started working again. If you have a card that appears to be incompatible, I recommend trying this on it. It is best to NOT format a working card, but if you do format, choose FAT (or FAT16) and the default other settings.

UPDATE -- I have a limited number of compatible memory cards available for $15 USD each, Paypal only, free shipping within the continental United States. These are 64 MB MMC+ cards. E-mail me at mwganson at gmail dot com with "workoutgensd cards" in the subject line. I will let you know how many cards I have left, if any, and tell you where to send the payment. I cannot guarantee the cards will work on your equipment, but I will test each card on my own treadmill before shipping.

The only piece of iFit equipment I have access to is a treadmill. I can verify the compiler output works with treadmills, but I can't test the other equipment since I don't have any of them. Thus, you might create a workout, try your new SD card with it, and erroneously blame the card being incompatible when the actual problem might be a bug in the compiler causing your machine to reject the workout. Luckily, I happen to have a few professionally-produced sample workouts for you to download and use to try out your SD cards. If the sample workouts function, but the compiler output does not function, then you know the issue is with the compiler (or possibly an error in your script) that is keeping it from working rather than the incompatible SD card. Here are the workouts:

sample bike workout
sample elliptical workout
sample treadmill workout

All of the above samples (sorry, I don't have an incline trainer sample) were professionally produced by personal trainer par excellence, Heather, and are worth trying out just for the pleasure of using a professionally produced workout. The samples are the first workouts in the weight-loss series she is (or maybe was) selling for the iFit devices. If you like them you might consider purchasing the rest of the workouts in the series if you can find them.

Instructions

You can think of a workout as a series of intervals, each interval lasting one minute long. Thus, a 25-minute workout would have 25 intervals. You can make your settings changes (speed, incline, RPM, or resistance) at the start of each interval. Optionally, you can also include .wav files to be played in various intervals during the workout. Let's get started with a simple, complete script for a bike workout:

bike
workout
interval 0 45 1
interval 1 50 1
interval 2 50 2
interval 3 50 2
interval 4 50 2
interval 5 45 1
interval 6 45 1
interval 7 45 1
interval 8 0 0
end

This is just a short and easy workout to show some of the basic features. Highlight the above code with your mouse, press CTRL + C, then click on the script box above and press CTRL+V. This will copy and paste the contents of the script into the script box. (Alternatively, you can type into the script box, drag and drop files from Windows Explorer directly on to the script box, or use the Choose File button above to load a file from your hard drive.) Now click the Generate Zip button. Congratulations, you've just created your first workout. Now, you need to download the .zip archive to your hard drive. Right-click on the link that appeared to the right of the Generate Zip button and select the Save As... option presented by your browser in the popup context menu. Depending on your browser it might say save link as, save target as, save file as, or just save as. When you name the file be sure to give a .zip extension, for example myworkout.zip would work. Some browsers, notably Edge, will append a .txt extension to the file no matter what you name it. Select open folder or view in folder and rename the file, if necessary, so that it has the .zip extension. Now you can extract it to a compatible SD card if you have one and do the workout on your iFit bike.

Breaking down the scripting language

The first line in the script must be one of the following words: bike, treadmill, incline, or elliptical. You can also use shortened versions: tread and elliptic if you prefer. The meaning of this first line is self-explanatory. The second line must be workout. The workout keyword tells the compiler this is a new workout. You may have multiple workouts in the same script. Just add a workout line after the end line in the script to start a new workout. All workouts in the script must be for the same machine type, e.g. treadmill or elliptical or whatever.

The interval keyword creates the interval. (You can use int as a shortcut for interval.)

interval 0 45 1

The syntax here is: interval [interval number] [RPM] [Resistance]

You must have an interval number to tell the compiler which interval you are creating because it's permissible to skip intervals and have the compiler insert the missing ones for you automatically. Here is an example of how we might have written the above script example using the skip interval feature:

bike
workout
interval 0 45 1
interval 1 50 1
interval 2 50 2
interval 5 45 1
interval 8 0 0
end

Notice how we left out intervals 3, 4, 6, and 7. The compiler creates the missing intervals for us, using the most recent interval settings. (It's a bit more complicated than that, but it's sufficient to think of it this way for now.)

Using mid-intervals

Each interval is one minute long. Interval 0 starts at 0:00 (minutes:seconds) and goes through to 0:59. Interval 1 goes from 01:00 through to 01:59. Interval 2 goes from 02:00 to 02:59, and so on... If a settings change is being made during an interval it happens at the start of the interval. If you want to make a change sooner than that, you can use what's called a mid-interval with the midinterval keyword. It works very similarly to the interval keyword except the first parameter instead of the interval number is an offset in seconds from the start of the previous interval. Let's say we wanted to go to 60 RPM at the 01:30 mark (between intervals 1 and 2), but only for 30 seconds. We can modify the above code as follows to accomplish this:

bike
workout
interval 0 45 1
interval 1 50 1
midinterval 30 60 1
interval 2 50 2
interval 5 45 1
interval 8 0 0
end

The syntax here is: midinterval [offset in seconds] [RPM] [Resistance]

(You can use mid as a shorter version of midinterval.)

Differences between workouts for bikes/ellipticals and treadmills/inclines

Making a workout for an elliptical is exactly the same as making one for a bike. In fact, the compiled code is identical in every way but for one line of code telling the iFit system the program is for an elliptical rather than for a bike. Treadmills and incline trainers are also virtually identical to one another from a programming standpoint. The script is identical except for using tread or treadmill at the top of the script instead of incline. But there is a slight difference in the syntax for the interval and midinterval keywords between bikes/ellipticals and treadmills/incline trainers. With a treadmill/incline we are dealing with speed as measured in miles/hour or kilometers/hour whereas with bikes/ellipticals we are dealing with speed as measured in RPM's. Here is an example script for a short treadmill workout:

treadmill
workout
interval 0 2 0 2
interval 1 2.5 0 2
midinterval 30 4 0 2
interval 2 2.5 0 2
interval 5 3 0 2
interval 8 2.5 0 0
interval 9 2 0 0
interval 10 0 0 0
end

The only difference is the extra parameter (for the metric speed) for treadmills/inclines versus bikes/ellipticals.

The syntax here is: interval [speed (mph)] [speed (kph)] [incline (%)]

Notice I used 0 for the kph metric speed. When the compiler sees 0 in the kph position it will automatically compute the correct kph value based on the value specified in the mph position. To convert miles-per-hour to kilometers-per-hour just multiply the mph by 1.60934 and that will give you the equivalent kph. Conversely, if the compiler sees 0 in the mph position it will use the value in the kph position to compute the appropriate mph value. The formula there is mph = kph * 0.6214. In practical terms, programming the treadmill/incline just means adding another 0 in either the kph or mph position, depending on which you prefer to use.

Treadmills should always have the final interval incline set to 0 for safety purposes with foldable storage designs.

Same keyword

Before we look at .wav file use, there's one more important thing we need to cover: the same keyword. If you wish to keep the same settings from the previous interval you can use the same keyword instead of simply repeating the value in the interval. Consider the following code snippet:

interval 5 3 0 2
interval 8 2.5 0 0

We can accomplish the same *exact* thing in the compiled output by replacing the above code with:

interval 5 3 0 2
interval 6 same same same
interval 7 same same same
interval 8 2.5 0 0

There is a subtle difference between doing the above and doing the following:

interval 5 3 0 2
interval 6 3 0 2
interval 7 3 0 2
interval 8 2.5 0 0

The difference is if the user makes a settings change between intervals 5 and 6, let's say by changing from 3 mph to 4 mph, the treadmill will change the speed back to 3 mph when it reaches the 6:00 minute mark, but if we had used the same keyword as in the former example when the user changes to 4 mph the new setting will persist until we reach interval #8, at the next literal speed change. So, if you want to give the user (probably yourself) more flexibility to change the settings on the fly while doing a workout, you should use the same keyword whenever you want to keep the same settings from the previous interval. This works for mph, kph, incline, RPM, and Resistance settings. (I believe.) This feature is implemented by the compiler by using a special magic value (0xfa = 250, base 10) whenever the same keyword is encountered. When the iFit equipment reads 0xfa for that settings byte value it basically just ignores it and leaves the setting alone. The end result is whether it was programmed to be at 4 mph in the previous interval or whether the user changed it manually, it doesn't matter, the equipment will not change the setting.

Working with .wav files

Now, let's turn to using .wav files. If you've ever used one of the iFit professionally produced workouts you will be familiar with the use of .wav files during the workouts. The compiler here supports the use of .wav files. It will even help you create files using a text-to-speech engine, but at this time this only works under Windows, Mac OS X, and Linux operating systems. Here is another very short workout featuring the use of .wav files:

treadmill
workout
play intro001.wav
interval 0 2 0 2
play autoname say this is a 10 minute workout at a very slow pace
interval 10 0 0 0
end

As you can see, the play keyword is used to play .wav files. You can use your own pre-recorded file as in the first example where we used one called "intro001.wav" or you can have the compiler help you generate one from text using the say keyword. Notice also the autoname keyword tells the compiler to go ahead and make up a name for the file for us. (You can substitute the word auto in place of autoname if you don't want to do so much typing.) There is also another variation on the play keyword which uses an offset parameter to tell the compiler when to play the file. The default is to just play the file at the start of the interval. If multiple files are listed in the same interval, they are played one after the other automatically, so you don't need to specify an offset to prevent overlapping. An offset would be used if you want to play the file right before the next interval, such as to notify the user of an impending settings change or if you want to space out the .wav files, for example 30 seconds apart instead of right after one another. The following example shows how to incorporate the offset into the play keyword script lines:

treadmill
workout
play intro001.wav
interval 0 2 0 2
play autoname say this is a 10 minute workout at a very slow pace
play intro002.wav 30
interval 10 0 0 0
end

If you want to incorporate both the offset feature and the say keyword, you would put the offset *before* the say keyword:

treadmill
workout
play intro001.wav
interval 0 2 0 2
play autoname say this is a 10 minute workout at a very slow pace
play intro002.wav 30 say this will be played at 2 minutes 30 seconds into the workout
interval 10 0 0 0
end

Just remember to use the offset *before* the say keyword or else the compiler will think you intended to say the offset value instead of use it as an offset.

More details on .wav files

The compiler doesn't create the .wav files for you during compilation. It creates and supplies the necessary scripting files to create the .wav files and places these files inside the .zip archive. After you download and extract the .zip archive file you can then create the .wav files by double-clicking on the runme.bat file in Windows or by executing the runme.sh shell script in Linux.

Creating the .wav files in Windows

Let's talk about the Windows procedure first, and then we'll cover the Linux procedure. The runme.bat script file calls a second file called makewavs.bat, which uses a third file called ttw.vbs. The latter file (ttw.vbs) is the one that does the actual .wav file creation. makewavs.bat supplies ttw.vbs with the necessary parameters (namely: the desired filename and the desired text to convert into the .wav image). If all this seems complicated, just remember all you need to worry about is double-clicking on runme.bat to make the .wav files. ttw.vbs is a visual basic script file, designed for Windows Scripting Host (WSH), which is why it won't work under other operating systems, such as Android or IOS. You might be able to get it to work under Linux using wine. You would need to download and install the WSH from microsoft dot com and get it installed into the wine system32 folder. Read the readme.txt (also included in the .zip archive) for a link containing more details on this subject. But there's a better way with Linux, which we'll get to in the next section.

Create the .wav files in Android

If you have an android device you might want to download the ttwhelper app to help generate the .wav files. The app can be used to generate the .wav files you specified with the say keyword or you can use it to record your own files. Ttwhelper can also be used as a frontend for the ffmpeg file conversion utility in Android. See the ttwhelper webpage for more details.

Create the .wav files in Linux

With Windows you would need to execute the runme.bat file, but with Linux it's the runme.sh file. With Windows it's a little bit easier because you can just double-click runme.bat from Windows Explorer file manager, but with Linux you have to first make the runme.sh file executable by giving it execute permission. You will also need to install a couple packages: espeak and ffmpeg. espeak will be used to create the .wav files, and ffmpeg will be used to convert them to the necessary format for use with the iFit system. When you install espeak, it will also bring in another package called portaudio. If it doesn't bring in portaudio as a dependency you might need to install portaudio separately. In Arch Linux you would do this:

# pacman -S espeak
# pacman -S ffmpeg

Note: the # means you need to be doing this as root or at least with elevated privileges using sudo. With Arch Linux you can also use su, which will ask for the root password, and then you'll have root privileges. Once you have those packages installed you can use the runme.sh script, which will be in the same folder inside the .zip as the runme.bat file. Before you can execute runme.sh you have to give it execution permission. This can be done as such:

# chmod +x runme.sh

And then to execute it:

# ./runme.sh

Be sure to add the "./" before the "runme.sh".

If you want to create the .wav files yourself and use ffmpeg to convert them to the appropriate format, here is the command line to use with ffmpeg:

ffmpeg -i inputfile.wav -ar 8000 -acodec pcm_u8 outputfile.wav

ffmpeg does a great job of discovering the format of the input file, so you should be able to use any file format as the input file, such as mp3, wav, wmv, etc. By the way, you can also get ffmpeg for Windows and use the same command line interface as described above for Linux. If you install WinFF, it will come with ffmpeg, and you can find it in the c:\program files (x86)\winff folder.

Creating the .wav files in Mac OS X

I just recently acquired a Mac to test this on, and can confirm it works. The Mac (OS X 10.11) has a built-in "say" command that can be used to create the text-to-speech files, but it saves them in .aiff format instead of .wav, so we'll again have to use ffmpeg to do the file conversions. The first thing you have to do is to install ffmpeg. Here is a step-by-step guide to installing ffmpeg on the Mac OS X system. I had to modify those instructions just a bit, by creating the /usr/local/bin folder on my machine. If you run into the same issue, here is how I fixed it: It was easy enough, just go:
cd /usr/local
mkdir bin

(then you'll need to navigate back to the folder where you downloaded and extracted the workout .zip file.)
Once you have ffmpeg installed as per the above-linked instructions, you're ready to begin making the .wav files. In the root folder of the .zip archive is a file called runme-mac.sh, which will be used to create the .wav files. From the Mac terminal you will first need to make this file executable by giving it execute privileges:

$ sudo chmod +x runme-mac.sh

And then to execute it:

$ sudo ./runme-mac.sh

The runme-mac.sh script uses the default system voice, so if you want a different voice you will need to change your system default text-to-speech voice or you can edit the runme-mac.sh file and add -v voice to the say command lines in runme-mac.sh, where voice is the name of the preferred voice to use, e.g. alex or kathy. Changing the default voice is the preferred way, just go to Settings, Dictation and Speech, Text-To-Speech, and you'll find the default system voice setting.

Another option for .wav files is to simply create them yourself using a third-party text-to-speech application or alternatively using a microphone to record them with a sound recording utility. The iFit system is picky about which format of .wav files it will play. They MUST be PCM RIFF 8000hz 8-bit MONO 64kbps uncompressed .wav format. Do NOT select any a-law or u-law compression algorithms when setting up your recording software if you elect to go this route. If you can't find the exact settings you can use ffmpeg, as described above, to convert them to the correct format. My personal opinion is the Windows text-to-speech voice is more pleasant than the one espeak uses. In Windows you can change the default voice through the Control Panel if you don't like the one it uses. In espeak you'd have to go in and modify the runme.sh file (using kate or a similar text editor). Use espeak --help for more information on changing voices with that software package.

Background on hacking the iFit system

The above sample files were indispensible in cracking the iFit mystery format. Without them, it simply would never have been done because the retail iFit workouts on the SD cards are copy-protected in some manner making them un-readable on a computer (or so I'm told). But, in an effort to sell the workouts, iFit made the mistake of releasing these sample workouts, which I and a few even cleverer cohorts were able to use to decipher the format. Despite our success, there yet remains parts of the format that are not yet fully understood. If you want to help crack the code, so to speak, you can perform some experiments on your equipment to potentially help figure out some of the rest of the format.

Experimenting with the iFit format

I have provided 2 keywords for experimentation: exp and remove. Using exp you can experiment by modifying the bytes in a command block that are suitable (meaning, the ones we don't fully understand) for experimentation. Using remove you can also remove some blocks to see what, if any, effect that might have on the workout.

After extracting the .zip archive file, you will find a file called workouts.txt in the root folder of the extracted archive. This same information can be found by pressing F12 in most browsers to bring up the developer tools, in the console tab in said tools. You might need to have the console open prior to clicking Generate Files to see the output. The workouts.txt file describes the binary workout file the compiler produced, command block by command block. The workout binary is basically just a series of command blocks. Each block is a series of bytes, ranging from 2 or 3 bytes to 9 bytes in length. The first 2 bytes in the file form together a 16-bit (2 byte) value giving the number of command blocks in the workout (not counting that first block, which isn't really a command block). The rest of the blocks are in the general form of: [code byte] [parameter bytes, if any] [checksum byte]. Consider the following line from a workouts.txt file:

7 0 0 f9;BlockType.UnknownBlock08 (exp1 = 0, exp2 = 0)

In this example, the code byte is 0x07 (all of the bytes are in hexadecimal, base 16 notation). When the iFit device sees that 0x07 as the first byte in a command block it knows this is the UnknownBlock08 block type, and presumably thus knows how to interpret the 2 0x00 values that follow. I have no idea what the two 0x00's are used for, but by experimenting on them you might be able to figure it out and let me know (mwganson at gmail). If you do, I'll list it here and give you credit for it if you want. So, the 0x00 and the other 0x00 we know are parameters of some type for some purpose. The 0xf9 is the checksum byte. The way the checksum byte works is it takes what value it needs to take in order for the sum of the entire block of bytes to be evenly divisible by 0x100 (256 base 10). To wit, 0xf9 + 0x07 = 0x100 = 256 (base 10). The sum of the bytes can be any multiple of 256 (base 10). So, let's try a little mental experiment here, by changing the first 0x00 to 0x01 and the other one to 0x03. Here's how we would do that:

end
exp unknownblock08 0x01 0x03

Take note how the exp keyword can ONLY be used AFTER the end keyword. You would use exp and/or remove AFTER the end line and BEFORE the next workout line, if any. If you did the above with a treadmill workout, and then checked the workouts.txt information, you'd see this for the same line:

7 1 3 f5;BlockType.UnknownBlock08 (exp1 = 1, exp2 = 3)

Notice how the checksum byte changed from 0xf9 to 0xf5 to reflect the new checksum value. The compiler handles the checksum byte for you, so it's not anything you have to worry about. You can also try to remove the command block to see what effect, if any, that might have on your workout using remove:

end
remove unknownblock08

Take note the fact that not all equipment types even *have* the unknownblock08 command block type. You need to review your workouts.txt file in order to see which command blocks are available for experimenting with. Attempting to remove or experiment on a block that doesn't exist will just result in a compile time error, so no biggie if you make that mistake. Some command lines support using exp in the script on the same line. Both interval and play support this type of experimentation (but NOT midinterval). Let's take a look at play and what's actually going on behind the scenes when it is used.

When you enter something like:

play wav0023.wav

the compiler actually creates 2 command blocks to handle that one play line. The two command blocks are:

5a 4 fa fa 0 ae;BlockType.FetchWaveFile (exp1 = 250, exp2 = 250, exp3 = 0 file index = 4)
5b ff fb 1 aa;BlockType.PlayFetchedWaveFile (exp1 = 255, exp2 = 251, exp3 = 1, fname = WAV0023)

First, the file is fetched, and then it is played. 0x5a is the code byte for fetching a .wav file. The first parameter (0x04) is the index of the file (WAV0023.wav), based on the order in which it is listed in the sound index file (default: S0000000.fit). If you opened the sound index file for this workout in notepad you'd see WAV0023 as the 4th file in the list. The next 3 parameters have unknown purposes: 0xfa (250 base 10), 0xfa (250 base 10) and 0x00. The final byte (0xae) we already know is the checksum byte. The 0x5b in the PlayFetchedWaveFile command block is the code byte. 0xff, 0xfb, and 0x01 are the 3 unknown purpose parameters for this command block type. Because these play lines are broken down into and executed with 2 separate command blocks per play line we have to provide the experimental values for both of them in one go. This is done as follows:

play wav0023.wav exp 0xfa 0xfa 0x00 0xff 0xfb 0x01

Note I just provided the default values instead of changing them. You would change the one(s) you wanted to experiment with and leave the other ones the same default values. If you find it more convenient you can use base 10 notation instead of base 16, as follows:

play wav0023.wav exp 250 250 0 255 251 1

or mix and match, as you please. If you use the say keyword on the play line you need to provide the exp parameters AFTER the say keyword text parameter. Just remember, exp is always LAST. Here is an example:

play autoname say this is a long example exp 250 0

Do you see what I did there? I only specified 2 experimental parameters even though I could have done all 6. What happens in this case is the defaults are used for all the unspecified experimentals. The only ones that get changed are the first 2, and actually the first value is the default, so it just gets "changed" back to what it already was. The 2nd 0xfa in the fetch command block gets changed to 0x00 in this example. You'd get a pair of lines in workouts.txt similar to the following:

5a 4 fa 0 0 b4;BlockType.FetchWaveFile (exp1 = 250, exp2 = 0, exp3 = 0 file index = 4)
5b ff fb 1 aa;BlockType.PlayFetchedWaveFile (exp1 = 255, exp2 = 251, exp3 = 1, fname = V0000004 -> say = this is a long example)

If you use an offset parameter with the play keyword you only get one command block in workouts.txt, the PlayWaveFile command block type. The fetch and play command block pair only take the single file index parameter, but the single playwavefile command block type needs to both fetch and play all in one go *and* it needs to know *when* to play the file, so it also gets a time parameter. Let's take a gander at a line from workouts.txt:

60 0 0 35 3 fa fa 0 74;BlockType.PlayWaveFile (offset = 53, file index = 3, exp1 = 0, exp2 = 250, exp3 = 250, exp4 = 0, fname = V0000003 -> say = changing to 2 point 5 miles an hour)

0x60 (96 in base 10) is the byte code for this type of command block. There are 4 bytes with unknown purposes: the 2nd byte (1st 0x00) and the 6th, 7th, and 8th bytes: 0xfa 0xfa 0x00. 0x74 is the checksum byte. Bytes 2 and 3, (0x00 and 0x35 here) are the time stamp bytes, which together form a 16-bit word. 0x00 is the high order byte, 0x35 is the low order byte. The combined word value is the number of seconds from the beginning of the workout. 0x35 = 53 (base 10). In this example, this file is being played :53 seconds past interval #0. 0x03 is the file index byte, which we already covered in the above paragraphs. To experiment with this form of the play line we can do something like this:

play autoname 53 say changing to 2 point 5 miles an hour exp 1 20 3 0x04

In this example, we'd be replacing the 2nd byte (0x00) with 0x01, the 6th byte with 0x14 (why not 0x20?), the 7th byte with 0x03, and the 8th byte with 0x04. You'd get a line in the workouts.txt file like this:

60 1 0 35 3 14 3 4 4c;BlockType.PlayWaveFile (offset = 53, file index = 1, exp1 = 1, exp2 = 20, exp3 = 3, exp4 = 4, fname = V0000003 -> say = changing to 2 point 5 miles an hour)

The blocks produced by the interval and midinterval lines are the same, but I've only put exp support into the interval lines. For treadmills and inclines the underlying command block type is AdjustSpeedAndIncline. For bikes and ellipticals it is called BikeAdjustRPMAndResistance. Both command block types are very similar, but the Bike block has one fewer bytes in it because of not needing separate mph and kph bytes and instead using a single RPM byte for speed control. I won't go into details of the implementation of those two command block types because you can study the workouts.txt file and figure that out with the information I've already supplied you about these other command block types. Each of them has a single experimental byte (byte #2, default=0x00). One thing I will say about them is they use a very clever way to encode the speeds (mph and kph). For example, if the speed is 2.0 mph, the mph byte would have a value of 0x14. Note that 0x14 = 20 (base 10). If the speed were 3.2 mph, the byte value would be 0x20. 0x20 = 32 (base 10). Do you see the pattern? Note that the bike block doesn't do this with RPM or Resistance bytes. A value of 0x20 = 32 (base 10) would mean 32 RPM, for example. One more thing you might find of interest about the AdjustSpeedAndIncline block type is the byte #8 is used. Its value depends on the number of 0x60 code type command blocks were in the previous interval. Formula for determining the value of this byte is 0xf7 - (9 * numberOf0x60BlocksInPreviousInterval). For the Bike block you would use 0xf8 in place of 0xf7 and do it on the 7th byte.

I can be reached at mwganson at gmail dot com for any questions/comments/suggestions. Put WorkoutGenSD Online in the subject and attach your script file, if applicable to your inquiry.