From f205d626e0fbcd98f3c0459b739d6688b19b157a Mon Sep 17 00:00:00 2001 From: David Robillard Date: Tue, 15 Mar 2016 18:01:14 -0400 Subject: Support sequences with beat time stamps Based on a patch from Hanspeter Portner. --- NEWS | 5 ++-- src/sratom.c | 83 +++++++++++++++++++++++++++++++++++++++-------------- tests/sratom_test.c | 32 +++++++++++++++++---- wscript | 2 +- 4 files changed, 92 insertions(+), 30 deletions(-) diff --git a/NEWS b/NEWS index 4cf5c78..6e2af0c 100644 --- a/NEWS +++ b/NEWS @@ -1,9 +1,10 @@ -sratom (0.4.7) unstable; +sratom (0.4.9) unstable; + * Support sequences with beat time stamps * Fix warnings when building with ISO C++ compilers * Upgrade to waf 1.8.14 - -- David Robillard Fri, 02 Oct 2015 21:03:46 -0400 + -- David Robillard Tue, 15 Mar 2016 17:57:49 -0400 sratom (0.4.6) stable; diff --git a/src/sratom.c b/src/sratom.c index e0996c4..6231d4a 100644 --- a/src/sratom.c +++ b/src/sratom.c @@ -41,6 +41,8 @@ struct SratomImpl { LV2_URID_Map* map; LV2_Atom_Forge forge; LV2_URID atom_Event; + LV2_URID atom_frameTime; + LV2_URID atom_beatTime; LV2_URID midi_MidiEvent; unsigned next_id; SerdNode base_uri; @@ -49,9 +51,11 @@ struct SratomImpl { void* handle; SratomObjectMode object_mode; bool pretty_numbers; + uint32_t seq_unit; struct { SordNode* atom_childType; SordNode* atom_frameTime; + SordNode* atom_beatTime; SordNode* rdf_first; SordNode* rdf_rest; SordNode* rdf_type; @@ -75,6 +79,8 @@ sratom_new(LV2_URID_Map* map) Sratom* sratom = (Sratom*)malloc(sizeof(Sratom)); sratom->map = map; sratom->atom_Event = map->map(map->handle, LV2_ATOM__Event); + sratom->atom_frameTime = map->map(map->handle, LV2_ATOM__frameTime); + sratom->atom_beatTime = map->map(map->handle, LV2_ATOM__beatTime); sratom->midi_MidiEvent = map->map(map->handle, LV2_MIDI__MidiEvent); sratom->next_id = 0; sratom->base_uri = SERD_NODE_NULL; @@ -206,6 +212,22 @@ path_is_absolute(const char* path) && (path[2] == '/' || path[2] == '\\'))); } +static SerdNode +number_type(const Sratom* sratom, const uint8_t* type) +{ + if (sratom->pretty_numbers && + (!strcmp((const char*)type, (const char*)NS_XSD "int") || + !strcmp((const char*)type, (const char*)NS_XSD "long"))) { + return serd_node_from_string(SERD_URI, NS_XSD "integer"); + } else if (sratom->pretty_numbers && + (!strcmp((const char*)type, (const char*)NS_XSD "float") || + !strcmp((const char*)type, (const char*)NS_XSD "double"))) { + return serd_node_from_string(SERD_URI, NS_XSD "decimal"); + } else { + return serd_node_from_string(SERD_URI, (const uint8_t*)type); + } +} + SRATOM_API int sratom_write(Sratom* sratom, @@ -285,23 +307,19 @@ sratom_write(Sratom* sratom, } else if (type_urid == sratom->forge.Int) { new_node = true; object = serd_node_new_integer(*(const int32_t*)body); - datatype = serd_node_from_string(SERD_URI, (sratom->pretty_numbers) - ? NS_XSD "integer" : NS_XSD "int"); + datatype = number_type(sratom, NS_XSD "int"); } else if (type_urid == sratom->forge.Long) { new_node = true; object = serd_node_new_integer(*(const int64_t*)body); - datatype = serd_node_from_string(SERD_URI, (sratom->pretty_numbers) - ? NS_XSD "integer" : NS_XSD "long"); + datatype = number_type(sratom, NS_XSD "long"); } else if (type_urid == sratom->forge.Float) { new_node = true; object = serd_node_new_decimal(*(const float*)body, 8); - datatype = serd_node_from_string(SERD_URI, (sratom->pretty_numbers) - ? NS_XSD "decimal" : NS_XSD "float"); + datatype = number_type(sratom, NS_XSD "float"); } else if (type_urid == sratom->forge.Double) { new_node = true; object = serd_node_new_decimal(*(const double*)body, 16); - datatype = serd_node_from_string(SERD_URI, (sratom->pretty_numbers) - ? NS_XSD "decimal" : NS_XSD "double"); + datatype = number_type(sratom, NS_XSD "double"); } else if (type_urid == sratom->forge.Bool) { const int32_t val = *(const int32_t*)body; datatype = serd_node_from_string(SERD_URI, NS_XSD "boolean"); @@ -320,11 +338,17 @@ sratom_write(Sratom* sratom, const LV2_Atom_Event* ev = (const LV2_Atom_Event*)body; gensym(&id, 'e', sratom->next_id++); start_object(sratom, &flags, subject, predicate, &id, NULL); - // TODO: beat time - SerdNode time = serd_node_new_integer(ev->time.frames); - SerdNode p = serd_node_from_string(SERD_URI, - USTR(LV2_ATOM__frameTime)); - datatype = serd_node_from_string(SERD_URI, NS_XSD "decimal"); + SerdNode time; + SerdNode p; + if (sratom->seq_unit == sratom->atom_beatTime) { + time = serd_node_new_decimal(ev->time.beats, 16); + p = serd_node_from_string(SERD_URI, USTR(LV2_ATOM__beatTime)); + datatype = number_type(sratom, NS_XSD "double"); + } else { + time = serd_node_new_integer(ev->time.frames); + p = serd_node_from_string(SERD_URI, USTR(LV2_ATOM__frameTime)); + datatype = number_type(sratom, NS_XSD "long"); + } sratom->write_statement(sratom->handle, SERD_ANON_CONT, NULL, &id, &p, &time, &datatype, &language); serd_node_free(&time); @@ -399,6 +423,7 @@ sratom_write(Sratom* sratom, SerdNode p = serd_node_from_string(SERD_URI, NS_RDF "value"); flags |= SERD_LIST_O_BEGIN; LV2_ATOM_SEQUENCE_BODY_FOREACH(seq, size, ev) { + sratom->seq_unit = seq->unit; list_append(sratom, unmap, &flags, &id, &p, &node, sizeof(LV2_Atom_Event) + ev->body.size, sratom->atom_Event, @@ -646,20 +671,34 @@ read_node(Sratom* sratom, LV2_Atom_Forge_Frame frame = { 0, 0 }; if (mode == MODE_SEQUENCE) { - SordNode* frame_time = sord_get( - model, node, sratom->nodes.atom_frameTime, NULL, NULL); - const char* frame_time_str = frame_time - ? (const char*)sord_node_get_string(frame_time) - : ""; - lv2_atom_forge_frame_time(forge, serd_strtod(frame_time_str, NULL)); + SordNode* time = sord_get( + model, node, sratom->nodes.atom_beatTime, NULL, NULL); + uint32_t seq_unit; + if (time) { + const char* time_str = (const char*)sord_node_get_string(time); + lv2_atom_forge_beat_time(forge, serd_strtod(time_str, NULL)); + seq_unit = sratom->atom_beatTime; + } else { + time = sord_get(model, node, sratom->nodes.atom_frameTime, NULL, NULL); + const char* time_str = time + ? (const char*)sord_node_get_string(time) + : ""; + lv2_atom_forge_frame_time(forge, serd_strtod(time_str, NULL)); + seq_unit = sratom->atom_frameTime; + } read_node(sratom, forge, world, model, value, MODE_BODY); - sord_node_free(world, frame_time); + sord_node_free(world, time); + sratom->seq_unit = seq_unit; } else if (type_urid == sratom->forge.Tuple) { lv2_atom_forge_tuple(forge, &frame); read_list_value(sratom, forge, world, model, value, MODE_BODY); } else if (type_urid == sratom->forge.Sequence) { - lv2_atom_forge_sequence_head(forge, &frame, 0); + const LV2_Atom_Forge_Ref ref = lv2_atom_forge_sequence_head(forge, &frame, 0); + sratom->seq_unit = 0; read_list_value(sratom, forge, world, model, value, MODE_SEQUENCE); + + LV2_Atom_Sequence* seq = (LV2_Atom_Sequence*)lv2_atom_forge_deref(forge, ref); + seq->body.unit = (sratom->seq_unit == sratom->atom_frameTime) ? 0 : sratom->seq_unit; } else if (type_urid == sratom->forge.Vector) { SordNode* child_type_node = sord_get( model, node, sratom->nodes.atom_childType, NULL, NULL); @@ -706,6 +745,7 @@ sratom_read(Sratom* sratom, { sratom->nodes.atom_childType = sord_new_uri(world, USTR(LV2_ATOM__childType)); sratom->nodes.atom_frameTime = sord_new_uri(world, USTR(LV2_ATOM__frameTime)); + sratom->nodes.atom_beatTime = sord_new_uri(world, USTR(LV2_ATOM__beatTime)); sratom->nodes.rdf_first = sord_new_uri(world, NS_RDF "first"); sratom->nodes.rdf_rest = sord_new_uri(world, NS_RDF "rest"); sratom->nodes.rdf_type = sord_new_uri(world, NS_RDF "type"); @@ -721,6 +761,7 @@ sratom_read(Sratom* sratom, sord_node_free(world, sratom->nodes.rdf_rest); sord_node_free(world, sratom->nodes.rdf_first); sord_node_free(world, sratom->nodes.atom_frameTime); + sord_node_free(world, sratom->nodes.atom_beatTime); sord_node_free(world, sratom->nodes.atom_childType); memset(&sratom->nodes, 0, sizeof(sratom->nodes)); } diff --git a/tests/sratom_test.c b/tests/sratom_test.c index 49b9357..90a81cd 100644 --- a/tests/sratom_test.c +++ b/tests/sratom_test.c @@ -112,7 +112,8 @@ test(bool top_level, bool pretty_numbers) LV2_URID eg_fvector = urid_map(NULL, "http://example.org/t-fvector"); LV2_URID eg_dvector = urid_map(NULL, "http://example.org/u-dvector"); LV2_URID eg_bvector = urid_map(NULL, "http://example.org/v-bvector"); - LV2_URID eg_seq = urid_map(NULL, "http://example.org/x-seq"); + LV2_URID eg_fseq = urid_map(NULL, "http://example.org/w-fseq"); + LV2_URID eg_bseq = urid_map(NULL, "http://example.org/x-bseq"); uint8_t buf[1024]; lv2_atom_forge_set_buffer(&forge, buf, sizeof(buf)); @@ -248,11 +249,11 @@ test(bool top_level, bool pretty_numbers) int32_t belems[] = { true, false }; lv2_atom_forge_vector(&forge, sizeof(int32_t), forge.Bool, 2, belems); - // eg_seq = (Sequence)1, 2 + // eg_fseq = (Sequence)1, 2 LV2_URID midi_midiEvent = map.map(map.handle, LV2_MIDI__MidiEvent); - lv2_atom_forge_key(&forge, eg_seq); - LV2_Atom_Forge_Frame seq_frame; - lv2_atom_forge_sequence_head(&forge, &seq_frame, 0); + lv2_atom_forge_key(&forge, eg_fseq); + LV2_Atom_Forge_Frame fseq_frame; + lv2_atom_forge_sequence_head(&forge, &fseq_frame, 0); const uint8_t ev1[3] = { 0x90, 0x1A, 0x1 }; lv2_atom_forge_frame_time(&forge, 1); @@ -266,7 +267,26 @@ test(bool top_level, bool pretty_numbers) lv2_atom_forge_raw(&forge, ev2, sizeof(ev2)); lv2_atom_forge_pad(&forge, sizeof(ev2)); - lv2_atom_forge_pop(&forge, &seq_frame); + lv2_atom_forge_pop(&forge, &fseq_frame); + + // eg_bseq = (Sequence)1.1, 2.2 + LV2_URID atom_beatTime = map.map(map.handle, LV2_ATOM__beatTime); + lv2_atom_forge_key(&forge, eg_bseq); + LV2_Atom_Forge_Frame bseq_frame; + lv2_atom_forge_sequence_head(&forge, &bseq_frame, atom_beatTime); + + lv2_atom_forge_beat_time(&forge, 1.0); + lv2_atom_forge_atom(&forge, sizeof(ev1), midi_midiEvent); + lv2_atom_forge_raw(&forge, ev1, sizeof(ev1)); + lv2_atom_forge_pad(&forge, sizeof(ev1)); + + lv2_atom_forge_beat_time(&forge, 2.0); + lv2_atom_forge_atom(&forge, sizeof(ev2), midi_midiEvent); + lv2_atom_forge_raw(&forge, ev2, sizeof(ev2)); + lv2_atom_forge_pad(&forge, sizeof(ev2)); + + lv2_atom_forge_pop(&forge, &bseq_frame); + lv2_atom_forge_pop(&forge, &obj_frame); const char* base_uri = "file:///tmp/base/"; diff --git a/wscript b/wscript index 8fd2a5c..b661f34 100644 --- a/wscript +++ b/wscript @@ -8,7 +8,7 @@ import waflib.extras.autowaf as autowaf # major increment <=> incompatible changes # minor increment <=> compatible changes (additions) # micro increment <=> no interface changes -SRATOM_VERSION = '0.4.7' +SRATOM_VERSION = '0.4.9' SRATOM_MAJOR_VERSION = '0' # Mandatory waf variables -- cgit v1.2.1