ChibiTracks is designed for simple music and provides a multiplatform solution to play a single compiled music binary file on multiple platforms.
ChibiTracks is NOT designed to provide advanced features comparable to solutions like ArkosTracker
To understand how ChibiTracks works, lets look at all the components:
ChibiSound PRO
ChibiSound is the sound driver that handles the particularities of a system, there is typically one driver per system, though the CPC and MSX drivers are essentially identical except for the AY register setting routines.
The original 'ChibiSound' gave us one channel, one Volume bit, six pitch bits, and the ability to turn noise on. Pitches were not matched across systems, so sound 32 won't sound the same on all systems.
The updated 'ChibiSound Pro' gives us all the channels provided by the hardware, 8 volume bits, 16 pitch bits, and the ability to turn noise on. Pitches were not matched across systems, however the 'ChibiOctave' lookup table provides values which ARE matched across all systems.
ChibiSound PRO is essentially a reduced subset of AY functionality, and was designed on the Z80 - it's 'PRO' suffix is a parody of the 'SoundBlaster PRO' - which could only do 8 bit sound so wasn't up to professional standards! (neither is ChibiSound PRO)
ChibiSound PRO provides a standard interface to the underlying hardware, it allows the following features to be set for each channel on the underlying hardware:
Function |
Register |
Notes: |
Channel Number (bit 0-6) Noise On/Off (bit 7) |
H | Multiple channels can be supported, but on single channel
systems only Channel 0 will be sure to play. If possible Channel 0 will be a center channel, Channels 1+ may be left/right Noise bit turns the noise effect on (1) or off (0) - this can be set on any channel, if the underlying hardware only supports one noise channel, this will be resolved by the driver. |
Volume | L | Set volume of the channel (0-255). Higher numbers are louder. O is off |
Pitch | DE | Set the pitch of the channel (0-65535). Higher numbers are
higher pitch. Using DE does not standardize the resulting pitch - however a 'Lookup table' of notes 'ChibiOctave' provides a standardized way of getting the correct DE value to get a pitch correct note on the platform. |
Chibisound PRO does not offer features like Envelope, LFE etc, as providing consistent functionality across different platforms would not be realistic.
ChibiTracks
ChibiTracks is the player for music files.
A Song contains multiple Channels (Currently tested for 3)
A Channel takes it's commands from a Sequence of Patterns
Patterns contains a set of lines. Each of the lines contain a sequence of one or more commands, and a delay to wait until the next line.
Channels also play instruments (usually started by the pattern). These are also a sequence of commands and delays, and are used to set ChibiSound settings for the playing sound. A channel will stop making a sound when an instrument ends (unless it loops)
It is important to note that functionally Patterns and Instrument command lines are identical - they are literally processed by the same code in ChibiTracks, however it would not make sense to
ChibiTracker PRO can output two kinds of file - a binary file (.CBT)
which can be used with any player, or an ASM file - the default format
used to save and reload files (.ASM) - This is a VASM/MAXAM compatible
Z80 ASM file which contains all the binary data in DB/DW lines, and all
extra data for the player in rem statements (Starting with a semicolon).
There should be no difference between an compiled ASM and a binary CBT
file.
There are two optional functions which can be enabled (but make the player larger)
ChibiTracks_AllowRelocation - This allows a song to be played from a different address than it was built, For example if a CBT binary was a save address of &4000, it can be loaded and played from address &8000 provided this function is enabled.
This function is mandatory on 68000, as only 16 bit addresses are specified in the binary files.
ChibiTracks_AllowSpeedChange - This allows the speed of songs to be changed via the 'SongSpeed' function - allowing songs to be sped up or slowed down on playback at the cost of a slightly slower and bigger player - Alternatively you can save with the 'PreMultiply Song Speed' option - in which case it's not needed.
ChibiTracks - A song file!
Lets take a look at the ASM file to see how ChibiTracks works.
At the start of the ASM file is the 'ChibiTracker' block These lines all appear as comments in the ASM file, but are functional lines to ChibiTracker PRO. These define settings and data that are used by the editor, but are not needed by the ChibiTracks player - because they are in REM statements, they will not be contained in any built file. |
![]() |
Next is the binary header - this is used by ChibiTracks
player.... All 16 bit addresses are in Little Endian (even on
68000) This contains the channel count (Should always be 3 at this stage as the player does not support more) Repeatpoint is the entry number in the sequence which the song should restart when it reaches the end. Songspeed is the multiplier for the line delay, A delay of 2 with a song speed of 5 would wait 10x interrupts before moving to the next line. NOTE: it is possible to assemble the player without songspeed support (ChibiTracks_AllowSpeedChange not defined). You can still get the same result by saving with 'Premultiplied songspeed' option enabled in ChibiTracker PatternList is a pointer to the pattern list table. This list appears at the end of the ASM file InstrumentList is a pointer to the Instrument list table. This list appears at the end of the ASM file There then follows one 16 bit address for each channel, which points to the sequence of patterns for that channel. |
![]() |
The Sequences define the pattern numbers that make up the
song. These are looked up in the PatternList table. Patten 255 represents the end of the song, at which play will resume at the repeatpoint. |
![]() |
Here are two sample patterns. Each line in the ASM file is a line of the pattern. Lets take a look at one! db 2,&0D,&3A,&0B,&01,&00 ;2 first is the length of the delay after this line before the next line is processed. 2 means 2*Songspeed ticks until the next line. There then follows a sequence of commands to be processed by ChibiTracks. &0D means set pitch to note &3A &0B means play instrument &01 &00 is the 'End of commands' command, the player will wait until the delay finishes before processing the next line db 1,&10,&00 ;33 Line 33 of the first pattern just has the 'End of pattern' command &10 - This tells the player to move to the next pattern in sequence - The delay has no effect in this case |
![]() |
Instruments are in the exact same format, and use the exact same
decoding routine of the player. However!... a instrument should end with a delay of 0, at which point the instrument will stop sounding. it is also possible to loop an instrument with a loop (Byte &0C, followed by a negative byte), the byte value will be added (subtracted) from the current position in the command sequence, effectively looping part of the sequence. To stop such a loop, a silent instrument, or other instrument should be played on the channel to stop the looping one. |
![]() |
At the end of the song data is the PatternList and
InstrumentList, These are a table of little endian 16 bit addresses of the pattern and instrument command sequences. |
![]() |
ChibiTracks - Byte commands
There are two types of command in ChibiTracks.
One Byte commands have the command number in the high nibble, and the 4 bit parameter in the bottom nibble, For Example &F1: &F_ = Shift volume &_1= Volume up 1
Two Byte commands have a second byte parameter, their top nibble is always Zero, For Example &0E,&FF: &0E = Set Volume &FF= New Volume
Command Type |
Byte Data |
Notes |
One Byte | &Fx |
Vol shift -8 to +7 |
&Ex |
Pitch shift down 0 to -15 |
|
&Dx |
Pitch shift up 0 to +15 |
|
&Cx | Noise state x (1=on 0=off) |
|
&10 |
End of Pattern |
|
Two Byte | &0F,x | Pitch to x |
&0E,x | Vol to x | |
&0D,x | Note to x (Even=true pitch Odd = Flat/Sharp) | |
&0C,x | Loop to Offset -0 to -255 bytes (eg &FF = back 1 byte) | |
&0B,x | Play Instrument x | |
&01,x &02,x &03,x &04,x | Reserved for you to add custom functionality to the player! (Named in ChibiTracker IXH IXL IYH IYL) |
Top Nibble commands: &B0 A0 90 80 70 60 50 40 30 20 spare!
Two Byte Commands: &0A 09 08 07 06 05 04 03 02 01 spare!
ChibiTracker PRO is a DotNet 3.5 application, it has also been complied
correctly in newer versions of DotNetYou can find the sourcecode for
Chibitracker in the Z80 sources.... see the folder "sources/C#
Tools/ChibiTracker"
There's a pre built EXE file in the sources/tools folder.
ChibiTracker supports a variety of MIDI controllers where available.
![]() |
ChibiTracks
and ChibiTracker are far from the best music player and editor! Their 'strength' is they are simple and designed to be easy to port to multiple systems, and play the same song file on all those systems. If you're just looking at the AY sound chip, take a look at ArkosTracker, which is better than what I can write! |
Installing ChibiTracker PRO
If you do not have SlimDX installed, ChibiTracker will not run correctly | ![]() |
We need to install the SlimDX
Runtime |
![]() ![]() |
ChibiTracker will now load correctly! On start up ChibiTracker automatically loads a set of sample instruments from the "ChibiTracker_defaults.cfg" file |
![]() |
We can load the sample song from File-
Load Chibisound ASM |
![]() |
There is a sample song in folder "ChibiTracker_defaults.cfg" "ChibiAkumasTheme.asm" is the ChibiAkumas theme song (Converted from the ArkosTracker1 original) Chibitracker saves and loads from ASM files - these music files can also be assembled with VASM Z80 and played in your programs! |
![]() |
You can play the song with the play button. | ![]() |
UI Overview
Here is the ChibiTracker UI The main panel is the pattern editor. above that is the Tab Strip - to select other tools At the top is the Playback buttons for playing the song At the top-left is the instrument list at the middle-left is the sequence list - this is the order of the patterns that make up the song - each column in the list is a different channel. at the bottom-left is the octave selection - this changes ChibiTracker tries to mimic ArkosTracker1 for keypresses and UI layout. |
![]() |
This is the instrument list. It's used to select an instrument to enter into the pattern when editing. It's also used to select the edited instrument in the instrument editor. |
![]() |
This is the sequence list, it
defines the patterns played in the song. Each column is a different channel, each row is a pattern to play at that point. You can change the selected pattern at a point by double clicking, and selecting a new pattern from the list. |
![]() |
This is the Play controls and
Status. The play controls allow for start, stop, looping and edit of the pattern. The Status line shows the byte data produced by the player, and MIDI data received from the controller when connected. |
![]() |
This is the Pattern editor.
each column is a different channel. When in 'Edit Mode' (purple background) the pattern can be edited. Keyboard Keys: Space - Toggle edit mode letter / num keys - note keys. Num0 - Num9 - Drum (when editing) / Select instrument 0-9 Backspace - clear line Insert - Add line Delete - Remove line F1 - Restart Play F2 - Resume Play F3 - Pattern Loop F4 - Stop F8 - Octave down F9 - Octave up F9 - Set Volume F10 - Set Pitch |
![]() |
This is the instrument editor...
it's rather basic at the moment! A line of the instrument can be selected in the top list. The Command / events for that line can be altered in the bottom panel. The Delay is the time this instrument commands occurs before the next line Each command can change the Pitch,Note,Volume and Noise - leave a box blank to make no changes. By changing the Pitch repeatedly (as shown here) we can make a 'wavy' sound. Noise can be used for drum effects. See the sample instruments for some examples. The data in the top list is in the format: Delay (byte count) Commands........ |
![]() |
This is the settings page. It
allows various changes. Song Settings define options of the edited song. Editor settings change how the editor works, if you're getting 'clicking' or poor performance, you can try changing these. Midi Controller Allows you to connect a Midi Controller such as a piano type keyboard. |
![]() |
This is the pattern list. You can double click on a pattern to rename it - This is optional, and is just to allow you to organize your song more easilly |
![]() |
This is the Info Panel. It shows some help info by default, and may show debug information in some special cases. |
![]() |
This is the Midi Editor. This only appears if you connect a midi device. This allows you to map buttons and dials to ChibiTracker events. Of course A keyboard can also be used to set notes in the pattern! |
![]() |
UI Overview
Lesson
CT2
- ChibiTracks Header Load and Init Lets start to look at the Z80 ChibiTracks music player code! |
![]() |
Multiplatform_ChibiTracks.asm
|
![]() |
State Memory, symbols, and the silent instrument
ChibiTracks uses a 16 byte
block to keep track of each of the channels (16*3=48 bytes total) These contain the values to be passed to the ChibiSound PRO driver, There are address pointers for the playing Instrument Line and Pattern Line, as well as 'Timeouts' after which the next line must be processed. There is also an address for the sequence position - this has no timeout, as the next sequence is loaded when the playing pattern ends. ChibiTracks will zero and initialize the memory it is provided for its channel data. Instrument/Pattern/Sequence data are loaded from the playing song. There are EQU definitions which are used as offsets to the corresponding data for a channel |
![]() |
Some Extra EQU definitions point to the start of the channels,
and the end of the data block |
![]() |
When a channel ends it must be switched to a 'silent instrument'
- this is an instrument script which only sets the channel volume
to zero |
![]() |
![]() |
ChibiTracks
is designed to work from ROM, so the variables for channels and
other core purposes can be loaded in a small block of RAM. In addition to the 48 bytes here there are aso : SongOffset (word),SongBase (word), Instrumentlist (word), Patternlist (word), SongChannels (byte),SongSpeed (byte) and RepPoint (byte). So ChibiTracks needs less than 64 bytes RAM total! ChibiSound PRO may need a few more though - depending on the platform! |
Common functions
A song has a built in 'Load address' where it's expected to be
in memory, however it's possible to play it from any address if
the 'ChibiTracks_AllowRelocation' option is enabled. There is a shared sub to add this offset to DE, though there are many other such routines contained in the code where other registers are used |
![]() |
The tracker updates patterns after a certain 'time' has passed.
This time may need to speed up and slow down depending on how fast
the end system is. enabling 'ChibiTracks_AllowSpeedChange' will calculate this speed change at play time. Alternately, enable 'PreMultiply SongSpeed' during export - and this calculation will be done in advance and 'ChibiTracks_AllowSpeedChange' is not needed - saving memory and CPU power, but losing the ability to change song speed later. |
![]() |
We run StartSong to init the music. RAM variable SongBase should point to the music data. Before we start playing anything, we need to clear the contents of the ChibiTracks RAM We can zero pretty much everything - but we need to program each channel with its number. |
![]() |
The first time we play as song, we start fron sequence entry 0
(we may loop back to an alternate position) We first load in the song channel count and the repeat point If we use speed changes, we load that too. If we are allowing relocation we calculate the difference between the address we're playing from, and the address the song was exported to play from - the result is out offset. We load in the pattern list address Finally we load in the instrument list address |
![]() |
The next three addresses are the song sequences. we use these to Initialize each of the channel patterns |
![]() |
We use any offset in A (for looping back) as an offset, and load in the first pattern in the channels sequence. | ![]() |
We use GetNextSequence to read in a pattern number and get the
channel to play it. Pattern=255 is the end of the song - in which case we force a restart. Otherwise we calculate the address of the pattern, and load it in to the channel pattern address. |
![]() |
To restart the song we clear down the stack, load in the repeat pos (sequence offset) and reload the song. | ![]() |
Lesson
CT3
- The main play routine Lets look at the main player code of ChibiTracks |
![]() |
Multiplatform_ChibiTracks.asm
|
![]() |
We run ChibiTracks_Play during our
interrupt routine. This uses AF,HL,BC,DE and IX only - IY and shadow registers are not used We update all the channels separately. First we decrease the delay time for the current pattern line... if it reached zero we need to process a new line of the pattern. |
![]() |
Next we update the channel itself (for any current playing instrument. If the delay time for the current instrument IS zero - there is no instrument playing any more... if it REACHED zero, the we need to process a new line of the instrument. | ![]() |
We repeat for all the other channels, and run the ChibiSoundPro_Update routine. this is only used on systems like the ZX Spectrum Beeper, which needs positive action by the cpu to generate the sound | ![]() |
To update the pattern we read in the
address of the next line, we load in the Delay (the time the next
line should play) and multiply it by the playing speed. We process the commands for this pattern line, and store the new pattern address. |
![]() |
To update the channel we read in the
address of the next line, we load in the Delay (the time the next
line should play) - if it's zero the instrument has stopped and we
need to silence the channel We process the commands for this instrument line, and store the new instrument address. |
![]() |
We need to update the sound the channel is playing. We use TweenTone to calculate the DE pair for the note frequency from the ChibiOctave lookup table We load H and L from the channels settungs and use ChibiSoundPro_Set to update the sound the channel is playing |
![]() |
There are two types of
commands. One byte commands use the top nibble as the command number, and the bottom nibble as the parameter Two byte commands use the first byte as the command number (The top nibble is always zero) , and the following byte as the parameter A Zero byte &00 is a special case - and represents the end of the script. |
![]() |
We can use a one nibble pitch bend for small adjustments (slight
wavy notes). There is a 1 byte version as well for larger shifts. |
![]() |
A -8 to +7 volume shift can also be used. |
![]() |
The noise command is a bit of an odd case! The only valid options are &C0 or &C1, as only the bottom bit of the parameter is used. It either turns noise on (&C1) or off (&C0) |
![]() |
The two byte command routine loads the second byte into the C
register |
![]() |
We have commands to set the playing note (in the octave) volume
and pitch bend These are just loaded into the channel data for processing by the channel code. |
![]() |
The loop command uses the parameter as a negative byte offset
within the script - to work correctly it should point to a command
byte within the script It's used for repeating instruments - for example a wavy sound with a looping pitch bend. |
![]() |
If an instrument is to be triggered, the instrument's address is
loaded from the Instrument List. The Delay is set to 1, so it will reach 0 next tick and force the first line of the instrument to play. |
![]() |
Lesson
CT4
- Tone Calculation The Note number and pitch shift need to be used to calculate a 16 bit DE value for ChibiSound Pro Lets learn how! |
![]() |
Multiplatform_ChibiTracks.asm
|
![]() |
ChibiSound PRO is the platform specific driver for the sound on
each system. There is a matching ChibiOctave table which allows the lookup of the correct frequency for notes. It only contains the pure tones, not sharps or flats, however these can be calculated by adding two notes and dividing by two to calculate the in-between value. |
![]() |
GetChibiTone will lookup the note E from the
lookup table. The format is %NNNNNNNS - where N is a Note, and S 'sharpens' the note. |
![]() |
TweenTone takes a note in E, and a shift in A The top nibble in A is a direct shift to the note number in E, The bottom nibble in A is a 0-15 'fractional shift' to the note number in E, allowing a smooth transition between note E and E+1 |
![]() |
Tween16HLDE calculates
a fractional position between HL and DE. it returns a result in HL A should be a value of 0-15, any value over 16 is treated as 16 If A=0 then the result is HL=100% of the value in HL If A=16 then the result is HL=100% of the value in DE If A=8 then the result is HL is halfway between HL and DE and so on |
![]() |
Fraction16 performs the calculation HL=HL * (A/16) We move our value into DE, and repeatedly divide it by two, testing the matching bit in A to see if we need to add it to HL to create the correct resulting fraction. This gives a quick calculated 1/16 - 15/16 fraction of HL |
![]() |