Improve audio and midi support.

This commit is contained in:
jmsgrogan 2021-05-23 21:02:38 +01:00
parent 9bcc0ae88e
commit 8b5f485d1e
47 changed files with 1446 additions and 634 deletions

View file

@ -9,9 +9,69 @@ MetaMidiEvent::MetaMidiEvent()
}
std::shared_ptr<MetaMidiEvent> MetaMidiEvent::Create()
std::unique_ptr<MetaMidiEvent> MetaMidiEvent::Create()
{
return std::make_shared<MetaMidiEvent>();
return std::make_unique<MetaMidiEvent>();
}
void MetaMidiEvent::SetType(char c)
{
switch (c)
{
case META_SEQ_NUM:
mType = Type::SEQ_NUM;
break;
case META_TEXT:
mType = Type::TEXT;
break;
case META_COPYRIGHT:
mType = Type::COPYRIGHT;
break;
case META_TRACK_NAME:
mType = Type::TRACK_NAME;
break;
case META_INSTRUMENT_NAME:
mType = Type::INSTRUMENT_NAME;
break;
case META_LYRIC:
mType = Type::LYRIC;
break;
case META_MARKER:
mType = Type::MARKER;
break;
case META_CUE_POINT:
mType = Type::CUE_POINT;
break;
case META_CHANNEL_PREFIX:
mType = Type::CHANNEL_PREFIX;
break;
case META_END_TRACK:
mType = Type::END_TRACK;
break;
case META_SET_TEMPO:
mType = Type::SET_TEMPO;
break;
case META_SMPTE_OFFSET:
mType = Type::SMPTE_OFFSET;
break;
case META_TIME_SIG:
mType = Type::TIME_SIG;
break;
case META_KEY_SIG:
mType = Type::KEY_SIG;
break;
case META_SEQ_CUSTOM:
mType = Type::SEQ_CUSTOM;
break;
default:
mType = Type::UNKNOWN;
mUnKnownMarker = c;
}
}
MetaMidiEvent::Type MetaMidiEvent::GetType() const
{
return mType;
}
void MetaMidiEvent::SetValue(int value)
@ -19,37 +79,22 @@ void MetaMidiEvent::SetValue(int value)
mValue = value;
}
bool MetaMidiEvent::IsTrackName(char c)
{
return (c & META_TRACK_NAME) == META_TRACK_NAME;
}
bool MetaMidiEvent::IsSetTempo(char c)
{
return (c & META_SET_TEMPO) == META_SET_TEMPO;
}
bool MetaMidiEvent::IsTimeSignature(char c)
{
return (c & META_TIME_SIG) == META_TIME_SIG;
}
void MetaMidiEvent::SetIsSetTempo()
{
mType = Type::SET_TEMPO;
}
void MetaMidiEvent::SetIsTrackName()
{
mType = Type::TRACK_NAME;
}
void MetaMidiEvent::SetIsTimeSignature()
{
mType = Type::TIME_SIGNATURE;
}
void MetaMidiEvent::SetLabel(const std::string& label)
{
mLabel = label;
}
void MetaMidiEvent::SetTimeSignature(const MidiTimeSignature& timeSig)
{
mTimeSig = timeSig;
}
void MetaMidiEvent::SetTimeCode(const MidiSmtpeTimecode& timeCode)
{
mTimecode = timeCode;
}
void MetaMidiEvent::SetKeySignature(const MidiKeySignature& keySig)
{
mKeySig = keySig;
}

View file

@ -1,6 +1,7 @@
#pragma once
#include "MidiEvent.h"
#include "MidiElements.h"
#include <memory>
#include <string>
@ -10,40 +11,65 @@ public:
enum class Type
{
UNSET,
UNKNOWN,
SEQ_NUM,
TEXT,
COPYRIGHT,
TRACK_NAME,
INSTRUMENT_NAME,
LYRIC,
MARKER,
CUE_POINT,
CHANNEL_PREFIX,
END_TRACK,
SET_TEMPO,
TIME_SIGNATURE
SMPTE_OFFSET,
TIME_SIG,
KEY_SIG,
SEQ_CUSTOM
};
private:
static const int META_SEQ_NUM = 0x0;
static const int META_TEXT = 0x1;
static const int META_COPYRIGHT = 0x2;
static const int META_TRACK_NAME = 0x3; // 0000 0011
static const int META_INSTRUMENT_NAME = 0x4;
static const int META_LYRIC = 0x5;
static const int META_MARKER = 0x6;
static const int META_CUE_POINT = 0x7;
static const int META_CHANNEL_PREFIX = 0x20;
static const int META_END_TRACK = 0x2F;
static const int META_SET_TEMPO = 0x51; // 0101 0001
static const int META_TRACK_NAME = 0x3; // 0000 0011
static const int META_SMPTE_OFFSET = 0x54;
static const int META_TIME_SIG = 0x58; // 0101 1000
Type mType;
std::string mLabel;
int mValue;
static const int META_KEY_SIG = 0x59; // 0101 1001
static const int META_SEQ_CUSTOM = 0x7F;
public:
MetaMidiEvent();
static std::shared_ptr<MetaMidiEvent> Create();
static std::unique_ptr<MetaMidiEvent> Create();
void SetValue(int value);
void SetIsTrackName();
void SetIsSetTempo();
void SetIsTimeSignature();
void SetType(char c);
Type GetType() const;
void SetLabel(const std::string& label);
void SetTimeSignature(const MidiTimeSignature& timeSig);
void SetTimeCode(const MidiSmtpeTimecode& timeCode);
void SetKeySignature(const MidiKeySignature& keySig);
static bool IsTrackName(char c);
static bool IsSetTempo(char c);
static bool IsTimeSignature(char c);
private:
Type mType {Type::UNSET};
char mUnKnownMarker{0};
std::string mLabel;
int mValue { 0 };
MidiTimeSignature mTimeSig;
MidiSmtpeTimecode mTimecode;
MidiKeySignature mKeySig;
};
using MetaMidiEventPtr = std::shared_ptr<MetaMidiEvent>;
using MetaMidiEventPtr = std::unique_ptr<MetaMidiEvent>;

