Code Monkey home page Code Monkey logo

mini_osc_controller's Introduction

Mini OSC Controller

Introduction

Mini OSC Cotroller is a gesture-based music controller built on a Raspberry Pi using an ADC chip (MCP3008), the RPi’s GPIO pins and a Python program.

The controller is built with a minimalistic approach. You can control only one parameter at a time using a distance sensor and your hand (resembling the logic behind Theremin). Which parameter is affected can be controlled by using a set of push buttons.

The controller communicates with SuperCollider on a remote machine using the OSC protocol. At this point the communication uses an Ethernet cable, but it is possible to implement using WiFi.

File Structure:

Components

Hardware

  • A Raspberry Pi Model B+
  • An ADC chip (MCP3008)
  • An IR distance sensor (SHARP)
  • Push buttons, jumper cables and a breadboard

Software

  • Raspberry Pi:
    • Python
    • Python libraries:
      • spidev: SPI interface
      • pyOsc: OSC interface
      • RPi.GPIO: Raspberry Pi GPIO interface
  • Remote machine:
    • SuperCollider

Technical Description

General

The controller consists of 5 push buttons connected to GPIO pins on the Raspberry Pi and an IR distance sensor connected to the ADC chip, which is connected to the Raspberry Pi through an SPI dedicated pin.

The Raspberry Pi is connected to a local network in which the remote machine is also connected.

A Python script runs on the Raspberry Pi, reading the input of the buttons and the sensor and sending an appropriate OSC message to the remote machine.

On SuperCollider, on the remote machine, some instances of OSCdef catch the messages and perform one of the following actions:

  1. Switch a synth ON or OFF
  2. Change the output of a synth (to route through effects)
  3. Change a parameter of a synth

Python Script

What the Python script does can be broke down into three parts:

  • Read the sensor’s output
  • Read the buttons’ output
  • Communicate with the remote machine.

Read Sensor’s output

To read the input of the ADC chip, to which the sensor is connected, the spidev library is used. An instance of SpiDev is opened and a function is used to read the value of the sensor.

A button controls wether to read or not from the sensor. If yes, then the output is read as described below. If not, the program sleeps for a small amount of time (equal to the interval used to stabilize the sensor’s output) and then goes to loop. Ommiting this step caused serious stability problems.

The output of the sensor needs to be stabilized. This is achieved by reading the output of the sensor into an array for a given tiny interval of time. After trial and error, the interval is set at 0.075 seconds, during which time the sensor is read approximately 300 times. Then, using the Counter class from the collections library, we find the most common value in the array, with the most_common method.

The output of this method is a 2D array, with each pair of values representing the dominant value and the number of times it occured. We only need the first pair (the most dominant value) and only the value itself.

Read Buttons’ Output

There are 5 push buttons performing the following actions:

  1. Cycle through the available synths
  2. Cycle through the available parameters of each synth
  3. Switch the sensor ON or OFF
  4. Change the selected synth’s output
  5. Switch the selected synth ON or OFF

To read from the GPIO pins, the RPi.GPIO library is used. The pins are set to input and the internal resistor is set to pull down, so that the ouput is 1 when the button is pressed and 0 when it is not pressed.

The buttons are read using interrupts to avoid checking for the state for each button during the program’s loop, which is both not elegant and resource-consuming. The logic behind interrupts is that you assign a function at a GPIO pin, which is run when an “edge” (a transition of states) is detected on that pin. The edge can be either “RISING” (from 0 to 1), “FALLING” (from 1 to 0) or “BOTH”. Here, since we want to perform an action when the button is pressed and no action when the button is released, we use the “RISING” option.

To cycle between synths and parameters, a dictionary is being used. The keys correspond to the names of the synths (prefixed with alphabet letters to help sorting) and the values of the keys are lists containing the parameters of each synth.

The interrupt functions change the indicies or the values of the lists by reading them as globals.

The function that change the current parameter read the values from the dictionary, according to which synth is being selected. The functions to change synths, turn synths on or off and change routes use a sorted array of the keys of the dictionary, so that the synths appear with the correct order (dictionaries are unsorted by default).

OSC Communication

Upon running the program the user is prompted to set the IP of the remote machine and the port used for OSC.

The OSC messages are used for: a. changing synth attributes (parameters, turning on or off and output) and b. monitoring purposes (synth and parameter to be changed and whether the sensor sends data or not).

The OSC message concerning the sensor is sent from the main loop, after reading and stabilizing the sensor’s value. Then, it is sent with current synth and parameter names. This is an example of an OSC message, as read in sclang:

(/sensor, fm, carr, 300)

The OSC messages concerning switching synths on or off and changing synths’ output are sent from the respective callback functions when an interrupt occurs. The message about monitoring synths and parameters is sent either from the change_synth function or the change_param function and consists of the name of the synth and the parameter to be changed. The OSC message about monitoring the sensor’s state is sent from the sensor_switch function.

SuperCollider

In SuperCollider the main task is to create sound. Some GUI windows are also created, containing the controls for each synth. They are used both for visualizing the changes made to the synths, and also controlling the synths, bypassing the controller.

