aboutsummaryrefslogtreecommitdiff
path: root/cyclone/hammer/urn.c
blob: 59b140eaeff3487f1e62c7af4b135c3054f19f71 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
/* 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.  */

/* LATER think again about avoiding memory allocation overhead in run-time.
   One would need to use a creation argument greater than any future right
   inlet value.  But this is incompatible (max uses a `static', max-size
   array), and should be put somewhere in the docs... */

#include "m_pd.h"
#include "common/loud.h"
#include "common/rand.h"
#include "common/grow.h"

#define URN_INISIZE          128  /* LATER rethink */
#define URN_MAXSIZE         4096  /* CHECKED */
#define URN_MAXIMUMSIZE    65536  /* LATER use USHRT_MAX */

typedef struct _urn
{
    t_object         x_ob;
    int              x_count;
    int              x_size;   /* as allocated (in bytes) */
    int              x_range;  /* as used */
    unsigned short  *x_urn;
    unsigned short   x_urnini[URN_INISIZE];
    unsigned int     x_seed;
    t_outlet        *x_bangout;
} t_urn;

static t_class *urn_class;

static int urn_resize(t_urn *x, t_float f, int init)
{
    int maxmax = URN_MAXSIZE;
    int range = (int)f;  /* CHECKED silent truncation */
    if (init)
    {
	maxmax--;  /* CHECKED: max 4095 here (a bug, sort of) */
	/* CHECKED in the constructor this is silent
	   (also > maxmax clipped without complaining) */
	if (range < 1)
	    range = 1;
    }
    else if (range < 1)
    {
	/* CHECKED (the same for > maxmax) */
	loud_error((t_pd *)x, "illegal size %d", f);
	return (0);
    }
    if (range > URN_MAXIMUMSIZE)
    {
	loud_warning((t_pd *)x,
		     "requested size (%d) clipped -- effective size is %d",
		     range, URN_MAXIMUMSIZE);
	range = URN_MAXIMUMSIZE;
    }
    if (range > maxmax)
	loud_incompatible_max(urn_class, maxmax, "elements");
    x->x_range = range;
    if (range > x->x_size)
	x->x_urn = grow_nodata(&x->x_range, &x->x_size, x->x_urn,
			       URN_INISIZE, x->x_urnini,
			       sizeof(*x->x_urn));
    return (1);
}

static void urn_bang(t_urn *x)
{
    if (x->x_count)
    {
	int ndx = rand_int(&x->x_seed, x->x_count);
	unsigned short pick = x->x_urn[ndx];
	x->x_urn[ndx] = x->x_urn[--x->x_count];
	outlet_float(((t_object *)x)->ob_outlet, pick);
    }
    /* CHECKED: start banging when the first bang is input
       into an empty urn (and not when the last value is output).
       CHECKED: keep banging until cleared. */
    else outlet_bang(x->x_bangout);
}

static void urn_clear(t_urn *x)
{
    int i;
    x->x_count = x->x_range;
    for (i = 0; i < x->x_count; i++) x->x_urn[i] = i;
}

static void urn_float(t_urn *x, t_float f)
{
    /* CHECKED: float loudly rejected, int (any value) same as bang */
    int i;
    if (loud_checkint((t_pd *)x, f, &i, &s_float))
	urn_bang(x);
}

static void urn_ft1(t_urn *x, t_floatarg f)
{
    if (urn_resize(x, f, 0))  /* CHECKED cleared only if a legal resize */
	urn_clear(x);
}

static void urn_seed(t_urn *x, t_floatarg f)
{
    int i = (int)f;  /* CHECKED */
    if (i < 0)
	i = 1;  /* CHECKED */
    rand_seed(&x->x_seed, (unsigned int)i);
}

static void urn_free(t_urn *x)
{
    if (x->x_urn != x->x_urnini)
	freebytes(x->x_urn, x->x_size * sizeof(*x->x_urn));
}

static void *urn_new(t_floatarg f1, t_floatarg f2)
{
    t_urn *x = (t_urn *)pd_new(urn_class);
    x->x_size = URN_INISIZE;
    x->x_urn = x->x_urnini;
    urn_resize(x, f1, 1);
    urn_seed(x, f2);  /* CHECKME */
    inlet_new((t_object *)x, (t_pd *)x, &s_float, gensym("ft1"));
    outlet_new((t_object *)x, &s_float);
    x->x_bangout = outlet_new((t_object *)x, &s_bang);
    urn_clear(x);
    return (x);
}

void urn_setup(void)
{
    urn_class = class_new(gensym("urn"),
			  (t_newmethod)urn_new,
			  (t_method)urn_free,
			  sizeof(t_urn), 0,
			  A_DEFFLOAT, A_DEFFLOAT, 0);
    class_addbang(urn_class, urn_bang);
    class_addfloat(urn_class, urn_float);
    class_addmethod(urn_class, (t_method)urn_ft1,
		    gensym("ft1"), A_FLOAT, 0);
    /* CHECKED list is auto-unfolded */
    class_addmethod(urn_class, (t_method)urn_seed,
		    gensym("seed"), A_FLOAT, 0);  /* CHECKED arg obligatory */
    class_addmethod(urn_class, (t_method)urn_clear,
		    gensym("clear"), 0);
}