View file

@ -9,16 +9,16 @@ MidiChannelEvent::MidiChannelEvent()
}
std::shared_ptr<MidiChannelEvent> MidiChannelEvent::Create()
std::unique_ptr<MidiChannelEvent> MidiChannelEvent::Create()
{
return std::make_shared<MidiChannelEvent>();
return std::make_unique<MidiChannelEvent>();
}
void MidiChannelEvent::SetType(Type type)
{
mType = type;
}
MidiChannelEvent::Type MidiChannelEvent::GetType()
MidiChannelEvent::Type MidiChannelEvent::GetType() const
{
return mType;
}
@ -27,54 +27,88 @@ void MidiChannelEvent::SetValues(int value0, int value1)
mValue0 = value0;
mValue1 = value1;
}
int MidiChannelEvent::GetValue0()
int MidiChannelEvent::GetValue0() const
{
return mValue0;
}
int MidiChannelEvent::GetValue1()
int MidiChannelEvent::GetValue1() const
{
return mValue1;
}
bool MidiChannelEvent::IsControllerEvent(int c)
void MidiChannelEvent::SetTypeAndChannel(char c)
{
return (c & CONTROLLER) == CONTROLLER;
}
const int first_four_bits = 0xF0;
const int second_four_bits = 0xF;
const int event_type = (c & first_four_bits) >> 4;
bool MidiChannelEvent::IsNoteOnEvent(int c)
{
return (c & NOTE_ON) == NOTE_ON;
}
bool MidiChannelEvent::IsNoteOffEvent(int c)
{
return (c & NOTE_OFF) == NOTE_OFF;
}
bool MidiChannelEvent::IsNoteAfterTouchEvent(int c)
{
return (c & NOTE_AFTERTOUCH) == NOTE_AFTERTOUCH;
}
bool MidiChannelEvent::IsProgramEvent(int c)
{
return (c & PROGRAM) == PROGRAM;
}
bool MidiChannelEvent::IsChannelAftertouchEvent(int c)
{
return (c & CHANNEL_AFTERTOUCH) == CHANNEL_AFTERTOUCH;
}
bool MidiChannelEvent::IsPitchbendEvent(int c)
{
return (c & PITCH_BEND) == PITCH_BEND;
}
bool MidiChannelEvent::IsStatusByte(int c)
{
return IsControllerEvent(c) || IsNoteOnEvent(c) ||
IsNoteOffEvent(c) || IsNoteAfterTouchEvent(c) ||
IsProgramEvent(c) || IsChannelAftertouchEvent(c) ||
IsPitchbendEvent(c);
const bool isSystemMessage = event_type == 0xF;
if(!isSystemMessage)
{
mChannel = (c & second_four_bits) >> 4;
switch(event_type)
{
case NOTE_OFF:
mType = Type::NOTE_OFF;
break;
case NOTE_ON:
mType = Type::NOTE_ON;
break;
case NOTE_AFTERTOUCH:
mType = Type::NOTE_AFTERTOUCH;
break;
case CONTROLLER:
mType = Type::CONTROLLER;
break;
case PROGRAM:
mType = Type::PROGRAM;
break;
case CHANNEL_AFTERTOUCH:
mType = Type::CHANNEL_AFTERTOUCH;
break;
case PITCH_BEND:
mType = Type::PITCH_BEND;
break;
}
}
else
{
const int subType = (c & second_four_bits) >> 4;
switch(subType)
{
case SYS_EXCLUSIVE:
mType = Type::SYS_EXCLUSIVE;
break;
case SYS_SONG_POS:
mType = Type::SYS_SONG_POS;
break;
case SYS_SONG_SELECT:
mType = Type::SYS_SONG_SELECT;
break;
case SYS_TUNE_REQUEST:
mType = Type::SYS_TUNE_REQUEST;
break;
case SYS_END_EXCLUSIVE:
mType = Type::SYS_END_EXCLUSIVE;
break;
case SYS_TIMING_CLOCK:
mType = Type::SYS_TIMING_CLOCK;
break;
case SYS_START:
mType = Type::SYS_START;
break;
case SYS_CONTINUE:
mType = Type::SYS_CONTINUE;
break;
case SYS_STOP:
mType = Type::SYS_STOP;
break;
case SYS_ACTIVE_SENSING:
mType = Type::SYS_ACTIVE_SENSING;
break;
case SYS_RESET:
mType = Type::SYS_RESET;
break;
}
}
}

View file

@ -1,13 +1,32 @@
#pragma once
#include "MidiEvent.h"
#include <string>
class MidiChannelEvent : public MidiEvent
{
public:
enum class Type{
NONE, NOTE_OFF, NOTE_ON, NOTE_AFTERTOUCH, CONTROLLER,
PROGRAM, CHANNEL_AFTERTOUCH, PITCH_BEND
NONE,
NOTE_OFF,
NOTE_ON,
NOTE_AFTERTOUCH,
CONTROLLER,
PROGRAM,
CHANNEL_AFTERTOUCH,
PITCH_BEND,
CHANNEL_MODE,
SYS_EXCLUSIVE,
SYS_SONG_POS,
SYS_SONG_SELECT,
SYS_TUNE_REQUEST,
SYS_END_EXCLUSIVE,
SYS_TIMING_CLOCK,
SYS_START,
SYS_CONTINUE,
SYS_STOP,
SYS_ACTIVE_SENSING,
SYS_RESET
};
private:
@ -18,35 +37,36 @@ private:
static const int PROGRAM = 0xC;
static const int CHANNEL_AFTERTOUCH = 0xD;
static const int PITCH_BEND = 0xE;
Type mType;
int mValue0;
int mValue1;
static const int SYS_EXCLUSIVE = 0x0;
static const int SYS_SONG_POS = 0x2;
static const int SYS_SONG_SELECT = 0x3;
static const int SYS_TUNE_REQUEST = 0x6;
static const int SYS_END_EXCLUSIVE = 0x7;
static const int SYS_TIMING_CLOCK = 0x8;
static const int SYS_START = 0xA;
static const int SYS_CONTINUE = 0xB;
static const int SYS_STOP = 0xC;
static const int SYS_ACTIVE_SENSING = 0xE;
static const int SYS_RESET = 0xF;
public:
MidiChannelEvent();
static std::shared_ptr<MidiChannelEvent> Create();
static std::unique_ptr<MidiChannelEvent> Create();
void SetTypeAndChannel(char c);
void SetType(Type type);
Type GetType();
Type GetType() const;
void SetValues(int value0, int value1);
int GetValue0();
int GetValue1();
int GetValue0() const;
int GetValue1() const;
static bool IsControllerEvent(int c);
static bool IsNoteOnEvent(int c);
static bool IsNoteOffEvent(int c);
static bool IsNoteAfterTouchEvent(int c);
static bool IsProgramEvent(int c);
static bool IsChannelAftertouchEvent(int c);
static bool IsPitchbendEvent(int c);
static bool IsStatusByte(int c);
private:
Type mType;
int mChannel {0};
int mValue0 {0};
int mValue1 {1};
};
using MidiChannelEventPtr = std::shared_ptr<MidiChannelEvent>;
using MidiChannelEventPtr = std::unique_ptr<MidiChannelEvent>;

