Go Back   Cockos Incorporated Forums > REAPER Forums > MIDI Hardware, Control Surfaces, and OSC

Reply
 
Thread Tools Display Modes
Old Today, 12:18 PM   #25721
Geoff Waddington
Human being with feelings
 
Geoff Waddington's Avatar
 
Join Date: Mar 2009
Location: Dartmouth, Nova Scotia
Posts: 11,246
Default

Some may recall the drag and drop code for the ReMap window was fairly tricky.

In fact, there is separate code for Mac and Windows, PITA.

With the new approach, do we even need drag and drop anymore ?
__________________
To install you need the CSI Software and Support Files
For installation instructions and documentation see the Wiki
Donate -- via PayPal to waddingtongeoff@gmail.com
Geoff Waddington is offline   Reply With Quote
Old Today, 12:19 PM   #25722
jacksoonbrowne
Human being with feelings
 
jacksoonbrowne's Avatar
 
Join Date: Aug 2017
Location: Ottawa, Canada
Posts: 595
Default

Quote:
Originally Posted by Geoff Waddington View Post
Cool, going to do it a bit differently.

Seems strange to have the standalone function int X32GetColorValue(const rgba_color &color) that replaces some of the repeated code, but then in each of the overrides there is this repeated code:

Code:
       lastColor_ = color;

        int surfaceColor = X32GetColorValue(color);

        string oscAddress = "/ch/";
        if (widget_->GetChannelNumber() < 10)   oscAddress += '0';
        oscAddress += int_to_string(widget_->GetChannelNumber()) + "/config/color";
        surface_->SendOSCMessage(this, oscAddress.c_str(), surfaceColor);
No perfect answer here, but I prefer to see all of the code repeated and encapsulated in X32 classes, rather than having it spread out amongst class methods and standalone functions.

Code:
void OSC_X32FeedbackProcessor::SetColorValue(const rgba_color &color)
{
    if (lastColor_ != color)
    {
        lastColor_ = color;

        
        int surfaceColor = 0;
        int r = color.r;
        int g = color.g;
        int b = color.b;
        
        if (r == 64 && g == 64 && b == 64)                               surfaceColor = 8;    // BLACK
        else if (r > g && r > b)                                         surfaceColor = 1;    // RED
        else if (g > r && g > b)                                         surfaceColor = 2;    // GREEN
        else if (abs(r - g) < 30 && r > b && g > b)                      surfaceColor = 3;    // YELLOW
        else if (b > r && b > g)                                         surfaceColor = 4;    // BLUE
        else if (abs(r - b) < 30 && r > g && b > g)                      surfaceColor = 5;    // MAGENTA
        else if (abs(g - b) < 30 && g > r && b > r)                      surfaceColor = 6;    // CYAN
        else if (abs(r - g) < 30 && abs(r - b) < 30 && abs(g - b) < 30)  surfaceColor = 7;    // WHITE
        
        string oscAddress = "/ch/";
        if (widget_->GetChannelNumber() < 10)   oscAddress += '0';
        oscAddress += int_to_string(widget_->GetChannelNumber()) + "/config/color";
        surface_->SendOSCMessage(this, oscAddress.c_str(), surfaceColor);
    }
}
Code:
void OSC_X32IntFeedbackProcessor::SetColorValue(const rgba_color &color)
{
    if (lastColor_ != color)
    {
        lastColor_ = color;

        
        int surfaceColor = 0;
        int r = color.r;
        int g = color.g;
        int b = color.b;
        
        if (r == 64 && g == 64 && b == 64)                               surfaceColor = 8;    // BLACK
        else if (r > g && r > b)                                         surfaceColor = 1;    // RED
        else if (g > r && g > b)                                         surfaceColor = 2;    // GREEN
        else if (abs(r - g) < 30 && r > b && g > b)                      surfaceColor = 3;    // YELLOW
        else if (b > r && b > g)                                         surfaceColor = 4;    // BLUE
        else if (abs(r - b) < 30 && r > g && b > g)                      surfaceColor = 5;    // MAGENTA
        else if (abs(g - b) < 30 && g > r && b > r)                      surfaceColor = 6;    // CYAN
        else if (abs(r - g) < 30 && abs(r - b) < 30 && abs(g - b) < 30)  surfaceColor = 7;    // WHITE
        
        string oscAddress = "/ch/";
        if (widget_->GetChannelNumber() < 10)   oscAddress += '0';
        oscAddress += int_to_string(widget_->GetChannelNumber()) + "/config/color";
        surface_->SendOSCMessage(this, oscAddress.c_str(), surfaceColor);
    }
}
The other alternative would be to put it all in a standalone function, dunno, what do you think ?
What ever you would prefer.
__________________
AKA: Roy Wallingford
jacksoonbrowne is online now   Reply With Quote
Old Today, 12:26 PM   #25723
Geoff Waddington
Human being with feelings
 
