« 4 / 30 »

featureCreep

2019-04-23 17:19 supercollider

This software was written for and in collaboration with violinist George Kentros. Premiered at "After Work - This violin must die", Fylkingen Stockholm 30 Mar 2019.

The program is written in SuperCollider with additional features like iPad control and video mixer made in MaxMSPJitter.

Main interface

The main window with its eight tracks (configurable) looks like this...

and here you can set an amplitude threshold for the microphone and arm tracks to automatically detect and capture sounds.

With the waveform selection (dark grey areas) you select what part of the captured sound you want your different players to use.

There are many players - all with different behaviours...

slow, medium, repetitive, scanner, sweep, nervous, fastRhythmic, slowRhythmic, limpRhythmic, fast, drill, jump, plain, half, third, quarter, pingpong, crawl, wave, waveHalf, pattern1, pattern2, pattern3, round, sync

Player behaviours include in which direction and how fast to progress in the captured sound buffer, when to play for how long, with which envelope and how loud, how much transposition, play in which audio channel etc.

Some of these players are using granular synthesis techniques while others are scratching and jumping around in the sound buffer in more or less unpredictable ways. Hopefully, the names listed here above will give a hint of what players do to the sound.

All players are pieces of code. For example this the code for the scanner player...

SynthDef(\scanner, {|buf, amp= 1, start= 0, end= 1|
  var dur= BufDur.kr(buf);
  var pha= LFNoise2.kr(In.kr(100)+0.1)+1/2;
  var pos= pha*(end-start)+start;
  var add= LFNoise2.kr(1);
  var fre= In.kr(101).linexp(0, 1, 1.1, 18)+add*SinOsc.kr(0.1).max(0.5);
  var snd= TGrains.ar(
    2,
    Impulse.ar(fre),
    buf,
    1+LFNoise2.kr(1, 0.05),
    pos*dur,
    0.75,
    LFNoise2.kr(0.06, 0.5),
    amp.lag(0.5)
  );
  SendReply.kr(Impulse.kr(\updateRate.kr+1), '/pos', pos);
  Out.ar(\syncBus.ir(40)+\index.ir, K2A.ar(pha));
  snd= CompanderD.ar(snd, \compressorThresh.kr, 0.5, 0.5, 0.005, 0.01, EnvGate());
  Out.ar(\out.kr, snd);
}).add;

and most players will look very similar to this. But there are also a few special ones like sync which just matches the playback position of the player in the track above.

In the main window one can also set track volume, bypass any global effects and read/write sound files.

The computer keyboard may be used for shortcuts quickly arming, playing and selecting what each track should play.

Global control

There is also an additional control GUI with global volume, effects, automation and record...

The three sliders delay, sustainer, distortion are just simple wet/dry control for global effects (that can be bypassed for each track using the bp button), while the other sliders freeze, fire, industry, chaos, air are more complex and control a bunch of effects and behaviours. For example, freeze will take the sound from all players and smear it out by removing transients and pitch shifting copies of the sound up/down in octaves.

When the automation button is clicked, the program itself can start and stop tracks, change effects and vary a lot of other parameters in the code. The idea here is that the software should be able to run independently and generate interesting variations over long periods of time. Also, it should never go completely silent nor go wild and overload everything.

Remote iPad interface

All the above can be viewed and controlled remotely from a tablet or a phone using a web browser. We programmed a look-alike GUI patch with MaxMSPJitter and the mira externals. This patch talks to the SuperCollider interface over Open Sound Control.

featureCreep screenshot from iPad interface
featureCreep screenshot from iPad interface
featureCreep screenshot from iPad interface

The interface here is even more ugly - especially the waveform display because the sound data had to be downsampled and kept at a low resolution to reduce wifi network traffic. Also, it was hard to fit everything on a single screen as all widgets had to be quite large and not too close to each other to be usable on an iPad.

Our software also includes a video mixer for camera and movie playback (not shown here).


SC: Fixed Number of Decimals

