Store/Retrieve more than one variable in Clipboard using an Array?

I use an app called MIDIpipe, which has a limited implementation of Applescript. One of the limitations is not being able to share variables among “pipes,” which are MIDI event modifier modules.

Currently I am able to store one character on the clipboard with one module and reference with another module, that outputs a pre-programmed MIDI controller. Unfortunately there is only one clipboard.

I am guessing that an array would work here, but I am unfamiliar with the syntax. I would want to declare a 4 element array, and be able to store/retrieve each element separately without disturbing the other elements.

My current code to store/retrieve the variable to the clipboard:


on runme(message)
if (item 1 of message = (144)) and (item 2 of message = 60) and (item 3 of message > 0) then
set myVar to “1”
set the clipboard to myVar

else if (item 1 of message = (144)) and (item 2 of message = 61) and (item 3 of message > 0) then
set myVar to “2”
set the clipboard to myVar (etc)

And to retrieve the variable:


on runme(message)

set MixVar to the clipboard

if (item 1 of message = (144)) and (item 2 of message = 60) and (item 3 of message > 0) then
	
	if (MixVar = "1") then
		set (item 1 of message) to (176)
		set (item 2 of message) to (60)
		set (item 3 of message) to (80)        (and so forth)

I am also a Filemaker programmer, learning Applescript has been a blast.

Stublito

Model: MBPro
Browser: Safari 537.36
Operating System: Mac OS X (10.9)

Is this what you are looking for:

set clipData to {23, 21, 44, 76}

set the clipboard to clipData


set readData to the clipboard as list