View file

@ -1,6 +1,50 @@
#include "MidiDocument.h"
#include "MidiTrack.h"
MidiDocument::MidiDocument()
{
}
std::unique_ptr<MidiDocument> MidiDocument::Create()
{
return std::make_unique<MidiDocument>();
}
void MidiDocument::AddTrack(MidiTrackPtr track)
{
mTracks.push_back(std::move(track));
}
void MidiDocument::SetFormatType(int format)
{
mFormatType = format;
}
void MidiDocument::SetExpectedTracks(int expected)
{
mExpectedTracks = expected;
}
void MidiDocument::SetTimeDivision(const MidiTimeDivision& timeDivision)
{
mTimeDivision = timeDivision;
}
std::string MidiDocument::Serialize() const
{
std::string output = "MidiDocument\n";
output += "Format type: " + std::to_string(mFormatType) + "\n";
output += "Expected Tracks: " + std::to_string(mExpectedTracks) + "\n";
output += "Use fps: " + std::to_string(mTimeDivision.mUseFps) + "\n";
output += "fps: " + std::to_string(mTimeDivision.mFps) + "\n";
output += "Ticks per frame: " + std::to_string(mTimeDivision.mTicks) + "\n";
output += "Num tracks: " + std::to_string(mTracks.size()) + "\n";
for(const auto& track : mTracks)
{
output += track->Serialize();
output += "------------------------\n";
}
return output;
}

View file

@ -1,16 +1,34 @@
#pragma once
#include <vector>
#include "MidiTrack.h"
#include <memory>
#include <string>
#include "MidiElements.h"
class MidiTrack;
using MidiTrackPtr = std::unique_ptr<MidiTrack>;
class MidiDocument
{
public:
int mFormatType = 0;
int mExpectedTracks = 0;
bool mUseFps = true;
int mFps = 0;
int ticksPerFrame = 0;
std::vector<MidiTrack> mTracks;
MidiDocument();
MidiDocument();
static std::unique_ptr<MidiDocument> Create();
void AddTrack(MidiTrackPtr track);
void SetFormatType(int format);
void SetExpectedTracks(int expected);
void SetTimeDivision(const MidiTimeDivision& timeDivision);
std::string Serialize() const;
private:
int mFormatType = 0;
int mExpectedTracks = 0;
MidiTimeDivision mTimeDivision;
std::vector<MidiTrackPtr> mTracks;
};
using MidiDocumentPtr = std::unique_ptr<MidiDocument>;

View file

@ -0,0 +1,31 @@
#pragma once
struct MidiTimeSignature
{
int mNumer {4};
int mDenom {4};
int mMetro {1};
int mF32 {1};
};
struct MidiTimeDivision
{
bool mUseFps {false};
int mFps { 0 };
int mTicks { 1 };
};
struct MidiSmtpeTimecode
{
int mHr{0};
int mMin{0};
int mSec{0};
int mFrame{0};
int mFrameFrac{0};
};
struct MidiKeySignature
{
int mSharpsFlats{0};
int mMinor{0};
};

View file

@ -6,16 +6,16 @@ MidiEvent::MidiEvent()
}
std::unique_ptr<MidiEvent> MidiEvent::Create()
{
return std::make_unique<MidiEvent>();
}
void MidiEvent::SetTimeDelta(int delta)
{
mTimeDelta = delta;
}
std::shared_ptr<MidiEvent> MidiEvent::Create()
{
return std::make_shared<MidiEvent>();
}
bool MidiEvent::IsMetaEvent(char c)
{
return (c & META_EVENT) == META_EVENT;

View file

@ -7,20 +7,23 @@ class MidiEvent
static const int META_EVENT = 0xFF; // 1111 1111
static const int SYSEX_EVENT = 0xF0;
static const int DIVIDED_SYSEX_EVENT = 0xF7;
int mTimeDelta;
public:
MidiEvent();
virtual ~MidiEvent() = default;
static std::shared_ptr<MidiEvent> Create();
static std::unique_ptr<MidiEvent> Create();
void SetTimeDelta(int delta);
static bool IsMetaEvent(char c);
static bool IsSysExEvent(char c);
static bool IsNormalSysExEvent(char c);
static bool IsDividedSysExEvent(char c);
private:
int mTimeDelta{0};
};
using MidiEventPtr = std::shared_ptr<MidiEvent>;
using MidiEventPtr = std::unique_ptr<MidiEvent>;

View file

