1
1
#pragma once
2
2
3
+ #include < cstdint>
3
4
#include < fstream>
5
+ #include < sstream>
4
6
#include < string>
5
7
#include < vector>
6
8
@@ -14,27 +16,28 @@ template <typename T> T ByteSwap(T value, int length = 8)
14
16
return (value >> length) | (value << length);
15
17
}
16
18
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))
18
21
{
19
22
T chunk{};
20
- file .read (reinterpret_cast <char *>(&chunk), length);
23
+ stream .read (reinterpret_cast <char *>(&chunk), length);
21
24
return chunk;
22
25
}
23
26
24
- std::string ReadString (std::ifstream &file , int length)
27
+ std::string ReadString (std::istringstream &stream , int length)
25
28
{
26
29
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 : " " ;
29
32
}
30
33
31
- uint32_t ReadVarLen (std::ifstream &file )
34
+ uint32_t ReadVarLen (std::istringstream &stream )
32
35
{
33
36
uint32_t value = 0 ;
34
37
uint8_t byte = 0 ;
35
38
do
36
39
{
37
- byte = ReadChunk<uint8_t >(file );
40
+ byte = ReadChunk<uint8_t >(stream );
38
41
value = (value << 7 ) | (byte & 0x7F );
39
42
} while (byte & 0x80 );
40
43
return value;
@@ -44,60 +47,56 @@ auto STATUS_NOTE_EVENT = 0x90;
44
47
auto STATUS_META_EVENT = 0xFF ;
45
48
auto TYPE_END_OF_TRACK = 0x2F ;
46
49
47
- std::vector<Note> ReadMidiFile (const std::string &path )
50
+ std::vector<Note> ReadMidiData (const std::vector< uint8_t > &data )
48
51
{
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);
55
54
56
- if (ReadString (file , 4 ) != " MThd" )
55
+ if (ReadString (stream , 4 ) != " MThd" )
57
56
{
58
57
throw std::runtime_error (" Invalid MIDI file header" );
59
58
}
60
59
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 ));
65
64
66
65
std::vector<Note> notes;
67
66
68
67
for (int t = 0 ; t < tracks; t += 1 )
69
68
{
70
- if (ReadString (file , 4 ) != " MTrk" )
69
+ if (ReadString (stream , 4 ) != " MTrk" )
71
70
{
72
71
throw std::runtime_error (" Invalid track header" );
73
72
}
74
73
75
- auto trackLength = ByteSwap (ReadChunk<uint32_t >(file ));
74
+ auto trackLength = ByteSwap (ReadChunk<uint32_t >(stream ));
76
75
77
76
uint32_t absoluteTick = 0 ;
78
77
79
78
while (true )
80
79
{
81
- absoluteTick += ReadVarLen (file );
80
+ absoluteTick += ReadVarLen (stream );
82
81
83
- auto status = ReadChunk<uint8_t >(file );
82
+ auto status = ReadChunk<uint8_t >(stream );
84
83
85
84
if ((status & 0xF0 ) == STATUS_NOTE_EVENT)
86
85
{
87
86
Note note{.Position = static_cast <int >(absoluteTick),
88
- .HandPosition = ReadChunk<uint8_t >(file )};
87
+ .HandPosition = ReadChunk<uint8_t >(stream )};
89
88
90
- auto velocity = ReadChunk<uint8_t >(file );
89
+ auto velocity = ReadChunk<uint8_t >(stream );
91
90
92
91
notes.push_back (note);
93
92
}
94
93
else if (status == STATUS_META_EVENT)
95
94
{
96
- auto type = ReadChunk<uint8_t >(file );
95
+ auto type = ReadChunk<uint8_t >(stream );
97
96
98
- auto length = ReadVarLen (file );
97
+ auto length = ReadVarLen (stream );
99
98
100
- file .seekg (length, std::ios::cur);
99
+ stream .seekg (length, std::ios::cur);
101
100
102
101
if (type == TYPE_END_OF_TRACK)
103
102
{
@@ -106,16 +105,36 @@ std::vector<Note> ReadMidiFile(const std::string &path)
106
105
}
107
106
else if ((status & 0xF0 ) == 0xC0 || (status & 0xF0 ) == 0xD0 )
108
107
{
109
- file .seekg (1 , std::ios::cur);
108
+ stream .seekg (1 , std::ios::cur);
110
109
}
111
110
else
112
111
{
113
- file .seekg (2 , std::ios::cur);
112
+ stream .seekg (2 , std::ios::cur);
114
113
}
115
114
}
116
115
}
117
116
118
117
return notes;
119
118
}
120
119
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
+
121
140
} // namespace RhythmGameUtilities
0 commit comments