«  …4 5 6 7 8 9 10 »

Paper Speakers

2016-09-11 20:44 electronics

Here's some technical information on a collaboration with visual artist Jenny Michel. It's a sounding art installation that has previously been shown at Galerie Feldbuschwiesner and Städtische Galerie Wolfsburg, and is now (Sep 2016) up and running at SMAC project space in Berlin. Also exhibited in Wiesbaden and at Kunstverein-Tiergarten in Berlin.

'Traps', as the piece is called, consists of speakers made out of paper and enamelled copper wire. The wire is embedded into the paper in the shape of a coil and two strong magnets are situated on the back. The paper is manually layered and filled with prints and later perforated with cutouts. Behind the paper is also a small circuit with a microcontroller, an amplifier and an antenna - all hand soldered dead-bug style. Power comes from a shared 5V wall adapter.

Traps consists of around 30 individual speakers and they all react on each other via their antennas. They also pick up different electromagnetic fields - but we can't say in exactly which frequency range they are sensitive as the antennas are made with arbitrary lengths and shapes (they're also copper wire).

The sound is synthesised with the microcontroller (ATtiny45). Loudness and quality vary depending on the shape of the speakers - they're all individual - but in general the resulting sounds are quite noisy and piercing. The code was written to create a lot of variation and not just sonify the raw antenna input. So it's more of an interpretation than a direct translation of trapped radio frequency signals to sound as we first had planned. We also wanted harsher electronic sounds as it both fits better with the visual impression and that that type of square wave audio work very well in these non-linear lo-fi speakers.

There are also two modes: if one quickly turn the power on-off-on, the installation will start in 'vernissage mode' and play more sound than if one just turns the power on as normal (look for the EEPROM part in the code below to see how it works more in detail).

In total, we built 50 circuits and many more paper speakers. Though ~30 is enough to fill a medium-sized gallery.

traps circuit 0 traps circuit 1 traps 0 traps 1 traps 2 traps 3

Here's the code...

//f.olofsson 2015-2016
//select ATtiny, 1MHz (internal)
//decreased durMax in two places from 80000 to 8000

#include <EEPROM.h>

int durPause;
unsigned long durMax;
byte pausePer;
void setup() {
  randomSeed(analogRead(3));
  pinMode(4, OUTPUT);
  byte magic = EEPROM.read(0);
  if (magic == 123) { //lots of sound - chaotic opening mode
    durPause = 15000;
    durMax = 8000;
    pausePer = 67;  //percentage
    for (byte i = 0; i < 3; i++) {  //beep three times at startup
      prgStatic(100);
      prgPause(100);
    }
  } else {  //default - soft gallery mode
    durPause = 30000;
    durMax = 8000;
    pausePer = 75;  //percentage
    EEPROM.write(0, 123);
    delay(3000);  //power on for <3sec & then next time mode 1
    prgStatic(1500);  //make a tone at startup to know all working
  }
  EEPROM.write(0, 255);
}
void loop() {
  prgNoise(analogRead(3));
  if (random(100) < pausePer) {
    prgPause(durPause);
  } else {
    unsigned long dur = random(durMax);
    switch (analogRead(3) % 7) {
      case 0:
        prgPause(dur);
        break;
      case 1:
        prgNoise(dur);
        break;
      case 2:
        prgNoise(dur);
        break;
      case 3:
        prgChunks(dur);
        break;
      case 4:
        prgChunks(dur);
        break;
      case 5:
        prgBitbang(dur);
        break;
      case 6:
        prgImpulses(dur);
        break;
    }
  }
}
void prgPause(unsigned long dur) {
  analogWrite(4, 0);
  pinMode(0, OUTPUT);
  digitalWrite(0, 0);
  delay(dur);
  pinMode(0, INPUT);
}
void prgStatic(int dur) {
  analogWrite(4, 127);
  delay(dur);
}
void prgNoise(unsigned long dur) {
  unsigned long stamp = millis();
  while (millis() - stamp < dur) {
    analogWrite(4, analogRead(3) >> 2);
  }
}
void prgNoise2(unsigned long dur) {
  unsigned long stamp = millis();
  while (millis() - stamp < dur) {
    analogWrite(4, analogRead(3) >> 2);
    delay(1);
  }
}
void prgChunks(unsigned long dur) {
  unsigned long stamp = millis();
  byte base = (analogRead(3) >> 10) * 255; //either 0 or 255
  while (millis() - stamp < dur) {
    int val = analogRead(3);
    analogWrite(4, val >> 2);
    delay(val);
  }
}
void prgChunks2(unsigned long dur) {
  unsigned long stamp = millis();
  byte base = (analogRead(3) >> 10) * 255; //either 0 or 255
  while (millis() - stamp < dur) {
    int val = analogRead(3);
    analogWrite(4, val >> 2);
    delay(val / 2);
    analogWrite(4, base);
    delay(val / 2);
  }
}
void prgBitbang(unsigned long dur) {
  unsigned long stamp = millis();
  while (millis() - stamp < dur) {
    int val = analogRead(3 >> 2);
    for (byte i = 0; i < 8; i++) {
      digitalWrite(4, val >> i & 1);
    }
  }
}
void prgImpulses(unsigned long dur) {
  unsigned long stamp = millis();
  while (millis() - stamp < dur) {
    int val = analogRead(3);
    digitalWrite(4, 1);
    digitalWrite(4, 0);
    delay(val);
  }
}

