# Link sonic pi to Python

**URL:** <https://in-thread.sonic-pi.net/t/link-sonic-pi-to-python/2938>\
**Category:** Support, Help & Resources\
**Created:** [November 19, 2019, 9:44am UTC](https://in-thread.sonic-pi.net/t/link-sonic-pi-to-python/2938 "2019-11-19T09:44:33Z")\
**Posts on this page:** 7\
**Page:** 1

<div class="post-metadata">

**Author:** ![Gydhia](https://avatars.discourse-cdn.com/v4/letter/g/7bcc69/32.png) [@Gydhia](https://in-thread.sonic-pi.net/u/Gydhia)\
**Post date:** [November 19, 2019, 9:44am UTC](https://in-thread.sonic-pi.net/t/link-sonic-pi-to-python/2938/1 "2019-11-19T09:44:34Z")

</div>

Hello !  
I’m working on a university project which is to create a python project during our english classes. We oriented this project as a music video-game (like Voez) and I recently discoverer Sonic Pi then learned to use it.  
The thing is that the video game is coded in Python 3 and the music on Sonic Pi. Is there a way to link both (I’ve done randomly created musics) ?

Thank you !

---

<div class="post-metadata">

**Author:** ![perpetual\_monday](https://dub1.discourse-cdn.com/flex017/user_avatar/in-thread.sonic-pi.net/perpetual_monday/32/955_2.png) [@perpetual\_monday](https://in-thread.sonic-pi.net/u/perpetual_monday)\
**Post date:** [November 19, 2019, 3:03pm UTC](https://in-thread.sonic-pi.net/t/link-sonic-pi-to-python/2938/2 "2019-11-19T15:03:41Z")

</div>

Hi @Gydhia, welcome!

“link both” could probably mean several things, but I’ll assume you mean having Sonic Pi play sounds in response to game events. A couple possibilities:

- [A Python interface for Sonic Pi](https://pypi.org/project/python-sonic/) I have not used this and cannot vouch for the quality. You might be able to port your to Python code using this and have it all run from your game
- OSC / MIDI communication - You can have your Python program send messages to Sonic Pi via one of the communication protocols. I’d choose [OSC](https://sonic-pi.net/tutorial.html#section-12) because it is more general purpose. Then you could [send OSC messages from your game](https://pypi.org/project/python-osc/), receive them in Sonic Pi, and map them to audio actions like starting a loop, playing a melody / sample, etc.

Personally, I’d probably avoid the Python interface even though I’m a longtime professional Python coder. You might run into weird bugs or edge case issues and spend all your time fighting those. But if you try it and it works I’d be glad to hear about your experience! Using OSC, you should be able to get up and running with only a few lines of code on each side.

---

<div class="post-metadata">

**Author:** ![robin.newman](https://avatars.discourse-cdn.com/v4/letter/r/76d3ee/32.png) [@robin.newman](https://in-thread.sonic-pi.net/u/robin.newman)\
**Post date:** [November 19, 2019, 5:26pm UTC](https://in-thread.sonic-pi.net/t/link-sonic-pi-to-python/2938/3 "2019-11-19T17:26:33Z")

</div>

I agree with **perpetual\_monday** that OSC is the way to go. I have done several projects to link python based projects to sonic pi using OSC messaging.  
Take a look at [https://rbnrpi.wordpress.com/project-list/a-musical-12-tone-alarm-for-the-raspberry-pi-part-1/12-input-touch-keyboard-for-sonic-pi/](https://rbnrpi.wordpress.com/project-list/a-musical-12-tone-alarm-for-the-raspberry-pi-part-1/12-input-touch-keyboard-for-sonic-pi/)

and at [https://rbnrpi.wordpress.com/2017/07/31/control-sonic-pi-3-with-a-ps3-wireless-controller/](https://rbnrpi.wordpress.com/2017/07/31/control-sonic-pi-3-with-a-ps3-wireless-controller/)

In both cases, inputs dreived from external devices connected to a python program are used to send OSC messages to Sonic Pi where they are used to produce sounds.

---

<div class="post-metadata">

**Author:** ![Gydhia](https://avatars.discourse-cdn.com/v4/letter/g/7bcc69/32.png) [@Gydhia](https://in-thread.sonic-pi.net/u/Gydhia)\
**Post date:** [December 3, 2019, 9:53am UTC](https://in-thread.sonic-pi.net/t/link-sonic-pi-to-python/2938/4 "2019-12-03T09:53:45Z")

</div>

Thank a lot !  
I come back to you because I checked the OSC and tried to make it work with my python program. The thing is that I’m not used in the projects that require a “link” between 2 differents langages. perpetual\_monday, you were right about what I wanted !  
Basically, the game will begin with 80BPM and will increase depending on the actions. What I want is to send the BPM of my python game to my sonic pi music to adjust both of them (or from music pi to python, doesn’t matter). But even by checking your projects I’m kinda lost about what to do to link the 2 programs ☹  
They would be on the same folder, what do I need to launch them in the same time and make them speak between each other ?

Thank you for your help !

---

<div class="post-metadata">

**Author:** ![perpetual\_monday](https://dub1.discourse-cdn.com/flex017/user_avatar/in-thread.sonic-pi.net/perpetual_monday/32/955_2.png) [@perpetual\_monday](https://in-thread.sonic-pi.net/u/perpetual_monday)\
**Post date:** [December 3, 2019, 9:26pm UTC](https://in-thread.sonic-pi.net/t/link-sonic-pi-to-python/2938/5 "2019-12-03T21:26:02Z")

</div>

@Gydhia

Here is a simple example with a Python client sending OSC messages to Sonic Pi. First, the Python client:

```ruby
import random
import time

from pythonosc import udp_client

client = udp_client.SimpleUDPClient('127.0.0.1', 4559)

while True:
    client.send_message("/bpm", random.randint(40, 200))
    time.sleep(5)

```

The Python code establishes a client connection to the Sonic Pi OSC server which runs on port `4559` on my localhost (`127.0.0.1`). OSC is a network protocol, so it doesn’t matter where the program runs from; only a UDP connection is needed, and the `python-osc` library provides that. Next, I enter an infinite loop which will send OSC messages called `bpm` every 5 seconds. The `bpm` label was arbitrary - you can name your messages whatever you want, but that name / key is what you’ll use to gather data. The `bpm` message is sending along a single randomly chosen number between 40 - 200 which I’ll use for the BPM.

The Python program can be run at this point and it will start sending UDP messages to port 4559. It doesn’t matter at this point if Sonic Pi is running, just that the messages won’t get received by anything.

Here is an example Sonic Pi code that will alter the BPM of a metronome as it receives messages:

```ruby
# Initial BPM
set :bpm, 120

live_loop :receiver do
  bpm_data = sync "/osc/bpm"
  set :bpm, bpm_data[0]
end

live_loop :metronome do
  with_bpm get(:bpm) do
    sample :elec_ping if [1,0,1,0,1,0,1,0].ring.tick == 1
    sleep 0.5
  end
end

```

Make sure your Sonic Pi is set to be an OSC server in settings, run the above, and you should see / hear the metronome changing tempo every 5 seconds.

> what do I need to launch them in the same time and make them speak between each other ?

It sounds like you probably want a ‘one-click’ solution where a user could launch the Python script and Sonic Pi with a single action? There are different ways you could do this - for example via a shell/bash script, or you could launch Sonic Pi from within your Python program. However, you’d likely need a buffer or few to execute after startup and I’m not sure the best way to accomplish that programmatically, but others will probably have some ideas. Not that you want to add another tool (from another language) into your setup, but there is a project that might suit you well for starting the Sonic Pi server and sending initial code to execute: [GitHub - lpil/sonic-pi-tool: 🎻 Controlling Sonic Pi from the command line](https://github.com/lpil/sonic-pi-tool)

---

<div class="post-metadata">

**Author:** ![Gydhia](https://avatars.discourse-cdn.com/v4/letter/g/7bcc69/32.png) [@Gydhia](https://in-thread.sonic-pi.net/u/Gydhia)\
**Post date:** [December 17, 2019, 10:30am UTC](https://in-thread.sonic-pi.net/t/link-sonic-pi-to-python/2938/6 "2019-12-17T10:30:20Z")

</div>

> [@perpetual\_monday](#):
>
> 0

Perfect ! It works really great, thank you a lot !  
There’s only one thing that doesn’t work :  
When I do  
`set :bpm, 80`  
or whatever speed it is, it does nothing. There’s always a 60BPM that can’t change. Where could the problem come from ?

Here the entire code :

```
set :bpm, 200
switch = 0

live_loop :receiver do
  bpm_data = sync "/osc/bpm"
  set :bpm, bpm_data[0]
end

define :next_note do |n, c|
  n = note(n)
  n + (c.map {|x| (note(x) - n) % 12}).min
end

in_thread(name: :drum_machine) do
  
  # choose your kit here (can be :acoustic, :acoustic_soft, :electro, :toy)
  use_kit :acoustic
  
  # program your pattern here - each item in the list represents 1/4 of a beat
  # for each item, enter a number between 0 and 9 (0=silent,9=loudest)
  
end

drum_kits = {
  acoustic: {
    hat: :drum_cymbal_closed,
    kick: :drum_bass_hard,
    snare: :drum_snare_hard
  },
  acoustic_soft: {
    hat: :drum_cymbal_closed,
    kick: :drum_bass_soft,
    snare: :drum_snare_soft
  },
  electro: {
    hat: :elec_triangle,
    kick: :elec_soft_kick,
    snare: :elec_hi_snare
  },
  toy: {
    hat: :elec_tick,
    kick: :elec_hollow_kick,
    snare: :elec_pop
  }
}
current_drum_kit = drum_kits[:acoustic]

ukulele = [:g, :c, :e, :a]
guitar_standard = [:e2, :a2, :d3, :g3, :b3, :e4]

define :guitar do |tonic, name, tuning=guitar_standard|
  chrd = (chord tonic, name)
  c = tuning.map {|n| next_note(n, chrd)}.ring
  root = note(chrd[0])
  first_root = c.take_while {|n| (n - root) % 12 != 0}.count
  if first_root > 0 and first_root < tuning.count / 2
    c = (ring :r) * first_root + c.drop(first_root)
  end
  #Display chord fingering
  #puts c.zip(tuning).map {|n, s| if n == :r then 'x' else (n - note(s)) end}.join, c
  c
end

# Strum a chord with a certain delay between strings
define :strum do |c, d=0.0625|
  in_thread do
    play_pattern_timed c.drop_while{|n| [nil,:r].include? n}, d
  end
end

use_debug false

live_loop :guit do
  
  chords = ring((guitar sca=:c, :M), (guitar :e, :m), (guitar :a, :m), (guitar :f, :M))
  with_fx :reverb do
    with_fx :lpf, cutoff: 115 do
      with_synth :pluck do
        tick
        "D.DU.UDU".split(//).each do |s|
          if s == 'D' # Down stroke
            strum chords.look, 0.05
          elsif s == 'U' # Up stroke
            with_fx :level, amp: 0.5 do
              strum chords.look.reverse, 0.03125
            end
          end
          sleep 0.5
        end
      end
    end
  end
end

define :normalDrum do
  hat [5, 0, 5, 0, 5, 0, 5, 0, 5, 0, 5, 0, 5, 0, 5, 0]
  kick [9, 0, 9, 0, 0, 0, 0, 0, 9, 0, 0, 3, 0, 0, 0, 0]
  snare [0, 0, 0, 0, 9, 0, 0, 2, 0, 1, 0, 0, 9, 0, 0, 1]
end

define :breakDrum do
  hat [5, 0, 0, 5, 5, 0, 5, 0, 5, 0, 5, 0 , 5, 0, 5, 0]
  kick [9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9]
  snare [0, 0, 3, 5, 0, 3, 5, 0, 3, 3, 4, 4, 5, 5, 7, 0]
end

define :use_kit do |kit_name|
  current_drum_kit = drum_kits[kit_name]
end

define :hat do |pattern|
  run_pattern :hat, pattern
end

define :kick do |pattern|
  run_pattern :kick, pattern
end

define :snare do |pattern|
  run_pattern :snare, pattern
end

normalDrum()

live_loop :pulse do
  sleep 0.05
end

define :floating_bass do
  use_synth :piano
  control play 60, note: scale(:a4, :minor_pentatonic).choose
  sleep 1.5
end
define :floating_melody do
  use_synth :piano
  control play 60, note: scale(:a3, :minor_pentatonic).choose
  sleep 0.5
end

define :run_pattern do |name, pattern|
  live_loop name do
    if switch < 3
      normalDrum()
      switch = switch + 1
    else
      breaklDrum()
      switch = 0
    end
    
    sync :pulse
    
    pattern.each do |p|
      sample current_drum_kit[name], amp: p/9.0
      sleep 0.25
    end
  end
end

live_loop :cool do
  floating_bass()
  floating_melody()
end

```

---

<div class="post-metadata">

**Author:** ![perpetual\_monday](https://dub1.discourse-cdn.com/flex017/user_avatar/in-thread.sonic-pi.net/perpetual_monday/32/955_2.png) [@perpetual\_monday](https://in-thread.sonic-pi.net/u/perpetual_monday)\
**Post date:** [December 17, 2019, 3:53pm UTC](https://in-thread.sonic-pi.net/t/link-sonic-pi-to-python/2938/7 "2019-12-17T15:53:09Z")

</div>

Hi @Gydhia,

There’s one more dot to connect. `set :bpm` is setting a custom variable - and maybe you won’t actually need that one at all. To change the BPM in Sonic Pi there’s a couple ways that I know of: `use_bpm` and `with_bpm`. I used `with_bpm` in my example, which I see now kind of obscures the intent. You could do this for example:

```ruby
use_bpm 200 # initial BPM
live_loop :receiver do
  bpm_data = sync "/osc/bpm"
  # immediately change BPM using the OSC value
  use_bpm bpm_data[0]
end

```
