Compare commits

..

2 Commits

3 changed files with 297 additions and 1 deletions

272
assets/songs/json/mary.json Normal file
View File

@ -0,0 +1,272 @@
{
"header": {
"keySignatures": [],
"meta": [],
"name": "",
"ppq": 480,
"tempos": [
{
"bpm": 120,
"ticks": 0
}
],
"timeSignatures": [
{
"ticks": 0,
"timeSignature": [
4,
4
],
"measures": 0
}
]
},
"tracks": [
{
"channel": 0,
"controlChanges": {
"1": [
{
"number": 1,
"ticks": 0,
"time": 0,
"value": 0
}
],
"6": [
{
"number": 6,
"ticks": 0,
"time": 0,
"value": 0.5039370078740157
},
{
"number": 6,
"ticks": 0,
"time": 0,
"value": 0.5039370078740157
},
{
"number": 6,
"ticks": 0,
"time": 0,
"value": 0.09448818897637795
}
],
"7": [
{
"number": 7,
"ticks": 0,
"time": 0,
"value": 0.7874015748031497
}
],
"10": [
{
"number": 10,
"ticks": 0,
"time": 0,
"value": 0.5039370078740157
}
],
"11": [
{
"number": 11,
"ticks": 0,
"time": 0,
"value": 1
}
],
"38": [
{
"number": 38,
"ticks": 0,
"time": 0,
"value": 0
}
],
"100": [
{
"number": 100,
"ticks": 0,
"time": 0,
"value": 0.015748031496062992
},
{
"number": 100,
"ticks": 0,
"time": 0,
"value": 0.007874015748031496
},
{
"number": 100,
"ticks": 0,
"time": 0,
"value": 0
}
],
"101": [
{
"number": 101,
"ticks": 0,
"time": 0,
"value": 0
},
{
"number": 101,
"ticks": 0,
"time": 0,
"value": 0
},
{
"number": 101,
"ticks": 0,
"time": 0,
"value": 0
}
],
"121": [
{
"number": 121,
"ticks": 0,
"time": 0,
"value": 0
}
]
},
"pitchBends": [
{
"ticks": 0,
"time": 0,
"value": 0
}
],
"instrument": {
"family": "piano",
"number": 0,
"name": "acoustic grand piano"
},
"name": "",
"notes": [
{
"duration": 0.5,
"durationTicks": 480,
"midi": 64,
"name": "E4",
"ticks": 0,
"time": 0,
"velocity": 0.7874015748031497
},
{
"duration": 0.5,
"durationTicks": 480,
"midi": 62,
"name": "D4",
"ticks": 480,
"time": 0.5,
"velocity": 0.7874015748031497
},
{
"duration": 0.5,
"durationTicks": 480,
"midi": 60,
"name": "C4",
"ticks": 960,
"time": 1,
"velocity": 0.7874015748031497
},
{
"duration": 0.5,
"durationTicks": 480,
"midi": 62,
"name": "D4",
"ticks": 1440,
"time": 1.5,
"velocity": 0.7874015748031497
},
{
"duration": 0.5,
"durationTicks": 480,
"midi": 64,
"name": "E4",
"ticks": 1920,
"time": 2,
"velocity": 0.7874015748031497
},
{
"duration": 0.5,
"durationTicks": 480,
"midi": 64,
"name": "E4",
"ticks": 2400,
"time": 2.5,
"velocity": 0.7874015748031497
},
{
"duration": 1,
"durationTicks": 960,
"midi": 64,
"name": "E4",
"ticks": 2880,
"time": 3,
"velocity": 0.7874015748031497
},
{
"duration": 0.5,
"durationTicks": 480,
"midi": 62,
"name": "D4",
"ticks": 3840,
"time": 4,
"velocity": 0.7874015748031497
},
{
"duration": 0.5,
"durationTicks": 480,
"midi": 62,
"name": "D4",
"ticks": 4320,
"time": 4.5,
"velocity": 0.7874015748031497
},
{
"duration": 1,
"durationTicks": 960,
"midi": 62,
"name": "D4",
"ticks": 4800,
"time": 5,
"velocity": 0.7874015748031497
},
{
"duration": 0.5,
"durationTicks": 480,
"midi": 64,
"name": "E4",
"ticks": 5760,
"time": 6,
"velocity": 0.7874015748031497
},
{
"duration": 0.5,
"durationTicks": 480,
"midi": 67,
"name": "G4",
"ticks": 6240,
"time": 6.5,
"velocity": 0.7874015748031497
},
{
"duration": 1,
"durationTicks": 960,
"midi": 67,
"name": "G4",
"ticks": 6720,
"time": 7,
"velocity": 0.7874015748031497
}
],
"endOfTrackTicks": 7680
}
]
}

