//=========================================================
//  MusE
//  Linux Music Editor
//  $Id: midithread.h,v 1.1.1.1 2003/10/29 10:05:18 wschweer Exp $
//
//  (C) Copyright 2001 Werner Schweer (ws@seh.de)
//=========================================================

#ifndef __SEQTHREAD_H__
#define __SEQTHREAD_H__

#include <qobject.h>
#include "thread.h"
#include "pos.h"
#include "mpevent.h"

class Part;
class Event;
class MidiPlayEvent;
class MidiEvent;
class Track;
class MidiPort;
class MidiDevice;
class EventList;
class MTC;
struct MidiThreadPrivate;
class SynthI;
class MidiInstrument;

enum State {IDLE, PLAY, START_PLAY, SYNC, PRECOUNT};
static const int REC_NOTE_FIFO_SIZE = 16;

//---------------------------------------------------------
//   MidiMsgId
//    this are the messages send from the GUI thread to
//    the midi thread
//---------------------------------------------------------

enum MidiMsgId {
      SEQM_ADD_TRACK, SEQM_REMOVE_TRACK, SEQM_CHANGE_TRACK, SEQM_MOVE_TRACK,
      SEQM_ADD_PART, SEQM_REMOVE_PART, SEQM_CHANGE_PART,
      SEQM_ADD_EVENT, SEQM_REMOVE_EVENT, SEQM_CHANGE_EVENT,
      SEQM_ADD_TEMPO, SEQM_SET_TEMPO, SEQM_REMOVE_TEMPO, SEQM_ADD_SIG,
      SEQM_SET_GLOBAL_TEMPO,
      SEQM_UNDO, SEQM_REDO,
      SEQM_RESET_DEVICES, SEQM_INIT_DEVICES, SEQM_PANIC,
      SEQM_MIDI_LOCAL_OFF,
      SEQM_MIDI_CTRL, SEQM_MIDI_MASTER_VOLUME,
      SEQM_SET_MIDI_DEVICE,
      SEQM_PLAY_MIDI_EVENT,
      SEQM_SET_MIXDOWN,
      SEQM_SET_RTC_TICKS,
      SEQM_SEEK, SEQM_PLAY,
      SEQM_SCAN_ALSA_MIDI_PORTS,
      SEQM_REMOVE_SYNTHI, SEQM_ADD_SYNTHI,
      MIDI_ADD_SYNTHI, MIDI_SHOW_INSTR_GUI,
      };

//---------------------------------------------------------
//   MidiMsg
//---------------------------------------------------------

struct MidiMsg : public ThreadMsg {
      void* p1;
      void* p2;
      void* p3;

      char port;
      char channel;
      char ctrl;

      int a;
      int b;
      int c;
      };

//---------------------------------------------------------
//   MidiThread
//---------------------------------------------------------

class MidiThread : public QObject, public Thread {
      Q_OBJECT;

      QTimer* heartBeatTimer;       // controls gui refreshes
      State state;
      int timerFd;
      int sigFd;

      // play values:
      int playTickPos;        // current midi tick position in song
      int rtcStartTick;
      int endSlice;

      MPEventList playEvents;
      MPEventList stuckNotes;

      bool controlChanged;

      int recTick;            // ext sync tick position
      int lastTickPos;        // position of last sync tick

      // record values:
      int recNoteFifo[REC_NOTE_FIFO_SIZE];
      volatile int noteFifoSize;
      int noteFifoWindex;
      int noteFifoRindex;

      Pos startRecord;
      Pos endRecord;
      bool loopPassed;

      // run values:
      unsigned _midiTick;

      unsigned rtcTick;             // free running rtcTick
      unsigned rtcTickStart;        // pos 0.0.0

      unsigned samplePos;           // audio sample time base
      unsigned samplePosStart;      // pos 0.0.0

      unsigned midiClock;           // next midi clock pos in midi ticks
      int realRtcTicks;             // current rtc tick rate 1/sec
      int tempoSN;                  // last tempo serial number to track
                                    //    tempo changes
      double mclock1, mclock2;

      unsigned midiClick;
      int clickno;
      int clicksMeasure;
      int ticksBeat;

      virtual void processMsg(const ThreadMsg*);
      virtual void defaultTick();

      void updatePollFd();
      bool setRtcTicks();
      int recTimeStamp() const;
      void recordStop();
      void sendLocalOff();
      bool filterEvent(const MidiPlayEvent* event, int type, bool thru);
      void mtcSyncMsg(const MTC& mtc, bool seekFlag);
      void statePlay();
      static void midiTick(void* p, void*);
      void initDevices();

      void startPlay(int);
      void stopPlay();
      void seek(int pos);
      void playEvent(const MidiPlayEvent* event);
      void processTimerTick();
      void processMidiClock();
      void nextEvents(int starTick, int endTick);
      void panic();

   public slots:
      void seqSignal(int fd);
      void resetDevices();
      void msgInitDevices();
      void localOff();
      void heartBeat();

   signals:
      void midiNote(int, int);

   public:
      MidiThread(int prio, const char* name);
      ~MidiThread();
      void start();
      virtual void threadStop();
      virtual void threadStart(void*);

      bool sendMessage(const MidiMsg* m, bool doUndo);

      bool isPlaying() const;
      void setHeartBeat();

      //-----------------------------------------
      //   message interface
      //-----------------------------------------

      void msgSeek(int val);
      void msgPlay(bool val);

      void msgAddTrack(Track* track, bool u = true);
      void msgRemoveTracks();
      void msgChangeTrack(Track* oldTrack, Track* newTrack, bool u = true);
      void msgMoveTrack(int idx1, int dx2, bool u = true);
      void msgAddPart(Part*, bool u = true);
      void msgRemovePart(Part*, bool u = true);
      void msgChangePart(Part* oldPart, Part* newPart, bool u = true);
      void msgAddEvent(Event*, Part*, bool u = true);
      void msgDeleteEvent(Event*, Part*, bool u = true);
      void msgChangeEvent(Event*, Event*, Part*, bool u = true);
      void msgScanAlsaMidiPorts();
      void msgAddTempo(int tick, int tempo, bool doUndoFlag = true);
      void msgSetTempo(int tick, int tempo, bool doUndoFlag = true);
      void msgSetGlobalTempo(int val);
      void msgDeleteTempo(int tick, int tempo, bool doUndoFlag = true);
      void msgAddSig(int tick, int z, int n, bool doUndoFlag = true);
      void msgAddSynthI(SynthI* synth);
      void msgShowInstrumentGui(MidiInstrument*, bool);
      void msgPanic();

      void undo();
      void redo();
      void ctrlChanged(int port, int channel, int ctrl, int val);
      void masterVolChanged(int val);

      void setMidiDevice(MidiPort*, MidiDevice*);
      void playMidiEvent(MidiPlayEvent* event);
      void rescanAlsaPorts();

      void eventReceived(MidiPlayEvent* event);
      void eventReceived(int port, unsigned char chan, unsigned char c1, unsigned c2);

      void mmcInput(const unsigned char* p, int n);
      void mtcInputQuarter(int, unsigned char c);
      void mtcInputFull(const unsigned char* p, int n);
      void nonRealtimeSystemSysex(const unsigned char*, int);
      void realtimeSystemInput(int, int);
      void setSongPosition(int, int);
      void midiPortsChanged();
      };

extern MidiThread* midiThread;
#endif

