Switch structure control for OSC msg - supercollider

I’m new to SC and the whole music programming thing in general.
I’ve done a python app, that reads a text and sends word by word to SC through OSC.
The text is only the words ‘miau’ and ‘guau’ repeated for fun and to try it out.
Another thing kind of weird happening, is that on the SC console I get three times the same word than on the text and on terminal (that python prints each word). So for each ‘miau’ on the txt/terminal, I get ‘miau miau miau’ on the SC console.
The OSC communication is working, but I hear no sound. I’ve played my buffers separately, and they are working.
When I play the buffers or the SynthDef I can hear the samples being played, so I assume the issue is on the switch.
s.boot;
~b0 = Buffer.read(s, "/path/to/bd/BT0A0A7.wav")
~b1 =Buffer.read(s, "/path/to/hh/000_hh3closedhh.wav")
~b0.play;
(
SynthDef.new(\playbuf, {|amp=1, out=0, buf, da=2, rate =1|
var sig;
sig = PlayBuf.ar(2, buf, BufRateScale.kr(buf) * rate, doneAction:da);
sig = sig*amp;
Out.ar(out, sig);
}).add;
)
Synth.new(\playbuf, [\buf, ~b1.bufnum]);
(
OSCdef.new("texto",{
|msg, time, addr, port|
msg[1].postln;
switch(msg[1],
"miau", {Synth.new(\playbuf, [\buf, ~b1.bufnum])},
"guau", {Synth.new(\playbuf, [\buf, ~b0.bufnum])}
);
},
'/supercollider',
)
)
Although it copies the text, so I know the OSC is working, the samples won't play.
Any tip appreciated!

Just use single quotes instead of double quotes in the switch. Like this:
(
OSCdef(\texto,{ |msg|
switch ( msg[1],
'miau', { Synth.new(\playbuf, [\buf, ~b1.bufnum] ) },
'guau', { Synth.new(\playbuf, [\buf, ~b0.bufnum] ) }
)
}, '/supercollider' )
)
saudações,
Gil
Edited to add:
Text surrounded by double quotes are strings. Text surrounded by single quotes are symbols. These are different classes. OSC messages use symbols instead of strings, which is why using double quotes won't work.
Symbols look like any of the following:
'symbol'
\symbol
"symbol".asSymbol
'symbol'.asSymbol
Those last two can help if for some reason you have text, but you aren't sure what it is.

Related

Enabling Closed-Display Mode w/o Meeting Apple's Requirements

