ChucK

 LiCK  Library for ChucK

Summary

LiCK, a Library for ChucK, was born out of frequent requests on the chuck-users mailing list to have a shared repository for various bits of reusable ChucK code.

 LiCK currently provides

  • int, float, and Object Lists
  • Functor classes
  • Interpolating functions
  • Composite procedures for building loops
  • An Assert class for writing unit tests

Download and installation

To download LiCK, visit the LiCK repository page on GitHub

http://github.com/heuermh/lick

Use git to clone to a local repository, or use the download link for a zip archive or a tarball.

To install and use LiCK in your own ChucK scripts, use the import.ck script

$ chuck --loop &

$ chuck + import.ck
...
"LiCK imported." : (string)

$ chuck + my-script.ck

If you don't want to include all of LiCK, use Machine.add(file name) to include individual LiCK source files.  Do be careful to include the LiCK source files of dependencies as well.

Machine.add("FloatFunction.ck");
Machine.add("Interpolation.ck");
Machine.add("ExponentialOut.ck");

Hopefully, a future version of ChucK will support a proper include and namespace mechanism, simplifying the use of external libraries like LiCK.

Contributing to LiCK

LiCK is welcome to any contributions! Don’t worry too much about style or formatting, that can all be worked out later.

Please add the license header (HEADER.txt) to the top of each file and provide an author statement if you wish.

If you add classes to LiCK, be sure to update import.ck with the new classes and their dependencies in the correct order. If you have unit tests for those classes, be sure to update tests.ck with the new unit tests.

Any suggestions as to where examples should live in the repository are also welcome.

int, float, and Object Lists

For those more comfortable with Smalltalk, C#, or Java-style collections than arrays, LiCK provides int, float, and Object Lists.

Lists are created and sized in manner similar to that of ChucK arrays

// create a new object list
ArrayList list;

// initially the size of the list is zero
Assert.assertTrue(0, list.size());
Assert.assertTrue(list.isEmpty());

// pass an argument to the size method to resize the array
list.size(16);
Assert.assertTrue(16, list.size());
Assert.assertFalse(list.isEmpty());

list.clear();
Assert.assertTrue(0, list.size());
Assert.assertTrue(list.isEmpty());

// or add elements to the list to resize it dynamically
Object foo;
list.add(foo);
Assert.assertTrue(1, list.size());
Assert.assertFalse(list.isEmpty());

Indexed access is provided via get and set methods

ArrayList list;
Object foo;
Object bar;
Object baz;

list.set(0, foo);
list.set(1, bar);
list.set(2, baz);

Assert.assertEquals(foo, list.get(0));
Assert.assertEquals(bar, list.get(1));
Assert.assertEquals(baz, list.get(2));

 All the elements in a list can be accessed using a for loop over the indices

for (0 => int i; i < list.size(); i++)
{
  list.get(i) @=> Object value;
  Assert.assertNotNull(value);
}

an Iterator

list.iterator() @=> Iterator iterator;
while (iterator.hasNext())
{
  iterator.next() @=> Object value;
  Assert.assertNotNull(value);
}

 or an internal iterator via any of the forEach methods

class AssertNotNull extends UnaryProcedure
{
  fun void run(Object value)
  {
    Assert.assertNotNull(value);
  }
}

AssertNotNull assertNotNull;
list.forEach(assertNotNull);

Int and float list implementations also provide similar behaviour.

IntArrayList intList;
intList.add(42);
intList.set(1, -42);
Assert.assertEquals(42, intList.get(0));
Assert.assertEquals(-42, intList.get(1));

FloatArrayList floatList;
floatList.size(16);
floatList.assign(3.14);
floatList.iterator() @=> Iterator iterator;
while (iterator.hasNext())
{
  iterator.next() => float value;
  Assert.assertEquals(3.14, value, 0.001);
}

Functor classes

LiCK provides a suite of Functor classes [1], objects that act as functions or procedures.  Functor objects can be passed to methods, as shown above in the ArrayList.forEach(UnaryProcedure) example.

A procedure accepts argument(s)

class Write extends UnaryProcedure
{
  fun void run(Object value)
  {
    <<<value>>>;
  }
}

A function accepts argument(s) and returns a value

class Multiply extends FloatFloatFunction
{
  fun float evaluate(float value0, float value1)
  {
    return value0 * value1;
  }
}

A predicate accepts argument(s) and returns a boolean value

class AllPositive extends IntIntIntPredicate
{
  fun int test(int value0, int value1, int value2)
  {
    return (value > 0) && (value1 > 0) && (value2 > 0);
  }
}

