\ \ JiNX Sound System Interface Description; DeepThroat/JiNX [Mark Hodson] / /
 \ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ /
  \ Editor-In-Chief: Penfold/JiNX /            \ Revision 9, for JSS V1.0e /
   ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~              ~~~~~~~~~~~~~~~~~~~~~~~~~~~

THIS DOCUMENT DESCRIBES THE JINX SOUND SYSTEM IN TERMS OF ITS DOS PORT

For information on other ports, see the associated documents for a description
of the interface differences.


The JiNX Sound System's interface consists entirely of functions written in C.
To access the standard interface, #include "jss_dos.h" in your programme (for
the DOS port of the JSS that is!).

Various parts of the interface are described below.  For the most part they
are listed in the order they should be called in.


CONTENTS:

1.  Error Codes
2.  SetUp and Initialisation
3.  Information Gathering
4.  RunTime Functions
5.  Module/Sound System Manipulation
6.  Closing The Sound System
7.  Timing and Synchronisation
8.  Accessible Structured Module Data
9.  Alternative Means of Choosing Your Sound Card
10. Notes on Hooking Interrupts


1. Error Codes
~~~~~~~~~~~~~~
 ________________________
/ int JiNX_SS_ErrorCode; \
 ______________________________
/ char *JiNX_SS_ErrorString[]; \

Certain critical functions return pointers or integers.  If the pointer or
integer is ever NULL, this signifies an error has occurred.  For more detail,
check the JiNX_SS_ErrorCode integer.  For a description of the error, index
the JiNX_SS_Error_String[] set of strings with the JiNX_SS_ErrorCode.

eg. result = JiNX_SS_CriticalFunction();
    if (result == NULL)
    {
        printf("JSS Error: %s\n", JiNX_SS_Error_String[JiNX_SS_ErrorCode]);
    }


2. SetUp and Initialisation
~~~~~~~~~~~~~~~~~~~~~~~~~~~
 ___________________________________________
/ int JiNX_SS_ChooseDevice(int autodetect); \

This is a simple function designed to allow the user to choose their sound
card.  Autodetection is optional, and occurs automatically if the autodetect
parameter is non-zero.  Returns NULL if an error occurred.  If you want to
make your own better or more flexible routine, see section 9.

  _______________________________________________________________________
 / int JiNX_SS_PreInitSoundSystem(int zero_volume_cutoff_ticks,          \
/      int reverb_delay_in_ms, int reverb_level, int continuous_samples); \

This is an optional initialisation function, giving access to certain internal
variables used by the sound system.  Returns NULL if an error occurred.

 - Zero Volume CutOff Ticks [1 to 100000, default 256]: Specifies how long a
   note may remain at zero volume before being killed.  If your module relies
   on notes staying active at zero volume for long periods, you may need to
   increase this.  Decreasing this can improve performance.
 - Reverb Delay (in ms) [50 to 300, default 100]: Specifies the reverb delay
   for the reverb effect in the JSS.  Once the sound system is initialised,
   changing this value has no effect (it cannot be changed runtime).
 - Reverb Level [1 to 4, default 2]: Specifies a volume divisor of 2^Level
   for the reverb effect.  Level 1 gives the loudest reverb echoes, and 4 the
   softest.
 - Continuous Samples [0 to 8192, default 0]: Specifies the minimum number of
   samples which will be stored in the internal mixing buffer.  This buffer,
   normally not accessible, can be accessed by declaring the pointer:
        extern int *JiNX_SS_OutputBuffer;
   This buffer is useful for some forms of rough analysis (FFT etc).  If
   stereo is enabled, left and right samples exist as a sample pair in the
   buffer (ie. 8192 continuous samples => 16384 integers in the buffer).

 ____________________________________________________________________
/ int JiNX_SS_InitSoundSystem(int type, int frequency, int num_vcs); \

