annotate projects/heavy/envelopeTrigger/HvMessage.c @ 162:c3e8226a5651 heavy-updated

- added additional flags to C rules (-DNDEBUG, -mfpu=neon) - sample-accurate envelope triggering pd/heavy example
author chnrx <chris.heinrichs@gmail.com>
date Thu, 12 Nov 2015 14:59:46 +0000
parents
children
rev   line source
chris@162 1 /**
chris@162 2 * Copyright (c) 2014, 2015, Enzien Audio Ltd.
chris@162 3 *
chris@162 4 * Permission to use, copy, modify, and/or distribute this software for any
chris@162 5 * purpose with or without fee is hereby granted, provided that the above
chris@162 6 * copyright notice and this permission notice appear in all copies.
chris@162 7 *
chris@162 8 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
chris@162 9 * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
chris@162 10 * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
chris@162 11 * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
chris@162 12 * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
chris@162 13 * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
chris@162 14 * PERFORMANCE OF THIS SOFTWARE.
chris@162 15 */
chris@162 16
chris@162 17 #include "HvMessage.h"
chris@162 18
chris@162 19 HvMessage *msg_init(HvMessage *m, hv_size_t numElements, hv_uint32_t timestamp) {
chris@162 20 m->timestamp = timestamp;
chris@162 21 m->numElements = (hv_uint16_t) numElements;
chris@162 22 m->numBytes = (hv_uint16_t) msg_getByteSize(numElements);
chris@162 23 return m;
chris@162 24 }
chris@162 25
chris@162 26 HvMessage *msg_initWithFloat(HvMessage *m, hv_uint32_t timestamp, float f) {
chris@162 27 m->timestamp = timestamp;
chris@162 28 m->numElements = 1;
chris@162 29 m->numBytes = sizeof(HvMessage);
chris@162 30 msg_setFloat(m, 0, f);
chris@162 31 return m;
chris@162 32 }
chris@162 33
chris@162 34 HvMessage *msg_initWithBang(HvMessage *m, hv_uint32_t timestamp) {
chris@162 35 m->timestamp = timestamp;
chris@162 36 m->numElements = 1;
chris@162 37 m->numBytes = sizeof(HvMessage);
chris@162 38 msg_setBang(m, 0);
chris@162 39 return m;
chris@162 40 }
chris@162 41
chris@162 42 HvMessage *msg_initWithSymbol(HvMessage *m, hv_uint32_t timestamp, char *s) {
chris@162 43 m->timestamp = timestamp;
chris@162 44 m->numElements = 1;
chris@162 45 m->numBytes = sizeof(HvMessage);
chris@162 46 msg_setSymbol(m, 0, s);
chris@162 47 return m;
chris@162 48 }
chris@162 49
chris@162 50 HvMessage *msg_initWithHash(HvMessage *m, hv_uint32_t timestamp, hv_uint32_t h) {
chris@162 51 m->timestamp = timestamp;
chris@162 52 m->numElements = 1;
chris@162 53 m->numBytes = sizeof(HvMessage);
chris@162 54 msg_setHash(m, 0, h);
chris@162 55 return m;
chris@162 56 }
chris@162 57
chris@162 58 HvMessage *msg_initV(HvMessage *const m, const hv_uint32_t timestamp, const char *format, ...) {
chris@162 59 va_list ap;
chris@162 60 va_start(ap, format);
chris@162 61
chris@162 62 const int numElem = (int) hv_strlen(format);
chris@162 63 msg_init(m, numElem, timestamp);
chris@162 64 for (int i = 0; i < numElem; i++) {
chris@162 65 switch (format[i]) {
chris@162 66 case 'b': msg_setBang(m,i); break;
chris@162 67 case 'f': msg_setFloat(m, i, (float) va_arg(ap, double)); break;
chris@162 68 case 's': msg_setSymbol(m, i, (char *) va_arg(ap, char *)); break;
chris@162 69 case 'h': // hash not supported
chris@162 70 default: break;
chris@162 71 }
chris@162 72 }
chris@162 73 va_end(ap);
chris@162 74
chris@162 75 return m;
chris@162 76 }
chris@162 77
chris@162 78 hv_size_t msg_getNumHeapBytes(const HvMessage *m) {
chris@162 79 // get the size of all symbol elements
chris@162 80 hv_size_t rsizeofsym = 0;
chris@162 81 for (int i = 0; i < msg_getNumElements(m); ++i) {
chris@162 82 if (msg_isSymbol(m,i)) {
chris@162 83 rsizeofsym += (hv_size_t) hv_strlen(msg_getSymbol(m,i)) + 1; // +1 to allow for trailing '\0'
chris@162 84 }
chris@162 85 }
chris@162 86
chris@162 87 // the total byte size on the heap
chris@162 88 return (msg_getByteSize(msg_getNumElements(m)) + rsizeofsym);
chris@162 89 }
chris@162 90
chris@162 91 void msg_copyToBuffer(const HvMessage *m, char *buffer, hv_size_t len) {
chris@162 92 HvMessage *r = (HvMessage *) buffer;
chris@162 93
chris@162 94 // assert that the message is not already larger than the length of the buffer
chris@162 95 hv_assert(msg_getNumBytes(m) <= len);
chris@162 96
chris@162 97 // copy the basic message to the buffer
chris@162 98 hv_memcpy(r, m, msg_getNumBytes(m));
chris@162 99
chris@162 100 hv_size_t len_r = msg_getNumBytes(m);
chris@162 101
chris@162 102 char *p = buffer + msg_getByteSize(msg_getNumElements(m)); // points to the end of the base message
chris@162 103 for (int i = 0; i < msg_getNumElements(m); ++i) {
chris@162 104 if (msg_isSymbol(m,i)) {
chris@162 105 const hv_size_t symLen = (hv_size_t) hv_strlen(msg_getSymbol(m,i)) + 1; // include the trailing null char
chris@162 106 hv_assert(len_r + symLen <= len); // stay safe!
chris@162 107 hv_strncpy(p, msg_getSymbol(m,i), symLen);
chris@162 108 msg_setSymbol(r, i, p);
chris@162 109 p += symLen;
chris@162 110 len_r += symLen;
chris@162 111 }
chris@162 112 }
chris@162 113
chris@162 114 r->numBytes = (hv_uint16_t) len_r; // update the message size in memory
chris@162 115 }
chris@162 116
chris@162 117 // the message is serialised such that all symbol elements are placed in order at the end of the buffer
chris@162 118 HvMessage *msg_copy(const HvMessage *m) {
chris@162 119 const hv_size_t heapSize = msg_getNumHeapBytes(m);
chris@162 120 char *r = (char *) hv_malloc(heapSize);
chris@162 121 msg_copyToBuffer(m, r, heapSize);
chris@162 122 return (HvMessage *) r;
chris@162 123 }
chris@162 124
chris@162 125 void msg_free(HvMessage *m) {
chris@162 126 hv_free(m); // because heap messages are serialised in memory, a simple call to free releases the message
chris@162 127 }
chris@162 128
chris@162 129 bool msg_hasFormat(const HvMessage *m, const char *fmt) {
chris@162 130 if (fmt == NULL) return false;
chris@162 131 if (msg_getNumElements(m) != hv_strlen(fmt)) return false;
chris@162 132 for (int i = 0; i < msg_getNumElements(m); i++) {
chris@162 133 switch (fmt[i]) {
chris@162 134 case 'b': if (!msg_isBang(m, i)) return false; break;
chris@162 135 case 'f': if (!msg_isFloat(m, i)) return false; break;
chris@162 136 case 's': if (!msg_isSymbol(m, i)) return false; break;
chris@162 137 case 'h': if (!msg_isHash(m, i)) return false; break;
chris@162 138 default: return false;
chris@162 139 }
chris@162 140 }
chris@162 141 return true;
chris@162 142 }
chris@162 143
chris@162 144 bool msg_compareSymbol(const HvMessage *m, int i, const char *s) {
chris@162 145 switch (msg_getType(m,i)) {
chris@162 146 case SYMBOL: return !hv_strcmp(msg_getSymbol(m, i), s);
chris@162 147 case HASH: return (msg_getHash(m,i) == msg_symbolToHash(s));
chris@162 148 default: return false;
chris@162 149 }
chris@162 150 }
chris@162 151
chris@162 152 bool msg_equalsElement(const HvMessage *m, int i_m, const HvMessage *n, int i_n) {
chris@162 153 if (i_m < msg_getNumElements(m) && i_n < msg_getNumElements(n)) {
chris@162 154 if (msg_getType(m, i_m) == msg_getType(n, i_n)) {
chris@162 155 switch (msg_getType(m, i_m)) {
chris@162 156 case BANG: return true;
chris@162 157 case FLOAT: return (msg_getFloat(m, i_m) == msg_getFloat(n, i_n));
chris@162 158 case SYMBOL: return msg_compareSymbol(m, i_m, msg_getSymbol(n, i_n));
chris@162 159 case HASH: return msg_getHash(m,i_m) == msg_getHash(n,i_n);
chris@162 160 default: break;
chris@162 161 }
chris@162 162 }
chris@162 163 }
chris@162 164 return false;
chris@162 165 }
chris@162 166
chris@162 167 void msg_setElementToFrom(HvMessage *n, int i_n, const HvMessage *const m, int i_m) {
chris@162 168 switch (msg_getType(m, i_m)) {
chris@162 169 case BANG: msg_setBang(n, i_n); break;
chris@162 170 case FLOAT: msg_setFloat(n, i_n, msg_getFloat(m, i_m)); break;
chris@162 171 case SYMBOL: msg_setSymbol(n, i_n, msg_getSymbol(m, i_m)); break;
chris@162 172 case HASH: msg_setHash(n, i_n, msg_getHash(m, i_m));
chris@162 173 default: break;
chris@162 174 }
chris@162 175 }
chris@162 176
chris@162 177 hv_uint32_t msg_symbolToHash(const char *s) {
chris@162 178 // this hash is based MurmurHash2
chris@162 179 // http://en.wikipedia.org/wiki/MurmurHash
chris@162 180 // https://sites.google.com/site/murmurhash/
chris@162 181 static const unsigned int n = 0x5bd1e995;
chris@162 182 static const int r = 24;
chris@162 183
chris@162 184 int len = (int) hv_strlen(s);
chris@162 185 hv_uint32_t x = (hv_uint32_t) (len); // seed (0) ^ len
chris@162 186
chris@162 187 while (len >= 4) {
chris@162 188 hv_uint32_t k = *((hv_uint32_t *)s);
chris@162 189 k *= n;
chris@162 190 k ^= k >> r;
chris@162 191 k *= n;
chris@162 192 x *= n;
chris@162 193 x ^= k;
chris@162 194 s += 4; len -= 4;
chris@162 195 }
chris@162 196
chris@162 197 switch(len) {
chris@162 198 case 3: x ^= s[2] << 16;
chris@162 199 case 2: x ^= s[1] << 8;
chris@162 200 case 1: x ^= s[0]; x *= n;
chris@162 201 default: break;
chris@162 202 }
chris@162 203
chris@162 204 x ^= x >> 13;
chris@162 205 x *= n;
chris@162 206 x ^= x >> 15;
chris@162 207
chris@162 208 return x;
chris@162 209 }
chris@162 210
chris@162 211 hv_uint32_t msg_getHash(const HvMessage *const m, int i) {
chris@162 212 hv_assert(i < msg_getNumElements(m)); // invalid index
chris@162 213 switch (msg_getType(m,i)) {
chris@162 214 case BANG: return 0xFFFFFFFF;
chris@162 215 case FLOAT: {
chris@162 216 float f = msg_getFloat(m,i);
chris@162 217 return *((hv_uint32_t *) &f);
chris@162 218 }
chris@162 219 case SYMBOL: return msg_symbolToHash(msg_getSymbol(m,i));
chris@162 220 case HASH: return (&(m->elem)+i)->data.h;
chris@162 221 default: return 0;
chris@162 222 }
chris@162 223 }
chris@162 224
chris@162 225 char *msg_toString(const HvMessage *m) {
chris@162 226 hv_assert(msg_getNumElements(m) > 0);
chris@162 227 int *len = (int *) hv_alloca(msg_getNumElements(m)*sizeof(int));
chris@162 228 int size = 0; // the total length of our final buffer
chris@162 229
chris@162 230 // loop through every element in our list of atoms
chris@162 231 // first loop figures out how long our buffer should be
chris@162 232 for (int i = 0; i < msg_getNumElements(m); i++) {
chris@162 233 // length of our string is each atom plus a space, or \0 on the end
chris@162 234 switch (msg_getType(m, i)) {
chris@162 235 case BANG: len[i] = hv_snprintf(NULL, 0, "%s", "bang") + 1; break;
chris@162 236 case FLOAT: len[i] = hv_snprintf(NULL, 0, "%g", msg_getFloat(m, i)) + 1; break;
chris@162 237 case SYMBOL: len[i] = hv_snprintf(NULL, 0, "%s", msg_getSymbol(m, i)) + 1; break;
chris@162 238 case HASH: len[i] = hv_snprintf(NULL, 0, "0x%X", msg_getHash(m, i)) + 1; break;
chris@162 239 default: break;
chris@162 240 }
chris@162 241 size += len[i];
chris@162 242 }
chris@162 243
chris@162 244 hv_assert(size > 0);
chris@162 245
chris@162 246 // now we do the piecewise concatenation into our final string
chris@162 247 // the final buffer we will pass back after concatenating all strings - user should free it
chris@162 248 char *finalString = (char *) hv_malloc(size*sizeof(char));
chris@162 249 int pos = 0;
chris@162 250 for (int i = 0; i < msg_getNumElements(m); i++) {
chris@162 251 // put a string representation of each atom into the final string
chris@162 252 switch (msg_getType(m, i)) {
chris@162 253 case BANG: hv_snprintf(finalString+pos, len[i], "%s", "bang"); break;
chris@162 254 case FLOAT: hv_snprintf(finalString+pos, len[i], "%g", msg_getFloat(m, i)); break;
chris@162 255 case SYMBOL: hv_snprintf(finalString+pos, len[i], "%s", msg_getSymbol(m, i)); break;
chris@162 256 case HASH: hv_snprintf(finalString+pos, len[i], "0x%X", msg_getHash(m, i)); break;
chris@162 257 default: break;
chris@162 258 }
chris@162 259 pos += len[i];
chris@162 260 finalString[pos-1] = 32; // ASCII space
chris@162 261 }
chris@162 262 finalString[size-1] = '\0'; // ensure that the string is null terminated
chris@162 263 return finalString;
chris@162 264 }
chris@162 265
chris@162 266 /*
chris@162 267 * TODO(mhroth): unnecessary for now
chris@162 268 bool msg_resolveDollarArguments(HvMessage *m, HvMessage *n, int z, char *buf, hv_size_t len, const char *args, ...) {
chris@162 269 va_list ap;
chris@162 270 va_start(ap, args);
chris@162 271
chris@162 272 hv_memset(buf, 0, len); // clear the buffer
chris@162 273 hv_size_t j = 0; // position in buffer
chris@162 274 const hv_size_t numArgs = hv_strlen(args); // the number of arguments
chris@162 275
chris@162 276 // if there is only one argument then the result has the chance of being a number, otherwise no
chris@162 277 bool isNumber = (numArgs == 1);
chris@162 278
chris@162 279 for (hv_size_t i = 0; i < numArgs; ++i) {
chris@162 280 switch (args[i]) {
chris@162 281 case 'i': { // a message index
chris@162 282 const int index = (int) va_arg(ap, int);
chris@162 283 if (index < 0) {
chris@162 284 // $0 always resolve to "0"
chris@162 285 const hv_size_t x = 1;
chris@162 286 if (x < len-j) { // always < in order to allow for trailing \0
chris@162 287 j += snprintf(buf+j, len-j, "0");
chris@162 288 }
chris@162 289 } else {
chris@162 290 switch (msg_getType(m, index)) {
chris@162 291 default:
chris@162 292 case BANG: break; // this case should never happen
chris@162 293 case FLOAT: {
chris@162 294 const hv_size_t x = snprintf(NULL, 0, "%g", msg_getFloat(m,index));
chris@162 295 if (x < len-j) { // ensure that the buffer is big enough
chris@162 296 j += snprintf(buf+j, len-j, "%g", msg_getFloat(m,index));
chris@162 297 }
chris@162 298 break;
chris@162 299 }
chris@162 300 case SYMBOL: {
chris@162 301 const hv_size_t x = snprintf(NULL, 0, "%s", msg_getSymbol(m,index));
chris@162 302 if (x < len-j) {
chris@162 303 j += snprintf(buf+j, len-j, "%s", msg_getSymbol(m,index));
chris@162 304 isNumber = false;
chris@162 305 }
chris@162 306 break;
chris@162 307 }
chris@162 308 }
chris@162 309 }
chris@162 310 break;
chris@162 311 }
chris@162 312 case 's': { // a string
chris@162 313 const char *s = (char *) va_arg(ap, char *);
chris@162 314 const hv_size_t x = snprintf(NULL, 0, "%s", s);
chris@162 315 if (x <= len-j) {
chris@162 316 j += snprintf(buf+j, len-j, "%s", s);
chris@162 317 isNumber = false;
chris@162 318 }
chris@162 319 break;
chris@162 320 }
chris@162 321 default: break;
chris@162 322 }
chris@162 323 }
chris@162 324
chris@162 325 if (isNumber) {
chris@162 326 msg_setFloat(n,z,(float) atof(buf));
chris@162 327 } else {
chris@162 328 msg_setSymbol(n,z,buf);
chris@162 329 }
chris@162 330
chris@162 331 va_end(ap);
chris@162 332
chris@162 333 return !isNumber;
chris@162 334 }
chris@162 335 */