@@ -609,7 +609,13 @@ earlier in the pipeline (data sources, for example) will be cheaper than the cos
...
@@ -609,7 +609,13 @@ earlier in the pipeline (data sources, for example) will be cheaper than the cos
higher level nodes, such as some kind of final post-processing endpoint. If you need to do mass
higher level nodes, such as some kind of final post-processing endpoint. If you need to do mass
detachments, detach starting from the lowest level nodes and work your way towards the final
detachments, detach starting from the lowest level nodes and work your way towards the final
endpoint node (but don't try detaching the node graph's endpoint). If the audio thread is not
endpoint node (but don't try detaching the node graph's endpoint). If the audio thread is not
running, detachment will be fast and detachment in any order will be the same.
running, detachment will be fast and detachment in any order will be the same. The reason nodes
need to wait for their input attachments to complete is due to the potential for desyncs between
data sources. If the node was to terminate processing mid way through processing it's inputs,
there's a chance that some of the underlying data sources will have been read, but then others not.
That will then result in a potential desynchronization when detaching and reattaching higher-level
nodes. A possible solution to this is to have an option when detaching to terminate processing
before processing all input attachments which should be fairly simple.
Another compromise, albeit less significant, is locking when attaching and detaching nodes. This
Another compromise, albeit less significant, is locking when attaching and detaching nodes. This
locking is achieved by means of a spinlock in order to reduce memory overhead. A lock is present
locking is achieved by means of a spinlock in order to reduce memory overhead. A lock is present
...
@@ -641,15 +647,12 @@ only be happening in a forward direction which means the "previous" pointer won'
...
@@ -641,15 +647,12 @@ only be happening in a forward direction which means the "previous" pointer won'
used. The same general process applies to detachment. See `ma_node_attach_to_output/input_node()`
used. The same general process applies to detachment. See `ma_node_attach_to_output/input_node()`
and `ma_node_detach_output_bus()` for the implementation of this mechanism.
and `ma_node_detach_output_bus()` for the implementation of this mechanism.
One outstanding problem exists regarding attaching and detaching. It is possible for an output bus
Loop detection is achieved through the use of a counter. At the ma_node_graph level there is a
to be detached while the audio thread is in the middle of processing it. This by itself is not a
counter which is updated after each read. There is also a counter for each node which is set to the
problem because the node is still valid. The problem is that of reattaching the output bus to a new
counter of the node graph plus 1 after each time it processes data. Before anything is processed, a
input bus while a read is still happening on the audio thread. What *could* happen is that the node
check is performed that the node's counter is lower or equal to the node graph. If so, it's fine to
is reattached to a new input bus which hasn't yet been iterated in the current call to
proceed with processing. If not, MA_LOOP is returned and nothing is output. This represents a sort
`ma_node_graph_read_pcm_frames()` thereby resulting in the node getting processed twice. This would
of termination point.
flow through the base data source and result in a desync because it's read from it twice in the
same call to `ma_node_graph_read_pcm_frames()`. This is an unusual scenario and would most likely
go unnoticed by the majority of people, but it's still an issue to consider.
*/
*/
...
@@ -786,6 +789,7 @@ struct ma_node_base
...
@@ -786,6 +789,7 @@ struct ma_node_base
ma_uint16consumedFrameCountIn;
ma_uint16consumedFrameCountIn;
/* These variables are read and written between different threads. */
/* These variables are read and written between different threads. */
volatilema_uint32readCounter;/* For loop prevention. Compared with the current read count of the node graph. If larger, means a loop was encountered and reading is aborted and no samples read. */
/* Adjust the volume of the splitter node's endpoints. We'll just do it 50/50 so that both of them combine to reproduce the original signal at the endpoint. */
@@ -108,11 +132,22 @@ int main(int argc, char** argv)
...
@@ -108,11 +132,22 @@ int main(int argc, char** argv)
/* Adjust the volume of the splitter node's endpoints. We'll just do it 50/50 so that both of them combine to reproduce the original signal at the endpoint. */
/* Adjust the volume of the splitter node's endpoints. We'll just do it 50/50 so that both of them combine to reproduce the original signal at the endpoint. */