gzdoom-gles/libraries/zmusic/midisources/midisource.h
Christoph Oelckers cfe89ef6e6 - created a new zmusic library which will eventually contain all the music playback code.
Currently all it contains are the MIDI sources and the MIDI devices, the rest needs to be reworked first.
2019-09-28 18:32:25 +02:00

229 lines
5.7 KiB
C++

//
// midisources.h
// GZDoom
//
// Created by Christoph Oelckers on 23.02.18.
//
#ifndef midisources_h
#define midisources_h
#include <stdint.h>
#include <functional>
#include <vector>
#include "zmusic/mus2midi.h"
#include "zmusic/mididefs.h"
extern char MIDI_EventLengths[7];
extern char MIDI_CommonLengths[15];
// base class for the different MIDI sources --------------------------------------
class MIDISource
{
int Volume = 0xffff;
int LoopLimit = 0;
std::function<bool(int)> TempoCallback = [](int t) { return false; };
protected:
bool isLooping = false;
bool skipSysex = false;
int Division = 0;
int Tempo = 500000;
int InitialTempo = 500000;
uint8_t ChannelVolumes[16];
int VolumeControllerChange(int channel, int volume);
void SetTempo(int new_tempo);
int ClampLoopCount(int loopcount);
public:
bool Exporting = false;
// Virtuals for subclasses to override
virtual ~MIDISource() {}
virtual void CheckCaps(int tech);
virtual void DoInitialSetup() = 0;
virtual void DoRestart() = 0;
virtual bool CheckDone() = 0;
virtual std::vector<uint16_t> PrecacheData();
virtual bool SetMIDISubsong(int subsong);
virtual uint32_t *MakeEvents(uint32_t *events, uint32_t *max_event_p, uint32_t max_time) = 0;
void StartPlayback(bool looped = true, int looplimit = 0)
{
Tempo = InitialTempo;
LoopLimit = looplimit;
isLooping = looped;
}
void SkipSysex() { skipSysex = true; }
bool isValid() const { return Division > 0; }
int getDivision() const { return Division; }
int getInitialTempo() const { return InitialTempo; }
int getTempo() const { return Tempo; }
int getChannelVolume(int ch) const { return ChannelVolumes[ch]; }
void setVolume(int vol) { Volume = vol; }
void setLoopLimit(int lim) { LoopLimit = lim; }
void setTempoCallback(std::function<bool(int)> cb)
{
TempoCallback = cb;
}
void CreateSMF(std::vector<uint8_t> &file, int looplimit);
};
// MUS file played with a MIDI stream ---------------------------------------
class MUSSong2 : public MIDISource
{
public:
MUSSong2(const uint8_t *data, size_t len);
protected:
void DoInitialSetup() override;
void DoRestart() override;
bool CheckDone() override;
std::vector<uint16_t> PrecacheData() override;
uint32_t *MakeEvents(uint32_t *events, uint32_t *max_events_p, uint32_t max_time) override;
private:
std::vector<uint8_t> MusData;
uint8_t* MusBuffer;
uint8_t LastVelocity[16];
size_t MusP, MaxMusP;
};
// MIDI file played with a MIDI stream --------------------------------------
class MIDISong2 : public MIDISource
{
public:
MIDISong2(const uint8_t* data, size_t len);
protected:
void CheckCaps(int tech) override;
void DoInitialSetup() override;
void DoRestart() override;
bool CheckDone() override;
uint32_t *MakeEvents(uint32_t *events, uint32_t *max_events_p, uint32_t max_time) override;
private:
void AdvanceTracks(uint32_t time);
struct TrackInfo;
void ProcessInitialMetaEvents ();
uint32_t *SendCommand (uint32_t *event, TrackInfo *track, uint32_t delay, ptrdiff_t room, bool &sysex_noroom);
TrackInfo *FindNextDue ();
std::vector<uint8_t> MusHeader;
std::vector<TrackInfo> Tracks;
TrackInfo *TrackDue;
int NumTracks;
int Format;
uint16_t DesignationMask;
};
// HMI file played with a MIDI stream ---------------------------------------
struct AutoNoteOff
{
uint32_t Delay;
uint8_t Channel, Key;
};
// Sorry, std::priority_queue, but I want to be able to modify the contents of the heap.
class NoteOffQueue : public std::vector<AutoNoteOff>
{
public:
void AddNoteOff(uint32_t delay, uint8_t channel, uint8_t key);
void AdvanceTime(uint32_t time);
bool Pop(AutoNoteOff &item);
protected:
void Heapify();
unsigned int Parent(unsigned int i) const { return (i + 1u) / 2u - 1u; }
unsigned int Left(unsigned int i) const { return (i + 1u) * 2u - 1u; }
unsigned int Right(unsigned int i) const { return (i + 1u) * 2u; }
};
class HMISong : public MIDISource
{
public:
HMISong(const uint8_t* data, size_t len);
protected:
void DoInitialSetup() override;
void DoRestart() override;
bool CheckDone() override;
void CheckCaps(int tech) override;
uint32_t *MakeEvents(uint32_t *events, uint32_t *max_events_p, uint32_t max_time) override;
private:
void SetupForHMI(int len);
void SetupForHMP(int len);
void AdvanceTracks(uint32_t time);
struct TrackInfo;
void ProcessInitialMetaEvents ();
uint32_t *SendCommand (uint32_t *event, TrackInfo *track, uint32_t delay, ptrdiff_t room, bool &sysex_noroom);
TrackInfo *FindNextDue ();
static uint32_t ReadVarLenHMI(TrackInfo *);
static uint32_t ReadVarLenHMP(TrackInfo *);
std::vector<uint8_t> MusHeader;
int NumTracks;
std::vector<TrackInfo> Tracks;
TrackInfo *TrackDue;
TrackInfo *FakeTrack;
uint32_t (*ReadVarLen)(TrackInfo *);
NoteOffQueue NoteOffs;
};
// XMI file played with a MIDI stream ---------------------------------------
class XMISong : public MIDISource
{
public:
XMISong(const uint8_t* data, size_t len);
protected:
bool SetMIDISubsong(int subsong) override;
void DoInitialSetup() override;
void DoRestart() override;
bool CheckDone() override;
uint32_t *MakeEvents(uint32_t *events, uint32_t *max_events_p, uint32_t max_time) override;
private:
struct TrackInfo;
enum EventSource { EVENT_None, EVENT_Real, EVENT_Fake };
int FindXMIDforms(const uint8_t *chunk, int len, TrackInfo *songs) const;
void FoundXMID(const uint8_t *chunk, int len, TrackInfo *song) const;
void AdvanceSong(uint32_t time);
void ProcessInitialMetaEvents();
uint32_t *SendCommand (uint32_t *event, EventSource track, uint32_t delay, ptrdiff_t room, bool &sysex_noroom);
EventSource FindNextDue();
std::vector<uint8_t> MusHeader;
int NumSongs;
std::vector<TrackInfo> Songs;
TrackInfo *CurrSong;
NoteOffQueue NoteOffs;
EventSource EventDue;
};
#endif /* midisources_h */