From 56d56947c9f6a86c431f353d77084af121a3af26 Mon Sep 17 00:00:00 2001 From: 0xFEEDC0DE64 Date: Tue, 27 Dec 2022 22:47:46 +0100 Subject: [PATCH] Implemented parsing of loop station presets --- DrumMachine.pro | 6 + drumpadjsonconverters.cpp | 376 ++++++++++++++++++++++++++++++++++ drumpadjsonconverters.h | 27 +++ drumpadpresets.cpp | 5 +- drumpadpresets.h | 7 +- jsonconverters.cpp | 375 +++++---------------------------- jsonconverters.h | 25 +-- loopstationjsonconverters.cpp | 239 +++++++++++++++++++++ loopstationjsonconverters.h | 24 +++ loopstationpresets.cpp | 2 + loopstationpresets.h | 62 ++++++ widgets/drumpadwidget.cpp | 6 +- widgets/drumpadwidget.ui | 2 +- widgets/loopstationwidget.cpp | 81 +++++++- widgets/loopstationwidget.h | 16 +- widgets/loopstationwidget.ui | 99 +++++++-- 16 files changed, 981 insertions(+), 371 deletions(-) create mode 100644 drumpadjsonconverters.cpp create mode 100644 drumpadjsonconverters.h create mode 100644 loopstationjsonconverters.cpp create mode 100644 loopstationjsonconverters.h create mode 100644 loopstationpresets.cpp create mode 100644 loopstationpresets.h diff --git a/DrumMachine.pro b/DrumMachine.pro index d4cccd8..ad42e0a 100755 --- a/DrumMachine.pro +++ b/DrumMachine.pro @@ -16,10 +16,13 @@ SOURCES += \ audioplayer.cpp \ drummachinesettings.cpp \ drumpadfilesmodel.cpp \ + drumpadjsonconverters.cpp \ drumpadpresets.cpp \ drumpadpresetsmodel.cpp \ graphrenderer.cpp \ jsonconverters.cpp \ + loopstationjsonconverters.cpp \ + loopstationpresets.cpp \ main.cpp \ midicontainers.cpp \ midiinwrapper.cpp \ @@ -46,10 +49,13 @@ HEADERS += \ audioplayer.h \ drummachinesettings.h \ drumpadfilesmodel.h \ + drumpadjsonconverters.h \ drumpadpresets.h \ drumpadpresetsmodel.h \ graphrenderer.h \ jsonconverters.h \ + loopstationjsonconverters.h \ + loopstationpresets.h \ midicontainers.h \ midiinwrapper.h \ midioutwrapper.h \ diff --git a/drumpadjsonconverters.cpp b/drumpadjsonconverters.cpp new file mode 100644 index 0000000..b922d88 --- /dev/null +++ b/drumpadjsonconverters.cpp @@ -0,0 +1,376 @@ +#include "drumpadjsonconverters.h" + +#include "jsonconverters.h" + +namespace json_converters { +namespace drumpad { + +drumpad_presets::PresetsConfig parsePresetsConfig(const QJsonObject &jsonObj) +{ + drumpad_presets::PresetsConfig presetConfig; + + for (auto iter = std::cbegin(jsonObj); iter != std::cend(jsonObj); iter++) + { + if (iter.key() == "categories") + presetConfig.categories = parseCategoryVector(iter.value()); + else if (iter.key() == "presets") + presetConfig.presets = parsePresetMap(iter.value()); + else + throw std::runtime_error{QString{"unknown key %0 (%1) for PresetConfig"}.arg(iter.key(), jsonTypeToString(iter->type())).toStdString()}; + } + + return presetConfig; +} + +std::vector parseCategoryVector(const QJsonValue &jsonValue) +{ + if (!jsonValue.isArray()) + throw std::runtime_error{QString{"json value for vector of Category is not an array but %0"}.arg(jsonTypeToString(jsonValue.type())).toStdString()}; + + std::vector vector; + + for (const auto &jsonValue : jsonValue.toArray()) + vector.emplace_back(parseCategory(jsonValue)); + + return vector; +} + +std::map parsePresetMap(const QJsonValue &jsonValue) +{ + if (!jsonValue.isObject()) + throw std::runtime_error{QString{"json value for Preset map is not an object but %0"}.arg(jsonTypeToString(jsonValue.type())).toStdString()}; + + const auto jsonObj = jsonValue.toObject(); + + std::map map; + + for (auto iter = std::cbegin(jsonObj); iter != std::cend(jsonObj); iter++) + map[iter.key()] = parsePreset(iter.value()); + + return map; +} + +drumpad_presets::Category parseCategory(const QJsonValue &jsonValue) +{ + if (!jsonValue.isObject()) + throw std::runtime_error{QString{"json value for Category is not an object but %0"}.arg(jsonTypeToString(jsonValue.type())).toStdString()}; + + const auto jsonObj = jsonValue.toObject(); + + drumpad_presets::Category category; + + for (auto iter = std::cbegin(jsonObj); iter != std::cend(jsonObj); iter++) + { + if (iter.key() == "title") + category.title = parseString(iter.value()); + else if (iter.key() == "filter") + category.filter = parseFilter(iter.value()); + else + throw std::runtime_error{QString{"unknown key %0 (%1) for Category"}.arg(iter.key(), jsonTypeToString(iter->type())).toStdString()}; + } + + return category; +} + +drumpad_presets::Filter parseFilter(const QJsonValue &jsonValue) +{ + if (!jsonValue.isObject()) + throw std::runtime_error{QString{"json value for Filters is not an object but %0"}.arg(jsonTypeToString(jsonValue.type())).toStdString()}; + + const auto jsonObj = jsonValue.toObject(); + + drumpad_presets::Filter filters; + + for (auto iter = std::cbegin(jsonObj); iter != std::cend(jsonObj); iter++) + { + if (iter.key() == "tags") + filters.tags = parseStringVector(iter.value()); + else + throw std::runtime_error{QString{"unknown key %0 (%1) for Filters"}.arg(iter.key(), jsonTypeToString(iter->type())).toStdString()}; + } + + return filters; +} + +drumpad_presets::Preset parsePreset(const QJsonValue &jsonValue) +{ + if (!jsonValue.isObject()) + throw std::runtime_error{QString{"json value for Preset is not an object but %0"}.arg(jsonTypeToString(jsonValue.type())).toStdString()}; + + const auto jsonObj = jsonValue.toObject(); + + drumpad_presets::Preset preset; + + for (auto iter = std::cbegin(jsonObj); iter != std::cend(jsonObj); iter++) + { + const auto key = iter.key(); + if (key == "id") + preset.id = parseString(iter.value()); + else if (key == "name") + preset.name = parseString(iter.value()); + else if (key == "author") + preset.author = parseString(iter.value()); + else if (key == "orderBy") + preset.orderBy = parseString(iter.value()); + else if (key == "version") + preset.version = parseString(iter.value()); + else if (key == "tempo") + preset.tempo = parseInt(iter.value()); + else if (key == "icon") + preset.icon = parseString(iter.value()); + else if (key == "price") + preset.price = parseInt(iter.value()); + else if (key == "priceForSession") + preset.priceForSession = parseInt(iter.value()); + else if (key == "hasInfo") + preset.hasInfo = parseBool(iter.value()); + else if (key == "tags") + preset.tags = parseStringVector(iter.value()); + else if (key == "DELETED") + preset.DELETED = parseBool(iter.value()); + else if (key == "difficulty") + preset.difficulty = parseInt(iter.value()); + else if (key == "sample") + preset.sample = parseInt(iter.value()); + else if (key == "audioPreview1Name") + preset.audioPreview1Name = parseString(iter.value()); + else if (key == "audioPreview1URL") + preset.audioPreview1URL = parseString(iter.value()); + else if (key == "audioPreview2Name") + preset.audioPreview2Name = parseString(iter.value()); + else if (key == "audioPreview2URL") + preset.audioPreview2URL = parseString(iter.value()); + else if (key == "imagePreview1") + preset.imagePreview1 = parseString(iter.value()); + else if (key == "videoPreview") + preset.videoPreview = parseString(iter.value()); + else if (key == "videoTutorial") + preset.videoTutorial = parseString(iter.value()); + else if (key == "files") + preset.files = parseFileArray(iter.value()); + else if (key == "beatSchool") + preset.beatSchool = parseSequenceVectorMap(iter.value()); + else if (key == "easyPlay") + preset.easyPlay = parseSequenceVectorMap(iter.value()); + else if (key == "timestamp") + preset.timestamp = QDateTime::fromSecsSinceEpoch(iter.value().toDouble()); + else if (key == "middleDescription") + { + //preset.middleDescription = parseString(iter.value()); + + // example value: + // [ + // { + // "type": "ButtonsRow", + // "buttons": [ + // { + // "type": "ImageButton", + // "action": "openLink", + // "link": "https://www.soundcloud.com/matta-music", + // "icon": "soundcloud" + // }, + // { + // "type": "ImageButton", + // "action": "openLink", + // "link": "https://www.facebook.com/MattaOfficial", + // "icon": "facebook" + // }, + // { + // "type": "ImageButton", + // "action": "openLink", + // "link": "https://www.twitter.com/matta_music", + // "icon": "twitter" + // }, + // { + // "type": "ImageButton", + // "action": "openLink", + // "link": "https://pro.beatport.com/artist/matta/118779", + // "icon": "beatport" + // }, + // { + // "type": "ImageButton", + // "action": "openLink", + // "link": "https://www.mattaofficial.com", + // "icon": "mattaofficial" + // } + // ] + // } + // ] + } + else + throw std::runtime_error{QString{"unknown key %0 (%1) for Preset"}.arg(key, jsonTypeToString(iter->type())).toStdString()}; + } + + return preset; +} + +std::array parseFileArray(const QJsonValue &jsonValue) +{ + if (!jsonValue.isObject()) + throw std::runtime_error{QString{"json value for File array is not an object but %0"}.arg(jsonTypeToString(jsonValue.type())).toStdString()}; + + const auto jsonObj = jsonValue.toObject(); + + if (jsonObj.size() != 24) + throw std::runtime_error{QString{"json value for File array doesn't have exactly 24 entries but %0"}.arg(jsonObj.size()).toStdString()}; + + std::array array; + + for (auto iter = std::cbegin(jsonObj); iter != std::cend(jsonObj); iter++) + { + bool ok; + const auto index = iter.key().toInt(&ok); + + if (!ok || index < 1 || index >= 25) + throw std::runtime_error{QString{"unknown key %0 (%1) for File"}.arg(iter.key(), jsonTypeToString(iter->type())).toStdString()}; + + array[index - 1] = parseFile(iter.value()); + } + + return array; +} + +drumpad_presets::File parseFile(const QJsonValue &jsonValue) +{ + if (!jsonValue.isObject()) + throw std::runtime_error{QString{"json value for File is not an object but %0"}.arg(jsonTypeToString(jsonValue.type())).toStdString()}; + + const auto jsonObj = jsonValue.toObject(); + + drumpad_presets::File file; + + for (auto iter = std::cbegin(jsonObj); iter != std::cend(jsonObj); iter++) + { + const auto key = iter.key(); + if (key == "filename") + file.filename = parseString(iter.value()); + else if (key == "color") + file.color = parseString(iter.value()); + else if (key == "stopOnRelease") + file.stopOnRelease = parseString(iter.value()); + else if (key == "looped") + file.looped = parseBool(iter.value()); + else if (key == "choke") + file.choke = parseInt(iter.value()); + else + throw std::runtime_error{QString{"unknown key %0 (%1) for File"}.arg(key, jsonTypeToString(iter->type())).toStdString()}; + } + + return file; +} + +std::vector parseSequenceVector(const QJsonValue &jsonValue) +{ + if (!jsonValue.isArray()) + throw std::runtime_error{QString{"json value for vector of Sequence is not an array but %0"}.arg(jsonTypeToString(jsonValue.type())).toStdString()}; + + std::vector vector; + + for (const auto &jsonValue : jsonValue.toArray()) + vector.emplace_back(parseSequence(jsonValue)); + + return vector; +} + +drumpad_presets::Sequence parseSequence(const QJsonValue &jsonValue) +{ + if (!jsonValue.isObject()) + throw std::runtime_error{QString{"json value for File is not an object but %0"}.arg(jsonTypeToString(jsonValue.type())).toStdString()}; + + const auto jsonObj = jsonValue.toObject(); + + drumpad_presets::Sequence sequence; + + for (auto iter = std::cbegin(jsonObj); iter != std::cend(jsonObj); iter++) + { + const auto key = iter.key(); + if (key == "name") + sequence.name = parseString(iter.value()); + else if (key == "id") + sequence.id = parseInt(iter.value()); + else if (key == "version") + sequence.version = parseInt(iter.value()); + else if (key == "orderBy") + sequence.orderBy = parseInt(iter.value()); + else if (key == "sequencerSize") + sequence.sequencerSize = parseInt(iter.value()); + else if (key == "pads") + sequence.pads = parseSequencePadVectorMap(iter.value()); + else if (key == "embientPads") + sequence.embientPads = parseIntVectorIgnoreNulls(iter.value()); + else + throw std::runtime_error{QString{"unknown key %0 (%1) for Sequence"}.arg(key, jsonTypeToString(iter->type())).toStdString()}; + } + + return sequence; +} + +std::map> parseSequenceVectorMap(const QJsonValue &jsonValue) +{ + if (!jsonValue.isObject()) + throw std::runtime_error{QString{"json value for Sequence vector map is not an object but %0"}.arg(jsonTypeToString(jsonValue.type())).toStdString()}; + + const auto jsonObj = jsonValue.toObject(); + + std::map> map; + + for (auto iter = std::cbegin(jsonObj); iter != std::cend(jsonObj); iter++) + map[iter.key()] = parseSequenceVector(iter.value()); + + return map; +} + +drumpad_presets::SequencePad parseSequencePad(const QJsonValue &jsonValue) +{ + if (!jsonValue.isObject()) + throw std::runtime_error{QString{"json value for File is not an object but %0"}.arg(jsonTypeToString(jsonValue.type())).toStdString()}; + + const auto jsonObj = jsonValue.toObject(); + + drumpad_presets::SequencePad sequencePad; + + for (auto iter = std::cbegin(jsonObj); iter != std::cend(jsonObj); iter++) + { + if (iter.key() == "start") + sequencePad.start = parseInt(iter.value()); + else if (iter.key() == "duration") + sequencePad.duration = parseInt(iter.value()); + else if (iter.key() == "embient") + sequencePad.embient = parseBool(iter.value()); + else + throw std::runtime_error{QString{"unknown key %0 (%1) for Sequence"}.arg(iter.key(), jsonTypeToString(iter->type())).toStdString()}; + } + + return sequencePad; +} + +std::vector parseSequencePadVector(const QJsonValue &jsonValue) +{ + if (!jsonValue.isArray()) + throw std::runtime_error{QString{"json value for vector of SequencePad is not an array but %0"}.arg(jsonTypeToString(jsonValue.type())).toStdString()}; + + std::vector vector; + + for (const auto &jsonValue : jsonValue.toArray()) + vector.emplace_back(parseSequencePad(jsonValue)); + + return vector; +} + +std::map> parseSequencePadVectorMap(const QJsonValue &jsonValue) +{ + if (!jsonValue.isObject()) + throw std::runtime_error{QString{"json value for SequencePad vector map is not an object but %0"}.arg(jsonTypeToString(jsonValue.type())).toStdString()}; + + const auto jsonObj = jsonValue.toObject(); + + std::map> map; + + for (auto iter = std::cbegin(jsonObj); iter != std::cend(jsonObj); iter++) + map[iter.key()] = parseSequencePadVector(iter.value()); + + return map; +} + +} // namespace drumpad +} // namespace json_converters diff --git a/drumpadjsonconverters.h b/drumpadjsonconverters.h new file mode 100644 index 0000000..ed16714 --- /dev/null +++ b/drumpadjsonconverters.h @@ -0,0 +1,27 @@ +#pragma once + +#include "drumpadpresets.h" + +class QJsonObject; +class QJsonValue; + +namespace json_converters { +namespace drumpad { + +drumpad_presets::PresetsConfig parsePresetsConfig(const QJsonObject &jsonObj); +std::vector parseCategoryVector(const QJsonValue &jsonValue); +std::map parsePresetMap(const QJsonValue &jsonValue); +drumpad_presets::Category parseCategory(const QJsonValue &jsonValue); +drumpad_presets::Filter parseFilter(const QJsonValue &jsonValue); +drumpad_presets::Preset parsePreset(const QJsonValue &jsonValue); +std::array parseFileArray(const QJsonValue &jsonValue); +drumpad_presets::File parseFile(const QJsonValue &jsonValue); +std::vector parseSequenceVector(const QJsonValue &jsonValue); +drumpad_presets::Sequence parseSequence(const QJsonValue &jsonValue); +std::map> parseSequenceVectorMap(const QJsonValue &jsonValue); +drumpad_presets::SequencePad parseSequencePad(const QJsonValue &jsonValue); +std::vector parseSequencePadVector(const QJsonValue &jsonValue); +std::map> parseSequencePadVectorMap(const QJsonValue &jsonValue); + +} // namespace drumpad +} // namespace json_converters diff --git a/drumpadpresets.cpp b/drumpadpresets.cpp index 164d977..d5208e6 100755 --- a/drumpadpresets.cpp +++ b/drumpadpresets.cpp @@ -1,7 +1,6 @@ #include "drumpadpresets.h" -namespace drumpad_presets -{ +namespace drumpad_presets { bool File::operator==(const File &other) const { @@ -12,4 +11,4 @@ bool File::operator==(const File &other) const choke == other.choke; } -} +} // namespace drumpad_presets diff --git a/drumpadpresets.h b/drumpadpresets.h index c17ea53..095dd3d 100755 --- a/drumpadpresets.h +++ b/drumpadpresets.h @@ -8,8 +8,8 @@ #include #include -namespace drumpad_presets -{ +namespace drumpad_presets { + struct Filter { std::optional> tags; @@ -84,4 +84,5 @@ struct PresetsConfig std::optional> categories; std::optional> presets; }; -} + +} // namespace drumpad_presets diff --git a/jsonconverters.cpp b/jsonconverters.cpp index 850f009..145cd36 100755 --- a/jsonconverters.cpp +++ b/jsonconverters.cpp @@ -4,8 +4,25 @@ #include -namespace json_converters +namespace json_converters { + +QString jsonTypeToString(QJsonValue::Type type) { + switch (type) + { + case QJsonValue::Null: return "Null"; + case QJsonValue::Bool: return "Bool"; + case QJsonValue::Double: return "Double"; + case QJsonValue::String: return "String"; + case QJsonValue::Array: return "Array"; + case QJsonValue::Object: return "Object"; + case QJsonValue::Undefined: return "Undefined"; + default: + qWarning() << "unknown jsonType" << type; + return QString{"unknown jsonType %0"}.arg(type); + } +} + QJsonObject loadJson(const QByteArray &buffer) { QJsonParseError error; @@ -22,7 +39,7 @@ QJsonObject loadJson(const QByteArray &buffer) QString parseString(const QJsonValue &jsonValue) { if (!jsonValue.isString()) - throw std::runtime_error{"json value for string is not a string"}; + throw std::runtime_error{QString{"json value for string is not a string but %0"}.arg(jsonTypeToString(jsonValue.type())).toStdString()}; return jsonValue.toString(); } @@ -30,7 +47,7 @@ QString parseString(const QJsonValue &jsonValue) std::vector parseStringVector(const QJsonValue &jsonValue) { if (!jsonValue.isArray()) - throw std::runtime_error{"json value for string vector is not an array"}; + throw std::runtime_error{QString{"json value for string vector is not an array but %0"}.arg(jsonTypeToString(jsonValue.type())).toStdString()}; std::vector vector; @@ -43,7 +60,7 @@ std::vector parseStringVector(const QJsonValue &jsonValue) int parseInt(const QJsonValue &jsonValue) { if (!jsonValue.isDouble()) - throw std::runtime_error{"json value for int is not a double"}; + throw std::runtime_error{QString{"json value for int is not a double but %0"}.arg(jsonTypeToString(jsonValue.type())).toStdString()}; return jsonValue.toDouble(); } @@ -51,15 +68,33 @@ int parseInt(const QJsonValue &jsonValue) bool parseBool(const QJsonValue &jsonValue) { if (!jsonValue.isBool()) - throw std::runtime_error{"json value for bool is not a bool"}; + throw std::runtime_error{QString{"json value for bool is not a bool but %0"}.arg(jsonTypeToString(jsonValue.type())).toStdString()}; return jsonValue.toBool(); } +bool parseBoolStr(const QJsonValue &jsonValue) +{ + if (jsonValue.isBool()) + return jsonValue.toBool(); + else if (jsonValue.isString()) + { + if (const auto &str = jsonValue.toString(); str == "true") + return true; + else if (str == "false") + return false; + else + throw std::runtime_error{QString{"json value for str-bool has invalid str value %0"}.arg(str).toStdString()}; + } + else + throw std::runtime_error{QString{"json value for str-bool is not a bool or string but %0"}.arg(jsonTypeToString(jsonValue.type())).toStdString()}; + +} + std::vector parseIntVector(const QJsonValue &jsonValue) { if (!jsonValue.isArray()) - throw std::runtime_error{"json value for int vector is not an array"}; + throw std::runtime_error{QString{"json value for int vector is not an array but %0"}.arg(jsonTypeToString(jsonValue.type())).toStdString()}; std::vector vector; @@ -72,7 +107,7 @@ std::vector parseIntVector(const QJsonValue &jsonValue) std::vector parseIntVectorIgnoreNulls(const QJsonValue &jsonValue) { if (!jsonValue.isArray()) - throw std::runtime_error{"json value for int vector is not an array"}; + throw std::runtime_error{QString{"json value for int vector is not an array but %0"}.arg(jsonTypeToString(jsonValue.type())).toStdString()}; std::vector vector; @@ -83,330 +118,24 @@ std::vector parseIntVectorIgnoreNulls(const QJsonValue &jsonValue) return vector; } -drumpad_presets::PresetsConfig parseDrumPadPresetsConfig(const QJsonObject &jsonObj) -{ - drumpad_presets::PresetsConfig presetConfig; - - for (auto iter = std::cbegin(jsonObj); iter != std::cend(jsonObj); iter++) - { - if (iter.key() == "categories") - presetConfig.categories = parseCategoryVector(iter.value()); - else if (iter.key() == "presets") - presetConfig.presets = parsePresetMap(iter.value()); - else - throw std::runtime_error{QString{"unknown key %0 for PresetConfig"}.arg(iter.key()).toStdString()}; - } - - return presetConfig; -} - -std::vector parseCategoryVector(const QJsonValue &jsonValue) +template +std::array parseStringArray(const QJsonValue &jsonValue) { if (!jsonValue.isArray()) - throw std::runtime_error{"json value for vector of Category is not an array"}; + throw std::runtime_error{QString{"json value for array of PadType is not an array but %0"}.arg(jsonTypeToString(jsonValue.type())).toStdString()}; - std::vector vector; + const auto jsonArr = jsonValue.toArray(); - for (const auto &jsonValue : jsonValue.toArray()) - vector.emplace_back(parseCategory(jsonValue)); + if (jsonArr.size() != LENGTH) + throw std::runtime_error{QString{"json value for PadType array doesn't have exactly %0 entries but %1"}.arg(LENGTH).arg(jsonArr.size()).toStdString()}; - return vector; -} + std::array array; -std::map parsePresetMap(const QJsonValue &jsonValue) -{ - if (!jsonValue.isObject()) - throw std::runtime_error{"json value for Preset map is not an object"}; - - const auto jsonObj = jsonValue.toObject(); - - std::map map; - - for (auto iter = std::cbegin(jsonObj); iter != std::cend(jsonObj); iter++) - map[iter.key()] = parsePreset(iter.value()); - - return map; -} - -drumpad_presets::Category parseCategory(const QJsonValue &jsonValue) -{ - if (!jsonValue.isObject()) - throw std::runtime_error{"json value for Category is not an object"}; - - const auto jsonObj = jsonValue.toObject(); - - drumpad_presets::Category category; - - for (auto iter = std::cbegin(jsonObj); iter != std::cend(jsonObj); iter++) - { - if (iter.key() == "title") - category.title = parseString(iter.value()); - else if (iter.key() == "filter") - category.filter = parseFilter(iter.value()); - else - throw std::runtime_error{QString{"unknown key %0 for Category"}.arg(iter.key()).toStdString()}; - } - - return category; -} - -drumpad_presets::Filter parseFilter(const QJsonValue &jsonValue) -{ - if (!jsonValue.isObject()) - throw std::runtime_error{"json value for Filters is not an object"}; - - const auto jsonObj = jsonValue.toObject(); - - drumpad_presets::Filter filters; - - for (auto iter = std::cbegin(jsonObj); iter != std::cend(jsonObj); iter++) - { - if (iter.key() == "tags") - filters.tags = parseStringVector(iter.value()); - else - throw std::runtime_error{QString{"unknown key %0 for Filters"}.arg(iter.key()).toStdString()}; - } - - return filters; -} - -drumpad_presets::Preset parsePreset(const QJsonValue &jsonValue) -{ - if (!jsonValue.isObject()) - throw std::runtime_error{"json value for Preset is not an object"}; - - const auto jsonObj = jsonValue.toObject(); - - drumpad_presets::Preset preset; - - for (auto iter = std::cbegin(jsonObj); iter != std::cend(jsonObj); iter++) - { - const auto key = iter.key(); - if (key == "id") - preset.id = parseString(iter.value()); - else if (key == "name") - preset.name = parseString(iter.value()); - else if (key == "author") - preset.author = parseString(iter.value()); - else if (key == "orderBy") - preset.orderBy = parseString(iter.value()); - else if (key == "version") - preset.version = parseString(iter.value()); - else if (key == "tempo") - preset.tempo = parseInt(iter.value()); - else if (key == "icon") - preset.icon = parseString(iter.value()); - else if (key == "price") - preset.price = parseInt(iter.value()); - else if (key == "priceForSession") - preset.priceForSession = parseInt(iter.value()); - else if (key == "hasInfo") - preset.hasInfo = parseBool(iter.value()); - else if (key == "tags") - preset.tags = parseStringVector(iter.value()); - else if (key == "DELETED") - preset.DELETED = parseBool(iter.value()); - else if (key == "difficulty") - preset.difficulty = parseInt(iter.value()); - else if (key == "sample") - preset.sample = parseInt(iter.value()); - else if (key == "audioPreview1Name") - preset.audioPreview1Name = parseString(iter.value()); - else if (key == "audioPreview1URL") - preset.audioPreview1URL = parseString(iter.value()); - else if (key == "audioPreview2Name") - preset.audioPreview2Name = parseString(iter.value()); - else if (key == "audioPreview2URL") - preset.audioPreview2URL = parseString(iter.value()); - else if (key == "imagePreview1") - preset.imagePreview1 = parseString(iter.value()); - else if (key == "videoPreview") - preset.videoPreview = parseString(iter.value()); - else if (key == "videoTutorial") - preset.videoTutorial = parseString(iter.value()); - else if (key == "files") - preset.files = parseFileArray(iter.value()); - else if (key == "beatSchool") - preset.beatSchool = parseSequenceVectorMap(iter.value()); - else if (key == "easyPlay") - preset.easyPlay = parseSequenceVectorMap(iter.value()); - else if (key == "timestamp") - preset.timestamp = QDateTime::fromSecsSinceEpoch(iter.value().toDouble()); - else if (key == "middleDescription") - {} - else - throw std::runtime_error{QString{"unknown key %0 for Preset"}.arg(key).toStdString()}; - } - - return preset; -} - -std::array parseFileArray(const QJsonValue &jsonValue) -{ - if (!jsonValue.isObject()) - throw std::runtime_error{"json value for File array is not an object"}; - - const auto jsonObj = jsonValue.toObject(); - - if (jsonObj.size() != 24) - throw std::runtime_error{"json value for File array doesn't have exactly 24 entries"}; - - std::array array; - - for (auto iter = std::cbegin(jsonObj); iter != std::cend(jsonObj); iter++) - { - bool ok; - const auto index = iter.key().toInt(&ok); - - if (!ok || index < 1 || index >= 25) - throw std::runtime_error{QString{"unknown key %0 for File"}.arg(iter.key()).toStdString()}; - - array[index - 1] = parseFile(iter.value()); - } + std::transform(std::cbegin(jsonArr), std::cend(jsonArr), std::begin(array), parseString); return array; } -drumpad_presets::File parseFile(const QJsonValue &jsonValue) -{ - if (!jsonValue.isObject()) - throw std::runtime_error{"json value for File is not an object"}; +template std::array parseStringArray(const QJsonValue &jsonValue); - const auto jsonObj = jsonValue.toObject(); - - drumpad_presets::File file; - - for (auto iter = std::cbegin(jsonObj); iter != std::cend(jsonObj); iter++) - { - const auto key = iter.key(); - if (key == "filename") - file.filename = parseString(iter.value()); - else if (key == "color") - file.color = parseString(iter.value()); - else if (key == "stopOnRelease") - file.stopOnRelease = parseString(iter.value()); - else if (key == "looped") - file.looped = parseBool(iter.value()); - else if (key == "choke") - file.choke = parseInt(iter.value()); - else - throw std::runtime_error{QString{"unknown key %0 for File"}.arg(key).toStdString()}; - } - - return file; -} - -std::vector parseSequenceVector(const QJsonValue &jsonValue) -{ - if (!jsonValue.isArray()) - throw std::runtime_error{"json value for vector of Sequence is not an array"}; - - std::vector vector; - - for (const auto &jsonValue : jsonValue.toArray()) - vector.emplace_back(parseSequence(jsonValue)); - - return vector; -} - -drumpad_presets::Sequence parseSequence(const QJsonValue &jsonValue) -{ - if (!jsonValue.isObject()) - throw std::runtime_error{"json value for File is not an object"}; - - const auto jsonObj = jsonValue.toObject(); - - drumpad_presets::Sequence sequence; - - for (auto iter = std::cbegin(jsonObj); iter != std::cend(jsonObj); iter++) - { - const auto key = iter.key(); - if (key == "name") - sequence.name = parseString(iter.value()); - else if (key == "id") - sequence.id = parseInt(iter.value()); - else if (key == "version") - sequence.version = parseInt(iter.value()); - else if (key == "orderBy") - sequence.orderBy = parseInt(iter.value()); - else if (key == "sequencerSize") - sequence.sequencerSize = parseInt(iter.value()); - else if (key == "pads") - sequence.pads = parseSequencePadVectorMap(iter.value()); - else if (key == "embientPads") - sequence.embientPads = parseIntVectorIgnoreNulls(iter.value()); - else - throw std::runtime_error{QString{"unknown key %0 for Sequence"}.arg(key).toStdString()}; - } - - return sequence; -} - -std::map> parseSequenceVectorMap(const QJsonValue &jsonValue) -{ - if (!jsonValue.isObject()) - throw std::runtime_error{"json value for Sequence vector map is not an object"}; - - const auto jsonObj = jsonValue.toObject(); - - std::map> map; - - for (auto iter = std::cbegin(jsonObj); iter != std::cend(jsonObj); iter++) - map[iter.key()] = parseSequenceVector(iter.value()); - - return map; -} - -drumpad_presets::SequencePad parseSequencePad(const QJsonValue &jsonValue) -{ - if (!jsonValue.isObject()) - throw std::runtime_error{"json value for File is not an object"}; - - const auto jsonObj = jsonValue.toObject(); - - drumpad_presets::SequencePad sequencePad; - - for (auto iter = std::cbegin(jsonObj); iter != std::cend(jsonObj); iter++) - { - if (iter.key() == "start") - sequencePad.start = parseInt(iter.value()); - else if (iter.key() == "duration") - sequencePad.duration = parseInt(iter.value()); - else if (iter.key() == "embient") - sequencePad.embient = parseBool(iter.value()); - else - throw std::runtime_error{QString{"unknown key %0 for Sequence"}.arg(iter.key()).toStdString()}; - } - - return sequencePad; -} - -std::vector parseSequencePadVector(const QJsonValue &jsonValue) -{ - if (!jsonValue.isArray()) - throw std::runtime_error{"json value for vector of SequencePad is not an array"}; - - std::vector vector; - - for (const auto &jsonValue : jsonValue.toArray()) - vector.emplace_back(parseSequencePad(jsonValue)); - - return vector; -} - -std::map> parseSequencePadVectorMap(const QJsonValue &jsonValue) -{ - if (!jsonValue.isObject()) - throw std::runtime_error{"json value for SequencePad vector map is not an object"}; - - const auto jsonObj = jsonValue.toObject(); - - std::map> map; - - for (auto iter = std::cbegin(jsonObj); iter != std::cend(jsonObj); iter++) - map[iter.key()] = parseSequencePadVector(iter.value()); - - return map; -} - -} +} // namespace json_converters diff --git a/jsonconverters.h b/jsonconverters.h index c5e761e..4e17c51 100755 --- a/jsonconverters.h +++ b/jsonconverters.h @@ -4,31 +4,18 @@ #include #include -#include "drumpadpresets.h" +namespace json_converters { -namespace json_converters -{ +QString jsonTypeToString(QJsonValue::Type type); QJsonObject loadJson(const QByteArray &buffer); - QString parseString(const QJsonValue &jsonValue); std::vector parseStringVector(const QJsonValue &jsonValue); int parseInt(const QJsonValue &jsonValue); bool parseBool(const QJsonValue &jsonValue); +bool parseBoolStr(const QJsonValue &jsonValue); std::vector parseIntVector(const QJsonValue &jsonValue); std::vector parseIntVectorIgnoreNulls(const QJsonValue &jsonValue); +template +std::array parseStringArray(const QJsonValue &jsonValue); -drumpad_presets::PresetsConfig parseDrumPadPresetsConfig(const QJsonObject &jsonObj); -std::vector parseCategoryVector(const QJsonValue &jsonValue); -std::map parsePresetMap(const QJsonValue &jsonValue); -drumpad_presets::Category parseCategory(const QJsonValue &jsonValue); -drumpad_presets::Filter parseFilter(const QJsonValue &jsonValue); -drumpad_presets::Preset parsePreset(const QJsonValue &jsonValue); -std::array parseFileArray(const QJsonValue &jsonValue); -drumpad_presets::File parseFile(const QJsonValue &jsonValue); -std::vector parseSequenceVector(const QJsonValue &jsonValue); -drumpad_presets::Sequence parseSequence(const QJsonValue &jsonValue); -std::map> parseSequenceVectorMap(const QJsonValue &jsonValue); -drumpad_presets::SequencePad parseSequencePad(const QJsonValue &jsonValue); -std::vector parseSequencePadVector(const QJsonValue &jsonValue); -std::map> parseSequencePadVectorMap(const QJsonValue &jsonValue); -} +} // namespace json_converters diff --git a/loopstationjsonconverters.cpp b/loopstationjsonconverters.cpp new file mode 100644 index 0000000..af27554 --- /dev/null +++ b/loopstationjsonconverters.cpp @@ -0,0 +1,239 @@ +#include "loopstationjsonconverters.h" + +#include + +#include "jsonconverters.h" + +namespace json_converters { +namespace loopstation { + +loopstation_presets::PresetsConfig parsePresetsConfig(const QJsonObject &jsonObj) +{ + loopstation_presets::PresetsConfig presetConfig; + + for (auto iter = std::cbegin(jsonObj); iter != std::cend(jsonObj); iter++) + { + if (iter.key() == "categories") + presetConfig.categories = parseCategoryVector(iter.value()); + else if (iter.key() == "presets") + presetConfig.presets = parsePresetMap(iter.value()); + else + throw std::runtime_error{QString{"unknown key %0 (%1) for PresetConfig"}.arg(iter.key(), jsonTypeToString(iter->type())).toStdString()}; + } + + return presetConfig; +} + +std::vector parseCategoryVector(const QJsonValue &jsonValue) +{ + if (!jsonValue.isArray()) + throw std::runtime_error{QString{"json value for vector of Category is not an array but %0"}.arg(jsonTypeToString(jsonValue.type())).toStdString()}; + + std::vector vector; + + for (const auto &jsonValue : jsonValue.toArray()) + vector.emplace_back(parseCategory(jsonValue)); + + return vector; +} + +std::map parsePresetMap(const QJsonValue &jsonValue) +{ + if (!jsonValue.isObject()) + throw std::runtime_error{QString{"json value for Preset map is not an object but %0"}.arg(jsonTypeToString(jsonValue.type())).toStdString()}; + + const auto jsonObj = jsonValue.toObject(); + + std::map map; + + for (auto iter = std::cbegin(jsonObj); iter != std::cend(jsonObj); iter++) + map[iter.key()] = parsePreset(iter.value()); + + return map; +} + +loopstation_presets::Category parseCategory(const QJsonValue &jsonValue) +{ + if (!jsonValue.isObject()) + throw std::runtime_error{QString{"json value for Category is not an object but %0"}.arg(jsonTypeToString(jsonValue.type())).toStdString()}; + + const auto jsonObj = jsonValue.toObject(); + + loopstation_presets::Category category; + + for (auto iter = std::cbegin(jsonObj); iter != std::cend(jsonObj); iter++) + { + if (iter.key() == "title") + category.title = parseString(iter.value()); + else if (iter.key() == "filter") + category.filter = parseFilter(iter.value()); + else + throw std::runtime_error{QString{"unknown key %0 (%1) for Category"}.arg(iter.key(), jsonTypeToString(iter->type())).toStdString()}; + } + + return category; +} + +loopstation_presets::Filter parseFilter(const QJsonValue &jsonValue) +{ + if (!jsonValue.isObject()) + throw std::runtime_error{QString{"json value for Filters is not an object but %0"}.arg(jsonTypeToString(jsonValue.type())).toStdString()}; + + const auto jsonObj = jsonValue.toObject(); + + loopstation_presets::Filter filters; + + for (auto iter = std::cbegin(jsonObj); iter != std::cend(jsonObj); iter++) + { + if (iter.key() == "tags") + filters.tags = parseStringVector(iter.value()); + else + throw std::runtime_error{QString{"unknown key %0 (%1) for Filters"}.arg(iter.key(), jsonTypeToString(iter->type())).toStdString()}; + } + + return filters; +} + +loopstation_presets::Preset parsePreset(const QJsonValue &jsonValue) +{ + if (!jsonValue.isObject()) + throw std::runtime_error{QString{"json value for Preset is not an object but %0"}.arg(jsonTypeToString(jsonValue.type())).toStdString()}; + + const auto jsonObj = jsonValue.toObject(); + + loopstation_presets::Preset preset; + + for (auto iter = std::cbegin(jsonObj); iter != std::cend(jsonObj); iter++) + { + const auto key = iter.key(); + if (key == "id") + preset.id = parseString(iter.value()); + else if (key == "audioPreviewUrl") + preset.audioPreviewUrl = parseString(iter.value()); + else if (key == "author") + preset.author = parseString(iter.value()); + else if (key == "bpm") + preset.bpm = parseInt(iter.value()); + else if (key == "coverUrl") + preset.coverUrl = parseString(iter.value()); + else if (key == "lessons") + preset.lessons = parseLessons(iter.value()); + else if (key == "loopLength") + preset.loopLength = parseInt(iter.value()); + else if (key == "orderBy") + preset.orderBy = parseString(iter.value()); + else if (key == "pads") + preset.pads = parseStringArray<48>(iter.value()); + else if (key == "premium") + preset.premium = parseBoolStr(iter.value()); + else if (key == "tags") + preset.tags = parseStringVector(iter.value()); + else if (key == "title") + preset.title = parseString(iter.value()); + else if (key == "beatSchool") + { + // {} + } + else if (key == "DELETED") + preset.DELETED = parseBool(iter.value()); + else + throw std::runtime_error{QString{"unknown key %0 (%1) for Preset"}.arg(key, jsonTypeToString(iter->type())).toStdString()}; + } + + return preset; +} + +std::vector parseLessons(const QJsonValue &jsonValue) +{ + if (!jsonValue.isArray()) + throw std::runtime_error{QString{"json value for vector of Lesson is not an array but %0"}.arg(jsonTypeToString(jsonValue.type())).toStdString()}; + + std::vector vector; + + for (const auto &jsonValue : jsonValue.toArray()) + vector.emplace_back(parseLesson(jsonValue)); + + return vector; +} + +loopstation_presets::Lesson parseLesson(const QJsonValue &jsonValue) +{ + if (!jsonValue.isObject()) + throw std::runtime_error{QString{"json value for Lesson is not an object but %0"}.arg(jsonTypeToString(jsonValue.type())).toStdString()}; + + const auto jsonObj = jsonValue.toObject(); + + loopstation_presets::Lesson lesson; + + for (auto iter = std::cbegin(jsonObj); iter != std::cend(jsonObj); iter++) + { + const auto key = iter.key(); + if (key == "version") + lesson.version = parseInt(iter.value()); + else if (key == "name") + lesson.name = parseString(iter.value()); + else if (key == "id") + lesson.id = parseInt(iter.value()); + else if (key == "orderBy") + lesson.orderBy = parseInt(iter.value()); + else if (key == "pads") + lesson.pads = parseLessonPadVectorMap(iter.value()); + else + throw std::runtime_error{QString{"unknown key %0 (%1) for Lesson"}.arg(key, jsonTypeToString(iter->type())).toStdString()}; + } + + return lesson; +} + +std::map> parseLessonPadVectorMap(const QJsonValue &jsonValue) +{ + if (!jsonValue.isObject()) + throw std::runtime_error{QString{"json value for LessonPad vector map is not an object but %0"}.arg(jsonTypeToString(jsonValue.type())).toStdString()}; + + const auto jsonObj = jsonValue.toObject(); + + std::map> map; + + for (auto iter = std::cbegin(jsonObj); iter != std::cend(jsonObj); iter++) + map[iter.key()] = parseLessonPadVector(iter.value()); + + return map; + +} + +std::vector parseLessonPadVector(const QJsonValue &jsonValue) +{ + if (!jsonValue.isArray()) + throw std::runtime_error{QString{"json value for vector of LessonPad is not an array but %0"}.arg(jsonTypeToString(jsonValue.type())).toStdString()}; + + std::vector vector; + + for (const auto &jsonValue : jsonValue.toArray()) + vector.emplace_back(parseLessonPad(jsonValue)); + + return vector; +} + +loopstation_presets::LessonPad parseLessonPad(const QJsonValue &jsonValue) +{ + if (!jsonValue.isObject()) + throw std::runtime_error{QString{"json value for LessonPad is not an object but %0"}.arg(jsonTypeToString(jsonValue.type())).toStdString()}; + + const auto jsonObj = jsonValue.toObject(); + + loopstation_presets::LessonPad lessonPad; + + for (auto iter = std::cbegin(jsonObj); iter != std::cend(jsonObj); iter++) + { + const auto key = iter.key(); + if (key == "tap") + lessonPad.tap = parseInt(iter.value()); + else + throw std::runtime_error{QString{"unknown key %0 (%1) for LessonPad"}.arg(key, jsonTypeToString(iter->type())).toStdString()}; + } + + return lessonPad; +} + +} // namespace loopstation +} // namespace json_converters diff --git a/loopstationjsonconverters.h b/loopstationjsonconverters.h new file mode 100644 index 0000000..8e1e30a --- /dev/null +++ b/loopstationjsonconverters.h @@ -0,0 +1,24 @@ +#pragma once + +#include "loopstationpresets.h" + +class QJsonObject; +class QJsonValue; + +namespace json_converters { +namespace loopstation { + +loopstation_presets::PresetsConfig parsePresetsConfig(const QJsonObject &jsonObj); +std::vector parseCategoryVector(const QJsonValue &jsonValue); +std::map parsePresetMap(const QJsonValue &jsonValue); +loopstation_presets::Category parseCategory(const QJsonValue &jsonValue); +loopstation_presets::Filter parseFilter(const QJsonValue &jsonValue); +loopstation_presets::Preset parsePreset(const QJsonValue &jsonValue); +std::vector parseLessons(const QJsonValue &jsonValue); +loopstation_presets::Lesson parseLesson(const QJsonValue &jsonValue); +std::map> parseLessonPadVectorMap(const QJsonValue &jsonValue); +std::vector parseLessonPadVector(const QJsonValue &jsonValue); +loopstation_presets::LessonPad parseLessonPad(const QJsonValue &jsonValue); + +} // namespace loopstation +} // namespace json_converters diff --git a/loopstationpresets.cpp b/loopstationpresets.cpp new file mode 100644 index 0000000..c633ae9 --- /dev/null +++ b/loopstationpresets.cpp @@ -0,0 +1,2 @@ +#include "loopstationpresets.h" + diff --git a/loopstationpresets.h b/loopstationpresets.h new file mode 100644 index 0000000..5068bd0 --- /dev/null +++ b/loopstationpresets.h @@ -0,0 +1,62 @@ +#pragma once + +#include +#include +#include +#include + +#include +#include + +namespace loopstation_presets { + +struct Filter +{ + std::optional> tags; +}; + +struct Category +{ + std::optional title; + std::optional filter; +}; + +struct LessonPad +{ + std::optional tap; +}; + +struct Lesson +{ + std::optional version; + std::optional name; + std::optional id; + std::optional orderBy; + std::optional>> pads; +}; + +struct Preset +{ + std::optional id; + std::optional audioPreviewUrl; + std::optional author; + std::optional bpm; + std::optional> lessons; + std::optional coverUrl; + std::optional loopLength; + std::optional orderBy; + std::optional> pads; + std::optional premium; + std::optional> tags; + std::optional title; + // TODO beatschool + std::optional DELETED; +}; + +struct PresetsConfig +{ + std::optional> categories; + std::optional> presets; +}; + +} // namespace loopstation_presets diff --git a/widgets/drumpadwidget.cpp b/widgets/drumpadwidget.cpp index 6ee2eaf..2dbdda7 100644 --- a/widgets/drumpadwidget.cpp +++ b/widgets/drumpadwidget.cpp @@ -9,6 +9,7 @@ #include "audioformat.h" #include "midicontainers.h" #include "jsonconverters.h" +#include "drumpadjsonconverters.h" #include "drummachinesettings.h" DrumPadWidget::DrumPadWidget(QWidget *parent) : @@ -19,6 +20,7 @@ DrumPadWidget::DrumPadWidget(QWidget *parent) : connect(m_ui->pushButtonUp, &QAbstractButton::pressed, this, &DrumPadWidget::selectPrevPreset); connect(m_ui->pushButtonDown, &QAbstractButton::pressed, this, &DrumPadWidget::selectNextPreset); + connect(m_ui->pushButtonRefresh, &QAbstractButton::pressed, this, &DrumPadWidget::loadPresets); connect(m_ui->sequencerWidget, &SequencerWidget::sendMidi, this, &DrumPadWidget::sendMidi); connect(m_ui->samplesWidget, &SamplesWidget::sendMidi, this, &DrumPadWidget::sendMidi); @@ -37,8 +39,6 @@ DrumPadWidget::DrumPadWidget(QWidget *parent) : m_ui->filesView->setModel(&m_filesModel); connect(m_ui->presetsView->selectionModel(), &QItemSelectionModel::currentRowChanged, this, &DrumPadWidget::currentRowChanged); - - connect(m_ui->pushButtonRefresh, &QAbstractButton::pressed, this, &DrumPadWidget::loadPresets); } DrumPadWidget::~DrumPadWidget() = default; @@ -182,7 +182,7 @@ void DrumPadWidget::requestFinished() try { - auto result = json_converters::parseDrumPadPresetsConfig(json_converters::loadJson(reply->readAll())); + auto result = json_converters::drumpad::parsePresetsConfig(json_converters::loadJson(reply->readAll())); if (!result.presets) throw std::runtime_error("presets missing in response"); diff --git a/widgets/drumpadwidget.ui b/widgets/drumpadwidget.ui index df14754..43ffa13 100644 --- a/widgets/drumpadwidget.ui +++ b/widgets/drumpadwidget.ui @@ -21,7 +21,7 @@ - + diff --git a/widgets/loopstationwidget.cpp b/widgets/loopstationwidget.cpp index 24aa81b..f750cd6 100644 --- a/widgets/loopstationwidget.cpp +++ b/widgets/loopstationwidget.cpp @@ -1,13 +1,26 @@ #include "loopstationwidget.h" #include "ui_loopstationwidget.h" +#include +#include +#include +#include + #include "audioformat.h" +#include "midicontainers.h" +#include "jsonconverters.h" +#include "loopstationjsonconverters.h" +#include "drummachinesettings.h" LoopStationWidget::LoopStationWidget(QWidget *parent) : - QWidget{parent}, + QSplitter{parent}, m_ui{std::make_unique()} { m_ui->setupUi(this); + + connect(m_ui->pushButtonUp, &QAbstractButton::pressed, this, &LoopStationWidget::selectPrevPreset); + connect(m_ui->pushButtonDown, &QAbstractButton::pressed, this, &LoopStationWidget::selectNextPreset); + connect(m_ui->pushButtonRefresh, &QAbstractButton::pressed, this, &LoopStationWidget::loadPresets); } LoopStationWidget::~LoopStationWidget() = default; @@ -19,7 +32,8 @@ void LoopStationWidget::writeSamples(frame_t *begin, frame_t *end) void LoopStationWidget::injectNetworkAccessManager(QNetworkAccessManager &networkAccessManager) { - + m_networkAccessManager = &networkAccessManager; + loadPresets(); } void LoopStationWidget::injectDecodingThread(QThread &thread) @@ -46,3 +60,66 @@ void LoopStationWidget::midiReceived(const midi::MidiMessage &message) { } + +void LoopStationWidget::loadPresets() +{ + if (!m_networkAccessManager) + { + qWarning() << "no network access manager available"; + return; + } + + m_ui->pushButtonRefresh->setEnabled(false); + + QNetworkRequest request{QUrl{"https://brunner.ninja/komposthaufen/groovepad/presets_config_v2.json"}}; + request.setAttribute(QNetworkRequest::CacheLoadControlAttribute, QNetworkRequest::PreferCache); + request.setAttribute(QNetworkRequest::CacheSaveControlAttribute, true); + + m_reply = std::unique_ptr(m_networkAccessManager->get(request)); + connect(m_reply.get(), &QNetworkReply::finished, this, &LoopStationWidget::requestFinished); +} + +void LoopStationWidget::requestFinished() +{ + if (!m_reply) + { + qWarning() << "no valid reply"; + return; + } + + auto reply = std::move(m_reply); + if (reply->error() != QNetworkReply::NoError) + { + QMessageBox::warning(this, tr("Could not load presets!"), tr("Could not load presets!") + "\n\n" + reply->errorString()); + return; + } + + try + { + auto result = json_converters::loopstation::parsePresetsConfig(json_converters::loadJson(reply->readAll())); + + if (!result.presets) + throw std::runtime_error("presets missing in response"); + + // TODO + } + catch (const std::exception &e) + { + QMessageBox::warning(this, tr("error"), tr("error") + "\n\n" + QString::fromStdString(e.what())); + } +} + +void LoopStationWidget::selectFirstPreset() +{ + +} + +void LoopStationWidget::selectPrevPreset() +{ + +} + +void LoopStationWidget::selectNextPreset() +{ + +} diff --git a/widgets/loopstationwidget.h b/widgets/loopstationwidget.h index acae42c..826cd75 100644 --- a/widgets/loopstationwidget.h +++ b/widgets/loopstationwidget.h @@ -1,16 +1,17 @@ #pragma once -#include +#include #include namespace Ui { class LoopStationWidget; } namespace midi { struct MidiMessage; } class QNetworkAccessManager; +class QNetworkReply; class DrumMachineSettings; struct frame_t; -class LoopStationWidget : public QWidget +class LoopStationWidget : public QSplitter { Q_OBJECT @@ -31,6 +32,17 @@ signals: public slots: void midiReceived(const midi::MidiMessage &message); +private slots: + void loadPresets(); + void requestFinished(); + void selectFirstPreset(); + void selectPrevPreset(); + void selectNextPreset(); + private: const std::unique_ptr m_ui; + + QNetworkAccessManager *m_networkAccessManager{}; + + std::unique_ptr m_reply; }; diff --git a/widgets/loopstationwidget.ui b/widgets/loopstationwidget.ui index 4926683..9d81b05 100644 --- a/widgets/loopstationwidget.ui +++ b/widgets/loopstationwidget.ui @@ -1,29 +1,98 @@ LoopStationWidget - + 0 0 - 400 - 300 + 1014 + 442 - - - - 40 - 50 - 261 - 18 - - - - Hier könnte ihre LoopStation stehen. - + + Qt::Horizontal + + + + + + + + + true + + + + + + + + 32 + 16777215 + + + + + + + + + + + + 32 + 16777215 + + + + + + + + + + + + 32 + 16777215 + + + + + + + + + + + + + + + + + + + 20 + 10 + 261 + 18 + + + + Hier könnte ihre LoopStation stehen. + + + + + MidiButton + QPushButton +
widgets/midibutton.h
+
+