@ -1,379 +0,0 @@
#include "MidiReader.h"
#include "MidiDocument.h"
#include "ByteUtils.h"
#include "MidiTrack.h"
#include "BinaryFile.h"
#include <fstream>
#include <iostream>
#include <cstring>
#include <bitset>
MidiReader::MidiReader()
:mDocument(),
mTrackByteCount(0),
mLastChannelEventType(MidiChannelEvent::Type::NONE)
{
}
bool MidiReader::ProcessHeader(std::ifstream& file)
{
if(!BinaryFile::CheckNextDWord(file, HeaderLabel))
{
return false;
}
int length = 0;
if(!BinaryFile::GetNextDWord(file, length))
{
return false;
}
if(!BinaryFile::GetNextWord(file, mDocument.mFormatType))
{
return false;
}
if(!BinaryFile::GetNextWord(file, mDocument.mExpectedTracks))
{
return false;
}
ProcessTimeDivision(file);
return true;
}
bool MidiReader::ProcessTimeDivision(std::ifstream& file)
{
int time_division;
if(!BinaryFile::GetNextWord(file, time_division, false))
{
return false;
}
const int TOP_7_BITS = 0x7F00; // 0111 1111 - 0000 0000
mDocument.mUseFps = ByteUtils::GetWordFirstBit(time_division);
mDocument.mFps = ((~time_division & TOP_7_BITS) >> 8) - 1; // Reverse 2complement of next 7 bits
mDocument.ticksPerFrame = ByteUtils::GetWordLastByte(time_division);
return true;
}
int MidiReader::ProcessTimeDelta(std::ifstream& file)
{
char c;
file.get(c);
mTrackByteCount ++;
if(unsigned(c >> 7) == 0)
{
int delta = int(c);
std::cout << "Time delta final: " << delta << "|" << std::bitset<16>(c)<< std::endl;
return delta;
}
int working_c = c;
int final_c = 0;
unsigned count = 0;
std::cout << "Working " << std::bitset<8>(working_c) << std::endl;
while(unsigned(working_c >> 7) != 0)
{
char corrected = (working_c &= ~(1UL << 7));
final_c <<= 7;
final_c |= (corrected << 7*count);
char file_c;
file.get(file_c);
mTrackByteCount ++;
working_c = int(file_c);
std::cout << "Working " << std::bitset<8>(working_c) << std::endl;
count ++;
}
std::cout << "Time delta start: " << std::bitset<16>(final_c) << std::endl;
final_c <<= 7;
std::cout << "Time delta pre: " << std::bitset<16>(final_c) << std::endl;
final_c |= (working_c << 7*(count-1));
int delta = int(final_c);
std::cout << "Time delta final: " << delta << "|" << std::bitset<16>(final_c)<< std::endl;
return delta;
}
bool MidiReader::ProcessTrackNameMetaEvent(std::ifstream& file, MetaMidiEvent& event)
{
event.SetIsTrackName();
int length = 0;
BinaryFile::GetNextByteAsInt(file, length);
mTrackByteCount ++;
std::string name;
BinaryFile::GetNextString(file, name, length);
mTrackByteCount += length;
std::cout << "Track name: " << name << "|" << length <<std::endl;
event.SetLabel(name);
return true;
}
bool MidiReader::ProcessSetTempoMetaEvent(std::ifstream& file, MetaMidiEvent& event)
{
event.SetIsSetTempo();
int length = 0;
BinaryFile::GetNextByteAsInt(file, length);
mTrackByteCount ++;
std::string buffer;
BinaryFile::GetNextNBytes(file, buffer.data(), length);
mTrackByteCount += length;
int tempo = ByteUtils::ToInt(buffer.data(), length);
const int MICROSECONDS_PER_MINUTE = 60000000;
std::cout << "Got tempo "<< tempo << "|" << MICROSECONDS_PER_MINUTE/tempo<< std::endl;
event.SetValue(tempo);
return true;
}
bool MidiReader::ProcessTimeSignatureMetaEvent(std::ifstream& file, MetaMidiEvent& event)
{
event.SetIsTimeSignature();
int length = 0;
BinaryFile::GetNextByteAsInt(file, length);
mTrackByteCount ++;
int numer = 0;
BinaryFile::GetNextByteAsInt(file, numer);
mTrackByteCount ++;
int denom = 0;
BinaryFile::GetNextByteAsInt(file, denom);
mTrackByteCount ++;
int metro = 0;
BinaryFile::GetNextByteAsInt(file, metro);
mTrackByteCount ++;
int f32s = 0;
BinaryFile::GetNextByteAsInt(file, f32s);
mTrackByteCount ++;
std::cout << "Time sig " << length << "|" << numer << "|" << denom << "|" << metro << "|" << f32s << std::endl;
return true;
}
bool MidiReader::ProcessMetaEvent(std::ifstream& file, MetaMidiEvent& event)
{
char c;
file.get(c);
mTrackByteCount ++;
if(MetaMidiEvent::IsTrackName(c))
{
ProcessTrackNameMetaEvent(file, event);
}
else if(MetaMidiEvent::IsSetTempo(c))
{
ProcessSetTempoMetaEvent(file, event);
}
else if(MetaMidiEvent::IsTimeSignature(c))
{
ProcessTimeSignatureMetaEvent(file, event);
}
else
{
std::cout << "something else" << std::endl;
std::cout << "Next " << std::bitset<8>(c) << "|" <<std::endl;
}
return true;
}
bool MidiReader::ProcessMidiEventData(std::ifstream& file, MidiChannelEvent& event, char c)
{
int value0 = int(c);
int value1 = 0;
BinaryFile::GetNextByteAsInt(file, value1);
mTrackByteCount ++;
event.SetValues(value0, value1);
std::cout << "sdata: " << value0 << "|" << value1 << "|" << std::endl;
return true;
}
bool MidiReader::ProcessMidiEventData(std::ifstream& file, MidiChannelEvent& event)
{
int value0 = 0;
BinaryFile::GetNextByteAsInt(file, value0);
mTrackByteCount ++;
int value1 = 0;
BinaryFile::GetNextByteAsInt(file, value1);
mTrackByteCount ++;
event.SetValues(value0, value1);
std::cout << "data: " << value0 << "|" << value1 << "|" << std::endl;
return true;
}
bool MidiReader::ProcessMidiChannelEvent(std::ifstream& file, char firstByte, MidiChannelEvent& event)
{
int first_four_bits = 0xF0;
int second_four_bits = 0xF;
int event_type = (firstByte & first_four_bits) >> 4;
int midi_channel = (firstByte & second_four_bits) >> 4;
std::cout << "Channel: " << midi_channel << std::endl;
if(MidiChannelEvent::IsStatusByte(event_type))
{
if(MidiChannelEvent::IsControllerEvent(event_type))
{
event.SetType(MidiChannelEvent::Type::CONTROLLER);
mLastChannelEventType = MidiChannelEvent::Type::CONTROLLER;
std::cout << "Controller Event"<< std::endl;
ProcessMidiEventData(file, event);
}
else if(MidiChannelEvent::IsProgramEvent(event_type))
{
event.SetType(MidiChannelEvent::Type::PROGRAM);
mLastChannelEventType = MidiChannelEvent::Type::PROGRAM;
std::cout << "Program Event"<< std::endl;
int value0 = 0;
BinaryFile::GetNextByteAsInt(file, value0);
mTrackByteCount ++;
std::cout << "value " << value0 << std::endl;
}
else if(MidiChannelEvent::IsNoteOnEvent(event_type))
{
event.SetType(MidiChannelEvent::Type::NOTE_ON);
mLastChannelEventType = MidiChannelEvent::Type::NOTE_ON;
std::cout << "Note on Event"<< std::endl;
ProcessMidiEventData(file, event);
}
else if(MidiChannelEvent::IsNoteOffEvent(event_type))
{
event.SetType(MidiChannelEvent::Type::NOTE_OFF);
mLastChannelEventType = MidiChannelEvent::Type::NOTE_OFF;
std::cout << "Note off Event"<< std::endl;
ProcessMidiEventData(file, event);
}
else
{
std::cout << "Unknown status event: " << std::bitset<8>(firstByte) << "|" << event_type <<std::endl;
}
}
else
{
if(mLastChannelEventType == MidiChannelEvent::Type::CONTROLLER)
{
std::cout << "Controller running event: " <<std::endl;
ProcessMidiEventData(file, event, firstByte);
}
else if(mLastChannelEventType == MidiChannelEvent::Type::NOTE_ON)
{
std::cout << "Note on running event: " <<std::endl;
ProcessMidiEventData(file, event, firstByte);
}
else if(mLastChannelEventType == MidiChannelEvent::Type::NOTE_OFF)
{
std::cout << "Note off running event: " <<std::endl;
ProcessMidiEventData(file, event, firstByte);
}
else
{
std::cout << "Unknown running event: " <<std::endl;
}
}
return true;
}
bool MidiReader::ProcessEvent(std::ifstream& file, MidiTrack& track)
{
char c;
int timeDelta = ProcessTimeDelta(file);
file.get(c);
std::cout << std::bitset<8>(c) << std::endl;
mTrackByteCount ++;
if(MidiEvent::IsMetaEvent(c))
{
MetaMidiEvent event;
event.SetTimeDelta(timeDelta);
std::cout << "Meta event " <<std::endl;
ProcessMetaEvent(file, event);
track.AddEvent(event);
}
else if(MidiEvent::IsSysExEvent(c))
{
std::cout << "Sysex event" << std::endl;
}
else
{ // Midi event
MidiChannelEvent event;
event.SetTimeDelta(timeDelta);
std::cout << "Midi event" << std::endl;
ProcessMidiChannelEvent(file, c, event);
}
return true;
}
bool MidiReader::ProcessTrackChunk(std::ifstream& file, bool debug)
{
if(!BinaryFile::CheckNextDWord(file, TrackChunkLabel))
{
return false;
}
int chunkSize = 0;
if(!BinaryFile::GetNextDWord(file, chunkSize))
{
return false;
}
std::cout << "Chunk size: "<< chunkSize << std::endl;
mTrackByteCount = 0;
MidiTrack track;
unsigned iter_count = 0;
while(mTrackByteCount < unsigned(chunkSize))
{
std::cout << "-------------" << std::endl;
ProcessEvent(file, track);
std::cout << "Track byte count: "<< mTrackByteCount << std::endl;
if(debug && iter_count == 25)
{
return true;
}
iter_count ++;
}
mDocument.mTracks.push_back(track);
return true;
}
void MidiReader::Read(const std::string& path)
{
std::ifstream file(path);
if(!ProcessHeader(file))
{
std::cout << "Problem processing header" << std::endl;
return;
}
int trackCount = 0;
if(!ProcessTrackChunk(file))
{
std::cout << "Problem processing track chunk" << std::endl;
return;
}
trackCount ++;
std::cout << "****************************************" << std::endl;
std::cout << "Track 2" << std::endl;
if(!ProcessTrackChunk(file, true))
{
std::cout << "Problem processing track chunk" << std::endl;
return;
}
trackCount ++;
file.close();
}

