Old 12-02-2009, 11:05 AM   #121
ajaym
Human being with feelings
 
Join Date: Aug 2009
Posts: 210
Default BCR2000

Thanks for the advice. I wasn't sure about action codes, but I see now how that works, many thanks.

I definitely have a call to IMPAPI for that OnPauseButtonEx function, but I'll go back and have another fiddle shortly. The control surface code examples I have all seem to use CSurf_OnStop which isn't really ideal. Actually, perhaps there's an action for pause. I'll check that, because if so, that'd be another way to do it.

Meanwhile I have all the FX parameter control stuff fully working, so now the surface can control every parameter of every FX insert on a track, full duplex.

This pretty much leaves the scrubbing stuff and the send stuff, and then I'm done and will start the unit testing phase, using some Reaper test projects, before I'm comfortable releasing it.

Meanwhile one quirk I did find is that some FX parameters - ReaComp's threshold, for example, report that their max value is 2.0 but in fact return a max value up to 2.5. Because Sonar normalises all parameters of everything to the range 0.... 1.0, the surface code is designed to work with that assumption and the external console UI, in particular, won't expect or cope with out-of-range values. So I am now normalising FX parameters on the way in and denormalising them on the way out. However, because Reacomp is telling fibs on me, the surface will not allow the threshold to be set all the way to the maximum possible value because it has scaled the values internally by Reacomp's claimed maximum value and will clip anything out of range to avoid internal failures. A minor quirk, though.

Summary of functionality now completely ported:

Control volume, pan, mute, solo for all tracks
Bank switching forward/backward by 8 tracks or 1 track at a time
Full transport control (subject to caveat regarding pause at this stage)
Select any track and/or make that track the leftmost controlled track on the surface
Control up to 8 FXs on any track, with up to 64 parameters per FX. Page switching on the control surface allows you to switch quickly between FXs and back to the main console view.
Coarse and fine parameter control for any param.
Full console UI displays a BCR2000 mimic console with scribble strips showing all control assignments, as well as the currently-selected FX, if applicable.

To be ported:

Control pan and gain for channel send 1, if present
Transport scrubbing
Alternate transport functions as discussed previously
ajaym is offline   Reply With Quote
Old 12-04-2009, 10:40 AM   #122
ajaym
Human being with feelings
 
Join Date: Aug 2009
Posts: 210
Default BCR2000 control surface

Ok, all functionality is now complete (even scrubbing, which turned out to be simpler than I had expected).
Once again, many many thanks for everyone's help. I understand the Reaper API much better now and it is great to have this working; I really missed being able to use my BCR2000 from Reaper and I hope others will find this plugin as useful as I have.

If time permits I will update some of the Wiki entries for the APIs with what I now know, so that others following in my footsteps will maybe not bug you guys so much with endless questions.

As the Sonar version is already hosted on Sourceforge, I'll be updating that project with a new version which will support both Sonar and Reaper from the one project tree, I think. I'll post back to the main forum when this has been done, probably tomorrow. I would like to rack up a little more testing, although at present it looks pretty rock solid. Also I need to create Reaper-specific install instructions, which will be just slightly different from Sonar.

HOWEVER

There are two bugs, I believe, with Reaper, that I uncovered and at present have no workaround for

One is minor, the other a little less so.

The first (minor) one is that programmatically changing the send volume or pan for a track works fine, and if you bring up the full send dialogue with the sliders, you can see them update correctly. However, if only the small send UI is displayed in the track, it does not update when the send volume or pan is changed programmatically. I could not see any obvious notification API that I should call, other than changing volume and pan as shown in the code snippet below (for volume). It looks like a screen redraw issue.

(fVal is a normalised parameter value as a float (0..1). The surface code manages all parameters in this way)

TRACK_VOL is "D_VOL"

// Reaper will return a NULL pointer if there is no send
// This is used as a guard to avoid attempting to update or retrieve non
// existent send information. I could not find any API to indicate
// the send count for a track, hence this approach.
pdVal = (double *) GetSetTrackSendInfo(tr, 0, 0, TRACK_VOL, NULL);
if (pdVal != NULL)
{
cVal = (unsigned char)(fVal * 127); //back to range 0-127
dVal=charToVol(cVal);
GetSetTrackSendInfo(tr, 0, 0, TRACK_VOL,&dVal);
}
break;

The second, more significant issue, is that the master track (track 0) doesn't behave consistently regarding getting/setting track properties. In particular, mute and solo can be programmatically set but the following code snippet, which works for all non-master tracks, to retrieve the mute status, always returns FALSE for both mute or solo (mute code shown)
(tr was retrieved for track 0, the master track, and is a valid reference)

bVal=*(bool *)GetSetMediaTrackInfo(tr,TRACK_MUTE,NULL);
*fVal = (float)bVal; // return

where TRACK_MUTE is "B_MUTE"

Therefore when mute or solo are toggled by clicking them in Reaper, the surface doesn't get the actual value back, although the same code works perfectly for tracks 1 onwards (regular tracks). However the code to SET mute and solo works correctly both for master and regular tracks.

In addition,programmatically attempting to set the master track's send volume and pan for the first send fails because

pdVal = (double *) GetSetTrackSendInfo(tr, 0, 0, TRACK_VOL, NULL);

will always return a NULL pointer, even though there is a send for the master track. This is inconsistent with the behaviour for the main tracks, where if a send exists, pdVal will be returned non-NULL and can be dereferenced to retrieve the send volume.
Note that tr is a valid MediaTrack reference at this point

Just thought I'd mention these in case anyone cares to have a look at them. They are obviously not show-stoppers and I will mention them as known issues in the release notes for the surface plugin, along with the issue of some plugin parameters misreporting the min and max values for the parameter, which causes the surface to be unable to control the full parameter range.

