Musical Water Bowl

Short Description: A bowl of water that will produce sound when one’s fingers are dipped into it. I created it using an Arduino, a music programming language called ChucK, an Instructable and a bowl of water.

On Saturday, I spent my day in the lab working on my midterm project, which was making music with water. So how did I turn water into an instrument?

To start off, I downloaded ChucK, and, as I’m a newbie to coding, the codes that were used to upload to the Arduino and ChucK. Here’s a link to the Instructable I used.

Besides the components already given in my Arduino beginner’s kit, the circuitry required 2 more resistors, 3,3k and 1M, 2 capacitors, 100pf and 10nf, a 1N4148 diode, and a 10mH coil/inductor, so I’d asked Matt to order them for me beforehand. The other necessary components that I already had were a 10k resistor, some jumper cables (around 8), an alligator clip, and my bowl. Vivian also gave me a bigger breadboard, to make things easier and ensure that the conductive wires of some of the components weren’t touching. So, I began putting the circuit together (a picture of it is shown below, taken from the Instructable), and there were a couple of slip ups I made that Vivian helped me with – the diagram showed a few components connected in parallel, which I mistakenly connected in series. We also had some trouble figuring out which pins on the Arduino to connect some of the jumper cables to, because the diagram showed the Arduino chip itself – so professor Benjamin pulled up the Arduino schematic for us, also shown below, so that we could figure it all out. The schematic was a .pdf file found here. So this is a screenshot, and therefore somewhat illegible because we had to zoom in. I just put it here for fun.

When the circuitry was all sorted out, it looked like this:

Next, I started up Arduino and ChucK, having connected the Arduino to my laptop, which was connected to a power supply, filled up my bowl with water, and dipped the end of the alligator clip into it. I uploaded the following Arduino code:


#define steps 128

float values[steps];
float alpha;
int maxPos, maxVal;

void setup ()
{
  pinMode (9, OUTPUT); 
  TCCR1A = 0;
  TCCR1B = 0;
  TCCR1A |= (1 < < COM1A0);        // Toggle OC1A on Compare Match.
  TCCR1B |= (1 << WGM12);         // CTC mode
  Serial.begin(9600);
}

void loop () {
  if (Serial.available()) {
    alpha = (float)Serial.read() / 255.0f;
  }
  maxPos = 0;
  maxVal = 0;
  for (int i = 0; i  maxVal) {                              // finds the signal peak
      maxVal = values[i];
      maxPos = i;
    }
  }
  Serial.print(maxPos, DEC);
  Serial.print(" ");
  Serial.println(maxVal, DEC);
  delay(200);
}

And this code in the ChucK program:


SerialIO.list() @=> string list[];