This function attempts to initialise the desired sound device.  ChooseDevice
or an equivalent function MUST have been called before attempting to
initialise the sound system, or the attempt will surely fail.  Also if you
are going to supply the optional parameters with PreInitSoundSystem, you MUST
call it before this function.  InstallTimerFunction is the only setup function
which may be called at any time.  This function returns NULL for an error.

 - Type: An integer specifying the playback type and quality.  If the chosen
   device does not support the type, the attempt will fail (although it is
   permissible to attempt lesser types in successive attempts).
         0 => Mono,    8 bit
         1 => Mono,   16 bit
         2 => Stereo,  8 bit
         3 => Stereo, 16 bit
 - Frequency: The desired rate of playback in Hz (ie. 44100).  If the type is
   supported by the chosen device but the frequency is out of range, then the
   JSS will choose the nearest frequency the device does support and return
   success.
 - Number Of Virtual Channels: The maximum number of channels you wish to be
   mixed at one time.  Impulse Tracker defaults to 64, although often you can
   do with less.  It is unwise to allocate less channels than there are
   "tracker" channels in the IT module(s) you plan to play.  If you wish to
   play sound effects too, you should probably add a few more.  32 channels is
   often a good balance.

 _____________________________________________________________
/ int JiNX_SS_InitSoundSystemMax(int frequency, int num_vcs); \

This is a lazy man's function.  It is an alternative to InitSoundSystem which
initialises the chosen device for the best quality output available, at a
frequency up to the value specified by you.

It does this by trying types 3, 2, 1 and 0 successively on InitSoundSystem.
If a type is not supported, failure results and InitSoundSystemMax tries the
next type down.

If a wrong IRQ/DMA has been supplied, failure takes 4 seconds and not the
usual 1 second (since InitSoundSystem gets called 4 times).


3. Information Gathering
~~~~~~~~~~~~~~~~~~~~~~~~
All these functions may be called AFTER InitSoundSystem has returned success.
They are supplied merely as a convenient way of allowing you to give feedback
to the user on the status of the sound system.

 ___________________________________
/ char *JiNX_SS_ReturnDevice(void); \

This function simply returns a string describing the chosen sound device.

 _________________________________
/ char *JiNX_SS_ReturnType(void); \

This function simply returns a string describing the output type being used by
the chosen sound device (ie. 8 or 16bit, Mono or Stereo).

 ____________________________________
/ int JiNX_SS_ReturnFrequency(void); \

This function returns the actual playback frequency used by the sound system.


4. RunTime Functions
~~~~~~~~~~~~~~~~~~~~
All these functions may be called AFTER InitSoundSystem has returned success.

 ______________________________________________________________________
/ JTM_module *JiNX_SS_LoadModuleFile(char *filename, int interpolate); \

Loads and structures an IT module from disk, with filename specified by the
function parameter.  Returns NULL if an error occurred loading the module.  If
"interpolate" is non-zero, every sample will be pre-interpolated up to the
output frequency of the sound device you are using.  This can chew up memory
but approximates the quality of a full linear interpolating mixer.

 _______________________________________________________________________
/ JTM_module *JiNX_SS_LoadModuleMemory(char *it_data, int interpolate); \

Loads and structures an IT module from data stored in memory (specified by the
pointer supplied to the function).  The data in memory should be an exact
mirror of the IT module (ie. what would be stored on disk).  After calling
this function your own copy in memory is no longer required and can be
destroyed.  Returns NULL if an error occurred.  Again "interpolate" has the
same meaning as above.

 _____________________________________
/ JTM_module *JiNX_SS_NoModule(void); \

If you do not wish to load a module, but just use the JSS as a sound FX
engine, you will need to call this function to create a "blank" shell module
to play.  This is because the JSS revolves around playing an IT module, with
sound FX as an add-on.  Playing the module returned by this function takes
negligible CPU power but enables you to trigger all the SFX you would like.
Returns NULL if an error occurred.

 ___________________________________________
/ void JiNX_SS_PlayModule(JTM_module *mod); \

Commences playing of the module supplied as a pointer.  Any modules which
were playing will be stopped in favour of playing this new module.

 ________________________________
/ void JiNX_SS_StopModule(void); \

Disables module (and sound effects!) playback.  This is not a necessary
function to call if you are about to close the sound system.  However, if you
wish to keep the sound system open (playing) while you load/play a different
module, this is required.

ie. Playing a single module only:
        1. InitSoundSystem()
        2. LoadModule(1)
        3. PlayModule(1)
        ------------------------
        4. CloseSoundSystem()
        5. UnloadModule(1)      <optional freeing of memory>

    Playing two modules one after the other:
        1. InitSoundSystem()
        2. LoadModule(1)
        3. PlayModule(1)
        ------------------------
        4. StopModule(1)
        5. UnloadModule(1)      <optional freeing of memory>
        6. LoadModule(2)        <can actually go anywhere before or at here>
        7. PlayModule(2)
        ------------------------
        8. CloseSoundSystem()
        9. UnloadModule(2)      <optional freeing of memory>