View file

@ -1,43 +0,0 @@
#pragma once
#include "MidiDocument.h"
#include "MetaMidiEvent.h"
#include "MidiChannelEvent.h"
#include <string>
class MidiReader
{
static constexpr const char TrackChunkLabel[] = "MTrk";
static constexpr const char HeaderLabel[] = "MThd";
MidiDocument mDocument;
unsigned mTrackByteCount;
MidiChannelEvent::Type mLastChannelEventType;
public:
MidiReader();
void Read(const std::string& path);
private:
bool ProcessHeader(std::ifstream& file);
bool ProcessTimeDivision(std::ifstream& file);
bool ProcessTrackChunk(std::ifstream& file, bool debug=false);
bool ProcessEvent(std::ifstream& file, MidiTrack& track);
bool ProcessMidiEventData(std::ifstream& file, MidiChannelEvent& event, char c);
bool ProcessMidiEventData(std::ifstream& file, MidiChannelEvent& event);
bool ProcessMidiChannelEvent(std::ifstream& file, char firstByte, MidiChannelEvent& event);
bool ProcessMetaEvent(std::ifstream& file, MetaMidiEvent& event);
int ProcessTimeDelta(std::ifstream& file);
bool ProcessTrackNameMetaEvent(std::ifstream& file, MetaMidiEvent& event);
bool ProcessSetTempoMetaEvent(std::ifstream& file, MetaMidiEvent& event);
bool ProcessTimeSignatureMetaEvent(std::ifstream& file, MetaMidiEvent& event);
};

View file