Otherwise Reaper has been rock-solid during testing, even with some fairly complex sample projects.
ajaym is offline   Reply With Quote
Old 12-04-2009, 11:32 AM   #123
ajaym
Human being with feelings
 
Join Date: Aug 2009
Posts: 210
Default BCR2000 control surface

I have managed to code around the master mute problem by using the callback SetSurfaceMute to keep the surface in sync, however, there is a second bug with master solo in this context. If you create two callbacks like this

void SetSurfaceMute(MediaTrack *trackid, bool mute)
{
int id;
id=CSurf_TrackToID(trackid,0);
if (id == 0)
{
// master track, for which we cannot otherwise retrieve status
m_bMasterMute = mute;
}
}

void SetSurfaceSolo(MediaTrack *trackid, bool solo)
{
int id;
id=CSurf_TrackToID(trackid,0);
if (id == 0)
{
// master track, for which we cannot otherwise retrieve status
m_bMasterSolo = solo;
}
}

then you will find that if you create a project with one track plus the master, then toggling master solo doesn't pass back the correct value to the SetSurfaceSolo callback, and furthermore, toggling track 1 solo will cause the callback to erroneously indicate that master solo was toggled (as well as track 1 solo).

I also noted that toggling either mute or solo causes both callbacks to be triggered, which is not what I would have expected, either.

Still, master solo is pretty much a useless function so I have simply disengaged the control surface from this entirely.
ajaym is offline   Reply With Quote
Old 12-06-2009, 10:45 AM   #124
MikeLacey
Human being with feelings
 
Join Date: Dec 2006
Location: UK
Posts: 789
Default

Would it be possible to implement the functionality implied by these made-up API calls below?

It wouldn't be done precisely like this I suspect, as I don't think we have a MIDINote object, so I'm asking for the functionality rather than these APIs.
Code:
int GetNumSelectedMidiNotes(MIDIEditor)

MIDINote* GetSelectedMidiNote(MIDIEditor,Index)

bool SetSelectedMidiNote(MIDIEditor, MIDINote* mn, param char, newvalue int)
"param" would be things like POS, PITCH, DURATION, VOLUME
__________________
Mike Lacey, Leicestershire, UK
MikeLacey is offline   Reply With Quote
Old 12-15-2009, 02:55 AM   #125
MikeLacey
Human being with feelings
 
Join Date: Dec 2006
Location: UK
Posts: 789
Default Translate between .rpp and raw MIDI data

How do I translate between .rpp MIDI data and raw MIDI data?
__________________
Mike Lacey, Leicestershire, UK
MikeLacey is offline   Reply With Quote
Old 12-15-2009, 06:36 AM   #126
Jeffos
Mortal
 
Jeffos's Avatar
 
Join Date: Dec 2008
Location: France
Posts: 1,969
Default

hi Mike, I'm not sure of what you're calling "raw data" but if your talking about MIDI_eventlist this http://forum.cockos.com/showpost.php...2&postcount=76 and that http://forum.cockos.com/showthread.php?t=28562 may help.
note: it seems some of the trickeriest guys are directly updating the chuncks..
Jeffos is offline   Reply With Quote
Old 12-15-2009, 07:01 AM   #127
MikeLacey
Human being with feelings
 
Join Date: Dec 2006
Location: UK
Posts: 789
Default

Ooh look at that, can open and worms just everywhere.

Thanks Jeffos, it's not quite the single function call conversion I had in mind but I will look at it.
__________________
Mike Lacey, Leicestershire, UK
MikeLacey is offline   Reply With Quote
Old 12-15-2009, 03:40 PM   #128
Jeffos
Mortal
 
Jeffos's Avatar
 
Join Date: Dec 2008
Location: France
Posts: 1,969
Default

... about the MIDI_eventlist approach i obviously forget http://forum.cockos.com/showthread.php?t=45430, with a cool example posted by rome. MIDI looks like a brand new place for extensions.. but padre & rome seems in the starting block for that!

Quote:
Originally Posted by MikeLacey View Post
Ooh look at that, can open and worms just everywhere.
What I meant above is that, if I was sure of what I'm doing I won't use the MIDI_eventlist appoach!

I've started digging into midi too.. but directly working with chunks. It's ok for READ and it can answer some your Q's above (see bellow). The few WRITE tests I did: removing cc123, empying, .. seemed also safe but I stopped there: it seemed "too easy to be true" (and schwa didn't talk about that in the thread above, where midi selection is also aksed)
So the few things I can answer you about your Q's in the previous posts are bellow. I only talk about my "C" point of view here, but HTH!

