Old 02-07-2015, 01:41 PM   #41
beyond
Human being with feelings
 
Join Date: Jun 2007
Location: Palm Beach FL
Posts: 265
Default

Quote:
Originally Posted by Aries1985 View Post
Recently I have discovered Beyond. It works really good! However I am struggling to get Sysex data from media item through Beyond with Reaper.RPR_MIDI_GetTextSysexEvt() function.
When I tried your code, I encountered an exception in tkinter:

"Traceback (most recent call last):
File "C:\Program Files\Python\lib\tkinter\__init__.py", line 1487, in __call__
return self.func(*args)
File "M:\Program\Python\Examples Reaper Community\Aries1985\Sysex.py", line 24, in <lambda>
button = Button(self, text = "Test", anchor = W, command = lambda: self.access(index))
NameError: name 'index' is not defined"

I would recommend starting with the core functions, and verifying the progress with Say() at every step, and later build a UI around it.

Code:
import beyond.Reaper
import beyond.Reaper.Item
import beyond.Screen


@ProgramStart
class Main(Parallel):

  def Start(o):

    with Reaper as r:

      for Item in r.ProjectSelected.ItemsSelected:

        T = Item.TakeActive

        Say(T.Name, T.Address)

        Say(r.RPR_MIDI_GetTextSysexEvt(T.Address, 0, 0, 0, 0, 0, 0, 0))
Also check out the much simpler UI functions and examples.
beyond is offline   Reply With Quote
Old 02-08-2015, 12:00 AM   #42
Aries1985
Human being with feelings
 
Join Date: Jul 2011
Posts: 59
Default

Quote:
Originally Posted by beyond View Post
I have been quite busy lately, but I would like to let everyone know that this project has been very active and quite a work horse in our productions. I will be posting an update soon, along with responding to your messages.
That is great news!


Apologies for not-properly-debugged example and thank you for having a look.

The behavior should be:
  • look if supported SysEx message is under cursor, if so, notify in GUI
  • when button in GUI is clicked message is: 1) removed if is on cursor position 2) added if it was not under cursor

I have EEL script that works for Sysex message detection in item REAPER (Python script is based on that). It is simple concept as well. When there is appropriate Sysex message under cursor, it writes message name in white. Following example shows that for one message:

Working EEL code:
Code:
sprintf(#sys_dr_on, "%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c", 0x42, 0x30, 0x00, 0x01, 0x05, 0x41, 0x00, 0x00, 0x0B, 0x00, 0x0A, 0x00, 0x00, 0x00, 0x00, 0x01);
sprintf(#sys_dr_off, "%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c", 0x42, 0x30, 0x00, 0x01, 0x05, 0x41, 0x00, 0x00, 0x0B, 0x00, 0x0A, 0x00, 0x00, 0x00, 0x00, 0x00);

dr_on_present = 0;
dr_off_present = 0;

PLUGIN_TITLE="Counter";

function dim() (
	gfx_r = 0.7;
	gfx_g = 0.7;
	gfx_b = 0.7;
);

function lit() (
	gfx_r = 1;
	gfx_g = 1;
	gfx_b = 1;
);

function checkWhatIsPresent() (
	// reinitialize variables, cursor position might have changed
	dr_on_present = 0;
	dr_off_present = 0;

	selectedTrack = GetSelectedTrack(0, 0);

	cursor_pos = GetCursorPosition();
	i = 0;
	iterate_media = 1;
	iterate_events = 1;

	loop(CountTrackMediaItems(selectedTrack), iterate_media ? (
		item_id = GetTrackMediaItem(selectedTrack, i);
		pos = GetMediaItemInfo_Value(item_id, "D_POSITION");
		length = GetMediaItemInfo_Value(item_id, "D_LENGTH");
		end_pos = pos + length;
  
		current_pos_ppq = MIDI_GetPPQPosFromProjTime(take, cursor_pos);
		// printf("MediaItem %f %f %f\n", pos, end_pos, cursor_pos);
		(cursor_pos >= pos + 0.001) && (cursor_pos <= end_pos + 0.001) ? (
		// cursor is on item
	
		take = GetActiveTake(item_id);
	
		current_pos_ppq = MIDI_GetPPQPosFromProjTime(take, cursor_pos);
	
		evt_index = 0;
		loop(MIDI_CountEvts(take, NULL, NULL, num_sysex), iterate_events ?
			(
				MIDI_GetTextSysexEvt(take, evt_index, NULL, NULL, current_evt_pos_ppq, current_evt_type, current_ent_msg);
				(current_evt_type == -1) ? (
					// yes, this is a sysex message
					(current_pos_ppq == current_evt_pos_ppq) ? (
						// yes, it is on current cursor position
						!strcmp(current_msg, #sys_dr_on) ? dr_on_present = 1;
						!strcmp(current_msg, #sys_dr_off) ? dr_off_present = 1;
					);  				
				);
				evt_index += 1;
			)
		);
		iterate_media = 0;
	  );
	  i += 1;  
	  )
	);
);

function btn(title, variable,  x_pos, y_pos) (
	gfx_x = x_pos;
	gfx_y = y_pos;

	variable ? lit() : dim();

	gfx_drawstr(title);
);

function run() (
	checkWhatIsPresent();

	btn("Dr On", dr_on_present, 10, 10);
	btn("Dr Off", dr_off_present, 10, 26);

 // check if "lmb down" and "mouse cursor is not in window" and "not currently moving slider"
  mouse_cap >= 1 && (mouse_x <= 0 || mouse_x >= gfx_w || mouse_y < 2 || mouse_y >= gfx_h) && drag_started == 0 ? (
    lmb_click_outside_window = 1;
  ) : mouse_cap == 0 ? (
    lmb_click_outside_window = 0;
  );
  
   mouse_cap == 0 ? (
    max_point_drag_started = 0; min_point_drag_started = 0; lmb_down = 0;
  );
  
  char = gfx_getchar();
  // ctrl+lmb to Undo
    
  // Esc to exit
  char == 27 ? (
    undo_block == 1 ? (
      Undo_OnStateChange("Adjust track colors");
    );
    gfx_quit();
  );
  char >= 0 ? (
    defer("run();");
  );

  
  gfx_update();
);

gfx_init("",420,200);
gfx_setfont(1, "Arial", 14);
lmb_click_outside_window = 1;
run();
Here is updated Python script with more reasonable debug messages written to console:
Code:
import array
import beyond.Reaper
import tkinter

from tkinter import *


# TODO - add loading support
sysex = [
		("Drum Track On", bytes.fromhex("42 30 00 01 05 41 00 00 0B 00 0A 00 00 00 00 01")),
		("Drum Track Off", bytes.fromhex("42 30 00 01 05 41 00 00 0B 00 0A 00 00 00 00 00"))
	]

button_states = []
		
class my_app(Frame):
	"""Basic Frame"""
	last_pos = 0
	buttons = []
	
	def __init__(self, master):
		"""Init the Frame"""
		Frame.__init__(self,master)
		self.grid()
		for index in range(0, len(sysex)):
			button_states.append(tkinter.IntVar())
			button_state_variable = button_states[index]
			button = Checkbutton(self, text = sysex[index][0], anchor = W, onvalue=1, offvalue=0, variable=button_state_variable , command =  lambda index=index: self.access(index))
			button.config(height = 3, width = 30)
			button.grid(column = 0, row = index, sticky = NW)
			self.buttons.append(button)
			self.last_pos = Reaper.RPR_GetCursorPosition()
		self.update_controls()
			
	def update_controls(self):
		selectedTrack = Reaper.RPR_GetSelectedTrack(0, 0);
		
		# TODO add caching
		cursor_pos = Reaper.RPR_GetCursorPosition()
		
		for i in range(0, Reaper.RPR_CountTrackMediaItems(selectedTrack)):
			item_id = Reaper.RPR_GetTrackMediaItem(selectedTrack, i)
			take = Reaper.RPR_GetActiveTake(item_id);
			pos = Reaper.RPR_GetMediaItemInfo_Value(item_id, "D_POSITION");
			length = Reaper.RPR_GetMediaItemInfo_Value(item_id, "D_LENGTH");
			end_pos = pos + length
			current_pos_ppq = Reaper.RPR_MIDI_GetPPQPosFromProjTime(take, cursor_pos);
			if (cursor_pos >= pos + 0.001) and (cursor_pos <= end_pos + 0.001):
				
				# current_pos_ppq = Reaper.RPR_MIDI_GetPPQPosFromProjTime(take, cursor_pos);
				# iterate over Sysex events, get status
				num_sysex = Reaper.RPR_MIDI_CountEvts(take, 0, 0, 0)[0]
				print("Num sysex: " + str(num_sysex))
				for j in range(0, num_sysex):
					# ta_ prefix for throw away
					(ta_retval, ta_take, ta_textsyxevtidx, ta_selectedOutOptional, ta_mutedOutOptional, current_evt_pos_ppq, current_evt_type, current_msg, msgOptional_sz) = Reaper.RPR_MIDI_GetTextSysexEvt(take, j, 0, 0, 0, 0, 0, 0)
					current_msg = Reaper.RPR_MIDI_GetTextSysexEvt(take, j, 0, 0, 0, 0, 0, 0)[7]
					if current_evt_type == -1:
						print("Sysex event number " + str(j))
						# yes, this is a sysex message
						if current_pos_ppq == current_evt_pos_ppq:
							print("Sysex event is on cursor position")
							print("Sysex event data: %s" % ''.join('{:02X}'.format(int(a)) for a in current_msg))
							# yes, it is on current cursor position
							# iterate over defined sysexes
							print("Configuration iteration")
							for conf_id in range(0, len(sysex)):
								print("Current configuration data: %s" % ''.join('{:02X}'.format(int(a)) for a in sysex[conf_id][1]))
								# print("Current configuration data: %s" % sysex[conf_id][1])
								if current_msg == sysex[conf_id][1]:
									print("Found sysex event for button switching")
									self.buttons[conf_id].config(relief = "sunken")
									break
	
	def access(self, b_id):
		self.b_id = b_id
		clicked_button = self.buttons[b_id]
		# get state, it is state after clicking
		checked = button_states[b_id].get()
		if checked:
			# add event on cursor position
			print("Would add sysex event on cursor position")
		else:
			# remove event under cursor
			print("Would remove sysex event under cursor")
			
		
@ProgramStartDirect
def Main():
	root = Tk()
	root.title("SysEx helper")
	root.geometry("500x500")
	app = my_app(root)
	root.mainloop()
Sample debug output looks following:
Code:
Num sysex: 2
Sysex event number 0
Sysex event number 1
Sysex event is on cursor position
Sysex event data: 00
Configuration iteration
Current configuration data: 42300001054100000B000A0000000001
Current configuration data: 42300001054100000B000A0000000000
Problem is seen in output Sysex event data: 00.

It should contain SysEx data returned from Reaper.
Calling len() on variable returns length of 1.
Aries1985 is offline   Reply With Quote
Old 02-08-2015, 06:24 AM   #43
beyond
Human being with feelings
 
Join Date: Jun 2007
Location: Palm Beach FL
Posts: 265
Default

Yes, I see the problem. I could get your code to pick up the sysx message under the cursor, as well as my code, but Say(r.RPR_MIDI_GetTextSysexEvt(T.Address, 0, 0, 0, 0, 0, 0, 0)) returns:

(1, '(MediaItem_Take*)0x0000000005920FF0', 0, 1, 0, 15480.0, -1, '0', 0) (tuple)

Looks like RPR_MIDI_GetTextSysexEvt is returning things correctly, except the sysx message and its size are both 0. I doubt this is a bug in beyond.Reaper and its serialization, as the message size is a simple integer with a value of 0 confirming the empty message, and other integers have data.

I suspect there is a bug in Reaper's Python implementation of this function. Try creating a bare minimum Python program (without beyond.Reaper or any tkinter) to run directly inside Reaper to confirm the bug, and then report it to the developers. If you can get RPR_MIDI_GetTextSysexEvt() to work directly in Reaper with bare minimum Python code, then I can help you.

As another tip, try using "with Reaper as r:" and change all your "Reaper." calls to "r." This will greatly speed up your program, as all calls will be made in one network session.
beyond is offline   Reply With Quote
Old 02-08-2015, 10:17 PM   #44
Aries1985
Human being with feelings
 
Join Date: Jul 2011
Posts: 59
Default

Quote:
Originally Posted by beyond View Post
... Say(r.RPR_MIDI_GetTextSysexEvt(T.Address, 0, 0, 0, 0, 0, 0, 0)) returns:

(1, '(MediaItem_Take*)0x0000000005920FF0', 0, 1, 0, 15480.0, -1, '0', 0) (tuple)

Looks like RPR_MIDI_GetTextSysexEvt is returning things correctly, except the sysx message and its size are both 0. ...

...Try creating a bare minimum Python program (without beyond.Reaper or any tkinter) to run directly inside Reaper to confirm the bug, and then report it to the developers. ...
Thank you for help, beyond. Logging whole return value is great idea.
I was able to reproduce that behavior in minimum Python program and it behaves same. So it looks like REAPER bug.
I have posted it here http://forums.cockos.com/showthread....23#post1475923


Quote:
Originally Posted by beyond View Post
As another tip, try using "with Reaper as r:" and change all your "Reaper." calls to "r." This will greatly speed up your program, as all calls will be made in one network session.
Wow, thanks! I did not realize it was connecting every time.

Many thanks for your help and library!
Aries1985 is offline   Reply With Quote
Old 04-27-2015, 12:17 AM   #45
BobBobson108
Human being with feelings
 
Join Date: Apr 2015
Posts: 3
Default GetPlayPosition() not upating

Hi! Firstly, thank you for making this! I'm trying to get the GetPlayPosition(), but it doesn't seem to update while RemoteControl is running. Maybe this is something on my end. I'm running a threaded socket to connect to use beyond (only one Reaper r instance ever exists though!).

My goal is to get the audio sample data from reaper, and I'm succeeding in that, but like I said GetPlayPosition() does not update passed the moment when RemoteControl is run. Is there an alternative to GetPlayPosition() in beyond or can you give me some advice on how to force an update?

Thanks!
~Jake
BobBobson108 is offline   Reply With Quote
Old 04-30-2015, 04:11 PM   #46
beyond
Human being with feelings
 
Join Date: Jun 2007
Location: Palm Beach FL
Posts: 265
Default

It looks like the Reaper API may not be updating GetPlayPosition() during the same script execution of RemoteControl.py. Try exiting and reentering the "with Reaper as r:" block as that will create another session of RemoteControl.py and may allow Reaper to update the API in between.
beyond is offline   Reply With Quote
Old 05-05-2015, 10:11 PM   #47
BobBobson108
Human being with feelings
 
Join Date: Apr 2015
Posts: 3
Default

EDIT: Just switched to a new computer and didn't check off 'Receive on Port' in the OSC Settings. Follow the instructions folks


###
Thanks beyond! That is definitely working but I'm running into one more issue.

Is the "with Reaper as r" block supposed to run RemoteControl.py automatically? For some reason, I'm having to run RemoteControl.py through the Reaper interface via the Run button in the Actions window every time I want to use beyond.

I'll run an external python script (or even the Test Installation script) and I will get no response until I manually run RemoteControl.py from Reaper. After that, I have to repeat the process for anything else, because RemoteControl.py seems to be closed after one communication is made.

If I try to reverse the order and run RemoteControl.py first, it will throw a

[WinError 10061] No connection could be made because the target machine actively refused it

I'm wondering if something weird happened. Has this ever happened to you or do you have any ideas?

Last edited by BobBobson108; 05-06-2015 at 06:30 AM.
BobBobson108 is offline   Reply With Quote
Old 05-15-2015, 08:04 PM   #48
oneindelijk
Human being with feelings
 
Join Date: Oct 2013
Posts: 21
Default Accessing the mixer

Hi,

I'm a big Reaper fan and I love python.
Beyond makes this marriage even better !!

I was wondering hwo I can programmatically access the mixer controls ?
What I want to do, is set the heights of the mixer based on their nesting in groups...

Thanks !!
oneindelijk is offline   Reply With Quote
Old 09-03-2015, 03:25 PM   #49
semiquaver
Human being with feelings
 
Join Date: Jun 2008
Posts: 4,923
Default

unable to get going in most recent REAPER 5.01 on mac Python 3.4

... the command id for Rempote Control,py is a string beginning _50a8e452301445b2ad360c9054e753fe - is beyond.reaper expecting a number?
semiquaver is offline   Reply With Quote
Old 09-03-2015, 10:58 PM   #50
beyond
Human being with feelings
 
Join Date: Jun 2007
Location: Palm Beach FL
Posts: 265
Default

Looks like Reaper is now hiding the older Numeric Cmd IDs. Current installations still work, and you maybe able to try and find the number for RemoteControl.py around 53010 for a new installation. I have to update the OSC trigger to use the new String Command ID, and this should also be more permanent. There are other updates to beyond Reaper V27 as well, most importantly a faster and more complete Element parser for Reaper chunks and encoded binaries.
beyond is offline   Reply With Quote
Old 09-04-2015, 09:31 AM   #51
semiquaver
Human being with feelings
 
Join Date: Jun 2008
Posts: 4,923
Default

thanks - I'll find the proper ID - looking forward to updates - cheers
semiquaver is offline   Reply With Quote
Old 09-04-2015, 09:56 AM   #52
semiquaver
Human being with feelings
 
Join Date: Jun 2008
Posts: 4,923
Default

...ahhh but now getting "error connection refused" as well - tia
semiquaver is offline   Reply With Quote
Old 01-01-2016, 10:34 PM   #53
t0b4cc0
Human being with feelings
 
Join Date: Dec 2015
Posts: 13
Default

Quote:
Originally Posted by beyond View Post
Looks like Reaper is now hiding the older Numeric Cmd IDs. Current installations still work, and you maybe able to try and find the number for RemoteControl.py around 53010 for a new installation. I have to update the OSC trigger to use the new String Command ID, and this should also be more permanent. There are other updates to beyond Reaper V27 as well, most importantly a faster and more complete Element parser for Reaper chunks and encoded binaries.
cant await this. reaper crashing constantly the bigger my scripts get
t0b4cc0 is offline   Reply With Quote
Old 02-26-2016, 03:58 PM   #54
beyond
Human being with feelings
 
Join Date: Jun 2007
Location: Palm Beach FL
Posts: 265
Default

V27 Update:
  • Support for the new and stable RemoteControl.py CommandID trigger (OSC s/action/str)
  • Faster and more complete de/serialization of Reaper Elements/Chunks
  • Updated Reaper 5.2 API calls

Test away guys, and contact me soon, while I am active on this project. :-)
beyond is offline   Reply With Quote
Old 03-28-2016, 04:13 AM   #55
mschnell
Human being with feelings
 
mschnell's Avatar
 
Join Date: Jun 2013
Location: Krefeld, Germany
Posts: 14,688
Default

Hi Beyond.

I am absolutely delighted about the BeyondPython project, as I am sure that in my work, there will be future projects that will include using a tightly remote-controlled Reaper installation. As those projects will have a rather complex additional business logic, (maybe) SQL Database work, and (maybe) sophisticated GUI, they will need to be done in a decent programming language, which might be Python (Or Pascal, or C++, or ....).

So I d/lded the V27 zip to read the documentation and unfortunately there is close to none.

As I did not use Python at all, yet, I can't easily understand or check out the many examples that are included.

That is why I would like to ask some simple questions to be able to decide if Beyond might be a way to be decently considered for those projects.

The questions include:

- I do understand that the external Python program can call any Reaper actions (including those that are provided as scripts within Reaper). Is it possible to transfer all parameters with that call, that are used by the appropriate action. (E.g. when starting a render action Reaper needs to know the destination file type and path, etc).

- Is it possible to dynamically retrieve states of the running Reaper installation such as "Recording" or "Busy doing a rendering action", "Name of the recorded WAV file of track 7" and similar.

- I understand that with Beyond, there is an internal Python program running as a script (provided by Beyond) in Reaper and an external Python program that can be completely done by the "user". Is it necessary / viable / common to enhance the internal Python script according to the project the user has in mind, or should same stay unmodified, being just a unified transfer module ?

Thanks for enlightenment,

-Michael

Last edited by mschnell; 03-28-2016 at 04:55 AM.
mschnell is online now   Reply With Quote
Old 03-29-2016, 09:53 PM   #56
mschnell
Human being with feelings
 
mschnell's Avatar
 
Join Date: Jun 2013
Location: Krefeld, Germany
Posts: 14,688
Default

Can anybody more knowledgeable than me comment on my questions ?

Thanks,
-Michael
mschnell is online now   Reply With Quote
Old 03-31-2016, 10:07 PM   #57
beyond
Human being with feelings
 
Join Date: Jun 2007
Location: Palm Beach FL
Posts: 265
Default

Quote:
Originally Posted by mschnell View Post
Hi Beyond.

The questions include:

- I do understand that the external Python program can call any Reaper actions (including those that are provided as scripts within Reaper). Is it possible to transfer all parameters with that call, that are used by the appropriate action. (E.g. when starting a render action Reaper needs to know the destination file type and path, etc).

- Is it possible to dynamically retrieve states of the running Reaper installation such as "Recording" or "Busy doing a rendering action", "Name of the recorded WAV file of track 7" and similar.

- I understand that with Beyond, there is an internal Python program running as a script (provided by Beyond) in Reaper and an external Python program that can be completely done by the "user". Is it necessary / viable / common to enhance the internal Python script according to the project the user has in mind, or should same stay unmodified, being just a unified transfer module ?

Thanks for enlightenment,

-Michael
Yes, any function that is in the Reaper or an extension API can be called without any limits on the parameters, returns or exception handling. In fact, beyond.Reaper lifts many limits on Reaper's internal Python handling, such as the 4MB string limit (especially for Track FX state returns with hefty plugins) that can simply be extended with Reaper.StringLimit = 16 * 1024 ** 2 # for 16MB for example.

You can also use Reaper.Execute(""" Say(l[1], l[2]) """, 11, 22) # To send and execute entire mini scripts (with more complex parameters than 11 and 22, anything Python pickle-able), such as tight, fast loops inside Reaper. So, RemoteControl.py can handle anything.

You have to research the Reaper API and test to see if Reaper supports responding or reporting status when it is busy such as during a render. You may have to check the status of an output file, to see if it is openable, to determine that Reaper is done with it and responsive again. The TCP connection may timeout, if an API call takes too long to return, so you may have to send a mini script to return asynchronously, and wait for completion to reconnect again. beyond.Python makes it quite easy to to do Parallel operations, and there are an abundance of other Python libraries for almost any (non realtime) task.

The best way to learn programming is by getting at least a basic good editor such as Sublime Text, and reading the source code for examples, and running lots of small tests for which Python is ideal. I recommend an interactive approach to development, using Say() to output and explore your progress at every step of the program you are writing. Such approach is preferable to classical breakpoint debugging, since you can put Say() into loops outputting any significant variables and study a list of trends, rather than wasting time and your focus tracing things at every irrelevant step. Once you discover how things should work, then you can remove or comment out such feedback and proceed to the next challenge.

Python is a good language for automating and conducting research in pre-existing, high end tools from 3D renderers to AI systems. C++ is the best programming language without any limits, but it takes longer to pick up momentum learning it and building up your own high level methods before you build usable applications.
beyond is offline   Reply With Quote
Old 04-01-2016, 05:33 AM   #58
mschnell
Human being with feelings
 
mschnell's Avatar
 
Join Date: Jun 2013
Location: Krefeld, Germany
Posts: 14,688
Default

Sounds great !

Thanks a lot,

-Michael
mschnell is online now   Reply With Quote
Old 04-30-2016, 12:56 AM   #59
mschnell
Human being with feelings
 
mschnell's Avatar
 
Join Date: Jun 2013
Location: Krefeld, Germany
Posts: 14,688
Default

Quote:
Originally Posted by beyond View Post
You have to research the Reaper API and test to see if Reaper supports responding or reporting status when it is busy such as during a render.
We found that the call that orders the rendering in fact hangs until the rendering is done.

(At least for now) this is OK.

But we still don't seem to find out how any state can be reported. We would like to display the "STOP / PLAYing / RECORDing" state and the current time counter.

-Michael

Last edited by mschnell; 04-30-2016 at 01:04 PM.
mschnell is online now   Reply With Quote
Old 12-13-2017, 01:09 PM   #60
TonE
Human being with feelings
 
Join Date: Feb 2009
Location: Reaper HAS send control via midi !!!
Posts: 4,031
Default

Which are coolest examples created with beyond.Reaper?
TonE is offline   Reply With Quote
Old 03-24-2018, 12:36 PM   #61
InLight-Tone
Human being with feelings
 
InLight-Tone's Avatar
 
Join Date: Jul 2017
Posts: 26
Default

Is this project still alive? I am VERY much interested in the chord track mentioned. My goal is to replace Rapid Composer functionality natively into Reaper. This project sounds like a possible solution plus there is a ton of Python code available for music composition...
__________________
InLight-Tone on Youtube
InLight-Tone is offline   Reply With Quote
Old 03-24-2018, 01:07 PM   #62
mschnell
Human being with feelings
 
mschnell's Avatar
 
Join Date: Jun 2013
Location: Krefeld, Germany
Posts: 14,688
Default

What do you mean by "Alive" ?

A friend of mine actively uses Beyond to remote-control Reaper from his Python program.

But I don't know how Beyond can access the data in media files. This might be possible, but this is not what he does.

-Michael
mschnell is online now   Reply With Quote
Old 04-09-2018, 07:40 AM   #63
JerContact
Human being with feelings
 
Join Date: Feb 2017
Posts: 54
Default Remote Control CommandID Changing

Hey Beyond!! I have to say, beyond reaper is amazing! It opens up so many possibilities to sound, especially in games that use middleware. I'm a huge fan, and thank you for everything you have done!

I came across an issue where the remote control commandID changed and it took me 30 mins or so to figure it out, haha. Once I changed it, it started working fine again. But, this seems like an issue that can easily be fixed.

I was wondering if you have thought about putting beyond.reaper as an extension so the commandID will never change? Or, if you want to be quick about it, adding it to ReaPack?

If you are busy, I can always add that .py script to ReaPack for you! Just let me know!

If that commandID stays the same, your stuff will NEVER break! And, in the installation process, it's one more thing you don't have to set or worry about. I hate that reaper doesn't have a more solid way to lock in their commandID's through script...
JerContact is offline   Reply With Quote
Old 04-09-2018, 11:53 AM   #64
fladd
Human being with feelings
 
fladd's Avatar
 
Join Date: May 2006
Posts: 1,030
Default

https://pypi.python.org/pypi/autopep8
__________________
www.fladd.de/sound
fladd is offline   Reply With Quote
Old 09-14-2018, 02:44 AM   #65
Levitanus
Human being with feelings
 
Join Date: Nov 2015
Location: Novosibirsk, Russia
Posts: 40
Default UndoBlock

Can someone halp, if my problem is related to my connection with beyond, or with command themselves?

I'm making simple region namer for samle editing.

here is whole listing:
Code:
import os
import sys
from typing import Tuple

from reaper_python import *

from tkinter import *
from tkinter.ttk import *
from tkinter import filedialog

import beyond.Reaper
# import beyond.Screen

# from items import Item
# from misc import pr
# from serialize import Persist

try:
    os.path.basename(sys.argv[0])
except AttributeError:
    sys.argv = ('__main__')
except KeyError:
    pass

# CONSTANTS
rr_amount = 4
dyn_names = ['pp', 'p', 'f', 'ff']


def get_project_dir():
    path = str()
    size = 100
    with Reaper as r:
        r.GetProjectPath(path, size)
    return path


# def get_project_dir():
#     return 'H:/contrabasses'


def load_func(ext_tuple: Tuple[str]):

    name = filedialog.askopenfilename(
        initialdir=get_project_dir(),
        filetypes=(ext_tuple,
                   ("All Files", "*.*")),
        title="Choose a file."
    )

    return name


def save_func(ext_tuple: Tuple[str]):
    name = filedialog.asksaveasfilename(
        initialdir=get_project_dir(),
        filetypes=(ext_tuple,
                   ("All Files", "*.*")),
        title="Choose a file.",
        defaultextension=ext_tuple[1]
    )

    return name


class Mask:
    selection = None
    instances = list()

    def __init__(self, parent: Tk, idx: int, name: str='name',
                 ext_tuple: str = ("Mask Files", "*.reg_mask")):
        cls = self.__class__
        if cls.selection is None:
            cls.selection = IntVar()
            cls.selection.set(0)
        cls.instances.append(self)
        self.idx = idx
        self.ext_tuple = ext_tuple
        self.frame = Frame(parent)
        self.frame.grid_configure(column=idx, row=0)
        self.textvar = StringVar(value=name)
        self.text = Entry(self.frame, textvariable=self.textvar)
        self.text.grid_configure(column=0, row=1)

        self.button = Button(self.frame, text='create',
                             command=self.create)
        self.button.grid_configure(column=0, row=2, sticky='nwes')
        self.select = Radiobutton(self.frame, variable=self.selection,
                                  value=idx)
        self.select.grid_configure(row=0, sticky='nswe')

    @property
    def name(self):
        return self.textvar.get()

    @name.setter
    def name(self, val):
        self.textvar.set(val)

    def create(self):
        create(self.textvar.get())

    @staticmethod
    def save(masks: list):
        names = list()
        for obj in masks:
            names.append(obj.name + '\n')
        global save_func
        file_n = save_func(("Mask Files", "*.reg_mask"))
        with open(file_n, 'w') as f:
            f.writelines(names)

    @staticmethod
    def load(masks: list):
        names = list()
        for obj in masks:
            names.append(obj.name + '\n')
        global save_func
        file_n = load_func(("Mask Files", "*.reg_mask"))
        with open(file_n, 'r') as f:
            names = f.readlines()
        for inst, name in zip(Mask.instances, names):
            inst.name = name


class RR:
    selection = None
    instances = list()

    def __init__(self, parent: Tk, idx: int, name: str='name',
                 ext_tuple: str = ("Mask Files", "*.reg_mask")):
        cls = self.__class__
        if cls.selection is None:
            cls.selection = IntVar()
            cls.selection.set(0)
        cls.instances.append(self)
        self.idx = idx
        self.frame = Frame(parent)
        self.frame.grid_configure(column=idx, row=0)
        self.textvar = StringVar(value=name)
        self.select = Radiobutton(self.frame, variable=self.selection,
                                  value=idx, textvariable=self.textvar)
        self.select.grid_configure(row=0, sticky='nswe')

    @property
    def name(self):
        return self.textvar.get()

    @name.setter
    def name(self, val):
        self.textvar.set(val)


class Dyn:
    selection = None
    instances = list()

    def __init__(self, parent: Tk, idx: int, name: str='name',
                 ext_tuple: str = ("Mask Files", "*.reg_mask")):
        cls = self.__class__
        if cls.selection is None:
            cls.selection = IntVar()
            cls.selection.set(0)
        cls.instances.append(self)
        self.idx = idx
        self.frame = Frame(parent)
        self.frame.grid_configure(column=idx, row=0)
        self.textvar = StringVar(value=name)
        self.select = Radiobutton(self.frame, variable=self.selection,
                                  value=idx, textvariable=self.textvar)
        self.select.grid_configure(row=0, sticky='nswe')

    @property
    def name(self):
        return self.textvar.get()

    @name.setter
    def name(self, val):
        self.textvar.set(val)


def create(name: str):
    with Reaper as r:
        r.Undo_BeginBlock2(0)
        sel_item = r.GetSelectedMediaItem(0, 0)
        # take = r.GetActiveTake(sel_item)
        start = r.GetMediaItemInfo_Value(sel_item, "D_POSITION")
        end = start + r.GetMediaItemInfo_Value(sel_item, "D_LENGTH")

        # start = item_obj._start
        # end = start + item_obj._len

        ret = r.AddProjectMarker(0, True, start, end, name, -1)
        # r.Undo_EndBlock(f'add region with name {name}', -1)
        r.Undo_EndBlock2(0, f'add region with name {name}', -1)
    return ret


class Prefixes:

    def __init__(self, parent, row: int):
        self.frame = Frame(parent)
        self.row = row
        lb = Button(self.frame, text='load postfixes',
                    command=self.load)
        lb.grid_configure(sticky='nswe')
        self.vars = list()
        self.frame.grid_configure(row=self.row,
                                  sticky='nwse')

    def load(self):
        file_n = load_func(("postfixes", "*.reg_post"))
        names = list()
        columns = 15
        with open(file_n, 'r') as f:
            names = f.readlines()
        for idx, name in enumerate(names):
            b = Button(self.frame, text=name,
                       command=lambda name=name: self.create(name))
            b.grid_configure(column=idx % columns,
                             row=idx // columns,
                             sticky='nwes')
        self.frame.grid_configure(row=self.row,
                                  sticky='nwse')

    def create(self, name):
        main = Mask.instances[Mask.selection.get()].name
        rr = RR.instances[RR.selection.get()].name
        if rr == 'Null':
            rr = ''
        else:
            rr = f'_{rr}'
        dyn = Dyn.instances[Dyn.selection.get()].name
        create(f'{main}{rr}_{dyn}_{name}')


@ProgramStartDirect
def Setup():

    mw = Tk(className='naming regions')
    # mw.option_add('*tearOff', FALSE)
    s = Style()
    s.theme_use('clam')

    names_frame = Frame(mw)
    names_frame.grid_configure(row=0)
    masks = list()
    for idx in range(10):
        masks.append(Mask(names_frame, idx))

    ls_frame = Frame(mw)
    load = Button(ls_frame, text='load names',
                  command=lambda: Mask.load(masks))
    load.grid_configure(column=0, row=0, sticky='nwes')
    save = Button(ls_frame, text='save names',
                  command=lambda: Mask.save(masks))
    save.grid_configure(column=1, row=0, sticky='nwes')
    ls_frame.grid_configure(column=0, row=1, sticky='nwes')

    rrs_frame = Frame(mw)
    rrs_frame.grid_configure(row=2)
    rrs = list()
    for idx in range(rr_amount + 1):
        if idx == 0:
            name = 'Null'
        else:
            name = f'RR{idx}'
        rrs.append(RR(rrs_frame, idx, name=name))

    dyn_frame = Frame(mw)
    dyn_frame.grid_configure(row=3)
    dyns = list()

    for idx, name in enumerate(dyn_names):
        dyns.append(Dyn(dyn_frame, idx, name=name))

    postfixes = Prefixes(mw, row=4)

    mw.call('wm', 'attributes', '.', '-topmost', '1')
    mw.mainloop()
The problem is UndoPoints from this function
Code:
def create(name: str):
    with Reaper as r:
        r.Undo_BeginBlock2(0)
        sel_item = r.GetSelectedMediaItem(0, 0)
        # take = r.GetActiveTake(sel_item)
        start = r.GetMediaItemInfo_Value(sel_item, "D_POSITION")
        end = start + r.GetMediaItemInfo_Value(sel_item, "D_LENGTH")

        # start = item_obj._start
        # end = start + item_obj._len

        ret = r.AddProjectMarker(0, True, start, end, name, -1)
        # r.Undo_EndBlock(f'add region with name {name}', -1)
        r.Undo_EndBlock2(0, f'add region with name {name}', -1)
    return ret
works wrong within multiple undos. Instead of deleting region, this thing happens:
https://yadi.sk/i/Eojo5iSqCp_QSQ (image)
And within reloading of saved project all regions made within one script instance opened does the same with all added regions.
Levitanus is offline   Reply With Quote
Old 09-14-2018, 07:28 AM   #66
beyond
Human being with feelings
 
Join Date: Jun 2007
Location: Palm Beach FL
Posts: 265
Default

I have to activate my brain on this, but as I remember the r. context access already has Undo management. In the example code like "Modify Selected Tracks.py", there are lines like "Project = r.ProjectSelected" and "Project.UndoName = "Modify Selected Tracks Example"" which set up the automatic undo handling. If those test are running, and most of your code, I think you are mostly there. Otherwise, there may be some recent Reaper API changes to update with.
beyond is offline   Reply With Quote
Old 09-14-2018, 07:54 AM   #67
Levitanus
Human being with feelings
 
Join Date: Nov 2015
Location: Novosibirsk, Russia
Posts: 40
Default

I found the issue
It was \n character within loaded patterns)
Levitanus is offline   Reply With Quote
Old 09-14-2018, 07:59 AM   #68
beyond
Human being with feelings
 
Join Date: Jun 2007
Location: Palm Beach FL
Posts: 265
Default

Quote:
Originally Posted by mschnell View Post
What do you mean by "Alive" ?

A friend of mine actively uses Beyond to remote-control Reaper from his Python program.
:-)

I've taken a huge break from music for music videos and graphics programming. But, I hope to get back to music soon, and these tools are essential to me. :-)
beyond is offline   Reply With Quote
Old 09-14-2018, 08:06 AM   #69
beyond
Human being with feelings
 
Join Date: Jun 2007
Location: Palm Beach FL
Posts: 265
Default

Quote:
Originally Posted by JerContact View Post
Hey Beyond!! I have to say, beyond reaper is amazing! It opens up so many possibilities to sound, especially in games that use middleware. I'm a huge fan, and thank you for everything you have done!
Thank you for the kind words and encouragement! Somehow I have missed these message notifications.

I have some unpublished updates to this project. I was also planning on starting an official repository on github. I would welcome your help and integrated distribution of these tools for the Reaper power users. Let me get back into it all (still busy with graphics coding), and we will chat again. :-)
beyond is offline   Reply With Quote
Old 09-14-2018, 08:20 AM   #70
beyond
Human being with feelings
 
