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/MidiTrack.cpp
|
||||||
midi/MidiDocument.cpp
|
midi/MidiDocument.cpp
|
||||||
midi/MidiEvent.cpp
|
midi/MidiEvent.cpp
|
||||||
midi/MetaMidiEvent.cpp)
|
midi/MetaMidiEvent.cpp
|
||||||
|
midi/MidiChannelEvent.cpp)
|
||||||
|
|
||||||
|
|
||||||
add_library(audio SHARED ${audio_LIB_INCLUDES})
|
add_library(audio SHARED ${audio_LIB_INCLUDES})
|
||||||
target_include_directories(audio PUBLIC
|
target_include_directories(audio PUBLIC
|
||||||
"${CMAKE_CURRENT_SOURCE_DIR}"
|
"${CMAKE_CURRENT_SOURCE_DIR}"
|
||||||
|
"${PROJECT_SOURCE_DIR}/src/core/file_utilities"
|
||||||
"${PROJECT_SOURCE_DIR}/src/core/loggers"
|
"${PROJECT_SOURCE_DIR}/src/core/loggers"
|
||||||
"${CMAKE_CURRENT_SOURCE_DIR}/audio_interfaces"
|
"${CMAKE_CURRENT_SOURCE_DIR}/audio_interfaces"
|
||||||
"${CMAKE_CURRENT_SOURCE_DIR}/midi"
|
"${CMAKE_CURRENT_SOURCE_DIR}/midi"
|
||||||
|
|
|
@ -1,7 +1,55 @@
|
||||||
#include "MetaMidiEvent.h"
|
#include "MetaMidiEvent.h"
|
||||||
|
|
||||||
MetaMidiEvent::MetaMidiEvent()
|
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,8 +1,8 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <string>
|
|
||||||
|
|
||||||
#include "MidiEvent.h"
|
#include "MidiEvent.h"
|
||||||
|
#include <memory>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
class MetaMidiEvent : public MidiEvent
|
class MetaMidiEvent : public MidiEvent
|
||||||
{
|
{
|
||||||
|
@ -10,17 +10,40 @@ public:
|
||||||
enum class Type
|
enum class Type
|
||||||
{
|
{
|
||||||
UNSET,
|
UNSET,
|
||||||
TRACK_NAME
|
TRACK_NAME,
|
||||||
|
SET_TEMPO,
|
||||||
|
TIME_SIGNATURE
|
||||||
};
|
};
|
||||||
|
|
||||||
Type mType = Type::UNSET;
|
private:
|
||||||
std::string mLabel;
|
|
||||||
MetaMidiEvent();
|
|
||||||
|
|
||||||
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_TRACK_NAME = 0x3; // 0000 0011
|
||||||
|
static const int META_TIME_SIG = 0x58; // 0101 1000
|
||||||
|
Type mType;
|
||||||
|
std::string mLabel;
|
||||||
|
int mValue;
|
||||||
|
|
||||||
|
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"
|
#include "MidiEvent.h"
|
||||||
|
|
||||||
MidiEvent::MidiEvent()
|
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
|
#pragma once
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
class MidiEvent
|
class MidiEvent
|
||||||
{
|
{
|
||||||
|
|
||||||
public:
|
|
||||||
int mTimeDelta = 0;
|
|
||||||
MidiEvent();
|
|
||||||
|
|
||||||
static bool IsMetaEvent(char c)
|
|
||||||
{
|
|
||||||
return (c & META_EVENT) == META_EVENT;
|
|
||||||
}
|
|
||||||
|
|
||||||
public:
|
|
||||||
static const int META_EVENT = 0xFF; // 1111 1111
|
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();
|
||||||
|
|
||||||
|
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,85 +1,57 @@
|
||||||
#include "MidiReader.h"
|
#include "MidiReader.h"
|
||||||
|
|
||||||
|
#include "MidiDocument.h"
|
||||||
|
#include "ByteUtils.h"
|
||||||
|
#include "MidiTrack.h"
|
||||||
|
#include "BinaryFile.h"
|
||||||
|
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
#include <bitset>
|
#include <bitset>
|
||||||
#include "MidiDocument.h"
|
|
||||||
#include "ByteUtils.h"
|
|
||||||
#include "MidiTrack.h"
|
|
||||||
|
|
||||||
MidiReader::MidiReader()
|
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)
|
bool MidiReader::ProcessHeader(std::ifstream& file)
|
||||||
{
|
{
|
||||||
char c;
|
if(!BinaryFile::CheckNextDWord(file, HeaderLabel))
|
||||||
unsigned bufferSize = ByteUtils::BYTES_PER_DWORD;
|
|
||||||
char buffer[bufferSize];
|
|
||||||
|
|
||||||
if(!GetDWord(file, buffer))
|
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(!ByteUtils::CompareDWords(buffer, HeaderLabel))
|
int length = 0;
|
||||||
|
if(!BinaryFile::GetNextDWord(file, length))
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(!GetDWord(file, buffer))
|
if(!BinaryFile::GetNextWord(file, mDocument.mFormatType))
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
int length = ByteUtils::ToDWord(buffer);
|
if(!BinaryFile::GetNextWord(file, mDocument.mExpectedTracks))
|
||||||
|
|
||||||
if(!GetWord(file, buffer))
|
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
mDocument.mFormatType = ByteUtils::ToWord(buffer);
|
|
||||||
|
|
||||||
if(!GetWord(file, buffer))
|
ProcessTimeDivision(file);
|
||||||
{
|
return true;
|
||||||
return false;
|
}
|
||||||
}
|
|
||||||
mDocument.mExpectedTracks = ByteUtils::ToWord(buffer);
|
|
||||||
|
|
||||||
if(!GetWord(file, buffer))
|
bool MidiReader::ProcessTimeDivision(std::ifstream& file)
|
||||||
{
|
{
|
||||||
return false;
|
|
||||||
}
|
|
||||||
int time_division;
|
int time_division;
|
||||||
std::memcpy(&time_division, buffer, sizeof(int));
|
if(!BinaryFile::GetNextWord(file, time_division, false))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
const int TOP_7_BITS = 0x7F00; // 0111 1111 - 0000 0000
|
const int TOP_7_BITS = 0x7F00; // 0111 1111 - 0000 0000
|
||||||
mDocument.mUseFps = ByteUtils::GetWordFirstBit(time_division);
|
mDocument.mUseFps = ByteUtils::GetWordFirstBit(time_division);
|
||||||
|
@ -92,16 +64,103 @@ int MidiReader::ProcessTimeDelta(std::ifstream& file)
|
||||||
{
|
{
|
||||||
char c;
|
char c;
|
||||||
file.get(c);
|
file.get(c);
|
||||||
bool needs_next = c & ByteUtils::BYTE_FIRST_BIT;
|
|
||||||
int time_offset = 0;
|
|
||||||
if(!needs_next)
|
|
||||||
{
|
|
||||||
time_offset = int(c);
|
|
||||||
mTrackByteCount ++;
|
mTrackByteCount ++;
|
||||||
|
|
||||||
|
if(unsigned(c >> 7) == 0)
|
||||||
|
{
|
||||||
|
int delta = int(c);
|
||||||
|
std::cout << "Time delta final: " << delta << "|" << std::bitset<16>(c)<< std::endl;
|
||||||
|
return delta;
|
||||||
}
|
}
|
||||||
std::cout << "Next " << needs_next <<std::endl;
|
|
||||||
std::cout << "Offset " << time_offset <<std::endl;
|
int working_c = c;
|
||||||
return time_offset;
|
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)
|
bool MidiReader::ProcessMetaEvent(std::ifstream& file, MetaMidiEvent& event)
|
||||||
|
@ -111,21 +170,15 @@ bool MidiReader::ProcessMetaEvent(std::ifstream& file, MetaMidiEvent& event)
|
||||||
mTrackByteCount ++;
|
mTrackByteCount ++;
|
||||||
if(MetaMidiEvent::IsTrackName(c))
|
if(MetaMidiEvent::IsTrackName(c))
|
||||||
{
|
{
|
||||||
event.mType = MetaMidiEvent::Type::TRACK_NAME;
|
ProcessTrackNameMetaEvent(file, event);
|
||||||
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;
|
else if(MetaMidiEvent::IsSetTempo(c))
|
||||||
event.mLabel = "Track name";
|
{
|
||||||
|
ProcessSetTempoMetaEvent(file, event);
|
||||||
|
}
|
||||||
|
else if(MetaMidiEvent::IsTimeSignature(c))
|
||||||
|
{
|
||||||
|
ProcessTimeSignatureMetaEvent(file, event);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -135,59 +188,160 @@ bool MidiReader::ProcessMetaEvent(std::ifstream& file, MetaMidiEvent& event)
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool MidiReader::ProcessEvent(std::ifstream& file)
|
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;
|
char c;
|
||||||
int timeDelta = ProcessTimeDelta(file);
|
int timeDelta = ProcessTimeDelta(file);
|
||||||
|
|
||||||
file.get(c);
|
file.get(c);
|
||||||
|
std::cout << std::bitset<8>(c) << std::endl;
|
||||||
mTrackByteCount ++;
|
mTrackByteCount ++;
|
||||||
if(MidiEvent::IsMetaEvent(c))
|
if(MidiEvent::IsMetaEvent(c))
|
||||||
{
|
{
|
||||||
MetaMidiEvent event;
|
MetaMidiEvent event;
|
||||||
event.mTimeDelta = timeDelta;
|
event.SetTimeDelta(timeDelta);
|
||||||
std::cout << "Meta event " <<std::endl;
|
std::cout << "Meta event " <<std::endl;
|
||||||
ProcessMetaEvent(file, event);
|
ProcessMetaEvent(file, event);
|
||||||
|
track.AddEvent(event);
|
||||||
|
}
|
||||||
|
else if(MidiEvent::IsSysExEvent(c))
|
||||||
|
{
|
||||||
|
std::cout << "Sysex event" << std::endl;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{ // Midi event
|
||||||
int first_byte = 0xF0;
|
MidiChannelEvent event;
|
||||||
int event_type = (c & first_byte) >> 4;
|
event.SetTimeDelta(timeDelta);
|
||||||
std::cout << "Next " << std::bitset<8>(c) << "|" << event_type <<std::endl;
|
std::cout << "Midi event" << std::endl;
|
||||||
std::cout << event_type << std::endl;
|
ProcessMidiChannelEvent(file, c, event);
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool MidiReader::ProcessTrackChunk(std::ifstream& file)
|
bool MidiReader::ProcessTrackChunk(std::ifstream& file, bool debug)
|
||||||
{
|
{
|
||||||
char c;
|
if(!BinaryFile::CheckNextDWord(file, TrackChunkLabel))
|
||||||
unsigned bufferSize = ByteUtils::BYTES_PER_DWORD;
|
|
||||||
char buffer[bufferSize];
|
|
||||||
|
|
||||||
if(!GetDWord(file, buffer))
|
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(!ByteUtils::CompareDWords(buffer, TrackChunkLabel))
|
int chunkSize = 0;
|
||||||
|
if(!BinaryFile::GetNextDWord(file, chunkSize))
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(!GetDWord(file, buffer))
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
int chunkSize = ByteUtils::ToDWord(buffer);
|
|
||||||
std::cout << "Chunk size: "<< chunkSize << std::endl;
|
std::cout << "Chunk size: "<< chunkSize << std::endl;
|
||||||
|
|
||||||
mTrackByteCount = 0;
|
mTrackByteCount = 0;
|
||||||
MidiTrack track;
|
MidiTrack track;
|
||||||
ProcessEvent(file);
|
unsigned iter_count = 0;
|
||||||
|
while(mTrackByteCount < chunkSize)
|
||||||
|
{
|
||||||
|
std::cout << "-------------" << std::endl;
|
||||||
|
ProcessEvent(file, track);
|
||||||
std::cout << "Track byte count: "<< mTrackByteCount << std::endl;
|
std::cout << "Track byte count: "<< mTrackByteCount << std::endl;
|
||||||
|
if(debug && iter_count == 25)
|
||||||
ProcessEvent(file);
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
iter_count ++;
|
||||||
|
}
|
||||||
|
|
||||||
mDocument.mTracks.push_back(track);
|
mDocument.mTracks.push_back(track);
|
||||||
return true;
|
return true;
|
||||||
|
@ -199,12 +353,26 @@ void MidiReader::Read(const std::string& path)
|
||||||
if(!ProcessHeader(file))
|
if(!ProcessHeader(file))
|
||||||
{
|
{
|
||||||
std::cout << "Problem processing header" << std::endl;
|
std::cout << "Problem processing header" << std::endl;
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int trackCount = 0;
|
||||||
if(!ProcessTrackChunk(file))
|
if(!ProcessTrackChunk(file))
|
||||||
{
|
{
|
||||||
std::cout << "Problem processing track chunk" << std::endl;
|
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,6 +2,7 @@
|
||||||
|
|
||||||
#include "MidiDocument.h"
|
#include "MidiDocument.h"
|
||||||
#include "MetaMidiEvent.h"
|
#include "MetaMidiEvent.h"
|
||||||
|
#include "MidiChannelEvent.h"
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
class MidiReader
|
class MidiReader
|
||||||
|
@ -10,6 +11,7 @@ class MidiReader
|
||||||
static constexpr const char HeaderLabel[] = "MThd";
|
static constexpr const char HeaderLabel[] = "MThd";
|
||||||
MidiDocument mDocument;
|
MidiDocument mDocument;
|
||||||
unsigned mTrackByteCount;
|
unsigned mTrackByteCount;
|
||||||
|
MidiChannelEvent::Type mLastChannelEventType;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
|
@ -21,11 +23,21 @@ 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 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);
|
bool ProcessMetaEvent(std::ifstream& file, MetaMidiEvent& event);
|
||||||
|
|
||||||
int ProcessTimeDelta(std::ifstream& file);
|
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
|
Color.cpp
|
||||||
CommandLineArgs.cpp
|
CommandLineArgs.cpp
|
||||||
loggers/FileLogger.cpp
|
loggers/FileLogger.cpp
|
||||||
|
file_utilities/BinaryFile.cpp
|
||||||
StringUtils.cpp
|
StringUtils.cpp
|
||||||
http/HttpResponse.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