
                            Indoor Music System

                 Copyright 1994-1998 Niklas Beisert et al.


Indoor Music System (IMS) is a module playback music system distributed
under the GNU General Public License Version 2 (see file COPYING).

    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.



What you need:
==============

  -Watcom C++ 10.0 and above
  -Turbo Assembler 3.0 and above


portability:
============

  I would not call IMS portable at the moment. I've used special Watcom C
  features, most importantly the #pragma aux directive and interface to
  assembly language. There are also several assumptions I made about the
  C compiler which makes a recompile on a different platform impossible.
  I plan to change all the sources for portability. I know that IMS works
  fine under Windows 95/NT with a little driver. If you want to have IMS
  on other platforms contact me, but only if you know what to do and
  would also code and test it. The libraries were compiled with Watcom C 11
  (and TASM 3.1), if you use any other version, recompile them.


How to use it all:
==================

  AFAIK most (demo) coders are not interested in sound coding, they just need
  a background player for their production. A simple example of how to set
  up a background player should be enough for you: see test.cpp.
  If you use register calling convention you can use IMS.LIB and BINFILE.LIB,
  otherwise you must remake them with stack calling convention.
  I will release the Lasse Reinboeng sources soon as an example of how to
  use IMS in a real demo/intro. (LASSESRC.ZIP)
  You can enable the PAS driver at the top of ims.obj.


binfile:
========

  Did I say I like binfile? :) Ok... I made binfile! =} nothing really
  special... just simple and easy to use and enhance. I hate the
  standard C functions for file-io. And fstream is kinda big and still
  not what I want. binfile makes access to binary data files easy.
  binfile is a base class and it can do nothing, but as soon as you overload
  some basic functions (open, close, read, write, seek) you get many
  higher level functions for free, most importantly easy bit, byte, word and
  dword io. With a little coding you use the oop concepts behind it and make
  classes which read directly from archives, access memory instead of
  files, output directly to the soundcard, or implement a stream via tcp/ip.
  Use binfile without restrictions in your programs if you like.

  sbinfile is the binfile for standard file io. open files with
  open(filename, mode), there are only a few modes like openro, openrw
  and opencr.
          abinfile is a binfile contained in another binfile (archive). You can use
  it if you want to put the module in one big file with all the data.


initialization:
===============

  not much to say. just some code and hints.

    imsinitstruct is;
    imsFillDefaults(is);

  then modify "is" to your needs. most values are default values for
  the setup screen. for example:

    is.usequiet=1;
      // quiet option (command line)
    if (!imsInit(is))
      fail

    // use IMS

    imsClose();


xm player:
==========

  The xm player can play XMs and MODs very accurately. Far more accurately
  than the general module player.

    // fil is an open binfile
    xmodule mod;

    xmpLoadModule(mod, fil); // load xm

    xmpLoadSamples(mod); // upload samples to soundcard

    xmpPlayModule(mod); // play
    xmpStopModule();

    xmpFreeModule(mod);


it player:
==========

  the it player is done as a class: itplayerclass.

    // fil is an open binfile
    itplayerclass itplayer;
    itplayerclass::module mod;

    mod.load(fil); // load it

    itplayerclass.loadsamples(mod); // upload samples to soundcard

    itplayerclass.play(mod, nch); // open nch channels and play
    itplayerclass.stop();

    mod.free(mod);


general module player:
======================

  Only a few loaders included in the package.

    // fil is an open binfile
    gmdmodule mod;

    mpLoadS3M(mod, fil); // load S3M
    mpReduceSamples(mod); // sorry, some extra work

    mpLoadSamples(mod); // upload samples to soundcard.

    mpPlayModule(mod);
    mpStopModule();

    mpFree(mod);


mixer control:
==============

  Hey, there are some guys out there who do not own a device like GUS
  which does all the mixing for you. Actually there are a lot, so
  do not ignore them!
  If you do screen synchronization the mixer background routines might
  interfere. Therefore you can set the size of the playback buffer, you
  can call the mixer explicitly and you can make the background routines
  only update the buffer in an emergency situation.
  Currently the mixer will be called about 70 times a second (there is no
  nice way to change it yet).
  To set the buffersize set bufsize in the imsinitstruct to 65536 times the
  length of the buffer in seconds. Note that the buffer is limited to 64k.
  If you want to update the buffer manually (eg. once per frame is a good
  idea, when you expect high framerates) call mcpIdle (buf only if mcpIdle
  is not zero: if (mcpIdle) mcpIdle();)
  To make the background routines sleep until there was no foreground call
  (mcpIdle) set pollmin in imsinitstruct to 65536 times the critical time
  in seconds. (Set it to a value reasonably smaller than bufsize of course!)


