Spinner Synth Prototype

The Spinner Synth is a MIDI controller made with a piece of scrap wood, a webcam and some tape.

I’d been meaning to mess around with a visualization library for a while, and this seemed like a great reason to do so. The only thing I am a bit disappointed about is the frame rate, and presumably if I can figure out how to drop the resolution or monkey with the exposure settings, there should be a reasonable workaround.

This is what the wheel looks like:
Front of the wheel Back of the wheel

And this is the vision program. The rectangles are drawn by the program, and indicate where the tracks are being sampled:

Test program screenshot

Source code (for Linux) follows, after the break.

To compile, try this:

gcc test.c `pkg-config --cflags --libs opencv`

Here is the main source file, I called it test.c:

//
// SpinnerSynth
//
// Really simple rotary synth controller using OpenCV.  It's probably not the
// most elegant way to go about things, and all the important values are more
// or less hardcoded into it, so good luck :-D.
//
// By Matt Mets, June 2009.
//
// Based on the example from http://opencv.willowgarage.com/wiki/CameraCapture
//
// This code is released as public domain.
 
#include <stdio.h>
#include <fcntl.h>
 
#include "cv.h"
#include "highgui.h"
 
 
// Center of the sensing region
#define centerX 320
#define centerY 240
 
// Size of the sensing areas
#define rectWidthX  3
#define rectWidthY  3
 
// Number of sensing regions
#define numSensors 4
 
#define midiPort "/dev/midi"
 
unsigned char channelBlue[numSensors] = {1, 2, 2, 3};   // MIDI channels
unsigned char noteBlue[numSensors] = {48, 52, 57, 60};  // MIDI notes
 
unsigned char noteOn[numSensors] = {0, 0, 0, 0};    // State trackers
unsigned char noteBlueOnVal[numSensors];
 
int main()
{
 
    // open the midi device
    char* device =  midiPort;
 
    // step 1: open the OSS device for writing
    int midi = open(device, O_WRONLY, 0);
    if (midi < 0) {
        printf("Error: cannot open %s\n", device);
        exit(1);
    }
 
    CvCapture* capture = cvCaptureFromCAM( CV_CAP_ANY );
    if( !capture ) {
        fprintf( stderr, "ERROR: capture is NULL \n" );
        getchar();
        return -1;
    }
 
    // See if we can bump up the frame rate (no effect)
//    cvSetCaptureProperty(capture, CV_CAP_PROP_FPS, 30);
//    cvSetCaptureProperty(capture, CV_CAP_PROP_FRAME_WIDTH, 320); 
//    cvSetCaptureProperty(capture, CV_CAP_PROP_FRAME_HEIGHT, 240); 
 
    // Create a window in which the captured images will be presented
    cvNamedWindow( "SpinnerSynth", CV_WINDOW_AUTOSIZE );
 
    // Show the image captured from the camera in the window and repeat
    while( 1 )
    {
        // Grab an image frame
        IplImage* frame = cvQueryFrame( capture );
        if( !frame ) {
            fprintf( stderr, "ERROR: frame is null...\n" );
            getchar();
            break;
        }
 
        // Draw a line to represent the scanner location
        cvLine( frame,
                cvPoint(centerX+15,centerY),
                cvPoint(centerX+215,centerY-105),
                CV_RGB(0,0,128),
                2, 8, 0);
 
        // Read in each rectangle's data
        unsigned int i;
        for ( i = 0; i < numSensors; i++) {
 
            // Determine the rectangle boundaries
            unsigned int rectX = centerX - 20 + (i+1)*44 + 8*i;
            unsigned int rectY = centerY + 10 - (i+1)*22 - 6*i;
 
            // Construct objects to hold the cut out piece of image data that
            // is to be sensed
            CvRect rect = cvRect(rectX, rectY, rectWidthX, rectWidthY);
            CvMat* mat = cvCreateMat(rectWidthX, rectWidthY, CV_8UC1);
            CvScalar value;
 
 
            // Draw a box to outline the sampling region
            cvRectangle( frame,
                         cvPoint(rectX - 1, rectY - 1),
                         cvPoint(rectX + rectWidthX, rectY + rectWidthY),
                         CV_RGB(0,0,128),
                         1, 8, 0);
 
            // Now figure out what the average of these point's values are
            cvGetSubRect( frame, mat, rect );
            value = cvAvg(mat, NULL);
 
            // Determine if a new note has started or the last note has ended.
            if ( (value.val[0] > 120) && (noteOn[i] == 0) )
            {
                // Do some sort of transformation to make the note interesting,
                // this part is improvised.
                unsigned char velocity = 60 + ((int)value.val[0] - 120);
                unsigned char pitch = noteBlue[i] + velocity/30;
 
                // Construct the MIDI on message and Write to the midi device
                unsigned char data[3] = {0x90 + channelBlue[i], pitch, 90};
                write(midi, data, sizeof(data));
 
                // Record that the note was turned on, and at what pitch.
                noteOn[i] = 1;
                noteBlueOnVal[i] = pitch;
            }
            else if ( (value.val[0] <= 120) && (noteOn[i] == 1) )
            {
                // Construct the MIDI off message and Write to the midi device
                unsigned char data[3] = {0x80 + channelBlue[i],
                                         noteBlueOnVal[i],
                                         0};
                write(midi, data, sizeof(data));
 
                // Record that the note was turned off
                noteOn[i] = 0;
            }
        }
 
        // Show the image
        cvShowImage( "SpinnerSynth", frame );
 
        //If ESC key pressed, Key=0x10001B under OpenCV 0.9.7(linux version),
        //remove higher bits using AND operator
        if( (cvWaitKey(10) & 255) == 27 ) break;
  }
 
  // Release the capture device housekeeping
  cvReleaseCapture( &capture );
  cvDestroyWindow( "SpinnerSynth" );
 
  // Close the MIDI device
  close(midi);
 
  return 0;
}
This entry was posted in tech. Bookmark the permalink.

7 Responses to Spinner Synth Prototype

  1. Lee Johnson says:

    World’s most complicated wind-up music box?

  2. Pingback: Circular Sequencer For Music Synthesizers » Synthtopia

  3. mahto says:

    Now now, I’m sure it could be made to be much more complicated than this :-D

  4. Lee Johnson says:

    Uh oh, lurking Rube Goldberg tendencies perhaps?

  5. Came across your name/website due to a search on Twitter. The device you’re working on is fabulous and I assume on track for the museum. Looks like something that could really get a kids attention. (Got mine, not sure what that says, but cool.)

  6. mahto says:

    Thanks! It’s coming along, I should probably do an update post about it :-)

  7. rockwallaby says:

    Yes, this is very cool. I also can see lots of variations on the idea, great for public art installations. Actually I think I would like to use the idea, not of the spinning wheel for input, but using people walking by. The camera could sense the colour (yes, it is spelt colour here in Australia) of their clothes.

    I have a wicked Kawai K5000s additive synth with many midi realtime controllers that would allow you really mulch up the sound.

    Also, check out a program called audiomulch at http://www.audiomulch.com

    Great work, nicely explained video too, well done.

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>