@ -1,11 +1,35 @@
#include "MidiTrack.h"
#include "MidiEvent.h"
MidiTrack::MidiTrack()
{
}
void MidiTrack::AddEvent(const MidiEvent& event)
MidiTrack::~MidiTrack()
{
mEvents.push_back(event);
}
void MidiTrack::AddEvent(MidiEventPtr event)
{
mEvents.push_back(std::move(event));
}
MidiEvent* MidiTrack::GetEvent(std::size_t idx) const
{
return mEvents[idx].get();
}
std::size_t MidiTrack::GetNumEvents()
{
return mEvents.size();
}
std::string MidiTrack::Serialize() const
{
std::string output = "MidiTrack\n";
output += "Num Events: " + std::to_string(mEvents.size()) + "\n";
return output;
}

View file

@ -1,15 +1,30 @@
#pragma once
#include <vector>
#include <memory>
#include <string>
#include "MidiEvent.h"
class MidiEvent;
using MidiEventPtr = std::unique_ptr<MidiEvent>;
class MidiTrack
{
std::vector<MidiEvent> mEvents;
public:
MidiTrack();
MidiTrack();
void AddEvent(const MidiEvent& event);
~MidiTrack();
void AddEvent(MidiEventPtr event);
MidiEvent* GetEvent(std::size_t idx) const;
std::size_t GetNumEvents();
std::string Serialize() const;
private:
std::vector<MidiEventPtr> mEvents;
};

View file

@ -0,0 +1,77 @@
#include "MidiChannelEventAdapter.h"
#include "BinaryFile.h"
#include "ByteUtils.h"
#include <iostream>
#include <bitset>
int MidiChannelEventAdapter::ReadEvent(std::ifstream* file, char firstByte, MidiChannelEvent* event, MidiChannelEvent::Type& lastEventType)
{
int first_four_bits = 0xF0;
int second_four_bits = 0xF;
int event_type = (firstByte & first_four_bits) >> 4;
int midi_channel = (firstByte & second_four_bits) >> 4;
unsigned byteCount = 0;
std::cout << "Channel: " << midi_channel << std::endl;
const bool isStatusByte = ByteUtils::MSBIsOne(firstByte);
if(isStatusByte)
{
event->SetTypeAndChannel(firstByte);
lastEventType = event->GetType();
}
else
{
event->SetType(lastEventType);
}
std::cout << "MC Type " << static_cast<int>(event->GetType()) << std::endl;
switch(event->GetType())
{
case MidiChannelEvent::Type::NOTE_ON:
case MidiChannelEvent::Type::NOTE_OFF:
case MidiChannelEvent::Type::CONTROLLER:
{
if (isStatusByte)
{
byteCount += ReadEventData(file, event);
}
else
{
byteCount += ReadEventData(file, event, firstByte);
}
break;
}
case MidiChannelEvent::Type::PROGRAM:
{
int value0 = 0;
BinaryFile::GetNextByteAsInt(file, value0);
byteCount ++;
break;
}
default:
std::cout << "Unknown status event: " << std::bitset<8>(firstByte) << "|" << event_type <<std::endl;
break;
}
return byteCount;
}
int MidiChannelEventAdapter::ReadEventData(std::ifstream* file, MidiChannelEvent* event, char c)
{
int value0 = int(c);
int value1 = 0;
BinaryFile::GetNextByteAsInt(file, value1);
event->SetValues(value0, value1);
return 1;
}
int MidiChannelEventAdapter::ReadEventData(std::ifstream* file, MidiChannelEvent* event)
{
int value0 = 0;
BinaryFile::GetNextByteAsInt(file, value0);
int value1 = 0;
BinaryFile::GetNextByteAsInt(file, value1);
event->SetValues(value0, value1);
return 2;
}

View file

@ -0,0 +1,14 @@
#pragma once
#include "MidiChannelEvent.h"
#include <fstream>
class MidiChannelEventAdapter
{
public:
static int ReadEvent(std::ifstream* file, char firstByte, MidiChannelEvent* event, MidiChannelEvent::Type& lastEventType);
static int ReadEventData(std::ifstream* file, MidiChannelEvent* event, char c);
static int ReadEventData(std::ifstream* file, MidiChannelEvent* event);
};

View file