synchronization/timing:
=======================

  At the moment I can only think of one reliable way to synchronize to
  the music. There is a function which gives you a time value which
  is perfectly synchronized with the music. Furthermore there is a
  function which calculates the same time value from a specific module
  position. This function should be called at the beginning of your
  program or you precalclate the time values with another program and
  insert them into the code. The timer starts with zero at the very
  first tick of the module. Note that before the first tick the timer
  will be negative. It will increase by 65536 per second. All your
  timing should be based on the timer function. I've heard that some
  guys use a periodically called function to do timing... All I can say
  is that I do not support this method, it's not reliable. It's not
  reliable either to use the system timer for timing. If you set
  up a soundcard to play something at 44.1kHz, it means that it will
  sound as if it did, although after some minutes you get a lag of a
  second, which you'll definitely notice!

  Now for you lazy guys out there:

  There are also functions to get the current module position or
  sync value. These functions are synchronized exactly.
  (even if you set a large playback buffer for the mixer)
  In XM/MOD sync is done by command Wxx, Sxx or EFx.
  In IT sync is done by command Zxx.

  You can also set events. Events are special positions in a module and the
  player will remember the time when the event was hit. for example you can
  set an event to order 2, row 4, tick 0 with a repeat of 8 rows. another
  function will return an error until order 2, row 4, tick 0 was first
  played. then it will return that position and the time since that position
  was played. after 8 rows it will return order 2, row 12, tick 0 and the
  time since that position. other functions can tell you the length of one
  tick or one row. With events you can synchronize your display with
  a rhythm or special effects. (note: you can set one event per module
  channel, i think that should be enough)

  IMS does not (yet) offer automatic screen synchronization. I never needed
  it and it was a pain to figure out how to. :) So, if you find a reliable
  way to do so, don't hesitate and write me (fine, clean working code).
  Do not clear the interrupt flag for a longer period of time or wavetable
  devices will get into trouble. With mixer control you can do manual screen
  sync, but it does not work with wavetable devices... :(

  functions:
    xmpGetTime()
    itplayerclass::gettime()
    mcpGet(-1, mcpGTimer)
      returns timer value (in 65536ths of a second since module start)

    xmpGetRealPos()
    itplayerclass::getrealpos()
    mpGetRealPos()
      returns (order<<16)|(row<<8)|tick

    xmpGetTickTime()
    xmpGetRowTime()
    itplayerclass::getticktime()
    itplayerclass::getrowtime()
      returns the time for one tick/row

    xmpSetEvPos(ch, pos, type, mod)
    itplayerclass::setevpos(ch, pos, type, mod)
      sets a sync event
        ch: module channel (handle) for event
        pos: start position ((order<<16)|(row<<8)|tick)
        type: 0:once, 1:ticks, 2:rows, 3:orders
        mod: number of (type) between events

    xmpGetEvPos(ch, time)
      gets last event and time since
        ch: module channel (handle) of event
        time: will be set to time since last event
        return: position of last event or -1 if no event

    xmpFindEvPos(pos, time)
      searches all events for a specific position
        pos: position to look for
        time: will be set to time since position
        return: position or -1 if no event found

    itplayerclass::getsync(ch, time)
      returns the sync value for channel ch or global sync for ch==-1
        time: will be set to time since sync was set.

    xmpPrecalcTime(module, startpos, calc, n, ite)
    itplayerclass::module.precalctime(startpos, calc, n, ite)
    gmdPrecalcTime(module, startpos, calc, n, ite)
      module position to timer value calculation:
        startpos: starting position: (order<<8)|row
        calc: array of positions and timer values
        n: number of positions to calculate
        ite: maximum number of ticks to process
        returns: 0: failed otherwise 1.
      calc[x][0] contains the event:
        (order<<16)|(row<<8)|tick: module position
                               -1: module loops
                        -256-sync: syncvalue hit
      calc[x][1] must be initialized with -1 or minus the number of times
        the event must occur before the time is calculated. it will contain
        the time value afterwards.
      ite must be set to a reasonably high value like 10000 or 100000,
        after that many ticks the function will fail if not all events were
        found.


revision history:
=================

  nb980510:
    -license changed
    -became part of OpenCP

  0.6:
    -license changed
    -new sync features (events, exact module position)
    -new it player
    -ptm loader included
    -several smaller fixes

  0.5:
    -first release


known bugs:
===========

  -GUS Software timing is messed up a little (could someone fix it, please?)


future enhancements:
====================

  -The structure of IMS is not final and there have been many changes lately,
    I still have to figure out several things. Expect changes.
  -sound, not just music (ie. foreground playing)
  -portability

