Issues with ProcessorGraph, race Condition from editor and processor?

void HRTFPluginV2AudioProcessor::updateGraph() {
    
    
    bool hasChanged = false;
    
    juce::Array<juce::AudioParameterChoice*> choices {processorSlot1.get(),
                                                      processorSlot2.get(),
                                                      processorSlot3.get(),
                                                      };


    juce::Array<juce::AudioParameterBool*> bypasses { bypassSlot1.get(),
                                                      bypassSlot2.get(),
                                                      bypassSlot3.get(),
                                                      };
                                                      
    juce::ReferenceCountedArray<Node> slots;
    slots.add(slot1Node);
    slots.add(slot2Node);
    slots.add(slot3Node);

    for (int i = 0; i < 3; ++i) {
        std::cout << "for loop idx: " << i << std::endl;
    
        auto& choice = choices.getReference (i);
        auto  slot   = slots  .getUnchecked (i);

        std::cout << "choice index: " << choice->getIndex() << std::endl;
        if (choice->getIndex() == 0) 
        {
            std::cout << "choice is empty" << std::endl;
            if (slot != nullptr) {
                mainProcessor->removeNode (slot.get());
                slots.set (i, nullptr);
                hasChanged = true;
            }
        }
        else if (choice->getIndex() == 1) 
        {
            std::cout << "choice->getIndex() == 1" << std::endl;
            if (slot != nullptr) {
                if (slot->getProcessor()->getName() == "HRTFProcessor")
                {
                    std::cout << "choice is HRTFProcessor" << std::endl;
                    continue;
                } 
                mainProcessor->removeNode (slot.get());
            }
            std::cout << "Making new HRTFProcessor" << std::endl;
            slots.set (i, mainProcessor->addNode (std::make_unique<HRTFProcessor>()));
            hasChanged = true;
        }
        else if (choice->getIndex() == 2) {
            std::cout << "choice->getIndex() == 2" << std::endl;
            if (slot != nullptr) {
                if (slot->getProcessor()->getName() == "EarlyReflections"){
                    std::cout << "choice is EarlyReflections" << std::endl;
                    continue;
                }
                mainProcessor->removeNode (slot.get());
            }
            std::cout << "Making new EarlyReflections" << std::endl;
            slots.set (i, mainProcessor->addNode (std::make_unique<EarlyReflections>()));
            hasChanged = true;
        }
        else if (choice->getIndex() == 3) {
            std::cout << "choice->getIndex() == 3" << std::endl;
            if (slot != nullptr) {
                if (slot->getProcessor()->getName() == "AirAbsorption"){
                    std::cout << "choice is AirAbsorption" << std::endl;
                    continue;
                }
                mainProcessor->removeNode (slot.get());
            }
            std::cout << "Making new AirAbsorption" << std::endl;
            slots.set (i, mainProcessor->addNode (std::make_unique<AirAbsorption>()));
            hasChanged = true;
        }
    }
    // If the graph has changed, we'll need to re-connect the nodes.
    if (hasChanged) {
        std::cout << "in hasChanged" << std::endl;
        for (auto connection : mainProcessor->getConnections()){ //remove all connections
            mainProcessor->removeConnection (connection);
            std::cout << "Removed a Connection" << std::endl;
        }
        juce::ReferenceCountedArray<Node> activeSlots;
        for (auto slot : slots) {
            if (slot != nullptr) {
                activeSlots.add (slot);                     
                slot->getProcessor()->setPlayConfigDetails (getMainBusNumInputChannels(),
                                                            getMainBusNumOutputChannels(),
                                                            getSampleRate(), getBlockSize());
            }
        }
        if (activeSlots.isEmpty()) { //if no active slots, connect input to output
            std::cout << "activeSlots is empty" << std::endl;
            connectAudioNodes();
        } 
        else 
        {
            std::cout << "activeSlots is not empty" << std::endl;
            for (int i = 0; i < activeSlots.size() - 1; ++i) {  //connect slots in series
                for (int channel = 0; channel < 2; ++channel) {
                    mainProcessor->addConnection ({ { activeSlots.getUnchecked (i)->nodeID,      channel },
                                                    { activeSlots.getUnchecked (i + 1)->nodeID,  channel } });
                }
            }
            for (int channel = 0; channel < 2; ++channel) { //connect input and output to first and last slots
                mainProcessor->addConnection ({ { audioInputNode->nodeID,         channel },
                                                { activeSlots.getFirst()->nodeID, channel } });
                mainProcessor->addConnection ({ { activeSlots.getLast()->nodeID,  channel },
                                                { audioOutputNode->nodeID,        channel } });
            }
        }
        connectMidiNodes();

        for (auto node : mainProcessor->getNodes()) { //enable all buses
            node->getProcessor()->enableAllBuses();
        }
        //deal with bypass state

        for (int i = 0; i < 3; ++i) {
            auto  slot   = slots   .getUnchecked (i);
            auto& bypass = bypasses.getReference (i);

            if (slot != nullptr){
                std::cout << "slot is not nullptr, set the bypass" << std::endl;
                slot->setBypassed (bypass->get());
            }
        }
 
        audioInputNode->setBypassed (muteInput->get());
        slot1Node = slots.getUnchecked (0);
        slot2Node = slots.getUnchecked (1);
        slot3Node = slots.getUnchecked (2);
    }                         
}