Note the use of the LM386 N-4. It is more powerful than the N-1, N-3 variants.

traps schematics

Stine

2016-09-09 15:41 electronics, supercollider

Next week in Bucharest we'll be setting up the subjective frequency transducer for the third time. I described the sound/vibration generating part of this system before but didn't write much about how the controllers work.

So for each sound channel (i.e. each bass transducer), there's a wireless controller that enables the audience to set their preferred frequency. Technically it's done with a rotary encoder, an ESP8266 WiFi module, an ATmega168 and a big 7-segment LCD. The circuit runs off two AAA batteries.

When someone touches the rotary encoder, the circuit wakes up and starts sending OSC messages to a laptop running SuperCollider. SuperCollider receives the values, starts playing an oscillator and sends the sound to the corresponding audio channel. When done, SuperCollider fades out the oscillator and sends an off message to the circuit and the controller goes back to sleep mode.

I spent quite some time optimising the microcontroller (ATmega168) code. It was hard to both reduce power consumption and still being able to quickly wake up and react on user input as well as on incoming OSC messages. It's a common problem with battery-powered radio devices.

Also getting the ESP8266 to handle OSC messages was a pain. /f0blog/esp8266-opensound-control/ and /f0blog/esp8266-opensound-control-teensy/ contain some more info and simplified versions of that.

In the end, the code for talking to these circuits in SuperCollider looked like this:

//SC example: sending. turn off circuit 3 and set it back to initial frequency
~encode= {|id, on, hi, lo| (id&255<<24)|(on&255<<16)|(hi&255<<8)|(lo&255)};
~encode.value(3, 0, 0, ~initFreq);
//SC example: receiving. decoding data from the four ESP8266
OSCdef(\sti, {|msg, time, addr|
    var id= msg[1]>>24;
    var onoff= (msg[1]>>16)&255;
    var freq= (msg[1]&65280)+(msg[1]&255);
    [\id, id, \onoff, onoff, \freq, freq].post;
}, \sti);

The microcontroller code could still be improved. I'd like it to wake up on both WDT and UART. At the moment the circuit is drawing 22mA average in off+idle state, and 33mA average with display set to '20' which is okey but not optimal. And when sending OSC you get current spikes of a few hundred milliamps but there's no way around that.

//f.olofsson 2015-2016

#define ID 3
#define FREQ 0 //start frequency
#define FREQ_MIN 0
#define FREQ_MAX 9999
#define WLAN_SSID "MYNETWORK"
#define WLAN_PASS "MYPASSWORD"
#define WLAN_ADDR "192.168.43.99" //laptop static ip
#define WLAN_PORT 1112
String tag = "/tap"; //incomming OSC addy

