Old 11-19-2015, 01:51 PM   #1
Argitoth
Human being with feelings
 
Argitoth's Avatar
 
Join Date: Feb 2008
Location: Mesa, AZ
Posts: 2,057
Default HOWTO: Create a multi-platform reaper plugin

WARNING: GUI stuff is currently a work in progress by Xenakios, will remove this message when it becomes nicely packaged and I have written a nice tutorial.

Special thanks to:
  • COCKOS for existing
  • SWS for trailblazing
  • Xenakios for all his persistence in helping me and the community in getting example Xcode and Visual Studio projects. The MyReaperPlugin example project is 99% Xenakios original code.
  • Breeder for helping me get a virtual mac running on my windows machine and answering all my REAPER API questions.
  • Etienne Laurin for answering a million C++ questions.


These instructions will be a collection of things I wish I had known when starting out. BTW mac regex implementation has a bug where infinite loop is caused if the regex expressions ends in a .*. I'd recommend using boost regex or other multiplatform regex library. Also, these instructions may be very noobie... take it or leave! Learning how to create reascript API functions is on my todo list.

KEY
fill in the blank, specific to you
exactly the text you must type

DOWNLOAD TEMPLATE PROJECTS
  • https://github.com/elanhickler/MyReaperPlugin.git
  • To ensure the plugin is compatible with your version of REAPER, or to ensure you have access to the latest API, generate a reaper_plugin_functions.h
    • Open REAPER and search for header in the actions list, run and overwrite the reaper_plugin_functions.h file in MyReaperPlugin\library\reaper_plugin
    • You may also check the "Write functions exported by third party extensions" to get access to SWS reascript API functions, for example. The example project comes with REAPER 5.1 header file.

FOLDER STRUCTURE
  • NameOfProject
    • binary (holds the final output, the dll/dylib files)
      • Release (added to .gitignore, holds the final dll/dylib release version)
      • Debug (added to .gitignore, holds the final dll/dylib debug version)
    • header (hpp files)
    • library (files from libraries you want to be responsible for managing and keeping up to date, otherwise do not put files here)
      • WDL (this library sits here nicely)
    • source (cpp files)
    • Visual Studio
      • NameOfProject.sln (solution file holds the project and the configuration/platform build options)
      • NameOfProject.vcxproj (the actual project file)
      • NameOfProject.vcxproj.filters (keeps track of project file organization)
      • NameOfProject.vcxproj.user (keeps track of debugger command arguments e.g. running reaper.exe when debugging)
      • NameOfProject.sdf (added to .gitignore)
      • NameOfProject.rc_mac_dlg (Windows GUI resource for Mac generated by swell/mac_resgen.php)
      • NameOfProject.rc_mac_menu (same as above)
    • Xcode
      • NameOfProject.xcodeproj (the actual project, right click and Show Package Contents to see contents, xcuserdata is added to .gitignore)
    • .gitignore (git file using special syntax which tells git clients what files should not be uploaded to repository)
    • main.cpp (code for implementing actions and building the reaper plugin)
    • main.hpp (included in main.cpp, define your actions here)

NAME YOUR PROJECT
* IMPORTANT: The the final dll or dylib file must have reaper_ as a prefix.
WIN:
  • In Solution Explorer right-click on MyReaperPlugin project (not the solution) --> Properties --> Configuration Properties --> General --> change Target Name
  • Make sure you are changing for all configurations and platforms by using the Configuration and Platform dropdown menu to edit all.
  • (Optional) Rename solution (.sln file)
  • (Optional) Rename project and re-add the project to the solution
MAC:
  • Folder icon --> Click on project --> Click on project under Project --> Build Settings --> Have All and Combined highlighted --> Search for Dynamic Library Install Name --> double click on the resulting field to rename
  • Folder icon --> Click on project --> Targets --> Single-click on a target to rename
  • Product --> Scheme --> Manage Schemes --> Click on a scheme --> Edit --> Build tab --> Remove old targets with - symbol, Re-add targets with the + symbol, do this for both schemes.
  • (optional) Rename project (.xcodeproj file)

