[chuck-users] OOP and Style Qs

Veli-Pekka Tätilä vtatila at mail.student.oulu.fi
Fri Mar 16 18:55:45 EDT 2007


Hi,
I have a number of relatively newbie Qs on my first-ever ChucK class I've 
written. the good thing is that it works, although it sounds pretty 
horrible. The bad thing is that I wish the ChucK language would have more 
features and might have discovered a bug related to null pointers.

My idea in this class is to emulate the functionality of the various audio 
and event switch modules as well as the audio relay module in Reaktor. That 
is n inputs and one output such that you can select which input is patched 
to the output at any given time.

The questions are:

1. Is the code in the class OK in terms of style and implementation i.e. is 
there something highly uncanonical in naming or some things that could be 
done smarter for such a simple thing?

- In particular, I'm not sure if the gain module is needed in the output.

- Is there a better way to grow the array on demand? In Perl I can just 
assign to higher indeces and it will grow, or use the push function to add 
an arbitraryly sized list at the end. And Java has array lists, too.

- ALso in the test script, I would like to initialize my UGen array 
anonymously without having to make up variable names for each object and 
manully assign the indeces, I'm lazy  I guess.

- Is there a style manual for ChucK? Until we can inherit from UGen it would 
be great to have a common set of method names for UGen like modules. 
Currently I've drawn my influences from Java and Dinky.ck.

- I've noticed that the names for most chuck properties are extremely short, 
for example.

- Which reminds me, does chucK support duck typing? Abstractly put,
Suppose a class or interface c with the method name m and an object o such 
that not o instanceof c. Does o.m() get called in its own class, even if o 
is not related to c by inheritance. Polymorphism that works based on a name 
is there in Perl and Python, I think, where as Java is stricter and requires 
that the object be a subclass or interface implementation of c, a common 
method name is not good enough.

2. How is ChucK naming like?
Suppose we have a parameter p of type t. I would like to create its 
accessors as:

- function void p(t i); // set
  function t p(); // get
Yet although chucK supports overloading functions, it does not let them 
differ only by return type, right? In a test class I get stuff like:

[test.ck]:line(5): function signatures differ in return type...

The same thing with JAva really and I do understand it could often be 
ambiguous. Perl is one of the few languages in which this works well as 
scalar is the only primitive scalar type and it has list and void context to 
make things more or less unambiguous. But ChucK is not Perl, nough said.

- As I think parameters with the verbs get and set are slightly redundant, 
if boolean stuff starts with e.g. is , I thought I would use:
function t p();
function setP(t i);
However, if there is already a data member named p chucK does not let me hav 
a member function named p in the same class, right? After modding my test 
class, I got:

[test.ck]:line(4): function name 'p' is already used by another value

3. To save some duplicated code, and as there's no constructor support, it 
occurred to me I could write a function that given an array of UGens, 
instanciates my audio switch (OneOfN), populates it and returns a reference. 
Yet if I paste this at the top of my test script and try to run it ChucK 
claims UGen doesn't have the caps method, even though printing out the 
function argument says it is of type UGen[], that is an array. What might be 
wrong, i.e. why does it think an array of UGen is just a UGen rather than an 
array? I'm mightily confused here. The code:

// Partially untested.
function OneOfN newOneOfN(UGen choices[])
{ // A pseudo-constructor for populating instances of OneOfN given an array.
   OneOfN selector;
   choices.caps() => int max; // Cache it, so only one method call.
   selector.setInputCount(max);
   for(0 => int i; i < max; ++i)
      selector.setInputAt(i, choices[i]);

   if(max > 0) // Array not empty.
      selector.selectInput(0);
   return selector;
} // function

4. When I use my audio switch to determine whether audio flows through, i.e. 
as an audio relay one of whose inputs is practically disconnected (I've 
patched blackhole there), it seems to work OK i.e. I get the sound of 
silence when expected. However, if I change my array initializer in 
OneOfN.ck:

from:

blackhole @=> newIn[i]; // Not connected initially.

to

null @=> newIn[i]; // Not connected initially.

Strange things start to happen. I don't get a null pointer exception at 
runtime as I would expect in my test script, as the unconnected input is 
actually null. In stead ChucK crashes spectacularly with the standard 
WIndows XP dialog about it. This happens at runtime, when the disconnected 
input is picked the first time. ChucK reports the version as:

C:\audio\CHUCK-~1.7-E\bin>chuck.exe --version

chuck version: 1.2.0.7b (dracula)
   exe target: microsoft win32
   http://chuck.cs.princeton.edu/

[Speaking of Dracula, I'm just reading Frankenstein, talk about funny 
co-incidences. Hmm what's the code name for the next release?]

I think these were my questions. As I tend to have a bad habbit of asking 
all too much, when someone who knows far more than I do is around, I'd 
actually be pleased if even half of these would get answered eventually, 
<smile>. And now finally the code:

public class OneOfN
{ // A parameterizable audio multiplexer (switch). Todo: write user docs,
   -1 => int n; // Initially invalid.
   UGen in[];
   Gain out; // Neede some Ugen for producing output?

   function void connect(UGen output) { out => output; }
   function void setInputAt(int i, UGen u) { u @=> in[i]; }
   function UGen getInputAt(int i) { return in[i]; }
   function UGen getOutput() { return out; }
   function int getSelectedInput() { return n; }
   function int getInputCount() { return in.cap(); }
   function void setSelectedInput(int i)
   {
      if(n > -1)
         in[n] =< out; // Disconnect only if size already set.
      i => n; // We have a new output.
      in[n] => out;
   } // function

   function void setInputCount(int newSize)
   { // Changes the size of the input array to accommodate that many inputs.
      UGen newIn[newSize];
      for(0 => int i; i < newSize; ++i)
         blackhole @=> newIn[i]; // Not connected initially.
      newIn @=> in;     // Not garbage collected, there's one ref left.
   } // function
} // class

And now the test script called patch.ck:
I run it by saying

C:\audio\CHUCK-~1.7-E\code>..\bin\chuck.exe OneOfN.ck patch.ck

The code:

// Use the switch to select one input and save CPU.
OneOfN oscSelect;
UGen @oscs[3];
// Our oscs.
SinOsc o1 @=> oscs[0];
SawOsc o2 @=> oscs[1];
SqrOsc o3 @=> oscs[2];
oscSelect.setInputCount(oscs.cap());
// Tweak the gains to make an audible difference.
for(0 => int i; i < oscs.cap(); ++i)
{
   oscs[i].gain(1.0 / (i + 1)); // Avoid divide by 0!
   oscSelect.setInputAt(i, oscs[i]);
} // for
// Use the switch as an audio relay and do the patching.
OneOfN audioRelay;
audioRelay.setInputCount(2); // 1 real, 1 empty.
audioRelay.setInputAt(0, oscSelect.getOutput());
audioRelay.connect(dac);
// Times, they keep on changing.
for(0 => int i; true; ++i)
{
   Math.rand2(-10, 10) => int chance;
   oscSelect.setSelectedInput(i % oscs.cap()); // CYcle waveform.
   audioRelay.setSelectedInput(chance < 5 ? 0 : 1); // 1 is connected to 
GND0.
   50::ms => now;
} // for

-- 
With kind regards Veli-Pekka Tätilä (vtatila at mail.student.oulu.fi)
Accessibility, game music, synthesizers and programming:
http://www.student.oulu.fi/~vtatila/ 



More information about the chuck-users mailing list