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