This repository has been archived by the owner on Jun 17, 2020. It is now read-only.
/
line.w
317 lines (258 loc) · 8.71 KB
/
line.w
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
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
@** The Line. A line is a sequence of points in time. A line smoothly steps
through the points with some sort of interpolation.
@<Top@> += @<The Line@>
@* The |ll_line| Declaration.
@ The line is mostly a linked list, with a root value, a pointer to the value
last appended, and the size.
@<The Line@> +=
struct ll_line {@/
ll_point *root;
ll_point *last;
int size;
int curpos; /* the current point position */
@ Since the line generated is a digital audio signal, it must have a
sampling rate |sr|.
@<The Line@> +=
int sr;
@ A counter variable is used as a sample-accurate timer to navigate between
sequential points.
@<The Line@> +=
unsigned int counter;
@ The duration of the current point is stored in the variable |idur|. This
unit of this duration is in whole-{\bf i}nteger samples, which is the
justification for the "i" in the beginning of the variable.
@<The Line@> +=
unsigned int idur;
@ The line interface can handle memory allocation internally. It does so
using callback interfaces for allocating and freeing memory. By default, these
functions are wrappers around the standard C |malloc| and |free| functions.
@<The Line@> +=
ll_cb_malloc malloc;
ll_cb_free free;
@ The struct also has an entry for custom user data, defined as a void
pointer |ud|.
@<The Line@> +=
void *ud;
@ The variable |end| is a boolean value that is set when the line reaches
the end.
@<The Line@> +=
int end;
@ A timescaling variable speeds or slows down the units of time. This can
be used to make the units of duration match to beats rather than just seconds.
@<The Line@> +=
ll_flt tscale;
@ When a line produces a sample of audio, it saves a copy of it to a pointer.
By default, this pointer points to an internal value. However, it can be
overriden later by other applications who wish to read the data directly.
@<The Line@> +=
ll_flt *val;
ll_flt ival;
@/};
@ The size of |ll_line| is implemented as a function.
@<The Line@> +=
size_t ll_line_size(void)
{
return sizeof(ll_line);
}
@* Line Initalization.
@ After the line is allocated, it must be initialized. A line starts out
with zero points. Pointers are set to be |NULL| ({\tt NULL}). The
memory allocation functions are set to defaults.
@<The Line@> +=
void ll_line_init(ll_line *ln, int sr)
{
ln->root = NULL;
ln->last = NULL;
ln->size = 0;
ln->sr = sr;
ln->malloc = ll_malloc;
ln->free = ll_free;
ln->idur = 0;
ln->counter = 0;
ln->curpos = 0;
ln->end = 0;
ln->tscale = 1.0;
ln->val = &ln->ival;
}
@ The time scale of a line determines the rate at which line is stepped
through. A value of 1 has the line move normally. A value of 0.5, twice the
speed. A value of 2 at half-speed.
@<The Line@> +=
void ll_line_timescale(ll_line *ln, ll_flt scale)
{
ln->tscale = scale;
}
@* Appending a Point to a Line.
@ Points are added to a line in chronological order because they are appended
to the end of a linked list.
A new line with zero points must set the root
of the linked list with the added point. For the case when there are already
items populated in the linked list, the last pointer entry is used. The "next"
entry in this pointer is set to be the appended point. The "B" value of the
last point is set to point to the "A" value of the appended point.
After the point is appended, the last point is set to be the appended point.
The size of the line is incremented by 1.
@<The Line@> +=
void ll_line_append_point(ll_line *ln, ll_point *p)
{
if(ln->size == 0) {
ln->root = p;
} else {
ll_point_set_next_point(ln->last, p);
ll_point_set_next_value(ln->last, ll_point_get_value(p));
}
ln->last = p;
ln->size++;
}
@ The function |ll_line_append_point| assumes that memory is already allocated.
This, however, is a very inconvenient burden for the programmer to keep
track of. The function |ll_line_append| wraps around
|ll_line_append_point| and uses the internal memory functions to allocate
memory.
When the point is initialized, the memory functions used in the line are
forwarded to the point callback via |ll_point_mem_callback|.
@<The Line@> +=
ll_point * ll_line_append(ll_line *ln, ll_flt val, ll_flt dur)
{
ll_point *pt;
pt = ln->malloc(ln->ud, ll_point_size());
ll_point_init(pt);
ll_point_value(pt, val);
ll_point_dur(pt, dur);
ll_point_mem_callback(pt, ln->malloc, ln->free);
ll_line_append_point(ln, pt);
return pt;
}
@ Once points are doing being added to a line, it must be rewound and reset
to the beginning.
@<The Line@>+=
void ll_line_done(ll_line *ln)
{
ln->curpos = 0;
ln->last = ln->root;
ln->idur = ll_point_get_dur(ln->root) * ln->sr * ln->tscale;
ln->counter = ln->idur;
ln->end = 0;
}
@ The function |ll_line_done| also can be called at any point to rewind the
line to the beginning.
@<The Line@>+=
void ll_line_reset(ll_line *ln)
{
ll_line_done(ln);
}
@* Freeing Line Memory.
@ All things that must be allocated internally must then be freed using
the function |ll_line_free|. This function walks through the
linked list and frees all the points.
@<The Line@> +=
void ll_line_free(ll_line *ln)
{
ll_point *pt;
ll_point *next;
unsigned int i;
pt = ln->root;
for(i = 0; i < ln->size; i++) {
next = ll_point_get_next_point(pt);
ll_point_destroy(pt);
ln->free(ln->ud, pt);
pt = next;
}
}
@* Line Step Function.
@ |ll_line_step| is the top-level function that computes
the line. This is done through both ticking down the timer and walking
through the linked list.
@<The Line@> +=
ll_flt ll_line_step(ll_line *ln)
{
UINT dur;
UINT pos;
@ If the line has ended, the step value is simply the "A" value of the
point. The function returns early with this value. If the line has not ended,
the routine moves forward.
@<The Line@> +=
if(ln->end) {
return ll_point_A(ln->last);
}
@ There is a check to see if the counter has ticked to zero.
@<The Line@> +=
if(ln->counter == 0) {
@ If the counter is zero, there is a check to see if there are any points left
in the list. This is done by comparing the current point position with the size
of the of the list. Note that since the current point position is zero indexed,
the size is subtracted by 1.
@<The Line@> +=
if(ln->curpos < (ln->size - 1)) {
@ If the line is not at the end, then it will step to the next point in the
linked list. The duration in samples is computed, the counter is reset, and
the position is incremented by one.
@<The Line@> +=
ln->last = ll_point_get_next_point(ln->last);
ln->idur = ll_point_get_dur(ln->last) * ln->sr * ln->tscale;
ln->counter = ln->idur;
ln->curpos++;
@ If there are no points left in the list, the line has ended, and the |end|
variable is turned on. This concludes both nested conditionals.
@<The Line@> +=
} else {
ln->end = 1;
}
}
@ The step function inside the point is called. The current point
position is a value derived from the counter. Since the counter moves backwards,
it is subtracted for the total line duration. The counter is then
decremented right before the point step function is called.
@<The Line@> +=
dur = ln->idur;
pos = dur - ln->counter;
ln->counter--;
*ln->val = ll_point_step(ln->last, pos, dur);
return *ln->val;
}
@* Other Functions. The following section is for functions that couldn't quite
fit anywhere else.
@ Sometimes it can be useful to print points in a line. |ll_line_print|
does just that, walking through the list and printing the values.
@<The Line@> +=
void ll_line_print(ll_line *ln)
{
ll_point *pt;
ll_point *next;
unsigned int i;
ll_flt *val;
pt = ln->root;
printf("there are %d points...\n", ln->size);
for(i = 0; i < ln->size; i++) {
next = ll_point_get_next_point(pt);
val = ll_point_get_value(pt);
printf("point %d: dur %g, val %g\n",
i,
ll_point_get_dur(pt),
*val
);
pt = next;
}
}
@ In Sporth, it is a better arrangement to have a Sporth float be injected into
libline, rather than a libline float injected into Sporth. This function
binds a float pointer to a line.
@<The Line@> +=
void ll_line_bind_float(ll_line *ln, ll_flt *line)
{
ln->val = line;
}
@ To access all points in a line, one only needs the top point. Since points
are entries in a linked list, one can step through the line using
|ll_get_next_point|.
@<The Line@> +=
ll_point* ll_line_top_point(ll_line *ln)
{
return ln->root;
}
@ The function |ll_line_npoints| returns the number of points in a line.
@<The Line@> +=
int ll_line_npoints(ll_line *ln)
{
return ln->size;
}