annotate projects/heavy/samphold/HvContext_bbb.c @ 160:5bcf04234f80 heavy-updated

- added -std=c99 to Makefile for user-supplied C files (required for heavy files) - changed heavy core render.cpp file to use latest API and removed all redundant functions (e.g. foleyDesigner/touchkey stuff) - use build_pd.sh to compile and run pd files (-h for usage instructions)
author chnrx <chris.heinrichs@gmail.com>
date Thu, 05 Nov 2015 18:58:26 +0000
parents
children c3e8226a5651
rev   line source
chris@160 1
chris@160 2 /**
chris@160 3 * Copyright (c) 2014,2015 Enzien Audio, Ltd.
chris@160 4 *
chris@160 5 * Permission is hereby granted, free of charge, to any person obtaining a copy
chris@160 6 * of this software and associated documentation files (the "Software"),
chris@160 7 * to deal in the Software without restriction, including without limitation
chris@160 8 * the rights to use, copy, modify, merge, publish, distribute, and/or
chris@160 9 * sublicense copies of the Software, strictly on a non-commercial basis,
chris@160 10 * and to permit persons to whom the Software is furnished to do so,
chris@160 11 * subject to the following conditions:
chris@160 12 *
chris@160 13 * The above copyright notice and this permission notice shall be included in
chris@160 14 * all copies or substantial portions of the Software.
chris@160 15 *
chris@160 16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
chris@160 17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
chris@160 18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
chris@160 19 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
chris@160 20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
chris@160 21 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
chris@160 22 * IN THE SOFTWARE.
chris@160 23 *
chris@160 24 * DO NOT MODIFY. THIS CODE IS MACHINE GENERATED BY THE SECTION6 HEAVY COMPILER.
chris@160 25 */
chris@160 26
chris@160 27 /*
chris@160 28 * System Includes
chris@160 29 */
chris@160 30
chris@160 31 #include <assert.h>
chris@160 32 #include <math.h>
chris@160 33 #include <string.h>
chris@160 34 #include <stdarg.h>
chris@160 35 #include "HvContext_bbb.h"
chris@160 36 #include "HeavyMath.h"
chris@160 37
chris@160 38
chris@160 39 /*
chris@160 40 * Function Declarations
chris@160 41 */
chris@160 42 static void cBinop_62MLs_sendMessage(HvBase *, int, const HvMessage *const);
chris@160 43 static void cUnop_Rm4T9_sendMessage(HvBase *, int, const HvMessage *const);
chris@160 44 static void cRandom_Uxs9y_sendMessage(HvBase *, int, const HvMessage *const);
chris@160 45 static void cLoadbang_XJMP6_sendMessage(HvBase *, int, const HvMessage *const);
chris@160 46 static void cMsg_eMw6t_sendMessage(HvBase *, int, const HvMessage *const);
chris@160 47 static void cBinop_vHnCM_sendMessage(HvBase *, int, const HvMessage *const);
chris@160 48 static void cBinop_PmuD1_sendMessage(HvBase *, int, const HvMessage *const);
chris@160 49 static void cBinop_lT4qw_sendMessage(HvBase *, int, const HvMessage *const);
chris@160 50 static void cBinop_vpZDi_sendMessage(HvBase *, int, const HvMessage *const);
chris@160 51 static void cMsg_zpcIL_sendMessage(HvBase *, int, const HvMessage *const);
chris@160 52 static void cMsg_pfnj7_sendMessage(HvBase *, int, const HvMessage *const);
chris@160 53 static void cSystem_lJZJR_sendMessage(HvBase *, int, const HvMessage *const);
chris@160 54 static void cVar_cPxQc_sendMessage(HvBase *, int, const HvMessage *const);
chris@160 55 static void cBinop_1u9M5_sendMessage(HvBase *, int, const HvMessage *const);
chris@160 56 static void cLoadbang_AXnu9_sendMessage(HvBase *, int, const HvMessage *const);
chris@160 57
chris@160 58
chris@160 59
chris@160 60 /*
chris@160 61 * Static Helper Functions
chris@160 62 */
chris@160 63
chris@160 64 static void ctx_intern_scheduleMessageForReceiver(
chris@160 65 HvBase *const _c, const char *name, HvMessage *m) {
chris@160 66 switch (msg_symbolToHash(name)) {
chris@160 67 default: return;
chris@160 68 }
chris@160 69 }
chris@160 70
chris@160 71 static struct HvTable *ctx_intern_getTableForHash(HvBase *const _c, hv_uint32_t h) {
chris@160 72 switch (h) {
chris@160 73 default: return NULL;
chris@160 74 }
chris@160 75 }
chris@160 76
chris@160 77
chris@160 78
chris@160 79 /*
chris@160 80 * Context Include and Implementatons
chris@160 81 */
chris@160 82
chris@160 83 Hv_bbb *hv_bbb_new_with_pool(double sampleRate, int poolKb) {
chris@160 84 hv_assert(sampleRate > 0.0); // can't initialise with sampling rate of 0
chris@160 85 hv_assert(poolKb >= 1); // a message pool of some reasonable size is always needed
chris@160 86 Hv_bbb *const _c = (Hv_bbb *) hv_malloc(sizeof(Hv_bbb));
chris@160 87
chris@160 88 Base(_c)->numInputChannels = 0;
chris@160 89 Base(_c)->numOutputChannels = 2;
chris@160 90 Base(_c)->sampleRate = sampleRate;
chris@160 91 Base(_c)->blockStartTimestamp = 0;
chris@160 92 Base(_c)->f_scheduleMessageForReceiver = &ctx_intern_scheduleMessageForReceiver;
chris@160 93 Base(_c)->f_getTableForHash = &ctx_intern_getTableForHash;
chris@160 94 mq_initWithPoolSize(&Base(_c)->mq, poolKb);
chris@160 95 Base(_c)->basePath = NULL;
chris@160 96 Base(_c)->printHook = NULL;
chris@160 97 Base(_c)->sendHook = NULL;
chris@160 98 Base(_c)->userData = NULL;
chris@160 99 Base(_c)->name = "bbb";
chris@160 100
chris@160 101 Base(_c)->numBytes = sizeof(Hv_bbb);
chris@160 102 Base(_c)->numBytes += sVari_init(&_c->sVari_ecpIx, 0, 0, false);
chris@160 103 Base(_c)->numBytes += sVarf_init(&_c->sVarf_VF9rD, 0.0f, 0.0f, false);
chris@160 104 Base(_c)->numBytes += sVarf_init(&_c->sVarf_0bFmM, 0.0f, 0.0f, false);
chris@160 105 Base(_c)->numBytes += sRPole_init(&_c->sRPole_WxgWS);
chris@160 106 Base(_c)->numBytes += sPhasor_k_init(&_c->sPhasor_YcHM3, 880.0f, sampleRate);
chris@160 107 Base(_c)->numBytes += sDel1_init(&_c->sDel1_FfVih);
chris@160 108 Base(_c)->numBytes += sSamphold_init(&_c->sSamphold_hq9sm);
chris@160 109 Base(_c)->numBytes += sPhasor_init(&_c->sPhasor_n1TcS, sampleRate);
chris@160 110 Base(_c)->numBytes += cBinop_init(&_c->cBinop_62MLs, 8388610.0f); // __mul
chris@160 111 Base(_c)->numBytes += cRandom_init(&_c->cRandom_Uxs9y, -1512500956);
chris@160 112 Base(_c)->numBytes += cBinop_init(&_c->cBinop_vHnCM, 1.0f); // __min
chris@160 113 Base(_c)->numBytes += cBinop_init(&_c->cBinop_PmuD1, 0.0f); // __max
chris@160 114 Base(_c)->numBytes += cBinop_init(&_c->cBinop_lT4qw, 0.0f); // __mul
chris@160 115 Base(_c)->numBytes += cVar_init_f(&_c->cVar_cPxQc, 1.0f);
chris@160 116
chris@160 117 // loadbang
chris@160 118 ctx_scheduleMessage(Base(_c), msg_initWithBang(HV_MESSAGE_ON_STACK(1), 0), &cLoadbang_AXnu9_sendMessage, 0);
chris@160 119 ctx_scheduleMessage(Base(_c), msg_initWithBang(HV_MESSAGE_ON_STACK(1), 0), &cLoadbang_XJMP6_sendMessage, 0);
chris@160 120
chris@160 121 return _c;
chris@160 122 }
chris@160 123
chris@160 124 Hv_bbb *hv_bbb_new(double sampleRate) {
chris@160 125 return hv_bbb_new_with_pool(sampleRate, 10); // default to 10KB MessagePool
chris@160 126 }
chris@160 127
chris@160 128 void hv_bbb_free(Hv_bbb *_c) {
chris@160 129
chris@160 130 hv_free(Base(_c)->basePath);
chris@160 131 mq_free(&Base(_c)->mq); // free queue after all objects have been freed, messages may be cancelled
chris@160 132
chris@160 133 hv_free(_c);
chris@160 134 }
chris@160 135
chris@160 136
chris@160 137
chris@160 138 /*
chris@160 139 * Static Function Implementation
chris@160 140 */
chris@160 141 static void cBinop_62MLs_sendMessage(HvBase *_c, int letIn, const HvMessage *const m) {
chris@160 142 cUnop_onMessage(_c, HV_UNOP_FLOOR, m, &cUnop_Rm4T9_sendMessage);
chris@160 143 }
chris@160 144
chris@160 145 static void cUnop_Rm4T9_sendMessage(HvBase *_c, int letIn, const HvMessage *const m) {
chris@160 146 cMsg_eMw6t_sendMessage(_c, 0, m);
chris@160 147 }
chris@160 148
chris@160 149 static void cRandom_Uxs9y_sendMessage(HvBase *_c, int letIn, const HvMessage *const m) {
chris@160 150 cBinop_onMessage(_c, &Context(_c)->cBinop_62MLs, HV_BINOP_MULTIPLY, 0, m, &cBinop_62MLs_sendMessage);
chris@160 151 }
chris@160 152
chris@160 153 static void cLoadbang_XJMP6_sendMessage(HvBase *_c, int letIn, const HvMessage *const m) {
chris@160 154 cRandom_onMessage(_c, &Context(_c)->cRandom_Uxs9y, 0, m, &cRandom_Uxs9y_sendMessage);
chris@160 155 }
chris@160 156
chris@160 157 static void cMsg_eMw6t_sendMessage(HvBase *_c, int letIn, const HvMessage *const n) {
chris@160 158 HvMessage *m = NULL;
chris@160 159 m = HV_MESSAGE_ON_STACK(2);
chris@160 160 msg_init(m, 2, msg_getTimestamp(n));
chris@160 161 msg_setElementToFrom(m, 0, n, 0);
chris@160 162 msg_setFloat(m, 1, 1.0f);
chris@160 163 sVari_onMessage(_c, &Context(_c)->sVari_ecpIx, m);
chris@160 164 }
chris@160 165
chris@160 166 static void cBinop_vHnCM_sendMessage(HvBase *_c, int letIn, const HvMessage *const m) {
chris@160 167 cBinop_onMessage(_c, &Context(_c)->cBinop_PmuD1, HV_BINOP_MAX, 0, m, &cBinop_PmuD1_sendMessage);
chris@160 168 }
chris@160 169
chris@160 170 static void cBinop_PmuD1_sendMessage(HvBase *_c, int letIn, const HvMessage *const m) {
chris@160 171 cBinop_k_onMessage(_c, NULL, HV_BINOP_SUBTRACT, 1.0f, 0, m, &cBinop_1u9M5_sendMessage);
chris@160 172 sVarf_onMessage(_c, &Context(_c)->sVarf_VF9rD, m);
chris@160 173 }
chris@160 174
chris@160 175 static void cBinop_lT4qw_sendMessage(HvBase *_c, int letIn, const HvMessage *const m) {
chris@160 176 cBinop_onMessage(_c, &Context(_c)->cBinop_vHnCM, HV_BINOP_MIN, 0, m, &cBinop_vHnCM_sendMessage);
chris@160 177 }
chris@160 178
chris@160 179 static void cBinop_vpZDi_sendMessage(HvBase *_c, int letIn, const HvMessage *const m) {
chris@160 180 cBinop_onMessage(_c, &Context(_c)->cBinop_lT4qw, HV_BINOP_MULTIPLY, 1, m, &cBinop_lT4qw_sendMessage);
chris@160 181 }
chris@160 182
chris@160 183 static void cMsg_zpcIL_sendMessage(HvBase *_c, int letIn, const HvMessage *const n) {
chris@160 184 HvMessage *m = NULL;
chris@160 185 m = HV_MESSAGE_ON_STACK(2);
chris@160 186 msg_init(m, 2, msg_getTimestamp(n));
chris@160 187 msg_setFloat(m, 0, 6.28319f);
chris@160 188 msg_setElementToFrom(m, 1, n, 0);
chris@160 189 cBinop_k_onMessage(_c, NULL, HV_BINOP_DIVIDE, 0.0f, 0, m, &cBinop_vpZDi_sendMessage);
chris@160 190 }
chris@160 191
chris@160 192 static void cMsg_pfnj7_sendMessage(HvBase *_c, int letIn, const HvMessage *const n) {
chris@160 193 HvMessage *m = NULL;
chris@160 194 m = HV_MESSAGE_ON_STACK(1);
chris@160 195 msg_init(m, 1, msg_getTimestamp(n));
chris@160 196 msg_setSymbol(m, 0, "samplerate");
chris@160 197 cSystem_onMessage(_c, NULL, 0, m, &cSystem_lJZJR_sendMessage);
chris@160 198 }
chris@160 199
chris@160 200 static void cSystem_lJZJR_sendMessage(HvBase *_c, int letIn, const HvMessage *const m) {
chris@160 201 cMsg_zpcIL_sendMessage(_c, 0, m);
chris@160 202 }
chris@160 203
chris@160 204 static void cVar_cPxQc_sendMessage(HvBase *_c, int letIn, const HvMessage *const m) {
chris@160 205 cBinop_onMessage(_c, &Context(_c)->cBinop_lT4qw, HV_BINOP_MULTIPLY, 0, m, &cBinop_lT4qw_sendMessage);
chris@160 206 }
chris@160 207
chris@160 208 static void cBinop_1u9M5_sendMessage(HvBase *_c, int letIn, const HvMessage *const m) {
chris@160 209 sVarf_onMessage(_c, &Context(_c)->sVarf_0bFmM, m);
chris@160 210 }
chris@160 211
chris@160 212 static void cLoadbang_AXnu9_sendMessage(HvBase *_c, int letIn, const HvMessage *const m) {
chris@160 213 cMsg_pfnj7_sendMessage(_c, 0, m);
chris@160 214 cVar_onMessage(_c, &Context(_c)->cVar_cPxQc, 0, m, &cVar_cPxQc_sendMessage);
chris@160 215 }
chris@160 216
chris@160 217
chris@160 218
chris@160 219
chris@160 220 /*
chris@160 221 * Context Process Implementation
chris@160 222 */
chris@160 223
chris@160 224 int hv_bbb_process(Hv_bbb *const _c, float **const inputBuffers, float **const outputBuffers, int nx) {
chris@160 225 const int n4 = nx & ~HV_N_SIMD_MASK; // ensure that the block size is a multiple of HV_N_SIMD
chris@160 226
chris@160 227 // temporary signal vars
chris@160 228 hv_bufferf_t Bf0, Bf1, Bf2, Bf3, Bf4;
chris@160 229 hv_bufferi_t Bi0, Bi1;
chris@160 230
chris@160 231 // input and output vars
chris@160 232 hv_bufferf_t O0, O1;
chris@160 233
chris@160 234 // declare and init the zero buffer
chris@160 235 hv_bufferf_t ZERO; __hv_zero_f(VOf(ZERO));
chris@160 236
chris@160 237 hv_uint32_t nextBlock = Base(_c)->blockStartTimestamp;
chris@160 238 for (int n = 0; n < n4; n += HV_N_SIMD) {
chris@160 239
chris@160 240 // process all of the messages for this block
chris@160 241 nextBlock += HV_N_SIMD;
chris@160 242 while (mq_hasMessageBefore(&Base(_c)->mq, nextBlock)) {
chris@160 243 MessageNode *const node = mq_peek(&Base(_c)->mq);
chris@160 244 node->sendMessage(Base(_c), node->let, node->m);
chris@160 245 mq_pop(&Base(_c)->mq);
chris@160 246 }
chris@160 247
chris@160 248
chris@160 249
chris@160 250 // zero output buffers
chris@160 251 __hv_zero_f(VOf(O0));
chris@160 252 __hv_zero_f(VOf(O1));
chris@160 253
chris@160 254 // process all signal functions
chris@160 255 __hv_var_i(&_c->sVari_ecpIx, VOi(Bi0));
chris@160 256 __hv_var_k_i(VOi(Bi1), 16807, 16807, 16807, 16807, 16807, 16807, 16807, 16807, 0);
chris@160 257 __hv_mul_i(VIi(Bi0), VIi(Bi1), VOi(Bi1));
chris@160 258 __hv_cast_if(VIi(Bi1), VOf(Bf0));
chris@160 259 __hv_var_k_f(VOf(Bf1), 4.65661e-10f, 4.65661e-10f, 4.65661e-10f, 4.65661e-10f, 4.65661e-10f, 4.65661e-10f, 4.65661e-10f, 4.65661e-10f, 0);
chris@160 260 __hv_mul_f(VIf(Bf0), VIf(Bf1), VOf(Bf1));
chris@160 261 sVarseti_process(&_c->sVari_ecpIx, VIi(Bi1));
chris@160 262 __hv_var_f(&_c->sVarf_VF9rD, VOf(Bf0));
chris@160 263 __hv_mul_f(VIf(Bf1), VIf(Bf0), VOf(Bf0));
chris@160 264 __hv_var_f(&_c->sVarf_0bFmM, VOf(Bf1));
chris@160 265 __hv_rpole_f(&_c->sRPole_WxgWS, VIf(Bf0), VIf(Bf1), VOf(Bf1));
chris@160 266 __hv_var_k_f(VOf(Bf0), 1000000.0f, 1000000.0f, 1000000.0f, 1000000.0f, 1000000.0f, 1000000.0f, 1000000.0f, 1000000.0f, 0);
chris@160 267 __hv_mul_f(VIf(Bf1), VIf(Bf0), VOf(Bf0));
chris@160 268 __hv_phasor_k_f(&_c->sPhasor_YcHM3, VOf(Bf1));
chris@160 269 __hv_del1_f(&_c->sDel1_FfVih, VIf(Bf1), VOf(Bf2));
chris@160 270 __hv_lt_f(VIf(Bf1), VIf(Bf2), VOf(Bf2));
chris@160 271 __hv_samphold_f(&_c->sSamphold_hq9sm, VIf(Bf0), VIf(Bf2), VOf(Bf2));
chris@160 272 __hv_phasor_f(&_c->sPhasor_n1TcS, VIf(Bf2), VOf(Bf2));
chris@160 273 __hv_var_k_f(VOf(Bf0), 0.5f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5f, 0);
chris@160 274 __hv_sub_f(VIf(Bf2), VIf(Bf0), VOf(Bf0));
chris@160 275 __hv_abs_f(VIf(Bf0), VOf(Bf0));
chris@160 276 __hv_var_k_f(VOf(Bf2), 0.25f, 0.25f, 0.25f, 0.25f, 0.25f, 0.25f, 0.25f, 0.25f, 0);
chris@160 277 __hv_sub_f(VIf(Bf0), VIf(Bf2), VOf(Bf2));
chris@160 278 __hv_var_k_f(VOf(Bf0), 6.28319f, 6.28319f, 6.28319f, 6.28319f, 6.28319f, 6.28319f, 6.28319f, 6.28319f, 0);
chris@160 279 __hv_mul_f(VIf(Bf2), VIf(Bf0), VOf(Bf0));
chris@160 280 __hv_mul_f(VIf(Bf0), VIf(Bf0), VOf(Bf2));
chris@160 281 __hv_mul_f(VIf(Bf0), VIf(Bf2), VOf(Bf1));
chris@160 282 __hv_mul_f(VIf(Bf1), VIf(Bf2), VOf(Bf2));
chris@160 283 __hv_var_k_f(VOf(Bf3), 0.00784314f, 0.00784314f, 0.00784314f, 0.00784314f, 0.00784314f, 0.00784314f, 0.00784314f, 0.00784314f, 0);
chris@160 284 __hv_var_k_f(VOf(Bf4), 0.166667f, 0.166667f, 0.166667f, 0.166667f, 0.166667f, 0.166667f, 0.166667f, 0.166667f, 0);
chris@160 285 __hv_mul_f(VIf(Bf1), VIf(Bf4), VOf(Bf4));
chris@160 286 __hv_sub_f(VIf(Bf0), VIf(Bf4), VOf(Bf4));
chris@160 287 __hv_fma_f(VIf(Bf2), VIf(Bf3), VIf(Bf4), VOf(Bf4));
chris@160 288 __hv_var_k_f(VOf(Bf3), 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0);
chris@160 289 __hv_mul_f(VIf(Bf4), VIf(Bf3), VOf(Bf3));
chris@160 290 __hv_add_f(VIf(Bf3), VIf(O0), VOf(O0));
chris@160 291 __hv_add_f(VIf(Bf3), VIf(O1), VOf(O1));
chris@160 292
chris@160 293 // save output vars to output buffer
chris@160 294 __hv_store_f(outputBuffers[0]+n, VIf(O0));
chris@160 295 __hv_store_f(outputBuffers[1]+n, VIf(O1));
chris@160 296 }
chris@160 297
chris@160 298 Base(_c)->blockStartTimestamp = nextBlock;
chris@160 299
chris@160 300 return n4; // return the number of frames processed
chris@160 301 }
chris@160 302
chris@160 303 int hv_bbb_process_inline(Hv_bbb *c, float *const inputBuffers, float *const outputBuffers, int n4) {
chris@160 304 hv_assert(!(n4 & HV_N_SIMD_MASK)); // ensure that n4 is a multiple of HV_N_SIMD
chris@160 305 int i = ctx_getNumInputChannels(Base(c));
chris@160 306 float **bIn = (float **) hv_alloca(i*sizeof(float *));
chris@160 307 while (i--) bIn[i] = inputBuffers+(i*n4);
chris@160 308
chris@160 309 i = ctx_getNumOutputChannels(Base(c));
chris@160 310 float **bOut = (float **) hv_alloca(i*sizeof(float *));
chris@160 311 while (i--) bOut[i] = outputBuffers+(i*n4);
chris@160 312
chris@160 313 int n = hv_bbb_process(c, bIn, bOut, n4);
chris@160 314 return n;
chris@160 315 }
chris@160 316
chris@160 317 int hv_bbb_process_inline_short(Hv_bbb *c, short *const inputBuffers, short *const outputBuffers, int n4) {
chris@160 318 hv_assert(!(n4 & HV_N_SIMD_MASK)); // ensure that n4 is a multiple of HV_N_SIMD
chris@160 319 int numChannels = ctx_getNumInputChannels(Base(c));
chris@160 320 float *bIn = (float *) hv_alloca(numChannels*n4*sizeof(float));
chris@160 321 for (int i = 0; i < numChannels; ++i) {
chris@160 322 for (int j = 0; j < n4; ++j) {
chris@160 323 bIn[i*n4+j] = ((float) inputBuffers[i+numChannels*j]) * 0.00003051757813f;
chris@160 324 }
chris@160 325 }
chris@160 326
chris@160 327 numChannels = ctx_getNumOutputChannels(Base(c));
chris@160 328 float *bOut = (float *) hv_alloca(numChannels*n4*sizeof(float));
chris@160 329
chris@160 330 int n = hv_bbb_process_inline(c, bIn, bOut, n4);
chris@160 331
chris@160 332 for (int i = 0; i < numChannels; ++i) {
chris@160 333 for (int j = 0; j < n4; ++j) {
chris@160 334 outputBuffers[i+numChannels*j] = (short) (bOut[i*n4+j] * 32767.0f);
chris@160 335 }
chris@160 336 }
chris@160 337
chris@160 338 return n;
chris@160 339 }