The SuperCollider script is divided in 4 different files, so that it can be easily read and revised:

  • 1.globals.scd: Initialization code, global variables
  • 2.synthdefs.scd: Synth definitions
  • 3.guis.scd: GUI creation
  • 4.osc_handling.scd: OSC functions definitions

The files are numbered because they must be evaluated in this particular order.

Globals

This file contains definitions and variables used thoughout the script: the number of synths, an empty array to contain synths, universal lag value, different groups for synths and effects (to ensure proper order of execution), buses for the effects and the proper ranges for the different parameters. These are defined using the ControlSpec class.

SynthDefs

This file contains the synth definitions. Each of the synths used implements, or implies, different synthesis techniques: FM synthesis, granular synthesis, additive synthesis, subtractive synthesis. Synths:

  • \fm: Simple frequency modulation. An audio rate sine wave (modulator) modulates another sine wave’s frequency (carrier). The modulator’s frequency represents the rate at which the carrier’s frequency is being changed. The mul value of the modulator represents the modulation depth, so, if it is set to 20, the carrier’s frequency (freq) will have a range of (freq - 20, freq + 20). Parameters:
    • carr: The carrier’s frequency.
    • mod: The modulator’s frequency.
    • dp: The modulation depth.
    • pan: Position of the sound in the stereo field.
    • amp: The sound’s amplitude.
    • out: Where the sound outputs.
  • \grains: Granular synthesis implementation using the TGrains UGen. An impulse triggers a sample’s grain to be played. As an impulse I used an instance of Dust, which provides random impulses of varying density. The sample used is a default sample of the SC’s sound library. An instance of LFNoise0 provides some randomization of the grains position in the sample’s duration. Parameters:
    • buf: The buffer containing the sample.
    • dens: The density of the random impulse.
    • rate: The playback rate of the sample.
    • pos: Position of the grain in the sample’s duration.
    • dur: Duration of the grain.
    • rfreq: Frequency by which the randomization of the grain’s position changes.
    • rdp: Depth of the randomization.
    • pan: Position of the sound in the stero field.
    • amp: Amplitude of the sound.
    • out: Where the sound outputs.
  • \comb: Simple comb filter (echo). This can also be used as a form of additive synthesis, when the time between the sound repeats gets very small. Parameters:
    • in: Input bus.
    • time: Interval between repeats.
    • dec: Sound decay in seconds.
    • amp: Amplitude of the repeats.
    • out: Where the sound outputs.
  • \bpf: Band Pass Filter implementation with a kind of FM. The BPF is used as an example of subtractive synthesis. Optionally, a sine wave is applied to the BPF’s frequency. Parameters:
    • in: Input bus.
    • freq: BPF frequency.
    • rq: The filter’s quality.
    • mfreq: Frequency of the modulator wave.
    • mdp: Depth of the modulation.
    • out: Where the sound outputs.

GUIs

Each synth is visualized with a respective GUI window. An extra GUI window is created for monitor purposes. The GUI windows use knobs and slidebars to change the different parameters, while mapping them to the appropriate range by using the ranges defined in 1.globals.scd.

Each window has a button to turn the synth ON or OFF and a drop-down menu to change the output of the synth.

The GUI windows also act as an “intermediate” between the OSC messages and the synths: Instead of directly affecting the parameters, the OSC messages affect the GUI element. This is useful because a. it automates the command used to alter the parameters, as the output of the sliders and knobs is standard from 0 to 1, and b. by setting different values of the knobs and sliders, we can visualize and keep track of the changes made in each parameter.

OSC handling

This file contains the OSC functions’ definitions. The OSC functions listen to the data sent from python and use them to formulate commands in the form of strings (so that we can set variables’ names according to the received message), which are then evaluated as SC code using the interpret command. Because the commands alter GUI elements, we must use the defer command, which executes code using the AppClock instead of the SystemClock. There are 4 OSC functions:

  1. \monitor: This is used to pass monitor data to the relevant window. It receives the names of the synth and the parameter and uses them to format a string to assign to the string element of the StaticText inside the window. It also receives whether the sensor is on or off and changes the color and the label on the monitor window.
  2. \onoff: This function receives the name of the synth and the state we want to assign to it (0 or 1).
  3. \route: This function receives the name of the synth that we want to change its output. It then uses the previous output to assign the next one (using the value of the PopUpMenu).
  4. \change_params: This function reads three values: the name of the synth, the name of the parameter and the value of the sensor’s output. It then maps the sensor’s value to a linear range of 0 to 1 (the knob’s and slider’s range). Then, it assigns the new value to the relevant GUI element.

TODO list:

  • Automate the way synths are read, so that you can create synths in SC and then send an OSC message to python that generates the synth dictionary (tricky, because of the use of GUI and global variables)
  • Change the way the synths are stored in SC. Instead of an array use a dictionary.
  • Change how the synths’ output is being switched. The current implementation is sloppy and error-prone.
  • Implement WiFi. Use previous work (SecretSchool etc)
  • Write code to enable sending to multiple SC instances in different machines
  • Update this ReadMe file with info about used libraries, classes, modules etc

mini_osc_controller's People

Contributors

dakodeon avatar

Watchers

James Cloos avatar  avatar

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.