for(int i; i < list.cap(); i++)
{
    chout <= i <= ": " <= list[i] int device; if(me.args()) { me.arg(0) => Std.atoi => device;
}

if(device >= list.cap())
{
    cherr < = "serial device #" <= device <= " not available\n";
    me.exit(); 
}

SerialIO cereal;
if(!cereal.open(device, SerialIO.B9600, SerialIO.ASCII))
{
	chout <= "unable to open serial device '" <= list[device] now; cereal.getLine() => string line;
    if(line$Object != null) {
        //chout < = "line: " <= line int pos; Std.atoi(tok.next()) => int val;
        muggy.play(pos);
        storm.play(pos);
        
    }
}

class Storm {
    string rainFile;
    me.sourceDir() + "/rainloop.wav" => rainFile;
    me.sourceDir() + "/thunderstrike.wav" => string thunderFile;
    SndBuf rainBuf => Gain rainGain => dac;


    rainFile => rainBuf.read;
    0.5 => rainBuf.gain;
    1.0 => rainBuf.rate;
    true => rainBuf.loop;
    rainGain.gain(1.0);

    0 => int lastVal;
    80 => int loudThresh;
    5 => int lowThresh;

    fun void play(int val) {
        if (shouldThunderStrike(val)) {
            spork~ playThunder();
            chout < = "Sporked thunder"  float volume;
        //chout <= "rain volume: " <= volume rainBuf.gain; val => lastVal;
    }

    fun float normalize(int val) {
        40.0 => float highVal;
        20.0 => float lowVal;

        if (val > highVal) {
            return 1.0;
        }
        else {
            return (val - lowVal) / (highVal - lowVal);
        }
    }

    fun int shouldThunderStrike(int val) {
        if ((val > loudThresh || val  loudThresh || lastVal  int randPick;
        me.sourceDir() + "/thunder" + randPick + ".wav" => string thunderFile;
        SndBuf buf  => dac;
        thunderFile => buf.read;
        1.0 => buf.rate;
        0.8 => buf.gain;
        buf.length() => now;
        chout  env => reverb => g => dac;
    g.gain(1);
    env.set(10::ms, 200::ms, 0.5, 100::ms);
    0 => int lastVal;

    fun void play(int val) {
        chout < = "Raw: " <= val <= " Scaled: "; // Loitering reading if (val 80) { val - 60 => val;
            }
            // Other frequency
            else if (29 < = val && val val; } else { val + 30 => val;
            }
            if (val != lastVal) {
                env.keyOff();
                10::ms => now;
                env.keyOn();
            }
        }
        
        c.freq(Std.mtof(val));
        val => lastVal;
        chout < = val <= IO.newline();

    }
}

So, after opening the .ck file with this code in it in the ChucK program’s miniAudicle, I went to the ChucK tab in the menu at the top and clicked Start Virtual Machine. In the argument field, I typed: chuck springshowers.ck:[COM3] – which was my serial port number, the same as that connected to the Arduino. To start the file/the music playing, I clicked ‘Add Shred’, and to stop, ‘Remove Shred’. Here’s a couple screenshots of what all of this looked like – I know that there’s a different file opened here, but we’ll get to that later.

Here are some shots of what the overall setup looked like:

Next, I started to experiment with playing the sound. The file’s code that I showed above was to create the sounds of a spring shower/rainstorm. Once I added shred, there was immediately a constant crackling sort of noise (the noise of thunder?), and when I dipped my fingers in the bowl of water it didn’t change much. Now, here I’ll introduce another problem which I skipped earlier when explaining what circuit components I needed, because it makes more sense to talk about it now. That 10mH inductor/coil? When professor Benjamin was shopping for parts, he was unable to find a 10mH one, so he got me two other sizes, a 1mH one and a 100mH one. He suggested that I try experimenting with the inductors and use a 4M resistor to replace the 1M, so Matt provided me with two more resistors – a 4M and a 4.7M. So, I began playing around and determining which combination worked better, moving my fingers around in the water, and ended up going through all of them. Here’s a fun table of my thoughts during the process.

The 1M resistor and 1mH coil combo worked best, creating rain, thunder, and a high pitched bell-like sound on occasion, although the whole thing seemed like somewhat of a horror track. I had a suspicion that the higher-pitched sound – which was supposed to happen – happened once when a finger from my other hand touched a part of the circuit. Professor Benjamin explained that as I played the water music, I also needed to ground myself, so we plugged in another jumper cable in the breadboard, and I held the open end of it in one hand, and played the music with the other – it worked much better like this.

The Instructable suggested trying the springshowers.ck file first – because it gave two. So, I decided to try out the simplest.ck file just to see what it was like, which is what you saw in the screenshots above, if you were paying attention. The code used in ChucK, then, was as follows.


SerialIO.list() @=> string list[];

for(int i; i < list.cap(); i++)
{
    chout <= i <= ": " <= list[i] int device; if(me.args()) { me.arg(0) => Std.atoi => device;
}

if(device >= list.cap())
{
    cherr < = "serial device #" <= device <= " not available\n";
    me.exit(); 
}

SerialIO cereal;
if(!cereal.open(device, SerialIO.B9600, SerialIO.ASCII))
{
	chout <= "unable to open serial device '" <= list[device] Gain g => dac;
g.gain(0.8);

while(true)
{
    cereal.onLine() => now;
    cereal.getLine() => string line;

    if(line$Object != null) {
        chout < = "read line: " <= line int pos; Std.atoi(tok.next()) => int val;
        osc.freq(Std.mtof(pos)); // Change sin wave frequency
    }
}

When I added shred, the sound produced was much better, in my opinion. It was higher, crisp, and sweet. It played out like music when I ran my fingers through the water, or dipped them to produce notes. I suppose that due to the noise produced by the sound of many, many rain droplets, and wind and thunder, the previous one was much busier, which seemed to reduce its quality.

Overall, creating this project was incredibly fun and I learned a lot.