Old 12-23-2017, 04:38 AM   #1
Geoff Waddington
Human being with feelings
 
Geoff Waddington's Avatar
 
Join Date: Mar 2009
Location: Dartmouth, Nova Scotia
Posts: 11,184
Default Rookie question on ProjState, State Chunks, RPP format

Never played in this sandbox, but surely lots of folks must have used this stuff before and there must be some examples/snippets out there.

Basically, I just need to push/pull a small number of project specific data items to/from the .RPP file, is that the ProjState stuff ?

As well I've decided to use the .RPP format for the maps.
I think that means I have to understand StateChunks, yes ?

Anyone know of source examples I should grab for these jobs ?
__________________
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 12-23-2017, 06:35 AM   #2
nofish
Human being with feelings
 
nofish's Avatar
 
Join Date: Oct 2007
Location: home is where the heart is
Posts: 12,096
Default

Get/SetProjExtState() is good for storing basic key/value pairs in the project file.

https://www.extremraym.com/cloud/rea...etProjExtState

Example where I recently used it to make a value persitant across script execution (Lua, but you should get the gist, as it's basic stuff really)

Check for ext. state and get it:
https://github.com/nofishonfriday/Re...opping.lua#L55

Set it:
https://github.com/nofishonfriday/Re...pping.lua#L134

edit:
Correction, above script uses Get/SetExtState(), not Get/SetProjExtState().
The difference is that Get/SetExtState() stores the values in a separate file (reaper-extstate.ini) and so is project independant, while Get/SetProjExtState() directly stores in the project file.


If you need to store more advanced stuff, maybe have a look at how Klinke does it with his plugin (he stores the track states and things in the rpp, using JUCE)

https://bitbucket.org/Klinkenstecker...Tracks.cpp-936

edit:
Addition, Klinke uses a dedicated section in the <EXTENSIONS chunk of the .rpp to store his stuff which looks like this:



If you want to study StateChunks, I find these great resources:

- Eugene's chunk editor script (can be used to see track chunk states / changes almost in realtime)

https://forum.cockos.com/showthread.php?t=194369

- The chunk definitions Wiki

http://wiki.cockos.com/wiki/index.ph...nk_Definitions


Sorry if this is too basic stuff, wasn't sure where to start...

Last edited by nofish; 12-23-2017 at 07:07 AM.
nofish is offline   Reply With Quote
Old 12-23-2017, 08:33 AM   #3
Geoff Waddington
Human being with feelings
 
Geoff Waddington's Avatar
 
Join Date: Mar 2009
Location: Dartmouth, Nova Scotia
Posts: 11,184
Default

Quote:
Originally Posted by nofish View Post
Get/SetProjExtState() is good for storing basic key/value pairs in the project file.

https://www.extremraym.com/cloud/rea...etProjExtState

Example where I recently used it to make a value persitant across script execution (Lua, but you should get the gist, as it's basic stuff really)

Check for ext. state and get it:
https://github.com/nofishonfriday/Re...opping.lua#L55

Set it:
https://github.com/nofishonfriday/Re...pping.lua#L134

edit:
Correction, above script uses Get/SetExtState(), not Get/SetProjExtState().
The difference is that Get/SetExtState() stores the values in a separate file (reaper-extstate.ini) and so is project independant, while Get/SetProjExtState() directly stores in the project file.


If you need to store more advanced stuff, maybe have a look at how Klinke does it with his plugin (he stores the track states and things in the rpp, using JUCE)

https://bitbucket.org/Klinkenstecker...Tracks.cpp-936

edit:
Addition, Klinke uses a dedicated section in the <EXTENSIONS chunk of the .rpp to store his stuff which looks like this:



If you want to study StateChunks, I find these great resources:

- Eugene's chunk editor script (can be used to see track chunk states / changes almost in realtime)

https://forum.cockos.com/showthread.php?t=194369

- The chunk definitions Wiki

http://wiki.cockos.com/wiki/index.ph...nk_Definitions


Sorry if this is too basic stuff, wasn't sure where to start...
Thanks !!

I need almost exactly what Klinke has, and for basically the same reason, locked tracks are on a per project basis, that's perfect for my needs.

The rest I'll have a look at, betcha' what I need is in there, thanks again !!
__________________
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 12-23-2017, 12:30 PM   #4
Geoff Waddington
Human being with feelings
 
Geoff Waddington's Avatar
 
Join Date: Mar 2009
Location: Dartmouth, Nova Scotia
Posts: 11,184
Default

Hmmm...

I'm calling SetExtState("Section" "Key", "Value", true) but nothing is being written to disk in the <EXTENSIONS> section of the .RPP file, what have I left out ?

Must be missing something simple and obvious...
__________________
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 12-23-2017, 01:30 PM   #5
Geoff Waddington
Human being with feelings
 
Geoff Waddington's Avatar
 
Join Date: Mar 2009
Location: Dartmouth, Nova Scotia
Posts: 11,184
Default

Just to be perfectly clear, here is the actual code snippet:

Code:
 
string value = "";
string section = ControlSurfaceIntegrator;
string key = channels_[i]->GetGUID() + GetName();
                
SetExtState(section.c_str(), key.c_str(), to_string(i - 1).c_str(), true);
                
bool foundIt = HasExtState(section.c_str(), key.c_str());
                
if(foundIt)
    value = GetExtState(section.c_str(), key.c_str());
foundIt == true, and the value returned is correct, just no persistence to disk...
__________________
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; 12-23-2017 at 01:35 PM.
Geoff Waddington is offline   Reply With Quote
Old 12-23-2017, 01:31 PM   #6
cfillion
Human being with feelings
 
cfillion's Avatar
 
Join Date: May 2015
Location: Québec, Canada
Posts: 4,937
Default

SetExtState with persist=true writes into reaper-extstate.ini. You probably meant to use SetProjExtState.
cfillion is offline   Reply With Quote
Old 12-23-2017, 02:00 PM   #7
Geoff Waddington
Human being with feelings
 
Geoff Waddington's Avatar
 
Join Date: Mar 2009
Location: Dartmouth, Nova Scotia
Posts: 11,184
Default

Quote:
Originally Posted by cfillion View Post
SetExtState with persist=true writes into reaper-extstate.ini. You probably meant to use SetProjExtState.
Doh !!

Works well now.

However, still have a couple of problems.

Need to save the project in order to flush the state.

So, what I'd like to do is mark the project dirty, so that it prompts for a save, any ideas?

I tried putting stuff in destructors, but it is already too late there.
__________________
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 12-23-2017, 02:33 PM   #8
nofish
Human being with feelings
 
nofish's Avatar
 
Join Date: Oct 2007
Location: home is where the heart is
Posts: 12,096
Default

There is MarkProjectDirty(), but it doesn't prompt for a save automatically, it just makes [modified] appear in Reaper's title bar (afaik).

https://www.extremraym.com/cloud/rea...rkProjectDirty

edit:
There's also Main_SaveProject()

https://www.extremraym.com/cloud/rea...in_SaveProject
nofish is offline   Reply With Quote
Old 12-23-2017, 05:08 PM   #9
Geoff Waddington
Human being with feelings
 
Geoff Waddington's Avatar
 
Join Date: Mar 2009
Location: Dartmouth, Nova Scotia
Posts: 11,184
Default

Quote:
Originally Posted by nofish View Post
There is MarkProjectDirty(), but it doesn't prompt for a save automatically, it just makes [modified] appear in Reaper's title bar (afaik).

https://www.extremraym.com/cloud/rea...rkProjectDirty

edit:
There's also Main_SaveProject()

https://www.extremraym.com/cloud/rea...in_SaveProject
Thanks, MarkProjectDirty() did the trick, I seem to remember it not working before too, but it does now prompt for a save.
__________________
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 12-23-2017, 05:35 PM   #10
nofish
Human being with feelings
 
nofish's Avatar
 
Join Date: Oct 2007
Location: home is where the heart is
Posts: 12,096
Default

Then I misunderstood, doesn't matter, glad it does what you need.
nofish is offline   Reply With Quote
Old 12-24-2017, 06:48 AM   #11
Geoff Waddington
Human being with feelings
 
Geoff Waddington's Avatar
 
Join Date: Mar 2009
Location: Dartmouth, Nova Scotia
Posts: 11,184
Default

Ok, so all of this Chunk stuff eventually leads you to GetSetObjectState().

Very cool, and I think I understand Reaper internals a bit better now.

Storing state that way looks useful for things like undo, etc. as well as writing/reading disk files.

Which brings us to the point.

GetSetObjectState() is a bit too meta for what's needed.

What's needed are the underlying parsing functions.

We won't be messing with .RPP files, with the exception of a few project specific items like locked tracks.

We don't need the high level stuff like GetSetObjectState() because we are writing/reading the map files NOT the .RPP file.

It just happens that the .RPP format is excellent for map file representation too.

So we just need much lower level parse functions, a lot more like SetProjExtState().

Are those even published anywhere ?

line parse.h ? projectContext.h ? projectContext.cpp ?

Is that where I should start ?
__________________
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 12-24-2017, 08:09 AM   #12
nofish
Human being with feelings
 
nofish's Avatar
 
Join Date: Oct 2007
Location: home is where the heart is
Posts: 12,096
Default

In addition to those you mention, for examples of manipulating chunk states implementations, I'd also think of

- ObjectState.h/.cpp
https://github.com/reaper-oss/sws/bl.../ObjectState.h

Which has e.g. GetChunkLine(), AppendChunkLine()

- SnM_ChunkParserPatcher.h/.cpp
https://github.com/reaper-oss/sws/bl...arserPatcher.h
nofish is offline   Reply With Quote
Old 12-24-2017, 09:17 AM   #13
Geoff Waddington
Human being with feelings
 
Geoff Waddington's Avatar
 
Join Date: Mar 2009
Location: Dartmouth, Nova Scotia
Posts: 11,184
Default

Quote:
Originally Posted by nofish View Post
In addition to those you mention, for examples of manipulating chunk states implementations, I'd also think of

- ObjectState.h/.cpp
https://github.com/reaper-oss/sws/bl.../ObjectState.h

Which has e.g. GetChunkLine(), AppendChunkLine()

- SnM_ChunkParserPatcher.h/.cpp
https://github.com/reaper-oss/sws/bl...arserPatcher.h
Yup, looked at those, that's what alerted me to the fact that the GetSetObjectState is very .RPP specific, I'm sure there are some things I can learn from there though, thanks again for the help.
__________________
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 12-25-2017, 10:02 AM   #14
Xenakios
Human being with feelings
 
Xenakios's Avatar
 
Join Date: Feb 2007
Location: Oulu, Finland
Posts: 8,062
Default

Quote:
Originally Posted by Geoff Waddington View Post
What's needed are the underlying parsing functions.
WDL's lineparse.h. You will need to write quite a lot of additional logic around the LineParser object. As its name says, it's just a a way to parse tokens from lines.

Here's my implementation for storing and loading a 1 chunk and 1 line state for my custom PCM_source object :
Code:
void SID_PCM_Source::SaveState(ProjectStateContext * ctx)
{
	ctx->AddLine("FILE \"%s\" %f %d %d %d %d", m_sidfn.toRawUTF8(),m_sidlen,m_sid_track, m_sid_channelmode,m_sid_sr,m_sid_render_mode);
}

int SID_PCM_Source::LoadState(const char * firstline, ProjectStateContext * ctx)
{
	LineParser lp;
	char buf[2048];
	for (;;)
	{
		if (ctx->GetLine(buf, sizeof(buf))) 
			break;
		lp.parse(buf);
		if (strcmp(lp.gettoken_str(0), "FILE") == 0)
		{
			m_sidfn = String(CharPointer_UTF8(lp.gettoken_str(1)));
			m_sidlen = lp.gettoken_float(2);
			m_sid_track = lp.gettoken_int(3);
			m_sid_channelmode = lp.gettoken_int(4);
			m_sid_sr = lp.gettoken_int(5);
			m_sid_render_mode = lp.gettoken_int(6);
		}
		if (lp.gettoken_str(0)[0] == '>')
		{
			renderSID();
			return 0;
		}
	}
	return -1;
}
That uses the Reaper provided ProjectContext stuff though. You will of course need to do things a bit differently when using your own text files and if you are using multiple state lines/chunks and so on.
__________________
I am no longer part of the REAPER community. Please don't contact me with any REAPER-related issues.

Last edited by Xenakios; 12-25-2017 at 11:40 AM.
Xenakios is offline   Reply With Quote
Old 12-25-2017, 11:41 AM   #15
Geoff Waddington
Human being with feelings
 
Geoff Waddington's Avatar
 
Join Date: Mar 2009
Location: Dartmouth, Nova Scotia
Posts: 11,184
Default

Quote:
Originally Posted by Xenakios View Post
WDL's lineparse.h. You will need to write quite a lot of additional logic around the LineParser object. As its name says, it's just a a way to parse tokens from lines.

Here's my implementation for storing and loading a 1 chunk and 1 line state for my custom PCM_source object :
Code:
void SID_PCM_Source::SaveState(ProjectStateContext * ctx)
{
	ctx->AddLine("FILE \"%s\" %f %d %d %d %d", m_sidfn.toRawUTF8(),m_sidlen,m_sid_track, m_sid_channelmode,m_sid_sr,m_sid_render_mode);
}

int SID_PCM_Source::LoadState(const char * firstline, ProjectStateContext * ctx)
{
	LineParser lp;
	char buf[2048];
	for (;;)
	{
		if (ctx->GetLine(buf, sizeof(buf))) 
			break;
		lp.parse(buf);
		if (strcmp(lp.gettoken_str(0), "FILE") == 0)
		{
			m_sidfn = String(CharPointer_UTF8(lp.gettoken_str(1)));
			m_sidlen = lp.gettoken_float(2);
			m_sid_track = lp.gettoken_int(3);
			m_sid_channelmode = lp.gettoken_int(4);
			m_sid_sr = lp.gettoken_int(5);
			m_sid_render_mode = lp.gettoken_int(6);
		}
		if (lp.gettoken_str(0)[0] == '>')
		{
			renderSID();
			return 0;
		}
	}
	return -1;
}
That uses the Reaper provided ProjectContext stuff though. You will of course need to do things a bit differently when using your own text files and if you are using multiple state lines/chunks and so on.
Thanks man, that's exactly the "starter" code example I was searching for !!
__________________
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 12-25-2017, 12:55 PM   #16
Xenakios
Human being with feelings
 
Xenakios's Avatar
 
Join Date: Feb 2007
Location: Oulu, Finland
Posts: 8,062
Default

As a bonus, a C++17 version (not completely tested and debugged) for parsing that uses fold expressions and constexpr if :

Code:
void SID_PCM_Source::SaveState(ProjectStateContext * ctx)
{
	ctx->AddLine("FILE \"%s\" %f %d %d %d %d", m_sidfn.toRawUTF8(),m_sidlen,m_sid_track, m_sid_channelmode,m_sid_sr,m_sid_render_mode);
}

template<typename T>
inline void setFromToken(LineParser& lp, int index, T& value)
{
	if constexpr (std::is_same<int, T>::value)
		value = lp.gettoken_int(index);
	else if constexpr (std::is_same<double, T>::value)
		value = lp.gettoken_float(index);
	else if constexpr (std::is_same<float, T>::value)
		value = static_cast<float>(lp.gettoken_float(index));
	else if constexpr (std::is_same<String, T>::value)
		value = String(CharPointer_UTF8(lp.gettoken_str(index)));
	else
		static_assert(false, "Type not supported by LineParser");
}

template<typename... Args>
inline void setFromTokens(LineParser& lp, Args&&... args)
{
	int index = 0;
	(setFromToken(lp, ++index, args), ...);
}

int SID_PCM_Source::LoadState(const char * firstline, ProjectStateContext * ctx)
{
	LineParser lp;
	char buf[2048];
	for (;;)
	{
		if (ctx->GetLine(buf, sizeof(buf))) 
			break;
		lp.parse(buf);
		if (strcmp(lp.gettoken_str(0), "FILE") == 0)
		{
			setFromTokens(lp, m_sidfn, m_sidlen, m_sid_track, m_sid_channelmode, m_sid_sr, m_sid_render_mode);
		}
		if (lp.gettoken_str(0)[0] == '>')
		{
			renderSID();
			return 0;
		}
	}
	return -1;
}
__________________
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-25-2017, 09:54 PM   #17
Geoff Waddington
Human being with feelings
 
Geoff Waddington's Avatar
 
Join Date: Mar 2009
Location: Dartmouth, Nova Scotia
Posts: 11,184
Default

Quote:
Originally Posted by Xenakios View Post
As a bonus, a C++17 version (not completely tested and debugged) for parsing that uses fold expressions and constexpr if :

Code:
void SID_PCM_Source::SaveState(ProjectStateContext * ctx)
{
	ctx->AddLine("FILE \"%s\" %f %d %d %d %d", m_sidfn.toRawUTF8(),m_sidlen,m_sid_track, m_sid_channelmode,m_sid_sr,m_sid_render_mode);
}

template<typename T>
inline void setFromToken(LineParser& lp, int index, T& value)
{
	if constexpr (std::is_same<int, T>::value)
		value = lp.gettoken_int(index);
	else if constexpr (std::is_same<double, T>::value)
		value = lp.gettoken_float(index);
	else if constexpr (std::is_same<float, T>::value)
		value = static_cast<float>(lp.gettoken_float(index));
	else if constexpr (std::is_same<String, T>::value)
		value = String(CharPointer_UTF8(lp.gettoken_str(index)));
	else
		static_assert(false, "Type not supported by LineParser");
}

template<typename... Args>
inline void setFromTokens(LineParser& lp, Args&&... args)
{
	int index = 0;
	(setFromToken(lp, ++index, args), ...);
}

int SID_PCM_Source::LoadState(const char * firstline, ProjectStateContext * ctx)
{
	LineParser lp;
	char buf[2048];
	for (;;)
	{
		if (ctx->GetLine(buf, sizeof(buf))) 
			break;
		lp.parse(buf);
		if (strcmp(lp.gettoken_str(0), "FILE") == 0)
		{
			setFromTokens(lp, m_sidfn, m_sidlen, m_sid_track, m_sid_channelmode, m_sid_sr, m_sid_render_mode);
		}
		if (lp.gettoken_str(0)[0] == '>')
		{
			renderSID();
			return 0;
		}
	}
	return -1;
}
Nice !!
__________________
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
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 01:59 PM.


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