If you have a problem or need to report a bug please email : support@dsprobotics.com
There are 3 sections to this support area:
DOWNLOADS: access to product manuals, support files and drivers
HELP & INFORMATION: tutorials and example files for learning or finding pre-made modules for your projects
USER FORUMS: meet with other users and exchange ideas, you can also get help and assistance here
NEW REGISTRATIONS - please contact us if you wish to register on the forum
Users are reminded of the forum rules they sign up to which prohibits any activity that violates any laws including posting material covered by copyright
Ruby is more!
10 posts
• Page 1 of 1
Ruby is more!
I once made a series of articles going into details about classes in Ruby, telling you that they are the heart of OOP. I think I was to quick with that. Most people will not really have an idea of object orientation in its entirety.
Of course, you can use Ruby as a one-time solution for a one-time task. That's totally fine:
This code will output 50% of the value that just came in. Nothing more and nothing less. And if you are only looking for such easy and quick solutions, you don't need to read further. These are valid tasks for scripting with Ruby.
But Ruby is so much more. Unfortunately you can't access its real power unless you understand the underlying idea of OOP. This time I will concentrate on this abstract layer and will not explain semantics or syntax. For those you can always go to ruby-doc.org. My examples in the past may have not been "audio" enough. So I will try to make you understand the idea of OOP by using a synthesizer as an image.
So, let us have a look at a synth's oscillators. There may be a sine oscillator, a square osc, a sawtooth and a triangle osc.
You might think of them as 4 objects. And in the analog world they indeed are. But the digital world is different. Let us have a closer look. What does such an oscillator provide? A phase setting, a frequency setting and in our case a sync setting.
If you consider this than there is only one difference to the oscillators: the produced waveform. In the digital world, therefore those oscillators (no matter how many of them you will have) are two objects: One object that has all the settings, and another object that builds on the first one and defines the waveform.
In Ruby it would look similar to the following code.
Note that I only cared about the sine oscillator. This is to keep the code small. A real code would of course have an option to define a waveform and not just hardcode "@waveform = "sine". And the class would not be called "Sine_osc", but probably "Waveform_osc".
Ruby itself works in the very same way. A number like 42 is an instance of the class "Fixnum". But Fixnum is based on "Numeric", a superclass that holds all informations that all numbers share. So, while 42.0 is of class "Float", Float is inherited from "Numeric", just as Fixnum was. If you ever wonder about the class relations, you can check them with the methods .class and .superclass:
There is one class that all the others are inherited from, and that class is "Object". It is the root of a tree, so to speak.
But back to our synth. Can I further inherit? Yes, of course. For example, you might have a class "Osc_section", inherited from "Waveform_osc" and adding tuning (octave, halfnotes, fine). Or adding envelopes. Or... You get the point.
Leaves one question: Why should I use such a tree structure, whith dozen of classes. Well, besides a well readable code, there's one mega-advantage. You only define those things once, and they are true for all instances you use of those classes. Now imagine you have created a synth with 100 oscillators, each featuring its own waveform. Now you encounter an error.
Without the OOP approach you have no choice than finding the error and correcting it in all 100 oscillators.
With the OOP approach you just have to find the error and correct it in the affected class - and all 100 oscillators are bug-free again! Is it cool, or is it cool?
Of course, you can use Ruby as a one-time solution for a one-time task. That's totally fine:
- Code: Select all
output(0, @ins[0] * 0.5)
This code will output 50% of the value that just came in. Nothing more and nothing less. And if you are only looking for such easy and quick solutions, you don't need to read further. These are valid tasks for scripting with Ruby.
But Ruby is so much more. Unfortunately you can't access its real power unless you understand the underlying idea of OOP. This time I will concentrate on this abstract layer and will not explain semantics or syntax. For those you can always go to ruby-doc.org. My examples in the past may have not been "audio" enough. So I will try to make you understand the idea of OOP by using a synthesizer as an image.
So, let us have a look at a synth's oscillators. There may be a sine oscillator, a square osc, a sawtooth and a triangle osc.
You might think of them as 4 objects. And in the analog world they indeed are. But the digital world is different. Let us have a closer look. What does such an oscillator provide? A phase setting, a frequency setting and in our case a sync setting.
If you consider this than there is only one difference to the oscillators: the produced waveform. In the digital world, therefore those oscillators (no matter how many of them you will have) are two objects: One object that has all the settings, and another object that builds on the first one and defines the waveform.
In Ruby it would look similar to the following code.
- Code: Select all
class Oscillator
def initialize
@frequency = 440.0
@phase = 0.0
@sync = false
end
attr_accessor :frequency, :phase, :sync
end
class Sine_osc < Oscillator ## "<" here indicates "inherit from"
def initialize
super ##calls the method with the same name from the class we're inheriting from
##you only need super, if you DON'T want to override the same method of the superclass
@waveform = "sine"
end
attr_accessor :waveform
end
s = Sine_osc.new
s.frequency ## returns 440.0
s.waveform ## returns "sine"
Note that I only cared about the sine oscillator. This is to keep the code small. A real code would of course have an option to define a waveform and not just hardcode "@waveform = "sine". And the class would not be called "Sine_osc", but probably "Waveform_osc".
Ruby itself works in the very same way. A number like 42 is an instance of the class "Fixnum". But Fixnum is based on "Numeric", a superclass that holds all informations that all numbers share. So, while 42.0 is of class "Float", Float is inherited from "Numeric", just as Fixnum was. If you ever wonder about the class relations, you can check them with the methods .class and .superclass:
- Code: Select all
42.0.class ## returns "Float"
42.0.class.superclass ## returns "Numeric"
42.0.class.superclass.superclass ## returns "Object"
There is one class that all the others are inherited from, and that class is "Object". It is the root of a tree, so to speak.
But back to our synth. Can I further inherit? Yes, of course. For example, you might have a class "Osc_section", inherited from "Waveform_osc" and adding tuning (octave, halfnotes, fine). Or adding envelopes. Or... You get the point.
Leaves one question: Why should I use such a tree structure, whith dozen of classes. Well, besides a well readable code, there's one mega-advantage. You only define those things once, and they are true for all instances you use of those classes. Now imagine you have created a synth with 100 oscillators, each featuring its own waveform. Now you encounter an error.
Without the OOP approach you have no choice than finding the error and correcting it in all 100 oscillators.
With the OOP approach you just have to find the error and correct it in the affected class - and all 100 oscillators are bug-free again! Is it cool, or is it cool?
"There lies the dog buried" (German saying translated literally)
- tulamide
- Posts: 2714
- Joined: Sat Jun 21, 2014 2:48 pm
- Location: Germany
Re: Ruby is more!
Thanks, Tulamide, for this new initiative. I have been following your tutorials with great interest, and yet my Ruby skills don't go much further than your first example. The OO paradigm seems to be orthogonal to my thinking. So please do continue, you have at least one faithful fan!
-
martinvicanek - Posts: 1328
- Joined: Sat Jun 22, 2013 8:28 pm
Re: Ruby is more!
Add me to the 'fan' list ...
Having zero formal programming education, I'm watching and trying to learn.
THX GUYS !
Having zero formal programming education, I'm watching and trying to learn.
THX GUYS !
- RJHollins
- Posts: 1571
- Joined: Thu Mar 08, 2012 7:58 pm
Re: Ruby is more!
+1 for me too.
Can I ask what may well be a really stupid question (based on zero knowledge of Ruby)?
Since you mention oscillators can you make oscillators in Ruby?
Cheers
Spogg
Can I ask what may well be a really stupid question (based on zero knowledge of Ruby)?
Since you mention oscillators can you make oscillators in Ruby?
Cheers
Spogg
-
Spogg - Posts: 3358
- Joined: Thu Nov 20, 2014 4:24 pm
- Location: Birmingham, England
Re: Ruby is more!
Spogg wrote:+1 for me too.
Can I ask what may well be a really stupid question (based on zero knowledge of Ruby)?
Since you mention oscillators can you make oscillators in Ruby?
Cheers
Spogg
You can, but they are extremely inefficient, because ruby is interpreted language. One such example is in the manual, if I recall correctly. (a simple sin osc takes like 60-90%cpu on my machine)
It is possible to run raw machine code in flowstone using analyser prim and float-to-mem prim / ruby, but it requires you to literally write your own assembler (myco has done it some time ago) and compiler. Practically you would be running reverse-engineered gui-less flowstone within the actual flowstone. In other words, kind of pointless.
- KG_is_back
- Posts: 1196
- Joined: Tue Oct 22, 2013 5:43 pm
- Location: Slovakia
Re: Ruby is more!
Thanks KG.
So may I assume that Ruby is similarly not suited to DSP and stuff like index counting for wave playing?
Cheers
Spogg
So may I assume that Ruby is similarly not suited to DSP and stuff like index counting for wave playing?
Cheers
Spogg
-
Spogg - Posts: 3358
- Joined: Thu Nov 20, 2014 4:24 pm
- Location: Birmingham, England
Re: Ruby is more!
Hey everyone, thanks for encouraging me. I was starting to think there is no interest.
Spogg,
see it this way: Ruby is superior to green (not necessarily speed-wise, but in organization and maintaining), on par with MIDI and the best solution for everything graphics.
And while it can be used as white (polystream) replacement it is totally impractical for that task. But, it can replace blue (monostream) with an acceptable tradeoff. I posted an example of Ruby doing a stereo peak meter, which involved the calculation of 176400 values per second. A dsp meter in the blue stream would be quicker, but then again Ruby is more comfortable in structuring code.
But basically, whenever you can do white or blue directly, don't use Ruby.
Spogg,
see it this way: Ruby is superior to green (not necessarily speed-wise, but in organization and maintaining), on par with MIDI and the best solution for everything graphics.
And while it can be used as white (polystream) replacement it is totally impractical for that task. But, it can replace blue (monostream) with an acceptable tradeoff. I posted an example of Ruby doing a stereo peak meter, which involved the calculation of 176400 values per second. A dsp meter in the blue stream would be quicker, but then again Ruby is more comfortable in structuring code.
But basically, whenever you can do white or blue directly, don't use Ruby.
"There lies the dog buried" (German saying translated literally)
- tulamide
- Posts: 2714
- Joined: Sat Jun 21, 2014 2:48 pm
- Location: Germany
Re: Ruby is more!
tulamide wrote:I posted an example of Ruby doing a stereo peak meter
Tula, would you share the link please?
-
martinvicanek - Posts: 1328
- Joined: Sat Jun 22, 2013 8:28 pm
Re: Ruby is more!
martinvicanek wrote:tulamide wrote:I posted an example of Ruby doing a stereo peak meter
Tula, would you share the link please?
Sure, here you are
http://www.dsprobotics.com/support/viewtopic.php?f=3&t=5669
"There lies the dog buried" (German saying translated literally)
- tulamide
- Posts: 2714
- Joined: Sat Jun 21, 2014 2:48 pm
- Location: Germany
Re: Ruby is more!
Contrary to the last time, I don't have a plan or something to organize this thread. I just talk without much preparation. Just so that you know
From my last example you should have noticed two important things:
1) You can't just write some code if you want to go the OOP way. You have to lay out your program first. It doesn't matter if you write some notes, draw a flowchart or, as I do it mostly, intensively think it through. It just needs you to be prepared before starting to write code.
2) You can't write OOP code without accepting and writing by the rules. OOP opens up a large canvas for you to paint on, but if you use a pencil instead of a brush you will damage the canvas. Freedom through restriction, so to speak.
Both points are especially important if you might share your code once with other programmers. They will have a look at your code with those rules in mind. If you don't follow the rules, chances are they will misinterpret your code or not understand it at all. Imagine it as speaking in your language with a foreigner but using words that are only used in your area. Chances are the foreigner never heard those words before.
The thing with objects can be irritatring, but once mastered it gives you a powerful weapon at hand to fight issues that you thought impossible to solve with code. For those who are in love with maths: It reminds a lot of Mengenlehre (I think it is called "set theory" in English). Look at this image:
There are two sets, A and B, and the intersection is the part that both sets share. In OOP, this would be 3 objects, with the intersection being the mother object and A and B both inheriting from the mother object, adding the parts of their sets that are not in the intersection.
Fortunately Ruby is very strict when it comes to inheriting. An object can always only inherit from one other object. Let's have a closer look at that.
Straight forward and easy to understand. However, what about a frog? It's an amphibious animal, their young form can only live at the sea (gills), while their adult form can only live at land (lung). Theoretically we would need to inherit from both classes, building the subclass "Amphibious", but that is not provided by Ruby.
That doesn't mean you can't express a frog with Ruby, though. Instead of multi-inheritance (which can become quite confusing), Ruby provides mixins. A mixin is a module that you make part of the class definition. A module is very similar to a class, and of course it is an object, too. But while you create a class and then use instances of it in your code, a module does not have instances. It only exists exactly once throughout Ruby. That makes modules generic. You can't place specific code in a module, since it isn't instantiated.
You may notice that I added a class Animal. That class includes a module. It is using a mixin. Since I used the keyword "include", the module's content will only be available in instances of the animal class. Not in the animal class itself. Confused? Well, I was too in the beginning, but it is easier than you might think.
If you try to call
There's also a keyword to make a module part of the class itself. It's "extend". When you use
You also see that it is very important to understand the difference between a class and a class instance, resp. a class method and a class instance method, or a class variable and a class instance variable. Distinguish classes from their instances. An instance exists as soon as you use :new on a class.
To summarize: If you want to have instances of a class have acces to a mixin, use keyword "include". For accessing the mixin directly from the class use "extend".
'til next time
From my last example you should have noticed two important things:
1) You can't just write some code if you want to go the OOP way. You have to lay out your program first. It doesn't matter if you write some notes, draw a flowchart or, as I do it mostly, intensively think it through. It just needs you to be prepared before starting to write code.
2) You can't write OOP code without accepting and writing by the rules. OOP opens up a large canvas for you to paint on, but if you use a pencil instead of a brush you will damage the canvas. Freedom through restriction, so to speak.
Both points are especially important if you might share your code once with other programmers. They will have a look at your code with those rules in mind. If you don't follow the rules, chances are they will misinterpret your code or not understand it at all. Imagine it as speaking in your language with a foreigner but using words that are only used in your area. Chances are the foreigner never heard those words before.
The thing with objects can be irritatring, but once mastered it gives you a powerful weapon at hand to fight issues that you thought impossible to solve with code. For those who are in love with maths: It reminds a lot of Mengenlehre (I think it is called "set theory" in English). Look at this image:
There are two sets, A and B, and the intersection is the part that both sets share. In OOP, this would be 3 objects, with the intersection being the mother object and A and B both inheriting from the mother object, adding the parts of their sets that are not in the intersection.
Fortunately Ruby is very strict when it comes to inheriting. An object can always only inherit from one other object. Let's have a closer look at that.
- Code: Select all
class Aquatic
def tell
"I live in the sea"
end
end
class Terrestrial
def tell
"I live at land"
end
end
horse = Terrestrial.new
horse.tell ## returns "I live at land"
shark = Aquatic.new
shark.tell ## returns "I live at sea"
Straight forward and easy to understand. However, what about a frog? It's an amphibious animal, their young form can only live at the sea (gills), while their adult form can only live at land (lung). Theoretically we would need to inherit from both classes, building the subclass "Amphibious", but that is not provided by Ruby.
That doesn't mean you can't express a frog with Ruby, though. Instead of multi-inheritance (which can become quite confusing), Ruby provides mixins. A mixin is a module that you make part of the class definition. A module is very similar to a class, and of course it is an object, too. But while you create a class and then use instances of it in your code, a module does not have instances. It only exists exactly once throughout Ruby. That makes modules generic. You can't place specific code in a module, since it isn't instantiated.
- Code: Select all
module Teller
def tell
myclass = self.class
if myclass == Terrestrial
"I live at land"
elsif myclass == Aquatic
"I live in the sea"
elsif myclass == Amphibious
"I live at sea when young and at land when adult"
else
"Unsure"
end
end
end
class Animal
include Teller
end
class Aquatic < Animal
end
class Terrestrial < Animal
end
class Amphibious < Animal
end
horse = Terrestrial.new
horse.tell ## returns "I live at land"
shark = Aquatic.new
shark.tell ## returns "I live at sea"
frog = Amphibious.new
frog.tell ## returns "I live at sea when young and at land when adult
You may notice that I added a class Animal. That class includes a module. It is using a mixin. Since I used the keyword "include", the module's content will only be available in instances of the animal class. Not in the animal class itself. Confused? Well, I was too in the beginning, but it is easier than you might think.
If you try to call
- Code: Select all
Animal.tell
There's also a keyword to make a module part of the class itself. It's "extend". When you use
- Code: Select all
extend Teller
You also see that it is very important to understand the difference between a class and a class instance, resp. a class method and a class instance method, or a class variable and a class instance variable. Distinguish classes from their instances. An instance exists as soon as you use :new on a class.
- Code: Select all
trump = Animal.new
- Code: Select all
trump.tell
- Code: Select all
Animal.tell
To summarize: If you want to have instances of a class have acces to a mixin, use keyword "include". For accessing the mixin directly from the class use "extend".
'til next time
"There lies the dog buried" (German saying translated literally)
- tulamide
- Posts: 2714
- Joined: Sat Jun 21, 2014 2:48 pm
- Location: Germany
10 posts
• Page 1 of 1
Who is online
Users browsing this forum: Google [Bot] and 53 guests