EDIT:
I have heavily edited this question after making some significant new discoveries and the question not having any answers yet.
Historically/AFAIK, keeping your Mac awake while in closed-display mode and not meeting Apple's requirements, has only been possible with a kernel extension (kext), or a command run as root. Recently however, I have discovered that there must be another way. I could really use some help figuring out how to get this working for use in a (100% free, no IAP) sandboxed Mac App Store (MAS) compatible app.
I have confirmed that some other MAS apps are able to do this, and it looks like they might be writing YES to a key named clamshellSleepDisabled. Or perhaps there's some other trickery involved that causes the key value to be set to YES? I found the function in IOPMrootDomain.cpp:
void IOPMrootDomain::setDisableClamShellSleep( bool val )
{
if (gIOPMWorkLoop->inGate() == false) {
gIOPMWorkLoop->runAction(
OSMemberFunctionCast(IOWorkLoop::Action, this, &IOPMrootDomain::setDisableClamShellSleep),
(OSObject *)this,
(void *)val);
return;
}
else {
DLOG("setDisableClamShellSleep(%x)\n", (uint32_t) val);
if ( clamshellSleepDisabled != val )
{
clamshellSleepDisabled = val;
// If clamshellSleepDisabled is reset to 0, reevaluate if
// system need to go to sleep due to clamshell state
if ( !clamshellSleepDisabled && clamshellClosed)
handlePowerNotification(kLocalEvalClamshellCommand);
}
}
}
I'd like to give this a try and see if that's all it takes, but I don't really have any idea about how to go about calling this function. It's certainly not a part of the IOPMrootDomain documentation, and I can't seem to find any helpful example code for functions that are in the IOPMrootDomain documentation, such as setAggressiveness or setPMAssertionLevel. Here's some evidence of what's going on behind the scenes according to Console:
I've had a tiny bit of experience working with IOMProotDomain via adapting some of ControlPlane's source for another project, but I'm at a loss for how to get started on this. Any help would be greatly appreciated. Thank you!
EDIT:
With #pmdj's contribution/answer, this has been solved!
Full example project:
https://github.com/x74353/CDMManager
This ended up being surprisingly simple/straightforward:
1. Import header:
#import <IOKit/pwr_mgt/IOPMLib.h>
2. Add this function in your implementation file:
IOReturn RootDomain_SetDisableClamShellSleep (io_connect_t root_domain_connection, bool disable)
{
uint32_t num_outputs = 0;
uint32_t input_count = 1;
uint64_t input[input_count];
input[0] = (uint64_t) { disable ? 1 : 0 };
return IOConnectCallScalarMethod(root_domain_connection, kPMSetClamshellSleepState, input, input_count, NULL, &num_outputs);
}
3. Use the following to call the above function from somewhere else in your implementation:
io_connect_t connection = IO_OBJECT_NULL;
io_service_t pmRootDomain = IOServiceGetMatchingService(kIOMasterPortDefault, IOServiceMatching("IOPMrootDomain"));
IOServiceOpen (pmRootDomain, current_task(), 0, &connection);
// 'enable' is a bool you should assign a YES or NO value to prior to making this call
RootDomain_SetDisableClamShellSleep(connection, enable);
IOServiceClose(connection);
I have no personal experience with the PM root domain, but I do have extensive experience with IOKit, so here goes:
You want IOPMrootDomain::setDisableClamShellSleep() to be called.
A code search for sites calling setDisableClamShellSleep() quickly reveals a location in RootDomainUserClient::externalMethod(), in the file iokit/Kernel/RootDomainUserClient.cpp. This is certainly promising, as externalMethod() is what gets called in response to user space programs calling the IOConnectCall*() family of functions.
Let's dig in:
IOReturn RootDomainUserClient::externalMethod(
uint32_t selector,
IOExternalMethodArguments * arguments,
IOExternalMethodDispatch * dispatch __unused,
OSObject * target __unused,
void * reference __unused )
{
IOReturn ret = kIOReturnBadArgument;
switch (selector)
{
…
…
…
case kPMSetClamshellSleepState:
fOwner->setDisableClamShellSleep(arguments->scalarInput[0] ? true : false);
ret = kIOReturnSuccess;
break;
…
So, to invoke setDisableClamShellSleep() you'll need to:
Open a user client connection to IOPMrootDomain. This looks straightforward, because:
Upon inspection, IOPMrootDomain has an IOUserClientClass property of RootDomainUserClient, so IOServiceOpen() from user space will by default create an RootDomainUserClient instance.
IOPMrootDomain does not override the newUserClient member function, so there are no access controls there.
RootDomainUserClient::initWithTask() does not appear to place any restrictions (e.g. root user, code signing) on the connecting user space process.
So it should simply be a case of running this code in your program:
io_connect_t connection = IO_OBJECT_NULL;
IOReturn ret = IOServiceOpen(
root_domain_service,
current_task(),
0, // user client type, ignored
&connection);
Call the appropriate external method.
From the code excerpt earlier on, we know that the selector must be kPMSetClamshellSleepState.
arguments->scalarInput[0] being zero will call setDisableClamShellSleep(false), while a nonzero value will call setDisableClamShellSleep(true).
This amounts to:
IOReturn RootDomain_SetDisableClamShellSleep(io_connect_t root_domain_connection, bool disable)
{
uint32_t num_outputs = 0;
uint64_t inputs[] = { disable ? 1 : 0 };
return IOConnectCallScalarMethod(
root_domain_connection, kPMSetClamshellSleepState,
&inputs, 1, // 1 = length of array 'inputs'
NULL, &num_outputs);
}
When you're done with your io_connect_t handle, don't forget to IOServiceClose() it.
This should let you toggle clamshell sleep on or off. Note that there does not appear to be any provision for automatically resetting the value to its original state, so if your program crashes or exits without cleaning up after itself, whatever state was last set will remain. This might not be great from a user experience perspective, so perhaps try to defend against it somehow, for example in a crash handler.

ignore ANSI colors in pexpect response

Can I use pexpect in a way that ignores ANSI escape codes (especially colors) in the output? I am trying to do this:
expect('foo 3 bar 5')
...but sometimes I get output with ANSI-colored numbers. The problem is I don't know which numbers will have ANSI colors and which won't.
Is there a way to use pexpect but have it ignore ANSI sequences in the response from the child process?
Here's a not entirely satisfying proposal, subclassing 2 routines of the pexpect classes pexpect.Expecter and pexpect.spawn so that incoming data can have the escape sequences removed before they get added to the buffer and tested for pattern match. It is a lazy implementation in that it assumes any escape sequence will always be read atomically, but coping with split reads is more difficult.
# https://stackoverflow.com/a/59413525/5008284
import re, pexpect
from pexpect.expect import searcher_re
# regex for vt100 from https://stackoverflow.com/a/14693789/5008284
class MyExpecter(pexpect.Expecter):
ansi_escape = re.compile(rb'\x1B[#-_][0-?]*[ -/]*[#-~]')
def new_data(self, data):
data = self.ansi_escape.sub(b'', data)
return pexpect.Expecter.new_data(self, data)
class Myspawn(pexpect.spawn):
def expect_list(self, pattern_list, timeout=-1, searchwindowsize=-1,
async=False):
if timeout == -1:
timeout = self.timeout
exp = MyExpecter(self, searcher_re(pattern_list), searchwindowsize)
return exp.expect_loop(timeout)
This assumes you use the expect() call with a list, and do
child = Myspawn("...")
rc = child.expect(['pat1'])
For some reason I had to use bytes rather than strings as I get the data before it is decoded, but that may just be because of a currently incorrect locale environment.
This workaround partially defeats the purpose of using pexpect but it satisfies my requirements.
The idea is:
expect anything at all (regex match .*) followed by the next prompt (which in my case is xsh $ - note the backslash in the "prompt" regex)
get the after property
trim off the prompt: [1:]
remove ANSI escape codes from that
compare the filtered text with my "expected" response regex
with pexpect.spawn(XINU_CMD, timeout=3, encoding='utf-8') as c:
# from https://stackoverflow.com/a/14693789/5008284
ansi_escape = re.compile(r"\x1B[#-_][0-?]*[ -/]*[#-~]")
system_prompt_wildcard = r".*xsh \$ " # backslash because prompt is "xsh $ "
# tests is {command:str, responses:[str]}
for test in tests:
c.sendline(test["cmd"])
response = c.expect([system_prompt_wildcard, pexpect.EOF, pexpect.TIMEOUT]) #=> (0|1|2)
if response != 0: # any error
continue
response_text = c.after.split('\n')[1:]
for expected, actual in zip(test['responses'], response_text):
norm_a = ansi_escape.sub('', norm_input.sub('', actual.strip()))
result = re.compile(norm_a).findall(expected)
if not len(result):
print('NO MATCH FOUND')

Using hammerspoon and the spaces module to move window to new space

I have installed the "undocumented spaces" module from https://github.com/asmagill/hs._asm.undocumented.spaces. In particular, it provides a method moveWindowToSpace that I am trying to use to bind cmd+1 to move the the current window to space 1 using the following:
local spaces = require("hs._asm.undocumented.spaces")
function MoveWindowToSpace(sp)
local spaceID = spaces.query()[sp]
spaces.moveWindowToSpace(hs.window.focusedWindow():id(), spaceID)
spaces.changeToSpace(spaceID)
end
hs.hotkey.bind({"cmd"}, "1",function() MoveWindowToSpace(1) end)
This works in the sense that it moves the window to a new space, however, the spaces appear to be in a pseudo random order.
Does any one know how to correctly map spaceIDs, as returned by spaces.query(), to the actual spaces?
As undocumented spaces has moved to spaces, the new code would be as follows (some lines could be merged, but I like the clarity of splitting operations):
spaces = require("hs.spaces")
-- move current window to the space sp
function MoveWindowToSpace(sp)
local win = hs.window.focusedWindow() -- current window
local cur_screen = hs.screen.mainScreen()
local cur_screen_id = cur_screen:getUUID()
local all_spaces=spaces.allSpaces()
local spaceID = all_spaces[cur_screen_id][sp]
spaces.moveWindowToSpace(win:id(), spaceID)
spaces.gotoSpace(spaceID) -- follow window to new space
end
hs.hotkey.bind(hyper, '1', function() MoveWindowToSpace(1) end)
hs.hotkey.bind(hyper, '2', function() MoveWindowToSpace(2) end)
hs.hotkey.bind(hyper, '3', function() MoveWindowToSpace(3) end)
After some hints from the author of the spaces module I came up with the following, which seems to do the trick.
local spaces = require("hs._asm.undocumented.spaces")
-- move current window to the space sp
function MoveWindowToSpace(sp)
local win = hs.window.focusedWindow() -- current window
local uuid = win:screen():spacesUUID() -- uuid for current screen
local spaceID = spaces.layout()[uuid][sp] -- internal index for sp
spaces.moveWindowToSpace(win:id(), spaceID) -- move window to new space
spaces.changeToSpace(spaceID) -- follow window to new space
end
hs.hotkey.bind(hyper, '1', function() MoveWindowToSpace(1) end)
Previously I was using a variation on the code at https://github.com/Hammerspoon/hammerspoon/issues/235, which hooks into osx defined hotkeys for switching spaces, but the code above is much faster!
For those in 2020 looking for simpler working solution, you could use apple scripts:
function moveWindowOneSpace(direction)
local keyCode = direction == "left" and 123 or 124
return hs.osascript.applescript([[
tell application "System Events"
keystroke (key code ]] .. keyCode .. [[ using control down)
end tell
]])
end
I tried to use solutions from this issue, but it didn’t work. On the other hand apple scripts work like charm.

What are the rules that Vala's Process.spawn_command_line_async follows in interpreting CLI arguments?

Specifically, how does it interpret arguments that are in quotes or that feature redirects from standard input (e.g. <)?
I've got the following string:
string cmd = "mail -s 'Work Order #%s' -c %s -r email#server.com %s < email.txt".printf(wo.get_text(), ownmail, outmail.get_text());
When I use
Posix.system(cmd);
The command runs as expected and an email is sent, with the body taken from email.txt.
When I use
Process.spawn_command_line_async(cmd);
I get the error from the mail command that 'option -c is not found' or words to that effect. When I lose the quotes around Work Order #%s and instead escape the spaces, the email sends (with the subject line containing the back slashes) but instead of getting the body of the message from email.txt, it treats email.txt as another recipient of the email (it shows up in my inbox with 'email.txt' under the To: section). The < is being ignored or dropped. To check things out, I used
Process.spawn_command_line_async("echo %s".printf(cmd));
This showed me that the quotes around the subject line were being dropped but the < was still there. I can use Posix.system() in my program but for the sake of simplicity and reducing dependencies (and being more idiomatic), I'd prefer to use Process.spawn_command_line(). What am I missing?
Thank you!
You probably want to play around with Shell.quote() and Shell.unquote() in your "".printf() arguments.
The Vala Process.spawn_command_line_async() function is bound to GLib's g_spawn_command_line_async () function. So a good place to start looking for more details is the GLib documentation. The GLib documentation states g_spawn_command_line_async() uses g-shell-parse-argv to parse the command line. This parses the command line so the "results are defined to be the same as those you would get from a UNIX98 /bin/sh, as long as the input contains none of the unsupported shell expansions."
Also on that page are g_shell_quote () and g_shell_unquote (). These functions are bound to Vala as Shell.quote () and Shell.unquote ().
mail only accepts the body of the message from STDIN and g_spawn_command_line_async() won't handle the redirect. So you will either need a command line tool that takes the body as an argument or using something like Subprocess instead.
Thanks to both AIThomas and Jens sending me looking in the right direction, I was able to get it working with the following code:
static int main(string[] args) {
string subject = "-s " + Shell.quote("Work Order #123131");
string cc = "-c ccemail#org.org";
string frommail = "-r " + "senderemail#org.org";
string[] argv = {"mail", subject, cc, frommail, "destinationemail#org.org"};
int standard_input;
int child_pid;
Process.spawn_async_with_pipes (
".",
argv,
null,
SpawnFlags.SEARCH_PATH,
null,
out child_pid,
out standard_input,
null,
null);
FileStream instream = FileStream.fdopen(standard_input, "w");
instream.write("This is what will be emailed\n".data);
return 0;
}

Print current frame during command line render?

Is there a way to basically print my own output during a command line render?
Let's say I don't need/want all the other output that maya spits out by default, I know you can change the verbosity level, but there's very specific things I'd like to output but I can't figure it out. I currently render out the verbosity output to file, so I wanted to print in the terminal (I'm using MAC) the frame that the render is currently up to.
This may just be simple minded, but here's what I tried:
Render -preFrame "print `currentTime -q`;" -s 1 -e 20 -rd /render/directory/ maya_file.mb
Obviously, -preFrame expects a string, according to the docs this can take mel commands, but obviously this is limited to certain commands, I'm assuming the currentTime command is pulling the information from the timeline in maya, not queering it from the Renderer it self... When I run the above command, straight away, it spits out this: -bash: currentTime: command not found and soon after the render fails/doesn't start.
Idealy, I'd like to print the following as it starts each frame:
"Started rendering frame XXXX at TIME GOES HERE", that way, I can quickly look at the terminal, and see if the renderer has failed, stuck or where it's up to and when it started it.
So my question is, seeing is currentTime is a mel command used from within Maya, is there another way I could print this information?
Cheers,
Shannon
After many hours of searching for this answer, I ended up finding out that you can start maya as an interactive shell. By doing this, I was able to source a script as I opened it, and run whatever I want into memory as If I had Maya open at the time.
/Applications/Autodesk/maya2014/Maya.app/Contents/MacOS/maya -prompt -script "/Volumes/raid/farm_script/setupRender.mel"
In the setupRender.mel file, I was able to assign variables, containing options for renders etc, in doing this, I was also able to create a global variable for the frame number, and increment it during the preFrame callback, like so:
int $startFrame = 100;
int $endFrame = 1110;
global int $frameCount = 0;
string $preRenderStatistics = "'global int $frameCount; $frameCount = " + $startFrame + ";'";
string $preFrameStatistics = "'print(\"Rendering frame: \" + $frameCount++)'";
string $additionalFlags = "";
string $sceneFilePath = "'/Volumes/path/to/file/intro_video_001.mb'";
system("Render -preRender " + $preRenderStatistics + " -preFrame " + $preFrameStatistics + " -s " + $startFrame + " -e " + $endFrame + " -x " + $additionalFlags + " " + $sceneFilePath);
This is a very simplified version of what I currently have, but hopefully this will help others if they stumble across it.
Take a look at the pre render layer MEL and/or pre render frame MEL section of the Render Settings.
It expects MEL, so you'll either need to write it in MEL or wrap your python in MEL. For such a simple use, I'd say just write it in MEL:
print `currentTime -q`

Resources