summaryrefslogtreecommitdiff
path: root/src/libs/sig/sig.c
blob: c68a420d5263d349be74ce4cd37b0c4f41ee5162 (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
149
150
#include <assert.h>
#include <stdarg.h>
#include <stdlib.h>

#include "sig.h"

/* This is to try ensure ctxt's alignment accomodates all the base type sizes,
 * it may waste some space in sig_t but since the caller supplies just a size
 * via the supplied sig_ops_t.size(), we know nothing of the alignment reqs.
 *
 * XXX: If callers start using other types like xmmintrinsics __m128, this
 * struct will have to get those added.
 */
typedef union sig_context_t {
	float		f;
	double		d;
	long double	ld;
	char		c;
	short		s;
	int		i;
	long		l;
	long long	ll;
	void		*p;
} sig_context_t;

typedef struct sig_t {
	const sig_ops_t	*ops;
	sig_context_t	ctxt[];
} sig_t;


/* return a new signal generator of ops type, configured according to va_list */
sig_t * sig_new(const sig_ops_t *ops, ...)
{
	static const sig_ops_t	null_ops;
	size_t			ctxt_size = 0;
	sig_t			*sig;
	va_list			ap;

	if (!ops)
		ops = &null_ops;

	va_start(ap, ops);
	if (ops->size)
		ctxt_size = ops->size(ap);
	va_end(ap);

	sig = calloc(1, sizeof(sig_t) + ctxt_size);
	if (!sig)
		return NULL;

	va_start(ap, ops);
	if (ops->init)
		ops->init(&sig->ctxt, ap);
	va_end(ap);

	sig->ops = ops;

	return sig;
}


/* free a signal generator, always returns NULL */
sig_t * sig_free(sig_t *sig)
{
	if (sig) {
		if (sig->ops->destroy)
			sig->ops->destroy(&sig->ctxt);

		free(sig);
	}

	return NULL;
}


/* produce the value for time ticks_ms from the supplied signal generator,
 * the returned value should always be kept in the range 0-1.
 */
float sig_output(sig_t *sig, unsigned ticks_ms)
{
	assert(sig);
	assert(sig->ops);

	if (sig->ops->output)
		return sig->ops->output(sig->ctxt, ticks_ms);

	return 0;
}


#ifdef TESTING

#include <stdio.h>

int main(int argc, char *argv[])
{
	sig_t	*sig;

	sig = sig_new(NULL);
	printf("null output=%f\n", sig_output(sig, 0));
	sig = sig_free(sig);

	sig = sig_new(&sig_ops_rand);
	for (unsigned j = 0; j < 2; j++) {
		for (unsigned i = 0; i < 10; i++)
			printf("rand j=%u i=%u output=%f\n", j, i, sig_output(sig, i));
	}

	sig = sig_new(&sig_ops_sin, sig_new(&sig_ops_const, 2.f));
	for (unsigned i = 0; i < 1000; i++)
		printf("sin 2hz output %i=%f\n", i, sig_output(sig, i));
	sig = sig_free(sig);

	sig = sig_new(&sig_ops_mult,
		      sig_new(&sig_ops_sin, sig_new(&sig_ops_const, 1.f)),	/* LFO @ 1hz */
		      sig_new(&sig_ops_sin, sig_new(&sig_ops_const, 100.f))	/* oscillator @ 100hz */
		);
	for (unsigned i = 0; i < 1000; i++)
		printf("sin 100hz * 1hz output %i=%f\n", i, sig_output(sig, i));
	sig = sig_free(sig);

	sig =	sig_new(&sig_ops_pow,								/* raise an ... */
			sig_new(&sig_ops_sin,							/* oscillator ... */
				sig_new(&sig_ops_const, 10.f)),					/* @ 10hz, */
			sig_new(&sig_ops_round,							/* to a rounded .. */
				sig_new(&sig_ops_mult, sig_new(&sig_ops_const, 50.f),		/* 50 X ... */
					sig_new(&sig_ops_sin, sig_new(&sig_ops_const, 1.f))	/* 1hz oscillator */
				)
			)
		);
	for (unsigned i = 0; i < 1000; i++)
		printf("sin 10hz ^ (sin 1hz * 50) output %i=%f\n", i, sig_output(sig, i));
	sig = sig_free(sig);

	sig =	sig_new(&sig_ops_scale,								/* scale a */
			sig_new(&sig_ops_lerp,							/* linear interpolation */
				sig_new(&sig_ops_sin, sig_new(&sig_ops_const, 10.f)),		/* between one 10hz oscillator */
				sig_new(&sig_ops_sin, sig_new(&sig_ops_const, 33.f)),		/* and another 33hz oscillator */
				sig_new(&sig_ops_sin, sig_new(&sig_ops_const, 2.f))		/* weighted by a 2hz oscillator */
			),
			sig_new(&sig_ops_const, -100.f), sig_new(&sig_ops_const, 100.f)		/* to the range -100 .. +100 */
		);
	for (unsigned i = 0; i < 1000; i++)
		printf("scale(lerp(sin(10hz), sin(33hz), sin(2hz)), -100, +100)  output %i=%f\n", i, sig_output(sig, i));
	sig = sig_free(sig);

}

#endif
© All Rights Reserved