Important: Do NOT call UnloadModule while the module is still playing!  Call
either StopModule or CloseSoundSystem beforehand.


5. Module/Sound System Manipulation
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
All these functions may be called AFTER PlayModule has been called with a
valid module as a parameter.

  ______________________________
 / void JiNX_SS_ReverbOn(void); \
/  void JiNX_SS_ReverbOff(void); \

These functions are an external method of enabling/disabling reverb.  The JSS
extended IT effects also utilise these functions, so their use may interfere
with extended effects in any playing IT module.

    ______________________________________________________
   / void JiNX_SS_LPfilterOn(int cutoff_frequency_in_Hz); \
  /  void JiNX_SS_LPfilterOff(void);                       \
 /   void JiNX_SS_HPfilterOn(int cutoff_frequency_in_Hz);   \
/    void JiNX_SS_HPfilterOff(void);                         \

These functions are an external method of enabling/disabling the JSS lowpass
and highpass filters.  As with reverb, these functions can interfere with any
extended FX in a playing IT module.

 - CutOff Frequency (in Hz): This is the corner frequency of the filter you
   are enabling.  Allowed range is 10Hz to 44100Hz.  Values outside this are
   clipped to within this range.

  ________________________________________
 / void JiNX_SS_EnableContinuation(void); \
/  void JiNX_SS_DisableContinuation(void); \

These functions enable and disable module-looping channel continuation.  What
on earth is that you say?  Well normally in the JSS when a module is looped
(ie. the song begins a second time) the sound system is completely reset.  All
playing channels are killed, all effects/tempos/speeds are reset, so that it
sounds exactly as it did the first time the module was played.

However, in some circumstances it may be desirable to loop the module without
a reset.  The main case of this is using the JSS as a sound effect engine,
where you will definately want any looping of the "blank shell module" to be
transparent to the seamless playing of your sound effects.

If continuation is disabled, PlayModule is called when a loop is required.
If continuation is enabled, GotoOrderRow(0,0) is called instead.

 _______________________________________________
/ int JiNX_SS_GotoOrderRow(int order, int row); \

Causes the next order/row to be processed in the current playing IT module to
be those specified by the two parameters.  The first order in the song is
order 0, and the first row in a pattern is row 0.  Returns NULL if the order-
row combination is invalid for the current module.

   ___________________________________________________________________
  / int JiNX_SS_TriggerExternal(void *sample_ptr, int type_8_or_16,   \
 /                              int length_in_bytes, int frequency,    \
/                               int volume_0_to_256, int pan_32_to_32); \

This is the main method of triggering sound effects.  It takes sample data
external to the playing IT module (you are responsible for loading it into
memory) and plays it with the supplied parameters.  If the parameters are
invalid, the function returns NULL.

 - Sample Pointer: Pointer to the beginning of the RAW signed PCM data to be
   played.
 - Type [8 or 16]: Specifies the bit depth of the data to be played.  Note
   that both 8bit and 16bit data must be SIGNED.  Most 8bit data is unsigned.
   To convert 8bit unsigned to 8bit signed, take 128 from each sample.
 - Length (in bytes): The size of the data to be played, measured in bytes
   for convenience.
 - Frequency (in Hz): The playback frequency for the sample in Hz, which is
   usually just the sampling frequency for a sound effect.
 - Volume [0 to 256]: Playback volume, with 256 the loudest.
 - Pan [-32 to 32]: Panning position of the sound effect, with -32 far left,
   +32 far right, and 0 central pan.


  _____________________________________________________________________
 / int JiNX_SS_TriggerInternal(int channel, int note, int instrument); \
/  int JiNX_SS_ConvertNoteString(char *string);                         \