#include <avr/sleep.h>
#include <avr/power.h>
#include <avr/wdt.h>

#include <Encoder.h>

Encoder myEnc(3, 2);
float freq = FREQ;  //starting frequency
int freqLast = -999;

byte state = 0;
int enc = 0;
byte dig = 0;
byte cnt = 0;
boolean resp;

uint8_t buf[16];  //OSC message

void setup() {
  pinMode(2, INPUT_PULLUP);  //encoder a
  pinMode(3, INPUT_PULLUP);  //encoder b
  pinMode(4, INPUT_PULLUP);  //encoder button
  DDRB = B11111111;  //segments
  DDRC = B00001111;  //digits selector

  //--set up wifi
  Serial.begin(115200);
  Serial.setTimeout(10000);
  resp = Serial.find("ready\r\n");
  progressDot(1);
  Serial.println("AT+CWMODE=1");
  resp = Serial.find("OK\r\n");
  progressDot(2);
  do {
    Serial.print("AT+CWJAP=\"");
    Serial.print(WLAN_SSID);
    Serial.print("\",\"");
    Serial.print(WLAN_PASS);
    Serial.println("\"");
    resp = Serial.find("OK\r\n");
  } while (!resp);
  progressDot(3);
  Serial.println("AT+CIPMUX=1");
  resp = Serial.find("OK\r\n");
  progressDot(4);
  Serial.print("AT+CIPSTART=4,\"UDP\",\"");
  Serial.print(WLAN_ADDR);
  Serial.print("\",57120,");  //SuperCollider default port
  Serial.print(WLAN_PORT);
  Serial.println(",0");
  resp = Serial.find("OK\r\n");
  Serial.setTimeout(1000);
  displayClear();

  //--OSC message
  buf[0] = 47;   // /
  buf[1] = 115;  // s
  buf[2] = 116;  // t
  buf[3] = 105;  // i
  buf[4] = 0;
  buf[5] = 0;
  buf[6] = 0;
  buf[7] = 0;
  buf[8] = 44;   // ,
  buf[9] = 105;  // i
  buf[10] = 0;
  buf[11] = 0;
  buf[12] = ID;  // a high   (id)
  buf[13] = state; // a low  (onoff)
  buf[14] = 0;   // b high   (freq hi)
  buf[15] = 0;   // b low    (freq lo)

  //--timer
  noInterrupts();
  TCCR1A = 0;
  TCCR1B = 0;
  TCNT1 = 0;
  OCR1A = 32768;  //62.5Hz display updaterate
  TCCR1B |= (1 << WGM12);
  TCCR1B |= (1 << CS10);  //prescaler divide by 1
  TIMSK1 |= (1 << OCIE1A);
  interrupts();

  //--sleep
  MCUSR &= ~(1 << WDRF);
  WDTCSR |= (1 << WDCE) | (1 << WDE);
  WDTCSR = 1 << WDP0 | 1 << WDP1;
  WDTCSR |= _BV(WDIE);
}

volatile int f_wdt = 1; //watchdog wakeup
ISR(WDT_vect) {
  if (f_wdt == 0) {
    f_wdt = 1;
  }
}
void enterSleep(void) {
  set_sleep_mode(SLEEP_MODE_IDLE);
  sleep_enable();
  sleep_mode();
  sleep_disable();
  power_all_enable();
}

ISR(TIMER1_COMPA_vect) {  //update display periodically
  if (state == 2) {
    displayFreq();
  }
}

void sendOsc() {
  buf[13] = state;
  buf[14] = int(freq) >> 8;
  buf[15] = int(freq) & 255;
  Serial.println("AT+CIPSEND=4,16");
  Serial.find(">");
  Serial.write(buf, sizeof(buf));
  resp = Serial.find("OK\r\n");
}

