How to add a new command?

Digging around in

\Sonic Pi\app\server\ruby\lib\sonicpi

I see the .rb for scale.rb, chord.rb, and note.rb which seems very promising to use to make a guitar chord class.

How would I go about adding a class that can be called from sonic pi? I tired making a test class based on chord.rb but there didn’t seem to be a way I noticed to call it from sonic pi. Do I need to compile or am I missing something in the ruby files to be able to do this?

My main goal is to add a guitar chord method. Similar to how chord works.

I’m thinking (gchord :c, :major) for example which would spit out a ring.

Why would anyone want this?

(1) It’s cool.
(2) A piano chord and guitar chord are not the same even if you use a guitar synth.
(3) It’s really cool to use has a midi control for your daw. Imagine being able to do something like a chord_degree and set your scale and choose a progression on the fly! A massive game changer over painting notes I would say!

Anyway one thing at a time.

Does anyone know how to set up a rb class that I could use has a new command. This way I can poke at the classes that already exist which would be massively better then writing something into a buffer.

BTW: The closest thing I’ve found to what I’m trying to do is this:

# Guitar Strumming - by Emlyn
# This tries to work out the guitar (or ukulele etc.) fingering for arbitrary chords (and tuning).
# It seems to work reasonably well for basic chords, but is quite naive and probably makes many mistakes.
# Ideas, bug reports, fixes etc. gratefully received, just comment below, or tweet @emlyn77.
# Feel free to make use of this code as you like (with attribution if you feel like it, but you don't have to).
# Thanks to @Project_Hell_CK for fixing the tuning, and spotting that it gets chord(:f, :major) not quite right.

# Next note higher or equal to base note n, that is in the chord c
define :next_note do |n, c|
  # Make sure n is a number
  n = note(n)
  # Get distances to each note in chord, add smallest to base note
  n + (c.map {|x| (note(x) - n) % 12}).min
end

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

# Return ring representing the chord chrd, as played on a guitar with given tuning
define :guitar do |tonic, name, tuning=guitar_standard|
  chrd = (chord tonic, name)
  # For each string, get the next higher note that is in the chord
  c = tuning.map {|n| next_note(n, chrd)}
  # We want the lowest note to be the root of the chord
  root = note(chrd[0])
  first_root = c.take_while {|n| (n - root) % 12 != 0}.count
  # Drop up to half the lowest strings to make that the case if possible
  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.1|
  in_thread do
    play_pattern_timed c.drop_while{|n| [nil,:r].include? n}, d
  end
end

use_debug false
use_bpm 120

live_loop :guit do
  #chords = ring((guitar :d, :m7), (guitar :g, '7'), (guitar :c, :M7), (guitar :f, :M7),
  #              (guitar :b, :m7), (guitar :e, '7'), (guitar :a, :m7))
  chords = ring((guitar :a, :m), (guitar :c, :M), (guitar :d, :M), (ring :r, :r, 53, 57, 60, 65),
                (guitar :a, :m), (guitar :c, :M), (guitar :e, :M), (guitar :e, '7'))
  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.03
            end
          end
          sleep 0.5
        end
      end
    end
  end
end

For my build with a little tweak below runs right out of the box.

define :strum do |c, d=0.1|
  in_thread do
    c.each do |n|
      if n != :r
        puts note_info(n)
        play n
        sleep d
      end
      ##| play_pattern_timed c.drop_while{|n| [nil,:r].include? n}, d
    end
  end
end

Thoughts?

1 Like

Hey Din,
You may find the following topic relevant…

1 Like

Yes I was aware of the class trick you can do in buffer.

But I guess I didn’t make it clear enough what I was asking.

My naive assumption was that you could just write a new ruby file in the lib folder and with the correct imports and sonic pi would be able to start up and recognize your commands.

I’m saying I don’t know enough about the way sonic pi knows what class means what command when running the GUI part. So my questions was if I wanted to add a new class with the api to the lib part of sonic pi as if it was like any other class like chord or chord_degree how would I do it?

I’m guessing you’ve already done all the work to add it to the Ruby server code. For an alternate chord-like function, that would mean creating the new class file to go along side the existing one, and inserting the appropriate import and API function definition into app/server/ruby/lib/sonicpi/lang/western_theory.rb.

In order to then make the function name auto-complete when you start typing the name of it into the editor, you also need to edit some files for the C++ GUI. As far as I can remember, this would involve adding a reference to the new type of object that needs to have autocomplete abilities into app/gui/qt/utils/scintilla_api.h and keyword entries into the corresponding app/gui/qt/utils/scintilla_api.cpp, the same way that was done for chords. (You’d need to recompile the app afterwards of course).