SETUP DEBUGGING
WIN:
  • My Computer --> Advanced --> Environmental Variables
  • Under "System variables" click NEW, type REAPER64_DIR and/or REAPER32_DIR and path to reaper root in the respective fields.
  • Configuration manager dropdown menu, switch to Debug.
  • Solution platform dropdown menu, switch to x32 or x64.
  • Debug --> Start Debugging
MAC:
  • If you are using REAPER 32-bit you will have to change your executable destination. Product --> Scheme --> Manage schemes --> Click on debug --> Edit --> Run tab --> Executable
  • Scheme dropdown menu, navigate to Debug
  • Click the play button

BUILD RELEASE
WIN:
  • Solution configuration dropdown menu, switch to Release.
  • Solution platform dropdown menu, switch to x32 or x64.
  • Build --> Build Solution
MAC:
  • Scheme dropdown menu, navigate to Release
  • Click the play button
__________________
Soundemote - Home of the chaosfly and pretty oscilloscope.
MyReaperPlugin - Easy-to-use cross-platform C++ REAPER extension template

Last edited by Argitoth; 12-15-2015 at 11:43 AM.
Argitoth is offline   Reply With Quote
Old 11-24-2015, 04:18 PM   #2
Argitoth
Human being with feelings
 
Argitoth's Avatar
 
Join Date: Feb 2008
Location: Mesa, AZ
Posts: 2,057
Default