Join Date: Jun 2007
Location: Palm Beach FL
Posts: 265
Default

Quote:
Originally Posted by InLight-Tone View Post
Is this project still alive? I am VERY much interested in the chord track mentioned. My goal is to replace Rapid Composer functionality natively into Reaper. This project sounds like a possible solution plus there is a ton of Python code available for music composition...
I have this unpublished project "beyond patterns", and we have been using it internally. Basically, it allows you to create styles like on Tyros keyboards, even converting psr style files is quite easy, but you can make your own, record and edit them right inside Reaper like regular midi. There is a chord lead track, that recognizes chords, inversions, bass, scale, lead dynamics, and it can even be driven real time/live. And there are following, connected tracks that take the patterns as regular midi clips editable in reaper. They have special midi channels for different types of chord, bass and velocity mappings. You can make an entire orchestra follow your chords, or a super arp synth stack. It also has a special mapper for drums, and guitars. It is implemented in only JS with a simple GUI, but it works stably and very musically, just as I have dreamed of it for so long. I just have to see if I want to share my secret sauce! :-)
beyond is offline   Reply With Quote
Old 09-14-2018, 08:26 AM   #71
mschnell
Human being with feelings
 
mschnell's Avatar
 
Join Date: Jun 2013
Location: Krefeld, Germany
Posts: 14,688
Default

