Skip to content

Instantly share code, notes, and snippets.

@rbnpi
Last active September 30, 2021 06:09
Show Gist options
  • Star 4 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save rbnpi/ca5e80258c0c1296b1c6c3974060b3ce to your computer and use it in GitHub Desktop.
Save rbnpi/ca5e80258c0c1296b1c6c3974060b3ce to your computer and use it in GitHub Desktop.
Experimental code to play with the new OSC-API in Sonic Pi2.11dev. Note this is highly experimental and will probably change. The two programs allow input from a Mac keyboard to Sonic Pi. You may need to change some of the keyboard mapping on other keyboards. You can here a recording of the system at https://soundcloud.com/scrbn/sonicpiexperimen…
#This program polls the keyboard and then maps the code for detected keys to midi note values
#which are tranmsitted to the new OSC-API in Sonic Pi
#Warning THIS API IS EXTREMEMLY EXPERIMENTAL AND MAY CHANGE (current version 2.11 dev 2d13e)
#This script runs in a terminal window. Once the SP script is running, type in the terminal window to send key presses.
require 'io/wait'
require 'socket'
require 'rubygems'
require 'osc-ruby'
client ||= OSC::Client.new('localhost', 4559) #set up OSC channel to port 4559
def char_if_pressed #routine to scan for keyboard press (non-blocking)
begin
system("stty raw -echo") # turn raw input on
c = nil
if $stdin.ready?
c = $stdin.getc
end
c.chr if c
ensure
system "stty -raw echo" # turn raw input off
end
end
nIn=[39,59,92,93,97,100,101,102,103,104,106,107,108,111,112,115,116,117,119,121] #ascii numbers from keyboard
nOut=[77,76,79,78,60,64,63,65,67,69,71,72,74,73,75,62,66,70,61,68] #midi number mapped by hash
nV=nIn.zip(nOut).to_h
while true #main loop, runs until stopped by ctrl-c
k=0 #0 will be transmitted if no key pressed
c = char_if_pressed
k= "#{c}".ord if c #work out ascii value of key
if k>0 then
note="#{nV[k]}".to_i #use the hash to get the correspending midi note (if any is mapped)
if note > 0 then #only process mapped notes
prepared_command = OSC::Message.new("/kyb", note) #transit the note info as an osc message to /kyb
client.send(prepared_command)
puts note #visual feed back in terminal
end
end
sleep 0.01 #short gap
end
#experimental program to drive SP from computer keyboard via generated OSC messages to new OSC API
#by Robin Newman June 2016
#use accompanying keyboard driver ruby program in a terminal window, start SP and then type "notes' in terminal window.
#The keyboard maps keys "A...\" to :c4 to :g5 with keys in the next row up used for sharp/flat notes
#You may have to change the odd key mapping for use on other keyboards
#THIS script runs in Sonic Pi
set_sched_ahead_time! 0
live_loop :kyb do
as=sync "/kyb" #incoming oosc messgage on /kbd sends note midi value as a parameter from kyeborad program
n=as[:args][0]
with_fx :reverb, room: 1 do
synth :square ,note: n,release: 0.1 #play the note with lots of reverb on square synth
end
end
live_loop :r do #accopanying drone notes
synth :tri,note: :c4,release: 0.4
sleep 1
synth :tri,note: :c3,release: 0.4
sleep 1
end
live_loop :dr do #accompanying rhythm
sample :bd_haus
sleep 1
sample :bd_haus
sleep 0.75
sample :bd_haus
sleep 0.25
end
@Enkerli
Copy link

Enkerli commented Oct 9, 2016

It works! How neat!

Wonder if it only works on localhost. Would like to use an external device to control SPi (for instance, TouchOSC running on an iPhone, sending OSC messages to a Mac or RasPi or Ubuntu machine running Sonic Pi). In the past, it was only working on localhost and it was basically a hack of the communication between the SPi GUI and server, AFAICT. But with Sam talking about an API for OSC in 2.11dev, it sounds like maybe this can work more largely?

There were similar things we could do with previous versions, but it sounds like the major difference might be in the realtime control. In the past, examples only polled the OSC messages at specific intervals and it wasn’t possible to have any kind of realtime control. But maybe because of the “fast OSC” code in 2.11dev, the result from these scripts has no noticeable latency and it’s really realtime control.

This changes everything!

Had to modify the terminal script slightly. It was complaining about the to_h part, which is the end of the section on converting keyboard values to MIDI.

Here’s the prompt:

$ ruby OSC_API_KeyboardInput.rb 

Here’s the error:

OSC_API_KeyboardInput.rb:27:in `<main>': undefined method `to_h' for #<Array:0x007f982a86aeb0> (NoMethodError)

Commented those lines out and set the note to k instead of nIn[k]. Works well, but with MIDI notes following the alphabetical order instead of the keyboard order. This is with a French-Canadian keyboard. (Accented characters do make it fail.)

Here’s the slightly modified version.

#This program polls the keyboard and then maps the code for detected keys to midi note values
#which are tranmsitted to the new OSC-API in Sonic Pi
#Warning THIS API IS EXTREMEMLY EXPERIMENTAL AND MAY CHANGE (current version 2.11 dev 2d13e)
#This script runs in a terminal window. Once the SP script is running, type in the terminal window to send key presses.
require 'io/wait'
require 'socket'
require 'rubygems'
require 'osc-ruby'

client ||= OSC::Client.new('localhost', 4559) #set up OSC channel to port 4559

def char_if_pressed #routine to scan for keyboard press (non-blocking)
  begin
    system("stty raw -echo") # turn raw input on
    c = nil
    if $stdin.ready?
      c = $stdin.getc
    end
    c.chr if c
  ensure
    system "stty -raw echo" # turn raw input off
  end
end

#nIn=[39,59,92,93,97,100,101,102,103,104,106,107,108,111,112,115,116,117,119,121] #ascii numbers from keyboard
#nOut=[77,76,79,78,60,64,63,65,67,69,71,72,74,73,75,62,66,70,61,68] #midi number mapped by hash
#nV=nIn.zip(nOut).to_h

while true #main loop, runs until stopped by ctrl-c
  k=0 #0 will be transmitted if no key pressed
  c = char_if_pressed
  k= "#{c}".ord if c #work out ascii value of key

  if k>0 then
    note="#{k}".to_i #use the hash to get the correspending midi note (if any is mapped)
    if note > 0 then #only process mapped notes
      prepared_command = OSC::Message.new("/kyb", note) #transit the note info as an osc message to /kyb
      client.send(prepared_command)
      puts note #visual feed back in terminal
    end
  end
  sleep 0.01 #short gap
end

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment