Files
op-pedal/src/AtomConverter.cpp
T
Robin Davies a3dca9fa5c Toob Flanger, Stereo Reverb, bug fixes
- support state in factory presets.
- Use uncompressed json for settings files.
- Set current plugin preset name to last loaded plugin preset.
- Append plugins after  start node,  before end node fixed.
- Correctly release web socket when web app goes idle.
- Fit and finish issues.
2023-06-07 04:57:13 -04:00

548 lines
18 KiB
C++

/*
* MIT License
*
* Copyright (c) 2023 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 <cstddef>
#include <cstring>
#include <sstream>
#include "json.hpp"
#include "ss.hpp"
#include "lv2/atom/util.h"
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<LV2_Atom*>(atom));
return std::move(variant);
}
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(&current->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);
}
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
}