Quote:
Originally Posted by beyond View Post
I hope to get back to music soon,
GREAT !!!
-Michael
mschnell is online now   Reply With Quote
Old 09-14-2018, 08:27 AM   #72
mschnell
Human being with feelings
 
mschnell's Avatar
 
Join Date: Jun 2013
Location: Krefeld, Germany
Posts: 14,688
Default

Quote:
Originally Posted by beyond View Post
I was also planning on starting an official repository on github.
Great again !
Hoping for seeing Beyond in ReaPack as a result of this ...

-Michael
mschnell is online now   Reply With Quote
Old 09-14-2018, 09:15 AM   #73
beyond
Human being with feelings
 
Join Date: Jun 2007
Location: Palm Beach FL
Posts: 265
Default

I've finally become very C++ focused in the recent years, and hope to convert many of my projects (from JS, Reaktor, Kontakt) into polished native versions.

Python is still my favorite dynamic, scripting language. It is clean, succinct, capable of modifying itself for seamless wrapping of other languages, apis or remoting. It is also becoming a powerful standard in the production industry for interconnecting powerful program, Blender, Nuke, Maya, Cinema 4D, AI and Science libraries, Reaper, Unreal Engine.

For the past 2 years, I have been busy with video processing, compositing, GPU algorithms, and Unreal Engine:

https://www.youtube.com/watch?v=oulEoN9Z15g

But it is all meant to come back to music. :-)
beyond is offline   Reply With Quote
Old 09-14-2018, 01:27 PM   #74
mschnell
Human being with feelings
 
mschnell's Avatar
 
Join Date: Jun 2013
Location: Krefeld, Germany
Posts: 14,688
Default

Quote:
Originally Posted by beyond View Post
Unbelievable !

-Michael
mschnell is online now   Reply With Quote
Old 11-15-2021, 03:34 AM   #75
Michael Z Freeman
Human being with feelings
 
Michael Z Freeman's Avatar
 
Join Date: May 2017
Location: Cornwall, UK
Posts: 17
Default

Quote:
Originally Posted by beyond View Post
beyond.Reaper V26

beyond.Reaper is a small API that brings both simpler and unlimited
programming to Reaper Python developers.
Is this project still a thing or is the ReaScript API now capable of doing this ? Last update seems to be years ago and there must have been a lot of features added to the Reaper API since then. Cheers.
__________________
Never Fade Away !
Michael Z Freeman is offline   Reply With Quote
Old 11-15-2021, 11:38 AM   #76
mschnell
Human being with feelings
 
mschnell's Avatar
 
Join Date: Jun 2013
Location: Krefeld, Germany
Posts: 14,688
Default

A friend of mine extensively used Beyond in a project of his some years ago,
In Theory it should be possibel do add new Reaper API features to Beyond.

But seemingly ReaPy took over...

-Michael
mschnell is online now   Reply With Quote
Old 11-16-2021, 04:23 AM   #77
Michael Z Freeman
Human being with feelings
 
Michael Z Freeman's Avatar
 
Join Date: May 2017
Location: Cornwall, UK
Posts: 17
Default

Yes, thanks. I did eventually find it ! - https://pypi.org/project/python-reapy/

Going to replicate what I did for Ableton ...

https://michaelzfreeman.org/generati...leton-live-11/
__________________
Never Fade Away !
Michael Z Freeman 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 03:48 PM.


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