@ -0,0 +1,190 @@
#include "MidiMetaEventAdapter.h"
#include "BinaryFile.h"
#include "ByteUtils.h"
#include <iostream>
#include <bitset>
int MidiMetaEventAdapter::ReadEvent(std::ifstream* file, MetaMidiEvent* event, int& lastMidiChannel)
{
unsigned byteCount = 0;
char c;
file->get(c);
byteCount++;
event->SetType(c);
std::cout << "Meta event type: " << std::hex << int(c) << std::dec<<std::endl;
switch (event->GetType())
{
case MetaMidiEvent::Type::SEQ_NUM:
byteCount += ReadIntEvent(file, event, 2);
break;
case MetaMidiEvent::Type::TEXT:
case MetaMidiEvent::Type::COPYRIGHT:
case MetaMidiEvent::Type::TRACK_NAME:
case MetaMidiEvent::Type::INSTRUMENT_NAME:
case MetaMidiEvent::Type::MARKER:
case MetaMidiEvent::Type::CUE_POINT:
byteCount += ReadStringEvent(file, event);
break;
case MetaMidiEvent::Type::CHANNEL_PREFIX:
byteCount += ReadChannelPrefixEvent(file, event, lastMidiChannel);
break;
case MetaMidiEvent::Type::END_TRACK:
{
int length = 0;
BinaryFile::GetNextByteAsInt(file, length);
byteCount ++;
break;
}
case MetaMidiEvent::Type::SMPTE_OFFSET:
byteCount += ReadTimeCodeEvent(file, event);
break;
case MetaMidiEvent::Type::SET_TEMPO:
byteCount += ReadIntEvent(file, event);
break;
case MetaMidiEvent::Type::TIME_SIG:
byteCount += ReadTimeSignatureEvent(file, event);
break;
case MetaMidiEvent::Type::KEY_SIG:
byteCount += ReadKeySignatureEvent(file, event);
break;
case MetaMidiEvent::Type::SEQ_CUSTOM:
break;
case MetaMidiEvent::Type::UNKNOWN:
std::cout << "Unknown meta event " << std::bitset<8>(c) << "|" <<std::endl;
byteCount += ReadUnknownEvent(file);
break;
default:
break;
}
return byteCount;
}
int MidiMetaEventAdapter::ReadUnknownEvent(std::ifstream* file)
{
int length = 0;
BinaryFile::GetNextByteAsInt(file, length);
char c;
for(unsigned idx=0; idx<length; idx++)
{
file->get(c);
}
return length;
}
int MidiMetaEventAdapter::ReadStringEvent(std::ifstream* file, MetaMidiEvent* event)
{
unsigned byteCount = 0;
int length = 0;
BinaryFile::GetNextByteAsInt(file, length);
byteCount++;
std::string name;
BinaryFile::GetNextString(file, name, length);
byteCount += length;
event->SetLabel(name);
return byteCount;
}
int MidiMetaEventAdapter::ReadIntEvent(std::ifstream* file, MetaMidiEvent* event, int lengthIn)
{
unsigned byteCount = 0;
int length = 0;
if(lengthIn > -1)
{
length = lengthIn;
}
else
{
BinaryFile::GetNextByteAsInt(file, length);
byteCount ++;
}
std::string buffer;
BinaryFile::GetNextNBytes(file, buffer.data(), length);
byteCount += length;
const int value = ByteUtils::ToInt(buffer.data(), length);
event->SetValue(value);
return byteCount;
}
int MidiMetaEventAdapter::ReadChannelPrefixEvent(std::ifstream* file, MetaMidiEvent* event, int& lastMidiChannel)
{
unsigned byteCount = 0;
int length = 0;
BinaryFile::GetNextByteAsInt(file, length);
byteCount ++;
std::string buffer;
BinaryFile::GetNextNBytes(file, buffer.data(), length);
byteCount += length;
const int value = ByteUtils::ToInt(buffer.data(), length);
event->SetValue(value);
lastMidiChannel = value;
return byteCount;
}
int MidiMetaEventAdapter::ReadTimeSignatureEvent(std::ifstream* file, MetaMidiEvent* event)
{
unsigned byteCount = 0;
int length = 0;
BinaryFile::GetNextByteAsInt(file, length);
byteCount++;
MidiTimeSignature timeSig;
BinaryFile::GetNextByteAsInt(file, timeSig.mNumer);
BinaryFile::GetNextByteAsInt(file, timeSig.mDenom);
BinaryFile::GetNextByteAsInt(file, timeSig.mMetro);
BinaryFile::GetNextByteAsInt(file, timeSig.mF32);
byteCount +=4;
if (length > 4)
{
char c;
for(unsigned idx=0; idx<length-4; idx++)
{
file->get(c);
byteCount++;
}
}
return byteCount;
}
int MidiMetaEventAdapter::ReadKeySignatureEvent(std::ifstream* file, MetaMidiEvent* event)
{
unsigned byteCount = 0;
int length = 0;
BinaryFile::GetNextByteAsInt(file, length);
byteCount++;
MidiKeySignature keySig;
BinaryFile::GetNextByteAsInt(file, keySig.mSharpsFlats);
BinaryFile::GetNextByteAsInt(file, keySig.mMinor);
byteCount +=2;
return byteCount;
}
int MidiMetaEventAdapter::ReadTimeCodeEvent(std::ifstream* file, MetaMidiEvent* event)
{
unsigned byteCount = 0;
int length = 0;
BinaryFile::GetNextByteAsInt(file, length);
byteCount++;
MidiSmtpeTimecode timeCode;
BinaryFile::GetNextByteAsInt(file, timeCode.mHr);
BinaryFile::GetNextByteAsInt(file, timeCode.mMin);
BinaryFile::GetNextByteAsInt(file, timeCode.mSec);
BinaryFile::GetNextByteAsInt(file, timeCode.mFrame);
BinaryFile::GetNextByteAsInt(file, timeCode.mFrameFrac);
byteCount +=5;
return byteCount;
}

View file

@ -0,0 +1,21 @@
#pragma once
#include "MetaMidiEvent.h"
#include <fstream>
class MidiMetaEventAdapter
{
public:
static int ReadEvent(std::ifstream* file, MetaMidiEvent* event, int& lastMidiChannel);
static int ReadIntEvent(std::ifstream* file, MetaMidiEvent* event, int length=-1);
static int ReadStringEvent(std::ifstream* file, MetaMidiEvent* event);
static int ReadChannelPrefixEvent(std::ifstream* file, MetaMidiEvent* event, int& lastMidiChannel);
static int ReadTimeSignatureEvent(std::ifstream* file, MetaMidiEvent* event);
static int ReadKeySignatureEvent(std::ifstream* file, MetaMidiEvent* event);
static int ReadTimeCodeEvent(std::ifstream* file, MetaMidiEvent* event);
static int ReadUnknownEvent(std::ifstream* file);
};

View file