Inside a take's chunk (GetSetObjectState(MediaItem*)), the midi event list looks like:
Code:
e 4781 90 0e 01
e 240 80 0e 00
E 269 90 10 7f
E 240 80 10 00
E 240 b0 7b 00
here 5 events: 2* note on/off on channel 1 and the famous "all notes off" (cc123 on channel 1) that ends midi takes.
- 1st token: dunno yet if it can be different from "e/E". however, lower/upper case distinguish selected events (one of yours Qs)
- 2nd token: *offset* with the previous event or with the begining of the take for the 1st event. it's not really a timing (dunno the english wording..), in short the unit is 1/2560T, e.g. 240 = 1/16. note: working with offset can be nice (copy/paste,..)
- 3 following "bytes" = classical hexa midi coding (one of your Qs, if we're talking about the same "raw data")

beware! in the middle of this gentle midi event list you can face a wicked base64 sysex!
Code:
<X 286306 0
8AECAwQF9w==
>
note: does not work with external midi source file. any tips to complete/correct these info would be appreciated..
Jeffos is offline   Reply With Quote
Old 12-16-2009, 03:51 AM   #129
Jeffos
Mortal
 
Jeffos's Avatar
 
Join Date: Dec 2008
Location: France
Posts: 1,969
Default

[EDIT July 2010] ignore this post, past issues: I was talking of chunk updates not taken into account (fixed in v3.22) and multi-threading issues (fixed in v3.31).
[/EDIT]

____

I have a last question for this "how do I" thread: I know I can update things at anytime (AWESOME!), but "how do I" am sure I can edit any chunk, anywhere ?

this is a bit scaring for the moment: for eg, I guess that if MIDI_eventlist exists, it's the recommended way for editing MIDI ? (or just here for frame offset processing ?) other example: I can remove the famous cc123 I was talking about in my previous post but the native ME forbidds it, there shoud be good reasons.. (edit) hmm.. i think i know them
so, the question is: can we edit any chunk, anywhere ? just a short "yes/no" reply would be much, much appreciated... if it's "no" a very short/rough list of unrecommended chunk updates would be much appreciated too. Right now, I feel a bit "limited" without the answers to these Q's.

[edit] removed, useless

Last edited by Jeffos; 07-03-2010 at 02:35 AM. Reason: fixed issues
Jeffos is offline   Reply With Quote
Old 12-16-2009, 04:08 AM   #130
MikeLacey
Human being with feelings
 
Join Date: Dec 2006
Location: UK
Posts: 789
Default

Thank you Jeff, much appreciated...

I'm starting to document "State Chunks" (a term I made up about an hour ago) in the Wiki, Track/Item/Envelope State Chunks, on the appropriate GetSet*State() page. Do feel free to correct my stupid and obvious mistakes.
__________________
Mike Lacey, Leicestershire, UK
MikeLacey is offline   Reply With Quote
Old 12-16-2009, 06:42 AM   #131
Jeffos
Mortal
 
Jeffos's Avatar
 
Join Date: Dec 2008
Location: France
Posts: 1,969
Default

Mike!! the wiki has grown a lot! awesome work you're doing about the chunks, really (this would have saved me many reverse engineerings a few time ago). There're little errors about midi decoding, I didn't detailed it 'cause I thought you were aware of that (there's not only notes in the chunks but also CC events, pitch events, etc..) => I'll update the wiki with the basics on how to decode the "3 'bytes' of classical hexa midi coding" i was talking about.

note: in case a knowledgeable people read that, please also have a look the Q raised in post 129.. last Q, I promise
Jeffos is offline   Reply With Quote
Old 12-16-2009, 12:41 PM   #132
MikeLacey
Human being with feelings
 
Join Date: Dec 2006
Location: UK
Posts: 789
Default

Thx Jeff,

Would be excellent if you could add to/edit the Item State Chunk MIDI stuff...

Documenting stuff like this is the way I learn it. You can pretty much follow my current interest by what I'm writing about - as long as I get it right it works out well for everyone.

[fingers in ears and singing "Na na na. I'm Not Listening"]

[wicked base64 sysex

[/fingers in ears]

Too scary, I'll think about sysex in a bit...

About the 2nd token though "*offset* with the previous event ... in short the unit is 1/2560T, e.g. 240 = 1/16. note:"

I get that it's an offset from the previous thing but.. Eh? What units is it in? 1/2560T?

Mike

P.S. The knowledgable person thing - I doubt anyone's tried it that intensiveley. My (cunning) plan is to to update objects using State Chunks until they start to smoke from friction through the air - and see what happens.
__________________
Mike Lacey, Leicestershire, UK
MikeLacey is offline   Reply With Quote
Old 12-16-2009, 01:39 PM   #133
Jeffos
Mortal
 
Jeffos's Avatar
 
Join Date: Dec 2008
Location: France
Posts: 1,969
Default

Quote:
Originally Posted by MikeLacey View Post
Thx Jeff,
Would be excellent if you could add to/edit the Item State Chunk MIDI stuff...
er.. yeah, ok, tomorrow!
Quote:
Originally Posted by MikeLacey View Post
Documenting stuff like this is the way I learn it. You can pretty much follow my current interest by what I'm writing about - as long as I get it right it works out well for everyone.
yeah I see! With SWS/Xen's open source + that wiki (+ that forum), I think that people now have some serious entry points to get started
Quote:
Originally Posted by MikeLacey View Post
[fingers in ears and singing "Na na na. I'm Not Listening"]

[wicked base64 sysex

[/fingers in ears]
Too scary, I'll think about sysex in a bit...
i hope i didn't made another english error that can be misinterpreted.. if so, not intended.
sysex is a kind of "custom" MIDI message that carries a variable number of bytes. this is mostly intended for midi hardware, not much for VST (e.g. bulk of settings, text for lcd, anything). you don't need to know much about it, execpt that you must be aware that you indeed have to ignore it! (eg parser). base64 is a codage that allows compacting data, sysex can be very, very big. there's a coder/decoder in SWS open source.
Quote:
Originally Posted by MikeLacey View Post
About the 2nd token though "*offset* with the previous event ... in short the unit is 1/2560T, e.g. 240 = 1/16. note:"

I get that it's an offset from the previous thing but.. Eh? What units is it in? 1/2560T?
ok, this one gonna be hard for me in english but.. i try! this is a temporal information that is not related to time (yeah.. I know) this is related to beats: this is a kind of pourcentage of a beat.

reminder: what I've said might be wrong, incomplete, bla-bla.

P.S. The smoke from friction thing - ha ha! same here.. let me know if something smokes in england.. for the moment all seems ok, on this side.
Jeffos is offline   Reply With Quote
Old 12-16-2009, 01:51 PM   #134
MikeLacey
Human being with feelings
 
Join Date: Dec 2006
Location: UK
Posts: 789
Default

Quote:
Originally Posted by Jeffos
.. hope i didn't made another english error that can be misinterpreted ..
No., not at all - I'm just putting off thinking about sysex messages until I get the other stuff nailed down.

The offset field, I get that it's a position relative to the last event rather than an absolute position in the timeline (and your english is fine, just by the way).

I'm sorry to be thick about this... If 1/240 = 1/16th note -- that means that 16 * 240 (that's 3840) equals one whole note?

Mike
__________________
Mike Lacey, Leicestershire, UK
MikeLacey is offline   Reply With Quote
Old 12-16-2009, 02:00 PM   #135
Jeffos
Mortal
 
Jeffos's Avatar
 
Join Date: Dec 2008
Location: France
Posts: 1,969
Default

1/16 -> 240. the full note is 2.6041666666666666666666666666667e-4 ("unit") * 240 * 16 = 1
Jeffos is offline   Reply With Quote
Old 12-16-2009, 02:03 PM   #136
MikeLacey
Human being with feelings
 
Join Date: Dec 2006
Location: UK
Posts: 789
Default

Bloody hell...

Ok, gotcha
__________________
Mike Lacey, Leicestershire, UK
MikeLacey is offline   Reply With Quote
Old 12-17-2009, 02:32 AM   #137
Jeffos
Mortal
 
Jeffos's Avatar
 
Join Date: Dec 2008
Location: France
Posts: 1,969
Default

Mike, I was in a rush when I did my last post, so first, thanks for my english skills, 1 beat=3840 that's it, I put that formula to be sure we're talking about the same thing (in french: "une règle de trois"). Wiki update done: cosmetic (but needed) updates 'cause someone did the job when I was sleeping! I'm impressed. There're other chunks I know quite well, I'll have a look, but later

About the smoking REAPER: in other words, if all is OK, I wonder why there's not only -one- function in the API: GetSetObjectState (ok, I exaggerate a bit but you can get the idea..).
Jeffos is offline   Reply With Quote
Old 12-17-2009, 03:27 AM   #138
MikeLacey
Human being with feelings
 
Join Date: Dec 2006
Location: UK
Posts: 789
Default

"une règle de trois" I like it, and yes.

Thanks for the Wiki updates, really helpful to my understanding, and kudos to you for picking your way through Wiki nested tables (not the nicest implementation I've ever seen)

Actually there was just the one GetSetObjectState function at one time and it was split into three (and GetSetObjectState hidden from ReaScript) after discussions between Xen and schwa - neither Xen or I could sucessfully call GetSetObjectState from a script so schwa helpfully got out his hammer and ajusted some bits here and there.
__________________
Mike Lacey, Leicestershire, UK
MikeLacey is offline   Reply With Quote
Old 12-17-2009, 06:39 AM   #139
Malevol3nt
Human being with feelings
 
Join Date: Sep 2009
Posts: 107
Default

Edit: nevermind, my bad.
Malevol3nt is offline   Reply With Quote
Old 12-17-2009, 08:48 AM   #140
Jeffos
Mortal
 
Jeffos's Avatar
 
Join Date: Dec 2008
Location: France
Posts: 1,969
Default

You're welcome Mike! I hope i'll be able to participate a bit more...

Quote:
Originally Posted by MikeLacey View Post
Actually there was just the one GetSetObjectState function at one time and it was split into three (and GetSetObjectState hidden from ReaScript) after discussions between Xen and schwa - neither Xen or I could sucessfully call GetSetObjectState from a script so schwa helpfully got out his hammer and ajusted some bits here and there.
no, that's not really why I meant: for scripts, GetSetObjectState was indeed splitted because of the pointer issue.
What I meant is that I think chuncks are the representation of Reaper's buisness model, ie the core stuff. GetSetObjectState -or its "children" for reaScript- can replace many functions of the API, for (random) example, we don't really need things like CountTrackMediaItems or CountTakes: we can do the job through GetSetObjectState, I hope these functions are not just wrappers themselves, that they don't process chunks themselves (if so, that would impact the way I code). Here, the more I swim in the API, the less functions I use..
Jeffos is offline   Reply With Quote
Old 12-17-2009, 02:20 PM   #141
MikeLacey
Human being with feelings
 
Join Date: Dec 2006
Location: UK
Posts: 789
Default

Ah.. I see what you mean.

Yes, I agree with you, I like the chunks stuff, very flexible - Xen on the other hand much prefers the more precise and focussed API functions.
__________________
Mike Lacey, Leicestershire, UK
MikeLacey is offline   Reply With Quote
Old 12-23-2009, 05:27 PM   #142
Malevol3nt
Human being with feelings
 
Join Date: Sep 2009
Posts: 107
Default

Does anyone know how to make the Reaper API calls available to imported modules in python?

The reason I'm asking is I want to put all the API-specific functions in a separate module and then call them via another module (e.g. the main script that reaper runs). Is this possible?
Malevol3nt is offline   Reply With Quote
Old 12-24-2009, 12:11 AM   #143
fingers
Human being with feelings
 
fingers's Avatar
 
Join Date: Dec 2009
Location: Wellington, NZ
Posts: 300
Default

Quote:
Originally Posted by Malevol3nt View Post
Does anyone know how to make the Reaper API calls available to imported modules in python?

The reason I'm asking is I want to put all the API-specific functions in a separate module and then call them via another module (e.g. the main script that reaper runs). Is this possible?
I don't think it is. I tried to wrap some of the api calls in some of python's OO goodness and store the wrapper class in a seperate module. Reaper couldn't see the function calls. I think it must parse the python file and look for RPR_ <function name> and then replace it with code to actually perform the api call.

One thing you could do is store your class which calls the api in a module and when you instantiate the class in your main script, you pass in a callback function to your class which resides in your main script. You then pass a function identifier and your arguments to the callback. That way you can still have your code nicely stored in modules for reuse with the trade off being a big callback function at the start of every script.
fingers is offline   Reply With Quote
Old 12-24-2009, 12:26 AM   #144
fingers
Human being with feelings
 
fingers's Avatar
 
Join Date: Dec 2009
Location: Wellington, NZ
Posts: 300
Default

An example:

MediaItem.py

Code:
class MediaItem:
    ParamMute = "B_MUTE"
    ParamLoopSource = "B_LOOPSRC"
    ParamAllTakesPlay = "B_ALLTAKESPLAY"
    ParamSelected = "B_UISEL"
    ParamVolume = "D_VOL"
    ParamPosition = "D_POSITION"
    ParamSnapOffset = "D_SNAPOFFSET"

    def __init__(self, hMediaItem, callback):
        self.hMediaItem = hMediaItem
        self.callback = callback

    def __repr__(self):
        str_repr  = ""
        str_repr += "Muted: " + str(self.Muted) + "\n"
        str_repr += "Loop Source: " + str(self.LoopSource) + "\n"
        str_repr += "All Takes Play: " + str(self.AllTakesPlay) + "\n"
        str_repr += "Selected: " + str(self.Selected) + "\n"
        str_repr += "Volume: " + str(self.Volume) + "\n"
        str_repr += "Position: " + str(self.Position) + "\n"
        str_repr += "SnapOffset: " + str(self.SnapOffset) + "\n"
        return str_repr

    def __get_api_call(self, param):
        return self.callback("GetMediaItemInfo_Value", (self.hMediaItem, param))

    def __set_api_call(self, param, new_value):
        self.callback("SetMediaItemInfo_Value", (self.hMediaItem, param, new_value))

    def __set_bool(self, param, bValue):
        if bValue:
            self.__set_api_call(param, 1.0)
        else:
            self.__set_api_call(param, 0.0)
    
    @property
    def Muted(self):
        return self.__get_api_call(MediaItem.ParamMute) == 1.0 

    @Muted.setter
    def Muted(self, value):
        self.__set_bool(MediaItem.ParamMute, value)

    @property
    def LoopSource(self):
        return self.__get_api_call(MediaItem.ParamLoopSource) == 1.0

    @LoopSource.setter
    def LoopSource(self, value):
        self.__set_bool(MediaItem.ParamLoopSource, value)

    @property
    def AllTakesPlay(self):
        return self.__get_api_call(MediaItem.ParamAllTakesPlay) == 1.0

    @AllTakesPlay.setter
    def AllTakesPlay(self, value):
        self.__set_bool(MediaItem.ParamAllTakesPlay, value)

    @property
    def Selected(self):
        return self.__get_api_call(MediaItem.ParamSelected) == 1.0

    @Selected.setter
    def Selected(self, value):
        self.__set_bool(MediaItem.ParamSelected, value)
        
    @property        
    def Volume(self):
        return self.__get_api_call(MediaItem.ParamVolume)

    @Volume.setter
    def Volume(self, value):
        return self.__set_api_call(MediaItem.ParamVolume, value)

    @property
    def Position(self):
        return self.__get_api_call(MediaItem.ParamPosition)

    @Position.setter
    def Position(self, value):
        return self.__set_api_call(MediaItem.ParamPosition, value)

    @property
    def SnapOffset(self):
        return self.__get_api_call(MediaItem.ParamSnapOffset)

    @SnapOffset.setter
    def SnapOffset(self, value):
        return self.__set_api_call(MediaItem.ParamSnapOffset, value)
MuteSelectedMediaItem.py
Code:
import sys

# add path to my scripts folder
sys.path.append("c:\\ReaperScripts")
from MediaItem import MediaItem

def ReaperCB(func_name, args):
    if func_name == "GetMediaItemInfo_Value":
        return RPR_GetMediaItemInfo_Value(args[0], args[1])
    if func_name == "SetMediaItemInfo_Value":
        RPR_SetMediaItemInfo_Value(args[0], args[1], args[2])


itemHandle = RPR_GetSelectedMediaItem(0,0)
myItem = MediaItem(itemHandle, ReaperCB)
myItem.Muted = True
fingers is offline   Reply With Quote
Old 12-24-2009, 03:30 AM   #145
MikeLacey
Human being with feelings
 
Join Date: Dec 2006
Location: UK
Posts: 789
Default

Quote:
Originally Posted by Malevol3nt View Post
Does anyone know how to make the Reaper API calls available to imported modules in python?

The reason I'm asking is I want to put all the API-specific functions in a separate module and then call them via another module (e.g. the main script that reaper runs). Is this possible?
Similar issue in Perl, you can't call RPR API functions in a Perl Module
__________________
Mike Lacey, Leicestershire, UK
MikeLacey is offline   Reply With Quote
Old 12-24-2009, 07:28 AM   #146
Malevol3nt
Human being with feelings
 
Join Date: Sep 2009
Posts: 107
Default

Quote:
Originally Posted by fingers View Post
An example:

MediaItem.py

Code:
class MediaItem:
    ParamMute = "B_MUTE"
    ParamLoopSource = "B_LOOPSRC"
    ParamAllTakesPlay = "B_ALLTAKESPLAY"
    ParamSelected = "B_UISEL"
    ParamVolume = "D_VOL"
    ParamPosition = "D_POSITION"
    ParamSnapOffset = "D_SNAPOFFSET"

    def __init__(self, hMediaItem, callback):
        self.hMediaItem = hMediaItem
        self.callback = callback

    def __repr__(self):
        str_repr  = ""
        str_repr += "Muted: " + str(self.Muted) + "\n"
        str_repr += "Loop Source: " + str(self.LoopSource) + "\n"
        str_repr += "All Takes Play: " + str(self.AllTakesPlay) + "\n"
        str_repr += "Selected: " + str(self.Selected) + "\n"
        str_repr += "Volume: " + str(self.Volume) + "\n"
        str_repr += "Position: " + str(self.Position) + "\n"
        str_repr += "SnapOffset: " + str(self.SnapOffset) + "\n"
        return str_repr

    def __get_api_call(self, param):
        return self.callback("GetMediaItemInfo_Value", (self.hMediaItem, param))

    def __set_api_call(self, param, new_value):
        self.callback("SetMediaItemInfo_Value", (self.hMediaItem, param, new_value))

    def __set_bool(self, param, bValue):
        if bValue:
            self.__set_api_call(param, 1.0)
        else:
            self.__set_api_call(param, 0.0)
    
    @property
    def Muted(self):
        return self.__get_api_call(MediaItem.ParamMute) == 1.0 

    @Muted.setter
    def Muted(self, value):
        self.__set_bool(MediaItem.ParamMute, value)

    @property
    def LoopSource(self):
        return self.__get_api_call(MediaItem.ParamLoopSource) == 1.0

    @LoopSource.setter
    def LoopSource(self, value):
        self.__set_bool(MediaItem.ParamLoopSource, value)

    @property
    def AllTakesPlay(self):
        return self.__get_api_call(MediaItem.ParamAllTakesPlay) == 1.0

    @AllTakesPlay.setter
    def AllTakesPlay(self, value):
        self.__set_bool(MediaItem.ParamAllTakesPlay, value)

    @property
    def Selected(self):
        return self.__get_api_call(MediaItem.ParamSelected) == 1.0

    @Selected.setter
    def Selected(self, value):
        self.__set_bool(MediaItem.ParamSelected, value)
        
    @property        
    def Volume(self):
        return self.__get_api_call(MediaItem.ParamVolume)

    @Volume.setter
    def Volume(self, value):
        return self.__set_api_call(MediaItem.ParamVolume, value)

    @property
    def Position(self):
        return self.__get_api_call(MediaItem.ParamPosition)

    @Position.setter
    def Position(self, value):
        return self.__set_api_call(MediaItem.ParamPosition, value)

    @property
    def SnapOffset(self):
        return self.__get_api_call(MediaItem.ParamSnapOffset)

    @SnapOffset.setter
    def SnapOffset(self, value):
        return self.__set_api_call(MediaItem.ParamSnapOffset, value)
MuteSelectedMediaItem.py
Code:
import sys

# add path to my scripts folder
sys.path.append("c:\\ReaperScripts")
from MediaItem import MediaItem

def ReaperCB(func_name, args):
    if func_name == "GetMediaItemInfo_Value":
        return RPR_GetMediaItemInfo_Value(args[0], args[1])
    if func_name == "SetMediaItemInfo_Value":
        RPR_SetMediaItemInfo_Value(args[0], args[1], args[2])


itemHandle = RPR_GetSelectedMediaItem(0,0)
myItem = MediaItem(itemHandle, ReaperCB)
myItem.Muted = True
I see I am not the only one using "sys.path.append". Why is it that Python can't load modules located in directories that have empty spaces in them?

I mean, if I put a couple of my modules in a folder such as "C:\Scripts", importing one module from the other works fine. But if they're located in a folder with spaces, e.g. "C:\Program Files\Reaper\Scripts" then Python simply won't be able to load any other modules in that same directory unless I specifically add "sys.path.append" in my main module.

Thanks for those pieces of code, I'll be studying them shortly. Cheers!


Edit: Btw instead of using a predefined folder location as you do, I use this:

Code:
import os
work_directory = (os.getcwd() + "\\Scripts\\")

import sys
sys.path.append(work_directory)
os.getcwd() returns the Reaper directory (since it's calling python in the first place). I think there's an API call for that as well, but I'm using this one.

Last edited by Malevol3nt; 12-25-2009 at 04:05 PM.
Malevol3nt is offline   Reply With Quote
Old 12-26-2009, 08:32 PM   #147
fingers
Human being with feelings
 
fingers's Avatar
 
Join Date: Dec 2009
Location: Wellington, NZ
Posts: 300
Default

Quote:
Originally Posted by Malevol3nt View Post
I see I am not the only one using "sys.path.append". Why is it that Python can't load modules located in directories that have empty spaces in them?
Can't say I have come across this. I think python will look in the cwd for a module and then anything in your python path.

Quote:
Edit: Btw instead of using a predefined folder location as you do, I use this:

Code:
import os
work_directory = (os.getcwd() + "\\Scripts\\")

import sys
sys.path.append(work_directory)
os.getcwd() returns the Reaper directory (since it's calling python in the first place). I think there's an API call for that as well, but I'm using this one.
The current working directory is generally the directory you saved your reaper project in, or opened it from. I have all my scripts in c:\ReaperScripts, because it is easy to find my scripts this way, and less typing for sys.path.append :-). I should put them somewhere different like you have.

One thing you could do (as C:\Program Files\Reaper is in your path already) is have a __init__.py file in c:\Program files\Reaper\Scripts. That way you can do something like
Code:
from Scripts.somemoduleinscripts import something
where you'd have a file c:\Program files\Reaper\Scripts\somemoduleinscripts.py with a python object called something.
fingers is offline   Reply With Quote
Old 12-27-2009, 07:34 AM   #148
Malevol3nt
Human being with feelings
 
Join Date: Sep 2009
Posts: 107
Default

Quote:
Originally Posted by fingers View Post
The current working directory is generally the directory you saved your reaper project in, or opened it from.
I did not know that. Thanks!

Quote:
Originally Posted by fingers View Post
One thing you could do (as C:\Program Files\Reaper is in your path already) is have a __init__.py file in c:\Program files\Reaper\Scripts. That way you can do something like
Code:
from Scripts.somemoduleinscripts import something
where you'd have a file c:\Program files\Reaper\Scripts\somemoduleinscripts.py with a python object called something.
Ahh, so I have to type in the folder name as well when I'm importing. I've already tried using __init__.py and importing a module, and it didn't work. But I never thought of adding the directory name before the module (e.g. import Scripts.modulename). It works fine now.

Thanks again!
Malevol3nt is offline   Reply With Quote
Old 12-27-2009, 02:42 PM   #149
cturner
Human being with feelings
 
cturner's Avatar
 
Join Date: Apr 2009
Location: GWB
Posts: 76
Default ShowConsoleMsg()?

Hi-

Can I use ShowConsoleMsg() from a VST to log debug information? I know I don't want to do this from the DSP loop, but outside?

Thx, Charles

(Later...)
Hey, works fine! I had to cast the function name string for OSX:
Code:
*(long *)&ShowConsoleMsg = audioMaster(NULL,0xdeadbeef,0xdeadf00d,0,(void *)"ShowConsoleMsg",0.0);

Last edited by cturner; 12-27-2009 at 04:12 PM.
cturner is offline   Reply With Quote
Old 01-11-2010, 10:27 AM   #150
MikeLacey
Human being with feelings
 
Join Date: Dec 2006
Location: UK
Posts: 789
Default

How do I get at the tempo envelope using ReaScript?

My first thought was that it belonged to the MASTER track - but I can't see how to get a reference to the MASTER track.

I'm assuming I could do this by making the envelope visible, selecting it and then calling GetSelectedEnv() - but that's cheating.
__________________
Mike Lacey, Leicestershire, UK
MikeLacey is offline   Reply With Quote
Old 05-10-2010, 09:29 AM   #151
jweite@yahoo.com
Human being with feelings
 
Join Date: May 2010
Posts: 8
Default State in Mem Between Actions

I'm prototyping a set of Actions in Reascript using Python. It'd be useful for me to be able to allows these scripts to share some info in memory with each between invocations. (I could use files if there's no other choice, but prefer not to.)

Are there any places where a script can park some info in Repaer and another can get it? Globally or in the project would work. (Track if I had to.) I'd be happy w/ raw storage or could massage my state into an XML chunk or string.
jweite@yahoo.com is offline   Reply With Quote
Old 05-11-2010, 12:32 AM   #152
Amazed
Human being with feelings
 
Amazed's Avatar
 
Join Date: Nov 2009
Location: Perth, W.A.
Posts: 1,708
Default

Quote:
Originally Posted by jweite@yahoo.com View Post
I'm prototyping a set of Actions in Reascript using Python. It'd be useful for me to be able to allows these scripts to share some info in memory with each between invocations. (I could use files if there's no other choice, but prefer not to.)

Are there any places where a script can park some info in Repaer and another can get it? Globally or in the project would work. (Track if I had to.) I'd be happy w/ raw storage or could massage my state into an XML chunk or string.
sharedmem = mmap.mmap(0, 16384, "GlobalSharedMemory")

also this
http://docs.python.org/library/mmap.html

Hope that helps.
Amazed is offline   Reply With Quote
Old 05-11-2010, 01:14 AM   #153
Xenakios
Human being with feelings
 
Xenakios's Avatar
 
Join Date: Feb 2007
Location: Oulu, Finland
Posts: 8,062
Default

Quote:
Originally Posted by Amazed View Post
sharedmem = mmap.mmap(0, 16384, "GlobalSharedMemory")

also this
http://docs.python.org/library/mmap.html

Hope that helps.
This solution somehow smells wrong...The original poster isn't talking about sharing memory between scripts/processes but keeping state between runs of the same script. (What happens in Reaper currently is that nothing remains of the script's state once it has been run.)

I ended up experimenting simply with disk files to save/restore the state. It isn't such a bad solution in the end, especially if nothing more suitable exists...
__________________
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 05-11-2010, 01:33 AM   #154
Amazed
Human being with feelings
 
Amazed's Avatar
 
Join Date: Nov 2009
Location: Perth, W.A.
Posts: 1,708
Default

Quote:
Originally Posted by Xenakios View Post
This solution somehow smells wrong...The original poster isn't talking about sharing memory between scripts/processes but keeping state between runs of the same script. (What happens in Reaper currently is that nothing remains of the script's state once it has been run.)

I ended up experimenting simply with disk files to save/restore the state. It isn't such a bad solution in the end, especially if nothing more suitable exists...
All entirely true besides which even if you had a handle to shared mem one would still need to persist the handle somehow. In which case a file becomes your only possible way at present. A simple extension to the api could of course make this quite easy by giving you access to, perhaps a named list of values so you could persist values between scripts that way.
Amazed is offline   Reply With Quote
Old 05-12-2010, 03:39 AM   #155
jweite@yahoo.com
Human being with feelings
 
Join Date: May 2010
Posts: 8
Default

Quote:
Originally Posted by Amazed View Post
All entirely true besides which even if you had a handle to shared mem one would still need to persist the handle somehow. In which case a file becomes your only possible way at present. A simple extension to the api could of course make this quite easy by giving you access to, perhaps a named list of values so you could persist values between scripts that way.
I've shied away from using files for this, because I need very fast response to these actions. The prototypes weren't awful, so I may end up going with that approach after all and seeing how it works out.

The shared memory approach is a good idea - I was thinking in the Reaper box instead of in the Python box. (I'm an old C++/Java guy learning Python on the fly). As pointed out, I'll still need to keep the handle somewhere unless I do memory mapped files, which I suspect take as long to open as plain-old-files (though may provide benefits if I'm doing random-access of the contents.)

I'm starting to wonder about "creative" ways to preserve the handle, like encoding it into a GUID and poking it into someplace that wants a GUID. Or are there any ways for me to add user-specific facts to the project config?
jweite@yahoo.com is offline   Reply With Quote
Old 06-23-2010, 02:56 AM   #156
drew
Mobile
 
drew's Avatar
 
Join Date: Jan 2006
Location: London & São Paulo. Hardcore commercial REAPERite
Posts: 1,669
Default

Hi - 3.64pre2 has the long-awaited: REAPERAPI_DECL void (*SetCurrentBPM)(ReaProject* __proj, double bpm, bool wantUndo);

Unfortunately I'm getting "syntax error : found 'void (' at global scope (was a declaration intended?)" on this line in my .cpp:
void (*SetCurrentBPM)(ReaProject *__proj, double bpm, bool wantUndo);

What am I doing wrong? I have a feeling it may be about ReaProject.

Incidentally I've not done any of this for a while and am now using the reaper_plugin_functions.h that REAPER generates for the first time.
Two questions:
1) Would I not need to update reaper_plugin.h too? The SDK one appears to be from last year.
2) There's no declaration of ReaProject in the REAPER-made reaper_plugin_functions.h - So do the following points still stand?

Quote:
Originally Posted by Xenakios View Post
Add to the top of reaper_plugin_functions.h :

class LICE_IBitmap;
class ReaProject;
Quote:
Originally Posted by schwa View Post
Those declares should be at the top of the automatically generated reaper_plugin_functions.h. As Xenakios says, the one in the SDK is dated (we need to update the SDK page to say you should use the automatically generated header).
__________________
Proudly using REAPER exclusively for...
* Media and event music composition & production, sound design + auto-processing at Qsonics.com
* Broadcast branding, promos, education & training and narration voice-overs at DrewWhite.com
drew is offline   Reply With Quote
Old 06-23-2010, 07:30 AM   #157
trugschluss
Human being with feelings
 
Join Date: Jun 2010
Posts: 1
Default Single FXs

Hi there,

is there a way via python to...

1) access a single FX on a track
2) set the bypass state of a single FX
3) set the offline state of a single FX
4) check if a single fx is a vst instrument

Thanks and regards,
trugschluss

Last edited by trugschluss; 06-23-2010 at 07:45 AM.
trugschluss is offline   Reply With Quote
Old 07-01-2010, 11:47 AM   #158
bruce
Human being with feelings
 
Join Date: Jul 2010
Posts: 66
Default discrete parameters

I see you have an extension that lets my plug-in tell you that it has discrete values. How does reaper know how may steps there are? This does not seem to be part of the API.

Also, how are these quantized? Is it like VST3 and RTAS where, for example, 0 to .5 maps to zero, and .5 to 1.0 maps to one?
bruce is offline   Reply With Quote
Old 07-01-2010, 03:29 PM   #159
Jeffos
Mortal
 
Jeffos's Avatar
 
Join Date: Dec 2008
Location: France
Posts: 1,969
Default

hi & welcome!

trugschluss,

here are just some clues (I'm on the C/C++ side, most of the time what I propose can't be done with reaScript but.. better than nothing!?):
- check out RPR_GetSetTrackState() and some threads about "RPP chunk patching" over here
- in a track (or an item, for item's fx) chunk, look closely how the nth "BYPASS" line behaves when you bypass/offline the nth FX
- look the diff for "<JS [...]", "<VST [...], etc..
- the rest is a matter of string processing (possibly HUGE string processing!)

In C++, toggeling bypass or offline states can be eased like this (the SNM_ChunkParserPatcher class can be grabbed here):
Code:
{
  SNM_ChunkParserPatcher p((MediaTrack*)tr);
  bool updated = (p.ParsePatch(_mode, 2, "FXCHAIN", "BYPASS", 3, fxId, _token, (void*)_value) > 0);
} // auto commit
.. where "_token" 1=bypass, 2=offline
___

bruce,

Cockos VST Extensions also allows defining "custom" mix & max values - outside the normalized range. The quantization is deduced from there, I guess, it's linear.
Also, yes, for e.g. an on/off fx param will be "on" when the normalized value is >= 0.5
___

[EDIT] didn't see drew's post!
Hi drew!! I guess you've fixed your issue since that post but.. just in case.. just upgrade your SDK, the last update has been done a few days after the schwa's post you quoted, with the fix.

Last edited by Jeffos; 07-01-2010 at 03:59 PM. Reason: drew!!!
Jeffos is offline   Reply With Quote
Old 07-01-2010, 04:03 PM   #160
bruce
Human being with feelings
 
Join Date: Jul 2010
Posts: 66
Default discrete parameters

so you are saying that if I want a control with five values, I must make its range be 0..4?

That is unfortunate. Many other plug-in APIs support discrete values naively - the ones I know of are RTAS and VST3.

Both of these use 0..1 for the range, but let the controls declare that they have a certain number of discrete values.

I would think that if I DID declare a control range of 0..4 that it would not work so well in other plug-in hosts, since the VST standard is 0..1

It seems that Reaper made an odd decision for this extension - if they had emulated VST3 or RTAS they would have a) been consistent with everyone else, and b) more compatible with other VST2 hosts.

How about a new version of the discrete extension that let's you specify a number of steps? That would address the flaws in today's API, but plug-ins that use today's API would still work.
bruce 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:29 AM.


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