summaryrefslogtreecommitdiff
path: root/src/libs/sig/sig.c
blob: 865c6b4afc8fc8cdd59cc1c0d262c731b294b4d4 (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
#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);
}

#endif
© All Rights Reserved