Functor classes are provided for int, float, and Object arguments, in varying number of arguments.  For Object functors, the prefix indicates the number of arguments, i.e. Unary is 1 argument, Binary is 2 arguments, Tertiary is 3 arguments, Quaternary is 4 arguments.  Thus a QuaternaryFunction accepts 4 Object arguments and returns an Object value.

Similarly, for int and float functors, the number of prefix repeats is the number of arguments, e.g. an IntIntIntIntFunction accepts four int arguments and returns an int value.

For convenience, all of the functions in Math are implemented as functors

// functors can be evaluated against scalar values
Log10 log10;
log10.evaluate(3.14) => float result;

// ...however, they really show their utility when you can pass them to a method
ArrayList list;
list.size(16);
list.assign(3.14);
list.transform(log10);  // log10 is used to transform all the values in list

Interpolating functions

Interpolating functions.

Composite procedures

Composite procedures.

Sample-based drum machine emulators

LiCK provides classes that trigger samples for various vintage drum machines, such as the Oberheim DMX (OberheimDmx.ck) and the Roland TR-909 (RolandTr909.ck). To use these classes, find or record samples of each instrument and copy them to the paths in the source code, or alternatively edit the paths in the source code to match your samples directory.

For example, the samples directory layout for the Roland CR-78 defaults to

samples/RolandCr78/Claves.wav
samples/RolandCr78/ClosedHat.wav
samples/RolandCr78/CowBell.wav
samples/RolandCr78/Crash.wav
samples/RolandCr78/Guiro.wav
samples/RolandCr78/HighBongo.wav
samples/RolandCr78/Kick.wav
samples/RolandCr78/LowBongo.wav
samples/RolandCr78/LowConga.wav
samples/RolandCr78/Maracas.wav
samples/RolandCr78/OpenHat.wav
samples/RolandCr78/Rim.wav
samples/RolandCr78/Snare.wav
samples/RolandCr78/Tamborine.wav 

Each sample is triggered by an IntProcedure that accepts a MIDI velocity value (0 .. 127) mapped to gain. The sample procedures also have rate and maxGain fields. Call these procedures directly in ChucK code

RolandCr78 cr78;
while (true)
{
  cr78.kick.run(127);
  400::ms => now;
  cr78.snare.run(80);
  400::ms => now;
} 

or use a MIDI controller class, such as the nanoPAD (NanoPad.ck)

NanoPad nanoPad;
RolandCr78 cr78;

// assign sample triggers to nanoPAD buttons
cr78.kick @=> nanoPad.button1;
cr78.snare @=> nanoPad.button2;
cr78.closedHat @=> nanoPad.button3;

// open nanoPAD MIDI device 0
nanoPad.open(0); 

Unit tests

Unit testing is a software verification, validation, and documentation method in which a programmer tests if individual units of source code are fit for use [2].  LiCK provides support for unit testing via its Assert.ck class and the following implementation pattern.

ChucK currently does not support calling methods via reflection, so unit tests in LiCK should follow the pattern described below to be executed properly.

Each unit test should be a class which extends Assert.ck

class MyUnitTest extends Assert
{
} 

Next, provide test methods that utilize assertXxx methods to make assertions about the class under test. Assertion messages are optional.

class MyUnitTest extends Assert
{

  fun void testFoo()
  {
    assertTrue("this should be true", true);
  }

  fun void testBar()
  {
    assertFalse("this should be false", false);
  }
}

Provide an pseudo-constructor method that sets exitOnFailure as desired, calls each of the testXxx methods, and prints out a message to stdout on success

class MyUnitTest extends Assert
{
  {
    true => exitOnFailure;
    testFoo();
    testBar();
    <<<"MyUnitTest ok">>>;
  }

  fun void testFoo()
  {
    assertTrue("this should be true", true);
  }

  fun void testBar()
  {
    assertFalse("this should be false", false);
  }
}

Finally, instantiate the unit test and allow ChucK time to pass.

class MyUnitTest extends Assert
{
  {
    true => exitOnFailure;
    testFoo();
    testBar();
    <<<"MyUnitTest ok">>>;
  }

  fun void testFoo()
  {
    assertTrue("this should be true", true);
  }

  fun void testBar()
  {
    assertFalse("this should be false", false);
  }
}

MyUnitTest myUnitTest;
1::second => now;

See http://www.junit.org for further documentation on assertions and unit testing in general.

For examples of actual unit tests, see e.g. ArrayListTest.ck, FloatArrayListTest.ck, or IntArrayListTest.ck in LiCK.

License

LiCK  Library for ChucK.
Copyright (c) 2007-2010 held jointly by the individual authors.

LiCK is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

LiCK is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

  References

1, http://c2.com/cgi/wiki?FunctorObject

2,  http://en.wikipedia.org/wiki/Unit_testing