Start midi file processing.
This commit is contained in:
parent
59c6161fdb
commit
36826fa1d4
22 changed files with 528 additions and 44 deletions
7
src/audio/midi/MetaMidiEvent.cpp
Normal file
7
src/audio/midi/MetaMidiEvent.cpp
Normal file
|
@ -0,0 +1,7 @@
|
|||
#include "MetaMidiEvent.h"
|
||||
|
||||
MetaMidiEvent::MetaMidiEvent()
|
||||
: MidiEvent()
|
||||
{
|
||||
|
||||
}
|
26
src/audio/midi/MetaMidiEvent.h
Normal file
26
src/audio/midi/MetaMidiEvent.h
Normal file
|
@ -0,0 +1,26 @@
|
|||
#pragma once
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "MidiEvent.h"
|
||||
|
||||
class MetaMidiEvent : public MidiEvent
|
||||
{
|
||||
public:
|
||||
enum class Type
|
||||
{
|
||||
UNSET,
|
||||
TRACK_NAME
|
||||
};
|
||||
|
||||
Type mType = Type::UNSET;
|
||||
std::string mLabel;
|
||||
MetaMidiEvent();
|
||||
|
||||
static bool IsTrackName(char c)
|
||||
{
|
||||
return (c & META_TRACK_NAME) == META_TRACK_NAME;
|
||||
}
|
||||
|
||||
static const int META_TRACK_NAME = 0x3; // 0000 0011
|
||||
};
|
6
src/audio/midi/MidiDocument.cpp
Normal file
6
src/audio/midi/MidiDocument.cpp
Normal file
|
@ -0,0 +1,6 @@
|
|||
#include "MidiDocument.h"
|
||||
|
||||
MidiDocument::MidiDocument()
|
||||
{
|
||||
|
||||
}
|
16
src/audio/midi/MidiDocument.h
Normal file
16
src/audio/midi/MidiDocument.h
Normal file
|
@ -0,0 +1,16 @@
|
|||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
#include "MidiTrack.h"
|
||||
|
||||
class MidiDocument
|
||||
{
|
||||
public:
|
||||
int mFormatType = 0;
|
||||
int mExpectedTracks = 0;
|
||||
bool mUseFps = true;
|
||||
int mFps = 0;
|
||||
int ticksPerFrame = 0;
|
||||
std::vector<MidiTrack> mTracks;
|
||||
MidiDocument();
|
||||
};
|
6
src/audio/midi/MidiEvent.cpp
Normal file
6
src/audio/midi/MidiEvent.cpp
Normal file
|
@ -0,0 +1,6 @@
|
|||
#include "MidiEvent.h"
|
||||
|
||||
MidiEvent::MidiEvent()
|
||||
{
|
||||
|
||||
}
|
17
src/audio/midi/MidiEvent.h
Normal file
17
src/audio/midi/MidiEvent.h
Normal file
|
@ -0,0 +1,17 @@
|
|||
#pragma once
|
||||
|
||||
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
|
||||
};
|
211
src/audio/midi/MidiReader.cpp
Normal file
211
src/audio/midi/MidiReader.cpp
Normal file
|
@ -0,0 +1,211 @@
|
|||
#include "MidiReader.h"
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <cstring>
|
||||
#include <bitset>
|
||||
#include "MidiDocument.h"
|
||||
#include "ByteUtils.h"
|
||||
#include "MidiTrack.h"
|
||||
|
||||
MidiReader::MidiReader()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
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))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
mDocument.mExpectedTracks = ByteUtils::ToWord(buffer);
|
||||
|
||||
if(!GetWord(file, buffer))
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
bool MidiReader::ProcessEvent(std::ifstream& file)
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
bool MidiReader::ProcessTrackChunk(std::ifstream& file)
|
||||
{
|
||||
char c;
|
||||
unsigned bufferSize = ByteUtils::BYTES_PER_DWORD;
|
||||
char buffer[bufferSize];
|
||||
|
||||
if(!GetDWord(file, buffer))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if(!ByteUtils::CompareDWords(buffer, TrackChunkLabel))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if(!GetDWord(file, buffer))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
int chunkSize = ByteUtils::ToDWord(buffer);
|
||||
std::cout << "Chunk size: "<< chunkSize << std::endl;
|
||||
|
||||
mTrackByteCount = 0;
|
||||
MidiTrack track;
|
||||
ProcessEvent(file);
|
||||
std::cout << "Track byte count: "<< mTrackByteCount << std::endl;
|
||||
|
||||
ProcessEvent(file);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
if(!ProcessTrackChunk(file))
|
||||
{
|
||||
std::cout << "Problem processing track chunk" << std::endl;
|
||||
}
|
||||
|
||||
|
||||
file.close();
|
||||
}
|
31
src/audio/midi/MidiReader.h
Normal file
31
src/audio/midi/MidiReader.h
Normal file
|
@ -0,0 +1,31 @@
|
|||
#pragma once
|
||||
|
||||
#include "MidiDocument.h"
|
||||
#include "MetaMidiEvent.h"
|
||||
#include <string>
|
||||
|
||||
class MidiReader
|
||||
{
|
||||
static constexpr const char TrackChunkLabel[] = "MTrk";
|
||||
static constexpr const char HeaderLabel[] = "MThd";
|
||||
MidiDocument mDocument;
|
||||
unsigned mTrackByteCount;
|
||||
|
||||
public:
|
||||
|
||||
MidiReader();
|
||||
|
||||
void Read(const std::string& path);
|
||||
|
||||
private:
|
||||
|
||||
bool ProcessHeader(std::ifstream& file);
|
||||
|
||||
bool ProcessTrackChunk(std::ifstream& file);
|
||||
|
||||
bool ProcessEvent(std::ifstream& file);
|
||||
|
||||
bool ProcessMetaEvent(std::ifstream& file, MetaMidiEvent& event);
|
||||
|
||||
int ProcessTimeDelta(std::ifstream& file);
|
||||
};
|
11
src/audio/midi/MidiTrack.cpp
Normal file
11
src/audio/midi/MidiTrack.cpp
Normal file
|
@ -0,0 +1,11 @@
|
|||
#include "MidiTrack.h"
|
||||
|
||||
MidiTrack::MidiTrack()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void MidiTrack::AddEvent(const MidiEvent& event)
|
||||
{
|
||||
mEvents.push_back(event);
|
||||
}
|
15
src/audio/midi/MidiTrack.h
Normal file
15
src/audio/midi/MidiTrack.h
Normal file
|
@ -0,0 +1,15 @@
|
|||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
#include "MidiEvent.h"
|
||||
|
||||
class MidiTrack
|
||||
{
|
||||
std::vector<MidiEvent> mEvents;
|
||||
|
||||
public:
|
||||
|
||||
MidiTrack();
|
||||
|
||||
void AddEvent(const MidiEvent& event);
|
||||
};
|
Loading…
Add table
Add a link
Reference in a new issue