aboutsummaryrefslogtreecommitdiff
path: root/src/adlmidi_private.hpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/adlmidi_private.hpp')
-rw-r--r--src/adlmidi_private.hpp287
1 files changed, 218 insertions, 69 deletions
diff --git a/src/adlmidi_private.hpp b/src/adlmidi_private.hpp
index d3709a2..622e9f2 100644
--- a/src/adlmidi_private.hpp
+++ b/src/adlmidi_private.hpp
@@ -50,6 +50,7 @@
#endif
#include <vector>
+#include <list>
#include <string>
#include <sstream>
#include <map>
@@ -155,7 +156,7 @@ public:
VOLUME_CMF,
VOLUME_DMX,
VOLUME_APOGEE,
- VOLUME_9X,
+ VOLUME_9X
} m_volumeScale;
OPL3();
@@ -180,31 +181,37 @@ public:
void Silence();
void updateFlags();
void ChangeVolumeRangesModel(ADLMIDI_VolumeModels volumeModel);
- void Reset();
+ void Reset(unsigned long PCM_RATE);
};
+/*
+ * TODO: Put usage of those hooks to the places where originally was UI usage.
+ * Also, provide external API to set those hooks
+ */
-class MIDIplay
+/**
+ * @brief Hooks of the internal events
+ */
+struct MIDIEventHooks
{
- // Information about each track
- struct Position
- {
- bool began;
- char padding[7];
- double wait;
- struct TrackInfo
- {
- size_t ptr;
- uint64_t delay;
- int status;
- char padding2[4];
- TrackInfo(): ptr(0), delay(0), status(0) {}
- };
- std::vector<TrackInfo> track;
-
- Position(): began(false), wait(0.0l), track() { }
- } CurrentPosition, LoopBeginPosition, trackBeginPosition;
+ //! Raw MIDI event hook
+ typedef void (*RawEventHook)(void *userdata, uint8_t type, uint8_t subtype, uint8_t channel, uint8_t *data, size_t len);
+ RawEventHook onEvent;
+ void *onEvent_userData;
+
+ //! Note on/off hooks
+ typedef void (*NoteHook)(void *userdata, int adlchn, int note, int ins, int pressure, double bend);
+ NoteHook onNote;
+ void *onNote_userData;
+
+ //! Library internal debug messages
+ typedef void (*DebugMessageHook)(void *userdata, const char *fmt, ...);
+ DebugMessageHook onDebugMessage;
+ void *onDebugMessage_userData;
+};
+class MIDIplay
+{
std::map<std::string, uint64_t> devices;
std::map<uint64_t /*track*/, uint64_t /*channel begin index*/> current_device;
@@ -293,9 +300,10 @@ class MIDIplay
AdlChannel(): users(), koff_time_until_neglible(0) { }
void AddAge(int64_t ms);
};
-public:
+
+ //Padding to fix CLanc code model's warning
char ____padding[7];
-private:
+
std::vector<AdlChannel> ch;
std::vector<std::vector<uint8_t> > TrackData;
@@ -310,29 +318,44 @@ private:
enum Types
{
T_UNKNOWN = 0x00,
- T_NOTEOFF = 0x08,
- T_NOTEON = 0x09,
- T_NOTETOUCH = 0x0A,
- T_CTRLCHANGE = 0x0B,
- T_PATCHCHANGE = 0x0C,
- T_CHANAFTTOUCH = 0x0D,
- T_WHEEL = 0x0E,
-
- T_SYSEX = 0xF7,
- T_SYSEX2 = 0xF0,
- T_SPECIAL = 0xFF,
+ T_NOTEOFF = 0x08,//size == 2
+ T_NOTEON = 0x09,//size == 2
+ T_NOTETOUCH = 0x0A,//size == 2
+ T_CTRLCHANGE = 0x0B,//size == 2
+ T_PATCHCHANGE = 0x0C,//size == 1
+ T_CHANAFTTOUCH = 0x0D,//size == 1
+ T_WHEEL = 0x0E,//size == 2
+
+ T_SYSEX = 0xF0,//size == len
+ T_SYSCOMSPOSPTR = 0xF2,//size == 2
+ T_SYSCOMSNGSEL = 0xF3,//size == 1
+ T_SYSEX2 = 0xF7,//size == len
+ T_SPECIAL = 0xFF
};
enum SubTypes
{
- ST_UNKNOWN = 0x00,
- ST_ENDTRACK = 0x2F,
- ST_TEMPOCHANGE = 0x51,
- ST_META = 0x06,
+ ST_SEQNUMBER = 0x00,//size == 2
+ ST_TEXT = 0x01,//size == len
+ ST_COPYRIGHT = 0x02,//size == len
+ ST_SQTRKTITLE = 0x03,//size == len
+ ST_INSTRTITLE = 0x04,//size == len
+ ST_LYRICS = 0x05,//size == len
+ ST_MARKER = 0x06,//size == len
+ ST_CUEPOINT = 0x07,//size == len
+ ST_DEVICESWITCH = 0x09,//size == len <CUSTOM>
+ ST_MIDICHPREFIX = 0x20,//size == 1
+
+ ST_ENDTRACK = 0x2F,//size == 0
+ ST_TEMPOCHANGE = 0x51,//size == 3
+ ST_SMPTEOFFSET = 0x54,//size == 5
+ ST_TIMESIGNATURE= 0x55,//size == 4
+ ST_KEYSIGNATURE = 0x59,//size == 2
+ ST_SEQUENCERSPEC= 0x7F,//size == len
/* Non-standard, internal ADLMIDI usage only */
- ST_LOOPSTART = 0xE1,
- ST_LOOPEND = 0xE2,
- ST_RAWOPL = 0xE3,
+ ST_LOOPSTART = 0xE1,//size == 0 <CUSTOM>
+ ST_LOOPEND = 0xE2,//size == 0 <CUSTOM>
+ ST_RAWOPL = 0xE3//size == 0 <CUSTOM>
};
//! Main type of event
uint8_t type;
@@ -340,9 +363,12 @@ private:
uint8_t subtype;
//! Targeted MIDI channel
uint8_t channel;
-
+ //! Is valid event
+ uint8_t isValid;
//! Reserved 5 bytes padding
- uint8_t __padding[5];
+ uint8_t __padding[4];
+ //! Absolute tick position (Used for the tempo calculation only)
+ uint64_t absPosition;
//! Raw data of this event
std::vector<uint8_t> data;
};
@@ -353,15 +379,17 @@ private:
* Created with purpose to sort events by type in the same position
* (for example, to keep controllers always first than note on events or lower than note-off events)
*/
- class MidiTrackPos
+ class MidiTrackRow
{
public:
- MidiTrackPos();
+ MidiTrackRow();
void reset();
//! Absolute time position in seconds
double time;
//! Delay to next event in ticks
uint64_t delay;
+ //! Absolute position in ticks
+ uint64_t absPos;
//! Delay to next event in seconds
double timeDelay;
std::vector<MidiEvent> events;
@@ -369,65 +397,142 @@ private:
* @brief Sort events in this position
*/
void sortEvents();
- //! Next event that follows current
- MidiTrackPos *next;
};
+ /**
+ * @brief Tempo change point entry. Used in the MIDI data building function only.
+ */
+ struct TempoChangePoint
+ {
+ uint64_t absPos;
+ fraction<uint64_t> tempo;
+ };
+ //P.S. I declared it here instead of local in-function because C++99 can't process templates with locally-declared structures
+
+ typedef std::list<MidiTrackRow> MidiTrackQueue;
+
// Information about each track
struct PositionNew
{
bool began;
char padding[7];
double wait;
+ double absTimePosition;
struct TrackInfo
{
size_t ptr;
uint64_t delay;
int status;
char padding2[4];
- MidiTrackPos *pos;
- TrackInfo(): ptr(0), delay(0), status(0), pos(NULL) {}
+ MidiTrackQueue::iterator pos;
+ TrackInfo(): ptr(0), delay(0), status(0) {}
};
std::vector<TrackInfo> track;
- PositionNew(): began(false), wait(0.0), track()
+ PositionNew(): began(false), wait(0.0), absTimePosition(0.0), track()
{}
} CurrentPositionNew, LoopBeginPositionNew, trackBeginPositionNew;
- std::vector<std::vector<MidiTrackPos> > trackDataNew;
- std::vector<int> trackDataNewStatus;
- void buildTrackData();
- MidiEvent parseEvent(uint8_t **ptr, int &status);
+ //! Full song length in seconds
+ double fullSongTimeLength;
+ //! Delay after song playd before rejecting the output stream requests
+ double postSongWaitDelay;
+
+ //! Loop start time
+ double loopStartTime;
+ //! Loop end time
+ double loopEndTime;
+ //! Local error string
+ std::string errorString;
+
+ //! Pre-processed track data storage
+ std::vector<MidiTrackQueue > trackDataNew;
+
+ /**
+ * @brief Build MIDI track data from the raw track data storage
+ * @return true if everything successfully processed, or false on any error
+ */
+ bool buildTrackData();
+
+ /**
+ * @brief Parse one event from raw MIDI track stream
+ * @param [_inout] ptr pointer to pointer to current position on the raw data track
+ * @param [_in] end address to end of raw track data, needed to validate position and size
+ * @param [_inout] status status of the track processing
+ * @return Parsed MIDI event entry
+ */
+ MidiEvent parseEvent(uint8_t **ptr, uint8_t *end, int &status);
public:
MIDIplay();
~MIDIplay()
{}
- ADL_MIDIPlayer *config;
std::string musTitle;
fraction<uint64_t> InvDeltaTicks, Tempo;
+ //! Tempo multiplier
+ double tempoMultiplier;
bool trackStart,
atEnd,
loopStart,
loopEnd,
- loopStart_passed /*Tells that "loopStart" already passed*/,
- invalidLoop /*Loop points are invalid (loopStart after loopEnd or loopStart and loopEnd are on same place)*/,
- loopStart_hit /*loopStart entry was hited in previous tick*/;
+ invalidLoop; /*Loop points are invalid (loopStart after loopEnd or loopStart and loopEnd are on same place)*/
char ____padding2[2];
OPL3 opl;
- //! Generated per-tick audio output buffer
+
int16_t outBuf[1024];
-public:
+
+ struct Setup
+ {
+ unsigned int AdlBank;
+ unsigned int NumFourOps;
+ unsigned int NumCards;
+ bool HighTremoloMode;
+ bool HighVibratoMode;
+ bool AdlPercussionMode;
+ bool LogarithmicVolumes;
+ int VolumeModel;
+ unsigned int SkipForward;
+ bool loopingIsEnabled;
+ bool ScaleModulators;
+
+ double delay;
+ double carry;
+
+ /* The lag between visual content and audio content equals */
+ /* the sum of these two buffers. */
+ double mindelay;
+ double maxdelay;
+
+ /* For internal usage */
+ ssize_t stored_samples; /* num of collected samples */
+ short backup_samples[1024]; /* Backup sample storage. */
+ ssize_t backup_samples_size; /* Backup sample storage. */
+ /* For internal usage */
+
+ unsigned long PCM_RATE;
+ } m_setup;
+
static uint64_t ReadBEint(const void *buffer, size_t nbytes);
static uint64_t ReadLEint(const void *buffer, size_t nbytes);
+ /**
+ * @brief Standard MIDI Variable-Length numeric value parser without of validation
+ * @param [_inout] ptr Pointer to memory block that contains begin of variable-length value
+ * @return Unsigned integer that conains parsed variable-length value
+ */
uint64_t ReadVarLen(uint8_t **ptr);
- uint64_t ReadVarLen(size_t tk);
- uint64_t ReadVarLenEx(size_t tk, bool &ok);
+ /**
+ * @brief Secure Standard MIDI Variable-Length numeric value parser with anti-out-of-range protection
+ * @param [_inout] ptr Pointer to memory block that contains begin of variable-length value, will be iterated forward
+ * @param [_in end Pointer to end of memory block where variable-length value is stored (after end of track)
+ * @param [_out] ok Reference to boolean which takes result of variable-length value parsing
+ * @return Unsigned integer that conains parsed variable-length value
+ */
+ uint64_t ReadVarLenEx(uint8_t **ptr, uint8_t *end, bool &ok);
/*
* A little class gives able to read filedata from disk and also from a memory segment
- */
+ */
class fileReader
{
public:
@@ -565,7 +670,10 @@ public:
bool eof()
{
- return mp_tell >= mp_size;
+ if(fp)
+ return std::feof(fp);
+ else
+ return mp_tell >= mp_size;
}
std::string _fileName;
std::FILE *fp;
@@ -582,15 +690,55 @@ public:
bool LoadMIDI(void *data, unsigned long size);
bool LoadMIDI(fileReader &fr);
- /* Periodic tick handler.
- * Input: s = seconds since last call
- * Input: granularity = don't expect intervals smaller than this, in seconds
- * Output: desired number of seconds until next call
+ /**
+ * @brief Periodic tick handler.
+ * @param s seconds since last call
+ * @param granularity don't expect intervals smaller than this, in seconds
+ * @return desired number of seconds until next call
*/
double Tick(double s, double granularity);
+ /**
+ * @brief Change current position to specified time position in seconds
+ * @param seconds Absolute time position in seconds
+ */
+ void seek(double seconds);
+
+ /**
+ * @brief Gives current time position in seconds
+ * @return Current time position in seconds
+ */
+ double tell();
+
+ /**
+ * @brief Gives time length of current song in seconds
+ * @return Time length of current song in seconds
+ */
+ double timeLength();
+
+ /**
+ * @brief Gives loop start time position in seconds
+ * @return Loop start time position in seconds or -1 if song has no loop points
+ */
+ double getLoopStart();
+
+ /**
+ * @brief Gives loop end time position in seconds
+ * @return Loop end time position in seconds or -1 if song has no loop points
+ */
+ double getLoopEnd();
+
+ /**
+ * @brief Return to begin of current song
+ */
void rewind();
+ /**
+ * @brief Set tempo multiplier
+ * @param tempo Tempo multiplier: 1.0 - original tempo. >1 - faster, <1 - slower
+ */
+ void setTempo(double tempo);
+
/* RealTime event triggers */
void realTime_ResetState();
@@ -627,7 +775,7 @@ private:
unsigned props_mask,
int32_t select_adlchn = -1);
bool ProcessEvents();
- bool ProcessEventsNew();
+ bool ProcessEventsNew(bool isSeek = false);
void HandleEvent(size_t tk);
void HandleEvent(size_t tk, MidiEvent &evt, int &status);
@@ -643,6 +791,7 @@ private:
size_t from_channel,
AdlChannel::users_t::iterator j,
MIDIchannel::activenoteiterator i);
+ void Panic();
void KillSustainingNotes(int32_t MidCh = -1, int32_t this_adlchn = -1);
void SetRPN(unsigned MidCh, unsigned value, bool MSB);
//void UpdatePortamento(unsigned MidCh)