11#pragma once
22
3+ #include < cstdint>
34#include < fstream>
5+ #include < sstream>
46#include < string>
57#include < vector>
68
@@ -14,27 +16,28 @@ template <typename T> T ByteSwap(T value, int length = 8)
1416 return (value >> length) | (value << length);
1517}
1618
17- template <typename T> T ReadChunk (std::ifstream &file, int length = sizeof (T))
19+ template <typename T>
20+ T ReadChunk (std::istringstream &stream, int length = sizeof (T))
1821{
1922 T chunk{};
20- file .read (reinterpret_cast <char *>(&chunk), length);
23+ stream .read (reinterpret_cast <char *>(&chunk), length);
2124 return chunk;
2225}
2326
24- std::string ReadString (std::ifstream &file , int length)
27+ std::string ReadString (std::istringstream &stream , int length)
2528{
2629 std::string chunk (length, ' \0 ' );
27- file .read (&chunk[0 ], length);
28- return file .gcount () == length ? chunk : " " ;
30+ stream .read (&chunk[0 ], length);
31+ return stream .gcount () == length ? chunk : " " ;
2932}
3033
31- uint32_t ReadVarLen (std::ifstream &file )
34+ uint32_t ReadVarLen (std::istringstream &stream )
3235{
3336 uint32_t value = 0 ;
3437 uint8_t byte = 0 ;
3538 do
3639 {
37- byte = ReadChunk<uint8_t >(file );
40+ byte = ReadChunk<uint8_t >(stream );
3841 value = (value << 7 ) | (byte & 0x7F );
3942 } while (byte & 0x80 );
4043 return value;
@@ -44,60 +47,56 @@ auto STATUS_NOTE_EVENT = 0x90;
4447auto STATUS_META_EVENT = 0xFF ;
4548auto TYPE_END_OF_TRACK = 0x2F ;
4649
47- std::vector<Note> ReadMidiFile (const std::string &path )
50+ std::vector<Note> ReadMidiData (const std::vector< uint8_t > &data )
4851{
49- std::ifstream file (path, std::ios::binary);
50-
51- if (!file.is_open ())
52- {
53- throw std::runtime_error (" Cannot open MIDI file" );
54- }
52+ std::istringstream stream (std::string (data.begin (), data.end ()),
53+ std::ios::binary);
5554
56- if (ReadString (file , 4 ) != " MThd" )
55+ if (ReadString (stream , 4 ) != " MThd" )
5756 {
5857 throw std::runtime_error (" Invalid MIDI file header" );
5958 }
6059
61- auto headerLength = ByteSwap (ReadChunk<uint32_t >(file ));
62- auto format = ByteSwap (ReadChunk<uint16_t >(file ));
63- auto tracks = ByteSwap (ReadChunk<uint16_t >(file ));
64- auto resolution = ByteSwap (ReadChunk<uint16_t >(file ));
60+ auto headerLength = ByteSwap (ReadChunk<uint32_t >(stream ));
61+ auto format = ByteSwap (ReadChunk<uint16_t >(stream ));
62+ auto tracks = ByteSwap (ReadChunk<uint16_t >(stream ));
63+ auto resolution = ByteSwap (ReadChunk<uint16_t >(stream ));
6564
6665 std::vector<Note> notes;
6766
6867 for (int t = 0 ; t < tracks; t += 1 )
6968 {
70- if (ReadString (file , 4 ) != " MTrk" )
69+ if (ReadString (stream , 4 ) != " MTrk" )
7170 {
7271 throw std::runtime_error (" Invalid track header" );
7372 }
7473
75- auto trackLength = ByteSwap (ReadChunk<uint32_t >(file ));
74+ auto trackLength = ByteSwap (ReadChunk<uint32_t >(stream ));
7675
7776 uint32_t absoluteTick = 0 ;
7877
7978 while (true )
8079 {
81- absoluteTick += ReadVarLen (file );
80+ absoluteTick += ReadVarLen (stream );
8281
83- auto status = ReadChunk<uint8_t >(file );
82+ auto status = ReadChunk<uint8_t >(stream );
8483
8584 if ((status & 0xF0 ) == STATUS_NOTE_EVENT)
8685 {
8786 Note note{.Position = static_cast <int >(absoluteTick),
88- .HandPosition = ReadChunk<uint8_t >(file )};
87+ .HandPosition = ReadChunk<uint8_t >(stream )};
8988
90- auto velocity = ReadChunk<uint8_t >(file );
89+ auto velocity = ReadChunk<uint8_t >(stream );
9190
9291 notes.push_back (note);
9392 }
9493 else if (status == STATUS_META_EVENT)
9594 {
96- auto type = ReadChunk<uint8_t >(file );
95+ auto type = ReadChunk<uint8_t >(stream );
9796
98- auto length = ReadVarLen (file );
97+ auto length = ReadVarLen (stream );
9998
100- file .seekg (length, std::ios::cur);
99+ stream .seekg (length, std::ios::cur);
101100
102101 if (type == TYPE_END_OF_TRACK)
103102 {
@@ -106,16 +105,36 @@ std::vector<Note> ReadMidiFile(const std::string &path)
106105 }
107106 else if ((status & 0xF0 ) == 0xC0 || (status & 0xF0 ) == 0xD0 )
108107 {
109- file .seekg (1 , std::ios::cur);
108+ stream .seekg (1 , std::ios::cur);
110109 }
111110 else
112111 {
113- file .seekg (2 , std::ios::cur);
112+ stream .seekg (2 , std::ios::cur);
114113 }
115114 }
116115 }
117116
118117 return notes;
119118}
120119
120+ std::vector<Note> ReadMidiFile (const std::string &path)
121+ {
122+ std::ifstream file (path, std::ios::binary | std::ios::ate);
123+
124+ if (!file.is_open ())
125+ {
126+ throw std::runtime_error (" Cannot open MIDI file" );
127+ }
128+
129+ auto fileSize = file.tellg ();
130+
131+ std::vector<uint8_t > buffer (static_cast <size_t >(fileSize));
132+
133+ file.seekg (0 , std::ios::beg);
134+
135+ file.read (reinterpret_cast <char *>(buffer.data ()), fileSize);
136+
137+ return ReadMidiData (buffer);
138+ }
139+
121140} // namespace RhythmGameUtilities
0 commit comments