diff --git a/Procedural-Midi/MidiAsset/MidiAsset.uplugin b/Procedural-Midi/MidiAsset/MidiAsset.uplugin index a803798..c37f5d0 100644 --- a/Procedural-Midi/MidiAsset/MidiAsset.uplugin +++ b/Procedural-Midi/MidiAsset/MidiAsset.uplugin @@ -1,20 +1,19 @@ { "FileVersion" : 3, - "Version" : 13.55, - "VersionName" : "2.52.5", + "Version" : 13.72, + "VersionName" : "2.62.5", "FriendlyName" : "Midi Asset", "Description" : "A plugin to add MIDI capability such as importing, playing, and interfacing with MIDI files. It also includes connecting with external devices and procedural audio", "Category" : "Audio", "CreatedBy" : "Scott Bishel", "CreatedByURL" : "", "DocsURL" : "", - "EngineVersion" : "4.20.0", "MarketplaceURL" : "com.epicgames.launcher://ue/marketplace/content/aff69119f95d405abbab38236e258c2e", "SupportURL" : "mailto:scott.bishel@yahoo.com", "EnabledByDefault" : true, "CanContainContent" : true, "IsBetaVersion" : false, - "Installed": false, + "Installed" : false, "Modules" : [ { diff --git a/Procedural-Midi/MidiAsset/Source/Midi/Classes/Event/ChannelEvent.cpp b/Procedural-Midi/MidiAsset/Source/Midi/Classes/Event/ChannelEvent.cpp index 7d2e36a..37e8bb0 100644 --- a/Procedural-Midi/MidiAsset/Source/Midi/Classes/Event/ChannelEvent.cpp +++ b/Procedural-Midi/MidiAsset/Source/Midi/Classes/Event/ChannelEvent.cpp @@ -68,9 +68,12 @@ int ChannelEvent::getEventSize() { int ChannelEvent::compareTo(MidiEvent *other) { // Compare time - int value = MidiEvent::compareTo(other); - if (value != 0) - return value; + if (mTick != other->getTick()) { + return mTick < other->getTick() ? -1 : 1; + } + if (mDelta->getValue() != other->getDelta()) { + return mDelta->getValue() < other->getDelta() ? 1 : -1; + } // check if other event is [not] a ChannelEvent if (!(other->getType() >= NOTE_OFF && other->getType() <= PITCH_BEND)) { @@ -81,9 +84,8 @@ int ChannelEvent::compareTo(MidiEvent *other) { ChannelEvent * o = static_cast(other); if (mType != o->getType()) { - int order1 = mType - PROGRAM_CHANGE; - int order2 = o->getType() - PROGRAM_CHANGE; - + int order1 = getOrder(mType); + int order2 = getOrder(o->getType()); return order1 < order2 ? -1 : 1; } if (mValue1 != o->mValue1) { @@ -127,8 +129,7 @@ void ChannelEvent::writeToFile(ostream & output, bool writeType){ } ChannelEvent * ChannelEvent::parseChannelEvent(long tick, long delta, int type, int channel, istream & input) { // Get Data1 value - int val1 = 0; - val1 = input.get(); + int val1 = input.get(); // Get Data2 value if its not a PROGRAM_CHANGE or CHANNEL_AFTERTOUCH event int val2 = 0; @@ -156,3 +157,23 @@ ChannelEvent * ChannelEvent::parseChannelEvent(long tick, long delta, int type, return NULL; } + +int ChannelEvent::getOrder(int type) { + switch (type) { + case PROGRAM_CHANGE: + return 0; + case CONTROLLER: + return 1; + case NOTE_ON: + return 2; + case NOTE_OFF: + return 3; + case NOTE_AFTERTOUCH: + return 4; + case CHANNEL_AFTERTOUCH: + return 5; + case PITCH_BEND: + return 6; + } + return -1; +} \ No newline at end of file diff --git a/Procedural-Midi/MidiAsset/Source/Midi/Classes/Event/ChannelEvent.h b/Procedural-Midi/MidiAsset/Source/Midi/Classes/Event/ChannelEvent.h index 028a0bb..f9c036e 100644 --- a/Procedural-Midi/MidiAsset/Source/Midi/Classes/Event/ChannelEvent.h +++ b/Procedural-Midi/MidiAsset/Source/Midi/Classes/Event/ChannelEvent.h @@ -48,4 +48,7 @@ class ChannelEvent : public MidiEvent static const int PROGRAM_CHANGE = 0xC; static const int CHANNEL_AFTERTOUCH = 0xD; static const int PITCH_BEND = 0xE; + +private: + static int getOrder(int type); }; \ No newline at end of file diff --git a/Procedural-Midi/MidiAsset/Source/Midi/Classes/Event/Meta/EndOfTrack.cpp b/Procedural-Midi/MidiAsset/Source/Midi/Classes/Event/Meta/EndOfTrack.cpp index c9b87ac..0cb113f 100644 --- a/Procedural-Midi/MidiAsset/Source/Midi/Classes/Event/Meta/EndOfTrack.cpp +++ b/Procedural-Midi/MidiAsset/Source/Midi/Classes/Event/Meta/EndOfTrack.cpp @@ -15,18 +15,20 @@ int EndOfTrack::getEventSize() { void EndOfTrack::writeToFile(ostream & output) { MetaEvent::writeToFile(output); - int size = getEventSize() - 3; // 0 - output.put((char)size); + output.put((char)0); //size } int EndOfTrack::compareTo(MidiEvent *other) { // Compare time - int value = MidiEvent::compareTo(other); - if (value != 0) - return value; + if (mTick != other->getTick()) { + return mTick < other->getTick() ? -1 : 1; + } + if (mDelta->getValue() != other->getDelta()) { + return mDelta->getValue() < other->getDelta() ? 1 : -1; + } - // Check events are not the same - if (!(other->getType() == this->getType())) { + // Check if same event type + if (!(other->getType() == MetaEvent::END_OF_TRACK)) { return 1; } diff --git a/Procedural-Midi/MidiAsset/Source/Midi/Classes/Event/Meta/GenericMetaEvent.cpp b/Procedural-Midi/MidiAsset/Source/Midi/Classes/Event/Meta/GenericMetaEvent.cpp index 87c985c..4d751c4 100644 --- a/Procedural-Midi/MidiAsset/Source/Midi/Classes/Event/Meta/GenericMetaEvent.cpp +++ b/Procedural-Midi/MidiAsset/Source/Midi/Classes/Event/Meta/GenericMetaEvent.cpp @@ -17,7 +17,7 @@ GenericMetaEvent::GenericMetaEvent(long tick, long delta, MetaEventData& info) GenericMetaEvent::~GenericMetaEvent() { if (mData != NULL) - delete[] mData; + delete []mData; mData = NULL; } @@ -29,14 +29,17 @@ void GenericMetaEvent::writeToFile(ostream & output) { MetaEvent::writeToFile(output); output.write(mLength->getBytes(), mLength->getByteCount()); - output.write(mData, sizeof(&mData)); + output.write(mData, mLength->getValue()); } int GenericMetaEvent::compareTo(MidiEvent *other) { // Compare time - int value = MidiEvent::compareTo(other); - if (value != 0) - return value; + if (mTick != other->getTick()) { + return mTick < other->getTick() ? -1 : 1; + } + if (mDelta->getValue() != other->getDelta()) { + return mDelta->getValue() < other->getDelta() ? 1 : -1; + } return 1; } \ No newline at end of file diff --git a/Procedural-Midi/MidiAsset/Source/Midi/Classes/Event/Meta/KeySignature.cpp b/Procedural-Midi/MidiAsset/Source/Midi/Classes/Event/Meta/KeySignature.cpp index b0d7c0f..a87e3a4 100644 --- a/Procedural-Midi/MidiAsset/Source/Midi/Classes/Event/Meta/KeySignature.cpp +++ b/Procedural-Midi/MidiAsset/Source/Midi/Classes/Event/Meta/KeySignature.cpp @@ -37,8 +37,7 @@ int KeySignature::getEventSize() { void KeySignature::writeToFile(ostream & output) { MetaEvent::writeToFile(output); - int size = getEventSize() - 3; // 2 - output.put((char)size); + output.put((char)2); //size output.put((char)mKey); output.put((char)mScale); } @@ -50,21 +49,23 @@ MetaEvent * KeySignature::parseKeySignature(long tick, long delta, MetaEventData return new GenericMetaEvent(tick, delta, info); } - int key = 0, scale = 0; - key = info.data[0]; - scale = info.data[1]; + int key = info.data[0]; + int scale = info.data[1]; return new KeySignature(tick, delta, key, scale); } int KeySignature::compareTo(MidiEvent *other) { // Compare time - int value = MidiEvent::compareTo(other); - if (value != 0) - return value; + if (mTick != other->getTick()) { + return mTick < other->getTick() ? -1 : 1; + } + if (mDelta->getValue() != other->getDelta()) { + return mDelta->getValue() < other->getDelta() ? 1 : -1; + } - // Check events are not the same - if (!(other->getType() == this->getType())) { + // Check if same event type + if (!(other->getType() == MetaEvent::KEY_SIGNATURE)) { return 1; } diff --git a/Procedural-Midi/MidiAsset/Source/Midi/Classes/Event/Meta/MetaEvent.cpp b/Procedural-Midi/MidiAsset/Source/Midi/Classes/Event/Meta/MetaEvent.cpp index 533ad3f..f580c57 100644 --- a/Procedural-Midi/MidiAsset/Source/Midi/Classes/Event/Meta/MetaEvent.cpp +++ b/Procedural-Midi/MidiAsset/Source/Midi/Classes/Event/Meta/MetaEvent.cpp @@ -42,8 +42,7 @@ void MetaEvent::writeToFile(ostream & output, bool writeType) { void MetaEvent::writeToFile(ostream & output) { MidiEvent::writeToFile(output, true); - int status = 0XFF; - output.put((char)status); + output.put((char)(unsigned char)0XFF); // meta event output.put((char)mType); } @@ -76,7 +75,7 @@ MetaEvent * MetaEvent::parseMetaEvent(long tick, long delta, istream & input) { } if (isText) { - string text = eventData.data; + string text(eventData.data, eventData.length->getValue()); switch (eventData.type) { case TEXT_EVENT: @@ -94,11 +93,7 @@ MetaEvent * MetaEvent::parseMetaEvent(long tick, long delta, istream & input) { case CUE_POINT: return new CuePoint(tick, delta, text); case SEQUENCER_SPECIFIC: - eventData.destroy = false; - delete eventData.length; - eventData.length = NULL; - - return new SequencerSpecificEvent(tick, delta, eventData.data); + return new SequencerSpecificEvent(tick, delta, new string(text)); default: return new GenericMetaEvent(tick, delta, eventData); } @@ -110,9 +105,6 @@ MetaEvent * MetaEvent::parseMetaEvent(long tick, long delta, istream & input) { case MIDI_CHANNEL_PREFIX: return MidiChannelPrefix::parseMidiChannelPrefix(tick, delta, eventData); case END_OF_TRACK: - // ignore next byte -// input.ignore(); // Size = 0; - return new EndOfTrack(tick, delta); case TEMPO: return Tempo::parseTempo(tick, delta, eventData); diff --git a/Procedural-Midi/MidiAsset/Source/Midi/Classes/Event/Meta/MetaEvent.h b/Procedural-Midi/MidiAsset/Source/Midi/Classes/Event/Meta/MetaEvent.h index 543f99a..4ff6dbc 100644 --- a/Procedural-Midi/MidiAsset/Source/Midi/Classes/Event/Meta/MetaEvent.h +++ b/Procedural-Midi/MidiAsset/Source/Midi/Classes/Event/Meta/MetaEvent.h @@ -6,6 +6,7 @@ #include "../MidiEvent.h" #include +#include using namespace std; /** @@ -22,7 +23,7 @@ class MetaEvent : public MidiEvent virtual int getEventSize() = 0; public: - virtual void writeToFile(ostream & output, bool writeType); + /*virtual*/void writeToFile(ostream & output, bool writeType); protected: virtual void writeToFile(ostream & output); @@ -57,7 +58,7 @@ class MetaEvent : public MidiEvent { if (destroy) { delete length; - delete []data; + delete data; length = NULL; data = NULL; diff --git a/Procedural-Midi/MidiAsset/Source/Midi/Classes/Event/Meta/MidiChannelPrefix.cpp b/Procedural-Midi/MidiAsset/Source/Midi/Classes/Event/Meta/MidiChannelPrefix.cpp index 627033d..9b830db 100644 --- a/Procedural-Midi/MidiAsset/Source/Midi/Classes/Event/Meta/MidiChannelPrefix.cpp +++ b/Procedural-Midi/MidiAsset/Source/Midi/Classes/Event/Meta/MidiChannelPrefix.cpp @@ -24,8 +24,7 @@ int MidiChannelPrefix::getEventSize() { void MidiChannelPrefix::writeToFile(ostream & output) { MetaEvent::writeToFile(output); - int size = getEventSize() - 3; // 1 - output.put((char)size); + output.put((char)1); // size output.put((char)mChannel); } @@ -36,20 +35,22 @@ MetaEvent * MidiChannelPrefix::parseMidiChannelPrefix(long tick, long delta, Met return new GenericMetaEvent(tick, delta, info); } - int channel = 0; - channel = info.data[0]; + int channel = info.data[0]; return new MidiChannelPrefix(tick, delta, channel); } int MidiChannelPrefix::compareTo(MidiEvent *other) { // Compare time - int value = MidiEvent::compareTo(other); - if (value != 0) - return value; + if (mTick != other->getTick()) { + return mTick < other->getTick() ? -1 : 1; + } + if (mDelta->getValue() != other->getDelta()) { + return mDelta->getValue() < other->getDelta() ? 1 : -1; + } - // Check events are not the same - if (!(other->getType() == this->getType())) { + // Check if same event type + if (!(other->getType() == MetaEvent::MIDI_CHANNEL_PREFIX)) { return 1; } diff --git a/Procedural-Midi/MidiAsset/Source/Midi/Classes/Event/Meta/SequenceNumber.cpp b/Procedural-Midi/MidiAsset/Source/Midi/Classes/Event/Meta/SequenceNumber.cpp index ffc04b6..98ff3b9 100644 --- a/Procedural-Midi/MidiAsset/Source/Midi/Classes/Event/Meta/SequenceNumber.cpp +++ b/Procedural-Midi/MidiAsset/Source/Midi/Classes/Event/Meta/SequenceNumber.cpp @@ -23,12 +23,9 @@ int SequenceNumber::getSequenceNumber() { void SequenceNumber::writeToFile(ostream& output) { MetaEvent::writeToFile(output); - int size = getEventSize() - 3; // 2 - output.put((char)size); - int high = getMostSignificantBits(); - int low = getLeastSignificantBits(); - output.put((char)high); - output.put((char)low); + output.put((char)2); // size + output.put((char)getMostSignificantBits()); // high byte + output.put((char)getLeastSignificantBits()); // low byte } MetaEvent* SequenceNumber::parseSequenceNumber(long tick, long delta, MetaEventData& info) { @@ -38,9 +35,8 @@ MetaEvent* SequenceNumber::parseSequenceNumber(long tick, long delta, MetaEventD return new GenericMetaEvent(tick, delta, info); } - int msb = 0, lsb = 0; - msb = info.data[0]; - lsb = info.data[1]; + int msb = info.data[0]; + int lsb = info.data[1]; int number = (msb << 8) + lsb; return new SequenceNumber(tick, delta, number); @@ -48,12 +44,15 @@ MetaEvent* SequenceNumber::parseSequenceNumber(long tick, long delta, MetaEventD int SequenceNumber::compareTo(MidiEvent *other) { // Compare time - int value = MidiEvent::compareTo(other); - if (value != 0) - return value; + if (mTick != other->getTick()) { + return mTick < other->getTick() ? -1 : 1; + } + if (mDelta->getValue() != other->getDelta()) { + return mDelta->getValue() < other->getDelta() ? 1 : -1; + } - // Check events are not the same - if (!(other->getType() == this->getType())) { + // Check if same event type + if (!(other->getType() == MetaEvent::SEQUENCE_NUMBER)) { return 1; } diff --git a/Procedural-Midi/MidiAsset/Source/Midi/Classes/Event/Meta/SequencerSpecificEvent.cpp b/Procedural-Midi/MidiAsset/Source/Midi/Classes/Event/Meta/SequencerSpecificEvent.cpp index 4c5b438..64e4308 100644 --- a/Procedural-Midi/MidiAsset/Source/Midi/Classes/Event/Meta/SequencerSpecificEvent.cpp +++ b/Procedural-Midi/MidiAsset/Source/Midi/Classes/Event/Meta/SequencerSpecificEvent.cpp @@ -5,55 +5,58 @@ #include "../../Util/MidiUtil.h" -SequencerSpecificEvent::SequencerSpecificEvent(long tick, long delta, char data[]) - : MetaEvent(tick, delta, MetaEvent::SEQUENCER_SPECIFIC, new VariableLengthInt(sizeof(&data))), mData(NULL) +SequencerSpecificEvent::SequencerSpecificEvent(long tick, long delta, string* data) + : MetaEvent(tick, delta, MetaEvent::SEQUENCER_SPECIFIC, new VariableLengthInt(data->size())), mData(NULL) { mData = data; } SequencerSpecificEvent::~SequencerSpecificEvent() { if (mData != NULL) - delete[] mData; + delete mData; mData = NULL; } -void SequencerSpecificEvent::setData(char data[]) { +void SequencerSpecificEvent::setData(string* data) { if (mData != NULL) - delete[] mData; + delete mData; mData = NULL; mData = data; - mLength->setValue(sizeof(&mData)); + mLength->setValue(mData->size()); } -char * SequencerSpecificEvent::getData() { +string * SequencerSpecificEvent::getData() { return mData; } int SequencerSpecificEvent::getEventSize() { - return 2 + mLength->getByteCount() + sizeof(&mData); + return 2 + mLength->getByteCount() + mLength->getValue(); } void SequencerSpecificEvent::writeToFile(ostream & output) { MetaEvent::writeToFile(output); output.write(mLength->getBytes(), mLength->getByteCount()); - output.write(mData, sizeof(&mData)); + output.write(mData->data(), mLength->getValue()); } int SequencerSpecificEvent::compareTo(MidiEvent *other) { // Compare time - int value = MidiEvent::compareTo(other); - if (value != 0) - return value; + if (mTick != other->getTick()) { + return mTick < other->getTick() ? -1 : 1; + } + if (mDelta->getValue() != other->getDelta()) { + return mDelta->getValue() < other->getDelta() ? 1 : -1; + } - // Check events are not the same - if (!(other->getType() == this->getType())) { + // Check if same event type + if (!(other->getType() == MetaEvent::SEQUENCER_SPECIFIC)) { return 1; } SequencerSpecificEvent * o = static_cast(other); - if (MidiUtil::bytesEqual(mData, o->mData, 0, sizeof(&mData))) { + if (MidiUtil::bytesEqual(*mData, *o->mData, 0, mLength->getValue())) { return 0; } return 1; diff --git a/Procedural-Midi/MidiAsset/Source/Midi/Classes/Event/Meta/SequencerSpecificEvent.h b/Procedural-Midi/MidiAsset/Source/Midi/Classes/Event/Meta/SequencerSpecificEvent.h index 81ceb2e..f59a3da 100644 --- a/Procedural-Midi/MidiAsset/Source/Midi/Classes/Event/Meta/SequencerSpecificEvent.h +++ b/Procedural-Midi/MidiAsset/Source/Midi/Classes/Event/Meta/SequencerSpecificEvent.h @@ -11,14 +11,14 @@ */ class SequencerSpecificEvent : public MetaEvent { - char * mData; + string * mData; public: - SequencerSpecificEvent(long tick, long delta, char data[]); + SequencerSpecificEvent(long tick, long delta, string* data); ~SequencerSpecificEvent(); - void setData(char data[]); - char * getData(); + void setData(string* data); + string* getData(); protected: int getEventSize(); diff --git a/Procedural-Midi/MidiAsset/Source/Midi/Classes/Event/Meta/SmpteOffset.cpp b/Procedural-Midi/MidiAsset/Source/Midi/Classes/Event/Meta/SmpteOffset.cpp index 859e4fc..8010cdf 100644 --- a/Procedural-Midi/MidiAsset/Source/Midi/Classes/Event/Meta/SmpteOffset.cpp +++ b/Procedural-Midi/MidiAsset/Source/Midi/Classes/Event/Meta/SmpteOffset.cpp @@ -64,8 +64,7 @@ int SmpteOffset::getEventSize() { void SmpteOffset::writeToFile(ostream & output) { MetaEvent::writeToFile(output); - int size = getEventSize() - 3; // 5 - output.put((char)size); + output.put((char)5); // size output.put((char)mHours); output.put((char)mMinutes); output.put((char)mSeconds); @@ -80,30 +79,30 @@ MetaEvent * SmpteOffset::parseSmpteOffset(long tick, long delta, MetaEventData& return new GenericMetaEvent(tick, delta, info); } - int rrHours = 0; - rrHours = info.data[0]; - + int rrHours = info.data[0]; int rr = rrHours >> 5; int fps = int(rr); int hour = rrHours & 0x1F; - int min = 0, sec = 0, frm = 0, sub = 0; - min = info.data[1]; - sec = info.data[2]; - frm = info.data[3]; - sub = info.data[4]; + int min = info.data[1]; + int sec = info.data[2]; + int frm = info.data[3]; + int sub = info.data[4]; return new SmpteOffset(tick, delta, fps, hour, min, sec, frm, sub); } int SmpteOffset::compareTo(MidiEvent *other) { // Compare time - int value = MidiEvent::compareTo(other); - if (value != 0) - return value; + if (mTick != other->getTick()) { + return mTick < other->getTick() ? -1 : 1; + } + if (mDelta->getValue() != other->getDelta()) { + return mDelta->getValue() < other->getDelta() ? 1 : -1; + } - // Check events are not the same - if (!(other->getType() == this->getType())) { + // Check if same event type + if (!(other->getType() == MetaEvent::SMPTE_OFFSET)) { return 1; } diff --git a/Procedural-Midi/MidiAsset/Source/Midi/Classes/Event/Meta/Tempo.cpp b/Procedural-Midi/MidiAsset/Source/Midi/Classes/Event/Meta/Tempo.cpp index 75f2b22..273b7b3 100644 --- a/Procedural-Midi/MidiAsset/Source/Midi/Classes/Event/Meta/Tempo.cpp +++ b/Procedural-Midi/MidiAsset/Source/Midi/Classes/Event/Meta/Tempo.cpp @@ -44,8 +44,7 @@ int Tempo::getEventSize() { void Tempo::writeToFile(ostream & output) { MetaEvent::writeToFile(output); - int size = getEventSize() - 3; // 3 - output.put((char)size); + output.put((char)3); // size output.write(MidiUtil::intToBytes(mMPQN, 3), 3); } @@ -63,12 +62,15 @@ MetaEvent * Tempo::parseTempo(long tick, long delta, MetaEventData& info) { int Tempo::compareTo(MidiEvent *other) { // Compare time - int value = MidiEvent::compareTo(other); - if (value != 0) - return value; + if (mTick != other->getTick()) { + return mTick < other->getTick() ? -1 : 1; + } + if (mDelta->getValue() != other->getDelta()) { + return mDelta->getValue() < other->getDelta() ? 1 : -1; + } - // Check events are not the same - if (!(other->getType() == this->getType())) { + // Check if same event type + if (!(other->getType() == MetaEvent::TEMPO)) { return 1; } diff --git a/Procedural-Midi/MidiAsset/Source/Midi/Classes/Event/Meta/TextualMetaEvent.cpp b/Procedural-Midi/MidiAsset/Source/Midi/Classes/Event/Meta/TextualMetaEvent.cpp index c3a346a..b8ec6ab 100644 --- a/Procedural-Midi/MidiAsset/Source/Midi/Classes/Event/Meta/TextualMetaEvent.cpp +++ b/Procedural-Midi/MidiAsset/Source/Midi/Classes/Event/Meta/TextualMetaEvent.cpp @@ -9,8 +9,8 @@ TextualMetaEvent::TextualMetaEvent(long tick, long delta, int type, string text) mText = text; } -TextualMetaEvent::~TextualMetaEvent() { - +TextualMetaEvent::~TextualMetaEvent() +{ } void TextualMetaEvent::setText(string t) { @@ -22,24 +22,27 @@ string TextualMetaEvent::getText() { } int TextualMetaEvent::getEventSize() { - return 2 + mLength->getByteCount() + (int)mText.length(); + return 2 + mLength->getByteCount() + mLength->getValue(); } void TextualMetaEvent::writeToFile(ostream & output) { MetaEvent::writeToFile(output); output.write(mLength->getBytes(), mLength->getByteCount()); - output.write((char*)mText.c_str(), mText.length()); + output.write(mText.data(), mLength->getValue()); } int TextualMetaEvent::compareTo(MidiEvent *other) { // Compare time - int value = MidiEvent::compareTo(other); - if (value != 0) - return value; + if (mTick != other->getTick()) { + return mTick < other->getTick() ? -1 : 1; + } + if (mDelta->getValue() != other->getDelta()) { + return mDelta->getValue() < other->getDelta() ? 1 : -1; + } - // Check events are not the same - if (!(other->getType() == this->getType())) { + // Check if event is a text type event + if (!(other->getType() >= TEXT_EVENT && other->getType() <= CUE_POINT)) { return 1; } diff --git a/Procedural-Midi/MidiAsset/Source/Midi/Classes/Event/Meta/TimeSignature.cpp b/Procedural-Midi/MidiAsset/Source/Midi/Classes/Event/Meta/TimeSignature.cpp index f200b93..1a6b397 100644 --- a/Procedural-Midi/MidiAsset/Source/Midi/Classes/Event/Meta/TimeSignature.cpp +++ b/Procedural-Midi/MidiAsset/Source/Midi/Classes/Event/Meta/TimeSignature.cpp @@ -49,8 +49,7 @@ int TimeSignature::getEventSize() { void TimeSignature::writeToFile(ostream & output) { MetaEvent::writeToFile(output); - int size = getEventSize() - 3; // 4 - output.put((char)size); + output.put((char)4); // size output.put((char)mNumerator); output.put((char)mDenominator); output.put((char)mMeter); @@ -64,11 +63,10 @@ MetaEvent * TimeSignature::parseTimeSignature(long tick, long delta, MetaEventDa return new GenericMetaEvent(tick, delta, info); } - int num = 0, den = 0, met = 0, fps = 0; - num = info.data[0]; - den = info.data[1]; - met = info.data[2]; - fps = info.data[3]; + int num = info.data[0]; + int den = info.data[1]; + int met = info.data[2]; + int fps = info.data[3]; den = (int)pow(2, den); @@ -93,12 +91,15 @@ int TimeSignature::log2(int den) { int TimeSignature::compareTo(MidiEvent *other) { // Compare time - int value = MidiEvent::compareTo(other); - if (value != 0) - return value; + if (mTick != other->getTick()) { + return mTick < other->getTick() ? -1 : 1; + } + if (mDelta->getValue() != other->getDelta()) { + return mDelta->getValue() < other->getDelta() ? 1 : -1; + } - // Check events are not the same - if (!(other->getType() == this->getType())) { + // Check if same event type + if (!(other->getType() == MetaEvent::TIME_SIGNATURE)) { return 1; } diff --git a/Procedural-Midi/MidiAsset/Source/Midi/Classes/Event/MidiEvent.cpp b/Procedural-Midi/MidiAsset/Source/Midi/Classes/Event/MidiEvent.cpp index 351001e..7288a7a 100644 --- a/Procedural-Midi/MidiAsset/Source/Midi/Classes/Event/MidiEvent.cpp +++ b/Procedural-Midi/MidiAsset/Source/Midi/Classes/Event/MidiEvent.cpp @@ -15,6 +15,7 @@ #include "Meta/MetaEvent.h" #include +#include MidiEvent::MidiEvent(long tick, long delta) : mType(-1), mDelta(NULL) { mTick = tick; @@ -71,10 +72,17 @@ bool MidiEvent::requiresStatusByte(MidiEvent * prevEvent) { return true; } - // make sure both events are not the same + // check if the events are the same if (this->getType() == prevEvent->getType()) { return false; } + + // A way to make sure if its a system exclusive event + if ( (this->getType() == 0xF0 || this->getType() == 0xF7) && + (prevEvent->getType() == 0xF0 || prevEvent->getType() == 0xF7) ) { + return false; + } + return true; } @@ -87,12 +95,11 @@ int MidiEvent::sId = -1; int MidiEvent::sType = -1; int MidiEvent::sChannel = -1; -MidiEvent * MidiEvent::parseEvent(long tick, long delta, istream & input){ +MidiEvent * MidiEvent::parseEvent(long tick, long delta, istream & input) { bool reset = false; // ID event - int id = 0; - id = input.get(); + int id = input.get(); if (!verifyIdentifier(id)) { // move back one bytes input.unget(); @@ -111,10 +118,11 @@ MidiEvent * MidiEvent::parseEvent(long tick, long delta, istream & input){ } // System Exclusive Event else if (sId == 0xF0 || sId == 0xF7) { - VariableLengthInt size(input); - char * data = new char[size.getValue()]; - input.read(data, size.getValue()); + + string* data = new string(size.getValue(), ' '); + input.read(&(*data)[0], size.getValue()); + return new SystemExclusiveEvent(sId, tick, delta, data); } // Unknown Event @@ -154,20 +162,8 @@ bool MidiEvent::verifyIdentifier(int id) { return true; } -int MidiEvent::compareTo(MidiEvent *other) -{ - if (mTick != other->getTick()) { - return mTick < other->getTick() ? -1 : 1; - } - if (mDelta->getValue() != other->getDelta()) { - return mDelta->getValue() < other->getDelta() ? 1 : -1; - } - - return 0; -} - // Just a way to return the name of the event -string getMidiClassName(int type) { +string MidiEvent::getMidiClassName(int type) { // ChannelEvent switch (type) { diff --git a/Procedural-Midi/MidiAsset/Source/Midi/Classes/Event/MidiEvent.h b/Procedural-Midi/MidiAsset/Source/Midi/Classes/Event/MidiEvent.h index d182c45..3dcccc7 100644 --- a/Procedural-Midi/MidiAsset/Source/Midi/Classes/Event/MidiEvent.h +++ b/Procedural-Midi/MidiAsset/Source/Midi/Classes/Event/MidiEvent.h @@ -5,7 +5,6 @@ #include #include "../Util/VariableLengthInt.h" -#include /** * abstract base class for midi events @@ -50,15 +49,17 @@ class MidiEvent static MidiEvent * parseEvent(long tick, long delta, istream & input); /* Compare MIDI events - * 0: Current event time is the same as Other - * -1: Current event time is less then Other - * 1: Current event time is greater then other + * 0: Current event is the same as Other + * -1: Current event is less then Other + * 1: Current event is greater then other */ - virtual int compareTo(MidiEvent *other); + virtual int compareTo(MidiEvent *other) = 0; private: static bool verifyIdentifier(int id); + string getMidiClassName(int type); + public: virtual string toString(); }; \ No newline at end of file diff --git a/Procedural-Midi/MidiAsset/Source/Midi/Classes/Event/ProgramChange.h b/Procedural-Midi/MidiAsset/Source/Midi/Classes/Event/ProgramChange.h index 1bb9449..2e4a8e9 100644 --- a/Procedural-Midi/MidiAsset/Source/Midi/Classes/Event/ProgramChange.h +++ b/Procedural-Midi/MidiAsset/Source/Midi/Classes/Event/ProgramChange.h @@ -20,6 +20,7 @@ class ProgramChange : public ChannelEvent void setProgramNumber(int p); enum MidiProgram { + /* Piano */ ACOUSTIC_GRAND_PIANO, BRIGHT_ACOUSTIC_PIANO, ELECTRIC_GRAND_PIANO, @@ -28,6 +29,7 @@ class ProgramChange : public ChannelEvent ELECTRIC_PIANO_2, HARPSICHORD, CLAVINET, + /* Chromatic Percussion */ CELESTA, GLOCKENSPIEL, MUSIC_BOX, @@ -36,6 +38,7 @@ class ProgramChange : public ChannelEvent XYLOPHONE, TUBULAR_BELLS, DULCIMER, + /* Organ */ DRAWBAR_ORGAN, PERCUSSIVE_ORGAN, ROCK_ORGAN, @@ -92,6 +95,7 @@ class ProgramChange : public ChannelEvent ENGLISH_HORN, BASSOON, CLARINET, + /* Pipe */ PICCOLO, FLUTE, RECORDER, @@ -100,6 +104,7 @@ class ProgramChange : public ChannelEvent SHAKUHACHI, WHISTLE, OCARINA, + /* Synth Lead */ LEAD_1_SQUARE, LEAD_2_SAWTOOTH, LEAD_3_CALLIOPE, @@ -108,6 +113,7 @@ class ProgramChange : public ChannelEvent LEAD_6_VOICE, LEAD_7_FIFTHS, LEAD_8_BASS_AND_LEAD, + /* Synth Pad */ PAD_1_NEW_AGE, PAD_2_WARM, PAD_3_POLYSYNTH, @@ -116,6 +122,7 @@ class ProgramChange : public ChannelEvent PAD_6_METALLIC, PAD_7_HALO, PAD_8_SWEEP, + /* Synth Effects */ FX_1_RAIN, FX_2_SOUNDTRACK, FX_3_CRYSTAL, @@ -124,6 +131,7 @@ class ProgramChange : public ChannelEvent FX_6_GOBLINS, FX_7_ECHOES, FX_8_SCIFI, + /* Ethnic */ SITAR, BANJO, SHAMISEN, @@ -132,6 +140,7 @@ class ProgramChange : public ChannelEvent BAGPIPE, FIDDLE, SHANAI, + /* Percussive */ TINKLE_BELL, AGOGO, STEEL_DRUMS, @@ -140,6 +149,7 @@ class ProgramChange : public ChannelEvent MELODIC_TOM, SYNTH_DRUM, REVERSE_CYMBAL, + /* Sound Effects */ GUITAR_FRET_NOISE, BREATH_NOISE, SEASHORE, diff --git a/Procedural-Midi/MidiAsset/Source/Midi/Classes/Event/SystemExclusiveEvent.cpp b/Procedural-Midi/MidiAsset/Source/Midi/Classes/Event/SystemExclusiveEvent.cpp index 6794fc6..b99a829 100644 --- a/Procedural-Midi/MidiAsset/Source/Midi/Classes/Event/SystemExclusiveEvent.cpp +++ b/Procedural-Midi/MidiAsset/Source/Midi/Classes/Event/SystemExclusiveEvent.cpp @@ -3,7 +3,7 @@ #include "SystemExclusiveEvent.h" -SystemExclusiveEvent::SystemExclusiveEvent(int type, long tick, char data[]) +SystemExclusiveEvent::SystemExclusiveEvent(int type, long tick, string* data) : MidiEvent(tick, 0), mLength(NULL), mData(NULL) { mType = type & 0xFF; @@ -11,11 +11,11 @@ SystemExclusiveEvent::SystemExclusiveEvent(int type, long tick, char data[]) mType = 0xF0; } - mLength = new VariableLengthInt(sizeof(&data)); + mLength = new VariableLengthInt(data->size()); mData = data; } -SystemExclusiveEvent::SystemExclusiveEvent(int type, long tick, long delta, char data[]) +SystemExclusiveEvent::SystemExclusiveEvent(int type, long tick, long delta, string* data) : MidiEvent(tick, delta), mLength(NULL), mData(NULL) { @@ -24,7 +24,7 @@ SystemExclusiveEvent::SystemExclusiveEvent(int type, long tick, long delta, char mType = 0xF0; } - mLength = new VariableLengthInt(sizeof(&data)); + mLength = new VariableLengthInt(data->size()); mData = data; } @@ -33,22 +33,20 @@ SystemExclusiveEvent::~SystemExclusiveEvent() if (mLength != NULL) delete mLength; if (mData != NULL) - delete[] mData; + delete mData; mLength = NULL; mData = NULL; } -char* SystemExclusiveEvent::getData() { +string* SystemExclusiveEvent::getData() { return mData; } -void SystemExclusiveEvent::setData(char data[]) { +void SystemExclusiveEvent::setData(string* data) { if (mData != NULL) - { - delete[] mData; - mData = NULL; - } + delete mData; + mData = NULL; - mLength->setValue(sizeof(&data)); + mLength->setValue(data->size()); mData = data; } @@ -59,24 +57,26 @@ bool SystemExclusiveEvent::requiresStatusByte(MidiEvent* prevEvent) { void SystemExclusiveEvent::writeToFile(ostream & output, bool writeType) { MidiEvent::writeToFile(output, writeType); - // TODO - if (writeType) { - output.put((char)mType); - } + output.put((char)mType); output.write(mLength->getBytes(), mLength->getByteCount()); - output.write(mData, sizeof(&mData)); + output.write(mData->data(), mLength->getValue()); } int SystemExclusiveEvent::compareTo(MidiEvent *other) { // Compare time - int value = MidiEvent::compareTo(other); - if (value != 0) - return value; - - // Check Events are the same - if (other->getType() == this->getType()) { - string curr = mData; - string comp = (static_cast(other)->mData); + if (mTick != other->getTick()) { + return mTick < other->getTick() ? -1 : 1; + } + if (mDelta->getValue() != other->getDelta()) { + return mDelta->getValue() < other->getDelta() ? 1 : -1; + } + + // Check if event is a system exlusive type event + if (other->getType() == 0xF0 || other->getType() == 0xF7) { + SystemExclusiveEvent * o = static_cast(other); + + string curr = *mData; + string comp = *o->mData; return curr.compare(comp); } @@ -84,5 +84,5 @@ int SystemExclusiveEvent::compareTo(MidiEvent *other) { } int SystemExclusiveEvent::getEventSize() { - return 1 + mLength->getByteCount() + sizeof(&mData); + return 1 + mLength->getByteCount() + mLength->getValue(); } \ No newline at end of file diff --git a/Procedural-Midi/MidiAsset/Source/Midi/Classes/Event/SystemExclusiveEvent.h b/Procedural-Midi/MidiAsset/Source/Midi/Classes/Event/SystemExclusiveEvent.h index e636b90..cee9542 100644 --- a/Procedural-Midi/MidiAsset/Source/Midi/Classes/Event/SystemExclusiveEvent.h +++ b/Procedural-Midi/MidiAsset/Source/Midi/Classes/Event/SystemExclusiveEvent.h @@ -4,6 +4,7 @@ #pragma once #include "ChannelEvent.h" +#include /** * System Exclusive MIDI Event @@ -12,15 +13,15 @@ class SystemExclusiveEvent : public MidiEvent { VariableLengthInt * mLength; - char * mData; + string * mData; public: - SystemExclusiveEvent(int type, long tick, char data[]); - SystemExclusiveEvent(int type, long tick, long delta, char data[]); + SystemExclusiveEvent(int type, long tick, string* data); + SystemExclusiveEvent(int type, long tick, long delta, string* data); ~SystemExclusiveEvent(); - char* getData(); - void setData(char data[]); + string* getData(); + void setData(string* data); bool requiresStatusByte(MidiEvent* prevEvent); diff --git a/Procedural-Midi/MidiAsset/Source/Midi/Classes/MML/LabMidiSong.cpp b/Procedural-Midi/MidiAsset/Source/Midi/Classes/MML/LabMidiSong.cpp index f851232..e2f9654 100644 --- a/Procedural-Midi/MidiAsset/Source/Midi/Classes/MML/LabMidiSong.cpp +++ b/Procedural-Midi/MidiAsset/Source/Midi/Classes/MML/LabMidiSong.cpp @@ -1,392 +1,19 @@ -// -// LabMidiSong.cpp -// - -/* - Copyright (c) 2012, Nick Porcino - All rights reserved. - - Redistribution and use in source and binary forms, with or without - modification, are permitted provided that the following conditions are met: - - * Redistributions of source code must retain the above copyright notice, this - list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above copyright notice, - this list of conditions and the following disclaimer in the documentation - and/or other materials provided with the distribution. - * The names of its contributors may not be used to endorse or promote products - derived from this software without specific prior written permission. - - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND - ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR - ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON - ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -// -// The parser is loosely ported to C++ from jasmid/midifile.js obtained from github 12/11/17 -// https://github.com/gasman/jasmid -// -// Thanks to the jasmid team for posting such clean and useful code on github. -// -// This is the only jasmid ported source in this library. Jasmid carried the -// following license when obtained from github: -/* -Copyright (c) 2010, Matt Westcott & Ben Firshman -All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are met: - -* Redistributions of source code must retain the above copyright notice, this -list of conditions and the following disclaimer. -* Redistributions in binary form must reproduce the above copyright notice, -this list of conditions and the following disclaimer in the documentation -and/or other materials provided with the distribution. -* The names of its contributors may not be used to endorse or promote products -derived from this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR -ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON -ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -*/ - // Updated: Scott Bishel #include "LabMidiSong.h" -#include -#include #include -#include -#include +#include "./MidiTrack.h" #include "./Event/Meta/Tempo.h" -#include "./Event/MidiEvent.h" - -#include "./Event/ChannelEvent.h" -#include "./Event/ProgramChange.h" -#include "./Event/NoteOn.h" - #include "./Util/MidiUtil.h" namespace Lab { - - //------------------------------------------------------------ - // MML support - // - - int secondsToTicks(double seconds, double bpm, int ticksPerBeat) - { - double beats = seconds * (bpm / 60.0); - double ticks = beats * ticksPerBeat; - return int(ticks); - } - - int wholeNoteToTicks(double fraction, double bpm, int ticksPerBeat) - { - double seconds = (bpm / 60.0f) / fraction; - return secondsToTicks(seconds, bpm, ticksPerBeat); - } - - int getMMLInt(char const*& curr) - { - int v = 0; - char c = *curr; - int sign = 1; - while (c == '-' || (c >= '0' && c <= '9')) { - if (c == '-') - sign = -1; - else - v = v * 10 + (c - '0'); - ++curr; - c = *curr; - } - return v * sign; - } - - int sharpFlat(char const*& midifiledata) - { - char c = *midifiledata; - if (c == '-') { - ++midifiledata; - return -1; - } - if (c == '+' || c == '#') { - ++midifiledata; - return 1; - } - return 0; - } - - int dotted(char const*& midifiledata, int bpm, int ticksPerBeat, int l) - { - int ret = l; - - // denominator is a fraction of a whole note - int denominator = getMMLInt(midifiledata); - if (denominator) - ret = denominator; - - int l2 = ret * 2; - char c = *midifiledata; - while (c == '.') { - ++midifiledata; - ret += l2; - l2 = l2 * 2; - - c = *midifiledata; //TODO fixed infinite loop - } - return wholeNoteToTicks(ret, bpm, ticksPerBeat); - } - - int noteToChord(char n) { - switch(n) { - case 'c': - case 'C': - return 0; - case 'd': - case 'D': - return 2; - case 'e': - case 'E': - return 4; - case 'f': - case 'F': - return 5; - - case 'g': - case 'G': - return 7; - case 'a': - case 'A': - return 9; - case 'b': - case 'B': - return 11; - - } - - return 0; - } - - // The variant of MML parsed here was produced by studying http://www.g200kg.com/en/docs/webmodular/, - // mml2mid by Arle (unfortunately Arle's pages and the mml2mid sources are no longer online) and - // the wikipedia artile http://en.wikipedia.org/wiki/Music_Macro_Language - // - - // sample MML from http://www.g200kg.com/en/docs/webmodular/ - // t150 e-d-g-rg-4e-d-g-rg-4e-d-g-4g-4frf4e-d-frf4e-d-frf4e-d-f4f4g-rg-4 - - MidiFile* MidiSong::parseMML(char const*const mmlStr, int length) - { - MidiFile* mMidiFile = new MidiFile(); - int ticksPerBeat = 240;//mMidiFile->getResolution(); // 480 - - long ticks = 0; - - char const* curr = mmlStr; - char const* end = mmlStr + length; - - int octave = 4; - int tr = 0; - int err = 0; - bool tied = false; - int tempo = 120; - int len = 8; // an 1/8th note - int volume = 127; - - //clearTracks(); - mMidiFile->addTrack(new MidiTrack()); - MidiTrack* track = (mMidiFile->getTracks())[tr]; - - do { - char c = *curr++; - - switch (c) { - case 'l': // length NN - case 'L': - len = getMMLInt(curr); - break; - - case 'o': - case 'O': - octave = getMMLInt(curr); - if (octave < 0) - octave = 0; - if (octave > 7) - octave = 7; - break; - - case '<': - ++octave; - if (octave > 7) - octave = 7; - break; - case '>': - --octave; - if (octave < 0) - octave = 0; - break; - - case '@': { // tone selection - int i = getMMLInt(curr); - if (i < 0) - i = 0; - if (i > 127) - i = 127; - track->insertEvent(new ProgramChange(ticks, tr, i)); - break; - } - - case 'v': // volume - case 'V': { - volume = getMMLInt(curr); - if (volume < 0) - volume = 0; - if (volume > 127) - volume = 127; - break; - } - - case 't': // tempo in bpm - case 'T': { - tempo = getMMLInt(curr); - if (tempo < 0) - tempo = 0; - if (tempo > 500) - tempo = 500; - track->insertEvent(new Tempo(ticks, 0, MidiUtil::bpmToMpqn((float)tempo))); - break; - } - - case ',': - ticks = 0; - tr++; - if (tr > 15) - tr = 15; - while (tr >= mMidiFile->getTrackCount()) - mMidiFile->addTrack(new MidiTrack()); - track = (mMidiFile->getTracks())[tr]; - break; - - case 'c': - case 'C': - case 'd': - case 'D': - case 'e': - case 'E': - case 'f': - case 'F': - case 'g': - case 'G': - case 'a': - case 'A': - case 'b': - case 'B': { - int note = noteToChord(c) + sharpFlat(curr) + octave * 12; - int duration = dotted(curr, tempo, ticksPerBeat, len); - - // note output { tr, cnt, size 3, 0x90|tr note & 0x7f vol=0x7f } - if (!tied) { - track->insertNote(tr, note & 0x7f, volume, ticks, duration); - } - else { - // note output { tr, cnt-1, size 3, 0x90|tr note & 0x7f vol=0x00 } - track->insertEvent(new NoteOn(ticks, tr, note & 0x7f, volume)); - track->insertEvent(new NoteOn(ticks + duration, tr, note & 0x7f, volume)); - - //track->insertNote(tr, note & 0x7f, volume, ticks, 0); - //track->insertNote(tr, note & 0x7f, volume, ticks, duration); - tied = false; - } - ticks += duration; - - - break; - } - - case '&': // tie (how to handle?) - tied = true; - break; - - case 'p': // pause - case 'P': - case 'r': // rest - case 'R': - { - int duration = dotted(curr, tempo, ticksPerBeat, len); - track->insertEvent(new NoteOn(ticks+ duration, tr, 0, 0)); - tied = false; - - ticks += duration; - break; - } - - //case ',': // other - case '[': - case ']': - break; - - case '#': // comment - { - while (c != '\n') { - ++curr; - c = *curr; //TODO fixed infinite loop - } - break; - } - - - case ' ': // ignore - case ' ': - case 10: - case 13: - break; - - default: - err = 1; - break; - } - } while (curr < end); - vector trks = mMidiFile->getTracks(); - for (int i = 0; i < (int)trks.size(); i++) - trks[i]->closeTrack(); - - return mMidiFile; - } - - // ---------------------------------------- static uint32_t get_note_length_ms(CHORD *p, uint32_t note_ticks) { - return (60000) * note_ticks / p->bpm / p->bticks; - } - - static void note_clear(CHORD *p) - { - p->count = 0; - } - - static void note_stack(CHORD *p, int number) - { - if ((p->count + 1) < CHORD_MAX_NOTES) { - p->freqlist[p->count++] = number; - } + return 60000 * note_ticks / p->bpm / p->bticks; } static void chord_init(CHORD *p, int bpm, int bticks) @@ -398,15 +25,16 @@ namespace Lab { static void note_sound(CHORD *p, int ticks, int number, MidiSong* component) { uint32_t ms = get_note_length_ms(p, ticks); - uint32_t duration = (uint32_t)MidiUtil::msToTicks((long)ms, (float)p->bpm, 480); - component->track->insertNote(component->trackNumber, number & 0x7f, 127, component->ticks, duration); + uint32_t duration = (uint32_t)MidiUtil::msToTicks((long)ms, (float)p->bpm, p->bticks); + + component->track->insertNote(component->channel, number & 0x7f, component->volume, component->ticks, duration); component->ticks += duration; } static void rest_sound(CHORD *p, int ticks, MidiSong* component) { uint32_t ms = get_note_length_ms(p, ticks); - uint32_t duration = (uint32_t)MidiUtil::msToTicks((long)ms, (float)p->bpm, 480); + uint32_t duration = (uint32_t)MidiUtil::msToTicks((long)ms, (float)p->bpm, p->bticks); component->ticks += duration; } @@ -414,6 +42,7 @@ namespace Lab { { MidiSong* component = (MidiSong*)extobj; CHORD& chord = component->chord; + switch (p->type) { case MML_TYPE_TEMPO: { @@ -426,14 +55,6 @@ namespace Lab { { MML_ARGS_NOTE *args = &(p->args.note); note_sound(&chord, args->ticks, args->number, component); - - //note_stack(&chord, args->number); - - //if (0 < args->ticks) { - - // note_sound(&chord, args->ticks, component); - // note_clear(&chord); - //} } break; case MML_TYPE_REST: @@ -442,17 +63,28 @@ namespace Lab { rest_sound(&chord, args->ticks, component); } break; + case MML_TYPE_LENGTH: + break; + case MML_TYPE_VOLUME: + { + MML_ARGS_VOLUME *args = &(p->args.volume); + component->volume = args->value; + break; + } + case MML_TYPE_OCTAVE: + case MML_TYPE_OCTAVE_UP: + case MML_TYPE_OCTAVE_DOWN: + case MML_TYPE_USER_EVENT: + break; } - } - // Called when the game starts void MidiSong::LoadString(const string &data) { - track = new MidiTrack(); + volume = 100; + ticks = 0; - trackNumber = 0; /* * Initialize the MML module. */ @@ -464,14 +96,13 @@ namespace Lab { */ int tempo_default = 120; chord_init(&chord, tempo_default, mml_opt.bticks); - chord.count = 0; mml_setup(&mml, &mml_opt, (char*)data.c_str()); - + MML_RESULT cher; - while ( (cher = mml_fetch(&mml)) == MML_RESULT_OK) { + while ((cher = mml_fetch(&mml)) == MML_RESULT_OK) { } - note_clear(&chord); + track->closeTrack(); } - + } // Lab diff --git a/Procedural-Midi/MidiAsset/Source/Midi/Classes/MML/LabMidiSong.h b/Procedural-Midi/MidiAsset/Source/Midi/Classes/MML/LabMidiSong.h index bf09f59..05ad72a 100644 --- a/Procedural-Midi/MidiAsset/Source/Midi/Classes/MML/LabMidiSong.h +++ b/Procedural-Midi/MidiAsset/Source/Midi/Classes/MML/LabMidiSong.h @@ -1,59 +1,20 @@ -// -// LabMidiSong.h -// +// Updated: Scott Bishel -/* - Copyright (c) 2012, Nick Porcino - All rights reserved. - - Redistribution and use in source and binary forms, with or without - modification, are permitted provided that the following conditions are met: - - * Redistributions of source code must retain the above copyright notice, this - list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above copyright notice, - this list of conditions and the following disclaimer in the documentation - and/or other materials provided with the distribution. - * The names of its contributors may not be used to endorse or promote products - derived from this software without specific prior written permission. - - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND - ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR - ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON - ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ #pragma once -#include "../MidiFile.h" - -#include -#include - +#include "../MidiTrack.h" #include "./MML/mml.h" namespace Lab { -#define CHORD_MAX_NOTES (128) -#define CHORD_SPLIT_TIME_MS (14) - typedef struct { uint32_t bpm; uint32_t bticks; - int freqlist[CHORD_MAX_NOTES]; - int count; } CHORD; class MidiSong { public: - static MidiFile* parseMML(char const*const mmlStr, int length); - void LoadString(const string &data); CHORD chord; @@ -62,7 +23,8 @@ namespace Lab { long ticks = 0; - int trackNumber = 0; + int channel = 0; + int volume = 100; MidiTrack* track; }; diff --git a/Procedural-Midi/MidiAsset/Source/Midi/Classes/MidiFile.cpp b/Procedural-Midi/MidiAsset/Source/Midi/Classes/MidiFile.cpp index 12a8557..1ea8e97 100644 --- a/Procedural-Midi/MidiAsset/Source/Midi/Classes/MidiFile.cpp +++ b/Procedural-Midi/MidiAsset/Source/Midi/Classes/MidiFile.cpp @@ -139,6 +139,9 @@ void MidiFile::removeTrack(int pos) { return; } + delete mTracks[pos]; + mTracks[pos] = NULL; + mTracks.erase(mTracks.begin() + pos); mTrackCount = (int)mTracks.size(); mType = mTrackCount > 1 ? 1 : 0; @@ -163,7 +166,7 @@ void MidiFile::writeToFile(ostream & output) void MidiFile::initFromBuffer(char buffer[]) { - if (!MidiUtil::bytesEqual(buffer, (char*)IDENTIFIER, 0, 4)) + if (!MidiUtil::bytesEqual(buffer, IDENTIFIER, 0, 4)) { cerr << "File identifier not MThd. Exiting"; mType = 0; diff --git a/Procedural-Midi/MidiAsset/Source/Midi/Classes/MidiTrack.cpp b/Procedural-Midi/MidiAsset/Source/Midi/Classes/MidiTrack.cpp index f73db08..137e3d5 100644 --- a/Procedural-Midi/MidiAsset/Source/Midi/Classes/MidiTrack.cpp +++ b/Procedural-Midi/MidiAsset/Source/Midi/Classes/MidiTrack.cpp @@ -9,8 +9,6 @@ #include "Event/Meta/Tempo.h" #include "Event/Meta/EndOfTrack.h" -#include // std::sort - const char MidiTrack::IDENTIFIER[] = { 'M', 'T', 'r', 'k' }; MidiTrack* MidiTrack::createTempoTrack() { @@ -38,7 +36,7 @@ MidiTrack::MidiTrack(istream & input) char buffer[4] = { 0 }; input.read(buffer, 4); - if (!MidiUtil::bytesEqual(buffer, (char*)IDENTIFIER, 0, 4)) { + if (!MidiUtil::bytesEqual(buffer, IDENTIFIER, 0, 4)) { cerr << "Track identifier did not match MTrk!"; return; } @@ -59,15 +57,6 @@ MidiTrack::~MidiTrack() } } -// sort MIDI predicate -inline static bool ConstPredicate(const MidiEvent* ip1, const MidiEvent* ip2) -{ - int value = ((MidiEvent*)ip1)->compareTo((MidiEvent*)ip2); - - // somehow value should be less then else it flips the MIDI file - return value < 0; -} - void MidiTrack::readTrackData(istream & input) { long totalTicks = 0; @@ -94,12 +83,9 @@ void MidiTrack::readTrackData(istream & input) } mEvents.push_back(E); } - - // TODO allow to resort -// std::sort(mEvents.begin(), mEvents.end(), ConstPredicate); } -vector& MidiTrack::getEvents() { +std::vector& MidiTrack::getEvents() { return mEvents; } @@ -172,9 +158,6 @@ void MidiTrack::insertEvent(MidiEvent * newEvent) { next->setDelta(next->getTick() - newEvent->getTick()); } - // TODO allow to resort -// std::sort(mEvents.begin(), mEvents.end(), ConstPredicate); - mSize += newEvent->getSize(); if (newEvent->getType() == MetaEvent::END_OF_TRACK) { if (next != NULL) { diff --git a/Procedural-Midi/MidiAsset/Source/Midi/Classes/MidiTrack.h b/Procedural-Midi/MidiAsset/Source/Midi/Classes/MidiTrack.h index 82b7fbd..c1d0573 100644 --- a/Procedural-Midi/MidiAsset/Source/Midi/Classes/MidiTrack.h +++ b/Procedural-Midi/MidiAsset/Source/Midi/Classes/MidiTrack.h @@ -21,7 +21,7 @@ class MidiTrack bool mSizeNeedsRecalculating; bool mClosed; - vector mEvents; + std::vector mEvents; public: static MidiTrack* createTempoTrack(); @@ -30,7 +30,7 @@ class MidiTrack MidiTrack(istream & input); ~MidiTrack(); - vector& getEvents(); + std::vector& getEvents(); private: void readTrackData(istream & input); diff --git a/Procedural-Midi/MidiAsset/Source/Midi/Classes/Util/MetronomeTick.cpp b/Procedural-Midi/MidiAsset/Source/Midi/Classes/Util/MetronomeTick.cpp index c753411..293b7d3 100644 --- a/Procedural-Midi/MidiAsset/Source/Midi/Classes/Util/MetronomeTick.cpp +++ b/Procedural-Midi/MidiAsset/Source/Midi/Classes/Util/MetronomeTick.cpp @@ -8,6 +8,9 @@ MetronomeTick::MetronomeTick(TimeSignature* sig, int resolution) : MidiEvent(0, 0), mMetronomeProgress(0.0) { + // custom type to represent class + mType = MetronomeTick::TYPE; + mResolution = resolution; setTimeSignature(sig); diff --git a/Procedural-Midi/MidiAsset/Source/Midi/Classes/Util/MetronomeTick.h b/Procedural-Midi/MidiAsset/Source/Midi/Classes/Util/MetronomeTick.h index cd82fde..7604824 100644 --- a/Procedural-Midi/MidiAsset/Source/Midi/Classes/Util/MetronomeTick.h +++ b/Procedural-Midi/MidiAsset/Source/Midi/Classes/Util/MetronomeTick.h @@ -36,6 +36,9 @@ class MetronomeTick : public MidiEvent std::string toString(); int compareTo(MidiEvent* o); + + // TODO custom event type + static const int TYPE = -255; protected: int getEventSize(); diff --git a/Procedural-Midi/MidiAsset/Source/Midi/Classes/Util/MidiProcessor.cpp b/Procedural-Midi/MidiAsset/Source/Midi/Classes/Util/MidiProcessor.cpp index 279e705..ae9ca02 100644 --- a/Procedural-Midi/MidiAsset/Source/Midi/Classes/Util/MidiProcessor.cpp +++ b/Procedural-Midi/MidiAsset/Source/Midi/Classes/Util/MidiProcessor.cpp @@ -62,17 +62,15 @@ void MidiProcessor::start(const double& deltaTime /*= clock()*/) { } void MidiProcessor::stop() { - mRunning = false; + if (!mRunning) return; + mRunning = false; mListener->onStop(false); } void MidiProcessor::reset() { mRunning = false; - // makes sure thread is stopped - mListener->onStop(false); - mTicksElapsed = 0; mMsElapsed = 0; @@ -169,6 +167,8 @@ void MidiProcessor::process() { } mRunning = false; + this->reset(); + mListener->onStop(true); } \ No newline at end of file diff --git a/Procedural-Midi/MidiAsset/Source/Midi/Classes/Util/MidiProcessor.h b/Procedural-Midi/MidiAsset/Source/Midi/Classes/Util/MidiProcessor.h index 68d782a..f532c7c 100644 --- a/Procedural-Midi/MidiAsset/Source/Midi/Classes/Util/MidiProcessor.h +++ b/Procedural-Midi/MidiAsset/Source/Midi/Classes/Util/MidiProcessor.h @@ -46,10 +46,6 @@ class MidiProcessor // Sets the play back rate double PlayRate; - /*TODO set time if using user based clock*/ - void setStartClock(double time) { - mLastMs = time; - } // TODO temp expose to get current track int _trackID; @@ -76,65 +72,4 @@ class MidiProcessor double mLastMs; MidiEventListener* mListener; - - /*class MidiTrackEventIterator - { - private: - MidiTrack* mTrack; - vector::iterator mIterator, mEnd; - MidiEvent* mEvent; - MidiProcessor * mProccessor; - - public: - MidiTrackEventIterator(MidiTrack* track, MidiProcessor* p) :mEvent(NULL) - { - mTrack = track; - mProccessor = p; - - this->Reset(); - } - - void parseNextEventsUpToTick(double tick) - { - - while (mEvent != NULL) - { - - if (mEvent->getTick() <= tick) - { - if (mIterator != mEnd) - { - mEvent = *mIterator; - mProccessor->dispatch(mEvent); - mIterator++; - } - else - { - mEvent = NULL; - } - } - else - { - break; - } - } - } - - bool hasMoreEvents() const - { - return mEvent != NULL; - } - - void Reset() { - mEvent = NULL; - - mIterator = mTrack->getEvents().begin(); - mEnd = mTrack->getEvents().end(); - - if (mIterator != mEnd) - { - mEvent = *mIterator; - } - } - };*/ }; diff --git a/Procedural-Midi/MidiAsset/Source/Midi/Classes/Util/MidiUtil.cpp b/Procedural-Midi/MidiAsset/Source/Midi/Classes/Util/MidiUtil.cpp index 3a60315..b6dba73 100644 --- a/Procedural-Midi/MidiAsset/Source/Midi/Classes/Util/MidiUtil.cpp +++ b/Procedural-Midi/MidiAsset/Source/Midi/Classes/Util/MidiUtil.cpp @@ -34,7 +34,7 @@ float MidiUtil::mpqnToBpm(int mpqn) { /** * Utility methods for working with bytes and byte buffers */ -int MidiUtil::bytesToInt(char buff[], int off, int len) { +int MidiUtil::bytesToInt(const char buff[], int off, int len) { int num = 0; @@ -66,10 +66,10 @@ char * MidiUtil::intToBytes(int val, int byteCount) { return buffer; } -bool MidiUtil::bytesEqual(char buf1[], char buf2[], int off, int len) { +bool MidiUtil::bytesEqual(string buf1, string buf2, int off, int len) { for (int i = off; i < off + len; i++) { - if (i >= sizeof(&buf1) || i >= sizeof(&buf2)) { + if ( (i >= buf1.size()) || (i >= buf2.size()) ) { return false; } if (buf1[i] != buf2[i]) { @@ -101,9 +101,9 @@ string MidiUtil::byteToHex(char b) { ss << HEX[high] << HEX[low]; return ss.str(); } -string MidiUtil::bytesToHex(char b[]) { +string MidiUtil::bytesToHex(string b) { stringstream ss; - for (int i = 0; i < sizeof(&b); i++) { + for (int i = 0; i < b.size(); i++) { ss << byteToHex(b[i]) << " "; } return ss.str(); diff --git a/Procedural-Midi/MidiAsset/Source/Midi/Classes/Util/MidiUtil.h b/Procedural-Midi/MidiAsset/Source/Midi/Classes/Util/MidiUtil.h index 20fdebf..d422fc6 100644 --- a/Procedural-Midi/MidiAsset/Source/Midi/Classes/Util/MidiUtil.h +++ b/Procedural-Midi/MidiAsset/Source/Midi/Classes/Util/MidiUtil.h @@ -8,6 +8,7 @@ /** * */ + class MidiUtil { @@ -21,10 +22,10 @@ class MidiUtil static int bpmToMpqn(float bpm); static float mpqnToBpm(int mpqn); - static int bytesToInt(char buff[], int off, int len); + static int bytesToInt(const char buff[], int off, int len); static char* intToBytes(int val, int byteCount); - static bool bytesEqual(char buf1[], char buf2[], int off, int len); + static bool bytesEqual(string buf1, string buf2, int off, int len); static char* extractBytes(char buffer[], int off, int len); private: @@ -32,5 +33,5 @@ class MidiUtil public: static std::string byteToHex(char b); - static std::string bytesToHex(char b[]); + static std::string bytesToHex(string b); }; \ No newline at end of file diff --git a/Procedural-Midi/MidiAsset/Source/Midi/Classes/Util/VariableLengthInt.cpp b/Procedural-Midi/MidiAsset/Source/Midi/Classes/Util/VariableLengthInt.cpp index 276d4dd..18cc7d9 100644 --- a/Procedural-Midi/MidiAsset/Source/Midi/Classes/Util/VariableLengthInt.cpp +++ b/Procedural-Midi/MidiAsset/Source/Midi/Classes/Util/VariableLengthInt.cpp @@ -7,15 +7,28 @@ #include VariableLengthInt::VariableLengthInt(int value) { + mBytes = NULL; setValue(value); } VariableLengthInt::VariableLengthInt(istream & input) { + mBytes = NULL; parseBytes(input); } +VariableLengthInt::~VariableLengthInt() { + if (mBytes != NULL) + delete[] mBytes; + mBytes = NULL; +} + void VariableLengthInt::setValue(int value) { mValue = value; + + if (mBytes != NULL) + delete[] mBytes; + mBytes = NULL; + buildBytes(); } int VariableLengthInt::getValue() { @@ -25,23 +38,18 @@ int VariableLengthInt::getValue() { int VariableLengthInt::getByteCount() { return mSizeInBytes; } -char * VariableLengthInt::getBytes() { +char* VariableLengthInt::getBytes() { return mBytes; } void VariableLengthInt::parseBytes(istream & input) { - for (int i = 0; i < 4; i++) { - mBytes[i] = 0; - } - int ints[4] = { 0 }; mSizeInBytes = 0; mValue = 0; int shift = 0; - int b = 0; - b = input.get(); + int b = input.get(); while (mSizeInBytes < 4) { mSizeInBytes++; @@ -59,7 +67,7 @@ void VariableLengthInt::parseBytes(istream & input) { for (int i = 1; i < mSizeInBytes; i++) { shift += 7; } - + mBytes = new char[mSizeInBytes]; for (int i = 0; i < mSizeInBytes; i++) { mBytes[i] = (char)ints[i]; @@ -69,12 +77,10 @@ void VariableLengthInt::parseBytes(istream & input) { } void VariableLengthInt::buildBytes() { - for (int i = 0; i < 4; i++) { - mBytes[i] = 0; - } - if (mValue == 0) { mSizeInBytes = 1; + mBytes = new char[mSizeInBytes]; + mBytes[0] = 0; return; } @@ -92,7 +98,7 @@ void VariableLengthInt::buildBytes() { for (int i = 1; i < mSizeInBytes; i++) { vals[i] |= 0x80; } - + mBytes = new char[mSizeInBytes]; for (int i = 0; i < mSizeInBytes; i++) { mBytes[i] = (char)vals[mSizeInBytes - i - 1]; } diff --git a/Procedural-Midi/MidiAsset/Source/Midi/Classes/Util/VariableLengthInt.h b/Procedural-Midi/MidiAsset/Source/Midi/Classes/Util/VariableLengthInt.h index 554224a..c0d314b 100644 --- a/Procedural-Midi/MidiAsset/Source/Midi/Classes/Util/VariableLengthInt.h +++ b/Procedural-Midi/MidiAsset/Source/Midi/Classes/Util/VariableLengthInt.h @@ -13,12 +13,13 @@ using namespace std; class VariableLengthInt { int mValue; - char mBytes[4]; + char* mBytes; int mSizeInBytes; public: VariableLengthInt(int value); VariableLengthInt(istream & input); + ~VariableLengthInt(); void setValue(int value); int getValue(); diff --git a/Procedural-Midi/MidiAsset/Source/Midi/Private/MidiComponent.cpp b/Procedural-Midi/MidiAsset/Source/Midi/Private/MidiComponent.cpp index e54e851..44f752d 100644 --- a/Procedural-Midi/MidiAsset/Source/Midi/Private/MidiComponent.cpp +++ b/Procedural-Midi/MidiAsset/Source/Midi/Private/MidiComponent.cpp @@ -7,7 +7,7 @@ #include "Engine/Engine.h" #include "Misc/FileHelper.h" -#include "MidiUtils.h" +#include "MidiStruct.h" #include "MidiFile.h" #include "Util/MidiUtil.h" @@ -22,13 +22,27 @@ #include "Event/SystemExclusiveEvent.h" #include "Event/Meta/Tempo.h" +#include "Event/Meta/TextualMetaEvent.h" +#include "Event/Meta/SequencerSpecificEvent.h" +#include "Event/Meta/TimeSignature.h" +#include "Event/Meta/EndOfTrack.h" +#include "Event/Meta/GenericMetaEvent.h" +#include "Event/Meta/KeySignature.h" +#include "Event/Meta/MidiChannelPrefix.h" +#include "Event/Meta/SequenceNumber.h" +#include "Event/Meta/SequencerSpecificEvent.h" +#include "Event/Meta/SmpteOffset.h" + #include "MidiAsset.h" +#include "Util/MetronomeTick.h" #include "Util/MidiProcessor.h" #include "MML/LabMidiSong.h" #include "Thread/MidiThread.h" +#include // std::sort + // Sets default values for this component's properties UMidiComponent::UMidiComponent() : PlaySpeed(1.0), mWorker(NULL) { @@ -44,11 +58,6 @@ UMidiComponent::UMidiComponent() : PlaySpeed(1.0), mWorker(NULL) UMidiComponent::~UMidiComponent() { mProcessor.stop(); - // make sure thread is deleted - if (mWorker) - delete mWorker; - mWorker = NULL; - if (mMidiFile) delete mMidiFile; mMidiFile = NULL; @@ -83,7 +92,7 @@ void UMidiComponent::TickComponent( float DeltaTime, ELevelTick TickType, FActor while (!mQueue.IsEmpty()) { MidiCallbackMessage _midiEvent; mQueue.Dequeue(_midiEvent); - OnMidiEvent.Broadcast(_midiEvent.Event, _midiEvent.ms, _midiEvent.trackID); + handleCallback(_midiEvent.Event, _midiEvent.ms, _midiEvent.trackID); } } @@ -144,52 +153,110 @@ void UMidiComponent::LoadFile(FString path) { void UMidiComponent::LoadMML(FString path) { if (!canInit()) return; std::string MyStdString(TCHAR_TO_UTF8(*path)); - + + mMidiFile = new MidiFile(); + // Load MML Lab::MidiSong song; - song.trackNumber = 0; + song.channel = 0; song.LoadString(MyStdString); - mMidiFile = new MidiFile(); mMidiFile->addTrack(song.track); + mProcessor.load(*mMidiFile); } void UMidiComponent::onEvent(MidiEvent* _event, long ms) { + if (InBackground) { + MidiCallbackMessage msg = { _event, ms, mProcessor._trackID }; + mQueue.Enqueue(msg); + } + else + handleCallback(_event, ms, mProcessor._trackID); +} + +void UMidiComponent::handleCallback(MidiEvent* _event, long ms, int trackID) +{ + int midi_type = _event->getType(); + // Channel Event - if (_event->getType() >= ChannelEvent::NOTE_OFF && _event->getType() <= ChannelEvent::PITCH_BEND) { + if (midi_type >= ChannelEvent::NOTE_OFF && + midi_type <= ChannelEvent::PITCH_BEND) + { ChannelEvent* channelEvent = static_cast(_event); FMidiEvent _midiEvent; - _midiEvent.Type = static_cast(_event->getType() & 0X0F); - _midiEvent.Channel = channelEvent->getChannel() & 0x0F; - _midiEvent.Data1 = channelEvent->getValue1() & 0xFF; - _midiEvent.Data2 = channelEvent->getValue2() & 0xFF; - + _midiEvent.Type = static_cast(midi_type); + _midiEvent.Channel = channelEvent->getChannel(); + _midiEvent.Data1 = channelEvent->getValue1(); + _midiEvent.Data2 = channelEvent->getValue2(); + if (SimplifyNote) { // Running Status Event [Improved Midi Performance] - if (_event->getType() == ChannelEvent::NOTE_OFF) { - _midiEvent.Type = static_cast(ChannelEvent::NOTE_ON & 0X0F); - _midiEvent.Data2 = 0 & 0XFF; + if (midi_type == ChannelEvent::NOTE_OFF) { + _midiEvent.Type = static_cast(ChannelEvent::NOTE_ON); + _midiEvent.Data2 = 0; } } - if (InBackground) { - MidiCallbackMessage msg = { _midiEvent, ms, mProcessor._trackID }; - mQueue.Enqueue(msg); + OnMidiEvent.Broadcast(_midiEvent, ms, trackID); + } + // System Exclusive + else if (midi_type == 0xF0 || midi_type == 0xF7) + { + if (!OnSysExEvent.IsBound()) + return; + + SystemExclusiveEvent* sysExEvent = static_cast(_event); + TArray data; + string * ptr = sysExEvent->getData(); + + // multi-packet event + bool isDivided = false; + + // Add 0xF0 SysEX Start + if(midi_type == 0xF0) + data.Add(midi_type); + // multi-packet check + else if (midi_type == 0xF7 && data.Num() > 3) { + data.Add((uint8)0xF0); + isDivided = true; } - else - OnMidiEvent.Broadcast(_midiEvent, ms, mProcessor._trackID); + + // Add + data.Append((uint8*)ptr->c_str(), ptr->size()); + + // add 0xF7 SysEx End on Divided + if (isDivided) { + // close message + if(data[data.Num() - 1] != 0xF7) + data.Add((uint8)0xF7); + } + + OnSysExEvent.Broadcast(data, ms, trackID); + } + // Textual Meta Event + else if (midi_type >= MetaEvent::TEXT_EVENT && + midi_type <= MetaEvent::CUE_POINT) + { + if (!OnTextEvent.IsBound()) + return; + TextualMetaEvent* textExEvent = static_cast(_event); + FString text = UTF8_TO_TCHAR(textExEvent->getText().c_str()); + OnTextEvent.Broadcast(static_cast(midi_type), text, ms, trackID); + } + // Metronome + else if (midi_type == MetronomeTick::TYPE) + { + if (!OnMetronomeTick.IsBound()) + return; + MetronomeTick* ev = static_cast(_event); + OnMetronomeTick.Broadcast(ev->getBeatNumber(), ev->getMeasure(), ms); } } void UMidiComponent::onStart(bool fromBeginning) { - // MultiThread - if (InBackground) { - mWorker = new FMidiProcessorWorker(&mProcessor, this->isGameTime); - } - OnStart.Broadcast(fromBeginning); } void UMidiComponent::onStop(bool finish) { - // MultiThread + // MultiThread if (mWorker) { mWorker->Stop(); delete mWorker; @@ -197,7 +264,7 @@ void UMidiComponent::onStop(bool finish) { mWorker = NULL; mQueue.Empty(); - OnStop.Broadcast(finish); + OnStop.Broadcast(finish); } //----------------------------------- @@ -207,13 +274,21 @@ void UMidiComponent::start(bool background, bool UseGameTime) { InBackground = background; this->isGameTime = UseGameTime; + if (UseGameTime) { mProcessor.milliFunction = NULL; - mProcessor.start(GetWorld()->TimeSeconds * 1000.0f); + if (!InBackground) + mProcessor.start(GetWorld()->TimeSeconds * 1000.0f); } else { mProcessor.milliFunction = FPlatformTime::ToMilliseconds; - mProcessor.start(FPlatformTime::Cycles()); + if (!InBackground) + mProcessor.start(FPlatformTime::Cycles()); + } + + // MultiThread + if (InBackground) { + mWorker = new FMidiProcessorWorker(&mProcessor, this->isGameTime); } } } @@ -223,6 +298,7 @@ void UMidiComponent::stop() { } void UMidiComponent::reset() { + mProcessor.stop(); mProcessor.reset(); } @@ -245,62 +321,63 @@ int UMidiComponent::GetResolution() } } +// sort MIDI predicate +inline static bool _ConstPredicate(const MidiEvent* ip1, const MidiEvent* ip2) +{ + int value = ((MidiEvent*)ip1)->compareTo((MidiEvent*)ip2); + + // somehow value should be less then else it flips the MIDI file + return value < 0; +} + float UMidiComponent::GetDuration() { - // TODO find a better solution if (mMidiFile) { - vector::iterator > mCurrEvents; - vector::iterator > mCurrEventsEnd; + vector::iterator > mCurrEvents; + vector::iterator > mCurrEventsEnd; + vector& tracks = mMidiFile->getTracks(); for (int i = 0; i < (int)tracks.size(); i++) { mCurrEvents.push_back(tracks[i]->getEvents().begin()); mCurrEventsEnd.push_back(tracks[i]->getEvents().end()); } - double mMsElapsed = 0; + double msElapsed = 0; const int& mPPQ = mMidiFile->getResolution(); int mMPQN = Tempo::DEFAULT_MPQN; - double mTicksElapsed = 0; - - while (true) { - - const double msElapsed = 1.0; - double ticksElapsed = MidiUtil::msToTicks(msElapsed, mMPQN, mPPQ) * mProcessor.PlayRate; - - mMsElapsed += msElapsed; - mTicksElapsed += ticksElapsed; - - for (int i = 0; i < (int)mCurrEvents.size(); i++) { - while (mCurrEvents[i] != mCurrEventsEnd[i]) { - MidiEvent * _event = *mCurrEvents[i]; - if (_event->getTick() <= mTicksElapsed) { - // Tempo and Time Signature events are always needed by the processor - if (_event->getType() == MetaEvent::TEMPO) { - mMPQN = (static_cast(_event))->getMpqn(); - } - ++mCurrEvents[i]; - } - else - break; - } - } - - bool more = false; - for (int i = 0; i < (int)mCurrEvents.size(); i++) { - if (mCurrEvents[i] != mCurrEventsEnd[i]) - { - more = true; - break; + + vector tempoTicks; + for (int i = 0; i < (int)mCurrEvents.size(); i++) { + while (mCurrEvents[i] != mCurrEventsEnd[i]) { + MidiEvent * _event = *mCurrEvents[i]; + // Tempo and Time Signature events are always needed by the processor + if (_event->getType() == MetaEvent::TEMPO) { + tempoTicks.push_back(static_cast(_event)); } + ++mCurrEvents[i]; } + } + std::sort(tempoTicks.begin(), tempoTicks.end(), _ConstPredicate); + + double ticksElapsed = 0; + for (int i = 0; i < (int)tempoTicks.size(); i++) { + Tempo * tempo = tempoTicks[i]; - if (more == false) - break; + if (i > 0) + ticksElapsed = tempoTicks[i - 1]->getTick(); + + msElapsed += MidiUtil::ticksToMs(tempo->getTick() - ticksElapsed, mMPQN, mPPQ); + mMPQN = tempo->getMpqn(); } - return mMsElapsed / 1000.0f; + if (tempoTicks.size() > 0) + ticksElapsed = tempoTicks[(int)tempoTicks.size() - 1]->getTick(); + + msElapsed += MidiUtil::ticksToMs(mMidiFile->getLengthInTicks() - ticksElapsed, mMPQN, mPPQ); + + return msElapsed / 1000.0; } else { diff --git a/Procedural-Midi/MidiAsset/Source/Midi/Private/MidiPrivatePCH.h b/Procedural-Midi/MidiAsset/Source/Midi/Private/MidiPrivatePCH.h index d54e705..2635d5c 100644 --- a/Procedural-Midi/MidiAsset/Source/Midi/Private/MidiPrivatePCH.h +++ b/Procedural-Midi/MidiAsset/Source/Midi/Private/MidiPrivatePCH.h @@ -1,6 +1,5 @@ // Copyright -> Scott Bishel -#include "CoreMinimal.h" #include "Midi.h" // You should place include statements to your module's private header files here. You only need to diff --git a/Procedural-Midi/MidiAsset/Source/Midi/Private/MidiUtils.cpp b/Procedural-Midi/MidiAsset/Source/Midi/Private/MidiUtils.cpp index ba3048f..dfc09f5 100644 --- a/Procedural-Midi/MidiAsset/Source/Midi/Private/MidiUtils.cpp +++ b/Procedural-Midi/MidiAsset/Source/Midi/Private/MidiUtils.cpp @@ -3,10 +3,6 @@ #include "MidiUtils.h" #include "MidiPrivatePCH.h" -#include "MidiFile.h" -#include "Event/ChannelEvent.h" -#include "Event/NoteOn.h" -#include "Event/NoteOff.h" const int CENTER_NOTE = 69; const float CENTER_FREQUENCY = 440.0; diff --git a/Procedural-Midi/MidiAsset/Source/Midi/Private/Thread/MidiThread.cpp b/Procedural-Midi/MidiAsset/Source/Midi/Private/Thread/MidiThread.cpp index 63d0ce7..a4b200a 100644 --- a/Procedural-Midi/MidiAsset/Source/Midi/Private/Thread/MidiThread.cpp +++ b/Procedural-Midi/MidiAsset/Source/Midi/Private/Thread/MidiThread.cpp @@ -38,10 +38,10 @@ uint32 FMidiProcessorWorker::Run() { return 0; if (this->isGameTime) { - ThePC->setStartClock(world->TimeSeconds * 1000.0f); + ThePC->start(world->TimeSeconds * 1000.0f); } else { - ThePC->setStartClock(FPlatformTime::Cycles()); + ThePC->start(FPlatformTime::Cycles()); } while (!IsFinished()) diff --git a/Procedural-Midi/MidiAsset/Source/Midi/Public/MidiComponent.h b/Procedural-Midi/MidiAsset/Source/Midi/Public/MidiComponent.h index 772a9c1..3f4cb90 100644 --- a/Procedural-Midi/MidiAsset/Source/Midi/Public/MidiComponent.h +++ b/Procedural-Midi/MidiAsset/Source/Midi/Public/MidiComponent.h @@ -3,11 +3,9 @@ #pragma once #include "MidiFile.h" -#include "Event/MidiEvent.h" -#include "Util/MetronomeTick.h" #include "Util/MidiProcessor.h" -#include "MidiUtils.h" +#include "MidiStruct.h" #include "Containers/Queue.h" #include "Components/ActorComponent.h" @@ -18,7 +16,10 @@ class FMidiProcessorWorker; DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FEventStart, bool, beginning); DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FEventStop, bool, finished); DECLARE_DYNAMIC_MULTICAST_DELEGATE_ThreeParams(FEventMidiEvent, struct FMidiEvent, Event, int32, time, int, TrackID); -//DECLARE_DYNAMIC_MULTICAST_DELEGATE_ThreeParams(FSysExEventReceive, const TArray&, data, int32, time, int, TrackID); +DECLARE_DYNAMIC_MULTICAST_DELEGATE_FourParams(FTextEventReceive, EMidiTextTypeEnum, type, const FString&, text, int32, time, int, TrackID); + +DECLARE_DYNAMIC_MULTICAST_DELEGATE_ThreeParams(FSysExEventReceive, const TArray&, data, int32, time, int, TrackID); +DECLARE_DYNAMIC_MULTICAST_DELEGATE_ThreeParams(FEventMetronome, int, beatNumber, int, measure, int32, time); /* * A component that loads/plays a MIDI Asset or file @@ -50,7 +51,7 @@ class MIDI_API UMidiComponent : public UActorComponent, public MidiEventListener bool SimplifyNote = false; /** - * loads a Midi Asset Data + * loads a MIDI Asset Data * @param MidiAsset - The UMidiAsset Object */ UFUNCTION(BlueprintCallable, Category = "MIDI|Processor") @@ -64,7 +65,7 @@ class MIDI_API UMidiComponent : public UActorComponent, public MidiEventListener void LoadFile(FString path); /** - * Load a MML Script/String - Experimental + * Load a MML Script/String * google tinymml * @param sheet - The MML script in string format */ @@ -115,9 +116,7 @@ class MIDI_API UMidiComponent : public UActorComponent, public MidiEventListener UFUNCTION(BlueprintCallable, BlueprintPure, Category = "MIDI|Processor") int GetResolution(); - /* Returns MIDI file duration in seconds - * Performance issue - */ + /* Returns MIDI file duration in seconds */ UFUNCTION(BlueprintCallable, BlueprintPure, Category = "MIDI|Processor") float GetDuration(); @@ -127,19 +126,21 @@ class MIDI_API UMidiComponent : public UActorComponent, public MidiEventListener UPROPERTY(BlueprintAssignable, Category = "MIDI|Processor") FEventStop OnStop; - /* Called when a Midi Event is received - * @param MidiEvent - The Event - * @param MS - time of event occured in milliseconds - * @param Track ID - Which track the event happened - */ + /* Called when a Midi Event is received */ UPROPERTY(BlueprintAssignable, Category = "MIDI|Processor") FEventMidiEvent OnMidiEvent; - //UPROPERTY(BlueprintAssignable, Category = "MIDI|Processor") - //FSysExEventReceive OnSysExEvent; + // Called when a System Exclusive is received + UPROPERTY(BlueprintAssignable, Category = "MIDI|Processor") + FSysExEventReceive OnSysExEvent; - //UPROPERTY(BlueprintAssignable, Category = "MIDI|Processor") - // FSysExEventReceive OnMetaEvent; + // Called when a Text Event is received (e.g. Cue, Marker) + UPROPERTY(BlueprintAssignable, Category = "MIDI|Processor") + FTextEventReceive OnTextEvent; + + // Metronome Ticker + UPROPERTY(BlueprintAssignable, Category = "MIDI|Processor") + FEventMetronome OnMetronomeTick; private: @@ -153,11 +154,12 @@ class MIDI_API UMidiComponent : public UActorComponent, public MidiEventListener class MidiCallbackMessage { public: - FMidiEvent Event; + MidiEvent* Event; long ms; int trackID; }; - // Handle Data Racing - TQueue mQueue; + // Handle Data Racing + TQueue mQueue; + void handleCallback(MidiEvent* _event, long ms, int trackID); }; \ No newline at end of file diff --git a/Procedural-Midi/MidiAsset/Source/Midi/Public/MidiStruct.h b/Procedural-Midi/MidiAsset/Source/Midi/Public/MidiStruct.h new file mode 100644 index 0000000..9c01ddd --- /dev/null +++ b/Procedural-Midi/MidiAsset/Source/Midi/Public/MidiStruct.h @@ -0,0 +1,243 @@ +// Copyright -> Scott Bishel + +#pragma once + +#include "UObject/ObjectMacros.h" +#include "UObject/Object.h" +#include "UObject/UnrealType.h" +#include "UObject/ScriptMacros.h" +#include "MidiStruct.generated.h" + +// Middle Key Notes +UENUM(BlueprintType) +enum ENoteEnum +{ + NE_C UMETA(DisplayName = "C"), + NE_CS UMETA(DisplayName = "C#"), + NE_D UMETA(DisplayName = "D"), + NE_DS UMETA(DisplayName = "D#"), + NE_E UMETA(DisplayName = "E"), + NE_F UMETA(DisplayName = "F"), + NE_FS UMETA(DisplayName = "F#"), + NE_G UMETA(DisplayName = "G"), + NE_GS UMETA(DisplayName = "G#"), + NE_A UMETA(DisplayName = "A"), + NE_AS UMETA(DisplayName = "A#"), + NE_B UMETA(DisplayName = "B") +}; + +UENUM(BlueprintType) +enum class EMidiTypeEnum : uint8 +{ + /* + * Note Off Event + * [Data1=Note, Data2=Velocity] + */ + MTE_NOTE_OFF = 8 UMETA(DisplayName = "Note Off"), + /* + * Note On + * [Data1=Note, Data2=Velocity] + * Note: Velocity of 0 = same as Note Off + */ + MTE_NOTE = 9 UMETA(DisplayName = "Note On"), + /* + * Note Aftertouch Event + * [Data1=Note, Data2=Amount] + */ + MTE_NOTE_AFTERTOUCH UMETA(DisplayName = "Note Aftertouch"), + /* + * Controller Event + * [Data1=Type, Data2=Amount] + */ + MTE_CONTROLLER UMETA(DisplayName = "Controller"), + /* + * Program(Instrument) Change Event + * [Data1=Number, Data2=0] + */ + MTE_PROGRAM_CHANGE UMETA(DisplayName = "Program Change"), + /* + * Channel Aftertouch Event + * [Data1=Amount, Data2=0] + */ + MTE_CHANNEL_AFTERTOUCH UMETA(DisplayName = "Channel Aftertouch"), + /* + * Pitch Bend Event + * [Data1=LowBit, Data2=HighBit] + */ + MTE_PITCH_BEND UMETA(DisplayName = "Pitch Bend") +}; +UENUM(BlueprintType) +enum class EMidiTextTypeEnum : uint8 +{ + MMTE_TEXT_EVENT = 1 UMETA(DisplayName = "Text Event"), + /* + * Copyright notice + */ + MMTE_COPYRIGHT_NOTICE UMETA(DisplayName = "Copyright Notice"), + /* + * Sequence / Track Name + */ + MMTE_TRACK_NAME UMETA(DisplayName = "Track Name"), + /* + * Instrument Name + */ + MMTE_INSTRUMENT_NAME UMETA(DisplayName = "Instrument Name"), + /* + * Lyrics + */ + MMTE_LYRICS UMETA(DisplayName = "Lyrics"), + /* + * Marker + */ + MMTE_MARKER UMETA(DisplayName = "Maker"), + /* + * Cue Point + */ + MMTE_CUE_POINT UMETA(DisplayName = "Cue Point"), +}; + +UENUM(BlueprintType) +enum class EMidiMetaTypeEnum : uint8 +{ + /* + * Sequence Number + * [Data1=Number] + */ + MMTE_SEQUENCE_NUMBER = 0 UMETA(DisplayName = "Sequence Number"), + /* + * Channel Prefix + * [Data1=Channel] + */ + MMTE_CHANNEL_PREFIX = 32 UMETA(DisplayName = "Channel Prefix"), + /* + * End Of Track + * [Data1=] + */ + END_OF_TRACK = 47 UMETA(DisplayName = "End Of Track"), + /* + * Tempo + * [Data1=] + */ + TEMPO = 81 UMETA(DisplayName = "Tempo"), + /* + * SMPTE Offset + * [Data1=] + */ + SMPTE_OFFSET = 84 UMETA(DisplayName = "SMPTE Offset"), + /* + * Time Signature + * [Data1=] + */ + TIME_SIGNATURE = 88 UMETA(DisplayName = "Time Signature"), + /* + * Key Signature + * [Data1=] + */ + KEY_SIGNATURE UMETA(DisplayName = "Key Signature"), +}; + +UENUM(BlueprintType) +enum class EMidiClockTypeEnum : uint8 +{ + /* + * Quarter Frame (MTC) + * [Data=] + */ + MCTE_QUARTER_FRAME = 1 UMETA(DisplayName = "Quarter Frame (MTC)"), + /* + * Song Position Pointer + * [Data=Position] + */ + MCTE_SONG_POSITION = 2 UMETA(DisplayName = "Song position pointer"), + /* + * Song Select + * [Data=SongNumber] + */ + MCTE_SONG_SELECT UMETA(DisplayName = "Song Select"), + /* + * Tune Request + * [Data=] + */ + MCTE_TUNE_REQUEST = 8 UMETA(DisplayName = "Tune Request"), + /* + * Clock + * [Data=] + */ + MCTE_CLOCK = 10 UMETA(DisplayName = "Timing Clock"), + /* + * Start + * [Data=] + */ + MCTE_START = 11 UMETA(DisplayName = "Start"), + /* + * Continue + * [Data=] + */ + MCTE_CONTINUE UMETA(DisplayName = "Continue"), + /* + * Stop + * [Data=] + */ + MCTE_STOP = 13 UMETA(DisplayName = "Stop"), + /* + * Active Sensing + * [Data=] + */ + MCTE_ACTIVE UMETA(DisplayName = "Active Sensing"), + /* + * Reset + * [Data=] + */ + MCTE_RESET UMETA(DisplayName = "Reset"), +}; +USTRUCT(BlueprintType) +struct FMidiEvent +{ + GENERATED_BODY() + + /* The Type of Event this struct represents */ + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "MIDI|Event") + EMidiTypeEnum Type; + + /* The Channel that the Event is on (0-15) */ + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "MIDI|Event") + uint8 Channel; + + /* The first data value (ex. Note) */ + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "MIDI|Event") + uint8 Data1; + + /* The second data value (ex, Velocity) */ + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "MIDI|Event") + uint8 Data2; + + /* Constructor */ + FMidiEvent() + { + Type = EMidiTypeEnum::MTE_NOTE; + Channel = 0; + Data1 = 0; + Data2 = 0; + } +}; + +USTRUCT(BlueprintType) +struct FMidiClockEvent +{ + GENERATED_BODY() + + /* The Type of Event this struct represents */ + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "MIDI|Event") + EMidiClockTypeEnum Type; + + /* The first data value */ + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "MIDI|Event") + int32 Data; + + /* Constructor */ + FMidiClockEvent() + { + Type = EMidiClockTypeEnum::MCTE_SONG_POSITION; + Data = 0; + } +}; \ No newline at end of file diff --git a/Procedural-Midi/MidiAsset/Source/Midi/Public/MidiUtils.h b/Procedural-Midi/MidiAsset/Source/Midi/Public/MidiUtils.h index 214423e..a82b4a5 100644 --- a/Procedural-Midi/MidiAsset/Source/Midi/Public/MidiUtils.h +++ b/Procedural-Midi/MidiAsset/Source/Midi/Public/MidiUtils.h @@ -3,174 +3,9 @@ #pragma once #include "Kismet/BlueprintFunctionLibrary.h" +#include "MidiStruct.h" #include "MidiUtils.generated.h" -// Middle Key Notes -UENUM(BlueprintType) -enum ENoteEnum -{ - NE_C UMETA(DisplayName = "C"), - NE_CS UMETA(DisplayName = "C#"), - NE_D UMETA(DisplayName = "D"), - NE_DS UMETA(DisplayName = "D#"), - NE_E UMETA(DisplayName = "E"), - NE_F UMETA(DisplayName = "F"), - NE_FS UMETA(DisplayName = "F#"), - NE_G UMETA(DisplayName = "G"), - NE_GS UMETA(DisplayName = "G#"), - NE_A UMETA(DisplayName = "A"), - NE_AS UMETA(DisplayName = "A#"), - NE_B UMETA(DisplayName = "B") -}; - -UENUM(BlueprintType) -enum class EMidiTypeEnum : uint8 -{ - /* - * Note Off Event - * [Data1=Note, Data2=Velocity] - */ - MTE_NOTE_OFF = 8 UMETA(DisplayName = "Note Off"), - /* - * Note On - * [Data1=Note, Data2=Velocity] - * Note: Velocity of 0 = same as Note Off - */ - MTE_NOTE = 9 UMETA(DisplayName = "Note On"), - /* - * Note Aftertouch Event - * [Data1=Note, Data2=Amount] - */ - MTE_NOTE_AFTERTOUCH UMETA(DisplayName = "Note Aftertouch"), - /* - * Controller Event - * [Data1=Type, Data2=Amount] - */ - MTE_CONTROLLER UMETA(DisplayName = "Controller"), - /* - * Program(Instrument) Change Event - * [Data1=Number, Data2=0] - */ - MTE_PROGRAM_CHANGE UMETA(DisplayName = "Program Change"), - /* - * Channel Aftertouch Event - * [Data1=Amount, Data2=0] - */ - MTE_CHANNEL_AFTERTOUCH UMETA(DisplayName = "Channel Aftertouch"), - /* - * Pitch Bend Event - * [Data1=LowBit, Data2=HighBit] - */ - MTE_PITCH_BEND UMETA(DisplayName = "Pitch Bend") -}; -UENUM(BlueprintType) -enum class EMidiClockTypeEnum : uint8 -{ - /* - * Quarter Frame (MTC) - * [Data=] - */ - MCTE_QUARTER_FRAME = 1 UMETA(DisplayName = "Quarter Frame (MTC)"), - /* - * Song Position Pointer - * [Data=Position] - */ - MCTE_SONG_POSITION = 2 UMETA(DisplayName = "Song position pointer"), - /* - * Song Select - * [Data=SongNumber] - */ - MCTE_SONG_SELECT UMETA(DisplayName = "Song Select"), - /* - * Tune Request - * [Data1=] - */ - MCTE_TUNE_REQUEST = 8 UMETA(DisplayName = "Tune Request"), - /* - * Clock - * [Data=] - */ - MCTE_CLOCK = 10 UMETA(DisplayName = "Timing Clock"), - /* - * Start - * [Data=] - */ - MCTE_START = 11 UMETA(DisplayName = "Start"), - /* - * Continue - * [Data1=] - */ - MCTE_CONTINUE UMETA(DisplayName = "Continue"), - /* - * Stop - * [Data=] - */ - MCTE_STOP = 13 UMETA(DisplayName = "Stop"), - /* - * Active Sensing - * [Data=] - */ - MCTE_ACTIVE UMETA(DisplayName = "Active Sensing"), - /* - * Reset - * [Data=] - */ - MCTE_RESET UMETA(DisplayName = "Reset"), -}; -USTRUCT(BlueprintType) -struct FMidiEvent -{ - GENERATED_BODY() - - /* The Type of Event this struct represents */ - UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "MIDI|Event") - EMidiTypeEnum Type; - - /* The Channel that the Event is on (0-15) */ - UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "MIDI|Event") - uint8 Channel; - - /* The first data value (ex. Note) */ - UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "MIDI|Event") - uint8 Data1; - - /* The second data value (ex, Velocity) */ - UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "MIDI|Event") - uint8 Data2; - - /* Constructor */ - FMidiEvent() - { - Type = EMidiTypeEnum::MTE_NOTE; - Channel = 0; - Data1 = 0; - Data2 = 0; - } -}; - - -USTRUCT(BlueprintType) -struct FMidiClockEvent -{ - GENERATED_BODY() - - /* The Type of Event this struct represents */ - UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "MIDI|Event") - EMidiClockTypeEnum Type; - - /* The first data value */ - UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "MIDI|Event") - int32 Data; - - /* Constructor */ - FMidiClockEvent() - { - Type = EMidiClockTypeEnum::MCTE_SONG_POSITION; - Data = 0; - } -}; - - /** * MIDI Frequency Conversion Utility diff --git a/Procedural-Midi/MidiAsset/Source/MidiAssetEditor/MidiAssetEditor.Build.cs b/Procedural-Midi/MidiAsset/Source/MidiAssetEditor/MidiAssetEditor.Build.cs index 083f10e..ba8f589 100644 --- a/Procedural-Midi/MidiAsset/Source/MidiAssetEditor/MidiAssetEditor.Build.cs +++ b/Procedural-Midi/MidiAsset/Source/MidiAssetEditor/MidiAssetEditor.Build.cs @@ -16,35 +16,35 @@ public MidiAssetEditor(TargetInfo Target) new string[] { "AssetTools", "MainFrame", - } + } ); PrivateIncludePaths.AddRange( new string[] { - } + } ); PublicDependencyModuleNames.AddRange( new string[] { - "Core", - "CoreUObject", + "Core", + "CoreUObject", } ); PrivateDependencyModuleNames.AddRange( new string[] { - "ContentBrowser", - "Core", - "CoreUObject", + "ContentBrowser", + "Core", + "CoreUObject", "DesktopWidgets", - "EditorStyle", - "Engine", - "InputCore", + "EditorStyle", + "Engine", + "InputCore", "Projects", - "Slate", - "SlateCore", + "Slate", + "SlateCore", "MidiAsset", - "UnrealEd", + "UnrealEd", } ); @@ -52,7 +52,7 @@ public MidiAssetEditor(TargetInfo Target) new string[] { "AssetTools", "UnrealEd", - } + } ); } } diff --git a/Procedural-Midi/MidiAsset/Source/MidiAssetEditor/Private/Factories/MidiAssetFactory.cpp b/Procedural-Midi/MidiAsset/Source/MidiAssetEditor/Private/Factories/MidiAssetFactory.cpp index dc45a09..8ee2727 100644 --- a/Procedural-Midi/MidiAsset/Source/MidiAssetEditor/Private/Factories/MidiAssetFactory.cpp +++ b/Procedural-Midi/MidiAsset/Source/MidiAssetEditor/Private/Factories/MidiAssetFactory.cpp @@ -11,7 +11,8 @@ UMidiAssetFactory::UMidiAssetFactory( const FObjectInitializer& ObjectInitializer ) : Super(ObjectInitializer) { - Formats.Add(FString(TEXT("mid;")) + NSLOCTEXT("UMidiAssetFactory", "FormatTxt", "Midi File").ToString()); + Formats.Add(FString(TEXT("mid;")) + NSLOCTEXT("UMidiAssetFactory", "ParseMIDI", "MIDI File").ToString()); + Formats.Add(FString(TEXT("midi;")) + NSLOCTEXT("UMidiAssetFactory", "ParseMIDI", "MIDI File").ToString()); SupportedClass = UMidiAsset::StaticClass(); bCreateNew = false; bEditorImport = true; @@ -28,7 +29,6 @@ UObject* UMidiAssetFactory::FactoryCreateBinary(UClass* Class, UObject* InParent if (FFileHelper::LoadFileToArray(data, *CurrentFilename)) { - MidiAsset = NewObject(InParent, Class, Name, Flags); MidiAsset->Data = data; } diff --git a/Procedural-Midi/MidiAsset/Source/MidiInterface/Classes/RtMidi.cpp b/Procedural-Midi/MidiAsset/Source/MidiInterface/Classes/RtMidi.cpp index 40f46d3..9af8d44 100644 --- a/Procedural-Midi/MidiAsset/Source/MidiInterface/Classes/RtMidi.cpp +++ b/Procedural-Midi/MidiAsset/Source/MidiInterface/Classes/RtMidi.cpp @@ -39,8 +39,6 @@ #include "RtMidi.h" #include -#include "Engine/Engine.h" - #if defined(__MACOSX_CORE__) /* #if TARGET_OS_IPHONE #define AudioGetCurrentHostTime CAHostTimeBase::GetCurrentTime diff --git a/Procedural-Midi/MidiAsset/Source/MidiInterface/Private/MidiInterfaceComponent.cpp b/Procedural-Midi/MidiAsset/Source/MidiInterface/Private/MidiInterfaceComponent.cpp index 922449e..de784e5 100644 --- a/Procedural-Midi/MidiAsset/Source/MidiInterface/Private/MidiInterfaceComponent.cpp +++ b/Procedural-Midi/MidiAsset/Source/MidiInterface/Private/MidiInterfaceComponent.cpp @@ -5,7 +5,6 @@ #include - void mycallback(double deltatime, std::vector< unsigned char > *message, void *userData) { UMidiInterfaceComponent* component = (UMidiInterfaceComponent*)userData; @@ -26,7 +25,6 @@ UMidiInterfaceComponent::UMidiInterfaceComponent() PrimaryComponentTick.bCanEverTick = queueCallbacks; inSysEx = false; - } // Called when the game starts or when spawned @@ -123,7 +121,7 @@ bool UMidiInterfaceComponent::OpenInput(uint8 port) { // Check available ports. unsigned int nPorts = midiIn.getPortCount(); - if (nPorts == 0 || port >= nPorts) { + if (nPorts < 1 || port >= nPorts) { UE_LOG(LogTemp, Display, TEXT("No ports available!")); return false; } @@ -143,14 +141,14 @@ bool UMidiInterfaceComponent::OpenInput(uint8 port) // Don't ignore sysex, timing, or active sensing messages. midiIn.ignoreTypes(false, false, true); - return true; + return midiIn.isPortOpen(); } bool UMidiInterfaceComponent::OpenOutput(uint8 port) { // Check available ports. unsigned int nPorts = midiOut.getPortCount(); - if (nPorts == 0 || port >= nPorts) { + if (nPorts < 1 || port >= nPorts) { UE_LOG(LogTemp, Display, TEXT("No ports available!")); return false; } @@ -162,7 +160,7 @@ bool UMidiInterfaceComponent::OpenOutput(uint8 port) midiOut.openPort(port); - return true; + return midiOut.isPortOpen(); } void UMidiInterfaceComponent::CloseInput() @@ -177,26 +175,19 @@ void UMidiInterfaceComponent::CloseOutput() void UMidiInterfaceComponent::Send(const FMidiEvent& Event) { - std::vector msg; uint8 status = ((uint8)Event.Type << 4) | Event.Channel; - msg.push_back(status); - msg.push_back(Event.Data1); + uint8 data[3] = { status, Event.Data1, Event.Data2 }; + // check for program change or CHANNEL_AFTERTOUCH if (Event.Type != EMidiTypeEnum::MTE_PROGRAM_CHANGE && Event.Type != EMidiTypeEnum::MTE_CHANNEL_AFTERTOUCH) { - msg.push_back(Event.Data2); + midiOut.sendMessage(&data[0], 3); } - midiOut.sendMessage(&msg); + else + midiOut.sendMessage(&data[0], 2); } -void UMidiInterfaceComponent::SendRaw(const TArray& Data) +void UMidiInterfaceComponent::SendRaw(const TArray& Data) { - std::vector msg; - - for (int i = 0; i < Data.Num(); i++) - { - msg.push_back(Data[i]); - } - - midiOut.sendMessage(&msg); + midiOut.sendMessage(Data.GetData(), Data.Num()); } void UMidiInterfaceComponent::startSysEx() { diff --git a/Procedural-Midi/MidiAsset/Source/MidiInterface/Private/MidiInterfaceLibrary.cpp b/Procedural-Midi/MidiAsset/Source/MidiInterface/Private/MidiInterfaceLibrary.cpp index e375f02..5a1770f 100644 --- a/Procedural-Midi/MidiAsset/Source/MidiInterface/Private/MidiInterfaceLibrary.cpp +++ b/Procedural-Midi/MidiAsset/Source/MidiInterface/Private/MidiInterfaceLibrary.cpp @@ -19,7 +19,7 @@ bool UMidiInterface::OpenMidiInput(uint8 port) { // Check available ports. unsigned int nPorts = midiIn.getPortCount(); - if (nPorts == 0 || port >= nPorts) { + if (nPorts < 1 || port >= nPorts) { UE_LOG(LogTemp, Display, TEXT("No ports available!")); return false; } @@ -39,14 +39,14 @@ bool UMidiInterface::OpenMidiInput(uint8 port) // Don't ignore sysex, timing, or active sensing messages. // midiIn.ignoreTypes(false, false, false); - return true; + return midiIn.isPortOpen(); } bool UMidiInterface::OpenMidiOutput(uint8 port) { // Check available ports. unsigned int nPorts = midiOut.getPortCount(); - if (nPorts == 0 || port >= nPorts) { + if (nPorts < 1 || port >= nPorts) { UE_LOG(LogTemp, Display, TEXT("No ports available!")); return false; } @@ -58,7 +58,7 @@ bool UMidiInterface::OpenMidiOutput(uint8 port) midiOut.openPort(port); - return true; + return midiOut.isPortOpen(); } @@ -79,15 +79,20 @@ bool UMidiInterface::isOutputOpen() void UMidiInterface::SendMidiEvent(const FMidiEvent& Event) { - std::vector msg; uint8 status = ((uint8)Event.Type << 4) | Event.Channel; - msg.push_back(status); - msg.push_back(Event.Data1); + uint8 data[3] = { status, Event.Data1, Event.Data2 }; + // check for program change or CHANNEL_AFTERTOUCH if (Event.Type != EMidiTypeEnum::MTE_PROGRAM_CHANGE && Event.Type != EMidiTypeEnum::MTE_CHANNEL_AFTERTOUCH) { - msg.push_back(Event.Data2); + midiOut.sendMessage(&data[0], 3); } - midiOut.sendMessage(&msg); + else + midiOut.sendMessage(&data[0], 2); +} + +void UMidiInterface::SendMidiRaw(const TArray& Data) +{ + midiOut.sendMessage(Data.GetData(), Data.Num()); } void UMidiInterface::GetMidiDevices(TArray& Input, TArray& Output) { diff --git a/Procedural-Midi/MidiAsset/Source/MidiInterface/Private/MidiInterfacePrivatePCH.h b/Procedural-Midi/MidiAsset/Source/MidiInterface/Private/MidiInterfacePrivatePCH.h index c53947b..dd97dec 100644 --- a/Procedural-Midi/MidiAsset/Source/MidiInterface/Private/MidiInterfacePrivatePCH.h +++ b/Procedural-Midi/MidiAsset/Source/MidiInterface/Private/MidiInterfacePrivatePCH.h @@ -1,6 +1,5 @@ // Copyright -> Scott Bishel -#include "CoreMinimal.h" #include "MidiInterface.h" // You should place include statements to your module's private header files here. You only need to diff --git a/Procedural-Midi/MidiAsset/Source/MidiInterface/Public/MidiInterfaceComponent.h b/Procedural-Midi/MidiAsset/Source/MidiInterface/Public/MidiInterfaceComponent.h index 7c6c1a0..aeb765d 100644 --- a/Procedural-Midi/MidiAsset/Source/MidiInterface/Public/MidiInterfaceComponent.h +++ b/Procedural-Midi/MidiAsset/Source/MidiInterface/Public/MidiInterfaceComponent.h @@ -3,7 +3,7 @@ #pragma once #include "RtMidi.h" -#include "MidiUtils.h" +#include "MidiStruct.h" #include "Containers/Queue.h" #include "GameFramework/Actor.h" @@ -70,16 +70,16 @@ class MIDIINTERFACE_API UMidiInterfaceComponent : public UActorComponent void SendRaw(const TArray& Data); - // Called when a device sends a Midi Event to the computer + // Called when a device sends a MIDI Event to the computer UPROPERTY(BlueprintAssignable, Category = "MIDI|Interface") FEventReceive OnReceiveEvent; - // Called when a device sends a Midi SysEx Event to the computer + // Called when a device sends a MIDI SysEx Event to the computer UPROPERTY(BlueprintAssignable, Category = "MIDI|Interface") FSysExEventReceive OnReceiveSysExEvent; - // Called when a device sends a Midi clock Event to the computer + // Called when a device sends a MIDI clock Event to the computer UPROPERTY(BlueprintAssignable, Category = "MIDI|Interface") FClockEventReceive OnReceiveClockEvent; @@ -95,7 +95,7 @@ class MIDIINTERFACE_API UMidiInterfaceComponent : public UActorComponent std::vector< unsigned char > message; }; TArray sysExArray; - TQueue messageQueue; + TQueue messageQueue; void setInSysEx(bool isInSysEx) { inSysEx = isInSysEx; } diff --git a/Procedural-Midi/MidiAsset/Source/MidiInterface/Public/MidiInterfaceLibrary.h b/Procedural-Midi/MidiAsset/Source/MidiInterface/Public/MidiInterfaceLibrary.h index 5f62301..03368bc 100644 --- a/Procedural-Midi/MidiAsset/Source/MidiInterface/Public/MidiInterfaceLibrary.h +++ b/Procedural-Midi/MidiAsset/Source/MidiInterface/Public/MidiInterfaceLibrary.h @@ -3,7 +3,7 @@ #pragma once #include "RtMidi.h" -#include "MidiUtils.h" +#include "MidiStruct.h" #include "Kismet/BlueprintFunctionLibrary.h" #include "MidiInterfaceLibrary.generated.h" @@ -19,10 +19,10 @@ struct FMidiDevice { GENERATED_BODY() - UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "MIDI|Device") - FString Name; UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "MIDI|Device") - uint8 Port; + FString Name; + UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "MIDI|Device") + uint8 Port; //Constructor FMidiDevice() @@ -62,12 +62,16 @@ class MIDIINTERFACE_API UMidiInterface : public UBlueprintFunctionLibrary UFUNCTION(BlueprintCallable, Category = "MIDI|Interface") static void CloseMidiOutput(); - UFUNCTION(BlueprintCallable, BlueprintPure, Category = "MIDI|Interface", meta=(DisplayName="IsMidiOutputOpen")) + UFUNCTION(BlueprintCallable, BlueprintPure, Category = "MIDI|Interface", meta=(DisplayName="Is Midi Output Open")) static bool isOutputOpen(); - // Sends a Midi Event to a Device + // Sends a MIDI Event to a Device UFUNCTION(BlueprintCallable, Category = "MIDI|Interface") static void SendMidiEvent(const FMidiEvent& Event); + + // Sends Raw MIDI data to a MIDI output device + UFUNCTION(BlueprintCallable, Category = "MIDI|Interface") + static void SendMidiRaw(const TArray& Data); // Gets all the Input and Output devices UFUNCTION(BlueprintCallable, Category = "MIDI|Interface")