8 static struct graphpin
*get_last_pin(struct graphnode
*node
)
11 for (pin
=node
->pins
; pin
&& pin
->next
; pin
=pin
->next
);
15 struct graphpin
*graphnode_get_pin(struct graphnode
*node
, enum direction dir
)
18 for (pin
=node
->pins
; pin
; pin
=pin
->next
){
26 err_t
graphnode_add_pin(struct graphnode
*node
, struct graphpin
**pin_out
)
28 // TODO: allocate node + pins in one block?
29 struct graphpin
*pin
= malloc(sizeof *pin
), *pin_prev
;
32 return make_error(ENOMEM
, node
, "memory full: allocating graph node pin");
34 memset(pin
, 0, sizeof *pin
);
39 pin_prev
= get_last_pin(node
);
50 err_t
graphnode_create(struct graphnode
**node_out
, const struct graphnode_functab
*functab
, size_t extra_bytes
)
52 struct graphnode
*node
;
53 node
= malloc(sizeof *node
+ extra_bytes
);
55 return make_error(ENOMEM
, NULL
, "memory full: allocating graph node");
57 memset(node
, 0, sizeof *node
);
58 node
->functab
= functab
;
59 node
->extra
= node
->extra_data
;
64 void graphnode_free(struct graphnode
*node
)
66 struct graphpin
*pin
, *pin_next
;
67 struct graphedge
*edge
;
74 // disconnect it (free the edge)
78 free(edge
); // XXX: may need more freeing than this in the future
87 bool graphnode_is_source(struct graphnode
*node
)
90 for (pin
=node
->pins
; pin
; pin
=pin
->next
){
91 if (pin
->dir
!= DIR_OUT
){
98 bool graphnode_all_inputs_connected(struct graphnode
*node
)
100 struct graphpin
*pin
;
101 for (pin
=node
->pins
; pin
; pin
=pin
->next
){
102 if (pin
->dir
== DIR_IN
&& !pin
->edge
){
109 err_t
graph_create(struct graph
*graph
)
111 graph
->node_first
= graph
->node_last
= NULL
;
115 void graph_destroy(struct graph
*graph
)
117 struct graphnode
*node
= graph
->node_first
, *node_next
;
119 node_next
= node
->next
;
120 graphnode_free(node
);
123 graph
->node_first
= graph
->node_last
= NULL
;
126 err_t
graph_add_node(struct graph
*graph
, struct graphnode
*node
)
128 node
->prev
= graph
->node_last
;
130 *(graph
->node_last
? &graph
->node_last
->next
: &graph
->node_first
) = node
;
131 graph
->node_last
= node
;
135 err_t
graph_remove_node(struct graph
*graph
, struct graphnode
*node
)
137 *(node
->prev
? &node
->prev
->next
: &graph
->node_first
) = node
->next
;
138 *(node
->next
? &node
->next
->prev
: &graph
->node_last
) = node
->prev
;
142 // guess the format of output pin 'pin' - i.e. same as input format
143 static err_t
guess_output_format(struct graphpin
*pin
, struct aformat
*af
)
145 struct graphnode
*node
;
146 struct graphpin
*pin2
;
147 bool found_input
= false;
150 for (pin2
=node
->pins
; pin2
; pin2
=pin2
->next
){
151 if (pin2
->dir
== DIR_IN
){
153 return make_error(ECANNOT_GUESS_FORMAT
, node
,
154 "cannot guess the output format of node \"%s\", because it has more than one input",
159 // the input pin is not connected, so it won't have
161 return make_error(ECANNOT_GUESS_FORMAT
, node
,
162 "cannot guess the output format of node \"%s\", because its input is not connected",
165 *af
= pin2
->edge
->buf
.format
;
171 // create a converter node to convert between formats
172 static err_t
create_converter_node(struct graph
*graph
, struct aformat
*src_af
, struct aformat
*dest_af
,
173 struct graphpin
*a
, struct graphpin
*b
, struct graphnode
**node_out
)
175 struct graphnode
*node
;
176 struct graphpin
*out_pin
, *in_pin
;
178 // create the new node
179 err
= audio_converter_create(&node
, src_af
, dest_af
);
184 err
= graph_add_node(graph
, node
);
190 out_pin
= graphnode_get_pin(node
, DIR_OUT
);
191 in_pin
= graphnode_get_pin(node
, DIR_IN
);
195 err
= graph_connect(graph
, a
, in_pin
);
200 err
= graph_connect(graph
, out_pin
, b
);
209 static err_t
do_connect(struct graph
*graph
, struct graphpin
*a
, struct graphpin
*b
, const struct aformat
*af
)
211 struct graphedge
*edge
= malloc(sizeof *edge
);
215 return make_error(ENOMEM
, graph
, "memory full: allocating edge");
218 buffer_init(&edge
->buf
);
219 edge
->buf
.format
= *af
;
221 if (a
->node
->functab
->set_buffer
){
222 err
= a
->node
->functab
->set_buffer(a
->node
, a
, &edge
->buf
);
229 if (b
->node
->functab
->set_buffer
){
230 err
= b
->node
->functab
->set_buffer(b
->node
, b
, &edge
->buf
);
239 edge
->processed
= false;
240 a
->edge
= b
->edge
= edge
;
244 err_t
graph_connect(struct graph
*graph
, struct graphpin
*a
, struct graphpin
*b
)
246 struct aformat af
, af2
;
247 struct graphnode
*cvt_node
;
248 struct graphpin
*cvt_input
;
251 // check a and b direction
252 if (!(a
->dir
== DIR_OUT
&& b
->dir
== DIR_IN
)){
254 return make_error(EPINDIR
, graph
,
255 "incorrect pin directions while trying to connect");
257 // if a is a source node, it will be able to provide us with a media type
258 // if a is not a source node, all its inputs must be connected
259 // otherwise we won't know what output format to use.
260 if (graphnode_all_inputs_connected(a
->node
)){
261 // get media type from source
262 if (a
->node
->functab
->get_output_format
){
263 err
= a
->node
->functab
->get_output_format(a
->node
, a
, &af
);
268 // assume output format is same as input format
269 err
= guess_output_format(a
, &af
);
274 // check media type with b node
275 if (b
->node
->functab
->is_acceptable_input_format
276 && !b
->node
->functab
->is_acceptable_input_format(b
->node
, b
, &af
)){
277 // it doesn't like that input format
278 // we'll have to add a converter node
279 if (b
->node
->functab
->get_ideal_input_format
){
280 err
= b
->node
->functab
->get_ideal_input_format(b
->node
, b
, &af2
);
285 return create_converter_node(graph
, &af
, &af2
,
288 return make_error(ENO_AGREEABLE_FORMAT
, graph
, "cannot agree on a common media format (\"%s\" won't specify an ideal format)",
292 // actually do the connection
293 return do_connect(graph
, a
, b
, &af
);
295 // there are unconnected inputs, so we can't connect
296 // (because we don't know what output format to use
297 // without an input format)
298 return make_error(EUNCONNECTED
, graph
,
299 "unconnected input pins: cannot determine media type (\"%s\")",
305 // count the number of unprocessed incoming edges of a node
306 static int count_incoming(struct graphnode
*node
)
308 struct graphpin
*pin
;
310 for (pin
=node
->pins
; pin
; pin
=pin
->next
){
311 if (pin
->dir
== DIR_IN
&& pin
->edge
&& !pin
->edge
->processed
){
318 err_t
graph_sort(struct graph
*graph
)
322 struct stack_item
*prev
;
323 struct graphnode
*node
;
324 } *S
= NULL
, *new, *old
;
326 struct graphnode
*n
, *m
, *prev_L
, **next_ptr_ptr
;
327 struct graphpin
*pin
;
328 struct graphedge
*edge
;
330 // find all source nodes
331 for (n
=graph
->node_first
; n
; n
=n
->next
){
332 if (graphnode_is_source(n
)){
334 new = malloc(sizeof *new); // TODO: use alloca or something?
338 return make_error(ENOMEM
, NULL
, "memory full: allocating stack item");
346 // while S is not empty
347 next_ptr_ptr
= &graph
->sorted_nodes
;
350 // remove a node n from S
355 // insert n into L (the sorted list)
356 n
->sorted_prev
= prev_L
;
358 next_ptr_ptr
= &n
->sorted_next
;
360 // for each node m with an edge from n to m
361 for (pin
=n
->pins
; pin
; pin
=pin
->next
){
362 if (pin
->dir
!= DIR_OUT
363 || !pin
->edge
// pin isn't connected (ignore it)
364 || pin
->edge
->processed
// edge already 'removed' by algorithm
370 // remove edge from graph (we actually want to keep it, so we just
371 // mark it, and not do any real removal)
372 edge
->processed
= true;
373 // does it have any other incoming edges?
374 if (count_incoming(m
) == 0){
377 new = malloc(sizeof *new);
381 return make_error(ENOMEM
, NULL
, "memory full: allocating stack item");
389 // set the next pointer of the last node in the sequence to NULL
390 // this finishes off the linked list.
391 *next_ptr_ptr
= NULL
;
393 // TODO: if the graph still has unprocessed edges, then the graph
394 // has at least one cycle. Maybe we should do something about that.
399 err_t
graph_run(struct graph
*graph
)
401 struct graphnode
*node
;
404 for (node
=graph
->sorted_nodes
; node
; node
=node
->sorted_next
){
405 if (node
->functab
->run
){
406 err
= node
->functab
->run(node
);