
/*
**  jazz - a midi sequencer for Linux
**
**  Copyright (C) 1994-1996 Andreas Voss (andreas@avix.rhein-neckar.de)
**
**  Copyright (C) 1995-1996 Per Sigmond (Per.Sigmond@hia.no)
**
**  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.
*/


#ifndef player_h
#define player_h

#ifndef config_h
#include "config.h"
#endif

#ifndef wx_timerh
#include <sys/types.h>
#include <time.h>
#include <wx_timer.h>
#endif

#ifndef events_h
#include "events.h"
#endif

#ifndef track_h
#include "track.h"
#endif

#ifndef song_h
#include "song.h"
#endif

#ifndef midinet_h
#include "midinet.h"
#endif

class tPlayLoop
{
  long StartClock;
  long StopClock;
public:
  tPlayLoop();
  void Set(long Start, long Stop);
  void Reset();

  // external clock -> internal clock where
  //   external clock == physical clock
  //   internal clock == song position
  long Ext2IntClock(long Clock);

  // the other way round
  long Int2ExtClock(long Clock);

  void PrepareOutput(tEventArray *buf, tSong *s, long ExtFr, long ExtTo);
};

enum tClockSource { CsInt = 0, CsFsk, CsMidi, CsMtc };

class tPlayer : public wxTimer
{
  protected:

    long OutClock;
    tPlayLoop *PlayLoop;

  public:

    int Playing;	// successful StartPlay
    virtual int Installed() = 0;	// Hardware found

    tSong *Song;
    tEventArray PlayBuffer;
    tEventArray RecdBuffer;

    tPlayer(tSong *song);
    virtual ~tPlayer();
    void Notify();
    virtual void FlushToDevice();

    // return 0 = ok, 1 = buffer full, try again later
    virtual int OutEvent(tEvent *e) = 0;
    virtual void OutBreak() = 0;

    // send event immediately ignoring clock
    virtual void OutNow(tEvent *e) = 0;
    virtual void OutNow( tParam *r ) = 0;

    // what's played right now?
    virtual long GetRealTimeClock() = 0;

    virtual void StartPlay(long Clock, long LoopClock = 0, int Continue = 0);
    virtual void StopPlay();
    virtual void AllNotesOff(int Reset = 0);

    virtual void SetSoftThru( int on ) { }
    virtual void SetHardThru( int on ) { }

    virtual void InitMtcRec() { }
    virtual tMtcTime* FreezeMtcRec() { return(0); }
};

extern tPlayer *Midi;
extern char *midinethost;

// --------------------------------------------------------
// Roland MPU 401
// --------------------------------------------------------

#ifdef DEV_MPU401

#include <unistd.h>
#include <fcntl.h>

class tBuffer : public tWriteBase
{

    char Buffer[2000];
    int  Written, Read;

  public:

    long Clock;
    int  RunningStatus;

    void Clear()
    {
      Read = Written = 0;
      Clock = 0;
      RunningStatus = 0;
    }

    tBuffer()
    {
      Clear();
    }

    int Put(char c)
    {
      if (Written < (int)sizeof(Buffer))
      {
	Buffer[Written++] = c;
        return 0;
      }
      return -1;
    }

    int Get(int dev)
    {
      if (Read == Written)
        ReadFile(dev);
      if (Read != Written)
        return (unsigned char)Buffer[Read++];
      return -1;
    }

    int Empty()
    {
      return Read == Written;
    }

    int FreeBytes()
    {
      return (int)sizeof(Buffer) - Written;
    }

    void PutVar(long val)
    {
      unsigned long buf;
      buf = val & 0x7f;
      while ((val >>= 7) > 0)
      {
	buf <<= 8;
	buf |= 0x80;
	buf += (val & 0x7f);
      }

      while (1)
      {
        Put((unsigned char)buf);
	if (buf & 0x80)
	  buf >>= 8;
	else
	  break;
      }
    }

    long GetVar(int dev)
    {
      unsigned long val;
      int c;
      val = Get(dev);
      if (val & 0x80)
      {
	val &= 0x7f;
	do
	{
	  c = Get(dev);
	  assert(c > 0);
	  val = (val << 7) + (c & 0x7f);
	} while (c & 0x80);
      }
      return val;
    }

