aboutsummaryrefslogtreecommitdiff
path: root/cyclone/hammer/prob.c
diff options
context:
space:
mode:
Diffstat (limited to 'cyclone/hammer/prob.c')
-rw-r--r--cyclone/hammer/prob.c312
1 files changed, 312 insertions, 0 deletions
diff --git a/cyclone/hammer/prob.c b/cyclone/hammer/prob.c
new file mode 100644
index 0000000..5227870
--- /dev/null
+++ b/cyclone/hammer/prob.c
@@ -0,0 +1,312 @@
+/* Copyright (c) 2002-2003 krzYszcz and others.
+ * For information on usage and redistribution, and for a DISCLAIMER OF ALL
+ * WARRANTIES, see the file, "LICENSE.txt," in this distribution. */
+
+#include <stdio.h>
+#include "m_pd.h"
+#include "common/loud.h"
+#include "common/rand.h"
+#include "hammer/file.h"
+
+/* CHECKED: no preallocation. Apparently, it looks like if the new
+ state-entries were added to the list's head, and the new transition-entries
+ were added to the sublist's head. No sorting of any kind. */
+
+#define PROB_DEBUG 0
+
+typedef struct _probtrans
+{
+ int tr_value; /* state (if a header trans), or suffix value (otherwise) */
+ int tr_count; /* (a total in case of a header trans) */
+ struct _probtrans *tr_suffix; /* header trans of a suffix state */
+ struct _probtrans *tr_nexttrans; /* next trans of this state */
+ struct _probtrans *tr_nextstate; /* header trans of a next state */
+} t_probtrans;
+
+typedef struct _prob
+{
+ t_object x_ob;
+ t_probtrans *x_translist;
+ t_probtrans *x_state;
+ t_probtrans *x_default;
+ int x_embedmode;
+ int x_silent;
+ unsigned int x_seed;
+ t_outlet *x_bangout;
+ t_hammerfile *x_filehandle;
+} t_prob;
+
+static t_class *prob_class;
+
+static t_probtrans *prob_findstate(t_prob *x, int value, int complain)
+{
+ t_probtrans *state;
+ for (state = x->x_translist; state; state = state->tr_nextstate)
+ if (state->tr_value == value)
+ break;
+ if (!state && complain && !x->x_silent)
+ loud_error((t_pd *)x, "no state %d", value); /* CHECKED */
+ return (state);
+}
+
+static void prob_reset(t_prob *x, t_floatarg f)
+{
+ int value = (int)f; /* CHECKED: float converted to int */
+ t_probtrans *state = prob_findstate(x, value, 1);
+ if (state) /* CHECKED */
+ {
+ x->x_default = state;
+ /* CHECKED (sort of): */
+ if (!x->x_state->tr_nexttrans)
+ x->x_state = state;
+ }
+}
+
+/*
+CHECKED: embedmode off:
+#N prob;
+#P newobj ... prob;
+
+CHECKED: embedmode on, after clear:
+#N prob;
+#T embed 1;
+#P newobj ... prob;
+
+CHECKED: embedmode on, filled:
+#N prob;
+#T <preffix> <suffix> <count>
+...
+#T embed 1;
+#T reset <default>; (if set)
+#P newobj ... prob;
+*/
+static void prob_embed(t_prob *x, t_floatarg f)
+{
+ x->x_embedmode = ((int)f != 0);
+ if (x->x_embedmode)
+ loud_incompatible(prob_class, "embedding not supported (yet)...");
+}
+
+static void prob_clear(t_prob *x)
+{
+ t_probtrans *state, *nextstate;
+ for (state = x->x_translist; state; state = nextstate)
+ {
+ t_probtrans *trans, *nexttrans;
+ for (trans = state->tr_nexttrans; trans; trans = nexttrans)
+ {
+ nexttrans = trans->tr_nexttrans;
+ freebytes(trans, sizeof(*trans));
+ }
+ nextstate = state->tr_nextstate;
+ freebytes(state, sizeof(*state));
+ }
+ x->x_translist = 0;
+ x->x_state = 0;
+ x->x_default = 0; /* CHECKED: default number is not kept */
+ /* CHECKED embedmode is kept */
+}
+
+/* CHECKED */
+static void prob_dump(t_prob *x)
+{
+ t_probtrans *state;
+ post("transition probabilities:");
+ for (state = x->x_translist; state; state = state->tr_nextstate)
+ {
+ t_probtrans *trans;
+ for (trans = state->tr_nexttrans; trans; trans = trans->tr_nexttrans)
+ post(" from %3d to %3d: %d",
+ state->tr_value, trans->tr_value, trans->tr_count);
+ /* CHECKED: dead-ends are reported */
+ post("total weights for state %d: %d",
+ state->tr_value, state->tr_count);
+ }
+}
+
+static void prob_bang(t_prob *x)
+{
+ if (x->x_state) /* CHECKED: no output after clear */
+ {
+ int rnd = rand_int(&x->x_seed, x->x_state->tr_count);
+ t_probtrans *trans = x->x_state->tr_nexttrans;
+ if (trans)
+ {
+ for (trans = x->x_state->tr_nexttrans; trans;
+ trans = trans->tr_nexttrans)
+ if ((rnd -= trans->tr_count) < 0)
+ break;
+ if (trans)
+ {
+ t_probtrans *nextstate = trans->tr_suffix;
+ if (nextstate)
+ {
+ outlet_float(((t_object *)x)->ob_outlet,
+ nextstate->tr_value);
+ x->x_state = nextstate;
+ }
+ else bug("prob_bang: void suffix");
+ }
+ else bug("prob_bang: search overflow");
+ }
+ else
+ {
+ outlet_bang(x->x_bangout);
+ if (x->x_default) /* CHECKED: stays at dead-end if no default */
+ x->x_state = x->x_default;
+ }
+ }
+}
+
+static void prob_float(t_prob *x, t_float f)
+{
+ int value;
+ if (loud_checkint((t_pd *)x, f, &value, &s_float)) /* CHECKED */
+ {
+ t_probtrans *state = prob_findstate(x, value, 1);
+ if (state) /* CHECKED */
+ x->x_state = state;
+ }
+}
+
+static void prob_list(t_prob *x, t_symbol *s, int ac, t_atom *av)
+{
+ int prefval, suffval, count;
+ if (ac == 3 && av->a_type == A_FLOAT
+ && av[1].a_type == A_FLOAT && av[2].a_type == A_FLOAT
+ && (prefval = (int)av->a_w.w_float) == av->a_w.w_float
+ && (suffval = (int)av[1].a_w.w_float) == av[1].a_w.w_float
+ && (count = (int)av[2].a_w.w_float) == av[2].a_w.w_float)
+ {
+ t_probtrans *prefix = prob_findstate(x, prefval, 0);
+ t_probtrans *suffix = prob_findstate(x, suffval, 0);
+ t_probtrans *trans;
+ if (prefix && suffix)
+ {
+ for (trans = prefix->tr_nexttrans; trans;
+ trans = trans->tr_nexttrans)
+ if (trans->tr_suffix == suffix)
+ break;
+ if (trans)
+ {
+ /* the transition already exists... */
+ prefix->tr_count += (count - trans->tr_count);
+ trans->tr_count = count;
+ return;
+ }
+ }
+ if (!prefix)
+ {
+ if (!(prefix = getbytes(sizeof(*prefix))))
+ return;
+ prefix->tr_value = prefval;
+ prefix->tr_count = 0;
+ prefix->tr_suffix = 0;
+ prefix->tr_nexttrans = 0;
+ prefix->tr_nextstate = x->x_translist;
+ x->x_translist = prefix;
+ if (suffval == prefval)
+ suffix = prefix;
+ }
+ if (!suffix)
+ {
+ if (!(suffix = getbytes(sizeof(*suffix))))
+ return;
+ suffix->tr_value = suffval;
+ suffix->tr_count = 0;
+ suffix->tr_suffix = 0;
+ suffix->tr_nexttrans = 0;
+ suffix->tr_nextstate = x->x_translist;
+ x->x_translist = suffix;
+ }
+ if (trans = getbytes(sizeof(*trans)))
+ {
+ trans->tr_value = suffval;
+ trans->tr_count = count;
+ trans->tr_suffix = suffix;
+ trans->tr_nexttrans = prefix->tr_nexttrans;
+ trans->tr_nextstate = prefix->tr_nextstate;
+ prefix->tr_count += count;
+ prefix->tr_nexttrans = trans;
+ }
+ if (!x->x_state) /* CHECKED */
+ x->x_state = prefix; /* CHECKED */
+ }
+ else loud_error((t_pd *)x, "bad list message format"); /* CHECKED */
+}
+
+static void prob_silent(t_prob *x)
+{
+ if (!x->x_silent)
+ {
+ loud_incompatible(prob_class, "no 'silent' message in max");
+ x->x_silent = 1;
+ }
+}
+
+static void prob_click(t_prob *x, t_floatarg xpos, t_floatarg ypos,
+ t_floatarg shift, t_floatarg ctrl, t_floatarg alt)
+{
+ t_probtrans *state;
+ char buf[64];
+ hammereditor_open(x->x_filehandle, "prob");
+ for (state = x->x_translist; state; state = state->tr_nextstate)
+ {
+ t_probtrans *trans;
+ for (trans = state->tr_nexttrans; trans; trans = trans->tr_nexttrans)
+ {
+ sprintf(buf, "%d %d %d\n",
+ state->tr_value, trans->tr_value, trans->tr_count);
+ hammereditor_append(x->x_filehandle, buf);
+ }
+ }
+}
+
+static void prob_free(t_prob *x)
+{
+ prob_clear(x);
+ hammerfile_free(x->x_filehandle);
+}
+
+static void *prob_new(void)
+{
+ t_prob *x = (t_prob *)pd_new(prob_class);
+ x->x_translist = 0;
+ x->x_state = 0;
+ x->x_default = 0;
+ x->x_embedmode = 0; /* CHECKED */
+ x->x_silent = 0;
+ rand_seed(&x->x_seed, 0);
+ outlet_new((t_object *)x, &s_float);
+ x->x_bangout = outlet_new((t_object *)x, &s_bang);
+ x->x_filehandle = hammerfile_new((t_pd *)x, 0, 0, 0, 0);
+ return (x);
+}
+
+void prob_setup(void)
+{
+ prob_class = class_new(gensym("prob"),
+ (t_newmethod)prob_new,
+ (t_method)prob_free,
+ sizeof(t_prob), 0, 0);
+ class_addbang(prob_class, prob_bang);
+ class_addfloat(prob_class, prob_float);
+ class_addlist(prob_class, prob_list);
+ class_addmethod(prob_class, (t_method)prob_embed,
+ gensym("embed"), A_FLOAT, 0);
+ class_addmethod(prob_class, (t_method)prob_reset,
+ gensym("reset"), A_FLOAT, 0);
+ class_addmethod(prob_class, (t_method)prob_clear,
+ gensym("clear"), 0);
+ class_addmethod(prob_class, (t_method)prob_dump,
+ gensym("dump"), 0);
+ /* CHECKED: doesn't understand "seed" */
+
+ /* below are the incompatible extensions... */
+ class_addmethod(prob_class, (t_method)prob_silent,
+ gensym("silent"), 0);
+ class_addmethod(prob_class, (t_method)prob_click,
+ gensym("click"),
+ A_FLOAT, A_FLOAT, A_FLOAT, A_FLOAT, A_FLOAT, 0);
+ hammerfile_setup(prob_class, 0); /* LATER embedding (, 1) */
+}