Feasible way to modify text in buffer via external controller?

A friend and I are looking into building an external controller for Sonic Pi for live performance that’s tactile, but doesn’t involve typing text. My initial idea is to have buttons and encoders modify specific parts of a buffer template by filling in text that is associated with the button-push or encoder-increment at that control element’s designated line number and column number location in the text buffer. A different button push would effect the reload.

For example, say a template starts with a live_loop statement at line 10, and and end at line 20, and a sleep 1 at line 19. Pressing a particular button could insert a my_function_that_contains_a_sleep() call at line 18 and comment out the sleep at line 19. You make other changes, then you hit the reload button when you’re ready.

This approach would make developing and debugging easy because, after an initial template setup, the state of the control elements (button on/off lights, encoder readouts, say -100 to 100) would represent exactly the buffer’s state. The goal would be to stuff a Raspberry Pi inside a box that has the control elements, hit start, which loads the template and runs its in the buffer, and voila! you’ve got a Sonic Pi MIDI controller.

My friend is the hardware person, not me, but does this sound feasible? I realize that operating SPI strictly from a limited number of control elements would limit how much of SPI’s operation you could use, but with clever library functions written for different buttons, I think you could do a lot to sequence external MIDI gear in a quick and reliable way that’s would be very musically useful.

I feel like I don’t have any particularly helpful advice on this off the top of my head :sweat_smile: at the moment, but I have daydreamed about a similar idea myself in the past - using external devices or software to affect the text in the buffer.

In theory, it sounds quite feasible - sending either text content or an event that Sonic Pi responds to sounds like the easy part. IMO the challenge would lie in the method of determining where and how to update the text content - certainly not impossible, just fiddly - tracking the boundaries of sections of text. Projects like GitHub - Qirky/Troop: Real-time Live Coding collaboration app would already be doing something similar, to track the cursors and text limits of multiple users within multiple code editor panels - no doubt a Sonic Pi variation such as what you have described would do the same, just a little simpler.

I thought if you always work from a template, e.g., an 80 x 80 grid of text characters, then it would just be a matter of overwriting what’s at a location with new stuff, and you’d be able to track your buffer’s state exactly. The options would be limited, but with cleverly chosen options the thing would be dynamic and musical.

What I have no concept of is where and how and at what point does one specify the contents of a Sonic Pi buffer without typing at the keyboard? That Troop thing might be over my head to do, but before I get into it, how does SPI “know” what’s in its buffer? Or any text editor, for that matter. It must be reading some place in memory, right? Is there some way for me to write directly to that place in memory?

Sure. If you guarantee that you will only be changing a known amount of lines, then yes.

To insert text into a buffer of the GUI could possibly be done with something like GitHub - jordansissel/xdotool: fake keyboard/mouse input, window management, and more, by simulating keyboard presses, and then doing the same to trigger a Run, thereby causing the current buffer contents to be stored as a Git commit (Given that Sonic Pi buffers are linked to files that are tracked with Git, where each Sonic Pi ‘Run’ is stored as a Git commit.)

1 Like

A non-flexible template is an easy price to pay if it reduces the programming task to simply plastering sequences of text characters here and there. But that whole Git thing is new to me. I’ll have to study up, I guess, or else make my friend do that chore.

1 Like

Reading through the xdotool documentation, it seems to be all about x, y screen locations in terms of pixels, not character row/column locations within a text buffer.

I see SPI’s buffers in ~/.sonic-pi/store/default on my Mac, and they’re just text files. Is there some way to make SPI, for example, update its workspace #4 from ~/.sonic-pi/store/default/workspace_four.spi, or monitor that file for updates, etc.?

Several ways, perhaps… but not necessarily trivial :slightly_smiling_face:

There is a Sonic Pi function load_buffer(file_path) which replaces the contents of the buffer where it itself was called. (Which of course is of no direct use if we are trying to trigger the process outside of the GUI, but maybe there’s a way to work around that somehow and call other code that makes it act the same way).

Or perhaps it’s necessary to go lower level and try to hook into the same server code that is used at start up to load the buffers :man_shrugging:

Just a thought. You might find it useful the sonic-pi-cli gem to run an external text file in Sonic Pi. This will superimpose on whatever is already running. You can send a stop command first if you want a fresh start.
You could then edit this file externally to sonic-pi in a different editor , which might be easier. You can at least get directly at the text source file. You have to work on the same machine as SP is running on.
BTW the existing sonic-pi-cli gem will need updating to run with SP4.0beta and above as the log file structures have changed and the relevant port to communicate on has to be extracted differently. I have a trial version running here with that setup.

I did a project several years ago which produced a jukebox which could select files to be run on Sonic Pi pretty instantly. It was controllable using TouchOSC, and used the sonic-pi-cli to play selected files.

1 Like

What on earth is “sonic-pi-cli gem”?

OOPS types corrected
A command line utility you can install to enable commands like ruN_file to be sent to SP. you can use this in a script which codes a file and sends and runs it in Sonic Pi.

2 Likes

Thanks—I’d never heard of gems before. I still don’t know what “chords a file” means.

Interestingly, you can control SPI directly from an emacs shell buffer.

Parsing documentation for sonic-pi-cli-0.2.0
Installing ri documentation for sonic-pi-cli-0.2.0
Done installing documentation for osc-ruby, sonic-pi-cli after 0 seconds
2 gems installed

How do you read the installed documentation?

I still don’t know what “chords a file” means.

I believe that was a typo. Robin was just talking about a way that Sonic Pi has to send bulk amounts of code to the server by calling a single function - run_file(file_path) will tell the Sonic Pi server to execute the entire contents of the file at file_path.

How do you read the installed documentation?

One way: notice where you shared the above command line output it talks about something called ri? That is a command line utility for reading Ruby documentation. Here’s a guide that describes it:

http://rubylearning.com/satishtalim/ruby_ri_tool.html

yeah, I’d guessed that (after also trying man), but I get:

% ri sonic-pi-cli
Nothing known about .sonic-pi-cli

% ri sonic-pi-cli-0.2.0
Nothing known about sonic-pi-cli-0.2

What am I still missing?

Oh right - well, it seems no documentation is automatically generated that way.
That message about ri must be a default message that occurs whether or not there is actually any available.
The ri tool is able to display documentation generated with RDoc - so you could use RDoc to generate basic documentation (RDoc inspects your source code and automatically parses things like class names, function definitions, etc) and then run gem server which runs a local web page that displays links to information about your local gems, including documentation.
Honestly though, if you’re just after basic usage of the gem, you’ll probably get enough of an idea by looking at the official source repo (GitHub - Widdershin/sonic-pi-cli: A simple command line interface for Sonic Pi, written in Ruby).

1 Like

yeah. it works great, actually.

1 Like

Glad to hear it :slight_smile:

You might also like GitHub - lpil/sonic-pi-tool: 🎻 Controlling Sonic Pi from the command line, which has a faster startup time due to being written in Rust, and has a few more features.

1 Like

I think stopping, then loading anew isn’t going to work for me. I need to be able to make small edits to a running buffer, then hit reload so that the changes take effect on the next beat. Is there a way to get standard reload behavior from the command line? Replace rather than superimpose, I guess you could say.

sonic-pi-tool reports Sonic Pi server NOT listening on port 4557.

I get:

> sonic-pi-tool start-server
Sonic Pi server booting...

Using primary protocol: udp
Detecting port numbers...
Listen port: 4557
  - OK
Scsynth port: 4556
  - OK
Scsynth send port: 4556
  - OK
OSC cues port: 4560
Port 4560 unavailable. Perhaps Sonic Pi is already running?

then

> sonic-pi-tool check
Sonic Pi server NOT listening on port 4557

Perhaps your versions of Sonic Pi and sonic-pi-tool are incompatible with each other :man_shrugging:

This is very much likely to be the case. Sonic Pi’s internal language runtime server stopped listening on a fixed port such as 4557 quite a while ago and now randomises the port number to increase security.

Also, v4-BETA contains significantly reworked internals which would likely require an update from external tools such as this to work effectively (although things are still in flux at this stage).

It is the hope that getting a nice command line tool will both be possible and easier in the future and not be so brittle, but it hasn’t been a core development focus.