Architecture

The 1tracker front end provides a flexible customization for the editing capabilities. Engine script configures the front end to have a certain song format by setting the number and type of the pattern columns. When a row or the whole song needs to be played, the engine script gets access to the song text and compiles it into a file of a standard container format like AY, NSF, VGM, and so on. The cursor position, mute state, and other parameters can be accessed during the compilation to provide an output that corresponds to the playing situation. The resulting file is then passed to be played through Game_Music_Emu. Engine script also can provide a set of export formats.

The engine scripts are written in AngelScript (http://www.angelcode.com/angelscript/). It is a C-like scripting language. The scripts are plain text files that should have .1te extension, placed into /engines/ directory, and have certain API implemented to be visible to the front end.



Engine script should implement these functions:


void Info(void)

Sets engine title and short description. It is called for every engine when the engine selection dialog is displayed. Following function are used:

  SetTitle("title");

   Sets title of the engine. Titles are displayed in the list.

  SetAbout("text");

   Sets short description. It is just a long text string. It displayed with automatic word wrapping, '\n' also could be used.

  SetDocFile(filename);
 
   Sets a filename for an optional doc file that should be located in the docs/engines/. If found, it will be displayed at the Info tab.


void Init(void)

Configures the front end after selecting a new engine and/or clearing the module. Following function are used:

  AddColumn(channel number, column type, max value, play row);

   Adds a column. It can then be accessed by number of the call (0 for the first added column).

   Channel number is a virtual channel number, the columns are grouped into channels by this number, so few columns can act as a single channel in the editor. Use -1 for empty (separator) columns.

   Column type is either empty (-1), or allow to input a single letter A-Z (-2), or a note (0), or a numberic parameter with given number of digits (1..N).

   Max value is only used for parameter columns. Don't forget to take number of digits into account.

   Play row is a flag that makes changes of the column audible. When it is set to 1, current row will be played when a new value is entered into the column.


  SetNoteRange(lowest note, highest note);

   Sets note range for all the note columns. The numbers are in semitones, 0 for C of lowest possible octave, 12 for C of the next octave, and so on.


  SetSpeedRange(slowest, fastest, default);
  
   Sets range of speed values, lowest to fastest, and default value that is set when song gets cleared.
   

  SetSpeedColumn(column);
  
   Enable speed column support and tell the front-end which column it is (usually 0).


  SetLoopSupport(enable);
  
   Enables or disables loop support in front-end. When enable is 0, loop markers won't be displayed.
   
   
  SetPatternHeader("header");

   Sets the pattern header that is displayed above the pattern and hints function of the columns to the user. It is just a text string.


  SetInstrumentsNumber(number);

   Sets range of instruments number (from 0 to number-1). When it is zero, the instrument editor will be not available.


  SetInstrumentValue(instrument number, offset in the array,value);

   Set a byte of instrument data. May be needed to make preset instruments.
   
   
  AddExportFormat("format name", "file extension");

   Adds an export format. List of added formats will be displayed in the Export dialog. File extension should be like "ay", "nsf". The export formats are numbered as 1...N by the order of the function calls.



void Instrument(void)

Configures the instrument editor. Called at instrument change.

Instrument data is stored in the editor as an array of bytes. It is up to the engine script to decide what every byte of the array stores. In the instruments editor different controls could be assigned to every byte or pair of bytes.

Following function are used:


  GetCurrentChannel();

   Returns number of current channel. It may be useful when different channels has different instruments. In this case different instrument editor configuration could be set based off this number. Instrument data will still be shared between all the channels, so different setups should use different areas of the instrument data.

   
  GetCurrentInstrument();

   Returns number of current instrument. It may be useful if instrument features aren't equal and grouped in a way, for example if instruments below 10 have one functionality, and instruments equal or above 10 have another functionality.

   
  ItemUnsignedByte("parameter name", minimum value, maximum value, offset in the array);

   Assigns a single-byte number control to a byte of the instrument array with given offset. This control allows to enter an unsigned number of 0..255 range.


  ItemSignedByte("parameter name", minimum value, maximum value, offset in the array);

   The same as previous but for signed numbers of -127..128 range.


  ItemUnsignedWord("parameter name", minimum value, maximum value, offset in the array);

   The same as previous, but for unsigned numbers 0..65535. Two consecutive bytes in the array are used to store the word (LSB, MSB).


  ItemSignedWord("parameter name", minimum value, maximum value, offset in the array);

   The same as previous, but for signed numbers of -32767..32768 range.


  ItemHexString("parameter name", length, offset in the array);

   This control allows to enter a string of HEX values (0..F, two values per byte) with given length. Takes the length of consecutive bytes in the array.


  ItemTextString("parameter name", length, offset in the array);

   This control allows to enter an arbitrary ASCIIZ string with given length minus 1.
   
   
  ItemOption("parameter name", "one|two|three", offset in the array);

   This control allows to select one of provided in the second string options. The options names are separated with | character. The control uses one byte of the array, so up to 256 options could be used. First option in the string will have number 0, second 1, etc.

   
  ItemLabel("parameter name");
   
   This control only adds a control's caption of any length, not affecting to the editor layout. Can be multiline using \n to split into lines. Could be useful to insert empty rows between the controls for better separation, or to provide some information that could depend from current channel and instrument number. The control is skipped when user navigates through the controls list.

  ItemEnvelope("parameter name", length, max value, resolution, offset in the array)
  
   Displays editable envelope of given length, that should not exceed screen width. The values range from 0 to max value-1. There is two possible resolutions, 1 (full height) or 2 (half-height).
   
  ItemSelectFile("parameter name" ,"file extension with dot", save mode flag, file dialog id number);
   
   Once selected, this control shows the file selection dialog. If file selection is confirmed, it then calls back the SelectFileCallback function in an engine script, passing a string with full path and filename into it, as well as a save mode flag (set to non-zero if the dialog is used to get a filename to save a file), and an assigned file id number. The latter can be used if an engine needs a few file dialogs, to differentiate between those.
   


void SelectFileCallback(string filename, int file_id)

An optional callback for file selection called from the instrument editor. May be used for things like loading samples. The file_id is the number specified in the ItemSelectFile.



void Compile(format, startRow, oneShot, useMute)

Compiles song into a file. This function used to play song from beginning from a row, with and without mute, and for export.

Format is a number 0 to N. When the format is 0, the function should compile a file that takes other parameters into account, providing correct result (play from current row with mute etc) that will be played in the editor. Other numbers are assinged to the added export formats, the resulting file will be then saved on the disk.

startRow is the row number where the song should start. However, if the engine supports loop, you need to compile whole song, and only start it from given row, to make the loop work properly - as given row could be located between start and end loop points.

oneShot is 1 when only current row should be played. In this case it is not necessary to compile whole song, just one row will be enough.

useMute is 1 when mute state should be taken into account. It could be done by skipping channels that are muted. Normally it is always 1 when format is 0, and 0 when format is not 0.

Following function are used:


  GetChannelMute(channel number);

   Returns mute state for a given channel. It is 1 when the channel is muted.


  GetSongLength();

   Returns actual song length in rows.


  GetSongLoopStart();
  
   Returns the number of row where loop starts (if supported by engine).
   
   
  GetSongLoopEnd();
  
   Returns the number of row where loop ends (if supported by engine).
   
   
  GetSongData(row, column);

   Returns a byte for given row and column. The meaning of the byte is defined by the AddColumn function in the Init function. For notes it is 0 for empty space, 1 for the rest note, 2..121 for notes C-0 to B-9. For values it is 0 to max defined for a column.


  GetSongSpeed();

   Returns global speed. It is a single byte value. There is no relation of the speed and time, it is up to the engine.


  GetSongSpeedMax();
  GetSongSpeedMin();

   Returns max and min global speed set by the engine itself with the SetSpeedRange call, just in case it would need it later.
   
   
  GetSongTitle();

   Returns a string with the song title.


  GetSongAuthor();

   Returns a string with the song author.


  GetInstrumentValue(instrument number, offset in the array);

   Get a byte of instrument data.


  CompileError("string with error message");

   When compilation fails by some reason, it could be reported to the user with a single line error message.


  DumpOutput(array);

   Transers compiled binary file to the front end, so it could be played or saved. The input is a dynamic array, the one that is defined in the script like uint8[];
   
  SetExtraInfo(string);
  
   Sets a multiline text string to be displayed in the info tab. Could be used to report compiled data size, the Compile function is called the same way as for playing the whole song just before displaying the info tab.


Various supplimentary functions:

  Print(string);
  
   Puts a string into log, could be useful for script debugging purposes.
   
  Random();
  
   Returns 32-bit random number.
   
   
  GetBaseDirectory();
  
   Returns a string with base directory of the tracker.
   
   
  LoadBinaryFileAsString(filename,offset,length)
  
   Loads a binary file and returns it as an abcd string (a..o pairs to encode two nibbles of a byte). Offset and length parameters allow to load from given offset in the file. When the length is a negative value, file loads from the offset till end of the file. Actual engines use a proxy function LoadBinaryFile implemented in the common.1tl that return a uint8[] array instead of string.
   
  SaveStringAsBinaryFile(filename,data)
  
   Saves contents of an abcd-encoded string into a binary file. Actual engines use a proxy function SaveBinaryFile implemented in the common.1tl that takes a uint8[] array instead of string as input data.
   


In addition to these functions, there are helper functions in the commons.1tl, zxspectrum.1tl, z80ass.1tl. These are scripts that could be included into an engine script via #include directive. They contains things that simplify construction of certain file formats.