I use this updateGraph() function as per the CascasdingPlugins tutorial code, However, no matter what processor I select, from the print statements I can see that index 3 is always being selected as true, thus it is creating an AirAbsorption plugin, regardless of the selection.

class HRTFPluginV2AudioProcessorEditor  : public juce::AudioProcessorEditor,
                                        //  public juce::Slider::Listener,
                                          public juce::ComboBox::Listener, 
                                          public juce::Button::Listener
                                    
{
public:
  HRTFPluginV2AudioProcessorEditor (HRTFPluginV2AudioProcessor&, juce::AudioProcessorValueTreeState& vts);
  ~HRTFPluginV2AudioProcessorEditor() override;
     
  juce::String getImpResFileName();
  juce::String fileName;
  //====SETTERS=====
  void setAirTemperature();
  void setEarlyReflections();
  void setHumidity();
  void setAirPressure();
  void setDistance();
  void setAzimuth();
  void setElevation();
  void setMaxDelayTime();
  void setDelayTime1();
  void setDelayTime2();
  void setFeedback();
  void setWetLevel();
  void setImpResFileName();

  //========================================================================
  //For adding the sliders to the userParameters object
  typedef juce::AudioProcessorValueTreeState::SliderAttachment SliderAttachment;
  typedef juce::AudioProcessorValueTreeState::ButtonAttachment ButtonAttachment;

  
  //Do not need slider Listener any more as value tree handles the Listeners
  //=============Slider Listener Override from base class================
  // void sliderValueChanged(juce::Slider* slider) override 
  //   {}


  //=============ComboBox Listener Override=========================
void comboBoxChanged(juce::ComboBox* comboBox)
{
    int processorIndex = comboBox->getSelectedItemIndex();
    selectProcessor(comboBox, processorIndex);
}

void selectProcessor(juce::ComboBox* box, int choiceIndex)
{
    // Define a pointer to the processor slot that needs to be updated based on the ComboBox
    juce::AudioParameterChoice* targetSlot = nullptr;

    if (box == &processorBox1)
    {
        targetSlot = audioProcessor.processorSlot1.get();
    }
    else if (box == &processorBox2)
    {
        targetSlot = audioProcessor.processorSlot2.get();
    }
    else if (box == &processorBox3)
    {
        targetSlot = audioProcessor.processorSlot3.get();
    }

    // Ensure the targetSlot is valid before using it
    if (!targetSlot) return;

    targetSlot->setValueNotifyingHost(choiceIndex);
}


  
  //=============Button Listener Overrides=========================
  void buttonStateChanged	(juce::Button *  button)	override
  {
  }
  void buttonClicked(juce::Button* button) override
  {
    if (button == &bypassButton1)
    {
      audioProcessor.bypassSlot1->beginChangeGesture(); // Begin parameter change
      *audioProcessor.bypassSlot1 = button->getToggleState(); // Set bypass state based on button's state
      audioProcessor.bypassSlot1->endChangeGesture(); // End parameter change
    }
    else if (button == &bypassButton2)
    {
      audioProcessor.bypassSlot2->beginChangeGesture(); // Begin parameter change
      *audioProcessor.bypassSlot2 = button->getToggleState(); // Set bypass state based on button's state
      audioProcessor.bypassSlot2->endChangeGesture(); // End parameter change
    }
    else if (button == &bypassButton3)
    {
      audioProcessor.bypassSlot3->beginChangeGesture();
      *audioProcessor.bypassSlot3 = button->getToggleState(); 
      audioProcessor.bypassSlot3->endChangeGesture();
    }

    // Call updateGraph to reflect the changes in the graph
    audioProcessor.updateGraph();
  }


  
  
  void paint (juce::Graphics&) override;
  void resized() override;

private:
    // This reference is provided as a quick way for your editor to
    // access the processor object that created it.
    HRTFPluginV2AudioProcessor& audioProcessor;

     std::unique_ptr<EarlyReflections> earlyReflections;
     std::unique_ptr<HRTFProcessor> hrtfProcessor;
     std::unique_ptr<AirAbsorption> airAbsorption;

    //Title text box 
    juce::TextButton titleComponent;
    

    //=============Sliders================
    //--HRTF--
    juce::Slider azimuthSlider;
    juce::Label azimuthLabel;
    juce::Slider elevationSlider;
    juce::Label elevationLabel;
    //--AirAbsorption--
    juce::Slider airTemperatureSlider;
    juce::Label airTemperatureLabel;
    juce::Slider humiditySlider;
    juce::Label humidityLabel;
    juce::Slider airPressureSlider;
    juce::Label airPressureLabel;
    juce::Slider distanceSlider;
    juce::Label distanceLabel;
    //--EarlyReflections/Reverb--
    juce::Slider earlyReflectionTimeSlider;
    juce::Label earlyReflectionTimeLabel;
    juce::Slider maxDelayTimeSlider;
    juce::Label maxDelayTimeLabel;
    juce::Slider delayTime1Slider;
    juce::Label delayTime1Label;
    juce::Slider delayTime2Slider;
    juce::Label delayTime2Label;
    juce::Slider feedbackSlider;
    juce::Label feedbackLabel;
    juce::Slider wetLevelSlider;
    juce::Label wetLevelLabel;   

    //=========Attachment Pointers for putting Sliders in the ValueTree================
    //Ptr to the valueTreeState
    juce::AudioProcessorValueTreeState& valueTreeState;
    std::unique_ptr<juce::AudioProcessorValueTreeState::SliderAttachment> azimuthSliderPtr;
    std::unique_ptr<juce::AudioProcessorValueTreeState::SliderAttachment> elevationSliderPtr;
    std::unique_ptr<juce::AudioProcessorValueTreeState::SliderAttachment> airTemperatureSliderPtr;
    std::unique_ptr<juce::AudioProcessorValueTreeState::SliderAttachment> humiditySliderPtr;
    std::unique_ptr<juce::AudioProcessorValueTreeState::SliderAttachment> airPressureSliderPtr;
    std::unique_ptr<juce::AudioProcessorValueTreeState::SliderAttachment> distanceSliderPtr;
    std::unique_ptr<juce::AudioProcessorValueTreeState::SliderAttachment> earlyReflectionTimeSliderPtr;
    std::unique_ptr<juce::AudioProcessorValueTreeState::SliderAttachment> maxDelayTimeSliderPtr;
    std::unique_ptr<juce::AudioProcessorValueTreeState::SliderAttachment> delayTime1SliderPtr;
    std::unique_ptr<juce::AudioProcessorValueTreeState::SliderAttachment> delayTime2SliderPtr;
    std::unique_ptr<juce::AudioProcessorValueTreeState::SliderAttachment> feedbackSliderPtr;
    std::unique_ptr<juce::AudioProcessorValueTreeState::SliderAttachment> wetLevelSliderPtr;



    //===Look and Feel===

    std::unique_ptr<juce::LookAndFeel_V4> HRTFLook;
    std::unique_ptr<juce::LookAndFeel_V4> airAbsorptionLook;
    std::unique_ptr<juce::LookAndFeel_V4> earlyReflectionsLook;


    //==Impulse Response Stuff==//
    juce::TextButton loadBtn;
    std::unique_ptr<juce::FileChooser> fileChooser;
    juce::Label IRname;
    juce::String impResfileName;

    // //Proccessor Graph 

    juce::ToggleButton muteInputButton { "Mute Input" };
    juce::Label labelSlot1 { {}, "Slot 1" };
    juce::Label labelSlot2 { {}, "Slot 2" };
    juce::Label labelSlot3 { {}, "Slot 3" };
    

    juce::ComboBox processorBox1;
    juce::ComboBox processorBox2;
    juce::ComboBox processorBox3;

    juce::ToggleButton bypassButton1 { "Bypass 1" };
    juce::ToggleButton bypassButton2 { "Bypass 2" };
    juce::ToggleButton bypassButton3 { "Bypass 3" };

    
    JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (HRTFPluginV2AudioProcessorEditor)
};

