aboutsummaryrefslogblamecommitdiff
path: root/core/qmpmidiplay.hpp
blob: ddbb63b8d88b15ca7fd675fc33f6f68195f06215 (plain) (tree)
1
2
3
4
5
6
7
8
9



                                     
                  
                             
                 
                
                        
                
                  
                 
                
                            
                  
                                       
 






                                   
 














                                                   
  











                                                                 
  

                 










                                                                    
                   





                                                             

                                  

























                                                                                  
 
















                                                      
 





                                             
                     


                          
 













                                                                
 
                             
 





                                                
 





















                                                                        
 

                                     
 
                                      

      
//sorry for the stupid C-like code...
#ifndef QMPMIDIPLAY_H
#define QMPMIDIPLAY_H
#include <cstring>
#include <cstdlib>
#include <condition_variable>
#include <chrono>
#include <mutex>
#include <unordered_map>
#include <tuple>
#include <utility>
#include <vector>
#define QMP_MAIN
#include "qmpcorepublic.hpp"
class CMidiPlayer;
class CSMFReader : public qmpFileReader
{
private:
    CMidiFile *ret;
    CMidiTrack *curTrack;
    uint32_t fmt, trk;
    FILE *f;
    bool eventdiscarded;
    uint32_t byteread, curt, curid;

    void error(int fatal, const char *format, ...);
    uint8_t read_u8();
    uint16_t read_u16();
    uint32_t read_u32();
    uint32_t read_varlen();
    int read_event();
    void read_track();
    void read_header();
    uint32_t read_chunk(int is_header);
public:
    CSMFReader();
    ~CSMFReader();
    CMidiFile *readFile(const char *fn);
    void discardCurrentEvent();
    void commitEventChange(SEvent d);
};
class CMidiFileReaderCollection
{
private:
    std::vector<std::pair<qmpFileReader *, std::string>> readers;
    qmpFileReader *currentReader;
public:
    CMidiFileReaderCollection();
    ~CMidiFileReaderCollection();
    void registerReader(qmpFileReader *reader, std::string name);
    void unregisterReader(std::string name);
    CMidiFile *readFile(const char *fn);
    qmpFileReader *getCurrentReader();
};
class CMidiPlayer
{
    friend class CMidiFileReaderCollection;
private:
    CMidiFileReaderCollection *midiReaders;
    CMidiFile *midiFile;
    std::vector<std::pair<size_t, size_t>> eorder;
    uint32_t stamps[101], notes, ecnt, maxtk;
    uint32_t ccstamps[101][16][135], ccc[16][135];
    //0..127:cc 128:pc 129:cp 130:pb 131:tempo 132:ts 133:ks 134:pbr
    int32_t rpnid[16], rpnval[16];
    uint16_t mute, solo;
    double ftime;
    bool sendSysEx;
    uint8_t chstatus[16][130];//0..127: cc 128: pc
    uint32_t ctempo, ctsn, ctsd, divs, cks;
    double dpt;//time per tick
    //raw tempo, timesig num., timesig den., division, keysig
    //thread control
    uint32_t tceptr, tcpaused, ct;
    std::mutex intmtx;
    std::condition_variable intcv;
    bool tcstop;
    uint32_t finished, resumed;
    uint32_t pbr[16], pbv[16];
    //playback correction
    uint32_t ttick;
    std::chrono::high_resolution_clock::time_point ttime;
    std::chrono::system_clock::time_point levtt[16];
    struct SMidiDev
    {
        std::string name;
        qmpMidiOutDevice *dev;
        int refcnt;
    };
    std::vector<SMidiDev> mididev;
    int mappedoutput[16];
    ICallBack *eventHandlerCB[16];
    ICallBack *eventReaderCB[16];
    ICallBack *fileReadFinishCB[16];
    void *eventHandlerCBuserdata[16];
    void *eventReaderCBuserdata[16];
    void *fileReadFinishCBuserdata[16];
    std::unordered_map<int, std::tuple<callback_t, void *, bool>> event_handlers;
    std::unordered_map<int, std::pair<callback_t, void *>> event_read_handlers;
    std::unordered_map<int, std::pair<callback_t, void *>> file_read_finish_hooks;
    int event_handlers_id, event_read_handlers_id, file_read_finish_hooks_id;
    static CMidiPlayer *ref;

    SEvent *getEvent(uint32_t id);
    void dumpFile();
    void setBit(uint16_t &n, uint16_t bn, uint16_t b);
    bool processEvent(const SEvent *e);
    void processEventStub(const SEvent *e);
    void prePlayInit();
    void playEvents();
    void fileTimer1Pass();
    void fileTimer2Pass();
public:
    CMidiPlayer();
    ~CMidiPlayer();
    bool playerLoadFile(const char *fn);
    void playerInit();
    void playerDeinit();
    void playerThread();
    void playerPanic(bool reset = false);

    //playing control methods
    uint32_t getStamp(int id);
    uint32_t getTCeptr();
    void setTCeptr(uint32_t ep, uint32_t st);
    uint32_t getTCpaused();
    void setTCpaused(uint32_t ps);
    void interrupt();
    uint32_t isFinished();
    bool stopFlag();
    void setResumed();

    double getFtime();
    void getCurrentTimeSignature(int *n, int *d);
    int getCurrentKeySignature();
    uint32_t getFileNoteCount();
    uint32_t getFileStandard();
    double getTempo();
    uint32_t getTick();
    uint32_t getRawTempo();
    uint32_t getDivision();
    uint32_t getMaxTick();
    double getPitchBend(int ch);
    double getPitchBendRaw(int ch, uint32_t *pb, uint32_t *pbr);
    const char *getTitle();
    const char *getCopyright();

    void sendSysX(bool send);

    void setChannelPreset(int ch, int b, int p);
    void setMute(int ch, bool m);
    void setSolo(int ch, bool s);
    bool getChannelMask(int ch);
    uint16_t getCC(int ch, int id);
    void setCC(int ch, int id, int val);

    void registerMidiOutDevice(qmpMidiOutDevice *dev, std::string name);
    void unregisterMidiOutDevice(std::string name);
    std::vector<std::string> getMidiOutDevices();
    int getChannelOutput(int ch);
    qmpMidiOutDevice *getChannelOutputDevice(int ch);
    void setChannelOutput(int ch, int outid);
    const std::chrono::system_clock::time_point *getLastEventTS();
    int setEventHandlerCB(ICallBack *cb, void *userdata);
    void unsetEventHandlerCB(int id);
    int setEventReaderCB(ICallBack *cb, void *userdata);
    void unsetEventReaderCB(int id);
    int setFileReadFinishedCB(ICallBack *cb, void *userdata);
    void unsetFileReadFinishedCB(int id);
    int registerEventHandler(callback_t cb, void *userdata, bool post);
    void unregisterEventHandler(int id);
    int registerEventReadHandler(callback_t cb, void *userdata);
    void unregisterEventReadHandler(int id);
    int registerFileReadFinishHook(callback_t cb, void *userdata);
    void unregisterFileReadFinishHook(int id);
    void registerReader(qmpFileReader *reader, std::string name);
    void unregisterReader(std::string name);
    void callEventReaderCB(SEvent d);

    void discardCurrentEvent();
    void commitEventChange(SEvent d);

    static CMidiPlayer *getInstance();
};
#endif