diff options
Diffstat (limited to 'src/adlmidi_private.hpp')
-rw-r--r-- | src/adlmidi_private.hpp | 287 |
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) |