Further midi file support.
This commit is contained in:
parent
36826fa1d4
commit
4d5ca4d654
12 changed files with 739 additions and 200 deletions
|
@ -6,12 +6,14 @@ list(APPEND audio_LIB_INCLUDES
|
|||
midi/MidiTrack.cpp
|
||||
midi/MidiDocument.cpp
|
||||
midi/MidiEvent.cpp
|
||||
midi/MetaMidiEvent.cpp)
|
||||
midi/MetaMidiEvent.cpp
|
||||
midi/MidiChannelEvent.cpp)
|
||||
|
||||
|
||||
add_library(audio SHARED ${audio_LIB_INCLUDES})
|
||||
target_include_directories(audio PUBLIC
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}"
|
||||
"${PROJECT_SOURCE_DIR}/src/core/file_utilities"
|
||||
"${PROJECT_SOURCE_DIR}/src/core/loggers"
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/audio_interfaces"
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/midi"
|
||||
|
|
|
@ -1,7 +1,55 @@
|
|||
#include "MetaMidiEvent.h"
|
||||
|
||||
MetaMidiEvent::MetaMidiEvent()
|
||||
: MidiEvent()
|
||||
: MidiEvent(),
|
||||
mType(Type::UNSET),
|
||||
mLabel(),
|
||||
mValue(0)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
std::shared_ptr<MetaMidiEvent> MetaMidiEvent::Create()
|
||||
{
|
||||
return std::make_shared<MetaMidiEvent>();
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
|
|
@ -1,26 +1,49 @@
|
|||
#pragma once
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "MidiEvent.h"
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
class MetaMidiEvent : public MidiEvent
|
||||
{
|
||||
public:
|
||||
enum class Type
|
||||
{
|
||||
UNSET,
|
||||
TRACK_NAME
|
||||
};
|
||||
enum class Type
|
||||
{
|
||||
UNSET,
|
||||
TRACK_NAME,
|
||||
SET_TEMPO,
|
||||
TIME_SIGNATURE
|
||||
};
|
||||
|
||||
Type mType = Type::UNSET;
|
||||
std::string mLabel;
|
||||
MetaMidiEvent();
|
||||
private:
|
||||
|
||||
static bool IsTrackName(char c)
|
||||
{
|
||||
return (c & META_TRACK_NAME) == META_TRACK_NAME;
|
||||
}
|
||||
static const int META_SET_TEMPO = 0x51; // 0101 0001
|
||||
static const int META_TRACK_NAME = 0x3; // 0000 0011
|
||||
static const int META_TIME_SIG = 0x58; // 0101 1000
|
||||
Type mType;
|
||||
std::string mLabel;
|
||||
int mValue;
|
||||
|
||||
static const int META_TRACK_NAME = 0x3; // 0000 0011
|
||||
public:
|
||||
|
||||
MetaMidiEvent();
|
||||
static std::shared_ptr<MetaMidiEvent> Create();
|
||||
|
||||
void SetValue(int value);
|
||||
|
||||
void SetIsTrackName();
|
||||
|
||||
void SetIsSetTempo();
|
||||
|
||||
void SetIsTimeSignature();
|
||||
|
||||
void SetLabel(const std::string& label);
|
||||
|
||||
static bool IsTrackName(char c);
|
||||
|
||||
static bool IsSetTempo(char c);
|
||||
|
||||
static bool IsTimeSignature(char c);
|
||||
};
|
||||
|
||||
using MetaMidiEventPtr = std::shared_ptr<MetaMidiEvent>;
|
||||
|
|
80
src/audio/midi/MidiChannelEvent.cpp
Normal file
80
src/audio/midi/MidiChannelEvent.cpp
Normal file
|
@ -0,0 +1,80 @@
|
|||
#include "MidiChannelEvent.h"
|
||||
|
||||
MidiChannelEvent::MidiChannelEvent()
|
||||
: MidiEvent(),
|
||||
mType(Type::NONE),
|
||||
mValue0(0),
|
||||
mValue1(0)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
std::shared_ptr<MidiChannelEvent> MidiChannelEvent::Create()
|
||||
{
|
||||
return std::make_shared<MidiChannelEvent>();
|
||||
}
|
||||
|
||||
void MidiChannelEvent::SetType(Type type)
|
||||
{
|
||||
mType = type;
|
||||
}
|
||||
MidiChannelEvent::Type MidiChannelEvent::GetType()
|
||||
{
|
||||
return mType;
|
||||
}
|
||||
void MidiChannelEvent::SetValues(int value0, int value1)
|
||||
{
|
||||
mValue0 = value0;
|
||||
mValue1 = value1;
|
||||
}
|
||||
int MidiChannelEvent::GetValue0()
|
||||
{
|
||||
return mValue0;
|
||||
}
|
||||
int MidiChannelEvent::GetValue1()
|
||||
{
|
||||
return mValue1;
|
||||
}
|
||||
|
||||
bool MidiChannelEvent::IsControllerEvent(int c)
|
||||
{
|
||||
return (c & CONTROLLER) == CONTROLLER;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
52
src/audio/midi/MidiChannelEvent.h
Normal file
52
src/audio/midi/MidiChannelEvent.h
Normal file
|
@ -0,0 +1,52 @@
|
|||
#pragma once
|
||||
|
||||
#include "MidiEvent.h"
|
||||
|
||||
class MidiChannelEvent : public MidiEvent
|
||||
{
|
||||
public:
|
||||
enum class Type{
|
||||
NONE, NOTE_OFF, NOTE_ON, NOTE_AFTERTOUCH, CONTROLLER,
|
||||
PROGRAM, CHANNEL_AFTERTOUCH, PITCH_BEND
|
||||
};
|
||||
|
||||
private:
|
||||
static const int NOTE_OFF = 0x8;
|
||||
static const int NOTE_ON = 0x9;
|
||||
static const int NOTE_AFTERTOUCH = 0xA;
|
||||
static const int CONTROLLER = 0xB;
|
||||
static const int PROGRAM = 0xC;
|
||||
static const int CHANNEL_AFTERTOUCH = 0xD;
|
||||
static const int PITCH_BEND = 0xE;
|
||||
Type mType;
|
||||
int mValue0;
|
||||
int mValue1;
|
||||
|
||||
public:
|
||||
MidiChannelEvent();
|
||||
static std::shared_ptr<MidiChannelEvent> Create();
|
||||
|
||||
void SetType(Type type);
|
||||
Type GetType();
|
||||
void SetValues(int value0, int value1);
|
||||
int GetValue0();
|
||||
int GetValue1();
|
||||
|
||||
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);
|
||||
};
|
||||
|
||||
using MidiChannelEventPtr = std::shared_ptr<MidiChannelEvent>;
|
|
@ -1,6 +1,37 @@
|
|||
#include "MidiEvent.h"
|
||||
|
||||
MidiEvent::MidiEvent()
|
||||
: mTimeDelta(0)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
bool MidiEvent::IsSysExEvent(char c)
|
||||
{
|
||||
return IsNormalSysExEvent(c) || IsDividedSysExEvent(c);
|
||||
}
|
||||
|
||||
bool MidiEvent::IsNormalSysExEvent(char c)
|
||||
{
|
||||
return (c & SYSEX_EVENT) == SYSEX_EVENT;
|
||||
}
|
||||
|
||||
bool MidiEvent::IsDividedSysExEvent(char c)
|
||||
{
|
||||
return (c & DIVIDED_SYSEX_EVENT) == DIVIDED_SYSEX_EVENT;
|
||||
}
|
||||
|
|
|
@ -1,17 +1,26 @@
|
|||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
|
||||
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:
|
||||
int mTimeDelta = 0;
|
||||
MidiEvent();
|
||||
|
||||
static bool IsMetaEvent(char c)
|
||||
{
|
||||
return (c & META_EVENT) == META_EVENT;
|
||||
}
|
||||
MidiEvent();
|
||||
virtual ~MidiEvent() = default;
|
||||
|
||||
public:
|
||||
static const int META_EVENT = 0xFF; // 1111 1111
|
||||
static std::shared_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);
|
||||
};
|
||||
|
||||
using MidiEventPtr = std::shared_ptr<MidiEvent>;
|
||||
|
|
|
@ -1,211 +1,379 @@
|
|||
#include "MidiReader.h"
|
||||
|
||||
#include "MidiDocument.h"
|
||||
#include "ByteUtils.h"
|
||||
#include "MidiTrack.h"
|
||||
#include "BinaryFile.h"
|
||||
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <cstring>
|
||||
#include <bitset>
|
||||
#include "MidiDocument.h"
|
||||
#include "ByteUtils.h"
|
||||
#include "MidiTrack.h"
|
||||
|
||||
MidiReader::MidiReader()
|
||||
:mDocument(),
|
||||
mTrackByteCount(0),
|
||||
mLastChannelEventType(MidiChannelEvent::Type::NONE)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
bool GetBytes(std::ifstream& file, char* buffer, unsigned number)
|
||||
{
|
||||
char c;
|
||||
for(unsigned idx=0; idx<number; idx++)
|
||||
{
|
||||
if(file.get(c))
|
||||
{
|
||||
buffer[idx] = c;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool GetWord(std::ifstream& file, char* buffer)
|
||||
{
|
||||
return GetBytes(file, buffer, 2);
|
||||
}
|
||||
|
||||
bool GetDWord(std::ifstream& file, char* buffer)
|
||||
{
|
||||
return GetBytes(file, buffer, 4);
|
||||
}
|
||||
|
||||
bool MidiReader::ProcessHeader(std::ifstream& file)
|
||||
{
|
||||
char c;
|
||||
unsigned bufferSize = ByteUtils::BYTES_PER_DWORD;
|
||||
char buffer[bufferSize];
|
||||
|
||||
if(!GetDWord(file, buffer))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if(!ByteUtils::CompareDWords(buffer, HeaderLabel))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if(!GetDWord(file, buffer))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
int length = ByteUtils::ToDWord(buffer);
|
||||
|
||||
if(!GetWord(file, buffer))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
mDocument.mFormatType = ByteUtils::ToWord(buffer);
|
||||
|
||||
if(!GetWord(file, buffer))
|
||||
if(!BinaryFile::CheckNextDWord(file, HeaderLabel))
|
||||
{
|
||||
return false;
|
||||
return false;
|
||||
}
|
||||
mDocument.mExpectedTracks = ByteUtils::ToWord(buffer);
|
||||
|
||||
if(!GetWord(file, buffer))
|
||||
int length = 0;
|
||||
if(!BinaryFile::GetNextDWord(file, length))
|
||||
{
|
||||
return false;
|
||||
return false;
|
||||
}
|
||||
int time_division;
|
||||
std::memcpy(&time_division, buffer, sizeof(int));
|
||||
|
||||
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;
|
||||
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);
|
||||
bool needs_next = c & ByteUtils::BYTE_FIRST_BIT;
|
||||
int time_offset = 0;
|
||||
if(!needs_next)
|
||||
{
|
||||
time_offset = int(c);
|
||||
mTrackByteCount ++;
|
||||
}
|
||||
std::cout << "Next " << needs_next <<std::endl;
|
||||
std::cout << "Offset " << time_offset <<std::endl;
|
||||
return time_offset;
|
||||
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 ++;
|
||||
|
||||
char buffer[length];
|
||||
BinaryFile::GetNextNBytes(file, buffer, length);
|
||||
mTrackByteCount += length;
|
||||
|
||||
int tempo = ByteUtils::ToInt(buffer, 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))
|
||||
{
|
||||
event.mType = MetaMidiEvent::Type::TRACK_NAME;
|
||||
file.get(c);
|
||||
mTrackByteCount ++;
|
||||
int length = int(c);
|
||||
std::string name;
|
||||
std::cout << "Track name: " << length <<std::endl;
|
||||
|
||||
for(unsigned idx=0; idx<length; idx++)
|
||||
{
|
||||
file.get(c);
|
||||
mTrackByteCount ++;
|
||||
name += c;
|
||||
}
|
||||
std::cout << name << std::endl;
|
||||
event.mLabel = "Track name";
|
||||
}
|
||||
else
|
||||
{
|
||||
std::cout << "something else" << std::endl;
|
||||
std::cout << "Next " << std::bitset<8>(c) << "|" <<std::endl;
|
||||
}
|
||||
return true;
|
||||
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::ProcessEvent(std::ifstream& file)
|
||||
bool MidiReader::ProcessMidiEventData(std::ifstream& file, MidiChannelEvent& event, char c)
|
||||
{
|
||||
char c;
|
||||
int timeDelta = ProcessTimeDelta(file);
|
||||
|
||||
file.get(c);
|
||||
mTrackByteCount ++;
|
||||
if(MidiEvent::IsMetaEvent(c))
|
||||
{
|
||||
MetaMidiEvent event;
|
||||
event.mTimeDelta = timeDelta;
|
||||
std::cout << "Meta event " <<std::endl;
|
||||
ProcessMetaEvent(file, event);
|
||||
}
|
||||
else
|
||||
{
|
||||
int first_byte = 0xF0;
|
||||
int event_type = (c & first_byte) >> 4;
|
||||
std::cout << "Next " << std::bitset<8>(c) << "|" << event_type <<std::endl;
|
||||
std::cout << event_type << std::endl;
|
||||
}
|
||||
return true;
|
||||
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::ProcessTrackChunk(std::ifstream& file)
|
||||
bool MidiReader::ProcessMidiEventData(std::ifstream& file, MidiChannelEvent& event)
|
||||
{
|
||||
char c;
|
||||
unsigned bufferSize = ByteUtils::BYTES_PER_DWORD;
|
||||
char buffer[bufferSize];
|
||||
int value0 = 0;
|
||||
BinaryFile::GetNextByteAsInt(file, value0);
|
||||
mTrackByteCount ++;
|
||||
|
||||
if(!GetDWord(file, buffer))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
int value1 = 0;
|
||||
BinaryFile::GetNextByteAsInt(file, value1);
|
||||
mTrackByteCount ++;
|
||||
event.SetValues(value0, value1);
|
||||
std::cout << "data: " << value0 << "|" << value1 << "|" << std::endl;
|
||||
return true;
|
||||
}
|
||||
|
||||
if(!ByteUtils::CompareDWords(buffer, TrackChunkLabel))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
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(!GetDWord(file, buffer))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
int chunkSize = ByteUtils::ToDWord(buffer);
|
||||
std::cout << "Chunk size: "<< chunkSize << 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;
|
||||
}
|
||||
|
||||
mTrackByteCount = 0;
|
||||
MidiTrack track;
|
||||
ProcessEvent(file);
|
||||
std::cout << "Track byte count: "<< mTrackByteCount << std::endl;
|
||||
bool MidiReader::ProcessEvent(std::ifstream& file, MidiTrack& track)
|
||||
{
|
||||
char c;
|
||||
int timeDelta = ProcessTimeDelta(file);
|
||||
|
||||
ProcessEvent(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;
|
||||
}
|
||||
|
||||
mDocument.mTracks.push_back(track);
|
||||
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 < 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;
|
||||
}
|
||||
std::ifstream file(path);
|
||||
if(!ProcessHeader(file))
|
||||
{
|
||||
std::cout << "Problem processing header" << std::endl;
|
||||
return;
|
||||
}
|
||||
|
||||
if(!ProcessTrackChunk(file))
|
||||
{
|
||||
std::cout << "Problem processing track chunk" << std::endl;
|
||||
}
|
||||
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();
|
||||
file.close();
|
||||
}
|
||||
|
|
|
@ -2,30 +2,42 @@
|
|||
|
||||
#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;
|
||||
static constexpr const char TrackChunkLabel[] = "MTrk";
|
||||
static constexpr const char HeaderLabel[] = "MThd";
|
||||
MidiDocument mDocument;
|
||||
unsigned mTrackByteCount;
|
||||
MidiChannelEvent::Type mLastChannelEventType;
|
||||
|
||||
public:
|
||||
|
||||
MidiReader();
|
||||
MidiReader();
|
||||
|
||||
void Read(const std::string& path);
|
||||
void Read(const std::string& path);
|
||||
|
||||
private:
|
||||
|
||||
bool ProcessHeader(std::ifstream& file);
|
||||
bool ProcessHeader(std::ifstream& file);
|
||||
|
||||
bool ProcessTrackChunk(std::ifstream& file);
|
||||
bool ProcessTimeDivision(std::ifstream& file);
|
||||
|
||||
bool ProcessEvent(std::ifstream& file);
|
||||
bool ProcessTrackChunk(std::ifstream& file, bool debug=false);
|
||||
|
||||
bool ProcessMetaEvent(std::ifstream& file, MetaMidiEvent& event);
|
||||
bool ProcessEvent(std::ifstream& file, MidiTrack& track);
|
||||
|
||||
int ProcessTimeDelta(std::ifstream& file);
|
||||
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);
|
||||
};
|
||||
|
|
|
@ -3,6 +3,7 @@ list(APPEND core_LIB_INCLUDES
|
|||
Color.cpp
|
||||
CommandLineArgs.cpp
|
||||
loggers/FileLogger.cpp
|
||||
file_utilities/BinaryFile.cpp
|
||||
StringUtils.cpp
|
||||
http/HttpResponse.cpp)
|
||||
|
||||
|
|
88
src/core/file_utilities/BinaryFile.cpp
Normal file
88
src/core/file_utilities/BinaryFile.cpp
Normal file
|
@ -0,0 +1,88 @@
|
|||
#include "BinaryFile.h"
|
||||
#include "ByteUtils.h"
|
||||
|
||||
bool BinaryFile::GetNextByteAsInt(std::ifstream& file, int& target)
|
||||
{
|
||||
char c;
|
||||
file.get(c);
|
||||
target = int(c);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool BinaryFile::GetNextNBytes(std::ifstream& file, char* buffer, unsigned number)
|
||||
{
|
||||
char c;
|
||||
for(unsigned idx=0; idx<number; idx++)
|
||||
{
|
||||
if(file.get(c))
|
||||
{
|
||||
buffer[idx] = c;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool BinaryFile::GetNextWord(std::ifstream& file, char* buffer)
|
||||
{
|
||||
return GetNextNBytes(file, buffer, ByteUtils::BYTES_PER_WORD);
|
||||
}
|
||||
|
||||
bool BinaryFile::GetNextDWord(std::ifstream& file, char* buffer)
|
||||
{
|
||||
return GetNextNBytes(file, buffer, ByteUtils::BYTES_PER_DWORD);
|
||||
}
|
||||
|
||||
bool BinaryFile::GetNextWord(std::ifstream& file, int& target, bool reverse)
|
||||
{
|
||||
char buffer[ByteUtils::BYTES_PER_WORD];
|
||||
if(!BinaryFile::GetNextWord(file, buffer))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
target = ByteUtils::ToWord(buffer, reverse);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool BinaryFile::GetNextDWord(std::ifstream& file, int& target)
|
||||
{
|
||||
char buffer[ByteUtils::BYTES_PER_DWORD];
|
||||
if(!BinaryFile::GetNextDWord(file, buffer))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
target = ByteUtils::ToDWord(buffer);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool BinaryFile::CheckNextDWord(std::ifstream& file, const char* target)
|
||||
{
|
||||
char buffer[ByteUtils::BYTES_PER_DWORD];
|
||||
if(!BinaryFile::GetNextDWord(file, buffer))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if(!ByteUtils::CompareDWords(buffer, target))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool BinaryFile::GetNextString(std::ifstream& file, std::string& target, unsigned numBytes)
|
||||
{
|
||||
char c;
|
||||
for(unsigned idx=0; idx<numBytes; idx++)
|
||||
{
|
||||
if(!file.get(c))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
target += c;
|
||||
}
|
||||
return true;
|
||||
}
|
25
src/core/file_utilities/BinaryFile.h
Normal file
25
src/core/file_utilities/BinaryFile.h
Normal file
|
@ -0,0 +1,25 @@
|
|||
#pragma once
|
||||
|
||||
#include <fstream>
|
||||
#include <string>
|
||||
|
||||
class BinaryFile
|
||||
{
|
||||
public:
|
||||
|
||||
static bool GetNextByteAsInt(std::ifstream& file, int& target);
|
||||
|
||||
static bool GetNextNBytes(std::ifstream& file, char* buffer, unsigned numBytes);
|
||||
|
||||
static bool GetNextWord(std::ifstream& file, char* buffer);
|
||||
|
||||
static bool GetNextDWord(std::ifstream& file, char* buffer);
|
||||
|
||||
static bool GetNextWord(std::ifstream& file, int& target, bool reverse = true);
|
||||
|
||||
static bool GetNextDWord(std::ifstream& file, int& target);
|
||||
|
||||
static bool CheckNextDWord(std::ifstream& file, const char* target);
|
||||
|
||||
static bool GetNextString(std::ifstream& file, std::string& target, unsigned numBytes);
|
||||
};
|
Loading…
Reference in a new issue