2019-04-23 11:46 supercollider

Here's a quick function for displaying float numbers as strings in SuperCollider.

(
~fixDec= {|val, numDecimals= 2|  //float to string with fixed number of decimals
  var str= val.round(0.1**numDecimals).asString;
  var num= str.size-str.indexOf($.)-1;
  str.extend(str.size+numDecimals-num, $0);
};
)


//test examples
~fixDec.value(0.1, 3)
-> 0.100
~fixDec.value(0.12345, 3)
-> 0.123

//rounds internally.  compare:
~fixDec.value(0.191, 2)
-> 0.19
~fixDec.value(0.197, 2)
-> 0.20

//can deal with negative values
~fixDec.value(-2pi, 4)
-> -6.2832

//and integers
~fixDec.value(10000, 4)
-> 10000.0000

~fixDec.value(10000, 0)
-> 10000.

Note: It does not play well with exponential notation

~fixDec.value(1e-4, 5)
-> 0.00010
~fixDec.value(1e-5, 5)
//error

anneVideotracking4

2019-01-21 22:33 visuals

A much-improved version of my old MaxMSPJitter application anneVideotracking

screenshot
screenshot
screenshot

With this version, you can use 12 zones on a web camera video input to trigger MIDI, sound files, audio input (mics) and OSC messages (send to SuperCollider for example).

The zones include filters and different types of thresholds and calibration. The data can be on/off triggers and or continuous values.

(sorry for the terrible GUI design.)

Download the macOS standalone from /code/apps/#annevideotracking.

and here's the updated SuperCollider example that demonstrates how to use the OSC data to control some sine oscillators.

//to start: select all & cmd+enter
//to stop: cmd + .
(
n= 12;
s.latency= 0.05;
s.waitForBoot{
  var dlast= 0.dup(n);
  d= {Bus.control(s, 1)}.dup(n);
  e= {Bus.control(s, 1)}.dup(n);
  OSCFunc({|m|
    var index= m[1], val= m[2], diff= (val-dlast[index]).abs;
    //m.postln;
    d[index].set(val);
    e[index].set(diff);
    dlast.put(index, val);
  }, \anneVideoTracking);
  CmdPeriod.doOnce({d.do{|x| x.free}; e.do{|x| x.free}});
  SynthDef(\annetest, {
    var src= Mix({|i| SinOsc.ar(i*100+400, 0, LagUD.kr(In.kr(e[i].index), 0.01, 0.1))}.dup(n));
    Out.ar(0, Pan2.ar(src));
  }).add;
  s.sync;
  Synth(\annetest);
};
)

MIDI Pedals

2019-01-16 14:01 electronics

Here's how I built a USB foot pedal that sends out MIDI CC messages as well as note on/off messages when crossing some threshold.

To get a rugged pedal I bought a sewing machine foot pedal/speed control. For converting to MIDI and connecting to a computer I used a Digispark module. The two other parts needed are a 3.5mm socket and a 4K7 resistor.

pedal - photo
pedal - photo
pedal - photo
pedal - closeup of circuit
pedal - closeup of circuit
pedal - closeup of circuit

The code for the Digispark is really simple and I programmed it from the Arduino IDE. THRESH_HI and THRESH_LO together with the state variable implement hysteresis, and lastVal is used to filter out any repeating values.

//with Digispark (Default - 16.5MHz)
//connect 4.7K resistor between tip and 5V, tip to P2 and sleeve to GND

//usb yellow or red     5V
//usb white             data-
//usb green             data+
//usb grey or black     GND

#define USB_CFG_DEVICE_NAME     'm','i','d','i','P','e','d','a','l'
#define USB_CFG_DEVICE_NAME_LEN 9
#include <DigiMIDI.h>

#define PINLED 1      //onboard led
#define PINSENSOR A1  //foot controller sensor (P2)
#define PINGND 0      //ground
#define CTRL 7        //midi controller (cc)
#define NOTE 99       //midi note
#define VELO 64       //midi velocity
#define CHAN 9        //midi channel
#define THRESH_HI 100 //0-127
#define THRESH_LO 50  //0-127

