294 lines
8.1 KiB
C++
294 lines
8.1 KiB
C++
|
|
#include "jsong.hpp"
|
|
#include <iostream>
|
|
#include <fstream>
|
|
#include <cassert>
|
|
|
|
namespace Jacker {
|
|
|
|
//=============================================================================
|
|
|
|
enum {
|
|
JSongVersion = 2,
|
|
};
|
|
|
|
//=============================================================================
|
|
|
|
void JSongWriter::collect(Json::Value &root, PatternEvent &event) {
|
|
root["frame"] = event.frame;
|
|
root["channel"] = event.channel;
|
|
root["param"] = event.param;
|
|
root["value"] = event.value;
|
|
}
|
|
|
|
void JSongWriter::collect(Json::Value &root, Pattern &pattern) {
|
|
root["name"] = pattern.name;
|
|
root["length"] = pattern.get_length();
|
|
root["channel_count"] = pattern.get_channel_count();
|
|
|
|
Json::Value events;
|
|
|
|
for (Pattern::iterator iter = pattern.begin();
|
|
iter != pattern.end(); ++iter) {
|
|
Json::Value event;
|
|
collect(event, iter->second);
|
|
if (!event.empty())
|
|
events.append(event);
|
|
}
|
|
|
|
if (!events.empty()) {
|
|
root["events"] = events;
|
|
}
|
|
}
|
|
|
|
void JSongWriter::collect(Json::Value &root, Song::Event &event) {
|
|
Pattern2IdMap::iterator iter = pattern2id.find(event.pattern);
|
|
if (iter == pattern2id.end())
|
|
return;
|
|
root["frame"] = event.frame;
|
|
root["track"] = event.track;
|
|
root["mute"] = event.mute;
|
|
root["pattern"] = iter->second;
|
|
}
|
|
|
|
void JSongWriter::collect(Json::Value &root, Song &song) {
|
|
Json::Value events;
|
|
|
|
for (Song::iterator iter = song.begin();
|
|
iter != song.end(); ++iter) {
|
|
Json::Value event;
|
|
collect(event, iter->second);
|
|
if (!event.empty())
|
|
events.append(event);
|
|
}
|
|
|
|
if (!events.empty()) {
|
|
root["events"] = events;
|
|
}
|
|
}
|
|
|
|
void JSongWriter::collect(Json::Value &root, Loop &loop) {
|
|
root["begin"] = loop.get_begin();
|
|
root["end"] = loop.get_end();
|
|
}
|
|
|
|
void JSongWriter::collect(Json::Value &root, Track &track) {
|
|
root["midi_channel"] = track.midi_channel;
|
|
root["midi_port"] = track.midi_port;
|
|
root["mute"] = track.mute;
|
|
root["name"] = track.name;
|
|
}
|
|
|
|
void JSongWriter::collect(Json::Value &root, Model &model) {
|
|
root["format"] = "jacker-song";
|
|
root["version"] = JSongVersion;
|
|
root["end_cue"] = model.end_cue;
|
|
root["frames_per_beat"] = model.frames_per_beat;
|
|
root["beats_per_bar"] = model.beats_per_bar;
|
|
root["beats_per_minute"] = model.beats_per_minute;
|
|
root["enable_loop"] = model.enable_loop;
|
|
|
|
Json::Value loop;
|
|
collect(loop, model.loop);
|
|
if (!loop.empty()) {
|
|
root["loop"] = loop;
|
|
}
|
|
|
|
Json::Value tracks;
|
|
for (TrackArray::iterator iter = model.tracks.begin();
|
|
iter != model.tracks.end(); ++iter) {
|
|
Json::Value track;
|
|
collect(track, *iter);
|
|
if (!track.empty()) {
|
|
tracks.append(track);
|
|
}
|
|
}
|
|
if (!tracks.empty()) {
|
|
root["tracks"] = tracks;
|
|
}
|
|
|
|
Json::Value patterns;
|
|
|
|
int index = 0;
|
|
for (PatternList::iterator iter = model.patterns.begin();
|
|
iter != model.patterns.end(); ++iter) {
|
|
Json::Value pattern;
|
|
collect(pattern, *(*iter));
|
|
if (!pattern.empty()) {
|
|
patterns.append(pattern);
|
|
pattern2id.insert(Pattern2IdMap::value_type(*iter,index));
|
|
index++;
|
|
}
|
|
}
|
|
|
|
if (!patterns.empty()) {
|
|
root["patterns"] = patterns;
|
|
}
|
|
|
|
Json::Value song;
|
|
collect(song, model.song);
|
|
if (!song.empty()) {
|
|
root["song"] = song;
|
|
}
|
|
}
|
|
|
|
void JSongWriter::write(Json::Value &root, const std::string &filepath) {
|
|
assert(!root.empty());
|
|
std::ofstream out(filepath.c_str());
|
|
assert(out);
|
|
out << root;
|
|
out.close();
|
|
}
|
|
|
|
//=============================================================================
|
|
|
|
bool JSongReader::extract(const Json::Value &value, std::string &target) {
|
|
if (!value.isString())
|
|
return false;
|
|
target = value.asString();
|
|
return true;
|
|
}
|
|
|
|
bool JSongReader::extract(const Json::Value &value, int &target) {
|
|
if (!value.isInt())
|
|
return false;
|
|
target = value.asInt();
|
|
return true;
|
|
}
|
|
|
|
bool JSongReader::extract(const Json::Value &value, bool &target) {
|
|
if (!value.isBool())
|
|
return false;
|
|
target = value.asBool();
|
|
return true;
|
|
}
|
|
|
|
void JSongReader::build(const Json::Value &root, Pattern::Event &event) {
|
|
extract(root["frame"], event.frame);
|
|
extract(root["channel"], event.channel);
|
|
extract(root["param"], event.param);
|
|
extract(root["value"], event.value);
|
|
}
|
|
|
|
void JSongReader::build(const Json::Value &root, Pattern &pattern) {
|
|
extract(root["name"], pattern.name);
|
|
int length = 64;
|
|
if (extract(root["length"], length))
|
|
pattern.set_length(length);
|
|
int channel_count = 1;
|
|
if (extract(root["channel_count"], channel_count))
|
|
pattern.set_channel_count(channel_count);
|
|
|
|
const Json::Value events = root["events"];
|
|
for (size_t i = 0; i < events.size(); ++i) {
|
|
Pattern::Event event;
|
|
build(events[i], event);
|
|
pattern.add_event(event);
|
|
}
|
|
|
|
patterns.push_back(&pattern);
|
|
}
|
|
|
|
bool JSongReader::build(const Json::Value &root, Song::Event &event) {
|
|
extract(root["frame"], event.frame);
|
|
extract(root["track"], event.track);
|
|
extract(root["mute"], event.mute);
|
|
int pattern_index = -1;
|
|
if (!extract(root["pattern"], pattern_index))
|
|
return false;
|
|
if ((pattern_index < 0)||(pattern_index >= (int)patterns.size()))
|
|
return false;
|
|
event.pattern = patterns[pattern_index];
|
|
return true;
|
|
}
|
|
|
|
void JSongReader::build(const Json::Value &root, Song &song) {
|
|
const Json::Value events = root["events"];
|
|
for (size_t i = 0; i < events.size(); ++i) {
|
|
Song::Event event;
|
|
if (build(events[i], event))
|
|
song.add_event(event);
|
|
}
|
|
}
|
|
|
|
void JSongReader::build(const Json::Value &root, Loop &loop) {
|
|
int begin = 0;
|
|
int end = 0;
|
|
extract(root["begin"], begin);
|
|
extract(root["end"], end);
|
|
loop.set(begin, end);
|
|
}
|
|
|
|
void JSongReader::build(const Json::Value &root, Track &track) {
|
|
extract(root["midi_channel"], track.midi_channel);
|
|
extract(root["midi_port"], track.midi_port);
|
|
extract(root["mute"], track.mute);
|
|
extract(root["name"], track.name);
|
|
}
|
|
|
|
void JSongReader::build(const Json::Value &root, Model &model) {
|
|
model.reset();
|
|
extract(root["end_cue"], model.end_cue);
|
|
extract(root["frames_per_beat"], model.frames_per_beat);
|
|
extract(root["beats_per_bar"], model.beats_per_bar);
|
|
extract(root["beats_per_minute"], model.beats_per_minute);
|
|
extract(root["enable_loop"], model.enable_loop);
|
|
|
|
const Json::Value loop = root["loop"];
|
|
if (!loop.empty()) {
|
|
build(loop, model.loop);
|
|
}
|
|
|
|
const Json::Value tracks = root["tracks"];
|
|
if (!tracks.empty())
|
|
model.tracks.clear();
|
|
for (size_t i = 0; i < tracks.size(); ++i) {
|
|
Track track;
|
|
build(tracks[i], track);
|
|
model.tracks.push_back(track);
|
|
}
|
|
|
|
const Json::Value patterns = root["patterns"];
|
|
for (size_t i = 0; i < patterns.size(); ++i) {
|
|
Pattern &pattern = model.new_pattern();
|
|
build(patterns[i], pattern);
|
|
}
|
|
|
|
const Json::Value song = root["song"];
|
|
build(song, model.song);
|
|
}
|
|
|
|
bool JSongReader::read(Json::Value &root, const std::string &filepath) {
|
|
Json::Reader reader;
|
|
std::ifstream inp(filepath.c_str());
|
|
bool result = reader.parse(inp, root);
|
|
if (!result) {
|
|
std::cout << "Error parsing JSong: " << reader.getFormatedErrorMessages();
|
|
}
|
|
inp.close();
|
|
if (root["format"] != "jacker-song")
|
|
return false;
|
|
return result;
|
|
}
|
|
|
|
//=============================================================================
|
|
|
|
bool read_jsong(Model &model, const std::string &filepath) {
|
|
JSongReader reader;
|
|
Json::Value root;
|
|
if (!reader.read(root, filepath))
|
|
return false;
|
|
reader.build(root,model);
|
|
return true;
|
|
}
|
|
|
|
void write_jsong(Model &model, const std::string &filepath) {
|
|
JSongWriter writer;
|
|
Json::Value root;
|
|
writer.collect(root,model);
|
|
writer.write(root,filepath);
|
|
}
|
|
|
|
//=============================================================================
|
|
|
|
} // namespace Jacker
|