Geoff Waddington's Avatar
 
Join Date: Mar 2009
Location: Dartmouth, Nova Scotia
Posts: 11,246
Default

Quote:
Originally Posted by jacksoonbrowne View Post
What ever you would prefer.
Yeah, it's a close call, but the standalone function would have to be passed the surface, widget, feedbackProcessor, and colour, so I think the duplicated encapsulated code is slightly better, as it will likely never change, so maintenance isn't really an issue.
__________________
To install you need the CSI Software and Support Files
For installation instructions and documentation see the Wiki
Donate -- via PayPal to waddingtongeoff@gmail.com
Geoff Waddington is offline   Reply With Quote
Old Today, 12:45 PM   #25724
Funkybot
Human being with feelings
 
Funkybot's Avatar
 
Join Date: Jul 2007
Location: New Joisey
Posts: 6,024
Default

Quote:
Originally Posted by Geoff Waddington View Post
Yup, and the option quagmire is sorted too

If you have EnableFocusedFXMapping on (the default), and there is no Zone definition, the ReMap window appears in Learn mode, ready to go.

Buttons at the bottom for AutoMap, EraseLastTouched, RemoveZone, Save, and Cancel, done.
I'm thinking about how this will play out and I'm not sure I love the idea of another window popping up on screen with focused FX mapping on. The windows popping up can get annoying in use. We already had a similar pop up window and nixed it.

I think it's better to have the surface in learn mode or AutoMap mode myself. That will be less intrusive and put the users in control. If AutoMap mode, the FX just maps. No action necessary. If Learn Mode you can Learn or do nothing. But on screen windows popping up can be peevish. I would say keep it as a "remap" for remapping things after the fact.

Also, how would this system work with GoFxSlot+NoMap? Same deal? Again, I would rather the surface do something and AutoMap or jump into Learn mode than an on screen pop-up window.

Just my two cents. I'm open to being wrong on this but I feel like we tried this already and nixed it for similar reasons.
__________________
CSI v3 Wiki
Funkybot is online now   Reply With Quote
Old Today, 12:53 PM   #25725
Geoff Waddington
Human being with feelings
 
Geoff Waddington's Avatar
 
Join Date: Mar 2009
Location: Dartmouth, Nova Scotia
Posts: 11,246
Default

Quote:
Originally Posted by Funkybot View Post
I'm thinking about how this will play out and I'm not sure I love the idea of another window popping up on screen with focused FX mapping on. The windows popping up can get annoying in use. We already had a similar pop up window and nixed it.

I think it's better to have the surface in learn mode or AutoMap mode myself. That will be less intrusive and put the users in control. If AutoMap mode, the FX just maps. No action necessary. If Learn Mode you can Learn or do nothing. But on screen windows popping up can be peevish. I would say keep it as a "remap" for remapping things after the fact.

Also, how would this system work with GoFxSlot+NoMap? Same deal? Again, I would rather the surface do something and AutoMap or jump into Learn mode than an on screen pop-up window.