Create your first include files (hpp and cpp)
C++:
  • I'm not sure of the fastest way to do this, but I just create txt files by hand and and rename them to .hpp and cpp, throw them into the correct folders and drag them into the projects.
  • Some quick things to note, copying memory is wasteful when you are sure you don't need to copy memory.
    PHP Code:
    // new memory is allocated, a copy of the given variable value is sent
    // to the memory location, this is how functions are usually written.
    int myfunc(int myparam
    {     
        
    myparam 2//myparam is now 2, 
    }

    // myparam references the given variable. myparam becomes an alias of the
    // given variable, efficient use of cpu and memory.
    int myfunc(intmyparam
    {     
        
    myparam 2//myparam changes the input variable
    }

    // myparam references the given variable but it cannot be changed
    // use should use const as it makes it clear you don't intend to 
    // make changes to input variable, but you also don't need a whole
    // new variable.
    int myfunc(const intmyparam
    {   
        
    int myequation 3*myparam //good
        
    myparam 2//illegal 

  • To create a class, define all functions and members in the hpp as well as default values (unless you want to define default values upon construction of an object version of the class).
    PHP Code:
    /*************/
    /*MyClass.hpp*/
    /*************/
    #prama once // always do this at the top of each header, allows
                // you to include the .hpp in multiple places without
                // getting redefinition errors.

    class MyClass {
        public: 
    // public is assumed if not written
        
    MyClass(intb// no return type because it's t
                        // the class constructor function
        
    int dostuff(int x = -1); // x = 4 makes x default to x, you
                                // cannot use this for int& b
        
    private: // use private to hide a bunch of functions and variables 
        
    int a;   // that are only needed for the object to do stuff,
        
    intb;  // makes autocomplete easier to use as well.

    }

    /*************/
    /*MyClass.cpp*/
    /*************/
    #include MyClass.hpp // always do this

    // the stuff after : is called a "class initializer list"
    // incase you want to google it for more info
    MyClass(intneeded_for_b) : b(needed_for_b) {
       
    // do other stuff here when creating the object
    }

    // if you didn't have a reference member class:
    MyClass() {}

    // or if you wanted int a as a parameter
    MyClass(int a) {}

    // do not specify the defaults of parameters here!
    int dostuff(int x) {
        if (
    == -10// oh look, we did not give 
                            // a variable since x is still
                            // -1
        
    return b;

  • Using a class:
    PHP Code:
    /**********/
    /*main.cpp*/
    /**********/
    void main {
        
    int some_kinda_int;

        
    int another_int 2;

        
    MyClass mc(some_kinda_int); // make an object

        
    mc.dostuff(another_int); // use an object's function

        
    MyClass::dostuff(some_kinda_int
                           
    // this is how you use a class function
                           // without having to make an object first
                           // google c++ static class function
                           // to learn how.

  • Do not use if statements when switch/case is more appropriate.
    PHP Code:
    //Google "c++ enum" for more info. Also learn struct and namespace!
    enum {stateAstateBstateC// unnamed enumeration, will
    int myvar stateB;           // clutter your namespace

    enum MyStates {stateAstateBstateC// now it is named
    int myvar MyStates::stateB // that's how you access it
                                 // very good for autocomplete

    enum class MyStates {stateAstateBstateC}
    int myvar MyStates::stateB // illegal because int is not MyStates
    MyStates myvar MyStates::// good if you want to ensure you are
                                 // using the right enum
    switch (myvar) {
        
    //when you don't name the enum
        
    case stateA:
            break; 
    //very important to break
                   // or the code will continue
                   // to stateB

        //when you name the enum
        
    case MyStates::stateB:
            
    myvar MyStates::stateC
            
    continue; // this is a neat trick when
                      // the switch is inside a loop

        
    case MyStates::stateC:
            if (
    myvar !=  MyStates::stateA)
                return 
    0// combine if and switch when appropriate
            
    return // return a value or...
            
    return myvar // return the state
                         // useful for when
                         // inside a sub-function
        // default case.
        
    default: {     
            
    int x 3// use brackets here or you will get
         
    }             // error because x needs initialization
        
        // you can do more stuff here




Dealing with include file errors
WIN AND MAC:
  • Symbols not found: Most likely due to not having the cpp somewhere in Visual Studio or Xcode file list, or a .a or .lib file is not in the library search path. Also caused by incorrect use of "static" class members or not defining in the cpp declared in the hpp.
  • Duplicate symbols: In Xcode it is easy to add a cpp file twice to the compile list. Under the folder icon click on project --> Click on a target --> Go to Build Phases, open Compile Sources dropdown menu.
  • Redefinitions: Caused by a mismatch between the hpp and cpp version of a function or variable. Define default function parameters in the hpp only.
  • Header not found: The path to the file was not included in header search path or the #include definition is incorrect.
  • Still having trouble?: Sometimes libraries/include files require very specific search paths due to how include files are including other include files. Know what those very specific include paths are.

Debugger not hitting breakpoints
WIN AND MAC:
  • Put a breakpoint somewhere in the extern C function and continue form there.
  • hpp not found, symbols not found: Caused by the latest dll not being used by REAPER possibly due to plugin failing to be copied to the folder. Try Copying the plugin to the correct place by hand or rebuild from scratch.
  • still nothing?: Ensure that your plugin folder for the wrong architecture(32 or 64 bit)
  • Still having trouble?: Other things you can try: Check that you are launching the right REAPER exe. Delete dll, do not rebuild, check your actions list for your plugin actions (there should be none). Make sure you don't have multiple copies of the plugin in different places in your REAPER directory.

Still having trouble?:
Online IDE for quick tests (you should make a test version of your project for anything more complex rather than use online IDE):
Final tips
  • Do not repeat yourself (DRY): If you write something 3 times, it may be time to create a function.
  • Write nice code: Do not micro-optimize. Write code that looks nice. If you have speed problems, only then should you do something differently, do not go down the optimization rabbit hole without a reason!
  • Avoid using macros: It clutters the namespace. Use sparingly! They definitely have their uses.
  • Avoid using unsigned int or various versions of int unless you absolutely have to (do use size_t when appropriate)
  • Avoid using float unless you have to. Otherwise use double.
__________________
Soundemote - Home of the chaosfly and pretty oscilloscope.
MyReaperPlugin - Easy-to-use cross-platform C++ REAPER extension template

Last edited by Argitoth; 12-09-2015 at 07:34 AM.
Argitoth is offline   Reply With Quote
Old 11-24-2015, 04:19 PM   #3
Argitoth
Human being with feelings
 
Argitoth's Avatar
 
Join Date: Feb 2008
Location: Mesa, AZ
Posts: 2,057
Default

(optional) Use a Git client
  • Download: Sourcetree, really awesome git client IMO, and I've tried a handful.
  • Commit: save changes to local files
  • Push: save changes to remote files (the online repository)
  • Pull: get changes from remote files (you must do this before pushing)
  • Discard: Remove changes from local files (be careful with this!)
  • Be careful about not saving you changes in Visual Studio or Xcode and pulling changes from remote, you could lose unsaved changes right then and there.


(optional) How to setup
  • Step 1: Remote setup (Github.com)
  • Step 2: Local setup (using Sourcetree)
    • File --> Clone / New --> Clone
    • in Source URL: paste in HTTPS link
    • Destination path: your intended project folder
    • Name: your intended project name
    • Drag files and folders from inside MyReaperPlugin folder into your project folder
    • Commit and push changes to your repository by clicking Commit tab
    • click Unstaged files check box
    • Fill out a message in the commit text box
    • click Push changes immediately check box
    • click Commit
__________________
Soundemote - Home of the chaosfly and pretty oscilloscope.
MyReaperPlugin - Easy-to-use cross-platform C++ REAPER extension template

Last edited by Argitoth; 12-10-2015 at 02:50 AM.
Argitoth is offline   Reply With Quote
Old 11-25-2015, 03:46 PM   #4
Argitoth
Human being with feelings
 
Argitoth's Avatar
 
Join Date: Feb 2008
Location: Mesa, AZ
Posts: 2,057
Default

(optional) GET boost
WIN:MAC:
  • Open terminal
  • Get brew by using terminal command: ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"
  • enter xcode-select --install (not sure if this is a required step)
  • enter brew install boost --universal

(optional) SETUP ENVIRONMENTAL VARIABLES for boost
WIN:
  • My Computer --> Advanced --> Environmental Variables
  • Under "System variables" click NEW, type BOOST_ROOT and path to boost root in the respective fields
Xcode:
  • Go to project build settings
  • Click on + to get a dropdown menu and click on Add User-Defined Setting
  • Add BOOST_INCLUDE as /usr/local/Cellar/boost/1.59.0/include
  • Add BOOST_LIBRARY as /usr/local/Cellar/boost/1.59.0/lib

(optional) SETUP PROJECTS for boost
Visual Studio:
  • Project --> Properties --> C/C++ --> General
  • Additional Include Directories add $(BOOST_ROOT)
  • Project --> Properties --> Linker --> General
  • Additional Library Directories add $(BOOST_ROOT)/lib64-msvc-14.0 and $(BOOST_ROOT)/lib32-msvc-14.0
Xcode
  • In project build settings Header Search Paths add $BOOST_INCLUDE
  • In project build settings Library Search Paths add $BOOST_LIBRARY
  • Navigate to your boost lib folder
  • drag libboost_regex-mt.a into Xcode. Have "create groups" and all targets checked and click Finish
  • do the same as above for libboost_filesystem-mt.a and libboost_system-mt.a as filesystem also requires system.
__________________
Soundemote - Home of the chaosfly and pretty oscilloscope.
MyReaperPlugin - Easy-to-use cross-platform C++ REAPER extension template

Last edited by Argitoth; 12-09-2015 at 09:37 PM.
Argitoth is offline   Reply With Quote
Old 12-09-2015, 05:10 AM   #5
Argitoth
Human being with feelings
 
Argitoth's Avatar
 
Join Date: Feb 2008
Location: Mesa, AZ
Posts: 2,057
Default

Github repository for example reaper plugin project is live! There is still more work to be done, for example the project is for OS X 10.7 and up. If someone has an earlier version of mac I could help them setup for 10.6 and update the repository to include 10.6 support.

This template project could include user interface stuff in the future. (XENAKIOS!! yes I'm looking at you.)

Post here if you need help.
__________________
Soundemote - Home of the chaosfly and pretty oscilloscope.
MyReaperPlugin - Easy-to-use cross-platform C++ REAPER extension template

Last edited by Argitoth; 12-09-2015 at 06:14 AM.
Argitoth is offline   Reply With Quote
Old 12-09-2015, 04:48 PM   #6
cfillion
Human being with feelings
 
cfillion's Avatar
 
Join Date: May 2015
Location: Québec, Canada
Posts: 4,970
Default

Quote:
Originally Posted by Argitoth View Post
  • res.rc_mac_dlg (not sure what it's for)
  • res.rc_mac_menu (not sure what it's for)
These are the SWELL version of Windows's resource (RC) files for OS X. They are generated from the original RC file by the mac_resgen.php script found in SWELL's root (WDL/WDL/swell).

They are not needed unless you use Windows RC files for Win32 dialog templates or menus.

Last edited by cfillion; 12-09-2015 at 07:12 PM.
cfillion is offline   Reply With Quote
Old 12-09-2015, 05:09 PM   #7
Xenakios
Human being with feelings
 
Xenakios's Avatar
 
Join Date: Feb 2007
Location: Oulu, Finland
Posts: 8,062
Default

It's cool you are gathering all this info!

It will also serve as a stark reminder creating C++ extension plugins for Reaper is not a thing to be taken lightly. Anyone considering it should first thoroughly investigate other options such as ReaScript.
__________________
I am no longer part of the REAPER community. Please don't contact me with any REAPER-related issues.
Xenakios is offline   Reply With Quote
Old 12-09-2015, 09:13 PM   #8
Argitoth
Human being with feelings
 
Argitoth's Avatar
 
Join Date: Feb 2008
Location: Mesa, AZ
Posts: 2,057
Default

Quote:
Originally Posted by cfillion View Post
These are the SWELL version of Windows's resource (RC) files for OS X. They are generated from the original RC file by the mac_resgen.php script found in SWELL's root (WDL/WDL/swell).
thanks!

Quote:
Originally Posted by Xenakios View Post
It's cool you are gathering all this info! It will also serve as a stark reminder creating C++ extension plugins for Reaper is not a thing to be taken lightly...
Oh Xen, this was meant to encourage not discourage! IMO a C++ plugin is trivial once you have your eggs in a basket. I've done my best here to make putting your eggs in a basket as easy as downloading the repository. The struggles I've faced with C++ are only due to the complexity of what I was trying to do. But that's what the community is for, to answer questions and keep you moving. Now that I know a thing or two I can answer questions.
__________________
Soundemote - Home of the chaosfly and pretty oscilloscope.
MyReaperPlugin - Easy-to-use cross-platform C++ REAPER extension template
Argitoth is offline   Reply With Quote
Old 12-10-2015, 05:17 PM   #9
Xenakios
Human being with feelings
 
Xenakios's Avatar
 
Join Date: Feb 2007
Location: Oulu, Finland
Posts: 8,062
Default

I made a very simple win32 GUI example thing for the plugin. Unfortunately it doesn't build/run correctly on OS-X. Does anybody see what the error is? I suspect the .rc file for the dialog isn't correctly parsed and transformed by the php code generation script...The script of course doesn't give any error when it is run.
__________________
I am no longer part of the REAPER community. Please don't contact me with any REAPER-related issues.
Xenakios is offline   Reply With Quote
Old 12-10-2015, 07:02 PM   #10
Argitoth
Human being with feelings
 
Argitoth's Avatar
 
Join Date: Feb 2008
Location: Mesa, AZ
Posts: 2,057
Default

Just an idea you probably already considered: did you try breakpoints?
__________________
Soundemote - Home of the chaosfly and pretty oscilloscope.
MyReaperPlugin - Easy-to-use cross-platform C++ REAPER extension template
Argitoth is offline   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 04:57 PM.


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