/*  player.h  - class MidiPlayer. Plays a set of tracks
    This file is part of LibKMid 0.9.5
    Copyright (C) 1997,98,99,2000  Antonio Larrosa Jimenez
    LibKMid's homepage : http://www.arrakis.es/~rlarrosa/libtdemid.html

    This library is free software; you can redistribute it and/or
    modify it under the terms of the GNU Library General Public
    License as published by the Free Software Foundation; either
    version 2 of the License, or (at your option) any later version.
 
    This library 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
    Library General Public License for more details.
 
    You should have received a copy of the GNU Library General Public License
    along with this library; see the file COPYING.LIB.  If not, write to
    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
    Boston, MA 02110-1301, USA.                                                  

    Send comments and bug fixes to Antonio Larrosa <larrosa@kde.org>

***************************************************************************/
#ifndef _PLAYER_H
#define _PLAYER_H

#include <libtdemid/dattypes.h>
#include <libtdemid/midfile.h>
#include <libtdemid/deviceman.h>
#include <libtdemid/track.h>
#include <libtdemid/notearray.h>
#include <tdemacros.h>

/**
 * This struct stores text, lyrics and change tempo events among others.
 *
 * It includes the main information for an event. That is, the absolute
 * millisecond at which this event is played (from the beginning of the song),
 * the delta milliseconds from the previous SpecialEvent, an ID, etc. 
 *
 * This struct is used as nodes for a linked list, which you can get using
 * MidiPlayer::specialEvents().
 *
 * @short Struct used to store certain events
 * @version 0.9.5 17/01/2000
 * @author Antonio Larrosa Jimenez <larrosa@kde.org>
 */
struct SpecialEvent
{
  /**
   * An integer ID, that is assigned in order to each SpecialEvent.
   */
  int id;

  /**
   * Delta milliseconds from the previous SpecialEvent. 
   *
   * @see absmilliseconds
   */
  ulong diffmilliseconds;

  /**
   * The absolute millisecond (from the beginning of the song) at which this
   * SpecialEvent object is played.
   *
   * @see diffmilliseconds
   */
  ulong absmilliseconds;

  /**
   * MIDI ticks (from the beginning of the song) at which this event is played.
   */
  int ticks;

  /**
   * Type of event. This currently includes:
   *
   * @li 0 - Nothing, end of linked list.
   * @li 1 - Text Event . See text.
   * @li 3 - Change Tempo Event . See tempo.
   * @li 5 - Lyrics Event . See text.
   * @li 6 - Change number of beats per measure . See num and den.
   * @li 7 - Beat . See num and den.
   *
   * The "Change number of beats per measure" and "beat" events are not really
   * in the midi file, but they are added to the linked list in case you have
   * an use for it.
   */
  int type;

  /**
   * Text field . It has a meaning only for Text and Lyrics events.
   */
  char text[1024];

  /**
   * Tempo field . It has a meaning only for Change Tempo events.
   */
  ulong tempo;
  /**
   * Numerator . It has a meaning only for Change number of beats per measure and
   * beat events.
   */
  int num;
  /**
   * Denominator . It has a meaning only for Change number of beats per measure
   * and beat events.
   */
  int den;

  /**
   * This struct stores text, lyrics and change tempo events among others.
   *
   * It includes the main information for an event. That is, the absolute
   * millisecond at which this event is played (from the beginning of the song),
   * the delta milliseconds from the previous SpecialEvent, an ID, etc. 
   *
   * This struct is used as nodes for a linked list, which you can get using
   * MidiPlayer::specialEvents().
   */
  struct SpecialEvent *next;

  /**
   * Next node in the linked list.
   */
  
};


/**
 * PlayerController is a struct that is used by the MidiPlayer object
 * to tell other parts of the application about the status of the MIDI playing.
 *
 * @short Struct used to have control over the player engine
 * @version 0.9.5 17/01/2000
 * @author Antonio Larrosa Jimenez <larrosa@kde.org>
 */
struct PlayerController
{
  volatile ulong  ticksTotal;
  volatile ulong  ticksPlayed;
  volatile double millisecsPlayed;
  volatile ulong  beginmillisec;

  volatile int tempo;
  volatile int num;
  volatile int den;

  volatile int SPEVprocessed;
  volatile int SPEVplayed;

  /**
   * When pause is released, if the caller must know when the player has
   * opened the devices and is playing again, then it just has to check
   * to see when OK changes the value to 1
   */
  volatile int OK; 

  /**
   * When the player is playing (or paused), playing is set to 1.
   */
  volatile int playing;

  /**
   * When the player is paused, paused is set to 1.
   */
  volatile int paused;

  /**
   * When the player seeking the position of the song, moving is set to 1.
   */
  volatile int moving;

  /**
   * When the player has finished playing a song, finished is set to 1.
   */
  volatile int finished;

  /**
   * @internal
   * @deprecated
   * Not used
   */
  volatile int message TDE_DEPRECATED; // set one of the following :

#define PLAYER_DOPAUSE	1
#define PLAYER_DOSTOP	2
#define PLAYER_SETPOS	4
#define PLAYER_HALT	8

  volatile ulong gotomsec; //milliseconds to go to,if player_setpos is set

  /**
   * When error is 1, an error has ocurred (i.e. it coultn't open the device)
   */    
  volatile int error;

  /**
   * If gm is 1, the song follows the General Midi standard, if gm is 0, the song
   * is in MT 32 format.
   */
  volatile int gm;