An alternative triggering method, which triggers an instrument within the
currently playing IT module.  This is a full instrument trigger and includes
affecting NNAs, DCTs, DCAs, all envelopes and randomisations and all the other
stuff which goes when you play a note in Impulse Tracker ;).  Returns NULL if
there is an error with the parameters.

 - Channel [0 to 63]: The "tracker" channel you wish the note to originate
   from.  These are the channels visible in Impulse Tracker.  If you do not
   wish the triggering of the instrument to interfere with the currently
   playing song, you will want to choose a high channel number (ie. 63).
 - Note [0 to 119]: An integer representing the note to play, where 0 is C-0,
   60 is C-5, and 119 is B-9.  For ease of use, use ConvertNoteString to
   convert a string such as "C-5" or "F#4" into its note number (function is
   case sensitive however!).
 - Instrument [1 upwards]: The number of the instrument (as displayed in IT)
   to play the supplied note.

eg. JiNX_SS_TriggerInternal(63, JiNX_SS_ConvertNoteString("D-4"), 4);


6. Closing The Sound System
~~~~~~~~~~~~~~~~~~~~~~~~~~~
 _____________________________________
/ int JiNX_SS_CloseSoundSystem(void); \

Stops the transfer of data to the soundcard, deallocates all soundcard DMA
buffers and installs old ISRs.  All the usual clean-up stuff.  Returns NULL if
there was a problem.

 _____________________________________________
/ void JiNX_SS_UnloadModule(JTM_module *mod); \

This function frees the memory allocated to a module previously loaded with
either LoadModule or NoModule.  Do NOT use this command while the JSS is still
open and playing the module!  Call either StopModule or CloseSoundSystem
before.


7. Timing and Synchronisation
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
The ol' trusty method of game/demo/music synchronisation is to poll the
module information such as order/row.  This is sufficient for most purposes.

However, this information is granular, and is only updated twice every reverb
period (for a reverb delay of 100ms, it is updated every 50ms).  Furthermore,
mixing occurs necessarily ahead of playing, so the information you read will
be slightly ahead of itself.

The JiNX Sound System does offer one alternative method of synchronisation,
intended for use with general-purpose external routines.  It is basically a
clock which is tightly synchronised to the sound system.

Due to sound card/calculation inaccuracies, the playing time of a module can
drift by as much as 1.0% from the system clock time.  Hence a module may take
100s to play on one sound card/computer, and 101s on another.

A better method is to use a hybrid internal time/PC clock time result.

 ________________________________________________
/ int JiNX_SS_InstallAccurateTimer(int counter); \

This function is the crux of the JSS's accurate timer.  For simplicity,
compatibility and a whole lot of other reasons, this timer requires PIT mode 2
operation (continuous countdown).  You must call this function AFTER
InitSoundSystem has returned success, especcially if you are going to use a
non-standard reload-counter value.  See section 10 for more information.

Hence this function programmes the PIT for mode 2 (standard divide-by-n) with
an interrupt-reload-counter supplied by you as a parameter.  If you don't know
or don't care what this counter should be, use 0.

If you want to use the PIT yourself, thats fine (so long as you want mode 2
operation!), but let this function programme the PIT after installing your
ISR which is designed for the faster calling rate.  See section 10 for details
on how to achieve this.

 ___________________________________________
/ int JiNX_SS_UninstallAccurateTimer(void); \

Uninstalls the accurate timer.  Call this BEFORE CloseSoundSystem so ISR's get
torn down in the correct order.  See section 10 for more information.

 ___________________________________
/ float JiNX_SS_WhatTimeIsIt(void); \

Once JiNX_SS_InstallAccurateTimer has been called successfully, you may call
this function to get an accurate real-playing time, which typically drifts
less than 0.2% regardless of soundcard and playback frequency.

The result returned is valid at the instant it was called.


Notes:

1. Under Windows, switching focus from the window playing using the JiNX
Sound System will often reprogramme the PIT to its default mode, which is
often mode 3.  This will introduce random errors of plus/minus 25ms to the
above function (which assumes mode 2 or compatible operation).

2. I have spent large amounts of time and effort making the WhatTimeIsIt
function reliable in the sense it shouldn't return wierd numbers.  Internally
there are complex interactions between your code and two interrupts which
serve this function, but by interrupt masking I seem to have eliminated all
jitters (cross fingers!).


8. Accessible Structured Module Data
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
The file ITSTRUCT.H contains all the structures used by the JSS.  Those which
will be of interest are those with a JTM prefix, as these are the runtime
structures defining how data is arranged in memory.