@ -0,0 +1,153 @@
#include "MidiReader.h"
#include "MidiDocument.h"
#include "ByteUtils.h"
#include "MidiTrack.h"
#include "BinaryFile.h"
#include "FileLogger.h"
#include "MidiElements.h"
#include "MidiTimeAdapter.h"
#include "MidiMetaEventAdapter.h"
#include "MidiChannelEventAdapter.h"
#include <fstream>
#include <iostream>
#include <cstring>
#include <bitset>
MidiReader::MidiReader()
: mDocument(MidiDocument::Create()),
mLastChannelEventType(MidiChannelEvent::Type::NONE)
{
}
MidiDocument* MidiReader::GetDocument() const
{
return mDocument.get();
}
bool MidiReader::ProcessHeader()
{
if(!BinaryFile::CheckNextDWord(mFile->GetInHandle(), HeaderLabel))
{
return false;
}
int length = 0;
if(!BinaryFile::GetNextDWord(mFile->GetInHandle(), length))
{
return false;
}
int formatType { 0 };
if(!BinaryFile::GetNextWord(mFile->GetInHandle(), formatType))
{
return false;
}
mDocument->SetFormatType(formatType);
int expectedTracks { 0 };
if(!BinaryFile::GetNextWord(mFile->GetInHandle(), expectedTracks))
{
return false;
}
mDocument->SetExpectedTracks(expectedTracks);
MidiTimeDivision timeDivision;
MidiTimeAdapter::ReadTimeDivision(mFile->GetInHandle(), timeDivision);
mDocument->SetTimeDivision(timeDivision);
return true;
}
int MidiReader::ProcessEvent(MidiTrack* track)
{
int timeDelta {0};
unsigned byteCount {0};
byteCount += MidiTimeAdapter::ReadEventTimeDelta(mFile->GetInHandle(), timeDelta);
char c;
mFile->GetInHandle()->get(c);
std::cout << "Event check: " << std::bitset<8>(c) << std::endl;
byteCount++;
if(MidiEvent::IsMetaEvent(c))
{
auto event = std::make_unique<MetaMidiEvent>();
event->SetTimeDelta(timeDelta);
std::cout << "Meta event " <<std::endl;
byteCount += MidiMetaEventAdapter::ReadEvent(mFile->GetInHandle(), event.get(), mLastMidiChannel);
track->AddEvent(std::move(event));
}
else if(MidiEvent::IsSysExEvent(c))
{
std::cout << "Sysex event" << std::endl;
}
else
{ // Midi event
auto event = std::make_unique<MidiChannelEvent>();
event->SetTimeDelta(timeDelta);
std::cout << "Midi event" << std::endl;
byteCount += MidiChannelEventAdapter::ReadEvent(mFile->GetInHandle(), c, event.get(), mLastChannelEventType);
track->AddEvent(std::move(event));
}
return byteCount;
}
bool MidiReader::ProcessTrackChunk(bool debug)
{
if(!BinaryFile::CheckNextDWord(mFile->GetInHandle(), TrackChunkLabel))
{
return false;
}
int chunkSize = 0;
if(!BinaryFile::GetNextDWord(mFile->GetInHandle(), chunkSize))
{
return false;
}
unsigned byteCount = 0;
auto track = std::make_unique<MidiTrack>();
unsigned iter_count = 0;
while(byteCount < unsigned(chunkSize))
{
std::cout << "-------------" << std::endl;
byteCount += ProcessEvent(track.get());
std::cout << "Track byte count: " << byteCount << " of " << chunkSize << std::endl;
if(debug && iter_count == 40)
{
return true;
}
iter_count ++;
}
mDocument->AddTrack(std::move(track));
return true;
}
void MidiReader::Read(const std::string& path)
{
mFile = std::make_unique<File>(path);
mFile->Open(true);
if(!ProcessHeader())
{
MLOG_ERROR("Problem processing header");
return;
}
int trackCount = 0;
if(!ProcessTrackChunk(false))
{
MLOG_ERROR("Problem processing track chunk");
return;
}
trackCount++;
if(!ProcessTrackChunk(true))
{
MLOG_ERROR("Problem processing track chunk");
return;
}
trackCount++;
mFile->Close();
}

View file

@ -0,0 +1,35 @@
#pragma once
#include "MidiDocument.h"
#include "MetaMidiEvent.h"
#include "MidiTrack.h"
#include "MidiChannelEvent.h"
#include "File.h"
#include <string>
class MidiReader
{
static constexpr const char TrackChunkLabel[] = "MTrk";
static constexpr const char HeaderLabel[] = "MThd";
public:
MidiReader();
void Read(const std::string& path);
MidiDocument* GetDocument() const;
private:
bool ProcessHeader();
bool ProcessTrackChunk(bool debug=false);
int ProcessEvent(MidiTrack* track);
private:
std::unique_ptr<File> mFile;
MidiDocumentPtr mDocument;
int mLastMidiChannel {0};
MidiChannelEvent::Type mLastChannelEventType;
};

View file

@ -0,0 +1,66 @@
#include "MidiTimeAdapter.h"
#include "BinaryFile.h"
#include "ByteUtils.h"
#include <iostream>
#include <bitset>
int MidiTimeAdapter::ReadEventTimeDelta(std::ifstream* file, int& delta)
{
unsigned byteCount = 0;
char c;
file->get(c);
byteCount++;
if(!ByteUtils::MSBIsOne(c))
{
delta = int(c);
std::cout << "Time delta final: " << delta << std::endl;
return byteCount;
}
int working_c = c;
int final_c = 0;
unsigned count = 0;
std::cout << "Working " << std::bitset<8>(working_c) << std::endl;
while(unsigned(working_c >> 7) != 0)
{
char corrected = (working_c &= ~(1UL << 7));
final_c <<= 7;
final_c |= (corrected << 7*count);
char file_c;
file->get(file_c);
byteCount++;
working_c = int(file_c);
std::cout << "Working " << std::bitset<8>(working_c) << std::endl;
count++;
}
std::cout << "Time delta start: " << std::bitset<16>(final_c) << std::endl;
final_c <<= 7;
std::cout << "Time delta pre: " << std::bitset<16>(final_c) << std::endl;
final_c |= (working_c << 7*(count-1));
delta = int(final_c);
std::cout << "Time delta final: " << delta << "|" << std::bitset<16>(final_c)<< std::endl;
return byteCount;
}
int MidiTimeAdapter::ReadTimeDivision(std::ifstream* file, MidiTimeDivision& division)
{
int time_division;
if(!BinaryFile::GetNextWord(file, time_division, false))
{
return -1;
}
division.mUseFps = ByteUtils::GetWordFirstBit(time_division);
if (division.mUseFps)
{
const int TOP_7_BITS = 0x7F00; // 0111 1111 - 0000 0000
division.mFps = ((~time_division & TOP_7_BITS) >> 8) - 1; // Reverse 2complement of next 7 bits
}
division.mTicks = ByteUtils::GetWordLastByte(time_division);
return 2; // Bytes advanced
}

View file

@ -0,0 +1,13 @@
#pragma once
#include "MidiElements.h"
#include <fstream>
class MidiTimeAdapter
{
public:
static int ReadEventTimeDelta(std::ifstream* file, int& delta);
static int ReadTimeDivision(std::ifstream* file, MidiTimeDivision& division);
};