Mixer now has variable number of inputs.
[aftubes.git] / mixer.c
blob9ed3537cfd22b51c2950d4304a1c2d83535b2f8a
1 #include "modules.h"
2 #include "graph.h"
3 #include "errors.h"
4 #include "assert.h"
5 #include "stdio.h"
7 ///// Simple additive mixer /////
8 ///// TODO: handle different input formats /////
10 struct mixer {
11 bool format_is_set;
12 struct aformat af;
13 struct graphpin *out_pin;
14 int n_inputs;
17 static bool is_acceptable_input_format(struct graphnode *node, struct graphpin *pin, const struct aformat *af)
19 struct mixer *m = node->extra;
20 assert(pin != m->out_pin);
21 if (m->format_is_set){
22 return af->media == m->af.media
23 && af->srate == m->af.srate
24 && af->channels == m->af.channels;
25 } else {
26 return true;
30 static err_t get_output_format(struct graphnode *node, struct graphpin *pin, struct aformat *af)
32 struct mixer *m = node->extra;
33 assert(pin == m->out_pin);
35 if (m->format_is_set){
36 *af = m->af;
37 return EOK;
38 } else {
39 return make_error(ECANNOT_GUESS_FORMAT, node, "mixer module is not yet bound to a format");
43 static err_t another_in_pin(struct graphnode *node, struct mixer *m)
45 // create a new input pin
46 struct graphpin *new_pin;
47 err_t err;
48 char *new_name = NULL;
50 err = graphnode_add_pin(node, &new_pin);
51 if (err != EOK){
52 return err;
54 asprintf(&new_name, "in%d", m->n_inputs);
55 new_pin->name = new_name;
56 new_pin->dir = DIR_IN;
57 ++m->n_inputs;
58 return EOK;
61 static err_t set_buffer(struct graphnode *node, struct graphpin *pin, struct buffer *buf)
63 struct mixer *m = node->extra;
64 if (m->format_is_set){
65 if (buf->format.media == m->af.media
66 && buf->format.srate == m->af.srate
67 && buf->format.channels == m->af.channels){
68 return another_in_pin(node, m);
69 } else {
70 return make_error(EBAD_FORMAT, node, "mixer module is being connected to a bad media format");
72 } else {
73 m->af = buf->format;
74 m->format_is_set = true;
75 return another_in_pin(node, m);
79 static size_t longest_in_buf_len(struct graphnode *restrict node)
81 struct graphpin *in_pin;
82 size_t sz = 0;
84 for (in_pin=node->pins; in_pin; in_pin=in_pin->next){
85 if (in_pin->dir != DIR_IN || !in_pin->edge){
86 continue;
88 if (in_pin->edge->buf.n_samples > sz){
89 sz = in_pin->edge->buf.n_samples;
92 return sz;
95 static err_t run(struct graphnode *node)
97 #define DO_MIX(T) { \
98 T *restrict s_ptr, *restrict d_ptr; \
99 register int i; \
100 /* clear the output buffer */ \
101 d_ptr = d_buf->data; \
102 for (i=sz; i; --i){ \
103 *d_ptr++ = 0; \
106 for (in_pin=node->pins; in_pin; in_pin=in_pin->next){ \
107 if (in_pin->dir != DIR_IN || !in_pin->edge){ \
108 continue; \
110 s_ptr = in_pin->edge->buf.data; \
111 d_ptr = d_buf->data; \
112 for (i=sz; i; --i){ \
113 /* Mix! */ \
114 *d_ptr++ += *s_ptr++ / m->n_inputs; /* *boggle* */ \
117 return EOK; }
119 struct mixer *restrict m = node->extra;
120 struct graphpin *in_pin;
121 struct buffer *restrict d_buf = &m->out_pin->edge->buf;
122 size_t sz;
123 err_t err;
125 sz = longest_in_buf_len(node);
126 err = buffer_alloc(d_buf, sz);
127 if (err != EOK){
128 return err;
131 sz *= m->out_pin->edge->buf.format.channels;
133 switch (m->out_pin->edge->buf.format.media){
134 case MT_AUDIO_32F:
135 DO_MIX(float)
136 case MT_AUDIO_16I:
137 DO_MIX(short)
138 case MT_AUDIO_32I:
139 DO_MIX(int)
141 return make_error(ENOTIMPL, NULL, "mixing these media types is not implemented");
144 static const struct graphnode_functab functab = {
145 is_acceptable_input_format,
146 NULL, // get_ideal_input_format
147 get_output_format,
148 set_buffer,
149 run,
152 err_t mixer_create(struct graphnode **node_out)
154 struct graphnode *node;
155 struct mixer *m;
156 struct graphpin *in_pin;
157 err_t err;
159 err = graphnode_create(&node, &functab, sizeof *m);
160 if (err != EOK){
161 return err;
163 m = node->extra;
165 m->n_inputs = 1;
166 m->format_is_set = false;
168 // create output pin
169 err = graphnode_add_pin(node, &m->out_pin);
170 if (err != EOK){
171 return err;
173 m->out_pin->dir = DIR_OUT;
174 m->out_pin->name = "out";
176 // create input pins
177 err = graphnode_add_pin(node, &in_pin);
178 if (err != EOK){
179 return err;
181 in_pin->dir = DIR_IN;
182 in_pin->name = "in0"; // how original
184 *node_out = node;
185 return EOK;