Using functions from lang/core.rb in a ruby class


#1

I want to write a class that has a function which uses in_thread, like so:

class MyClass
  def myFn
    in_thread do
      puts "test"
      play 60
    end
  end
end

x = MyClass.new
x.myFn

but it gives me this error:

Runtime Error: [buffer 6, line 11] - NoMethodError
Thread death!
undefined method `in_thread’ for #<SonicPi::RuntimeMethods::MyClass:0x26cdf60>

I know that in_thread and other functions like play are defined in lang/core.rb.
How can I access these functions from within a class?

Thanks in advance,
Ole

PS:
I’m a C# programer, but I’m completly new to ruby and sonic pi.
Today I learned what a &block parameter is


#2

Hi Ole,

the short answer is that this isn’t supported. Sonic Pi isn’t a standard Ruby library, it’s a DSL built on top of Ruby. Whilst many aspects of Ruby are available to Sonic Pi, only the code described in the tutorials is officially supported.


#3

Thanks Sam, for the quick answer :blush: .

For people that want to do this nevertheless:
I found out, that you can do this:

class MyClass
  def initialize(play, in_thread)
    @play = play
    @in_thread = in_thread
  end
  
  def myFn
    @play.call 70
    @in_thread.call do
      @play.call 60
    end
  end
end


x = MyClass.new(lambda {|*args| play *args}, lambda {|*args, &block| in_thread *args, &block})
x.myFn

However that is not elegant, because you must pass every function, you want to use, to the new function.


#4

Sam’s been pretty vocal about not wanting Sonic Pi to head into an object oriented direction. Still, for people who are really familiar with Ruby it feels weird to be locked into the procedural model of the Sonic Pi DSL.

Though it’s not supported, I do this all the time and I’ve developed a pretty elegant way of going about using my own classes as libraries.

You can really shoot yourself in the foot with this approach, especially with threads, so if you don’t know ruby in and out, avert your gaze.

This is how I do it:

# Usually, put this defonce in a file like "/path/to/my/doorbell.rb"
# You must set override to true to make changes

defonce :use_doorbell, :override => false do
  class Doorbell
    def initialize(amp=1.0)
      @amp = amp
    end
    
    def self.set_runtime(rt)
      @@runtime = rt
    end
    
    def runtime
      @@runtime
    end
    
    def ring
      runtime.with_synth :pretty_bell do
        runtime.play 67, amp: @amp
        sleep 0.75
        runtime.play 60, amp: @amp
      end
    end
  end
  
  Doorbell.set_runtime(self)
end

# Usually load the library from the file
# run_file "/path/to/my/doorbell.rb"

use_doorbell

Doorbell.new(0.7).ring