void loop() {
  dig = 1 - ((PIND >> 4) & 1);  //encoder momentary button
  switch (state) {
    case 2:  //running (display on)
      enc = myEnc.read();
      if (enc != 0) {
        float incStep = enc / 2.0;
        myEnc.write(0);
        freq = max(FREQ_MIN, min(FREQ_MAX, freq + incStep));
        if (int(freq) != freqLast) {
          sendOsc();
          freqLast = int(freq);
        }
      }
      if (dig == 1) {  //TODO: or timeout here?
        state = 3;
      }
      break;
    case 0:  //sleeping (display off)
      f_wdt = 0;
      enterSleep();
      enc = myEnc.read();
      if ((dig == 1) || (enc != 0)) {
        state = 1;
        freq = FREQ; //reset
        sendOsc();
      }
      break;
    case 3:  //turning off when button released
      displayClear();
      if (dig == 0) {
        state = 0;
        sendOsc();
      }
      break;
    case 1:  //turning on when button released
      if ((dig == 0) || (enc != 0)) {
        state = 2;
        myEnc.write(0);
      }
  }

  //--receive OSC
  while (Serial.available()) {
    String abc = Serial.readStringUntil('\n');
    if (abc.startsWith("+IPD,4,16:" + tag)) {
      //if(abc[22]==ID) { //optional filter by device ID
      if (abc[23] == 0) {
        displayClear();
        state = 0;
      } else {
        state = 2;
        myEnc.write(0);
      }
      freq = (abc[24] << 8) + abc[25];
    }
  }
}
void displayClear() {
  PORTC = B00001111;
  PORTB = B00000000;
}
void progressDot(byte index) {
  setChr(255, true);
  selDig(index);
}
void displayFreq() {
  int val = freq; //cuts off fraction
  switch (cnt) {
    case 0:
      if (val > 999) {
        setChr((val % 10000) / 1000, false);
      } else {
        setChr(255, false);
      }
      selDig(1);
      cnt = 1;
      break;
    case 1:
      if (val > 99) {
        setChr((val % 1000) / 100, false);
      } else {
        setChr(255, false);
      }
      selDig(2);
      cnt = 2;
      break;
    case 2:
      if (val > 9) {
        setChr((val % 100) / 10, false);
      } else {
        setChr(255, false);
      }
      selDig(3);
      cnt = 3;
      break;
    case 3:
      setChr(val % 10, false);
      selDig(4);
      cnt = 0;
  }
}

void selDig(byte index) {
  switch (index) {
    case 1:
      PORTC = B00001110;
      break;
    case 2:
      PORTC = B00001101;
      break;
    case 3:
      PORTC = B00001011;
      break;
    case 4:
      PORTC = B00000111;
  }
}

void setChr(byte chr, bool dot) {
  switch (chr) {
    case 255:  //clear
      PORTB = B00000000;
      break;
    case 0:
      PORTB = B11111100;
      break;
    case 1:
      PORTB = B01100000;
      break;
    case 2:
      PORTB = B11011010;
      break;
    case 3:
      PORTB = B11110010;
      break;
    case 4:
      PORTB = B01100110;
      break;
    case 5:
      PORTB = B10110110;
      break;
    case 6:
      PORTB = B10111110;
      break;
    case 7:
      PORTB = B11100000;
      break;
    case 8:
      PORTB = B11111110;
      break;
    case 9:
      PORTB = B11100110;
      break;
      /*
        case 10:  //A
        case 11:  //B
        case 12:  //C
        case 13:  //D
        case 14:  //E
        case 15:  //F
        case 16:  //G
        case 17:  //H
      */
  }
  if (dot) {
    PORTB |= B00000001;
  }
}
subjectivefrequencytransducer photo 0 subjectivefrequencytransducer photo 1 subjectivefrequencytransducer schematics

Rara Avis

2016-09-08 11:53 other

Got this...

ALKU99 cassette

It's a project generating bird calls with code.

alkualkualkualkualkualkualkualkualkualku.org/pmwiki/pmwiki.php/Main/ALKU99

Thank you Roc, Anna and Joe for all the hard work releasing this.


Tamas

2016-09-01 14:32 electronics

