diff --git a/.gitignore b/.gitignore index cc0f797e3..32e4bea06 100644 --- a/.gitignore +++ b/.gitignore @@ -5,3 +5,4 @@ build-xcode/ editables/ deps/ .idea/ +build-codex/ diff --git a/CMakeLists.txt b/CMakeLists.txt index 94a1bbe81..7c780ad91 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -72,6 +72,11 @@ FetchContent_Declare(readerwriterqueue SOURCE_DIR ${CMAKE_SOURCE_DIR}/deps/readerwriterqueue ) +FetchContent_Declare(kaitai_runtime + GIT_REPOSITORY https://github.com/izzyreal/kaitai_struct_cpp_stl_runtime + GIT_TAG cpp-serialization +) + FetchContent_Declare(json-schema-validator GIT_REPOSITORY https://github.com/pboettch/json-schema-validator.git GIT_TAG 2.3.0 @@ -101,6 +106,11 @@ target_include_directories(platform_folders INTERFACE $ ) +message(STATUS "Downloading kaitai runtime...") +set(BUILD_TESTS OFF CACHE BOOL "" FORCE) +FetchContent_MakeAvailable(kaitai_runtime) +unset(BUILD_TESTS CACHE) + if(NOT TARGET nlohmann_json::nlohmann_json) FetchContent_Declare(json URL https://github.com/nlohmann/json/releases/download/v3.12.0/json.tar.xz @@ -179,7 +189,7 @@ target_include_directories(mpc PUBLIC ${readerwriterqueue_SOURCE_DIR} ) -target_link_libraries(mpc akaifat ghc_filesystem rtmidi samplerate platform_folders nlohmann_json::nlohmann_json nlohmann_json_schema_validator lodepng) +target_link_libraries(mpc akaifat ghc_filesystem rtmidi samplerate platform_folders nlohmann_json::nlohmann_json nlohmann_json_schema_validator lodepng kaitai_struct_cpp_stl_runtime) if(MSVC) target_compile_options(mpc PRIVATE "/MP") diff --git a/PARSERS.md b/PARSERS.md new file mode 100644 index 000000000..96401b8d9 --- /dev/null +++ b/PARSERS.md @@ -0,0 +1,107 @@ +# Kaitai Parser Maintenance + +This repo vendors generated C++ Kaitai parsers under: + +- `src/main/file/kaitai/generated` + +The source `.ksy` files live in: + +- `/Users/izmar/git/mpc2000xl_kaitai` + +The generator we use is the C++ write-capable compiler from: + +- `/Users/izmar/git/kaitai_struct` +- branch: `cpp-serialization` + +## Formats currently generated + +- `mpc2000xl.snd.ksy` +- `mpc2000xl.pgm.ksy` +- `mpc2000xl.aps.ksy` +- `standard_midi_file_with_running_status.ksy` +- `mpc2000xl.mid.ksy` +- `mpc2000xl.all.ksy` +- `mpc60.set.v1.ksy` +- `mpc60.snd.v1.ksy` +- `mpc3000.snd.v2.ksy` +- `mpc3000.pgm.v3.ksy` +- `mpc3000.seq.v3.ksy` +- `mpc3000.all.v3.ksy` +- `mpc60.seq.body.ksy` +- `mpc60.seq.v2.ksy` +- `mpc60.all.body.ksy` +- `mpc60.all.v2.ksy` + +## Regenerate parsers + +From anywhere: + +```sh +/Users/izmar/git/kaitai_struct/compiler/jvm/target/universal/stage/bin/kaitai-struct-compiler \ + -t cpp_stl \ + --cpp-standard 11 \ + -w \ + --outdir /Users/izmar/git/vmpc-juce/editables/mpc/src/main/file/kaitai/generated \ + -I /Users/izmar/git/mpc2000xl_kaitai \ + /Users/izmar/git/mpc2000xl_kaitai/mpc2000xl.snd.ksy \ + /Users/izmar/git/mpc2000xl_kaitai/mpc2000xl.pgm.ksy \ + /Users/izmar/git/mpc2000xl_kaitai/mpc2000xl.aps.ksy \ + /Users/izmar/git/mpc2000xl_kaitai/standard_midi_file_with_running_status.ksy \ + /Users/izmar/git/mpc2000xl_kaitai/mpc2000xl.mid.ksy \ + /Users/izmar/git/mpc2000xl_kaitai/mpc2000xl.all.ksy \ + /Users/izmar/git/mpc2000xl_kaitai/mpc60.set.v1.ksy \ + /Users/izmar/git/mpc2000xl_kaitai/mpc60.snd.v1.ksy \ + /Users/izmar/git/mpc2000xl_kaitai/mpc3000.snd.v2.ksy \ + /Users/izmar/git/mpc2000xl_kaitai/mpc3000.pgm.v3.ksy \ + /Users/izmar/git/mpc2000xl_kaitai/mpc3000.seq.v3.ksy \ + /Users/izmar/git/mpc2000xl_kaitai/mpc3000.all.v3.ksy \ + /Users/izmar/git/mpc2000xl_kaitai/mpc60.seq.body.ksy \ + /Users/izmar/git/mpc2000xl_kaitai/mpc60.seq.v2.ksy \ + /Users/izmar/git/mpc2000xl_kaitai/mpc60.all.body.ksy \ + /Users/izmar/git/mpc2000xl_kaitai/mpc60.all.v2.ksy +``` + +## Typical update flow + +1. Change the relevant `.ksy` file in `mpc2000xl_kaitai`. +2. Regenerate the C++ outputs into `src/main/file/kaitai/generated`. +3. If needed, update the integration layer in: + - `src/main/file/kaitai/SndIo.*` + - `src/main/file/kaitai/PgmIo.*` + - `src/main/file/kaitai/ApsIo.*` + - `src/main/file/kaitai/MidIo.*` + - `src/main/file/kaitai/AllIo.*` + - `src/main/file/kaitai/Mpc3000SeqIo.*` +4. Run downstream tests. +5. Commit both the `.ksy` change and the regenerated vendored C++. + +## Verify in editables/mpc + +Build tests. Use any build directory you want. Examples below use `build`: + +```sh +cmake -G "Ninja Multi-Config" -B build +cmake --build build --target mpc-tests --config Debug +``` + +Run the focused Kaitai suite: + +```sh +./build/Debug/mpc-tests.app/Contents/MacOS/mpc-tests "[kaitai-snd],[kaitai-pgm],[kaitai-aps],[kaitai-mid],[kaitai-all],[kaitai-set]" +``` + +Run one format only if needed: + +```sh +./build/Debug/mpc-tests.app/Contents/MacOS/mpc-tests "[kaitai-aps]" +./build/Debug/mpc-tests.app/Contents/MacOS/mpc-tests "[kaitai-pgm]" +./build/Debug/mpc-tests.app/Contents/MacOS/mpc-tests "[kaitai-snd]" +./build/Debug/mpc-tests.app/Contents/MacOS/mpc-tests "[kaitai-mid]" +./build/Debug/mpc-tests.app/Contents/MacOS/mpc-tests "[kaitai-all]" +./build/Debug/mpc-tests.app/Contents/MacOS/mpc-tests "[kaitai-set]" +``` + +## Notes + +- Do not regenerate with upstream Kaitai compiler unless it has the same C++ serialization support as the `cpp-serialization` branch. +- `generated/` is vendored on purpose. As far as `editables/mpc` is concerned, generated parsers are ordinary source files. diff --git a/cmake/MpcResourceBundling.cmake b/cmake/MpcResourceBundling.cmake index 8bebd70d7..6e627c403 100644 --- a/cmake/MpcResourceBundling.cmake +++ b/cmake/MpcResourceBundling.cmake @@ -40,7 +40,7 @@ function(_bundle_resources) _generate_mpc_required_resource_manifest() - file(GLOB_RECURSE MPC_RESOURCES "${_mpc_resources_root}/*") + file(GLOB_RECURSE MPC_RESOURCES CONFIGURE_DEPENDS "${_mpc_resources_root}/*") list(FILTER MPC_RESOURCES EXCLUDE REGEX "\\.DS_Store$") if (APPLE) @@ -62,7 +62,7 @@ function(_bundle_resources) target_link_libraries(mpc mpc::rc) endif() - file(GLOB_RECURSE MPC_TEST_RESOURCES "${_mpc_test_resources_root}/*") + file(GLOB_RECURSE MPC_TEST_RESOURCES CONFIGURE_DEPENDS "${_mpc_test_resources_root}/*") list(FILTER MPC_TEST_RESOURCES EXCLUDE REGEX "\\.DS_Store$") cmrc_add_resource_library( diff --git a/resources/screens/bg/conversion-table.png b/resources/screens/bg/conversion-table.png new file mode 100644 index 000000000..6a1813d1f Binary files /dev/null and b/resources/screens/bg/conversion-table.png differ diff --git a/resources/screens/bg/load-a-set-replace-add.png b/resources/screens/bg/load-a-set-replace-add.png new file mode 100644 index 000000000..9cc559520 Binary files /dev/null and b/resources/screens/bg/load-a-set-replace-add.png differ diff --git a/resources/screens/bg/load-a-set-sound.png b/resources/screens/bg/load-a-set-sound.png new file mode 100644 index 000000000..23898c191 Binary files /dev/null and b/resources/screens/bg/load-a-set-sound.png differ diff --git a/resources/screens/bg/load-a-set.png b/resources/screens/bg/load-a-set.png new file mode 100644 index 000000000..41a2a7341 Binary files /dev/null and b/resources/screens/bg/load-a-set.png differ diff --git a/resources/screens/bg/mpc3000-all-file.png b/resources/screens/bg/mpc3000-all-file.png new file mode 100644 index 000000000..5b997458e Binary files /dev/null and b/resources/screens/bg/mpc3000-all-file.png differ diff --git a/resources/screens/layer2.json b/resources/screens/layer2.json index bd135ac94..be8425be7 100644 --- a/resources/screens/layer2.json +++ b/resources/screens/layer2.json @@ -93,6 +93,31 @@ "fblabels" : [ null, null, "CLEAR", "CANCEL", "LOAD", null ], "fbtypes" : [ null, null, 1, 0, 1, null ] }, +"load-a-set": +{ + "fblabels" : [ null, null, "SOUND", "CANCEL", "LOAD", null ], + "fbtypes" : [ null, null, 1, 0, 1, null ] +}, +"conversion-table": +{ + "labels" : [ "MPC60 Pad:", "Becomes note:" ], + "x" : [ 67, 67 ], + "y" : [ 15, 37 ], + "tfsize" : [ 90, 36 ], + "parameters" : [ "mpc60-pad", "becomes-note" ], + "fblabels" : [ null, null, null, "CANCEL", "DO IT", null ], + "fbtypes" : [ null, null, null, 0, 1, null ] +}, +"load-a-set-replace-add": +{ + "labels" : [ ":" ], + "x" : [ 77 ], + "y" : [ 21 ], + "tfsize" : [ 60 ], + "parameters" : [ "load-replace-sound" ], + "fblabels" : [ null, null, "CLEAR", "CANCEL", "LOAD", null ], + "fbtypes" : [ null, null, 1, 0, 1, null ] +}, "save-all-file": { "labels" : [ "File\u00CEname:" ], diff --git a/resources/screens/layer3.json b/resources/screens/layer3.json index afd202433..67ba945d6 100644 --- a/resources/screens/layer3.json +++ b/resources/screens/layer3.json @@ -1,9 +1,30 @@ { +"load-a-sequence-play": +{ + "infowidgets" : [ "position" ], + "infosize" : [ 60 ], + "infox" : [ 95 ], + "infoy" : [ 28 ] +}, "file-exists": { "fblabels" : [ null, null, "REPLAC", "CANCEL", "RENAME", null], "fbtypes" : [ null, null, 1, 0, 1, null ] }, +"load-a-set-sound": +{ + "labels" : [ "MPC60 pad:" ], + "x" : [ 64 ], + "y" : [ 21 ], + "tfsize" : [ 90 ], + "parameters" : [ "mpc60-pad" ], + "infowidgets" : [ "file" ], + "infox" : [ 95 ], + "infoy" : [ 35 ], + "infosize" : [ 126 ], + "fblabels" : [ null, null, null, "CANCEL", "LOAD", null ], + "fbtypes" : [ null, null, null, 0, 1, null ] +}, "vmpc-record-jam": { "fblabels" : [ null, null, null, "CANCEL", "OK", null], diff --git a/src/main/AutoSave.cpp b/src/main/AutoSave.cpp index 351d29a64..097c254a6 100644 --- a/src/main/AutoSave.cpp +++ b/src/main/AutoSave.cpp @@ -10,10 +10,10 @@ #include "disk/AbstractDisk.hpp" #include "disk/ApsLoader.hpp" #include "FileIoPolicy.hpp" -#include "file/aps/ApsParser.hpp" #include "file/ByteUtil.hpp" -#include "file/sndreader/SndReader.hpp" -#include "file/sndwriter/SndWriter.hpp" +#include "file/kaitai/ApsIo.hpp" +#include "file/kaitai/AllIo.hpp" +#include "file/kaitai/SndIo.hpp" #include "lcdgui/screens/VmpcAutoSaveScreen.hpp" #include "lcdgui/screens/window/VmpcContinuePreviousSessionScreen.hpp" #include "lcdgui/screens/window/SaveAllFileScreen.hpp" @@ -21,7 +21,6 @@ #include "lcdgui/Screens.hpp" #include "disk/AllLoader.hpp" -#include "file/all/AllParser.hpp" #include "sampler/Sampler.hpp" #include "sequencer/Bus.hpp" #include "sequencer/Sequencer.hpp" @@ -41,10 +40,6 @@ using namespace mpc; using namespace mpc::file_io; using namespace mpc::file; -using namespace mpc::file::all; -using namespace mpc::file::aps; -using namespace mpc::file::sndwriter; -using namespace mpc::file::sndreader; using namespace mpc::lcdgui; using namespace mpc::lcdgui::screens; using namespace mpc::lcdgui::screens::window; @@ -52,6 +47,18 @@ using namespace mpc::lcdgui::screens::dialog2; namespace { +constexpr size_t kApsHeaderLength = 4; +constexpr size_t kApsSoundNameLength = 17; +constexpr size_t kApsPadLength1 = 2; +constexpr size_t kApsNameLength = 17; +constexpr size_t kApsParametersLength = 8; +constexpr size_t kApsMasterTableLength = 64; +constexpr size_t kApsPadLength2 = 7; +constexpr size_t kApsMixerLength = 384; +constexpr size_t kApsDrumConfigLength = 9; +constexpr size_t kApsDrumPadLength = 3; +constexpr size_t kAllSequencesOffset = 14406; + bool rangeFits(const size_t totalSize, const size_t offset, const size_t length) { return offset <= totalSize && length <= totalSize - offset; @@ -59,7 +66,7 @@ bool rangeFits(const size_t totalSize, const size_t offset, const size_t length) bool isValidAutoSaveApsData(const std::vector &data) { - if (!rangeFits(data.size(), ApsParser::HEADER_OFFSET, ApsParser::HEADER_LENGTH)) + if (!rangeFits(data.size(), 0, kApsHeaderLength)) { return false; } @@ -78,22 +85,22 @@ bool isValidAutoSaveApsData(const std::vector &data) return false; } - const size_t minSize = static_cast(ApsParser::HEADER_LENGTH) + + const size_t minSize = kApsHeaderLength + static_cast(soundCount) * - ApsParser::SOUND_NAME_LENGTH + - ApsParser::PAD_LENGTH1 + ApsParser::APS_NAME_LENGTH + - ApsParser::PARAMETERS_LENGTH + ApsParser::TABLE_LENGTH + - ApsParser::PAD_LENGTH2 + - 4u * (ApsParser::MIXER_LENGTH + - ApsParser::DRUM_CONFIG_LENGTH + - ApsParser::DRUM_PAD_LENGTH); + kApsSoundNameLength + + kApsPadLength1 + kApsNameLength + + kApsParametersLength + kApsMasterTableLength + + kApsPadLength2 + + 4u * (kApsMixerLength + + kApsDrumConfigLength + + kApsDrumPadLength); return data.size() >= minSize; } bool isValidAutoSaveAllData(const std::vector &data) { - if (data.size() < static_cast(AllParser::SEQUENCES_OFFSET)) + if (data.size() < kAllSequencesOffset) { return false; } @@ -132,6 +139,12 @@ bool isValidAutoSaveSndData(const std::vector &data) static_cast(frameCount) * channelCount * sizeof(int16_t); return rangeFits(data.size(), sndHeaderSize, sampleDataBytes); } + +std::string stripExtension(const std::string &fileName) +{ + const auto dotPos = fileName.find_last_of('.'); + return dotPos == std::string::npos ? fileName : fileName.substr(0, dotPos); +} } // namespace void AutoSave::restoreAutoSavedState(Mpc &mpc, @@ -284,15 +297,10 @@ void AutoSave::restoreAutoSavedState(Mpc &mpc, { throw std::invalid_argument("invalid APS autosave data"); } - ApsParser apsParser(data); constexpr bool withoutSounds = true; - // Regardless of whether the outer context is headless, - // we want the ApsLoader to always operate in headless - // mode so it doesn't spawn popups. We manage popups - // ourselves here in AutoSave. constexpr bool apsLoadingHeadless = true; - disk::ApsLoader::loadFromParsedAps( - apsParser, mpc, withoutSounds, apsLoadingHeadless); + file::kaitai::ApsIo::loadBytes( + mpc, data, withoutSounds, apsLoadingHeadless); } else if (f == "ALL.ALL") { @@ -300,8 +308,7 @@ void AutoSave::restoreAutoSavedState(Mpc &mpc, { throw std::invalid_argument("invalid ALL autosave data"); } - AllParser allParser(mpc, data); - disk::AllLoader::loadEverythingFromAllParser(mpc, allParser); + disk::AllLoader::loadEverythingFromBytes(mpc, data); } else if (f == "sounds.txt") { @@ -332,25 +339,21 @@ void AutoSave::restoreAutoSavedState(Mpc &mpc, soundName + "'"); continue; } - SndReader sndReader(soundData); - auto sound = mpc.getSampler()->addSound( - sndReader.getSampleRate()); + auto sound = mpc.getSampler()->addSound(); if (!sound) { break; } - - sound->setMono(sndReader.isMono()); - sndReader.readData(sound->getMutableSampleData()); - sound->setName(sndReader.getName()); - sound->setTune(sndReader.getTune()); - sound->setLevel(sndReader.getLevel()); - sound->setStart(sndReader.getStart()); - sound->setEnd(sndReader.getEnd()); - sound->setLoopTo(sound->getEnd() - - sndReader.getLoopLength()); - sound->setBeatCount(sndReader.getNumberOfBeats()); - sound->setLoopEnabled(sndReader.isLoopEnabled()); + const auto loadResult = file::kaitai::SndIo::loadBytes( + soundData, + sound, + stripExtension(soundName)); + if (!loadResult) + { + mpc.getSampler()->deleteSoundWithoutRepairingPrograms(sound); + MLOG("AutoSave restore skipped corrupt file '" + + soundName + "'"); + } } } else @@ -607,8 +610,7 @@ void AutoSave::storeAutoSavedState( } { - ApsParser apsParser(mpc, "stateinfo"); - if (!writeRequired("APS.APS", apsParser.getBytes())) + if (!writeRequired("APS.APS", file::kaitai::ApsIo::save(mpc, "stateinfo"))) { layeredScreen->closeCurrentScreen(); return; @@ -619,10 +621,8 @@ void AutoSave::storeAutoSavedState( for (auto &sound : mpc.getSampler()->getSounds()) { const auto sndPath = sound->getName() + ".SND"; - SndWriter sndWriter(sound.get()); - if (!writeRequired(sndPath, - {sndWriter.getSndFileArray().begin(), - sndWriter.getSndFileArray().end()})) + const auto soundBytes = file::kaitai::SndIo::saveSound(*sound); + if (!writeRequired(sndPath, soundBytes)) { layeredScreen->closeCurrentScreen(); return; @@ -631,8 +631,7 @@ void AutoSave::storeAutoSavedState( } { - AllParser allParser(mpc); - if (!writeRequired("ALL.ALL", allParser.getBytes())) + if (!writeRequired("ALL.ALL", file::kaitai::AllIo::save(mpc))) { layeredScreen->closeCurrentScreen(); return; diff --git a/src/main/Mpc.cpp b/src/main/Mpc.cpp index f1f296f5f..52a67892f 100644 --- a/src/main/Mpc.cpp +++ b/src/main/Mpc.cpp @@ -7,6 +7,7 @@ #include "engine/audio/server/NonRealTimeAudioServer.hpp" #include "performance/PerformanceManager.hpp" #include "lcdgui/ScreenComponent.hpp" +#include "lcdgui/screens/window/LoadASetReplaceAddScreen.hpp" #include "Paths.hpp" #include "nvram/NvRam.hpp" @@ -52,7 +53,7 @@ Mpc::Mpc() clock = std::make_shared(); } -void Mpc::init() +void Mpc::init(const MpcInitOptions &options) { const auto logFsError = [](const mpc_fs::fs_error &error) { @@ -74,7 +75,8 @@ void Mpc::init() paths->getDocuments()->storesPath(), paths->getDocuments()->defaultLocalVolumePath(), paths->getDocuments()->recordingsPath(), - paths->getDocuments()->autoSavePath()}; + paths->getDocuments()->autoSavePath(), + paths->getDocuments()->tempPath()}; for (auto &p : requiredPaths) { @@ -315,8 +317,15 @@ void Mpc::init() layeredScreen->openScreenById(ScreenId::SequencerScreen); - startMidiDeviceDetector(); - getEngineHost()->getAudioServer()->start(); + if (options.startMidiDeviceDetector) + { + startMidiDeviceDetector(); + } + + if (options.startAudioServer) + { + getEngineHost()->getAudioServer()->start(); + } autoSave = std::make_unique(); } @@ -413,6 +422,15 @@ Mpc::~Mpc() nvram::NvRam::saveVmpcSettings(*this); } + if (screens) + { + if (const auto setLoader = + screens->get()) + { + setLoader->waitForLoadThread(); + } + } + if (engineHost) { engineHost->destroyServices(); diff --git a/src/main/Mpc.hpp b/src/main/Mpc.hpp index 8811f0fa0..c439ad58b 100644 --- a/src/main/Mpc.hpp +++ b/src/main/Mpc.hpp @@ -71,6 +71,12 @@ namespace mpc::sampler namespace mpc { + struct MpcInitOptions + { + bool startMidiDeviceDetector = true; + bool startAudioServer = true; + }; + class Mpc { bool pluginModeEnabled = false; @@ -95,7 +101,7 @@ namespace mpc std::shared_ptr midiDeviceDetector; public: - void init(); + void init(const MpcInitOptions &options = {}); void startMidiDeviceDetector(); void panic() const; void setPluginModeEnabled(bool); diff --git a/src/main/audiomidi/SndInputFileStream.hpp b/src/main/audiomidi/SndInputFileStream.hpp index dae36738d..6934b61c6 100644 --- a/src/main/audiomidi/SndInputFileStream.hpp +++ b/src/main/audiomidi/SndInputFileStream.hpp @@ -1,13 +1,27 @@ #pragma once +#include "file/kaitai/Mpc60SampleDecoder.hpp" + +#include #include +#include +#include #include #ifdef __linux__ #include // For strcmp #endif -const char ID[] = {0x01, 0x04, '\0'}; +enum class SndInputEncoding +{ + Mpc2000XlPcm16, + Mpc3000Pcm16, + Mpc60Packed12, +}; + +const char MPC2000XL_SND_ID[] = {0x01, 0x04, '\0'}; +const char MPC3000_SND_ID[] = {0x01, 0x02, '\0'}; +const char MPC60_SND_ID[] = {0x01, 0x01, '\0'}; const int ID_INDEX = 0; // 2 byte ANSI string of a number greater than '10' and // equal to or smaller than '15' @@ -23,16 +37,19 @@ const int LOOP_LENGTH_INDEX = 34; // 4-byte uint const int LOOP_ENABLED_INDEX = 38; // 1 byte, 1 if true const int BEAT_COUNT_INDEX = 39; // 1 byte const int SAMPLE_RATE_INDEX = 40; // 2 byte unsigned short +const int MPC2000XL_SND_HEADER_SIZE = 42; +const int MPC3000_SND_HEADER_SIZE = 38; +const int MPC60_SND_HEADER_SIZE = 39; -void snd_read_bytes(const std::shared_ptr &stream, - const std::vector &bytes, const int maxLength) +inline void snd_read_bytes(const std::shared_ptr &stream, + const std::vector &bytes, const int maxLength) { auto byteCountToRead = std::min((int)bytes.size(), maxLength); stream->read((char *)&bytes[0], byteCountToRead); } -std::string snd_get_string(const std::shared_ptr &stream, - const int maxLength) +inline std::string snd_get_string(const std::shared_ptr &stream, + const int maxLength) { std::vector buffer(maxLength); snd_read_bytes(stream, buffer, maxLength); @@ -49,14 +66,16 @@ std::string snd_get_string(const std::shared_ptr &stream, return std::string(buffer.begin(), buffer.end()); } -uint16_t snd_get_unsigned_short_LE(const std::shared_ptr &stream) +inline uint16_t snd_get_unsigned_short_LE( + const std::shared_ptr &stream) { char buffer[2]; stream->read(buffer, 2); return static_cast((buffer[1] & 0xFF) << 8 | buffer[0] & 0xFF); } -int snd_get_LE(const std::shared_ptr &stream, int numBytes) +inline int snd_get_LE(const std::shared_ptr &stream, + int numBytes) { if (numBytes < 1 || numBytes > 4) @@ -82,24 +101,23 @@ int snd_get_LE(const std::shared_ptr &stream, int numBytes) return val; } -char snd_get_char(const std::shared_ptr &stream) +inline uint32_t snd_get_unsigned_LE( + const std::shared_ptr &stream, int numBytes) +{ + return static_cast(snd_get_LE(stream, numBytes)); +} + +inline char snd_get_char(const std::shared_ptr &stream) { char buf[1]; stream->read(buf, 1); return *buf; } -bool snd_read_header(const std::shared_ptr &stream, - int &sampleRate, int &validBits, int &numChannels, - int &numFrames) +inline bool snd_read_mpc2000xl_header( + const std::shared_ptr &stream, int &sampleRate, + int &validBits, int &numChannels, int &numFrames) { - auto sndId = snd_get_string(stream, 2); - - if (strcmp(sndId.c_str(), ID) != 0) - { - return false; - } - auto name = snd_get_string(stream, 17); /*auto level =*/snd_get_char(stream); @@ -132,3 +150,121 @@ bool snd_read_header(const std::shared_ptr &stream, return true; } + +inline bool snd_read_mpc60_header(const std::shared_ptr &stream, + int &sampleRate, int &validBits, + int &numChannels, int &numFrames) +{ + stream->seekg(0, std::ios::end); + const auto fileSize = stream->tellg(); + + if (fileSize < MPC60_SND_HEADER_SIZE) + { + return false; + } + + stream->seekg(2, std::ios::beg); + + auto name = snd_get_string(stream, 17); + numFrames = static_cast(snd_get_unsigned_LE(stream, 4)); + /*auto startMsec =*/snd_get_unsigned_LE(stream, 2); + /*auto endMsec =*/snd_get_unsigned_LE(stream, 4); + /*auto decayMsec =*/snd_get_unsigned_LE(stream, 2); + /*auto volume =*/snd_get_char(stream); + /*auto tuning =*/snd_get_char(stream); + stream->ignore(5); + /*auto velocityToVolume =*/snd_get_char(stream); + + const auto packedByteCount = static_cast( + ((static_cast(numFrames) + 1U) / 2U) * 3U); + + if (numFrames <= 0 || + fileSize != MPC60_SND_HEADER_SIZE + packedByteCount) + { + return false; + } + + sampleRate = mpc::file::kaitai::kMpc60SampleRate; + validBits = 16; + numChannels = 1; + stream->seekg(MPC60_SND_HEADER_SIZE, std::ios::beg); + return true; +} + +inline bool snd_read_mpc3000_header(const std::shared_ptr &stream, + int &sampleRate, int &validBits, + int &numChannels, int &numFrames) +{ + stream->seekg(0, std::ios::end); + const auto fileSize = stream->tellg(); + + if (fileSize < MPC3000_SND_HEADER_SIZE) + { + return false; + } + + stream->seekg(2, std::ios::beg); + + auto name = snd_get_string(stream, 17); + /*auto level =*/snd_get_char(stream); + stream->ignore(2); + /*auto start =*/snd_get_unsigned_LE(stream, 4); + /*auto end =*/snd_get_unsigned_LE(stream, 4); + numFrames = static_cast(snd_get_unsigned_LE(stream, 4)); + /*auto unknown2 =*/snd_get_unsigned_LE(stream, 4); + + if (numFrames <= 0 || + fileSize != MPC3000_SND_HEADER_SIZE + static_cast(numFrames * 2)) + { + return false; + } + + sampleRate = 44100; + validBits = 16; + numChannels = 1; + stream->seekg(MPC3000_SND_HEADER_SIZE, std::ios::beg); + return true; +} + +inline bool snd_read_header(const std::shared_ptr &stream, + int &sampleRate, int &validBits, + int &numChannels, int &numFrames, + SndInputEncoding &encoding) +{ + stream->clear(); + stream->seekg(0, std::ios::beg); + + const auto sndId = snd_get_string(stream, 2); + + if (strcmp(sndId.c_str(), MPC2000XL_SND_ID) == 0) + { + encoding = SndInputEncoding::Mpc2000XlPcm16; + return snd_read_mpc2000xl_header(stream, sampleRate, validBits, + numChannels, numFrames); + } + + if (strcmp(sndId.c_str(), MPC3000_SND_ID) == 0) + { + encoding = SndInputEncoding::Mpc3000Pcm16; + return snd_read_mpc3000_header(stream, sampleRate, validBits, + numChannels, numFrames); + } + + if (strcmp(sndId.c_str(), MPC60_SND_ID) == 0) + { + encoding = SndInputEncoding::Mpc60Packed12; + return snd_read_mpc60_header(stream, sampleRate, validBits, + numChannels, numFrames); + } + + return false; +} + +inline bool snd_read_header(const std::shared_ptr &stream, + int &sampleRate, int &validBits, + int &numChannels, int &numFrames) +{ + SndInputEncoding encoding; + return snd_read_header(stream, sampleRate, validBits, numChannels, + numFrames, encoding); +} diff --git a/src/main/audiomidi/SoundPlayer.cpp b/src/main/audiomidi/SoundPlayer.cpp index fa3f8fda8..c2adcc393 100644 --- a/src/main/audiomidi/SoundPlayer.cpp +++ b/src/main/audiomidi/SoundPlayer.cpp @@ -74,7 +74,8 @@ bool SoundPlayer::start(const std::shared_ptr &streamToUse, else if (fileFormat == SND) { valid = snd_read_header(stream, sourceSampleRate, validBits, - sourceNumChannels, sourceFrameCount); + sourceNumChannels, sourceFrameCount, + sndInputEncoding); wavSamplesAreFloat32 = false; } @@ -114,6 +115,10 @@ bool SoundPlayer::start(const std::shared_ptr &streamToUse, stopEarly.store(false); playedFrameCount = 0; ingestedSourceFrameCount = 0; + mpc60SampleDecoder = mpc::file::kaitai::Mpc60SampleDecoder(); + mpc60HasPendingSample = false; + mpc60PendingSample = 0.0f; + mpc60DecodedSampleIndex = 0; playing.store(true); @@ -314,8 +319,14 @@ void SoundPlayer::readWithResampling(const float ratio) } } -float SoundPlayer::readNextFrame() const +float SoundPlayer::readNextFrame() { + if (fileFormat == SND && + sndInputEncoding == SndInputEncoding::Mpc60Packed12) + { + return readNextMpc60Frame(); + } + if (inputAudioFormat->getSampleSizeInBits() == 24) { return sampleops::int24_to_float(readNext24BitInt()); @@ -331,6 +342,41 @@ float SoundPlayer::readNextFrame() const return sampleops::short_to_float(readNextShort()); } +float SoundPlayer::readNextMpc60Frame() +{ + if (mpc60HasPendingSample) + { + mpc60HasPendingSample = false; + ++mpc60DecodedSampleIndex; + return mpc60PendingSample; + } + + char buffer[3]; + stream->read(buffer, 3); + + if (stream->gcount() != 3) + { + return 0.0f; + } + + const auto byte0 = static_cast(buffer[0]); + const auto byte1 = static_cast(buffer[1]); + const auto byte2 = static_cast(buffer[2]); + const auto sample0 = static_cast( + byte0 | ((byte1 & 0x0fU) << 8U)); + const auto sample1 = static_cast( + (byte2 << 4U) | ((byte1 & 0xf0U) >> 4U)); + + const auto decodedSample0 = + mpc60SampleDecoder.decodeImportedFloat(sample0, false); + mpc60PendingSample = + mpc60SampleDecoder.decodeImportedFloat(sample1, true); + mpc60HasPendingSample = true; + + ++mpc60DecodedSampleIndex; + return decodedSample0; +} + int32_t SoundPlayer::readNext24BitInt() const { char buffer[3]; diff --git a/src/main/audiomidi/SoundPlayer.hpp b/src/main/audiomidi/SoundPlayer.hpp index 9c505b74b..1895771da 100644 --- a/src/main/audiomidi/SoundPlayer.hpp +++ b/src/main/audiomidi/SoundPlayer.hpp @@ -1,6 +1,8 @@ #pragma once #include "engine/audio/core/AudioProcess.hpp" +#include "SndInputFileStream.hpp" +#include "file/kaitai/Mpc60SampleDecoder.hpp" #include "sampler/Sound.hpp" #include @@ -35,6 +37,7 @@ namespace mpc::audiomidi unsigned long resamplerGeneratedFrameCounter = 0; std::shared_ptr inputAudioFormat; SoundPlayerFileFormat fileFormat; + SndInputEncoding sndInputEncoding = SndInputEncoding::Mpc2000XlPcm16; bool wavSamplesAreFloat32 = false; float fadeFactor = -1.0f; std::atomic stopEarly{false}; @@ -44,7 +47,8 @@ namespace mpc::audiomidi int32_t readNext24BitInt() const; int32_t readNextInt32() const; float readNextFloat32() const; - float readNextFrame() const; + float readNextFrame(); + float readNextMpc60Frame(); std::atomic playing{false}; std::string filePath; @@ -58,6 +62,10 @@ namespace mpc::audiomidi int srcLeftError = 0; int srcRightError = 0; std::shared_ptr stream; + mpc::file::kaitai::Mpc60SampleDecoder mpc60SampleDecoder; + bool mpc60HasPendingSample = false; + float mpc60PendingSample = 0.0f; + int mpc60DecodedSampleIndex = 0; public: bool start(const std::shared_ptr &, SoundPlayerFileFormat, diff --git a/src/main/command/ReleaseFunctionCommand.cpp b/src/main/command/ReleaseFunctionCommand.cpp index 4ea4fdc91..bbc125f08 100644 --- a/src/main/command/ReleaseFunctionCommand.cpp +++ b/src/main/command/ReleaseFunctionCommand.cpp @@ -6,6 +6,7 @@ #include "engine/SequencerPlaybackEngine.hpp" #include "lcdgui/screens/window/DirectoryScreen.hpp" +#include "lcdgui/screens/window/LoadASequenceScreen.hpp" #include "sequencer/Sequencer.hpp" using namespace mpc::command; @@ -33,6 +34,19 @@ void ReleaseFunctionCommand::execute() { mpc.getEngineHost()->finishPreviewSoundPlayerVoice(); } + else if (ls->isCurrentScreen( + {ScreenId::LoadASequencePlayScreen, + ScreenId::LoadASequenceScreen})) + { + mpc.screens->get() + ->releasePlayPreview(); + + if (ls->isCurrentScreen( + {ScreenId::LoadASequencePlayScreen})) + { + ls->closeCurrentScreen(); + } + } break; case 4: { diff --git a/src/main/concurrency/AtomicStateExchange.hpp b/src/main/concurrency/AtomicStateExchange.hpp index 39572f3b6..b8c89d580 100644 --- a/src/main/concurrency/AtomicStateExchange.hpp +++ b/src/main/concurrency/AtomicStateExchange.hpp @@ -47,6 +47,10 @@ namespace mpc::concurrency callbackQueue.enqueue(std::move(cb)); } + // Must be called by the owner thread for this state exchange. For + // PerformanceManager, that is the audio thread; other threads should + // enqueue messages and, if needed, enqueue a callback to observe + // completion after the owner thread has published the new state. void drainQueue() noexcept { alignas(Message) unsigned char msgBuf[sizeof(Message)]; diff --git a/src/main/disk/AbstractDisk.cpp b/src/main/disk/AbstractDisk.cpp index fde0c71ed..893fa9282 100644 --- a/src/main/disk/AbstractDisk.cpp +++ b/src/main/disk/AbstractDisk.cpp @@ -8,13 +8,12 @@ #include "disk/ProgramLoader.hpp" #include "file/wav/WavFile.hpp" -#include "file/mid/MidiWriter.hpp" -#include "file/mid/MidiReader.hpp" -#include "file/pgmwriter/PgmWriter.hpp" -#include "file/sndwriter/SndWriter.hpp" -#include "file/sndreader/SndReader.hpp" -#include "file/aps/ApsParser.hpp" -#include "file/all/AllParser.hpp" +#include "file/kaitai/SndIo.hpp" +#include "file/kaitai/PgmIo.hpp" +#include "file/kaitai/ApsIo.hpp" +#include "file/kaitai/MidIo.hpp" +#include "file/kaitai/Mpc3000SeqIo.hpp" +#include "file/kaitai/AllIo.hpp" #include "sampler/Sampler.hpp" #include "sampler/Program.hpp" @@ -40,13 +39,6 @@ using namespace mpc::input::midi; using namespace mpc::file; using namespace mpc::file::wav; -using namespace mpc::file::sndwriter; -using namespace mpc::file::sndreader; -using namespace mpc::file::mid; -using namespace mpc::file::pgmwriter; -using namespace mpc::file::aps; -using namespace mpc::file::all; - using namespace mpc::lcdgui; using namespace mpc::lcdgui::screens; using namespace mpc::lcdgui::screens::window; @@ -166,8 +158,7 @@ void AbstractDisk::writeSnd(const std::shared_ptr &s, const auto name = Util::getFileName( fileName == "" ? s->getName() + ".SND" : fileName); auto f = newFile(name); - auto sw = SndWriter(s.get()); - auto &sndArray = sw.getSndFileArray(); + auto sndArray = file::kaitai::SndIo::saveSound(*s); f->setFileData(sndArray); flush(); initFiles(); @@ -237,8 +228,7 @@ void AbstractDisk::writeMid(const std::shared_ptr &s, return tl::make_unexpected( mpc_io_error_msg{"Unable to open MIDI output stream"}); } - const MidiWriter writer(s.get()); - writer.writeToOStream(outputStream); + file::kaitai::MidIo::save(s, outputStream); flush(); initFiles(); mpc.getLayeredScreen()->showPopupAndThenReturnToLayer( @@ -330,8 +320,7 @@ void AbstractDisk::writePgm(const std::shared_ptr &p, const std::function writeFunc = [&]() -> file_or_error { auto f = newFile(fileName); - const PgmWriter writer(p.get(), mpc.getSampler()); - auto bytes = writer.get(); + auto bytes = file::kaitai::PgmIo::saveProgram(*p, mpc.getSampler()); f->setFileData(bytes); const std::string popupMsg = "Saving " + fileName; @@ -394,8 +383,7 @@ void AbstractDisk::writeAps(const std::string &fileName) { auto f = newFile(fileName); const auto apsName = f->getNameWithoutExtension(); - ApsParser apsParser(mpc, apsName); - auto bytes = apsParser.getBytes(); + auto bytes = file::kaitai::ApsIo::save(mpc, apsName); f->setFileData(bytes); auto saveAProgramScreen = @@ -439,8 +427,7 @@ void AbstractDisk::writeAll(const std::string &fileName) const std::function writeFunc = [&]() -> file_or_error { auto f = newFile(fileName); - AllParser allParser(mpc); - auto bytes = allParser.getBytes(); + auto bytes = file::kaitai::AllIo::save(mpc); f->setFileData(bytes); flush(); @@ -573,45 +560,21 @@ sound_or_error AbstractDisk::readWav2( return performRequiredIoOrOpenErrorPopup(readFunc); } -sound_or_error AbstractDisk::readSnd2( - std::shared_ptr f, - std::function)> onSuccess) +sequence_or_error AbstractDisk::readMid2(std::shared_ptr f) { - const std::function readFunc = [f, onSuccess] + const std::function readFunc = [this, f]() -> sequence_or_error { - return onSuccess(std::make_shared(f.get())); + return file::kaitai::MidIo::load(mpc, f); }; + return performRequiredIoOrOpenErrorPopup(readFunc); } -sequence_or_error AbstractDisk::readMid2(std::shared_ptr f) +sequence_or_error AbstractDisk::readSeq2(std::shared_ptr f) { const std::function readFunc = [this, f]() -> sequence_or_error { - if (!f) - { - return tl::make_unexpected(mpc_io_error_msg{"Empty file"}); - } - - const auto fStream = f->getInputStream(); - if (!fStream) - { - return tl::make_unexpected( - mpc_io_error_msg{"Unable to open MIDI input stream"}); - } - auto newSeq = mpc.getSequencer()->getSequence(TempSequenceIndex); - - newSeq->init(0); - - const MidiReader midiReader(fStream, newSeq); - midiReader.parseSequence(mpc); - - if (newSeq->getName().empty()) - { - newSeq->setName(f->getNameWithoutExtension()); - } - - return newSeq; + return file::kaitai::Mpc3000SeqIo::load(mpc, f); }; return performRequiredIoOrOpenErrorPopup(readFunc); diff --git a/src/main/disk/AbstractDisk.hpp b/src/main/disk/AbstractDisk.hpp index a23c41681..dce5af673 100644 --- a/src/main/disk/AbstractDisk.hpp +++ b/src/main/disk/AbstractDisk.hpp @@ -44,11 +44,6 @@ namespace mpc::file::wav class WavFile; } -namespace mpc::file::sndreader -{ - class SndReader; -} - namespace mpc::disk { @@ -173,11 +168,8 @@ namespace mpc::disk std::shared_ptr, std::function)> onSuccess); - sound_or_error - readSnd2(std::shared_ptr, - std::function)>); sequence_or_error readMid2(std::shared_ptr); + sequence_or_error readSeq2(std::shared_ptr); void readAps2(std::shared_ptr, std::function onSuccess, bool headless); diff --git a/src/main/disk/AllLoader.cpp b/src/main/disk/AllLoader.cpp index d121b26a0..3606da1f8 100644 --- a/src/main/disk/AllLoader.cpp +++ b/src/main/disk/AllLoader.cpp @@ -3,23 +3,21 @@ #include "Mpc.hpp" #include "StrUtil.hpp" +#include "file/ByteUtil.hpp" #include "controller/ClientEventController.hpp" #include "controller/ClientMidiEventController.hpp" #include "controller/MidiFootswitchFunctionMap.hpp" -#include "file/all/AllParser.hpp" -#include "file/all/Count.hpp" -#include "file/all/Defaults.hpp" -#include "file/all/MidiInput.hpp" -#include "file/all/MidiSyncMisc.hpp" -#include "file/all/Misc.hpp" -#include "file/all/AllSequence.hpp" -#include "file/all/SequenceNames.hpp" -#include "file/all/AllSequencer.hpp" -#include "file/all/AllSong.hpp" +#include "file/kaitai/AllIo.hpp" +#include "file/kaitai/KaitaiIoUtil.hpp" +#include "file/kaitai/Mpc3000SeqIo.hpp" +#include "file/kaitai/generated/mpc2000xl_all.h" +#include "file/kaitai/generated/mpc60_all_v2.h" +#include "file/kaitai/generated/mpc3000_all_v3.h" #include "disk/MpcFile.hpp" #include "sequencer/Sequencer.hpp" +#include "sequencer/Sequence.hpp" #include "sequencer/Transport.hpp" #include "sequencer/Song.hpp" @@ -41,6 +39,617 @@ #include "lcdgui/screens/dialog/MetronomeSoundScreen.hpp" #include "sequencer/SequencerStateManager.hpp" +#include +#include +#include +#include +#include + +namespace { + +enum class all_variant_t +{ + mpc2000xl, + mpc60_v2, + mpc3000_v3, + unknown +}; + +constexpr size_t kLegacyAllHeaderSize = 6; +constexpr size_t kLegacyAllEmbeddedPreludeSize = + 37 + 5 + (64 * 4) + 2 + 15 + 3 + 16 + 1 + 1 + 1; +constexpr size_t kMpc3000TrackHeaderSize = 24; +constexpr size_t kMpc3000TempoChangeSize = 6; + +bool startsWith(const std::vector& bytes, const std::string_view prefix) +{ + return bytes.size() >= prefix.size() && + std::equal(prefix.begin(), prefix.end(), bytes.begin()); +} + +all_variant_t detectAllVariant(const std::vector& bytes) +{ + if (startsWith(bytes, "MPC2KXL ALL 1.00")) + { + return all_variant_t::mpc2000xl; + } + + if (bytes.size() >= 2 && + static_cast(bytes[0]) == 0x04 && + static_cast(bytes[1]) == 0x02) + { + return all_variant_t::mpc60_v2; + } + + if (bytes.size() >= 2 && + static_cast(bytes[0]) == 0x04 && + static_cast(bytes[1]) == 0x03) + { + return all_variant_t::mpc3000_v3; + } + + return all_variant_t::unknown; +} + +std::string bytesUntilNull(const std::string& value) +{ + const auto pos = value.find('\0'); + return pos == std::string::npos ? value : value.substr(0, pos); +} + +mpc::sequencer::EventData +parsedEventToEventData(const mpc2000xl_all_t::event_t& parsedEvent) +{ + mpc::sequencer::EventData e; + e.tick = parsedEvent.tick(); + e.trackIndex = mpc::TrackIndex(parsedEvent.track()); + + if (parsedEvent.note_event() != nullptr) + { + e.type = mpc::sequencer::EventType::NoteOn; + e.noteNumber = mpc::NoteNumber(parsedEvent.note_event()->note()); + e.duration = mpc::Duration(parsedEvent.note_event()->duration()); + e.velocity = mpc::Velocity(parsedEvent.note_event()->velocity()); + e.noteVariationType = + mpc::NoteVariationType(parsedEvent.note_event()->variation_type()); + e.noteVariationValue = + mpc::NoteVariationValue(parsedEvent.note_event()->variation_value()); + return e; + } + + if (parsedEvent.pitch_bend() != nullptr) + { + e.type = mpc::sequencer::EventType::PitchBend; + auto candidate = static_cast( + parsedEvent.pitch_bend()->amount_bits_1() + + (parsedEvent.pitch_bend()->amount_bits_2() << 8) + ) - 16384; + if (candidate < -8192) + { + candidate += 8192; + } + e.amount = candidate; + return e; + } + + if (parsedEvent.control_change() != nullptr) + { + e.type = mpc::sequencer::EventType::ControlChange; + e.controllerNumber = parsedEvent.control_change()->controller(); + e.controllerValue = parsedEvent.control_change()->value(); + return e; + } + + if (parsedEvent.program_change() != nullptr) + { + e.type = mpc::sequencer::EventType::ProgramChange; + e.programChangeProgramIndex = + mpc::ProgramIndex(parsedEvent.program_change()->program()); + return e; + } + + if (parsedEvent.ch_pressure() != nullptr) + { + e.type = mpc::sequencer::EventType::ChannelPressure; + e.amount = parsedEvent.ch_pressure()->pressure(); + return e; + } + + if (parsedEvent.poly_pressure() != nullptr) + { + e.type = mpc::sequencer::EventType::PolyPressure; + e.noteNumber = mpc::NoteNumber(parsedEvent.poly_pressure()->note()); + e.amount = parsedEvent.poly_pressure()->pressure(); + return e; + } + + if (parsedEvent.exclusive() != nullptr) + { + if (parsedEvent.exclusive()->mixer() != nullptr) + { + e.type = mpc::sequencer::EventType::Mixer; + auto mixerParameter = + static_cast(parsedEvent.exclusive()->mixer()->param()) - 1; + if (mixerParameter == 4) + { + mixerParameter = 3; + } + e.mixerParameter = mixerParameter; + e.mixerPad = parsedEvent.exclusive()->mixer()->pad_index(); + e.mixerValue = parsedEvent.exclusive()->mixer()->value(); + } + else + { + e.type = mpc::sequencer::EventType::SystemExclusive; + const auto& bytes = parsedEvent.exclusive()->bytes(); + e.sysExByteA = bytes.size() > 0 + ? static_cast(bytes[0]) + : 0; + e.sysExByteB = bytes.size() > 1 + ? static_cast(bytes[1]) + : 0; + } + } + + return e; +} + +void applyParsedSequenceToInMemorySequence( + mpc2000xl_all_t& parsed, + const size_t sourceIndex, + const std::shared_ptr& inMemorySequence, + mpc::sequencer::SequencerStateManager* const manager) +{ + auto& parsedSequence = *parsed.sequences()->at(sourceIndex); + auto* body = parsedSequence.body(); + if (body == nullptr) + { + return; + } + + const auto tempoBytes = body->_unnamed2(); + const auto tempo = static_cast(mpc::file::ByteUtil::bytes2ushort({tempoBytes[3], tempoBytes[4]})) / 10.0; + + inMemorySequence->init(body->bar_count() - 1); + + for (size_t i = 0; i < body->bars()->size(); ++i) + { + inMemorySequence->setTimeSignature( + static_cast(i), + body->bars()->at(i)->numerator(), + body->bars()->at(i)->denominator() + ); + } + + const auto slotIndex = static_cast(body->index() - 1); + const auto* sequenceMeta = parsed.sequences_metas()->at(slotIndex).get(); + inMemorySequence->setName(mpc::StrUtil::trim(bytesUntilNull(sequenceMeta->name()))); + inMemorySequence->setInitialTempo(tempo); + + const auto sequenceIndex = inMemorySequence->getSequenceIndex(); + + mpc::sequencer::UpdateSequenceTracks updateSequenceTracks{sequenceIndex}; + manager->trackStatesSnapshots[sequenceIndex] = mpc::sequencer::SequenceTrackStatesSnapshot(); + updateSequenceTracks.trackStates = &manager->trackStatesSnapshots[sequenceIndex]; + + auto& trackStates = *updateSequenceTracks.trackStates; + const auto* parsedTracks = body->tracks(); + + for (int i = 0; i < mpc::Mpc2000XlSpecs::TRACK_COUNT; ++i) + { + const auto* status = parsedTracks->status()->at(i).get(); + trackStates[i].name = bytesUntilNull(parsedTracks->names()->at(i)); + trackStates[i].deviceIndex = parsedTracks->device()->at(i); + trackStates[i].busType = mpc::sequencer::busIndexToBusType(static_cast(parsedTracks->bus()->at(i))); + trackStates[i].programChange = parsedTracks->program_change()->at(i); + trackStates[i].on = status->off_or_on() == mpc2000xl_all_t::OFF_ON_TRUE; + trackStates[i].velocityRatio = parsedTracks->velocity_ratio()->at(i); + trackStates[i].transmitProgramChangesEnabled = + status->transmit_program_changes() == mpc2000xl_all_t::OFF_ON_TRUE; + trackStates[i].used = status->unused_or_used() == mpc2000xl_all_t::UNUSED_USED_USED; + } + + manager->enqueue(updateSequenceTracks); + + mpc::sequencer::UpdateSequenceEvents updateSequenceEvents{sequenceIndex}; + updateSequenceEvents.trackSnapshots = &manager->trackEventsSnapshots[sequenceIndex]; + updateSequenceEvents.trackSnapshots->clear(); + + for (const auto& parsedEvent : *body->events()) + { + if (parsedEvent->tick() >= 0xFFFFF) + { + continue; + } + + auto e = parsedEventToEventData(*parsedEvent); + if (e.type == mpc::sequencer::EventType::Unknown) + { + continue; + } + + (*updateSequenceEvents.trackSnapshots)[e.trackIndex].push_back(e); + } + + manager->enqueue(updateSequenceEvents); + + for (int i = 0; i < 32; i++) + { + inMemorySequence->setDeviceName(i, bytesUntilNull(body->device_names()->at(i + 1))); + } + + inMemorySequence->setFirstLoopBarIndex(mpc::BarIndex(body->loop_start_bar_index())); + if (body->loop_end_bar_index() > 998) + { + inMemorySequence->setLastLoopBarIndex(mpc::EndOfSequence); + } + else + { + inMemorySequence->setLastLoopBarIndex(mpc::BarIndex(body->loop_end_bar_index())); + } + + inMemorySequence->setLoopEnabled(body->loop_enabled()); + inMemorySequence->getStartTime().hours = body->start_time()->hours(); + inMemorySequence->getStartTime().minutes = body->start_time()->minutes(); + inMemorySequence->getStartTime().seconds = body->start_time()->seconds(); + inMemorySequence->getStartTime().frames = body->start_time()->frames(); + inMemorySequence->getStartTime().frameDecimals = body->start_time()->frame_decimals(); +} + +size_t embeddedMpc3000SequenceByteCount( + const mpc3000_all_v3_t::sequence_t& sequence) +{ + const auto* misc = sequence.misc_chunks(); + return kLegacyAllEmbeddedPreludeSize + + (static_cast(misc->num_track_headers()) * + kMpc3000TrackHeaderSize) + + (static_cast(misc->num_tempo_changes()) * + kMpc3000TempoChangeSize) + + static_cast( + misc->sequence_header()->event_stream_length_in_bytes()->value()); +} + +std::vector buildStandaloneMpc3000SequenceBytes( + const std::vector& allBytes, + const size_t sequenceOffset, + const mpc3000_all_v3_t::sequence_t& sequence) +{ + const auto embeddedByteCount = embeddedMpc3000SequenceByteCount(sequence); + const auto sequenceEnd = sequenceOffset + embeddedByteCount; + if (sequenceEnd > allBytes.size()) + { + throw std::runtime_error("MPC3000 ALL sequence extends past end of file"); + } + + std::vector result; + result.reserve(2 + embeddedByteCount); + result.push_back(0x03); + result.push_back(0x03); + result.insert( + result.end(), + allBytes.begin() + static_cast(sequenceOffset), + allBytes.begin() + static_cast(sequenceEnd) + ); + return result; +} + +void loadEverythingFromMpc3000AllBytes( + mpc::Mpc& mpc, + const std::vector& bytes) +{ + std::stringstream parseStream( + std::string(bytes.begin(), bytes.end()), + std::ios::in | std::ios::out | std::ios::binary + ); + kaitai::kstream parseIo(&parseStream); + mpc3000_all_v3_t parsed(&parseIo); + parsed._read(); + + auto sequencer = mpc.getSequencer(); + auto stateManager = sequencer->getStateManager(); + + sequencer->deleteAllSequences(); + for (int i = 0; i < 20; ++i) + { + sequencer->getSong(i)->setUsed(false); + } + stateManager->drainQueue(); + + size_t sequenceOffset = kLegacyAllHeaderSize; + for (size_t i = 0; i < parsed.sequences()->size(); ++i) + { + const auto seqBytes = buildStandaloneMpc3000SequenceBytes( + bytes, + sequenceOffset, + *parsed.sequences()->at(i) + ); + sequenceOffset += embeddedMpc3000SequenceByteCount(*parsed.sequences()->at(i)); + + const auto name = mpc::StrUtil::trim( + parsed.sequences()->at(i)->misc_chunks()->sequence_header()->sequence_name()); + const auto loaded = mpc::file::kaitai::Mpc3000SeqIo::loadBytes(mpc, seqBytes, name); + if (!loaded) + { + throw std::runtime_error(loaded.error()); + } + + sequencer->copySequence( + mpc::TempSequenceIndex, + mpc::SequenceIndex(static_cast(i)) + ); + stateManager->drainQueue(); + } + + for (const auto& parsedSongPtr : *parsed.songs()) + { + const auto* body = parsedSongPtr->song_body(); + if (body == nullptr) + { + continue; + } + + const auto targetIndex = std::clamp( + static_cast(body->song_number()) - 1, + 0, + 19 + ); + const auto song = sequencer->getSong(targetIndex); + song->setUsed(true); + song->setName(mpc::StrUtil::trim(body->song_name())); + + for (size_t stepIndex = 0; stepIndex < body->steps()->size(); ++stepIndex) + { + const auto* parsedStep = body->steps()->at(stepIndex).get(); + song->insertStep(mpc::SongStepIndex(static_cast(stepIndex))); + song->setStepSequenceIndex( + mpc::SongStepIndex(static_cast(stepIndex)), + mpc::SequenceIndex(std::max(0, static_cast(parsedStep->sequence_number()) - 1)) + ); + song->setStepRepetitionCount( + mpc::SongStepIndex(static_cast(stepIndex)), + parsedStep->repetition_count() + ); + } + + const auto loopEnabled = + body->end_status() == + mpc3000_all_v3_t::song_t::song_body_t::END_STATUS_LOOP_TO_A_STEP; + song->setLoopEnabled(loopEnabled); + song->setFirstLoopStepIndex( + mpc::SongStepIndex(std::max(0, static_cast(body->loop_back_step_number()) - 1)) + ); + song->setLastLoopStepIndex( + mpc::SongStepIndex(std::max(0, static_cast(body->steps()->size()) - 1)) + ); + } + + if (!parsed.sequences()->empty()) + { + sequencer->setSelectedSequenceIndex(mpc::MinSequenceIndex, false); + sequencer->setSelectedTrackIndex(0); + sequencer->getTransport()->setPosition(0); + } +} + +void loadEverythingFromMpc60AllBytes( + mpc::Mpc& mpc, + const std::vector& bytes) +{ + std::stringstream parseStream( + std::string(bytes.begin(), bytes.end()), + std::ios::in | std::ios::out | std::ios::binary + ); + kaitai::kstream parseIo(&parseStream); + mpc60_all_v2_t parsed(&parseIo); + parsed._read(); + + auto sequencer = mpc.getSequencer(); + auto stateManager = sequencer->getStateManager(); + + sequencer->deleteAllSequences(); + for (int i = 0; i < 20; ++i) + { + sequencer->getSong(i)->setUsed(false); + } + stateManager->drainQueue(); + + for (size_t i = 0; i < parsed.body()->sequences()->size(); ++i) + { + const auto& sequence = *parsed.body()->sequences()->at(i); + const auto name = mpc::StrUtil::trim(sequence.sequence_header()->sequence_name()); + const auto loaded = + mpc::file::kaitai::Mpc3000SeqIo::loadMpc60Sequence(mpc, sequence, name); + if (!loaded) + { + throw std::runtime_error(loaded.error()); + } + + sequencer->copySequence( + mpc::TempSequenceIndex, + mpc::SequenceIndex(static_cast(i)) + ); + stateManager->drainQueue(); + } + + for (const auto& parsedSongPtr : *parsed.body()->songs()) + { + auto* songBody = + dynamic_cast(parsedSongPtr->body()); + if (songBody == nullptr) + { + continue; + } + + const auto targetIndex = std::clamp( + static_cast(songBody->song_number()) - 1, + 0, + 19 + ); + const auto song = sequencer->getSong(targetIndex); + song->setUsed(true); + song->setName(mpc::StrUtil::trim(songBody->song_name())); + + for (size_t stepIndex = 0; stepIndex < songBody->steps()->size(); ++stepIndex) + { + const auto* parsedStep = songBody->steps()->at(stepIndex).get(); + const auto songStepIndex = mpc::SongStepIndex(static_cast(stepIndex)); + song->insertStep(songStepIndex); + song->setStepSequenceIndex( + songStepIndex, + mpc::SequenceIndex(std::max( + 0, static_cast(parsedStep->sequence_number()) - 1)) + ); + song->setStepRepetitionCount(songStepIndex, parsedStep->repeats()); + } + + song->setFirstLoopStepIndex(mpc::SongStepIndex(0)); + song->setLastLoopStepIndex( + mpc::SongStepIndex(std::max( + 0, static_cast(songBody->steps()->size()) - 1)) + ); + song->setLoopEnabled(false); + } + + if (!parsed.body()->sequences()->empty()) + { + sequencer->setSelectedSequenceIndex(mpc::MinSequenceIndex, false); + sequencer->setSelectedTrackIndex(0); + sequencer->getTransport()->setPosition(0); + } +} + +std::vector loadMpc3000SequenceMetaInfos( + const std::vector& bytes) +{ + std::stringstream parseStream( + std::string(bytes.begin(), bytes.end()), + std::ios::in | std::ios::out | std::ios::binary + ); + kaitai::kstream parseIo(&parseStream); + mpc3000_all_v3_t parsed(&parseIo); + parsed._read(); + + std::vector result; + result.reserve(parsed.sequences()->size()); + + for (const auto& sequence : *parsed.sequences()) + { + result.push_back(mpc::sequencer::SequenceMetaInfo{ + true, + mpc::StrUtil::trim(sequence->misc_chunks()->sequence_header()->sequence_name()) + }); + } + + return result; +} + +std::vector loadMpc60SequenceMetaInfos( + const std::vector& bytes) +{ + std::stringstream parseStream( + std::string(bytes.begin(), bytes.end()), + std::ios::in | std::ios::out | std::ios::binary + ); + kaitai::kstream parseIo(&parseStream); + mpc60_all_v2_t parsed(&parseIo); + parsed._read(); + + std::vector result; + result.reserve(parsed.body()->sequences()->size()); + + for (const auto& sequence : *parsed.body()->sequences()) + { + result.push_back(mpc::sequencer::SequenceMetaInfo{ + true, + mpc::StrUtil::trim(sequence->sequence_header()->sequence_name()) + }); + } + + return result; +} + +std::shared_ptr loadOneSequenceFromMpc3000AllBytes( + mpc::Mpc& mpc, + const std::vector& bytes, + const mpc::SequenceIndex sourceIndexInAllFile, + const mpc::SequenceIndex destIndexInMpcMemory) +{ + std::stringstream parseStream( + std::string(bytes.begin(), bytes.end()), + std::ios::in | std::ios::out | std::ios::binary + ); + kaitai::kstream parseIo(&parseStream); + mpc3000_all_v3_t parsed(&parseIo); + parsed._read(); + + const auto sourceIndex = static_cast(sourceIndexInAllFile); + const auto destination = mpc.getSequencer()->getSequence(destIndexInMpcMemory); + if (sourceIndex >= parsed.sequences()->size()) + { + return destination; + } + + size_t sequenceOffset = kLegacyAllHeaderSize; + for (size_t i = 0; i < sourceIndex; ++i) + { + sequenceOffset += embeddedMpc3000SequenceByteCount(*parsed.sequences()->at(i)); + } + + const auto seqBytes = buildStandaloneMpc3000SequenceBytes( + bytes, + sequenceOffset, + *parsed.sequences()->at(sourceIndex) + ); + const auto name = mpc::StrUtil::trim( + parsed.sequences()->at(sourceIndex)->misc_chunks()->sequence_header()->sequence_name()); + const auto loaded = mpc::file::kaitai::Mpc3000SeqIo::loadBytes(mpc, seqBytes, name); + if (!loaded) + { + throw std::runtime_error(loaded.error()); + } + + mpc.getSequencer()->copySequence(mpc::TempSequenceIndex, destIndexInMpcMemory); + mpc.getSequencer()->getStateManager()->drainQueue(); + return destination; +} + +std::shared_ptr loadOneSequenceFromMpc60AllBytes( + mpc::Mpc& mpc, + const std::vector& bytes, + const mpc::SequenceIndex sourceIndexInAllFile, + const mpc::SequenceIndex destIndexInMpcMemory) +{ + std::stringstream parseStream( + std::string(bytes.begin(), bytes.end()), + std::ios::in | std::ios::out | std::ios::binary + ); + kaitai::kstream parseIo(&parseStream); + mpc60_all_v2_t parsed(&parseIo); + parsed._read(); + + const auto sourceIndex = static_cast(sourceIndexInAllFile); + const auto destination = mpc.getSequencer()->getSequence(destIndexInMpcMemory); + if (sourceIndex >= parsed.body()->sequences()->size()) + { + return destination; + } + + const auto& sequence = *parsed.body()->sequences()->at(sourceIndex); + const auto name = mpc::StrUtil::trim(sequence.sequence_header()->sequence_name()); + const auto loaded = + mpc::file::kaitai::Mpc3000SeqIo::loadMpc60Sequence(mpc, sequence, name); + if (!loaded) + { + throw std::runtime_error(loaded.error()); + } + + mpc.getSequencer()->copySequence(mpc::TempSequenceIndex, destIndexInMpcMemory); + mpc.getSequencer()->getStateManager()->drainQueue(); + return destination; +} + +} + using namespace mpc::lcdgui; using namespace mpc::lcdgui::screens; using namespace mpc::lcdgui::screens::window; @@ -51,145 +660,182 @@ using namespace mpc::sequencer; void AllLoader::loadEverythingFromFile(Mpc &mpc, MpcFile *f) { - AllParser allParser(mpc, f->getBytes()); - loadEverythingFromAllParser(mpc, allParser); + loadEverythingFromBytes(mpc, f->getBytes()); +} + +void AllLoader::loadEverythingFromBytes(Mpc &mpc, const std::vector &bytes) +{ + switch (detectAllVariant(bytes)) + { + case all_variant_t::mpc2000xl: + { + const auto canonicalBytes = file::kaitai::parseRewrite(bytes); + loadEverythingFromCanonicalBytes(mpc, canonicalBytes); + return; + } + case all_variant_t::mpc60_v2: + loadEverythingFromMpc60AllBytes(mpc, bytes); + return; + case all_variant_t::mpc3000_v3: + loadEverythingFromMpc3000AllBytes(mpc, bytes); + return; + default: + throw std::runtime_error("Unsupported ALL file format"); + } } -void AllLoader::loadEverythingFromAllParser(Mpc &mpc, AllParser &allParser) +void AllLoader::loadEverythingFromCanonicalBytes( + Mpc &mpc, + const std::vector &canonicalBytes) { + std::stringstream parseStream( + std::string(canonicalBytes.begin(), canonicalBytes.end()), + std::ios::in | std::ios::out | std::ios::binary + ); + kaitai::kstream parseIo(&parseStream); + mpc2000xl_all_t parsed(&parseIo); + parsed._read(); + auto mpcSequencer = mpc.getSequencer(); - auto allSequences = allParser.getAllSequences(); - auto allSeqNames = allParser.getSeqNames()->getNames(); - auto defaults = allParser.getDefaults(); - auto userScreen = mpc.screens->get(); + auto* defaults = parsed.defaults(); + auto* parsedSequencer = parsed.sequencer(); + auto* songGlobal = parsed.song_global(); - userScreen->setLastBar(defaults->getBarCount() - 1); - userScreen->setLoop(defaults->isLoopEnabled()); - userScreen->setBus(busIndexToBusType(defaults->getBusses()[0])); + auto userScreen = mpc.screens->get(); - mpcSequencer->setDefaultSequenceName(defaults->getDefaultSeqName()); - auto defTrackNames = defaults->getDefaultTrackNames(); + userScreen->setLastBar(static_cast(defaults->bar_count()) - 1); + userScreen->setLoop(defaults->unknown2()[4] != 0x00); + userScreen->setBus(mpc::sequencer::busIndexToBusType(static_cast(defaults->buses()->at(0)))); - for (int i = 0; i < Mpc2000XlSpecs::TRACK_COUNT; ++i) + mpcSequencer->setDefaultSequenceName(bytesUntilNull(defaults->sequence_name())); + for (int i = 0; i < mpc::Mpc2000XlSpecs::TRACK_COUNT; ++i) { - mpcSequencer->setDefaultTrackName(i, defTrackNames[i]); + mpcSequencer->setDefaultTrackName(i, bytesUntilNull(defaults->track_names()->at(i))); } - userScreen->setDeviceNumber(defaults->getDevices()[0]); - userScreen->setTimeSig(defaults->getTimeSigNum(), - defaults->getTimeSigDen()); - userScreen->setPgm(defaults->getPgms()[0]); - userScreen->setTempo(defaults->getTempo() * 0.1); - userScreen->setVelo(defaults->getTrVelos()[0]); - - int index = 0; + userScreen->setDeviceNumber(defaults->devices()->at(0)); + userScreen->setTimeSig(defaults->numerator(), defaults->denominator()); + userScreen->setPgm(defaults->programs()->at(0)); + userScreen->setTempo(defaults->tempo() * 0.1); + userScreen->setVelo(defaults->track_velocities()->at(0)); mpcSequencer->deleteAllSequences(); - for (auto &as : allSequences) + for (size_t sourceIndex = 0; sourceIndex < parsed.sequences()->size(); ++sourceIndex) { - if (as == nullptr) + if (parsed.sequences()->at(sourceIndex)->body() == nullptr) { - index++; continue; } - auto mpcSeq = mpcSequencer->getSequence(index++); - as->applyToInMemorySequence(mpcSeq, - mpcSequencer->getStateManager().get()); + const auto targetIndex = static_cast(parsed.sequences()->at(sourceIndex)->body()->index()) - 1; + auto mpcSeq = mpcSequencer->getSequence(targetIndex); + applyParsedSequenceToInMemorySequence( + parsed, + sourceIndex, + mpcSeq, + mpcSequencer->getStateManager().get() + ); } - auto allParserSequencer = allParser.getSequencer(); mpcSequencer->setSelectedSequenceIndex( - SequenceIndex(allParserSequencer->sequence), false); + SequenceIndex(parsedSequencer->active_sequence()), false); mpcSequencer->getTransport()->setPosition(0); - mpcSequencer->setSelectedTrackIndex(allParserSequencer->track); + mpcSequencer->setSelectedTrackIndex(parsedSequencer->active_track()); mpcSequencer->getTransport()->setMasterTempo( - allParserSequencer->masterTempo); + parsedSequencer->master_tempo() * 0.1); mpcSequencer->getTransport()->setTempoSourceIsSequence( - allParserSequencer->tempoSourceIsSequence); + parsedSequencer->tempo_source_is_sequence()); auto timingCorrectScreen = mpc.screens->get(); - timingCorrectScreen->setNoteValue(allParserSequencer->tc); + timingCorrectScreen->setNoteValue(static_cast(parsedSequencer->timing_correct())); auto timeDisplayScreen = mpc.screens->get(); - timeDisplayScreen->setDisplayStyle(allParserSequencer->timeDisplayStyle); + timeDisplayScreen->setDisplayStyle( + static_cast(parsedSequencer->time_display_style())); - auto count = allParser.getCount(); + auto* count = parsed.count(); + auto* midiOutput = parsed.midi_output(); + auto* midiInput = parsed.midi_input(); + auto* midiSync = parsed.midi_sync(); + auto* misc = parsed.misc(); + auto* stepEditOptions = parsed.step_edit_options(); auto countMetronomeScreen = mpc.screens->get(); auto metronomeSoundScreen = mpc.screens->get(); - countMetronomeScreen->setCountIn(count->getCountInMode()); - metronomeSoundScreen->accentPad = count->getAccentPad(); - metronomeSoundScreen->normalPad = count->getNormalPad(); - metronomeSoundScreen->setAccentVelo(count->getAccentVelo()); - metronomeSoundScreen->setNormalVelo(count->getNormalVelo()); - metronomeSoundScreen->setOutput(count->getClickOutput()); - metronomeSoundScreen->setVolume(count->getClickVolume()); - countMetronomeScreen->setRate(count->getRate()); - metronomeSoundScreen->setSound(count->getSound()); - countMetronomeScreen->setInPlay(count->isEnabledInPlay()); - countMetronomeScreen->setInRec(count->isEnabledInRec()); - countMetronomeScreen->setWaitForKey(count->isWaitForKeyEnabled()); - mpcSequencer->getTransport()->setCountEnabled(count->isEnabled()); - - auto midiInput = allParser.getMidiInput(); + countMetronomeScreen->setCountIn(static_cast(count->count_in_mode())); + metronomeSoundScreen->accentPad = count->accent_pad_index(); + metronomeSoundScreen->normalPad = count->normal_pad_index(); + metronomeSoundScreen->setAccentVelo(count->accent_velo()); + metronomeSoundScreen->setNormalVelo(count->normal_velo()); + metronomeSoundScreen->setOutput(static_cast(count->click_output())); + metronomeSoundScreen->setVolume(count->click_volume()); + countMetronomeScreen->setRate(static_cast(count->rate())); + metronomeSoundScreen->setSound(static_cast(count->sound_source())); + countMetronomeScreen->setInPlay(count->enabled_in_play()); + countMetronomeScreen->setInRec(count->enabled_in_rec()); + countMetronomeScreen->setWaitForKey(count->wait_for_key()); + mpcSequencer->getTransport()->setCountEnabled(count->enabled()); mpc.screens->get()->setSoftThru( - midiInput->getSoftThruMode()); + static_cast(midiOutput->soft_thru_mode())); auto midiInputScreen = mpc.screens->get(); - midiInputScreen->setReceiveCh(midiInput->getReceiveCh() - 1); - midiInputScreen->setType(midiInput->getFilterType()); - midiInputScreen->setMidiFilterEnabled(midiInput->isFilterEnabled()); + midiInputScreen->setReceiveCh( + static_cast(midiInput->receive_channel()) - 1); + midiInputScreen->setType(static_cast(midiInput->filter_type())); + midiInputScreen->setMidiFilterEnabled(midiInput->filter_enabled()); midiInputScreen->setSustainPedalToDuration( - midiInput->isSustainPedalToDurationEnabled()); - - auto trackDests = midiInput->getMultiRecTrackDests(); + midiInput->sustain_pedal_to_duration()); auto multiRecordingSetupScreen = mpc.screens->get(); - for (int i = 0; i < trackDests.size(); i++) + for (int i = 0; i < midiInput->multi_rec_destination_tracks()->size(); i++) { - multiRecordingSetupScreen->getMrsLines()[i]->setTrack(trackDests[i]); + multiRecordingSetupScreen->getMrsLines()[i]->setTrack( + midiInput->multi_rec_destination_tracks()->at(i) - 1); } midiInputScreen->setChPressurePassEnabled( - midiInput->isChPressurePassEnabled()); + midiInput->ch_pressure_pass_enabled()); midiInputScreen->setExclusivePassEnabled( - midiInput->isExclusivePassEnabled()); - mpcSequencer->setRecordingModeMulti(midiInput->isMultiRecEnabled()); - midiInputScreen->setNotePassEnabled(midiInput->isNotePassEnabled()); + midiInput->exclusive_pass_enabled()); + mpcSequencer->setRecordingModeMulti(midiInput->multi_rec_enabled()); + midiInputScreen->setNotePassEnabled(midiInput->note_pass_enabled()); midiInputScreen->setPgmChangePassEnabled( - midiInput->isPgmChangePassEnabled()); + midiInput->pgm_change_pass_enabled()); midiInputScreen->setPitchBendPassEnabled( - midiInput->isPitchBendPassEnabled()); + midiInput->pitch_bend_pass_enabled()); midiInputScreen->setPolyPressurePassEnabled( - midiInput->isPolyPressurePassEnabled()); - midiInputScreen->getCcPassEnabled() = midiInput->getCcPassEnabled(); + midiInput->poly_pressure_pass_enabled()); + midiInputScreen->getCcPassEnabled() = *midiInput->cc_pass_enabled(); - auto midiSyncMisc = allParser.getMidiSync(); - - auto misc = allParser.getMisc(); auto stepEditOptionsScreen = mpc.screens->get(); stepEditOptionsScreen->setAutoStepIncrementEnabled( - misc->isAutoStepIncEnabled()); + stepEditOptions->auto_step_increment()); stepEditOptionsScreen->setDurationOfRecordedNotesTcValue( - misc->isDurationOfRecNotesTc()); + stepEditOptions->duration_of_recorded_notes() == + mpc2000xl_all_t::DURATION_OF_RECORDED_NOTES_TC_VALUE); stepEditOptionsScreen->setTcValueRecordedNotes( - misc->getDurationTcPercentage()); - midiInputScreen->setProgChangeSeq(misc->isPgmChToSeqEnabled()); + stepEditOptions->tc_value_percentage()); + midiInputScreen->setProgChangeSeq(parsed.prog_change_to_seq()); auto locateScreen = mpc.screens->get(); - locateScreen->setLocations(misc->getLocations()); + std::vector locations; + locations.reserve(parsed.locations()->size()); + for (const auto& location : *parsed.locations()) + { + locations.emplace_back(location->bar(), location->beat(), location->clock()); + } + locateScreen->setLocations(locations); auto footswitchController = mpc.clientEventController->getClientMidiEventController() @@ -198,8 +844,10 @@ void AllLoader::loadEverythingFromAllParser(Mpc &mpc, AllParser &allParser) using MT = input::midi::MidiBindingBase::MessageType; int bindingCounter = 0; - for (auto [cc, functionIndex] : misc->getSwitches()) + for (const auto& midiSwitch : *misc->midi_switch()) { + const int cc = midiSwitch->controller() == 0xFF ? -1 : midiSwitch->controller(); + const auto functionIndex = static_cast(midiSwitch->function()); auto fn = static_cast(functionIndex); @@ -230,68 +878,74 @@ void AllLoader::loadEverythingFromAllParser(Mpc &mpc, AllParser &allParser) auto othersScreen = mpc.screens->get(); - othersScreen->setTapAveraging(misc->getTapAvg() + 2); + othersScreen->setTapAveraging(static_cast(misc->tap_averaging()) + 2); auto syncScreen = mpc.screens->get(); - syncScreen->receiveMMCEnabled = misc->isInReceiveMMCEnabled(); - syncScreen->sendMMCEnabled = midiSyncMisc->isSendMMCEnabled(); - syncScreen->in = midiSyncMisc->getInput(); - syncScreen->out = midiSyncMisc->getOutput(); + syncScreen->receiveMMCEnabled = misc->midi_sync_in_receive_mmc_enabled(); + syncScreen->sendMMCEnabled = midiSync->send_mmc_enabled(); + syncScreen->in = midiSync->input(); + syncScreen->out = static_cast(midiSync->output()); if (!mpc.isPluginModeEnabled()) { - syncScreen->setModeIn(midiSyncMisc->getInMode()); - syncScreen->setModeOut(midiSyncMisc->getOutMode()); + syncScreen->setModeIn(static_cast(midiSync->in_mode())); + syncScreen->setModeOut(static_cast(midiSync->out_mode())); } - syncScreen->shiftEarly = midiSyncMisc->getShiftEarly(); - syncScreen->frameRate = midiSyncMisc->getFrameRate(); + syncScreen->shiftEarly = midiSync->shift_early(); + syncScreen->frameRate = static_cast(midiSync->frame_rate()); mpcSequencer->setSecondSequenceEnabled( - allParserSequencer->secondSeqEnabled); + parsedSequencer->second_sequence_enabled()); auto secondSequenceScreen = mpc.screens->get(); - secondSequenceScreen->sq = allParserSequencer->secondSeqIndex; + secondSequenceScreen->sq = parsedSequencer->sequence_sequence_index(); auto songScreen = mpc.screens->get(); - songScreen->setDefaultSongName(midiSyncMisc->getDefSongName()); + songScreen->setDefaultSongName(songGlobal->default_song_name()); auto ignoreTempoChangeScreen = mpc.screens->get(); ignoreTempoChangeScreen->setIgnore( - midiSyncMisc->getSongModeIgnoreTempoChangeEvents()); + songGlobal->ignore_tempo_change_events_in_sequence()); - auto songs = allParser.getSongs(); + const auto* songs = parsed.songs(); for (int i = 0; i < 20; i++) { - auto allSong = songs[i]; + const auto& parsedSong = songs->at(i); auto mpcSong = mpcSequencer->getSong(i); mpcSong->setUsed(false); - if (allSong->getIsUsed()) + const auto trimmedName = mpc::StrUtil::trim(bytesUntilNull(parsedSong->name())); + const bool isUsed = parsedSong->is_used() || trimmedName != "(Unused)"; + if (isUsed) { mpcSong->setUsed(true); - mpcSong->setName(StrUtil::trim(allSong->name)); - - auto steps = allSong->getSteps(); + mpcSong->setName(trimmedName); - for (int stepIndex = 0; stepIndex < steps.size(); ++stepIndex) + int stepIndex = 0; + for (const auto& step : *parsedSong->steps()) { - const auto idx = SongStepIndex(stepIndex); + if (step->sequence_index() == 0xFF || step->repeat_count() == 0xFF) + { + continue; + } + + const auto idx = SongStepIndex(stepIndex++); mpcSong->insertStep(idx); mpcSong->setStepSequenceIndex( - idx, SequenceIndex(steps[stepIndex].first)); - mpcSong->setStepRepetitionCount(idx, steps[stepIndex].second); + idx, SequenceIndex(step->sequence_index())); + mpcSong->setStepRepetitionCount(idx, step->repeat_count()); } mpcSong->setFirstLoopStepIndex( - SongStepIndex(allSong->getLoopFirstStepIndex())); + SongStepIndex(parsedSong->loop_first_step())); mpcSong->setLastLoopStepIndex( - SongStepIndex(allSong->getLoopLastStepIndex())); + SongStepIndex(parsedSong->loop_last_step())); - mpcSong->setLoopEnabled(allSong->isLoopEnabled()); + mpcSong->setLoopEnabled(parsedSong->is_loop_enabled()); } } @@ -302,18 +956,73 @@ void AllLoader::loadEverythingFromAllParser(Mpc &mpc, AllParser &allParser) std::vector AllLoader::loadSequenceMetaInfosFromFile(Mpc &mpc, MpcFile *f) { - std::vector result; - const AllParser allParser(mpc, f->getBytes()); + const auto bytes = f->getBytes(); + switch (detectAllVariant(bytes)) + { + case all_variant_t::mpc2000xl: + { + auto result = file::kaitai::AllIo::loadSequenceMetaInfos(mpc, f); + if (!result) + { + throw std::runtime_error(result.error()); + } + return *result; + } + case all_variant_t::mpc60_v2: + return loadMpc60SequenceMetaInfos(bytes); + case all_variant_t::mpc3000_v3: + return loadMpc3000SequenceMetaInfos(bytes); + default: + throw std::runtime_error("Unsupported ALL file format"); + } +} - const auto allSeqNames = allParser.getSeqNames()->getNames(); - const auto allSeqUsednesses = allParser.getSeqNames()->getUsednesses(); +std::shared_ptr +AllLoader::loadOneSequenceFromCanonicalBytes( + Mpc &mpc, + const std::vector &canonicalBytes, + const SequenceIndex sourceIndexInAllFile, + const SequenceIndex destIndexInMpcMemory) +{ + std::stringstream parseStream( + std::string(canonicalBytes.begin(), canonicalBytes.end()), + std::ios::in | std::ios::out | std::ios::binary + ); + kaitai::kstream parseIo(&parseStream); + mpc2000xl_all_t parsed(&parseIo); + parsed._read(); + + size_t sourceOrdinal = parsed.sequences()->size(); + const auto sourceSlot = static_cast(sourceIndexInAllFile); + for (size_t i = 0; i < parsed.sequences()->size(); ++i) + { + auto* body = parsed.sequences()->at(i)->body(); + if (body == nullptr) + { + continue; + } - for (int i = 0; i < Mpc2000XlSpecs::SEQUENCE_COUNT; ++i) + if (static_cast(body->index()) - 1 == sourceSlot) + { + sourceOrdinal = i; + break; + } + } + + const auto sequence = mpc.getSequencer()->getSequence(destIndexInMpcMemory); + if (sourceOrdinal == parsed.sequences()->size()) { - result.push_back({allSeqUsednesses[i], allSeqNames[i]}); + return sequence; } - return result; + applyParsedSequenceToInMemorySequence( + parsed, + sourceOrdinal, + sequence, + mpc.getSequencer()->getStateManager().get() + ); + + return sequence; } std::shared_ptr @@ -321,18 +1030,19 @@ AllLoader::loadOneSequenceFromFile(Mpc &mpc, MpcFile *f, const SequenceIndex sourceIndexInAllFile, const SequenceIndex destIndexInMpcMemory) { - AllParser allParser(mpc, f->getBytes()); - - const auto allSequences = allParser.getAllSequences(); - - const auto allSeqNames = allParser.getSeqNames()->getNames(); - - const auto sequencer = mpc.getSequencer(); - - const auto sequence = sequencer->getSequence(destIndexInMpcMemory); - - allSequences[sourceIndexInAllFile]->applyToInMemorySequence( - sequence, sequencer->getStateManager().get()); - - return sequence; + const auto bytes = f->getBytes(); + switch (detectAllVariant(bytes)) + { + case all_variant_t::mpc2000xl: + return file::kaitai::AllIo::loadOneSequence( + mpc, f, sourceIndexInAllFile, destIndexInMpcMemory); + case all_variant_t::mpc60_v2: + return loadOneSequenceFromMpc60AllBytes( + mpc, bytes, sourceIndexInAllFile, destIndexInMpcMemory); + case all_variant_t::mpc3000_v3: + return loadOneSequenceFromMpc3000AllBytes( + mpc, bytes, sourceIndexInAllFile, destIndexInMpcMemory); + default: + throw std::runtime_error("Unsupported ALL file format"); + } } diff --git a/src/main/disk/AllLoader.hpp b/src/main/disk/AllLoader.hpp index 816cd36e7..95197b0e4 100644 --- a/src/main/disk/AllLoader.hpp +++ b/src/main/disk/AllLoader.hpp @@ -19,8 +19,6 @@ namespace mpc::disk namespace mpc::file::all { - class AllParser; - class AllSequence; } // namespace mpc::file::all namespace mpc::sequencer @@ -34,7 +32,12 @@ namespace mpc::disk { public: static void loadEverythingFromFile(Mpc &, MpcFile *); - static void loadEverythingFromAllParser(Mpc &, file::all::AllParser &); + static void loadEverythingFromBytes(Mpc &, const std::vector &); + static void loadEverythingFromCanonicalBytes(Mpc &, const std::vector &); + static std::shared_ptr + loadOneSequenceFromCanonicalBytes(Mpc &, const std::vector &, + SequenceIndex sourceIndexInAllFile, + SequenceIndex destIndexInMpcMemory); static std::vector loadSequenceMetaInfosFromFile(Mpc &, MpcFile *f); static std::shared_ptr diff --git a/src/main/disk/ApsLoader.cpp b/src/main/disk/ApsLoader.cpp index f10e46b44..0a742fd98 100644 --- a/src/main/disk/ApsLoader.cpp +++ b/src/main/disk/ApsLoader.cpp @@ -1,375 +1,12 @@ #include "ApsLoader.hpp" -#include "lcdgui/screens/PgmAssignScreen.hpp" -#include "sampler/Pad.hpp" #include "Mpc.hpp" -#include "disk/StdDisk.hpp" #include "disk/MpcFile.hpp" -#include "disk/SoundLoader.hpp" -#include "file/aps/ApsNoteParameters.hpp" -#include "file/aps/ApsParser.hpp" -#include "file/aps/ApsSlider.hpp" +#include "file/kaitai/ApsIo.hpp" -#include "lcdgui/screens/DrumScreen.hpp" -#include "lcdgui/screens/MixerSetupScreen.hpp" -#include "lcdgui/screens/window/CantFindFileScreen.hpp" - -#include "StrUtil.hpp" - -#include - -#include "Logger.hpp" -#include "engine/EngineHost.hpp" -#include "engine/IndivFxMixer.hpp" -#include "engine/StereoMixer.hpp" -#include "performance/PerformanceManager.hpp" -#include "sampler/Sampler.hpp" -#include "sequencer/Sequencer.hpp" - -using namespace mpc::lcdgui; -using namespace mpc::lcdgui::screens; -using namespace mpc::lcdgui::screens::window; -using namespace mpc::lcdgui::screens::dialog2; -using namespace mpc::disk; -using namespace mpc::sampler; -using namespace mpc::file::aps; - -void ApsLoader::load(Mpc &mpc, const std::shared_ptr &file, - const bool headless) -{ - if (!file->exists()) - { - throw std::invalid_argument("File does not exist"); - } - - const auto seqScreen = mpc.screens->get(); - - const auto cantFindFileScreen = - mpc.screens->get(); - cantFindFileScreen->skipAll = false; - - ApsParser apsParser(file->getBytes()); - - if (!apsParser.isHeaderValid()) - { - MLOG( - "The APS file you're trying to load does not have a valid ID. The " - "first 2 bytes of an MPC2000XL APS file should be 0A 05. MPC2000 " - "APS files start with 0A 04 and are not supported (yet?)."); - - throw std::runtime_error("Invalid APS header"); - } - - constexpr auto withoutSounds = false; - loadFromParsedAps(apsParser, mpc, withoutSounds, headless); - - mpc.getSampler()->setSoundIndex(0); -} - -void ApsLoader::loadFromParsedAps(ApsParser &apsParser, Mpc &mpc, - bool withoutSounds, bool headless) -{ - auto sampler = mpc.getSampler(); - auto disk = mpc.getDisk(); - - // These remain empty when loading the APS without sounds. - // It is trusted that the caller of this method validates - // sound indices to make sure they actually exist. This - // allows loading APS and sounds in any order. - std::vector unavailableSoundIndices; - std::map finalSoundIndices; - - if (!withoutSounds) - { - int skipCount = 0; - sampler->deleteAllSamples(); - - for (int i = 0; i < apsParser.getSoundNames().size(); i++) - { - auto ext = "snd"; - std::shared_ptr soundFile; - std::string soundFileName = - StrUtil::replaceAll(apsParser.getSoundNames()[i], ' ', ""); - - for (auto &f : disk->getAllFiles()) - { - if (StrUtil::eqIgnoreCase( - StrUtil::replaceAll(f->getName(), ' ', ""), - soundFileName + ".SND")) - { - soundFile = f; - break; - } - } - - if (!soundFile || !soundFile->exists()) - { - for (auto &f : disk->getAllFiles()) - { - if (StrUtil::eqIgnoreCase( - StrUtil::replaceAll(f->getName(), ' ', ""), - soundFileName + ".WAV")) - { - soundFile = f; - ext = "wav"; - break; - } - } - } - - if (!soundFile || !soundFile->exists()) - { - unavailableSoundIndices.push_back(i); - - skipCount++; - - if (!headless) - { - handleSoundNotFound(mpc, soundFileName); - } - - continue; - } - - finalSoundIndices[i] = i - skipCount; - - loadSound(mpc, soundFileName, ext, soundFile, headless); - } - } - - for (auto &apsProgram : apsParser.getPrograms()) - { - sampler->getProgram(apsProgram->index)->setName(apsProgram->getName()); - - performance::UpdateProgramBulk msg; - auto &perfProgram = msg.program; - msg.programIndex = ProgramIndex(apsProgram->index); - perfProgram.used = true; - - auto assignTable = apsProgram->getAssignTable()->get(); - - for (int noteIndex = 0; noteIndex < 64; noteIndex++) - { - perfProgram.pads[noteIndex].note = - DrumNoteNumber(assignTable[noteIndex]); - - auto sourceStereoMixerChannel = - apsProgram->getStereoMixerChannel(noteIndex); - auto sourceIndivFxMixerChannel = - apsProgram->getIndivFxMixerChannel(noteIndex); - - auto &destNoteParams = perfProgram.noteParameters[noteIndex]; - destNoteParams.stereoMixer = sourceStereoMixerChannel; - destNoteParams.indivFxMixer = sourceIndivFxMixerChannel; - - auto srcNoteParams = apsProgram->getNoteParameters(noteIndex); - - auto soundIndex = srcNoteParams->getSoundIndex(); - - if (find(begin(unavailableSoundIndices), - end(unavailableSoundIndices), - soundIndex) != end(unavailableSoundIndices)) - { - soundIndex = -1; - } - - if (soundIndex != -1 && - finalSoundIndices.find(soundIndex) != end(finalSoundIndices)) - { - soundIndex = finalSoundIndices[soundIndex]; - } - - destNoteParams.soundIndex = soundIndex; - destNoteParams.tune = srcNoteParams->getTune(); - destNoteParams.voiceOverlapMode = - srcNoteParams->getVoiceOverlapMode(); - - destNoteParams.decayMode = srcNoteParams->getDecayMode(); - destNoteParams.attack = srcNoteParams->getAttack(); - destNoteParams.decay = srcNoteParams->getDecay(); - - destNoteParams.filterAttack = - srcNoteParams->getVelocityToFilterAttack(); - destNoteParams.filterDecay = - srcNoteParams->getVelocityToFilterDecay(); - destNoteParams.filterEnvelopeAmount = - srcNoteParams->getVelocityToFilterAmount(); - - destNoteParams.filterFrequency = - srcNoteParams->getCutoffFrequency(); - destNoteParams.filterResonance = srcNoteParams->getResonance(); - - destNoteParams.muteAssignA = - DrumNoteNumber(srcNoteParams->getMute1()); - destNoteParams.muteAssignB = - DrumNoteNumber(srcNoteParams->getMute2()); - - destNoteParams.optionalNoteA = - DrumNoteNumber(srcNoteParams->getAlsoPlay1()); - destNoteParams.optionalNoteB = - DrumNoteNumber(srcNoteParams->getAlsoPlay2()); - - destNoteParams.sliderParameterNumber = - srcNoteParams->getSliderParameter(); - destNoteParams.soundGenerationMode = - srcNoteParams->getSoundGenerationMode(); - - destNoteParams.velocityToStart = - srcNoteParams->getVelocityToStart(); - destNoteParams.velocityToAttack = - srcNoteParams->getVelocityToAttack(); - destNoteParams.velocityToFilterFrequency = - srcNoteParams->getVelocityToFilterFrequency(); - - destNoteParams.velocityToLevel = - srcNoteParams->getVelocityToLevel(); - - destNoteParams.velocityRangeLower = - srcNoteParams->getVelocityRangeLower(); - destNoteParams.velocityRangeUpper = - srcNoteParams->getVelocityRangeUpper(); - - destNoteParams.velocityToPitch = - srcNoteParams->getVelocityToPitch(); - } - - perfProgram.slider.attackHighRange = - apsProgram->getSlider()->getAttackHigh(); - perfProgram.slider.attackLowRange = - apsProgram->getSlider()->getAttackLow(); - perfProgram.slider.controlChange = - apsProgram->getSlider()->getProgramChange(); - perfProgram.slider.decayHighRange = - apsProgram->getSlider()->getDecayHigh(); - perfProgram.slider.decayLowRange = - apsProgram->getSlider()->getDecayLow(); - perfProgram.slider.filterHighRange = - apsProgram->getSlider()->getFilterHigh(); - perfProgram.slider.filterLowRange = - apsProgram->getSlider()->getFilterLow(); - perfProgram.slider.assignNote = - DrumNoteNumber(apsProgram->getSlider()->getNote()); - perfProgram.slider.tuneHighRange = - apsProgram->getSlider()->getTuneHigh(); - perfProgram.slider.tuneLowRange = apsProgram->getSlider()->getTuneLow(); - - mpc.getPerformanceManager().lock()->enqueue( - performance::PerformanceMessage{std::move(msg)}); - } - - for (int i = 0; i < Mpc2000XlSpecs::DRUM_BUS_COUNT; i++) - { - auto m = apsParser.getDrumMixers()[i]; - - performance::Drum perfDrum; - perfDrum.drumBusIndex = DrumBusIndex(i); - - for (int noteIndex = 0; noteIndex < 64; noteIndex++) - { - perfDrum.stereoMixers[noteIndex] = - m->getStereoMixerChannel(noteIndex); - perfDrum.indivFxMixers[noteIndex] = - m->getIndivFxMixerChannel(noteIndex); - } - - auto pgm = apsParser.getDrumConfiguration(i)->getProgramIndex(); - perfDrum.programIndex = ProgramIndex(pgm); - perfDrum.receivePgmChangeEnabled = - apsParser.getDrumConfiguration(i)->isReceivePgmChangeEnabled(); - perfDrum.receiveMidiVolumeEnabled = - apsParser.getDrumConfiguration(i)->isReceiveMidiVolumeEnabled(); - - mpc.getPerformanceManager().lock()->enqueue( - performance::UpdateDrumBulk{perfDrum}); - } - - auto mixerSetupScreen = mpc.screens->get(); - - auto globals = apsParser.getGlobalParameters(); - - mixerSetupScreen->setRecordMixChangesEnabled( - globals->isRecordMixChangesEnabled()); - mixerSetupScreen->setCopyPgmMixToDrumEnabled( - globals->isCopyPgmMixToDrumEnabled()); - mixerSetupScreen->setFxDrum(globals->getFxDrum()); - mixerSetupScreen->setIndivFxSourceDrum(globals->isIndivFxSourceDrum()); - mixerSetupScreen->setStereoMixSourceDrum(globals->isStereoMixSourceDrum()); - - auto drumScreen = mpc.screens->get(); - drumScreen->setPadToIntSound(globals->isPadToIntSoundEnabled()); - mixerSetupScreen->setMasterLevel(globals->getMasterLevel()); - - auto pgmAssignScreen = mpc.screens->get(); - - pgmAssignScreen->setPadAssign(globals->isPadAssignMaster()); -} - -void ApsLoader::loadSound(Mpc &mpc, const std::string &soundFileName, - const std::string &ext, - const std::shared_ptr &soundFile, - const bool headless) +void mpc::disk::ApsLoader::load(mpc::Mpc &mpc, + const std::shared_ptr &file, + const bool headless) { - constexpr auto replace = false; - SoundLoader soundLoader(mpc, replace); - - if (!headless) - { - showPopup(mpc, soundFileName, ext); - } - - SoundLoaderResult result; - constexpr bool shouldBeConverted = false; - const auto sound = mpc.getSampler()->addSound(); - - if (!sound) - { - return; - } - - soundLoader.loadSound(soundFile, result, sound, shouldBeConverted); - - if (!result.success) - { - mpc.getSampler()->deleteSound(sound); - } -} - -void ApsLoader::showPopup(Mpc &mpc, const std::string &name, - const std::string &ext) -{ - const std::string msg = - "Loading " + - StrUtil::toUpper(StrUtil::padRight(name, " ", 16) + "." + ext); - - const auto ls = mpc.getLayeredScreen(); - - ls->postToUiThread(utils::Task( - [ls, msg] - { - ls->showPopup(msg); - })); - std::this_thread::sleep_for(std::chrono::milliseconds(50)); -} - -void ApsLoader::handleSoundNotFound(Mpc &mpc, const std::string &soundFileName) -{ - const auto cantFindFileScreen = - mpc.screens->get(); - - if (const auto skipAll = cantFindFileScreen->skipAll; !skipAll) - { - const auto ls = mpc.getLayeredScreen(); - ls->postToUiThread(utils::Task( - [ls, cantFindFileScreen, soundFileName] - { - cantFindFileScreen->waitingForUser = true; - cantFindFileScreen->fileName = soundFileName; - ls->openScreenById(ScreenId::CantFindFileScreen); - })); - - while (cantFindFileScreen->waitingForUser) - { - std::this_thread::sleep_for(std::chrono::milliseconds(50)); - } - } + mpc::file::kaitai::ApsIo::load(mpc, file, headless); } diff --git a/src/main/disk/ApsLoader.hpp b/src/main/disk/ApsLoader.hpp index 772908c74..24ecc2228 100644 --- a/src/main/disk/ApsLoader.hpp +++ b/src/main/disk/ApsLoader.hpp @@ -15,11 +15,6 @@ namespace mpc::disk { class MpcFile; } -namespace mpc::file::aps -{ - class ApsParser; -} - namespace mpc::disk { class ApsLoader @@ -27,17 +22,5 @@ namespace mpc::disk public: static void load(Mpc &, const std::shared_ptr &, bool headless); - static void loadFromParsedAps(file::aps::ApsParser &, Mpc &, - bool withoutSounds, bool headless); - - private: - static void handleSoundNotFound(Mpc &, - const std::string &soundFileName); - static void loadSound(Mpc &, const std::string &soundFileName, - const std::string &ext, - const std::shared_ptr &soundFile, - bool headless = false); - static void showPopup(Mpc &, const std::string &name, - const std::string &ext); }; } // namespace mpc::disk diff --git a/src/main/disk/PgmFileToProgramConverter.cpp b/src/main/disk/PgmFileToProgramConverter.cpp deleted file mode 100644 index d73905fbb..000000000 --- a/src/main/disk/PgmFileToProgramConverter.cpp +++ /dev/null @@ -1,189 +0,0 @@ -#include "disk/PgmFileToProgramConverter.hpp" - -#include "Mpc.hpp" -#include "file/pgmreader/PRMixer.hpp" -#include "file/pgmreader/PRPads.hpp" -#include "file/pgmreader/PgmAllNoteParameters.hpp" -#include "file/pgmreader/PgmHeader.hpp" -#include "file/pgmreader/ProgramFileReader.hpp" -#include "file/pgmreader/ProgramName.hpp" -#include "file/pgmreader/PRSlider.hpp" -#include "file/pgmreader/SoundNames.hpp" -#include "sampler/Pad.hpp" -#include "sampler/Program.hpp" -#include "sampler/PgmSlider.hpp" -#include "sampler/NoteParameters.hpp" - -#include "disk/MpcFile.hpp" - -#include "StrUtil.hpp" - -#include -#include - -#include "engine/IndivFxMixer.hpp" -#include "engine/StereoMixer.hpp" -#include "performance/PerformanceManager.hpp" - -using namespace mpc::disk; -using namespace mpc::sampler; -using namespace mpc::file::pgmreader; - -void PgmFileToProgramConverter::setSlider( - const ProgramFileReader &reader, const std::shared_ptr &program) -{ - const auto slider = reader.getSlider(); - const auto nn = slider->getMidiNoteAssign() == 0 - ? NoDrumNoteAssigned - : DrumNoteNumber(slider->getMidiNoteAssign()); - const auto pgmSlider = program->getSlider(); - pgmSlider->setAssignNote(nn); - pgmSlider->setAttackHighRange(slider->getAttackHigh()); - pgmSlider->setAttackLowRange(slider->getAttackLow()); - pgmSlider->setControlChange(slider->getControlChange()); - pgmSlider->setDecayHighRange(slider->getDecayHigh()); - pgmSlider->setDecayLowRange(slider->getDecayLow()); - pgmSlider->setFilterHighRange(slider->getFilterHigh()); - pgmSlider->setFilterLowRange(slider->getFilterLow()); - pgmSlider->setTuneHighRange(slider->getTuneHigh()); - pgmSlider->setTuneLowRange(slider->getTuneLow()); -} - -void PgmFileToProgramConverter::setNoteParameters( - mpc::Mpc &mpc, const ProgramFileReader &reader, - const std::shared_ptr &program) -{ - const auto pgmNoteParameters = reader.getAllNoteParameters(); - const auto pgmPads = reader.getPads(); - - std::array - allPerfNoteParams; - - for (int programPadIndex = 0; - programPadIndex < Mpc2000XlSpecs::PROGRAM_PAD_COUNT; ++programPadIndex) - { - const auto padNote = pgmPads->getNote(programPadIndex); - const auto note = padNote == -1 ? NoDrumNoteAssigned : padNote; - program->getPad(programPadIndex)->setNote(DrumNoteNumber(note)); - - auto &perfNoteParams = allPerfNoteParams[programPadIndex]; - - perfNoteParams.attack = pgmNoteParameters->getAttack(programPadIndex); - perfNoteParams.decay = pgmNoteParameters->getDecay(programPadIndex); - perfNoteParams.decayMode = - pgmNoteParameters->getDecayMode(programPadIndex); - perfNoteParams.filterAttack = - pgmNoteParameters->getVelEnvToFiltAtt(programPadIndex); - perfNoteParams.filterDecay = - pgmNoteParameters->getVelEnvToFiltDec(programPadIndex); - perfNoteParams.filterEnvelopeAmount = - pgmNoteParameters->getVelEnvToFiltAmt(programPadIndex); - perfNoteParams.filterFrequency = - pgmNoteParameters->getCutoff(programPadIndex); - perfNoteParams.filterResonance = - pgmNoteParameters->getResonance(programPadIndex); - perfNoteParams.muteAssignA = - DrumNoteNumber(pgmNoteParameters->getMuteAssign1(programPadIndex)); - perfNoteParams.muteAssignB = - DrumNoteNumber(pgmNoteParameters->getMuteAssign2(programPadIndex)); - perfNoteParams.optionalNoteA = - DrumNoteNumber(pgmNoteParameters->getAlsoPlayUse1(programPadIndex)); - perfNoteParams.optionalNoteB = - DrumNoteNumber(pgmNoteParameters->getAlsoPlayUse2(programPadIndex)); - - const auto sampleSelect = - pgmNoteParameters->getSampleSelect(programPadIndex); - - perfNoteParams.soundIndex = sampleSelect == 255 ? -1 : sampleSelect; - - perfNoteParams.sliderParameterNumber = - pgmNoteParameters->getSliderParameter(programPadIndex); - perfNoteParams.soundGenerationMode = - pgmNoteParameters->getSoundGenerationMode(programPadIndex); - perfNoteParams.tune = pgmNoteParameters->getTune(programPadIndex); - perfNoteParams.velocityRangeLower = - pgmNoteParameters->getVelocityRangeLower(programPadIndex); - perfNoteParams.velocityRangeUpper = - pgmNoteParameters->getVelocityRangeUpper(programPadIndex); - perfNoteParams.velocityToAttack = - pgmNoteParameters->getVelocityToAttack(programPadIndex); - perfNoteParams.velocityToFilterFrequency = - pgmNoteParameters->getVelocityToCutoff(programPadIndex); - perfNoteParams.velocityToLevel = - pgmNoteParameters->getVelocityToLevel(programPadIndex); - perfNoteParams.velocityToPitch = - pgmNoteParameters->getVelocityToPitch(programPadIndex); - perfNoteParams.velocityToStart = - pgmNoteParameters->getVelocityToStart(programPadIndex); - perfNoteParams.voiceOverlapMode = - pgmNoteParameters->getVoiceOverlapMode(programPadIndex); - } - - performance::UpdateAllNoteParametersBulk msg{program->getProgramIndex(), - allPerfNoteParams}; - mpc.getPerformanceManager().lock()->enqueue(msg); -} - -void PgmFileToProgramConverter::setMixer( - const ProgramFileReader &reader, const std::shared_ptr &program) -{ - const auto pgmMixer = reader.getMixer(); - - for (int i = 0; i < 64; i++) - { - const auto noteParameters = program->getNoteParameters(i + 35); - const auto stereoMixer = noteParameters->getStereoMixer(); - const auto indivFxMixer = noteParameters->getIndivFxMixer(); - - stereoMixer->setLevel(DrumMixerLevel(pgmMixer->getVolume(i))); - stereoMixer->setPanning(DrumMixerPanning(pgmMixer->getPan(i))); - indivFxMixer->setVolumeIndividualOut( - DrumMixerLevel(pgmMixer->getVolumeIndividual(i))); - indivFxMixer->setOutput( - DrumMixerIndividualOutput(pgmMixer->getOutput(i))); - indivFxMixer->setFxPath( - DrumMixerIndividualFxPath(pgmMixer->getEffectsOutput(i))); - } -} - -program_or_error PgmFileToProgramConverter::loadFromFileAndConvert( - mpc::Mpc &mpc, const std::shared_ptr &f, - std::shared_ptr program, std::vector &soundNames) -{ - if (!f->exists()) - { - throw std::invalid_argument("File does not exist"); - } - - const ProgramFileReader reader(f); - - if (!reader.getHeader()->verifyMagic()) - { - throw std::invalid_argument("PGM first 2 bytes are incorrect"); - } - - const auto pgmSoundNames = reader.getSampleNames(); - - for (int i = 0; i < reader.getHeader()->getSoundCount(); i++) - { - soundNames.push_back(pgmSoundNames->getSampleName(i)); - } - - const std::string programNameInData = - StrUtil::trim(reader.getProgramName()->getProgramNameASCII()); - - if (StrUtil::eqIgnoreCase(programNameInData, f->getNameWithoutExtension())) - { - program->setName(programNameInData); - } - else - { - program->setName(f->getNameWithoutExtension()); - } - - setNoteParameters(mpc, reader, program); - setMixer(reader, program); - setSlider(reader, program); - - return program; -} diff --git a/src/main/disk/PgmFileToProgramConverter.hpp b/src/main/disk/PgmFileToProgramConverter.hpp deleted file mode 100644 index 539886484..000000000 --- a/src/main/disk/PgmFileToProgramConverter.hpp +++ /dev/null @@ -1,49 +0,0 @@ -#pragma once - -#include -#include -#include -#include "mpc_types.hpp" - -namespace mpc -{ - class Mpc; -} - -namespace mpc::sampler -{ - class Sampler; - class Program; -} // namespace mpc::sampler - -namespace mpc::file::pgmreader -{ - class ProgramFileReader; -} - -namespace mpc::disk -{ - class MpcFile; -} - -namespace mpc::disk -{ - class PgmFileToProgramConverter - { - public: - static program_or_error - loadFromFileAndConvert(mpc::Mpc &, const std::shared_ptr &, - std::shared_ptr, - std::vector &soundNames); - - private: - static void setSlider(const file::pgmreader::ProgramFileReader &, - const std::shared_ptr &); - static void - setNoteParameters(mpc::Mpc &, - const file::pgmreader::ProgramFileReader &, - const std::shared_ptr &); - static void setMixer(const file::pgmreader::ProgramFileReader &, - const std::shared_ptr &); - }; -} // namespace mpc::disk diff --git a/src/main/disk/ProgramLoader.cpp b/src/main/disk/ProgramLoader.cpp index aae3719ee..4d1bb99da 100644 --- a/src/main/disk/ProgramLoader.cpp +++ b/src/main/disk/ProgramLoader.cpp @@ -4,7 +4,7 @@ #include "disk/AbstractDisk.hpp" #include "disk/MpcFile.hpp" -#include "disk/PgmFileToProgramConverter.hpp" +#include "file/kaitai/PgmIo.hpp" #include "disk/SoundLoader.hpp" #include "sampler/Program.hpp" @@ -81,8 +81,7 @@ ProgramLoader::loadProgram(Mpc &mpc, const std::shared_ptr &file, std::vector pgmSoundNames; - return PgmFileToProgramConverter::loadFromFileAndConvert(mpc, file, program, - pgmSoundNames) + return file::kaitai::PgmIo::loadProgram(mpc, file, program, pgmSoundNames) .map( [pgmSoundNames, &mpc, programIndex](std::shared_ptr p) { diff --git a/src/main/disk/SoundLoader.cpp b/src/main/disk/SoundLoader.cpp index 4538d5db8..e0cd7f73d 100644 --- a/src/main/disk/SoundLoader.cpp +++ b/src/main/disk/SoundLoader.cpp @@ -4,7 +4,7 @@ #include "disk/MpcFile.hpp" #include "disk/AbstractDisk.hpp" #include "file/wav/WavFile.hpp" -#include "file/sndreader/SndReader.hpp" +#include "file/kaitai/SndIo.hpp" #include "sampler/Sampler.hpp" #include "sampler/Sound.hpp" #include "lcdgui/screens/VmpcSettingsScreen.hpp" @@ -14,7 +14,6 @@ using namespace mpc::sampler; using namespace mpc::disk; using namespace mpc::file::wav; -using namespace mpc::file::sndreader; using namespace mpc::lcdgui; using namespace mpc::lcdgui::screens; @@ -83,42 +82,8 @@ void SoundLoader::loadSound(std::shared_ptr f, SoundLoaderResult &r, } else if (StrUtil::eqIgnoreCase(extension, ".snd")) { - auto onSndReaderSuccess = - [sampler, sound, nameWithoutExtension, &existingSoundIndex]( - const std::shared_ptr &sndReader) -> sound_or_error - { - if (!sndReader->isHeaderValid()) - { - return tl::make_unexpected( - mpc_io_error_msg{"Invalid SND header"}); - } - - if (StrUtil::eqIgnoreCase(sndReader->getName(), - nameWithoutExtension)) - { - existingSoundIndex = sampler->checkExists(sndReader->getName()); - sound->setName(sndReader->getName()); - } - else - { - existingSoundIndex = sampler->checkExists(nameWithoutExtension); - sound->setName(nameWithoutExtension); - } - - sndReader->readData(sound->getMutableSampleData()); - sound->setMono(sndReader->isMono()); - sound->setStart(sndReader->getStart()); - sound->setEnd(sndReader->getEnd()); - sound->setLoopTo(sndReader->getEnd() - sndReader->getLoopLength()); - sound->setSampleRate(sndReader->getSampleRate()); - sound->setLoopEnabled(sndReader->isLoopEnabled()); - sound->setLevel(sndReader->getLevel()); - sound->setTune(sndReader->getTune()); - sound->setBeatCount(sndReader->getNumberOfBeats()); - return sound; - }; - - soundOrError = mpc.getDisk()->readSnd2(f, onSndReaderSuccess); + existingSoundIndex = sampler->checkExists(nameWithoutExtension); + soundOrError = file::kaitai::SndIo::loadSound(f, sound, nameWithoutExtension); } if (!soundOrError.has_value()) diff --git a/src/main/engine/DrumNoteEventHandler.cpp b/src/main/engine/DrumNoteEventHandler.cpp index 3f0b4c3b6..4ac81072d 100644 --- a/src/main/engine/DrumNoteEventHandler.cpp +++ b/src/main/engine/DrumNoteEventHandler.cpp @@ -14,6 +14,7 @@ #include "MpcSpecs.hpp" #include "sequencer/Bus.hpp" +#include "sampler/SoundGenerationMode.hpp" #include @@ -31,6 +32,8 @@ static constexpr float AUX_PAN_RIGHT = 1.0f; namespace { + using mpc::sampler::SoundGenerationMode; + void handleMuteGroups(const performance::NoteParameters ¬eParameters, const int drumIndex, const std::vector> *voices) @@ -52,6 +55,46 @@ namespace } } } + + struct SwitchedNoteResolution + { + DrumNoteNumber note{NoDrumNoteAssigned}; + bool useOptionalB = false; + }; + + std::optional resolveSwitchedNote( + const performance::NoteParameters ¬eParameters, + const DrumNoteOnContext &context) + { + const auto mode = mpc::sampler::soundGenerationModeFromRaw( + noteParameters.soundGenerationMode); + + if (mode != SoundGenerationMode::VelocitySwitch && + mode != SoundGenerationMode::DecaySwitch) + { + return std::nullopt; + } + + const int switchValue = mode == SoundGenerationMode::VelocitySwitch + ? context.velocity + : (context.varType == NoteVariationTypeDecay.get() + ? context.varValue + : noteParameters.decay); + + if (noteParameters.optionalNoteB != NoDrumNoteAssigned && + switchValue > noteParameters.velocityRangeUpper) + { + return SwitchedNoteResolution{noteParameters.optionalNoteB, true}; + } + + if (noteParameters.optionalNoteA != NoDrumNoteAssigned && + switchValue > noteParameters.velocityRangeLower) + { + return SwitchedNoteResolution{noteParameters.optionalNoteA, false}; + } + + return std::nullopt; + } } // namespace void DrumNoteEventHandler::noteOn(const DrumNoteOnContext &c) @@ -65,6 +108,32 @@ void DrumNoteEventHandler::noteOn(const DrumNoteOnContext &c) const auto program = c.drumBus->getPerformanceProgram(); const auto np = program.getNoteParameters(c.note); + if (c.firstGeneration) + { + if (const auto switched = resolveSwitchedNote(np, c); + switched && switched->note != c.note) + { + auto switchedCtx = DrumNoteEventContextBuilder::buildDrumNoteOnContext( + c.noteEventId, c.drum, c.drumBus, c.sampler, c.mixer, + c.mixerSetupScreen, c.voices, switched->note, c.velocity, + c.varType, c.varValue, c.frameOffset, false, c.startTick, + c.durationFrames); + + noteOn(switchedCtx); + + if (switched->useOptionalB) + { + c.setOptB(c.note, switched->note); + } + else + { + c.setOptA(c.note, switched->note); + } + + return; + } + } + handleMuteGroups(np, c.drum.drumBusIndex, c.voices); auto soundNumber = np.soundIndex; @@ -162,7 +231,9 @@ void DrumNoteEventHandler::noteOn(const DrumNoteOnContext &c) c.mixer->getSharedBuffer()->getSampleRate(), c.noteEventId, c.drum.programIndex); - if (c.firstGeneration && np.soundGenerationMode == 1) + if (c.firstGeneration && + mpc::sampler::soundGenerationModeFromRaw(np.soundGenerationMode) == + SoundGenerationMode::Simult) { int optA = np.optionalNoteA; int optB = np.optionalNoteB; diff --git a/src/main/file/all/AllChannelPressureEvent.cpp b/src/main/file/all/AllChannelPressureEvent.cpp deleted file mode 100644 index 3ecffe20c..000000000 --- a/src/main/file/all/AllChannelPressureEvent.cpp +++ /dev/null @@ -1,29 +0,0 @@ -#include "file/all/AllChannelPressureEvent.hpp" - -#include "file/all/AllEvent.hpp" - -using namespace mpc::file::all; -using namespace mpc::sequencer; - -EventData -AllChannelPressureEvent::bytesToMpcEvent(const std::vector &bytes) -{ - EventData e; - e.type = EventType::ChannelPressure; - e.tick = AllEvent::readTick(bytes); - e.trackIndex = TrackIndex(bytes[AllEvent::TRACK_OFFSET]); - e.amount = bytes[AMOUNT_OFFSET]; - return e; -} - -std::vector AllChannelPressureEvent::mpcEventToBytes(const EventData &e) -{ - std::vector bytes(8); - - bytes[AllEvent::EVENT_ID_OFFSET] = AllEvent::CH_PRESSURE_ID; - AllEvent::writeTick(bytes, e.tick); - bytes[AllEvent::TRACK_OFFSET] = e.trackIndex; - bytes[AMOUNT_OFFSET] = e.amount; - - return bytes; -} diff --git a/src/main/file/all/AllChannelPressureEvent.hpp b/src/main/file/all/AllChannelPressureEvent.hpp deleted file mode 100644 index c8cb77956..000000000 --- a/src/main/file/all/AllChannelPressureEvent.hpp +++ /dev/null @@ -1,17 +0,0 @@ -#pragma once - -#include "sequencer/EventData.hpp" - -#include - -namespace mpc::file::all -{ - class AllChannelPressureEvent - { - static constexpr int AMOUNT_OFFSET = 5; - - public: - static sequencer::EventData bytesToMpcEvent(const std::vector &); - static std::vector mpcEventToBytes(const sequencer::EventData &); - }; -} // namespace mpc::file::all diff --git a/src/main/file/all/AllControlChangeEvent.cpp b/src/main/file/all/AllControlChangeEvent.cpp deleted file mode 100644 index b785f9891..000000000 --- a/src/main/file/all/AllControlChangeEvent.cpp +++ /dev/null @@ -1,31 +0,0 @@ -#include "file/all/AllControlChangeEvent.hpp" - -#include "file/all/AllEvent.hpp" - -using namespace mpc::file::all; -using namespace mpc::sequencer; - -EventData AllControlChangeEvent::bytesToMpcEvent(const std::vector &bytes) -{ - EventData e; - e.type = EventType::ControlChange; - e.tick = AllEvent::readTick(bytes); - e.trackIndex = TrackIndex(bytes[AllEvent::TRACK_OFFSET]); - e.controllerNumber = bytes[CONTROLLER_OFFSET]; - e.controllerValue = bytes[AMOUNT_OFFSET]; - - return e; -} - -std::vector AllControlChangeEvent::mpcEventToBytes(const EventData &e) -{ - std::vector bytes(8); - - bytes[AllEvent::EVENT_ID_OFFSET] = AllEvent::CONTROL_CHANGE_ID; - AllEvent::writeTick(bytes, e.tick); - bytes[AllEvent::TRACK_OFFSET] = e.trackIndex; - bytes[CONTROLLER_OFFSET] = e.controllerNumber; - bytes[AMOUNT_OFFSET] = e.controllerValue; - - return bytes; -} diff --git a/src/main/file/all/AllControlChangeEvent.hpp b/src/main/file/all/AllControlChangeEvent.hpp deleted file mode 100644 index a41e2bb87..000000000 --- a/src/main/file/all/AllControlChangeEvent.hpp +++ /dev/null @@ -1,18 +0,0 @@ -#pragma once - -#include "sequencer/EventData.hpp" - -#include - -namespace mpc::file::all -{ - class AllControlChangeEvent - { - static constexpr int CONTROLLER_OFFSET = 5; - static constexpr int AMOUNT_OFFSET = 6; - - public: - static sequencer::EventData bytesToMpcEvent(const std::vector &); - static std::vector mpcEventToBytes(const sequencer::EventData &); - }; -} // namespace mpc::file::all diff --git a/src/main/file/all/AllEvent.cpp b/src/main/file/all/AllEvent.cpp deleted file mode 100644 index 011777ccc..000000000 --- a/src/main/file/all/AllEvent.cpp +++ /dev/null @@ -1,106 +0,0 @@ -#include "AllEvent.hpp" - -#include "AllChannelPressureEvent.hpp" -#include "AllControlChangeEvent.hpp" -#include "AllNoteEvent.hpp" -#include "AllPitchBendEvent.hpp" -#include "AllPolyPressureEvent.hpp" -#include "AllProgramChangeEvent.hpp" -#include "AllSysExEvent.hpp" - -#include - -#include "file/BitUtil.hpp" -#include "file/ByteUtil.hpp" - -using namespace mpc::file::all; -using namespace mpc::sequencer; - -std::vector AllEvent::TICK_BYTE3_BIT_RANGE{0, 3}; - -EventData AllEvent::bytesToMpcEvent(const std::vector &bytes) -{ - if (const auto eventID = bytes[EVENT_ID_OFFSET]; eventID < 0) - { - switch (bytes[EVENT_ID_OFFSET]) - { - case POLY_PRESSURE_ID: - return AllPolyPressureEvent::bytesToMpcEvent(bytes); - case CONTROL_CHANGE_ID: - return AllControlChangeEvent::bytesToMpcEvent(bytes); - case PGM_CHANGE_ID: - return AllProgramChangeEvent::bytesToMpcEvent(bytes); - case CH_PRESSURE_ID: - return AllChannelPressureEvent::bytesToMpcEvent(bytes); - case PITCH_BEND_ID: - return AllPitchBendEvent::bytesToMpcEvent(bytes); - case SYS_EX_ID: - return AllSysExEvent::bytesToMpcEvent(bytes); - default:; - } - } - - return AllNoteOnEvent::bytesToMpcEvent(bytes); -} - -std::vector AllEvent::mpcEventToBytes(const EventData &e) -{ - assert(e.type != sequencer::EventType::NoteOff); - - if (e.type == EventType::NoteOn) - { - return AllNoteOnEvent::mpcEventToBytes(e); - } - if (e.type == EventType::PolyPressure) - { - return AllPolyPressureEvent::mpcEventToBytes(e); - } - if (e.type == EventType::ControlChange) - { - return AllControlChangeEvent::mpcEventToBytes(e); - } - if (e.type == EventType::ProgramChange) - { - return AllProgramChangeEvent::mpcEventToBytes(e); - } - if (e.type == EventType::ChannelPressure) - { - return AllChannelPressureEvent::mpcEventToBytes(e); - } - if (e.type == EventType::PitchBend) - { - return AllPitchBendEvent::mpcEventToBytes(e); - } - if (e.type == EventType::SystemExclusive || e.type == EventType::Mixer) - { - return AllSysExEvent::mpcEventToBytes(e); - } - - return {}; -} - -int AllEvent::readTick(const std::vector &bytes) -{ - unsigned short s3 = BitUtil::removeUnusedBits(bytes[TICK_BYTE3_OFFSET], - TICK_BYTE3_BIT_RANGE); - - int result = ByteUtil::bytes2ushort(std::vector{bytes[TICK_BYTE1_OFFSET], - bytes[TICK_BYTE2_OFFSET]}) + - s3 * 65536; - - return result; -} - -void AllEvent::writeTick(std::vector &event, int tick) -{ - auto remainder = tick % 65536; - auto ba = ByteUtil::ushort2bytes(remainder); - event[TICK_BYTE1_OFFSET] = ba[0]; - event[TICK_BYTE2_OFFSET] = ba[1]; - auto s3 = static_cast(floor(tick / 65536.0)); - - event[TICK_BYTE3_OFFSET] = BitUtil::stitchBytes( - static_cast(event[TICK_BYTE3_OFFSET]), - AllNoteOnEvent::DURATION_BYTE1_BIT_RANGE, - static_cast(s3), TICK_BYTE3_BIT_RANGE); -} diff --git a/src/main/file/all/AllEvent.hpp b/src/main/file/all/AllEvent.hpp deleted file mode 100644 index b4d222899..000000000 --- a/src/main/file/all/AllEvent.hpp +++ /dev/null @@ -1,39 +0,0 @@ -#pragma once - -#include "sequencer/EventData.hpp" - -#include -#include - -namespace mpc::sequencer -{ - class EventRef; -} - -namespace mpc::file::all -{ - class AllEvent - { - public: - static constexpr int TICK_BYTE1_OFFSET = 0; - static constexpr int TICK_BYTE2_OFFSET = 1; - static constexpr int TICK_BYTE3_OFFSET = 2; - static std::vector TICK_BYTE3_BIT_RANGE; - - static constexpr int TRACK_OFFSET = 3; - static constexpr int EVENT_ID_OFFSET = 4; - - static constexpr char POLY_PRESSURE_ID = 0xA0; - static constexpr char CONTROL_CHANGE_ID = 0xB0; - static constexpr char PGM_CHANGE_ID = 0xC0; - static constexpr char CH_PRESSURE_ID = 0xD0; - static constexpr char PITCH_BEND_ID = 0xE0; - static constexpr char SYS_EX_ID = 0xF0; - - static int readTick(const std::vector &); - static void writeTick(std::vector &, int); - - static sequencer::EventData bytesToMpcEvent(const std::vector &); - static std::vector mpcEventToBytes(const sequencer::EventData &); - }; -} // namespace mpc::file::all diff --git a/src/main/file/all/AllNoteEvent.cpp b/src/main/file/all/AllNoteEvent.cpp deleted file mode 100644 index 6dc687f32..000000000 --- a/src/main/file/all/AllNoteEvent.cpp +++ /dev/null @@ -1,182 +0,0 @@ -#include "AllNoteEvent.hpp" - -#include "AllEvent.hpp" -#include "AllSequence.hpp" - -#include "file/BitUtil.hpp" - -using namespace mpc::file::all; -using namespace mpc::sequencer; - -EventData AllNoteOnEvent::bytesToMpcEvent(const std::vector &bytes) -{ - EventData e; - e.type = EventType::NoteOn; - e.noteNumber = NoteNumber(bytes[NOTE_NUMBER_OFFSET]); - e.tick = AllEvent::readTick(bytes); - const auto track = readTrackIndex(bytes); - e.trackIndex = TrackIndex(track); - e.duration = Duration(readDuration(bytes) - track * 4); - e.velocity = Velocity(readVelocity(bytes)); - e.noteVariationType = readVariationType(bytes); - e.noteVariationValue = NoteVariationValue(readVariationValue(bytes)); - - return e; -} - -std::vector AllNoteOnEvent::mpcEventToBytes(const EventData &e) -{ - std::vector bytes(AllSequence::EVENT_SEG_LENGTH); - - bytes[NOTE_NUMBER_OFFSET] = static_cast(e.noteNumber); - - writeTrackIndex(bytes, e.trackIndex); - writeVariationType(bytes, e.noteVariationType); - writeVariationValue(bytes, e.noteVariationValue); - AllEvent::writeTick(bytes, e.tick); - writeDuration(bytes, e.duration); - writeVelocity(bytes, e.velocity); - - return bytes; -} - -std::vector AllNoteOnEvent::DURATION_BYTE1_BIT_RANGE = std::vector{4, 7}; -std::vector AllNoteOnEvent::DURATION_BYTE2_BIT_RANGE = std::vector{6, 7}; -std::vector AllNoteOnEvent::TRACK_NUMBER_BIT_RANGE = std::vector{0, 5}; -std::vector AllNoteOnEvent::VELOCITY_BIT_RANGE = std::vector{0, 6}; -std::vector AllNoteOnEvent::VAR_VALUE_BIT_RANGE = std::vector{0, 6}; - -int AllNoteOnEvent::readDuration(const std::vector &bytes) -{ - auto b1 = bytes[DURATION_BYTE1_OFFSET]; - auto b2 = bytes[DURATION_BYTE2_OFFSET]; - const auto b3 = bytes[DURATION_BYTE3_OFFSET]; - - if (static_cast(b1) == 255 && - static_cast(b2) == 255 && - static_cast(b3) == 255) - { - return -1; - } - - b1 = BitUtil::removeUnusedBits(b1, DURATION_BYTE1_BIT_RANGE); - b2 = BitUtil::removeUnusedBits(b2, DURATION_BYTE2_BIT_RANGE); - - const auto i1 = b1 & 255; - const auto i2 = b2 & 255; - const auto i3 = b3 & 255; - - return (i1 << 6) + (i2 << 2) + i3; -} - -int AllNoteOnEvent::readTrackIndex(const std::vector &bytes) -{ - const auto raw = static_cast(bytes[TRACK_NUMBER_OFFSET]); - return raw & 0x3F; -} - -int AllNoteOnEvent::readVelocity(const std::vector &bytes) -{ - auto b = static_cast(bytes[VELOCITY_OFFSET]); - for (int i = 0; i < 8; i++) - { - if (i < VELOCITY_BIT_RANGE[0] || i > VELOCITY_BIT_RANGE[1]) - { - b = BitUtil::setBit(b, i, false); - } - } - return b; -} - -int AllNoteOnEvent::readVariationValue(const std::vector &bytes) -{ - auto b = static_cast(bytes[VAR_VALUE_OFFSET]); - for (int i = 0; i < 8; i++) - { - if (i < VAR_VALUE_BIT_RANGE[0] || i > VAR_VALUE_BIT_RANGE[1]) - { - b = BitUtil::setBit(b, i, false); - } - } - return b; -} - -mpc::NoteVariationType -AllNoteOnEvent::readVariationType(const std::vector &bytes) -{ - const auto byte1 = bytes[VAR_TYPE_BYTE1_OFFSET]; - const auto byte2 = bytes[VAR_TYPE_BYTE2_OFFSET]; - - const auto b1 = BitUtil::isBitOn(byte1, VAR_TYPE_BYTE1_BIT); - const auto b2 = BitUtil::isBitOn(byte2, VAR_TYPE_BYTE2_BIT); - - if (b1 && b2) - { - return NoteVariationTypeFilter; - } - if (b1 && !b2) - { - return NoteVariationTypeAttack; - } - if (!b1 && b2) - { - return NoteVariationTypeDecay; - } - return NoteVariationTypeTune; -} - -void AllNoteOnEvent::writeVelocity(std::vector &event, const int v) -{ - const auto value = static_cast(v); - event[VELOCITY_OFFSET] = BitUtil::stitchBytes( - event[VELOCITY_OFFSET], - std::vector{VAR_TYPE_BYTE1_BIT, VAR_TYPE_BYTE1_BIT}, value, - VELOCITY_BIT_RANGE); -} - -void AllNoteOnEvent::writeTrackIndex(std::vector &event, const int t) -{ - const auto value = static_cast(t); - event[TRACK_NUMBER_OFFSET] = BitUtil::stitchBytes( - event[TRACK_NUMBER_OFFSET], DURATION_BYTE2_BIT_RANGE, value, - TRACK_NUMBER_BIT_RANGE); -} - -void AllNoteOnEvent::writeVariationValue(std::vector &event, const int v) -{ - const auto value = static_cast(v); - event[VAR_VALUE_OFFSET] = BitUtil::stitchBytes( - event[VAR_VALUE_OFFSET], - std::vector{VAR_TYPE_BYTE2_BIT, VAR_TYPE_BYTE2_BIT}, value, - VAR_VALUE_BIT_RANGE); -} - -void AllNoteOnEvent::writeDuration(std::vector &event, const int duration) -{ - const auto s1 = static_cast(duration >> 6); - const auto s2 = static_cast(duration >> 2); - const auto s3 = static_cast(duration & 255); - - event[DURATION_BYTE1_OFFSET] = BitUtil::stitchBytes( - event[DURATION_BYTE1_OFFSET], AllEvent::TICK_BYTE3_BIT_RANGE, - static_cast(s1), DURATION_BYTE1_BIT_RANGE); - - event[DURATION_BYTE2_OFFSET] = BitUtil::stitchBytes( - event[DURATION_BYTE2_OFFSET], TRACK_NUMBER_BIT_RANGE, - static_cast(s2), DURATION_BYTE2_BIT_RANGE); - - event[DURATION_BYTE3_OFFSET] = static_cast(s3); -} - -void AllNoteOnEvent::writeVariationType(std::vector &event, - const int type) -{ - auto byte1 = event[VAR_TYPE_BYTE1_OFFSET]; - auto byte2 = event[VAR_TYPE_BYTE2_OFFSET]; - byte1 = static_cast( - BitUtil::setBit(byte1, VAR_TYPE_BYTE1_BIT, type == 2 || type == 3)); - byte2 = static_cast( - BitUtil::setBit(byte2, VAR_TYPE_BYTE2_BIT, type == 1 || type == 3)); - event[VAR_TYPE_BYTE1_OFFSET] = byte1; - event[VAR_TYPE_BYTE2_OFFSET] = byte2; -} diff --git a/src/main/file/all/AllNoteEvent.hpp b/src/main/file/all/AllNoteEvent.hpp deleted file mode 100644 index f6521a8d2..000000000 --- a/src/main/file/all/AllNoteEvent.hpp +++ /dev/null @@ -1,47 +0,0 @@ -#pragma once - -#include "sequencer/EventData.hpp" - -#include - -namespace mpc::file::all -{ - class AllEvent; - class AllNoteOnEvent - { - static constexpr int DURATION_BYTE1_OFFSET = 2; - static std::vector DURATION_BYTE1_BIT_RANGE; - static constexpr int DURATION_BYTE2_OFFSET = 3; - static std::vector DURATION_BYTE2_BIT_RANGE; - static constexpr int TRACK_NUMBER_OFFSET = 3; - static std::vector TRACK_NUMBER_BIT_RANGE; - static constexpr int NOTE_NUMBER_OFFSET = 4; - static constexpr int DURATION_BYTE3_OFFSET = 5; - static constexpr int VELOCITY_OFFSET = 6; - static std::vector VELOCITY_BIT_RANGE; - static constexpr int VAR_TYPE_BYTE1_OFFSET = 6; - static constexpr int VAR_TYPE_BYTE1_BIT = 7; - static constexpr int VAR_TYPE_BYTE2_OFFSET = 7; - static constexpr int VAR_TYPE_BYTE2_BIT = 7; - static constexpr int VAR_VALUE_OFFSET = 7; - static std::vector VAR_VALUE_BIT_RANGE; - - static int readDuration(const std::vector &); - static int readTrackIndex(const std::vector &); - static int readVelocity(const std::vector &); - static int readVariationValue(const std::vector &); - static NoteVariationType readVariationType(const std::vector &); - - static void writeVelocity(std::vector &event, int); - static void writeTrackIndex(std::vector &event, int); - static void writeVariationValue(std::vector &event, int); - static void writeDuration(std::vector &event, int); - static void writeVariationType(std::vector &event, int); - - friend class AllEvent; - - public: - static sequencer::EventData bytesToMpcEvent(const std::vector &); - static std::vector mpcEventToBytes(const sequencer::EventData &); - }; -} // namespace mpc::file::all diff --git a/src/main/file/all/AllParser.cpp b/src/main/file/all/AllParser.cpp deleted file mode 100644 index a9d9c4253..000000000 --- a/src/main/file/all/AllParser.cpp +++ /dev/null @@ -1,293 +0,0 @@ -#include "AllParser.hpp" - -#include - -#include "Mpc.hpp" - -#include "file/all/Count.hpp" -#include "file/all/Defaults.hpp" -#include "file/all/Header.hpp" -#include "file/all/MidiInput.hpp" -#include "file/all/MidiSyncMisc.hpp" -#include "file/all/Misc.hpp" -#include "file/all/AllSequence.hpp" -#include "file/all/SequenceNames.hpp" -#include "file/all/AllSequencer.hpp" -#include "file/all/AllSong.hpp" - -#include "sequencer/Sequencer.hpp" - -#include "file/ByteUtil.hpp" - -#include "Util.hpp" - -using namespace mpc::file::all; - -AllParser::AllParser(Mpc &_mpc, const std::vector &loadBytes) : mpc(_mpc) -{ - if (loadBytes.size() >= HEADER_OFFSET + HEADER_LENGTH) - { - header = new Header(Util::vecCopyOfRange( - loadBytes, HEADER_OFFSET, HEADER_OFFSET + HEADER_LENGTH)); - } - - if (header == nullptr || !header->verifyFileID()) - { - throw std::invalid_argument("Invalid ALL file header ID"); - } - - defaults = new Defaults( - mpc, Util::vecCopyOfRange(loadBytes, DEFAULTS_OFFSET, - DEFAULTS_OFFSET + DEFAULTS_LENGTH)); - sequencer = new AllSequencer(Util::vecCopyOfRange( - loadBytes, SEQUENCER_OFFSET, SEQUENCER_OFFSET + AllSequencer::LENGTH)); - count = new Count(Util::vecCopyOfRange(loadBytes, COUNT_OFFSET, - COUNT_OFFSET + COUNT_LENGTH)); - midiInput = new MidiInput(Util::vecCopyOfRange( - loadBytes, MIDI_INPUT_OFFSET, MIDI_INPUT_OFFSET + MidiInput::LENGTH)); - midiSyncMisc = new MidiSyncMisc(Util::vecCopyOfRange( - loadBytes, MIDI_SYNC_OFFSET, MIDI_SYNC_OFFSET + MidiSyncMisc::LENGTH)); - misc = new Misc(Util::vecCopyOfRange(loadBytes, MISC_OFFSET, - MISC_OFFSET + Misc::LENGTH)); - seqNames = new SequenceNames( - Util::vecCopyOfRange(loadBytes, SEQUENCE_NAMES_OFFSET, - SEQUENCE_NAMES_OFFSET + SequenceNames::LENGTH)); - - for (int i = 0; i < 20; i++) - { - int offset = SONGS_OFFSET + i * Song::LENGTH; - songs[i] = new Song( - Util::vecCopyOfRange(loadBytes, offset, offset + Song::LENGTH)); - } - - sequences = readSequences( - Util::vecCopyOfRange(loadBytes, SEQUENCES_OFFSET, loadBytes.size())); -} - -AllParser::AllParser(Mpc &_mpc) : mpc(_mpc) -{ - std::vector> chunks; - chunks.push_back(Header().getBytes()); - - Defaults saveDefaults(mpc); - - chunks.push_back(saveDefaults.getBytes()); - chunks.push_back(UNKNOWN_CHUNK); - sequencer = new AllSequencer(mpc); - chunks.push_back(sequencer->getBytes()); - - chunks.push_back({0, 12, 0, 0, 0, 0, 0, 0, 0, 0}); - - count = new Count(mpc); - chunks.push_back(count->getBytes()); - midiInput = new MidiInput(mpc); - chunks.push_back(midiInput->getBytes()); - - midiSyncMisc = new MidiSyncMisc(mpc); - chunks.push_back(midiSyncMisc->getBytes()); - - chunks.push_back({0, 0, 0, 0, 0}); - - misc = new Misc(mpc); - chunks.push_back(misc->getBytes()); - seqNames = new SequenceNames(mpc); - chunks.push_back(seqNames->getBytes()); - - auto mpcSequencer = mpc.getSequencer(); - - for (int i = 0; i < 20; i++) - { - songs[i] = new Song(mpcSequencer->getSong(i).get()); - chunks.push_back(songs[i]->getBytes()); - } - - auto usedSeqs = mpcSequencer->getUsedSequences(); - - for (int i = 0; i < usedSeqs.size(); i++) - { - auto seq = usedSeqs[i]; - AllSequence allSeq(seq.get(), - mpcSequencer->getUsedSequenceIndexes()[i] + 1); - chunks.push_back(allSeq.getBytes()); - } - - saveBytes = ByteUtil::stitchByteArrays(chunks); -} - -AllParser::~AllParser() -{ - if (header != nullptr) - { - delete header; - } - if (defaults != nullptr) - { - delete defaults; - } - if (sequencer != nullptr) - { - delete sequencer; - } - if (count != nullptr) - { - delete count; - } - if (midiInput != nullptr) - { - delete midiInput; - } - if (midiSyncMisc != nullptr) - { - delete midiSyncMisc; - } - if (misc != nullptr) - { - delete misc; - } - if (seqNames != nullptr) - { - delete seqNames; - } - - for (auto &s : sequences) - { - if (s != nullptr) - { - delete s; - } - } - - for (auto &s : songs) - { - if (s != nullptr) - { - delete s; - } - } -} - -std::vector AllParser::getAllSequences() -{ - return sequences; -} - -Defaults *AllParser::getDefaults() const -{ - return defaults; -} - -AllSequencer *AllParser::getSequencer() const -{ - return sequencer; -} - -Count *AllParser::getCount() const -{ - return count; -} - -MidiInput *AllParser::getMidiInput() const -{ - return midiInput; -} - -MidiSyncMisc *AllParser::getMidiSync() const -{ - return midiSyncMisc; -} - -Misc *AllParser::getMisc() const -{ - return misc; -} - -SequenceNames *AllParser::getSeqNames() const -{ - return seqNames; -} - -std::vector AllParser::getSongs() -{ - return songs; -} - -std::vector -AllParser::readSequences(const std::vector &trimmedSeqsArray) const -{ - const int totalSeqChunkLength = trimmedSeqsArray.size(); - - if (totalSeqChunkLength == 0) - { - return {}; - } - - std::vector seqs; - int read = 0; - - auto usednesses = seqNames->getUsednesses(); - - size_t pos = 0; - - for (const auto &u : usednesses) - { - if (!u) - { - seqs.push_back(nullptr); - continue; - } - - std::vector view; - if (pos < trimmedSeqsArray.size()) - { - view = Util::vecCopyOfRange(trimmedSeqsArray, pos, - trimmedSeqsArray.size()); - } - else - { - seqs.push_back(nullptr); - continue; - } - - int eventSegments = - AllSequence::getNumberOfEventSegmentsForThisSeq(view); - - int currentSeqLen = EMPTY_SEQ_LENGTH + eventSegments * EVENT_LENGTH; - - if ((size_t)currentSeqLen > view.size()) - { - currentSeqLen = (int)view.size(); - } - - auto currentSeqArray = - Util::vecCopyOfRange(trimmedSeqsArray, pos, pos + currentSeqLen); - - auto as = new AllSequence(currentSeqArray); - seqs.push_back(as); - - read += currentSeqLen; - pos += currentSeqLen; - - if ((eventSegments & 1) != 0) - { - if (pos >= EVENT_LENGTH) - { - pos -= EVENT_LENGTH; - read -= EVENT_LENGTH; - } - else - { - pos = 0; - read = 0; - } - } - } - - return seqs; -} - -std::vector &AllParser::getBytes() -{ - return saveBytes; -} - -std::vector AllParser::UNKNOWN_CHUNK = std::vector(64); diff --git a/src/main/file/all/AllParser.hpp b/src/main/file/all/AllParser.hpp deleted file mode 100644 index 46b991fb8..000000000 --- a/src/main/file/all/AllParser.hpp +++ /dev/null @@ -1,92 +0,0 @@ -#pragma once - -#include "mpc_types.hpp" - -#include - -namespace mpc -{ - class Mpc; -} - -namespace mpc::disk -{ - class MpcFile; -} - -namespace mpc::file::all -{ - class Header; - class Defaults; - class AllSequencer; - class Count; - class MidiInput; - class MidiSyncMisc; - class Misc; - class SequenceNames; - class AllSequence; - class Song; -} // namespace mpc::file::all - -namespace mpc::file::all -{ - class AllParser - { - - public: - static const int NAME_LENGTH{16}; - static const int DEV_NAME_LENGTH{8}; - static const int EMPTY_SEQ_LENGTH{10256}; - static const int EVENT_LENGTH{8}; - static const int HEADER_OFFSET{0}; - static const int HEADER_LENGTH{16}; - static const int DEFAULTS_OFFSET{16}; - static std::vector UNKNOWN_CHUNK; - - static const int DEFAULTS_LENGTH{1728}; - - static const int SEQUENCER_OFFSET{1808}; - static const int COUNT_OFFSET{1829}; - static const int COUNT_LENGTH{13}; - static const int MIDI_INPUT_OFFSET{1842}; - static const int MIDI_SYNC_OFFSET{1904}; - static const int MISC_OFFSET{1933}; - static const int SEQUENCE_NAMES_OFFSET{2064}; - static const int SONGS_OFFSET{3846}; - static const int SEQUENCES_OFFSET{14406}; - Header *header = nullptr; - Defaults *defaults = nullptr; - AllSequencer *sequencer = nullptr; - Count *count = nullptr; - MidiInput *midiInput = nullptr; - MidiSyncMisc *midiSyncMisc = nullptr; - Misc *misc = nullptr; - SequenceNames *seqNames = nullptr; - std::vector sequences; - std::vector songs = std::vector(20); - std::vector saveBytes; - - std::vector getAllSequences(); - Defaults *getDefaults() const; - AllSequencer *getSequencer() const; - Count *getCount() const; - MidiInput *getMidiInput() const; - MidiSyncMisc *getMidiSync() const; - Misc *getMisc() const; - SequenceNames *getSeqNames() const; - std::vector getSongs(); - - private: - Mpc &mpc; - - std::vector - readSequences(const std::vector &trimmedSeqsArray) const; - - public: - std::vector &getBytes(); - - AllParser(Mpc &, const std::vector &loadBytes); - AllParser(Mpc &); - ~AllParser(); - }; -} // namespace mpc::file::all diff --git a/src/main/file/all/AllPitchBendEvent.cpp b/src/main/file/all/AllPitchBendEvent.cpp deleted file mode 100644 index cca787673..000000000 --- a/src/main/file/all/AllPitchBendEvent.cpp +++ /dev/null @@ -1,51 +0,0 @@ -#include "file/all/AllPitchBendEvent.hpp" - -#include "file/all/AllEvent.hpp" - -#include "file/ByteUtil.hpp" - -using namespace mpc::file::all; -using namespace mpc::sequencer; - -EventData AllPitchBendEvent::bytesToMpcEvent(const std::vector &bytes) -{ - EventData e; - e.type = EventType::PitchBend; - e.tick = AllEvent::readTick(bytes); - e.trackIndex = TrackIndex(bytes[AllEvent::TRACK_OFFSET]); - - auto candidate = ByteUtil::bytes2ushort(std::vector{ - bytes[AMOUNT_OFFSET], bytes[AMOUNT_OFFSET + 1]}) - - 16384; - - if (candidate < -8192) - { - candidate += 8192; - } - - e.amount = candidate; - - return e; -} - -std::vector AllPitchBendEvent::mpcEventToBytes(const EventData &e) -{ - std::vector bytes(8); - bytes[AllEvent::EVENT_ID_OFFSET] = AllEvent::PITCH_BEND_ID; - AllEvent::writeTick(bytes, e.tick); - bytes[AllEvent::TRACK_OFFSET] = e.trackIndex; - - auto candidate = e.amount + 16384; - - if (e.amount < 0) - { - candidate = e.amount + 8192; - } - - const auto amountBytes = ByteUtil::ushort2bytes(candidate); - - bytes[AMOUNT_OFFSET] = amountBytes[0]; - bytes[AMOUNT_OFFSET + 1] = amountBytes[1]; - - return bytes; -} diff --git a/src/main/file/all/AllPitchBendEvent.hpp b/src/main/file/all/AllPitchBendEvent.hpp deleted file mode 100644 index d3ee098f4..000000000 --- a/src/main/file/all/AllPitchBendEvent.hpp +++ /dev/null @@ -1,17 +0,0 @@ -#pragma once - -#include "sequencer/EventData.hpp" - -#include - -namespace mpc::file::all -{ - class AllPitchBendEvent - { - static constexpr int AMOUNT_OFFSET = 5; - - public: - static sequencer::EventData bytesToMpcEvent(const std::vector &); - static std::vector mpcEventToBytes(const sequencer::EventData &); - }; -} // namespace mpc::file::all diff --git a/src/main/file/all/AllPolyPressureEvent.cpp b/src/main/file/all/AllPolyPressureEvent.cpp deleted file mode 100644 index e3170a1cc..000000000 --- a/src/main/file/all/AllPolyPressureEvent.cpp +++ /dev/null @@ -1,31 +0,0 @@ -#include "AllPolyPressureEvent.hpp" - -#include "file/all/AllEvent.hpp" - -using namespace mpc::file::all; -using namespace mpc::sequencer; - -EventData AllPolyPressureEvent::bytesToMpcEvent(const std::vector &bytes) -{ - EventData e; - e.type = EventType::PolyPressure; - e.tick = AllEvent::readTick(bytes); - e.trackIndex = TrackIndex(bytes[AllEvent::TRACK_OFFSET]); - e.noteNumber = NoteNumber(bytes[NOTE_OFFSET]); - e.amount = bytes[AMOUNT_OFFSET]; - - return e; -} - -std::vector AllPolyPressureEvent::mpcEventToBytes(const EventData &e) -{ - std::vector bytes(8); - - bytes[AllEvent::EVENT_ID_OFFSET] = AllEvent::POLY_PRESSURE_ID; - AllEvent::writeTick(bytes, e.tick); - bytes[AllEvent::TRACK_OFFSET] = e.trackIndex; - bytes[NOTE_OFFSET] = e.noteNumber; - bytes[AMOUNT_OFFSET] = e.amount; - - return bytes; -} diff --git a/src/main/file/all/AllPolyPressureEvent.hpp b/src/main/file/all/AllPolyPressureEvent.hpp deleted file mode 100644 index 879e8d7ea..000000000 --- a/src/main/file/all/AllPolyPressureEvent.hpp +++ /dev/null @@ -1,18 +0,0 @@ -#pragma once - -#include "sequencer/EventData.hpp" - -#include - -namespace mpc::file::all -{ - class AllPolyPressureEvent - { - static constexpr int NOTE_OFFSET = 5; - static constexpr int AMOUNT_OFFSET = 6; - - public: - static sequencer::EventData bytesToMpcEvent(const std::vector &); - static std::vector mpcEventToBytes(const sequencer::EventData &); - }; -} // namespace mpc::file::all diff --git a/src/main/file/all/AllProgramChangeEvent.cpp b/src/main/file/all/AllProgramChangeEvent.cpp deleted file mode 100644 index 51dc79ff8..000000000 --- a/src/main/file/all/AllProgramChangeEvent.cpp +++ /dev/null @@ -1,29 +0,0 @@ -#include "AllProgramChangeEvent.hpp" - -#include "file/all/AllEvent.hpp" - -using namespace mpc::file::all; -using namespace mpc::sequencer; - -EventData AllProgramChangeEvent::bytesToMpcEvent(const std::vector &bytes) -{ - EventData e; - e.type = EventType::ProgramChange; - e.tick = AllEvent::readTick(bytes); - e.trackIndex = TrackIndex(bytes[AllEvent::TRACK_OFFSET]); - e.programChangeProgramIndex = ProgramIndex(bytes[PROGRAM_OFFSET]); - - return e; -} - -std::vector AllProgramChangeEvent::mpcEventToBytes(const EventData &e) -{ - std::vector bytes(8); - - bytes[AllEvent::EVENT_ID_OFFSET] = AllEvent::PGM_CHANGE_ID; - AllEvent::writeTick(bytes, e.tick); - bytes[AllEvent::TRACK_OFFSET] = e.trackIndex; - bytes[PROGRAM_OFFSET] = e.programChangeProgramIndex; - - return bytes; -} diff --git a/src/main/file/all/AllProgramChangeEvent.hpp b/src/main/file/all/AllProgramChangeEvent.hpp deleted file mode 100644 index a54b177a8..000000000 --- a/src/main/file/all/AllProgramChangeEvent.hpp +++ /dev/null @@ -1,17 +0,0 @@ -#pragma once - -#include "sequencer/EventData.hpp" - -#include - -namespace mpc::file::all -{ - class AllProgramChangeEvent - { - static constexpr int PROGRAM_OFFSET = 5; - - public: - static sequencer::EventData bytesToMpcEvent(const std::vector &); - static std::vector mpcEventToBytes(const sequencer::EventData &); - }; -} // namespace mpc::file::all diff --git a/src/main/file/all/AllSequence.cpp b/src/main/file/all/AllSequence.cpp deleted file mode 100644 index f09a6495c..000000000 --- a/src/main/file/all/AllSequence.cpp +++ /dev/null @@ -1,556 +0,0 @@ -#include "AllSequence.hpp" - -#include "Bar.hpp" -#include "AllEvent.hpp" -#include "AllParser.hpp" -#include "BarList.hpp" -#include "SequenceNames.hpp" -#include "Tracks.hpp" - -#include "sequencer/EventRef.hpp" -#include "sequencer/MixerEvent.hpp" -#include "sequencer/Sequence.hpp" -#include "sequencer/Track.hpp" -#include "sequencer/SystemExclusiveEvent.hpp" - -#include "file/ByteUtil.hpp" -#include "StrUtil.hpp" -#include "Util.hpp" -#include "sequencer/SequencerStateManager.hpp" - -#ifdef __linux__ -#include -#endif - -using namespace mpc::file::all; -using namespace mpc::sequencer; - -AllSequence::AllSequence(const std::vector &bytes) -{ - barList = new BarList(Util::vecCopyOfRange( - bytes, BAR_LIST_OFFSET, BAR_LIST_OFFSET + BAR_LIST_LENGTH)); - const auto nameBytes = Util::vecCopyOfRange( - bytes, NAME_OFFSET, NAME_OFFSET + AllParser::NAME_LENGTH); - name = ""; - - for (const char c : nameBytes) - { - if (c == 0x00) - { - break; - } - - name.push_back(c); - } - - name = StrUtil::trim(name); - - tempo = getTempoDouble( - std::vector{bytes[TEMPO_BYTE1_OFFSET], bytes[TEMPO_BYTE2_OFFSET]}); - const auto barCountBytes = std::vector{bytes[BAR_COUNT_BYTE1_OFFSET], - bytes[BAR_COUNT_BYTE2_OFFSET]}; - barCount = ByteUtil::bytes2ushort(barCountBytes); - loopFirst = ByteUtil::bytes2ushort( - std::vector{bytes[LOOP_FIRST_OFFSET], bytes[LOOP_FIRST_OFFSET + 1]}); - loopLast = ByteUtil::bytes2ushort( - std::vector{bytes[LOOP_LAST_OFFSET], bytes[LOOP_LAST_OFFSET + 1]}); - - if (loopLast > 998) - { - loopLast = barCount; - loopLastEnd = true; - } - - loop = bytes[LOOP_ENABLED_OFFSET] > 0; - - startTime.hours = bytes[START_TIME_OFFSET]; - startTime.minutes = bytes[START_TIME_OFFSET + 1]; - startTime.seconds = bytes[START_TIME_OFFSET + 2]; - startTime.frames = bytes[START_TIME_OFFSET + 3]; - startTime.frameDecimals = bytes[START_TIME_OFFSET + 4]; - - for (int i = 0; i < 33; i++) - { - const auto offset = - DEVICE_NAMES_OFFSET + i * AllParser::DEV_NAME_LENGTH; - std::string stringBuffer; - auto stringBytes = Util::vecCopyOfRange( - bytes, offset, offset + AllParser::DEV_NAME_LENGTH); - - for (const char c : stringBytes) - { - if (c == 0x00) - { - break; - } - - stringBuffer.push_back(c); - } - - devNames[i] = stringBuffer; - } - - tracks = - new Tracks(Util::vecCopyOfRange(bytes, TRACKS_OFFSET, TRACKS_LENGTH)); - allEvents = readEvents(bytes); -} - -AllSequence::~AllSequence() -{ - if (tracks != nullptr) - { - delete tracks; - } - - if (barList != nullptr) - { - delete barList; - } -} - -void AllSequence::applyToInMemorySequence( - const std::shared_ptr &inMemorySequence, - SequencerStateManager *const manager) const -{ - inMemorySequence->init(barCount - 1); - - for (int i = 0; i < barCount; i++) - { - const auto num = barList->getBars()[i]->getNumerator(); - const auto den = barList->getBars()[i]->getDenominator(); - inMemorySequence->setTimeSignature(i, num, den); - } - - inMemorySequence->setName(name); - inMemorySequence->setInitialTempo(tempo); - - const auto sequenceIndex = inMemorySequence->getSequenceIndex(); - - UpdateSequenceTracks updateSequenceTracks{sequenceIndex}; - manager->trackStatesSnapshots[sequenceIndex] = - SequenceTrackStatesSnapshot(); - updateSequenceTracks.trackStates = - &manager->trackStatesSnapshots[sequenceIndex]; - - auto &trackStates = *updateSequenceTracks.trackStates; - - for (int i = 0; i < Mpc2000XlSpecs::TRACK_COUNT; ++i) - { - const auto inMemoryTrack = inMemorySequence->getTrack(i); - - trackStates[i].name = tracks->getName(i); - trackStates[i].deviceIndex = tracks->getDeviceIndex(i); - trackStates[i].busType = busIndexToBusType(tracks->getBus(i)); - trackStates[i].programChange = tracks->getPgm(i); - trackStates[i].on = tracks->isOn(i); - trackStates[i].velocityRatio = tracks->getVelo(i); - trackStates[i].transmitProgramChangesEnabled = - tracks->isTransmitProgramChangesEnabled(i); - trackStates[i].used = tracks->isUsed(i); - } - - manager->enqueue(updateSequenceTracks); - - UpdateSequenceEvents updateSequenceEvents{ - inMemorySequence->getSequenceIndex()}; - - updateSequenceEvents.trackSnapshots = - &manager->trackEventsSnapshots[sequenceIndex]; - - updateSequenceEvents.trackSnapshots->clear(); - - for (int j = 0; j < getEventAmount(); j++) - { - auto e = allEvents[j]; - - if (e.type == EventType::Unknown) - { - continue; - } - - (*updateSequenceEvents.trackSnapshots)[e.trackIndex].push_back(e); - } - - manager->enqueue(updateSequenceEvents); - - for (int i = 0; i < 32; i++) - { - inMemorySequence->setDeviceName(i, devNames[i + 1]); - } - - inMemorySequence->setFirstLoopBarIndex(BarIndex(loopFirst)); - inMemorySequence->setLastLoopBarIndex(BarIndex(loopLast)); - inMemorySequence->setLastLoopBarIndex(BarIndex(loopLast)); - - if (loopLastEnd) - { - inMemorySequence->setLastLoopBarIndex(EndOfSequence); - } - - inMemorySequence->setLoopEnabled(loop); - inMemorySequence->getStartTime().hours = startTime.hours; - inMemorySequence->getStartTime().minutes = startTime.minutes; - inMemorySequence->getStartTime().seconds = startTime.seconds; - inMemorySequence->getStartTime().frames = startTime.frames; - inMemorySequence->getStartTime().frameDecimals = startTime.frameDecimals; -} - -AllSequence::AllSequence(Sequence *seq, int number) -{ - auto segmentCountLastEventIndex = SequenceNames::getSegmentCount(seq); - auto segmentCount = getSegmentCount(seq); - auto terminatorCount = (segmentCount & 1) == 0 ? 2 : 1; - saveBytes = std::vector(10240 + segmentCount * EVENT_SEG_LENGTH + - terminatorCount * EVENT_SEG_LENGTH); - - for (int i = 0; i < AllParser::NAME_LENGTH; i++) - { - saveBytes[i] = - StrUtil::padRight(seq->getName(), " ", AllParser::NAME_LENGTH)[i]; - } - - if ((segmentCountLastEventIndex & 1) != 0) - { - segmentCountLastEventIndex--; - } - - segmentCountLastEventIndex /= 2; - auto lastEventIndexBytes = ByteUtil::ushort2bytes( - 1 + (segmentCountLastEventIndex < 0 ? 0 : segmentCountLastEventIndex)); - - saveBytes[LAST_EVENT_INDEX_OFFSET] = lastEventIndexBytes[0]; - saveBytes[LAST_EVENT_INDEX_OFFSET + 1] = lastEventIndexBytes[1]; - - for (int i = PADDING1_OFFSET; i < PADDING1_OFFSET + PADDING1.size(); i++) - { - saveBytes[i] = PADDING1[i - PADDING1_OFFSET]; - } - - setTempoDouble(seq->getInitialTempo()); - - for (int i = PADDING2_OFFSET; i < PADDING2_OFFSET + PADDING2.size(); i++) - { - saveBytes[i] = PADDING2[i - PADDING2_OFFSET]; - } - - setBarCount(seq->getLastBarIndex() + 1); - setLastTick(seq); - saveBytes[SEQUENCE_INDEX_OFFSET] = number; - setUnknown32BitInt(seq); - auto loopStartBytes = ByteUtil::ushort2bytes(seq->getFirstLoopBarIndex()); - auto loopEndBytes = ByteUtil::ushort2bytes(seq->getLastLoopBarIndex()); - - if (seq->getLastLoopBarIndex() == EndOfSequence) - { - loopEndBytes = - std::vector{static_cast(255), static_cast(255)}; - } - - saveBytes[LOOP_FIRST_OFFSET] = loopStartBytes[0]; - saveBytes[LOOP_FIRST_OFFSET + 1] = loopStartBytes[1]; - saveBytes[LOOP_LAST_OFFSET] = loopEndBytes[0]; - saveBytes[LOOP_LAST_OFFSET + 1] = loopEndBytes[1]; - saveBytes[LOOP_ENABLED_OFFSET] = seq->isLoopEnabled() ? 1 : 0; - - saveBytes[START_TIME_OFFSET] = seq->getStartTime().hours; - saveBytes[START_TIME_OFFSET + 1] = seq->getStartTime().minutes; - saveBytes[START_TIME_OFFSET + 2] = seq->getStartTime().seconds; - saveBytes[START_TIME_OFFSET + 3] = seq->getStartTime().frames; - saveBytes[START_TIME_OFFSET + 4] = seq->getStartTime().frameDecimals; - - for (int i = 0; i < PADDING4.size(); i++) - { - saveBytes[PADDING4_OFFSET + i] = PADDING4[i]; - } - - for (int i = 0; i < 33; i++) - { - const auto deviceNameOffset = - DEVICE_NAMES_OFFSET + i * AllParser::DEV_NAME_LENGTH; - - const auto deviceName = - i == 0 ? " " - : StrUtil::padRight(seq->getDeviceName(i - 1), " ", - AllParser::DEV_NAME_LENGTH); - - for (int charOffset = 0; charOffset < AllParser::DEV_NAME_LENGTH; - charOffset++) - { - saveBytes[deviceNameOffset + charOffset] = deviceName[charOffset]; - } - } - Tracks allFileSeqTracks(seq); - - for (int i = 0; i < TRACKS_LENGTH; i++) - { - saveBytes[i + TRACKS_OFFSET] = allFileSeqTracks.getBytes()[i]; - } - - BarList allFileBarList(seq); - - for (int i = BAR_LIST_OFFSET; i < BAR_LIST_OFFSET + BAR_LIST_LENGTH; i++) - { - saveBytes[i] = allFileBarList.getBytes()[i - BAR_LIST_OFFSET]; - } - - auto eventArraysChunk = createEventSegmentsChunk(seq); - - for (int i = EVENTS_OFFSET; i < EVENTS_OFFSET + eventArraysChunk.size(); - i++) - { - saveBytes[i] = eventArraysChunk[i - EVENTS_OFFSET]; - } - - for (int i = static_cast(saveBytes.size()) - 8; i < saveBytes.size(); - i++) - { - saveBytes[i] = static_cast(255); - } -} - -const int AllSequence::MAX_SYSEX_SIZE; -const int AllSequence::EVENT_ID_OFFSET; -const char AllSequence::POLY_PRESSURE_ID; -const char AllSequence::CONTROL_CHANGE_ID; -const char AllSequence::PGM_CHANGE_ID; -const char AllSequence::CH_PRESSURE_ID; -const char AllSequence::PITCH_BEND_ID; -const char AllSequence::SYS_EX_ID; -const char AllSequence::SYS_EX_TERMINATOR_ID; -std::vector AllSequence::TERMINATOR = std::vector{ - static_cast(0xFF), static_cast(0xFF), static_cast(0xFF), - static_cast(0xFF), static_cast(0xFF), static_cast(0xFF), - static_cast(0xFF), static_cast(0xFF)}; -const int AllSequence::MAX_EVENT_SEG_COUNT; -const int AllSequence::EVENT_SEG_LENGTH; -const int AllSequence::NAME_OFFSET; -const int AllSequence::LAST_EVENT_INDEX_OFFSET; -const int AllSequence::SEQUENCE_INDEX_OFFSET; -const int AllSequence::PADDING1_OFFSET; -std::vector AllSequence::PADDING1 = std::vector{1, 1, 0}; -const int AllSequence::TEMPO_BYTE1_OFFSET; -const int AllSequence::TEMPO_BYTE2_OFFSET; -const int AllSequence::PADDING2_OFFSET; -std::vector AllSequence::PADDING2 = std::vector{4, 4}; -const int AllSequence::BAR_COUNT_BYTE1_OFFSET; -const int AllSequence::BAR_COUNT_BYTE2_OFFSET; -const int AllSequence::LAST_TICK_BYTE1_OFFSET; -const int AllSequence::LAST_TICK_BYTE2_OFFSET; -const int AllSequence::UNKNOWN32_BIT_INT_OFFSET; -const int AllSequence::LOOP_FIRST_OFFSET; -const int AllSequence::LOOP_LAST_OFFSET; -const int AllSequence::LOOP_ENABLED_OFFSET; -const int AllSequence::PADDING4_OFFSET; -std::vector AllSequence::PADDING4 = - std::vector{40, 0, static_cast(128), 0, 0}; -const int AllSequence::LAST_TICK_BYTE3_OFFSET; -const int AllSequence::LAST_TICK_BYTE4_OFFSET; -const int AllSequence::DEVICE_NAMES_OFFSET; -const int AllSequence::TRACKS_OFFSET; -const int AllSequence::TRACKS_LENGTH; -const int AllSequence::BAR_LIST_OFFSET; -const int AllSequence::BAR_LIST_LENGTH; -const int AllSequence::EVENTS_OFFSET; - -std::vector -AllSequence::readEvents(const std::vector &seqBytes) -{ - std::vector events; - - for (auto &eventBytes : readEventSegments(seqBytes)) - { - events.emplace_back(AllEvent::bytesToMpcEvent(eventBytes)); - } - - return events; -} - -std::vector> -AllSequence::readEventSegments(const std::vector &seqBytes) -{ - std::vector> eventArrays; - int candidateOffset = EVENTS_OFFSET; - - for (int i = 0; i < MAX_EVENT_SEG_COUNT; i++) - { - auto ea = Util::vecCopyOfRange(seqBytes, candidateOffset, - candidateOffset + EVENT_SEG_LENGTH); - - if (Util::vecEquals(ea, TERMINATOR)) - { - break; - } - - if (ea[EVENT_ID_OFFSET] == SYS_EX_ID) - { - int sysexSegs; - for (sysexSegs = 0; sysexSegs < MAX_SYSEX_SIZE; sysexSegs++) - { - auto potentialTerminator = Util::vecCopyOfRange( - seqBytes, candidateOffset + sysexSegs * EVENT_SEG_LENGTH, - candidateOffset + sysexSegs * EVENT_SEG_LENGTH + - EVENT_SEG_LENGTH); - - if (potentialTerminator[EVENT_ID_OFFSET] == - SYS_EX_TERMINATOR_ID) - { - break; - } - } - - sysexSegs++; - ea = Util::vecCopyOfRange(seqBytes, candidateOffset, - candidateOffset + - sysexSegs * EVENT_SEG_LENGTH); - } - eventArrays.push_back(ea); - candidateOffset += static_cast(ea.size()); - } - return eventArrays; -} - -double AllSequence::getTempoDouble(const std::vector &bytePair) -{ - const auto s = ByteUtil::bytes2ushort(bytePair); - return static_cast(s) / 10.0; -} - -int AllSequence::getNumberOfEventSegmentsForThisSeq( - const std::vector &seqBytes) -{ - auto accum = 0; - - for (auto &ba : readEventSegments(seqBytes)) - { - accum += static_cast(ba.size()) / 8; - } - - return accum; -} - -int AllSequence::getEventAmount() const -{ - return static_cast(allEvents.size()); -} - -int AllSequence::getSegmentCount(Sequence *seq) -{ - int segmentCount = 0; - - for (const auto &track : seq->getTracks()) - { - if (track->getIndex() > 63) - { - break; - } - - for (auto &e : track->getEvents()) - { - if (const auto sysExEvent = - std::dynamic_pointer_cast(e); - sysExEvent) - { - constexpr int dataSegments = 1; - segmentCount += dataSegments + 2; - } - else if (const auto mixerEvent = - std::dynamic_pointer_cast(e); - mixerEvent) - { - segmentCount += 4; - } - else - { - segmentCount++; - } - } - } - return segmentCount; -} - -void AllSequence::setUnknown32BitInt(const Sequence *seq) -{ - const auto unknownNumberBytes1 = ByteUtil::uint2bytes(10000000); - const auto unknownNumberBytes2 = - ByteUtil::uint2bytes(seq->getLastTick() * 5208.333333333333); - - for (int i = 0; i < 2; i++) - { - for (int j = 0; j < 4; j++) - { - saveBytes[UNKNOWN32_BIT_INT_OFFSET + j + i * 4] = - unknownNumberBytes1[j]; - } - } - - for (int i = 2; i < 4; i++) - { - for (int j = 0; j < 4; j++) - { - saveBytes[UNKNOWN32_BIT_INT_OFFSET + j + i * 4] = - unknownNumberBytes2[j]; - } - } -} - -void AllSequence::setBarCount(const int i) -{ - const auto ba = ByteUtil::ushort2bytes(i); - saveBytes[BAR_COUNT_BYTE1_OFFSET] = ba[0]; - saveBytes[BAR_COUNT_BYTE2_OFFSET] = ba[1]; -} - -std::vector AllSequence::createEventSegmentsChunk(Sequence *seq) -{ - std::vector> ea; - - for (int i = 0; i < seq->getLastTick(); i++) - { - for (const auto &track : seq->getTracks()) - { - if (track->getIndex() > 63) - { - break; - } - - for (const auto &event : track->getEvents()) - { - if (event->getTick() == i) - { - event->setTrack(track->getIndex()); - ea.push_back(AllEvent::mpcEventToBytes(*event->handle)); - } - } - } - } - - ea.push_back(TERMINATOR); - return ByteUtil::stitchByteArrays(ea); -} - -void AllSequence::setTempoDouble(const double tempoForSaveBytes) -{ - const auto ba = ByteUtil::ushort2bytes( - static_cast(tempoForSaveBytes * 10.0)); - saveBytes[TEMPO_BYTE1_OFFSET] = ba[0]; - saveBytes[TEMPO_BYTE2_OFFSET] = ba[1]; -} - -void AllSequence::setLastTick(const Sequence *seq) -{ - const auto lastTick = seq->getLastTick(); - const auto remainder = lastTick % 65536; - const auto b = ByteUtil::ushort2bytes(remainder); - const auto large = static_cast(floor(lastTick / 65536.0)); - saveBytes[LAST_TICK_BYTE1_OFFSET] = b[0]; - saveBytes[LAST_TICK_BYTE2_OFFSET] = b[1]; - saveBytes[LAST_TICK_BYTE2_OFFSET + 1] = large; - saveBytes[LAST_TICK_BYTE3_OFFSET] = b[0]; - saveBytes[LAST_TICK_BYTE4_OFFSET] = b[1]; - saveBytes[LAST_TICK_BYTE4_OFFSET + 1] = large; -} - -std::vector &AllSequence::getBytes() -{ - return saveBytes; -} diff --git a/src/main/file/all/AllSequence.hpp b/src/main/file/all/AllSequence.hpp deleted file mode 100644 index 92bd03da7..000000000 --- a/src/main/file/all/AllSequence.hpp +++ /dev/null @@ -1,131 +0,0 @@ -#pragma once - -#include -#include -#include - -#include "sequencer/Sequence.hpp" -#include "sequencer/EventData.hpp" - -namespace mpc::sequencer -{ - class Sequence; - class EventRef; -} // namespace mpc::sequencer - -namespace mpc::file::all -{ - class BarList; - class Tracks; -} // namespace mpc::file::all - -namespace mpc::file::all -{ - class AllSequence final - { - public: - static constexpr int MAX_SYSEX_SIZE{256}; - static constexpr int EVENT_ID_OFFSET{4}; - static constexpr char POLY_PRESSURE_ID = 0xA0; - static constexpr char CONTROL_CHANGE_ID = 0xB0; - static constexpr char PGM_CHANGE_ID = 0xC0; - static constexpr char CH_PRESSURE_ID = 0xD0; - static constexpr char PITCH_BEND_ID = 0xE0; - static constexpr char SYS_EX_ID = 0xF0; - static constexpr char SYS_EX_TERMINATOR_ID = 0xF8; - - private: - static std::vector TERMINATOR; - - public: - static constexpr int MAX_EVENT_SEG_COUNT{50000}; - static constexpr int EVENT_SEG_LENGTH{8}; - static constexpr int NAME_OFFSET{0}; - static constexpr int LAST_EVENT_INDEX_OFFSET{16}; - static constexpr int SEQUENCE_INDEX_OFFSET{18}; - static constexpr int PADDING1_OFFSET{19}; - - private: - static std::vector PADDING1; - - public: - static constexpr int TEMPO_BYTE1_OFFSET{22}; - static constexpr int TEMPO_BYTE2_OFFSET{23}; - static constexpr int PADDING2_OFFSET{24}; - - private: - static std::vector PADDING2; - - public: - static constexpr int BAR_COUNT_BYTE1_OFFSET{26}; - static constexpr int BAR_COUNT_BYTE2_OFFSET{27}; - static constexpr int LAST_TICK_BYTE1_OFFSET{28}; - static constexpr int LAST_TICK_BYTE2_OFFSET{29}; - static constexpr int UNKNOWN32_BIT_INT_OFFSET{32}; - static constexpr int LOOP_FIRST_OFFSET{48}; - static constexpr int LOOP_LAST_OFFSET{50}; - static constexpr int LOOP_ENABLED_OFFSET{52}; - static constexpr int START_TIME_OFFSET{53}; - static constexpr int PADDING4_OFFSET{59}; - - private: - static std::vector PADDING4; - - public: - static constexpr int LAST_TICK_BYTE3_OFFSET{64}; - static constexpr int LAST_TICK_BYTE4_OFFSET{65}; - static constexpr int DEVICE_NAMES_OFFSET{120}; - static constexpr int TRACKS_OFFSET{384}; - static constexpr int TRACKS_LENGTH{1764}; - static constexpr int BAR_LIST_OFFSET{5379}; - static constexpr int BAR_LIST_LENGTH{3996}; - static constexpr int EVENTS_OFFSET{10240}; - std::string name; - int barCount; - int loopFirst; - int loopLast; - bool loopLastEnd; - bool loop; - sequencer::Sequence::StartTime startTime{0, 0, 0, 0, 0}; - - double tempo; - std::vector devNames = std::vector(33); - Tracks *tracks = nullptr; - BarList *barList = nullptr; - std::vector allEvents; - - private: - std::vector saveBytes; - - static std::vector - readEvents(const std::vector &seqBytes); - static std::vector> - readEventSegments(const std::vector &seqBytes); - static double getTempoDouble(const std::vector &bytePair); - - public: - static int - getNumberOfEventSegmentsForThisSeq(const std::vector &seqBytes); - int getEventAmount() const; - - private: - static int getSegmentCount(sequencer::Sequence *seq); - void setUnknown32BitInt(const sequencer::Sequence *seq); - void setBarCount(int i); - static std::vector - createEventSegmentsChunk(sequencer::Sequence *seq); - void setTempoDouble(double tempoForSaveBytes); - void setLastTick(const sequencer::Sequence *seq); - - public: - std::vector &getBytes(); - - void applyToInMemorySequence( - const std::shared_ptr &inMemorySequence, - sequencer::SequencerStateManager *) const; - - explicit AllSequence(const std::vector &bytes); - AllSequence(sequencer::Sequence *seq, int number); - ~AllSequence(); - }; -} // namespace mpc::file::all diff --git a/src/main/file/all/AllSequencer.cpp b/src/main/file/all/AllSequencer.cpp deleted file mode 100644 index 105405045..000000000 --- a/src/main/file/all/AllSequencer.cpp +++ /dev/null @@ -1,82 +0,0 @@ -#include "AllSequencer.hpp" -#include "sequencer/Transport.hpp" -#include "file/ByteUtil.hpp" -#include "lcdgui/screens/window/TimeDisplayScreen.hpp" - -#include "Mpc.hpp" - -#include "lcdgui/screens/SecondSeqScreen.hpp" -#include "lcdgui/screens/window/TimingCorrectScreen.hpp" -#include "sequencer/Sequencer.hpp" - -using namespace mpc::file::all; -using namespace mpc::lcdgui; -using namespace mpc::lcdgui::screens; -using namespace mpc::lcdgui::screens::window; - -AllSequencer::AllSequencer(const std::vector &loadBytes) -{ - sequence = loadBytes[SEQ_OFFSET]; - track = loadBytes[TR_OFFSET]; - const auto masterTempoBytes = {loadBytes[MASTER_TEMPO_OFFSET], - loadBytes[MASTER_TEMPO_OFFSET + 1]}; - masterTempo = ByteUtil::bytes2ushort(masterTempoBytes) / 10.0; - tempoSourceIsSequence = loadBytes[TEMPO_SOURCE_IS_SEQUENCE_OFFSET] == 0x01; - tc = loadBytes[TC_OFFSET]; - timeDisplayStyle = loadBytes[TIME_DISPLAY_STYLE_OFFSET]; - secondSeqEnabled = loadBytes[SECOND_SEQ_ENABLED_OFFSET] > 0; - secondSeqIndex = loadBytes[SECOND_SEQ_INDEX_OFFSET]; -} - -AllSequencer::AllSequencer(Mpc &mpc) -{ - saveBytes = std::vector(LENGTH); - - for (int i = 0; i < LENGTH; i++) - { - saveBytes[i] = TEMPLATE[i]; - } - - const auto mpcSequencer = mpc.getSequencer(); - - saveBytes[SEQ_OFFSET] = mpcSequencer->getSelectedSequenceIndex(); - saveBytes[TR_OFFSET] = mpcSequencer->getSelectedTrackIndex(); - - const bool tempoSourceIsSequence = - mpcSequencer->getTransport()->isTempoSourceSequence(); - - mpcSequencer->getTransport()->setTempoSourceIsSequence(false); - - const auto masterTempoBytes = ByteUtil::ushort2bytes( - mpcSequencer->getTransport()->getMasterTempo() * 10.0); - - mpcSequencer->getTransport()->setTempoSourceIsSequence( - tempoSourceIsSequence); - - saveBytes[MASTER_TEMPO_OFFSET] = masterTempoBytes[0]; - saveBytes[MASTER_TEMPO_OFFSET + 1] = masterTempoBytes[1]; - - saveBytes[TEMPO_SOURCE_IS_SEQUENCE_OFFSET] = - mpcSequencer->getTransport()->isTempoSourceSequence() ? 0x01 : 0x00; - - const auto timingCorrectScreen = - mpc.screens->get(); - const auto noteValue = timingCorrectScreen->getNoteValue(); - - saveBytes[TC_OFFSET] = noteValue; - saveBytes[TIME_DISPLAY_STYLE_OFFSET] = - mpc.screens->get()->getDisplayStyle(); - saveBytes[SECOND_SEQ_ENABLED_OFFSET] = - mpcSequencer->isSecondSequenceEnabled() ? 1 : 0; - - auto secondSequenceScreen = mpc.screens->get(); - saveBytes[SECOND_SEQ_INDEX_OFFSET] = secondSequenceScreen->sq; -} - -std::vector AllSequencer::TEMPLATE = {0, 0, 0, 0, (char)176, 4, - 1, 3, 0, 0, 0}; - -std::vector &AllSequencer::getBytes() -{ - return saveBytes; -} diff --git a/src/main/file/all/AllSequencer.hpp b/src/main/file/all/AllSequencer.hpp deleted file mode 100644 index dbf2c5f13..000000000 --- a/src/main/file/all/AllSequencer.hpp +++ /dev/null @@ -1,45 +0,0 @@ -#pragma once - -#include - -namespace mpc -{ - class Mpc; -} - -namespace mpc::file::all -{ - class AllSequencer - { - public: - static const int LENGTH = 11; - - private: - static std::vector TEMPLATE; - - public: - static const int SEQ_OFFSET = 0; - static const int TR_OFFSET = 2; - static const int MASTER_TEMPO_OFFSET = 4; - static const int TEMPO_SOURCE_IS_SEQUENCE_OFFSET = 6; - static const int TC_OFFSET = 7; - static const int TIME_DISPLAY_STYLE_OFFSET = 8; - static const int SECOND_SEQ_ENABLED_OFFSET = 9; - static const int SECOND_SEQ_INDEX_OFFSET = 10; - - int sequence; - int track; - double masterTempo; - bool tempoSourceIsSequence; - int tc; - int timeDisplayStyle; - bool secondSeqEnabled; - int secondSeqIndex; - std::vector saveBytes; - - std::vector &getBytes(); - - AllSequencer(const std::vector &loadBytes); - AllSequencer(Mpc &); - }; -} // namespace mpc::file::all diff --git a/src/main/file/all/AllSong.cpp b/src/main/file/all/AllSong.cpp deleted file mode 100644 index 6be25464e..000000000 --- a/src/main/file/all/AllSong.cpp +++ /dev/null @@ -1,138 +0,0 @@ -#include "file/all/AllSong.hpp" - -#include "sequencer/Song.hpp" - -#include "Util.hpp" - -using namespace mpc::file::all; - -Song::Song(const std::vector &loadBytes) -{ - const auto nameBytes = Util::vecCopyOfRange( - loadBytes, NAME_OFFSET, NAME_OFFSET + AllParser::NAME_LENGTH); - - for (const char &c : nameBytes) - { - if (c == 0x00) - { - break; - } - name.push_back(c); - } - - const size_t end = name.find_last_not_of(' '); - name = end == std::string::npos ? "" : name.substr(0, end + 1); - - const auto stepsBytes = Util::vecCopyOfRange( - loadBytes, FIRST_STEP_OFFSET, FIRST_STEP_OFFSET + STEPS_LENGTH); - - for (int i = 0; i < STEPS_LENGTH; i += STEP_LENGTH) - { - uint8_t seqIndex = stepsBytes[i]; - uint8_t repeatCount = stepsBytes[i + 1]; - - if (seqIndex == 0xFF || repeatCount == 0xFF) - { - continue; - } - - steps.emplace_back(seqIndex, repeatCount); - } - - loopFirstStepIndex = loadBytes[LOOP_FIRST_STEP_INDEX_OFFSET]; - loopLastStepIndex = loadBytes[LOOP_LAST_STEP_INDEX_OFFSET]; - loopEnabled = loadBytes[LOOP_ENABLED_OFFSET] == 1; - - // This is what it's supposed to do, but there may be users out there - // with badly persisted files due to older versions of VMPC2000XL: - - // isUsed = loadBytes[IS_USED_OFFSET]; - - // So for now we rely on the following heuristic, as this will - // correctly parse real MPC2000XL ALL songs, as well as the ones written - // with older versions of VMPC2000XL, unless someone decides to name - // their song "(Unused)": - isUsed = name != "(Unused)"; -} - -Song::Song(sequencer::Song *mpcSong) -{ - const auto songName = mpcSong->getName(); - - for (int i = 0; i < AllParser::NAME_LENGTH; i++) - { - if (i >= songName.length()) - { - saveBytes[NAME_OFFSET + i] = ' '; - continue; - } - - saveBytes[NAME_OFFSET + i] = songName[i]; - } - - for (int i = 0; i < STEP_COUNT; i++) - { - if (i >= mpcSong->getStepCount()) - { - saveBytes[FIRST_STEP_OFFSET + i * 2] = static_cast(0xFF); - saveBytes[FIRST_STEP_OFFSET + i * 2 + 1] = static_cast(0xFF); - continue; - } - - const auto step = mpcSong->getStep(SongStepIndex(i)); - - saveBytes[FIRST_STEP_OFFSET + i * 2] = - static_cast(step.sequenceIndex); - - saveBytes[FIRST_STEP_OFFSET + i * 2 + 1] = - static_cast(step.repetitionCount); - } - - saveBytes[STEPS_TERMINATOR_OFFSET] = static_cast(0xFF); - saveBytes[STEPS_TERMINATOR_OFFSET + 1] = static_cast(0xFF); - - saveBytes[IS_USED_OFFSET] = mpcSong->isUsed() ? 1 : 0; - saveBytes[LOOP_FIRST_STEP_INDEX_OFFSET] = mpcSong->getFirstLoopStepIndex(); - saveBytes[LOOP_LAST_STEP_INDEX_OFFSET] = mpcSong->getLastLoopStepIndex(); - saveBytes[LOOP_ENABLED_OFFSET] = mpcSong->isLoopEnabled() ? 1 : 0; - - for (int i = LOOP_ENABLED_OFFSET + 1; i < LENGTH; i++) - { - saveBytes[i] = 0; - } -} - -std::string Song::getName() -{ - return name; -} - -std::vector> Song::getSteps() -{ - return steps; -} - -bool Song::getIsUsed() const -{ - return isUsed; -} - -int Song::getLoopFirstStepIndex() const -{ - return loopFirstStepIndex; -} - -int Song::getLoopLastStepIndex() const -{ - return loopLastStepIndex; -} - -bool Song::isLoopEnabled() const -{ - return loopEnabled; -} - -std::vector &Song::getBytes() -{ - return saveBytes; -} diff --git a/src/main/file/all/AllSong.hpp b/src/main/file/all/AllSong.hpp deleted file mode 100644 index 0ebb45c7b..000000000 --- a/src/main/file/all/AllSong.hpp +++ /dev/null @@ -1,65 +0,0 @@ -#pragma once - -#include - -#include -#include -#include - -namespace mpc::sequencer -{ - class Song; -} - -namespace mpc::file::all -{ - class Song - { - - public: - static const int LENGTH = 528; - - private: - static const int NAME_OFFSET = 0; - - /** - * Each step in a song is 2 bytes. The first byte is the sequence index - * (0 - 99), the second byte is the number of repeats. - */ - static const int FIRST_STEP_OFFSET = - NAME_OFFSET + AllParser::NAME_LENGTH; - static const int STEPS_LENGTH = 500; - static const int STEP_LENGTH = 2; - static const int STEP_COUNT = 250; - static const int STEPS_TERMINATOR_OFFSET = - FIRST_STEP_OFFSET + STEP_COUNT * STEP_LENGTH; - static const int IS_USED_OFFSET = STEPS_TERMINATOR_OFFSET + 2; - static const int LOOP_FIRST_STEP_INDEX_OFFSET = IS_USED_OFFSET + 1; - static const int LOOP_LAST_STEP_INDEX_OFFSET = - LOOP_FIRST_STEP_INDEX_OFFSET + 1; - static const int LOOP_ENABLED_OFFSET = LOOP_LAST_STEP_INDEX_OFFSET + 1; - - int loopFirstStepIndex = 0; - int loopLastStepIndex = 0; - bool loopEnabled = false; - - public: - std::string name; - std::vector> steps; - bool isUsed = false; - - std::vector saveBytes = std::vector(LENGTH); - - std::string getName(); - - std::vector &getBytes(); - - Song(const std::vector &loadBytes); - Song(sequencer::Song *mpcSong); - std::vector> getSteps(); - bool getIsUsed() const; - int getLoopFirstStepIndex() const; - int getLoopLastStepIndex() const; - bool isLoopEnabled() const; - }; -} // namespace mpc::file::all diff --git a/src/main/file/all/AllSysExEvent.cpp b/src/main/file/all/AllSysExEvent.cpp deleted file mode 100644 index 0d9814df6..000000000 --- a/src/main/file/all/AllSysExEvent.cpp +++ /dev/null @@ -1,115 +0,0 @@ -#include "AllSysExEvent.hpp" - -#include "AllEvent.hpp" -#include "AllSequence.hpp" -#include "Util.hpp" - -using namespace mpc::file::all; -using namespace mpc::sequencer; - -std::vector AllSysExEvent::MIXER_SIGNATURE = {static_cast(240), 71, - 0, 68, 69}; - -EventData AllSysExEvent::bytesToMpcEvent(const std::vector &bytes) -{ - const int byteCount = bytes[BYTE_COUNT_OFFSET]; - - std::vector sysexLoadData(byteCount); - - EventData e; - - for (int i = 0; i < byteCount; i++) - { - sysexLoadData[i] = bytes[DATA_OFFSET + i]; - } - - if (Util::vecEquals(Util::vecCopyOfRange( - sysexLoadData, MIXER_SIGNATURE_OFFSET, - MIXER_SIGNATURE_OFFSET + MIXER_SIGNATURE.size()), - MIXER_SIGNATURE)) - { - e.type = EventType::Mixer; - - auto paramCandidate = sysexLoadData[MIXER_PARAMETER_OFFSET] - 1; - - if (paramCandidate == 4) - { - paramCandidate = 3; - } - - e.mixerParameter = paramCandidate; - e.mixerPad = sysexLoadData[MIXER_PAD_OFFSET]; - e.mixerValue = sysexLoadData[MIXER_VALUE_OFFSET]; - e.tick = AllEvent::readTick(bytes); - } - else - { - e.type = EventType::SystemExclusive; - e.sysExByteA = sysexLoadData.size() > 0 - ? static_cast(sysexLoadData[0]) - : 0; - e.sysExByteB = sysexLoadData.size() > 1 - ? static_cast(sysexLoadData[1]) - : 0; - e.tick = AllEvent::readTick(bytes); - } - - e.trackIndex = TrackIndex(bytes[AllEvent::TRACK_OFFSET]); - - return e; -} - -std::vector AllSysExEvent::mpcEventToBytes(const EventData &e) -{ - std::vector bytes; - - if (e.type == EventType::Mixer) - { - bytes = std::vector(32); - - AllEvent::writeTick(bytes, e.tick); - bytes[AllEvent::TRACK_OFFSET] = e.trackIndex; - bytes[CHUNK_HEADER_ID_OFFSET] = HEADER_ID; - bytes[BYTE_COUNT_OFFSET] = 9; - bytes[DATA_HEADER_ID_OFFSET] = HEADER_ID; - for (int i = 0; i < MIXER_SIGNATURE.size(); i++) - { - bytes[DATA_OFFSET + i] = MIXER_SIGNATURE[i]; - } - - bytes[DATA_OFFSET + MIXER_PAD_OFFSET] = e.mixerPad; - auto paramCandidate = e.mixerParameter; - - if (paramCandidate == 3) - { - paramCandidate = 4; - } - - paramCandidate++; - bytes[DATA_OFFSET + MIXER_PARAMETER_OFFSET] = paramCandidate; - bytes[DATA_OFFSET + MIXER_VALUE_OFFSET] = e.mixerValue; - bytes[DATA_OFFSET + MIXER_VALUE_OFFSET + 1] = DATA_TERMINATOR_ID; - bytes[MIX_TERMINATOR_ID_OFFSET] = CHUNK_TERMINATOR_ID; - } - else if (e.type == EventType::SystemExclusive) - { - constexpr int dataSize = 2; - constexpr int dataSegments = (dataSize + 7) / 8; - bytes = std::vector((dataSegments + 2) * - AllSequence::EVENT_SEG_LENGTH); - AllEvent::writeTick(bytes, e.tick); - bytes[AllEvent::TRACK_OFFSET] = e.trackIndex; - bytes[AllEvent::TRACK_OFFSET + - (dataSegments + 1) * AllSequence::EVENT_SEG_LENGTH] = - e.trackIndex; - bytes[CHUNK_HEADER_ID_OFFSET] = HEADER_ID; - bytes[BYTE_COUNT_OFFSET] = static_cast(dataSize); - - bytes[DATA_OFFSET] = static_cast(e.sysExByteA); - bytes[DATA_OFFSET + 1] = static_cast(e.sysExByteB); - - bytes[static_cast(bytes.size()) - 4] = CHUNK_TERMINATOR_ID; - } - - return bytes; -} diff --git a/src/main/file/all/AllSysExEvent.hpp b/src/main/file/all/AllSysExEvent.hpp deleted file mode 100644 index 88a1630dc..000000000 --- a/src/main/file/all/AllSysExEvent.hpp +++ /dev/null @@ -1,31 +0,0 @@ -#pragma once - -#include "sequencer/EventData.hpp" - -#include -#include - -namespace mpc::file::all -{ - class AllSysExEvent - { - static constexpr int CHUNK_HEADER_ID_OFFSET = 4; - static constexpr int BYTE_COUNT_OFFSET = 5; - static constexpr int DATA_OFFSET = 8; - static constexpr int MIX_TERMINATOR_ID_OFFSET = 28; - static constexpr int DATA_HEADER_ID_OFFSET = 8; - static constexpr uint8_t HEADER_ID = 240; - static constexpr uint8_t DATA_TERMINATOR_ID = 247; - static constexpr uint8_t CHUNK_TERMINATOR_ID = 248; - static constexpr int MIXER_SIGNATURE_OFFSET = 0; - static constexpr int MIXER_PARAMETER_OFFSET = 5; - static constexpr int MIXER_PAD_OFFSET = 6; - static constexpr int MIXER_VALUE_OFFSET = 7; - - static std::vector MIXER_SIGNATURE; - - public: - static sequencer::EventData bytesToMpcEvent(const std::vector &); - static std::vector mpcEventToBytes(const sequencer::EventData &); - }; -} // namespace mpc::file::all diff --git a/src/main/file/all/Bar.cpp b/src/main/file/all/Bar.cpp deleted file mode 100644 index d9aad996d..000000000 --- a/src/main/file/all/Bar.cpp +++ /dev/null @@ -1,66 +0,0 @@ -#include "file/all/Bar.hpp" - -#include "file/ByteUtil.hpp" - -using namespace mpc::file::all; - -Bar::Bar(const std::vector &bytes, Bar *previousBar) -{ - ticksPerBeat = bytes[0] & 255; - auto intVal = ByteUtil::bytes2ushort({bytes[1], bytes[2]}); - lastTick = (bytes[3] & 255) * 65536 + intVal; - barLength = lastTick - (previousBar == nullptr ? 0 : previousBar->lastTick); -} - -Bar::Bar(int ticksPerBeat, int lastTick) -{ - saveBytes = std::vector(4); - saveBytes[0] = static_cast(ticksPerBeat); - auto intVal = lastTick % 65536; - auto bytePair = ByteUtil::ushort2bytes(intVal); - saveBytes[1] = bytePair[0]; - saveBytes[2] = bytePair[1]; - saveBytes[3] = static_cast((lastTick - intVal) / 65536); -} - -int Bar::getTicksPerBeat() const -{ - return ticksPerBeat; -} - -int Bar::getDenominator() const -{ - auto result = 0; - switch (ticksPerBeat) - { - case 96: - result = 4; - break; - case 48: - result = 8; - break; - case 24: - result = 16; - break; - case 12: - result = 32; - break; - } - - return result; -} - -int Bar::getNumerator() const -{ - return barLength / ticksPerBeat; -} - -int Bar::getLastTick() const -{ - return lastTick; -} - -std::vector &Bar::getBytes() -{ - return saveBytes; -} diff --git a/src/main/file/all/Bar.hpp b/src/main/file/all/Bar.hpp deleted file mode 100644 index ead2103e9..000000000 --- a/src/main/file/all/Bar.hpp +++ /dev/null @@ -1,28 +0,0 @@ -#pragma once - -#include - -namespace mpc::file::all -{ - class Bar; - - class Bar - { - - public: - int ticksPerBeat; - int lastTick; - int barLength; - std::vector saveBytes; - - int getTicksPerBeat() const; - int getDenominator() const; - int getNumerator() const; - int getLastTick() const; - - std::vector &getBytes(); - - Bar(const std::vector &bytes, Bar *previousBar); - Bar(int ticksPerBeat, int lastTick); - }; -} // namespace mpc::file::all diff --git a/src/main/file/all/BarList.cpp b/src/main/file/all/BarList.cpp deleted file mode 100644 index 3d37638dd..000000000 --- a/src/main/file/all/BarList.cpp +++ /dev/null @@ -1,77 +0,0 @@ -#include "file/all/BarList.hpp" - -#include "file/all/Bar.hpp" -#include "sequencer/Sequence.hpp" - -#include "Util.hpp" - -using namespace mpc::file::all; - -BarList::BarList(const std::vector &loadBytes) -{ - Bar *previousBar = nullptr; - - for (int i = 0; i < 999; i++) - { - auto bar = new Bar(Util::vecCopyOfRange(loadBytes, i * 4, i * 4 + 4), - previousBar); - - if (bar->lastTick == 0) - { - delete bar; - break; - } - - bars.push_back(bar); - previousBar = bar; - } -} - -BarList::~BarList() -{ - for (auto &b : bars) - { - if (b != nullptr) - { - delete b; - } - } -} - -BarList::BarList(sequencer::Sequence *seq) -{ - saveBytes = std::vector(3996); - auto ticksPerBeat = 0; - auto lastTick = 0; - - for (int i = 0; i < seq->getLastBarIndex() + 1; i++) - { - const auto barLength = seq->getBarLength(i); - lastTick += barLength; - ticksPerBeat = barLength / seq->getNumerator(i); - - Bar bar(ticksPerBeat, lastTick); - - for (auto j = 0; j < 4; j++) - { - saveBytes[i * 4 + j] = bar.getBytes()[j]; - } - } - - Bar bar(ticksPerBeat, 0); - - for (int i = 0; i < 4; i++) - { - saveBytes[(seq->getLastBarIndex() + 1) * 4 + i] = bar.getBytes()[i]; - } -} - -std::vector BarList::getBars() -{ - return bars; -} - -std::vector &BarList::getBytes() -{ - return saveBytes; -} diff --git a/src/main/file/all/BarList.hpp b/src/main/file/all/BarList.hpp deleted file mode 100644 index 877f3c348..000000000 --- a/src/main/file/all/BarList.hpp +++ /dev/null @@ -1,29 +0,0 @@ -#pragma once - -#include - -namespace mpc::sequencer -{ - class Sequence; -} - -namespace mpc::file::all -{ - class Bar; - - class BarList - { - - public: - std::vector bars; - std::vector saveBytes; - - std::vector getBars(); - - std::vector &getBytes(); - BarList(const std::vector &loadBytes); - - BarList(sequencer::Sequence *seq); - ~BarList(); - }; -} // namespace mpc::file::all diff --git a/src/main/file/all/Count.cpp b/src/main/file/all/Count.cpp deleted file mode 100644 index 7ebe0f5c8..000000000 --- a/src/main/file/all/Count.cpp +++ /dev/null @@ -1,141 +0,0 @@ -#include "file/all/Count.hpp" -#include "sequencer/Transport.hpp" - -#include "Mpc.hpp" - -#include "file/all/AllParser.hpp" - -#include "lcdgui/screens/window/CountMetronomeScreen.hpp" -#include "lcdgui/screens/dialog/MetronomeSoundScreen.hpp" -#include "sequencer/Sequencer.hpp" -#include "sequencer/Transport.hpp" - -using namespace mpc::lcdgui; -using namespace mpc::lcdgui::screens::window; -using namespace mpc::lcdgui::screens::dialog; -using namespace mpc::file::all; - -Count::Count(const std::vector &b) -{ - enabled = b[ENABLED_OFFSET] > 0; - countInMode = b[COUNT_IN_MODE_OFFSET]; - clickVolume = b[CLICK_VOLUME_OFFSET]; - rate = b[RATE_OFFSET]; - enabledInPlay = b[ENABLED_IN_PLAY_OFFSET] > 0; - enabledInRec = b[ENABLED_IN_REC_OFFSET] > 0; - clickOutput = b[CLICK_OUTPUT_OFFSET]; - waitForKeyEnabled = b[WAIT_FOR_KEY_ENABLED_OFFSET] > 0; - sound = b[SOUND_OFFSET]; - accentPad = b[ACCENT_PAD_OFFSET]; - normalPad = b[NORMAL_PAD_OFFSET]; - accentVelo = b[ACCENT_VELO_OFFSET]; - normalVelo = b[NORMAL_VELO_OFFSET]; -} - -Count::Count(Mpc &mpc) -{ - auto countMetronomeScreen = - mpc.screens->get(); - auto metronomeSoundScreen = - mpc.screens->get(); - - auto lSequencer = mpc.getSequencer(); - - saveBytes = std::vector(AllParser::COUNT_LENGTH); - saveBytes[ENABLED_OFFSET] = static_cast( - lSequencer->getTransport()->isCountEnabled() ? 1 : 0); - saveBytes[COUNT_IN_MODE_OFFSET] = - static_cast(countMetronomeScreen->getCountInMode()); - saveBytes[CLICK_VOLUME_OFFSET] = - static_cast(metronomeSoundScreen->getVolume()); - saveBytes[RATE_OFFSET] = - static_cast(countMetronomeScreen->getRate()); - saveBytes[ENABLED_IN_PLAY_OFFSET] = - static_cast(countMetronomeScreen->getInPlay() ? 1 : 0); - saveBytes[ENABLED_IN_REC_OFFSET] = - static_cast(countMetronomeScreen->getInRec() ? 1 : 0); - saveBytes[CLICK_OUTPUT_OFFSET] = - static_cast(metronomeSoundScreen->getOutput()); - saveBytes[WAIT_FOR_KEY_ENABLED_OFFSET] = static_cast( - countMetronomeScreen->isWaitForKeyEnabled() ? 1 : 0); - saveBytes[SOUND_OFFSET] = - static_cast(metronomeSoundScreen->getSound()); - saveBytes[ACCENT_PAD_OFFSET] = - static_cast(metronomeSoundScreen->getAccentPad()); - saveBytes[NORMAL_PAD_OFFSET] = - static_cast(metronomeSoundScreen->getNormalPad()); - saveBytes[ACCENT_VELO_OFFSET] = - static_cast(metronomeSoundScreen->getAccentVelo()); - saveBytes[NORMAL_VELO_OFFSET] = - static_cast(metronomeSoundScreen->getNormalVelo()); -} - -bool Count::isEnabled() const -{ - return enabled; -} - -int Count::getCountInMode() const -{ - return countInMode; -} - -int Count::getClickVolume() const -{ - return clickVolume; -} - -int Count::getRate() const -{ - return rate; -} - -bool Count::isEnabledInPlay() const -{ - return enabledInPlay; -} - -bool Count::isEnabledInRec() const -{ - return enabledInRec; -} - -int Count::getClickOutput() const -{ - return clickOutput; -} - -bool Count::isWaitForKeyEnabled() const -{ - return waitForKeyEnabled; -} - -int Count::getSound() const -{ - return sound; -} - -int Count::getAccentPad() const -{ - return accentPad; -} - -int Count::getNormalPad() const -{ - return normalPad; -} - -int Count::getAccentVelo() const -{ - return accentVelo; -} - -int Count::getNormalVelo() const -{ - return normalVelo; -} - -std::vector &Count::getBytes() -{ - return saveBytes; -} diff --git a/src/main/file/all/Count.hpp b/src/main/file/all/Count.hpp deleted file mode 100644 index 8b402ba8b..000000000 --- a/src/main/file/all/Count.hpp +++ /dev/null @@ -1,64 +0,0 @@ -#pragma once - -#include - -namespace mpc -{ - class Mpc; -} - -namespace mpc::file::all -{ - - class Count - { - - public: - static const int ENABLED_OFFSET = 0; - static const int COUNT_IN_MODE_OFFSET = 1; - static const int CLICK_VOLUME_OFFSET = 2; - static const int RATE_OFFSET = 3; - static const int ENABLED_IN_PLAY_OFFSET = 4; - static const int ENABLED_IN_REC_OFFSET = 5; - static const int CLICK_OUTPUT_OFFSET = 6; - static const int WAIT_FOR_KEY_ENABLED_OFFSET = 7; - static const int SOUND_OFFSET = 8; - static const int ACCENT_PAD_OFFSET = 9; - static const int NORMAL_PAD_OFFSET = 10; - static const int ACCENT_VELO_OFFSET = 11; - static const int NORMAL_VELO_OFFSET = 12; - bool enabled; - int countInMode; - int clickVolume; - int rate; - bool enabledInPlay; - bool enabledInRec; - int clickOutput; - bool waitForKeyEnabled; - int sound; - int accentPad; - int normalPad; - int accentVelo; - int normalVelo; - std::vector saveBytes; - - bool isEnabled() const; - int getCountInMode() const; - int getClickVolume() const; - int getRate() const; - bool isEnabledInPlay() const; - bool isEnabledInRec() const; - int getClickOutput() const; - bool isWaitForKeyEnabled() const; - int getSound() const; - int getAccentPad() const; - int getNormalPad() const; - int getAccentVelo() const; - int getNormalVelo() const; - - std::vector &getBytes(); - - Count(const std::vector &b); - Count(Mpc &mpc); - }; -} // namespace mpc::file::all diff --git a/src/main/file/all/Defaults.cpp b/src/main/file/all/Defaults.cpp index 716ac47cc..ed37306f2 100644 --- a/src/main/file/all/Defaults.cpp +++ b/src/main/file/all/Defaults.cpp @@ -2,7 +2,6 @@ #include "Util.hpp" #include "Mpc.hpp" -#include "file/all/AllParser.hpp" #include "lcdgui/screens/UserScreen.hpp" @@ -124,7 +123,7 @@ Defaults::Defaults(Mpc &mpc, const std::vector &loadBytes) : mpc(mpc) Defaults::Defaults(Mpc &mpc) : mpc(mpc) { - saveBytes = std::vector(AllParser::DEFAULTS_LENGTH); + saveBytes = std::vector(TOTAL_LENGTH); setNames(); @@ -174,7 +173,7 @@ void Defaults::parseNames(const std::vector &loadBytes) { std::vector stringBuffer = Util::vecCopyOfRange(loadBytes, DEF_SEQ_NAME_OFFSET, - DEF_SEQ_NAME_OFFSET + AllParser::NAME_LENGTH); + DEF_SEQ_NAME_OFFSET + NAME_LENGTH); defaultSeqName = ""; @@ -192,9 +191,9 @@ void Defaults::parseNames(const std::vector &loadBytes) for (int i = 0; i < 33; i++) { - offset = DEV_NAMES_OFFSET + i * AllParser::DEV_NAME_LENGTH; + offset = DEV_NAMES_OFFSET + i * DEV_NAME_LENGTH; stringBuffer = Util::vecCopyOfRange( - loadBytes, offset, offset + AllParser::DEV_NAME_LENGTH); + loadBytes, offset, offset + DEV_NAME_LENGTH); std::string s; for (const char c : stringBuffer) @@ -212,9 +211,9 @@ void Defaults::parseNames(const std::vector &loadBytes) for (int i = 0; i < 64; i++) { - offset = TR_NAMES_OFFSET + i * AllParser::NAME_LENGTH; + offset = TR_NAMES_OFFSET + i * NAME_LENGTH; stringBuffer = Util::vecCopyOfRange(loadBytes, offset, - offset + AllParser::NAME_LENGTH); + offset + NAME_LENGTH); std::string s; for (const char c : stringBuffer) @@ -329,7 +328,7 @@ void Defaults::setNames() { auto const defSeqName = StrUtil::padRight(mpc.getSequencer()->getDefaultSequenceName(), " ", - AllParser::NAME_LENGTH); + NAME_LENGTH); for (int i = 0; i < 16; i++) { @@ -342,9 +341,9 @@ void Defaults::setNames() i == 0 ? " " : "Device" + StrUtil::padLeft(std::to_string(i), "0", 2); - const auto offset = DEV_NAMES_OFFSET + i * AllParser::DEV_NAME_LENGTH; + const auto offset = DEV_NAMES_OFFSET + i * DEV_NAME_LENGTH; - for (int j = offset; j < offset + AllParser::DEV_NAME_LENGTH; j++) + for (int j = offset; j < offset + DEV_NAME_LENGTH; j++) { saveBytes[j] = deviceName[j - offset]; } @@ -354,11 +353,11 @@ void Defaults::setNames() { const auto trackName = StrUtil::padRight(mpc.getSequencer()->getDefaultTrackName(i), " ", - AllParser::NAME_LENGTH); + NAME_LENGTH); - const auto offset = TR_NAMES_OFFSET + i * AllParser::NAME_LENGTH; + const auto offset = TR_NAMES_OFFSET + i * NAME_LENGTH; - for (int j = offset; j < offset + AllParser::NAME_LENGTH; j++) + for (int j = offset; j < offset + NAME_LENGTH; j++) { saveBytes[j] = trackName[j - offset]; } diff --git a/src/main/file/all/Defaults.hpp b/src/main/file/all/Defaults.hpp index 1d7f1060e..207cdb1f7 100644 --- a/src/main/file/all/Defaults.hpp +++ b/src/main/file/all/Defaults.hpp @@ -47,6 +47,9 @@ namespace mpc::file::all static const int TR_VELOS_LENGTH{64}; static const int TR_STATUS_OFFSET{1664}; static const int TR_STATUS_LENGTH{64}; + static const int TOTAL_LENGTH{1728}; + static const int NAME_LENGTH{16}; + static const int DEV_NAME_LENGTH{8}; std::string defaultSeqName; int tempo; diff --git a/src/main/file/all/Header.cpp b/src/main/file/all/Header.cpp deleted file mode 100644 index 3927e204c..000000000 --- a/src/main/file/all/Header.cpp +++ /dev/null @@ -1,50 +0,0 @@ -#include "file/all/Header.hpp" - -#include "Util.hpp" - -#include - -using namespace mpc::file::all; - -Header::Header(const std::vector &_loadBytes) : loadBytes(_loadBytes) {} - -Header::Header() -{ - const std::string s = "MPC2KXL ALL 1.00"; - saveBytes = std::vector(16); - for (int i = 0; i < 16; i++) - { - saveBytes[i] = s[i]; - } -} - -std::vector &Header::getHeaderArray() -{ - return loadBytes; -} - -bool Header::verifyFileID() const -{ - auto verifyFileID = false; - auto checkFileID = Util::vecCopyOfRange(loadBytes, 0, 16); - std::string fileIDString; - for (char c : checkFileID) - { - if (c == 0x00) - { - break; - } - fileIDString.push_back(c); - } - std::string shouldBe = "MPC2KXL ALL 1.00"; - if (fileIDString == shouldBe) - { - verifyFileID = true; - } - return verifyFileID; -} - -std::vector &Header::getBytes() -{ - return saveBytes; -} diff --git a/src/main/file/all/Header.hpp b/src/main/file/all/Header.hpp deleted file mode 100644 index 68845fb3b..000000000 --- a/src/main/file/all/Header.hpp +++ /dev/null @@ -1,21 +0,0 @@ -#pragma once - -#include - -namespace mpc::file::all -{ - class Header - { - public: - std::vector loadBytes; - std::vector saveBytes; - - std::vector &getHeaderArray(); - bool verifyFileID() const; - - std::vector &getBytes(); - - Header(const std::vector &loadBytes); - Header(); - }; -} // namespace mpc::file::all diff --git a/src/main/file/all/MidiInput.cpp b/src/main/file/all/MidiInput.cpp deleted file mode 100644 index f55448e66..000000000 --- a/src/main/file/all/MidiInput.cpp +++ /dev/null @@ -1,180 +0,0 @@ -#include "MidiInput.hpp" - -#include "Mpc.hpp" - -#include "file/BitUtil.hpp" - -#include "lcdgui/screens/window/MultiRecordingSetupScreen.hpp" -#include "lcdgui/screens/window/MidiInputScreen.hpp" -#include "lcdgui/screens/window/MidiOutputScreen.hpp" -#include "sequencer/Sequencer.hpp" - -using namespace mpc::lcdgui; -using namespace mpc::lcdgui::screens::window; -using namespace mpc::file::all; - -MidiInput::MidiInput(const std::vector &b) -{ - softThruMode = b[SOFT_THRU_MODE_OFFSET]; - receiveCh = b[RECEIVE_CH_OFFSET]; - sustainPedalToDuration = b[SUSTAIN_PEDAL_TO_DURATION_OFFSET] > 0; - filterEnabled = b[FILTER_ENABLED_OFFSET] > 0; - filterType = b[FILTER_TYPE_OFFSET]; - multiRecEnabled = b[MULTI_REC_ENABLED_OFFSET] > 0; - - for (int i = 0; i < MULTI_REC_TRACK_DESTS_LENGTH; i++) - { - multiRecTrackDests[i] = b[MULTI_REC_TRACK_DESTS_OFFSET + i] - 1; - } - - notePassEnabled = b[NOTE_PASS_ENABLED_OFFSET] > 0; - pitchBendPassEnabled = b[PITCH_BEND_PASS_ENABLED_OFFSET] > 0; - pgmChangePassEnabled = b[PGM_CHANGE_PASS_ENABLED_OFFSET] > 0; - chPressurePassEnabled = b[CH_PRESSURE_PASS_ENABLED_OFFSET] > 0; - polyPressurePassEnabled = b[POLY_PRESSURE_PASS_ENABLED_OFFSET] > 0; - exclusivePassEnabled = b[EXCLUSIVE_PASS_ENABLED_OFFSET] > 0; - - int ccPassEnabledCounter = 0; - - for (int i = 0; i < 16; i++) - { - const auto currentCcPassEnabled = b[CC_PASS_ENABLED_OFFSET + i]; - for (int j = 0; j < 8; j++) - { - ccPassEnabled[ccPassEnabledCounter++] = - BitUtil::isBitOn(currentCcPassEnabled, j); - } - } -} - -MidiInput::MidiInput(Mpc &mpc) -{ - saveBytes = std::vector(LENGTH); - - auto midiInputScreen = mpc.screens->get(); - - saveBytes[SOFT_THRU_MODE_OFFSET] = - mpc.screens->get()->getSoftThru(); - saveBytes[RECEIVE_CH_OFFSET] = - static_cast(midiInputScreen->getReceiveCh() + 1); - saveBytes[SUSTAIN_PEDAL_TO_DURATION_OFFSET] = static_cast( - midiInputScreen->isSustainPedalToDurationEnabled() ? 1 : 0); - saveBytes[FILTER_ENABLED_OFFSET] = - static_cast(midiInputScreen->isMidiFilterEnabled() ? 1 : 0); - saveBytes[FILTER_TYPE_OFFSET] = - static_cast(midiInputScreen->getType()); - saveBytes[MULTI_REC_ENABLED_OFFSET] = - static_cast(mpc.getSequencer()->isRecordingModeMulti() ? 1 : 0); - - auto screen = mpc.screens->get(); - - for (int i = 0; i < MULTI_REC_TRACK_DESTS_LENGTH; i++) - { - saveBytes[MULTI_REC_TRACK_DESTS_OFFSET + i] = - static_cast(screen->getMrsLines()[i]->getTrack() + 1); - } - - saveBytes[NOTE_PASS_ENABLED_OFFSET] = - static_cast(midiInputScreen->isNotePassEnabled() ? 1 : 0); - saveBytes[PITCH_BEND_PASS_ENABLED_OFFSET] = - static_cast(midiInputScreen->isPitchBendPassEnabled() ? 1 : 0); - saveBytes[PGM_CHANGE_PASS_ENABLED_OFFSET] = - static_cast(midiInputScreen->isPgmChangePassEnabled() ? 1 : 0); - saveBytes[CH_PRESSURE_PASS_ENABLED_OFFSET] = - static_cast(midiInputScreen->isChPressurePassEnabled() ? 1 : 0); - saveBytes[POLY_PRESSURE_PASS_ENABLED_OFFSET] = static_cast( - midiInputScreen->isPolyPressurePassEnabled() ? 1 : 0); - saveBytes[EXCLUSIVE_PASS_ENABLED_OFFSET] = - static_cast(midiInputScreen->isExclusivePassEnabled() ? 1 : 0); - - for (int i = 0; i < 16; i++) - { - char currentCcPassByte = 0x00; - - for (int j = 0; j < 8; j++) - { - const bool currentCcPassEnabled = - midiInputScreen->getCcPassEnabled()[i * 8 + j]; - - currentCcPassByte = - BitUtil::setBit(currentCcPassByte, j, currentCcPassEnabled); - } - - saveBytes[CC_PASS_ENABLED_OFFSET + i] = currentCcPassByte; - } -} - -int MidiInput::getSoftThruMode() const -{ - return softThruMode; -} - -int MidiInput::getReceiveCh() const -{ - return receiveCh; -} - -bool MidiInput::isSustainPedalToDurationEnabled() const -{ - return sustainPedalToDuration; -} - -bool MidiInput::isFilterEnabled() const -{ - return filterEnabled; -} - -int MidiInput::getFilterType() const -{ - return filterType; -} - -bool MidiInput::isMultiRecEnabled() const -{ - return multiRecEnabled; -} - -std::vector MidiInput::getMultiRecTrackDests() -{ - return multiRecTrackDests; -} - -bool MidiInput::isNotePassEnabled() const -{ - return notePassEnabled; -} - -bool MidiInput::isPitchBendPassEnabled() const -{ - return pitchBendPassEnabled; -} - -bool MidiInput::isPgmChangePassEnabled() const -{ - return pgmChangePassEnabled; -} - -bool MidiInput::isChPressurePassEnabled() const -{ - return chPressurePassEnabled; -} - -bool MidiInput::isPolyPressurePassEnabled() const -{ - return polyPressurePassEnabled; -} - -bool MidiInput::isExclusivePassEnabled() const -{ - return exclusivePassEnabled; -} - -std::vector MidiInput::getCcPassEnabled() -{ - return ccPassEnabled; -} - -std::vector &MidiInput::getBytes() -{ - return saveBytes; -} diff --git a/src/main/file/all/MidiInput.hpp b/src/main/file/all/MidiInput.hpp deleted file mode 100644 index bd1984645..000000000 --- a/src/main/file/all/MidiInput.hpp +++ /dev/null @@ -1,70 +0,0 @@ -#pragma once - -#include - -namespace mpc -{ - class Mpc; -} - -namespace mpc::file::all -{ - - class MidiInput - { - - public: - static const int LENGTH{62}; - static const int SOFT_THRU_MODE_OFFSET = 0; - static const int RECEIVE_CH_OFFSET{1}; - static const int SUSTAIN_PEDAL_TO_DURATION_OFFSET{2}; - static const int FILTER_ENABLED_OFFSET{3}; - static const int FILTER_TYPE_OFFSET{4}; - static const int MULTI_REC_ENABLED_OFFSET{5}; - static const int MULTI_REC_TRACK_DESTS_OFFSET{6}; - static const int MULTI_REC_TRACK_DESTS_LENGTH{32}; - static const int NOTE_PASS_ENABLED_OFFSET{40}; - static const int PITCH_BEND_PASS_ENABLED_OFFSET{41}; - static const int PGM_CHANGE_PASS_ENABLED_OFFSET{42}; - static const int CH_PRESSURE_PASS_ENABLED_OFFSET{43}; - static const int POLY_PRESSURE_PASS_ENABLED_OFFSET{44}; - static const int EXCLUSIVE_PASS_ENABLED_OFFSET{45}; - static const int CC_PASS_ENABLED_OFFSET{46}; - - int softThruMode; - int receiveCh; - bool sustainPedalToDuration; - bool filterEnabled; - int filterType; - bool multiRecEnabled; - std::vector multiRecTrackDests = std::vector(34); - bool notePassEnabled; - bool pitchBendPassEnabled; - bool pgmChangePassEnabled; - bool chPressurePassEnabled; - bool polyPressurePassEnabled; - bool exclusivePassEnabled; - std::vector ccPassEnabled = std::vector(128); - std::vector saveBytes; - - int getSoftThruMode() const; - int getReceiveCh() const; - bool isSustainPedalToDurationEnabled() const; - bool isFilterEnabled() const; - int getFilterType() const; - bool isMultiRecEnabled() const; - std::vector getMultiRecTrackDests(); - bool isNotePassEnabled() const; - bool isPitchBendPassEnabled() const; - bool isPgmChangePassEnabled() const; - bool isChPressurePassEnabled() const; - bool isPolyPressurePassEnabled() const; - bool isExclusivePassEnabled() const; - std::vector getCcPassEnabled(); - - std::vector &getBytes(); - - MidiInput(const std::vector &b); - MidiInput(Mpc &mpc); - }; -} // namespace mpc::file::all diff --git a/src/main/file/all/MidiSyncMisc.cpp b/src/main/file/all/MidiSyncMisc.cpp deleted file mode 100644 index b34ffa8c1..000000000 --- a/src/main/file/all/MidiSyncMisc.cpp +++ /dev/null @@ -1,113 +0,0 @@ -#include "file/all/MidiSyncMisc.hpp" - -#include "Mpc.hpp" -#include "file/all/AllParser.hpp" - -#include "StrUtil.hpp" -#include "Util.hpp" - -#include "lcdgui/screens/SongScreen.hpp" -#include "lcdgui/screens/SyncScreen.hpp" -#include "lcdgui/screens/window/IgnoreTempoChangeScreen.hpp" - -using namespace mpc::lcdgui::screens; -using namespace mpc::lcdgui::screens::window; -using namespace mpc::lcdgui; -using namespace mpc::file::all; - -MidiSyncMisc::MidiSyncMisc(const std::vector &b) -{ - inMode = b[IN_MODE_OFFSET]; - outMode = b[OUT_MODE_OFFSET]; - shiftEarly = b[SHIFT_EARLY_OFFSET]; - sendMMCEnabled = b[SEND_MMC_OFFSET] > 0; - frameRate = b[FRAME_RATE_OFFSET]; - input = b[INPUT_OFFSET]; - output = b[OUTPUT_OFFSET]; - - auto stringBuffer = Util::vecCopyOfRange( - b, DEF_SONG_NAME_OFFSET, DEF_SONG_NAME_OFFSET + AllParser::NAME_LENGTH); - defSongName = std::string(stringBuffer.begin(), stringBuffer.end()); - - songModeIgnoreTempoChangeEventsInSequence = - b[SONG_MODE_IGNORE_TEMPO_CHANGE_EVENTS_IN_SEQUENCE_OFFSET] == 0x01; -} - -MidiSyncMisc::MidiSyncMisc(Mpc &mpc) -{ - saveBytes = std::vector(LENGTH); - - auto syncScreen = mpc.screens->get(); - - saveBytes[IN_MODE_OFFSET] = static_cast(syncScreen->getModeIn()); - saveBytes[OUT_MODE_OFFSET] = static_cast(syncScreen->getModeOut()); - saveBytes[SHIFT_EARLY_OFFSET] = static_cast(syncScreen->shiftEarly); - saveBytes[SEND_MMC_OFFSET] = - static_cast(syncScreen->sendMMCEnabled ? 1 : 0); - saveBytes[FRAME_RATE_OFFSET] = static_cast(syncScreen->frameRate); - saveBytes[INPUT_OFFSET] = static_cast(syncScreen->in); - saveBytes[OUTPUT_OFFSET] = static_cast(syncScreen->out); - - auto songScreen = mpc.screens->get(); - - for (int i = 0; i < AllParser::NAME_LENGTH; i++) - { - saveBytes[DEF_SONG_NAME_OFFSET + i] = - StrUtil::padRight(songScreen->getDefaultSongName(), " ", 16)[i]; - } - - saveBytes[DEF_SONG_NAME_OFFSET + 16] = 1; - - saveBytes[SONG_MODE_IGNORE_TEMPO_CHANGE_EVENTS_IN_SEQUENCE_OFFSET] = - mpc.screens->get()->getIgnore(); -} - -int MidiSyncMisc::getInMode() const -{ - return inMode; -} - -int MidiSyncMisc::getOutMode() const -{ - return outMode; -} - -int MidiSyncMisc::getShiftEarly() const -{ - return shiftEarly; -} - -bool MidiSyncMisc::isSendMMCEnabled() const -{ - return sendMMCEnabled; -} - -int MidiSyncMisc::getFrameRate() const -{ - return frameRate; -} - -int MidiSyncMisc::getInput() const -{ - return input; -} - -int MidiSyncMisc::getOutput() const -{ - return output; -} - -std::string MidiSyncMisc::getDefSongName() -{ - return defSongName; -} - -bool MidiSyncMisc::getSongModeIgnoreTempoChangeEvents() const -{ - return songModeIgnoreTempoChangeEventsInSequence; -} - -std::vector &MidiSyncMisc::getBytes() -{ - return saveBytes; -} diff --git a/src/main/file/all/MidiSyncMisc.hpp b/src/main/file/all/MidiSyncMisc.hpp deleted file mode 100644 index ad5a5a9ed..000000000 --- a/src/main/file/all/MidiSyncMisc.hpp +++ /dev/null @@ -1,59 +0,0 @@ -#pragma once - -#include -#include - -namespace mpc -{ - class Mpc; -} - -namespace mpc::file::all -{ - class MidiSyncMisc - { - - public: - static const int LENGTH = 24; - - private: - static const int IN_MODE_OFFSET = 0; - static const int OUT_MODE_OFFSET = 1; - static const int SHIFT_EARLY_OFFSET = 2; - static const int SEND_MMC_OFFSET = 3; - static const int FRAME_RATE_OFFSET = 4; - static const int INPUT_OFFSET = 5; - static const int OUTPUT_OFFSET = 6; - static const int DEF_SONG_NAME_OFFSET = 7; - static const int - SONG_MODE_IGNORE_TEMPO_CHANGE_EVENTS_IN_SEQUENCE_OFFSET = 23; - - public: - int inMode; - int outMode; - int shiftEarly; - bool sendMMCEnabled; - int frameRate; - int input; - int output; - std::string defSongName{""}; - bool songModeIgnoreTempoChangeEventsInSequence; - - std::vector saveBytes; - - int getInMode() const; - int getOutMode() const; - int getShiftEarly() const; - bool isSendMMCEnabled() const; - int getFrameRate() const; - int getInput() const; - int getOutput() const; - std::string getDefSongName(); - bool getSongModeIgnoreTempoChangeEvents() const; - - std::vector &getBytes(); - - MidiSyncMisc(const std::vector &b); - MidiSyncMisc(Mpc &mpc); - }; -} // namespace mpc::file::all diff --git a/src/main/file/all/Misc.cpp b/src/main/file/all/Misc.cpp deleted file mode 100644 index ebf752393..000000000 --- a/src/main/file/all/Misc.cpp +++ /dev/null @@ -1,183 +0,0 @@ -#include "file/all/Misc.hpp" - -#include "Mpc.hpp" - -#include "controller/ClientEventController.hpp" -#include "controller/ClientMidiEventController.hpp" -#include "controller/MidiFootswitchFunctionMap.hpp" -#include "lcdgui/screens/window/StepEditOptionsScreen.hpp" -#include "lcdgui/screens/window/MidiInputScreen.hpp" -#include "lcdgui/screens/OthersScreen.hpp" -#include "lcdgui/screens/SyncScreen.hpp" -#include "lcdgui/screens/MidiSwScreen.hpp" - -#include "file/ByteUtil.hpp" - -using namespace mpc::lcdgui::screens; -using namespace mpc::lcdgui::screens::window; -using namespace mpc::lcdgui; -using namespace mpc::file::all; - -Misc::Misc(const std::vector &b) -{ - for (int i = 0; i < 9; i++) - { - const auto locationOffset = LOCATIONS_OFFSET + i * 4; - const uint16_t locationBarIndex = - ByteUtil::bytes2ushort({b[locationOffset], b[locationOffset + 1]}); - const uint8_t locationBeatIndex = b[locationOffset + 2]; - const uint8_t locationClock = b[locationOffset + 3]; - locations[i] = {locationBarIndex, locationBeatIndex, locationClock}; - } - - tapAvg = b[TAP_AVG_OFFSET]; - inReceiveMMCEnabled = b[MIDI_SYNC_IN_RECEIVE_MMC_OFFSET] > 0; - - for (int i = 0; i < 4; i++) - { - auto ctrl = static_cast(b[MIDI_SWITCH_OFFSET + i * 2]); - auto func = b[MIDI_SWITCH_OFFSET + i * 2 + 1]; - switches[i] = std::pair(ctrl == 0xFF ? -1 : ctrl, func); - } - - autoStepInc = b[AUTO_STEP_INCREMENT_OFFSET] > 0; - durationOfRecNotesTcEnabled = b[DURATION_OF_REC_NOTES_OFFSET] > 0; - durationTcPercentage = b[DURATION_TC_PERCENTAGE_OFFSET]; - pgmChToSeqEnabled = b[MIDI_PGM_CHANGE_TO_SEQ_OFFSET] > 0; -} - -Misc::Misc(Mpc &mpc) -{ - saveBytes = std::vector(LENGTH); - - auto &locationsToPersist = - mpc.screens->get()->getLocations(); - - assert(locationsToPersist.size() == 9); - - for (int i = 0; i < 9; i++) - { - const auto locationBarBytes = - ByteUtil::ushort2bytes(std::get<0>(locationsToPersist[i])); - const auto locationBeatByte = std::get<1>(locationsToPersist[i]); - const auto locationClockByte = std::get<2>(locationsToPersist[i]); - const auto locationOffset = LOCATIONS_OFFSET + i * 4; - saveBytes[locationOffset] = locationBarBytes[0]; - saveBytes[locationOffset + 1] = locationBarBytes[1]; - saveBytes[locationOffset + 2] = locationBeatByte; - saveBytes[locationOffset + 3] = locationClockByte; - } - - auto stepEditOptionsScreen = - mpc.screens->get(); - auto othersScreen = mpc.screens->get(); - - saveBytes[TAP_AVG_OFFSET] = (char)(othersScreen->getTapAveraging() - 2); - - auto syncScreen = mpc.screens->get(); - - saveBytes[MIDI_SYNC_IN_RECEIVE_MMC_OFFSET] = - (char)(syncScreen->receiveMMCEnabled ? 1 : 0); - - auto midiSwScreen = mpc.screens->get(); - - auto &footswitchBindings = - mpc.clientEventController->getClientMidiEventController() - ->getFootswitchAssignmentController() - ->bindings; - - for (int i = 0; - i < controller::ClientMidiFootswitchAssignmentController::SWITCH_COUNT; - i++) - { - int cc = -1; - int funcIndex = 0; - - std::visit( - [&](auto &b) - { - cc = b.number; - - if constexpr (std::is_same_v, - input::midi::HardwareBinding>) - { - if (auto fn = controller::componentIdToFootswitch( - b.target.componentId)) - { - funcIndex = static_cast(*fn); - } - } - else if constexpr (std::is_same_v< - std::decay_t, - input::midi::SequencerBinding>) - { - if (auto fn = controller::sequencerCmdToFootswitch( - b.target.command)) - { - funcIndex = static_cast(*fn); - } - } - }, - footswitchBindings[i]); - - saveBytes[MIDI_SWITCH_OFFSET + i * 2] = - cc == -1 ? (char)0xFF : (char)cc; - saveBytes[MIDI_SWITCH_OFFSET + i * 2 + 1] = (char)funcIndex; - } - - saveBytes[AUTO_STEP_INCREMENT_OFFSET] = - (char)(stepEditOptionsScreen->isAutoStepIncrementEnabled() ? 1 : 0); - saveBytes[DURATION_OF_REC_NOTES_OFFSET] = - (char)(stepEditOptionsScreen->isDurationOfRecordedNotesTcValue() ? 1 - : 0); - saveBytes[DURATION_TC_PERCENTAGE_OFFSET] = - (char)stepEditOptionsScreen->getTcValuePercentage(); - const auto midiInputScreen = mpc.screens->get(); - saveBytes[MIDI_PGM_CHANGE_TO_SEQ_OFFSET] = - midiInputScreen->getProgChangeSeq() ? 0x01 : 0x00; -} - -int Misc::getTapAvg() const -{ - return tapAvg; -} - -bool Misc::isInReceiveMMCEnabled() const -{ - return inReceiveMMCEnabled; -} - -bool Misc::isAutoStepIncEnabled() const -{ - return autoStepInc; -} - -bool Misc::isDurationOfRecNotesTc() const -{ - return durationOfRecNotesTcEnabled; -} - -int Misc::getDurationTcPercentage() const -{ - return durationTcPercentage; -} - -bool Misc::isPgmChToSeqEnabled() const -{ - return pgmChToSeqEnabled; -} - -std::vector> Misc::getSwitches() -{ - return switches; -} - -std::vector &Misc::getLocations() -{ - return locations; -} - -std::vector &Misc::getBytes() -{ - return saveBytes; -} diff --git a/src/main/file/all/Misc.hpp b/src/main/file/all/Misc.hpp deleted file mode 100644 index 088b26875..000000000 --- a/src/main/file/all/Misc.hpp +++ /dev/null @@ -1,56 +0,0 @@ -#pragma once - -#include -#include - -#include "lcdgui/screens/window/LocateScreen.hpp" - -namespace mpc -{ - class Mpc; -} - -namespace mpc::file::all -{ - class Misc - { - public: - static const int LENGTH = 131; - - std::vector> getSwitches(); - int getTapAvg() const; - bool isInReceiveMMCEnabled() const; - bool isAutoStepIncEnabled() const; - bool isDurationOfRecNotesTc() const; - int getDurationTcPercentage() const; - bool isPgmChToSeqEnabled() const; - std::vector &getLocations(); - - std::vector &getBytes(); - - Misc(const std::vector &b); - Misc(Mpc &); - - private: - static const int LOCATIONS_OFFSET = 0; - static const int TAP_AVG_OFFSET = 36; - static const int MIDI_SYNC_IN_RECEIVE_MMC_OFFSET = 37; - static const int MIDI_SWITCH_OFFSET = 38; - static const int AUTO_STEP_INCREMENT_OFFSET = 49; - static const int DURATION_OF_REC_NOTES_OFFSET = 50; - static const int DURATION_TC_PERCENTAGE_OFFSET = 51; - static const int MIDI_PGM_CHANGE_TO_SEQ_OFFSET = 52; - - int tapAvg; - bool inReceiveMMCEnabled; - bool autoStepInc; - bool durationOfRecNotesTcEnabled; - int durationTcPercentage; - bool pgmChToSeqEnabled; - std::vector saveBytes; - std::vector> switches = - std::vector>(4); - std::vector locations = - std::vector(9, {0, 0, 0}); - }; -} // namespace mpc::file::all diff --git a/src/main/file/all/SequenceNames.cpp b/src/main/file/all/SequenceNames.cpp deleted file mode 100644 index 0ae363da6..000000000 --- a/src/main/file/all/SequenceNames.cpp +++ /dev/null @@ -1,130 +0,0 @@ -#include "file/all/SequenceNames.hpp" - -#include "Mpc.hpp" -#include "file/all/AllParser.hpp" -#include "sequencer/MixerEvent.hpp" -#include "sequencer/Sequence.hpp" -#include "sequencer/Track.hpp" -#include "sequencer/Sequencer.hpp" -#include "sequencer/SystemExclusiveEvent.hpp" - -#include "file/ByteUtil.hpp" -#include "StrUtil.hpp" -#include "Util.hpp" - -using namespace mpc::file::all; - -SequenceNames::SequenceNames(const std::vector &b) -{ - for (int i = 0; i < names.size(); i++) - { - int offset = i * ENTRY_LENGTH; - std::string stringBuffer = ""; - auto nameBytes = - Util::vecCopyOfRange(b, offset, offset + AllParser::NAME_LENGTH); - - for (char c : nameBytes) - { - if (c == 0x00) - { - break; - } - stringBuffer.push_back(c); - } - names[i] = stringBuffer; - - auto usednessBytes = - Util::vecCopyOfRange(b, offset + AllParser::NAME_LENGTH, - offset + AllParser::NAME_LENGTH + 2); - usednesses[i] = usednessBytes[0] != 0 || usednessBytes[1] != 0; - } -} - -SequenceNames::SequenceNames(Mpc &mpc) -{ - saveBytes = std::vector(LENGTH); - auto sequencer = mpc.getSequencer(); - - for (int i = 0; i < 99; i++) - { - auto seq = sequencer->getSequence(i); - auto name = seq->getName(); - auto offset = i * ENTRY_LENGTH; - - for (auto j = 0; j < AllParser::NAME_LENGTH; j++) - { - saveBytes[offset + j] = StrUtil::padRight(name, " ", 16)[j]; - } - - if (name.find("(Unused)") == std::string::npos) - { - auto eventSegmentCount = getSegmentCount(seq.get()); - - if ((eventSegmentCount & 1) != 0) - { - eventSegmentCount--; - } - - auto lastEventIndex = 641 + eventSegmentCount / 2; - - if (lastEventIndex < 641) - { - lastEventIndex = 641; - } - - auto eventCountBytes = ByteUtil::ushort2bytes(lastEventIndex); - saveBytes[offset + LAST_EVENT_INDEX_OFFSET] = eventCountBytes[0]; - saveBytes[offset + LAST_EVENT_INDEX_OFFSET + 1] = - eventCountBytes[1]; - } - } -} - -std::vector &SequenceNames::getNames() -{ - return names; -} - -std::vector &SequenceNames::getUsednesses() -{ - return usednesses; -} - -std::vector &SequenceNames::getBytes() -{ - return saveBytes; -} - -int SequenceNames::getSegmentCount(sequencer::Sequence *seq) -{ - auto segmentCount = 0; - for (auto &track : seq->getTracks()) - { - if (track->getIndex() > 63) - { - break; - } - - for (auto &e : track->getEvents()) - { - auto sysEx = - std::dynamic_pointer_cast(e); - - if (sysEx) - { - constexpr int dataSegments = 1; - segmentCount += dataSegments + 1; - } - else if (std::dynamic_pointer_cast(e)) - { - segmentCount += 2; - } - else - { - segmentCount++; - } - } - } - - return segmentCount; -} diff --git a/src/main/file/all/SequenceNames.hpp b/src/main/file/all/SequenceNames.hpp deleted file mode 100644 index be274f110..000000000 --- a/src/main/file/all/SequenceNames.hpp +++ /dev/null @@ -1,38 +0,0 @@ -#pragma once - -#include -#include - -namespace mpc -{ - class Mpc; -} - -namespace mpc::sequencer -{ - class Sequence; -} - -namespace mpc::file::all -{ - class SequenceNames - { - - public: - static const int LENGTH = 1782; - static const int ENTRY_LENGTH = 18; - static const int LAST_EVENT_INDEX_OFFSET = 16; - std::vector names = std::vector(99); - std::vector usednesses = std::vector(99); - std::vector saveBytes; - - std::vector &getNames(); - std::vector &getUsednesses(); - - std::vector &getBytes(); - static int getSegmentCount(sequencer::Sequence *seq); - - SequenceNames(const std::vector &b); - SequenceNames(Mpc &mpc); - }; -} // namespace mpc::file::all diff --git a/src/main/file/all/Tracks.cpp b/src/main/file/all/Tracks.cpp deleted file mode 100644 index f46faf108..000000000 --- a/src/main/file/all/Tracks.cpp +++ /dev/null @@ -1,157 +0,0 @@ -#include "file/all/Tracks.hpp" - -#include "file/all/AllParser.hpp" -#include "file/all/AllSequence.hpp" -#include "sequencer/Sequence.hpp" -#include "sequencer/Track.hpp" - -#include "file/ByteUtil.hpp" -#include "StrUtil.hpp" -#include "Util.hpp" - -using namespace mpc::file::all; - -Tracks::Tracks(const std::vector &loadBytes) -{ - for (int i = 0; i < 64; i++) - { - devices[i] = loadBytes[DEVICES_OFFSET + i]; - busses[i] = loadBytes[BUSSES_OFFSET + i]; - pgms[i] = loadBytes[PGMS_OFFSET + i]; - veloRatios[i] = loadBytes[VELO_RATIOS_OFFSET + i]; - const auto offset = TRACK_NAMES_OFFSET + i * AllParser::NAME_LENGTH; - std::string name; - - for (const char c : Util::vecCopyOfRange( - loadBytes, offset, offset + AllParser::NAME_LENGTH)) - { - if (c == 0x00) - { - break; - } - name.push_back(c); - } - - names[i] = name; - - const auto statusByte = loadBytes[STATUS_OFFSET + i]; - - usednesses[i] = (statusByte >> 0) & 1; - ons[i] = (statusByte >> 1) & 1; - transmitProgramChangesEnableds[i] = (statusByte >> 2) & 1; - } -} - -Tracks::Tracks(sequencer::Sequence *seq) -{ - saveBytes = std::vector(AllSequence::TRACKS_LENGTH); - for (int i = 0; i < 64; i++) - { - const auto t = seq->getTrack(i); - - for (auto j = 0; j < AllParser::NAME_LENGTH; j++) - { - const auto offset = TRACK_NAMES_OFFSET + i * AllParser::NAME_LENGTH; - auto name = - StrUtil::padRight(t->getName(), " ", AllParser::NAME_LENGTH); - saveBytes[offset + j] = name[j]; - } - - saveBytes[DEVICES_OFFSET + i] = t->getDeviceIndex(); - saveBytes[BUSSES_OFFSET + i] = busTypeToIndex(t->getBusType()); - saveBytes[PGMS_OFFSET + i] = t->getProgramChange(); - saveBytes[VELO_RATIOS_OFFSET + i] = t->getVelocityRatio(); - - uint8_t b = 0; - if (t->isUsed()) - { - b |= 1u << 0; - } - if (t->isOn()) - { - b |= 1u << 1; - } - if (t->isTransmitProgramChangesEnabled()) - { - b |= 1u << 2; - } - saveBytes[STATUS_OFFSET + i] = b; - } - - for (int i = 0; i < PADDING1.size(); i++) - { - saveBytes[PADDING1_OFFSET + i] = PADDING1[i]; - } - - const auto lastTick = seq->getLastTick(); - const auto remainder = lastTick % 65535; - const auto large = static_cast(floor(lastTick / 65536.0)); - const auto lastTickBytes = ByteUtil::ushort2bytes(remainder); - saveBytes[LAST_TICK_BYTE1_OFFSET] = lastTickBytes[0]; - saveBytes[LAST_TICK_BYTE2_OFFSET] = lastTickBytes[1]; - saveBytes[LAST_TICK_BYTE3_OFFSET] = large; - - const auto unknown32BitIntBytes1 = ByteUtil::uint2bytes(10000000); - const auto unknown32BitIntBytes2 = ByteUtil::uint2bytes( - static_cast(seq->getLastTick() * 5208.333333333333)); - - for (int j = 0; j < 4; j++) - { - const int offset = UNKNOWN32_BIT_INT_OFFSET + j; - saveBytes[offset] = unknown32BitIntBytes1[j]; - } - - for (int j = 0; j < 4; j++) - { - saveBytes[UNKNOWN32_BIT_INT_OFFSET + j + 4] = unknown32BitIntBytes2[j]; - } -} - -std::vector Tracks::PADDING1 = std::vector{ - static_cast(232), 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - static_cast(232), 3}; - -int Tracks::getDeviceIndex(const int i) const -{ - return devices[i]; -} - -int Tracks::getBus(const int i) const -{ - return busses[i]; -} - -int Tracks::getVelo(const int i) const -{ - return veloRatios[i]; -} - -int Tracks::getPgm(const int i) const -{ - return pgms[i]; -} - -std::string Tracks::getName(const int i) -{ - return names[i]; -} - -bool Tracks::isUsed(const int i) const -{ - return usednesses[i]; -} - -bool Tracks::isOn(const int i) const -{ - return ons[i]; -} - -bool Tracks::isTransmitProgramChangesEnabled(const int i) -{ - return transmitProgramChangesEnableds[i]; -} - -std::vector &Tracks::getBytes() -{ - return saveBytes; -} diff --git a/src/main/file/all/Tracks.hpp b/src/main/file/all/Tracks.hpp deleted file mode 100644 index 38cf51f5f..000000000 --- a/src/main/file/all/Tracks.hpp +++ /dev/null @@ -1,55 +0,0 @@ -#pragma once - -#include -#include - -namespace mpc::sequencer -{ - class Sequence; -} - -namespace mpc::file::all -{ - class Tracks - { - - static const int TRACK_NAMES_OFFSET{0}; - static const int DEVICES_OFFSET{1024}; - static const int BUSSES_OFFSET{1088}; - static const int PGMS_OFFSET{1152}; - static const int VELO_RATIOS_OFFSET{1216}; - static const int STATUS_OFFSET{1280}; - static const int PADDING1_OFFSET{1408}; - static std::vector PADDING1; - static const int LAST_TICK_BYTE1_OFFSET{1424}; - static const int LAST_TICK_BYTE2_OFFSET{1425}; - static const int LAST_TICK_BYTE3_OFFSET{1426}; - static const int UNKNOWN32_BIT_INT_OFFSET{1428}; - std::vector busses = std::vector(64); - std::vector devices = std::vector(64); - std::vector veloRatios = std::vector(64); - std::vector pgms = std::vector(64); - std::vector names = std::vector(64); - std::vector usednesses = std::vector(64); - std::vector ons = std::vector(64); - std::vector transmitProgramChangesEnableds = - std::vector(64); - - public: - std::vector saveBytes{}; - - int getDeviceIndex(int i) const; - int getBus(int i) const; - int getVelo(int i) const; - int getPgm(int i) const; - std::string getName(int i); - bool isUsed(int i) const; - bool isOn(int i) const; - bool isTransmitProgramChangesEnabled(int i); - - std::vector &getBytes(); - - explicit Tracks(const std::vector &loadBytes); - explicit Tracks(sequencer::Sequence *seq); - }; -} // namespace mpc::file::all diff --git a/src/main/file/aps/ApsAssignTable.cpp b/src/main/file/aps/ApsAssignTable.cpp deleted file mode 100644 index eeb1ec094..000000000 --- a/src/main/file/aps/ApsAssignTable.cpp +++ /dev/null @@ -1,31 +0,0 @@ -#include "file/aps/ApsAssignTable.hpp" - -using namespace mpc::file::aps; - -ApsAssignTable::ApsAssignTable(const std::vector &loadBytes) -{ - assignTable = std::vector(64); - for (int i = 0; i < 64; i++) - { - assignTable[i] = loadBytes[i]; - } -} - -ApsAssignTable::ApsAssignTable(const std::vector &assignTable) -{ - saveBytes = std::vector(Mpc2000XlSpecs::PROGRAM_PAD_COUNT); - for (int i = 0; i < Mpc2000XlSpecs::PROGRAM_PAD_COUNT; i++) - { - saveBytes[i] = assignTable[i]; - } -} - -std::vector ApsAssignTable::get() -{ - return assignTable; -} - -std::vector ApsAssignTable::getBytes() -{ - return saveBytes; -} diff --git a/src/main/file/aps/ApsAssignTable.hpp b/src/main/file/aps/ApsAssignTable.hpp deleted file mode 100644 index 58b9364f3..000000000 --- a/src/main/file/aps/ApsAssignTable.hpp +++ /dev/null @@ -1,22 +0,0 @@ -#pragma once - -#include "IntTypes.hpp" - -#include - -namespace mpc::file::aps -{ - class ApsAssignTable - { - - public: - std::vector assignTable; - std::vector saveBytes; - - std::vector get(); - std::vector getBytes(); - - explicit ApsAssignTable(const std::vector &loadBytes); - ApsAssignTable(const std::vector &assignTable); - }; -} // namespace mpc::file::aps diff --git a/src/main/file/aps/ApsDrumConfiguration.cpp b/src/main/file/aps/ApsDrumConfiguration.cpp deleted file mode 100644 index 6f7b50051..000000000 --- a/src/main/file/aps/ApsDrumConfiguration.cpp +++ /dev/null @@ -1,56 +0,0 @@ -#include "file/aps/ApsDrumConfiguration.hpp" - -#include "file/BitUtil.hpp" - -using namespace mpc::file::aps; - -ApsDrumConfiguration::ApsDrumConfiguration(const std::vector &loadBytes) -{ - programIndex = loadBytes[5]; - receivePgmChangeEnabled = BitUtil::getBits(loadBytes[2])[7] == '1'; - receiveMidiVolumeEnabled = BitUtil::getBits(loadBytes[3])[7] == '1'; -} - -ApsDrumConfiguration::ApsDrumConfiguration(int program, bool recPgmChange, - bool recMidiVolume) -{ - saveBytes = std::vector(12); - for (int i = 0; i < 9; i++) - { - saveBytes[i] = TEMPLATE[i]; - } - - for (int i = 0; i < 3; i++) - { - saveBytes[i + 9] = PADDING[i]; - } - - saveBytes[1] = program; - saveBytes[2] = BitUtil::setBits(1, saveBytes[2], recPgmChange); - saveBytes[3] = BitUtil::setBits(1, saveBytes[3], recMidiVolume); - saveBytes[5] = program; -} - -std::vector ApsDrumConfiguration::TEMPLATE = - std::vector{0, 0, 1, 1, 127, 0, 1, 1, 127}; -std::vector ApsDrumConfiguration::PADDING = std::vector{64, 0, 6}; - -int ApsDrumConfiguration::getProgramIndex() const -{ - return programIndex; -} - -bool ApsDrumConfiguration::isReceivePgmChangeEnabled() const -{ - return receivePgmChangeEnabled; -} - -bool ApsDrumConfiguration::isReceiveMidiVolumeEnabled() const -{ - return receiveMidiVolumeEnabled; -} - -std::vector ApsDrumConfiguration::getBytes() -{ - return saveBytes; -} diff --git a/src/main/file/aps/ApsDrumConfiguration.hpp b/src/main/file/aps/ApsDrumConfiguration.hpp deleted file mode 100644 index 5af0b787f..000000000 --- a/src/main/file/aps/ApsDrumConfiguration.hpp +++ /dev/null @@ -1,31 +0,0 @@ -#pragma once - -#include - -namespace mpc::file::aps -{ - class ApsDrumConfiguration - { - - public: - int programIndex; - bool receivePgmChangeEnabled; - bool receiveMidiVolumeEnabled; - std::vector saveBytes; - - private: - static std::vector TEMPLATE; - static std::vector PADDING; - - public: - int getProgramIndex() const; - bool isReceivePgmChangeEnabled() const; - bool isReceiveMidiVolumeEnabled() const; - - std::vector getBytes(); - - ApsDrumConfiguration(const std::vector &loadBytes); - ApsDrumConfiguration(int program, bool recPgmChange, - bool recMidiVolume); - }; -} // namespace mpc::file::aps diff --git a/src/main/file/aps/ApsGlobalParameters.cpp b/src/main/file/aps/ApsGlobalParameters.cpp deleted file mode 100644 index 787e34d8c..000000000 --- a/src/main/file/aps/ApsGlobalParameters.cpp +++ /dev/null @@ -1,131 +0,0 @@ -#include "ApsGlobalParameters.hpp" - -#include "Mpc.hpp" -#include "file/aps/ApsParser.hpp" - -#include "lcdgui/screens/MixerSetupScreen.hpp" -#include "lcdgui/screens/DrumScreen.hpp" -#include "lcdgui/screens/PgmAssignScreen.hpp" - -#include "file/BitUtil.hpp" -#include "sampler/Sampler.hpp" - -using namespace mpc::lcdgui; -using namespace mpc::lcdgui::screens; -using namespace mpc::file::aps; - -ApsGlobalParameters::ApsGlobalParameters(const std::vector &loadBytes) -{ - padToInternalSound = BitUtil::getBits(loadBytes[0])[7] == '1'; - padAssignMaster = BitUtil::getBits(loadBytes[1])[7] == '1'; - stereoMixSourceDrum = BitUtil::getBits(loadBytes[2])[7] == '1'; - indivFxSourceDrum = BitUtil::getBits(loadBytes[2])[6] == '1'; - copyPgmMixToDrum = BitUtil::getBits(loadBytes[3])[7] == '1'; - recordMixChanges = BitUtil::getBits(loadBytes[3])[3] == '1'; - fxDrum = readFxDrum(loadBytes[4]); - masterLevel = loadBytes[6]; -} - -ApsGlobalParameters::ApsGlobalParameters(Mpc &mpc) -{ - saveBytes = std::vector(ApsParser::PARAMETERS_LENGTH); - - for (int i = 0; i < saveBytes.size(); i++) - { - if (mpc.getSampler()->getSoundCount() == 0) - { - saveBytes[i] = TEMPLATE_NO_SOUNDS[i]; - } - else - { - saveBytes[i] = TEMPLATE_SOUNDS[i]; - } - } - - auto drumScreen = mpc.screens->get(); - - auto const padToInternalSoundVal = drumScreen->isPadToIntSound(); - - auto pgmAssignScreen = mpc.screens->get(); - auto const padAssignMasterVal = pgmAssignScreen->isPadAssignMaster(); - - auto mixerSetupScreen = mpc.screens->get(); - auto const stereoMixSourceDrumVal = - mixerSetupScreen->isStereoMixSourceDrum(); - auto const indivFxSourceDrumVal = mixerSetupScreen->isIndivFxSourceDrum(); - auto const copyPgmMixToDrumVal = - mixerSetupScreen->isCopyPgmMixToDrumEnabled(); - auto const recordMixChangesVal = - mixerSetupScreen->isRecordMixChangesEnabled(); - auto const masterLevelVal = mixerSetupScreen->getMasterLevel(); - auto const fxDrumVal = mixerSetupScreen->getFxDrum(); - - saveBytes[0] = BitUtil::setBits(1, saveBytes[0], padToInternalSoundVal); - saveBytes[1] = BitUtil::setBits(1, saveBytes[1], padAssignMasterVal); - saveBytes[2] = BitUtil::setBits(1, saveBytes[2], stereoMixSourceDrumVal); - saveBytes[2] = BitUtil::setBits(2, saveBytes[2], indivFxSourceDrumVal); - saveBytes[3] = BitUtil::setBits(1, saveBytes[3], copyPgmMixToDrumVal); - saveBytes[3] = BitUtil::setBits(16, saveBytes[3], recordMixChangesVal); - saveBytes[4] = fxDrumVal; - saveBytes[6] = masterLevelVal; -} - -std::vector ApsGlobalParameters::TEMPLATE_NO_SOUNDS = { - 127, (char)(254 & 0xff), 124, (char)(238 & 0xff), 0, 0, 0, 64}; -std::vector ApsGlobalParameters::TEMPLATE_SOUNDS = { - '\x03', '\x38', '\x00', '\xa1', 0, 0, 0, 64}; - -int ApsGlobalParameters::readFxDrum(char b) const -{ - for (int i = 2; i < 8; i++) - { - b &= ~(1 << i); - } - - return b; -} - -int ApsGlobalParameters::getFxDrum() const -{ - return fxDrum; -} - -bool ApsGlobalParameters::isPadToIntSoundEnabled() const -{ - return padToInternalSound; -} - -bool ApsGlobalParameters::isPadAssignMaster() const -{ - return padAssignMaster; -} - -bool ApsGlobalParameters::isStereoMixSourceDrum() const -{ - return stereoMixSourceDrum; -} - -bool ApsGlobalParameters::isIndivFxSourceDrum() const -{ - return indivFxSourceDrum; -} - -bool ApsGlobalParameters::isCopyPgmMixToDrumEnabled() const -{ - return copyPgmMixToDrum; -} - -bool ApsGlobalParameters::isRecordMixChangesEnabled() const -{ - return recordMixChanges; -} - -int ApsGlobalParameters::getMasterLevel() const -{ - return masterLevel; -} - -std::vector ApsGlobalParameters::getBytes() -{ - return saveBytes; -} diff --git a/src/main/file/aps/ApsGlobalParameters.hpp b/src/main/file/aps/ApsGlobalParameters.hpp deleted file mode 100644 index 4998076c0..000000000 --- a/src/main/file/aps/ApsGlobalParameters.hpp +++ /dev/null @@ -1,47 +0,0 @@ -#pragma once - -#include - -namespace mpc -{ - class Mpc; -} - -namespace mpc::file::aps -{ - class ApsGlobalParameters - { - bool padToInternalSound; - bool padAssignMaster; - bool stereoMixSourceDrum; - bool indivFxSourceDrum; - bool copyPgmMixToDrum; - bool recordMixChanges; - int masterLevel; - int fxDrum; - - static std::vector TEMPLATE_NO_SOUNDS; - static std::vector TEMPLATE_SOUNDS; - - public: - std::vector saveBytes{}; - - private: - int readFxDrum(char b) const; - - public: - int getFxDrum() const; - bool isPadToIntSoundEnabled() const; - bool isPadAssignMaster() const; - bool isStereoMixSourceDrum() const; - bool isIndivFxSourceDrum() const; - bool isCopyPgmMixToDrumEnabled() const; - bool isRecordMixChangesEnabled() const; - int getMasterLevel() const; - - std::vector getBytes(); - - ApsGlobalParameters(const std::vector &loadBytes); - ApsGlobalParameters(Mpc &mpc); - }; -} // namespace mpc::file::aps diff --git a/src/main/file/aps/ApsHeader.cpp b/src/main/file/aps/ApsHeader.cpp deleted file mode 100644 index 48f804e0d..000000000 --- a/src/main/file/aps/ApsHeader.cpp +++ /dev/null @@ -1,46 +0,0 @@ -#include "file/aps/ApsHeader.hpp" - -#include "file/ByteUtil.hpp" -#include "MpcSpecs.hpp" - -#include - -using namespace mpc::file::aps; - -ApsHeader::ApsHeader(const std::vector &loadBytes) -{ - valid = loadBytes[0] == APS_HEADER_MAGIC[0] && - loadBytes[1] == APS_HEADER_MAGIC[1]; - - soundCount = ByteUtil::bytes2ushort({loadBytes[2], loadBytes[3]}); - - assert(soundCount <= Mpc2000XlSpecs::MAX_SOUND_COUNT_IN_MEMORY); -} - -ApsHeader::ApsHeader(const uint16_t soundCount) -{ - assert(soundCount <= Mpc2000XlSpecs::MAX_SOUND_COUNT_IN_MEMORY); - - saveBytes = std::vector(4); - - const auto soundCountBytes = ByteUtil::ushort2bytes(soundCount); - saveBytes[0] = 10; - saveBytes[1] = 5; - saveBytes[2] = soundCountBytes[0]; - saveBytes[3] = soundCountBytes[1]; -} - -bool ApsHeader::isValid() const -{ - return valid; -} - -int ApsHeader::getSoundAmount() const -{ - return soundCount; -} - -std::vector ApsHeader::getBytes() const -{ - return saveBytes; -} diff --git a/src/main/file/aps/ApsHeader.hpp b/src/main/file/aps/ApsHeader.hpp deleted file mode 100644 index 155f10a1d..000000000 --- a/src/main/file/aps/ApsHeader.hpp +++ /dev/null @@ -1,24 +0,0 @@ -#pragma once - -#include -#include - -namespace mpc::file::aps -{ - class ApsHeader - { - const std::vector APS_HEADER_MAGIC{0x0A, 0x05}; - - public: - bool valid = false; - uint16_t soundCount = 0; - std::vector saveBytes; - - bool isValid() const; - int getSoundAmount() const; - std::vector getBytes() const; - - explicit ApsHeader(const std::vector &loadBytes); - explicit ApsHeader(uint16_t soundCount); - }; -} // namespace mpc::file::aps diff --git a/src/main/file/aps/ApsMixer.cpp b/src/main/file/aps/ApsMixer.cpp deleted file mode 100644 index 786d3ccd3..000000000 --- a/src/main/file/aps/ApsMixer.cpp +++ /dev/null @@ -1,95 +0,0 @@ -#include "file/aps/ApsMixer.hpp" - -#include "engine/StereoMixer.hpp" -#include "engine/IndivFxMixer.hpp" - -using namespace mpc::file::aps; -using namespace mpc::performance; - -ApsMixer::ApsMixer(const std::vector &loadBytes) -{ - for (int i = 0; i < 64; i++) - { - fxPaths[i] = loadBytes[i * 6 + 0]; - levels[i] = loadBytes[i * 6 + 1]; - pannings[i] = loadBytes[i * 6 + 2]; - iLevels[i] = loadBytes[i * 6 + 3]; - iOutputs[i] = loadBytes[i * 6 + 4]; - sendLevels[i] = loadBytes[i * 6 + 5]; - } -} - -ApsMixer::ApsMixer( - const std::vector> &stereoMixer, - const std::vector> &indivFxMixer) -{ - for (int i = 0; i < 64; i++) - { - const auto mixerChannel = stereoMixer[i]; - const auto indivFxMixerChannel = indivFxMixer[i]; - saveBytes[i * 6 + 0] = - static_cast(indivFxMixerChannel->getFxPath()); - saveBytes[i * 6 + 1] = static_cast(mixerChannel->getLevel()); - saveBytes[i * 6 + 2] = static_cast(mixerChannel->getPanning()); - saveBytes[i * 6 + 3] = - static_cast(indivFxMixerChannel->getVolumeIndividualOut()); - saveBytes[i * 6 + 4] = - static_cast(indivFxMixerChannel->getOutput()); - saveBytes[i * 6 + 5] = - static_cast(indivFxMixerChannel->getFxSendLevel()); - } -} - -StereoMixer ApsMixer::getStereoMixerChannel(const int noteIndex) const -{ - StereoMixer result; - result.level = DrumMixerLevel(getLevel(noteIndex)); - result.panning = DrumMixerPanning(getPanning(noteIndex)); - return result; -} - -IndivFxMixer ApsMixer::getIndivFxMixerChannel(const int noteIndex) const -{ - IndivFxMixer result; - result.individualOutLevel = DrumMixerLevel(getIndividualLevel(noteIndex)); - result.individualOutput = - DrumMixerIndividualOutput(getIndividualOutput(noteIndex)); - result.fxSendLevel = DrumMixerLevel(getSendLevel(noteIndex)); - result.fxPath = DrumMixerIndividualFxPath(getFxPath(noteIndex)); - return result; -} - -int ApsMixer::getFxPath(const int noteIndex) const -{ - return fxPaths[noteIndex]; -} - -int ApsMixer::getLevel(const int noteIndex) const -{ - return levels[noteIndex]; -} - -int ApsMixer::getPanning(const int noteIndex) const -{ - return pannings[noteIndex]; -} - -int ApsMixer::getIndividualLevel(const int noteIndex) const -{ - return iLevels[noteIndex]; -} - -int ApsMixer::getIndividualOutput(const int noteIndex) const -{ - return iOutputs[noteIndex]; -} - -int ApsMixer::getSendLevel(const int noteIndex) const -{ - return sendLevels[noteIndex]; -} - -std::vector ApsMixer::getBytes() -{ - return saveBytes; -} diff --git a/src/main/file/aps/ApsMixer.hpp b/src/main/file/aps/ApsMixer.hpp deleted file mode 100644 index 9253c895d..000000000 --- a/src/main/file/aps/ApsMixer.hpp +++ /dev/null @@ -1,44 +0,0 @@ -#pragma once - -#include "performance/Drum.hpp" - -#include -#include - -namespace mpc::engine -{ - class StereoMixer; - class IndivFxMixer; -} // namespace mpc::engine - -namespace mpc::file::aps -{ - class ApsMixer - { - - public: - std::vector fxPaths = std::vector(64); - std::vector levels = std::vector(64); - std::vector pannings = std::vector(64); - std::vector iLevels = std::vector(64); - std::vector iOutputs = std::vector(64); - std::vector sendLevels = std::vector(64); - std::vector saveBytes = std::vector(384); - - performance::StereoMixer getStereoMixerChannel(int noteIndex) const; - performance::IndivFxMixer getIndivFxMixerChannel(int noteIndex) const; - int getFxPath(int noteIndex) const; - int getLevel(int noteIndex) const; - int getPanning(int noteIndex) const; - int getIndividualLevel(int noteIndex) const; - int getIndividualOutput(int noteIndex) const; - int getSendLevel(int noteIndex) const; - std::vector getBytes(); - - ApsMixer(const std::vector &loadBytes); - ApsMixer(const std::vector> - &stereoMixer, - const std::vector> - &indivFxMixer); - }; -} // namespace mpc::file::aps diff --git a/src/main/file/aps/ApsName.cpp b/src/main/file/aps/ApsName.cpp deleted file mode 100644 index e15065107..000000000 --- a/src/main/file/aps/ApsName.cpp +++ /dev/null @@ -1,49 +0,0 @@ -#include "file/aps/ApsName.hpp" - -#include "file/aps/ApsParser.hpp" - -#include "Util.hpp" - -using namespace mpc::file::aps; - -ApsName::ApsName(const std::vector &loadBytes) -{ - auto nameBytes = Util::vecCopyOfRange(loadBytes, 0, NAME_STRING_LENGTH); - name = ""; - for (char c : nameBytes) - { - if (c == 0x00) - { - break; - } - name.push_back(c); - } -} - -ApsName::ApsName(std::string name) -{ - saveBytes = std::vector(ApsParser::APS_NAME_LENGTH); - while (name.length() < NAME_STRING_LENGTH) - { - name.push_back(' '); - } - - for (int i = 0; i < NAME_STRING_LENGTH; i++) - { - saveBytes[i] = name[i]; - } - - saveBytes[NAME_STRING_LENGTH] = ApsParser::NAME_TERMINATOR; -} - -const int ApsName::NAME_STRING_LENGTH; - -std::string ApsName::get() -{ - return name; -} - -std::vector ApsName::getBytes() -{ - return saveBytes; -} diff --git a/src/main/file/aps/ApsName.hpp b/src/main/file/aps/ApsName.hpp deleted file mode 100644 index cd3eb6bfb..000000000 --- a/src/main/file/aps/ApsName.hpp +++ /dev/null @@ -1,25 +0,0 @@ -#pragma once - -#include -#include - -namespace mpc::file::aps -{ - - class ApsName - { - - static const int NAME_STRING_LENGTH{16}; - - public: - std::string name; - std::vector saveBytes; - - std::string get(); - - std::vector getBytes(); - - ApsName(const std::vector &loadBytes); - ApsName(std::string name); - }; -} // namespace mpc::file::aps diff --git a/src/main/file/aps/ApsNoteParameters.cpp b/src/main/file/aps/ApsNoteParameters.cpp deleted file mode 100644 index 04043c3cd..000000000 --- a/src/main/file/aps/ApsNoteParameters.cpp +++ /dev/null @@ -1,197 +0,0 @@ -#include "sampler/VoiceOverlapMode.hpp" -#include "file/aps/ApsNoteParameters.hpp" - -#include "sampler/NoteParameters.hpp" - -#include "file/ByteUtil.hpp" - -#include - -using namespace mpc::file::aps; - -ApsNoteParameters::ApsNoteParameters(const std::vector &loadBytes) -{ - soundIndex = - loadBytes[0] == '\xff' ? -1 : static_cast(loadBytes[0]); - assert(soundIndex >= -1); - soundGenerationMode = loadBytes[2]; - velocityRangeLower = loadBytes[3]; - alsoPlay1 = loadBytes[4] == 0 ? 34 : loadBytes[4]; - velocityRangeUpper = loadBytes[5]; - alsoPlay2 = loadBytes[6] == 0 ? 34 : loadBytes[6]; - voiceOverlapMode = static_cast(loadBytes[7]); - mute1 = loadBytes[8] == 0 ? 34 : loadBytes[8]; - mute2 = loadBytes[9] == 0 ? 34 : loadBytes[9]; - auto buf = std::vector{loadBytes[10], loadBytes[11]}; - tune = ByteUtil::bytes2short(buf); - attack = loadBytes[12]; - decay = loadBytes[13]; - decayMode = loadBytes[14]; - cutoffFrequency = loadBytes[15]; - resonance = loadBytes[16]; - filterAttack = loadBytes[17]; - filterDecay = loadBytes[18]; - filterEnvelopeAmount = loadBytes[19]; - velocityToLevel = loadBytes[20]; - velocityToAttack = loadBytes[21]; - velocityToStart = loadBytes[22]; - velocityToFilterFrequency = loadBytes[23]; - sliderParameter = loadBytes[24]; - velocityToPitch = loadBytes[25]; -} - -ApsNoteParameters::ApsNoteParameters(sampler::NoteParameters *np) -{ - saveBytes[0] = np->getSoundIndex() == -1 ? '\xff' : np->getSoundIndex(); - saveBytes[1] = np->getSoundIndex() == -1 ? '\xff' : '\x00'; - saveBytes[2] = np->getSoundGenerationMode(); - saveBytes[3] = np->getVelocityRangeLower(); - saveBytes[4] = np->getOptionalNoteA() == 34 ? 0 : np->getOptionalNoteA(); - saveBytes[5] = np->getVelocityRangeUpper(); - saveBytes[6] = np->getOptionalNoteB() == 34 ? 0 : np->getOptionalNoteB(); - saveBytes[7] = static_cast(np->getVoiceOverlapMode()); - saveBytes[8] = np->getMuteAssignA() == 34 ? 0 : np->getMuteAssignA(); - saveBytes[9] = np->getMuteAssignB() == 34 ? 0 : np->getMuteAssignB(); - auto buf = ByteUtil::ushort2bytes(np->getTune()); - saveBytes[10] = buf[0]; - saveBytes[11] = buf[1]; - saveBytes[12] = np->getAttack(); - saveBytes[13] = np->getDecay(); - saveBytes[14] = np->getDecayMode(); - saveBytes[15] = np->getFilterFrequency(); - saveBytes[16] = np->getFilterResonance(); - saveBytes[17] = np->getFilterAttack(); - saveBytes[18] = np->getFilterDecay(); - saveBytes[19] = np->getFilterEnvelopeAmount(); - saveBytes[20] = np->getVeloToLevel(); - saveBytes[21] = np->getVelocityToAttack(); - saveBytes[22] = np->getVelocityToStart(); - saveBytes[23] = np->getVelocityToFilterFrequency(); - saveBytes[24] = np->getSliderParameterNumber(); - saveBytes[25] = np->getVelocityToPitch(); -} - -int ApsNoteParameters::getSoundIndex() const -{ - return soundIndex; -} - -mpc::sampler::VoiceOverlapMode ApsNoteParameters::getVoiceOverlapMode() const -{ - return voiceOverlapMode; -} - -int ApsNoteParameters::getTune() const -{ - return tune; -} - -int ApsNoteParameters::getDecayMode() const -{ - return decayMode; -} - -int ApsNoteParameters::getSoundGenerationMode() const -{ - return soundGenerationMode; -} - -int ApsNoteParameters::getVelocityRangeLower() const -{ - return velocityRangeLower; -} - -int ApsNoteParameters::getAlsoPlay1() const -{ - return alsoPlay1; -} - -int ApsNoteParameters::getVelocityRangeUpper() const -{ - return velocityRangeUpper; -} - -int ApsNoteParameters::getAlsoPlay2() const -{ - return alsoPlay2; -} - -int ApsNoteParameters::getMute1() const -{ - return mute1; -} - -int ApsNoteParameters::getMute2() const -{ - return mute2; -} - -int ApsNoteParameters::getAttack() const -{ - return attack; -} - -int ApsNoteParameters::getDecay() const -{ - return decay; -} - -int ApsNoteParameters::getCutoffFrequency() const -{ - return cutoffFrequency; -} - -int ApsNoteParameters::getResonance() const -{ - return resonance; -} - -int ApsNoteParameters::getVelocityToFilterAttack() const -{ - return filterAttack; -} - -int ApsNoteParameters::getVelocityToFilterDecay() const -{ - return filterDecay; -} - -int ApsNoteParameters::getVelocityToFilterAmount() const -{ - return filterEnvelopeAmount; -} - -int ApsNoteParameters::getVelocityToLevel() const -{ - return velocityToLevel; -} - -int ApsNoteParameters::getVelocityToAttack() const -{ - return velocityToAttack; -} - -int ApsNoteParameters::getVelocityToStart() const -{ - return velocityToStart; -} - -int ApsNoteParameters::getVelocityToFilterFrequency() const -{ - return velocityToFilterFrequency; -} - -int ApsNoteParameters::getSliderParameter() const -{ - return sliderParameter; -} - -int ApsNoteParameters::getVelocityToPitch() const -{ - return velocityToPitch; -} - -std::vector ApsNoteParameters::getBytes() -{ - return saveBytes; -} diff --git a/src/main/file/aps/ApsNoteParameters.hpp b/src/main/file/aps/ApsNoteParameters.hpp deleted file mode 100644 index 3ab53e864..000000000 --- a/src/main/file/aps/ApsNoteParameters.hpp +++ /dev/null @@ -1,73 +0,0 @@ -#pragma once - -#include "sampler/VoiceOverlapMode.hpp" - -#include - -namespace mpc::sampler -{ - class NoteParameters; -} - -namespace mpc::file::aps -{ - class ApsNoteParameters - { - - public: - int soundIndex; - int soundGenerationMode; - int velocityRangeLower; - int alsoPlay1; - int velocityRangeUpper; - int alsoPlay2; - sampler::VoiceOverlapMode voiceOverlapMode; - int mute1; - int mute2; - int tune; - int attack; - int decay; - int decayMode; - int cutoffFrequency; - int resonance; - int filterAttack; - int filterDecay; - int filterEnvelopeAmount; - int velocityToLevel; - int velocityToAttack; - int velocityToStart; - int velocityToFilterFrequency; - int sliderParameter; - int velocityToPitch; - std::vector saveBytes = std::vector(26); - - int getSoundIndex() const; - sampler::VoiceOverlapMode getVoiceOverlapMode() const; - int getTune() const; - int getDecayMode() const; - int getSoundGenerationMode() const; - int getVelocityRangeLower() const; - int getAlsoPlay1() const; - int getVelocityRangeUpper() const; - int getAlsoPlay2() const; - int getMute1() const; - int getMute2() const; - int getAttack() const; - int getDecay() const; - int getCutoffFrequency() const; - int getResonance() const; - int getVelocityToFilterAttack() const; - int getVelocityToFilterDecay() const; - int getVelocityToFilterAmount() const; - int getVelocityToLevel() const; - int getVelocityToAttack() const; - int getVelocityToStart() const; - int getVelocityToFilterFrequency() const; - int getSliderParameter() const; - int getVelocityToPitch() const; - std::vector getBytes(); - - ApsNoteParameters(const std::vector &loadBytes); - ApsNoteParameters(sampler::NoteParameters *np); - }; -} // namespace mpc::file::aps diff --git a/src/main/file/aps/ApsParser.cpp b/src/main/file/aps/ApsParser.cpp deleted file mode 100644 index c938bd9a2..000000000 --- a/src/main/file/aps/ApsParser.cpp +++ /dev/null @@ -1,223 +0,0 @@ -#include "file/aps/ApsParser.hpp" - -#include "Mpc.hpp" - -#include "sampler/Program.hpp" -#include "sampler/Sampler.hpp" -#include "sequencer/Bus.hpp" -#include "sequencer/Sequencer.hpp" - -#include "Util.hpp" - -using namespace mpc::file::aps; -using namespace mpc::disk; -using namespace mpc::sampler; - -ApsParser::ApsParser(const std::vector &loadBytes) -{ - if (loadBytes.empty()) - { - // throw invalid_argument(name + " has no data"); - return; - } - - header = std::make_unique(Util::vecCopyOfRange( - loadBytes, HEADER_OFFSET, HEADER_OFFSET + HEADER_LENGTH)); - - auto const soundNamesEnd = - HEADER_LENGTH + header->getSoundAmount() * SOUND_NAME_LENGTH; - soundNames = std::make_unique(Util::vecCopyOfRange( - loadBytes, HEADER_OFFSET + HEADER_LENGTH, soundNamesEnd)); - programCount = (loadBytes.size() - 1689 - soundNames->get().size() * 17) / - PROGRAM_LENGTH; - auto const nameEnd = soundNamesEnd + PAD_LENGTH1 + APS_NAME_LENGTH; - auto const nameOffset = soundNamesEnd + PAD_LENGTH1; - apsName = std::make_unique( - Util::vecCopyOfRange(loadBytes, nameOffset, nameEnd)); - auto const parametersEnd = nameEnd + PARAMETERS_LENGTH; - globalParameters = std::make_unique( - Util::vecCopyOfRange(loadBytes, nameEnd, parametersEnd)); - auto const maTableEnd = parametersEnd + TABLE_LENGTH; - maTable = std::make_unique( - Util::vecCopyOfRange(loadBytes, parametersEnd, maTableEnd)); - int const drum1MixerOffset = maTableEnd + PAD_LENGTH2; - - for (int i = 0; i < 4; i++) - { - int offset = drum1MixerOffset + - i * (MIXER_LENGTH + DRUM_CONFIG_LENGTH + DRUM_PAD_LENGTH); - drumMixers[i] = std::make_unique( - Util::vecCopyOfRange(loadBytes, offset, offset + MIXER_LENGTH)); - drumConfigurations[i] = std::make_unique( - Util::vecCopyOfRange(loadBytes, offset + MIXER_LENGTH, - offset + MIXER_LENGTH + DRUM_CONFIG_LENGTH)); - } - - int const firstProgramOffset = drum1MixerOffset + - (MIXER_LENGTH + DRUM_CONFIG_LENGTH) * 4 + - PAD_LENGTH3 - 6; - - for (int i = 0; i < programCount; i++) - { - int offset = - firstProgramOffset + i * (PROGRAM_LENGTH + PROGRAM_PAD_LENGTH); - programs.push_back(std::make_unique( - Util::vecCopyOfRange(loadBytes, offset, offset + PROGRAM_LENGTH))); - } -} - -ApsParser::ApsParser(Mpc &mpc, std::string apsNameString) -{ - auto sampler = mpc.getSampler(); - std::vector> chunks; - programCount = sampler->getProgramCount(); - int const soundCount = sampler->getSoundCount(); - - chunks.push_back(ApsHeader(soundCount).getBytes()); - - chunks.push_back(ApsSoundNames(sampler.get()).getBytes()); - - chunks.push_back(std::vector{24, 0}); - - chunks.push_back(ApsName(apsNameString).getBytes()); - - auto parameters = ApsGlobalParameters(mpc); - chunks.push_back(parameters.getBytes()); - - auto masterTable = ApsAssignTable(*sampler->getMasterPadAssign()); - chunks.push_back(masterTable.getBytes()); - chunks.push_back( - std::vector{4, 0, static_cast(136), 1, 64, 0, 6}); - - for (int i = 0; i < Mpc2000XlSpecs::DRUM_BUS_COUNT; i++) - { - auto drumBus = mpc.getSequencer()->getDrumBus(DrumBusIndex(i)); - - ApsMixer mixer(drumBus->getStereoMixerChannels(), - drumBus->getIndivFxMixerChannels()); - - ApsDrumConfiguration drumConfig(drumBus->getProgramIndex(), - drumBus->receivesPgmChange(), - drumBus->receivesMidiVolume()); - - chunks.push_back(mixer.getBytes()); - - if (i < 3) - { - chunks.push_back(drumConfig.getBytes()); - } - else - { - auto ba = drumConfig.getBytes(); - auto ba1 = std::vector(7); - - for (int j = 0; j < 7; j++) - { - ba1[j] = ba[j]; - } - - chunks.push_back(ba1); - } - } - - chunks.push_back({1, 127}); - - for (int i = 0; i < Mpc2000XlSpecs::MAX_PROGRAM_COUNT; i++) - { - auto p = sampler->getProgram(i); - - if (!p->isUsed()) - { - continue; - } - - auto program = ApsProgram(p.get(), i); - chunks.push_back(program.getBytes()); - } - - chunks.push_back(std::vector{(char)255, (char)255}); - auto totalSize = 0; - - for (auto &ba : chunks) - { - totalSize += ba.size(); - } - - saveBytes = std::vector(totalSize); - auto counter = 0; - - for (auto &ba : chunks) - { - for (auto &b : ba) - { - saveBytes[counter++] = b; - } - } -} - -const int ApsParser::HEADER_OFFSET; -const int ApsParser::HEADER_LENGTH; -const int ApsParser::SOUND_NAME_LENGTH; -const int ApsParser::PAD_LENGTH1; -const int ApsParser::APS_NAME_LENGTH; -const int ApsParser::PARAMETERS_LENGTH; -const int ApsParser::TABLE_LENGTH; -const int ApsParser::PAD_LENGTH2; -const int ApsParser::MIXER_LENGTH; -const int ApsParser::DRUM_CONFIG_LENGTH; -const int ApsParser::DRUM_PAD_LENGTH; -const int ApsParser::PAD_LENGTH3; -const int ApsParser::PROGRAM_LENGTH; -const int ApsParser::PROGRAM_PAD_LENGTH; -const int ApsParser::LAST_PROGRAM_PAD_LENGTH; -const char ApsParser::NAME_TERMINATOR; - -std::vector ApsParser::getSoundNames() const -{ - return soundNames->get(); -} - -bool ApsParser::isHeaderValid() const -{ - return header->isValid(); -} - -std::string ApsParser::getApsName() const -{ - return apsName->get(); -} - -std::vector ApsParser::getPrograms() const -{ - std::vector res; - for (auto &ap : programs) - { - res.push_back(ap.get()); - } - return res; -} - -std::vector ApsParser::getDrumMixers() const -{ - std::vector res; - for (auto &am : drumMixers) - { - res.push_back(am.get()); - } - return res; -} - -ApsDrumConfiguration *ApsParser::getDrumConfiguration(int i) const -{ - return drumConfigurations[i].get(); -} - -ApsGlobalParameters *ApsParser::getGlobalParameters() const -{ - return globalParameters.get(); -} - -std::vector ApsParser::getBytes() -{ - return saveBytes; -} diff --git a/src/main/file/aps/ApsParser.hpp b/src/main/file/aps/ApsParser.hpp deleted file mode 100644 index 131be96ed..000000000 --- a/src/main/file/aps/ApsParser.hpp +++ /dev/null @@ -1,73 +0,0 @@ -#pragma once - -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -namespace mpc -{ - class Mpc; -} - -namespace mpc::disk -{ - class MpcFile; -} -namespace mpc::file::aps -{ - class ApsParser - { - - public: - static const int HEADER_OFFSET{0}; - static const int HEADER_LENGTH{4}; - static const int SOUND_NAME_LENGTH{17}; - static const int PAD_LENGTH1{2}; - static const int APS_NAME_LENGTH{17}; - static const int PARAMETERS_LENGTH{8}; - static const int TABLE_LENGTH{64}; - static const int PAD_LENGTH2{7}; - static const int MIXER_LENGTH{384}; - static const int DRUM_CONFIG_LENGTH{9}; - static const int DRUM_PAD_LENGTH{3}; - static const int PAD_LENGTH3{15}; - static const int PROGRAM_LENGTH{2350}; - static const int PROGRAM_PAD_LENGTH{4}; - static const int LAST_PROGRAM_PAD_LENGTH{2}; - static const char NAME_TERMINATOR{0}; - int programCount; - std::unique_ptr header; - std::unique_ptr soundNames; - std::unique_ptr apsName; - std::unique_ptr globalParameters; - std::unique_ptr maTable; - std::vector> drumMixers = - std::vector>(4); - std::vector> drumConfigurations = - std::vector>(4); - std::vector> programs; - std::vector saveBytes; - - std::vector getSoundNames() const; - bool isHeaderValid() const; - std::string getApsName() const; - std::vector getPrograms() const; - std::vector getDrumMixers() const; - ApsDrumConfiguration *getDrumConfiguration(int i) const; - ApsGlobalParameters *getGlobalParameters() const; - - std::vector getBytes(); - - ApsParser(const std::vector &loadBytes); - ApsParser(Mpc &, std::string apsNameString); - }; -} // namespace mpc::file::aps diff --git a/src/main/file/aps/ApsProgram.cpp b/src/main/file/aps/ApsProgram.cpp deleted file mode 100644 index 407f8a0c0..000000000 --- a/src/main/file/aps/ApsProgram.cpp +++ /dev/null @@ -1,201 +0,0 @@ -#include "file/aps/ApsProgram.hpp" - -#include "file/aps/ApsAssignTable.hpp" -#include "file/aps/ApsNoteParameters.hpp" -#include "file/aps/ApsSlider.hpp" -#include "file/pgmwriter/Pads.hpp" -#include "engine/StereoMixer.hpp" -#include "sampler/Program.hpp" - -#include "StrUtil.hpp" -#include "Util.hpp" - -using namespace mpc::file::aps; -using namespace mpc::engine; - -ApsProgram::ApsProgram(const std::vector &loadBytes) -{ - index = loadBytes[INDEX_OFFSET]; - const auto nameBytes = - Util::vecCopyOfRange(loadBytes, NAME_OFFSET, NAME_OFFSET + NAME_LENGTH); - - name = ""; - - for (const char c : nameBytes) - { - if (c == 0x00) - { - break; - } - - name.push_back(c); - } - - name = StrUtil::trim(name); - - slider = new ApsSlider(Util::vecCopyOfRange(loadBytes, SLIDER_OFFSET, - SLIDER_OFFSET + SLIDER_LENGTH)); - - for (int i = 0; i < 64; i++) - { - const int offset = NOTE_PARAMETERS_OFFSET + i * NOTE_PARAMETERS_LENGTH; - noteParameters[i] = new ApsNoteParameters(Util::vecCopyOfRange( - loadBytes, offset, offset + NOTE_PARAMETERS_LENGTH)); - } - - mixer = - new ApsMixer(Util::vecCopyOfRange(loadBytes, MIXER_OFFSET, MIXER_END)); - assignTable = new ApsAssignTable( - Util::vecCopyOfRange(loadBytes, ASSIGN_TABLE_OFFSET, - ASSIGN_TABLE_OFFSET + ASSIGN_TABLE_LENGTH)); -} - -ApsProgram::ApsProgram(sampler::Program *program, int index) -{ - std::vector> byteList; - this->index = index; - byteList.push_back({static_cast(index)}); - byteList.push_back(UNKNOWN); - auto programName = StrUtil::padRight(program->getName(), " ", 16); - - for (char c : programName) - { - byteList.push_back({c}); - } - - byteList.push_back({0}); // Name terminator - - ApsSlider apsSlider(program->getSlider()); - byteList.push_back(apsSlider.getBytes()); - byteList.push_back({35, 64, 0, 26, 0}); - - for (int i = 0; i < 64; i++) - { - ApsNoteParameters np((program->getNoteParameters(i + 35))); - byteList.push_back(np.getBytes()); - } - - byteList.push_back({6}); - - std::vector> stereoMixerChannels(64); - std::vector> indivFxMixerChannels(64); - - for (int i = 0; i < 64; i++) - { - stereoMixerChannels[i] = program->getStereoMixerChannel(i); - indivFxMixerChannels[i] = program->getIndivFxMixerChannel(i); - } - - ApsMixer apsMixer(stereoMixerChannels, indivFxMixerChannels); - byteList.push_back(apsMixer.getBytes()); - byteList.push_back({0, Mpc2000XlSpecs::PROGRAM_PAD_COUNT, 0}); - auto apsAssignTable = - std::vector(Mpc2000XlSpecs::PROGRAM_PAD_COUNT); - - for (int i = 0; i < Mpc2000XlSpecs::PROGRAM_PAD_COUNT; i++) - { - apsAssignTable[i] = program->getNoteFromPad(ProgramPadIndex(i)); - } - - ApsAssignTable table(apsAssignTable); - byteList.push_back(table.getBytes()); - byteList.push_back(pgmwriter::Pads::getFxBoardSettings()); - auto totalSize = 0; - - for (auto &ba : byteList) - { - totalSize += ba.size(); - } - - saveBytes = std::vector(totalSize); - auto counter = 0; - - for (auto &ba : byteList) - { - for (auto b : ba) - { - saveBytes[counter++] = b; - } - } -} - -const int ApsProgram::NAME_OFFSET; -const int ApsProgram::NAME_LENGTH; -const int ApsProgram::NAME_TERMINATOR_LENGTH; -const int ApsProgram::SLIDER_OFFSET; -const int ApsProgram::SLIDER_LENGTH; -const int ApsProgram::PADDING1_LENGTH; -const int ApsProgram::NOTE_PARAMETERS_OFFSET; -const int ApsProgram::NOTE_PARAMETERS_LENGTH; -const int ApsProgram::PADDING2_LENGTH; -const int ApsProgram::MIXER_OFFSET; -const int ApsProgram::MIXER_END; -const int ApsProgram::PADDING3_LENGTH; -const int ApsProgram::ASSIGN_TABLE_OFFSET; -const int ApsProgram::ASSIGN_TABLE_LENGTH; - -ApsNoteParameters *ApsProgram::getNoteParameters(const int noteIndex) const -{ - return noteParameters[noteIndex]; -} - -ApsMixer *ApsProgram::getMixer() const -{ - return mixer; -} - -ApsAssignTable *ApsProgram::getAssignTable() const -{ - return assignTable; -} - -ApsSlider *ApsProgram::getSlider() const -{ - return slider; -} - -std::string ApsProgram::getName() -{ - return name; -} - -std::vector ApsProgram::getBytes() -{ - return saveBytes; -} - -mpc::performance::StereoMixer -ApsProgram::getStereoMixerChannel(const int noteIndex) const -{ - return mixer->getStereoMixerChannel(noteIndex); -} - -mpc::performance::IndivFxMixer -ApsProgram::getIndivFxMixerChannel(const int noteIndex) const -{ - return mixer->getIndivFxMixerChannel(noteIndex); -} - -ApsProgram::~ApsProgram() -{ - if (slider != nullptr) - { - delete slider; - } - if (mixer != nullptr) - { - delete mixer; - } - if (assignTable != nullptr) - { - delete assignTable; - } - - for (const auto &np : noteParameters) - { - if (np != nullptr) - { - delete np; - } - } -} diff --git a/src/main/file/aps/ApsProgram.hpp b/src/main/file/aps/ApsProgram.hpp deleted file mode 100644 index 3d55832a7..000000000 --- a/src/main/file/aps/ApsProgram.hpp +++ /dev/null @@ -1,75 +0,0 @@ -#pragma once - -#include - -#include -#include - -namespace mpc::engine -{ - class StereoMixer; - class IndivFxMixer; -} // namespace mpc::engine - -namespace mpc::sampler -{ - class Program; -} - -namespace mpc::file::aps -{ - class ApsNoteParameters; - class ApsAssignTable; - class ApsSlider; -} // namespace mpc::file::aps - -namespace mpc::file::aps -{ - class ApsProgram - { - - public: - static const int INDEX_OFFSET{0}; - static const int UNKNOWN_OFFSET{1}; - const std::vector UNKNOWN{0, 7, 4, 30, 0}; - static const int NAME_OFFSET{6}; - static const int NAME_LENGTH{16}; - static const int NAME_TERMINATOR_LENGTH{1}; - static const int SLIDER_OFFSET{23}; - static const int SLIDER_LENGTH{10}; - static const int PADDING1_LENGTH{5}; - static const int NOTE_PARAMETERS_OFFSET{38}; - static const int NOTE_PARAMETERS_LENGTH{26}; - static const int PADDING2_LENGTH{7}; - static const int MIXER_OFFSET{1703}; - static const int MIXER_END{2087}; - static const int PADDING3_LENGTH{3}; - static const int ASSIGN_TABLE_OFFSET{2090}; - static const int ASSIGN_TABLE_LENGTH{64}; - - std::string name; - int index; - ApsSlider *slider = nullptr; - std::vector noteParameters = - std::vector(64); - ApsMixer *mixer = nullptr; - ApsAssignTable *assignTable = nullptr; - std::vector saveBytes; - - ApsNoteParameters *getNoteParameters(int noteIndex) const; - ApsMixer *getMixer() const; - ApsAssignTable *getAssignTable() const; - ApsSlider *getSlider() const; - std::string getName(); - - std::vector getBytes(); - - performance::StereoMixer getStereoMixerChannel(int noteIndex) const; - - performance::IndivFxMixer getIndivFxMixerChannel(int noteIndex) const; - - ApsProgram(const std::vector &loadBytes); - ApsProgram(sampler::Program *program, int index); - ~ApsProgram(); - }; -} // namespace mpc::file::aps diff --git a/src/main/file/aps/ApsSlider.cpp b/src/main/file/aps/ApsSlider.cpp deleted file mode 100644 index cc4921e10..000000000 --- a/src/main/file/aps/ApsSlider.cpp +++ /dev/null @@ -1,92 +0,0 @@ -#include "file/aps/ApsSlider.hpp" - -#include "file/aps/ApsProgram.hpp" -#include "sampler/PgmSlider.hpp" - -using namespace mpc::file::aps; - -ApsSlider::ApsSlider(const std::vector &loadBytes) -{ - note = loadBytes[0] == 0 ? 34 : loadBytes[0]; - tuneLow = loadBytes[1]; - tuneHigh = loadBytes[2]; - decayLow = loadBytes[3]; - decayHigh = loadBytes[4]; - attackLow = loadBytes[5]; - attackHigh = loadBytes[6]; - filterLow = loadBytes[7]; - filterHigh = loadBytes[8]; - programChange = loadBytes[9]; -} - -ApsSlider::ApsSlider(sampler::PgmSlider *slider) -{ - saveBytes = std::vector(ApsProgram::SLIDER_LENGTH); - saveBytes[0] = slider->getNote() == 34 ? 0 : slider->getNote(); - saveBytes[1] = slider->getTuneLowRange(); - saveBytes[2] = slider->getTuneHighRange(); - saveBytes[3] = slider->getDecayLowRange(); - saveBytes[4] = slider->getDecayHighRange(); - saveBytes[5] = slider->getAttackLowRange(); - saveBytes[6] = slider->getAttackHighRange(); - saveBytes[7] = slider->getFilterLowRange(); - saveBytes[8] = slider->getFilterHighRange(); - saveBytes[9] = slider->getControlChange(); -} - -std::vector ApsSlider::PADDING = std::vector{0, 35, 64, 0, 26, 0}; - -int ApsSlider::getNote() const -{ - return note; -} - -int ApsSlider::getTuneLow() const -{ - return tuneLow; -} - -int ApsSlider::getTuneHigh() const -{ - return tuneHigh; -} - -int ApsSlider::getDecayLow() const -{ - return decayLow; -} - -int ApsSlider::getDecayHigh() const -{ - return decayHigh; -} - -int ApsSlider::getAttackLow() const -{ - return attackLow; -} - -int ApsSlider::getAttackHigh() const -{ - return attackHigh; -} - -int ApsSlider::getFilterLow() const -{ - return filterLow; -} - -int ApsSlider::getFilterHigh() const -{ - return filterHigh; -} - -int ApsSlider::getProgramChange() const -{ - return programChange; -} - -std::vector ApsSlider::getBytes() -{ - return saveBytes; -} diff --git a/src/main/file/aps/ApsSlider.hpp b/src/main/file/aps/ApsSlider.hpp deleted file mode 100644 index d3e43dd7d..000000000 --- a/src/main/file/aps/ApsSlider.hpp +++ /dev/null @@ -1,47 +0,0 @@ -#pragma once - -#include - -namespace mpc::sampler -{ - class PgmSlider; -} - -namespace mpc::file::aps -{ - class ApsSlider - { - - public: - int note; - int tuneLow; - int tuneHigh; - int decayLow; - int decayHigh; - int attackLow; - int attackHigh; - int filterLow; - int filterHigh; - int programChange; - std::vector saveBytes; - - private: - static std::vector PADDING; - - public: - int getNote() const; - int getTuneLow() const; - int getTuneHigh() const; - int getDecayLow() const; - int getDecayHigh() const; - int getAttackLow() const; - int getAttackHigh() const; - int getFilterLow() const; - int getFilterHigh() const; - int getProgramChange() const; - std::vector getBytes(); - - ApsSlider(const std::vector &loadBytes); - ApsSlider(sampler::PgmSlider *slider); - }; -} // namespace mpc::file::aps diff --git a/src/main/file/aps/ApsSoundNames.cpp b/src/main/file/aps/ApsSoundNames.cpp deleted file mode 100644 index 87cacaba3..000000000 --- a/src/main/file/aps/ApsSoundNames.cpp +++ /dev/null @@ -1,64 +0,0 @@ -#include "file/aps/ApsSoundNames.hpp" - -#include "file/aps/ApsParser.hpp" -#include "sampler/Sampler.hpp" - -#include "StrUtil.hpp" - -#include "Util.hpp" - -using namespace mpc::file::aps; - -ApsSoundNames::ApsSoundNames(const std::vector &loadBytes) -{ - int sound = 0; - - while (sound < loadBytes.size() / ApsParser::SOUND_NAME_LENGTH) - { - auto nameBytes = Util::vecCopyOfRange( - loadBytes, sound * ApsParser::SOUND_NAME_LENGTH, - sound * ApsParser::SOUND_NAME_LENGTH + NAME_STRING_LENGTH); - - std::string nameStr; - for (char c : nameBytes) - { - if (c == 0x00) - { - break; - } - nameStr.push_back(c); - } - - names.push_back(nameStr); - sound++; - } -} - -ApsSoundNames::ApsSoundNames(sampler::Sampler *sampler) -{ - saveBytes = std::vector(sampler->getSoundCount() * - ApsParser::SOUND_NAME_LENGTH); - for (int i = 0; i < sampler->getSoundCount(); i++) - { - int offset = i * ApsParser::SOUND_NAME_LENGTH; - for (int j = 0; j < NAME_STRING_LENGTH; j++) - { - saveBytes[offset + j] = StrUtil::padRight( - sampler->getSound(i)->getName(), " ", NAME_STRING_LENGTH)[j]; - } - - saveBytes[offset + NAME_STRING_LENGTH] = ApsParser::NAME_TERMINATOR; - } -} - -const int ApsSoundNames::NAME_STRING_LENGTH; - -std::vector ApsSoundNames::get() -{ - return names; -} - -std::vector ApsSoundNames::getBytes() -{ - return saveBytes; -} diff --git a/src/main/file/aps/ApsSoundNames.hpp b/src/main/file/aps/ApsSoundNames.hpp deleted file mode 100644 index 2a425c293..000000000 --- a/src/main/file/aps/ApsSoundNames.hpp +++ /dev/null @@ -1,27 +0,0 @@ -#pragma once - -#include -#include - -namespace mpc::sampler -{ - class Sampler; -} - -namespace mpc::file::aps -{ - class ApsSoundNames - { - public: - ApsSoundNames(const std::vector &loadBytes); - ApsSoundNames(sampler::Sampler *sampler); - - static const int NAME_STRING_LENGTH = 16; - - std::vector names; - std::vector saveBytes; - - std::vector get(); - std::vector getBytes(); - }; -} // namespace mpc::file::aps diff --git a/src/main/file/kaitai/AllIo.cpp b/src/main/file/kaitai/AllIo.cpp new file mode 100644 index 000000000..d988c695e --- /dev/null +++ b/src/main/file/kaitai/AllIo.cpp @@ -0,0 +1,1078 @@ +#include "file/kaitai/AllIo.hpp" + +#include "Mpc.hpp" +#include "controller/ClientEventController.hpp" +#include "controller/ClientMidiEventController.hpp" +#include "controller/ClientMidiFootswitchAssignmentController.hpp" +#include "controller/MidiFootswitchFunctionMap.hpp" +#include "disk/AllLoader.hpp" +#include "disk/MpcFile.hpp" +#include "file/kaitai/KaitaiIoUtil.hpp" +#include "file/kaitai/generated/mpc2000xl_all.h" +#include "input/midi/MidiControlTargetBinding.hpp" +#include "lcdgui/screens/OthersScreen.hpp" +#include "lcdgui/screens/SecondSeqScreen.hpp" +#include "lcdgui/screens/SongScreen.hpp" +#include "lcdgui/screens/SyncScreen.hpp" +#include "lcdgui/screens/UserScreen.hpp" +#include "lcdgui/screens/dialog/MetronomeSoundScreen.hpp" +#include "lcdgui/screens/window/CountMetronomeScreen.hpp" +#include "lcdgui/screens/window/IgnoreTempoChangeScreen.hpp" +#include "lcdgui/screens/window/LocateScreen.hpp" +#include "lcdgui/screens/window/MidiInputScreen.hpp" +#include "lcdgui/screens/window/MidiOutputScreen.hpp" +#include "lcdgui/screens/window/MultiRecordingSetupScreen.hpp" +#include "lcdgui/screens/window/StepEditOptionsScreen.hpp" +#include "lcdgui/screens/window/TimeDisplayScreen.hpp" +#include "lcdgui/screens/window/TimingCorrectScreen.hpp" +#include "sequencer/EventRef.hpp" +#include "sequencer/EventType.hpp" +#include "sequencer/Sequence.hpp" +#include "sequencer/Sequencer.hpp" +#include "sequencer/Song.hpp" +#include "sequencer/Track.hpp" +#include "sequencer/Transport.hpp" + +#include + +#include +#include +#include +#include +#include + +using namespace mpc::file::kaitai; + +namespace { + +using all_t = mpc2000xl_all_t; +constexpr size_t kVisibleNameLength = 16; +constexpr size_t kSongStepCount = 250; +constexpr size_t kMultiRecTrackCount = 34; +constexpr size_t kSequenceCount = 99; +constexpr size_t kSongCount = 20; +constexpr char kAllUnknown10[] = {0, 12, 0, 0, 0, 0, 0, 0, 0, 0}; +constexpr size_t kSequenceHeaderLength = 10240; +constexpr size_t kSequenceTrackChunkLength = 1764; +constexpr size_t kSequenceBarChunkOffset = 5379; +constexpr size_t kSequenceBarChunkLength = 3996; +constexpr size_t kSequenceEventsOffset = 10240; +constexpr size_t kSequenceEventSegmentLength = 8; +constexpr char kSequencePadding1[] = {1, 1, 0}; +constexpr char kSequencePadding2[] = {4, 4}; +constexpr char kSequenceTrackPadding[] = { + static_cast(232), 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, static_cast(232), 3 +}; +constexpr char kSequencePadding4[] = {40, 0, static_cast(128), 0, 0}; +constexpr char kSequenceTerminator[] = { + static_cast(0xFF), static_cast(0xFF), static_cast(0xFF), static_cast(0xFF), + static_cast(0xFF), static_cast(0xFF), static_cast(0xFF), static_cast(0xFF) +}; +constexpr char kDefaultsUnknown1[] = {1, 0, 0, 1, 1, 0}; +constexpr char kDefaultsUnknown2[] = { + 0, 0, static_cast(0xFF), static_cast(0xFF), 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32, 32, 32, 32, 32, 32, 32, 32 +}; + +std::vector slice(const std::vector& bytes, const size_t offset, const size_t length) +{ + return std::vector(bytes.begin() + static_cast(offset), + bytes.begin() + static_cast(offset + length)); +} + +template +std::unique_ptr parseSection(const std::vector& bytes, all_t& root) +{ + std::stringstream parseStream( + std::string(bytes.begin(), bytes.end()), + std::ios::in | std::ios::out | std::ios::binary + ); + ::kaitai::kstream parseIo(&parseStream); + auto parsed = std::make_unique(&parseIo, &root, &root); + parsed->_read(); + return parsed; +} + +template +std::vector writeStruct(T& value) +{ + std::stringstream writeStream(std::ios::in | std::ios::out | std::ios::binary); + ::kaitai::kstream writeIo(&writeStream); + value._set_io(&writeIo); + value._check(); + value._write(); + + const auto written = writeStream.str(); + return std::vector(written.begin(), written.end()); +} + +std::string padRight(const std::string& value, const size_t width, const char fill = ' ') +{ + if (value.size() >= width) + { + return value.substr(0, width); + } + return value + std::string(width - value.size(), fill); +} + +std::unique_ptr makeDefaults(mpc::Mpc& mpc, all_t& root) +{ + constexpr size_t kDefaultsLength = 1792; + constexpr size_t kDefaultSeqNameOffset = 0; + constexpr size_t kDefaultsUnknown1Offset = 16; + constexpr size_t kTempoOffset = 22; + constexpr size_t kNumeratorOffset = 24; + constexpr size_t kDenominatorOffset = 25; + constexpr size_t kBarCountOffset = 26; + constexpr size_t kTickCountOffset = 28; + constexpr size_t kUnknownNumberOffset = 32; + constexpr size_t kDefaultsUnknown2Offset = 48; + constexpr size_t kLoopEnabledOffset = 52; +constexpr size_t kDeviceNamesOffset = 120; +constexpr size_t kDefaultsUnknown2Length = 68; + constexpr size_t kTrackNamesOffset = 384; + constexpr size_t kDevicesOffset = 1408; + constexpr size_t kBusesOffset = 1472; + constexpr size_t kProgramsOffset = 1536; + constexpr size_t kTrackVelocitiesOffset = 1600; + constexpr size_t kTrackStatusesOffset = 1664; + constexpr size_t kDefaultDeviceNameLength = 8; + + auto bytes = std::vector(kDefaultsLength, char{0}); + const auto sequencer = mpc.getSequencer(); + const auto userScreen = mpc.screens->get(); + + const auto sequenceName = padRight(sequencer->getDefaultSequenceName(), kVisibleNameLength); + std::copy(sequenceName.begin(), sequenceName.end(), bytes.begin() + static_cast(kDefaultSeqNameOffset)); + + std::copy(std::begin(kDefaultsUnknown1), std::end(kDefaultsUnknown1), bytes.begin() + static_cast(kDefaultsUnknown1Offset)); + + const auto tempo = static_cast(userScreen->getTempo() * 10.0); + bytes[kTempoOffset] = static_cast(tempo & 0xFF); + bytes[kTempoOffset + 1] = static_cast((tempo >> 8) & 0xFF); + bytes[kNumeratorOffset] = static_cast(userScreen->getTimeSig().numerator); + bytes[kDenominatorOffset] = static_cast(userScreen->getTimeSig().denominator); + + const auto barCount = static_cast(userScreen->getLastBar() + 1); + bytes[kBarCountOffset] = static_cast(barCount & 0xFF); + bytes[kBarCountOffset + 1] = static_cast((barCount >> 8) & 0xFF); + + auto lastBar = userScreen->getLastBar(); + uint16_t tickCount = static_cast((lastBar + 1) * 384); + if (lastBar == 1) + { + tickCount = 0; + lastBar = 0; + } + bytes[kTickCountOffset] = static_cast(tickCount & 0xFF); + bytes[kTickCountOffset + 1] = static_cast((tickCount >> 8) & 0xFF); + + const auto unknownNumber = static_cast((lastBar + 1) * 2000000); + for (int i = 0; i < 4; ++i) + { + bytes[kUnknownNumberOffset + i * 4] = static_cast(unknownNumber & 0xFF); + bytes[kUnknownNumberOffset + i * 4 + 1] = static_cast((unknownNumber >> 8) & 0xFF); + bytes[kUnknownNumberOffset + i * 4 + 2] = static_cast((unknownNumber >> 16) & 0xFF); + bytes[kUnknownNumberOffset + i * 4 + 3] = static_cast((unknownNumber >> 24) & 0xFF); + } + + std::copy( + std::begin(kDefaultsUnknown2), + std::begin(kDefaultsUnknown2) + static_cast(kDefaultsUnknown2Length), + bytes.begin() + static_cast(kDefaultsUnknown2Offset) + ); + bytes[kLoopEnabledOffset] = static_cast(userScreen->isLoopEnabled() ? 0x01 : 0x00); + + for (int i = 0; i < 33; ++i) + { + const auto deviceName = i == 0 + ? std::string(kDefaultDeviceNameLength, ' ') + : std::string("Device") + (i < 10 ? std::string("0") : std::string()) + std::to_string(i); + std::copy( + deviceName.begin(), + deviceName.end(), + bytes.begin() + static_cast(kDeviceNamesOffset + i * kDefaultDeviceNameLength) + ); + } + + for (int i = 0; i < 64; ++i) + { + const auto trackName = padRight(sequencer->getDefaultTrackName(i), kVisibleNameLength); + std::copy( + trackName.begin(), + trackName.end(), + bytes.begin() + static_cast(kTrackNamesOffset + i * kVisibleNameLength) + ); + bytes[kDevicesOffset + i] = static_cast(userScreen->getDevice()); + bytes[kBusesOffset + i] = static_cast(mpc::sequencer::busTypeToIndex(userScreen->getBusType())); + bytes[kProgramsOffset + i] = static_cast(userScreen->getPgm()); + bytes[kTrackVelocitiesOffset + i] = static_cast(userScreen->getVelo()); + bytes[kTrackStatusesOffset + i] = static_cast(userScreen->getTrackStatus()); + } + + return parseSection(bytes, root); +} + +std::unique_ptr makeSequencer(mpc::Mpc& mpc, all_t& root) +{ + auto sequencer = std::make_unique(nullptr, &root, &root); + const auto mpcSequencer = mpc.getSequencer(); + const auto transport = mpcSequencer->getTransport(); + auto timingCorrectScreen = mpc.screens->get(); + auto timeDisplayScreen = mpc.screens->get(); + auto secondSeqScreen = mpc.screens->get(); + + sequencer->set_active_sequence(mpcSequencer->getSelectedSequenceIndex()); + sequencer->set__unnamed1(std::string(1, char{0})); + sequencer->set_active_track(mpcSequencer->getSelectedTrackIndex()); + sequencer->set__unnamed3(std::string(1, char{0})); + sequencer->set_master_tempo(static_cast(transport->getMasterTempo() * 10.0)); + sequencer->set_tempo_source_is_sequence(transport->isTempoSourceSequence()); + sequencer->set__unnamed6(0); + sequencer->set_timing_correct(static_cast(timingCorrectScreen->getNoteValue())); + sequencer->set_time_display_style(static_cast(timeDisplayScreen->getDisplayStyle())); + sequencer->set__unnamed9(0); + sequencer->set_second_sequence_enabled(mpcSequencer->isSecondSequenceEnabled()); + sequencer->set__unnamed11(0); + sequencer->set_sequence_sequence_index(secondSeqScreen->getSq()); + return sequencer; +} + +std::unique_ptr makeCount(mpc::Mpc& mpc, all_t& root) +{ + auto count = std::make_unique(nullptr, &root, &root); + const auto transport = mpc.getSequencer()->getTransport(); + auto countMetronomeScreen = mpc.screens->get(); + auto metronomeSoundScreen = mpc.screens->get(); + + count->set_enabled(transport->isCountEnabled()); + count->set__unnamed1(0); + count->set_count_in_mode(static_cast(countMetronomeScreen->getCountInMode())); + count->set_click_volume(static_cast(metronomeSoundScreen->getVolume())); + count->set_rate(static_cast(countMetronomeScreen->getRate())); + count->set_enabled_in_play(countMetronomeScreen->getInPlay()); + count->set__unnamed6(0); + count->set_enabled_in_rec(countMetronomeScreen->getInRec()); + count->set__unnamed8(0); + count->set_click_output(static_cast(metronomeSoundScreen->getOutput())); + count->set_wait_for_key(countMetronomeScreen->isWaitForKeyEnabled()); + count->set__unnamed11(0); + count->set_sound_source(static_cast(metronomeSoundScreen->getSound())); + count->set_accent_pad_index(static_cast(metronomeSoundScreen->getAccentPad())); + count->set_normal_pad_index(static_cast(metronomeSoundScreen->getNormalPad())); + count->set_accent_velo(static_cast(metronomeSoundScreen->getAccentVelo())); + count->set_normal_velo(static_cast(metronomeSoundScreen->getNormalVelo())); + return count; +} + +std::unique_ptr makeMidiOutput(mpc::Mpc& mpc, all_t& root) +{ + auto midiOutput = std::make_unique(nullptr, &root, &root); + auto midiOutputScreen = mpc.screens->get(); + midiOutput->set_soft_thru_mode(static_cast(midiOutputScreen->getSoftThru())); + return midiOutput; +} + +std::unique_ptr makeMidiInput(mpc::Mpc& mpc, all_t& root) +{ + auto midiInput = std::make_unique(nullptr, &root, &root); + auto midiInputScreen = mpc.screens->get(); + auto multiRecordingSetupScreen = mpc.screens->get(); + + midiInput->set_receive_channel(static_cast(midiInputScreen->getReceiveCh() + 1)); + midiInput->set_sustain_pedal_to_duration(midiInputScreen->isSustainPedalToDurationEnabled()); + midiInput->set__unnamed2(0); + midiInput->set_filter_enabled(midiInputScreen->isMidiFilterEnabled()); + midiInput->set__unnamed4(0); + midiInput->set_filter_type(static_cast(midiInputScreen->getType())); + midiInput->set_multi_rec_enabled(mpc.getSequencer()->isRecordingModeMulti()); + midiInput->set__unnamed7(0); + + auto multiRecTracks = std::make_unique>(); + multiRecTracks->reserve(kMultiRecTrackCount); + for (auto* line : multiRecordingSetupScreen->getMrsLines()) + { + multiRecTracks->push_back(static_cast(line->getTrack() + 1)); + } + midiInput->set_multi_rec_destination_tracks(std::move(multiRecTracks)); + + midiInput->set_note_pass_enabled(midiInputScreen->isNotePassEnabled()); + midiInput->set__unnamed10(0); + midiInput->set_pitch_bend_pass_enabled(midiInputScreen->isPitchBendPassEnabled()); + midiInput->set__unnamed12(0); + midiInput->set_pgm_change_pass_enabled(midiInputScreen->isPgmChangePassEnabled()); + midiInput->set__unnamed14(0); + midiInput->set_ch_pressure_pass_enabled(midiInputScreen->isChPressurePassEnabled()); + midiInput->set__unnamed16(0); + midiInput->set_poly_pressure_pass_enabled(midiInputScreen->isPolyPressurePassEnabled()); + midiInput->set__unnamed18(0); + midiInput->set_exclusive_pass_enabled(midiInputScreen->isExclusivePassEnabled()); + midiInput->set__unnamed20(0); + + auto ccPass = std::make_unique>(midiInputScreen->getCcPassEnabled()); + midiInput->set_cc_pass_enabled(std::move(ccPass)); + return midiInput; +} + +std::unique_ptr makeMidiSync(mpc::Mpc& mpc, all_t& root) +{ + auto midiSync = std::make_unique(nullptr, &root, &root); + auto syncScreen = mpc.screens->get(); + midiSync->set_in_mode(static_cast(syncScreen->getModeIn())); + midiSync->set_out_mode(static_cast(syncScreen->getModeOut())); + midiSync->set_shift_early(static_cast(syncScreen->getShiftEarly())); + midiSync->set_send_mmc_enabled(syncScreen->isSendMMCEnabled()); + midiSync->set__unnamed4(0); + midiSync->set_frame_rate(static_cast(syncScreen->getFrameRate())); + midiSync->set_input(static_cast(syncScreen->getInput())); + midiSync->set_output(static_cast(syncScreen->getOut())); + return midiSync; +} + +std::unique_ptr makeSongGlobal(mpc::Mpc& mpc, all_t& root) +{ + auto songGlobal = std::make_unique(nullptr, &root, &root); + auto songScreen = mpc.screens->get(); + auto ignoreTempoChangeScreen = mpc.screens->get(); + songGlobal->set_default_song_name(padRight(songScreen->getDefaultSongName(), 16)); + songGlobal->set_ignore_tempo_change_events_in_sequence(ignoreTempoChangeScreen->getIgnore()); + songGlobal->set__unnamed2(0); + return songGlobal; +} + +std::unique_ptr>> makeLocations(mpc::Mpc& mpc, all_t& root) +{ + auto locateScreen = mpc.screens->get(); + auto result = std::make_unique>>(); + result->reserve(locateScreen->getLocations().size()); + + for (const auto& location : locateScreen->getLocations()) + { + auto parsedLocation = std::make_unique(nullptr, &root, &root); + parsedLocation->set_bar(std::get<0>(location)); + parsedLocation->set_beat(std::get<1>(location)); + parsedLocation->set_clock(std::get<2>(location)); + result->push_back(std::move(parsedLocation)); + } + + return result; +} + +std::unique_ptr makeMisc(mpc::Mpc& mpc, all_t& root) +{ + auto misc = std::make_unique(nullptr, &root, &root); + auto othersScreen = mpc.screens->get(); + auto syncScreen = mpc.screens->get(); + auto footswitchController = + mpc.clientEventController->getClientMidiEventController() + ->getFootswitchAssignmentController(); + + misc->set_tap_averaging(static_cast(othersScreen->getTapAveraging() - 2)); + misc->set_midi_sync_in_receive_mmc_enabled(syncScreen->isReceiveMMCEnabled()); + + auto midiSwitches = std::make_unique>>(); + midiSwitches->reserve(footswitchController->bindings.size()); + for (const auto& bindingVariant : footswitchController->bindings) + { + auto midiSwitch = std::make_unique(nullptr, misc.get(), &root); + std::visit( + [&](const auto& binding) + { + midiSwitch->set_controller(static_cast(binding.number < 0 ? 0xFF : binding.number)); + if constexpr (std::is_same_v, mpc::input::midi::HardwareBinding>) + { + const auto fn = mpc::controller::componentIdToFootswitch(binding.target.componentId).value(); + midiSwitch->set_function(static_cast(fn)); + } + else + { + const auto fn = mpc::controller::sequencerCmdToFootswitch(binding.target.command).value(); + midiSwitch->set_function(static_cast(fn)); + } + }, + bindingVariant + ); + midiSwitches->push_back(std::move(midiSwitch)); + } + misc->set_midi_switch(std::move(midiSwitches)); + return misc; +} + +std::unique_ptr makeStepEditOptions(mpc::Mpc& mpc, all_t& root) +{ + auto stepEditOptions = std::make_unique(nullptr, &root, &root); + auto screen = mpc.screens->get(); + stepEditOptions->set_auto_step_increment(screen->isAutoStepIncrementEnabled()); + stepEditOptions->set_duration_of_recorded_notes( + screen->isDurationOfRecordedNotesTcValue() + ? all_t::DURATION_OF_RECORDED_NOTES_TC_VALUE + : all_t::DURATION_OF_RECORDED_NOTES_AS_PLAYED + ); + stepEditOptions->set_tc_value_percentage(static_cast(screen->getTcValuePercentage())); + return stepEditOptions; +} + +int countSequenceMetaSegments(mpc::sequencer::Sequence& seq); +int countSequenceBodySegments(mpc::sequencer::Sequence& seq); + +std::unique_ptr>> makeSequenceMetas(mpc::Mpc& mpc, all_t& root) +{ + auto result = std::make_unique>>(); + const auto sequencer = mpc.getSequencer(); + result->reserve(kSequenceCount); + + for (size_t i = 0; i < kSequenceCount; ++i) + { + auto meta = std::make_unique(nullptr, &root, &root); + const auto seq = sequencer->getSequence(static_cast(i)); + meta->set_name(padRight(seq->getName(), kVisibleNameLength)); + auto segmentCount = countSequenceMetaSegments(*seq); + if ((segmentCount & 1) != 0) + { + segmentCount--; + } + auto lastEventIndex = 0; + if (seq->isUsed()) + { + lastEventIndex = 641 + std::max(0, segmentCount / 2); + } + meta->set_last_event_index(static_cast(lastEventIndex)); + result->push_back(std::move(meta)); + } + + return result; +} + +std::unique_ptr makeSongStep(uint8_t sequenceIndex, uint8_t repeatCount, all_t::song_t* parent, all_t& root) +{ + auto step = std::make_unique(nullptr, parent, &root); + step->set_sequence_index(sequenceIndex); + step->set_repeat_count(repeatCount); + return step; +} + +std::unique_ptr>> makeSongs(mpc::Mpc& mpc, all_t& root) +{ + auto result = std::make_unique>>(); + auto sequencer = mpc.getSequencer(); + result->reserve(kSongCount); + + for (size_t i = 0; i < kSongCount; ++i) + { + auto song = std::make_unique(nullptr, &root, &root); + const auto mpcSong = sequencer->getSong(static_cast(i)); + song->set_name(padRight(mpcSong->getName(), kVisibleNameLength)); + + auto steps = std::make_unique>>(); + steps->reserve(kSongStepCount); + for (size_t stepIndex = 0; stepIndex < kSongStepCount; ++stepIndex) + { + if (stepIndex < mpcSong->getStepCount()) + { + const auto stepState = mpcSong->getStep(mpc::SongStepIndex(static_cast(stepIndex))); + steps->push_back(makeSongStep(stepState.sequenceIndex, stepState.repetitionCount, song.get(), root)); + } + else + { + steps->push_back(makeSongStep(0xFF, 0xFF, song.get(), root)); + } + } + song->set_steps(std::move(steps)); + song->set__unnamed2(std::string(2, static_cast(0xFF))); + song->set_is_used(mpcSong->isUsed()); + song->set_loop_first_step(static_cast(mpcSong->getFirstLoopStepIndex())); + song->set_loop_last_step(static_cast(mpcSong->getLastLoopStepIndex())); + song->set_is_loop_enabled(mpcSong->isLoopEnabled()); + song->set__unnamed7(std::string(6, char{0})); + result->push_back(std::move(song)); + } + + return result; +} + +int countSequenceMetaSegments(mpc::sequencer::Sequence& seq) +{ + int segmentCount = 0; + for (const auto& track : seq.getTracks()) + { + if (track->getIndex() > 63) + { + break; + } + + for (const auto& event : track->getEvents()) + { + switch (event->handle->type) + { + case mpc::sequencer::EventType::SystemExclusive: + segmentCount += 2; + break; + case mpc::sequencer::EventType::Mixer: + segmentCount += 2; + break; + default: + segmentCount += 1; + break; + } + } + } + return segmentCount; +} + +int countSequenceBodySegments(mpc::sequencer::Sequence& seq) +{ + int segmentCount = 0; + for (const auto& track : seq.getTracks()) + { + if (track->getIndex() > 63) + { + break; + } + + for (const auto& event : track->getEvents()) + { + switch (event->handle->type) + { + case mpc::sequencer::EventType::SystemExclusive: + segmentCount += 3; + break; + case mpc::sequencer::EventType::Mixer: + segmentCount += 4; + break; + default: + segmentCount += 1; + break; + } + } + } + return segmentCount; +} + +std::vector makeTrackChunk(mpc::sequencer::Sequence& seq) +{ + std::vector bytes(kSequenceTrackChunkLength, char{0}); + for (int i = 0; i < 64; ++i) + { + const auto track = seq.getTrack(i); + const auto trackName = padRight(track->getName(), kVisibleNameLength); + std::copy(trackName.begin(), trackName.end(), bytes.begin() + i * kVisibleNameLength); + bytes[1024 + i] = static_cast(track->getDeviceIndex()); + bytes[1088 + i] = static_cast(mpc::sequencer::busTypeToIndex(track->getBusType())); + bytes[1152 + i] = static_cast(track->getProgramChange()); + bytes[1216 + i] = static_cast(track->getVelocityRatio()); + + uint8_t status = 0; + if (track->isUsed()) + { + status |= 1u << 0; + } + if (track->isOn()) + { + status |= 1u << 1; + } + if (track->isTransmitProgramChangesEnabled()) + { + status |= 1u << 2; + } + bytes[1280 + i] = static_cast(status); + } + + std::copy(std::begin(kSequenceTrackPadding), std::end(kSequenceTrackPadding), bytes.begin() + 1408); + + const auto lastTick = static_cast(seq.getLastTick()); + const auto remainder = static_cast(lastTick % 65535); + const auto large = static_cast(std::floor(lastTick / 65536.0)); + bytes[1424] = static_cast(remainder & 0xFF); + bytes[1425] = static_cast((remainder >> 8) & 0xFF); + bytes[1426] = static_cast(large); + + const auto unknown1 = static_cast(10000000); + const auto unknown2 = static_cast(seq.getLastTick() * 5208.333333333333); + bytes[1428] = static_cast(unknown1 & 0xFF); + bytes[1429] = static_cast((unknown1 >> 8) & 0xFF); + bytes[1430] = static_cast((unknown1 >> 16) & 0xFF); + bytes[1431] = static_cast((unknown1 >> 24) & 0xFF); + bytes[1432] = static_cast(unknown2 & 0xFF); + bytes[1433] = static_cast((unknown2 >> 8) & 0xFF); + bytes[1434] = static_cast((unknown2 >> 16) & 0xFF); + bytes[1435] = static_cast((unknown2 >> 24) & 0xFF); + return bytes; +} + +std::vector makeBarChunk(mpc::sequencer::Sequence& seq) +{ + std::vector bytes(kSequenceBarChunkLength, char{0}); + int ticksPerBeat = 0; + int lastTick = 0; + + for (int i = 0; i < seq.getLastBarIndex() + 1; ++i) + { + const auto barLength = seq.getBarLength(i); + lastTick += barLength; + ticksPerBeat = barLength / seq.getNumerator(i); + bytes[i * 4] = static_cast(ticksPerBeat); + bytes[i * 4 + 1] = static_cast(lastTick & 0xFF); + bytes[i * 4 + 2] = static_cast((lastTick >> 8) & 0xFF); + bytes[i * 4 + 3] = static_cast((lastTick >> 16) & 0xFF); + } + + const auto terminatorOffset = static_cast((seq.getLastBarIndex() + 1) * 4); + bytes[terminatorOffset] = static_cast(ticksPerBeat); + return bytes; +} + + +std::unique_ptr makeTerminatorEvent() +{ + auto event = std::make_unique(nullptr, nullptr, nullptr); + event->set_tick(0xFFFFF); + event->set_terminator_pad(0xF); + event->set_terminator(std::string(5, static_cast(0xFF))); + return event; +} + +std::unique_ptr makeNoteEvent(const mpc::sequencer::EventData& state, const uint8_t trackIndex) +{ + auto event = std::make_unique(nullptr, nullptr, nullptr); + const auto duration = static_cast(state.duration); + event->set_tick(state.tick); + event->set_duration_bits_1((duration >> 10) & 0x0F); + event->set_track(trackIndex); + event->set_duration_bits_2((duration >> 8) & 0x03); + event->set_id(state.noteNumber); + + auto note = std::make_unique(nullptr, event.get(), nullptr); + note->set_duration_bits_3(duration & 0xFF); + note->set_velocity(state.velocity); + note->set_variation_type_bit_1( + state.noteVariationType == mpc::NoteVariationTypeAttack || state.noteVariationType == mpc::NoteVariationTypeFilter + ); + note->set_variation_value(state.noteVariationValue); + note->set_variation_type_bit_2( + state.noteVariationType == mpc::NoteVariationTypeDecay || state.noteVariationType == mpc::NoteVariationTypeFilter + ); + event->set_note_event(std::move(note)); + return event; +} + +std::unique_ptr makePitchBendEvent(const mpc::sequencer::EventData& state, const uint8_t trackIndex) +{ + auto event = std::make_unique(nullptr, nullptr, nullptr); + event->set_tick(state.tick); + event->set_duration_bits_1(0); + event->set_track(trackIndex); + event->set_duration_bits_2(0); + event->set_id(0xE0); + + auto pitchBend = std::make_unique(nullptr, event.get(), nullptr); + uint16_t candidate = static_cast(state.amount + 16384); + if (state.amount < 0) + { + candidate = static_cast(state.amount + 8192); + } + pitchBend->set_amount_bits_1(candidate & 0xFF); + pitchBend->set_amount_bits_2((candidate >> 8) & 0xFF); + pitchBend->set__unnamed2(std::string(1, char{0})); + event->set_pitch_bend(std::move(pitchBend)); + return event; +} + +std::unique_ptr makeControlChangeEvent(const mpc::sequencer::EventData& state, const uint8_t trackIndex) +{ + auto event = std::make_unique(nullptr, nullptr, nullptr); + event->set_tick(state.tick); + event->set_duration_bits_1(0); + event->set_track(trackIndex); + event->set_duration_bits_2(0); + event->set_id(0xB0); + + auto controlChange = std::make_unique(nullptr, event.get(), nullptr); + controlChange->set_controller(static_cast(state.controllerNumber)); + controlChange->set_value(state.controllerValue); + controlChange->set__unnamed2(std::string(1, char{0})); + event->set_control_change(std::move(controlChange)); + return event; +} + +std::unique_ptr makeProgramChangeEvent(const mpc::sequencer::EventData& state, const uint8_t trackIndex) +{ + auto event = std::make_unique(nullptr, nullptr, nullptr); + event->set_tick(state.tick); + event->set_duration_bits_1(0); + event->set_track(trackIndex); + event->set_duration_bits_2(0); + event->set_id(0xC0); + + auto programChange = std::make_unique(nullptr, event.get(), nullptr); + programChange->set_program(state.programChangeProgramIndex); + programChange->set__unnamed1(std::string(2, char{0})); + event->set_program_change(std::move(programChange)); + return event; +} + +std::unique_ptr makeChannelPressureEvent(const mpc::sequencer::EventData& state, const uint8_t trackIndex) +{ + auto event = std::make_unique(nullptr, nullptr, nullptr); + event->set_tick(state.tick); + event->set_duration_bits_1(0); + event->set_track(trackIndex); + event->set_duration_bits_2(0); + event->set_id(0xD0); + + auto chPressure = std::make_unique(nullptr, event.get(), nullptr); + chPressure->set_pressure(state.amount); + chPressure->set__unnamed1(std::string(2, char{0})); + event->set_ch_pressure(std::move(chPressure)); + return event; +} + +std::unique_ptr makePolyPressureEvent(const mpc::sequencer::EventData& state, const uint8_t trackIndex) +{ + auto event = std::make_unique(nullptr, nullptr, nullptr); + event->set_tick(state.tick); + event->set_duration_bits_1(0); + event->set_track(trackIndex); + event->set_duration_bits_2(0); + event->set_id(0xA0); + + auto polyPressure = std::make_unique(nullptr, event.get(), nullptr); + polyPressure->set_note(state.noteNumber); + polyPressure->set_pressure(state.amount); + polyPressure->set__unnamed2(std::string(1, char{0})); + event->set_poly_pressure(std::move(polyPressure)); + return event; +} + +std::unique_ptr makeExclusiveEvent(const mpc::sequencer::EventData& state, const uint8_t trackIndex) +{ + auto event = std::make_unique(nullptr, nullptr, nullptr); + event->set_tick(state.tick); + event->set_duration_bits_1(0); + event->set_track(trackIndex); + event->set_duration_bits_2(0); + event->set_id(0xF0); + + auto exclusive = std::make_unique(nullptr, event.get(), nullptr); + if (state.type == mpc::sequencer::EventType::Mixer) + { + exclusive->set__unnamed0(std::string({9, 0, 0})); + exclusive->set_bytes(std::string({static_cast(0xF0), 0x47})); + + auto mixer = std::make_unique(nullptr, exclusive.get(), nullptr); + mixer->set__unnamed0(std::string({0, 68, 69})); + auto param = state.mixerParameter; + if (param == 3) + { + param = 4; + } + mixer->set_param(static_cast(param + 1)); + mixer->set_pad_index(state.mixerPad); + mixer->set_value(state.mixerValue); + mixer->set__unnamed4(std::string({static_cast(0xF7), 0})); + exclusive->set_mixer(std::move(mixer)); + + auto tail = std::string(14, char{0}); + tail[10] = static_cast(0xF8); + exclusive->set__unnamed3(std::move(tail)); + } + else + { + exclusive->set__unnamed0(std::string({2, 0, 0})); + exclusive->set_bytes(std::string({static_cast(state.sysExByteA), static_cast(state.sysExByteB)})); + + auto tail = std::string(14, char{0}); + tail[10] = static_cast(0xF8); + exclusive->set__unnamed3(std::move(tail)); + } + + event->set_exclusive(std::move(exclusive)); + return event; +} + +std::unique_ptr makeSequenceEvent(const mpc::sequencer::EventData& state, const uint8_t trackIndex) +{ + switch (state.type) + { + case mpc::sequencer::EventType::NoteOn: + return makeNoteEvent(state, trackIndex); + case mpc::sequencer::EventType::PitchBend: + return makePitchBendEvent(state, trackIndex); + case mpc::sequencer::EventType::ControlChange: + return makeControlChangeEvent(state, trackIndex); + case mpc::sequencer::EventType::ProgramChange: + return makeProgramChangeEvent(state, trackIndex); + case mpc::sequencer::EventType::ChannelPressure: + return makeChannelPressureEvent(state, trackIndex); + case mpc::sequencer::EventType::PolyPressure: + return makePolyPressureEvent(state, trackIndex); + case mpc::sequencer::EventType::Mixer: + case mpc::sequencer::EventType::SystemExclusive: + return makeExclusiveEvent(state, trackIndex); + default: + throw std::runtime_error("Unsupported ALL event type in Kaitai writer"); + } +} + +std::vector makeSequenceEventChunk(mpc::sequencer::Sequence& seq) +{ + std::vector chunk; + for (int tick = 0; tick < seq.getLastTick(); ++tick) + { + for (const auto& track : seq.getTracks()) + { + if (track->getIndex() > 63) + { + break; + } + + for (const auto& event : track->getEvents()) + { + if (event->getTick() != tick) + { + continue; + } + + auto kaitaiEvent = makeSequenceEvent(*event->handle, static_cast(track->getIndex())); + auto eventBytes = writeStruct(*kaitaiEvent); + chunk.insert(chunk.end(), eventBytes.begin(), eventBytes.end()); + } + } + } + + auto terminator = makeTerminatorEvent(); + auto terminatorBytes = writeStruct(*terminator); + chunk.insert(chunk.end(), terminatorBytes.begin(), terminatorBytes.end()); + return chunk; +} + +std::vector makeSequenceBytes(mpc::sequencer::Sequence& seq, int number) +{ + const auto segmentCountLastEventIndex = countSequenceMetaSegments(seq); + auto segmentCount = countSequenceBodySegments(seq); + const auto terminatorCount = (segmentCount & 1) == 0 ? 2 : 1; + auto bytes = std::vector( + kSequenceHeaderLength + segmentCount * kSequenceEventSegmentLength + terminatorCount * kSequenceEventSegmentLength, + char{0} + ); + + const auto sequenceName = padRight(seq.getName(), kVisibleNameLength); + std::copy(sequenceName.begin(), sequenceName.end(), bytes.begin()); + + auto lastEventSegmentIndex = segmentCountLastEventIndex; + if ((lastEventSegmentIndex & 1) != 0) + { + lastEventSegmentIndex--; + } + lastEventSegmentIndex /= 2; + const auto lastEventIndex = static_cast(1 + (lastEventSegmentIndex < 0 ? 0 : lastEventSegmentIndex)); + bytes[16] = static_cast(lastEventIndex & 0xFF); + bytes[17] = static_cast((lastEventIndex >> 8) & 0xFF); + + std::copy(std::begin(kSequencePadding1), std::end(kSequencePadding1), bytes.begin() + 19); + + const auto tempo = static_cast(seq.getInitialTempo() * 10.0); + bytes[22] = static_cast(tempo & 0xFF); + bytes[23] = static_cast((tempo >> 8) & 0xFF); + std::copy(std::begin(kSequencePadding2), std::end(kSequencePadding2), bytes.begin() + 24); + + const auto barCount = static_cast(seq.getLastBarIndex() + 1); + bytes[26] = static_cast(barCount & 0xFF); + bytes[27] = static_cast((barCount >> 8) & 0xFF); + + const auto lastTick = static_cast(seq.getLastTick()); + const auto lastTickRemainder = static_cast(lastTick % 65536); + const auto lastTickHigh = static_cast(lastTick / 65536); + bytes[28] = static_cast(lastTickRemainder & 0xFF); + bytes[29] = static_cast((lastTickRemainder >> 8) & 0xFF); + bytes[30] = static_cast(lastTickHigh); + bytes[64] = static_cast(lastTickRemainder & 0xFF); + bytes[65] = static_cast((lastTickRemainder >> 8) & 0xFF); + bytes[66] = static_cast(lastTickHigh); + + bytes[18] = static_cast(number); + + const auto unknown1 = static_cast(10000000); + const auto unknown2 = static_cast(seq.getLastTick() * 5208.333333333333); + for (int i = 0; i < 2; ++i) + { + bytes[32 + i * 4] = static_cast(unknown1 & 0xFF); + bytes[33 + i * 4] = static_cast((unknown1 >> 8) & 0xFF); + bytes[34 + i * 4] = static_cast((unknown1 >> 16) & 0xFF); + bytes[35 + i * 4] = static_cast((unknown1 >> 24) & 0xFF); + } + for (int i = 2; i < 4; ++i) + { + bytes[32 + i * 4] = static_cast(unknown2 & 0xFF); + bytes[33 + i * 4] = static_cast((unknown2 >> 8) & 0xFF); + bytes[34 + i * 4] = static_cast((unknown2 >> 16) & 0xFF); + bytes[35 + i * 4] = static_cast((unknown2 >> 24) & 0xFF); + } + + const auto loopFirst = static_cast(seq.getFirstLoopBarIndex()); + auto loopLast = static_cast(seq.getLastLoopBarIndex()); + bytes[48] = static_cast(loopFirst & 0xFF); + bytes[49] = static_cast((loopFirst >> 8) & 0xFF); + if (seq.getLastLoopBarIndex() == mpc::EndOfSequence) + { + loopLast = 0xFFFF; + } + bytes[50] = static_cast(loopLast & 0xFF); + bytes[51] = static_cast((loopLast >> 8) & 0xFF); + bytes[52] = static_cast(seq.isLoopEnabled() ? 1 : 0); + + const auto& startTime = seq.getStartTime(); + bytes[53] = static_cast(startTime.hours); + bytes[54] = static_cast(startTime.minutes); + bytes[55] = static_cast(startTime.seconds); + bytes[56] = static_cast(startTime.frames); + bytes[57] = static_cast(startTime.frameDecimals); + + std::copy(std::begin(kSequencePadding4), std::end(kSequencePadding4), bytes.begin() + 59); + + for (int i = 0; i < 33; ++i) + { + const auto deviceName = i == 0 + ? std::string(8, ' ') + : padRight(seq.getDeviceName(i - 1), 8); + std::copy(deviceName.begin(), deviceName.end(), bytes.begin() + 120 + i * 8); + } + + const auto trackBytes = makeTrackChunk(seq); + std::copy(trackBytes.begin(), trackBytes.end(), bytes.begin() + 384); + + const auto barBytes = makeBarChunk(seq); + std::copy(barBytes.begin(), barBytes.end(), bytes.begin() + static_cast(kSequenceBarChunkOffset)); + + const auto eventChunk = makeSequenceEventChunk(seq); + std::copy(eventChunk.begin(), eventChunk.end(), bytes.begin() + static_cast(kSequenceEventsOffset)); + + for (size_t i = bytes.size() - 8; i < bytes.size(); ++i) + { + bytes[i] = static_cast(0xFF); + } + + return bytes; +} + +std::unique_ptr>> makeSequences(mpc::Mpc& mpc, all_t& root) +{ + auto result = std::make_unique>>(); + + auto sequencer = mpc.getSequencer(); + const auto usedSequences = sequencer->getUsedSequences(); + + if (usedSequences.empty()) + { + return result; + } + + result->reserve(usedSequences.size() * 2); + + for (size_t usedSequenceCursor = 0; usedSequenceCursor < usedSequences.size(); ++usedSequenceCursor) + { + const auto sequenceIndex = sequencer->getUsedSequenceIndexes()[usedSequenceCursor]; + auto sequenceBytes = + makeSequenceBytes(*usedSequences[usedSequenceCursor], sequenceIndex + 1); + result->push_back(parseSection(sequenceBytes, root)); + + // On disk, an even body segment count is followed by an extra 0xFF + // terminator segment that Kaitai parses as an empty sequence record. + if ((countSequenceBodySegments(*usedSequences[usedSequenceCursor]) & 1) == 0) + { + result->push_back( + parseSection(std::vector(8, static_cast(0xFF)), root) + ); + } + } + + return result; +} + + +std::vector writeAll(all_t& root) +{ + return writeStruct(root); +} + +} // namespace + +void AllIo::loadEverything(mpc::Mpc &mpc, mpc::disk::MpcFile *file) +{ + const auto canonicalBytes = parseRewrite(file->getBytes()); + mpc::disk::AllLoader::loadEverythingFromCanonicalBytes(mpc, canonicalBytes); +} + +sequence_meta_infos_or_error AllIo::loadSequenceMetaInfos(mpc::Mpc &mpc, mpc::disk::MpcFile *file) +{ + const auto canonicalBytes = parseRewrite(file->getBytes()); + std::stringstream parseStream( + std::string(canonicalBytes.begin(), canonicalBytes.end()), + std::ios::in | std::ios::out | std::ios::binary + ); + ::kaitai::kstream parseIo(&parseStream); + mpc2000xl_all_t parsed(&parseIo); + parsed._read(); + + std::vector result; + const auto* sequenceMetas = parsed.sequences_metas(); + if (sequenceMetas == nullptr) + { + return result; + } + + for (const auto& sequenceMeta : *sequenceMetas) + { + result.push_back( + { + sequenceMeta->is_used(), + sequenceMeta->name() + } + ); + } + + return result; +} + +std::shared_ptr AllIo::loadOneSequence( + mpc::Mpc &mpc, + mpc::disk::MpcFile *file, + mpc::SequenceIndex sourceIndexInAllFile, + mpc::SequenceIndex destIndexInMpcMemory) +{ + const auto canonicalBytes = parseRewrite(file->getBytes()); + return mpc::disk::AllLoader::loadOneSequenceFromCanonicalBytes( + mpc, + canonicalBytes, + sourceIndexInAllFile, + destIndexInMpcMemory + ); +} + +std::vector AllIo::save(mpc::Mpc &mpc) +{ + all_t root(nullptr); + root.set_magic("MPC2KXL ALL 1.00"); + + root.set_defaults(makeDefaults(mpc, root)); + + root.set_sequencer(makeSequencer(mpc, root)); + root.set__unnamed3(std::string(std::begin(kAllUnknown10), std::end(kAllUnknown10))); + root.set_count(makeCount(mpc, root)); + root.set_midi_output(makeMidiOutput(mpc, root)); + root.set_midi_input(makeMidiInput(mpc, root)); + root.set_midi_sync(makeMidiSync(mpc, root)); + root.set_song_global(makeSongGlobal(mpc, root)); + root.set__unnamed9(std::string(5, char{0})); + root.set_locations(makeLocations(mpc, root)); + root.set_misc(makeMisc(mpc, root)); + root.set__unnamed12(std::string(3, char{0})); + root.set_step_edit_options(makeStepEditOptions(mpc, root)); + root.set_prog_change_to_seq(mpc.screens->get()->getProgChangeSeq()); + root.set__unnamed15(std::string(78, char{0})); + root.set_sequences_metas(makeSequenceMetas(mpc, root)); + root.set_songs(makeSongs(mpc, root)); + root.set_sequences(makeSequences(mpc, root)); + + return writeAll(root); +} diff --git a/src/main/file/kaitai/AllIo.hpp b/src/main/file/kaitai/AllIo.hpp new file mode 100644 index 000000000..1ffb86c8a --- /dev/null +++ b/src/main/file/kaitai/AllIo.hpp @@ -0,0 +1,39 @@ +#pragma once + +#include "IntTypes.hpp" +#include "mpc_types.hpp" + +#include +#include + +namespace mpc +{ + class Mpc; +} + +namespace mpc::disk +{ + class MpcFile; +} + +namespace mpc::sequencer +{ + class Sequence; + struct SequenceMetaInfo; +} + +namespace mpc::file::kaitai +{ + class AllIo + { + public: + static void loadEverything(mpc::Mpc &, mpc::disk::MpcFile *); + static sequence_meta_infos_or_error loadSequenceMetaInfos(mpc::Mpc &, mpc::disk::MpcFile *); + static std::shared_ptr loadOneSequence( + mpc::Mpc &, + mpc::disk::MpcFile *, + mpc::SequenceIndex sourceIndexInAllFile, + mpc::SequenceIndex destIndexInMpcMemory); + static std::vector save(mpc::Mpc &); + }; +} diff --git a/src/main/file/kaitai/ApsIo.cpp b/src/main/file/kaitai/ApsIo.cpp new file mode 100644 index 000000000..17b2d9c33 --- /dev/null +++ b/src/main/file/kaitai/ApsIo.cpp @@ -0,0 +1,651 @@ +#include "file/kaitai/ApsIo.hpp" + +#include "Mpc.hpp" +#include "StrUtil.hpp" +#include "disk/AbstractDisk.hpp" +#include "disk/MpcFile.hpp" +#include "disk/SoundLoader.hpp" +#include "engine/IndivFxMixer.hpp" +#include "engine/StereoMixer.hpp" +#include "file/BitUtil.hpp" +#include "file/ByteUtil.hpp" +#include "file/kaitai/KaitaiIoUtil.hpp" +#include "file/kaitai/generated/mpc2000xl_aps.h" +#include "lcdgui/screens/DrumScreen.hpp" +#include "lcdgui/screens/MixerSetupScreen.hpp" +#include "lcdgui/screens/PgmAssignScreen.hpp" +#include "lcdgui/screens/window/CantFindFileScreen.hpp" +#include "performance/PerformanceManager.hpp" +#include "sampler/NoteParameters.hpp" +#include "sampler/PgmSlider.hpp" +#include "sampler/Program.hpp" +#include "sampler/Sampler.hpp" +#include "sequencer/Bus.hpp" +#include "sequencer/Sequencer.hpp" + +#include +#include +#include +#include +#include +#include + +#include + +namespace { + +constexpr uint8_t kUnassignedSoundIndex = 0xFF; +constexpr int kOffNoteNumber = 34; +constexpr int kVisibleNameLength = 16; +constexpr int kProgramPadCount = 64; +constexpr char kApsProgramUnknownBytes[] = {0, 7, 4, 30, 0}; +constexpr char kApsProgramSliderPadding[] = {35, 64, 0, 26, 0}; +constexpr char kApsProgramNoteTerminator = 6; +constexpr char kApsProgramMixerPadding[] = {0, 64, 0}; +constexpr char kApsTopLevelPadAfterSounds[] = {24, 0}; +constexpr char kApsDrumPadBeforeFirstDrum[] = {4, 0, static_cast(136), 1, 64, 0, 6}; +constexpr char kApsAfterDrums[] = {1, 127}; +constexpr char kApsEndMarker[] = {static_cast(255), static_cast(255)}; +constexpr char kApsGlobalTemplateNoSounds[] = {127, static_cast(254), 124, static_cast(238), 0, 0, 0, 64}; +constexpr char kApsGlobalTemplateWithSounds[] = {3, 56, 0, static_cast(161), 0, 0, 0, 64}; +constexpr char kApsDrumConfigTemplate[] = {0, 0, 1, 1, 127, 0, 1, 1, 127, 64, 0, 6}; +constexpr char kPadTailFxBoardSettingsHex[] = + "02004800D0070000630114081DFC323302323C08050A1414320000020F19000541141E0105" + "0000056300F4FF0C0000000000000002004F014F0100424F0100424F01004232006328003C" + "0000D0070000630114081DFC323302323C08050A1414320000020F19000541141E01050000" + "056300F4FF0C0000000000000002004F014F0100424F0100424F01004232006328003C0000" + "04000C000000320023003E335A3214000000320023003E335A3214000000320023003E335A" + "3214000000320023003E335A321400"; + +std::string stripNullTerminatedField(const std::string& raw) +{ + const auto terminator = raw.find('\0'); + return terminator == std::string::npos ? raw : raw.substr(0, terminator); +} + +std::string trimNullTerminatedField(const std::string& raw) +{ + return mpc::StrUtil::trim(stripNullTerminatedField(raw)); +} + +int normalizeOffNote(const uint8_t raw) +{ + return raw == 0 ? kOffNoteNumber : raw; +} + +void copyMixer(mpc::performance::StereoMixer& stereoDest, + mpc::performance::IndivFxMixer& indivDest, + const mpc2000xl_pgm_t::pad_mixer_t& mixer) +{ + stereoDest.level = mpc::DrumMixerLevel(mixer.volume()); + stereoDest.panning = mpc::DrumMixerPanning(mixer.pan()); + indivDest.individualOutLevel = mpc::DrumMixerLevel(mixer.volume_individual()); + indivDest.individualOutput = mpc::DrumMixerIndividualOutput(mixer.output()); + indivDest.fxSendLevel = mpc::DrumMixerLevel(mixer.effects_send_level()); + indivDest.fxPath = mpc::DrumMixerIndividualFxPath(mixer.fx_output()); +} + +void appendBytes(std::vector& dest, const std::vector& src) +{ + dest.insert(dest.end(), src.begin(), src.end()); +} + +void appendBytes(std::vector& dest, const std::string& src) +{ + dest.insert(dest.end(), src.begin(), src.end()); +} + +void appendBytes(std::vector& dest, const char* src, const std::size_t size) +{ + dest.insert(dest.end(), src, src + size); +} + +std::vector toNullTerminatedAsciiBytes(const std::string& value) +{ + std::vector bytes(kVisibleNameLength + 1, ' '); + const auto trimmed = value.substr(0, kVisibleNameLength); + std::copy(trimmed.begin(), trimmed.end(), bytes.begin()); + bytes.back() = 0; + return bytes; +} + +std::vector buildGlobalParametersBytes(mpc::Mpc& mpc, const int soundCount) +{ + std::vector bytes( + soundCount == 0 + ? std::begin(kApsGlobalTemplateNoSounds) + : std::begin(kApsGlobalTemplateWithSounds), + soundCount == 0 + ? std::end(kApsGlobalTemplateNoSounds) + : std::end(kApsGlobalTemplateWithSounds)); + + auto drumScreen = mpc.screens->get(); + auto pgmAssignScreen = mpc.screens->get(); + auto mixerSetupScreen = mpc.screens->get(); + + bytes[0] = mpc::file::BitUtil::setBits(1, bytes[0], drumScreen->isPadToIntSound()); + bytes[1] = mpc::file::BitUtil::setBits(1, bytes[1], pgmAssignScreen->isPadAssignMaster()); + bytes[2] = mpc::file::BitUtil::setBits(1, bytes[2], mixerSetupScreen->isStereoMixSourceDrum()); + bytes[2] = mpc::file::BitUtil::setBits(2, bytes[2], mixerSetupScreen->isIndivFxSourceDrum()); + bytes[3] = mpc::file::BitUtil::setBits(1, bytes[3], mixerSetupScreen->isCopyPgmMixToDrumEnabled()); + bytes[3] = mpc::file::BitUtil::setBits(16, bytes[3], mixerSetupScreen->isRecordMixChangesEnabled()); + bytes[4] = static_cast(mixerSetupScreen->getFxDrum()); + bytes[6] = static_cast(mixerSetupScreen->getMasterLevel()); + return bytes; +} + +std::vector buildDrumMixerBytes(const std::shared_ptr& drumBus) +{ + std::vector bytes; + bytes.reserve(kProgramPadCount * 6); + + const auto stereoMixers = drumBus->getStereoMixerChannels(); + const auto indivFxMixers = drumBus->getIndivFxMixerChannels(); + + for (int i = 0; i < kProgramPadCount; ++i) + { + const auto& stereo = stereoMixers[i]; + const auto& indiv = indivFxMixers[i]; + bytes.push_back(static_cast(indiv->getFxPath())); + bytes.push_back(static_cast(stereo->getLevel())); + bytes.push_back(static_cast(stereo->getPanning())); + bytes.push_back(static_cast(indiv->getVolumeIndividualOut())); + bytes.push_back(static_cast(indiv->getOutput())); + bytes.push_back(static_cast(indiv->getFxSendLevel())); + } + + return bytes; +} + +std::vector buildDrumConfigBytes(const std::shared_ptr& drumBus, + const bool fullLength) +{ + std::vector bytes(std::begin(kApsDrumConfigTemplate), std::end(kApsDrumConfigTemplate)); + bytes[1] = static_cast(drumBus->getProgramIndex()); + bytes[2] = mpc::file::BitUtil::setBits(1, bytes[2], drumBus->receivesPgmChange()); + bytes[3] = mpc::file::BitUtil::setBits(1, bytes[3], drumBus->receivesMidiVolume()); + bytes[5] = static_cast(drumBus->getProgramIndex()); + + if (!fullLength) + { + bytes.resize(7); + } + + return bytes; +} + +std::vector buildProgramBytes(mpc::sampler::Program& program, const int index) +{ + std::vector bytes; + bytes.reserve(2350); + + bytes.push_back(static_cast(index)); + appendBytes(bytes, kApsProgramUnknownBytes, sizeof(kApsProgramUnknownBytes)); + appendBytes(bytes, toNullTerminatedAsciiBytes(program.getName())); + + const auto* slider = program.getSlider(); + bytes.push_back(static_cast(slider->getNote() == 34 ? 0 : slider->getNote().get())); + bytes.push_back(static_cast(slider->getTuneLowRange())); + bytes.push_back(static_cast(slider->getTuneHighRange())); + bytes.push_back(static_cast(slider->getDecayLowRange())); + bytes.push_back(static_cast(slider->getDecayHighRange())); + bytes.push_back(static_cast(slider->getAttackLowRange())); + bytes.push_back(static_cast(slider->getAttackHighRange())); + bytes.push_back(static_cast(slider->getFilterLowRange())); + bytes.push_back(static_cast(slider->getFilterHighRange())); + bytes.push_back(static_cast(program.getMidiProgramChange() - 1)); + appendBytes(bytes, kApsProgramSliderPadding, sizeof(kApsProgramSliderPadding)); + + for (int i = 0; i < kProgramPadCount; ++i) + { + const auto* note = program.getNoteParameters(i + 35); + bytes.push_back(static_cast(note->getSoundIndex() == -1 ? 0xFF : note->getSoundIndex())); + bytes.push_back(static_cast(note->getSoundIndex() == -1 ? 0xFF : 0x00)); + bytes.push_back(static_cast(mpc::sampler::toRaw( + note->getSoundGenerationMode()))); + bytes.push_back(static_cast(note->getVelocityRangeLower())); + bytes.push_back(static_cast(note->getOptionalNoteA() == 34 ? 0 : note->getOptionalNoteA().get())); + bytes.push_back(static_cast(note->getVelocityRangeUpper())); + bytes.push_back(static_cast(note->getOptionalNoteB() == 34 ? 0 : note->getOptionalNoteB().get())); + bytes.push_back(static_cast(static_cast(note->getVoiceOverlapMode()))); + bytes.push_back(static_cast(note->getMuteAssignA() == 34 ? 0 : note->getMuteAssignA().get())); + bytes.push_back(static_cast(note->getMuteAssignB() == 34 ? 0 : note->getMuteAssignB().get())); + appendBytes(bytes, mpc::file::ByteUtil::ushort2bytes(static_cast(note->getTune()))); + bytes.push_back(static_cast(note->getAttack())); + bytes.push_back(static_cast(note->getDecay())); + bytes.push_back(static_cast(note->getDecayMode())); + bytes.push_back(static_cast(note->getFilterFrequency())); + bytes.push_back(static_cast(note->getFilterResonance())); + bytes.push_back(static_cast(note->getFilterAttack())); + bytes.push_back(static_cast(note->getFilterDecay())); + bytes.push_back(static_cast(note->getFilterEnvelopeAmount())); + bytes.push_back(static_cast(note->getVeloToLevel())); + bytes.push_back(static_cast(note->getVelocityToAttack())); + bytes.push_back(static_cast(note->getVelocityToStart())); + bytes.push_back(static_cast(note->getVelocityToFilterFrequency())); + bytes.push_back(static_cast(note->getSliderParameterNumber())); + bytes.push_back(static_cast(note->getVelocityToPitch())); + } + + bytes.push_back(kApsProgramNoteTerminator); + + for (int i = 0; i < kProgramPadCount; ++i) + { + const auto stereo = program.getStereoMixerChannel(i); + const auto indiv = program.getIndivFxMixerChannel(i); + bytes.push_back(static_cast(indiv->getFxPath())); + bytes.push_back(static_cast(stereo->getLevel())); + bytes.push_back(static_cast(stereo->getPanning())); + bytes.push_back(static_cast(indiv->getVolumeIndividualOut())); + bytes.push_back(static_cast(indiv->getOutput())); + bytes.push_back(static_cast(indiv->getFxSendLevel())); + } + + appendBytes(bytes, kApsProgramMixerPadding, sizeof(kApsProgramMixerPadding)); + + for (int i = 0; i < kProgramPadCount; ++i) + { + bytes.push_back(static_cast(program.getNoteFromPad(mpc::ProgramPadIndex(i)).get())); + } + + appendBytes(bytes, mpc::file::ByteUtil::hexToBytes(kPadTailFxBoardSettingsHex)); + return bytes; +} + +void showPopup(mpc::Mpc& mpc, const std::string& name, const std::string& ext) +{ + const std::string msg = + "Loading " + + mpc::StrUtil::toUpper(mpc::StrUtil::padRight(name, " ", 16) + "." + ext); + + const auto ls = mpc.getLayeredScreen(); + ls->postToUiThread(mpc::utils::Task( + [ls, msg] + { + ls->showPopup(msg); + })); + std::this_thread::sleep_for(std::chrono::milliseconds(50)); +} + +void handleSoundNotFound(mpc::Mpc& mpc, const std::string& soundFileName) +{ + const auto cantFindFileScreen = + mpc.screens->get(); + + if (const auto skipAll = cantFindFileScreen->skipAll; !skipAll) + { + const auto ls = mpc.getLayeredScreen(); + ls->postToUiThread(mpc::utils::Task( + [ls, cantFindFileScreen, soundFileName] + { + cantFindFileScreen->waitingForUser = true; + cantFindFileScreen->fileName = soundFileName; + ls->openScreenById(mpc::lcdgui::ScreenId::CantFindFileScreen); + })); + + while (cantFindFileScreen->waitingForUser) + { + std::this_thread::sleep_for(std::chrono::milliseconds(50)); + } + } +} + +void loadSound(mpc::Mpc& mpc, + const std::string& soundFileName, + const std::string& ext, + const std::shared_ptr& soundFile, + const bool headless) +{ + constexpr auto replace = false; + mpc::disk::SoundLoader soundLoader(mpc, replace); + + if (!headless) + { + showPopup(mpc, soundFileName, ext); + } + + mpc::disk::SoundLoaderResult result; + constexpr bool shouldBeConverted = false; + const auto sound = mpc.getSampler()->addSound(); + + if (!sound) + { + return; + } + + soundLoader.loadSound(soundFile, result, sound, shouldBeConverted); + + if (!result.success) + { + mpc.getSampler()->deleteSound(sound); + } +} + +void loadReferencedSounds(mpc2000xl_aps_t& parsed, + mpc::Mpc& mpc, + const bool headless, + std::vector& unavailableSoundIndices, + std::map& finalSoundIndices) +{ + auto sampler = mpc.getSampler(); + auto disk = mpc.getDisk(); + + int skipCount = 0; + sampler->deleteAllSamples(); + + for (int i = 0; i < static_cast(parsed.sound_names()->size()); ++i) + { + auto ext = std::string("snd"); + std::shared_ptr soundFile; + const auto soundFileName = + mpc::StrUtil::replaceAll(stripNullTerminatedField(parsed.sound_names()->at(i)), ' ', ""); + + for (auto& f : disk->getAllFiles()) + { + if (mpc::StrUtil::eqIgnoreCase( + mpc::StrUtil::replaceAll(f->getName(), ' ', ""), + soundFileName + ".SND")) + { + soundFile = f; + break; + } + } + + if (!soundFile || !soundFile->exists()) + { + for (auto& f : disk->getAllFiles()) + { + if (mpc::StrUtil::eqIgnoreCase( + mpc::StrUtil::replaceAll(f->getName(), ' ', ""), + soundFileName + ".WAV")) + { + soundFile = f; + ext = "wav"; + break; + } + } + } + + if (!soundFile || !soundFile->exists()) + { + unavailableSoundIndices.push_back(i); + ++skipCount; + + if (!headless) + { + handleSoundNotFound(mpc, soundFileName); + } + + continue; + } + + finalSoundIndices[i] = i - skipCount; + loadSound(mpc, soundFileName, ext, soundFile, headless); + } +} + +void loadPrograms(mpc2000xl_aps_t& parsed, + mpc::Mpc& mpc, + const std::vector& unavailableSoundIndices, + const std::map& finalSoundIndices) +{ + auto sampler = mpc.getSampler(); + + for (const auto& apsProgram : *parsed.aps_programs()) + { + if (!apsProgram || apsProgram->body() == nullptr) + { + continue; + } + + const auto programIndex = apsProgram->index(); + auto program = sampler->getProgram(programIndex); + auto* body = apsProgram->body(); + program->setName(trimNullTerminatedField(body->name())); + + const auto* slider = body->slider(); + const auto pgmSlider = program->getSlider(); + pgmSlider->setAssignNote(mpc::DrumNoteNumber(normalizeOffNote(slider->note()))); + pgmSlider->setTuneLowRange(slider->tune_low()); + pgmSlider->setTuneHighRange(slider->tune_high()); + pgmSlider->setDecayLowRange(slider->decay_low()); + pgmSlider->setDecayHighRange(slider->decay_high()); + pgmSlider->setAttackLowRange(slider->attack_low()); + pgmSlider->setAttackHighRange(slider->attack_high()); + pgmSlider->setFilterLowRange(slider->filter_low()); + pgmSlider->setFilterHighRange(slider->filter_high()); + program->setMidiProgramChange(body->program_change() + 1); + + mpc::performance::UpdateProgramBulk msg; + auto& perfProgram = msg.program; + msg.programIndex = mpc::ProgramIndex(programIndex); + perfProgram.used = true; + + for (int noteIndex = 0; noteIndex < 64; ++noteIndex) + { + perfProgram.pads[noteIndex].note = + mpc::DrumNoteNumber(body->pad_to_note_mapping()->at(noteIndex)); + + auto& destNoteParams = perfProgram.noteParameters[noteIndex]; + const auto* sourceMixer = body->pad_mixers()->at(noteIndex).get(); + copyMixer(destNoteParams.stereoMixer, destNoteParams.indivFxMixer, *sourceMixer); + + const auto* srcNoteParams = body->note_parameters()->at(noteIndex).get(); + auto soundIndex = srcNoteParams->sound_index() == 0xFFFF + ? -1 + : static_cast(srcNoteParams->sound_index()); + + if (std::find(begin(unavailableSoundIndices), + end(unavailableSoundIndices), + soundIndex) != end(unavailableSoundIndices)) + { + soundIndex = -1; + } + + if (soundIndex != -1) + { + const auto finalSoundIndex = finalSoundIndices.find(soundIndex); + if (finalSoundIndex != end(finalSoundIndices)) + { + soundIndex = finalSoundIndex->second; + } + } + + destNoteParams.soundIndex = soundIndex; + destNoteParams.tune = srcNoteParams->tune(); + destNoteParams.voiceOverlapMode = + static_cast(srcNoteParams->voice_overlap_mode()); + destNoteParams.decayMode = static_cast(srcNoteParams->decay_mode()); + destNoteParams.attack = srcNoteParams->attack(); + destNoteParams.decay = srcNoteParams->decay(); + destNoteParams.filterAttack = srcNoteParams->velocity_envelope_to_filter_attack(); + destNoteParams.filterDecay = srcNoteParams->velocity_envelope_to_filter_decay(); + destNoteParams.filterEnvelopeAmount = srcNoteParams->velocity_envelope_to_filter_amount(); + destNoteParams.filterFrequency = srcNoteParams->cutoff(); + destNoteParams.filterResonance = srcNoteParams->resonance(); + destNoteParams.muteAssignA = + mpc::DrumNoteNumber(normalizeOffNote(srcNoteParams->mute_assign_1())); + destNoteParams.muteAssignB = + mpc::DrumNoteNumber(normalizeOffNote(srcNoteParams->mute_assign_2())); + destNoteParams.optionalNoteA = + mpc::DrumNoteNumber(normalizeOffNote(srcNoteParams->also_play_use_note_1())); + destNoteParams.optionalNoteB = + mpc::DrumNoteNumber(normalizeOffNote(srcNoteParams->also_play_use_note_2())); + destNoteParams.sliderParameterNumber = + static_cast(srcNoteParams->slider_parameter()); + destNoteParams.soundGenerationMode = + static_cast(srcNoteParams->sound_generation_mode()); + destNoteParams.velocityToStart = srcNoteParams->velocity_to_start(); + destNoteParams.velocityToAttack = srcNoteParams->velocity_to_attack(); + destNoteParams.velocityToFilterFrequency = srcNoteParams->velocity_to_cutoff(); + destNoteParams.velocityToLevel = srcNoteParams->velocity_to_level(); + destNoteParams.velocityRangeLower = srcNoteParams->velocity_range_lower(); + destNoteParams.velocityRangeUpper = srcNoteParams->velocity_range_upper(); + destNoteParams.velocityToPitch = srcNoteParams->velocity_to_pitch(); + } + + perfProgram.slider.attackHighRange = slider->attack_high(); + perfProgram.slider.attackLowRange = slider->attack_low(); + perfProgram.slider.decayHighRange = slider->decay_high(); + perfProgram.slider.decayLowRange = slider->decay_low(); + perfProgram.slider.filterHighRange = slider->filter_high(); + perfProgram.slider.filterLowRange = slider->filter_low(); + perfProgram.slider.assignNote = mpc::DrumNoteNumber(normalizeOffNote(slider->note())); + perfProgram.slider.tuneHighRange = slider->tune_high(); + perfProgram.slider.tuneLowRange = slider->tune_low(); + perfProgram.midiProgramChange = body->program_change() + 1; + + mpc.getPerformanceManager().lock()->enqueue( + mpc::performance::PerformanceMessage{std::move(msg)}); + } +} + +void loadDrums(mpc2000xl_aps_t& parsed, mpc::Mpc& mpc) +{ + for (int i = 0; i < mpc::Mpc2000XlSpecs::DRUM_BUS_COUNT; ++i) + { + auto* drum = + i == 0 ? parsed.drum1() + : i == 1 ? parsed.drum2() + : i == 2 ? parsed.drum3() + : parsed.drum4(); + + mpc::performance::Drum perfDrum; + perfDrum.drumBusIndex = mpc::DrumBusIndex(i); + + for (int noteIndex = 0; noteIndex < 64; ++noteIndex) + { + const auto* mixer = drum->pad_mixers()->at(noteIndex).get(); + copyMixer(perfDrum.stereoMixers[noteIndex], perfDrum.indivFxMixers[noteIndex], *mixer); + } + + perfDrum.programIndex = mpc::ProgramIndex(drum->program()); + perfDrum.receivePgmChangeEnabled = + drum->receive_program_change() == mpc2000xl_aps_t::NO_YES_TRUE; + perfDrum.receiveMidiVolumeEnabled = + drum->receive_midi_volume() == mpc2000xl_aps_t::NO_YES_TRUE; + + mpc.getPerformanceManager().lock()->enqueue( + mpc::performance::UpdateDrumBulk{perfDrum}); + } +} + +void loadGlobals(mpc2000xl_aps_t& parsed, mpc::Mpc& mpc) +{ + auto mixerSetupScreen = mpc.screens->get(); + auto* globals = parsed.global_parameters(); + + mixerSetupScreen->setRecordMixChangesEnabled( + globals->record_mix_changes() == mpc2000xl_aps_t::NO_YES_TRUE); + mixerSetupScreen->setCopyPgmMixToDrumEnabled( + globals->copy_pgm_mix_to_drum() == mpc2000xl_aps_t::NO_YES_TRUE); + mixerSetupScreen->setFxDrum(globals->fx_drum()); + mixerSetupScreen->setIndivFxSourceDrum( + globals->indiv_fx_source() == mpc2000xl_aps_t::MIX_SOURCE_DRUM); + mixerSetupScreen->setStereoMixSourceDrum( + globals->stereo_mix_source() == mpc2000xl_aps_t::MIX_SOURCE_DRUM); + + auto drumScreen = mpc.screens->get(); + drumScreen->setPadToIntSound( + globals->pad_to_internal_sound() == mpc2000xl_aps_t::NO_YES_TRUE); + mixerSetupScreen->setMasterLevel(globals->master_level()); + + auto pgmAssignScreen = mpc.screens->get(); + std::vector masterPadAssign; + masterPadAssign.reserve(64); + for (const auto note : *parsed.master_pad_to_note_mapping()) + { + masterPadAssign.emplace_back(note); + } + mpc.getSampler()->setMasterPadAssign(masterPadAssign); + pgmAssignScreen->setPadAssign( + globals->pad_assign() == mpc2000xl_aps_t::PAD_ASSIGN_MASTERS); +} + +} // namespace + +using namespace mpc::file::kaitai; + +void ApsIo::loadBytes(mpc::Mpc& mpc, + const std::vector& bytes, + const bool withoutSounds, + bool headless) +{ + const auto canonicalBytes = parseRewrite(bytes); + std::stringstream parseStream( + std::string(canonicalBytes.begin(), canonicalBytes.end()), + std::ios::in | std::ios::out | std::ios::binary + ); + ::kaitai::kstream parseIo(&parseStream); + mpc2000xl_aps_t parsed(&parseIo); + parsed._read(); + + if (parsed.magic() != std::string("\x0a\x05", 2)) + { + throw std::runtime_error("Invalid APS header"); + } + + std::vector unavailableSoundIndices; + std::map finalSoundIndices; + if (!withoutSounds) + { + loadReferencedSounds(parsed, mpc, headless, unavailableSoundIndices, finalSoundIndices); + } + loadPrograms(parsed, mpc, unavailableSoundIndices, finalSoundIndices); + loadDrums(parsed, mpc); + loadGlobals(parsed, mpc); + mpc.getSampler()->setSoundIndex(0); +} + +void ApsIo::load(mpc::Mpc& mpc, const std::shared_ptr& file, bool headless) +{ + loadBytes(mpc, file->getBytes(), false, headless); +} + +std::vector ApsIo::save(mpc::Mpc& mpc, const std::string& apsName) +{ + auto sampler = mpc.getSampler(); + std::vector bytes; + + appendBytes(bytes, std::string("\x0a\x05", 2)); + appendBytes(bytes, mpc::file::ByteUtil::ushort2bytes(static_cast(sampler->getSoundCount()))); + + for (int i = 0; i < sampler->getSoundCount(); ++i) + { + appendBytes(bytes, toNullTerminatedAsciiBytes(sampler->getSound(i)->getName())); + } + + appendBytes(bytes, kApsTopLevelPadAfterSounds, sizeof(kApsTopLevelPadAfterSounds)); + appendBytes(bytes, toNullTerminatedAsciiBytes(apsName)); + appendBytes(bytes, buildGlobalParametersBytes(mpc, sampler->getSoundCount())); + + for (const auto& note : *sampler->getMasterPadAssign()) + { + bytes.push_back(static_cast(note.get())); + } + + appendBytes(bytes, kApsDrumPadBeforeFirstDrum, sizeof(kApsDrumPadBeforeFirstDrum)); + + for (int i = 0; i < mpc::Mpc2000XlSpecs::DRUM_BUS_COUNT; ++i) + { + auto drumBus = mpc.getSequencer()->getDrumBus(mpc::DrumBusIndex(i)); + appendBytes(bytes, buildDrumMixerBytes(drumBus)); + appendBytes(bytes, buildDrumConfigBytes(drumBus, i < 3)); + } + + appendBytes(bytes, kApsAfterDrums, sizeof(kApsAfterDrums)); + + for (int i = 0; i < mpc::Mpc2000XlSpecs::MAX_PROGRAM_COUNT; ++i) + { + auto program = sampler->getProgram(i); + if (!program->isUsed()) + { + continue; + } + + appendBytes(bytes, buildProgramBytes(*program, i)); + } + + appendBytes(bytes, kApsEndMarker, sizeof(kApsEndMarker)); + return parseRewrite(bytes); +} diff --git a/src/main/file/kaitai/ApsIo.hpp b/src/main/file/kaitai/ApsIo.hpp new file mode 100644 index 000000000..940e5be75 --- /dev/null +++ b/src/main/file/kaitai/ApsIo.hpp @@ -0,0 +1,27 @@ +#pragma once + +#include +#include +#include +#include + +namespace mpc +{ + class Mpc; +} + +namespace mpc::disk +{ + class MpcFile; +} + +namespace mpc::file::kaitai +{ + class ApsIo + { + public: + static void load(mpc::Mpc &, const std::shared_ptr &, bool headless); + static void loadBytes(mpc::Mpc &, const std::vector &, bool withoutSounds, bool headless); + static std::vector save(mpc::Mpc &, const std::string &apsName); + }; +} diff --git a/src/main/file/kaitai/KaitaiIoUtil.hpp b/src/main/file/kaitai/KaitaiIoUtil.hpp new file mode 100644 index 000000000..7107a0082 --- /dev/null +++ b/src/main/file/kaitai/KaitaiIoUtil.hpp @@ -0,0 +1,31 @@ +#pragma once + +#include + +#include +#include +#include + +namespace mpc::file::kaitai +{ + template + inline std::vector parseRewrite(const std::vector &bytes) + { + std::stringstream parseStream( + std::string(bytes.begin(), bytes.end()), + std::ios::in | std::ios::out | std::ios::binary + ); + ::kaitai::kstream parseIo(&parseStream); + T parsed(&parseIo); + parsed._read(); + + std::stringstream writeStream(std::ios::in | std::ios::out | std::ios::binary); + ::kaitai::kstream writeIo(&writeStream); + parsed._set_io(&writeIo); + parsed._check(); + parsed._write(); + + const auto written = writeStream.str(); + return std::vector(written.begin(), written.end()); + } +} diff --git a/src/main/file/kaitai/MidIo.cpp b/src/main/file/kaitai/MidIo.cpp new file mode 100644 index 000000000..e78fd5e9c --- /dev/null +++ b/src/main/file/kaitai/MidIo.cpp @@ -0,0 +1,1507 @@ +#include "file/kaitai/MidIo.hpp" + +#include "Mpc.hpp" +#include "disk/MpcFile.hpp" +#include "file/kaitai/KaitaiIoUtil.hpp" +#include "file/kaitai/generated/standard_midi_file_with_running_status.h" +#include "sequencer/BusType.hpp" +#include "sequencer/EventData.hpp" +#include "sequencer/Sequence.hpp" +#include "sequencer/Sequencer.hpp" +#include "sequencer/SequencerStateManager.hpp" +#include "sequencer/TempoChangeEvent.hpp" +#include "sequencer/Track.hpp" +#include "sequencer/Transport.hpp" + +#include +#include +#include +#include +#include + +using namespace mpc::file::kaitai; + +namespace { + +using midi_t = standard_midi_file_with_running_status_t; +using meta_type_t = midi_t::meta_event_body_t::meta_type_enum_t; + +std::string asciiBody(const std::string& body) +{ + return body; +} + +bool isInteger(const std::string& s) +{ + for (const auto c : s) + { + if (!std::isdigit(static_cast(c))) + { + return false; + } + } + return true; +} + +bool isMpc2000xlTrackName(const std::string& value) +{ + return value.rfind("MPC2000XL 1.00 ", 0) == 0; +} + +bool isMpc2000xlMetaTrack(const midi_t& parsed) +{ + if (parsed.tracks() == nullptr || parsed.tracks()->empty()) + { + return false; + } + + for (const auto& eventPtr : *parsed.tracks()->at(0)->events()->event()) + { + const auto* meta = eventPtr->meta_event_body(); + if (meta == nullptr || + meta->meta_type() != meta_type_t::META_TYPE_ENUM_SEQUENCE_TRACK_NAME) + { + continue; + } + + if (isMpc2000xlTrackName(asciiBody(meta->body()))) + { + return true; + } + } + + return false; +} + +double bpmFromTempoBody(const std::string& body) +{ + if (body.size() < 3) + { + return 120.0; + } + + const auto mpqn = + (static_cast(body[0]) << 16) | + (static_cast(body[1]) << 8) | + static_cast(body[2]); + return mpqn == 0 ? 120.0 : 60000000.0 / static_cast(mpqn); +} + +int denominatorFromTimeSignatureBody(const std::string& body) +{ + return body.size() < 2 ? mpc::DefaultTimeSigDenominator + : 1 << static_cast(body[1]); +} + +struct TimedMidiEvent +{ + int tick; + int order; + std::vector bytes; +}; + +void appendU2Be(std::vector& out, const uint16_t value) +{ + out.push_back(static_cast((value >> 8) & 0xFF)); + out.push_back(static_cast(value & 0xFF)); +} + +void appendU4Be(std::vector& out, const uint32_t value) +{ + out.push_back(static_cast((value >> 24) & 0xFF)); + out.push_back(static_cast((value >> 16) & 0xFF)); + out.push_back(static_cast((value >> 8) & 0xFF)); + out.push_back(static_cast(value & 0xFF)); +} + +void appendVlq(std::vector& out, uint32_t value) +{ + std::array reversed{}; + auto count = 0; + reversed[count++] = static_cast(value & 0x7F); + value >>= 7; + + while (value > 0) + { + reversed[count++] = static_cast((value & 0x7F) | 0x80); + value >>= 7; + } + + for (auto i = count - 1; i >= 0; --i) + { + out.push_back(reversed[i]); + } +} + +std::vector metaEventBytes(const uint8_t type, const std::vector& payload) +{ + std::vector result{ + static_cast(0xFF), + static_cast(type) + }; + appendVlq(result, static_cast(payload.size())); + result.insert(result.end(), payload.begin(), payload.end()); + return result; +} + +std::vector channelEventBytes(const uint8_t status, const uint8_t dataA) +{ + return std::vector{ + static_cast(status), + static_cast(dataA) + }; +} + +std::vector channelEventBytes(const uint8_t status, const uint8_t dataA, const uint8_t dataB) +{ + return std::vector{ + static_cast(status), + static_cast(dataA), + static_cast(dataB) + }; +} + +std::vector sysexEventBytes(const std::vector& payload) +{ + std::vector result{ + static_cast(0xF0) + }; + appendVlq(result, static_cast(payload.size())); + result.insert(result.end(), payload.begin(), payload.end()); + return result; +} + +std::string zeroPad3(const int value) +{ + std::ostringstream ss; + ss << std::setw(3) << std::setfill('0') << std::clamp(value, 0, 999); + return ss.str(); +} + +std::string padRightTruncate(const std::string& value, const size_t width) +{ + if (value.size() >= width) + { + return value.substr(0, width); + } + return value + std::string(width - value.size(), ' '); +} + +std::string hexByte(const int value) +{ + std::ostringstream ss; + ss << std::uppercase << std::hex + << std::setw(2) << std::setfill('0') + << (value & 0xFF); + return ss.str(); +} + +bool isDefaultNoteVariation(const mpc::sequencer::EventData& event) +{ + return event.noteVariationType == mpc::NoteVariationTypeTune && + event.noteVariationValue == mpc::DefaultNoteVariationValue; +} + +std::string sequenceParamsText(const mpc::sequencer::Sequence& sequence) +{ + const auto loopStart = zeroPad3(sequence.getFirstLoopBarIndex()); + const auto loopEnd = + sequence.getLastLoopBarIndex() == mpc::EndOfSequence + ? std::string("END") + : zeroPad3(sequence.getLastLoopBarIndex()); + + return std::string("LOOP=") + + (sequence.isLoopEnabled() ? "ON " : "OFF") + + " START=" + loopStart + + " END=" + loopEnd + + " TEMPO=" + (sequence.isTempoChangeOn() ? "ON " : "OFF") + " "; +} + +std::vector stringPayload(const std::string& value) +{ + return std::vector(value.begin(), value.end()); +} + +int tempoToMpqn(const double bpm) +{ + const auto clamped = std::clamp(bpm, 30.0, 300.0); + return static_cast(60000000.0 / clamped); +} + +int timeSignatureDenominatorPow(const int denominator) +{ + auto value = denominator; + auto power = 0; + while (value > 1) + { + value >>= 1; + power++; + } + return power; +} + +std::vector serializeTrack(const std::vector& inputEvents, const int lastTick) +{ + auto events = inputEvents; + std::stable_sort( + events.begin(), + events.end(), + [](const auto& a, const auto& b) + { + if (a.tick != b.tick) + { + return a.tick < b.tick; + } + return a.order < b.order; + }); + + std::vector trackBody; + auto previousTick = 0; + for (const auto& event : events) + { + appendVlq(trackBody, static_cast(std::max(0, event.tick - previousTick))); + trackBody.insert(trackBody.end(), event.bytes.begin(), event.bytes.end()); + previousTick = event.tick; + } + + appendVlq(trackBody, static_cast(std::max(0, lastTick - previousTick))); + trackBody.push_back(static_cast(0xFF)); + trackBody.push_back(static_cast(0x2F)); + trackBody.push_back(static_cast(0x00)); + + std::vector result{'M', 'T', 'r', 'k'}; + appendU4Be(result, static_cast(trackBody.size())); + result.insert(result.end(), trackBody.begin(), trackBody.end()); + return result; +} + +std::vector buildMetaTrack(const mpc::sequencer::Sequence& sequence) +{ + std::vector events; + events.push_back(TimedMidiEvent{ + 0, + 0, + metaEventBytes(0x01, stringPayload(sequenceParamsText(sequence))) + }); + events.push_back(TimedMidiEvent{ + 0, + 1, + metaEventBytes( + 0x03, + stringPayload("MPC2000XL 1.00 " + padRightTruncate(sequence.getName(), 16))) + }); + + const auto initialMpqn = tempoToMpqn(sequence.getInitialTempo()); + events.push_back(TimedMidiEvent{ + 0, + 2, + metaEventBytes( + 0x51, + std::vector{ + static_cast((initialMpqn >> 16) & 0xFF), + static_cast((initialMpqn >> 8) & 0xFF), + static_cast(initialMpqn & 0xFF)}) + }); + + for (const auto& tempoChange : sequence.getTempoChangeEvents()) + { + if (tempoChange->getTick() <= 0 || tempoChange->getTick() >= sequence.getLastTick()) + { + continue; + } + + const auto mpqn = tempoToMpqn(tempoChange->getTempo()); + events.push_back(TimedMidiEvent{ + tempoChange->getTick(), + 2, + metaEventBytes( + 0x51, + std::vector{ + static_cast((mpqn >> 16) & 0xFF), + static_cast((mpqn >> 8) & 0xFF), + static_cast(mpqn & 0xFF)}) + }); + } + + events.push_back(TimedMidiEvent{ + 0, + 3, + metaEventBytes(0x54, std::vector{0, 0, 0, 0, 0}) + }); + + auto barStartTick = 0; + auto previousNumerator = sequence.getNumerator(0); + auto previousDenominator = sequence.getDenominator(0); + events.push_back(TimedMidiEvent{ + 0, + 4, + metaEventBytes( + 0x58, + std::vector{ + static_cast(previousNumerator), + static_cast(timeSignatureDenominatorPow(previousDenominator)), + static_cast(24), + static_cast(8)}) + }); + + for (auto barIndex = 1; barIndex <= sequence.getLastBarIndex(); ++barIndex) + { + barStartTick += sequence.getBarLength(barIndex - 1); + const auto numerator = sequence.getNumerator(barIndex); + const auto denominator = sequence.getDenominator(barIndex); + if (numerator == previousNumerator && denominator == previousDenominator) + { + continue; + } + + previousNumerator = numerator; + previousDenominator = denominator; + events.push_back(TimedMidiEvent{ + barStartTick, + 4, + metaEventBytes( + 0x58, + std::vector{ + static_cast(numerator), + static_cast(timeSignatureDenominatorPow(denominator)), + static_cast(24), + static_cast(8)}) + }); + } + + return serializeTrack(events, sequence.getLastTick()); +} + +std::vector buildSequenceTrack(const std::shared_ptr& sequence, + const std::shared_ptr& track) +{ + std::vector events; + events.push_back(TimedMidiEvent{ + 0, + 0, + metaEventBytes(0x04, stringPayload(std::string(8, ' '))) + }); + + const auto deviceIndex = track->getDeviceIndex(); + const auto trackNumber = zeroPad3(track->getIndex()).substr(1, 2); + const auto trackDevice = + deviceIndex == 0 ? std::string("C0") : hexByte(0xE0 + deviceIndex - 1); + const auto deviceNumber = hexByte(deviceIndex); + const auto busIndex = hexByte(static_cast(mpc::sequencer::busTypeToIndex(track->getBusType()))); + events.push_back(TimedMidiEvent{ + 0, + 1, + metaEventBytes( + 0x01, + stringPayload( + "TRACK DATA:" + + trackNumber + + trackDevice + + "006403 " + + deviceNumber + + busIndex + + deviceNumber + + " ")) + }); + events.push_back(TimedMidiEvent{ + 0, + 2, + metaEventBytes(0x03, stringPayload(padRightTruncate(track->getName(), 16))) + }); + + std::vector noteOffEvents; + std::vector noteVariationEvents; + std::vector noteOnEvents; + std::vector miscEvents; + + for (const auto& event : track->getEventStates()) + { + if (event.tick >= sequence->getLastTick()) + { + continue; + } + + switch (event.type) + { + case mpc::sequencer::EventType::NoteOn: + { + const auto noteOffTick = static_cast(event.tick + event.duration); + for (auto& noteOffEvent : noteOffEvents) + { + if (noteOffEvent.tick > event.tick && + noteOffEvent.bytes.size() >= 3 && + static_cast(noteOffEvent.bytes[1]) == + static_cast(event.noteNumber)) + { + noteOffEvent.tick = event.tick; + } + } + + if (!isDefaultNoteVariation(event)) + { + noteVariationEvents.push_back(TimedMidiEvent{ + static_cast(event.tick), + 1, + channelEventBytes( + 0x80, + static_cast(event.noteVariationType.get()), + static_cast(event.noteVariationValue.get())) + }); + } + + noteOnEvents.push_back(TimedMidiEvent{ + static_cast(event.tick), + 2, + channelEventBytes( + 0x90, + static_cast(event.noteNumber.get()), + static_cast(event.velocity.get())) + }); + + if (noteOffTick < sequence->getLastTick()) + { + noteOffEvents.push_back(TimedMidiEvent{ + noteOffTick, + 0, + channelEventBytes( + 0x90, + static_cast(event.noteNumber.get()), + 0) + }); + } + break; + } + case mpc::sequencer::EventType::PolyPressure: + miscEvents.push_back(TimedMidiEvent{ + static_cast(event.tick), + 3, + channelEventBytes( + 0xA0, + static_cast(event.noteNumber.get()), + static_cast(event.amount & 0x7F)) + }); + break; + case mpc::sequencer::EventType::ControlChange: + miscEvents.push_back(TimedMidiEvent{ + static_cast(event.tick), + 3, + channelEventBytes( + 0xB0, + static_cast(event.controllerNumber & 0x7F), + static_cast(event.controllerValue & 0x7F)) + }); + break; + case mpc::sequencer::EventType::ProgramChange: + miscEvents.push_back(TimedMidiEvent{ + static_cast(event.tick), + 3, + channelEventBytes( + 0xC0, + static_cast(event.programChangeProgramIndex.get())) + }); + break; + case mpc::sequencer::EventType::ChannelPressure: + miscEvents.push_back(TimedMidiEvent{ + static_cast(event.tick), + 3, + channelEventBytes( + 0xD0, + static_cast(event.amount & 0x7F)) + }); + break; + case mpc::sequencer::EventType::PitchBend: + { + const auto bendValue = std::clamp(event.amount + 8192, 0, 16383); + miscEvents.push_back(TimedMidiEvent{ + static_cast(event.tick), + 3, + channelEventBytes( + 0xE0, + static_cast(bendValue & 0x7F), + static_cast((bendValue >> 7) & 0x7F)) + }); + break; + } + case mpc::sequencer::EventType::Mixer: + miscEvents.push_back(TimedMidiEvent{ + static_cast(event.tick), + 3, + sysexEventBytes(std::vector{ + 71, + 0, + 68, + 69, + static_cast(event.mixerParameter + 1), + static_cast(event.mixerPad), + static_cast(event.mixerValue), + static_cast(0xF7)}) + }); + break; + case mpc::sequencer::EventType::SystemExclusive: + miscEvents.push_back(TimedMidiEvent{ + static_cast(event.tick), + 3, + sysexEventBytes(std::vector{ + static_cast(event.sysExByteA), + static_cast(event.sysExByteB)}) + }); + break; + default: + break; + } + } + + events.insert(events.end(), noteOffEvents.begin(), noteOffEvents.end()); + events.insert(events.end(), noteVariationEvents.begin(), noteVariationEvents.end()); + events.insert(events.end(), noteOnEvents.begin(), noteOnEvents.end()); + events.insert(events.end(), miscEvents.begin(), miscEvents.end()); + return serializeTrack(events, sequence->getLastTick()); +} + +std::vector buildMpc2000xlMidiBytes(const std::shared_ptr& sequence) +{ + auto firstUsedTrackIndex = -1; + auto lastUsedTrackIndex = -1; + for (auto i = 0; i < 64; ++i) + { + if (!sequence->getTrack(i)->isUsed()) + { + continue; + } + if (firstUsedTrackIndex == -1) + { + firstUsedTrackIndex = i; + } + lastUsedTrackIndex = i; + } + + std::vector> tracks; + tracks.push_back(buildMetaTrack(*sequence)); + + for (auto trackIndex = firstUsedTrackIndex; + trackIndex >= 0 && trackIndex <= lastUsedTrackIndex; + ++trackIndex) + { + tracks.push_back(buildSequenceTrack(sequence, sequence->getTrack(trackIndex))); + } + + std::vector result{'M', 'T', 'h', 'd'}; + appendU4Be(result, 6); + appendU2Be(result, tracks.size() > 1 ? 1 : 0); + appendU2Be(result, static_cast(tracks.size())); + appendU2Be(result, 96); + + for (const auto& track : tracks) + { + result.insert(result.end(), track.begin(), track.end()); + } + return result; +} + +bool loadGenericStandardMidi(mpc::Mpc& mpc, + const midi_t& parsed, + const std::shared_ptr& sequence) +{ + auto sequencer = mpc.getSequencer(); + auto transport = sequencer->getTransport(); + auto stateManager = sequencer->getStateManager(); + + sequence->setUsed(true); + + std::vector> tempoChanges; + struct TimeSigChange { int tick; int numerator; int denominator; }; + std::vector timeSignatures; + + int lengthInTicks = 0; + for (const auto& midiTrack : *parsed.tracks()) + { + int trackTick = 0; + for (const auto& eventPtr : *midiTrack->events()->event()) + { + trackTick += eventPtr->v_time()->value(); + } + lengthInTicks = std::max(lengthInTicks, trackTick); + } + + int tick = 0; + for (const auto& eventPtr : *parsed.tracks()->at(0)->events()->event()) + { + tick += eventPtr->v_time()->value(); + const auto* meta = eventPtr->meta_event_body(); + if (meta == nullptr) + { + continue; + } + + if (meta->meta_type() == meta_type_t::META_TYPE_ENUM_TEMPO) + { + tempoChanges.push_back({tick, bpmFromTempoBody(meta->body())}); + } + else if (meta->meta_type() == meta_type_t::META_TYPE_ENUM_TIME_SIGNATURE) + { + timeSignatures.push_back( + TimeSigChange{ + tick, + meta->body().empty() + ? mpc::DefaultTimeSigNumerator + : static_cast(meta->body()[0]), + denominatorFromTimeSignatureBody(meta->body()) + } + ); + } + } + + const auto initialTempo = tempoChanges.empty() ? 120.0 : tempoChanges[0].second; + sequence->setInitialTempo(initialTempo); + if (!transport->isTempoSourceSequence()) + { + transport->setTempo(initialTempo); + } + + for (size_t i = 1; i < tempoChanges.size(); ++i) + { + const auto ratio = tempoChanges[i].second / initialTempo; + sequence->addTempoChangeEvent( + tempoChanges[i].first, + static_cast(ratio * 1000.0) + ); + } + + if (timeSignatures.empty()) + { + timeSignatures.push_back( + TimeSigChange{0, mpc::DefaultTimeSigNumerator, mpc::DefaultTimeSigDenominator} + ); + } + + lengthInTicks = std::max(lengthInTicks, 1); + int accumLength = 0; + int barCounter = 0; + for (size_t i = 0; i < timeSignatures.size(); ++i) + { + const auto current = timeSignatures[i]; + const auto nextTick = + i + 1 < timeSignatures.size() ? timeSignatures[i + 1].tick : lengthInTicks; + + while (accumLength < nextTick) + { + sequence->setTimeSignature(barCounter, current.numerator, current.denominator); + const auto newDenTicks = 96 * (4.0 / current.denominator); + const auto barLength = static_cast(newDenTicks * current.numerator); + accumLength += barLength; + barCounter++; + } + } + + sequence->setLastBarIndex(barCounter - 1); + sequence->setFirstLoopBarIndex(mpc::BarIndex(-1)); + sequence->setLastLoopBarIndex(mpc::EndOfSequence); + + const auto sequenceIndex = sequence->getSequenceIndex(); + mpc::sequencer::UpdateSequenceEvents updateSequenceEvents{sequenceIndex}; + updateSequenceEvents.trackSnapshots = &stateManager->trackEventsSnapshots[sequenceIndex]; + updateSequenceEvents.trackSnapshots->clear(); + + mpc::sequencer::UpdateSequenceTracks updateSequenceTracks{sequenceIndex}; + updateSequenceTracks.trackStates = &stateManager->trackStatesSnapshots[sequenceIndex]; + *updateSequenceTracks.trackStates = mpc::sequencer::SequenceTrackStatesSnapshot(); + + const std::string trackDataPrefix = "TRACK DATA:"; + + for (size_t i = 0; i < parsed.tracks()->size() && i < 64; ++i) + { + auto resolvedTrackIndex = static_cast(i); + auto deviceIndex = 0; + auto busType = mpc::sequencer::BusType::DRUM1; + + for (const auto& eventPtr : *parsed.tracks()->at(i)->events()->event()) + { + const auto* meta = eventPtr->meta_event_body(); + if (meta == nullptr || meta->meta_type() != meta_type_t::META_TYPE_ENUM_TEXT_EVENT) + { + continue; + } + + const auto body = asciiBody(meta->body()); + if (body.rfind(trackDataPrefix, 0) != 0) + { + continue; + } + + const auto payload = body.substr(trackDataPrefix.size()); + resolvedTrackIndex = std::stoi(payload.substr(0, 2)); + + const auto deviceIndexStr = payload.substr(2, 2); + if (deviceIndexStr != "C0") + { + deviceIndex = std::stoi(deviceIndexStr, nullptr, 16) - std::stoi(std::string("E0"), nullptr, 16) + 1; + } + + if (payload.size() >= 16) + { + busType = mpc::sequencer::busIndexToBusType(std::stoi(payload.substr(14, 2), nullptr, 16)); + } + break; + } + + if (resolvedTrackIndex < 0 || resolvedTrackIndex > mpc::Mpc2000XlSpecs::LAST_TRACK_INDEX) + { + continue; + } + + auto& trackState = (*updateSequenceTracks.trackStates)[resolvedTrackIndex]; + trackState.busType = busType; + trackState.deviceIndex = deviceIndex; + std::string pendingTrackName; + bool hasPendingTrackName = false; + bool hasImportedEvents = false; + + struct OpenNote + { + mpc::sequencer::EventData event; + }; + + std::vector openNotes; + std::vector> noteOffs; + + int absoluteTick = 0; + for (const auto& eventPtr : *parsed.tracks()->at(i)->events()->event()) + { + absoluteTick += eventPtr->v_time()->value(); + + if (const auto* meta = eventPtr->meta_event_body(); meta != nullptr) + { + if (meta->meta_type() == meta_type_t::META_TYPE_ENUM_SEQUENCE_TRACK_NAME) + { + const auto body = asciiBody(meta->body()); + if (!body.empty()) + { + pendingTrackName = body.substr(0, mpc::Mpc2000XlSpecs::MAX_TRACK_NAME_LENGTH); + hasPendingTrackName = true; + } + } + continue; + } + + switch (eventPtr->event_type()) + { + case 0x80: + { + const auto* noteOff = + dynamic_cast(eventPtr->event_body()); + if (noteOff != nullptr) + { + noteOffs.push_back({noteOff->note(), absoluteTick}); + } + break; + } + case 0x90: + { + const auto* noteOn = + dynamic_cast(eventPtr->event_body()); + if (noteOn == nullptr) + { + break; + } + mpc::sequencer::EventData ne; + ne.type = mpc::sequencer::EventType::NoteOn; + ne.noteNumber = mpc::NoteNumber(noteOn->note()); + ne.tick = absoluteTick; + ne.velocity = mpc::Velocity(noteOn->velocity()); + ne.noteVariationType = mpc::NoteVariationTypeTune; + ne.noteVariationValue = mpc::DefaultNoteVariationValue; + ne.duration = mpc::Duration(24); + openNotes.push_back(OpenNote{ne}); + break; + } + case 0xA0: + { + const auto* noteAftertouch = + dynamic_cast(eventPtr->event_body()); + if (noteAftertouch == nullptr) + { + break; + } + mpc::sequencer::EventData e; + e.type = mpc::sequencer::EventType::PolyPressure; + e.tick = absoluteTick; + e.noteNumber = mpc::NoteNumber(noteAftertouch->note()); + e.amount = noteAftertouch->pressure(); + (*updateSequenceEvents.trackSnapshots)[resolvedTrackIndex].push_back(e); + hasImportedEvents = true; + break; + } + case 0xB0: + { + const auto* controller = + dynamic_cast(eventPtr->event_body()); + if (controller == nullptr) + { + break; + } + mpc::sequencer::EventData e; + e.type = mpc::sequencer::EventType::ControlChange; + e.tick = absoluteTick; + e.controllerNumber = controller->controller(); + e.controllerValue = controller->value(); + (*updateSequenceEvents.trackSnapshots)[resolvedTrackIndex].push_back(e); + hasImportedEvents = true; + break; + } + case 0xC0: + { + const auto* programChange = + dynamic_cast(eventPtr->event_body()); + if (programChange == nullptr) + { + break; + } + mpc::sequencer::EventData e; + e.type = mpc::sequencer::EventType::ProgramChange; + e.tick = absoluteTick; + e.programChangeProgramIndex = mpc::ProgramIndex(programChange->program()); + (*updateSequenceEvents.trackSnapshots)[resolvedTrackIndex].push_back(e); + hasImportedEvents = true; + break; + } + case 0xD0: + { + const auto* channelPressure = + dynamic_cast(eventPtr->event_body()); + if (channelPressure == nullptr) + { + break; + } + mpc::sequencer::EventData e; + e.type = mpc::sequencer::EventType::ChannelPressure; + e.tick = absoluteTick; + e.amount = channelPressure->pressure(); + (*updateSequenceEvents.trackSnapshots)[resolvedTrackIndex].push_back(e); + hasImportedEvents = true; + break; + } + case 0xE0: + { + auto* pitchBend = + dynamic_cast(eventPtr->event_body()); + if (pitchBend == nullptr) + { + break; + } + mpc::sequencer::EventData e; + e.type = mpc::sequencer::EventType::PitchBend; + e.tick = absoluteTick; + e.amount = pitchBend->bend_value(); + (*updateSequenceEvents.trackSnapshots)[resolvedTrackIndex].push_back(e); + hasImportedEvents = true; + break; + } + case 0xF0: + { + const auto* sysEx = eventPtr->sysex_body(); + if (sysEx == nullptr) + { + break; + } + const auto& data = sysEx->data(); + if (data.size() == 8 && + static_cast(data[0]) == 71 && + static_cast(data[1]) == 0 && + static_cast(data[2]) == 68 && + static_cast(data[3]) == 69 && + static_cast(data[7]) == 247) + { + mpc::sequencer::EventData e; + e.type = mpc::sequencer::EventType::Mixer; + e.tick = absoluteTick; + e.mixerParameter = static_cast(data[4]) - 1; + e.mixerPad = static_cast(data[5]); + e.mixerValue = static_cast(data[6]); + (*updateSequenceEvents.trackSnapshots)[resolvedTrackIndex].push_back(e); + } + else + { + mpc::sequencer::EventData e; + e.type = mpc::sequencer::EventType::SystemExclusive; + e.tick = absoluteTick; + (*updateSequenceEvents.trackSnapshots)[resolvedTrackIndex].push_back(e); + } + hasImportedEvents = true; + break; + } + default: + break; + } + } + + for (auto& on : openNotes) + { + for (auto it = noteOffs.begin(); it != noteOffs.end(); ++it) + { + if (it->first == on.event.noteNumber && it->second >= on.event.tick) + { + on.event.duration = mpc::Duration(it->second - on.event.tick); + noteOffs.erase(it); + break; + } + } + + (*updateSequenceEvents.trackSnapshots)[resolvedTrackIndex].push_back(on.event); + hasImportedEvents = true; + } + + if (hasPendingTrackName) + { + trackState.name = pendingTrackName; + } + + trackState.used = hasImportedEvents; + } + + stateManager->enqueue(updateSequenceTracks); + stateManager->enqueue(updateSequenceEvents); + stateManager->drainQueue(); + + return true; +} + +bool tryLoadMpc2000xl(mpc::Mpc& mpc, + const midi_t& parsed, + const std::shared_ptr& sequence) +{ + if (!isMpc2000xlMetaTrack(parsed)) + { + return false; + } + + auto sequencer = mpc.getSequencer(); + auto transport = sequencer->getTransport(); + + sequence->setUsed(true); + + struct TimedMetaEvent + { + int tick; + const midi_t::track_event_t* event; + const midi_t::meta_event_body_t* meta; + }; + + std::vector metaTrackEvents; + int tick = 0; + for (const auto& eventPtr : *parsed.tracks()->at(0)->events()->event()) + { + tick += eventPtr->v_time()->value(); + if (const auto* meta = eventPtr->meta_event_body(); meta != nullptr) + { + metaTrackEvents.push_back(TimedMetaEvent{tick, eventPtr.get(), meta}); + } + } + + std::vector> tempoChanges; + struct TimeSigChange { int tick; int numerator; int denominator; }; + std::vector timeSignatures; + int firstLoopBar = -1; + int lastLoopBar = -1; + + for (const auto& entry : metaTrackEvents) + { + const auto metaType = entry.meta->meta_type(); + const auto body = asciiBody(entry.meta->body()); + + if (metaType == meta_type_t::META_TYPE_ENUM_TEXT_EVENT) + { + if (body.find("LOOP=ON") != std::string::npos) + { + sequence->setLoopEnabled(true); + } + else if (body.find("LOOP=OFF") != std::string::npos) + { + sequence->setLoopEnabled(false); + } + + if (body.find("TEMPO=ON") != std::string::npos) + { + transport->setTempoSourceIsSequence(true); + } + else if (body.find("TEMPO=OFF") != std::string::npos) + { + transport->setTempoSourceIsSequence(false); + } + + if (body.size() >= 18) + { + firstLoopBar = std::stoi(body.substr(15, 18)); + } + lastLoopBar = -1; + if (body.size() >= 26) + { + const auto loopEnd = body.substr(23, 26); + if (isInteger(loopEnd)) + { + lastLoopBar = std::stoi(loopEnd); + } + } + } + else if (metaType == meta_type_t::META_TYPE_ENUM_SEQUENCE_TRACK_NAME && + isMpc2000xlTrackName(body)) + { + sequence->setName(body.substr(16)); + } + else if (metaType == meta_type_t::META_TYPE_ENUM_TEMPO) + { + tempoChanges.push_back({entry.tick, bpmFromTempoBody(entry.meta->body())}); + } + else if (metaType == meta_type_t::META_TYPE_ENUM_TIME_SIGNATURE) + { + timeSignatures.push_back( + TimeSigChange{ + entry.tick, + entry.meta->body().empty() + ? mpc::DefaultTimeSigNumerator + : static_cast(entry.meta->body()[0]), + denominatorFromTimeSignatureBody(entry.meta->body())}); + } + } + + const auto initialTempo = tempoChanges.empty() ? 120.0 : tempoChanges[0].second; + sequence->setInitialTempo(initialTempo); + if (!transport->isTempoSourceSequence()) + { + transport->setTempo(initialTempo); + } + + for (size_t i = 1; i < tempoChanges.size(); ++i) + { + const auto ratio = tempoChanges[i].second / initialTempo; + sequence->addTempoChangeEvent(tempoChanges[i].first, + static_cast(ratio * 1000.0)); + } + + const bool hadExplicitTimeSignatures = !timeSignatures.empty(); + if (!hadExplicitTimeSignatures) + { + timeSignatures.push_back( + TimeSigChange{0, mpc::DefaultTimeSigNumerator, mpc::DefaultTimeSigDenominator}); + } + + int lengthInTicks = 1; + for (const auto& trackPtr : *parsed.tracks()) + { + int trackTick = 0; + for (const auto& eventPtr : *trackPtr->events()->event()) + { + trackTick += eventPtr->v_time()->value(); + } + lengthInTicks = std::max(lengthInTicks, trackTick); + } + + int accumLength = 0; + int barCounter = 0; + for (size_t i = 0; i < timeSignatures.size(); ++i) + { + const auto current = timeSignatures[i]; + const auto hasNext = i + 1 < timeSignatures.size(); + const auto nextTick = hasNext ? timeSignatures[i + 1].tick : lengthInTicks; + + while (accumLength < nextTick) + { + sequence->setTimeSignature(barCounter, current.numerator, current.denominator); + const auto newDenTicks = 96 * (4.0 / current.denominator); + const auto barLength = static_cast(newDenTicks * current.numerator); + accumLength += barLength; + barCounter++; + } + } + + sequence->setLastBarIndex(barCounter - 1); + sequence->setFirstLoopBarIndex(mpc::BarIndex(firstLoopBar)); + if (lastLoopBar == -1) + { + sequence->setLastLoopBarIndex(mpc::EndOfSequence); + } + else + { + sequence->setLastLoopBarIndex(mpc::BarIndex(lastLoopBar)); + } + + for (size_t trackContainerIndex = 1; trackContainerIndex < parsed.tracks()->size(); ++trackContainerIndex) + { + const auto& midiTrack = parsed.tracks()->at(trackContainerIndex); + auto trackIndex = static_cast(trackContainerIndex - 1); + auto deviceIndex = 0; + auto busType = mpc::sequencer::BusType::DRUM1; + auto track = sequence->getTrack(trackIndex); + + struct OpenNote + { + mpc::sequencer::EventData event; + bool isClosed = false; + }; + + auto noteVariationType = mpc::NoteVariationTypeTune; + auto noteVariationValue = mpc::DefaultNoteVariationValue; + std::vector openNotes; + std::string pendingTrackName; + bool hasPendingTrackName = false; + bool hasImportedEvents = false; + int absoluteTick = 0; + + for (const auto& eventPtr : *midiTrack->events()->event()) + { + absoluteTick += eventPtr->v_time()->value(); + + if (const auto* meta = eventPtr->meta_event_body(); meta != nullptr) + { + const auto body = asciiBody(meta->body()); + if (meta->meta_type() == meta_type_t::META_TYPE_ENUM_TEXT_EVENT && + body.find("TRACK DATA:") != std::string::npos) + { + const auto payload = body.substr(std::string("TRACK DATA:").length()); + trackIndex = std::stoi(payload.substr(0, 2)); + if (payload.substr(2, 2) != "C0") + { + deviceIndex = + std::stoi(payload.substr(2, 2), nullptr, 16) - + std::stoi(std::string("E0"), nullptr, 16) + 1; + } + if (payload.size() >= 16) + { + busType = mpc::sequencer::busIndexToBusType( + std::stoi(payload.substr(14, 2), nullptr, 16)); + } + track = sequence->getTrack(trackIndex); + track->setBusType(busType, false); + track->setDeviceIndex(deviceIndex, false); + } + else if (meta->meta_type() == + meta_type_t::META_TYPE_ENUM_SEQUENCE_TRACK_NAME && + !body.empty()) + { + pendingTrackName = + body.substr(0, mpc::Mpc2000XlSpecs::MAX_TRACK_NAME_LENGTH); + hasPendingTrackName = true; + } + continue; + } + + switch (eventPtr->event_type()) + { + case 0x80: + { + const auto* noteOff = + dynamic_cast(eventPtr->event_body()); + if (noteOff == nullptr) + { + break; + } + noteVariationType = + mpc::NoteVariationType(noteOff->note()); + noteVariationValue = + mpc::NoteVariationValue(noteOff->velocity()); + break; + } + case 0x90: + { + const auto* noteOn = + dynamic_cast(eventPtr->event_body()); + if (noteOn == nullptr) + { + break; + } + + if (noteOn->velocity() == 0) + { + auto bestIt = openNotes.end(); + const auto isDefaultVariation = + [](const mpc::sequencer::EventData& event) + { + if (event.noteVariationType == mpc::NoteVariationTypeTune) + { + return event.noteVariationValue == + mpc::DefaultNoteVariationValue; + } + if (event.noteVariationType == mpc::NoteVariationTypeDecay || + event.noteVariationType == mpc::NoteVariationTypeAttack) + { + return event.noteVariationValue == 0; + } + if (event.noteVariationType == mpc::NoteVariationTypeFilter) + { + return event.noteVariationValue == 50; + } + return false; + }; + const auto isEligible = + [&](const OpenNote& openNote) + { + return !openNote.isClosed && + openNote.event.noteNumber == + mpc::NoteNumber(noteOn->note()) && + openNote.event.tick <= absoluteTick; + }; + + auto latestNonDefaultIt = openNotes.end(); + for (auto it = openNotes.rbegin(); it != openNotes.rend(); ++it) + { + if (!isEligible(*it) || isDefaultVariation(it->event)) + { + continue; + } + latestNonDefaultIt = std::prev(it.base()); + break; + } + + if (latestNonDefaultIt != openNotes.end()) + { + auto sameVariationCount = 0; + for (auto it = openNotes.begin(); it != openNotes.end(); ++it) + { + if (!isEligible(*it)) + { + continue; + } + if (it->event.noteVariationType == + latestNonDefaultIt->event.noteVariationType && + it->event.noteVariationValue == + latestNonDefaultIt->event.noteVariationValue) + { + sameVariationCount++; + if (bestIt == openNotes.end()) + { + bestIt = it; + } + } + } + + if (sameVariationCount < 2) + { + bestIt = openNotes.end(); + } + } + + if (bestIt == openNotes.end()) + { + for (auto it = openNotes.begin(); it != openNotes.end(); ++it) + { + if (!isEligible(*it)) + { + continue; + } + bestIt = it; + break; + } + } + + if (bestIt != openNotes.end()) + { + bestIt->event.duration = + mpc::Duration(absoluteTick - bestIt->event.tick); + bestIt->isClosed = true; + } + } + else + { + mpc::sequencer::EventData e; + e.type = mpc::sequencer::EventType::NoteOn; + e.noteNumber = mpc::NoteNumber(noteOn->note()); + e.tick = absoluteTick; + e.velocity = mpc::Velocity(noteOn->velocity()); + e.noteVariationType = noteVariationType; + e.noteVariationValue = noteVariationValue; + e.duration = mpc::Duration(24); + openNotes.push_back(OpenNote{e}); + noteVariationType = mpc::NoteVariationTypeTune; + noteVariationValue = mpc::DefaultNoteVariationValue; + } + break; + } + case 0xA0: + { + const auto* noteAftertouch = + dynamic_cast(eventPtr->event_body()); + if (noteAftertouch == nullptr) + { + break; + } + mpc::sequencer::EventData e; + e.type = mpc::sequencer::EventType::PolyPressure; + e.tick = absoluteTick; + e.noteNumber = mpc::NoteNumber(noteAftertouch->note()); + e.amount = noteAftertouch->pressure(); + track->acquireAndInsertEvent(e); + hasImportedEvents = true; + break; + } + case 0xB0: + { + const auto* controller = + dynamic_cast(eventPtr->event_body()); + if (controller == nullptr) + { + break; + } + mpc::sequencer::EventData e; + e.type = mpc::sequencer::EventType::ControlChange; + e.tick = absoluteTick; + e.controllerNumber = controller->controller(); + e.controllerValue = controller->value(); + track->acquireAndInsertEvent(e); + hasImportedEvents = true; + break; + } + case 0xC0: + { + const auto* programChange = + dynamic_cast(eventPtr->event_body()); + if (programChange == nullptr) + { + break; + } + mpc::sequencer::EventData e; + e.type = mpc::sequencer::EventType::ProgramChange; + e.tick = absoluteTick; + e.programChangeProgramIndex = + mpc::ProgramIndex(programChange->program()); + track->acquireAndInsertEvent(e); + hasImportedEvents = true; + break; + } + case 0xD0: + { + const auto* channelPressure = + dynamic_cast(eventPtr->event_body()); + if (channelPressure == nullptr) + { + break; + } + mpc::sequencer::EventData e; + e.type = mpc::sequencer::EventType::ChannelPressure; + e.tick = absoluteTick; + e.amount = channelPressure->pressure(); + track->acquireAndInsertEvent(e); + hasImportedEvents = true; + break; + } + case 0xE0: + { + auto* pitchBend = + dynamic_cast(eventPtr->event_body()); + if (pitchBend == nullptr) + { + break; + } + mpc::sequencer::EventData e; + e.type = mpc::sequencer::EventType::PitchBend; + e.tick = absoluteTick; + e.amount = pitchBend->bend_value(); + track->acquireAndInsertEvent(e); + hasImportedEvents = true; + break; + } + case 0xF0: + { + const auto* sysEx = eventPtr->sysex_body(); + if (sysEx == nullptr) + { + break; + } + const auto& data = sysEx->data(); + if (data.size() == 8 && + static_cast(data[0]) == 71 && + static_cast(data[1]) == 0 && + static_cast(data[2]) == 68 && + static_cast(data[3]) == 69 && + static_cast(data[7]) == 247) + { + mpc::sequencer::EventData e; + e.type = mpc::sequencer::EventType::Mixer; + e.tick = absoluteTick; + e.mixerParameter = static_cast(data[4]) - 1; + e.mixerPad = static_cast(data[5]); + e.mixerValue = static_cast(data[6]); + track->acquireAndInsertEvent(e); + } + else if (data.size() >= 2) + { + mpc::sequencer::EventData e; + e.type = mpc::sequencer::EventType::SystemExclusive; + e.tick = absoluteTick; + e.sysExByteA = static_cast(data[0]); + e.sysExByteB = static_cast(data[1]); + track->acquireAndInsertEvent(e); + } + hasImportedEvents = true; + break; + } + } + } + + for (const auto& openNote : openNotes) + { + track->acquireAndInsertEvent(openNote.event); + hasImportedEvents = true; + } + + if (hasImportedEvents) + { + track->setUsedIfCurrentlyUnused(); + } + + if (hasPendingTrackName) + { + track->setName(pendingTrackName); + } + } + + return true; +} +} // namespace + +sequence_or_error MidIo::loadBytes(mpc::Mpc &mpc, + const std::vector &bytes, + const std::string &fileNameWithoutExtension) +{ + const auto canonicalBytes = parseRewrite(bytes); + + auto newSeq = mpc.getSequencer()->getSequence(mpc::TempSequenceIndex); + newSeq->init(0); + mpc.getSequencer()->getStateManager()->drainQueue(); + + std::stringstream kaitaiStream( + std::string(canonicalBytes.begin(), canonicalBytes.end()), + std::ios::in | std::ios::out | std::ios::binary + ); + ::kaitai::kstream kaitaiIo(&kaitaiStream); + midi_t parsed(&kaitaiIo); + parsed._read(); + + if (!tryLoadMpc2000xl(mpc, parsed, newSeq)) + { + loadGenericStandardMidi(mpc, parsed, newSeq); + } + + if (newSeq->getName().empty()) + { + newSeq->setName(fileNameWithoutExtension); + } + + return newSeq; +} + +sequence_or_error MidIo::load(mpc::Mpc &mpc, const std::shared_ptr &file) +{ + return loadBytes(mpc, file->getBytes(), file->getNameWithoutExtension()); +} + +std::vector MidIo::saveBytes(const std::shared_ptr &sequence) +{ + return parseRewrite( + buildMpc2000xlMidiBytes(sequence)); +} + +void MidIo::save(const std::shared_ptr &sequence, + const std::shared_ptr &outputStream) +{ + const auto bytes = saveBytes(sequence); + outputStream->write(bytes.data(), static_cast(bytes.size())); +} diff --git a/src/main/file/kaitai/MidIo.hpp b/src/main/file/kaitai/MidIo.hpp new file mode 100644 index 000000000..d2cb8b42f --- /dev/null +++ b/src/main/file/kaitai/MidIo.hpp @@ -0,0 +1,37 @@ +#pragma once + +#include "mpc_types.hpp" + +#include +#include +#include + +namespace mpc +{ + class Mpc; +} + +namespace mpc::disk +{ + class MpcFile; +} + +namespace mpc::sequencer +{ + class Sequence; +} + +namespace mpc::file::kaitai +{ + class MidIo + { + public: + static sequence_or_error loadBytes( + mpc::Mpc &, + const std::vector &, + const std::string &fileNameWithoutExtension); + static sequence_or_error load(mpc::Mpc &, const std::shared_ptr &); + static std::vector saveBytes(const std::shared_ptr &); + static void save(const std::shared_ptr &, const std::shared_ptr &); + }; +} diff --git a/src/main/file/kaitai/Mpc3000SeqIo.cpp b/src/main/file/kaitai/Mpc3000SeqIo.cpp new file mode 100644 index 000000000..479e53c46 --- /dev/null +++ b/src/main/file/kaitai/Mpc3000SeqIo.cpp @@ -0,0 +1,802 @@ +#include "file/kaitai/Mpc3000SeqIo.hpp" + +#include "Mpc.hpp" +#include "MpcSpecs.hpp" +#include "StrUtil.hpp" +#include "disk/MpcFile.hpp" +#include "file/kaitai/generated/mpc60_seq_v2.h" +#include "file/kaitai/generated/mpc3000_seq_v3.h" +#include "sequencer/BusType.hpp" +#include "sequencer/EventData.hpp" +#include "sequencer/Sequence.hpp" +#include "sequencer/Sequencer.hpp" +#include "sequencer/SequencerStateManager.hpp" +#include "sequencer/Track.hpp" +#include "sequencer/Transport.hpp" +#include "utils/SimpleAction.hpp" + +#include +#include +#include +#include +#include +#include +#include + +using namespace mpc::file::kaitai; + +namespace +{ +using legacy_event_t = mpc3000_seq_v3_t::event_t; +using legacy_track_header_t = mpc3000_seq_v3_t::track_header_t; +using legacy_tempo_change_t = mpc3000_seq_v3_t::tempo_change_t; + +constexpr size_t kFixedPreludeSize = + 2 + 37 + 5 + (64 * 4) + 2 + 15 + 3 + 16 + 1 + 1 + 1; +constexpr size_t kTrackHeaderSize = 24; +constexpr size_t kTempoChangeSize = 6; + +struct ParsedSequenceView +{ + std::string sequenceName; + int barCount; + double headerTempo; + bool loopEnabled; + int loopToBarNumber; + int numberOfTrackHeaders; + int numberOfTempoChanges; + std::optional eventsStartOffset; + const std::vector> *trackHeaders; + const std::vector> *tempoChanges; + const std::vector> *parsedEvents; +}; + +struct TempoChangeData +{ + int tick; + double tempo; +}; + +uint16_t readU2Le(const std::vector &bytes, const size_t pos) +{ + return static_cast( + static_cast(bytes.at(pos)) | + (static_cast(bytes.at(pos + 1)) << 8)); +} + +std::string trimAscii(const std::string &value) +{ + return mpc::StrUtil::trim(value); +} + +std::vector payloadWithoutLookahead( + const std::vector &bytes, + size_t &pos) +{ + std::vector payload; + + while (pos < bytes.size()) + { + const auto byte = static_cast(bytes[pos++]); + if (byte > 0x7F) + { + if (!payload.empty()) + { + --pos; + break; + } + + payload.push_back(byte); + break; + } + + payload.push_back(byte); + } + + return payload; +} + +bool isMixerSysex(const std::vector &payload) +{ + return payload.size() >= 7 && payload[0] == 71 && payload[1] == 0 && + payload[2] == 68 && payload[3] == 69; +} + +mpc::TrackIndex resolveTrackIndex(const uint8_t rawTrackNumber) +{ + return mpc::TrackIndex(std::clamp(static_cast(rawTrackNumber) - 1, 0, + mpc::Mpc2000XlSpecs::TRACK_COUNT - 1)); +} + +void applyTrackHeader( + const legacy_track_header_t &header, + const std::shared_ptr &track) +{ + using mpc::sequencer::BusType; + + track->setOn(!header.track_mute(), false); + track->setBusType(header.drum_track() ? BusType::DRUM1 : BusType::MIDI, + false); + + if (!header.drum_track()) + { + track->setDeviceIndex( + std::clamp(static_cast(header.primary_port_channel_assignment()), + 0, 31), + false); + } + + track->setProgramChange(header.program_change_number(), false); + + const auto trackName = trimAscii(header.track_name()); + if (header.track_in_use()) + { + track->setUsedIfCurrentlyUnused( + mpc::utils::SimpleAction( + [track, trackName] + { + if (!trackName.empty()) + { + track->setName(trackName); + } + })); + } + else if (!trackName.empty()) + { + track->setName(trackName); + } +} + +bool isKnownEventStatus(const uint8_t status) +{ + switch (status) + { + case 0x88: + case 0x90: + case 0x98: + case 0xA0: + case 0xA8: + case 0xB0: + case 0xC0: + case 0xD0: + case 0xE0: + case 0xE8: + case 0xF0: + case 0xFF: + return true; + default: + return false; + } +} + +size_t eventStreamStartOffset( + const ParsedSequenceView &parsed, + const std::vector &bytes) +{ + if (parsed.eventsStartOffset.has_value()) + { + return std::min(*parsed.eventsStartOffset, bytes.size()); + } + + const auto start = kFixedPreludeSize + + (parsed.numberOfTrackHeaders * kTrackHeaderSize) + + (parsed.numberOfTempoChanges * kTempoChangeSize); + return std::min(start, bytes.size()); +} + +std::vector decodeTempoChanges(const ParsedSequenceView &parsed) +{ + std::vector result; + const auto *tempoChanges = parsed.tempoChanges; + if (tempoChanges == nullptr) + { + return result; + } + + result.reserve(tempoChanges->size()); + + for (const auto &tempoChangePtr : *tempoChanges) + { + if (!tempoChangePtr) + { + continue; + } + + result.push_back( + TempoChangeData{ + static_cast(tempoChangePtr->ticks_from_sequence_start()), + parsed.headerTempo * (tempoChangePtr->factor_percentage() / 100.0) + }); + } + + return result; +} + +ParsedSequenceView makeParsedSequenceView(const mpc3000_seq_v3_t &parsed) +{ + return ParsedSequenceView{ + trimAscii(parsed.sequence_header()->sequence_name()), + std::max(1, static_cast(parsed.sequence_header()->number_of_bars())), + static_cast(parsed.sequence_header()->tempo()) / 10.0, + parsed.sequence_header()->loop_to_bar() == mpc3000_seq_v3_t::OFF_ON_TRUE, + std::max(0, static_cast(parsed.sequence_header()->loop_to_bar_number())), + static_cast(parsed.num_track_headers()), + static_cast(parsed.num_tempo_changes()), + std::nullopt, + parsed.track_headers(), + parsed.tempo_changes(), + nullptr, + }; +} + +ParsedSequenceView makeParsedSequenceView(mpc60_seq_body_t::sequence_t &sequence) +{ + const auto *header = sequence.sequence_header(); + return ParsedSequenceView{ + trimAscii(header->sequence_name()), + std::max(1, static_cast(header->number_of_bars())), + static_cast(header->tempo()) / 10.0, + header->loop_to_bar() == mpc60_seq_body_t::OFF_ON_TRUE, + std::max(0, static_cast(header->loop_to_bar_number())), + static_cast(header->num_track_headers()), + static_cast(header->num_tempo_changes()), + static_cast(sequence.events_start()), + sequence.track_headers(), + sequence.tempo_changes(), + sequence.events(), + }; +} + +ParsedSequenceView makeParsedSequenceView(const mpc60_seq_v2_t &parsed) +{ + auto *sequence = + const_cast(parsed.sequence()); + return makeParsedSequenceView(*sequence); +} + +void loadEventsFromParsedObjects( + const ParsedSequenceView &parsed, + const std::shared_ptr &newSeq) +{ + int absoluteTick = 0; + + for (const auto &eventPtr : *parsed.parsedEvents) + { + auto &event = *eventPtr; + const auto status = event.status(); + + switch (status) + { + case 0x88: + { + auto *deltaTime = + dynamic_cast( + event.event_body()); + if (deltaTime != nullptr) + { + absoluteTick += deltaTime->delta_time(); + } + break; + } + case 0xA8: + { + auto *barEvent = + dynamic_cast( + event.event_body()); + if (barEvent != nullptr) + { + const auto barIndex = std::max(0, barEvent->bar_number() - 1); + newSeq->setTimeSignature( + barIndex, barEvent->numerator(), barEvent->denominator()); + } + break; + } + case 0xA0: + { + auto *polyPressure = + dynamic_cast( + event.event_body()); + if (polyPressure != nullptr) + { + const auto trackIndex = resolveTrackIndex(event.track_number()); + mpc::sequencer::EventData e; + e.type = mpc::sequencer::EventType::PolyPressure; + e.tick = absoluteTick; + e.noteNumber = mpc::NoteNumber(polyPressure->note()); + e.amount = polyPressure->pressure(); + newSeq->getTrack(trackIndex)->acquireAndInsertEvent(e); + } + break; + } + case 0xB0: + { + auto *controlChange = + dynamic_cast( + event.event_body()); + if (controlChange != nullptr) + { + const auto trackIndex = resolveTrackIndex(event.track_number()); + mpc::sequencer::EventData e; + e.type = mpc::sequencer::EventType::ControlChange; + e.tick = absoluteTick; + e.controllerNumber = + static_cast(controlChange->controller()); + e.controllerValue = controlChange->value(); + newSeq->getTrack(trackIndex)->acquireAndInsertEvent(e); + } + break; + } + case 0xC0: + { + auto *programChange = + dynamic_cast( + event.event_body()); + if (programChange != nullptr) + { + const auto trackIndex = resolveTrackIndex(event.track_number()); + mpc::sequencer::EventData e; + e.type = mpc::sequencer::EventType::ProgramChange; + e.tick = absoluteTick; + e.programChangeProgramIndex = + mpc::ProgramIndex(programChange->program()); + newSeq->getTrack(trackIndex)->acquireAndInsertEvent(e); + } + break; + } + case 0xD0: + { + auto *chPressure = + dynamic_cast( + event.event_body()); + if (chPressure != nullptr) + { + const auto trackIndex = resolveTrackIndex(event.track_number()); + mpc::sequencer::EventData e; + e.type = mpc::sequencer::EventType::ChannelPressure; + e.tick = absoluteTick; + e.amount = chPressure->pressure(); + newSeq->getTrack(trackIndex)->acquireAndInsertEvent(e); + } + break; + } + case 0xE0: + { + auto *pitchBend = + dynamic_cast( + event.event_body()); + if (pitchBend != nullptr) + { + const auto trackIndex = resolveTrackIndex(event.track_number()); + mpc::sequencer::EventData e; + e.type = mpc::sequencer::EventType::PitchBend; + e.tick = absoluteTick; + e.amount = pitchBend->corrected_pitch_bend_amount(); + newSeq->getTrack(trackIndex)->acquireAndInsertEvent(e); + } + break; + } + case 0xF0: + { + const auto trackIndex = resolveTrackIndex(event.track_number()); + auto track = newSeq->getTrack(trackIndex); + + if (event.mixer_event() != nullptr) + { + mpc::sequencer::EventData e; + e.type = mpc::sequencer::EventType::Mixer; + e.tick = absoluteTick; + e.mixerParameter = + static_cast(event.mixer_event()->param() - 1); + e.mixerPad = event.mixer_event()->pad_index(); + e.mixerValue = event.mixer_event()->value(); + track->acquireAndInsertEvent(e); + } + else if (event.system_exclusive_body() != nullptr && + !event.system_exclusive_body()->empty()) + { + mpc::sequencer::EventData e; + e.type = mpc::sequencer::EventType::SystemExclusive; + e.tick = absoluteTick; + e.sysExByteA = event.system_exclusive_body()->at(0); + e.sysExByteB = + event.system_exclusive_body()->size() > 1 + ? event.system_exclusive_body()->at(1) + : 0; + track->acquireAndInsertEvent(e); + } + break; + } + case 0xE8: + break; + case 0x90: + case 0x98: + { + auto *noteEvent = + dynamic_cast( + event.event_body()); + if (noteEvent != nullptr) + { + const auto trackIndex = resolveTrackIndex(event.track_number()); + mpc::sequencer::EventData e; + e.type = mpc::sequencer::EventType::NoteOn; + e.tick = absoluteTick; + e.noteNumber = mpc::NoteNumber(noteEvent->note_number()); + e.velocity = mpc::Velocity(noteEvent->velocity()); + e.noteVariationValue = + mpc::NoteVariationValue(noteEvent->note_variation_value()); + e.duration = mpc::Duration(noteEvent->duration()); + e.noteVariationType = + status == 0x90 + ? mpc::NoteVariationTypeTune + : mpc::NoteVariationType(status - 0x98); + newSeq->getTrack(trackIndex)->acquireAndInsertEvent(e); + } + break; + } + default: + break; + } + } +} + +void loadEventsFromRawBytes( + const ParsedSequenceView &parsed, + const std::vector &bytes, + const std::shared_ptr &newSeq) +{ + int absoluteTick = 0; + size_t pos = eventStreamStartOffset(parsed, bytes); + + if (pos >= bytes.size()) + { + return; + } + + uint8_t status = static_cast(bytes[pos++]); + if (status == 0xFF && pos < bytes.size() && + isKnownEventStatus(static_cast(bytes[pos]))) + { + status = static_cast(bytes[pos++]); + } + + while (true) + { + if (!isKnownEventStatus(status)) + { + break; + } + + if (status == 0xFF) + { + break; + } + + auto readTrack = [&]() -> mpc::TrackIndex + { + return resolveTrackIndex(static_cast(bytes.at(pos++))); + }; + + switch (status) + { + case 0x88: + absoluteTick += readU2Le(bytes, pos); + pos += 2; + break; + case 0xA8: + { + const auto barNumber = + static_cast(bytes.at(pos)) | + (static_cast(bytes.at(pos + 1)) << 7); + const auto numerator = + static_cast(bytes.at(pos + 2)); + const auto denominator = + static_cast(bytes.at(pos + 3)); + pos += 4; + const auto barIndex = std::max(0, barNumber - 1); + newSeq->setTimeSignature(barIndex, numerator, denominator); + break; + } + case 0xA0: + { + const auto trackIndex = readTrack(); + mpc::sequencer::EventData e; + e.type = mpc::sequencer::EventType::PolyPressure; + e.tick = absoluteTick; + e.noteNumber = + mpc::NoteNumber(static_cast(bytes.at(pos++))); + e.amount = static_cast(bytes.at(pos++)); + newSeq->getTrack(trackIndex)->acquireAndInsertEvent(e); + break; + } + case 0xB0: + { + const auto trackIndex = readTrack(); + mpc::sequencer::EventData e; + e.type = mpc::sequencer::EventType::ControlChange; + e.tick = absoluteTick; + e.controllerNumber = static_cast(bytes.at(pos++)); + e.controllerValue = static_cast(bytes.at(pos++)); + newSeq->getTrack(trackIndex)->acquireAndInsertEvent(e); + break; + } + case 0xC0: + { + const auto trackIndex = readTrack(); + mpc::sequencer::EventData e; + e.type = mpc::sequencer::EventType::ProgramChange; + e.tick = absoluteTick; + e.programChangeProgramIndex = + mpc::ProgramIndex(static_cast(bytes.at(pos++))); + newSeq->getTrack(trackIndex)->acquireAndInsertEvent(e); + break; + } + case 0xD0: + { + const auto trackIndex = readTrack(); + mpc::sequencer::EventData e; + e.type = mpc::sequencer::EventType::ChannelPressure; + e.tick = absoluteTick; + e.amount = static_cast(bytes.at(pos++)); + newSeq->getTrack(trackIndex)->acquireAndInsertEvent(e); + break; + } + case 0xE0: + { + const auto trackIndex = readTrack(); + const auto bits1 = static_cast(bytes.at(pos++)); + const auto bits2 = static_cast(bytes.at(pos++)); + mpc::sequencer::EventData e; + e.type = mpc::sequencer::EventType::PitchBend; + e.tick = absoluteTick; + e.amount = static_cast((bits1 + (bits2 << 7)) - 8192); + newSeq->getTrack(trackIndex)->acquireAndInsertEvent(e); + break; + } + case 0xF0: + { + const auto trackIndex = readTrack(); + const auto payload = payloadWithoutLookahead(bytes, pos); + auto track = newSeq->getTrack(trackIndex); + + if (isMixerSysex(payload)) + { + mpc::sequencer::EventData e; + e.type = mpc::sequencer::EventType::Mixer; + e.tick = absoluteTick; + e.mixerParameter = static_cast(payload.at(4) - 1); + e.mixerPad = payload.at(5); + e.mixerValue = payload.at(6); + track->acquireAndInsertEvent(e); + } + else if (!payload.empty()) + { + mpc::sequencer::EventData e; + e.type = mpc::sequencer::EventType::SystemExclusive; + e.tick = absoluteTick; + e.sysExByteA = payload[0]; + e.sysExByteB = payload.size() > 1 ? payload[1] : 0; + track->acquireAndInsertEvent(e); + } + break; + } + case 0xE8: + (void)readTrack(); + break; + case 0x90: + case 0x98: + { + const auto trackIndex = readTrack(); + mpc::sequencer::EventData e; + e.type = mpc::sequencer::EventType::NoteOn; + e.tick = absoluteTick; + e.noteNumber = + mpc::NoteNumber(static_cast(bytes.at(pos++))); + e.velocity = + mpc::Velocity(static_cast(bytes.at(pos++))); + e.noteVariationValue = mpc::NoteVariationValue( + static_cast(bytes.at(pos++))); + e.duration = mpc::Duration( + static_cast(bytes.at(pos)) | + (static_cast(bytes.at(pos + 1)) << 7)); + pos += 2; + e.noteVariationType = + status == 0x90 + ? mpc::NoteVariationTypeTune + : mpc::NoteVariationType(status - 0x98); + newSeq->getTrack(trackIndex)->acquireAndInsertEvent(e); + break; + } + default: + break; + } + + if (pos >= bytes.size()) + { + break; + } + + status = static_cast(bytes[pos++]); + if (status == 0xFF && pos < bytes.size() && + isKnownEventStatus(static_cast(bytes[pos]))) + { + status = static_cast(bytes[pos++]); + } + + if (status == 0xFF && pos >= bytes.size()) + { + break; + } + if (status == 0x00 && pos >= bytes.size()) + { + break; + } + } +} + +sequence_or_error loadParsedSequence( + mpc::Mpc &mpc, + const std::vector &bytes, + const std::string &fileNameWithoutExtension, + const ParsedSequenceView &parsed) +{ + auto newSeq = mpc.getSequencer()->getSequence(mpc::TempSequenceIndex); + + newSeq->init(parsed.barCount - 1); + mpc.getSequencer()->getStateManager()->drainQueue(); + + const auto tempoChanges = decodeTempoChanges(parsed); + + auto initialTempo = parsed.headerTempo; + bool consumedInitialTempoChange = false; + for (const auto &tempoChange : tempoChanges) + { + if (tempoChange.tick == 0) + { + initialTempo = tempoChange.tempo; + consumedInitialTempoChange = true; + break; + } + } + + newSeq->setInitialTempo(initialTempo); + const auto transport = mpc.getSequencer()->getTransport(); + if (!transport->isTempoSourceSequence()) + { + transport->setTempo(initialTempo); + } + + for (const auto &tempoChange : tempoChanges) + { + if (consumedInitialTempoChange && tempoChange.tick == 0) + { + consumedInitialTempoChange = false; + continue; + } + + if (tempoChange.tick <= 0) + { + continue; + } + + const auto ratio = static_cast( + std::round((tempoChange.tempo / initialTempo) * 1000.0)); + newSeq->addTempoChangeEvent(tempoChange.tick, ratio); + } + + newSeq->setName( + parsed.sequenceName.empty() ? fileNameWithoutExtension : parsed.sequenceName); + + newSeq->setFirstLoopBarIndex(mpc::BarIndex(0)); + newSeq->setLoopEnabled(parsed.loopEnabled); + if (parsed.loopEnabled) + { + newSeq->setLastLoopBarIndex( + mpc::BarIndex(std::max(0, parsed.loopToBarNumber - 1))); + } + else + { + newSeq->setLastLoopBarIndex(mpc::EndOfSequence); + } + + std::vector trackHeaders( + mpc::Mpc2000XlSpecs::TRACK_COUNT, nullptr); + for (const auto &headerPtr : *parsed.trackHeaders) + { + const auto *header = headerPtr.get(); + if (header->absolute_recorded_track_number() == -1) + { + continue; + } + + const auto trackIndex = std::clamp( + static_cast(header->user_track_number()) - 1, 0, + mpc::Mpc2000XlSpecs::TRACK_COUNT - 1); + trackHeaders[trackIndex] = header; + } + + if (parsed.parsedEvents != nullptr) + { + loadEventsFromParsedObjects(parsed, newSeq); + } + else + { + loadEventsFromRawBytes(parsed, bytes, newSeq); + } + + mpc.getSequencer()->getStateManager()->drainQueue(); + + for (size_t trackIndex = 0; trackIndex < trackHeaders.size(); ++trackIndex) + { + if (trackHeaders[trackIndex] == nullptr) + { + continue; + } + + applyTrackHeader(*trackHeaders[trackIndex], newSeq->getTrack(trackIndex)); + } + + mpc.getSequencer()->getStateManager()->drainQueue(); + + return newSeq; +} +} // namespace + +sequence_or_error Mpc3000SeqIo::loadMpc60Sequence( + mpc::Mpc &mpc, + const mpc60_seq_body_t::sequence_t &sequence, + const std::string &fileNameWithoutExtension) +{ + auto *mutableSequence = + const_cast(&sequence); + return loadParsedSequence( + mpc, {}, fileNameWithoutExtension, makeParsedSequenceView(*mutableSequence)); +} + +sequence_or_error Mpc3000SeqIo::loadBytes( + mpc::Mpc &mpc, + const std::vector &bytes, + const std::string &fileNameWithoutExtension) +{ + if (bytes.size() < 2) + { + return tl::make_unexpected("SEQ file is too short"); + } + + std::stringstream kaitaiStream( + std::string(bytes.begin(), bytes.end()), + std::ios::in | std::ios::out | std::ios::binary); + ::kaitai::kstream kaitaiIo(&kaitaiStream); + + const auto fileId = static_cast(bytes[0]); + const auto version = static_cast(bytes[1]); + + if (fileId != 0x03) + { + return tl::make_unexpected("SEQ first byte is incorrect"); + } + + if (version == 0x02) + { + mpc60_seq_v2_t parsed(&kaitaiIo); + parsed._read(); + return loadParsedSequence( + mpc, bytes, fileNameWithoutExtension, makeParsedSequenceView(parsed)); + } + + if (version == 0x03) + { + mpc3000_seq_v3_t parsed(&kaitaiIo); + parsed._read(); + return loadParsedSequence( + mpc, bytes, fileNameWithoutExtension, makeParsedSequenceView(parsed)); + } + + return tl::make_unexpected("Unsupported SEQ file format"); +} + +sequence_or_error +Mpc3000SeqIo::load(mpc::Mpc &mpc, + const std::shared_ptr &file) +{ + return loadBytes(mpc, file->getBytes(), file->getNameWithoutExtension()); +} diff --git a/src/main/file/kaitai/Mpc3000SeqIo.hpp b/src/main/file/kaitai/Mpc3000SeqIo.hpp new file mode 100644 index 000000000..b6cebf641 --- /dev/null +++ b/src/main/file/kaitai/Mpc3000SeqIo.hpp @@ -0,0 +1,36 @@ +#pragma once + +#include "mpc_types.hpp" +#include "file/kaitai/generated/mpc60_seq_body.h" + +#include +#include +#include + +namespace mpc +{ + class Mpc; +} + +namespace mpc::disk +{ + class MpcFile; +} + +namespace mpc::file::kaitai +{ + class Mpc3000SeqIo + { + public: + static sequence_or_error loadMpc60Sequence( + mpc::Mpc &, + const mpc60_seq_body_t::sequence_t &, + const std::string &fileNameWithoutExtension); + static sequence_or_error loadBytes( + mpc::Mpc &, + const std::vector &, + const std::string &fileNameWithoutExtension); + static sequence_or_error + load(mpc::Mpc &, const std::shared_ptr &); + }; +} // namespace mpc::file::kaitai diff --git a/src/main/file/kaitai/Mpc60SampleDecoder.cpp b/src/main/file/kaitai/Mpc60SampleDecoder.cpp new file mode 100644 index 000000000..8bbac6719 --- /dev/null +++ b/src/main/file/kaitai/Mpc60SampleDecoder.cpp @@ -0,0 +1,276 @@ +#include "file/kaitai/Mpc60SampleDecoder.hpp" + +#include "SampleOps.hpp" + +#include +#include +#include + +namespace +{ + constexpr uint16_t kMpc2000xlSetImportScale = 0x7fff; + + uint16_t u16(const uint32_t value) + { + return static_cast(value & 0xffffU); + } + + int16_t s16(const uint32_t value) + { + return static_cast(u16(value)); + } + + uint16_t cwd(const uint16_t ax) + { + return (ax & 0x8000U) != 0 ? 0xffffU : 0U; + } + + uint16_t shl1(uint16_t &value) + { + const auto carry = static_cast((value & 0x8000U) != 0); + value = u16(static_cast(value) << 1U); + return carry; + } + + uint16_t rcl1(uint16_t &value, const uint16_t carryIn) + { + const auto carry = static_cast((value & 0x8000U) != 0); + value = u16((static_cast(value) << 1U) | (carryIn & 1U)); + return carry; + } + + uint16_t sar1(uint16_t &value) + { + const auto carry = static_cast(value & 1U); + value = u16((value >> 1U) | (value & 0x8000U)); + return carry; + } + + uint16_t rcr1(uint16_t &value, const uint16_t carryIn) + { + const auto carry = static_cast(value & 1U); + value = u16((value >> 1U) | ((carryIn & 1U) << 15U)); + return carry; + } + + uint16_t add16(uint16_t &a, const uint16_t b) + { + const auto sum = static_cast(a) + b; + a = u16(sum); + return static_cast(sum > 0xffffU); + } + + uint16_t adc16(uint16_t &a, const uint16_t b, const uint16_t carryIn) + { + const auto sum = static_cast(a) + b + (carryIn & 1U); + a = u16(sum); + return static_cast(sum > 0xffffU); + } + + uint16_t sub16(uint16_t &a, const uint16_t b) + { + const auto carry = static_cast(a < b); + a = u16(static_cast(a) - b); + return carry; + } + + uint16_t sbb16(uint16_t &a, const uint16_t b, const uint16_t carryIn) + { + const auto rhs = static_cast(b) + (carryIn & 1U); + const auto carry = static_cast(a < rhs); + a = u16(static_cast(a) - rhs); + return carry; + } + + void leftShift48(uint16_t &bx, uint16_t &ax, uint16_t &dx, const int count) + { + uint16_t carry = 0; + for (int i = 0; i < count; ++i) + { + carry = shl1(bx); + carry = rcl1(ax, carry); + carry = rcl1(dx, carry); + } + } + + void arithmeticRightShift32(uint16_t &hi, uint16_t &lo, const int count) + { + uint16_t carry = 0; + for (int i = 0; i < count; ++i) + { + carry = sar1(hi); + carry = rcr1(lo, carry); + } + } + + enum class TransformOp + { + Add, + Subtract, + ShiftOnly, + }; + + struct TransformStep + { + TransformOp op; + int shift; + }; + + std::pair + transform(const uint16_t lo, const uint16_t hi, + const std::initializer_list steps) + { + auto bx = lo; + auto ax = hi; + auto dx = cwd(ax); + const auto sign = dx; + + for (const auto &step : steps) + { + leftShift48(bx, ax, dx, step.shift); + + if (step.op == TransformOp::Add) + { + auto carry = add16(bx, lo); + carry = adc16(ax, hi, carry); + adc16(dx, sign, carry); + } + else if (step.op == TransformOp::Subtract) + { + auto carry = sub16(bx, lo); + carry = sbb16(ax, hi, carry); + sbb16(dx, sign, carry); + } + } + + arithmeticRightShift32(dx, ax, 4); + return {ax, dx}; + } +} + +int16_t mpc::file::kaitai::Mpc60SampleDecoder::decodeImportedPcm( + const uint16_t sampleWord, const bool isOddWordIndex) +{ + const auto inputWord = toFirmwareInputWord(sampleWord, isOddWordIndex); + + const auto oldLo = stateLo; + const auto oldHi = stateHi; + + const auto a = transform(oldLo, oldHi, + {{TransformOp::Add, 2}, + {TransformOp::Subtract, 4}, + {TransformOp::Add, 4}, + {TransformOp::Add, 4}, + {TransformOp::Add, 2}, + {TransformOp::Subtract, 3}}); + + auto newLo = oldLo; + auto newHi = inputWord; + arithmeticRightShift32(newHi, newLo, 8); + auto carry = add16(newLo, a.first); + adc16(newHi, a.second, carry); + + const auto b = transform(newLo, newHi, + {{TransformOp::Add, 1}, + {TransformOp::Subtract, 3}, + {TransformOp::Subtract, 3}, + {TransformOp::Subtract, 3}, + {TransformOp::Add, 5}, + {TransformOp::ShiftOnly, 3}}); + + const auto c = transform(oldLo, oldHi, + {{TransformOp::Subtract, 2}, + {TransformOp::Add, 3}, + {TransformOp::Add, 4}, + {TransformOp::Add, 1}, + {TransformOp::Subtract, 3}, + {TransformOp::Add, 2}}); + + auto mixedLo = b.first; + auto mixedHi = b.second; + carry = add16(mixedLo, c.first); + adc16(mixedHi, c.second, carry); + + stateLo = newLo; + stateHi = newHi; + + auto tmpLo = mixedLo; + auto tmpHi = mixedHi; + arithmeticRightShift32(tmpHi, tmpLo, 3); + + auto outLo = mixedLo; + auto outHi = mixedHi; + carry = add16(outLo, tmpLo); + carry = adc16(outHi, tmpHi, carry); + + arithmeticRightShift32(tmpHi, tmpLo, 1); + carry = add16(outLo, tmpLo); + adc16(outHi, tmpHi, carry); + + return scaleAndClip(outLo, outHi); +} + +float mpc::file::kaitai::Mpc60SampleDecoder::decodeImportedFloat( + const uint16_t sampleWord, const bool isOddWordIndex) +{ + return mpc::sampleops::short_to_float( + decodeImportedPcm(sampleWord, isOddWordIndex)); +} + +uint16_t mpc::file::kaitai::Mpc60SampleDecoder::toFirmwareInputWord( + const uint16_t sampleWord, const bool isOddWordIndex) +{ + if (isOddWordIndex) + { + return static_cast((sampleWord & 0x0fffU) << 4U); + } + + return static_cast(((sampleWord & 0x00ffU) << 8U) | + ((sampleWord & 0x0f00U) >> 4U)); +} + +int16_t mpc::file::kaitai::Mpc60SampleDecoder::scaleAndClip(uint16_t lo, + uint16_t hi) +{ + const auto negative = (hi & 0x8000U) != 0; + if (!negative) + { + if ((hi >> 8U) != 0) + { + return static_cast(kMpc2000xlSetImportScale - 1); + } + + const auto magnitude = static_cast( + ((hi & 0x00ffU) << 8U) | ((lo >> 8U) & 0x00ffU)); + if (magnitude >= kMpc2000xlSetImportScale) + { + return static_cast(kMpc2000xlSetImportScale - 1); + } + } + else + { + if ((hi >> 8U) != 0x00ffU) + { + return static_cast( + -static_cast(kMpc2000xlSetImportScale - 1)); + } + + const auto magnitude = static_cast( + -static_cast(static_cast( + ((hi & 0x00ffU) << 8U) | ((lo >> 8U) & 0x00ffU)))); + if (magnitude >= kMpc2000xlSetImportScale) + { + return static_cast( + -static_cast(kMpc2000xlSetImportScale - 1)); + } + } + + for (int i = 0; i < 7; ++i) + { + const auto carry = shl1(lo); + rcl1(hi, carry); + } + + const auto dividend = static_cast(s16(hi)) * 65536 + lo; + return static_cast(dividend / kMpc2000xlSetImportScale); +} diff --git a/src/main/file/kaitai/Mpc60SampleDecoder.hpp b/src/main/file/kaitai/Mpc60SampleDecoder.hpp new file mode 100644 index 000000000..711502a4f --- /dev/null +++ b/src/main/file/kaitai/Mpc60SampleDecoder.hpp @@ -0,0 +1,23 @@ +#pragma once + +#include + +namespace mpc::file::kaitai +{ + constexpr int kMpc60SampleRate = 40000; + + class Mpc60SampleDecoder + { + public: + int16_t decodeImportedPcm(uint16_t sampleWord, bool isOddWordIndex); + float decodeImportedFloat(uint16_t sampleWord, bool isOddWordIndex); + + private: + static uint16_t toFirmwareInputWord(uint16_t sampleWord, + bool isOddWordIndex); + static int16_t scaleAndClip(uint16_t lo, uint16_t hi); + + uint16_t stateLo = 0; + uint16_t stateHi = 0; + }; +} diff --git a/src/main/file/kaitai/Mpc60SetPreview.cpp b/src/main/file/kaitai/Mpc60SetPreview.cpp new file mode 100644 index 000000000..e81de7008 --- /dev/null +++ b/src/main/file/kaitai/Mpc60SetPreview.cpp @@ -0,0 +1,151 @@ +#include "Mpc60SetPreview.hpp" + +#include "disk/MpcFile.hpp" +#include "file/kaitai/generated/mpc60_set_v1.h" + +#include + +#include +#include +#include +#include +#include + +namespace +{ + constexpr uint8_t kNoSoundDirectoryEntry = 255; + + std::string trimRightSpaces(std::string value) + { + while (!value.empty() && value.back() == ' ') + { + value.pop_back(); + } + return value; + } + + constexpr std::array kMpc60PadNames{ + "hiht_clsd", "hiht_medm", "hiht_open", "snr1", "snr2", "bass", + "tom1", "tom2", "tom3", "tom4", "rid1", "rid2", + "crs1", "crs2", "prc1", "prc2", "prc3", "prc4", + "dr01", "dr02", "dr03", "dr04", "dr05", "dr06", + "dr07", "dr08", "dr09", "dr10", "dr11", "dr12", + "dr13", "dr14", "dr15", "dr16", + }; + + std::string formatSourceSlotLabel(const char bank, const int slotNumber) + { + std::ostringstream oss; + oss << bank << std::setw(2) << std::setfill('0') << slotNumber; + return oss.str(); + } +} + +const mpc::file::kaitai::Mpc60SetPreviewEntry * +mpc::file::kaitai::Mpc60SetPreview::assignedSoundAtMpc60Pad( + const size_t padIndex) const +{ + if (padIndex >= soundDirectoryEntryIndexByMpc60Pad.size()) + { + return nullptr; + } + + const auto soundIndex = soundDirectoryEntryIndexByMpc60Pad[padIndex]; + if (soundIndex >= soundDirectoryEntries.size()) + { + return nullptr; + } + + return &soundDirectoryEntries[soundIndex]; +} + +std::string +mpc::file::kaitai::Mpc60SetPreview::mpc60PadName(const size_t padIndex) +{ + if (padIndex >= kMpc60PadNames.size()) + { + return {}; + } + + return std::string(kMpc60PadNames[padIndex]); +} + +std::string +mpc::file::kaitai::Mpc60SetPreview::mpc60SourceSlotLabel( + const size_t padIndex) +{ + if (padIndex <= 2) + { + return formatSourceSlotLabel('A', 1); + } + + if (padIndex <= 17) + { + return formatSourceSlotLabel('A', static_cast(padIndex) - 1); + } + + if (padIndex <= 33) + { + return formatSourceSlotLabel('B', static_cast(padIndex) - 17); + } + + return {}; +} + +mpc::file::kaitai::Mpc60SetPreview +mpc::file::kaitai::Mpc60SetPreviewLoader::loadPreview( + const std::shared_ptr &file) +{ + return loadPreview(file->getBytes()); +} + +mpc::file::kaitai::Mpc60SetPreview +mpc::file::kaitai::Mpc60SetPreviewLoader::loadPreview( + const std::vector &bytes) +{ + std::stringstream parseStream( + std::string(bytes.begin(), bytes.end()), + std::ios::in | std::ios::out | std::ios::binary + ); + ::kaitai::kstream parseIo(&parseStream); + mpc60_set_v1_t parsed(&parseIo); + parsed._read(); + + Mpc60SetPreview result; + result.totalNumberOfSampleWords = parsed.total_number_of_sample_words()->value(); + result.useMasterMixData = + parsed.use_master_mix_data() == mpc60_set_v1_t::USE_MASTER_MIX_DATA_USE; + + result.soundDirectoryEntries.reserve(parsed.sound_directory_entry()->size()); + for (const auto &entry : *parsed.sound_directory_entry()) + { + Mpc60SetPreviewEntry previewEntry; + previewEntry.name = trimRightSpaces(entry->name()); + previewEntry.sharedSoundLink = entry->shared_sound_link(); + previewEntry.lengthInSamples = entry->length_in_samples(); + previewEntry.startAddressForPlaying = entry->start_address_for_playing(); + previewEntry.soundDuration = entry->sound_duration(); + previewEntry.requestedStereoMixVolume = + entry->requested_stereo_mix_volume(); + previewEntry.requestedStereoMixPan = entry->requested_stereo_mix_pan(); + previewEntry.isHihat = + entry->sound_characteristics()->normal_or_hihat_sound() == + mpc60_set_v1_t::NORMAL_OR_HIHAT_HIHAT; + result.soundDirectoryEntries.push_back(std::move(previewEntry)); + } + + result.soundDirectoryEntryIndexByMpc60Pad.reserve( + parsed.sound_map()->size() + ); + for (const auto entryIndex : *parsed.sound_map()) + { + if (entryIndex != kNoSoundDirectoryEntry && + entryIndex >= result.soundDirectoryEntries.size()) + { + throw std::runtime_error("MPC60 SET sound_map entry index out of range"); + } + result.soundDirectoryEntryIndexByMpc60Pad.push_back(entryIndex); + } + + return result; +} diff --git a/src/main/file/kaitai/Mpc60SetPreview.hpp b/src/main/file/kaitai/Mpc60SetPreview.hpp new file mode 100644 index 000000000..9ceefc459 --- /dev/null +++ b/src/main/file/kaitai/Mpc60SetPreview.hpp @@ -0,0 +1,48 @@ +#pragma once + +#include +#include +#include +#include + +namespace mpc::disk +{ + class MpcFile; +} + +namespace mpc::file::kaitai +{ + struct Mpc60SetPreviewEntry + { + std::string name; + uint8_t sharedSoundLink = 0; + uint32_t lengthInSamples = 0; + uint32_t startAddressForPlaying = 0; + uint16_t soundDuration = 0; + uint8_t requestedStereoMixVolume = 0; + uint8_t requestedStereoMixPan = 0; + bool isHihat = false; + }; + + struct Mpc60SetPreview + { + uint32_t totalNumberOfSampleWords = 0; + bool useMasterMixData = false; + std::vector soundDirectoryEntries; + std::vector soundDirectoryEntryIndexByMpc60Pad; + + const Mpc60SetPreviewEntry *assignedSoundAtMpc60Pad(size_t padIndex) const; + + static std::string mpc60PadName(size_t padIndex); + static std::string mpc60SourceSlotLabel(size_t padIndex); + }; + + class Mpc60SetPreviewLoader + { + public: + static Mpc60SetPreview + loadPreview(const std::shared_ptr &file); + + static Mpc60SetPreview loadPreview(const std::vector &bytes); + }; +} diff --git a/src/main/file/kaitai/Mpc60SetProgramLoader.cpp b/src/main/file/kaitai/Mpc60SetProgramLoader.cpp new file mode 100644 index 000000000..6d97d5cec --- /dev/null +++ b/src/main/file/kaitai/Mpc60SetProgramLoader.cpp @@ -0,0 +1,245 @@ +#include "file/kaitai/Mpc60SetProgramLoader.hpp" + +#include "Mpc.hpp" +#include "disk/MpcFile.hpp" +#include "file/kaitai/Mpc60SetPreview.hpp" +#include "file/kaitai/Mpc60SetSoundLoader.hpp" +#include "lcdgui/LayeredScreen.hpp" +#include "performance/PerformanceManager.hpp" +#include "performance/PerformanceMessage.hpp" +#include "sampler/Pad.hpp" +#include "sampler/Program.hpp" +#include "sampler/Sampler.hpp" +#include "sampler/SoundGenerationMode.hpp" +#include "sequencer/Bus.hpp" +#include "sequencer/Sequencer.hpp" +#include "sequencer/Track.hpp" +#include "StrUtil.hpp" + +#include +#include +#include + +using namespace mpc::file::kaitai; + +namespace +{ +constexpr int kImportedMpc60HihatVelocityThreshold1 = 14; +constexpr int kImportedMpc60HihatVelocityThreshold2 = 42; + +void configureImportedHihatSwitching( + const Mpc60SetPreview &preview, + const Mpc60SetProgramLoader::ConversionTable &conversionTable, + mpc::performance::Program &perfProgram) +{ + if (preview.soundDirectoryEntryIndexByMpc60Pad.size() < 3 || + conversionTable.size() < 3) + { + return; + } + + const auto *closed = preview.assignedSoundAtMpc60Pad(0); + const auto *medium = preview.assignedSoundAtMpc60Pad(1); + const auto *open = preview.assignedSoundAtMpc60Pad(2); + + if (closed == nullptr || medium == nullptr || open == nullptr || + !closed->isHihat || !medium->isHihat || !open->isHihat) + { + return; + } + + auto &closedHatNoteParams = + perfProgram.noteParameters[conversionTable[0].get() - + mpc::MinDrumNoteNumber.get()]; + closedHatNoteParams.soundGenerationMode = mpc::sampler::toRaw( + mpc::sampler::SoundGenerationMode::VelocitySwitch); + closedHatNoteParams.velocityRangeLower = + kImportedMpc60HihatVelocityThreshold1; + closedHatNoteParams.velocityRangeUpper = + kImportedMpc60HihatVelocityThreshold2; + closedHatNoteParams.optionalNoteA = conversionTable[1]; + closedHatNoteParams.optionalNoteB = conversionTable[2]; +} + +bool loadIntoProgram( + mpc::Mpc &mpc, + const std::shared_ptr &file, + const Mpc60SetPreview &preview, + const Mpc60SetProgramLoader::ConversionTable &conversionTable, + const std::shared_ptr &program, + mpc::performance::Program &perfProgram) +{ + auto sampler = mpc.getSampler(); + const auto existingSoundCount = sampler->getSoundCount(); + std::vector soundIndexByEntry(preview.soundDirectoryEntries.size(), -1); + + for (size_t entryIndex = 0; entryIndex < preview.soundDirectoryEntries.size(); + ++entryIndex) + { + const auto soundName = preview.soundDirectoryEntries[entryIndex].name; + if (preview.soundDirectoryEntries[entryIndex].lengthInSamples == 0) + { + continue; + } + + const auto msg = + "Loading " + + mpc::StrUtil::toUpper(mpc::StrUtil::padRight(soundName, " ", 16)); + const auto ls = mpc.getLayeredScreen(); + ls->postToUiThread(mpc::utils::Task( + [ls, msg] + { + ls->showPopup(msg); + })); + std::this_thread::sleep_for(std::chrono::milliseconds(50)); + + auto sound = sampler->addSound(40000); + if (sound == nullptr) + { + return false; + } + + const auto result = + Mpc60SetSoundLoader::loadSoundDirectoryEntry(file, entryIndex, sound); + + if (!result.has_value()) + { + sampler->deleteSound(sound); + return false; + } + + soundIndexByEntry[entryIndex] = + existingSoundCount + static_cast(entryIndex); + } + + program->setName(file->getNameWithoutExtension()); + perfProgram.used = true; + + const auto &padNotes = mpc::sampler::Pad::getPadNotes(mpc); + for (int8_t padIndex = 0; + padIndex < mpc::Mpc2000XlSpecs::PROGRAM_PAD_COUNT; + ++padIndex) + { + perfProgram.pads[padIndex].note = padNotes[padIndex]; + } + + for (size_t mpc60PadIndex = 0; + mpc60PadIndex < preview.soundDirectoryEntryIndexByMpc60Pad.size() && + mpc60PadIndex < conversionTable.size(); + ++mpc60PadIndex) + { + const auto entryIndex = preview.soundDirectoryEntryIndexByMpc60Pad[mpc60PadIndex]; + if (entryIndex >= soundIndexByEntry.size()) + { + continue; + } + + const auto soundIndex = soundIndexByEntry[entryIndex]; + + if (soundIndex < 0) + { + continue; + } + + perfProgram + .noteParameters[conversionTable[mpc60PadIndex].get() - + mpc::MinDrumNoteNumber.get()] + .soundIndex = soundIndex; + } + + configureImportedHihatSwitching(preview, conversionTable, perfProgram); + + if (const auto track = mpc.getSequencer()->getSelectedTrack(); + mpc::sequencer::isDrumBusType(track->getBusType())) + { + const auto drumBus = mpc.getSequencer()->getDrumBus(track->getBusType()); + drumBus->setProgramIndex(program->getProgramIndex()); + } + + return true; +} + +void resetPerformancePrograms(mpc::Mpc &mpc) +{ + for (int i = 0; i < mpc::Mpc2000XlSpecs::MAX_PROGRAM_COUNT; ++i) + { + mpc::performance::UpdateProgramBulk msg; + msg.programIndex = mpc::ProgramIndex(i); + mpc.getPerformanceManager().lock()->enqueue( + mpc::performance::PerformanceMessage{std::move(msg)}); + } +} +} // namespace + +Mpc60SetProgramLoader::ConversionTable +Mpc60SetProgramLoader::defaultConversionTable() +{ + return ConversionTable{ + DrumNoteNumber(42), DrumNoteNumber(82), DrumNoteNumber(46), + DrumNoteNumber(38), DrumNoteNumber(37), DrumNoteNumber(36), + DrumNoteNumber(48), DrumNoteNumber(47), DrumNoteNumber(45), + DrumNoteNumber(43), DrumNoteNumber(51), DrumNoteNumber(53), + DrumNoteNumber(49), DrumNoteNumber(55), DrumNoteNumber(69), + DrumNoteNumber(54), DrumNoteNumber(56), DrumNoteNumber(39), + DrumNoteNumber(57), DrumNoteNumber(58), DrumNoteNumber(59), + DrumNoteNumber(60), DrumNoteNumber(61), DrumNoteNumber(62), + DrumNoteNumber(63), DrumNoteNumber(64), DrumNoteNumber(65), + DrumNoteNumber(66), DrumNoteNumber(67), DrumNoteNumber(68), + DrumNoteNumber(70), DrumNoteNumber(71), DrumNoteNumber(72), + DrumNoteNumber(73)}; +} + +bool Mpc60SetProgramLoader::load( + mpc::Mpc &mpc, + const std::shared_ptr &file, + const Mpc60SetPreview &preview, + const ConversionTable &conversionTable, + const bool clearExisting) +{ + auto sampler = mpc.getSampler(); + + if (clearExisting) + { + sampler->deleteAllSamples(); + resetPerformancePrograms(mpc); + + mpc::performance::UpdateProgramBulk msg; + msg.programIndex = mpc::ProgramIndex(0); + const auto loaded = loadIntoProgram(mpc, file, preview, conversionTable, + mpc.getSampler()->getProgram(0), + msg.program); + if (loaded) + { + mpc.getPerformanceManager().lock()->enqueue( + mpc::performance::PerformanceMessage{std::move(msg)}); + } + return loaded; + } + + std::shared_ptr program; + for (int i = 0; i < mpc::Mpc2000XlSpecs::MAX_PROGRAM_COUNT; ++i) + { + const auto candidate = sampler->getProgram(i); + if (!candidate->isUsed()) + { + program = candidate; + break; + } + } + + if (!program) + { + return false; + } + + mpc::performance::UpdateProgramBulk msg; + msg.programIndex = program->getProgramIndex(); + const auto loaded = loadIntoProgram(mpc, file, preview, conversionTable, + program, msg.program); + if (loaded) + { + mpc.getPerformanceManager().lock()->enqueue( + mpc::performance::PerformanceMessage{std::move(msg)}); + } + return loaded; +} diff --git a/src/main/file/kaitai/Mpc60SetProgramLoader.hpp b/src/main/file/kaitai/Mpc60SetProgramLoader.hpp new file mode 100644 index 000000000..879345055 --- /dev/null +++ b/src/main/file/kaitai/Mpc60SetProgramLoader.hpp @@ -0,0 +1,37 @@ +#pragma once + +#include "IntTypes.hpp" + +#include +#include + +namespace mpc +{ + class Mpc; +} + +namespace mpc::disk +{ + class MpcFile; +} + +namespace mpc::file::kaitai +{ + struct Mpc60SetPreview; + + class Mpc60SetProgramLoader + { + public: + static constexpr size_t kMpc60PadCount = 34; + using ConversionTable = std::array; + + static ConversionTable defaultConversionTable(); + + static bool load( + Mpc &mpc, + const std::shared_ptr &file, + const Mpc60SetPreview &preview, + const ConversionTable &conversionTable, + bool clearExisting); + }; +} diff --git a/src/main/file/kaitai/Mpc60SetSoundLoader.cpp b/src/main/file/kaitai/Mpc60SetSoundLoader.cpp new file mode 100644 index 000000000..30a67351c --- /dev/null +++ b/src/main/file/kaitai/Mpc60SetSoundLoader.cpp @@ -0,0 +1,127 @@ +#include "file/kaitai/Mpc60SetSoundLoader.hpp" + +#include "disk/MpcFile.hpp" +#include "file/kaitai/Mpc60SampleDecoder.hpp" +#include "file/kaitai/generated/mpc60_set_v1.h" +#include "sampler/Sound.hpp" + +#include + +#include + +namespace +{ + std::string trimRightSpaces(std::string value) + { + while (!value.empty() && value.back() == ' ') + { + value.pop_back(); + } + return value; + } + sound_or_error loadEntry(mpc60_set_v1_t &parsed, + const size_t soundDirectoryEntryIndex, + const std::shared_ptr &sound) + { + const auto *entries = parsed.sound_directory_entry(); + if (soundDirectoryEntryIndex >= entries->size()) + { + return tl::make_unexpected("SET sound entry index out of range"); + } + + const auto &entry = entries->at(soundDirectoryEntryIndex); + const auto start = + static_cast(entry->start_address_in_memory()); + const auto length = static_cast(entry->length_in_samples()); + const auto *sampleWords = parsed.sound_samples(); + + if (start > sampleWords->size() || length > sampleWords->size() - start) + { + return tl::make_unexpected("SET sound sample range out of bounds"); + } + + sound->setName(trimRightSpaces(entry->name())); + sound->setSampleRate(mpc::file::kaitai::kMpc60SampleRate); + sound->setMono(true); + sound->setLevel(100); + sound->setTune(0); + sound->setLoopEnabled(false); + + auto sampleData = sound->getMutableSampleData(); + sampleData->clear(); + sampleData->reserve(length); + + mpc::file::kaitai::Mpc60SampleDecoder converter; + for (size_t i = 0; i < length; ++i) + { + sampleData->push_back(converter.decodeImportedFloat( + static_cast(sampleWords->at(start + i)), + ((start + i) % 2U) != 0)); + } + + sound->setStart(0); + sound->setEnd(sound->getLastFrameIndex()); + sound->setLoopTo(0); + + return sound; + } +} // namespace + +sound_or_error mpc::file::kaitai::Mpc60SetSoundLoader::loadSoundDirectoryEntry( + const std::shared_ptr &file, + const size_t soundDirectoryEntryIndex, + const std::shared_ptr &sound) +{ + return loadSoundDirectoryEntry(file->getBytes(), soundDirectoryEntryIndex, + sound); +} + +sound_or_error mpc::file::kaitai::Mpc60SetSoundLoader::loadSoundDirectoryEntry( + const std::vector &bytes, const size_t soundDirectoryEntryIndex, + const std::shared_ptr &sound) +{ + std::stringstream parseStream(std::string(bytes.begin(), bytes.end()), + std::ios::in | std::ios::out | + std::ios::binary); + ::kaitai::kstream parseIo(&parseStream); + mpc60_set_v1_t parsed(&parseIo); + parsed._read(); + + return loadEntry(parsed, soundDirectoryEntryIndex, sound); +} + +sound_or_error +mpc::file::kaitai::Mpc60SetSoundLoader::loadAssignedSoundAtMpc60Pad( + const std::shared_ptr &file, const size_t mpc60PadIndex, + const std::shared_ptr &sound) +{ + return loadAssignedSoundAtMpc60Pad(file->getBytes(), mpc60PadIndex, sound); +} + +sound_or_error +mpc::file::kaitai::Mpc60SetSoundLoader::loadAssignedSoundAtMpc60Pad( + const std::vector &bytes, const size_t mpc60PadIndex, + const std::shared_ptr &sound) +{ + std::stringstream parseStream(std::string(bytes.begin(), bytes.end()), + std::ios::in | std::ios::out | + std::ios::binary); + ::kaitai::kstream parseIo(&parseStream); + mpc60_set_v1_t parsed(&parseIo); + parsed._read(); + + const auto *soundMap = parsed.sound_map(); + if (mpc60PadIndex >= soundMap->size()) + { + return tl::make_unexpected("SET MPC60 pad index out of range"); + } + + const auto entryIndex = soundMap->at(mpc60PadIndex); + const auto *entries = parsed.sound_directory_entry(); + if (entryIndex >= entries->size()) + { + return tl::make_unexpected("SET sound entry index out of range"); + } + + return loadEntry(parsed, entryIndex, sound); +} diff --git a/src/main/file/kaitai/Mpc60SetSoundLoader.hpp b/src/main/file/kaitai/Mpc60SetSoundLoader.hpp new file mode 100644 index 000000000..f97312925 --- /dev/null +++ b/src/main/file/kaitai/Mpc60SetSoundLoader.hpp @@ -0,0 +1,44 @@ +#pragma once + +#include "mpc_types.hpp" + +#include +#include +#include + +namespace mpc::disk +{ + class MpcFile; +} + +namespace mpc::sampler +{ + class Sound; +} + +namespace mpc::file::kaitai +{ + class Mpc60SetSoundLoader + { + public: + static sound_or_error loadSoundDirectoryEntry( + const std::shared_ptr &file, + size_t soundDirectoryEntryIndex, + const std::shared_ptr &sound); + + static sound_or_error loadSoundDirectoryEntry( + const std::vector &bytes, + size_t soundDirectoryEntryIndex, + const std::shared_ptr &sound); + + static sound_or_error loadAssignedSoundAtMpc60Pad( + const std::shared_ptr &file, + size_t mpc60PadIndex, + const std::shared_ptr &sound); + + static sound_or_error loadAssignedSoundAtMpc60Pad( + const std::vector &bytes, + size_t mpc60PadIndex, + const std::shared_ptr &sound); + }; +} diff --git a/src/main/file/kaitai/PgmIo.cpp b/src/main/file/kaitai/PgmIo.cpp new file mode 100644 index 000000000..f607888af --- /dev/null +++ b/src/main/file/kaitai/PgmIo.cpp @@ -0,0 +1,628 @@ +#include "file/kaitai/PgmIo.hpp" + +#include "Mpc.hpp" +#include "StrUtil.hpp" +#include "disk/MpcFile.hpp" +#include "engine/IndivFxMixer.hpp" +#include "engine/StereoMixer.hpp" +#include "file/ByteUtil.hpp" +#include "file/kaitai/KaitaiIoUtil.hpp" +#include "file/kaitai/generated/mpc2000xl_pgm.h" +#include "file/kaitai/generated/mpc3000_pgm_v3.h" +#include "performance/PerformanceManager.hpp" +#include "sampler/NoteParameters.hpp" +#include "sampler/Pad.hpp" +#include "sampler/PgmSlider.hpp" +#include "sampler/Program.hpp" +#include "sampler/Sampler.hpp" + +#include +#include +#include + +#include + +namespace { + +constexpr uint8_t kUnassignedSoundIndex = 0xFF; +constexpr uint8_t kRawMuteAssignOff = 0; +constexpr int kNormalizedMuteAssignOff = 34; +constexpr int kProgramNameLength = 16; +constexpr int kSoundNameLength = 16; +constexpr int kProgramPadCount = 64; +constexpr int kDefaultSliderUnknown0 = 35; +constexpr int kDefaultSliderUnknown1 = 64; +constexpr int kDefaultSliderUnknown2 = 0; +constexpr int kDefaultSliderUnknown3 = 25; +constexpr int kDefaultSliderUnknown4 = 0; +constexpr int kDefaultNoteParametersTerminator = 6; +constexpr int kDefaultMixerUnknown0 = 0; +constexpr int kDefaultMixerUnknown1 = 64; +constexpr int kDefaultMixerUnknown2 = 0; +constexpr int kMpc3000SoundTableSize = 128; +constexpr int kMpc3000EffectsGeneratorFxPath = 1; +constexpr char kPadTailFxBoardSettingsHex[] = + "02004800D0070000630114081DFC323302323C08050A1414320000020F19000541141E0105" + "0000056300F4FF0C0000000000000002004F014F0100424F0100424F01004232006328003C" + "0000D0070000630114081DFC323302323C08050A1414320000020F19000541141E01050000" + "056300F4FF0C0000000000000002004F014F0100424F0100424F01004232006328003C0000" + "04000C000000320023003E335A3214000000320023003E335A3214000000320023003E335A" + "3214000000320023003E335A321400"; + +int normalizeMuteAssign(const uint8_t rawValue) +{ + return rawValue == kRawMuteAssignOff ? kNormalizedMuteAssignOff : rawValue; +} + +int normalizeOptionalNote(const uint8_t rawValue) +{ + return rawValue == 0 ? kNormalizedMuteAssignOff : rawValue; +} + +int decodeSignedByte(const uint8_t rawValue) +{ + return static_cast(rawValue); +} + +std::string stripNullTerminatedField(const std::string& raw) +{ + const auto terminator = raw.find('\0'); + return terminator == std::string::npos ? raw : raw.substr(0, terminator); +} + +std::string toNullTerminatedAsciiField(const std::string& value, const int visibleLength) +{ + auto field = mpc::StrUtil::padRight(value.substr(0, visibleLength), " ", visibleLength); + field.push_back('\0'); + return field; +} + +void setSlider(const mpc2000xl_pgm_t& parsed, + const std::shared_ptr& program) +{ + const auto* slider = parsed.slider(); + const auto note = slider->note() == 0 + ? mpc::NoDrumNoteAssigned + : mpc::DrumNoteNumber(slider->note()); + const auto pgmSlider = program->getSlider(); + pgmSlider->setAssignNote(note); + pgmSlider->setAttackHighRange(slider->attack_high()); + pgmSlider->setAttackLowRange(slider->attack_low()); + pgmSlider->setDecayHighRange(slider->decay_high()); + pgmSlider->setDecayLowRange(slider->decay_low()); + pgmSlider->setFilterHighRange(slider->filter_high()); + pgmSlider->setFilterLowRange(slider->filter_low()); + pgmSlider->setTuneHighRange(slider->tune_high()); + pgmSlider->setTuneLowRange(slider->tune_low()); +} + +void setSlider(const mpc3000_pgm_v3_t& parsed, + const std::shared_ptr& program) +{ + const auto* slider = parsed.note_variation_screen(); + const auto note = slider->note_number_assignment() == 0 + ? mpc::NoDrumNoteAssigned + : mpc::DrumNoteNumber(normalizeOptionalNote(slider->note_number_assignment())); + const auto pgmSlider = program->getSlider(); + pgmSlider->setAssignNote(note); + pgmSlider->setAttackHighRange(slider->attack_hi_range()); + pgmSlider->setAttackLowRange(slider->attack_low_range()); + pgmSlider->setDecayHighRange(slider->decay_hi_range()); + pgmSlider->setDecayLowRange(slider->decay_low_range()); + pgmSlider->setFilterHighRange(slider->filter_hi_range()); + pgmSlider->setFilterLowRange(decodeSignedByte(slider->filter_low_range())); + pgmSlider->setTuneHighRange(decodeSignedByte(slider->tuning_hi_range())); + pgmSlider->setTuneLowRange(decodeSignedByte(slider->tuning_low_range())); +} + +void setNoteParameters(mpc::Mpc& mpc, + const mpc2000xl_pgm_t& parsed, + const std::shared_ptr& program) +{ + std::array + allPerfNoteParams; + + for (int programPadIndex = 0; + programPadIndex < mpc::Mpc2000XlSpecs::PROGRAM_PAD_COUNT; + ++programPadIndex) + { + const auto padNote = parsed.pad_to_note_mapping()->at(programPadIndex); + const auto note = padNote == -1 ? mpc::NoDrumNoteAssigned : padNote; + program->getPad(programPadIndex)->setNote(mpc::DrumNoteNumber(note)); + + const auto* parsedNote = parsed.note_parameters()->at(programPadIndex).get(); + auto& perfNoteParams = allPerfNoteParams[programPadIndex]; + + perfNoteParams.attack = parsedNote->attack(); + perfNoteParams.decay = parsedNote->decay(); + perfNoteParams.decayMode = static_cast(parsedNote->decay_mode()); + perfNoteParams.filterAttack = parsedNote->velocity_envelope_to_filter_attack(); + perfNoteParams.filterDecay = parsedNote->velocity_envelope_to_filter_decay(); + perfNoteParams.filterEnvelopeAmount = parsedNote->velocity_envelope_to_filter_amount(); + perfNoteParams.filterFrequency = parsedNote->cutoff(); + perfNoteParams.filterResonance = parsedNote->resonance(); + perfNoteParams.muteAssignA = mpc::DrumNoteNumber(normalizeMuteAssign(parsedNote->mute_assign_1())); + perfNoteParams.muteAssignB = mpc::DrumNoteNumber(normalizeMuteAssign(parsedNote->mute_assign_2())); + perfNoteParams.optionalNoteA = mpc::DrumNoteNumber(parsedNote->also_play_use_note_1()); + perfNoteParams.optionalNoteB = mpc::DrumNoteNumber(parsedNote->also_play_use_note_2()); + perfNoteParams.soundIndex = parsedNote->sound_index() == kUnassignedSoundIndex + ? -1 + : parsedNote->sound_index(); + perfNoteParams.sliderParameterNumber = static_cast(parsedNote->slider_parameter()); + perfNoteParams.soundGenerationMode = static_cast(parsedNote->sound_generation_mode()); + perfNoteParams.tune = parsedNote->tune(); + perfNoteParams.velocityRangeLower = parsedNote->velocity_range_lower(); + perfNoteParams.velocityRangeUpper = parsedNote->velocity_range_upper(); + perfNoteParams.velocityToAttack = parsedNote->velocity_to_attack(); + perfNoteParams.velocityToFilterFrequency = parsedNote->velocity_to_cutoff(); + perfNoteParams.velocityToLevel = parsedNote->velocity_to_level(); + perfNoteParams.velocityToPitch = parsedNote->velocity_to_pitch(); + perfNoteParams.velocityToStart = parsedNote->velocity_to_start(); + perfNoteParams.voiceOverlapMode = static_cast(parsedNote->voice_overlap_mode()); + } + + mpc::performance::UpdateAllNoteParametersBulk msg{program->getProgramIndex(), + allPerfNoteParams}; + mpc.getPerformanceManager().lock()->enqueue(msg); +} + +std::array buildMpc3000SoundIndexConversionTable( + const mpc3000_pgm_v3_t& parsed, + std::vector& soundNames) +{ + std::array conversionTable{}; + conversionTable.fill(-1); + + for (const auto& assignment : *parsed.sound_assignments()) + { + const auto rawSoundNumber = assignment->sound_number(); + if (rawSoundNumber == kUnassignedSoundIndex || + rawSoundNumber >= kMpc3000SoundTableSize) + { + continue; + } + + if (conversionTable[rawSoundNumber] != -1) + { + continue; + } + + const auto soundName = mpc::StrUtil::trim( + stripNullTerminatedField(parsed.sound_names()->at(rawSoundNumber)->name()) + ); + + if (soundName.empty()) + { + continue; + } + + conversionTable[rawSoundNumber] = static_cast(soundNames.size()); + soundNames.push_back(soundName); + } + + return conversionTable; +} + +void setNoteParameters(mpc::Mpc& mpc, + const mpc3000_pgm_v3_t& parsed, + const std::shared_ptr& program, + const std::array& soundIndexConversionTable) +{ + std::array + allPerfNoteParams; + + for (int programPadIndex = 0; + programPadIndex < mpc::Mpc2000XlSpecs::PROGRAM_PAD_COUNT; + ++programPadIndex) + { + const auto padNote = parsed.pad_note_number_assignments()->at(programPadIndex)->note_number(); + program->getPad(programPadIndex)->setNote(mpc::DrumNoteNumber(padNote)); + + const auto* parsedNote = parsed.sound_assignments()->at(programPadIndex).get(); + auto& perfNoteParams = allPerfNoteParams[programPadIndex]; + + perfNoteParams.attack = parsedNote->attack(); + perfNoteParams.decay = parsedNote->decay(); + perfNoteParams.decayMode = static_cast(parsedNote->decay_mode()); + perfNoteParams.filterAttack = parsedNote->filter_envel_attack(); + perfNoteParams.filterDecay = parsedNote->filter_envel_decay(); + perfNoteParams.filterEnvelopeAmount = parsedNote->filter_envel_amount(); + perfNoteParams.filterFrequency = parsedNote->filter_frequency(); + perfNoteParams.filterResonance = parsedNote->filter_resonance(); + perfNoteParams.muteAssignA = + mpc::DrumNoteNumber(normalizeOptionalNote(parsedNote->cutoff_note_1())); + perfNoteParams.muteAssignB = + mpc::DrumNoteNumber(normalizeOptionalNote(parsedNote->cutoff_note_2())); + perfNoteParams.optionalNoteA = + mpc::DrumNoteNumber(normalizeOptionalNote(parsedNote->use_also_plays1())); + perfNoteParams.optionalNoteB = + mpc::DrumNoteNumber(normalizeOptionalNote(parsedNote->use_also_plays2())); + + const auto rawSoundNumber = parsedNote->sound_number(); + if (rawSoundNumber == kUnassignedSoundIndex || + rawSoundNumber >= kMpc3000SoundTableSize) + { + perfNoteParams.soundIndex = -1; + } + else + { + perfNoteParams.soundIndex = soundIndexConversionTable[rawSoundNumber]; + } + + perfNoteParams.sliderParameterNumber = static_cast(parsedNote->param()); + perfNoteParams.soundGenerationMode = static_cast(parsedNote->sound_generator_mode()); + perfNoteParams.tune = parsedNote->tune(); + perfNoteParams.velocityRangeLower = parsedNote->if_over1(); + perfNoteParams.velocityRangeUpper = parsedNote->if_over2(); + perfNoteParams.velocityToAttack = parsedNote->veloc_mod_of_attack(); + perfNoteParams.velocityToFilterFrequency = parsedNote->veloc_mod_of_filter_freq(); + perfNoteParams.velocityToLevel = parsedNote->veloc_mod_of_volume(); + perfNoteParams.velocityToPitch = 0; + perfNoteParams.velocityToStart = parsedNote->veloc_mod_of_soft_start(); + perfNoteParams.voiceOverlapMode = + static_cast(static_cast(parsedNote->poly())); + } + + mpc::performance::UpdateAllNoteParametersBulk msg{program->getProgramIndex(), + allPerfNoteParams}; + mpc.getPerformanceManager().lock()->enqueue(msg); +} + +void setMixer(const mpc2000xl_pgm_t& parsed, + const std::shared_ptr& program) +{ + for (int i = 0; i < 64; ++i) + { + const auto noteParameters = program->getNoteParameters(i + 35); + const auto stereoMixer = noteParameters->getStereoMixer(); + const auto indivFxMixer = noteParameters->getIndivFxMixer(); + const auto* parsedMixer = parsed.pad_mixers()->at(i).get(); + + stereoMixer->setLevel(mpc::DrumMixerLevel(parsedMixer->volume())); + stereoMixer->setPanning(mpc::DrumMixerPanning(parsedMixer->pan())); + indivFxMixer->setVolumeIndividualOut( + mpc::DrumMixerLevel(parsedMixer->volume_individual())); + indivFxMixer->setOutput( + mpc::DrumMixerIndividualOutput(parsedMixer->output())); + indivFxMixer->setFxPath( + mpc::DrumMixerIndividualFxPath(parsedMixer->fx_output())); + indivFxMixer->setFxSendLevel( + mpc::DrumMixerLevel(parsedMixer->effects_send_level())); + } +} + +void setMixer(const mpc3000_pgm_v3_t& parsed, + const std::shared_ptr& program) +{ + for (int i = 0; i < 64; ++i) + { + const auto noteParameters = program->getNoteParameters(i + 35); + const auto stereoMixer = noteParameters->getStereoMixer(); + const auto indivFxMixer = noteParameters->getIndivFxMixer(); + const auto* parsedMixer = parsed.mixer_screens()->at(i).get(); + + stereoMixer->setLevel(mpc::DrumMixerLevel(parsedMixer->stereo_mix_volume())); + stereoMixer->setPanning(mpc::DrumMixerPanning(parsedMixer->stereo_mix_pan())); + indivFxMixer->setFollowStereo(parsedMixer->follow_stereo()); + indivFxMixer->setVolumeIndividualOut( + mpc::DrumMixerLevel(parsedMixer->echo_volume())); + + if (parsedMixer->out_assign() == mpc3000_pgm_v3_t::INDIVIDUAL_OUT_INTERNAL_EFFECTS_GENERATOR) + { + // MPC3000 stores a dedicated internal-effects routing value here, + // while the consumer model is MPC2000XL-shaped. For import we + // preserve the semantic intent by routing through the FX path. + indivFxMixer->setOutput(mpc::DrumMixerIndividualOutput(0)); + indivFxMixer->setFxPath(mpc::DrumMixerIndividualFxPath(kMpc3000EffectsGeneratorFxPath)); + indivFxMixer->setFxSendLevel(mpc::DrumMixerLevel(parsedMixer->echo_volume())); + } + else + { + indivFxMixer->setOutput( + mpc::DrumMixerIndividualOutput(static_cast(parsedMixer->out_assign()))); + indivFxMixer->setFxPath(mpc::DrumMixerIndividualFxPath(0)); + indivFxMixer->setFxSendLevel(mpc::DrumMixerLevel(0)); + } + } +} + +std::vector collectSavedSoundNames( + mpc::sampler::Program& program, + const std::shared_ptr& sampler) +{ + std::vector soundIndices; + for (auto* noteParameters : program.getNotesParameters()) + { + if (noteParameters->getSoundIndex() != -1) + { + soundIndices.push_back(noteParameters->getSoundIndex()); + } + } + + std::set uniqueSoundNames; + std::vector finalNames; + for (const auto soundIndex : soundIndices) + { + const auto soundName = sampler->getSoundName(soundIndex); + if (uniqueSoundNames.emplace(soundName).second) + { + finalNames.push_back(soundName); + } + } + return finalNames; +} + +std::vector buildSoundIndexConversionTable( + const std::vector& finalSoundNames, + const std::shared_ptr& sampler) +{ + std::vector conversionTable; + conversionTable.reserve(sampler->getSoundCount()); + + for (int soundIndex = 0; soundIndex < sampler->getSoundCount(); ++soundIndex) + { + const auto soundName = sampler->getSoundName(soundIndex); + int mappedIndex = -1; + for (int i = 0; i < static_cast(finalSoundNames.size()); ++i) + { + if (finalSoundNames[i] == soundName) + { + mappedIndex = i; + break; + } + } + conversionTable.push_back(mappedIndex); + } + + return conversionTable; +} + +void populateSavedSlider(mpc2000xl_pgm_t& parsed) +{ + parsed.set__unnamed7(std::string{ + static_cast(kDefaultSliderUnknown0), + static_cast(kDefaultSliderUnknown1), + static_cast(kDefaultSliderUnknown2), + static_cast(kDefaultSliderUnknown3), + static_cast(kDefaultSliderUnknown4) + }); +} + +void populateSavedNoteParameters( + mpc2000xl_pgm_t& parsed, + mpc::sampler::Program& program, + const std::vector& soundIndexConversionTable) +{ + auto notes = std::make_unique>>(); + notes->reserve(kProgramPadCount); + + for (int i = 0; i < kProgramPadCount; ++i) + { + auto note = std::make_unique(nullptr, &parsed, &parsed); + const auto* src = program.getNoteParameters(i + 35); + + if (src->getSoundIndex() == -1) + { + note->set_sound_index(kUnassignedSoundIndex); + } + else + { + note->set_sound_index(static_cast(soundIndexConversionTable.at(src->getSoundIndex()))); + } + + note->set_sound_generation_mode(static_cast( + mpc::sampler::toRaw(src->getSoundGenerationMode()))); + note->set_velocity_range_lower(src->getVelocityRangeLower()); + note->set_also_play_use_note_1(src->getOptionalNoteA()); + note->set_velocity_range_upper(src->getVelocityRangeUpper()); + note->set_also_play_use_note_2(src->getOptionalNoteB()); + note->set_voice_overlap_mode(static_cast(static_cast(src->getVoiceOverlapMode()))); + note->set_mute_assign_1(src->getMuteAssignA() == 34 ? 0 : src->getMuteAssignA()); + note->set_mute_assign_2(src->getMuteAssignB() == 34 ? 0 : src->getMuteAssignB()); + note->set_tune(static_cast(src->getTune())); + note->set_attack(src->getAttack()); + note->set_decay(src->getDecay()); + note->set_decay_mode(static_cast(src->getDecayMode())); + note->set_cutoff(src->getFilterFrequency()); + note->set_resonance(src->getFilterResonance()); + note->set_velocity_envelope_to_filter_attack(src->getFilterAttack()); + note->set_velocity_envelope_to_filter_decay(src->getFilterDecay()); + note->set_velocity_envelope_to_filter_amount(src->getFilterEnvelopeAmount()); + note->set_velocity_to_level(src->getVeloToLevel()); + note->set_velocity_to_attack(src->getVelocityToAttack()); + note->set_velocity_to_start(src->getVelocityToStart()); + note->set_velocity_to_cutoff(src->getVelocityToFilterFrequency()); + note->set_slider_parameter(static_cast(src->getSliderParameterNumber())); + note->set_velocity_to_pitch(src->getVelocityToPitch()); + + notes->push_back(std::move(note)); + } + + parsed.set_note_parameters(std::move(notes)); + parsed.set__unnamed9(std::string{static_cast(kDefaultNoteParametersTerminator)}); +} + +void populateSavedMixers(mpc2000xl_pgm_t& parsed, mpc::sampler::Program& program) +{ + auto mixers = std::make_unique>>(); + mixers->reserve(kProgramPadCount); + + for (int i = 0; i < kProgramPadCount; ++i) + { + auto mixer = std::make_unique(nullptr, &parsed, &parsed); + const auto* src = program.getNoteParameters(i + 35); + const auto stereo = src->getStereoMixer(); + const auto indiv = src->getIndivFxMixer(); + + mixer->set_fx_output(static_cast(static_cast(indiv->getFxPath()))); + mixer->set_volume(stereo->getLevel()); + mixer->set_pan(stereo->getPanning()); + mixer->set_volume_individual(indiv->getVolumeIndividualOut()); + mixer->set_output(indiv->getOutput()); + mixer->set_effects_send_level(indiv->getFxSendLevel()); + mixers->push_back(std::move(mixer)); + } + + parsed.set_pad_mixers(std::move(mixers)); + parsed.set__unnamed11(std::string{ + static_cast(kDefaultMixerUnknown0), + static_cast(kDefaultMixerUnknown1), + static_cast(kDefaultMixerUnknown2) + }); +} + +std::string fxBoardSettingsBytes() +{ + const auto bytes = mpc::file::ByteUtil::hexToBytes(kPadTailFxBoardSettingsHex); + return std::string(bytes.begin(), bytes.end()); +} + +} // namespace + +using namespace mpc::file::kaitai; + +program_or_error PgmIo::loadProgram(mpc::Mpc &mpc, + const std::shared_ptr &file, + const std::shared_ptr &program, + std::vector &soundNames) +{ + const auto fileBytes = file->getBytes(); + if (fileBytes.size() < 2) + { + return tl::make_unexpected(mpc::disk::mpc_io_error_msg{"PGM file is too short"}); + } + + if (static_cast(fileBytes[0]) == 0x07 && + static_cast(fileBytes[1]) == 0x04) + { + const auto canonicalBytes = parseRewrite(fileBytes); + std::stringstream parseStream( + std::string(canonicalBytes.begin(), canonicalBytes.end()), + std::ios::in | std::ios::out | std::ios::binary + ); + ::kaitai::kstream parseIo(&parseStream); + mpc2000xl_pgm_t parsed(&parseIo); + parsed._read(); + + soundNames.clear(); + soundNames.reserve(parsed.sound_count()); + for (int i = 0; i < parsed.sound_count(); ++i) + { + soundNames.push_back(stripNullTerminatedField(parsed.sound_names()->at(i))); + } + + const std::string programNameInData = mpc::StrUtil::trim(stripNullTerminatedField(parsed.name())); + if (mpc::StrUtil::eqIgnoreCase(programNameInData, file->getNameWithoutExtension())) + { + program->setName(programNameInData); + } + else + { + program->setName(file->getNameWithoutExtension()); + } + + setNoteParameters(mpc, parsed, program); + setMixer(parsed, program); + setSlider(parsed, program); + program->setMidiProgramChange(parsed.program_change() + 1); + return program; + } + + if (static_cast(fileBytes[0]) == 0x07 && + static_cast(fileBytes[1]) == 0x00) + { + const auto canonicalBytes = parseRewrite(fileBytes); + std::stringstream parseStream( + std::string(canonicalBytes.begin(), canonicalBytes.end()), + std::ios::in | std::ios::out | std::ios::binary + ); + ::kaitai::kstream parseIo(&parseStream); + mpc3000_pgm_v3_t parsed(&parseIo); + parsed._read(); + + soundNames.clear(); + const auto soundIndexConversionTable = + buildMpc3000SoundIndexConversionTable(parsed, soundNames); + + const std::string programNameInData = mpc::StrUtil::trim(parsed.program_name()); + if (mpc::StrUtil::eqIgnoreCase(programNameInData, file->getNameWithoutExtension())) + { + program->setName(programNameInData); + } + else + { + program->setName(file->getNameWithoutExtension()); + } + + setNoteParameters(mpc, parsed, program, soundIndexConversionTable); + setMixer(parsed, program); + setSlider(parsed, program); + return program; + } + + return tl::make_unexpected(mpc::disk::mpc_io_error_msg{"PGM first 2 bytes are incorrect"}); +} + +std::vector PgmIo::saveProgram(mpc::sampler::Program &program, + const std::weak_ptr &sampler) +{ + const auto lockedSampler = sampler.lock(); + if (!lockedSampler) + { + return {}; + } + + std::stringstream writeStream(std::ios::in | std::ios::out | std::ios::binary); + ::kaitai::kstream writeIo(&writeStream); + mpc2000xl_pgm_t parsed(&writeIo); + + const auto finalSoundNames = collectSavedSoundNames(program, lockedSampler); + const auto soundIndexConversionTable = + buildSoundIndexConversionTable(finalSoundNames, lockedSampler); + + auto soundNames = std::make_unique>(); + soundNames->reserve(finalSoundNames.size()); + for (const auto& soundName : finalSoundNames) + { + soundNames->push_back(toNullTerminatedAsciiField(soundName, kSoundNameLength)); + } + + auto slider = std::make_unique(nullptr, &parsed, &parsed); + const auto* srcSlider = program.getSlider(); + slider->set_note(srcSlider->getNote() == 34 ? 0 : srcSlider->getNote()); + slider->set_tune_low(srcSlider->getTuneLowRange()); + slider->set_tune_high(srcSlider->getTuneHighRange()); + slider->set_decay_low(srcSlider->getDecayLowRange()); + slider->set_decay_high(srcSlider->getDecayHighRange()); + slider->set_attack_low(srcSlider->getAttackLowRange()); + slider->set_attack_high(srcSlider->getAttackHighRange()); + slider->set_filter_low(srcSlider->getFilterLowRange()); + slider->set_filter_high(srcSlider->getFilterHighRange()); + + parsed.set_magic(std::string("\x07\x04", 2)); + parsed.set_sound_count(static_cast(finalSoundNames.size())); + parsed.set_sound_names(std::move(soundNames)); + parsed.set__unnamed3(std::string{30, 0}); + parsed.set_name(toNullTerminatedAsciiField(program.getName(), kProgramNameLength)); + parsed.set_slider(std::move(slider)); + parsed.set_program_change(static_cast(program.getMidiProgramChange() - 1)); + populateSavedSlider(parsed); + populateSavedNoteParameters(parsed, program, soundIndexConversionTable); + populateSavedMixers(parsed, program); + + auto padMapping = std::make_unique>(); + padMapping->reserve(kProgramPadCount); + for (int i = 0; i < kProgramPadCount; ++i) + { + const auto note = program.getPad(i)->getNote(); + padMapping->push_back(static_cast(note == 34 ? 0 : note.get())); + } + parsed.set_pad_to_note_mapping(std::move(padMapping)); + parsed.set__unnamed13(fxBoardSettingsBytes()); + + parsed._check(); + parsed._write(); + + const auto bytes = writeStream.str(); + return std::vector(bytes.begin(), bytes.end()); +} diff --git a/src/main/file/kaitai/PgmIo.hpp b/src/main/file/kaitai/PgmIo.hpp new file mode 100644 index 000000000..c74c1aca1 --- /dev/null +++ b/src/main/file/kaitai/PgmIo.hpp @@ -0,0 +1,40 @@ +#pragma once + +#include "mpc_types.hpp" + +#include +#include +#include + +namespace mpc +{ + class Mpc; +} + +namespace mpc::disk +{ + class MpcFile; +} + +namespace mpc::sampler +{ + class Program; + class Sampler; +} + +namespace mpc::file::kaitai +{ + class PgmIo + { + public: + static program_or_error loadProgram( + mpc::Mpc &, + const std::shared_ptr &, + const std::shared_ptr &, + std::vector &soundNames); + + static std::vector saveProgram( + sampler::Program &, + const std::weak_ptr &); + }; +} diff --git a/src/main/file/kaitai/SndIo.cpp b/src/main/file/kaitai/SndIo.cpp new file mode 100644 index 000000000..d9c3aae51 --- /dev/null +++ b/src/main/file/kaitai/SndIo.cpp @@ -0,0 +1,207 @@ +#include "file/kaitai/SndIo.hpp" + +#include "SampleOps.hpp" +#include "StrUtil.hpp" +#include "disk/MpcFile.hpp" +#include "file/kaitai/KaitaiIoUtil.hpp" +#include "file/kaitai/Mpc60SampleDecoder.hpp" +#include "file/kaitai/generated/mpc2000snd.h" +#include "file/kaitai/generated/mpc3000_snd_v2.h" +#include "file/kaitai/generated/mpc60_snd_v1.h" +#include "sampler/Sound.hpp" + +using namespace mpc::file::kaitai; +using namespace mpc::file::kaitai::generated; +using namespace mpc::sampleops; + +namespace +{ + std::string trimRightSpaces(std::string value) + { + while (!value.empty() && value.back() == ' ') + { + value.pop_back(); + } + return value; + } + + std::string parsedSoundName(const std::string &parsedName, + const std::string &nameWithoutExtension) + { + const auto trimmed = trimRightSpaces(parsedName); + return mpc::StrUtil::eqIgnoreCase(trimmed, nameWithoutExtension) + ? trimmed + : nameWithoutExtension; + } +} + +sound_or_error SndIo::loadBytes(const std::vector &bytes, + const std::shared_ptr &sound, + const std::string &nameWithoutExtension) +{ + if (bytes.size() < 2) + { + return tl::make_unexpected("SND file is too short"); + } + + const auto firstByte = static_cast(bytes[0]); + const auto secondByte = static_cast(bytes[1]); + + std::stringstream parseStream( + std::string(bytes.begin(), bytes.end()), + std::ios::in | std::ios::out | std::ios::binary + ); + ::kaitai::kstream parseIo(&parseStream); + + if (firstByte == 0x01 && secondByte == 0x04) + { + mpc2000snd_t parsed(&parseIo); + parsed._read(); + + auto parsedName = parsed.name(); + const auto nulPos = parsedName.find('\0'); + if (nulPos != std::string::npos) + { + parsedName.erase(nulPos); + } + + sound->setName(parsedSoundName(parsedName, nameWithoutExtension)); + sound->setMono(!parsed.stereo()); + sound->setSampleRate(parsed.sample_rate()); + sound->setLevel(parsed.level()); + sound->setTune(parsed.tune()); + sound->setBeatCount(parsed.beat_count()); + sound->setLoopEnabled(parsed.loop_enabled()); + + auto sampleData = sound->getMutableSampleData(); + sampleData->clear(); + if (parsed.sample_data() != nullptr) + { + for (const auto frame : *parsed.sample_data()) + { + sampleData->push_back(short_to_float(frame)); + } + } + + sound->setStart(parsed.start()); + sound->setEnd(parsed.end()); + sound->setLoopTo(parsed.end() - parsed.loop_frame_count()); + + return sound; + } + + if (firstByte == 0x01 && secondByte == 0x01) + { + mpc60_snd_v1_t parsed(&parseIo); + + sound->setName(parsedSoundName(parsed.name(), nameWithoutExtension)); + sound->setMono(true); + sound->setSampleRate(parsed.sample_rate()); + sound->setLevel(parsed.volume_percent()); + sound->setTune(parsed.tuning()); + sound->setBeatCount(4); + sound->setLoopEnabled(false); + + auto sampleData = sound->getMutableSampleData(); + sampleData->clear(); + sampleData->reserve(parsed.sample_count()); + + Mpc60SampleDecoder decoder; + size_t decodedCount = 0; + for (const auto &pair : *parsed.sample_data_pairs()) + { + if (decodedCount < parsed.sample_count()) + { + sampleData->push_back( + decoder.decodeImportedFloat(pair->sample0_code(), false)); + ++decodedCount; + } + + if (decodedCount < parsed.sample_count()) + { + sampleData->push_back( + decoder.decodeImportedFloat(pair->sample1_code(), true)); + ++decodedCount; + } + } + + sound->setStart(0); + sound->setEnd(sound->getLastFrameIndex()); + sound->setLoopTo(0); + + return sound; + } + + if (firstByte == 0x01 && secondByte == 0x02) + { + mpc3000_snd_v2_t parsed(&parseIo); + parsed._read(); + + sound->setName(parsedSoundName(parsed.name(), nameWithoutExtension)); + sound->setMono(true); + sound->setSampleRate(parsed.sample_rate()); + sound->setLevel(parsed.level()); + sound->setTune(0); + sound->setBeatCount(4); + sound->setLoopEnabled(false); + + auto sampleData = sound->getMutableSampleData(); + sampleData->clear(); + if (parsed.sample_data() != nullptr) + { + sampleData->reserve(parsed.sample_data()->size()); + for (const auto sample : *parsed.sample_data()) + { + sampleData->push_back(short_to_float(sample)); + } + } + + sound->setStart(parsed.start()); + sound->setEnd(parsed.end()); + sound->setLoopTo(0); + + return sound; + } + + return tl::make_unexpected("Unsupported SND file format"); +} + +sound_or_error SndIo::loadSound(const std::shared_ptr &file, + const std::shared_ptr &sound, + const std::string &nameWithoutExtension) +{ + return loadBytes(file->getBytes(), sound, nameWithoutExtension); +} + +std::vector SndIo::saveSound(mpc::sampler::Sound &sound) +{ + mpc2000snd_t parsed(nullptr); + parsed.set_magic(std::string("\x01\x04", 2)); + parsed.set_name(StrUtil::padRight(sound.getName().substr(0, 16), " ", 16) + '\0'); + parsed.set_level(sound.getSndLevel()); + parsed.set_tune(static_cast(sound.getTune())); + parsed.set_stereo(!sound.isMono()); + parsed.set_start(sound.getStart()); + parsed.set_end(sound.getEnd()); + parsed.set_frame_count(sound.getFrameCount()); + parsed.set_loop_frame_count(sound.getEnd() - sound.getLoopTo()); + parsed.set_loop_enabled(sound.isLoopEnabled()); + parsed.set_beat_count(sound.getBeatCount()); + parsed.set_sample_rate(sound.getSampleRate()); + + auto frames = std::make_unique>(); + for (const auto sample : *sound.getSampleData()) + { + frames->push_back(mean_normalized_float_to_short(sample)); + } + parsed.set_sample_data(std::move(frames)); + + std::stringstream writeStream(std::ios::in | std::ios::out | std::ios::binary); + ::kaitai::kstream writeIo(&writeStream); + parsed._set_io(&writeIo); + parsed._check(); + parsed._write(); + + const auto written = writeStream.str(); + return std::vector(written.begin(), written.end()); +} diff --git a/src/main/file/kaitai/SndIo.hpp b/src/main/file/kaitai/SndIo.hpp new file mode 100644 index 000000000..84aeee66c --- /dev/null +++ b/src/main/file/kaitai/SndIo.hpp @@ -0,0 +1,36 @@ +#pragma once + +#include "mpc_types.hpp" + +#include +#include +#include + +namespace mpc::disk +{ + class MpcFile; +} + +namespace mpc::sampler +{ + class Sound; +} + +namespace mpc::file::kaitai +{ + class SndIo + { + public: + static sound_or_error loadBytes( + const std::vector &, + const std::shared_ptr &, + const std::string &nameWithoutExtension); + + static sound_or_error loadSound( + const std::shared_ptr &, + const std::shared_ptr &, + const std::string &nameWithoutExtension); + + static std::vector saveSound(sampler::Sound &); + }; +} diff --git a/src/main/file/kaitai/generated/mpc2000snd.cpp b/src/main/file/kaitai/generated/mpc2000snd.cpp new file mode 100644 index 000000000..976356bde --- /dev/null +++ b/src/main/file/kaitai/generated/mpc2000snd.cpp @@ -0,0 +1,87 @@ +// This is a generated file! Please edit source .ksy file and use kaitai-struct-compiler to rebuild + +#include "mpc2000snd.h" +#include "kaitai/exceptions.h" +#include +#include + +mpc2000snd_t::mpc2000snd_t(kaitai::kstream* p__io, kaitai::kstruct* p__parent, mpc2000snd_t* p__root) : kaitai::kstruct(p__io) { + m__parent = p__parent; + m__root = p__root ? p__root : this; + m__dirty = false; +} + +void mpc2000snd_t::_read() { + m_magic = m__io->read_bytes(2); + if (!(m_magic == std::string("\x01\x04", 2))) { + throw kaitai::validation_not_equal_error(std::string("\x01\x04", 2), m_magic, m__io, std::string("/seq/0")); + } + m_name = kaitai::kstream::bytes_to_str(m__io->read_bytes(17), "ASCII"); + m_level = m__io->read_u1(); + m_tune = m__io->read_s1(); + m_stereo = m__io->read_bits_int_le(1); + m_start = m__io->read_u4le(); + m_end = m__io->read_u4le(); + m_frame_count = m__io->read_u4le(); + m_loop_frame_count = m__io->read_u4le(); + m_loop_enabled = m__io->read_bits_int_le(1); + m_beat_count = m__io->read_u1(); + m_sample_rate = m__io->read_u2le(); + m_sample_data = std::unique_ptr>(new std::vector()); + const int l_sample_data = ((stereo()) ? (frame_count() * 2) : (frame_count())); + for (int i = 0; i < l_sample_data; i++) { + m_sample_data->push_back(std::move(m__io->read_s2le())); + } + m__dirty = false; +} + +void mpc2000snd_t::_fetch_instances() { + for (std::size_t i = 0; i < m_sample_data->size(); ++i) { + } +} + +void mpc2000snd_t::_write() { + m__io->write_bytes(m_magic); + m__io->write_bytes(m_name); + m__io->write_u1(m_level); + m__io->write_s1(m_tune); + m__io->write_bits_int_le(1, ((m_stereo) ? 1 : 0)); + m__io->write_u4le(m_start); + m__io->write_u4le(m_end); + m__io->write_u4le(m_frame_count); + m__io->write_u4le(m_loop_frame_count); + m__io->write_bits_int_le(1, ((m_loop_enabled) ? 1 : 0)); + m__io->write_u1(m_beat_count); + m__io->write_u2le(m_sample_rate); + if (m_sample_data == nullptr) { + throw std::runtime_error("/seq/12: repeated field is not set"); + } + for (std::vector::const_iterator it = m_sample_data->begin(); it != m_sample_data->end(); ++it) { + m__io->write_s2le((*it)); + } + _fetch_instances(); + m__dirty = false; +} + +void mpc2000snd_t::_check() { + if (m_magic.size() != static_cast(2)) { + throw std::runtime_error("/seq/0: size mismatch"); + } + if (!(m_magic == std::string("\x01\x04", 2))) { + throw kaitai::validation_not_equal_error(std::string("\x01\x04", 2), m_magic, m__io, std::string("/seq/0")); + } + if (m_name.size() != static_cast(17)) { + throw std::runtime_error("/seq/1: size mismatch"); + } + if (m_sample_data == nullptr) { + throw std::runtime_error("/seq/12: repeated field is not set"); + } + if (m_sample_data->size() != static_cast(((stereo()) ? (frame_count() * 2) : (frame_count())))) { + throw std::runtime_error("/seq/12: repeat-expr size mismatch"); + } + for (std::vector::const_iterator it = m_sample_data->begin(); it != m_sample_data->end(); ++it) { + } + m__dirty = false; +} + +mpc2000snd_t::~mpc2000snd_t() {} diff --git a/src/main/file/kaitai/generated/mpc2000snd.h b/src/main/file/kaitai/generated/mpc2000snd.h new file mode 100644 index 000000000..04f1afa69 --- /dev/null +++ b/src/main/file/kaitai/generated/mpc2000snd.h @@ -0,0 +1,78 @@ +#pragma once + +// This is a generated file! Please edit source .ksy file and use kaitai-struct-compiler to rebuild + +class mpc2000snd_t; + +#include "kaitai/kaitaistruct.h" +#include +#include +#include + +#if KAITAI_STRUCT_VERSION < 11000L +#error "Incompatible Kaitai Struct C++/STL API: version 0.11 or later is required" +#endif + +class mpc2000snd_t : public kaitai::kstruct { + +public: + +private: + bool m__dirty; + +public: + + mpc2000snd_t(kaitai::kstream* p__io, kaitai::kstruct* p__parent = nullptr, mpc2000snd_t* p__root = nullptr); + void _read(); + void _fetch_instances(); + void _write(); + void _check(); + ~mpc2000snd_t(); + std::string magic() const { return m_magic; } + void set_magic(std::string _v) { m__dirty = true; m_magic = std::move(_v); } + std::string name() const { return m_name; } + void set_name(std::string _v) { m__dirty = true; m_name = std::move(_v); } + uint8_t level() const { return m_level; } + void set_level(uint8_t _v) { m__dirty = true; m_level = std::move(_v); } + int8_t tune() const { return m_tune; } + void set_tune(int8_t _v) { m__dirty = true; m_tune = std::move(_v); } + bool stereo() const { return m_stereo; } + void set_stereo(bool _v) { m__dirty = true; m_stereo = std::move(_v); } + uint32_t start() const { return m_start; } + void set_start(uint32_t _v) { m__dirty = true; m_start = std::move(_v); } + uint32_t end() const { return m_end; } + void set_end(uint32_t _v) { m__dirty = true; m_end = std::move(_v); } + uint32_t frame_count() const { return m_frame_count; } + void set_frame_count(uint32_t _v) { m__dirty = true; m_frame_count = std::move(_v); } + uint32_t loop_frame_count() const { return m_loop_frame_count; } + void set_loop_frame_count(uint32_t _v) { m__dirty = true; m_loop_frame_count = std::move(_v); } + bool loop_enabled() const { return m_loop_enabled; } + void set_loop_enabled(bool _v) { m__dirty = true; m_loop_enabled = std::move(_v); } + uint8_t beat_count() const { return m_beat_count; } + void set_beat_count(uint8_t _v) { m__dirty = true; m_beat_count = std::move(_v); } + uint16_t sample_rate() const { return m_sample_rate; } + void set_sample_rate(uint16_t _v) { m__dirty = true; m_sample_rate = std::move(_v); } + std::vector* sample_data() const { return m_sample_data.get(); } + void set_sample_data(std::unique_ptr> _v) { m__dirty = true; m_sample_data = std::move(_v); } + mpc2000snd_t* _root() const { return m__root; } + void set__root(mpc2000snd_t* _v) { m__dirty = true; m__root = std::move(_v); } + kaitai::kstruct* _parent() const { return m__parent; } + void set__parent(kaitai::kstruct* _v) { m__dirty = true; m__parent = std::move(_v); } + +private: + std::string m_magic; + std::string m_name; + uint8_t m_level; + int8_t m_tune; + bool m_stereo; + uint32_t m_start; + uint32_t m_end; + uint32_t m_frame_count; + uint32_t m_loop_frame_count; + bool m_loop_enabled; + uint8_t m_beat_count; + uint16_t m_sample_rate; + std::unique_ptr> m_sample_data; + mpc2000snd_t* m__root; + kaitai::kstruct* m__parent; +}; diff --git a/src/main/file/kaitai/generated/mpc2000xl_all.cpp b/src/main/file/kaitai/generated/mpc2000xl_all.cpp new file mode 100644 index 000000000..06bde019f --- /dev/null +++ b/src/main/file/kaitai/generated/mpc2000xl_all.cpp @@ -0,0 +1,3167 @@ +// This is a generated file! Please edit source .ksy file and use kaitai-struct-compiler to rebuild + +#include "mpc2000xl_all.h" +#include "kaitai/exceptions.h" +#include +#include +const std::set mpc2000xl_all_t::_values_bus_t{ + mpc2000xl_all_t::BUS_MIDI, + mpc2000xl_all_t::BUS_DRUM1, + mpc2000xl_all_t::BUS_DRUM2, + mpc2000xl_all_t::BUS_DRUM3, + mpc2000xl_all_t::BUS_DRUM4, +}; +bool mpc2000xl_all_t::_is_defined_bus_t(mpc2000xl_all_t::bus_t v) { + return mpc2000xl_all_t::_values_bus_t.find(v) != mpc2000xl_all_t::_values_bus_t.end(); +} +const std::set mpc2000xl_all_t::_values_click_output_t{ + mpc2000xl_all_t::CLICK_OUTPUT_STEREO, + mpc2000xl_all_t::CLICK_OUTPUT_INDIV1, + mpc2000xl_all_t::CLICK_OUTPUT_INDIV2, + mpc2000xl_all_t::CLICK_OUTPUT_INDIV3, + mpc2000xl_all_t::CLICK_OUTPUT_INDIV4, + mpc2000xl_all_t::CLICK_OUTPUT_INDIV5, + mpc2000xl_all_t::CLICK_OUTPUT_INDIV6, + mpc2000xl_all_t::CLICK_OUTPUT_INDIV7, + mpc2000xl_all_t::CLICK_OUTPUT_INDIV8, +}; +bool mpc2000xl_all_t::_is_defined_click_output_t(mpc2000xl_all_t::click_output_t v) { + return mpc2000xl_all_t::_values_click_output_t.find(v) != mpc2000xl_all_t::_values_click_output_t.end(); +} +const std::set mpc2000xl_all_t::_values_controller_t{ + mpc2000xl_all_t::CONTROLLER_BANK_SEL_MSB, + mpc2000xl_all_t::CONTROLLER_MOD_WHEEL, + mpc2000xl_all_t::CONTROLLER_BREATH_CONT, + mpc2000xl_all_t::CONTROLLER_CC3, + mpc2000xl_all_t::CONTROLLER_FOOT_CONTROL, + mpc2000xl_all_t::CONTROLLER_PORTA_TIME, + mpc2000xl_all_t::CONTROLLER_DATA_ENTRY, + mpc2000xl_all_t::CONTROLLER_MAIN_VOLUME, + mpc2000xl_all_t::CONTROLLER_BALANCE, + mpc2000xl_all_t::CONTROLLER_CC9, + mpc2000xl_all_t::CONTROLLER_PAN, + mpc2000xl_all_t::CONTROLLER_EXPRESSION, + mpc2000xl_all_t::CONTROLLER_EFFECT_1, + mpc2000xl_all_t::CONTROLLER_EFFECT_2, + mpc2000xl_all_t::CONTROLLER_CC14, + mpc2000xl_all_t::CONTROLLER_CC15, + mpc2000xl_all_t::CONTROLLER_GEN_PUR_1, + mpc2000xl_all_t::CONTROLLER_GEN_PUR_2, + mpc2000xl_all_t::CONTROLLER_GEN_PUR_3, + mpc2000xl_all_t::CONTROLLER_GEN_PUR_4, + mpc2000xl_all_t::CONTROLLER_CC20, + mpc2000xl_all_t::CONTROLLER_CC21, + mpc2000xl_all_t::CONTROLLER_CC22, + mpc2000xl_all_t::CONTROLLER_CC23, + mpc2000xl_all_t::CONTROLLER_CC24, + mpc2000xl_all_t::CONTROLLER_CC25, + mpc2000xl_all_t::CONTROLLER_CC26, + mpc2000xl_all_t::CONTROLLER_CC27, + mpc2000xl_all_t::CONTROLLER_CC28, + mpc2000xl_all_t::CONTROLLER_CC29, + mpc2000xl_all_t::CONTROLLER_CC30, + mpc2000xl_all_t::CONTROLLER_CC31, + mpc2000xl_all_t::CONTROLLER_BANK_SEL_LSB, + mpc2000xl_all_t::CONTROLLER_MOD_WHEL_LSB, + mpc2000xl_all_t::CONTROLLER_BREATH_LSB, + mpc2000xl_all_t::CONTROLLER_CC35, + mpc2000xl_all_t::CONTROLLER_FOOT_CNT_LSB, + mpc2000xl_all_t::CONTROLLER_PORT_TIME_LS, + mpc2000xl_all_t::CONTROLLER_DATA_ENT_LSB, + mpc2000xl_all_t::CONTROLLER_MAIN_VOL_LSB, + mpc2000xl_all_t::CONTROLLER_BALANCE_LSB, + mpc2000xl_all_t::CONTROLLER_CC41, + mpc2000xl_all_t::CONTROLLER_PAN_LSB, + mpc2000xl_all_t::CONTROLLER_EXPRESS_LSB, + mpc2000xl_all_t::CONTROLLER_EFFECT_1_LSB, + mpc2000xl_all_t::CONTROLLER_EFFECT_2_MSB, + mpc2000xl_all_t::CONTROLLER_CC46, + mpc2000xl_all_t::CONTROLLER_CC47, + mpc2000xl_all_t::CONTROLLER_GEN_PUR_1_LS, + mpc2000xl_all_t::CONTROLLER_GEN_PUR_2_LS, + mpc2000xl_all_t::CONTROLLER_GEN_PUR_3_LS, + mpc2000xl_all_t::CONTROLLER_GEN_PUR_4_LS, + mpc2000xl_all_t::CONTROLLER_CC52, + mpc2000xl_all_t::CONTROLLER_CC53, + mpc2000xl_all_t::CONTROLLER_CC54, + mpc2000xl_all_t::CONTROLLER_CC55, + mpc2000xl_all_t::CONTROLLER_CC56, + mpc2000xl_all_t::CONTROLLER_CC57, + mpc2000xl_all_t::CONTROLLER_CC58, + mpc2000xl_all_t::CONTROLLER_CC59, + mpc2000xl_all_t::CONTROLLER_CC60, + mpc2000xl_all_t::CONTROLLER_CC61, + mpc2000xl_all_t::CONTROLLER_CC62, + mpc2000xl_all_t::CONTROLLER_CC63, + mpc2000xl_all_t::CONTROLLER_SUSTAIN_PDL, + mpc2000xl_all_t::CONTROLLER_PORTA_PEDAL, + mpc2000xl_all_t::CONTROLLER_SOSTENUTO, + mpc2000xl_all_t::CONTROLLER_SOFT_PEDAL, + mpc2000xl_all_t::CONTROLLER_LEGATO_FT_SW, + mpc2000xl_all_t::CONTROLLER_HOLD_2, + mpc2000xl_all_t::CONTROLLER_SOUND_VARI, + mpc2000xl_all_t::CONTROLLER_TIMBER_HARMO, + mpc2000xl_all_t::CONTROLLER_RELEASE_TIME, + mpc2000xl_all_t::CONTROLLER_ATTACK_TIME, + mpc2000xl_all_t::CONTROLLER_BRIGHTNESS, + mpc2000xl_all_t::CONTROLLER_SOUND_CONT_6, + mpc2000xl_all_t::CONTROLLER_SOUND_CONT_7, + mpc2000xl_all_t::CONTROLLER_SOUND_CONT_8, + mpc2000xl_all_t::CONTROLLER_SOUND_CONT_9, + mpc2000xl_all_t::CONTROLLER_SOUND_CONT10, + mpc2000xl_all_t::CONTROLLER_GEN_PUR_5, + mpc2000xl_all_t::CONTROLLER_GEN_PUR_6, + mpc2000xl_all_t::CONTROLLER_GEN_PUR_7, + mpc2000xl_all_t::CONTROLLER_GEN_PUR_8, + mpc2000xl_all_t::CONTROLLER_PORTA_CNTRL, + mpc2000xl_all_t::CONTROLLER_CC85, + mpc2000xl_all_t::CONTROLLER_CC86, + mpc2000xl_all_t::CONTROLLER_CC87, + mpc2000xl_all_t::CONTROLLER_CC88, + mpc2000xl_all_t::CONTROLLER_CC89, + mpc2000xl_all_t::CONTROLLER_CC90, + mpc2000xl_all_t::CONTROLLER_EXT_EFF_DPTH, + mpc2000xl_all_t::CONTROLLER_TREMOLO_DPTH, + mpc2000xl_all_t::CONTROLLER_CHORUS_DEPTH, + mpc2000xl_all_t::CONTROLLER_DETUNE_DEPTH, + mpc2000xl_all_t::CONTROLLER_PHASER_DEPTH, + mpc2000xl_all_t::CONTROLLER_DATA_INCRE, + mpc2000xl_all_t::CONTROLLER_DATA_DECRE, + mpc2000xl_all_t::CONTROLLER_NRPN_LSB, + mpc2000xl_all_t::CONTROLLER_NRPN_MSB, + mpc2000xl_all_t::CONTROLLER_RPN_LSB, + mpc2000xl_all_t::CONTROLLER_RPN_MSB, + mpc2000xl_all_t::CONTROLLER_CC102, + mpc2000xl_all_t::CONTROLLER_CC103, + mpc2000xl_all_t::CONTROLLER_CC104, + mpc2000xl_all_t::CONTROLLER_CC105, + mpc2000xl_all_t::CONTROLLER_CC106, + mpc2000xl_all_t::CONTROLLER_CC107, + mpc2000xl_all_t::CONTROLLER_CC108, + mpc2000xl_all_t::CONTROLLER_CC109, + mpc2000xl_all_t::CONTROLLER_CC110, + mpc2000xl_all_t::CONTROLLER_CC111, + mpc2000xl_all_t::CONTROLLER_CC112, + mpc2000xl_all_t::CONTROLLER_CC113, + mpc2000xl_all_t::CONTROLLER_CC114, + mpc2000xl_all_t::CONTROLLER_CC115, + mpc2000xl_all_t::CONTROLLER_CC116, + mpc2000xl_all_t::CONTROLLER_CC117, + mpc2000xl_all_t::CONTROLLER_CC118, + mpc2000xl_all_t::CONTROLLER_CC119, + mpc2000xl_all_t::CONTROLLER_ALL_SND_OFF, + mpc2000xl_all_t::CONTROLLER_RESET_CONTRL, + mpc2000xl_all_t::CONTROLLER_LOCAL_ON_OFF, + mpc2000xl_all_t::CONTROLLER_ALL_NOTE_OFF, + mpc2000xl_all_t::CONTROLLER_OMNI_OFF, + mpc2000xl_all_t::CONTROLLER_OMNI_ON, + mpc2000xl_all_t::CONTROLLER_MONO_MODE_ON, + mpc2000xl_all_t::CONTROLLER_POLY_MODE_ON, +}; +bool mpc2000xl_all_t::_is_defined_controller_t(mpc2000xl_all_t::controller_t v) { + return mpc2000xl_all_t::_values_controller_t.find(v) != mpc2000xl_all_t::_values_controller_t.end(); +} +const std::set mpc2000xl_all_t::_values_count_in_mode_t{ + mpc2000xl_all_t::COUNT_IN_MODE_FALSE, + mpc2000xl_all_t::COUNT_IN_MODE_REC_ONLY, + mpc2000xl_all_t::COUNT_IN_MODE_REC_AND_PLAY, +}; +bool mpc2000xl_all_t::_is_defined_count_in_mode_t(mpc2000xl_all_t::count_in_mode_t v) { + return mpc2000xl_all_t::_values_count_in_mode_t.find(v) != mpc2000xl_all_t::_values_count_in_mode_t.end(); +} +const std::set mpc2000xl_all_t::_values_duration_of_recorded_notes_t{ + mpc2000xl_all_t::DURATION_OF_RECORDED_NOTES_AS_PLAYED, + mpc2000xl_all_t::DURATION_OF_RECORDED_NOTES_TC_VALUE, +}; +bool mpc2000xl_all_t::_is_defined_duration_of_recorded_notes_t(mpc2000xl_all_t::duration_of_recorded_notes_t v) { + return mpc2000xl_all_t::_values_duration_of_recorded_notes_t.find(v) != mpc2000xl_all_t::_values_duration_of_recorded_notes_t.end(); +} +const std::set mpc2000xl_all_t::_values_frame_rate_t{ + mpc2000xl_all_t::FRAME_RATE_FPS_24, + mpc2000xl_all_t::FRAME_RATE_FPS_25, + mpc2000xl_all_t::FRAME_RATE_FPS_30D, + mpc2000xl_all_t::FRAME_RATE_FPS_30, +}; +bool mpc2000xl_all_t::_is_defined_frame_rate_t(mpc2000xl_all_t::frame_rate_t v) { + return mpc2000xl_all_t::_values_frame_rate_t.find(v) != mpc2000xl_all_t::_values_frame_rate_t.end(); +} +const std::set mpc2000xl_all_t::_values_midi_input_filter_type_t{ + mpc2000xl_all_t::MIDI_INPUT_FILTER_TYPE_NOTES, + mpc2000xl_all_t::MIDI_INPUT_FILTER_TYPE_PITCH_BEND, + mpc2000xl_all_t::MIDI_INPUT_FILTER_TYPE_PROG_CHANGE, + mpc2000xl_all_t::MIDI_INPUT_FILTER_TYPE_CH_PRESSURE, + mpc2000xl_all_t::MIDI_INPUT_FILTER_TYPE_POLY_PRESS, + mpc2000xl_all_t::MIDI_INPUT_FILTER_TYPE_EXCLUSIVE, + mpc2000xl_all_t::MIDI_INPUT_FILTER_TYPE_CC0_BANK_SEL_MSB, + mpc2000xl_all_t::MIDI_INPUT_FILTER_TYPE_CC1_MOD_WHEEL, + mpc2000xl_all_t::MIDI_INPUT_FILTER_TYPE_CC2_BREATH_CONT, + mpc2000xl_all_t::MIDI_INPUT_FILTER_TYPE_CC3, + mpc2000xl_all_t::MIDI_INPUT_FILTER_TYPE_CC4_FOOT_CONTROL, + mpc2000xl_all_t::MIDI_INPUT_FILTER_TYPE_CC5_PORTA_TIME, + mpc2000xl_all_t::MIDI_INPUT_FILTER_TYPE_CC6_DATA_ENTRY, + mpc2000xl_all_t::MIDI_INPUT_FILTER_TYPE_CC7_MAIN_VOLUME, + mpc2000xl_all_t::MIDI_INPUT_FILTER_TYPE_CC8_BALANCE, + mpc2000xl_all_t::MIDI_INPUT_FILTER_TYPE_CC9, + mpc2000xl_all_t::MIDI_INPUT_FILTER_TYPE_CC10_PAN, + mpc2000xl_all_t::MIDI_INPUT_FILTER_TYPE_CC11_EXPRESSION, + mpc2000xl_all_t::MIDI_INPUT_FILTER_TYPE_CC12_EFFECT_1, + mpc2000xl_all_t::MIDI_INPUT_FILTER_TYPE_CC13_EFFECT_2, + mpc2000xl_all_t::MIDI_INPUT_FILTER_TYPE_CC14, + mpc2000xl_all_t::MIDI_INPUT_FILTER_TYPE_CC15, + mpc2000xl_all_t::MIDI_INPUT_FILTER_TYPE_CC16_GEN_PUR_1, + mpc2000xl_all_t::MIDI_INPUT_FILTER_TYPE_CC17_GEN_PUR_2, + mpc2000xl_all_t::MIDI_INPUT_FILTER_TYPE_CC18_GEN_PUR_3, + mpc2000xl_all_t::MIDI_INPUT_FILTER_TYPE_CC19_GEN_PUR_4, + mpc2000xl_all_t::MIDI_INPUT_FILTER_TYPE_CC20, + mpc2000xl_all_t::MIDI_INPUT_FILTER_TYPE_CC21, + mpc2000xl_all_t::MIDI_INPUT_FILTER_TYPE_CC22, + mpc2000xl_all_t::MIDI_INPUT_FILTER_TYPE_CC23, + mpc2000xl_all_t::MIDI_INPUT_FILTER_TYPE_CC24, + mpc2000xl_all_t::MIDI_INPUT_FILTER_TYPE_CC25, + mpc2000xl_all_t::MIDI_INPUT_FILTER_TYPE_CC26, + mpc2000xl_all_t::MIDI_INPUT_FILTER_TYPE_CC27, + mpc2000xl_all_t::MIDI_INPUT_FILTER_TYPE_CC28, + mpc2000xl_all_t::MIDI_INPUT_FILTER_TYPE_CC29, + mpc2000xl_all_t::MIDI_INPUT_FILTER_TYPE_CC30, + mpc2000xl_all_t::MIDI_INPUT_FILTER_TYPE_CC31, + mpc2000xl_all_t::MIDI_INPUT_FILTER_TYPE_CC32_BANK_SEL_LSB, + mpc2000xl_all_t::MIDI_INPUT_FILTER_TYPE_CC33_MOD_WHEL_LSB, + mpc2000xl_all_t::MIDI_INPUT_FILTER_TYPE_CC34_BREATH_LSB, + mpc2000xl_all_t::MIDI_INPUT_FILTER_TYPE_CC35, + mpc2000xl_all_t::MIDI_INPUT_FILTER_TYPE_CC36_FOOT_CNT_LSB, + mpc2000xl_all_t::MIDI_INPUT_FILTER_TYPE_CC37_PORT_TIME_LS, + mpc2000xl_all_t::MIDI_INPUT_FILTER_TYPE_CC38_DATA_ENT_LSB, + mpc2000xl_all_t::MIDI_INPUT_FILTER_TYPE_CC39_MAIN_VOL_LSB, + mpc2000xl_all_t::MIDI_INPUT_FILTER_TYPE_CC40_BALANCE_LSB, + mpc2000xl_all_t::MIDI_INPUT_FILTER_TYPE_CC41, + mpc2000xl_all_t::MIDI_INPUT_FILTER_TYPE_CC42_PAN_LSB, + mpc2000xl_all_t::MIDI_INPUT_FILTER_TYPE_CC43_EXPRESS_LSB, + mpc2000xl_all_t::MIDI_INPUT_FILTER_TYPE_CC44_EFFECT_1_LSB, + mpc2000xl_all_t::MIDI_INPUT_FILTER_TYPE_CC45_EFFECT_2_MSB, + mpc2000xl_all_t::MIDI_INPUT_FILTER_TYPE_CC46, + mpc2000xl_all_t::MIDI_INPUT_FILTER_TYPE_CC47, + mpc2000xl_all_t::MIDI_INPUT_FILTER_TYPE_CC48_GEN_PUR_1_LS, + mpc2000xl_all_t::MIDI_INPUT_FILTER_TYPE_CC49_GEN_PUR_2_LS, + mpc2000xl_all_t::MIDI_INPUT_FILTER_TYPE_CC50_GEN_PUR_3_LS, + mpc2000xl_all_t::MIDI_INPUT_FILTER_TYPE_CC51_GEN_PUR_4_LS, + mpc2000xl_all_t::MIDI_INPUT_FILTER_TYPE_CC52, + mpc2000xl_all_t::MIDI_INPUT_FILTER_TYPE_CC53, + mpc2000xl_all_t::MIDI_INPUT_FILTER_TYPE_CC54, + mpc2000xl_all_t::MIDI_INPUT_FILTER_TYPE_CC55, + mpc2000xl_all_t::MIDI_INPUT_FILTER_TYPE_CC56, + mpc2000xl_all_t::MIDI_INPUT_FILTER_TYPE_CC57, + mpc2000xl_all_t::MIDI_INPUT_FILTER_TYPE_CC58, + mpc2000xl_all_t::MIDI_INPUT_FILTER_TYPE_CC59, + mpc2000xl_all_t::MIDI_INPUT_FILTER_TYPE_CC60, + mpc2000xl_all_t::MIDI_INPUT_FILTER_TYPE_CC61, + mpc2000xl_all_t::MIDI_INPUT_FILTER_TYPE_CC62, + mpc2000xl_all_t::MIDI_INPUT_FILTER_TYPE_CC63, + mpc2000xl_all_t::MIDI_INPUT_FILTER_TYPE_CC64_SUSTAIN_PDL, + mpc2000xl_all_t::MIDI_INPUT_FILTER_TYPE_CC65_PORTA_PEDAL, + mpc2000xl_all_t::MIDI_INPUT_FILTER_TYPE_CC66_SOSTENUTO, + mpc2000xl_all_t::MIDI_INPUT_FILTER_TYPE_CC67_SOFT_PEDAL, + mpc2000xl_all_t::MIDI_INPUT_FILTER_TYPE_CC68_LEGATO_FT_SW, + mpc2000xl_all_t::MIDI_INPUT_FILTER_TYPE_CC69_HOLD_2, + mpc2000xl_all_t::MIDI_INPUT_FILTER_TYPE_CC70_SOUND_VARI, + mpc2000xl_all_t::MIDI_INPUT_FILTER_TYPE_CC71_TIMBER_HARMO, + mpc2000xl_all_t::MIDI_INPUT_FILTER_TYPE_CC72_RELEASE_TIME, + mpc2000xl_all_t::MIDI_INPUT_FILTER_TYPE_CC73_ATTACK_TIME, + mpc2000xl_all_t::MIDI_INPUT_FILTER_TYPE_CC74_BRIGHTNESS, + mpc2000xl_all_t::MIDI_INPUT_FILTER_TYPE_CC75_SOUND_CONT_6, + mpc2000xl_all_t::MIDI_INPUT_FILTER_TYPE_CC76_SOUND_CONT_7, + mpc2000xl_all_t::MIDI_INPUT_FILTER_TYPE_CC77_SOUND_CONT_8, + mpc2000xl_all_t::MIDI_INPUT_FILTER_TYPE_CC78_SOUND_CONT_9, + mpc2000xl_all_t::MIDI_INPUT_FILTER_TYPE_CC79_SOUND_CONT10, + mpc2000xl_all_t::MIDI_INPUT_FILTER_TYPE_CC80_GEN_PUR_5, + mpc2000xl_all_t::MIDI_INPUT_FILTER_TYPE_CC81_GEN_PUR_6, + mpc2000xl_all_t::MIDI_INPUT_FILTER_TYPE_CC82_GEN_PUR_7, + mpc2000xl_all_t::MIDI_INPUT_FILTER_TYPE_CC83_GEN_PUR_8, + mpc2000xl_all_t::MIDI_INPUT_FILTER_TYPE_CC84_PORTA_CNTRL, + mpc2000xl_all_t::MIDI_INPUT_FILTER_TYPE_CC85, + mpc2000xl_all_t::MIDI_INPUT_FILTER_TYPE_CC86, + mpc2000xl_all_t::MIDI_INPUT_FILTER_TYPE_CC87, + mpc2000xl_all_t::MIDI_INPUT_FILTER_TYPE_CC88, + mpc2000xl_all_t::MIDI_INPUT_FILTER_TYPE_CC89, + mpc2000xl_all_t::MIDI_INPUT_FILTER_TYPE_CC90, + mpc2000xl_all_t::MIDI_INPUT_FILTER_TYPE_CC91_EXT_EFF_DPTH, + mpc2000xl_all_t::MIDI_INPUT_FILTER_TYPE_CC92_TREMOLO_DPTH, + mpc2000xl_all_t::MIDI_INPUT_FILTER_TYPE_CC93_CHORUS_DEPTH, + mpc2000xl_all_t::MIDI_INPUT_FILTER_TYPE_CC94_DETUNE_DEPTH, + mpc2000xl_all_t::MIDI_INPUT_FILTER_TYPE_CC95_PHASER_DEPTH, + mpc2000xl_all_t::MIDI_INPUT_FILTER_TYPE_CC96_DATA_INCRE, + mpc2000xl_all_t::MIDI_INPUT_FILTER_TYPE_CC97_DATA_DECRE, + mpc2000xl_all_t::MIDI_INPUT_FILTER_TYPE_CC98_NRPN_LSB, + mpc2000xl_all_t::MIDI_INPUT_FILTER_TYPE_CC99_NRPN_MSB, + mpc2000xl_all_t::MIDI_INPUT_FILTER_TYPE_CC100_RPN_LSB, + mpc2000xl_all_t::MIDI_INPUT_FILTER_TYPE_CC101_RPN_MSB, + mpc2000xl_all_t::MIDI_INPUT_FILTER_TYPE_CC102, + mpc2000xl_all_t::MIDI_INPUT_FILTER_TYPE_CC103, + mpc2000xl_all_t::MIDI_INPUT_FILTER_TYPE_CC104, + mpc2000xl_all_t::MIDI_INPUT_FILTER_TYPE_CC105, + mpc2000xl_all_t::MIDI_INPUT_FILTER_TYPE_CC106, + mpc2000xl_all_t::MIDI_INPUT_FILTER_TYPE_CC107, + mpc2000xl_all_t::MIDI_INPUT_FILTER_TYPE_CC108, + mpc2000xl_all_t::MIDI_INPUT_FILTER_TYPE_CC109, + mpc2000xl_all_t::MIDI_INPUT_FILTER_TYPE_CC110, + mpc2000xl_all_t::MIDI_INPUT_FILTER_TYPE_CC111, + mpc2000xl_all_t::MIDI_INPUT_FILTER_TYPE_CC112, + mpc2000xl_all_t::MIDI_INPUT_FILTER_TYPE_CC113, + mpc2000xl_all_t::MIDI_INPUT_FILTER_TYPE_CC114, + mpc2000xl_all_t::MIDI_INPUT_FILTER_TYPE_CC115, + mpc2000xl_all_t::MIDI_INPUT_FILTER_TYPE_CC116, + mpc2000xl_all_t::MIDI_INPUT_FILTER_TYPE_CC117, + mpc2000xl_all_t::MIDI_INPUT_FILTER_TYPE_CC118, + mpc2000xl_all_t::MIDI_INPUT_FILTER_TYPE_CC119, + mpc2000xl_all_t::MIDI_INPUT_FILTER_TYPE_CC120_ALL_SND_OFF, + mpc2000xl_all_t::MIDI_INPUT_FILTER_TYPE_CC121_RESET_CONTRL, + mpc2000xl_all_t::MIDI_INPUT_FILTER_TYPE_CC122_LOCAL_ON_OFF, + mpc2000xl_all_t::MIDI_INPUT_FILTER_TYPE_CC123_ALL_NOTE_OFF, + mpc2000xl_all_t::MIDI_INPUT_FILTER_TYPE_CC124_OMNI_OFF, + mpc2000xl_all_t::MIDI_INPUT_FILTER_TYPE_CC125_OMNI_ON, + mpc2000xl_all_t::MIDI_INPUT_FILTER_TYPE_CC126_MONO_MODE_ON, + mpc2000xl_all_t::MIDI_INPUT_FILTER_TYPE_CC127_POLY_MODE_ON, +}; +bool mpc2000xl_all_t::_is_defined_midi_input_filter_type_t(mpc2000xl_all_t::midi_input_filter_type_t v) { + return mpc2000xl_all_t::_values_midi_input_filter_type_t.find(v) != mpc2000xl_all_t::_values_midi_input_filter_type_t.end(); +} +const std::set mpc2000xl_all_t::_values_midi_input_receive_channel_t{ + mpc2000xl_all_t::MIDI_INPUT_RECEIVE_CHANNEL_ALL, + mpc2000xl_all_t::MIDI_INPUT_RECEIVE_CHANNEL_CH1, + mpc2000xl_all_t::MIDI_INPUT_RECEIVE_CHANNEL_CH2, + mpc2000xl_all_t::MIDI_INPUT_RECEIVE_CHANNEL_CH3, + mpc2000xl_all_t::MIDI_INPUT_RECEIVE_CHANNEL_CH4, + mpc2000xl_all_t::MIDI_INPUT_RECEIVE_CHANNEL_CH5, + mpc2000xl_all_t::MIDI_INPUT_RECEIVE_CHANNEL_CH6, + mpc2000xl_all_t::MIDI_INPUT_RECEIVE_CHANNEL_CH7, + mpc2000xl_all_t::MIDI_INPUT_RECEIVE_CHANNEL_CH8, + mpc2000xl_all_t::MIDI_INPUT_RECEIVE_CHANNEL_CH9, + mpc2000xl_all_t::MIDI_INPUT_RECEIVE_CHANNEL_CH10, + mpc2000xl_all_t::MIDI_INPUT_RECEIVE_CHANNEL_CH11, + mpc2000xl_all_t::MIDI_INPUT_RECEIVE_CHANNEL_CH12, + mpc2000xl_all_t::MIDI_INPUT_RECEIVE_CHANNEL_CH13, + mpc2000xl_all_t::MIDI_INPUT_RECEIVE_CHANNEL_CH14, + mpc2000xl_all_t::MIDI_INPUT_RECEIVE_CHANNEL_CH15, + mpc2000xl_all_t::MIDI_INPUT_RECEIVE_CHANNEL_CH16, +}; +bool mpc2000xl_all_t::_is_defined_midi_input_receive_channel_t(mpc2000xl_all_t::midi_input_receive_channel_t v) { + return mpc2000xl_all_t::_values_midi_input_receive_channel_t.find(v) != mpc2000xl_all_t::_values_midi_input_receive_channel_t.end(); +} +const std::set mpc2000xl_all_t::_values_midi_switch_function_t{ + mpc2000xl_all_t::MIDI_SWITCH_FUNCTION_PLAY_STRT, + mpc2000xl_all_t::MIDI_SWITCH_FUNCTION_PLAY, + mpc2000xl_all_t::MIDI_SWITCH_FUNCTION_STOP, + mpc2000xl_all_t::MIDI_SWITCH_FUNCTION_REC_AND_PLAY, + mpc2000xl_all_t::MIDI_SWITCH_FUNCTION_ODUB_AND_PLAY, + mpc2000xl_all_t::MIDI_SWITCH_FUNCTION_REC_PUNCH, + mpc2000xl_all_t::MIDI_SWITCH_FUNCTION_ODUB_PNCH, + mpc2000xl_all_t::MIDI_SWITCH_FUNCTION_TAP, + mpc2000xl_all_t::MIDI_SWITCH_FUNCTION_PAD_BNK_A, + mpc2000xl_all_t::MIDI_SWITCH_FUNCTION_PAD_BNK_B, + mpc2000xl_all_t::MIDI_SWITCH_FUNCTION_PAD_BNK_C, + mpc2000xl_all_t::MIDI_SWITCH_FUNCTION_PAD_BNK_D, + mpc2000xl_all_t::MIDI_SWITCH_FUNCTION_PAD_1, + mpc2000xl_all_t::MIDI_SWITCH_FUNCTION_PAD_2, + mpc2000xl_all_t::MIDI_SWITCH_FUNCTION_PAD_3, + mpc2000xl_all_t::MIDI_SWITCH_FUNCTION_PAD_4, + mpc2000xl_all_t::MIDI_SWITCH_FUNCTION_PAD_5, + mpc2000xl_all_t::MIDI_SWITCH_FUNCTION_PAD_6, + mpc2000xl_all_t::MIDI_SWITCH_FUNCTION_PAD_7, + mpc2000xl_all_t::MIDI_SWITCH_FUNCTION_PAD_8, + mpc2000xl_all_t::MIDI_SWITCH_FUNCTION_PAD_9, + mpc2000xl_all_t::MIDI_SWITCH_FUNCTION_PAD_10, + mpc2000xl_all_t::MIDI_SWITCH_FUNCTION_PAD_11, + mpc2000xl_all_t::MIDI_SWITCH_FUNCTION_PAD_12, + mpc2000xl_all_t::MIDI_SWITCH_FUNCTION_PAD_13, + mpc2000xl_all_t::MIDI_SWITCH_FUNCTION_PAD_14, + mpc2000xl_all_t::MIDI_SWITCH_FUNCTION_PAD_15, + mpc2000xl_all_t::MIDI_SWITCH_FUNCTION_PAD_16, + mpc2000xl_all_t::MIDI_SWITCH_FUNCTION_F1, + mpc2000xl_all_t::MIDI_SWITCH_FUNCTION_F2, + mpc2000xl_all_t::MIDI_SWITCH_FUNCTION_F3, + mpc2000xl_all_t::MIDI_SWITCH_FUNCTION_F4, + mpc2000xl_all_t::MIDI_SWITCH_FUNCTION_F5, + mpc2000xl_all_t::MIDI_SWITCH_FUNCTION_F6, +}; +bool mpc2000xl_all_t::_is_defined_midi_switch_function_t(mpc2000xl_all_t::midi_switch_function_t v) { + return mpc2000xl_all_t::_values_midi_switch_function_t.find(v) != mpc2000xl_all_t::_values_midi_switch_function_t.end(); +} +const std::set mpc2000xl_all_t::_values_midi_sync_mode_t{ + mpc2000xl_all_t::MIDI_SYNC_MODE_FALSE, + mpc2000xl_all_t::MIDI_SYNC_MODE_MIDI_CLOCK, + mpc2000xl_all_t::MIDI_SYNC_MODE_TIME_CODE, +}; +bool mpc2000xl_all_t::_is_defined_midi_sync_mode_t(mpc2000xl_all_t::midi_sync_mode_t v) { + return mpc2000xl_all_t::_values_midi_sync_mode_t.find(v) != mpc2000xl_all_t::_values_midi_sync_mode_t.end(); +} +const std::set mpc2000xl_all_t::_values_midi_sync_output_t{ + mpc2000xl_all_t::MIDI_SYNC_OUTPUT_A, + mpc2000xl_all_t::MIDI_SYNC_OUTPUT_B, + mpc2000xl_all_t::MIDI_SYNC_OUTPUT_A_AND_B, +}; +bool mpc2000xl_all_t::_is_defined_midi_sync_output_t(mpc2000xl_all_t::midi_sync_output_t v) { + return mpc2000xl_all_t::_values_midi_sync_output_t.find(v) != mpc2000xl_all_t::_values_midi_sync_output_t.end(); +} +const std::set mpc2000xl_all_t::_values_mixer_event_param_t{ + mpc2000xl_all_t::MIXER_EVENT_PARAM_STEREO_LEVEL, + mpc2000xl_all_t::MIXER_EVENT_PARAM_STEREO_PAN, + mpc2000xl_all_t::MIXER_EVENT_PARAM_FXSEND_LEVEL, + mpc2000xl_all_t::MIXER_EVENT_PARAM_INDIV_LEVEL, +}; +bool mpc2000xl_all_t::_is_defined_mixer_event_param_t(mpc2000xl_all_t::mixer_event_param_t v) { + return mpc2000xl_all_t::_values_mixer_event_param_t.find(v) != mpc2000xl_all_t::_values_mixer_event_param_t.end(); +} +const std::set mpc2000xl_all_t::_values_off_on_t{ + mpc2000xl_all_t::OFF_ON_FALSE, + mpc2000xl_all_t::OFF_ON_TRUE, +}; +bool mpc2000xl_all_t::_is_defined_off_on_t(mpc2000xl_all_t::off_on_t v) { + return mpc2000xl_all_t::_values_off_on_t.find(v) != mpc2000xl_all_t::_values_off_on_t.end(); +} +const std::set mpc2000xl_all_t::_values_rate_t{ + mpc2000xl_all_t::RATE_ONE_4, + mpc2000xl_all_t::RATE_ONE_4_3, + mpc2000xl_all_t::RATE_ONE_8, + mpc2000xl_all_t::RATE_ONE_8_3, + mpc2000xl_all_t::RATE_ONE_16, + mpc2000xl_all_t::RATE_ONE_16_3, + mpc2000xl_all_t::RATE_ONE_32, + mpc2000xl_all_t::RATE_ONE_32_3, +}; +bool mpc2000xl_all_t::_is_defined_rate_t(mpc2000xl_all_t::rate_t v) { + return mpc2000xl_all_t::_values_rate_t.find(v) != mpc2000xl_all_t::_values_rate_t.end(); +} +const std::set mpc2000xl_all_t::_values_sequence_is_used_t{ + mpc2000xl_all_t::SEQUENCE_IS_USED_FALSE, + mpc2000xl_all_t::SEQUENCE_IS_USED_TRUE, +}; +bool mpc2000xl_all_t::_is_defined_sequence_is_used_t(mpc2000xl_all_t::sequence_is_used_t v) { + return mpc2000xl_all_t::_values_sequence_is_used_t.find(v) != mpc2000xl_all_t::_values_sequence_is_used_t.end(); +} +const std::set mpc2000xl_all_t::_values_soft_thru_mode_t{ + mpc2000xl_all_t::SOFT_THRU_MODE_FALSE, + mpc2000xl_all_t::SOFT_THRU_MODE_AS_TRACK, + mpc2000xl_all_t::SOFT_THRU_MODE_OMNI_A, + mpc2000xl_all_t::SOFT_THRU_MODE_OMNI_B, + mpc2000xl_all_t::SOFT_THRU_MODE_OMNI_AB, +}; +bool mpc2000xl_all_t::_is_defined_soft_thru_mode_t(mpc2000xl_all_t::soft_thru_mode_t v) { + return mpc2000xl_all_t::_values_soft_thru_mode_t.find(v) != mpc2000xl_all_t::_values_soft_thru_mode_t.end(); +} +const std::set mpc2000xl_all_t::_values_sound_source_t{ + mpc2000xl_all_t::SOUND_SOURCE_CLICK, + mpc2000xl_all_t::SOUND_SOURCE_DRUM1, + mpc2000xl_all_t::SOUND_SOURCE_DRUM2, + mpc2000xl_all_t::SOUND_SOURCE_DRUM3, + mpc2000xl_all_t::SOUND_SOURCE_DRUM4, +}; +bool mpc2000xl_all_t::_is_defined_sound_source_t(mpc2000xl_all_t::sound_source_t v) { + return mpc2000xl_all_t::_values_sound_source_t.find(v) != mpc2000xl_all_t::_values_sound_source_t.end(); +} +const std::set mpc2000xl_all_t::_values_tap_averaging_t{ + mpc2000xl_all_t::TAP_AVERAGING_TAP_AVG_2, + mpc2000xl_all_t::TAP_AVERAGING_TAP_AVG_3, + mpc2000xl_all_t::TAP_AVERAGING_TAP_AVG_4, +}; +bool mpc2000xl_all_t::_is_defined_tap_averaging_t(mpc2000xl_all_t::tap_averaging_t v) { + return mpc2000xl_all_t::_values_tap_averaging_t.find(v) != mpc2000xl_all_t::_values_tap_averaging_t.end(); +} +const std::set mpc2000xl_all_t::_values_time_display_style_t{ + mpc2000xl_all_t::TIME_DISPLAY_STYLE_BAR_BEAT_CLOCK, + mpc2000xl_all_t::TIME_DISPLAY_STYLE_HOUR_MINUTE_SEC, +}; +bool mpc2000xl_all_t::_is_defined_time_display_style_t(mpc2000xl_all_t::time_display_style_t v) { + return mpc2000xl_all_t::_values_time_display_style_t.find(v) != mpc2000xl_all_t::_values_time_display_style_t.end(); +} +const std::set mpc2000xl_all_t::_values_timing_correct_t{ + mpc2000xl_all_t::TIMING_CORRECT_FALSE, + mpc2000xl_all_t::TIMING_CORRECT_ONE_8, + mpc2000xl_all_t::TIMING_CORRECT_ONE_8_3, + mpc2000xl_all_t::TIMING_CORRECT_ONE_16, + mpc2000xl_all_t::TIMING_CORRECT_ONE_16_3, + mpc2000xl_all_t::TIMING_CORRECT_ONE_32, + mpc2000xl_all_t::TIMING_CORRECT_ONE_32_3, +}; +bool mpc2000xl_all_t::_is_defined_timing_correct_t(mpc2000xl_all_t::timing_correct_t v) { + return mpc2000xl_all_t::_values_timing_correct_t.find(v) != mpc2000xl_all_t::_values_timing_correct_t.end(); +} +const std::set mpc2000xl_all_t::_values_unused_used_t{ + mpc2000xl_all_t::UNUSED_USED_UNUSED, + mpc2000xl_all_t::UNUSED_USED_USED, +}; +bool mpc2000xl_all_t::_is_defined_unused_used_t(mpc2000xl_all_t::unused_used_t v) { + return mpc2000xl_all_t::_values_unused_used_t.find(v) != mpc2000xl_all_t::_values_unused_used_t.end(); +} + +mpc2000xl_all_t::mpc2000xl_all_t(kaitai::kstream* p__io, kaitai::kstruct* p__parent, mpc2000xl_all_t* p__root) : kaitai::kstruct(p__io) { + m__parent = p__parent; + m__root = p__root ? p__root : this; + m__dirty = false; +} + +void mpc2000xl_all_t::_read() { + m_magic = m__io->read_bytes(16); + if (!(m_magic == std::string("\x4D\x50\x43\x32\x4B\x58\x4C\x20\x41\x4C\x4C\x20\x31\x2E\x30\x30", 16))) { + throw kaitai::validation_not_equal_error(std::string("\x4D\x50\x43\x32\x4B\x58\x4C\x20\x41\x4C\x4C\x20\x31\x2E\x30\x30", 16), m_magic, m__io, std::string("/seq/0")); + } + m_defaults = std::unique_ptr(new defaults_t(m__io, this, m__root)); + m_defaults->_read(); + m_sequencer = std::unique_ptr(new sequencer_t(m__io, this, m__root)); + m_sequencer->_read(); + m__unnamed3 = m__io->read_bytes(10); + m_count = std::unique_ptr(new count_t(m__io, this, m__root)); + m_count->_read(); + m_midi_output = std::unique_ptr(new midi_output_t(m__io, this, m__root)); + m_midi_output->_read(); + m_midi_input = std::unique_ptr(new midi_input_t(m__io, this, m__root)); + m_midi_input->_read(); + m_midi_sync = std::unique_ptr(new midi_sync_t(m__io, this, m__root)); + m_midi_sync->_read(); + m_song_global = std::unique_ptr(new song_global_t(m__io, this, m__root)); + m_song_global->_read(); + m__unnamed9 = m__io->read_bytes(5); + m_locations = std::unique_ptr>>(new std::vector>()); + const int l_locations = 9; + for (int i = 0; i < l_locations; i++) { + std::unique_ptr _t_locations = std::unique_ptr(new location_t(m__io, this, m__root)); + try { + _t_locations->_read(); + } catch(...) { + m_locations->push_back(std::move(_t_locations)); + throw; + } + m_locations->push_back(std::move(_t_locations)); + } + m_misc = std::unique_ptr(new misc_t(m__io, this, m__root)); + m_misc->_read(); + m__unnamed12 = m__io->read_bytes(3); + m_step_edit_options = std::unique_ptr(new step_edit_options_t(m__io, this, m__root)); + m_step_edit_options->_read(); + m_prog_change_to_seq = m__io->read_bits_int_le(1); + m__unnamed15 = m__io->read_bytes(78); + m_sequences_metas = std::unique_ptr>>(new std::vector>()); + const int l_sequences_metas = 99; + for (int i = 0; i < l_sequences_metas; i++) { + std::unique_ptr _t_sequences_metas = std::unique_ptr(new sequence_meta_t(m__io, this, m__root)); + try { + _t_sequences_metas->_read(); + } catch(...) { + m_sequences_metas->push_back(std::move(_t_sequences_metas)); + throw; + } + m_sequences_metas->push_back(std::move(_t_sequences_metas)); + } + m_songs = std::unique_ptr>>(new std::vector>()); + const int l_songs = 20; + for (int i = 0; i < l_songs; i++) { + std::unique_ptr _t_songs = std::unique_ptr(new song_t(m__io, this, m__root)); + try { + _t_songs->_read(); + } catch(...) { + m_songs->push_back(std::move(_t_songs)); + throw; + } + m_songs->push_back(std::move(_t_songs)); + } + m_sequences = std::unique_ptr>>(new std::vector>()); + { + int i = 0; + while (!m__io->is_eof()) { + std::unique_ptr _t_sequences = std::unique_ptr(new sequence_t(m__io, this, m__root)); + try { + _t_sequences->_read(); + } catch(...) { + m_sequences->push_back(std::move(_t_sequences)); + throw; + } + m_sequences->push_back(std::move(_t_sequences)); + i++; + } + } + m__dirty = false; +} + +void mpc2000xl_all_t::_fetch_instances() { + m_defaults.get()->_fetch_instances(); + m_sequencer.get()->_fetch_instances(); + m_count.get()->_fetch_instances(); + m_midi_output.get()->_fetch_instances(); + m_midi_input.get()->_fetch_instances(); + m_midi_sync.get()->_fetch_instances(); + m_song_global.get()->_fetch_instances(); + for (std::size_t i = 0; i < m_locations->size(); ++i) { + m_locations->at(i).get()->_fetch_instances(); + } + m_misc.get()->_fetch_instances(); + m_step_edit_options.get()->_fetch_instances(); + for (std::size_t i = 0; i < m_sequences_metas->size(); ++i) { + m_sequences_metas->at(i).get()->_fetch_instances(); + } + for (std::size_t i = 0; i < m_songs->size(); ++i) { + m_songs->at(i).get()->_fetch_instances(); + } + for (std::size_t i = 0; i < m_sequences->size(); ++i) { + m_sequences->at(i).get()->_fetch_instances(); + } +} + +void mpc2000xl_all_t::_write() { + m__io->write_bytes(m_magic); + if (m_defaults.get() == nullptr) { + throw std::runtime_error("/seq/1: nested object is not set"); + } + m_defaults.get()->_set_io(m__io); + m_defaults.get()->_write(); + if (m_sequencer.get() == nullptr) { + throw std::runtime_error("/seq/2: nested object is not set"); + } + m_sequencer.get()->_set_io(m__io); + m_sequencer.get()->_write(); + m__io->write_bytes(m__unnamed3); + if (m_count.get() == nullptr) { + throw std::runtime_error("/seq/4: nested object is not set"); + } + m_count.get()->_set_io(m__io); + m_count.get()->_write(); + if (m_midi_output.get() == nullptr) { + throw std::runtime_error("/seq/5: nested object is not set"); + } + m_midi_output.get()->_set_io(m__io); + m_midi_output.get()->_write(); + if (m_midi_input.get() == nullptr) { + throw std::runtime_error("/seq/6: nested object is not set"); + } + m_midi_input.get()->_set_io(m__io); + m_midi_input.get()->_write(); + if (m_midi_sync.get() == nullptr) { + throw std::runtime_error("/seq/7: nested object is not set"); + } + m_midi_sync.get()->_set_io(m__io); + m_midi_sync.get()->_write(); + if (m_song_global.get() == nullptr) { + throw std::runtime_error("/seq/8: nested object is not set"); + } + m_song_global.get()->_set_io(m__io); + m_song_global.get()->_write(); + m__io->write_bytes(m__unnamed9); + if (m_locations == nullptr) { + throw std::runtime_error("/seq/10: repeated field is not set"); + } + for (std::vector>::const_iterator it = m_locations->begin(); it != m_locations->end(); ++it) { + if ((*it).get() == nullptr) { + throw std::runtime_error("/seq/10: nested object is not set"); + } + (*it).get()->_set_io(m__io); + (*it).get()->_write(); + } + if (m_misc.get() == nullptr) { + throw std::runtime_error("/seq/11: nested object is not set"); + } + m_misc.get()->_set_io(m__io); + m_misc.get()->_write(); + m__io->write_bytes(m__unnamed12); + if (m_step_edit_options.get() == nullptr) { + throw std::runtime_error("/seq/13: nested object is not set"); + } + m_step_edit_options.get()->_set_io(m__io); + m_step_edit_options.get()->_write(); + m__io->write_bits_int_le(1, ((m_prog_change_to_seq) ? 1 : 0)); + m__io->write_bytes(m__unnamed15); + if (m_sequences_metas == nullptr) { + throw std::runtime_error("/seq/16: repeated field is not set"); + } + for (std::vector>::const_iterator it = m_sequences_metas->begin(); it != m_sequences_metas->end(); ++it) { + if ((*it).get() == nullptr) { + throw std::runtime_error("/seq/16: nested object is not set"); + } + (*it).get()->_set_io(m__io); + (*it).get()->_write(); + } + if (m_songs == nullptr) { + throw std::runtime_error("/seq/17: repeated field is not set"); + } + for (std::vector>::const_iterator it = m_songs->begin(); it != m_songs->end(); ++it) { + if ((*it).get() == nullptr) { + throw std::runtime_error("/seq/17: nested object is not set"); + } + (*it).get()->_set_io(m__io); + (*it).get()->_write(); + } + if (m_sequences == nullptr) { + throw std::runtime_error("/seq/18: repeated field is not set"); + } + if (m_sequences == nullptr) { + throw std::runtime_error("/seq/18: repeated field is not set"); + } + for (std::vector>::const_iterator it = m_sequences->begin(); it != m_sequences->end(); ++it) { + if ((*it).get() == nullptr) { + throw std::runtime_error("/seq/18: nested object is not set"); + } + (*it).get()->_set_io(m__io); + (*it).get()->_write(); + } + _fetch_instances(); + m__dirty = false; +} + +void mpc2000xl_all_t::_check() { + if (m_magic.size() != static_cast(16)) { + throw std::runtime_error("/seq/0: size mismatch"); + } + if (!(m_magic == std::string("\x4D\x50\x43\x32\x4B\x58\x4C\x20\x41\x4C\x4C\x20\x31\x2E\x30\x30", 16))) { + throw kaitai::validation_not_equal_error(std::string("\x4D\x50\x43\x32\x4B\x58\x4C\x20\x41\x4C\x4C\x20\x31\x2E\x30\x30", 16), m_magic, m__io, std::string("/seq/0")); + } + if (m_defaults.get() == nullptr) { + throw std::runtime_error("/seq/1: nested object is not set"); + } + m_defaults.get()->_set_io(m__io); + m_defaults.get()->_check(); + if (m_sequencer.get() == nullptr) { + throw std::runtime_error("/seq/2: nested object is not set"); + } + m_sequencer.get()->_set_io(m__io); + m_sequencer.get()->_check(); + if (m__unnamed3.size() != static_cast(10)) { + throw std::runtime_error("/seq/3: size mismatch"); + } + if (m_count.get() == nullptr) { + throw std::runtime_error("/seq/4: nested object is not set"); + } + m_count.get()->_set_io(m__io); + m_count.get()->_check(); + if (m_midi_output.get() == nullptr) { + throw std::runtime_error("/seq/5: nested object is not set"); + } + m_midi_output.get()->_set_io(m__io); + m_midi_output.get()->_check(); + if (m_midi_input.get() == nullptr) { + throw std::runtime_error("/seq/6: nested object is not set"); + } + m_midi_input.get()->_set_io(m__io); + m_midi_input.get()->_check(); + if (m_midi_sync.get() == nullptr) { + throw std::runtime_error("/seq/7: nested object is not set"); + } + m_midi_sync.get()->_set_io(m__io); + m_midi_sync.get()->_check(); + if (m_song_global.get() == nullptr) { + throw std::runtime_error("/seq/8: nested object is not set"); + } + m_song_global.get()->_set_io(m__io); + m_song_global.get()->_check(); + if (m__unnamed9.size() != static_cast(5)) { + throw std::runtime_error("/seq/9: size mismatch"); + } + if (m_locations == nullptr) { + throw std::runtime_error("/seq/10: repeated field is not set"); + } + if (m_locations->size() != static_cast(9)) { + throw std::runtime_error("/seq/10: repeat-expr size mismatch"); + } + for (std::vector>::const_iterator it = m_locations->begin(); it != m_locations->end(); ++it) { + if ((*it).get() == nullptr) { + throw std::runtime_error("/seq/10: nested object is not set"); + } + (*it).get()->_set_io(m__io); + (*it).get()->_check(); + } + if (m_misc.get() == nullptr) { + throw std::runtime_error("/seq/11: nested object is not set"); + } + m_misc.get()->_set_io(m__io); + m_misc.get()->_check(); + if (m__unnamed12.size() != static_cast(3)) { + throw std::runtime_error("/seq/12: size mismatch"); + } + if (m_step_edit_options.get() == nullptr) { + throw std::runtime_error("/seq/13: nested object is not set"); + } + m_step_edit_options.get()->_set_io(m__io); + m_step_edit_options.get()->_check(); + if (m__unnamed15.size() != static_cast(78)) { + throw std::runtime_error("/seq/15: size mismatch"); + } + if (m_sequences_metas == nullptr) { + throw std::runtime_error("/seq/16: repeated field is not set"); + } + if (m_sequences_metas->size() != static_cast(99)) { + throw std::runtime_error("/seq/16: repeat-expr size mismatch"); + } + for (std::vector>::const_iterator it = m_sequences_metas->begin(); it != m_sequences_metas->end(); ++it) { + if ((*it).get() == nullptr) { + throw std::runtime_error("/seq/16: nested object is not set"); + } + (*it).get()->_set_io(m__io); + (*it).get()->_check(); + } + if (m_songs == nullptr) { + throw std::runtime_error("/seq/17: repeated field is not set"); + } + if (m_songs->size() != static_cast(20)) { + throw std::runtime_error("/seq/17: repeat-expr size mismatch"); + } + for (std::vector>::const_iterator it = m_songs->begin(); it != m_songs->end(); ++it) { + if ((*it).get() == nullptr) { + throw std::runtime_error("/seq/17: nested object is not set"); + } + (*it).get()->_set_io(m__io); + (*it).get()->_check(); + } + if (m_sequences == nullptr) { + throw std::runtime_error("/seq/18: repeated field is not set"); + } + for (std::vector>::const_iterator it = m_sequences->begin(); it != m_sequences->end(); ++it) { + const std::size_t i = static_cast(it - m_sequences->begin()); + const sequence_t* _ = (*it).get(); + if ((*it).get() == nullptr) { + throw std::runtime_error("/seq/18: nested object is not set"); + } + (*it).get()->_set_io(m__io); + (*it).get()->_check(); + } + m__dirty = false; +} + +mpc2000xl_all_t::~mpc2000xl_all_t() {} + +mpc2000xl_all_t::bar_t::bar_t(int32_t p_idx, kaitai::kstream* p__io, mpc2000xl_all_t::sequence_body_t* p__parent, mpc2000xl_all_t* p__root) : kaitai::kstruct(p__io) { + m__parent = p__parent; + m__root = p__root; + m_idx = p_idx; + m__dirty = false; + f_denominator = false; + f_first_tick = false; + f_numerator = false; +} + +void mpc2000xl_all_t::bar_t::_read() { + m_ticks_per_beat = m__io->read_u1(); + m_last_tick = m__io->read_bits_int_le(24); + m__dirty = false; +} + +void mpc2000xl_all_t::bar_t::_fetch_instances() { +} + +void mpc2000xl_all_t::bar_t::_write() { + m__io->write_u1(m_ticks_per_beat); + m__io->write_bits_int_le(24, m_last_tick); + _fetch_instances(); + m__dirty = false; +} + +void mpc2000xl_all_t::bar_t::_check() { + m__dirty = false; +} + +mpc2000xl_all_t::bar_t::~bar_t() {} + +int32_t mpc2000xl_all_t::bar_t::denominator() { + if (f_denominator) + return m_denominator; + f_denominator = true; + m_denominator = 4 / (ticks_per_beat() / 96); + return m_denominator; +} + +int32_t mpc2000xl_all_t::bar_t::first_tick() { + if (f_first_tick) + return m_first_tick; + f_first_tick = true; + m_first_tick = ((idx() == 0) ? (0) : (static_cast(_parent())->bars()->at(idx() - 1)->last_tick())); + return m_first_tick; +} + +int32_t mpc2000xl_all_t::bar_t::numerator() { + if (f_numerator) + return m_numerator; + f_numerator = true; + m_numerator = (last_tick() - first_tick()) / ticks_per_beat(); + return m_numerator; +} + +mpc2000xl_all_t::ch_pressure_event_t::ch_pressure_event_t(kaitai::kstream* p__io, mpc2000xl_all_t::event_t* p__parent, mpc2000xl_all_t* p__root) : kaitai::kstruct(p__io) { + m__parent = p__parent; + m__root = p__root; + m__dirty = false; +} + +void mpc2000xl_all_t::ch_pressure_event_t::_read() { + m_pressure = m__io->read_u1(); + if (!(m_pressure >= 0)) { + throw kaitai::validation_less_than_error(0, m_pressure, m__io, std::string("/types/ch_pressure_event/seq/0")); + } + if (!(m_pressure <= 127)) { + throw kaitai::validation_greater_than_error(127, m_pressure, m__io, std::string("/types/ch_pressure_event/seq/0")); + } + m__unnamed1 = m__io->read_bytes(2); + m__dirty = false; +} + +void mpc2000xl_all_t::ch_pressure_event_t::_fetch_instances() { +} + +void mpc2000xl_all_t::ch_pressure_event_t::_write() { + m__io->write_u1(m_pressure); + m__io->write_bytes(m__unnamed1); + _fetch_instances(); + m__dirty = false; +} + +void mpc2000xl_all_t::ch_pressure_event_t::_check() { + if (!(m_pressure >= 0)) { + throw kaitai::validation_less_than_error(0, m_pressure, m__io, std::string("/types/ch_pressure_event/seq/0")); + } + if (!(m_pressure <= 127)) { + throw kaitai::validation_greater_than_error(127, m_pressure, m__io, std::string("/types/ch_pressure_event/seq/0")); + } + if (m__unnamed1.size() != static_cast(2)) { + throw std::runtime_error("/types/ch_pressure_event/seq/1: size mismatch"); + } + m__dirty = false; +} + +mpc2000xl_all_t::ch_pressure_event_t::~ch_pressure_event_t() {} + +mpc2000xl_all_t::control_change_event_t::control_change_event_t(kaitai::kstream* p__io, mpc2000xl_all_t::event_t* p__parent, mpc2000xl_all_t* p__root) : kaitai::kstruct(p__io) { + m__parent = p__parent; + m__root = p__root; + m__dirty = false; +} + +void mpc2000xl_all_t::control_change_event_t::_read() { + m_controller = static_cast(m__io->read_u1()); + m_value = m__io->read_u1(); + if (!(m_value >= 0)) { + throw kaitai::validation_less_than_error(0, m_value, m__io, std::string("/types/control_change_event/seq/1")); + } + if (!(m_value <= 127)) { + throw kaitai::validation_greater_than_error(127, m_value, m__io, std::string("/types/control_change_event/seq/1")); + } + m__unnamed2 = m__io->read_bytes(1); + m__dirty = false; +} + +void mpc2000xl_all_t::control_change_event_t::_fetch_instances() { +} + +void mpc2000xl_all_t::control_change_event_t::_write() { + m__io->write_u1(static_cast(m_controller)); + m__io->write_u1(m_value); + m__io->write_bytes(m__unnamed2); + _fetch_instances(); + m__dirty = false; +} + +void mpc2000xl_all_t::control_change_event_t::_check() { + if (!(m_value >= 0)) { + throw kaitai::validation_less_than_error(0, m_value, m__io, std::string("/types/control_change_event/seq/1")); + } + if (!(m_value <= 127)) { + throw kaitai::validation_greater_than_error(127, m_value, m__io, std::string("/types/control_change_event/seq/1")); + } + if (m__unnamed2.size() != static_cast(1)) { + throw std::runtime_error("/types/control_change_event/seq/2: size mismatch"); + } + m__dirty = false; +} + +mpc2000xl_all_t::control_change_event_t::~control_change_event_t() {} + +mpc2000xl_all_t::count_t::count_t(kaitai::kstream* p__io, mpc2000xl_all_t* p__parent, mpc2000xl_all_t* p__root) : kaitai::kstruct(p__io) { + m__parent = p__parent; + m__root = p__root; + m__dirty = false; +} + +void mpc2000xl_all_t::count_t::_read() { + m_enabled = m__io->read_bits_int_le(1); + m__unnamed1 = m__io->read_bits_int_le(7); + m_count_in_mode = static_cast(m__io->read_u1()); + m_click_volume = m__io->read_u1(); + if (!(m_click_volume >= 0)) { + throw kaitai::validation_less_than_error(0, m_click_volume, m__io, std::string("/types/count/seq/3")); + } + if (!(m_click_volume <= 100)) { + throw kaitai::validation_greater_than_error(100, m_click_volume, m__io, std::string("/types/count/seq/3")); + } + m_rate = static_cast(m__io->read_u1()); + m_enabled_in_play = m__io->read_bits_int_le(1); + m__unnamed6 = m__io->read_bits_int_le(7); + m_enabled_in_rec = m__io->read_bits_int_le(1); + m__unnamed8 = m__io->read_bits_int_le(7); + m_click_output = static_cast(m__io->read_u1()); + m_wait_for_key = m__io->read_bits_int_le(1); + m__unnamed11 = m__io->read_bits_int_le(7); + m_sound_source = static_cast(m__io->read_u1()); + m_accent_pad_index = m__io->read_u1(); + m_normal_pad_index = m__io->read_u1(); + m_accent_velo = m__io->read_u1(); + if (!(m_accent_velo >= 1)) { + throw kaitai::validation_less_than_error(1, m_accent_velo, m__io, std::string("/types/count/seq/15")); + } + if (!(m_accent_velo <= 127)) { + throw kaitai::validation_greater_than_error(127, m_accent_velo, m__io, std::string("/types/count/seq/15")); + } + m_normal_velo = m__io->read_u1(); + if (!(m_normal_velo >= 1)) { + throw kaitai::validation_less_than_error(1, m_normal_velo, m__io, std::string("/types/count/seq/16")); + } + if (!(m_normal_velo <= 127)) { + throw kaitai::validation_greater_than_error(127, m_normal_velo, m__io, std::string("/types/count/seq/16")); + } + m__dirty = false; +} + +void mpc2000xl_all_t::count_t::_fetch_instances() { +} + +void mpc2000xl_all_t::count_t::_write() { + m__io->write_bits_int_le(1, ((m_enabled) ? 1 : 0)); + m__io->write_bits_int_le(7, m__unnamed1); + m__io->write_u1(static_cast(m_count_in_mode)); + m__io->write_u1(m_click_volume); + m__io->write_u1(static_cast(m_rate)); + m__io->write_bits_int_le(1, ((m_enabled_in_play) ? 1 : 0)); + m__io->write_bits_int_le(7, m__unnamed6); + m__io->write_bits_int_le(1, ((m_enabled_in_rec) ? 1 : 0)); + m__io->write_bits_int_le(7, m__unnamed8); + m__io->write_u1(static_cast(m_click_output)); + m__io->write_bits_int_le(1, ((m_wait_for_key) ? 1 : 0)); + m__io->write_bits_int_le(7, m__unnamed11); + m__io->write_u1(static_cast(m_sound_source)); + m__io->write_u1(m_accent_pad_index); + m__io->write_u1(m_normal_pad_index); + m__io->write_u1(m_accent_velo); + m__io->write_u1(m_normal_velo); + _fetch_instances(); + m__dirty = false; +} + +void mpc2000xl_all_t::count_t::_check() { + if (!(m_click_volume >= 0)) { + throw kaitai::validation_less_than_error(0, m_click_volume, m__io, std::string("/types/count/seq/3")); + } + if (!(m_click_volume <= 100)) { + throw kaitai::validation_greater_than_error(100, m_click_volume, m__io, std::string("/types/count/seq/3")); + } + if (!(m_accent_velo >= 1)) { + throw kaitai::validation_less_than_error(1, m_accent_velo, m__io, std::string("/types/count/seq/15")); + } + if (!(m_accent_velo <= 127)) { + throw kaitai::validation_greater_than_error(127, m_accent_velo, m__io, std::string("/types/count/seq/15")); + } + if (!(m_normal_velo >= 1)) { + throw kaitai::validation_less_than_error(1, m_normal_velo, m__io, std::string("/types/count/seq/16")); + } + if (!(m_normal_velo <= 127)) { + throw kaitai::validation_greater_than_error(127, m_normal_velo, m__io, std::string("/types/count/seq/16")); + } + m__dirty = false; +} + +mpc2000xl_all_t::count_t::~count_t() {} + +mpc2000xl_all_t::defaults_t::defaults_t(kaitai::kstream* p__io, mpc2000xl_all_t* p__parent, mpc2000xl_all_t* p__root) : kaitai::kstruct(p__io) { + m__parent = p__parent; + m__root = p__root; + m__dirty = false; +} + +void mpc2000xl_all_t::defaults_t::_read() { + m_sequence_name = kaitai::kstream::bytes_to_str(m__io->read_bytes(16), "ASCII"); + m__unnamed1 = m__io->read_bytes(6); + m_tempo = m__io->read_u2le(); + m_numerator = m__io->read_u1(); + m_denominator = m__io->read_u1(); + m_bar_count = m__io->read_u2le(); + m_tick_count = m__io->read_u2le(); + m_unknown1 = std::unique_ptr>(new std::vector()); + const int l_unknown1 = 4; + for (int i = 0; i < l_unknown1; i++) { + m_unknown1->push_back(std::move(m__io->read_u4le())); + } + m_unknown2 = m__io->read_bytes(74); + m_device_names = std::unique_ptr>(new std::vector()); + const int l_device_names = 33; + for (int i = 0; i < l_device_names; i++) { + m_device_names->push_back(std::move(kaitai::kstream::bytes_to_str(m__io->read_bytes(8), "ASCII"))); + } + m_track_names = std::unique_ptr>(new std::vector()); + const int l_track_names = 64; + for (int i = 0; i < l_track_names; i++) { + m_track_names->push_back(std::move(kaitai::kstream::bytes_to_str(m__io->read_bytes(16), "ASCII"))); + } + m_devices = std::unique_ptr>(new std::vector()); + const int l_devices = 64; + for (int i = 0; i < l_devices; i++) { + m_devices->push_back(std::move(m__io->read_u1())); + } + m_buses = std::unique_ptr>(new std::vector()); + const int l_buses = 64; + for (int i = 0; i < l_buses; i++) { + m_buses->push_back(std::move(static_cast(m__io->read_u1()))); + } + m_programs = std::unique_ptr>(new std::vector()); + const int l_programs = 64; + for (int i = 0; i < l_programs; i++) { + m_programs->push_back(std::move(m__io->read_u1())); + } + m_track_velocities = std::unique_ptr>(new std::vector()); + const int l_track_velocities = 64; + for (int i = 0; i < l_track_velocities; i++) { + m_track_velocities->push_back(std::move(m__io->read_u1())); + } + m_track_statuses = std::unique_ptr>>(new std::vector>()); + const int l_track_statuses = 64; + for (int i = 0; i < l_track_statuses; i++) { + std::unique_ptr _t_track_statuses = std::unique_ptr(new track_status_t(m__io, this, m__root)); + try { + _t_track_statuses->_read(); + } catch(...) { + m_track_statuses->push_back(std::move(_t_track_statuses)); + throw; + } + m_track_statuses->push_back(std::move(_t_track_statuses)); + } + m__unnamed16 = m__io->read_bytes(64); + m__dirty = false; +} + +void mpc2000xl_all_t::defaults_t::_fetch_instances() { + for (std::size_t i = 0; i < m_unknown1->size(); ++i) { + } + for (std::size_t i = 0; i < m_device_names->size(); ++i) { + } + for (std::size_t i = 0; i < m_track_names->size(); ++i) { + } + for (std::size_t i = 0; i < m_devices->size(); ++i) { + } + for (std::size_t i = 0; i < m_buses->size(); ++i) { + } + for (std::size_t i = 0; i < m_programs->size(); ++i) { + } + for (std::size_t i = 0; i < m_track_velocities->size(); ++i) { + } + for (std::size_t i = 0; i < m_track_statuses->size(); ++i) { + m_track_statuses->at(i).get()->_fetch_instances(); + } +} + +void mpc2000xl_all_t::defaults_t::_write() { + m__io->write_bytes(m_sequence_name); + m__io->write_bytes(m__unnamed1); + m__io->write_u2le(m_tempo); + m__io->write_u1(m_numerator); + m__io->write_u1(m_denominator); + m__io->write_u2le(m_bar_count); + m__io->write_u2le(m_tick_count); + if (m_unknown1 == nullptr) { + throw std::runtime_error("/types/defaults/seq/7: repeated field is not set"); + } + for (std::vector::const_iterator it = m_unknown1->begin(); it != m_unknown1->end(); ++it) { + m__io->write_u4le((*it)); + } + m__io->write_bytes(m_unknown2); + if (m_device_names == nullptr) { + throw std::runtime_error("/types/defaults/seq/9: repeated field is not set"); + } + for (std::vector::const_iterator it = m_device_names->begin(); it != m_device_names->end(); ++it) { + m__io->write_bytes((*it)); + } + if (m_track_names == nullptr) { + throw std::runtime_error("/types/defaults/seq/10: repeated field is not set"); + } + for (std::vector::const_iterator it = m_track_names->begin(); it != m_track_names->end(); ++it) { + m__io->write_bytes((*it)); + } + if (m_devices == nullptr) { + throw std::runtime_error("/types/defaults/seq/11: repeated field is not set"); + } + for (std::vector::const_iterator it = m_devices->begin(); it != m_devices->end(); ++it) { + m__io->write_u1((*it)); + } + if (m_buses == nullptr) { + throw std::runtime_error("/types/defaults/seq/12: repeated field is not set"); + } + for (std::vector::const_iterator it = m_buses->begin(); it != m_buses->end(); ++it) { + m__io->write_u1(static_cast((*it))); + } + if (m_programs == nullptr) { + throw std::runtime_error("/types/defaults/seq/13: repeated field is not set"); + } + for (std::vector::const_iterator it = m_programs->begin(); it != m_programs->end(); ++it) { + m__io->write_u1((*it)); + } + if (m_track_velocities == nullptr) { + throw std::runtime_error("/types/defaults/seq/14: repeated field is not set"); + } + for (std::vector::const_iterator it = m_track_velocities->begin(); it != m_track_velocities->end(); ++it) { + m__io->write_u1((*it)); + } + if (m_track_statuses == nullptr) { + throw std::runtime_error("/types/defaults/seq/15: repeated field is not set"); + } + for (std::vector>::const_iterator it = m_track_statuses->begin(); it != m_track_statuses->end(); ++it) { + if ((*it).get() == nullptr) { + throw std::runtime_error("/types/defaults/seq/15: nested object is not set"); + } + (*it).get()->_set_io(m__io); + (*it).get()->_write(); + } + m__io->write_bytes(m__unnamed16); + _fetch_instances(); + m__dirty = false; +} + +void mpc2000xl_all_t::defaults_t::_check() { + if (m_sequence_name.size() != static_cast(16)) { + throw std::runtime_error("/types/defaults/seq/0: size mismatch"); + } + if (m__unnamed1.size() != static_cast(6)) { + throw std::runtime_error("/types/defaults/seq/1: size mismatch"); + } + if (m_unknown1 == nullptr) { + throw std::runtime_error("/types/defaults/seq/7: repeated field is not set"); + } + if (m_unknown1->size() != static_cast(4)) { + throw std::runtime_error("/types/defaults/seq/7: repeat-expr size mismatch"); + } + for (std::vector::const_iterator it = m_unknown1->begin(); it != m_unknown1->end(); ++it) { + } + if (m_unknown2.size() != static_cast(74)) { + throw std::runtime_error("/types/defaults/seq/8: size mismatch"); + } + if (m_device_names == nullptr) { + throw std::runtime_error("/types/defaults/seq/9: repeated field is not set"); + } + if (m_device_names->size() != static_cast(33)) { + throw std::runtime_error("/types/defaults/seq/9: repeat-expr size mismatch"); + } + for (std::vector::const_iterator it = m_device_names->begin(); it != m_device_names->end(); ++it) { + if ((*it).size() != static_cast(8)) { + throw std::runtime_error("/types/defaults/seq/9: size mismatch"); + } + } + if (m_track_names == nullptr) { + throw std::runtime_error("/types/defaults/seq/10: repeated field is not set"); + } + if (m_track_names->size() != static_cast(64)) { + throw std::runtime_error("/types/defaults/seq/10: repeat-expr size mismatch"); + } + for (std::vector::const_iterator it = m_track_names->begin(); it != m_track_names->end(); ++it) { + if ((*it).size() != static_cast(16)) { + throw std::runtime_error("/types/defaults/seq/10: size mismatch"); + } + } + if (m_devices == nullptr) { + throw std::runtime_error("/types/defaults/seq/11: repeated field is not set"); + } + if (m_devices->size() != static_cast(64)) { + throw std::runtime_error("/types/defaults/seq/11: repeat-expr size mismatch"); + } + for (std::vector::const_iterator it = m_devices->begin(); it != m_devices->end(); ++it) { + } + if (m_buses == nullptr) { + throw std::runtime_error("/types/defaults/seq/12: repeated field is not set"); + } + if (m_buses->size() != static_cast(64)) { + throw std::runtime_error("/types/defaults/seq/12: repeat-expr size mismatch"); + } + for (std::vector::const_iterator it = m_buses->begin(); it != m_buses->end(); ++it) { + } + if (m_programs == nullptr) { + throw std::runtime_error("/types/defaults/seq/13: repeated field is not set"); + } + if (m_programs->size() != static_cast(64)) { + throw std::runtime_error("/types/defaults/seq/13: repeat-expr size mismatch"); + } + for (std::vector::const_iterator it = m_programs->begin(); it != m_programs->end(); ++it) { + } + if (m_track_velocities == nullptr) { + throw std::runtime_error("/types/defaults/seq/14: repeated field is not set"); + } + if (m_track_velocities->size() != static_cast(64)) { + throw std::runtime_error("/types/defaults/seq/14: repeat-expr size mismatch"); + } + for (std::vector::const_iterator it = m_track_velocities->begin(); it != m_track_velocities->end(); ++it) { + } + if (m_track_statuses == nullptr) { + throw std::runtime_error("/types/defaults/seq/15: repeated field is not set"); + } + if (m_track_statuses->size() != static_cast(64)) { + throw std::runtime_error("/types/defaults/seq/15: repeat-expr size mismatch"); + } + for (std::vector>::const_iterator it = m_track_statuses->begin(); it != m_track_statuses->end(); ++it) { + if ((*it).get() == nullptr) { + throw std::runtime_error("/types/defaults/seq/15: nested object is not set"); + } + (*it).get()->_set_io(m__io); + (*it).get()->_check(); + } + if (m__unnamed16.size() != static_cast(64)) { + throw std::runtime_error("/types/defaults/seq/16: size mismatch"); + } + m__dirty = false; +} + +mpc2000xl_all_t::defaults_t::~defaults_t() {} + +mpc2000xl_all_t::event_t::event_t(kaitai::kstream* p__io, mpc2000xl_all_t::sequence_body_t* p__parent, mpc2000xl_all_t* p__root) : kaitai::kstruct(p__io) { + m__parent = p__parent; + m__root = p__root; + m__dirty = false; + n_duration_bits_1 = true; + n_track = true; + n_duration_bits_2 = true; + n_id = true; + n_terminator_pad = true; + n_terminator = true; +} + +void mpc2000xl_all_t::event_t::_read() { + m_tick = m__io->read_bits_int_le(20); + n_duration_bits_1 = true; + if (tick() < 1048575) { + n_duration_bits_1 = false; + m_duration_bits_1 = m__io->read_bits_int_le(4); + } + n_track = true; + if (tick() < 1048575) { + n_track = false; + m_track = m__io->read_bits_int_le(6); + } + n_duration_bits_2 = true; + if (tick() < 1048575) { + n_duration_bits_2 = false; + m_duration_bits_2 = m__io->read_bits_int_le(2); + } + n_id = true; + if (tick() < 1048575) { + n_id = false; + m_id = m__io->read_u1(); + } + n_terminator_pad = true; + if (tick() >= 1048575) { + n_terminator_pad = false; + m_terminator_pad = m__io->read_bits_int_le(4); + } + if ( ((tick() < 1048575) && (id() <= 127)) ) { + m_note_event = std::unique_ptr(new note_event_t(m__io, this, m__root)); + m_note_event->_read(); + } + if ( ((tick() < 1048575) && (id() == 224)) ) { + m_pitch_bend = std::unique_ptr(new pitch_bend_event_t(m__io, this, m__root)); + m_pitch_bend->_read(); + } + if ( ((tick() < 1048575) && (id() == 176)) ) { + m_control_change = std::unique_ptr(new control_change_event_t(m__io, this, m__root)); + m_control_change->_read(); + } + if ( ((tick() < 1048575) && (id() == 192)) ) { + m_program_change = std::unique_ptr(new program_change_event_t(m__io, this, m__root)); + m_program_change->_read(); + } + if ( ((tick() < 1048575) && (id() == 208)) ) { + m_ch_pressure = std::unique_ptr(new ch_pressure_event_t(m__io, this, m__root)); + m_ch_pressure->_read(); + } + if ( ((tick() < 1048575) && (id() == 160)) ) { + m_poly_pressure = std::unique_ptr(new poly_pressure_event_t(m__io, this, m__root)); + m_poly_pressure->_read(); + } + if ( ((tick() < 1048575) && (id() == 240)) ) { + m_exclusive = std::unique_ptr(new exclusive_event_t(m__io, this, m__root)); + m_exclusive->_read(); + } + n_terminator = true; + if (tick() >= 1048575) { + n_terminator = false; + m_terminator = m__io->read_bytes(5); + } + m__dirty = false; +} + +void mpc2000xl_all_t::event_t::_fetch_instances() { + n_duration_bits_1 = true; + if (tick() < 1048575) { + n_duration_bits_1 = false; + } + n_track = true; + if (tick() < 1048575) { + n_track = false; + } + n_duration_bits_2 = true; + if (tick() < 1048575) { + n_duration_bits_2 = false; + } + n_id = true; + if (tick() < 1048575) { + n_id = false; + } + n_terminator_pad = true; + if (tick() >= 1048575) { + n_terminator_pad = false; + } + if ( ((tick() < 1048575) && (id() <= 127)) ) { + m_note_event.get()->_fetch_instances(); + } + if ( ((tick() < 1048575) && (id() == 224)) ) { + m_pitch_bend.get()->_fetch_instances(); + } + if ( ((tick() < 1048575) && (id() == 176)) ) { + m_control_change.get()->_fetch_instances(); + } + if ( ((tick() < 1048575) && (id() == 192)) ) { + m_program_change.get()->_fetch_instances(); + } + if ( ((tick() < 1048575) && (id() == 208)) ) { + m_ch_pressure.get()->_fetch_instances(); + } + if ( ((tick() < 1048575) && (id() == 160)) ) { + m_poly_pressure.get()->_fetch_instances(); + } + if ( ((tick() < 1048575) && (id() == 240)) ) { + m_exclusive.get()->_fetch_instances(); + } + n_terminator = true; + if (tick() >= 1048575) { + n_terminator = false; + } +} + +void mpc2000xl_all_t::event_t::_write() { + m__io->write_bits_int_le(20, m_tick); + if (tick() < 1048575) { + m__io->write_bits_int_le(4, m_duration_bits_1); + } + if (tick() < 1048575) { + m__io->write_bits_int_le(6, m_track); + } + if (tick() < 1048575) { + m__io->write_bits_int_le(2, m_duration_bits_2); + } + if (tick() < 1048575) { + m__io->write_u1(m_id); + } + if (tick() >= 1048575) { + m__io->write_bits_int_le(4, m_terminator_pad); + } + if ( ((tick() < 1048575) && (id() <= 127)) ) { + if (m_note_event.get() == nullptr) { + throw std::runtime_error("/types/event/seq/6: nested object is not set"); + } + m_note_event.get()->_set_io(m__io); + m_note_event.get()->_write(); + } + if ( ((tick() < 1048575) && (id() == 224)) ) { + if (m_pitch_bend.get() == nullptr) { + throw std::runtime_error("/types/event/seq/7: nested object is not set"); + } + m_pitch_bend.get()->_set_io(m__io); + m_pitch_bend.get()->_write(); + } + if ( ((tick() < 1048575) && (id() == 176)) ) { + if (m_control_change.get() == nullptr) { + throw std::runtime_error("/types/event/seq/8: nested object is not set"); + } + m_control_change.get()->_set_io(m__io); + m_control_change.get()->_write(); + } + if ( ((tick() < 1048575) && (id() == 192)) ) { + if (m_program_change.get() == nullptr) { + throw std::runtime_error("/types/event/seq/9: nested object is not set"); + } + m_program_change.get()->_set_io(m__io); + m_program_change.get()->_write(); + } + if ( ((tick() < 1048575) && (id() == 208)) ) { + if (m_ch_pressure.get() == nullptr) { + throw std::runtime_error("/types/event/seq/10: nested object is not set"); + } + m_ch_pressure.get()->_set_io(m__io); + m_ch_pressure.get()->_write(); + } + if ( ((tick() < 1048575) && (id() == 160)) ) { + if (m_poly_pressure.get() == nullptr) { + throw std::runtime_error("/types/event/seq/11: nested object is not set"); + } + m_poly_pressure.get()->_set_io(m__io); + m_poly_pressure.get()->_write(); + } + if ( ((tick() < 1048575) && (id() == 240)) ) { + if (m_exclusive.get() == nullptr) { + throw std::runtime_error("/types/event/seq/12: nested object is not set"); + } + m_exclusive.get()->_set_io(m__io); + m_exclusive.get()->_write(); + } + if (tick() >= 1048575) { + m__io->write_bytes(m_terminator); + } + _fetch_instances(); + m__dirty = false; +} + +void mpc2000xl_all_t::event_t::_check() { + if (tick() < 1048575) { + if (!(!n_duration_bits_1)) { + throw std::runtime_error("/types/event/seq/1: conditional field is not set"); + } + } else { + if (!n_duration_bits_1) { + throw std::runtime_error("/types/event/seq/1: conditional field should be absent"); + } + } + if (tick() < 1048575) { + if (!(!n_track)) { + throw std::runtime_error("/types/event/seq/2: conditional field is not set"); + } + } else { + if (!n_track) { + throw std::runtime_error("/types/event/seq/2: conditional field should be absent"); + } + } + if (tick() < 1048575) { + if (!(!n_duration_bits_2)) { + throw std::runtime_error("/types/event/seq/3: conditional field is not set"); + } + } else { + if (!n_duration_bits_2) { + throw std::runtime_error("/types/event/seq/3: conditional field should be absent"); + } + } + if (tick() < 1048575) { + if (!(!n_id)) { + throw std::runtime_error("/types/event/seq/4: conditional field is not set"); + } + } else { + if (!n_id) { + throw std::runtime_error("/types/event/seq/4: conditional field should be absent"); + } + } + if (tick() >= 1048575) { + if (!(!n_terminator_pad)) { + throw std::runtime_error("/types/event/seq/5: conditional field is not set"); + } + } else { + if (!n_terminator_pad) { + throw std::runtime_error("/types/event/seq/5: conditional field should be absent"); + } + } + if ( ((tick() < 1048575) && (id() <= 127)) ) { + if (!(m_note_event.get() != nullptr)) { + throw std::runtime_error("/types/event/seq/6: conditional field is not set"); + } + if (m_note_event.get() == nullptr) { + throw std::runtime_error("/types/event/seq/6: nested object is not set"); + } + m_note_event.get()->_set_io(m__io); + m_note_event.get()->_check(); + } else { + if (m_note_event.get() != nullptr) { + throw std::runtime_error("/types/event/seq/6: conditional field should be absent"); + } + } + if ( ((tick() < 1048575) && (id() == 224)) ) { + if (!(m_pitch_bend.get() != nullptr)) { + throw std::runtime_error("/types/event/seq/7: conditional field is not set"); + } + if (m_pitch_bend.get() == nullptr) { + throw std::runtime_error("/types/event/seq/7: nested object is not set"); + } + m_pitch_bend.get()->_set_io(m__io); + m_pitch_bend.get()->_check(); + } else { + if (m_pitch_bend.get() != nullptr) { + throw std::runtime_error("/types/event/seq/7: conditional field should be absent"); + } + } + if ( ((tick() < 1048575) && (id() == 176)) ) { + if (!(m_control_change.get() != nullptr)) { + throw std::runtime_error("/types/event/seq/8: conditional field is not set"); + } + if (m_control_change.get() == nullptr) { + throw std::runtime_error("/types/event/seq/8: nested object is not set"); + } + m_control_change.get()->_set_io(m__io); + m_control_change.get()->_check(); + } else { + if (m_control_change.get() != nullptr) { + throw std::runtime_error("/types/event/seq/8: conditional field should be absent"); + } + } + if ( ((tick() < 1048575) && (id() == 192)) ) { + if (!(m_program_change.get() != nullptr)) { + throw std::runtime_error("/types/event/seq/9: conditional field is not set"); + } + if (m_program_change.get() == nullptr) { + throw std::runtime_error("/types/event/seq/9: nested object is not set"); + } + m_program_change.get()->_set_io(m__io); + m_program_change.get()->_check(); + } else { + if (m_program_change.get() != nullptr) { + throw std::runtime_error("/types/event/seq/9: conditional field should be absent"); + } + } + if ( ((tick() < 1048575) && (id() == 208)) ) { + if (!(m_ch_pressure.get() != nullptr)) { + throw std::runtime_error("/types/event/seq/10: conditional field is not set"); + } + if (m_ch_pressure.get() == nullptr) { + throw std::runtime_error("/types/event/seq/10: nested object is not set"); + } + m_ch_pressure.get()->_set_io(m__io); + m_ch_pressure.get()->_check(); + } else { + if (m_ch_pressure.get() != nullptr) { + throw std::runtime_error("/types/event/seq/10: conditional field should be absent"); + } + } + if ( ((tick() < 1048575) && (id() == 160)) ) { + if (!(m_poly_pressure.get() != nullptr)) { + throw std::runtime_error("/types/event/seq/11: conditional field is not set"); + } + if (m_poly_pressure.get() == nullptr) { + throw std::runtime_error("/types/event/seq/11: nested object is not set"); + } + m_poly_pressure.get()->_set_io(m__io); + m_poly_pressure.get()->_check(); + } else { + if (m_poly_pressure.get() != nullptr) { + throw std::runtime_error("/types/event/seq/11: conditional field should be absent"); + } + } + if ( ((tick() < 1048575) && (id() == 240)) ) { + if (!(m_exclusive.get() != nullptr)) { + throw std::runtime_error("/types/event/seq/12: conditional field is not set"); + } + if (m_exclusive.get() == nullptr) { + throw std::runtime_error("/types/event/seq/12: nested object is not set"); + } + m_exclusive.get()->_set_io(m__io); + m_exclusive.get()->_check(); + } else { + if (m_exclusive.get() != nullptr) { + throw std::runtime_error("/types/event/seq/12: conditional field should be absent"); + } + } + if (tick() >= 1048575) { + if (!(!n_terminator)) { + throw std::runtime_error("/types/event/seq/13: conditional field is not set"); + } + if (m_terminator.size() != static_cast(5)) { + throw std::runtime_error("/types/event/seq/13: size mismatch"); + } + } else { + if (!n_terminator) { + throw std::runtime_error("/types/event/seq/13: conditional field should be absent"); + } + } + m__dirty = false; +} + +mpc2000xl_all_t::event_t::~event_t() {} + +mpc2000xl_all_t::exclusive_event_t::exclusive_event_t(kaitai::kstream* p__io, mpc2000xl_all_t::event_t* p__parent, mpc2000xl_all_t* p__root) : kaitai::kstruct(p__io) { + m__parent = p__parent; + m__root = p__root; + m__dirty = false; +} + +void mpc2000xl_all_t::exclusive_event_t::_read() { + m__unnamed0 = m__io->read_bytes(3); + m_bytes = m__io->read_bytes(2); + if (bytes() == std::string("\xF0\x47", 2)) { + m_mixer = std::unique_ptr(new mixer_event_t(m__io, this, m__root)); + m_mixer->_read(); + } + m__unnamed3 = m__io->read_bytes(14); + m__dirty = false; +} + +void mpc2000xl_all_t::exclusive_event_t::_fetch_instances() { + if (bytes() == std::string("\xF0\x47", 2)) { + m_mixer.get()->_fetch_instances(); + } +} + +void mpc2000xl_all_t::exclusive_event_t::_write() { + m__io->write_bytes(m__unnamed0); + m__io->write_bytes(m_bytes); + if (bytes() == std::string("\xF0\x47", 2)) { + if (m_mixer.get() == nullptr) { + throw std::runtime_error("/types/exclusive_event/seq/2: nested object is not set"); + } + m_mixer.get()->_set_io(m__io); + m_mixer.get()->_write(); + } + m__io->write_bytes(m__unnamed3); + _fetch_instances(); + m__dirty = false; +} + +void mpc2000xl_all_t::exclusive_event_t::_check() { + if (m__unnamed0.size() != static_cast(3)) { + throw std::runtime_error("/types/exclusive_event/seq/0: size mismatch"); + } + if (m_bytes.size() != static_cast(2)) { + throw std::runtime_error("/types/exclusive_event/seq/1: size mismatch"); + } + if (bytes() == std::string("\xF0\x47", 2)) { + if (!(m_mixer.get() != nullptr)) { + throw std::runtime_error("/types/exclusive_event/seq/2: conditional field is not set"); + } + if (m_mixer.get() == nullptr) { + throw std::runtime_error("/types/exclusive_event/seq/2: nested object is not set"); + } + m_mixer.get()->_set_io(m__io); + m_mixer.get()->_check(); + } else { + if (m_mixer.get() != nullptr) { + throw std::runtime_error("/types/exclusive_event/seq/2: conditional field should be absent"); + } + } + if (m__unnamed3.size() != static_cast(14)) { + throw std::runtime_error("/types/exclusive_event/seq/3: size mismatch"); + } + m__dirty = false; +} + +mpc2000xl_all_t::exclusive_event_t::~exclusive_event_t() {} + +mpc2000xl_all_t::location_t::location_t(kaitai::kstream* p__io, mpc2000xl_all_t* p__parent, mpc2000xl_all_t* p__root) : kaitai::kstruct(p__io) { + m__parent = p__parent; + m__root = p__root; + m__dirty = false; + f_display_value = false; +} + +void mpc2000xl_all_t::location_t::_read() { + m_bar = m__io->read_u2le(); + m_beat = m__io->read_u1(); + m_clock = m__io->read_u1(); + m__dirty = false; +} + +void mpc2000xl_all_t::location_t::_fetch_instances() { +} + +void mpc2000xl_all_t::location_t::_write() { + m__io->write_u2le(m_bar); + m__io->write_u1(m_beat); + m__io->write_u1(m_clock); + _fetch_instances(); + m__dirty = false; +} + +void mpc2000xl_all_t::location_t::_check() { + m__dirty = false; +} + +mpc2000xl_all_t::location_t::~location_t() {} + +std::string mpc2000xl_all_t::location_t::display_value() { + if (f_display_value) + return m_display_value; + f_display_value = true; + m_display_value = (((((bar() > 98) ? (kaitai::kstream::to_string(bar() + 1)) : (((bar() > 8) ? (std::string("0") + kaitai::kstream::to_string(bar() + 1)) : (std::string("00") + kaitai::kstream::to_string(bar() + 1))))) + std::string(".")) + ((beat() > 8) ? (kaitai::kstream::to_string(beat() + 1)) : (std::string("0") + kaitai::kstream::to_string(beat() + 1)))) + std::string(".")) + ((clock() > 9) ? (kaitai::kstream::to_string(clock())) : (std::string("0") + kaitai::kstream::to_string(clock()))); + return m_display_value; +} + +mpc2000xl_all_t::midi_input_t::midi_input_t(kaitai::kstream* p__io, mpc2000xl_all_t* p__parent, mpc2000xl_all_t* p__root) : kaitai::kstruct(p__io) { + m__parent = p__parent; + m__root = p__root; + m__dirty = false; +} + +void mpc2000xl_all_t::midi_input_t::_read() { + m_receive_channel = static_cast(m__io->read_u1()); + m_sustain_pedal_to_duration = m__io->read_bits_int_le(1); + m__unnamed2 = m__io->read_bits_int_le(7); + m_filter_enabled = m__io->read_bits_int_le(1); + m__unnamed4 = m__io->read_bits_int_le(7); + m_filter_type = static_cast(m__io->read_u1()); + m_multi_rec_enabled = m__io->read_bits_int_le(1); + m__unnamed7 = m__io->read_bits_int_le(7); + m_multi_rec_destination_tracks = std::unique_ptr>(new std::vector()); + const int l_multi_rec_destination_tracks = 34; + for (int i = 0; i < l_multi_rec_destination_tracks; i++) { + m_multi_rec_destination_tracks->push_back(std::move(m__io->read_u1())); + } + m_note_pass_enabled = m__io->read_bits_int_le(1); + m__unnamed10 = m__io->read_bits_int_le(7); + m_pitch_bend_pass_enabled = m__io->read_bits_int_le(1); + m__unnamed12 = m__io->read_bits_int_le(7); + m_pgm_change_pass_enabled = m__io->read_bits_int_le(1); + m__unnamed14 = m__io->read_bits_int_le(7); + m_ch_pressure_pass_enabled = m__io->read_bits_int_le(1); + m__unnamed16 = m__io->read_bits_int_le(7); + m_poly_pressure_pass_enabled = m__io->read_bits_int_le(1); + m__unnamed18 = m__io->read_bits_int_le(7); + m_exclusive_pass_enabled = m__io->read_bits_int_le(1); + m__unnamed20 = m__io->read_bits_int_le(7); + m_cc_pass_enabled = std::unique_ptr>(new std::vector()); + const int l_cc_pass_enabled = 128; + for (int i = 0; i < l_cc_pass_enabled; i++) { + m_cc_pass_enabled->push_back(std::move(m__io->read_bits_int_le(1))); + } + m__dirty = false; +} + +void mpc2000xl_all_t::midi_input_t::_fetch_instances() { + for (std::size_t i = 0; i < m_multi_rec_destination_tracks->size(); ++i) { + } + for (std::size_t i = 0; i < m_cc_pass_enabled->size(); ++i) { + } +} + +void mpc2000xl_all_t::midi_input_t::_write() { + m__io->write_u1(static_cast(m_receive_channel)); + m__io->write_bits_int_le(1, ((m_sustain_pedal_to_duration) ? 1 : 0)); + m__io->write_bits_int_le(7, m__unnamed2); + m__io->write_bits_int_le(1, ((m_filter_enabled) ? 1 : 0)); + m__io->write_bits_int_le(7, m__unnamed4); + m__io->write_u1(static_cast(m_filter_type)); + m__io->write_bits_int_le(1, ((m_multi_rec_enabled) ? 1 : 0)); + m__io->write_bits_int_le(7, m__unnamed7); + if (m_multi_rec_destination_tracks == nullptr) { + throw std::runtime_error("/types/midi_input/seq/8: repeated field is not set"); + } + for (std::vector::const_iterator it = m_multi_rec_destination_tracks->begin(); it != m_multi_rec_destination_tracks->end(); ++it) { + m__io->write_u1((*it)); + } + m__io->write_bits_int_le(1, ((m_note_pass_enabled) ? 1 : 0)); + m__io->write_bits_int_le(7, m__unnamed10); + m__io->write_bits_int_le(1, ((m_pitch_bend_pass_enabled) ? 1 : 0)); + m__io->write_bits_int_le(7, m__unnamed12); + m__io->write_bits_int_le(1, ((m_pgm_change_pass_enabled) ? 1 : 0)); + m__io->write_bits_int_le(7, m__unnamed14); + m__io->write_bits_int_le(1, ((m_ch_pressure_pass_enabled) ? 1 : 0)); + m__io->write_bits_int_le(7, m__unnamed16); + m__io->write_bits_int_le(1, ((m_poly_pressure_pass_enabled) ? 1 : 0)); + m__io->write_bits_int_le(7, m__unnamed18); + m__io->write_bits_int_le(1, ((m_exclusive_pass_enabled) ? 1 : 0)); + m__io->write_bits_int_le(7, m__unnamed20); + if (m_cc_pass_enabled == nullptr) { + throw std::runtime_error("/types/midi_input/seq/21: repeated field is not set"); + } + for (std::vector::const_iterator it = m_cc_pass_enabled->begin(); it != m_cc_pass_enabled->end(); ++it) { + m__io->write_bits_int_le(1, (((*it)) ? 1 : 0)); + } + _fetch_instances(); + m__dirty = false; +} + +void mpc2000xl_all_t::midi_input_t::_check() { + if (m_multi_rec_destination_tracks == nullptr) { + throw std::runtime_error("/types/midi_input/seq/8: repeated field is not set"); + } + if (m_multi_rec_destination_tracks->size() != static_cast(34)) { + throw std::runtime_error("/types/midi_input/seq/8: repeat-expr size mismatch"); + } + for (std::vector::const_iterator it = m_multi_rec_destination_tracks->begin(); it != m_multi_rec_destination_tracks->end(); ++it) { + } + if (m_cc_pass_enabled == nullptr) { + throw std::runtime_error("/types/midi_input/seq/21: repeated field is not set"); + } + if (m_cc_pass_enabled->size() != static_cast(128)) { + throw std::runtime_error("/types/midi_input/seq/21: repeat-expr size mismatch"); + } + for (std::vector::const_iterator it = m_cc_pass_enabled->begin(); it != m_cc_pass_enabled->end(); ++it) { + } + m__dirty = false; +} + +mpc2000xl_all_t::midi_input_t::~midi_input_t() {} + +mpc2000xl_all_t::midi_output_t::midi_output_t(kaitai::kstream* p__io, mpc2000xl_all_t* p__parent, mpc2000xl_all_t* p__root) : kaitai::kstruct(p__io) { + m__parent = p__parent; + m__root = p__root; + m__dirty = false; +} + +void mpc2000xl_all_t::midi_output_t::_read() { + m_soft_thru_mode = static_cast(m__io->read_u1()); + m__dirty = false; +} + +void mpc2000xl_all_t::midi_output_t::_fetch_instances() { +} + +void mpc2000xl_all_t::midi_output_t::_write() { + m__io->write_u1(static_cast(m_soft_thru_mode)); + _fetch_instances(); + m__dirty = false; +} + +void mpc2000xl_all_t::midi_output_t::_check() { + m__dirty = false; +} + +mpc2000xl_all_t::midi_output_t::~midi_output_t() {} + +mpc2000xl_all_t::midi_switch_t::midi_switch_t(kaitai::kstream* p__io, mpc2000xl_all_t::misc_t* p__parent, mpc2000xl_all_t* p__root) : kaitai::kstruct(p__io) { + m__parent = p__parent; + m__root = p__root; + m__dirty = false; +} + +void mpc2000xl_all_t::midi_switch_t::_read() { + m_controller = m__io->read_u1(); + m_function = static_cast(m__io->read_u1()); + m__dirty = false; +} + +void mpc2000xl_all_t::midi_switch_t::_fetch_instances() { +} + +void mpc2000xl_all_t::midi_switch_t::_write() { + m__io->write_u1(m_controller); + m__io->write_u1(static_cast(m_function)); + _fetch_instances(); + m__dirty = false; +} + +void mpc2000xl_all_t::midi_switch_t::_check() { + m__dirty = false; +} + +mpc2000xl_all_t::midi_switch_t::~midi_switch_t() {} + +mpc2000xl_all_t::midi_sync_t::midi_sync_t(kaitai::kstream* p__io, mpc2000xl_all_t* p__parent, mpc2000xl_all_t* p__root) : kaitai::kstruct(p__io) { + m__parent = p__parent; + m__root = p__root; + m__dirty = false; +} + +void mpc2000xl_all_t::midi_sync_t::_read() { + m_in_mode = static_cast(m__io->read_u1()); + m_out_mode = static_cast(m__io->read_u1()); + m_shift_early = m__io->read_u1(); + if (!(m_shift_early >= 0)) { + throw kaitai::validation_less_than_error(0, m_shift_early, m__io, std::string("/types/midi_sync/seq/2")); + } + if (!(m_shift_early <= 20)) { + throw kaitai::validation_greater_than_error(20, m_shift_early, m__io, std::string("/types/midi_sync/seq/2")); + } + m_send_mmc_enabled = m__io->read_bits_int_le(1); + m__unnamed4 = m__io->read_bits_int_le(7); + m_frame_rate = static_cast(m__io->read_u1()); + m_input = m__io->read_u1(); + m_output = static_cast(m__io->read_u1()); + m__dirty = false; +} + +void mpc2000xl_all_t::midi_sync_t::_fetch_instances() { +} + +void mpc2000xl_all_t::midi_sync_t::_write() { + m__io->write_u1(static_cast(m_in_mode)); + m__io->write_u1(static_cast(m_out_mode)); + m__io->write_u1(m_shift_early); + m__io->write_bits_int_le(1, ((m_send_mmc_enabled) ? 1 : 0)); + m__io->write_bits_int_le(7, m__unnamed4); + m__io->write_u1(static_cast(m_frame_rate)); + m__io->write_u1(m_input); + m__io->write_u1(static_cast(m_output)); + _fetch_instances(); + m__dirty = false; +} + +void mpc2000xl_all_t::midi_sync_t::_check() { + if (!(m_shift_early >= 0)) { + throw kaitai::validation_less_than_error(0, m_shift_early, m__io, std::string("/types/midi_sync/seq/2")); + } + if (!(m_shift_early <= 20)) { + throw kaitai::validation_greater_than_error(20, m_shift_early, m__io, std::string("/types/midi_sync/seq/2")); + } + m__dirty = false; +} + +mpc2000xl_all_t::midi_sync_t::~midi_sync_t() {} + +mpc2000xl_all_t::misc_t::misc_t(kaitai::kstream* p__io, mpc2000xl_all_t* p__parent, mpc2000xl_all_t* p__root) : kaitai::kstruct(p__io) { + m__parent = p__parent; + m__root = p__root; + m__dirty = false; +} + +void mpc2000xl_all_t::misc_t::_read() { + m_tap_averaging = static_cast(m__io->read_u1()); + m_midi_sync_in_receive_mmc_enabled = m__io->read_bits_int_le(1); + m_midi_switch = std::unique_ptr>>(new std::vector>()); + const int l_midi_switch = 4; + for (int i = 0; i < l_midi_switch; i++) { + std::unique_ptr _t_midi_switch = std::unique_ptr(new midi_switch_t(m__io, this, m__root)); + try { + _t_midi_switch->_read(); + } catch(...) { + m_midi_switch->push_back(std::move(_t_midi_switch)); + throw; + } + m_midi_switch->push_back(std::move(_t_midi_switch)); + } + m__dirty = false; +} + +void mpc2000xl_all_t::misc_t::_fetch_instances() { + for (std::size_t i = 0; i < m_midi_switch->size(); ++i) { + m_midi_switch->at(i).get()->_fetch_instances(); + } +} + +void mpc2000xl_all_t::misc_t::_write() { + m__io->write_u1(static_cast(m_tap_averaging)); + m__io->write_bits_int_le(1, ((m_midi_sync_in_receive_mmc_enabled) ? 1 : 0)); + if (m_midi_switch == nullptr) { + throw std::runtime_error("/types/misc/seq/2: repeated field is not set"); + } + for (std::vector>::const_iterator it = m_midi_switch->begin(); it != m_midi_switch->end(); ++it) { + if ((*it).get() == nullptr) { + throw std::runtime_error("/types/misc/seq/2: nested object is not set"); + } + (*it).get()->_set_io(m__io); + (*it).get()->_write(); + } + _fetch_instances(); + m__dirty = false; +} + +void mpc2000xl_all_t::misc_t::_check() { + if (m_midi_switch == nullptr) { + throw std::runtime_error("/types/misc/seq/2: repeated field is not set"); + } + if (m_midi_switch->size() != static_cast(4)) { + throw std::runtime_error("/types/misc/seq/2: repeat-expr size mismatch"); + } + for (std::vector>::const_iterator it = m_midi_switch->begin(); it != m_midi_switch->end(); ++it) { + if ((*it).get() == nullptr) { + throw std::runtime_error("/types/misc/seq/2: nested object is not set"); + } + (*it).get()->_set_io(m__io); + (*it).get()->_check(); + } + m__dirty = false; +} + +mpc2000xl_all_t::misc_t::~misc_t() {} + +mpc2000xl_all_t::mixer_event_t::mixer_event_t(kaitai::kstream* p__io, mpc2000xl_all_t::exclusive_event_t* p__parent, mpc2000xl_all_t* p__root) : kaitai::kstruct(p__io) { + m__parent = p__parent; + m__root = p__root; + m__dirty = false; +} + +void mpc2000xl_all_t::mixer_event_t::_read() { + m__unnamed0 = m__io->read_bytes(3); + m_param = static_cast(m__io->read_u1()); + m_pad_index = m__io->read_u1(); + m_value = m__io->read_u1(); + if (!(m_value >= 0)) { + throw kaitai::validation_less_than_error(0, m_value, m__io, std::string("/types/mixer_event/seq/3")); + } + if (!(m_value <= 100)) { + throw kaitai::validation_greater_than_error(100, m_value, m__io, std::string("/types/mixer_event/seq/3")); + } + m__unnamed4 = m__io->read_bytes(2); + m__dirty = false; +} + +void mpc2000xl_all_t::mixer_event_t::_fetch_instances() { +} + +void mpc2000xl_all_t::mixer_event_t::_write() { + m__io->write_bytes(m__unnamed0); + m__io->write_u1(static_cast(m_param)); + m__io->write_u1(m_pad_index); + m__io->write_u1(m_value); + m__io->write_bytes(m__unnamed4); + _fetch_instances(); + m__dirty = false; +} + +void mpc2000xl_all_t::mixer_event_t::_check() { + if (m__unnamed0.size() != static_cast(3)) { + throw std::runtime_error("/types/mixer_event/seq/0: size mismatch"); + } + if (!(m_value >= 0)) { + throw kaitai::validation_less_than_error(0, m_value, m__io, std::string("/types/mixer_event/seq/3")); + } + if (!(m_value <= 100)) { + throw kaitai::validation_greater_than_error(100, m_value, m__io, std::string("/types/mixer_event/seq/3")); + } + if (m__unnamed4.size() != static_cast(2)) { + throw std::runtime_error("/types/mixer_event/seq/4: size mismatch"); + } + m__dirty = false; +} + +mpc2000xl_all_t::mixer_event_t::~mixer_event_t() {} +const std::set mpc2000xl_all_t::note_event_t::_values_note_variation_type_t{ + mpc2000xl_all_t::note_event_t::NOTE_VARIATION_TYPE_TUNE, + mpc2000xl_all_t::note_event_t::NOTE_VARIATION_TYPE_DECAY, + mpc2000xl_all_t::note_event_t::NOTE_VARIATION_TYPE_ATTACK, + mpc2000xl_all_t::note_event_t::NOTE_VARIATION_TYPE_FILTER, +}; +bool mpc2000xl_all_t::note_event_t::_is_defined_note_variation_type_t(mpc2000xl_all_t::note_event_t::note_variation_type_t v) { + return mpc2000xl_all_t::note_event_t::_values_note_variation_type_t.find(v) != mpc2000xl_all_t::note_event_t::_values_note_variation_type_t.end(); +} + +mpc2000xl_all_t::note_event_t::note_event_t(kaitai::kstream* p__io, mpc2000xl_all_t::event_t* p__parent, mpc2000xl_all_t* p__root) : kaitai::kstruct(p__io) { + m__parent = p__parent; + m__root = p__root; + m__dirty = false; + f_duration = false; + f_note = false; + f_variation_type = false; +} + +void mpc2000xl_all_t::note_event_t::_read() { + m_duration_bits_3 = m__io->read_u1(); + m_velocity = m__io->read_bits_int_le(7); + m_variation_type_bit_1 = m__io->read_bits_int_le(1); + m_variation_value = m__io->read_bits_int_le(7); + m_variation_type_bit_2 = m__io->read_bits_int_le(1); + m__dirty = false; +} + +void mpc2000xl_all_t::note_event_t::_fetch_instances() { +} + +void mpc2000xl_all_t::note_event_t::_write() { + m__io->write_u1(m_duration_bits_3); + m__io->write_bits_int_le(7, m_velocity); + m__io->write_bits_int_le(1, ((m_variation_type_bit_1) ? 1 : 0)); + m__io->write_bits_int_le(7, m_variation_value); + m__io->write_bits_int_le(1, ((m_variation_type_bit_2) ? 1 : 0)); + _fetch_instances(); + m__dirty = false; +} + +void mpc2000xl_all_t::note_event_t::_check() { + m__dirty = false; +} + +mpc2000xl_all_t::note_event_t::~note_event_t() {} + +int32_t mpc2000xl_all_t::note_event_t::duration() { + if (f_duration) + return m_duration; + f_duration = true; + m_duration = ((static_cast(_parent())->duration_bits_1() << 10) + (static_cast(_parent())->duration_bits_2() << 8)) + duration_bits_3(); + return m_duration; +} + +uint8_t mpc2000xl_all_t::note_event_t::note() { + if (f_note) + return m_note; + f_note = true; + m_note = static_cast(_parent())->id(); + return m_note; +} + +mpc2000xl_all_t::note_event_t::note_variation_type_t mpc2000xl_all_t::note_event_t::variation_type() { + if (f_variation_type) + return m_variation_type; + f_variation_type = true; + m_variation_type = static_cast(static_cast(variation_type_bit_1()) << 1 | static_cast(variation_type_bit_2())); + return m_variation_type; +} + +mpc2000xl_all_t::pitch_bend_event_t::pitch_bend_event_t(kaitai::kstream* p__io, mpc2000xl_all_t::event_t* p__parent, mpc2000xl_all_t* p__root) : kaitai::kstruct(p__io) { + m__parent = p__parent; + m__root = p__root; + m__dirty = false; + f_corrected_amount = false; +} + +void mpc2000xl_all_t::pitch_bend_event_t::_read() { + m_amount_bits_1 = m__io->read_bits_int_le(8); + m_amount_bits_2 = m__io->read_bits_int_le(8); + m__unnamed2 = m__io->read_bytes(1); + m__dirty = false; +} + +void mpc2000xl_all_t::pitch_bend_event_t::_fetch_instances() { +} + +void mpc2000xl_all_t::pitch_bend_event_t::_write() { + m__io->write_bits_int_le(8, m_amount_bits_1); + m__io->write_bits_int_le(8, m_amount_bits_2); + m__io->write_bytes(m__unnamed2); + _fetch_instances(); + m__dirty = false; +} + +void mpc2000xl_all_t::pitch_bend_event_t::_check() { + if (m__unnamed2.size() != static_cast(1)) { + throw std::runtime_error("/types/pitch_bend_event/seq/2: size mismatch"); + } + m__dirty = false; +} + +mpc2000xl_all_t::pitch_bend_event_t::~pitch_bend_event_t() {} + +int32_t mpc2000xl_all_t::pitch_bend_event_t::corrected_amount() { + if (f_corrected_amount) + return m_corrected_amount; + f_corrected_amount = true; + m_corrected_amount = (amount_bits_1() + (amount_bits_2() << 7)) - 8192; + return m_corrected_amount; +} + +mpc2000xl_all_t::poly_pressure_event_t::poly_pressure_event_t(kaitai::kstream* p__io, mpc2000xl_all_t::event_t* p__parent, mpc2000xl_all_t* p__root) : kaitai::kstruct(p__io) { + m__parent = p__parent; + m__root = p__root; + m__dirty = false; +} + +void mpc2000xl_all_t::poly_pressure_event_t::_read() { + m_note = m__io->read_u1(); + if (!(m_note >= 0)) { + throw kaitai::validation_less_than_error(0, m_note, m__io, std::string("/types/poly_pressure_event/seq/0")); + } + if (!(m_note <= 127)) { + throw kaitai::validation_greater_than_error(127, m_note, m__io, std::string("/types/poly_pressure_event/seq/0")); + } + m_pressure = m__io->read_u1(); + if (!(m_pressure >= 0)) { + throw kaitai::validation_less_than_error(0, m_pressure, m__io, std::string("/types/poly_pressure_event/seq/1")); + } + if (!(m_pressure <= 127)) { + throw kaitai::validation_greater_than_error(127, m_pressure, m__io, std::string("/types/poly_pressure_event/seq/1")); + } + m__unnamed2 = m__io->read_bytes(1); + m__dirty = false; +} + +void mpc2000xl_all_t::poly_pressure_event_t::_fetch_instances() { +} + +void mpc2000xl_all_t::poly_pressure_event_t::_write() { + m__io->write_u1(m_note); + m__io->write_u1(m_pressure); + m__io->write_bytes(m__unnamed2); + _fetch_instances(); + m__dirty = false; +} + +void mpc2000xl_all_t::poly_pressure_event_t::_check() { + if (!(m_note >= 0)) { + throw kaitai::validation_less_than_error(0, m_note, m__io, std::string("/types/poly_pressure_event/seq/0")); + } + if (!(m_note <= 127)) { + throw kaitai::validation_greater_than_error(127, m_note, m__io, std::string("/types/poly_pressure_event/seq/0")); + } + if (!(m_pressure >= 0)) { + throw kaitai::validation_less_than_error(0, m_pressure, m__io, std::string("/types/poly_pressure_event/seq/1")); + } + if (!(m_pressure <= 127)) { + throw kaitai::validation_greater_than_error(127, m_pressure, m__io, std::string("/types/poly_pressure_event/seq/1")); + } + if (m__unnamed2.size() != static_cast(1)) { + throw std::runtime_error("/types/poly_pressure_event/seq/2: size mismatch"); + } + m__dirty = false; +} + +mpc2000xl_all_t::poly_pressure_event_t::~poly_pressure_event_t() {} + +mpc2000xl_all_t::program_change_event_t::program_change_event_t(kaitai::kstream* p__io, mpc2000xl_all_t::event_t* p__parent, mpc2000xl_all_t* p__root) : kaitai::kstruct(p__io) { + m__parent = p__parent; + m__root = p__root; + m__dirty = false; +} + +void mpc2000xl_all_t::program_change_event_t::_read() { + m_program = m__io->read_u1(); + if (!(m_program >= 0)) { + throw kaitai::validation_less_than_error(0, m_program, m__io, std::string("/types/program_change_event/seq/0")); + } + if (!(m_program <= 127)) { + throw kaitai::validation_greater_than_error(127, m_program, m__io, std::string("/types/program_change_event/seq/0")); + } + m__unnamed1 = m__io->read_bytes(2); + m__dirty = false; +} + +void mpc2000xl_all_t::program_change_event_t::_fetch_instances() { +} + +void mpc2000xl_all_t::program_change_event_t::_write() { + m__io->write_u1(m_program); + m__io->write_bytes(m__unnamed1); + _fetch_instances(); + m__dirty = false; +} + +void mpc2000xl_all_t::program_change_event_t::_check() { + if (!(m_program >= 0)) { + throw kaitai::validation_less_than_error(0, m_program, m__io, std::string("/types/program_change_event/seq/0")); + } + if (!(m_program <= 127)) { + throw kaitai::validation_greater_than_error(127, m_program, m__io, std::string("/types/program_change_event/seq/0")); + } + if (m__unnamed1.size() != static_cast(2)) { + throw std::runtime_error("/types/program_change_event/seq/1: size mismatch"); + } + m__dirty = false; +} + +mpc2000xl_all_t::program_change_event_t::~program_change_event_t() {} + +mpc2000xl_all_t::sequence_t::sequence_t(kaitai::kstream* p__io, mpc2000xl_all_t* p__parent, mpc2000xl_all_t* p__root) : kaitai::kstruct(p__io) { + m__parent = p__parent; + m__root = p__root; + m__dirty = false; + n_name_part_2 = true; + f_name = false; +} + +void mpc2000xl_all_t::sequence_t::_read() { + m_name_part_1 = kaitai::kstream::bytes_to_str(kaitai::kstream::bytes_terminate(kaitai::kstream::bytes_strip_right(m__io->read_bytes(8), static_cast(255)), static_cast(255), false), "ASCII"); + n_name_part_2 = true; + if (name_part_1() != std::string("")) { + n_name_part_2 = false; + m_name_part_2 = kaitai::kstream::bytes_to_str(kaitai::kstream::bytes_terminate(kaitai::kstream::bytes_strip_right(m__io->read_bytes(8), static_cast(255)), static_cast(255), false), "ASCII"); + } + if (name_part_1() != std::string("")) { + m_body = std::unique_ptr(new sequence_body_t(m__io, this, m__root)); + m_body->_read(); + } + m__dirty = false; +} + +void mpc2000xl_all_t::sequence_t::_fetch_instances() { + n_name_part_2 = true; + if (name_part_1() != std::string("")) { + n_name_part_2 = false; + } + if (name_part_1() != std::string("")) { + m_body.get()->_fetch_instances(); + } +} + +void mpc2000xl_all_t::sequence_t::_write() { + std::string _bufm_name_part_1 = m_name_part_1; + if (_bufm_name_part_1.size() < static_cast(8)) { + _bufm_name_part_1 += std::string(1, static_cast(255)); + } + if (_bufm_name_part_1.size() < static_cast(8)) { + _bufm_name_part_1.append(static_cast(8) - _bufm_name_part_1.size(), static_cast(255)); + } + m__io->write_bytes(_bufm_name_part_1); + if (name_part_1() != std::string("")) { + std::string _bufm_name_part_2 = m_name_part_2; + if (_bufm_name_part_2.size() < static_cast(8)) { + _bufm_name_part_2 += std::string(1, static_cast(255)); + } + if (_bufm_name_part_2.size() < static_cast(8)) { + _bufm_name_part_2.append(static_cast(8) - _bufm_name_part_2.size(), static_cast(255)); + } + m__io->write_bytes(_bufm_name_part_2); + } + if (name_part_1() != std::string("")) { + if (m_body.get() == nullptr) { + throw std::runtime_error("/types/sequence/seq/2: nested object is not set"); + } + m_body.get()->_set_io(m__io); + m_body.get()->_write(); + } + _fetch_instances(); + m__dirty = false; +} + +void mpc2000xl_all_t::sequence_t::_check() { + if (m_name_part_1.size() > static_cast(8)) { + throw std::runtime_error("/types/sequence/seq/0: size mismatch"); + } + if (m_name_part_1.find(static_cast(255)) != std::string::npos) { + throw std::runtime_error("/types/sequence/seq/0: terminator must not appear in value"); + } + if (name_part_1() != std::string("")) { + if (!(!n_name_part_2)) { + throw std::runtime_error("/types/sequence/seq/1: conditional field is not set"); + } + if (m_name_part_2.size() > static_cast(8)) { + throw std::runtime_error("/types/sequence/seq/1: size mismatch"); + } + if (m_name_part_2.find(static_cast(255)) != std::string::npos) { + throw std::runtime_error("/types/sequence/seq/1: terminator must not appear in value"); + } + } else { + if (!n_name_part_2) { + throw std::runtime_error("/types/sequence/seq/1: conditional field should be absent"); + } + } + if (name_part_1() != std::string("")) { + if (!(m_body.get() != nullptr)) { + throw std::runtime_error("/types/sequence/seq/2: conditional field is not set"); + } + if (m_body.get() == nullptr) { + throw std::runtime_error("/types/sequence/seq/2: nested object is not set"); + } + m_body.get()->_set_io(m__io); + m_body.get()->_check(); + } else { + if (m_body.get() != nullptr) { + throw std::runtime_error("/types/sequence/seq/2: conditional field should be absent"); + } + } + m__dirty = false; +} + +mpc2000xl_all_t::sequence_t::~sequence_t() {} + +std::string mpc2000xl_all_t::sequence_t::name() { + if (f_name) + return m_name; + f_name = true; + n_name = true; + if ( ((name_part_1() != std::string("")) && (name_part_2() != std::string(""))) ) { + n_name = false; + m_name = name_part_1() + name_part_2(); + } + return m_name; +} + +mpc2000xl_all_t::sequence_body_t::sequence_body_t(kaitai::kstream* p__io, mpc2000xl_all_t::sequence_t* p__parent, mpc2000xl_all_t* p__root) : kaitai::kstruct(p__io) { + m__parent = p__parent; + m__root = p__root; + m__dirty = false; +} + +void mpc2000xl_all_t::sequence_body_t::_read() { + m_is_used = static_cast(m__io->read_u2le()); + m_index = m__io->read_u1(); + m__unnamed2 = m__io->read_bytes(7); + m_bar_count = m__io->read_u2le(); + m_last_tick = m__io->read_u4le(); + m__unnamed5 = m__io->read_bytes(16); + m_loop_start_bar_index = m__io->read_u2le(); + m_loop_end_bar_index = m__io->read_u2le(); + m_loop_enabled = m__io->read_bits_int_le(1); + m__unnamed9 = m__io->read_bits_int_le(7); + m_start_time = std::unique_ptr(new start_time_t(m__io, this, m__root)); + m_start_time->_read(); + m__unnamed11 = m__io->read_bytes(6); + m_last_tick2 = m__io->read_u4le(); + m__unnamed13 = m__io->read_bytes(52); + m_device_names = std::unique_ptr>(new std::vector()); + const int l_device_names = 33; + for (int i = 0; i < l_device_names; i++) { + m_device_names->push_back(std::move(kaitai::kstream::bytes_to_str(m__io->read_bytes(8), "ASCII"))); + } + m_tracks = std::unique_ptr(new tracks_t(m__io, this, m__root)); + m_tracks->_read(); + m__unnamed16 = m__io->read_bytes(3587); + m_bars = std::unique_ptr>>(new std::vector>()); + const int l_bars = bar_count(); + for (int i = 0; i < l_bars; i++) { + std::unique_ptr _t_bars = std::unique_ptr(new bar_t(i, m__io, this, m__root)); + try { + _t_bars->_read(); + } catch(...) { + m_bars->push_back(std::move(_t_bars)); + throw; + } + m_bars->push_back(std::move(_t_bars)); + } + m__unnamed18 = m__io->read_bytes(4 * (999 - bar_count())); + m__unnamed19 = m__io->read_bytes(865); + m_events = std::unique_ptr>>(new std::vector>()); + { + int i = 0; + event_t* _; + do { + std::unique_ptr _t_events = std::unique_ptr(new event_t(m__io, this, m__root)); + try { + _t_events->_read(); + } catch(...) { + _ = _t_events.get(); + m_events->push_back(std::move(_t_events)); + throw; + } + _ = _t_events.get(); + m_events->push_back(std::move(_t_events)); + i++; + } while (!(_->tick() == 1048575)); + } + m__dirty = false; +} + +void mpc2000xl_all_t::sequence_body_t::_fetch_instances() { + m_start_time.get()->_fetch_instances(); + for (std::size_t i = 0; i < m_device_names->size(); ++i) { + } + m_tracks.get()->_fetch_instances(); + for (std::size_t i = 0; i < m_bars->size(); ++i) { + m_bars->at(i).get()->_fetch_instances(); + } + for (std::size_t i = 0; i < m_events->size(); ++i) { + m_events->at(i).get()->_fetch_instances(); + } +} + +void mpc2000xl_all_t::sequence_body_t::_write() { + m__io->write_u2le(static_cast(m_is_used)); + m__io->write_u1(m_index); + m__io->write_bytes(m__unnamed2); + m__io->write_u2le(m_bar_count); + m__io->write_u4le(m_last_tick); + m__io->write_bytes(m__unnamed5); + m__io->write_u2le(m_loop_start_bar_index); + m__io->write_u2le(m_loop_end_bar_index); + m__io->write_bits_int_le(1, ((m_loop_enabled) ? 1 : 0)); + m__io->write_bits_int_le(7, m__unnamed9); + if (m_start_time.get() == nullptr) { + throw std::runtime_error("/types/sequence_body/seq/10: nested object is not set"); + } + m_start_time.get()->_set_io(m__io); + m_start_time.get()->_write(); + m__io->write_bytes(m__unnamed11); + m__io->write_u4le(m_last_tick2); + m__io->write_bytes(m__unnamed13); + if (m_device_names == nullptr) { + throw std::runtime_error("/types/sequence_body/seq/14: repeated field is not set"); + } + for (std::vector::const_iterator it = m_device_names->begin(); it != m_device_names->end(); ++it) { + m__io->write_bytes((*it)); + } + if (m_tracks.get() == nullptr) { + throw std::runtime_error("/types/sequence_body/seq/15: nested object is not set"); + } + m_tracks.get()->_set_io(m__io); + m_tracks.get()->_write(); + m__io->write_bytes(m__unnamed16); + if (m_bars == nullptr) { + throw std::runtime_error("/types/sequence_body/seq/17: repeated field is not set"); + } + for (std::vector>::const_iterator it = m_bars->begin(); it != m_bars->end(); ++it) { + if ((*it).get() == nullptr) { + throw std::runtime_error("/types/sequence_body/seq/17: nested object is not set"); + } + (*it).get()->_set_io(m__io); + (*it).get()->_write(); + } + m__io->write_bytes(m__unnamed18); + m__io->write_bytes(m__unnamed19); + if (m_events == nullptr) { + throw std::runtime_error("/types/sequence_body/seq/20: repeated field is not set"); + } + for (std::vector>::const_iterator it = m_events->begin(); it != m_events->end(); ++it) { + if ((*it).get() == nullptr) { + throw std::runtime_error("/types/sequence_body/seq/20: nested object is not set"); + } + (*it).get()->_set_io(m__io); + (*it).get()->_write(); + } + _fetch_instances(); + m__dirty = false; +} + +void mpc2000xl_all_t::sequence_body_t::_check() { + if (m__unnamed2.size() != static_cast(7)) { + throw std::runtime_error("/types/sequence_body/seq/2: size mismatch"); + } + if (m__unnamed5.size() != static_cast(16)) { + throw std::runtime_error("/types/sequence_body/seq/5: size mismatch"); + } + if (m_start_time.get() == nullptr) { + throw std::runtime_error("/types/sequence_body/seq/10: nested object is not set"); + } + m_start_time.get()->_set_io(m__io); + m_start_time.get()->_check(); + if (m__unnamed11.size() != static_cast(6)) { + throw std::runtime_error("/types/sequence_body/seq/11: size mismatch"); + } + if (m__unnamed13.size() != static_cast(52)) { + throw std::runtime_error("/types/sequence_body/seq/13: size mismatch"); + } + if (m_device_names == nullptr) { + throw std::runtime_error("/types/sequence_body/seq/14: repeated field is not set"); + } + if (m_device_names->size() != static_cast(33)) { + throw std::runtime_error("/types/sequence_body/seq/14: repeat-expr size mismatch"); + } + for (std::vector::const_iterator it = m_device_names->begin(); it != m_device_names->end(); ++it) { + if ((*it).size() != static_cast(8)) { + throw std::runtime_error("/types/sequence_body/seq/14: size mismatch"); + } + } + if (m_tracks.get() == nullptr) { + throw std::runtime_error("/types/sequence_body/seq/15: nested object is not set"); + } + m_tracks.get()->_set_io(m__io); + m_tracks.get()->_check(); + if (m__unnamed16.size() != static_cast(3587)) { + throw std::runtime_error("/types/sequence_body/seq/16: size mismatch"); + } + if (m_bars == nullptr) { + throw std::runtime_error("/types/sequence_body/seq/17: repeated field is not set"); + } + if (m_bars->size() != static_cast(bar_count())) { + throw std::runtime_error("/types/sequence_body/seq/17: repeat-expr size mismatch"); + } + for (std::vector>::const_iterator it = m_bars->begin(); it != m_bars->end(); ++it) { + if ((*it).get() == nullptr) { + throw std::runtime_error("/types/sequence_body/seq/17: nested object is not set"); + } + (*it).get()->_set_io(m__io); + (*it).get()->_check(); + } + if (m__unnamed18.size() != static_cast(4 * (999 - bar_count()))) { + throw std::runtime_error("/types/sequence_body/seq/18: size mismatch"); + } + if (m__unnamed19.size() != static_cast(865)) { + throw std::runtime_error("/types/sequence_body/seq/19: size mismatch"); + } + if (m_events == nullptr) { + throw std::runtime_error("/types/sequence_body/seq/20: repeated field is not set"); + } + if (m_events->empty()) { + throw std::runtime_error("/types/sequence_body/seq/20: repeat-until field must not be empty"); + } + for (std::vector>::const_iterator it = m_events->begin(); it != m_events->end(); ++it) { + const std::size_t i = static_cast(it - m_events->begin()); + const event_t* _ = (*it).get(); + if ((*it).get() == nullptr) { + throw std::runtime_error("/types/sequence_body/seq/20: nested object is not set"); + } + (*it).get()->_set_io(m__io); + (*it).get()->_check(); + const bool _is_last = (i == m_events->size() - 1); + if ((_->tick() == 1048575) != _is_last) { + throw std::runtime_error("/types/sequence_body/seq/20: repeat-until condition mismatch"); + } + } + m__dirty = false; +} + +mpc2000xl_all_t::sequence_body_t::~sequence_body_t() {} + +mpc2000xl_all_t::sequence_body_t::start_time_t::start_time_t(kaitai::kstream* p__io, mpc2000xl_all_t::sequence_body_t* p__parent, mpc2000xl_all_t* p__root) : kaitai::kstruct(p__io) { + m__parent = p__parent; + m__root = p__root; + m__dirty = false; +} + +void mpc2000xl_all_t::sequence_body_t::start_time_t::_read() { + m_hours = m__io->read_u1(); + m_minutes = m__io->read_u1(); + m_seconds = m__io->read_u1(); + m_frames = m__io->read_u1(); + m_frame_decimals = m__io->read_u1(); + m__dirty = false; +} + +void mpc2000xl_all_t::sequence_body_t::start_time_t::_fetch_instances() { +} + +void mpc2000xl_all_t::sequence_body_t::start_time_t::_write() { + m__io->write_u1(m_hours); + m__io->write_u1(m_minutes); + m__io->write_u1(m_seconds); + m__io->write_u1(m_frames); + m__io->write_u1(m_frame_decimals); + _fetch_instances(); + m__dirty = false; +} + +void mpc2000xl_all_t::sequence_body_t::start_time_t::_check() { + m__dirty = false; +} + +mpc2000xl_all_t::sequence_body_t::start_time_t::~start_time_t() {} + +mpc2000xl_all_t::sequence_meta_t::sequence_meta_t(kaitai::kstream* p__io, mpc2000xl_all_t* p__parent, mpc2000xl_all_t* p__root) : kaitai::kstruct(p__io) { + m__parent = p__parent; + m__root = p__root; + m__dirty = false; + f_is_used = false; +} + +void mpc2000xl_all_t::sequence_meta_t::_read() { + m_name = kaitai::kstream::bytes_to_str(m__io->read_bytes(16), "ASCII"); + m_last_event_index = m__io->read_u2le(); + m__dirty = false; +} + +void mpc2000xl_all_t::sequence_meta_t::_fetch_instances() { +} + +void mpc2000xl_all_t::sequence_meta_t::_write() { + m__io->write_bytes(m_name); + m__io->write_u2le(m_last_event_index); + _fetch_instances(); + m__dirty = false; +} + +void mpc2000xl_all_t::sequence_meta_t::_check() { + if (m_name.size() != static_cast(16)) { + throw std::runtime_error("/types/sequence_meta/seq/0: size mismatch"); + } + m__dirty = false; +} + +mpc2000xl_all_t::sequence_meta_t::~sequence_meta_t() {} + +bool mpc2000xl_all_t::sequence_meta_t::is_used() { + if (f_is_used) + return m_is_used; + f_is_used = true; + m_is_used = last_event_index() != 0; + return m_is_used; +} + +mpc2000xl_all_t::sequencer_t::sequencer_t(kaitai::kstream* p__io, mpc2000xl_all_t* p__parent, mpc2000xl_all_t* p__root) : kaitai::kstruct(p__io) { + m__parent = p__parent; + m__root = p__root; + m__dirty = false; +} + +void mpc2000xl_all_t::sequencer_t::_read() { + m_active_sequence = m__io->read_u1(); + m__unnamed1 = m__io->read_bytes(1); + m_active_track = m__io->read_u1(); + m__unnamed3 = m__io->read_bytes(1); + m_master_tempo = m__io->read_u2le(); + m_tempo_source_is_sequence = m__io->read_bits_int_le(1); + m__unnamed6 = m__io->read_bits_int_le(7); + m_timing_correct = static_cast(m__io->read_u1()); + m_time_display_style = static_cast(m__io->read_bits_int_le(1)); + m__unnamed9 = m__io->read_bits_int_le(7); + m_second_sequence_enabled = m__io->read_bits_int_le(1); + m__unnamed11 = m__io->read_bits_int_le(7); + m_sequence_sequence_index = m__io->read_u1(); + m__dirty = false; +} + +void mpc2000xl_all_t::sequencer_t::_fetch_instances() { +} + +void mpc2000xl_all_t::sequencer_t::_write() { + m__io->write_u1(m_active_sequence); + m__io->write_bytes(m__unnamed1); + m__io->write_u1(m_active_track); + m__io->write_bytes(m__unnamed3); + m__io->write_u2le(m_master_tempo); + m__io->write_bits_int_le(1, ((m_tempo_source_is_sequence) ? 1 : 0)); + m__io->write_bits_int_le(7, m__unnamed6); + m__io->write_u1(static_cast(m_timing_correct)); + m__io->write_bits_int_le(1, static_cast(m_time_display_style)); + m__io->write_bits_int_le(7, m__unnamed9); + m__io->write_bits_int_le(1, ((m_second_sequence_enabled) ? 1 : 0)); + m__io->write_bits_int_le(7, m__unnamed11); + m__io->write_u1(m_sequence_sequence_index); + _fetch_instances(); + m__dirty = false; +} + +void mpc2000xl_all_t::sequencer_t::_check() { + if (m__unnamed1.size() != static_cast(1)) { + throw std::runtime_error("/types/sequencer/seq/1: size mismatch"); + } + if (m__unnamed3.size() != static_cast(1)) { + throw std::runtime_error("/types/sequencer/seq/3: size mismatch"); + } + m__dirty = false; +} + +mpc2000xl_all_t::sequencer_t::~sequencer_t() {} + +mpc2000xl_all_t::song_t::song_t(kaitai::kstream* p__io, mpc2000xl_all_t* p__parent, mpc2000xl_all_t* p__root) : kaitai::kstruct(p__io) { + m__parent = p__parent; + m__root = p__root; + m__dirty = false; +} + +void mpc2000xl_all_t::song_t::_read() { + m_name = kaitai::kstream::bytes_to_str(m__io->read_bytes(16), "ASCII"); + m_steps = std::unique_ptr>>(new std::vector>()); + const int l_steps = 250; + for (int i = 0; i < l_steps; i++) { + std::unique_ptr _t_steps = std::unique_ptr(new song_step_t(m__io, this, m__root)); + try { + _t_steps->_read(); + } catch(...) { + m_steps->push_back(std::move(_t_steps)); + throw; + } + m_steps->push_back(std::move(_t_steps)); + } + m__unnamed2 = m__io->read_bytes(2); + m_is_used = m__io->read_bits_int_le(1); + m_loop_first_step = m__io->read_u1(); + m_loop_last_step = m__io->read_u1(); + m_is_loop_enabled = m__io->read_bits_int_le(1); + m__unnamed7 = m__io->read_bytes(6); + m__dirty = false; +} + +void mpc2000xl_all_t::song_t::_fetch_instances() { + for (std::size_t i = 0; i < m_steps->size(); ++i) { + m_steps->at(i).get()->_fetch_instances(); + } +} + +void mpc2000xl_all_t::song_t::_write() { + m__io->write_bytes(m_name); + if (m_steps == nullptr) { + throw std::runtime_error("/types/song/seq/1: repeated field is not set"); + } + for (std::vector>::const_iterator it = m_steps->begin(); it != m_steps->end(); ++it) { + if ((*it).get() == nullptr) { + throw std::runtime_error("/types/song/seq/1: nested object is not set"); + } + (*it).get()->_set_io(m__io); + (*it).get()->_write(); + } + m__io->write_bytes(m__unnamed2); + m__io->write_bits_int_le(1, ((m_is_used) ? 1 : 0)); + m__io->write_u1(m_loop_first_step); + m__io->write_u1(m_loop_last_step); + m__io->write_bits_int_le(1, ((m_is_loop_enabled) ? 1 : 0)); + m__io->write_bytes(m__unnamed7); + _fetch_instances(); + m__dirty = false; +} + +void mpc2000xl_all_t::song_t::_check() { + if (m_name.size() != static_cast(16)) { + throw std::runtime_error("/types/song/seq/0: size mismatch"); + } + if (m_steps == nullptr) { + throw std::runtime_error("/types/song/seq/1: repeated field is not set"); + } + if (m_steps->size() != static_cast(250)) { + throw std::runtime_error("/types/song/seq/1: repeat-expr size mismatch"); + } + for (std::vector>::const_iterator it = m_steps->begin(); it != m_steps->end(); ++it) { + if ((*it).get() == nullptr) { + throw std::runtime_error("/types/song/seq/1: nested object is not set"); + } + (*it).get()->_set_io(m__io); + (*it).get()->_check(); + } + if (m__unnamed2.size() != static_cast(2)) { + throw std::runtime_error("/types/song/seq/2: size mismatch"); + } + if (m__unnamed7.size() != static_cast(6)) { + throw std::runtime_error("/types/song/seq/7: size mismatch"); + } + m__dirty = false; +} + +mpc2000xl_all_t::song_t::~song_t() {} + +mpc2000xl_all_t::song_global_t::song_global_t(kaitai::kstream* p__io, mpc2000xl_all_t* p__parent, mpc2000xl_all_t* p__root) : kaitai::kstruct(p__io) { + m__parent = p__parent; + m__root = p__root; + m__dirty = false; +} + +void mpc2000xl_all_t::song_global_t::_read() { + m_default_song_name = kaitai::kstream::bytes_to_str(m__io->read_bytes(16), "ASCII"); + m_ignore_tempo_change_events_in_sequence = m__io->read_bits_int_le(1); + m__unnamed2 = m__io->read_bits_int_le(7); + m__dirty = false; +} + +void mpc2000xl_all_t::song_global_t::_fetch_instances() { +} + +void mpc2000xl_all_t::song_global_t::_write() { + m__io->write_bytes(m_default_song_name); + m__io->write_bits_int_le(1, ((m_ignore_tempo_change_events_in_sequence) ? 1 : 0)); + m__io->write_bits_int_le(7, m__unnamed2); + _fetch_instances(); + m__dirty = false; +} + +void mpc2000xl_all_t::song_global_t::_check() { + if (m_default_song_name.size() != static_cast(16)) { + throw std::runtime_error("/types/song_global/seq/0: size mismatch"); + } + m__dirty = false; +} + +mpc2000xl_all_t::song_global_t::~song_global_t() {} + +mpc2000xl_all_t::song_step_t::song_step_t(kaitai::kstream* p__io, mpc2000xl_all_t::song_t* p__parent, mpc2000xl_all_t* p__root) : kaitai::kstruct(p__io) { + m__parent = p__parent; + m__root = p__root; + m__dirty = false; +} + +void mpc2000xl_all_t::song_step_t::_read() { + m_sequence_index = m__io->read_u1(); + m_repeat_count = m__io->read_u1(); + m__dirty = false; +} + +void mpc2000xl_all_t::song_step_t::_fetch_instances() { +} + +void mpc2000xl_all_t::song_step_t::_write() { + m__io->write_u1(m_sequence_index); + m__io->write_u1(m_repeat_count); + _fetch_instances(); + m__dirty = false; +} + +void mpc2000xl_all_t::song_step_t::_check() { + m__dirty = false; +} + +mpc2000xl_all_t::song_step_t::~song_step_t() {} + +mpc2000xl_all_t::step_edit_options_t::step_edit_options_t(kaitai::kstream* p__io, mpc2000xl_all_t* p__parent, mpc2000xl_all_t* p__root) : kaitai::kstruct(p__io) { + m__parent = p__parent; + m__root = p__root; + m__dirty = false; +} + +void mpc2000xl_all_t::step_edit_options_t::_read() { + m_auto_step_increment = m__io->read_bits_int_le(1); + m_duration_of_recorded_notes = static_cast(m__io->read_u1()); + m_tc_value_percentage = m__io->read_u1(); + if (!(m_tc_value_percentage >= 0)) { + throw kaitai::validation_less_than_error(0, m_tc_value_percentage, m__io, std::string("/types/step_edit_options/seq/2")); + } + if (!(m_tc_value_percentage <= 100)) { + throw kaitai::validation_greater_than_error(100, m_tc_value_percentage, m__io, std::string("/types/step_edit_options/seq/2")); + } + m__dirty = false; +} + +void mpc2000xl_all_t::step_edit_options_t::_fetch_instances() { +} + +void mpc2000xl_all_t::step_edit_options_t::_write() { + m__io->write_bits_int_le(1, ((m_auto_step_increment) ? 1 : 0)); + m__io->write_u1(static_cast(m_duration_of_recorded_notes)); + m__io->write_u1(m_tc_value_percentage); + _fetch_instances(); + m__dirty = false; +} + +void mpc2000xl_all_t::step_edit_options_t::_check() { + if (!(m_tc_value_percentage >= 0)) { + throw kaitai::validation_less_than_error(0, m_tc_value_percentage, m__io, std::string("/types/step_edit_options/seq/2")); + } + if (!(m_tc_value_percentage <= 100)) { + throw kaitai::validation_greater_than_error(100, m_tc_value_percentage, m__io, std::string("/types/step_edit_options/seq/2")); + } + m__dirty = false; +} + +mpc2000xl_all_t::step_edit_options_t::~step_edit_options_t() {} + +mpc2000xl_all_t::track_status_t::track_status_t(kaitai::kstream* p__io, kaitai::kstruct* p__parent, mpc2000xl_all_t* p__root) : kaitai::kstruct(p__io) { + m__parent = p__parent; + m__root = p__root; + m__dirty = false; +} + +void mpc2000xl_all_t::track_status_t::_read() { + m_unused_or_used = static_cast(m__io->read_bits_int_le(1)); + m_off_or_on = static_cast(m__io->read_bits_int_le(1)); + m_transmit_program_changes = static_cast(m__io->read_bits_int_le(1)); + m__unnamed3 = m__io->read_bits_int_le(5); + m__dirty = false; +} + +void mpc2000xl_all_t::track_status_t::_fetch_instances() { +} + +void mpc2000xl_all_t::track_status_t::_write() { + m__io->write_bits_int_le(1, static_cast(m_unused_or_used)); + m__io->write_bits_int_le(1, static_cast(m_off_or_on)); + m__io->write_bits_int_le(1, static_cast(m_transmit_program_changes)); + m__io->write_bits_int_le(5, m__unnamed3); + _fetch_instances(); + m__dirty = false; +} + +void mpc2000xl_all_t::track_status_t::_check() { + m__dirty = false; +} + +mpc2000xl_all_t::track_status_t::~track_status_t() {} + +mpc2000xl_all_t::tracks_t::tracks_t(kaitai::kstream* p__io, mpc2000xl_all_t::sequence_body_t* p__parent, mpc2000xl_all_t* p__root) : kaitai::kstruct(p__io) { + m__parent = p__parent; + m__root = p__root; + m__dirty = false; +} + +void mpc2000xl_all_t::tracks_t::_read() { + m_names = std::unique_ptr>(new std::vector()); + const int l_names = 64; + for (int i = 0; i < l_names; i++) { + m_names->push_back(std::move(kaitai::kstream::bytes_to_str(m__io->read_bytes(16), "ASCII"))); + } + m_device = std::unique_ptr>(new std::vector()); + const int l_device = 64; + for (int i = 0; i < l_device; i++) { + m_device->push_back(std::move(m__io->read_u1())); + } + m_bus = std::unique_ptr>(new std::vector()); + const int l_bus = 64; + for (int i = 0; i < l_bus; i++) { + m_bus->push_back(std::move(static_cast(m__io->read_u1()))); + } + m_program_change = std::unique_ptr>(new std::vector()); + const int l_program_change = 64; + for (int i = 0; i < l_program_change; i++) { + m_program_change->push_back(std::move(m__io->read_u1())); + } + m_velocity_ratio = std::unique_ptr>(new std::vector()); + const int l_velocity_ratio = 64; + for (int i = 0; i < l_velocity_ratio; i++) { + m_velocity_ratio->push_back(std::move(m__io->read_u1())); + } + m_status = std::unique_ptr>>(new std::vector>()); + const int l_status = 64; + for (int i = 0; i < l_status; i++) { + std::unique_ptr _t_status = std::unique_ptr(new track_status_t(m__io, this, m__root)); + try { + _t_status->_read(); + } catch(...) { + m_status->push_back(std::move(_t_status)); + throw; + } + m_status->push_back(std::move(_t_status)); + } + m_unknown = m__io->read_bytes(64); + m__dirty = false; +} + +void mpc2000xl_all_t::tracks_t::_fetch_instances() { + for (std::size_t i = 0; i < m_names->size(); ++i) { + } + for (std::size_t i = 0; i < m_device->size(); ++i) { + } + for (std::size_t i = 0; i < m_bus->size(); ++i) { + } + for (std::size_t i = 0; i < m_program_change->size(); ++i) { + } + for (std::size_t i = 0; i < m_velocity_ratio->size(); ++i) { + } + for (std::size_t i = 0; i < m_status->size(); ++i) { + m_status->at(i).get()->_fetch_instances(); + } +} + +void mpc2000xl_all_t::tracks_t::_write() { + if (m_names == nullptr) { + throw std::runtime_error("/types/tracks/seq/0: repeated field is not set"); + } + for (std::vector::const_iterator it = m_names->begin(); it != m_names->end(); ++it) { + m__io->write_bytes((*it)); + } + if (m_device == nullptr) { + throw std::runtime_error("/types/tracks/seq/1: repeated field is not set"); + } + for (std::vector::const_iterator it = m_device->begin(); it != m_device->end(); ++it) { + m__io->write_u1((*it)); + } + if (m_bus == nullptr) { + throw std::runtime_error("/types/tracks/seq/2: repeated field is not set"); + } + for (std::vector::const_iterator it = m_bus->begin(); it != m_bus->end(); ++it) { + m__io->write_u1(static_cast((*it))); + } + if (m_program_change == nullptr) { + throw std::runtime_error("/types/tracks/seq/3: repeated field is not set"); + } + for (std::vector::const_iterator it = m_program_change->begin(); it != m_program_change->end(); ++it) { + m__io->write_u1((*it)); + } + if (m_velocity_ratio == nullptr) { + throw std::runtime_error("/types/tracks/seq/4: repeated field is not set"); + } + for (std::vector::const_iterator it = m_velocity_ratio->begin(); it != m_velocity_ratio->end(); ++it) { + m__io->write_u1((*it)); + } + if (m_status == nullptr) { + throw std::runtime_error("/types/tracks/seq/5: repeated field is not set"); + } + for (std::vector>::const_iterator it = m_status->begin(); it != m_status->end(); ++it) { + if ((*it).get() == nullptr) { + throw std::runtime_error("/types/tracks/seq/5: nested object is not set"); + } + (*it).get()->_set_io(m__io); + (*it).get()->_write(); + } + m__io->write_bytes(m_unknown); + _fetch_instances(); + m__dirty = false; +} + +void mpc2000xl_all_t::tracks_t::_check() { + if (m_names == nullptr) { + throw std::runtime_error("/types/tracks/seq/0: repeated field is not set"); + } + if (m_names->size() != static_cast(64)) { + throw std::runtime_error("/types/tracks/seq/0: repeat-expr size mismatch"); + } + for (std::vector::const_iterator it = m_names->begin(); it != m_names->end(); ++it) { + if ((*it).size() != static_cast(16)) { + throw std::runtime_error("/types/tracks/seq/0: size mismatch"); + } + } + if (m_device == nullptr) { + throw std::runtime_error("/types/tracks/seq/1: repeated field is not set"); + } + if (m_device->size() != static_cast(64)) { + throw std::runtime_error("/types/tracks/seq/1: repeat-expr size mismatch"); + } + for (std::vector::const_iterator it = m_device->begin(); it != m_device->end(); ++it) { + } + if (m_bus == nullptr) { + throw std::runtime_error("/types/tracks/seq/2: repeated field is not set"); + } + if (m_bus->size() != static_cast(64)) { + throw std::runtime_error("/types/tracks/seq/2: repeat-expr size mismatch"); + } + for (std::vector::const_iterator it = m_bus->begin(); it != m_bus->end(); ++it) { + } + if (m_program_change == nullptr) { + throw std::runtime_error("/types/tracks/seq/3: repeated field is not set"); + } + if (m_program_change->size() != static_cast(64)) { + throw std::runtime_error("/types/tracks/seq/3: repeat-expr size mismatch"); + } + for (std::vector::const_iterator it = m_program_change->begin(); it != m_program_change->end(); ++it) { + } + if (m_velocity_ratio == nullptr) { + throw std::runtime_error("/types/tracks/seq/4: repeated field is not set"); + } + if (m_velocity_ratio->size() != static_cast(64)) { + throw std::runtime_error("/types/tracks/seq/4: repeat-expr size mismatch"); + } + for (std::vector::const_iterator it = m_velocity_ratio->begin(); it != m_velocity_ratio->end(); ++it) { + } + if (m_status == nullptr) { + throw std::runtime_error("/types/tracks/seq/5: repeated field is not set"); + } + if (m_status->size() != static_cast(64)) { + throw std::runtime_error("/types/tracks/seq/5: repeat-expr size mismatch"); + } + for (std::vector>::const_iterator it = m_status->begin(); it != m_status->end(); ++it) { + if ((*it).get() == nullptr) { + throw std::runtime_error("/types/tracks/seq/5: nested object is not set"); + } + (*it).get()->_set_io(m__io); + (*it).get()->_check(); + } + if (m_unknown.size() != static_cast(64)) { + throw std::runtime_error("/types/tracks/seq/6: size mismatch"); + } + m__dirty = false; +} + +mpc2000xl_all_t::tracks_t::~tracks_t() {} diff --git a/src/main/file/kaitai/generated/mpc2000xl_all.h b/src/main/file/kaitai/generated/mpc2000xl_all.h new file mode 100644 index 000000000..1daa74dbf --- /dev/null +++ b/src/main/file/kaitai/generated/mpc2000xl_all.h @@ -0,0 +1,2144 @@ +#pragma once + +// This is a generated file! Please edit source .ksy file and use kaitai-struct-compiler to rebuild + +class mpc2000xl_all_t; + +#include "kaitai/kaitaistruct.h" +#include +#include +#include +#include + +#if KAITAI_STRUCT_VERSION < 11000L +#error "Incompatible Kaitai Struct C++/STL API: version 0.11 or later is required" +#endif + +class mpc2000xl_all_t : public kaitai::kstruct { + +public: + class bar_t; + class ch_pressure_event_t; + class control_change_event_t; + class count_t; + class defaults_t; + class event_t; + class exclusive_event_t; + class location_t; + class midi_input_t; + class midi_output_t; + class midi_switch_t; + class midi_sync_t; + class misc_t; + class mixer_event_t; + class note_event_t; + class pitch_bend_event_t; + class poly_pressure_event_t; + class program_change_event_t; + class sequence_t; + class sequence_body_t; + class sequence_meta_t; + class sequencer_t; + class song_t; + class song_global_t; + class song_step_t; + class step_edit_options_t; + class track_status_t; + class tracks_t; + + enum bus_t { + BUS_MIDI = 0, + BUS_DRUM1 = 1, + BUS_DRUM2 = 2, + BUS_DRUM3 = 3, + BUS_DRUM4 = 4 + }; + static bool _is_defined_bus_t(bus_t v); + +private: + static const std::set _values_bus_t; + +public: + + enum click_output_t { + CLICK_OUTPUT_STEREO = 0, + CLICK_OUTPUT_INDIV1 = 1, + CLICK_OUTPUT_INDIV2 = 2, + CLICK_OUTPUT_INDIV3 = 3, + CLICK_OUTPUT_INDIV4 = 4, + CLICK_OUTPUT_INDIV5 = 5, + CLICK_OUTPUT_INDIV6 = 6, + CLICK_OUTPUT_INDIV7 = 7, + CLICK_OUTPUT_INDIV8 = 8 + }; + static bool _is_defined_click_output_t(click_output_t v); + +private: + static const std::set _values_click_output_t; + +public: + + enum controller_t { + CONTROLLER_BANK_SEL_MSB = 0, + CONTROLLER_MOD_WHEEL = 1, + CONTROLLER_BREATH_CONT = 2, + CONTROLLER_CC3 = 3, + CONTROLLER_FOOT_CONTROL = 4, + CONTROLLER_PORTA_TIME = 5, + CONTROLLER_DATA_ENTRY = 6, + CONTROLLER_MAIN_VOLUME = 7, + CONTROLLER_BALANCE = 8, + CONTROLLER_CC9 = 9, + CONTROLLER_PAN = 10, + CONTROLLER_EXPRESSION = 11, + CONTROLLER_EFFECT_1 = 12, + CONTROLLER_EFFECT_2 = 13, + CONTROLLER_CC14 = 14, + CONTROLLER_CC15 = 15, + CONTROLLER_GEN_PUR_1 = 16, + CONTROLLER_GEN_PUR_2 = 17, + CONTROLLER_GEN_PUR_3 = 18, + CONTROLLER_GEN_PUR_4 = 19, + CONTROLLER_CC20 = 20, + CONTROLLER_CC21 = 21, + CONTROLLER_CC22 = 22, + CONTROLLER_CC23 = 23, + CONTROLLER_CC24 = 24, + CONTROLLER_CC25 = 25, + CONTROLLER_CC26 = 26, + CONTROLLER_CC27 = 27, + CONTROLLER_CC28 = 28, + CONTROLLER_CC29 = 29, + CONTROLLER_CC30 = 30, + CONTROLLER_CC31 = 31, + CONTROLLER_BANK_SEL_LSB = 32, + CONTROLLER_MOD_WHEL_LSB = 33, + CONTROLLER_BREATH_LSB = 34, + CONTROLLER_CC35 = 35, + CONTROLLER_FOOT_CNT_LSB = 36, + CONTROLLER_PORT_TIME_LS = 37, + CONTROLLER_DATA_ENT_LSB = 38, + CONTROLLER_MAIN_VOL_LSB = 39, + CONTROLLER_BALANCE_LSB = 40, + CONTROLLER_CC41 = 41, + CONTROLLER_PAN_LSB = 42, + CONTROLLER_EXPRESS_LSB = 43, + CONTROLLER_EFFECT_1_LSB = 44, + CONTROLLER_EFFECT_2_MSB = 45, + CONTROLLER_CC46 = 46, + CONTROLLER_CC47 = 47, + CONTROLLER_GEN_PUR_1_LS = 48, + CONTROLLER_GEN_PUR_2_LS = 49, + CONTROLLER_GEN_PUR_3_LS = 50, + CONTROLLER_GEN_PUR_4_LS = 51, + CONTROLLER_CC52 = 52, + CONTROLLER_CC53 = 53, + CONTROLLER_CC54 = 54, + CONTROLLER_CC55 = 55, + CONTROLLER_CC56 = 56, + CONTROLLER_CC57 = 57, + CONTROLLER_CC58 = 58, + CONTROLLER_CC59 = 59, + CONTROLLER_CC60 = 60, + CONTROLLER_CC61 = 61, + CONTROLLER_CC62 = 62, + CONTROLLER_CC63 = 63, + CONTROLLER_SUSTAIN_PDL = 64, + CONTROLLER_PORTA_PEDAL = 65, + CONTROLLER_SOSTENUTO = 66, + CONTROLLER_SOFT_PEDAL = 67, + CONTROLLER_LEGATO_FT_SW = 68, + CONTROLLER_HOLD_2 = 69, + CONTROLLER_SOUND_VARI = 70, + CONTROLLER_TIMBER_HARMO = 71, + CONTROLLER_RELEASE_TIME = 72, + CONTROLLER_ATTACK_TIME = 73, + CONTROLLER_BRIGHTNESS = 74, + CONTROLLER_SOUND_CONT_6 = 75, + CONTROLLER_SOUND_CONT_7 = 76, + CONTROLLER_SOUND_CONT_8 = 77, + CONTROLLER_SOUND_CONT_9 = 78, + CONTROLLER_SOUND_CONT10 = 79, + CONTROLLER_GEN_PUR_5 = 80, + CONTROLLER_GEN_PUR_6 = 81, + CONTROLLER_GEN_PUR_7 = 82, + CONTROLLER_GEN_PUR_8 = 83, + CONTROLLER_PORTA_CNTRL = 84, + CONTROLLER_CC85 = 85, + CONTROLLER_CC86 = 86, + CONTROLLER_CC87 = 87, + CONTROLLER_CC88 = 88, + CONTROLLER_CC89 = 89, + CONTROLLER_CC90 = 90, + CONTROLLER_EXT_EFF_DPTH = 91, + CONTROLLER_TREMOLO_DPTH = 92, + CONTROLLER_CHORUS_DEPTH = 93, + CONTROLLER_DETUNE_DEPTH = 94, + CONTROLLER_PHASER_DEPTH = 95, + CONTROLLER_DATA_INCRE = 96, + CONTROLLER_DATA_DECRE = 97, + CONTROLLER_NRPN_LSB = 98, + CONTROLLER_NRPN_MSB = 99, + CONTROLLER_RPN_LSB = 100, + CONTROLLER_RPN_MSB = 101, + CONTROLLER_CC102 = 102, + CONTROLLER_CC103 = 103, + CONTROLLER_CC104 = 104, + CONTROLLER_CC105 = 105, + CONTROLLER_CC106 = 106, + CONTROLLER_CC107 = 107, + CONTROLLER_CC108 = 108, + CONTROLLER_CC109 = 109, + CONTROLLER_CC110 = 110, + CONTROLLER_CC111 = 111, + CONTROLLER_CC112 = 112, + CONTROLLER_CC113 = 113, + CONTROLLER_CC114 = 114, + CONTROLLER_CC115 = 115, + CONTROLLER_CC116 = 116, + CONTROLLER_CC117 = 117, + CONTROLLER_CC118 = 118, + CONTROLLER_CC119 = 119, + CONTROLLER_ALL_SND_OFF = 120, + CONTROLLER_RESET_CONTRL = 121, + CONTROLLER_LOCAL_ON_OFF = 122, + CONTROLLER_ALL_NOTE_OFF = 123, + CONTROLLER_OMNI_OFF = 124, + CONTROLLER_OMNI_ON = 125, + CONTROLLER_MONO_MODE_ON = 126, + CONTROLLER_POLY_MODE_ON = 127 + }; + static bool _is_defined_controller_t(controller_t v); + +private: + static const std::set _values_controller_t; + +public: + + enum count_in_mode_t { + COUNT_IN_MODE_FALSE = 0, + COUNT_IN_MODE_REC_ONLY = 1, + COUNT_IN_MODE_REC_AND_PLAY = 2 + }; + static bool _is_defined_count_in_mode_t(count_in_mode_t v); + +private: + static const std::set _values_count_in_mode_t; + +public: + + enum duration_of_recorded_notes_t { + DURATION_OF_RECORDED_NOTES_AS_PLAYED = 0, + DURATION_OF_RECORDED_NOTES_TC_VALUE = 1 + }; + static bool _is_defined_duration_of_recorded_notes_t(duration_of_recorded_notes_t v); + +private: + static const std::set _values_duration_of_recorded_notes_t; + +public: + + enum frame_rate_t { + FRAME_RATE_FPS_24 = 0, + FRAME_RATE_FPS_25 = 1, + FRAME_RATE_FPS_30D = 2, + FRAME_RATE_FPS_30 = 3 + }; + static bool _is_defined_frame_rate_t(frame_rate_t v); + +private: + static const std::set _values_frame_rate_t; + +public: + + enum midi_input_filter_type_t { + MIDI_INPUT_FILTER_TYPE_NOTES = 0, + MIDI_INPUT_FILTER_TYPE_PITCH_BEND = 1, + MIDI_INPUT_FILTER_TYPE_PROG_CHANGE = 2, + MIDI_INPUT_FILTER_TYPE_CH_PRESSURE = 3, + MIDI_INPUT_FILTER_TYPE_POLY_PRESS = 4, + MIDI_INPUT_FILTER_TYPE_EXCLUSIVE = 5, + MIDI_INPUT_FILTER_TYPE_CC0_BANK_SEL_MSB = 6, + MIDI_INPUT_FILTER_TYPE_CC1_MOD_WHEEL = 7, + MIDI_INPUT_FILTER_TYPE_CC2_BREATH_CONT = 8, + MIDI_INPUT_FILTER_TYPE_CC3 = 9, + MIDI_INPUT_FILTER_TYPE_CC4_FOOT_CONTROL = 10, + MIDI_INPUT_FILTER_TYPE_CC5_PORTA_TIME = 11, + MIDI_INPUT_FILTER_TYPE_CC6_DATA_ENTRY = 12, + MIDI_INPUT_FILTER_TYPE_CC7_MAIN_VOLUME = 13, + MIDI_INPUT_FILTER_TYPE_CC8_BALANCE = 14, + MIDI_INPUT_FILTER_TYPE_CC9 = 15, + MIDI_INPUT_FILTER_TYPE_CC10_PAN = 16, + MIDI_INPUT_FILTER_TYPE_CC11_EXPRESSION = 17, + MIDI_INPUT_FILTER_TYPE_CC12_EFFECT_1 = 18, + MIDI_INPUT_FILTER_TYPE_CC13_EFFECT_2 = 19, + MIDI_INPUT_FILTER_TYPE_CC14 = 20, + MIDI_INPUT_FILTER_TYPE_CC15 = 21, + MIDI_INPUT_FILTER_TYPE_CC16_GEN_PUR_1 = 22, + MIDI_INPUT_FILTER_TYPE_CC17_GEN_PUR_2 = 23, + MIDI_INPUT_FILTER_TYPE_CC18_GEN_PUR_3 = 24, + MIDI_INPUT_FILTER_TYPE_CC19_GEN_PUR_4 = 25, + MIDI_INPUT_FILTER_TYPE_CC20 = 26, + MIDI_INPUT_FILTER_TYPE_CC21 = 27, + MIDI_INPUT_FILTER_TYPE_CC22 = 28, + MIDI_INPUT_FILTER_TYPE_CC23 = 29, + MIDI_INPUT_FILTER_TYPE_CC24 = 30, + MIDI_INPUT_FILTER_TYPE_CC25 = 31, + MIDI_INPUT_FILTER_TYPE_CC26 = 32, + MIDI_INPUT_FILTER_TYPE_CC27 = 33, + MIDI_INPUT_FILTER_TYPE_CC28 = 34, + MIDI_INPUT_FILTER_TYPE_CC29 = 35, + MIDI_INPUT_FILTER_TYPE_CC30 = 36, + MIDI_INPUT_FILTER_TYPE_CC31 = 37, + MIDI_INPUT_FILTER_TYPE_CC32_BANK_SEL_LSB = 38, + MIDI_INPUT_FILTER_TYPE_CC33_MOD_WHEL_LSB = 39, + MIDI_INPUT_FILTER_TYPE_CC34_BREATH_LSB = 40, + MIDI_INPUT_FILTER_TYPE_CC35 = 41, + MIDI_INPUT_FILTER_TYPE_CC36_FOOT_CNT_LSB = 42, + MIDI_INPUT_FILTER_TYPE_CC37_PORT_TIME_LS = 43, + MIDI_INPUT_FILTER_TYPE_CC38_DATA_ENT_LSB = 44, + MIDI_INPUT_FILTER_TYPE_CC39_MAIN_VOL_LSB = 45, + MIDI_INPUT_FILTER_TYPE_CC40_BALANCE_LSB = 46, + MIDI_INPUT_FILTER_TYPE_CC41 = 47, + MIDI_INPUT_FILTER_TYPE_CC42_PAN_LSB = 48, + MIDI_INPUT_FILTER_TYPE_CC43_EXPRESS_LSB = 49, + MIDI_INPUT_FILTER_TYPE_CC44_EFFECT_1_LSB = 50, + MIDI_INPUT_FILTER_TYPE_CC45_EFFECT_2_MSB = 51, + MIDI_INPUT_FILTER_TYPE_CC46 = 52, + MIDI_INPUT_FILTER_TYPE_CC47 = 53, + MIDI_INPUT_FILTER_TYPE_CC48_GEN_PUR_1_LS = 54, + MIDI_INPUT_FILTER_TYPE_CC49_GEN_PUR_2_LS = 55, + MIDI_INPUT_FILTER_TYPE_CC50_GEN_PUR_3_LS = 56, + MIDI_INPUT_FILTER_TYPE_CC51_GEN_PUR_4_LS = 57, + MIDI_INPUT_FILTER_TYPE_CC52 = 58, + MIDI_INPUT_FILTER_TYPE_CC53 = 59, + MIDI_INPUT_FILTER_TYPE_CC54 = 60, + MIDI_INPUT_FILTER_TYPE_CC55 = 61, + MIDI_INPUT_FILTER_TYPE_CC56 = 62, + MIDI_INPUT_FILTER_TYPE_CC57 = 63, + MIDI_INPUT_FILTER_TYPE_CC58 = 64, + MIDI_INPUT_FILTER_TYPE_CC59 = 65, + MIDI_INPUT_FILTER_TYPE_CC60 = 66, + MIDI_INPUT_FILTER_TYPE_CC61 = 67, + MIDI_INPUT_FILTER_TYPE_CC62 = 68, + MIDI_INPUT_FILTER_TYPE_CC63 = 69, + MIDI_INPUT_FILTER_TYPE_CC64_SUSTAIN_PDL = 70, + MIDI_INPUT_FILTER_TYPE_CC65_PORTA_PEDAL = 71, + MIDI_INPUT_FILTER_TYPE_CC66_SOSTENUTO = 72, + MIDI_INPUT_FILTER_TYPE_CC67_SOFT_PEDAL = 73, + MIDI_INPUT_FILTER_TYPE_CC68_LEGATO_FT_SW = 74, + MIDI_INPUT_FILTER_TYPE_CC69_HOLD_2 = 75, + MIDI_INPUT_FILTER_TYPE_CC70_SOUND_VARI = 76, + MIDI_INPUT_FILTER_TYPE_CC71_TIMBER_HARMO = 77, + MIDI_INPUT_FILTER_TYPE_CC72_RELEASE_TIME = 78, + MIDI_INPUT_FILTER_TYPE_CC73_ATTACK_TIME = 79, + MIDI_INPUT_FILTER_TYPE_CC74_BRIGHTNESS = 80, + MIDI_INPUT_FILTER_TYPE_CC75_SOUND_CONT_6 = 81, + MIDI_INPUT_FILTER_TYPE_CC76_SOUND_CONT_7 = 82, + MIDI_INPUT_FILTER_TYPE_CC77_SOUND_CONT_8 = 83, + MIDI_INPUT_FILTER_TYPE_CC78_SOUND_CONT_9 = 84, + MIDI_INPUT_FILTER_TYPE_CC79_SOUND_CONT10 = 85, + MIDI_INPUT_FILTER_TYPE_CC80_GEN_PUR_5 = 86, + MIDI_INPUT_FILTER_TYPE_CC81_GEN_PUR_6 = 87, + MIDI_INPUT_FILTER_TYPE_CC82_GEN_PUR_7 = 88, + MIDI_INPUT_FILTER_TYPE_CC83_GEN_PUR_8 = 89, + MIDI_INPUT_FILTER_TYPE_CC84_PORTA_CNTRL = 90, + MIDI_INPUT_FILTER_TYPE_CC85 = 91, + MIDI_INPUT_FILTER_TYPE_CC86 = 92, + MIDI_INPUT_FILTER_TYPE_CC87 = 93, + MIDI_INPUT_FILTER_TYPE_CC88 = 94, + MIDI_INPUT_FILTER_TYPE_CC89 = 95, + MIDI_INPUT_FILTER_TYPE_CC90 = 96, + MIDI_INPUT_FILTER_TYPE_CC91_EXT_EFF_DPTH = 97, + MIDI_INPUT_FILTER_TYPE_CC92_TREMOLO_DPTH = 98, + MIDI_INPUT_FILTER_TYPE_CC93_CHORUS_DEPTH = 99, + MIDI_INPUT_FILTER_TYPE_CC94_DETUNE_DEPTH = 100, + MIDI_INPUT_FILTER_TYPE_CC95_PHASER_DEPTH = 101, + MIDI_INPUT_FILTER_TYPE_CC96_DATA_INCRE = 102, + MIDI_INPUT_FILTER_TYPE_CC97_DATA_DECRE = 103, + MIDI_INPUT_FILTER_TYPE_CC98_NRPN_LSB = 104, + MIDI_INPUT_FILTER_TYPE_CC99_NRPN_MSB = 105, + MIDI_INPUT_FILTER_TYPE_CC100_RPN_LSB = 106, + MIDI_INPUT_FILTER_TYPE_CC101_RPN_MSB = 107, + MIDI_INPUT_FILTER_TYPE_CC102 = 108, + MIDI_INPUT_FILTER_TYPE_CC103 = 109, + MIDI_INPUT_FILTER_TYPE_CC104 = 110, + MIDI_INPUT_FILTER_TYPE_CC105 = 111, + MIDI_INPUT_FILTER_TYPE_CC106 = 112, + MIDI_INPUT_FILTER_TYPE_CC107 = 113, + MIDI_INPUT_FILTER_TYPE_CC108 = 114, + MIDI_INPUT_FILTER_TYPE_CC109 = 115, + MIDI_INPUT_FILTER_TYPE_CC110 = 116, + MIDI_INPUT_FILTER_TYPE_CC111 = 117, + MIDI_INPUT_FILTER_TYPE_CC112 = 118, + MIDI_INPUT_FILTER_TYPE_CC113 = 119, + MIDI_INPUT_FILTER_TYPE_CC114 = 120, + MIDI_INPUT_FILTER_TYPE_CC115 = 121, + MIDI_INPUT_FILTER_TYPE_CC116 = 122, + MIDI_INPUT_FILTER_TYPE_CC117 = 123, + MIDI_INPUT_FILTER_TYPE_CC118 = 124, + MIDI_INPUT_FILTER_TYPE_CC119 = 125, + MIDI_INPUT_FILTER_TYPE_CC120_ALL_SND_OFF = 126, + MIDI_INPUT_FILTER_TYPE_CC121_RESET_CONTRL = 127, + MIDI_INPUT_FILTER_TYPE_CC122_LOCAL_ON_OFF = 128, + MIDI_INPUT_FILTER_TYPE_CC123_ALL_NOTE_OFF = 129, + MIDI_INPUT_FILTER_TYPE_CC124_OMNI_OFF = 130, + MIDI_INPUT_FILTER_TYPE_CC125_OMNI_ON = 131, + MIDI_INPUT_FILTER_TYPE_CC126_MONO_MODE_ON = 132, + MIDI_INPUT_FILTER_TYPE_CC127_POLY_MODE_ON = 133 + }; + static bool _is_defined_midi_input_filter_type_t(midi_input_filter_type_t v); + +private: + static const std::set _values_midi_input_filter_type_t; + +public: + + enum midi_input_receive_channel_t { + MIDI_INPUT_RECEIVE_CHANNEL_ALL = 0, + MIDI_INPUT_RECEIVE_CHANNEL_CH1 = 1, + MIDI_INPUT_RECEIVE_CHANNEL_CH2 = 2, + MIDI_INPUT_RECEIVE_CHANNEL_CH3 = 3, + MIDI_INPUT_RECEIVE_CHANNEL_CH4 = 4, + MIDI_INPUT_RECEIVE_CHANNEL_CH5 = 5, + MIDI_INPUT_RECEIVE_CHANNEL_CH6 = 6, + MIDI_INPUT_RECEIVE_CHANNEL_CH7 = 7, + MIDI_INPUT_RECEIVE_CHANNEL_CH8 = 8, + MIDI_INPUT_RECEIVE_CHANNEL_CH9 = 9, + MIDI_INPUT_RECEIVE_CHANNEL_CH10 = 10, + MIDI_INPUT_RECEIVE_CHANNEL_CH11 = 11, + MIDI_INPUT_RECEIVE_CHANNEL_CH12 = 12, + MIDI_INPUT_RECEIVE_CHANNEL_CH13 = 13, + MIDI_INPUT_RECEIVE_CHANNEL_CH14 = 14, + MIDI_INPUT_RECEIVE_CHANNEL_CH15 = 15, + MIDI_INPUT_RECEIVE_CHANNEL_CH16 = 16 + }; + static bool _is_defined_midi_input_receive_channel_t(midi_input_receive_channel_t v); + +private: + static const std::set _values_midi_input_receive_channel_t; + +public: + + enum midi_switch_function_t { + MIDI_SWITCH_FUNCTION_PLAY_STRT = 0, + MIDI_SWITCH_FUNCTION_PLAY = 1, + MIDI_SWITCH_FUNCTION_STOP = 2, + MIDI_SWITCH_FUNCTION_REC_AND_PLAY = 3, + MIDI_SWITCH_FUNCTION_ODUB_AND_PLAY = 4, + MIDI_SWITCH_FUNCTION_REC_PUNCH = 5, + MIDI_SWITCH_FUNCTION_ODUB_PNCH = 6, + MIDI_SWITCH_FUNCTION_TAP = 7, + MIDI_SWITCH_FUNCTION_PAD_BNK_A = 8, + MIDI_SWITCH_FUNCTION_PAD_BNK_B = 9, + MIDI_SWITCH_FUNCTION_PAD_BNK_C = 10, + MIDI_SWITCH_FUNCTION_PAD_BNK_D = 11, + MIDI_SWITCH_FUNCTION_PAD_1 = 12, + MIDI_SWITCH_FUNCTION_PAD_2 = 13, + MIDI_SWITCH_FUNCTION_PAD_3 = 14, + MIDI_SWITCH_FUNCTION_PAD_4 = 15, + MIDI_SWITCH_FUNCTION_PAD_5 = 16, + MIDI_SWITCH_FUNCTION_PAD_6 = 17, + MIDI_SWITCH_FUNCTION_PAD_7 = 18, + MIDI_SWITCH_FUNCTION_PAD_8 = 19, + MIDI_SWITCH_FUNCTION_PAD_9 = 20, + MIDI_SWITCH_FUNCTION_PAD_10 = 21, + MIDI_SWITCH_FUNCTION_PAD_11 = 22, + MIDI_SWITCH_FUNCTION_PAD_12 = 23, + MIDI_SWITCH_FUNCTION_PAD_13 = 24, + MIDI_SWITCH_FUNCTION_PAD_14 = 25, + MIDI_SWITCH_FUNCTION_PAD_15 = 26, + MIDI_SWITCH_FUNCTION_PAD_16 = 27, + MIDI_SWITCH_FUNCTION_F1 = 28, + MIDI_SWITCH_FUNCTION_F2 = 29, + MIDI_SWITCH_FUNCTION_F3 = 30, + MIDI_SWITCH_FUNCTION_F4 = 31, + MIDI_SWITCH_FUNCTION_F5 = 32, + MIDI_SWITCH_FUNCTION_F6 = 33 + }; + static bool _is_defined_midi_switch_function_t(midi_switch_function_t v); + +private: + static const std::set _values_midi_switch_function_t; + +public: + + enum midi_sync_mode_t { + MIDI_SYNC_MODE_FALSE = 0, + MIDI_SYNC_MODE_MIDI_CLOCK = 1, + MIDI_SYNC_MODE_TIME_CODE = 2 + }; + static bool _is_defined_midi_sync_mode_t(midi_sync_mode_t v); + +private: + static const std::set _values_midi_sync_mode_t; + +public: + + enum midi_sync_output_t { + MIDI_SYNC_OUTPUT_A = 0, + MIDI_SYNC_OUTPUT_B = 1, + MIDI_SYNC_OUTPUT_A_AND_B = 2 + }; + static bool _is_defined_midi_sync_output_t(midi_sync_output_t v); + +private: + static const std::set _values_midi_sync_output_t; + +public: + + enum mixer_event_param_t { + MIXER_EVENT_PARAM_STEREO_LEVEL = 1, + MIXER_EVENT_PARAM_STEREO_PAN = 2, + MIXER_EVENT_PARAM_FXSEND_LEVEL = 3, + MIXER_EVENT_PARAM_INDIV_LEVEL = 5 + }; + static bool _is_defined_mixer_event_param_t(mixer_event_param_t v); + +private: + static const std::set _values_mixer_event_param_t; + +public: + + enum off_on_t { + OFF_ON_FALSE = 0, + OFF_ON_TRUE = 1 + }; + static bool _is_defined_off_on_t(off_on_t v); + +private: + static const std::set _values_off_on_t; + +public: + + enum rate_t { + RATE_ONE_4 = 0, + RATE_ONE_4_3 = 1, + RATE_ONE_8 = 2, + RATE_ONE_8_3 = 3, + RATE_ONE_16 = 4, + RATE_ONE_16_3 = 5, + RATE_ONE_32 = 6, + RATE_ONE_32_3 = 7 + }; + static bool _is_defined_rate_t(rate_t v); + +private: + static const std::set _values_rate_t; + +public: + + enum sequence_is_used_t { + SEQUENCE_IS_USED_FALSE = 0, + SEQUENCE_IS_USED_TRUE = 641 + }; + static bool _is_defined_sequence_is_used_t(sequence_is_used_t v); + +private: + static const std::set _values_sequence_is_used_t; + +public: + + enum soft_thru_mode_t { + SOFT_THRU_MODE_FALSE = 0, + SOFT_THRU_MODE_AS_TRACK = 1, + SOFT_THRU_MODE_OMNI_A = 2, + SOFT_THRU_MODE_OMNI_B = 3, + SOFT_THRU_MODE_OMNI_AB = 4 + }; + static bool _is_defined_soft_thru_mode_t(soft_thru_mode_t v); + +private: + static const std::set _values_soft_thru_mode_t; + +public: + + enum sound_source_t { + SOUND_SOURCE_CLICK = 0, + SOUND_SOURCE_DRUM1 = 1, + SOUND_SOURCE_DRUM2 = 2, + SOUND_SOURCE_DRUM3 = 3, + SOUND_SOURCE_DRUM4 = 4 + }; + static bool _is_defined_sound_source_t(sound_source_t v); + +private: + static const std::set _values_sound_source_t; + +public: + + enum tap_averaging_t { + TAP_AVERAGING_TAP_AVG_2 = 0, + TAP_AVERAGING_TAP_AVG_3 = 1, + TAP_AVERAGING_TAP_AVG_4 = 2 + }; + static bool _is_defined_tap_averaging_t(tap_averaging_t v); + +private: + static const std::set _values_tap_averaging_t; + +public: + + enum time_display_style_t { + TIME_DISPLAY_STYLE_BAR_BEAT_CLOCK = 0, + TIME_DISPLAY_STYLE_HOUR_MINUTE_SEC = 1 + }; + static bool _is_defined_time_display_style_t(time_display_style_t v); + +private: + static const std::set _values_time_display_style_t; + +public: + + enum timing_correct_t { + TIMING_CORRECT_FALSE = 0, + TIMING_CORRECT_ONE_8 = 1, + TIMING_CORRECT_ONE_8_3 = 2, + TIMING_CORRECT_ONE_16 = 3, + TIMING_CORRECT_ONE_16_3 = 4, + TIMING_CORRECT_ONE_32 = 5, + TIMING_CORRECT_ONE_32_3 = 6 + }; + static bool _is_defined_timing_correct_t(timing_correct_t v); + +private: + static const std::set _values_timing_correct_t; + +public: + + enum unused_used_t { + UNUSED_USED_UNUSED = 0, + UNUSED_USED_USED = 1 + }; + static bool _is_defined_unused_used_t(unused_used_t v); + +private: + static const std::set _values_unused_used_t; + +public: + +private: + bool m__dirty; + +public: + + mpc2000xl_all_t(kaitai::kstream* p__io, kaitai::kstruct* p__parent = nullptr, mpc2000xl_all_t* p__root = nullptr); + void _read(); + void _fetch_instances(); + void _write(); + void _check(); + ~mpc2000xl_all_t(); + + class bar_t : public kaitai::kstruct { + + public: + + private: + bool m__dirty; + + public: + + bar_t(int32_t p_idx, kaitai::kstream* p__io, mpc2000xl_all_t::sequence_body_t* p__parent = nullptr, mpc2000xl_all_t* p__root = nullptr); + void _read(); + void _fetch_instances(); + void _write(); + void _check(); + ~bar_t(); + int32_t denominator(); + void _invalidate_denominator() { f_denominator = false; } + int32_t first_tick(); + void _invalidate_first_tick() { f_first_tick = false; } + int32_t numerator(); + void _invalidate_numerator() { f_numerator = false; } + uint8_t ticks_per_beat() const { return m_ticks_per_beat; } + void set_ticks_per_beat(uint8_t _v) { m__dirty = true; f_denominator = false; f_first_tick = false; f_numerator = false; m_ticks_per_beat = std::move(_v); } + uint64_t last_tick() const { return m_last_tick; } + void set_last_tick(uint64_t _v) { m__dirty = true; f_denominator = false; f_first_tick = false; f_numerator = false; m_last_tick = std::move(_v); } + int32_t idx() const { return m_idx; } + void set_idx(int32_t _v) { m__dirty = true; f_denominator = false; f_first_tick = false; f_numerator = false; m_idx = std::move(_v); } + mpc2000xl_all_t* _root() const { return m__root; } + void set__root(mpc2000xl_all_t* _v) { m__dirty = true; f_denominator = false; f_first_tick = false; f_numerator = false; m__root = std::move(_v); } + kaitai::kstruct* _parent() const { return m__parent; } + void set__parent(mpc2000xl_all_t::sequence_body_t* _v) { m__dirty = true; f_denominator = false; f_first_tick = false; f_numerator = false; m__parent = std::move(_v); } + + private: + bool f_denominator; + int32_t m_denominator; + bool f_first_tick; + int32_t m_first_tick; + bool f_numerator; + int32_t m_numerator; + uint8_t m_ticks_per_beat; + uint64_t m_last_tick; + int32_t m_idx; + mpc2000xl_all_t* m__root; + mpc2000xl_all_t::sequence_body_t* m__parent; + }; + + class ch_pressure_event_t : public kaitai::kstruct { + + public: + + private: + bool m__dirty; + + public: + + ch_pressure_event_t(kaitai::kstream* p__io, mpc2000xl_all_t::event_t* p__parent = nullptr, mpc2000xl_all_t* p__root = nullptr); + void _read(); + void _fetch_instances(); + void _write(); + void _check(); + ~ch_pressure_event_t(); + uint8_t pressure() const { return m_pressure; } + void set_pressure(uint8_t _v) { m__dirty = true; m_pressure = std::move(_v); } + std::string _unnamed1() const { return m__unnamed1; } + void set__unnamed1(std::string _v) { m__dirty = true; m__unnamed1 = std::move(_v); } + mpc2000xl_all_t* _root() const { return m__root; } + void set__root(mpc2000xl_all_t* _v) { m__dirty = true; m__root = std::move(_v); } + kaitai::kstruct* _parent() const { return m__parent; } + void set__parent(mpc2000xl_all_t::event_t* _v) { m__dirty = true; m__parent = std::move(_v); } + + private: + uint8_t m_pressure; + std::string m__unnamed1; + mpc2000xl_all_t* m__root; + mpc2000xl_all_t::event_t* m__parent; + }; + + class control_change_event_t : public kaitai::kstruct { + + public: + + private: + bool m__dirty; + + public: + + control_change_event_t(kaitai::kstream* p__io, mpc2000xl_all_t::event_t* p__parent = nullptr, mpc2000xl_all_t* p__root = nullptr); + void _read(); + void _fetch_instances(); + void _write(); + void _check(); + ~control_change_event_t(); + controller_t controller() const { return m_controller; } + void set_controller(controller_t _v) { m__dirty = true; m_controller = std::move(_v); } + uint8_t value() const { return m_value; } + void set_value(uint8_t _v) { m__dirty = true; m_value = std::move(_v); } + std::string _unnamed2() const { return m__unnamed2; } + void set__unnamed2(std::string _v) { m__dirty = true; m__unnamed2 = std::move(_v); } + mpc2000xl_all_t* _root() const { return m__root; } + void set__root(mpc2000xl_all_t* _v) { m__dirty = true; m__root = std::move(_v); } + kaitai::kstruct* _parent() const { return m__parent; } + void set__parent(mpc2000xl_all_t::event_t* _v) { m__dirty = true; m__parent = std::move(_v); } + + private: + controller_t m_controller; + uint8_t m_value; + std::string m__unnamed2; + mpc2000xl_all_t* m__root; + mpc2000xl_all_t::event_t* m__parent; + }; + + class count_t : public kaitai::kstruct { + + public: + + private: + bool m__dirty; + + public: + + count_t(kaitai::kstream* p__io, mpc2000xl_all_t* p__parent = nullptr, mpc2000xl_all_t* p__root = nullptr); + void _read(); + void _fetch_instances(); + void _write(); + void _check(); + ~count_t(); + bool enabled() const { return m_enabled; } + void set_enabled(bool _v) { m__dirty = true; m_enabled = std::move(_v); } + uint64_t _unnamed1() const { return m__unnamed1; } + void set__unnamed1(uint64_t _v) { m__dirty = true; m__unnamed1 = std::move(_v); } + count_in_mode_t count_in_mode() const { return m_count_in_mode; } + void set_count_in_mode(count_in_mode_t _v) { m__dirty = true; m_count_in_mode = std::move(_v); } + uint8_t click_volume() const { return m_click_volume; } + void set_click_volume(uint8_t _v) { m__dirty = true; m_click_volume = std::move(_v); } + rate_t rate() const { return m_rate; } + void set_rate(rate_t _v) { m__dirty = true; m_rate = std::move(_v); } + bool enabled_in_play() const { return m_enabled_in_play; } + void set_enabled_in_play(bool _v) { m__dirty = true; m_enabled_in_play = std::move(_v); } + uint64_t _unnamed6() const { return m__unnamed6; } + void set__unnamed6(uint64_t _v) { m__dirty = true; m__unnamed6 = std::move(_v); } + bool enabled_in_rec() const { return m_enabled_in_rec; } + void set_enabled_in_rec(bool _v) { m__dirty = true; m_enabled_in_rec = std::move(_v); } + uint64_t _unnamed8() const { return m__unnamed8; } + void set__unnamed8(uint64_t _v) { m__dirty = true; m__unnamed8 = std::move(_v); } + click_output_t click_output() const { return m_click_output; } + void set_click_output(click_output_t _v) { m__dirty = true; m_click_output = std::move(_v); } + bool wait_for_key() const { return m_wait_for_key; } + void set_wait_for_key(bool _v) { m__dirty = true; m_wait_for_key = std::move(_v); } + uint64_t _unnamed11() const { return m__unnamed11; } + void set__unnamed11(uint64_t _v) { m__dirty = true; m__unnamed11 = std::move(_v); } + sound_source_t sound_source() const { return m_sound_source; } + void set_sound_source(sound_source_t _v) { m__dirty = true; m_sound_source = std::move(_v); } + uint8_t accent_pad_index() const { return m_accent_pad_index; } + void set_accent_pad_index(uint8_t _v) { m__dirty = true; m_accent_pad_index = std::move(_v); } + uint8_t normal_pad_index() const { return m_normal_pad_index; } + void set_normal_pad_index(uint8_t _v) { m__dirty = true; m_normal_pad_index = std::move(_v); } + uint8_t accent_velo() const { return m_accent_velo; } + void set_accent_velo(uint8_t _v) { m__dirty = true; m_accent_velo = std::move(_v); } + uint8_t normal_velo() const { return m_normal_velo; } + void set_normal_velo(uint8_t _v) { m__dirty = true; m_normal_velo = std::move(_v); } + mpc2000xl_all_t* _root() const { return m__root; } + void set__root(mpc2000xl_all_t* _v) { m__dirty = true; m__root = std::move(_v); } + kaitai::kstruct* _parent() const { return m__parent; } + void set__parent(mpc2000xl_all_t* _v) { m__dirty = true; m__parent = std::move(_v); } + + private: + bool m_enabled; + uint64_t m__unnamed1; + count_in_mode_t m_count_in_mode; + uint8_t m_click_volume; + rate_t m_rate; + bool m_enabled_in_play; + uint64_t m__unnamed6; + bool m_enabled_in_rec; + uint64_t m__unnamed8; + click_output_t m_click_output; + bool m_wait_for_key; + uint64_t m__unnamed11; + sound_source_t m_sound_source; + uint8_t m_accent_pad_index; + uint8_t m_normal_pad_index; + uint8_t m_accent_velo; + uint8_t m_normal_velo; + mpc2000xl_all_t* m__root; + mpc2000xl_all_t* m__parent; + }; + + class defaults_t : public kaitai::kstruct { + + public: + + private: + bool m__dirty; + + public: + + defaults_t(kaitai::kstream* p__io, mpc2000xl_all_t* p__parent = nullptr, mpc2000xl_all_t* p__root = nullptr); + void _read(); + void _fetch_instances(); + void _write(); + void _check(); + ~defaults_t(); + std::string sequence_name() const { return m_sequence_name; } + void set_sequence_name(std::string _v) { m__dirty = true; m_sequence_name = std::move(_v); } + std::string _unnamed1() const { return m__unnamed1; } + void set__unnamed1(std::string _v) { m__dirty = true; m__unnamed1 = std::move(_v); } + uint16_t tempo() const { return m_tempo; } + void set_tempo(uint16_t _v) { m__dirty = true; m_tempo = std::move(_v); } + uint8_t numerator() const { return m_numerator; } + void set_numerator(uint8_t _v) { m__dirty = true; m_numerator = std::move(_v); } + uint8_t denominator() const { return m_denominator; } + void set_denominator(uint8_t _v) { m__dirty = true; m_denominator = std::move(_v); } + uint16_t bar_count() const { return m_bar_count; } + void set_bar_count(uint16_t _v) { m__dirty = true; m_bar_count = std::move(_v); } + uint16_t tick_count() const { return m_tick_count; } + void set_tick_count(uint16_t _v) { m__dirty = true; m_tick_count = std::move(_v); } + std::vector* unknown1() const { return m_unknown1.get(); } + void set_unknown1(std::unique_ptr> _v) { m__dirty = true; m_unknown1 = std::move(_v); } + std::string unknown2() const { return m_unknown2; } + void set_unknown2(std::string _v) { m__dirty = true; m_unknown2 = std::move(_v); } + std::vector* device_names() const { return m_device_names.get(); } + void set_device_names(std::unique_ptr> _v) { m__dirty = true; m_device_names = std::move(_v); } + std::vector* track_names() const { return m_track_names.get(); } + void set_track_names(std::unique_ptr> _v) { m__dirty = true; m_track_names = std::move(_v); } + std::vector* devices() const { return m_devices.get(); } + void set_devices(std::unique_ptr> _v) { m__dirty = true; m_devices = std::move(_v); } + std::vector* buses() const { return m_buses.get(); } + void set_buses(std::unique_ptr> _v) { m__dirty = true; m_buses = std::move(_v); } + std::vector* programs() const { return m_programs.get(); } + void set_programs(std::unique_ptr> _v) { m__dirty = true; m_programs = std::move(_v); } + std::vector* track_velocities() const { return m_track_velocities.get(); } + void set_track_velocities(std::unique_ptr> _v) { m__dirty = true; m_track_velocities = std::move(_v); } + std::vector>* track_statuses() const { return m_track_statuses.get(); } + void set_track_statuses(std::unique_ptr>> _v) { m__dirty = true; m_track_statuses = std::move(_v); } + std::string _unnamed16() const { return m__unnamed16; } + void set__unnamed16(std::string _v) { m__dirty = true; m__unnamed16 = std::move(_v); } + mpc2000xl_all_t* _root() const { return m__root; } + void set__root(mpc2000xl_all_t* _v) { m__dirty = true; m__root = std::move(_v); } + kaitai::kstruct* _parent() const { return m__parent; } + void set__parent(mpc2000xl_all_t* _v) { m__dirty = true; m__parent = std::move(_v); } + + private: + std::string m_sequence_name; + std::string m__unnamed1; + uint16_t m_tempo; + uint8_t m_numerator; + uint8_t m_denominator; + uint16_t m_bar_count; + uint16_t m_tick_count; + std::unique_ptr> m_unknown1; + std::string m_unknown2; + std::unique_ptr> m_device_names; + std::unique_ptr> m_track_names; + std::unique_ptr> m_devices; + std::unique_ptr> m_buses; + std::unique_ptr> m_programs; + std::unique_ptr> m_track_velocities; + std::unique_ptr>> m_track_statuses; + std::string m__unnamed16; + mpc2000xl_all_t* m__root; + mpc2000xl_all_t* m__parent; + }; + + class event_t : public kaitai::kstruct { + + public: + + private: + bool m__dirty; + + public: + + event_t(kaitai::kstream* p__io, mpc2000xl_all_t::sequence_body_t* p__parent = nullptr, mpc2000xl_all_t* p__root = nullptr); + void _read(); + void _fetch_instances(); + void _write(); + void _check(); + ~event_t(); + uint64_t tick() const { return m_tick; } + void set_tick(uint64_t _v) { m__dirty = true; m_tick = std::move(_v); } + uint64_t duration_bits_1() const { return m_duration_bits_1; } + void set_duration_bits_1(uint64_t _v) { m__dirty = true; n_duration_bits_1 = false; m_duration_bits_1 = std::move(_v); } + uint64_t track() const { return m_track; } + void set_track(uint64_t _v) { m__dirty = true; n_track = false; m_track = std::move(_v); } + uint64_t duration_bits_2() const { return m_duration_bits_2; } + void set_duration_bits_2(uint64_t _v) { m__dirty = true; n_duration_bits_2 = false; m_duration_bits_2 = std::move(_v); } + uint8_t id() const { return m_id; } + void set_id(uint8_t _v) { m__dirty = true; n_id = false; m_id = std::move(_v); } + uint64_t terminator_pad() const { return m_terminator_pad; } + void set_terminator_pad(uint64_t _v) { m__dirty = true; n_terminator_pad = false; m_terminator_pad = std::move(_v); } + note_event_t* note_event() const { return m_note_event.get(); } + void set_note_event(std::unique_ptr _v) { m__dirty = true; m_note_event = std::move(_v); } + pitch_bend_event_t* pitch_bend() const { return m_pitch_bend.get(); } + void set_pitch_bend(std::unique_ptr _v) { m__dirty = true; m_pitch_bend = std::move(_v); } + control_change_event_t* control_change() const { return m_control_change.get(); } + void set_control_change(std::unique_ptr _v) { m__dirty = true; m_control_change = std::move(_v); } + program_change_event_t* program_change() const { return m_program_change.get(); } + void set_program_change(std::unique_ptr _v) { m__dirty = true; m_program_change = std::move(_v); } + ch_pressure_event_t* ch_pressure() const { return m_ch_pressure.get(); } + void set_ch_pressure(std::unique_ptr _v) { m__dirty = true; m_ch_pressure = std::move(_v); } + poly_pressure_event_t* poly_pressure() const { return m_poly_pressure.get(); } + void set_poly_pressure(std::unique_ptr _v) { m__dirty = true; m_poly_pressure = std::move(_v); } + exclusive_event_t* exclusive() const { return m_exclusive.get(); } + void set_exclusive(std::unique_ptr _v) { m__dirty = true; m_exclusive = std::move(_v); } + std::string terminator() const { return m_terminator; } + void set_terminator(std::string _v) { m__dirty = true; n_terminator = false; m_terminator = std::move(_v); } + mpc2000xl_all_t* _root() const { return m__root; } + void set__root(mpc2000xl_all_t* _v) { m__dirty = true; m__root = std::move(_v); } + kaitai::kstruct* _parent() const { return m__parent; } + void set__parent(mpc2000xl_all_t::sequence_body_t* _v) { m__dirty = true; m__parent = std::move(_v); } + + private: + uint64_t m_tick; + uint64_t m_duration_bits_1; + bool n_duration_bits_1; + + public: + bool _is_null_duration_bits_1() { duration_bits_1(); return n_duration_bits_1; }; + + private: + uint64_t m_track; + bool n_track; + + public: + bool _is_null_track() { track(); return n_track; }; + + private: + uint64_t m_duration_bits_2; + bool n_duration_bits_2; + + public: + bool _is_null_duration_bits_2() { duration_bits_2(); return n_duration_bits_2; }; + + private: + uint8_t m_id; + bool n_id; + + public: + bool _is_null_id() { id(); return n_id; }; + + private: + uint64_t m_terminator_pad; + bool n_terminator_pad; + + public: + bool _is_null_terminator_pad() { terminator_pad(); return n_terminator_pad; }; + + private: + std::unique_ptr m_note_event; + + public: + bool _is_null_note_event() { return !note_event(); }; + + private: + std::unique_ptr m_pitch_bend; + + public: + bool _is_null_pitch_bend() { return !pitch_bend(); }; + + private: + std::unique_ptr m_control_change; + + public: + bool _is_null_control_change() { return !control_change(); }; + + private: + std::unique_ptr m_program_change; + + public: + bool _is_null_program_change() { return !program_change(); }; + + private: + std::unique_ptr m_ch_pressure; + + public: + bool _is_null_ch_pressure() { return !ch_pressure(); }; + + private: + std::unique_ptr m_poly_pressure; + + public: + bool _is_null_poly_pressure() { return !poly_pressure(); }; + + private: + std::unique_ptr m_exclusive; + + public: + bool _is_null_exclusive() { return !exclusive(); }; + + private: + std::string m_terminator; + bool n_terminator; + + public: + bool _is_null_terminator() { terminator(); return n_terminator; }; + + private: + mpc2000xl_all_t* m__root; + mpc2000xl_all_t::sequence_body_t* m__parent; + }; + + class exclusive_event_t : public kaitai::kstruct { + + public: + + private: + bool m__dirty; + + public: + + exclusive_event_t(kaitai::kstream* p__io, mpc2000xl_all_t::event_t* p__parent = nullptr, mpc2000xl_all_t* p__root = nullptr); + void _read(); + void _fetch_instances(); + void _write(); + void _check(); + ~exclusive_event_t(); + std::string _unnamed0() const { return m__unnamed0; } + void set__unnamed0(std::string _v) { m__dirty = true; m__unnamed0 = std::move(_v); } + std::string bytes() const { return m_bytes; } + void set_bytes(std::string _v) { m__dirty = true; m_bytes = std::move(_v); } + mixer_event_t* mixer() const { return m_mixer.get(); } + void set_mixer(std::unique_ptr _v) { m__dirty = true; m_mixer = std::move(_v); } + std::string _unnamed3() const { return m__unnamed3; } + void set__unnamed3(std::string _v) { m__dirty = true; m__unnamed3 = std::move(_v); } + mpc2000xl_all_t* _root() const { return m__root; } + void set__root(mpc2000xl_all_t* _v) { m__dirty = true; m__root = std::move(_v); } + kaitai::kstruct* _parent() const { return m__parent; } + void set__parent(mpc2000xl_all_t::event_t* _v) { m__dirty = true; m__parent = std::move(_v); } + + private: + std::string m__unnamed0; + std::string m_bytes; + std::unique_ptr m_mixer; + + public: + bool _is_null_mixer() { return !mixer(); }; + + private: + std::string m__unnamed3; + mpc2000xl_all_t* m__root; + mpc2000xl_all_t::event_t* m__parent; + }; + + class location_t : public kaitai::kstruct { + + public: + + private: + bool m__dirty; + + public: + + location_t(kaitai::kstream* p__io, mpc2000xl_all_t* p__parent = nullptr, mpc2000xl_all_t* p__root = nullptr); + void _read(); + void _fetch_instances(); + void _write(); + void _check(); + ~location_t(); + std::string display_value(); + void _invalidate_display_value() { f_display_value = false; } + uint16_t bar() const { return m_bar; } + void set_bar(uint16_t _v) { m__dirty = true; f_display_value = false; m_bar = std::move(_v); } + uint8_t beat() const { return m_beat; } + void set_beat(uint8_t _v) { m__dirty = true; f_display_value = false; m_beat = std::move(_v); } + uint8_t clock() const { return m_clock; } + void set_clock(uint8_t _v) { m__dirty = true; f_display_value = false; m_clock = std::move(_v); } + mpc2000xl_all_t* _root() const { return m__root; } + void set__root(mpc2000xl_all_t* _v) { m__dirty = true; f_display_value = false; m__root = std::move(_v); } + kaitai::kstruct* _parent() const { return m__parent; } + void set__parent(mpc2000xl_all_t* _v) { m__dirty = true; f_display_value = false; m__parent = std::move(_v); } + + private: + bool f_display_value; + std::string m_display_value; + uint16_t m_bar; + uint8_t m_beat; + uint8_t m_clock; + mpc2000xl_all_t* m__root; + mpc2000xl_all_t* m__parent; + }; + + class midi_input_t : public kaitai::kstruct { + + public: + + private: + bool m__dirty; + + public: + + midi_input_t(kaitai::kstream* p__io, mpc2000xl_all_t* p__parent = nullptr, mpc2000xl_all_t* p__root = nullptr); + void _read(); + void _fetch_instances(); + void _write(); + void _check(); + ~midi_input_t(); + midi_input_receive_channel_t receive_channel() const { return m_receive_channel; } + void set_receive_channel(midi_input_receive_channel_t _v) { m__dirty = true; m_receive_channel = std::move(_v); } + bool sustain_pedal_to_duration() const { return m_sustain_pedal_to_duration; } + void set_sustain_pedal_to_duration(bool _v) { m__dirty = true; m_sustain_pedal_to_duration = std::move(_v); } + uint64_t _unnamed2() const { return m__unnamed2; } + void set__unnamed2(uint64_t _v) { m__dirty = true; m__unnamed2 = std::move(_v); } + bool filter_enabled() const { return m_filter_enabled; } + void set_filter_enabled(bool _v) { m__dirty = true; m_filter_enabled = std::move(_v); } + uint64_t _unnamed4() const { return m__unnamed4; } + void set__unnamed4(uint64_t _v) { m__dirty = true; m__unnamed4 = std::move(_v); } + midi_input_filter_type_t filter_type() const { return m_filter_type; } + void set_filter_type(midi_input_filter_type_t _v) { m__dirty = true; m_filter_type = std::move(_v); } + bool multi_rec_enabled() const { return m_multi_rec_enabled; } + void set_multi_rec_enabled(bool _v) { m__dirty = true; m_multi_rec_enabled = std::move(_v); } + uint64_t _unnamed7() const { return m__unnamed7; } + void set__unnamed7(uint64_t _v) { m__dirty = true; m__unnamed7 = std::move(_v); } + std::vector* multi_rec_destination_tracks() const { return m_multi_rec_destination_tracks.get(); } + void set_multi_rec_destination_tracks(std::unique_ptr> _v) { m__dirty = true; m_multi_rec_destination_tracks = std::move(_v); } + bool note_pass_enabled() const { return m_note_pass_enabled; } + void set_note_pass_enabled(bool _v) { m__dirty = true; m_note_pass_enabled = std::move(_v); } + uint64_t _unnamed10() const { return m__unnamed10; } + void set__unnamed10(uint64_t _v) { m__dirty = true; m__unnamed10 = std::move(_v); } + bool pitch_bend_pass_enabled() const { return m_pitch_bend_pass_enabled; } + void set_pitch_bend_pass_enabled(bool _v) { m__dirty = true; m_pitch_bend_pass_enabled = std::move(_v); } + uint64_t _unnamed12() const { return m__unnamed12; } + void set__unnamed12(uint64_t _v) { m__dirty = true; m__unnamed12 = std::move(_v); } + bool pgm_change_pass_enabled() const { return m_pgm_change_pass_enabled; } + void set_pgm_change_pass_enabled(bool _v) { m__dirty = true; m_pgm_change_pass_enabled = std::move(_v); } + uint64_t _unnamed14() const { return m__unnamed14; } + void set__unnamed14(uint64_t _v) { m__dirty = true; m__unnamed14 = std::move(_v); } + bool ch_pressure_pass_enabled() const { return m_ch_pressure_pass_enabled; } + void set_ch_pressure_pass_enabled(bool _v) { m__dirty = true; m_ch_pressure_pass_enabled = std::move(_v); } + uint64_t _unnamed16() const { return m__unnamed16; } + void set__unnamed16(uint64_t _v) { m__dirty = true; m__unnamed16 = std::move(_v); } + bool poly_pressure_pass_enabled() const { return m_poly_pressure_pass_enabled; } + void set_poly_pressure_pass_enabled(bool _v) { m__dirty = true; m_poly_pressure_pass_enabled = std::move(_v); } + uint64_t _unnamed18() const { return m__unnamed18; } + void set__unnamed18(uint64_t _v) { m__dirty = true; m__unnamed18 = std::move(_v); } + bool exclusive_pass_enabled() const { return m_exclusive_pass_enabled; } + void set_exclusive_pass_enabled(bool _v) { m__dirty = true; m_exclusive_pass_enabled = std::move(_v); } + uint64_t _unnamed20() const { return m__unnamed20; } + void set__unnamed20(uint64_t _v) { m__dirty = true; m__unnamed20 = std::move(_v); } + std::vector* cc_pass_enabled() const { return m_cc_pass_enabled.get(); } + void set_cc_pass_enabled(std::unique_ptr> _v) { m__dirty = true; m_cc_pass_enabled = std::move(_v); } + mpc2000xl_all_t* _root() const { return m__root; } + void set__root(mpc2000xl_all_t* _v) { m__dirty = true; m__root = std::move(_v); } + kaitai::kstruct* _parent() const { return m__parent; } + void set__parent(mpc2000xl_all_t* _v) { m__dirty = true; m__parent = std::move(_v); } + + private: + midi_input_receive_channel_t m_receive_channel; + bool m_sustain_pedal_to_duration; + uint64_t m__unnamed2; + bool m_filter_enabled; + uint64_t m__unnamed4; + midi_input_filter_type_t m_filter_type; + bool m_multi_rec_enabled; + uint64_t m__unnamed7; + std::unique_ptr> m_multi_rec_destination_tracks; + bool m_note_pass_enabled; + uint64_t m__unnamed10; + bool m_pitch_bend_pass_enabled; + uint64_t m__unnamed12; + bool m_pgm_change_pass_enabled; + uint64_t m__unnamed14; + bool m_ch_pressure_pass_enabled; + uint64_t m__unnamed16; + bool m_poly_pressure_pass_enabled; + uint64_t m__unnamed18; + bool m_exclusive_pass_enabled; + uint64_t m__unnamed20; + std::unique_ptr> m_cc_pass_enabled; + mpc2000xl_all_t* m__root; + mpc2000xl_all_t* m__parent; + }; + + class midi_output_t : public kaitai::kstruct { + + public: + + private: + bool m__dirty; + + public: + + midi_output_t(kaitai::kstream* p__io, mpc2000xl_all_t* p__parent = nullptr, mpc2000xl_all_t* p__root = nullptr); + void _read(); + void _fetch_instances(); + void _write(); + void _check(); + ~midi_output_t(); + soft_thru_mode_t soft_thru_mode() const { return m_soft_thru_mode; } + void set_soft_thru_mode(soft_thru_mode_t _v) { m__dirty = true; m_soft_thru_mode = std::move(_v); } + mpc2000xl_all_t* _root() const { return m__root; } + void set__root(mpc2000xl_all_t* _v) { m__dirty = true; m__root = std::move(_v); } + kaitai::kstruct* _parent() const { return m__parent; } + void set__parent(mpc2000xl_all_t* _v) { m__dirty = true; m__parent = std::move(_v); } + + private: + soft_thru_mode_t m_soft_thru_mode; + mpc2000xl_all_t* m__root; + mpc2000xl_all_t* m__parent; + }; + + class midi_switch_t : public kaitai::kstruct { + + public: + + private: + bool m__dirty; + + public: + + midi_switch_t(kaitai::kstream* p__io, mpc2000xl_all_t::misc_t* p__parent = nullptr, mpc2000xl_all_t* p__root = nullptr); + void _read(); + void _fetch_instances(); + void _write(); + void _check(); + ~midi_switch_t(); + + /** + * 0xFF is used for an unassigned footswitch controller. + */ + uint8_t controller() const { return m_controller; } + void set_controller(uint8_t _v) { m__dirty = true; m_controller = std::move(_v); } + midi_switch_function_t function() const { return m_function; } + void set_function(midi_switch_function_t _v) { m__dirty = true; m_function = std::move(_v); } + mpc2000xl_all_t* _root() const { return m__root; } + void set__root(mpc2000xl_all_t* _v) { m__dirty = true; m__root = std::move(_v); } + kaitai::kstruct* _parent() const { return m__parent; } + void set__parent(mpc2000xl_all_t::misc_t* _v) { m__dirty = true; m__parent = std::move(_v); } + + private: + uint8_t m_controller; + midi_switch_function_t m_function; + mpc2000xl_all_t* m__root; + mpc2000xl_all_t::misc_t* m__parent; + }; + + class midi_sync_t : public kaitai::kstruct { + + public: + + private: + bool m__dirty; + + public: + + midi_sync_t(kaitai::kstream* p__io, mpc2000xl_all_t* p__parent = nullptr, mpc2000xl_all_t* p__root = nullptr); + void _read(); + void _fetch_instances(); + void _write(); + void _check(); + ~midi_sync_t(); + midi_sync_mode_t in_mode() const { return m_in_mode; } + void set_in_mode(midi_sync_mode_t _v) { m__dirty = true; m_in_mode = std::move(_v); } + midi_sync_mode_t out_mode() const { return m_out_mode; } + void set_out_mode(midi_sync_mode_t _v) { m__dirty = true; m_out_mode = std::move(_v); } + uint8_t shift_early() const { return m_shift_early; } + void set_shift_early(uint8_t _v) { m__dirty = true; m_shift_early = std::move(_v); } + bool send_mmc_enabled() const { return m_send_mmc_enabled; } + void set_send_mmc_enabled(bool _v) { m__dirty = true; m_send_mmc_enabled = std::move(_v); } + uint64_t _unnamed4() const { return m__unnamed4; } + void set__unnamed4(uint64_t _v) { m__dirty = true; m__unnamed4 = std::move(_v); } + frame_rate_t frame_rate() const { return m_frame_rate; } + void set_frame_rate(frame_rate_t _v) { m__dirty = true; m_frame_rate = std::move(_v); } + uint8_t input() const { return m_input; } + void set_input(uint8_t _v) { m__dirty = true; m_input = std::move(_v); } + midi_sync_output_t output() const { return m_output; } + void set_output(midi_sync_output_t _v) { m__dirty = true; m_output = std::move(_v); } + mpc2000xl_all_t* _root() const { return m__root; } + void set__root(mpc2000xl_all_t* _v) { m__dirty = true; m__root = std::move(_v); } + kaitai::kstruct* _parent() const { return m__parent; } + void set__parent(mpc2000xl_all_t* _v) { m__dirty = true; m__parent = std::move(_v); } + + private: + midi_sync_mode_t m_in_mode; + midi_sync_mode_t m_out_mode; + uint8_t m_shift_early; + bool m_send_mmc_enabled; + uint64_t m__unnamed4; + frame_rate_t m_frame_rate; + uint8_t m_input; + midi_sync_output_t m_output; + mpc2000xl_all_t* m__root; + mpc2000xl_all_t* m__parent; + }; + + class misc_t : public kaitai::kstruct { + + public: + + private: + bool m__dirty; + + public: + + misc_t(kaitai::kstream* p__io, mpc2000xl_all_t* p__parent = nullptr, mpc2000xl_all_t* p__root = nullptr); + void _read(); + void _fetch_instances(); + void _write(); + void _check(); + ~misc_t(); + tap_averaging_t tap_averaging() const { return m_tap_averaging; } + void set_tap_averaging(tap_averaging_t _v) { m__dirty = true; m_tap_averaging = std::move(_v); } + bool midi_sync_in_receive_mmc_enabled() const { return m_midi_sync_in_receive_mmc_enabled; } + void set_midi_sync_in_receive_mmc_enabled(bool _v) { m__dirty = true; m_midi_sync_in_receive_mmc_enabled = std::move(_v); } + std::vector>* midi_switch() const { return m_midi_switch.get(); } + void set_midi_switch(std::unique_ptr>> _v) { m__dirty = true; m_midi_switch = std::move(_v); } + mpc2000xl_all_t* _root() const { return m__root; } + void set__root(mpc2000xl_all_t* _v) { m__dirty = true; m__root = std::move(_v); } + kaitai::kstruct* _parent() const { return m__parent; } + void set__parent(mpc2000xl_all_t* _v) { m__dirty = true; m__parent = std::move(_v); } + + private: + tap_averaging_t m_tap_averaging; + bool m_midi_sync_in_receive_mmc_enabled; + std::unique_ptr>> m_midi_switch; + mpc2000xl_all_t* m__root; + mpc2000xl_all_t* m__parent; + }; + + class mixer_event_t : public kaitai::kstruct { + + public: + + private: + bool m__dirty; + + public: + + mixer_event_t(kaitai::kstream* p__io, mpc2000xl_all_t::exclusive_event_t* p__parent = nullptr, mpc2000xl_all_t* p__root = nullptr); + void _read(); + void _fetch_instances(); + void _write(); + void _check(); + ~mixer_event_t(); + std::string _unnamed0() const { return m__unnamed0; } + void set__unnamed0(std::string _v) { m__dirty = true; m__unnamed0 = std::move(_v); } + mixer_event_param_t param() const { return m_param; } + void set_param(mixer_event_param_t _v) { m__dirty = true; m_param = std::move(_v); } + uint8_t pad_index() const { return m_pad_index; } + void set_pad_index(uint8_t _v) { m__dirty = true; m_pad_index = std::move(_v); } + uint8_t value() const { return m_value; } + void set_value(uint8_t _v) { m__dirty = true; m_value = std::move(_v); } + std::string _unnamed4() const { return m__unnamed4; } + void set__unnamed4(std::string _v) { m__dirty = true; m__unnamed4 = std::move(_v); } + mpc2000xl_all_t* _root() const { return m__root; } + void set__root(mpc2000xl_all_t* _v) { m__dirty = true; m__root = std::move(_v); } + kaitai::kstruct* _parent() const { return m__parent; } + void set__parent(mpc2000xl_all_t::exclusive_event_t* _v) { m__dirty = true; m__parent = std::move(_v); } + + private: + std::string m__unnamed0; + mixer_event_param_t m_param; + uint8_t m_pad_index; + uint8_t m_value; + std::string m__unnamed4; + mpc2000xl_all_t* m__root; + mpc2000xl_all_t::exclusive_event_t* m__parent; + }; + + class note_event_t : public kaitai::kstruct { + + public: + + enum note_variation_type_t { + NOTE_VARIATION_TYPE_TUNE = 0, + NOTE_VARIATION_TYPE_DECAY = 1, + NOTE_VARIATION_TYPE_ATTACK = 2, + NOTE_VARIATION_TYPE_FILTER = 3 + }; + static bool _is_defined_note_variation_type_t(note_variation_type_t v); + + private: + static const std::set _values_note_variation_type_t; + + public: + + private: + bool m__dirty; + + public: + + note_event_t(kaitai::kstream* p__io, mpc2000xl_all_t::event_t* p__parent = nullptr, mpc2000xl_all_t* p__root = nullptr); + void _read(); + void _fetch_instances(); + void _write(); + void _check(); + ~note_event_t(); + int32_t duration(); + void _invalidate_duration() { f_duration = false; } + uint8_t note(); + void _invalidate_note() { f_note = false; } + mpc2000xl_all_t::note_event_t::note_variation_type_t variation_type(); + void _invalidate_variation_type() { f_variation_type = false; } + uint8_t duration_bits_3() const { return m_duration_bits_3; } + void set_duration_bits_3(uint8_t _v) { m__dirty = true; f_duration = false; f_note = false; f_variation_type = false; m_duration_bits_3 = std::move(_v); } + uint64_t velocity() const { return m_velocity; } + void set_velocity(uint64_t _v) { m__dirty = true; f_duration = false; f_note = false; f_variation_type = false; m_velocity = std::move(_v); } + bool variation_type_bit_1() const { return m_variation_type_bit_1; } + void set_variation_type_bit_1(bool _v) { m__dirty = true; f_duration = false; f_note = false; f_variation_type = false; m_variation_type_bit_1 = std::move(_v); } + uint64_t variation_value() const { return m_variation_value; } + void set_variation_value(uint64_t _v) { m__dirty = true; f_duration = false; f_note = false; f_variation_type = false; m_variation_value = std::move(_v); } + bool variation_type_bit_2() const { return m_variation_type_bit_2; } + void set_variation_type_bit_2(bool _v) { m__dirty = true; f_duration = false; f_note = false; f_variation_type = false; m_variation_type_bit_2 = std::move(_v); } + mpc2000xl_all_t* _root() const { return m__root; } + void set__root(mpc2000xl_all_t* _v) { m__dirty = true; f_duration = false; f_note = false; f_variation_type = false; m__root = std::move(_v); } + kaitai::kstruct* _parent() const { return m__parent; } + void set__parent(mpc2000xl_all_t::event_t* _v) { m__dirty = true; f_duration = false; f_note = false; f_variation_type = false; m__parent = std::move(_v); } + + private: + bool f_duration; + int32_t m_duration; + bool f_note; + uint8_t m_note; + bool f_variation_type; + mpc2000xl_all_t::note_event_t::note_variation_type_t m_variation_type; + uint8_t m_duration_bits_3; + uint64_t m_velocity; + bool m_variation_type_bit_1; + uint64_t m_variation_value; + bool m_variation_type_bit_2; + mpc2000xl_all_t* m__root; + mpc2000xl_all_t::event_t* m__parent; + }; + + class pitch_bend_event_t : public kaitai::kstruct { + + public: + + private: + bool m__dirty; + + public: + + pitch_bend_event_t(kaitai::kstream* p__io, mpc2000xl_all_t::event_t* p__parent = nullptr, mpc2000xl_all_t* p__root = nullptr); + void _read(); + void _fetch_instances(); + void _write(); + void _check(); + ~pitch_bend_event_t(); + int32_t corrected_amount(); + void _invalidate_corrected_amount() { f_corrected_amount = false; } + uint64_t amount_bits_1() const { return m_amount_bits_1; } + void set_amount_bits_1(uint64_t _v) { m__dirty = true; f_corrected_amount = false; m_amount_bits_1 = std::move(_v); } + uint64_t amount_bits_2() const { return m_amount_bits_2; } + void set_amount_bits_2(uint64_t _v) { m__dirty = true; f_corrected_amount = false; m_amount_bits_2 = std::move(_v); } + std::string _unnamed2() const { return m__unnamed2; } + void set__unnamed2(std::string _v) { m__dirty = true; f_corrected_amount = false; m__unnamed2 = std::move(_v); } + mpc2000xl_all_t* _root() const { return m__root; } + void set__root(mpc2000xl_all_t* _v) { m__dirty = true; f_corrected_amount = false; m__root = std::move(_v); } + kaitai::kstruct* _parent() const { return m__parent; } + void set__parent(mpc2000xl_all_t::event_t* _v) { m__dirty = true; f_corrected_amount = false; m__parent = std::move(_v); } + + private: + bool f_corrected_amount; + int32_t m_corrected_amount; + uint64_t m_amount_bits_1; + uint64_t m_amount_bits_2; + std::string m__unnamed2; + mpc2000xl_all_t* m__root; + mpc2000xl_all_t::event_t* m__parent; + }; + + class poly_pressure_event_t : public kaitai::kstruct { + + public: + + private: + bool m__dirty; + + public: + + poly_pressure_event_t(kaitai::kstream* p__io, mpc2000xl_all_t::event_t* p__parent = nullptr, mpc2000xl_all_t* p__root = nullptr); + void _read(); + void _fetch_instances(); + void _write(); + void _check(); + ~poly_pressure_event_t(); + uint8_t note() const { return m_note; } + void set_note(uint8_t _v) { m__dirty = true; m_note = std::move(_v); } + uint8_t pressure() const { return m_pressure; } + void set_pressure(uint8_t _v) { m__dirty = true; m_pressure = std::move(_v); } + std::string _unnamed2() const { return m__unnamed2; } + void set__unnamed2(std::string _v) { m__dirty = true; m__unnamed2 = std::move(_v); } + mpc2000xl_all_t* _root() const { return m__root; } + void set__root(mpc2000xl_all_t* _v) { m__dirty = true; m__root = std::move(_v); } + kaitai::kstruct* _parent() const { return m__parent; } + void set__parent(mpc2000xl_all_t::event_t* _v) { m__dirty = true; m__parent = std::move(_v); } + + private: + uint8_t m_note; + uint8_t m_pressure; + std::string m__unnamed2; + mpc2000xl_all_t* m__root; + mpc2000xl_all_t::event_t* m__parent; + }; + + class program_change_event_t : public kaitai::kstruct { + + public: + + private: + bool m__dirty; + + public: + + program_change_event_t(kaitai::kstream* p__io, mpc2000xl_all_t::event_t* p__parent = nullptr, mpc2000xl_all_t* p__root = nullptr); + void _read(); + void _fetch_instances(); + void _write(); + void _check(); + ~program_change_event_t(); + uint8_t program() const { return m_program; } + void set_program(uint8_t _v) { m__dirty = true; m_program = std::move(_v); } + std::string _unnamed1() const { return m__unnamed1; } + void set__unnamed1(std::string _v) { m__dirty = true; m__unnamed1 = std::move(_v); } + mpc2000xl_all_t* _root() const { return m__root; } + void set__root(mpc2000xl_all_t* _v) { m__dirty = true; m__root = std::move(_v); } + kaitai::kstruct* _parent() const { return m__parent; } + void set__parent(mpc2000xl_all_t::event_t* _v) { m__dirty = true; m__parent = std::move(_v); } + + private: + uint8_t m_program; + std::string m__unnamed1; + mpc2000xl_all_t* m__root; + mpc2000xl_all_t::event_t* m__parent; + }; + + class sequence_t : public kaitai::kstruct { + + public: + + private: + bool m__dirty; + + public: + + sequence_t(kaitai::kstream* p__io, mpc2000xl_all_t* p__parent = nullptr, mpc2000xl_all_t* p__root = nullptr); + void _read(); + void _fetch_instances(); + void _write(); + void _check(); + ~sequence_t(); + std::string name(); + void _invalidate_name() { f_name = false; } + std::string name_part_1() const { return m_name_part_1; } + void set_name_part_1(std::string _v) { m__dirty = true; f_name = false; m_name_part_1 = std::move(_v); } + std::string name_part_2() const { return m_name_part_2; } + void set_name_part_2(std::string _v) { m__dirty = true; n_name_part_2 = false; f_name = false; m_name_part_2 = std::move(_v); } + sequence_body_t* body() const { return m_body.get(); } + void set_body(std::unique_ptr _v) { m__dirty = true; f_name = false; m_body = std::move(_v); } + mpc2000xl_all_t* _root() const { return m__root; } + void set__root(mpc2000xl_all_t* _v) { m__dirty = true; f_name = false; m__root = std::move(_v); } + kaitai::kstruct* _parent() const { return m__parent; } + void set__parent(mpc2000xl_all_t* _v) { m__dirty = true; f_name = false; m__parent = std::move(_v); } + + private: + bool f_name; + std::string m_name; + bool n_name; + + public: + bool _is_null_name() { name(); return n_name; }; + + private: + std::string m_name_part_1; + std::string m_name_part_2; + bool n_name_part_2; + + public: + bool _is_null_name_part_2() { name_part_2(); return n_name_part_2; }; + + private: + std::unique_ptr m_body; + + public: + bool _is_null_body() { return !body(); }; + + private: + mpc2000xl_all_t* m__root; + mpc2000xl_all_t* m__parent; + }; + + class sequence_body_t : public kaitai::kstruct { + + public: + class start_time_t; + + private: + bool m__dirty; + + public: + + sequence_body_t(kaitai::kstream* p__io, mpc2000xl_all_t::sequence_t* p__parent = nullptr, mpc2000xl_all_t* p__root = nullptr); + void _read(); + void _fetch_instances(); + void _write(); + void _check(); + ~sequence_body_t(); + + class start_time_t : public kaitai::kstruct { + + public: + + private: + bool m__dirty; + + public: + + start_time_t(kaitai::kstream* p__io, mpc2000xl_all_t::sequence_body_t* p__parent = nullptr, mpc2000xl_all_t* p__root = nullptr); + void _read(); + void _fetch_instances(); + void _write(); + void _check(); + ~start_time_t(); + uint8_t hours() const { return m_hours; } + void set_hours(uint8_t _v) { m__dirty = true; m_hours = std::move(_v); } + uint8_t minutes() const { return m_minutes; } + void set_minutes(uint8_t _v) { m__dirty = true; m_minutes = std::move(_v); } + uint8_t seconds() const { return m_seconds; } + void set_seconds(uint8_t _v) { m__dirty = true; m_seconds = std::move(_v); } + uint8_t frames() const { return m_frames; } + void set_frames(uint8_t _v) { m__dirty = true; m_frames = std::move(_v); } + uint8_t frame_decimals() const { return m_frame_decimals; } + void set_frame_decimals(uint8_t _v) { m__dirty = true; m_frame_decimals = std::move(_v); } + mpc2000xl_all_t* _root() const { return m__root; } + void set__root(mpc2000xl_all_t* _v) { m__dirty = true; m__root = std::move(_v); } + kaitai::kstruct* _parent() const { return m__parent; } + void set__parent(mpc2000xl_all_t::sequence_body_t* _v) { m__dirty = true; m__parent = std::move(_v); } + + private: + uint8_t m_hours; + uint8_t m_minutes; + uint8_t m_seconds; + uint8_t m_frames; + uint8_t m_frame_decimals; + mpc2000xl_all_t* m__root; + mpc2000xl_all_t::sequence_body_t* m__parent; + }; + + public: + sequence_is_used_t is_used() const { return m_is_used; } + void set_is_used(sequence_is_used_t _v) { m__dirty = true; m_is_used = std::move(_v); } + + /** + * Warning! 1-based. You'll need this to put the sequence in the right slot + */ + uint8_t index() const { return m_index; } + void set_index(uint8_t _v) { m__dirty = true; m_index = std::move(_v); } + std::string _unnamed2() const { return m__unnamed2; } + void set__unnamed2(std::string _v) { m__dirty = true; m__unnamed2 = std::move(_v); } + uint16_t bar_count() const { return m_bar_count; } + void set_bar_count(uint16_t _v) { m__dirty = true; m_bar_count = std::move(_v); } + uint32_t last_tick() const { return m_last_tick; } + void set_last_tick(uint32_t _v) { m__dirty = true; m_last_tick = std::move(_v); } + std::string _unnamed5() const { return m__unnamed5; } + void set__unnamed5(std::string _v) { m__dirty = true; m__unnamed5 = std::move(_v); } + uint16_t loop_start_bar_index() const { return m_loop_start_bar_index; } + void set_loop_start_bar_index(uint16_t _v) { m__dirty = true; m_loop_start_bar_index = std::move(_v); } + uint16_t loop_end_bar_index() const { return m_loop_end_bar_index; } + void set_loop_end_bar_index(uint16_t _v) { m__dirty = true; m_loop_end_bar_index = std::move(_v); } + bool loop_enabled() const { return m_loop_enabled; } + void set_loop_enabled(bool _v) { m__dirty = true; m_loop_enabled = std::move(_v); } + uint64_t _unnamed9() const { return m__unnamed9; } + void set__unnamed9(uint64_t _v) { m__dirty = true; m__unnamed9 = std::move(_v); } + start_time_t* start_time() const { return m_start_time.get(); } + void set_start_time(std::unique_ptr _v) { m__dirty = true; m_start_time = std::move(_v); } + std::string _unnamed11() const { return m__unnamed11; } + void set__unnamed11(std::string _v) { m__dirty = true; m__unnamed11 = std::move(_v); } + uint32_t last_tick2() const { return m_last_tick2; } + void set_last_tick2(uint32_t _v) { m__dirty = true; m_last_tick2 = std::move(_v); } + std::string _unnamed13() const { return m__unnamed13; } + void set__unnamed13(std::string _v) { m__dirty = true; m__unnamed13 = std::move(_v); } + std::vector* device_names() const { return m_device_names.get(); } + void set_device_names(std::unique_ptr> _v) { m__dirty = true; m_device_names = std::move(_v); } + tracks_t* tracks() const { return m_tracks.get(); } + void set_tracks(std::unique_ptr _v) { m__dirty = true; m_tracks = std::move(_v); } + std::string _unnamed16() const { return m__unnamed16; } + void set__unnamed16(std::string _v) { m__dirty = true; m__unnamed16 = std::move(_v); } + std::vector>* bars() const { return m_bars.get(); } + void set_bars(std::unique_ptr>> _v) { m__dirty = true; m_bars = std::move(_v); } + std::string _unnamed18() const { return m__unnamed18; } + void set__unnamed18(std::string _v) { m__dirty = true; m__unnamed18 = std::move(_v); } + std::string _unnamed19() const { return m__unnamed19; } + void set__unnamed19(std::string _v) { m__dirty = true; m__unnamed19 = std::move(_v); } + std::vector>* events() const { return m_events.get(); } + void set_events(std::unique_ptr>> _v) { m__dirty = true; m_events = std::move(_v); } + mpc2000xl_all_t* _root() const { return m__root; } + void set__root(mpc2000xl_all_t* _v) { m__dirty = true; m__root = std::move(_v); } + kaitai::kstruct* _parent() const { return m__parent; } + void set__parent(mpc2000xl_all_t::sequence_t* _v) { m__dirty = true; m__parent = std::move(_v); } + + private: + sequence_is_used_t m_is_used; + uint8_t m_index; + std::string m__unnamed2; + uint16_t m_bar_count; + uint32_t m_last_tick; + std::string m__unnamed5; + uint16_t m_loop_start_bar_index; + uint16_t m_loop_end_bar_index; + bool m_loop_enabled; + uint64_t m__unnamed9; + std::unique_ptr m_start_time; + std::string m__unnamed11; + uint32_t m_last_tick2; + std::string m__unnamed13; + std::unique_ptr> m_device_names; + std::unique_ptr m_tracks; + std::string m__unnamed16; + std::unique_ptr>> m_bars; + std::string m__unnamed18; + std::string m__unnamed19; + std::unique_ptr>> m_events; + mpc2000xl_all_t* m__root; + mpc2000xl_all_t::sequence_t* m__parent; + }; + + class sequence_meta_t : public kaitai::kstruct { + + public: + + private: + bool m__dirty; + + public: + + sequence_meta_t(kaitai::kstream* p__io, mpc2000xl_all_t* p__parent = nullptr, mpc2000xl_all_t* p__root = nullptr); + void _read(); + void _fetch_instances(); + void _write(); + void _check(); + ~sequence_meta_t(); + bool is_used(); + void _invalidate_is_used() { f_is_used = false; } + std::string name() const { return m_name; } + void set_name(std::string _v) { m__dirty = true; f_is_used = false; m_name = std::move(_v); } + uint16_t last_event_index() const { return m_last_event_index; } + void set_last_event_index(uint16_t _v) { m__dirty = true; f_is_used = false; m_last_event_index = std::move(_v); } + mpc2000xl_all_t* _root() const { return m__root; } + void set__root(mpc2000xl_all_t* _v) { m__dirty = true; f_is_used = false; m__root = std::move(_v); } + kaitai::kstruct* _parent() const { return m__parent; } + void set__parent(mpc2000xl_all_t* _v) { m__dirty = true; f_is_used = false; m__parent = std::move(_v); } + + private: + bool f_is_used; + bool m_is_used; + std::string m_name; + uint16_t m_last_event_index; + mpc2000xl_all_t* m__root; + mpc2000xl_all_t* m__parent; + }; + + class sequencer_t : public kaitai::kstruct { + + public: + + private: + bool m__dirty; + + public: + + sequencer_t(kaitai::kstream* p__io, mpc2000xl_all_t* p__parent = nullptr, mpc2000xl_all_t* p__root = nullptr); + void _read(); + void _fetch_instances(); + void _write(); + void _check(); + ~sequencer_t(); + uint8_t active_sequence() const { return m_active_sequence; } + void set_active_sequence(uint8_t _v) { m__dirty = true; m_active_sequence = std::move(_v); } + std::string _unnamed1() const { return m__unnamed1; } + void set__unnamed1(std::string _v) { m__dirty = true; m__unnamed1 = std::move(_v); } + uint8_t active_track() const { return m_active_track; } + void set_active_track(uint8_t _v) { m__dirty = true; m_active_track = std::move(_v); } + std::string _unnamed3() const { return m__unnamed3; } + void set__unnamed3(std::string _v) { m__dirty = true; m__unnamed3 = std::move(_v); } + uint16_t master_tempo() const { return m_master_tempo; } + void set_master_tempo(uint16_t _v) { m__dirty = true; m_master_tempo = std::move(_v); } + bool tempo_source_is_sequence() const { return m_tempo_source_is_sequence; } + void set_tempo_source_is_sequence(bool _v) { m__dirty = true; m_tempo_source_is_sequence = std::move(_v); } + uint64_t _unnamed6() const { return m__unnamed6; } + void set__unnamed6(uint64_t _v) { m__dirty = true; m__unnamed6 = std::move(_v); } + timing_correct_t timing_correct() const { return m_timing_correct; } + void set_timing_correct(timing_correct_t _v) { m__dirty = true; m_timing_correct = std::move(_v); } + time_display_style_t time_display_style() const { return m_time_display_style; } + void set_time_display_style(time_display_style_t _v) { m__dirty = true; m_time_display_style = std::move(_v); } + uint64_t _unnamed9() const { return m__unnamed9; } + void set__unnamed9(uint64_t _v) { m__dirty = true; m__unnamed9 = std::move(_v); } + bool second_sequence_enabled() const { return m_second_sequence_enabled; } + void set_second_sequence_enabled(bool _v) { m__dirty = true; m_second_sequence_enabled = std::move(_v); } + uint64_t _unnamed11() const { return m__unnamed11; } + void set__unnamed11(uint64_t _v) { m__dirty = true; m__unnamed11 = std::move(_v); } + uint8_t sequence_sequence_index() const { return m_sequence_sequence_index; } + void set_sequence_sequence_index(uint8_t _v) { m__dirty = true; m_sequence_sequence_index = std::move(_v); } + mpc2000xl_all_t* _root() const { return m__root; } + void set__root(mpc2000xl_all_t* _v) { m__dirty = true; m__root = std::move(_v); } + kaitai::kstruct* _parent() const { return m__parent; } + void set__parent(mpc2000xl_all_t* _v) { m__dirty = true; m__parent = std::move(_v); } + + private: + uint8_t m_active_sequence; + std::string m__unnamed1; + uint8_t m_active_track; + std::string m__unnamed3; + uint16_t m_master_tempo; + bool m_tempo_source_is_sequence; + uint64_t m__unnamed6; + timing_correct_t m_timing_correct; + time_display_style_t m_time_display_style; + uint64_t m__unnamed9; + bool m_second_sequence_enabled; + uint64_t m__unnamed11; + uint8_t m_sequence_sequence_index; + mpc2000xl_all_t* m__root; + mpc2000xl_all_t* m__parent; + }; + + class song_t : public kaitai::kstruct { + + public: + + private: + bool m__dirty; + + public: + + song_t(kaitai::kstream* p__io, mpc2000xl_all_t* p__parent = nullptr, mpc2000xl_all_t* p__root = nullptr); + void _read(); + void _fetch_instances(); + void _write(); + void _check(); + ~song_t(); + std::string name() const { return m_name; } + void set_name(std::string _v) { m__dirty = true; m_name = std::move(_v); } + std::vector>* steps() const { return m_steps.get(); } + void set_steps(std::unique_ptr>> _v) { m__dirty = true; m_steps = std::move(_v); } + std::string _unnamed2() const { return m__unnamed2; } + void set__unnamed2(std::string _v) { m__dirty = true; m__unnamed2 = std::move(_v); } + bool is_used() const { return m_is_used; } + void set_is_used(bool _v) { m__dirty = true; m_is_used = std::move(_v); } + uint8_t loop_first_step() const { return m_loop_first_step; } + void set_loop_first_step(uint8_t _v) { m__dirty = true; m_loop_first_step = std::move(_v); } + uint8_t loop_last_step() const { return m_loop_last_step; } + void set_loop_last_step(uint8_t _v) { m__dirty = true; m_loop_last_step = std::move(_v); } + bool is_loop_enabled() const { return m_is_loop_enabled; } + void set_is_loop_enabled(bool _v) { m__dirty = true; m_is_loop_enabled = std::move(_v); } + std::string _unnamed7() const { return m__unnamed7; } + void set__unnamed7(std::string _v) { m__dirty = true; m__unnamed7 = std::move(_v); } + mpc2000xl_all_t* _root() const { return m__root; } + void set__root(mpc2000xl_all_t* _v) { m__dirty = true; m__root = std::move(_v); } + kaitai::kstruct* _parent() const { return m__parent; } + void set__parent(mpc2000xl_all_t* _v) { m__dirty = true; m__parent = std::move(_v); } + + private: + std::string m_name; + std::unique_ptr>> m_steps; + std::string m__unnamed2; + bool m_is_used; + uint8_t m_loop_first_step; + uint8_t m_loop_last_step; + bool m_is_loop_enabled; + std::string m__unnamed7; + mpc2000xl_all_t* m__root; + mpc2000xl_all_t* m__parent; + }; + + class song_global_t : public kaitai::kstruct { + + public: + + private: + bool m__dirty; + + public: + + song_global_t(kaitai::kstream* p__io, mpc2000xl_all_t* p__parent = nullptr, mpc2000xl_all_t* p__root = nullptr); + void _read(); + void _fetch_instances(); + void _write(); + void _check(); + ~song_global_t(); + std::string default_song_name() const { return m_default_song_name; } + void set_default_song_name(std::string _v) { m__dirty = true; m_default_song_name = std::move(_v); } + bool ignore_tempo_change_events_in_sequence() const { return m_ignore_tempo_change_events_in_sequence; } + void set_ignore_tempo_change_events_in_sequence(bool _v) { m__dirty = true; m_ignore_tempo_change_events_in_sequence = std::move(_v); } + uint64_t _unnamed2() const { return m__unnamed2; } + void set__unnamed2(uint64_t _v) { m__dirty = true; m__unnamed2 = std::move(_v); } + mpc2000xl_all_t* _root() const { return m__root; } + void set__root(mpc2000xl_all_t* _v) { m__dirty = true; m__root = std::move(_v); } + kaitai::kstruct* _parent() const { return m__parent; } + void set__parent(mpc2000xl_all_t* _v) { m__dirty = true; m__parent = std::move(_v); } + + private: + std::string m_default_song_name; + bool m_ignore_tempo_change_events_in_sequence; + uint64_t m__unnamed2; + mpc2000xl_all_t* m__root; + mpc2000xl_all_t* m__parent; + }; + + class song_step_t : public kaitai::kstruct { + + public: + + private: + bool m__dirty; + + public: + + song_step_t(kaitai::kstream* p__io, mpc2000xl_all_t::song_t* p__parent = nullptr, mpc2000xl_all_t* p__root = nullptr); + void _read(); + void _fetch_instances(); + void _write(); + void _check(); + ~song_step_t(); + uint8_t sequence_index() const { return m_sequence_index; } + void set_sequence_index(uint8_t _v) { m__dirty = true; m_sequence_index = std::move(_v); } + uint8_t repeat_count() const { return m_repeat_count; } + void set_repeat_count(uint8_t _v) { m__dirty = true; m_repeat_count = std::move(_v); } + mpc2000xl_all_t* _root() const { return m__root; } + void set__root(mpc2000xl_all_t* _v) { m__dirty = true; m__root = std::move(_v); } + kaitai::kstruct* _parent() const { return m__parent; } + void set__parent(mpc2000xl_all_t::song_t* _v) { m__dirty = true; m__parent = std::move(_v); } + + private: + uint8_t m_sequence_index; + uint8_t m_repeat_count; + mpc2000xl_all_t* m__root; + mpc2000xl_all_t::song_t* m__parent; + }; + + class step_edit_options_t : public kaitai::kstruct { + + public: + + private: + bool m__dirty; + + public: + + step_edit_options_t(kaitai::kstream* p__io, mpc2000xl_all_t* p__parent = nullptr, mpc2000xl_all_t* p__root = nullptr); + void _read(); + void _fetch_instances(); + void _write(); + void _check(); + ~step_edit_options_t(); + bool auto_step_increment() const { return m_auto_step_increment; } + void set_auto_step_increment(bool _v) { m__dirty = true; m_auto_step_increment = std::move(_v); } + duration_of_recorded_notes_t duration_of_recorded_notes() const { return m_duration_of_recorded_notes; } + void set_duration_of_recorded_notes(duration_of_recorded_notes_t _v) { m__dirty = true; m_duration_of_recorded_notes = std::move(_v); } + uint8_t tc_value_percentage() const { return m_tc_value_percentage; } + void set_tc_value_percentage(uint8_t _v) { m__dirty = true; m_tc_value_percentage = std::move(_v); } + mpc2000xl_all_t* _root() const { return m__root; } + void set__root(mpc2000xl_all_t* _v) { m__dirty = true; m__root = std::move(_v); } + kaitai::kstruct* _parent() const { return m__parent; } + void set__parent(mpc2000xl_all_t* _v) { m__dirty = true; m__parent = std::move(_v); } + + private: + bool m_auto_step_increment; + duration_of_recorded_notes_t m_duration_of_recorded_notes; + uint8_t m_tc_value_percentage; + mpc2000xl_all_t* m__root; + mpc2000xl_all_t* m__parent; + }; + + class track_status_t : public kaitai::kstruct { + + public: + + private: + bool m__dirty; + + public: + + track_status_t(kaitai::kstream* p__io, kaitai::kstruct* p__parent = nullptr, mpc2000xl_all_t* p__root = nullptr); + void _read(); + void _fetch_instances(); + void _write(); + void _check(); + ~track_status_t(); + unused_used_t unused_or_used() const { return m_unused_or_used; } + void set_unused_or_used(unused_used_t _v) { m__dirty = true; m_unused_or_used = std::move(_v); } + off_on_t off_or_on() const { return m_off_or_on; } + void set_off_or_on(off_on_t _v) { m__dirty = true; m_off_or_on = std::move(_v); } + off_on_t transmit_program_changes() const { return m_transmit_program_changes; } + void set_transmit_program_changes(off_on_t _v) { m__dirty = true; m_transmit_program_changes = std::move(_v); } + uint64_t _unnamed3() const { return m__unnamed3; } + void set__unnamed3(uint64_t _v) { m__dirty = true; m__unnamed3 = std::move(_v); } + mpc2000xl_all_t* _root() const { return m__root; } + void set__root(mpc2000xl_all_t* _v) { m__dirty = true; m__root = std::move(_v); } + kaitai::kstruct* _parent() const { return m__parent; } + void set__parent(kaitai::kstruct* _v) { m__dirty = true; m__parent = std::move(_v); } + + private: + unused_used_t m_unused_or_used; + off_on_t m_off_or_on; + off_on_t m_transmit_program_changes; + uint64_t m__unnamed3; + mpc2000xl_all_t* m__root; + kaitai::kstruct* m__parent; + }; + + class tracks_t : public kaitai::kstruct { + + public: + + private: + bool m__dirty; + + public: + + tracks_t(kaitai::kstream* p__io, mpc2000xl_all_t::sequence_body_t* p__parent = nullptr, mpc2000xl_all_t* p__root = nullptr); + void _read(); + void _fetch_instances(); + void _write(); + void _check(); + ~tracks_t(); + std::vector* names() const { return m_names.get(); } + void set_names(std::unique_ptr> _v) { m__dirty = true; m_names = std::move(_v); } + std::vector* device() const { return m_device.get(); } + void set_device(std::unique_ptr> _v) { m__dirty = true; m_device = std::move(_v); } + std::vector* bus() const { return m_bus.get(); } + void set_bus(std::unique_ptr> _v) { m__dirty = true; m_bus = std::move(_v); } + std::vector* program_change() const { return m_program_change.get(); } + void set_program_change(std::unique_ptr> _v) { m__dirty = true; m_program_change = std::move(_v); } + std::vector* velocity_ratio() const { return m_velocity_ratio.get(); } + void set_velocity_ratio(std::unique_ptr> _v) { m__dirty = true; m_velocity_ratio = std::move(_v); } + std::vector>* status() const { return m_status.get(); } + void set_status(std::unique_ptr>> _v) { m__dirty = true; m_status = std::move(_v); } + std::string unknown() const { return m_unknown; } + void set_unknown(std::string _v) { m__dirty = true; m_unknown = std::move(_v); } + mpc2000xl_all_t* _root() const { return m__root; } + void set__root(mpc2000xl_all_t* _v) { m__dirty = true; m__root = std::move(_v); } + kaitai::kstruct* _parent() const { return m__parent; } + void set__parent(mpc2000xl_all_t::sequence_body_t* _v) { m__dirty = true; m__parent = std::move(_v); } + + private: + std::unique_ptr> m_names; + std::unique_ptr> m_device; + std::unique_ptr> m_bus; + std::unique_ptr> m_program_change; + std::unique_ptr> m_velocity_ratio; + std::unique_ptr>> m_status; + std::string m_unknown; + mpc2000xl_all_t* m__root; + mpc2000xl_all_t::sequence_body_t* m__parent; + }; + +public: + std::string magic() const { return m_magic; } + void set_magic(std::string _v) { m__dirty = true; m_magic = std::move(_v); } + defaults_t* defaults() const { return m_defaults.get(); } + void set_defaults(std::unique_ptr _v) { m__dirty = true; m_defaults = std::move(_v); } + sequencer_t* sequencer() const { return m_sequencer.get(); } + void set_sequencer(std::unique_ptr _v) { m__dirty = true; m_sequencer = std::move(_v); } + std::string _unnamed3() const { return m__unnamed3; } + void set__unnamed3(std::string _v) { m__dirty = true; m__unnamed3 = std::move(_v); } + count_t* count() const { return m_count.get(); } + void set_count(std::unique_ptr _v) { m__dirty = true; m_count = std::move(_v); } + midi_output_t* midi_output() const { return m_midi_output.get(); } + void set_midi_output(std::unique_ptr _v) { m__dirty = true; m_midi_output = std::move(_v); } + midi_input_t* midi_input() const { return m_midi_input.get(); } + void set_midi_input(std::unique_ptr _v) { m__dirty = true; m_midi_input = std::move(_v); } + midi_sync_t* midi_sync() const { return m_midi_sync.get(); } + void set_midi_sync(std::unique_ptr _v) { m__dirty = true; m_midi_sync = std::move(_v); } + song_global_t* song_global() const { return m_song_global.get(); } + void set_song_global(std::unique_ptr _v) { m__dirty = true; m_song_global = std::move(_v); } + std::string _unnamed9() const { return m__unnamed9; } + void set__unnamed9(std::string _v) { m__dirty = true; m__unnamed9 = std::move(_v); } + std::vector>* locations() const { return m_locations.get(); } + void set_locations(std::unique_ptr>> _v) { m__dirty = true; m_locations = std::move(_v); } + misc_t* misc() const { return m_misc.get(); } + void set_misc(std::unique_ptr _v) { m__dirty = true; m_misc = std::move(_v); } + std::string _unnamed12() const { return m__unnamed12; } + void set__unnamed12(std::string _v) { m__dirty = true; m__unnamed12 = std::move(_v); } + step_edit_options_t* step_edit_options() const { return m_step_edit_options.get(); } + void set_step_edit_options(std::unique_ptr _v) { m__dirty = true; m_step_edit_options = std::move(_v); } + bool prog_change_to_seq() const { return m_prog_change_to_seq; } + void set_prog_change_to_seq(bool _v) { m__dirty = true; m_prog_change_to_seq = std::move(_v); } + std::string _unnamed15() const { return m__unnamed15; } + void set__unnamed15(std::string _v) { m__dirty = true; m__unnamed15 = std::move(_v); } + std::vector>* sequences_metas() const { return m_sequences_metas.get(); } + void set_sequences_metas(std::unique_ptr>> _v) { m__dirty = true; m_sequences_metas = std::move(_v); } + std::vector>* songs() const { return m_songs.get(); } + void set_songs(std::unique_ptr>> _v) { m__dirty = true; m_songs = std::move(_v); } + std::vector>* sequences() const { return m_sequences.get(); } + void set_sequences(std::unique_ptr>> _v) { m__dirty = true; m_sequences = std::move(_v); } + mpc2000xl_all_t* _root() const { return m__root; } + void set__root(mpc2000xl_all_t* _v) { m__dirty = true; m__root = std::move(_v); } + kaitai::kstruct* _parent() const { return m__parent; } + void set__parent(kaitai::kstruct* _v) { m__dirty = true; m__parent = std::move(_v); } + +private: + std::string m_magic; + std::unique_ptr m_defaults; + std::unique_ptr m_sequencer; + std::string m__unnamed3; + std::unique_ptr m_count; + std::unique_ptr m_midi_output; + std::unique_ptr m_midi_input; + std::unique_ptr m_midi_sync; + std::unique_ptr m_song_global; + std::string m__unnamed9; + std::unique_ptr>> m_locations; + std::unique_ptr m_misc; + std::string m__unnamed12; + std::unique_ptr m_step_edit_options; + bool m_prog_change_to_seq; + std::string m__unnamed15; + std::unique_ptr>> m_sequences_metas; + std::unique_ptr>> m_songs; + std::unique_ptr>> m_sequences; + mpc2000xl_all_t* m__root; + kaitai::kstruct* m__parent; +}; diff --git a/src/main/file/kaitai/generated/mpc2000xl_aps.cpp b/src/main/file/kaitai/generated/mpc2000xl_aps.cpp new file mode 100644 index 000000000..8074ff3c4 --- /dev/null +++ b/src/main/file/kaitai/generated/mpc2000xl_aps.cpp @@ -0,0 +1,732 @@ +// This is a generated file! Please edit source .ksy file and use kaitai-struct-compiler to rebuild + +#include "mpc2000xl_aps.h" +#include "kaitai/exceptions.h" +#include +#include +const std::set mpc2000xl_aps_t::_values_mix_source_t{ + mpc2000xl_aps_t::MIX_SOURCE_PROGRAM, + mpc2000xl_aps_t::MIX_SOURCE_DRUM, +}; +bool mpc2000xl_aps_t::_is_defined_mix_source_t(mpc2000xl_aps_t::mix_source_t v) { + return mpc2000xl_aps_t::_values_mix_source_t.find(v) != mpc2000xl_aps_t::_values_mix_source_t.end(); +} +const std::set mpc2000xl_aps_t::_values_no_yes_t{ + mpc2000xl_aps_t::NO_YES_FALSE, + mpc2000xl_aps_t::NO_YES_TRUE, +}; +bool mpc2000xl_aps_t::_is_defined_no_yes_t(mpc2000xl_aps_t::no_yes_t v) { + return mpc2000xl_aps_t::_values_no_yes_t.find(v) != mpc2000xl_aps_t::_values_no_yes_t.end(); +} +const std::set mpc2000xl_aps_t::_values_pad_assign_t{ + mpc2000xl_aps_t::PAD_ASSIGN_PROGRAM, + mpc2000xl_aps_t::PAD_ASSIGN_MASTERS, +}; +bool mpc2000xl_aps_t::_is_defined_pad_assign_t(mpc2000xl_aps_t::pad_assign_t v) { + return mpc2000xl_aps_t::_values_pad_assign_t.find(v) != mpc2000xl_aps_t::_values_pad_assign_t.end(); +} + +mpc2000xl_aps_t::mpc2000xl_aps_t(kaitai::kstream* p__io, kaitai::kstruct* p__parent, mpc2000xl_aps_t* p__root) : kaitai::kstruct(p__io) { + m__parent = p__parent; + m__root = p__root ? p__root : this; + m__dirty = false; +} + +void mpc2000xl_aps_t::_read() { + m_magic = m__io->read_bytes(2); + if (!(m_magic == std::string("\x0A\x05", 2))) { + throw kaitai::validation_not_equal_error(std::string("\x0A\x05", 2), m_magic, m__io, std::string("/seq/0")); + } + m_sound_count = m__io->read_u2le(); + m_sound_names = std::unique_ptr>(new std::vector()); + const int l_sound_names = sound_count(); + for (int i = 0; i < l_sound_names; i++) { + m_sound_names->push_back(std::move(kaitai::kstream::bytes_to_str(m__io->read_bytes(17), "ASCII"))); + } + m__unnamed3 = m__io->read_bytes(2); + m_name = kaitai::kstream::bytes_to_str(m__io->read_bytes(17), "ASCII"); + m_global_parameters = std::unique_ptr(new global_parameters_t(m__io, this, m__root)); + m_global_parameters->_read(); + m__unnamed6 = m__io->read_bytes(1); + m_master_pad_to_note_mapping = std::unique_ptr>(new std::vector()); + const int l_master_pad_to_note_mapping = 64; + for (int i = 0; i < l_master_pad_to_note_mapping; i++) { + m_master_pad_to_note_mapping->push_back(std::move(m__io->read_s1())); + } + m__unnamed8 = m__io->read_bytes(7); + m_drum1 = std::unique_ptr(new drum_t(m__io, this, m__root)); + m_drum1->_read(); + m__unnamed10 = m__io->read_bytes(4); + m_drum2 = std::unique_ptr(new drum_t(m__io, this, m__root)); + m_drum2->_read(); + m__unnamed12 = m__io->read_bytes(4); + m_drum3 = std::unique_ptr(new drum_t(m__io, this, m__root)); + m_drum3->_read(); + m__unnamed14 = m__io->read_bytes(4); + m_drum4 = std::unique_ptr(new drum_t(m__io, this, m__root)); + m_drum4->_read(); + m__unnamed16 = m__io->read_bytes(1); + m_aps_programs = std::unique_ptr>>(new std::vector>()); + { + int i = 0; + while (!m__io->is_eof()) { + std::unique_ptr _t_aps_programs = std::unique_ptr(new aps_program_meta_t(m__io, this, m__root)); + try { + _t_aps_programs->_read(); + } catch(...) { + m_aps_programs->push_back(std::move(_t_aps_programs)); + throw; + } + m_aps_programs->push_back(std::move(_t_aps_programs)); + i++; + } + } + m__dirty = false; +} + +void mpc2000xl_aps_t::_fetch_instances() { + for (std::size_t i = 0; i < m_sound_names->size(); ++i) { + } + m_global_parameters.get()->_fetch_instances(); + for (std::size_t i = 0; i < m_master_pad_to_note_mapping->size(); ++i) { + } + m_drum1.get()->_fetch_instances(); + m_drum2.get()->_fetch_instances(); + m_drum3.get()->_fetch_instances(); + m_drum4.get()->_fetch_instances(); + for (std::size_t i = 0; i < m_aps_programs->size(); ++i) { + m_aps_programs->at(i).get()->_fetch_instances(); + } +} + +void mpc2000xl_aps_t::_write() { + m__io->write_bytes(m_magic); + m__io->write_u2le(m_sound_count); + if (m_sound_names == nullptr) { + throw std::runtime_error("/seq/2: repeated field is not set"); + } + for (std::vector::const_iterator it = m_sound_names->begin(); it != m_sound_names->end(); ++it) { + m__io->write_bytes((*it)); + } + m__io->write_bytes(m__unnamed3); + m__io->write_bytes(m_name); + if (m_global_parameters.get() == nullptr) { + throw std::runtime_error("/seq/5: nested object is not set"); + } + m_global_parameters.get()->_set_io(m__io); + m_global_parameters.get()->_write(); + m__io->write_bytes(m__unnamed6); + if (m_master_pad_to_note_mapping == nullptr) { + throw std::runtime_error("/seq/7: repeated field is not set"); + } + for (std::vector::const_iterator it = m_master_pad_to_note_mapping->begin(); it != m_master_pad_to_note_mapping->end(); ++it) { + m__io->write_s1((*it)); + } + m__io->write_bytes(m__unnamed8); + if (m_drum1.get() == nullptr) { + throw std::runtime_error("/seq/9: nested object is not set"); + } + m_drum1.get()->_set_io(m__io); + m_drum1.get()->_write(); + m__io->write_bytes(m__unnamed10); + if (m_drum2.get() == nullptr) { + throw std::runtime_error("/seq/11: nested object is not set"); + } + m_drum2.get()->_set_io(m__io); + m_drum2.get()->_write(); + m__io->write_bytes(m__unnamed12); + if (m_drum3.get() == nullptr) { + throw std::runtime_error("/seq/13: nested object is not set"); + } + m_drum3.get()->_set_io(m__io); + m_drum3.get()->_write(); + m__io->write_bytes(m__unnamed14); + if (m_drum4.get() == nullptr) { + throw std::runtime_error("/seq/15: nested object is not set"); + } + m_drum4.get()->_set_io(m__io); + m_drum4.get()->_write(); + m__io->write_bytes(m__unnamed16); + if (m_aps_programs == nullptr) { + throw std::runtime_error("/seq/17: repeated field is not set"); + } + if (m_aps_programs == nullptr) { + throw std::runtime_error("/seq/17: repeated field is not set"); + } + for (std::vector>::const_iterator it = m_aps_programs->begin(); it != m_aps_programs->end(); ++it) { + if ((*it).get() == nullptr) { + throw std::runtime_error("/seq/17: nested object is not set"); + } + (*it).get()->_set_io(m__io); + (*it).get()->_write(); + } + _fetch_instances(); + m__dirty = false; +} + +void mpc2000xl_aps_t::_check() { + if (m_magic.size() != static_cast(2)) { + throw std::runtime_error("/seq/0: size mismatch"); + } + if (!(m_magic == std::string("\x0A\x05", 2))) { + throw kaitai::validation_not_equal_error(std::string("\x0A\x05", 2), m_magic, m__io, std::string("/seq/0")); + } + if (m_sound_names == nullptr) { + throw std::runtime_error("/seq/2: repeated field is not set"); + } + if (m_sound_names->size() != static_cast(sound_count())) { + throw std::runtime_error("/seq/2: repeat-expr size mismatch"); + } + for (std::vector::const_iterator it = m_sound_names->begin(); it != m_sound_names->end(); ++it) { + if ((*it).size() != static_cast(17)) { + throw std::runtime_error("/seq/2: size mismatch"); + } + } + if (m__unnamed3.size() != static_cast(2)) { + throw std::runtime_error("/seq/3: size mismatch"); + } + if (m_name.size() != static_cast(17)) { + throw std::runtime_error("/seq/4: size mismatch"); + } + if (m_global_parameters.get() == nullptr) { + throw std::runtime_error("/seq/5: nested object is not set"); + } + m_global_parameters.get()->_set_io(m__io); + m_global_parameters.get()->_check(); + if (m__unnamed6.size() != static_cast(1)) { + throw std::runtime_error("/seq/6: size mismatch"); + } + if (m_master_pad_to_note_mapping == nullptr) { + throw std::runtime_error("/seq/7: repeated field is not set"); + } + if (m_master_pad_to_note_mapping->size() != static_cast(64)) { + throw std::runtime_error("/seq/7: repeat-expr size mismatch"); + } + for (std::vector::const_iterator it = m_master_pad_to_note_mapping->begin(); it != m_master_pad_to_note_mapping->end(); ++it) { + } + if (m__unnamed8.size() != static_cast(7)) { + throw std::runtime_error("/seq/8: size mismatch"); + } + if (m_drum1.get() == nullptr) { + throw std::runtime_error("/seq/9: nested object is not set"); + } + m_drum1.get()->_set_io(m__io); + m_drum1.get()->_check(); + if (m__unnamed10.size() != static_cast(4)) { + throw std::runtime_error("/seq/10: size mismatch"); + } + if (m_drum2.get() == nullptr) { + throw std::runtime_error("/seq/11: nested object is not set"); + } + m_drum2.get()->_set_io(m__io); + m_drum2.get()->_check(); + if (m__unnamed12.size() != static_cast(4)) { + throw std::runtime_error("/seq/12: size mismatch"); + } + if (m_drum3.get() == nullptr) { + throw std::runtime_error("/seq/13: nested object is not set"); + } + m_drum3.get()->_set_io(m__io); + m_drum3.get()->_check(); + if (m__unnamed14.size() != static_cast(4)) { + throw std::runtime_error("/seq/14: size mismatch"); + } + if (m_drum4.get() == nullptr) { + throw std::runtime_error("/seq/15: nested object is not set"); + } + m_drum4.get()->_set_io(m__io); + m_drum4.get()->_check(); + if (m__unnamed16.size() != static_cast(1)) { + throw std::runtime_error("/seq/16: size mismatch"); + } + if (m_aps_programs == nullptr) { + throw std::runtime_error("/seq/17: repeated field is not set"); + } + for (std::vector>::const_iterator it = m_aps_programs->begin(); it != m_aps_programs->end(); ++it) { + const std::size_t i = static_cast(it - m_aps_programs->begin()); + const aps_program_meta_t* _ = (*it).get(); + if ((*it).get() == nullptr) { + throw std::runtime_error("/seq/17: nested object is not set"); + } + (*it).get()->_set_io(m__io); + (*it).get()->_check(); + } + m__dirty = false; +} + +mpc2000xl_aps_t::~mpc2000xl_aps_t() {} + +mpc2000xl_aps_t::aps_program_body_t::aps_program_body_t(kaitai::kstream* p__io, mpc2000xl_aps_t::aps_program_meta_t* p__parent, mpc2000xl_aps_t* p__root) : kaitai::kstruct(p__io) { + m__parent = p__parent; + m__root = p__root; + m__dirty = false; +} + +void mpc2000xl_aps_t::aps_program_body_t::_read() { + m__unnamed0 = m__io->read_bytes(5); + m_name = kaitai::kstream::bytes_to_str(m__io->read_bytes(17), "ASCII"); + m_slider = std::unique_ptr(new mpc2000xl_pgm_t::slider_t(m__io)); + m_slider->_read(); + m_program_change = m__io->read_u1(); + m__unnamed4 = m__io->read_bytes(5); + m_note_parameters = std::unique_ptr>>(new std::vector>()); + const int l_note_parameters = 64; + for (int i = 0; i < l_note_parameters; i++) { + std::unique_ptr _t_note_parameters = std::unique_ptr(new note_t(m__io, this, m__root)); + try { + _t_note_parameters->_read(); + } catch(...) { + m_note_parameters->push_back(std::move(_t_note_parameters)); + throw; + } + m_note_parameters->push_back(std::move(_t_note_parameters)); + } + m__unnamed6 = m__io->read_bytes(1); + m_pad_mixers = std::unique_ptr>>(new std::vector>()); + const int l_pad_mixers = 64; + for (int i = 0; i < l_pad_mixers; i++) { + std::unique_ptr _t_pad_mixers = std::unique_ptr(new mpc2000xl_pgm_t::pad_mixer_t(m__io)); + try { + _t_pad_mixers->_read(); + } catch(...) { + m_pad_mixers->push_back(std::move(_t_pad_mixers)); + throw; + } + m_pad_mixers->push_back(std::move(_t_pad_mixers)); + } + m__unnamed8 = m__io->read_bytes(3); + m_pad_to_note_mapping = std::unique_ptr>(new std::vector()); + const int l_pad_to_note_mapping = 64; + for (int i = 0; i < l_pad_to_note_mapping; i++) { + m_pad_to_note_mapping->push_back(std::move(m__io->read_s1())); + } + m__unnamed10 = m__io->read_bytes(200); + m__dirty = false; +} + +void mpc2000xl_aps_t::aps_program_body_t::_fetch_instances() { + m_slider.get()->_fetch_instances(); + for (std::size_t i = 0; i < m_note_parameters->size(); ++i) { + m_note_parameters->at(i).get()->_fetch_instances(); + } + for (std::size_t i = 0; i < m_pad_mixers->size(); ++i) { + m_pad_mixers->at(i).get()->_fetch_instances(); + } + for (std::size_t i = 0; i < m_pad_to_note_mapping->size(); ++i) { + } +} + +void mpc2000xl_aps_t::aps_program_body_t::_write() { + m__io->write_bytes(m__unnamed0); + m__io->write_bytes(m_name); + if (m_slider.get() == nullptr) { + throw std::runtime_error("/types/aps_program_body/seq/2: nested object is not set"); + } + m_slider.get()->_set_io(m__io); + m_slider.get()->_write(); + m__io->write_u1(m_program_change); + m__io->write_bytes(m__unnamed4); + if (m_note_parameters == nullptr) { + throw std::runtime_error("/types/aps_program_body/seq/5: repeated field is not set"); + } + for (std::vector>::const_iterator it = m_note_parameters->begin(); it != m_note_parameters->end(); ++it) { + if ((*it).get() == nullptr) { + throw std::runtime_error("/types/aps_program_body/seq/5: nested object is not set"); + } + (*it).get()->_set_io(m__io); + (*it).get()->_write(); + } + m__io->write_bytes(m__unnamed6); + if (m_pad_mixers == nullptr) { + throw std::runtime_error("/types/aps_program_body/seq/7: repeated field is not set"); + } + for (std::vector>::const_iterator it = m_pad_mixers->begin(); it != m_pad_mixers->end(); ++it) { + if ((*it).get() == nullptr) { + throw std::runtime_error("/types/aps_program_body/seq/7: nested object is not set"); + } + (*it).get()->_set_io(m__io); + (*it).get()->_write(); + } + m__io->write_bytes(m__unnamed8); + if (m_pad_to_note_mapping == nullptr) { + throw std::runtime_error("/types/aps_program_body/seq/9: repeated field is not set"); + } + for (std::vector::const_iterator it = m_pad_to_note_mapping->begin(); it != m_pad_to_note_mapping->end(); ++it) { + m__io->write_s1((*it)); + } + m__io->write_bytes(m__unnamed10); + _fetch_instances(); + m__dirty = false; +} + +void mpc2000xl_aps_t::aps_program_body_t::_check() { + if (m__unnamed0.size() != static_cast(5)) { + throw std::runtime_error("/types/aps_program_body/seq/0: size mismatch"); + } + if (m_name.size() != static_cast(17)) { + throw std::runtime_error("/types/aps_program_body/seq/1: size mismatch"); + } + if (m_slider.get() == nullptr) { + throw std::runtime_error("/types/aps_program_body/seq/2: nested object is not set"); + } + m_slider.get()->_set_io(m__io); + m_slider.get()->_check(); + if (m__unnamed4.size() != static_cast(5)) { + throw std::runtime_error("/types/aps_program_body/seq/4: size mismatch"); + } + if (m_note_parameters == nullptr) { + throw std::runtime_error("/types/aps_program_body/seq/5: repeated field is not set"); + } + if (m_note_parameters->size() != static_cast(64)) { + throw std::runtime_error("/types/aps_program_body/seq/5: repeat-expr size mismatch"); + } + for (std::vector>::const_iterator it = m_note_parameters->begin(); it != m_note_parameters->end(); ++it) { + if ((*it).get() == nullptr) { + throw std::runtime_error("/types/aps_program_body/seq/5: nested object is not set"); + } + (*it).get()->_set_io(m__io); + (*it).get()->_check(); + } + if (m__unnamed6.size() != static_cast(1)) { + throw std::runtime_error("/types/aps_program_body/seq/6: size mismatch"); + } + if (m_pad_mixers == nullptr) { + throw std::runtime_error("/types/aps_program_body/seq/7: repeated field is not set"); + } + if (m_pad_mixers->size() != static_cast(64)) { + throw std::runtime_error("/types/aps_program_body/seq/7: repeat-expr size mismatch"); + } + for (std::vector>::const_iterator it = m_pad_mixers->begin(); it != m_pad_mixers->end(); ++it) { + if ((*it).get() == nullptr) { + throw std::runtime_error("/types/aps_program_body/seq/7: nested object is not set"); + } + (*it).get()->_set_io(m__io); + (*it).get()->_check(); + } + if (m__unnamed8.size() != static_cast(3)) { + throw std::runtime_error("/types/aps_program_body/seq/8: size mismatch"); + } + if (m_pad_to_note_mapping == nullptr) { + throw std::runtime_error("/types/aps_program_body/seq/9: repeated field is not set"); + } + if (m_pad_to_note_mapping->size() != static_cast(64)) { + throw std::runtime_error("/types/aps_program_body/seq/9: repeat-expr size mismatch"); + } + for (std::vector::const_iterator it = m_pad_to_note_mapping->begin(); it != m_pad_to_note_mapping->end(); ++it) { + } + if (m__unnamed10.size() != static_cast(200)) { + throw std::runtime_error("/types/aps_program_body/seq/10: size mismatch"); + } + m__dirty = false; +} + +mpc2000xl_aps_t::aps_program_body_t::~aps_program_body_t() {} + +mpc2000xl_aps_t::aps_program_meta_t::aps_program_meta_t(kaitai::kstream* p__io, mpc2000xl_aps_t* p__parent, mpc2000xl_aps_t* p__root) : kaitai::kstruct(p__io) { + m__parent = p__parent; + m__root = p__root; + m__dirty = false; +} + +void mpc2000xl_aps_t::aps_program_meta_t::_read() { + m_index = m__io->read_u1(); + if (index() < 24) { + m_body = std::unique_ptr(new aps_program_body_t(m__io, this, m__root)); + m_body->_read(); + } + if (index() > 23) { + m__unnamed2 = std::unique_ptr>(new std::vector()); + { + int i = 0; + while (!m__io->is_eof()) { + m__unnamed2->push_back(std::move(m__io->read_u1())); + i++; + } + } + } + m__dirty = false; +} + +void mpc2000xl_aps_t::aps_program_meta_t::_fetch_instances() { + if (index() < 24) { + m_body.get()->_fetch_instances(); + } + if (index() > 23) { + for (std::size_t i = 0; i < m__unnamed2->size(); ++i) { + } + } +} + +void mpc2000xl_aps_t::aps_program_meta_t::_write() { + m__io->write_u1(m_index); + if (index() < 24) { + if (m_body.get() == nullptr) { + throw std::runtime_error("/types/aps_program_meta/seq/1: nested object is not set"); + } + m_body.get()->_set_io(m__io); + m_body.get()->_write(); + } + if (index() > 23) { + if (m__unnamed2 == nullptr) { + throw std::runtime_error("/types/aps_program_meta/seq/2: repeated field is not set"); + } + const std::size_t _remaining = static_cast(m__io->size() - m__io->pos()); + const std::size_t _actual = m__unnamed2->size() * static_cast(1); + if (_remaining != 0 && _remaining != _actual) { + throw std::runtime_error("/types/aps_program_meta/seq/2: expected: " + kaitai::kstream::to_string(_remaining) + ", actual: " + kaitai::kstream::to_string(_actual)); + } + if (m__unnamed2 == nullptr) { + throw std::runtime_error("/types/aps_program_meta/seq/2: repeated field is not set"); + } + for (std::vector::const_iterator it = m__unnamed2->begin(); it != m__unnamed2->end(); ++it) { + m__io->write_u1((*it)); + } + } + _fetch_instances(); + m__dirty = false; +} + +void mpc2000xl_aps_t::aps_program_meta_t::_check() { + if (index() < 24) { + if (!(m_body.get() != nullptr)) { + throw std::runtime_error("/types/aps_program_meta/seq/1: conditional field is not set"); + } + if (m_body.get() == nullptr) { + throw std::runtime_error("/types/aps_program_meta/seq/1: nested object is not set"); + } + m_body.get()->_set_io(m__io); + m_body.get()->_check(); + } else { + if (m_body.get() != nullptr) { + throw std::runtime_error("/types/aps_program_meta/seq/1: conditional field should be absent"); + } + } + if (index() > 23) { + if (!(m__unnamed2 != nullptr)) { + throw std::runtime_error("/types/aps_program_meta/seq/2: conditional field is not set"); + } + if (m__unnamed2 == nullptr) { + throw std::runtime_error("/types/aps_program_meta/seq/2: repeated field is not set"); + } + for (std::vector::const_iterator it = m__unnamed2->begin(); it != m__unnamed2->end(); ++it) { + const std::size_t i = static_cast(it - m__unnamed2->begin()); + const uint8_t _ = (*it); + } + } else { + if (m__unnamed2 != nullptr) { + throw std::runtime_error("/types/aps_program_meta/seq/2: conditional field should be absent"); + } + } + m__dirty = false; +} + +mpc2000xl_aps_t::aps_program_meta_t::~aps_program_meta_t() {} + +mpc2000xl_aps_t::drum_t::drum_t(kaitai::kstream* p__io, mpc2000xl_aps_t* p__parent, mpc2000xl_aps_t* p__root) : kaitai::kstruct(p__io) { + m__parent = p__parent; + m__root = p__root; + m__dirty = false; +} + +void mpc2000xl_aps_t::drum_t::_read() { + m_pad_mixers = std::unique_ptr>>(new std::vector>()); + const int l_pad_mixers = 64; + for (int i = 0; i < l_pad_mixers; i++) { + std::unique_ptr _t_pad_mixers = std::unique_ptr(new mpc2000xl_pgm_t::pad_mixer_t(m__io)); + try { + _t_pad_mixers->_read(); + } catch(...) { + m_pad_mixers->push_back(std::move(_t_pad_mixers)); + throw; + } + m_pad_mixers->push_back(std::move(_t_pad_mixers)); + } + m__unnamed1 = m__io->read_bytes(2); + m_receive_program_change = static_cast(m__io->read_u1()); + m_receive_midi_volume = static_cast(m__io->read_u1()); + m__unnamed4 = m__io->read_bytes(1); + m_program = m__io->read_u1(); + m_receive_program_change_duplicate = static_cast(m__io->read_u1()); + m_receive_midi_volume_duplicate = static_cast(m__io->read_u1()); + m__dirty = false; +} + +void mpc2000xl_aps_t::drum_t::_fetch_instances() { + for (std::size_t i = 0; i < m_pad_mixers->size(); ++i) { + m_pad_mixers->at(i).get()->_fetch_instances(); + } +} + +void mpc2000xl_aps_t::drum_t::_write() { + if (m_pad_mixers == nullptr) { + throw std::runtime_error("/types/drum/seq/0: repeated field is not set"); + } + for (std::vector>::const_iterator it = m_pad_mixers->begin(); it != m_pad_mixers->end(); ++it) { + if ((*it).get() == nullptr) { + throw std::runtime_error("/types/drum/seq/0: nested object is not set"); + } + (*it).get()->_set_io(m__io); + (*it).get()->_write(); + } + m__io->write_bytes(m__unnamed1); + m__io->write_u1(static_cast(m_receive_program_change)); + m__io->write_u1(static_cast(m_receive_midi_volume)); + m__io->write_bytes(m__unnamed4); + m__io->write_u1(m_program); + m__io->write_u1(static_cast(m_receive_program_change_duplicate)); + m__io->write_u1(static_cast(m_receive_midi_volume_duplicate)); + _fetch_instances(); + m__dirty = false; +} + +void mpc2000xl_aps_t::drum_t::_check() { + if (m_pad_mixers == nullptr) { + throw std::runtime_error("/types/drum/seq/0: repeated field is not set"); + } + if (m_pad_mixers->size() != static_cast(64)) { + throw std::runtime_error("/types/drum/seq/0: repeat-expr size mismatch"); + } + for (std::vector>::const_iterator it = m_pad_mixers->begin(); it != m_pad_mixers->end(); ++it) { + if ((*it).get() == nullptr) { + throw std::runtime_error("/types/drum/seq/0: nested object is not set"); + } + (*it).get()->_set_io(m__io); + (*it).get()->_check(); + } + if (m__unnamed1.size() != static_cast(2)) { + throw std::runtime_error("/types/drum/seq/1: size mismatch"); + } + if (m__unnamed4.size() != static_cast(1)) { + throw std::runtime_error("/types/drum/seq/4: size mismatch"); + } + m__dirty = false; +} + +mpc2000xl_aps_t::drum_t::~drum_t() {} + +mpc2000xl_aps_t::global_parameters_t::global_parameters_t(kaitai::kstream* p__io, mpc2000xl_aps_t* p__parent, mpc2000xl_aps_t* p__root) : kaitai::kstruct(p__io) { + m__parent = p__parent; + m__root = p__root; + m__dirty = false; +} + +void mpc2000xl_aps_t::global_parameters_t::_read() { + m_pad_to_internal_sound = static_cast(m__io->read_bits_int_le(1)); + m__unnamed1 = m__io->read_bits_int_le(7); + m_pad_assign = static_cast(m__io->read_bits_int_le(1)); + m__unnamed3 = m__io->read_bits_int_le(7); + m_stereo_mix_source = static_cast(m__io->read_bits_int_le(1)); + m_indiv_fx_source = static_cast(m__io->read_bits_int_le(1)); + m__unnamed6 = m__io->read_bits_int_le(6); + m_copy_pgm_mix_to_drum = static_cast(m__io->read_bits_int_le(1)); + m__unnamed8 = m__io->read_bits_int_le(3); + m_record_mix_changes = static_cast(m__io->read_bits_int_le(1)); + m__unnamed10 = m__io->read_bits_int_le(3); + m_fx_drum = m__io->read_u1(); + m__unnamed12 = m__io->read_bytes(1); + m_master_level = m__io->read_u1(); + m__dirty = false; +} + +void mpc2000xl_aps_t::global_parameters_t::_fetch_instances() { +} + +void mpc2000xl_aps_t::global_parameters_t::_write() { + m__io->write_bits_int_le(1, static_cast(m_pad_to_internal_sound)); + m__io->write_bits_int_le(7, m__unnamed1); + m__io->write_bits_int_le(1, static_cast(m_pad_assign)); + m__io->write_bits_int_le(7, m__unnamed3); + m__io->write_bits_int_le(1, static_cast(m_stereo_mix_source)); + m__io->write_bits_int_le(1, static_cast(m_indiv_fx_source)); + m__io->write_bits_int_le(6, m__unnamed6); + m__io->write_bits_int_le(1, static_cast(m_copy_pgm_mix_to_drum)); + m__io->write_bits_int_le(3, m__unnamed8); + m__io->write_bits_int_le(1, static_cast(m_record_mix_changes)); + m__io->write_bits_int_le(3, m__unnamed10); + m__io->write_u1(m_fx_drum); + m__io->write_bytes(m__unnamed12); + m__io->write_u1(m_master_level); + _fetch_instances(); + m__dirty = false; +} + +void mpc2000xl_aps_t::global_parameters_t::_check() { + if (m__unnamed12.size() != static_cast(1)) { + throw std::runtime_error("/types/global_parameters/seq/12: size mismatch"); + } + m__dirty = false; +} + +mpc2000xl_aps_t::global_parameters_t::~global_parameters_t() {} + +mpc2000xl_aps_t::note_t::note_t(kaitai::kstream* p__io, mpc2000xl_aps_t::aps_program_body_t* p__parent, mpc2000xl_aps_t* p__root) : kaitai::kstruct(p__io) { + m__parent = p__parent; + m__root = p__root; + m__dirty = false; +} + +void mpc2000xl_aps_t::note_t::_read() { + m_sound_index = m__io->read_u2le(); + m_sound_generation_mode = static_cast(m__io->read_u1()); + m_velocity_range_lower = m__io->read_u1(); + m_also_play_use_note_1 = m__io->read_u1(); + m_velocity_range_upper = m__io->read_u1(); + m_also_play_use_note_2 = m__io->read_u1(); + m_voice_overlap_mode = static_cast(m__io->read_u1()); + m_mute_assign_1 = m__io->read_u1(); + m_mute_assign_2 = m__io->read_u1(); + m_tune = m__io->read_s2le(); + m_attack = m__io->read_u1(); + m_decay = m__io->read_u1(); + m_decay_mode = static_cast(m__io->read_u1()); + m_cutoff = m__io->read_u1(); + m_resonance = m__io->read_u1(); + m_velocity_envelope_to_filter_attack = m__io->read_u1(); + m_velocity_envelope_to_filter_decay = m__io->read_u1(); + m_velocity_envelope_to_filter_amount = m__io->read_u1(); + m_velocity_to_level = m__io->read_u1(); + m_velocity_to_attack = m__io->read_u1(); + m_velocity_to_start = m__io->read_u1(); + m_velocity_to_cutoff = m__io->read_u1(); + m_slider_parameter = static_cast(m__io->read_u1()); + m_velocity_to_pitch = m__io->read_u1(); + m__dirty = false; +} + +void mpc2000xl_aps_t::note_t::_fetch_instances() { +} + +void mpc2000xl_aps_t::note_t::_write() { + m__io->write_u2le(m_sound_index); + m__io->write_u1(static_cast(m_sound_generation_mode)); + m__io->write_u1(m_velocity_range_lower); + m__io->write_u1(m_also_play_use_note_1); + m__io->write_u1(m_velocity_range_upper); + m__io->write_u1(m_also_play_use_note_2); + m__io->write_u1(static_cast(m_voice_overlap_mode)); + m__io->write_u1(m_mute_assign_1); + m__io->write_u1(m_mute_assign_2); + m__io->write_s2le(m_tune); + m__io->write_u1(m_attack); + m__io->write_u1(m_decay); + m__io->write_u1(static_cast(m_decay_mode)); + m__io->write_u1(m_cutoff); + m__io->write_u1(m_resonance); + m__io->write_u1(m_velocity_envelope_to_filter_attack); + m__io->write_u1(m_velocity_envelope_to_filter_decay); + m__io->write_u1(m_velocity_envelope_to_filter_amount); + m__io->write_u1(m_velocity_to_level); + m__io->write_u1(m_velocity_to_attack); + m__io->write_u1(m_velocity_to_start); + m__io->write_u1(m_velocity_to_cutoff); + m__io->write_u1(static_cast(m_slider_parameter)); + m__io->write_u1(m_velocity_to_pitch); + _fetch_instances(); + m__dirty = false; +} + +void mpc2000xl_aps_t::note_t::_check() { + m__dirty = false; +} + +mpc2000xl_aps_t::note_t::~note_t() {} diff --git a/src/main/file/kaitai/generated/mpc2000xl_aps.h b/src/main/file/kaitai/generated/mpc2000xl_aps.h new file mode 100644 index 000000000..f196b3ca0 --- /dev/null +++ b/src/main/file/kaitai/generated/mpc2000xl_aps.h @@ -0,0 +1,450 @@ +#pragma once + +// This is a generated file! Please edit source .ksy file and use kaitai-struct-compiler to rebuild + +class mpc2000xl_aps_t; + +#include "kaitai/kaitaistruct.h" +#include +#include +#include "mpc2000xl_pgm.h" +#include +#include + +#if KAITAI_STRUCT_VERSION < 11000L +#error "Incompatible Kaitai Struct C++/STL API: version 0.11 or later is required" +#endif + +class mpc2000xl_aps_t : public kaitai::kstruct { + +public: + class aps_program_body_t; + class aps_program_meta_t; + class drum_t; + class global_parameters_t; + class note_t; + + enum mix_source_t { + MIX_SOURCE_PROGRAM = 0, + MIX_SOURCE_DRUM = 1 + }; + static bool _is_defined_mix_source_t(mix_source_t v); + +private: + static const std::set _values_mix_source_t; + +public: + + enum no_yes_t { + NO_YES_FALSE = 0, + NO_YES_TRUE = 1 + }; + static bool _is_defined_no_yes_t(no_yes_t v); + +private: + static const std::set _values_no_yes_t; + +public: + + enum pad_assign_t { + PAD_ASSIGN_PROGRAM = 0, + PAD_ASSIGN_MASTERS = 1 + }; + static bool _is_defined_pad_assign_t(pad_assign_t v); + +private: + static const std::set _values_pad_assign_t; + +public: + +private: + bool m__dirty; + +public: + + mpc2000xl_aps_t(kaitai::kstream* p__io, kaitai::kstruct* p__parent = nullptr, mpc2000xl_aps_t* p__root = nullptr); + void _read(); + void _fetch_instances(); + void _write(); + void _check(); + ~mpc2000xl_aps_t(); + + class aps_program_body_t : public kaitai::kstruct { + + public: + + private: + bool m__dirty; + + public: + + aps_program_body_t(kaitai::kstream* p__io, mpc2000xl_aps_t::aps_program_meta_t* p__parent = nullptr, mpc2000xl_aps_t* p__root = nullptr); + void _read(); + void _fetch_instances(); + void _write(); + void _check(); + ~aps_program_body_t(); + std::string _unnamed0() const { return m__unnamed0; } + void set__unnamed0(std::string _v) { m__dirty = true; m__unnamed0 = std::move(_v); } + std::string name() const { return m_name; } + void set_name(std::string _v) { m__dirty = true; m_name = std::move(_v); } + mpc2000xl_pgm_t::slider_t* slider() const { return m_slider.get(); } + void set_slider(std::unique_ptr _v) { m__dirty = true; m_slider = std::move(_v); } + uint8_t program_change() const { return m_program_change; } + void set_program_change(uint8_t _v) { m__dirty = true; m_program_change = std::move(_v); } + std::string _unnamed4() const { return m__unnamed4; } + void set__unnamed4(std::string _v) { m__dirty = true; m__unnamed4 = std::move(_v); } + std::vector>* note_parameters() const { return m_note_parameters.get(); } + void set_note_parameters(std::unique_ptr>> _v) { m__dirty = true; m_note_parameters = std::move(_v); } + std::string _unnamed6() const { return m__unnamed6; } + void set__unnamed6(std::string _v) { m__dirty = true; m__unnamed6 = std::move(_v); } + std::vector>* pad_mixers() const { return m_pad_mixers.get(); } + void set_pad_mixers(std::unique_ptr>> _v) { m__dirty = true; m_pad_mixers = std::move(_v); } + std::string _unnamed8() const { return m__unnamed8; } + void set__unnamed8(std::string _v) { m__dirty = true; m__unnamed8 = std::move(_v); } + std::vector* pad_to_note_mapping() const { return m_pad_to_note_mapping.get(); } + void set_pad_to_note_mapping(std::unique_ptr> _v) { m__dirty = true; m_pad_to_note_mapping = std::move(_v); } + std::string _unnamed10() const { return m__unnamed10; } + void set__unnamed10(std::string _v) { m__dirty = true; m__unnamed10 = std::move(_v); } + mpc2000xl_aps_t* _root() const { return m__root; } + void set__root(mpc2000xl_aps_t* _v) { m__dirty = true; m__root = std::move(_v); } + kaitai::kstruct* _parent() const { return m__parent; } + void set__parent(mpc2000xl_aps_t::aps_program_meta_t* _v) { m__dirty = true; m__parent = std::move(_v); } + + private: + std::string m__unnamed0; + std::string m_name; + std::unique_ptr m_slider; + uint8_t m_program_change; + std::string m__unnamed4; + std::unique_ptr>> m_note_parameters; + std::string m__unnamed6; + std::unique_ptr>> m_pad_mixers; + std::string m__unnamed8; + std::unique_ptr> m_pad_to_note_mapping; + std::string m__unnamed10; + mpc2000xl_aps_t* m__root; + mpc2000xl_aps_t::aps_program_meta_t* m__parent; + }; + + class aps_program_meta_t : public kaitai::kstruct { + + public: + + private: + bool m__dirty; + + public: + + aps_program_meta_t(kaitai::kstream* p__io, mpc2000xl_aps_t* p__parent = nullptr, mpc2000xl_aps_t* p__root = nullptr); + void _read(); + void _fetch_instances(); + void _write(); + void _check(); + ~aps_program_meta_t(); + uint8_t index() const { return m_index; } + void set_index(uint8_t _v) { m__dirty = true; m_index = std::move(_v); } + aps_program_body_t* body() const { return m_body.get(); } + void set_body(std::unique_ptr _v) { m__dirty = true; m_body = std::move(_v); } + std::vector* _unnamed2() const { return m__unnamed2.get(); } + void set__unnamed2(std::unique_ptr> _v) { m__dirty = true; m__unnamed2 = std::move(_v); } + mpc2000xl_aps_t* _root() const { return m__root; } + void set__root(mpc2000xl_aps_t* _v) { m__dirty = true; m__root = std::move(_v); } + kaitai::kstruct* _parent() const { return m__parent; } + void set__parent(mpc2000xl_aps_t* _v) { m__dirty = true; m__parent = std::move(_v); } + + private: + uint8_t m_index; + std::unique_ptr m_body; + + public: + bool _is_null_body() { return !body(); }; + + private: + std::unique_ptr> m__unnamed2; + + public: + bool _is_null__unnamed2() { return !_unnamed2(); }; + + private: + mpc2000xl_aps_t* m__root; + mpc2000xl_aps_t* m__parent; + }; + + class drum_t : public kaitai::kstruct { + + public: + + private: + bool m__dirty; + + public: + + drum_t(kaitai::kstream* p__io, mpc2000xl_aps_t* p__parent = nullptr, mpc2000xl_aps_t* p__root = nullptr); + void _read(); + void _fetch_instances(); + void _write(); + void _check(); + ~drum_t(); + std::vector>* pad_mixers() const { return m_pad_mixers.get(); } + void set_pad_mixers(std::unique_ptr>> _v) { m__dirty = true; m_pad_mixers = std::move(_v); } + std::string _unnamed1() const { return m__unnamed1; } + void set__unnamed1(std::string _v) { m__dirty = true; m__unnamed1 = std::move(_v); } + no_yes_t receive_program_change() const { return m_receive_program_change; } + void set_receive_program_change(no_yes_t _v) { m__dirty = true; m_receive_program_change = std::move(_v); } + no_yes_t receive_midi_volume() const { return m_receive_midi_volume; } + void set_receive_midi_volume(no_yes_t _v) { m__dirty = true; m_receive_midi_volume = std::move(_v); } + std::string _unnamed4() const { return m__unnamed4; } + void set__unnamed4(std::string _v) { m__dirty = true; m__unnamed4 = std::move(_v); } + uint8_t program() const { return m_program; } + void set_program(uint8_t _v) { m__dirty = true; m_program = std::move(_v); } + no_yes_t receive_program_change_duplicate() const { return m_receive_program_change_duplicate; } + void set_receive_program_change_duplicate(no_yes_t _v) { m__dirty = true; m_receive_program_change_duplicate = std::move(_v); } + no_yes_t receive_midi_volume_duplicate() const { return m_receive_midi_volume_duplicate; } + void set_receive_midi_volume_duplicate(no_yes_t _v) { m__dirty = true; m_receive_midi_volume_duplicate = std::move(_v); } + mpc2000xl_aps_t* _root() const { return m__root; } + void set__root(mpc2000xl_aps_t* _v) { m__dirty = true; m__root = std::move(_v); } + kaitai::kstruct* _parent() const { return m__parent; } + void set__parent(mpc2000xl_aps_t* _v) { m__dirty = true; m__parent = std::move(_v); } + + private: + std::unique_ptr>> m_pad_mixers; + std::string m__unnamed1; + no_yes_t m_receive_program_change; + no_yes_t m_receive_midi_volume; + std::string m__unnamed4; + uint8_t m_program; + no_yes_t m_receive_program_change_duplicate; + no_yes_t m_receive_midi_volume_duplicate; + mpc2000xl_aps_t* m__root; + mpc2000xl_aps_t* m__parent; + }; + + class global_parameters_t : public kaitai::kstruct { + + public: + + private: + bool m__dirty; + + public: + + global_parameters_t(kaitai::kstream* p__io, mpc2000xl_aps_t* p__parent = nullptr, mpc2000xl_aps_t* p__root = nullptr); + void _read(); + void _fetch_instances(); + void _write(); + void _check(); + ~global_parameters_t(); + no_yes_t pad_to_internal_sound() const { return m_pad_to_internal_sound; } + void set_pad_to_internal_sound(no_yes_t _v) { m__dirty = true; m_pad_to_internal_sound = std::move(_v); } + uint64_t _unnamed1() const { return m__unnamed1; } + void set__unnamed1(uint64_t _v) { m__dirty = true; m__unnamed1 = std::move(_v); } + pad_assign_t pad_assign() const { return m_pad_assign; } + void set_pad_assign(pad_assign_t _v) { m__dirty = true; m_pad_assign = std::move(_v); } + uint64_t _unnamed3() const { return m__unnamed3; } + void set__unnamed3(uint64_t _v) { m__dirty = true; m__unnamed3 = std::move(_v); } + mix_source_t stereo_mix_source() const { return m_stereo_mix_source; } + void set_stereo_mix_source(mix_source_t _v) { m__dirty = true; m_stereo_mix_source = std::move(_v); } + mix_source_t indiv_fx_source() const { return m_indiv_fx_source; } + void set_indiv_fx_source(mix_source_t _v) { m__dirty = true; m_indiv_fx_source = std::move(_v); } + uint64_t _unnamed6() const { return m__unnamed6; } + void set__unnamed6(uint64_t _v) { m__dirty = true; m__unnamed6 = std::move(_v); } + no_yes_t copy_pgm_mix_to_drum() const { return m_copy_pgm_mix_to_drum; } + void set_copy_pgm_mix_to_drum(no_yes_t _v) { m__dirty = true; m_copy_pgm_mix_to_drum = std::move(_v); } + uint64_t _unnamed8() const { return m__unnamed8; } + void set__unnamed8(uint64_t _v) { m__dirty = true; m__unnamed8 = std::move(_v); } + no_yes_t record_mix_changes() const { return m_record_mix_changes; } + void set_record_mix_changes(no_yes_t _v) { m__dirty = true; m_record_mix_changes = std::move(_v); } + uint64_t _unnamed10() const { return m__unnamed10; } + void set__unnamed10(uint64_t _v) { m__dirty = true; m__unnamed10 = std::move(_v); } + uint8_t fx_drum() const { return m_fx_drum; } + void set_fx_drum(uint8_t _v) { m__dirty = true; m_fx_drum = std::move(_v); } + std::string _unnamed12() const { return m__unnamed12; } + void set__unnamed12(std::string _v) { m__dirty = true; m__unnamed12 = std::move(_v); } + uint8_t master_level() const { return m_master_level; } + void set_master_level(uint8_t _v) { m__dirty = true; m_master_level = std::move(_v); } + mpc2000xl_aps_t* _root() const { return m__root; } + void set__root(mpc2000xl_aps_t* _v) { m__dirty = true; m__root = std::move(_v); } + kaitai::kstruct* _parent() const { return m__parent; } + void set__parent(mpc2000xl_aps_t* _v) { m__dirty = true; m__parent = std::move(_v); } + + private: + no_yes_t m_pad_to_internal_sound; + uint64_t m__unnamed1; + pad_assign_t m_pad_assign; + uint64_t m__unnamed3; + mix_source_t m_stereo_mix_source; + mix_source_t m_indiv_fx_source; + uint64_t m__unnamed6; + no_yes_t m_copy_pgm_mix_to_drum; + uint64_t m__unnamed8; + no_yes_t m_record_mix_changes; + uint64_t m__unnamed10; + uint8_t m_fx_drum; + std::string m__unnamed12; + uint8_t m_master_level; + mpc2000xl_aps_t* m__root; + mpc2000xl_aps_t* m__parent; + }; + + class note_t : public kaitai::kstruct { + + public: + + private: + bool m__dirty; + + public: + + note_t(kaitai::kstream* p__io, mpc2000xl_aps_t::aps_program_body_t* p__parent = nullptr, mpc2000xl_aps_t* p__root = nullptr); + void _read(); + void _fetch_instances(); + void _write(); + void _check(); + ~note_t(); + uint16_t sound_index() const { return m_sound_index; } + void set_sound_index(uint16_t _v) { m__dirty = true; m_sound_index = std::move(_v); } + mpc2000xl_pgm_t::sound_generation_mode_t sound_generation_mode() const { return m_sound_generation_mode; } + void set_sound_generation_mode(mpc2000xl_pgm_t::sound_generation_mode_t _v) { m__dirty = true; m_sound_generation_mode = std::move(_v); } + uint8_t velocity_range_lower() const { return m_velocity_range_lower; } + void set_velocity_range_lower(uint8_t _v) { m__dirty = true; m_velocity_range_lower = std::move(_v); } + uint8_t also_play_use_note_1() const { return m_also_play_use_note_1; } + void set_also_play_use_note_1(uint8_t _v) { m__dirty = true; m_also_play_use_note_1 = std::move(_v); } + uint8_t velocity_range_upper() const { return m_velocity_range_upper; } + void set_velocity_range_upper(uint8_t _v) { m__dirty = true; m_velocity_range_upper = std::move(_v); } + uint8_t also_play_use_note_2() const { return m_also_play_use_note_2; } + void set_also_play_use_note_2(uint8_t _v) { m__dirty = true; m_also_play_use_note_2 = std::move(_v); } + mpc2000xl_pgm_t::voice_overlap_mode_t voice_overlap_mode() const { return m_voice_overlap_mode; } + void set_voice_overlap_mode(mpc2000xl_pgm_t::voice_overlap_mode_t _v) { m__dirty = true; m_voice_overlap_mode = std::move(_v); } + uint8_t mute_assign_1() const { return m_mute_assign_1; } + void set_mute_assign_1(uint8_t _v) { m__dirty = true; m_mute_assign_1 = std::move(_v); } + uint8_t mute_assign_2() const { return m_mute_assign_2; } + void set_mute_assign_2(uint8_t _v) { m__dirty = true; m_mute_assign_2 = std::move(_v); } + int16_t tune() const { return m_tune; } + void set_tune(int16_t _v) { m__dirty = true; m_tune = std::move(_v); } + uint8_t attack() const { return m_attack; } + void set_attack(uint8_t _v) { m__dirty = true; m_attack = std::move(_v); } + uint8_t decay() const { return m_decay; } + void set_decay(uint8_t _v) { m__dirty = true; m_decay = std::move(_v); } + mpc2000xl_pgm_t::decay_mode_t decay_mode() const { return m_decay_mode; } + void set_decay_mode(mpc2000xl_pgm_t::decay_mode_t _v) { m__dirty = true; m_decay_mode = std::move(_v); } + uint8_t cutoff() const { return m_cutoff; } + void set_cutoff(uint8_t _v) { m__dirty = true; m_cutoff = std::move(_v); } + uint8_t resonance() const { return m_resonance; } + void set_resonance(uint8_t _v) { m__dirty = true; m_resonance = std::move(_v); } + uint8_t velocity_envelope_to_filter_attack() const { return m_velocity_envelope_to_filter_attack; } + void set_velocity_envelope_to_filter_attack(uint8_t _v) { m__dirty = true; m_velocity_envelope_to_filter_attack = std::move(_v); } + uint8_t velocity_envelope_to_filter_decay() const { return m_velocity_envelope_to_filter_decay; } + void set_velocity_envelope_to_filter_decay(uint8_t _v) { m__dirty = true; m_velocity_envelope_to_filter_decay = std::move(_v); } + uint8_t velocity_envelope_to_filter_amount() const { return m_velocity_envelope_to_filter_amount; } + void set_velocity_envelope_to_filter_amount(uint8_t _v) { m__dirty = true; m_velocity_envelope_to_filter_amount = std::move(_v); } + uint8_t velocity_to_level() const { return m_velocity_to_level; } + void set_velocity_to_level(uint8_t _v) { m__dirty = true; m_velocity_to_level = std::move(_v); } + uint8_t velocity_to_attack() const { return m_velocity_to_attack; } + void set_velocity_to_attack(uint8_t _v) { m__dirty = true; m_velocity_to_attack = std::move(_v); } + uint8_t velocity_to_start() const { return m_velocity_to_start; } + void set_velocity_to_start(uint8_t _v) { m__dirty = true; m_velocity_to_start = std::move(_v); } + uint8_t velocity_to_cutoff() const { return m_velocity_to_cutoff; } + void set_velocity_to_cutoff(uint8_t _v) { m__dirty = true; m_velocity_to_cutoff = std::move(_v); } + mpc2000xl_pgm_t::slider_parameter_t slider_parameter() const { return m_slider_parameter; } + void set_slider_parameter(mpc2000xl_pgm_t::slider_parameter_t _v) { m__dirty = true; m_slider_parameter = std::move(_v); } + uint8_t velocity_to_pitch() const { return m_velocity_to_pitch; } + void set_velocity_to_pitch(uint8_t _v) { m__dirty = true; m_velocity_to_pitch = std::move(_v); } + mpc2000xl_aps_t* _root() const { return m__root; } + void set__root(mpc2000xl_aps_t* _v) { m__dirty = true; m__root = std::move(_v); } + kaitai::kstruct* _parent() const { return m__parent; } + void set__parent(mpc2000xl_aps_t::aps_program_body_t* _v) { m__dirty = true; m__parent = std::move(_v); } + + private: + uint16_t m_sound_index; + mpc2000xl_pgm_t::sound_generation_mode_t m_sound_generation_mode; + uint8_t m_velocity_range_lower; + uint8_t m_also_play_use_note_1; + uint8_t m_velocity_range_upper; + uint8_t m_also_play_use_note_2; + mpc2000xl_pgm_t::voice_overlap_mode_t m_voice_overlap_mode; + uint8_t m_mute_assign_1; + uint8_t m_mute_assign_2; + int16_t m_tune; + uint8_t m_attack; + uint8_t m_decay; + mpc2000xl_pgm_t::decay_mode_t m_decay_mode; + uint8_t m_cutoff; + uint8_t m_resonance; + uint8_t m_velocity_envelope_to_filter_attack; + uint8_t m_velocity_envelope_to_filter_decay; + uint8_t m_velocity_envelope_to_filter_amount; + uint8_t m_velocity_to_level; + uint8_t m_velocity_to_attack; + uint8_t m_velocity_to_start; + uint8_t m_velocity_to_cutoff; + mpc2000xl_pgm_t::slider_parameter_t m_slider_parameter; + uint8_t m_velocity_to_pitch; + mpc2000xl_aps_t* m__root; + mpc2000xl_aps_t::aps_program_body_t* m__parent; + }; + +public: + std::string magic() const { return m_magic; } + void set_magic(std::string _v) { m__dirty = true; m_magic = std::move(_v); } + uint16_t sound_count() const { return m_sound_count; } + void set_sound_count(uint16_t _v) { m__dirty = true; m_sound_count = std::move(_v); } + std::vector* sound_names() const { return m_sound_names.get(); } + void set_sound_names(std::unique_ptr> _v) { m__dirty = true; m_sound_names = std::move(_v); } + std::string _unnamed3() const { return m__unnamed3; } + void set__unnamed3(std::string _v) { m__dirty = true; m__unnamed3 = std::move(_v); } + std::string name() const { return m_name; } + void set_name(std::string _v) { m__dirty = true; m_name = std::move(_v); } + global_parameters_t* global_parameters() const { return m_global_parameters.get(); } + void set_global_parameters(std::unique_ptr _v) { m__dirty = true; m_global_parameters = std::move(_v); } + std::string _unnamed6() const { return m__unnamed6; } + void set__unnamed6(std::string _v) { m__dirty = true; m__unnamed6 = std::move(_v); } + std::vector* master_pad_to_note_mapping() const { return m_master_pad_to_note_mapping.get(); } + void set_master_pad_to_note_mapping(std::unique_ptr> _v) { m__dirty = true; m_master_pad_to_note_mapping = std::move(_v); } + std::string _unnamed8() const { return m__unnamed8; } + void set__unnamed8(std::string _v) { m__dirty = true; m__unnamed8 = std::move(_v); } + drum_t* drum1() const { return m_drum1.get(); } + void set_drum1(std::unique_ptr _v) { m__dirty = true; m_drum1 = std::move(_v); } + std::string _unnamed10() const { return m__unnamed10; } + void set__unnamed10(std::string _v) { m__dirty = true; m__unnamed10 = std::move(_v); } + drum_t* drum2() const { return m_drum2.get(); } + void set_drum2(std::unique_ptr _v) { m__dirty = true; m_drum2 = std::move(_v); } + std::string _unnamed12() const { return m__unnamed12; } + void set__unnamed12(std::string _v) { m__dirty = true; m__unnamed12 = std::move(_v); } + drum_t* drum3() const { return m_drum3.get(); } + void set_drum3(std::unique_ptr _v) { m__dirty = true; m_drum3 = std::move(_v); } + std::string _unnamed14() const { return m__unnamed14; } + void set__unnamed14(std::string _v) { m__dirty = true; m__unnamed14 = std::move(_v); } + drum_t* drum4() const { return m_drum4.get(); } + void set_drum4(std::unique_ptr _v) { m__dirty = true; m_drum4 = std::move(_v); } + std::string _unnamed16() const { return m__unnamed16; } + void set__unnamed16(std::string _v) { m__dirty = true; m__unnamed16 = std::move(_v); } + std::vector>* aps_programs() const { return m_aps_programs.get(); } + void set_aps_programs(std::unique_ptr>> _v) { m__dirty = true; m_aps_programs = std::move(_v); } + mpc2000xl_aps_t* _root() const { return m__root; } + void set__root(mpc2000xl_aps_t* _v) { m__dirty = true; m__root = std::move(_v); } + kaitai::kstruct* _parent() const { return m__parent; } + void set__parent(kaitai::kstruct* _v) { m__dirty = true; m__parent = std::move(_v); } + +private: + std::string m_magic; + uint16_t m_sound_count; + std::unique_ptr> m_sound_names; + std::string m__unnamed3; + std::string m_name; + std::unique_ptr m_global_parameters; + std::string m__unnamed6; + std::unique_ptr> m_master_pad_to_note_mapping; + std::string m__unnamed8; + std::unique_ptr m_drum1; + std::string m__unnamed10; + std::unique_ptr m_drum2; + std::string m__unnamed12; + std::unique_ptr m_drum3; + std::string m__unnamed14; + std::unique_ptr m_drum4; + std::string m__unnamed16; + std::unique_ptr>> m_aps_programs; + mpc2000xl_aps_t* m__root; + kaitai::kstruct* m__parent; +}; diff --git a/src/main/file/kaitai/generated/mpc2000xl_mid.cpp b/src/main/file/kaitai/generated/mpc2000xl_mid.cpp new file mode 100644 index 000000000..fc4dbb2b8 --- /dev/null +++ b/src/main/file/kaitai/generated/mpc2000xl_mid.cpp @@ -0,0 +1,287 @@ +// This is a generated file! Please edit source .ksy file and use kaitai-struct-compiler to rebuild + +#include "mpc2000xl_mid.h" +#include +#include +#include "kaitai/exceptions.h" + +mpc2000xl_mid_t::mpc2000xl_mid_t(kaitai::kstream* p__io, kaitai::kstruct* p__parent, mpc2000xl_mid_t* p__root) : kaitai::kstruct(p__io) { + m__parent = p__parent; + m__root = p__root ? p__root : this; + m__dirty = false; + f_denominator = false; + f_first_meta = false; + f_is_mpc2000xl_mid = false; + f_meta_events = false; + f_numerator = false; + f_sequence_name = false; + f_sequencer = false; + f_smpte_offset = false; + f_tempo_bpm = false; + w_sequencer = false; + e_sequencer = true; +} + +void mpc2000xl_mid_t::_read() { + m_hdr = std::unique_ptr(new standard_midi_file_with_running_status_t::header_t(m__io)); + m_hdr->_read(); + m_tracks = std::unique_ptr>>(new std::vector>()); + const int l_tracks = hdr()->num_tracks(); + for (int i = 0; i < l_tracks; i++) { + std::unique_ptr _t_tracks = std::unique_ptr(new standard_midi_file_with_running_status_t::track_t(m__io)); + try { + _t_tracks->_read(); + } catch(...) { + m_tracks->push_back(std::move(_t_tracks)); + throw; + } + m_tracks->push_back(std::move(_t_tracks)); + } + m__dirty = false; +} + +void mpc2000xl_mid_t::_fetch_instances() { + m_hdr.get()->_fetch_instances(); + for (std::size_t i = 0; i < m_tracks->size(); ++i) { + m_tracks->at(i).get()->_fetch_instances(); + } + sequencer(); + if (f_sequencer) { + m_sequencer.get()->_fetch_instances(); + } +} + +void mpc2000xl_mid_t::_write() { + w_sequencer = e_sequencer; + if (m_hdr.get() == nullptr) { + throw std::runtime_error("/seq/0: nested object is not set"); + } + m_hdr.get()->_set_io(m__io); + m_hdr.get()->_write(); + if (m_tracks == nullptr) { + throw std::runtime_error("/seq/1: repeated field is not set"); + } + for (std::vector>::const_iterator it = m_tracks->begin(); it != m_tracks->end(); ++it) { + if ((*it).get() == nullptr) { + throw std::runtime_error("/seq/1: nested object is not set"); + } + (*it).get()->_set_io(m__io); + (*it).get()->_write(); + } + _fetch_instances(); + m__dirty = false; +} + +void mpc2000xl_mid_t::_check() { + if (m_hdr.get() == nullptr) { + throw std::runtime_error("/seq/0: nested object is not set"); + } + m_hdr.get()->_set_io(m__io); + m_hdr.get()->_check(); + if (m_tracks == nullptr) { + throw std::runtime_error("/seq/1: repeated field is not set"); + } + if (m_tracks->size() != static_cast(hdr()->num_tracks())) { + throw std::runtime_error("/seq/1: repeat-expr size mismatch"); + } + for (std::vector>::const_iterator it = m_tracks->begin(); it != m_tracks->end(); ++it) { + if ((*it).get() == nullptr) { + throw std::runtime_error("/seq/1: nested object is not set"); + } + (*it).get()->_set_io(m__io); + (*it).get()->_check(); + } + if (e_sequencer) { + kaitai::kstream *io = tracks()->at(0)->events()->_io(); + if (m_sequencer.get() == nullptr) { + throw std::runtime_error("/instances/sequencer: nested object is not set"); + } + m_sequencer.get()->_set_io(io); + m_sequencer.get()->_check(); + } + m__dirty = false; +} + +mpc2000xl_mid_t::~mpc2000xl_mid_t() {} + +mpc2000xl_mid_t::sequencer_t::sequencer_t(kaitai::kstream* p__io, mpc2000xl_mid_t* p__parent, mpc2000xl_mid_t* p__root) : kaitai::kstruct(p__io) { + m__parent = p__parent; + m__root = p__root; + m__dirty = false; +} + +void mpc2000xl_mid_t::sequencer_t::_read() { + m__unnamed0 = m__io->read_bytes(5); + if (!(m__unnamed0 == std::string("\x4C\x4F\x4F\x50\x3D", 5))) { + throw kaitai::validation_not_equal_error(std::string("\x4C\x4F\x4F\x50\x3D", 5), m__unnamed0, m__io, std::string("/types/sequencer/seq/0")); + } + m_loop_enabled = kaitai::kstream::bytes_to_str(m__io->read_bytes(4), "ASCII"); + m__unnamed2 = m__io->read_bytes(6); + if (!(m__unnamed2 == std::string("\x53\x54\x41\x52\x54\x3D", 6))) { + throw kaitai::validation_not_equal_error(std::string("\x53\x54\x41\x52\x54\x3D", 6), m__unnamed2, m__io, std::string("/types/sequencer/seq/2")); + } + m_loop_start = kaitai::kstream::bytes_to_str(m__io->read_bytes(4), "ASCII"); + m__unnamed4 = m__io->read_bytes(4); + if (!(m__unnamed4 == std::string("\x45\x4E\x44\x3D", 4))) { + throw kaitai::validation_not_equal_error(std::string("\x45\x4E\x44\x3D", 4), m__unnamed4, m__io, std::string("/types/sequencer/seq/4")); + } + m_loop_end = kaitai::kstream::bytes_to_str(m__io->read_bytes(4), "ASCII"); + m__unnamed6 = m__io->read_bytes(6); + if (!(m__unnamed6 == std::string("\x54\x45\x4D\x50\x4F\x3D", 6))) { + throw kaitai::validation_not_equal_error(std::string("\x54\x45\x4D\x50\x4F\x3D", 6), m__unnamed6, m__io, std::string("/types/sequencer/seq/6")); + } + m_tempo_source = kaitai::kstream::bytes_to_str(m__io->read_bytes(3), "ASCII"); + m__dirty = false; +} + +void mpc2000xl_mid_t::sequencer_t::_fetch_instances() { +} + +void mpc2000xl_mid_t::sequencer_t::_write() { + m__io->write_bytes(m__unnamed0); + m__io->write_bytes(m_loop_enabled); + m__io->write_bytes(m__unnamed2); + m__io->write_bytes(m_loop_start); + m__io->write_bytes(m__unnamed4); + m__io->write_bytes(m_loop_end); + m__io->write_bytes(m__unnamed6); + m__io->write_bytes(m_tempo_source); + _fetch_instances(); + m__dirty = false; +} + +void mpc2000xl_mid_t::sequencer_t::_check() { + if (m__unnamed0.size() != static_cast(5)) { + throw std::runtime_error("/types/sequencer/seq/0: size mismatch"); + } + if (!(m__unnamed0 == std::string("\x4C\x4F\x4F\x50\x3D", 5))) { + throw kaitai::validation_not_equal_error(std::string("\x4C\x4F\x4F\x50\x3D", 5), m__unnamed0, m__io, std::string("/types/sequencer/seq/0")); + } + if (m_loop_enabled.size() != static_cast(4)) { + throw std::runtime_error("/types/sequencer/seq/1: size mismatch"); + } + if (m__unnamed2.size() != static_cast(6)) { + throw std::runtime_error("/types/sequencer/seq/2: size mismatch"); + } + if (!(m__unnamed2 == std::string("\x53\x54\x41\x52\x54\x3D", 6))) { + throw kaitai::validation_not_equal_error(std::string("\x53\x54\x41\x52\x54\x3D", 6), m__unnamed2, m__io, std::string("/types/sequencer/seq/2")); + } + if (m_loop_start.size() != static_cast(4)) { + throw std::runtime_error("/types/sequencer/seq/3: size mismatch"); + } + if (m__unnamed4.size() != static_cast(4)) { + throw std::runtime_error("/types/sequencer/seq/4: size mismatch"); + } + if (!(m__unnamed4 == std::string("\x45\x4E\x44\x3D", 4))) { + throw kaitai::validation_not_equal_error(std::string("\x45\x4E\x44\x3D", 4), m__unnamed4, m__io, std::string("/types/sequencer/seq/4")); + } + if (m_loop_end.size() != static_cast(4)) { + throw std::runtime_error("/types/sequencer/seq/5: size mismatch"); + } + if (m__unnamed6.size() != static_cast(6)) { + throw std::runtime_error("/types/sequencer/seq/6: size mismatch"); + } + if (!(m__unnamed6 == std::string("\x54\x45\x4D\x50\x4F\x3D", 6))) { + throw kaitai::validation_not_equal_error(std::string("\x54\x45\x4D\x50\x4F\x3D", 6), m__unnamed6, m__io, std::string("/types/sequencer/seq/6")); + } + if (m_tempo_source.size() != static_cast(3)) { + throw std::runtime_error("/types/sequencer/seq/7: size mismatch"); + } + m__dirty = false; +} + +mpc2000xl_mid_t::sequencer_t::~sequencer_t() {} + +int32_t mpc2000xl_mid_t::denominator() { + if (f_denominator) + return m_denominator; + f_denominator = true; + m_denominator = 1 << meta_events()->at(4)->meta_event_body()->body().at(1); + return m_denominator; +} + +standard_midi_file_with_running_status_t::meta_event_body_t* mpc2000xl_mid_t::first_meta() { + if (f_first_meta) + return m_first_meta; + f_first_meta = true; + m_first_meta = meta_events()->at(0)->meta_event_body(); + return m_first_meta; +} + +bool mpc2000xl_mid_t::is_mpc2000xl_mid() { + if (f_is_mpc2000xl_mid) + return m_is_mpc2000xl_mid; + f_is_mpc2000xl_mid = true; + m_is_mpc2000xl_mid = ((tracks()->size() > 0) && (meta_events()->size() > 0) && (meta_events()->at(0)->event_type() == 240) && (first_meta()->len()->value() == 32) && (kaitai::kstream::bytes_to_str(first_meta()->body(), "ASCII").substr(0, 16 - 0) == std::string("MPC2000XL 1.00 "))) ; + return m_is_mpc2000xl_mid; +} + +std::vector>* mpc2000xl_mid_t::meta_events() { + if (f_meta_events) + return m_meta_events; + f_meta_events = true; + m_meta_events = tracks()->at(0)->events()->event(); + return m_meta_events; +} + +uint8_t mpc2000xl_mid_t::numerator() { + if (f_numerator) + return m_numerator; + f_numerator = true; + m_numerator = meta_events()->at(4)->meta_event_body()->body().at(0); + return m_numerator; +} + +std::string mpc2000xl_mid_t::sequence_name() { + if (f_sequence_name) + return m_sequence_name; + f_sequence_name = true; + m_sequence_name = kaitai::kstream::bytes_to_str(first_meta()->body(), "ASCII").substr(16, 32 - 16); + return m_sequence_name; +} + +mpc2000xl_mid_t::sequencer_t* mpc2000xl_mid_t::sequencer() { + if (w_sequencer) + _write_sequencer(); + if (f_sequencer) + return m_sequencer.get(); + if (!e_sequencer) + return nullptr; + f_sequencer = true; + kaitai::kstream *io = tracks()->at(0)->events()->_io(); + std::streampos _pos = io->pos(); + io->seek(40); + m_sequencer = std::unique_ptr(new sequencer_t(io, this, m__root)); + m_sequencer->_read(); + io->seek(_pos); + return m_sequencer.get(); +} + +void mpc2000xl_mid_t::_write_sequencer() { + w_sequencer = false; + kaitai::kstream *io = tracks()->at(0)->events()->_io(); + std::streampos _pos = io->pos(); + io->seek(40); + if (m_sequencer.get() == nullptr) { + throw std::runtime_error("/instances/sequencer: nested object is not set"); + } + m_sequencer.get()->_set_io(io); + m_sequencer.get()->_write(); + io->seek(_pos); +} + +std::string mpc2000xl_mid_t::smpte_offset() { + if (f_smpte_offset) + return m_smpte_offset; + f_smpte_offset = true; + m_smpte_offset = meta_events()->at(3)->meta_event_body()->body(); + return m_smpte_offset; +} + +double mpc2000xl_mid_t::tempo_bpm() { + if (f_tempo_bpm) + return m_tempo_bpm; + f_tempo_bpm = true; + m_tempo_bpm = 60000000.0 / (((meta_events()->at(2)->meta_event_body()->body().at(0) << 16) + (meta_events()->at(2)->meta_event_body()->body().at(1) << 8)) + meta_events()->at(2)->meta_event_body()->body().at(2)); + return m_tempo_bpm; +} diff --git a/src/main/file/kaitai/generated/mpc2000xl_mid.h b/src/main/file/kaitai/generated/mpc2000xl_mid.h new file mode 100644 index 000000000..ac0d47701 --- /dev/null +++ b/src/main/file/kaitai/generated/mpc2000xl_mid.h @@ -0,0 +1,142 @@ +#pragma once + +// This is a generated file! Please edit source .ksy file and use kaitai-struct-compiler to rebuild + +class mpc2000xl_mid_t; + +#include "kaitai/kaitaistruct.h" +#include +#include +#include "standard_midi_file_with_running_status.h" +#include + +#if KAITAI_STRUCT_VERSION < 11000L +#error "Incompatible Kaitai Struct C++/STL API: version 0.11 or later is required" +#endif + +class mpc2000xl_mid_t : public kaitai::kstruct { + +public: + class sequencer_t; + +private: + bool m__dirty; + +public: + + mpc2000xl_mid_t(kaitai::kstream* p__io, kaitai::kstruct* p__parent = nullptr, mpc2000xl_mid_t* p__root = nullptr); + void _read(); + void _fetch_instances(); + void _write(); + void _check(); + ~mpc2000xl_mid_t(); + + class sequencer_t : public kaitai::kstruct { + + public: + + private: + bool m__dirty; + + public: + + sequencer_t(kaitai::kstream* p__io, mpc2000xl_mid_t* p__parent = nullptr, mpc2000xl_mid_t* p__root = nullptr); + void _read(); + void _fetch_instances(); + void _write(); + void _check(); + ~sequencer_t(); + std::string _unnamed0() const { return m__unnamed0; } + void set__unnamed0(std::string _v) { m__dirty = true; m__unnamed0 = std::move(_v); } + std::string loop_enabled() const { return m_loop_enabled; } + void set_loop_enabled(std::string _v) { m__dirty = true; m_loop_enabled = std::move(_v); } + std::string _unnamed2() const { return m__unnamed2; } + void set__unnamed2(std::string _v) { m__dirty = true; m__unnamed2 = std::move(_v); } + std::string loop_start() const { return m_loop_start; } + void set_loop_start(std::string _v) { m__dirty = true; m_loop_start = std::move(_v); } + std::string _unnamed4() const { return m__unnamed4; } + void set__unnamed4(std::string _v) { m__dirty = true; m__unnamed4 = std::move(_v); } + std::string loop_end() const { return m_loop_end; } + void set_loop_end(std::string _v) { m__dirty = true; m_loop_end = std::move(_v); } + std::string _unnamed6() const { return m__unnamed6; } + void set__unnamed6(std::string _v) { m__dirty = true; m__unnamed6 = std::move(_v); } + std::string tempo_source() const { return m_tempo_source; } + void set_tempo_source(std::string _v) { m__dirty = true; m_tempo_source = std::move(_v); } + mpc2000xl_mid_t* _root() const { return m__root; } + void set__root(mpc2000xl_mid_t* _v) { m__dirty = true; m__root = std::move(_v); } + kaitai::kstruct* _parent() const { return m__parent; } + void set__parent(mpc2000xl_mid_t* _v) { m__dirty = true; m__parent = std::move(_v); } + + private: + std::string m__unnamed0; + std::string m_loop_enabled; + std::string m__unnamed2; + std::string m_loop_start; + std::string m__unnamed4; + std::string m_loop_end; + std::string m__unnamed6; + std::string m_tempo_source; + mpc2000xl_mid_t* m__root; + mpc2000xl_mid_t* m__parent; + }; + +public: + int32_t denominator(); + void _invalidate_denominator() { f_denominator = false; } + standard_midi_file_with_running_status_t::meta_event_body_t* first_meta(); + void _invalidate_first_meta() { f_first_meta = false; } + bool is_mpc2000xl_mid(); + void _invalidate_is_mpc2000xl_mid() { f_is_mpc2000xl_mid = false; } + std::vector>* meta_events(); + void _invalidate_meta_events() { f_meta_events = false; } + uint8_t numerator(); + void _invalidate_numerator() { f_numerator = false; } + std::string sequence_name(); + void _invalidate_sequence_name() { f_sequence_name = false; } + mpc2000xl_mid_t::sequencer_t* sequencer(); + void set_sequencer(std::unique_ptr _v) { m__dirty = true; f_denominator = false; f_first_meta = false; f_is_mpc2000xl_mid = false; f_meta_events = false; f_numerator = false; f_sequence_name = false; f_smpte_offset = false; f_tempo_bpm = false; f_sequencer = true; m_sequencer = std::move(_v); } + void set_sequencer_enabled(bool _v) { m__dirty = true; e_sequencer = _v; } + +private: + void _write_sequencer(); + +public: + std::string smpte_offset(); + void _invalidate_smpte_offset() { f_smpte_offset = false; } + double tempo_bpm(); + void _invalidate_tempo_bpm() { f_tempo_bpm = false; } + standard_midi_file_with_running_status_t::header_t* hdr() const { return m_hdr.get(); } + void set_hdr(std::unique_ptr _v) { m__dirty = true; f_denominator = false; f_first_meta = false; f_is_mpc2000xl_mid = false; f_meta_events = false; f_numerator = false; f_sequence_name = false; f_smpte_offset = false; f_tempo_bpm = false; m_hdr = std::move(_v); } + std::vector>* tracks() const { return m_tracks.get(); } + void set_tracks(std::unique_ptr>> _v) { m__dirty = true; f_denominator = false; f_first_meta = false; f_is_mpc2000xl_mid = false; f_meta_events = false; f_numerator = false; f_sequence_name = false; f_smpte_offset = false; f_tempo_bpm = false; m_tracks = std::move(_v); } + mpc2000xl_mid_t* _root() const { return m__root; } + void set__root(mpc2000xl_mid_t* _v) { m__dirty = true; f_denominator = false; f_first_meta = false; f_is_mpc2000xl_mid = false; f_meta_events = false; f_numerator = false; f_sequence_name = false; f_smpte_offset = false; f_tempo_bpm = false; m__root = std::move(_v); } + kaitai::kstruct* _parent() const { return m__parent; } + void set__parent(kaitai::kstruct* _v) { m__dirty = true; f_denominator = false; f_first_meta = false; f_is_mpc2000xl_mid = false; f_meta_events = false; f_numerator = false; f_sequence_name = false; f_smpte_offset = false; f_tempo_bpm = false; m__parent = std::move(_v); } + +private: + bool f_denominator; + int32_t m_denominator; + bool f_first_meta; + standard_midi_file_with_running_status_t::meta_event_body_t* m_first_meta; + bool f_is_mpc2000xl_mid; + bool m_is_mpc2000xl_mid; + bool f_meta_events; + std::vector>* m_meta_events; + bool f_numerator; + uint8_t m_numerator; + bool f_sequence_name; + std::string m_sequence_name; + bool f_sequencer; + std::unique_ptr m_sequencer; + bool w_sequencer; + bool e_sequencer; + bool f_smpte_offset; + std::string m_smpte_offset; + bool f_tempo_bpm; + double m_tempo_bpm; + std::unique_ptr m_hdr; + std::unique_ptr>> m_tracks; + mpc2000xl_mid_t* m__root; + kaitai::kstruct* m__parent; +}; diff --git a/src/main/file/kaitai/generated/mpc2000xl_pgm.cpp b/src/main/file/kaitai/generated/mpc2000xl_pgm.cpp new file mode 100644 index 000000000..f16ac2029 --- /dev/null +++ b/src/main/file/kaitai/generated/mpc2000xl_pgm.cpp @@ -0,0 +1,402 @@ +// This is a generated file! Please edit source .ksy file and use kaitai-struct-compiler to rebuild + +#include "mpc2000xl_pgm.h" +#include "kaitai/exceptions.h" +#include +#include +const std::set mpc2000xl_pgm_t::_values_decay_mode_t{ + mpc2000xl_pgm_t::DECAY_MODE_END, + mpc2000xl_pgm_t::DECAY_MODE_START, +}; +bool mpc2000xl_pgm_t::_is_defined_decay_mode_t(mpc2000xl_pgm_t::decay_mode_t v) { + return mpc2000xl_pgm_t::_values_decay_mode_t.find(v) != mpc2000xl_pgm_t::_values_decay_mode_t.end(); +} +const std::set mpc2000xl_pgm_t::_values_fx_output_t{ + mpc2000xl_pgm_t::FX_OUTPUT_NONE, + mpc2000xl_pgm_t::FX_OUTPUT_M1, + mpc2000xl_pgm_t::FX_OUTPUT_M2, + mpc2000xl_pgm_t::FX_OUTPUT_R1, + mpc2000xl_pgm_t::FX_OUTPUT_R2, +}; +bool mpc2000xl_pgm_t::_is_defined_fx_output_t(mpc2000xl_pgm_t::fx_output_t v) { + return mpc2000xl_pgm_t::_values_fx_output_t.find(v) != mpc2000xl_pgm_t::_values_fx_output_t.end(); +} +const std::set mpc2000xl_pgm_t::_values_slider_parameter_t{ + mpc2000xl_pgm_t::SLIDER_PARAMETER_TUNING, + mpc2000xl_pgm_t::SLIDER_PARAMETER_DECAY, + mpc2000xl_pgm_t::SLIDER_PARAMETER_ATTACK, + mpc2000xl_pgm_t::SLIDER_PARAMETER_FILTER, +}; +bool mpc2000xl_pgm_t::_is_defined_slider_parameter_t(mpc2000xl_pgm_t::slider_parameter_t v) { + return mpc2000xl_pgm_t::_values_slider_parameter_t.find(v) != mpc2000xl_pgm_t::_values_slider_parameter_t.end(); +} +const std::set mpc2000xl_pgm_t::_values_sound_generation_mode_t{ + mpc2000xl_pgm_t::SOUND_GENERATION_MODE_NORMAL, + mpc2000xl_pgm_t::SOUND_GENERATION_MODE_SIMULT, + mpc2000xl_pgm_t::SOUND_GENERATION_MODE_VEL_SW, + mpc2000xl_pgm_t::SOUND_GENERATION_MODE_DCY_SW, +}; +bool mpc2000xl_pgm_t::_is_defined_sound_generation_mode_t(mpc2000xl_pgm_t::sound_generation_mode_t v) { + return mpc2000xl_pgm_t::_values_sound_generation_mode_t.find(v) != mpc2000xl_pgm_t::_values_sound_generation_mode_t.end(); +} +const std::set mpc2000xl_pgm_t::_values_voice_overlap_mode_t{ + mpc2000xl_pgm_t::VOICE_OVERLAP_MODE_POLY, + mpc2000xl_pgm_t::VOICE_OVERLAP_MODE_MONO, + mpc2000xl_pgm_t::VOICE_OVERLAP_MODE_NOTE_OFF, +}; +bool mpc2000xl_pgm_t::_is_defined_voice_overlap_mode_t(mpc2000xl_pgm_t::voice_overlap_mode_t v) { + return mpc2000xl_pgm_t::_values_voice_overlap_mode_t.find(v) != mpc2000xl_pgm_t::_values_voice_overlap_mode_t.end(); +} + +mpc2000xl_pgm_t::mpc2000xl_pgm_t(kaitai::kstream* p__io, kaitai::kstruct* p__parent, mpc2000xl_pgm_t* p__root) : kaitai::kstruct(p__io) { + m__parent = p__parent; + m__root = p__root ? p__root : this; + m__dirty = false; +} + +void mpc2000xl_pgm_t::_read() { + m_magic = m__io->read_bytes(2); + if (!(m_magic == std::string("\x07\x04", 2))) { + throw kaitai::validation_not_equal_error(std::string("\x07\x04", 2), m_magic, m__io, std::string("/seq/0")); + } + m_sound_count = m__io->read_u2le(); + m_sound_names = std::unique_ptr>(new std::vector()); + const int l_sound_names = sound_count(); + for (int i = 0; i < l_sound_names; i++) { + m_sound_names->push_back(std::move(kaitai::kstream::bytes_to_str(m__io->read_bytes(17), "ASCII"))); + } + m__unnamed3 = m__io->read_bytes(2); + m_name = kaitai::kstream::bytes_to_str(m__io->read_bytes(17), "ASCII"); + m_slider = std::unique_ptr(new slider_t(m__io, this, m__root)); + m_slider->_read(); + m_program_change = m__io->read_u1(); + m__unnamed7 = m__io->read_bytes(5); + m_note_parameters = std::unique_ptr>>(new std::vector>()); + const int l_note_parameters = 64; + for (int i = 0; i < l_note_parameters; i++) { + std::unique_ptr _t_note_parameters = std::unique_ptr(new note_t(m__io, this, m__root)); + try { + _t_note_parameters->_read(); + } catch(...) { + m_note_parameters->push_back(std::move(_t_note_parameters)); + throw; + } + m_note_parameters->push_back(std::move(_t_note_parameters)); + } + m__unnamed9 = m__io->read_bytes(1); + m_pad_mixers = std::unique_ptr>>(new std::vector>()); + const int l_pad_mixers = 64; + for (int i = 0; i < l_pad_mixers; i++) { + std::unique_ptr _t_pad_mixers = std::unique_ptr(new pad_mixer_t(m__io, this, m__root)); + try { + _t_pad_mixers->_read(); + } catch(...) { + m_pad_mixers->push_back(std::move(_t_pad_mixers)); + throw; + } + m_pad_mixers->push_back(std::move(_t_pad_mixers)); + } + m__unnamed11 = m__io->read_bytes(3); + m_pad_to_note_mapping = std::unique_ptr>(new std::vector()); + const int l_pad_to_note_mapping = 64; + for (int i = 0; i < l_pad_to_note_mapping; i++) { + m_pad_to_note_mapping->push_back(std::move(m__io->read_s1())); + } + m__unnamed13 = m__io->read_bytes(200); + m__dirty = false; +} + +void mpc2000xl_pgm_t::_fetch_instances() { + for (std::size_t i = 0; i < m_sound_names->size(); ++i) { + } + m_slider.get()->_fetch_instances(); + for (std::size_t i = 0; i < m_note_parameters->size(); ++i) { + m_note_parameters->at(i).get()->_fetch_instances(); + } + for (std::size_t i = 0; i < m_pad_mixers->size(); ++i) { + m_pad_mixers->at(i).get()->_fetch_instances(); + } + for (std::size_t i = 0; i < m_pad_to_note_mapping->size(); ++i) { + } +} + +void mpc2000xl_pgm_t::_write() { + m__io->write_bytes(m_magic); + m__io->write_u2le(m_sound_count); + if (m_sound_names == nullptr) { + throw std::runtime_error("/seq/2: repeated field is not set"); + } + for (std::vector::const_iterator it = m_sound_names->begin(); it != m_sound_names->end(); ++it) { + m__io->write_bytes((*it)); + } + m__io->write_bytes(m__unnamed3); + m__io->write_bytes(m_name); + if (m_slider.get() == nullptr) { + throw std::runtime_error("/seq/5: nested object is not set"); + } + m_slider.get()->_set_io(m__io); + m_slider.get()->_write(); + m__io->write_u1(m_program_change); + m__io->write_bytes(m__unnamed7); + if (m_note_parameters == nullptr) { + throw std::runtime_error("/seq/8: repeated field is not set"); + } + for (std::vector>::const_iterator it = m_note_parameters->begin(); it != m_note_parameters->end(); ++it) { + if ((*it).get() == nullptr) { + throw std::runtime_error("/seq/8: nested object is not set"); + } + (*it).get()->_set_io(m__io); + (*it).get()->_write(); + } + m__io->write_bytes(m__unnamed9); + if (m_pad_mixers == nullptr) { + throw std::runtime_error("/seq/10: repeated field is not set"); + } + for (std::vector>::const_iterator it = m_pad_mixers->begin(); it != m_pad_mixers->end(); ++it) { + if ((*it).get() == nullptr) { + throw std::runtime_error("/seq/10: nested object is not set"); + } + (*it).get()->_set_io(m__io); + (*it).get()->_write(); + } + m__io->write_bytes(m__unnamed11); + if (m_pad_to_note_mapping == nullptr) { + throw std::runtime_error("/seq/12: repeated field is not set"); + } + for (std::vector::const_iterator it = m_pad_to_note_mapping->begin(); it != m_pad_to_note_mapping->end(); ++it) { + m__io->write_s1((*it)); + } + m__io->write_bytes(m__unnamed13); + _fetch_instances(); + m__dirty = false; +} + +void mpc2000xl_pgm_t::_check() { + if (m_magic.size() != static_cast(2)) { + throw std::runtime_error("/seq/0: size mismatch"); + } + if (!(m_magic == std::string("\x07\x04", 2))) { + throw kaitai::validation_not_equal_error(std::string("\x07\x04", 2), m_magic, m__io, std::string("/seq/0")); + } + if (m_sound_names == nullptr) { + throw std::runtime_error("/seq/2: repeated field is not set"); + } + if (m_sound_names->size() != static_cast(sound_count())) { + throw std::runtime_error("/seq/2: repeat-expr size mismatch"); + } + for (std::vector::const_iterator it = m_sound_names->begin(); it != m_sound_names->end(); ++it) { + if ((*it).size() != static_cast(17)) { + throw std::runtime_error("/seq/2: size mismatch"); + } + } + if (m__unnamed3.size() != static_cast(2)) { + throw std::runtime_error("/seq/3: size mismatch"); + } + if (m_name.size() != static_cast(17)) { + throw std::runtime_error("/seq/4: size mismatch"); + } + if (m_slider.get() == nullptr) { + throw std::runtime_error("/seq/5: nested object is not set"); + } + m_slider.get()->_set_io(m__io); + m_slider.get()->_check(); + if (m__unnamed7.size() != static_cast(5)) { + throw std::runtime_error("/seq/7: size mismatch"); + } + if (m_note_parameters == nullptr) { + throw std::runtime_error("/seq/8: repeated field is not set"); + } + if (m_note_parameters->size() != static_cast(64)) { + throw std::runtime_error("/seq/8: repeat-expr size mismatch"); + } + for (std::vector>::const_iterator it = m_note_parameters->begin(); it != m_note_parameters->end(); ++it) { + if ((*it).get() == nullptr) { + throw std::runtime_error("/seq/8: nested object is not set"); + } + (*it).get()->_set_io(m__io); + (*it).get()->_check(); + } + if (m__unnamed9.size() != static_cast(1)) { + throw std::runtime_error("/seq/9: size mismatch"); + } + if (m_pad_mixers == nullptr) { + throw std::runtime_error("/seq/10: repeated field is not set"); + } + if (m_pad_mixers->size() != static_cast(64)) { + throw std::runtime_error("/seq/10: repeat-expr size mismatch"); + } + for (std::vector>::const_iterator it = m_pad_mixers->begin(); it != m_pad_mixers->end(); ++it) { + if ((*it).get() == nullptr) { + throw std::runtime_error("/seq/10: nested object is not set"); + } + (*it).get()->_set_io(m__io); + (*it).get()->_check(); + } + if (m__unnamed11.size() != static_cast(3)) { + throw std::runtime_error("/seq/11: size mismatch"); + } + if (m_pad_to_note_mapping == nullptr) { + throw std::runtime_error("/seq/12: repeated field is not set"); + } + if (m_pad_to_note_mapping->size() != static_cast(64)) { + throw std::runtime_error("/seq/12: repeat-expr size mismatch"); + } + for (std::vector::const_iterator it = m_pad_to_note_mapping->begin(); it != m_pad_to_note_mapping->end(); ++it) { + } + if (m__unnamed13.size() != static_cast(200)) { + throw std::runtime_error("/seq/13: size mismatch"); + } + m__dirty = false; +} + +mpc2000xl_pgm_t::~mpc2000xl_pgm_t() {} + +mpc2000xl_pgm_t::note_t::note_t(kaitai::kstream* p__io, mpc2000xl_pgm_t* p__parent, mpc2000xl_pgm_t* p__root) : kaitai::kstruct(p__io) { + m__parent = p__parent; + m__root = p__root; + m__dirty = false; +} + +void mpc2000xl_pgm_t::note_t::_read() { + m_sound_index = m__io->read_u1(); + m_sound_generation_mode = static_cast(m__io->read_u1()); + m_velocity_range_lower = m__io->read_u1(); + m_also_play_use_note_1 = m__io->read_u1(); + m_velocity_range_upper = m__io->read_u1(); + m_also_play_use_note_2 = m__io->read_u1(); + m_voice_overlap_mode = static_cast(m__io->read_u1()); + m_mute_assign_1 = m__io->read_u1(); + m_mute_assign_2 = m__io->read_u1(); + m_tune = m__io->read_s2le(); + m_attack = m__io->read_u1(); + m_decay = m__io->read_u1(); + m_decay_mode = static_cast(m__io->read_u1()); + m_cutoff = m__io->read_u1(); + m_resonance = m__io->read_u1(); + m_velocity_envelope_to_filter_attack = m__io->read_u1(); + m_velocity_envelope_to_filter_decay = m__io->read_u1(); + m_velocity_envelope_to_filter_amount = m__io->read_u1(); + m_velocity_to_level = m__io->read_u1(); + m_velocity_to_attack = m__io->read_u1(); + m_velocity_to_start = m__io->read_u1(); + m_velocity_to_cutoff = m__io->read_u1(); + m_slider_parameter = static_cast(m__io->read_u1()); + m_velocity_to_pitch = m__io->read_u1(); + m__dirty = false; +} + +void mpc2000xl_pgm_t::note_t::_fetch_instances() { +} + +void mpc2000xl_pgm_t::note_t::_write() { + m__io->write_u1(m_sound_index); + m__io->write_u1(static_cast(m_sound_generation_mode)); + m__io->write_u1(m_velocity_range_lower); + m__io->write_u1(m_also_play_use_note_1); + m__io->write_u1(m_velocity_range_upper); + m__io->write_u1(m_also_play_use_note_2); + m__io->write_u1(static_cast(m_voice_overlap_mode)); + m__io->write_u1(m_mute_assign_1); + m__io->write_u1(m_mute_assign_2); + m__io->write_s2le(m_tune); + m__io->write_u1(m_attack); + m__io->write_u1(m_decay); + m__io->write_u1(static_cast(m_decay_mode)); + m__io->write_u1(m_cutoff); + m__io->write_u1(m_resonance); + m__io->write_u1(m_velocity_envelope_to_filter_attack); + m__io->write_u1(m_velocity_envelope_to_filter_decay); + m__io->write_u1(m_velocity_envelope_to_filter_amount); + m__io->write_u1(m_velocity_to_level); + m__io->write_u1(m_velocity_to_attack); + m__io->write_u1(m_velocity_to_start); + m__io->write_u1(m_velocity_to_cutoff); + m__io->write_u1(static_cast(m_slider_parameter)); + m__io->write_u1(m_velocity_to_pitch); + _fetch_instances(); + m__dirty = false; +} + +void mpc2000xl_pgm_t::note_t::_check() { + m__dirty = false; +} + +mpc2000xl_pgm_t::note_t::~note_t() {} + +mpc2000xl_pgm_t::pad_mixer_t::pad_mixer_t(kaitai::kstream* p__io, mpc2000xl_pgm_t* p__parent, mpc2000xl_pgm_t* p__root) : kaitai::kstruct(p__io) { + m__parent = p__parent; + m__root = p__root; + m__dirty = false; +} + +void mpc2000xl_pgm_t::pad_mixer_t::_read() { + m_fx_output = static_cast(m__io->read_u1()); + m_volume = m__io->read_u1(); + m_pan = m__io->read_u1(); + m_volume_individual = m__io->read_u1(); + m_output = m__io->read_u1(); + m_effects_send_level = m__io->read_u1(); + m__dirty = false; +} + +void mpc2000xl_pgm_t::pad_mixer_t::_fetch_instances() { +} + +void mpc2000xl_pgm_t::pad_mixer_t::_write() { + m__io->write_u1(static_cast(m_fx_output)); + m__io->write_u1(m_volume); + m__io->write_u1(m_pan); + m__io->write_u1(m_volume_individual); + m__io->write_u1(m_output); + m__io->write_u1(m_effects_send_level); + _fetch_instances(); + m__dirty = false; +} + +void mpc2000xl_pgm_t::pad_mixer_t::_check() { + m__dirty = false; +} + +mpc2000xl_pgm_t::pad_mixer_t::~pad_mixer_t() {} + +mpc2000xl_pgm_t::slider_t::slider_t(kaitai::kstream* p__io, mpc2000xl_pgm_t* p__parent, mpc2000xl_pgm_t* p__root) : kaitai::kstruct(p__io) { + m__parent = p__parent; + m__root = p__root; + m__dirty = false; +} + +void mpc2000xl_pgm_t::slider_t::_read() { + m_note = m__io->read_u1(); + m_tune_low = m__io->read_s1(); + m_tune_high = m__io->read_s1(); + m_decay_low = m__io->read_s1(); + m_decay_high = m__io->read_s1(); + m_attack_low = m__io->read_s1(); + m_attack_high = m__io->read_s1(); + m_filter_low = m__io->read_s1(); + m_filter_high = m__io->read_s1(); + m__dirty = false; +} + +void mpc2000xl_pgm_t::slider_t::_fetch_instances() { +} + +void mpc2000xl_pgm_t::slider_t::_write() { + m__io->write_u1(m_note); + m__io->write_s1(m_tune_low); + m__io->write_s1(m_tune_high); + m__io->write_s1(m_decay_low); + m__io->write_s1(m_decay_high); + m__io->write_s1(m_attack_low); + m__io->write_s1(m_attack_high); + m__io->write_s1(m_filter_low); + m__io->write_s1(m_filter_high); + _fetch_instances(); + m__dirty = false; +} + +void mpc2000xl_pgm_t::slider_t::_check() { + m__dirty = false; +} + +mpc2000xl_pgm_t::slider_t::~slider_t() {} diff --git a/src/main/file/kaitai/generated/mpc2000xl_pgm.h b/src/main/file/kaitai/generated/mpc2000xl_pgm.h new file mode 100644 index 000000000..5bb5d4b69 --- /dev/null +++ b/src/main/file/kaitai/generated/mpc2000xl_pgm.h @@ -0,0 +1,342 @@ +#pragma once + +// This is a generated file! Please edit source .ksy file and use kaitai-struct-compiler to rebuild + +class mpc2000xl_pgm_t; + +#include "kaitai/kaitaistruct.h" +#include +#include +#include +#include + +#if KAITAI_STRUCT_VERSION < 11000L +#error "Incompatible Kaitai Struct C++/STL API: version 0.11 or later is required" +#endif + +class mpc2000xl_pgm_t : public kaitai::kstruct { + +public: + class note_t; + class pad_mixer_t; + class slider_t; + + enum decay_mode_t { + DECAY_MODE_END = 0, + DECAY_MODE_START = 1 + }; + static bool _is_defined_decay_mode_t(decay_mode_t v); + +private: + static const std::set _values_decay_mode_t; + +public: + + enum fx_output_t { + FX_OUTPUT_NONE = 0, + FX_OUTPUT_M1 = 1, + FX_OUTPUT_M2 = 2, + FX_OUTPUT_R1 = 3, + FX_OUTPUT_R2 = 4 + }; + static bool _is_defined_fx_output_t(fx_output_t v); + +private: + static const std::set _values_fx_output_t; + +public: + + enum slider_parameter_t { + SLIDER_PARAMETER_TUNING = 0, + SLIDER_PARAMETER_DECAY = 1, + SLIDER_PARAMETER_ATTACK = 2, + SLIDER_PARAMETER_FILTER = 3 + }; + static bool _is_defined_slider_parameter_t(slider_parameter_t v); + +private: + static const std::set _values_slider_parameter_t; + +public: + + enum sound_generation_mode_t { + SOUND_GENERATION_MODE_NORMAL = 0, + SOUND_GENERATION_MODE_SIMULT = 1, + SOUND_GENERATION_MODE_VEL_SW = 2, + SOUND_GENERATION_MODE_DCY_SW = 3 + }; + static bool _is_defined_sound_generation_mode_t(sound_generation_mode_t v); + +private: + static const std::set _values_sound_generation_mode_t; + +public: + + enum voice_overlap_mode_t { + VOICE_OVERLAP_MODE_POLY = 0, + VOICE_OVERLAP_MODE_MONO = 1, + VOICE_OVERLAP_MODE_NOTE_OFF = 2 + }; + static bool _is_defined_voice_overlap_mode_t(voice_overlap_mode_t v); + +private: + static const std::set _values_voice_overlap_mode_t; + +public: + +private: + bool m__dirty; + +public: + + mpc2000xl_pgm_t(kaitai::kstream* p__io, kaitai::kstruct* p__parent = nullptr, mpc2000xl_pgm_t* p__root = nullptr); + void _read(); + void _fetch_instances(); + void _write(); + void _check(); + ~mpc2000xl_pgm_t(); + + class note_t : public kaitai::kstruct { + + public: + + private: + bool m__dirty; + + public: + + note_t(kaitai::kstream* p__io, mpc2000xl_pgm_t* p__parent = nullptr, mpc2000xl_pgm_t* p__root = nullptr); + void _read(); + void _fetch_instances(); + void _write(); + void _check(); + ~note_t(); + uint8_t sound_index() const { return m_sound_index; } + void set_sound_index(uint8_t _v) { m__dirty = true; m_sound_index = std::move(_v); } + sound_generation_mode_t sound_generation_mode() const { return m_sound_generation_mode; } + void set_sound_generation_mode(sound_generation_mode_t _v) { m__dirty = true; m_sound_generation_mode = std::move(_v); } + uint8_t velocity_range_lower() const { return m_velocity_range_lower; } + void set_velocity_range_lower(uint8_t _v) { m__dirty = true; m_velocity_range_lower = std::move(_v); } + uint8_t also_play_use_note_1() const { return m_also_play_use_note_1; } + void set_also_play_use_note_1(uint8_t _v) { m__dirty = true; m_also_play_use_note_1 = std::move(_v); } + uint8_t velocity_range_upper() const { return m_velocity_range_upper; } + void set_velocity_range_upper(uint8_t _v) { m__dirty = true; m_velocity_range_upper = std::move(_v); } + uint8_t also_play_use_note_2() const { return m_also_play_use_note_2; } + void set_also_play_use_note_2(uint8_t _v) { m__dirty = true; m_also_play_use_note_2 = std::move(_v); } + voice_overlap_mode_t voice_overlap_mode() const { return m_voice_overlap_mode; } + void set_voice_overlap_mode(voice_overlap_mode_t _v) { m__dirty = true; m_voice_overlap_mode = std::move(_v); } + uint8_t mute_assign_1() const { return m_mute_assign_1; } + void set_mute_assign_1(uint8_t _v) { m__dirty = true; m_mute_assign_1 = std::move(_v); } + uint8_t mute_assign_2() const { return m_mute_assign_2; } + void set_mute_assign_2(uint8_t _v) { m__dirty = true; m_mute_assign_2 = std::move(_v); } + int16_t tune() const { return m_tune; } + void set_tune(int16_t _v) { m__dirty = true; m_tune = std::move(_v); } + uint8_t attack() const { return m_attack; } + void set_attack(uint8_t _v) { m__dirty = true; m_attack = std::move(_v); } + uint8_t decay() const { return m_decay; } + void set_decay(uint8_t _v) { m__dirty = true; m_decay = std::move(_v); } + decay_mode_t decay_mode() const { return m_decay_mode; } + void set_decay_mode(decay_mode_t _v) { m__dirty = true; m_decay_mode = std::move(_v); } + uint8_t cutoff() const { return m_cutoff; } + void set_cutoff(uint8_t _v) { m__dirty = true; m_cutoff = std::move(_v); } + uint8_t resonance() const { return m_resonance; } + void set_resonance(uint8_t _v) { m__dirty = true; m_resonance = std::move(_v); } + uint8_t velocity_envelope_to_filter_attack() const { return m_velocity_envelope_to_filter_attack; } + void set_velocity_envelope_to_filter_attack(uint8_t _v) { m__dirty = true; m_velocity_envelope_to_filter_attack = std::move(_v); } + uint8_t velocity_envelope_to_filter_decay() const { return m_velocity_envelope_to_filter_decay; } + void set_velocity_envelope_to_filter_decay(uint8_t _v) { m__dirty = true; m_velocity_envelope_to_filter_decay = std::move(_v); } + uint8_t velocity_envelope_to_filter_amount() const { return m_velocity_envelope_to_filter_amount; } + void set_velocity_envelope_to_filter_amount(uint8_t _v) { m__dirty = true; m_velocity_envelope_to_filter_amount = std::move(_v); } + uint8_t velocity_to_level() const { return m_velocity_to_level; } + void set_velocity_to_level(uint8_t _v) { m__dirty = true; m_velocity_to_level = std::move(_v); } + uint8_t velocity_to_attack() const { return m_velocity_to_attack; } + void set_velocity_to_attack(uint8_t _v) { m__dirty = true; m_velocity_to_attack = std::move(_v); } + uint8_t velocity_to_start() const { return m_velocity_to_start; } + void set_velocity_to_start(uint8_t _v) { m__dirty = true; m_velocity_to_start = std::move(_v); } + uint8_t velocity_to_cutoff() const { return m_velocity_to_cutoff; } + void set_velocity_to_cutoff(uint8_t _v) { m__dirty = true; m_velocity_to_cutoff = std::move(_v); } + slider_parameter_t slider_parameter() const { return m_slider_parameter; } + void set_slider_parameter(slider_parameter_t _v) { m__dirty = true; m_slider_parameter = std::move(_v); } + uint8_t velocity_to_pitch() const { return m_velocity_to_pitch; } + void set_velocity_to_pitch(uint8_t _v) { m__dirty = true; m_velocity_to_pitch = std::move(_v); } + mpc2000xl_pgm_t* _root() const { return m__root; } + void set__root(mpc2000xl_pgm_t* _v) { m__dirty = true; m__root = std::move(_v); } + kaitai::kstruct* _parent() const { return m__parent; } + void set__parent(mpc2000xl_pgm_t* _v) { m__dirty = true; m__parent = std::move(_v); } + + private: + uint8_t m_sound_index; + sound_generation_mode_t m_sound_generation_mode; + uint8_t m_velocity_range_lower; + uint8_t m_also_play_use_note_1; + uint8_t m_velocity_range_upper; + uint8_t m_also_play_use_note_2; + voice_overlap_mode_t m_voice_overlap_mode; + uint8_t m_mute_assign_1; + uint8_t m_mute_assign_2; + int16_t m_tune; + uint8_t m_attack; + uint8_t m_decay; + decay_mode_t m_decay_mode; + uint8_t m_cutoff; + uint8_t m_resonance; + uint8_t m_velocity_envelope_to_filter_attack; + uint8_t m_velocity_envelope_to_filter_decay; + uint8_t m_velocity_envelope_to_filter_amount; + uint8_t m_velocity_to_level; + uint8_t m_velocity_to_attack; + uint8_t m_velocity_to_start; + uint8_t m_velocity_to_cutoff; + slider_parameter_t m_slider_parameter; + uint8_t m_velocity_to_pitch; + mpc2000xl_pgm_t* m__root; + mpc2000xl_pgm_t* m__parent; + }; + + class pad_mixer_t : public kaitai::kstruct { + + public: + + private: + bool m__dirty; + + public: + + pad_mixer_t(kaitai::kstream* p__io, mpc2000xl_pgm_t* p__parent = nullptr, mpc2000xl_pgm_t* p__root = nullptr); + void _read(); + void _fetch_instances(); + void _write(); + void _check(); + ~pad_mixer_t(); + fx_output_t fx_output() const { return m_fx_output; } + void set_fx_output(fx_output_t _v) { m__dirty = true; m_fx_output = std::move(_v); } + uint8_t volume() const { return m_volume; } + void set_volume(uint8_t _v) { m__dirty = true; m_volume = std::move(_v); } + uint8_t pan() const { return m_pan; } + void set_pan(uint8_t _v) { m__dirty = true; m_pan = std::move(_v); } + uint8_t volume_individual() const { return m_volume_individual; } + void set_volume_individual(uint8_t _v) { m__dirty = true; m_volume_individual = std::move(_v); } + uint8_t output() const { return m_output; } + void set_output(uint8_t _v) { m__dirty = true; m_output = std::move(_v); } + uint8_t effects_send_level() const { return m_effects_send_level; } + void set_effects_send_level(uint8_t _v) { m__dirty = true; m_effects_send_level = std::move(_v); } + mpc2000xl_pgm_t* _root() const { return m__root; } + void set__root(mpc2000xl_pgm_t* _v) { m__dirty = true; m__root = std::move(_v); } + kaitai::kstruct* _parent() const { return m__parent; } + void set__parent(mpc2000xl_pgm_t* _v) { m__dirty = true; m__parent = std::move(_v); } + + private: + fx_output_t m_fx_output; + uint8_t m_volume; + uint8_t m_pan; + uint8_t m_volume_individual; + uint8_t m_output; + uint8_t m_effects_send_level; + mpc2000xl_pgm_t* m__root; + mpc2000xl_pgm_t* m__parent; + }; + + class slider_t : public kaitai::kstruct { + + public: + + private: + bool m__dirty; + + public: + + slider_t(kaitai::kstream* p__io, mpc2000xl_pgm_t* p__parent = nullptr, mpc2000xl_pgm_t* p__root = nullptr); + void _read(); + void _fetch_instances(); + void _write(); + void _check(); + ~slider_t(); + uint8_t note() const { return m_note; } + void set_note(uint8_t _v) { m__dirty = true; m_note = std::move(_v); } + int8_t tune_low() const { return m_tune_low; } + void set_tune_low(int8_t _v) { m__dirty = true; m_tune_low = std::move(_v); } + int8_t tune_high() const { return m_tune_high; } + void set_tune_high(int8_t _v) { m__dirty = true; m_tune_high = std::move(_v); } + int8_t decay_low() const { return m_decay_low; } + void set_decay_low(int8_t _v) { m__dirty = true; m_decay_low = std::move(_v); } + int8_t decay_high() const { return m_decay_high; } + void set_decay_high(int8_t _v) { m__dirty = true; m_decay_high = std::move(_v); } + int8_t attack_low() const { return m_attack_low; } + void set_attack_low(int8_t _v) { m__dirty = true; m_attack_low = std::move(_v); } + int8_t attack_high() const { return m_attack_high; } + void set_attack_high(int8_t _v) { m__dirty = true; m_attack_high = std::move(_v); } + int8_t filter_low() const { return m_filter_low; } + void set_filter_low(int8_t _v) { m__dirty = true; m_filter_low = std::move(_v); } + int8_t filter_high() const { return m_filter_high; } + void set_filter_high(int8_t _v) { m__dirty = true; m_filter_high = std::move(_v); } + mpc2000xl_pgm_t* _root() const { return m__root; } + void set__root(mpc2000xl_pgm_t* _v) { m__dirty = true; m__root = std::move(_v); } + kaitai::kstruct* _parent() const { return m__parent; } + void set__parent(mpc2000xl_pgm_t* _v) { m__dirty = true; m__parent = std::move(_v); } + + private: + uint8_t m_note; + int8_t m_tune_low; + int8_t m_tune_high; + int8_t m_decay_low; + int8_t m_decay_high; + int8_t m_attack_low; + int8_t m_attack_high; + int8_t m_filter_low; + int8_t m_filter_high; + mpc2000xl_pgm_t* m__root; + mpc2000xl_pgm_t* m__parent; + }; + +public: + std::string magic() const { return m_magic; } + void set_magic(std::string _v) { m__dirty = true; m_magic = std::move(_v); } + uint16_t sound_count() const { return m_sound_count; } + void set_sound_count(uint16_t _v) { m__dirty = true; m_sound_count = std::move(_v); } + std::vector* sound_names() const { return m_sound_names.get(); } + void set_sound_names(std::unique_ptr> _v) { m__dirty = true; m_sound_names = std::move(_v); } + std::string _unnamed3() const { return m__unnamed3; } + void set__unnamed3(std::string _v) { m__dirty = true; m__unnamed3 = std::move(_v); } + std::string name() const { return m_name; } + void set_name(std::string _v) { m__dirty = true; m_name = std::move(_v); } + slider_t* slider() const { return m_slider.get(); } + void set_slider(std::unique_ptr _v) { m__dirty = true; m_slider = std::move(_v); } + uint8_t program_change() const { return m_program_change; } + void set_program_change(uint8_t _v) { m__dirty = true; m_program_change = std::move(_v); } + std::string _unnamed7() const { return m__unnamed7; } + void set__unnamed7(std::string _v) { m__dirty = true; m__unnamed7 = std::move(_v); } + std::vector>* note_parameters() const { return m_note_parameters.get(); } + void set_note_parameters(std::unique_ptr>> _v) { m__dirty = true; m_note_parameters = std::move(_v); } + std::string _unnamed9() const { return m__unnamed9; } + void set__unnamed9(std::string _v) { m__dirty = true; m__unnamed9 = std::move(_v); } + std::vector>* pad_mixers() const { return m_pad_mixers.get(); } + void set_pad_mixers(std::unique_ptr>> _v) { m__dirty = true; m_pad_mixers = std::move(_v); } + std::string _unnamed11() const { return m__unnamed11; } + void set__unnamed11(std::string _v) { m__dirty = true; m__unnamed11 = std::move(_v); } + std::vector* pad_to_note_mapping() const { return m_pad_to_note_mapping.get(); } + void set_pad_to_note_mapping(std::unique_ptr> _v) { m__dirty = true; m_pad_to_note_mapping = std::move(_v); } + std::string _unnamed13() const { return m__unnamed13; } + void set__unnamed13(std::string _v) { m__dirty = true; m__unnamed13 = std::move(_v); } + mpc2000xl_pgm_t* _root() const { return m__root; } + void set__root(mpc2000xl_pgm_t* _v) { m__dirty = true; m__root = std::move(_v); } + kaitai::kstruct* _parent() const { return m__parent; } + void set__parent(kaitai::kstruct* _v) { m__dirty = true; m__parent = std::move(_v); } + +private: + std::string m_magic; + uint16_t m_sound_count; + std::unique_ptr> m_sound_names; + std::string m__unnamed3; + std::string m_name; + std::unique_ptr m_slider; + uint8_t m_program_change; + std::string m__unnamed7; + std::unique_ptr>> m_note_parameters; + std::string m__unnamed9; + std::unique_ptr>> m_pad_mixers; + std::string m__unnamed11; + std::unique_ptr> m_pad_to_note_mapping; + std::string m__unnamed13; + mpc2000xl_pgm_t* m__root; + kaitai::kstruct* m__parent; +}; diff --git a/src/main/file/kaitai/generated/mpc3000_all_v3.cpp b/src/main/file/kaitai/generated/mpc3000_all_v3.cpp new file mode 100644 index 000000000..40955ccfb --- /dev/null +++ b/src/main/file/kaitai/generated/mpc3000_all_v3.cpp @@ -0,0 +1,699 @@ +// This is a generated file! Please edit source .ksy file and use kaitai-struct-compiler to rebuild + +#include "mpc3000_all_v3.h" +#include "kaitai/exceptions.h" +#include +#include + +mpc3000_all_v3_t::mpc3000_all_v3_t(kaitai::kstream* p__io, kaitai::kstruct* p__parent, mpc3000_all_v3_t* p__root) : kaitai::kstruct(p__io) { + m__parent = p__parent; + m__root = p__root ? p__root : this; + m__dirty = false; +} + +void mpc3000_all_v3_t::_read() { + m_all_file_header = std::unique_ptr(new all_file_header_t(m__io, this, m__root)); + m_all_file_header->_read(); + m_sequences = std::unique_ptr>>(new std::vector>()); + { + int i = 0; + sequence_t* _; + do { + std::unique_ptr _t_sequences = std::unique_ptr(new sequence_t(m__io, this, m__root)); + try { + _t_sequences->_read(); + } catch(...) { + _ = _t_sequences.get(); + m_sequences->push_back(std::move(_t_sequences)); + throw; + } + _ = _t_sequences.get(); + m_sequences->push_back(std::move(_t_sequences)); + i++; + } while (!(_io()->pos() - 6 >= all_file_header()->total_number_of_bytes_in_all_sequences() - 1)); + } + m_sequences_terminator = m__io->read_bytes(1); + if (!(m_sequences_terminator == std::string("\xFF", 1))) { + throw kaitai::validation_not_equal_error(std::string("\xFF", 1), m_sequences_terminator, m__io, std::string("/seq/2")); + } + m_songs = std::unique_ptr>>(new std::vector>()); + { + int i = 0; + song_t* _; + do { + std::unique_ptr _t_songs = std::unique_ptr(new song_t(m__io, this, m__root)); + try { + _t_songs->_read(); + } catch(...) { + _ = _t_songs.get(); + m_songs->push_back(std::move(_t_songs)); + throw; + } + _ = _t_songs.get(); + m_songs->push_back(std::move(_t_songs)); + i++; + } while (!(_->number_of_steps() == 0)); + } + m__dirty = false; +} + +void mpc3000_all_v3_t::_fetch_instances() { + m_all_file_header.get()->_fetch_instances(); + for (std::size_t i = 0; i < m_sequences->size(); ++i) { + m_sequences->at(i).get()->_fetch_instances(); + } + for (std::size_t i = 0; i < m_songs->size(); ++i) { + m_songs->at(i).get()->_fetch_instances(); + } +} + +void mpc3000_all_v3_t::_write() { + if (m_all_file_header.get() == nullptr) { + throw std::runtime_error("/seq/0: nested object is not set"); + } + m_all_file_header.get()->_set_io(m__io); + m_all_file_header.get()->_write(); + if (m_sequences == nullptr) { + throw std::runtime_error("/seq/1: repeated field is not set"); + } + for (std::vector>::const_iterator it = m_sequences->begin(); it != m_sequences->end(); ++it) { + if ((*it).get() == nullptr) { + throw std::runtime_error("/seq/1: nested object is not set"); + } + (*it).get()->_set_io(m__io); + (*it).get()->_write(); + } + m__io->write_bytes(m_sequences_terminator); + if (m_songs == nullptr) { + throw std::runtime_error("/seq/3: repeated field is not set"); + } + for (std::vector>::const_iterator it = m_songs->begin(); it != m_songs->end(); ++it) { + if ((*it).get() == nullptr) { + throw std::runtime_error("/seq/3: nested object is not set"); + } + (*it).get()->_set_io(m__io); + (*it).get()->_write(); + } + _fetch_instances(); + m__dirty = false; +} + +void mpc3000_all_v3_t::_check() { + if (m_all_file_header.get() == nullptr) { + throw std::runtime_error("/seq/0: nested object is not set"); + } + m_all_file_header.get()->_set_io(m__io); + m_all_file_header.get()->_check(); + if (m_sequences == nullptr) { + throw std::runtime_error("/seq/1: repeated field is not set"); + } + if (m_sequences->empty()) { + throw std::runtime_error("/seq/1: repeat-until field must not be empty"); + } + for (std::vector>::const_iterator it = m_sequences->begin(); it != m_sequences->end(); ++it) { + const std::size_t i = static_cast(it - m_sequences->begin()); + const sequence_t* _ = (*it).get(); + if ((*it).get() == nullptr) { + throw std::runtime_error("/seq/1: nested object is not set"); + } + (*it).get()->_set_io(m__io); + (*it).get()->_check(); + const bool _is_last = (i == m_sequences->size() - 1); + if ((_io()->pos() - 6 >= all_file_header()->total_number_of_bytes_in_all_sequences() - 1) != _is_last) { + throw std::runtime_error("/seq/1: repeat-until condition mismatch"); + } + } + if (m_sequences_terminator.size() != static_cast(1)) { + throw std::runtime_error("/seq/2: size mismatch"); + } + if (!(m_sequences_terminator == std::string("\xFF", 1))) { + throw kaitai::validation_not_equal_error(std::string("\xFF", 1), m_sequences_terminator, m__io, std::string("/seq/2")); + } + if (m_songs == nullptr) { + throw std::runtime_error("/seq/3: repeated field is not set"); + } + if (m_songs->empty()) { + throw std::runtime_error("/seq/3: repeat-until field must not be empty"); + } + for (std::vector>::const_iterator it = m_songs->begin(); it != m_songs->end(); ++it) { + const std::size_t i = static_cast(it - m_songs->begin()); + const song_t* _ = (*it).get(); + if ((*it).get() == nullptr) { + throw std::runtime_error("/seq/3: nested object is not set"); + } + (*it).get()->_set_io(m__io); + (*it).get()->_check(); + const bool _is_last = (i == m_songs->size() - 1); + if ((_->number_of_steps() == 0) != _is_last) { + throw std::runtime_error("/seq/3: repeat-until condition mismatch"); + } + } + m__dirty = false; +} + +mpc3000_all_v3_t::~mpc3000_all_v3_t() {} + +mpc3000_all_v3_t::all_file_header_t::all_file_header_t(kaitai::kstream* p__io, mpc3000_all_v3_t* p__parent, mpc3000_all_v3_t* p__root) : kaitai::kstruct(p__io) { + m__parent = p__parent; + m__root = p__root; + m__dirty = false; +} + +void mpc3000_all_v3_t::all_file_header_t::_read() { + m_file_id = m__io->read_bytes(1); + if (!(m_file_id == std::string("\x04", 1))) { + throw kaitai::validation_not_equal_error(std::string("\x04", 1), m_file_id, m__io, std::string("/types/all_file_header/seq/0")); + } + m_file_version = m__io->read_bytes(1); + if (!(m_file_version == std::string("\x03", 1))) { + throw kaitai::validation_not_equal_error(std::string("\x03", 1), m_file_version, m__io, std::string("/types/all_file_header/seq/1")); + } + m_total_number_of_bytes_in_all_sequences = m__io->read_u4le(); + m__dirty = false; +} + +void mpc3000_all_v3_t::all_file_header_t::_fetch_instances() { +} + +void mpc3000_all_v3_t::all_file_header_t::_write() { + m__io->write_bytes(m_file_id); + m__io->write_bytes(m_file_version); + m__io->write_u4le(m_total_number_of_bytes_in_all_sequences); + _fetch_instances(); + m__dirty = false; +} + +void mpc3000_all_v3_t::all_file_header_t::_check() { + if (m_file_id.size() != static_cast(1)) { + throw std::runtime_error("/types/all_file_header/seq/0: size mismatch"); + } + if (!(m_file_id == std::string("\x04", 1))) { + throw kaitai::validation_not_equal_error(std::string("\x04", 1), m_file_id, m__io, std::string("/types/all_file_header/seq/0")); + } + if (m_file_version.size() != static_cast(1)) { + throw std::runtime_error("/types/all_file_header/seq/1: size mismatch"); + } + if (!(m_file_version == std::string("\x03", 1))) { + throw kaitai::validation_not_equal_error(std::string("\x03", 1), m_file_version, m__io, std::string("/types/all_file_header/seq/1")); + } + m__dirty = false; +} + +mpc3000_all_v3_t::all_file_header_t::~all_file_header_t() {} + +mpc3000_all_v3_t::misc_chunks_t::misc_chunks_t(kaitai::kstream* p__io, mpc3000_all_v3_t::sequence_t* p__parent, mpc3000_all_v3_t* p__root) : kaitai::kstruct(p__io) { + m__parent = p__parent; + m__root = p__root; + m__dirty = false; + f_position_after_tempo_changes = false; +} + +void mpc3000_all_v3_t::misc_chunks_t::_read() { + m_sequence_header = std::unique_ptr(new mpc3000_seq_v3_t::sequence_header_t(m__io)); + m_sequence_header->_read(); + m_smpte_offset = std::unique_ptr(new mpc3000_seq_v3_t::smpte_offset_t(m__io)); + m_smpte_offset->_read(); + m_mixer = std::unique_ptr>>(new std::vector>()); + const int l_mixer = 64; + for (int i = 0; i < l_mixer; i++) { + std::unique_ptr _t_mixer = std::unique_ptr(new mpc3000_seq_v3_t::mixer_t(m__io)); + try { + _t_mixer->_read(); + } catch(...) { + m_mixer->push_back(std::move(_t_mixer)); + throw; + } + m_mixer->push_back(std::move(_t_mixer)); + } + m_reserved_after_mixer = m__io->read_bytes(2); + if (!(m_reserved_after_mixer == std::string("\x00\x00", 2))) { + throw kaitai::validation_not_equal_error(std::string("\x00\x00", 2), m_reserved_after_mixer, m__io, std::string("/types/misc_chunks/seq/3")); + } + m_delays = std::unique_ptr(new mpc3000_seq_v3_t::delays_t(m__io)); + m_delays->_read(); + m_reserved_after_delays = m__io->read_bytes(3); + if (!(m_reserved_after_delays == std::string("\x00\x00\x00", 3))) { + throw kaitai::validation_not_equal_error(std::string("\x00\x00\x00", 3), m_reserved_after_delays, m__io, std::string("/types/misc_chunks/seq/5")); + } + m_reserved_before_track_summary = m__io->read_bytes(16); + if (!(m_reserved_before_track_summary == std::string("\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", 16))) { + throw kaitai::validation_not_equal_error(std::string("\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", 16), m_reserved_before_track_summary, m__io, std::string("/types/misc_chunks/seq/6")); + } + m_last_active_track = m__io->read_u1(); + m_num_tempo_changes = m__io->read_u1(); + m_num_track_headers = m__io->read_u1(); + m_track_headers = std::unique_ptr>>(new std::vector>()); + const int l_track_headers = num_track_headers(); + for (int i = 0; i < l_track_headers; i++) { + std::unique_ptr _t_track_headers = std::unique_ptr(new mpc3000_seq_v3_t::track_header_t(m__io)); + try { + _t_track_headers->_read(); + } catch(...) { + m_track_headers->push_back(std::move(_t_track_headers)); + throw; + } + m_track_headers->push_back(std::move(_t_track_headers)); + } + m_tempo_changes = std::unique_ptr>>(new std::vector>()); + const int l_tempo_changes = num_tempo_changes(); + for (int i = 0; i < l_tempo_changes; i++) { + std::unique_ptr _t_tempo_changes = std::unique_ptr(new mpc3000_seq_v3_t::tempo_change_t(m__io)); + try { + _t_tempo_changes->_read(); + } catch(...) { + m_tempo_changes->push_back(std::move(_t_tempo_changes)); + throw; + } + m_tempo_changes->push_back(std::move(_t_tempo_changes)); + } + m__dirty = false; +} + +void mpc3000_all_v3_t::misc_chunks_t::_fetch_instances() { + m_sequence_header.get()->_fetch_instances(); + m_smpte_offset.get()->_fetch_instances(); + for (std::size_t i = 0; i < m_mixer->size(); ++i) { + m_mixer->at(i).get()->_fetch_instances(); + } + m_delays.get()->_fetch_instances(); + for (std::size_t i = 0; i < m_track_headers->size(); ++i) { + m_track_headers->at(i).get()->_fetch_instances(); + } + for (std::size_t i = 0; i < m_tempo_changes->size(); ++i) { + m_tempo_changes->at(i).get()->_fetch_instances(); + } +} + +void mpc3000_all_v3_t::misc_chunks_t::_write() { + if (m_sequence_header.get() == nullptr) { + throw std::runtime_error("/types/misc_chunks/seq/0: nested object is not set"); + } + m_sequence_header.get()->_set_io(m__io); + m_sequence_header.get()->_write(); + if (m_smpte_offset.get() == nullptr) { + throw std::runtime_error("/types/misc_chunks/seq/1: nested object is not set"); + } + m_smpte_offset.get()->_set_io(m__io); + m_smpte_offset.get()->_write(); + if (m_mixer == nullptr) { + throw std::runtime_error("/types/misc_chunks/seq/2: repeated field is not set"); + } + for (std::vector>::const_iterator it = m_mixer->begin(); it != m_mixer->end(); ++it) { + if ((*it).get() == nullptr) { + throw std::runtime_error("/types/misc_chunks/seq/2: nested object is not set"); + } + (*it).get()->_set_io(m__io); + (*it).get()->_write(); + } + m__io->write_bytes(m_reserved_after_mixer); + if (m_delays.get() == nullptr) { + throw std::runtime_error("/types/misc_chunks/seq/4: nested object is not set"); + } + m_delays.get()->_set_io(m__io); + m_delays.get()->_write(); + m__io->write_bytes(m_reserved_after_delays); + m__io->write_bytes(m_reserved_before_track_summary); + m__io->write_u1(m_last_active_track); + m__io->write_u1(m_num_tempo_changes); + m__io->write_u1(m_num_track_headers); + if (m_track_headers == nullptr) { + throw std::runtime_error("/types/misc_chunks/seq/10: repeated field is not set"); + } + for (std::vector>::const_iterator it = m_track_headers->begin(); it != m_track_headers->end(); ++it) { + if ((*it).get() == nullptr) { + throw std::runtime_error("/types/misc_chunks/seq/10: nested object is not set"); + } + (*it).get()->_set_io(m__io); + (*it).get()->_write(); + } + if (m_tempo_changes == nullptr) { + throw std::runtime_error("/types/misc_chunks/seq/11: repeated field is not set"); + } + for (std::vector>::const_iterator it = m_tempo_changes->begin(); it != m_tempo_changes->end(); ++it) { + if ((*it).get() == nullptr) { + throw std::runtime_error("/types/misc_chunks/seq/11: nested object is not set"); + } + (*it).get()->_set_io(m__io); + (*it).get()->_write(); + } + _fetch_instances(); + m__dirty = false; +} + +void mpc3000_all_v3_t::misc_chunks_t::_check() { + if (m_sequence_header.get() == nullptr) { + throw std::runtime_error("/types/misc_chunks/seq/0: nested object is not set"); + } + m_sequence_header.get()->_set_io(m__io); + m_sequence_header.get()->_check(); + if (m_smpte_offset.get() == nullptr) { + throw std::runtime_error("/types/misc_chunks/seq/1: nested object is not set"); + } + m_smpte_offset.get()->_set_io(m__io); + m_smpte_offset.get()->_check(); + if (m_mixer == nullptr) { + throw std::runtime_error("/types/misc_chunks/seq/2: repeated field is not set"); + } + if (m_mixer->size() != static_cast(64)) { + throw std::runtime_error("/types/misc_chunks/seq/2: repeat-expr size mismatch"); + } + for (std::vector>::const_iterator it = m_mixer->begin(); it != m_mixer->end(); ++it) { + if ((*it).get() == nullptr) { + throw std::runtime_error("/types/misc_chunks/seq/2: nested object is not set"); + } + (*it).get()->_set_io(m__io); + (*it).get()->_check(); + } + if (m_reserved_after_mixer.size() != static_cast(2)) { + throw std::runtime_error("/types/misc_chunks/seq/3: size mismatch"); + } + if (!(m_reserved_after_mixer == std::string("\x00\x00", 2))) { + throw kaitai::validation_not_equal_error(std::string("\x00\x00", 2), m_reserved_after_mixer, m__io, std::string("/types/misc_chunks/seq/3")); + } + if (m_delays.get() == nullptr) { + throw std::runtime_error("/types/misc_chunks/seq/4: nested object is not set"); + } + m_delays.get()->_set_io(m__io); + m_delays.get()->_check(); + if (m_reserved_after_delays.size() != static_cast(3)) { + throw std::runtime_error("/types/misc_chunks/seq/5: size mismatch"); + } + if (!(m_reserved_after_delays == std::string("\x00\x00\x00", 3))) { + throw kaitai::validation_not_equal_error(std::string("\x00\x00\x00", 3), m_reserved_after_delays, m__io, std::string("/types/misc_chunks/seq/5")); + } + if (m_reserved_before_track_summary.size() != static_cast(16)) { + throw std::runtime_error("/types/misc_chunks/seq/6: size mismatch"); + } + if (!(m_reserved_before_track_summary == std::string("\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", 16))) { + throw kaitai::validation_not_equal_error(std::string("\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", 16), m_reserved_before_track_summary, m__io, std::string("/types/misc_chunks/seq/6")); + } + if (m_track_headers == nullptr) { + throw std::runtime_error("/types/misc_chunks/seq/10: repeated field is not set"); + } + if (m_track_headers->size() != static_cast(num_track_headers())) { + throw std::runtime_error("/types/misc_chunks/seq/10: repeat-expr size mismatch"); + } + for (std::vector>::const_iterator it = m_track_headers->begin(); it != m_track_headers->end(); ++it) { + if ((*it).get() == nullptr) { + throw std::runtime_error("/types/misc_chunks/seq/10: nested object is not set"); + } + (*it).get()->_set_io(m__io); + (*it).get()->_check(); + } + if (m_tempo_changes == nullptr) { + throw std::runtime_error("/types/misc_chunks/seq/11: repeated field is not set"); + } + if (m_tempo_changes->size() != static_cast(num_tempo_changes())) { + throw std::runtime_error("/types/misc_chunks/seq/11: repeat-expr size mismatch"); + } + for (std::vector>::const_iterator it = m_tempo_changes->begin(); it != m_tempo_changes->end(); ++it) { + if ((*it).get() == nullptr) { + throw std::runtime_error("/types/misc_chunks/seq/11: nested object is not set"); + } + (*it).get()->_set_io(m__io); + (*it).get()->_check(); + } + m__dirty = false; +} + +mpc3000_all_v3_t::misc_chunks_t::~misc_chunks_t() {} + +int32_t mpc3000_all_v3_t::misc_chunks_t::position_after_tempo_changes() { + if (f_position_after_tempo_changes) + return m_position_after_tempo_changes; + f_position_after_tempo_changes = true; + m_position_after_tempo_changes = _io()->pos(); + return m_position_after_tempo_changes; +} + +mpc3000_all_v3_t::sequence_t::sequence_t(kaitai::kstream* p__io, mpc3000_all_v3_t* p__parent, mpc3000_all_v3_t* p__root) : kaitai::kstruct(p__io) { + m__parent = p__parent; + m__root = p__root; + m__dirty = false; +} + +void mpc3000_all_v3_t::sequence_t::_read() { + m_misc_chunks = std::unique_ptr(new misc_chunks_t(m__io, this, m__root)); + m_misc_chunks->_read(); + m_events = std::unique_ptr>>(new std::vector>()); + { + int i = 0; + mpc3000_seq_v3_t::event_t* _; + do { + std::unique_ptr _t_events = std::unique_ptr(new mpc3000_seq_v3_t::event_t(i == 0, ((i > 0) ? (events()->at(i - 1)->next_status()) : (255)), misc_chunks()->sequence_header()->event_stream_length_in_bytes()->value() - (_io()->pos() - misc_chunks()->position_after_tempo_changes()), m__io)); + try { + _t_events->_read(); + } catch(...) { + _ = _t_events.get(); + m_events->push_back(std::move(_t_events)); + throw; + } + _ = _t_events.get(); + m_events->push_back(std::move(_t_events)); + i++; + } while (!(misc_chunks()->sequence_header()->event_stream_length_in_bytes()->value() - (_io()->pos() - misc_chunks()->position_after_tempo_changes()) == 0)); + } + m__dirty = false; +} + +void mpc3000_all_v3_t::sequence_t::_fetch_instances() { + m_misc_chunks.get()->_fetch_instances(); + for (std::size_t i = 0; i < m_events->size(); ++i) { + m_events->at(i).get()->_fetch_instances(); + } +} + +void mpc3000_all_v3_t::sequence_t::_write() { + if (m_misc_chunks.get() == nullptr) { + throw std::runtime_error("/types/sequence/seq/0: nested object is not set"); + } + m_misc_chunks.get()->_set_io(m__io); + m_misc_chunks.get()->_write(); + if (m_events == nullptr) { + throw std::runtime_error("/types/sequence/seq/1: repeated field is not set"); + } + for (std::vector>::const_iterator it = m_events->begin(); it != m_events->end(); ++it) { + if ((*it).get() == nullptr) { + throw std::runtime_error("/types/sequence/seq/1: nested object is not set"); + } + (*it).get()->_set_io(m__io); + (*it).get()->_write(); + } + _fetch_instances(); + m__dirty = false; +} + +void mpc3000_all_v3_t::sequence_t::_check() { + if (m_misc_chunks.get() == nullptr) { + throw std::runtime_error("/types/sequence/seq/0: nested object is not set"); + } + m_misc_chunks.get()->_set_io(m__io); + m_misc_chunks.get()->_check(); + if (m_events == nullptr) { + throw std::runtime_error("/types/sequence/seq/1: repeated field is not set"); + } + if (m_events->empty()) { + throw std::runtime_error("/types/sequence/seq/1: repeat-until field must not be empty"); + } + for (std::vector>::const_iterator it = m_events->begin(); it != m_events->end(); ++it) { + const std::size_t i = static_cast(it - m_events->begin()); + const mpc3000_seq_v3_t::event_t* _ = (*it).get(); + if ((*it).get() == nullptr) { + throw std::runtime_error("/types/sequence/seq/1: nested object is not set"); + } + (*it).get()->_set_io(m__io); + (*it).get()->_check(); + const bool _is_last = (i == m_events->size() - 1); + if ((misc_chunks()->sequence_header()->event_stream_length_in_bytes()->value() - (_io()->pos() - misc_chunks()->position_after_tempo_changes()) == 0) != _is_last) { + throw std::runtime_error("/types/sequence/seq/1: repeat-until condition mismatch"); + } + } + m__dirty = false; +} + +mpc3000_all_v3_t::sequence_t::~sequence_t() {} + +mpc3000_all_v3_t::song_t::song_t(kaitai::kstream* p__io, mpc3000_all_v3_t* p__parent, mpc3000_all_v3_t* p__root) : kaitai::kstruct(p__io) { + m__parent = p__parent; + m__root = p__root; + m__dirty = false; +} + +void mpc3000_all_v3_t::song_t::_read() { + m_number_of_steps = m__io->read_u1(); + if (number_of_steps() != 0) { + m_song_body = std::unique_ptr(new song_body_t(m__io, this, m__root)); + m_song_body->_read(); + } + m__dirty = false; +} + +void mpc3000_all_v3_t::song_t::_fetch_instances() { + if (number_of_steps() != 0) { + m_song_body.get()->_fetch_instances(); + } +} + +void mpc3000_all_v3_t::song_t::_write() { + m__io->write_u1(m_number_of_steps); + if (number_of_steps() != 0) { + if (m_song_body.get() == nullptr) { + throw std::runtime_error("/types/song/seq/1: nested object is not set"); + } + m_song_body.get()->_set_io(m__io); + m_song_body.get()->_write(); + } + _fetch_instances(); + m__dirty = false; +} + +void mpc3000_all_v3_t::song_t::_check() { + if (number_of_steps() != 0) { + if (!(m_song_body.get() != nullptr)) { + throw std::runtime_error("/types/song/seq/1: conditional field is not set"); + } + if (m_song_body.get() == nullptr) { + throw std::runtime_error("/types/song/seq/1: nested object is not set"); + } + m_song_body.get()->_set_io(m__io); + m_song_body.get()->_check(); + } else { + if (m_song_body.get() != nullptr) { + throw std::runtime_error("/types/song/seq/1: conditional field should be absent"); + } + } + m__dirty = false; +} + +mpc3000_all_v3_t::song_t::~song_t() {} +const std::set mpc3000_all_v3_t::song_t::song_body_t::_values_end_status_t{ + mpc3000_all_v3_t::song_t::song_body_t::END_STATUS_STOP_AT_END, + mpc3000_all_v3_t::song_t::song_body_t::END_STATUS_LOOP_TO_A_STEP, +}; +bool mpc3000_all_v3_t::song_t::song_body_t::_is_defined_end_status_t(mpc3000_all_v3_t::song_t::song_body_t::end_status_t v) { + return mpc3000_all_v3_t::song_t::song_body_t::_values_end_status_t.find(v) != mpc3000_all_v3_t::song_t::song_body_t::_values_end_status_t.end(); +} + +mpc3000_all_v3_t::song_t::song_body_t::song_body_t(kaitai::kstream* p__io, mpc3000_all_v3_t::song_t* p__parent, mpc3000_all_v3_t* p__root) : kaitai::kstruct(p__io) { + m__parent = p__parent; + m__root = p__root; + m__dirty = false; +} + +void mpc3000_all_v3_t::song_t::song_body_t::_read() { + m_song_number = m__io->read_u1(); + m_end_status = static_cast(m__io->read_u1()); + m_loop_back_step_number = m__io->read_u1(); + m_song_name = kaitai::kstream::bytes_to_str(kaitai::kstream::bytes_terminate(m__io->read_bytes(16), static_cast(32), false), "ASCII"); + m_smpte_offset = std::unique_ptr(new mpc3000_seq_v3_t::smpte_offset_t(m__io)); + m_smpte_offset->_read(); + m_steps = std::unique_ptr>>(new std::vector>()); + const int l_steps = static_cast(_parent())->number_of_steps(); + for (int i = 0; i < l_steps; i++) { + std::unique_ptr _t_steps = std::unique_ptr(new step_t(m__io, this, m__root)); + try { + _t_steps->_read(); + } catch(...) { + m_steps->push_back(std::move(_t_steps)); + throw; + } + m_steps->push_back(std::move(_t_steps)); + } + m__dirty = false; +} + +void mpc3000_all_v3_t::song_t::song_body_t::_fetch_instances() { + m_smpte_offset.get()->_fetch_instances(); + for (std::size_t i = 0; i < m_steps->size(); ++i) { + m_steps->at(i).get()->_fetch_instances(); + } +} + +void mpc3000_all_v3_t::song_t::song_body_t::_write() { + m__io->write_u1(m_song_number); + m__io->write_u1(static_cast(m_end_status)); + m__io->write_u1(m_loop_back_step_number); + std::string _bufm_song_name = m_song_name; + if (_bufm_song_name.size() < static_cast(16)) { + _bufm_song_name += std::string(1, static_cast(32)); + } + if (_bufm_song_name.size() < static_cast(16)) { + _bufm_song_name.append(static_cast(16) - _bufm_song_name.size(), static_cast(0)); + } + m__io->write_bytes(_bufm_song_name); + if (m_smpte_offset.get() == nullptr) { + throw std::runtime_error("/types/song/types/song_body/seq/4: nested object is not set"); + } + m_smpte_offset.get()->_set_io(m__io); + m_smpte_offset.get()->_write(); + if (m_steps == nullptr) { + throw std::runtime_error("/types/song/types/song_body/seq/5: repeated field is not set"); + } + for (std::vector>::const_iterator it = m_steps->begin(); it != m_steps->end(); ++it) { + if ((*it).get() == nullptr) { + throw std::runtime_error("/types/song/types/song_body/seq/5: nested object is not set"); + } + (*it).get()->_set_io(m__io); + (*it).get()->_write(); + } + _fetch_instances(); + m__dirty = false; +} + +void mpc3000_all_v3_t::song_t::song_body_t::_check() { + if (m_song_name.size() > static_cast(16)) { + throw std::runtime_error("/types/song/types/song_body/seq/3: size mismatch"); + } + if (m_song_name.find(static_cast(32)) != std::string::npos) { + throw std::runtime_error("/types/song/types/song_body/seq/3: terminator must not appear in value"); + } + if (m_smpte_offset.get() == nullptr) { + throw std::runtime_error("/types/song/types/song_body/seq/4: nested object is not set"); + } + m_smpte_offset.get()->_set_io(m__io); + m_smpte_offset.get()->_check(); + if (m_steps == nullptr) { + throw std::runtime_error("/types/song/types/song_body/seq/5: repeated field is not set"); + } + if (m_steps->size() != static_cast(static_cast(_parent())->number_of_steps())) { + throw std::runtime_error("/types/song/types/song_body/seq/5: repeat-expr size mismatch"); + } + for (std::vector>::const_iterator it = m_steps->begin(); it != m_steps->end(); ++it) { + if ((*it).get() == nullptr) { + throw std::runtime_error("/types/song/types/song_body/seq/5: nested object is not set"); + } + (*it).get()->_set_io(m__io); + (*it).get()->_check(); + } + m__dirty = false; +} + +mpc3000_all_v3_t::song_t::song_body_t::~song_body_t() {} + +mpc3000_all_v3_t::song_t::song_body_t::step_t::step_t(kaitai::kstream* p__io, mpc3000_all_v3_t::song_t::song_body_t* p__parent, mpc3000_all_v3_t* p__root) : kaitai::kstruct(p__io) { + m__parent = p__parent; + m__root = p__root; + m__dirty = false; +} + +void mpc3000_all_v3_t::song_t::song_body_t::step_t::_read() { + m_sequence_number = m__io->read_u1(); + m_repetition_count = m__io->read_u1(); + m__dirty = false; +} + +void mpc3000_all_v3_t::song_t::song_body_t::step_t::_fetch_instances() { +} + +void mpc3000_all_v3_t::song_t::song_body_t::step_t::_write() { + m__io->write_u1(m_sequence_number); + m__io->write_u1(m_repetition_count); + _fetch_instances(); + m__dirty = false; +} + +void mpc3000_all_v3_t::song_t::song_body_t::step_t::_check() { + m__dirty = false; +} + +mpc3000_all_v3_t::song_t::song_body_t::step_t::~step_t() {} diff --git a/src/main/file/kaitai/generated/mpc3000_all_v3.h b/src/main/file/kaitai/generated/mpc3000_all_v3.h new file mode 100644 index 000000000..5aa158ed2 --- /dev/null +++ b/src/main/file/kaitai/generated/mpc3000_all_v3.h @@ -0,0 +1,316 @@ +#pragma once + +// This is a generated file! Please edit source .ksy file and use kaitai-struct-compiler to rebuild + +class mpc3000_all_v3_t; + +#include "kaitai/kaitaistruct.h" +#include +#include +#include "mpc3000_seq_v3.h" +#include +#include + +#if KAITAI_STRUCT_VERSION < 11000L +#error "Incompatible Kaitai Struct C++/STL API: version 0.11 or later is required" +#endif + +class mpc3000_all_v3_t : public kaitai::kstruct { + +public: + class all_file_header_t; + class misc_chunks_t; + class sequence_t; + class song_t; + +private: + bool m__dirty; + +public: + + mpc3000_all_v3_t(kaitai::kstream* p__io, kaitai::kstruct* p__parent = nullptr, mpc3000_all_v3_t* p__root = nullptr); + void _read(); + void _fetch_instances(); + void _write(); + void _check(); + ~mpc3000_all_v3_t(); + + class all_file_header_t : public kaitai::kstruct { + + public: + + private: + bool m__dirty; + + public: + + all_file_header_t(kaitai::kstream* p__io, mpc3000_all_v3_t* p__parent = nullptr, mpc3000_all_v3_t* p__root = nullptr); + void _read(); + void _fetch_instances(); + void _write(); + void _check(); + ~all_file_header_t(); + std::string file_id() const { return m_file_id; } + void set_file_id(std::string _v) { m__dirty = true; m_file_id = std::move(_v); } + std::string file_version() const { return m_file_version; } + void set_file_version(std::string _v) { m__dirty = true; m_file_version = std::move(_v); } + uint32_t total_number_of_bytes_in_all_sequences() const { return m_total_number_of_bytes_in_all_sequences; } + void set_total_number_of_bytes_in_all_sequences(uint32_t _v) { m__dirty = true; m_total_number_of_bytes_in_all_sequences = std::move(_v); } + mpc3000_all_v3_t* _root() const { return m__root; } + void set__root(mpc3000_all_v3_t* _v) { m__dirty = true; m__root = std::move(_v); } + kaitai::kstruct* _parent() const { return m__parent; } + void set__parent(mpc3000_all_v3_t* _v) { m__dirty = true; m__parent = std::move(_v); } + + private: + std::string m_file_id; + std::string m_file_version; + uint32_t m_total_number_of_bytes_in_all_sequences; + mpc3000_all_v3_t* m__root; + mpc3000_all_v3_t* m__parent; + }; + + class misc_chunks_t : public kaitai::kstruct { + + public: + + private: + bool m__dirty; + + public: + + misc_chunks_t(kaitai::kstream* p__io, mpc3000_all_v3_t::sequence_t* p__parent = nullptr, mpc3000_all_v3_t* p__root = nullptr); + void _read(); + void _fetch_instances(); + void _write(); + void _check(); + ~misc_chunks_t(); + int32_t position_after_tempo_changes(); + void _invalidate_position_after_tempo_changes() { f_position_after_tempo_changes = false; } + mpc3000_seq_v3_t::sequence_header_t* sequence_header() const { return m_sequence_header.get(); } + void set_sequence_header(std::unique_ptr _v) { m__dirty = true; f_position_after_tempo_changes = false; m_sequence_header = std::move(_v); } + mpc3000_seq_v3_t::smpte_offset_t* smpte_offset() const { return m_smpte_offset.get(); } + void set_smpte_offset(std::unique_ptr _v) { m__dirty = true; f_position_after_tempo_changes = false; m_smpte_offset = std::move(_v); } + std::vector>* mixer() const { return m_mixer.get(); } + void set_mixer(std::unique_ptr>> _v) { m__dirty = true; f_position_after_tempo_changes = false; m_mixer = std::move(_v); } + std::string reserved_after_mixer() const { return m_reserved_after_mixer; } + void set_reserved_after_mixer(std::string _v) { m__dirty = true; f_position_after_tempo_changes = false; m_reserved_after_mixer = std::move(_v); } + mpc3000_seq_v3_t::delays_t* delays() const { return m_delays.get(); } + void set_delays(std::unique_ptr _v) { m__dirty = true; f_position_after_tempo_changes = false; m_delays = std::move(_v); } + std::string reserved_after_delays() const { return m_reserved_after_delays; } + void set_reserved_after_delays(std::string _v) { m__dirty = true; f_position_after_tempo_changes = false; m_reserved_after_delays = std::move(_v); } + std::string reserved_before_track_summary() const { return m_reserved_before_track_summary; } + void set_reserved_before_track_summary(std::string _v) { m__dirty = true; f_position_after_tempo_changes = false; m_reserved_before_track_summary = std::move(_v); } + uint8_t last_active_track() const { return m_last_active_track; } + void set_last_active_track(uint8_t _v) { m__dirty = true; f_position_after_tempo_changes = false; m_last_active_track = std::move(_v); } + uint8_t num_tempo_changes() const { return m_num_tempo_changes; } + void set_num_tempo_changes(uint8_t _v) { m__dirty = true; f_position_after_tempo_changes = false; m_num_tempo_changes = std::move(_v); } + uint8_t num_track_headers() const { return m_num_track_headers; } + void set_num_track_headers(uint8_t _v) { m__dirty = true; f_position_after_tempo_changes = false; m_num_track_headers = std::move(_v); } + std::vector>* track_headers() const { return m_track_headers.get(); } + void set_track_headers(std::unique_ptr>> _v) { m__dirty = true; f_position_after_tempo_changes = false; m_track_headers = std::move(_v); } + std::vector>* tempo_changes() const { return m_tempo_changes.get(); } + void set_tempo_changes(std::unique_ptr>> _v) { m__dirty = true; f_position_after_tempo_changes = false; m_tempo_changes = std::move(_v); } + mpc3000_all_v3_t* _root() const { return m__root; } + void set__root(mpc3000_all_v3_t* _v) { m__dirty = true; f_position_after_tempo_changes = false; m__root = std::move(_v); } + kaitai::kstruct* _parent() const { return m__parent; } + void set__parent(mpc3000_all_v3_t::sequence_t* _v) { m__dirty = true; f_position_after_tempo_changes = false; m__parent = std::move(_v); } + + private: + bool f_position_after_tempo_changes; + int32_t m_position_after_tempo_changes; + std::unique_ptr m_sequence_header; + std::unique_ptr m_smpte_offset; + std::unique_ptr>> m_mixer; + std::string m_reserved_after_mixer; + std::unique_ptr m_delays; + std::string m_reserved_after_delays; + std::string m_reserved_before_track_summary; + uint8_t m_last_active_track; + uint8_t m_num_tempo_changes; + uint8_t m_num_track_headers; + std::unique_ptr>> m_track_headers; + std::unique_ptr>> m_tempo_changes; + mpc3000_all_v3_t* m__root; + mpc3000_all_v3_t::sequence_t* m__parent; + }; + + class sequence_t : public kaitai::kstruct { + + public: + + private: + bool m__dirty; + + public: + + sequence_t(kaitai::kstream* p__io, mpc3000_all_v3_t* p__parent = nullptr, mpc3000_all_v3_t* p__root = nullptr); + void _read(); + void _fetch_instances(); + void _write(); + void _check(); + ~sequence_t(); + misc_chunks_t* misc_chunks() const { return m_misc_chunks.get(); } + void set_misc_chunks(std::unique_ptr _v) { m__dirty = true; m_misc_chunks = std::move(_v); } + std::vector>* events() const { return m_events.get(); } + void set_events(std::unique_ptr>> _v) { m__dirty = true; m_events = std::move(_v); } + mpc3000_all_v3_t* _root() const { return m__root; } + void set__root(mpc3000_all_v3_t* _v) { m__dirty = true; m__root = std::move(_v); } + kaitai::kstruct* _parent() const { return m__parent; } + void set__parent(mpc3000_all_v3_t* _v) { m__dirty = true; m__parent = std::move(_v); } + + private: + std::unique_ptr m_misc_chunks; + std::unique_ptr>> m_events; + mpc3000_all_v3_t* m__root; + mpc3000_all_v3_t* m__parent; + }; + + class song_t : public kaitai::kstruct { + + public: + class song_body_t; + + private: + bool m__dirty; + + public: + + song_t(kaitai::kstream* p__io, mpc3000_all_v3_t* p__parent = nullptr, mpc3000_all_v3_t* p__root = nullptr); + void _read(); + void _fetch_instances(); + void _write(); + void _check(); + ~song_t(); + + class song_body_t : public kaitai::kstruct { + + public: + class step_t; + + enum end_status_t { + END_STATUS_STOP_AT_END = 0, + END_STATUS_LOOP_TO_A_STEP = 1 + }; + static bool _is_defined_end_status_t(end_status_t v); + + private: + static const std::set _values_end_status_t; + + public: + + private: + bool m__dirty; + + public: + + song_body_t(kaitai::kstream* p__io, mpc3000_all_v3_t::song_t* p__parent = nullptr, mpc3000_all_v3_t* p__root = nullptr); + void _read(); + void _fetch_instances(); + void _write(); + void _check(); + ~song_body_t(); + + class step_t : public kaitai::kstruct { + + public: + + private: + bool m__dirty; + + public: + + step_t(kaitai::kstream* p__io, mpc3000_all_v3_t::song_t::song_body_t* p__parent = nullptr, mpc3000_all_v3_t* p__root = nullptr); + void _read(); + void _fetch_instances(); + void _write(); + void _check(); + ~step_t(); + uint8_t sequence_number() const { return m_sequence_number; } + void set_sequence_number(uint8_t _v) { m__dirty = true; m_sequence_number = std::move(_v); } + uint8_t repetition_count() const { return m_repetition_count; } + void set_repetition_count(uint8_t _v) { m__dirty = true; m_repetition_count = std::move(_v); } + mpc3000_all_v3_t* _root() const { return m__root; } + void set__root(mpc3000_all_v3_t* _v) { m__dirty = true; m__root = std::move(_v); } + kaitai::kstruct* _parent() const { return m__parent; } + void set__parent(mpc3000_all_v3_t::song_t::song_body_t* _v) { m__dirty = true; m__parent = std::move(_v); } + + private: + uint8_t m_sequence_number; + uint8_t m_repetition_count; + mpc3000_all_v3_t* m__root; + mpc3000_all_v3_t::song_t::song_body_t* m__parent; + }; + + public: + uint8_t song_number() const { return m_song_number; } + void set_song_number(uint8_t _v) { m__dirty = true; m_song_number = std::move(_v); } + end_status_t end_status() const { return m_end_status; } + void set_end_status(end_status_t _v) { m__dirty = true; m_end_status = std::move(_v); } + uint8_t loop_back_step_number() const { return m_loop_back_step_number; } + void set_loop_back_step_number(uint8_t _v) { m__dirty = true; m_loop_back_step_number = std::move(_v); } + std::string song_name() const { return m_song_name; } + void set_song_name(std::string _v) { m__dirty = true; m_song_name = std::move(_v); } + mpc3000_seq_v3_t::smpte_offset_t* smpte_offset() const { return m_smpte_offset.get(); } + void set_smpte_offset(std::unique_ptr _v) { m__dirty = true; m_smpte_offset = std::move(_v); } + std::vector>* steps() const { return m_steps.get(); } + void set_steps(std::unique_ptr>> _v) { m__dirty = true; m_steps = std::move(_v); } + mpc3000_all_v3_t* _root() const { return m__root; } + void set__root(mpc3000_all_v3_t* _v) { m__dirty = true; m__root = std::move(_v); } + kaitai::kstruct* _parent() const { return m__parent; } + void set__parent(mpc3000_all_v3_t::song_t* _v) { m__dirty = true; m__parent = std::move(_v); } + + private: + uint8_t m_song_number; + end_status_t m_end_status; + uint8_t m_loop_back_step_number; + std::string m_song_name; + std::unique_ptr m_smpte_offset; + std::unique_ptr>> m_steps; + mpc3000_all_v3_t* m__root; + mpc3000_all_v3_t::song_t* m__parent; + }; + + public: + uint8_t number_of_steps() const { return m_number_of_steps; } + void set_number_of_steps(uint8_t _v) { m__dirty = true; m_number_of_steps = std::move(_v); } + song_body_t* song_body() const { return m_song_body.get(); } + void set_song_body(std::unique_ptr _v) { m__dirty = true; m_song_body = std::move(_v); } + mpc3000_all_v3_t* _root() const { return m__root; } + void set__root(mpc3000_all_v3_t* _v) { m__dirty = true; m__root = std::move(_v); } + kaitai::kstruct* _parent() const { return m__parent; } + void set__parent(mpc3000_all_v3_t* _v) { m__dirty = true; m__parent = std::move(_v); } + + private: + uint8_t m_number_of_steps; + std::unique_ptr m_song_body; + + public: + bool _is_null_song_body() { return !song_body(); }; + + private: + mpc3000_all_v3_t* m__root; + mpc3000_all_v3_t* m__parent; + }; + +public: + all_file_header_t* all_file_header() const { return m_all_file_header.get(); } + void set_all_file_header(std::unique_ptr _v) { m__dirty = true; m_all_file_header = std::move(_v); } + std::vector>* sequences() const { return m_sequences.get(); } + void set_sequences(std::unique_ptr>> _v) { m__dirty = true; m_sequences = std::move(_v); } + std::string sequences_terminator() const { return m_sequences_terminator; } + void set_sequences_terminator(std::string _v) { m__dirty = true; m_sequences_terminator = std::move(_v); } + std::vector>* songs() const { return m_songs.get(); } + void set_songs(std::unique_ptr>> _v) { m__dirty = true; m_songs = std::move(_v); } + mpc3000_all_v3_t* _root() const { return m__root; } + void set__root(mpc3000_all_v3_t* _v) { m__dirty = true; m__root = std::move(_v); } + kaitai::kstruct* _parent() const { return m__parent; } + void set__parent(kaitai::kstruct* _v) { m__dirty = true; m__parent = std::move(_v); } + +private: + std::unique_ptr m_all_file_header; + std::unique_ptr>> m_sequences; + std::string m_sequences_terminator; + std::unique_ptr>> m_songs; + mpc3000_all_v3_t* m__root; + kaitai::kstruct* m__parent; +}; diff --git a/src/main/file/kaitai/generated/mpc3000_pgm_v3.cpp b/src/main/file/kaitai/generated/mpc3000_pgm_v3.cpp new file mode 100644 index 000000000..117affb2f --- /dev/null +++ b/src/main/file/kaitai/generated/mpc3000_pgm_v3.cpp @@ -0,0 +1,603 @@ +// This is a generated file! Please edit source .ksy file and use kaitai-struct-compiler to rebuild + +#include "mpc3000_pgm_v3.h" +#include "kaitai/exceptions.h" +#include +#include +const std::set mpc3000_pgm_v3_t::_values_decay_mode_t{ + mpc3000_pgm_v3_t::DECAY_MODE_END, + mpc3000_pgm_v3_t::DECAY_MODE_START, +}; +bool mpc3000_pgm_v3_t::_is_defined_decay_mode_t(mpc3000_pgm_v3_t::decay_mode_t v) { + return mpc3000_pgm_v3_t::_values_decay_mode_t.find(v) != mpc3000_pgm_v3_t::_values_decay_mode_t.end(); +} +const std::set mpc3000_pgm_v3_t::_values_individual_out_t{ + mpc3000_pgm_v3_t::INDIVIDUAL_OUT_UNASSIGNED, + mpc3000_pgm_v3_t::INDIVIDUAL_OUT_OUT_1, + mpc3000_pgm_v3_t::INDIVIDUAL_OUT_OUT_2, + mpc3000_pgm_v3_t::INDIVIDUAL_OUT_OUT_3, + mpc3000_pgm_v3_t::INDIVIDUAL_OUT_OUT_4, + mpc3000_pgm_v3_t::INDIVIDUAL_OUT_OUT_5, + mpc3000_pgm_v3_t::INDIVIDUAL_OUT_OUT_6, + mpc3000_pgm_v3_t::INDIVIDUAL_OUT_OUT_7, + mpc3000_pgm_v3_t::INDIVIDUAL_OUT_OUT_8, + mpc3000_pgm_v3_t::INDIVIDUAL_OUT_INTERNAL_EFFECTS_GENERATOR, +}; +bool mpc3000_pgm_v3_t::_is_defined_individual_out_t(mpc3000_pgm_v3_t::individual_out_t v) { + return mpc3000_pgm_v3_t::_values_individual_out_t.find(v) != mpc3000_pgm_v3_t::_values_individual_out_t.end(); +} +const std::set mpc3000_pgm_v3_t::_values_note_variation_type_t{ + mpc3000_pgm_v3_t::NOTE_VARIATION_TYPE_TUNE, + mpc3000_pgm_v3_t::NOTE_VARIATION_TYPE_DECAY, + mpc3000_pgm_v3_t::NOTE_VARIATION_TYPE_ATTACK, + mpc3000_pgm_v3_t::NOTE_VARIATION_TYPE_FILTER, +}; +bool mpc3000_pgm_v3_t::_is_defined_note_variation_type_t(mpc3000_pgm_v3_t::note_variation_type_t v) { + return mpc3000_pgm_v3_t::_values_note_variation_type_t.find(v) != mpc3000_pgm_v3_t::_values_note_variation_type_t.end(); +} +const std::set mpc3000_pgm_v3_t::_values_poly_mode_t{ + mpc3000_pgm_v3_t::POLY_MODE_POLY, + mpc3000_pgm_v3_t::POLY_MODE_MONO, + mpc3000_pgm_v3_t::POLY_MODE_NOTE_OFF, +}; +bool mpc3000_pgm_v3_t::_is_defined_poly_mode_t(mpc3000_pgm_v3_t::poly_mode_t v) { + return mpc3000_pgm_v3_t::_values_poly_mode_t.find(v) != mpc3000_pgm_v3_t::_values_poly_mode_t.end(); +} +const std::set mpc3000_pgm_v3_t::_values_sound_generator_mode_t{ + mpc3000_pgm_v3_t::SOUND_GENERATOR_MODE_NORMAL, + mpc3000_pgm_v3_t::SOUND_GENERATOR_MODE_SIMULTANEOUS, + mpc3000_pgm_v3_t::SOUND_GENERATOR_MODE_VELOCITY_SWITCH, + mpc3000_pgm_v3_t::SOUND_GENERATOR_MODE_DECAY_SWITCH, +}; +bool mpc3000_pgm_v3_t::_is_defined_sound_generator_mode_t(mpc3000_pgm_v3_t::sound_generator_mode_t v) { + return mpc3000_pgm_v3_t::_values_sound_generator_mode_t.find(v) != mpc3000_pgm_v3_t::_values_sound_generator_mode_t.end(); +} + +mpc3000_pgm_v3_t::mpc3000_pgm_v3_t(kaitai::kstream* p__io, kaitai::kstruct* p__parent, mpc3000_pgm_v3_t* p__root) : kaitai::kstruct(p__io) { + m__parent = p__parent; + m__root = p__root ? p__root : this; + m__dirty = false; +} + +void mpc3000_pgm_v3_t::_read() { + m_file_id = m__io->read_bytes(1); + if (!(m_file_id == std::string("\x07", 1))) { + throw kaitai::validation_not_equal_error(std::string("\x07", 1), m_file_id, m__io, std::string("/seq/0")); + } + m_file_version = m__io->read_bytes(1); + if (!(m_file_version == std::string("\x00", 1))) { + throw kaitai::validation_not_equal_error(std::string("\x00", 1), m_file_version, m__io, std::string("/seq/1")); + } + m_sound_names = std::unique_ptr>>(new std::vector>()); + const int l_sound_names = 128; + for (int i = 0; i < l_sound_names; i++) { + std::unique_ptr _t_sound_names = std::unique_ptr(new sound_name_t(m__io, this, m__root)); + try { + _t_sound_names->_read(); + } catch(...) { + m_sound_names->push_back(std::move(_t_sound_names)); + throw; + } + m_sound_names->push_back(std::move(_t_sound_names)); + } + m_sound_sizes = std::unique_ptr>(new std::vector()); + const int l_sound_sizes = 128; + for (int i = 0; i < l_sound_sizes; i++) { + m_sound_sizes->push_back(std::move(m__io->read_u4le())); + } + m_program_name = kaitai::kstream::bytes_to_str(kaitai::kstream::bytes_terminate(m__io->read_bytes(17), static_cast(255), false), "ASCII"); + m_note_variation_screen = std::unique_ptr(new note_variation_t(m__io, this, m__root)); + m_note_variation_screen->_read(); + m_effects_screen = std::unique_ptr(new effects_screen_t(m__io, this, m__root)); + m_effects_screen->_read(); + m_sound_assignments = std::unique_ptr>>(new std::vector>()); + const int l_sound_assignments = 64; + for (int i = 0; i < l_sound_assignments; i++) { + std::unique_ptr _t_sound_assignments = std::unique_ptr(new sound_assignment_t(m__io, this, m__root)); + try { + _t_sound_assignments->_read(); + } catch(...) { + m_sound_assignments->push_back(std::move(_t_sound_assignments)); + throw; + } + m_sound_assignments->push_back(std::move(_t_sound_assignments)); + } + m_mixer_screens = std::unique_ptr>>(new std::vector>()); + const int l_mixer_screens = 64; + for (int i = 0; i < l_mixer_screens; i++) { + std::unique_ptr _t_mixer_screens = std::unique_ptr(new mixer_screen_t(m__io, this, m__root)); + try { + _t_mixer_screens->_read(); + } catch(...) { + m_mixer_screens->push_back(std::move(_t_mixer_screens)); + throw; + } + m_mixer_screens->push_back(std::move(_t_mixer_screens)); + } + m_pad_note_number_assignments = std::unique_ptr>>(new std::vector>()); + const int l_pad_note_number_assignments = 64; + for (int i = 0; i < l_pad_note_number_assignments; i++) { + std::unique_ptr _t_pad_note_number_assignments = std::unique_ptr(new pad_note_number_assignment_t(m__io, this, m__root)); + try { + _t_pad_note_number_assignments->_read(); + } catch(...) { + m_pad_note_number_assignments->push_back(std::move(_t_pad_note_number_assignments)); + throw; + } + m_pad_note_number_assignments->push_back(std::move(_t_pad_note_number_assignments)); + } + m__dirty = false; +} + +void mpc3000_pgm_v3_t::_fetch_instances() { + for (std::size_t i = 0; i < m_sound_names->size(); ++i) { + m_sound_names->at(i).get()->_fetch_instances(); + } + for (std::size_t i = 0; i < m_sound_sizes->size(); ++i) { + } + m_note_variation_screen.get()->_fetch_instances(); + m_effects_screen.get()->_fetch_instances(); + for (std::size_t i = 0; i < m_sound_assignments->size(); ++i) { + m_sound_assignments->at(i).get()->_fetch_instances(); + } + for (std::size_t i = 0; i < m_mixer_screens->size(); ++i) { + m_mixer_screens->at(i).get()->_fetch_instances(); + } + for (std::size_t i = 0; i < m_pad_note_number_assignments->size(); ++i) { + m_pad_note_number_assignments->at(i).get()->_fetch_instances(); + } +} + +void mpc3000_pgm_v3_t::_write() { + m__io->write_bytes(m_file_id); + m__io->write_bytes(m_file_version); + if (m_sound_names == nullptr) { + throw std::runtime_error("/seq/2: repeated field is not set"); + } + for (std::vector>::const_iterator it = m_sound_names->begin(); it != m_sound_names->end(); ++it) { + if ((*it).get() == nullptr) { + throw std::runtime_error("/seq/2: nested object is not set"); + } + (*it).get()->_set_io(m__io); + (*it).get()->_write(); + } + if (m_sound_sizes == nullptr) { + throw std::runtime_error("/seq/3: repeated field is not set"); + } + for (std::vector::const_iterator it = m_sound_sizes->begin(); it != m_sound_sizes->end(); ++it) { + m__io->write_u4le((*it)); + } + std::string _bufm_program_name = m_program_name; + if (_bufm_program_name.size() < static_cast(17)) { + _bufm_program_name += std::string(1, static_cast(255)); + } + if (_bufm_program_name.size() < static_cast(17)) { + _bufm_program_name.append(static_cast(17) - _bufm_program_name.size(), static_cast(0)); + } + m__io->write_bytes(_bufm_program_name); + if (m_note_variation_screen.get() == nullptr) { + throw std::runtime_error("/seq/5: nested object is not set"); + } + m_note_variation_screen.get()->_set_io(m__io); + m_note_variation_screen.get()->_write(); + if (m_effects_screen.get() == nullptr) { + throw std::runtime_error("/seq/6: nested object is not set"); + } + m_effects_screen.get()->_set_io(m__io); + m_effects_screen.get()->_write(); + if (m_sound_assignments == nullptr) { + throw std::runtime_error("/seq/7: repeated field is not set"); + } + for (std::vector>::const_iterator it = m_sound_assignments->begin(); it != m_sound_assignments->end(); ++it) { + if ((*it).get() == nullptr) { + throw std::runtime_error("/seq/7: nested object is not set"); + } + (*it).get()->_set_io(m__io); + (*it).get()->_write(); + } + if (m_mixer_screens == nullptr) { + throw std::runtime_error("/seq/8: repeated field is not set"); + } + for (std::vector>::const_iterator it = m_mixer_screens->begin(); it != m_mixer_screens->end(); ++it) { + if ((*it).get() == nullptr) { + throw std::runtime_error("/seq/8: nested object is not set"); + } + (*it).get()->_set_io(m__io); + (*it).get()->_write(); + } + if (m_pad_note_number_assignments == nullptr) { + throw std::runtime_error("/seq/9: repeated field is not set"); + } + for (std::vector>::const_iterator it = m_pad_note_number_assignments->begin(); it != m_pad_note_number_assignments->end(); ++it) { + if ((*it).get() == nullptr) { + throw std::runtime_error("/seq/9: nested object is not set"); + } + (*it).get()->_set_io(m__io); + (*it).get()->_write(); + } + _fetch_instances(); + m__dirty = false; +} + +void mpc3000_pgm_v3_t::_check() { + if (m_file_id.size() != static_cast(1)) { + throw std::runtime_error("/seq/0: size mismatch"); + } + if (!(m_file_id == std::string("\x07", 1))) { + throw kaitai::validation_not_equal_error(std::string("\x07", 1), m_file_id, m__io, std::string("/seq/0")); + } + if (m_file_version.size() != static_cast(1)) { + throw std::runtime_error("/seq/1: size mismatch"); + } + if (!(m_file_version == std::string("\x00", 1))) { + throw kaitai::validation_not_equal_error(std::string("\x00", 1), m_file_version, m__io, std::string("/seq/1")); + } + if (m_sound_names == nullptr) { + throw std::runtime_error("/seq/2: repeated field is not set"); + } + if (m_sound_names->size() != static_cast(128)) { + throw std::runtime_error("/seq/2: repeat-expr size mismatch"); + } + for (std::vector>::const_iterator it = m_sound_names->begin(); it != m_sound_names->end(); ++it) { + if ((*it).get() == nullptr) { + throw std::runtime_error("/seq/2: nested object is not set"); + } + (*it).get()->_set_io(m__io); + (*it).get()->_check(); + } + if (m_sound_sizes == nullptr) { + throw std::runtime_error("/seq/3: repeated field is not set"); + } + if (m_sound_sizes->size() != static_cast(128)) { + throw std::runtime_error("/seq/3: repeat-expr size mismatch"); + } + for (std::vector::const_iterator it = m_sound_sizes->begin(); it != m_sound_sizes->end(); ++it) { + } + if (m_program_name.size() > static_cast(17)) { + throw std::runtime_error("/seq/4: size mismatch"); + } + if (m_program_name.find(static_cast(255)) != std::string::npos) { + throw std::runtime_error("/seq/4: terminator must not appear in value"); + } + if (m_note_variation_screen.get() == nullptr) { + throw std::runtime_error("/seq/5: nested object is not set"); + } + m_note_variation_screen.get()->_set_io(m__io); + m_note_variation_screen.get()->_check(); + if (m_effects_screen.get() == nullptr) { + throw std::runtime_error("/seq/6: nested object is not set"); + } + m_effects_screen.get()->_set_io(m__io); + m_effects_screen.get()->_check(); + if (m_sound_assignments == nullptr) { + throw std::runtime_error("/seq/7: repeated field is not set"); + } + if (m_sound_assignments->size() != static_cast(64)) { + throw std::runtime_error("/seq/7: repeat-expr size mismatch"); + } + for (std::vector>::const_iterator it = m_sound_assignments->begin(); it != m_sound_assignments->end(); ++it) { + if ((*it).get() == nullptr) { + throw std::runtime_error("/seq/7: nested object is not set"); + } + (*it).get()->_set_io(m__io); + (*it).get()->_check(); + } + if (m_mixer_screens == nullptr) { + throw std::runtime_error("/seq/8: repeated field is not set"); + } + if (m_mixer_screens->size() != static_cast(64)) { + throw std::runtime_error("/seq/8: repeat-expr size mismatch"); + } + for (std::vector>::const_iterator it = m_mixer_screens->begin(); it != m_mixer_screens->end(); ++it) { + if ((*it).get() == nullptr) { + throw std::runtime_error("/seq/8: nested object is not set"); + } + (*it).get()->_set_io(m__io); + (*it).get()->_check(); + } + if (m_pad_note_number_assignments == nullptr) { + throw std::runtime_error("/seq/9: repeated field is not set"); + } + if (m_pad_note_number_assignments->size() != static_cast(64)) { + throw std::runtime_error("/seq/9: repeat-expr size mismatch"); + } + for (std::vector>::const_iterator it = m_pad_note_number_assignments->begin(); it != m_pad_note_number_assignments->end(); ++it) { + if ((*it).get() == nullptr) { + throw std::runtime_error("/seq/9: nested object is not set"); + } + (*it).get()->_set_io(m__io); + (*it).get()->_check(); + } + m__dirty = false; +} + +mpc3000_pgm_v3_t::~mpc3000_pgm_v3_t() {} + +mpc3000_pgm_v3_t::effects_screen_t::effects_screen_t(kaitai::kstream* p__io, mpc3000_pgm_v3_t* p__parent, mpc3000_pgm_v3_t* p__root) : kaitai::kstruct(p__io) { + m__parent = p__parent; + m__root = p__root; + m__dirty = false; +} + +void mpc3000_pgm_v3_t::effects_screen_t::_read() { + m_effects_on = m__io->read_bits_int_le(1); + m__unnamed1 = m__io->read_bits_int_le(7); + m__unnamed2 = m__io->read_u1(); + m_delay_volume_tap1 = m__io->read_u1(); + m_delay_volume_tap2 = m__io->read_u1(); + m_delay_volume_tap3 = m__io->read_u1(); + m_delay_pan_tap1 = m__io->read_u1(); + m_delay_pan_tap2 = m__io->read_u1(); + m_delay_pan_tap3 = m__io->read_u1(); + m_delay_msecs_tap1 = m__io->read_u2le(); + m_delay_msecs_tap2 = m__io->read_u2le(); + m_delay_msecs_tap3 = m__io->read_u2le(); + m__unnamed12 = m__io->read_bytes(6); + m_delay_feedback_tap1 = m__io->read_u1(); + m_delay_feedback_tap2 = m__io->read_u1(); + m_delay_feedback_tap3 = m__io->read_u1(); + m__unnamed16 = m__io->read_bytes(13); + m__dirty = false; +} + +void mpc3000_pgm_v3_t::effects_screen_t::_fetch_instances() { +} + +void mpc3000_pgm_v3_t::effects_screen_t::_write() { + m__io->write_bits_int_le(1, ((m_effects_on) ? 1 : 0)); + m__io->write_bits_int_le(7, m__unnamed1); + m__io->write_u1(m__unnamed2); + m__io->write_u1(m_delay_volume_tap1); + m__io->write_u1(m_delay_volume_tap2); + m__io->write_u1(m_delay_volume_tap3); + m__io->write_u1(m_delay_pan_tap1); + m__io->write_u1(m_delay_pan_tap2); + m__io->write_u1(m_delay_pan_tap3); + m__io->write_u2le(m_delay_msecs_tap1); + m__io->write_u2le(m_delay_msecs_tap2); + m__io->write_u2le(m_delay_msecs_tap3); + m__io->write_bytes(m__unnamed12); + m__io->write_u1(m_delay_feedback_tap1); + m__io->write_u1(m_delay_feedback_tap2); + m__io->write_u1(m_delay_feedback_tap3); + m__io->write_bytes(m__unnamed16); + _fetch_instances(); + m__dirty = false; +} + +void mpc3000_pgm_v3_t::effects_screen_t::_check() { + if (m__unnamed12.size() != static_cast(6)) { + throw std::runtime_error("/types/effects_screen/seq/12: size mismatch"); + } + if (m__unnamed16.size() != static_cast(13)) { + throw std::runtime_error("/types/effects_screen/seq/16: size mismatch"); + } + m__dirty = false; +} + +mpc3000_pgm_v3_t::effects_screen_t::~effects_screen_t() {} + +mpc3000_pgm_v3_t::mixer_screen_t::mixer_screen_t(kaitai::kstream* p__io, mpc3000_pgm_v3_t* p__parent, mpc3000_pgm_v3_t* p__root) : kaitai::kstruct(p__io) { + m__parent = p__parent; + m__root = p__root; + m__dirty = false; +} + +void mpc3000_pgm_v3_t::mixer_screen_t::_read() { + m_stereo_mix_volume = m__io->read_u1(); + m_stereo_mix_pan = m__io->read_u1(); + m_echo_volume = m__io->read_u1(); + m_out_assign = static_cast(m__io->read_bits_int_le(4)); + m__unnamed4 = m__io->read_bits_int_le(3); + m_follow_stereo = m__io->read_bits_int_le(1); + m__dirty = false; +} + +void mpc3000_pgm_v3_t::mixer_screen_t::_fetch_instances() { +} + +void mpc3000_pgm_v3_t::mixer_screen_t::_write() { + m__io->write_u1(m_stereo_mix_volume); + m__io->write_u1(m_stereo_mix_pan); + m__io->write_u1(m_echo_volume); + m__io->write_bits_int_le(4, static_cast(m_out_assign)); + m__io->write_bits_int_le(3, m__unnamed4); + m__io->write_bits_int_le(1, ((m_follow_stereo) ? 1 : 0)); + _fetch_instances(); + m__dirty = false; +} + +void mpc3000_pgm_v3_t::mixer_screen_t::_check() { + m__dirty = false; +} + +mpc3000_pgm_v3_t::mixer_screen_t::~mixer_screen_t() {} + +mpc3000_pgm_v3_t::note_variation_t::note_variation_t(kaitai::kstream* p__io, mpc3000_pgm_v3_t* p__parent, mpc3000_pgm_v3_t* p__root) : kaitai::kstruct(p__io) { + m__parent = p__parent; + m__root = p__root; + m__dirty = false; +} + +void mpc3000_pgm_v3_t::note_variation_t::_read() { + m_note_number_assignment = m__io->read_u1(); + m_tuning_low_range = m__io->read_u1(); + m_tuning_hi_range = m__io->read_u1(); + m_attack_low_range = m__io->read_u1(); + m_attack_hi_range = m__io->read_u1(); + m_decay_low_range = m__io->read_u1(); + m_decay_hi_range = m__io->read_u1(); + m_filter_low_range = m__io->read_u1(); + m_filter_hi_range = m__io->read_u1(); + m__dirty = false; +} + +void mpc3000_pgm_v3_t::note_variation_t::_fetch_instances() { +} + +void mpc3000_pgm_v3_t::note_variation_t::_write() { + m__io->write_u1(m_note_number_assignment); + m__io->write_u1(m_tuning_low_range); + m__io->write_u1(m_tuning_hi_range); + m__io->write_u1(m_attack_low_range); + m__io->write_u1(m_attack_hi_range); + m__io->write_u1(m_decay_low_range); + m__io->write_u1(m_decay_hi_range); + m__io->write_u1(m_filter_low_range); + m__io->write_u1(m_filter_hi_range); + _fetch_instances(); + m__dirty = false; +} + +void mpc3000_pgm_v3_t::note_variation_t::_check() { + m__dirty = false; +} + +mpc3000_pgm_v3_t::note_variation_t::~note_variation_t() {} + +mpc3000_pgm_v3_t::pad_note_number_assignment_t::pad_note_number_assignment_t(kaitai::kstream* p__io, mpc3000_pgm_v3_t* p__parent, mpc3000_pgm_v3_t* p__root) : kaitai::kstruct(p__io) { + m__parent = p__parent; + m__root = p__root; + m__dirty = false; +} + +void mpc3000_pgm_v3_t::pad_note_number_assignment_t::_read() { + m_note_number = m__io->read_u1(); + if (!(m_note_number >= 35)) { + throw kaitai::validation_less_than_error(35, m_note_number, m__io, std::string("/types/pad_note_number_assignment/seq/0")); + } + if (!(m_note_number <= 98)) { + throw kaitai::validation_greater_than_error(98, m_note_number, m__io, std::string("/types/pad_note_number_assignment/seq/0")); + } + m__dirty = false; +} + +void mpc3000_pgm_v3_t::pad_note_number_assignment_t::_fetch_instances() { +} + +void mpc3000_pgm_v3_t::pad_note_number_assignment_t::_write() { + m__io->write_u1(m_note_number); + _fetch_instances(); + m__dirty = false; +} + +void mpc3000_pgm_v3_t::pad_note_number_assignment_t::_check() { + if (!(m_note_number >= 35)) { + throw kaitai::validation_less_than_error(35, m_note_number, m__io, std::string("/types/pad_note_number_assignment/seq/0")); + } + if (!(m_note_number <= 98)) { + throw kaitai::validation_greater_than_error(98, m_note_number, m__io, std::string("/types/pad_note_number_assignment/seq/0")); + } + m__dirty = false; +} + +mpc3000_pgm_v3_t::pad_note_number_assignment_t::~pad_note_number_assignment_t() {} + +mpc3000_pgm_v3_t::sound_assignment_t::sound_assignment_t(kaitai::kstream* p__io, mpc3000_pgm_v3_t* p__parent, mpc3000_pgm_v3_t* p__root) : kaitai::kstruct(p__io) { + m__parent = p__parent; + m__root = p__root; + m__dirty = false; +} + +void mpc3000_pgm_v3_t::sound_assignment_t::_read() { + m_sound_number = m__io->read_u1(); + m_sound_generator_mode = static_cast(m__io->read_u1()); + m_if_over1 = m__io->read_u1(); + m_use_also_plays1 = m__io->read_u1(); + m_if_over2 = m__io->read_u1(); + m_use_also_plays2 = m__io->read_u1(); + m_poly = static_cast(m__io->read_u1()); + m_cutoff_note_1 = m__io->read_u1(); + m_cutoff_note_2 = m__io->read_u1(); + m_tune = m__io->read_s2le(); + m_attack = m__io->read_u1(); + m_decay = m__io->read_u1(); + m_decay_mode = static_cast(m__io->read_u1()); + m_filter_frequency = m__io->read_u1(); + m_filter_resonance = m__io->read_u1(); + m_filter_envel_attack = m__io->read_u1(); + m_filter_envel_decay = m__io->read_u1(); + m_filter_envel_amount = m__io->read_u1(); + m_veloc_mod_of_volume = m__io->read_u1(); + m_veloc_mod_of_attack = m__io->read_u1(); + m_veloc_mod_of_soft_start = m__io->read_u1(); + m_veloc_mod_of_filter_freq = m__io->read_u1(); + m_param = static_cast(m__io->read_u1()); + m__dirty = false; +} + +void mpc3000_pgm_v3_t::sound_assignment_t::_fetch_instances() { +} + +void mpc3000_pgm_v3_t::sound_assignment_t::_write() { + m__io->write_u1(m_sound_number); + m__io->write_u1(static_cast(m_sound_generator_mode)); + m__io->write_u1(m_if_over1); + m__io->write_u1(m_use_also_plays1); + m__io->write_u1(m_if_over2); + m__io->write_u1(m_use_also_plays2); + m__io->write_u1(static_cast(m_poly)); + m__io->write_u1(m_cutoff_note_1); + m__io->write_u1(m_cutoff_note_2); + m__io->write_s2le(m_tune); + m__io->write_u1(m_attack); + m__io->write_u1(m_decay); + m__io->write_u1(static_cast(m_decay_mode)); + m__io->write_u1(m_filter_frequency); + m__io->write_u1(m_filter_resonance); + m__io->write_u1(m_filter_envel_attack); + m__io->write_u1(m_filter_envel_decay); + m__io->write_u1(m_filter_envel_amount); + m__io->write_u1(m_veloc_mod_of_volume); + m__io->write_u1(m_veloc_mod_of_attack); + m__io->write_u1(m_veloc_mod_of_soft_start); + m__io->write_u1(m_veloc_mod_of_filter_freq); + m__io->write_u1(static_cast(m_param)); + _fetch_instances(); + m__dirty = false; +} + +void mpc3000_pgm_v3_t::sound_assignment_t::_check() { + m__dirty = false; +} + +mpc3000_pgm_v3_t::sound_assignment_t::~sound_assignment_t() {} + +mpc3000_pgm_v3_t::sound_name_t::sound_name_t(kaitai::kstream* p__io, mpc3000_pgm_v3_t* p__parent, mpc3000_pgm_v3_t* p__root) : kaitai::kstruct(p__io) { + m__parent = p__parent; + m__root = p__root; + m__dirty = false; +} + +void mpc3000_pgm_v3_t::sound_name_t::_read() { + m_name = kaitai::kstream::bytes_to_str(kaitai::kstream::bytes_terminate(m__io->read_bytes(17), static_cast(0), false), "ASCII"); + m__dirty = false; +} + +void mpc3000_pgm_v3_t::sound_name_t::_fetch_instances() { +} + +void mpc3000_pgm_v3_t::sound_name_t::_write() { + std::string _bufm_name = m_name; + if (_bufm_name.size() < static_cast(17)) { + _bufm_name += std::string(1, static_cast(0)); + } + if (_bufm_name.size() < static_cast(17)) { + _bufm_name.append(static_cast(17) - _bufm_name.size(), static_cast(0)); + } + m__io->write_bytes(_bufm_name); + _fetch_instances(); + m__dirty = false; +} + +void mpc3000_pgm_v3_t::sound_name_t::_check() { + if (m_name.size() > static_cast(17)) { + throw std::runtime_error("/types/sound_name/seq/0: size mismatch"); + } + if (m_name.find(static_cast(0)) != std::string::npos) { + throw std::runtime_error("/types/sound_name/seq/0: terminator must not appear in value"); + } + m__dirty = false; +} + +mpc3000_pgm_v3_t::sound_name_t::~sound_name_t() {} diff --git a/src/main/file/kaitai/generated/mpc3000_pgm_v3.h b/src/main/file/kaitai/generated/mpc3000_pgm_v3.h new file mode 100644 index 000000000..1e958c64d --- /dev/null +++ b/src/main/file/kaitai/generated/mpc3000_pgm_v3.h @@ -0,0 +1,541 @@ +#pragma once + +// This is a generated file! Please edit source .ksy file and use kaitai-struct-compiler to rebuild + +class mpc3000_pgm_v3_t; + +#include "kaitai/kaitaistruct.h" +#include +#include +#include +#include + +#if KAITAI_STRUCT_VERSION < 11000L +#error "Incompatible Kaitai Struct C++/STL API: version 0.11 or later is required" +#endif + +class mpc3000_pgm_v3_t : public kaitai::kstruct { + +public: + class effects_screen_t; + class mixer_screen_t; + class note_variation_t; + class pad_note_number_assignment_t; + class sound_assignment_t; + class sound_name_t; + + enum decay_mode_t { + DECAY_MODE_END = 0, + DECAY_MODE_START = 1 + }; + static bool _is_defined_decay_mode_t(decay_mode_t v); + +private: + static const std::set _values_decay_mode_t; + +public: + + enum individual_out_t { + INDIVIDUAL_OUT_UNASSIGNED = 0, + INDIVIDUAL_OUT_OUT_1 = 1, + INDIVIDUAL_OUT_OUT_2 = 2, + INDIVIDUAL_OUT_OUT_3 = 3, + INDIVIDUAL_OUT_OUT_4 = 4, + INDIVIDUAL_OUT_OUT_5 = 5, + INDIVIDUAL_OUT_OUT_6 = 6, + INDIVIDUAL_OUT_OUT_7 = 7, + INDIVIDUAL_OUT_OUT_8 = 8, + INDIVIDUAL_OUT_INTERNAL_EFFECTS_GENERATOR = 9 + }; + static bool _is_defined_individual_out_t(individual_out_t v); + +private: + static const std::set _values_individual_out_t; + +public: + + enum note_variation_type_t { + NOTE_VARIATION_TYPE_TUNE = 0, + NOTE_VARIATION_TYPE_DECAY = 1, + NOTE_VARIATION_TYPE_ATTACK = 2, + NOTE_VARIATION_TYPE_FILTER = 3 + }; + static bool _is_defined_note_variation_type_t(note_variation_type_t v); + +private: + static const std::set _values_note_variation_type_t; + +public: + + enum poly_mode_t { + POLY_MODE_POLY = 0, + POLY_MODE_MONO = 1, + POLY_MODE_NOTE_OFF = 2 + }; + static bool _is_defined_poly_mode_t(poly_mode_t v); + +private: + static const std::set _values_poly_mode_t; + +public: + + enum sound_generator_mode_t { + SOUND_GENERATOR_MODE_NORMAL = 0, + SOUND_GENERATOR_MODE_SIMULTANEOUS = 1, + SOUND_GENERATOR_MODE_VELOCITY_SWITCH = 2, + SOUND_GENERATOR_MODE_DECAY_SWITCH = 3 + }; + static bool _is_defined_sound_generator_mode_t(sound_generator_mode_t v); + +private: + static const std::set _values_sound_generator_mode_t; + +public: + +private: + bool m__dirty; + +public: + + mpc3000_pgm_v3_t(kaitai::kstream* p__io, kaitai::kstruct* p__parent = nullptr, mpc3000_pgm_v3_t* p__root = nullptr); + void _read(); + void _fetch_instances(); + void _write(); + void _check(); + ~mpc3000_pgm_v3_t(); + + class effects_screen_t : public kaitai::kstruct { + + public: + + private: + bool m__dirty; + + public: + + effects_screen_t(kaitai::kstream* p__io, mpc3000_pgm_v3_t* p__parent = nullptr, mpc3000_pgm_v3_t* p__root = nullptr); + void _read(); + void _fetch_instances(); + void _write(); + void _check(); + ~effects_screen_t(); + bool effects_on() const { return m_effects_on; } + void set_effects_on(bool _v) { m__dirty = true; m_effects_on = std::move(_v); } + uint64_t _unnamed1() const { return m__unnamed1; } + void set__unnamed1(uint64_t _v) { m__dirty = true; m__unnamed1 = std::move(_v); } + + /** + * Unused + */ + uint8_t _unnamed2() const { return m__unnamed2; } + void set__unnamed2(uint8_t _v) { m__dirty = true; m__unnamed2 = std::move(_v); } + uint8_t delay_volume_tap1() const { return m_delay_volume_tap1; } + void set_delay_volume_tap1(uint8_t _v) { m__dirty = true; m_delay_volume_tap1 = std::move(_v); } + uint8_t delay_volume_tap2() const { return m_delay_volume_tap2; } + void set_delay_volume_tap2(uint8_t _v) { m__dirty = true; m_delay_volume_tap2 = std::move(_v); } + uint8_t delay_volume_tap3() const { return m_delay_volume_tap3; } + void set_delay_volume_tap3(uint8_t _v) { m__dirty = true; m_delay_volume_tap3 = std::move(_v); } + uint8_t delay_pan_tap1() const { return m_delay_pan_tap1; } + void set_delay_pan_tap1(uint8_t _v) { m__dirty = true; m_delay_pan_tap1 = std::move(_v); } + uint8_t delay_pan_tap2() const { return m_delay_pan_tap2; } + void set_delay_pan_tap2(uint8_t _v) { m__dirty = true; m_delay_pan_tap2 = std::move(_v); } + uint8_t delay_pan_tap3() const { return m_delay_pan_tap3; } + void set_delay_pan_tap3(uint8_t _v) { m__dirty = true; m_delay_pan_tap3 = std::move(_v); } + uint16_t delay_msecs_tap1() const { return m_delay_msecs_tap1; } + void set_delay_msecs_tap1(uint16_t _v) { m__dirty = true; m_delay_msecs_tap1 = std::move(_v); } + uint16_t delay_msecs_tap2() const { return m_delay_msecs_tap2; } + void set_delay_msecs_tap2(uint16_t _v) { m__dirty = true; m_delay_msecs_tap2 = std::move(_v); } + uint16_t delay_msecs_tap3() const { return m_delay_msecs_tap3; } + void set_delay_msecs_tap3(uint16_t _v) { m__dirty = true; m_delay_msecs_tap3 = std::move(_v); } + + /** + * Unused + */ + std::string _unnamed12() const { return m__unnamed12; } + void set__unnamed12(std::string _v) { m__dirty = true; m__unnamed12 = std::move(_v); } + uint8_t delay_feedback_tap1() const { return m_delay_feedback_tap1; } + void set_delay_feedback_tap1(uint8_t _v) { m__dirty = true; m_delay_feedback_tap1 = std::move(_v); } + uint8_t delay_feedback_tap2() const { return m_delay_feedback_tap2; } + void set_delay_feedback_tap2(uint8_t _v) { m__dirty = true; m_delay_feedback_tap2 = std::move(_v); } + uint8_t delay_feedback_tap3() const { return m_delay_feedback_tap3; } + void set_delay_feedback_tap3(uint8_t _v) { m__dirty = true; m_delay_feedback_tap3 = std::move(_v); } + + /** + * Unused + */ + std::string _unnamed16() const { return m__unnamed16; } + void set__unnamed16(std::string _v) { m__dirty = true; m__unnamed16 = std::move(_v); } + mpc3000_pgm_v3_t* _root() const { return m__root; } + void set__root(mpc3000_pgm_v3_t* _v) { m__dirty = true; m__root = std::move(_v); } + kaitai::kstruct* _parent() const { return m__parent; } + void set__parent(mpc3000_pgm_v3_t* _v) { m__dirty = true; m__parent = std::move(_v); } + + private: + bool m_effects_on; + uint64_t m__unnamed1; + uint8_t m__unnamed2; + uint8_t m_delay_volume_tap1; + uint8_t m_delay_volume_tap2; + uint8_t m_delay_volume_tap3; + uint8_t m_delay_pan_tap1; + uint8_t m_delay_pan_tap2; + uint8_t m_delay_pan_tap3; + uint16_t m_delay_msecs_tap1; + uint16_t m_delay_msecs_tap2; + uint16_t m_delay_msecs_tap3; + std::string m__unnamed12; + uint8_t m_delay_feedback_tap1; + uint8_t m_delay_feedback_tap2; + uint8_t m_delay_feedback_tap3; + std::string m__unnamed16; + mpc3000_pgm_v3_t* m__root; + mpc3000_pgm_v3_t* m__parent; + }; + + class mixer_screen_t : public kaitai::kstruct { + + public: + + private: + bool m__dirty; + + public: + + mixer_screen_t(kaitai::kstream* p__io, mpc3000_pgm_v3_t* p__parent = nullptr, mpc3000_pgm_v3_t* p__root = nullptr); + void _read(); + void _fetch_instances(); + void _write(); + void _check(); + ~mixer_screen_t(); + uint8_t stereo_mix_volume() const { return m_stereo_mix_volume; } + void set_stereo_mix_volume(uint8_t _v) { m__dirty = true; m_stereo_mix_volume = std::move(_v); } + uint8_t stereo_mix_pan() const { return m_stereo_mix_pan; } + void set_stereo_mix_pan(uint8_t _v) { m__dirty = true; m_stereo_mix_pan = std::move(_v); } + uint8_t echo_volume() const { return m_echo_volume; } + void set_echo_volume(uint8_t _v) { m__dirty = true; m_echo_volume = std::move(_v); } + individual_out_t out_assign() const { return m_out_assign; } + void set_out_assign(individual_out_t _v) { m__dirty = true; m_out_assign = std::move(_v); } + + /** + * Unused + */ + uint64_t _unnamed4() const { return m__unnamed4; } + void set__unnamed4(uint64_t _v) { m__dirty = true; m__unnamed4 = std::move(_v); } + bool follow_stereo() const { return m_follow_stereo; } + void set_follow_stereo(bool _v) { m__dirty = true; m_follow_stereo = std::move(_v); } + mpc3000_pgm_v3_t* _root() const { return m__root; } + void set__root(mpc3000_pgm_v3_t* _v) { m__dirty = true; m__root = std::move(_v); } + kaitai::kstruct* _parent() const { return m__parent; } + void set__parent(mpc3000_pgm_v3_t* _v) { m__dirty = true; m__parent = std::move(_v); } + + private: + uint8_t m_stereo_mix_volume; + uint8_t m_stereo_mix_pan; + uint8_t m_echo_volume; + individual_out_t m_out_assign; + uint64_t m__unnamed4; + bool m_follow_stereo; + mpc3000_pgm_v3_t* m__root; + mpc3000_pgm_v3_t* m__parent; + }; + + class note_variation_t : public kaitai::kstruct { + + public: + + private: + bool m__dirty; + + public: + + note_variation_t(kaitai::kstream* p__io, mpc3000_pgm_v3_t* p__parent = nullptr, mpc3000_pgm_v3_t* p__root = nullptr); + void _read(); + void _fetch_instances(); + void _write(); + void _check(); + ~note_variation_t(); + uint8_t note_number_assignment() const { return m_note_number_assignment; } + void set_note_number_assignment(uint8_t _v) { m__dirty = true; m_note_number_assignment = std::move(_v); } + uint8_t tuning_low_range() const { return m_tuning_low_range; } + void set_tuning_low_range(uint8_t _v) { m__dirty = true; m_tuning_low_range = std::move(_v); } + uint8_t tuning_hi_range() const { return m_tuning_hi_range; } + void set_tuning_hi_range(uint8_t _v) { m__dirty = true; m_tuning_hi_range = std::move(_v); } + uint8_t attack_low_range() const { return m_attack_low_range; } + void set_attack_low_range(uint8_t _v) { m__dirty = true; m_attack_low_range = std::move(_v); } + uint8_t attack_hi_range() const { return m_attack_hi_range; } + void set_attack_hi_range(uint8_t _v) { m__dirty = true; m_attack_hi_range = std::move(_v); } + uint8_t decay_low_range() const { return m_decay_low_range; } + void set_decay_low_range(uint8_t _v) { m__dirty = true; m_decay_low_range = std::move(_v); } + uint8_t decay_hi_range() const { return m_decay_hi_range; } + void set_decay_hi_range(uint8_t _v) { m__dirty = true; m_decay_hi_range = std::move(_v); } + uint8_t filter_low_range() const { return m_filter_low_range; } + void set_filter_low_range(uint8_t _v) { m__dirty = true; m_filter_low_range = std::move(_v); } + uint8_t filter_hi_range() const { return m_filter_hi_range; } + void set_filter_hi_range(uint8_t _v) { m__dirty = true; m_filter_hi_range = std::move(_v); } + mpc3000_pgm_v3_t* _root() const { return m__root; } + void set__root(mpc3000_pgm_v3_t* _v) { m__dirty = true; m__root = std::move(_v); } + kaitai::kstruct* _parent() const { return m__parent; } + void set__parent(mpc3000_pgm_v3_t* _v) { m__dirty = true; m__parent = std::move(_v); } + + private: + uint8_t m_note_number_assignment; + uint8_t m_tuning_low_range; + uint8_t m_tuning_hi_range; + uint8_t m_attack_low_range; + uint8_t m_attack_hi_range; + uint8_t m_decay_low_range; + uint8_t m_decay_hi_range; + uint8_t m_filter_low_range; + uint8_t m_filter_hi_range; + mpc3000_pgm_v3_t* m__root; + mpc3000_pgm_v3_t* m__parent; + }; + + class pad_note_number_assignment_t : public kaitai::kstruct { + + public: + + private: + bool m__dirty; + + public: + + pad_note_number_assignment_t(kaitai::kstream* p__io, mpc3000_pgm_v3_t* p__parent = nullptr, mpc3000_pgm_v3_t* p__root = nullptr); + void _read(); + void _fetch_instances(); + void _write(); + void _check(); + ~pad_note_number_assignment_t(); + uint8_t note_number() const { return m_note_number; } + void set_note_number(uint8_t _v) { m__dirty = true; m_note_number = std::move(_v); } + mpc3000_pgm_v3_t* _root() const { return m__root; } + void set__root(mpc3000_pgm_v3_t* _v) { m__dirty = true; m__root = std::move(_v); } + kaitai::kstruct* _parent() const { return m__parent; } + void set__parent(mpc3000_pgm_v3_t* _v) { m__dirty = true; m__parent = std::move(_v); } + + private: + uint8_t m_note_number; + mpc3000_pgm_v3_t* m__root; + mpc3000_pgm_v3_t* m__parent; + }; + + class sound_assignment_t : public kaitai::kstruct { + + public: + + private: + bool m__dirty; + + public: + + sound_assignment_t(kaitai::kstream* p__io, mpc3000_pgm_v3_t* p__parent = nullptr, mpc3000_pgm_v3_t* p__root = nullptr); + void _read(); + void _fetch_instances(); + void _write(); + void _check(); + ~sound_assignment_t(); + uint8_t sound_number() const { return m_sound_number; } + void set_sound_number(uint8_t _v) { m__dirty = true; m_sound_number = std::move(_v); } + + /** + * Confirmed by MAME MPC3000 v3.10 contrast saves and Roger Linn's MPC file-format notes. Byte value 0 is NORMAL, 1 is SIMULT, 2 is VEL SW, and the doc describes 3 as DCY SW. + */ + sound_generator_mode_t sound_generator_mode() const { return m_sound_generator_mode; } + void set_sound_generator_mode(sound_generator_mode_t _v) { m__dirty = true; m_sound_generator_mode = std::move(_v); } + + /** + * Active on switch-based generator modes. The exact UI-to-byte mapping was probed on MAME MPC3000 v3.10. In a saved VEL SW contrast, changing the first visible threshold from 44 to 43 changed this byte from 0x2c to 0x2b. + */ + uint8_t if_over1() const { return m_if_over1; } + void set_if_over1(uint8_t _v) { m__dirty = true; m_if_over1 = std::move(_v); } + + /** + * Active on switch-based generator modes. In a saved VEL SW contrast, changing the first visible target from OFF to 35/C14 changed this byte from 0x22 to 0x23. + */ + uint8_t use_also_plays1() const { return m_use_also_plays1; } + void set_use_also_plays1(uint8_t _v) { m__dirty = true; m_use_also_plays1 = std::move(_v); } + + /** + * Active on switch-based generator modes. Confirmed by MAME MPC3000 v3.10 contrast saves after correcting the note-record table start to file offset 2752. + */ + uint8_t if_over2() const { return m_if_over2; } + void set_if_over2(uint8_t _v) { m__dirty = true; m_if_over2 = std::move(_v); } + + /** + * Active on switch-based generator modes and simultaneous mode. Confirmed by MAME MPC3000 v3.10 contrast saves after correcting the note-record table start to file offset 2752. + */ + uint8_t use_also_plays2() const { return m_use_also_plays2; } + void set_use_also_plays2(uint8_t _v) { m__dirty = true; m_use_also_plays2 = std::move(_v); } + + /** + * Confirmed by MAME MPC3000 v3.10 contrast saves and Roger Linn's MPC file-format notes. 0 = POLY, 1 = MONO, 2 = NOTE OFF. + */ + poly_mode_t poly() const { return m_poly; } + void set_poly(poly_mode_t _v) { m__dirty = true; m_poly = std::move(_v); } + + /** + * First cutoff-assignment note number from the Env,Veloc.. screen. This is not a filter cutoff value. Roger Linn's file-format notes describe it as 'Cutoff 1 (notes 35-98 or 0)' and live saves matched visible values like 64/B12. + */ + uint8_t cutoff_note_1() const { return m_cutoff_note_1; } + void set_cutoff_note_1(uint8_t _v) { m__dirty = true; m_cutoff_note_1 = std::move(_v); } + + /** + * Second cutoff-assignment note number from the Env,Veloc.. screen. This is not a filter cutoff value. Roger Linn's file-format notes describe it as 'Cutoff 2 (notes 35-98 or 0)' and live saves matched visible values like 65/B05. + */ + uint8_t cutoff_note_2() const { return m_cutoff_note_2; } + void set_cutoff_note_2(uint8_t _v) { m__dirty = true; m_cutoff_note_2 = std::move(_v); } + + /** + * Signed tune value. Confirmed by a MAME MPC3000 v3.10 contrast save where visible `Tune:-1` wrote `0xffff`. + */ + int16_t tune() const { return m_tune; } + void set_tune(int16_t _v) { m__dirty = true; m_tune = std::move(_v); } + uint8_t attack() const { return m_attack; } + void set_attack(uint8_t _v) { m__dirty = true; m_attack = std::move(_v); } + + /** + * Real empty hardware PROGRAM.pgm carries value 6 in every record, while MAME MPC3000 v3.10 Initialize Program writes 0 in every record. + */ + uint8_t decay() const { return m_decay; } + void set_decay(uint8_t _v) { m__dirty = true; m_decay = std::move(_v); } + + /** + * Confirmed by a dedicated MAME MPC3000 v3.10 contrast save: + * visible `Dcy md:START` wrote byte value 1, so the firmware's file + * encoding is 0 = END and 1 = START. + */ + decay_mode_t decay_mode() const { return m_decay_mode; } + void set_decay_mode(decay_mode_t _v) { m__dirty = true; m_decay_mode = std::move(_v); } + uint8_t filter_frequency() const { return m_filter_frequency; } + void set_filter_frequency(uint8_t _v) { m__dirty = true; m_filter_frequency = std::move(_v); } + uint8_t filter_resonance() const { return m_filter_resonance; } + void set_filter_resonance(uint8_t _v) { m__dirty = true; m_filter_resonance = std::move(_v); } + uint8_t filter_envel_attack() const { return m_filter_envel_attack; } + void set_filter_envel_attack(uint8_t _v) { m__dirty = true; m_filter_envel_attack = std::move(_v); } + uint8_t filter_envel_decay() const { return m_filter_envel_decay; } + void set_filter_envel_decay(uint8_t _v) { m__dirty = true; m_filter_envel_decay = std::move(_v); } + uint8_t filter_envel_amount() const { return m_filter_envel_amount; } + void set_filter_envel_amount(uint8_t _v) { m__dirty = true; m_filter_envel_amount = std::move(_v); } + uint8_t veloc_mod_of_volume() const { return m_veloc_mod_of_volume; } + void set_veloc_mod_of_volume(uint8_t _v) { m__dirty = true; m_veloc_mod_of_volume = std::move(_v); } + uint8_t veloc_mod_of_attack() const { return m_veloc_mod_of_attack; } + void set_veloc_mod_of_attack(uint8_t _v) { m__dirty = true; m_veloc_mod_of_attack = std::move(_v); } + uint8_t veloc_mod_of_soft_start() const { return m_veloc_mod_of_soft_start; } + void set_veloc_mod_of_soft_start(uint8_t _v) { m__dirty = true; m_veloc_mod_of_soft_start = std::move(_v); } + uint8_t veloc_mod_of_filter_freq() const { return m_veloc_mod_of_filter_freq; } + void set_veloc_mod_of_filter_freq(uint8_t _v) { m__dirty = true; m_veloc_mod_of_filter_freq = std::move(_v); } + + /** + * Per-note note-variation parameter selector. Roger Linn's file-format notes describe 0 = TUNING, 1 = DECAY, 2 = ATTACK, 3 = FILTER. + */ + note_variation_type_t param() const { return m_param; } + void set_param(note_variation_type_t _v) { m__dirty = true; m_param = std::move(_v); } + mpc3000_pgm_v3_t* _root() const { return m__root; } + void set__root(mpc3000_pgm_v3_t* _v) { m__dirty = true; m__root = std::move(_v); } + kaitai::kstruct* _parent() const { return m__parent; } + void set__parent(mpc3000_pgm_v3_t* _v) { m__dirty = true; m__parent = std::move(_v); } + + private: + uint8_t m_sound_number; + sound_generator_mode_t m_sound_generator_mode; + uint8_t m_if_over1; + uint8_t m_use_also_plays1; + uint8_t m_if_over2; + uint8_t m_use_also_plays2; + poly_mode_t m_poly; + uint8_t m_cutoff_note_1; + uint8_t m_cutoff_note_2; + int16_t m_tune; + uint8_t m_attack; + uint8_t m_decay; + decay_mode_t m_decay_mode; + uint8_t m_filter_frequency; + uint8_t m_filter_resonance; + uint8_t m_filter_envel_attack; + uint8_t m_filter_envel_decay; + uint8_t m_filter_envel_amount; + uint8_t m_veloc_mod_of_volume; + uint8_t m_veloc_mod_of_attack; + uint8_t m_veloc_mod_of_soft_start; + uint8_t m_veloc_mod_of_filter_freq; + note_variation_type_t m_param; + mpc3000_pgm_v3_t* m__root; + mpc3000_pgm_v3_t* m__parent; + }; + + class sound_name_t : public kaitai::kstruct { + + public: + + private: + bool m__dirty; + + public: + + sound_name_t(kaitai::kstream* p__io, mpc3000_pgm_v3_t* p__parent = nullptr, mpc3000_pgm_v3_t* p__root = nullptr); + void _read(); + void _fetch_instances(); + void _write(); + void _check(); + ~sound_name_t(); + std::string name() const { return m_name; } + void set_name(std::string _v) { m__dirty = true; m_name = std::move(_v); } + mpc3000_pgm_v3_t* _root() const { return m__root; } + void set__root(mpc3000_pgm_v3_t* _v) { m__dirty = true; m__root = std::move(_v); } + kaitai::kstruct* _parent() const { return m__parent; } + void set__parent(mpc3000_pgm_v3_t* _v) { m__dirty = true; m__parent = std::move(_v); } + + private: + std::string m_name; + mpc3000_pgm_v3_t* m__root; + mpc3000_pgm_v3_t* m__parent; + }; + +public: + std::string file_id() const { return m_file_id; } + void set_file_id(std::string _v) { m__dirty = true; m_file_id = std::move(_v); } + + /** + * According to a document provided by Roger Linn, this byte should be set to 0, despite it being a property in the description for PGM file version 3. + */ + std::string file_version() const { return m_file_version; } + void set_file_version(std::string _v) { m__dirty = true; m_file_version = std::move(_v); } + std::vector>* sound_names() const { return m_sound_names.get(); } + void set_sound_names(std::unique_ptr>> _v) { m__dirty = true; m_sound_names = std::move(_v); } + std::vector* sound_sizes() const { return m_sound_sizes.get(); } + void set_sound_sizes(std::unique_ptr> _v) { m__dirty = true; m_sound_sizes = std::move(_v); } + std::string program_name() const { return m_program_name; } + void set_program_name(std::string _v) { m__dirty = true; m_program_name = std::move(_v); } + note_variation_t* note_variation_screen() const { return m_note_variation_screen.get(); } + void set_note_variation_screen(std::unique_ptr _v) { m__dirty = true; m_note_variation_screen = std::move(_v); } + effects_screen_t* effects_screen() const { return m_effects_screen.get(); } + void set_effects_screen(std::unique_ptr _v) { m__dirty = true; m_effects_screen = std::move(_v); } + std::vector>* sound_assignments() const { return m_sound_assignments.get(); } + void set_sound_assignments(std::unique_ptr>> _v) { m__dirty = true; m_sound_assignments = std::move(_v); } + std::vector>* mixer_screens() const { return m_mixer_screens.get(); } + void set_mixer_screens(std::unique_ptr>> _v) { m__dirty = true; m_mixer_screens = std::move(_v); } + + /** + * Note number assignments for pads A01..D16 + */ + std::vector>* pad_note_number_assignments() const { return m_pad_note_number_assignments.get(); } + void set_pad_note_number_assignments(std::unique_ptr>> _v) { m__dirty = true; m_pad_note_number_assignments = std::move(_v); } + mpc3000_pgm_v3_t* _root() const { return m__root; } + void set__root(mpc3000_pgm_v3_t* _v) { m__dirty = true; m__root = std::move(_v); } + kaitai::kstruct* _parent() const { return m__parent; } + void set__parent(kaitai::kstruct* _v) { m__dirty = true; m__parent = std::move(_v); } + +private: + std::string m_file_id; + std::string m_file_version; + std::unique_ptr>> m_sound_names; + std::unique_ptr> m_sound_sizes; + std::string m_program_name; + std::unique_ptr m_note_variation_screen; + std::unique_ptr m_effects_screen; + std::unique_ptr>> m_sound_assignments; + std::unique_ptr>> m_mixer_screens; + std::unique_ptr>> m_pad_note_number_assignments; + mpc3000_pgm_v3_t* m__root; + kaitai::kstruct* m__parent; +}; diff --git a/src/main/file/kaitai/generated/mpc3000_seq_v3.cpp b/src/main/file/kaitai/generated/mpc3000_seq_v3.cpp new file mode 100644 index 000000000..87083cd00 --- /dev/null +++ b/src/main/file/kaitai/generated/mpc3000_seq_v3.cpp @@ -0,0 +1,2388 @@ +// This is a generated file! Please edit source .ksy file and use kaitai-struct-compiler to rebuild + +#include "mpc3000_seq_v3.h" +#include "kaitai/exceptions.h" +#include +#include +const std::set mpc3000_seq_v3_t::_values_controller_t{ + mpc3000_seq_v3_t::CONTROLLER_BANK_SEL_MSB, + mpc3000_seq_v3_t::CONTROLLER_MOD_WHEEL, + mpc3000_seq_v3_t::CONTROLLER_BREATH_CONT, + mpc3000_seq_v3_t::CONTROLLER_CC3, + mpc3000_seq_v3_t::CONTROLLER_FOOT_CONTROL, + mpc3000_seq_v3_t::CONTROLLER_PORTA_TIME, + mpc3000_seq_v3_t::CONTROLLER_DATA_ENTRY, + mpc3000_seq_v3_t::CONTROLLER_MAIN_VOLUME, + mpc3000_seq_v3_t::CONTROLLER_BALANCE, + mpc3000_seq_v3_t::CONTROLLER_CC9, + mpc3000_seq_v3_t::CONTROLLER_PAN, + mpc3000_seq_v3_t::CONTROLLER_EXPRESSION, + mpc3000_seq_v3_t::CONTROLLER_EFFECT_1, + mpc3000_seq_v3_t::CONTROLLER_EFFECT_2, + mpc3000_seq_v3_t::CONTROLLER_CC14, + mpc3000_seq_v3_t::CONTROLLER_CC15, + mpc3000_seq_v3_t::CONTROLLER_GEN_PUR_1, + mpc3000_seq_v3_t::CONTROLLER_GEN_PUR_2, + mpc3000_seq_v3_t::CONTROLLER_GEN_PUR_3, + mpc3000_seq_v3_t::CONTROLLER_GEN_PUR_4, + mpc3000_seq_v3_t::CONTROLLER_CC20, + mpc3000_seq_v3_t::CONTROLLER_CC21, + mpc3000_seq_v3_t::CONTROLLER_CC22, + mpc3000_seq_v3_t::CONTROLLER_CC23, + mpc3000_seq_v3_t::CONTROLLER_CC24, + mpc3000_seq_v3_t::CONTROLLER_CC25, + mpc3000_seq_v3_t::CONTROLLER_CC26, + mpc3000_seq_v3_t::CONTROLLER_CC27, + mpc3000_seq_v3_t::CONTROLLER_CC28, + mpc3000_seq_v3_t::CONTROLLER_CC29, + mpc3000_seq_v3_t::CONTROLLER_CC30, + mpc3000_seq_v3_t::CONTROLLER_CC31, + mpc3000_seq_v3_t::CONTROLLER_BANK_SEL_LSB, + mpc3000_seq_v3_t::CONTROLLER_MOD_WHEL_LSB, + mpc3000_seq_v3_t::CONTROLLER_BREATH_LSB, + mpc3000_seq_v3_t::CONTROLLER_CC35, + mpc3000_seq_v3_t::CONTROLLER_FOOT_CNT_LSB, + mpc3000_seq_v3_t::CONTROLLER_PORT_TIME_LS, + mpc3000_seq_v3_t::CONTROLLER_DATA_ENT_LSB, + mpc3000_seq_v3_t::CONTROLLER_MAIN_VOL_LSB, + mpc3000_seq_v3_t::CONTROLLER_BALANCE_LSB, + mpc3000_seq_v3_t::CONTROLLER_CC41, + mpc3000_seq_v3_t::CONTROLLER_PAN_LSB, + mpc3000_seq_v3_t::CONTROLLER_EXPRESS_LSB, + mpc3000_seq_v3_t::CONTROLLER_EFFECT_1_LSB, + mpc3000_seq_v3_t::CONTROLLER_EFFECT_2_MSB, + mpc3000_seq_v3_t::CONTROLLER_CC46, + mpc3000_seq_v3_t::CONTROLLER_CC47, + mpc3000_seq_v3_t::CONTROLLER_GEN_PUR_1_LS, + mpc3000_seq_v3_t::CONTROLLER_GEN_PUR_2_LS, + mpc3000_seq_v3_t::CONTROLLER_GEN_PUR_3_LS, + mpc3000_seq_v3_t::CONTROLLER_GEN_PUR_4_LS, + mpc3000_seq_v3_t::CONTROLLER_CC52, + mpc3000_seq_v3_t::CONTROLLER_CC53, + mpc3000_seq_v3_t::CONTROLLER_CC54, + mpc3000_seq_v3_t::CONTROLLER_CC55, + mpc3000_seq_v3_t::CONTROLLER_CC56, + mpc3000_seq_v3_t::CONTROLLER_CC57, + mpc3000_seq_v3_t::CONTROLLER_CC58, + mpc3000_seq_v3_t::CONTROLLER_CC59, + mpc3000_seq_v3_t::CONTROLLER_CC60, + mpc3000_seq_v3_t::CONTROLLER_CC61, + mpc3000_seq_v3_t::CONTROLLER_CC62, + mpc3000_seq_v3_t::CONTROLLER_CC63, + mpc3000_seq_v3_t::CONTROLLER_SUSTAIN_PDL, + mpc3000_seq_v3_t::CONTROLLER_PORTA_PEDAL, + mpc3000_seq_v3_t::CONTROLLER_SOSTENUTO, + mpc3000_seq_v3_t::CONTROLLER_SOFT_PEDAL, + mpc3000_seq_v3_t::CONTROLLER_LEGATO_FT_SW, + mpc3000_seq_v3_t::CONTROLLER_HOLD_2, + mpc3000_seq_v3_t::CONTROLLER_SOUND_VARI, + mpc3000_seq_v3_t::CONTROLLER_TIMBER_HARMO, + mpc3000_seq_v3_t::CONTROLLER_RELEASE_TIME, + mpc3000_seq_v3_t::CONTROLLER_ATTACK_TIME, + mpc3000_seq_v3_t::CONTROLLER_BRIGHTNESS, + mpc3000_seq_v3_t::CONTROLLER_SOUND_CONT_6, + mpc3000_seq_v3_t::CONTROLLER_SOUND_CONT_7, + mpc3000_seq_v3_t::CONTROLLER_SOUND_CONT_8, + mpc3000_seq_v3_t::CONTROLLER_SOUND_CONT_9, + mpc3000_seq_v3_t::CONTROLLER_SOUND_CONT10, + mpc3000_seq_v3_t::CONTROLLER_GEN_PUR_5, + mpc3000_seq_v3_t::CONTROLLER_GEN_PUR_6, + mpc3000_seq_v3_t::CONTROLLER_GEN_PUR_7, + mpc3000_seq_v3_t::CONTROLLER_GEN_PUR_8, + mpc3000_seq_v3_t::CONTROLLER_PORTA_CNTRL, + mpc3000_seq_v3_t::CONTROLLER_CC85, + mpc3000_seq_v3_t::CONTROLLER_CC86, + mpc3000_seq_v3_t::CONTROLLER_CC87, + mpc3000_seq_v3_t::CONTROLLER_CC88, + mpc3000_seq_v3_t::CONTROLLER_CC89, + mpc3000_seq_v3_t::CONTROLLER_CC90, + mpc3000_seq_v3_t::CONTROLLER_EXT_EFF_DPTH, + mpc3000_seq_v3_t::CONTROLLER_TREMOLO_DPTH, + mpc3000_seq_v3_t::CONTROLLER_CHORUS_DEPTH, + mpc3000_seq_v3_t::CONTROLLER_DETUNE_DEPTH, + mpc3000_seq_v3_t::CONTROLLER_PHASER_DEPTH, + mpc3000_seq_v3_t::CONTROLLER_DATA_INCRE, + mpc3000_seq_v3_t::CONTROLLER_DATA_DECRE, + mpc3000_seq_v3_t::CONTROLLER_NRPN_LSB, + mpc3000_seq_v3_t::CONTROLLER_NRPN_MSB, + mpc3000_seq_v3_t::CONTROLLER_RPN_LSB, + mpc3000_seq_v3_t::CONTROLLER_RPN_MSB, + mpc3000_seq_v3_t::CONTROLLER_CC102, + mpc3000_seq_v3_t::CONTROLLER_CC103, + mpc3000_seq_v3_t::CONTROLLER_CC104, + mpc3000_seq_v3_t::CONTROLLER_CC105, + mpc3000_seq_v3_t::CONTROLLER_CC106, + mpc3000_seq_v3_t::CONTROLLER_CC107, + mpc3000_seq_v3_t::CONTROLLER_CC108, + mpc3000_seq_v3_t::CONTROLLER_CC109, + mpc3000_seq_v3_t::CONTROLLER_CC110, + mpc3000_seq_v3_t::CONTROLLER_CC111, + mpc3000_seq_v3_t::CONTROLLER_CC112, + mpc3000_seq_v3_t::CONTROLLER_CC113, + mpc3000_seq_v3_t::CONTROLLER_CC114, + mpc3000_seq_v3_t::CONTROLLER_CC115, + mpc3000_seq_v3_t::CONTROLLER_CC116, + mpc3000_seq_v3_t::CONTROLLER_CC117, + mpc3000_seq_v3_t::CONTROLLER_CC118, + mpc3000_seq_v3_t::CONTROLLER_CC119, + mpc3000_seq_v3_t::CONTROLLER_ALL_SND_OFF, + mpc3000_seq_v3_t::CONTROLLER_RESET_CONTRL, + mpc3000_seq_v3_t::CONTROLLER_LOCAL_ON_OFF, + mpc3000_seq_v3_t::CONTROLLER_ALL_NOTE_OFF, + mpc3000_seq_v3_t::CONTROLLER_OMNI_OFF, + mpc3000_seq_v3_t::CONTROLLER_OMNI_ON, + mpc3000_seq_v3_t::CONTROLLER_MONO_MODE_ON, + mpc3000_seq_v3_t::CONTROLLER_POLY_MODE_ON, +}; +bool mpc3000_seq_v3_t::_is_defined_controller_t(mpc3000_seq_v3_t::controller_t v) { + return mpc3000_seq_v3_t::_values_controller_t.find(v) != mpc3000_seq_v3_t::_values_controller_t.end(); +} +const std::set mpc3000_seq_v3_t::_values_individual_out_t{ + mpc3000_seq_v3_t::INDIVIDUAL_OUT_UNASSIGNED, + mpc3000_seq_v3_t::INDIVIDUAL_OUT_OUT_1, + mpc3000_seq_v3_t::INDIVIDUAL_OUT_OUT_2, + mpc3000_seq_v3_t::INDIVIDUAL_OUT_OUT_3, + mpc3000_seq_v3_t::INDIVIDUAL_OUT_OUT_4, + mpc3000_seq_v3_t::INDIVIDUAL_OUT_OUT_5, + mpc3000_seq_v3_t::INDIVIDUAL_OUT_OUT_6, + mpc3000_seq_v3_t::INDIVIDUAL_OUT_OUT_7, + mpc3000_seq_v3_t::INDIVIDUAL_OUT_OUT_8, + mpc3000_seq_v3_t::INDIVIDUAL_OUT_INTERNAL_EFFECTS_GENERATOR, +}; +bool mpc3000_seq_v3_t::_is_defined_individual_out_t(mpc3000_seq_v3_t::individual_out_t v) { + return mpc3000_seq_v3_t::_values_individual_out_t.find(v) != mpc3000_seq_v3_t::_values_individual_out_t.end(); +} +const std::set mpc3000_seq_v3_t::_values_mixer_event_param_t{ + mpc3000_seq_v3_t::MIXER_EVENT_PARAM_STEREO_LEVEL, + mpc3000_seq_v3_t::MIXER_EVENT_PARAM_STEREO_PAN, + mpc3000_seq_v3_t::MIXER_EVENT_PARAM_FXSEND_LEVEL, + mpc3000_seq_v3_t::MIXER_EVENT_PARAM_INDIV_LEVEL, +}; +bool mpc3000_seq_v3_t::_is_defined_mixer_event_param_t(mpc3000_seq_v3_t::mixer_event_param_t v) { + return mpc3000_seq_v3_t::_values_mixer_event_param_t.find(v) != mpc3000_seq_v3_t::_values_mixer_event_param_t.end(); +} +const std::set mpc3000_seq_v3_t::_values_no_yes_t{ + mpc3000_seq_v3_t::NO_YES_FALSE, + mpc3000_seq_v3_t::NO_YES_TRUE, +}; +bool mpc3000_seq_v3_t::_is_defined_no_yes_t(mpc3000_seq_v3_t::no_yes_t v) { + return mpc3000_seq_v3_t::_values_no_yes_t.find(v) != mpc3000_seq_v3_t::_values_no_yes_t.end(); +} +const std::set mpc3000_seq_v3_t::_values_off_on_t{ + mpc3000_seq_v3_t::OFF_ON_FALSE, + mpc3000_seq_v3_t::OFF_ON_TRUE, +}; +bool mpc3000_seq_v3_t::_is_defined_off_on_t(mpc3000_seq_v3_t::off_on_t v) { + return mpc3000_seq_v3_t::_values_off_on_t.find(v) != mpc3000_seq_v3_t::_values_off_on_t.end(); +} + +mpc3000_seq_v3_t::mpc3000_seq_v3_t(kaitai::kstream* p__io, kaitai::kstruct* p__parent, mpc3000_seq_v3_t* p__root) : kaitai::kstruct(p__io) { + m__parent = p__parent; + m__root = p__root ? p__root : this; + m__dirty = false; +} + +void mpc3000_seq_v3_t::_read() { + m_file_id = m__io->read_bytes(1); + if (!(m_file_id == std::string("\x03", 1))) { + throw kaitai::validation_not_equal_error(std::string("\x03", 1), m_file_id, m__io, std::string("/seq/0")); + } + m_file_format_version = m__io->read_bytes(1); + if (!(m_file_format_version == std::string("\x03", 1))) { + throw kaitai::validation_not_equal_error(std::string("\x03", 1), m_file_format_version, m__io, std::string("/seq/1")); + } + m_sequence_header = std::unique_ptr(new sequence_header_t(m__io, this, m__root)); + m_sequence_header->_read(); + m_smpte_offset = std::unique_ptr(new smpte_offset_t(m__io, this, m__root)); + m_smpte_offset->_read(); + m_mixer = std::unique_ptr>>(new std::vector>()); + const int l_mixer = 64; + for (int i = 0; i < l_mixer; i++) { + std::unique_ptr _t_mixer = std::unique_ptr(new mixer_t(m__io, this, m__root)); + try { + _t_mixer->_read(); + } catch(...) { + m_mixer->push_back(std::move(_t_mixer)); + throw; + } + m_mixer->push_back(std::move(_t_mixer)); + } + m_reserved_after_mixer = m__io->read_bytes(2); + if (!(m_reserved_after_mixer == std::string("\x00\x00", 2))) { + throw kaitai::validation_not_equal_error(std::string("\x00\x00", 2), m_reserved_after_mixer, m__io, std::string("/seq/5")); + } + m_delays = std::unique_ptr(new delays_t(m__io, this, m__root)); + m_delays->_read(); + m_reserved_after_delays = m__io->read_bytes(3); + if (!(m_reserved_after_delays == std::string("\x00\x00\x00", 3))) { + throw kaitai::validation_not_equal_error(std::string("\x00\x00\x00", 3), m_reserved_after_delays, m__io, std::string("/seq/7")); + } + m_reserved_before_track_summary = m__io->read_bytes(16); + if (!(m_reserved_before_track_summary == std::string("\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", 16))) { + throw kaitai::validation_not_equal_error(std::string("\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", 16), m_reserved_before_track_summary, m__io, std::string("/seq/8")); + } + m_last_active_track = m__io->read_u1(); + m_num_tempo_changes = m__io->read_u1(); + m_num_track_headers = m__io->read_u1(); + m_track_headers = std::unique_ptr>>(new std::vector>()); + const int l_track_headers = num_track_headers(); + for (int i = 0; i < l_track_headers; i++) { + std::unique_ptr _t_track_headers = std::unique_ptr(new track_header_t(m__io, this, m__root)); + try { + _t_track_headers->_read(); + } catch(...) { + m_track_headers->push_back(std::move(_t_track_headers)); + throw; + } + m_track_headers->push_back(std::move(_t_track_headers)); + } + m_tempo_changes = std::unique_ptr>>(new std::vector>()); + const int l_tempo_changes = num_tempo_changes(); + for (int i = 0; i < l_tempo_changes; i++) { + std::unique_ptr _t_tempo_changes = std::unique_ptr(new tempo_change_t(m__io, this, m__root)); + try { + _t_tempo_changes->_read(); + } catch(...) { + m_tempo_changes->push_back(std::move(_t_tempo_changes)); + throw; + } + m_tempo_changes->push_back(std::move(_t_tempo_changes)); + } + m_events = std::unique_ptr>>(new std::vector>()); + { + int i = 0; + while (!m__io->is_eof()) { + std::unique_ptr _t_events = std::unique_ptr(new event_t(i == 0, ((i > 0) ? (events()->at(i - 1)->next_status()) : (255)), _root()->_io()->size() - _root()->_io()->pos(), m__io, this, m__root)); + try { + _t_events->_read(); + } catch(...) { + m_events->push_back(std::move(_t_events)); + throw; + } + m_events->push_back(std::move(_t_events)); + i++; + } + } + m__dirty = false; +} + +void mpc3000_seq_v3_t::_fetch_instances() { + m_sequence_header.get()->_fetch_instances(); + m_smpte_offset.get()->_fetch_instances(); + for (std::size_t i = 0; i < m_mixer->size(); ++i) { + m_mixer->at(i).get()->_fetch_instances(); + } + m_delays.get()->_fetch_instances(); + for (std::size_t i = 0; i < m_track_headers->size(); ++i) { + m_track_headers->at(i).get()->_fetch_instances(); + } + for (std::size_t i = 0; i < m_tempo_changes->size(); ++i) { + m_tempo_changes->at(i).get()->_fetch_instances(); + } + for (std::size_t i = 0; i < m_events->size(); ++i) { + m_events->at(i).get()->_fetch_instances(); + } +} + +void mpc3000_seq_v3_t::_write() { + m__io->write_bytes(m_file_id); + m__io->write_bytes(m_file_format_version); + if (m_sequence_header.get() == nullptr) { + throw std::runtime_error("/seq/2: nested object is not set"); + } + m_sequence_header.get()->_set_io(m__io); + m_sequence_header.get()->_write(); + if (m_smpte_offset.get() == nullptr) { + throw std::runtime_error("/seq/3: nested object is not set"); + } + m_smpte_offset.get()->_set_io(m__io); + m_smpte_offset.get()->_write(); + if (m_mixer == nullptr) { + throw std::runtime_error("/seq/4: repeated field is not set"); + } + for (std::vector>::const_iterator it = m_mixer->begin(); it != m_mixer->end(); ++it) { + if ((*it).get() == nullptr) { + throw std::runtime_error("/seq/4: nested object is not set"); + } + (*it).get()->_set_io(m__io); + (*it).get()->_write(); + } + m__io->write_bytes(m_reserved_after_mixer); + if (m_delays.get() == nullptr) { + throw std::runtime_error("/seq/6: nested object is not set"); + } + m_delays.get()->_set_io(m__io); + m_delays.get()->_write(); + m__io->write_bytes(m_reserved_after_delays); + m__io->write_bytes(m_reserved_before_track_summary); + m__io->write_u1(m_last_active_track); + m__io->write_u1(m_num_tempo_changes); + m__io->write_u1(m_num_track_headers); + if (m_track_headers == nullptr) { + throw std::runtime_error("/seq/12: repeated field is not set"); + } + for (std::vector>::const_iterator it = m_track_headers->begin(); it != m_track_headers->end(); ++it) { + if ((*it).get() == nullptr) { + throw std::runtime_error("/seq/12: nested object is not set"); + } + (*it).get()->_set_io(m__io); + (*it).get()->_write(); + } + if (m_tempo_changes == nullptr) { + throw std::runtime_error("/seq/13: repeated field is not set"); + } + for (std::vector>::const_iterator it = m_tempo_changes->begin(); it != m_tempo_changes->end(); ++it) { + if ((*it).get() == nullptr) { + throw std::runtime_error("/seq/13: nested object is not set"); + } + (*it).get()->_set_io(m__io); + (*it).get()->_write(); + } + if (m_events == nullptr) { + throw std::runtime_error("/seq/14: repeated field is not set"); + } + if (m_events == nullptr) { + throw std::runtime_error("/seq/14: repeated field is not set"); + } + for (std::vector>::const_iterator it = m_events->begin(); it != m_events->end(); ++it) { + if ((*it).get() == nullptr) { + throw std::runtime_error("/seq/14: nested object is not set"); + } + (*it).get()->_set_io(m__io); + (*it).get()->_write(); + } + _fetch_instances(); + m__dirty = false; +} + +void mpc3000_seq_v3_t::_check() { + if (m_file_id.size() != static_cast(1)) { + throw std::runtime_error("/seq/0: size mismatch"); + } + if (!(m_file_id == std::string("\x03", 1))) { + throw kaitai::validation_not_equal_error(std::string("\x03", 1), m_file_id, m__io, std::string("/seq/0")); + } + if (m_file_format_version.size() != static_cast(1)) { + throw std::runtime_error("/seq/1: size mismatch"); + } + if (!(m_file_format_version == std::string("\x03", 1))) { + throw kaitai::validation_not_equal_error(std::string("\x03", 1), m_file_format_version, m__io, std::string("/seq/1")); + } + if (m_sequence_header.get() == nullptr) { + throw std::runtime_error("/seq/2: nested object is not set"); + } + m_sequence_header.get()->_set_io(m__io); + m_sequence_header.get()->_check(); + if (m_smpte_offset.get() == nullptr) { + throw std::runtime_error("/seq/3: nested object is not set"); + } + m_smpte_offset.get()->_set_io(m__io); + m_smpte_offset.get()->_check(); + if (m_mixer == nullptr) { + throw std::runtime_error("/seq/4: repeated field is not set"); + } + if (m_mixer->size() != static_cast(64)) { + throw std::runtime_error("/seq/4: repeat-expr size mismatch"); + } + for (std::vector>::const_iterator it = m_mixer->begin(); it != m_mixer->end(); ++it) { + if ((*it).get() == nullptr) { + throw std::runtime_error("/seq/4: nested object is not set"); + } + (*it).get()->_set_io(m__io); + (*it).get()->_check(); + } + if (m_reserved_after_mixer.size() != static_cast(2)) { + throw std::runtime_error("/seq/5: size mismatch"); + } + if (!(m_reserved_after_mixer == std::string("\x00\x00", 2))) { + throw kaitai::validation_not_equal_error(std::string("\x00\x00", 2), m_reserved_after_mixer, m__io, std::string("/seq/5")); + } + if (m_delays.get() == nullptr) { + throw std::runtime_error("/seq/6: nested object is not set"); + } + m_delays.get()->_set_io(m__io); + m_delays.get()->_check(); + if (m_reserved_after_delays.size() != static_cast(3)) { + throw std::runtime_error("/seq/7: size mismatch"); + } + if (!(m_reserved_after_delays == std::string("\x00\x00\x00", 3))) { + throw kaitai::validation_not_equal_error(std::string("\x00\x00\x00", 3), m_reserved_after_delays, m__io, std::string("/seq/7")); + } + if (m_reserved_before_track_summary.size() != static_cast(16)) { + throw std::runtime_error("/seq/8: size mismatch"); + } + if (!(m_reserved_before_track_summary == std::string("\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", 16))) { + throw kaitai::validation_not_equal_error(std::string("\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", 16), m_reserved_before_track_summary, m__io, std::string("/seq/8")); + } + if (m_track_headers == nullptr) { + throw std::runtime_error("/seq/12: repeated field is not set"); + } + if (m_track_headers->size() != static_cast(num_track_headers())) { + throw std::runtime_error("/seq/12: repeat-expr size mismatch"); + } + for (std::vector>::const_iterator it = m_track_headers->begin(); it != m_track_headers->end(); ++it) { + if ((*it).get() == nullptr) { + throw std::runtime_error("/seq/12: nested object is not set"); + } + (*it).get()->_set_io(m__io); + (*it).get()->_check(); + } + if (m_tempo_changes == nullptr) { + throw std::runtime_error("/seq/13: repeated field is not set"); + } + if (m_tempo_changes->size() != static_cast(num_tempo_changes())) { + throw std::runtime_error("/seq/13: repeat-expr size mismatch"); + } + for (std::vector>::const_iterator it = m_tempo_changes->begin(); it != m_tempo_changes->end(); ++it) { + if ((*it).get() == nullptr) { + throw std::runtime_error("/seq/13: nested object is not set"); + } + (*it).get()->_set_io(m__io); + (*it).get()->_check(); + } + if (m_events == nullptr) { + throw std::runtime_error("/seq/14: repeated field is not set"); + } + for (std::vector>::const_iterator it = m_events->begin(); it != m_events->end(); ++it) { + const std::size_t i = static_cast(it - m_events->begin()); + const event_t* _ = (*it).get(); + if ((*it).get() == nullptr) { + throw std::runtime_error("/seq/14: nested object is not set"); + } + (*it).get()->_set_io(m__io); + (*it).get()->_check(); + } + m__dirty = false; +} + +mpc3000_seq_v3_t::~mpc3000_seq_v3_t() {} + +mpc3000_seq_v3_t::bar_event_t::bar_event_t(kaitai::kstream* p__io, mpc3000_seq_v3_t::event_t* p__parent, mpc3000_seq_v3_t* p__root) : kaitai::kstruct(p__io) { + m__parent = p__parent; + m__root = p__root; + m__dirty = false; + f_bar_number = false; +} + +void mpc3000_seq_v3_t::bar_event_t::_read() { + m_bar_number1 = m__io->read_u1(); + m_bar_number2 = m__io->read_u1(); + m_numerator = m__io->read_u1(); + m_denominator = m__io->read_u1(); + m__dirty = false; +} + +void mpc3000_seq_v3_t::bar_event_t::_fetch_instances() { +} + +void mpc3000_seq_v3_t::bar_event_t::_write() { + m__io->write_u1(m_bar_number1); + m__io->write_u1(m_bar_number2); + m__io->write_u1(m_numerator); + m__io->write_u1(m_denominator); + _fetch_instances(); + m__dirty = false; +} + +void mpc3000_seq_v3_t::bar_event_t::_check() { + m__dirty = false; +} + +mpc3000_seq_v3_t::bar_event_t::~bar_event_t() {} + +int32_t mpc3000_seq_v3_t::bar_event_t::bar_number() { + if (f_bar_number) + return m_bar_number; + f_bar_number = true; + m_bar_number = bar_number2() << 7 | bar_number1(); + return m_bar_number; +} + +mpc3000_seq_v3_t::ch_pressure_event_t::ch_pressure_event_t(kaitai::kstream* p__io, mpc3000_seq_v3_t::event_t* p__parent, mpc3000_seq_v3_t* p__root) : kaitai::kstruct(p__io) { + m__parent = p__parent; + m__root = p__root; + m__dirty = false; +} + +void mpc3000_seq_v3_t::ch_pressure_event_t::_read() { + m_pressure = m__io->read_u1(); + if (!(m_pressure >= 0)) { + throw kaitai::validation_less_than_error(0, m_pressure, m__io, std::string("/types/ch_pressure_event/seq/0")); + } + if (!(m_pressure <= 127)) { + throw kaitai::validation_greater_than_error(127, m_pressure, m__io, std::string("/types/ch_pressure_event/seq/0")); + } + m__dirty = false; +} + +void mpc3000_seq_v3_t::ch_pressure_event_t::_fetch_instances() { +} + +void mpc3000_seq_v3_t::ch_pressure_event_t::_write() { + m__io->write_u1(m_pressure); + _fetch_instances(); + m__dirty = false; +} + +void mpc3000_seq_v3_t::ch_pressure_event_t::_check() { + if (!(m_pressure >= 0)) { + throw kaitai::validation_less_than_error(0, m_pressure, m__io, std::string("/types/ch_pressure_event/seq/0")); + } + if (!(m_pressure <= 127)) { + throw kaitai::validation_greater_than_error(127, m_pressure, m__io, std::string("/types/ch_pressure_event/seq/0")); + } + m__dirty = false; +} + +mpc3000_seq_v3_t::ch_pressure_event_t::~ch_pressure_event_t() {} + +mpc3000_seq_v3_t::control_change_event_t::control_change_event_t(kaitai::kstream* p__io, mpc3000_seq_v3_t::event_t* p__parent, mpc3000_seq_v3_t* p__root) : kaitai::kstruct(p__io) { + m__parent = p__parent; + m__root = p__root; + m__dirty = false; +} + +void mpc3000_seq_v3_t::control_change_event_t::_read() { + m_controller = static_cast(m__io->read_u1()); + m_value = m__io->read_u1(); + if (!(m_value >= 0)) { + throw kaitai::validation_less_than_error(0, m_value, m__io, std::string("/types/control_change_event/seq/1")); + } + if (!(m_value <= 127)) { + throw kaitai::validation_greater_than_error(127, m_value, m__io, std::string("/types/control_change_event/seq/1")); + } + m__dirty = false; +} + +void mpc3000_seq_v3_t::control_change_event_t::_fetch_instances() { +} + +void mpc3000_seq_v3_t::control_change_event_t::_write() { + m__io->write_u1(static_cast(m_controller)); + m__io->write_u1(m_value); + _fetch_instances(); + m__dirty = false; +} + +void mpc3000_seq_v3_t::control_change_event_t::_check() { + if (!(m_value >= 0)) { + throw kaitai::validation_less_than_error(0, m_value, m__io, std::string("/types/control_change_event/seq/1")); + } + if (!(m_value <= 127)) { + throw kaitai::validation_greater_than_error(127, m_value, m__io, std::string("/types/control_change_event/seq/1")); + } + m__dirty = false; +} + +mpc3000_seq_v3_t::control_change_event_t::~control_change_event_t() {} + +mpc3000_seq_v3_t::delays_t::delays_t(kaitai::kstream* p__io, mpc3000_seq_v3_t* p__parent, mpc3000_seq_v3_t* p__root) : kaitai::kstruct(p__io) { + m__parent = p__parent; + m__root = p__root; + m__dirty = false; +} + +void mpc3000_seq_v3_t::delays_t::_read() { + m_volume1 = m__io->read_u1(); + if (!(m_volume1 >= 0)) { + throw kaitai::validation_less_than_error(0, m_volume1, m__io, std::string("/types/delays/seq/0")); + } + if (!(m_volume1 <= 100)) { + throw kaitai::validation_greater_than_error(100, m_volume1, m__io, std::string("/types/delays/seq/0")); + } + m_volume2 = m__io->read_u1(); + if (!(m_volume2 >= 0)) { + throw kaitai::validation_less_than_error(0, m_volume2, m__io, std::string("/types/delays/seq/1")); + } + if (!(m_volume2 <= 100)) { + throw kaitai::validation_greater_than_error(100, m_volume2, m__io, std::string("/types/delays/seq/1")); + } + m_volume3 = m__io->read_u1(); + if (!(m_volume3 >= 0)) { + throw kaitai::validation_less_than_error(0, m_volume3, m__io, std::string("/types/delays/seq/2")); + } + if (!(m_volume3 <= 100)) { + throw kaitai::validation_greater_than_error(100, m_volume3, m__io, std::string("/types/delays/seq/2")); + } + m_pan1 = m__io->read_u1(); + if (!(m_pan1 >= 0)) { + throw kaitai::validation_less_than_error(0, m_pan1, m__io, std::string("/types/delays/seq/3")); + } + if (!(m_pan1 <= 100)) { + throw kaitai::validation_greater_than_error(100, m_pan1, m__io, std::string("/types/delays/seq/3")); + } + m_pan2 = m__io->read_u1(); + if (!(m_pan2 >= 0)) { + throw kaitai::validation_less_than_error(0, m_pan2, m__io, std::string("/types/delays/seq/4")); + } + if (!(m_pan2 <= 100)) { + throw kaitai::validation_greater_than_error(100, m_pan2, m__io, std::string("/types/delays/seq/4")); + } + m_pan3 = m__io->read_u1(); + if (!(m_pan3 >= 0)) { + throw kaitai::validation_less_than_error(0, m_pan3, m__io, std::string("/types/delays/seq/5")); + } + if (!(m_pan3 <= 100)) { + throw kaitai::validation_greater_than_error(100, m_pan3, m__io, std::string("/types/delays/seq/5")); + } + m_time1 = m__io->read_u2le(); + if (!(m_time1 >= 0)) { + throw kaitai::validation_less_than_error(0, m_time1, m__io, std::string("/types/delays/seq/6")); + } + if (!(m_time1 <= 1486)) { + throw kaitai::validation_greater_than_error(1486, m_time1, m__io, std::string("/types/delays/seq/6")); + } + m_time2 = m__io->read_u2le(); + if (!(m_time2 >= 0)) { + throw kaitai::validation_less_than_error(0, m_time2, m__io, std::string("/types/delays/seq/7")); + } + if (!(m_time2 <= 1486)) { + throw kaitai::validation_greater_than_error(1486, m_time2, m__io, std::string("/types/delays/seq/7")); + } + m_time3 = m__io->read_u2le(); + if (!(m_time3 >= 0)) { + throw kaitai::validation_less_than_error(0, m_time3, m__io, std::string("/types/delays/seq/8")); + } + if (!(m_time3 <= 1486)) { + throw kaitai::validation_greater_than_error(1486, m_time3, m__io, std::string("/types/delays/seq/8")); + } + m_feedback1 = m__io->read_u1(); + if (!(m_feedback1 >= 0)) { + throw kaitai::validation_less_than_error(0, m_feedback1, m__io, std::string("/types/delays/seq/9")); + } + if (!(m_feedback1 <= 100)) { + throw kaitai::validation_greater_than_error(100, m_feedback1, m__io, std::string("/types/delays/seq/9")); + } + m_feedback2 = m__io->read_u1(); + if (!(m_feedback2 >= 0)) { + throw kaitai::validation_less_than_error(0, m_feedback2, m__io, std::string("/types/delays/seq/10")); + } + if (!(m_feedback2 <= 100)) { + throw kaitai::validation_greater_than_error(100, m_feedback2, m__io, std::string("/types/delays/seq/10")); + } + m_feedback3 = m__io->read_u1(); + if (!(m_feedback3 >= 0)) { + throw kaitai::validation_less_than_error(0, m_feedback3, m__io, std::string("/types/delays/seq/11")); + } + if (!(m_feedback3 <= 100)) { + throw kaitai::validation_greater_than_error(100, m_feedback3, m__io, std::string("/types/delays/seq/11")); + } + m__dirty = false; +} + +void mpc3000_seq_v3_t::delays_t::_fetch_instances() { +} + +void mpc3000_seq_v3_t::delays_t::_write() { + m__io->write_u1(m_volume1); + m__io->write_u1(m_volume2); + m__io->write_u1(m_volume3); + m__io->write_u1(m_pan1); + m__io->write_u1(m_pan2); + m__io->write_u1(m_pan3); + m__io->write_u2le(m_time1); + m__io->write_u2le(m_time2); + m__io->write_u2le(m_time3); + m__io->write_u1(m_feedback1); + m__io->write_u1(m_feedback2); + m__io->write_u1(m_feedback3); + _fetch_instances(); + m__dirty = false; +} + +void mpc3000_seq_v3_t::delays_t::_check() { + if (!(m_volume1 >= 0)) { + throw kaitai::validation_less_than_error(0, m_volume1, m__io, std::string("/types/delays/seq/0")); + } + if (!(m_volume1 <= 100)) { + throw kaitai::validation_greater_than_error(100, m_volume1, m__io, std::string("/types/delays/seq/0")); + } + if (!(m_volume2 >= 0)) { + throw kaitai::validation_less_than_error(0, m_volume2, m__io, std::string("/types/delays/seq/1")); + } + if (!(m_volume2 <= 100)) { + throw kaitai::validation_greater_than_error(100, m_volume2, m__io, std::string("/types/delays/seq/1")); + } + if (!(m_volume3 >= 0)) { + throw kaitai::validation_less_than_error(0, m_volume3, m__io, std::string("/types/delays/seq/2")); + } + if (!(m_volume3 <= 100)) { + throw kaitai::validation_greater_than_error(100, m_volume3, m__io, std::string("/types/delays/seq/2")); + } + if (!(m_pan1 >= 0)) { + throw kaitai::validation_less_than_error(0, m_pan1, m__io, std::string("/types/delays/seq/3")); + } + if (!(m_pan1 <= 100)) { + throw kaitai::validation_greater_than_error(100, m_pan1, m__io, std::string("/types/delays/seq/3")); + } + if (!(m_pan2 >= 0)) { + throw kaitai::validation_less_than_error(0, m_pan2, m__io, std::string("/types/delays/seq/4")); + } + if (!(m_pan2 <= 100)) { + throw kaitai::validation_greater_than_error(100, m_pan2, m__io, std::string("/types/delays/seq/4")); + } + if (!(m_pan3 >= 0)) { + throw kaitai::validation_less_than_error(0, m_pan3, m__io, std::string("/types/delays/seq/5")); + } + if (!(m_pan3 <= 100)) { + throw kaitai::validation_greater_than_error(100, m_pan3, m__io, std::string("/types/delays/seq/5")); + } + if (!(m_time1 >= 0)) { + throw kaitai::validation_less_than_error(0, m_time1, m__io, std::string("/types/delays/seq/6")); + } + if (!(m_time1 <= 1486)) { + throw kaitai::validation_greater_than_error(1486, m_time1, m__io, std::string("/types/delays/seq/6")); + } + if (!(m_time2 >= 0)) { + throw kaitai::validation_less_than_error(0, m_time2, m__io, std::string("/types/delays/seq/7")); + } + if (!(m_time2 <= 1486)) { + throw kaitai::validation_greater_than_error(1486, m_time2, m__io, std::string("/types/delays/seq/7")); + } + if (!(m_time3 >= 0)) { + throw kaitai::validation_less_than_error(0, m_time3, m__io, std::string("/types/delays/seq/8")); + } + if (!(m_time3 <= 1486)) { + throw kaitai::validation_greater_than_error(1486, m_time3, m__io, std::string("/types/delays/seq/8")); + } + if (!(m_feedback1 >= 0)) { + throw kaitai::validation_less_than_error(0, m_feedback1, m__io, std::string("/types/delays/seq/9")); + } + if (!(m_feedback1 <= 100)) { + throw kaitai::validation_greater_than_error(100, m_feedback1, m__io, std::string("/types/delays/seq/9")); + } + if (!(m_feedback2 >= 0)) { + throw kaitai::validation_less_than_error(0, m_feedback2, m__io, std::string("/types/delays/seq/10")); + } + if (!(m_feedback2 <= 100)) { + throw kaitai::validation_greater_than_error(100, m_feedback2, m__io, std::string("/types/delays/seq/10")); + } + if (!(m_feedback3 >= 0)) { + throw kaitai::validation_less_than_error(0, m_feedback3, m__io, std::string("/types/delays/seq/11")); + } + if (!(m_feedback3 <= 100)) { + throw kaitai::validation_greater_than_error(100, m_feedback3, m__io, std::string("/types/delays/seq/11")); + } + m__dirty = false; +} + +mpc3000_seq_v3_t::delays_t::~delays_t() {} + +mpc3000_seq_v3_t::delta_time_event_t::delta_time_event_t(kaitai::kstream* p__io, mpc3000_seq_v3_t::event_t* p__parent, mpc3000_seq_v3_t* p__root) : kaitai::kstruct(p__io) { + m__parent = p__parent; + m__root = p__root; + m__dirty = false; +} + +void mpc3000_seq_v3_t::delta_time_event_t::_read() { + m_delta_time = m__io->read_u2le(); + m__dirty = false; +} + +void mpc3000_seq_v3_t::delta_time_event_t::_fetch_instances() { +} + +void mpc3000_seq_v3_t::delta_time_event_t::_write() { + m__io->write_u2le(m_delta_time); + _fetch_instances(); + m__dirty = false; +} + +void mpc3000_seq_v3_t::delta_time_event_t::_check() { + m__dirty = false; +} + +mpc3000_seq_v3_t::delta_time_event_t::~delta_time_event_t() {} + +mpc3000_seq_v3_t::event_t::event_t(bool p_is_first_event, uint8_t p_preparsed_status, int32_t p_remaining_byte_count, kaitai::kstream* p__io, mpc3000_seq_v3_t* p__parent, mpc3000_seq_v3_t* p__root) : kaitai::kstruct(p__io) { + m__parent = p__parent; + m__root = p__root; + m_is_first_event = p_is_first_event; + m_preparsed_status = p_preparsed_status; + m_remaining_byte_count = p_remaining_byte_count; + m__dirty = false; + n_parsed_status = true; + n_track_number = true; + n_parsed_next_status = true; + f_mixer_event = false; + f_next_status = false; + f_status = false; + w_mixer_event = false; + e_mixer_event = true; +} + +void mpc3000_seq_v3_t::event_t::_read() { + n_parsed_status = true; + if (is_first_event()) { + n_parsed_status = false; + m_parsed_status = m__io->read_u1(); + } + n_track_number = true; + if ( ((status() != 168) && (status() != 136) && (status() != 255)) ) { + n_track_number = false; + m_track_number = m__io->read_u1(); + } + switch (status()) { + case 136: { + m_event_body = std::unique_ptr(new delta_time_event_t(m__io, this, m__root)); + static_cast(m_event_body.get())->_read(); + break; + } + case 144: { + m_event_body = std::unique_ptr(new note_event_t(m__io, this, m__root)); + static_cast(m_event_body.get())->_read(); + break; + } + case 152: { + m_event_body = std::unique_ptr(new note_event_t(m__io, this, m__root)); + static_cast(m_event_body.get())->_read(); + break; + } + case 160: { + m_event_body = std::unique_ptr(new poly_pressure_event_t(m__io, this, m__root)); + static_cast(m_event_body.get())->_read(); + break; + } + case 168: { + m_event_body = std::unique_ptr(new bar_event_t(m__io, this, m__root)); + static_cast(m_event_body.get())->_read(); + break; + } + case 176: { + m_event_body = std::unique_ptr(new control_change_event_t(m__io, this, m__root)); + static_cast(m_event_body.get())->_read(); + break; + } + case 192: { + m_event_body = std::unique_ptr(new program_change_event_t(m__io, this, m__root)); + static_cast(m_event_body.get())->_read(); + break; + } + case 208: { + m_event_body = std::unique_ptr(new ch_pressure_event_t(m__io, this, m__root)); + static_cast(m_event_body.get())->_read(); + break; + } + case 224: { + m_event_body = std::unique_ptr(new pitch_bend_event_t(m__io, this, m__root)); + static_cast(m_event_body.get())->_read(); + break; + } + case 232: { + m_event_body = std::unique_ptr(new tune_request_event_t(m__io, this, m__root)); + static_cast(m_event_body.get())->_read(); + break; + } + } + if (status() == 240) { + m_system_exclusive_body = std::unique_ptr>(new std::vector()); + { + int i = 0; + uint8_t _; + do { + _ = m__io->read_u1(); + m_system_exclusive_body->push_back(_); + i++; + } while (!( ((i >= remaining_byte_count()) || (_ > 127)) )); + } + } + n_parsed_next_status = true; + if ( ((!(status() == 240)) && (remaining_byte_count() > 2)) ) { + n_parsed_next_status = false; + m_parsed_next_status = m__io->read_u1(); + } + m__dirty = false; +} + +void mpc3000_seq_v3_t::event_t::_fetch_instances() { + n_parsed_status = true; + if (is_first_event()) { + n_parsed_status = false; + } + n_track_number = true; + if ( ((status() != 168) && (status() != 136) && (status() != 255)) ) { + n_track_number = false; + } + switch (status()) { + case 136: { + { + delta_time_event_t* _switch_obj = dynamic_cast(m_event_body.get()); + if (_switch_obj == nullptr) { + throw std::runtime_error("switch object type mismatch in _fetch_instances"); + } + _switch_obj->_fetch_instances(); + } + break; + } + case 144: { + { + note_event_t* _switch_obj = dynamic_cast(m_event_body.get()); + if (_switch_obj == nullptr) { + throw std::runtime_error("switch object type mismatch in _fetch_instances"); + } + _switch_obj->_fetch_instances(); + } + break; + } + case 152: { + { + note_event_t* _switch_obj = dynamic_cast(m_event_body.get()); + if (_switch_obj == nullptr) { + throw std::runtime_error("switch object type mismatch in _fetch_instances"); + } + _switch_obj->_fetch_instances(); + } + break; + } + case 160: { + { + poly_pressure_event_t* _switch_obj = dynamic_cast(m_event_body.get()); + if (_switch_obj == nullptr) { + throw std::runtime_error("switch object type mismatch in _fetch_instances"); + } + _switch_obj->_fetch_instances(); + } + break; + } + case 168: { + { + bar_event_t* _switch_obj = dynamic_cast(m_event_body.get()); + if (_switch_obj == nullptr) { + throw std::runtime_error("switch object type mismatch in _fetch_instances"); + } + _switch_obj->_fetch_instances(); + } + break; + } + case 176: { + { + control_change_event_t* _switch_obj = dynamic_cast(m_event_body.get()); + if (_switch_obj == nullptr) { + throw std::runtime_error("switch object type mismatch in _fetch_instances"); + } + _switch_obj->_fetch_instances(); + } + break; + } + case 192: { + { + program_change_event_t* _switch_obj = dynamic_cast(m_event_body.get()); + if (_switch_obj == nullptr) { + throw std::runtime_error("switch object type mismatch in _fetch_instances"); + } + _switch_obj->_fetch_instances(); + } + break; + } + case 208: { + { + ch_pressure_event_t* _switch_obj = dynamic_cast(m_event_body.get()); + if (_switch_obj == nullptr) { + throw std::runtime_error("switch object type mismatch in _fetch_instances"); + } + _switch_obj->_fetch_instances(); + } + break; + } + case 224: { + { + pitch_bend_event_t* _switch_obj = dynamic_cast(m_event_body.get()); + if (_switch_obj == nullptr) { + throw std::runtime_error("switch object type mismatch in _fetch_instances"); + } + _switch_obj->_fetch_instances(); + } + break; + } + case 232: { + { + tune_request_event_t* _switch_obj = dynamic_cast(m_event_body.get()); + if (_switch_obj == nullptr) { + throw std::runtime_error("switch object type mismatch in _fetch_instances"); + } + _switch_obj->_fetch_instances(); + } + break; + } + } + if (status() == 240) { + for (std::size_t i = 0; i < m_system_exclusive_body->size(); ++i) { + } + } + n_parsed_next_status = true; + if ( ((!(status() == 240)) && (remaining_byte_count() > 2)) ) { + n_parsed_next_status = false; + } + mixer_event(); + if (f_mixer_event) { + m_mixer_event.get()->_fetch_instances(); + } +} + +void mpc3000_seq_v3_t::event_t::_write() { + w_mixer_event = e_mixer_event; + if (is_first_event()) { + m__io->write_u1(m_parsed_status); + } + if ( ((status() != 168) && (status() != 136) && (status() != 255)) ) { + m__io->write_u1(m_track_number); + } + switch (status()) { + case 136: { + { + delta_time_event_t* _switch_obj = dynamic_cast(m_event_body.get()); + if (_switch_obj == nullptr) { + throw std::runtime_error("/types/event/seq/2: switch object type mismatch"); + } + if (_switch_obj == nullptr) { + throw std::runtime_error("/types/event/seq/2: nested object is not set"); + } + _switch_obj->_set_io(m__io); + _switch_obj->_write(); + } + break; + } + case 144: { + { + note_event_t* _switch_obj = dynamic_cast(m_event_body.get()); + if (_switch_obj == nullptr) { + throw std::runtime_error("/types/event/seq/2: switch object type mismatch"); + } + if (_switch_obj == nullptr) { + throw std::runtime_error("/types/event/seq/2: nested object is not set"); + } + _switch_obj->_set_io(m__io); + _switch_obj->_write(); + } + break; + } + case 152: { + { + note_event_t* _switch_obj = dynamic_cast(m_event_body.get()); + if (_switch_obj == nullptr) { + throw std::runtime_error("/types/event/seq/2: switch object type mismatch"); + } + if (_switch_obj == nullptr) { + throw std::runtime_error("/types/event/seq/2: nested object is not set"); + } + _switch_obj->_set_io(m__io); + _switch_obj->_write(); + } + break; + } + case 160: { + { + poly_pressure_event_t* _switch_obj = dynamic_cast(m_event_body.get()); + if (_switch_obj == nullptr) { + throw std::runtime_error("/types/event/seq/2: switch object type mismatch"); + } + if (_switch_obj == nullptr) { + throw std::runtime_error("/types/event/seq/2: nested object is not set"); + } + _switch_obj->_set_io(m__io); + _switch_obj->_write(); + } + break; + } + case 168: { + { + bar_event_t* _switch_obj = dynamic_cast(m_event_body.get()); + if (_switch_obj == nullptr) { + throw std::runtime_error("/types/event/seq/2: switch object type mismatch"); + } + if (_switch_obj == nullptr) { + throw std::runtime_error("/types/event/seq/2: nested object is not set"); + } + _switch_obj->_set_io(m__io); + _switch_obj->_write(); + } + break; + } + case 176: { + { + control_change_event_t* _switch_obj = dynamic_cast(m_event_body.get()); + if (_switch_obj == nullptr) { + throw std::runtime_error("/types/event/seq/2: switch object type mismatch"); + } + if (_switch_obj == nullptr) { + throw std::runtime_error("/types/event/seq/2: nested object is not set"); + } + _switch_obj->_set_io(m__io); + _switch_obj->_write(); + } + break; + } + case 192: { + { + program_change_event_t* _switch_obj = dynamic_cast(m_event_body.get()); + if (_switch_obj == nullptr) { + throw std::runtime_error("/types/event/seq/2: switch object type mismatch"); + } + if (_switch_obj == nullptr) { + throw std::runtime_error("/types/event/seq/2: nested object is not set"); + } + _switch_obj->_set_io(m__io); + _switch_obj->_write(); + } + break; + } + case 208: { + { + ch_pressure_event_t* _switch_obj = dynamic_cast(m_event_body.get()); + if (_switch_obj == nullptr) { + throw std::runtime_error("/types/event/seq/2: switch object type mismatch"); + } + if (_switch_obj == nullptr) { + throw std::runtime_error("/types/event/seq/2: nested object is not set"); + } + _switch_obj->_set_io(m__io); + _switch_obj->_write(); + } + break; + } + case 224: { + { + pitch_bend_event_t* _switch_obj = dynamic_cast(m_event_body.get()); + if (_switch_obj == nullptr) { + throw std::runtime_error("/types/event/seq/2: switch object type mismatch"); + } + if (_switch_obj == nullptr) { + throw std::runtime_error("/types/event/seq/2: nested object is not set"); + } + _switch_obj->_set_io(m__io); + _switch_obj->_write(); + } + break; + } + case 232: { + { + tune_request_event_t* _switch_obj = dynamic_cast(m_event_body.get()); + if (_switch_obj == nullptr) { + throw std::runtime_error("/types/event/seq/2: switch object type mismatch"); + } + if (_switch_obj == nullptr) { + throw std::runtime_error("/types/event/seq/2: nested object is not set"); + } + _switch_obj->_set_io(m__io); + _switch_obj->_write(); + } + break; + } + } + if (status() == 240) { + if (m_system_exclusive_body == nullptr) { + throw std::runtime_error("/types/event/seq/3: repeated field is not set"); + } + for (std::vector::const_iterator it = m_system_exclusive_body->begin(); it != m_system_exclusive_body->end(); ++it) { + m__io->write_u1((*it)); + } + } + if ( ((!(status() == 240)) && (remaining_byte_count() > 2)) ) { + m__io->write_u1(m_parsed_next_status); + } + _fetch_instances(); + m__dirty = false; +} + +void mpc3000_seq_v3_t::event_t::_check() { + if (is_first_event()) { + if (!(!n_parsed_status)) { + throw std::runtime_error("/types/event/seq/0: conditional field is not set"); + } + } else { + if (!n_parsed_status) { + throw std::runtime_error("/types/event/seq/0: conditional field should be absent"); + } + } + if ( ((status() != 168) && (status() != 136) && (status() != 255)) ) { + if (!(!n_track_number)) { + throw std::runtime_error("/types/event/seq/1: conditional field is not set"); + } + } else { + if (!n_track_number) { + throw std::runtime_error("/types/event/seq/1: conditional field should be absent"); + } + } + switch (status()) { + case 136: { + { + delta_time_event_t* _switch_obj = dynamic_cast(m_event_body.get()); + if (_switch_obj == nullptr) { + throw std::runtime_error("/types/event/seq/2: switch object type mismatch"); + } + if (_switch_obj == nullptr) { + throw std::runtime_error("/types/event/seq/2: nested object is not set"); + } + _switch_obj->_set_io(m__io); + _switch_obj->_check(); + } + break; + } + case 144: { + { + note_event_t* _switch_obj = dynamic_cast(m_event_body.get()); + if (_switch_obj == nullptr) { + throw std::runtime_error("/types/event/seq/2: switch object type mismatch"); + } + if (_switch_obj == nullptr) { + throw std::runtime_error("/types/event/seq/2: nested object is not set"); + } + _switch_obj->_set_io(m__io); + _switch_obj->_check(); + } + break; + } + case 152: { + { + note_event_t* _switch_obj = dynamic_cast(m_event_body.get()); + if (_switch_obj == nullptr) { + throw std::runtime_error("/types/event/seq/2: switch object type mismatch"); + } + if (_switch_obj == nullptr) { + throw std::runtime_error("/types/event/seq/2: nested object is not set"); + } + _switch_obj->_set_io(m__io); + _switch_obj->_check(); + } + break; + } + case 160: { + { + poly_pressure_event_t* _switch_obj = dynamic_cast(m_event_body.get()); + if (_switch_obj == nullptr) { + throw std::runtime_error("/types/event/seq/2: switch object type mismatch"); + } + if (_switch_obj == nullptr) { + throw std::runtime_error("/types/event/seq/2: nested object is not set"); + } + _switch_obj->_set_io(m__io); + _switch_obj->_check(); + } + break; + } + case 168: { + { + bar_event_t* _switch_obj = dynamic_cast(m_event_body.get()); + if (_switch_obj == nullptr) { + throw std::runtime_error("/types/event/seq/2: switch object type mismatch"); + } + if (_switch_obj == nullptr) { + throw std::runtime_error("/types/event/seq/2: nested object is not set"); + } + _switch_obj->_set_io(m__io); + _switch_obj->_check(); + } + break; + } + case 176: { + { + control_change_event_t* _switch_obj = dynamic_cast(m_event_body.get()); + if (_switch_obj == nullptr) { + throw std::runtime_error("/types/event/seq/2: switch object type mismatch"); + } + if (_switch_obj == nullptr) { + throw std::runtime_error("/types/event/seq/2: nested object is not set"); + } + _switch_obj->_set_io(m__io); + _switch_obj->_check(); + } + break; + } + case 192: { + { + program_change_event_t* _switch_obj = dynamic_cast(m_event_body.get()); + if (_switch_obj == nullptr) { + throw std::runtime_error("/types/event/seq/2: switch object type mismatch"); + } + if (_switch_obj == nullptr) { + throw std::runtime_error("/types/event/seq/2: nested object is not set"); + } + _switch_obj->_set_io(m__io); + _switch_obj->_check(); + } + break; + } + case 208: { + { + ch_pressure_event_t* _switch_obj = dynamic_cast(m_event_body.get()); + if (_switch_obj == nullptr) { + throw std::runtime_error("/types/event/seq/2: switch object type mismatch"); + } + if (_switch_obj == nullptr) { + throw std::runtime_error("/types/event/seq/2: nested object is not set"); + } + _switch_obj->_set_io(m__io); + _switch_obj->_check(); + } + break; + } + case 224: { + { + pitch_bend_event_t* _switch_obj = dynamic_cast(m_event_body.get()); + if (_switch_obj == nullptr) { + throw std::runtime_error("/types/event/seq/2: switch object type mismatch"); + } + if (_switch_obj == nullptr) { + throw std::runtime_error("/types/event/seq/2: nested object is not set"); + } + _switch_obj->_set_io(m__io); + _switch_obj->_check(); + } + break; + } + case 232: { + { + tune_request_event_t* _switch_obj = dynamic_cast(m_event_body.get()); + if (_switch_obj == nullptr) { + throw std::runtime_error("/types/event/seq/2: switch object type mismatch"); + } + if (_switch_obj == nullptr) { + throw std::runtime_error("/types/event/seq/2: nested object is not set"); + } + _switch_obj->_set_io(m__io); + _switch_obj->_check(); + } + break; + } + } + if (status() == 240) { + if (!(m_system_exclusive_body != nullptr)) { + throw std::runtime_error("/types/event/seq/3: conditional field is not set"); + } + if (m_system_exclusive_body == nullptr) { + throw std::runtime_error("/types/event/seq/3: repeated field is not set"); + } + if (m_system_exclusive_body->empty()) { + throw std::runtime_error("/types/event/seq/3: repeat-until field must not be empty"); + } + for (std::vector::const_iterator it = m_system_exclusive_body->begin(); it != m_system_exclusive_body->end(); ++it) { + const std::size_t i = static_cast(it - m_system_exclusive_body->begin()); + const uint8_t _ = (*it); + const bool _is_last = (i == m_system_exclusive_body->size() - 1); + if (( ((i >= remaining_byte_count()) || (_ > 127)) ) != _is_last) { + throw std::runtime_error("/types/event/seq/3: repeat-until condition mismatch"); + } + } + } else { + if (m_system_exclusive_body != nullptr) { + throw std::runtime_error("/types/event/seq/3: conditional field should be absent"); + } + } + if ( ((!(status() == 240)) && (remaining_byte_count() > 2)) ) { + if (!(!n_parsed_next_status)) { + throw std::runtime_error("/types/event/seq/4: conditional field is not set"); + } + } else { + if (!n_parsed_next_status) { + throw std::runtime_error("/types/event/seq/4: conditional field should be absent"); + } + } + if (e_mixer_event) { + if ( ((status() == 240) && (system_exclusive_body()->size() >= 7) && (system_exclusive_body()->at(0) == 71) && (system_exclusive_body()->at(1) == 0) && (system_exclusive_body()->at(2) == 68) && (system_exclusive_body()->at(3) == 69)) ) { + if (!(m_mixer_event.get() != nullptr)) { + throw std::runtime_error("/types/event/instances/mixer_event: conditional field is not set"); + } + if (m_mixer_event.get() == nullptr) { + throw std::runtime_error("/types/event/instances/mixer_event: nested object is not set"); + } + m_mixer_event.get()->_set_io(m__io); + m_mixer_event.get()->_check(); + } else { + if (m_mixer_event.get() != nullptr) { + throw std::runtime_error("/types/event/instances/mixer_event: conditional field should be absent"); + } + } + } + m__dirty = false; +} + +mpc3000_seq_v3_t::event_t::~event_t() {} + +mpc3000_seq_v3_t::mixer_event_t* mpc3000_seq_v3_t::event_t::mixer_event() { + if (w_mixer_event) + _write_mixer_event(); + if (f_mixer_event) + return m_mixer_event.get(); + if (!e_mixer_event) + return nullptr; + f_mixer_event = true; + if ( ((status() == 240) && (system_exclusive_body()->size() >= 7) && (system_exclusive_body()->at(0) == 71) && (system_exclusive_body()->at(1) == 0) && (system_exclusive_body()->at(2) == 68) && (system_exclusive_body()->at(3) == 69)) ) { + m_mixer_event = std::unique_ptr(new mixer_event_t(system_exclusive_body(), m__io, this, m__root)); + m_mixer_event->_read(); + } + return m_mixer_event.get(); +} + +void mpc3000_seq_v3_t::event_t::_write_mixer_event() { + w_mixer_event = false; + if ( ((status() == 240) && (system_exclusive_body()->size() >= 7) && (system_exclusive_body()->at(0) == 71) && (system_exclusive_body()->at(1) == 0) && (system_exclusive_body()->at(2) == 68) && (system_exclusive_body()->at(3) == 69)) ) { + if (m_mixer_event.get() == nullptr) { + throw std::runtime_error("/types/event/instances/mixer_event: nested object is not set"); + } + m_mixer_event.get()->_set_io(m__io); + m_mixer_event.get()->_write(); + } +} + +uint8_t mpc3000_seq_v3_t::event_t::next_status() { + if (f_next_status) + return m_next_status; + f_next_status = true; + m_next_status = ((status() == 240) ? (system_exclusive_body()->back()) : (parsed_next_status())); + return m_next_status; +} + +uint8_t mpc3000_seq_v3_t::event_t::status() { + if (f_status) + return m_status; + f_status = true; + m_status = ((preparsed_status() == 255) ? (parsed_status()) : (preparsed_status())); + return m_status; +} + +mpc3000_seq_v3_t::mixer_t::mixer_t(kaitai::kstream* p__io, mpc3000_seq_v3_t* p__parent, mpc3000_seq_v3_t* p__root) : kaitai::kstruct(p__io) { + m__parent = p__parent; + m__root = p__root; + m__dirty = false; +} + +void mpc3000_seq_v3_t::mixer_t::_read() { + m_stereo_mix = m__io->read_u1(); + if (!(m_stereo_mix >= 0)) { + throw kaitai::validation_less_than_error(0, m_stereo_mix, m__io, std::string("/types/mixer/seq/0")); + } + if (!(m_stereo_mix <= 100)) { + throw kaitai::validation_greater_than_error(100, m_stereo_mix, m__io, std::string("/types/mixer/seq/0")); + } + m_stereo_pan = m__io->read_u1(); + if (!(m_stereo_pan >= 0)) { + throw kaitai::validation_less_than_error(0, m_stereo_pan, m__io, std::string("/types/mixer/seq/1")); + } + if (!(m_stereo_pan <= 100)) { + throw kaitai::validation_greater_than_error(100, m_stereo_pan, m__io, std::string("/types/mixer/seq/1")); + } + m_individual_out_mix = m__io->read_u1(); + m_individual_out = static_cast(m__io->read_bits_int_le(7)); + m_follow_stereo = static_cast(m__io->read_bits_int_le(1)); + m__dirty = false; +} + +void mpc3000_seq_v3_t::mixer_t::_fetch_instances() { +} + +void mpc3000_seq_v3_t::mixer_t::_write() { + m__io->write_u1(m_stereo_mix); + m__io->write_u1(m_stereo_pan); + m__io->write_u1(m_individual_out_mix); + m__io->write_bits_int_le(7, static_cast(m_individual_out)); + m__io->write_bits_int_le(1, static_cast(m_follow_stereo)); + _fetch_instances(); + m__dirty = false; +} + +void mpc3000_seq_v3_t::mixer_t::_check() { + if (!(m_stereo_mix >= 0)) { + throw kaitai::validation_less_than_error(0, m_stereo_mix, m__io, std::string("/types/mixer/seq/0")); + } + if (!(m_stereo_mix <= 100)) { + throw kaitai::validation_greater_than_error(100, m_stereo_mix, m__io, std::string("/types/mixer/seq/0")); + } + if (!(m_stereo_pan >= 0)) { + throw kaitai::validation_less_than_error(0, m_stereo_pan, m__io, std::string("/types/mixer/seq/1")); + } + if (!(m_stereo_pan <= 100)) { + throw kaitai::validation_greater_than_error(100, m_stereo_pan, m__io, std::string("/types/mixer/seq/1")); + } + m__dirty = false; +} + +mpc3000_seq_v3_t::mixer_t::~mixer_t() {} + +mpc3000_seq_v3_t::mixer_event_t::mixer_event_t(std::vector* p_data, kaitai::kstream* p__io, mpc3000_seq_v3_t::event_t* p__parent, mpc3000_seq_v3_t* p__root) : kaitai::kstruct(p__io) { + m__parent = p__parent; + m__root = p__root; + m_data = p_data; + m__dirty = false; + f_pad_index = false; + f_param = false; + f_value = false; +} + +void mpc3000_seq_v3_t::mixer_event_t::_read() { + m__dirty = false; +} + +void mpc3000_seq_v3_t::mixer_event_t::_fetch_instances() { +} + +void mpc3000_seq_v3_t::mixer_event_t::_write() { + _fetch_instances(); + m__dirty = false; +} + +void mpc3000_seq_v3_t::mixer_event_t::_check() { + m__dirty = false; +} + +mpc3000_seq_v3_t::mixer_event_t::~mixer_event_t() {} + +uint8_t mpc3000_seq_v3_t::mixer_event_t::pad_index() { + if (f_pad_index) + return m_pad_index; + f_pad_index = true; + m_pad_index = data()->at(5); + return m_pad_index; +} + +mpc3000_seq_v3_t::mixer_event_param_t mpc3000_seq_v3_t::mixer_event_t::param() { + if (f_param) + return m_param; + f_param = true; + m_param = static_cast(data()->at(4)); + return m_param; +} + +uint8_t mpc3000_seq_v3_t::mixer_event_t::value() { + if (f_value) + return m_value; + f_value = true; + m_value = data()->at(6); + return m_value; +} +const std::set mpc3000_seq_v3_t::note_event_t::_values_note_variation_type_t{ + mpc3000_seq_v3_t::note_event_t::NOTE_VARIATION_TYPE_TUNE, + mpc3000_seq_v3_t::note_event_t::NOTE_VARIATION_TYPE_DECAY, + mpc3000_seq_v3_t::note_event_t::NOTE_VARIATION_TYPE_ATTACK, + mpc3000_seq_v3_t::note_event_t::NOTE_VARIATION_TYPE_FILTER, +}; +bool mpc3000_seq_v3_t::note_event_t::_is_defined_note_variation_type_t(mpc3000_seq_v3_t::note_event_t::note_variation_type_t v) { + return mpc3000_seq_v3_t::note_event_t::_values_note_variation_type_t.find(v) != mpc3000_seq_v3_t::note_event_t::_values_note_variation_type_t.end(); +} + +mpc3000_seq_v3_t::note_event_t::note_event_t(kaitai::kstream* p__io, mpc3000_seq_v3_t::event_t* p__parent, mpc3000_seq_v3_t* p__root) : kaitai::kstruct(p__io) { + m__parent = p__parent; + m__root = p__root; + m__dirty = false; + f_duration = false; + f_note_variation_type = false; +} + +void mpc3000_seq_v3_t::note_event_t::_read() { + m_note_number = m__io->read_u1(); + if (!(m_note_number >= 0)) { + throw kaitai::validation_less_than_error(0, m_note_number, m__io, std::string("/types/note_event/seq/0")); + } + if (!(m_note_number <= 127)) { + throw kaitai::validation_greater_than_error(127, m_note_number, m__io, std::string("/types/note_event/seq/0")); + } + m_velocity = m__io->read_u1(); + if (!(m_velocity >= 0)) { + throw kaitai::validation_less_than_error(0, m_velocity, m__io, std::string("/types/note_event/seq/1")); + } + if (!(m_velocity <= 127)) { + throw kaitai::validation_greater_than_error(127, m_velocity, m__io, std::string("/types/note_event/seq/1")); + } + m_note_variation_value = m__io->read_u1(); + m_duration_byte_1 = m__io->read_u1(); + m_duration_byte_2 = m__io->read_u1(); + m__dirty = false; +} + +void mpc3000_seq_v3_t::note_event_t::_fetch_instances() { +} + +void mpc3000_seq_v3_t::note_event_t::_write() { + m__io->write_u1(m_note_number); + m__io->write_u1(m_velocity); + m__io->write_u1(m_note_variation_value); + m__io->write_u1(m_duration_byte_1); + m__io->write_u1(m_duration_byte_2); + _fetch_instances(); + m__dirty = false; +} + +void mpc3000_seq_v3_t::note_event_t::_check() { + if (!(m_note_number >= 0)) { + throw kaitai::validation_less_than_error(0, m_note_number, m__io, std::string("/types/note_event/seq/0")); + } + if (!(m_note_number <= 127)) { + throw kaitai::validation_greater_than_error(127, m_note_number, m__io, std::string("/types/note_event/seq/0")); + } + if (!(m_velocity >= 0)) { + throw kaitai::validation_less_than_error(0, m_velocity, m__io, std::string("/types/note_event/seq/1")); + } + if (!(m_velocity <= 127)) { + throw kaitai::validation_greater_than_error(127, m_velocity, m__io, std::string("/types/note_event/seq/1")); + } + m__dirty = false; +} + +mpc3000_seq_v3_t::note_event_t::~note_event_t() {} + +int32_t mpc3000_seq_v3_t::note_event_t::duration() { + if (f_duration) + return m_duration; + f_duration = true; + m_duration = duration_byte_1() + (duration_byte_2() << 7); + return m_duration; +} + +mpc3000_seq_v3_t::note_event_t::note_variation_type_t mpc3000_seq_v3_t::note_event_t::note_variation_type() { + if (f_note_variation_type) + return m_note_variation_type; + f_note_variation_type = true; + m_note_variation_type = static_cast(static_cast(_parent())->status() - 152); + return m_note_variation_type; +} + +mpc3000_seq_v3_t::pitch_bend_event_t::pitch_bend_event_t(kaitai::kstream* p__io, mpc3000_seq_v3_t::event_t* p__parent, mpc3000_seq_v3_t* p__root) : kaitai::kstruct(p__io) { + m__parent = p__parent; + m__root = p__root; + m__dirty = false; + f_corrected_pitch_bend_amount = false; +} + +void mpc3000_seq_v3_t::pitch_bend_event_t::_read() { + m_pitch_bend_amount_bits_1 = m__io->read_bits_int_le(8); + m_pitch_bend_amount_bits_2 = m__io->read_bits_int_le(8); + m__dirty = false; +} + +void mpc3000_seq_v3_t::pitch_bend_event_t::_fetch_instances() { +} + +void mpc3000_seq_v3_t::pitch_bend_event_t::_write() { + m__io->write_bits_int_le(8, m_pitch_bend_amount_bits_1); + m__io->write_bits_int_le(8, m_pitch_bend_amount_bits_2); + _fetch_instances(); + m__dirty = false; +} + +void mpc3000_seq_v3_t::pitch_bend_event_t::_check() { + m__dirty = false; +} + +mpc3000_seq_v3_t::pitch_bend_event_t::~pitch_bend_event_t() {} + +int32_t mpc3000_seq_v3_t::pitch_bend_event_t::corrected_pitch_bend_amount() { + if (f_corrected_pitch_bend_amount) + return m_corrected_pitch_bend_amount; + f_corrected_pitch_bend_amount = true; + m_corrected_pitch_bend_amount = (pitch_bend_amount_bits_1() + (pitch_bend_amount_bits_2() << 7)) - 8192; + return m_corrected_pitch_bend_amount; +} + +mpc3000_seq_v3_t::poly_pressure_event_t::poly_pressure_event_t(kaitai::kstream* p__io, mpc3000_seq_v3_t::event_t* p__parent, mpc3000_seq_v3_t* p__root) : kaitai::kstruct(p__io) { + m__parent = p__parent; + m__root = p__root; + m__dirty = false; +} + +void mpc3000_seq_v3_t::poly_pressure_event_t::_read() { + m_note = m__io->read_u1(); + if (!(m_note >= 0)) { + throw kaitai::validation_less_than_error(0, m_note, m__io, std::string("/types/poly_pressure_event/seq/0")); + } + if (!(m_note <= 127)) { + throw kaitai::validation_greater_than_error(127, m_note, m__io, std::string("/types/poly_pressure_event/seq/0")); + } + m_pressure = m__io->read_u1(); + if (!(m_pressure >= 0)) { + throw kaitai::validation_less_than_error(0, m_pressure, m__io, std::string("/types/poly_pressure_event/seq/1")); + } + if (!(m_pressure <= 127)) { + throw kaitai::validation_greater_than_error(127, m_pressure, m__io, std::string("/types/poly_pressure_event/seq/1")); + } + m__dirty = false; +} + +void mpc3000_seq_v3_t::poly_pressure_event_t::_fetch_instances() { +} + +void mpc3000_seq_v3_t::poly_pressure_event_t::_write() { + m__io->write_u1(m_note); + m__io->write_u1(m_pressure); + _fetch_instances(); + m__dirty = false; +} + +void mpc3000_seq_v3_t::poly_pressure_event_t::_check() { + if (!(m_note >= 0)) { + throw kaitai::validation_less_than_error(0, m_note, m__io, std::string("/types/poly_pressure_event/seq/0")); + } + if (!(m_note <= 127)) { + throw kaitai::validation_greater_than_error(127, m_note, m__io, std::string("/types/poly_pressure_event/seq/0")); + } + if (!(m_pressure >= 0)) { + throw kaitai::validation_less_than_error(0, m_pressure, m__io, std::string("/types/poly_pressure_event/seq/1")); + } + if (!(m_pressure <= 127)) { + throw kaitai::validation_greater_than_error(127, m_pressure, m__io, std::string("/types/poly_pressure_event/seq/1")); + } + m__dirty = false; +} + +mpc3000_seq_v3_t::poly_pressure_event_t::~poly_pressure_event_t() {} + +mpc3000_seq_v3_t::program_change_event_t::program_change_event_t(kaitai::kstream* p__io, mpc3000_seq_v3_t::event_t* p__parent, mpc3000_seq_v3_t* p__root) : kaitai::kstruct(p__io) { + m__parent = p__parent; + m__root = p__root; + m__dirty = false; +} + +void mpc3000_seq_v3_t::program_change_event_t::_read() { + m_program = m__io->read_u1(); + if (!(m_program >= 0)) { + throw kaitai::validation_less_than_error(0, m_program, m__io, std::string("/types/program_change_event/seq/0")); + } + if (!(m_program <= 127)) { + throw kaitai::validation_greater_than_error(127, m_program, m__io, std::string("/types/program_change_event/seq/0")); + } + m__dirty = false; +} + +void mpc3000_seq_v3_t::program_change_event_t::_fetch_instances() { +} + +void mpc3000_seq_v3_t::program_change_event_t::_write() { + m__io->write_u1(m_program); + _fetch_instances(); + m__dirty = false; +} + +void mpc3000_seq_v3_t::program_change_event_t::_check() { + if (!(m_program >= 0)) { + throw kaitai::validation_less_than_error(0, m_program, m__io, std::string("/types/program_change_event/seq/0")); + } + if (!(m_program <= 127)) { + throw kaitai::validation_greater_than_error(127, m_program, m__io, std::string("/types/program_change_event/seq/0")); + } + m__dirty = false; +} + +mpc3000_seq_v3_t::program_change_event_t::~program_change_event_t() {} + +mpc3000_seq_v3_t::sequence_header_t::sequence_header_t(kaitai::kstream* p__io, mpc3000_seq_v3_t* p__parent, mpc3000_seq_v3_t* p__root) : kaitai::kstruct(p__io) { + m__parent = p__parent; + m__root = p__root; + m__dirty = false; +} + +void mpc3000_seq_v3_t::sequence_header_t::_read() { + m_sequence_number = m__io->read_u1(); + m_event_stream_length_in_bytes = std::unique_ptr(new u3le_t(m__io, this, m__root)); + m_event_stream_length_in_bytes->_read(); + m_reserved_before_event_stream_sentinel_flag = m__io->read_bytes(1); + if (!(m_reserved_before_event_stream_sentinel_flag == std::string("\x00", 1))) { + throw kaitai::validation_not_equal_error(std::string("\x00", 1), m_reserved_before_event_stream_sentinel_flag, m__io, std::string("/types/sequence_header/seq/2")); + } + m_event_stream_starts_with_ff = static_cast(m__io->read_u1()); + m_reserved_before_name = m__io->read_bytes(3); + if (!(m_reserved_before_name == std::string("\x00\x00\x00", 3))) { + throw kaitai::validation_not_equal_error(std::string("\x00\x00\x00", 3), m_reserved_before_name, m__io, std::string("/types/sequence_header/seq/4")); + } + m_sequence_name = kaitai::kstream::bytes_to_str(m__io->read_bytes(16), "ASCII"); + m_reserved_after_name = m__io->read_bytes(1); + if (!(m_reserved_after_name == std::string("\x00", 1))) { + throw kaitai::validation_not_equal_error(std::string("\x00", 1), m_reserved_after_name, m__io, std::string("/types/sequence_header/seq/6")); + } + m_loop_to_bar = static_cast(m__io->read_bits_int_le(1)); + m_loop_flags_reserved = m__io->read_bits_int_le(7); + if (!(m_loop_flags_reserved == 4)) { + throw kaitai::validation_not_equal_error(4, m_loop_flags_reserved, m__io, std::string("/types/sequence_header/seq/8")); + } + m_loop_to_bar_number = m__io->read_u2le(); + m_number_of_bars = m__io->read_u2le(); + m_length_in_ticks = m__io->read_u4le(); + m_tempo = m__io->read_u2le(); + m__dirty = false; +} + +void mpc3000_seq_v3_t::sequence_header_t::_fetch_instances() { + m_event_stream_length_in_bytes.get()->_fetch_instances(); +} + +void mpc3000_seq_v3_t::sequence_header_t::_write() { + m__io->write_u1(m_sequence_number); + if (m_event_stream_length_in_bytes.get() == nullptr) { + throw std::runtime_error("/types/sequence_header/seq/1: nested object is not set"); + } + m_event_stream_length_in_bytes.get()->_set_io(m__io); + m_event_stream_length_in_bytes.get()->_write(); + m__io->write_bytes(m_reserved_before_event_stream_sentinel_flag); + m__io->write_u1(static_cast(m_event_stream_starts_with_ff)); + m__io->write_bytes(m_reserved_before_name); + m__io->write_bytes(m_sequence_name); + m__io->write_bytes(m_reserved_after_name); + m__io->write_bits_int_le(1, static_cast(m_loop_to_bar)); + m__io->write_bits_int_le(7, m_loop_flags_reserved); + m__io->write_u2le(m_loop_to_bar_number); + m__io->write_u2le(m_number_of_bars); + m__io->write_u4le(m_length_in_ticks); + m__io->write_u2le(m_tempo); + _fetch_instances(); + m__dirty = false; +} + +void mpc3000_seq_v3_t::sequence_header_t::_check() { + if (m_event_stream_length_in_bytes.get() == nullptr) { + throw std::runtime_error("/types/sequence_header/seq/1: nested object is not set"); + } + m_event_stream_length_in_bytes.get()->_set_io(m__io); + m_event_stream_length_in_bytes.get()->_check(); + if (m_reserved_before_event_stream_sentinel_flag.size() != static_cast(1)) { + throw std::runtime_error("/types/sequence_header/seq/2: size mismatch"); + } + if (!(m_reserved_before_event_stream_sentinel_flag == std::string("\x00", 1))) { + throw kaitai::validation_not_equal_error(std::string("\x00", 1), m_reserved_before_event_stream_sentinel_flag, m__io, std::string("/types/sequence_header/seq/2")); + } + if (m_reserved_before_name.size() != static_cast(3)) { + throw std::runtime_error("/types/sequence_header/seq/4: size mismatch"); + } + if (!(m_reserved_before_name == std::string("\x00\x00\x00", 3))) { + throw kaitai::validation_not_equal_error(std::string("\x00\x00\x00", 3), m_reserved_before_name, m__io, std::string("/types/sequence_header/seq/4")); + } + if (m_sequence_name.size() != static_cast(16)) { + throw std::runtime_error("/types/sequence_header/seq/5: size mismatch"); + } + if (m_reserved_after_name.size() != static_cast(1)) { + throw std::runtime_error("/types/sequence_header/seq/6: size mismatch"); + } + if (!(m_reserved_after_name == std::string("\x00", 1))) { + throw kaitai::validation_not_equal_error(std::string("\x00", 1), m_reserved_after_name, m__io, std::string("/types/sequence_header/seq/6")); + } + if (!(m_loop_flags_reserved == 4)) { + throw kaitai::validation_not_equal_error(4, m_loop_flags_reserved, m__io, std::string("/types/sequence_header/seq/8")); + } + m__dirty = false; +} + +mpc3000_seq_v3_t::sequence_header_t::~sequence_header_t() {} + +mpc3000_seq_v3_t::smpte_offset_t::smpte_offset_t(kaitai::kstream* p__io, mpc3000_seq_v3_t* p__parent, mpc3000_seq_v3_t* p__root) : kaitai::kstruct(p__io) { + m__parent = p__parent; + m__root = p__root; + m__dirty = false; +} + +void mpc3000_seq_v3_t::smpte_offset_t::_read() { + m_hundredth_frames = m__io->read_u1(); + if (!(m_hundredth_frames >= 0)) { + throw kaitai::validation_less_than_error(0, m_hundredth_frames, m__io, std::string("/types/smpte_offset/seq/0")); + } + if (!(m_hundredth_frames <= 99)) { + throw kaitai::validation_greater_than_error(99, m_hundredth_frames, m__io, std::string("/types/smpte_offset/seq/0")); + } + m_frames = m__io->read_u1(); + if (!(m_frames >= 0)) { + throw kaitai::validation_less_than_error(0, m_frames, m__io, std::string("/types/smpte_offset/seq/1")); + } + if (!(m_frames <= 29)) { + throw kaitai::validation_greater_than_error(29, m_frames, m__io, std::string("/types/smpte_offset/seq/1")); + } + m_seconds = m__io->read_u1(); + if (!(m_seconds >= 0)) { + throw kaitai::validation_less_than_error(0, m_seconds, m__io, std::string("/types/smpte_offset/seq/2")); + } + if (!(m_seconds <= 59)) { + throw kaitai::validation_greater_than_error(59, m_seconds, m__io, std::string("/types/smpte_offset/seq/2")); + } + m_minutes = m__io->read_u1(); + if (!(m_minutes >= 0)) { + throw kaitai::validation_less_than_error(0, m_minutes, m__io, std::string("/types/smpte_offset/seq/3")); + } + if (!(m_minutes <= 59)) { + throw kaitai::validation_greater_than_error(59, m_minutes, m__io, std::string("/types/smpte_offset/seq/3")); + } + m_hours = m__io->read_u1(); + if (!(m_hours >= 0)) { + throw kaitai::validation_less_than_error(0, m_hours, m__io, std::string("/types/smpte_offset/seq/4")); + } + if (!(m_hours <= 23)) { + throw kaitai::validation_greater_than_error(23, m_hours, m__io, std::string("/types/smpte_offset/seq/4")); + } + m__dirty = false; +} + +void mpc3000_seq_v3_t::smpte_offset_t::_fetch_instances() { +} + +void mpc3000_seq_v3_t::smpte_offset_t::_write() { + m__io->write_u1(m_hundredth_frames); + m__io->write_u1(m_frames); + m__io->write_u1(m_seconds); + m__io->write_u1(m_minutes); + m__io->write_u1(m_hours); + _fetch_instances(); + m__dirty = false; +} + +void mpc3000_seq_v3_t::smpte_offset_t::_check() { + if (!(m_hundredth_frames >= 0)) { + throw kaitai::validation_less_than_error(0, m_hundredth_frames, m__io, std::string("/types/smpte_offset/seq/0")); + } + if (!(m_hundredth_frames <= 99)) { + throw kaitai::validation_greater_than_error(99, m_hundredth_frames, m__io, std::string("/types/smpte_offset/seq/0")); + } + if (!(m_frames >= 0)) { + throw kaitai::validation_less_than_error(0, m_frames, m__io, std::string("/types/smpte_offset/seq/1")); + } + if (!(m_frames <= 29)) { + throw kaitai::validation_greater_than_error(29, m_frames, m__io, std::string("/types/smpte_offset/seq/1")); + } + if (!(m_seconds >= 0)) { + throw kaitai::validation_less_than_error(0, m_seconds, m__io, std::string("/types/smpte_offset/seq/2")); + } + if (!(m_seconds <= 59)) { + throw kaitai::validation_greater_than_error(59, m_seconds, m__io, std::string("/types/smpte_offset/seq/2")); + } + if (!(m_minutes >= 0)) { + throw kaitai::validation_less_than_error(0, m_minutes, m__io, std::string("/types/smpte_offset/seq/3")); + } + if (!(m_minutes <= 59)) { + throw kaitai::validation_greater_than_error(59, m_minutes, m__io, std::string("/types/smpte_offset/seq/3")); + } + if (!(m_hours >= 0)) { + throw kaitai::validation_less_than_error(0, m_hours, m__io, std::string("/types/smpte_offset/seq/4")); + } + if (!(m_hours <= 23)) { + throw kaitai::validation_greater_than_error(23, m_hours, m__io, std::string("/types/smpte_offset/seq/4")); + } + m__dirty = false; +} + +mpc3000_seq_v3_t::smpte_offset_t::~smpte_offset_t() {} + +mpc3000_seq_v3_t::tempo_change_t::tempo_change_t(kaitai::kstream* p__io, mpc3000_seq_v3_t* p__parent, mpc3000_seq_v3_t* p__root) : kaitai::kstruct(p__io) { + m__parent = p__parent; + m__root = p__root; + m__dirty = false; + f_factor_percentage = false; +} + +void mpc3000_seq_v3_t::tempo_change_t::_read() { + m_ticks_from_sequence_start = m__io->read_u4le(); + m_factor1 = m__io->read_bits_int_le(12); + m_factor2 = m__io->read_bits_int_le(4); + m__dirty = false; +} + +void mpc3000_seq_v3_t::tempo_change_t::_fetch_instances() { +} + +void mpc3000_seq_v3_t::tempo_change_t::_write() { + m__io->write_u4le(m_ticks_from_sequence_start); + m__io->write_bits_int_le(12, m_factor1); + m__io->write_bits_int_le(4, m_factor2); + _fetch_instances(); + m__dirty = false; +} + +void mpc3000_seq_v3_t::tempo_change_t::_check() { + m__dirty = false; +} + +mpc3000_seq_v3_t::tempo_change_t::~tempo_change_t() {} + +double mpc3000_seq_v3_t::tempo_change_t::factor_percentage() { + if (f_factor_percentage) + return m_factor_percentage; + f_factor_percentage = true; + m_factor_percentage = (factor1() / 4096.0) * 100 + factor2() * 100; + return m_factor_percentage; +} + +mpc3000_seq_v3_t::track_header_t::track_header_t(kaitai::kstream* p__io, mpc3000_seq_v3_t* p__parent, mpc3000_seq_v3_t* p__root) : kaitai::kstruct(p__io) { + m__parent = p__parent; + m__root = p__root; + m__dirty = false; + n_unused_track_header_padding = true; + n_user_track_number = true; + n_track_mute = true; + n_track_in_use = true; + n_drum_track = true; + n_track_flags_reserved = true; + n_primary_port_channel_assignment = true; + n_secondary_port_channel_assignment = true; + n_track_name = true; + n_track_volume = true; + n_program_change_number = true; + n_reserved_after_program_change = true; +} + +void mpc3000_seq_v3_t::track_header_t::_read() { + m_absolute_recorded_track_number = m__io->read_s1(); + n_unused_track_header_padding = true; + if (absolute_recorded_track_number() == -1) { + n_unused_track_header_padding = false; + m_unused_track_header_padding = m__io->read_bytes(23); + if (!(m_unused_track_header_padding == std::string("\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", 23))) { + throw kaitai::validation_not_equal_error(std::string("\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", 23), m_unused_track_header_padding, m__io, std::string("/types/track_header/seq/1")); + } + } + n_user_track_number = true; + if (absolute_recorded_track_number() != -1) { + n_user_track_number = false; + m_user_track_number = m__io->read_u1(); + } + n_track_mute = true; + if (absolute_recorded_track_number() != -1) { + n_track_mute = false; + m_track_mute = m__io->read_bits_int_le(1); + } + n_track_in_use = true; + if (absolute_recorded_track_number() != -1) { + n_track_in_use = false; + m_track_in_use = m__io->read_bits_int_le(1); + } + n_drum_track = true; + if (absolute_recorded_track_number() != -1) { + n_drum_track = false; + m_drum_track = m__io->read_bits_int_le(1); + } + n_track_flags_reserved = true; + if (absolute_recorded_track_number() != -1) { + n_track_flags_reserved = false; + m_track_flags_reserved = m__io->read_bits_int_le(5); + if (!(m_track_flags_reserved == 0)) { + throw kaitai::validation_not_equal_error(0, m_track_flags_reserved, m__io, std::string("/types/track_header/seq/6")); + } + } + n_primary_port_channel_assignment = true; + if (absolute_recorded_track_number() != -1) { + n_primary_port_channel_assignment = false; + m_primary_port_channel_assignment = m__io->read_u1(); + } + n_secondary_port_channel_assignment = true; + if (absolute_recorded_track_number() != -1) { + n_secondary_port_channel_assignment = false; + m_secondary_port_channel_assignment = m__io->read_s1(); + } + n_track_name = true; + if (absolute_recorded_track_number() != -1) { + n_track_name = false; + m_track_name = kaitai::kstream::bytes_to_str(m__io->read_bytes(16), "ASCII"); + } + n_track_volume = true; + if (absolute_recorded_track_number() != -1) { + n_track_volume = false; + m_track_volume = m__io->read_u1(); + if (!(m_track_volume >= 1)) { + throw kaitai::validation_less_than_error(1, m_track_volume, m__io, std::string("/types/track_header/seq/10")); + } + if (!(m_track_volume <= 200)) { + throw kaitai::validation_greater_than_error(200, m_track_volume, m__io, std::string("/types/track_header/seq/10")); + } + } + n_program_change_number = true; + if (absolute_recorded_track_number() != -1) { + n_program_change_number = false; + m_program_change_number = m__io->read_u1(); + } + n_reserved_after_program_change = true; + if (absolute_recorded_track_number() != -1) { + n_reserved_after_program_change = false; + m_reserved_after_program_change = m__io->read_bytes(1); + if (!(m_reserved_after_program_change == std::string("\x00", 1))) { + throw kaitai::validation_not_equal_error(std::string("\x00", 1), m_reserved_after_program_change, m__io, std::string("/types/track_header/seq/12")); + } + } + m__dirty = false; +} + +void mpc3000_seq_v3_t::track_header_t::_fetch_instances() { + n_unused_track_header_padding = true; + if (absolute_recorded_track_number() == -1) { + n_unused_track_header_padding = false; + } + n_user_track_number = true; + if (absolute_recorded_track_number() != -1) { + n_user_track_number = false; + } + n_track_mute = true; + if (absolute_recorded_track_number() != -1) { + n_track_mute = false; + } + n_track_in_use = true; + if (absolute_recorded_track_number() != -1) { + n_track_in_use = false; + } + n_drum_track = true; + if (absolute_recorded_track_number() != -1) { + n_drum_track = false; + } + n_track_flags_reserved = true; + if (absolute_recorded_track_number() != -1) { + n_track_flags_reserved = false; + } + n_primary_port_channel_assignment = true; + if (absolute_recorded_track_number() != -1) { + n_primary_port_channel_assignment = false; + } + n_secondary_port_channel_assignment = true; + if (absolute_recorded_track_number() != -1) { + n_secondary_port_channel_assignment = false; + } + n_track_name = true; + if (absolute_recorded_track_number() != -1) { + n_track_name = false; + } + n_track_volume = true; + if (absolute_recorded_track_number() != -1) { + n_track_volume = false; + } + n_program_change_number = true; + if (absolute_recorded_track_number() != -1) { + n_program_change_number = false; + } + n_reserved_after_program_change = true; + if (absolute_recorded_track_number() != -1) { + n_reserved_after_program_change = false; + } +} + +void mpc3000_seq_v3_t::track_header_t::_write() { + m__io->write_s1(m_absolute_recorded_track_number); + if (absolute_recorded_track_number() == -1) { + m__io->write_bytes(m_unused_track_header_padding); + } + if (absolute_recorded_track_number() != -1) { + m__io->write_u1(m_user_track_number); + } + if (absolute_recorded_track_number() != -1) { + m__io->write_bits_int_le(1, ((m_track_mute) ? 1 : 0)); + } + if (absolute_recorded_track_number() != -1) { + m__io->write_bits_int_le(1, ((m_track_in_use) ? 1 : 0)); + } + if (absolute_recorded_track_number() != -1) { + m__io->write_bits_int_le(1, ((m_drum_track) ? 1 : 0)); + } + if (absolute_recorded_track_number() != -1) { + m__io->write_bits_int_le(5, m_track_flags_reserved); + } + if (absolute_recorded_track_number() != -1) { + m__io->write_u1(m_primary_port_channel_assignment); + } + if (absolute_recorded_track_number() != -1) { + m__io->write_s1(m_secondary_port_channel_assignment); + } + if (absolute_recorded_track_number() != -1) { + m__io->write_bytes(m_track_name); + } + if (absolute_recorded_track_number() != -1) { + m__io->write_u1(m_track_volume); + } + if (absolute_recorded_track_number() != -1) { + m__io->write_u1(m_program_change_number); + } + if (absolute_recorded_track_number() != -1) { + m__io->write_bytes(m_reserved_after_program_change); + } + _fetch_instances(); + m__dirty = false; +} + +void mpc3000_seq_v3_t::track_header_t::_check() { + if (absolute_recorded_track_number() == -1) { + if (!(!n_unused_track_header_padding)) { + throw std::runtime_error("/types/track_header/seq/1: conditional field is not set"); + } + if (m_unused_track_header_padding.size() != static_cast(23)) { + throw std::runtime_error("/types/track_header/seq/1: size mismatch"); + } + if (!(m_unused_track_header_padding == std::string("\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", 23))) { + throw kaitai::validation_not_equal_error(std::string("\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", 23), m_unused_track_header_padding, m__io, std::string("/types/track_header/seq/1")); + } + } else { + if (!n_unused_track_header_padding) { + throw std::runtime_error("/types/track_header/seq/1: conditional field should be absent"); + } + } + if (absolute_recorded_track_number() != -1) { + if (!(!n_user_track_number)) { + throw std::runtime_error("/types/track_header/seq/2: conditional field is not set"); + } + } else { + if (!n_user_track_number) { + throw std::runtime_error("/types/track_header/seq/2: conditional field should be absent"); + } + } + if (absolute_recorded_track_number() != -1) { + if (!(!n_track_mute)) { + throw std::runtime_error("/types/track_header/seq/3: conditional field is not set"); + } + } else { + if (!n_track_mute) { + throw std::runtime_error("/types/track_header/seq/3: conditional field should be absent"); + } + } + if (absolute_recorded_track_number() != -1) { + if (!(!n_track_in_use)) { + throw std::runtime_error("/types/track_header/seq/4: conditional field is not set"); + } + } else { + if (!n_track_in_use) { + throw std::runtime_error("/types/track_header/seq/4: conditional field should be absent"); + } + } + if (absolute_recorded_track_number() != -1) { + if (!(!n_drum_track)) { + throw std::runtime_error("/types/track_header/seq/5: conditional field is not set"); + } + } else { + if (!n_drum_track) { + throw std::runtime_error("/types/track_header/seq/5: conditional field should be absent"); + } + } + if (absolute_recorded_track_number() != -1) { + if (!(!n_track_flags_reserved)) { + throw std::runtime_error("/types/track_header/seq/6: conditional field is not set"); + } + if (!(m_track_flags_reserved == 0)) { + throw kaitai::validation_not_equal_error(0, m_track_flags_reserved, m__io, std::string("/types/track_header/seq/6")); + } + } else { + if (!n_track_flags_reserved) { + throw std::runtime_error("/types/track_header/seq/6: conditional field should be absent"); + } + } + if (absolute_recorded_track_number() != -1) { + if (!(!n_primary_port_channel_assignment)) { + throw std::runtime_error("/types/track_header/seq/7: conditional field is not set"); + } + } else { + if (!n_primary_port_channel_assignment) { + throw std::runtime_error("/types/track_header/seq/7: conditional field should be absent"); + } + } + if (absolute_recorded_track_number() != -1) { + if (!(!n_secondary_port_channel_assignment)) { + throw std::runtime_error("/types/track_header/seq/8: conditional field is not set"); + } + } else { + if (!n_secondary_port_channel_assignment) { + throw std::runtime_error("/types/track_header/seq/8: conditional field should be absent"); + } + } + if (absolute_recorded_track_number() != -1) { + if (!(!n_track_name)) { + throw std::runtime_error("/types/track_header/seq/9: conditional field is not set"); + } + if (m_track_name.size() != static_cast(16)) { + throw std::runtime_error("/types/track_header/seq/9: size mismatch"); + } + } else { + if (!n_track_name) { + throw std::runtime_error("/types/track_header/seq/9: conditional field should be absent"); + } + } + if (absolute_recorded_track_number() != -1) { + if (!(!n_track_volume)) { + throw std::runtime_error("/types/track_header/seq/10: conditional field is not set"); + } + if (!(m_track_volume >= 1)) { + throw kaitai::validation_less_than_error(1, m_track_volume, m__io, std::string("/types/track_header/seq/10")); + } + if (!(m_track_volume <= 200)) { + throw kaitai::validation_greater_than_error(200, m_track_volume, m__io, std::string("/types/track_header/seq/10")); + } + } else { + if (!n_track_volume) { + throw std::runtime_error("/types/track_header/seq/10: conditional field should be absent"); + } + } + if (absolute_recorded_track_number() != -1) { + if (!(!n_program_change_number)) { + throw std::runtime_error("/types/track_header/seq/11: conditional field is not set"); + } + } else { + if (!n_program_change_number) { + throw std::runtime_error("/types/track_header/seq/11: conditional field should be absent"); + } + } + if (absolute_recorded_track_number() != -1) { + if (!(!n_reserved_after_program_change)) { + throw std::runtime_error("/types/track_header/seq/12: conditional field is not set"); + } + if (m_reserved_after_program_change.size() != static_cast(1)) { + throw std::runtime_error("/types/track_header/seq/12: size mismatch"); + } + if (!(m_reserved_after_program_change == std::string("\x00", 1))) { + throw kaitai::validation_not_equal_error(std::string("\x00", 1), m_reserved_after_program_change, m__io, std::string("/types/track_header/seq/12")); + } + } else { + if (!n_reserved_after_program_change) { + throw std::runtime_error("/types/track_header/seq/12: conditional field should be absent"); + } + } + m__dirty = false; +} + +mpc3000_seq_v3_t::track_header_t::~track_header_t() {} + +mpc3000_seq_v3_t::tune_request_event_t::tune_request_event_t(kaitai::kstream* p__io, mpc3000_seq_v3_t::event_t* p__parent, mpc3000_seq_v3_t* p__root) : kaitai::kstruct(p__io) { + m__parent = p__parent; + m__root = p__root; + m__dirty = false; +} + +void mpc3000_seq_v3_t::tune_request_event_t::_read() { + m_tune_request_event = m__io->read_bytes(0); + m__dirty = false; +} + +void mpc3000_seq_v3_t::tune_request_event_t::_fetch_instances() { +} + +void mpc3000_seq_v3_t::tune_request_event_t::_write() { + m__io->write_bytes(m_tune_request_event); + _fetch_instances(); + m__dirty = false; +} + +void mpc3000_seq_v3_t::tune_request_event_t::_check() { + if (m_tune_request_event.size() != static_cast(0)) { + throw std::runtime_error("/types/tune_request_event/seq/0: size mismatch"); + } + m__dirty = false; +} + +mpc3000_seq_v3_t::tune_request_event_t::~tune_request_event_t() {} + +mpc3000_seq_v3_t::u3le_t::u3le_t(kaitai::kstream* p__io, mpc3000_seq_v3_t::sequence_header_t* p__parent, mpc3000_seq_v3_t* p__root) : kaitai::kstruct(p__io) { + m__parent = p__parent; + m__root = p__root; + m__dirty = false; + f_value = false; +} + +void mpc3000_seq_v3_t::u3le_t::_read() { + m_b12 = m__io->read_u2le(); + m_b3 = m__io->read_u1(); + m__dirty = false; +} + +void mpc3000_seq_v3_t::u3le_t::_fetch_instances() { +} + +void mpc3000_seq_v3_t::u3le_t::_write() { + m__io->write_u2le(m_b12); + m__io->write_u1(m_b3); + _fetch_instances(); + m__dirty = false; +} + +void mpc3000_seq_v3_t::u3le_t::_check() { + m__dirty = false; +} + +mpc3000_seq_v3_t::u3le_t::~u3le_t() {} + +int32_t mpc3000_seq_v3_t::u3le_t::value() { + if (f_value) + return m_value; + f_value = true; + m_value = b12() | b3() << 16; + return m_value; +} diff --git a/src/main/file/kaitai/generated/mpc3000_seq_v3.h b/src/main/file/kaitai/generated/mpc3000_seq_v3.h new file mode 100644 index 000000000..41134e48b --- /dev/null +++ b/src/main/file/kaitai/generated/mpc3000_seq_v3.h @@ -0,0 +1,1163 @@ +#pragma once + +// This is a generated file! Please edit source .ksy file and use kaitai-struct-compiler to rebuild + +class mpc3000_seq_v3_t; + +#include "kaitai/kaitaistruct.h" +#include +#include +#include +#include + +#if KAITAI_STRUCT_VERSION < 11000L +#error "Incompatible Kaitai Struct C++/STL API: version 0.11 or later is required" +#endif + +class mpc3000_seq_v3_t : public kaitai::kstruct { + +public: + class bar_event_t; + class ch_pressure_event_t; + class control_change_event_t; + class delays_t; + class delta_time_event_t; + class event_t; + class mixer_t; + class mixer_event_t; + class note_event_t; + class pitch_bend_event_t; + class poly_pressure_event_t; + class program_change_event_t; + class sequence_header_t; + class smpte_offset_t; + class tempo_change_t; + class track_header_t; + class tune_request_event_t; + class u3le_t; + + enum controller_t { + CONTROLLER_BANK_SEL_MSB = 0, + CONTROLLER_MOD_WHEEL = 1, + CONTROLLER_BREATH_CONT = 2, + CONTROLLER_CC3 = 3, + CONTROLLER_FOOT_CONTROL = 4, + CONTROLLER_PORTA_TIME = 5, + CONTROLLER_DATA_ENTRY = 6, + CONTROLLER_MAIN_VOLUME = 7, + CONTROLLER_BALANCE = 8, + CONTROLLER_CC9 = 9, + CONTROLLER_PAN = 10, + CONTROLLER_EXPRESSION = 11, + CONTROLLER_EFFECT_1 = 12, + CONTROLLER_EFFECT_2 = 13, + CONTROLLER_CC14 = 14, + CONTROLLER_CC15 = 15, + CONTROLLER_GEN_PUR_1 = 16, + CONTROLLER_GEN_PUR_2 = 17, + CONTROLLER_GEN_PUR_3 = 18, + CONTROLLER_GEN_PUR_4 = 19, + CONTROLLER_CC20 = 20, + CONTROLLER_CC21 = 21, + CONTROLLER_CC22 = 22, + CONTROLLER_CC23 = 23, + CONTROLLER_CC24 = 24, + CONTROLLER_CC25 = 25, + CONTROLLER_CC26 = 26, + CONTROLLER_CC27 = 27, + CONTROLLER_CC28 = 28, + CONTROLLER_CC29 = 29, + CONTROLLER_CC30 = 30, + CONTROLLER_CC31 = 31, + CONTROLLER_BANK_SEL_LSB = 32, + CONTROLLER_MOD_WHEL_LSB = 33, + CONTROLLER_BREATH_LSB = 34, + CONTROLLER_CC35 = 35, + CONTROLLER_FOOT_CNT_LSB = 36, + CONTROLLER_PORT_TIME_LS = 37, + CONTROLLER_DATA_ENT_LSB = 38, + CONTROLLER_MAIN_VOL_LSB = 39, + CONTROLLER_BALANCE_LSB = 40, + CONTROLLER_CC41 = 41, + CONTROLLER_PAN_LSB = 42, + CONTROLLER_EXPRESS_LSB = 43, + CONTROLLER_EFFECT_1_LSB = 44, + CONTROLLER_EFFECT_2_MSB = 45, + CONTROLLER_CC46 = 46, + CONTROLLER_CC47 = 47, + CONTROLLER_GEN_PUR_1_LS = 48, + CONTROLLER_GEN_PUR_2_LS = 49, + CONTROLLER_GEN_PUR_3_LS = 50, + CONTROLLER_GEN_PUR_4_LS = 51, + CONTROLLER_CC52 = 52, + CONTROLLER_CC53 = 53, + CONTROLLER_CC54 = 54, + CONTROLLER_CC55 = 55, + CONTROLLER_CC56 = 56, + CONTROLLER_CC57 = 57, + CONTROLLER_CC58 = 58, + CONTROLLER_CC59 = 59, + CONTROLLER_CC60 = 60, + CONTROLLER_CC61 = 61, + CONTROLLER_CC62 = 62, + CONTROLLER_CC63 = 63, + CONTROLLER_SUSTAIN_PDL = 64, + CONTROLLER_PORTA_PEDAL = 65, + CONTROLLER_SOSTENUTO = 66, + CONTROLLER_SOFT_PEDAL = 67, + CONTROLLER_LEGATO_FT_SW = 68, + CONTROLLER_HOLD_2 = 69, + CONTROLLER_SOUND_VARI = 70, + CONTROLLER_TIMBER_HARMO = 71, + CONTROLLER_RELEASE_TIME = 72, + CONTROLLER_ATTACK_TIME = 73, + CONTROLLER_BRIGHTNESS = 74, + CONTROLLER_SOUND_CONT_6 = 75, + CONTROLLER_SOUND_CONT_7 = 76, + CONTROLLER_SOUND_CONT_8 = 77, + CONTROLLER_SOUND_CONT_9 = 78, + CONTROLLER_SOUND_CONT10 = 79, + CONTROLLER_GEN_PUR_5 = 80, + CONTROLLER_GEN_PUR_6 = 81, + CONTROLLER_GEN_PUR_7 = 82, + CONTROLLER_GEN_PUR_8 = 83, + CONTROLLER_PORTA_CNTRL = 84, + CONTROLLER_CC85 = 85, + CONTROLLER_CC86 = 86, + CONTROLLER_CC87 = 87, + CONTROLLER_CC88 = 88, + CONTROLLER_CC89 = 89, + CONTROLLER_CC90 = 90, + CONTROLLER_EXT_EFF_DPTH = 91, + CONTROLLER_TREMOLO_DPTH = 92, + CONTROLLER_CHORUS_DEPTH = 93, + CONTROLLER_DETUNE_DEPTH = 94, + CONTROLLER_PHASER_DEPTH = 95, + CONTROLLER_DATA_INCRE = 96, + CONTROLLER_DATA_DECRE = 97, + CONTROLLER_NRPN_LSB = 98, + CONTROLLER_NRPN_MSB = 99, + CONTROLLER_RPN_LSB = 100, + CONTROLLER_RPN_MSB = 101, + CONTROLLER_CC102 = 102, + CONTROLLER_CC103 = 103, + CONTROLLER_CC104 = 104, + CONTROLLER_CC105 = 105, + CONTROLLER_CC106 = 106, + CONTROLLER_CC107 = 107, + CONTROLLER_CC108 = 108, + CONTROLLER_CC109 = 109, + CONTROLLER_CC110 = 110, + CONTROLLER_CC111 = 111, + CONTROLLER_CC112 = 112, + CONTROLLER_CC113 = 113, + CONTROLLER_CC114 = 114, + CONTROLLER_CC115 = 115, + CONTROLLER_CC116 = 116, + CONTROLLER_CC117 = 117, + CONTROLLER_CC118 = 118, + CONTROLLER_CC119 = 119, + CONTROLLER_ALL_SND_OFF = 120, + CONTROLLER_RESET_CONTRL = 121, + CONTROLLER_LOCAL_ON_OFF = 122, + CONTROLLER_ALL_NOTE_OFF = 123, + CONTROLLER_OMNI_OFF = 124, + CONTROLLER_OMNI_ON = 125, + CONTROLLER_MONO_MODE_ON = 126, + CONTROLLER_POLY_MODE_ON = 127 + }; + static bool _is_defined_controller_t(controller_t v); + +private: + static const std::set _values_controller_t; + +public: + + enum individual_out_t { + INDIVIDUAL_OUT_UNASSIGNED = 0, + INDIVIDUAL_OUT_OUT_1 = 1, + INDIVIDUAL_OUT_OUT_2 = 2, + INDIVIDUAL_OUT_OUT_3 = 3, + INDIVIDUAL_OUT_OUT_4 = 4, + INDIVIDUAL_OUT_OUT_5 = 5, + INDIVIDUAL_OUT_OUT_6 = 6, + INDIVIDUAL_OUT_OUT_7 = 7, + INDIVIDUAL_OUT_OUT_8 = 8, + INDIVIDUAL_OUT_INTERNAL_EFFECTS_GENERATOR = 9 + }; + static bool _is_defined_individual_out_t(individual_out_t v); + +private: + static const std::set _values_individual_out_t; + +public: + + enum mixer_event_param_t { + MIXER_EVENT_PARAM_STEREO_LEVEL = 1, + MIXER_EVENT_PARAM_STEREO_PAN = 2, + MIXER_EVENT_PARAM_FXSEND_LEVEL = 3, + MIXER_EVENT_PARAM_INDIV_LEVEL = 5 + }; + static bool _is_defined_mixer_event_param_t(mixer_event_param_t v); + +private: + static const std::set _values_mixer_event_param_t; + +public: + + enum no_yes_t { + NO_YES_FALSE = 0, + NO_YES_TRUE = 1 + }; + static bool _is_defined_no_yes_t(no_yes_t v); + +private: + static const std::set _values_no_yes_t; + +public: + + enum off_on_t { + OFF_ON_FALSE = 0, + OFF_ON_TRUE = 1 + }; + static bool _is_defined_off_on_t(off_on_t v); + +private: + static const std::set _values_off_on_t; + +public: + +private: + bool m__dirty; + +public: + + mpc3000_seq_v3_t(kaitai::kstream* p__io, kaitai::kstruct* p__parent = nullptr, mpc3000_seq_v3_t* p__root = nullptr); + void _read(); + void _fetch_instances(); + void _write(); + void _check(); + ~mpc3000_seq_v3_t(); + + class bar_event_t : public kaitai::kstruct { + + public: + + private: + bool m__dirty; + + public: + + bar_event_t(kaitai::kstream* p__io, mpc3000_seq_v3_t::event_t* p__parent = nullptr, mpc3000_seq_v3_t* p__root = nullptr); + void _read(); + void _fetch_instances(); + void _write(); + void _check(); + ~bar_event_t(); + int32_t bar_number(); + void _invalidate_bar_number() { f_bar_number = false; } + uint8_t bar_number1() const { return m_bar_number1; } + void set_bar_number1(uint8_t _v) { m__dirty = true; f_bar_number = false; m_bar_number1 = std::move(_v); } + uint8_t bar_number2() const { return m_bar_number2; } + void set_bar_number2(uint8_t _v) { m__dirty = true; f_bar_number = false; m_bar_number2 = std::move(_v); } + uint8_t numerator() const { return m_numerator; } + void set_numerator(uint8_t _v) { m__dirty = true; f_bar_number = false; m_numerator = std::move(_v); } + uint8_t denominator() const { return m_denominator; } + void set_denominator(uint8_t _v) { m__dirty = true; f_bar_number = false; m_denominator = std::move(_v); } + mpc3000_seq_v3_t* _root() const { return m__root; } + void set__root(mpc3000_seq_v3_t* _v) { m__dirty = true; f_bar_number = false; m__root = std::move(_v); } + kaitai::kstruct* _parent() const { return m__parent; } + void set__parent(mpc3000_seq_v3_t::event_t* _v) { m__dirty = true; f_bar_number = false; m__parent = std::move(_v); } + + private: + bool f_bar_number; + int32_t m_bar_number; + uint8_t m_bar_number1; + uint8_t m_bar_number2; + uint8_t m_numerator; + uint8_t m_denominator; + mpc3000_seq_v3_t* m__root; + mpc3000_seq_v3_t::event_t* m__parent; + }; + + class ch_pressure_event_t : public kaitai::kstruct { + + public: + + private: + bool m__dirty; + + public: + + ch_pressure_event_t(kaitai::kstream* p__io, mpc3000_seq_v3_t::event_t* p__parent = nullptr, mpc3000_seq_v3_t* p__root = nullptr); + void _read(); + void _fetch_instances(); + void _write(); + void _check(); + ~ch_pressure_event_t(); + uint8_t pressure() const { return m_pressure; } + void set_pressure(uint8_t _v) { m__dirty = true; m_pressure = std::move(_v); } + mpc3000_seq_v3_t* _root() const { return m__root; } + void set__root(mpc3000_seq_v3_t* _v) { m__dirty = true; m__root = std::move(_v); } + kaitai::kstruct* _parent() const { return m__parent; } + void set__parent(mpc3000_seq_v3_t::event_t* _v) { m__dirty = true; m__parent = std::move(_v); } + + private: + uint8_t m_pressure; + mpc3000_seq_v3_t* m__root; + mpc3000_seq_v3_t::event_t* m__parent; + }; + + class control_change_event_t : public kaitai::kstruct { + + public: + + private: + bool m__dirty; + + public: + + control_change_event_t(kaitai::kstream* p__io, mpc3000_seq_v3_t::event_t* p__parent = nullptr, mpc3000_seq_v3_t* p__root = nullptr); + void _read(); + void _fetch_instances(); + void _write(); + void _check(); + ~control_change_event_t(); + controller_t controller() const { return m_controller; } + void set_controller(controller_t _v) { m__dirty = true; m_controller = std::move(_v); } + uint8_t value() const { return m_value; } + void set_value(uint8_t _v) { m__dirty = true; m_value = std::move(_v); } + mpc3000_seq_v3_t* _root() const { return m__root; } + void set__root(mpc3000_seq_v3_t* _v) { m__dirty = true; m__root = std::move(_v); } + kaitai::kstruct* _parent() const { return m__parent; } + void set__parent(mpc3000_seq_v3_t::event_t* _v) { m__dirty = true; m__parent = std::move(_v); } + + private: + controller_t m_controller; + uint8_t m_value; + mpc3000_seq_v3_t* m__root; + mpc3000_seq_v3_t::event_t* m__parent; + }; + + class delays_t : public kaitai::kstruct { + + public: + + private: + bool m__dirty; + + public: + + delays_t(kaitai::kstream* p__io, mpc3000_seq_v3_t* p__parent = nullptr, mpc3000_seq_v3_t* p__root = nullptr); + void _read(); + void _fetch_instances(); + void _write(); + void _check(); + ~delays_t(); + uint8_t volume1() const { return m_volume1; } + void set_volume1(uint8_t _v) { m__dirty = true; m_volume1 = std::move(_v); } + uint8_t volume2() const { return m_volume2; } + void set_volume2(uint8_t _v) { m__dirty = true; m_volume2 = std::move(_v); } + uint8_t volume3() const { return m_volume3; } + void set_volume3(uint8_t _v) { m__dirty = true; m_volume3 = std::move(_v); } + uint8_t pan1() const { return m_pan1; } + void set_pan1(uint8_t _v) { m__dirty = true; m_pan1 = std::move(_v); } + uint8_t pan2() const { return m_pan2; } + void set_pan2(uint8_t _v) { m__dirty = true; m_pan2 = std::move(_v); } + uint8_t pan3() const { return m_pan3; } + void set_pan3(uint8_t _v) { m__dirty = true; m_pan3 = std::move(_v); } + uint16_t time1() const { return m_time1; } + void set_time1(uint16_t _v) { m__dirty = true; m_time1 = std::move(_v); } + uint16_t time2() const { return m_time2; } + void set_time2(uint16_t _v) { m__dirty = true; m_time2 = std::move(_v); } + uint16_t time3() const { return m_time3; } + void set_time3(uint16_t _v) { m__dirty = true; m_time3 = std::move(_v); } + uint8_t feedback1() const { return m_feedback1; } + void set_feedback1(uint8_t _v) { m__dirty = true; m_feedback1 = std::move(_v); } + uint8_t feedback2() const { return m_feedback2; } + void set_feedback2(uint8_t _v) { m__dirty = true; m_feedback2 = std::move(_v); } + uint8_t feedback3() const { return m_feedback3; } + void set_feedback3(uint8_t _v) { m__dirty = true; m_feedback3 = std::move(_v); } + mpc3000_seq_v3_t* _root() const { return m__root; } + void set__root(mpc3000_seq_v3_t* _v) { m__dirty = true; m__root = std::move(_v); } + kaitai::kstruct* _parent() const { return m__parent; } + void set__parent(mpc3000_seq_v3_t* _v) { m__dirty = true; m__parent = std::move(_v); } + + private: + uint8_t m_volume1; + uint8_t m_volume2; + uint8_t m_volume3; + uint8_t m_pan1; + uint8_t m_pan2; + uint8_t m_pan3; + uint16_t m_time1; + uint16_t m_time2; + uint16_t m_time3; + uint8_t m_feedback1; + uint8_t m_feedback2; + uint8_t m_feedback3; + mpc3000_seq_v3_t* m__root; + mpc3000_seq_v3_t* m__parent; + }; + + class delta_time_event_t : public kaitai::kstruct { + + public: + + private: + bool m__dirty; + + public: + + delta_time_event_t(kaitai::kstream* p__io, mpc3000_seq_v3_t::event_t* p__parent = nullptr, mpc3000_seq_v3_t* p__root = nullptr); + void _read(); + void _fetch_instances(); + void _write(); + void _check(); + ~delta_time_event_t(); + uint16_t delta_time() const { return m_delta_time; } + void set_delta_time(uint16_t _v) { m__dirty = true; m_delta_time = std::move(_v); } + mpc3000_seq_v3_t* _root() const { return m__root; } + void set__root(mpc3000_seq_v3_t* _v) { m__dirty = true; m__root = std::move(_v); } + kaitai::kstruct* _parent() const { return m__parent; } + void set__parent(mpc3000_seq_v3_t::event_t* _v) { m__dirty = true; m__parent = std::move(_v); } + + private: + uint16_t m_delta_time; + mpc3000_seq_v3_t* m__root; + mpc3000_seq_v3_t::event_t* m__parent; + }; + + class event_t : public kaitai::kstruct { + + public: + + private: + bool m__dirty; + + public: + + event_t(bool p_is_first_event, uint8_t p_preparsed_status, int32_t p_remaining_byte_count, kaitai::kstream* p__io, mpc3000_seq_v3_t* p__parent = nullptr, mpc3000_seq_v3_t* p__root = nullptr); + void _read(); + void _fetch_instances(); + void _write(); + void _check(); + ~event_t(); + mpc3000_seq_v3_t::mixer_event_t* mixer_event(); + void set_mixer_event(std::unique_ptr _v) { m__dirty = true; f_next_status = false; f_status = false; f_mixer_event = true; m_mixer_event = std::move(_v); } + void set_mixer_event_enabled(bool _v) { m__dirty = true; e_mixer_event = _v; } + + private: + void _write_mixer_event(); + + public: + uint8_t next_status(); + void _invalidate_next_status() { f_next_status = false; } + uint8_t status(); + void _invalidate_status() { f_status = false; } + uint8_t parsed_status() const { return m_parsed_status; } + void set_parsed_status(uint8_t _v) { m__dirty = true; n_parsed_status = false; f_next_status = false; f_status = false; m_parsed_status = std::move(_v); } + uint8_t track_number() const { return m_track_number; } + void set_track_number(uint8_t _v) { m__dirty = true; n_track_number = false; f_next_status = false; f_status = false; m_track_number = std::move(_v); } + kaitai::kstruct* event_body() const { return m_event_body.get(); } + void set_event_body(std::unique_ptr _v) { m__dirty = true; f_next_status = false; f_status = false; m_event_body = std::move(_v); } + std::vector* system_exclusive_body() const { return m_system_exclusive_body.get(); } + void set_system_exclusive_body(std::unique_ptr> _v) { m__dirty = true; f_next_status = false; f_status = false; m_system_exclusive_body = std::move(_v); } + uint8_t parsed_next_status() const { return m_parsed_next_status; } + void set_parsed_next_status(uint8_t _v) { m__dirty = true; n_parsed_next_status = false; f_next_status = false; f_status = false; m_parsed_next_status = std::move(_v); } + bool is_first_event() const { return m_is_first_event; } + void set_is_first_event(bool _v) { m__dirty = true; f_next_status = false; f_status = false; m_is_first_event = std::move(_v); } + uint8_t preparsed_status() const { return m_preparsed_status; } + void set_preparsed_status(uint8_t _v) { m__dirty = true; f_next_status = false; f_status = false; m_preparsed_status = std::move(_v); } + int32_t remaining_byte_count() const { return m_remaining_byte_count; } + void set_remaining_byte_count(int32_t _v) { m__dirty = true; f_next_status = false; f_status = false; m_remaining_byte_count = std::move(_v); } + mpc3000_seq_v3_t* _root() const { return m__root; } + void set__root(mpc3000_seq_v3_t* _v) { m__dirty = true; f_next_status = false; f_status = false; m__root = std::move(_v); } + kaitai::kstruct* _parent() const { return m__parent; } + void set__parent(mpc3000_seq_v3_t* _v) { m__dirty = true; f_next_status = false; f_status = false; m__parent = std::move(_v); } + + private: + bool f_mixer_event; + std::unique_ptr m_mixer_event; + + public: + bool _is_null_mixer_event() { return !mixer_event(); }; + + private: + bool w_mixer_event; + bool e_mixer_event; + bool f_next_status; + uint8_t m_next_status; + bool f_status; + uint8_t m_status; + uint8_t m_parsed_status; + bool n_parsed_status; + + public: + bool _is_null_parsed_status() { parsed_status(); return n_parsed_status; }; + + private: + uint8_t m_track_number; + bool n_track_number; + + public: + bool _is_null_track_number() { track_number(); return n_track_number; }; + + private: + std::unique_ptr m_event_body; + + public: + bool _is_null_event_body() { return !event_body(); }; + + private: + std::unique_ptr> m_system_exclusive_body; + + public: + bool _is_null_system_exclusive_body() { return !system_exclusive_body(); }; + + private: + uint8_t m_parsed_next_status; + bool n_parsed_next_status; + + public: + bool _is_null_parsed_next_status() { parsed_next_status(); return n_parsed_next_status; }; + + private: + bool m_is_first_event; + uint8_t m_preparsed_status; + int32_t m_remaining_byte_count; + mpc3000_seq_v3_t* m__root; + mpc3000_seq_v3_t* m__parent; + }; + + class mixer_t : public kaitai::kstruct { + + public: + + private: + bool m__dirty; + + public: + + mixer_t(kaitai::kstream* p__io, mpc3000_seq_v3_t* p__parent = nullptr, mpc3000_seq_v3_t* p__root = nullptr); + void _read(); + void _fetch_instances(); + void _write(); + void _check(); + ~mixer_t(); + uint8_t stereo_mix() const { return m_stereo_mix; } + void set_stereo_mix(uint8_t _v) { m__dirty = true; m_stereo_mix = std::move(_v); } + uint8_t stereo_pan() const { return m_stereo_pan; } + void set_stereo_pan(uint8_t _v) { m__dirty = true; m_stereo_pan = std::move(_v); } + uint8_t individual_out_mix() const { return m_individual_out_mix; } + void set_individual_out_mix(uint8_t _v) { m__dirty = true; m_individual_out_mix = std::move(_v); } + individual_out_t individual_out() const { return m_individual_out; } + void set_individual_out(individual_out_t _v) { m__dirty = true; m_individual_out = std::move(_v); } + no_yes_t follow_stereo() const { return m_follow_stereo; } + void set_follow_stereo(no_yes_t _v) { m__dirty = true; m_follow_stereo = std::move(_v); } + mpc3000_seq_v3_t* _root() const { return m__root; } + void set__root(mpc3000_seq_v3_t* _v) { m__dirty = true; m__root = std::move(_v); } + kaitai::kstruct* _parent() const { return m__parent; } + void set__parent(mpc3000_seq_v3_t* _v) { m__dirty = true; m__parent = std::move(_v); } + + private: + uint8_t m_stereo_mix; + uint8_t m_stereo_pan; + uint8_t m_individual_out_mix; + individual_out_t m_individual_out; + no_yes_t m_follow_stereo; + mpc3000_seq_v3_t* m__root; + mpc3000_seq_v3_t* m__parent; + }; + + class mixer_event_t : public kaitai::kstruct { + + public: + + private: + bool m__dirty; + + public: + + mixer_event_t(std::vector* p_data, kaitai::kstream* p__io, mpc3000_seq_v3_t::event_t* p__parent = nullptr, mpc3000_seq_v3_t* p__root = nullptr); + void _read(); + void _fetch_instances(); + void _write(); + void _check(); + ~mixer_event_t(); + uint8_t pad_index(); + void _invalidate_pad_index() { f_pad_index = false; } + mpc3000_seq_v3_t::mixer_event_param_t param(); + void _invalidate_param() { f_param = false; } + uint8_t value(); + void _invalidate_value() { f_value = false; } + std::vector* data() const { return m_data; } + void set_data(std::vector* _v) { m__dirty = true; f_pad_index = false; f_param = false; f_value = false; m_data = std::move(_v); } + mpc3000_seq_v3_t* _root() const { return m__root; } + void set__root(mpc3000_seq_v3_t* _v) { m__dirty = true; f_pad_index = false; f_param = false; f_value = false; m__root = std::move(_v); } + kaitai::kstruct* _parent() const { return m__parent; } + void set__parent(mpc3000_seq_v3_t::event_t* _v) { m__dirty = true; f_pad_index = false; f_param = false; f_value = false; m__parent = std::move(_v); } + + private: + bool f_pad_index; + uint8_t m_pad_index; + bool f_param; + mpc3000_seq_v3_t::mixer_event_param_t m_param; + bool f_value; + uint8_t m_value; + std::vector* m_data; + mpc3000_seq_v3_t* m__root; + mpc3000_seq_v3_t::event_t* m__parent; + }; + + class note_event_t : public kaitai::kstruct { + + public: + + enum note_variation_type_t { + NOTE_VARIATION_TYPE_TUNE = 0, + NOTE_VARIATION_TYPE_DECAY = 1, + NOTE_VARIATION_TYPE_ATTACK = 2, + NOTE_VARIATION_TYPE_FILTER = 3 + }; + static bool _is_defined_note_variation_type_t(note_variation_type_t v); + + private: + static const std::set _values_note_variation_type_t; + + public: + + private: + bool m__dirty; + + public: + + note_event_t(kaitai::kstream* p__io, mpc3000_seq_v3_t::event_t* p__parent = nullptr, mpc3000_seq_v3_t* p__root = nullptr); + void _read(); + void _fetch_instances(); + void _write(); + void _check(); + ~note_event_t(); + int32_t duration(); + void _invalidate_duration() { f_duration = false; } + mpc3000_seq_v3_t::note_event_t::note_variation_type_t note_variation_type(); + void _invalidate_note_variation_type() { f_note_variation_type = false; } + uint8_t note_number() const { return m_note_number; } + void set_note_number(uint8_t _v) { m__dirty = true; f_duration = false; f_note_variation_type = false; m_note_number = std::move(_v); } + uint8_t velocity() const { return m_velocity; } + void set_velocity(uint8_t _v) { m__dirty = true; f_duration = false; f_note_variation_type = false; m_velocity = std::move(_v); } + uint8_t note_variation_value() const { return m_note_variation_value; } + void set_note_variation_value(uint8_t _v) { m__dirty = true; f_duration = false; f_note_variation_type = false; m_note_variation_value = std::move(_v); } + uint8_t duration_byte_1() const { return m_duration_byte_1; } + void set_duration_byte_1(uint8_t _v) { m__dirty = true; f_duration = false; f_note_variation_type = false; m_duration_byte_1 = std::move(_v); } + uint8_t duration_byte_2() const { return m_duration_byte_2; } + void set_duration_byte_2(uint8_t _v) { m__dirty = true; f_duration = false; f_note_variation_type = false; m_duration_byte_2 = std::move(_v); } + mpc3000_seq_v3_t* _root() const { return m__root; } + void set__root(mpc3000_seq_v3_t* _v) { m__dirty = true; f_duration = false; f_note_variation_type = false; m__root = std::move(_v); } + kaitai::kstruct* _parent() const { return m__parent; } + void set__parent(mpc3000_seq_v3_t::event_t* _v) { m__dirty = true; f_duration = false; f_note_variation_type = false; m__parent = std::move(_v); } + + private: + bool f_duration; + int32_t m_duration; + bool f_note_variation_type; + mpc3000_seq_v3_t::note_event_t::note_variation_type_t m_note_variation_type; + uint8_t m_note_number; + uint8_t m_velocity; + uint8_t m_note_variation_value; + uint8_t m_duration_byte_1; + uint8_t m_duration_byte_2; + mpc3000_seq_v3_t* m__root; + mpc3000_seq_v3_t::event_t* m__parent; + }; + + class pitch_bend_event_t : public kaitai::kstruct { + + public: + + private: + bool m__dirty; + + public: + + pitch_bend_event_t(kaitai::kstream* p__io, mpc3000_seq_v3_t::event_t* p__parent = nullptr, mpc3000_seq_v3_t* p__root = nullptr); + void _read(); + void _fetch_instances(); + void _write(); + void _check(); + ~pitch_bend_event_t(); + int32_t corrected_pitch_bend_amount(); + void _invalidate_corrected_pitch_bend_amount() { f_corrected_pitch_bend_amount = false; } + uint64_t pitch_bend_amount_bits_1() const { return m_pitch_bend_amount_bits_1; } + void set_pitch_bend_amount_bits_1(uint64_t _v) { m__dirty = true; f_corrected_pitch_bend_amount = false; m_pitch_bend_amount_bits_1 = std::move(_v); } + uint64_t pitch_bend_amount_bits_2() const { return m_pitch_bend_amount_bits_2; } + void set_pitch_bend_amount_bits_2(uint64_t _v) { m__dirty = true; f_corrected_pitch_bend_amount = false; m_pitch_bend_amount_bits_2 = std::move(_v); } + mpc3000_seq_v3_t* _root() const { return m__root; } + void set__root(mpc3000_seq_v3_t* _v) { m__dirty = true; f_corrected_pitch_bend_amount = false; m__root = std::move(_v); } + kaitai::kstruct* _parent() const { return m__parent; } + void set__parent(mpc3000_seq_v3_t::event_t* _v) { m__dirty = true; f_corrected_pitch_bend_amount = false; m__parent = std::move(_v); } + + private: + bool f_corrected_pitch_bend_amount; + int32_t m_corrected_pitch_bend_amount; + uint64_t m_pitch_bend_amount_bits_1; + uint64_t m_pitch_bend_amount_bits_2; + mpc3000_seq_v3_t* m__root; + mpc3000_seq_v3_t::event_t* m__parent; + }; + + class poly_pressure_event_t : public kaitai::kstruct { + + public: + + private: + bool m__dirty; + + public: + + poly_pressure_event_t(kaitai::kstream* p__io, mpc3000_seq_v3_t::event_t* p__parent = nullptr, mpc3000_seq_v3_t* p__root = nullptr); + void _read(); + void _fetch_instances(); + void _write(); + void _check(); + ~poly_pressure_event_t(); + uint8_t note() const { return m_note; } + void set_note(uint8_t _v) { m__dirty = true; m_note = std::move(_v); } + uint8_t pressure() const { return m_pressure; } + void set_pressure(uint8_t _v) { m__dirty = true; m_pressure = std::move(_v); } + mpc3000_seq_v3_t* _root() const { return m__root; } + void set__root(mpc3000_seq_v3_t* _v) { m__dirty = true; m__root = std::move(_v); } + kaitai::kstruct* _parent() const { return m__parent; } + void set__parent(mpc3000_seq_v3_t::event_t* _v) { m__dirty = true; m__parent = std::move(_v); } + + private: + uint8_t m_note; + uint8_t m_pressure; + mpc3000_seq_v3_t* m__root; + mpc3000_seq_v3_t::event_t* m__parent; + }; + + class program_change_event_t : public kaitai::kstruct { + + public: + + private: + bool m__dirty; + + public: + + program_change_event_t(kaitai::kstream* p__io, mpc3000_seq_v3_t::event_t* p__parent = nullptr, mpc3000_seq_v3_t* p__root = nullptr); + void _read(); + void _fetch_instances(); + void _write(); + void _check(); + ~program_change_event_t(); + uint8_t program() const { return m_program; } + void set_program(uint8_t _v) { m__dirty = true; m_program = std::move(_v); } + mpc3000_seq_v3_t* _root() const { return m__root; } + void set__root(mpc3000_seq_v3_t* _v) { m__dirty = true; m__root = std::move(_v); } + kaitai::kstruct* _parent() const { return m__parent; } + void set__parent(mpc3000_seq_v3_t::event_t* _v) { m__dirty = true; m__parent = std::move(_v); } + + private: + uint8_t m_program; + mpc3000_seq_v3_t* m__root; + mpc3000_seq_v3_t::event_t* m__parent; + }; + + class sequence_header_t : public kaitai::kstruct { + + public: + + private: + bool m__dirty; + + public: + + sequence_header_t(kaitai::kstream* p__io, mpc3000_seq_v3_t* p__parent = nullptr, mpc3000_seq_v3_t* p__root = nullptr); + void _read(); + void _fetch_instances(); + void _write(); + void _check(); + ~sequence_header_t(); + uint8_t sequence_number() const { return m_sequence_number; } + void set_sequence_number(uint8_t _v) { m__dirty = true; m_sequence_number = std::move(_v); } + u3le_t* event_stream_length_in_bytes() const { return m_event_stream_length_in_bytes.get(); } + void set_event_stream_length_in_bytes(std::unique_ptr _v) { m__dirty = true; m_event_stream_length_in_bytes = std::move(_v); } + std::string reserved_before_event_stream_sentinel_flag() const { return m_reserved_before_event_stream_sentinel_flag; } + void set_reserved_before_event_stream_sentinel_flag(std::string _v) { m__dirty = true; m_reserved_before_event_stream_sentinel_flag = std::move(_v); } + no_yes_t event_stream_starts_with_ff() const { return m_event_stream_starts_with_ff; } + void set_event_stream_starts_with_ff(no_yes_t _v) { m__dirty = true; m_event_stream_starts_with_ff = std::move(_v); } + std::string reserved_before_name() const { return m_reserved_before_name; } + void set_reserved_before_name(std::string _v) { m__dirty = true; m_reserved_before_name = std::move(_v); } + std::string sequence_name() const { return m_sequence_name; } + void set_sequence_name(std::string _v) { m__dirty = true; m_sequence_name = std::move(_v); } + std::string reserved_after_name() const { return m_reserved_after_name; } + void set_reserved_after_name(std::string _v) { m__dirty = true; m_reserved_after_name = std::move(_v); } + off_on_t loop_to_bar() const { return m_loop_to_bar; } + void set_loop_to_bar(off_on_t _v) { m__dirty = true; m_loop_to_bar = std::move(_v); } + uint64_t loop_flags_reserved() const { return m_loop_flags_reserved; } + void set_loop_flags_reserved(uint64_t _v) { m__dirty = true; m_loop_flags_reserved = std::move(_v); } + uint16_t loop_to_bar_number() const { return m_loop_to_bar_number; } + void set_loop_to_bar_number(uint16_t _v) { m__dirty = true; m_loop_to_bar_number = std::move(_v); } + uint16_t number_of_bars() const { return m_number_of_bars; } + void set_number_of_bars(uint16_t _v) { m__dirty = true; m_number_of_bars = std::move(_v); } + uint32_t length_in_ticks() const { return m_length_in_ticks; } + void set_length_in_ticks(uint32_t _v) { m__dirty = true; m_length_in_ticks = std::move(_v); } + uint16_t tempo() const { return m_tempo; } + void set_tempo(uint16_t _v) { m__dirty = true; m_tempo = std::move(_v); } + mpc3000_seq_v3_t* _root() const { return m__root; } + void set__root(mpc3000_seq_v3_t* _v) { m__dirty = true; m__root = std::move(_v); } + kaitai::kstruct* _parent() const { return m__parent; } + void set__parent(mpc3000_seq_v3_t* _v) { m__dirty = true; m__parent = std::move(_v); } + + private: + uint8_t m_sequence_number; + std::unique_ptr m_event_stream_length_in_bytes; + std::string m_reserved_before_event_stream_sentinel_flag; + no_yes_t m_event_stream_starts_with_ff; + std::string m_reserved_before_name; + std::string m_sequence_name; + std::string m_reserved_after_name; + off_on_t m_loop_to_bar; + uint64_t m_loop_flags_reserved; + uint16_t m_loop_to_bar_number; + uint16_t m_number_of_bars; + uint32_t m_length_in_ticks; + uint16_t m_tempo; + mpc3000_seq_v3_t* m__root; + mpc3000_seq_v3_t* m__parent; + }; + + class smpte_offset_t : public kaitai::kstruct { + + public: + + private: + bool m__dirty; + + public: + + smpte_offset_t(kaitai::kstream* p__io, mpc3000_seq_v3_t* p__parent = nullptr, mpc3000_seq_v3_t* p__root = nullptr); + void _read(); + void _fetch_instances(); + void _write(); + void _check(); + ~smpte_offset_t(); + uint8_t hundredth_frames() const { return m_hundredth_frames; } + void set_hundredth_frames(uint8_t _v) { m__dirty = true; m_hundredth_frames = std::move(_v); } + uint8_t frames() const { return m_frames; } + void set_frames(uint8_t _v) { m__dirty = true; m_frames = std::move(_v); } + uint8_t seconds() const { return m_seconds; } + void set_seconds(uint8_t _v) { m__dirty = true; m_seconds = std::move(_v); } + uint8_t minutes() const { return m_minutes; } + void set_minutes(uint8_t _v) { m__dirty = true; m_minutes = std::move(_v); } + uint8_t hours() const { return m_hours; } + void set_hours(uint8_t _v) { m__dirty = true; m_hours = std::move(_v); } + mpc3000_seq_v3_t* _root() const { return m__root; } + void set__root(mpc3000_seq_v3_t* _v) { m__dirty = true; m__root = std::move(_v); } + kaitai::kstruct* _parent() const { return m__parent; } + void set__parent(mpc3000_seq_v3_t* _v) { m__dirty = true; m__parent = std::move(_v); } + + private: + uint8_t m_hundredth_frames; + uint8_t m_frames; + uint8_t m_seconds; + uint8_t m_minutes; + uint8_t m_hours; + mpc3000_seq_v3_t* m__root; + mpc3000_seq_v3_t* m__parent; + }; + + class tempo_change_t : public kaitai::kstruct { + + public: + + private: + bool m__dirty; + + public: + + tempo_change_t(kaitai::kstream* p__io, mpc3000_seq_v3_t* p__parent = nullptr, mpc3000_seq_v3_t* p__root = nullptr); + void _read(); + void _fetch_instances(); + void _write(); + void _check(); + ~tempo_change_t(); + double factor_percentage(); + void _invalidate_factor_percentage() { f_factor_percentage = false; } + uint32_t ticks_from_sequence_start() const { return m_ticks_from_sequence_start; } + void set_ticks_from_sequence_start(uint32_t _v) { m__dirty = true; f_factor_percentage = false; m_ticks_from_sequence_start = std::move(_v); } + uint64_t factor1() const { return m_factor1; } + void set_factor1(uint64_t _v) { m__dirty = true; f_factor_percentage = false; m_factor1 = std::move(_v); } + uint64_t factor2() const { return m_factor2; } + void set_factor2(uint64_t _v) { m__dirty = true; f_factor_percentage = false; m_factor2 = std::move(_v); } + mpc3000_seq_v3_t* _root() const { return m__root; } + void set__root(mpc3000_seq_v3_t* _v) { m__dirty = true; f_factor_percentage = false; m__root = std::move(_v); } + kaitai::kstruct* _parent() const { return m__parent; } + void set__parent(mpc3000_seq_v3_t* _v) { m__dirty = true; f_factor_percentage = false; m__parent = std::move(_v); } + + private: + bool f_factor_percentage; + double m_factor_percentage; + uint32_t m_ticks_from_sequence_start; + uint64_t m_factor1; + uint64_t m_factor2; + mpc3000_seq_v3_t* m__root; + mpc3000_seq_v3_t* m__parent; + }; + + class track_header_t : public kaitai::kstruct { + + public: + + private: + bool m__dirty; + + public: + + track_header_t(kaitai::kstream* p__io, mpc3000_seq_v3_t* p__parent = nullptr, mpc3000_seq_v3_t* p__root = nullptr); + void _read(); + void _fetch_instances(); + void _write(); + void _check(); + ~track_header_t(); + int8_t absolute_recorded_track_number() const { return m_absolute_recorded_track_number; } + void set_absolute_recorded_track_number(int8_t _v) { m__dirty = true; m_absolute_recorded_track_number = std::move(_v); } + std::string unused_track_header_padding() const { return m_unused_track_header_padding; } + void set_unused_track_header_padding(std::string _v) { m__dirty = true; n_unused_track_header_padding = false; m_unused_track_header_padding = std::move(_v); } + uint8_t user_track_number() const { return m_user_track_number; } + void set_user_track_number(uint8_t _v) { m__dirty = true; n_user_track_number = false; m_user_track_number = std::move(_v); } + bool track_mute() const { return m_track_mute; } + void set_track_mute(bool _v) { m__dirty = true; n_track_mute = false; m_track_mute = std::move(_v); } + bool track_in_use() const { return m_track_in_use; } + void set_track_in_use(bool _v) { m__dirty = true; n_track_in_use = false; m_track_in_use = std::move(_v); } + bool drum_track() const { return m_drum_track; } + void set_drum_track(bool _v) { m__dirty = true; n_drum_track = false; m_drum_track = std::move(_v); } + uint64_t track_flags_reserved() const { return m_track_flags_reserved; } + void set_track_flags_reserved(uint64_t _v) { m__dirty = true; n_track_flags_reserved = false; m_track_flags_reserved = std::move(_v); } + uint8_t primary_port_channel_assignment() const { return m_primary_port_channel_assignment; } + void set_primary_port_channel_assignment(uint8_t _v) { m__dirty = true; n_primary_port_channel_assignment = false; m_primary_port_channel_assignment = std::move(_v); } + int8_t secondary_port_channel_assignment() const { return m_secondary_port_channel_assignment; } + void set_secondary_port_channel_assignment(int8_t _v) { m__dirty = true; n_secondary_port_channel_assignment = false; m_secondary_port_channel_assignment = std::move(_v); } + std::string track_name() const { return m_track_name; } + void set_track_name(std::string _v) { m__dirty = true; n_track_name = false; m_track_name = std::move(_v); } + uint8_t track_volume() const { return m_track_volume; } + void set_track_volume(uint8_t _v) { m__dirty = true; n_track_volume = false; m_track_volume = std::move(_v); } + uint8_t program_change_number() const { return m_program_change_number; } + void set_program_change_number(uint8_t _v) { m__dirty = true; n_program_change_number = false; m_program_change_number = std::move(_v); } + std::string reserved_after_program_change() const { return m_reserved_after_program_change; } + void set_reserved_after_program_change(std::string _v) { m__dirty = true; n_reserved_after_program_change = false; m_reserved_after_program_change = std::move(_v); } + mpc3000_seq_v3_t* _root() const { return m__root; } + void set__root(mpc3000_seq_v3_t* _v) { m__dirty = true; m__root = std::move(_v); } + kaitai::kstruct* _parent() const { return m__parent; } + void set__parent(mpc3000_seq_v3_t* _v) { m__dirty = true; m__parent = std::move(_v); } + + private: + int8_t m_absolute_recorded_track_number; + std::string m_unused_track_header_padding; + bool n_unused_track_header_padding; + + public: + bool _is_null_unused_track_header_padding() { unused_track_header_padding(); return n_unused_track_header_padding; }; + + private: + uint8_t m_user_track_number; + bool n_user_track_number; + + public: + bool _is_null_user_track_number() { user_track_number(); return n_user_track_number; }; + + private: + bool m_track_mute; + bool n_track_mute; + + public: + bool _is_null_track_mute() { track_mute(); return n_track_mute; }; + + private: + bool m_track_in_use; + bool n_track_in_use; + + public: + bool _is_null_track_in_use() { track_in_use(); return n_track_in_use; }; + + private: + bool m_drum_track; + bool n_drum_track; + + public: + bool _is_null_drum_track() { drum_track(); return n_drum_track; }; + + private: + uint64_t m_track_flags_reserved; + bool n_track_flags_reserved; + + public: + bool _is_null_track_flags_reserved() { track_flags_reserved(); return n_track_flags_reserved; }; + + private: + uint8_t m_primary_port_channel_assignment; + bool n_primary_port_channel_assignment; + + public: + bool _is_null_primary_port_channel_assignment() { primary_port_channel_assignment(); return n_primary_port_channel_assignment; }; + + private: + int8_t m_secondary_port_channel_assignment; + bool n_secondary_port_channel_assignment; + + public: + bool _is_null_secondary_port_channel_assignment() { secondary_port_channel_assignment(); return n_secondary_port_channel_assignment; }; + + private: + std::string m_track_name; + bool n_track_name; + + public: + bool _is_null_track_name() { track_name(); return n_track_name; }; + + private: + uint8_t m_track_volume; + bool n_track_volume; + + public: + bool _is_null_track_volume() { track_volume(); return n_track_volume; }; + + private: + uint8_t m_program_change_number; + bool n_program_change_number; + + public: + bool _is_null_program_change_number() { program_change_number(); return n_program_change_number; }; + + private: + std::string m_reserved_after_program_change; + bool n_reserved_after_program_change; + + public: + bool _is_null_reserved_after_program_change() { reserved_after_program_change(); return n_reserved_after_program_change; }; + + private: + mpc3000_seq_v3_t* m__root; + mpc3000_seq_v3_t* m__parent; + }; + + class tune_request_event_t : public kaitai::kstruct { + + public: + + private: + bool m__dirty; + + public: + + tune_request_event_t(kaitai::kstream* p__io, mpc3000_seq_v3_t::event_t* p__parent = nullptr, mpc3000_seq_v3_t* p__root = nullptr); + void _read(); + void _fetch_instances(); + void _write(); + void _check(); + ~tune_request_event_t(); + std::string tune_request_event() const { return m_tune_request_event; } + void set_tune_request_event(std::string _v) { m__dirty = true; m_tune_request_event = std::move(_v); } + mpc3000_seq_v3_t* _root() const { return m__root; } + void set__root(mpc3000_seq_v3_t* _v) { m__dirty = true; m__root = std::move(_v); } + kaitai::kstruct* _parent() const { return m__parent; } + void set__parent(mpc3000_seq_v3_t::event_t* _v) { m__dirty = true; m__parent = std::move(_v); } + + private: + std::string m_tune_request_event; + mpc3000_seq_v3_t* m__root; + mpc3000_seq_v3_t::event_t* m__parent; + }; + + class u3le_t : public kaitai::kstruct { + + public: + + private: + bool m__dirty; + + public: + + u3le_t(kaitai::kstream* p__io, mpc3000_seq_v3_t::sequence_header_t* p__parent = nullptr, mpc3000_seq_v3_t* p__root = nullptr); + void _read(); + void _fetch_instances(); + void _write(); + void _check(); + ~u3le_t(); + int32_t value(); + void _invalidate_value() { f_value = false; } + uint16_t b12() const { return m_b12; } + void set_b12(uint16_t _v) { m__dirty = true; f_value = false; m_b12 = std::move(_v); } + uint8_t b3() const { return m_b3; } + void set_b3(uint8_t _v) { m__dirty = true; f_value = false; m_b3 = std::move(_v); } + mpc3000_seq_v3_t* _root() const { return m__root; } + void set__root(mpc3000_seq_v3_t* _v) { m__dirty = true; f_value = false; m__root = std::move(_v); } + kaitai::kstruct* _parent() const { return m__parent; } + void set__parent(mpc3000_seq_v3_t::sequence_header_t* _v) { m__dirty = true; f_value = false; m__parent = std::move(_v); } + + private: + bool f_value; + int32_t m_value; + uint16_t m_b12; + uint8_t m_b3; + mpc3000_seq_v3_t* m__root; + mpc3000_seq_v3_t::sequence_header_t* m__parent; + }; + +public: + std::string file_id() const { return m_file_id; } + void set_file_id(std::string _v) { m__dirty = true; m_file_id = std::move(_v); } + std::string file_format_version() const { return m_file_format_version; } + void set_file_format_version(std::string _v) { m__dirty = true; m_file_format_version = std::move(_v); } + sequence_header_t* sequence_header() const { return m_sequence_header.get(); } + void set_sequence_header(std::unique_ptr _v) { m__dirty = true; m_sequence_header = std::move(_v); } + smpte_offset_t* smpte_offset() const { return m_smpte_offset.get(); } + void set_smpte_offset(std::unique_ptr _v) { m__dirty = true; m_smpte_offset = std::move(_v); } + std::vector>* mixer() const { return m_mixer.get(); } + void set_mixer(std::unique_ptr>> _v) { m__dirty = true; m_mixer = std::move(_v); } + std::string reserved_after_mixer() const { return m_reserved_after_mixer; } + void set_reserved_after_mixer(std::string _v) { m__dirty = true; m_reserved_after_mixer = std::move(_v); } + delays_t* delays() const { return m_delays.get(); } + void set_delays(std::unique_ptr _v) { m__dirty = true; m_delays = std::move(_v); } + std::string reserved_after_delays() const { return m_reserved_after_delays; } + void set_reserved_after_delays(std::string _v) { m__dirty = true; m_reserved_after_delays = std::move(_v); } + std::string reserved_before_track_summary() const { return m_reserved_before_track_summary; } + void set_reserved_before_track_summary(std::string _v) { m__dirty = true; m_reserved_before_track_summary = std::move(_v); } + uint8_t last_active_track() const { return m_last_active_track; } + void set_last_active_track(uint8_t _v) { m__dirty = true; m_last_active_track = std::move(_v); } + uint8_t num_tempo_changes() const { return m_num_tempo_changes; } + void set_num_tempo_changes(uint8_t _v) { m__dirty = true; m_num_tempo_changes = std::move(_v); } + uint8_t num_track_headers() const { return m_num_track_headers; } + void set_num_track_headers(uint8_t _v) { m__dirty = true; m_num_track_headers = std::move(_v); } + std::vector>* track_headers() const { return m_track_headers.get(); } + void set_track_headers(std::unique_ptr>> _v) { m__dirty = true; m_track_headers = std::move(_v); } + std::vector>* tempo_changes() const { return m_tempo_changes.get(); } + void set_tempo_changes(std::unique_ptr>> _v) { m__dirty = true; m_tempo_changes = std::move(_v); } + std::vector>* events() const { return m_events.get(); } + void set_events(std::unique_ptr>> _v) { m__dirty = true; m_events = std::move(_v); } + mpc3000_seq_v3_t* _root() const { return m__root; } + void set__root(mpc3000_seq_v3_t* _v) { m__dirty = true; m__root = std::move(_v); } + kaitai::kstruct* _parent() const { return m__parent; } + void set__parent(kaitai::kstruct* _v) { m__dirty = true; m__parent = std::move(_v); } + +private: + std::string m_file_id; + std::string m_file_format_version; + std::unique_ptr m_sequence_header; + std::unique_ptr m_smpte_offset; + std::unique_ptr>> m_mixer; + std::string m_reserved_after_mixer; + std::unique_ptr m_delays; + std::string m_reserved_after_delays; + std::string m_reserved_before_track_summary; + uint8_t m_last_active_track; + uint8_t m_num_tempo_changes; + uint8_t m_num_track_headers; + std::unique_ptr>> m_track_headers; + std::unique_ptr>> m_tempo_changes; + std::unique_ptr>> m_events; + mpc3000_seq_v3_t* m__root; + kaitai::kstruct* m__parent; +}; diff --git a/src/main/file/kaitai/generated/mpc3000_snd_v2.cpp b/src/main/file/kaitai/generated/mpc3000_snd_v2.cpp new file mode 100644 index 000000000..110ee9750 --- /dev/null +++ b/src/main/file/kaitai/generated/mpc3000_snd_v2.cpp @@ -0,0 +1,121 @@ +// This is a generated file! Please edit source .ksy file and use kaitai-struct-compiler to rebuild + +#include "mpc3000_snd_v2.h" +#include "kaitai/exceptions.h" +#include +#include + +mpc3000_snd_v2_t::mpc3000_snd_v2_t(kaitai::kstream* p__io, kaitai::kstruct* p__parent, mpc3000_snd_v2_t* p__root) : kaitai::kstruct(p__io) { + m__parent = p__parent; + m__root = p__root ? p__root : this; + m__dirty = false; + f_header_size = false; + f_sample_rate = false; +} + +void mpc3000_snd_v2_t::_read() { + m_file_id = m__io->read_bytes(1); + if (!(m_file_id == std::string("\x01", 1))) { + throw kaitai::validation_not_equal_error(std::string("\x01", 1), m_file_id, m__io, std::string("/seq/0")); + } + m_file_version = m__io->read_bytes(1); + if (!(m_file_version == std::string("\x02", 1))) { + throw kaitai::validation_not_equal_error(std::string("\x02", 1), m_file_version, m__io, std::string("/seq/1")); + } + m_name = kaitai::kstream::bytes_to_str(kaitai::kstream::bytes_terminate(m__io->read_bytes(17), static_cast(0), false), "ASCII"); + m_level = m__io->read_u1(); + m_unknown_1 = m__io->read_bytes(2); + m_start = m__io->read_u4le(); + m_end = m__io->read_u4le(); + m_frame_count = m__io->read_u4le(); + m_unknown_2 = m__io->read_u4le(); + m_sample_data = std::unique_ptr>(new std::vector()); + const int l_sample_data = frame_count(); + for (int i = 0; i < l_sample_data; i++) { + m_sample_data->push_back(std::move(m__io->read_s2le())); + } + m__dirty = false; +} + +void mpc3000_snd_v2_t::_fetch_instances() { + for (std::size_t i = 0; i < m_sample_data->size(); ++i) { + } +} + +void mpc3000_snd_v2_t::_write() { + m__io->write_bytes(m_file_id); + m__io->write_bytes(m_file_version); + std::string _bufm_name = m_name; + if (_bufm_name.size() < static_cast(17)) { + _bufm_name += std::string(1, static_cast(0)); + } + if (_bufm_name.size() < static_cast(17)) { + _bufm_name.append(static_cast(17) - _bufm_name.size(), static_cast(0)); + } + m__io->write_bytes(_bufm_name); + m__io->write_u1(m_level); + m__io->write_bytes(m_unknown_1); + m__io->write_u4le(m_start); + m__io->write_u4le(m_end); + m__io->write_u4le(m_frame_count); + m__io->write_u4le(m_unknown_2); + if (m_sample_data == nullptr) { + throw std::runtime_error("/seq/9: repeated field is not set"); + } + for (std::vector::const_iterator it = m_sample_data->begin(); it != m_sample_data->end(); ++it) { + m__io->write_s2le((*it)); + } + _fetch_instances(); + m__dirty = false; +} + +void mpc3000_snd_v2_t::_check() { + if (m_file_id.size() != static_cast(1)) { + throw std::runtime_error("/seq/0: size mismatch"); + } + if (!(m_file_id == std::string("\x01", 1))) { + throw kaitai::validation_not_equal_error(std::string("\x01", 1), m_file_id, m__io, std::string("/seq/0")); + } + if (m_file_version.size() != static_cast(1)) { + throw std::runtime_error("/seq/1: size mismatch"); + } + if (!(m_file_version == std::string("\x02", 1))) { + throw kaitai::validation_not_equal_error(std::string("\x02", 1), m_file_version, m__io, std::string("/seq/1")); + } + if (m_name.size() > static_cast(17)) { + throw std::runtime_error("/seq/2: size mismatch"); + } + if (m_name.find(static_cast(0)) != std::string::npos) { + throw std::runtime_error("/seq/2: terminator must not appear in value"); + } + if (m_unknown_1.size() != static_cast(2)) { + throw std::runtime_error("/seq/4: size mismatch"); + } + if (m_sample_data == nullptr) { + throw std::runtime_error("/seq/9: repeated field is not set"); + } + if (m_sample_data->size() != static_cast(frame_count())) { + throw std::runtime_error("/seq/9: repeat-expr size mismatch"); + } + for (std::vector::const_iterator it = m_sample_data->begin(); it != m_sample_data->end(); ++it) { + } + m__dirty = false; +} + +mpc3000_snd_v2_t::~mpc3000_snd_v2_t() {} + +int8_t mpc3000_snd_v2_t::header_size() { + if (f_header_size) + return m_header_size; + f_header_size = true; + m_header_size = 38; + return m_header_size; +} + +int32_t mpc3000_snd_v2_t::sample_rate() { + if (f_sample_rate) + return m_sample_rate; + f_sample_rate = true; + m_sample_rate = 44100; + return m_sample_rate; +} diff --git a/src/main/file/kaitai/generated/mpc3000_snd_v2.h b/src/main/file/kaitai/generated/mpc3000_snd_v2.h new file mode 100644 index 000000000..dd59a59c9 --- /dev/null +++ b/src/main/file/kaitai/generated/mpc3000_snd_v2.h @@ -0,0 +1,142 @@ +#pragma once + +// This is a generated file! Please edit source .ksy file and use kaitai-struct-compiler to rebuild + +class mpc3000_snd_v2_t; + +#include "kaitai/kaitaistruct.h" +#include +#include +#include + +#if KAITAI_STRUCT_VERSION < 11000L +#error "Incompatible Kaitai Struct C++/STL API: version 0.11 or later is required" +#endif + +/** + * Provisional parser for MPC3000 SND files whose first two bytes are 0x01 0x02. + * + * This definition is currently based on one hardware MPC3000 sample: + * SOUND017.SND, recorded as a two-second mono sample. That file is confirmed in + * the local hardware MPC3000 OS 3.11 corpus: + * + * /Users/izmar/projects/VMPC2000XL/reverse_engineer/mpc3000-os3.11/empty/SOUND017.SND + * + * The known-good interpretation for that file is a 38-byte header followed by + * signed 16-bit little-endian PCM at 44100 Hz. + * + * Known producers of this 0x01 0x02 family currently include: + * + * - hardware MPC3000 OS 3.11 + * - MAME MPC3000 firmware 3.10 + * + * A focused MAME MPC3000 3.10 save-session probe established the following: + * + * - `level` is the saved Vol% byte. Files saved with Vol% 50, 100, and 200 + * encoded `0x32`, `0x64`, and `0xc8` respectively. + * - `unknown_2` is not a fixed constant and is not sample-rate metadata. Across + * a sequence of saves of the same 88200-frame sample, it increased by exactly + * `frame_count` each time: `0x000107d0`, `0x00026058`, `0x0003b8e0`, + * `0x00051168`, `0x000669f0`, `0x0007c278`. This strongly suggests a running + * allocator / sample-offset style value rather than an intrinsic property of + * the sample itself. + * - In that same save path, editing `Soft st` to `000.500.00` and `Soft end` to + * `002.500.00` did not change the persisted payload or the bytes currently + * mapped as `start`, `end`, and `frame_count`. So those fields remain + * provisional, and the save path appears to flatten or ignore those edit + * parameters. + * + * Field names other than file_id, file_version, name, level, and sample_data are + * still provisional. + */ + +class mpc3000_snd_v2_t : public kaitai::kstruct { + +public: + +private: + bool m__dirty; + +public: + + mpc3000_snd_v2_t(kaitai::kstream* p__io, kaitai::kstruct* p__parent = nullptr, mpc3000_snd_v2_t* p__root = nullptr); + void _read(); + void _fetch_instances(); + void _write(); + void _check(); + ~mpc3000_snd_v2_t(); + int8_t header_size(); + void _invalidate_header_size() { f_header_size = false; } + + /** + * Hardware MPC3000 SND probe SOUND017.SND has 88200 frames for a known two-second recording, implying 44100 Hz. This is exposed as a constant until sample-rate encoding is identified in the header. + */ + int32_t sample_rate(); + void _invalidate_sample_rate() { f_sample_rate = false; } + std::string file_id() const { return m_file_id; } + void set_file_id(std::string _v) { m__dirty = true; f_header_size = false; f_sample_rate = false; m_file_id = std::move(_v); } + std::string file_version() const { return m_file_version; } + void set_file_version(std::string _v) { m__dirty = true; f_header_size = false; f_sample_rate = false; m_file_version = std::move(_v); } + std::string name() const { return m_name; } + void set_name(std::string _v) { m__dirty = true; f_header_size = false; f_sample_rate = false; m_name = std::move(_v); } + + /** + * Observed as 100 in SOUND017.SND. + */ + uint8_t level() const { return m_level; } + void set_level(uint8_t _v) { m__dirty = true; f_header_size = false; f_sample_rate = false; m_level = std::move(_v); } + + /** + * Observed as 0x0000 in SOUND017.SND. + */ + std::string unknown_1() const { return m_unknown_1; } + void set_unknown_1(std::string _v) { m__dirty = true; f_header_size = false; f_sample_rate = false; m_unknown_1 = std::move(_v); } + + /** + * Provisional. Observed as 0 in SOUND017.SND and unchanged in a MAME 3.10 save-session probe even when `Soft st` was edited to `000.500.00`. + */ + uint32_t start() const { return m_start; } + void set_start(uint32_t _v) { m__dirty = true; f_header_size = false; f_sample_rate = false; m_start = std::move(_v); } + + /** + * Provisional. Observed as 88200 in SOUND017.SND and unchanged in a MAME 3.10 save-session probe even when `Soft end` was edited. + */ + uint32_t end() const { return m_end; } + void set_end(uint32_t _v) { m__dirty = true; f_header_size = false; f_sample_rate = false; m_end = std::move(_v); } + + /** + * Strongly supported. In SOUND017.SND this is 88200, and 38 + frame_count * 2 equals the file size. It also matched the delta of `unknown_2` across repeated MAME 3.10 saves of the same sample. + */ + uint32_t frame_count() const { return m_frame_count; } + void set_frame_count(uint32_t _v) { m__dirty = true; f_header_size = false; f_sample_rate = false; m_frame_count = std::move(_v); } + + /** + * Not yet fully classified. In a MAME MPC3000 3.10 probe, this value increased by exactly `frame_count` on each save of the same sample, which strongly suggests a running allocator / sample-offset style value rather than sample-rate metadata. + */ + uint32_t unknown_2() const { return m_unknown_2; } + void set_unknown_2(uint32_t _v) { m__dirty = true; f_header_size = false; f_sample_rate = false; m_unknown_2 = std::move(_v); } + std::vector* sample_data() const { return m_sample_data.get(); } + void set_sample_data(std::unique_ptr> _v) { m__dirty = true; f_header_size = false; f_sample_rate = false; m_sample_data = std::move(_v); } + mpc3000_snd_v2_t* _root() const { return m__root; } + void set__root(mpc3000_snd_v2_t* _v) { m__dirty = true; f_header_size = false; f_sample_rate = false; m__root = std::move(_v); } + kaitai::kstruct* _parent() const { return m__parent; } + void set__parent(kaitai::kstruct* _v) { m__dirty = true; f_header_size = false; f_sample_rate = false; m__parent = std::move(_v); } + +private: + bool f_header_size; + int8_t m_header_size; + bool f_sample_rate; + int32_t m_sample_rate; + std::string m_file_id; + std::string m_file_version; + std::string m_name; + uint8_t m_level; + std::string m_unknown_1; + uint32_t m_start; + uint32_t m_end; + uint32_t m_frame_count; + uint32_t m_unknown_2; + std::unique_ptr> m_sample_data; + mpc3000_snd_v2_t* m__root; + kaitai::kstruct* m__parent; +}; diff --git a/src/main/file/kaitai/generated/mpc60_all_body.cpp b/src/main/file/kaitai/generated/mpc60_all_body.cpp new file mode 100644 index 000000000..1718c4d1e --- /dev/null +++ b/src/main/file/kaitai/generated/mpc60_all_body.cpp @@ -0,0 +1,441 @@ +// This is a generated file! Please edit source .ksy file and use kaitai-struct-compiler to rebuild + +#include "mpc60_all_body.h" +#include "kaitai/exceptions.h" +#include +#include + +mpc60_all_body_t::mpc60_all_body_t(kaitai::kstream* p__io, kaitai::kstruct* p__parent, mpc60_all_body_t* p__root) : kaitai::kstruct(p__io) { + m__parent = p__parent; + m__root = p__root ? p__root : this; + m__dirty = false; +} + +void mpc60_all_body_t::_read() { + m__dirty = false; +} + +void mpc60_all_body_t::_fetch_instances() { +} + +void mpc60_all_body_t::_write() { + _fetch_instances(); + m__dirty = false; +} + +void mpc60_all_body_t::_check() { + m__dirty = false; +} + +mpc60_all_body_t::~mpc60_all_body_t() {} + +mpc60_all_body_t::all_file_body_t::all_file_body_t(kaitai::kstream* p__io, kaitai::kstruct* p__parent, mpc60_all_body_t* p__root) : kaitai::kstruct(p__io) { + m__parent = p__parent; + m__root = p__root; + m__dirty = false; +} + +void mpc60_all_body_t::all_file_body_t::_read() { + m_total_number_of_bytes_in_all_sequences = m__io->read_u4le(); + m_sequences = std::unique_ptr>>(new std::vector>()); + { + int i = 0; + mpc60_seq_body_t::sequence_t* _; + do { + std::unique_ptr _t_sequences = std::unique_ptr(new mpc60_seq_body_t::sequence_t(m__io)); + try { + _t_sequences->_read(); + } catch(...) { + _ = _t_sequences.get(); + m_sequences->push_back(std::move(_t_sequences)); + throw; + } + _ = _t_sequences.get(); + m_sequences->push_back(std::move(_t_sequences)); + i++; + } while (!(_io()->pos() - 6 >= total_number_of_bytes_in_all_sequences() - 1)); + } + m_sequences_terminator = m__io->read_bytes(1); + if (!(m_sequences_terminator == std::string("\xFF", 1))) { + throw kaitai::validation_not_equal_error(std::string("\xFF", 1), m_sequences_terminator, m__io, std::string("/types/all_file_body/seq/2")); + } + m_songs = std::unique_ptr>>(new std::vector>()); + { + int i = 0; + song_t* _; + do { + std::unique_ptr _t_songs = std::unique_ptr(new song_t(m__io, this, m__root)); + try { + _t_songs->_read(); + } catch(...) { + _ = _t_songs.get(); + m_songs->push_back(std::move(_t_songs)); + throw; + } + _ = _t_songs.get(); + m_songs->push_back(std::move(_t_songs)); + i++; + } while (!(_->step_count() == 0)); + } + m__dirty = false; +} + +void mpc60_all_body_t::all_file_body_t::_fetch_instances() { + for (std::size_t i = 0; i < m_sequences->size(); ++i) { + m_sequences->at(i).get()->_fetch_instances(); + } + for (std::size_t i = 0; i < m_songs->size(); ++i) { + m_songs->at(i).get()->_fetch_instances(); + } +} + +void mpc60_all_body_t::all_file_body_t::_write() { + m__io->write_u4le(m_total_number_of_bytes_in_all_sequences); + if (m_sequences == nullptr) { + throw std::runtime_error("/types/all_file_body/seq/1: repeated field is not set"); + } + for (std::vector>::const_iterator it = m_sequences->begin(); it != m_sequences->end(); ++it) { + if ((*it).get() == nullptr) { + throw std::runtime_error("/types/all_file_body/seq/1: nested object is not set"); + } + (*it).get()->_set_io(m__io); + (*it).get()->_write(); + } + m__io->write_bytes(m_sequences_terminator); + if (m_songs == nullptr) { + throw std::runtime_error("/types/all_file_body/seq/3: repeated field is not set"); + } + for (std::vector>::const_iterator it = m_songs->begin(); it != m_songs->end(); ++it) { + if ((*it).get() == nullptr) { + throw std::runtime_error("/types/all_file_body/seq/3: nested object is not set"); + } + (*it).get()->_set_io(m__io); + (*it).get()->_write(); + } + _fetch_instances(); + m__dirty = false; +} + +void mpc60_all_body_t::all_file_body_t::_check() { + if (m_sequences == nullptr) { + throw std::runtime_error("/types/all_file_body/seq/1: repeated field is not set"); + } + if (m_sequences->empty()) { + throw std::runtime_error("/types/all_file_body/seq/1: repeat-until field must not be empty"); + } + for (std::vector>::const_iterator it = m_sequences->begin(); it != m_sequences->end(); ++it) { + const std::size_t i = static_cast(it - m_sequences->begin()); + const mpc60_seq_body_t::sequence_t* _ = (*it).get(); + if ((*it).get() == nullptr) { + throw std::runtime_error("/types/all_file_body/seq/1: nested object is not set"); + } + (*it).get()->_set_io(m__io); + (*it).get()->_check(); + const bool _is_last = (i == m_sequences->size() - 1); + if ((_io()->pos() - 6 >= total_number_of_bytes_in_all_sequences() - 1) != _is_last) { + throw std::runtime_error("/types/all_file_body/seq/1: repeat-until condition mismatch"); + } + } + if (m_sequences_terminator.size() != static_cast(1)) { + throw std::runtime_error("/types/all_file_body/seq/2: size mismatch"); + } + if (!(m_sequences_terminator == std::string("\xFF", 1))) { + throw kaitai::validation_not_equal_error(std::string("\xFF", 1), m_sequences_terminator, m__io, std::string("/types/all_file_body/seq/2")); + } + if (m_songs == nullptr) { + throw std::runtime_error("/types/all_file_body/seq/3: repeated field is not set"); + } + if (m_songs->empty()) { + throw std::runtime_error("/types/all_file_body/seq/3: repeat-until field must not be empty"); + } + for (std::vector>::const_iterator it = m_songs->begin(); it != m_songs->end(); ++it) { + const std::size_t i = static_cast(it - m_songs->begin()); + const song_t* _ = (*it).get(); + if ((*it).get() == nullptr) { + throw std::runtime_error("/types/all_file_body/seq/3: nested object is not set"); + } + (*it).get()->_set_io(m__io); + (*it).get()->_check(); + const bool _is_last = (i == m_songs->size() - 1); + if ((_->step_count() == 0) != _is_last) { + throw std::runtime_error("/types/all_file_body/seq/3: repeat-until condition mismatch"); + } + } + m__dirty = false; +} + +mpc60_all_body_t::all_file_body_t::~all_file_body_t() {} + +mpc60_all_body_t::empty_song_t::empty_song_t(kaitai::kstream* p__io, mpc60_all_body_t::song_t* p__parent, mpc60_all_body_t* p__root) : kaitai::kstruct(p__io) { + m__parent = p__parent; + m__root = p__root; + m__dirty = false; +} + +void mpc60_all_body_t::empty_song_t::_read() { + m__dirty = false; +} + +void mpc60_all_body_t::empty_song_t::_fetch_instances() { +} + +void mpc60_all_body_t::empty_song_t::_write() { + _fetch_instances(); + m__dirty = false; +} + +void mpc60_all_body_t::empty_song_t::_check() { + m__dirty = false; +} + +mpc60_all_body_t::empty_song_t::~empty_song_t() {} + +mpc60_all_body_t::song_t::song_t(kaitai::kstream* p__io, mpc60_all_body_t::all_file_body_t* p__parent, mpc60_all_body_t* p__root) : kaitai::kstruct(p__io) { + m__parent = p__parent; + m__root = p__root; + m__dirty = false; +} + +void mpc60_all_body_t::song_t::_read() { + m_step_count = m__io->read_u1(); + switch (step_count()) { + case 0: { + m_body = std::unique_ptr(new empty_song_t(m__io, this, m__root)); + static_cast(m_body.get())->_read(); + break; + } + default: { + m_body = std::unique_ptr(new song_body_t(m__io, this, m__root)); + static_cast(m_body.get())->_read(); + break; + } + } + m__dirty = false; +} + +void mpc60_all_body_t::song_t::_fetch_instances() { + switch (step_count()) { + case 0: { + { + empty_song_t* _switch_obj = dynamic_cast(m_body.get()); + if (_switch_obj == nullptr) { + throw std::runtime_error("switch object type mismatch in _fetch_instances"); + } + _switch_obj->_fetch_instances(); + } + break; + } + default: { + { + song_body_t* _switch_obj = dynamic_cast(m_body.get()); + if (_switch_obj == nullptr) { + throw std::runtime_error("switch object type mismatch in _fetch_instances"); + } + _switch_obj->_fetch_instances(); + } + break; + } + } +} + +void mpc60_all_body_t::song_t::_write() { + m__io->write_u1(m_step_count); + switch (step_count()) { + case 0: { + { + empty_song_t* _switch_obj = dynamic_cast(m_body.get()); + if (_switch_obj == nullptr) { + throw std::runtime_error("/types/song/seq/1: switch object type mismatch"); + } + if (_switch_obj == nullptr) { + throw std::runtime_error("/types/song/seq/1: nested object is not set"); + } + _switch_obj->_set_io(m__io); + _switch_obj->_write(); + } + break; + } + default: { + { + song_body_t* _switch_obj = dynamic_cast(m_body.get()); + if (_switch_obj == nullptr) { + throw std::runtime_error("/types/song/seq/1: switch object type mismatch"); + } + if (_switch_obj == nullptr) { + throw std::runtime_error("/types/song/seq/1: nested object is not set"); + } + _switch_obj->_set_io(m__io); + _switch_obj->_write(); + } + break; + } + } + _fetch_instances(); + m__dirty = false; +} + +void mpc60_all_body_t::song_t::_check() { + switch (step_count()) { + case 0: { + { + empty_song_t* _switch_obj = dynamic_cast(m_body.get()); + if (_switch_obj == nullptr) { + throw std::runtime_error("/types/song/seq/1: switch object type mismatch"); + } + if (_switch_obj == nullptr) { + throw std::runtime_error("/types/song/seq/1: nested object is not set"); + } + _switch_obj->_set_io(m__io); + _switch_obj->_check(); + } + break; + } + default: { + { + song_body_t* _switch_obj = dynamic_cast(m_body.get()); + if (_switch_obj == nullptr) { + throw std::runtime_error("/types/song/seq/1: switch object type mismatch"); + } + if (_switch_obj == nullptr) { + throw std::runtime_error("/types/song/seq/1: nested object is not set"); + } + _switch_obj->_set_io(m__io); + _switch_obj->_check(); + } + break; + } + } + m__dirty = false; +} + +mpc60_all_body_t::song_t::~song_t() {} + +mpc60_all_body_t::song_body_t::song_body_t(kaitai::kstream* p__io, mpc60_all_body_t::song_t* p__parent, mpc60_all_body_t* p__root) : kaitai::kstruct(p__io) { + m__parent = p__parent; + m__root = p__root; + m__dirty = false; +} + +void mpc60_all_body_t::song_body_t::_read() { + m_song_number = m__io->read_u1(); + m_reserved_1 = m__io->read_bytes(1); + if (!(m_reserved_1 == std::string("\x00", 1))) { + throw kaitai::validation_not_equal_error(std::string("\x00", 1), m_reserved_1, m__io, std::string("/types/song_body/seq/1")); + } + m_reserved_2 = m__io->read_bytes(1); + if (!(m_reserved_2 == std::string("\x01", 1))) { + throw kaitai::validation_not_equal_error(std::string("\x01", 1), m_reserved_2, m__io, std::string("/types/song_body/seq/2")); + } + m_song_name = kaitai::kstream::bytes_to_str(m__io->read_bytes(16), "ASCII"); + m_reserved_3 = m__io->read_bytes(5); + if (!(m_reserved_3 == std::string("\x00\x00\x00\x00\x00", 5))) { + throw kaitai::validation_not_equal_error(std::string("\x00\x00\x00\x00\x00", 5), m_reserved_3, m__io, std::string("/types/song_body/seq/4")); + } + m_steps = std::unique_ptr>>(new std::vector>()); + const int l_steps = static_cast(_parent())->step_count(); + for (int i = 0; i < l_steps; i++) { + std::unique_ptr _t_steps = std::unique_ptr(new song_step_t(m__io, this, m__root)); + try { + _t_steps->_read(); + } catch(...) { + m_steps->push_back(std::move(_t_steps)); + throw; + } + m_steps->push_back(std::move(_t_steps)); + } + m__dirty = false; +} + +void mpc60_all_body_t::song_body_t::_fetch_instances() { + for (std::size_t i = 0; i < m_steps->size(); ++i) { + m_steps->at(i).get()->_fetch_instances(); + } +} + +void mpc60_all_body_t::song_body_t::_write() { + m__io->write_u1(m_song_number); + m__io->write_bytes(m_reserved_1); + m__io->write_bytes(m_reserved_2); + m__io->write_bytes(m_song_name); + m__io->write_bytes(m_reserved_3); + if (m_steps == nullptr) { + throw std::runtime_error("/types/song_body/seq/5: repeated field is not set"); + } + for (std::vector>::const_iterator it = m_steps->begin(); it != m_steps->end(); ++it) { + if ((*it).get() == nullptr) { + throw std::runtime_error("/types/song_body/seq/5: nested object is not set"); + } + (*it).get()->_set_io(m__io); + (*it).get()->_write(); + } + _fetch_instances(); + m__dirty = false; +} + +void mpc60_all_body_t::song_body_t::_check() { + if (m_reserved_1.size() != static_cast(1)) { + throw std::runtime_error("/types/song_body/seq/1: size mismatch"); + } + if (!(m_reserved_1 == std::string("\x00", 1))) { + throw kaitai::validation_not_equal_error(std::string("\x00", 1), m_reserved_1, m__io, std::string("/types/song_body/seq/1")); + } + if (m_reserved_2.size() != static_cast(1)) { + throw std::runtime_error("/types/song_body/seq/2: size mismatch"); + } + if (!(m_reserved_2 == std::string("\x01", 1))) { + throw kaitai::validation_not_equal_error(std::string("\x01", 1), m_reserved_2, m__io, std::string("/types/song_body/seq/2")); + } + if (m_song_name.size() != static_cast(16)) { + throw std::runtime_error("/types/song_body/seq/3: size mismatch"); + } + if (m_reserved_3.size() != static_cast(5)) { + throw std::runtime_error("/types/song_body/seq/4: size mismatch"); + } + if (!(m_reserved_3 == std::string("\x00\x00\x00\x00\x00", 5))) { + throw kaitai::validation_not_equal_error(std::string("\x00\x00\x00\x00\x00", 5), m_reserved_3, m__io, std::string("/types/song_body/seq/4")); + } + if (m_steps == nullptr) { + throw std::runtime_error("/types/song_body/seq/5: repeated field is not set"); + } + if (m_steps->size() != static_cast(static_cast(_parent())->step_count())) { + throw std::runtime_error("/types/song_body/seq/5: repeat-expr size mismatch"); + } + for (std::vector>::const_iterator it = m_steps->begin(); it != m_steps->end(); ++it) { + if ((*it).get() == nullptr) { + throw std::runtime_error("/types/song_body/seq/5: nested object is not set"); + } + (*it).get()->_set_io(m__io); + (*it).get()->_check(); + } + m__dirty = false; +} + +mpc60_all_body_t::song_body_t::~song_body_t() {} + +mpc60_all_body_t::song_step_t::song_step_t(kaitai::kstream* p__io, mpc60_all_body_t::song_body_t* p__parent, mpc60_all_body_t* p__root) : kaitai::kstruct(p__io) { + m__parent = p__parent; + m__root = p__root; + m__dirty = false; +} + +void mpc60_all_body_t::song_step_t::_read() { + m_sequence_number = m__io->read_u1(); + m_repeats = m__io->read_u1(); + m__dirty = false; +} + +void mpc60_all_body_t::song_step_t::_fetch_instances() { +} + +void mpc60_all_body_t::song_step_t::_write() { + m__io->write_u1(m_sequence_number); + m__io->write_u1(m_repeats); + _fetch_instances(); + m__dirty = false; +} + +void mpc60_all_body_t::song_step_t::_check() { + m__dirty = false; +} + +mpc60_all_body_t::song_step_t::~song_step_t() {} diff --git a/src/main/file/kaitai/generated/mpc60_all_body.h b/src/main/file/kaitai/generated/mpc60_all_body.h new file mode 100644 index 000000000..7b0a55934 --- /dev/null +++ b/src/main/file/kaitai/generated/mpc60_all_body.h @@ -0,0 +1,214 @@ +#pragma once + +// This is a generated file! Please edit source .ksy file and use kaitai-struct-compiler to rebuild + +class mpc60_all_body_t; + +#include "kaitai/kaitaistruct.h" +#include +#include +#include "mpc60_seq_body.h" +#include + +#if KAITAI_STRUCT_VERSION < 11000L +#error "Incompatible Kaitai Struct C++/STL API: version 0.11 or later is required" +#endif + +class mpc60_all_body_t : public kaitai::kstruct { + +public: + class all_file_body_t; + class empty_song_t; + class song_t; + class song_body_t; + class song_step_t; + +private: + bool m__dirty; + +public: + + mpc60_all_body_t(kaitai::kstream* p__io, kaitai::kstruct* p__parent = nullptr, mpc60_all_body_t* p__root = nullptr); + void _read(); + void _fetch_instances(); + void _write(); + void _check(); + ~mpc60_all_body_t(); + + class all_file_body_t : public kaitai::kstruct { + + public: + + private: + bool m__dirty; + + public: + + all_file_body_t(kaitai::kstream* p__io, kaitai::kstruct* p__parent = nullptr, mpc60_all_body_t* p__root = nullptr); + void _read(); + void _fetch_instances(); + void _write(); + void _check(); + ~all_file_body_t(); + uint32_t total_number_of_bytes_in_all_sequences() const { return m_total_number_of_bytes_in_all_sequences; } + void set_total_number_of_bytes_in_all_sequences(uint32_t _v) { m__dirty = true; m_total_number_of_bytes_in_all_sequences = std::move(_v); } + std::vector>* sequences() const { return m_sequences.get(); } + void set_sequences(std::unique_ptr>> _v) { m__dirty = true; m_sequences = std::move(_v); } + std::string sequences_terminator() const { return m_sequences_terminator; } + void set_sequences_terminator(std::string _v) { m__dirty = true; m_sequences_terminator = std::move(_v); } + std::vector>* songs() const { return m_songs.get(); } + void set_songs(std::unique_ptr>> _v) { m__dirty = true; m_songs = std::move(_v); } + mpc60_all_body_t* _root() const { return m__root; } + void set__root(mpc60_all_body_t* _v) { m__dirty = true; m__root = std::move(_v); } + kaitai::kstruct* _parent() const { return m__parent; } + void set__parent(kaitai::kstruct* _v) { m__dirty = true; m__parent = std::move(_v); } + + private: + uint32_t m_total_number_of_bytes_in_all_sequences; + std::unique_ptr>> m_sequences; + std::string m_sequences_terminator; + std::unique_ptr>> m_songs; + mpc60_all_body_t* m__root; + kaitai::kstruct* m__parent; + }; + + class empty_song_t : public kaitai::kstruct { + + public: + + private: + bool m__dirty; + + public: + + empty_song_t(kaitai::kstream* p__io, mpc60_all_body_t::song_t* p__parent = nullptr, mpc60_all_body_t* p__root = nullptr); + void _read(); + void _fetch_instances(); + void _write(); + void _check(); + ~empty_song_t(); + mpc60_all_body_t* _root() const { return m__root; } + void set__root(mpc60_all_body_t* _v) { m__dirty = true; m__root = std::move(_v); } + kaitai::kstruct* _parent() const { return m__parent; } + void set__parent(mpc60_all_body_t::song_t* _v) { m__dirty = true; m__parent = std::move(_v); } + + private: + mpc60_all_body_t* m__root; + mpc60_all_body_t::song_t* m__parent; + }; + + class song_t : public kaitai::kstruct { + + public: + + private: + bool m__dirty; + + public: + + song_t(kaitai::kstream* p__io, mpc60_all_body_t::all_file_body_t* p__parent = nullptr, mpc60_all_body_t* p__root = nullptr); + void _read(); + void _fetch_instances(); + void _write(); + void _check(); + ~song_t(); + uint8_t step_count() const { return m_step_count; } + void set_step_count(uint8_t _v) { m__dirty = true; m_step_count = std::move(_v); } + kaitai::kstruct* body() const { return m_body.get(); } + void set_body(std::unique_ptr _v) { m__dirty = true; m_body = std::move(_v); } + mpc60_all_body_t* _root() const { return m__root; } + void set__root(mpc60_all_body_t* _v) { m__dirty = true; m__root = std::move(_v); } + kaitai::kstruct* _parent() const { return m__parent; } + void set__parent(mpc60_all_body_t::all_file_body_t* _v) { m__dirty = true; m__parent = std::move(_v); } + + private: + uint8_t m_step_count; + std::unique_ptr m_body; + mpc60_all_body_t* m__root; + mpc60_all_body_t::all_file_body_t* m__parent; + }; + + class song_body_t : public kaitai::kstruct { + + public: + + private: + bool m__dirty; + + public: + + song_body_t(kaitai::kstream* p__io, mpc60_all_body_t::song_t* p__parent = nullptr, mpc60_all_body_t* p__root = nullptr); + void _read(); + void _fetch_instances(); + void _write(); + void _check(); + ~song_body_t(); + uint8_t song_number() const { return m_song_number; } + void set_song_number(uint8_t _v) { m__dirty = true; m_song_number = std::move(_v); } + std::string reserved_1() const { return m_reserved_1; } + void set_reserved_1(std::string _v) { m__dirty = true; m_reserved_1 = std::move(_v); } + std::string reserved_2() const { return m_reserved_2; } + void set_reserved_2(std::string _v) { m__dirty = true; m_reserved_2 = std::move(_v); } + std::string song_name() const { return m_song_name; } + void set_song_name(std::string _v) { m__dirty = true; m_song_name = std::move(_v); } + std::string reserved_3() const { return m_reserved_3; } + void set_reserved_3(std::string _v) { m__dirty = true; m_reserved_3 = std::move(_v); } + std::vector>* steps() const { return m_steps.get(); } + void set_steps(std::unique_ptr>> _v) { m__dirty = true; m_steps = std::move(_v); } + mpc60_all_body_t* _root() const { return m__root; } + void set__root(mpc60_all_body_t* _v) { m__dirty = true; m__root = std::move(_v); } + kaitai::kstruct* _parent() const { return m__parent; } + void set__parent(mpc60_all_body_t::song_t* _v) { m__dirty = true; m__parent = std::move(_v); } + + private: + uint8_t m_song_number; + std::string m_reserved_1; + std::string m_reserved_2; + std::string m_song_name; + std::string m_reserved_3; + std::unique_ptr>> m_steps; + mpc60_all_body_t* m__root; + mpc60_all_body_t::song_t* m__parent; + }; + + class song_step_t : public kaitai::kstruct { + + public: + + private: + bool m__dirty; + + public: + + song_step_t(kaitai::kstream* p__io, mpc60_all_body_t::song_body_t* p__parent = nullptr, mpc60_all_body_t* p__root = nullptr); + void _read(); + void _fetch_instances(); + void _write(); + void _check(); + ~song_step_t(); + uint8_t sequence_number() const { return m_sequence_number; } + void set_sequence_number(uint8_t _v) { m__dirty = true; m_sequence_number = std::move(_v); } + uint8_t repeats() const { return m_repeats; } + void set_repeats(uint8_t _v) { m__dirty = true; m_repeats = std::move(_v); } + mpc60_all_body_t* _root() const { return m__root; } + void set__root(mpc60_all_body_t* _v) { m__dirty = true; m__root = std::move(_v); } + kaitai::kstruct* _parent() const { return m__parent; } + void set__parent(mpc60_all_body_t::song_body_t* _v) { m__dirty = true; m__parent = std::move(_v); } + + private: + uint8_t m_sequence_number; + uint8_t m_repeats; + mpc60_all_body_t* m__root; + mpc60_all_body_t::song_body_t* m__parent; + }; + +public: + mpc60_all_body_t* _root() const { return m__root; } + void set__root(mpc60_all_body_t* _v) { m__dirty = true; m__root = std::move(_v); } + kaitai::kstruct* _parent() const { return m__parent; } + void set__parent(kaitai::kstruct* _v) { m__dirty = true; m__parent = std::move(_v); } + +private: + mpc60_all_body_t* m__root; + kaitai::kstruct* m__parent; +}; diff --git a/src/main/file/kaitai/generated/mpc60_all_v2.cpp b/src/main/file/kaitai/generated/mpc60_all_v2.cpp new file mode 100644 index 000000000..bef4f8603 --- /dev/null +++ b/src/main/file/kaitai/generated/mpc60_all_v2.cpp @@ -0,0 +1,64 @@ +// This is a generated file! Please edit source .ksy file and use kaitai-struct-compiler to rebuild + +#include "mpc60_all_v2.h" +#include "kaitai/exceptions.h" +#include + +mpc60_all_v2_t::mpc60_all_v2_t(kaitai::kstream* p__io, kaitai::kstruct* p__parent, mpc60_all_v2_t* p__root) : kaitai::kstruct(p__io) { + m__parent = p__parent; + m__root = p__root ? p__root : this; + m__dirty = false; +} + +void mpc60_all_v2_t::_read() { + m_file_id = m__io->read_bytes(1); + if (!(m_file_id == std::string("\x04", 1))) { + throw kaitai::validation_not_equal_error(std::string("\x04", 1), m_file_id, m__io, std::string("/seq/0")); + } + m_file_format_version = m__io->read_bytes(1); + if (!(m_file_format_version == std::string("\x02", 1))) { + throw kaitai::validation_not_equal_error(std::string("\x02", 1), m_file_format_version, m__io, std::string("/seq/1")); + } + m_body = std::unique_ptr(new mpc60_all_body_t::all_file_body_t(m__io)); + m_body->_read(); + m__dirty = false; +} + +void mpc60_all_v2_t::_fetch_instances() { + m_body.get()->_fetch_instances(); +} + +void mpc60_all_v2_t::_write() { + m__io->write_bytes(m_file_id); + m__io->write_bytes(m_file_format_version); + if (m_body.get() == nullptr) { + throw std::runtime_error("/seq/2: nested object is not set"); + } + m_body.get()->_set_io(m__io); + m_body.get()->_write(); + _fetch_instances(); + m__dirty = false; +} + +void mpc60_all_v2_t::_check() { + if (m_file_id.size() != static_cast(1)) { + throw std::runtime_error("/seq/0: size mismatch"); + } + if (!(m_file_id == std::string("\x04", 1))) { + throw kaitai::validation_not_equal_error(std::string("\x04", 1), m_file_id, m__io, std::string("/seq/0")); + } + if (m_file_format_version.size() != static_cast(1)) { + throw std::runtime_error("/seq/1: size mismatch"); + } + if (!(m_file_format_version == std::string("\x02", 1))) { + throw kaitai::validation_not_equal_error(std::string("\x02", 1), m_file_format_version, m__io, std::string("/seq/1")); + } + if (m_body.get() == nullptr) { + throw std::runtime_error("/seq/2: nested object is not set"); + } + m_body.get()->_set_io(m__io); + m_body.get()->_check(); + m__dirty = false; +} + +mpc60_all_v2_t::~mpc60_all_v2_t() {} diff --git a/src/main/file/kaitai/generated/mpc60_all_v2.h b/src/main/file/kaitai/generated/mpc60_all_v2.h new file mode 100644 index 000000000..afcd531a0 --- /dev/null +++ b/src/main/file/kaitai/generated/mpc60_all_v2.h @@ -0,0 +1,48 @@ +#pragma once + +// This is a generated file! Please edit source .ksy file and use kaitai-struct-compiler to rebuild + +class mpc60_all_v2_t; + +#include "kaitai/kaitaistruct.h" +#include +#include +#include "mpc60_all_body.h" + +#if KAITAI_STRUCT_VERSION < 11000L +#error "Incompatible Kaitai Struct C++/STL API: version 0.11 or later is required" +#endif + +class mpc60_all_v2_t : public kaitai::kstruct { + +public: + +private: + bool m__dirty; + +public: + + mpc60_all_v2_t(kaitai::kstream* p__io, kaitai::kstruct* p__parent = nullptr, mpc60_all_v2_t* p__root = nullptr); + void _read(); + void _fetch_instances(); + void _write(); + void _check(); + ~mpc60_all_v2_t(); + std::string file_id() const { return m_file_id; } + void set_file_id(std::string _v) { m__dirty = true; m_file_id = std::move(_v); } + std::string file_format_version() const { return m_file_format_version; } + void set_file_format_version(std::string _v) { m__dirty = true; m_file_format_version = std::move(_v); } + mpc60_all_body_t::all_file_body_t* body() const { return m_body.get(); } + void set_body(std::unique_ptr _v) { m__dirty = true; m_body = std::move(_v); } + mpc60_all_v2_t* _root() const { return m__root; } + void set__root(mpc60_all_v2_t* _v) { m__dirty = true; m__root = std::move(_v); } + kaitai::kstruct* _parent() const { return m__parent; } + void set__parent(kaitai::kstruct* _v) { m__dirty = true; m__parent = std::move(_v); } + +private: + std::string m_file_id; + std::string m_file_format_version; + std::unique_ptr m_body; + mpc60_all_v2_t* m__root; + kaitai::kstruct* m__parent; +}; diff --git a/src/main/file/kaitai/generated/mpc60_seq_body.cpp b/src/main/file/kaitai/generated/mpc60_seq_body.cpp new file mode 100644 index 000000000..d755739cb --- /dev/null +++ b/src/main/file/kaitai/generated/mpc60_seq_body.cpp @@ -0,0 +1,512 @@ +// This is a generated file! Please edit source .ksy file and use kaitai-struct-compiler to rebuild + +#include "mpc60_seq_body.h" +#include +#include +#include "kaitai/exceptions.h" +const std::set mpc60_seq_body_t::_values_off_on_t{ + mpc60_seq_body_t::OFF_ON_FALSE, + mpc60_seq_body_t::OFF_ON_TRUE, +}; +bool mpc60_seq_body_t::_is_defined_off_on_t(mpc60_seq_body_t::off_on_t v) { + return mpc60_seq_body_t::_values_off_on_t.find(v) != mpc60_seq_body_t::_values_off_on_t.end(); +} + +mpc60_seq_body_t::mpc60_seq_body_t(kaitai::kstream* p__io, kaitai::kstruct* p__parent, mpc60_seq_body_t* p__root) : kaitai::kstruct(p__io) { + m__parent = p__parent; + m__root = p__root ? p__root : this; + m__dirty = false; +} + +void mpc60_seq_body_t::_read() { + m__dirty = false; +} + +void mpc60_seq_body_t::_fetch_instances() { +} + +void mpc60_seq_body_t::_write() { + _fetch_instances(); + m__dirty = false; +} + +void mpc60_seq_body_t::_check() { + m__dirty = false; +} + +mpc60_seq_body_t::~mpc60_seq_body_t() {} + +mpc60_seq_body_t::sequence_t::sequence_t(kaitai::kstream* p__io, kaitai::kstruct* p__parent, mpc60_seq_body_t* p__root) : kaitai::kstruct(p__io) { + m__parent = p__parent; + m__root = p__root; + m__dirty = false; + f_events_start = false; +} + +void mpc60_seq_body_t::sequence_t::_read() { + m_sequence_header = std::unique_ptr(new sequence_header_t(m__io, this, m__root)); + m_sequence_header->_read(); + m_track_headers = std::unique_ptr>>(new std::vector>()); + const int l_track_headers = sequence_header()->num_track_headers(); + for (int i = 0; i < l_track_headers; i++) { + std::unique_ptr _t_track_headers = std::unique_ptr(new mpc3000_seq_v3_t::track_header_t(m__io)); + try { + _t_track_headers->_read(); + } catch(...) { + m_track_headers->push_back(std::move(_t_track_headers)); + throw; + } + m_track_headers->push_back(std::move(_t_track_headers)); + } + m_tempo_changes = std::unique_ptr>>(new std::vector>()); + const int l_tempo_changes = sequence_header()->num_tempo_changes(); + for (int i = 0; i < l_tempo_changes; i++) { + std::unique_ptr _t_tempo_changes = std::unique_ptr(new mpc3000_seq_v3_t::tempo_change_t(m__io)); + try { + _t_tempo_changes->_read(); + } catch(...) { + m_tempo_changes->push_back(std::move(_t_tempo_changes)); + throw; + } + m_tempo_changes->push_back(std::move(_t_tempo_changes)); + } + m_events = std::unique_ptr>>(new std::vector>()); + { + int i = 0; + mpc3000_seq_v3_t::event_t* _; + do { + std::unique_ptr _t_events = std::unique_ptr(new mpc3000_seq_v3_t::event_t(i == 0, ((i > 0) ? (events()->at(i - 1)->next_status()) : (255)), sequence_header()->event_stream_length_in_bytes()->value() - (_io()->pos() - events_start()), m__io)); + try { + _t_events->_read(); + } catch(...) { + _ = _t_events.get(); + m_events->push_back(std::move(_t_events)); + throw; + } + _ = _t_events.get(); + m_events->push_back(std::move(_t_events)); + i++; + } while (!(sequence_header()->event_stream_length_in_bytes()->value() - (_io()->pos() - events_start()) == 0)); + } + m__dirty = false; +} + +void mpc60_seq_body_t::sequence_t::_fetch_instances() { + m_sequence_header.get()->_fetch_instances(); + for (std::size_t i = 0; i < m_track_headers->size(); ++i) { + m_track_headers->at(i).get()->_fetch_instances(); + } + for (std::size_t i = 0; i < m_tempo_changes->size(); ++i) { + m_tempo_changes->at(i).get()->_fetch_instances(); + } + for (std::size_t i = 0; i < m_events->size(); ++i) { + m_events->at(i).get()->_fetch_instances(); + } +} + +void mpc60_seq_body_t::sequence_t::_write() { + if (m_sequence_header.get() == nullptr) { + throw std::runtime_error("/types/sequence/seq/0: nested object is not set"); + } + m_sequence_header.get()->_set_io(m__io); + m_sequence_header.get()->_write(); + if (m_track_headers == nullptr) { + throw std::runtime_error("/types/sequence/seq/1: repeated field is not set"); + } + for (std::vector>::const_iterator it = m_track_headers->begin(); it != m_track_headers->end(); ++it) { + if ((*it).get() == nullptr) { + throw std::runtime_error("/types/sequence/seq/1: nested object is not set"); + } + (*it).get()->_set_io(m__io); + (*it).get()->_write(); + } + if (m_tempo_changes == nullptr) { + throw std::runtime_error("/types/sequence/seq/2: repeated field is not set"); + } + for (std::vector>::const_iterator it = m_tempo_changes->begin(); it != m_tempo_changes->end(); ++it) { + if ((*it).get() == nullptr) { + throw std::runtime_error("/types/sequence/seq/2: nested object is not set"); + } + (*it).get()->_set_io(m__io); + (*it).get()->_write(); + } + if (m_events == nullptr) { + throw std::runtime_error("/types/sequence/seq/3: repeated field is not set"); + } + for (std::vector>::const_iterator it = m_events->begin(); it != m_events->end(); ++it) { + if ((*it).get() == nullptr) { + throw std::runtime_error("/types/sequence/seq/3: nested object is not set"); + } + (*it).get()->_set_io(m__io); + (*it).get()->_write(); + } + _fetch_instances(); + m__dirty = false; +} + +void mpc60_seq_body_t::sequence_t::_check() { + if (m_sequence_header.get() == nullptr) { + throw std::runtime_error("/types/sequence/seq/0: nested object is not set"); + } + m_sequence_header.get()->_set_io(m__io); + m_sequence_header.get()->_check(); + if (m_track_headers == nullptr) { + throw std::runtime_error("/types/sequence/seq/1: repeated field is not set"); + } + if (m_track_headers->size() != static_cast(sequence_header()->num_track_headers())) { + throw std::runtime_error("/types/sequence/seq/1: repeat-expr size mismatch"); + } + for (std::vector>::const_iterator it = m_track_headers->begin(); it != m_track_headers->end(); ++it) { + if ((*it).get() == nullptr) { + throw std::runtime_error("/types/sequence/seq/1: nested object is not set"); + } + (*it).get()->_set_io(m__io); + (*it).get()->_check(); + } + if (m_tempo_changes == nullptr) { + throw std::runtime_error("/types/sequence/seq/2: repeated field is not set"); + } + if (m_tempo_changes->size() != static_cast(sequence_header()->num_tempo_changes())) { + throw std::runtime_error("/types/sequence/seq/2: repeat-expr size mismatch"); + } + for (std::vector>::const_iterator it = m_tempo_changes->begin(); it != m_tempo_changes->end(); ++it) { + if ((*it).get() == nullptr) { + throw std::runtime_error("/types/sequence/seq/2: nested object is not set"); + } + (*it).get()->_set_io(m__io); + (*it).get()->_check(); + } + if (m_events == nullptr) { + throw std::runtime_error("/types/sequence/seq/3: repeated field is not set"); + } + if (m_events->empty()) { + throw std::runtime_error("/types/sequence/seq/3: repeat-until field must not be empty"); + } + for (std::vector>::const_iterator it = m_events->begin(); it != m_events->end(); ++it) { + const std::size_t i = static_cast(it - m_events->begin()); + const mpc3000_seq_v3_t::event_t* _ = (*it).get(); + if ((*it).get() == nullptr) { + throw std::runtime_error("/types/sequence/seq/3: nested object is not set"); + } + (*it).get()->_set_io(m__io); + (*it).get()->_check(); + const bool _is_last = (i == m_events->size() - 1); + if ((sequence_header()->event_stream_length_in_bytes()->value() - (_io()->pos() - events_start()) == 0) != _is_last) { + throw std::runtime_error("/types/sequence/seq/3: repeat-until condition mismatch"); + } + } + m__dirty = false; +} + +mpc60_seq_body_t::sequence_t::~sequence_t() {} + +int32_t mpc60_seq_body_t::sequence_t::events_start() { + if (f_events_start) + return m_events_start; + f_events_start = true; + m_events_start = _io()->pos(); + return m_events_start; +} + +mpc60_seq_body_t::sequence_header_t::sequence_header_t(kaitai::kstream* p__io, mpc60_seq_body_t::sequence_t* p__parent, mpc60_seq_body_t* p__root) : kaitai::kstruct(p__io) { + m__parent = p__parent; + m__root = p__root; + m__dirty = false; +} + +void mpc60_seq_body_t::sequence_header_t::_read() { + m_sequence_number = m__io->read_u1(); + m_event_stream_length_in_bytes = std::unique_ptr(new u3le_t(m__io, this, m__root)); + m_event_stream_length_in_bytes->_read(); + m_offset_from_bottom_of_sequence_to_sequence_start = std::unique_ptr(new u3le_t(m__io, this, m__root)); + m_offset_from_bottom_of_sequence_to_sequence_start->_read(); + m_sequence_name = kaitai::kstream::bytes_to_str(m__io->read_bytes(16), "ASCII"); + m_loop_to_bar = static_cast(m__io->read_bits_int_le(1)); + m__unnamed5 = m__io->read_bits_int_le(7); + m_loop_to_bar_number = m__io->read_u2le(); + m_number_of_bars = m__io->read_u2le(); + m_length_in_ticks = m__io->read_u4le(); + m_tempo = m__io->read_u2le(); + m_smpte_offset = std::unique_ptr(new smpte_offset_t(m__io, this, m__root)); + m_smpte_offset->_read(); + m_stereo_mix = std::unique_ptr>(new std::vector()); + const int l_stereo_mix = 32; + for (int i = 0; i < l_stereo_mix; i++) { + m_stereo_mix->push_back(std::move(m__io->read_u1())); + } + m_stereo_pan = std::unique_ptr>(new std::vector()); + const int l_stereo_pan = 32; + for (int i = 0; i < l_stereo_pan; i++) { + m_stereo_pan->push_back(std::move(m__io->read_u1())); + } + m_echo_mix = std::unique_ptr>(new std::vector()); + const int l_echo_mix = 32; + for (int i = 0; i < l_echo_mix; i++) { + m_echo_mix->push_back(std::move(m__io->read_u1())); + } + m_drum_tuning = std::unique_ptr>(new std::vector()); + const int l_drum_tuning = 32; + for (int i = 0; i < l_drum_tuning; i++) { + m_drum_tuning->push_back(std::move(m__io->read_u2le())); + } + m_last_active_user_track = m__io->read_u1(); + m_num_tempo_changes = m__io->read_u1(); + m_num_track_headers = m__io->read_u1(); + m__dirty = false; +} + +void mpc60_seq_body_t::sequence_header_t::_fetch_instances() { + m_event_stream_length_in_bytes.get()->_fetch_instances(); + m_offset_from_bottom_of_sequence_to_sequence_start.get()->_fetch_instances(); + m_smpte_offset.get()->_fetch_instances(); + for (std::size_t i = 0; i < m_stereo_mix->size(); ++i) { + } + for (std::size_t i = 0; i < m_stereo_pan->size(); ++i) { + } + for (std::size_t i = 0; i < m_echo_mix->size(); ++i) { + } + for (std::size_t i = 0; i < m_drum_tuning->size(); ++i) { + } +} + +void mpc60_seq_body_t::sequence_header_t::_write() { + m__io->write_u1(m_sequence_number); + if (m_event_stream_length_in_bytes.get() == nullptr) { + throw std::runtime_error("/types/sequence_header/seq/1: nested object is not set"); + } + m_event_stream_length_in_bytes.get()->_set_io(m__io); + m_event_stream_length_in_bytes.get()->_write(); + if (m_offset_from_bottom_of_sequence_to_sequence_start.get() == nullptr) { + throw std::runtime_error("/types/sequence_header/seq/2: nested object is not set"); + } + m_offset_from_bottom_of_sequence_to_sequence_start.get()->_set_io(m__io); + m_offset_from_bottom_of_sequence_to_sequence_start.get()->_write(); + m__io->write_bytes(m_sequence_name); + m__io->write_bits_int_le(1, static_cast(m_loop_to_bar)); + m__io->write_bits_int_le(7, m__unnamed5); + m__io->write_u2le(m_loop_to_bar_number); + m__io->write_u2le(m_number_of_bars); + m__io->write_u4le(m_length_in_ticks); + m__io->write_u2le(m_tempo); + if (m_smpte_offset.get() == nullptr) { + throw std::runtime_error("/types/sequence_header/seq/10: nested object is not set"); + } + m_smpte_offset.get()->_set_io(m__io); + m_smpte_offset.get()->_write(); + if (m_stereo_mix == nullptr) { + throw std::runtime_error("/types/sequence_header/seq/11: repeated field is not set"); + } + for (std::vector::const_iterator it = m_stereo_mix->begin(); it != m_stereo_mix->end(); ++it) { + m__io->write_u1((*it)); + } + if (m_stereo_pan == nullptr) { + throw std::runtime_error("/types/sequence_header/seq/12: repeated field is not set"); + } + for (std::vector::const_iterator it = m_stereo_pan->begin(); it != m_stereo_pan->end(); ++it) { + m__io->write_u1((*it)); + } + if (m_echo_mix == nullptr) { + throw std::runtime_error("/types/sequence_header/seq/13: repeated field is not set"); + } + for (std::vector::const_iterator it = m_echo_mix->begin(); it != m_echo_mix->end(); ++it) { + m__io->write_u1((*it)); + } + if (m_drum_tuning == nullptr) { + throw std::runtime_error("/types/sequence_header/seq/14: repeated field is not set"); + } + for (std::vector::const_iterator it = m_drum_tuning->begin(); it != m_drum_tuning->end(); ++it) { + m__io->write_u2le((*it)); + } + m__io->write_u1(m_last_active_user_track); + m__io->write_u1(m_num_tempo_changes); + m__io->write_u1(m_num_track_headers); + _fetch_instances(); + m__dirty = false; +} + +void mpc60_seq_body_t::sequence_header_t::_check() { + if (m_event_stream_length_in_bytes.get() == nullptr) { + throw std::runtime_error("/types/sequence_header/seq/1: nested object is not set"); + } + m_event_stream_length_in_bytes.get()->_set_io(m__io); + m_event_stream_length_in_bytes.get()->_check(); + if (m_offset_from_bottom_of_sequence_to_sequence_start.get() == nullptr) { + throw std::runtime_error("/types/sequence_header/seq/2: nested object is not set"); + } + m_offset_from_bottom_of_sequence_to_sequence_start.get()->_set_io(m__io); + m_offset_from_bottom_of_sequence_to_sequence_start.get()->_check(); + if (m_sequence_name.size() != static_cast(16)) { + throw std::runtime_error("/types/sequence_header/seq/3: size mismatch"); + } + if (m_smpte_offset.get() == nullptr) { + throw std::runtime_error("/types/sequence_header/seq/10: nested object is not set"); + } + m_smpte_offset.get()->_set_io(m__io); + m_smpte_offset.get()->_check(); + if (m_stereo_mix == nullptr) { + throw std::runtime_error("/types/sequence_header/seq/11: repeated field is not set"); + } + if (m_stereo_mix->size() != static_cast(32)) { + throw std::runtime_error("/types/sequence_header/seq/11: repeat-expr size mismatch"); + } + for (std::vector::const_iterator it = m_stereo_mix->begin(); it != m_stereo_mix->end(); ++it) { + } + if (m_stereo_pan == nullptr) { + throw std::runtime_error("/types/sequence_header/seq/12: repeated field is not set"); + } + if (m_stereo_pan->size() != static_cast(32)) { + throw std::runtime_error("/types/sequence_header/seq/12: repeat-expr size mismatch"); + } + for (std::vector::const_iterator it = m_stereo_pan->begin(); it != m_stereo_pan->end(); ++it) { + } + if (m_echo_mix == nullptr) { + throw std::runtime_error("/types/sequence_header/seq/13: repeated field is not set"); + } + if (m_echo_mix->size() != static_cast(32)) { + throw std::runtime_error("/types/sequence_header/seq/13: repeat-expr size mismatch"); + } + for (std::vector::const_iterator it = m_echo_mix->begin(); it != m_echo_mix->end(); ++it) { + } + if (m_drum_tuning == nullptr) { + throw std::runtime_error("/types/sequence_header/seq/14: repeated field is not set"); + } + if (m_drum_tuning->size() != static_cast(32)) { + throw std::runtime_error("/types/sequence_header/seq/14: repeat-expr size mismatch"); + } + for (std::vector::const_iterator it = m_drum_tuning->begin(); it != m_drum_tuning->end(); ++it) { + } + m__dirty = false; +} + +mpc60_seq_body_t::sequence_header_t::~sequence_header_t() {} + +mpc60_seq_body_t::smpte_offset_t::smpte_offset_t(kaitai::kstream* p__io, mpc60_seq_body_t::sequence_header_t* p__parent, mpc60_seq_body_t* p__root) : kaitai::kstruct(p__io) { + m__parent = p__parent; + m__root = p__root; + m__dirty = false; +} + +void mpc60_seq_body_t::smpte_offset_t::_read() { + m_hundredth_frames = m__io->read_u1(); + if (!(m_hundredth_frames >= 0)) { + throw kaitai::validation_less_than_error(0, m_hundredth_frames, m__io, std::string("/types/smpte_offset/seq/0")); + } + if (!(m_hundredth_frames <= 99)) { + throw kaitai::validation_greater_than_error(99, m_hundredth_frames, m__io, std::string("/types/smpte_offset/seq/0")); + } + m_frames = m__io->read_u1(); + if (!(m_frames >= 0)) { + throw kaitai::validation_less_than_error(0, m_frames, m__io, std::string("/types/smpte_offset/seq/1")); + } + if (!(m_frames <= 29)) { + throw kaitai::validation_greater_than_error(29, m_frames, m__io, std::string("/types/smpte_offset/seq/1")); + } + m_seconds = m__io->read_u1(); + if (!(m_seconds >= 0)) { + throw kaitai::validation_less_than_error(0, m_seconds, m__io, std::string("/types/smpte_offset/seq/2")); + } + if (!(m_seconds <= 59)) { + throw kaitai::validation_greater_than_error(59, m_seconds, m__io, std::string("/types/smpte_offset/seq/2")); + } + m_minutes = m__io->read_u1(); + if (!(m_minutes >= 0)) { + throw kaitai::validation_less_than_error(0, m_minutes, m__io, std::string("/types/smpte_offset/seq/3")); + } + if (!(m_minutes <= 59)) { + throw kaitai::validation_greater_than_error(59, m_minutes, m__io, std::string("/types/smpte_offset/seq/3")); + } + m_hours = m__io->read_u1(); + if (!(m_hours >= 0)) { + throw kaitai::validation_less_than_error(0, m_hours, m__io, std::string("/types/smpte_offset/seq/4")); + } + if (!(m_hours <= 23)) { + throw kaitai::validation_greater_than_error(23, m_hours, m__io, std::string("/types/smpte_offset/seq/4")); + } + m__dirty = false; +} + +void mpc60_seq_body_t::smpte_offset_t::_fetch_instances() { +} + +void mpc60_seq_body_t::smpte_offset_t::_write() { + m__io->write_u1(m_hundredth_frames); + m__io->write_u1(m_frames); + m__io->write_u1(m_seconds); + m__io->write_u1(m_minutes); + m__io->write_u1(m_hours); + _fetch_instances(); + m__dirty = false; +} + +void mpc60_seq_body_t::smpte_offset_t::_check() { + if (!(m_hundredth_frames >= 0)) { + throw kaitai::validation_less_than_error(0, m_hundredth_frames, m__io, std::string("/types/smpte_offset/seq/0")); + } + if (!(m_hundredth_frames <= 99)) { + throw kaitai::validation_greater_than_error(99, m_hundredth_frames, m__io, std::string("/types/smpte_offset/seq/0")); + } + if (!(m_frames >= 0)) { + throw kaitai::validation_less_than_error(0, m_frames, m__io, std::string("/types/smpte_offset/seq/1")); + } + if (!(m_frames <= 29)) { + throw kaitai::validation_greater_than_error(29, m_frames, m__io, std::string("/types/smpte_offset/seq/1")); + } + if (!(m_seconds >= 0)) { + throw kaitai::validation_less_than_error(0, m_seconds, m__io, std::string("/types/smpte_offset/seq/2")); + } + if (!(m_seconds <= 59)) { + throw kaitai::validation_greater_than_error(59, m_seconds, m__io, std::string("/types/smpte_offset/seq/2")); + } + if (!(m_minutes >= 0)) { + throw kaitai::validation_less_than_error(0, m_minutes, m__io, std::string("/types/smpte_offset/seq/3")); + } + if (!(m_minutes <= 59)) { + throw kaitai::validation_greater_than_error(59, m_minutes, m__io, std::string("/types/smpte_offset/seq/3")); + } + if (!(m_hours >= 0)) { + throw kaitai::validation_less_than_error(0, m_hours, m__io, std::string("/types/smpte_offset/seq/4")); + } + if (!(m_hours <= 23)) { + throw kaitai::validation_greater_than_error(23, m_hours, m__io, std::string("/types/smpte_offset/seq/4")); + } + m__dirty = false; +} + +mpc60_seq_body_t::smpte_offset_t::~smpte_offset_t() {} + +mpc60_seq_body_t::u3le_t::u3le_t(kaitai::kstream* p__io, mpc60_seq_body_t::sequence_header_t* p__parent, mpc60_seq_body_t* p__root) : kaitai::kstruct(p__io) { + m__parent = p__parent; + m__root = p__root; + m__dirty = false; + f_value = false; +} + +void mpc60_seq_body_t::u3le_t::_read() { + m_b12 = m__io->read_u2le(); + m_b3 = m__io->read_u1(); + m__dirty = false; +} + +void mpc60_seq_body_t::u3le_t::_fetch_instances() { +} + +void mpc60_seq_body_t::u3le_t::_write() { + m__io->write_u2le(m_b12); + m__io->write_u1(m_b3); + _fetch_instances(); + m__dirty = false; +} + +void mpc60_seq_body_t::u3le_t::_check() { + m__dirty = false; +} + +mpc60_seq_body_t::u3le_t::~u3le_t() {} + +int32_t mpc60_seq_body_t::u3le_t::value() { + if (f_value) + return m_value; + f_value = true; + m_value = b12() | b3() << 16; + return m_value; +} diff --git a/src/main/file/kaitai/generated/mpc60_seq_body.h b/src/main/file/kaitai/generated/mpc60_seq_body.h new file mode 100644 index 000000000..676c9b514 --- /dev/null +++ b/src/main/file/kaitai/generated/mpc60_seq_body.h @@ -0,0 +1,253 @@ +#pragma once + +// This is a generated file! Please edit source .ksy file and use kaitai-struct-compiler to rebuild + +class mpc60_seq_body_t; + +#include "kaitai/kaitaistruct.h" +#include +#include +#include "mpc3000_seq_v3.h" +#include +#include + +#if KAITAI_STRUCT_VERSION < 11000L +#error "Incompatible Kaitai Struct C++/STL API: version 0.11 or later is required" +#endif + +class mpc60_seq_body_t : public kaitai::kstruct { + +public: + class sequence_t; + class sequence_header_t; + class smpte_offset_t; + class u3le_t; + + enum off_on_t { + OFF_ON_FALSE = 0, + OFF_ON_TRUE = 1 + }; + static bool _is_defined_off_on_t(off_on_t v); + +private: + static const std::set _values_off_on_t; + +public: + +private: + bool m__dirty; + +public: + + mpc60_seq_body_t(kaitai::kstream* p__io, kaitai::kstruct* p__parent = nullptr, mpc60_seq_body_t* p__root = nullptr); + void _read(); + void _fetch_instances(); + void _write(); + void _check(); + ~mpc60_seq_body_t(); + + class sequence_t : public kaitai::kstruct { + + public: + + private: + bool m__dirty; + + public: + + sequence_t(kaitai::kstream* p__io, kaitai::kstruct* p__parent = nullptr, mpc60_seq_body_t* p__root = nullptr); + void _read(); + void _fetch_instances(); + void _write(); + void _check(); + ~sequence_t(); + int32_t events_start(); + void _invalidate_events_start() { f_events_start = false; } + sequence_header_t* sequence_header() const { return m_sequence_header.get(); } + void set_sequence_header(std::unique_ptr _v) { m__dirty = true; f_events_start = false; m_sequence_header = std::move(_v); } + std::vector>* track_headers() const { return m_track_headers.get(); } + void set_track_headers(std::unique_ptr>> _v) { m__dirty = true; f_events_start = false; m_track_headers = std::move(_v); } + std::vector>* tempo_changes() const { return m_tempo_changes.get(); } + void set_tempo_changes(std::unique_ptr>> _v) { m__dirty = true; f_events_start = false; m_tempo_changes = std::move(_v); } + std::vector>* events() const { return m_events.get(); } + void set_events(std::unique_ptr>> _v) { m__dirty = true; f_events_start = false; m_events = std::move(_v); } + mpc60_seq_body_t* _root() const { return m__root; } + void set__root(mpc60_seq_body_t* _v) { m__dirty = true; f_events_start = false; m__root = std::move(_v); } + kaitai::kstruct* _parent() const { return m__parent; } + void set__parent(kaitai::kstruct* _v) { m__dirty = true; f_events_start = false; m__parent = std::move(_v); } + + private: + bool f_events_start; + int32_t m_events_start; + std::unique_ptr m_sequence_header; + std::unique_ptr>> m_track_headers; + std::unique_ptr>> m_tempo_changes; + std::unique_ptr>> m_events; + mpc60_seq_body_t* m__root; + kaitai::kstruct* m__parent; + }; + + class sequence_header_t : public kaitai::kstruct { + + public: + + private: + bool m__dirty; + + public: + + sequence_header_t(kaitai::kstream* p__io, mpc60_seq_body_t::sequence_t* p__parent = nullptr, mpc60_seq_body_t* p__root = nullptr); + void _read(); + void _fetch_instances(); + void _write(); + void _check(); + ~sequence_header_t(); + uint8_t sequence_number() const { return m_sequence_number; } + void set_sequence_number(uint8_t _v) { m__dirty = true; m_sequence_number = std::move(_v); } + u3le_t* event_stream_length_in_bytes() const { return m_event_stream_length_in_bytes.get(); } + void set_event_stream_length_in_bytes(std::unique_ptr _v) { m__dirty = true; m_event_stream_length_in_bytes = std::move(_v); } + u3le_t* offset_from_bottom_of_sequence_to_sequence_start() const { return m_offset_from_bottom_of_sequence_to_sequence_start.get(); } + void set_offset_from_bottom_of_sequence_to_sequence_start(std::unique_ptr _v) { m__dirty = true; m_offset_from_bottom_of_sequence_to_sequence_start = std::move(_v); } + std::string sequence_name() const { return m_sequence_name; } + void set_sequence_name(std::string _v) { m__dirty = true; m_sequence_name = std::move(_v); } + off_on_t loop_to_bar() const { return m_loop_to_bar; } + void set_loop_to_bar(off_on_t _v) { m__dirty = true; m_loop_to_bar = std::move(_v); } + uint64_t _unnamed5() const { return m__unnamed5; } + void set__unnamed5(uint64_t _v) { m__dirty = true; m__unnamed5 = std::move(_v); } + uint16_t loop_to_bar_number() const { return m_loop_to_bar_number; } + void set_loop_to_bar_number(uint16_t _v) { m__dirty = true; m_loop_to_bar_number = std::move(_v); } + uint16_t number_of_bars() const { return m_number_of_bars; } + void set_number_of_bars(uint16_t _v) { m__dirty = true; m_number_of_bars = std::move(_v); } + uint32_t length_in_ticks() const { return m_length_in_ticks; } + void set_length_in_ticks(uint32_t _v) { m__dirty = true; m_length_in_ticks = std::move(_v); } + uint16_t tempo() const { return m_tempo; } + void set_tempo(uint16_t _v) { m__dirty = true; m_tempo = std::move(_v); } + smpte_offset_t* smpte_offset() const { return m_smpte_offset.get(); } + void set_smpte_offset(std::unique_ptr _v) { m__dirty = true; m_smpte_offset = std::move(_v); } + std::vector* stereo_mix() const { return m_stereo_mix.get(); } + void set_stereo_mix(std::unique_ptr> _v) { m__dirty = true; m_stereo_mix = std::move(_v); } + std::vector* stereo_pan() const { return m_stereo_pan.get(); } + void set_stereo_pan(std::unique_ptr> _v) { m__dirty = true; m_stereo_pan = std::move(_v); } + std::vector* echo_mix() const { return m_echo_mix.get(); } + void set_echo_mix(std::unique_ptr> _v) { m__dirty = true; m_echo_mix = std::move(_v); } + std::vector* drum_tuning() const { return m_drum_tuning.get(); } + void set_drum_tuning(std::unique_ptr> _v) { m__dirty = true; m_drum_tuning = std::move(_v); } + uint8_t last_active_user_track() const { return m_last_active_user_track; } + void set_last_active_user_track(uint8_t _v) { m__dirty = true; m_last_active_user_track = std::move(_v); } + uint8_t num_tempo_changes() const { return m_num_tempo_changes; } + void set_num_tempo_changes(uint8_t _v) { m__dirty = true; m_num_tempo_changes = std::move(_v); } + uint8_t num_track_headers() const { return m_num_track_headers; } + void set_num_track_headers(uint8_t _v) { m__dirty = true; m_num_track_headers = std::move(_v); } + mpc60_seq_body_t* _root() const { return m__root; } + void set__root(mpc60_seq_body_t* _v) { m__dirty = true; m__root = std::move(_v); } + kaitai::kstruct* _parent() const { return m__parent; } + void set__parent(mpc60_seq_body_t::sequence_t* _v) { m__dirty = true; m__parent = std::move(_v); } + + private: + uint8_t m_sequence_number; + std::unique_ptr m_event_stream_length_in_bytes; + std::unique_ptr m_offset_from_bottom_of_sequence_to_sequence_start; + std::string m_sequence_name; + off_on_t m_loop_to_bar; + uint64_t m__unnamed5; + uint16_t m_loop_to_bar_number; + uint16_t m_number_of_bars; + uint32_t m_length_in_ticks; + uint16_t m_tempo; + std::unique_ptr m_smpte_offset; + std::unique_ptr> m_stereo_mix; + std::unique_ptr> m_stereo_pan; + std::unique_ptr> m_echo_mix; + std::unique_ptr> m_drum_tuning; + uint8_t m_last_active_user_track; + uint8_t m_num_tempo_changes; + uint8_t m_num_track_headers; + mpc60_seq_body_t* m__root; + mpc60_seq_body_t::sequence_t* m__parent; + }; + + class smpte_offset_t : public kaitai::kstruct { + + public: + + private: + bool m__dirty; + + public: + + smpte_offset_t(kaitai::kstream* p__io, mpc60_seq_body_t::sequence_header_t* p__parent = nullptr, mpc60_seq_body_t* p__root = nullptr); + void _read(); + void _fetch_instances(); + void _write(); + void _check(); + ~smpte_offset_t(); + uint8_t hundredth_frames() const { return m_hundredth_frames; } + void set_hundredth_frames(uint8_t _v) { m__dirty = true; m_hundredth_frames = std::move(_v); } + uint8_t frames() const { return m_frames; } + void set_frames(uint8_t _v) { m__dirty = true; m_frames = std::move(_v); } + uint8_t seconds() const { return m_seconds; } + void set_seconds(uint8_t _v) { m__dirty = true; m_seconds = std::move(_v); } + uint8_t minutes() const { return m_minutes; } + void set_minutes(uint8_t _v) { m__dirty = true; m_minutes = std::move(_v); } + uint8_t hours() const { return m_hours; } + void set_hours(uint8_t _v) { m__dirty = true; m_hours = std::move(_v); } + mpc60_seq_body_t* _root() const { return m__root; } + void set__root(mpc60_seq_body_t* _v) { m__dirty = true; m__root = std::move(_v); } + kaitai::kstruct* _parent() const { return m__parent; } + void set__parent(mpc60_seq_body_t::sequence_header_t* _v) { m__dirty = true; m__parent = std::move(_v); } + + private: + uint8_t m_hundredth_frames; + uint8_t m_frames; + uint8_t m_seconds; + uint8_t m_minutes; + uint8_t m_hours; + mpc60_seq_body_t* m__root; + mpc60_seq_body_t::sequence_header_t* m__parent; + }; + + class u3le_t : public kaitai::kstruct { + + public: + + private: + bool m__dirty; + + public: + + u3le_t(kaitai::kstream* p__io, mpc60_seq_body_t::sequence_header_t* p__parent = nullptr, mpc60_seq_body_t* p__root = nullptr); + void _read(); + void _fetch_instances(); + void _write(); + void _check(); + ~u3le_t(); + int32_t value(); + void _invalidate_value() { f_value = false; } + uint16_t b12() const { return m_b12; } + void set_b12(uint16_t _v) { m__dirty = true; f_value = false; m_b12 = std::move(_v); } + uint8_t b3() const { return m_b3; } + void set_b3(uint8_t _v) { m__dirty = true; f_value = false; m_b3 = std::move(_v); } + mpc60_seq_body_t* _root() const { return m__root; } + void set__root(mpc60_seq_body_t* _v) { m__dirty = true; f_value = false; m__root = std::move(_v); } + kaitai::kstruct* _parent() const { return m__parent; } + void set__parent(mpc60_seq_body_t::sequence_header_t* _v) { m__dirty = true; f_value = false; m__parent = std::move(_v); } + + private: + bool f_value; + int32_t m_value; + uint16_t m_b12; + uint8_t m_b3; + mpc60_seq_body_t* m__root; + mpc60_seq_body_t::sequence_header_t* m__parent; + }; + +public: + mpc60_seq_body_t* _root() const { return m__root; } + void set__root(mpc60_seq_body_t* _v) { m__dirty = true; m__root = std::move(_v); } + kaitai::kstruct* _parent() const { return m__parent; } + void set__parent(kaitai::kstruct* _v) { m__dirty = true; m__parent = std::move(_v); } + +private: + mpc60_seq_body_t* m__root; + kaitai::kstruct* m__parent; +}; diff --git a/src/main/file/kaitai/generated/mpc60_seq_v2.cpp b/src/main/file/kaitai/generated/mpc60_seq_v2.cpp new file mode 100644 index 000000000..30cc6f9cd --- /dev/null +++ b/src/main/file/kaitai/generated/mpc60_seq_v2.cpp @@ -0,0 +1,64 @@ +// This is a generated file! Please edit source .ksy file and use kaitai-struct-compiler to rebuild + +#include "mpc60_seq_v2.h" +#include "kaitai/exceptions.h" +#include + +mpc60_seq_v2_t::mpc60_seq_v2_t(kaitai::kstream* p__io, kaitai::kstruct* p__parent, mpc60_seq_v2_t* p__root) : kaitai::kstruct(p__io) { + m__parent = p__parent; + m__root = p__root ? p__root : this; + m__dirty = false; +} + +void mpc60_seq_v2_t::_read() { + m_file_id = m__io->read_bytes(1); + if (!(m_file_id == std::string("\x03", 1))) { + throw kaitai::validation_not_equal_error(std::string("\x03", 1), m_file_id, m__io, std::string("/seq/0")); + } + m_file_format_version = m__io->read_bytes(1); + if (!(m_file_format_version == std::string("\x02", 1))) { + throw kaitai::validation_not_equal_error(std::string("\x02", 1), m_file_format_version, m__io, std::string("/seq/1")); + } + m_sequence = std::unique_ptr(new mpc60_seq_body_t::sequence_t(m__io)); + m_sequence->_read(); + m__dirty = false; +} + +void mpc60_seq_v2_t::_fetch_instances() { + m_sequence.get()->_fetch_instances(); +} + +void mpc60_seq_v2_t::_write() { + m__io->write_bytes(m_file_id); + m__io->write_bytes(m_file_format_version); + if (m_sequence.get() == nullptr) { + throw std::runtime_error("/seq/2: nested object is not set"); + } + m_sequence.get()->_set_io(m__io); + m_sequence.get()->_write(); + _fetch_instances(); + m__dirty = false; +} + +void mpc60_seq_v2_t::_check() { + if (m_file_id.size() != static_cast(1)) { + throw std::runtime_error("/seq/0: size mismatch"); + } + if (!(m_file_id == std::string("\x03", 1))) { + throw kaitai::validation_not_equal_error(std::string("\x03", 1), m_file_id, m__io, std::string("/seq/0")); + } + if (m_file_format_version.size() != static_cast(1)) { + throw std::runtime_error("/seq/1: size mismatch"); + } + if (!(m_file_format_version == std::string("\x02", 1))) { + throw kaitai::validation_not_equal_error(std::string("\x02", 1), m_file_format_version, m__io, std::string("/seq/1")); + } + if (m_sequence.get() == nullptr) { + throw std::runtime_error("/seq/2: nested object is not set"); + } + m_sequence.get()->_set_io(m__io); + m_sequence.get()->_check(); + m__dirty = false; +} + +mpc60_seq_v2_t::~mpc60_seq_v2_t() {} diff --git a/src/main/file/kaitai/generated/mpc60_seq_v2.h b/src/main/file/kaitai/generated/mpc60_seq_v2.h new file mode 100644 index 000000000..5d668ce71 --- /dev/null +++ b/src/main/file/kaitai/generated/mpc60_seq_v2.h @@ -0,0 +1,48 @@ +#pragma once + +// This is a generated file! Please edit source .ksy file and use kaitai-struct-compiler to rebuild + +class mpc60_seq_v2_t; + +#include "kaitai/kaitaistruct.h" +#include +#include +#include "mpc60_seq_body.h" + +#if KAITAI_STRUCT_VERSION < 11000L +#error "Incompatible Kaitai Struct C++/STL API: version 0.11 or later is required" +#endif + +class mpc60_seq_v2_t : public kaitai::kstruct { + +public: + +private: + bool m__dirty; + +public: + + mpc60_seq_v2_t(kaitai::kstream* p__io, kaitai::kstruct* p__parent = nullptr, mpc60_seq_v2_t* p__root = nullptr); + void _read(); + void _fetch_instances(); + void _write(); + void _check(); + ~mpc60_seq_v2_t(); + std::string file_id() const { return m_file_id; } + void set_file_id(std::string _v) { m__dirty = true; m_file_id = std::move(_v); } + std::string file_format_version() const { return m_file_format_version; } + void set_file_format_version(std::string _v) { m__dirty = true; m_file_format_version = std::move(_v); } + mpc60_seq_body_t::sequence_t* sequence() const { return m_sequence.get(); } + void set_sequence(std::unique_ptr _v) { m__dirty = true; m_sequence = std::move(_v); } + mpc60_seq_v2_t* _root() const { return m__root; } + void set__root(mpc60_seq_v2_t* _v) { m__dirty = true; m__root = std::move(_v); } + kaitai::kstruct* _parent() const { return m__parent; } + void set__parent(kaitai::kstruct* _v) { m__dirty = true; m__parent = std::move(_v); } + +private: + std::string m_file_id; + std::string m_file_format_version; + std::unique_ptr m_sequence; + mpc60_seq_v2_t* m__root; + kaitai::kstruct* m__parent; +}; diff --git a/src/main/file/kaitai/generated/mpc60_set_v1.cpp b/src/main/file/kaitai/generated/mpc60_set_v1.cpp new file mode 100644 index 000000000..fc2912fbf --- /dev/null +++ b/src/main/file/kaitai/generated/mpc60_set_v1.cpp @@ -0,0 +1,445 @@ +// This is a generated file! Please edit source .ksy file and use kaitai-struct-compiler to rebuild + +#include "mpc60_set_v1.h" +#include "kaitai/exceptions.h" +#include +#include +const std::set mpc60_set_v1_t::_values_analog_output_jack_t{ + mpc60_set_v1_t::ANALOG_OUTPUT_JACK_FALSE, + mpc60_set_v1_t::ANALOG_OUTPUT_JACK_JACK_1, + mpc60_set_v1_t::ANALOG_OUTPUT_JACK_JACK_2, + mpc60_set_v1_t::ANALOG_OUTPUT_JACK_JACK_3, + mpc60_set_v1_t::ANALOG_OUTPUT_JACK_JACK_4, + mpc60_set_v1_t::ANALOG_OUTPUT_JACK_JACK_5, + mpc60_set_v1_t::ANALOG_OUTPUT_JACK_JACK_6, + mpc60_set_v1_t::ANALOG_OUTPUT_JACK_JACK_7, + mpc60_set_v1_t::ANALOG_OUTPUT_JACK_JACK_8, +}; +bool mpc60_set_v1_t::_is_defined_analog_output_jack_t(mpc60_set_v1_t::analog_output_jack_t v) { + return mpc60_set_v1_t::_values_analog_output_jack_t.find(v) != mpc60_set_v1_t::_values_analog_output_jack_t.end(); +} +const std::set mpc60_set_v1_t::_values_filter_range_t{ + mpc60_set_v1_t::FILTER_RANGE_LESS_THAN_OR_EQUALS_47_KHZ, + mpc60_set_v1_t::FILTER_RANGE_GREATER_THAN_47_KHZ, +}; +bool mpc60_set_v1_t::_is_defined_filter_range_t(mpc60_set_v1_t::filter_range_t v) { + return mpc60_set_v1_t::_values_filter_range_t.find(v) != mpc60_set_v1_t::_values_filter_range_t.end(); +} +const std::set mpc60_set_v1_t::_values_mpc60_pad_t{ + mpc60_set_v1_t::MPC60_PAD_HIHT_CLSD, + mpc60_set_v1_t::MPC60_PAD_HIHT_MEDM, + mpc60_set_v1_t::MPC60_PAD_HIHT_OPEN, + mpc60_set_v1_t::MPC60_PAD_SNR1, + mpc60_set_v1_t::MPC60_PAD_SNR2, + mpc60_set_v1_t::MPC60_PAD_BASS, + mpc60_set_v1_t::MPC60_PAD_TOM1, + mpc60_set_v1_t::MPC60_PAD_TOM2, + mpc60_set_v1_t::MPC60_PAD_TOM3, + mpc60_set_v1_t::MPC60_PAD_TOM4, + mpc60_set_v1_t::MPC60_PAD_RID1, + mpc60_set_v1_t::MPC60_PAD_RID2, + mpc60_set_v1_t::MPC60_PAD_CRS1, + mpc60_set_v1_t::MPC60_PAD_CRS2, + mpc60_set_v1_t::MPC60_PAD_PRC1, + mpc60_set_v1_t::MPC60_PAD_PRC2, + mpc60_set_v1_t::MPC60_PAD_PRC3, + mpc60_set_v1_t::MPC60_PAD_PRC4, + mpc60_set_v1_t::MPC60_PAD_DR01, + mpc60_set_v1_t::MPC60_PAD_DR02, + mpc60_set_v1_t::MPC60_PAD_DR03, + mpc60_set_v1_t::MPC60_PAD_DR04, + mpc60_set_v1_t::MPC60_PAD_DR05, + mpc60_set_v1_t::MPC60_PAD_DR06, + mpc60_set_v1_t::MPC60_PAD_DR07, + mpc60_set_v1_t::MPC60_PAD_DR08, + mpc60_set_v1_t::MPC60_PAD_DR09, + mpc60_set_v1_t::MPC60_PAD_DR10, + mpc60_set_v1_t::MPC60_PAD_DR11, + mpc60_set_v1_t::MPC60_PAD_DR12, + mpc60_set_v1_t::MPC60_PAD_DR13, + mpc60_set_v1_t::MPC60_PAD_DR14, + mpc60_set_v1_t::MPC60_PAD_DR15, + mpc60_set_v1_t::MPC60_PAD_DR16, +}; +bool mpc60_set_v1_t::_is_defined_mpc60_pad_t(mpc60_set_v1_t::mpc60_pad_t v) { + return mpc60_set_v1_t::_values_mpc60_pad_t.find(v) != mpc60_set_v1_t::_values_mpc60_pad_t.end(); +} +const std::set mpc60_set_v1_t::_values_normal_or_hihat_t{ + mpc60_set_v1_t::NORMAL_OR_HIHAT_NORMAL, + mpc60_set_v1_t::NORMAL_OR_HIHAT_HIHAT, +}; +bool mpc60_set_v1_t::_is_defined_normal_or_hihat_t(mpc60_set_v1_t::normal_or_hihat_t v) { + return mpc60_set_v1_t::_values_normal_or_hihat_t.find(v) != mpc60_set_v1_t::_values_normal_or_hihat_t.end(); +} +const std::set mpc60_set_v1_t::_values_use_master_mix_data_t{ + mpc60_set_v1_t::USE_MASTER_MIX_DATA_IGNORE, + mpc60_set_v1_t::USE_MASTER_MIX_DATA_USE, +}; +bool mpc60_set_v1_t::_is_defined_use_master_mix_data_t(mpc60_set_v1_t::use_master_mix_data_t v) { + return mpc60_set_v1_t::_values_use_master_mix_data_t.find(v) != mpc60_set_v1_t::_values_use_master_mix_data_t.end(); +} + +mpc60_set_v1_t::mpc60_set_v1_t(kaitai::kstream* p__io, kaitai::kstruct* p__parent, mpc60_set_v1_t* p__root) : kaitai::kstruct(p__io) { + m__parent = p__parent; + m__root = p__root ? p__root : this; + m__dirty = false; +} + +void mpc60_set_v1_t::_read() { + m_file_id = m__io->read_bytes(1); + if (!(m_file_id == std::string("\x02", 1))) { + throw kaitai::validation_not_equal_error(std::string("\x02", 1), m_file_id, m__io, std::string("/seq/0")); + } + m_file_format_version = m__io->read_bytes(1); + if (!(m_file_format_version == std::string("\x00", 1))) { + throw kaitai::validation_not_equal_error(std::string("\x00", 1), m_file_format_version, m__io, std::string("/seq/1")); + } + m_total_number_of_sample_words = std::unique_ptr(new u3le_t(m__io, this, m__root)); + m_total_number_of_sample_words->_read(); + m_sound_directory_entry = std::unique_ptr>>(new std::vector>()); + const int l_sound_directory_entry = 34; + for (int i = 0; i < l_sound_directory_entry; i++) { + std::unique_ptr _t_sound_directory_entry = std::unique_ptr(new sound_directory_entry_t(m__io, this, m__root)); + try { + _t_sound_directory_entry->_read(); + } catch(...) { + m_sound_directory_entry->push_back(std::move(_t_sound_directory_entry)); + throw; + } + m_sound_directory_entry->push_back(std::move(_t_sound_directory_entry)); + } + m_sound_map = std::unique_ptr>(new std::vector()); + const int l_sound_map = 34; + for (int i = 0; i < l_sound_map; i++) { + m_sound_map->push_back(std::move(m__io->read_u1())); + } + m_use_master_mix_data = static_cast(m__io->read_u1()); + m_master_stereo_mix = m__io->read_bytes(32); + m_master_stereo_pan = m__io->read_bytes(32); + m_master_echo_mix = m__io->read_bytes(32); + m_master_drum_tuning = m__io->read_bytes(64); + m_double_play_assignments = m__io->read_bytes(32); + m_velocity_switch_on_off_for_each_double_play_assign = m__io->read_bytes(32); + m_velocity_switch_threshold_value_for_each_double_play_assign = m__io->read_bytes(32); + m__unnamed13 = m__io->read_bytes(770); + m_sound_samples = std::unique_ptr>(new std::vector()); + { + int i = 0; + uint64_t _; + do { + _ = m__io->read_bits_int_le(12); + m_sound_samples->push_back(_); + i++; + } while (!(_io()->is_eof())); + } + m__dirty = false; +} + +void mpc60_set_v1_t::_fetch_instances() { + m_total_number_of_sample_words.get()->_fetch_instances(); + for (std::size_t i = 0; i < m_sound_directory_entry->size(); ++i) { + m_sound_directory_entry->at(i).get()->_fetch_instances(); + } + for (std::size_t i = 0; i < m_sound_map->size(); ++i) { + } + for (std::size_t i = 0; i < m_sound_samples->size(); ++i) { + } +} + +void mpc60_set_v1_t::_write() { + m__io->write_bytes(m_file_id); + m__io->write_bytes(m_file_format_version); + if (m_total_number_of_sample_words.get() == nullptr) { + throw std::runtime_error("/seq/2: nested object is not set"); + } + m_total_number_of_sample_words.get()->_set_io(m__io); + m_total_number_of_sample_words.get()->_write(); + if (m_sound_directory_entry == nullptr) { + throw std::runtime_error("/seq/3: repeated field is not set"); + } + for (std::vector>::const_iterator it = m_sound_directory_entry->begin(); it != m_sound_directory_entry->end(); ++it) { + if ((*it).get() == nullptr) { + throw std::runtime_error("/seq/3: nested object is not set"); + } + (*it).get()->_set_io(m__io); + (*it).get()->_write(); + } + if (m_sound_map == nullptr) { + throw std::runtime_error("/seq/4: repeated field is not set"); + } + for (std::vector::const_iterator it = m_sound_map->begin(); it != m_sound_map->end(); ++it) { + m__io->write_u1((*it)); + } + m__io->write_u1(static_cast(m_use_master_mix_data)); + m__io->write_bytes(m_master_stereo_mix); + m__io->write_bytes(m_master_stereo_pan); + m__io->write_bytes(m_master_echo_mix); + m__io->write_bytes(m_master_drum_tuning); + m__io->write_bytes(m_double_play_assignments); + m__io->write_bytes(m_velocity_switch_on_off_for_each_double_play_assign); + m__io->write_bytes(m_velocity_switch_threshold_value_for_each_double_play_assign); + m__io->write_bytes(m__unnamed13); + if (m_sound_samples == nullptr) { + throw std::runtime_error("/seq/14: repeated field is not set"); + } + for (std::vector::const_iterator it = m_sound_samples->begin(); it != m_sound_samples->end(); ++it) { + m__io->write_bits_int_le(12, (*it)); + } + _fetch_instances(); + m__dirty = false; +} + +void mpc60_set_v1_t::_check() { + if (m_file_id.size() != static_cast(1)) { + throw std::runtime_error("/seq/0: size mismatch"); + } + if (!(m_file_id == std::string("\x02", 1))) { + throw kaitai::validation_not_equal_error(std::string("\x02", 1), m_file_id, m__io, std::string("/seq/0")); + } + if (m_file_format_version.size() != static_cast(1)) { + throw std::runtime_error("/seq/1: size mismatch"); + } + if (!(m_file_format_version == std::string("\x00", 1))) { + throw kaitai::validation_not_equal_error(std::string("\x00", 1), m_file_format_version, m__io, std::string("/seq/1")); + } + if (m_total_number_of_sample_words.get() == nullptr) { + throw std::runtime_error("/seq/2: nested object is not set"); + } + m_total_number_of_sample_words.get()->_set_io(m__io); + m_total_number_of_sample_words.get()->_check(); + if (m_sound_directory_entry == nullptr) { + throw std::runtime_error("/seq/3: repeated field is not set"); + } + if (m_sound_directory_entry->size() != static_cast(34)) { + throw std::runtime_error("/seq/3: repeat-expr size mismatch"); + } + for (std::vector>::const_iterator it = m_sound_directory_entry->begin(); it != m_sound_directory_entry->end(); ++it) { + if ((*it).get() == nullptr) { + throw std::runtime_error("/seq/3: nested object is not set"); + } + (*it).get()->_set_io(m__io); + (*it).get()->_check(); + } + if (m_sound_map == nullptr) { + throw std::runtime_error("/seq/4: repeated field is not set"); + } + if (m_sound_map->size() != static_cast(34)) { + throw std::runtime_error("/seq/4: repeat-expr size mismatch"); + } + for (std::vector::const_iterator it = m_sound_map->begin(); it != m_sound_map->end(); ++it) { + } + if (m_master_stereo_mix.size() != static_cast(32)) { + throw std::runtime_error("/seq/6: size mismatch"); + } + if (m_master_stereo_pan.size() != static_cast(32)) { + throw std::runtime_error("/seq/7: size mismatch"); + } + if (m_master_echo_mix.size() != static_cast(32)) { + throw std::runtime_error("/seq/8: size mismatch"); + } + if (m_master_drum_tuning.size() != static_cast(64)) { + throw std::runtime_error("/seq/9: size mismatch"); + } + if (m_double_play_assignments.size() != static_cast(32)) { + throw std::runtime_error("/seq/10: size mismatch"); + } + if (m_velocity_switch_on_off_for_each_double_play_assign.size() != static_cast(32)) { + throw std::runtime_error("/seq/11: size mismatch"); + } + if (m_velocity_switch_threshold_value_for_each_double_play_assign.size() != static_cast(32)) { + throw std::runtime_error("/seq/12: size mismatch"); + } + if (m__unnamed13.size() != static_cast(770)) { + throw std::runtime_error("/seq/13: size mismatch"); + } + if (m_sound_samples == nullptr) { + throw std::runtime_error("/seq/14: repeated field is not set"); + } + if (m_sound_samples->empty()) { + throw std::runtime_error("/seq/14: repeat-until field must not be empty"); + } + for (std::vector::const_iterator it = m_sound_samples->begin(); it != m_sound_samples->end(); ++it) { + const std::size_t i = static_cast(it - m_sound_samples->begin()); + const uint64_t _ = (*it); + const bool _is_last = (i == m_sound_samples->size() - 1); + if ((_io()->is_eof()) != _is_last) { + throw std::runtime_error("/seq/14: repeat-until condition mismatch"); + } + } + m__dirty = false; +} + +mpc60_set_v1_t::~mpc60_set_v1_t() {} + +mpc60_set_v1_t::sound_directory_entry_t::sound_directory_entry_t(kaitai::kstream* p__io, mpc60_set_v1_t* p__parent, mpc60_set_v1_t* p__root) : kaitai::kstruct(p__io) { + m__parent = p__parent; + m__root = p__root; + m__dirty = false; +} + +void mpc60_set_v1_t::sound_directory_entry_t::_read() { + m_name = kaitai::kstream::bytes_to_str(kaitai::kstream::bytes_terminate(m__io->read_bytes(17), static_cast(0), false), "ASCII"); + m_shared_sound_link = m__io->read_u1(); + if (!(m_shared_sound_link >= 0)) { + throw kaitai::validation_less_than_error(0, m_shared_sound_link, m__io, std::string("/types/sound_directory_entry/seq/1")); + } + if (!(m_shared_sound_link <= 33)) { + throw kaitai::validation_greater_than_error(33, m_shared_sound_link, m__io, std::string("/types/sound_directory_entry/seq/1")); + } + m_start_address_in_memory = m__io->read_u4le(); + m_length_in_samples = m__io->read_u4le(); + m_read_write_pointer = m__io->read_u4le(); + m_start_address_for_playing = m__io->read_u4le(); + m_time_from_start_of_data_to_start_play_msec = m__io->read_u2le(); + m_time_from_start_of_data_to_end_of_play_msec = m__io->read_u2le(); + m_attack_time_msec = m__io->read_u2le(); + m_decay_time_msec = m__io->read_u2le(); + m_pitch_factor = m__io->read_u2le(); + m_attack_rate_sent_to_sound_generator = m__io->read_u2le(); + m_decay_rate_sent_to_sound_generator = m__io->read_u2le(); + m_decay_start_time = m__io->read_u2le(); + m_sound_duration = m__io->read_u2le(); + m_analog_output_jack = static_cast(m__io->read_u1()); + m_requested_stereo_mix_volume = m__io->read_u1(); + m_requested_stereo_mix_pan = m__io->read_u1(); + m_left_level_sent_to_sound_generator = m__io->read_u1(); + m_right_level_sent_to_sound_generator = m__io->read_u1(); + m_echo_level_sent_to_sound_generator = m__io->read_u1(); + m_sound_characteristics = std::unique_ptr(new sound_characteristics_t(m__io, this, m__root)); + m_sound_characteristics->_read(); + m__dirty = false; +} + +void mpc60_set_v1_t::sound_directory_entry_t::_fetch_instances() { + m_sound_characteristics.get()->_fetch_instances(); +} + +void mpc60_set_v1_t::sound_directory_entry_t::_write() { + std::string _bufm_name = m_name; + if (_bufm_name.size() < static_cast(17)) { + _bufm_name += std::string(1, static_cast(0)); + } + if (_bufm_name.size() < static_cast(17)) { + _bufm_name.append(static_cast(17) - _bufm_name.size(), static_cast(0)); + } + m__io->write_bytes(_bufm_name); + m__io->write_u1(m_shared_sound_link); + m__io->write_u4le(m_start_address_in_memory); + m__io->write_u4le(m_length_in_samples); + m__io->write_u4le(m_read_write_pointer); + m__io->write_u4le(m_start_address_for_playing); + m__io->write_u2le(m_time_from_start_of_data_to_start_play_msec); + m__io->write_u2le(m_time_from_start_of_data_to_end_of_play_msec); + m__io->write_u2le(m_attack_time_msec); + m__io->write_u2le(m_decay_time_msec); + m__io->write_u2le(m_pitch_factor); + m__io->write_u2le(m_attack_rate_sent_to_sound_generator); + m__io->write_u2le(m_decay_rate_sent_to_sound_generator); + m__io->write_u2le(m_decay_start_time); + m__io->write_u2le(m_sound_duration); + m__io->write_u1(static_cast(m_analog_output_jack)); + m__io->write_u1(m_requested_stereo_mix_volume); + m__io->write_u1(m_requested_stereo_mix_pan); + m__io->write_u1(m_left_level_sent_to_sound_generator); + m__io->write_u1(m_right_level_sent_to_sound_generator); + m__io->write_u1(m_echo_level_sent_to_sound_generator); + if (m_sound_characteristics.get() == nullptr) { + throw std::runtime_error("/types/sound_directory_entry/seq/21: nested object is not set"); + } + m_sound_characteristics.get()->_set_io(m__io); + m_sound_characteristics.get()->_write(); + _fetch_instances(); + m__dirty = false; +} + +void mpc60_set_v1_t::sound_directory_entry_t::_check() { + if (m_name.size() > static_cast(17)) { + throw std::runtime_error("/types/sound_directory_entry/seq/0: size mismatch"); + } + if (m_name.find(static_cast(0)) != std::string::npos) { + throw std::runtime_error("/types/sound_directory_entry/seq/0: terminator must not appear in value"); + } + if (!(m_shared_sound_link >= 0)) { + throw kaitai::validation_less_than_error(0, m_shared_sound_link, m__io, std::string("/types/sound_directory_entry/seq/1")); + } + if (!(m_shared_sound_link <= 33)) { + throw kaitai::validation_greater_than_error(33, m_shared_sound_link, m__io, std::string("/types/sound_directory_entry/seq/1")); + } + if (m_sound_characteristics.get() == nullptr) { + throw std::runtime_error("/types/sound_directory_entry/seq/21: nested object is not set"); + } + m_sound_characteristics.get()->_set_io(m__io); + m_sound_characteristics.get()->_check(); + m__dirty = false; +} + +mpc60_set_v1_t::sound_directory_entry_t::~sound_directory_entry_t() {} + +mpc60_set_v1_t::sound_directory_entry_t::sound_characteristics_t::sound_characteristics_t(kaitai::kstream* p__io, mpc60_set_v1_t::sound_directory_entry_t* p__parent, mpc60_set_v1_t* p__root) : kaitai::kstruct(p__io) { + m__parent = p__parent; + m__root = p__root; + m__dirty = false; +} + +void mpc60_set_v1_t::sound_directory_entry_t::sound_characteristics_t::_read() { + m_normal_or_hihat_sound = static_cast(m__io->read_bits_int_le(1)); + m_save_required = m__io->read_bits_int_le(1); + m__unnamed2 = m__io->read_bits_int_le(5); + m_filter = static_cast(m__io->read_bits_int_le(1)); + m__dirty = false; +} + +void mpc60_set_v1_t::sound_directory_entry_t::sound_characteristics_t::_fetch_instances() { +} + +void mpc60_set_v1_t::sound_directory_entry_t::sound_characteristics_t::_write() { + m__io->write_bits_int_le(1, static_cast(m_normal_or_hihat_sound)); + m__io->write_bits_int_le(1, ((m_save_required) ? 1 : 0)); + m__io->write_bits_int_le(5, m__unnamed2); + m__io->write_bits_int_le(1, static_cast(m_filter)); + _fetch_instances(); + m__dirty = false; +} + +void mpc60_set_v1_t::sound_directory_entry_t::sound_characteristics_t::_check() { + m__dirty = false; +} + +mpc60_set_v1_t::sound_directory_entry_t::sound_characteristics_t::~sound_characteristics_t() {} + +mpc60_set_v1_t::u3le_t::u3le_t(kaitai::kstream* p__io, mpc60_set_v1_t* p__parent, mpc60_set_v1_t* p__root) : kaitai::kstruct(p__io) { + m__parent = p__parent; + m__root = p__root; + m__dirty = false; + f_value = false; +} + +void mpc60_set_v1_t::u3le_t::_read() { + m_b12 = m__io->read_u2le(); + m_b3 = m__io->read_u1(); + m__dirty = false; +} + +void mpc60_set_v1_t::u3le_t::_fetch_instances() { +} + +void mpc60_set_v1_t::u3le_t::_write() { + m__io->write_u2le(m_b12); + m__io->write_u1(m_b3); + _fetch_instances(); + m__dirty = false; +} + +void mpc60_set_v1_t::u3le_t::_check() { + m__dirty = false; +} + +mpc60_set_v1_t::u3le_t::~u3le_t() {} + +int32_t mpc60_set_v1_t::u3le_t::value() { + if (f_value) + return m_value; + f_value = true; + m_value = b12() | b3() << 16; + return m_value; +} diff --git a/src/main/file/kaitai/generated/mpc60_set_v1.h b/src/main/file/kaitai/generated/mpc60_set_v1.h new file mode 100644 index 000000000..78ddc0cfe --- /dev/null +++ b/src/main/file/kaitai/generated/mpc60_set_v1.h @@ -0,0 +1,349 @@ +#pragma once + +// This is a generated file! Please edit source .ksy file and use kaitai-struct-compiler to rebuild + +class mpc60_set_v1_t; + +#include "kaitai/kaitaistruct.h" +#include +#include +#include +#include + +#if KAITAI_STRUCT_VERSION < 11000L +#error "Incompatible Kaitai Struct C++/STL API: version 0.11 or later is required" +#endif + +class mpc60_set_v1_t : public kaitai::kstruct { + +public: + class sound_directory_entry_t; + class u3le_t; + + enum analog_output_jack_t { + ANALOG_OUTPUT_JACK_FALSE = 7, + ANALOG_OUTPUT_JACK_JACK_1 = 8, + ANALOG_OUTPUT_JACK_JACK_2 = 9, + ANALOG_OUTPUT_JACK_JACK_3 = 10, + ANALOG_OUTPUT_JACK_JACK_4 = 11, + ANALOG_OUTPUT_JACK_JACK_5 = 12, + ANALOG_OUTPUT_JACK_JACK_6 = 13, + ANALOG_OUTPUT_JACK_JACK_7 = 14, + ANALOG_OUTPUT_JACK_JACK_8 = 15 + }; + static bool _is_defined_analog_output_jack_t(analog_output_jack_t v); + +private: + static const std::set _values_analog_output_jack_t; + +public: + + enum filter_range_t { + FILTER_RANGE_LESS_THAN_OR_EQUALS_47_KHZ = 0, + FILTER_RANGE_GREATER_THAN_47_KHZ = 1 + }; + static bool _is_defined_filter_range_t(filter_range_t v); + +private: + static const std::set _values_filter_range_t; + +public: + + enum mpc60_pad_t { + MPC60_PAD_HIHT_CLSD = 0, + MPC60_PAD_HIHT_MEDM = 1, + MPC60_PAD_HIHT_OPEN = 2, + MPC60_PAD_SNR1 = 3, + MPC60_PAD_SNR2 = 4, + MPC60_PAD_BASS = 5, + MPC60_PAD_TOM1 = 6, + MPC60_PAD_TOM2 = 7, + MPC60_PAD_TOM3 = 8, + MPC60_PAD_TOM4 = 9, + MPC60_PAD_RID1 = 10, + MPC60_PAD_RID2 = 11, + MPC60_PAD_CRS1 = 12, + MPC60_PAD_CRS2 = 13, + MPC60_PAD_PRC1 = 14, + MPC60_PAD_PRC2 = 15, + MPC60_PAD_PRC3 = 16, + MPC60_PAD_PRC4 = 17, + MPC60_PAD_DR01 = 18, + MPC60_PAD_DR02 = 19, + MPC60_PAD_DR03 = 20, + MPC60_PAD_DR04 = 21, + MPC60_PAD_DR05 = 22, + MPC60_PAD_DR06 = 23, + MPC60_PAD_DR07 = 24, + MPC60_PAD_DR08 = 25, + MPC60_PAD_DR09 = 26, + MPC60_PAD_DR10 = 27, + MPC60_PAD_DR11 = 28, + MPC60_PAD_DR12 = 29, + MPC60_PAD_DR13 = 30, + MPC60_PAD_DR14 = 31, + MPC60_PAD_DR15 = 32, + MPC60_PAD_DR16 = 33 + }; + static bool _is_defined_mpc60_pad_t(mpc60_pad_t v); + +private: + static const std::set _values_mpc60_pad_t; + +public: + + enum normal_or_hihat_t { + NORMAL_OR_HIHAT_NORMAL = 0, + NORMAL_OR_HIHAT_HIHAT = 1 + }; + static bool _is_defined_normal_or_hihat_t(normal_or_hihat_t v); + +private: + static const std::set _values_normal_or_hihat_t; + +public: + + enum use_master_mix_data_t { + USE_MASTER_MIX_DATA_IGNORE = 0, + USE_MASTER_MIX_DATA_USE = 1 + }; + static bool _is_defined_use_master_mix_data_t(use_master_mix_data_t v); + +private: + static const std::set _values_use_master_mix_data_t; + +public: + +private: + bool m__dirty; + +public: + + mpc60_set_v1_t(kaitai::kstream* p__io, kaitai::kstruct* p__parent = nullptr, mpc60_set_v1_t* p__root = nullptr); + void _read(); + void _fetch_instances(); + void _write(); + void _check(); + ~mpc60_set_v1_t(); + + class sound_directory_entry_t : public kaitai::kstruct { + + public: + class sound_characteristics_t; + + private: + bool m__dirty; + + public: + + sound_directory_entry_t(kaitai::kstream* p__io, mpc60_set_v1_t* p__parent = nullptr, mpc60_set_v1_t* p__root = nullptr); + void _read(); + void _fetch_instances(); + void _write(); + void _check(); + ~sound_directory_entry_t(); + + class sound_characteristics_t : public kaitai::kstruct { + + public: + + private: + bool m__dirty; + + public: + + sound_characteristics_t(kaitai::kstream* p__io, mpc60_set_v1_t::sound_directory_entry_t* p__parent = nullptr, mpc60_set_v1_t* p__root = nullptr); + void _read(); + void _fetch_instances(); + void _write(); + void _check(); + ~sound_characteristics_t(); + normal_or_hihat_t normal_or_hihat_sound() const { return m_normal_or_hihat_sound; } + void set_normal_or_hihat_sound(normal_or_hihat_t _v) { m__dirty = true; m_normal_or_hihat_sound = std::move(_v); } + bool save_required() const { return m_save_required; } + void set_save_required(bool _v) { m__dirty = true; m_save_required = std::move(_v); } + uint64_t _unnamed2() const { return m__unnamed2; } + void set__unnamed2(uint64_t _v) { m__dirty = true; m__unnamed2 = std::move(_v); } + filter_range_t filter() const { return m_filter; } + void set_filter(filter_range_t _v) { m__dirty = true; m_filter = std::move(_v); } + mpc60_set_v1_t* _root() const { return m__root; } + void set__root(mpc60_set_v1_t* _v) { m__dirty = true; m__root = std::move(_v); } + kaitai::kstruct* _parent() const { return m__parent; } + void set__parent(mpc60_set_v1_t::sound_directory_entry_t* _v) { m__dirty = true; m__parent = std::move(_v); } + + private: + normal_or_hihat_t m_normal_or_hihat_sound; + bool m_save_required; + uint64_t m__unnamed2; + filter_range_t m_filter; + mpc60_set_v1_t* m__root; + mpc60_set_v1_t::sound_directory_entry_t* m__parent; + }; + + public: + std::string name() const { return m_name; } + void set_name(std::string _v) { m__dirty = true; m_name = std::move(_v); } + uint8_t shared_sound_link() const { return m_shared_sound_link; } + void set_shared_sound_link(uint8_t _v) { m__dirty = true; m_shared_sound_link = std::move(_v); } + uint32_t start_address_in_memory() const { return m_start_address_in_memory; } + void set_start_address_in_memory(uint32_t _v) { m__dirty = true; m_start_address_in_memory = std::move(_v); } + uint32_t length_in_samples() const { return m_length_in_samples; } + void set_length_in_samples(uint32_t _v) { m__dirty = true; m_length_in_samples = std::move(_v); } + uint32_t read_write_pointer() const { return m_read_write_pointer; } + void set_read_write_pointer(uint32_t _v) { m__dirty = true; m_read_write_pointer = std::move(_v); } + uint32_t start_address_for_playing() const { return m_start_address_for_playing; } + void set_start_address_for_playing(uint32_t _v) { m__dirty = true; m_start_address_for_playing = std::move(_v); } + uint16_t time_from_start_of_data_to_start_play_msec() const { return m_time_from_start_of_data_to_start_play_msec; } + void set_time_from_start_of_data_to_start_play_msec(uint16_t _v) { m__dirty = true; m_time_from_start_of_data_to_start_play_msec = std::move(_v); } + uint16_t time_from_start_of_data_to_end_of_play_msec() const { return m_time_from_start_of_data_to_end_of_play_msec; } + void set_time_from_start_of_data_to_end_of_play_msec(uint16_t _v) { m__dirty = true; m_time_from_start_of_data_to_end_of_play_msec = std::move(_v); } + uint16_t attack_time_msec() const { return m_attack_time_msec; } + void set_attack_time_msec(uint16_t _v) { m__dirty = true; m_attack_time_msec = std::move(_v); } + uint16_t decay_time_msec() const { return m_decay_time_msec; } + void set_decay_time_msec(uint16_t _v) { m__dirty = true; m_decay_time_msec = std::move(_v); } + uint16_t pitch_factor() const { return m_pitch_factor; } + void set_pitch_factor(uint16_t _v) { m__dirty = true; m_pitch_factor = std::move(_v); } + uint16_t attack_rate_sent_to_sound_generator() const { return m_attack_rate_sent_to_sound_generator; } + void set_attack_rate_sent_to_sound_generator(uint16_t _v) { m__dirty = true; m_attack_rate_sent_to_sound_generator = std::move(_v); } + uint16_t decay_rate_sent_to_sound_generator() const { return m_decay_rate_sent_to_sound_generator; } + void set_decay_rate_sent_to_sound_generator(uint16_t _v) { m__dirty = true; m_decay_rate_sent_to_sound_generator = std::move(_v); } + uint16_t decay_start_time() const { return m_decay_start_time; } + void set_decay_start_time(uint16_t _v) { m__dirty = true; m_decay_start_time = std::move(_v); } + uint16_t sound_duration() const { return m_sound_duration; } + void set_sound_duration(uint16_t _v) { m__dirty = true; m_sound_duration = std::move(_v); } + analog_output_jack_t analog_output_jack() const { return m_analog_output_jack; } + void set_analog_output_jack(analog_output_jack_t _v) { m__dirty = true; m_analog_output_jack = std::move(_v); } + uint8_t requested_stereo_mix_volume() const { return m_requested_stereo_mix_volume; } + void set_requested_stereo_mix_volume(uint8_t _v) { m__dirty = true; m_requested_stereo_mix_volume = std::move(_v); } + uint8_t requested_stereo_mix_pan() const { return m_requested_stereo_mix_pan; } + void set_requested_stereo_mix_pan(uint8_t _v) { m__dirty = true; m_requested_stereo_mix_pan = std::move(_v); } + uint8_t left_level_sent_to_sound_generator() const { return m_left_level_sent_to_sound_generator; } + void set_left_level_sent_to_sound_generator(uint8_t _v) { m__dirty = true; m_left_level_sent_to_sound_generator = std::move(_v); } + uint8_t right_level_sent_to_sound_generator() const { return m_right_level_sent_to_sound_generator; } + void set_right_level_sent_to_sound_generator(uint8_t _v) { m__dirty = true; m_right_level_sent_to_sound_generator = std::move(_v); } + uint8_t echo_level_sent_to_sound_generator() const { return m_echo_level_sent_to_sound_generator; } + void set_echo_level_sent_to_sound_generator(uint8_t _v) { m__dirty = true; m_echo_level_sent_to_sound_generator = std::move(_v); } + sound_characteristics_t* sound_characteristics() const { return m_sound_characteristics.get(); } + void set_sound_characteristics(std::unique_ptr _v) { m__dirty = true; m_sound_characteristics = std::move(_v); } + mpc60_set_v1_t* _root() const { return m__root; } + void set__root(mpc60_set_v1_t* _v) { m__dirty = true; m__root = std::move(_v); } + kaitai::kstruct* _parent() const { return m__parent; } + void set__parent(mpc60_set_v1_t* _v) { m__dirty = true; m__parent = std::move(_v); } + + private: + std::string m_name; + uint8_t m_shared_sound_link; + uint32_t m_start_address_in_memory; + uint32_t m_length_in_samples; + uint32_t m_read_write_pointer; + uint32_t m_start_address_for_playing; + uint16_t m_time_from_start_of_data_to_start_play_msec; + uint16_t m_time_from_start_of_data_to_end_of_play_msec; + uint16_t m_attack_time_msec; + uint16_t m_decay_time_msec; + uint16_t m_pitch_factor; + uint16_t m_attack_rate_sent_to_sound_generator; + uint16_t m_decay_rate_sent_to_sound_generator; + uint16_t m_decay_start_time; + uint16_t m_sound_duration; + analog_output_jack_t m_analog_output_jack; + uint8_t m_requested_stereo_mix_volume; + uint8_t m_requested_stereo_mix_pan; + uint8_t m_left_level_sent_to_sound_generator; + uint8_t m_right_level_sent_to_sound_generator; + uint8_t m_echo_level_sent_to_sound_generator; + std::unique_ptr m_sound_characteristics; + mpc60_set_v1_t* m__root; + mpc60_set_v1_t* m__parent; + }; + + class u3le_t : public kaitai::kstruct { + + public: + + private: + bool m__dirty; + + public: + + u3le_t(kaitai::kstream* p__io, mpc60_set_v1_t* p__parent = nullptr, mpc60_set_v1_t* p__root = nullptr); + void _read(); + void _fetch_instances(); + void _write(); + void _check(); + ~u3le_t(); + int32_t value(); + void _invalidate_value() { f_value = false; } + uint16_t b12() const { return m_b12; } + void set_b12(uint16_t _v) { m__dirty = true; f_value = false; m_b12 = std::move(_v); } + uint8_t b3() const { return m_b3; } + void set_b3(uint8_t _v) { m__dirty = true; f_value = false; m_b3 = std::move(_v); } + mpc60_set_v1_t* _root() const { return m__root; } + void set__root(mpc60_set_v1_t* _v) { m__dirty = true; f_value = false; m__root = std::move(_v); } + kaitai::kstruct* _parent() const { return m__parent; } + void set__parent(mpc60_set_v1_t* _v) { m__dirty = true; f_value = false; m__parent = std::move(_v); } + + private: + bool f_value; + int32_t m_value; + uint16_t m_b12; + uint8_t m_b3; + mpc60_set_v1_t* m__root; + mpc60_set_v1_t* m__parent; + }; + +public: + std::string file_id() const { return m_file_id; } + void set_file_id(std::string _v) { m__dirty = true; m_file_id = std::move(_v); } + std::string file_format_version() const { return m_file_format_version; } + void set_file_format_version(std::string _v) { m__dirty = true; m_file_format_version = std::move(_v); } + u3le_t* total_number_of_sample_words() const { return m_total_number_of_sample_words.get(); } + void set_total_number_of_sample_words(std::unique_ptr _v) { m__dirty = true; m_total_number_of_sample_words = std::move(_v); } + std::vector>* sound_directory_entry() const { return m_sound_directory_entry.get(); } + void set_sound_directory_entry(std::unique_ptr>> _v) { m__dirty = true; m_sound_directory_entry = std::move(_v); } + std::vector* sound_map() const { return m_sound_map.get(); } + void set_sound_map(std::unique_ptr> _v) { m__dirty = true; m_sound_map = std::move(_v); } + use_master_mix_data_t use_master_mix_data() const { return m_use_master_mix_data; } + void set_use_master_mix_data(use_master_mix_data_t _v) { m__dirty = true; m_use_master_mix_data = std::move(_v); } + std::string master_stereo_mix() const { return m_master_stereo_mix; } + void set_master_stereo_mix(std::string _v) { m__dirty = true; m_master_stereo_mix = std::move(_v); } + std::string master_stereo_pan() const { return m_master_stereo_pan; } + void set_master_stereo_pan(std::string _v) { m__dirty = true; m_master_stereo_pan = std::move(_v); } + std::string master_echo_mix() const { return m_master_echo_mix; } + void set_master_echo_mix(std::string _v) { m__dirty = true; m_master_echo_mix = std::move(_v); } + std::string master_drum_tuning() const { return m_master_drum_tuning; } + void set_master_drum_tuning(std::string _v) { m__dirty = true; m_master_drum_tuning = std::move(_v); } + std::string double_play_assignments() const { return m_double_play_assignments; } + void set_double_play_assignments(std::string _v) { m__dirty = true; m_double_play_assignments = std::move(_v); } + std::string velocity_switch_on_off_for_each_double_play_assign() const { return m_velocity_switch_on_off_for_each_double_play_assign; } + void set_velocity_switch_on_off_for_each_double_play_assign(std::string _v) { m__dirty = true; m_velocity_switch_on_off_for_each_double_play_assign = std::move(_v); } + std::string velocity_switch_threshold_value_for_each_double_play_assign() const { return m_velocity_switch_threshold_value_for_each_double_play_assign; } + void set_velocity_switch_threshold_value_for_each_double_play_assign(std::string _v) { m__dirty = true; m_velocity_switch_threshold_value_for_each_double_play_assign = std::move(_v); } + std::string _unnamed13() const { return m__unnamed13; } + void set__unnamed13(std::string _v) { m__dirty = true; m__unnamed13 = std::move(_v); } + std::vector* sound_samples() const { return m_sound_samples.get(); } + void set_sound_samples(std::unique_ptr> _v) { m__dirty = true; m_sound_samples = std::move(_v); } + mpc60_set_v1_t* _root() const { return m__root; } + void set__root(mpc60_set_v1_t* _v) { m__dirty = true; m__root = std::move(_v); } + kaitai::kstruct* _parent() const { return m__parent; } + void set__parent(kaitai::kstruct* _v) { m__dirty = true; m__parent = std::move(_v); } + +private: + std::string m_file_id; + std::string m_file_format_version; + std::unique_ptr m_total_number_of_sample_words; + std::unique_ptr>> m_sound_directory_entry; + std::unique_ptr> m_sound_map; + use_master_mix_data_t m_use_master_mix_data; + std::string m_master_stereo_mix; + std::string m_master_stereo_pan; + std::string m_master_echo_mix; + std::string m_master_drum_tuning; + std::string m_double_play_assignments; + std::string m_velocity_switch_on_off_for_each_double_play_assign; + std::string m_velocity_switch_threshold_value_for_each_double_play_assign; + std::string m__unnamed13; + std::unique_ptr> m_sound_samples; + mpc60_set_v1_t* m__root; + kaitai::kstruct* m__parent; +}; diff --git a/src/main/file/kaitai/generated/mpc60_snd_v1.cpp b/src/main/file/kaitai/generated/mpc60_snd_v1.cpp new file mode 100644 index 000000000..702b479b9 --- /dev/null +++ b/src/main/file/kaitai/generated/mpc60_snd_v1.cpp @@ -0,0 +1,123 @@ +// This is a generated file! Please edit source .ksy file and use kaitai-struct-compiler to rebuild + +#include "mpc60_snd_v1.h" +#include "kaitai/exceptions.h" +#include +namespace mpc { + namespace file { + namespace kaitai { + namespace generated { + + mpc60_snd_v1_t::mpc60_snd_v1_t(kaitai::kstream* p__io, kaitai::kstruct* p__parent, mpc60_snd_v1_t* p__root) : kaitai::kstruct(p__io) { + m__parent = p__parent; + m__root = p__root ? p__root : this; + f_header_size = false; + f_packed_sample_data_byte_count = false; + f_packed_sample_pair_count = false; + f_sample_rate = false; + _read(); + } + + void mpc60_snd_v1_t::_read() { + m_file_id = m__io->read_bytes(1); + if (!(m_file_id == std::string("\x01", 1))) { + throw kaitai::validation_not_equal_error(std::string("\x01", 1), m_file_id, m__io, std::string("/seq/0")); + } + m_file_version = m__io->read_bytes(1); + if (!(m_file_version == std::string("\x01", 1))) { + throw kaitai::validation_not_equal_error(std::string("\x01", 1), m_file_version, m__io, std::string("/seq/1")); + } + m_name = kaitai::kstream::bytes_to_str(kaitai::kstream::bytes_terminate(m__io->read_bytes(17), static_cast(0), false), "ASCII"); + m_sample_count = m__io->read_u4le(); + m_time_from_start_of_data_to_start_play_msec = m__io->read_u2le(); + m_time_from_start_of_data_to_end_of_play_msec = m__io->read_u4le(); + m_decay_time_msec = m__io->read_u2le(); + m_volume_percent = m__io->read_u1(); + m_tuning = m__io->read_s1(); + m_reserved_zeroes = m__io->read_bytes(5); + m_velocity_to_volume_percent = m__io->read_u1(); + m_sample_data_pairs = std::unique_ptr>>(new std::vector>()); + const int l_sample_data_pairs = packed_sample_pair_count(); + for (int i = 0; i < l_sample_data_pairs; i++) { + m_sample_data_pairs->push_back(std::move(std::unique_ptr(new sample_pair_t(m__io, this, m__root)))); + } + } + + void mpc60_snd_v1_t::_fetch_instances() { + for (std::size_t i = 0; i < m_sample_data_pairs->size(); ++i) { + m_sample_data_pairs->at(i).get()->_fetch_instances(); + } + } + + mpc60_snd_v1_t::~mpc60_snd_v1_t() {} + + mpc60_snd_v1_t::sample_pair_t::sample_pair_t(kaitai::kstream* p__io, mpc60_snd_v1_t* p__parent, mpc60_snd_v1_t* p__root) : kaitai::kstruct(p__io) { + m__parent = p__parent; + m__root = p__root; + f_sample0_code = false; + f_sample1_code = false; + _read(); + } + + void mpc60_snd_v1_t::sample_pair_t::_read() { + m_byte0 = m__io->read_u1(); + m_byte1 = m__io->read_u1(); + m_byte2 = m__io->read_u1(); + } + + void mpc60_snd_v1_t::sample_pair_t::_fetch_instances() { + } + + mpc60_snd_v1_t::sample_pair_t::~sample_pair_t() {} + + int32_t mpc60_snd_v1_t::sample_pair_t::sample0_code() { + if (f_sample0_code) + return m_sample0_code; + f_sample0_code = true; + m_sample0_code = byte0() | (byte1() & 15) << 8; + return m_sample0_code; + } + + int32_t mpc60_snd_v1_t::sample_pair_t::sample1_code() { + if (f_sample1_code) + return m_sample1_code; + f_sample1_code = true; + m_sample1_code = byte2() << 4 | (byte1() & 240) >> 4; + return m_sample1_code; + } + + int8_t mpc60_snd_v1_t::header_size() { + if (f_header_size) + return m_header_size; + f_header_size = true; + m_header_size = 39; + return m_header_size; + } + + int32_t mpc60_snd_v1_t::packed_sample_data_byte_count() { + if (f_packed_sample_data_byte_count) + return m_packed_sample_data_byte_count; + f_packed_sample_data_byte_count = true; + m_packed_sample_data_byte_count = packed_sample_pair_count() * 3; + return m_packed_sample_data_byte_count; + } + + int32_t mpc60_snd_v1_t::packed_sample_pair_count() { + if (f_packed_sample_pair_count) + return m_packed_sample_pair_count; + f_packed_sample_pair_count = true; + m_packed_sample_pair_count = (sample_count() + 1) / 2; + return m_packed_sample_pair_count; + } + + int32_t mpc60_snd_v1_t::sample_rate() { + if (f_sample_rate) + return m_sample_rate; + f_sample_rate = true; + m_sample_rate = 40000; + return m_sample_rate; + } + } + } + } +} diff --git a/src/main/file/kaitai/generated/mpc60_snd_v1.h b/src/main/file/kaitai/generated/mpc60_snd_v1.h new file mode 100644 index 000000000..4bc39c980 --- /dev/null +++ b/src/main/file/kaitai/generated/mpc60_snd_v1.h @@ -0,0 +1,222 @@ +#pragma once + +// This is a generated file! Please edit source .ksy file and use kaitai-struct-compiler to rebuild + +#include "kaitai/kaitaistruct.h" +#include +#include +#include + +#if KAITAI_STRUCT_VERSION < 11000L +#error "Incompatible Kaitai Struct C++/STL API: version 0.11 or later is required" +#endif +namespace mpc { + namespace file { + namespace kaitai { + namespace kaitai = ::kaitai; + namespace generated { + + /** + * Parser for MPC60 SND files whose first two bytes are 0x01 0x01. + * + * This definition is based on hardware MPC60 SND files plus a native MAME MPC60 + * 2.14 ROCK.SET export corpus. The header layout and packed sample byte count + * are supported by all currently preserved native MPC60 SND files. The packed + * 12-bit sample-code layout matches the MPC60 SET sample storage layout; the + * stateful conversion from those codes to imported 16-bit PCM is documented + * separately in MPC60_12BIT_SAMPLE_DECODER.md rather than expressed directly in + * this schema. + * + * Known evidence: + * - SOUND002.SND: 6039 bytes total, 4000 samples, 6000 sample bytes + * - SOUND003.SND: 120039 bytes total, 80000 samples, 120000 sample bytes + * - 17 native MAME MPC60 2.14 ROCK.SET exports preserved in codex-mame + * - all imply a 39-byte header and 12-bit packed sample storage + * - offsets 0x21..0x25 are zero in all 42 preserved MPC60 SND files checked + * + * The MPC60 v3.10 manual describes SND contents as sound name, sample data, + * tuning, volume, soft start, and soft end. Native 2.14 SET-to-SND exports also + * show that SET-only fields such as pitch factor, attack rate, mix/pan, output, + * echo, and sound characteristics are not copied into standalone SND headers. + */ + + class mpc60_snd_v1_t : public kaitai::kstruct { + + public: + class sample_pair_t; + + mpc60_snd_v1_t(kaitai::kstream* p__io, kaitai::kstruct* p__parent = nullptr, mpc60_snd_v1_t* p__root = nullptr); + + private: + void _read(); + + public: + void _fetch_instances(); + ~mpc60_snd_v1_t(); + + class sample_pair_t : public kaitai::kstruct { + + public: + + sample_pair_t(kaitai::kstream* p__io, mpc60_snd_v1_t* p__parent = nullptr, mpc60_snd_v1_t* p__root = nullptr); + + private: + void _read(); + + public: + void _fetch_instances(); + ~sample_pair_t(); + int32_t sample0_code(); + int32_t sample1_code(); + uint8_t byte0() const { return m_byte0; } + uint8_t byte1() const { return m_byte1; } + uint8_t byte2() const { return m_byte2; } + mpc60_snd_v1_t* _root() const { return m__root; } + kaitai::kstruct* _parent() const { return m__parent; } + + private: + bool f_sample0_code; + int32_t m_sample0_code; + bool f_sample1_code; + int32_t m_sample1_code; + uint8_t m_byte0; + uint8_t m_byte1; + uint8_t m_byte2; + mpc60_snd_v1_t* m__root; + mpc60_snd_v1_t* m__parent; + }; + + public: + int8_t header_size(); + + /** + * Two 12-bit sample words are packed into three bytes. The expression rounds + * odd sample counts up to the next pair, matching common 12-bit packing + * practice. + */ + int32_t packed_sample_data_byte_count(); + int32_t packed_sample_pair_count(); + + /** + * Current hardware/file evidence points to 40 kHz for this version-family. + * If later MPC60 SND evidence shows multiple rates, promote this into a real + * parsed field or a version split. + */ + int32_t sample_rate(); + std::string file_id() const { return m_file_id; } + std::string file_version() const { return m_file_version; } + std::string name() const { return m_name; } + + /** + * Number of 12-bit mono sample words. Supported by file size: + * header_size + packed_sample_data_byte_count equals total file size for + * both known hardware MPC60 SND files. + */ + uint32_t sample_count() const { return m_sample_count; } + + /** + * Playback start time relative to the beginning of sample data, in + * milliseconds. This matches the same-named MPC60 SET sound-directory + * field for the native 2.14 ROCK.SET exports. + */ + uint16_t time_from_start_of_data_to_start_play_msec() const { return m_time_from_start_of_data_to_start_play_msec; } + + /** + * Playback end time relative to the beginning of sample data, in + * milliseconds. This matches the same-named MPC60 SET sound-directory + * field for the native 2.14 ROCK.SET exports. + * + * Some hardware-created files store the final millisecond index rather than + * the nominal duration: SOUND002.SND stores 99 for a 4000-sample / 100 ms + * sound, and SOUND003.SND stores 1999 for 80000 samples / 2000 ms. + * Native 2.14 SET exports observed so far store the nominal end time. + */ + uint32_t time_from_start_of_data_to_end_of_play_msec() const { return m_time_from_start_of_data_to_end_of_play_msec; } + + /** + * Decay time in milliseconds. This matches the same-named MPC60 SET + * sound-directory field for the native 2.14 ROCK.SET exports. + */ + uint16_t decay_time_msec() const { return m_decay_time_msec; } + + /** + * Sound volume percentage from Edit a Sound page 1. Confirmed on MAME + * MPC60 2.14 by changing only Volume% from 100 to 99 for SNARE#2 from + * ROCK.SET: the saved SND changed only byte offset 0x1f from 0x64 to 0x63 + * and left packed sample data byte-identical. + */ + uint8_t volume_percent() const { return m_volume_percent; } + + /** + * Sound tuning from Edit a Sound page 1. Confirmed on MAME MPC60 2.14 by + * changing only Tuning from 0 to 1 for SNARE#2 from ROCK.SET: the saved SND + * changed only byte offset 0x20 from 0x00 to 0x01 and left packed sample + * data byte-identical. + * + * JAUND017.SND, produced by loading an MPC3000 SND on hardware MPC60 and + * resaving it, stores 17 here. That is consistent with this field being + * sound tuning / import pitch adjustment, not part of a 16-bit volume value. + */ + int8_t tuning() const { return m_tuning; } + + /** + * Observed as all zeroes in all current MPC60 SND evidence, including 42 + * preserved hardware and MAME MPC60 SND files. + * + * These bytes do not correspond to the richer MPC60 SET sound-directory + * fields: native 2.14 ROCK.SET exports vary in pitch factor, attack rate, + * mix/pan, output, echo, and sound-characteristics values, while the + * corresponding SND exports keep this region zero. + */ + std::string reserved_zeroes() const { return m_reserved_zeroes; } + + /** + * Velocity-to-volume percentage from Edit a Sound page 2, + * "Vel>vol(0-100)". Confirmed on MAME MPC60 2.14 by changing only + * Vel>vol from 100 to 99 for SNARE#2 from ROCK.SET: the saved SND changed + * only byte offset 0x26 from 0x64 to 0x63 and left packed sample data + * byte-identical. + */ + uint8_t velocity_to_volume_percent() const { return m_velocity_to_volume_percent; } + + /** + * Packed 12-bit sample code pairs. This is the same little-bit-endian + * two-words-in-three-bytes layout used by MPC60 SET sample data and + * documented in MPC60_12BIT_SAMPLE_DECODER.md. + * + * These are sample codes, not final decoded audio amplitudes. The exact + * MPC2000XL import conversion is stateful consumer logic and is intentionally + * outside this structural schema. + */ + std::vector>* sample_data_pairs() const { return m_sample_data_pairs.get(); } + mpc60_snd_v1_t* _root() const { return m__root; } + kaitai::kstruct* _parent() const { return m__parent; } + + private: + bool f_header_size; + int8_t m_header_size; + bool f_packed_sample_data_byte_count; + int32_t m_packed_sample_data_byte_count; + bool f_packed_sample_pair_count; + int32_t m_packed_sample_pair_count; + bool f_sample_rate; + int32_t m_sample_rate; + std::string m_file_id; + std::string m_file_version; + std::string m_name; + uint32_t m_sample_count; + uint16_t m_time_from_start_of_data_to_start_play_msec; + uint32_t m_time_from_start_of_data_to_end_of_play_msec; + uint16_t m_decay_time_msec; + uint8_t m_volume_percent; + int8_t m_tuning; + std::string m_reserved_zeroes; + uint8_t m_velocity_to_volume_percent; + std::unique_ptr>> m_sample_data_pairs; + mpc60_snd_v1_t* m__root; + kaitai::kstruct* m__parent; + }; + } + } + } +} diff --git a/src/main/file/kaitai/generated/standard_midi_file_with_running_status.cpp b/src/main/file/kaitai/generated/standard_midi_file_with_running_status.cpp new file mode 100644 index 000000000..8bd5f8882 --- /dev/null +++ b/src/main/file/kaitai/generated/standard_midi_file_with_running_status.cpp @@ -0,0 +1,1059 @@ +// This is a generated file! Please edit source .ksy file and use kaitai-struct-compiler to rebuild + +#include "standard_midi_file_with_running_status.h" +#include +#include +#include "kaitai/exceptions.h" + +standard_midi_file_with_running_status_t::standard_midi_file_with_running_status_t(kaitai::kstream* p__io, kaitai::kstruct* p__parent, standard_midi_file_with_running_status_t* p__root) : kaitai::kstruct(p__io) { + m__parent = p__parent; + m__root = p__root ? p__root : this; + m__dirty = false; +} + +void standard_midi_file_with_running_status_t::_read() { + m_hdr = std::unique_ptr(new header_t(m__io, this, m__root)); + m_hdr->_read(); + m_tracks = std::unique_ptr>>(new std::vector>()); + const int l_tracks = hdr()->num_tracks(); + for (int i = 0; i < l_tracks; i++) { + std::unique_ptr _t_tracks = std::unique_ptr(new track_t(m__io, this, m__root)); + try { + _t_tracks->_read(); + } catch(...) { + m_tracks->push_back(std::move(_t_tracks)); + throw; + } + m_tracks->push_back(std::move(_t_tracks)); + } + m__dirty = false; +} + +void standard_midi_file_with_running_status_t::_fetch_instances() { + m_hdr.get()->_fetch_instances(); + for (std::size_t i = 0; i < m_tracks->size(); ++i) { + m_tracks->at(i).get()->_fetch_instances(); + } +} + +void standard_midi_file_with_running_status_t::_write() { + if (m_hdr.get() == nullptr) { + throw std::runtime_error("/seq/0: nested object is not set"); + } + m_hdr.get()->_set_io(m__io); + m_hdr.get()->_write(); + if (m_tracks == nullptr) { + throw std::runtime_error("/seq/1: repeated field is not set"); + } + for (std::vector>::const_iterator it = m_tracks->begin(); it != m_tracks->end(); ++it) { + if ((*it).get() == nullptr) { + throw std::runtime_error("/seq/1: nested object is not set"); + } + (*it).get()->_set_io(m__io); + (*it).get()->_write(); + } + _fetch_instances(); + m__dirty = false; +} + +void standard_midi_file_with_running_status_t::_check() { + if (m_hdr.get() == nullptr) { + throw std::runtime_error("/seq/0: nested object is not set"); + } + m_hdr.get()->_set_io(m__io); + m_hdr.get()->_check(); + if (m_tracks == nullptr) { + throw std::runtime_error("/seq/1: repeated field is not set"); + } + if (m_tracks->size() != static_cast(hdr()->num_tracks())) { + throw std::runtime_error("/seq/1: repeat-expr size mismatch"); + } + for (std::vector>::const_iterator it = m_tracks->begin(); it != m_tracks->end(); ++it) { + if ((*it).get() == nullptr) { + throw std::runtime_error("/seq/1: nested object is not set"); + } + (*it).get()->_set_io(m__io); + (*it).get()->_check(); + } + m__dirty = false; +} + +standard_midi_file_with_running_status_t::~standard_midi_file_with_running_status_t() {} + +standard_midi_file_with_running_status_t::channel_pressure_event_t::channel_pressure_event_t(kaitai::kstream* p__io, standard_midi_file_with_running_status_t::track_event_t* p__parent, standard_midi_file_with_running_status_t* p__root) : kaitai::kstruct(p__io) { + m__parent = p__parent; + m__root = p__root; + m__dirty = false; +} + +void standard_midi_file_with_running_status_t::channel_pressure_event_t::_read() { + m_pressure = m__io->read_u1(); + m__dirty = false; +} + +void standard_midi_file_with_running_status_t::channel_pressure_event_t::_fetch_instances() { +} + +void standard_midi_file_with_running_status_t::channel_pressure_event_t::_write() { + m__io->write_u1(m_pressure); + _fetch_instances(); + m__dirty = false; +} + +void standard_midi_file_with_running_status_t::channel_pressure_event_t::_check() { + m__dirty = false; +} + +standard_midi_file_with_running_status_t::channel_pressure_event_t::~channel_pressure_event_t() {} + +standard_midi_file_with_running_status_t::controller_event_t::controller_event_t(kaitai::kstream* p__io, standard_midi_file_with_running_status_t::track_event_t* p__parent, standard_midi_file_with_running_status_t* p__root) : kaitai::kstruct(p__io) { + m__parent = p__parent; + m__root = p__root; + m__dirty = false; +} + +void standard_midi_file_with_running_status_t::controller_event_t::_read() { + m_controller = m__io->read_u1(); + m_value = m__io->read_u1(); + m__dirty = false; +} + +void standard_midi_file_with_running_status_t::controller_event_t::_fetch_instances() { +} + +void standard_midi_file_with_running_status_t::controller_event_t::_write() { + m__io->write_u1(m_controller); + m__io->write_u1(m_value); + _fetch_instances(); + m__dirty = false; +} + +void standard_midi_file_with_running_status_t::controller_event_t::_check() { + m__dirty = false; +} + +standard_midi_file_with_running_status_t::controller_event_t::~controller_event_t() {} + +standard_midi_file_with_running_status_t::header_t::header_t(kaitai::kstream* p__io, standard_midi_file_with_running_status_t* p__parent, standard_midi_file_with_running_status_t* p__root) : kaitai::kstruct(p__io) { + m__parent = p__parent; + m__root = p__root; + m__dirty = false; +} + +void standard_midi_file_with_running_status_t::header_t::_read() { + m_magic = m__io->read_bytes(4); + if (!(m_magic == std::string("\x4D\x54\x68\x64", 4))) { + throw kaitai::validation_not_equal_error(std::string("\x4D\x54\x68\x64", 4), m_magic, m__io, std::string("/types/header/seq/0")); + } + m_len_header = m__io->read_u4be(); + m_format = m__io->read_u2be(); + m_num_tracks = m__io->read_u2be(); + m_division = m__io->read_s2be(); + m__dirty = false; +} + +void standard_midi_file_with_running_status_t::header_t::_fetch_instances() { +} + +void standard_midi_file_with_running_status_t::header_t::_write() { + m__io->write_bytes(m_magic); + m__io->write_u4be(m_len_header); + m__io->write_u2be(m_format); + m__io->write_u2be(m_num_tracks); + m__io->write_s2be(m_division); + _fetch_instances(); + m__dirty = false; +} + +void standard_midi_file_with_running_status_t::header_t::_check() { + if (m_magic.size() != static_cast(4)) { + throw std::runtime_error("/types/header/seq/0: size mismatch"); + } + if (!(m_magic == std::string("\x4D\x54\x68\x64", 4))) { + throw kaitai::validation_not_equal_error(std::string("\x4D\x54\x68\x64", 4), m_magic, m__io, std::string("/types/header/seq/0")); + } + m__dirty = false; +} + +standard_midi_file_with_running_status_t::header_t::~header_t() {} +const std::set standard_midi_file_with_running_status_t::meta_event_body_t::_values_meta_type_enum_t{ + standard_midi_file_with_running_status_t::meta_event_body_t::META_TYPE_ENUM_SEQUENCE_NUMBER, + standard_midi_file_with_running_status_t::meta_event_body_t::META_TYPE_ENUM_TEXT_EVENT, + standard_midi_file_with_running_status_t::meta_event_body_t::META_TYPE_ENUM_COPYRIGHT, + standard_midi_file_with_running_status_t::meta_event_body_t::META_TYPE_ENUM_SEQUENCE_TRACK_NAME, + standard_midi_file_with_running_status_t::meta_event_body_t::META_TYPE_ENUM_INSTRUMENT_NAME, + standard_midi_file_with_running_status_t::meta_event_body_t::META_TYPE_ENUM_LYRIC_TEXT, + standard_midi_file_with_running_status_t::meta_event_body_t::META_TYPE_ENUM_MARKER_TEXT, + standard_midi_file_with_running_status_t::meta_event_body_t::META_TYPE_ENUM_CUE_POINT, + standard_midi_file_with_running_status_t::meta_event_body_t::META_TYPE_ENUM_MIDI_CHANNEL_PREFIX_ASSIGNMENT, + standard_midi_file_with_running_status_t::meta_event_body_t::META_TYPE_ENUM_END_OF_TRACK, + standard_midi_file_with_running_status_t::meta_event_body_t::META_TYPE_ENUM_TEMPO, + standard_midi_file_with_running_status_t::meta_event_body_t::META_TYPE_ENUM_SMPTE_OFFSET, + standard_midi_file_with_running_status_t::meta_event_body_t::META_TYPE_ENUM_TIME_SIGNATURE, + standard_midi_file_with_running_status_t::meta_event_body_t::META_TYPE_ENUM_KEY_SIGNATURE, + standard_midi_file_with_running_status_t::meta_event_body_t::META_TYPE_ENUM_SEQUENCER_SPECIFIC_EVENT, +}; +bool standard_midi_file_with_running_status_t::meta_event_body_t::_is_defined_meta_type_enum_t(standard_midi_file_with_running_status_t::meta_event_body_t::meta_type_enum_t v) { + return standard_midi_file_with_running_status_t::meta_event_body_t::_values_meta_type_enum_t.find(v) != standard_midi_file_with_running_status_t::meta_event_body_t::_values_meta_type_enum_t.end(); +} + +standard_midi_file_with_running_status_t::meta_event_body_t::meta_event_body_t(kaitai::kstream* p__io, standard_midi_file_with_running_status_t::track_event_t* p__parent, standard_midi_file_with_running_status_t* p__root) : kaitai::kstruct(p__io) { + m__parent = p__parent; + m__root = p__root; + m__dirty = false; +} + +void standard_midi_file_with_running_status_t::meta_event_body_t::_read() { + m_meta_type = static_cast(m__io->read_u1()); + m_len = std::unique_ptr(new vlq_base128_be_t(m__io)); + m_len->_read(); + m_body = m__io->read_bytes(len()->value()); + m__dirty = false; +} + +void standard_midi_file_with_running_status_t::meta_event_body_t::_fetch_instances() { + m_len.get()->_fetch_instances(); +} + +void standard_midi_file_with_running_status_t::meta_event_body_t::_write() { + m__io->write_u1(static_cast(m_meta_type)); + if (m_len.get() == nullptr) { + throw std::runtime_error("/types/meta_event_body/seq/1: nested object is not set"); + } + m_len.get()->_set_io(m__io); + m_len.get()->_write(); + m__io->write_bytes(m_body); + _fetch_instances(); + m__dirty = false; +} + +void standard_midi_file_with_running_status_t::meta_event_body_t::_check() { + if (m_len.get() == nullptr) { + throw std::runtime_error("/types/meta_event_body/seq/1: nested object is not set"); + } + m_len.get()->_set_io(m__io); + m_len.get()->_check(); + if (m_body.size() != static_cast(len()->value())) { + throw std::runtime_error("/types/meta_event_body/seq/2: size mismatch"); + } + m__dirty = false; +} + +standard_midi_file_with_running_status_t::meta_event_body_t::~meta_event_body_t() {} + +standard_midi_file_with_running_status_t::note_off_event_t::note_off_event_t(kaitai::kstream* p__io, standard_midi_file_with_running_status_t::track_event_t* p__parent, standard_midi_file_with_running_status_t* p__root) : kaitai::kstruct(p__io) { + m__parent = p__parent; + m__root = p__root; + m__dirty = false; +} + +void standard_midi_file_with_running_status_t::note_off_event_t::_read() { + m_note = m__io->read_u1(); + m_velocity = m__io->read_u1(); + m__dirty = false; +} + +void standard_midi_file_with_running_status_t::note_off_event_t::_fetch_instances() { +} + +void standard_midi_file_with_running_status_t::note_off_event_t::_write() { + m__io->write_u1(m_note); + m__io->write_u1(m_velocity); + _fetch_instances(); + m__dirty = false; +} + +void standard_midi_file_with_running_status_t::note_off_event_t::_check() { + m__dirty = false; +} + +standard_midi_file_with_running_status_t::note_off_event_t::~note_off_event_t() {} + +standard_midi_file_with_running_status_t::note_on_event_t::note_on_event_t(kaitai::kstream* p__io, standard_midi_file_with_running_status_t::track_event_t* p__parent, standard_midi_file_with_running_status_t* p__root) : kaitai::kstruct(p__io) { + m__parent = p__parent; + m__root = p__root; + m__dirty = false; +} + +void standard_midi_file_with_running_status_t::note_on_event_t::_read() { + m_note = m__io->read_u1(); + m_velocity = m__io->read_u1(); + m__dirty = false; +} + +void standard_midi_file_with_running_status_t::note_on_event_t::_fetch_instances() { +} + +void standard_midi_file_with_running_status_t::note_on_event_t::_write() { + m__io->write_u1(m_note); + m__io->write_u1(m_velocity); + _fetch_instances(); + m__dirty = false; +} + +void standard_midi_file_with_running_status_t::note_on_event_t::_check() { + m__dirty = false; +} + +standard_midi_file_with_running_status_t::note_on_event_t::~note_on_event_t() {} + +standard_midi_file_with_running_status_t::pitch_bend_event_t::pitch_bend_event_t(kaitai::kstream* p__io, standard_midi_file_with_running_status_t::track_event_t* p__parent, standard_midi_file_with_running_status_t* p__root) : kaitai::kstruct(p__io) { + m__parent = p__parent; + m__root = p__root; + m__dirty = false; + f_adj_bend_value = false; + f_bend_value = false; +} + +void standard_midi_file_with_running_status_t::pitch_bend_event_t::_read() { + m_b1 = m__io->read_u1(); + m_b2 = m__io->read_u1(); + m__dirty = false; +} + +void standard_midi_file_with_running_status_t::pitch_bend_event_t::_fetch_instances() { +} + +void standard_midi_file_with_running_status_t::pitch_bend_event_t::_write() { + m__io->write_u1(m_b1); + m__io->write_u1(m_b2); + _fetch_instances(); + m__dirty = false; +} + +void standard_midi_file_with_running_status_t::pitch_bend_event_t::_check() { + m__dirty = false; +} + +standard_midi_file_with_running_status_t::pitch_bend_event_t::~pitch_bend_event_t() {} + +int32_t standard_midi_file_with_running_status_t::pitch_bend_event_t::adj_bend_value() { + if (f_adj_bend_value) + return m_adj_bend_value; + f_adj_bend_value = true; + m_adj_bend_value = bend_value() - 16384; + return m_adj_bend_value; +} + +int32_t standard_midi_file_with_running_status_t::pitch_bend_event_t::bend_value() { + if (f_bend_value) + return m_bend_value; + f_bend_value = true; + m_bend_value = ((b2() << 7) + b1()) - 16384; + return m_bend_value; +} + +standard_midi_file_with_running_status_t::polyphonic_pressure_event_t::polyphonic_pressure_event_t(kaitai::kstream* p__io, standard_midi_file_with_running_status_t::track_event_t* p__parent, standard_midi_file_with_running_status_t* p__root) : kaitai::kstruct(p__io) { + m__parent = p__parent; + m__root = p__root; + m__dirty = false; +} + +void standard_midi_file_with_running_status_t::polyphonic_pressure_event_t::_read() { + m_note = m__io->read_u1(); + m_pressure = m__io->read_u1(); + m__dirty = false; +} + +void standard_midi_file_with_running_status_t::polyphonic_pressure_event_t::_fetch_instances() { +} + +void standard_midi_file_with_running_status_t::polyphonic_pressure_event_t::_write() { + m__io->write_u1(m_note); + m__io->write_u1(m_pressure); + _fetch_instances(); + m__dirty = false; +} + +void standard_midi_file_with_running_status_t::polyphonic_pressure_event_t::_check() { + m__dirty = false; +} + +standard_midi_file_with_running_status_t::polyphonic_pressure_event_t::~polyphonic_pressure_event_t() {} + +standard_midi_file_with_running_status_t::program_change_event_t::program_change_event_t(kaitai::kstream* p__io, standard_midi_file_with_running_status_t::track_event_t* p__parent, standard_midi_file_with_running_status_t* p__root) : kaitai::kstruct(p__io) { + m__parent = p__parent; + m__root = p__root; + m__dirty = false; +} + +void standard_midi_file_with_running_status_t::program_change_event_t::_read() { + m_program = m__io->read_u1(); + m__dirty = false; +} + +void standard_midi_file_with_running_status_t::program_change_event_t::_fetch_instances() { +} + +void standard_midi_file_with_running_status_t::program_change_event_t::_write() { + m__io->write_u1(m_program); + _fetch_instances(); + m__dirty = false; +} + +void standard_midi_file_with_running_status_t::program_change_event_t::_check() { + m__dirty = false; +} + +standard_midi_file_with_running_status_t::program_change_event_t::~program_change_event_t() {} + +standard_midi_file_with_running_status_t::sysex_event_body_t::sysex_event_body_t(kaitai::kstream* p__io, standard_midi_file_with_running_status_t::track_event_t* p__parent, standard_midi_file_with_running_status_t* p__root) : kaitai::kstruct(p__io) { + m__parent = p__parent; + m__root = p__root; + m__dirty = false; +} + +void standard_midi_file_with_running_status_t::sysex_event_body_t::_read() { + m_len = std::unique_ptr(new vlq_base128_be_t(m__io)); + m_len->_read(); + m_data = m__io->read_bytes(len()->value()); + m__dirty = false; +} + +void standard_midi_file_with_running_status_t::sysex_event_body_t::_fetch_instances() { + m_len.get()->_fetch_instances(); +} + +void standard_midi_file_with_running_status_t::sysex_event_body_t::_write() { + if (m_len.get() == nullptr) { + throw std::runtime_error("/types/sysex_event_body/seq/0: nested object is not set"); + } + m_len.get()->_set_io(m__io); + m_len.get()->_write(); + m__io->write_bytes(m_data); + _fetch_instances(); + m__dirty = false; +} + +void standard_midi_file_with_running_status_t::sysex_event_body_t::_check() { + if (m_len.get() == nullptr) { + throw std::runtime_error("/types/sysex_event_body/seq/0: nested object is not set"); + } + m_len.get()->_set_io(m__io); + m_len.get()->_check(); + if (m_data.size() != static_cast(len()->value())) { + throw std::runtime_error("/types/sysex_event_body/seq/1: size mismatch"); + } + m__dirty = false; +} + +standard_midi_file_with_running_status_t::sysex_event_body_t::~sysex_event_body_t() {} + +standard_midi_file_with_running_status_t::track_t::track_t(kaitai::kstream* p__io, standard_midi_file_with_running_status_t* p__parent, standard_midi_file_with_running_status_t* p__root) : kaitai::kstruct(p__io) { + m__parent = p__parent; + m__root = p__root; + m__dirty = false; +} + +void standard_midi_file_with_running_status_t::track_t::_read() { + m_magic = m__io->read_bytes(4); + if (!(m_magic == std::string("\x4D\x54\x72\x6B", 4))) { + throw kaitai::validation_not_equal_error(std::string("\x4D\x54\x72\x6B", 4), m_magic, m__io, std::string("/types/track/seq/0")); + } + m_len_events = m__io->read_u4be(); + m__raw_events = m__io->read_bytes(len_events()); + m__io__raw_events = std::unique_ptr(new kaitai::kstream(m__raw_events)); + m_events = std::unique_ptr(new track_events_t(m__io__raw_events.get(), this, m__root)); + m_events->_read(); + m__dirty = false; +} + +void standard_midi_file_with_running_status_t::track_t::_fetch_instances() { + m_events.get()->_fetch_instances(); +} + +void standard_midi_file_with_running_status_t::track_t::_write() { + m__io->write_bytes(m_magic); + m__io->write_u4be(m_len_events); + if (m_events.get() == nullptr) { + throw std::runtime_error("/types/track/seq/2: nested object is not set"); + } + m__raw_events = std::string(static_cast(len_events()), '\0'); + m__io__raw_events = std::unique_ptr(new kaitai::kstream(m__raw_events)); + m_events.get()->_set_io(m__io__raw_events.get()); + m_events.get()->_write(); + if (m__io__raw_events.get()->pos() != static_cast(len_events())) { + throw std::runtime_error("/types/track/seq/2: serialized size mismatch"); + } + if (m__io__raw_events.get()->to_byte_array().size() != static_cast(len_events())) { + throw std::runtime_error("/types/track/seq/2: raw buffer size mismatch"); + } + m__raw_events = m__io__raw_events.get()->to_byte_array(); + m__io->write_bytes(m__raw_events); + _fetch_instances(); + m__dirty = false; +} + +void standard_midi_file_with_running_status_t::track_t::_check() { + if (m_magic.size() != static_cast(4)) { + throw std::runtime_error("/types/track/seq/0: size mismatch"); + } + if (!(m_magic == std::string("\x4D\x54\x72\x6B", 4))) { + throw kaitai::validation_not_equal_error(std::string("\x4D\x54\x72\x6B", 4), m_magic, m__io, std::string("/types/track/seq/0")); + } + if (m_events.get() == nullptr) { + throw std::runtime_error("/types/track/seq/2: nested object is not set"); + } + m__raw_events = std::string(static_cast(len_events()), '\0'); + m__io__raw_events = std::unique_ptr(new kaitai::kstream(m__raw_events)); + m_events.get()->_set_io(m__io__raw_events.get()); + m_events.get()->_check(); + m__dirty = false; +} + +standard_midi_file_with_running_status_t::track_t::~track_t() {} + +standard_midi_file_with_running_status_t::track_event_t::track_event_t(uint8_t p_previous_event_type, kaitai::kstream* p__io, standard_midi_file_with_running_status_t::track_events_t* p__parent, standard_midi_file_with_running_status_t* p__root) : kaitai::kstruct(p__io) { + m__parent = p__parent; + m__root = p__root; + m_previous_event_type = p_previous_event_type; + m__dirty = false; + n_event_header = true; + f_channel = false; + f_event_type = false; + f_status_byte_lookahead = false; + f_using_running_status = false; + w_status_byte_lookahead = false; + e_status_byte_lookahead = m__io != nullptr; +} + +void standard_midi_file_with_running_status_t::track_event_t::_read() { + m_v_time = std::unique_ptr(new vlq_base128_be_t(m__io)); + m_v_time->_read(); + n_event_header = true; + if (!(using_running_status())) { + n_event_header = false; + m_event_header = m__io->read_u1(); + } + if (event_header() == 255) { + m_meta_event_body = std::unique_ptr(new meta_event_body_t(m__io, this, m__root)); + m_meta_event_body->_read(); + } + if (event_header() == 240) { + m_sysex_body = std::unique_ptr(new sysex_event_body_t(m__io, this, m__root)); + m_sysex_body->_read(); + } + switch (event_type()) { + case 128: { + m_event_body = std::unique_ptr(new note_off_event_t(m__io, this, m__root)); + static_cast(m_event_body.get())->_read(); + break; + } + case 144: { + m_event_body = std::unique_ptr(new note_on_event_t(m__io, this, m__root)); + static_cast(m_event_body.get())->_read(); + break; + } + case 160: { + m_event_body = std::unique_ptr(new polyphonic_pressure_event_t(m__io, this, m__root)); + static_cast(m_event_body.get())->_read(); + break; + } + case 176: { + m_event_body = std::unique_ptr(new controller_event_t(m__io, this, m__root)); + static_cast(m_event_body.get())->_read(); + break; + } + case 192: { + m_event_body = std::unique_ptr(new program_change_event_t(m__io, this, m__root)); + static_cast(m_event_body.get())->_read(); + break; + } + case 208: { + m_event_body = std::unique_ptr(new channel_pressure_event_t(m__io, this, m__root)); + static_cast(m_event_body.get())->_read(); + break; + } + case 224: { + m_event_body = std::unique_ptr(new pitch_bend_event_t(m__io, this, m__root)); + static_cast(m_event_body.get())->_read(); + break; + } + } + m__dirty = false; +} + +void standard_midi_file_with_running_status_t::track_event_t::_fetch_instances() { + m_v_time.get()->_fetch_instances(); + n_event_header = true; + if (!(using_running_status())) { + n_event_header = false; + } + if (event_header() == 255) { + m_meta_event_body.get()->_fetch_instances(); + } + if (event_header() == 240) { + m_sysex_body.get()->_fetch_instances(); + } + switch (event_type()) { + case 128: { + { + note_off_event_t* _switch_obj = dynamic_cast(m_event_body.get()); + if (_switch_obj == nullptr) { + throw std::runtime_error("switch object type mismatch in _fetch_instances"); + } + _switch_obj->_fetch_instances(); + } + break; + } + case 144: { + { + note_on_event_t* _switch_obj = dynamic_cast(m_event_body.get()); + if (_switch_obj == nullptr) { + throw std::runtime_error("switch object type mismatch in _fetch_instances"); + } + _switch_obj->_fetch_instances(); + } + break; + } + case 160: { + { + polyphonic_pressure_event_t* _switch_obj = dynamic_cast(m_event_body.get()); + if (_switch_obj == nullptr) { + throw std::runtime_error("switch object type mismatch in _fetch_instances"); + } + _switch_obj->_fetch_instances(); + } + break; + } + case 176: { + { + controller_event_t* _switch_obj = dynamic_cast(m_event_body.get()); + if (_switch_obj == nullptr) { + throw std::runtime_error("switch object type mismatch in _fetch_instances"); + } + _switch_obj->_fetch_instances(); + } + break; + } + case 192: { + { + program_change_event_t* _switch_obj = dynamic_cast(m_event_body.get()); + if (_switch_obj == nullptr) { + throw std::runtime_error("switch object type mismatch in _fetch_instances"); + } + _switch_obj->_fetch_instances(); + } + break; + } + case 208: { + { + channel_pressure_event_t* _switch_obj = dynamic_cast(m_event_body.get()); + if (_switch_obj == nullptr) { + throw std::runtime_error("switch object type mismatch in _fetch_instances"); + } + _switch_obj->_fetch_instances(); + } + break; + } + case 224: { + { + pitch_bend_event_t* _switch_obj = dynamic_cast(m_event_body.get()); + if (_switch_obj == nullptr) { + throw std::runtime_error("switch object type mismatch in _fetch_instances"); + } + _switch_obj->_fetch_instances(); + } + break; + } + } + status_byte_lookahead(); + if (f_status_byte_lookahead) { + } +} + +void standard_midi_file_with_running_status_t::track_event_t::_write() { + w_status_byte_lookahead = false; + if (m_v_time.get() == nullptr) { + throw std::runtime_error("/types/track_event/seq/0: nested object is not set"); + } + m_v_time.get()->_set_io(m__io); + m_v_time.get()->_write(); + if (!(using_running_status())) { + m__io->write_u1(m_event_header); + } + if (event_header() == 255) { + if (m_meta_event_body.get() == nullptr) { + throw std::runtime_error("/types/track_event/seq/2: nested object is not set"); + } + m_meta_event_body.get()->_set_io(m__io); + m_meta_event_body.get()->_write(); + } + if (event_header() == 240) { + if (m_sysex_body.get() == nullptr) { + throw std::runtime_error("/types/track_event/seq/3: nested object is not set"); + } + m_sysex_body.get()->_set_io(m__io); + m_sysex_body.get()->_write(); + } + switch (event_type()) { + case 128: { + { + note_off_event_t* _switch_obj = dynamic_cast(m_event_body.get()); + if (_switch_obj == nullptr) { + throw std::runtime_error("/types/track_event/seq/4: switch object type mismatch"); + } + if (_switch_obj == nullptr) { + throw std::runtime_error("/types/track_event/seq/4: nested object is not set"); + } + _switch_obj->_set_io(m__io); + _switch_obj->_write(); + } + break; + } + case 144: { + { + note_on_event_t* _switch_obj = dynamic_cast(m_event_body.get()); + if (_switch_obj == nullptr) { + throw std::runtime_error("/types/track_event/seq/4: switch object type mismatch"); + } + if (_switch_obj == nullptr) { + throw std::runtime_error("/types/track_event/seq/4: nested object is not set"); + } + _switch_obj->_set_io(m__io); + _switch_obj->_write(); + } + break; + } + case 160: { + { + polyphonic_pressure_event_t* _switch_obj = dynamic_cast(m_event_body.get()); + if (_switch_obj == nullptr) { + throw std::runtime_error("/types/track_event/seq/4: switch object type mismatch"); + } + if (_switch_obj == nullptr) { + throw std::runtime_error("/types/track_event/seq/4: nested object is not set"); + } + _switch_obj->_set_io(m__io); + _switch_obj->_write(); + } + break; + } + case 176: { + { + controller_event_t* _switch_obj = dynamic_cast(m_event_body.get()); + if (_switch_obj == nullptr) { + throw std::runtime_error("/types/track_event/seq/4: switch object type mismatch"); + } + if (_switch_obj == nullptr) { + throw std::runtime_error("/types/track_event/seq/4: nested object is not set"); + } + _switch_obj->_set_io(m__io); + _switch_obj->_write(); + } + break; + } + case 192: { + { + program_change_event_t* _switch_obj = dynamic_cast(m_event_body.get()); + if (_switch_obj == nullptr) { + throw std::runtime_error("/types/track_event/seq/4: switch object type mismatch"); + } + if (_switch_obj == nullptr) { + throw std::runtime_error("/types/track_event/seq/4: nested object is not set"); + } + _switch_obj->_set_io(m__io); + _switch_obj->_write(); + } + break; + } + case 208: { + { + channel_pressure_event_t* _switch_obj = dynamic_cast(m_event_body.get()); + if (_switch_obj == nullptr) { + throw std::runtime_error("/types/track_event/seq/4: switch object type mismatch"); + } + if (_switch_obj == nullptr) { + throw std::runtime_error("/types/track_event/seq/4: nested object is not set"); + } + _switch_obj->_set_io(m__io); + _switch_obj->_write(); + } + break; + } + case 224: { + { + pitch_bend_event_t* _switch_obj = dynamic_cast(m_event_body.get()); + if (_switch_obj == nullptr) { + throw std::runtime_error("/types/track_event/seq/4: switch object type mismatch"); + } + if (_switch_obj == nullptr) { + throw std::runtime_error("/types/track_event/seq/4: nested object is not set"); + } + _switch_obj->_set_io(m__io); + _switch_obj->_write(); + } + break; + } + } + _fetch_instances(); + m__dirty = false; +} + +void standard_midi_file_with_running_status_t::track_event_t::_check() { + if (m_v_time.get() == nullptr) { + throw std::runtime_error("/types/track_event/seq/0: nested object is not set"); + } + m_v_time.get()->_set_io(m__io); + m_v_time.get()->_check(); + if (!(using_running_status())) { + if (!(!n_event_header)) { + throw std::runtime_error("/types/track_event/seq/1: conditional field is not set"); + } + } else { + if (!n_event_header) { + throw std::runtime_error("/types/track_event/seq/1: conditional field should be absent"); + } + } + if (event_header() == 255) { + if (!(m_meta_event_body.get() != nullptr)) { + throw std::runtime_error("/types/track_event/seq/2: conditional field is not set"); + } + if (m_meta_event_body.get() == nullptr) { + throw std::runtime_error("/types/track_event/seq/2: nested object is not set"); + } + m_meta_event_body.get()->_set_io(m__io); + m_meta_event_body.get()->_check(); + } else { + if (m_meta_event_body.get() != nullptr) { + throw std::runtime_error("/types/track_event/seq/2: conditional field should be absent"); + } + } + if (event_header() == 240) { + if (!(m_sysex_body.get() != nullptr)) { + throw std::runtime_error("/types/track_event/seq/3: conditional field is not set"); + } + if (m_sysex_body.get() == nullptr) { + throw std::runtime_error("/types/track_event/seq/3: nested object is not set"); + } + m_sysex_body.get()->_set_io(m__io); + m_sysex_body.get()->_check(); + } else { + if (m_sysex_body.get() != nullptr) { + throw std::runtime_error("/types/track_event/seq/3: conditional field should be absent"); + } + } + switch (event_type()) { + case 128: { + { + note_off_event_t* _switch_obj = dynamic_cast(m_event_body.get()); + if (_switch_obj == nullptr) { + throw std::runtime_error("/types/track_event/seq/4: switch object type mismatch"); + } + if (_switch_obj == nullptr) { + throw std::runtime_error("/types/track_event/seq/4: nested object is not set"); + } + _switch_obj->_set_io(m__io); + _switch_obj->_check(); + } + break; + } + case 144: { + { + note_on_event_t* _switch_obj = dynamic_cast(m_event_body.get()); + if (_switch_obj == nullptr) { + throw std::runtime_error("/types/track_event/seq/4: switch object type mismatch"); + } + if (_switch_obj == nullptr) { + throw std::runtime_error("/types/track_event/seq/4: nested object is not set"); + } + _switch_obj->_set_io(m__io); + _switch_obj->_check(); + } + break; + } + case 160: { + { + polyphonic_pressure_event_t* _switch_obj = dynamic_cast(m_event_body.get()); + if (_switch_obj == nullptr) { + throw std::runtime_error("/types/track_event/seq/4: switch object type mismatch"); + } + if (_switch_obj == nullptr) { + throw std::runtime_error("/types/track_event/seq/4: nested object is not set"); + } + _switch_obj->_set_io(m__io); + _switch_obj->_check(); + } + break; + } + case 176: { + { + controller_event_t* _switch_obj = dynamic_cast(m_event_body.get()); + if (_switch_obj == nullptr) { + throw std::runtime_error("/types/track_event/seq/4: switch object type mismatch"); + } + if (_switch_obj == nullptr) { + throw std::runtime_error("/types/track_event/seq/4: nested object is not set"); + } + _switch_obj->_set_io(m__io); + _switch_obj->_check(); + } + break; + } + case 192: { + { + program_change_event_t* _switch_obj = dynamic_cast(m_event_body.get()); + if (_switch_obj == nullptr) { + throw std::runtime_error("/types/track_event/seq/4: switch object type mismatch"); + } + if (_switch_obj == nullptr) { + throw std::runtime_error("/types/track_event/seq/4: nested object is not set"); + } + _switch_obj->_set_io(m__io); + _switch_obj->_check(); + } + break; + } + case 208: { + { + channel_pressure_event_t* _switch_obj = dynamic_cast(m_event_body.get()); + if (_switch_obj == nullptr) { + throw std::runtime_error("/types/track_event/seq/4: switch object type mismatch"); + } + if (_switch_obj == nullptr) { + throw std::runtime_error("/types/track_event/seq/4: nested object is not set"); + } + _switch_obj->_set_io(m__io); + _switch_obj->_check(); + } + break; + } + case 224: { + { + pitch_bend_event_t* _switch_obj = dynamic_cast(m_event_body.get()); + if (_switch_obj == nullptr) { + throw std::runtime_error("/types/track_event/seq/4: switch object type mismatch"); + } + if (_switch_obj == nullptr) { + throw std::runtime_error("/types/track_event/seq/4: nested object is not set"); + } + _switch_obj->_set_io(m__io); + _switch_obj->_check(); + } + break; + } + } + if (e_status_byte_lookahead) { + } + m__dirty = false; +} + +standard_midi_file_with_running_status_t::track_event_t::~track_event_t() {} + +int32_t standard_midi_file_with_running_status_t::track_event_t::channel() { + if (f_channel) + return m_channel; + f_channel = true; + n_channel = true; + if (event_type() != 240) { + n_channel = false; + m_channel = event_header() & 15; + } + return m_channel; +} + +int32_t standard_midi_file_with_running_status_t::track_event_t::event_type() { + if (f_event_type) + return m_event_type; + f_event_type = true; + m_event_type = ((using_running_status()) ? (previous_event_type()) : (event_header() & 240)); + return m_event_type; +} + +uint8_t standard_midi_file_with_running_status_t::track_event_t::status_byte_lookahead() { + if (w_status_byte_lookahead) + _write_status_byte_lookahead(); + if (f_status_byte_lookahead) + return m_status_byte_lookahead; + if (!e_status_byte_lookahead) + return 0; + f_status_byte_lookahead = true; + std::streampos _pos = m__io->pos(); + m__io->seek(_io()->pos()); + m_status_byte_lookahead = m__io->read_u1(); + m__io->seek(_pos); + return m_status_byte_lookahead; +} + +void standard_midi_file_with_running_status_t::track_event_t::_write_status_byte_lookahead() { + w_status_byte_lookahead = false; + std::streampos _pos = m__io->pos(); + m__io->seek(_io()->pos()); + m__io->write_u1(m_status_byte_lookahead); + m__io->seek(_pos); +} + +bool standard_midi_file_with_running_status_t::track_event_t::using_running_status() { + if (f_using_running_status) + return m_using_running_status; + f_using_running_status = true; + m_using_running_status = (status_byte_lookahead() & 128) == 0; + return m_using_running_status; +} + +standard_midi_file_with_running_status_t::track_events_t::track_events_t(kaitai::kstream* p__io, standard_midi_file_with_running_status_t::track_t* p__parent, standard_midi_file_with_running_status_t* p__root) : kaitai::kstruct(p__io) { + m__parent = p__parent; + m__root = p__root; + m__dirty = false; +} + +void standard_midi_file_with_running_status_t::track_events_t::_read() { + m_event = std::unique_ptr>>(new std::vector>()); + { + int i = 0; + while (!m__io->is_eof()) { + std::unique_ptr _t_event = std::unique_ptr(new track_event_t(((i != 0) ? (event()->at(i - 1)->event_type()) : (255)), m__io, this, m__root)); + try { + _t_event->_read(); + } catch(...) { + m_event->push_back(std::move(_t_event)); + throw; + } + m_event->push_back(std::move(_t_event)); + i++; + } + } + m__dirty = false; +} + +void standard_midi_file_with_running_status_t::track_events_t::_fetch_instances() { + for (std::size_t i = 0; i < m_event->size(); ++i) { + m_event->at(i).get()->_fetch_instances(); + } +} + +void standard_midi_file_with_running_status_t::track_events_t::_write() { + if (m_event == nullptr) { + throw std::runtime_error("/types/track_events/seq/0: repeated field is not set"); + } + if (m_event == nullptr) { + throw std::runtime_error("/types/track_events/seq/0: repeated field is not set"); + } + for (std::vector>::const_iterator it = m_event->begin(); it != m_event->end(); ++it) { + if ((*it).get() == nullptr) { + throw std::runtime_error("/types/track_events/seq/0: nested object is not set"); + } + (*it).get()->_set_io(m__io); + (*it).get()->_write(); + } + _fetch_instances(); + m__dirty = false; +} + +void standard_midi_file_with_running_status_t::track_events_t::_check() { + if (m_event == nullptr) { + throw std::runtime_error("/types/track_events/seq/0: repeated field is not set"); + } + for (std::vector>::const_iterator it = m_event->begin(); it != m_event->end(); ++it) { + const std::size_t i = static_cast(it - m_event->begin()); + const track_event_t* _ = (*it).get(); + if ((*it).get() == nullptr) { + throw std::runtime_error("/types/track_events/seq/0: nested object is not set"); + } + (*it).get()->_set_io(m__io); + (*it).get()->_check(); + } + m__dirty = false; +} + +standard_midi_file_with_running_status_t::track_events_t::~track_events_t() {} diff --git a/src/main/file/kaitai/generated/standard_midi_file_with_running_status.h b/src/main/file/kaitai/generated/standard_midi_file_with_running_status.h new file mode 100644 index 000000000..5316cece0 --- /dev/null +++ b/src/main/file/kaitai/generated/standard_midi_file_with_running_status.h @@ -0,0 +1,589 @@ +#pragma once + +// This is a generated file! Please edit source .ksy file and use kaitai-struct-compiler to rebuild + +class standard_midi_file_with_running_status_t; + +#include "kaitai/kaitaistruct.h" +#include +#include +#include "vlq_base128_be.h" +#include +#include + +#if KAITAI_STRUCT_VERSION < 11000L +#error "Incompatible Kaitai Struct C++/STL API: version 0.11 or later is required" +#endif + +/** + * Standard MIDI file, typically known just as "MID", is a standard way + * to serialize series of MIDI events, which is a protocol used in many + * music synthesizers to transfer music data: notes being played, + * effects being applied, etc. + * + * Internally, file consists of a header and series of tracks, every + * track listing MIDI events with certain header designating time these + * events are happening. + * + * NOTE: Rarely, MIDI files employ certain stateful compression scheme + * to avoid storing certain elements of further elements, instead + * reusing them from events which happened earlier in the + * stream. Kaitai Struct (as of v0.9) is currently unable to parse + * these, but files employing this mechanism are relatively rare. + */ + +class standard_midi_file_with_running_status_t : public kaitai::kstruct { + +public: + class channel_pressure_event_t; + class controller_event_t; + class header_t; + class meta_event_body_t; + class note_off_event_t; + class note_on_event_t; + class pitch_bend_event_t; + class polyphonic_pressure_event_t; + class program_change_event_t; + class sysex_event_body_t; + class track_t; + class track_event_t; + class track_events_t; + +private: + bool m__dirty; + +public: + + standard_midi_file_with_running_status_t(kaitai::kstream* p__io, kaitai::kstruct* p__parent = nullptr, standard_midi_file_with_running_status_t* p__root = nullptr); + void _read(); + void _fetch_instances(); + void _write(); + void _check(); + ~standard_midi_file_with_running_status_t(); + + class channel_pressure_event_t : public kaitai::kstruct { + + public: + + private: + bool m__dirty; + + public: + + channel_pressure_event_t(kaitai::kstream* p__io, standard_midi_file_with_running_status_t::track_event_t* p__parent = nullptr, standard_midi_file_with_running_status_t* p__root = nullptr); + void _read(); + void _fetch_instances(); + void _write(); + void _check(); + ~channel_pressure_event_t(); + uint8_t pressure() const { return m_pressure; } + void set_pressure(uint8_t _v) { m__dirty = true; m_pressure = std::move(_v); } + standard_midi_file_with_running_status_t* _root() const { return m__root; } + void set__root(standard_midi_file_with_running_status_t* _v) { m__dirty = true; m__root = std::move(_v); } + kaitai::kstruct* _parent() const { return m__parent; } + void set__parent(standard_midi_file_with_running_status_t::track_event_t* _v) { m__dirty = true; m__parent = std::move(_v); } + + private: + uint8_t m_pressure; + standard_midi_file_with_running_status_t* m__root; + standard_midi_file_with_running_status_t::track_event_t* m__parent; + }; + + class controller_event_t : public kaitai::kstruct { + + public: + + private: + bool m__dirty; + + public: + + controller_event_t(kaitai::kstream* p__io, standard_midi_file_with_running_status_t::track_event_t* p__parent = nullptr, standard_midi_file_with_running_status_t* p__root = nullptr); + void _read(); + void _fetch_instances(); + void _write(); + void _check(); + ~controller_event_t(); + uint8_t controller() const { return m_controller; } + void set_controller(uint8_t _v) { m__dirty = true; m_controller = std::move(_v); } + uint8_t value() const { return m_value; } + void set_value(uint8_t _v) { m__dirty = true; m_value = std::move(_v); } + standard_midi_file_with_running_status_t* _root() const { return m__root; } + void set__root(standard_midi_file_with_running_status_t* _v) { m__dirty = true; m__root = std::move(_v); } + kaitai::kstruct* _parent() const { return m__parent; } + void set__parent(standard_midi_file_with_running_status_t::track_event_t* _v) { m__dirty = true; m__parent = std::move(_v); } + + private: + uint8_t m_controller; + uint8_t m_value; + standard_midi_file_with_running_status_t* m__root; + standard_midi_file_with_running_status_t::track_event_t* m__parent; + }; + + class header_t : public kaitai::kstruct { + + public: + + private: + bool m__dirty; + + public: + + header_t(kaitai::kstream* p__io, standard_midi_file_with_running_status_t* p__parent = nullptr, standard_midi_file_with_running_status_t* p__root = nullptr); + void _read(); + void _fetch_instances(); + void _write(); + void _check(); + ~header_t(); + std::string magic() const { return m_magic; } + void set_magic(std::string _v) { m__dirty = true; m_magic = std::move(_v); } + uint32_t len_header() const { return m_len_header; } + void set_len_header(uint32_t _v) { m__dirty = true; m_len_header = std::move(_v); } + uint16_t format() const { return m_format; } + void set_format(uint16_t _v) { m__dirty = true; m_format = std::move(_v); } + uint16_t num_tracks() const { return m_num_tracks; } + void set_num_tracks(uint16_t _v) { m__dirty = true; m_num_tracks = std::move(_v); } + int16_t division() const { return m_division; } + void set_division(int16_t _v) { m__dirty = true; m_division = std::move(_v); } + standard_midi_file_with_running_status_t* _root() const { return m__root; } + void set__root(standard_midi_file_with_running_status_t* _v) { m__dirty = true; m__root = std::move(_v); } + kaitai::kstruct* _parent() const { return m__parent; } + void set__parent(standard_midi_file_with_running_status_t* _v) { m__dirty = true; m__parent = std::move(_v); } + + private: + std::string m_magic; + uint32_t m_len_header; + uint16_t m_format; + uint16_t m_num_tracks; + int16_t m_division; + standard_midi_file_with_running_status_t* m__root; + standard_midi_file_with_running_status_t* m__parent; + }; + + class meta_event_body_t : public kaitai::kstruct { + + public: + + enum meta_type_enum_t { + META_TYPE_ENUM_SEQUENCE_NUMBER = 0, + META_TYPE_ENUM_TEXT_EVENT = 1, + META_TYPE_ENUM_COPYRIGHT = 2, + META_TYPE_ENUM_SEQUENCE_TRACK_NAME = 3, + META_TYPE_ENUM_INSTRUMENT_NAME = 4, + META_TYPE_ENUM_LYRIC_TEXT = 5, + META_TYPE_ENUM_MARKER_TEXT = 6, + META_TYPE_ENUM_CUE_POINT = 7, + META_TYPE_ENUM_MIDI_CHANNEL_PREFIX_ASSIGNMENT = 32, + META_TYPE_ENUM_END_OF_TRACK = 47, + META_TYPE_ENUM_TEMPO = 81, + META_TYPE_ENUM_SMPTE_OFFSET = 84, + META_TYPE_ENUM_TIME_SIGNATURE = 88, + META_TYPE_ENUM_KEY_SIGNATURE = 89, + META_TYPE_ENUM_SEQUENCER_SPECIFIC_EVENT = 127 + }; + static bool _is_defined_meta_type_enum_t(meta_type_enum_t v); + + private: + static const std::set _values_meta_type_enum_t; + + public: + + private: + bool m__dirty; + + public: + + meta_event_body_t(kaitai::kstream* p__io, standard_midi_file_with_running_status_t::track_event_t* p__parent = nullptr, standard_midi_file_with_running_status_t* p__root = nullptr); + void _read(); + void _fetch_instances(); + void _write(); + void _check(); + ~meta_event_body_t(); + meta_type_enum_t meta_type() const { return m_meta_type; } + void set_meta_type(meta_type_enum_t _v) { m__dirty = true; m_meta_type = std::move(_v); } + vlq_base128_be_t* len() const { return m_len.get(); } + void set_len(std::unique_ptr _v) { m__dirty = true; m_len = std::move(_v); } + std::string body() const { return m_body; } + void set_body(std::string _v) { m__dirty = true; m_body = std::move(_v); } + standard_midi_file_with_running_status_t* _root() const { return m__root; } + void set__root(standard_midi_file_with_running_status_t* _v) { m__dirty = true; m__root = std::move(_v); } + kaitai::kstruct* _parent() const { return m__parent; } + void set__parent(standard_midi_file_with_running_status_t::track_event_t* _v) { m__dirty = true; m__parent = std::move(_v); } + + private: + meta_type_enum_t m_meta_type; + std::unique_ptr m_len; + std::string m_body; + standard_midi_file_with_running_status_t* m__root; + standard_midi_file_with_running_status_t::track_event_t* m__parent; + }; + + class note_off_event_t : public kaitai::kstruct { + + public: + + private: + bool m__dirty; + + public: + + note_off_event_t(kaitai::kstream* p__io, standard_midi_file_with_running_status_t::track_event_t* p__parent = nullptr, standard_midi_file_with_running_status_t* p__root = nullptr); + void _read(); + void _fetch_instances(); + void _write(); + void _check(); + ~note_off_event_t(); + uint8_t note() const { return m_note; } + void set_note(uint8_t _v) { m__dirty = true; m_note = std::move(_v); } + uint8_t velocity() const { return m_velocity; } + void set_velocity(uint8_t _v) { m__dirty = true; m_velocity = std::move(_v); } + standard_midi_file_with_running_status_t* _root() const { return m__root; } + void set__root(standard_midi_file_with_running_status_t* _v) { m__dirty = true; m__root = std::move(_v); } + kaitai::kstruct* _parent() const { return m__parent; } + void set__parent(standard_midi_file_with_running_status_t::track_event_t* _v) { m__dirty = true; m__parent = std::move(_v); } + + private: + uint8_t m_note; + uint8_t m_velocity; + standard_midi_file_with_running_status_t* m__root; + standard_midi_file_with_running_status_t::track_event_t* m__parent; + }; + + class note_on_event_t : public kaitai::kstruct { + + public: + + private: + bool m__dirty; + + public: + + note_on_event_t(kaitai::kstream* p__io, standard_midi_file_with_running_status_t::track_event_t* p__parent = nullptr, standard_midi_file_with_running_status_t* p__root = nullptr); + void _read(); + void _fetch_instances(); + void _write(); + void _check(); + ~note_on_event_t(); + uint8_t note() const { return m_note; } + void set_note(uint8_t _v) { m__dirty = true; m_note = std::move(_v); } + uint8_t velocity() const { return m_velocity; } + void set_velocity(uint8_t _v) { m__dirty = true; m_velocity = std::move(_v); } + standard_midi_file_with_running_status_t* _root() const { return m__root; } + void set__root(standard_midi_file_with_running_status_t* _v) { m__dirty = true; m__root = std::move(_v); } + kaitai::kstruct* _parent() const { return m__parent; } + void set__parent(standard_midi_file_with_running_status_t::track_event_t* _v) { m__dirty = true; m__parent = std::move(_v); } + + private: + uint8_t m_note; + uint8_t m_velocity; + standard_midi_file_with_running_status_t* m__root; + standard_midi_file_with_running_status_t::track_event_t* m__parent; + }; + + class pitch_bend_event_t : public kaitai::kstruct { + + public: + + private: + bool m__dirty; + + public: + + pitch_bend_event_t(kaitai::kstream* p__io, standard_midi_file_with_running_status_t::track_event_t* p__parent = nullptr, standard_midi_file_with_running_status_t* p__root = nullptr); + void _read(); + void _fetch_instances(); + void _write(); + void _check(); + ~pitch_bend_event_t(); + int32_t adj_bend_value(); + void _invalidate_adj_bend_value() { f_adj_bend_value = false; } + int32_t bend_value(); + void _invalidate_bend_value() { f_bend_value = false; } + uint8_t b1() const { return m_b1; } + void set_b1(uint8_t _v) { m__dirty = true; f_adj_bend_value = false; f_bend_value = false; m_b1 = std::move(_v); } + uint8_t b2() const { return m_b2; } + void set_b2(uint8_t _v) { m__dirty = true; f_adj_bend_value = false; f_bend_value = false; m_b2 = std::move(_v); } + standard_midi_file_with_running_status_t* _root() const { return m__root; } + void set__root(standard_midi_file_with_running_status_t* _v) { m__dirty = true; f_adj_bend_value = false; f_bend_value = false; m__root = std::move(_v); } + kaitai::kstruct* _parent() const { return m__parent; } + void set__parent(standard_midi_file_with_running_status_t::track_event_t* _v) { m__dirty = true; f_adj_bend_value = false; f_bend_value = false; m__parent = std::move(_v); } + + private: + bool f_adj_bend_value; + int32_t m_adj_bend_value; + bool f_bend_value; + int32_t m_bend_value; + uint8_t m_b1; + uint8_t m_b2; + standard_midi_file_with_running_status_t* m__root; + standard_midi_file_with_running_status_t::track_event_t* m__parent; + }; + + class polyphonic_pressure_event_t : public kaitai::kstruct { + + public: + + private: + bool m__dirty; + + public: + + polyphonic_pressure_event_t(kaitai::kstream* p__io, standard_midi_file_with_running_status_t::track_event_t* p__parent = nullptr, standard_midi_file_with_running_status_t* p__root = nullptr); + void _read(); + void _fetch_instances(); + void _write(); + void _check(); + ~polyphonic_pressure_event_t(); + uint8_t note() const { return m_note; } + void set_note(uint8_t _v) { m__dirty = true; m_note = std::move(_v); } + uint8_t pressure() const { return m_pressure; } + void set_pressure(uint8_t _v) { m__dirty = true; m_pressure = std::move(_v); } + standard_midi_file_with_running_status_t* _root() const { return m__root; } + void set__root(standard_midi_file_with_running_status_t* _v) { m__dirty = true; m__root = std::move(_v); } + kaitai::kstruct* _parent() const { return m__parent; } + void set__parent(standard_midi_file_with_running_status_t::track_event_t* _v) { m__dirty = true; m__parent = std::move(_v); } + + private: + uint8_t m_note; + uint8_t m_pressure; + standard_midi_file_with_running_status_t* m__root; + standard_midi_file_with_running_status_t::track_event_t* m__parent; + }; + + class program_change_event_t : public kaitai::kstruct { + + public: + + private: + bool m__dirty; + + public: + + program_change_event_t(kaitai::kstream* p__io, standard_midi_file_with_running_status_t::track_event_t* p__parent = nullptr, standard_midi_file_with_running_status_t* p__root = nullptr); + void _read(); + void _fetch_instances(); + void _write(); + void _check(); + ~program_change_event_t(); + uint8_t program() const { return m_program; } + void set_program(uint8_t _v) { m__dirty = true; m_program = std::move(_v); } + standard_midi_file_with_running_status_t* _root() const { return m__root; } + void set__root(standard_midi_file_with_running_status_t* _v) { m__dirty = true; m__root = std::move(_v); } + kaitai::kstruct* _parent() const { return m__parent; } + void set__parent(standard_midi_file_with_running_status_t::track_event_t* _v) { m__dirty = true; m__parent = std::move(_v); } + + private: + uint8_t m_program; + standard_midi_file_with_running_status_t* m__root; + standard_midi_file_with_running_status_t::track_event_t* m__parent; + }; + + class sysex_event_body_t : public kaitai::kstruct { + + public: + + private: + bool m__dirty; + + public: + + sysex_event_body_t(kaitai::kstream* p__io, standard_midi_file_with_running_status_t::track_event_t* p__parent = nullptr, standard_midi_file_with_running_status_t* p__root = nullptr); + void _read(); + void _fetch_instances(); + void _write(); + void _check(); + ~sysex_event_body_t(); + vlq_base128_be_t* len() const { return m_len.get(); } + void set_len(std::unique_ptr _v) { m__dirty = true; m_len = std::move(_v); } + std::string data() const { return m_data; } + void set_data(std::string _v) { m__dirty = true; m_data = std::move(_v); } + standard_midi_file_with_running_status_t* _root() const { return m__root; } + void set__root(standard_midi_file_with_running_status_t* _v) { m__dirty = true; m__root = std::move(_v); } + kaitai::kstruct* _parent() const { return m__parent; } + void set__parent(standard_midi_file_with_running_status_t::track_event_t* _v) { m__dirty = true; m__parent = std::move(_v); } + + private: + std::unique_ptr m_len; + std::string m_data; + standard_midi_file_with_running_status_t* m__root; + standard_midi_file_with_running_status_t::track_event_t* m__parent; + }; + + class track_t : public kaitai::kstruct { + + public: + + private: + bool m__dirty; + + public: + + track_t(kaitai::kstream* p__io, standard_midi_file_with_running_status_t* p__parent = nullptr, standard_midi_file_with_running_status_t* p__root = nullptr); + void _read(); + void _fetch_instances(); + void _write(); + void _check(); + ~track_t(); + std::string magic() const { return m_magic; } + void set_magic(std::string _v) { m__dirty = true; m_magic = std::move(_v); } + uint32_t len_events() const { return m_len_events; } + void set_len_events(uint32_t _v) { m__dirty = true; m_len_events = std::move(_v); } + track_events_t* events() const { return m_events.get(); } + void set_events(std::unique_ptr _v) { m__dirty = true; m_events = std::move(_v); } + standard_midi_file_with_running_status_t* _root() const { return m__root; } + void set__root(standard_midi_file_with_running_status_t* _v) { m__dirty = true; m__root = std::move(_v); } + kaitai::kstruct* _parent() const { return m__parent; } + void set__parent(standard_midi_file_with_running_status_t* _v) { m__dirty = true; m__parent = std::move(_v); } + std::string _raw_events() const { return m__raw_events; } + void set__raw_events(std::string _v) { m__dirty = true; m__raw_events = std::move(_v); } + kaitai::kstream* _io__raw_events() const { return m__io__raw_events.get(); } + void set__io__raw_events(std::unique_ptr _v) { m__io__raw_events = std::move(_v); } + + private: + std::string m_magic; + uint32_t m_len_events; + std::unique_ptr m_events; + standard_midi_file_with_running_status_t* m__root; + standard_midi_file_with_running_status_t* m__parent; + std::string m__raw_events; + std::unique_ptr m__io__raw_events; + }; + + class track_event_t : public kaitai::kstruct { + + public: + + private: + bool m__dirty; + + public: + + track_event_t(uint8_t p_previous_event_type, kaitai::kstream* p__io, standard_midi_file_with_running_status_t::track_events_t* p__parent = nullptr, standard_midi_file_with_running_status_t* p__root = nullptr); + void _read(); + void _fetch_instances(); + void _write(); + void _check(); + ~track_event_t(); + int32_t channel(); + void _invalidate_channel() { f_channel = false; } + int32_t event_type(); + void _invalidate_event_type() { f_event_type = false; } + uint8_t status_byte_lookahead(); + void set_status_byte_lookahead(uint8_t _v) { m__dirty = true; f_channel = false; f_event_type = false; f_using_running_status = false; e_status_byte_lookahead = false; f_status_byte_lookahead = true; m_status_byte_lookahead = std::move(_v); } + void set_status_byte_lookahead_enabled(bool _v) { m__dirty = true; e_status_byte_lookahead = _v; } + + private: + void _write_status_byte_lookahead(); + + public: + bool using_running_status(); + void _invalidate_using_running_status() { f_using_running_status = false; } + vlq_base128_be_t* v_time() const { return m_v_time.get(); } + void set_v_time(std::unique_ptr _v) { m__dirty = true; f_channel = false; f_event_type = false; f_using_running_status = false; m_v_time = std::move(_v); } + uint8_t event_header() const { return m_event_header; } + void set_event_header(uint8_t _v) { m__dirty = true; n_event_header = false; f_channel = false; f_event_type = false; f_using_running_status = false; m_event_header = std::move(_v); } + meta_event_body_t* meta_event_body() const { return m_meta_event_body.get(); } + void set_meta_event_body(std::unique_ptr _v) { m__dirty = true; f_channel = false; f_event_type = false; f_using_running_status = false; m_meta_event_body = std::move(_v); } + sysex_event_body_t* sysex_body() const { return m_sysex_body.get(); } + void set_sysex_body(std::unique_ptr _v) { m__dirty = true; f_channel = false; f_event_type = false; f_using_running_status = false; m_sysex_body = std::move(_v); } + kaitai::kstruct* event_body() const { return m_event_body.get(); } + void set_event_body(std::unique_ptr _v) { m__dirty = true; f_channel = false; f_event_type = false; f_using_running_status = false; m_event_body = std::move(_v); } + uint8_t previous_event_type() const { return m_previous_event_type; } + void set_previous_event_type(uint8_t _v) { m__dirty = true; f_channel = false; f_event_type = false; f_using_running_status = false; m_previous_event_type = std::move(_v); } + standard_midi_file_with_running_status_t* _root() const { return m__root; } + void set__root(standard_midi_file_with_running_status_t* _v) { m__dirty = true; f_channel = false; f_event_type = false; f_using_running_status = false; m__root = std::move(_v); } + kaitai::kstruct* _parent() const { return m__parent; } + void set__parent(standard_midi_file_with_running_status_t::track_events_t* _v) { m__dirty = true; f_channel = false; f_event_type = false; f_using_running_status = false; m__parent = std::move(_v); } + + private: + bool f_channel; + int32_t m_channel; + bool n_channel; + + public: + bool _is_null_channel() { channel(); return n_channel; }; + + private: + bool f_event_type; + int32_t m_event_type; + bool f_status_byte_lookahead; + uint8_t m_status_byte_lookahead; + bool w_status_byte_lookahead; + bool e_status_byte_lookahead; + bool f_using_running_status; + bool m_using_running_status; + std::unique_ptr m_v_time; + uint8_t m_event_header; + bool n_event_header; + + public: + bool _is_null_event_header() { event_header(); return n_event_header; }; + + private: + std::unique_ptr m_meta_event_body; + + public: + bool _is_null_meta_event_body() { return !meta_event_body(); }; + + private: + std::unique_ptr m_sysex_body; + + public: + bool _is_null_sysex_body() { return !sysex_body(); }; + + private: + std::unique_ptr m_event_body; + + public: + bool _is_null_event_body() { return !event_body(); }; + + private: + uint8_t m_previous_event_type; + standard_midi_file_with_running_status_t* m__root; + standard_midi_file_with_running_status_t::track_events_t* m__parent; + }; + + class track_events_t : public kaitai::kstruct { + + public: + + private: + bool m__dirty; + + public: + + track_events_t(kaitai::kstream* p__io, standard_midi_file_with_running_status_t::track_t* p__parent = nullptr, standard_midi_file_with_running_status_t* p__root = nullptr); + void _read(); + void _fetch_instances(); + void _write(); + void _check(); + ~track_events_t(); + std::vector>* event() const { return m_event.get(); } + void set_event(std::unique_ptr>> _v) { m__dirty = true; m_event = std::move(_v); } + standard_midi_file_with_running_status_t* _root() const { return m__root; } + void set__root(standard_midi_file_with_running_status_t* _v) { m__dirty = true; m__root = std::move(_v); } + kaitai::kstruct* _parent() const { return m__parent; } + void set__parent(standard_midi_file_with_running_status_t::track_t* _v) { m__dirty = true; m__parent = std::move(_v); } + + private: + std::unique_ptr>> m_event; + standard_midi_file_with_running_status_t* m__root; + standard_midi_file_with_running_status_t::track_t* m__parent; + }; + +public: + header_t* hdr() const { return m_hdr.get(); } + void set_hdr(std::unique_ptr _v) { m__dirty = true; m_hdr = std::move(_v); } + std::vector>* tracks() const { return m_tracks.get(); } + void set_tracks(std::unique_ptr>> _v) { m__dirty = true; m_tracks = std::move(_v); } + standard_midi_file_with_running_status_t* _root() const { return m__root; } + void set__root(standard_midi_file_with_running_status_t* _v) { m__dirty = true; m__root = std::move(_v); } + kaitai::kstruct* _parent() const { return m__parent; } + void set__parent(kaitai::kstruct* _v) { m__dirty = true; m__parent = std::move(_v); } + +private: + std::unique_ptr m_hdr; + std::unique_ptr>> m_tracks; + standard_midi_file_with_running_status_t* m__root; + kaitai::kstruct* m__parent; +}; diff --git a/src/main/file/kaitai/generated/vlq_base128_be.cpp b/src/main/file/kaitai/generated/vlq_base128_be.cpp new file mode 100644 index 000000000..6ee58ab88 --- /dev/null +++ b/src/main/file/kaitai/generated/vlq_base128_be.cpp @@ -0,0 +1,125 @@ +// This is a generated file! Please edit source .ksy file and use kaitai-struct-compiler to rebuild + +#include "vlq_base128_be.h" +#include +#include + +vlq_base128_be_t::vlq_base128_be_t(kaitai::kstream* p__io, kaitai::kstruct* p__parent, vlq_base128_be_t* p__root) : kaitai::kstruct(p__io) { + m__parent = p__parent; + m__root = p__root ? p__root : this; + m__dirty = false; + f_last = false; + f_value = false; +} + +void vlq_base128_be_t::_read() { + m_groups = std::unique_ptr>>(new std::vector>()); + { + int i = 0; + group_t* _; + do { + std::unique_ptr _t_groups = std::unique_ptr(new group_t(m__io, this, m__root)); + try { + _t_groups->_read(); + } catch(...) { + _ = _t_groups.get(); + m_groups->push_back(std::move(_t_groups)); + throw; + } + _ = _t_groups.get(); + m_groups->push_back(std::move(_t_groups)); + i++; + } while (!(!(_->has_next()))); + } + m__dirty = false; +} + +void vlq_base128_be_t::_fetch_instances() { + for (std::size_t i = 0; i < m_groups->size(); ++i) { + m_groups->at(i).get()->_fetch_instances(); + } +} + +void vlq_base128_be_t::_write() { + if (m_groups == nullptr) { + throw std::runtime_error("/seq/0: repeated field is not set"); + } + for (std::vector>::const_iterator it = m_groups->begin(); it != m_groups->end(); ++it) { + if ((*it).get() == nullptr) { + throw std::runtime_error("/seq/0: nested object is not set"); + } + (*it).get()->_set_io(m__io); + (*it).get()->_write(); + } + _fetch_instances(); + m__dirty = false; +} + +void vlq_base128_be_t::_check() { + if (m_groups == nullptr) { + throw std::runtime_error("/seq/0: repeated field is not set"); + } + if (m_groups->empty()) { + throw std::runtime_error("/seq/0: repeat-until field must not be empty"); + } + for (std::vector>::const_iterator it = m_groups->begin(); it != m_groups->end(); ++it) { + const std::size_t i = static_cast(it - m_groups->begin()); + const group_t* _ = (*it).get(); + if ((*it).get() == nullptr) { + throw std::runtime_error("/seq/0: nested object is not set"); + } + (*it).get()->_set_io(m__io); + (*it).get()->_check(); + const bool _is_last = (i == m_groups->size() - 1); + if ((!(_->has_next())) != _is_last) { + throw std::runtime_error("/seq/0: repeat-until condition mismatch"); + } + } + m__dirty = false; +} + +vlq_base128_be_t::~vlq_base128_be_t() {} + +vlq_base128_be_t::group_t::group_t(kaitai::kstream* p__io, vlq_base128_be_t* p__parent, vlq_base128_be_t* p__root) : kaitai::kstruct(p__io) { + m__parent = p__parent; + m__root = p__root; + m__dirty = false; +} + +void vlq_base128_be_t::group_t::_read() { + m_has_next = m__io->read_bits_int_be(1); + m_value = m__io->read_bits_int_be(7); + m__dirty = false; +} + +void vlq_base128_be_t::group_t::_fetch_instances() { +} + +void vlq_base128_be_t::group_t::_write() { + m__io->write_bits_int_be(1, ((m_has_next) ? 1 : 0)); + m__io->write_bits_int_be(7, m_value); + _fetch_instances(); + m__dirty = false; +} + +void vlq_base128_be_t::group_t::_check() { + m__dirty = false; +} + +vlq_base128_be_t::group_t::~group_t() {} + +int32_t vlq_base128_be_t::last() { + if (f_last) + return m_last; + f_last = true; + m_last = groups()->size() - 1; + return m_last; +} + +uint64_t vlq_base128_be_t::value() { + if (f_value) + return m_value; + f_value = true; + m_value = static_cast(((((((groups()->at(last())->value() + ((last() >= 1) ? (groups()->at(last() - 1)->value() << 7) : (0))) + ((last() >= 2) ? (groups()->at(last() - 2)->value() << 14) : (0))) + ((last() >= 3) ? (groups()->at(last() - 3)->value() << 21) : (0))) + ((last() >= 4) ? (groups()->at(last() - 4)->value() << 28) : (0))) + ((last() >= 5) ? (groups()->at(last() - 5)->value() << 35) : (0))) + ((last() >= 6) ? (groups()->at(last() - 6)->value() << 42) : (0))) + ((last() >= 7) ? (groups()->at(last() - 7)->value() << 49) : (0))); + return m_value; +} diff --git a/src/main/file/kaitai/generated/vlq_base128_be.h b/src/main/file/kaitai/generated/vlq_base128_be.h new file mode 100644 index 000000000..a693af5ac --- /dev/null +++ b/src/main/file/kaitai/generated/vlq_base128_be.h @@ -0,0 +1,117 @@ +#pragma once + +// This is a generated file! Please edit source .ksy file and use kaitai-struct-compiler to rebuild + +class vlq_base128_be_t; + +#include "kaitai/kaitaistruct.h" +#include +#include +#include + +#if KAITAI_STRUCT_VERSION < 11000L +#error "Incompatible Kaitai Struct C++/STL API: version 0.11 or later is required" +#endif + +/** + * A variable-length unsigned integer using base128 encoding. 1-byte groups + * consist of 1-bit flag of continuation and 7-bit value chunk, and are ordered + * "most significant group first", i.e. in "big-endian" manner. + * + * This particular encoding is specified and used in: + * + * * Standard MIDI file format + * * ASN.1 BER encoding + * * RAR 5.0 file format + * + * More information on this encoding is available at + * + * + * This particular implementation supports serialized values to up 8 bytes long. + */ + +class vlq_base128_be_t : public kaitai::kstruct { + +public: + class group_t; + +private: + bool m__dirty; + +public: + + vlq_base128_be_t(kaitai::kstream* p__io, kaitai::kstruct* p__parent = nullptr, vlq_base128_be_t* p__root = nullptr); + void _read(); + void _fetch_instances(); + void _write(); + void _check(); + ~vlq_base128_be_t(); + + /** + * One byte group, clearly divided into 7-bit "value" chunk and 1-bit "continuation" flag. + */ + + class group_t : public kaitai::kstruct { + + public: + + private: + bool m__dirty; + + public: + + group_t(kaitai::kstream* p__io, vlq_base128_be_t* p__parent = nullptr, vlq_base128_be_t* p__root = nullptr); + void _read(); + void _fetch_instances(); + void _write(); + void _check(); + ~group_t(); + + /** + * If true, then we have more bytes to read + */ + bool has_next() const { return m_has_next; } + void set_has_next(bool _v) { m__dirty = true; m_has_next = std::move(_v); } + + /** + * The 7-bit (base128) numeric value chunk of this group + */ + uint64_t value() const { return m_value; } + void set_value(uint64_t _v) { m__dirty = true; m_value = std::move(_v); } + vlq_base128_be_t* _root() const { return m__root; } + void set__root(vlq_base128_be_t* _v) { m__dirty = true; m__root = std::move(_v); } + kaitai::kstruct* _parent() const { return m__parent; } + void set__parent(vlq_base128_be_t* _v) { m__dirty = true; m__parent = std::move(_v); } + + private: + bool m_has_next; + uint64_t m_value; + vlq_base128_be_t* m__root; + vlq_base128_be_t* m__parent; + }; + +public: + int32_t last(); + void _invalidate_last() { f_last = false; } + + /** + * Resulting value as normal integer + */ + uint64_t value(); + void _invalidate_value() { f_value = false; } + std::vector>* groups() const { return m_groups.get(); } + void set_groups(std::unique_ptr>> _v) { m__dirty = true; f_last = false; f_value = false; m_groups = std::move(_v); } + vlq_base128_be_t* _root() const { return m__root; } + void set__root(vlq_base128_be_t* _v) { m__dirty = true; f_last = false; f_value = false; m__root = std::move(_v); } + kaitai::kstruct* _parent() const { return m__parent; } + void set__parent(kaitai::kstruct* _v) { m__dirty = true; f_last = false; f_value = false; m__parent = std::move(_v); } + +private: + bool f_last; + int32_t m_last; + bool f_value; + uint64_t m_value; + std::unique_ptr>> m_groups; + vlq_base128_be_t* m__root; + kaitai::kstruct* m__parent; +}; diff --git a/src/main/file/mid/MidiFile.cpp b/src/main/file/mid/MidiFile.cpp deleted file mode 100644 index 721f88d05..000000000 --- a/src/main/file/mid/MidiFile.cpp +++ /dev/null @@ -1,133 +0,0 @@ -#include "file/mid/MidiFile.hpp" -#include "file/mid/MidiTrack.hpp" -#include "file/mid/util/MidiUtil.hpp" - -#include - -using namespace mpc::file::mid; -using namespace mpc::file::mid::util; - -MidiFile::MidiFile(std::shared_ptr stream) -{ - std::vector buffer(HEADER_SIZE); - stream->read(&buffer[0], buffer.size()); - initFromBuffer(buffer); - mTracks.clear(); - - for (int i = 0; i < mTrackCount; i++) - { - mTracks.emplace_back(std::make_shared(stream)); - } -} - -std::vector MidiFile::IDENTIFIER = {'M', 'T', 'h', 'd'}; - -void MidiFile::setType(int type) -{ - if (type < 0) - { - type = 0; - } - else if (type > 2) - { - type = 1; - } - else if (type == 0 && mTrackCount > 1) - { - type = 1; - } - mType = type; -} - -int MidiFile::getType() const -{ - return mType; -} - -int MidiFile::getLengthInTicks() const -{ - int length = 0; - for (auto &T : mTracks) - { - int l = T->getLengthInTicks(); - if (l > length) - { - length = l; - } - } - return length; -} - -std::vector> MidiFile::getTracks() const -{ - auto res = std::vector>(); - for (auto &t : mTracks) - { - res.push_back(t); - } - return res; -} - -void MidiFile::addTrack(const std::shared_ptr &track) -{ - addTrack(track, mTracks.size()); -} - -void MidiFile::addTrack(std::shared_ptr track, int pos) -{ - if (pos > mTracks.size()) - { - pos = mTracks.size(); - } - else if (pos < 0) - { - pos = 0; - } - mTracks.insert(mTracks.begin() + pos, std::move(track)); - mTrackCount = mTracks.size(); - mType = mTrackCount > 1 ? 1 : 0; -} - -void MidiFile::writeToOutputStream( - const std::shared_ptr &stream) const -{ - stream->write(&IDENTIFIER[0], IDENTIFIER.size()); - - auto val1 = MidiUtil::intToBytes(6, 4); - stream->write(&val1[0], val1.size()); - - auto type = MidiUtil::intToBytes(mType, 2); - stream->write(&type[0], type.size()); - - auto trackCount = MidiUtil::intToBytes(mTrackCount, 2); - stream->write(&trackCount[0], trackCount.size()); - - auto resolution = MidiUtil::intToBytes(mResolution, 2); - stream->write(&resolution[0], resolution.size()); - - for (auto &track : mTracks) - { - track->writeToOutputStream(stream); - } -} - -void MidiFile::initFromBuffer(std::vector &buffer) -{ - if (!MidiUtil::bytesEqual(buffer, IDENTIFIER, 0, 4)) - { - mType = 0; - mTrackCount = 0; - MLOG("File header does not indicate this is a MIDI file"); - return; - } - mType = MidiUtil::bytesToInt(buffer, 8, 2); - mTrackCount = MidiUtil::bytesToInt(buffer, 10, 2); - mResolution = MidiUtil::bytesToInt(buffer, 12, 2); - - if (mResolution != 96) - { - MLOG( - "Developer warning: non-96ppq MIDI file was loaded. Most likely " - "the event positions are incorrect."); - } -} diff --git a/src/main/file/mid/MidiFile.hpp b/src/main/file/mid/MidiFile.hpp deleted file mode 100644 index 6fb64e4b7..000000000 --- a/src/main/file/mid/MidiFile.hpp +++ /dev/null @@ -1,48 +0,0 @@ -#pragma once - -#include -#include -#include -#include - -namespace mpc::file::mid -{ - class MidiTrack; -} - -namespace mpc::file::mid -{ - - class MidiFile - { - - public: - static const int HEADER_SIZE{14}; - - private: - static std::vector IDENTIFIER; - - int mType = 0; - int mTrackCount = 0; - int mResolution = 96; - std::vector> mTracks; - - public: - void setType(int type); - int getType() const; - - int getLengthInTicks() const; - std::vector> getTracks() const; - void addTrack(const std::shared_ptr &T); - void addTrack(std::shared_ptr T, int pos); - - void writeToOutputStream(const std::shared_ptr &) const; - - private: - void initFromBuffer(std::vector &buffer); - - public: - MidiFile() = default; - explicit MidiFile(std::shared_ptr); - }; -} // namespace mpc::file::mid diff --git a/src/main/file/mid/MidiReader.cpp b/src/main/file/mid/MidiReader.cpp deleted file mode 100644 index 113212081..000000000 --- a/src/main/file/mid/MidiReader.cpp +++ /dev/null @@ -1,664 +0,0 @@ -#include "file/mid/MidiReader.hpp" -#include "sequencer/Transport.hpp" - -#include "Mpc.hpp" - -#include "file/mid/MidiFile.hpp" -#include "file/mid/MidiTrack.hpp" -#include "file/mid/event/ChannelAftertouch.hpp" -#include "file/mid/event/Controller.hpp" -#include "file/mid/event/NoteAftertouch.hpp" -#include "file/mid/event/NoteOff.hpp" -#include "file/mid/event/NoteOn.hpp" -#include "file/mid/event/PitchBend.hpp" -#include "file/mid/event/ProgramChange.hpp" -#include "file/mid/event/SystemExclusive.hpp" -#include "file/mid/event/meta/Tempo.hpp" -#include "file/mid/event/meta/Text.hpp" -#include "file/mid/event/meta/TimeSignatureEvent.hpp" -#include "file/mid/event/meta/TrackName.hpp" - -#include "sequencer/Sequence.hpp" -#include "sequencer/Track.hpp" -#include "sequencer/Sequencer.hpp" -#include "sequencer/SequencerStateManager.hpp" - -#include - -using namespace mpc::file::mid; -using namespace mpc::file::mid::event; -using namespace mpc::sequencer; - -MidiReader::MidiReader(std::shared_ptr istream, - const std::weak_ptr &dest) - : dest(dest) -{ - midiFile = std::make_unique(istream); -} - -void MidiReader::parseSequence(Mpc &mpc) const -{ - auto lSequencer = mpc.getSequencer(); - auto midiTracks = midiFile->getTracks(); - int lengthInTicks = 0; - for (const auto &mtWeak : midiTracks) - { - if (const auto mt = mtWeak.lock()) - { - lengthInTicks = std::max(lengthInTicks, mt->getLengthInTicks() + - mt->getEndOfTrackDelta()); - } - } - - std::vector> timeSignatures; - std::vector> tempoChanges; - - auto firstLoopBar = -1; - auto lastLoopBar = -1; - - auto sequence = dest.lock(); - sequence->setUsed(true); - - const auto sequenceIndex = sequence->getSequenceIndex(); - - UpdateSequenceEvents updateSequenceEvents{sequenceIndex}; - updateSequenceEvents.trackSnapshots = - &lSequencer->getStateManager()->trackEventsSnapshots[sequenceIndex]; - updateSequenceEvents.trackSnapshots->clear(); - - UpdateSequenceTracks updateSequenceTracks{sequence->getSequenceIndex()}; - updateSequenceTracks.trackStates = - &lSequencer->getStateManager()->trackStatesSnapshots[sequenceIndex]; - *updateSequenceTracks.trackStates = SequenceTrackStatesSnapshot(); - - bool isMpc2000XlMidiFile = false; - - for (auto &event : midiTracks[0].lock()->getEvents()) - { - if (const auto textEvent = - std::dynamic_pointer_cast(event.lock()); - textEvent) - { - auto text = textEvent->getText(); - - if (text.find("LOOP=ON") != std::string::npos) - { - sequence->setLoopEnabled(true); - } - else if (text.find("LOOP=OFF") != std::string::npos) - { - sequence->setLoopEnabled(false); - } - - if (text.find("TEMPO=ON") != std::string::npos) - { - lSequencer->getTransport()->setTempoSourceIsSequence(true); - } - else if (text.find("TEMPO=OFF") != std::string::npos) - { - lSequencer->getTransport()->setTempoSourceIsSequence(false); - } - - firstLoopBar = stoi(text.substr(15, 18)); - lastLoopBar = -1; - - if (isInteger(text.substr(23, 26))) - { - lastLoopBar = stoi(text.substr(23, 26)); - } - } - else if (const auto trackNameEvent = - std::dynamic_pointer_cast(event.lock()); - trackNameEvent && trackNameEvent->getTrackName().find( - "MPC2000XL 1.00 ") != std::string::npos) - { - isMpc2000XlMidiFile = true; - auto sequenceName = trackNameEvent->getTrackName().substr(16); - sequence->setName(sequenceName); - } - else if (const auto tempoEvent = - std::dynamic_pointer_cast(event.lock()); - tempoEvent) - { - tempoChanges.push_back(tempoEvent); - } - else if (const auto timeSigEvent = - std::dynamic_pointer_cast( - event.lock()); - timeSigEvent) - { - timeSignatures.push_back(timeSigEvent); - } - } - - const auto initialTempo = - tempoChanges.empty() ? 120 : tempoChanges[0].lock()->getBpm(); - - sequence->setInitialTempo(initialTempo); - - if (!lSequencer->getTransport()->isTempoSourceSequence()) - { - lSequencer->getTransport()->setTempo(initialTempo); - } - - for (int i = 1; i < tempoChanges.size(); i++) - { - auto lTcMidi = tempoChanges[i].lock(); - auto ratio = lTcMidi->getBpm() / initialTempo; - auto amount = static_cast(ratio * 1000.0); - sequence->addTempoChangeEvent(lTcMidi->getTick(), amount); - } - - const bool hadExplicitTimeSignatures = !timeSignatures.empty(); - - if (!hadExplicitTimeSignatures) - { - timeSignatures.push_back(std::make_shared( - 0, 0, DefaultTimeSigNumerator, DefaultTimeSigDenominator, 24, 8)); - } - - lengthInTicks = std::max(lengthInTicks, 1); - - int accumLength = 0; - int barCounter = 0; - - for (int i = 0; i < timeSignatures.size(); i++) - { - auto current = timeSignatures[i].lock(); - std::shared_ptr next; - - if (timeSignatures.size() > i + 1) - { - next = timeSignatures[i + 1].lock(); - } - - if (next) - { - while (accumLength < next->getTick()) - { - sequence->setTimeSignature(barCounter, current->getNumerator(), - current->getRealDenominator()); - const auto newDenTicks = - 96 * (4.0 / current->getRealDenominator()); - const auto barLength = - static_cast(newDenTicks * current->getNumerator()); - accumLength += barLength; - barCounter++; - } - } - else - { - while (accumLength < lengthInTicks) - { - sequence->setTimeSignature(barCounter, current->getNumerator(), - current->getRealDenominator()); - const auto newDenTicks = - 96 * (4.0 / current->getRealDenominator()); - const auto barLength = - static_cast(newDenTicks * current->getNumerator()); - accumLength += barLength; - barCounter++; - } - } - } - - sequence->setLastBarIndex(barCounter - 1); - sequence->setFirstLoopBarIndex(BarIndex(firstLoopBar)); - - if (lastLoopBar == -1) - { - sequence->setLastLoopBarIndex(EndOfSequence); - } - else - { - sequence->setLastLoopBarIndex(BarIndex(lastLoopBar)); - } - - std::pair noteVariationData{NoteVariationTypeTune, - DefaultNoteVariationValue}; - - const std::string trackDataPrefix = "TRACK DATA:"; - - const int midiTrackStartIndexForCollectingNoteEvents = - isMpc2000XlMidiFile ? 1 : 0; - - for (int i = midiTrackStartIndexForCollectingNoteEvents; - i < midiTracks.size(); i++) - { - auto mt = midiTracks[i].lock(); - auto trackIndex = isMpc2000XlMidiFile ? i - 1 : i; - auto deviceIndex = 0; - auto busType = BusType::DRUM1; - - for (auto &e : mt->getEvents()) - { - if (auto textEvent = - std::dynamic_pointer_cast(e.lock())) - { - if (auto text = textEvent->getText(); - text.find(trackDataPrefix) != std::string::npos) - { - auto payload = text.substr(trackDataPrefix.length()); - trackIndex = stoi(payload.substr(0, 2)); - - if (auto deviceIndexStr = payload.substr(2, 2); - deviceIndexStr != "C0") - { - deviceIndex = - stoi(deviceIndexStr, nullptr, 16) - - stoi(std::string("E0"), nullptr, 16) + 1; - } - - if (payload.size() >= 16) - { - busType = busIndexToBusType( - stoi(payload.substr(14, 2), nullptr, 16)); - } - break; - } - } - } - - if (trackIndex < 0 || trackIndex > Mpc2000XlSpecs::LAST_TRACK_INDEX) - { - continue; - } - - auto &track = (*updateSequenceTracks.trackStates)[trackIndex]; - track.deviceIndex = deviceIndex; - track.busType = busType; - bool hasImportedEvents = false; - - struct OpenNote - { - EventData event; - bool isClosed = false; - }; - - std::vector openNotes; - std::vector> noteOffs; - std::vector noteOns; - - if (isMpc2000XlMidiFile) - { - for (auto &me : mt->getEvents()) - { - auto noteOff = std::dynamic_pointer_cast(me.lock()); - auto noteOn = std::dynamic_pointer_cast(me.lock()); - - if (noteOff) - { - noteVariationData = { - NoteVariationType(noteOff->getNoteValue()), - NoteVariationValue(noteOff->getVelocity())}; - } - else if (noteOn) - { - if (noteOn->getVelocity() == 0) - { - const auto isDefaultVariation = [](const EventData &event) - { - if (event.noteVariationType == NoteVariationTypeTune) - { - return event.noteVariationValue == - DefaultNoteVariationValue; - } - - if (event.noteVariationType == - NoteVariationTypeDecay || - event.noteVariationType == - NoteVariationTypeAttack) - { - return event.noteVariationValue == 0; - } - - if (event.noteVariationType == - NoteVariationTypeFilter) - { - return event.noteVariationValue == 50; - } - - return false; - }; - - auto bestIt = openNotes.end(); - - auto isEligible = [&](const OpenNote &openNote) - { - return !openNote.isClosed && - openNote.event.noteNumber == - NoteNumber(noteOn->getNoteValue()) && - openNote.event.tick <= noteOn->getTick(); - }; - - auto latestNonDefaultIt = openNotes.end(); - for (auto it = openNotes.rbegin(); it != openNotes.rend(); - ++it) - { - if (!isEligible(*it) || - isDefaultVariation(it->event)) - { - continue; - } - - latestNonDefaultIt = std::prev(it.base()); - break; - } - - if (latestNonDefaultIt != openNotes.end()) - { - auto sameVariationCount = 0; - for (auto it = openNotes.begin(); it != openNotes.end(); - ++it) - { - if (!isEligible(*it)) - { - continue; - } - - if (it->event.noteVariationType == - latestNonDefaultIt->event.noteVariationType && - it->event.noteVariationValue == - latestNonDefaultIt->event.noteVariationValue) - { - sameVariationCount++; - if (bestIt == openNotes.end()) - { - bestIt = it; - } - } - } - - if (sameVariationCount < 2) - { - bestIt = openNotes.end(); - } - } - - if (bestIt == openNotes.end()) - { - for (auto it = openNotes.begin(); it != openNotes.end(); - ++it) - { - if (!isEligible(*it)) - { - continue; - } - - bestIt = it; - break; - } - } - - if (bestIt != openNotes.end()) - { - bestIt->event.duration = - Duration(noteOn->getTick() - bestIt->event.tick); - bestIt->isClosed = true; - } - } - else - { - EventData ne; - ne.type = EventType::NoteOn; - ne.noteNumber = NoteNumber(noteOn->getNoteValue()); - ne.tick = noteOn->getTick(); - ne.velocity = Velocity(noteOn->getVelocity()); - ne.noteVariationType = noteVariationData.first; - ne.noteVariationValue = noteVariationData.second; - ne.duration = Duration(24); - openNotes.push_back(OpenNote{ne}); - noteVariationData = {NoteVariationTypeTune, - DefaultNoteVariationValue}; - } - } - } - } - else - { - for (auto &me : mt->getEvents()) - { - auto noteOff = std::dynamic_pointer_cast(me.lock()); - auto noteOn = std::dynamic_pointer_cast(me.lock()); - - if (noteOff) - { - noteOffs.push_back(noteOff); - } - else if (noteOn) - { - EventData ne; - ne.type = EventType::NoteOn; - ne.noteNumber = NoteNumber(noteOn->getNoteValue()); - ne.tick = noteOn->getTick(); - ne.velocity = Velocity(noteOn->getVelocity()); - ne.noteVariationType = noteVariationData.first; - ne.noteVariationValue = noteVariationData.second; - ne.duration = Duration(24); - openNotes.push_back(OpenNote{ne}); - } - } - } - - // - // Resolve durations and build EventData - // - for (auto &on : openNotes) - { - if (isMpc2000XlMidiFile) - { - noteOns.emplace_back(on.event); - continue; - } - - int bestIdx = -1; - int endTick = -1; - - for (int k = 0; k < noteOffs.size(); k++) - { - int offVal; - int offTick; - - if (auto nOn = std::dynamic_pointer_cast(noteOffs[k])) - { - offVal = nOn->getNoteValue(); - offTick = nOn->getTick(); - } - else - { - auto nOff = std::dynamic_pointer_cast(noteOffs[k]); - offVal = nOff->getNoteValue(); - offTick = nOff->getTick(); - } - - if (offVal == on.event.noteNumber && offTick >= on.event.tick) - { - endTick = offTick; - bestIdx = k; - break; - } - } - - EventData ne = on.event; - - if (bestIdx != -1) - { - ne.duration = Duration(endTick - on.event.tick); - noteOffs.erase(noteOffs.begin() + bestIdx); - } - - noteOns.emplace_back(ne); - } - - // - // Add final resolved notes into updateSequenceEvents - // - for (auto &n : noteOns) - { - (*updateSequenceEvents.trackSnapshots)[trackIndex].push_back(n); - hasImportedEvents = true; - } - - // - // Non-note events - // - for (auto &me : mt->getEvents()) - { - if (const auto sysEx = - std::dynamic_pointer_cast(me.lock()); - sysEx) - { - - if (auto sysExEventBytes = sysEx->getData(); - sysExEventBytes.size() == 8 && sysExEventBytes[0] == 71 && - sysExEventBytes[1] == 0 && sysExEventBytes[2] == 68 && - sysExEventBytes[3] == 69 && - sysExEventBytes[7] == static_cast(247)) - { - EventData e; - e.type = EventType::Mixer; - e.tick = sysEx->getTick(); - e.mixerParameter = sysExEventBytes[4] - 1; - e.mixerPad = sysExEventBytes[5]; - e.mixerValue = sysExEventBytes[6]; - (*updateSequenceEvents.trackSnapshots)[trackIndex] - .push_back(e); - hasImportedEvents = true; - } - else - { - std::vector bytes(sysEx->getData().size() + 1); - bytes[0] = 0xF0; - for (int j = 0; j < sysEx->getData().size(); j++) - { - bytes[j + 1] = sysEx->getData()[j]; - } - - EventData e; - e.type = EventType::SystemExclusive; - e.tick = sysEx->getTick(); - - (*updateSequenceEvents.trackSnapshots)[trackIndex] - .push_back(e); - hasImportedEvents = true; - } - } - else if (const auto noteAfterTouch = - std::dynamic_pointer_cast(me.lock()); - noteAfterTouch) - { - EventData e; - e.type = EventType::PolyPressure; - e.tick = noteAfterTouch->getTick(); - e.noteNumber = NoteNumber(noteAfterTouch->getNoteValue()); - e.amount = noteAfterTouch->getAmount(); - (*updateSequenceEvents.trackSnapshots)[trackIndex].push_back(e); - hasImportedEvents = true; - } - else if (const auto channelAfterTouch = - std::dynamic_pointer_cast( - me.lock()); - channelAfterTouch) - { - EventData e; - e.type = EventType::ChannelPressure; - e.amount = channelAfterTouch->getAmount(); - (*updateSequenceEvents.trackSnapshots)[trackIndex].push_back(e); - hasImportedEvents = true; - } - else if (const auto programChange = - std::dynamic_pointer_cast(me.lock()); - programChange) - { - EventData e; - e.type = EventType::ProgramChange; - e.programChangeProgramIndex = - ProgramIndex(programChange->getProgramNumber()); - (*updateSequenceEvents.trackSnapshots)[trackIndex].push_back(e); - hasImportedEvents = true; - } - else if (const auto trackName = - std::dynamic_pointer_cast(me.lock()); - trackName && !trackName->getTrackName().empty()) - { - track.name = trackName->getTrackName().substr( - 0, Mpc2000XlSpecs::MAX_TRACK_NAME_LENGTH); - } - else if (const auto controller = - std::dynamic_pointer_cast(me.lock()); - controller) - { - EventData e; - e.type = EventType::ControlChange; - e.controllerNumber = controller->getControllerType(); - e.controllerValue = controller->getValue(); - (*updateSequenceEvents.trackSnapshots)[trackIndex].push_back(e); - hasImportedEvents = true; - } - else if (const auto pitchBend = - std::dynamic_pointer_cast(me.lock()); - pitchBend) - { - EventData e; - e.type = EventType::PitchBend; - e.amount = pitchBend->getBendAmount(); - (*updateSequenceEvents.trackSnapshots)[trackIndex].push_back(e); - hasImportedEvents = true; - } - } - - track.used = hasImportedEvents; - } - - mpc.getSequencer()->getStateManager()->enqueue(updateSequenceTracks); - mpc.getSequencer()->getStateManager()->enqueue(updateSequenceEvents); -} - -bool MidiReader::isInteger(const std::string &s) -{ - for (auto &c : s) - { - if (!std::isdigit(c)) - { - return false; - } - } - - return true; -} - -int MidiReader::getNumberOfNoteOns( - const int noteValue, - const std::vector> &allNotes) -{ - int counter = 0; - - for (auto &no : allNotes) - { - if (const auto noteOn = std::dynamic_pointer_cast(no)) - { - if (noteOn->getNoteValue() == noteValue) - { - counter++; - } - } - } - - return counter; -} - -int MidiReader::getNumberOfNotes(const int noteValue, - const std::vector &allNotes) -{ - int counter = 0; - - for (auto &ne : allNotes) - { - if (ne.noteNumber == noteValue) - { - counter++; - } - } - - return counter; -} diff --git a/src/main/file/mid/MidiReader.hpp b/src/main/file/mid/MidiReader.hpp deleted file mode 100644 index bbef91658..000000000 --- a/src/main/file/mid/MidiReader.hpp +++ /dev/null @@ -1,54 +0,0 @@ -#pragma once - -#include "file/mid/MidiFile.hpp" - -#include "sequencer/EventData.hpp" - -#include -#include -#include - -namespace mpc -{ - class Mpc; -} - -namespace mpc::file::mid::event -{ - class ChannelEvent; -} - -namespace mpc::sequencer -{ - class Sequence; - class NoteOnEvent; -} // namespace mpc::sequencer - -namespace mpc::disk -{ - class MpcFile; -} - -namespace mpc::file::mid -{ - class MidiReader - { - public: - MidiReader(std::shared_ptr, - const std::weak_ptr &dest); - void parseSequence(Mpc &) const; - - private: - static bool isInteger(const std::string &); - - std::unique_ptr midiFile; - std::weak_ptr dest; - - static int getNumberOfNoteOns( - int noteValue, - const std::vector> &allNotes); - static int - getNumberOfNotes(int noteValue, - const std::vector &allNotes); - }; -} // namespace mpc::file::mid diff --git a/src/main/file/mid/MidiTrack.cpp b/src/main/file/mid/MidiTrack.cpp deleted file mode 100644 index 947257415..000000000 --- a/src/main/file/mid/MidiTrack.cpp +++ /dev/null @@ -1,301 +0,0 @@ -#include "file/mid/MidiTrack.hpp" - -#include "file/mid/event/MidiEvent.hpp" -#include "file/mid/event/NoteOn.hpp" -#include "file/mid/event/meta/EndOfTrack.hpp" -#include "file/mid/event/meta/Tempo.hpp" -#include "file/mid/event/meta/TimeSignatureEvent.hpp" -#include "file/mid/util/MidiUtil.hpp" -#include "file/mid/util/VariableLengthInt.hpp" - -#include -#include - -#include - -using namespace mpc::file::mid; -using namespace mpc::file::mid::util; -using namespace mpc::file::mid::event; - -MidiTrack::MidiTrack() -{ - mSize = 0; - mSizeNeedsRecalculating = false; - mClosed = false; - mEndOfTrackDelta = 0; -} - -MidiTrack::MidiTrack(const std::shared_ptr &stream) -{ - mSize = 0; - mSizeNeedsRecalculating = false; - mClosed = false; - mEndOfTrackDelta = 0; - std::vector buffer(4); - - stream->read(&buffer[0], buffer.size()); - - if (!MidiUtil::bytesEqual(buffer, IDENTIFIER, 0, 4)) - { - std::string error = "Track identifier did not match MTrk!"; - throw std::invalid_argument(error); - } - - stream->read(&buffer[0], buffer.size()); - - mSize = MidiUtil::bytesToInt(buffer, 0, 4); - buffer.clear(); - buffer.resize(mSize); - stream->read(&buffer[0], mSize); - - readTrackData(buffer); -} - -const bool MidiTrack::VERBOSE; - -std::vector MidiTrack::IDENTIFIER{'M', 'T', 'r', 'k'}; - -MidiTrack *MidiTrack::createTempoTrack() -{ - auto track = new MidiTrack(); - track->insertEvent(std::make_shared()); - track->insertEvent(std::make_shared()); - return track; -} - -void MidiTrack::readTrackData(std::vector &data) -{ - std::stringstream in(std::string(data.begin(), data.end())); - in.unsetf(std::ios_base::skipws); - - int totalTicks = 0; - - auto available = (int)in.rdbuf()->in_avail(); - - while ((available = (int)in.rdbuf()->in_avail() > 0)) - { - auto delta = VariableLengthInt(in); - int value = delta.getValue(); - totalTicks += value; - - auto event = MidiEvent::parseEvent(totalTicks, value, in); - - if (!event) - { - MLOG("MidiTrack readTrackData skipped an event!"); - continue; - } - - if (std::dynamic_pointer_cast(event)) - { - mEndOfTrackDelta = event->getDelta(); - break; - } - - mEvents.emplace_back(event); - } -} - -std::vector> MidiTrack::getEvents() const -{ - std::vector> res; - for (auto &e : mEvents) - { - res.push_back(e); - } - return res; -} - -int MidiTrack::getEventCount() const -{ - return mEvents.size(); -} - -int MidiTrack::getSize() -{ - if (mSizeNeedsRecalculating) - { - recalculateSize(); - } - return mSize; -} - -int MidiTrack::getLengthInTicks() -{ - if (mEvents.size() == 0) - { - return 0; - } - auto E = *std::next(mEvents.begin(), (int)mEvents.size() - 1); - return E->getTick(); -} - -int MidiTrack::getEndOfTrackDelta() const -{ - return mEndOfTrackDelta; -} - -void MidiTrack::setEndOfTrackDelta(int delta) -{ - mEndOfTrackDelta = delta; -} - -void MidiTrack::insertNote(int channel, int pitch, int velocity, int tick, - int duration) -{ - insertEvent(std::make_shared(tick, channel, pitch, velocity)); - insertEvent(std::make_shared(tick + duration, channel, pitch, 0)); -} - -void MidiTrack::insertEvent(const std::weak_ptr &newE) -{ - auto newEvent = newE.lock(); - if (!newEvent) - { - return; - } - if (mClosed) - { - // npc(::java::lang::System::err())->println(u"Error: Cannot add an - // event to a closed track."_j); - return; - } - std::shared_ptr prev; - std::shared_ptr next; - if (mEvents.size() > 0) - { - prev = mEvents.back(); - } - - /* - for (auto& n : mEvents) { - next = n; - if (next->getTick() > newEvent->getTick()) { - break; - } - prev = next; - next.reset(); - } - */ - - mEvents.push_back(newEvent); - mSizeNeedsRecalculating = true; - - if (prev) - { - newEvent->setDelta(newEvent->getTick() - prev->getTick()); - } - else - { - newEvent->setDelta(newEvent->getTick()); - } - if (next) - { - next->setDelta(next->getTick() - newEvent->getTick()); - } - - mSize += newEvent->getSize(); - - if (std::dynamic_pointer_cast(newEvent)) - { - if (next) - { - throw std::invalid_argument( - "Attempting to insert EndOfTrack before an existing event. Use " - "closeTrack() when finished with MidiTrack."); - } - mClosed = true; - } -} - -bool MidiTrack::removeEvent(MidiEvent *e) const -{ - std::shared_ptr prev; - std::shared_ptr curr; - std::shared_ptr next; - - for (auto &n : mEvents) - { - next = n; - if (e == curr.get()) - { - break; - } - prev = curr; - curr = next; - next = nullptr; - } - if (!next) - { - // return mEvents.erase(curr); - return false; - } - // if (!mEvents.erase(curr)) { - // return false; - //} - if (prev != nullptr) - { - next->setDelta(next->getTick() - prev->getTick()); - } - else - { - next->setDelta(next->getTick()); - } - return true; -} - -void MidiTrack::closeTrack() -{ - int lastTick = 0; - if (mEvents.size() > 0) - { - auto last = std::dynamic_pointer_cast( - *std::next(mEvents.begin(), (int)mEvents.size() - 1)); - lastTick = last->getTick(); - } - insertEvent( - std::make_shared(lastTick + mEndOfTrackDelta, 0)); -} - -void MidiTrack::recalculateSize() -{ - mSize = 0; - std::shared_ptr last; - for (auto &e : mEvents) - { - mSize += e->getSize(); - if (last && !e->requiresStatusByte(last.get())) - { - mSize--; - } - last = e; - } - mSizeNeedsRecalculating = false; -} - -void MidiTrack::writeToOutputStream(const std::shared_ptr &stream) -{ - if (!mClosed) - { - closeTrack(); - } - - if (mSizeNeedsRecalculating) - { - recalculateSize(); - } - - stream->write(&IDENTIFIER[0], IDENTIFIER.size()); - - auto trackSizeBuffer = MidiUtil::intToBytes(mSize, 4); - stream->write(&trackSizeBuffer[0], trackSizeBuffer.size()); - - std::shared_ptr lastEvent; - - for (auto &event : mEvents) - { - event->writeToOutputStream(*stream.get(), - event->requiresStatusByte(lastEvent.get())); - lastEvent = event; - } -} diff --git a/src/main/file/mid/MidiTrack.hpp b/src/main/file/mid/MidiTrack.hpp deleted file mode 100644 index 4c5ca0d57..000000000 --- a/src/main/file/mid/MidiTrack.hpp +++ /dev/null @@ -1,56 +0,0 @@ -#pragma once - -#include -#include -#include -#include -#include - -namespace mpc::file::mid::event -{ - class MidiEvent; -} - -namespace mpc::file::mid -{ - class MidiTrack - { - - static const bool VERBOSE = false; - static std::vector IDENTIFIER; - - int mSize; - bool mSizeNeedsRecalculating; - bool mClosed; - int mEndOfTrackDelta; - std::vector> mEvents; - - public: - static MidiTrack *createTempoTrack(); - - private: - void readTrackData(std::vector &data); - - public: - std::vector> getEvents() const; - int getEventCount() const; - int getSize(); - int getLengthInTicks(); - int getEndOfTrackDelta() const; - void setEndOfTrackDelta(int delta); - void insertNote(int channel, int pitch, int velocity, int tick, - int duration); - void insertEvent(const std::weak_ptr &newEvent); - bool removeEvent(event::MidiEvent *E) const; - void closeTrack(); - - private: - void recalculateSize(); - - public: - void writeToOutputStream(const std::shared_ptr &); - - MidiTrack(); - MidiTrack(const std::shared_ptr &); - }; -} // namespace mpc::file::mid diff --git a/src/main/file/mid/MidiWriter.cpp b/src/main/file/mid/MidiWriter.cpp deleted file mode 100644 index b9d007415..000000000 --- a/src/main/file/mid/MidiWriter.cpp +++ /dev/null @@ -1,389 +0,0 @@ -#include "file/mid/MidiWriter.hpp" - -#include "file/mid/MidiFile.hpp" -#include "file/mid/MidiTrack.hpp" -#include "file/mid/event/ChannelAftertouch.hpp" -#include "file/mid/event/Controller.hpp" -#include "file/mid/event/MidiEvent.hpp" -#include "file/mid/event/NoteAftertouch.hpp" -#include "file/mid/event/NoteOff.hpp" -#include "file/mid/event/NoteOn.hpp" -#include "file/mid/event/PitchBend.hpp" -#include "file/mid/event/ProgramChange.hpp" -#include "file/mid/event/SystemExclusive.hpp" -#include "file/mid/event/meta/InstrumentName.hpp" -#include "file/mid/event/meta/FrameRate.hpp" -#include "file/mid/event/meta/SmpteOffset.hpp" -#include "file/mid/event/meta/Tempo.hpp" -#include "file/mid/event/meta/Text.hpp" -#include "file/mid/event/meta/TimeSignatureEvent.hpp" -#include "file/mid/event/meta/TrackName.hpp" -#include "disk/AbstractDisk.hpp" -#include "sequencer/ChannelPressureEvent.hpp" -#include "sequencer/ControlChangeEvent.hpp" -#include "sequencer/MixerEvent.hpp" -#include "sequencer/Sequence.hpp" -#include "sequencer/Track.hpp" -#include "sequencer/PitchBendEvent.hpp" -#include "sequencer/PolyPressureEvent.hpp" -#include "sequencer/ProgramChangeEvent.hpp" -#include "sequencer/SystemExclusiveEvent.hpp" -#include "sequencer/TempoChangeEvent.hpp" - -#include "StrUtil.hpp" -#include "file/ByteUtil.hpp" - -#include -#include "file/mid/util/MidiUtil.hpp" -#include "sequencer/NoteOnEvent.hpp" - -using namespace mpc::file::mid::event; -using namespace mpc::file::mid; - -MidiWriter::MidiWriter(sequencer::Sequence *sequence) -{ - this->sequence = sequence; - mf = std::make_unique(); - auto meta = std::make_shared(); - auto seqParams = std::make_shared( - 0, 0, "LOOP=ON START=000 END=END TEMPO=ON "); - meta->insertEvent(seqParams); - auto seqName = std::make_shared( - 0, 0, - "MPC2000XL 1.00 " + StrUtil::padRight(sequence->getName(), " ", 16)); - meta->insertEvent(seqName); - std::vector> tempos; - int previousTick = 0; - auto tempo = sequence->getInitialTempo(); - auto mpqn = static_cast(6.0E7 / tempo); - tempos.push_back(std::make_shared(0, 0, mpqn)); - - for (auto &e : sequence->getTempoChangeEvents()) - { - tempo = e->getTempo(); - mpqn = static_cast(6.0E7 / tempo); - tempos.push_back(std::make_shared( - e->getTick(), e->getTick() - previousTick, mpqn)); - previousTick = e->getTick(); - } - - for (auto &t : tempos) - { - meta->insertEvent(t); - } - - meta->insertEvent(std::make_shared( - 0, 0, meta::FrameRate::FRAME_RATE_25, 0, 0, 0, 0, 0)); - std::set> tSigs; - auto tSigTick = 0; - auto lastAdded = std::vector(3); - - for (int i = 0; i < sequence->getLastBarIndex() + 1; i++) - { - auto actualTick = tSigTick; - if (lastAdded[0] == sequence->getNumerator(i) && - lastAdded[1] == sequence->getDenominator(i)) - { - actualTick = lastAdded[2]; - } - - auto vec = std::vector{sequence->getNumerator(i), - sequence->getDenominator(i), actualTick}; - - if (tSigs.emplace(vec).second) - { - lastAdded[0] = sequence->getNumerator(i); - lastAdded[1] = sequence->getDenominator(i); - lastAdded[2] = actualTick; - } - - tSigTick += sequence->getBarLength(i); - } - previousTick = 0; - for (auto &ia : tSigs) - { - meta->insertEvent(std::make_shared( - ia[2], ia[2] - previousTick, ia[0], ia[1], 24, 8)); - previousTick = ia[2]; - } - meta->setEndOfTrackDelta(sequence->getLastTick()); - mf->addTrack(meta); - - int firstUsedTrackIndex = -1; - int lastUsedTrackIndex = -1; - - for (int i = 0; i < 64; i++) - { - if (sequence->getTrack(i)->isUsed()) - { - if (firstUsedTrackIndex == -1) - { - firstUsedTrackIndex = i; - } - lastUsedTrackIndex = i; - } - } - - for (int trackIndex = firstUsedTrackIndex; - trackIndex >= 0 && trackIndex <= lastUsedTrackIndex; trackIndex++) - { - noteOffs.clear(); - variations.clear(); - noteOns.clear(); - miscEvents.clear(); - - auto t = sequence->getTrack(trackIndex); - - if (t->getIndex() > 63) - { - break; - } - - auto mt = std::make_shared(); - auto in = std::make_shared(0, 0, " "); - mt->insertEvent(in); - auto trackNumber = - StrUtil::padLeft(std::to_string(t->getIndex()), "0", 2); - std::string trackDevice = t->getDeviceIndex() == 0 ? "C0" : "E0"; - - if (t->getDeviceIndex() > 0) - { - auto value = stoi(trackDevice, nullptr, 16); - value += t->getDeviceIndex() - 1; - trackDevice = util::MidiUtil::byteToHex(static_cast(value)); - } - - const auto busIndex = util::MidiUtil::byteToHex( - static_cast(busTypeToIndex(t->getBusType()))); - const auto deviceNumber = util::MidiUtil::byteToHex( - static_cast(t->getDeviceIndex())); - - auto text = std::make_shared( - 0, 0, - "TRACK DATA:" + trackNumber + trackDevice + "006403 " + - deviceNumber + busIndex + deviceNumber + " "); - mt->insertEvent(text); - auto tn = std::make_shared( - 0, 0, StrUtil::padRight(t->getName(), " ", 16)); - mt->insertEvent(tn); - for (auto &event : t->getEvents()) - { - auto noteEvent = - std::dynamic_pointer_cast(event); - auto mpcSysExEvent = - std::dynamic_pointer_cast( - event); - auto pitchBendEvent = - std::dynamic_pointer_cast(event); - auto channelPressureEvent = - std::dynamic_pointer_cast( - event); - auto polyPressureEvent = - std::dynamic_pointer_cast(event); - auto controlChangeEvent = - std::dynamic_pointer_cast(event); - auto programChangeEvent = - std::dynamic_pointer_cast(event); - auto mixerEvent = - std::dynamic_pointer_cast(event); - if (noteEvent) - { - addNoteOn(std::make_shared( - noteEvent->getTick(), t->getIndex(), noteEvent->getNote(), - noteEvent->getVelocity())); - noteOffs.push_back(std::make_shared( - noteEvent->getTick() + noteEvent->getDuration(), - t->getIndex(), noteEvent->getNote(), 0)); - auto variation = false; - std::shared_ptr varNoteOff; - auto varType = noteEvent->getVariationType(); - auto varVal = noteEvent->getVariationValue(); - if (varType == 0 && varVal != 64) - { - variation = true; - } - - if ((varType == 1 || varType == 2) && varVal != 0) - { - variation = true; - } - - if (varType == 3 && varVal != 50) - { - variation = true; - } - - if (variation) - { - varNoteOff = std::make_shared( - noteEvent->getTick(), t->getIndex(), varType, varVal); - } - - if (varNoteOff != nullptr) - { - variations.push_back(varNoteOff); - } - } - else if (mpcSysExEvent) - { - auto sysExData = std::vector{ - static_cast(mpcSysExEvent->getByteA()), - static_cast(mpcSysExEvent->getByteB())}; - - auto see = std::make_shared( - 240, mpcSysExEvent->getTick(), sysExData); - miscEvents.push_back(see); - } - else if (pitchBendEvent) - { - auto amountBytes = - ByteUtil::ushort2bytes(pitchBendEvent->getAmount() + 8192); - auto pb = std::make_shared(pitchBendEvent->getTick(), - 1, amountBytes[0] & 255, - amountBytes[1] & 255); - pb->setBendAmount(pitchBendEvent->getAmount() + 8192); - miscEvents.push_back(pb); - } - else if (channelPressureEvent) - { - auto ca = std::make_shared( - channelPressureEvent->getTick(), 1, - channelPressureEvent->getAmount()); - miscEvents.push_back(ca); - } - else if (polyPressureEvent) - { - auto na = std::make_shared( - polyPressureEvent->getTick(), 1, - polyPressureEvent->getNote(), - polyPressureEvent->getAmount()); - miscEvents.push_back(na); - } - else if (controlChangeEvent) - { - auto c = std::make_shared( - controlChangeEvent->getTick(), 1, - controlChangeEvent->getController(), - controlChangeEvent->getAmount()); - miscEvents.push_back(c); - } - else if (programChangeEvent) - { - auto pc = std::make_shared( - programChangeEvent->getTick(), 1, - programChangeEvent->getProgram()); - miscEvents.push_back(pc); - } - else if (mixerEvent) - { - auto sysExData = std::vector(8); - sysExData[0] = 71; - sysExData[1] = 0; - sysExData[2] = 68; - sysExData[3] = 69; - sysExData[4] = mixerEvent->getParameter() + 1; - sysExData[5] = mixerEvent->getPad(); - sysExData[6] = mixerEvent->getValue(); - sysExData[7] = 0xF7; - auto see = std::make_shared( - 240, mixerEvent->getTick(), sysExData); - miscEvents.push_back(see); - } - } - - for (auto i = 0; i < sequence->getLastTick(); i++) - { - for (auto &no : noteOffs) - { - if (no->getTick() == i) - { - mt->insertEvent(no); - } - } - - for (auto &var : variations) - { - if (var->getTick() == i) - { - mt->insertEvent(var); - } - } - - for (auto &no : noteOns) - { - if (no->getTick() == i) - { - mt->insertEvent(no); - } - } - - for (auto &e : miscEvents) - { - if (e->getTick() == i) - { - mt->insertEvent(e); - } - } - } - createDeltas(mt); - mf->addTrack(mt); - } - mf->setType(1); -} - -void MidiWriter::addNoteOn(const std::shared_ptr ¬eOn) -{ - for (auto &no : noteOffs) - { - if (no->getNoteValue() == noteOn->getNoteValue() && - no->getTick() > noteOn->getTick()) - { - no = std::make_shared(noteOn->getTick(), no->getChannel(), - no->getNoteValue(), 0); - } - } - noteOns.push_back(noteOn); -} - -void MidiWriter::createDeltas(const std::weak_ptr &midiTrack) const -{ - const auto mt = midiTrack.lock(); - std::shared_ptr previousEvent; - - for (auto &me : mt->getEvents()) - { - if (const auto event = me.lock()) - { - if (previousEvent) - { - if (event->getTick() != previousEvent->getTick()) - { - event->setDelta(event->getTick() - - previousEvent->getTick()); - } - else - { - event->setDelta(0); - } - } - previousEvent = event; - } - } - const auto previousTick = !previousEvent ? 0 : previousEvent->getTick(); - mt->setEndOfTrackDelta(sequence->getLastTick() - previousTick); -} - -void MidiWriter::writeToOStream( - const std::shared_ptr &_ostream) const -{ - const auto _ofstream = std::dynamic_pointer_cast(_ostream); - if (_ofstream) - { - _ofstream->unsetf(std::ios::skipws); - } - mf->writeToOutputStream(_ostream); - if (_ofstream) - { - _ofstream->close(); - } -} diff --git a/src/main/file/mid/MidiWriter.hpp b/src/main/file/mid/MidiWriter.hpp deleted file mode 100644 index b47bbc30d..000000000 --- a/src/main/file/mid/MidiWriter.hpp +++ /dev/null @@ -1,34 +0,0 @@ -#pragma once - -#include "file/mid/event/NoteOn.hpp" -#include "file/mid/event/NoteOff.hpp" -#include "file/mid/MidiFile.hpp" -#include "file/mid/MidiTrack.hpp" - -#include - -namespace mpc::sequencer -{ - class Sequence; -} - -namespace mpc::file::mid -{ - class MidiWriter - { - public: - MidiWriter(sequencer::Sequence *sequence); - void writeToOStream(const std::shared_ptr &) const; - - private: - std::vector> noteOffs; - std::vector> variations; - std::vector> noteOns; - std::vector> miscEvents; - sequencer::Sequence *sequence; - std::unique_ptr mf; - - void addNoteOn(const std::shared_ptr ¬eOn); - void createDeltas(const std::weak_ptr &midiTrack) const; - }; -} // namespace mpc::file::mid diff --git a/src/main/file/mid/event/ChannelAftertouch.cpp b/src/main/file/mid/event/ChannelAftertouch.cpp deleted file mode 100644 index e52c0742f..000000000 --- a/src/main/file/mid/event/ChannelAftertouch.cpp +++ /dev/null @@ -1,26 +0,0 @@ -#include "file/mid/event/ChannelAftertouch.hpp" - -#include "file/mid/event/ChannelEvent.hpp" - -using namespace mpc::file::mid::event; - -ChannelAftertouch::ChannelAftertouch(int tick, int channel, int amount) - : ChannelEvent(tick, CHANNEL_AFTERTOUCH, channel, amount, 0) -{ -} - -ChannelAftertouch::ChannelAftertouch(int tick, int delta, int channel, - int amount) - : ChannelEvent(tick, delta, CHANNEL_AFTERTOUCH, channel, amount, 0) -{ -} - -int ChannelAftertouch::getAmount() const -{ - return mValue1; -} - -void ChannelAftertouch::setAmount(int p) -{ - mValue1 = p; -} diff --git a/src/main/file/mid/event/ChannelAftertouch.hpp b/src/main/file/mid/event/ChannelAftertouch.hpp deleted file mode 100644 index ab01d540f..000000000 --- a/src/main/file/mid/event/ChannelAftertouch.hpp +++ /dev/null @@ -1,15 +0,0 @@ -#pragma once -#include "file/mid/event/ChannelEvent.hpp" - -namespace mpc::file::mid::event -{ - class ChannelAftertouch : public ChannelEvent - { - public: - int getAmount() const; - void setAmount(int p); - - ChannelAftertouch(int tick, int channel, int amount); - ChannelAftertouch(int tick, int delta, int channel, int amount); - }; -} // namespace mpc::file::mid::event diff --git a/src/main/file/mid/event/ChannelEvent.cpp b/src/main/file/mid/event/ChannelEvent.cpp deleted file mode 100644 index d2fcb07a6..000000000 --- a/src/main/file/mid/event/ChannelEvent.cpp +++ /dev/null @@ -1,145 +0,0 @@ -#include "file/mid/event/ChannelEvent.hpp" - -#include "file/mid/event/ChannelAftertouch.hpp" -#include "file/mid/event/Controller.hpp" -#include "file/mid/event/MidiEvent.hpp" -#include "file/mid/event/NoteAftertouch.hpp" -#include "file/mid/event/NoteOff.hpp" -#include "file/mid/event/NoteOn.hpp" -#include "file/mid/event/PitchBend.hpp" -#include "file/mid/event/ProgramChange.hpp" - -using namespace mpc::file::mid::event; - -ChannelEvent::ChannelEvent(int tick, int type, int channel, int param1, - int param2) - : ChannelEvent(tick, 0, type, channel, param1, param2) -{ -} - -ChannelEvent::ChannelEvent(int tick, int delta, int type, int channel, - int param1, int param2) - : MidiEvent(tick, delta) -{ - mType = type & 0x0F; - mChannel = channel & 0x0F; - mValue1 = param1 & 0xFF; - mValue2 = param2 & 0xFF; -} - -int ChannelEvent::getType() -{ - return mType; -} - -void ChannelEvent::setChannel(int c) -{ - if (c < 0) - { - c = 0; - } - else if (c > 15) - { - c = 15; - } - mChannel = c; -} - -int ChannelEvent::getChannel() -{ - return mChannel; -} - -int ChannelEvent::getEventSize() -{ - switch (mType) - { - case PROGRAM_CHANGE: - case CHANNEL_AFTERTOUCH: - return 2; - default: - return 3; - } -} - -int ChannelEvent::compareTo(MidiEvent *o) -{ - return 1; // hack to emulate MPC2000XL MID writing. this makes sure the - // event order is not changed after they are added -} - -bool ChannelEvent::requiresStatusByte(MidiEvent *prevEvent) -{ - if (prevEvent == nullptr) - { - return true; - } - if (dynamic_cast(prevEvent) == nullptr) - { - return true; - } - auto ce = dynamic_cast(prevEvent); - return !(mType == ce->getType() && mChannel == ce->getChannel()); -} - -void ChannelEvent::writeToOutputStream(std::ostream &out, bool writeType) -{ - MidiEvent::writeToOutputStream(out, writeType); - - if (writeType) - { - auto typeChannel = (mType << 4) + mChannel; - out << (char)typeChannel; - } - - out << (char)mValue1; - - if (mType != PROGRAM_CHANGE && mType != CHANNEL_AFTERTOUCH) - { - out << (char)mValue2; - } -} - -std::shared_ptr -ChannelEvent::parseChannelEvent(int tick, int delta, int type, int channel, - std::stringstream &in) -{ - int val1 = in.get(); - int val2 = 0; - if (type != PROGRAM_CHANGE && type != CHANNEL_AFTERTOUCH) - { - val2 = in.get(); - } - switch (type) - { - case NOTE_OFF: - return std::make_shared(tick, delta, channel, val1, val2); - case NOTE_ON: - return std::make_shared(tick, delta, channel, val1, val2); - case NOTE_AFTERTOUCH: - return std::make_shared(tick, delta, channel, val1, - val2); - case CONTROLLER: - return std::make_shared(tick, delta, channel, val1, - val2); - case PROGRAM_CHANGE: - return std::make_shared(tick, delta, channel, val1); - case CHANNEL_AFTERTOUCH: - return std::make_shared(tick, delta, channel, - val1); - case PITCH_BEND: - return std::make_shared(tick, delta, channel, val1, - val2); - default: - return std::make_shared(tick, delta, type, channel, - val1, val2); - } -} - -const int ChannelEvent::NOTE_OFF; -const int ChannelEvent::NOTE_ON; -const int ChannelEvent::NOTE_AFTERTOUCH; -const int ChannelEvent::CONTROLLER; -const int ChannelEvent::PROGRAM_CHANGE; -const int ChannelEvent::CHANNEL_AFTERTOUCH; -const int ChannelEvent::PITCH_BEND; diff --git a/src/main/file/mid/event/ChannelEvent.hpp b/src/main/file/mid/event/ChannelEvent.hpp deleted file mode 100644 index c742bf4de..000000000 --- a/src/main/file/mid/event/ChannelEvent.hpp +++ /dev/null @@ -1,43 +0,0 @@ -#pragma once - -#include "file/mid/event/MidiEvent.hpp" - -namespace mpc::file::mid::event -{ - - class ChannelEvent : public MidiEvent - { - - public: - int mType{0}; - int mChannel{0}; - int mValue1{0}; - int mValue2{0}; - - virtual int getType(); - virtual void setChannel(int c); - virtual int getChannel(); - - int getEventSize() override; - - virtual int compareTo(MidiEvent *other); - virtual bool requiresStatusByte(MidiEvent *prevEvent) override; - virtual void writeToOutputStream(std::ostream &out, - bool writeType) override; - static std::shared_ptr - parseChannelEvent(int tick, int delta, int type, int channel, - std::stringstream &in); - - static const int NOTE_OFF{8}; - static const int NOTE_ON{9}; - static const int NOTE_AFTERTOUCH{10}; - static const int CONTROLLER{11}; - static const int PROGRAM_CHANGE{12}; - static const int CHANNEL_AFTERTOUCH{13}; - static const int PITCH_BEND{14}; - - ChannelEvent(int tick, int type, int channel, int param1, int param2); - ChannelEvent(int tick, int delta, int type, int channel, int param1, - int param2); - }; -} // namespace mpc::file::mid::event diff --git a/src/main/file/mid/event/Controller.cpp b/src/main/file/mid/event/Controller.cpp deleted file mode 100644 index 1078936e1..000000000 --- a/src/main/file/mid/event/Controller.cpp +++ /dev/null @@ -1,36 +0,0 @@ -#include "file/mid/event/Controller.hpp" - -#include "file/mid/event/ChannelEvent.hpp" - -using namespace mpc::file::mid::event; - -Controller::Controller(int tick, int channel, int controllerType, int value) - : ChannelEvent(tick, CONTROLLER, channel, controllerType, value) -{ -} - -Controller::Controller(int tick, int delta, int channel, int controllerType, - int value) - : ChannelEvent(tick, delta, CONTROLLER, channel, controllerType, value) -{ -} - -int Controller::getControllerType() const -{ - return mValue1; -} - -int Controller::getValue() const -{ - return mValue2; -} - -void Controller::setControllerType(int t) -{ - mValue1 = t; -} - -void Controller::setValue(int v) -{ - mValue2 = v; -} diff --git a/src/main/file/mid/event/Controller.hpp b/src/main/file/mid/event/Controller.hpp deleted file mode 100644 index b637b5d32..000000000 --- a/src/main/file/mid/event/Controller.hpp +++ /dev/null @@ -1,19 +0,0 @@ -#pragma once -#include "file/mid/event/ChannelEvent.hpp" - -namespace mpc::file::mid::event -{ - class Controller : public ChannelEvent - { - - public: - int getControllerType() const; - int getValue() const; - void setControllerType(int t); - void setValue(int v); - - Controller(int tick, int channel, int controllerType, int value); - Controller(int tick, int delta, int channel, int controllerType, - int value); - }; -} // namespace mpc::file::mid::event diff --git a/src/main/file/mid/event/MidiEvent.cpp b/src/main/file/mid/event/MidiEvent.cpp deleted file mode 100644 index cf4c54a6a..000000000 --- a/src/main/file/mid/event/MidiEvent.cpp +++ /dev/null @@ -1,140 +0,0 @@ -#include "file/mid/event/MidiEvent.hpp" - -#include "file/mid/event/ChannelEvent.hpp" -#include "file/mid/event/SystemExclusive.hpp" -#include "file/mid/event/meta/MetaEvent.hpp" - -using namespace mpc::file::mid::event; - -MidiEvent::MidiEvent(int tick, int delta) -{ - mTick = tick; - mDelta = util::VariableLengthInt(delta); -} - -int MidiEvent::getTick() -{ - return mTick; -} - -int MidiEvent::getDelta() -{ - return mDelta.getValue(); -} - -void MidiEvent::setDelta(int d) -{ - mDelta.setValue(d); -} - -int MidiEvent::getSize() -{ - return getEventSize() + mDelta.getByteCount(); -} - -bool MidiEvent::requiresStatusByte(MidiEvent *prevEvent) -{ - if (prevEvent == nullptr) - { - return true; - } - - if (dynamic_cast(this) != nullptr) - { - return true; - } - - if (typeid(this) == typeid(prevEvent)) - { - return false; - } - - return true; -} - -void MidiEvent::writeToOutputStream(std::ostream &out, bool writeType) -{ - auto bytes = mDelta.getBytes(); - out.write(&bytes[0], bytes.size()); -} - -int MidiEvent::sId = -1; -int MidiEvent::sType = -1; -int MidiEvent::sChannel = -1; - -std::shared_ptr MidiEvent::parseEvent(int tick, int delta, - std::stringstream &in) -{ - auto reset = false; - - auto pos = in.tellg(); - - auto id = in.get() & 0xFF; - - if (!verifyIdentifier(id)) - { - in.seekg(pos); - reset = true; - } - - if (sType >= 8 && sType <= 14) - { - return ChannelEvent::parseChannelEvent(tick, delta, sType, sChannel, - in); - } - if (sId == 255) - { - return meta::MetaEvent::parseMetaEvent(tick, delta, in); - } - if (sId == 240 || sId == 247) - { - auto size = util::VariableLengthInt(in); - auto data = std::vector(size.getValue()); - in.read(&data[0], data.size()); - return std::make_shared(sId, tick, delta, data); - } - std::string error = - "Unable to handle status byte, skipping: " + std::to_string(sId); - if (reset) - { - in.ignore(1); - } - - return {}; -} - -bool MidiEvent::verifyIdentifier(int id) -{ - sId = id; - auto type = id >> 4; - auto channel = id & 15; - if (type >= 8 && type <= 14) - { - sId = id; - sType = type; - sChannel = channel; - } - else if (id == 255) - { - sId = id; - sType = -1; - sChannel = -1; - } - else if (type == 15) - { - sId = id; - sType = type; - sChannel = -1; - } - else - { - return false; - } - return true; -} - -std::string MidiEvent::toString() -{ - return std::to_string(mTick) + " (" + std::to_string(mDelta.getValue()) + - "): " + typeid(this).name(); -} diff --git a/src/main/file/mid/event/MidiEvent.hpp b/src/main/file/mid/event/MidiEvent.hpp deleted file mode 100644 index 4847b5741..000000000 --- a/src/main/file/mid/event/MidiEvent.hpp +++ /dev/null @@ -1,44 +0,0 @@ -#pragma once - -#include "file/mid/util/VariableLengthInt.hpp" - -#include -#include -#include - -namespace mpc::file::mid::event -{ - class MidiEvent - { - public: - int mTick = 0; - util::VariableLengthInt mDelta; - - virtual int getTick(); - virtual int getDelta(); - virtual void setDelta(int d); - - virtual int getEventSize() = 0; - - virtual int getSize(); - virtual bool requiresStatusByte(MidiEvent *prevEvent); - virtual void writeToOutputStream(std::ostream &out, bool writeType); - - private: - static int sId; - static int sType; - static int sChannel; - - public: - static std::shared_ptr parseEvent(int tick, int delta, - std::stringstream &in); - - private: - static bool verifyIdentifier(int id); - - public: - virtual std::string toString(); - - MidiEvent(int tick, int delta); - }; -} // namespace mpc::file::mid::event diff --git a/src/main/file/mid/event/NoteAftertouch.cpp b/src/main/file/mid/event/NoteAftertouch.cpp deleted file mode 100644 index 7ec169cf8..000000000 --- a/src/main/file/mid/event/NoteAftertouch.cpp +++ /dev/null @@ -1,36 +0,0 @@ -#include "file/mid/event/NoteAftertouch.hpp" - -#include "file/mid/event/ChannelEvent.hpp" - -using namespace mpc::file::mid::event; - -NoteAftertouch::NoteAftertouch(int tick, int channel, int note, int amount) - : ChannelEvent(tick, NOTE_AFTERTOUCH, channel, note, amount) -{ -} - -NoteAftertouch::NoteAftertouch(int tick, int delta, int channel, int note, - int amount) - : ChannelEvent(tick, delta, NOTE_AFTERTOUCH, channel, note, amount) -{ -} - -int NoteAftertouch::getNoteValue() const -{ - return mValue1; -} - -int NoteAftertouch::getAmount() const -{ - return mValue2; -} - -void NoteAftertouch::setNoteValue(int p) -{ - mValue1 = p; -} - -void NoteAftertouch::setAmount(int a) -{ - mValue2 = a; -} diff --git a/src/main/file/mid/event/NoteAftertouch.hpp b/src/main/file/mid/event/NoteAftertouch.hpp deleted file mode 100644 index c98e7f8e8..000000000 --- a/src/main/file/mid/event/NoteAftertouch.hpp +++ /dev/null @@ -1,19 +0,0 @@ -#pragma once - -#include "file/mid/event/ChannelEvent.hpp" - -namespace mpc::file::mid::event -{ - class NoteAftertouch : public ChannelEvent - { - - public: - int getNoteValue() const; - int getAmount() const; - void setNoteValue(int p); - void setAmount(int a); - - NoteAftertouch(int tick, int channel, int note, int amount); - NoteAftertouch(int tick, int delta, int channel, int note, int amount); - }; -} // namespace mpc::file::mid::event diff --git a/src/main/file/mid/event/NoteOff.cpp b/src/main/file/mid/event/NoteOff.cpp deleted file mode 100644 index 13e410201..000000000 --- a/src/main/file/mid/event/NoteOff.cpp +++ /dev/null @@ -1,35 +0,0 @@ -#include "file/mid/event/NoteOff.hpp" - -#include "file/mid/event/ChannelEvent.hpp" - -using namespace mpc::file::mid::event; - -NoteOff::NoteOff(int tick, int channel, int note, int velocity) - : ChannelEvent(tick, NOTE_OFF, channel, note, velocity) -{ -} - -NoteOff::NoteOff(int tick, int delta, int channel, int note, int velocity) - : ChannelEvent(tick, delta, NOTE_OFF, channel, note, velocity) -{ -} - -int NoteOff::getNoteValue() const -{ - return mValue1; -} - -int NoteOff::getVelocity() const -{ - return mValue2; -} - -void NoteOff::setNoteValue(int p) -{ - mValue1 = p; -} - -void NoteOff::setVelocity(int v) -{ - mValue2 = v; -} diff --git a/src/main/file/mid/event/NoteOff.hpp b/src/main/file/mid/event/NoteOff.hpp deleted file mode 100644 index 725ef3357..000000000 --- a/src/main/file/mid/event/NoteOff.hpp +++ /dev/null @@ -1,17 +0,0 @@ -#pragma once -#include "file/mid/event/ChannelEvent.hpp" - -namespace mpc::file::mid::event -{ - class NoteOff : public ChannelEvent - { - public: - int getNoteValue() const; - int getVelocity() const; - void setNoteValue(int p); - void setVelocity(int v); - - NoteOff(int tick, int channel, int note, int velocity); - NoteOff(int tick, int delta, int channel, int note, int velocity); - }; -} // namespace mpc::file::mid::event diff --git a/src/main/file/mid/event/NoteOn.cpp b/src/main/file/mid/event/NoteOn.cpp deleted file mode 100644 index 1a0baa08b..000000000 --- a/src/main/file/mid/event/NoteOn.cpp +++ /dev/null @@ -1,35 +0,0 @@ -#include "file/mid/event/NoteOn.hpp" - -#include "file/mid/event/ChannelEvent.hpp" - -using namespace mpc::file::mid::event; - -NoteOn::NoteOn(int tick, int channel, int note, int velocity) - : ChannelEvent(tick, NOTE_ON, channel, note, velocity) -{ -} - -NoteOn::NoteOn(int tick, int delta, int channel, int note, int velocity) - : ChannelEvent(tick, delta, NOTE_ON, channel, note, velocity) -{ -} - -int NoteOn::getNoteValue() const -{ - return mValue1; -} - -int NoteOn::getVelocity() const -{ - return mValue2; -} - -void NoteOn::setNoteValue(int p) -{ - mValue1 = p; -} - -void NoteOn::setVelocity(int v) -{ - mValue2 = v; -} diff --git a/src/main/file/mid/event/NoteOn.hpp b/src/main/file/mid/event/NoteOn.hpp deleted file mode 100644 index f3eed6fc8..000000000 --- a/src/main/file/mid/event/NoteOn.hpp +++ /dev/null @@ -1,20 +0,0 @@ -#pragma once -#include "file/mid/event/ChannelEvent.hpp" - -namespace mpc::file::mid::event -{ - class NoteOn : public ChannelEvent - { - - public: - typedef ChannelEvent super; - - int getNoteValue() const; - int getVelocity() const; - void setNoteValue(int p); - void setVelocity(int v); - - NoteOn(int tick, int channel, int note, int velocity); - NoteOn(int tick, int delta, int channel, int note, int velocity); - }; -} // namespace mpc::file::mid::event diff --git a/src/main/file/mid/event/PitchBend.cpp b/src/main/file/mid/event/PitchBend.cpp deleted file mode 100644 index fdacf0a47..000000000 --- a/src/main/file/mid/event/PitchBend.cpp +++ /dev/null @@ -1,49 +0,0 @@ -#include "file/mid/event/PitchBend.hpp" - -#include "file/mid/event/ChannelEvent.hpp" - -using namespace mpc::file::mid::event; - -PitchBend::PitchBend(int tick, int channel, int lsb, int msb) - : ChannelEvent(tick, PITCH_BEND, channel, lsb, msb) -{ -} - -PitchBend::PitchBend(int tick, int delta, int channel, int lsb, int msb) - : ChannelEvent(tick, delta, PITCH_BEND, channel, lsb, msb) -{ -} - -int PitchBend::getLeastSignificantBits() const -{ - return mValue1; -} - -int PitchBend::getMostSignificantBits() const -{ - return mValue2; -} - -int PitchBend::getBendAmount() const -{ - auto y = (mValue2 & 127) << 7; - auto x = mValue1; - return y + x; -} - -void PitchBend::setLeastSignificantBits(int p) -{ - mValue1 = p & 127; -} - -void PitchBend::setMostSignificantBits(int p) -{ - mValue2 = p & 127; -} - -void PitchBend::setBendAmount(int amount) -{ - amount = amount & 16383; - mValue1 = amount & 127; - mValue2 = amount >> 7; -} diff --git a/src/main/file/mid/event/PitchBend.hpp b/src/main/file/mid/event/PitchBend.hpp deleted file mode 100644 index aedcad8a9..000000000 --- a/src/main/file/mid/event/PitchBend.hpp +++ /dev/null @@ -1,20 +0,0 @@ -#pragma once -#include "file/mid/event/ChannelEvent.hpp" - -namespace mpc::file::mid::event -{ - class PitchBend : public ChannelEvent - { - - public: - int getLeastSignificantBits() const; - int getMostSignificantBits() const; - int getBendAmount() const; - void setLeastSignificantBits(int p); - void setMostSignificantBits(int p); - void setBendAmount(int amount); - - PitchBend(int tick, int channel, int lsb, int msb); - PitchBend(int tick, int delta, int channel, int lsb, int msb); - }; -} // namespace mpc::file::mid::event diff --git a/src/main/file/mid/event/ProgramChange.cpp b/src/main/file/mid/event/ProgramChange.cpp deleted file mode 100644 index b0e50e071..000000000 --- a/src/main/file/mid/event/ProgramChange.cpp +++ /dev/null @@ -1,25 +0,0 @@ -#include "file/mid/event/ProgramChange.hpp" - -#include "file/mid/event/ChannelEvent.hpp" - -using namespace mpc::file::mid::event; - -ProgramChange::ProgramChange(int tick, int channel, int program) - : ChannelEvent(tick, PROGRAM_CHANGE, channel, program, 0) -{ -} - -ProgramChange::ProgramChange(int tick, int delta, int channel, int program) - : ChannelEvent(tick, delta, PROGRAM_CHANGE, channel, program, 0) -{ -} - -int ProgramChange::getProgramNumber() const -{ - return mValue1; -} - -void ProgramChange::setProgramNumber(int p) -{ - mValue1 = p; -} diff --git a/src/main/file/mid/event/ProgramChange.hpp b/src/main/file/mid/event/ProgramChange.hpp deleted file mode 100644 index 50bf68693..000000000 --- a/src/main/file/mid/event/ProgramChange.hpp +++ /dev/null @@ -1,15 +0,0 @@ -#pragma once -#include "file/mid/event/ChannelEvent.hpp" - -namespace mpc::file::mid::event -{ - class ProgramChange : public ChannelEvent - { - public: - int getProgramNumber() const; - void setProgramNumber(int p); - - ProgramChange(int tick, int channel, int program); - ProgramChange(int tick, int delta, int channel, int program); - }; -} // namespace mpc::file::mid::event diff --git a/src/main/file/mid/event/SystemExclusive.cpp b/src/main/file/mid/event/SystemExclusive.cpp deleted file mode 100644 index 9cdf89e05..000000000 --- a/src/main/file/mid/event/SystemExclusive.cpp +++ /dev/null @@ -1,100 +0,0 @@ -#include "file/mid/event/SystemExclusive.hpp" - -#include "file/mid/event/MidiEvent.hpp" -#include "file/mid/util/VariableLengthInt.hpp" - -using namespace mpc::file::mid::event; - -SystemExclusiveEvent::SystemExclusiveEvent(int type, int tick, - const std::vector &data) - : SystemExclusiveEvent(type, tick, 0, data) -{ -} - -SystemExclusiveEvent::SystemExclusiveEvent(int type, int tick, int delta, - const std::vector &data) - : MidiEvent(tick, delta) -{ - mType = type & 255; - if (mType != 240 && mType != 247) - { - mType = 240; - } - mLength = new util::VariableLengthInt(data.size()); - mData = data; -} - -std::vector SystemExclusiveEvent::getData() -{ - return mData; -} - -void SystemExclusiveEvent::setData(std::vector data) -{ - mLength->setValue(data.size()); - mData = data; -} - -bool SystemExclusiveEvent::requiresStatusByte(MidiEvent *prevEvent) -{ - return true; -} - -void SystemExclusiveEvent::writeToOutputStream(std::ostream &out, - bool writeType) -{ - MidiEvent::writeToOutputStream(out, writeType); - out << (char)mType; - out.write(&mLength->getBytes()[0], mLength->getBytes().size()); - out.write(&mData[0], mData.size()); -} - -int SystemExclusiveEvent::compareTo(MidiEvent *other) -{ - if (this->mTick < other->mTick) - { - return -1; - } - - if (this->mTick > other->mTick) - { - return 1; - } - - if (mDelta.getValue() > other->mDelta.getValue()) - { - return -1; - } - - if (mDelta.getValue() < other->mDelta.getValue()) - { - return 1; - } - - if (dynamic_cast(other) != nullptr) - { - std::string curr; - - for (char c : mData) - { - curr.push_back(c); - } - - auto tmp = dynamic_cast(other)->mData; - - std::string comp; - - for (char c : tmp) - { - comp.push_back(c); - } - - return curr.compare(comp); - } - return 1; -} - -int SystemExclusiveEvent::getEventSize() -{ - return 1 + mLength->getByteCount() + mData.size(); -} diff --git a/src/main/file/mid/event/SystemExclusive.hpp b/src/main/file/mid/event/SystemExclusive.hpp deleted file mode 100644 index 12e182f71..000000000 --- a/src/main/file/mid/event/SystemExclusive.hpp +++ /dev/null @@ -1,29 +0,0 @@ -#pragma once -#include "file/mid/event/MidiEvent.hpp" - -#include "file/mid/util/VariableLengthInt.hpp" - -namespace mpc::file::mid::event -{ - - class SystemExclusiveEvent : public MidiEvent - { - - int mType; - util::VariableLengthInt *mLength; - std::vector mData; - - public: - virtual std::vector getData(); - virtual void setData(std::vector data); - bool requiresStatusByte(MidiEvent *prevEvent) override; - void writeToOutputStream(std::ostream &out, bool writeType) override; - virtual int compareTo(MidiEvent *other); - - int getEventSize() override; - - SystemExclusiveEvent(int type, int tick, const std::vector &data); - SystemExclusiveEvent(int type, int tick, int delta, - const std::vector &data); - }; -} // namespace mpc::file::mid::event diff --git a/src/main/file/mid/event/meta/CopyrightNotice.cpp b/src/main/file/mid/event/meta/CopyrightNotice.cpp deleted file mode 100644 index 234dd45ef..000000000 --- a/src/main/file/mid/event/meta/CopyrightNotice.cpp +++ /dev/null @@ -1,20 +0,0 @@ -#include "file/mid/event/meta/CopyrightNotice.hpp" - -#include "file/mid/event/meta/MetaEvent.hpp" - -using namespace mpc::file::mid::event::meta; - -CopyrightNotice::CopyrightNotice(int tick, int delta, const std::string &text) - : TextualMetaEvent(tick, delta, COPYRIGHT_NOTICE, text) -{ -} - -void CopyrightNotice::setNotice(const std::string &t) -{ - setText(t); -} - -std::string CopyrightNotice::getNotice() -{ - return getText(); -} diff --git a/src/main/file/mid/event/meta/CopyrightNotice.hpp b/src/main/file/mid/event/meta/CopyrightNotice.hpp deleted file mode 100644 index 4912b91db..000000000 --- a/src/main/file/mid/event/meta/CopyrightNotice.hpp +++ /dev/null @@ -1,17 +0,0 @@ -#pragma once -#include "file/mid/event/meta/TextualMetaEvent.hpp" - -#include - -namespace mpc::file::mid::event::meta -{ - class CopyrightNotice : public TextualMetaEvent - { - - public: - void setNotice(const std::string &t); - std::string getNotice(); - - CopyrightNotice(int tick, int delta, const std::string &text); - }; -} // namespace mpc::file::mid::event::meta diff --git a/src/main/file/mid/event/meta/CuePoint.cpp b/src/main/file/mid/event/meta/CuePoint.cpp deleted file mode 100644 index 40832a020..000000000 --- a/src/main/file/mid/event/meta/CuePoint.cpp +++ /dev/null @@ -1,20 +0,0 @@ -#include "file/mid/event/meta/CuePoint.hpp" - -#include "file/mid/event/meta/MetaEvent.hpp" - -using namespace mpc::file::mid::event::meta; - -CuePoint::CuePoint(int tick, int delta, const std::string &marker) - : TextualMetaEvent(tick, delta, CUE_POINT, marker) -{ -} - -void CuePoint::setCue(const std::string &name) -{ - setText(name); -} - -std::string CuePoint::getCue() -{ - return getText(); -} diff --git a/src/main/file/mid/event/meta/CuePoint.hpp b/src/main/file/mid/event/meta/CuePoint.hpp deleted file mode 100644 index 285be89dd..000000000 --- a/src/main/file/mid/event/meta/CuePoint.hpp +++ /dev/null @@ -1,15 +0,0 @@ -#pragma once -#include "file/mid/event/meta/TextualMetaEvent.hpp" - -namespace mpc::file::mid::event::meta -{ - class CuePoint : public TextualMetaEvent - { - - public: - void setCue(const std::string &name); - std::string getCue(); - - CuePoint(int tick, int delta, const std::string &marker); - }; -} // namespace mpc::file::mid::event::meta diff --git a/src/main/file/mid/event/meta/EndOfTrack.cpp b/src/main/file/mid/event/meta/EndOfTrack.cpp deleted file mode 100644 index ec5063f72..000000000 --- a/src/main/file/mid/event/meta/EndOfTrack.cpp +++ /dev/null @@ -1,46 +0,0 @@ -#include "file/mid/event/meta/EndOfTrack.hpp" - -#include "file/mid/event/MidiEvent.hpp" -#include "file/mid/event/meta/MetaEvent.hpp" -#include "file/mid/util/VariableLengthInt.hpp" - -using namespace mpc::file::mid::event::meta; - -EndOfTrack::EndOfTrack(int tick, int delta) - : MetaEvent(tick, delta, END_OF_TRACK) -{ - mLength = util::VariableLengthInt(0); -} - -int EndOfTrack::getEventSize() -{ - return 3; -} - -void EndOfTrack::writeToOutputStream(std::ostream &out) -{ - MetaEvent::writeToOutputStream(out); - out << (char)0x00; -} - -void EndOfTrack::writeToOutputStream(std::ostream &out, bool writeType) -{ - MetaEvent::writeToOutputStream(out, writeType); -} - -int EndOfTrack::compareTo(MidiEvent *other) const -{ - if (mTick != other->getTick()) - { - return mTick < other->getTick() ? -1 : 1; - } - if (mDelta.getValue() != other->getDelta()) - { - return mDelta.getValue() < other->getDelta() ? 1 : -1; - } - if (dynamic_cast(other) == nullptr) - { - return 1; - } - return 0; -} diff --git a/src/main/file/mid/event/meta/EndOfTrack.hpp b/src/main/file/mid/event/meta/EndOfTrack.hpp deleted file mode 100644 index e2524c191..000000000 --- a/src/main/file/mid/event/meta/EndOfTrack.hpp +++ /dev/null @@ -1,20 +0,0 @@ -#pragma once -#include "file/mid/event/meta/MetaEvent.hpp" - -#include - -namespace mpc::file::mid::event::meta -{ - class EndOfTrack : public MetaEvent - { - - public: - int getEventSize() override; - void writeToOutputStream(std::ostream &out) override; - void writeToOutputStream(std::ostream &out, bool writeType) override; - - int compareTo(MidiEvent *other) const; - - EndOfTrack(int tick, int delta); - }; -} // namespace mpc::file::mid::event::meta diff --git a/src/main/file/mid/event/meta/FrameRate.cpp b/src/main/file/mid/event/meta/FrameRate.cpp deleted file mode 100644 index c2db42daa..000000000 --- a/src/main/file/mid/event/meta/FrameRate.cpp +++ /dev/null @@ -1,69 +0,0 @@ -#include "file/mid/event/meta/FrameRate.hpp" - -#include - -#ifdef __linux__ -#include -#endif // __linux__ - -using namespace mpc::file::mid::event::meta; - -FrameRate::FrameRate(const std::string &name, int ordinal, int v) -{ - this->name = name; - this->ordinal = ordinal; - value = v; -} - -FrameRate *FrameRate::FRAME_RATE_24 = new FrameRate("FRAME_RATE_24", 0, 0); -FrameRate *FrameRate::FRAME_RATE_25 = new FrameRate("FRAME_RATE_25", 1, 1); -FrameRate *FrameRate::FRAME_RATE_30_DROP = - new FrameRate("FRAME_RATE_30_DROP", 2, 2); -FrameRate *FrameRate::FRAME_RATE_30 = new FrameRate("FRAME_RATE_30", 3, 3); - -FrameRate *FrameRate::fromInt(int val) -{ - switch (val) - { - case 0: - return FRAME_RATE_24; - case 1: - return FRAME_RATE_25; - case 2: - return FRAME_RATE_30_DROP; - case 3: - return FRAME_RATE_30; - } - return nullptr; -} - -FrameRate *FrameRate::valueOf(const std::string &a0) -{ - if (FRAME_RATE_24->name == a0) - { - return FRAME_RATE_24; - } - if (FRAME_RATE_25->name == a0) - { - return FRAME_RATE_25; - } - if (FRAME_RATE_30->name == a0) - { - return FRAME_RATE_30; - } - if (FRAME_RATE_30_DROP->name == a0) - { - return FRAME_RATE_30_DROP; - } - throw std::invalid_argument("Illegal argument"); -} - -std::vector FrameRate::values() -{ - return std::vector{ - FRAME_RATE_24, - FRAME_RATE_25, - FRAME_RATE_30, - FRAME_RATE_30_DROP, - }; -} diff --git a/src/main/file/mid/event/meta/FrameRate.hpp b/src/main/file/mid/event/meta/FrameRate.hpp deleted file mode 100644 index 9ce7a2d35..000000000 --- a/src/main/file/mid/event/meta/FrameRate.hpp +++ /dev/null @@ -1,30 +0,0 @@ -#pragma once - -#include -#include - -namespace mpc::file::mid::event::meta -{ - class FrameRate - { - std::string name; - int ordinal{-1}; - - public: - static FrameRate *FRAME_RATE_24; - static FrameRate *FRAME_RATE_25; - static FrameRate *FRAME_RATE_30_DROP; - static FrameRate *FRAME_RATE_30; - - int value{}; - - static FrameRate *fromInt(int val); - - private: - FrameRate(const std::string &name, int ordinal, int v); - - public: - static FrameRate *valueOf(const std::string &a0); - static std::vector values(); - }; -} // namespace mpc::file::mid::event::meta diff --git a/src/main/file/mid/event/meta/GenericMetaEvent.cpp b/src/main/file/mid/event/meta/GenericMetaEvent.cpp deleted file mode 100644 index 61b21f189..000000000 --- a/src/main/file/mid/event/meta/GenericMetaEvent.cpp +++ /dev/null @@ -1,45 +0,0 @@ -#include "file/mid/event/meta/GenericMetaEvent.hpp" - -#include "file/mid/event/MidiEvent.hpp" -#include "file/mid/event/meta/MetaEventData.hpp" -#include "file/mid/util/VariableLengthInt.hpp" - -using namespace mpc::file::mid::event::meta; - -GenericMetaEvent::GenericMetaEvent(int tick, int delta, MetaEventData *info) - : MetaEvent(tick, delta, info->type) -{ - mData = info->data; - mLength = info->length; -} - -int GenericMetaEvent::getEventSize() -{ - return 1 + 1 + mLength.getByteCount() + mLength.getValue(); -} - -void GenericMetaEvent::writeToOutputStream(std::ostream &out) -{ - MetaEvent::writeToOutputStream(out); - auto length = mLength.getBytes(); - out.write(&length[0], length.size()); - out.write(&mData[0], mData.size()); -} - -void GenericMetaEvent::writeToOutputStream(std::ostream &out, bool writeType) -{ - MetaEvent::writeToOutputStream(out, writeType); -} - -int GenericMetaEvent::compareTo(MidiEvent *other) -{ - if (mTick != other->getTick()) - { - return mTick < other->getTick() ? -1 : 1; - } - if (mDelta.getValue() != other->getDelta()) - { - return mDelta.getValue() < other->getDelta() ? 1 : -1; - } - return 1; -} diff --git a/src/main/file/mid/event/meta/GenericMetaEvent.hpp b/src/main/file/mid/event/meta/GenericMetaEvent.hpp deleted file mode 100644 index b881b0e9e..000000000 --- a/src/main/file/mid/event/meta/GenericMetaEvent.hpp +++ /dev/null @@ -1,23 +0,0 @@ -#pragma once -#include "file/mid/event/meta/MetaEvent.hpp" - -namespace mpc::file::mid::event::meta -{ - - class MetaEventData; - - class GenericMetaEvent : public MetaEvent - { - - std::vector mData; - - public: - int getEventSize() override; - void writeToOutputStream(std::ostream &out) override; - void writeToOutputStream(std::ostream &out, bool writeType) override; - - virtual int compareTo(MidiEvent *other); - - GenericMetaEvent(int tick, int delta, MetaEventData *info); - }; -} // namespace mpc::file::mid::event::meta diff --git a/src/main/file/mid/event/meta/InstrumentName.cpp b/src/main/file/mid/event/meta/InstrumentName.cpp deleted file mode 100644 index f62853975..000000000 --- a/src/main/file/mid/event/meta/InstrumentName.cpp +++ /dev/null @@ -1,20 +0,0 @@ -#include "file/mid/event/meta/InstrumentName.hpp" - -#include "file/mid/event/meta/MetaEvent.hpp" - -using namespace mpc::file::mid::event::meta; - -InstrumentName::InstrumentName(int tick, int delta, const std::string &name) - : TextualMetaEvent(tick, delta, INSTRUMENT_NAME, name) -{ -} - -void InstrumentName::setName(const std::string &name) -{ - setText(name); -} - -std::string InstrumentName::getName() -{ - return getText(); -} diff --git a/src/main/file/mid/event/meta/InstrumentName.hpp b/src/main/file/mid/event/meta/InstrumentName.hpp deleted file mode 100644 index 97fb7014c..000000000 --- a/src/main/file/mid/event/meta/InstrumentName.hpp +++ /dev/null @@ -1,15 +0,0 @@ -#pragma once -#include "file/mid/event/meta/TextualMetaEvent.hpp" - -namespace mpc::file::mid::event::meta -{ - class InstrumentName : public TextualMetaEvent - { - - public: - void setName(const std::string &name); - std::string getName(); - - InstrumentName(int tick, int delta, const std::string &name); - }; -} // namespace mpc::file::mid::event::meta diff --git a/src/main/file/mid/event/meta/KeySignature.cpp b/src/main/file/mid/event/meta/KeySignature.cpp deleted file mode 100644 index 5e74766dc..000000000 --- a/src/main/file/mid/event/meta/KeySignature.cpp +++ /dev/null @@ -1,110 +0,0 @@ -#include "file/mid/event/meta/KeySignature.hpp" - -#include "file/mid/event/MidiEvent.hpp" -#include "file/mid/event/meta/GenericMetaEvent.hpp" -#include "file/mid/event/meta/MetaEventData.hpp" -#include "file/mid/event/meta/MetaEvent.hpp" -#include "file/mid/util/VariableLengthInt.hpp" - -using namespace mpc::file::mid::event::meta; - -KeySignature::KeySignature(int tick, int delta, int key, int scale) - : MetaEvent(tick, delta, KEY_SIGNATURE) -{ - setKey(key); - mScale = scale; - mLength = util::VariableLengthInt(2); -} - -const int KeySignature::SCALE_MAJOR; -const int KeySignature::SCALE_MINOR; - -void KeySignature::setKey(int key) -{ - mKey = static_cast(key); - if (mKey < -7) - { - mKey = -7; - } - else if (mKey > 7) - { - mKey = 7; - } -} - -int KeySignature::getKey() const -{ - return mKey; -} - -void KeySignature::setScale(int scale) -{ - mScale = scale; -} - -int KeySignature::getScale() const -{ - return mScale; -} - -int KeySignature::getEventSize() -{ - return 5; -} - -void KeySignature::writeToOutputStream(std::ostream &out) -{ - MetaEvent::writeToOutputStream(out); - out << (char)0x02; - out << (char)mKey; - out << (char)mScale; -} - -void KeySignature::writeToOutputStream(std::ostream &out, bool writeType) -{ - MetaEvent::writeToOutputStream(out, writeType); -} - -std::shared_ptr KeySignature::parseKeySignature(int tick, int delta, - MetaEventData *info) -{ - if (info->length.getValue() != 2) - { - return std::make_shared(tick, delta, info); - } - int key = info->data[0]; - int scale = info->data[1]; - return std::make_shared(tick, delta, key, scale); -} - -int KeySignature::compareTo(MidiEvent *other) const -{ - if (mTick != other->getTick()) - { - return mTick < other->getTick() ? -1 : 1; - } - - if (mDelta.getValue() != other->getDelta()) - { - return mDelta.getValue() < other->getDelta() ? 1 : -1; - } - - if (dynamic_cast(other) == nullptr) - { - return 1; - } - - auto o = dynamic_cast(other); - - if (mKey != o->mKey) - { - return mKey < o->mKey ? -1 : 1; - } - - if (mScale != o->mScale) - { - return mKey < o->mScale ? -1 : 1; - } - - return 0; -} diff --git a/src/main/file/mid/event/meta/KeySignature.hpp b/src/main/file/mid/event/meta/KeySignature.hpp deleted file mode 100644 index 4dda01756..000000000 --- a/src/main/file/mid/event/meta/KeySignature.hpp +++ /dev/null @@ -1,35 +0,0 @@ -#pragma once -#include "file/mid/event/meta/MetaEvent.hpp" - -namespace mpc::file::mid::event::meta -{ - class MetaEventData; - - class KeySignature : public MetaEvent - { - - public: - static const int SCALE_MAJOR{0}; - static const int SCALE_MINOR{1}; - - private: - int mKey; - int mScale; - - public: - void setKey(int key); - int getKey() const; - void setScale(int scale); - int getScale() const; - - int getEventSize() override; - void writeToOutputStream(std::ostream &out) override; - void writeToOutputStream(std::ostream &out, bool writeType) override; - - static std::shared_ptr - parseKeySignature(int tick, int delta, MetaEventData *info); - int compareTo(MidiEvent *other) const; - - KeySignature(int tick, int delta, int key, int scale); - }; -} // namespace mpc::file::mid::event::meta diff --git a/src/main/file/mid/event/meta/Marker.cpp b/src/main/file/mid/event/meta/Marker.cpp deleted file mode 100644 index 5522d4cdb..000000000 --- a/src/main/file/mid/event/meta/Marker.cpp +++ /dev/null @@ -1,20 +0,0 @@ -#include "file/mid/event/meta/Marker.hpp" - -#include "file/mid/event/meta/MetaEvent.hpp" - -using namespace mpc::file::mid::event::meta; - -Marker::Marker(int tick, int delta, const std::string &marker) - : TextualMetaEvent(tick, delta, MARKER, marker) -{ -} - -void Marker::setMarkerName(const std::string &name) -{ - setText(name); -} - -std::string Marker::getMarkerName() -{ - return getText(); -} diff --git a/src/main/file/mid/event/meta/Marker.hpp b/src/main/file/mid/event/meta/Marker.hpp deleted file mode 100644 index 763ccf89a..000000000 --- a/src/main/file/mid/event/meta/Marker.hpp +++ /dev/null @@ -1,17 +0,0 @@ -#pragma once -#include "file/mid/event/meta/TextualMetaEvent.hpp" - -#include - -namespace mpc::file::mid::event::meta -{ - class Marker : public TextualMetaEvent - { - - public: - void setMarkerName(const std::string &name); - std::string getMarkerName(); - - Marker(int tick, int delta, const std::string &marker); - }; -} // namespace mpc::file::mid::event::meta diff --git a/src/main/file/mid/event/meta/MetaEvent.cpp b/src/main/file/mid/event/meta/MetaEvent.cpp deleted file mode 100644 index 62be86d37..000000000 --- a/src/main/file/mid/event/meta/MetaEvent.cpp +++ /dev/null @@ -1,137 +0,0 @@ -#include "file/mid/event/meta/MetaEvent.hpp" - -#include "file/mid/event/meta/CopyrightNotice.hpp" -#include "file/mid/event/meta/CuePoint.hpp" -#include "file/mid/event/meta/EndOfTrack.hpp" -#include "file/mid/event/meta/GenericMetaEvent.hpp" -#include "file/mid/event/meta/InstrumentName.hpp" -#include "file/mid/event/meta/KeySignature.hpp" -#include "file/mid/event/meta/Marker.hpp" -#include "file/mid/event/meta/MetaEventData.hpp" -#include "file/mid/event/meta/MidiChannelPrefix.hpp" -#include "file/mid/event/meta/SequenceNumber.hpp" -#include "file/mid/event/meta/SequencerSpecificEvent.hpp" -#include "file/mid/event/meta/SmpteOffset.hpp" -#include "file/mid/event/meta/Tempo.hpp" -#include "file/mid/event/meta/Text.hpp" -#include "file/mid/event/meta/TimeSignatureEvent.hpp" -#include "file/mid/event/meta/TrackName.hpp" - -using namespace mpc::file::mid::event::meta; - -MetaEvent::MetaEvent(int tick, int delta, int type) : MidiEvent(tick, delta) -{ - mType = type & 255; -} - -void MetaEvent::writeToOutputStream(std::ostream &out, bool writeType) -{ - writeToOutputStream(out); -} - -void MetaEvent::writeToOutputStream(std::ostream &out) -{ - MidiEvent::writeToOutputStream(out, true); - out << (char)0xFF; - out << (char)mType; -} - -std::shared_ptr MetaEvent::parseMetaEvent(int tick, int delta, - std::stringstream &in) -{ - auto eventData = MetaEventData(in); - auto isText = false; - switch (eventData.type) - { - case SEQUENCE_NUMBER: - case MIDI_CHANNEL_PREFIX: - case END_OF_TRACK: - case TEMPO: - case SMPTE_OFFSET: - case TIME_SIGNATURE: - case KEY_SIGNATURE: - break; - case TEXT_EVENT: - case COPYRIGHT_NOTICE: - case TRACK_NAME: - case INSTRUMENT_NAME: - case LYRICS: - case MARKER: - case CUE_POINT: - case SEQUENCER_SPECIFIC: - default: - isText = true; - break; - } - - if (isText) - { - std::string text; - for (char c : eventData.data) - { - text.push_back(c); - } - switch (eventData.type) - { - case TEXT_EVENT: - return std::make_shared(tick, delta, text); - case COPYRIGHT_NOTICE: - return std::make_shared(tick, delta, text); - case TRACK_NAME: - return std::make_shared(tick, delta, text); - case INSTRUMENT_NAME: - return std::make_shared(tick, delta, text); - case LYRICS: - // return new Lyrics(tick, delta, text); - return nullptr; - case MARKER: - return std::make_shared(tick, delta, text); - case CUE_POINT: - return std::make_shared(tick, delta, text); - case SEQUENCER_SPECIFIC: - return std::make_shared(tick, delta, - eventData.data); - default: - return std::make_shared(tick, delta, - &eventData); - } - } - - switch (eventData.type) - { - case SEQUENCE_NUMBER: - return SequenceNumber::parseSequenceNumber(tick, delta, &eventData); - case MIDI_CHANNEL_PREFIX: - return MidiChannelPrefix::parseMidiChannelPrefix(tick, delta, - &eventData); - case END_OF_TRACK: - return std::make_shared(tick, delta); - case TEMPO: - return Tempo::parseTempo(tick, delta, &eventData); - case SMPTE_OFFSET: - return SmpteOffset::parseSmpteOffset(tick, delta, &eventData); - case TIME_SIGNATURE: - return TimeSignature::parseTimeSignature(tick, delta, &eventData); - case KEY_SIGNATURE: - return KeySignature::parseKeySignature(tick, delta, &eventData); - } - - std::string error = "Completely broken in MetaEvent.parseMetaEvent()"; - return nullptr; -} - -const int MetaEvent::SEQUENCE_NUMBER; -const int MetaEvent::TEXT_EVENT; -const int MetaEvent::COPYRIGHT_NOTICE; -const int MetaEvent::TRACK_NAME; -const int MetaEvent::INSTRUMENT_NAME; -const int MetaEvent::LYRICS; -const int MetaEvent::MARKER; -const int MetaEvent::CUE_POINT; -const int MetaEvent::MIDI_CHANNEL_PREFIX; -const int MetaEvent::END_OF_TRACK; -const int MetaEvent::TEMPO; -const int MetaEvent::SMPTE_OFFSET; -const int MetaEvent::TIME_SIGNATURE; -const int MetaEvent::KEY_SIGNATURE; -const int MetaEvent::SEQUENCER_SPECIFIC; diff --git a/src/main/file/mid/event/meta/MetaEvent.hpp b/src/main/file/mid/event/meta/MetaEvent.hpp deleted file mode 100644 index 875a57dcf..000000000 --- a/src/main/file/mid/event/meta/MetaEvent.hpp +++ /dev/null @@ -1,43 +0,0 @@ -#pragma once -#include "file/mid/event/MidiEvent.hpp" - -#include "file/mid/util/VariableLengthInt.hpp" - -#include -#include - -namespace mpc::file::mid::event::meta -{ - - class MetaEvent : public MidiEvent - { - - protected: - int mType = 0; - util::VariableLengthInt mLength; - - public: - virtual void writeToOutputStream(std::ostream &output); - void writeToOutputStream(std::ostream &out, bool writeType) override; - - static std::shared_ptr parseMetaEvent(int tick, int delta, - std::stringstream &in); - static const int SEQUENCE_NUMBER{0}; - static const int TEXT_EVENT{1}; - static const int COPYRIGHT_NOTICE{2}; - static const int TRACK_NAME{3}; - static const int INSTRUMENT_NAME{4}; - static const int LYRICS{5}; - static const int MARKER{6}; - static const int CUE_POINT{7}; - static const int MIDI_CHANNEL_PREFIX{32}; - static const int END_OF_TRACK{47}; - static const int TEMPO{81}; - static const int SMPTE_OFFSET{84}; - static const int TIME_SIGNATURE{88}; - static const int KEY_SIGNATURE{89}; - static const int SEQUENCER_SPECIFIC{127}; - - MetaEvent(int tick, int delta, int type); - }; -} // namespace mpc::file::mid::event::meta diff --git a/src/main/file/mid/event/meta/MetaEventData.cpp b/src/main/file/mid/event/meta/MetaEventData.cpp deleted file mode 100644 index 53f24de09..000000000 --- a/src/main/file/mid/event/meta/MetaEventData.cpp +++ /dev/null @@ -1,17 +0,0 @@ -#include "file/mid/event/meta/MetaEventData.hpp" - -using namespace mpc::file::mid::event::meta; - -MetaEventData::MetaEventData(std::stringstream &in) -{ - type = in.get(); - - length = util::VariableLengthInt(in); - - data = std::vector(length.getValue()); - - if (length.getValue() > 0) - { - in.read(&data[0], data.size()); - } -} diff --git a/src/main/file/mid/event/meta/MetaEventData.hpp b/src/main/file/mid/event/meta/MetaEventData.hpp deleted file mode 100644 index 02cba3752..000000000 --- a/src/main/file/mid/event/meta/MetaEventData.hpp +++ /dev/null @@ -1,20 +0,0 @@ -#pragma once - -#include "file/mid/util/VariableLengthInt.hpp" - -#include - -namespace mpc::file::mid::event::meta -{ - - class MetaEventData - { - - public: - int type = 0; - util::VariableLengthInt length; - std::vector data; - - MetaEventData(std::stringstream &in); - }; -} // namespace mpc::file::mid::event::meta diff --git a/src/main/file/mid/event/meta/MidiChannelPrefix.cpp b/src/main/file/mid/event/meta/MidiChannelPrefix.cpp deleted file mode 100644 index 36cae0b60..000000000 --- a/src/main/file/mid/event/meta/MidiChannelPrefix.cpp +++ /dev/null @@ -1,55 +0,0 @@ -#include "file/mid/event/meta/MidiChannelPrefix.hpp" - -#include "file/mid/event/MidiEvent.hpp" -#include "file/mid/event/meta/GenericMetaEvent.hpp" -#include "file/mid/event/meta/MetaEventData.hpp" -#include "file/mid/event/meta/MetaEvent.hpp" -#include "file/mid/util/VariableLengthInt.hpp" - -using namespace mpc::file::mid::event::meta; - -MidiChannelPrefix::MidiChannelPrefix(int tick, int delta, int channel) - : MetaEvent(tick, delta, MIDI_CHANNEL_PREFIX) -{ - mChannel = channel; - mLength = util::VariableLengthInt(4); -} - -void MidiChannelPrefix::setChannel(int c) -{ - mChannel = c; -} - -int MidiChannelPrefix::getChannel() -{ - return mChannel; -} - -int MidiChannelPrefix::getEventSize() -{ - return 4; -} - -void MidiChannelPrefix::writeToOutputStream(std::ostream &out) -{ - MetaEvent::writeToOutputStream(out); - out << (char)1; - out << (char)mChannel; -} - -std::shared_ptr -MidiChannelPrefix::parseMidiChannelPrefix(int tick, int delta, - MetaEventData *info) -{ - if (info->length.getValue() != 1) - { - return std::make_shared(tick, delta, info); - } - int channel = info->data[0]; - return std::make_shared(tick, delta, channel); -} - -int MidiChannelPrefix::compareTo(MidiEvent *other) -{ - return 0; -} diff --git a/src/main/file/mid/event/meta/MidiChannelPrefix.hpp b/src/main/file/mid/event/meta/MidiChannelPrefix.hpp deleted file mode 100644 index 42b062a8d..000000000 --- a/src/main/file/mid/event/meta/MidiChannelPrefix.hpp +++ /dev/null @@ -1,29 +0,0 @@ -#pragma once -#include "file/mid/event/meta/MetaEvent.hpp" - -namespace mpc::file::mid::event::meta -{ - class MetaEventData; -} - -namespace mpc::file::mid::event::meta -{ - class MidiChannelPrefix : public MetaEvent - { - - int mChannel; - - public: - virtual void setChannel(int c); - virtual int getChannel(); - - int getEventSize() override; - - void writeToOutputStream(std::ostream &out) override; - static std::shared_ptr - parseMidiChannelPrefix(int tick, int delta, MetaEventData *info); - virtual int compareTo(MidiEvent *other); - - MidiChannelPrefix(int tick, int delta, int channel); - }; -} // namespace mpc::file::mid::event::meta diff --git a/src/main/file/mid/event/meta/SequenceNumber.cpp b/src/main/file/mid/event/meta/SequenceNumber.cpp deleted file mode 100644 index 3703b40ee..000000000 --- a/src/main/file/mid/event/meta/SequenceNumber.cpp +++ /dev/null @@ -1,84 +0,0 @@ -#include "file/mid/event/meta/SequenceNumber.hpp" - -#include "file/mid/event/MidiEvent.hpp" -#include "file/mid/event/meta/GenericMetaEvent.hpp" -#include "file/mid/event/meta/MetaEventData.hpp" -#include "file/mid/event/meta/MetaEvent.hpp" -#include "file/mid/util/VariableLengthInt.hpp" - -using namespace mpc::file::mid::event::meta; - -SequenceNumber::SequenceNumber(int tick, int delta, int number) - : MetaEvent(tick, delta, SEQUENCE_NUMBER) -{ - mNumber = number; - mLength = util::VariableLengthInt(2); -} - -int SequenceNumber::getMostSignificantBits() const -{ - return mNumber >> 8; -} - -int SequenceNumber::getLeastSignificantBits() const -{ - return mNumber & 255; -} - -int SequenceNumber::getSequenceNumber() const -{ - return mNumber; -} - -void SequenceNumber::writeToOutputStream(std::ostream &out) -{ - MetaEvent::writeToOutputStream(out); - out << (char)2; - out << (char)getMostSignificantBits(); - out << (char)getLeastSignificantBits(); -} - -void SequenceNumber::writeToOutputStream(std::ostream &out, bool writeType) -{ - MetaEvent::writeToOutputStream(out, writeType); -} - -std::shared_ptr -SequenceNumber::parseSequenceNumber(int tick, int delta, MetaEventData *info) -{ - if (info->length.getValue() != 2) - { - return std::make_shared(tick, delta, info); - } - int msb = info->data[0]; - int lsb = info->data[1]; - auto number = (msb << 8) + lsb; - return std::make_shared(tick, delta, number); -} - -int SequenceNumber::getEventSize() -{ - return 5; -} - -int SequenceNumber::compareTo(MidiEvent *other) const -{ - if (mTick != other->getTick()) - { - return mTick < other->getTick() ? -1 : 1; - } - if (mDelta.getValue() != other->getDelta()) - { - return mDelta.getValue() < other->getDelta() ? 1 : -1; - } - if (dynamic_cast(other) == nullptr) - { - return 1; - } - auto o = dynamic_cast(other); - if (mNumber != o->mNumber) - { - return mNumber < o->mNumber ? -1 : 1; - } - return 0; -} diff --git a/src/main/file/mid/event/meta/SequenceNumber.hpp b/src/main/file/mid/event/meta/SequenceNumber.hpp deleted file mode 100644 index 27bdd53ce..000000000 --- a/src/main/file/mid/event/meta/SequenceNumber.hpp +++ /dev/null @@ -1,30 +0,0 @@ -#pragma once -#include "file/mid/event/meta/MetaEvent.hpp" - -namespace mpc::file::mid::event::meta -{ - - class MetaEventData; - - class SequenceNumber : public MetaEvent - { - - int mNumber; - - public: - int getMostSignificantBits() const; - int getLeastSignificantBits() const; - int getSequenceNumber() const; - - static std::shared_ptr - parseSequenceNumber(int tick, int delta, MetaEventData *info); - - void writeToOutputStream(std::ostream &out) override; - void writeToOutputStream(std::ostream &out, bool writeType) override; - int getEventSize() override; - - int compareTo(MidiEvent *other) const; - - SequenceNumber(int tick, int delta, int number); - }; -} // namespace mpc::file::mid::event::meta diff --git a/src/main/file/mid/event/meta/SequencerSpecificEvent.cpp b/src/main/file/mid/event/meta/SequencerSpecificEvent.cpp deleted file mode 100644 index 555c9a314..000000000 --- a/src/main/file/mid/event/meta/SequencerSpecificEvent.cpp +++ /dev/null @@ -1,68 +0,0 @@ -#include "file/mid/event/meta/SequencerSpecificEvent.hpp" - -#include "file/mid/event/MidiEvent.hpp" -#include "file/mid/event/meta/MetaEvent.hpp" -#include "file/mid/util/MidiUtil.hpp" -#include "file/mid/util/VariableLengthInt.hpp" - -using namespace mpc::file::mid::event::meta; - -SequencerSpecificEvent::SequencerSpecificEvent(int tick, int delta, - const std::vector &data) - : MetaEvent(tick, delta, SEQUENCER_SPECIFIC) -{ - mData = data; - util::VariableLengthInt(data.size()); -} - -void SequencerSpecificEvent::setData(std::vector data) -{ - mData = data; - mLength.setValue(mData.size()); -} - -std::vector SequencerSpecificEvent::getData() -{ - return mData; -} - -int SequencerSpecificEvent::getEventSize() -{ - return 1 + 1 + mLength.getByteCount() + mData.size(); -} - -void SequencerSpecificEvent::writeToOutputStream(std::ostream &out) -{ - MetaEvent::writeToOutputStream(out); - auto length = mLength.getBytes(); - out.write(&length[0], length.size()); - out.write(&mData[0], mData.size()); -} - -void SequencerSpecificEvent::writeToOutputStream(std::ostream &out, - bool writeType) -{ - MetaEvent::writeToOutputStream(out, writeType); -} - -int SequencerSpecificEvent::compareTo(MidiEvent *other) -{ - if (mTick != other->getTick()) - { - return mTick < other->getTick() ? -1 : 1; - } - if (mDelta.getValue() != other->getDelta()) - { - return mDelta.getValue() < other->getDelta() ? 1 : -1; - } - if (dynamic_cast(other) == nullptr) - { - return 1; - } - auto o = dynamic_cast(other); - if (util::MidiUtil::bytesEqual(mData, o->mData, 0, mData.size())) - { - return 0; - } - return 1; -} diff --git a/src/main/file/mid/event/meta/SequencerSpecificEvent.hpp b/src/main/file/mid/event/meta/SequencerSpecificEvent.hpp deleted file mode 100644 index 858732ee9..000000000 --- a/src/main/file/mid/event/meta/SequencerSpecificEvent.hpp +++ /dev/null @@ -1,25 +0,0 @@ -#pragma once - -#include "file/mid/event/meta/MetaEvent.hpp" - -namespace mpc::file::mid::event::meta -{ - - class SequencerSpecificEvent : public MetaEvent - { - std::vector mData; - - public: - virtual void setData(std::vector data); - virtual std::vector getData(); - - int getEventSize() override; - void writeToOutputStream(std::ostream &out) override; - void writeToOutputStream(std::ostream &out, bool writeType) override; - - virtual int compareTo(MidiEvent *other); - - SequencerSpecificEvent(int tick, int delta, - const std::vector &data); - }; -} // namespace mpc::file::mid::event::meta diff --git a/src/main/file/mid/event/meta/SmpteOffset.cpp b/src/main/file/mid/event/meta/SmpteOffset.cpp deleted file mode 100644 index eecdb04ec..000000000 --- a/src/main/file/mid/event/meta/SmpteOffset.cpp +++ /dev/null @@ -1,145 +0,0 @@ -#include "file/mid/event/meta/SmpteOffset.hpp" - -#include "file/mid/event/MidiEvent.hpp" -#include "file/mid/event/meta/GenericMetaEvent.hpp" -#include "file/mid/event/meta/MetaEventData.hpp" -#include "file/mid/event/meta/MetaEvent.hpp" -#include "file/mid/event/meta/FrameRate.hpp" -#include "file/mid/util/VariableLengthInt.hpp" - -using namespace mpc::file::mid::event::meta; - -SmpteOffset::SmpteOffset(int tick, int delta, FrameRate *fps, int hour, int min, - int sec, int fr, int subfr) - : MetaEvent(tick, delta, SMPTE_OFFSET) -{ - mFrameRate = fps; - mHours = hour; - mMinutes = min; - mSeconds = sec; - mFrames = fr; - mSubFrames = subfr; - mLength = util::VariableLengthInt(5); -} - -const int SmpteOffset::FRAME_RATE_24; -const int SmpteOffset::FRAME_RATE_25; -const int SmpteOffset::FRAME_RATE_30_DROP; -const int SmpteOffset::FRAME_RATE_30; - -void SmpteOffset::setFrameRate(FrameRate *fps) -{ - mFrameRate = fps; -} - -FrameRate *SmpteOffset::getFrameRate() -{ - return mFrameRate; -} - -void SmpteOffset::setHours(int h) -{ - mHours = h; -} - -int SmpteOffset::getHours() -{ - return mHours; -} - -void SmpteOffset::setMinutes(int m) -{ - mMinutes = m; -} - -int SmpteOffset::getMinutes() -{ - return mMinutes; -} - -void SmpteOffset::setSeconds(int s) -{ - mSeconds = s; -} - -int SmpteOffset::getSeconds() -{ - return mSeconds; -} - -void SmpteOffset::setFrames(int f) -{ - mFrames = f; -} - -int SmpteOffset::getFrames() -{ - return mFrames; -} - -void SmpteOffset::setSubFrames(int s) -{ - mSubFrames = s; -} - -int SmpteOffset::getSubFrames() -{ - return mSubFrames; -} - -int SmpteOffset::getEventSize() -{ - return 8; -} - -void SmpteOffset::writeToOutputStream(std::ostream &out) -{ - MetaEvent::writeToOutputStream(out); - out << (char)5; - out << (char)mHours; - out << (char)mMinutes; - out << (char)mSeconds; - out << (char)mFrames; - out << (char)mSubFrames; -} - -void SmpteOffset::writeToOutputStream(std::ostream &out, bool writeType) -{ - MetaEvent::writeToOutputStream(out, writeType); -} - -std::shared_ptr SmpteOffset::parseSmpteOffset(int tick, int delta, - MetaEventData *info) -{ - if (info->length.getValue() != 5) - { - return std::make_shared(tick, delta, info); - } - int rrHours = info->data[0]; - auto rr = rrHours >> 5; - auto fps = FrameRate::fromInt(rr); - auto hour = rrHours & 31; - int min = info->data[1]; - int sec = info->data[2]; - int frm = info->data[3]; - int sub = info->data[4]; - return std::make_shared(tick, delta, fps, hour, min, sec, frm, - sub); -} - -int SmpteOffset::compareTo(MidiEvent *other) -{ - if (mTick != other->getTick()) - { - return mTick < other->getTick() ? -1 : 1; - } - if (mDelta.getValue() != other->getDelta()) - { - return mDelta.getValue() < other->getDelta() ? 1 : -1; - } - if (dynamic_cast(other) == nullptr) - { - return 1; - } - return 0; -} diff --git a/src/main/file/mid/event/meta/SmpteOffset.hpp b/src/main/file/mid/event/meta/SmpteOffset.hpp deleted file mode 100644 index 54368b885..000000000 --- a/src/main/file/mid/event/meta/SmpteOffset.hpp +++ /dev/null @@ -1,53 +0,0 @@ -#pragma once -#include "file/mid/event/meta/MetaEvent.hpp" - -namespace mpc::file::mid::event::meta -{ - - class FrameRate; - - class MetaEventData; - - class SmpteOffset : public MetaEvent - { - - public: - static const int FRAME_RATE_24{0}; - static const int FRAME_RATE_25{1}; - static const int FRAME_RATE_30_DROP{2}; - static const int FRAME_RATE_30{3}; - - private: - FrameRate *mFrameRate{}; - int mHours{}; - int mMinutes{}; - int mSeconds{}; - int mFrames{}; - int mSubFrames{}; - - public: - virtual void setFrameRate(FrameRate *fps); - virtual FrameRate *getFrameRate(); - virtual void setHours(int h); - virtual int getHours(); - virtual void setMinutes(int m); - virtual int getMinutes(); - virtual void setSeconds(int s); - virtual int getSeconds(); - virtual void setFrames(int f); - virtual int getFrames(); - virtual void setSubFrames(int s); - virtual int getSubFrames(); - - int getEventSize() override; - void writeToOutputStream(std::ostream &out) override; - void writeToOutputStream(std::ostream &out, bool writeType) override; - - static std::shared_ptr parseSmpteOffset(int tick, int delta, - MetaEventData *info); - virtual int compareTo(MidiEvent *other); - - SmpteOffset(int tick, int delta, FrameRate *fps, int hour, int min, - int sec, int fr, int subfr); - }; -} // namespace mpc::file::mid::event::meta diff --git a/src/main/file/mid/event/meta/Tempo.cpp b/src/main/file/mid/event/meta/Tempo.cpp deleted file mode 100644 index e62eb331a..000000000 --- a/src/main/file/mid/event/meta/Tempo.cpp +++ /dev/null @@ -1,88 +0,0 @@ -#include "file/mid/event/meta/Tempo.hpp" - -#include "file/mid/event/MidiEvent.hpp" -#include "file/mid/event/meta/GenericMetaEvent.hpp" -#include "file/mid/event/meta/MetaEventData.hpp" -#include "file/mid/event/meta/MetaEvent.hpp" -#include "file/mid/util/MidiUtil.hpp" -#include "file/mid/util/VariableLengthInt.hpp" - -using namespace mpc::file::mid::event::meta; - -Tempo::Tempo() : Tempo(0, 0, DEFAULT_MPQN) {} - -Tempo::Tempo(int tick, int delta, int mpqn) : MetaEvent(tick, delta, TEMPO) -{ - setMpqn(mpqn); - mLength = util::VariableLengthInt(3); -} - -const int Tempo::DEFAULT_MPQN; - -float Tempo::getBpm() const -{ - return mBPM; -} - -void Tempo::setMpqn(int m) -{ - mMPQN = m; - mBPM = 6.0E7f / mMPQN; -} - -void Tempo::setBpm(float b) -{ - mBPM = b; - mMPQN = static_cast(60000000 / mBPM); -} - -int Tempo::getEventSize() -{ - return 6; -} - -void Tempo::writeToOutputStream(std::ostream &out) -{ - MetaEvent::writeToOutputStream(out); - out << (char)3; - auto mpqn = util::MidiUtil::intToBytes(mMPQN, 3); - out.write(&mpqn[0], mpqn.size()); -} - -void Tempo::writeToOutputStream(std::ostream &out, bool writeType) -{ - MetaEvent::writeToOutputStream(out, writeType); -} - -std::shared_ptr Tempo::parseTempo(int tick, int delta, - MetaEventData *info) -{ - if (info->length.getValue() != 3) - { - return std::make_shared(tick, delta, info); - } - auto mpqn = util::MidiUtil::bytesToInt(info->data, 0, 3); - return std::make_shared(tick, delta, mpqn); -} - -int Tempo::compareTo(MidiEvent *other) -{ - if (mTick != other->getTick()) - { - return mTick < other->getTick() ? -1 : 1; - } - if (mDelta.getValue() != other->getDelta()) - { - return mDelta.getValue() < other->getDelta() ? 1 : -1; - } - if (dynamic_cast(other) == nullptr) - { - return 1; - } - auto o = dynamic_cast(other); - if (mMPQN != o->mMPQN) - { - return mMPQN < o->mMPQN ? -1 : 1; - } - return 0; -} diff --git a/src/main/file/mid/event/meta/Tempo.hpp b/src/main/file/mid/event/meta/Tempo.hpp deleted file mode 100644 index 8b2da661c..000000000 --- a/src/main/file/mid/event/meta/Tempo.hpp +++ /dev/null @@ -1,38 +0,0 @@ -#pragma once -#include "file/mid/event/meta/MetaEvent.hpp" - -namespace mpc::file::mid::event::meta -{ - class MetaEventData; -} - -namespace mpc::file::mid::event::meta -{ - class Tempo : public MetaEvent - { - - public: - static const int DEFAULT_MPQN{500000}; - - private: - int mMPQN; - float mBPM; - - public: - float getBpm() const; - void setMpqn(int m); - void setBpm(float b); - - int getEventSize() override; - void writeToOutputStream(std::ostream &out) override; - void writeToOutputStream(std::ostream &out, bool writeType) override; - - static std::shared_ptr parseTempo(int tick, int delta, - MetaEventData *info); - - virtual int compareTo(MidiEvent *other); - - Tempo(); - Tempo(int tick, int delta, int mpqn); - }; -} // namespace mpc::file::mid::event::meta diff --git a/src/main/file/mid/event/meta/Text.cpp b/src/main/file/mid/event/meta/Text.cpp deleted file mode 100644 index 2adf47f21..000000000 --- a/src/main/file/mid/event/meta/Text.cpp +++ /dev/null @@ -1,10 +0,0 @@ -#include "file/mid/event/meta/Text.hpp" - -#include "file/mid/event/meta/MetaEvent.hpp" - -using namespace mpc::file::mid::event::meta; - -Text::Text(int tick, int delta, const std::string &text) - : TextualMetaEvent(tick, delta, TEXT_EVENT, text) -{ -} diff --git a/src/main/file/mid/event/meta/Text.hpp b/src/main/file/mid/event/meta/Text.hpp deleted file mode 100644 index 455082453..000000000 --- a/src/main/file/mid/event/meta/Text.hpp +++ /dev/null @@ -1,12 +0,0 @@ -#pragma once -#include "file/mid/event/meta/TextualMetaEvent.hpp" - -namespace mpc::file::mid::event::meta -{ - class Text : public TextualMetaEvent - { - - public: - Text(int tick, int delta, const std::string &text); - }; -} // namespace mpc::file::mid::event::meta diff --git a/src/main/file/mid/event/meta/TextualMetaEvent.cpp b/src/main/file/mid/event/meta/TextualMetaEvent.cpp deleted file mode 100644 index 97253ffff..000000000 --- a/src/main/file/mid/event/meta/TextualMetaEvent.cpp +++ /dev/null @@ -1,66 +0,0 @@ -#include "file/mid/event/meta/TextualMetaEvent.hpp" - -#include "file/mid/event/MidiEvent.hpp" -#include "file/mid/util/VariableLengthInt.hpp" - -using namespace mpc::file::mid::event::meta; - -TextualMetaEvent::TextualMetaEvent(int tick, int delta, int type, - const std::string &text) - : MetaEvent(tick, delta, type) -{ - setText(text); - mLength = util::VariableLengthInt(text.length()); -} - -void TextualMetaEvent::setText(const std::string &t) -{ - mText = t; - mLength.setValue(t.length()); -} - -std::string TextualMetaEvent::getText() -{ - return mText; -} - -int TextualMetaEvent::getEventSize() -{ - return 1 + 1 + mLength.getByteCount() + mLength.getValue(); -} - -void TextualMetaEvent::writeToOutputStream(std::ostream &out) -{ - MetaEvent::writeToOutputStream(out); - auto length = mLength.getBytes(); - out.write(&length[0], length.size()); - out.write(&mText[0], mText.size()); -} - -void TextualMetaEvent::writeToOutputStream(std::ostream &out, bool writeType) -{ - MetaEvent::writeToOutputStream(out, writeType); -} - -int TextualMetaEvent::compareTo(MidiEvent *other) const -{ - if (mTick != other->getTick()) - { - return mTick < other->getTick() ? -1 : 1; - } - if (mDelta.getValue() != other->getDelta()) - { - return mDelta.getValue() < other->getDelta() ? 1 : -1; - } - if (dynamic_cast(other) == nullptr) - { - return 1; - } - auto o = dynamic_cast(other); - return mText.compare(o->mText); -} - -std::string TextualMetaEvent::toString() -{ - return MetaEvent::toString() + ": " + mText; -} diff --git a/src/main/file/mid/event/meta/TextualMetaEvent.hpp b/src/main/file/mid/event/meta/TextualMetaEvent.hpp deleted file mode 100644 index c64efb409..000000000 --- a/src/main/file/mid/event/meta/TextualMetaEvent.hpp +++ /dev/null @@ -1,27 +0,0 @@ -#pragma once -#include "file/mid/event/meta/MetaEvent.hpp" - -#include - -namespace mpc::file::mid::event::meta -{ - class TextualMetaEvent : public MetaEvent - { - - public: - std::string mText; - - void setText(const std::string &t); - std::string getText(); - - void writeToOutputStream(std::ostream &out) override; - void writeToOutputStream(std::ostream &out, bool writeType) override; - std::string toString() override; - int getEventSize() override; - - int compareTo(MidiEvent *other) const; - - TextualMetaEvent(int tick, int delta, int type, - const std::string &text); - }; -} // namespace mpc::file::mid::event::meta diff --git a/src/main/file/mid/event/meta/TimeSignatureEvent.cpp b/src/main/file/mid/event/meta/TimeSignatureEvent.cpp deleted file mode 100644 index 6f44fb722..000000000 --- a/src/main/file/mid/event/meta/TimeSignatureEvent.cpp +++ /dev/null @@ -1,150 +0,0 @@ -#include "file/mid/event/meta/TimeSignatureEvent.hpp" - -#include "file/mid/event/MidiEvent.hpp" -#include "file/mid/event/meta/GenericMetaEvent.hpp" -#include "file/mid/event/meta/MetaEventData.hpp" -#include "file/mid/event/meta/MetaEvent.hpp" -#include "file/mid/util/VariableLengthInt.hpp" - -#include - -using namespace mpc::file::mid::event::meta; - -TimeSignature::TimeSignature() - : TimeSignature(0, 0, 4, 4, DEFAULT_METER, DEFAULT_DIVISION) -{ -} - -TimeSignature::TimeSignature(int tick, int delta, int num, int den, int meter, - int div) - : MetaEvent(tick, delta, TIME_SIGNATURE) -{ - setTimeSignature(num, den, meter, div); - mLength = util::VariableLengthInt(4); -} - -const int TimeSignature::METER_EIGHTH; -const int TimeSignature::METER_QUARTER; -const int TimeSignature::METER_HALF; -const int TimeSignature::METER_WHOLE; -const int TimeSignature::DEFAULT_METER; -const int TimeSignature::DEFAULT_DIVISION; - -void TimeSignature::setTimeSignature(int num, int den, int meter, int div) -{ - mNumerator = num; - mDenominator = log2(den); - mMeter = meter; - mDivision = div; -} - -int TimeSignature::getNumerator() const -{ - return mNumerator; -} - -int TimeSignature::getDenominatorValue() const -{ - return mDenominator; -} - -int TimeSignature::getRealDenominator() const -{ - return static_cast(pow(2, mDenominator)); -} - -int TimeSignature::getMeter() const -{ - return mMeter; -} - -int TimeSignature::getDivision() const -{ - return mDivision; -} - -int TimeSignature::getEventSize() -{ - return 7; -} - -void TimeSignature::writeToOutputStream(std::ostream &out) -{ - MetaEvent::writeToOutputStream(out); - out << (char)4; - out << (char)mNumerator; - out << (char)mDenominator; - out << (char)mMeter; - out << (char)mDivision; -} - -void TimeSignature::writeToOutputStream(std::ostream &out, bool writeType) -{ - MetaEvent::writeToOutputStream(out, writeType); -} - -std::shared_ptr -TimeSignature::parseTimeSignature(int tick, int delta, MetaEventData *info) -{ - if (info->length.getValue() != 4) - { - return std::make_shared(tick, delta, info); - } - int num = info->data[0]; - int den = info->data[1]; - int met = info->data[2]; - int fps = info->data[3]; - den = (int)pow(2, den); - return std::make_shared(tick, delta, num, den, met, fps); -} - -int TimeSignature::log2(int den) const -{ - switch (den) - { - case 2: - return 1; - case 4: - return 2; - case 8: - return 3; - case 16: - return 4; - case 32: - return 5; - } - - return 0; -} - -std::string TimeSignature::toString() -{ - return MetaEvent::toString() + " " + std::to_string(mNumerator) + "/" + - std::to_string(getRealDenominator()); -} - -int TimeSignature::compareTo(MidiEvent *other) -{ - if (mTick != other->getTick()) - { - return mTick < other->getTick() ? -1 : 1; - } - if (mDelta.getValue() != other->getDelta()) - { - return mDelta.getValue() < other->getDelta() ? 1 : -1; - } - if (dynamic_cast(other) == nullptr) - { - return 1; - } - auto o = dynamic_cast(other); - if (mNumerator != o->mNumerator) - { - return mNumerator < o->mNumerator ? -1 : 1; - } - if (mDenominator != o->mDenominator) - { - return mDenominator < o->mDenominator ? -1 : 1; - } - return 0; -} diff --git a/src/main/file/mid/event/meta/TimeSignatureEvent.hpp b/src/main/file/mid/event/meta/TimeSignatureEvent.hpp deleted file mode 100644 index 7d6257db6..000000000 --- a/src/main/file/mid/event/meta/TimeSignatureEvent.hpp +++ /dev/null @@ -1,54 +0,0 @@ -#pragma once -#include "file/mid/event/meta/MetaEvent.hpp" - -namespace mpc::file::mid::event::meta -{ - class MetaEventData; -} - -namespace mpc::file::mid::event::meta -{ - class TimeSignature : public MetaEvent - { - - public: - static const int METER_EIGHTH{12}; - static const int METER_QUARTER{24}; - static const int METER_HALF{48}; - static const int METER_WHOLE{96}; - static const int DEFAULT_METER{24}; - static const int DEFAULT_DIVISION{8}; - - private: - int mNumerator; - int mDenominator; - int mMeter; - int mDivision; - - public: - void setTimeSignature(int num, int den, int meter, int div); - int getNumerator() const; - int getDenominatorValue() const; - int getRealDenominator() const; - int getMeter() const; - int getDivision() const; - - int getEventSize() override; - void writeToOutputStream(std::ostream &out) override; - void writeToOutputStream(std::ostream &out, bool writeType) override; - - static std::shared_ptr - parseTimeSignature(int tick, int delta, MetaEventData *info); - - private: - int log2(int den) const; - - public: - std::string toString() override; - virtual int compareTo(MidiEvent *other); - - TimeSignature(); - TimeSignature(int tick, int delta, int num, int den, int meter, - int div); - }; -} // namespace mpc::file::mid::event::meta diff --git a/src/main/file/mid/event/meta/TrackName.cpp b/src/main/file/mid/event/meta/TrackName.cpp deleted file mode 100644 index 4d68f36be..000000000 --- a/src/main/file/mid/event/meta/TrackName.cpp +++ /dev/null @@ -1,20 +0,0 @@ -#include "file/mid/event/meta/TrackName.hpp" - -#include "file/mid/event/meta/MetaEvent.hpp" - -using namespace mpc::file::mid::event::meta; - -TrackName::TrackName(int tick, int delta, const std::string &name) - : TextualMetaEvent(tick, delta, TRACK_NAME, name) -{ -} - -void TrackName::setName(const std::string &name) -{ - setText(name); -} - -std::string TrackName::getTrackName() -{ - return getText(); -} diff --git a/src/main/file/mid/event/meta/TrackName.hpp b/src/main/file/mid/event/meta/TrackName.hpp deleted file mode 100644 index 1fc52d268..000000000 --- a/src/main/file/mid/event/meta/TrackName.hpp +++ /dev/null @@ -1,17 +0,0 @@ -#pragma once -#include "file/mid/event/meta/TextualMetaEvent.hpp" - -#include - -namespace mpc::file::mid::event::meta -{ - class TrackName : public TextualMetaEvent - { - - public: - void setName(const std::string &name); - std::string getTrackName(); - - TrackName(int tick, int delta, const std::string &name); - }; -} // namespace mpc::file::mid::event::meta diff --git a/src/main/file/mid/util/MidiUtil.cpp b/src/main/file/mid/util/MidiUtil.cpp deleted file mode 100644 index 9ca1b634c..000000000 --- a/src/main/file/mid/util/MidiUtil.cpp +++ /dev/null @@ -1,73 +0,0 @@ -#include "file/mid/util/MidiUtil.hpp" - -using namespace mpc::file::mid::util; - -int MidiUtil::bytesToInt(const std::vector &buff, int off, int len) -{ - int num = 0; - int shift = 0; - for (int i = off + len - 1; i >= off; i--) - { - num += (buff[i] & 255) << shift; - shift += 8; - } - return num; -} - -std::vector MidiUtil::intToBytes(int val, int byteCount) -{ - - auto buffer = std::vector(byteCount); - auto ints = std::vector(byteCount); - for (int i = 0; i < byteCount; i++) - { - ints[i] = val & 255; - buffer[byteCount - i - 1] = ints[i]; - val = val >> 8; - if (val == 0) - { - break; - } - } - return buffer; -} - -bool MidiUtil::bytesEqual(const std::vector &buf1, - const std::vector &buf2, int off, int len) -{ - for (int i = off; i < off + len; i++) - { - if (i >= buf1.size() || i >= buf2.size()) - { - return false; - } - if (buf1[i] != buf2[i]) - { - return false; - } - } - return true; -} - -std::string MidiUtil::HEX = "0123456789ABCDEF"; - -std::string MidiUtil::byteToHex(char b) -{ - - int high = (b & 240) >> 4; - int low = b & 15; - std::string res; - res.push_back(HEX[high]); - res.push_back(HEX[low]); - return res; -} - -std::string MidiUtil::bytesToHex(const std::vector &b) -{ - std::string res = ""; - for (int i = 0; i < b.size(); i++) - { - res += byteToHex(b[i]) + " "; - } - return res; -} diff --git a/src/main/file/mid/util/MidiUtil.hpp b/src/main/file/mid/util/MidiUtil.hpp deleted file mode 100644 index 50e3b1546..000000000 --- a/src/main/file/mid/util/MidiUtil.hpp +++ /dev/null @@ -1,23 +0,0 @@ -#pragma once - -#include -#include - -namespace mpc::file::mid::util -{ - class MidiUtil - { - public: - static int bytesToInt(const std::vector &buff, int off, int len); - static std::vector intToBytes(int val, int byteCount); - static bool bytesEqual(const std::vector &buf1, - const std::vector &buf2, int off, int len); - - private: - static std::string HEX; - - public: - static std::string byteToHex(char b); - static std::string bytesToHex(const std::vector &b); - }; -} // namespace mpc::file::mid::util diff --git a/src/main/file/mid/util/VariableLengthInt.cpp b/src/main/file/mid/util/VariableLengthInt.cpp deleted file mode 100644 index f49ac9f10..000000000 --- a/src/main/file/mid/util/VariableLengthInt.cpp +++ /dev/null @@ -1,112 +0,0 @@ -#include "file/mid/util/VariableLengthInt.hpp" - -#include "file/mid/util/MidiUtil.hpp" - -using namespace mpc::file::mid::util; - -VariableLengthInt::VariableLengthInt() : VariableLengthInt(0) {} - -VariableLengthInt::VariableLengthInt(int value) -{ - setValue(value); -} - -VariableLengthInt::VariableLengthInt(std::stringstream &in) -{ - parseBytes(in); -} - -void VariableLengthInt::setValue(int value) -{ - mValue = value; - buildBytes(); -} - -int VariableLengthInt::getValue() const -{ - return mValue; -} - -int VariableLengthInt::getByteCount() const -{ - return mSizeInBytes; -} - -std::vector VariableLengthInt::getBytes() -{ - return mBytes; -} - -void VariableLengthInt::parseBytes(std::stringstream &in) -{ - auto ints = std::vector(4); - mSizeInBytes = 0; - mValue = 0; - int shift = 0; - - auto b = in.get(); - - auto firstB = std::to_string(b); - - while (mSizeInBytes < 4) - { - mSizeInBytes++; - auto variable = (b & 128) > 0; - - if (!variable) - { - ints[mSizeInBytes - 1] = b & 127; - break; - } - - ints[mSizeInBytes - 1] = b & 127; - b = in.get(); - } - - for (int i = 1; i < mSizeInBytes; i++) - { - shift += 7; - } - mBytes = std::vector(mSizeInBytes); - - for (int i = 0; i < mSizeInBytes; i++) - { - mBytes[i] = static_cast(ints[i]); - mValue += ints[i] << shift; - shift -= 7; - } -} - -void VariableLengthInt::buildBytes() -{ - if (mValue == 0) - { - mBytes = std::vector(1); - mBytes[0] = 0; - mSizeInBytes = 1; - return; - } - mSizeInBytes = 0; - auto vals = std::vector(4); - auto tmpVal = mValue; - while (mSizeInBytes < 4 && tmpVal > 0) - { - vals[mSizeInBytes] = tmpVal & 127; - mSizeInBytes++; - tmpVal = tmpVal >> 7; - } - for (int i = 1; i < mSizeInBytes; i++) - { - vals[i] |= 128; - } - mBytes = std::vector(mSizeInBytes); - for (int i = 0; i < mSizeInBytes; i++) - { - mBytes[i] = static_cast(vals[mSizeInBytes - i - 1]); - } -} - -std::string VariableLengthInt::toString() const -{ - return MidiUtil::bytesToHex(mBytes) + " (" + std::to_string(mValue) + ")"; -} diff --git a/src/main/file/mid/util/VariableLengthInt.hpp b/src/main/file/mid/util/VariableLengthInt.hpp deleted file mode 100644 index 0512c0dc6..000000000 --- a/src/main/file/mid/util/VariableLengthInt.hpp +++ /dev/null @@ -1,33 +0,0 @@ -#pragma once - -#include -#include -#include - -namespace mpc::file::mid::util -{ - - class VariableLengthInt - { - - int mValue = 0; - std::vector mBytes; - int mSizeInBytes = 0; - - public: - void setValue(int value); - int getValue() const; - int getByteCount() const; - std::vector getBytes(); - std::string toString() const; - - private: - void parseBytes(std::stringstream &in); - void buildBytes(); - - public: - VariableLengthInt(); - VariableLengthInt(int value); - VariableLengthInt(std::stringstream &in); - }; -} // namespace mpc::file::mid::util diff --git a/src/main/file/pgmreader/PRMixer.cpp b/src/main/file/pgmreader/PRMixer.cpp deleted file mode 100644 index e35ca4da0..000000000 --- a/src/main/file/pgmreader/PRMixer.cpp +++ /dev/null @@ -1,78 +0,0 @@ -#include "file/pgmreader/PRMixer.hpp" - -#include "file/pgmreader/ProgramFileReader.hpp" -#include "file/pgmreader/SoundNames.hpp" - -#include "Util.hpp" - -using namespace mpc::file::pgmreader; - -Mixer::Mixer(ProgramFileReader *programFile) -{ - this->programFile = programFile; -} - -int Mixer::getSampleNamesSize() const -{ - auto sampleNamesSize = programFile->getSampleNames()->getSampleNamesSize(); - return sampleNamesSize; -} - -int Mixer::getMixerStart() -{ - auto mixerStart = 4 + getSampleNamesSize() + 2 + 17 + 9 + 6 + 1601; - return mixerStart; -} - -int Mixer::getMixerEnd() -{ - auto mixerEnd = 4 + getSampleNamesSize() + 2 + 17 + 9 + 6 + 1601 + 387; - return mixerEnd; -} - -std::vector Mixer::getMixerArray() -{ - auto pgmFileArray = programFile->readProgramFileArray(); - if (mixerArray.size() == 0) - { - mixerArray = - Util::vecCopyOfRange(pgmFileArray, getMixerStart(), getMixerEnd()); - } - return mixerArray; -} - -int Mixer::getEffectsOutput(int noteIndex) -{ - auto effectsOutput = getMixerArray()[noteIndex * 6 + 0]; - return effectsOutput; -} - -int Mixer::getVolume(int noteIndex) -{ - auto volume = getMixerArray()[noteIndex * 6 + 1]; - return volume; -} - -int Mixer::getPan(int noteIndex) -{ - auto pan = getMixerArray()[noteIndex * 6 + 2]; - return pan; -} - -int Mixer::getVolumeIndividual(int noteIndex) -{ - auto volumeIndividual = getMixerArray()[noteIndex * 6 + 3]; - return volumeIndividual; -} - -int Mixer::getOutput(int noteIndex) -{ - auto output = getMixerArray()[noteIndex * 6 + 4]; - return output; -} - -int Mixer::getEffectsSendLevel(int noteIndex) -{ - auto effectsSendLevel = getMixerArray()[noteIndex * 6 + 5]; - return effectsSendLevel; -} diff --git a/src/main/file/pgmreader/PRMixer.hpp b/src/main/file/pgmreader/PRMixer.hpp deleted file mode 100644 index a2bd08be4..000000000 --- a/src/main/file/pgmreader/PRMixer.hpp +++ /dev/null @@ -1,41 +0,0 @@ -#pragma once - -#include - -namespace mpc -{ - namespace file - { - namespace pgmreader - { - - class ProgramFileReader; - - class Mixer - { - - public: - std::vector mixerArray{}; - - private: - ProgramFileReader *programFile{}; - - public: - int getSampleNamesSize() const; - int getMixerStart(); - int getMixerEnd(); - std::vector getMixerArray(); - - int getEffectsOutput(int note); - int getVolume(int note); - int getPan(int note); - int getVolumeIndividual(int note); - int getOutput(int note); - int getEffectsSendLevel(int note); - - Mixer(ProgramFileReader *programFile); - }; - - } // namespace pgmreader - } // namespace file -} // namespace mpc diff --git a/src/main/file/pgmreader/PRPads.cpp b/src/main/file/pgmreader/PRPads.cpp deleted file mode 100644 index c4da745f3..000000000 --- a/src/main/file/pgmreader/PRPads.cpp +++ /dev/null @@ -1,46 +0,0 @@ -#include "file/pgmreader/PRPads.hpp" - -#include "file/pgmreader/PgmAllNoteParameters.hpp" -#include "file/pgmreader/ProgramFileReader.hpp" -#include "file/pgmreader/SoundNames.hpp" - -#include "Util.hpp" - -using namespace mpc::file::pgmreader; - -Pads::Pads(ProgramFileReader *programFile) -{ - this->programFile = programFile; -} - -int Pads::getSampleNamesSize() -{ - sampleNamesSize = programFile->getSampleNames()->getSampleNamesSize(); - return sampleNamesSize; -} - -int Pads::getPadsStart() -{ - auto padsStart = 4 + getSampleNamesSize() + 2 + 17 + 9 + 6 + 1601 + 387; - return padsStart; -} - -int Pads::getPadsEnd() -{ - auto padsEnd = 4 + getSampleNamesSize() + 2 + 17 + 9 + 6 + 1601 + 387 + 264; - return padsEnd; -} - -std::vector Pads::getPadsArray() -{ - auto pgmFileArray = programFile->readProgramFileArray(); - padsArray = - Util::vecCopyOfRange(pgmFileArray, getPadsStart(), getPadsEnd()); - return padsArray; -} - -int Pads::getNote(int pad) -{ - int padMidiNote = getPadsArray()[pad]; - return padMidiNote; -} diff --git a/src/main/file/pgmreader/PRPads.hpp b/src/main/file/pgmreader/PRPads.hpp deleted file mode 100644 index 1e2c09fbc..000000000 --- a/src/main/file/pgmreader/PRPads.hpp +++ /dev/null @@ -1,38 +0,0 @@ -#pragma once - -#include - -namespace mpc -{ - namespace file - { - namespace pgmreader - { - - class ProgramFileReader; - - class Pads - { - - public: - int sampleNamesSize{0}; - - std::vector padsArray{}; - - private: - ProgramFileReader *programFile{}; - - public: - int getSampleNamesSize(); - int getPadsStart(); - int getPadsEnd(); - std::vector getPadsArray(); - - int getNote(int pad); - - Pads(ProgramFileReader *programFile); - }; - - } // namespace pgmreader - } // namespace file -} // namespace mpc diff --git a/src/main/file/pgmreader/PRSlider.cpp b/src/main/file/pgmreader/PRSlider.cpp deleted file mode 100644 index 48c9ef75c..000000000 --- a/src/main/file/pgmreader/PRSlider.cpp +++ /dev/null @@ -1,99 +0,0 @@ -#include "file/pgmreader/PRSlider.hpp" - -#include "file/pgmreader/ProgramFileReader.hpp" -#include "file/pgmreader/SoundNames.hpp" - -#include "Util.hpp" - -using namespace mpc::file::pgmreader; - -Slider::Slider(ProgramFileReader *programFile) -{ - this->programFile = programFile; -} - -int Slider::getSampleNamesSize() -{ - sampleNamesSize = programFile->getSampleNames()->getSampleNamesSize(); - return sampleNamesSize; -} - -int Slider::getSliderStart() -{ - int sliderStart = 4 + getSampleNamesSize() + 2 + 17; - return sliderStart; -} - -int Slider::getSliderEnd() -{ - auto sliderEnd = 4 + getSampleNamesSize() + 2 + 17 + 10 + 5; - return sliderEnd; -} - -std::vector Slider::getSliderArray() -{ - auto pgmFileArray = programFile->readProgramFileArray(); - sliderArray = - Util::vecCopyOfRange(pgmFileArray, getSliderStart(), getSliderEnd()); - return sliderArray; -} - -int Slider::getMidiNoteAssign() -{ - const auto midiNoteAssign = getSliderArray()[0]; - return midiNoteAssign; -} - -int Slider::getTuneLow() -{ - auto tuneLow = getSliderArray()[1]; - return tuneLow; -} - -int Slider::getTuneHigh() -{ - auto tuneHigh = getSliderArray()[2]; - return tuneHigh; -} - -int Slider::getDecayLow() -{ - auto decayLow = getSliderArray()[3]; - return decayLow; -} - -int Slider::getDecayHigh() -{ - auto decayHigh = getSliderArray()[4]; - return decayHigh; -} - -int Slider::getAttackLow() -{ - auto attackLow = getSliderArray()[5]; - return attackLow; -} - -int Slider::getAttackHigh() -{ - auto attackHigh = getSliderArray()[6]; - return attackHigh; -} - -int Slider::getFilterLow() -{ - auto filterLow = getSliderArray()[7]; - return filterLow; -} - -int Slider::getFilterHigh() -{ - auto filterHigh = getSliderArray()[8]; - return filterHigh; -} - -int Slider::getControlChange() -{ - auto controlChange = getSliderArray()[9]; - return controlChange; -} diff --git a/src/main/file/pgmreader/PRSlider.hpp b/src/main/file/pgmreader/PRSlider.hpp deleted file mode 100644 index 705221629..000000000 --- a/src/main/file/pgmreader/PRSlider.hpp +++ /dev/null @@ -1,47 +0,0 @@ -#pragma once - -#include - -namespace mpc -{ - namespace file - { - namespace pgmreader - { - - class ProgramFileReader; - - class Slider - { - - public: - int sampleNamesSize{0}; - - std::vector sliderArray{}; - - private: - ProgramFileReader *programFile{}; - - public: - int getSampleNamesSize(); - int getSliderStart(); - int getSliderEnd(); - std::vector getSliderArray(); - - int getMidiNoteAssign(); - int getTuneLow(); - int getTuneHigh(); - int getDecayLow(); - int getDecayHigh(); - int getAttackLow(); - int getAttackHigh(); - int getFilterLow(); - int getFilterHigh(); - int getControlChange(); - - Slider(ProgramFileReader *programFile); - }; - - } // namespace pgmreader - } // namespace file -} // namespace mpc diff --git a/src/main/file/pgmreader/PgmAllNoteParameters.cpp b/src/main/file/pgmreader/PgmAllNoteParameters.cpp deleted file mode 100644 index 96bf89e1b..000000000 --- a/src/main/file/pgmreader/PgmAllNoteParameters.cpp +++ /dev/null @@ -1,191 +0,0 @@ -#include "sampler/VoiceOverlapMode.hpp" -#include "file/pgmreader/PgmAllNoteParameters.hpp" - -#include "file/pgmreader/PRPads.hpp" -#include "file/pgmreader/ProgramFileReader.hpp" -#include "file/pgmreader/SoundNames.hpp" - -#include "file/ByteUtil.hpp" - -#include "Util.hpp" - -using namespace mpc::file::pgmreader; - -PgmAllNoteParameters::PgmAllNoteParameters(ProgramFileReader *programFile) -{ - this->programFile = programFile; -} - -int PgmAllNoteParameters::getSampleNamesSize() -{ - sampleNamesSize = programFile->getSampleNames()->getSampleNamesSize(); - return sampleNamesSize; -} - -int PgmAllNoteParameters::getMidiNotesStart() -{ - auto midiNotesStart = 4 + getSampleNamesSize() + 2 + 17 + 9 + 6; - return midiNotesStart; -} - -int PgmAllNoteParameters::getMidiNotesEnd() -{ - auto midiNotesEnd = 4 + getSampleNamesSize() + 2 + 17 + 9 + 6 + 1601; - return midiNotesEnd; -} - -std::vector PgmAllNoteParameters::getMidiNotesArray() -{ - auto pgmFileArray = programFile->readProgramFileArray(); - midiNotesArray = Util::vecCopyOfRange(pgmFileArray, getMidiNotesStart(), - getMidiNotesEnd()); - return midiNotesArray; -} - -int PgmAllNoteParameters::getSampleSelect(int midiNote) -{ - int sampleSelect = getMidiNotesArray()[midiNote * 25 + 0]; - return sampleSelect; -} - -int PgmAllNoteParameters::getSoundGenerationMode(int midiNote) -{ - auto soundGenerationMode = getMidiNotesArray()[midiNote * 25 + 1]; - return soundGenerationMode; -} - -int PgmAllNoteParameters::getVelocityRangeLower(int midiNote) -{ - auto velocityRangeLower = getMidiNotesArray()[midiNote * 25 + 2]; - return velocityRangeLower; -} - -int PgmAllNoteParameters::getAlsoPlayUse1(int midiNote) -{ - auto alsoPlayUse1 = getMidiNotesArray()[midiNote * 25 + 3]; - return alsoPlayUse1; -} - -int PgmAllNoteParameters::getVelocityRangeUpper(int midiNote) -{ - auto velocityRangeUpper = getMidiNotesArray()[midiNote * 25 + 4]; - return velocityRangeUpper; -} - -int PgmAllNoteParameters::getAlsoPlayUse2(int midiNote) -{ - auto alsoPlayUse2 = getMidiNotesArray()[midiNote * 25 + 5]; - return alsoPlayUse2; -} - -mpc::sampler::VoiceOverlapMode -PgmAllNoteParameters::getVoiceOverlapMode(int midiNote) -{ - auto voiceOverlapMode = getMidiNotesArray()[midiNote * 25 + 6]; - return static_cast(voiceOverlapMode); -} - -int PgmAllNoteParameters::getMuteAssign1(int midiNote) -{ - auto muteAssign1 = getMidiNotesArray()[midiNote * 25 + 7]; - return muteAssign1 == 0 ? 34 : muteAssign1; -} - -int PgmAllNoteParameters::getMuteAssign2(int midiNote) -{ - auto muteAssign2 = getMidiNotesArray()[midiNote * 25 + 8]; - return muteAssign2 == 0 ? 34 : muteAssign2; -} - -int PgmAllNoteParameters::getTune(int midiNote) -{ - auto startPos = midiNote * 25 + 9; - auto endPos = midiNote * 25 + 11; - auto tuneBytes = - Util::vecCopyOfRange(getMidiNotesArray(), startPos, endPos); - return ByteUtil::bytes2short(tuneBytes); -} - -int PgmAllNoteParameters::getAttack(int midiNote) -{ - auto attack = getMidiNotesArray()[midiNote * 25 + 11]; - return attack; -} - -int PgmAllNoteParameters::getDecay(int midiNote) -{ - auto decay = getMidiNotesArray()[midiNote * 25 + 12]; - return decay; -} - -int PgmAllNoteParameters::getDecayMode(int midiNote) -{ - auto decayMode = getMidiNotesArray()[midiNote * 25 + 13]; - return decayMode; -} - -int PgmAllNoteParameters::getCutoff(int midiNote) -{ - auto cutoff = getMidiNotesArray()[midiNote * 25 + 14]; - return cutoff; -} - -int PgmAllNoteParameters::getResonance(int midiNote) -{ - auto resonance = getMidiNotesArray()[midiNote * 25 + 15]; - return resonance; -} - -int PgmAllNoteParameters::getVelEnvToFiltAtt(int midiNote) -{ - auto velEnvToFiltAtt = getMidiNotesArray()[midiNote * 25 + 16]; - return velEnvToFiltAtt; -} - -int PgmAllNoteParameters::getVelEnvToFiltDec(int midiNote) -{ - auto velEnvToFiltDec = getMidiNotesArray()[midiNote * 25 + 17]; - return velEnvToFiltDec; -} - -int PgmAllNoteParameters::getVelEnvToFiltAmt(int midiNote) -{ - auto velEnvToFiltAmt = getMidiNotesArray()[midiNote * 25 + 18]; - return velEnvToFiltAmt; -} - -int PgmAllNoteParameters::getVelocityToLevel(int midiNote) -{ - auto velocityToLevel = getMidiNotesArray()[midiNote * 25 + 19]; - return velocityToLevel; -} - -int PgmAllNoteParameters::getVelocityToAttack(int midiNote) -{ - auto velocityToAttack = getMidiNotesArray()[midiNote * 25 + 20]; - return velocityToAttack; -} - -int PgmAllNoteParameters::getVelocityToStart(int midiNote) -{ - auto velocityToStart = getMidiNotesArray()[midiNote * 25 + 21]; - return velocityToStart; -} - -int PgmAllNoteParameters::getVelocityToCutoff(int midiNote) -{ - auto velocityToCutoff = getMidiNotesArray()[midiNote * 25 + 22]; - return velocityToCutoff; -} - -int PgmAllNoteParameters::getSliderParameter(int midiNote) -{ - auto sliderParameter = getMidiNotesArray()[midiNote * 25 + 23]; - return sliderParameter; -} - -int PgmAllNoteParameters::getVelocityToPitch(int midiNote) -{ - auto velocityToPitch = getMidiNotesArray()[midiNote * 25 + 24]; - return velocityToPitch; -} diff --git a/src/main/file/pgmreader/PgmAllNoteParameters.hpp b/src/main/file/pgmreader/PgmAllNoteParameters.hpp deleted file mode 100644 index 8fe0c535d..000000000 --- a/src/main/file/pgmreader/PgmAllNoteParameters.hpp +++ /dev/null @@ -1,54 +0,0 @@ -#pragma once - -#include "sampler/VoiceOverlapMode.hpp" -#include - -namespace mpc::file::pgmreader -{ - class ProgramFileReader; - - class PgmAllNoteParameters - { - - public: - int sampleNamesSize = 0; - - private: - std::vector midiNotesArray; - - ProgramFileReader *programFile; - - public: - int getSampleNamesSize(); - int getMidiNotesStart(); - int getMidiNotesEnd(); - std::vector getMidiNotesArray(); - - int getSampleSelect(int midiNote); - int getSoundGenerationMode(int midiNote); - int getVelocityRangeLower(int midiNote); - int getAlsoPlayUse1(int midiNote); - int getVelocityRangeUpper(int midiNote); - int getAlsoPlayUse2(int midiNote); - sampler::VoiceOverlapMode getVoiceOverlapMode(int midiNote); - int getMuteAssign1(int midiNote); - int getMuteAssign2(int midiNote); - int getTune(int midiNote); - int getAttack(int midiNote); - int getDecay(int midiNote); - int getDecayMode(int midiNote); - int getCutoff(int midiNote); - int getResonance(int midiNote); - int getVelEnvToFiltAtt(int midiNote); - int getVelEnvToFiltDec(int midiNote); - int getVelEnvToFiltAmt(int midiNote); - int getVelocityToLevel(int midiNote); - int getVelocityToAttack(int midiNote); - int getVelocityToStart(int midiNote); - int getVelocityToCutoff(int midiNote); - int getSliderParameter(int midiNote); - int getVelocityToPitch(int midiNote); - - PgmAllNoteParameters(ProgramFileReader *programFile); - }; -} // namespace mpc::file::pgmreader diff --git a/src/main/file/pgmreader/PgmHeader.cpp b/src/main/file/pgmreader/PgmHeader.cpp deleted file mode 100644 index f78ebd48e..000000000 --- a/src/main/file/pgmreader/PgmHeader.cpp +++ /dev/null @@ -1,39 +0,0 @@ -#include "PgmHeader.hpp" - -#include "MpcSpecs.hpp" - -#include "file/pgmreader/ProgramFileReader.hpp" - -#include "file/ByteUtil.hpp" -#include "Util.hpp" - -#include - -using namespace mpc::file::pgmreader; - -PgmHeader::PgmHeader(ProgramFileReader *programFile) -{ - auto pgmFileArray = programFile->readProgramFileArray(); - headerArray = Util::vecCopyOfRange(pgmFileArray, 0, 4); -} - -std::vector PgmHeader::getHeaderArray() -{ - return headerArray; -} - -bool PgmHeader::verifyMagic() const -{ - return headerArray[0] == PGM_HEADER_MAGIC[0] && - headerArray[1] == PGM_HEADER_MAGIC[1]; -} - -const uint16_t PgmHeader::getSoundCount() -{ - const auto result = - ByteUtil::bytes2ushort({headerArray[2], headerArray[3]}); - - assert(result <= Mpc2000XlSpecs::MAX_SOUND_COUNT_IN_MEMORY); - - return result; -} diff --git a/src/main/file/pgmreader/PgmHeader.hpp b/src/main/file/pgmreader/PgmHeader.hpp deleted file mode 100644 index 5bb442af4..000000000 --- a/src/main/file/pgmreader/PgmHeader.hpp +++ /dev/null @@ -1,26 +0,0 @@ -#pragma once - -#include -#include - -namespace mpc::file::pgmreader -{ - - class ProgramFileReader; - - class PgmHeader - { - - const std::vector PGM_HEADER_MAGIC{0x07, 0x04}; - std::vector headerArray; - - public: - std::vector getHeaderArray(); - - bool verifyMagic() const; - - const uint16_t getSoundCount(); - - PgmHeader(ProgramFileReader *programFile); - }; -} // namespace mpc::file::pgmreader diff --git a/src/main/file/pgmreader/ProgramFileReader.cpp b/src/main/file/pgmreader/ProgramFileReader.cpp deleted file mode 100644 index 5b9df1f56..000000000 --- a/src/main/file/pgmreader/ProgramFileReader.cpp +++ /dev/null @@ -1,76 +0,0 @@ -#include "file/pgmreader/ProgramFileReader.hpp" - -#include "disk/MpcFile.hpp" -#include "file/pgmreader/PRMixer.hpp" -#include "file/pgmreader/PRPads.hpp" -#include "file/pgmreader/PgmAllNoteParameters.hpp" -#include "file/pgmreader/PgmHeader.hpp" -#include "file/pgmreader/ProgramName.hpp" -#include "file/pgmreader/PRSlider.hpp" -#include "file/pgmreader/SoundNames.hpp" - -using namespace mpc::file::pgmreader; - -ProgramFileReader::ProgramFileReader( - const std::weak_ptr &_programFile) - : programFile(_programFile) -{ - pgmHeader = new PgmHeader(this); - programName = new ProgramName(this); - sampleNames = new SoundNames(this); - midiNotes = new PgmAllNoteParameters(this); - mixer = new Mixer(this); - slider = new Slider(this); - pads = new Pads(this); -} - -ProgramFileReader::~ProgramFileReader() -{ - delete pgmHeader; - delete programName; - delete sampleNames; - delete midiNotes; - delete mixer; - delete slider; - delete pads; -} - -std::vector ProgramFileReader::readProgramFileArray() const -{ - return programFile.lock()->getBytes(); -} - -PgmHeader *ProgramFileReader::getHeader() const -{ - return pgmHeader; -} - -SoundNames *ProgramFileReader::getSampleNames() const -{ - return sampleNames; -} - -PgmAllNoteParameters *ProgramFileReader::getAllNoteParameters() const -{ - return midiNotes; -} - -Mixer *ProgramFileReader::getMixer() const -{ - return mixer; -} - -Pads *ProgramFileReader::getPads() const -{ - return pads; -} - -ProgramName *ProgramFileReader::getProgramName() const -{ - return programName; -} - -Slider *ProgramFileReader::getSlider() const -{ - return slider; -} diff --git a/src/main/file/pgmreader/ProgramFileReader.hpp b/src/main/file/pgmreader/ProgramFileReader.hpp deleted file mode 100644 index 9c00edc3e..000000000 --- a/src/main/file/pgmreader/ProgramFileReader.hpp +++ /dev/null @@ -1,51 +0,0 @@ -#pragma once - -#include -#include - -namespace mpc::disk -{ - class MpcFile; -} - -namespace mpc::file::pgmreader -{ - class PgmHeader; - class SoundNames; - class ProgramName; - class PgmAllNoteParameters; - class Mixer; - class Slider; - class Pads; -} // namespace mpc::file::pgmreader - -namespace mpc::file::pgmreader -{ - - class ProgramFileReader - { - - PgmHeader *pgmHeader; - SoundNames *sampleNames; - ProgramName *programName; - PgmAllNoteParameters *midiNotes; - Mixer *mixer; - Slider *slider; - Pads *pads; - std::weak_ptr programFile; - - public: - std::vector readProgramFileArray() const; - - PgmHeader *getHeader() const; - SoundNames *getSampleNames() const; - PgmAllNoteParameters *getAllNoteParameters() const; - Mixer *getMixer() const; - Pads *getPads() const; - ProgramName *getProgramName() const; - Slider *getSlider() const; - - ProgramFileReader(const std::weak_ptr &); - ~ProgramFileReader(); - }; -} // namespace mpc::file::pgmreader diff --git a/src/main/file/pgmreader/ProgramName.cpp b/src/main/file/pgmreader/ProgramName.cpp deleted file mode 100644 index 364128fb8..000000000 --- a/src/main/file/pgmreader/ProgramName.cpp +++ /dev/null @@ -1,58 +0,0 @@ -#include "file/pgmreader/ProgramName.hpp" - -#include "file/pgmreader/ProgramFileReader.hpp" -#include "file/pgmreader/SoundNames.hpp" - -#include "Util.hpp" - -using namespace mpc::file::pgmreader; - -ProgramName::ProgramName(ProgramFileReader *programFile) -{ - this->programFile = programFile; -} - -int ProgramName::getSampleNamesSize() -{ - sampleNamesSize = programFile->getSampleNames()->getSampleNamesSize(); - return sampleNamesSize; -} - -int ProgramName::getProgramNameStart() -{ - auto programNameStart = 4 + getSampleNamesSize() + 2; - return programNameStart; -} - -int ProgramName::getProgramNameEnd() -{ - auto programNameEnd = 4 + getSampleNamesSize() + 2 + 17; - return programNameEnd; -} - -std::vector ProgramName::getProgramNameArray() -{ - auto pgmFileArray = programFile->readProgramFileArray(); - programNameArray = Util::vecCopyOfRange(pgmFileArray, getProgramNameStart(), - getProgramNameEnd()); - return programNameArray; -} - -std::string ProgramName::getProgramNameASCII() -{ - std::string programNameASCII; - auto bytes = getProgramNameArray(); - for (char c : bytes) - { - if (c == 0x00) - { - break; - } - programNameASCII.push_back(c); - } - if (programNameASCII.length() > 16) - { - programNameASCII = programNameASCII.substr(0, 16); - } - return programNameASCII; -} diff --git a/src/main/file/pgmreader/ProgramName.hpp b/src/main/file/pgmreader/ProgramName.hpp deleted file mode 100644 index e43f3884a..000000000 --- a/src/main/file/pgmreader/ProgramName.hpp +++ /dev/null @@ -1,39 +0,0 @@ -#pragma once - -#include -#include - -namespace mpc -{ - namespace file - { - namespace pgmreader - { - - class ProgramFileReader; - - class ProgramName - { - - public: - int sampleNamesSize{0}; - - std::vector programNameArray{}; - - private: - ProgramFileReader *programFile{}; - - public: - int getSampleNamesSize(); - int getProgramNameStart(); - int getProgramNameEnd(); - std::vector getProgramNameArray(); - - std::string getProgramNameASCII(); - - ProgramName(ProgramFileReader *programFile); - }; - - } // namespace pgmreader - } // namespace file -} // namespace mpc diff --git a/src/main/file/pgmreader/SoundNames.cpp b/src/main/file/pgmreader/SoundNames.cpp deleted file mode 100644 index e6bb67060..000000000 --- a/src/main/file/pgmreader/SoundNames.cpp +++ /dev/null @@ -1,53 +0,0 @@ -#include "file/pgmreader/SoundNames.hpp" - -#include "file/pgmreader/PgmHeader.hpp" -#include "file/pgmreader/ProgramFileReader.hpp" - -#include "Util.hpp" - -using namespace mpc::file::pgmreader; - -SoundNames::SoundNames(ProgramFileReader *programFile) -{ - this->programFile = programFile; -} - -int SoundNames::getSampleNamesSize() const -{ - int sampleNamesSize = programFile->getHeader()->getSoundCount() * 17; - return sampleNamesSize; -} - -std::vector SoundNames::getSampleNamesArray() -{ - auto sampleNamesSize = getSampleNamesSize(); - auto pgmFileArray = programFile->readProgramFileArray(); - sampleNamesArray = - Util::vecCopyOfRange(pgmFileArray, 4, 4 + sampleNamesSize + 2); - return sampleNamesArray; -} - -std::string SoundNames::getSampleName(int sampleNumber) -{ - std::string sampleNameString; - auto h = programFile->getHeader(); - if (sampleNumber < h->getSoundCount()) - { - sampleNamesArray = getSampleNamesArray(); - auto sampleName = Util::vecCopyOfRange( - sampleNamesArray, sampleNumber * 17, sampleNumber * 17 + 16); - for (char c : sampleName) - { - if (c == 0x00) - { - break; - } - sampleNameString.push_back(c); - } - } - else - { - sampleNameString = "OFF"; - } - return sampleNameString; -} diff --git a/src/main/file/pgmreader/SoundNames.hpp b/src/main/file/pgmreader/SoundNames.hpp deleted file mode 100644 index 0a31f3ae3..000000000 --- a/src/main/file/pgmreader/SoundNames.hpp +++ /dev/null @@ -1,35 +0,0 @@ -#pragma once - -#include -#include - -namespace mpc -{ - namespace file - { - namespace pgmreader - { - - class ProgramFileReader; - - class SoundNames - { - - public: - std::vector sampleNamesArray{}; - - private: - ProgramFileReader *programFile{}; - - public: - int getSampleNamesSize() const; - std::vector getSampleNamesArray(); - - std::string getSampleName(int sampleNumber); - - SoundNames(ProgramFileReader *programFile); - }; - - } // namespace pgmreader - } // namespace file -} // namespace mpc diff --git a/src/main/file/pgmwriter/MidiNotes.cpp b/src/main/file/pgmwriter/MidiNotes.cpp deleted file mode 100644 index 47afd6012..000000000 --- a/src/main/file/pgmwriter/MidiNotes.cpp +++ /dev/null @@ -1,183 +0,0 @@ -#include "file/pgmwriter/MidiNotes.hpp" - -#include "sampler/NoteParameters.hpp" -#include "sampler/Program.hpp" - -using namespace mpc::file::pgmwriter; - -MidiNotes::MidiNotes(sampler::Program *program, - const std::vector &snConvTable) -{ - midiNotesArray = std::vector(1601); - - for (int i = 0; i < 64; i++) - { - auto nn = program->getNoteParameters(i + 35); - - if (nn->getSoundIndex() == -1) - { - setSampleSelect(i, 255); - } - else - { - setSampleSelect(i, snConvTable[nn->getSoundIndex()]); - } - - setSoundGenerationMode(i, nn->getSoundGenerationMode()); - setVelocityRangeLower(i, nn->getVelocityRangeLower()); - setAlsoPlayUse1(i, nn->getOptionalNoteA()); - setVelocityRangeUpper(i, nn->getVelocityRangeUpper()); - setAlsoPlayUse2(i, nn->getOptionalNoteB()); - setVoiceOverlapMode(i, nn->getVoiceOverlapMode()); - setMuteAssign1(i, - nn->getMuteAssignA() == 34 ? 0 : nn->getMuteAssignA()); - setMuteAssign2(i, - nn->getMuteAssignB() == 34 ? 0 : nn->getMuteAssignB()); - setTune(i, static_cast(nn->getTune())); - setAttack(i, nn->getAttack()); - setDecay(i, nn->getDecay()); - setDecayMode(i, nn->getDecayMode()); - setCutoff(i, nn->getFilterFrequency()); - setResonance(i, nn->getFilterResonance()); - setVelEnvToFiltAtt(i, nn->getFilterAttack()); - setVelEnvToFiltDec(i, nn->getFilterDecay()); - setVelEnvToFiltAmt(i, nn->getFilterEnvelopeAmount()); - setVelocityToLevel(i, nn->getVeloToLevel()); - setVelocityToAttack(i, nn->getVelocityToAttack()); - setVelocityToStart(i, nn->getVelocityToStart()); - setVelocityToCutoff(i, nn->getVelocityToFilterFrequency()); - setSliderParameter(i, nn->getSliderParameterNumber()); - setVelocityToPitch(i, nn->getVelocityToPitch()); - } - - midiNotesArray[1600] = 6; -} - -void MidiNotes::setSampleSelect(int midiNote, int sampleNumber) -{ - char sampleSelect = sampleNumber; - midiNotesArray[midiNote * 25 + 0] = sampleSelect; -} - -void MidiNotes::setSoundGenerationMode(int midiNote, int soundGenerationMode) -{ - midiNotesArray[midiNote * 25 + 1] = soundGenerationMode; -} - -void MidiNotes::setVelocityRangeLower(int midiNote, int velocityRangeLower) -{ - midiNotesArray[midiNote * 25 + 2] = velocityRangeLower; -} - -void MidiNotes::setAlsoPlayUse1(int midiNote, int alsoPlayUse1) -{ - midiNotesArray[midiNote * 25 + 3] = alsoPlayUse1; -} - -void MidiNotes::setVelocityRangeUpper(int midiNote, int velocityRangeUpper) -{ - midiNotesArray[midiNote * 25 + 4] = velocityRangeUpper; -} - -void MidiNotes::setAlsoPlayUse2(int midiNote, int alsoPlayUse2) -{ - midiNotesArray[midiNote * 25 + 5] = alsoPlayUse2; -} - -void MidiNotes::setVoiceOverlapMode( - int midiNote, const sampler::VoiceOverlapMode voiceOverlapMode) -{ - midiNotesArray[midiNote * 25 + 6] = static_cast(voiceOverlapMode); -} - -void MidiNotes::setMuteAssign1(int midiNote, int muteAssign1) -{ - midiNotesArray[midiNote * 25 + 7] = muteAssign1; -} - -void MidiNotes::setMuteAssign2(int midiNote, int muteAssign2) -{ - midiNotesArray[midiNote * 25 + 8] = muteAssign2; -} - -void MidiNotes::setTune(int midiNote, int16_t tune) -{ - auto startPos = midiNote * 25 + 9; - setShort(midiNotesArray, startPos, tune); -} - -void MidiNotes::setAttack(int midiNote, int attack) -{ - midiNotesArray[midiNote * 25 + 11] = attack; -} - -void MidiNotes::setDecay(int midiNote, int decay) -{ - midiNotesArray[midiNote * 25 + 12] = decay; -} - -void MidiNotes::setDecayMode(int midiNote, int decayMode) -{ - midiNotesArray[midiNote * 25 + 13] = decayMode; -} - -void MidiNotes::setCutoff(int midiNote, int cutoff) -{ - midiNotesArray[midiNote * 25 + 14] = cutoff; -} - -void MidiNotes::setResonance(int midiNote, int resonance) -{ - midiNotesArray[midiNote * 25 + 15] = resonance; -} - -void MidiNotes::setVelEnvToFiltAtt(int midiNote, int velEnvToFiltAtt) -{ - midiNotesArray[midiNote * 25 + 16] = velEnvToFiltAtt; -} - -void MidiNotes::setVelEnvToFiltDec(int midiNote, int velEnvToFiltDec) -{ - midiNotesArray[midiNote * 25 + 17] = velEnvToFiltDec; -} - -void MidiNotes::setVelEnvToFiltAmt(int midiNote, int velEnvToFiltAmt) -{ - midiNotesArray[midiNote * 25 + 18] = velEnvToFiltAmt; -} - -void MidiNotes::setVelocityToLevel(int midiNote, int velocityToLevel) -{ - midiNotesArray[midiNote * 25 + 19] = velocityToLevel; -} - -void MidiNotes::setVelocityToAttack(int midiNote, int velocityToAttack) -{ - midiNotesArray[midiNote * 25 + 20] = velocityToAttack; -} - -void MidiNotes::setVelocityToStart(int midiNote, int velocityToStart) -{ - midiNotesArray[midiNote * 25 + 21] = velocityToStart; -} - -void MidiNotes::setVelocityToCutoff(int midiNote, int velocityToCutoff) -{ - midiNotesArray[midiNote * 25 + 22] = velocityToCutoff; -} - -void MidiNotes::setSliderParameter(int midiNote, int sliderParameter) -{ - midiNotesArray[midiNote * 25 + 23] = sliderParameter; -} - -void MidiNotes::setVelocityToPitch(int midiNote, int velocityToPitch) -{ - midiNotesArray[midiNote * 25 + 24] = velocityToPitch; -} - -void MidiNotes::setShort(std::vector &ca, int offset, int16_t s) const -{ - ca[offset] = s; - ca[offset + 1] = s >> 8; -} diff --git a/src/main/file/pgmwriter/MidiNotes.hpp b/src/main/file/pgmwriter/MidiNotes.hpp deleted file mode 100644 index 8278dfe1f..000000000 --- a/src/main/file/pgmwriter/MidiNotes.hpp +++ /dev/null @@ -1,56 +0,0 @@ -#pragma once - -#include - -#ifdef __linux__ -#include -#endif // __linux__ - -#include "sampler/VoiceOverlapMode.hpp" - -namespace mpc::sampler -{ - class Program; -} - -namespace mpc::file::pgmwriter -{ - - class MidiNotes - { - - public: - std::vector midiNotesArray; - - private: - void setSampleSelect(int midiNote, int sampleNumber); - void setSoundGenerationMode(int midiNote, int soundGenerationMode); - void setVelocityRangeLower(int midiNote, int velocityRangeLower); - void setAlsoPlayUse1(int midiNote, int alsoPlayUse1); - void setVelocityRangeUpper(int midiNote, int velocityRangeUpper); - void setAlsoPlayUse2(int midiNote, int alsoPlayUse2); - void setVoiceOverlapMode(int midiNote, const sampler::VoiceOverlapMode); - void setMuteAssign1(int midiNote, int muteAssign1); - void setMuteAssign2(int midiNote, int muteAssign2); - void setTune(int midiNote, int16_t tune); - void setAttack(int midiNote, int attack); - void setDecay(int midiNote, int decay); - void setDecayMode(int midiNote, int decayMode); - void setCutoff(int midiNote, int cutoff); - void setResonance(int midiNote, int resonance); - void setVelEnvToFiltAtt(int midiNote, int velEnvToFiltAtt); - void setVelEnvToFiltDec(int midiNote, int velEnvToFiltDec); - void setVelEnvToFiltAmt(int midiNote, int velEnvToFiltAmt); - void setVelocityToLevel(int midiNote, int velocityToLevel); - void setVelocityToAttack(int midiNote, int velocityToAttack); - void setVelocityToStart(int midiNote, int velocityToStart); - void setVelocityToCutoff(int midiNote, int velocityToCutoff); - void setSliderParameter(int midiNote, int sliderParameter); - void setVelocityToPitch(int midiNote, int velocityToPitch); - void setShort(std::vector &ca, int offset, int16_t s) const; - - public: - MidiNotes(sampler::Program *program, - const std::vector &snConvTable); - }; -} // namespace mpc::file::pgmwriter diff --git a/src/main/file/pgmwriter/Mixer.cpp b/src/main/file/pgmwriter/Mixer.cpp deleted file mode 100644 index ad375f626..000000000 --- a/src/main/file/pgmwriter/Mixer.cpp +++ /dev/null @@ -1,66 +0,0 @@ -#include "file/pgmwriter/Mixer.hpp" - -#include "engine/IndivFxMixer.hpp" -#include "engine/StereoMixer.hpp" -#include "sampler/Pad.hpp" -#include "sampler/Program.hpp" - -using namespace mpc::file::pgmwriter; -using namespace mpc::sampler; - -Mixer::Mixer(Program *program) -{ - mixerArray = std::vector(384 + 3); - - for (int i = 0; i < 64; i++) - { - auto noteParameters = program->getNoteParameters(i + 35); - auto smc = noteParameters->getStereoMixer(); - auto ifmc = noteParameters->getIndivFxMixer(); - setVolume(i, smc->getLevel()); - setPan(i, smc->getPanning()); - setVolumeIndividual(i, ifmc->getVolumeIndividualOut()); - setOutput(i, ifmc->getOutput()); - setEffectsSendLevel(i, ifmc->getFxSendLevel()); - setEffectsOutput(i, ifmc->getFxPath()); - } - - mixerArray[384] = 0; - mixerArray[385] = 64; - mixerArray[386] = 0; -} - -std::vector Mixer::getMixerArray() -{ - return mixerArray; -} - -void Mixer::setEffectsOutput(int pad, int effectsOutputNumber) -{ - mixerArray[pad * 6 + 0] = (char)effectsOutputNumber; -} - -void Mixer::setVolume(int pad, int volume) -{ - mixerArray[pad * 6 + 1] = (char)volume; -} - -void Mixer::setPan(int pad, int pan) -{ - mixerArray[pad * 6 + 2] = (char)pan; -} - -void Mixer::setVolumeIndividual(int pad, int volumeIndividual) -{ - mixerArray[pad * 6 + 3] = (char)volumeIndividual; -} - -void Mixer::setOutput(int pad, int output) -{ - mixerArray[pad * 6 + 4] = (char)output; -} - -void Mixer::setEffectsSendLevel(int pad, int effectsSendLevel) -{ - mixerArray[pad * 6 + 5] = (char)effectsSendLevel; -} diff --git a/src/main/file/pgmwriter/Mixer.hpp b/src/main/file/pgmwriter/Mixer.hpp deleted file mode 100644 index e28cdefcd..000000000 --- a/src/main/file/pgmwriter/Mixer.hpp +++ /dev/null @@ -1,40 +0,0 @@ -#pragma once - -#include - -namespace mpc -{ - - namespace sampler - { - class Program; - } - - namespace file - { - namespace pgmwriter - { - - class Mixer - { - - public: - std::vector mixerArray{}; - - std::vector getMixerArray(); - - private: - void setEffectsOutput(int pad, int effectsOutputNumber); - void setVolume(int pad, int volume); - void setPan(int pad, int pan); - void setVolumeIndividual(int pad, int volumeIndividual); - void setOutput(int pad, int output); - void setEffectsSendLevel(int pad, int effectsSendLevel); - - public: - Mixer(sampler::Program *program); - }; - - } // namespace pgmwriter - } // namespace file -} // namespace mpc diff --git a/src/main/file/pgmwriter/PWHeader.cpp b/src/main/file/pgmwriter/PWHeader.cpp deleted file mode 100644 index 60feefb6c..000000000 --- a/src/main/file/pgmwriter/PWHeader.cpp +++ /dev/null @@ -1,35 +0,0 @@ -#include "PWHeader.hpp" - -#include "MpcSpecs.hpp" -#include "file/ByteUtil.hpp" - -#include - -using namespace mpc::file::pgmwriter; - -PWHeader::PWHeader(int numberOfSamples) -{ - headerArray = std::vector(4); - writeMagic(); - setSoundCount(numberOfSamples); - headerArray[3] = 0; -} - -void PWHeader::writeMagic() -{ - headerArray[0] = PGM_HEADER_MAGIC[0]; - headerArray[1] = PGM_HEADER_MAGIC[1]; -} - -void PWHeader::setSoundCount(const uint16_t soundCount) -{ - assert(soundCount <= Mpc2000XlSpecs::MAX_SOUND_COUNT_IN_MEMORY); - const auto soundCountBytes = ByteUtil::ushort2bytes(soundCount); - headerArray[2] = soundCountBytes[0]; - headerArray[3] = soundCountBytes[1]; -} - -std::vector PWHeader::getHeaderArray() -{ - return headerArray; -} diff --git a/src/main/file/pgmwriter/PWHeader.hpp b/src/main/file/pgmwriter/PWHeader.hpp deleted file mode 100644 index 176c2cf54..000000000 --- a/src/main/file/pgmwriter/PWHeader.hpp +++ /dev/null @@ -1,22 +0,0 @@ -#pragma once - -#include -#include - -namespace mpc::file::pgmwriter -{ - class PWHeader - { - const std::vector PGM_HEADER_MAGIC{0x07, 0x04}; - std::vector headerArray; - - void writeMagic(); - - void setSoundCount(uint16_t soundCount); - - public: - std::vector getHeaderArray(); - - PWHeader(int numberOfSamples); - }; -} // namespace mpc::file::pgmwriter diff --git a/src/main/file/pgmwriter/PWSlider.cpp b/src/main/file/pgmwriter/PWSlider.cpp deleted file mode 100644 index 380f79501..000000000 --- a/src/main/file/pgmwriter/PWSlider.cpp +++ /dev/null @@ -1,87 +0,0 @@ -#include "file/pgmwriter/PWSlider.hpp" - -#include "sampler/Program.hpp" - -using namespace mpc::file::pgmwriter; - -Slider::Slider(sampler::Program *program) -{ - sliderArray = std::vector(15); - if (program->getSlider()->getNote() == 34) - { - setMidiNoteAssign(0); - } - else - { - setMidiNoteAssign(program->getSlider()->getNote()); - } - setTuneLow(program->getSlider()->getTuneLowRange()); - setTuneHigh(program->getSlider()->getTuneHighRange()); - setDecayLow(program->getSlider()->getDecayLowRange()); - setDecayHigh(program->getSlider()->getDecayHighRange()); - setAttackLow(program->getSlider()->getAttackLowRange()); - setAttackHigh(program->getSlider()->getAttackHighRange()); - setFilterLow(program->getSlider()->getFilterLowRange()); - setFilterHigh(program->getSlider()->getFilterHighRange()); - setControlChange(program->getSlider()->getControlChange()); - sliderArray[10] = 35; - sliderArray[11] = 64; - sliderArray[12] = 0; - sliderArray[13] = 25; - sliderArray[14] = 0; -} - -std::vector Slider::getSliderArray() -{ - return sliderArray; -} - -void Slider::setMidiNoteAssign(int midiNoteAssign) -{ - sliderArray[0] = midiNoteAssign; -} - -void Slider::setTuneLow(int tuneLow) -{ - sliderArray[1] = tuneLow; -} - -void Slider::setTuneHigh(int tuneHigh) -{ - sliderArray[2] = tuneHigh; -} - -void Slider::setDecayLow(int decayLow) -{ - sliderArray[3] = decayLow; -} - -void Slider::setDecayHigh(int decayHigh) -{ - sliderArray[4] = decayHigh; -} - -void Slider::setAttackLow(int attackLow) -{ - sliderArray[5] = attackLow; -} - -void Slider::setAttackHigh(int attackHigh) -{ - sliderArray[6] = attackHigh; -} - -void Slider::setFilterLow(int filterLow) -{ - sliderArray[7] = filterLow; -} - -void Slider::setFilterHigh(int filterHigh) -{ - sliderArray[8] = filterHigh; -} - -void Slider::setControlChange(int controlChange) -{ - sliderArray[9] = controlChange; -} diff --git a/src/main/file/pgmwriter/PWSlider.hpp b/src/main/file/pgmwriter/PWSlider.hpp deleted file mode 100644 index 96209b110..000000000 --- a/src/main/file/pgmwriter/PWSlider.hpp +++ /dev/null @@ -1,44 +0,0 @@ -#pragma once - -#include - -namespace mpc -{ - - namespace sampler - { - class Program; - } - - namespace file - { - namespace pgmwriter - { - - class Slider - { - - public: - std::vector sliderArray{}; - - std::vector getSliderArray(); - - private: - void setMidiNoteAssign(int midiNoteAssign); - void setTuneLow(int tuneLow); - void setTuneHigh(int tuneHigh); - void setDecayLow(int decayLow); - void setDecayHigh(int decayHigh); - void setAttackLow(int attackLow); - void setAttackHigh(int attackHigh); - void setFilterLow(int filterLow); - void setFilterHigh(int filterHigh); - void setControlChange(int controlChange); - - public: - Slider(sampler::Program *program); - }; - - } // namespace pgmwriter - } // namespace file -} // namespace mpc diff --git a/src/main/file/pgmwriter/Pads.cpp b/src/main/file/pgmwriter/Pads.cpp deleted file mode 100644 index 244ca9408..000000000 --- a/src/main/file/pgmwriter/Pads.cpp +++ /dev/null @@ -1,53 +0,0 @@ -#include "file/pgmwriter/Pads.hpp" - -#include "sampler/Pad.hpp" -#include "sampler/Program.hpp" - -#include "file/ByteUtil.hpp" - -using namespace mpc::file::pgmwriter; - -Pads::Pads(sampler::Program *program) -{ - - padsArray = std::vector(264); - for (int i = 0; i < 64; i++) - { - auto nn = program->getPad(i)->getNote(); - if (nn == 34) - { - setPadMidiNote(i, 0); - } - else - { - setPadMidiNote(i, nn); - } - } - auto paddingBytes = getFxBoardSettings(); - for (int i = 0; i < 200; i++) - { - padsArray[i + 64] = paddingBytes[i]; - } -} -std::string Pads::fxBoardSettings = - "02004800D0070000630114081DFC323302323C08050A1414320000020F19000541141E0105" - "0000056300F4FF0C0000000000000002004F014F0100424F0100424F01004232006328003C" - "0000D0070000630114081DFC323302323C08050A1414320000020F19000541141E01050000" - "056300F4FF0C0000000000000002004F014F0100424F0100424F01004232006328003C0000" - "04000C000000320023003E335A3214000000320023003E335A3214000000320023003E335A" - "3214000000320023003E335A321400"; - -std::vector Pads::getPadsArray() -{ - return padsArray; -} - -void Pads::setPadMidiNote(int pad, int padMidiNote) -{ - padsArray[pad] = padMidiNote; -} - -std::vector Pads::getFxBoardSettings() -{ - return ByteUtil::hexToBytes(fxBoardSettings); -} diff --git a/src/main/file/pgmwriter/Pads.hpp b/src/main/file/pgmwriter/Pads.hpp deleted file mode 100644 index a5525ec2b..000000000 --- a/src/main/file/pgmwriter/Pads.hpp +++ /dev/null @@ -1,33 +0,0 @@ -#pragma once - -#include -#include - -namespace mpc::sampler -{ - class Program; -} - -namespace mpc::file::pgmwriter -{ - class Pads - { - - std::vector padsArray; - static std::string - fxBoardSettings; // dummy -- not implemented in any way -- just to - // fool anybody who's trying to read the product of - // this parser - - public: - std::vector getPadsArray(); - - private: - void setPadMidiNote(int pad, int padMidiNote); - - public: - static std::vector getFxBoardSettings(); - - Pads(sampler::Program *program); - }; -} // namespace mpc::file::pgmwriter diff --git a/src/main/file/pgmwriter/PgmName.cpp b/src/main/file/pgmwriter/PgmName.cpp deleted file mode 100644 index f0c5c0c22..000000000 --- a/src/main/file/pgmwriter/PgmName.cpp +++ /dev/null @@ -1,29 +0,0 @@ -#include "file/pgmwriter/PgmName.hpp" - -#include "sampler/Program.hpp" - -using namespace mpc::file::pgmwriter; - -PgmName::PgmName(sampler::Program *program) -{ - auto ca = program->getName(); - std::vector temp(17); - for (int i = 0; i < ca.length(); i++) - { - temp[i] = ca[i]; - } - - for (int i = ca.length(); i < 16; i++) - { - temp[i] = 32; - } - - temp[16] = 0; - programNameArray = temp; - sampleNamesSize = program->getNumberOfSamples() * 17; -} - -std::vector PgmName::getPgmNameArray() -{ - return programNameArray; -} diff --git a/src/main/file/pgmwriter/PgmName.hpp b/src/main/file/pgmwriter/PgmName.hpp deleted file mode 100644 index 7d88e27c2..000000000 --- a/src/main/file/pgmwriter/PgmName.hpp +++ /dev/null @@ -1,33 +0,0 @@ -#pragma once - -#include - -namespace mpc -{ - - namespace sampler - { - class Program; - } - - namespace file - { - namespace pgmwriter - { - - class PgmName - { - - public: - int sampleNamesSize{0}; - - std::vector programNameArray{}; - - std::vector getPgmNameArray(); - - PgmName(sampler::Program *program); - }; - - } // namespace pgmwriter - } // namespace file -} // namespace mpc diff --git a/src/main/file/pgmwriter/PgmWriter.cpp b/src/main/file/pgmwriter/PgmWriter.cpp deleted file mode 100644 index f5b7491c3..000000000 --- a/src/main/file/pgmwriter/PgmWriter.cpp +++ /dev/null @@ -1,66 +0,0 @@ -#include "file/pgmwriter/PgmWriter.hpp" - -#include "file/pgmwriter/PWHeader.hpp" -#include "file/pgmwriter/MidiNotes.hpp" -#include "file/pgmwriter/Mixer.hpp" -#include "file/pgmwriter/Pads.hpp" -#include "file/pgmwriter/PgmName.hpp" -#include "file/pgmwriter/SampleNames.hpp" -#include "file/pgmwriter/PWSlider.hpp" - -using namespace mpc::file::pgmwriter; - -PgmWriter::PgmWriter(sampler::Program *program, - const std::weak_ptr &sampler) -{ - SampleNames pwSampleNames(program, sampler); - sampleNames = pwSampleNames.getSampleNamesArray(); - - PWHeader pwHeader(pwSampleNames.getNumberOfSamples()); - header = pwHeader.getHeaderArray(); - - PgmName pwPgmName(program); - pgmName = pwPgmName.getPgmNameArray(); - - Slider pwSlider(program); - slider = pwSlider.getSliderArray(); - - MidiNotes pwMidiNotes(program, pwSampleNames.getSnConvTable()); - midiNotes = pwMidiNotes.midiNotesArray; - - Mixer pwMixer(program); - mixer = pwMixer.getMixerArray(); - - Pads pwPads(program); - pads = pwPads.getPadsArray(); -} - -std::vector PgmWriter::get() const -{ - std::vector> charArrayChunks(7); - charArrayChunks[0] = header; - charArrayChunks[1] = sampleNames; - charArrayChunks[2] = pgmName; - charArrayChunks[3] = slider; - charArrayChunks[4] = midiNotes; - charArrayChunks[5] = mixer; - charArrayChunks[6] = pads; - auto programFileSize = 0; - - for (auto ca : charArrayChunks) - { - programFileSize += ca.size(); - } - - std::vector result(programFileSize); - int counter = 0; - - for (auto ca : charArrayChunks) - { - for (auto c : ca) - { - result[counter++] = c; - } - } - return result; -} diff --git a/src/main/file/pgmwriter/PgmWriter.hpp b/src/main/file/pgmwriter/PgmWriter.hpp deleted file mode 100644 index caa77343d..000000000 --- a/src/main/file/pgmwriter/PgmWriter.hpp +++ /dev/null @@ -1,32 +0,0 @@ -#pragma once - -#include -#include - -namespace mpc::sampler -{ - class Sampler; - class Program; -} // namespace mpc::sampler - -namespace mpc::file::pgmwriter -{ - - class PgmWriter - { - - std::vector header; - std::vector pgmName; - std::vector sampleNames; - std::vector slider; - std::vector midiNotes; - std::vector mixer; - std::vector pads; - - public: - std::vector get() const; - - PgmWriter(sampler::Program *program, - const std::weak_ptr &sampler); - }; -} // namespace mpc::file::pgmwriter diff --git a/src/main/file/pgmwriter/SampleNames.cpp b/src/main/file/pgmwriter/SampleNames.cpp deleted file mode 100644 index c87bd8745..000000000 --- a/src/main/file/pgmwriter/SampleNames.cpp +++ /dev/null @@ -1,89 +0,0 @@ -#include "file/pgmwriter/SampleNames.hpp" - -#include "sampler/Program.hpp" -#include "sampler/Sampler.hpp" - -#include - -using namespace mpc::file::pgmwriter; - -SampleNames::SampleNames(sampler::Program *program, - const std::weak_ptr &sampler) -{ - std::vector list; - - for (auto &nn : program->getNotesParameters()) - { - if (nn->getSoundIndex() != -1) - { - list.push_back(nn->getSoundIndex()); - } - } - - std::set sampleNamesSet; - std::vector finalNames; - - auto lSampler = sampler.lock(); - - for (int i = 0; i < list.size(); i++) - { - if (sampleNamesSet.emplace(lSampler->getSoundName(list[i])).second) - { - finalNames.push_back(lSampler->getSoundName(list[i])); - } - } - - for (int i = 0; i < lSampler->getSoundCount(); i++) - { - int j = -1; - for (int k = 0; k < finalNames.size(); k++) - { - if (finalNames[k] == lSampler->getSoundName(i)) - { - j = k; - break; - } - } - - snConvTable.push_back(j); - } - numberOfSamples = finalNames.size(); - sampleNamesArray = std::vector(numberOfSamples * 17 + 2); - int counter = 0; - for (auto &s : finalNames) - { - setSampleName(counter++, s); - } - sampleNamesArray[(int)sampleNamesArray.size() - 2] = 30; - sampleNamesArray[(int)sampleNamesArray.size() - 1] = 0; -} - -std::vector SampleNames::getSampleNamesArray() -{ - return sampleNamesArray; -} - -void SampleNames::setSampleName(int sampleNumber, const std::string &name) -{ - for (int i = 0; i < name.length(); i++) - { - sampleNamesArray[i + sampleNumber * 17] = name[i]; - } - - for (int i = name.length(); i < 16; i++) - { - sampleNamesArray[i + sampleNumber * 17] = 32; - } - - sampleNamesArray[16 + sampleNumber * 17] = 0; -} - -int SampleNames::getNumberOfSamples() const -{ - return numberOfSamples; -} - -std::vector SampleNames::getSnConvTable() -{ - return snConvTable; -} diff --git a/src/main/file/pgmwriter/SampleNames.hpp b/src/main/file/pgmwriter/SampleNames.hpp deleted file mode 100644 index 3b3236e3a..000000000 --- a/src/main/file/pgmwriter/SampleNames.hpp +++ /dev/null @@ -1,44 +0,0 @@ -#pragma once - -#include -#include -#include - -namespace mpc -{ - - namespace sampler - { - class Sampler; - class Program; - } // namespace sampler - - namespace file - { - namespace pgmwriter - { - - class SampleNames - { - - std::vector sampleNamesArray{}; - int numberOfSamples{}; - std::vector snConvTable{}; - - public: - std::vector getSampleNamesArray(); - - private: - void setSampleName(int sampleNumber, const std::string &name); - - public: - int getNumberOfSamples() const; - std::vector getSnConvTable(); - - SampleNames(sampler::Program *program, - const std::weak_ptr &sampler); - }; - - } // namespace pgmwriter - } // namespace file -} // namespace mpc diff --git a/src/main/file/sndreader/SndHeaderReader.cpp b/src/main/file/sndreader/SndHeaderReader.cpp deleted file mode 100644 index e9f60a2c9..000000000 --- a/src/main/file/sndreader/SndHeaderReader.cpp +++ /dev/null @@ -1,106 +0,0 @@ -#include "SndHeaderReader.hpp" - -#include "file/sndreader/SndReader.hpp" - -#include "Util.hpp" -#include "file/ByteUtil.hpp" -#include "StrUtil.hpp" - -using namespace mpc::file::sndreader; - -SndHeaderReader::SndHeaderReader(SndReader *sndReader) -{ - auto sndFileArray = sndReader->getSndFileArray(); - headerArray = Util::vecCopyOfRange(sndFileArray, 0, 42); -} - -std::vector &SndHeaderReader::getHeaderArray() -{ - return headerArray; -} - -bool SndHeaderReader::hasValidId() const -{ - int i = headerArray[0]; - int j = headerArray[1]; - - return i == 1 && j < 5; -} - -std::string SndHeaderReader::getName() const -{ - std::string name; - - for (int i = 2; i < 18; i++) - { - if (headerArray[i] == 0x00) - { - break; - } - name.push_back(headerArray[i]); - } - - return StrUtil::trim(name); -} - -int SndHeaderReader::getLevel() const -{ - return headerArray[19]; -} - -int SndHeaderReader::getTune() const -{ - return headerArray[20]; -} - -bool SndHeaderReader::isMono() const -{ - auto channels = headerArray[21]; - return channels == 0; -} - -int SndHeaderReader::getStart() const -{ - auto startArray = Util::vecCopyOfRange(headerArray, 22, 26); - auto start = ByteUtil::bytes2uint(startArray); - return start; -} - -int SndHeaderReader::getEnd() const -{ - auto endArray = Util::vecCopyOfRange(headerArray, 26, 30); - auto end = ByteUtil::bytes2uint(endArray); - return end; -} - -int SndHeaderReader::getNumberOfFrames() const -{ - auto numberOfFramesArray = Util::vecCopyOfRange(headerArray, 30, 34); - int numberOfFrames = ByteUtil::bytes2uint(numberOfFramesArray); - return numberOfFrames; -} - -int SndHeaderReader::getLoopLength() const -{ - auto loopLengthArray = Util::vecCopyOfRange(headerArray, 34, 38); - auto loopLength = ByteUtil::bytes2uint(loopLengthArray); - return loopLength; -} - -bool SndHeaderReader::isLoopEnabled() const -{ - int loop = headerArray[38]; - return loop == 1; -} - -int SndHeaderReader::getNumberOfBeats() const -{ - return headerArray[39]; -} - -int SndHeaderReader::getSampleRate() const -{ - auto rateArray = Util::vecCopyOfRange(headerArray, 40, 42); - auto rate = ByteUtil::bytes2ushort(rateArray); - return rate; -} diff --git a/src/main/file/sndreader/SndHeaderReader.hpp b/src/main/file/sndreader/SndHeaderReader.hpp deleted file mode 100644 index adbebb15e..000000000 --- a/src/main/file/sndreader/SndHeaderReader.hpp +++ /dev/null @@ -1,34 +0,0 @@ -#pragma once - -#include -#include - -namespace mpc::file::sndreader -{ - class SndReader; -} - -namespace mpc::file::sndreader -{ - class SndHeaderReader - { - std::vector headerArray = std::vector(42); - - public: - std::vector &getHeaderArray(); - bool hasValidId() const; - std::string getName() const; - int getLevel() const; - int getTune() const; - bool isMono() const; - int getStart() const; - int getEnd() const; - int getNumberOfFrames() const; - int getLoopLength() const; - bool isLoopEnabled() const; - int getNumberOfBeats() const; - int getSampleRate() const; - - SndHeaderReader(SndReader *sndReader); - }; -} // namespace mpc::file::sndreader diff --git a/src/main/file/sndreader/SndReader.cpp b/src/main/file/sndreader/SndReader.cpp deleted file mode 100644 index d5067a471..000000000 --- a/src/main/file/sndreader/SndReader.cpp +++ /dev/null @@ -1,118 +0,0 @@ -#include "file/sndreader/SndReader.hpp" - -#include "disk/MpcFile.hpp" -#include "file/sndreader/SndHeaderReader.hpp" - -#include - -#include "file/ByteUtil.hpp" - -using namespace mpc::file::sndreader; -using namespace mpc::sampleops; - -SndReader::SndReader(disk::MpcFile *soundFile) - : SndReader(soundFile->getBytes()) -{ -} - -SndReader::SndReader(const std::vector &loadBytes) -{ - sndFileArray = loadBytes; - sndHeaderReader = std::make_shared(this); -} - -bool SndReader::isHeaderValid() const -{ - return sndHeaderReader->hasValidId(); -} - -std::string SndReader::getName() const -{ - return sndHeaderReader->getName(); -} - -bool SndReader::isMono() const -{ - return sndHeaderReader->isMono(); -} - -int SndReader::getSampleRate() const -{ - return sndHeaderReader->getSampleRate(); -} - -int SndReader::getLevel() const -{ - return sndHeaderReader->getLevel(); -} - -int SndReader::getStart() const -{ - return sndHeaderReader->getStart(); -} - -int SndReader::getEnd() const -{ - return sndHeaderReader->getEnd(); -} - -int SndReader::getLoopLength() const -{ - return sndHeaderReader->getLoopLength(); -} - -bool SndReader::isLoopEnabled() const -{ - return sndHeaderReader->isLoopEnabled(); -} - -int SndReader::getTune() const -{ - return sndHeaderReader->getTune(); -} - -int SndReader::getNumberOfBeats() const -{ - return sndHeaderReader->getNumberOfBeats(); -} - -void SndReader::readData(const std::shared_ptr> &dest) -{ - int length = sndHeaderReader->getNumberOfFrames(); - - bool mono = sndHeaderReader->isMono(); - - if (!mono) - { - length *= 2; - } - - dest->clear(); - dest->resize(length); - - auto shorts = ByteUtil::bytesToShorts( - std::vector(sndFileArray.begin() + 42, sndFileArray.end())); - - for (int i = 0; i < length; ++i) - { - short value = shorts[i]; - auto f = short_to_float(value); - - if (f < -1) - { - f = -1.0f; - } - - if (f > 1) - { - f = 1.0f; - } - - (*dest)[i] = f; - } -} - -std::vector &SndReader::getSndFileArray() -{ - return sndFileArray; -} diff --git a/src/main/file/sndreader/SndReader.hpp b/src/main/file/sndreader/SndReader.hpp deleted file mode 100644 index 533a0250d..000000000 --- a/src/main/file/sndreader/SndReader.hpp +++ /dev/null @@ -1,47 +0,0 @@ -#pragma once -#include "Util.hpp" - -#include -#include -#include - -namespace mpc::disk -{ - class MpcFile; -} - -namespace mpc::file::sndreader -{ - class SndHeaderReader; -} - -namespace mpc::file::sndreader -{ - class SndReader - { - - std::vector sndFileArray; - std::shared_ptr sndHeaderReader; - - public: - std::string getName() const; - bool isHeaderValid() const; - bool isMono() const; - - int getSampleRate() const; - int getLevel() const; - int getStart() const; - int getEnd() const; - int getLoopLength() const; - bool isLoopEnabled() const; - int getTune() const; - int getNumberOfBeats() const; - void readData(const std::shared_ptr> &); - - std::vector &getSndFileArray(); - - SndReader(disk::MpcFile *); - - SndReader(const std::vector &loadBytes); - }; -} // namespace mpc::file::sndreader diff --git a/src/main/file/sndwriter/SndHeaderWriter.cpp b/src/main/file/sndwriter/SndHeaderWriter.cpp deleted file mode 100644 index 219868486..000000000 --- a/src/main/file/sndwriter/SndHeaderWriter.cpp +++ /dev/null @@ -1,101 +0,0 @@ -#include "file/sndwriter/SndHeaderWriter.hpp" -#include "file/ByteUtil.hpp" - -using namespace mpc::file::sndwriter; - -SndHeaderWriter::SndHeaderWriter() -{ - setFirstTwoBytes(); -} - -std::vector &SndHeaderWriter::getHeaderArray() -{ - return headerArray; -} - -void SndHeaderWriter::setFirstTwoBytes() -{ - headerArray[0] = 1; - headerArray[1] = 4; -} - -void SndHeaderWriter::setName(const std::string &name) -{ - std::vector nameArray(16); - - for (int i = 0; i < name.length(); i++) - { - nameArray[i] = name[i]; - } - - for (int i = name.length(); i < nameArray.size(); i++) - { - nameArray[i] = 32; - } - - for (int i = 0; i < nameArray.size(); i++) - { - headerArray[i + 2] = nameArray[i]; - } -} - -void SndHeaderWriter::setLevel(int i) -{ - headerArray[19] = static_cast(i); -} - -void SndHeaderWriter::setTune(int i) -{ - headerArray[20] = static_cast(i); -} - -void SndHeaderWriter::setMono(bool b) -{ - headerArray[21] = static_cast(b ? 0 : 1); -} - -void SndHeaderWriter::setStart(int i) -{ - putLE(22, i); -} - -void SndHeaderWriter::setEnd(int i) -{ - putLE(26, i); -} - -void SndHeaderWriter::setFrameCount(int i) -{ - putLE(30, i); -} - -void SndHeaderWriter::setLoopLength(int i) -{ - putLE(34, i); -} - -void SndHeaderWriter::setLoopEnabled(bool b) -{ - headerArray[38] = b ? 1 : 0; -} - -void SndHeaderWriter::setBeatCount(int i) -{ - headerArray[39] = i; -} - -void SndHeaderWriter::setSampleRate(int i) -{ - auto shortBytes = ByteUtil::short2bytes(i - 65536); - headerArray[40] = shortBytes[0]; - headerArray[41] = shortBytes[1]; -} - -void SndHeaderWriter::putLE(int offset, int value) -{ - auto ba = ByteUtil::uint2bytes(value); - for (auto j = 0; j < ba.size(); j++) - { - headerArray[j + offset] = ba[j]; - } -} diff --git a/src/main/file/sndwriter/SndHeaderWriter.hpp b/src/main/file/sndwriter/SndHeaderWriter.hpp deleted file mode 100644 index c6a55b484..000000000 --- a/src/main/file/sndwriter/SndHeaderWriter.hpp +++ /dev/null @@ -1,38 +0,0 @@ -#pragma once - -#include -#include - -namespace mpc::file::sndwriter -{ - class SndWriter; -} - -namespace mpc::file::sndwriter -{ - class SndHeaderWriter - { - std::vector headerArray = std::vector(42); - - public: - std::vector &getHeaderArray(); - void setFirstTwoBytes(); - void setName(const std::string &name); - void setLevel(int i); - void setTune(int i); - void setMono(bool b); - void setStart(int i); - void setEnd(int i); - void setFrameCount(int i); - void setLoopLength(int i); - void setLoopEnabled(bool b); - void setBeatCount(int i); - void setSampleRate(int i); - - private: - void putLE(int offset, int value); - - public: - SndHeaderWriter(); - }; -} // namespace mpc::file::sndwriter diff --git a/src/main/file/sndwriter/SndWriter.cpp b/src/main/file/sndwriter/SndWriter.cpp deleted file mode 100644 index 515324a24..000000000 --- a/src/main/file/sndwriter/SndWriter.cpp +++ /dev/null @@ -1,119 +0,0 @@ -#include "file/sndwriter/SndWriter.hpp" - -#include "file/sndwriter/SndHeaderWriter.hpp" -#include "sampler/Sound.hpp" - -#include "file/ByteUtil.hpp" - -#include - -using namespace mpc::file::sndwriter; - -SndWriter::SndWriter(sampler::Sound *sound) -{ - this->sound = sound; - sndHeaderWriter = std::make_shared(); - setValues(); -} -const int SndWriter::HEADER_SIZE; - -void SndWriter::setValues() -{ - setName(sound->getName()); - setMono(sound->isMono()); - setFramesCount(sound->getFrameCount()); - setSampleRate(sound->getSampleRate()); - setLevel(sound->getSndLevel()); - setStart(sound->getStart()); - setEnd(sound->getEnd()); - setLoopLength(sound->getEnd() - sound->getLoopTo()); - setLoopEnabled(sound->isLoopEnabled()); - setTune(sound->getTune()); - setBeatCount(sound->getBeatCount()); - setSampleData(*sound->getSampleData(), sound->isMono()); -} - -void SndWriter::setName(const std::string &s) const -{ - sndHeaderWriter->setName(s); -} - -void SndWriter::setMono(bool b) const -{ - sndHeaderWriter->setMono(b); -} - -void SndWriter::setFramesCount(int i) const -{ - sndHeaderWriter->setFrameCount(i); -} - -void SndWriter::setSampleRate(int i) const -{ - sndHeaderWriter->setSampleRate(i); -} - -void SndWriter::setLevel(int i) const -{ - sndHeaderWriter->setLevel(i); -} - -void SndWriter::setStart(int i) const -{ - sndHeaderWriter->setStart(i); -} - -void SndWriter::setEnd(int i) const -{ - sndHeaderWriter->setEnd(i); -} - -void SndWriter::setLoopLength(int i) const -{ - sndHeaderWriter->setLoopLength(i); -} - -void SndWriter::setLoopEnabled(bool b) const -{ - sndHeaderWriter->setLoopEnabled(b); -} - -void SndWriter::setTune(int i) const -{ - sndHeaderWriter->setTune(i); -} - -void SndWriter::setBeatCount(int i) const -{ - sndHeaderWriter->setBeatCount(i); -} - -void SndWriter::setSampleData(const std::vector &fa, bool mono) -{ - sndFileArray = std::vector(HEADER_SIZE + fa.size() * 2); - auto frames = mono ? fa.size() : fa.size() * 0.5; - sndHeaderWriter->setFrameCount(frames); - - std::vector buffer(2); - auto sPos = 0; - - auto bytePos = HEADER_SIZE; - for (int i = 0; i < fa.size(); i++) - { - auto shortres = sampleops::mean_normalized_float_to_short(fa[sPos++]); - buffer = ByteUtil::short2bytes(shortres); - sndFileArray[bytePos++] = buffer[0]; - sndFileArray[bytePos++] = buffer[1]; - } -} - -std::vector &SndWriter::getSndFileArray() -{ - auto header = sndHeaderWriter->getHeaderArray(); - for (int i = 0; i < header.size(); i++) - { - sndFileArray[i] = header[i]; - } - - return sndFileArray; -} diff --git a/src/main/file/sndwriter/SndWriter.hpp b/src/main/file/sndwriter/SndWriter.hpp deleted file mode 100644 index 5085e528f..000000000 --- a/src/main/file/sndwriter/SndWriter.hpp +++ /dev/null @@ -1,46 +0,0 @@ -#pragma once - -#include -#include -#include - -namespace mpc::sampler -{ - class Sound; -} - -namespace mpc::file::sndwriter -{ - class SndHeaderWriter; -} - -namespace mpc::file::sndwriter -{ - class SndWriter - { - - static const int HEADER_SIZE = 42; - std::shared_ptr sndHeaderWriter; - sampler::Sound *sound; - std::vector sndFileArray; - - void setValues(); - void setName(const std::string &s) const; - void setMono(bool b) const; - void setFramesCount(int i) const; - void setSampleRate(int i) const; - void setLevel(int i) const; - void setStart(int i) const; - void setEnd(int i) const; - void setLoopLength(int i) const; - void setLoopEnabled(bool b) const; - void setTune(int i) const; - void setBeatCount(int i) const; - void setSampleData(const std::vector &fa, bool b); - - public: - std::vector &getSndFileArray(); - - SndWriter(sampler::Sound *sound); - }; -} // namespace mpc::file::sndwriter diff --git a/src/main/lcdgui/AllScreens.hpp b/src/main/lcdgui/AllScreens.hpp index cc592e306..1844c6b74 100644 --- a/src/main/lcdgui/AllScreens.hpp +++ b/src/main/lcdgui/AllScreens.hpp @@ -88,7 +88,12 @@ #include "lcdgui/screens/window/VeloPitchScreen.hpp" #include "lcdgui/screens/window/DirectoryScreen.hpp" #include "lcdgui/screens/window/LoadAProgramScreen.hpp" +#include "lcdgui/screens/window/LoadASetScreen.hpp" +#include "lcdgui/screens/window/ConversionTableScreen.hpp" +#include "lcdgui/screens/window/LoadASetReplaceAddScreen.hpp" +#include "lcdgui/screens/window/LoadASetSoundScreen.hpp" #include "lcdgui/screens/window/LoadASequenceScreen.hpp" +#include "lcdgui/screens/window/LoadASequencePlayScreen.hpp" #include "lcdgui/screens/window/LoadASequenceFromAllScreen.hpp" #include "lcdgui/screens/window/LoadASoundScreen.hpp" #include "lcdgui/screens/window/LoadApsFileScreen.hpp" diff --git a/src/main/lcdgui/ScreenComponent.cpp b/src/main/lcdgui/ScreenComponent.cpp index 1ef32d049..35a10b247 100644 --- a/src/main/lcdgui/ScreenComponent.cpp +++ b/src/main/lcdgui/ScreenComponent.cpp @@ -18,7 +18,6 @@ #include "lcdgui/screens/dialog/MetronomeSoundScreen.hpp" #include "lcdgui/screens/dialog/MidiOutputMonitorScreen.hpp" #include "lcdgui/screens/DrumScreen.hpp" - #include using namespace mpc::lcdgui; diff --git a/src/main/lcdgui/ScreenRegistry.hpp b/src/main/lcdgui/ScreenRegistry.hpp index 63cd79f9f..005a1fc41 100644 --- a/src/main/lcdgui/ScreenRegistry.hpp +++ b/src/main/lcdgui/ScreenRegistry.hpp @@ -98,7 +98,12 @@ X(screens, SaveScreen, "save") \ X(screens::window, DirectoryScreen, "directory") \ X(screens::window, LoadAProgramScreen, "load-a-program") \ + X(screens::window, LoadASetScreen, "load-a-set") \ + X(screens::window, ConversionTableScreen, "conversion-table") \ + X(screens::window, LoadASetReplaceAddScreen, "load-a-set-replace-add") \ + X(screens::window, LoadASetSoundScreen, "load-a-set-sound") \ X(screens::window, LoadASequenceScreen, "load-a-sequence") \ + X(screens::window, LoadASequencePlayScreen, "load-a-sequence-play") \ X(screens::window, LoadASequenceFromAllScreen, "load-a-sequence-from-all") \ X(screens::window, LoadASoundScreen, "load-a-sound") \ X(screens::window, LoadApsFileScreen, "load-aps-file") \ diff --git a/src/main/lcdgui/screens/AssignScreen.cpp b/src/main/lcdgui/screens/AssignScreen.cpp index 273df2a5e..ae9062342 100644 --- a/src/main/lcdgui/screens/AssignScreen.cpp +++ b/src/main/lcdgui/screens/AssignScreen.cpp @@ -15,6 +15,7 @@ AssignScreen::AssignScreen(Mpc &mpc, const int layerIndex) void AssignScreen::open() { + syncSliderParameterToAssignedNote(); displayAssignNote(); displayParameter(); displayHighRange(); @@ -47,7 +48,13 @@ void AssignScreen::turnWheel(int i) } else if (focusedFieldName == "parameter") { - slider->setParameter(slider->getParameter() + i); + const auto nextParameter = getSelectedSliderParameter() + i; + if (slider->getNote() != mpc::NoDrumNoteAssigned) + { + program->getNoteParameters(slider->getNote()) + ->setSliderParameterNumber(nextParameter); + } + slider->setParameter(nextParameter); } else if (focusedFieldName == "highrange") { @@ -202,7 +209,11 @@ void AssignScreen::update(Observable *observable, Message message) if (msg == "assignnote") { + syncSliderParameterToAssignedNote(); displayAssignNote(); + displayParameter(); + displayHighRange(); + displayLowRange(); } else if (msg == "parameter") { @@ -223,3 +234,29 @@ void AssignScreen::update(Observable *observable, Message message) displayAssignNv(); } } + +int AssignScreen::getSelectedSliderParameter() const +{ + const auto program = getProgramOrThrow(); + const auto slider = program->getSlider(); + const auto note = slider->getNote(); + + if (note == mpc::NoDrumNoteAssigned) + { + return slider->getParameter(); + } + + return program->getNoteParameters(note)->getSliderParameterNumber(); +} + +void AssignScreen::syncSliderParameterToAssignedNote() const +{ + const auto program = getProgramOrThrow(); + const auto slider = program->getSlider(); + const auto selectedParameter = getSelectedSliderParameter(); + + if (slider->getParameter() != selectedParameter) + { + slider->setParameter(selectedParameter); + } +} diff --git a/src/main/lcdgui/screens/AssignScreen.hpp b/src/main/lcdgui/screens/AssignScreen.hpp index f21de45e8..5eb216ff0 100644 --- a/src/main/lcdgui/screens/AssignScreen.hpp +++ b/src/main/lcdgui/screens/AssignScreen.hpp @@ -15,6 +15,8 @@ namespace mpc::lcdgui::screens void update(Observable *o, Message message) override; private: + int getSelectedSliderParameter() const; + void syncSliderParameterToAssignedNote() const; void displayAssignNote() const; void displayParameter() const; void displayHighRange() const; diff --git a/src/main/lcdgui/screens/LoadScreen.cpp b/src/main/lcdgui/screens/LoadScreen.cpp index 2b2bc06a0..8b4687d1e 100644 --- a/src/main/lcdgui/screens/LoadScreen.cpp +++ b/src/main/lcdgui/screens/LoadScreen.cpp @@ -228,7 +228,12 @@ void LoadScreen::function(const int i) { openScreenById(ScreenId::LoadAProgramScreen); } - else if (StrUtil::eqIgnoreCase(ext, ".mid")) + else if (StrUtil::eqIgnoreCase(ext, ".set")) + { + openScreenById(ScreenId::LoadASetScreen); + } + else if (StrUtil::eqIgnoreCase(ext, ".mid") || + StrUtil::eqIgnoreCase(ext, ".seq")) { openScreenById(ScreenId::LoadASequenceScreen); } diff --git a/src/main/lcdgui/screens/PgmAssignScreen.cpp b/src/main/lcdgui/screens/PgmAssignScreen.cpp index 05c6ad73c..bcd09cc73 100644 --- a/src/main/lcdgui/screens/PgmAssignScreen.cpp +++ b/src/main/lcdgui/screens/PgmAssignScreen.cpp @@ -8,6 +8,7 @@ #include "lcdgui/Label.hpp" #include "sampler/Pad.hpp" #include "sampler/Program.hpp" +#include "sampler/SoundGenerationMode.hpp" #include "sampler/Sampler.hpp" #include "sequencer/Bus.hpp" @@ -318,7 +319,7 @@ void PgmAssignScreen::turnWheel(const int i) else if (focusedFieldName == "mode") { selectedNoteParameters->setSoundGenMode( - selectedNoteParameters->getSoundGenerationMode() + i); + mpc::sampler::toRaw(selectedNoteParameters->getSoundGenerationMode()) + i); } else if (focusedFieldName == "velocity-range-lower") { @@ -440,7 +441,8 @@ void PgmAssignScreen::displayPadNote() const void PgmAssignScreen::displaySoundGenerationMode() { - int soundGenerationMode = -1; + auto soundGenerationMode = mpc::sampler::SoundGenerationMode::Normal; + bool hasSoundGenerationMode = false; const auto program = getProgramOrThrow(); const auto selectedNoteParameters = program->getNoteParameters( @@ -449,9 +451,11 @@ void PgmAssignScreen::displaySoundGenerationMode() if (selectedNoteParameters != nullptr) { soundGenerationMode = selectedNoteParameters->getSoundGenerationMode(); - findField("mode")->setText(soundGenerationModes[soundGenerationMode]); + hasSoundGenerationMode = true; + findField("mode")->setText( + soundGenerationModes[mpc::sampler::toRaw(soundGenerationMode)]); - if (soundGenerationMode != 0) + if (soundGenerationMode != mpc::sampler::SoundGenerationMode::Normal) { findLabel("velocity-range-lower")->Hide(true); findField("velocity-range-lower")->Hide(true); @@ -467,7 +471,8 @@ void PgmAssignScreen::displaySoundGenerationMode() displayOptionalNoteB(); } - if (soundGenerationMode == 2 || soundGenerationMode == 3) + if (soundGenerationMode == mpc::sampler::SoundGenerationMode::VelocitySwitch || + soundGenerationMode == mpc::sampler::SoundGenerationMode::DecaySwitch) { findLabel("optional-note-a")->setText("over: , use:"); findLabel("optional-note-b")->setText("over: , use:"); @@ -484,8 +489,8 @@ void PgmAssignScreen::displaySoundGenerationMode() } } - if (selectedNoteParameters == nullptr || soundGenerationMode == -1 || - soundGenerationMode == 0) + if (selectedNoteParameters == nullptr || !hasSoundGenerationMode || + soundGenerationMode == mpc::sampler::SoundGenerationMode::Normal) { findLabel("velocity-range-lower")->Hide(true); findField("velocity-range-lower")->Hide(true); diff --git a/src/main/lcdgui/screens/SecondSeqScreen.hpp b/src/main/lcdgui/screens/SecondSeqScreen.hpp index 342554648..53a0043c6 100644 --- a/src/main/lcdgui/screens/SecondSeqScreen.hpp +++ b/src/main/lcdgui/screens/SecondSeqScreen.hpp @@ -6,11 +6,6 @@ namespace mpc::disk class AllLoader; } -namespace mpc::file::all -{ - class AllSequencer; -} - namespace mpc::sequencer { class Sequencer; @@ -43,6 +38,5 @@ namespace mpc::lcdgui::screens friend class sequencer::Sequencer; friend class disk::AllLoader; - friend class file::all::AllSequencer; }; } // namespace mpc::lcdgui::screens diff --git a/src/main/lcdgui/screens/SyncScreen.cpp b/src/main/lcdgui/screens/SyncScreen.cpp index a35ca71ce..05e625ccf 100644 --- a/src/main/lcdgui/screens/SyncScreen.cpp +++ b/src/main/lcdgui/screens/SyncScreen.cpp @@ -222,3 +222,28 @@ int SyncScreen::getOut() const { return out; } + +int SyncScreen::getInput() const +{ + return in; +} + +int SyncScreen::getShiftEarly() const +{ + return shiftEarly; +} + +bool SyncScreen::isSendMMCEnabled() const +{ + return sendMMCEnabled; +} + +int SyncScreen::getFrameRate() const +{ + return frameRate; +} + +bool SyncScreen::isReceiveMMCEnabled() const +{ + return receiveMMCEnabled; +} diff --git a/src/main/lcdgui/screens/SyncScreen.hpp b/src/main/lcdgui/screens/SyncScreen.hpp index 5adeaf9cf..bdab9ec66 100644 --- a/src/main/lcdgui/screens/SyncScreen.hpp +++ b/src/main/lcdgui/screens/SyncScreen.hpp @@ -7,12 +7,6 @@ namespace mpc::audiomidi class MidiInput; } // namespace mpc::audiomidi -namespace mpc::file::all -{ - class MidiSyncMisc; - class Misc; -} // namespace mpc::file::all - namespace mpc::disk { class AllLoader; @@ -30,6 +24,11 @@ namespace mpc::lcdgui::screens int getModeOut() const; int getModeIn() const; int getOut() const; + int getInput() const; + int getShiftEarly() const; + bool isSendMMCEnabled() const; + int getFrameRate() const; + bool isReceiveMMCEnabled() const; SyncScreen(Mpc &, int layerIndex); @@ -73,7 +72,5 @@ namespace mpc::lcdgui::screens friend class audiomidi::EventHandler; friend class audiomidi::MidiInput; friend class disk::AllLoader; - friend class file::all::MidiSyncMisc; - friend class file::all::Misc; }; } // namespace mpc::lcdgui::screens diff --git a/src/main/lcdgui/screens/UserScreen.hpp b/src/main/lcdgui/screens/UserScreen.hpp index b04075320..9cea5939b 100644 --- a/src/main/lcdgui/screens/UserScreen.hpp +++ b/src/main/lcdgui/screens/UserScreen.hpp @@ -59,6 +59,7 @@ namespace mpc::lcdgui::screens int getPgm() const; int getVelo() const; sequencer::TimeSignature getTimeSig() const; + int8_t getTrackStatus() const; private: void displayTempo() const; @@ -92,8 +93,6 @@ namespace mpc::lcdgui::screens void setVelo(int i); void setTimeSig(int num, int den); - int8_t getTrackStatus() const; - void resetPreferences(); friend class InitScreen; diff --git a/src/main/lcdgui/screens/VmpcSettingsScreen.cpp b/src/main/lcdgui/screens/VmpcSettingsScreen.cpp index 7c50b9611..ebafb9c92 100644 --- a/src/main/lcdgui/screens/VmpcSettingsScreen.cpp +++ b/src/main/lcdgui/screens/VmpcSettingsScreen.cpp @@ -2,6 +2,8 @@ #include "Mpc.hpp" #include "lcdgui/LayeredScreen.hpp" #include "lcdgui/screens/window/TimingCorrectScreen.hpp" +#include "sampler/Pad.hpp" +#include "sampler/Sampler.hpp" using namespace mpc::lcdgui::screens; @@ -99,6 +101,8 @@ void VmpcSettingsScreen::setInitialPadMapping(const int i) } initialPadMapping = i; + // Future PROGRAM/MASTER init-pad-assign operations copy from this cache. + *mpc.getSampler()->getInitMasterPadAssign() = sampler::Pad::getPadNotes(mpc); displayInitialPadMapping(); } diff --git a/src/main/lcdgui/screens/window/ConversionTableScreen.cpp b/src/main/lcdgui/screens/window/ConversionTableScreen.cpp new file mode 100644 index 000000000..11bde2ff1 --- /dev/null +++ b/src/main/lcdgui/screens/window/ConversionTableScreen.cpp @@ -0,0 +1,90 @@ +#include "ConversionTableScreen.hpp" + +#include "Mpc.hpp" +#include "file/kaitai/Mpc60SetPreview.hpp" +#include "StrUtil.hpp" +#include "lcdgui/screens/window/LoadASetScreen.hpp" +#include "sampler/Pad.hpp" +#include "sampler/Sampler.hpp" + +#include + +using namespace mpc::lcdgui::screens::window; +using namespace mpc::lcdgui::screens; + +ConversionTableScreen::ConversionTableScreen(Mpc &mpc, const int layerIndex) + : ScreenComponent(mpc, "conversion-table", layerIndex) +{ +} + +void ConversionTableScreen::open() +{ + displayPad(); + displayBecomesNote(); +} + +void ConversionTableScreen::turnWheel(const int i) +{ + const auto loadASetScreen = mpc.screens->get(); + + if (const auto focusedFieldName = getFocusedFieldNameOrThrow(); + focusedFieldName == "mpc60-pad") + { + selectedMpc60PadIndex = static_cast(std::clamp( + static_cast(selectedMpc60PadIndex) + i, 0, + 33)); + displayPad(); + displayBecomesNote(); + } + else if (focusedFieldName == "becomes-note") + { + const auto note = loadASetScreen->getConversionTargetNote(selectedMpc60PadIndex); + loadASetScreen->setConversionTargetNote( + selectedMpc60PadIndex, + DrumNoteNumber(std::clamp(static_cast(note) + i, + static_cast(MinDrumNoteNumber), + static_cast(MaxDrumNoteNumber)))); + displayBecomesNote(); + } +} + +void ConversionTableScreen::displayPad() const +{ + auto padLabel = + file::kaitai::Mpc60SetPreview::mpc60PadName(selectedMpc60PadIndex); + std::replace(padLabel.begin(), padLabel.end(), '_', ' '); + padLabel = StrUtil::padRight(StrUtil::toUpper(padLabel), " ", 10) + "(" + + file::kaitai::Mpc60SetPreview::mpc60SourceSlotLabel( + selectedMpc60PadIndex) + + ")"; + findField("mpc60-pad")->setText(StrUtil::padRight(padLabel, " ", 18)); +} + +void ConversionTableScreen::displayBecomesNote() const +{ + const auto loadASetScreen = mpc.screens->get(); + const auto note = loadASetScreen->getConversionTargetNote(selectedMpc60PadIndex); + const auto program = getProgram(); + const auto padIndices = + program ? program->getPadIndicesFromNote(note) + : std::vector{}; + const auto padName = padIndices.empty() + ? std::string("OFF") + : sampler.lock()->getPadName( + static_cast(padIndices.front())); + findField("becomes-note")->setText(std::to_string(note) + "/" + padName); +} + +void ConversionTableScreen::function(const int i) +{ + switch (i) + { + case 3: + openScreenById(ScreenId::LoadScreen); + break; + case 4: + openScreenById(ScreenId::LoadASetReplaceAddScreen); + break; + default:; + } +} diff --git a/src/main/lcdgui/screens/window/ConversionTableScreen.hpp b/src/main/lcdgui/screens/window/ConversionTableScreen.hpp new file mode 100644 index 000000000..f3cbf65cc --- /dev/null +++ b/src/main/lcdgui/screens/window/ConversionTableScreen.hpp @@ -0,0 +1,23 @@ +#pragma once +#include "lcdgui/ScreenComponent.hpp" + +#include + +namespace mpc::lcdgui::screens::window +{ + class ConversionTableScreen final : public ScreenComponent + { + public: + ConversionTableScreen(Mpc &mpc, int layerIndex); + + void open() override; + void turnWheel(int i) override; + void function(int i) override; + + private: + size_t selectedMpc60PadIndex = 0; + + void displayPad() const; + void displayBecomesNote() const; + }; +} // namespace mpc::lcdgui::screens::window diff --git a/src/main/lcdgui/screens/window/LoadASequenceFromAllScreen.cpp b/src/main/lcdgui/screens/window/LoadASequenceFromAllScreen.cpp index f4f01e7a1..a011cb853 100644 --- a/src/main/lcdgui/screens/window/LoadASequenceFromAllScreen.cpp +++ b/src/main/lcdgui/screens/window/LoadASequenceFromAllScreen.cpp @@ -47,7 +47,7 @@ void LoadASequenceFromAllScreen::function(const int i) { if (i == 3) { - openScreenById(ScreenId::Mpc2000XlAllFileScreen); + openScreenById(ScreenId::LoadScreen); } else if (i == 4) { diff --git a/src/main/lcdgui/screens/window/LoadASequencePlayScreen.cpp b/src/main/lcdgui/screens/window/LoadASequencePlayScreen.cpp new file mode 100644 index 000000000..ff108052d --- /dev/null +++ b/src/main/lcdgui/screens/window/LoadASequencePlayScreen.cpp @@ -0,0 +1,46 @@ +#include "LoadASequencePlayScreen.hpp" + +#include "Mpc.hpp" +#include "StrUtil.hpp" +#include "lcdgui/Label.hpp" +#include "sequencer/Sequencer.hpp" +#include "sequencer/Transport.hpp" + +using namespace mpc::lcdgui::screens::window; + +LoadASequencePlayScreen::LoadASequencePlayScreen(Mpc &mpc, const int layerIndex) + : ScreenComponent(mpc, "load-a-sequence-play", layerIndex) +{ + findBackground()->setBackgroundName("step-timing-correct"); + + addReactiveBinding({[&] + { + return sequencer.lock() + ->getTransport() + ->getTickPositionGuiPresentation(); + }, + [&](auto) + { + displayPosition(); + }}); +} + +void LoadASequencePlayScreen::open() +{ + displayPosition(); +} + +void LoadASequencePlayScreen::displayPosition() const +{ + const auto transport = sequencer.lock()->getTransport(); + + findLabel("position")->setText( + StrUtil::padLeft( + std::to_string(transport->getCurrentBarIndex() + 1), "0", 3) + + "." + + StrUtil::padLeft( + std::to_string(transport->getCurrentBeatIndex() + 1), "0", 2) + + "." + + StrUtil::padLeft( + std::to_string(transport->getCurrentClockNumber()), "0", 2)); +} diff --git a/src/main/lcdgui/screens/window/LoadASequencePlayScreen.hpp b/src/main/lcdgui/screens/window/LoadASequencePlayScreen.hpp new file mode 100644 index 000000000..57f42a188 --- /dev/null +++ b/src/main/lcdgui/screens/window/LoadASequencePlayScreen.hpp @@ -0,0 +1,17 @@ +#pragma once + +#include "lcdgui/ScreenComponent.hpp" + +namespace mpc::lcdgui::screens::window +{ + class LoadASequencePlayScreen final : public ScreenComponent + { + public: + LoadASequencePlayScreen(Mpc &mpc, int layerIndex); + + void open() override; + + private: + void displayPosition() const; + }; +} // namespace mpc::lcdgui::screens::window diff --git a/src/main/lcdgui/screens/window/LoadASequenceScreen.cpp b/src/main/lcdgui/screens/window/LoadASequenceScreen.cpp index 3ad705368..ece070493 100644 --- a/src/main/lcdgui/screens/window/LoadASequenceScreen.cpp +++ b/src/main/lcdgui/screens/window/LoadASequenceScreen.cpp @@ -7,7 +7,10 @@ #include "lcdgui/Label.hpp" #include "lcdgui/screens/LoadScreen.hpp" #include "sequencer/Sequencer.hpp" +#include "sequencer/SequencerMessage.hpp" +#include "sequencer/SequencerStateManager.hpp" #include "sequencer/Sequence.hpp" +#include "sequencer/Transport.hpp" using namespace mpc::lcdgui::screens::window; @@ -18,17 +21,32 @@ LoadASequenceScreen::LoadASequenceScreen(Mpc &mpc, const int layerIndex) void LoadASequenceScreen::open() { + if (mpc.getLayeredScreen()->isPreviousScreen( + {ScreenId::LoadASequencePlayScreen})) + { + displayLoadInto(); + return; + } + + originalSelectedSequenceIndex = sequencer.lock()->getSelectedSequenceIndex(); + previewingTempSequence = false; + const auto loadScreen = mpc.screens->get(); - const auto midFile = loadScreen->getSelectedFile(); + const auto sequenceFile = loadScreen->getSelectedFile(); + const auto extension = sequenceFile->getExtension(); - if (!StrUtil::eqIgnoreCase(midFile->getExtension(), ".mid")) + if (!StrUtil::eqIgnoreCase(extension, ".mid") && + !StrUtil::eqIgnoreCase(extension, ".seq")) { return; } - if (const sequence_or_error parsedMidFile = - mpc.getDisk()->readMid2(midFile); - parsedMidFile.has_value()) + const sequence_or_error parsedSequenceFile = + StrUtil::eqIgnoreCase(extension, ".seq") + ? mpc.getDisk()->readSeq2(sequenceFile) + : mpc.getDisk()->readMid2(sequenceFile); + + if (parsedSequenceFile.has_value()) { auto usedSeqs = sequencer.lock()->getUsedSequenceIndexes(); int index; @@ -61,15 +79,24 @@ void LoadASequenceScreen::function(const int i) { switch (i) { + case 2: + startTempPreview(); + break; case 3: + stopTempPreviewAndRestoreSelection(); mpc.getLayeredScreen()->closeCurrentScreen(); sequencer.lock()->getSequence(TempSequenceIndex)->init(0); break; case 4: { const auto lockedSequencer = sequencer.lock(); + if (lockedSequencer->getTransport()->isPlaying()) + { + lockedSequencer->getTransport()->stop(); + } lockedSequencer->copySequence(TempSequenceIndex, loadInto); lockedSequencer->setSelectedSequenceIndex(loadInto, true); + previewingTempSequence = false; openScreenById(ScreenId::SequencerScreen); break; } @@ -77,6 +104,50 @@ void LoadASequenceScreen::function(const int i) } } +void LoadASequenceScreen::startTempPreview() +{ + auto lockedSequencer = sequencer.lock(); + auto stateManager = lockedSequencer->getStateManager(); + auto transport = lockedSequencer->getTransport(); + + if (transport->isPlaying()) + { + transport->stop(); + } + + stateManager->enqueue( + mpc::sequencer::SetSelectedSequenceIndex{TempSequenceIndex, true}); + transport->play(true); + previewingTempSequence = true; + openScreenById(ScreenId::LoadASequencePlayScreen); +} + +void LoadASequenceScreen::stopTempPreviewAndRestoreSelection() +{ + auto lockedSequencer = sequencer.lock(); + auto transport = lockedSequencer->getTransport(); + + if (transport->isPlaying()) + { + transport->stop(); + } + + if (!previewingTempSequence) + { + return; + } + + lockedSequencer->getStateManager()->enqueue( + mpc::sequencer::SetSelectedSequenceIndex{ + originalSelectedSequenceIndex, true}); + previewingTempSequence = false; +} + +void LoadASequenceScreen::releasePlayPreview() +{ + stopTempPreviewAndRestoreSelection(); +} + void LoadASequenceScreen::setLoadInto(const SequenceIndex i) { loadInto = std::clamp(i, MinSequenceIndex, MaxSequenceIndex); @@ -93,10 +164,11 @@ void LoadASequenceScreen::displayLoadInto() const void LoadASequenceScreen::displayFile() const { const auto loadScreen = mpc.screens->get(); - const auto midFile = loadScreen->getSelectedFile(); + const auto sequenceFile = loadScreen->getSelectedFile(); findLabel("file")->setText( "File:" + - StrUtil::padRight(StrUtil::toUpper(midFile->getNameWithoutExtension()), + StrUtil::padRight( + StrUtil::toUpper(sequenceFile->getNameWithoutExtension()), " ", 16) + - ".MID"); + StrUtil::toUpper(sequenceFile->getExtension())); } diff --git a/src/main/lcdgui/screens/window/LoadASequenceScreen.hpp b/src/main/lcdgui/screens/window/LoadASequenceScreen.hpp index edca871f6..dff907fdc 100644 --- a/src/main/lcdgui/screens/window/LoadASequenceScreen.hpp +++ b/src/main/lcdgui/screens/window/LoadASequenceScreen.hpp @@ -11,11 +11,17 @@ namespace mpc::lcdgui::screens::window void open() override; void turnWheel(int increment) override; void function(int i) override; + void releasePlayPreview(); SequenceIndex loadInto{MinSequenceIndex}; void setLoadInto(SequenceIndex); private: + SequenceIndex originalSelectedSequenceIndex{MinSequenceIndex}; + bool previewingTempSequence = false; + + void startTempPreview(); + void stopTempPreviewAndRestoreSelection(); void displayLoadInto() const; void displayFile() const; }; diff --git a/src/main/lcdgui/screens/window/LoadASetReplaceAddScreen.cpp b/src/main/lcdgui/screens/window/LoadASetReplaceAddScreen.cpp new file mode 100644 index 000000000..13d762bbc --- /dev/null +++ b/src/main/lcdgui/screens/window/LoadASetReplaceAddScreen.cpp @@ -0,0 +1,127 @@ +#include "LoadASetReplaceAddScreen.hpp" + +#include "Mpc.hpp" +#include "disk/MpcFile.hpp" +#include "file/kaitai/Mpc60SetProgramLoader.hpp" +#include "file/kaitai/Mpc60SetPreview.hpp" +#include "lcdgui/LayeredScreen.hpp" +#include "lcdgui/screens/LoadScreen.hpp" +#include "lcdgui/screens/window/LoadASetScreen.hpp" +#include "performance/PerformanceManager.hpp" +#include "utils/SimpleAction.hpp" + +#include + +using namespace mpc::lcdgui::screens::window; +using namespace mpc::lcdgui::screens; + +LoadASetReplaceAddScreen::LoadASetReplaceAddScreen(Mpc &mpc, + const int layerIndex) + : ScreenComponent(mpc, "load-a-set-replace-add", layerIndex) +{ +} + +LoadASetReplaceAddScreen::~LoadASetReplaceAddScreen() +{ + waitForLoadThread(); +} + +void LoadASetReplaceAddScreen::waitForLoadThread() +{ + if (loadThread.joinable()) + { + loadThread.join(); + } +} + +void LoadASetReplaceAddScreen::open() +{ + displayLoadReplaceSound(); +} + +void LoadASetReplaceAddScreen::turnWheel(const int i) +{ + if (const auto focusedFieldName = getFocusedFieldNameOrThrow(); + focusedFieldName == "load-replace-sound") + { + loadReplaceSameSound = i > 0; + displayLoadReplaceSound(); + } +} + +void LoadASetReplaceAddScreen::function(const int i) +{ + switch (i) + { + case 2: + case 4: + { + const auto loadASetScreen = mpc.screens->get(); + const auto preview = loadASetScreen->getPreview(); + const auto file = mpc.screens->get()->getSelectedFile(); + + if (preview == nullptr || file == nullptr) + { + ls.lock()->showPopupAndThenOpen(ScreenId::LoadScreen, + "Can't read SET", 1000); + break; + } + + const bool clearExisting = i == 2; + auto previewCopy = std::make_shared(*preview); + auto conversionTableCopy = + std::make_shared( + loadASetScreen->getConversionTable()); + const auto layeredScreen = mpc.getLayeredScreen(); + + layeredScreen->showPopup("Loading " + file->getName()); + + waitForLoadThread(); + + loadThread = std::thread( + [this, file, previewCopy, conversionTableCopy, clearExisting, + layeredScreen] + { + const auto loaded = + file::kaitai::Mpc60SetProgramLoader::load( + mpc, file, *previewCopy, *conversionTableCopy, + clearExisting); + + if (!loaded) + { + layeredScreen->postToUiThread(mpc::utils::Task( + [layeredScreen] + { + layeredScreen->showPopupAndThenOpen( + ScreenId::LoadScreen, "Can't read SET", + 1000); + })); + return; + } + + mpc.getPerformanceManager().lock()->enqueueCallback( + mpc::utils::SimpleAction( + [layeredScreen] + { + layeredScreen->postToUiThread(mpc::utils::Task( + [layeredScreen] + { + layeredScreen->openScreenById( + ScreenId::LoadScreen); + })); + })); + }); + break; + } + case 3: + openScreenById(ScreenId::LoadScreen); + break; + default:; + } +} + +void LoadASetReplaceAddScreen::displayLoadReplaceSound() const +{ + findField("load-replace-sound") + ->setText(std::string(loadReplaceSameSound ? "YES" : "NO(FASTER)")); +} diff --git a/src/main/lcdgui/screens/window/LoadASetReplaceAddScreen.hpp b/src/main/lcdgui/screens/window/LoadASetReplaceAddScreen.hpp new file mode 100644 index 000000000..c4b26c961 --- /dev/null +++ b/src/main/lcdgui/screens/window/LoadASetReplaceAddScreen.hpp @@ -0,0 +1,25 @@ +#pragma once +#include "lcdgui/ScreenComponent.hpp" + +#include + +namespace mpc::lcdgui::screens::window +{ + class LoadASetReplaceAddScreen final : public ScreenComponent + { + public: + LoadASetReplaceAddScreen(Mpc &mpc, int layerIndex); + ~LoadASetReplaceAddScreen() override; + void waitForLoadThread(); + + void open() override; + void turnWheel(int i) override; + void function(int i) override; + + private: + std::thread loadThread; + bool loadReplaceSameSound = false; + + void displayLoadReplaceSound() const; + }; +} // namespace mpc::lcdgui::screens::window diff --git a/src/main/lcdgui/screens/window/LoadASetScreen.cpp b/src/main/lcdgui/screens/window/LoadASetScreen.cpp new file mode 100644 index 000000000..c62e1253e --- /dev/null +++ b/src/main/lcdgui/screens/window/LoadASetScreen.cpp @@ -0,0 +1,81 @@ +#include "LoadASetScreen.hpp" + +#include "Mpc.hpp" +#include "file/kaitai/Mpc60SetPreview.hpp" +#include "lcdgui/LayeredScreen.hpp" +#include "lcdgui/screens/LoadScreen.hpp" + +#include + +using namespace mpc::lcdgui::screens::window; +using namespace mpc::lcdgui::screens; + +LoadASetScreen::LoadASetScreen(Mpc &mpc, const int layerIndex) + : ScreenComponent(mpc, "load-a-set", layerIndex) +{ +} + +LoadASetScreen::~LoadASetScreen() = default; + +void LoadASetScreen::open() +{ + preview.reset(); + conversionTable = file::kaitai::Mpc60SetProgramLoader::defaultConversionTable(); + + try + { + preview = std::make_unique( + file::kaitai::Mpc60SetPreviewLoader::loadPreview( + mpc.screens->get()->getSelectedFile())); + } + catch (const std::exception &) + { + const auto layeredScreen = ls.lock(); + layeredScreen->postToUiThread(utils::Task( + [layeredScreen] + { + layeredScreen->showPopupAndThenOpen(ScreenId::LoadScreen, + "Can't read SET", 1000); + })); + } +} + +void LoadASetScreen::function(const int i) +{ + switch (i) + { + case 2: + openScreenById(ScreenId::LoadASetSoundScreen); + break; + case 3: + openScreenById(ScreenId::LoadScreen); + break; + case 4: + openScreenById(ScreenId::ConversionTableScreen); + break; + default:; + } +} + +const mpc::file::kaitai::Mpc60SetPreview *LoadASetScreen::getPreview() const +{ + return preview.get(); +} + +const mpc::file::kaitai::Mpc60SetProgramLoader::ConversionTable & +LoadASetScreen::getConversionTable() const +{ + return conversionTable; +} + +mpc::DrumNoteNumber LoadASetScreen::getConversionTargetNote( + const size_t mpc60PadIndex) const +{ + return conversionTable.at(mpc60PadIndex); +} + +void LoadASetScreen::setConversionTargetNote(const size_t mpc60PadIndex, + const DrumNoteNumber note) +{ + conversionTable.at(mpc60PadIndex) = note; +} diff --git a/src/main/lcdgui/screens/window/LoadASetScreen.hpp b/src/main/lcdgui/screens/window/LoadASetScreen.hpp new file mode 100644 index 000000000..527d823c4 --- /dev/null +++ b/src/main/lcdgui/screens/window/LoadASetScreen.hpp @@ -0,0 +1,32 @@ +#pragma once +#include "file/kaitai/Mpc60SetProgramLoader.hpp" +#include "lcdgui/ScreenComponent.hpp" + +namespace mpc::file::kaitai +{ + struct Mpc60SetPreview; +} + +namespace mpc::lcdgui::screens::window +{ + class LoadASetScreen final : public ScreenComponent + { + public: + LoadASetScreen(Mpc &mpc, int layerIndex); + ~LoadASetScreen() override; + + void open() override; + void function(int i) override; + + const file::kaitai::Mpc60SetPreview *getPreview() const; + const file::kaitai::Mpc60SetProgramLoader::ConversionTable & + getConversionTable() const; + DrumNoteNumber getConversionTargetNote(size_t mpc60PadIndex) const; + void setConversionTargetNote(size_t mpc60PadIndex, DrumNoteNumber note); + + private: + std::unique_ptr preview; + file::kaitai::Mpc60SetProgramLoader::ConversionTable conversionTable = + file::kaitai::Mpc60SetProgramLoader::defaultConversionTable(); + }; +} // namespace mpc::lcdgui::screens::window diff --git a/src/main/lcdgui/screens/window/LoadASetSoundScreen.cpp b/src/main/lcdgui/screens/window/LoadASetSoundScreen.cpp new file mode 100644 index 000000000..cc9418c57 --- /dev/null +++ b/src/main/lcdgui/screens/window/LoadASetSoundScreen.cpp @@ -0,0 +1,189 @@ +#include "LoadASetSoundScreen.hpp" + +#include "Mpc.hpp" +#include "disk/MpcFile.hpp" +#include "file/kaitai/Mpc60SetPreview.hpp" +#include "file/kaitai/Mpc60SetSoundLoader.hpp" +#include "lcdgui/LayeredScreen.hpp" +#include "lcdgui/screens/LoadScreen.hpp" +#include "lcdgui/screens/window/LoadASetScreen.hpp" +#include "sampler/Pad.hpp" +#include "sampler/Sampler.hpp" +#include "StrUtil.hpp" +#include "lcdgui/Label.hpp" + +#include +#include + +using namespace mpc::lcdgui::screens::window; +using namespace mpc::lcdgui::screens; + +LoadASetSoundScreen::LoadASetSoundScreen(Mpc &mpc, const int layerIndex) + : ScreenComponent(mpc, "load-a-set-sound", layerIndex) +{ +} + +void LoadASetSoundScreen::open() +{ + assignedMpc60PadIndexes.clear(); + selectedAssignedPadIndex = 0; + + const auto loadScreen = mpc.screens->get(); + const auto file = loadScreen->getSelectedFile(); + if (file == nullptr) + { + ls.lock()->showPopupAndAwaitInteraction("No SET selected"); + return; + } + + const auto setScreen = mpc.screens->get(); + const auto *preview = setScreen->getPreview(); + + if (preview == nullptr) + { + ls.lock()->showPopupAndAwaitInteraction("Can't read SET"); + return; + } + + assignedMpc60PadIndexes.reserve( + preview->soundDirectoryEntryIndexByMpc60Pad.size()); + for (size_t padIndex = 0; + padIndex < preview->soundDirectoryEntryIndexByMpc60Pad.size(); + ++padIndex) + { + assignedMpc60PadIndexes.push_back(padIndex); + } + + displayMpc60Pad(); + displayFile(); +} + +void LoadASetSoundScreen::turnWheel(const int i) +{ + if (assignedMpc60PadIndexes.empty()) + { + return; + } + + const auto current = static_cast(selectedAssignedPadIndex); + selectedAssignedPadIndex = static_cast(std::clamp( + current + i, 0, + static_cast(assignedMpc60PadIndexes.size() - 1))); + displayMpc60Pad(); + displayFile(); +} + +void LoadASetSoundScreen::displayMpc60Pad() const +{ + if (assignedMpc60PadIndexes.empty()) + { + findField("mpc60-pad")->setText(""); + return; + } + + const auto padIndex = assignedMpc60PadIndexes[selectedAssignedPadIndex]; + auto padName = file::kaitai::Mpc60SetPreview::mpc60PadName(padIndex); + std::replace(padName.begin(), padName.end(), '_', ' '); + padName = StrUtil::toUpper(padName); + const auto sourceSlot = + file::kaitai::Mpc60SetPreview::mpc60SourceSlotLabel(padIndex); + findField("mpc60-pad")->setText( + StrUtil::padRight(padName, " ", 10) + "(" + sourceSlot + ")"); +} + +void LoadASetSoundScreen::displayFile() const +{ + const auto setScreen = mpc.screens->get(); + const auto *preview = setScreen->getPreview(); + const auto entryIndex = getSelectedSoundDirectoryEntryIndex(); + + if (preview == nullptr || !entryIndex) + { + findLabel("file")->setText("File:(no assign)"); + return; + } + + if (*entryIndex >= preview->soundDirectoryEntries.size() || + preview->soundDirectoryEntries[*entryIndex].lengthInSamples == 0) + { + findLabel("file")->setText("File:(no assign)"); + return; + } + + findLabel("file")->setText( + "File:" + + StrUtil::padRight(preview->soundDirectoryEntries[*entryIndex].name, " ", + 16)); +} + +std::optional +LoadASetSoundScreen::getSelectedSoundDirectoryEntryIndex() const +{ + const auto setScreen = mpc.screens->get(); + const auto *preview = setScreen->getPreview(); + + if (preview == nullptr || assignedMpc60PadIndexes.empty()) + { + return std::nullopt; + } + + const auto padIndex = assignedMpc60PadIndexes[selectedAssignedPadIndex]; + const auto entryIndex = + preview->soundDirectoryEntryIndexByMpc60Pad[padIndex]; + + if (entryIndex >= preview->soundDirectoryEntries.size()) + { + return std::nullopt; + } + + return entryIndex; +} + +void LoadASetSoundScreen::function(const int i) +{ + switch (i) + { + case 3: + ls.lock()->closeCurrentScreen(); + break; + case 4: + { + const auto loadScreen = mpc.screens->get(); + const auto file = loadScreen->getSelectedFile(); + + if (file == nullptr) + { + ls.lock()->showPopupAndAwaitInteraction("No SET selected"); + break; + } + + const auto entryIndex = getSelectedSoundDirectoryEntryIndex(); + if (!entryIndex) + { + break; + } + + auto sound = sampler.lock()->addSound(40000); + if (sound == nullptr) + { + break; + } + + const auto soundOrError = + file::kaitai::Mpc60SetSoundLoader::loadSoundDirectoryEntry( + file, *entryIndex, sound); + + if (!soundOrError.has_value()) + { + sampler.lock()->deleteSound(sound); + ls.lock()->showPopupAndAwaitInteraction("Can't read SET"); + break; + } + + const auto msg = "LOADING " + sound->getName(); + ls.lock()->showPopupAndThenOpen(ScreenId::LoadASoundScreen, msg, 300); + break; + } + default:; + } +} diff --git a/src/main/lcdgui/screens/window/LoadASetSoundScreen.hpp b/src/main/lcdgui/screens/window/LoadASetSoundScreen.hpp new file mode 100644 index 000000000..e3ecc8fa1 --- /dev/null +++ b/src/main/lcdgui/screens/window/LoadASetSoundScreen.hpp @@ -0,0 +1,26 @@ +#pragma once +#include "lcdgui/ScreenComponent.hpp" + +#include +#include + +namespace mpc::lcdgui::screens::window +{ + class LoadASetSoundScreen final : public ScreenComponent + { + public: + LoadASetSoundScreen(Mpc &mpc, int layerIndex); + + void open() override; + void turnWheel(int i) override; + void function(int i) override; + + private: + std::vector assignedMpc60PadIndexes; + size_t selectedAssignedPadIndex = 0; + + void displayMpc60Pad() const; + void displayFile() const; + std::optional getSelectedSoundDirectoryEntryIndex() const; + }; +} // namespace mpc::lcdgui::screens::window diff --git a/src/main/lcdgui/screens/window/Mpc2000XlAllFileScreen.cpp b/src/main/lcdgui/screens/window/Mpc2000XlAllFileScreen.cpp index f801d0ce7..adf00e8d4 100644 --- a/src/main/lcdgui/screens/window/Mpc2000XlAllFileScreen.cpp +++ b/src/main/lcdgui/screens/window/Mpc2000XlAllFileScreen.cpp @@ -11,11 +11,37 @@ using namespace mpc::lcdgui::screens::window; using namespace mpc::lcdgui::screens; +namespace +{ + bool isLegacyForeignAllFile(const std::shared_ptr& file) + { + if (!file) + { + return false; + } + + const auto bytes = file->getBytes(); + return bytes.size() >= 2 && + static_cast(bytes[0]) == 0x04 && + (static_cast(bytes[1]) == 0x02 || + static_cast(bytes[1]) == 0x03); + } +} + Mpc2000XlAllFileScreen::Mpc2000XlAllFileScreen(Mpc &mpc, const int layerIndex) : ScreenComponent(mpc, "mpc2000xl-all-file", layerIndex) { } +void Mpc2000XlAllFileScreen::open() +{ + const auto loadScreen = mpc.screens->get(); + const auto bgName = isLegacyForeignAllFile(loadScreen->getSelectedFile()) + ? "mpc3000-all-file" + : "mpc2000xl-all-file"; + findBackground()->setBackgroundName(bgName); +} + void Mpc2000XlAllFileScreen::function(const int i) { const auto loadScreen = mpc.screens->get(); @@ -45,10 +71,10 @@ void Mpc2000XlAllFileScreen::function(const int i) case 4: { const auto selectedFile = loadScreen->getSelectedFile(); + mpc.screens->get()->setFileName( + selectedFile->getNameWithoutExtension()); auto on_success = [&, selectedFile] { - mpc.screens->get()->setFileName( - selectedFile->getNameWithoutExtension()); openScreenById(ScreenId::LoadScreen); }; mpc.getDisk()->readAll2(selectedFile, on_success); diff --git a/src/main/lcdgui/screens/window/Mpc2000XlAllFileScreen.hpp b/src/main/lcdgui/screens/window/Mpc2000XlAllFileScreen.hpp index 9083f9253..f2161708e 100644 --- a/src/main/lcdgui/screens/window/Mpc2000XlAllFileScreen.hpp +++ b/src/main/lcdgui/screens/window/Mpc2000XlAllFileScreen.hpp @@ -5,8 +5,8 @@ namespace mpc::lcdgui::screens::window { class Mpc2000XlAllFileScreen final : public ScreenComponent { - public: + void open() override; void function(int i) override; Mpc2000XlAllFileScreen(Mpc &mpc, int layerIndex); diff --git a/src/main/nvram/DefaultsParser.cpp b/src/main/nvram/DefaultsParser.cpp index 405dfe161..0c1482e1c 100644 --- a/src/main/nvram/DefaultsParser.cpp +++ b/src/main/nvram/DefaultsParser.cpp @@ -1,6 +1,5 @@ #include "DefaultsParser.hpp" -#include "file/all/AllParser.hpp" #include "file/all/Defaults.hpp" #include "Logger.hpp" @@ -21,7 +20,7 @@ Defaults DefaultsParser::AllDefaultsFromFile(Mpc &mpc, mpc_fs::path p) } const auto &data = *dataRes; - return {mpc, Util::vecCopyOfRange(data, 0, AllParser::DEFAULTS_LENGTH)}; + return {mpc, Util::vecCopyOfRange(data, 0, Defaults::TOTAL_LENGTH)}; } DefaultsParser::DefaultsParser(Mpc &mpc) diff --git a/src/main/nvram/NvRam.cpp b/src/main/nvram/NvRam.cpp index 8f326b038..d06e7c929 100644 --- a/src/main/nvram/NvRam.cpp +++ b/src/main/nvram/NvRam.cpp @@ -15,7 +15,6 @@ #include "hardware/Hardware.hpp" #include "sequencer/TimeSignature.hpp" -#include "file/all/AllParser.hpp" #include "file/all/Defaults.hpp" using namespace mpc::nvram; @@ -48,7 +47,7 @@ void NvRam::loadUserScreenValues(Mpc &mpc) return; } - if (*sizeValue != file::all::AllParser::DEFAULTS_LENGTH) + if (*sizeValue != file::all::Defaults::TOTAL_LENGTH) { return; } diff --git a/src/main/performance/ProgramMapper.cpp b/src/main/performance/ProgramMapper.cpp index 927057995..74136f682 100644 --- a/src/main/performance/ProgramMapper.cpp +++ b/src/main/performance/ProgramMapper.cpp @@ -46,7 +46,7 @@ namespace mpc::performance p.optionalNoteA = s->getOptionalNoteA(); p.optionalNoteB = s->getOptionalNoteB(); p.sliderParameterNumber = s->getSliderParameterNumber(); - p.soundGenerationMode = s->getSoundGenerationMode(); + p.soundGenerationMode = sampler::toRaw(s->getSoundGenerationMode()); p.stereoMixer.level = s->getStereoMixer()->getLevel(); p.stereoMixer.panning = s->getStereoMixer()->getPanning(); @@ -90,7 +90,7 @@ namespace mpc::performance perfNoteParameters.soundIndex = pgmNoteParameters->getSoundIndex(); perfNoteParameters.soundGenerationMode = - pgmNoteParameters->getSoundGenerationMode(); + sampler::toRaw(pgmNoteParameters->getSoundGenerationMode()); perfNoteParameters.velocityRangeLower = pgmNoteParameters->getVelocityRangeLower(); perfNoteParameters.optionalNoteA = @@ -161,4 +161,4 @@ namespace mpc::performance dst.midiProgramChange = samplerProgram.getMidiProgramChange(); } -} // namespace mpc::performance \ No newline at end of file +} // namespace mpc::performance diff --git a/src/main/sampler/NoteParameters.cpp b/src/main/sampler/NoteParameters.cpp index 1c3a4d197..f274968cb 100644 --- a/src/main/sampler/NoteParameters.cpp +++ b/src/main/sampler/NoteParameters.cpp @@ -56,9 +56,9 @@ int NoteParameters::getSoundIndex() const return getSnapshot().soundIndex; } -int NoteParameters::getSoundGenerationMode() const +SoundGenerationMode NoteParameters::getSoundGenerationMode() const { - return getSnapshot().soundGenerationMode; + return soundGenerationModeFromRaw(getSnapshot().soundGenerationMode); } void NoteParameters::setSoundIndex(const int i) const diff --git a/src/main/sampler/NoteParameters.hpp b/src/main/sampler/NoteParameters.hpp index a42f44c5c..52a4bc105 100644 --- a/src/main/sampler/NoteParameters.hpp +++ b/src/main/sampler/NoteParameters.hpp @@ -1,6 +1,7 @@ #pragma once #include "IntTypes.hpp" +#include "sampler/SoundGenerationMode.hpp" #include "sampler/VoiceOverlapMode.hpp" #include "performance/PerformanceMessage.hpp" @@ -91,7 +92,7 @@ namespace mpc::sampler int getSoundIndex() const; - int getSoundGenerationMode() const; + SoundGenerationMode getSoundGenerationMode() const; DrumNoteNumber getOptionalNoteA() const; diff --git a/src/main/sampler/Program.cpp b/src/main/sampler/Program.cpp index 11fe7da16..bcf21f22b 100644 --- a/src/main/sampler/Program.cpp +++ b/src/main/sampler/Program.cpp @@ -156,7 +156,7 @@ Program::getPadIndexFromNote(const DrumNoteNumber note) const for (int8_t i = 0; i < Mpc2000XlSpecs::PROGRAM_PAD_COUNT; i++) { - if (pads[i]->getNote() == note) + if (getSnapshot(index).getPad(ProgramPadIndex(i)).note == note) { return ProgramPadIndex{i}; } @@ -208,7 +208,7 @@ void Program::initPadAssign() const mpc::DrumNoteNumber Program::getNoteFromPad(const ProgramPadIndex i) const { - return pads[i]->getNote(); + return getSnapshot(index).getPad(i).note; } std::vector @@ -218,7 +218,7 @@ Program::getPadIndicesFromNote(const DrumNoteNumber note) const for (int i = 0; i < pads.size(); i++) { - if (pads[i]->getNote() == note) + if (getSnapshot(index).getPad(ProgramPadIndex(i)).note == note) { result.push_back(ProgramPadIndex(i)); } diff --git a/src/main/sampler/SoundGenerationMode.hpp b/src/main/sampler/SoundGenerationMode.hpp new file mode 100644 index 000000000..cc8adb385 --- /dev/null +++ b/src/main/sampler/SoundGenerationMode.hpp @@ -0,0 +1,34 @@ +#pragma once + +#include + +namespace mpc::sampler +{ + enum class SoundGenerationMode : int8_t + { + Normal = 0, + Simult = 1, + VelocitySwitch = 2, + DecaySwitch = 3, + }; + + constexpr int8_t toRaw(const SoundGenerationMode mode) + { + return static_cast(mode); + } + + constexpr SoundGenerationMode soundGenerationModeFromRaw(const int rawValue) + { + switch (rawValue) + { + case 1: + return SoundGenerationMode::Simult; + case 2: + return SoundGenerationMode::VelocitySwitch; + case 3: + return SoundGenerationMode::DecaySwitch; + default: + return SoundGenerationMode::Normal; + } + } +} diff --git a/src/test/AllFileTest.cpp b/src/test/AllFileTest.cpp index bbf711b6d..bf657a098 100644 --- a/src/test/AllFileTest.cpp +++ b/src/test/AllFileTest.cpp @@ -6,22 +6,20 @@ #include "sequencer/Track.hpp" #include "sequencer/NoteOnEvent.hpp" #include "sequencer/ProgramChangeEvent.hpp" +#include "sequencer/SystemExclusiveEvent.hpp" #include "sequencer/Song.hpp" #include "disk/AbstractDisk.hpp" #include "disk/AllLoader.hpp" #include "disk/MpcFile.hpp" -#include "file/all/AllParser.hpp" -#include "file/all/AllSequence.hpp" +#include "file/kaitai/AllIo.hpp" #include "sequencer/SequencerStateManager.hpp" using namespace mpc::disk; -using namespace mpc::file::all; using namespace mpc::sequencer; void saveAndLoadTestAllFile(mpc::Mpc &mpc) { - AllParser allParser(mpc); - auto bytes = allParser.getBytes(); + auto bytes = mpc::file::kaitai::AllIo::save(mpc); const auto sequencer = mpc.getSequencer(); const auto stateManager = sequencer->getStateManager(); @@ -36,8 +34,7 @@ void saveAndLoadTestAllFile(mpc::Mpc &mpc) stateManager->drainQueue(); - AllParser reparsed(mpc, bytes); - AllLoader::loadEverythingFromAllParser(mpc, reparsed); + AllLoader::loadEverythingFromBytes(mpc, bytes); } TEST_CASE("ALL file song", "[allfile]") @@ -100,17 +97,20 @@ TEST_CASE("ALL file save does not crash with sysex event", "[allfile]") seq->getTrack(0)->acquireAndInsertEvent(eventData); stateManager->drainQueue(); - AllParser allParser(mpc); - REQUIRE_NOTHROW(allParser.getBytes()); - - auto bytes = allParser.getBytes(); - AllParser reparsed(mpc, bytes); - auto allSequences = reparsed.getAllSequences(); - REQUIRE_FALSE(allSequences.empty()); - REQUIRE(allSequences[0]->getEventAmount() == 1); - REQUIRE(allSequences[0]->allEvents[0].type == EventType::SystemExclusive); - REQUIRE(allSequences[0]->allEvents[0].sysExByteA == 0x12); - REQUIRE(allSequences[0]->allEvents[0].sysExByteB == 0x34); + REQUIRE_NOTHROW(mpc::file::kaitai::AllIo::save(mpc)); + + saveAndLoadTestAllFile(mpc); + stateManager->drainQueue(); + + auto loadedSequence = sequencer->getSequence(0); + REQUIRE(loadedSequence->isUsed()); + auto loadedTrack = loadedSequence->getTrack(0); + REQUIRE(loadedTrack->getEvents().size() == 1); + auto loadedSystemExclusive = std::dynamic_pointer_cast(loadedTrack->getEvent(0)); + REQUIRE(loadedSystemExclusive); + REQUIRE(loadedSystemExclusive->getTick() == 0); + REQUIRE(loadedSystemExclusive->getByteA() == 0x12); + REQUIRE(loadedSystemExclusive->getByteB() == 0x34); } TEST_CASE("ALL file track is on, used and transmits program changes", diff --git a/src/test/AllFileUiStateTest.cpp b/src/test/AllFileUiStateTest.cpp index e67d55d3f..37e1f1dd2 100644 --- a/src/test/AllFileUiStateTest.cpp +++ b/src/test/AllFileUiStateTest.cpp @@ -5,12 +5,20 @@ #include "disk/AbstractDisk.hpp" #include "disk/MpcFile.hpp" #include "lcdgui/screens/LoadScreen.hpp" +#include "lcdgui/screens/window/LoadASequenceFromAllScreen.hpp" #include "lcdgui/screens/window/Mpc2000XlAllFileScreen.hpp" #include "lcdgui/screens/window/SaveAllFileScreen.hpp" +#include "lcdgui/Label.hpp" +#include "sequencer/Sequencer.hpp" +#include "sequencer/Sequence.hpp" +#include "sequencer/SequencerStateManager.hpp" +#include "sequencer/Song.hpp" +#include "sequencer/Track.hpp" #include "StrUtil.hpp" #include #include +#include CMRC_DECLARE(mpctest); @@ -24,19 +32,80 @@ namespace { constexpr auto kAllResourcePath = "test/AllFileUi/FOO.ALL"; constexpr auto kCompatibleAllFileName = "FOO.ALL"; + constexpr auto kMpc3000AllResourcePath = "test/RealMpc3000/All/M3K_2SEQ.ALL"; + constexpr auto kMpc3000AllFileName = "M3K_2SEQ.ALL"; + constexpr auto kMpc3000AllSongResourcePath = "test/RealMpc3000/All/M3K_2SEQ1SONG.ALL"; + constexpr auto kMpc3000AllSongFileName = "M3K_2SEQ1SONG.ALL"; + constexpr auto kMpc3000AllEmptyResourcePath = "test/RealMpc3000/All/M3K_EMPTY2.ALL"; + constexpr auto kMpc3000AllEmptyFileName = "M3K_EMPTY2.ALL"; + constexpr auto kMpc60AllResourcePath = "test/RealMpc60/All/MPC60_V214_ALL_SEQS.ALL"; + constexpr auto kMpc60AllFileName = "M60SEQ.ALL"; + constexpr auto kMpc60AllSongResourcePath = "test/RealMpc60/All/MPC60_V214_SONG_2REALSTEPS.ALL"; + constexpr auto kMpc60AllSongFileName = "M60SONG.ALL"; - void prepareAllResources(Mpc &mpc) + void addResourceFile(Mpc &mpc, const std::string& resourcePath, + const std::string& fileName) { auto disk = mpc.getDisk(); auto fs = cmrc::mpctest::get_filesystem(); - auto file = fs.open(kAllResourcePath); + auto file = fs.open(resourcePath); std::vector data(file.begin(), file.end()); - auto newFile = disk->newFile(kCompatibleAllFileName); + auto newFile = disk->newFile(fileName); newFile->setFileData(data); + } + void prepareAllResources(Mpc &mpc) + { + addResourceFile(mpc, kAllResourcePath, kCompatibleAllFileName); + addResourceFile(mpc, kMpc3000AllResourcePath, kMpc3000AllFileName); + addResourceFile(mpc, kMpc3000AllSongResourcePath, kMpc3000AllSongFileName); + addResourceFile(mpc, kMpc3000AllEmptyResourcePath, kMpc3000AllEmptyFileName); + addResourceFile(mpc, kMpc60AllResourcePath, kMpc60AllFileName); + addResourceFile(mpc, kMpc60AllSongResourcePath, kMpc60AllSongFileName); + auto disk = mpc.getDisk(); disk->initFiles(); } + + int findFileLoadIndex(Mpc &mpc, const std::string& fileName) + { + const auto fileNames = mpc.getDisk()->getFileNames(); + const auto it = + std::find_if(fileNames.begin(), fileNames.end(), + [&](const std::string &candidate) + { + return StrUtil::eqIgnoreCase(candidate, fileName); + }); + REQUIRE(it != fileNames.end()); + return static_cast(std::distance(fileNames.begin(), it)); + } + + std::vector> + renderAllFileScreenFor(Mpc &mpc, const std::string& fileName) + { + const auto loadScreen = mpc.screens->get(); + loadScreen->setFileLoad(findFileLoadIndex(mpc, fileName)); + + const auto allFileScreen = + mpc.screens->get(); + allFileScreen->open(); + + std::vector pixels(248, std::vector(60)); + allFileScreen->drawRecursive(&pixels); + return pixels; + } + + void openAllWindowFor(Mpc &mpc, const std::string& fileName) + { + auto layeredScreen = mpc.getLayeredScreen(); + layeredScreen->openScreen("load"); + + const auto loadScreen = mpc.screens->get(); + loadScreen->setFileLoad(findFileLoadIndex(mpc, fileName)); + loadScreen->function(5); + + REQUIRE(layeredScreen->getCurrentScreenName() == "mpc2000xl-all-file"); + } } TEST_CASE("Complete ALL load remembers name for later ALL save", @@ -52,19 +121,8 @@ TEST_CASE("Complete ALL load remembers name for later ALL save", const auto allFile = mpc.getDisk()->getFile(kCompatibleAllFileName); REQUIRE(allFile); - const auto fileNames = mpc.getDisk()->getFileNames(); - const auto allFileIt = - std::find_if(fileNames.begin(), fileNames.end(), - [&](const std::string &fileName) - { - return StrUtil::eqIgnoreCase(fileName, - allFile->getName()); - }); - REQUIRE(allFileIt != fileNames.end()); - const auto loadScreen = mpc.screens->get(); - loadScreen->setFileLoad( - static_cast(std::distance(fileNames.begin(), allFileIt))); + loadScreen->setFileLoad(findFileLoadIndex(mpc, kCompatibleAllFileName)); const auto allFileScreen = mpc.screens->get(); @@ -73,3 +131,223 @@ TEST_CASE("Complete ALL load remembers name for later ALL save", REQUIRE(saveAllFileScreen->getFileName() == allFile->getNameWithoutExtension()); } + +TEST_CASE("ALL window title follows the selected ALL file variant", + "[load-all][ui]") +{ + Mpc mpc; + TestMpc::initializeTestMpc(mpc); + prepareAllResources(mpc); + + const auto mpc2000xlPixels = + renderAllFileScreenFor(mpc, kCompatibleAllFileName); + const auto mpc3000Pixels = + renderAllFileScreenFor(mpc, kMpc3000AllFileName); + + REQUIRE(mpc2000xlPixels != mpc3000Pixels); +} + +TEST_CASE("MPC3000 ALL branch lets the user browse embedded sequences and discard", + "[load-all][ui][real-mpc3000]") +{ + Mpc mpc; + TestMpc::initializeTestMpcWithoutMidiServices(mpc); + prepareAllResources(mpc); + + auto sequencer = mpc.getSequencer(); + auto stateManager = sequencer->getStateManager(); + sequencer->getSequence(0)->init(0); + sequencer->getSequence(0)->setName("DEST_A"); + sequencer->getSequence(1)->init(0); + sequencer->getSequence(1)->setName("DEST_B"); + stateManager->drainQueue(); + + openAllWindowFor(mpc, kMpc3000AllFileName); + + auto layeredScreen = mpc.getLayeredScreen(); + layeredScreen->getCurrentScreen()->function(2); + REQUIRE(layeredScreen->getCurrentScreenName() == "load-a-sequence-from-all"); + + const auto seqFromAllScreen = + mpc.screens->get(); + REQUIRE(seqFromAllScreen->findField("file")->getText() == "01"); + REQUIRE(seqFromAllScreen->findLabel("file0")->getText() == "-SEQ01"); + REQUIRE(seqFromAllScreen->findField("load-into")->getText() == "01"); + REQUIRE(seqFromAllScreen->findLabel("load-into0")->getText() == "-DEST_A"); + + seqFromAllScreen->turnWheel(1); + REQUIRE(seqFromAllScreen->findField("file")->getText() == "02"); + REQUIRE(seqFromAllScreen->findLabel("file0")->getText() == "-SEQ02"); + + seqFromAllScreen->down(); + REQUIRE(seqFromAllScreen->getFocusedFieldNameOrThrow() == "load-into"); + seqFromAllScreen->turnWheel(1); + REQUIRE(seqFromAllScreen->findField("load-into")->getText() == "02"); + REQUIRE(seqFromAllScreen->findLabel("load-into0")->getText() == "-DEST_B"); + + seqFromAllScreen->function(3); + REQUIRE(layeredScreen->getCurrentScreenName() == "load"); + REQUIRE(sequencer->getSequence(1)->getName() == "DEST_B"); +} + +TEST_CASE("MPC3000 ALL KEEP loads the chosen embedded sequence into the chosen slot", + "[load-all][ui][real-mpc3000]") +{ + Mpc mpc; + TestMpc::initializeTestMpcWithoutMidiServices(mpc); + prepareAllResources(mpc); + + auto sequencer = mpc.getSequencer(); + auto stateManager = sequencer->getStateManager(); + sequencer->getSequence(0)->init(0); + sequencer->getSequence(0)->setName("DEST_A"); + sequencer->getSequence(1)->init(0); + sequencer->getSequence(1)->setName("DEST_B"); + stateManager->drainQueue(); + + openAllWindowFor(mpc, kMpc3000AllFileName); + + auto layeredScreen = mpc.getLayeredScreen(); + layeredScreen->getCurrentScreen()->function(2); + REQUIRE(layeredScreen->getCurrentScreenName() == "load-a-sequence-from-all"); + + const auto seqFromAllScreen = + mpc.screens->get(); + seqFromAllScreen->turnWheel(1); + seqFromAllScreen->down(); + seqFromAllScreen->turnWheel(1); + + seqFromAllScreen->function(4); + stateManager->drainQueue(); + + REQUIRE(layeredScreen->getCurrentScreenName() == "load"); + auto loadedSequence = sequencer->getSequence(1); + REQUIRE(loadedSequence->isUsed()); + REQUIRE(loadedSequence->getName() == "SEQ02"); + REQUIRE(loadedSequence->getTrack(0)->getEvents().size() == 4); +} + +TEST_CASE("MPC3000 ALL KEEP loads empty embedded sequences without phantom events", + "[load-all][ui][real-mpc3000]") +{ + Mpc mpc; + TestMpc::initializeTestMpcWithoutMidiServices(mpc); + prepareAllResources(mpc); + + auto sequencer = mpc.getSequencer(); + auto stateManager = sequencer->getStateManager(); + sequencer->getSequence(0)->init(1); + sequencer->getSequence(0)->setName("DEST_A"); + stateManager->drainQueue(); + + openAllWindowFor(mpc, kMpc3000AllEmptyFileName); + + auto layeredScreen = mpc.getLayeredScreen(); + layeredScreen->getCurrentScreen()->function(2); + REQUIRE(layeredScreen->getCurrentScreenName() == "load-a-sequence-from-all"); + + const auto seqFromAllScreen = + mpc.screens->get(); + REQUIRE(seqFromAllScreen->findField("file")->getText() == "01"); + REQUIRE(seqFromAllScreen->findLabel("file0")->getText() == "-SEQ01"); + seqFromAllScreen->turnWheel(1); + REQUIRE(seqFromAllScreen->findField("file")->getText() == "02"); + REQUIRE(seqFromAllScreen->findLabel("file0")->getText() == "-SEQ02"); + + seqFromAllScreen->function(4); + stateManager->drainQueue(); + + REQUIRE(layeredScreen->getCurrentScreenName() == "load"); + auto loadedSequence = sequencer->getSequence(0); + REQUIRE(loadedSequence->isUsed()); + REQUIRE(loadedSequence->getName() == "SEQ02"); + REQUIRE(loadedSequence->getLastBarIndex() == 0); + REQUIRE(loadedSequence->getTrack(0)->getEvents().empty()); +} + +TEST_CASE("MPC3000 ALL LOAD imports sequences and songs through the real UI path", + "[load-all][ui][real-mpc3000]") +{ + Mpc mpc; + TestMpc::initializeTestMpcWithoutMidiServices(mpc); + prepareAllResources(mpc); + + auto sequencer = mpc.getSequencer(); + auto stateManager = sequencer->getStateManager(); + + openAllWindowFor(mpc, kMpc3000AllSongFileName); + + auto layeredScreen = mpc.getLayeredScreen(); + layeredScreen->getCurrentScreen()->function(4); + stateManager->drainQueue(); + + REQUIRE(layeredScreen->getCurrentScreenName() == "load"); + REQUIRE(sequencer->getSequence(0)->isUsed()); + REQUIRE(sequencer->getSequence(0)->getName() == "SEQ01"); + REQUIRE(sequencer->getSequence(1)->isUsed()); + REQUIRE(sequencer->getSequence(1)->getName() == "SEQ02"); + + auto song0 = sequencer->getSong(0); + REQUIRE(song0->isUsed()); + REQUIRE(song0->getName() == "SONG01"); + REQUIRE(song0->getStepCount() == 2); +} + +TEST_CASE("MPC60 v2 ALL branch lets the user browse embedded sequences and keep", + "[load-all][ui][real-mpc60]") +{ + Mpc mpc; + TestMpc::initializeTestMpcWithoutMidiServices(mpc); + prepareAllResources(mpc); + + auto sequencer = mpc.getSequencer(); + auto stateManager = sequencer->getStateManager(); + sequencer->getSequence(0)->init(0); + sequencer->getSequence(0)->setName("DEST_A"); + stateManager->drainQueue(); + + openAllWindowFor(mpc, kMpc60AllFileName); + + auto layeredScreen = mpc.getLayeredScreen(); + layeredScreen->getCurrentScreen()->function(2); + REQUIRE(layeredScreen->getCurrentScreenName() == "load-a-sequence-from-all"); + + const auto seqFromAllScreen = + mpc.screens->get(); + REQUIRE(seqFromAllScreen->findField("file")->getText() == "01"); + REQUIRE(seqFromAllScreen->findLabel("file0")->getText() == "-SEQ01"); + + seqFromAllScreen->function(4); + stateManager->drainQueue(); + + REQUIRE(layeredScreen->getCurrentScreenName() == "load"); + auto loadedSequence = sequencer->getSequence(0); + REQUIRE(loadedSequence->isUsed()); + REQUIRE(loadedSequence->getName() == "SEQ01"); +} + +TEST_CASE("MPC60 v2 ALL LOAD imports sequences and songs through the real UI path", + "[load-all][ui][real-mpc60]") +{ + Mpc mpc; + TestMpc::initializeTestMpcWithoutMidiServices(mpc); + prepareAllResources(mpc); + + auto sequencer = mpc.getSequencer(); + auto stateManager = sequencer->getStateManager(); + + openAllWindowFor(mpc, kMpc60AllSongFileName); + + auto layeredScreen = mpc.getLayeredScreen(); + layeredScreen->getCurrentScreen()->function(4); + stateManager->drainQueue(); + + REQUIRE(layeredScreen->getCurrentScreenName() == "load"); + REQUIRE(sequencer->getSequence(0)->isUsed()); + REQUIRE(sequencer->getSequence(0)->getName() == "SEQ01"); + + auto song0 = sequencer->getSong(0); + REQUIRE(song0->isUsed()); + REQUIRE(song0->getName() == "SONG01"); + REQUIRE(song0->getStepCount() == 2); +} diff --git a/src/test/AllLoadingTest.cpp b/src/test/AllLoadingTest.cpp index 643c0de57..e69de29bb 100644 --- a/src/test/AllLoadingTest.cpp +++ b/src/test/AllLoadingTest.cpp @@ -1,67 +0,0 @@ -#include "catch2/catch_test_macros.hpp" - -#include "TestMpc.hpp" -#include "Mpc.hpp" -#include "file/all/SequenceNames.hpp" -#include "sequencer/Sequence.hpp" -#include "sequencer/Sequencer.hpp" -#include "disk/AbstractDisk.hpp" -#include "disk/MpcFile.hpp" -#include "disk/AllLoader.hpp" -#include "file/all/AllParser.hpp" -#include "sequencer/SequencerStateManager.hpp" - -#include -#include - -CMRC_DECLARE(mpctest); - -using namespace mpc; -using namespace mpc::disk; - -void prepareAllResources(Mpc &mpc) -{ - auto disk = mpc.getDisk(); - auto fs = cmrc::mpctest::get_filesystem(); - - for (auto &&entry : fs.iterate_directory("test/AllLoading")) - { - auto file = fs.open("test/AllLoading/" + entry.filename()); - std::vector data(file.begin(), file.end()); - auto newFile = disk->newFile(entry.filename()); - newFile->setFileData(data); - } - - disk->initFiles(); -} - -TEST_CASE("Load ALL and check sequence 21 is used", "[load-all]") -{ - Mpc mpc; - TestMpc::initializeTestMpc(mpc); - prepareAllResources(mpc); - - auto disk = mpc.getDisk(); - auto allFile = disk->getFile("ShouldLoadSeq21.ALL"); - - REQUIRE(allFile); - - file::all::AllParser parser(mpc, allFile->getBytes()); - - auto seqNames = parser.getSeqNames(); - - REQUIRE(seqNames->getNames()[20] == "Sequence21 "); - REQUIRE(seqNames->getUsednesses()[20]); - - AllLoader::loadEverythingFromAllParser(mpc, parser); - - mpc.getSequencer()->getStateManager()->drainQueue(); - - auto seq1 = mpc.getSequencer()->getSequence(0); - REQUIRE(seq1); - REQUIRE(seq1->isUsed()); - - auto seq21 = mpc.getSequencer()->getSequence(20); - REQUIRE(seq21); - REQUIRE(seq21->isUsed()); -} diff --git a/src/test/ApsParserTest.cpp b/src/test/ApsParserTest.cpp deleted file mode 100644 index 892d4b782..000000000 --- a/src/test/ApsParserTest.cpp +++ /dev/null @@ -1,68 +0,0 @@ -#include - -#include "TestMpc.hpp" -#include "sampler/Sampler.hpp" -#include "file/aps/ApsParser.hpp" - -#include "performance/PerformanceManager.hpp" -#include "sampler/Pad.hpp" - -TEST_CASE("APS with maximum number of sounds", "[apsparser]") -{ - const uint16_t MAX_SOUND_COUNT = 256; - - mpc::Mpc mpc; - mpc::TestMpc::initializeTestMpc(mpc); - - for (uint16_t i = 0; i < MAX_SOUND_COUNT; i++) - { - auto sound = mpc.getSampler()->addSound(); - assert(sound != nullptr); - } - - assert(mpc.getSampler()->getSoundCount() == MAX_SOUND_COUNT); - - mpc::file::aps::ApsParser apsParserSave(mpc, "FOO"); - - mpc.getSampler()->purge(); - - assert(mpc.getSampler()->getSoundCount() == 0); - - mpc::file::aps::ApsParser apsParserLoad(apsParserSave.saveBytes); - - REQUIRE(apsParserLoad.getSoundNames().size() == MAX_SOUND_COUNT); -} - -TEST_CASE("APS with program with unassigned pad", "[apsparser]") -{ - mpc::Mpc mpc; - mpc::TestMpc::initializeTestMpc(mpc); - - const auto manager = mpc.getPerformanceManager().lock(); - - mpc.getSampler() - ->getProgram(mpc::MinProgramIndex) - ->getPad(mpc::MinProgramPadIndex) - ->setNote(mpc::NoDrumNoteAssigned); - - manager->drainQueue(); - - mpc::file::aps::ApsParser apsParserSave(mpc, "FOO"); - - mpc.getSampler()->deleteAllPrograms(true); - - manager->drainQueue(); - manager->drainQueue(); - - assert(mpc.getSampler() - ->getProgram(mpc::MinProgramIndex) - ->getPad(mpc::MinProgramPadIndex) - ->getNote() != mpc::NoDrumNoteAssigned); - - mpc::file::aps::ApsParser apsParserLoad(apsParserSave.saveBytes); - - manager->drainQueue(); - - REQUIRE(apsParserLoad.getPrograms()[0]->assignTable->get()[0] == - mpc::NoDrumNoteAssigned); -} diff --git a/src/test/DrumNoteEventHandlerRoutingTest.cpp b/src/test/DrumNoteEventHandlerRoutingTest.cpp index 9a0046205..91908cf2b 100644 --- a/src/test/DrumNoteEventHandlerRoutingTest.cpp +++ b/src/test/DrumNoteEventHandlerRoutingTest.cpp @@ -18,6 +18,8 @@ #include "sampler/Sampler.hpp" #include "sampler/Program.hpp" #include "sampler/NoteParameters.hpp" +#include "sampler/SoundGenerationMode.hpp" +#include "engine/Voice.hpp" #include "engine/IndivFxMixer.hpp" #include @@ -34,6 +36,8 @@ namespace { static constexpr int kBufferSize = 64; static constexpr DrumNoteNumber kTestNote{35}; + static constexpr DrumNoteNumber kSwitchNoteA{36}; + static constexpr DrumNoteNumber kSwitchNoteB{37}; static constexpr int kIndivLevel = 73; static constexpr float kPanLeft = 0.f; static constexpr float kPanCenter = 0.5f; @@ -122,6 +126,79 @@ namespace mpc.getSequencer()->getEventHandler()->handleUnfinalizedNoteOn( event, std::nullopt, BusType::DRUM1); } + + void configureSwitchingProgramNote( + Mpc &mpc, const mpc::sampler::SoundGenerationMode mode) + { + const auto sampler = mpc.getSampler(); + for (int i = 0; i < 3; ++i) + { + const auto sound = sampler->addSound(); + REQUIRE(sound != nullptr); + sound->insertFrame(std::vector{1.f}, 0); + sound->setStart(0); + sound->setEnd(1); + } + + const auto program = sampler->getProgram(0); + REQUIRE(program != nullptr); + + const auto note35 = program->getNoteParameters(kTestNote); + const auto note36 = program->getNoteParameters(kSwitchNoteA); + const auto note37 = program->getNoteParameters(kSwitchNoteB); + REQUIRE(note35 != nullptr); + REQUIRE(note36 != nullptr); + REQUIRE(note37 != nullptr); + + note35->setSoundIndex(0); + note35->setSoundGenMode(mpc::sampler::toRaw(mode)); + note35->setVeloRangeLower(44); + note35->setVeloRangeUpper(88); + note35->setOptionalNoteA(kSwitchNoteA); + note35->setOptionalNoteB(kSwitchNoteB); + + note36->setSoundIndex(1); + note37->setSoundIndex(2); + } + + std::vector getActiveVoiceNotes(Mpc &mpc) + { + std::vector result; + for (const auto &voice : mpc.getEngineHost()->getVoices()) + { + if (!voice->isFinished()) + { + result.push_back(voice->getNote()); + } + } + return result; + } + + void triggerUnfinalizedDrumNoteOn( + Mpc &mpc, const int velocity, const mpc::NoteVariationType variationType, + const int variationValue) + { + const auto mixerSetupScreen = + mpc.screens->get(); + mixerSetupScreen->setStereoMixSourceDrum(false); + mixerSetupScreen->setIndivFxSourceDrum(false); + + mpc.getSequencer()->getDrumBus(DrumBusIndex(0))->setProgramIndex( + ProgramIndex(0)); + + mpc.getEngineHost()->prepareProcessBlock(kBufferSize); + + EventData event; + event.type = EventType::NoteOn; + event.noteNumber = NoteNumber(kTestNote.get()); + event.velocity = Velocity(velocity); + event.noteVariationType = variationType; + event.noteVariationValue = NoteVariationValue(variationValue); + event.duration = NoDuration; + + mpc.getSequencer()->getEventHandler()->handleUnfinalizedNoteOn( + event, std::nullopt, BusType::DRUM1); + } } // namespace TEST_CASE("Mono sound routed to individual out uses a single strip AUX send", @@ -191,3 +268,49 @@ TEST_CASE("Stereo sound routed to main keeps both channels and no individual AUX REQUIRE(getAuxLevelControl(mixer, "1", "AUX#3")->getValue() == 0.f); REQUIRE(getAuxLevelControl(mixer, "1", "AUX#4")->getValue() == 0.f); } + +TEST_CASE("Velocity switch mode selects the configured alternate notes", + "[drum-note-routing]") +{ + Mpc mpc; + TestMpc::initializeTestMpc(mpc); + + configureSwitchingProgramNote( + mpc, mpc::sampler::SoundGenerationMode::VelocitySwitch); + triggerUnfinalizedDrumNoteOn(mpc, 45, mpc::NoteVariationTypeTune, 64); + + const auto activeNotes = getActiveVoiceNotes(mpc); + REQUIRE(activeNotes.size() == 1U); + REQUIRE(activeNotes.front() == kSwitchNoteA.get()); +} + +TEST_CASE("Velocity switch mode uses the second alternate note above the upper threshold", + "[drum-note-routing]") +{ + Mpc mpc; + TestMpc::initializeTestMpc(mpc); + + configureSwitchingProgramNote( + mpc, mpc::sampler::SoundGenerationMode::VelocitySwitch); + triggerUnfinalizedDrumNoteOn(mpc, 89, mpc::NoteVariationTypeTune, 64); + + const auto activeNotes = getActiveVoiceNotes(mpc); + REQUIRE(activeNotes.size() == 1U); + REQUIRE(activeNotes.front() == kSwitchNoteB.get()); +} + +TEST_CASE("Decay switch mode uses note variation decay when selecting the target note", + "[drum-note-routing]") +{ + Mpc mpc; + TestMpc::initializeTestMpc(mpc); + + configureSwitchingProgramNote( + mpc, mpc::sampler::SoundGenerationMode::DecaySwitch); + triggerUnfinalizedDrumNoteOn( + mpc, 64, mpc::NoteVariationTypeDecay, 89); + + const auto activeNotes = getActiveVoiceNotes(mpc); + REQUIRE(activeNotes.size() == 1U); + REQUIRE(activeNotes.front() == kSwitchNoteB.get()); +} diff --git a/src/test/KaitaiAllTest.cpp b/src/test/KaitaiAllTest.cpp new file mode 100644 index 000000000..a91787eea --- /dev/null +++ b/src/test/KaitaiAllTest.cpp @@ -0,0 +1,1288 @@ +#include +#include + +#include "TestMpc.hpp" +#include "Mpc.hpp" +#include "controller/ClientEventController.hpp" +#include "controller/ClientMidiEventController.hpp" +#include "disk/AbstractDisk.hpp" +#include "disk/AllLoader.hpp" +#include "disk/MpcFile.hpp" +#include "file/kaitai/AllIo.hpp" +#include "file/kaitai/generated/mpc2000xl_all.h" +#include "file/kaitai/generated/mpc60_all_v2.h" +#include "file/kaitai/generated/mpc3000_all_v3.h" +#include "lcdgui/ScreenId.hpp" +#include "lcdgui/screens/UserScreen.hpp" +#include "sequencer/BusType.hpp" +#include "sequencer/ChannelPressureEvent.hpp" +#include "sequencer/ControlChangeEvent.hpp" +#include "sequencer/MixerEvent.hpp" +#include "sequencer/NoteOnEvent.hpp" +#include "sequencer/PitchBendEvent.hpp" +#include "sequencer/PolyPressureEvent.hpp" +#include "sequencer/ProgramChangeEvent.hpp" +#include "sequencer/Sequence.hpp" +#include "sequencer/Sequencer.hpp" +#include "sequencer/SequencerStateManager.hpp" +#include "sequencer/Song.hpp" +#include "sequencer/SystemExclusiveEvent.hpp" +#include "sequencer/Track.hpp" + +#include +#include + +#include +#include +#include +#include +#include + +CMRC_DECLARE(mpctest); + +namespace { + +void prepareAllResources(mpc::Mpc& mpc) +{ + auto disk = mpc.getDisk(); + auto fs = cmrc::mpctest::get_filesystem(); + + for (auto&& entry : fs.iterate_directory("test/AllLoading")) { + auto file = fs.open("test/AllLoading/" + entry.filename()); + std::vector data(file.begin(), file.end()); + auto newFile = disk->newFile(entry.filename()); + newFile->setFileData(data); + } + + disk->initFiles(); +} + +std::shared_ptr installResourceFile( + mpc::Mpc& mpc, + const std::string& resourcePath, + const std::string& fileName) +{ + auto disk = mpc.getDisk(); + auto fs = cmrc::mpctest::get_filesystem(); + auto file = fs.open(resourcePath); + std::vector data(file.begin(), file.end()); + auto newFile = disk->newFile(fileName); + newFile->setFileData(data); + disk->initFiles(); + return newFile; +} + +std::shared_ptr installAllBytes( + mpc::Mpc& mpc, + const std::vector& bytes, + const std::string& fileName) +{ + auto disk = mpc.getDisk(); + auto newFile = disk->newFile(fileName); + auto mutableBytes = bytes; + newFile->setFileData(mutableBytes); + disk->initFiles(); + return newFile; +} + +std::string describeFirstByteDiff( + const std::vector& lhs, + const std::vector& rhs) +{ + const auto mismatch = std::mismatch(lhs.begin(), lhs.end(), rhs.begin(), rhs.end()); + if (mismatch.first == lhs.end() && mismatch.second == rhs.end()) + { + return "no diff"; + } + + const auto offset = static_cast(mismatch.first - lhs.begin()); + std::ostringstream oss; + oss << "first diff at offset " << offset + << ": lhs=0x" + << std::hex << std::setw(2) << std::setfill('0') + << (static_cast(static_cast(*mismatch.first))) + << " rhs=0x" + << std::hex << std::setw(2) << std::setfill('0') + << (static_cast(static_cast(*mismatch.second))); + return oss.str(); +} + +std::vector parseAllSequenceBodyIndexes(const std::vector& bytes) +{ + std::stringstream parseStream( + std::string(bytes.begin(), bytes.end()), + std::ios::in | std::ios::out | std::ios::binary + ); + kaitai::kstream parseIo(&parseStream); + mpc2000xl_all_t parsed(&parseIo); + parsed._read(); + + std::vector result; + if (parsed.sequences() == nullptr) + { + return result; + } + + for (const auto& sequence : *parsed.sequences()) + { + if (sequence->body() != nullptr) + { + result.push_back(sequence->body()->index()); + } + else + { + result.push_back(-1); + } + } + + return result; +} + +std::string describeSequenceBodyIndexes(const std::vector& indexes) +{ + std::ostringstream oss; + oss << "["; + for (size_t i = 0; i < indexes.size(); ++i) + { + if (i != 0) + { + oss << ", "; + } + oss << indexes[i]; + } + oss << "]"; + return oss.str(); +} + +std::vector buildBroadMutatedAllBytes() +{ + auto fs = cmrc::mpctest::get_filesystem(); + auto file = fs.open("test/RealMpc2000xl/All/ALL.ALL"); + const std::string originalBytes( + std::string_view(file.begin(), file.end() - file.begin()) + ); + + std::stringstream parseStream( + originalBytes, + std::ios::in | std::ios::out | std::ios::binary + ); + kaitai::kstream parseIo(&parseStream); + mpc2000xl_all_t parsed(&parseIo); + parsed._read(); + + parsed.defaults()->set_tempo(875); + parsed.defaults()->set_numerator(7); + parsed.defaults()->set_denominator(8); + + auto defaultDevices = std::make_unique>(*parsed.defaults()->devices()); + defaultDevices->at(0) = 31; + parsed.defaults()->set_devices(std::move(defaultDevices)); + + auto defaultBuses = std::make_unique>(*parsed.defaults()->buses()); + defaultBuses->at(0) = mpc2000xl_all_t::BUS_DRUM4; + parsed.defaults()->set_buses(std::move(defaultBuses)); + + std::vector usedSequenceBodies; + for (const auto& sequence : *parsed.sequences()) { + if (sequence->body() != nullptr) { + usedSequenceBodies.push_back(sequence->body()); + } + } + + auto sequence0 = usedSequenceBodies.at(0); + auto sequence0Devices = std::make_unique>(*sequence0->tracks()->device()); + sequence0Devices->at(0) = 29; + sequence0->tracks()->set_device(std::move(sequence0Devices)); + + auto sequence0Buses = std::make_unique>(*sequence0->tracks()->bus()); + sequence0Buses->at(0) = mpc2000xl_all_t::BUS_DRUM3; + sequence0->tracks()->set_bus(std::move(sequence0Buses)); + + auto sequence1 = usedSequenceBodies.at(1); + auto sequence1Devices = std::make_unique>(*sequence1->tracks()->device()); + sequence1Devices->at(0) = 5; + sequence1->tracks()->set_device(std::move(sequence1Devices)); + + auto sequence1Buses = std::make_unique>(*sequence1->tracks()->bus()); + sequence1Buses->at(0) = mpc2000xl_all_t::BUS_MIDI; + sequence1->tracks()->set_bus(std::move(sequence1Buses)); + + parsed.sequencer()->set_active_sequence(1); + parsed.sequencer()->set_active_track(3); + parsed.sequencer()->set_master_tempo(875); + + auto song0 = parsed.songs()->at(0).get(); + song0->steps()->at(0)->set_sequence_index(1); + song0->steps()->at(0)->set_repeat_count(4); + song0->steps()->at(1)->set_sequence_index(0); + song0->steps()->at(1)->set_repeat_count(5); + song0->set_loop_first_step(0); + song0->set_loop_last_step(1); + song0->set_is_loop_enabled(true); + + std::stringstream writeStream(std::ios::in | std::ios::out | std::ios::binary); + kaitai::kstream writeIo(&writeStream); + parsed._set_io(&writeIo); + parsed._check(); + parsed._write(); + + const auto rewritten = writeStream.str(); + return std::vector(rewritten.begin(), rewritten.end()); +} + +} + +TEST_CASE("Kaitai MPC2000 ALL parses and rewrites real 2KXL ALL", "[kaitai-all][real-2kxl]") +{ + auto fs = cmrc::mpctest::get_filesystem(); + auto file = fs.open("test/RealMpc2000xl/All/ALL.ALL"); + const std::string originalBytes( + std::string_view(file.begin(), file.end() - file.begin()) + ); + + std::stringstream parseStream( + originalBytes, + std::ios::in | std::ios::out | std::ios::binary + ); + kaitai::kstream parseIo(&parseStream); + mpc2000xl_all_t parsed(&parseIo); + parsed._read(); + + REQUIRE(parsed.defaults() != nullptr); + REQUIRE(parsed.defaults()->tempo() == 1162); + REQUIRE(parsed.defaults()->numerator() == 4); + REQUIRE(parsed.defaults()->denominator() == 4); + REQUIRE(parsed.defaults()->buses() != nullptr); + REQUIRE(parsed.defaults()->devices() != nullptr); + REQUIRE(parsed.defaults()->buses()->at(0) == mpc2000xl_all_t::BUS_DRUM2); + REQUIRE(parsed.defaults()->devices()->at(0) == 7); + + REQUIRE(parsed.sequences_metas() != nullptr); + REQUIRE(parsed.sequences() != nullptr); + REQUIRE(parsed.sequences_metas()->size() >= 2U); + REQUIRE(parsed.sequences()->size() >= 2U); + REQUIRE(parsed.sequences_metas()->at(0)->is_used()); + REQUIRE(parsed.sequences_metas()->at(1)->is_used()); + std::vector usedSequenceBodies; + for (const auto& seq : *parsed.sequences()) { + if (seq->body() != nullptr) { + usedSequenceBodies.push_back(seq->body()); + } + } + REQUIRE(usedSequenceBodies.size() == 2U); + REQUIRE(usedSequenceBodies.at(0)->index() == 1); + REQUIRE(usedSequenceBodies.at(1)->index() == 2); + REQUIRE(usedSequenceBodies.at(0)->tracks() != nullptr); + REQUIRE(usedSequenceBodies.at(1)->tracks() != nullptr); + REQUIRE(usedSequenceBodies.at(0)->tracks()->bus()->at(0) == mpc2000xl_all_t::BUS_DRUM2); + REQUIRE(usedSequenceBodies.at(1)->tracks()->bus()->at(0) == mpc2000xl_all_t::BUS_DRUM2); + REQUIRE(usedSequenceBodies.at(0)->tracks()->device()->at(0) == 7); + REQUIRE(usedSequenceBodies.at(1)->tracks()->device()->at(0) == 7); + + REQUIRE(parsed.songs() != nullptr); + REQUIRE(parsed.songs()->size() >= 2U); + REQUIRE(parsed.songs()->at(0)->is_used()); + REQUIRE(parsed.songs()->at(1)->is_used()); + REQUIRE(parsed.songs()->at(0)->steps() != nullptr); + REQUIRE(parsed.songs()->at(1)->steps() != nullptr); + REQUIRE(parsed.songs()->at(0)->steps()->at(0)->sequence_index() == 0); + REQUIRE(parsed.songs()->at(0)->steps()->at(0)->repeat_count() == 1); + REQUIRE(parsed.songs()->at(0)->steps()->at(1)->sequence_index() == 1); + REQUIRE(parsed.songs()->at(0)->steps()->at(1)->repeat_count() == 1); + REQUIRE(parsed.songs()->at(1)->steps()->at(0)->sequence_index() == 1); + REQUIRE(parsed.songs()->at(1)->steps()->at(0)->repeat_count() == 2); + REQUIRE(parsed.songs()->at(1)->steps()->at(1)->sequence_index() == 0); + REQUIRE(parsed.songs()->at(1)->steps()->at(1)->repeat_count() == 3); + + std::stringstream writeStream(std::ios::in | std::ios::out | std::ios::binary); + kaitai::kstream writeIo(&writeStream); + parsed._set_io(&writeIo); + parsed._check(); + parsed._write(); + + const auto rewrittenBytes = writeStream.str(); + REQUIRE(rewrittenBytes.size() == originalBytes.size()); + REQUIRE(std::equal(rewrittenBytes.begin(), rewrittenBytes.end(), originalBytes.begin())); +} + +TEST_CASE("Kaitai MPC2000 ALL production save preserves real RESIST.ALL bytes", "[kaitai-all][real-2kxl][resist]") +{ + mpc::Mpc mpc; + mpc::TestMpc::initializeTestMpcWithoutMidiServices(mpc); + + auto allFile = installResourceFile( + mpc, + "test/RealMpc2000xl/Resist/RESIST.ALL", + "RESIST.ALL" + ); + REQUIRE(allFile); + + const auto originalBytes = allFile->getBytes(); + + mpc::disk::AllLoader::loadEverythingFromFile(mpc, allFile.get()); + mpc.getSequencer()->getStateManager()->drainQueue(); + + const auto savedBytes = mpc::file::kaitai::AllIo::save(mpc); + const auto originalSequenceBodyIndexes = parseAllSequenceBodyIndexes(originalBytes); + const auto savedSequenceBodyIndexes = parseAllSequenceBodyIndexes(savedBytes); + + INFO("original size: " << originalBytes.size()); + INFO("saved size: " << savedBytes.size()); + INFO(describeFirstByteDiff(originalBytes, savedBytes)); + INFO("original sequence bodies: " << describeSequenceBodyIndexes(originalSequenceBodyIndexes)); + INFO("saved sequence bodies: " << describeSequenceBodyIndexes(savedSequenceBodyIndexes)); + REQUIRE(savedBytes.size() == originalBytes.size()); + REQUIRE(std::equal(savedBytes.begin(), savedBytes.end(), originalBytes.begin())); +} + +TEST_CASE("Kaitai MPC2000 ALL saves and reloads ShouldLoadSeq21 semantics", "[kaitai-all]") +{ + mpc::Mpc mpc; + mpc::TestMpc::initializeTestMpc(mpc); + prepareAllResources(mpc); + + auto allFile = mpc.getDisk()->getFile("ShouldLoadSeq21.ALL"); + REQUIRE(allFile); + + mpc::disk::AllLoader::loadEverythingFromFile(mpc, allFile.get()); + mpc.getSequencer()->getStateManager()->drainQueue(); + + REQUIRE(mpc.getSequencer()->getSequence(0)->isUsed()); + REQUIRE(mpc.getSequencer()->getSequence(20)->isUsed()); + + auto footswitchController = + mpc.clientEventController->getClientMidiEventController() + ->getFootswitchAssignmentController(); + std::visit( + [](auto& binding) + { + binding.number = -1; + }, + footswitchController->bindings.at(0) + ); + + const auto kaitaiBytesVec = mpc::file::kaitai::AllIo::save(mpc); + + std::stringstream parseStream( + std::string(kaitaiBytesVec.begin(), kaitaiBytesVec.end()), + std::ios::in | std::ios::out | std::ios::binary + ); + kaitai::kstream parseIo(&parseStream); + mpc2000xl_all_t parsed(&parseIo); + parsed._read(); + + REQUIRE(parsed.sequences_metas() != nullptr); + REQUIRE(parsed.sequences_metas()->at(0)->is_used()); + REQUIRE(parsed.sequences_metas()->at(20)->is_used()); + REQUIRE(parsed.misc() != nullptr); + REQUIRE(parsed.misc()->midi_switch() != nullptr); + REQUIRE(parsed.misc()->midi_switch()->at(0)->controller() == 0xFF); + + std::stringstream writeStream(std::ios::in | std::ios::out | std::ios::binary); + kaitai::kstream writeIo(&writeStream); + parsed._set_io(&writeIo); + parsed._check(); + parsed._write(); + + const auto kaitaiBytes = writeStream.str(); + REQUIRE(kaitaiBytes.size() == kaitaiBytesVec.size()); + REQUIRE(std::equal(kaitaiBytes.begin(), kaitaiBytes.end(), kaitaiBytesVec.begin())); + + mpc::Mpc reparsedMpc; + mpc::TestMpc::initializeTestMpc(reparsedMpc); + auto reparsedFile = installAllBytes( + reparsedMpc, + std::vector(kaitaiBytes.begin(), kaitaiBytes.end()), + "ROUNDTRIP.ALL" + ); + REQUIRE(reparsedFile); + mpc::disk::AllLoader::loadEverythingFromFile(reparsedMpc, reparsedFile.get()); + reparsedMpc.getSequencer()->getStateManager()->drainQueue(); + + REQUIRE(reparsedMpc.getSequencer()->getSequence(0)->isUsed()); + REQUIRE(reparsedMpc.getSequencer()->getSequence(20)->isUsed()); + + auto reparsedFootswitchController = + reparsedMpc.clientEventController->getClientMidiEventController() + ->getFootswitchAssignmentController(); + std::visit( + [](auto& binding) + { + REQUIRE(binding.number == -1); + }, + reparsedFootswitchController->bindings.at(0) + ); +} + +TEST_CASE("Kaitai MPC2000 ALL loads real 2KXL ALL through production seam", "[kaitai-all][real-2kxl]") +{ + mpc::Mpc mpc; + mpc::TestMpc::initializeTestMpcWithoutMidiServices(mpc); + + auto allFile = installResourceFile( + mpc, + "test/RealMpc2000xl/All/ALL.ALL", + "ALL.ALL" + ); + REQUIRE(allFile); + + mpc::disk::AllLoader::loadEverythingFromFile(mpc, allFile.get()); + mpc.getSequencer()->getStateManager()->drainQueue(); + + auto sequencer = mpc.getSequencer(); + auto userScreen = mpc.screens->get(); + REQUIRE(userScreen); + + REQUIRE_THAT(userScreen->getTempo(), Catch::Matchers::WithinAbs(116.2, 0.001)); + REQUIRE(userScreen->getBusType() == mpc::sequencer::BusType::DRUM2); + REQUIRE(userScreen->getDevice() == 7); + REQUIRE(userScreen->getTimeSig().numerator == 4); + REQUIRE(userScreen->getTimeSig().denominator == 4); + + auto sequence0 = sequencer->getSequence(0); + auto sequence1 = sequencer->getSequence(1); + REQUIRE(sequence0->isUsed()); + REQUIRE(sequence1->isUsed()); + REQUIRE_THAT(sequence0->getInitialTempo(), Catch::Matchers::WithinAbs(116.2, 0.001)); + REQUIRE_THAT(sequence1->getInitialTempo(), Catch::Matchers::WithinAbs(116.2, 0.001)); + REQUIRE(sequence0->getTrack(0)->getBusType() == mpc::sequencer::BusType::DRUM2); + REQUIRE(sequence1->getTrack(0)->getBusType() == mpc::sequencer::BusType::DRUM2); + REQUIRE(sequence0->getTrack(0)->getDeviceIndex() == 7); + REQUIRE(sequence1->getTrack(0)->getDeviceIndex() == 7); + REQUIRE(sequence0->getTimeSignatureFromBarIndex(0).numerator == 4); + REQUIRE(sequence0->getTimeSignatureFromBarIndex(0).denominator == 4); + REQUIRE(sequence1->getTimeSignatureFromBarIndex(0).numerator == 4); + REQUIRE(sequence1->getTimeSignatureFromBarIndex(0).denominator == 4); + + auto song0 = sequencer->getSong(0); + auto song1 = sequencer->getSong(1); + REQUIRE(song0->isUsed()); + REQUIRE(song1->isUsed()); + REQUIRE(song0->getStepCount() == 2); + REQUIRE(song1->getStepCount() == 2); + REQUIRE(song0->getStep(mpc::SongStepIndex(0)).sequenceIndex == 0); + REQUIRE(song0->getStep(mpc::SongStepIndex(0)).repetitionCount == 1); + REQUIRE(song0->getStep(mpc::SongStepIndex(1)).sequenceIndex == 1); + REQUIRE(song0->getStep(mpc::SongStepIndex(1)).repetitionCount == 1); + REQUIRE(song1->getStep(mpc::SongStepIndex(0)).sequenceIndex == 1); + REQUIRE(song1->getStep(mpc::SongStepIndex(0)).repetitionCount == 2); + REQUIRE(song1->getStep(mpc::SongStepIndex(1)).sequenceIndex == 0); + REQUIRE(song1->getStep(mpc::SongStepIndex(1)).repetitionCount == 3); +} + +TEST_CASE("Kaitai MPC2000 ALL sequence meta infos expose expected real 2KXL names and usedness", "[kaitai-all][real-2kxl]") +{ + mpc::Mpc mpc; + mpc::TestMpc::initializeTestMpcWithoutMidiServices(mpc); + + auto allFile = installResourceFile( + mpc, + "test/RealMpc2000xl/All/ALL.ALL", + "ALL.ALL" + ); + REQUIRE(allFile); + + const auto metaInfos = mpc::disk::AllLoader::loadSequenceMetaInfosFromFile(mpc, allFile.get()); + REQUIRE(metaInfos.size() >= 64U); + REQUIRE(metaInfos[0].used); + REQUIRE(metaInfos[0].name == "Sequence01 "); + REQUIRE(metaInfos[1].used); + REQUIRE(metaInfos[1].name == "Sequence02 "); + REQUIRE_FALSE(metaInfos[2].used); + REQUIRE(metaInfos[2].name == "(Unused) "); + REQUIRE_FALSE(metaInfos[63].used); + REQUIRE(metaInfos[63].name == "(Unused) "); +} + +TEST_CASE("Kaitai MPC2000 ALL loads one real 2KXL sequence with expected defaults", "[kaitai-all][real-2kxl]") +{ + mpc::Mpc kaitaiMpc; + mpc::TestMpc::initializeTestMpcWithoutMidiServices(kaitaiMpc); + auto kaitaiFile = installResourceFile( + kaitaiMpc, + "test/RealMpc2000xl/All/ALL.ALL", + "ALL.ALL" + ); + REQUIRE(kaitaiFile); + + const auto loaded = mpc::disk::AllLoader::loadOneSequenceFromFile( + kaitaiMpc, + kaitaiFile.get(), + mpc::SequenceIndex(1), + mpc::SequenceIndex(10) + ); + REQUIRE(loaded); + kaitaiMpc.getSequencer()->getStateManager()->drainQueue(); + + REQUIRE(loaded->isUsed()); + REQUIRE(loaded->getName() == "Sequence02"); + REQUIRE_THAT(loaded->getInitialTempo(), Catch::Matchers::WithinAbs(116.2, 0.001)); + REQUIRE(loaded->getTimeSignatureFromBarIndex(0).numerator == 4); + REQUIRE(loaded->getTimeSignatureFromBarIndex(0).denominator == 4); + REQUIRE(loaded->getTrack(0)->getBusType() == mpc::sequencer::BusType::DRUM2); + REQUIRE(loaded->getTrack(0)->getDeviceIndex() == 7); +} + +TEST_CASE("Kaitai MPC3000 ALL parser exposes expected embedded sequence and song structure", "[kaitai-all][real-mpc3000]") +{ + auto fs = cmrc::mpctest::get_filesystem(); + auto file = fs.open("test/RealMpc3000/All/M3K_2SEQ1SONG.ALL"); + const std::string bytes( + std::string_view(file.begin(), file.end() - file.begin()) + ); + + std::stringstream parseStream( + bytes, + std::ios::in | std::ios::out | std::ios::binary + ); + kaitai::kstream parseIo(&parseStream); + mpc3000_all_v3_t parsed(&parseIo); + parsed._read(); + + REQUIRE(parsed.sequences()->size() == 2U); + REQUIRE(parsed.sequences()->at(0)->misc_chunks()->sequence_header()->sequence_name() == "SEQ01 "); + REQUIRE(parsed.sequences()->at(1)->misc_chunks()->sequence_header()->sequence_name() == "SEQ02 "); + REQUIRE(parsed.sequences()->at(0)->events()->size() == 5U); + REQUIRE(parsed.sequences()->at(1)->events()->size() == 9U); + + REQUIRE(parsed.songs()->size() == 2U); + + const auto* songBody = parsed.songs()->at(0)->song_body(); + REQUIRE(songBody != nullptr); + REQUIRE(parsed.songs()->at(0)->number_of_steps() == 2); + REQUIRE(songBody->song_number() == 1); + REQUIRE(songBody->song_name() == "SONG01"); + REQUIRE(songBody->end_status() == mpc3000_all_v3_t::song_t::song_body_t::END_STATUS_STOP_AT_END); + REQUIRE(songBody->loop_back_step_number() == 2); + REQUIRE(songBody->steps()->size() == 2U); + REQUIRE(songBody->steps()->at(0)->sequence_number() == 1); + REQUIRE(songBody->steps()->at(0)->repetition_count() == 2); + REQUIRE(songBody->steps()->at(1)->sequence_number() == 1); + REQUIRE(songBody->steps()->at(1)->repetition_count() == 3); + + REQUIRE(parsed.songs()->at(1)->number_of_steps() == 0); + REQUIRE(parsed.songs()->at(1)->song_body() == nullptr); +} + +TEST_CASE("Kaitai MPC3000 ALL sequence meta infos expose expected names and usedness", "[kaitai-all][real-mpc3000]") +{ + mpc::Mpc mpc; + mpc::TestMpc::initializeTestMpcWithoutMidiServices(mpc); + + auto allFile = installResourceFile( + mpc, + "test/RealMpc3000/All/M3K_2SEQ.ALL", + "M3K_2SEQ.ALL" + ); + REQUIRE(allFile); + + const auto metaInfos = mpc::disk::AllLoader::loadSequenceMetaInfosFromFile(mpc, allFile.get()); + REQUIRE(metaInfos.size() == 2U); + REQUIRE(metaInfos[0].used); + REQUIRE(metaInfos[0].name == "SEQ01"); + REQUIRE(metaInfos[1].used); + REQUIRE(metaInfos[1].name == "SEQ02"); +} + +TEST_CASE("Kaitai MPC3000 ALL empty embedded sequences still expose used sequence meta infos", "[kaitai-all][real-mpc3000]") +{ + mpc::Mpc mpc; + mpc::TestMpc::initializeTestMpcWithoutMidiServices(mpc); + + auto allFile = installResourceFile( + mpc, + "test/RealMpc3000/All/M3K_EMPTY2.ALL", + "M3K_EMPTY2.ALL" + ); + REQUIRE(allFile); + + const auto metaInfos = mpc::disk::AllLoader::loadSequenceMetaInfosFromFile(mpc, allFile.get()); + REQUIRE(metaInfos.size() == 2U); + REQUIRE(metaInfos[0].used); + REQUIRE(metaInfos[0].name == "SEQ01"); + REQUIRE(metaInfos[1].used); + REQUIRE(metaInfos[1].name == "SEQ02"); +} + +TEST_CASE("Kaitai MPC3000 ALL loads one sequence with expected defaults", "[kaitai-all][real-mpc3000]") +{ + mpc::Mpc kaitaiMpc; + mpc::TestMpc::initializeTestMpcWithoutMidiServices(kaitaiMpc); + auto kaitaiFile = installResourceFile( + kaitaiMpc, + "test/RealMpc3000/All/M3K_2SEQ.ALL", + "M3K_2SEQ.ALL" + ); + REQUIRE(kaitaiFile); + + const auto loaded = mpc::disk::AllLoader::loadOneSequenceFromFile( + kaitaiMpc, + kaitaiFile.get(), + mpc::SequenceIndex(1), + mpc::SequenceIndex(10) + ); + REQUIRE(loaded); + kaitaiMpc.getSequencer()->getStateManager()->drainQueue(); + + REQUIRE(loaded->isUsed()); + REQUIRE(loaded->getName() == "SEQ02"); + REQUIRE_THAT(loaded->getInitialTempo(), Catch::Matchers::WithinAbs(99.0, 0.001)); + REQUIRE(loaded->getTimeSignatureFromBarIndex(0).numerator == 4); + REQUIRE(loaded->getTimeSignatureFromBarIndex(0).denominator == 4); + REQUIRE(loaded->isLoopEnabled()); +} + +TEST_CASE("Kaitai MPC3000 ALL loads empty embedded sequences without inventing musical events", "[kaitai-all][real-mpc3000]") +{ + mpc::Mpc kaitaiMpc; + mpc::TestMpc::initializeTestMpcWithoutMidiServices(kaitaiMpc); + auto kaitaiFile = installResourceFile( + kaitaiMpc, + "test/RealMpc3000/All/M3K_EMPTY2.ALL", + "M3K_EMPTY2.ALL" + ); + REQUIRE(kaitaiFile); + + const auto loaded = mpc::disk::AllLoader::loadOneSequenceFromFile( + kaitaiMpc, + kaitaiFile.get(), + mpc::SequenceIndex(1), + mpc::SequenceIndex(10) + ); + REQUIRE(loaded); + kaitaiMpc.getSequencer()->getStateManager()->drainQueue(); + + REQUIRE(loaded->isUsed()); + REQUIRE(loaded->getName() == "SEQ02"); + REQUIRE(loaded->getLastBarIndex() == 0); + REQUIRE_THAT(loaded->getInitialTempo(), Catch::Matchers::WithinAbs(99.0, 0.001)); + REQUIRE(loaded->getTimeSignatureFromBarIndex(0).numerator == 4); + REQUIRE(loaded->getTimeSignatureFromBarIndex(0).denominator == 4); + REQUIRE(loaded->isLoopEnabled()); + REQUIRE(loaded->getTrack(0)->getEvents().empty()); +} + +TEST_CASE("Kaitai MPC3000 ALL loads sequences and songs through the production seam", "[kaitai-all][real-mpc3000]") +{ + mpc::Mpc mpc; + mpc::TestMpc::initializeTestMpcWithoutMidiServices(mpc); + + auto allFile = installResourceFile( + mpc, + "test/RealMpc3000/All/M3K_2SEQ1SONG.ALL", + "M3K_2SEQ1SONG.ALL" + ); + REQUIRE(allFile); + + mpc::disk::AllLoader::loadEverythingFromFile(mpc, allFile.get()); + mpc.getSequencer()->getStateManager()->drainQueue(); + + auto sequencer = mpc.getSequencer(); + auto sequence0 = sequencer->getSequence(0); + auto sequence1 = sequencer->getSequence(1); + REQUIRE(sequence0->isUsed()); + REQUIRE(sequence1->isUsed()); + REQUIRE(sequence0->getName() == "SEQ01"); + REQUIRE(sequence1->getName() == "SEQ02"); + REQUIRE_THAT(sequence0->getInitialTempo(), Catch::Matchers::WithinAbs(99.0, 0.001)); + REQUIRE_THAT(sequence1->getInitialTempo(), Catch::Matchers::WithinAbs(99.0, 0.001)); + + auto song0 = sequencer->getSong(0); + auto song1 = sequencer->getSong(1); + REQUIRE(song0->isUsed()); + REQUIRE(song0->getName() == "SONG01"); + REQUIRE(song0->getStepCount() == 2); + REQUIRE(song0->getStep(mpc::SongStepIndex(0)).sequenceIndex == 0); + REQUIRE(song0->getStep(mpc::SongStepIndex(0)).repetitionCount == 2); + REQUIRE(song0->getStep(mpc::SongStepIndex(1)).sequenceIndex == 0); + REQUIRE(song0->getStep(mpc::SongStepIndex(1)).repetitionCount == 3); + REQUIRE_FALSE(song0->isLoopEnabled()); + REQUIRE_FALSE(song1->isUsed()); +} + +TEST_CASE("AllParser reads mutated default tempo and song repeat count", "[kaitai-all]") +{ + auto fs = cmrc::mpctest::get_filesystem(); + auto file = fs.open("test/RealMpc2000xl/All/ALL.ALL"); + const std::string originalBytes( + std::string_view(file.begin(), file.end() - file.begin()) + ); + + std::stringstream parseStream( + originalBytes, + std::ios::in | std::ios::out | std::ios::binary + ); + kaitai::kstream parseIo(&parseStream); + mpc2000xl_all_t parsed(&parseIo); + parsed._read(); + parsed.defaults()->set_tempo(999); + parsed.songs()->at(1)->steps()->at(1)->set_repeat_count(7); + + std::stringstream writeStream(std::ios::in | std::ios::out | std::ios::binary); + kaitai::kstream writeIo(&writeStream); + parsed._set_io(&writeIo); + parsed._check(); + parsed._write(); + + const auto rewritten = writeStream.str(); + std::stringstream reparsedStream( + rewritten, + std::ios::in | std::ios::out | std::ios::binary + ); + kaitai::kstream reparsedIo(&reparsedStream); + mpc2000xl_all_t reparsed(&reparsedIo); + reparsed._read(); + + REQUIRE(reparsed.defaults()->tempo() == 999); + REQUIRE(reparsed.songs()->at(1)->steps()->at(1)->repeat_count() == 7); +} + +TEST_CASE("AllLoader loads mutated default tempo and song repeat count", "[kaitai-all]") +{ + auto fs = cmrc::mpctest::get_filesystem(); + auto file = fs.open("test/RealMpc2000xl/All/ALL.ALL"); + const std::string originalBytes( + std::string_view(file.begin(), file.end() - file.begin()) + ); + + std::stringstream parseStream( + originalBytes, + std::ios::in | std::ios::out | std::ios::binary + ); + kaitai::kstream parseIo(&parseStream); + mpc2000xl_all_t parsed(&parseIo); + parsed._read(); + parsed.defaults()->set_tempo(999); + parsed.songs()->at(1)->steps()->at(1)->set_repeat_count(7); + + std::stringstream writeStream(std::ios::in | std::ios::out | std::ios::binary); + kaitai::kstream writeIo(&writeStream); + parsed._set_io(&writeIo); + parsed._check(); + parsed._write(); + + const auto rewritten = writeStream.str(); + std::vector bytes(rewritten.begin(), rewritten.end()); + + mpc::Mpc mpc; + mpc::TestMpc::initializeTestMpcWithoutMidiServices(mpc); + auto allFile = installAllBytes(mpc, bytes, "MUTATED.ALL"); + REQUIRE(allFile); + + mpc::disk::AllLoader::loadEverythingFromFile(mpc, allFile.get()); + mpc.getSequencer()->getStateManager()->drainQueue(); + + auto userScreen = mpc.screens->get(); + auto song1 = mpc.getSequencer()->getSong(1); + REQUIRE(userScreen); + REQUIRE_THAT(userScreen->getTempo(), Catch::Matchers::WithinAbs(99.9, 0.001)); + REQUIRE(song1->getStep(mpc::SongStepIndex(1)).repetitionCount == 7); +} + +TEST_CASE("AllParser reads broad mutated defaults, track routing, and song loop semantics", "[kaitai-all]") +{ + const auto bytes = buildBroadMutatedAllBytes(); + + std::stringstream parseStream( + std::string(bytes.begin(), bytes.end()), + std::ios::in | std::ios::out | std::ios::binary + ); + kaitai::kstream parseIo(&parseStream); + mpc2000xl_all_t parsed(&parseIo); + parsed._read(); + + REQUIRE(parsed.defaults()->tempo() == 875); + REQUIRE(parsed.defaults()->numerator() == 7); + REQUIRE(parsed.defaults()->denominator() == 8); + REQUIRE(parsed.defaults()->buses()->at(0) == mpc2000xl_all_t::BUS_DRUM4); + REQUIRE(parsed.defaults()->devices()->at(0) == 31); + + std::vector usedSequenceBodies; + for (const auto& sequence : *parsed.sequences()) { + if (sequence->body() != nullptr) { + usedSequenceBodies.push_back(sequence->body()); + } + } + REQUIRE(usedSequenceBodies.size() >= 2U); + REQUIRE(usedSequenceBodies.at(0)->tracks()->bus()->at(0) == mpc2000xl_all_t::BUS_DRUM3); + REQUIRE(usedSequenceBodies.at(0)->tracks()->device()->at(0) == 29); + REQUIRE(usedSequenceBodies.at(1)->tracks()->bus()->at(0) == mpc2000xl_all_t::BUS_MIDI); + REQUIRE(usedSequenceBodies.at(1)->tracks()->device()->at(0) == 5); + + REQUIRE(parsed.sequencer()->active_sequence() == 1); + REQUIRE(parsed.sequencer()->active_track() == 3); + REQUIRE(parsed.sequencer()->master_tempo() == 875); + + REQUIRE(parsed.songs()->at(0)->steps()->at(0)->sequence_index() == 1); + REQUIRE(parsed.songs()->at(0)->steps()->at(0)->repeat_count() == 4); + REQUIRE(parsed.songs()->at(0)->steps()->at(1)->sequence_index() == 0); + REQUIRE(parsed.songs()->at(0)->steps()->at(1)->repeat_count() == 5); + REQUIRE(parsed.songs()->at(0)->loop_first_step() == 0); + REQUIRE(parsed.songs()->at(0)->loop_last_step() == 1); + REQUIRE(parsed.songs()->at(0)->is_loop_enabled()); +} + +TEST_CASE("AllLoader loads broad mutated defaults, track routing, and song loop semantics", "[kaitai-all]") +{ + const auto bytes = buildBroadMutatedAllBytes(); + + mpc::Mpc mpc; + mpc::TestMpc::initializeTestMpcWithoutMidiServices(mpc); + auto allFile = installAllBytes(mpc, bytes, "BROAD_MUTATED.ALL"); + REQUIRE(allFile); + + mpc::disk::AllLoader::loadEverythingFromFile(mpc, allFile.get()); + mpc.getSequencer()->getStateManager()->drainQueue(); + + auto sequencer = mpc.getSequencer(); + auto userScreen = mpc.screens->get(); + REQUIRE(userScreen); + REQUIRE_THAT(userScreen->getTempo(), Catch::Matchers::WithinAbs(87.5, 0.001)); + REQUIRE(userScreen->getBusType() == mpc::sequencer::BusType::DRUM4); + REQUIRE(userScreen->getDevice() == 31); + REQUIRE(userScreen->getTimeSig().numerator == 7); + REQUIRE(userScreen->getTimeSig().denominator == 8); + + REQUIRE(sequencer->getSelectedSequenceIndex() == 1); + REQUIRE(sequencer->getSelectedTrackIndex() == 3); + + auto sequence0 = sequencer->getSequence(0); + auto sequence1 = sequencer->getSequence(1); + REQUIRE(sequence0->getTrack(0)->getBusType() == mpc::sequencer::BusType::DRUM3); + REQUIRE(sequence0->getTrack(0)->getDeviceIndex() == 29); + REQUIRE(sequence1->getTrack(0)->getBusType() == mpc::sequencer::BusType::MIDI); + REQUIRE(sequence1->getTrack(0)->getDeviceIndex() == 5); + + auto song0 = sequencer->getSong(0); + REQUIRE(song0->getStep(mpc::SongStepIndex(0)).sequenceIndex == 1); + REQUIRE(song0->getStep(mpc::SongStepIndex(0)).repetitionCount == 4); + REQUIRE(song0->getStep(mpc::SongStepIndex(1)).sequenceIndex == 0); + REQUIRE(song0->getStep(mpc::SongStepIndex(1)).repetitionCount == 5); + REQUIRE(song0->getFirstLoopStepIndex() == mpc::SongStepIndex(0)); + REQUIRE(song0->getLastLoopStepIndex() == mpc::SongStepIndex(1)); + REQUIRE(song0->isLoopEnabled()); +} + +TEST_CASE("AllLoader loads representative ALL sequence event types through production seam", "[kaitai-all]") +{ + mpc::Mpc sourceMpc; + mpc::TestMpc::initializeTestMpc(sourceMpc); + + auto sourceSequencer = sourceMpc.getSequencer(); + auto sourceStateManager = sourceSequencer->getStateManager(); + auto sourceSequence = sourceSequencer->getSequence(0); + sourceSequence->init(1); + sourceStateManager->drainQueue(); + + auto footswitchController = + sourceMpc.clientEventController->getClientMidiEventController() + ->getFootswitchAssignmentController(); + for (int i = 0; i < footswitchController->bindings.size(); ++i) + { + std::visit( + [i](auto& binding) + { + binding.number = 20 + i; + }, + footswitchController->bindings[i] + ); + } + + auto sourceTrack = sourceSequence->getTrack(63); + auto openNote = sourceTrack->recordNoteEventNonLive( + 0, + mpc::NoteNumber(60), + mpc::Velocity(127), + 0 + ); + sourceTrack->finalizeNoteEventNonLive(openNote, mpc::Duration(1600)); + sourceStateManager->drainQueue(); + sourceTrack->getNoteEvents().front()->setVariationType(mpc::NoteVariationTypeFilter); + sourceStateManager->drainQueue(); + sourceTrack->getNoteEvents().front()->setVariationValue(mpc::NoteVariationValue(20)); + sourceStateManager->drainQueue(); + + mpc::sequencer::EventData programChange; + programChange.type = mpc::sequencer::EventType::ProgramChange; + programChange.tick = 96; + programChange.programChangeProgramIndex = mpc::ProgramIndex(7); + sourceTrack->acquireAndInsertEvent(programChange); + + mpc::sequencer::EventData controlChange; + controlChange.type = mpc::sequencer::EventType::ControlChange; + controlChange.tick = 120; + controlChange.controllerNumber = 12; + controlChange.controllerValue = 34; + sourceTrack->acquireAndInsertEvent(controlChange); + + mpc::sequencer::EventData pitchBend; + pitchBend.type = mpc::sequencer::EventType::PitchBend; + pitchBend.tick = 144; + pitchBend.amount = -1234; + sourceTrack->acquireAndInsertEvent(pitchBend); + + mpc::sequencer::EventData polyPressure; + polyPressure.type = mpc::sequencer::EventType::PolyPressure; + polyPressure.tick = 168; + polyPressure.noteNumber = mpc::NoteNumber(61); + polyPressure.amount = 45; + sourceTrack->acquireAndInsertEvent(polyPressure); + + mpc::sequencer::EventData channelPressure; + channelPressure.type = mpc::sequencer::EventType::ChannelPressure; + channelPressure.tick = 192; + channelPressure.amount = 55; + sourceTrack->acquireAndInsertEvent(channelPressure); + + mpc::sequencer::EventData systemExclusive; + systemExclusive.type = mpc::sequencer::EventType::SystemExclusive; + systemExclusive.tick = 216; + systemExclusive.sysExByteA = 0x12; + systemExclusive.sysExByteB = 0x34; + sourceTrack->acquireAndInsertEvent(systemExclusive); + + mpc::sequencer::EventData mixer; + mixer.type = mpc::sequencer::EventType::Mixer; + mixer.tick = 240; + mixer.mixerParameter = 3; + mixer.mixerPad = 9; + mixer.mixerValue = 87; + sourceTrack->acquireAndInsertEvent(mixer); + + sourceStateManager->drainQueue(); + + const auto bytes = mpc::file::kaitai::AllIo::save(sourceMpc); + + mpc::Mpc loadedMpc; + mpc::TestMpc::initializeTestMpc(loadedMpc); + auto allFile = installAllBytes(loadedMpc, bytes, "EVENTS.ALL"); + REQUIRE(allFile); + + mpc::disk::AllLoader::loadEverythingFromFile(loadedMpc, allFile.get()); + loadedMpc.getSequencer()->getStateManager()->drainQueue(); + + auto loadedSequence = loadedMpc.getSequencer()->getSequence(0); + REQUIRE(loadedSequence->isUsed()); + auto loadedTrack = loadedSequence->getTrack(63); + REQUIRE(loadedTrack->getEvents().size() == 8); + + auto noteEvent = + std::dynamic_pointer_cast(loadedTrack->getEvent(0)); + REQUIRE(noteEvent); + REQUIRE(noteEvent->getTick() == 0); + REQUIRE(noteEvent->getNote() == 60); + REQUIRE(noteEvent->getVelocity() == 127); + REQUIRE(noteEvent->getDuration() == 1600); + REQUIRE(noteEvent->getVariationType() == 3); + REQUIRE(noteEvent->getVariationValue() == 20); + + auto loadedProgramChange = + std::dynamic_pointer_cast(loadedTrack->getEvent(1)); + REQUIRE(loadedProgramChange); + REQUIRE(loadedProgramChange->getTick() == 96); + REQUIRE(loadedProgramChange->getProgram() == 7); + + auto loadedControlChange = + std::dynamic_pointer_cast(loadedTrack->getEvent(2)); + REQUIRE(loadedControlChange); + REQUIRE(loadedControlChange->getTick() == 120); + REQUIRE(loadedControlChange->getController() == 12); + REQUIRE(loadedControlChange->getAmount() == 34); + + auto loadedPitchBend = + std::dynamic_pointer_cast(loadedTrack->getEvent(3)); + REQUIRE(loadedPitchBend); + REQUIRE(loadedPitchBend->getTick() == 144); + REQUIRE(loadedPitchBend->getAmount() == -1234); + + auto loadedPolyPressure = + std::dynamic_pointer_cast(loadedTrack->getEvent(4)); + REQUIRE(loadedPolyPressure); + REQUIRE(loadedPolyPressure->getTick() == 168); + REQUIRE(loadedPolyPressure->getNote() == 61); + REQUIRE(loadedPolyPressure->getAmount() == 45); + + auto loadedChannelPressure = + std::dynamic_pointer_cast(loadedTrack->getEvent(5)); + REQUIRE(loadedChannelPressure); + REQUIRE(loadedChannelPressure->getTick() == 192); + REQUIRE(loadedChannelPressure->getAmount() == 55); + + auto loadedSystemExclusive = + std::dynamic_pointer_cast(loadedTrack->getEvent(6)); + REQUIRE(loadedSystemExclusive); + REQUIRE(loadedSystemExclusive->getTick() == 216); + REQUIRE(loadedSystemExclusive->getByteA() == 0x12); + REQUIRE(loadedSystemExclusive->getByteB() == 0x34); + + auto loadedMixer = + std::dynamic_pointer_cast(loadedTrack->getEvent(7)); + REQUIRE(loadedMixer); + REQUIRE(loadedMixer->getTick() == 240); + REQUIRE(loadedMixer->getParameter() == 3); + REQUIRE(loadedMixer->getPad() == 9); + REQUIRE(loadedMixer->getValue() == 87); +} + +TEST_CASE("Kaitai MPC2000 ALL preserves all note variation types through production seam", "[kaitai-all]") +{ + mpc::Mpc sourceMpc; + mpc::TestMpc::initializeTestMpc(sourceMpc); + + auto sourceSequencer = sourceMpc.getSequencer(); + auto sourceStateManager = sourceSequencer->getStateManager(); + auto sourceSequence = sourceSequencer->getSequence(0); + sourceSequence->init(0); + sourceStateManager->drainQueue(); + + auto sourceTrack = sourceSequence->getTrack(0); + + const std::array, 4> noteVariations{{ + {mpc::NoteVariationTypeTune, 11}, + {mpc::NoteVariationTypeDecay, 22}, + {mpc::NoteVariationTypeAttack, 33}, + {mpc::NoteVariationTypeFilter, 44}, + }}; + + int tick = 0; + int noteNumber = 60; + for (const auto& [variationType, variationValue] : noteVariations) + { + auto noteEvent = sourceTrack->recordNoteEventNonLive( + tick, + mpc::NoteNumber(noteNumber), + mpc::Velocity(100), + 0 + ); + sourceTrack->finalizeNoteEventNonLive(noteEvent, mpc::Duration(24)); + sourceStateManager->drainQueue(); + + auto insertedNote = sourceTrack->getNoteEvents().back(); + insertedNote->setVariationType(variationType); + sourceStateManager->drainQueue(); + insertedNote = sourceTrack->getNoteEvents().back(); + insertedNote->setVariationValue(mpc::NoteVariationValue(variationValue)); + sourceStateManager->drainQueue(); + + tick += 24; + noteNumber += 1; + } + + REQUIRE(sourceTrack->getEvents().size() == noteVariations.size()); + for (size_t i = 0; i < noteVariations.size(); ++i) + { + const auto& [expectedVariationType, expectedVariationValue] = noteVariations[i]; + auto sourceNote = + std::dynamic_pointer_cast(sourceTrack->getEvent(static_cast(i))); + REQUIRE(sourceNote); + REQUIRE(sourceNote->getVariationType() == expectedVariationType); + REQUIRE(sourceNote->getVariationValue() == expectedVariationValue); + } + + const auto bytes = mpc::file::kaitai::AllIo::save(sourceMpc); + + mpc::Mpc loadedMpc; + mpc::TestMpc::initializeTestMpc(loadedMpc); + auto allFile = installAllBytes(loadedMpc, bytes, "VARIANTS.ALL"); + REQUIRE(allFile); + + mpc::disk::AllLoader::loadEverythingFromFile(loadedMpc, allFile.get()); + loadedMpc.getSequencer()->getStateManager()->drainQueue(); + + auto loadedTrack = loadedMpc.getSequencer()->getSequence(0)->getTrack(0); + REQUIRE(loadedTrack->getEvents().size() == noteVariations.size()); + + for (size_t i = 0; i < noteVariations.size(); ++i) + { + const auto& [expectedVariationType, expectedVariationValue] = noteVariations[i]; + auto loadedNote = + std::dynamic_pointer_cast(loadedTrack->getEvent(static_cast(i))); + REQUIRE(loadedNote); + REQUIRE(loadedNote->getTick() == static_cast(i) * 24); + REQUIRE(loadedNote->getNote() == 60 + static_cast(i)); + REQUIRE(loadedNote->getDuration() == 24); + REQUIRE(loadedNote->getVariationType() == expectedVariationType); + REQUIRE(loadedNote->getVariationValue() == expectedVariationValue); + } +} + +TEST_CASE("Kaitai MPC2000 ALL preserves minimum and maximum pitch bend through production seam", "[kaitai-all]") +{ + mpc::Mpc sourceMpc; + mpc::TestMpc::initializeTestMpc(sourceMpc); + + auto sourceSequencer = sourceMpc.getSequencer(); + auto sourceStateManager = sourceSequencer->getStateManager(); + auto sourceSequence = sourceSequencer->getSequence(0); + sourceSequence->init(0); + sourceStateManager->drainQueue(); + + auto sourceTrack = sourceSequence->getTrack(0); + + mpc::sequencer::EventData minPitchBend; + minPitchBend.type = mpc::sequencer::EventType::PitchBend; + minPitchBend.tick = 0; + minPitchBend.amount = -8192; + sourceTrack->acquireAndInsertEvent(minPitchBend); + + mpc::sequencer::EventData maxPitchBend; + maxPitchBend.type = mpc::sequencer::EventType::PitchBend; + maxPitchBend.tick = 24; + maxPitchBend.amount = 8191; + sourceTrack->acquireAndInsertEvent(maxPitchBend); + + sourceStateManager->drainQueue(); + + const auto bytes = mpc::file::kaitai::AllIo::save(sourceMpc); + + mpc::Mpc loadedMpc; + mpc::TestMpc::initializeTestMpc(loadedMpc); + auto allFile = installAllBytes(loadedMpc, bytes, "PITCHMAX.ALL"); + REQUIRE(allFile); + + mpc::disk::AllLoader::loadEverythingFromFile(loadedMpc, allFile.get()); + loadedMpc.getSequencer()->getStateManager()->drainQueue(); + + auto loadedTrack = loadedMpc.getSequencer()->getSequence(0)->getTrack(0); + REQUIRE(loadedTrack->getEvents().size() == 2); + + auto loadedMin = + std::dynamic_pointer_cast(loadedTrack->getEvent(0)); + REQUIRE(loadedMin); + REQUIRE(loadedMin->getTick() == 0); + REQUIRE(loadedMin->getAmount() == -8192); + + auto loadedMax = + std::dynamic_pointer_cast(loadedTrack->getEvent(1)); + REQUIRE(loadedMax); + REQUIRE(loadedMax->getTick() == 24); + REQUIRE(loadedMax->getAmount() == 8191); +} + +TEST_CASE("Kaitai MPC60 v2 ALL parser exposes expected embedded sequence and song structure", "[kaitai-all][real-mpc60]") +{ + auto fs = cmrc::mpctest::get_filesystem(); + auto file = fs.open("test/RealMpc60/All/MPC60_V214_SONG_2REALSTEPS.ALL"); + const std::string bytes( + std::string_view(file.begin(), file.end() - file.begin()) + ); + + std::stringstream parseStream( + bytes, + std::ios::in | std::ios::out | std::ios::binary + ); + kaitai::kstream parseIo(&parseStream); + mpc60_all_v2_t parsed(&parseIo); + parsed._read(); + + REQUIRE(parsed.body()->sequences()->size() == 1U); + REQUIRE(parsed.body()->sequences()->at(0)->sequence_header()->sequence_name() == "SEQ01 "); + REQUIRE(parsed.body()->sequences()->at(0)->events()->size() == 5U); + + REQUIRE(parsed.body()->songs()->size() == 2U); + REQUIRE(parsed.body()->songs()->at(0)->step_count() == 2); + + const auto* songBody = + dynamic_cast(parsed.body()->songs()->at(0)->body()); + REQUIRE(songBody != nullptr); + REQUIRE(songBody->song_number() == 1); + REQUIRE(songBody->song_name() == "SONG01 "); + REQUIRE(songBody->steps()->size() == 2U); + REQUIRE(songBody->steps()->at(0)->sequence_number() == 1); + REQUIRE(songBody->steps()->at(0)->repeats() == 1); + REQUIRE(songBody->steps()->at(1)->sequence_number() == 1); + REQUIRE(songBody->steps()->at(1)->repeats() == 1); + + REQUIRE(parsed.body()->songs()->at(1)->step_count() == 0); +} + +TEST_CASE("Kaitai MPC60 v2 ALL sequence meta infos expose expected names and usedness", "[kaitai-all][real-mpc60]") +{ + mpc::Mpc mpc; + mpc::TestMpc::initializeTestMpcWithoutMidiServices(mpc); + + auto allFile = installResourceFile( + mpc, + "test/RealMpc60/All/MPC60_V214_ALL_SEQS.ALL", + "M60SEQ.ALL" + ); + REQUIRE(allFile); + + const auto metaInfos = mpc::disk::AllLoader::loadSequenceMetaInfosFromFile(mpc, allFile.get()); + REQUIRE(metaInfos.size() == 1U); + REQUIRE(metaInfos[0].used); + REQUIRE(metaInfos[0].name == "SEQ01"); +} + +TEST_CASE("Kaitai MPC60 v2 ALL loads one embedded sequence with expected defaults", "[kaitai-all][real-mpc60]") +{ + mpc::Mpc kaitaiMpc; + mpc::TestMpc::initializeTestMpcWithoutMidiServices(kaitaiMpc); + auto kaitaiFile = installResourceFile( + kaitaiMpc, + "test/RealMpc60/All/MPC60_V214_ALL_SEQS.ALL", + "M60SEQ.ALL" + ); + REQUIRE(kaitaiFile); + + const auto loaded = mpc::disk::AllLoader::loadOneSequenceFromFile( + kaitaiMpc, + kaitaiFile.get(), + mpc::SequenceIndex(0), + mpc::SequenceIndex(10) + ); + REQUIRE(loaded); + kaitaiMpc.getSequencer()->getStateManager()->drainQueue(); + + REQUIRE(loaded->isUsed()); + REQUIRE(loaded->getName() == "SEQ01"); + REQUIRE(loaded->getLastBarIndex() == 1); + REQUIRE_THAT(loaded->getInitialTempo(), Catch::Matchers::WithinAbs(120.0, 0.001)); + REQUIRE(loaded->getTimeSignatureFromBarIndex(0).numerator == 4); + REQUIRE(loaded->getTimeSignatureFromBarIndex(0).denominator == 4); + REQUIRE(loaded->isLoopEnabled()); + + REQUIRE(loaded->getTrack(0)->getEvents().size() == 1U); + auto loadedNote = + std::dynamic_pointer_cast( + loaded->getTrack(0)->getEvents().at(0)); + REQUIRE(loadedNote); + REQUIRE(loadedNote->getTick() == 0); + REQUIRE(loadedNote->getNote() == 1); + REQUIRE(loadedNote->getVelocity() == 64); + REQUIRE(loadedNote->getDuration() == 96); +} + +TEST_CASE("Kaitai MPC60 v2 ALL loads sequences and songs through the production seam", "[kaitai-all][real-mpc60]") +{ + mpc::Mpc mpc; + mpc::TestMpc::initializeTestMpcWithoutMidiServices(mpc); + + auto allFile = installResourceFile( + mpc, + "test/RealMpc60/All/MPC60_V214_SONG_2REALSTEPS.ALL", + "M60SONG.ALL" + ); + REQUIRE(allFile); + + mpc::disk::AllLoader::loadEverythingFromFile(mpc, allFile.get()); + mpc.getSequencer()->getStateManager()->drainQueue(); + + auto sequencer = mpc.getSequencer(); + auto sequence0 = sequencer->getSequence(0); + REQUIRE(sequence0->isUsed()); + REQUIRE(sequence0->getName() == "SEQ01"); + REQUIRE_THAT(sequence0->getInitialTempo(), Catch::Matchers::WithinAbs(120.0, 0.001)); + + auto song0 = sequencer->getSong(0); + auto song1 = sequencer->getSong(1); + REQUIRE(song0->isUsed()); + REQUIRE(song0->getName() == "SONG01"); + REQUIRE(song0->getStepCount() == 2); + REQUIRE(song0->getStep(mpc::SongStepIndex(0)).sequenceIndex == 0); + REQUIRE(song0->getStep(mpc::SongStepIndex(0)).repetitionCount == 1); + REQUIRE(song0->getStep(mpc::SongStepIndex(1)).sequenceIndex == 0); + REQUIRE(song0->getStep(mpc::SongStepIndex(1)).repetitionCount == 1); + REQUIRE_FALSE(song0->isLoopEnabled()); + REQUIRE_FALSE(song1->isUsed()); +} diff --git a/src/test/KaitaiApsTest.cpp b/src/test/KaitaiApsTest.cpp new file mode 100644 index 000000000..d5e60a224 --- /dev/null +++ b/src/test/KaitaiApsTest.cpp @@ -0,0 +1,803 @@ +#include + +#include "TestMpc.hpp" +#include "Mpc.hpp" +#include "disk/AbstractDisk.hpp" +#include "disk/ApsLoader.hpp" +#include "disk/MpcFile.hpp" +#include "engine/IndivFxMixer.hpp" +#include "engine/EngineHost.hpp" +#include "engine/StereoMixer.hpp" +#include "file/kaitai/ApsIo.hpp" +#include "file/kaitai/generated/mpc2000xl_aps.h" +#include "lcdgui/screens/DrumScreen.hpp" +#include "lcdgui/screens/MixerSetupScreen.hpp" +#include "lcdgui/screens/PgmAssignScreen.hpp" +#include "performance/PerformanceManager.hpp" +#include "sampler/Pad.hpp" +#include "sampler/Program.hpp" +#include "sampler/Sampler.hpp" +#include "sampler/SoundGenerationMode.hpp" +#include "sequencer/Bus.hpp" +#include "sequencer/Sequencer.hpp" + +#include +#include + +#include +#include +#include +#include +#include +#include + +CMRC_DECLARE(mpctest); + +namespace { + +std::vector rewriteAllPgmsApsWithMutations( + const std::function& mutate) +{ + auto fs = cmrc::mpctest::get_filesystem(); + auto file = fs.open("test/ApsLoading/ALL_PGMS.APS"); + const std::string originalBytes( + std::string_view(file.begin(), file.end() - file.begin()) + ); + + std::stringstream parseStream( + originalBytes, + std::ios::in | std::ios::out | std::ios::binary + ); + kaitai::kstream parseIo(&parseStream); + mpc2000xl_aps_t parsed(&parseIo); + parsed._read(); + + mutate(parsed); + + std::stringstream writeStream(std::ios::in | std::ios::out | std::ios::binary); + kaitai::kstream writeIo(&writeStream); + parsed._set_io(&writeIo); + parsed._check(); + parsed._write(); + + const auto rewrittenBytes = writeStream.str(); + return std::vector(rewrittenBytes.begin(), rewrittenBytes.end()); +} + +std::string describeFirstByteDiff( + const std::string& lhs, + const std::string& rhs) +{ + const auto mismatch = std::mismatch(lhs.begin(), lhs.end(), rhs.begin(), rhs.end()); + if (mismatch.first == lhs.end() && mismatch.second == rhs.end()) + { + return "no diff"; + } + + const auto offset = static_cast(mismatch.first - lhs.begin()); + std::ostringstream oss; + oss << "first diff at offset " << offset + << ": lhs=0x" + << std::hex << std::setw(2) << std::setfill('0') + << (static_cast(static_cast(*mismatch.first))) + << " rhs=0x" + << std::hex << std::setw(2) << std::setfill('0') + << (static_cast(static_cast(*mismatch.second))); + return oss.str(); +} + +void applyBroadApsMutation(mpc2000xl_aps_t& parsed) +{ + auto* program0 = parsed.aps_programs()->at(0)->body(); + auto* note35 = program0->note_parameters()->at(0).get(); + note35->set_sound_index(0xFFFF); + note35->set_sound_generation_mode(mpc2000xl_pgm_t::SOUND_GENERATION_MODE_DCY_SW); + note35->set_velocity_range_lower(1); + note35->set_also_play_use_note_1(41); + note35->set_velocity_range_upper(127); + note35->set_also_play_use_note_2(42); + note35->set_voice_overlap_mode(mpc2000xl_pgm_t::VOICE_OVERLAP_MODE_NOTE_OFF); + note35->set_mute_assign_1(43); + note35->set_mute_assign_2(44); + note35->set_tune(-120); + note35->set_attack(99); + note35->set_decay(98); + note35->set_decay_mode(mpc2000xl_pgm_t::DECAY_MODE_START); + note35->set_cutoff(17); + note35->set_resonance(18); + note35->set_velocity_envelope_to_filter_attack(19); + note35->set_velocity_envelope_to_filter_decay(20); + note35->set_velocity_envelope_to_filter_amount(21); + note35->set_velocity_to_level(22); + note35->set_velocity_to_attack(23); + note35->set_velocity_to_start(24); + note35->set_velocity_to_cutoff(25); + note35->set_slider_parameter(mpc2000xl_pgm_t::SLIDER_PARAMETER_FILTER); + note35->set_velocity_to_pitch(26); + + auto* mixer35 = program0->pad_mixers()->at(0).get(); + mixer35->set_fx_output(mpc2000xl_pgm_t::FX_OUTPUT_R2); + mixer35->set_volume(12); + mixer35->set_pan(13); + mixer35->set_volume_individual(14); + mixer35->set_output(2); + mixer35->set_effects_send_level(15); + + program0->slider()->set_note(52); + program0->slider()->set_tune_low(-119); + program0->slider()->set_tune_high(118); + program0->slider()->set_decay_low(11); + program0->slider()->set_decay_high(12); + program0->slider()->set_attack_low(13); + program0->slider()->set_attack_high(14); + program0->slider()->set_filter_low(-15); + program0->slider()->set_filter_high(16); + program0->set_program_change(126); + program0->pad_to_note_mapping()->at(0) = 50; + parsed.master_pad_to_note_mapping()->at(0) = 50; + + auto* drum0 = parsed.drum1(); + drum0->set_receive_program_change(mpc2000xl_aps_t::NO_YES_FALSE); + drum0->set_receive_midi_volume(mpc2000xl_aps_t::NO_YES_FALSE); + drum0->set_program(1); + drum0->set_receive_program_change_duplicate(mpc2000xl_aps_t::NO_YES_FALSE); + drum0->set_receive_midi_volume_duplicate(mpc2000xl_aps_t::NO_YES_FALSE); + + auto* globals = parsed.global_parameters(); + globals->set_pad_to_internal_sound(mpc2000xl_aps_t::NO_YES_TRUE); + globals->set_pad_assign(mpc2000xl_aps_t::PAD_ASSIGN_MASTERS); + globals->set_indiv_fx_source(mpc2000xl_aps_t::MIX_SOURCE_DRUM); + globals->set_stereo_mix_source(mpc2000xl_aps_t::MIX_SOURCE_DRUM); + globals->set_record_mix_changes(mpc2000xl_aps_t::NO_YES_TRUE); + globals->set_copy_pgm_mix_to_drum(mpc2000xl_aps_t::NO_YES_FALSE); + globals->set_fx_drum(3); + globals->set_master_level(2); +} + +void prepareApsResources(mpc::Mpc& mpc) +{ + auto disk = mpc.getDisk(); + auto fs = cmrc::mpctest::get_filesystem(); + + for (auto&& entry : fs.iterate_directory("test/ApsLoading")) { + auto file = fs.open("test/ApsLoading/" + entry.filename()); + std::vector data(file.begin(), file.end()); + auto newFile = disk->newFile(entry.filename()); + newFile->setFileData(data); + } + + disk->initFiles(); +} + +std::shared_ptr installApsResourceFile( + mpc::Mpc& mpc, + const std::string& resourcePath, + const std::string& fileName) +{ + auto disk = mpc.getDisk(); + auto fs = cmrc::mpctest::get_filesystem(); + auto file = fs.open(resourcePath); + std::vector data(file.begin(), file.end()); + auto newFile = disk->newFile(fileName); + newFile->setFileData(data); + disk->initFiles(); + return newFile; +} + +void loadAllPgmsAps(mpc::Mpc& mpc) +{ + prepareApsResources(mpc); + auto apsFile = mpc.getDisk()->getFile("ALL_PGMS.APS"); + constexpr bool headless = true; + mpc::disk::ApsLoader::load(mpc, apsFile, headless); + mpc.getEngineHost()->prepareProcessBlock(512); +} + +std::shared_ptr loadAllPgmsApsFileFromBytes( + mpc::Mpc& mpc, + const std::vector& apsBytes) +{ + prepareApsResources(mpc); + auto apsFile = mpc.getDisk()->getFile("ALL_PGMS.APS"); + auto mutableBytes = apsBytes; + apsFile->setFileData(mutableBytes); + mpc.getDisk()->initFiles(); + return apsFile; +} + +void loadAllPgmsApsWithoutRender(mpc::Mpc& mpc, const std::vector& apsBytes) +{ + auto apsFile = loadAllPgmsApsFileFromBytes(mpc, apsBytes); + constexpr bool headless = true; + mpc::disk::ApsLoader::load(mpc, apsFile, headless); + mpc.getPerformanceManager().lock()->drainQueue(); +} + +} + +TEST_CASE("Kaitai MPC2000 APS parses and rewrites ALL_PGMS", "[kaitai-aps]") +{ + auto fs = cmrc::mpctest::get_filesystem(); + auto file = fs.open("test/ApsLoading/ALL_PGMS.APS"); + const std::string originalBytes( + std::string_view(file.begin(), file.end() - file.begin()) + ); + + std::stringstream parseStream( + originalBytes, + std::ios::in | std::ios::out | std::ios::binary + ); + kaitai::kstream parseIo(&parseStream); + mpc2000xl_aps_t parsed(&parseIo); + parsed._read(); + + REQUIRE(parsed.magic() == std::string("\x0a\x05", 2)); + REQUIRE(parsed.sound_count() == 3); + REQUIRE(parsed.sound_names() != nullptr); + REQUIRE(parsed.sound_names()->size() == 3U); + REQUIRE(parsed.sound_names()->at(0).substr(0, 6) == "sound1"); + REQUIRE(parsed.sound_names()->at(1).substr(0, 6) == "sound2"); + REQUIRE(parsed.sound_names()->at(2).substr(0, 6) == "sound3"); + REQUIRE(parsed.aps_programs() != nullptr); + REQUIRE(parsed.aps_programs()->size() >= 2U); + REQUIRE(parsed.aps_programs()->at(0)->index() == 0); + REQUIRE(parsed.aps_programs()->at(0)->body() != nullptr); + REQUIRE(parsed.aps_programs()->at(0)->body()->name().substr(0, 8) == "PROGRAM1"); + REQUIRE(parsed.aps_programs()->at(0)->body()->note_parameters()->at(0)->sound_index() == 0); + REQUIRE(parsed.aps_programs()->at(0)->body()->note_parameters()->at(1)->sound_index() == 1); + REQUIRE(parsed.aps_programs()->at(1)->index() == 1); + REQUIRE(parsed.aps_programs()->at(1)->body() != nullptr); + REQUIRE(parsed.aps_programs()->at(1)->body()->name().substr(0, 8) == "PROGRAM2"); + REQUIRE(parsed.aps_programs()->at(1)->body()->note_parameters()->at(0)->sound_index() == 1); + REQUIRE(parsed.aps_programs()->at(1)->body()->note_parameters()->at(1)->sound_index() == 2); + std::stringstream writeStream(std::ios::in | std::ios::out | std::ios::binary); + kaitai::kstream writeIo(&writeStream); + parsed._set_io(&writeIo); + parsed._check(); + parsed._write(); + + const auto rewrittenBytes = writeStream.str(); + REQUIRE(rewrittenBytes.size() == originalBytes.size()); + INFO(describeFirstByteDiff(rewrittenBytes, originalBytes)); + REQUIRE(std::equal(rewrittenBytes.begin(), rewrittenBytes.end(), originalBytes.begin())); +} + +TEST_CASE("Kaitai MPC2000 APS parses and rewrites real 2KXL ALL_PGMS", "[kaitai-aps][real-2kxl]") +{ + auto fs = cmrc::mpctest::get_filesystem(); + auto file = fs.open("test/RealMpc2000xl/Aps/ALL_PGMS.APS"); + const std::string originalBytes( + std::string_view(file.begin(), file.end() - file.begin()) + ); + + std::stringstream parseStream( + originalBytes, + std::ios::in | std::ios::out | std::ios::binary + ); + kaitai::kstream parseIo(&parseStream); + mpc2000xl_aps_t parsed(&parseIo); + parsed._read(); + + REQUIRE(parsed.magic() == std::string("\x0a\x05", 2)); + REQUIRE(parsed.sound_count() == 2); + REQUIRE(parsed.sound_names() != nullptr); + REQUIRE(parsed.sound_names()->size() == 2U); + REQUIRE(parsed.sound_names()->at(0).substr(0, 1) == "M"); + REQUIRE(parsed.sound_names()->at(1).substr(0, 2) == "ML"); + REQUIRE(parsed.name().substr(0, 8) == "ALL_PGMS"); + REQUIRE(parsed.aps_programs() != nullptr); + REQUIRE(parsed.aps_programs()->size() >= 1U); + REQUIRE(parsed.aps_programs()->at(0)->index() == 0); + REQUIRE(parsed.aps_programs()->at(0)->body() != nullptr); + + std::stringstream writeStream(std::ios::in | std::ios::out | std::ios::binary); + kaitai::kstream writeIo(&writeStream); + parsed._set_io(&writeIo); + parsed._check(); + parsed._write(); + + const auto rewrittenBytes = writeStream.str(); + REQUIRE(rewrittenBytes.size() == originalBytes.size()); + INFO(describeFirstByteDiff(rewrittenBytes, originalBytes)); + REQUIRE(std::equal(rewrittenBytes.begin(), rewrittenBytes.end(), originalBytes.begin())); +} + +TEST_CASE("Kaitai MPC2000 APS saves and reloads ALL_PGMS through production seam", "[kaitai-aps]") +{ + mpc::Mpc mpc; + mpc::TestMpc::initializeTestMpcWithoutMidiServices(mpc); + loadAllPgmsAps(mpc); + + REQUIRE(mpc.getSampler()->getProgramCount() == 2); + REQUIRE(mpc.getSampler()->getSoundCount() == 3); + + const auto directKaitaiBytes = mpc::file::kaitai::ApsIo::save(mpc, "ALL_PGMS"); + + std::stringstream parseStream( + std::string(directKaitaiBytes.begin(), directKaitaiBytes.end()), + std::ios::in | std::ios::out | std::ios::binary + ); + kaitai::kstream parseIo(&parseStream); + mpc2000xl_aps_t parsed(&parseIo); + parsed._read(); + + REQUIRE(parsed.magic() == std::string("\x0a\x05", 2)); + REQUIRE(parsed.sound_count() == 3); + REQUIRE(parsed.sound_names() != nullptr); + REQUIRE(parsed.sound_names()->at(0).substr(0, 6) == "sound1"); + REQUIRE(parsed.sound_names()->at(1).substr(0, 6) == "sound2"); + REQUIRE(parsed.sound_names()->at(2).substr(0, 6) == "sound3"); + REQUIRE(parsed.aps_programs() != nullptr); + REQUIRE(parsed.aps_programs()->at(0)->body() != nullptr); + REQUIRE(parsed.aps_programs()->at(0)->body()->name().substr(0, 8) == "PROGRAM1"); + REQUIRE(parsed.aps_programs()->at(1)->body() != nullptr); + REQUIRE(parsed.aps_programs()->at(1)->body()->name().substr(0, 8) == "PROGRAM2"); + + std::stringstream writeStream(std::ios::in | std::ios::out | std::ios::binary); + kaitai::kstream writeIo(&writeStream); + parsed._set_io(&writeIo); + parsed._check(); + parsed._write(); + + const auto kaitaiBytes = writeStream.str(); + REQUIRE(kaitaiBytes.size() == directKaitaiBytes.size()); + INFO(describeFirstByteDiff( + kaitaiBytes, + std::string(directKaitaiBytes.begin(), directKaitaiBytes.end()))); + REQUIRE(std::equal(kaitaiBytes.begin(), kaitaiBytes.end(), directKaitaiBytes.begin())); + + auto sampler = mpc.getSampler(); + sampler->deleteAllPrograms(false); + sampler->deleteAllSamples(); + + auto disk = mpc.getDisk(); + auto kaitaiFile = disk->newFile("KAPS.APS"); + std::vector kaitaiBytesVec(kaitaiBytes.begin(), kaitaiBytes.end()); + kaitaiFile->setFileData(kaitaiBytesVec); + disk->initFiles(); + + constexpr bool headless = true; + mpc::disk::ApsLoader::load(mpc, kaitaiFile, headless); + mpc.getEngineHost()->prepareProcessBlock(512); + + auto p1 = sampler->getProgram(0); + auto p2 = sampler->getProgram(1); + REQUIRE(sampler->getProgramCount() == 2); + REQUIRE(p1->getName() == "PROGRAM1"); + REQUIRE(p2->getName() == "PROGRAM2"); + REQUIRE(sampler->getSoundCount() == 3); + REQUIRE(sampler->getSoundName(0) == "sound1"); + REQUIRE(sampler->getSoundName(1) == "sound2"); + REQUIRE(sampler->getSoundName(2) == "sound3"); + REQUIRE(p1->getNoteParameters(35)->getSoundIndex() == 0); + REQUIRE(p1->getNoteParameters(36)->getSoundIndex() == 1); + REQUIRE(p2->getNoteParameters(35)->getSoundIndex() == 1); + REQUIRE(p2->getNoteParameters(36)->getSoundIndex() == 2); +} + +TEST_CASE("Kaitai MPC2000 APS saves MIDI program change separately from slider control change", "[kaitai-aps]") +{ + mpc::Mpc mpc; + mpc::TestMpc::initializeTestMpcWithoutMidiServices(mpc); + loadAllPgmsAps(mpc); + + auto p1 = mpc.getSampler()->getProgram(0); + REQUIRE(p1 != nullptr); + + p1->setMidiProgramChange(2); + mpc.getPerformanceManager().lock()->drainQueue(); + REQUIRE(p1->getMidiProgramChange() == 2); + p1->getSlider()->setControlChange(0); + + const auto bytesWithZeroControl = + mpc::file::kaitai::ApsIo::save(mpc, "ALL_PGMS"); + + std::stringstream parseStreamZero( + std::string(bytesWithZeroControl.begin(), bytesWithZeroControl.end()), + std::ios::in | std::ios::out | std::ios::binary + ); + kaitai::kstream parseIoZero(&parseStreamZero); + mpc2000xl_aps_t parsedZero(&parseIoZero); + parsedZero._read(); + REQUIRE(parsedZero.aps_programs()->at(0)->body()->program_change() == 1); + + p1->getSlider()->setControlChange(17); + const auto bytesWithNonZeroControl = + mpc::file::kaitai::ApsIo::save(mpc, "ALL_PGMS"); + + REQUIRE(bytesWithNonZeroControl == bytesWithZeroControl); +} + +TEST_CASE("Kaitai MPC2000 APS loads real 2KXL ALL_PGMS through production seam", "[kaitai-aps][real-2kxl]") +{ + mpc::Mpc mpc; + mpc::TestMpc::initializeTestMpcWithoutMidiServices(mpc); + + auto apsFile = installApsResourceFile( + mpc, + "test/RealMpc2000xl/Aps/ALL_PGMS.APS", + "ALL_PGMS.APS" + ); + REQUIRE(apsFile); + + constexpr bool headless = true; + mpc::file::kaitai::ApsIo::load(mpc, apsFile, headless); + mpc.getEngineHost()->prepareProcessBlock(512); + + auto sampler = mpc.getSampler(); + REQUIRE(sampler->getProgramCount() == 1); + REQUIRE(sampler->getSoundCount() == 0); + + auto p1 = sampler->getProgram(0); + REQUIRE(p1 != nullptr); + REQUIRE(p1->getName() == "NewPgm-A"); + REQUIRE(p1->getMidiProgramChange() == 1); + REQUIRE(p1->getSlider()->getControlChange() == 0); + REQUIRE(p1->getNoteParameters(35)->getSoundIndex() == -1); + REQUIRE(p1->getNoteParameters(36)->getSoundIndex() == -1); +} + +TEST_CASE("Kaitai APS rewrite preserves upper-bound MIDI program change", "[kaitai-aps]") +{ + auto fs = cmrc::mpctest::get_filesystem(); + auto file = fs.open("test/ApsLoading/ALL_PGMS.APS"); + const std::string originalBytes( + std::string_view(file.begin(), file.end() - file.begin()) + ); + + std::stringstream parseStream( + originalBytes, + std::ios::in | std::ios::out | std::ios::binary + ); + kaitai::kstream parseIo(&parseStream); + mpc2000xl_aps_t parsed(&parseIo); + parsed._read(); + parsed.aps_programs()->at(0)->body()->set_program_change(127); + + std::stringstream writeStream(std::ios::in | std::ios::out | std::ios::binary); + kaitai::kstream writeIo(&writeStream); + parsed._set_io(&writeIo); + parsed._check(); + parsed._write(); + + const auto rewrittenBytes = writeStream.str(); + std::stringstream reparsedStream( + rewrittenBytes, + std::ios::in | std::ios::out | std::ios::binary + ); + kaitai::kstream reparsedIo(&reparsedStream); + mpc2000xl_aps_t reparsed(&reparsedIo); + reparsed._read(); + REQUIRE(reparsed.aps_programs()->at(0)->body()->program_change() == 127); +} + +TEST_CASE("ApsLoader loads upper-bound MIDI program change", "[kaitai-aps]") +{ + auto fs = cmrc::mpctest::get_filesystem(); + auto file = fs.open("test/ApsLoading/ALL_PGMS.APS"); + const std::string originalBytes( + std::string_view(file.begin(), file.end() - file.begin()) + ); + + std::stringstream parseStream( + originalBytes, + std::ios::in | std::ios::out | std::ios::binary + ); + kaitai::kstream parseIo(&parseStream); + mpc2000xl_aps_t parsed(&parseIo); + parsed._read(); + parsed.aps_programs()->at(0)->body()->set_program_change(127); + + std::stringstream writeStream(std::ios::in | std::ios::out | std::ios::binary); + kaitai::kstream writeIo(&writeStream); + parsed._set_io(&writeIo); + parsed._check(); + parsed._write(); + + const auto rewrittenBytes = writeStream.str(); + std::vector apsBytes(rewrittenBytes.begin(), rewrittenBytes.end()); + + mpc::Mpc mpc; + mpc::TestMpc::initializeTestMpcWithoutMidiServices(mpc); + loadAllPgmsApsWithoutRender(mpc, apsBytes); + + auto p1 = mpc.getSampler()->getProgram(0); + REQUIRE(p1 != nullptr); + REQUIRE(p1->getMidiProgramChange() == 128); +} + +TEST_CASE("ApsLoader loads mutated note tune", "[kaitai-aps]") +{ + auto fs = cmrc::mpctest::get_filesystem(); + auto file = fs.open("test/ApsLoading/ALL_PGMS.APS"); + const std::string originalBytes( + std::string_view(file.begin(), file.end() - file.begin()) + ); + + std::stringstream parseStream( + originalBytes, + std::ios::in | std::ios::out | std::ios::binary + ); + kaitai::kstream parseIo(&parseStream); + mpc2000xl_aps_t parsed(&parseIo); + parsed._read(); + parsed.aps_programs()->at(0)->body()->note_parameters()->at(0)->set_tune(-120); + + std::stringstream writeStream(std::ios::in | std::ios::out | std::ios::binary); + kaitai::kstream writeIo(&writeStream); + parsed._set_io(&writeIo); + parsed._check(); + parsed._write(); + + const auto rewrittenBytes = writeStream.str(); + std::vector apsBytes(rewrittenBytes.begin(), rewrittenBytes.end()); + + mpc::Mpc mpc; + mpc::TestMpc::initializeTestMpcWithoutMidiServices(mpc); + loadAllPgmsApsWithoutRender(mpc, apsBytes); + + auto p1 = mpc.getSampler()->getProgram(0); + REQUIRE(p1 != nullptr); + REQUIRE(p1->getNoteParameters(35)->getTune() == -120); +} + +TEST_CASE("ApsLoader maps all four APS drum buses to distinct MPC drum buses", "[kaitai-aps]") +{ + const auto apsBytes = rewriteAllPgmsApsWithMutations([](mpc2000xl_aps_t& parsed) + { + parsed.drum1()->set_program(1); + parsed.drum1()->set_receive_program_change(mpc2000xl_aps_t::NO_YES_FALSE); + parsed.drum1()->set_receive_midi_volume(mpc2000xl_aps_t::NO_YES_TRUE); + + parsed.drum2()->set_program(2); + parsed.drum2()->set_receive_program_change(mpc2000xl_aps_t::NO_YES_TRUE); + parsed.drum2()->set_receive_midi_volume(mpc2000xl_aps_t::NO_YES_FALSE); + + parsed.drum3()->set_program(3); + parsed.drum3()->set_receive_program_change(mpc2000xl_aps_t::NO_YES_FALSE); + parsed.drum3()->set_receive_midi_volume(mpc2000xl_aps_t::NO_YES_FALSE); + + parsed.drum4()->set_program(4); + parsed.drum4()->set_receive_program_change(mpc2000xl_aps_t::NO_YES_TRUE); + parsed.drum4()->set_receive_midi_volume(mpc2000xl_aps_t::NO_YES_TRUE); + }); + + mpc::Mpc mpc; + mpc::TestMpc::initializeTestMpcWithoutMidiServices(mpc); + loadAllPgmsApsWithoutRender(mpc, apsBytes); + + auto drum0 = mpc.getSequencer()->getDrumBus(mpc::DrumBusIndex(0)); + auto drum1 = mpc.getSequencer()->getDrumBus(mpc::DrumBusIndex(1)); + auto drum2 = mpc.getSequencer()->getDrumBus(mpc::DrumBusIndex(2)); + auto drum3 = mpc.getSequencer()->getDrumBus(mpc::DrumBusIndex(3)); + + REQUIRE(drum0->getProgramIndex() == 1); + REQUIRE(!drum0->receivesPgmChange()); + REQUIRE(drum0->receivesMidiVolume()); + + REQUIRE(drum1->getProgramIndex() == 2); + REQUIRE(drum1->receivesPgmChange()); + REQUIRE(!drum1->receivesMidiVolume()); + + REQUIRE(drum2->getProgramIndex() == 3); + REQUIRE(!drum2->receivesPgmChange()); + REQUIRE(!drum2->receivesMidiVolume()); + + REQUIRE(drum3->getProgramIndex() == 4); + REQUIRE(drum3->receivesPgmChange()); + REQUIRE(drum3->receivesMidiVolume()); +} + +TEST_CASE("Kaitai APS parses broad mutated APS semantics", "[kaitai-aps]") +{ + const auto apsBytes = rewriteAllPgmsApsWithMutations(applyBroadApsMutation); + INFO("raw pad_assign byte=0x" + << std::hex << std::setw(2) << std::setfill('0') + << static_cast(static_cast(apsBytes.at(75)))); + INFO("raw mix-source byte=0x" + << std::hex << std::setw(2) << std::setfill('0') + << static_cast(static_cast(apsBytes.at(76)))); + INFO("raw record/copy byte=0x" + << std::hex << std::setw(2) << std::setfill('0') + << static_cast(static_cast(apsBytes.at(77)))); + INFO("raw slider note byte=0x" + << std::hex << std::setw(2) << std::setfill('0') + << static_cast(static_cast(apsBytes.at(1757)))); + + std::stringstream parseStream( + std::string(apsBytes.begin(), apsBytes.end()), + std::ios::in | std::ios::out | std::ios::binary + ); + kaitai::kstream parseIo(&parseStream); + mpc2000xl_aps_t parsed(&parseIo); + parsed._read(); + + REQUIRE(parsed.aps_programs()->size() >= 1U); + + auto* p1 = parsed.aps_programs()->at(0)->body(); + REQUIRE(p1->name().substr(0, 8) == "PROGRAM1"); + REQUIRE(p1->pad_to_note_mapping()->at(0) == 50); + REQUIRE(p1->slider()->note() == 52); + REQUIRE(p1->slider()->tune_low() == -119); + REQUIRE(p1->slider()->tune_high() == 118); + REQUIRE(p1->slider()->decay_low() == 11); + REQUIRE(p1->slider()->decay_high() == 12); + REQUIRE(p1->slider()->attack_low() == 13); + REQUIRE(p1->slider()->attack_high() == 14); + REQUIRE(p1->slider()->filter_low() == -15); + REQUIRE(p1->slider()->filter_high() == 16); + REQUIRE(p1->program_change() == 126); + + auto* note35 = p1->note_parameters()->at(0).get(); + REQUIRE(note35->sound_index() == 0xFFFF); + REQUIRE(note35->sound_generation_mode() == mpc2000xl_pgm_t::SOUND_GENERATION_MODE_DCY_SW); + REQUIRE(note35->velocity_range_lower() == 1); + REQUIRE(note35->also_play_use_note_1() == 41); + REQUIRE(note35->velocity_range_upper() == 127); + REQUIRE(note35->also_play_use_note_2() == 42); + REQUIRE(note35->voice_overlap_mode() == mpc2000xl_pgm_t::VOICE_OVERLAP_MODE_NOTE_OFF); + REQUIRE(note35->mute_assign_1() == 43); + REQUIRE(note35->mute_assign_2() == 44); + REQUIRE(note35->tune() == -120); + REQUIRE(note35->attack() == 99); + REQUIRE(note35->decay() == 98); + REQUIRE(note35->decay_mode() == mpc2000xl_pgm_t::DECAY_MODE_START); + REQUIRE(note35->cutoff() == 17); + REQUIRE(note35->resonance() == 18); + REQUIRE(note35->velocity_envelope_to_filter_attack() == 19); + REQUIRE(note35->velocity_envelope_to_filter_decay() == 20); + REQUIRE(note35->velocity_envelope_to_filter_amount() == 21); + REQUIRE(note35->velocity_to_level() == 22); + REQUIRE(note35->velocity_to_attack() == 23); + REQUIRE(note35->velocity_to_start() == 24); + REQUIRE(note35->velocity_to_cutoff() == 25); + REQUIRE(note35->slider_parameter() == mpc2000xl_pgm_t::SLIDER_PARAMETER_FILTER); + REQUIRE(note35->velocity_to_pitch() == 26); + + auto* drum0 = parsed.drum1(); + REQUIRE(drum0->program() == 1); + REQUIRE(drum0->receive_program_change() == mpc2000xl_aps_t::NO_YES_FALSE); + REQUIRE(drum0->receive_midi_volume() == mpc2000xl_aps_t::NO_YES_FALSE); + + auto* globals = parsed.global_parameters(); + REQUIRE(globals->pad_to_internal_sound() == mpc2000xl_aps_t::NO_YES_TRUE); + REQUIRE(globals->pad_assign() == mpc2000xl_aps_t::PAD_ASSIGN_MASTERS); + REQUIRE(globals->indiv_fx_source() == mpc2000xl_aps_t::MIX_SOURCE_DRUM); + REQUIRE(globals->stereo_mix_source() == mpc2000xl_aps_t::MIX_SOURCE_DRUM); + REQUIRE(globals->record_mix_changes() == mpc2000xl_aps_t::NO_YES_TRUE); + REQUIRE(globals->copy_pgm_mix_to_drum() == mpc2000xl_aps_t::NO_YES_FALSE); + REQUIRE(globals->fx_drum() == 3); + REQUIRE(globals->master_level() == 2); +} + +TEST_CASE("Kaitai APS saves maximum sound count in 16-bit header", "[kaitai-aps]") +{ + mpc::Mpc mpc; + mpc::TestMpc::initializeTestMpcWithoutMidiServices(mpc); + + for (uint16_t i = 0; i < 256; ++i) + { + auto sound = mpc.getSampler()->addSound(); + REQUIRE(sound != nullptr); + sound->setName("S" + std::to_string(i)); + } + + const auto apsBytes = mpc::file::kaitai::ApsIo::save(mpc, "MAXSOUNDS"); + + std::stringstream parseStream( + std::string(apsBytes.begin(), apsBytes.end()), + std::ios::in | std::ios::out | std::ios::binary + ); + kaitai::kstream parseIo(&parseStream); + mpc2000xl_aps_t parsed(&parseIo); + parsed._read(); + + REQUIRE(parsed.sound_count() == 256); + REQUIRE(parsed.sound_names()->size() == 256U); +} + +TEST_CASE("Kaitai APS saves unassigned pad mapping", "[kaitai-aps]") +{ + mpc::Mpc mpc; + mpc::TestMpc::initializeTestMpcWithoutMidiServices(mpc); + + const auto manager = mpc.getPerformanceManager().lock(); + mpc.getSampler() + ->getProgram(mpc::MinProgramIndex) + ->getPad(mpc::MinProgramPadIndex) + ->setNote(mpc::NoDrumNoteAssigned); + manager->drainQueue(); + + const auto apsBytes = mpc::file::kaitai::ApsIo::save(mpc, "UNASSIGN"); + + std::stringstream parseStream( + std::string(apsBytes.begin(), apsBytes.end()), + std::ios::in | std::ios::out | std::ios::binary + ); + kaitai::kstream parseIo(&parseStream); + mpc2000xl_aps_t parsed(&parseIo); + parsed._read(); + + REQUIRE(parsed.aps_programs()->at(0)->body()->pad_to_note_mapping()->at(0) == 34); +} + +TEST_CASE("ApsLoader loads broad mutated APS semantics", "[kaitai-aps]") +{ + const auto apsBytes = rewriteAllPgmsApsWithMutations(applyBroadApsMutation); + + mpc::Mpc mpc; + mpc::TestMpc::initializeTestMpcWithoutMidiServices(mpc); + loadAllPgmsApsWithoutRender(mpc, apsBytes); + + auto sampler = mpc.getSampler(); + auto p1 = sampler->getProgram(0); + REQUIRE(p1 != nullptr); + REQUIRE(p1->getPad(0)->getNote() == 50); + REQUIRE(p1->getMidiProgramChange() == 127); + + auto note35 = p1->getNoteParameters(35); + REQUIRE(note35->getSoundIndex() == -1); + REQUIRE(note35->getSoundGenerationMode() == + mpc::sampler::SoundGenerationMode::DecaySwitch); + REQUIRE(note35->getVelocityRangeLower() == 1); + REQUIRE(note35->getOptionalNoteA() == 41); + REQUIRE(note35->getVelocityRangeUpper() == 127); + REQUIRE(note35->getOptionalNoteB() == 42); + REQUIRE(note35->getVoiceOverlapMode() == mpc::sampler::VoiceOverlapMode::NOTE_OFF); + REQUIRE(note35->getMuteAssignA() == 43); + REQUIRE(note35->getMuteAssignB() == 44); + REQUIRE(note35->getTune() == -120); + REQUIRE(note35->getAttack() == 99); + REQUIRE(note35->getDecay() == 98); + REQUIRE(note35->getDecayMode() == 1); + REQUIRE(note35->getFilterFrequency() == 17); + REQUIRE(note35->getFilterResonance() == 18); + REQUIRE(note35->getFilterAttack() == 19); + REQUIRE(note35->getFilterDecay() == 20); + REQUIRE(note35->getFilterEnvelopeAmount() == 21); + REQUIRE(note35->getVeloToLevel() == 22); + REQUIRE(note35->getVelocityToAttack() == 23); + REQUIRE(note35->getVelocityToStart() == 24); + REQUIRE(note35->getVelocityToFilterFrequency() == 25); + REQUIRE(note35->getSliderParameterNumber() == 3); + REQUIRE(note35->getVelocityToPitch() == 26); + + auto stereoMixer = note35->getStereoMixer(); + auto indivFxMixer = note35->getIndivFxMixer(); + REQUIRE(stereoMixer->getLevel() == 12); + REQUIRE(stereoMixer->getPanning() == 13); + REQUIRE(indivFxMixer->getVolumeIndividualOut() == 14); + REQUIRE(indivFxMixer->getOutput() == 2); + REQUIRE(indivFxMixer->getFxPath() == 4); + REQUIRE(indivFxMixer->getFxSendLevel() == 15); + + auto slider = p1->getSlider(); + REQUIRE(slider->getNote() == 52); + REQUIRE(slider->getTuneLowRange() == -119); + REQUIRE(slider->getTuneHighRange() == 118); + REQUIRE(slider->getDecayLowRange() == 11); + REQUIRE(slider->getDecayHighRange() == 12); + REQUIRE(slider->getAttackLowRange() == 13); + REQUIRE(slider->getAttackHighRange() == 14); + REQUIRE(slider->getFilterLowRange() == -15); + REQUIRE(slider->getFilterHighRange() == 16); + + auto drum0 = mpc.getSequencer()->getDrumBus(mpc::DrumBusIndex(0)); + REQUIRE(drum0->getProgramIndex() == 1); + REQUIRE(!drum0->receivesPgmChange()); + REQUIRE(!drum0->receivesMidiVolume()); + + auto mixerSetup = mpc.screens->get(); + REQUIRE(mixerSetup->isRecordMixChangesEnabled()); + REQUIRE(!mixerSetup->isCopyPgmMixToDrumEnabled()); + REQUIRE(mixerSetup->getFxDrum() == 3); + REQUIRE(mixerSetup->isIndivFxSourceDrum()); + REQUIRE(mixerSetup->isStereoMixSourceDrum()); + REQUIRE(mixerSetup->getMasterLevel() == 2); + + auto drumScreen = mpc.screens->get(); + REQUIRE(drumScreen->isPadToIntSound()); + + auto pgmAssignScreen = mpc.screens->get(); + REQUIRE(pgmAssignScreen->isPadAssignMaster()); +} diff --git a/src/test/KaitaiMidTest.cpp b/src/test/KaitaiMidTest.cpp new file mode 100644 index 000000000..cdc0ba858 --- /dev/null +++ b/src/test/KaitaiMidTest.cpp @@ -0,0 +1,1523 @@ +#include + +#include "TestMpc.hpp" + +#include "disk/AllLoader.hpp" +#include "disk/AbstractDisk.hpp" +#include "disk/MpcFile.hpp" +#include "file/kaitai/MidIo.hpp" +#include "file/kaitai/generated/standard_midi_file_with_running_status.h" +#include "sequencer/ChannelPressureEvent.hpp" +#include "sequencer/ControlChangeEvent.hpp" +#include "sequencer/MixerEvent.hpp" +#include "sequencer/NoteOnEvent.hpp" +#include "sequencer/PolyPressureEvent.hpp" +#include "sequencer/ProgramChangeEvent.hpp" +#include "sequencer/Sequence.hpp" +#include "sequencer/Sequencer.hpp" +#include "sequencer/SequencerStateManager.hpp" +#include "sequencer/Track.hpp" +#include "sequencer/Transport.hpp" + +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +CMRC_DECLARE(mpctest); + +namespace { + +using midi_t = standard_midi_file_with_running_status_t; +using meta_type_t = midi_t::meta_event_body_t::meta_type_enum_t; + +struct ParsedNoteSnapshot { + int note; + int velocity; + int duration; + int variationType; + + bool operator==(const ParsedNoteSnapshot& other) const + { + return note == other.note && + velocity == other.velocity && + duration == other.duration && + variationType == other.variationType; + } +}; + +struct DetailedParsedNoteSnapshot { + int tick; + int note; + int velocity; + int duration; + int variationType; + int variationValue; + + bool operator==(const DetailedParsedNoteSnapshot& other) const + { + return tick == other.tick && + note == other.note && + velocity == other.velocity && + duration == other.duration && + variationType == other.variationType && + variationValue == other.variationValue; + } +}; + +struct MidiTimelineEventSnapshot { + int tick; + std::string kind; + int a; + int b; + + bool operator==(const MidiTimelineEventSnapshot& other) const + { + return tick == other.tick && + kind == other.kind && + a == other.a && + b == other.b; + } +}; + +std::vector collectParsedNotes(const std::shared_ptr& track) +{ + std::vector result; + for (const auto& event : track->getEvents()) { + const auto note = std::dynamic_pointer_cast(event); + if (!note) { + continue; + } + result.push_back(ParsedNoteSnapshot{ + note->getNote(), + note->getVelocity(), + note->getDuration(), + note->getVariationType() + }); + } + return result; +} + +std::string asciiBody(const std::string& body) +{ + return body; +} + +double bpmFromTempoBody(const std::string& body) +{ + if (body.size() < 3) + { + return 120.0; + } + + const auto mpqn = + (static_cast(body[0]) << 16) | + (static_cast(body[1]) << 8) | + static_cast(body[2]); + return mpqn == 0 ? 120.0 : 60000000.0 / static_cast(mpqn); +} + +int denominatorFromTimeSignatureBody(const std::string& body) +{ + return body.size() < 2 ? mpc::DefaultTimeSigDenominator + : 1 << static_cast(body[1]); +} + +std::vector collectDetailedParsedNotes( + const std::shared_ptr& track) +{ + std::vector result; + for (const auto& event : track->getEvents()) { + const auto note = std::dynamic_pointer_cast(event); + if (!note) { + continue; + } + result.push_back(DetailedParsedNoteSnapshot{ + note->getTick(), + note->getNote(), + note->getVelocity(), + note->getDuration(), + note->getVariationType(), + note->getVariationValue() + }); + } + return result; +} + +std::vector collectKaitaiTimeline(const std::vector& bytes, size_t trackIndex) +{ + std::stringstream parseStream( + std::string(bytes.begin(), bytes.end()), + std::ios::in | std::ios::out | std::ios::binary + ); + kaitai::kstream parseIo(&parseStream); + midi_t parsed(&parseIo); + parsed._read(); + + REQUIRE(trackIndex < parsed.tracks()->size()); + const auto& track = parsed.tracks()->at(trackIndex); + + std::vector result; + int tick = 0; + for (const auto& eventPtr : *track->events()->event()) + { + tick += eventPtr->v_time()->value(); + if (const auto* meta = eventPtr->meta_event_body(); meta != nullptr) + { + switch (meta->meta_type()) + { + case meta_type_t::META_TYPE_ENUM_SEQUENCE_TRACK_NAME: + result.push_back({tick, "track_name", static_cast(asciiBody(meta->body()).size()), 0}); + break; + case meta_type_t::META_TYPE_ENUM_TEXT_EVENT: + result.push_back({tick, "text", static_cast(asciiBody(meta->body()).size()), 0}); + break; + case meta_type_t::META_TYPE_ENUM_TEMPO: + result.push_back({tick, "tempo", static_cast(std::lround(bpmFromTempoBody(meta->body()) * 10.0)), 0}); + break; + case meta_type_t::META_TYPE_ENUM_TIME_SIGNATURE: + result.push_back({tick, "time_signature", meta->body().empty() ? mpc::DefaultTimeSigNumerator : static_cast(meta->body()[0]), denominatorFromTimeSignatureBody(meta->body())}); + break; + case meta_type_t::META_TYPE_ENUM_END_OF_TRACK: + break; + default: + result.push_back({tick, "meta_other", static_cast(meta->meta_type()), static_cast(meta->body().size())}); + break; + } + continue; + } + + switch (eventPtr->event_type()) + { + case 0x80: + { + const auto* noteOff = dynamic_cast(eventPtr->event_body()); + result.push_back({tick, "note_off", noteOff->note(), noteOff->velocity()}); + break; + } + case 0x90: + { + const auto* noteOn = dynamic_cast(eventPtr->event_body()); + result.push_back({tick, "note_on", noteOn->note(), noteOn->velocity()}); + break; + } + case 0xA0: + { + const auto* noteAftertouch = dynamic_cast(eventPtr->event_body()); + result.push_back({tick, "poly_pressure", noteAftertouch->note(), noteAftertouch->pressure()}); + break; + } + case 0xB0: + { + const auto* controller = dynamic_cast(eventPtr->event_body()); + result.push_back({tick, "control_change", controller->controller(), controller->value()}); + break; + } + case 0xC0: + { + const auto* programChange = dynamic_cast(eventPtr->event_body()); + result.push_back({tick, "program_change", programChange->program(), 0}); + break; + } + case 0xD0: + { + const auto* channelPressure = dynamic_cast(eventPtr->event_body()); + result.push_back({tick, "channel_pressure", channelPressure->pressure(), 0}); + break; + } + case 0xE0: + { + const auto* pitchBend = dynamic_cast(eventPtr->event_body()); + result.push_back({tick, "pitch_bend", pitchBend->b1(), pitchBend->b2()}); + break; + } + case 0xF0: + { + const auto* sysEx = eventPtr->sysex_body(); + result.push_back({tick, "sysex", static_cast(sysEx->data().size()), sysEx->data().empty() ? -1 : static_cast(sysEx->data()[0])}); + break; + } + default: + result.push_back({tick, "other", eventPtr->event_type(), 0}); + break; + } + } + return result; +} + +template +std::shared_ptr findEventOfType(const std::shared_ptr& track) +{ + for (const auto& event : track->getEvents()) { + if (const auto cast = std::dynamic_pointer_cast(event)) { + return cast; + } + } + return {}; +} + +std::shared_ptr installMidiResourceFile( + mpc::Mpc& mpc, + const std::string& resourcePath, + const std::string& fileName) +{ + auto disk = mpc.getDisk(); + auto fs = cmrc::mpctest::get_filesystem(); + auto file = fs.open(resourcePath); + std::vector data(file.begin(), file.end()); + auto newFile = disk->newFile(fileName); + newFile->setFileData(data); + disk->initFiles(); + return newFile; +} + +std::shared_ptr installMidiBytes( + mpc::Mpc& mpc, + const std::vector& bytes, + const std::string& fileName) +{ + auto disk = mpc.getDisk(); + auto newFile = disk->newFile(fileName); + auto mutableBytes = bytes; + newFile->setFileData(mutableBytes); + disk->initFiles(); + return newFile; +} + +std::vector saveSequenceWithMidIo( + const std::shared_ptr& sequence) +{ + return mpc::file::kaitai::MidIo::saveBytes(sequence); +} + +std::shared_ptr loadSequenceWithMidIo( + mpc::Mpc& mpc, + const std::vector& bytes, + const std::string& fileName) +{ + const auto stem = fileName.substr(0, fileName.find_last_of('.')); + const auto sequenceOrError = mpc::file::kaitai::MidIo::loadBytes(mpc, bytes, stem); + REQUIRE(sequenceOrError.has_value()); + auto stateManager = mpc.getSequencer()->getStateManager(); + stateManager->drainQueue(); + return sequenceOrError.value(); +} + +std::shared_ptr loadSequenceWithDirectGenericKaitai( + mpc::Mpc& mpc, + const std::vector& bytes, + const std::string& fileName) +{ + const auto stem = fileName.substr(0, fileName.find_last_of('.')); + auto sequence = mpc.getSequencer()->getSequence(mpc::TempSequenceIndex); + sequence->init(0); + mpc.getSequencer()->getStateManager()->drainQueue(); + + std::stringstream parseStream( + std::string(bytes.begin(), bytes.end()), + std::ios::in | std::ios::out | std::ios::binary + ); + kaitai::kstream parseIo(&parseStream); + midi_t parsed(&parseIo); + parsed._read(); + + auto sequencer = mpc.getSequencer(); + auto transport = sequencer->getTransport(); + + std::vector> tempoChanges; + struct TimeSigChange { int tick; int numerator; int denominator; }; + std::vector timeSignatures; + + int lengthInTicks = 0; + for (const auto& midiTrack : *parsed.tracks()) + { + int trackTick = 0; + for (const auto& eventPtr : *midiTrack->events()->event()) + { + trackTick += eventPtr->v_time()->value(); + } + lengthInTicks = std::max(lengthInTicks, trackTick); + } + + int tick = 0; + for (const auto& eventPtr : *parsed.tracks()->at(0)->events()->event()) + { + tick += eventPtr->v_time()->value(); + const auto* meta = eventPtr->meta_event_body(); + if (meta == nullptr) + { + continue; + } + + if (meta->meta_type() == meta_type_t::META_TYPE_ENUM_TEMPO) + { + tempoChanges.push_back({tick, bpmFromTempoBody(meta->body())}); + } + else if (meta->meta_type() == meta_type_t::META_TYPE_ENUM_TIME_SIGNATURE) + { + timeSignatures.push_back( + TimeSigChange{ + tick, + meta->body().empty() + ? mpc::DefaultTimeSigNumerator + : static_cast(meta->body()[0]), + denominatorFromTimeSignatureBody(meta->body()) + } + ); + } + } + + UNSCOPED_INFO("direct-generic file=" << fileName); + UNSCOPED_INFO("direct-generic lengthInTicks=" << lengthInTicks); + UNSCOPED_INFO("direct-generic tempoChanges=" << tempoChanges.size()); + UNSCOPED_INFO("direct-generic timeSignatures=" << timeSignatures.size()); + for (size_t i = 0; i < timeSignatures.size(); ++i) + { + UNSCOPED_INFO( + "direct-generic timesig[" << i << "] tick=" << timeSignatures[i].tick + << " num=" << timeSignatures[i].numerator + << " den=" << timeSignatures[i].denominator + ); + } + + const auto initialTempo = tempoChanges.empty() ? 120.0 : tempoChanges[0].second; + sequence->setInitialTempo(initialTempo); + if (!transport->isTempoSourceSequence()) + { + transport->setTempo(initialTempo); + } + + for (size_t i = 1; i < tempoChanges.size(); ++i) + { + const auto ratio = tempoChanges[i].second / initialTempo; + sequence->addTempoChangeEvent( + tempoChanges[i].first, + static_cast(ratio * 1000.0) + ); + } + + if (timeSignatures.empty()) + { + timeSignatures.push_back( + TimeSigChange{0, mpc::DefaultTimeSigNumerator, mpc::DefaultTimeSigDenominator} + ); + } + + lengthInTicks = std::max(lengthInTicks, 1); + int accumLength = 0; + int barCounter = 0; + for (size_t i = 0; i < timeSignatures.size(); ++i) + { + const auto current = timeSignatures[i]; + const auto nextTick = + i + 1 < timeSignatures.size() ? timeSignatures[i + 1].tick : lengthInTicks; + + while (accumLength < nextTick) + { + sequence->setTimeSignature(barCounter, current.numerator, current.denominator); + const auto newDenTicks = 96 * (4.0 / current.denominator); + const auto barLength = static_cast(newDenTicks * current.numerator); + accumLength += barLength; + barCounter++; + } + } + + sequence->setLastBarIndex(barCounter - 1); + sequence->setFirstLoopBarIndex(mpc::BarIndex(-1)); + sequence->setLastLoopBarIndex(mpc::EndOfSequence); + + const std::string trackDataPrefix = "TRACK DATA:"; + + for (size_t i = 0; i < parsed.tracks()->size() && i < 64; ++i) + { + auto resolvedTrackIndex = static_cast(i); + auto deviceIndex = 0; + auto busType = mpc::sequencer::BusType::DRUM1; + + for (const auto& eventPtr : *parsed.tracks()->at(i)->events()->event()) + { + const auto* meta = eventPtr->meta_event_body(); + if (meta == nullptr || meta->meta_type() != meta_type_t::META_TYPE_ENUM_TEXT_EVENT) + { + continue; + } + + const auto body = asciiBody(meta->body()); + if (body.rfind(trackDataPrefix, 0) != 0) + { + continue; + } + + const auto payload = body.substr(trackDataPrefix.size()); + resolvedTrackIndex = std::stoi(payload.substr(0, 2)); + + const auto deviceIndexStr = payload.substr(2, 2); + if (deviceIndexStr != "C0") + { + deviceIndex = std::stoi(deviceIndexStr, nullptr, 16) - std::stoi(std::string("E0"), nullptr, 16) + 1; + } + + if (payload.size() >= 16) + { + busType = mpc::sequencer::busIndexToBusType(std::stoi(payload.substr(14, 2), nullptr, 16)); + } + break; + } + + if (resolvedTrackIndex < 0 || resolvedTrackIndex > mpc::Mpc2000XlSpecs::LAST_TRACK_INDEX) + { + continue; + } + + auto track = sequence->getTrack(resolvedTrackIndex); + track->setBusType(busType); + track->setDeviceIndex(deviceIndex); + std::string pendingTrackName; + bool hasPendingTrackName = false; + bool hasImportedEvents = false; + + struct OpenNote + { + mpc::sequencer::EventData event; + bool isClosed = false; + }; + + std::vector openNotes; + std::vector> noteOffs; + + int absoluteTick = 0; + for (const auto& eventPtr : *parsed.tracks()->at(i)->events()->event()) + { + absoluteTick += eventPtr->v_time()->value(); + + if (const auto* meta = eventPtr->meta_event_body(); meta != nullptr) + { + if (meta->meta_type() == meta_type_t::META_TYPE_ENUM_SEQUENCE_TRACK_NAME) + { + const auto body = asciiBody(meta->body()); + if (!body.empty()) + { + pendingTrackName = body.substr(0, mpc::Mpc2000XlSpecs::MAX_TRACK_NAME_LENGTH); + hasPendingTrackName = true; + } + } + continue; + } + + switch (eventPtr->event_type()) + { + case 0x80: + { + const auto* noteOff = + dynamic_cast(eventPtr->event_body()); + if (noteOff != nullptr) + { + noteOffs.push_back({noteOff->note(), absoluteTick}); + } + break; + } + case 0x90: + { + const auto* noteOn = + dynamic_cast(eventPtr->event_body()); + if (noteOn == nullptr) + { + break; + } + mpc::sequencer::EventData ne; + ne.type = mpc::sequencer::EventType::NoteOn; + ne.noteNumber = mpc::NoteNumber(noteOn->note()); + ne.tick = absoluteTick; + ne.velocity = mpc::Velocity(noteOn->velocity()); + ne.noteVariationType = mpc::NoteVariationTypeTune; + ne.noteVariationValue = mpc::DefaultNoteVariationValue; + ne.duration = mpc::Duration(24); + openNotes.push_back(OpenNote{ne}); + break; + } + case 0xA0: + { + const auto* noteAftertouch = + dynamic_cast(eventPtr->event_body()); + if (noteAftertouch == nullptr) + { + break; + } + mpc::sequencer::EventData e; + e.type = mpc::sequencer::EventType::PolyPressure; + e.tick = absoluteTick; + e.noteNumber = mpc::NoteNumber(noteAftertouch->note()); + e.amount = noteAftertouch->pressure(); + track->acquireAndInsertEvent(e); + hasImportedEvents = true; + break; + } + case 0xB0: + { + const auto* controller = + dynamic_cast(eventPtr->event_body()); + if (controller == nullptr) + { + break; + } + mpc::sequencer::EventData e; + e.type = mpc::sequencer::EventType::ControlChange; + e.tick = absoluteTick; + e.controllerNumber = controller->controller(); + e.controllerValue = controller->value(); + track->acquireAndInsertEvent(e); + hasImportedEvents = true; + break; + } + case 0xC0: + { + const auto* programChange = + dynamic_cast(eventPtr->event_body()); + if (programChange == nullptr) + { + break; + } + mpc::sequencer::EventData e; + e.type = mpc::sequencer::EventType::ProgramChange; + e.tick = absoluteTick; + e.programChangeProgramIndex = mpc::ProgramIndex(programChange->program()); + track->acquireAndInsertEvent(e); + hasImportedEvents = true; + break; + } + case 0xD0: + { + const auto* channelPressure = + dynamic_cast(eventPtr->event_body()); + if (channelPressure == nullptr) + { + break; + } + mpc::sequencer::EventData e; + e.type = mpc::sequencer::EventType::ChannelPressure; + e.tick = absoluteTick; + e.amount = channelPressure->pressure(); + track->acquireAndInsertEvent(e); + hasImportedEvents = true; + break; + } + case 0xE0: + { + auto* pitchBend = + dynamic_cast(eventPtr->event_body()); + if (pitchBend == nullptr) + { + break; + } + mpc::sequencer::EventData e; + e.type = mpc::sequencer::EventType::PitchBend; + e.tick = absoluteTick; + e.amount = pitchBend->bend_value(); + track->acquireAndInsertEvent(e); + hasImportedEvents = true; + break; + } + case 0xF0: + { + const auto* sysEx = eventPtr->sysex_body(); + if (sysEx == nullptr) + { + break; + } + const auto& data = sysEx->data(); + if (data.size() == 8 && + static_cast(data[0]) == 71 && + static_cast(data[1]) == 0 && + static_cast(data[2]) == 68 && + static_cast(data[3]) == 69 && + static_cast(data[7]) == 247) + { + mpc::sequencer::EventData e; + e.type = mpc::sequencer::EventType::Mixer; + e.tick = absoluteTick; + e.mixerParameter = static_cast(data[4]) - 1; + e.mixerPad = static_cast(data[5]); + e.mixerValue = static_cast(data[6]); + track->acquireAndInsertEvent(e); + } + else + { + mpc::sequencer::EventData e; + e.type = mpc::sequencer::EventType::SystemExclusive; + e.tick = absoluteTick; + track->acquireAndInsertEvent(e); + } + hasImportedEvents = true; + break; + } + default: + break; + } + } + + for (auto& on : openNotes) + { + for (auto it = noteOffs.begin(); it != noteOffs.end(); ++it) + { + if (it->first == on.event.noteNumber && it->second >= on.event.tick) + { + on.event.duration = mpc::Duration(it->second - on.event.tick); + noteOffs.erase(it); + break; + } + } + + track->acquireAndInsertEvent(on.event); + hasImportedEvents = true; + } + + if (hasImportedEvents) + { + track->setUsedIfCurrentlyUnused(); + } + + if (hasPendingTrackName) + { + track->setName(pendingTrackName); + } + } + + auto stateManager = mpc.getSequencer()->getStateManager(); + stateManager->drainQueue(); + return sequence; +} + +void insertNote( + const std::shared_ptr& track, + int tick, + int note, + int velocity, + int duration, + int variationType, + int variationValue) +{ + mpc::sequencer::EventData eventData; + eventData.type = mpc::sequencer::EventType::NoteOn; + eventData.tick = tick; + eventData.noteNumber = mpc::NoteNumber(note); + eventData.velocity = mpc::Velocity(velocity); + eventData.duration = mpc::Duration(duration); + eventData.noteVariationType = mpc::NoteVariationType(variationType); + eventData.noteVariationValue = mpc::NoteVariationValue(variationValue); + track->acquireAndInsertEvent(eventData); +} + +void requireDetailedNote( + const std::shared_ptr& track, + size_t noteIndex, + int tick, + int note, + int velocity, + int duration, + int variationType = mpc::NoteVariationTypeTune, + int variationValue = mpc::DefaultNoteVariationValue) +{ + const auto notes = collectDetailedParsedNotes(track); + REQUIRE(noteIndex < notes.size()); + const auto& actual = notes[noteIndex]; + REQUIRE(actual.tick == tick); + REQUIRE(actual.note == note); + REQUIRE(actual.velocity == velocity); + REQUIRE(actual.duration == duration); + REQUIRE(actual.variationType == variationType); + REQUIRE(actual.variationValue == variationValue); +} + +} + +TEST_CASE("Kaitai standard MIDI parses and rewrites FRUTZLE", "[kaitai-mid]") +{ + auto fs = cmrc::mpctest::get_filesystem(); + auto file = fs.open("test/MidiFile/FRUTZLE.MID"); + const std::string originalBytes( + std::string_view(file.begin(), file.end() - file.begin()) + ); + + std::stringstream parseStream( + originalBytes, + std::ios::in | std::ios::out | std::ios::binary + ); + kaitai::kstream parseIo(&parseStream); + standard_midi_file_with_running_status_t parsed(&parseIo); + parsed._read(); + + REQUIRE(parsed.hdr() != nullptr); + REQUIRE(parsed.hdr()->num_tracks() > 0); + REQUIRE(parsed.tracks() != nullptr); + REQUIRE(parsed.tracks()->size() == parsed.hdr()->num_tracks()); + + std::stringstream writeStream(std::ios::in | std::ios::out | std::ios::binary); + kaitai::kstream writeIo(&writeStream); + parsed._set_io(&writeIo); + parsed._check(); + parsed._write(); + + const auto rewrittenBytes = writeStream.str(); + REQUIRE(rewrittenBytes.size() == originalBytes.size()); + REQUIRE(std::equal(rewrittenBytes.begin(), rewrittenBytes.end(), originalBytes.begin())); +} + +TEST_CASE("Kaitai standard MIDI loads FRUTZLE with expected semantics", "[kaitai-mid]") +{ + auto fs = cmrc::mpctest::get_filesystem(); + auto file = fs.open("test/MidiFile/FRUTZLE.MID"); + std::vector bytes(file.begin(), file.end()); + + mpc::Mpc mpc; + mpc::TestMpc::initializeTestMpcWithoutMidiServices(mpc); + auto sequence = loadSequenceWithMidIo(mpc, bytes, "FRUTZLE.MID"); + + REQUIRE(sequence->getName() == "Sequence01 "); + REQUIRE(sequence->getBarCount() == 4); + REQUIRE(sequence->getInitialTempo() == 96.0); + + const auto track0 = sequence->getTrack(0); + REQUIRE(track0->isUsed()); + REQUIRE(track0->getName() == "Track-01 "); + REQUIRE(track0->getBusType() == mpc::sequencer::BusType::DRUM1); + REQUIRE(track0->getDeviceIndex() == 0); + REQUIRE(track0->getEvents().size() == 18); + requireDetailedNote(track0, 0, 0, 37, 127, 13); + requireDetailedNote(track0, 3, 288, 37, 16, 12); + requireDetailedNote(track0, 17, 1440, 36, 127, 0); + + const auto track4 = sequence->getTrack(4); + REQUIRE(track4->isUsed()); + REQUIRE(track4->getName() == "Track-05 "); + REQUIRE(track4->getEvents().size() == 32); + requireDetailedNote(track4, 0, 0, 48, 96, 3); + requireDetailedNote(track4, 15, 722, 47, 91, 8); + requireDetailedNote(track4, 31, 1490, 47, 44, 5); + + const auto track8 = sequence->getTrack(8); + REQUIRE(track8->isUsed()); + REQUIRE(track8->getName() == "Track-09 "); + REQUIRE(track8->getEvents().size() == 6); + requireDetailedNote(track8, 0, 3, 82, 127, 180); + requireDetailedNote(track8, 5, 725, 82, 127, 31); +} + +TEST_CASE("Kaitai standard MIDI loads HWIF316 with expected semantics", "[kaitai-mid]") +{ + auto fs = cmrc::mpctest::get_filesystem(); + auto file = fs.open("test/MidiFile/HWIF316.MID"); + std::vector bytes(file.begin(), file.end()); + + mpc::Mpc mpc; + mpc::TestMpc::initializeTestMpcWithoutMidiServices(mpc); + auto sequence = loadSequenceWithMidIo(mpc, bytes, "HWIF316.MID"); + + REQUIRE(sequence->getName() == "Sequence100"); + REQUIRE(sequence->getBarCount() == 16); + REQUIRE(std::abs(sequence->getInitialTempo() - 125.0) < 0.001); + + const auto track0 = sequence->getTrack(0); + REQUIRE_FALSE(track0->isUsed()); + REQUIRE(track0->getName() == "HWIF v3 t1 p6"); + REQUIRE(track0->getEvents().empty()); + + const auto track1 = sequence->getTrack(1); + REQUIRE(track1->isUsed()); + REQUIRE(track1->getName() == "Jupiter ARP? out"); + REQUIRE(track1->getEvents().size() == 111); + requireDetailedNote(track1, 0, 0, 67, 127, 25); + requireDetailedNote(track1, 31, 1537, 67, 110, 25); + requireDetailedNote(track1, 110, 5377, 67, 92, 24); + + const auto track2 = sequence->getTrack(2); + REQUIRE(track2->isUsed()); + REQUIRE(track2->getName() == "JUNO UNISON CHOR"); + REQUIRE(track2->getEvents().size() == 118); + requireDetailedNote(track2, 0, 1, 55, 73, 24); + requireDetailedNote(track2, 57, 3073, 55, 73, 24); + requireDetailedNote(track2, 117, 6144, 65, 0, 24); + + const auto track3 = sequence->getTrack(3); + REQUIRE(track3->isUsed()); + REQUIRE(track3->getName() == " JD880 pad"); + REQUIRE(track3->getEvents().size() == 48); + requireDetailedNote(track3, 0, 1, 70, 78, 24); + requireDetailedNote(track3, 24, 3074, 67, 81, 24); + requireDetailedNote(track3, 47, 5486, 67, 0, 24); +} + +TEST_CASE("Kaitai standard MIDI loads HWIF6-0V2 with expected semantics", "[kaitai-mid]") +{ + auto fs = cmrc::mpctest::get_filesystem(); + auto file = fs.open("test/MidiFile/HWIF6-0V2.MID"); + std::vector bytes(file.begin(), file.end()); + + mpc::Mpc mpc; + mpc::TestMpc::initializeTestMpcWithoutMidiServices(mpc); + auto sequence = loadSequenceWithMidIo(mpc, bytes, "HWIF6-0V2.MID"); + + REQUIRE(sequence->getName() == "Sequence100"); + REQUIRE(sequence->getBarCount() == 16); + REQUIRE(std::abs(sequence->getInitialTempo() - 125.0) < 0.001); + + const auto track0 = sequence->getTrack(0); + REQUIRE(track0->isUsed()); + REQUIRE(track0->getName() == "HWIF6-0v2"); + REQUIRE(track0->getEvents().size() == 204); + requireDetailedNote(track0, 0, 0, 67, 114, 24); + requireDetailedNote(track0, 52, 1536, 67, 83, 24); + requireDetailedNote(track0, 104, 3072, 67, 18, 24); + requireDetailedNote(track0, 200, 6144, 79, 93, 24); +} + +TEST_CASE("Kaitai standard MIDI parses and rewrites HWIF6-0V2", "[.][kaitai-mid-direct]") +{ + auto fs = cmrc::mpctest::get_filesystem(); + auto file = fs.open("test/MidiFile/HWIF6-0V2.MID"); + const std::string originalBytes( + std::string_view(file.begin(), file.end() - file.begin()) + ); + + std::stringstream parseStream( + originalBytes, + std::ios::in | std::ios::out | std::ios::binary + ); + kaitai::kstream parseIo(&parseStream); + standard_midi_file_with_running_status_t parsed(&parseIo); + parsed._read(); + + std::stringstream writeStream(std::ios::in | std::ios::out | std::ios::binary); + kaitai::kstream writeIo(&writeStream); + parsed._set_io(&writeIo); + parsed._check(); + parsed._write(); + + const auto rewrittenBytes = writeStream.str(); + REQUIRE(rewrittenBytes.size() == originalBytes.size()); + REQUIRE(std::equal(rewrittenBytes.begin(), rewrittenBytes.end(), originalBytes.begin())); +} + +TEST_CASE("Kaitai standard MIDI parses and rewrites HWIF316", "[.][kaitai-mid-direct]") +{ + auto fs = cmrc::mpctest::get_filesystem(); + auto file = fs.open("test/MidiFile/HWIF316.MID"); + const std::string originalBytes( + std::string_view(file.begin(), file.end() - file.begin()) + ); + + std::stringstream parseStream( + originalBytes, + std::ios::in | std::ios::out | std::ios::binary + ); + kaitai::kstream parseIo(&parseStream); + standard_midi_file_with_running_status_t parsed(&parseIo); + parsed._read(); + + std::stringstream writeStream(std::ios::in | std::ios::out | std::ios::binary); + kaitai::kstream writeIo(&writeStream); + parsed._set_io(&writeIo); + parsed._check(); + parsed._write(); + + const auto rewrittenBytes = writeStream.str(); + REQUIRE(rewrittenBytes.size() == originalBytes.size()); + REQUIRE(std::equal(rewrittenBytes.begin(), rewrittenBytes.end(), originalBytes.begin())); +} + +TEST_CASE("Kaitai raw MIDI timeline exposes expected HWIF316 track 0 events", "[.][kaitai-mid-direct]") +{ + auto fs = cmrc::mpctest::get_filesystem(); + auto file = fs.open("test/MidiFile/HWIF316.MID"); + std::vector bytes(file.begin(), file.end()); + + const auto kaitai = collectKaitaiTimeline(bytes, 0); + + REQUIRE(kaitai.size() == 3); + REQUIRE(kaitai[0] == MidiTimelineEventSnapshot{0, "track_name", 13, 0}); + REQUIRE(kaitai[1] == MidiTimelineEventSnapshot{0, "tempo", 1250, 0}); + REQUIRE(kaitai[2] == MidiTimelineEventSnapshot{0, "other", 252, 0}); +} + +TEST_CASE("Kaitai raw MIDI timeline exposes expected HWIF316 track 3 events", "[.][kaitai-mid-direct]") +{ + auto fs = cmrc::mpctest::get_filesystem(); + auto file = fs.open("test/MidiFile/HWIF316.MID"); + std::vector bytes(file.begin(), file.end()); + + const auto kaitai = collectKaitaiTimeline(bytes, 3); + + REQUIRE(kaitai.size() == 49); + REQUIRE(kaitai[0] == MidiTimelineEventSnapshot{0, "track_name", 10, 0}); + REQUIRE(kaitai[1] == MidiTimelineEventSnapshot{1, "note_on", 70, 78}); + REQUIRE(kaitai[2] == MidiTimelineEventSnapshot{1, "note_on", 74, 67}); + REQUIRE(kaitai[3] == MidiTimelineEventSnapshot{2, "note_on", 67, 81}); + REQUIRE(kaitai[24] == MidiTimelineEventSnapshot{3074, "note_on", 67, 81}); + REQUIRE(kaitai[48] == MidiTimelineEventSnapshot{5486, "note_on", 67, 0}); +} + +TEST_CASE("Kaitai first raw event on HWIF316 track 0 is a meta event", "[.][kaitai-mid-direct]") +{ + auto fs = cmrc::mpctest::get_filesystem(); + auto file = fs.open("test/MidiFile/HWIF316.MID"); + std::vector bytes(file.begin(), file.end()); + + std::stringstream parseStream( + std::string(bytes.begin(), bytes.end()), + std::ios::in | std::ios::out | std::ios::binary + ); + kaitai::kstream parseIo(&parseStream); + midi_t parsed(&parseIo); + parsed._read(); + + const auto& firstEvent = parsed.tracks()->at(0)->events()->event()->at(0); + UNSCOPED_INFO("first delta=" << firstEvent->v_time()->value()); + UNSCOPED_INFO("first using_running_status=" << firstEvent->using_running_status()); + UNSCOPED_INFO("first status_byte_lookahead=" << static_cast(firstEvent->status_byte_lookahead())); + if (!firstEvent->_is_null_event_header()) + { + UNSCOPED_INFO("first event_header=" << static_cast(firstEvent->event_header())); + } + UNSCOPED_INFO("first event_type=" << firstEvent->event_type()); + REQUIRE(firstEvent->meta_event_body() != nullptr); +} + +TEST_CASE("Kaitai standard MIDI saves and loads explicit sequence semantics", "[kaitai-mid]") +{ + mpc::Mpc mpc; + mpc::TestMpc::initializeTestMpc(mpc); + + auto sequencer = mpc.getSequencer(); + auto stateManager = sequencer->getStateManager(); + auto sequence = sequencer->getSequence(0); + sequence->init(1); + sequence->setName("KTAIMID"); + stateManager->drainQueue(); + + auto track0 = sequence->getTrack(0); + track0->setUsedIfCurrentlyUnused(); + track0->setDeviceIndex(2); + + mpc::sequencer::EventData eventData; + eventData.type = mpc::sequencer::EventType::NoteOn; + eventData.tick = 0; + eventData.noteNumber = mpc::NoteNumber(37); + eventData.velocity = mpc::MaxVelocity; + eventData.duration = mpc::Duration(10); + track0->acquireAndInsertEvent(eventData); + stateManager->drainQueue(); + + const auto savedBytes = saveSequenceWithMidIo(sequence); + + std::stringstream parseStream( + std::string(savedBytes.begin(), savedBytes.end()), + std::ios::in | std::ios::out | std::ios::binary + ); + kaitai::kstream parseIo(&parseStream); + standard_midi_file_with_running_status_t parsed(&parseIo); + parsed._read(); + + REQUIRE(parsed.hdr() != nullptr); + REQUIRE(parsed.hdr()->num_tracks() >= 2); + REQUIRE(parsed.tracks() != nullptr); + REQUIRE(parsed.tracks()->size() == parsed.hdr()->num_tracks()); + + std::stringstream writeStream(std::ios::in | std::ios::out | std::ios::binary); + kaitai::kstream writeIo(&writeStream); + parsed._set_io(&writeIo); + parsed._check(); + parsed._write(); + + const auto kaitaiBytes = writeStream.str(); + REQUIRE(kaitaiBytes.size() == savedBytes.size()); + REQUIRE(std::equal(kaitaiBytes.begin(), kaitaiBytes.end(), savedBytes.begin())); + + auto reloadSequence = loadSequenceWithMidIo( + mpc, + std::vector(kaitaiBytes.begin(), kaitaiBytes.end()), + "KTAIMID.MID"); + + REQUIRE(reloadSequence->getTrack(0)->getDeviceIndex() == 2); + REQUIRE(reloadSequence->getTrack(0)->getEvents().size() == 1); + auto note = std::dynamic_pointer_cast( + reloadSequence->getTrack(0)->getEvents()[0] + ); + REQUIRE(note); + REQUIRE(note->getNote() == 37); + REQUIRE(note->getVelocity() == 127); + REQUIRE(note->getDuration() == 10); +} + +TEST_CASE("Kaitai standard MIDI roundtrips broad sequence semantics", "[kaitai-mid]") +{ + mpc::Mpc mpc; + mpc::TestMpc::initializeTestMpcWithoutMidiServices(mpc); + + auto sequencer = mpc.getSequencer(); + auto stateManager = sequencer->getStateManager(); + auto sequence = sequencer->getSequence(0); + sequence->init(1); + sequence->setName("MIDBROAD"); + stateManager->drainQueue(); + + auto track0 = sequence->getTrack(0); + track0->setUsedIfCurrentlyUnused(); + track0->setBusType(mpc::sequencer::BusType::DRUM3); + track0->setDeviceIndex(7); + insertNote(track0, 0, 37, 65, 13, mpc::NoteVariationTypeTune, 80); + insertNote(track0, 120, 37, 35, 49, mpc::NoteVariationTypeDecay, 26); + insertNote(track0, 240, 37, 75, 101, mpc::NoteVariationTypeAttack, 31); + insertNote(track0, 360, 37, 30, 35, mpc::NoteVariationTypeFilter, 61); + + auto track1 = sequence->getTrack(1); + track1->setUsedIfCurrentlyUnused(); + track1->setBusType(mpc::sequencer::BusType::MIDI); + track1->setDeviceIndex(2); + insertNote(track1, 60, 45, 99, 88, mpc::NoteVariationTypeTune, 64); + stateManager->drainQueue(); + + const auto savedBytes = saveSequenceWithMidIo(sequence); + + std::stringstream parseStream( + std::string(savedBytes.begin(), savedBytes.end()), + std::ios::in | std::ios::out | std::ios::binary + ); + kaitai::kstream parseIo(&parseStream); + standard_midi_file_with_running_status_t parsed(&parseIo); + parsed._read(); + + std::stringstream writeStream(std::ios::in | std::ios::out | std::ios::binary); + kaitai::kstream writeIo(&writeStream); + parsed._set_io(&writeIo); + parsed._check(); + parsed._write(); + + const auto kaitaiBytes = writeStream.str(); + REQUIRE(kaitaiBytes.size() == savedBytes.size()); + REQUIRE(std::equal(kaitaiBytes.begin(), kaitaiBytes.end(), savedBytes.begin())); + + auto reloadSequence = loadSequenceWithMidIo( + mpc, + std::vector(kaitaiBytes.begin(), kaitaiBytes.end()), + "MIDBROAD.MID"); + + REQUIRE(reloadSequence->getTrack(0)->getBusType() == mpc::sequencer::BusType::DRUM3); + REQUIRE(reloadSequence->getTrack(0)->getDeviceIndex() == 7); + REQUIRE(reloadSequence->getTrack(1)->getBusType() == mpc::sequencer::BusType::MIDI); + REQUIRE(reloadSequence->getTrack(1)->getDeviceIndex() == 2); + + const std::vector expectedTrack0{ + {37, 65, 13, mpc::NoteVariationTypeTune}, + {37, 35, 49, mpc::NoteVariationTypeDecay}, + {37, 75, 101, mpc::NoteVariationTypeAttack}, + {37, 30, 35, mpc::NoteVariationTypeFilter}, + }; + const std::vector expectedTrack1{ + {45, 99, 88, mpc::NoteVariationTypeTune}, + }; + REQUIRE(collectParsedNotes(reloadSequence->getTrack(0)) == expectedTrack0); + REQUIRE(collectParsedNotes(reloadSequence->getTrack(1)) == expectedTrack1); +} + +TEST_CASE("Kaitai standard MIDI production load preserves broad sequence semantics", "[kaitai-mid]") +{ + mpc::Mpc sourceMpc; + mpc::TestMpc::initializeTestMpcWithoutMidiServices(sourceMpc); + + auto sourceSequencer = sourceMpc.getSequencer(); + auto sourceStateManager = sourceSequencer->getStateManager(); + auto sourceSequence = sourceSequencer->getSequence(0); + sourceSequence->init(1); + sourceSequence->setName("MIDBROAD"); + sourceStateManager->drainQueue(); + + auto track0 = sourceSequence->getTrack(0); + track0->setUsedIfCurrentlyUnused(); + track0->setBusType(mpc::sequencer::BusType::DRUM3); + track0->setDeviceIndex(7); + insertNote(track0, 0, 37, 65, 13, mpc::NoteVariationTypeTune, 80); + insertNote(track0, 120, 37, 35, 49, mpc::NoteVariationTypeDecay, 26); + insertNote(track0, 240, 37, 75, 101, mpc::NoteVariationTypeAttack, 31); + insertNote(track0, 360, 37, 30, 35, mpc::NoteVariationTypeFilter, 61); + + auto track1 = sourceSequence->getTrack(1); + track1->setUsedIfCurrentlyUnused(); + track1->setBusType(mpc::sequencer::BusType::MIDI); + track1->setDeviceIndex(2); + insertNote(track1, 60, 45, 99, 88, mpc::NoteVariationTypeTune, 64); + sourceStateManager->drainQueue(); + + auto output = std::make_shared(); + mpc::file::kaitai::MidIo::save(sourceSequence, output); + const auto saved = output->str(); + std::vector bytes(saved.begin(), saved.end()); + + mpc::Mpc mpc; + mpc::TestMpc::initializeTestMpcWithoutMidiServices(mpc); + auto midiFile = installMidiBytes(mpc, bytes, "BROAD.MID"); + REQUIRE(midiFile); + + const auto sequenceOrError = mpc::file::kaitai::MidIo::load(mpc, midiFile); + REQUIRE(sequenceOrError.has_value()); + + auto sequence = sequenceOrError.value(); + auto stateManager = mpc.getSequencer()->getStateManager(); + stateManager->drainQueue(); + + REQUIRE(sequence->getTrack(0)->getBusType() == mpc::sequencer::BusType::DRUM3); + REQUIRE(sequence->getTrack(0)->getDeviceIndex() == 7); + REQUIRE(sequence->getTrack(1)->getBusType() == mpc::sequencer::BusType::MIDI); + REQUIRE(sequence->getTrack(1)->getDeviceIndex() == 2); + + const std::vector expectedTrack0{ + {37, 65, 13, mpc::NoteVariationTypeTune}, + {37, 35, 49, mpc::NoteVariationTypeDecay}, + {37, 75, 101, mpc::NoteVariationTypeAttack}, + {37, 30, 35, mpc::NoteVariationTypeFilter}, + }; + const std::vector expectedTrack1{ + {45, 99, 88, mpc::NoteVariationTypeTune}, + }; + REQUIRE(collectParsedNotes(sequence->getTrack(0)) == expectedTrack0); + REQUIRE(collectParsedNotes(sequence->getTrack(1)) == expectedTrack1); +} + +TEST_CASE("Kaitai standard MIDI preserves zero-valued filter note variation", "[kaitai-mid]") +{ + mpc::Mpc mpc; + mpc::TestMpc::initializeTestMpcWithoutMidiServices(mpc); + + auto sequencer = mpc.getSequencer(); + auto stateManager = sequencer->getStateManager(); + auto sequence = sequencer->getSequence(0); + sequence->init(1); + sequence->setName("MIDFLT0"); + stateManager->drainQueue(); + + auto track = sequence->getTrack(0); + track->setUsedIfCurrentlyUnused(); + insertNote(track, 0, 37, 100, 24, mpc::NoteVariationTypeFilter, 0); + stateManager->drainQueue(); + + auto reloadSequence = loadSequenceWithMidIo(mpc, saveSequenceWithMidIo(sequence), "MIDFLT0.MID"); + auto reloadTrack = reloadSequence->getTrack(0); + + REQUIRE(reloadTrack->getEvents().size() == 1); + requireDetailedNote( + reloadTrack, + 0, + 0, + 37, + 100, + 24, + mpc::NoteVariationTypeFilter, + 0 + ); +} + +TEST_CASE("Kaitai standard MIDI preserves default-valued non-tune note variation", "[kaitai-mid]") +{ + mpc::Mpc mpc; + mpc::TestMpc::initializeTestMpcWithoutMidiServices(mpc); + + auto sequencer = mpc.getSequencer(); + auto stateManager = sequencer->getStateManager(); + auto sequence = sequencer->getSequence(0); + sequence->init(1); + sequence->setName("MIDFLT50"); + stateManager->drainQueue(); + + auto track = sequence->getTrack(0); + track->setUsedIfCurrentlyUnused(); + insertNote(track, 0, 37, 100, 24, mpc::NoteVariationTypeFilter, 50); + stateManager->drainQueue(); + + auto reloadSequence = loadSequenceWithMidIo(mpc, saveSequenceWithMidIo(sequence), "MIDFLT50.MID"); + auto reloadTrack = reloadSequence->getTrack(0); + + REQUIRE(reloadTrack->getEvents().size() == 1); + requireDetailedNote( + reloadTrack, + 0, + 0, + 37, + 100, + 24, + mpc::NoteVariationTypeFilter, + 50 + ); +} + +TEST_CASE("Kaitai standard MIDI preserves RESIST INTRO note variation semantics", "[kaitai-mid][resist]") +{ + mpc::Mpc mpc; + mpc::TestMpc::initializeTestMpcWithoutMidiServices(mpc); + + auto allFile = installMidiResourceFile( + mpc, + "test/RealMpc2000xl/Resist/RESIST.ALL", + "RESIST.ALL" + ); + REQUIRE(allFile); + + mpc::disk::AllLoader::loadEverythingFromFile(mpc, allFile.get()); + mpc.getSequencer()->getStateManager()->drainQueue(); + + auto sourceSequence = mpc.getSequencer()->getSequence(0); + REQUIRE(sourceSequence->isUsed()); + REQUIRE(sourceSequence->getName() == "Intro"); + + const auto sourceNotes = collectDetailedParsedNotes(sourceSequence->getTrack(0)); + auto reloadedSequence = loadSequenceWithMidIo(mpc, saveSequenceWithMidIo(sourceSequence), "INTRO.MID"); + const auto reloadedNotes = collectDetailedParsedNotes(reloadedSequence->getTrack(0)); + + REQUIRE(reloadedNotes.size() == sourceNotes.size()); + for (size_t i = 0; i < sourceNotes.size(); ++i) + { + CAPTURE(i); + CAPTURE(sourceNotes[i].tick, reloadedNotes[i].tick); + CAPTURE(sourceNotes[i].note, reloadedNotes[i].note); + CAPTURE(sourceNotes[i].velocity, reloadedNotes[i].velocity); + CAPTURE(sourceNotes[i].duration, reloadedNotes[i].duration); + CAPTURE(sourceNotes[i].variationType, reloadedNotes[i].variationType); + CAPTURE(sourceNotes[i].variationValue, reloadedNotes[i].variationValue); + REQUIRE(reloadedNotes[i] == sourceNotes[i]); + } +} + +TEST_CASE("Kaitai standard MIDI roundtrips MPC2000XL meta and channel events", "[kaitai-mid]") +{ + mpc::Mpc mpc; + mpc::TestMpc::initializeTestMpcWithoutMidiServices(mpc); + + auto sequencer = mpc.getSequencer(); + auto stateManager = sequencer->getStateManager(); + auto sequence = sequencer->getSequence(0); + sequence->init(1); + sequence->setName("MIDMETA"); + sequence->setLoopEnabled(false); + sequence->setLastLoopBarIndex(mpc::BarIndex(1)); + sequence->setInitialTempo(98.7); + sequence->setTempoChangeOn(true); + sequence->addTempoChangeEvent(96, 1250); + sequence->setTimeSignature(1, 3, 4); + + auto track = sequence->getTrack(0); + track->setUsedIfCurrentlyUnused(); + track->setName("CTRLMETA"); + track->setBusType(mpc::sequencer::BusType::MIDI); + track->setDeviceIndex(5); + + insertNote(track, 0, 57, 101, 24, mpc::NoteVariationTypeTune, 64); + + mpc::sequencer::EventData polyPressure; + polyPressure.type = mpc::sequencer::EventType::PolyPressure; + polyPressure.tick = 12; + polyPressure.noteNumber = mpc::NoteNumber(57); + polyPressure.amount = 54; + track->acquireAndInsertEvent(polyPressure); + + mpc::sequencer::EventData controlChange; + controlChange.type = mpc::sequencer::EventType::ControlChange; + controlChange.tick = 24; + controlChange.controllerNumber = 7; + controlChange.controllerValue = 99; + track->acquireAndInsertEvent(controlChange); + + mpc::sequencer::EventData programChange; + programChange.type = mpc::sequencer::EventType::ProgramChange; + programChange.tick = 36; + programChange.programChangeProgramIndex = mpc::ProgramIndex(12); + track->acquireAndInsertEvent(programChange); + + mpc::sequencer::EventData channelPressure; + channelPressure.type = mpc::sequencer::EventType::ChannelPressure; + channelPressure.tick = 48; + channelPressure.amount = 60; + track->acquireAndInsertEvent(channelPressure); + + mpc::sequencer::EventData mixer; + mixer.type = mpc::sequencer::EventType::Mixer; + mixer.tick = 60; + mixer.mixerParameter = 3; + mixer.mixerPad = 12; + mixer.mixerValue = 87; + track->acquireAndInsertEvent(mixer); + + stateManager->drainQueue(); + + auto reloaded = loadSequenceWithMidIo(mpc, saveSequenceWithMidIo(sequence), "MIDMETA.MID"); + auto reloadedTrack = reloaded->getTrack(0); + + REQUIRE(reloaded->getName() == "MIDMETA "); + REQUIRE(reloaded->isLoopEnabled() == false); + REQUIRE(reloaded->getFirstLoopBarIndex() == mpc::BarIndex(0)); + REQUIRE(reloaded->getLastLoopBarIndex() == mpc::EndOfSequence); + REQUIRE(mpc.getSequencer()->getTransport()->isTempoSourceSequence()); + REQUIRE(std::abs(reloaded->getInitialTempo() - 98.7) < 0.2); + REQUIRE(reloaded->getNumerator(0) == 4); + REQUIRE(reloaded->getDenominator(0) == 4); + REQUIRE(reloaded->getNumerator(1) == 3); + REQUIRE(reloaded->getDenominator(1) == 4); + REQUIRE(reloadedTrack->getName() == "CTRLMETA "); + REQUIRE(reloadedTrack->getBusType() == mpc::sequencer::BusType::MIDI); + REQUIRE(reloadedTrack->getDeviceIndex() == 5); + + const auto notes = collectParsedNotes(reloadedTrack); + REQUIRE(notes == std::vector{ + {57, 101, 24, mpc::NoteVariationTypeTune} + }); + + const auto reloadedPolyPressure = + findEventOfType(reloadedTrack); + REQUIRE(reloadedPolyPressure); + REQUIRE(reloadedPolyPressure->getNote() == 57); + REQUIRE(reloadedPolyPressure->getAmount() == 54); + + const auto reloadedControlChange = + findEventOfType(reloadedTrack); + REQUIRE(reloadedControlChange); + REQUIRE(reloadedControlChange->getController() == 7); + REQUIRE(reloadedControlChange->getAmount() == 99); + + const auto reloadedProgramChange = + findEventOfType(reloadedTrack); + REQUIRE(reloadedProgramChange); + REQUIRE(reloadedProgramChange->getProgram() == 12); + + const auto reloadedChannelPressure = + findEventOfType(reloadedTrack); + REQUIRE(reloadedChannelPressure); + REQUIRE(reloadedChannelPressure->getAmount() == 60); + + const auto reloadedMixer = + findEventOfType(reloadedTrack); + REQUIRE(reloadedMixer); + REQUIRE(reloadedMixer->getParameter() == 3); + REQUIRE(reloadedMixer->getPad() == 12); + REQUIRE(reloadedMixer->getValue() == 87); + +} + +TEST_CASE("Kaitai standard MIDI parses and rewrites real 2KXL SEQ", "[kaitai-mid][real-2kxl]") +{ + auto fs = cmrc::mpctest::get_filesystem(); + auto file = fs.open("test/RealMpc2000xl/Mid/mpc2000xl_seq_variations.MID"); + const std::string originalBytes( + std::string_view(file.begin(), file.end() - file.begin()) + ); + + std::stringstream parseStream( + originalBytes, + std::ios::in | std::ios::out | std::ios::binary + ); + kaitai::kstream parseIo(&parseStream); + standard_midi_file_with_running_status_t parsed(&parseIo); + parsed._read(); + + std::stringstream writeStream(std::ios::in | std::ios::out | std::ios::binary); + kaitai::kstream writeIo(&writeStream); + parsed._set_io(&writeIo); + parsed._check(); + parsed._write(); + + const auto rewrittenBytes = writeStream.str(); + REQUIRE(rewrittenBytes.size() == originalBytes.size()); + REQUIRE(std::equal(rewrittenBytes.begin(), rewrittenBytes.end(), originalBytes.begin())); + + mpc::Mpc mpc; + mpc::TestMpc::initializeTestMpc(mpc); + auto sequence = mpc.getSequencer()->getSequence(mpc::TempSequenceIndex); + auto stateManager = mpc.getSequencer()->getStateManager(); + sequence->init(1); + stateManager->drainQueue(); + + auto midiFile = installMidiBytes( + mpc, + std::vector(rewrittenBytes.begin(), rewrittenBytes.end()), + "SEQ.MID"); + REQUIRE(midiFile); + const auto sequenceOrError = mpc::file::kaitai::MidIo::load(mpc, midiFile); + REQUIRE(sequenceOrError.has_value()); + sequence = sequenceOrError.value(); + stateManager->drainQueue(); + + REQUIRE(sequence->getBarCount() == 1); + + int totalEvents = 0; + for (int trackIndex = 0; trackIndex < 64; ++trackIndex) { + totalEvents += static_cast(sequence->getTrack(trackIndex)->getEvents().size()); + } + REQUIRE(totalEvents == 7); + + const auto parsedNotes = collectParsedNotes(sequence->getTrack(0)); + REQUIRE(parsedNotes.size() == 7U); + + const std::vector expected{ + {0, 65, 13, mpc::NoteVariationTypeTune}, + {0, 35, 49, mpc::NoteVariationTypeDecay}, + {0, 75, 101, mpc::NoteVariationTypeAttack}, + {0, 30, 35, mpc::NoteVariationTypeFilter}, + {0, 22, 89, mpc::NoteVariationTypeFilter}, + {0, 46, 174, mpc::NoteVariationTypeTune}, + {0, 19, 161, mpc::NoteVariationTypeTune}, + }; + REQUIRE(parsedNotes.size() == expected.size()); + for (std::size_t i = 0; i < expected.size(); ++i) { + CAPTURE(i); + CAPTURE(parsedNotes[i].note); + CHECK(parsedNotes[i].velocity == expected[i].velocity); + CHECK(parsedNotes[i].duration == expected[i].duration); + CHECK(parsedNotes[i].variationType == expected[i].variationType); + } +} + +TEST_CASE("Kaitai standard MIDI loads real 2KXL SEQ through production seam", "[kaitai-mid][real-2kxl]") +{ + mpc::Mpc mpc; + mpc::TestMpc::initializeTestMpcWithoutMidiServices(mpc); + + auto midiFile = installMidiResourceFile( + mpc, + "test/RealMpc2000xl/Mid/mpc2000xl_seq_variations.MID", + "SEQ.MID" + ); + REQUIRE(midiFile); + + const auto sequenceOrError = mpc::file::kaitai::MidIo::load(mpc, midiFile); + REQUIRE(sequenceOrError.has_value()); + + auto sequence = sequenceOrError.value(); + auto stateManager = mpc.getSequencer()->getStateManager(); + stateManager->drainQueue(); + + REQUIRE(sequence == mpc.getSequencer()->getSequence(mpc::TempSequenceIndex)); + REQUIRE(sequence->getBarCount() == 1); + + const auto track0 = sequence->getTrack(0); + REQUIRE(track0->getBusType() == mpc::sequencer::BusType::DRUM2); + REQUIRE(track0->getDeviceIndex() == 7); + + int totalEvents = 0; + for (int trackIndex = 0; trackIndex < 64; ++trackIndex) { + totalEvents += static_cast(sequence->getTrack(trackIndex)->getEvents().size()); + } + REQUIRE(totalEvents == 7); + + const auto parsedNotes = collectParsedNotes(track0); + const std::vector expected{ + {37, 65, 13, mpc::NoteVariationTypeTune}, + {37, 35, 49, mpc::NoteVariationTypeDecay}, + {37, 75, 101, mpc::NoteVariationTypeAttack}, + {37, 30, 35, mpc::NoteVariationTypeFilter}, + {37, 22, 89, mpc::NoteVariationTypeFilter}, + {37, 46, 174, mpc::NoteVariationTypeTune}, + {37, 19, 161, mpc::NoteVariationTypeTune}, + }; + REQUIRE(parsedNotes == expected); +} diff --git a/src/test/KaitaiPgmTest.cpp b/src/test/KaitaiPgmTest.cpp new file mode 100644 index 000000000..81c1c849e --- /dev/null +++ b/src/test/KaitaiPgmTest.cpp @@ -0,0 +1,1209 @@ +#include + +#include "TestMpc.hpp" +#include "Mpc.hpp" +#include "IntTypes.hpp" +#include "disk/AbstractDisk.hpp" +#include "disk/MpcFile.hpp" +#include "disk/ProgramLoader.hpp" +#include "engine/IndivFxMixer.hpp" +#include "engine/EngineHost.hpp" +#include "engine/StereoMixer.hpp" +#include "file/kaitai/PgmIo.hpp" +#include "file/kaitai/generated/mpc2000xl_pgm.h" +#include "file/kaitai/generated/mpc3000_pgm_v3.h" +#include "performance/PerformanceManager.hpp" +#include "sampler/Pad.hpp" +#include "sampler/PgmSlider.hpp" +#include "sampler/Program.hpp" +#include "sampler/Sampler.hpp" +#include "sampler/SoundGenerationMode.hpp" +#include "sampler/VoiceOverlapMode.hpp" + +#include +#include + +#include +#include +#include +#include +#include + +CMRC_DECLARE(mpctest); + +namespace { + +std::vector rewriteProgram1WithMutations( + const std::function& mutate) +{ + auto fs = cmrc::mpctest::get_filesystem(); + auto file = fs.open("test/ProgramLoading/program1/PROGRAM1.PGM"); + const std::string originalBytes( + std::string_view(file.begin(), file.end() - file.begin()) + ); + + std::stringstream parseStream( + originalBytes, + std::ios::in | std::ios::out | std::ios::binary + ); + kaitai::kstream parseIo(&parseStream); + mpc2000xl_pgm_t parsed(&parseIo); + parsed._read(); + + mutate(parsed); + + std::stringstream writeStream(std::ios::in | std::ios::out | std::ios::binary); + kaitai::kstream writeIo(&writeStream); + parsed._set_io(&writeIo); + parsed._check(); + parsed._write(); + + const auto rewrittenBytes = writeStream.str(); + return std::vector(rewrittenBytes.begin(), rewrittenBytes.end()); +} + +std::vector rewriteRealMpc3000ProgramWithMutations( + const std::string& resourcePath, + const std::function& mutate) +{ + auto fs = cmrc::mpctest::get_filesystem(); + auto file = fs.open(resourcePath); + const std::string originalBytes( + std::string_view(file.begin(), file.end() - file.begin()) + ); + + std::stringstream parseStream( + originalBytes, + std::ios::in | std::ios::out | std::ios::binary + ); + kaitai::kstream parseIo(&parseStream); + mpc3000_pgm_v3_t parsed(&parseIo); + parsed._read(); + + mutate(parsed); + + std::stringstream writeStream(std::ios::in | std::ios::out | std::ios::binary); + kaitai::kstream writeIo(&writeStream); + parsed._set_io(&writeIo); + parsed._check(); + parsed._write(); + + const auto rewrittenBytes = writeStream.str(); + return std::vector(rewrittenBytes.begin(), rewrittenBytes.end()); +} + +void applyBroadProgramMutation(mpc2000xl_pgm_t& parsed) +{ + auto* note35 = parsed.note_parameters()->at(0).get(); + note35->set_sound_index(255); + note35->set_sound_generation_mode(mpc2000xl_pgm_t::SOUND_GENERATION_MODE_DCY_SW); + note35->set_velocity_range_lower(1); + note35->set_velocity_range_upper(127); + note35->set_also_play_use_note_1(41); + note35->set_also_play_use_note_2(42); + note35->set_voice_overlap_mode(mpc2000xl_pgm_t::VOICE_OVERLAP_MODE_NOTE_OFF); + note35->set_mute_assign_1(43); + note35->set_mute_assign_2(44); + note35->set_tune(-120); + note35->set_attack(99); + note35->set_decay(98); + note35->set_decay_mode(mpc2000xl_pgm_t::DECAY_MODE_START); + note35->set_cutoff(17); + note35->set_resonance(18); + note35->set_velocity_envelope_to_filter_attack(19); + note35->set_velocity_envelope_to_filter_decay(20); + note35->set_velocity_envelope_to_filter_amount(21); + note35->set_velocity_to_level(22); + note35->set_velocity_to_attack(23); + note35->set_velocity_to_start(24); + note35->set_velocity_to_cutoff(25); + note35->set_slider_parameter(mpc2000xl_pgm_t::SLIDER_PARAMETER_FILTER); + note35->set_velocity_to_pitch(26); + + auto* mixer35 = parsed.pad_mixers()->at(0).get(); + mixer35->set_fx_output(mpc2000xl_pgm_t::FX_OUTPUT_R2); + mixer35->set_volume(12); + mixer35->set_pan(13); + mixer35->set_volume_individual(14); + mixer35->set_output(2); + mixer35->set_effects_send_level(15); + + parsed.slider()->set_note(52); + parsed.slider()->set_tune_low(-119); + parsed.slider()->set_tune_high(118); + parsed.slider()->set_decay_low(11); + parsed.slider()->set_decay_high(12); + parsed.slider()->set_attack_low(13); + parsed.slider()->set_attack_high(14); + parsed.slider()->set_filter_low(-15); + parsed.slider()->set_filter_high(16); + + parsed.set_program_change(126); + parsed.pad_to_note_mapping()->at(0) = 50; +} + +std::string mpc3000SoundNameField(const std::string& name) +{ + return name.substr(0, 16); +} + +void requireBroadMutatedProgramState( + const std::shared_ptr& program, + const std::vector& soundNames) +{ + REQUIRE(program != nullptr); + REQUIRE(soundNames.size() == 2U); + REQUIRE(soundNames[0] == std::string("sound1") + std::string(10, ' ')); + REQUIRE(soundNames[1] == std::string("sound2") + std::string(10, ' ')); + + REQUIRE(program->getPad(0)->getNote() == 50); + REQUIRE(program->getMidiProgramChange() == 127); + + const auto note35 = program->getNoteParameters(35); + REQUIRE(note35->getSoundIndex() == -1); + REQUIRE(note35->getSoundGenerationMode() == + mpc::sampler::SoundGenerationMode::DecaySwitch); + REQUIRE(note35->getVelocityRangeLower() == 1); + REQUIRE(note35->getVelocityRangeUpper() == 127); + REQUIRE(note35->getOptionalNoteA() == 41); + REQUIRE(note35->getOptionalNoteB() == 42); + REQUIRE(note35->getMuteAssignA() == 43); + REQUIRE(note35->getMuteAssignB() == 44); + REQUIRE(note35->getTune() == -120); + REQUIRE(note35->getAttack() == 99); + REQUIRE(note35->getDecay() == 98); + REQUIRE(note35->getDecayMode() == 1); + REQUIRE(note35->getFilterFrequency() == 17); + REQUIRE(note35->getFilterResonance() == 18); + REQUIRE(note35->getFilterAttack() == 19); + REQUIRE(note35->getFilterDecay() == 20); + REQUIRE(note35->getFilterEnvelopeAmount() == 21); + REQUIRE(note35->getVeloToLevel() == 22); + REQUIRE(note35->getVelocityToAttack() == 23); + REQUIRE(note35->getVelocityToStart() == 24); + REQUIRE(note35->getVelocityToFilterFrequency() == 25); + REQUIRE(note35->getSliderParameterNumber() == 3); + REQUIRE(note35->getVelocityToPitch() == 26); + REQUIRE(note35->getVoiceOverlapMode() == mpc::sampler::VoiceOverlapMode::NOTE_OFF); + + const auto stereoMixer = note35->getStereoMixer(); + const auto indivFxMixer = note35->getIndivFxMixer(); + REQUIRE(stereoMixer->getLevel() == 12); + REQUIRE(stereoMixer->getPanning() == 13); + REQUIRE(indivFxMixer->getVolumeIndividualOut() == 14); + REQUIRE(indivFxMixer->getOutput() == 2); + REQUIRE(indivFxMixer->getFxPath() == 4); + REQUIRE(indivFxMixer->getFxSendLevel() == 15); + + const auto slider = program->getSlider(); + REQUIRE(slider->getNote() == 52); + REQUIRE(slider->getTuneLowRange() == -119); + REQUIRE(slider->getTuneHighRange() == 118); + REQUIRE(slider->getDecayLowRange() == 11); + REQUIRE(slider->getDecayHighRange() == 12); + REQUIRE(slider->getAttackLowRange() == 13); + REQUIRE(slider->getAttackHighRange() == 14); + REQUIRE(slider->getFilterLowRange() == -15); + REQUIRE(slider->getFilterHighRange() == 16); +} + +void prepareProgramLoadingResources(mpc::Mpc& mpc) +{ + auto disk = mpc.getDisk(); + auto fs = cmrc::mpctest::get_filesystem(); + + for (auto&& entry : fs.iterate_directory("test/ProgramLoading")) { + disk->newFolder(entry.filename()); + disk->initFiles(); + disk->moveForward(entry.filename()); + disk->initFiles(); + + for (auto&& entry2 : fs.iterate_directory("test/ProgramLoading/" + entry.filename())) { + auto file = fs.open("test/ProgramLoading/" + entry.filename() + "/" + entry2.filename()); + std::vector data(file.begin(), file.end()); + auto newFile = disk->newFile(entry2.filename()); + newFile->setFileData(data); + } + + disk->moveBack(); + disk->initFiles(); + } +} + +std::shared_ptr loadProgram1(mpc::Mpc& mpc) +{ + prepareProgramLoadingResources(mpc); + + auto disk = mpc.getDisk(); + disk->moveForward("program1"); + disk->initFiles(); + auto pgmFile = disk->getFile("PROGRAM1.PGM"); + + auto sampler = mpc.getSampler(); + sampler->deleteAllPrograms(false); + sampler->deleteAllSamples(); + + auto program = sampler->createNewProgramAddFirstAvailableSlotAndThen({}).lock(); + (void)mpc::disk::ProgramLoader::loadProgram(mpc, pgmFile, program, 0); + mpc.getEngineHost()->prepareProcessBlock(512); + return program; +} + +std::shared_ptr loadProgram1FromBytes( + mpc::Mpc& mpc, + const std::vector& programBytes) +{ + prepareProgramLoadingResources(mpc); + + auto disk = mpc.getDisk(); + disk->moveForward("program1"); + disk->initFiles(); + auto pgmFile = disk->getFile("PROGRAM1.PGM"); + auto mutableBytes = programBytes; + pgmFile->setFileData(mutableBytes); + + auto sampler = mpc.getSampler(); + sampler->deleteAllPrograms(false); + sampler->deleteAllSamples(); + + auto program = sampler->createNewProgramAddFirstAvailableSlotAndThen({}).lock(); + (void)mpc::disk::ProgramLoader::loadProgram(mpc, pgmFile, program, 0); + mpc.getEngineHost()->prepareProcessBlock(512); + return program; +} + +std::shared_ptr loadProgram1FromBytesWithoutRender( + mpc::Mpc& mpc, + const std::vector& programBytes) +{ + prepareProgramLoadingResources(mpc); + + auto disk = mpc.getDisk(); + disk->moveForward("program1"); + disk->initFiles(); + auto pgmFile = disk->getFile("PROGRAM1.PGM"); + auto mutableBytes = programBytes; + pgmFile->setFileData(mutableBytes); + + auto sampler = mpc.getSampler(); + sampler->deleteAllPrograms(false); + sampler->deleteAllSamples(); + + auto program = sampler->createNewProgramAddFirstAvailableSlotAndThen({}).lock(); + (void)mpc::disk::ProgramLoader::loadProgram(mpc, pgmFile, program, 0); + mpc.getPerformanceManager().lock()->drainQueue(); + return program; +} + +std::shared_ptr loadProgram1FileFromBytes( + mpc::Mpc& mpc, + const std::vector& programBytes) +{ + prepareProgramLoadingResources(mpc); + + auto disk = mpc.getDisk(); + disk->moveForward("program1"); + disk->initFiles(); + auto pgmFile = disk->getFile("PROGRAM1.PGM"); + auto mutableBytes = programBytes; + pgmFile->setFileData(mutableBytes); + return pgmFile; +} + +std::shared_ptr loadProgram1ViaPgmIoFromBytes( + mpc::Mpc& mpc, + const std::vector& programBytes, + std::vector& soundNames) +{ + auto pgmFile = loadProgram1FileFromBytes(mpc, programBytes); + + auto sampler = mpc.getSampler(); + sampler->deleteAllPrograms(false); + sampler->deleteAllSamples(); + + auto program = sampler->createNewProgramAddFirstAvailableSlotAndThen({}).lock(); + auto result = mpc::file::kaitai::PgmIo::loadProgram(mpc, pgmFile, program, soundNames); + REQUIRE(result); + mpc.getPerformanceManager().lock()->drainQueue(); + return program; +} + +std::shared_ptr loadProgramViaPgmIoFromBytes( + mpc::Mpc& mpc, + const std::string& filename, + const std::vector& programBytes, + std::vector& soundNames) +{ + auto disk = mpc.getDisk(); + auto file = disk->newFile(filename); + auto mutableBytes = programBytes; + file->setFileData(mutableBytes); + disk->initFiles(); + + auto sampler = mpc.getSampler(); + sampler->deleteAllPrograms(false); + sampler->deleteAllSamples(); + + auto program = sampler->createNewProgramAddFirstAvailableSlotAndThen({}).lock(); + auto result = mpc::file::kaitai::PgmIo::loadProgram(mpc, file, program, soundNames); + REQUIRE(result); + mpc.getPerformanceManager().lock()->drainQueue(); + return program; +} + +void requireProgram1LoadedState(mpc::Mpc& mpc, + const std::shared_ptr& program) +{ + REQUIRE(program != nullptr); + REQUIRE(program->getName() == "PROGRAM1"); + + auto sampler = mpc.getSampler(); + REQUIRE(sampler->getSoundCount() == 2); + REQUIRE(sampler->getSoundName(0) == "sound1"); + REQUIRE(sampler->getSoundName(1) == "sound2"); + REQUIRE(sampler->getSound(1)->getEnd() == 1000); + + const auto note35 = program->getNoteParameters(35); + const auto note36 = program->getNoteParameters(36); + const auto note37 = program->getNoteParameters(37); + + REQUIRE(note35->getSoundIndex() == 0); + REQUIRE(note36->getSoundIndex() == 1); + REQUIRE(note37->getSoundIndex() == -1); + + REQUIRE(note35->getSoundGenerationMode() == + mpc::sampler::SoundGenerationMode::Normal); + REQUIRE(note35->getVelocityRangeLower() == 44); + REQUIRE(note35->getVelocityRangeUpper() == 88); + REQUIRE(note35->getTune() == 0); + REQUIRE(note35->getAttack() == 0); + REQUIRE(note35->getDecay() == 5); + REQUIRE(note35->getDecayMode() == 0); + REQUIRE(note35->getFilterFrequency() == 100); + REQUIRE(note35->getFilterResonance() == 0); + REQUIRE(note35->getFilterAttack() == 0); + REQUIRE(note35->getFilterDecay() == 0); + REQUIRE(note35->getFilterEnvelopeAmount() == 0); + REQUIRE(note35->getVeloToLevel() == 100); + REQUIRE(note35->getVelocityToAttack() == 0); + REQUIRE(note35->getVelocityToStart() == 0); + REQUIRE(note35->getVelocityToFilterFrequency() == 0); + REQUIRE(note35->getSliderParameterNumber() == 0); + REQUIRE(note35->getVelocityToPitch() == 0); + REQUIRE(note35->getVoiceOverlapMode() == mpc::sampler::VoiceOverlapMode::POLY); + REQUIRE(note35->getOptionalNoteA() == mpc::NoDrumNoteAssigned); + REQUIRE(note35->getOptionalNoteB() == mpc::NoDrumNoteAssigned); + REQUIRE(note35->getMuteAssignA() == mpc::NoDrumNoteAssigned); + REQUIRE(note35->getMuteAssignB() == mpc::NoDrumNoteAssigned); + + const auto stereoMixer = note35->getStereoMixer(); + const auto indivFxMixer = note35->getIndivFxMixer(); + REQUIRE(stereoMixer->getLevel() == mpc::MaxDrumMixerLevel); + REQUIRE(stereoMixer->getPanning() == mpc::PanningCenter); + REQUIRE(indivFxMixer->getVolumeIndividualOut() == mpc::MaxDrumMixerLevel); + REQUIRE(indivFxMixer->getOutput() == mpc::MinDrumMixerIndividualOut); + REQUIRE(indivFxMixer->getFxPath() == mpc::MinDrumMixerFxPath); + REQUIRE(indivFxMixer->getFxSendLevel() == mpc::MinDrumMixerLevel); + + const auto slider = program->getSlider(); + REQUIRE(slider->getNote() == mpc::NoDrumNoteAssigned); + REQUIRE(slider->getTuneLowRange() == -120); + REQUIRE(slider->getTuneHighRange() == 120); + REQUIRE(slider->getDecayLowRange() == 12); + REQUIRE(slider->getDecayHighRange() == 45); + REQUIRE(slider->getAttackLowRange() == 0); + REQUIRE(slider->getAttackHighRange() == 20); + REQUIRE(slider->getFilterLowRange() == -50); + REQUIRE(slider->getFilterHighRange() == 50); + REQUIRE(slider->getControlChange() == 0); + REQUIRE(slider->getParameter() == mpc::NoteVariationTypeTune); + REQUIRE(program->getMidiProgramChange() == 1); +} + +} + +TEST_CASE("Kaitai MPC2000 PGM parses and rewrites PROGRAM1", "[kaitai-pgm]") +{ + auto fs = cmrc::mpctest::get_filesystem(); + auto file = fs.open("test/ProgramLoading/program1/PROGRAM1.PGM"); + const std::string originalBytes( + std::string_view(file.begin(), file.end() - file.begin()) + ); + + std::stringstream parseStream( + originalBytes, + std::ios::in | std::ios::out | std::ios::binary + ); + kaitai::kstream parseIo(&parseStream); + mpc2000xl_pgm_t parsed(&parseIo); + parsed._read(); + + REQUIRE(parsed.magic() == std::string("\x07\x04", 2)); + REQUIRE(parsed.sound_count() == 2); + REQUIRE(parsed.sound_names() != nullptr); + REQUIRE(parsed.sound_names()->size() == 2U); + REQUIRE(parsed.sound_names()->at(0).size() == 17U); + REQUIRE(parsed.sound_names()->at(0).substr(0, 6) == "sound1"); + REQUIRE(parsed.sound_names()->at(0).back() == '\0'); + REQUIRE(parsed.sound_names()->at(1).size() == 17U); + REQUIRE(parsed.sound_names()->at(1).substr(0, 6) == "sound2"); + REQUIRE(parsed.sound_names()->at(1).back() == '\0'); + REQUIRE(parsed.name().size() == 17U); + REQUIRE(parsed.name().substr(0, 16) == "VMPCTESTPROGRAM1"); + REQUIRE(parsed.name().back() == '\0'); + REQUIRE(parsed.note_parameters() != nullptr); + REQUIRE(parsed.note_parameters()->size() == 64U); + REQUIRE(parsed.note_parameters()->at(0)->sound_index() == 0); + REQUIRE(parsed.note_parameters()->at(1)->sound_index() == 1); + // Hardware-backed comparison shows this standalone byte stores MIDI + // program change as a zero-based value. + REQUIRE(parsed.program_change() == 0); + REQUIRE(parsed.pad_to_note_mapping() != nullptr); + REQUIRE(parsed.pad_to_note_mapping()->size() == 64U); + + std::stringstream writeStream(std::ios::in | std::ios::out | std::ios::binary); + kaitai::kstream writeIo(&writeStream); + parsed._set_io(&writeIo); + parsed._check(); + parsed._write(); + + REQUIRE(writeStream.str() == originalBytes); +} + +TEST_CASE("Kaitai MPC2000 PGM parses and rewrites real 2KXL NEWPGM-A", "[kaitai-pgm][real-2kxl]") +{ + auto fs = cmrc::mpctest::get_filesystem(); + auto file = fs.open("test/RealMpc2000xl/Pgm/NEWPGM-A.PGM"); + const std::string originalBytes( + std::string_view(file.begin(), file.end() - file.begin()) + ); + + std::stringstream parseStream( + originalBytes, + std::ios::in | std::ios::out | std::ios::binary + ); + kaitai::kstream parseIo(&parseStream); + mpc2000xl_pgm_t parsed(&parseIo); + parsed._read(); + + REQUIRE(parsed.magic() == std::string("\x07\x04", 2)); + REQUIRE(parsed.sound_count() == 2); + REQUIRE(parsed.sound_names() != nullptr); + REQUIRE(parsed.sound_names()->size() == 2U); + REQUIRE(parsed.sound_names()->at(0).substr(0, 2) == "ML"); + REQUIRE(parsed.sound_names()->at(0).back() == '\0'); + REQUIRE(parsed.sound_names()->at(1).substr(0, 1) == "M"); + REQUIRE(parsed.sound_names()->at(1).back() == '\0'); + REQUIRE(parsed.name().size() == 17U); + REQUIRE(parsed.name().substr(0, 8) == "NewPgm-A"); + REQUIRE(parsed.name().back() == '\0'); + REQUIRE(parsed.note_parameters() != nullptr); + REQUIRE(parsed.note_parameters()->size() == 64U); + REQUIRE(parsed.note_parameters()->at(0)->sound_index() == 255); + REQUIRE(parsed.note_parameters()->at(1)->sound_index() == 0); + + std::stringstream writeStream(std::ios::in | std::ios::out | std::ios::binary); + kaitai::kstream writeIo(&writeStream); + parsed._set_io(&writeIo); + parsed._check(); + parsed._write(); + + REQUIRE(writeStream.str() == originalBytes); +} + +TEST_CASE("Kaitai MPC3000 PGM parses a real hardware empty PROGRAM.pgm", "[kaitai-pgm][real-mpc3000]") +{ + auto fs = cmrc::mpctest::get_filesystem(); + auto file = fs.open("test/RealMpc3000/Pgm/PROGRAM.pgm"); + const std::string bytes( + std::string_view(file.begin(), file.end() - file.begin()) + ); + + std::stringstream parseStream( + bytes, + std::ios::in | std::ios::out | std::ios::binary + ); + kaitai::kstream parseIo(&parseStream); + mpc3000_pgm_v3_t parsed(&parseIo); + parsed._read(); + + REQUIRE(static_cast(bytes[0]) == 0x07); + REQUIRE(static_cast(bytes[1]) == 0x00); + REQUIRE(parsed.sound_names() != nullptr); + REQUIRE(parsed.sound_names()->size() == 128U); + REQUIRE(parsed.sound_sizes() != nullptr); + REQUIRE(parsed.sound_sizes()->size() == 128U); + REQUIRE(parsed.sound_assignments() != nullptr); + REQUIRE(parsed.sound_assignments()->size() == 64U); + REQUIRE(parsed.mixer_screens() != nullptr); + REQUIRE(parsed.mixer_screens()->size() == 64U); + REQUIRE(parsed.pad_note_number_assignments() != nullptr); + REQUIRE(parsed.pad_note_number_assignments()->size() == 64U); + + REQUIRE(parsed.sound_names()->at(0)->name().empty()); + REQUIRE(parsed.sound_sizes()->at(0) == 0U); + REQUIRE(parsed.program_name().substr(0, 10) == "PROGRAM_01"); + + REQUIRE(parsed.note_variation_screen()->note_number_assignment() == 34U); + REQUIRE(parsed.note_variation_screen()->tuning_low_range() == 136U); + REQUIRE(parsed.note_variation_screen()->tuning_hi_range() == 120U); + REQUIRE(parsed.note_variation_screen()->attack_low_range() == 12U); + REQUIRE(parsed.note_variation_screen()->attack_hi_range() == 45U); + REQUIRE(parsed.note_variation_screen()->decay_low_range() == 0U); + REQUIRE(parsed.note_variation_screen()->decay_hi_range() == 20U); + REQUIRE(parsed.note_variation_screen()->filter_low_range() == 206U); + REQUIRE(parsed.note_variation_screen()->filter_hi_range() == 50U); + + REQUIRE(parsed.effects_screen()->effects_on() == false); + REQUIRE(parsed.effects_screen()->delay_volume_tap1() == 50U); + REQUIRE(parsed.effects_screen()->delay_volume_tap2() == 0U); + REQUIRE(parsed.effects_screen()->delay_volume_tap3() == 0U); + REQUIRE(parsed.effects_screen()->delay_pan_tap1() == 50U); + REQUIRE(parsed.effects_screen()->delay_pan_tap2() == 50U); + REQUIRE(parsed.effects_screen()->delay_pan_tap3() == 50U); + REQUIRE(parsed.effects_screen()->delay_msecs_tap1() == 100U); + REQUIRE(parsed.effects_screen()->delay_msecs_tap2() == 200U); + REQUIRE(parsed.effects_screen()->delay_msecs_tap3() == 300U); + REQUIRE(parsed.effects_screen()->delay_feedback_tap1() == 0U); + REQUIRE(parsed.effects_screen()->delay_feedback_tap2() == 0U); + REQUIRE(parsed.effects_screen()->delay_feedback_tap3() == 0U); + + REQUIRE(parsed.sound_assignments()->at(0)->sound_number() == 255U); + REQUIRE(parsed.sound_assignments()->at(0)->sound_generator_mode() == mpc3000_pgm_v3_t::SOUND_GENERATOR_MODE_NORMAL); + REQUIRE(parsed.sound_assignments()->at(0)->if_over1() == 44U); + REQUIRE(parsed.sound_assignments()->at(0)->use_also_plays1() == 34U); + REQUIRE(parsed.sound_assignments()->at(0)->if_over2() == 88U); + REQUIRE(parsed.sound_assignments()->at(0)->use_also_plays2() == 34U); + REQUIRE(parsed.sound_assignments()->at(0)->poly() == mpc3000_pgm_v3_t::POLY_MODE_POLY); + REQUIRE(parsed.sound_assignments()->at(0)->cutoff_note_1() == 34U); + REQUIRE(parsed.sound_assignments()->at(0)->cutoff_note_2() == 34U); + REQUIRE(parsed.sound_assignments()->at(0)->tune() == 0); + REQUIRE(parsed.sound_assignments()->at(0)->attack() == 0U); + REQUIRE(parsed.sound_assignments()->at(0)->decay() == 6U); + REQUIRE(parsed.sound_assignments()->at(0)->decay_mode() == mpc3000_pgm_v3_t::DECAY_MODE_END); + REQUIRE(parsed.sound_assignments()->at(0)->filter_frequency() == 100U); + REQUIRE(parsed.sound_assignments()->at(0)->filter_resonance() == 0U); + REQUIRE(parsed.sound_assignments()->at(0)->filter_envel_attack() == 0U); + REQUIRE(parsed.sound_assignments()->at(0)->filter_envel_decay() == 0U); + REQUIRE(parsed.sound_assignments()->at(0)->filter_envel_amount() == 0U); + REQUIRE(parsed.sound_assignments()->at(0)->veloc_mod_of_volume() == 100U); + REQUIRE(parsed.sound_assignments()->at(0)->veloc_mod_of_attack() == 0U); + REQUIRE(parsed.sound_assignments()->at(0)->veloc_mod_of_soft_start() == 0U); + REQUIRE(parsed.sound_assignments()->at(0)->veloc_mod_of_filter_freq() == 0U); + REQUIRE(parsed.sound_assignments()->at(0)->param() == mpc3000_pgm_v3_t::NOTE_VARIATION_TYPE_TUNE); + + REQUIRE(parsed.mixer_screens()->at(0)->stereo_mix_volume() == 100U); + REQUIRE(parsed.mixer_screens()->at(0)->stereo_mix_pan() == 50U); + REQUIRE(parsed.mixer_screens()->at(0)->echo_volume() == 0U); + REQUIRE(parsed.mixer_screens()->at(0)->out_assign() == mpc3000_pgm_v3_t::INDIVIDUAL_OUT_INTERNAL_EFFECTS_GENERATOR); + REQUIRE(parsed.mixer_screens()->at(0)->follow_stereo() == false); + + REQUIRE(parsed.pad_note_number_assignments()->at(0)->note_number() == 37U); + REQUIRE(parsed.pad_note_number_assignments()->at(1)->note_number() == 36U); + REQUIRE(parsed.pad_note_number_assignments()->at(2)->note_number() == 42U); + REQUIRE(parsed.pad_note_number_assignments()->at(3)->note_number() == 82U); + REQUIRE(parsed.pad_note_number_assignments()->at(63)->note_number() == 98U); + +} + +TEST_CASE("Kaitai MPC3000 PGM decodes a probed SIMULT generator-mode variant", "[kaitai-pgm][real-mpc3000]") +{ + auto fs = cmrc::mpctest::get_filesystem(); + auto file = fs.open("test/RealMpc3000/Pgm/PROGRAM_01_SIMULT.pgm"); + const std::string bytes( + std::string_view(file.begin(), file.end() - file.begin()) + ); + + std::stringstream parseStream( + bytes, + std::ios::in | std::ios::out | std::ios::binary + ); + kaitai::kstream parseIo(&parseStream); + mpc3000_pgm_v3_t parsed(&parseIo); + parsed._read(); + + REQUIRE(parsed.program_name().substr(0, 10) == "PROGRAM_01"); + + size_t simultCount = 0; + for (const auto &assignment : *parsed.sound_assignments()) { + if (assignment->sound_generator_mode() == mpc3000_pgm_v3_t::SOUND_GENERATOR_MODE_SIMULTANEOUS) { + simultCount++; + } + } + + REQUIRE(simultCount == 1U); +} + +TEST_CASE("Kaitai MPC3000 PGM decodes a probed VEL SW generator-mode variant", "[kaitai-pgm][real-mpc3000]") +{ + auto fs = cmrc::mpctest::get_filesystem(); + auto file = fs.open("test/RealMpc3000/Pgm/PROGRAM_02_VELSW.pgm"); + const std::string bytes( + std::string_view(file.begin(), file.end() - file.begin()) + ); + + std::stringstream parseStream( + bytes, + std::ios::in | std::ios::out | std::ios::binary + ); + kaitai::kstream parseIo(&parseStream); + mpc3000_pgm_v3_t parsed(&parseIo); + parsed._read(); + + REQUIRE(parsed.program_name().substr(0, 10) == "PROGRAM_02"); + + size_t velocitySwitchCount = 0; + for (const auto &assignment : *parsed.sound_assignments()) { + if (assignment->sound_generator_mode() == mpc3000_pgm_v3_t::SOUND_GENERATOR_MODE_VELOCITY_SWITCH) { + velocitySwitchCount++; + } + } + + REQUIRE(velocitySwitchCount == 1U); +} + +TEST_CASE("Kaitai MPC3000 PGM decodes a probed VEL SW threshold and target variant", "[kaitai-pgm][real-mpc3000]") +{ + auto fs = cmrc::mpctest::get_filesystem(); + auto file = fs.open("test/RealMpc3000/Pgm/PROGRAM_03_VELSW_43.pgm"); + const std::string bytes( + std::string_view(file.begin(), file.end() - file.begin()) + ); + + std::stringstream parseStream( + bytes, + std::ios::in | std::ios::out | std::ios::binary + ); + kaitai::kstream parseIo(&parseStream); + mpc3000_pgm_v3_t parsed(&parseIo); + parsed._read(); + + REQUIRE(parsed.program_name().substr(0, 10) == "PROGRAM_03"); + + size_t velocitySwitchCount = 0; + const mpc3000_pgm_v3_t::sound_assignment_t* switchedAssignment = nullptr; + for (const auto &assignment : *parsed.sound_assignments()) { + if (assignment->sound_generator_mode() == mpc3000_pgm_v3_t::SOUND_GENERATOR_MODE_VELOCITY_SWITCH) { + velocitySwitchCount++; + switchedAssignment = assignment.get(); + } + } + + REQUIRE(velocitySwitchCount == 1U); + REQUIRE(switchedAssignment != nullptr); + REQUIRE(switchedAssignment->if_over1() == 43U); + REQUIRE(switchedAssignment->use_also_plays1() == 35U); + REQUIRE(switchedAssignment->if_over2() == 88U); + REQUIRE(switchedAssignment->use_also_plays2() == 34U); + REQUIRE(switchedAssignment->poly() == mpc3000_pgm_v3_t::POLY_MODE_POLY); + REQUIRE(switchedAssignment->cutoff_note_1() == 34U); + REQUIRE(switchedAssignment->cutoff_note_2() == 34U); + REQUIRE(switchedAssignment->param() == mpc3000_pgm_v3_t::NOTE_VARIATION_TYPE_TUNE); +} + +TEST_CASE("Kaitai MPC3000 PGM captures raw tune and decay-mode bytes from an Env/Veloc probe", "[kaitai-pgm][real-mpc3000]") +{ + auto fs = cmrc::mpctest::get_filesystem(); + auto file = fs.open("test/RealMpc3000/Pgm/PROGRAM_04_ENV_PROBE.pgm"); + const std::string bytes( + std::string_view(file.begin(), file.end() - file.begin()) + ); + + std::stringstream parseStream( + bytes, + std::ios::in | std::ios::out | std::ios::binary + ); + kaitai::kstream parseIo(&parseStream); + mpc3000_pgm_v3_t parsed(&parseIo); + parsed._read(); + + const auto* assignment = parsed.sound_assignments()->at(2).get(); + REQUIRE(assignment != nullptr); + REQUIRE(assignment->sound_generator_mode() == mpc3000_pgm_v3_t::SOUND_GENERATOR_MODE_VELOCITY_SWITCH); + REQUIRE(assignment->if_over1() == 43U); + REQUIRE(assignment->use_also_plays1() == 35U); + REQUIRE(assignment->if_over2() == 87U); + REQUIRE(assignment->use_also_plays2() == 35U); + REQUIRE(assignment->poly() == mpc3000_pgm_v3_t::POLY_MODE_NOTE_OFF); + REQUIRE(assignment->cutoff_note_1() == 64U); + REQUIRE(assignment->cutoff_note_2() == 65U); + // This fixture preserves the raw bytes proven live on MPC3000 v3.10: + // visible Tune:-1 wrote 0xffff and visible Dcy md:START wrote byte 1. + REQUIRE(assignment->tune() == -1); + REQUIRE(assignment->decay_mode() == mpc3000_pgm_v3_t::DECAY_MODE_START); +} + +TEST_CASE("Kaitai MPC3000 PGM loads through the production seam with imported semantics", "[kaitai-pgm][real-mpc3000]") +{ + auto fs = cmrc::mpctest::get_filesystem(); + auto file = fs.open("test/RealMpc3000/Pgm/PROGRAM_04_ENV_PROBE.pgm"); + std::vector bytes(file.begin(), file.end()); + + mpc::Mpc mpc; + mpc::TestMpc::initializeTestMpcWithoutIoServices(mpc); + std::vector soundNames; + const auto program = loadProgramViaPgmIoFromBytes(mpc, "PROGRAM_04.PGM", bytes, soundNames); + + REQUIRE(program != nullptr); + REQUIRE(program->getName() == "PROGRAM_04"); + REQUIRE(soundNames.empty()); + + REQUIRE(program->getPad(0)->getNote() == 37); + REQUIRE(program->getPad(1)->getNote() == 36); + REQUIRE(program->getPad(2)->getNote() == 42); + REQUIRE(program->getPad(3)->getNote() == 82); + + const auto note37 = program->getNoteParameters(37); + REQUIRE(note37->getSoundIndex() == -1); + REQUIRE(note37->getSoundGenerationMode() == mpc::sampler::SoundGenerationMode::VelocitySwitch); + REQUIRE(note37->getVelocityRangeLower() == 43); + REQUIRE(note37->getVelocityRangeUpper() == 87); + REQUIRE(note37->getOptionalNoteA() == 35); + REQUIRE(note37->getOptionalNoteB() == 35); + REQUIRE(note37->getMuteAssignA() == 64); + REQUIRE(note37->getMuteAssignB() == 65); + REQUIRE(note37->getTune() == -1); + REQUIRE(note37->getAttack() == 0); + REQUIRE(note37->getDecay() == 0); + REQUIRE(note37->getDecayMode() == 1); + REQUIRE(note37->getFilterFrequency() == 100); + REQUIRE(note37->getFilterResonance() == 0); + REQUIRE(note37->getFilterAttack() == 0); + REQUIRE(note37->getFilterDecay() == 0); + REQUIRE(note37->getFilterEnvelopeAmount() == 0); + REQUIRE(note37->getVeloToLevel() == 100); + REQUIRE(note37->getVelocityToAttack() == 0); + REQUIRE(note37->getVelocityToStart() == 0); + REQUIRE(note37->getVelocityToFilterFrequency() == 0); + REQUIRE(note37->getSliderParameterNumber() == 0); + REQUIRE(note37->getVelocityToPitch() == 0); + REQUIRE(note37->getVoiceOverlapMode() == mpc::sampler::VoiceOverlapMode::NOTE_OFF); + + const auto stereoMixer = note37->getStereoMixer(); + const auto indivFxMixer = note37->getIndivFxMixer(); + REQUIRE(stereoMixer->getLevel() == 100); + REQUIRE(stereoMixer->getPanning() == 50); + REQUIRE(indivFxMixer->getVolumeIndividualOut() == 0); + REQUIRE(indivFxMixer->getOutput() == 0); + REQUIRE(indivFxMixer->getFxPath() == 1); + REQUIRE(indivFxMixer->getFxSendLevel() == 0); + REQUIRE(indivFxMixer->isFollowingStereo() == false); + + const auto slider = program->getSlider(); + REQUIRE(slider->getNote() == 34); + REQUIRE(slider->getTuneLowRange() == -120); + REQUIRE(slider->getTuneHighRange() == 120); + REQUIRE(slider->getDecayLowRange() == 0); + REQUIRE(slider->getDecayHighRange() == 20); + REQUIRE(slider->getAttackLowRange() == 12); + REQUIRE(slider->getAttackHighRange() == 45); + REQUIRE(slider->getFilterLowRange() == -50); + REQUIRE(slider->getFilterHighRange() == 50); +} + +TEST_CASE("Kaitai MPC3000 PGM compacts the 128-entry sound table for import", "[kaitai-pgm][real-mpc3000]") +{ + const auto bytes = rewriteRealMpc3000ProgramWithMutations( + "test/RealMpc3000/Pgm/PROGRAM.pgm", + [](mpc3000_pgm_v3_t& parsed) + { + parsed.sound_names()->at(0)->set_name(mpc3000SoundNameField("KICK")); + parsed.sound_names()->at(5)->set_name(mpc3000SoundNameField("SNARE")); + + parsed.sound_assignments()->at(0)->set_sound_number(5); + parsed.sound_assignments()->at(1)->set_sound_number(0); + parsed.sound_assignments()->at(2)->set_sound_number(255); + parsed.sound_assignments()->at(3)->set_sound_number(255); + } + ); + + mpc::Mpc mpc; + mpc::TestMpc::initializeTestMpcWithoutIoServices(mpc); + std::vector soundNames; + const auto program = + loadProgramViaPgmIoFromBytes(mpc, "PROGRAM_05.PGM", bytes, soundNames); + + REQUIRE(program != nullptr); + REQUIRE(soundNames.size() == 2U); + REQUIRE(soundNames[0] == "SNARE"); + REQUIRE(soundNames[1] == "KICK"); +} + +TEST_CASE("Kaitai MPC2000 PGM saves and reloads PROGRAM1 without handwritten code", "[kaitai-pgm]") +{ + mpc::Mpc mpc; + mpc::TestMpc::initializeTestMpcWithoutMidiServices(mpc); + + auto program = loadProgram1(mpc); + REQUIRE(program != nullptr); + REQUIRE(program->getName() == "PROGRAM1"); + + const auto kaitaiBytes = mpc::file::kaitai::PgmIo::saveProgram(*program, mpc.getSampler()); + + std::stringstream parseStream( + std::string(kaitaiBytes.begin(), kaitaiBytes.end()), + std::ios::in | std::ios::out | std::ios::binary + ); + kaitai::kstream parseIo(&parseStream); + mpc2000xl_pgm_t parsed(&parseIo); + parsed._read(); + + REQUIRE(parsed.magic() == std::string("\x07\x04", 2)); + REQUIRE(parsed.sound_count() == 2); + REQUIRE(parsed.sound_names() != nullptr); + REQUIRE(parsed.sound_names()->size() == 2U); + REQUIRE(parsed.sound_names()->at(0).substr(0, 6) == "sound1"); + REQUIRE(parsed.sound_names()->at(1).substr(0, 6) == "sound2"); + REQUIRE(parsed.name().substr(0, 8) == "PROGRAM1"); + REQUIRE(parsed.note_parameters() != nullptr); + REQUIRE(parsed.note_parameters()->at(0)->sound_index() == 0); + REQUIRE(parsed.note_parameters()->at(1)->sound_index() == 1); + + std::stringstream writeStream(std::ios::in | std::ios::out | std::ios::binary); + kaitai::kstream writeIo(&writeStream); + parsed._set_io(&writeIo); + parsed._check(); + parsed._write(); + + REQUIRE(writeStream.str() == std::string(kaitaiBytes.begin(), kaitaiBytes.end())); + + auto disk = mpc.getDisk(); + auto kaitaiFile = disk->newFile("KPGM.PGM"); + std::vector kaitaiBytesVec(kaitaiBytes.begin(), kaitaiBytes.end()); + kaitaiFile->setFileData(kaitaiBytesVec); + disk->initFiles(); + + auto sampler = mpc.getSampler(); + sampler->deleteAllPrograms(false); + sampler->deleteAllSamples(); + + auto reparsedProgram = sampler->createNewProgramAddFirstAvailableSlotAndThen({}).lock(); + (void)mpc::disk::ProgramLoader::loadProgram(mpc, kaitaiFile, reparsedProgram, 0); + mpc.getEngineHost()->prepareProcessBlock(512); + + REQUIRE(reparsedProgram != nullptr); + REQUIRE(reparsedProgram->getName() == "KPGM"); + REQUIRE(sampler->getSoundCount() == 2); + REQUIRE(sampler->getSoundName(0) == "sound1"); + REQUIRE(sampler->getSoundName(1) == "sound2"); + REQUIRE(reparsedProgram->getNoteParameters(35)->getSoundIndex() == 0); + REQUIRE(reparsedProgram->getNoteParameters(36)->getSoundIndex() == 1); +} + +TEST_CASE("Kaitai MPC2000 PGM loads PROGRAM1 through the production seam with explicit semantics", "[kaitai-pgm]") +{ + mpc::Mpc mpc; + mpc::TestMpc::initializeTestMpcWithoutMidiServices(mpc); + + const auto program = loadProgram1(mpc); + requireProgram1LoadedState(mpc, program); +} + +TEST_CASE("Kaitai MPC2000 PGM load uses standalone byte as MIDI program change", "[kaitai-pgm]") +{ + auto fs = cmrc::mpctest::get_filesystem(); + auto file = fs.open("test/ProgramLoading/program1/PROGRAM1.PGM"); + const std::string originalBytes( + std::string_view(file.begin(), file.end() - file.begin()) + ); + + std::stringstream parseStream( + originalBytes, + std::ios::in | std::ios::out | std::ios::binary + ); + kaitai::kstream parseIo(&parseStream); + mpc2000xl_pgm_t parsed(&parseIo); + parsed._read(); + parsed.set_program_change(1); + + std::stringstream writeStream(std::ios::in | std::ios::out | std::ios::binary); + kaitai::kstream writeIo(&writeStream); + parsed._set_io(&writeIo); + parsed._check(); + parsed._write(); + + const auto rewrittenBytes = writeStream.str(); + std::vector programBytes(rewrittenBytes.begin(), rewrittenBytes.end()); + + mpc::Mpc mpc; + mpc::TestMpc::initializeTestMpcWithoutIoServices(mpc); + const auto program = loadProgram1FromBytesWithoutRender(mpc, programBytes); + + REQUIRE(program->getMidiProgramChange() == 2); + REQUIRE(program->getSlider()->getControlChange() == 0); +} + +TEST_CASE("Kaitai MPC2000 PGM save writes MIDI program change and ignores slider control change", "[kaitai-pgm]") +{ + mpc::Mpc mpc; + mpc::TestMpc::initializeTestMpcWithoutIoServices(mpc); + const auto program = loadProgram1(mpc); + + program->setMidiProgramChange(2); + mpc.getPerformanceManager().lock()->drainQueue(); + program->getSlider()->setControlChange(0); + const auto bytesWithZeroControl = + mpc::file::kaitai::PgmIo::saveProgram(*program, mpc.getSampler()); + + std::stringstream parseStreamZero( + std::string(bytesWithZeroControl.begin(), bytesWithZeroControl.end()), + std::ios::in | std::ios::out | std::ios::binary + ); + kaitai::kstream parseIoZero(&parseStreamZero); + mpc2000xl_pgm_t parsedZero(&parseIoZero); + parsedZero._read(); + REQUIRE(parsedZero.program_change() == 1); + + program->getSlider()->setControlChange(17); + const auto bytesWithNonZeroControl = + mpc::file::kaitai::PgmIo::saveProgram(*program, mpc.getSampler()); + + REQUIRE(bytesWithNonZeroControl == bytesWithZeroControl); +} + +TEST_CASE("Kaitai MPC2000 PGM load preserves mixer effects send level", "[kaitai-pgm]") +{ + auto fs = cmrc::mpctest::get_filesystem(); + auto file = fs.open("test/ProgramLoading/program1/PROGRAM1.PGM"); + const std::string originalBytes( + std::string_view(file.begin(), file.end() - file.begin()) + ); + + std::stringstream parseStream( + originalBytes, + std::ios::in | std::ios::out | std::ios::binary + ); + kaitai::kstream parseIo(&parseStream); + mpc2000xl_pgm_t parsed(&parseIo); + parsed._read(); + + parsed.pad_mixers()->at(1)->set_effects_send_level(50); + + std::stringstream writeStream(std::ios::in | std::ios::out | std::ios::binary); + kaitai::kstream writeIo(&writeStream); + parsed._set_io(&writeIo); + parsed._check(); + parsed._write(); + + const auto rewrittenBytes = writeStream.str(); + std::vector programBytes(rewrittenBytes.begin(), rewrittenBytes.end()); + + mpc::Mpc mpc; + mpc::TestMpc::initializeTestMpcWithoutIoServices(mpc); + const auto program = loadProgram1FromBytesWithoutRender(mpc, programBytes); + + REQUIRE(program != nullptr); + REQUIRE(program->getNoteParameters(36)->getIndivFxMixer()->getFxSendLevel() == 50); +} + +TEST_CASE("Kaitai MPC2000 PGM direct load handles upper-bound MIDI program change", "[kaitai-pgm]") +{ + auto fs = cmrc::mpctest::get_filesystem(); + auto file = fs.open("test/ProgramLoading/program1/PROGRAM1.PGM"); + const std::string originalBytes( + std::string_view(file.begin(), file.end() - file.begin()) + ); + + std::stringstream parseStream( + originalBytes, + std::ios::in | std::ios::out | std::ios::binary + ); + kaitai::kstream parseIo(&parseStream); + mpc2000xl_pgm_t parsed(&parseIo); + parsed._read(); + parsed.set_program_change(127); + + std::stringstream writeStream(std::ios::in | std::ios::out | std::ios::binary); + kaitai::kstream writeIo(&writeStream); + parsed._set_io(&writeIo); + parsed._check(); + parsed._write(); + + const auto rewrittenBytes = writeStream.str(); + std::vector programBytes(rewrittenBytes.begin(), rewrittenBytes.end()); + + mpc::Mpc mpc; + mpc::TestMpc::initializeTestMpcWithoutIoServices(mpc); + std::vector soundNames; + const auto program = loadProgram1ViaPgmIoFromBytes(mpc, programBytes, soundNames); + REQUIRE(program->getMidiProgramChange() == 128); + REQUIRE(soundNames.size() == 2U); +} + +TEST_CASE("Kaitai MPC2000 PGM direct parse reads upper-bound MIDI program change byte", "[kaitai-pgm]") +{ + auto fs = cmrc::mpctest::get_filesystem(); + auto file = fs.open("test/ProgramLoading/program1/PROGRAM1.PGM"); + const std::string originalBytes( + std::string_view(file.begin(), file.end() - file.begin()) + ); + + std::stringstream parseStream( + originalBytes, + std::ios::in | std::ios::out | std::ios::binary + ); + kaitai::kstream parseIo(&parseStream); + mpc2000xl_pgm_t parsed(&parseIo); + parsed._read(); + parsed.set_program_change(127); + + std::stringstream writeStream(std::ios::in | std::ios::out | std::ios::binary); + kaitai::kstream writeIo(&writeStream); + parsed._set_io(&writeIo); + parsed._check(); + parsed._write(); + + const auto rewrittenBytes = writeStream.str(); + std::stringstream reparsedStream( + rewrittenBytes, + std::ios::in | std::ios::out | std::ios::binary + ); + kaitai::kstream reparsedIo(&reparsedStream); + mpc2000xl_pgm_t reparsed(&reparsedIo); + reparsed._read(); + REQUIRE(reparsed.program_change() == 127); +} + +TEST_CASE("Kaitai MPC2000 PGM direct load preserves broad mutated program semantics", "[kaitai-pgm]") +{ + const auto programBytes = rewriteProgram1WithMutations(applyBroadProgramMutation); + + mpc::Mpc mpc; + mpc::TestMpc::initializeTestMpcWithoutIoServices(mpc); + std::vector soundNames; + const auto program = loadProgram1ViaPgmIoFromBytes(mpc, programBytes, soundNames); + requireBroadMutatedProgramState(program, soundNames); +} + +TEST_CASE("Kaitai MPC2000 PGM direct load preserves mutated note tune", "[kaitai-pgm]") +{ + auto fs = cmrc::mpctest::get_filesystem(); + auto file = fs.open("test/ProgramLoading/program1/PROGRAM1.PGM"); + const std::string originalBytes( + std::string_view(file.begin(), file.end() - file.begin()) + ); + + std::stringstream parseStream( + originalBytes, + std::ios::in | std::ios::out | std::ios::binary + ); + kaitai::kstream parseIo(&parseStream); + mpc2000xl_pgm_t parsed(&parseIo); + parsed._read(); + parsed.note_parameters()->at(0)->set_tune(-120); + + std::stringstream writeStream(std::ios::in | std::ios::out | std::ios::binary); + kaitai::kstream writeIo(&writeStream); + parsed._set_io(&writeIo); + parsed._check(); + parsed._write(); + + const auto rewrittenBytes = writeStream.str(); + std::vector programBytes(rewrittenBytes.begin(), rewrittenBytes.end()); + + mpc::Mpc mpc; + mpc::TestMpc::initializeTestMpcWithoutIoServices(mpc); + std::vector soundNames; + const auto program = loadProgram1ViaPgmIoFromBytes(mpc, programBytes, soundNames); + REQUIRE(program->getNoteParameters(35)->getTune() == -120); +} + +TEST_CASE("ProgramLoader loads upper-bound MIDI program change", "[kaitai-pgm]") +{ + auto fs = cmrc::mpctest::get_filesystem(); + auto file = fs.open("test/ProgramLoading/program1/PROGRAM1.PGM"); + const std::string originalBytes( + std::string_view(file.begin(), file.end() - file.begin()) + ); + + std::stringstream parseStream( + originalBytes, + std::ios::in | std::ios::out | std::ios::binary + ); + kaitai::kstream parseIo(&parseStream); + mpc2000xl_pgm_t parsed(&parseIo); + parsed._read(); + parsed.set_program_change(127); + + std::stringstream writeStream(std::ios::in | std::ios::out | std::ios::binary); + kaitai::kstream writeIo(&writeStream); + parsed._set_io(&writeIo); + parsed._check(); + parsed._write(); + + const auto rewrittenBytes = writeStream.str(); + std::vector programBytes(rewrittenBytes.begin(), rewrittenBytes.end()); + + mpc::Mpc mpc; + mpc::TestMpc::initializeTestMpcWithoutMidiServices(mpc); + const auto program = loadProgram1FromBytes(mpc, programBytes); + + REQUIRE(program != nullptr); + REQUIRE(program->getMidiProgramChange() == 128); +} + +TEST_CASE("ProgramLoader loads mutated note tune", "[kaitai-pgm]") +{ + auto fs = cmrc::mpctest::get_filesystem(); + auto file = fs.open("test/ProgramLoading/program1/PROGRAM1.PGM"); + const std::string originalBytes( + std::string_view(file.begin(), file.end() - file.begin()) + ); + + std::stringstream parseStream( + originalBytes, + std::ios::in | std::ios::out | std::ios::binary + ); + kaitai::kstream parseIo(&parseStream); + mpc2000xl_pgm_t parsed(&parseIo); + parsed._read(); + parsed.note_parameters()->at(0)->set_tune(-120); + + std::stringstream writeStream(std::ios::in | std::ios::out | std::ios::binary); + kaitai::kstream writeIo(&writeStream); + parsed._set_io(&writeIo); + parsed._check(); + parsed._write(); + + const auto rewrittenBytes = writeStream.str(); + std::vector programBytes(rewrittenBytes.begin(), rewrittenBytes.end()); + + mpc::Mpc mpc; + mpc::TestMpc::initializeTestMpcWithoutMidiServices(mpc); + const auto program = loadProgram1FromBytes(mpc, programBytes); + + REQUIRE(program != nullptr); + REQUIRE(program->getNoteParameters(35)->getTune() == -120); +} + +TEST_CASE("Kaitai MPC2000 PGM save and production load preserve broad mutated program semantics", "[kaitai-pgm]") +{ + mpc::Mpc mpc; + mpc::TestMpc::initializeTestMpcWithoutIoServices(mpc); + const auto mutatedBytes = rewriteProgram1WithMutations(applyBroadProgramMutation); + const auto mutatedProgram = loadProgram1FromBytesWithoutRender(mpc, mutatedBytes); + const std::vector expectedSoundNames{ + std::string("sound1") + std::string(10, ' '), + std::string("sound2") + std::string(10, ' ') + }; + requireBroadMutatedProgramState(mutatedProgram, expectedSoundNames); + + const auto savedBytes = mpc::file::kaitai::PgmIo::saveProgram(*mutatedProgram, mpc.getSampler()); + const auto reparsedProgram = loadProgram1FromBytesWithoutRender(mpc, savedBytes); + requireBroadMutatedProgramState(reparsedProgram, expectedSoundNames); +} + +TEST_CASE("ProgramLoader loads broad mutated program semantics", "[kaitai-pgm]") +{ + const auto programBytes = rewriteProgram1WithMutations(applyBroadProgramMutation); + + mpc::Mpc mpc; + mpc::TestMpc::initializeTestMpcWithoutMidiServices(mpc); + const auto program = loadProgram1FromBytesWithoutRender(mpc, programBytes); + const std::vector expectedSoundNames{ + std::string("sound1") + std::string(10, ' '), + std::string("sound2") + std::string(10, ' ') + }; + + requireBroadMutatedProgramState(program, expectedSoundNames); +} diff --git a/src/test/KaitaiSeqTest.cpp b/src/test/KaitaiSeqTest.cpp new file mode 100644 index 000000000..17f8b8bcc --- /dev/null +++ b/src/test/KaitaiSeqTest.cpp @@ -0,0 +1,1328 @@ +#include + +#include "TestMpc.hpp" + +#include "Mpc.hpp" +#include "StrUtil.hpp" +#include "disk/AbstractDisk.hpp" +#include "disk/MpcFile.hpp" +#include "engine/EngineHost.hpp" +#include "file/AkaiName.hpp" +#include "file/kaitai/Mpc3000SeqIo.hpp" +#include "command/ReleaseFunctionCommand.hpp" +#include "lcdgui/Label.hpp" +#include "lcdgui/LayeredScreen.hpp" +#include "lcdgui/screens/LoadScreen.hpp" +#include "lcdgui/screens/window/LoadASequenceScreen.hpp" +#include "sequencer/BusType.hpp" +#include "sequencer/ChannelPressureEvent.hpp" +#include "sequencer/ControlChangeEvent.hpp" +#include "sequencer/MixerEvent.hpp" +#include "sequencer/NoteOnEvent.hpp" +#include "sequencer/PolyPressureEvent.hpp" +#include "sequencer/PitchBendEvent.hpp" +#include "sequencer/ProgramChangeEvent.hpp" +#include "sequencer/Sequence.hpp" +#include "sequencer/Sequencer.hpp" +#include "sequencer/SequencerStateManager.hpp" +#include "sequencer/SystemExclusiveEvent.hpp" +#include "sequencer/TempoChangeEvent.hpp" +#include "sequencer/Track.hpp" +#include "sequencer/Transport.hpp" + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +CMRC_DECLARE(mpctest); + +namespace +{ +constexpr size_t kMpc3000SeqFixedPreludeSize = + 2 + 37 + 5 + (64 * 4) + 2 + 15 + 3 + 16 + 1 + 1 + 1; +constexpr size_t kMpc3000SeqTrackHeaderSize = 24; +constexpr size_t kMpc3000SeqTempoChangeSize = 6; +constexpr size_t kMpc3000SeqTempoChangeCountOffset = + kMpc3000SeqFixedPreludeSize - 2; +constexpr size_t kMpc3000SeqTrackHeaderCountOffset = + kMpc3000SeqFixedPreludeSize - 1; + +std::shared_ptr prepareSeqFile( + mpc::Mpc &mpc, + const std::vector &data, + const std::string &fileName) +{ + auto mutableData = data; + auto newFile = mpc.getDisk()->newFile(fileName); + newFile->setFileData(mutableData); + mpc.getDisk()->initFiles(); + return newFile; +} + +std::shared_ptr prepareSeqFile( + mpc::Mpc &mpc, + const std::string &resourcePath, + const std::string &fileName) +{ + auto fs = cmrc::mpctest::get_filesystem(); + auto file = fs.open(resourcePath); + std::vector data(file.begin(), file.end()); + return prepareSeqFile(mpc, data, fileName); +} + +void selectPreparedSeqFileForLoad( + mpc::Mpc &mpc, + const std::shared_ptr &loadScreen, + const std::string &fileName) +{ + const auto akaiFileName = mpc::file::AkaiName::generate(fileName); + const auto fileNames = mpc.getDisk()->getFileNames(); + std::ostringstream fileList; + for (const auto &candidate : fileNames) + { + fileList << "[" << candidate << "]"; + } + INFO("looking for " << fileName << " or " << akaiFileName + << " in " << fileList.str()); + int selectedIndex = -1; + for (int i = 0; i < static_cast(fileNames.size()); ++i) + { + const auto &candidate = fileNames[i]; + if (mpc::StrUtil::eqIgnoreCase(candidate, fileName) || + mpc::StrUtil::eqIgnoreCase(candidate, akaiFileName)) + { + selectedIndex = i; + break; + } + } + + if (selectedIndex < 0) + { + FAIL("SEQ fixture not found in test disk"); + } + + loadScreen->setFileLoad(selectedIndex); +} + +template +std::shared_ptr eventAs(const std::shared_ptr &event) +{ + auto cast = std::dynamic_pointer_cast(event); + REQUIRE(cast); + return cast; +} + +size_t tempoChangeOffset(const std::vector &data, const size_t index) +{ + REQUIRE(data.size() > kMpc3000SeqTrackHeaderCountOffset); + const auto numberOfTrackHeaders = + static_cast(data[kMpc3000SeqTrackHeaderCountOffset]); + return kMpc3000SeqFixedPreludeSize + + (numberOfTrackHeaders * kMpc3000SeqTrackHeaderSize) + + (index * kMpc3000SeqTempoChangeSize); +} + +void setTempoChangeFactorPercentage( + std::vector &data, + const size_t index, + const uint16_t factorWord) +{ + const auto offset = tempoChangeOffset(data, index); + REQUIRE(offset + 5 < data.size()); + data[offset + 4] = static_cast(factorWord & 0xff); + data[offset + 5] = static_cast((factorWord >> 8) & 0xff); +} + +void insertTempoChange( + std::vector &data, + const uint32_t tick, + const uint16_t factorWord) +{ + REQUIRE(data.size() > kMpc3000SeqTempoChangeCountOffset); + const auto numberOfTempoChanges = + static_cast(data[kMpc3000SeqTempoChangeCountOffset]); + const auto insertOffset = tempoChangeOffset(data, numberOfTempoChanges); + + const std::vector entry{ + static_cast(tick & 0xff), + static_cast((tick >> 8) & 0xff), + static_cast((tick >> 16) & 0xff), + static_cast((tick >> 24) & 0xff), + static_cast(factorWord & 0xff), + static_cast((factorWord >> 8) & 0xff), + }; + + data.insert( + data.begin() + static_cast(insertOffset), + entry.begin(), entry.end()); + data[kMpc3000SeqTempoChangeCountOffset] = + static_cast(numberOfTempoChanges + 1); +} + +void setLoopToBar(std::vector &data, const bool enabled, + const uint16_t loopBarNumber) +{ + REQUIRE(data.size() > 30); + auto flags = static_cast(data[28]); + flags = static_cast((flags & 0xfe) | (enabled ? 0x01 : 0x00)); + data[28] = static_cast(flags); + data[29] = static_cast(loopBarNumber & 0xff); + data[30] = static_cast((loopBarNumber >> 8) & 0xff); +} + +size_t trackHeaderOffset(const std::vector &data, const size_t index) +{ + const auto numberOfTrackHeaders = + static_cast(data[kMpc3000SeqTrackHeaderCountOffset]); + REQUIRE(index < numberOfTrackHeaders); + return kMpc3000SeqFixedPreludeSize + (index * kMpc3000SeqTrackHeaderSize); +} + +size_t eventStreamOffset(const std::vector &data) +{ + REQUIRE(data.size() > kMpc3000SeqTrackHeaderCountOffset); + const auto numberOfTempoChanges = + static_cast(data[kMpc3000SeqTempoChangeCountOffset]); + const auto numberOfTrackHeaders = + static_cast(data[kMpc3000SeqTrackHeaderCountOffset]); + return kMpc3000SeqFixedPreludeSize + + (numberOfTrackHeaders * kMpc3000SeqTrackHeaderSize) + + (numberOfTempoChanges * kMpc3000SeqTempoChangeSize); +} + +void setTrackHeaderFlags( + std::vector &data, + const size_t index, + const bool muted, + const bool inUse, + const bool drumTrack) +{ + const auto offset = trackHeaderOffset(data, index); + auto flags = static_cast(data[offset + 2]); + flags = static_cast(flags & 0xf8); + if (muted) + { + flags = static_cast(flags | 0x01); + } + if (inUse) + { + flags = static_cast(flags | 0x02); + } + if (drumTrack) + { + flags = static_cast(flags | 0x04); + } + data[offset + 2] = static_cast(flags); +} + +void setTrackPrimaryPortChannelAssignment( + std::vector &data, + const size_t index, + const uint8_t assignment) +{ + const auto offset = trackHeaderOffset(data, index); + data[offset + 3] = static_cast(assignment); +} + +void setTrackProgramChangeNumber( + std::vector &data, + const size_t index, + const uint8_t programChangeNumber) +{ + const auto offset = trackHeaderOffset(data, index); + data[offset + 22] = static_cast(programChangeNumber); +} + +void setTrackName( + std::vector &data, + const size_t index, + const std::string &name) +{ + const auto offset = trackHeaderOffset(data, index); + auto paddedName = mpc::StrUtil::padRight(name, " ", 16); + paddedName.resize(16); + std::copy( + paddedName.begin(), paddedName.end(), data.begin() + offset + 5); +} + +void configureTrackHeader( + std::vector &data, + const size_t index, + const int8_t absoluteRecordedTrackNumber, + const uint8_t userTrackNumber, + const bool muted, + const bool inUse, + const bool drumTrack, + const uint8_t primaryPortChannelAssignment, + const int8_t secondaryPortChannelAssignment, + const std::string &name, + const uint8_t trackVolume, + const uint8_t programChangeNumber) +{ + auto offset = trackHeaderOffset(data, index); + data[offset] = static_cast(absoluteRecordedTrackNumber); + data[offset + 1] = static_cast(userTrackNumber); + + unsigned char flags = 0; + if (muted) + { + flags |= 0x01; + } + if (inUse) + { + flags |= 0x02; + } + if (drumTrack) + { + flags |= 0x04; + } + data[offset + 2] = static_cast(flags); + data[offset + 3] = static_cast(primaryPortChannelAssignment); + data[offset + 4] = static_cast(secondaryPortChannelAssignment); + + auto paddedName = mpc::StrUtil::padRight(name, " ", 16); + paddedName.resize(16); + std::copy( + paddedName.begin(), paddedName.end(), data.begin() + offset + 5); + + data[offset + 21] = static_cast(trackVolume); + data[offset + 22] = static_cast(programChangeNumber); + data[offset + 23] = 0; +} + +void mutateBarEvent( + std::vector &data, + const uint16_t oneBasedBarNumber, + const uint8_t numerator, + const uint8_t denominator) +{ + const auto offset = eventStreamOffset(data); + const unsigned char barNumber1 = static_cast(oneBasedBarNumber & 0x7f); + const unsigned char barNumber2 = + static_cast((oneBasedBarNumber >> 7) & 0x7f); + + for (size_t i = offset; i + 4 < data.size(); ++i) + { + if (static_cast(data[i]) != 0xA8) + { + continue; + } + if (static_cast(data[i + 1]) != barNumber1 || + static_cast(data[i + 2]) != barNumber2) + { + continue; + } + + data[i + 3] = static_cast(numerator); + data[i + 4] = static_cast(denominator); + return; + } + + FAIL("Did not find target MPC3000 SEQ bar event to mutate"); +} + +void replaceProgramChangeWithPitchBend( + std::vector &data, + const uint8_t lsb, + const uint8_t msb) +{ + const std::vector programChangeBytes{ + static_cast(0xC0), + static_cast(0x01), + static_cast(0x00), + static_cast(0x88), + static_cast(0x00), + static_cast(0x03), + }; + const std::vector pitchBendBytes{ + static_cast(0xE0), + static_cast(0x01), + static_cast(lsb), + static_cast(msb), + static_cast(0x88), + static_cast(0x00), + static_cast(0x03), + }; + + const auto eventIt = std::search( + data.begin(), data.end(), programChangeBytes.begin(), programChangeBytes.end()); + REQUIRE(eventIt != data.end()); + const auto eventOffset = static_cast(std::distance(data.begin(), eventIt)); + data.erase(data.begin() + static_cast(eventOffset), + data.begin() + static_cast(eventOffset + programChangeBytes.size())); + data.insert(data.begin() + static_cast(eventOffset), + pitchBendBytes.begin(), pitchBendBytes.end()); +} + +void waitForTimedPopupToReturnToLoad(mpc::Mpc& mpc) +{ + constexpr auto timeout = std::chrono::seconds(5); + const auto start = std::chrono::steady_clock::now(); + + while (mpc.getLayeredScreen()->getCurrentScreenName() != "load") + { + REQUIRE(std::chrono::steady_clock::now() - start < timeout); + mpc.getEngineHost()->prepareProcessBlock(512); + mpc.getLayeredScreen()->timerCallback(); + std::this_thread::sleep_for(std::chrono::milliseconds(10)); + } +} +} // namespace + +TEST_CASE("Kaitai MPC3000 SEQ production load imports verified event fixture", + "[kaitai-seq]") +{ + mpc::Mpc mpc; + mpc::TestMpc::initializeTestMpcWithoutMidiServices(mpc); + const std::string fileName = "M3KEVT.SEQ"; + prepareSeqFile(mpc, "test/RealMpc3000/Seq/M3KEVT.SEQ", fileName); + + auto layeredScreen = mpc.getLayeredScreen(); + layeredScreen->openScreen("load"); + + const auto loadScreen = mpc.screens->get(); + selectPreparedSeqFileForLoad(mpc, loadScreen, fileName); + loadScreen->function(5); + + REQUIRE(layeredScreen->getCurrentScreenName() == "load-a-sequence"); + REQUIRE(layeredScreen->getCurrentScreen()->findLabel("file")->getText() == + "File:M3KEVT .SEQ"); + REQUIRE(layeredScreen->getCurrentScreen()->findField("load-into")->getText() == + "01"); + + mpc.getSequencer()->getStateManager()->drainQueue(); + + auto sequence = mpc.getSequencer()->getSequence(mpc::TempSequenceIndex); + REQUIRE(sequence->getName() == "SEQ01"); + REQUIRE(sequence->getLastBarIndex() == 0); + REQUIRE(std::abs(sequence->getInitialTempo() - 99.0) < 0.0001); + REQUIRE(sequence->getNumerator(0) == 4); + REQUIRE(sequence->getDenominator(0) == 4); + + auto track0 = sequence->getTrack(0); + REQUIRE(track0->getName() == "TRK01"); + REQUIRE(track0->getBusType() == mpc::sequencer::BusType::DRUM1); + + const auto events = track0->getEvents(); + REQUIRE(events.size() == 7); + + const auto channelPressure = + eventAs(events[0]); + REQUIRE(channelPressure->getTick() == 0); + REQUIRE(channelPressure->getAmount() == 64); + + const auto polyPressure = + eventAs(events[1]); + REQUIRE(polyPressure->getTick() == 24); + REQUIRE(polyPressure->getNote() == 35); + REQUIRE(polyPressure->getAmount() == 64); + + const auto systemExclusive = + eventAs(events[2]); + REQUIRE(systemExclusive->getTick() == 48); + REQUIRE(systemExclusive->getByteA() == 0x00); + REQUIRE(systemExclusive->getByteB() == 0x00); + + const auto mixerLevel = eventAs(events[3]); + REQUIRE(mixerLevel->getTick() == 72); + REQUIRE(mixerLevel->getParameter() == 0); + REQUIRE(mixerLevel->getPad() == 0); + REQUIRE(mixerLevel->getValue() == 100); + + const auto mixerPan = eventAs(events[4]); + REQUIRE(mixerPan->getTick() == 96); + REQUIRE(mixerPan->getParameter() == 1); + REQUIRE(mixerPan->getPad() == 0); + REQUIRE(mixerPan->getValue() == 50); + + const auto mixerFxSend = eventAs(events[5]); + REQUIRE(mixerFxSend->getTick() == 120); + REQUIRE(mixerFxSend->getParameter() == 2); + REQUIRE(mixerFxSend->getPad() == 0); + REQUIRE(mixerFxSend->getValue() == 100); + + const auto controlChange = + eventAs(events[6]); + REQUIRE(controlChange->getTick() == 144); + REQUIRE(controlChange->getController() == 0); + REQUIRE(controlChange->getAmount() == 0); +} + +TEST_CASE("Kaitai MPC3000 SEQ production load handles real minimal sentinel fixture", + "[kaitai-seq]") +{ + mpc::Mpc mpc; + mpc::TestMpc::initializeTestMpcWithoutMidiServices(mpc); + const std::string fileName = "M3KMIN.SEQ"; + prepareSeqFile(mpc, "test/RealMpc3000/Seq/M3KMIN.SEQ", fileName); + + auto layeredScreen = mpc.getLayeredScreen(); + layeredScreen->openScreen("load"); + + const auto loadScreen = mpc.screens->get(); + selectPreparedSeqFileForLoad(mpc, loadScreen, fileName); + loadScreen->function(5); + + REQUIRE(layeredScreen->getCurrentScreenName() == "load-a-sequence"); + REQUIRE(layeredScreen->getCurrentScreen()->findLabel("file")->getText() == + "File:M3KMIN .SEQ"); + + mpc.getSequencer()->getStateManager()->drainQueue(); + + auto sequence = mpc.getSequencer()->getSequence(mpc::TempSequenceIndex); + REQUIRE(sequence->getName() == "SEQ01"); + REQUIRE(sequence->getLastBarIndex() == 0); + REQUIRE(std::abs(sequence->getInitialTempo() - 99.0) < 0.0001); + REQUIRE(sequence->getNumerator(0) == 1); + REQUIRE(sequence->getDenominator(0) == 4); + + auto track0 = sequence->getTrack(0); + REQUIRE(track0->getName() == "TRK01"); + REQUIRE(track0->getBusType() == mpc::sequencer::BusType::DRUM1); + REQUIRE(track0->getEvents().empty()); +} + +TEST_CASE("Kaitai MPC3000 SEQ production load imports real note event fixture", + "[kaitai-seq]") +{ + mpc::Mpc mpc; + mpc::TestMpc::initializeTestMpcWithoutMidiServices(mpc); + const std::string fileName = "M3KNOTE.SEQ"; + prepareSeqFile(mpc, "test/RealMpc3000/Seq/M3KNOTE.SEQ", fileName); + + auto layeredScreen = mpc.getLayeredScreen(); + layeredScreen->openScreen("load"); + + const auto loadScreen = mpc.screens->get(); + selectPreparedSeqFileForLoad(mpc, loadScreen, fileName); + loadScreen->function(5); + + REQUIRE(layeredScreen->getCurrentScreenName() == "load-a-sequence"); + + mpc.getSequencer()->getStateManager()->drainQueue(); + + auto sequence = mpc.getSequencer()->getSequence(mpc::TempSequenceIndex); + REQUIRE(sequence->getName() == "SEQ01"); + REQUIRE(sequence->getLastBarIndex() == 1); + REQUIRE(sequence->getNumerator(0) == 4); + REQUIRE(sequence->getDenominator(0) == 4); + REQUIRE(sequence->getNumerator(1) == 4); + REQUIRE(sequence->getDenominator(1) == 4); + + auto track0 = sequence->getTrack(0); + REQUIRE(track0->getEvents().size() == 1); + + const auto note = eventAs(track0->getEvents()[0]); + REQUIRE(note->getTick() == 24); + REQUIRE(note->getNote() == 35); + REQUIRE(note->getVelocity() == 64); + REQUIRE(note->getDuration() == 96); + REQUIRE(note->getVariationType() == mpc::NoteVariationTypeTune); + REQUIRE(note->getVariationValue() == 64); +} + +TEST_CASE("Kaitai MPC3000 SEQ production load imports real duration-mutated note fixture", + "[kaitai-seq]") +{ + mpc::Mpc mpc; + mpc::TestMpc::initializeTestMpcWithoutMidiServices(mpc); + const std::string fileName = "M3KD120.SEQ"; + prepareSeqFile(mpc, "test/RealMpc3000/Seq/M3KD120.SEQ", fileName); + + auto layeredScreen = mpc.getLayeredScreen(); + layeredScreen->openScreen("load"); + + const auto loadScreen = mpc.screens->get(); + selectPreparedSeqFileForLoad(mpc, loadScreen, fileName); + loadScreen->function(5); + + REQUIRE(layeredScreen->getCurrentScreenName() == "load-a-sequence"); + + mpc.getSequencer()->getStateManager()->drainQueue(); + + auto sequence = mpc.getSequencer()->getSequence(mpc::TempSequenceIndex); + REQUIRE(sequence->getName() == "SEQ01"); + REQUIRE(sequence->getLastBarIndex() == 1); + + auto track0 = sequence->getTrack(0); + REQUIRE(track0->getEvents().size() == 1); + + const auto note = eventAs(track0->getEvents()[0]); + REQUIRE(note->getTick() == 0); + REQUIRE(note->getNote() == 35); + REQUIRE(note->getVelocity() == 64); + REQUIRE(note->getDuration() == 120); + REQUIRE(note->getVariationType() == mpc::NoteVariationTypeTune); + REQUIRE(note->getVariationValue() == 64); +} + +TEST_CASE("Kaitai MPC3000 SEQ production load imports loop header state", + "[kaitai-seq]") +{ + auto fs = cmrc::mpctest::get_filesystem(); + auto file = fs.open("test/RealMpc3000/Seq/M3KNOTE.SEQ"); + std::vector data(file.begin(), file.end()); + setLoopToBar(data, true, 2); + + mpc::Mpc mpc; + mpc::TestMpc::initializeTestMpcWithoutMidiServices(mpc); + const std::string fileName = "M3KLOOP.SEQ"; + prepareSeqFile(mpc, data, fileName); + + auto layeredScreen = mpc.getLayeredScreen(); + layeredScreen->openScreen("load"); + + const auto loadScreen = mpc.screens->get(); + selectPreparedSeqFileForLoad(mpc, loadScreen, fileName); + loadScreen->function(5); + + REQUIRE(layeredScreen->getCurrentScreenName() == "load-a-sequence"); + + mpc.getSequencer()->getStateManager()->drainQueue(); + + auto sequence = mpc.getSequencer()->getSequence(mpc::TempSequenceIndex); + REQUIRE(sequence->isLoopEnabled()); + REQUIRE(sequence->getFirstLoopBarIndex() == 0); + REQUIRE(sequence->getLastLoopBarIndex() == 1); +} + +TEST_CASE("Kaitai MPC3000 SEQ production load applies bar time-signature events", + "[kaitai-seq]") +{ + auto fs = cmrc::mpctest::get_filesystem(); + auto file = fs.open("test/RealMpc3000/Seq/M3KNOTE.SEQ"); + std::vector data(file.begin(), file.end()); + mutateBarEvent(data, 2, 3, 8); + + mpc::Mpc mpc; + mpc::TestMpc::initializeTestMpcWithoutMidiServices(mpc); + const std::string fileName = "M3KBAR2.SEQ"; + prepareSeqFile(mpc, data, fileName); + + auto layeredScreen = mpc.getLayeredScreen(); + layeredScreen->openScreen("load"); + + const auto loadScreen = mpc.screens->get(); + selectPreparedSeqFileForLoad(mpc, loadScreen, fileName); + loadScreen->function(5); + + REQUIRE(layeredScreen->getCurrentScreenName() == "load-a-sequence"); + + mpc.getSequencer()->getStateManager()->drainQueue(); + + auto sequence = mpc.getSequencer()->getSequence(mpc::TempSequenceIndex); + REQUIRE(sequence->getLastBarIndex() == 1); + REQUIRE(sequence->getNumerator(0) == 4); + REQUIRE(sequence->getDenominator(0) == 4); + REQUIRE(sequence->getNumerator(1) == 3); + REQUIRE(sequence->getDenominator(1) == 8); + REQUIRE(sequence->getTimeSignatureFromBarIndex(0).numerator == 4); + REQUIRE(sequence->getTimeSignatureFromBarIndex(0).denominator == 4); + REQUIRE(sequence->getTimeSignatureFromBarIndex(1).numerator == 3); + REQUIRE(sequence->getTimeSignatureFromBarIndex(1).denominator == 8); + + REQUIRE(sequence->getBarLength(0) == 384); + REQUIRE(sequence->getBarLength(1) == 144); + REQUIRE(sequence->getBarLengths()[0] == 384); + REQUIRE(sequence->getBarLengths()[1] == 144); + + REQUIRE(sequence->getFirstTickOfBar(0) == 0); + REQUIRE(sequence->getLastTickOfBar(0) == 383); + REQUIRE(sequence->getFirstTickOfBar(1) == 384); + REQUIRE(sequence->getLastTickOfBar(1) == 527); + REQUIRE(sequence->getLastTick() == 528); + + REQUIRE(sequence->getBarIndexForPositionTicks(0) == 0); + REQUIRE(sequence->getBarIndexForPositionTicks(383) == 0); + REQUIRE(sequence->getBarIndexForPositionTicks(384) == 1); + REQUIRE(sequence->getBarIndexForPositionTicks(527) == 1); + + REQUIRE(sequence->getTimeSignatureFromTickPos(0).numerator == 4); + REQUIRE(sequence->getTimeSignatureFromTickPos(0).denominator == 4); + REQUIRE(sequence->getTimeSignatureFromTickPos(383).numerator == 4); + REQUIRE(sequence->getTimeSignatureFromTickPos(383).denominator == 4); + REQUIRE(sequence->getTimeSignatureFromTickPos(384).numerator == 3); + REQUIRE(sequence->getTimeSignatureFromTickPos(384).denominator == 8); + REQUIRE(sequence->getTimeSignatureFromTickPos(527).numerator == 3); + REQUIRE(sequence->getTimeSignatureFromTickPos(527).denominator == 8); + + auto track0 = sequence->getTrack(0); + REQUIRE(track0->getEvents().size() == 1); + const auto note = eventAs(track0->getEvents()[0]); + REQUIRE(note->getTick() == 24); +} + +TEST_CASE("Kaitai MPC3000 SEQ production load maps MIDI track header fields", + "[kaitai-seq]") +{ + auto fs = cmrc::mpctest::get_filesystem(); + auto file = fs.open("test/RealMpc3000/Seq/M3KMIN.SEQ"); + std::vector data(file.begin(), file.end()); + setTrackHeaderFlags(data, 0, true, true, false); + setTrackPrimaryPortChannelAssignment(data, 0, 17); + setTrackProgramChangeNumber(data, 0, 42); + setTrackName(data, 0, "MIDITRK01"); + + mpc::Mpc mpc; + mpc::TestMpc::initializeTestMpcWithoutMidiServices(mpc); + const std::string fileName = "M3KTRKH.SEQ"; + prepareSeqFile(mpc, data, fileName); + + auto layeredScreen = mpc.getLayeredScreen(); + layeredScreen->openScreen("load"); + + const auto loadScreen = mpc.screens->get(); + selectPreparedSeqFileForLoad(mpc, loadScreen, fileName); + loadScreen->function(5); + + REQUIRE(layeredScreen->getCurrentScreenName() == "load-a-sequence"); + + mpc.getSequencer()->getStateManager()->drainQueue(); + + auto sequence = mpc.getSequencer()->getSequence(mpc::TempSequenceIndex); + auto track0 = sequence->getTrack(0); + REQUIRE(track0->getName() == "MIDITRK01"); + REQUIRE(track0->getBusType() == mpc::sequencer::BusType::MIDI); + REQUIRE(track0->getDeviceIndex() == 17); + REQUIRE(track0->getProgramChange() == 42); + REQUIRE_FALSE(track0->isOn()); +} + +TEST_CASE("Kaitai MPC3000 SEQ production load maps sparse additional track headers by user track number", + "[kaitai-seq]") +{ + auto fs = cmrc::mpctest::get_filesystem(); + auto file = fs.open("test/RealMpc3000/Seq/M3KMIN.SEQ"); + std::vector data(file.begin(), file.end()); + + configureTrackHeader( + data, 1, 1, 8, false, true, false, 5, -1, "MIDITRK08", 100, 17); + + mpc::Mpc mpc; + mpc::TestMpc::initializeTestMpcWithoutMidiServices(mpc); + const std::string fileName = "M3KTRK8.SEQ"; + prepareSeqFile(mpc, data, fileName); + + auto layeredScreen = mpc.getLayeredScreen(); + layeredScreen->openScreen("load"); + + const auto loadScreen = mpc.screens->get(); + selectPreparedSeqFileForLoad(mpc, loadScreen, fileName); + loadScreen->function(5); + + REQUIRE(layeredScreen->getCurrentScreenName() == "load-a-sequence"); + + mpc.getSequencer()->getStateManager()->drainQueue(); + + auto sequence = mpc.getSequencer()->getSequence(mpc::TempSequenceIndex); + + auto track0 = sequence->getTrack(0); + REQUIRE(track0->getName() == "TRK01"); + REQUIRE(track0->getBusType() == mpc::sequencer::BusType::DRUM1); + + auto track7 = sequence->getTrack(7); + REQUIRE(track7->getName() == "MIDITRK08"); + REQUIRE(track7->getBusType() == mpc::sequencer::BusType::MIDI); + REQUIRE(track7->getDeviceIndex() == 5); + REQUIRE(track7->getProgramChange() == 17); + REQUIRE(track7->isOn()); + REQUIRE(track7->getEvents().empty()); +} + +TEST_CASE("Kaitai MPC3000 SEQ production load imports real MIDI track fixture", + "[kaitai-seq]") +{ + mpc::Mpc mpc; + mpc::TestMpc::initializeTestMpcWithoutMidiServices(mpc); + const std::string fileName = "M3KMID1.SEQ"; + prepareSeqFile(mpc, "test/RealMpc3000/Seq/M3KMID1.SEQ", fileName); + + auto layeredScreen = mpc.getLayeredScreen(); + layeredScreen->openScreen("load"); + + const auto loadScreen = mpc.screens->get(); + selectPreparedSeqFileForLoad(mpc, loadScreen, fileName); + loadScreen->function(5); + + REQUIRE(layeredScreen->getCurrentScreenName() == "load-a-sequence"); + + mpc.getSequencer()->getStateManager()->drainQueue(); + + auto sequence = mpc.getSequencer()->getSequence(mpc::TempSequenceIndex); + auto track0 = sequence->getTrack(0); + REQUIRE(sequence->getName() == "SEQ01"); + REQUIRE(track0->getName() == "TRK01"); + REQUIRE(track0->getBusType() == mpc::sequencer::BusType::MIDI); + REQUIRE(track0->isOn()); +} + +TEST_CASE("Kaitai MPC3000 SEQ production load imports real track-off fixture", + "[kaitai-seq]") +{ + mpc::Mpc mpc; + mpc::TestMpc::initializeTestMpcWithoutMidiServices(mpc); + const std::string fileName = "M3KTRKO.SEQ"; + prepareSeqFile(mpc, "test/RealMpc3000/Seq/M3KTRKO.SEQ", fileName); + + auto layeredScreen = mpc.getLayeredScreen(); + layeredScreen->openScreen("load"); + + const auto loadScreen = mpc.screens->get(); + selectPreparedSeqFileForLoad(mpc, loadScreen, fileName); + loadScreen->function(5); + + REQUIRE(layeredScreen->getCurrentScreenName() == "load-a-sequence"); + + mpc.getSequencer()->getStateManager()->drainQueue(); + + auto sequence = mpc.getSequencer()->getSequence(mpc::TempSequenceIndex); + auto track0 = sequence->getTrack(0); + REQUIRE(sequence->getName() == "SEQ01"); + REQUIRE(track0->getName() == "TRK01"); + REQUIRE(track0->getBusType() == mpc::sequencer::BusType::DRUM1); + REQUIRE_FALSE(track0->isOn()); +} + +TEST_CASE("Kaitai MPC3000 SEQ production load imports real loop-enabled fixture", + "[kaitai-seq]") +{ + mpc::Mpc mpc; + mpc::TestMpc::initializeTestMpcWithoutMidiServices(mpc); + const std::string fileName = "M3KLPON.SEQ"; + prepareSeqFile(mpc, "test/RealMpc3000/Seq/M3KLPON.SEQ", fileName); + + auto layeredScreen = mpc.getLayeredScreen(); + layeredScreen->openScreen("load"); + + const auto loadScreen = mpc.screens->get(); + selectPreparedSeqFileForLoad(mpc, loadScreen, fileName); + loadScreen->function(5); + + REQUIRE(layeredScreen->getCurrentScreenName() == "load-a-sequence"); + + mpc.getSequencer()->getStateManager()->drainQueue(); + + auto sequence = mpc.getSequencer()->getSequence(mpc::TempSequenceIndex); + REQUIRE(sequence->getName() == "SEQ01"); + REQUIRE(sequence->getLastBarIndex() == 1); + REQUIRE(sequence->isLoopEnabled()); + REQUIRE(sequence->getFirstLoopBarIndex() == 0); + REQUIRE(sequence->getLastLoopBarIndex() == 0); +} + +TEST_CASE("Kaitai MPC3000 SEQ production load derives initial tempo from tick-zero tempo change", + "[kaitai-seq]") +{ + auto fs = cmrc::mpctest::get_filesystem(); + auto file = fs.open("test/RealMpc3000/Seq/M3KNOTE.SEQ"); + std::vector data(file.begin(), file.end()); + + // 150.0% in MPC3000 SEQ fixed-point format. + setTempoChangeFactorPercentage(data, 0, 0x1800); + + mpc::Mpc mpc; + mpc::TestMpc::initializeTestMpcWithoutMidiServices(mpc); + const std::string fileName = "M3KTMPO.SEQ"; + prepareSeqFile(mpc, data, fileName); + + auto layeredScreen = mpc.getLayeredScreen(); + layeredScreen->openScreen("load"); + + const auto loadScreen = mpc.screens->get(); + selectPreparedSeqFileForLoad(mpc, loadScreen, fileName); + loadScreen->function(5); + + REQUIRE(layeredScreen->getCurrentScreenName() == "load-a-sequence"); + + mpc.getSequencer()->getStateManager()->drainQueue(); + + auto sequence = mpc.getSequencer()->getSequence(mpc::TempSequenceIndex); + REQUIRE(std::abs(sequence->getInitialTempo() - 180.0) < 0.0001); + + const auto tempoChanges = sequence->getTempoChangeEvents(); + REQUIRE(tempoChanges.size() == 1); + REQUIRE(tempoChanges[0]->getTick() == 0); + REQUIRE(tempoChanges[0]->getRatio() == 1000); + REQUIRE(std::abs(tempoChanges[0]->getTempo() - 180.0) < 0.0001); +} + +TEST_CASE("Kaitai MPC3000 SEQ production load imports additional tempo changes as ratio events", + "[kaitai-seq]") +{ + auto fs = cmrc::mpctest::get_filesystem(); + auto file = fs.open("test/RealMpc3000/Seq/M3KNOTE.SEQ"); + std::vector data(file.begin(), file.end()); + + // Add a second tempo-change entry at tick 96 with 150.0%. + insertTempoChange(data, 96, 0x1800); + + mpc::Mpc mpc; + mpc::TestMpc::initializeTestMpcWithoutMidiServices(mpc); + const std::string fileName = "M3KTMCH.SEQ"; + prepareSeqFile(mpc, data, fileName); + + auto layeredScreen = mpc.getLayeredScreen(); + layeredScreen->openScreen("load"); + + const auto loadScreen = mpc.screens->get(); + selectPreparedSeqFileForLoad(mpc, loadScreen, fileName); + loadScreen->function(5); + + REQUIRE(layeredScreen->getCurrentScreenName() == "load-a-sequence"); + + mpc.getSequencer()->getStateManager()->drainQueue(); + + auto sequence = mpc.getSequencer()->getSequence(mpc::TempSequenceIndex); + REQUIRE(std::abs(sequence->getInitialTempo() - 120.0) < 0.0001); + + const auto tempoChanges = sequence->getTempoChangeEvents(); + REQUIRE(tempoChanges.size() == 2); + REQUIRE(tempoChanges[0]->getTick() == 0); + REQUIRE(tempoChanges[0]->getRatio() == 1000); + REQUIRE(tempoChanges[1]->getTick() == 96); + REQUIRE(tempoChanges[1]->getRatio() == 1500); + REQUIRE(std::abs(tempoChanges[1]->getTempo() - 180.0) < 0.0001); +} + +TEST_CASE("Kaitai MPC3000 SEQ production load imports real non-default header tempo fixture", + "[kaitai-seq]") +{ + mpc::Mpc mpc; + mpc::TestMpc::initializeTestMpcWithoutMidiServices(mpc); + const std::string fileName = "M3KT119.SEQ"; + prepareSeqFile(mpc, "test/RealMpc3000/Seq/M3KT119.SEQ", fileName); + + auto layeredScreen = mpc.getLayeredScreen(); + layeredScreen->openScreen("load"); + + const auto loadScreen = mpc.screens->get(); + selectPreparedSeqFileForLoad(mpc, loadScreen, fileName); + loadScreen->function(5); + + REQUIRE(layeredScreen->getCurrentScreenName() == "load-a-sequence"); + REQUIRE(layeredScreen->getCurrentScreen()->findLabel("file")->getText() == + "File:M3KT119 .SEQ"); + + mpc.getSequencer()->getStateManager()->drainQueue(); + + auto sequence = mpc.getSequencer()->getSequence(mpc::TempSequenceIndex); + REQUIRE(sequence->getName() == "SEQ01"); + REQUIRE(sequence->getLastBarIndex() == 1); + REQUIRE(std::abs(sequence->getInitialTempo() - 119.0) < 0.0001); + + const auto tempoChanges = sequence->getTempoChangeEvents(); + REQUIRE(tempoChanges.size() == 1); + REQUIRE(tempoChanges[0]->getTick() == 0); + REQUIRE(tempoChanges[0]->getRatio() == 1000); + REQUIRE(std::abs(tempoChanges[0]->getTempo() - 119.0) < 0.0001); +} + +TEST_CASE("Kaitai MPC3000 SEQ production load imports second real non-default header tempo fixture", + "[kaitai-seq]") +{ + mpc::Mpc mpc; + mpc::TestMpc::initializeTestMpcWithoutMidiServices(mpc); + const std::string fileName = "M3KT150.SEQ"; + prepareSeqFile(mpc, "test/RealMpc3000/Seq/M3KT150.SEQ", fileName); + + auto layeredScreen = mpc.getLayeredScreen(); + layeredScreen->openScreen("load"); + + const auto loadScreen = mpc.screens->get(); + selectPreparedSeqFileForLoad(mpc, loadScreen, fileName); + loadScreen->function(5); + + REQUIRE(layeredScreen->getCurrentScreenName() == "load-a-sequence"); + REQUIRE(layeredScreen->getCurrentScreen()->findLabel("file")->getText() == + "File:M3KT150 .SEQ"); + + mpc.getSequencer()->getStateManager()->drainQueue(); + + auto sequence = mpc.getSequencer()->getSequence(mpc::TempSequenceIndex); + REQUIRE(sequence->getName() == "SEQ01"); + REQUIRE(sequence->getLastBarIndex() == 1); + REQUIRE(std::abs(sequence->getInitialTempo() - 150.0) < 0.0001); + + const auto tempoChanges = sequence->getTempoChangeEvents(); + REQUIRE(tempoChanges.size() == 1); + REQUIRE(tempoChanges[0]->getTick() == 0); + REQUIRE(tempoChanges[0]->getRatio() == 1000); + REQUIRE(std::abs(tempoChanges[0]->getTempo() - 150.0) < 0.0001); +} + +TEST_CASE("Kaitai MPC3000 SEQ production load imports third real non-default header tempo fixture", + "[kaitai-seq]") +{ + mpc::Mpc mpc; + mpc::TestMpc::initializeTestMpcWithoutMidiServices(mpc); + const std::string fileName = "M3KT090.SEQ"; + prepareSeqFile(mpc, "test/RealMpc3000/Seq/M3KT090.SEQ", fileName); + + auto layeredScreen = mpc.getLayeredScreen(); + layeredScreen->openScreen("load"); + + const auto loadScreen = mpc.screens->get(); + selectPreparedSeqFileForLoad(mpc, loadScreen, fileName); + loadScreen->function(5); + + REQUIRE(layeredScreen->getCurrentScreenName() == "load-a-sequence"); + REQUIRE(layeredScreen->getCurrentScreen()->findLabel("file")->getText() == + "File:M3KT090 .SEQ"); + + mpc.getSequencer()->getStateManager()->drainQueue(); + + auto sequence = mpc.getSequencer()->getSequence(mpc::TempSequenceIndex); + REQUIRE(sequence->getName() == "SEQ01"); + REQUIRE(sequence->getLastBarIndex() == 1); + REQUIRE(std::abs(sequence->getInitialTempo() - 90.0) < 0.0001); + + const auto tempoChanges = sequence->getTempoChangeEvents(); + REQUIRE(tempoChanges.size() == 1); + REQUIRE(tempoChanges[0]->getTick() == 0); + REQUIRE(tempoChanges[0]->getRatio() == 1000); + REQUIRE(std::abs(tempoChanges[0]->getTempo() - 90.0) < 0.0001); +} + +TEST_CASE("Kaitai MPC3000 SEQ production load imports real program change fixture", + "[kaitai-seq]") +{ + mpc::Mpc mpc; + mpc::TestMpc::initializeTestMpcWithoutMidiServices(mpc); + const std::string fileName = "M3KPC.SEQ"; + prepareSeqFile(mpc, "test/RealMpc3000/Seq/M3KPC.SEQ", fileName); + + auto layeredScreen = mpc.getLayeredScreen(); + layeredScreen->openScreen("load"); + + const auto loadScreen = mpc.screens->get(); + selectPreparedSeqFileForLoad(mpc, loadScreen, fileName); + loadScreen->function(5); + + REQUIRE(layeredScreen->getCurrentScreenName() == "load-a-sequence"); + + mpc.getSequencer()->getStateManager()->drainQueue(); + + auto sequence = mpc.getSequencer()->getSequence(mpc::TempSequenceIndex); + REQUIRE(sequence->getName() == "SEQ02"); + REQUIRE(sequence->getLastBarIndex() == 1); + + auto track0 = sequence->getTrack(0); + REQUIRE(track0->getEvents().size() == 1); + + const auto programChange = + eventAs(track0->getEvents()[0]); + REQUIRE(programChange->getTick() == 0); + REQUIRE(programChange->getProgram() == 0); +} + +TEST_CASE("Kaitai MPC3000 SEQ production load decodes two-byte pitch bend payload", + "[kaitai-seq]") +{ + auto fs = cmrc::mpctest::get_filesystem(); + auto file = fs.open("test/RealMpc3000/Seq/M3KPC.SEQ"); + std::vector data(file.begin(), file.end()); + replaceProgramChangeWithPitchBend(data, 0x00, 0x40); + + mpc::Mpc mpc; + mpc::TestMpc::initializeTestMpcWithoutMidiServices(mpc); + const std::string fileName = "M3KPB_SYN.SEQ"; + prepareSeqFile(mpc, data, fileName); + + auto layeredScreen = mpc.getLayeredScreen(); + layeredScreen->openScreen("load"); + + const auto loadScreen = mpc.screens->get(); + selectPreparedSeqFileForLoad(mpc, loadScreen, fileName); + loadScreen->function(5); + + REQUIRE(layeredScreen->getCurrentScreenName() == "load-a-sequence"); + + mpc.getSequencer()->getStateManager()->drainQueue(); + + auto sequence = mpc.getSequencer()->getSequence(mpc::TempSequenceIndex); + REQUIRE(sequence->getName() == "SEQ02"); + REQUIRE(sequence->getLastBarIndex() == 1); + + auto track0 = sequence->getTrack(0); + REQUIRE(track0->getEvents().size() == 1); + + const auto pitchBend = + eventAs(track0->getEvents()[0]); + REQUIRE(pitchBend->getTick() == 0); + REQUIRE(pitchBend->getAmount() == 0); +} + +TEST_CASE("Kaitai MPC3000 SEQ production load decodes minimum pitch bend payload", + "[kaitai-seq]") +{ + auto fs = cmrc::mpctest::get_filesystem(); + auto file = fs.open("test/RealMpc3000/Seq/M3KPC.SEQ"); + std::vector data(file.begin(), file.end()); + replaceProgramChangeWithPitchBend(data, 0x00, 0x00); + + mpc::Mpc mpc; + mpc::TestMpc::initializeTestMpcWithoutMidiServices(mpc); + const std::string fileName = "M3KPB_MIN.SEQ"; + prepareSeqFile(mpc, data, fileName); + + auto layeredScreen = mpc.getLayeredScreen(); + layeredScreen->openScreen("load"); + + const auto loadScreen = mpc.screens->get(); + selectPreparedSeqFileForLoad(mpc, loadScreen, fileName); + loadScreen->function(5); + + REQUIRE(layeredScreen->getCurrentScreenName() == "load-a-sequence"); + + mpc.getSequencer()->getStateManager()->drainQueue(); + + auto sequence = mpc.getSequencer()->getSequence(mpc::TempSequenceIndex); + auto track0 = sequence->getTrack(0); + REQUIRE(track0->getEvents().size() == 1); + + const auto pitchBend = + eventAs(track0->getEvents()[0]); + REQUIRE(pitchBend->getAmount() == -8192); +} + +TEST_CASE("Kaitai MPC3000 SEQ production load decodes maximum pitch bend payload", + "[kaitai-seq]") +{ + auto fs = cmrc::mpctest::get_filesystem(); + auto file = fs.open("test/RealMpc3000/Seq/M3KPC.SEQ"); + std::vector data(file.begin(), file.end()); + replaceProgramChangeWithPitchBend(data, 0x7f, 0x7f); + + mpc::Mpc mpc; + mpc::TestMpc::initializeTestMpcWithoutMidiServices(mpc); + const std::string fileName = "M3KPB_MAX.SEQ"; + prepareSeqFile(mpc, data, fileName); + + auto layeredScreen = mpc.getLayeredScreen(); + layeredScreen->openScreen("load"); + + const auto loadScreen = mpc.screens->get(); + selectPreparedSeqFileForLoad(mpc, loadScreen, fileName); + loadScreen->function(5); + + REQUIRE(layeredScreen->getCurrentScreenName() == "load-a-sequence"); + + mpc.getSequencer()->getStateManager()->drainQueue(); + + auto sequence = mpc.getSequencer()->getSequence(mpc::TempSequenceIndex); + auto track0 = sequence->getTrack(0); + REQUIRE(track0->getEvents().size() == 1); + + const auto pitchBend = + eventAs(track0->getEvents()[0]); + REQUIRE(pitchBend->getAmount() == 8191); +} + +TEST_CASE("Kaitai MPC3000 SEQ production load imports real pitch bend fixture", + "[kaitai-seq]") +{ + mpc::Mpc mpc; + mpc::TestMpc::initializeTestMpcWithoutMidiServices(mpc); + const std::string fileName = "M3KPB.SEQ"; + prepareSeqFile(mpc, "test/RealMpc3000/Seq/M3KPB.SEQ", fileName); + + auto layeredScreen = mpc.getLayeredScreen(); + layeredScreen->openScreen("load"); + + const auto loadScreen = mpc.screens->get(); + selectPreparedSeqFileForLoad(mpc, loadScreen, fileName); + loadScreen->function(5); + + REQUIRE(layeredScreen->getCurrentScreenName() == "load-a-sequence"); + + mpc.getSequencer()->getStateManager()->drainQueue(); + + auto sequence = mpc.getSequencer()->getSequence(mpc::TempSequenceIndex); + REQUIRE(sequence->getName() == "SEQ04"); + REQUIRE(sequence->getLastBarIndex() == 1); + + auto track0 = sequence->getTrack(0); + REQUIRE(track0->getEvents().size() == 1); + + const auto pitchBend = + eventAs(track0->getEvents()[0]); + REQUIRE(pitchBend->getTick() == 0); + REQUIRE(pitchBend->getAmount() == 0); +} + +TEST_CASE("Load a sequence PLAY shows held preview overlay and release restores prior selection", + "[kaitai-seq][ui]") +{ + mpc::Mpc mpc; + mpc::TestMpc::initializeTestMpcWithoutMidiServices(mpc); + + auto sequencer = mpc.getSequencer(); + auto stateManager = sequencer->getStateManager(); + sequencer->getTransport()->setCountEnabled(false); + + sequencer->getSequence(5)->init(0); + stateManager->drainQueue(); + sequencer->setSelectedSequenceIndex(mpc::SequenceIndex(5), true); + stateManager->drainQueue(); + + const std::string fileName = "M3KNOTE.SEQ"; + prepareSeqFile(mpc, "test/RealMpc3000/Seq/M3KNOTE.SEQ", fileName); + + auto layeredScreen = mpc.getLayeredScreen(); + layeredScreen->openScreen("load"); + + const auto loadScreen = mpc.screens->get(); + selectPreparedSeqFileForLoad(mpc, loadScreen, fileName); + loadScreen->function(5); + REQUIRE(layeredScreen->getCurrentScreenName() == "load-a-sequence"); + + auto loadSequenceScreen = + mpc.screens->get(); + + layeredScreen->getCurrentScreen()->function(2); + stateManager->drainQueue(); + + REQUIRE(layeredScreen->getCurrentScreenName() == "load-a-sequence-play"); + REQUIRE(sequencer->getSelectedSequenceIndex() == mpc::TempSequenceIndex); + REQUIRE(sequencer->getTransport()->isPlaying()); + + mpc.getEngineHost()->prepareProcessBlock(512); + layeredScreen->timerCallback(); + REQUIRE_FALSE( + layeredScreen->getCurrentScreen()->findLabel("position")->getText().empty()); + + mpc::command::ReleaseFunctionCommand(mpc, 2).execute(); + stateManager->drainQueue(); + + REQUIRE(layeredScreen->getCurrentScreenName() == "load-a-sequence"); + REQUIRE_FALSE(sequencer->getTransport()->isPlaying()); + REQUIRE(sequencer->getSelectedSequenceIndex() == mpc::SequenceIndex(5)); + + loadSequenceScreen->function(3); + stateManager->drainQueue(); + + REQUIRE(layeredScreen->getCurrentScreenName() == "load"); +} + +TEST_CASE("Load a sequence KEEP copies temp sequence into chosen slot and stops preview", + "[kaitai-seq][ui]") +{ + mpc::Mpc mpc; + mpc::TestMpc::initializeTestMpcWithoutMidiServices(mpc); + + auto sequencer = mpc.getSequencer(); + auto stateManager = sequencer->getStateManager(); + sequencer->getTransport()->setCountEnabled(false); + + sequencer->getSequence(5)->init(0); + stateManager->drainQueue(); + sequencer->setSelectedSequenceIndex(mpc::SequenceIndex(5), true); + stateManager->drainQueue(); + + const std::string fileName = "M3KNOTE.SEQ"; + prepareSeqFile(mpc, "test/RealMpc3000/Seq/M3KNOTE.SEQ", fileName); + + auto layeredScreen = mpc.getLayeredScreen(); + layeredScreen->openScreen("load"); + + const auto loadScreen = mpc.screens->get(); + selectPreparedSeqFileForLoad(mpc, loadScreen, fileName); + loadScreen->function(5); + REQUIRE(layeredScreen->getCurrentScreenName() == "load-a-sequence"); + + auto loadSequenceScreen = + mpc.screens->get(); + loadSequenceScreen->setLoadInto(mpc::SequenceIndex(7)); + + layeredScreen->getCurrentScreen()->function(2); + stateManager->drainQueue(); + REQUIRE(layeredScreen->getCurrentScreenName() == "load-a-sequence-play"); + REQUIRE(sequencer->getSelectedSequenceIndex() == mpc::TempSequenceIndex); + REQUIRE(sequencer->getTransport()->isPlaying()); + + mpc::command::ReleaseFunctionCommand(mpc, 2).execute(); + stateManager->drainQueue(); + REQUIRE(layeredScreen->getCurrentScreenName() == "load-a-sequence"); + REQUIRE_FALSE(sequencer->getTransport()->isPlaying()); + REQUIRE(sequencer->getSelectedSequenceIndex() == mpc::SequenceIndex(5)); + + loadSequenceScreen->function(4); + stateManager->drainQueue(); + + REQUIRE(layeredScreen->getCurrentScreenName() == "sequencer"); + REQUIRE_FALSE(sequencer->getTransport()->isPlaying()); + REQUIRE(sequencer->getSelectedSequenceIndex() == mpc::SequenceIndex(7)); + + auto keptSequence = sequencer->getSequence(7); + REQUIRE(keptSequence->getTrack(0)->getEvents().size() == 1); + const auto keptNote = + eventAs( + keptSequence->getTrack(0)->getEvents()[0]); + REQUIRE(keptNote->getTick() == 24); + REQUIRE(keptNote->getNote() == 35); + REQUIRE(keptNote->getVelocity() == 64); + REQUIRE(keptNote->getDuration() == 96); +} + +TEST_CASE("Unreadable .SEQ file reports error and returns to LOAD", + "[kaitai-seq][ui]") +{ + mpc::Mpc mpc; + mpc::TestMpc::initializeTestMpc(mpc); + + const std::string fileName = "BROKEN.SEQ"; + prepareSeqFile(mpc, std::vector{'B', 'A', 'D'}, fileName); + + auto layeredScreen = mpc.getLayeredScreen(); + layeredScreen->openScreen("load"); + + const auto loadScreen = mpc.screens->get(); + selectPreparedSeqFileForLoad(mpc, loadScreen, fileName); + loadScreen->function(5); + + layeredScreen->timerCallback(); + REQUIRE(layeredScreen->getCurrentScreenName() == "popup"); + waitForTimedPopupToReturnToLoad(mpc); + REQUIRE(layeredScreen->getCurrentScreenName() == "load"); +} + +TEST_CASE("Kaitai MPC60 v2 SEQ loads a real MPC60 2.14 sequence through the production seam", "[kaitai-seq][real-mpc60]") +{ + mpc::Mpc mpc; + mpc::TestMpc::initializeTestMpcWithoutMidiServices(mpc); + + auto seqFile = prepareSeqFile( + mpc, + "test/RealMpc60/Seq/MPC60_V214_SEQ01.SEQ", + "M60SEQ.SEQ" + ); + REQUIRE(seqFile); + + const auto loaded = mpc::file::kaitai::Mpc3000SeqIo::load(mpc, seqFile); + REQUIRE(loaded); + mpc.getSequencer()->getStateManager()->drainQueue(); + + const auto sequence = *loaded; + REQUIRE(sequence->isUsed()); + REQUIRE(sequence->getName() == "SEQ01"); + REQUIRE(sequence->getLastBarIndex() == 1); + REQUIRE(std::fabs(sequence->getInitialTempo() - 120.0) < 0.001); + REQUIRE(sequence->getTimeSignatureFromBarIndex(0).numerator == 4); + REQUIRE(sequence->getTimeSignatureFromBarIndex(0).denominator == 4); + REQUIRE(sequence->isLoopEnabled()); + + REQUIRE(sequence->getTrack(0)->getEvents().size() == 1U); + const auto note = eventAs( + sequence->getTrack(0)->getEvents().at(0)); + REQUIRE(note->getTick() == 0); + REQUIRE(note->getNote() == 1); + REQUIRE(note->getVelocity() == 64); + REQUIRE(note->getDuration() == 96); +} diff --git a/src/test/KaitaiSndTest.cpp b/src/test/KaitaiSndTest.cpp new file mode 100644 index 000000000..2a223e429 --- /dev/null +++ b/src/test/KaitaiSndTest.cpp @@ -0,0 +1,849 @@ +#include + +#include "Mpc.hpp" +#include "SampleOps.hpp" +#include "TestMpc.hpp" +#include "disk/MpcFile.hpp" +#include "disk/SoundLoader.hpp" +#include "file/kaitai/SndIo.hpp" +#include "file/kaitai/generated/mpc2000snd.h" +#include "file/kaitai/generated/mpc3000_snd_v2.h" +#include "file/kaitai/generated/mpc60_snd_v1.h" +#include "sampler/Sampler.hpp" +#include "sampler/Sound.hpp" + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +CMRC_DECLARE(mpctest); + +using namespace mpc::disk; +using namespace mpc::file::kaitai; +using namespace mpc::file::kaitai::generated; +using namespace mpc::sampleops; + +namespace +{ + struct ExpectedSound + { + std::string name; + bool mono; + int sampleRate; + int level; + int tune; + int beatCount; + int start; + int end; + int loopTo; + bool loopEnabled; + std::vector sampleData; + }; + + struct SaveRoundtripCase + { + std::string label; + std::string fileStem; + ExpectedSound expected; + }; + + std::vector resourceBytes(const std::string &resourcePath) + { + auto fs = cmrc::mpctest::get_filesystem(); + auto file = fs.open(resourcePath); + return std::vector(file.begin(), file.end()); + } + + template + void withParsedSndBytes(const std::vector &bytes, Assertions &&assertions) + { + std::stringstream parseStream( + std::string(bytes.begin(), bytes.end()), + std::ios::in | std::ios::out | std::ios::binary + ); + kaitai::kstream parseIo(&parseStream); + mpc2000snd_t parsed(&parseIo); + parsed._read(); + assertions(parsed); + } + + template + void withParsedMpc3000SndBytes(const std::vector &bytes, Assertions &&assertions) + { + std::stringstream parseStream( + std::string(bytes.begin(), bytes.end()), + std::ios::in | std::ios::out | std::ios::binary + ); + kaitai::kstream parseIo(&parseStream); + mpc3000_snd_v2_t parsed(&parseIo); + parsed._read(); + assertions(parsed); + } + + template + void withParsedMpc60SndBytes(const std::vector &bytes, Assertions &&assertions) + { + std::stringstream parseStream( + std::string(bytes.begin(), bytes.end()), + std::ios::in | std::ios::out | std::ios::binary + ); + kaitai::kstream parseIo(&parseStream); + mpc60_snd_v1_t parsed(&parseIo); + assertions(parsed); + } + + std::shared_ptr writeTempSndFile(mpc::Mpc &mpc, + const std::vector &bytes, + const std::string &fileName) + { + const auto tempDir = mpc.paths->getDocuments()->tempPath(); + (void) mpc_fs::create_directories(tempDir); + const auto path = tempDir / fileName; + auto file = std::make_shared(path); + auto bytesCopy = bytes; + file->setFileData(bytesCopy); + return file; + } + + std::shared_ptr loadWithSoundLoader(mpc::Mpc &mpc, + const std::vector &bytes, + const std::string &fileName) + { + auto file = writeTempSndFile(mpc, bytes, fileName); + auto sound = mpc.getSampler()->addSound(); + SoundLoaderResult result; + SoundLoader loader(mpc, false); + loader.loadSound(file, result, sound, false); + REQUIRE(result.success); + return sound; + } + + void requireSoundMatches(const std::shared_ptr &sound, + const ExpectedSound &expected) + { + REQUIRE(sound->getName() == expected.name); + REQUIRE(sound->isMono() == expected.mono); + REQUIRE(sound->getSampleRate() == expected.sampleRate); + REQUIRE(sound->getSndLevel() == expected.level); + REQUIRE(sound->getTune() == expected.tune); + REQUIRE(sound->getBeatCount() == expected.beatCount); + REQUIRE(sound->getStart() == expected.start); + REQUIRE(sound->getEnd() == expected.end); + REQUIRE(sound->getLoopTo() == expected.loopTo); + REQUIRE(sound->isLoopEnabled() == expected.loopEnabled); + REQUIRE(sound->getFrameCount() == static_cast(expected.sampleData.size() / (expected.mono ? 1 : 2))); + REQUIRE(*sound->getSampleData() == expected.sampleData); + } + + std::vector monoSamples(const std::vector &samples) + { + std::vector result; + result.reserve(samples.size()); + for (const auto sample : samples) + { + result.push_back(short_to_float(sample)); + } + return result; + } + + std::vector stereoSamples(const std::vector &left, + const std::vector &right) + { + std::vector result; + result.reserve(left.size() + right.size()); + for (const auto sample : left) + { + result.push_back(short_to_float(sample)); + } + for (const auto sample : right) + { + result.push_back(short_to_float(sample)); + } + return result; + } + + std::string expectedHeaderName(const std::string &name) + { + auto truncated = name.substr(0, 16); + truncated.append(16 - truncated.size(), ' '); + truncated.push_back('\0'); + return truncated; + } + + uint64_t fnv1aPcmHash(const std::vector &samples) + { + uint64_t hash = 1469598103934665603ULL; + for (const auto sample : samples) + { + const auto pcm = static_cast(mean_normalized_float_to_short(sample)); + hash ^= (pcm & 0xffU); + hash *= 1099511628211ULL; + hash ^= ((pcm >> 8U) & 0xffU); + hash *= 1099511628211ULL; + } + return hash; + } + + void requirePcmWindow(const std::vector &samples, + const std::size_t offset, + const std::array &expected) + { + REQUIRE(samples.size() >= offset + expected.size()); + for (std::size_t i = 0; i < expected.size(); ++i) + { + REQUIRE(mean_normalized_float_to_short(samples[offset + i]) == + expected[i]); + } + } + +} + +TEST_CASE("Kaitai MPC2000 SND saves and loads explicit semantics", "[kaitai-snd]") +{ + mpc::sampler::Sound sound(44100); + sound.setName("KICK_01"); + sound.setMono(true); + sound.setLevel(100); + sound.setTune(-2); + + auto inputData = sound.getMutableSampleData(); + inputData->push_back(short_to_float(static_cast(1234))); + inputData->push_back(short_to_float(static_cast(-1234))); + + sound.setStart(0); + sound.setEnd(2); + sound.setLoopTo(1); + sound.setLoopEnabled(true); + sound.setBeatCount(4); + + const auto kaitaiBytes = SndIo::saveSound(sound); + + withParsedSndBytes(kaitaiBytes, [&](mpc2000snd_t &parsed) + { + REQUIRE(parsed.magic() == std::string("\x01\x04", 2)); + REQUIRE(parsed.name() == expectedHeaderName("KICK_01")); + REQUIRE(parsed.level() == 100); + REQUIRE(parsed.tune() == -2); + REQUIRE(parsed.stereo() == false); + REQUIRE(parsed.start() == 0U); + REQUIRE(parsed.end() == 2U); + REQUIRE(parsed.frame_count() == 2U); + REQUIRE(parsed.loop_frame_count() == 1U); + REQUIRE(parsed.loop_enabled() == true); + REQUIRE(parsed.beat_count() == 4); + REQUIRE(parsed.sample_rate() == 44100); + REQUIRE(parsed.sample_data() != nullptr); + REQUIRE(parsed.sample_data()->size() == 2U); + REQUIRE(parsed.sample_data()->at(0) == 1234); + REQUIRE(parsed.sample_data()->at(1) == -1234); + }); + + mpc::Mpc mpc; + mpc::TestMpc::initializeTestMpc(mpc); + const auto loaded = loadWithSoundLoader(mpc, kaitaiBytes, "KICK_01.SND"); + requireSoundMatches( + loaded, + ExpectedSound{ + "KICK_01", + true, + 44100, + 100, + -2, + 4, + 0, + 2, + 1, + true, + *inputData + }); +} + +TEST_CASE("Kaitai MPC2000 SND save/load covers prod-used sound properties", "[kaitai-snd]") +{ + const auto cases = std::vector{ + { + "mono minimum bounds", + "mono_min_bounds", + ExpectedSound{ + "mono_min_bounds", + true, + 0, + 0, + -120, + 1, + 0, + 0, + 0, + false, + monoSamples({-32768, -1, 0, 32767}) + } + }, + { + "mono mid values", + "mono_mid_values", + ExpectedSound{ + "mono_mid_values", + true, + 44100, + 100, + 0, + 16, + 1, + 3, + 2, + true, + monoSamples({-12345, -17, 19, 12345}) + } + }, + { + "stereo full span loop off", + "stereo_full_span", + ExpectedSound{ + "stereo_full_span", + false, + 22050, + 100, + 0, + 16, + 0, + 5, + 0, + false, + stereoSamples({1000, 2000, 3000, 4000, 5000}, {-1000, -2000, -3000, -4000, -5000}) + } + }, + { + "stereo maximum bounds", + "stereo_max_bounds", + ExpectedSound{ + "stereo_max_bounds", + false, + 65535, + 200, + 120, + 32, + 5, + 5, + 5, + true, + stereoSamples({32767, 23456, 12345, 1, -1}, {-32768, -23456, -12345, -1, 1}) + } + } + }; + + for (const auto &testCase : cases) + { + SECTION(testCase.label) + { + mpc::sampler::Sound source(testCase.expected.sampleRate); + source.setName(testCase.expected.name); + source.setMono(testCase.expected.mono); + source.setLevel(testCase.expected.level); + source.setTune(testCase.expected.tune); + source.setBeatCount(testCase.expected.beatCount); + + auto mutableData = source.getMutableSampleData(); + *mutableData = testCase.expected.sampleData; + + source.setStart(testCase.expected.start); + source.setEnd(testCase.expected.end); + source.setLoopTo(testCase.expected.loopTo); + source.setLoopEnabled(testCase.expected.loopEnabled); + + const auto bytes = SndIo::saveSound(source); + withParsedSndBytes(bytes, [&](mpc2000snd_t &parsed) + { + REQUIRE(parsed.level() == static_cast(testCase.expected.level)); + REQUIRE(parsed.tune() == testCase.expected.tune); + REQUIRE(parsed.stereo() == !testCase.expected.mono); + REQUIRE(parsed.start() == static_cast(testCase.expected.start)); + REQUIRE(parsed.end() == static_cast(testCase.expected.end)); + REQUIRE(parsed.frame_count() == static_cast(testCase.expected.sampleData.size() / (testCase.expected.mono ? 1 : 2))); + REQUIRE(parsed.loop_frame_count() == static_cast(testCase.expected.end - testCase.expected.loopTo)); + REQUIRE(parsed.loop_enabled() == testCase.expected.loopEnabled); + REQUIRE(parsed.beat_count() == static_cast(testCase.expected.beatCount)); + REQUIRE(parsed.sample_rate() == static_cast(testCase.expected.sampleRate)); + REQUIRE(parsed.sample_data() != nullptr); + REQUIRE(parsed.sample_data()->size() == testCase.expected.sampleData.size()); + + for (std::size_t i = 0; i < parsed.sample_data()->size(); i++) + { + REQUIRE(parsed.sample_data()->at(i) == mean_normalized_float_to_short(testCase.expected.sampleData[i])); + } + }); + + mpc::Mpc mpc; + mpc::TestMpc::initializeTestMpc(mpc); + const auto loaded = loadWithSoundLoader(mpc, bytes, testCase.fileStem + ".SND"); + requireSoundMatches(loaded, testCase.expected); + } + } +} + +TEST_CASE("Kaitai MPC2000 SND parses real 2KXL mono and stereo files through the production loader", "[kaitai-snd][real-2kxl]") +{ + mpc::Mpc mpc; + mpc::TestMpc::initializeTestMpc(mpc); + + const auto monoBytes = resourceBytes("test/RealMpc2000xl/Snd/mono_loop_off.SND"); + const auto mono = loadWithSoundLoader(mpc, monoBytes, "mono_loop_off.SND"); + + REQUIRE(mono->getName() == "mono_loop_off"); + REQUIRE(mono->isMono()); + REQUIRE(mono->getSampleRate() == 44100); + REQUIRE(mono->getSndLevel() == 100); + REQUIRE(mono->getTune() == 0); + REQUIRE(mono->getBeatCount() == 4); + REQUIRE(mono->getStart() == 0); + REQUIRE(mono->getEnd() == 8012); + REQUIRE(mono->getLoopTo() == 0); + REQUIRE(!mono->isLoopEnabled()); + REQUIRE(mono->getFrameCount() == 8012); + REQUIRE(mono->getSampleData()->size() == 8012U); + REQUIRE((*mono->getSampleData())[0] == short_to_float(-3326)); + REQUIRE((*mono->getSampleData())[1] == short_to_float(-2941)); + REQUIRE((*mono->getSampleData())[2] == short_to_float(-3809)); + + const auto stereoBytes = resourceBytes("test/RealMpc2000xl/Snd/stereo_loop_off.SND"); + const auto stereo = loadWithSoundLoader(mpc, stereoBytes, "stereo_loop_off.SND"); + + REQUIRE(stereo->getName() == "stereo_loop_off"); + REQUIRE(!stereo->isMono()); + REQUIRE(stereo->getSampleRate() == 44100); + REQUIRE(stereo->getSndLevel() == 100); + REQUIRE(stereo->getTune() == 0); + REQUIRE(stereo->getBeatCount() == 4); + REQUIRE(stereo->getStart() == 0); + REQUIRE(stereo->getEnd() == 8012); + REQUIRE(stereo->getLoopTo() == 0); + REQUIRE(!stereo->isLoopEnabled()); + REQUIRE(stereo->getFrameCount() == 8012); + REQUIRE(stereo->getSampleData()->size() == 16024U); + REQUIRE((*stereo->getSampleData())[0] == short_to_float(-3326)); + REQUIRE((*stereo->getSampleData())[1] == short_to_float(-2941)); + REQUIRE((*stereo->getSampleData())[2] == short_to_float(-3809)); + REQUIRE((*stereo->getSampleData())[8012] == short_to_float(-2233)); +} + +TEST_CASE("Kaitai MPC60 SND parses a real hardware 01 01 file through the production loader", "[kaitai-snd][real-mpc60]") +{ + const auto bytes = resourceBytes("test/RealMpc60/Snd/SOUND002.SND"); + + withParsedMpc60SndBytes(bytes, [&](mpc60_snd_v1_t &parsed) + { + REQUIRE(static_cast(bytes[0]) == 0x01); + REQUIRE(static_cast(bytes[1]) == 0x01); + REQUIRE(parsed.name() == expectedHeaderName("sound002").substr(0, 16)); + REQUIRE(parsed.sample_count() == 4000U); + REQUIRE(parsed.time_from_start_of_data_to_end_of_play_msec() == 99U); + REQUIRE(parsed.time_from_start_of_data_to_start_play_msec() == 20U); + REQUIRE(parsed.decay_time_msec() == 20U); + REQUIRE(parsed.volume_percent() == 100U); + REQUIRE(parsed.tuning() == 0); + REQUIRE(parsed.reserved_zeroes() == std::string(5, '\0')); + REQUIRE(parsed.velocity_to_volume_percent() == 100U); + REQUIRE(parsed.sample_rate() == 40000); + REQUIRE(parsed.sample_data_pairs() != nullptr); + REQUIRE(parsed.sample_data_pairs()->size() == 2000U); + }); + + mpc::Mpc mpc; + mpc::TestMpc::initializeTestMpc(mpc); + const auto loaded = loadWithSoundLoader(mpc, bytes, "SOUND002.SND"); + + REQUIRE(loaded->getName() == "sound002"); + REQUIRE(loaded->isMono()); + REQUIRE(loaded->getSampleRate() == 40000); + REQUIRE(loaded->getSndLevel() == 100); + REQUIRE(loaded->getTune() == 0); + REQUIRE(loaded->getBeatCount() == 4); + REQUIRE(loaded->getStart() == 0); + REQUIRE(loaded->getEnd() == 3999); + REQUIRE(loaded->getLoopTo() == 0); + REQUIRE_FALSE(loaded->isLoopEnabled()); + REQUIRE(loaded->getFrameCount() == 4000); + REQUIRE(loaded->getSampleData()->size() == 4000U); + const auto [minIt, maxIt] = std::minmax_element( + loaded->getSampleData()->begin(), loaded->getSampleData()->end()); + CAPTURE(*minIt, *maxIt); + REQUIRE(*minIt < *maxIt); +} + +TEST_CASE("Kaitai MPC3000 SND parses a real hardware 01 02 file through the production loader", "[kaitai-snd][real-mpc3000]") +{ + const auto bytes = resourceBytes("test/RealMpc3000/Snd/SOUND017.SND"); + + withParsedMpc3000SndBytes(bytes, [&](mpc3000_snd_v2_t &parsed) + { + REQUIRE(static_cast(bytes[0]) == 0x01); + REQUIRE(static_cast(bytes[1]) == 0x02); + REQUIRE(parsed.name() == expectedHeaderName("sound017").substr(0, 16)); + REQUIRE(parsed.level() == 100U); + REQUIRE(parsed.unknown_1() == std::string(2, '\0')); + REQUIRE(parsed.start() == 44U); + REQUIRE(parsed.end() == 4410U); + REQUIRE(parsed.frame_count() == 4410U); + REQUIRE(parsed.sample_rate() == 44100); + REQUIRE(parsed.header_size() == 38); + REQUIRE(parsed.sample_data() != nullptr); + REQUIRE(parsed.sample_data()->size() == 4410U); + REQUIRE(parsed.sample_data()->at(0) == 5); + REQUIRE(parsed.sample_data()->at(1) == 6); + REQUIRE(parsed.sample_data()->at(2) == 7); + REQUIRE(parsed.sample_data()->at(3) == 6); + }); + + mpc::Mpc mpc; + mpc::TestMpc::initializeTestMpc(mpc); + const auto loaded = loadWithSoundLoader(mpc, bytes, "SOUND017.SND"); + + REQUIRE(loaded->getName() == "sound017"); + REQUIRE(loaded->isMono()); + REQUIRE(loaded->getSampleRate() == 44100); + REQUIRE(loaded->getSndLevel() == 100); + REQUIRE(loaded->getTune() == 0); + REQUIRE(loaded->getBeatCount() == 4); + REQUIRE(loaded->getStart() == 44); + REQUIRE(loaded->getEnd() == 4410); + REQUIRE(loaded->getLoopTo() == 0); + REQUIRE_FALSE(loaded->isLoopEnabled()); + REQUIRE(loaded->getFrameCount() == 4410); + REQUIRE(loaded->getSampleData()->size() == 4410U); + REQUIRE((*loaded->getSampleData())[0] == short_to_float(5)); + REQUIRE((*loaded->getSampleData())[1] == short_to_float(6)); + REQUIRE((*loaded->getSampleData())[2] == short_to_float(7)); + REQUIRE((*loaded->getSampleData())[3] == short_to_float(6)); +} + +TEST_CASE("Kaitai MPC60 SND parses native MPC60 2.14 ROCK exports through the production loader", "[kaitai-snd][real-mpc60]") +{ + struct NativeMpc60SndCase + { + std::string fileName; + std::string headerName; + std::string loadedName; + uint32_t playbackEndMsec; + uint16_t decayTimeMsec; + uint64_t decodedPcmHash; + std::array firstPcm; + std::array middlePcm; + std::array lastPcm; + }; + + const auto files = std::vector{ + {"BASSGUIT.SND", "BASSGUIT", "BASSGUIT", 200, 50, 0xa5d8d39839ca44abULL, {162, 315, 446, 557}, {1717, 1169, 184, -1198}, {-5648, -5480, -5297, -5104}}, + {"BIG_CLAP.SND", "BIG_CLAP", "BIG_CLAP", 600, 350, 0xdea861a215d6edc7ULL, {6, 11, 14, 9}, {-1066, -880, -632, -212}, {0, 0, 0, 0}}, + {"COWBEL#1.SND", "COWBEL#1", "COWBEL#1", 350, 100, 0xbc117cd727a9f5b2ULL, {27, 52, 68, 72}, {18, 134, 124, 25}, {234, 211, 209, 202}}, + {"CRASH2_H.SND", "CRASH2_H", "CRASH2_H", 995, 163, 0x2b4a71d36d7733b7ULL, {-11666, -2611, -1215, -748}, {-709, -1140, -1363, 479}, {1127, 1296, 445, -219}}, + {"CRASH2_L.SND", "CRASH2_L", "CRASH2_L", 1766, 261, 0xfa4c815ae3824e34ULL, {-11666, -2611, -1208, -737}, {-661, -1047, -816, -180}, {191, 225, 207, 166}}, + {"CRASH_MU.SND", "CRASH_MUTE", "CRASH_MU", 634, 117, 0x7e2aba841084908fULL, {13, 9, 19, 12}, {-5, 175, 229, 183}, {58, 38, 30, 26}}, + {"GONG.SND", "GONG", "GONG", 1310, 148, 0x377926352459736fULL, {-11761, -2336, -1049, -824}, {1663, 1547, 1340, 1238}, {203, 169, 134, 173}}, + {"HAT2CLSD.SND", "HAT2CLSD", "HAT2CLSD", 400, 100, 0x4ca36b849f85e99cULL, {27, 52, 55, 69}, {674, 18, 1, -52}, {72, 87, 84, 81}}, + {"HAT2MED.SND", "HAT2MED", "HAT2MED", 600, 100, 0xe513d30847955496ULL, {33, 57, 58, 64}, {1164, 1296, 941, -435}, {390, 304, 6, 114}}, + {"HAT2OPN.SND", "HAT2OPN", "HAT2OPN", 1000, 200, 0x21b0c6dba5e0f595ULL, {27, 45, 57, 64}, {-1474, -776, -1336, -288}, {371, 307, 430, 377}}, + {"KICK#2.SND", "KiCK#2", "KiCK#2", 400, 100, 0x1b017ff1ffe6524bULL, {13, 22, 28, 32}, {293, 265, 244, 13}, {-181, -206, -235, -280}}, + {"METALSHO.SND", "METALSHOT", "METALSHO", 269, 131, 0xf1529a4cd4fb64ecULL, {-11612, -2675, -1336, -862}, {-169, -402, -750, -896}, {-75, -67, -70, -72}}, + {"RIDE4B.SND", "RIDE4B", "RIDE4B", 961, 172, 0xd07be7c6d058175fULL, {2551, -5462, -4310, -2054}, {-839, -522, -213, -104}, {-106, -116, -115, -87}}, + {"ROCTOMID.SND", "ROCTOMID", "ROCTOMID", 800, 350, 0x2376e1fe2307dba6ULL, {13, 16, 24, 29}, {827, 815, 794, 774}, {39, 46, 43, 41}}, + {"ROCTOMLO.SND", "ROCTOMLO", "ROCTOMLO", 1500, 900, 0x6ba2aad9ea269cadULL, {0, -6, -4, -8}, {-347, -372, -396, -411}, {-17, -17, -11, -6}}, + {"SIDESTK2.SND", "SIDESTK2", "SIDESTK2", 400, 100, 0x1c938199fd48368bULL, {-95, -160, -213, -254}, {17, -57, -134, -82}, {-44, -55, -49, -51}}, + {"SNARE#2.SND", "SNARE#2", "SNARE#2", 500, 100, 0xe36c4c5128b7dd18ULL, {6, 11, 14, 9}, {138, -22, 535, 878}, {158, 212, 254, 280}}, + }; + + for (const auto &testCase : files) + { + SECTION(testCase.fileName) + { + const auto resourcePath = + "test/RealMpc60/Snd/Mpc60V214Rock/" + testCase.fileName; + const auto bytes = resourceBytes(resourcePath); + const auto fileStem = testCase.fileName.substr(0, testCase.fileName.size() - 4); + const auto expectedSampleCount = + static_cast((bytes.size() - 39U) * 2U / 3U); + + withParsedMpc60SndBytes(bytes, [&](mpc60_snd_v1_t &parsed) + { + REQUIRE(static_cast(bytes[0]) == 0x01); + REQUIRE(static_cast(bytes[1]) == 0x01); + REQUIRE(parsed.name() == expectedHeaderName(testCase.headerName).substr(0, 16)); + REQUIRE(parsed.sample_count() == expectedSampleCount); + REQUIRE(parsed.packed_sample_data_byte_count() == + static_cast(bytes.size() - 39U)); + REQUIRE(parsed.sample_rate() == 40000); + REQUIRE(parsed.time_from_start_of_data_to_start_play_msec() == 0U); + REQUIRE(parsed.time_from_start_of_data_to_end_of_play_msec() == + testCase.playbackEndMsec); + REQUIRE(parsed.decay_time_msec() == testCase.decayTimeMsec); + REQUIRE(parsed.volume_percent() == 100U); + REQUIRE(parsed.tuning() == 0); + REQUIRE(parsed.reserved_zeroes() == std::string(5, '\0')); + REQUIRE(parsed.velocity_to_volume_percent() == 100U); + REQUIRE(parsed.sample_data_pairs() != nullptr); + REQUIRE(parsed.sample_data_pairs()->size() == + static_cast((expectedSampleCount + 1U) / 2U)); + }); + + mpc::Mpc mpc; + mpc::TestMpc::initializeTestMpc(mpc); + const auto loaded = loadWithSoundLoader(mpc, bytes, testCase.fileName); + + REQUIRE(loaded->getName() == testCase.loadedName); + REQUIRE(loaded->isMono()); + REQUIRE(loaded->getSampleRate() == 40000); + REQUIRE(loaded->getSndLevel() == 100); + REQUIRE(loaded->getTune() == 0); + REQUIRE(loaded->getBeatCount() == 4); + REQUIRE(loaded->getStart() == 0); + REQUIRE(loaded->getEnd() == static_cast(expectedSampleCount) - 1); + REQUIRE(loaded->getLoopTo() == 0); + REQUIRE_FALSE(loaded->isLoopEnabled()); + REQUIRE(loaded->getFrameCount() == static_cast(expectedSampleCount)); + REQUIRE(loaded->getSampleData()->size() == expectedSampleCount); + + const auto &samples = *loaded->getSampleData(); + const auto [minIt, maxIt] = std::minmax_element( + samples.begin(), samples.end()); + CAPTURE(testCase.fileName, *minIt, *maxIt); + REQUIRE(*minIt < *maxIt); + REQUIRE(fnv1aPcmHash(samples) == testCase.decodedPcmHash); + requirePcmWindow(samples, 0, testCase.firstPcm); + requirePcmWindow(samples, samples.size() / 2, testCase.middlePcm); + requirePcmWindow(samples, samples.size() - testCase.lastPcm.size(), + testCase.lastPcm); + } + } +} + +TEST_CASE("Generated MPC2000XL SND corpus loads through the production loader", "[kaitai-snd][generated-corpus]") +{ + struct CorpusCase + { + std::string resourcePath; + std::string fileName; + ExpectedSound expected; + }; + + const auto cases = std::vector{ + { + "test/GeneratedMpc2000xl/Snd/mono_corpus_min.SND", + "mono_corpus_min.SND", + ExpectedSound{ + "mono_corpus_min", + true, + 8000, + 0, + -120, + 1, + 0, + 0, + 0, + false, + monoSamples({-32768, -1, 0, 32767}) + } + }, + { + "test/GeneratedMpc2000xl/Snd/mono_loop_mid.SND", + "mono_loop_mid.SND", + ExpectedSound{ + "mono_loop_mid", + true, + 44100, + 100, + 0, + 16, + 1, + 3, + 2, + true, + monoSamples({-12345, -17, 19, 12345}) + } + }, + { + "test/GeneratedMpc2000xl/Snd/stereo_span.SND", + "stereo_span.SND", + ExpectedSound{ + "stereo_span", + false, + 22050, + 100, + 0, + 16, + 0, + 5, + 0, + false, + stereoSamples({1000, 2000, 3000, 4000, 5000}, {-1000, -2000, -3000, -4000, -5000}) + } + }, + { + "test/GeneratedMpc2000xl/Snd/stereo_max.SND", + "stereo_max.SND", + ExpectedSound{ + "stereo_max", + false, + 44100, + 200, + 120, + 32, + 5, + 5, + 5, + true, + stereoSamples({32767, 23456, 12345, 1, -1}, {-32768, -23456, -12345, -1, 1}) + } + }, + { + "test/GeneratedMpc2000xl/Snd/name_len_sixteen.SND", + "name_len_sixteen.SND", + ExpectedSound{ + "name_len_sixteen", + false, + 32000, + 150, + -37, + 8, + 2, + 4, + 2, + true, + stereoSamples({3210, -4321, 123, -456}, {999, -999, 456, -123}) + } + } + }; + + for (const auto &testCase : cases) + { + SECTION(testCase.fileName) + { + const auto bytes = resourceBytes(testCase.resourcePath); + + withParsedSndBytes(bytes, [&](mpc2000snd_t &parsed) + { + REQUIRE(parsed.name() == expectedHeaderName(testCase.expected.name)); + REQUIRE(parsed.level() == static_cast(testCase.expected.level)); + REQUIRE(parsed.tune() == testCase.expected.tune); + REQUIRE(parsed.stereo() == !testCase.expected.mono); + REQUIRE(parsed.start() == static_cast(testCase.expected.start)); + REQUIRE(parsed.end() == static_cast(testCase.expected.end)); + REQUIRE(parsed.frame_count() == static_cast(testCase.expected.sampleData.size() / (testCase.expected.mono ? 1 : 2))); + REQUIRE(parsed.loop_frame_count() == static_cast(testCase.expected.end - testCase.expected.loopTo)); + REQUIRE(parsed.loop_enabled() == testCase.expected.loopEnabled); + REQUIRE(parsed.beat_count() == static_cast(testCase.expected.beatCount)); + REQUIRE(parsed.sample_rate() == static_cast(testCase.expected.sampleRate)); + REQUIRE(parsed.sample_data() != nullptr); + REQUIRE(parsed.sample_data()->size() == testCase.expected.sampleData.size()); + + for (std::size_t i = 0; i < parsed.sample_data()->size(); i++) + { + REQUIRE(parsed.sample_data()->at(i) == mean_normalized_float_to_short(testCase.expected.sampleData[i])); + } + }); + + mpc::Mpc mpc; + mpc::TestMpc::initializeTestMpc(mpc); + const auto loaded = loadWithSoundLoader(mpc, bytes, testCase.fileName); + requireSoundMatches(loaded, testCase.expected); + } + } +} + +TEST_CASE("SndIo reads mutated header semantics from Kaitai-written bytes", "[kaitai-snd]") +{ + auto bytes = resourceBytes("test/GeneratedMpc2000xl/Snd/mono_loop_mid.SND"); + + std::vector rewrittenBytes; + withParsedSndBytes(bytes, [&](mpc2000snd_t &parsed) + { + parsed.set_tune(120); + parsed.set_start(0); + parsed.set_end(3); + parsed.set_loop_frame_count(2); + parsed.set_loop_enabled(false); + parsed.set_beat_count(32); + + std::stringstream writeStream(std::ios::in | std::ios::out | std::ios::binary); + kaitai::kstream writeIo(&writeStream); + parsed._set_io(&writeIo); + parsed._check(); + parsed._write(); + + const auto written = writeStream.str(); + rewrittenBytes.assign(written.begin(), written.end()); + }); + + auto sound = std::make_shared(44100); + auto result = SndIo::loadBytes(rewrittenBytes, sound, "mono_loop_mid"); + REQUIRE(result); + REQUIRE(sound->getName() == "mono_loop_mid"); + REQUIRE(sound->isMono()); + REQUIRE(sound->getSampleRate() == 44100); + REQUIRE(sound->getSndLevel() == 100); + REQUIRE(sound->getTune() == 120); + REQUIRE(sound->getStart() == 0); + REQUIRE(sound->getEnd() == 3); + REQUIRE(sound->getLoopTo() == 1); + REQUIRE(!sound->isLoopEnabled()); + REQUIRE(sound->getBeatCount() == 32); + REQUIRE(sound->getSampleData()->size() == 4U); +} + +TEST_CASE("SoundLoader loads mutated SND header semantics from Kaitai-written bytes", "[kaitai-snd]") +{ + auto bytes = resourceBytes("test/GeneratedMpc2000xl/Snd/stereo_span.SND"); + + std::vector rewrittenBytes; + withParsedSndBytes(bytes, [&](mpc2000snd_t &parsed) + { + parsed.set_tune(-120); + parsed.set_start(1); + parsed.set_end(4); + parsed.set_loop_frame_count(1); + parsed.set_loop_enabled(true); + parsed.set_beat_count(1); + + std::stringstream writeStream(std::ios::in | std::ios::out | std::ios::binary); + kaitai::kstream writeIo(&writeStream); + parsed._set_io(&writeIo); + parsed._check(); + parsed._write(); + + const auto written = writeStream.str(); + rewrittenBytes.assign(written.begin(), written.end()); + }); + + mpc::Mpc mpc; + mpc::TestMpc::initializeTestMpc(mpc); + const auto loaded = loadWithSoundLoader(mpc, rewrittenBytes, "stereo_span.SND"); + + REQUIRE(loaded->getName() == "stereo_span"); + REQUIRE(!loaded->isMono()); + REQUIRE(loaded->getTune() == -120); + REQUIRE(loaded->getStart() == 1); + REQUIRE(loaded->getEnd() == 4); + REQUIRE(loaded->getLoopTo() == 3); + REQUIRE(loaded->isLoopEnabled()); + REQUIRE(loaded->getBeatCount() == 1); +} + +TEST_CASE("SndIo rejects too-short and unsupported SND data", "[kaitai-snd]") +{ + auto sound = std::make_shared(44100); + + auto tooShort = SndIo::loadBytes({static_cast(0x01)}, sound, "bad"); + REQUIRE_FALSE(tooShort); + REQUIRE(tooShort.error() == "SND file is too short"); + + auto unsupported = SndIo::loadBytes( + {static_cast(0x01), static_cast(0x03)}, sound, + "bad"); + REQUIRE_FALSE(unsupported); + REQUIRE(unsupported.error() == "Unsupported SND file format"); +} diff --git a/src/test/MidiFileTest.cpp b/src/test/MidiFileTest.cpp index 860f8a866..5e8f7a4ab 100644 --- a/src/test/MidiFileTest.cpp +++ b/src/test/MidiFileTest.cpp @@ -7,13 +7,11 @@ #include "sequencer/Track.hpp" #include "sequencer/NoteOnEvent.hpp" -#include "file/mid/MidiReader.hpp" -#include "file/mid/MidiWriter.hpp" +#include "file/kaitai/MidIo.hpp" #include "sequencer/SequencerStateManager.hpp" #include #include -#include #include CMRC_DECLARE(mpctest); @@ -21,8 +19,6 @@ CMRC_DECLARE(mpctest); using namespace mpc; using namespace mpc::sequencer; using namespace mpc::disk; -using namespace mpc::file::mid; - namespace { struct NoteSnapshot @@ -65,6 +61,25 @@ namespace } return result; } + + std::shared_ptr loadSequenceWithMidIo( + Mpc &mpc, + const std::vector &bytes, + const std::string &fileName) + { + const auto stem = fileName.substr(0, fileName.find_last_of('.')); + const auto sequenceOrError = + mpc::file::kaitai::MidIo::loadBytes(mpc, bytes, stem); + REQUIRE(sequenceOrError.has_value()); + mpc.getSequencer()->getStateManager()->drainQueue(); + return sequenceOrError.value(); + } + + std::vector saveSequenceWithMidIo( + const std::shared_ptr &sequence) + { + return mpc::file::kaitai::MidIo::saveBytes(sequence); + } } // namespace SCENARIO("A MidiFile can be written", "[file]") @@ -95,52 +110,39 @@ SCENARIO("A MidiFile can be written", "[file]") track0->acquireAndInsertEvent(eventData); stateManager->drainQueue(); - MidiWriter midiWriter(sequence.get()); - auto ostream = std::make_shared(); - midiWriter.writeToOStream(ostream); - sequence->init(1); stateManager->drainQueue(); track0->removeEvents(); stateManager->drainQueue(); REQUIRE(track0->getEvents().empty()); - auto istream = std::make_shared(ostream->str()); - MidiReader midiReader(istream, sequence); - midiReader.parseSequence(mpc); - stateManager->drainQueue(); + auto loaded = loadSequenceWithMidIo( + mpc, + saveSequenceWithMidIo(sequence), + "ROUNDTRIP.MID"); - REQUIRE(sequence->getTrack(0)->getEvents().size() == 1); + REQUIRE(loaded->getTrack(0)->getEvents().size() == 1); REQUIRE(std::dynamic_pointer_cast( - sequence->getTrack(0)->getEvents()[0])); + loaded->getTrack(0)->getEvents()[0])); REQUIRE(std::dynamic_pointer_cast( - sequence->getTrack(0)->getEvents()[0]) + loaded->getTrack(0)->getEvents()[0]) ->getNote() == 37); - REQUIRE(sequence->getTrack(0)->getBusType() == BusType::DRUM2); - REQUIRE(sequence->getTrack(0)->getDeviceIndex() == 7); - REQUIRE(sequence->getTrack(1)->getDeviceIndex() == 0); + REQUIRE(loaded->getTrack(0)->getBusType() == BusType::DRUM2); + REQUIRE(loaded->getTrack(0)->getDeviceIndex() == 7); + REQUIRE(loaded->getTrack(1)->getDeviceIndex() == 0); } } -TEST_CASE("MidiReader parses FRUTZLE.MID without crashing", +TEST_CASE("MidIo parses FRUTZLE.MID without crashing", "[file][midi-file]") { Mpc mpc; TestMpc::initializeTestMpc(mpc); - auto sequence = mpc.getSequencer()->getSequence(0); - auto stateManager = mpc.getSequencer()->getStateManager(); - sequence->init(1); - stateManager->drainQueue(); - auto fs = cmrc::mpctest::get_filesystem(); auto file = fs.open("test/MidiFile/FRUTZLE.MID"); - std::string bytes(file.begin(), file.end()); - auto stream = std::make_shared(bytes); - - MidiReader reader(stream, sequence); - REQUIRE_NOTHROW(reader.parseSequence(mpc)); - stateManager->drainQueue(); + std::vector bytes(file.begin(), file.end()); + auto sequence = loadSequenceWithMidIo(mpc, bytes, "FRUTZLE.MID"); int totalEvents = 0; for (int trackIndex = 0; trackIndex < 64; ++trackIndex) @@ -153,25 +155,16 @@ TEST_CASE("MidiReader parses FRUTZLE.MID without crashing", REQUIRE(sequence->getBarCount() == 4); } -TEST_CASE("MidiReader parses FRUTZLE.MID into temp sequence without crashing", +TEST_CASE("MidIo parses FRUTZLE.MID into temp sequence without crashing", "[file][midi-file]") { Mpc mpc; TestMpc::initializeTestMpc(mpc); - auto sequence = mpc.getSequencer()->getSequence(TempSequenceIndex); - auto stateManager = mpc.getSequencer()->getStateManager(); - sequence->init(1); - stateManager->drainQueue(); - auto fs = cmrc::mpctest::get_filesystem(); auto file = fs.open("test/MidiFile/FRUTZLE.MID"); - std::string bytes(file.begin(), file.end()); - auto stream = std::make_shared(bytes); - - MidiReader reader(stream, sequence); - REQUIRE_NOTHROW(reader.parseSequence(mpc)); - stateManager->drainQueue(); + std::vector bytes(file.begin(), file.end()); + auto sequence = loadSequenceWithMidIo(mpc, bytes, "FRUTZLE.MID"); int totalEvents = 0; for (int trackIndex = 0; trackIndex < 64; ++trackIndex) @@ -183,115 +176,6 @@ TEST_CASE("MidiReader parses FRUTZLE.MID into temp sequence without crashing", REQUIRE(totalEvents > 0); } -TEST_CASE("Busy 8-track midi roundtrip preserves note events", - "[file][midi-file]") -{ - Mpc mpc; - TestMpc::initializeTestMpc(mpc); - - auto sequencer = mpc.getSequencer(); - auto sequence = sequencer->getSequence(0); - auto stateManager = sequencer->getStateManager(); - - sequence->init(1); - stateManager->drainQueue(); - - for (int trackIndex = 0; trackIndex < 8; ++trackIndex) - { - auto track = sequence->getTrack(trackIndex); - track->setUsedIfCurrentlyUnused(); - track->setDeviceIndex((trackIndex % 4) + 1); - - for (int eventIndex = 0; eventIndex < 32; ++eventIndex) - { - EventData eventData; - eventData.type = EventType::NoteOn; - eventData.tick = - (eventIndex * 11 + trackIndex * 17) % - static_cast(sequence->getLastTick()); - eventData.noteNumber = NoteNumber(35 + eventIndex); - eventData.velocity = Velocity(20 + ((trackIndex * 13 + - eventIndex * 5) % - 107)); - eventData.duration = Duration(1 + ((trackIndex * 7 + - eventIndex * 13) % - 95)); - eventData.noteVariationType = - NoteVariationType((trackIndex + eventIndex) % 4); - - switch (static_cast(eventData.noteVariationType)) - { - case 0: - eventData.noteVariationValue = - NoteVariationValue((eventIndex * 3) % 125); - break; - case 1: - case 2: - eventData.noteVariationValue = - NoteVariationValue((trackIndex * 11 + eventIndex) % - 101); - break; - default: - eventData.noteVariationValue = - NoteVariationValue((trackIndex * 9 + eventIndex * 4) % - 101); - break; - } - - track->acquireAndInsertEvent(eventData); - } - } - - stateManager->drainQueue(); - - std::array, 8> expectedPerTrack; - std::array expectedDeviceIndices{}; - for (int trackIndex = 0; trackIndex < 8; ++trackIndex) - { - auto track = sequence->getTrack(trackIndex); - expectedPerTrack[trackIndex] = collectNoteSnapshots(track); - expectedDeviceIndices[trackIndex] = track->getDeviceIndex(); - REQUIRE_FALSE(expectedPerTrack[trackIndex].empty()); - } - - MidiWriter writer(sequence.get()); - auto output = std::make_shared(); - writer.writeToOStream(output); - - sequence->init(1); - stateManager->drainQueue(); - - auto input = std::make_shared(output->str()); - MidiReader reader(input, sequence); - REQUIRE_NOTHROW(reader.parseSequence(mpc)); - stateManager->drainQueue(); - - for (int trackIndex = 0; trackIndex < 8; ++trackIndex) - { - auto track = sequence->getTrack(trackIndex); - const auto actual = collectNoteSnapshots(track); - - REQUIRE(track->getDeviceIndex() == expectedDeviceIndices[trackIndex]); - REQUIRE(actual.size() == expectedPerTrack[trackIndex].size()); - - for (size_t i = 0; i < actual.size(); ++i) - { - CAPTURE(trackIndex, i); - CAPTURE(actual[i].tick, expectedPerTrack[trackIndex][i].tick); - CAPTURE(actual[i].note, expectedPerTrack[trackIndex][i].note); - CAPTURE(actual[i].velocity, - expectedPerTrack[trackIndex][i].velocity); - CAPTURE(actual[i].duration, - expectedPerTrack[trackIndex][i].duration); - CAPTURE(actual[i].variationType, - expectedPerTrack[trackIndex][i].variationType); - CAPTURE(actual[i].variationValue, - expectedPerTrack[trackIndex][i].variationValue); - REQUIRE(actual[i] == expectedPerTrack[trackIndex][i]); - } - } -} - TEST_CASE("Cubase MID files load with valid bar count and name lengths", "[file][midi-file]") { @@ -306,19 +190,10 @@ TEST_CASE("Cubase MID files load with valid bar count and name lengths", Mpc mpc; TestMpc::initializeTestMpc(mpc); - auto sequence = mpc.getSequencer()->getSequence(TempSequenceIndex); - auto stateManager = mpc.getSequencer()->getStateManager(); - sequence->init(1); - stateManager->drainQueue(); - auto fs = cmrc::mpctest::get_filesystem(); auto file = fs.open("test/MidiFile/" + fileName); - std::string bytes(file.begin(), file.end()); - auto stream = std::make_shared(bytes); - - MidiReader reader(stream, sequence); - REQUIRE_NOTHROW(reader.parseSequence(mpc)); - stateManager->drainQueue(); + std::vector bytes(file.begin(), file.end()); + auto sequence = loadSequenceWithMidIo(mpc, bytes, fileName); REQUIRE(sequence->getBarCount() > 0); REQUIRE(sequence->getName().size() <= @@ -338,19 +213,10 @@ TEST_CASE("HWIF316.MID loads with 16 bars", "[file][midi-file]") Mpc mpc; TestMpc::initializeTestMpc(mpc); - auto sequence = mpc.getSequencer()->getSequence(TempSequenceIndex); - auto stateManager = mpc.getSequencer()->getStateManager(); - sequence->init(1); - stateManager->drainQueue(); - auto fs = cmrc::mpctest::get_filesystem(); auto file = fs.open("test/MidiFile/HWIF316.MID"); - std::string bytes(file.begin(), file.end()); - auto stream = std::make_shared(bytes); - - MidiReader reader(stream, sequence); - REQUIRE_NOTHROW(reader.parseSequence(mpc)); - stateManager->drainQueue(); + std::vector bytes(file.begin(), file.end()); + auto sequence = loadSequenceWithMidIo(mpc, bytes, "HWIF316.MID"); REQUIRE(sequence->getBarCount() == 16); } @@ -361,19 +227,10 @@ TEST_CASE("Real 2KXL SEQ.MID preserves MPC note variation semantics", Mpc mpc; TestMpc::initializeTestMpc(mpc); - auto sequence = mpc.getSequencer()->getSequence(TempSequenceIndex); - auto stateManager = mpc.getSequencer()->getStateManager(); - sequence->init(1); - stateManager->drainQueue(); - auto fs = cmrc::mpctest::get_filesystem(); auto file = fs.open("test/MidiFile/SEQ.MID"); - std::string bytes(file.begin(), file.end()); - auto stream = std::make_shared(bytes); - - MidiReader reader(stream, sequence); - REQUIRE_NOTHROW(reader.parseSequence(mpc)); - stateManager->drainQueue(); + std::vector bytes(file.begin(), file.end()); + auto sequence = loadSequenceWithMidIo(mpc, bytes, "SEQ.MID"); REQUIRE(sequence->getBarCount() == 1); @@ -414,19 +271,10 @@ TEST_CASE("FRUTZLE.MID roundtrip keeps non-empty sequence", "[file][midi-file]") Mpc mpc; TestMpc::initializeTestMpc(mpc); - auto sequence = mpc.getSequencer()->getSequence(TempSequenceIndex); - auto stateManager = mpc.getSequencer()->getStateManager(); - sequence->init(1); - stateManager->drainQueue(); - auto fs = cmrc::mpctest::get_filesystem(); auto file = fs.open("test/MidiFile/FRUTZLE.MID"); - std::string bytes(file.begin(), file.end()); - auto initialInput = std::make_shared(bytes); - - MidiReader initialReader(initialInput, sequence); - REQUIRE_NOTHROW(initialReader.parseSequence(mpc)); - stateManager->drainQueue(); + std::vector bytes(file.begin(), file.end()); + auto sequence = loadSequenceWithMidIo(mpc, bytes, "FRUTZLE.MID"); int eventsAfterFirstLoad = 0; for (int trackIndex = 0; trackIndex < 64; ++trackIndex) @@ -437,25 +285,18 @@ TEST_CASE("FRUTZLE.MID roundtrip keeps non-empty sequence", "[file][midi-file]") REQUIRE(eventsAfterFirstLoad > 0); REQUIRE(sequence->getBarCount() == 4); - MidiWriter writer(sequence.get()); - auto output = std::make_shared(); - writer.writeToOStream(output); - - sequence->init(1); - stateManager->drainQueue(); - - auto secondInput = std::make_shared(output->str()); - MidiReader secondReader(secondInput, sequence); - REQUIRE_NOTHROW(secondReader.parseSequence(mpc)); - stateManager->drainQueue(); + auto reloaded = loadSequenceWithMidIo( + mpc, + saveSequenceWithMidIo(sequence), + "FRUTZLE.MID"); int eventsAfterRoundtrip = 0; for (int trackIndex = 0; trackIndex < 64; ++trackIndex) { eventsAfterRoundtrip += static_cast( - sequence->getTrack(trackIndex)->getEvents().size()); + reloaded->getTrack(trackIndex)->getEvents().size()); } REQUIRE(eventsAfterRoundtrip > 0); - REQUIRE(sequence->getBarCount() == 4); + REQUIRE(reloaded->getBarCount() == 4); } diff --git a/src/test/Mpc60SetPreviewTest.cpp b/src/test/Mpc60SetPreviewTest.cpp new file mode 100644 index 000000000..64f46f231 --- /dev/null +++ b/src/test/Mpc60SetPreviewTest.cpp @@ -0,0 +1,96 @@ +#include + +#include "file/kaitai/Mpc60SetPreview.hpp" + +#include + +#include +#include + +CMRC_DECLARE(mpctest); + +namespace +{ + std::vector readResource(const std::string &path) + { + const auto fs = cmrc::mpctest::get_filesystem(); + const auto file = fs.open(path); + return std::vector(file.begin(), file.end()); + } +} + +TEST_CASE("MPC60 SET preview maps MPC60 pads to directory entries", + "[kaitai-set][real-mpc60]") +{ + using mpc::file::kaitai::Mpc60SetPreviewLoader; + + const auto rockPreview = Mpc60SetPreviewLoader::loadPreview( + readResource("test/RealMpc60/Set/ROCK.SET")); + const auto studioPreview = Mpc60SetPreviewLoader::loadPreview( + readResource("test/RealMpc60/Set/STUDIO.SET")); + const auto uk8Preview = Mpc60SetPreviewLoader::loadPreview( + readResource("test/RealMpc60/Set/UK-8.SET")); + + REQUIRE(rockPreview.totalNumberOfSampleWords == 510936U); + REQUIRE(studioPreview.totalNumberOfSampleWords == 519560U); + REQUIRE(uk8Preview.totalNumberOfSampleWords == 517520U); + + REQUIRE(rockPreview.useMasterMixData); + REQUIRE(studioPreview.useMasterMixData); + REQUIRE(uk8Preview.useMasterMixData); + + REQUIRE(rockPreview.soundDirectoryEntries.size() == 34U); + REQUIRE(studioPreview.soundDirectoryEntries.size() == 34U); + REQUIRE(uk8Preview.soundDirectoryEntries.size() == 34U); + REQUIRE(rockPreview.soundDirectoryEntryIndexByMpc60Pad.size() == 34U); + REQUIRE(studioPreview.soundDirectoryEntryIndexByMpc60Pad.size() == 34U); + REQUIRE(uk8Preview.soundDirectoryEntryIndexByMpc60Pad.size() == 34U); + + REQUIRE(rockPreview.soundDirectoryEntryIndexByMpc60Pad[0] == 19); + REQUIRE(rockPreview.soundDirectoryEntryIndexByMpc60Pad[1] == 20); + REQUIRE(rockPreview.soundDirectoryEntryIndexByMpc60Pad[2] == 21); + REQUIRE(rockPreview.soundDirectoryEntryIndexByMpc60Pad[10] == 16); + + REQUIRE(studioPreview.soundDirectoryEntryIndexByMpc60Pad[0] == 19); + REQUIRE(studioPreview.soundDirectoryEntryIndexByMpc60Pad[1] == 20); + REQUIRE(studioPreview.soundDirectoryEntryIndexByMpc60Pad[2] == 21); + REQUIRE(studioPreview.soundDirectoryEntryIndexByMpc60Pad[10] == 23); + + REQUIRE(uk8Preview.soundDirectoryEntryIndexByMpc60Pad[0] == 0); + REQUIRE(uk8Preview.soundDirectoryEntryIndexByMpc60Pad[18] == 255); + REQUIRE(uk8Preview.soundDirectoryEntryIndexByMpc60Pad[25] == 255); + REQUIRE(uk8Preview.soundDirectoryEntryIndexByMpc60Pad[33] == 20); + + REQUIRE(rockPreview.assignedSoundAtMpc60Pad(0)->name == "HAT2CLSD"); + REQUIRE(rockPreview.assignedSoundAtMpc60Pad(1)->name == "HAT2MED"); + REQUIRE(rockPreview.assignedSoundAtMpc60Pad(2)->name == "HAT2OPN"); + REQUIRE(rockPreview.assignedSoundAtMpc60Pad(10)->name == "RIDE4B"); + + REQUIRE(studioPreview.assignedSoundAtMpc60Pad(0)->name == "HAT1CLSD"); + REQUIRE(studioPreview.assignedSoundAtMpc60Pad(1)->name == "HAT1MED"); + REQUIRE(studioPreview.assignedSoundAtMpc60Pad(2)->name == "HAT1OPN"); + REQUIRE(studioPreview.assignedSoundAtMpc60Pad(10)->name == "RIDE_#1"); + + REQUIRE(uk8Preview.assignedSoundAtMpc60Pad(0)->name == "S7_HH_CL"); + REQUIRE(uk8Preview.assignedSoundAtMpc60Pad(10)->name == "S7_RIDE1"); + REQUIRE(uk8Preview.assignedSoundAtMpc60Pad(18) == nullptr); + REQUIRE(uk8Preview.assignedSoundAtMpc60Pad(25) == nullptr); + REQUIRE(uk8Preview.assignedSoundAtMpc60Pad(33)->name == "SCRATCH4"); + + REQUIRE(rockPreview.assignedSoundAtMpc60Pad(0)->isHihat); + REQUIRE(rockPreview.assignedSoundAtMpc60Pad(1)->isHihat); + REQUIRE(rockPreview.assignedSoundAtMpc60Pad(2)->isHihat); + REQUIRE_FALSE(rockPreview.assignedSoundAtMpc60Pad(16)->isHihat); + + REQUIRE(mpc::file::kaitai::Mpc60SetPreview::mpc60PadName(0) == "hiht_clsd"); + REQUIRE(mpc::file::kaitai::Mpc60SetPreview::mpc60PadName(10) == "rid1"); + REQUIRE(mpc::file::kaitai::Mpc60SetPreview::mpc60PadName(33) == "dr16"); + + REQUIRE(mpc::file::kaitai::Mpc60SetPreview::mpc60SourceSlotLabel(0) == "A01"); + REQUIRE(mpc::file::kaitai::Mpc60SetPreview::mpc60SourceSlotLabel(1) == "A01"); + REQUIRE(mpc::file::kaitai::Mpc60SetPreview::mpc60SourceSlotLabel(2) == "A01"); + REQUIRE(mpc::file::kaitai::Mpc60SetPreview::mpc60SourceSlotLabel(3) == "A02"); + REQUIRE(mpc::file::kaitai::Mpc60SetPreview::mpc60SourceSlotLabel(17) == "A16"); + REQUIRE(mpc::file::kaitai::Mpc60SetPreview::mpc60SourceSlotLabel(18) == "B01"); + REQUIRE(mpc::file::kaitai::Mpc60SetPreview::mpc60SourceSlotLabel(33) == "B16"); +} diff --git a/src/test/Mpc60SetSoundLoaderTest.cpp b/src/test/Mpc60SetSoundLoaderTest.cpp new file mode 100644 index 000000000..341f666ad --- /dev/null +++ b/src/test/Mpc60SetSoundLoaderTest.cpp @@ -0,0 +1,126 @@ +#include + +#include "SampleOps.hpp" +#include "file/kaitai/Mpc60SetSoundLoader.hpp" +#include "sampler/Sound.hpp" + +#include + +#include +#include +#include +#include + +CMRC_DECLARE(mpctest); + +namespace +{ + std::vector readResource(const std::string &path) + { + const auto fs = cmrc::mpctest::get_filesystem(); + const auto file = fs.open(path); + return std::vector(file.begin(), file.end()); + } + + uint32_t readU4le(const std::vector &bytes, const size_t offset) + { + return static_cast( + static_cast(bytes[offset]) | + (static_cast(bytes[offset + 1]) << 8U) | + (static_cast(bytes[offset + 2]) << 16U) | + (static_cast(bytes[offset + 3]) << 24U)); + } + + std::vector readMpc2000xlSndPcm(const std::string &path) + { + const auto bytes = readResource(path); + REQUIRE(bytes.size() >= 42); + REQUIRE(static_cast(bytes[0]) == 0x01); + REQUIRE(static_cast(bytes[1]) == 0x04); + + const auto stereo = (static_cast(bytes[21]) & 1U) != 0; + REQUIRE_FALSE(stereo); + + const auto frameCount = readU4le(bytes, 30); + REQUIRE(bytes.size() == 42U + frameCount * 2U); + + std::vector result; + result.reserve(frameCount); + + for (size_t offset = 42; offset < bytes.size(); offset += 2) + { + const auto value = static_cast( + static_cast(bytes[offset]) | + (static_cast(bytes[offset + 1]) << 8U)); + result.push_back(static_cast(value)); + } + + return result; + } + + void requireMatchesMpc2000xlSnd(const mpc::sampler::Sound &sound, + const std::string &referencePath) + { + const auto expected = readMpc2000xlSndPcm(referencePath); + const auto sampleData = sound.getSampleData(); + + REQUIRE(sampleData->size() == expected.size()); + + for (size_t i = 0; i < expected.size(); ++i) + { + REQUIRE(mpc::sampleops::mean_normalized_float_to_short( + sampleData->at(i)) == expected[i]); + } + } +} // namespace + +TEST_CASE("MPC60 SET single-sound loader decodes the default assigned sound", + "[kaitai-set][real-mpc60]") +{ + auto sound = std::make_shared(40000); + + const auto result = + mpc::file::kaitai::Mpc60SetSoundLoader::loadAssignedSoundAtMpc60Pad( + readResource("test/RealMpc60/Set/ROCK.SET"), 0, sound); + + REQUIRE(result.has_value()); + REQUIRE(sound->getName() == "HAT2CLSD"); + REQUIRE(sound->isMono()); + REQUIRE(sound->getSampleRate() == 40000); + REQUIRE(sound->getFrameCount() == 16000); + REQUIRE(sound->getStart() == 0); + REQUIRE(sound->getEnd() == 15999); + REQUIRE_FALSE(sound->isLoopEnabled()); + + const auto sampleData = sound->getSampleData(); + REQUIRE(sampleData->size() == 16000U); + REQUIRE(std::any_of(sampleData->begin(), sampleData->end(), + [](const float sample) + { + return sample < 0.0f; + })); + REQUIRE(std::any_of(sampleData->begin(), sampleData->end(), + [](const float sample) + { + return sample > 0.0f; + })); + requireMatchesMpc2000xlSnd( + *sound, "test/RealMpc60/SetImportedSnd/Rock/HAT2CLSD.SND"); +} + +TEST_CASE("MPC60 SET single-sound loader decodes a selected directory entry", + "[kaitai-set][real-mpc60]") +{ + auto sound = std::make_shared(40000); + + const auto result = + mpc::file::kaitai::Mpc60SetSoundLoader::loadSoundDirectoryEntry( + readResource("test/RealMpc60/Set/ROCK.SET"), 17, sound); + + REQUIRE(result.has_value()); + REQUIRE(sound->getName() == "BIG_CLAP"); + REQUIRE(sound->getFrameCount() == 24000); + REQUIRE(sound->getEnd() == 23999); + requireMatchesMpc2000xlSnd( + *sound, "test/RealMpc60/SetImportedSnd/Rock/BIG_CLAP.SND"); +} diff --git a/src/test/Mpc60SetWholeLoadTest.cpp b/src/test/Mpc60SetWholeLoadTest.cpp new file mode 100644 index 000000000..09eece467 --- /dev/null +++ b/src/test/Mpc60SetWholeLoadTest.cpp @@ -0,0 +1,286 @@ +#include + +#include "Mpc.hpp" +#include "TestMpc.hpp" +#include "StrUtil.hpp" +#include "disk/AbstractDisk.hpp" +#include "disk/MpcFile.hpp" +#include "engine/EngineHost.hpp" +#include "lcdgui/LayeredScreen.hpp" +#include "lcdgui/ScreenId.hpp" +#include "lcdgui/screens/LoadScreen.hpp" +#include "lcdgui/screens/window/LoadASetScreen.hpp" +#include "sampler/SoundGenerationMode.hpp" +#include "sampler/Sampler.hpp" +#include "sequencer/Bus.hpp" +#include "sequencer/Sequencer.hpp" +#include "sequencer/Track.hpp" + +#include +#include +#include +#include + +CMRC_DECLARE(mpctest); + +namespace +{ + void prepareSetFile(mpc::Mpc &mpc, const std::string &setFileName) + { + auto fs = cmrc::mpctest::get_filesystem(); + auto file = fs.open("test/RealMpc60/Set/" + setFileName); + std::vector data(file.begin(), file.end()); + auto newFile = mpc.getDisk()->newFile(setFileName); + newFile->setFileData(data); + mpc.getDisk()->initFiles(); + } + + void selectSetFile(mpc::Mpc &mpc, const std::string &setFileName) + { + auto layeredScreen = mpc.getLayeredScreen(); + layeredScreen->openScreen("load"); + + const auto loadScreen = + mpc.screens->get(); + const auto fileNames = mpc.getDisk()->getFileNames(); + const auto setFileIt = + std::find_if(fileNames.begin(), fileNames.end(), + [&setFileName](const std::string &fileName) + { + return mpc::StrUtil::eqIgnoreCase(fileName, + setFileName); + }); + REQUIRE(setFileIt != fileNames.end()); + loadScreen->setFileLoad( + static_cast(std::distance(fileNames.begin(), setFileIt))); + loadScreen->function(5); + REQUIRE(layeredScreen->getCurrentScreenName() == "load-a-set"); + } + + void waitForLoadScreen(mpc::Mpc &mpc) + { + constexpr auto timeout = std::chrono::seconds(5); + const auto start = std::chrono::steady_clock::now(); + + while (mpc.getLayeredScreen()->getCurrentScreenName() != "load") + { + REQUIRE(std::chrono::steady_clock::now() - start < timeout); + mpc.getEngineHost()->prepareProcessBlock(512); + mpc.getLayeredScreen()->timerCallback(); + std::this_thread::sleep_for(std::chrono::milliseconds(10)); + } + } + + void requireSelectedDrumProgramIsUsed(mpc::Mpc &mpc, + const mpc::ProgramIndex expected) + { + const auto track = mpc.getSequencer()->getSelectedTrack(); + REQUIRE(mpc::sequencer::isDrumBusType(track->getBusType())); + + const auto drumBus = mpc.getSequencer()->getDrumBus(track->getBusType()); + REQUIRE(drumBus->getProgramIndex() == expected); + REQUIRE(mpc.getSampler()->getProgram(expected.get())->isUsed()); + } + + void requireImportedHihatVelocitySwitch( + const std::shared_ptr &program, + const mpc::DrumNoteNumber closedHatNote, + const mpc::DrumNoteNumber mediumHatNote, + const mpc::DrumNoteNumber openHatNote) + { + REQUIRE(program != nullptr); + const auto noteParameters = program->getNoteParameters(closedHatNote); + REQUIRE(noteParameters->getSoundGenerationMode() == + mpc::sampler::SoundGenerationMode::VelocitySwitch); + REQUIRE(noteParameters->getVelocityRangeLower() == 14); + REQUIRE(noteParameters->getVelocityRangeUpper() == 42); + REQUIRE(noteParameters->getOptionalNoteA() == mediumHatNote); + REQUIRE(noteParameters->getOptionalNoteB() == openHatNote); + } +} + +TEST_CASE("MPC60 SET CLEAR load imports sounds and assigns converted notes", + "[kaitai-set][load-set]") +{ + mpc::Mpc mpc; + mpc::TestMpc::initializeTestMpcWithoutMidiServices(mpc); + mpc.getEngineHost()->prepareProcessBlock(512); + prepareSetFile(mpc, "ROCK.SET"); + + auto existing = mpc.getSampler()->addSound(); + REQUIRE(existing != nullptr); + existing->setName("dummy"); + + selectSetFile(mpc, "ROCK.SET"); + + const auto setScreen = + mpc.screens->get(); + const auto hiHatNote = setScreen->getConversionTargetNote(0); + const auto hiHatMediumNote = setScreen->getConversionTargetNote(1); + const auto hiHatOpenNote = setScreen->getConversionTargetNote(2); + const auto rideNote = setScreen->getConversionTargetNote(10); + + mpc.getLayeredScreen()->getCurrentScreen()->function(4); + REQUIRE(mpc.getLayeredScreen()->getCurrentScreenName() == "conversion-table"); + mpc.getLayeredScreen()->getCurrentScreen()->function(4); + REQUIRE(mpc.getLayeredScreen()->getCurrentScreenName() == "load-a-set-replace-add"); + mpc.getLayeredScreen()->getCurrentScreen()->function(2); + REQUIRE(mpc.getLayeredScreen()->getCurrentScreenName() == "popup"); + waitForLoadScreen(mpc); + + REQUIRE(mpc.getLayeredScreen()->getCurrentScreenName() == "load"); + REQUIRE(mpc.getSampler()->getProgramCount() == 1); + REQUIRE(mpc.getSampler()->getSoundCount() == 34); + REQUIRE(mpc.getSampler()->getProgram(0)->getName() == "ROCK"); + REQUIRE(mpc.getSampler()->getSoundName(19) == "HAT2CLSD"); + REQUIRE(mpc.getSampler()->getSoundName(16) == "RIDE4B"); + REQUIRE(mpc.getSampler()->getProgram(0)->getNoteParameters(hiHatNote)->getSoundIndex() == 19); + REQUIRE(mpc.getSampler()->getProgram(0)->getNoteParameters(rideNote)->getSoundIndex() == 16); + requireImportedHihatVelocitySwitch( + mpc.getSampler()->getProgram(0), hiHatNote, hiHatMediumNote, + hiHatOpenNote); + requireSelectedDrumProgramIsUsed(mpc, mpc::ProgramIndex(0)); +} + +TEST_CASE("MPC60 SET default conversion table matches MPC2000XL", + "[kaitai-set][load-set]") +{ + mpc::Mpc mpc; + mpc::TestMpc::initializeTestMpcWithoutMidiServices(mpc); + prepareSetFile(mpc, "ROCK.SET"); + selectSetFile(mpc, "ROCK.SET"); + + const auto setScreen = + mpc.screens->get(); + + const std::vector expectedNotes{ + 42, 82, 46, 38, 37, 36, 48, 47, 45, 43, 51, 53, 49, 55, 69, 54, 56, + 39, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 70, 71, 72, 73}; + + REQUIRE(expectedNotes.size() == 34); + + for (size_t i = 0; i < expectedNotes.size(); ++i) + { + REQUIRE(static_cast( + setScreen->getConversionTargetNote(static_cast(i))) == + expectedNotes[i]); + } +} + +TEST_CASE("MPC60 SET LOAD adds imported program and offsets sound indices", + "[kaitai-set][load-set]") +{ + mpc::Mpc mpc; + mpc::TestMpc::initializeTestMpcWithoutMidiServices(mpc); + mpc.getEngineHost()->prepareProcessBlock(512); + prepareSetFile(mpc, "ROCK.SET"); + + auto existing = mpc.getSampler()->addSound(); + REQUIRE(existing != nullptr); + existing->setName("dummy"); + + selectSetFile(mpc, "ROCK.SET"); + + const auto setScreen = + mpc.screens->get(); + const auto hiHatNote = setScreen->getConversionTargetNote(0); + const auto hiHatMediumNote = setScreen->getConversionTargetNote(1); + const auto hiHatOpenNote = setScreen->getConversionTargetNote(2); + + mpc.getLayeredScreen()->getCurrentScreen()->function(4); + mpc.getLayeredScreen()->getCurrentScreen()->function(4); + mpc.getLayeredScreen()->getCurrentScreen()->function(4); + REQUIRE(mpc.getLayeredScreen()->getCurrentScreenName() == "popup"); + waitForLoadScreen(mpc); + + REQUIRE(mpc.getLayeredScreen()->getCurrentScreenName() == "load"); + REQUIRE(mpc.getSampler()->getProgramCount() == 2); + REQUIRE(mpc.getSampler()->getSoundCount() == 35); + REQUIRE(mpc.getSampler()->getSoundName(0) == "dummy"); + REQUIRE(mpc.getSampler()->getProgram(1)->getName() == "ROCK"); + REQUIRE(mpc.getSampler()->getProgram(1)->getNoteParameters(hiHatNote)->getSoundIndex() == 20); + requireImportedHihatVelocitySwitch( + mpc.getSampler()->getProgram(1), hiHatNote, hiHatMediumNote, + hiHatOpenNote); + requireSelectedDrumProgramIsUsed(mpc, mpc::ProgramIndex(1)); +} + +TEST_CASE("MPC60 STUDIO SET CLEAR load imports sounds and assigns converted notes", + "[kaitai-set][load-set]") +{ + mpc::Mpc mpc; + mpc::TestMpc::initializeTestMpcWithoutMidiServices(mpc); + mpc.getEngineHost()->prepareProcessBlock(512); + prepareSetFile(mpc, "STUDIO.SET"); + + selectSetFile(mpc, "STUDIO.SET"); + + const auto setScreen = + mpc.screens->get(); + const auto hiHatNote = setScreen->getConversionTargetNote(0); + const auto hiHatMediumNote = setScreen->getConversionTargetNote(1); + const auto hiHatOpenNote = setScreen->getConversionTargetNote(2); + const auto rideNote = setScreen->getConversionTargetNote(10); + + mpc.getLayeredScreen()->getCurrentScreen()->function(4); + REQUIRE(mpc.getLayeredScreen()->getCurrentScreenName() == "conversion-table"); + mpc.getLayeredScreen()->getCurrentScreen()->function(4); + REQUIRE(mpc.getLayeredScreen()->getCurrentScreenName() == "load-a-set-replace-add"); + mpc.getLayeredScreen()->getCurrentScreen()->function(2); + REQUIRE(mpc.getLayeredScreen()->getCurrentScreenName() == "popup"); + waitForLoadScreen(mpc); + + REQUIRE(mpc.getLayeredScreen()->getCurrentScreenName() == "load"); + REQUIRE(mpc.getSampler()->getProgramCount() == 1); + REQUIRE(mpc.getSampler()->getSoundCount() == 34); + REQUIRE(mpc.getSampler()->getProgram(0)->getName() == "STUDIO"); + REQUIRE(mpc.getSampler()->getSoundName(19) == "HAT1CLSD"); + REQUIRE(mpc.getSampler()->getSoundName(23) == "RIDE_#1"); + REQUIRE(mpc.getSampler()->getProgram(0)->getNoteParameters(hiHatNote)->getSoundIndex() == 19); + REQUIRE(mpc.getSampler()->getProgram(0)->getNoteParameters(rideNote)->getSoundIndex() == 23); + requireImportedHihatVelocitySwitch( + mpc.getSampler()->getProgram(0), hiHatNote, hiHatMediumNote, + hiHatOpenNote); + requireSelectedDrumProgramIsUsed(mpc, mpc::ProgramIndex(0)); +} + +TEST_CASE("MPC60 UK-8 SET load skips unassigned source pads and blank entries", + "[kaitai-set][load-set]") +{ + mpc::Mpc mpc; + mpc::TestMpc::initializeTestMpcWithoutMidiServices(mpc); + mpc.getEngineHost()->prepareProcessBlock(512); + prepareSetFile(mpc, "UK-8.SET"); + + selectSetFile(mpc, "UK-8.SET"); + + const auto setScreen = + mpc.screens->get(); + const auto hiHatNote = setScreen->getConversionTargetNote(0); + const auto hiHatMediumNote = setScreen->getConversionTargetNote(1); + const auto hiHatOpenNote = setScreen->getConversionTargetNote(2); + const auto unassignedNote = setScreen->getConversionTargetNote(18); + const auto scratchNote = setScreen->getConversionTargetNote(33); + + mpc.getLayeredScreen()->getCurrentScreen()->function(4); + REQUIRE(mpc.getLayeredScreen()->getCurrentScreenName() == "conversion-table"); + mpc.getLayeredScreen()->getCurrentScreen()->function(4); + REQUIRE(mpc.getLayeredScreen()->getCurrentScreenName() == "load-a-set-replace-add"); + mpc.getLayeredScreen()->getCurrentScreen()->function(2); + REQUIRE(mpc.getLayeredScreen()->getCurrentScreenName() == "popup"); + waitForLoadScreen(mpc); + + REQUIRE(mpc.getLayeredScreen()->getCurrentScreenName() == "load"); + REQUIRE(mpc.getSampler()->getProgramCount() == 1); + REQUIRE(mpc.getSampler()->getSoundCount() == 26); + REQUIRE(mpc.getSampler()->getProgram(0)->getName() == "UK-8"); + REQUIRE(mpc.getSampler()->getSoundName(0) == "S7_HH_CL"); + REQUIRE(mpc.getSampler()->getSoundName(20) == "SCRATCH4"); + REQUIRE(mpc.getSampler()->getProgram(0)->getNoteParameters(hiHatNote)->getSoundIndex() == 0); + REQUIRE(mpc.getSampler()->getProgram(0)->getNoteParameters(unassignedNote)->getSoundIndex() == -1); + REQUIRE(mpc.getSampler()->getProgram(0)->getNoteParameters(scratchNote)->getSoundIndex() == 20); + requireImportedHihatVelocitySwitch( + mpc.getSampler()->getProgram(0), hiHatNote, hiHatMediumNote, + hiHatOpenNote); + requireSelectedDrumProgramIsUsed(mpc, mpc::ProgramIndex(0)); +} diff --git a/src/test/SamplerTest.cpp b/src/test/SamplerTest.cpp index 33a19e82d..ebb5614de 100644 --- a/src/test/SamplerTest.cpp +++ b/src/test/SamplerTest.cpp @@ -13,6 +13,7 @@ #include "engine/control/CompoundControl.hpp" #include "lcdgui/ScreenComponent.hpp" #include "lcdgui/screens/dialog/MetronomeSoundScreen.hpp" +#include "performance/PerformanceManager.hpp" #include "disk/SoundLoader.hpp" #include @@ -393,6 +394,47 @@ TEST_CASE("Sort does not corrupt note parameter sound indices", "[sampler]") } } +TEST_CASE("Assign screen parameter follows assigned note and edits that note", "[sampler]") +{ + Mpc mpc; + TestMpc::initializeTestMpc(mpc); + + auto sampler = mpc.getSampler(); + auto program = sampler->getProgram(0); + REQUIRE(program != nullptr); + + program->getSlider()->setAssignNote(mpc::DrumNoteNumber(42)); + program->getSlider()->setParameter(mpc::NoteVariationTypeTune); + program->getNoteParameters(42)->setSliderParameterNumber(mpc::NoteVariationTypeFilter); + program->getNoteParameters(43)->setSliderParameterNumber(mpc::NoteVariationTypeAttack); + mpc.getPerformanceManager().lock()->drainQueue(); + + mpc.getLayeredScreen()->openScreenById(ScreenId::AssignScreen); + auto controls = mpc.getScreen(); + REQUIRE(controls != nullptr); + + auto parameterField = [&] + { + return controls->findChild("parameter"); + }; + + REQUIRE(parameterField() != nullptr); + REQUIRE(parameterField()->getText() == "FILTER"); + + REQUIRE(mpc.getLayeredScreen()->setFocus("assignnote")); + controls->turnWheel(1); + REQUIRE(parameterField()->getText() == "ATTACK"); + REQUIRE(program->getSlider()->getParameter() == mpc::NoteVariationTypeAttack); + + REQUIRE(mpc.getLayeredScreen()->setFocus("parameter")); + controls->turnWheel(1); + REQUIRE(parameterField()->getText() == "FILTER"); + mpc.getPerformanceManager().lock()->drainQueue(); + REQUIRE(program->getNoteParameters(43)->getSliderParameterNumber() == mpc::NoteVariationTypeFilter); + REQUIRE(program->getSlider()->getParameter() == mpc::NoteVariationTypeFilter); + REQUIRE(program->getNoteParameters(42)->getSliderParameterNumber() == mpc::NoteVariationTypeFilter); +} + TEST_CASE("Delete sound 1", "[sampler]") { Mpc mpc; diff --git a/src/test/SetFileUiTest.cpp b/src/test/SetFileUiTest.cpp new file mode 100644 index 000000000..a7b6e52a8 --- /dev/null +++ b/src/test/SetFileUiTest.cpp @@ -0,0 +1,333 @@ +#include "catch2/catch_test_macros.hpp" + +#include "TestMpc.hpp" +#include "Mpc.hpp" +#include "disk/AbstractDisk.hpp" +#include "disk/MpcFile.hpp" +#include "engine/EngineHost.hpp" +#include "lcdgui/Label.hpp" +#include "lcdgui/LayeredScreen.hpp" +#include "lcdgui/screens/LoadScreen.hpp" +#include "lcdgui/screens/window/LoadASetScreen.hpp" +#include "sampler/Pad.hpp" +#include "sampler/Program.hpp" +#include "sampler/Sampler.hpp" +#include "sequencer/Bus.hpp" +#include "sequencer/Sequencer.hpp" +#include "StrUtil.hpp" + +#include +#include +#include +#include + +CMRC_DECLARE(mpctest); + +using namespace mpc; +using namespace mpc::lcdgui; +using namespace mpc::lcdgui::screens; + +namespace +{ + constexpr auto kSetFileName = "ROCK.SET"; + + void prepareSetFile(Mpc &mpc) + { + auto fs = cmrc::mpctest::get_filesystem(); + auto file = fs.open("test/RealMpc60/Set/ROCK.SET"); + std::vector data(file.begin(), file.end()); + auto newFile = mpc.getDisk()->newFile(kSetFileName); + newFile->setFileData(data); + mpc.getDisk()->initFiles(); + } + + void prepareBrokenSetFile(Mpc &mpc) + { + auto newFile = mpc.getDisk()->newFile("BROKEN.SET"); + std::vector data{'B', 'A', 'D'}; + newFile->setFileData(data); + mpc.getDisk()->initFiles(); + } + + void waitForLoadScreen(Mpc &mpc) + { + constexpr auto timeout = std::chrono::seconds(5); + const auto start = std::chrono::steady_clock::now(); + + while (mpc.getLayeredScreen()->getCurrentScreenName() != "load") + { + REQUIRE(std::chrono::steady_clock::now() - start < timeout); + mpc.getEngineHost()->prepareProcessBlock(512); + mpc.getLayeredScreen()->timerCallback(); + std::this_thread::sleep_for(std::chrono::milliseconds(10)); + } + } + + void waitForTimedPopup(Mpc &mpc) + { + std::this_thread::sleep_for(std::chrono::milliseconds(1100)); + mpc.getLayeredScreen()->timerCallback(); + } +} + +TEST_CASE(".SET file opens discovered UI flow", "[load-set][ui]") +{ + Mpc mpc; + TestMpc::initializeTestMpc(mpc); + prepareSetFile(mpc); + + auto layeredScreen = mpc.getLayeredScreen(); + layeredScreen->openScreen("load"); + + const auto loadScreen = mpc.screens->get(); + const auto fileNames = mpc.getDisk()->getFileNames(); + const auto setFileIt = + std::find_if(fileNames.begin(), fileNames.end(), + [](const std::string &fileName) + { + return StrUtil::eqIgnoreCase(fileName, kSetFileName); + }); + REQUIRE(setFileIt != fileNames.end()); + loadScreen->setFileLoad( + static_cast(std::distance(fileNames.begin(), setFileIt))); + loadScreen->function(5); + + REQUIRE(layeredScreen->getCurrentScreenName() == "load-a-set"); + + layeredScreen->getCurrentScreen()->function(4); + REQUIRE(layeredScreen->getCurrentScreenName() == "conversion-table"); + + layeredScreen->getCurrentScreen()->function(3); + REQUIRE(layeredScreen->getCurrentScreenName() == "load"); + + loadScreen->function(5); + REQUIRE(layeredScreen->getCurrentScreenName() == "load-a-set"); + + layeredScreen->getCurrentScreen()->function(4); + REQUIRE(layeredScreen->getCurrentScreenName() == "conversion-table"); + + layeredScreen->getCurrentScreen()->function(4); + REQUIRE(layeredScreen->getCurrentScreenName() == "load-a-set-replace-add"); + REQUIRE(layeredScreen->getCurrentScreen() + ->findField("load-replace-sound") + ->getText() == "NO(FASTER)"); + + layeredScreen->getCurrentScreen()->turnWheel(1); + REQUIRE(layeredScreen->getCurrentScreen() + ->findField("load-replace-sound") + ->getText() == "YES"); + + layeredScreen->getCurrentScreen()->turnWheel(-1); + REQUIRE(layeredScreen->getCurrentScreen() + ->findField("load-replace-sound") + ->getText() == "NO(FASTER)"); + + layeredScreen->getCurrentScreen()->function(4); + REQUIRE(layeredScreen->getCurrentScreenName() == "popup"); + waitForLoadScreen(mpc); + REQUIRE(layeredScreen->getCurrentScreenName() == "load"); + + loadScreen->function(5); + REQUIRE(layeredScreen->getCurrentScreenName() == "load-a-set"); + + layeredScreen->getCurrentScreen()->function(2); + REQUIRE(layeredScreen->getCurrentScreenName() == "load-a-set-sound"); + REQUIRE(layeredScreen->getCurrentScreen()->findField("mpc60-pad")->getText() == + "HIHT CLSD (A01)"); + REQUIRE(layeredScreen->getCurrentScreen()->findLabel("file")->getText() == + "File:HAT2CLSD "); + + layeredScreen->getCurrentScreen()->function(3); + REQUIRE(layeredScreen->getCurrentScreenName() == "load-a-set"); + + layeredScreen->getCurrentScreen()->function(2); + REQUIRE(layeredScreen->getCurrentScreenName() == "load-a-set-sound"); + + layeredScreen->getCurrentScreen()->turnWheel(1); + REQUIRE(layeredScreen->getCurrentScreen()->findField("mpc60-pad")->getText() == + "HIHT MEDM (A01)"); + REQUIRE(layeredScreen->getCurrentScreen()->findLabel("file")->getText() == + "File:HAT2MED "); + + layeredScreen->getCurrentScreen()->turnWheel(1); + REQUIRE(layeredScreen->getCurrentScreen()->findField("mpc60-pad")->getText() == + "HIHT OPEN (A01)"); + REQUIRE(layeredScreen->getCurrentScreen()->findLabel("file")->getText() == + "File:HAT2OPN "); + + layeredScreen->getCurrentScreen()->turnWheel(15); + REQUIRE(layeredScreen->getCurrentScreen()->findField("mpc60-pad")->getText() == + "PRC4 (A16)"); + REQUIRE(layeredScreen->getCurrentScreen()->findLabel("file")->getText() == + "File:BIG_CLAP "); + + layeredScreen->getCurrentScreen()->function(4); + REQUIRE(layeredScreen->getCurrentScreenName() == "popup"); + std::this_thread::sleep_for(std::chrono::milliseconds(350)); + layeredScreen->timerCallback(); + REQUIRE(layeredScreen->getCurrentScreenName() == "load-a-sound"); + REQUIRE(mpc.getSampler()->getPreviewSound()->getName() == "BIG_CLAP"); + REQUIRE(mpc.getSampler()->getPreviewSound()->getSampleRate() == 40000); +} + +TEST_CASE("Unreadable .SET file reports error and returns to LOAD", + "[load-set][ui]") +{ + Mpc mpc; + TestMpc::initializeTestMpc(mpc); + prepareBrokenSetFile(mpc); + + auto layeredScreen = mpc.getLayeredScreen(); + layeredScreen->openScreen("load"); + + const auto loadScreen = mpc.screens->get(); + const auto fileNames = mpc.getDisk()->getFileNames(); + const auto setFileIt = + std::find_if(fileNames.begin(), fileNames.end(), + [](const std::string &fileName) + { + return StrUtil::eqIgnoreCase(fileName, "BROKEN.SET"); + }); + REQUIRE(setFileIt != fileNames.end()); + loadScreen->setFileLoad( + static_cast(std::distance(fileNames.begin(), setFileIt))); + loadScreen->function(5); + + layeredScreen->timerCallback(); + REQUIRE(layeredScreen->getCurrentScreenName() == "popup"); + waitForTimedPopup(mpc); + REQUIRE(layeredScreen->getCurrentScreenName() == "load"); +} + +TEST_CASE(".SET conversion table resolves pad names through DRUM1 program", + "[load-set][ui]") +{ + Mpc mpc; + TestMpc::initializeTestMpcWithoutMidiServices(mpc); + prepareSetFile(mpc); + + auto layeredScreen = mpc.getLayeredScreen(); + layeredScreen->openScreen("vmpc-settings"); + REQUIRE(layeredScreen->setFocus("initial-pad-mapping")); + layeredScreen->getCurrentScreen()->turnWheel(1); + REQUIRE(layeredScreen->getCurrentScreen() + ->findField("initial-pad-mapping") + ->getText() == "ORIGINAL"); + + layeredScreen->openScreen("load"); + + const auto loadScreen = mpc.screens->get(); + const auto fileNames = mpc.getDisk()->getFileNames(); + const auto setFileIt = + std::find_if(fileNames.begin(), fileNames.end(), + [](const std::string &fileName) + { + return StrUtil::eqIgnoreCase(fileName, kSetFileName); + }); + REQUIRE(setFileIt != fileNames.end()); + loadScreen->setFileLoad( + static_cast(std::distance(fileNames.begin(), setFileIt))); + loadScreen->function(5); + + auto drum1Program = + mpc.getSampler()->createNewProgramAddFirstAvailableSlotAndThen({}).lock(); + REQUIRE(drum1Program); + drum1Program->initPadAssign(); + mpc.getSequencer()->getDrumBus(mpc::sequencer::BusType::DRUM1)->setProgramIndex( + drum1Program->getProgramIndex()); + mpc.getEngineHost()->prepareProcessBlock(512); + layeredScreen->timerCallback(); + REQUIRE(drum1Program->getNoteFromPad(ProgramPadIndex(0)) == DrumNoteNumber(37)); + REQUIRE(drum1Program->getNoteFromPad(ProgramPadIndex(1)) == DrumNoteNumber(36)); + REQUIRE(drum1Program->getNoteFromPad(ProgramPadIndex(2)) == DrumNoteNumber(42)); + REQUIRE(drum1Program->getNoteFromPad(ProgramPadIndex(3)) == DrumNoteNumber(82)); + + layeredScreen->getCurrentScreen()->function(4); + REQUIRE(layeredScreen->getCurrentScreenName() == "conversion-table"); + + layeredScreen->getCurrentScreen()->open(); + REQUIRE(layeredScreen->getCurrentScreen()->findField("mpc60-pad")->getText() == + "HIHT CLSD (A01) "); + REQUIRE(layeredScreen->getCurrentScreen()->findField("becomes-note")->getText() == + "42/A03"); + + mpc.screens->get()->setConversionTargetNote(0, DrumNoteNumber(82)); + layeredScreen->getCurrentScreen()->open(); + REQUIRE(layeredScreen->getCurrentScreen()->findField("becomes-note")->getText() == + "82/A04"); +} + +TEST_CASE(".SET single-sound browser keeps unassigned UK-8 pads visible", + "[load-set][ui]") +{ + Mpc mpc; + TestMpc::initializeTestMpc(mpc); + + auto fs = cmrc::mpctest::get_filesystem(); + auto file = fs.open("test/RealMpc60/Set/UK-8.SET"); + std::vector data(file.begin(), file.end()); + auto newFile = mpc.getDisk()->newFile("UK-8.SET"); + newFile->setFileData(data); + mpc.getDisk()->initFiles(); + + auto layeredScreen = mpc.getLayeredScreen(); + layeredScreen->openScreen("load"); + + const auto loadScreen = mpc.screens->get(); + const auto fileNames = mpc.getDisk()->getFileNames(); + const auto setFileIt = + std::find_if(fileNames.begin(), fileNames.end(), + [](const std::string &fileName) + { + return StrUtil::eqIgnoreCase(fileName, "UK-8.SET"); + }); + REQUIRE(setFileIt != fileNames.end()); + loadScreen->setFileLoad( + static_cast(std::distance(fileNames.begin(), setFileIt))); + loadScreen->function(5); + + layeredScreen->getCurrentScreen()->function(2); + REQUIRE(layeredScreen->getCurrentScreenName() == "load-a-set-sound"); + REQUIRE(layeredScreen->getCurrentScreen()->findField("mpc60-pad")->getText() == + "HIHT CLSD (A01)"); + REQUIRE(layeredScreen->getCurrentScreen()->findLabel("file")->getText() == + "File:S7_HH_CL "); + + layeredScreen->getCurrentScreen()->turnWheel(1); + REQUIRE(layeredScreen->getCurrentScreen()->findField("mpc60-pad")->getText() == + "HIHT MEDM (A01)"); + REQUIRE(layeredScreen->getCurrentScreen()->findLabel("file")->getText() == + "File:S7_HH_MD "); + + layeredScreen->getCurrentScreen()->turnWheel(1); + REQUIRE(layeredScreen->getCurrentScreen()->findField("mpc60-pad")->getText() == + "HIHT OPEN (A01)"); + REQUIRE(layeredScreen->getCurrentScreen()->findLabel("file")->getText() == + "File:S7_HH_OP "); + + layeredScreen->getCurrentScreen()->turnWheel(1); + REQUIRE(layeredScreen->getCurrentScreen()->findField("mpc60-pad")->getText() == + "SNR1 (A02)"); + REQUIRE(layeredScreen->getCurrentScreen()->findLabel("file")->getText() == + "File:UK-SN31 "); + + layeredScreen->getCurrentScreen()->turnWheel(15); + REQUIRE(layeredScreen->getCurrentScreen()->findField("mpc60-pad")->getText() == + "DR01 (B01)"); + REQUIRE(layeredScreen->getCurrentScreen()->findLabel("file")->getText() == + "File:(no assign)"); + + layeredScreen->getCurrentScreen()->function(4); + REQUIRE(layeredScreen->getCurrentScreenName() == "load-a-set-sound"); + REQUIRE(layeredScreen->getCurrentScreen()->findField("mpc60-pad")->getText() == + "DR01 (B01)"); + REQUIRE(layeredScreen->getCurrentScreen()->findLabel("file")->getText() == + "File:(no assign)"); + + layeredScreen->getCurrentScreen()->turnWheel(7); + REQUIRE(layeredScreen->getCurrentScreen() + ->findField("mpc60-pad") + ->getText() == "DR08 (B08)"); + REQUIRE(layeredScreen->getCurrentScreen()->findLabel("file")->getText() == + "File:(no assign)"); +} diff --git a/src/test/SndPersistenceTest.cpp b/src/test/SndPersistenceTest.cpp index 6d54c7307..929ff1504 100644 --- a/src/test/SndPersistenceTest.cpp +++ b/src/test/SndPersistenceTest.cpp @@ -1,13 +1,11 @@ #include #include "sampler/Sound.hpp" -#include "file/sndwriter/SndWriter.hpp" -#include "file/sndreader/SndReader.hpp" +#include "disk/MpcFile.hpp" +#include "file/kaitai/SndIo.hpp" #include "SampleOps.hpp" -using namespace mpc::file::sndwriter; -using namespace mpc::file::sndreader; using namespace mpc::sampleops; TEST_CASE("Sample ops", "[sample-ops]") @@ -24,6 +22,7 @@ TEST_CASE("Sample ops", "[sample-ops]") TEST_CASE("Write and read non-destructively", "[snd-persistence]") { mpc::sampler::Sound snd1(44100); + snd1.setName("snd_persist"); snd1.setMono(true); auto inputData = snd1.getMutableSampleData(); @@ -33,11 +32,13 @@ TEST_CASE("Write and read non-destructively", "[snd-persistence]") inputData->emplace_back(short_to_float(static_cast(i))); } - SndWriter sndWriter(&snd1); - auto &writtenData = sndWriter.getSndFileArray(); - SndReader reader(writtenData); - auto outputData = std::make_shared>(); - reader.readData(outputData); + const auto writtenData = mpc::file::kaitai::SndIo::saveSound(snd1); + auto sound = std::make_shared(44100); + auto result = mpc::file::kaitai::SndIo::loadBytes( + writtenData, sound, "snd_persist"); + REQUIRE(result); + + const auto outputData = sound->getSampleData(); REQUIRE(outputData->size() == snd1.getSampleData()->size()); diff --git a/src/test/TestMpc.cpp b/src/test/TestMpc.cpp index bcc424aaa..e62f8c7c3 100644 --- a/src/test/TestMpc.cpp +++ b/src/test/TestMpc.cpp @@ -19,5 +19,22 @@ void mpc::TestMpc::initializeTestMpc(Mpc &mpc) { resetTestDataRoot(mpc); mpc.init(); - mpc.getEngineHost()->getAudioServer()->start(); +} + +void mpc::TestMpc::initializeTestMpcWithoutMidiServices(Mpc &mpc) +{ + resetTestDataRoot(mpc); + MpcInitOptions options; + options.startMidiDeviceDetector = false; + options.startAudioServer = true; + mpc.init(options); +} + +void mpc::TestMpc::initializeTestMpcWithoutIoServices(Mpc &mpc) +{ + resetTestDataRoot(mpc); + MpcInitOptions options; + options.startMidiDeviceDetector = false; + options.startAudioServer = false; + mpc.init(options); } diff --git a/src/test/TestMpc.hpp b/src/test/TestMpc.hpp index 6e368acd4..65135ee3f 100644 --- a/src/test/TestMpc.hpp +++ b/src/test/TestMpc.hpp @@ -9,5 +9,7 @@ namespace mpc public: static void resetTestDataRoot(Mpc &mpc); static void initializeTestMpc(Mpc &mpc); + static void initializeTestMpcWithoutMidiServices(Mpc &mpc); + static void initializeTestMpcWithoutIoServices(Mpc &mpc); }; } // namespace mpc diff --git a/src/test/TestPaths.cpp b/src/test/TestPaths.cpp index c69bb0872..0d49ced96 100644 --- a/src/test/TestPaths.cpp +++ b/src/test/TestPaths.cpp @@ -1,5 +1,44 @@ #include "TestPaths.hpp" +#include +#include +#include + +#ifdef _WIN32 +#include +#else +#include +#endif + +namespace +{ + int current_process_id() + { +#ifdef _WIN32 + return _getpid(); +#else + return getpid(); +#endif + } + + mpc_fs::path make_unique_test_documents_path() + { + static std::atomic counter{0}; + const auto serial = counter.fetch_add(1, std::memory_order_relaxed); + const auto ticks = + std::chrono::steady_clock::now().time_since_epoch().count(); + + return mpc_fs::temp_directory_path().value_or(mpc_fs::path{}) / + ("VMPC2000XL-test-" + std::to_string(current_process_id()) + + "-" + std::to_string(serial) + "-" + std::to_string(ticks)); + } +} // namespace + +mpc::TestPaths::TestPaths() + : testDocumentsPath(make_unique_test_documents_path()) +{ +} + mpc_fs::path mpc::TestPaths::appConfigHome() const { return appDocumentsPath() / "config"; @@ -7,6 +46,5 @@ mpc_fs::path mpc::TestPaths::appConfigHome() const mpc_fs::path mpc::TestPaths::appDocumentsPath() const { - return mpc_fs::temp_directory_path().value_or(mpc_fs::path{}) / - "VMPC2000XL-test"; + return testDocumentsPath; } diff --git a/src/test/TestPaths.hpp b/src/test/TestPaths.hpp index f0548f984..486fe3311 100644 --- a/src/test/TestPaths.hpp +++ b/src/test/TestPaths.hpp @@ -6,11 +6,13 @@ namespace mpc { class TestPaths : public Paths { + mpc_fs::path testDocumentsPath; protected: mpc_fs::path appConfigHome() const override; public: + TestPaths(); mpc_fs::path appDocumentsPath() const override; }; -} // namespace mpc \ No newline at end of file +} // namespace mpc diff --git a/src/test/WavSupportTest.cpp b/src/test/WavSupportTest.cpp index dc6933ee7..39704ef2f 100644 --- a/src/test/WavSupportTest.cpp +++ b/src/test/WavSupportTest.cpp @@ -1,10 +1,13 @@ #include +#include "SampleOps.hpp" #include "audiomidi/SoundPlayer.hpp" #include "audiomidi/WavInputFileStream.hpp" #include "engine/audio/core/AudioBuffer.hpp" #include "file/wav/WavFile.hpp" +#include + #include #include #include @@ -18,9 +21,19 @@ using namespace mpc::audiomidi; using namespace mpc::engine::audio::core; using namespace mpc::file::wav; +using namespace mpc::sampleops; + +CMRC_DECLARE(mpctest); namespace { + std::vector resourceBytes(const std::string &resourcePath) + { + auto fs = cmrc::mpctest::get_filesystem(); + auto file = fs.open(resourcePath); + return std::vector(file.begin(), file.end()); + } + void appendLe(std::string &bytes, uint32_t value, int byteCount) { for (int i = 0; i < byteCount; i++) @@ -138,3 +151,68 @@ TEST_CASE("32-bit PCM WAV preview reads full-width samples", "[wav][preview]") REQUIRE(std::fabs(buffer.getChannel(0)[1] + 0.5f) < 0.0001f); REQUIRE(std::fabs(buffer.getChannel(0)[2]) < 0.0001f); } + +TEST_CASE("MPC60 SND preview decodes packed 12-bit samples", "[snd][preview]") +{ + const auto bytes = + resourceBytes("test/RealMpc60/Snd/Mpc60V214Rock/HAT2CLSD.SND"); + auto stream = std::make_shared( + std::string(bytes.begin(), bytes.end()), + std::ios::in | std::ios::out | std::ios::binary); + + SoundPlayer soundPlayer; + REQUIRE(soundPlayer.start(stream, SoundPlayerFileFormat::SND, 40000)); + + AudioBuffer buffer("preview", 2, 4, 40000); + + for (int i = 0; i < 20; i++) + { + std::this_thread::sleep_for(std::chrono::milliseconds(5)); + soundPlayer.processAudio(&buffer, 4); + if (!buffer.isSilent()) + { + break; + } + } + + REQUIRE(std::fabs(buffer.getChannel(0)[0] - short_to_float(27)) < 0.0001f); + REQUIRE(std::fabs(buffer.getChannel(0)[1] - short_to_float(52)) < 0.0001f); + REQUIRE(std::fabs(buffer.getChannel(0)[2] - short_to_float(55)) < 0.0001f); + REQUIRE(std::fabs(buffer.getChannel(0)[3] - short_to_float(69)) < 0.0001f); + REQUIRE(std::fabs(buffer.getChannel(1)[0] - buffer.getChannel(0)[0]) < + 0.0001f); + REQUIRE(std::fabs(buffer.getChannel(1)[3] - buffer.getChannel(0)[3]) < + 0.0001f); +} + +TEST_CASE("MPC3000 SND preview streams mono 16-bit PCM", "[snd][preview]") +{ + const auto bytes = resourceBytes("test/RealMpc3000/Snd/SOUND017.SND"); + auto stream = std::make_shared( + std::string(bytes.begin(), bytes.end()), + std::ios::in | std::ios::out | std::ios::binary); + + SoundPlayer soundPlayer; + REQUIRE(soundPlayer.start(stream, SoundPlayerFileFormat::SND, 44100)); + + AudioBuffer buffer("preview", 2, 4, 44100); + + for (int i = 0; i < 20; i++) + { + std::this_thread::sleep_for(std::chrono::milliseconds(5)); + soundPlayer.processAudio(&buffer, 4); + if (!buffer.isSilent()) + { + break; + } + } + + REQUIRE(std::fabs(buffer.getChannel(0)[0] - short_to_float(5)) < 0.0001f); + REQUIRE(std::fabs(buffer.getChannel(0)[1] - short_to_float(6)) < 0.0001f); + REQUIRE(std::fabs(buffer.getChannel(0)[2] - short_to_float(7)) < 0.0001f); + REQUIRE(std::fabs(buffer.getChannel(0)[3] - short_to_float(6)) < 0.0001f); + REQUIRE(std::fabs(buffer.getChannel(1)[0] - buffer.getChannel(0)[0]) < + 0.0001f); + REQUIRE(std::fabs(buffer.getChannel(1)[3] - buffer.getChannel(0)[3]) < + 0.0001f); +} diff --git a/test-resources/GeneratedMpc2000xl/Snd/README.txt b/test-resources/GeneratedMpc2000xl/Snd/README.txt new file mode 100644 index 000000000..a0795d3d7 --- /dev/null +++ b/test-resources/GeneratedMpc2000xl/Snd/README.txt @@ -0,0 +1,12 @@ +Generated MPC2000XL SND corpus + +These fixtures are synthetic and intentionally small. +They are bundled into mpc-tests to exercise the production SND load path +without relying on the handwritten parser as the oracle. + +Files: +- mono_corpus_min.SND: Mono corpus case with low behavioral values. +- mono_loop_mid.SND: Mono loop-enabled mid-range case. +- stereo_span.SND: Stereo case that makes non-interleaved payload ordering obvious. +- stereo_max.SND: Stereo case with high behavioral values. +- name_len_sixteen.SND: Exact 16-character name boundary case. diff --git a/test-resources/GeneratedMpc2000xl/Snd/mono_corpus_min.SND b/test-resources/GeneratedMpc2000xl/Snd/mono_corpus_min.SND new file mode 100644 index 000000000..d47942ba0 Binary files /dev/null and b/test-resources/GeneratedMpc2000xl/Snd/mono_corpus_min.SND differ diff --git a/test-resources/GeneratedMpc2000xl/Snd/mono_loop_mid.SND b/test-resources/GeneratedMpc2000xl/Snd/mono_loop_mid.SND new file mode 100644 index 000000000..f937c0264 Binary files /dev/null and b/test-resources/GeneratedMpc2000xl/Snd/mono_loop_mid.SND differ diff --git a/test-resources/GeneratedMpc2000xl/Snd/name_len_sixteen.SND b/test-resources/GeneratedMpc2000xl/Snd/name_len_sixteen.SND new file mode 100644 index 000000000..71c8ba98e Binary files /dev/null and b/test-resources/GeneratedMpc2000xl/Snd/name_len_sixteen.SND differ diff --git a/test-resources/GeneratedMpc2000xl/Snd/stereo_max.SND b/test-resources/GeneratedMpc2000xl/Snd/stereo_max.SND new file mode 100644 index 000000000..bbc551a42 Binary files /dev/null and b/test-resources/GeneratedMpc2000xl/Snd/stereo_max.SND differ diff --git a/test-resources/GeneratedMpc2000xl/Snd/stereo_span.SND b/test-resources/GeneratedMpc2000xl/Snd/stereo_span.SND new file mode 100644 index 000000000..c0b39e576 Binary files /dev/null and b/test-resources/GeneratedMpc2000xl/Snd/stereo_span.SND differ diff --git a/test-resources/RealMpc2000xl/All/ALL.ALL b/test-resources/RealMpc2000xl/All/ALL.ALL new file mode 100755 index 000000000..cc349e0f1 Binary files /dev/null and b/test-resources/RealMpc2000xl/All/ALL.ALL differ diff --git a/test-resources/RealMpc2000xl/Aps/ALL_PGMS.APS b/test-resources/RealMpc2000xl/Aps/ALL_PGMS.APS new file mode 100755 index 000000000..23e1efb4d Binary files /dev/null and b/test-resources/RealMpc2000xl/Aps/ALL_PGMS.APS differ diff --git a/test-resources/RealMpc2000xl/Mid/mpc2000xl_seq_variations.MID b/test-resources/RealMpc2000xl/Mid/mpc2000xl_seq_variations.MID new file mode 100755 index 000000000..abac48599 Binary files /dev/null and b/test-resources/RealMpc2000xl/Mid/mpc2000xl_seq_variations.MID differ diff --git a/test-resources/RealMpc2000xl/Pgm/NEWPGM-A.PGM b/test-resources/RealMpc2000xl/Pgm/NEWPGM-A.PGM new file mode 100755 index 000000000..042089c89 Binary files /dev/null and b/test-resources/RealMpc2000xl/Pgm/NEWPGM-A.PGM differ diff --git a/test-resources/RealMpc2000xl/Resist/RESIST.ALL b/test-resources/RealMpc2000xl/Resist/RESIST.ALL new file mode 100644 index 000000000..d80cedabe Binary files /dev/null and b/test-resources/RealMpc2000xl/Resist/RESIST.ALL differ diff --git a/test-resources/RealMpc2000xl/Snd/mono_loop_off.SND b/test-resources/RealMpc2000xl/Snd/mono_loop_off.SND new file mode 100755 index 000000000..1ce0f1ed7 Binary files /dev/null and b/test-resources/RealMpc2000xl/Snd/mono_loop_off.SND differ diff --git a/test-resources/RealMpc2000xl/Snd/stereo_loop_off.SND b/test-resources/RealMpc2000xl/Snd/stereo_loop_off.SND new file mode 100755 index 000000000..93dffb0fb Binary files /dev/null and b/test-resources/RealMpc2000xl/Snd/stereo_loop_off.SND differ diff --git a/test-resources/RealMpc3000/All/M3K_2SEQ.ALL b/test-resources/RealMpc3000/All/M3K_2SEQ.ALL new file mode 100755 index 000000000..09ebb1932 Binary files /dev/null and b/test-resources/RealMpc3000/All/M3K_2SEQ.ALL differ diff --git a/test-resources/RealMpc3000/All/M3K_2SEQ1SONG.ALL b/test-resources/RealMpc3000/All/M3K_2SEQ1SONG.ALL new file mode 100755 index 000000000..341779902 Binary files /dev/null and b/test-resources/RealMpc3000/All/M3K_2SEQ1SONG.ALL differ diff --git a/test-resources/RealMpc3000/All/M3K_EMPTY2.ALL b/test-resources/RealMpc3000/All/M3K_EMPTY2.ALL new file mode 100755 index 000000000..a9a284c28 Binary files /dev/null and b/test-resources/RealMpc3000/All/M3K_EMPTY2.ALL differ diff --git a/test-resources/RealMpc3000/Pgm/PROGRAM.pgm b/test-resources/RealMpc3000/Pgm/PROGRAM.pgm new file mode 100755 index 000000000..7f9a42740 Binary files /dev/null and b/test-resources/RealMpc3000/Pgm/PROGRAM.pgm differ diff --git a/test-resources/RealMpc3000/Pgm/PROGRAM_01_SIMULT.pgm b/test-resources/RealMpc3000/Pgm/PROGRAM_01_SIMULT.pgm new file mode 100644 index 000000000..99ae7a1c2 Binary files /dev/null and b/test-resources/RealMpc3000/Pgm/PROGRAM_01_SIMULT.pgm differ diff --git a/test-resources/RealMpc3000/Pgm/PROGRAM_02_VELSW.pgm b/test-resources/RealMpc3000/Pgm/PROGRAM_02_VELSW.pgm new file mode 100644 index 000000000..db670ae0c Binary files /dev/null and b/test-resources/RealMpc3000/Pgm/PROGRAM_02_VELSW.pgm differ diff --git a/test-resources/RealMpc3000/Pgm/PROGRAM_03_VELSW_43.pgm b/test-resources/RealMpc3000/Pgm/PROGRAM_03_VELSW_43.pgm new file mode 100644 index 000000000..168ea2756 Binary files /dev/null and b/test-resources/RealMpc3000/Pgm/PROGRAM_03_VELSW_43.pgm differ diff --git a/test-resources/RealMpc3000/Pgm/PROGRAM_04_ENV_PROBE.pgm b/test-resources/RealMpc3000/Pgm/PROGRAM_04_ENV_PROBE.pgm new file mode 100644 index 000000000..d1a6b5766 Binary files /dev/null and b/test-resources/RealMpc3000/Pgm/PROGRAM_04_ENV_PROBE.pgm differ diff --git a/test-resources/RealMpc3000/Seq/M3KD120.SEQ b/test-resources/RealMpc3000/Seq/M3KD120.SEQ new file mode 100755 index 000000000..3de19e5ec Binary files /dev/null and b/test-resources/RealMpc3000/Seq/M3KD120.SEQ differ diff --git a/test-resources/RealMpc3000/Seq/M3KEVT.SEQ b/test-resources/RealMpc3000/Seq/M3KEVT.SEQ new file mode 100644 index 000000000..681ced761 Binary files /dev/null and b/test-resources/RealMpc3000/Seq/M3KEVT.SEQ differ diff --git a/test-resources/RealMpc3000/Seq/M3KLPON.SEQ b/test-resources/RealMpc3000/Seq/M3KLPON.SEQ new file mode 100755 index 000000000..b9f2efb4e Binary files /dev/null and b/test-resources/RealMpc3000/Seq/M3KLPON.SEQ differ diff --git a/test-resources/RealMpc3000/Seq/M3KMID1.SEQ b/test-resources/RealMpc3000/Seq/M3KMID1.SEQ new file mode 100644 index 000000000..68bb1f1a0 Binary files /dev/null and b/test-resources/RealMpc3000/Seq/M3KMID1.SEQ differ diff --git a/test-resources/RealMpc3000/Seq/M3KMIN.SEQ b/test-resources/RealMpc3000/Seq/M3KMIN.SEQ new file mode 100755 index 000000000..cf1bb181c Binary files /dev/null and b/test-resources/RealMpc3000/Seq/M3KMIN.SEQ differ diff --git a/test-resources/RealMpc3000/Seq/M3KNOTE.SEQ b/test-resources/RealMpc3000/Seq/M3KNOTE.SEQ new file mode 100755 index 000000000..909540756 Binary files /dev/null and b/test-resources/RealMpc3000/Seq/M3KNOTE.SEQ differ diff --git a/test-resources/RealMpc3000/Seq/M3KPB.SEQ b/test-resources/RealMpc3000/Seq/M3KPB.SEQ new file mode 100755 index 000000000..f0cb67f7e Binary files /dev/null and b/test-resources/RealMpc3000/Seq/M3KPB.SEQ differ diff --git a/test-resources/RealMpc3000/Seq/M3KPC.SEQ b/test-resources/RealMpc3000/Seq/M3KPC.SEQ new file mode 100755 index 000000000..2a18607ae Binary files /dev/null and b/test-resources/RealMpc3000/Seq/M3KPC.SEQ differ diff --git a/test-resources/RealMpc3000/Seq/M3KT090.SEQ b/test-resources/RealMpc3000/Seq/M3KT090.SEQ new file mode 100755 index 000000000..8e172c8d9 Binary files /dev/null and b/test-resources/RealMpc3000/Seq/M3KT090.SEQ differ diff --git a/test-resources/RealMpc3000/Seq/M3KT119.SEQ b/test-resources/RealMpc3000/Seq/M3KT119.SEQ new file mode 100755 index 000000000..82a4a805d Binary files /dev/null and b/test-resources/RealMpc3000/Seq/M3KT119.SEQ differ diff --git a/test-resources/RealMpc3000/Seq/M3KT150.SEQ b/test-resources/RealMpc3000/Seq/M3KT150.SEQ new file mode 100755 index 000000000..397bdf937 Binary files /dev/null and b/test-resources/RealMpc3000/Seq/M3KT150.SEQ differ diff --git a/test-resources/RealMpc3000/Seq/M3KTRKO.SEQ b/test-resources/RealMpc3000/Seq/M3KTRKO.SEQ new file mode 100644 index 000000000..b2ccf2548 Binary files /dev/null and b/test-resources/RealMpc3000/Seq/M3KTRKO.SEQ differ diff --git a/test-resources/RealMpc3000/Snd/SOUND017.SND b/test-resources/RealMpc3000/Snd/SOUND017.SND new file mode 100755 index 000000000..200bab082 Binary files /dev/null and b/test-resources/RealMpc3000/Snd/SOUND017.SND differ diff --git a/test-resources/RealMpc60/All/MPC60_V214_ALL_SEQS.ALL b/test-resources/RealMpc60/All/MPC60_V214_ALL_SEQS.ALL new file mode 100755 index 000000000..b66f805f4 Binary files /dev/null and b/test-resources/RealMpc60/All/MPC60_V214_ALL_SEQS.ALL differ diff --git a/test-resources/RealMpc60/All/MPC60_V214_SONG_2REALSTEPS.ALL b/test-resources/RealMpc60/All/MPC60_V214_SONG_2REALSTEPS.ALL new file mode 100755 index 000000000..b5e78f8df Binary files /dev/null and b/test-resources/RealMpc60/All/MPC60_V214_SONG_2REALSTEPS.ALL differ diff --git a/test-resources/RealMpc60/Seq/MPC60_V214_SEQ01.SEQ b/test-resources/RealMpc60/Seq/MPC60_V214_SEQ01.SEQ new file mode 100755 index 000000000..324eba737 Binary files /dev/null and b/test-resources/RealMpc60/Seq/MPC60_V214_SEQ01.SEQ differ diff --git a/test-resources/RealMpc60/Set/ROCK.SET b/test-resources/RealMpc60/Set/ROCK.SET new file mode 100755 index 000000000..f4cbfed00 Binary files /dev/null and b/test-resources/RealMpc60/Set/ROCK.SET differ diff --git a/test-resources/RealMpc60/Set/STUDIO.SET b/test-resources/RealMpc60/Set/STUDIO.SET new file mode 100755 index 000000000..8d372d914 Binary files /dev/null and b/test-resources/RealMpc60/Set/STUDIO.SET differ diff --git a/test-resources/RealMpc60/Set/UK-8.SET b/test-resources/RealMpc60/Set/UK-8.SET new file mode 100755 index 000000000..efe89f49f Binary files /dev/null and b/test-resources/RealMpc60/Set/UK-8.SET differ diff --git a/test-resources/RealMpc60/SetImportedSnd/Rock/BIG_CLAP.SND b/test-resources/RealMpc60/SetImportedSnd/Rock/BIG_CLAP.SND new file mode 100644 index 000000000..e243c7db6 Binary files /dev/null and b/test-resources/RealMpc60/SetImportedSnd/Rock/BIG_CLAP.SND differ diff --git a/test-resources/RealMpc60/SetImportedSnd/Rock/HAT2CLSD.SND b/test-resources/RealMpc60/SetImportedSnd/Rock/HAT2CLSD.SND new file mode 100644 index 000000000..07c3fdae6 Binary files /dev/null and b/test-resources/RealMpc60/SetImportedSnd/Rock/HAT2CLSD.SND differ diff --git a/test-resources/RealMpc60/Snd/Mpc60V214Rock/BASSGUIT.SND b/test-resources/RealMpc60/Snd/Mpc60V214Rock/BASSGUIT.SND new file mode 100644 index 000000000..4547dc8eb Binary files /dev/null and b/test-resources/RealMpc60/Snd/Mpc60V214Rock/BASSGUIT.SND differ diff --git a/test-resources/RealMpc60/Snd/Mpc60V214Rock/BIG_CLAP.SND b/test-resources/RealMpc60/Snd/Mpc60V214Rock/BIG_CLAP.SND new file mode 100644 index 000000000..100618b72 Binary files /dev/null and b/test-resources/RealMpc60/Snd/Mpc60V214Rock/BIG_CLAP.SND differ diff --git a/test-resources/RealMpc60/Snd/Mpc60V214Rock/COWBEL#1.SND b/test-resources/RealMpc60/Snd/Mpc60V214Rock/COWBEL#1.SND new file mode 100644 index 000000000..4c45cb164 Binary files /dev/null and b/test-resources/RealMpc60/Snd/Mpc60V214Rock/COWBEL#1.SND differ diff --git a/test-resources/RealMpc60/Snd/Mpc60V214Rock/CRASH2_H.SND b/test-resources/RealMpc60/Snd/Mpc60V214Rock/CRASH2_H.SND new file mode 100644 index 000000000..67f456d05 Binary files /dev/null and b/test-resources/RealMpc60/Snd/Mpc60V214Rock/CRASH2_H.SND differ diff --git a/test-resources/RealMpc60/Snd/Mpc60V214Rock/CRASH2_L.SND b/test-resources/RealMpc60/Snd/Mpc60V214Rock/CRASH2_L.SND new file mode 100644 index 000000000..9d1db03a7 Binary files /dev/null and b/test-resources/RealMpc60/Snd/Mpc60V214Rock/CRASH2_L.SND differ diff --git a/test-resources/RealMpc60/Snd/Mpc60V214Rock/CRASH_MU.SND b/test-resources/RealMpc60/Snd/Mpc60V214Rock/CRASH_MU.SND new file mode 100644 index 000000000..c5949b007 Binary files /dev/null and b/test-resources/RealMpc60/Snd/Mpc60V214Rock/CRASH_MU.SND differ diff --git a/test-resources/RealMpc60/Snd/Mpc60V214Rock/GONG.SND b/test-resources/RealMpc60/Snd/Mpc60V214Rock/GONG.SND new file mode 100644 index 000000000..fcb74a946 Binary files /dev/null and b/test-resources/RealMpc60/Snd/Mpc60V214Rock/GONG.SND differ diff --git a/test-resources/RealMpc60/Snd/Mpc60V214Rock/HAT2CLSD.SND b/test-resources/RealMpc60/Snd/Mpc60V214Rock/HAT2CLSD.SND new file mode 100644 index 000000000..c8734a8a8 Binary files /dev/null and b/test-resources/RealMpc60/Snd/Mpc60V214Rock/HAT2CLSD.SND differ diff --git a/test-resources/RealMpc60/Snd/Mpc60V214Rock/HAT2MED.SND b/test-resources/RealMpc60/Snd/Mpc60V214Rock/HAT2MED.SND new file mode 100644 index 000000000..0d78ef536 Binary files /dev/null and b/test-resources/RealMpc60/Snd/Mpc60V214Rock/HAT2MED.SND differ diff --git a/test-resources/RealMpc60/Snd/Mpc60V214Rock/HAT2OPN.SND b/test-resources/RealMpc60/Snd/Mpc60V214Rock/HAT2OPN.SND new file mode 100644 index 000000000..4d3c77193 Binary files /dev/null and b/test-resources/RealMpc60/Snd/Mpc60V214Rock/HAT2OPN.SND differ diff --git a/test-resources/RealMpc60/Snd/Mpc60V214Rock/KICK#2.SND b/test-resources/RealMpc60/Snd/Mpc60V214Rock/KICK#2.SND new file mode 100644 index 000000000..f3eb1ea4a Binary files /dev/null and b/test-resources/RealMpc60/Snd/Mpc60V214Rock/KICK#2.SND differ diff --git a/test-resources/RealMpc60/Snd/Mpc60V214Rock/METALSHO.SND b/test-resources/RealMpc60/Snd/Mpc60V214Rock/METALSHO.SND new file mode 100644 index 000000000..a21b9f866 Binary files /dev/null and b/test-resources/RealMpc60/Snd/Mpc60V214Rock/METALSHO.SND differ diff --git a/test-resources/RealMpc60/Snd/Mpc60V214Rock/RIDE4B.SND b/test-resources/RealMpc60/Snd/Mpc60V214Rock/RIDE4B.SND new file mode 100644 index 000000000..9ea3bcc2e Binary files /dev/null and b/test-resources/RealMpc60/Snd/Mpc60V214Rock/RIDE4B.SND differ diff --git a/test-resources/RealMpc60/Snd/Mpc60V214Rock/ROCTOMID.SND b/test-resources/RealMpc60/Snd/Mpc60V214Rock/ROCTOMID.SND new file mode 100644 index 000000000..cea7e084e Binary files /dev/null and b/test-resources/RealMpc60/Snd/Mpc60V214Rock/ROCTOMID.SND differ diff --git a/test-resources/RealMpc60/Snd/Mpc60V214Rock/ROCTOMLO.SND b/test-resources/RealMpc60/Snd/Mpc60V214Rock/ROCTOMLO.SND new file mode 100644 index 000000000..0725cc53b Binary files /dev/null and b/test-resources/RealMpc60/Snd/Mpc60V214Rock/ROCTOMLO.SND differ diff --git a/test-resources/RealMpc60/Snd/Mpc60V214Rock/SIDESTK2.SND b/test-resources/RealMpc60/Snd/Mpc60V214Rock/SIDESTK2.SND new file mode 100644 index 000000000..b0b07dc60 Binary files /dev/null and b/test-resources/RealMpc60/Snd/Mpc60V214Rock/SIDESTK2.SND differ diff --git a/test-resources/RealMpc60/Snd/Mpc60V214Rock/SNARE#2.SND b/test-resources/RealMpc60/Snd/Mpc60V214Rock/SNARE#2.SND new file mode 100644 index 000000000..4059bb52b Binary files /dev/null and b/test-resources/RealMpc60/Snd/Mpc60V214Rock/SNARE#2.SND differ diff --git a/test-resources/RealMpc60/Snd/SOUND002.SND b/test-resources/RealMpc60/Snd/SOUND002.SND new file mode 100644 index 000000000..8a87e23a9 Binary files /dev/null and b/test-resources/RealMpc60/Snd/SOUND002.SND differ