/* * MIT License * * Copyright (c) 2023-2024 Robin E. R. Davies * * Permission is hereby granted, free of charge, to any person obtaining a copy of * this software and associated documentation files (the "Software"), to deal in * the Software without restriction, including without limitation the rights to * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies * of the Software, and to permit persons to whom the Software is furnished to do * so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ #include "AtomConverter.hpp" #include #include #include #include "json.hpp" #include "ss.hpp" #include "lv2/atom/util.h" #include "ss.hpp" using namespace pipedal; const std::string AtomConverter::ID_TAG {"id_"}; const std::string AtomConverter::OTYPE_TAG {"otype_"}; const std::string AtomConverter::VTYPE_TAG {"vtype_"}; #define SHORT_ATOM__Bool "Bool" #define SHORT_ATOM__Float "Float" #define SHORT_ATOM__Int "Int" #define SHORT_ATOM__Long "Long" #define SHORT_ATOM__Double "Double" #define SHORT_ATOM__Vector "Vector" #define SHORT_ATOM__Object "Object" #define SHORT_ATOM__Tuple "Tuple" #define SHORT_ATOM__URI "URI" #define SHORT_ATOM__URID "URID" #define SHORT_ATOM__String "String" #define SHORT_ATOM__Path "Path" AtomConverter::AtomConverter(MapFeature &map) : map(map) { InitUrids(); if (prototype) { this->prototypeBuffer.resize(prototype->size+sizeof(LV2_Atom)); std::memcpy(&(this->prototypeBuffer[0]), prototype, this->prototypeBuffer.size()); this->prototype = (LV2_Atom*)&(prototype[0]); } lv2_atom_forge_init(&outputForge, map.GetMap()); } json_variant AtomConverter::ToJson(const LV2_Atom *atom) { json_variant variant = ToVariant(const_cast(atom)); return std::move(variant); } LV2_Atom*AtomConverter::ToAtom(const std::string&jsonString) { json_variant jvValue; std::istringstream ss(jsonString); json_reader reader(ss); reader.read(&jvValue); return ToAtom(jvValue); } std::string AtomConverter::ToString(const LV2_Atom*atom) { json_variant v = ToJson(atom); std::ostringstream ss; json_writer writer(ss); writer.write(v); return ss.str(); } json_variant AtomConverter::MapPath(const json_variant&json, const std::string &pluginStoragePath) { std::string oType = json[OTYPE_TAG].as_string(); if (oType == SHORT_ATOM__Path) { std::string path = json["value"].as_string().c_str(); if (path.length() == 0 || path[0] == '/') { return json; } std::string newPath = SS(pluginStoragePath << "/" << path); json_variant result = json_variant::make_object(); result[OTYPE_TAG] = json_variant(SHORT_ATOM__Path); result["value"] = newPath; return result; } else { return json; } } json_variant AtomConverter::AbstractPath(const json_variant&json, const std::string &pluginStoragePath) { std::string oType = json[OTYPE_TAG].as_string(); if (oType == SHORT_ATOM__Path) { std::string path = json["value"].as_string().c_str(); if (path.length() == 0) { return json; } if (path.starts_with(pluginStoragePath)) { auto pos = pluginStoragePath.length(); if (pos < path.length() && path[pos] == '/') { ++pos; } path = path.substr(pos); } json_variant result = json_variant::make_object(); result[OTYPE_TAG] = json_variant(SHORT_ATOM__Path); result["value"] = path; return result; } else { return json; } } LV2_Atom*AtomConverter::ToAtom(const json_variant&json) { if (outputBuffer.size() == 0) { outputBuffer.resize(512); } while (true) { try { LV2_Atom *result = (LV2_Atom*)(&outputBuffer[0]); result->size = outputBuffer.size(); lv2_atom_forge_set_buffer(&outputForge,(uint8_t*)&(outputBuffer[0]),outputBuffer.size()); ToForge(json); return (LV2_Atom*)(&outputBuffer[0]); } catch (const BufferOverflowException&) { } if (outputBuffer.size() >= 1024*1024) { throw std::logic_error("Atom is too large."); } outputBuffer.resize(outputBuffer.size()*2); } } static inline void *AtomContent(LV2_Atom*atom,size_t offset = 0) { // always % sizeof(LV2_Atom) return (void*)((char*)atom + sizeof(LV2_Atom)+offset); } static inline LV2_Atom*NextAtom(LV2_Atom*atom) { size_t size = sizeof(LV2_Atom) + (atom->size +(sizeof(LV2_Atom)-1))/sizeof(LV2_Atom)*sizeof(LV2_Atom); return (LV2_Atom*)((char*)atom + size); } json_variant AtomConverter::ToVariant(LV2_Atom *atom) { if (atom->type == urids.ATOM__Float) { LV2_Atom_Float*pVal = (LV2_Atom_Float*)atom; return json_variant((double)pVal->body); } if (atom->type == urids.ATOM__Bool) { LV2_Atom_Bool *pVal = (LV2_Atom_Bool*)atom; return json_variant(pVal->body != 0); } if (atom->type == urids.ATOM__Int) { LV2_Atom_Int *pVal = (LV2_Atom_Int*)atom; return TypedProperty(SHORT_ATOM__Int,(double)pVal->body); } if (atom->type == urids.ATOM__Long) { LV2_Atom_Long *pVal = (LV2_Atom_Long*)atom; return TypedProperty(SHORT_ATOM__Long,(double)pVal->body); } if (atom->type == urids.ATOM__Double) { LV2_Atom_Double*pVal = (LV2_Atom_Double*)atom; return TypedProperty(SHORT_ATOM__Double,(double)pVal->body); } if (atom->type == urids.ATOM__URID) { LV2_Atom_URID*pVal = (LV2_Atom_URID*)atom; const char*strValue = map.UridToString(pVal->body); return TypedProperty(SHORT_ATOM__URID,std::string(strValue)); } if (atom->type == urids.ATOM__String) { const char*p = (const char*)atom +sizeof(LV2_Atom); size_t size = atom->size; while (size > 0 && p[size-1] == '\0') { --size; } return json_variant(std::string(p,size)); } else if (atom->type == urids.ATOM__Path) { const char*p = (const char*)atom +sizeof(LV2_Atom); size_t size = atom->size; while (size > 0 && p[size-1] == '\0') { --size; } return TypedProperty(SHORT_ATOM__Path,std::string(p,size)); } else if (atom->type == urids.ATOM__URI) { const char*p = (const char*)atom +sizeof(LV2_Atom); size_t size = atom->size; while (size > 0 && p[size-1] == '\0') { --size; } return TypedProperty(SHORT_ATOM__URI,std::string(p,size)); } else if (atom->type == urids.ATOM__Tuple) { json_array array; LV2_Atom*current = (LV2_Atom*)AtomContent(atom,0); LV2_Atom*end = (LV2_Atom*)AtomContent(atom,atom->size); while (current < end) { array.push_back(ToVariant(current)); current = NextAtom(current); } json_variant vArray { std::move(array)}; return TypedProperty(SHORT_ATOM__Tuple,std::move(vArray)); } if (atom->type == urids.ATOM__URID) { LV2_Atom_URID *pVal = (LV2_Atom_URID*)atom; return TypedProperty(SHORT_ATOM__URID,std::string(map.UridToString(pVal->body))); } if (atom->type == urids.ATOM__Vector) { json_array array; LV2_Atom_Vector *pVal = (LV2_Atom_Vector*)atom; size_t n = (atom->size-sizeof(LV2_Atom_Vector_Body))/pVal->body.child_size; void *vectorData = AtomContent(atom,sizeof(LV2_Atom_Vector_Body)); if (pVal->body.child_type == urids.ATOM__Float) { float*p = (float*)vectorData; for (size_t i = 0; i < n; ++i) { array.push_back(json_variant((double)p[i])); } } else if (pVal->body.child_type == urids.ATOM__Int) { auto p = (int32_t*)vectorData; for (size_t i = 0; i < n; ++i) { array.push_back(json_variant((double)p[i])); } } else if (pVal->body.child_type == urids.ATOM__Bool) { auto p = (int32_t*)vectorData; for (size_t i = 0; i < n; ++i) { array.push_back(json_variant(p[i] != 0)); } } else if (pVal->body.child_type == urids.ATOM__Long) { auto p = (int64_t*)vectorData; for (size_t i = 0; i < n; ++i) { array.push_back(json_variant((double)(p[i]))); } } else if (pVal->body.child_type == urids.ATOM__Double) { auto p = (double*)vectorData; for (size_t i = 0; i < n; ++i) { array.push_back(json_variant((double)p[i])); } } else { std::string dataType = map.UridToString(pVal->body.child_type); throw std::logic_error("AtomConverter: Vector dataype not supported. (" + dataType + ") Please contact support if you get this message."); } json_variant vArray {std::move(array)}; json_variant object = json_variant::make_object(); object[OTYPE_TAG] = json_variant(std::string(SHORT_ATOM__Vector)); object[VTYPE_TAG] = json_variant(std::string(TypeUridToString(pVal->body.child_type))); object["value"] = std::move(vArray); return std::move(object); } else if (atom->type == urids.ATOM__Property) { throw std::logic_error("Not implemented."); } else if (atom->type == urids.ATOM__Object) { LV2_Atom_Object *pVal = (LV2_Atom_Object*)atom; json_variant result = json_variant::make_object(); if (pVal->body.id != 0) { result[ ID_TAG] = map.UridToString(pVal->body.id); } if (pVal->body.otype != 0) { result[OTYPE_TAG] = map.UridToString(pVal->body.otype); } LV2_Atom_Property_Body *current = (LV2_Atom_Property_Body *)AtomContent(atom,sizeof(LV2_Atom_Object_Body)); LV2_Atom_Property_Body *end = (LV2_Atom_Property_Body *)AtomContent(atom,atom->size); while (current < end) { std::string key = map.UridToString(current->key); json_variant value = ToVariant(¤t->value); result[key] = std::move(value); current = (LV2_Atom_Property_Body*)(NextAtom(&(current->value))); } return result; } throw std::logic_error( SS("AtomConverter: Datatype not supported. (" << map.UridToString(atom->type) << ") Please contact support if you get this message.")); } bool AtomConverter::AreTypesTheSame(LV2_Atom*left, LV2_Atom *right) const { if (left->type != right->type) return false; if (left->type == urids.ATOM__Object) { LV2_Atom_Object *l = (LV2_Atom_Object*)left; LV2_Atom_Object *r = (LV2_Atom_Object*)right; return l->body.id == r->body.id && l->body.otype == r->body.otype; } return true; } void AtomConverter::ObjectToForge(const json_variant&json) { LV2_URID bodyId = 0; assert(json.is_object()); std::string oType = json[OTYPE_TAG].as_string(); if (oType == SHORT_ATOM__Int) { lv2_atom_forge_int(&this->outputForge,(int32_t)json["value"].as_number()); } else if (oType == SHORT_ATOM__Long) { lv2_atom_forge_long(&this->outputForge,(int64_t)json["value"].as_number()); } else if (oType == SHORT_ATOM__Double) { lv2_atom_forge_double(&this->outputForge,(double)json["value"].as_number()); } else if (oType == SHORT_ATOM__Path) { std::string value = json["value"].as_string().c_str(); lv2_atom_forge_path(&this->outputForge,value.c_str(),value.length()+1); } else if (oType == SHORT_ATOM__URI) { std::string value = json["value"].as_string().c_str(); lv2_atom_forge_uri(&this->outputForge,value.c_str(),value.length()+1); } else if (oType == SHORT_ATOM__URID) { LV2_URID urid = map.GetUrid(json["value"].as_string().c_str()); lv2_atom_forge_urid(&outputForge,urid); } else if (oType == SHORT_ATOM__Tuple) { TupleToForge(json); } else if (oType == SHORT_ATOM__Vector) { VectorToForge(json); } else { LV2_URID id = 0; if (json.contains(ID_TAG)) { id = map.GetUrid(json[ID_TAG].as_string().c_str()); } LV2_URID oType = 0; if (json.contains(OTYPE_TAG)) { oType = map.GetUrid(json[OTYPE_TAG].as_string().c_str()); } LV2_Atom_Forge_Frame frame; lv2_atom_forge_object(&outputForge,&frame,id,oType); const auto& obj = json.as_object(); for (auto member: *obj.get()) { const std::string& property = member.first; if (property != OTYPE_TAG && property != ID_TAG) { LV2_URID property = map.GetUrid(member.first.c_str()); lv2_atom_forge_key(&outputForge,property); ToForge(member.second); } } lv2_atom_forge_pop(&outputForge,&frame); } } void AtomConverter::ToForge(const json_variant&json) { if (json.is_number()) { CheckResult(lv2_atom_forge_float(&outputForge,(float)json.as_number())); } else if (json.is_bool()) { CheckResult(lv2_atom_forge_bool(&outputForge,json.as_bool())); } else if (json.is_string()) { const std::string&str = json.as_string(); CheckResult(lv2_atom_forge_string(&outputForge,str.c_str(),str.size()+1)); } else if (json.is_object()) { return ObjectToForge(json); } else { throw std::logic_error("Malformed json atom."); } } void AtomConverter::VectorToForge(const json_variant&json) { const json_array& array = *(json["value"].as_array().get()); size_t size = array.size(); LV2_Atom_Forge_Frame frame; LV2_URID childType = GetTypeUrid(json[VTYPE_TAG].as_string().c_str()); if (childType == urids.ATOM__Float) { using T = float; CheckResult(lv2_atom_forge_vector_head(&outputForge,&frame,sizeof(T),childType)); for (size_t i = 0; i < size; ++i) { T value = (T)(array[i].as_number()); CheckResult(lv2_atom_forge_raw(&outputForge,&value,sizeof(value))); } // does this pad the frame? lv2_atom_forge_pop(&outputForge,&frame); lv2_atom_forge_pad(&outputForge,sizeof(LV2_Atom_Vector_Body)+size*sizeof(float)); } else if (childType == urids.ATOM__Int) { using T = int32_t; CheckResult(lv2_atom_forge_vector_head(&outputForge,&frame,sizeof(T),childType)); for (size_t i = 0; i < size; ++i) { T value = (T)(array[i].as_number()); CheckResult(lv2_atom_forge_raw(&outputForge,&value,sizeof(value))); } // does this pad the frame? lv2_atom_forge_pop(&outputForge,&frame); lv2_atom_forge_pad(&outputForge,sizeof(LV2_Atom_Vector_Body)+size*sizeof(float)); } else if (childType == urids.ATOM__Bool) { using T = int32_t; CheckResult(lv2_atom_forge_vector_head(&outputForge,&frame,sizeof(T),childType)); for (size_t i = 0; i < size; ++i) { T value = (T)(array[i].as_bool()); CheckResult(lv2_atom_forge_raw(&outputForge,&value,sizeof(value))); } // does this pad the frame? lv2_atom_forge_pop(&outputForge,&frame); lv2_atom_forge_pad(&outputForge,sizeof(LV2_Atom_Vector_Body)+size*sizeof(float)); } else if (childType == urids.ATOM__Long) { using T = int64_t; CheckResult(lv2_atom_forge_vector_head(&outputForge,&frame,sizeof(T),childType)); for (size_t i = 0; i < size; ++i) { T value = (T)(array[i].as_number()); CheckResult(lv2_atom_forge_raw(&outputForge,&value,sizeof(value))); } // does this pad the frame? lv2_atom_forge_pop(&outputForge,&frame); lv2_atom_forge_pad(&outputForge,sizeof(LV2_Atom_Vector_Body)+size*sizeof(float)); } else if (childType == urids.ATOM__Double) { using T = double; CheckResult(lv2_atom_forge_vector_head(&outputForge,&frame,sizeof(T),childType)); for (size_t i = 0; i < size; ++i) { T value = (T)(array[i].as_number()); CheckResult(lv2_atom_forge_raw(&outputForge,&value,sizeof(value))); } // does this pad the frame? lv2_atom_forge_pop(&outputForge,&frame); lv2_atom_forge_pad(&outputForge,sizeof(LV2_Atom_Vector_Body)+size*sizeof(float)); } else { std::string dataType = map.UridToString(childType); throw std::logic_error("AtomConverter: Vector dataype not supported. (" + dataType + ") Please contact support if you get this message."); } } void AtomConverter::TupleToForge(const json_variant&json) { LV2_Atom_Forge_Frame frame; const json_array&array = *(json["value"].as_array().get()); lv2_atom_forge_tuple(&outputForge,&frame); { for (const json_variant&v: array) { ToForge(v); } } lv2_atom_forge_pop(&outputForge,&frame); } LV2_URID AtomConverter::GetTypeUrid(const std::string uri) { if (stringToTypeUrid.find(uri) != stringToTypeUrid.end()) { return stringToTypeUrid[uri]; } return map.GetUrid(uri.c_str()); } LV2_URID AtomConverter::InitUrid(const char*uri, const char*shortUri ) { LV2_URID urid = map.GetUrid(uri); stringToTypeUrid[uri] = urid; stringToTypeUrid[shortUri] = urid; typeUridToString[urid] = shortUri; return urid; } std::string AtomConverter::TypeUridToString(LV2_URID urid) { if (typeUridToString.find(urid) != typeUridToString.end()) { return typeUridToString[urid]; } return map.UridToString(urid); } static bool gEmptyPathInitialized = false; static std::mutex gInitMutex; std::string gEmptyPathstring; std::string AtomConverter::EmptyPathstring() { std::lock_guard lock{gInitMutex}; if (!gEmptyPathInitialized) { json_variant v = TypedProperty(SHORT_ATOM__Path,std::string("",0)); std::ostringstream ss; json_writer writer(ss); writer.write(v); gEmptyPathstring = ss.str(); } return gEmptyPathstring; } void AtomConverter::InitUrids() { #define ATOM_INIT(name,shortName) urids.name = InitUrid(LV2_##name,#shortName) ATOM_INIT(ATOM__Bool,Bool); ATOM_INIT(ATOM__Chunk,Chunk); ATOM_INIT(ATOM__Double,Double); ATOM_INIT(ATOM__Event,Event); ATOM_INIT(ATOM__Float,Float); ATOM_INIT(ATOM__Int,Int); ATOM_INIT(ATOM__Literal,Literal); ATOM_INIT(ATOM__Long,Long); ATOM_INIT(ATOM__Number,Number); ATOM_INIT(ATOM__Object,Object); ATOM_INIT(ATOM__Path,Path); ATOM_INIT(ATOM__Property,Property); //ATOM_INIT(ATOM__Resource,Resource); //ATOM_INIT(ATOM__Sequence,Sequence); //ATOM_INIT(ATOM__Sound,Sound); ATOM_INIT(ATOM__String,String); ATOM_INIT(ATOM__Tuple,Tuple); ATOM_INIT(ATOM__Vector,Vector); ATOM_INIT(ATOM__URI,URI); ATOM_INIT(ATOM__URID,URID); ATOM_INIT(ATOM__Vector,Vector); //ATOM_INIT(ATOM__beatTime,beatTime); //ATOM_INIT(ATOM__frameTime,frameTime); //ATOM_INIT(ATOM__timeUnit,timeUnit); #undef ATOM_INIT }