@@ -15,10 +15,11 @@ int AudioGraph::add_node(const std::string &name, NodeRoutingType type,
1515
1616 // Dynamically configure pin structures using the same unified ID pool
1717 if (type == NodeRoutingType::Mixer || type == NodeRoutingType::MergeSum) {
18- int inputs_to_create = ( num_inputs > 0 ) ? num_inputs : 2 ;
18+ int inputs_to_create = std::clamp (( num_inputs > 0 ) ? num_inputs : 2 , 2 , 8 ) ;
1919 for (int i = 0 ; i < inputs_to_create; ++i) {
2020 node.input_pin_ids .push_back (next_id_++);
2121 }
22+ node.input_gains .assign (inputs_to_create, 1 .0f );
2223 node.output_pin_ids .push_back (next_id_++); // 1 Output Pin
2324 } else if (type == NodeRoutingType::Splitter) {
2425 node.input_pin_ids .push_back (next_id_++); // 1 Input Pin
@@ -342,6 +343,151 @@ void AudioGraph::restore_link(const GraphLink& link) {
342343 rebuild_topology ();
343344 }
344345}
346+ bool AudioGraph::add_input_pin (int node_id) {
347+ for (auto &node : nodes_) {
348+ if (node.id == node_id && node.routing_type == NodeRoutingType::Mixer) {
349+ if (node.input_pin_ids .size () < 8 ) {
350+ node.input_pin_ids .push_back (next_id_++);
351+ node.input_gains .push_back (1 .0f );
352+ // Do not necessarily need to rebuild topology if we just added an unconnected pin
353+ return true ;
354+ }
355+ return false ;
356+ }
357+ }
358+ return false ;
359+ }
345360
361+ bool AudioGraph::remove_input_pin (int node_id, int pin_id) {
362+ for (auto &node : nodes_) {
363+ if (node.id == node_id && (node.routing_type == NodeRoutingType::Mixer || node.routing_type == NodeRoutingType::MergeSum)) {
364+ if (node.input_pin_ids .size () > 2 ) {
365+ if (node.input_gains .size () < node.input_pin_ids .size ()) {
366+ node.input_gains .resize (node.input_pin_ids .size (), 1 .0f );
367+ }
368+ int index_to_remove = -1 ;
369+ if (pin_id == -1 ) {
370+ index_to_remove = node.input_pin_ids .size () - 1 ;
371+ } else {
372+ for (size_t i = 0 ; i < node.input_pin_ids .size (); ++i) {
373+ if (node.input_pin_ids [i] == pin_id) {
374+ index_to_remove = i;
375+ break ;
376+ }
377+ }
378+ }
379+ if (index_to_remove != -1 ) {
380+ int pin_to_remove = node.input_pin_ids [index_to_remove];
381+ // Prevent removal if the pin is linked
382+ for (const auto &link : links_) {
383+ if (link.dest_pin_id == pin_to_remove) {
384+ return false ;
385+ }
386+ }
387+ node.input_pin_ids .erase (node.input_pin_ids .begin () + index_to_remove);
388+ node.input_gains .erase (node.input_gains .begin () + index_to_remove);
389+ return true ;
390+ }
391+ }
392+ return false ;
393+ }
394+ }
395+ return false ;
396+ }
397+
398+ void AudioGraph::restore_input_pin (int node_id, int pin_id, int index, float gain) {
399+ for (auto &node : nodes_) {
400+ if (node.id == node_id) {
401+ if (index >= 0 ) {
402+ size_t idx = static_cast <size_t >(index);
403+ if (idx <= node.input_pin_ids .size ()) {
404+ node.input_pin_ids .insert (node.input_pin_ids .begin () + idx, pin_id);
405+ while (node.input_gains .size () < idx) {
406+ node.input_gains .push_back (1 .0f );
407+ }
408+ node.input_gains .insert (node.input_gains .begin () + idx, gain);
409+ } else {
410+ node.input_pin_ids .push_back (pin_id);
411+ node.input_gains .push_back (gain);
412+ }
413+ } else {
414+ node.input_pin_ids .push_back (pin_id);
415+ node.input_gains .push_back (gain);
416+ }
417+ if (pin_id >= next_id_) next_id_ = pin_id + 1 ;
418+ break ;
419+ }
420+ }
421+ }
422+
423+ void AudioGraph::set_mixer_input_gain (int node_id, size_t pin_index, float gain) {
424+ for (auto &node : nodes_) {
425+ if (node.id == node_id && node.routing_type == NodeRoutingType::Mixer) {
426+ if (pin_index < node.input_gains .size ()) {
427+ node.input_gains [pin_index] = std::clamp (gain, 0 .0f , 2 .0f );
428+ }
429+ break ;
430+ }
431+ }
432+ }
433+
434+ bool AudioGraph::add_output_pin (int node_id) {
435+ for (auto &node : nodes_) {
436+ if (node.id == node_id && node.routing_type == NodeRoutingType::Splitter) {
437+ if (node.output_pin_ids .size () < 8 ) {
438+ node.output_pin_ids .push_back (next_id_++);
439+ return true ;
440+ }
441+ return false ;
442+ }
443+ }
444+ return false ;
445+ }
446+
447+ bool AudioGraph::remove_output_pin (int node_id, int pin_id) {
448+ for (auto &node : nodes_) {
449+ if (node.id == node_id && node.routing_type == NodeRoutingType::Splitter) {
450+ if (node.output_pin_ids .size () > 2 ) {
451+ int index_to_remove = -1 ;
452+ if (pin_id == -1 ) {
453+ index_to_remove = node.output_pin_ids .size () - 1 ;
454+ } else {
455+ for (size_t i = 0 ; i < node.output_pin_ids .size (); ++i) {
456+ if (node.output_pin_ids [i] == pin_id) {
457+ index_to_remove = i;
458+ break ;
459+ }
460+ }
461+ }
462+ if (index_to_remove != -1 ) {
463+ int pin_to_remove = node.output_pin_ids [index_to_remove];
464+ for (const auto &link : links_) {
465+ if (link.source_pin_id == pin_to_remove) {
466+ return false ;
467+ }
468+ }
469+ node.output_pin_ids .erase (node.output_pin_ids .begin () + index_to_remove);
470+ return true ;
471+ }
472+ }
473+ return false ;
474+ }
475+ }
476+ return false ;
477+ }
478+
479+ void AudioGraph::restore_output_pin (int node_id, int pin_id, int index) {
480+ for (auto &node : nodes_) {
481+ if (node.id == node_id && node.routing_type == NodeRoutingType::Splitter) {
482+ if (index >= 0 && index <= node.output_pin_ids .size ()) {
483+ node.output_pin_ids .insert (node.output_pin_ids .begin () + index, pin_id);
484+ } else {
485+ node.output_pin_ids .push_back (pin_id);
486+ }
487+ if (pin_id >= next_id_) next_id_ = pin_id + 1 ;
488+ break ;
489+ }
490+ }
491+ }
346492
347493} // namespace Amplitron
0 commit comments