(readData's item 3)

-->  44

With your example, I realize it’s more complicated than I first imagined, as (Its obvious now) the whole string needs to be read/written whenever any individual element is referenced.

First, I would have to either initialize the clipboard string at the outset, or put an “if null” statement in every modifying script. The clipboard will always be modified (a MIDI key played) before being read.

Every modifying script would need to load the current array from the clipboard, modify one (or more, if I can get the hang of it) of its elements, and then write the string back.

Every reading script would have to again load the whole string and grab the correct element(s).

FYI, In MIDIPipe you can declare local Property variables.

Can someone help me wit the syntax?

Thank you.

Is there a particular reason that you must use the clipboard? You can declare a wide variety of variables using plain AppleScript that could simplify your work immensely.

The applescript code is executed in a module, which is part of an individual process called a “pipe”, run in a hosting app called MIDIpipe. Many of these so-called “pipes” can be run concurrently. The standard syntax of one of these “pipes” would be - Specify MIDI In Port, Execute Applescript (or not, there are other modules with specific functions as well) , Specify MIDI Out port.

Each pipe acts serially on one midi event at a time. The Applescript implementation is limited, as I understand it, variables in one pipe cannot be accessed by another.

Another MIDIpipe Applescript technique I found was written for an app called QLab. It involved running a (real) Applescript process in the background, to initialize an array of variables, and then peek and poke between them. This was for an array of 128 MIDI controllers, so it seemed unnecessarily complex for just 4 variables.

Being able to easily store just the one variable on the clipboard has really expanded MIDIpipe’s utility for me. My goal is to keep my setup as simple as it now is, but increase the number of variables modestly.

Thanks for everyone’s input.

No longer store one variable to the clipboard but store a record.
Extracting a record from the clipboard requires an awful piece of code but it may be done.

set alpha to {property1:"4", property2:"oops", property3:"what"}
set the clipboard to alpha
delay 0.2

set maybe to list of (the clipboard)
--> {list:{"property1", "4", "property2", "oops", "property3", "what"}}

# Create a fake record
set alpha2 to {property1:"", property2:"", property3:""}

# Fill it
repeat with i from 1 to ((count maybe) - 1) by 2
	if item i of maybe is "property1" then
		set property1 of alpha2 to item (i + 1) of maybe
	else if item i of maybe is "property2" then
		set property2 of alpha2 to item (i + 1) of maybe
	else if item i of maybe is "property3" then
		set property3 of alpha2 to item (i + 1) of maybe
	else
		--
	end if
	
end repeat

# Here we have rebuilt the original record
# We may insert code to change one of several properties before pushing it again to the clipboard
set property2 of alpha2 to "bingo"
alpha2 --> {property1:"4", property2:"bingo", property3:"what"}
set property3 of alpha2 to "WHAT"
alpha2 --> {property1:"4", property2:"bingo", property3:"WHAT"}
set the clipboard to alpha2

# Now check if the job is well done
the clipboard
--> {list:{"property1", "4", "property2", "bingo", "property3", "WHAT"}}

Yvan KOENIG running Sierra 10.12.5 in French (VALLAURIS, France) vendredi 14 juillet 2017 10:54:58

This variation on an old hack still seems to work. Still awful, though. :wink:

set alpha to {property1:"4", property2:"oops", property3:"what"}
set the clipboard to alpha

set beta to (record {«class usrf»:(the clipboard as list)} as record)'s «class seld»
--> {property1:"4", property2:"oops", property3:"what"}

Although this is faster:

set alpha to {property1:"4", property2:"oops", property3:"what"}
set the clipboard to alpha

set beta to (run script "on run {fred}
	return fred
end run" with parameters {{«class usrf»:(the clipboard as list)}})
--> {property1:"4", property2:"oops", property3:"what"}

Like Yvan’s suggestion, these don’t work where the record’s labels are reserved terms like ‘name’ or ‘date’, but that’s the way the clipboard works, not the fault of any of the scripts.

I’ve been fooling with MidiPipe for a couple of days, although I don’t have a MIDI system with which to test it properly. It’s certainly very limited AppleScriptwise, with only one command of its own and the ability to run script modules much like Automator’s which have no connection with each other — except possibly by passing MIDI messages down the pipes they’re in or injecting them into other pipes using the one command. It’s also difficult to offer scripting suggestions when its not totally clear what has to be passed between pipes (actual MIDI data or just indicative signals?), why, and whether or not it’s possible to rationalise the piping set-up itself. I imagine the transfer of information and the acting upon it would have to happen very quickly in a musical situation.

Thanks Nigel.
This day is a good one, I learnt something.

Yvan KOENIG running Sierra 10.12.5 in French (VALLAURIS, France) vendredi 14 juillet 2017 14:18:56

That was my thought, which perhaps makes the clipboard a bad choice.

Assuming Midipipe is running the scripts as host, and there are only four or five values, one option would be to (ab)use some AppleScript properties. I’m thinking of print depth, print length, pi and perhaps seconds. They can be set to any reasonably numeric value, I assume, and the scripts are probably unlikely to use at least the first three.

And it’s even more awful than your code :cool:

Thank you! :lol:

But you’ll be gratified to hear that your idea does actually work — provided the terms are preceded by ‘my’ or ‘AppleScript’s’ in each script. I’ve tried so far with various types of value in ‘pi’, ‘minutes’, and ‘text item delimiters’. Once they’re changed in a script in one pipe, the changes are visible to a script in another. I think you’ve cracked it, Carruthers. Or cracked something, anyway. :wink:

I’m a guitar player/singer that uses bass pedals and a laptop. I’ve created a pretty convoluted process, but it works.

You probably know about arranger keyboards, you can play the keyboard or bass pedals and the program plays short accompaniment loops. When you press a footswitch, the arranger waits until the beginning of the next bar and then does something - changes the loop, add extra instruments etc.

Arranger song files (called styles) use obscure non MIDI info, they are difficult to program. I have found a way to do more or less the same thing using my MIDI sequencer program.

Currently I CAN switch parts, but the action happens WHEN I press a footswitch. As this is usually the same time (the beginning of a bar) I have to play a bass note (and perhaps do something else with my feet), I really can’t do it. I need to be able to press the footswitch ahead of time and have the switching action occur at the beginning of the next bar.

The footswitch is connected to the circuit board matrix from a USB keyboard’s number pad, which generates MIDI notes (using another app called MIDIkeys), which goe to MIDIpipe. MIDIpipe then uses applescript to write a specific “part number” to the clipboard based on the MIDI note in.

However, due to MIDIpipe’s limited functioning, in order to be able to press the footswitch early and have the part change happen on the new bar, I would have to do three separate processes.

1, Press the footswitch, set clipboard first number to the Mix number.

  1. Have a special “trigger” MIDI note sent from the sequencer to MIDIPipe on the first beat of the bar, which then uses Applescript to set the second character of the clipboard string to a boolean 1.

  2. Then the MID switching part, which says, read the clipboard, if the second character is 1, switch to part 1-5.

The lat process would be in a loop that runs for every MIDI event passing through the “pipe,” so speed might be an issue. If there is a slight lag, I haven’y yet noticed, and also I can adjust the timing of the trigger note to compensate anyway.

And finally, I would like to expand the use this “delay till next bar” function for other foot triggered events, as I am pretty clumsy ha ha… I would just add more “positions” to the string (list?) stored on the clipboard.

BTW I am in Hawaii which is literally the other side of the world from you guys in Europe.

Thanks again.

Sorry, I got carried away. Back to the code.

If I understand you correctly, certain well labeled properties can be set with a numerical value in one script and referenced in another:

my print depth
my print length
my pi
my seconds
my text item delimiters

I will give them a go.

Thank you.

Hi.

That’s right. They’re properties of the AppleScript instance being used by MidiPipe and are shared by every script it’s running. Any changes made to their values remain in force across all the scripts until either further changes are made or MidiPipe is quit. However, using them like this is a hack and you have to be sure not to use any labels that your scripts may need to use in the normal way!

Triggered scripts, by the way, can declare their own individual properties in which they can store data for their own use on subsequent triggerings. This is of course ideal for the script that’s waiting for the “first beat of the bar” cue.

Is it feasible in your set-up to send the footswitch signal(s) down the same pipe as the other stuff but on a different channel? That way you’d only need one script.

As the footswitch is really a keyboard, connected to MIDIKeys (which outputs the notes to be processed in MIDIpipe), then yes, I can set MIDIkeys to an unused MIDI channel.

However, being a novice, I am balking at all those nested if statements :wink:

As I have MIDI events flying in and out of MIDIpipe in several directions at once, a lot of nice simple pipes are preferable to fewer more complicated ones. At least, until I get better at scripting.

I googled for the complete list of the properties above, but didn’t find anything. I guess this is a hack so they are undocumented. Are there any more (I’m getting greedy…)

Mahalo.

Peter

It’s definitely a hack, but they’re not undocumented. They’re often referred to as global constants, because generally it makes no sense to change their value. But you can – you just need to make sure you then never use them with the expectation that they contain what’s written on the tin.

You could use minutes and weeks, I suppose.

These should be reasonably safe to use when only MIDI-watching:

These aren’t mentioned as constants in ASLG, but appear to be settable:

This is a settable constant in some contexts — such as when preceded by ‘my’ — but the return command in others:

This isn’t a settable constant, but with ‘my’ or ‘Applescript’s’ in front of it, does appear to be a settable AppleScript property.

  1. Don’t use any other constants you may discover — at least, not without checking here first.
  2. Always include the ‘my’.
  3. Make sure your scripts are thoroughly commented so that you always know which values are supposed to go in which properties!

By the way, a non-hack solution to this in Yosemite or later would be:

use AppleScript version "2.4" -- Yosemite (10.10) or later
use framework "Foundation"
use scripting additions

set alpha to {property1:4, property2:"oops", property3:{1, 2, 3, 4, 5}, property4:{a:"aardvark"}, property5:Tuesday}
set the clipboard to alpha
delay 0.2

set maybe to (the clipboard as list)
set values to {}
set keys to {}
repeat with i from 1 to (count maybe) by 2
	set end of keys to item i of maybe
	set end of values to item (i + 1) of maybe
end repeat
set beta to (current application's class "NSDictionary"'s dictionaryWithObjects:(values) forKeys:(keys)) as record

Or if you still wanted it to look awful:

use AppleScript version "2.4" -- Yosemite (10.10) or later
use framework "Foundation"
use scripting additions

set alpha to {property1:4, property2:"oops", property3:{1, 2, 3, 4, 5}, property4:{a:"aardvark"}, property5:Tuesday}
set the clipboard to alpha
delay 0.2

tell {the clipboard as list, {{}, {}}}
	repeat with i from 1 to (count beginning) by 2
		set end's beginning's end to beginning's item i
		set end's end's end to beginning's item (i + 1)
	end repeat
	set beta to (current application's class "NSDictionary"'s dictionaryWithObjects:(end's end) forKeys:(end's beginning)) as record
end tell

Or speed things up a little:

use AppleScript version "2.4" -- Yosemite (10.10) or later
use framework "Foundation"
use framework "AppKit"
use scripting additions

set alpha to {property1:4, property2:"oops", property3:{1, 2, 3, 4, 5}, property4:{a:"aardvark"}, property5:Tuesday}
set theDict to current application's NSDictionary's dictionaryWithDictionary:alpha
set theData to current application's NSKeyedArchiver's archivedDataWithRootObject:theDict
set thePasteboard to current application's NSPasteboard's generalPasteboard()
thePasteboard's clearContents()
thePasteboard's setData:theData forType:"myVeryOwnType"
delay 0.2
set theData to thePasteboard's dataForType:"myVeryOwnType"
set beta to (current application's NSKeyedUnarchiver's unarchiveObjectWithData:theData) as record

:wink:

:cool:

Here’s how I think a one-pipe/multi-channel solution might look. It’s not a fully working script as I don’t know the numbers involved and can’t test it anyway. Just a demo of how it might be structured. The script module’s “pass through” option would be left unchecked so that the script controlled both output and throughput.

-- Footswitch signal channel. (16 here. Channels are numbered 0-15 in the transmitted data.)
property footSignalChannel : 16 - 1
-- MIDI Note On and Control Change event codes.
property noteOn : 9
property controlChange : 11
-- MIDI number of a note reserved for marking the first beat of every bar. Set as required.
property barlineNote : 60
-- List of Control Change messages for the mixes to be used. Set as required. The actual channel(s) will be set before they're transmitted.
property mixChangePresets : {{176, 60, 80}, {176, 60, 81}} -- etc.
-- Mix change message waiting to be sent, when there is one.
property mixChangeMessage : missing value
-- Flag to indicate if a mix change is waiting.
property mixChangeWaiting : false

on runme(message)
	-- Get the value of this message's first byte, which contains the event code and channel number.
	set eventAndChannel to item 1 of message
	-- Extract the channel number
	set messageChannel to eventAndChannel mod 16
	-- Is it the footswitch signal channel?
	if (messageChannel is footSignalChannel) then
		-- If so, is the message a Note On event with a velocity > 0?
		if (eventAndChannel div 16 is noteOn) and (item 3 of message > 0) then
			-- If so, set up a mix change to be actioned on the next bar line.
			-- I've assumed here is that note #60 should set the first mix, note #61 the next, and so on.
			set mixChangeMessage to item ((item 2 of message) - 59) of mixChangePresets
			set mixChangeWaiting to true
		end if
		-- Since this channel's only used for the footswitch signal, it doesn't matter if the message is passed through or not.
		
		-- Otherwise assume this message is on the sequencer channel.
		-- Is there a mix change waiting to be actioned?
	else if (mixChangeWaiting) then
		-- If so, is this message the trigger note indicating the first beat of the bar?
		if (eventAndChannel div 16 is noteOn) and (item 2 of message is barlineNote) and (item 3 of message > 0) then
			-- If so, cancel the change-waiting flag and output the change message on the original channel.
			set mixChangeWaiting to false
			set item 1 of mixChangeMessage to controlChange * 16 + messageChannel
			return mixChangeMessage
		else
			-- Not a trigger note. Pass the original message.
			return message
		end if
	else
		-- No mix change waiting. Pass the original message.
		return message
	end if
end runme