    int WriteFile(int dev)
    {
      int bytes;
      if (Read != Written)
      {
        bytes = write_noack_mpu(Buffer + Read, Written - Read);
        if (bytes > 0)
	  Read += bytes;
      }
      if (Read != Written)
	return 0;
      Read = Written = 0;
      return 1;
    }

    int ReadFile(int dev)
    {
      int i, bytes;
      if (Read)	// move data to beginning of buffer
      {
        for (i = 0; i < Written - Read; i++)
          Buffer[i] = Buffer[i + Read];
        Written -= Read;
        Read = 0;
      }
      non_block_io( dev, 1 );
      bytes = read(dev, Buffer + Written, sizeof(Buffer) - Written);
      non_block_io( dev, 0 );
      if (bytes > 0)
      {
        Written += bytes;
        return bytes;
      }
      return 0;
    }

    int Write(tEvent *e, uchar *data, int len)
    {
      int i;
      for (i = 0; i < len; i++)
	Put(data[i]);
      return 0;
    }
};


// ---------------------------- jazz-driver -----------------------------

// Define 0xfa to mean start-play-command (filtered by midinetd)
#define START_PLAY_COMMAND 0xfa

// Define 0xfb to mean stop-play-command (filtered by midinetd)
#define STOP_PLAY_COMMAND 0xfb

// 0xfb also sent by midinetd to mark start of recorded data
#define START_OF_RECORD_BUFFER 0xfb

#define ACTIVE_TRACKS 7
#define ACTIVE_TRACKS_MASK 0x7f

class tMpuPlayer : public tPlayer
{
    int  dev;
    tBuffer PlyBytes;
    tBuffer RecBytes;
    long playclock;
    int clock_to_host_counter;

    int ActiveTrack;
    long TrackClock[ACTIVE_TRACKS];
    int TrackRunningStatus[ACTIVE_TRACKS];

    tEventArray OutOfBandEvents;

  public:

    tMpuPlayer(tSong *song);
    virtual ~tMpuPlayer();
    int  OutEvent(tEvent *e);
    void OutNow(tEvent *e);
    void OutNow(tParam *rpn);
    void OutBreak();
    void OutBreak(long BreakOver);
    void StartPlay(long Clock, long LoopClock = 0, int Continue = 0);
    void StopPlay();
    long GetRealTimeClock();
    int  Installed();
    long GetRecordedData();
    void SetHardThru( int on );

    void FlushOutOfBand( long Clock );
};

#define TRK (0<<6)
#define DAT (1<<6)
#define CMD (2<<6)
#define RES (3<<6)

#define MPUDEVICE "/dev/mpu401"
#define MIDINETSERVICE "midinet"

#endif // DEV_MPU401

// ------------------------------ null-driver -------------------------------

class tNullPlayer : public tPlayer
{

  public:

    tNullPlayer(tSong *song) : tPlayer(song) {}
    int Installed() { return 1; }
    virtual ~tNullPlayer() {}
    int  OutEvent(tEvent *e) { return 0; }
    void OutNow(tEvent *e) {}
    void OutNow( tParam *r ) {}
    void OutBreak() {}
    void StartPlay(long Clock, long LoopClock = 0, int Continue = 0) {}
    void StopPlay() {}
    long GetRealTimeClock() { return 0; }
};

// --------------------------- voxware driver -------------------------------

#ifdef DEV_SEQUENCER2

#include <sys/soundcard.h>
#define SEQUENCER_DEVICE "/dev/sequencer2"

SEQ_USE_EXTBUF();

class tSeq2Player : public tPlayer
{
  public:
    friend class tSeq2Through;
    tSeq2Player(tSong *song);
    int Installed();
    virtual ~tSeq2Player();
    int  OutEvent(tEvent *e, int now); 
    int  OutEvent(tEvent *e) { OutEvent(e, 0); return 0; }
    void OutNow(tEvent *e)   { OutEvent(e, 1); }
    void OutNow(tParam *r);
    void OutBreak();
    void OutBreak(long BreakOver);
    void StartPlay(long Clock, long LoopClock = 0, int Continue = 0);
    void StopPlay();
    long GetRealTimeClock();
    virtual void FlushToDevice();
    void SetSoftThru( int on );

  private:
    long    play_clock;
    long    recd_clock;
    long    start_clock;
    long    echo_clock;

    tSeq2Through *through;

    int     card_id;
};


#endif

#endif // player_h