  /**
   * 100 means no change, 50 halfs the volume, 200 doubles it (if possible), etc.
   *
   * @see DeviceManager::setVolumePercentage()
   */
  volatile int volumepercentage ; 

  /**
   * Activate or disactivate the force to use a patch for a given channel.
   * @see pgm
   */
  volatile bool forcepgm[16];

  /**
   * Force a given patch in each channel at "this" moment, as determined by
   * forcepgm.
   */
  volatile int pgm[16];

  /**
   * Ratio to multiply the tempo to.
   */
  volatile double ratioTempo;

  /**
   * @internal Used to stop the main pid until the child has finished to 
   * send the all notes off event
   */
  volatile bool isSendingAllNotesOff;

  volatile MidiEvent *ev;
};


/**
 * MIDI file player routines . This class reads a MIDI file and
 * play it using a DeviceManager object.
 *
 * To use it, just call loadSong() with the filename you want to play,
 * and then play().
 * 
 * Please have a look at the note in the play() documentation.
 *
 * MidiPlayer will write information about the playing process on a 
 * PlayerController() structure that you must supply to the constructor
 *
 * Alternatively, if everything you want is to play a midi file in a game or
 * any other application that doesn't need to fine tune the midi playing, just
 * use the tdemidplay() function.
 *
 * @see KMidSimpleAPI
 *
 * @short The MIDI file player engine
 * @version 0.9.5 17/01/2000
 * @author Antonio Larrosa Jimenez <larrosa@kde.org>
 */
class KMID_EXPORT MidiPlayer
{
  class MidiPlayerPrivate;
  MidiPlayerPrivate *d;

  DeviceManager *midi;
  MidiFileInfo *info;
  MidiTrack **tracks;
  SpecialEvent *spev;
  NoteArray *na;

  int songLoaded;

  PlayerController *ctl;

  bool parsesong;
  bool generatebeats;

  void removeSpecialEvents(void);
  void parseSpecialEvents(void);
  void insertBeat(SpecialEvent *ev,ulong ms,int num,int den);
  void generateBeats(void);

  //NoteArray *parseNotes(void);
  void debugSpecialEvents(void);
  public:

  /**
   * Constructor . You must construct and pass a DeviceManager object and a 
   * PlayerController structure. None of them will be destroyed by this
   * object, so you should do it after destroying the MidiPlayer object.
   */
  MidiPlayer(DeviceManager *midi_,PlayerController *pctl);

  /**
   * Destructor.
   */
  ~MidiPlayer();

  /**
   * Loads a Song, and parses it (it the parse wasn't disabled with 
   * setParseSong() ) . It also generates the Beat events (see 
   * SpecialEvent::type() ) if you enabled this by using 
   * setGenerateBeats() .
   */
  int loadSong(const char *filename);

  /**
   * Unloads the current song, so that every internal variable is empty and clean
   * for further usage.
   */
  void removeSong(void); 

  /**
   * Returns true if there's a song already loaded (with a previous call to 
   * loadSong() ) and false if not.
   */
  int isSongLoaded(void) { return songLoaded; }

  /**
   * Returns the linked list of SpecialEvents objects . For this to work,
   * the parse should be enabled (the default), by using setParseSong().
   */
  SpecialEvent *specialEvents() { return spev; }

  /**
   * Returns and array with the notes playen through the song . MidiPlayer must
   * parse the song to get the notes, so be sure not to disable the parsing of
   * the song.
   *
   * Returns an array of notes, (just note on and note off events), in the form
   * of a NoteArray object
   */
  NoteArray *noteArray(void) { return na; }


  /**
   * Plays the song using the DeviceManager object supplied in the
   * constructor. It should be already configured, as play doesn't change the
   * volume, nor midi mapper, for example.
   *
   * Note: Calling this function will block the execution of your application
   * until the song finishes playing. The solution for this is simple, fork
   * before calling it, and create the PlayerController object on shared
   * memory.
   *
   * As alternative, if everything you want is playing a midi file in a game or
   * any other application that doesn't need to fine tune the midi playing, just
   * use the KMidSimpleAPI::kMidPlay() function. 
   *
   * @see KMidSimpleAPI::kMidInit
   * @see KMidSimpleAPI::kMidPlay
   * @see KMidSimpleAPI::kMidStop
   * @see KMidSimpleAPI::kMidDestruct
   */
  void play(bool calloutput=false,void output(void) = 0);

  /**
   * Enables or disables the parsing of the song when loading it. This affects
   * the SpecialEvents ( specialEvents() ) and the NoteArray 
   * ( noteArray() ).
   */
  void setParseSong(bool b = true);

  /**
   * Enables or disables the generation of beats event in a song when loading
   * it. 
   */
  void setGenerateBeats(bool b = false);

  /**
   * Returns information about the current MIDI file.
   *
   * @see loadSong
   */
  MidiFileInfo *information(void) { return info; }

  /**
   * Sets the position in a song. 
   * @param gotomsec the number of milliseconds to go to . A subsequent call to
   * play() will start playing the song from that moment, instead of the
   * beginning.
   * @param midistat a MidiStatus object that will contain the status in
   * which the MIDI device would be if it would have arrived to this situation by
   * a normal playing of the song.
   */
  void setPos(ulong gotomsec, class MidiStatus *midistat);

  /**
   * Changes the speed at which a song is played. The song's tempo is multiplied
   * by the specified ratio.
   */
  void setTempoRatio(double ratio);

};

#endif
