Via Reaper's SetExtState(...), notify interested applications as to which tracks are visible on the control surface

Issue #48 on hold
Christopher Norman created an issue

Hi there,

I’ve been working on an enhanced meter bridge for control surfaces. In order enable the bridge’s display to sync to control surface I’ve hastily, dirtily and windows-only-ly modified the MCU plugin as per the code snippet. In this guise it isn’t cross-platform, as it uses Windows mutexes as opposed to those from Boost, which are a file lock, not a mutex.

Any chance we could have this for the benefit of the broader community?

Best regards,

CJPN

String m_cjpn_previous_successful_track_visible_GUIDs = "";

void Tracks::updateTrackStates(int numMCUChannels) {
    BOOST_FOREACH(tTrackStates::value_type & v, m_trackStates) {
        if (!v.second -> getMediaTrack())
            continue;

        v.second -> setIsOnMCUChannel(0);

        if (m_pOptions2 -> isOptionSetTo(MTO2_TCP_ADJUCT, MTO2A_TCP_NO)) {
            v.second -> setIsShownInTCP(
                MediaTrackInfo::isShownInTCP(v.second -> getMediaTrack()));
            v.second -> setTCPHeight(
                MediaTrackInfo::getHeight(v.second -> getMediaTrack()));
        }

        if (m_pOptions2 -> isOptionSetTo(MTO2_MCP_ADJUCT, MTO2A_MCP_NO)) {
            v.second -> setIsShownInMCP(
                MediaTrackInfo::isShownInMCP(v.second -> getMediaTrack()));
        }
    }
    //CJPN
    String ext_section = "Control_Surface_Klinke";
    String ext_key = "Visible_Tracks_By_GUID";
    String cjpn_visible_tracks_guids = "";

    for (int c = 1; c <= numMCUChannels; c++) {
        MediaTrack * pMT = getMediaTrackForChannel(c);

        //CJPN
        if (c > 1) cjpn_visible_tracks_guids += ",";

        if (pMT == NULL) {
            //CJPN
            cjpn_visible_tracks_guids += "NULL";

            continue;
        }

        TrackState * pTS = getTrackStateForMediaTrack(pMT);

        //CJPN
        cjpn_visible_tracks_guids += pTS -> getGuidAsString();

        safe_call(pTS, setIsOnMCUChannel(c));
    }

    //CJPN notify Reaper of tracks visible on the control surface.  This, in turn, can be consumed by any interested party by locking the mutex and calling GetExtState on "Control_Surface" / "Visible_Tracks_By_GUID"
    //Assumes we're single-threaded by this point.  Chaos will ensue if we're called by multiple threads.
    bool lock_success = false;
    bool hasNotifiedReaper = false;
    if (!m_cjpn_previous_successful_track_visible_GUIDs.equalsIgnoreCase(cjpn_visible_tracks_guids)) {
        try {
            //Notify Reaper.
            lock_success = reaper_extstate_mutex.lock(10); //Wait for 10ms 
            if (lock_success) {
                SetExtState(ext_section.toCString(), ext_key.toCString(), cjpn_visible_tracks_guids.toCString(), false); //false for non-persisted

                //Other synchronised shenanigans here?

                //IMPORTANT! This must be the last line in the lock
                hasNotifiedReaper = true;
            }
        } catch (...) {
            //Play dumb
            int breakhere = 123;
        }

        if (lock_success)
            reaper_extstate_mutex.unlock();

        //Keep a record to prevent spamming of Reaper SetExtState API, yet keep trying until the call to SetExtState, or antyhing else in the muxex-lock, has succeeded in response to a change of state.
        if (hasNotifiedReaper)
            m_cjpn_previous_successful_track_visible_GUIDs = cjpn_visible_tracks_guids;
    }
}

Comments (7)

  1. Christopher Norman reporter

    Further up the class:

    #include "NamedMutex.h"
    
    NamedMutex reaper_extstate_mutex;
    
    Tracks::Tracks(void)
        : m_pCurrentBaseTrack(NULL), m_pOptions1(NULL), m_pOptions2(NULL),
          m_globalOffset(0) {
      m_selectedTracks.clear();
      m_pAllTracksBefore = new tTrackSet();
      m_pAllTracksNow = new tTrackSet();
    
      m_projectChangedConnectionId =
          ProjectConfig::instance()->connect2ProjectChangeSignal(
              boost::bind(&Tracks::projectChanged, this, _1, _2));
    
      try 
      {
          reaper_extstate_mutex = NamedMutex(L"Global\\com.cjpn.reaper.extstate.Control_Surface_Klinke.Visible_Tracks_By_GUID.B528D44C-702D-4EF1-A1C1-9DF78CD69E1A");
      }
      catch (...)
      {
          //Well, that went horribly wrong
      }
    }
    

  2. Steffen Fuerst repo owner

    Hmm, I am not sure. Can you please tell me more about this project (do you have a link)? And how many users does it have?

  3. Christopher Norman reporter

    To put it to you directly I have a cross-platform (WIn/IOS/Android) meter bridge working locally and over network.

  4. Christopher Norman reporter

    People, sooner or later, will desire a better meter bridge for their Mackie Control. Or similar. Especially if they can merely use an old phone.

  5. Log in to comment