DigiMIDIDevice midi;
int lastVal = 0;
int state = 0;

void setup() {
  pinMode(PINLED, OUTPUT);
  pinMode(PINGND, OUTPUT);
  digitalWrite(PINGND, LOW);
  midi.sendNoteOff(NOTE, VELO, CHAN);
}

void loop() {
  midi.update();
  int val = analogRead(PINSENSOR);
  val = constrain(val, 9, 900);
  val = map(val, 9, 900, 127, 0);
  if (val != lastVal) {
    midi.sendControlChange(CTRL, val, CHAN);
    if (state == 0 && val > THRESH_HI) {
      midi.sendNoteOn(NOTE, VELO, CHAN);
      digitalWrite(PINLED, HIGH);
      state = 1;
    } else {
      if (state == 1 && val < THRESH_LO) {
        midi.sendNoteOff(NOTE, VELO, CHAN);
        digitalWrite(PINLED, LOW);
        state = 0;
      }
    }
    lastVal = val;
  }
  midi.delay(100);
}

Some SuperCollider test code...

MIDIClient.init;
MIDIIn.connectAll;
MIDIdef.cc(\pedalCont, {|...args| [\pedalCont, args].postln}, 7, 8);
MIDIdef.noteOn(\pedalOn, {|...args| [\pedalOn, args].postln}, 99);
MIDIdef.noteOff(\pedalOff, {|...args| [\pedalOff, args].postln}, 99);

MIDIdef.trace;

For another project, I also made a 3D printed variant. This one is not so rugged and only acts as an on/off switch. It's based on Adafruit's USB_Foot_Switch_Controller but modified to fit a Digispark.

3D printed pedal
3D printed pedal
3D printed pedal

Arduino code for this discrete pedal (only note on/off)...

//with Digispark (Default - 16.5MHz)
//modified Adafruit's USB_Foot_Switch_Controller

#define USB_CFG_DEVICE_NAME     'm','i','d','i','P','e','d','a','l'
#define USB_CFG_DEVICE_NAME_LEN 9
#include <DigiMIDI.h>

#define PINLED 1  //onboard led
#define PINSENSOR 2 //switch
#define NOTE 89  //midi note
#define VELO 64  //midi velocity
#define CHAN 9  //midi channel

DigiMIDIDevice midi;

int state = 0;

void setup() {
  pinMode(PINSENSOR, INPUT_PULLUP);
  pinMode(PINLED, OUTPUT);
  midi.sendNoteOff(NOTE, VELO, CHAN);
}

void loop() {
  midi.update();

  if (digitalRead(PINSENSOR) == 0) {
    if (state == 0) {
      digitalWrite(PINLED, HIGH);
      midi.sendNoteOn(NOTE, VELO, CHAN);
      state = 1;
    }
  } else {
    if (state == 1) {
      digitalWrite(PINLED, LOW);
      midi.sendNoteOff(NOTE, VELO, CHAN);
      state = 0;
    }
  }
  midi.delay(100);
}

OpenBCI WiFi Shield DIY

2019-01-13 13:28 electronics

Here is how I built my own WiFi shield for the OpenBCI Cyton and Ganglion boards. The total cost was less than €10.

See the schematic.

On the white adapter board, I first removed the 0K resistor link in the middle (R2). Then I cut the two tracks that run from VCC to the left-hand side 10K resistor (R3). Next, I soldered on an NCP1117 on the backside (not visible in the photo) and finally added two 100nF caps.

Last I mounted the ESP+adapter on a Vero board, added male pin headers and the other components and wires.

ESP8266-12 module on proto board
ESP8266-12 module on proto board
ESP8266-12 module on proto board
Cyton board with DIY WiFi shield
Cyton board with DIY WiFi shield
Cyton board with DIY WiFi shield