All structures are force-packed (_Packed modifier) so you are free to use any
structure alignment you wish in your code.

It is not recommended you change the contents of these structures, although
an experienced programmer may be able to do so without adverse effects.  There
are a few structures which you will almost certainly wish to read from, and
their contents are detailed here.

Remember reading any data from the JSS this way is quite granular, and appears
ahead of itself.


This section assumes you have executed "mod = JiNX_SS_LoadModule" and
"JiNX_SS_PlayModule(mod)" successfully.


Structure:      Main module structure
Type Name:      JTM_module
Access:         mod->

    int current_global_volume;
    int current_mixing_volume;

    int current_order;
    int current_pattern;
    int current_row;
    int current_tick;

    int current_speed;
    int current_tempo;

    This information is self-explanatory, and has integer values equivalent to
    those used in Impulse Tracker.  Order, pattern, row and tick all have
    values starting at zero.

    JTM_tracker     *tracker_channel;
    JTM_virtual     *virtual_channel;

    char            *order_set;
    JTM_pattern     *pattern_set;
    JTM_instrument  *instrument_set;
    JTM_sample      *sample_set;

    These are pointers to arrays of other various JSS structures you will need
    to follow to access additional data.

    int last_virtual_channel;

    This is the number of channels being processed by the JSS (although many
    of them may be inactive and hence ignored when it comes to mixing).  This
    number serves as an upper limit on the virtual channel numbers you can
    safely access.  Anything >= 0 and < last_virtual_channel is OK.


Structure:      Virtual Channel Structure
Type Name:      JTM_virtual
Access:         mod->virtual_channel[virtual_channel_number].
                mod->tracker_channel[tracker_channel_number].virtual->
                      (but only if this pointer is not NULL! ^^^^^^^)

    int active_status;

    If this number is non-zero, the channel is active and is being mixed.

    int final_volume;           Range => 0 (inaudible) to 256 (max volume)
    int final_pan;              Range => 0 (left) to 64 (right)
    int final_frequency;        Range => Any value (Hz)

    These values are the final calculated values for the sound playing in
    this channel.


9. Alternative Means of Choosing Your Sound Card
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
The following functions allow you to make your own sound card choosing
routine.  To access these functions, #include "jss_dev.h" also in your code.
These routines have NOT been thoroughly tested.

 __________________________________________
/ int JiNX_SS_ReturnNumberOfDevices(void); \

The number of devices supported by the JSS.  Valid device numbers are from 0
to NumberOfDevices - 1.  Using this function will make upgrading to later
versions of the JSS with more drivers much easier.  Returns 7 for V1.0e.

In V1.0e, the devices are:
        0       No Sound
        1       SB 2.0
        2       SB Pro
        3       SB Pro w/ Ext Capabilities
        4       SB 16
        5       WSS or Compat
        6       ESS AudioDrive

 ______________________________________________
/ char *JiNX_SS_DeviceDescription(int device); \

Returns a string description of the device specified, similar to the above
table of devices.

 _________________________________
/ int JiNX_SS_DetectDevice(void); \

This function attempts to autodetect the user's soundcard.  It has the ability
to detect all the devices supported by the JSS at this time.  If it cannot
detect a soundcard, it returns 0 (the No Sound device).

    __________________________________
   / int JiNX_SS_GetDevicePort(void); \
  /  int JiNX_SS_GetDeviceIRQ(void);   \
 /   int JiNX_SS_GetDeviceDMA(void);    \
/    int JiNX_SS_GetDeviceDMAHigh(void); \

These functions are used to access the results of autodetection.  With some
devices, IRQs and DMAs cannot be autodetected.  Either these functions will
return valid numbers or -1.

Device:         Detectable?     Port?   IRQ?    DMA?    Means
SB 2.0          Yes             Yes     Yes     Yes     BLASTER environment
SB Pro          Yes             Yes     Yes     Yes     BLASTER environment
SB Pro w/Ext    No
SB 16           Yes             Yes     Yes     Yes     BLASTER environment
WSS             Yes             Yes     No      No      Port probe
ESS AudioDrive  Yes             Yes     Yes     Yes     BLASTER / Port probe

 ____________________________________________________________________________
/ int JiNX_SS_SetDevice(int device, int port, int irq, int dma, int dma_hi); \

