I’ve got my synth setup properly and Sonic Pi seems to be logging information sent from my synth when I push keys. However I was wondering how to go about sending the BPM of my project to my synth so I can chain all my synths together and maintain the same BPM as my project.
On another note I’d like to know how to receive the BMP from my synth and alter it in my project so I can control the BPM externally with a knob on the synth?
Hi there,
as far as I’m aware, the MIDI message protocol has no notion of BPM, rather it uses pulses (clock ticks) to advance time - 24 pulses per quarter note.
Out of interest, how were you imagining sending the BPM from your external synth? Is this something you can do with other software?
Hmm there might be a way to do it with sending/receiving the pulse signal.
I don’t think this is something I can do with other software because I want to directly control the bpm in Sonic Pi. I imagine when I turn a knob on my synth I can have it send a MIDI message to Sonic Pi and get it to update the BPM based on the change in pulse rate. I understand maybe this will be difficult because I’ll need the code to keep updating as if I’m continually pushing the “run code button.”
Alternatively I’d also be just as happy if I could send the MIDI pulse data to my synth and use Sonic PI to control the synth’s BPM every time I click “run code.” This may be easier to pull off I’m just not sure if there’ll be a delay between Sonic Pi and my synth, I guess I’ll have to test it out.
The pulse signal is the way to temporally sync MIDI devices.
The easiest thing to do would be to send out a MIDI clock signal to your external synth. Check out the fn midi_clock_beat
which will send 1 beats worth of pulses (24 in total) in the current BPM over MIDI. You’d just need to place this in a live loop:
live_loop :midi_clock do
midi_clock_beat
sleep 1
end
Note that this will send midi clock pulses to all connected MIDI devices. Check the docs for the midi_clock_beat
for examples on how to send to a single specific device, MIDI port or MIDI channel.
Alternatively, you can indeed turn a knob on your synth and have it affect the BPM within Sonic Pi. To do this, you need to first realise that Sonic Pi has no global BPM. Each thread (or live loop) has its own independent BPM. Also, you need to know that you can’t change the BPM of a thread externally - in other words, thread A can’t change the BPM of thread B. A thread can only change its own BPM via the use_bpm
fn. You will have to ask for the most recent MIDI value via the get
fn and then convert it into a BPM and use that value. For example:
live_loop :foo do
n, v = get "/midi/**/control_change"
bpm = v * 2. # bpm is now the range 0 -> 255
use_bpm bpm
# insert rhythmic code here!
end
Note that this will introduce latency between knob changes and BPM changes for 2 reasons:
- You have to wait for your live loop to spin round before you change the BPM (if your live loop spins round every 8 beats, you’ll have to wait up to 8 beats to hear the change)
- Given that this live loop is doing time-critical things, it schedules all events 0.5s ahead of time. There will therefore be a 0.5s latency between knob twiddling and BPM change. This can be reduced by lowering the schedule ahead time using the
set_sched_ahead_time!
to use a lower value than 0.5. Note, that on slower systems this might introduce timing issues.
Hope that this helps.
2 Likes
Okay thanks that should accomplish what I want. I’ll likely opt for using a Sonic Pi thread as the master BPM and send it out to my synth to avoid latency issues.
1 Like
When I try the second example, I get an error:
Syntax Error: [buffer 1, line 4]
syntax error, unexpected tIDENTIFIER, expecting keyword_end
[Line 4]: use_bpm bpm
Why is that? Would be great if this would work!
I think there is a wayward . in the previous line 3 . Remove that and should work better.
live_loop :foo do
n, v = get "/midi/**/control_change"
bpm = v * 2 # bpm is now the range 0 -> 255
use_bpm bpm
# insert rhythmic code here!
end
(It will still have a doesn’t sleep error until you insert to further code as prompted).
1 Like
@robin.newman Cool, many thanks for the correction; I might have figured that out for myself…