It looks very rough but works great. Here are instructions on how to upload firmware and how to use it.


Bluetooth Module Repair

2019-01-13 12:24 electronics

This documents a repair of a Cyton board. The board would turn on and kind of work but one couldn't upload new firmware to it.

I localised the issue to the RFD22301 (aka RFDuino) module. To desolder it from the board I used some rose's metal - a material with low melting temperature (94°c) - and then put on new temporary pins to a few pads and put it on a breadboard.

repairing a RFDuino
repairing a RFDuino
repairing a RFDuino

The module sort of worked but it still wouldn't be programmed. After lifting the metal shield and checking all the connections, I found a broken trace from the chip out to the rf_rxd pad (gpio0/aref) on the module. Tracing the fault a bit further it turned out to be a broken via. I tried to refill the via with solder but it was just too small to repair. So I ended up adding a thin copper wire directly from the pin to the pad and wrapped it in some Kapton tape for isolation.

RFDuino without metal can
RFDuino without metal can
RFDuino without metal can

I had to cut out a slot in the metal shield so that my wire could get out without getting squeezed and shorting to ground. It doesn't look pretty, but the module now worked and I could finally upload new firmware. And after soldering the shield back and putting back the module, the Cyton board worked again.

Working RFDuino back on Cyton board
Working RFDuino back on Cyton board
Working RFDuino back on Cyton board

I also repaired a broken trace on one of the serial lines. It's visible on the right-hand side in the photo.


Lidar Sensor

2019-01-12 11:10 visuals

Last summer I wrote code to talk to the YDLIDAR X4 Lidar – 360-degree Laser Range Scanner (10m). It was quite difficult to parse the data and get the correct readout of the point cloud. The X4_Lidar_Development_Manual.pdf had all the information but it was quite obscure.

I also added tracking (red cross in the screenshot) to figure out coordinates of a person moving around in a room. Nothing fancy but worked ok.

The attached code is for Unity3d and written in C#.

Attachments:


Radar Sensor

2019-01-11 14:31 electronics

Last year I built this very simple motion detection device. It will trigger a noteOn MIDI message when someone enters a room, and a noteOff message when there's no activity. It's using a RCWL-0516 radar sensor and a Digispark clone (basically a ATtiny85).

Sensor with Digispark and USB cable
Sensor with Digispark and USB cable
Sensor with Digispark and USB cable
//with Digispark (Default - 16.5MHz)
//RCWL-0516 Microw. Radar-ModulRCWL-0516

#define USB_CFG_DEVICE_NAME     'm','i','d','i','R','a','d','a','r'
#define USB_CFG_DEVICE_NAME_LEN 9
#include <DigiMIDI.h>

#define PINLED 1  //onboard led
#define PINSENSOR 2 //radar sensor
#define NOTE 99  //midi note
#define VELO 64  //midi velocity
#define CHAN 9  //midi channel

DigiMIDIDevice midi;

int state = 0;

void setup() {
  pinMode(PINSENSOR, INPUT);
  pinMode(PINLED, OUTPUT);
  midi.sendNoteOff(NOTE, VELO, CHAN);
}

void loop() {
  midi.update();

  if (digitalRead(PINSENSOR) == 1) {
    if (state == 0) {
      digitalWrite(PINLED, HIGH);
      midi.sendNoteOn(NOTE, VELO, CHAN);
      state = 1;
    }
  } else {
    if (state == 1) {
      digitalWrite(PINLED, LOW);
      midi.sendNoteOff(NOTE, VELO, CHAN);
      state = 0;
    }
  }
  midi.delay(100);
}

The device shows up as a MIDI device and the following code is an example of how to connect to it in SuperCollider...

MIDIClient.init;
MIDIIn.connectAll;
MIDIdef.noteOn(\radarOn, {|...args| [\radarOn, args].postln}, 99);
MIDIdef.noteOff(\radarOff, {|...args| [\radarOff, args].postln}, 99);

MIDIdef.trace;

« 4 / 30 »