What's going on with OSC and Midi?

This is relevant to SPi as I’m trying to control things using SPi as the brain. Not a negative thing about SPi btw, which is the hero here. But getting the comms to work is all string and sticky tape.

I stayed away from electronic music for ages, and now I’m back in I find that MIDI is still there (incredibly) and the newer OSC - which looks so obviously better for the Wifi-connected age - seems to be some specs and a rag bag of implementations. In contrast the SPi implementation is very good, and led me to OSC was the way forward.

The obvious solution - a plug-and-play network hardware device that translates - is mentioned on the OSC website “The Missing Link” but http://opensoundcontrol.org/implementation/missing-link-osc-midi-translator but it’s from 2011 and dead(?)

If anyone knows different - like there is a box on sale out there - or some other solution, please could you let me know?

What I’m thinking now is looking at running an instance of SPi on remote machines simply to take OSC and put out MIDI.

This is precisely what a future version of Sonic Pi is going to make very easy to deploy on cheap hardware such as Raspberry Pis. It’s one of the key benefits that will be obtained from all the recent MIDI system reworking which will feature in v3.3.

1 Like

Excellent news. This what I hacked together just now, seems to work OK. Currently Rpi as the controller, a remote Windows PC as the player, with a VST synth running in a host and accepting MIDI. I’ll try the other way round, but it ought to work - because SPi is the thing that runs anywhere.

I mean, for Pete’s sake, these boxes all have to be on a network and the alternative is a load of midi hardware as well. I did try a usb-4x4-midi box btw. Didn’t work properly anyway.

#Raspberry Pi controller

use_osc "192.168.1.64", 4560

live_loop :conductor do
  sample :sn_generic, amp: 0.3
  notes = scale :c4, :aeolian, num_octaves: 3
  tick_reset
  time_warp 4.0-rt(0.07) do
    16.times do
      osc "/midi", notes.tick, 80, 0.2
      sleep 4.0/16
    end
  end
  sleep 4
end
#Remote Windows PC
live_loop :oscin do
  use_real_time
  n,v,d = sync "/osc:192.168.1.81:4560/midi"
  midi n, v, channel: 1, sustain: d, port: "loopmidi_port"
end

Here’s an example. Sonic Pi running a script which includes a drum machine, some samples and a bit of synth, sending OSC to a remote PC also running SPi just to translate OSC->MIDI which goes to a 3-voice VCV Rack patch. Latency correction about right. You’ll hear the different voices come in and out.

All the note sequencing is done in SPi, just raw notes sent to Rack.

The recording is live, straight off the mixer, one channel for SPi, one for Rack. Into Reaper for just for recording and master limiter.

hi,
Good job ! Clap clap clap.
Some scheme and code available somewhere to have an overall view of your set ? Which programs on which hosts etc ?
cheers

1 Like

Thank you, thank you. It’s hot of the press, so yes I’ll put up some detail in a bit after I’ve calmed down

The exciting thing though is that it’s a general scheme. I can fill the room with low cost/power computers and run all kinds of things on them. I’m thinking Raspberry Pi’s mostly. It’s a culture thing - all this amazing free music software but you tend to need quite expensive Windows or Mac box to run things together on one machine. VCV Rack is great but power hungry

Some of the money not spent on yet-another-laptop can go to a better cause…like the developers!

For this demo I have two PCs. The remote PC is running Spi and VCV Rack. The Spi code simply picks up an OSC message /midi and gets the paramters for note, volume, duration and channel. Then sends out a midi note to the loopmidi_port. These are picked up by a VCV Rack patch and played. The Rack patch has three voices, on channels 1,2 & 3.

#Remote Code
live_loop "osc2midi" do
  use_real_time
  n,v,d,ch = sync "/osc:192.168.1.64:4560/midi"
  midi n, v, sustain: d, channel: ch, port: "loopmidi_port"
end

The controller PC is just running SPi and the code is a bit more involved, mostly because I’m changing chord in each bar, and I need to make sure that the local SPi loops and the remote midi calls get to use the correct chord at the correct time. And that the loop sending OSC is advanced in time to account for the latency. That’s in live_loop :main. Otherwise it’s pretty straightforward, the live_loop external sends out OSC /midi messages

use_bpm 120

roots = [:G4, :G4, :G4, :G4,
         :G4, :G4, :G4, :G4,
         :D4, :D4, :D4, :D4].ring

live_loop :external do
  sync :ext
  
  #Get some notes based on the current root note
  notes = scale get[:extroot]-12, :blues_minor, num_octaves: 2

  #Send OSC/midi notes to the remote PC
  use_osc "192.168.1.3", 4560
  
  in_thread do
    8.times do
      osc "/midi", notes["04005432".ring[look].to_i],127,0.1,1 if ("xxxxxxxx".ring[tick]=="x") ^ one_in(8)
      sleep 0.5
    end
  end
  
  in_thread do
    12.times do
      osc "/midi", notes["34262847".ring[look].to_i],127,0.3,2 if ("x-x-x-xxx".ring[tick]=="x") ^ one_in(0)
      sleep 1.0/3
    end
  end
  
  in_thread do
    4.times do
      osc "/midi", notes["0000".ring[look].to_i],127,0.5,3 if ("-x--".ring[tick]=="x") ^ one_in(0)
      sleep 1
    end
  end
  sleep 4
end

live_loop :main do
  
  root = roots.tick(:root)
  
  time_warp 3-rt(0.07) do
    #Cue the external loop, slightly advanced for latency
    #Set the root note to use, ahead of time so it's ready when the bar starts
    set :extroot, root
    sleep 1
    cue :ext
  end
  
  time_warp 4.0-1.0/32 do
    #Set the current root note, for the local Spi loops to pick up
    set :root, root
  end
  
  sleep 4
  cue :bar
  cue :bar2 if tick%2==0
  cue :bar4 if look%4==0
  set :n, look
  
end

live_loop :arp1 do
  #Play something local
  sync :bar
  notes = scale get[:root], :blues_minor, num_octaves: 2
  notes = [get[:root]].ring
  8.times do
    play notes.tick, amp: 0.2
    sleep 1.0/2
  end
end


live_loop :drums do
  sync :bar
  n = tick(:bar)
  s = [:bd_tek, :elec_cymbal, :mehackit_robot3, :elec_filt_snare]
  with_fx :echo, mix: 0.2, phase: 0.75 do
    in_thread do
      16.times do
        tick
        sample s[0], amp: 0.4 if ("x---x---x---x---"[look]=="x") or one_in(16)
        sample s[1], amp: 0.3 if ("--x---x---x---x-"[look]=="x") or one_in(24)
        sample s[2], amp: 1.0 if ("---x------------"[look]=="x") and n%2==0
        sample s[3], amp: 0.5 if ("------------x---"[look]=="x") and n%4==0
        sleep 1.0/4
      end
    end
  end
end
2 Likes