Just my two cents. I'm open to being wrong on this but I feel like we tried this already and nixed it for similar reasons.
Good points all !

The thinking is that if you have Focused FX mapping on and there is no map, you would like to map it, so the window only opens if there is a no map.

Same for GoFXSlot.

Another benefit is for surfaces with low, or even nonexistent button counts, like an extender.

The jury is definitely still out on all of this...

[edit] One thing important to note -- in this new approach the Remap window is Learn mode.
__________________
To install you need the CSI Software and Support Files
For installation instructions and documentation see the Wiki
Donate -- via PayPal to waddingtongeoff@gmail.com

Last edited by Geoff Waddington; Today at 01:38 PM.
Geoff Waddington is offline   Reply With Quote
Old Today, 01:59 PM   #25726
jacksoonbrowne
Human being with feelings
 
jacksoonbrowne's Avatar
 
Join Date: Aug 2017
Location: Ottawa, Canada
Posts: 595
Default

Found a problem with Shutdown() when OSC messages per slice is active (message queueing active).
This is not X32 specific.

Code:
void Shutdown()
{
    // We want to stop polling
    shouldRun_ = false;
        
    // Zero out all Widgets before shutting down
    if (pages_.Get(currentPageIndex_))
        pages_.Get(currentPageIndex_)->ForceClear();
}
Because OSC message queueing is on, the ForceClear() will cause immediate queueing of all shutdown messages.

And then Shutdown() returns and Reaper exits before all queued messages have been sent.

What we need is a way to ensure all queued message have been flushed out before Shutdown() returns.

Something like this
Code:
void Shutdown()
{
    // We want to stop polling
    shouldRun_ = false;
        
    // Zero out all Widgets before shutting down
    if (pages_.Get(currentPageIndex_))
        pages_.Get(currentPageIndex_)->ForceClear();

    // PSUEDO CODE HERE
    FOR each OSC surface
    {
        IF queue NOT empty
            SLEEP(100)
    }
}
__________________
AKA: Roy Wallingford
jacksoonbrowne is online now   Reply With Quote
Old Today, 02:06 PM   #25727
Geoff Waddington
Human being with feelings
 
Geoff Waddington's Avatar
 
Join Date: Mar 2009
Location: Dartmouth, Nova Scotia
Posts: 11,246
Default

Quote:
Originally Posted by jacksoonbrowne View Post
Found a problem with Shutdown() when OSC messages per slice is active (message queueing active).
This is not X32 specific.

Code:
void Shutdown()
{
    // We want to stop polling
    shouldRun_ = false;
        
    // Zero out all Widgets before shutting down
    if (pages_.Get(currentPageIndex_))
        pages_.Get(currentPageIndex_)->ForceClear();
}
Because OSC message queueing is on, the ForceClear() will cause immediate queueing of all shutdown messages.

And then Shutdown() returns and Reaper exits before all queued messages have been sent.

What we need is a way to ensure all queued message have been flushed out before Shutdown() returns.

Something like this
Code:
void Shutdown()
{
    // We want to stop polling
    shouldRun_ = false;
        
    // Zero out all Widgets before shutting down
    if (pages_.Get(currentPageIndex_))
        pages_.Get(currentPageIndex_)->ForceClear();

    // PSUEDO CODE HERE
    FOR each OSC surface
    {
        IF queue NOT empty
            SLEEP(100)
    }
}
Nice catch !!

Will look into it.
__________________
To install you need the CSI Software and Support Files
For installation instructions and documentation see the Wiki
Donate -- via PayPal to waddingtongeoff@gmail.com
Geoff Waddington is offline   Reply With Quote
Old Today, 02:50 PM   #25728
Geoff Waddington
Human being with feelings
 
Geoff Waddington's Avatar
 
Join Date: Mar 2009
Location: Dartmouth, Nova Scotia
Posts: 11,246
Default

Does this flush the queue properly ?

