I'd like to access the shell from Pd to run some scripts (mainly to automate the process of archiving recorded sessions to an external HDD), but the shell object from the ggee external is causing some issues

Bela will start spewing and looping the same pipe read error message as soon as the shell object receives a valid message
@spoitras got the external working, so it must be something on my end

Is there another way to access the shell from Pd?
alt text

Hmm that's weird. What commands are you running with geee/shell?

It seems the commands do get executed, unless something gets printed to stdout
If anyone is having the same problem in the future; a hacky way to circumvent the issue is sending all output to /dev/null

I find this sort of expected with large amounts of text being printed. Could you confirm what commands you use to trigger the issue? Also, can you try following the instructions here to upgrade to Pd 0.51 and see if the problem persists?

You should recompile your external after the update, as I am not 100% sure I preserved binary compatibility.

3 years later

Hi!
I have the same kind of issue using ggee/shell. The command used is date.
I cannot join the shell object itself I'm using here ("not allowed").

bug-shell.pd
942B

yes I remember about [send bela_system], but I need the return of the command date to get the current time...

Do you need the current date or a monotonic time since the start of the script? The latter can be obtained with the [timer] object.

You can also do this:

copy this zip archive to the board, it provides pdsend which allows to send messages to pd from outside Pd.
Then run

unzip pdutils.zip
mv pdsend pdreceive /usr/local/bin

Then create a file like this called get_time.sh in your project:

#!/bin/bash

# do magic here to get time into variable TIME
TIME=$(date +%Y.%m.%d-%T)
# send TIME to Pd's netreceive
echo $TIME | pdsend 9999 localhost udp 2>/dev/null

Then you can have a patch like the attached one (with a suitable interval in the metro):

This patch executes get_time.sh which in turn sends the current time to pd via pdsend.

Nice workaround!
But since I need a bang on every second, should I write a shell script (or whatever) that sends directly the time on seconds to Pd?

sure that's also easy enough: put something like this in a .sh file and use this procedure to run it in the background: https://learn.bela.io/using-bela/bela-techniques/running-a-program-as-a-service/

#!/bin/bash

while sleep 1; do
  # do magic here to get time into variable TIME
  TIME=$(date +%Y.%m.%d-%T)
  # send TIME to Pd's netreceive
  echo $TIME | pdsend 9999 localhost udp 2>/dev/null
done
6 days later

ok,
I finally wrote a pd object (my first one, be indulgent).
But let's say it immediately: it doesn't work very well, I get an error on each call (bang):
[n] mode switches detected on the audio thread. (n begin at 1 and is incremented on each error)
Here is the compiled object for Bela, and source code. It is based on time.h. Maybe you have an idea on how to improve. It works on linux.
https://www.dropbox.com/scl/fi/7sxt9fgy1ehd8ex4znzd5/ahora.pd_linux.zip?rlkey=jk5d8he4jh274rxwrxk2vpu9j&dl=0

The object [ahora n] works as follow:
outlets, floats, from left to right:
-year
-month
-day
-hour
-min
-sec
arg: number between 1 - 5. The number of active outlet active when inlet banged, from left to right (ex: 3 means hour,min,sec). None, 0 or >5 means all outlets actives.
inlet:
-bang: outputs time and date following creation arg.
-message year, month, day, hour, min, sec outputs corresponding element, regardless of creation arg

Nice one, it probably works just fine, the issue is that time() will call into the Linux kernel and as it is called from within the audio thread it breaks the real-time guarantees of Bela, hence the "mode switch" warning.

You have several options:

  • start up a thread and call time() in there, have the audio thread print the result that was obtained from the other thread. If you don't want the other thread to be polling all the time, you'll need to trigger a time() call in the other thread when you receive a bang, then set a pd timer (I believe that's what it's called?) a few ms later that reads the up-to-date result of time when it's ready and sends it out of the output. This is the most generally-applicable version, which will also improve real-time friendliness of your external when running outside Bela. It may get complicated because on a multi-core processor you'll need some way of guaranteeing memory coherence so that the audio thread never sees a corrupted version of the time and has a way of knowing for sure whether it's been updated.
  • instead of calling time(), call __wrap_time(). This is a function provided by Xenomai's libcobalt that is safe to call from the audio thread. Before you call it you may have to declare it (depending on your compiler's options):
    extern time_t __wrap_time(time_t *tloc);
    I don't think you have to change your linker flags. You should be able to build it like this and at runtime it will be resolved because libcobalt is already loaded by the Bela binary.
  • leave the external exactly as it is. Bang it only once at the beginning of the script to get the time, ignore the mode switch message. Then use bangs to the [timer] object to measure elapsed time while the program is running. I appreciate this may have accuracy implications for long running patches (which you can obviate to by resetting the timer every so often) and if you need the year/month/day/hour/min/sec the previous option may be easier.

    giuliomoro I appreciate this may have accuracy implications for long running patches

    and the running time will be... 5 years!!! ;-D (that is the time we have to guarantee the installation)

    I'll check your propositions and try one... Thank you!!

    ok, I've just tried __wrap_time(). Doesn't work without extern time_t __wrap_time(time_t *tloc);. It works with it, but same errors. It seems localtime is guilty...

    I will try the thread way later, but if that one works it would be nice too...

    Weird, it should work. Did you replace all instances of time()? Is there any other function call you are making that could be responsible?

    Try to remove localtime and see if the mode switches persist

    a summary:
    Original code calls time() and localtime(), and generates errors
    If I only call time() (with a comment on localtime()), I get the the errors as well, and obviously 0's on outputs.
    If I call __wrap_time() without localtime(): NO errors!
    But with __wrap_time() with localtime(), errors again.

    It means both time() and localtime() are guilty. I couldn't find a way to wrap localtime the same way...

      rph-r But with __wrap_time() with localtime(), errors again.

      Ok that's what I was suspecting . It looks like xenomai doesn't provide a wrapper foe localtime.

      Try using localtime_r() instead and that may avoid the kernel call?

      ok yes, no more mode switch!
      But I've just noticed time is not right: time() gives 1731277487 and __wrap_time() gives 7912 !?