Here another project. It's a box for replacing some old MIDI hardware. It can read 16 analogue sensors, 16 digital sensors and control 16 LEDs via PWM. Basically, it is just a Teensy 3.2 plus a PWM breakout board from SparkFun. To easily access all the pins on the Teensy, I also used the excellent Teensy 3.2 Breakout Board R3 by Daniel Gilbert.

Attached are schematics, Teensy code, and SuperCollider and MaxMSP code for dealing with the serial communication.

tamas circuit photo 1 tamas circuit photo 2

Updates:

Attachments:
TamasSerial.sc
tamas_teensy_schematics.png
tamas_teensy.ino
tamas_test.maxpat

MP3 Speakers

2016-08-29 13:33 electronics

For an upcoming installation, we need a few speakers hanging on the walls playing back sound files in a loop. I found the DFPlayer Mini to work well. It's a small and cheap board that can play sound files from an SD card and it has a built-in 3W amplifier. With a decent 5V power bank (here 4400mAh) it can run for ~4 days continuously.

mp3speakers

Example Class

2016-05-04 23:30 supercollider

Here is a very basic SuperCollider class I wrote as an example of why and how to write classes.

//save this as MySequencerTrack.sc in your extensions folder and recompile
MySequencerTrack {
  var <steps;
  var <>array;
  *new {|steps= 16|
    ^super.newCopyArgs(steps).init;
  }
  init {
    array= Array.fill(4, {Array.fill(steps, 0)});  //4 here because of the four params: amp, freq, mod, pan
  }

  //array get/set
  amps {^array[0]}
  amps_ {|arr| array[0]= arr}
  freqs {^array[1]}
  freqs_ {|arr| array[1]= arr}
  mods {^array[2]}
  mods_ {|arr| array[2]= arr}
  pans {^array[3]}
  pans_ {|arr| array[3]= arr}

  //single value get/set
  amp {|index| ^array[0][index]}
  amp_ {|index, val| array[0].put(index, val)}
  freq {|index| ^array[1][index]}
  freq_ {|index, val| array[1].put(index, val)}
  mod {|index| ^array[2][index]}
  mod_ {|index, val| array[2].put(index, val)}
  pan {|index| ^array[3][index]}
  pan_ {|index, val| array[3].put(index, val)}
}

And here is some test code for it...

a= MySequencerTrack.new;
a.freqs
a.steps
a.amps= {1.0.rand}!a.steps
a.amps
a.amp(10)  //first one
//and then the same for a.freqs etc

(
s.waitForBoot{
  a.amps= {[0.5, 0.25, 0, 0, 0].choose}!a.steps;
  a.freqs= {[60, 66, 70].choose.midicps}!a.steps;
  a.mods= {1.0.linrand}!a.steps;
  a.pans= {1.0.rand2}!a.steps;
  b= {|freq= 400, amp= 0, mod= 0, pan= 0| Pan2.ar(SinOsc.ar(freq, SinOsc.ar*mod, amp), pan)}.play;
  s.sync;
  r= Routine.run({
    inf.do{
      a.steps.do{|i|
        b.set(\freq, a.freq(i), \amp, a.amp(i), \mod, a.mod(i), \pan, a.pan(i));
        0.125.wait;
      };
    };
  });
};
)

//and while it is running...  replace freqs
a.freqs= {[52, 66, 70, 80].choose.midicps}!a.steps;
a.amp_(0, 1)  //set first amp to 1.0
a.amps
a.amps= a.amps.rotate(-1)  //rotate amps left
a.freqs= a.freqs.rotate(2) //rotate freqs right
a.freqs= a.freqs+10  //transpose up
a.freqs= a.freqs.scramble //reorder

r.stop
b.free

//now the important thing and why classes are good...
//here we make 10 tracks all 32 values in length...
~mysequencer= {MySequencerTrack(32)}!10;
~mysequencer[0].amps  //amplitudes for first track
~mysequencer[0].amps= {1.0.rand}!a.steps
~mysequencer[0].amps
~mysequencer[0].amp(0)  //first one

