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:
And this is the vision program. The rectangles are drawn by the program, and indicate where the tracks are being sampled:
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; } |
World’s most complicated wind-up music box?
Pingback: Circular Sequencer For Music Synthesizers » Synthtopia
Now now, I’m sure it could be made to be much more complicated than this
Uh oh, lurking Rube Goldberg tendencies perhaps?
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.)
Thanks! It’s coming along, I should probably do an update post about it
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.