// SPDX-License-Identifier: BSD-2-Clause // This code is part of the sfizz library and is licensed under a BSD 2-clause // license. You should have receive a LICENSE.md file along with the code. // If not, contact the sfizz maintainers at https://github.com/sfztools/sfizz #include "InstrumentDescription.h" #include "MessageUtils.h" #include "sfizz.hpp" #include #include #include #include #include #include template static void bufferedStrCat(std::string* buffer, const Args&... args) { buffer->clear(); absl::StrAppend(buffer, args...); } std::string getDescriptionBlob(sfizz_synth_t* handle) { std::string blob; blob.reserve(128 * 1024); std::vector msgbuf; msgbuf.resize(1024); std::string pathbuf; pathbuf.reserve(256); struct ClientData { sfz::Sfizz* synth = nullptr; sfz::Client* client = nullptr; std::string* blob = nullptr; std::vector* msgbuf = nullptr; std::string* pathbuf = nullptr; }; sfz::Sfizz synth(handle); ClientData cdata; sfz::ClientPtr client = synth.createClient(&cdata); cdata.synth = &synth; cdata.client = client.get(); cdata.blob = &blob; cdata.msgbuf = &msgbuf; cdata.pathbuf = &pathbuf; synth.setReceiveCallback(*client, [](void* data, int, const char* path, const char* sig, const sfizz_arg_t* args) { ClientData& cdata = *reinterpret_cast(data); unsigned indices[8]; /// uint32_t msglen = sfizz_prepare_message(cdata.msgbuf->data(), cdata.msgbuf->size(), path, sig, args); if (msglen > cdata.msgbuf->size()) { cdata.msgbuf->resize(msglen); sfizz_prepare_message(cdata.msgbuf->data(), cdata.msgbuf->size(), path, sig, args); } cdata.blob->append(cdata.msgbuf->data(), msglen); /// if (Messages::matchOSC("/key/slots", path, indices) && !strcmp(sig, "b")) { ConstBitSpan bits(args[0].b->data, 8 * args[0].b->size); for (unsigned key = 0; key < 128 && key < bits.bit_size(); ++key) { if (bits.test(key)) { bufferedStrCat(cdata.pathbuf, "/key", key, "/label"); cdata.synth->sendMessage(*cdata.client, 0, cdata.pathbuf->c_str(), "", nullptr); } } } else if (Messages::matchOSC("/sw/last/slots", path, indices) && !strcmp(sig, "b")) { ConstBitSpan bits(args[0].b->data, 8 * args[0].b->size); for (unsigned key = 0; key < 128 && key < bits.bit_size(); ++key) { if (bits.test(key)) { bufferedStrCat(cdata.pathbuf, "/sw/last/", key, "/label"); cdata.synth->sendMessage(*cdata.client, 0, cdata.pathbuf->c_str(), "", nullptr); } } } else if (Messages::matchOSC("/cc/slots", path, indices) && !strcmp(sig, "b")) { ConstBitSpan bits(args[0].b->data, 8 * args[0].b->size); for (unsigned cc = 0; cc < sfz::config::numCCs && cc < bits.bit_size(); ++cc) { if (bits.test(cc)) { bufferedStrCat(cdata.pathbuf, "/cc", cc, "/label"); cdata.synth->sendMessage(*cdata.client, 0, cdata.pathbuf->c_str(), "", nullptr); bufferedStrCat(cdata.pathbuf, "/cc", cc, "/default"); cdata.synth->sendMessage(*cdata.client, 0, cdata.pathbuf->c_str(), "", nullptr); bufferedStrCat(cdata.pathbuf, "/cc", cc, "/value"); cdata.synth->sendMessage(*cdata.client, 0, cdata.pathbuf->c_str(), "", nullptr); } } } }); synth.sendMessage(*client, 0, "/num_regions", "", nullptr); synth.sendMessage(*client, 0, "/num_groups", "", nullptr); synth.sendMessage(*client, 0, "/num_masters", "", nullptr); synth.sendMessage(*client, 0, "/num_curves", "", nullptr); synth.sendMessage(*client, 0, "/num_samples", "", nullptr); synth.sendMessage(*client, 0, "/root_path", "", nullptr); synth.sendMessage(*client, 0, "/image", "", nullptr); synth.sendMessage(*client, 0, "/image_controls", "", nullptr); synth.sendMessage(*client, 0, "/key/slots", "", nullptr); synth.sendMessage(*client, 0, "/sw/last/slots", "", nullptr); synth.sendMessage(*client, 0, "/cc/slots", "", nullptr); synth.sendMessage(*client, 0, "/sustain_or_sostenuto/slots", "", nullptr); blob.shrink_to_fit(); return blob; } InstrumentDescription parseDescriptionBlob(absl::string_view blob) { InstrumentDescription desc; const uint8_t* src = reinterpret_cast(blob.data()); uint32_t srcSize = blob.size(); char buffer[1024]; const char* path; const char* sig; const sfizz_arg_t* args; int32_t byteCount; while ((byteCount = sfizz_extract_message(src, srcSize, buffer, sizeof(buffer), &path, &sig, &args)) > 0) { unsigned indices[8]; /// auto copyArgToBitSpan = [](const sfizz_arg_t& arg, BitSpan bits) { size_t size = std::min(bits.byte_size(), arg.b->size); memcpy(bits.data(), arg.b->data, size); }; // if (Messages::matchOSC("/num_regions", path, indices) && !strcmp(sig, "i")) desc.numRegions = uint32_t(args[0].i); else if (Messages::matchOSC("/num_groups", path, indices) && !strcmp(sig, "i")) desc.numGroups = uint32_t(args[0].i); else if (Messages::matchOSC("/num_masters", path, indices) && !strcmp(sig, "i")) desc.numMasters = uint32_t(args[0].i); else if (Messages::matchOSC("/num_curves", path, indices) && !strcmp(sig, "i")) desc.numCurves = uint32_t(args[0].i); else if (Messages::matchOSC("/num_samples", path, indices) && !strcmp(sig, "i")) desc.numSamples = uint32_t(args[0].i); else if (Messages::matchOSC("/root_path", path, indices) && !strcmp(sig, "s")) desc.rootPath = args[0].s; else if (Messages::matchOSC("/image", path, indices) && !strcmp(sig, "s")) desc.image = args[0].s; else if (Messages::matchOSC("/image_controls", path, indices) && !strcmp(sig, "s")) desc.image_controls = args[0].s; else if (Messages::matchOSC("/key/slots", path, indices) && !strcmp(sig, "b")) copyArgToBitSpan(args[0], desc.keyUsed.span()); else if (Messages::matchOSC("/sw/last/slots", path, indices) && !strcmp(sig, "b")) copyArgToBitSpan(args[0], desc.keyswitchUsed.span()); else if (Messages::matchOSC("/cc/slots", path, indices) && !strcmp(sig, "b")) copyArgToBitSpan(args[0], desc.ccUsed.span()); else if (Messages::matchOSC("/sustain_or_sostenuto/slots", path, indices) && !strcmp(sig, "b")) copyArgToBitSpan(args[0], desc.sustainOrSostenuto.span()); else if (Messages::matchOSC("/key&/label", path, indices) && !strcmp(sig, "s")) desc.keyLabel[indices[0]] = args[0].s; else if (Messages::matchOSC("/sw/last/&/label", path, indices) && !strcmp(sig, "s")) desc.keyswitchLabel[indices[0]] = args[0].s; else if (Messages::matchOSC("/cc&/label", path, indices) && !strcmp(sig, "s")) desc.ccLabel[indices[0]] = args[0].s; else if (Messages::matchOSC("/cc&/default", path, indices) && !strcmp(sig, "f")) desc.ccDefault[indices[0]] = args[0].f; else if (Messages::matchOSC("/cc&/value", path, indices) && !strcmp(sig, "f")) desc.ccValue[indices[0]] = args[0].f; src += byteCount; srcSize -= byteCount; } return desc; } std::ostream& operator<<(std::ostream& os, const InstrumentDescription& desc) { os << "instrument:\n"; os << " regions: " << desc.numRegions << "\n"; os << " groups: " << desc.numGroups << "\n"; os << " masters: " << desc.numMasters << "\n"; os << " curves: " << desc.numCurves << "\n"; os << " samples: " << desc.numSamples << "\n"; os << " root_path: " << desc.rootPath << "\n"; os << " image: " << desc.image << "\n"; os << " image_controls: " << desc.image_controls << "\n"; os << " keys:\n"; for (unsigned i = 0; i < 128; ++i) { if (desc.keyUsed.test(i)) { os << " - number: " << i << "\n"; if (!desc.keyLabel[i].empty()) os << " label: " << desc.keyLabel[i].c_str() << "\n"; } } os << " keyswitches:\n"; for (unsigned i = 0; i < 128; ++i) { if (desc.keyswitchUsed.test(i)) { os << " - number: " << i << "\n"; if (!desc.keyswitchLabel[i].empty()) os << " label: " << desc.keyswitchLabel[i].c_str() << "\n"; } } os << " cc:\n"; for (unsigned i = 0; i < sfz::config::numCCs; ++i) { if (desc.ccUsed.test(i)) { os << " - number: " << i << "\n"; os << " default: " << desc.ccDefault[i] << "\n"; if (!desc.ccLabel[i].empty()) os << " label: " << desc.ccLabel[i].c_str() << "\n"; } } return os; }