Code:
    void Shutdown()
    {
        // GAW -- IMPORTANT
        
        // We want to stop polling
        shouldRun_ = false;
        
        // Zero out all Widgets before shutting down
        if (pages_.Get(currentPageIndex_))
        {
            pages_.Get(currentPageIndex_)->ForceClear();
            pages_.Get(currentPageIndex_)->Run(); // To flush queues
        }
    }
__________________
To install you need the CSI Software and Support Files
For installation instructions and documentation see the Wiki
Donate -- via PayPal to waddingtongeoff@gmail.com
Geoff Waddington is offline   Reply With Quote
Old Today, 02:59 PM   #25729
Geoff Waddington
Human being with feelings
 
Geoff Waddington's Avatar
 
Join Date: Mar 2009
Location: Dartmouth, Nova Scotia
Posts: 11,246
Default

Hmmm...

Wondering what it would be like if we reversed the Learn workflow.

Code:
Focus an FX for which there is no map
Learn Window (Remap) appears
Move a surface control
That control is selected in the Learn Window
Move a gui FX control with the mouse
They are linked
What say you ?
__________________
To install you need the CSI Software and Support Files
For installation instructions and documentation see the Wiki
Donate -- via PayPal to waddingtongeoff@gmail.com
Geoff Waddington is offline   Reply With Quote
Old Today, 03:04 PM   #25730
Funkybot
Human being with feelings
 
Funkybot's Avatar
 
Join Date: Jul 2007
Location: New Joisey
Posts: 6,024
Default

Quote:
Originally Posted by Geoff Waddington View Post
Hmmm...

Wondering what it would be like if we reversed the Learn workflow.

Code:
Focus an FX for which there is no map
Learn Window (Remap) appears
Move a control
That control is selected in the Learn Window
Move a gui FX control with the mouse
They are linked
What say you ?
What does that look like on screen? You wouldn't want users having to scroll through a long list of plugin parameters to find what Fader1 is mapped to. Some plugins have hundreds of parameters. Workflow wise it sounds good with my prior concerns about pop up screens remaining.
__________________
CSI v3 Wiki
Funkybot is online now   Reply With Quote
Old Today, 03:04 PM   #25731
jacksoonbrowne
Human being with feelings
 
jacksoonbrowne's Avatar
 
Join Date: Aug 2017
Location: Ottawa, Canada
Posts: 595
Default

Quote:
Originally Posted by Geoff Waddington View Post
Does this flush the queue properly ?

Code:
    void Shutdown()
    {
        // GAW -- IMPORTANT
        
        // We want to stop polling
        shouldRun_ = false;
        
        // Zero out all Widgets before shutting down
        if (pages_.Get(currentPageIndex_))
        {
            pages_.Get(currentPageIndex_)->ForceClear();
            pages_.Get(currentPageIndex_)->Run(); // To flush queues
        }
    }
Nope.

I have my Messages Per run set to 16 and when there are more than 16 messages queued, for example 54,
"pages_.Get(currentPageIndex_)->Run();" will only flush 16 messages

Seems we need a way to do multiple Run()'s if the queue is not empty.

Maye something like this:
Code:
    void Shutdown()
    {
        // GAW -- IMPORTANT
        
        // We want to stop polling
        shouldRun_ = false;
        
        // Zero out all Widgets before shutting down
        if (pages_.Get(currentPageIndex_))
        {
            pages_.Get(currentPageIndex_)->ForceClear();
            
            // PSUEDO CODE
            WHILE queue IS NOT EMPTY
                pages_.Get(currentPageIndex_)->Run(); // To flush queues
        }
    }
__________________
AKA: Roy Wallingford
jacksoonbrowne is online now   Reply With Quote
Reply

Thread Tools
Display Modes

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off

Forum Jump


All times are GMT -7. The time now is 03:20 PM.


Powered by vBulletin® Version 3.8.11
Copyright ©2000 - 2024, vBulletin Solutions Inc.