Sets a device to be used, replacing the convenience function ChooseDevice.  If
the settings are wrong you will be notified by a failure when you come to call
InitSoundSystem.  -1 can be used to fill in inconsequential parameters, such
as "dma_hi" for all but the SB16.

An example of a fully automatic device choosing routine would be:


dev = JiNX_SS_DetectDevice();
if (dev  == 0)  goto DONE;                      // No Sound - No Port/IRQ/DMA

port = JiNX_SS_GetDevicePort();
if (port == -1) port = ask_user("Port");        // No Port, ask user

irq = JiNX_SS_GetDeviceIRQ();
if (irq  == -1) irq  = ask_user("IRQ");         // No IRQ, ask user

dma = JiNX_SS_GetDeviceDMA();
if (dma  == -1) irq  = ask_user("DMA");         // No DMA, ask user

dma_hi = JiNX_SS_GetDeviceDMAHigh();
if (dma_hi == -1 && dev == 4) dma_hi = ask_user("High DMA");
                                                // SB16 only: no high DMA
DONE:

error = JiNX_SS_SetDevice(dev, port, irq, dma, dma_hi);


10. Notes on Hooking Interrupts
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
The JiNX Sound System hooks sound card interrupts or possibly the PIT when
you call InitSoundSystem.  When this function is called, the PIT must be
running in its default mode at its default rate.

Here's the order you may do things in:

Without the JSS Accurate Timer
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Before JiNX_SS_InitSoundSystem();
    Hook any normal interrupts.
    Hook the PIT interrupt (8) but do NOT reprogramme the PIT rate (must be
        at 18.2Hz, reload value of 65536).

After JiNX_SS_InitSoundSystem();
    Hook any normal interrupts.
    Hook the PIT interrupt (8) and reprogramme the PIT.  Your ISR should
        ensure chaining still occurs at 18.2Hz.


Before JiNX_SS_CloseSoundSystem();
    Reprogramme the PIT to its normal settings and unhook the PIT interrupt.
    Unhook any normal interrupts.

After JiNX_SS_CloseSoundSystem();
    Unhook the PIT interrupt installed at the default rate.
    Unhook any normal interrupts.


With the JSS Accurate Timer
~~~~~~~~~~~~~~~~~~~~~~~~~~~
Before JiNX_SS_InitSoundSystem();
    Hook any normal interrupts.
    Hook the PIT interrupt (8) but do NOT reprogramme the PIT rate (must be
        at 18.2Hz, reload value of 65536).

After JiNX_SS_InitSoundSystem();
    Hook any normal interrupts.
    Hook the PIT interrupt and THEN reprogramme the PIT using
        JiNX_SS_InstallAccurateTimer(counter).  Again your ISR should ensure
        chaining occurs at 18.2Hz.


Before JiNX_SS_CloseSoundSystem();
    Reprogramme the PIT to its normal settings using
        JiNX_SS_UninstallAccurateTimer() and THEN unhook the PIT interrupt.
    Unhook any normal interrupts.

After JiNX_SS_CloseSoundSystem();
    Unhook the PIT interrupt installed at the default rate.
    Unhook any normal interrupts.


Example:

    My_Hook_COM2(?);                    // Any old interrupt
    My_Hook_BIOS_PIT_Tick_Counter(?);   // PIT interrupt at 18.2Hz

    JiNX_SS_InitSoundSystem(?);         // Initialise sound system

    My_Hook_Fast_PIT(?);                // Hook PIT (no reprogramme, must
                                        // chain at the correct rate)
    JiNX_SS_InstallAccurateTimer(1000); // Use this to reprogramme the PIT

    ....

    JiNX_SS_UninstallAccurateTimer();

    My_UnHook_Fast_PIT(?);

    JiNX_SS_CloseSoundSystem(?);

    My_UnHook_BIOS_PIT_Tick_Counter(?);
    My_UnHook_COM2(?);


Can I do my own thing with the PIT?
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Yes!

If you want to use the PIT yourself AND use the accurate timer, you are
restricted to PIT mode 2, and use the above example as a guide to installing
your own function.

If you don't care for the accurate timer, and want to use the PIT, you are
free to reprogramme it in any way you see fit, providing you do that AFTER
calling InitSoundSystem AND you still chain interrupt 8 at the correct rate of
18.2Hz.

