Library:
  neilsciatrackerlib #56

Author:
  Neil O'Rourke, 6 Victoria St, TAMWORTH, NSW 2340, AUSTRALIA

Overview:
  Many thanks to Neil, from what I have seen on the net there are
  already many BlitzUsers using this library to great success. I'm
  trying to fit the example code on the disk as I type...

Commands:
  LoadTrackerModule:   success=LoadTrackerModule(TrackerModule#,FileName$)
  StartTracker:        success=StartTracker(TrackerModule#)
  StopTracker:         StopTracker
  SetDMAWait:          SetDMAWait value
  FreeTrackerModule:   FreeTrackerModule TrackerModule#
  SetTrackerModule:    SetTrackerModule TrackerModule#,ModuleAddress
  GetTrackerSize:      trackerlength=GetTrackerSize(TrackerModule#)
  GetTrackerLocation:  GetTrackerLocation (TrackerModule#)
  GetTrackerEvent:     trackerevent=GetTrackerEvent
  CheckTrackerEvent:   success=CheckTrackerEvent
  WaitTrackerEvent:    WaitTrackerEvent
  CheckTrackerModuleID:success=CheckTrackerModuleID(TrackerModule#)
  GetTrackerVolume:    volume=GetTrackerVolume(TrackerChannel#)
  GetTrackerNote:      note=GetTrackerNote(TrackerChannel#)
  SetTrackerTempo:     SetTrackerTempo Tempo
  GetTrackerInstrument:instrument=GetTrackerInstrument(TrackerChannel#)
  GetPatternPosition:  PatPos=GetPatternPosition
  GetSongPosition:     SongPos=GetSongPosition
  SetSongPatternPosition:  SetSongPatternPosition Pattern#,Position#
  GetSongLength:       NumPatterns=GetSongLength
  SetTrackerMask:      SetTrackerMask Mask
  GetTrackerNoteNumber:notenumber=GetTrackerNoteNumber(Channel#)
  StartTrackerPatPos:  ret.l=StartTrackerPatPos(TrackerModule#,Pattern#,Position#)
  PauseTracker:        PauseTracker
  RestartTracker:      RestartTracker
  PlayTrackerSample:   PlayTrackerSample Sample#,Period,Volume,Channel
  InitTracker:         success=InitTracker(TrackerModule#)
  GetSampleLocation:   location=GetSampleLocation(Sample#)
  GetSampleLength:     length=GetSampleLength(Sample#)
  GetSampleName:       name$=GetSampleName(Sample#)
  GetTrackerName:      name$=GetTrackerName(TrackerModule#)

Author's Documentation:

CIATracker.lib Documentation

Neil O'Rourke

Version 1.6 (24/6/94)


Introduction
~~~~~~~~~~~~
The standard soundtracker replay routines supplied with Blitz Basic 2 have
many faults, which this library attempts to overcome.  Some of the features
are:

  - Plays all ST/NT/PT songs that utilise either the VBLANK timing or the
    more recent CIA based timings
  - Plays back correctly on 50/60Hz systems, running either PAL or NTSC
  - Contains more specialised functions for advanced programmers
  - Enables the programmer to syncronise graphics with their music


Credits:
~~~~~~~~
Original ProTracker playroutine by Amiga Freelancers, converted and
enhanced for Blitz by Neil O'Rourke.  Naggings from Roy, Jeff and Richard.


The 1.6 upgrade
~~~~~~~~~~~~~~~
This is a maintenance upgrade, with some subtle (and not so subtle) bugs
fixed or noted.

LoadTrackerModule no longer crashes the machine if the name was invalid.

SetTrackerMask has been removed for the moment (this was causing the
TrackerEvent system to foul up)

WaitTrackerEvent has a nasty tendancy to lock the machine up.  Don't call
this command, use While NOT CheckTrackerEvent:Wend to wait for an event if
you must.  WaitTrackerEvent currently sits on the VBLANK interrupt, however
I think the problem is due to the sheer bulk of ciaTrackerLib getting in
the way of checking.  I think.

GetTrackerNoteNumber was found to be chewing up CPU time, and has been
replaced by a new version that chews up 2K of ram extra.

I've found that if you have run errors enabled to bring up the requester,
your module won't start sometimes.  Don't know what to do about this, as I
don't know what causes it.


Quick Usage:
~~~~~~~~~~~~
First you must set the DMAWait time with the SetDMAWait command.  Then,
enable all the channels with SetTrackerMask.  Load the module you want with
the LoadTrackerModule command, and then either StartTrackerModule it, or
InitTracker/RestartTracker later on.


Basic Commands
~~~~~~~~~~~~~~

success=LoadTrackerModule(TrackerModule#,FileName$)

Loads the named module into chip ram, ready for playing.  This command can
only be called in Amiga mode.  success is a boolean return code (true).  If
the load fails for any reason, success returns the AmigaDOS error code.

Note that there is an implicit call to FreeTrackerModule for whatever
module you are trying to load.  However, if you want to load another
module, don't try to load it on top of the existing one that is playing.
Use another TrackerModule# (you have from 0 to 8).  The results are
unpredictable, and range from nothing to a system crash.  We can't call
StopTracker, because this will stop everything.


success=StartTracker(TrackerModule#)

Starts to play the requested module, stopping any modules already playing,
or restarts the current module, and returns true.  Returns false if the
module couldn't be started for some reason (like it isn't loaded).


StopTracker

Stops the current module


SetDMAWait value

This sets the DMA Wait for your machine.  On a standard 7.14MHz 68000 based
machine, the value is the default (300).  However, faster machines can cause
the replay routine to skip notes.  On a 25MHz 68030 machine, the suggested
value is 900.  Set this as low as possible so that you still hear all the
notes.  A future upgrade *may* do this automatically, but I have no
intention of implementing it at this stage, as I don't know what DMAWait to
set for different speed processors and version motherboards.

DMA wait is important.  Technically, when the replay routine loads the chip
registers with the information about the current note (location, volume,
pitch), a delay is needed to ensure that the chips actually get the data,
which happens on the next DMA slot.  Since the CPU can be clocked
independantly of the motherboard, we can't just delay by a set amount.  How
this problem has been solved is a busy wait that simply loops around the
number of times as specified by the DMAWait value.  A low value therefore
lessens the load on the CPU but increases the chances of missing notes
while playing a song.  Too high a value can bog the CPU down, and slow the
song down as interrupts are missed.  


FreeTrackerModule TrackerModule#

This frees a module loaded with LoadTrackerModule.  You cannot free a
module that has been set up with SetTrackerModule (see below), but there is
nothing to stop you trying.


SetTrackerModule TrackerModule#,ModuleAddress

This sets an arbitary area of memory as a tracker module, useful if you
have BLoaded a file and want to hear if it is a module. Caution: a
non-module may crash the Amiga.


trackerlength=GetTrackerSize(TrackerModule#)  GetTrackerLocation (TrackerModule#)

Both these functions return information about the module that has been
loaded with LoadTrackerModule.  There should be no need to use this
information, and these commands are only included because they served a
purpose in debugging a long time ago, and to remove them would cause
problems with the Blitz tokens


trackerevent=GetTrackerEvent

This command is a customised extension to the ProTracker replay routine.  A
"TrackerEvent" occurs when the replay routine comes across a $8xx command.
This command is not defined in the command list, and many demos (eg Jesus
on E's) use it to trigger effects.  This command gets the most recent
TrackerEvent, so any program looking at this will have to compare the
current value to the value that triggered the current effect.


success=CheckTrackerEvent

This routine checks to see if a TrackerEvent has occured since the last
time the routine was called, and returns True if it has.  Use
GetTrackerEvent to determine what data the $8xx command had.


WaitTrackerEvent

** V1.6: DO NOT USE THIS COMMAND! **


success=CheckTrackerModuleID(TrackerModule#)

This checks the module for the standard Pro/Noise/SoundTracker ID string
"M.K." (or "M!K!" in the case of a 100 pattern PT module), and returns True
if one of them is found.  This means that you can safely call StartTracker.

Note that there is no 100% guarenteed way of determining what is a module
and what isn't.  Bit Arts, for example, remove the M.K. identifier to make
it harder to rip modules, so if you're writing a module ripping program,
you have to take this result with a grain of salt.


volume=GetTrackerVolume(TrackerChannel#)

Returns the last volume set by a $Cxx command for the named channel, which
are numbered from 0 to 3.  This is not the "real" volume of the sample that
is currently playing.


note=GetTrackerNote(TrackerChannel#)

Returns the note that the play routine has just played in the named
channel.  This command is really only useful for graphic bars or simple
syncronisation of graphics to the music, but for that purpose the
TrackerEvent commands are far more flexable.  Note that the value returned
is the period of the note.  You have to look up the note in a period table
to find out what was actually being played.


SetTrackerTempo Tempo

Sets the tempo of the current song.  Note that a tempo command ($Fxx) will
override any value set by this command.  This command is really a stub to
the actual $Fxx command in the playroutine, and has all the features
associated with it.  Check your tracker docs for more details.


instrument=GetTrackerInstrument(TrackerChannel#)

Gets the instrument that is playing in the channel.


PatPos=GetPatternPosition

This returns the current position in the current pattern.


SongPos=GetSongPosition

This returns the current pattern that is playing in the song


SetSongPatternPosition Pattern#,Position#

This command sets what pattern to play, and from what position.  Use this
while a song is playing to jump to another pattern (eg. a game over music).
Call StartTrackerPatPos() to start a module from scratch.


NumPatterns=GetSongLength

Returns the number of patterns in the current module.  Useful for displays
like in IntuiTracker, where the title bar of the window gives a display
that can be done like:
  NPrint GetSongLength,":",GetSongPosition


SetTrackerMask Mask

** REMOVED IN V1.6 **


notenumber=OldGetTrackerNoteNumber(Channel#)

This returns the number of the note played on the specified channel, with
C-1 being note 1.  Of use really in creating "equalizer bars".

V1.6: This command has turned out to be a CPU-hog!  The new implementation
will consume a lot of memory but will be much faster.  When you load your
old programs, GetTracker... will be replaced by OldGetTracker..., so your
code will continue to work.


ret.l=StartTrackerPatPos(TrackerModule#,Pattern#,Position#)

This starts the named module at the requested pattern and position.  In all
other respects it is the same as StartTracker.


PauseTracker  RestartTracker

These commands allow you to stop a tracker module are restart it at a later
time.


PlayTrackerSample Sample#,Period,Volume,Channel

Plays a sample through the channel.  The module must not be running.


success=InitTracker(TrackerModule#)

Identical to StartTracker, except that the module doesn't start, but is
initialised.  Of use with the commands that use the current tracker module.
Use ReStartTracker to start playing.


location=GetSampleLocation(Sample#)

Returns the address in memory of the named sample in the current module.


length=GetSampleLength(Sample#)

Returns the length in words of the named sample in the current module.
Multiply by two to get the byte length.


name$=GetSampleName(Sample#)

Returns the name of the sample in name$.


name$=GetTrackerName(TrackerModule#)

Returns the name of the module in name$


BuildNoteTable

This command builds a note table for use with GetTrackerNoteNumber.  It
consumes 2K of memory for the look-up table.


notenumber=GetTrackerNoteNumber(Channel#)

This returns the number of the note played on the specified channel, with
C-1 being note 1.  Of use really in creating "equalizer bars".

For speed purposes, no error checking (like, has the note table been
built?) is done.


Notes:
~~~~~~
Quite a number of these commands extract their data from the playroutine in
real time; that is, around fifty times a second (depending upon the tempo).
Therefore, the value your program receives could well be very different
from what is actually happening in the song.


Disclaimer:
~~~~~~~~~~~
By installing this software on your system, you are agreeing that I have no
liability as to the outcome of such use.  If, for example, you use a
command as documented and a floppy disk is ejected from your disk drive
with such force that it severs your head from your neck, tough.  Next time,
duck.