(
s.waitForBoot{
  10.do{|i|
    var steps= ~mysequencer[i].steps;
    ~mysequencer[i].amps= {[0.5, 0.25, 0, 0, 0, 0, 0, 0].choose}!steps;
    ~mysequencer[i].freqs= {[60, 66, 70, 90].choose.midicps}!steps;
    ~mysequencer[i].mods= {1.0.linrand}!steps;
    ~mysequencer[i].pans= {1.0.rand2}!steps;
  };
  ~synths= {
    {|freq= 400, amp= 0, mod= 0, pan= 0| Pan2.ar(SinOsc.ar(freq, SinOsc.ar*mod, amp/2), pan)}.play;
  }!10;
  s.sync;
  r= ~mysequencer.collect{|trk, i|
    var syn= ~synths[i];
    Routine.run({
      inf.do{|j|
        var x= j%trk.steps;
        syn.set(\freq, trk.freq(x), \amp, trk.amp(x), \mod, trk.mod(x), \pan, trk.pan(x));
        0.125.wait;
      };
    });
  };
};
)

//while the above is running
~mysequencer[0..7].do{|trk| trk.amps= 0!32}  //mute all tracks except 8&9
~mysequencer[8..9].do{|trk| trk.mods= {4.0.linrand}!32}  //more fmod on tracks 8&9
~mysequencer[0..1].do{|trk, i| trk.freqs= {i+1*150}!32; trk.mods= {0}!32; trk.amps= {|j| [0, 1].wrapAt(i+j)}!32} //renew and add tracks 0&1
~mysequencer[8..9].do{|trk| trk.amps= {0.75.linrand*[1, 0].choose}!32}  //new amps for track 8&9
~mysequencer[8..9].do{|trk| trk.freqs= {8.linrand+1*150}!32}  //new freqs for track 8&9
~mysequencer[6].amps= {0.4}!9++({0}!23); ~mysequencer[6].freqs= {|i| 2**i*50+50}!32; //add arpeggio on track 6

~mysequencer.do{|x| x.freqs= x.freqs*1.1} //transpose all frequencies
~mysequencer.do{|x| x.amps= x.amps.rotate(-3)} //rotate all amps

r.do{|x| x.stop}
~synths.do{|x| x.free}

ESP8266 OpenSound Control Teensy

2016-04-30 17:29 electronics

Today I ported my Open Sound Control ESP8266 example for Arduino to run on a Teensy 3.

The version below is a bit simpler but still works the same. It is just to show how to send and receive OSC messages directly in SuperCollider or MaxMSPJitter.

Teensy code:

//f0 150705 - modified for Teensy3 160430
//sending and receiving UDP OSC with an ESP8266
//for Teensy + ESP8266 with firmware 0.9.5.2
#define WLAN_SSID  "ssid"
#define WLAN_PASS  "pass"
#define WLAN_ADDR  "192.168.1.3" //laptop running SC EDIT
#define ADDR "/tap" //incoming OSC addy
#define PORT  1112  //incoming OSC port
uint8_t buf[16];
char indata[12];
char inbuffer[256];
char OKrn[] = "OK\r\n";
byte wait_for_esp_response(int timeout, char* term = OKrn) {
  unsigned long t = millis();
  bool found = false;
  int i = 0;
  int len = strlen(term);
  while (millis() < (t + timeout)) {
    if (Serial1.available()) {
      inbuffer[i++] = Serial1.read();
      if (i >= len) {
        if (strncmp(inbuffer + i - len, term, len) == 0) {
          found = true;
          break;
        }
      }
    }
  }
  inbuffer[i] = 0;
  return found;
}
void setup() {
  //--OSC message
  buf[0] = 47;   // /
  buf[1] = 115;  // s
  buf[2] = 116;  // t
  buf[3] = 105;  // i
  buf[4] = 0;
  buf[5] = 0;
  buf[6] = 0;
  buf[7] = 0;
  buf[8] = 44;   // ,
  buf[9] = 105;  // i
  buf[10] = 0;
  buf[11] = 0;
  buf[12] = 4;   // a
  buf[13] = 3;   // b
  buf[14] = 2;   // c
  buf[15] = 0;   // d
  pinMode(23, OUTPUT);
  Serial.begin(115200);   //USB serial for feedback
  delay(400);
  Serial1.begin(115200);  //Teensy hardware pins 0 and 1
  Serial.println("starting");
  Serial.print("hard reset...");
  pinMode(4, OUTPUT);
  delay(10);
  pinMode(4, INPUT);
  Serial.print("ready...");
  boolean resp = wait_for_esp_response(1000, "ready\r\n");
  Serial.println(resp);
  Serial.print("mode1...");
  Serial1.println("AT+CWMODE=1");
  resp = wait_for_esp_response(1000);
  Serial.println(resp);
  Serial.print("connecting...");
  do {
    Serial1.print("AT+CWJAP=\"");
    Serial1.print(WLAN_SSID);
    Serial1.print("\",\"");
    Serial1.print(WLAN_PASS);
    Serial1.println("\"");
    resp = wait_for_esp_response(3000);
    Serial.print(resp);
  } while (!resp);
  Serial.print("\nmux1...");
  Serial1.println("AT+CIPMUX=1");
  resp = wait_for_esp_response(1000);
  Serial.println(resp);
  Serial.print("udp...");
  Serial1.print("AT+CIPSTART=4,\"UDP\",\"");
  Serial1.print(WLAN_ADDR);
  Serial1.print("\",57120,");
  Serial1.print(PORT);
  Serial1.println(",0");
  resp = wait_for_esp_response(1000);
  Serial.println(resp);
  Serial.println("setup done");
}
void loop() {
  if (wait_for_esp_response(1000, "\r\n+IPD,4,16:")) {
    if (wait_for_esp_response(1000, ADDR)) {
      Serial1.readBytes(indata, 12);
      buf[12] = indata[8] + 1; //add one to incomming values
      buf[13] = indata[9] + 1;
      buf[14] = indata[10] + 1;
      buf[15] = indata[11] + 1;
      Serial.println(int(indata[8]));
      Serial.println(int(indata[9]));
      Serial.println(int(indata[10]));
      Serial.println(int(indata[11]));
      Serial1.println("AT+CIPSEND=4,16");
      if (wait_for_esp_response(1000, "> ")) {
        Serial1.write(buf, sizeof(buf));
        if (wait_for_esp_response(1000)) {
          Serial.println("reply sent!");
        }
      }
    }
  }
}

SuperCollider code:

(
//--call&response
var send= 0, last= Main.elapsedTime;
OSCdef(\sti, {|msg, time, addr|
  //should receive the values you sent +1
  ([msg[1]>>24, (msg[1]>>16)&255, (msg[1]>>8)&255, msg[1]&255]).post;
  (" % sec since last: %, % sec since sent").format(addr, time-last, time-send).postln;
  last= time;
}, \sti);
n= NetAddr("192.168.1.4", 1112); //esp8266 ip address EDIT
f= {|id, on, hi, lo| (id&255<<24)|(on&255<<16)|(hi&255<<8)|(lo&255)};
r= Routine.run({
  inf.do{|i|
    n.sendMsg(\tap, f.value(4, 3, i.asInteger%256, 1));
    send= Main.elapsedTime;
    0.5.wait;
  };
});
)

Note: my new and better way to do this is described in this post: /f0blog/f0led/

Updates:

Attachments:
teensy_esp8266_osc.maxpat.zip

GreenPeace

2016-03-29 15:35 supercollider

Here's a handy class for SuperCollider. It's an audio clipping detector loosely based on Batuhan Bozkurt's StageLimiter.

To install it download and extract the zip file into your SuperCollider extensions folder. Then recompile and type...

GreenPeace.activate

Now as soon as you play a sound that's clipping (i.e. exceeds -1.0 or 1.0), the class will warn you.

To turn it off type...

GreenPeace.deactivate
Attachments:
GreenPeace.zip

«  …4 5 6 7 8 9 10 »