View File

@ -10,7 +10,9 @@ struct Note {
std::string name; std::string name;
int midi; int midi;
int duration_ticks; int duration_ticks;
int duration_ms; // Duration of the note in milliseconds
int ticks; int ticks;
int position_ms; // How many milliseconds into the song the note occurs
}; };
struct Instrument { struct Instrument {
@ -26,6 +28,9 @@ struct Track {
struct Header { struct Header {
std::string name; std::string name;
int ppq;
float bpm;
double tempo;
}; };
struct Song { struct Song {
@ -33,6 +38,13 @@ struct Song {
std::vector<Track> tracks; std::vector<Track> tracks;
}; };
int ticksToMilliseconds(int ticks, double tempo, int ppq) {
double microseconds = (ticks * tempo) / ppq;
// Convert to milliseconds with rounding
return static_cast<int>(microseconds / 1000.0 + 0.5);
}
Song parseSong(const rapidjson::Document& doc) { Song parseSong(const rapidjson::Document& doc) {
assert(doc.IsObject()); assert(doc.IsObject());
@ -42,6 +54,13 @@ Song parseSong(const rapidjson::Document& doc) {
const auto& h = doc["header"].GetObject(); const auto& h = doc["header"].GetObject();
Header header; Header header;
header.name = h["name"].GetString(); header.name = h["name"].GetString();
header.ppq = h["ppq"].GetInt();
printf("header has tempos: %d\n", h.HasMember("tempos"));
if (h.HasMember("tempos") && h["tempos"].IsArray()) {
const auto& tempos = h["tempos"].GetArray();
header.bpm = tempos[0]["bpm"].GetFloat();
header.tempo = 60000000.0 / header.bpm;
}
song.header = header; song.header = header;
} }
@ -66,7 +85,9 @@ Song parseSong(const rapidjson::Document& doc) {
note.name = n["name"].GetString(); note.name = n["name"].GetString();
note.midi = n["midi"].GetInt(); note.midi = n["midi"].GetInt();
note.duration_ticks = n["durationTicks"].GetInt(); note.duration_ticks = n["durationTicks"].GetInt();
note.duration_ms = ticksToMilliseconds(note.duration_ticks, song.header.tempo, song.header.ppq);
note.ticks = n["ticks"].GetInt(); note.ticks = n["ticks"].GetInt();
note.position_ms = ticksToMilliseconds(note.ticks, song.header.tempo, song.header.ppq);
track.notes.push_back(note); track.notes.push_back(note);
} }
} }

View File

@ -32,8 +32,11 @@ int main(int argc, char** argv)
game.add_scene<TitleScreen>("title"); game.add_scene<TitleScreen>("title");
game.add_scene<GHHBScene>("ghhb"); game.add_scene<GHHBScene>("ghhb");
Song& song = song_manager->load_song("tetris", "assets/songs/json/tetris.json"); Song& song = song_manager->load_song("mary_had_a_lil_lamb", "assets/songs/json/mary.json");
printf("Song name: %s\n", song.header.name.c_str()); printf("Song name: %s\n", song.header.name.c_str());
printf("Song bpm: %f\n", song.header.bpm);
printf("Song tempo: %f\n", song.header.tempo);
printf("First note duration: %d\n", song.tracks[0].notes[0].duration_ms);
#ifdef __EMSCRIPTEN__ #ifdef __EMSCRIPTEN__