This is my editor class, I belive using the processorChoices as part of a ComboBox may be having issues, but I don’t understand why.

For context here is my full AudioProcessor class:

class HRTFPluginV2AudioProcessor  : public juce::AudioProcessor, 
                                    public juce::AudioProcessorValueTreeState::Listener
                            #if JucePlugin_Enable_ARA
                             , public juce::AudioProcessorARAExtension
                            #endif
{
public:
  //==============================================================================
  HRTFPluginV2AudioProcessor();
  ~HRTFPluginV2AudioProcessor() override;

  //==============================================================================
  void prepareToPlay (double sampleRate, int samplesPerBlock) override;
  void releaseResources() override;

  #ifndef JucePlugin_PreferredChannelConfigurations
   bool isBusesLayoutSupported (const BusesLayout& layouts) const override;
  #endif

  void processBlock (juce::AudioBuffer<float>&, juce::MidiBuffer&) override;
  void processBlock (juce::AudioBuffer<double>&, juce::MidiBuffer&) override;
  //==============================================================================
  juce::AudioProcessorEditor* createEditor() override;
  bool hasEditor() const override;

  //==============================================================================
  const juce::String getName() const override;

  bool acceptsMidi() const override;
  bool producesMidi() const override;
  bool isMidiEffect() const override;
  double getTailLengthSeconds() const override;

  //==============================================================================
  int getNumPrograms() override;
  int getCurrentProgram() override;
  void setCurrentProgram (int index) override;
  const juce::String getProgramName (int index) override;
  void changeProgramName (int index, const juce::String& newName) override;

  //==============================================================================
  void getStateInformation (juce::MemoryBlock& destData) override;
  void setStateInformation (const void* data, int sizeInBytes) override;

  //===================MY CODE================
  //===============ValueTreeState===============
  std::atomic<float> getAzimuth();
  std::atomic<float> getElevation();
  std::atomic<float> getDistance();
  std::atomic<float> getAirTemperature();
  std::atomic<float> getEarlyReflections();
  std::atomic<float> getHumidity();
  std::atomic<float> getAirPressure();
  std::atomic<float> getMaxDelayTime();
  std::atomic<float> getDelayTime1();
  std::atomic<float> getDelayTime2();
  std::atomic<float> getFeedback();
  std::atomic<float> getWetLevel();

  //typedef for readability:
  typedef juce::AudioProcessorValueTreeState::SliderAttachment SliderAttachment;
  //===Setters===

  void setAirTemperatureFromEditor(float newValue);
  void setEarlyReflectionsFromEditor(float newValue);
  void setHumidityFromEditor(float newValue);
  void setAirPressureFromEditor(float newValue);
  void setDistanceFromEditor(float newValue);
  void setAzimuthFromEditor(float newValue);
  void setElevationFromEditor(float newValue);
  void setMaxDelayTimeFromEditor(float newValue);
  void setDelayTime1FromEditor(float newValue);
  void setDelayTime2FromEditor(float newValue);
  void setFeedbackFromEditor(float newValue);
  void setWetLevelFromEditor(float newValue);
  void setFileName(juce::String fileName);
    
  //===============ProcessorGraphMethods================
  void updateGraph();
  void initialiseGraph();
  void connectMidiNodes();
  void connectAudioNodes();
    
  //===============ProcessorGraphVariables================
  using AudioGraphIOProcessor = juce::AudioProcessorGraph::AudioGraphIOProcessor;
  using Node = juce::AudioProcessorGraph::Node;
  juce::StringArray processorChoices { "Empty", "HRTFProcessor", "AirAbsorption", "EarlyReflections" }; //list of available processors
  std::unique_ptr<juce::AudioProcessorGraph> mainProcessor;
    

  std::unique_ptr<juce::AudioParameterBool> muteInput;
  std::unique_ptr<juce::AudioParameterChoice> processorSlot1;
  std::unique_ptr<juce::AudioParameterChoice> processorSlot2;
  std::unique_ptr<juce::AudioParameterChoice> processorSlot3;
  std::unique_ptr<juce::AudioParameterBool> bypassSlot1;
  std::unique_ptr<juce::AudioParameterBool> bypassSlot2;
  std::unique_ptr<juce::AudioParameterBool> bypassSlot3;
    

  //==============================================================================
  //==Impulse Response ==// 
  juce::AudioProcessorValueTreeState treeState;
  juce::ValueTree variableTree;
  //===File Choosing Variables===
  juce::File root, savedFile;
  juce::String fileName;

   //==User Choice Variables ValueTree==
   juce::AudioProcessorValueTreeState userParameters;


private:
  //==Slider variables to be then set in other classes==//
  std::atomic<float> azimuth;
  std::atomic<float> elevation;
  std::atomic<float> airTemp;
  std::atomic<float> humidity;
  std::atomic<float> airPressure;
  std::atomic<float> distance;
  std::atomic<float> earlyReflectionsValue;
  std::atomic<float> maxDelayTime;
  std::atomic<float> delayTime1;
  std::atomic<float> delayTime2;
  std::atomic<float> feedback;
  std::atomic<float> wetLevel;

  //==atmomic Pointers to the user parameters==//
  std::atomic<float>* aziParam = nullptr;
  std::atomic<float>* eleParam = nullptr;
  std::atomic<float>* airTempParam = nullptr;
  std::atomic<float>* humidityParam = nullptr;
  std::atomic<float>* airPressureParam = nullptr;
  std::atomic<float>* distanceParam = nullptr;
  std::atomic<float>* earlyReflectionsValueParam = nullptr;
  std::atomic<float>* maxDelayTimeParam = nullptr;
  std::atomic<float>* delayTime1Param = nullptr;
  std::atomic<float>* delayTime2Param = nullptr;
  std::atomic<float>* feedbackParam = nullptr;
  std::atomic<float>* wetLevelParam = nullptr;


  //===============ProcessorGraph================
  Node::Ptr audioInputNode; //pointers to the main AudioProcessorGraph as well as the audio and midi input and output nodes
  Node::Ptr audioOutputNode;
  Node::Ptr midiInputNode;
  Node::Ptr midiOutputNode;

  Node::Ptr slot1Node; //pointers to the nodes for each of the 3 slots
  Node::Ptr slot2Node;
  Node::Ptr slot3Node;
    

  //==============================================================================
  //==Impulse Response ==//
  juce::AudioProcessorValueTreeState::ParameterLayout createParameterLayout();
   void parameterChanged(const juce::String& parameterID, float newValue) override;
  
  
  JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (HRTFPluginV2AudioProcessor)
};

Any suggestions on this or other improvements would be very much appreciated