Soundskulptur mit einem ESP32 und der Mozzi Bibliothek bauen

Das hier ist erstmal eine Minimal-Dokumentation, da ich selbst nicht so schnell fündig geworden bin.

Der Anschluss der Sensoren und Ausgänge sieht wie folgt aus:

  • Basis des Projekts ist ein NodeMCU ESP32
  • Lichtwiderstandsmodul (LDR-Sensor Iduino SE012) an 3,3V, GND und Signal an Pin 4
  • Infrarot-Abstandssensor von Sharp GP2Y0A21YK0F an 3,3V, GND und Signal an Pin 2
  • Die Ausgangsbuchse, in die der Kopfhörer kommt ist mit GND zu GND verbunden und der Tip der Buchse geht am ESP32 an Pin 25, den DAC-Pin (Digital Analog Wandler). Auf dem Weg zum Pin 25 ist noch ein 10k Potentiometer verkabelt, um die Lautstärke regeln zu können. Außerdem ist zwischen Pin 25 und GND noch ein kleiner unpolarisierter Kondensator geschaltet.

Wichtig: Ich bin absolut kein Experte für Sounderzeugung. Das Exponat dient nur der Inspiration. Sollte jemand von euch Verbesserungsvorschläge haben, gerne per Mail an mich -> Kontakt

Der Code auf Basis des Mozzi-Beispiels

/*
  Plays a fluctuating ambient wash in response to light and temperature sensors, using Mozzi sonification library.
  Basis: Mozzi Example MultiOsc

  8 control rate oscillators are used to set the volume of 8 audio oscillators.
  Temperature readings from a thermistor are used to set the notes
  being played, and light readings from a light dependent resistor are
  mapped to the pulse rates of the volume control oscillators.

  Circuit:
    ESP32

    Audio output am digital Pin 25 am ESP32
    check the README or http://sensorium.github.io/Mozzi/

   An Pin 2: Infrarot Abstandssensor

   An Pin 4: Lichtwiderstands-Modul

    Light dependent resistor (LDR) and 5.1k resistor on analog pin 2:
      LDR from analog pin to +5V (3.3V on Teensy 3.1)
      5.1k resistor from analog pin to ground

  Mozzi documentation/API
  https://sensorium.github.io/Mozzi/doc/html/index.html

  Mozzi help/discussion/announcements:
  https://groups.google.com/forum/#!forum/mozzi-users

  Tim Barrass 2013, CC by-nc-sa.
*/

#include <MozziGuts.h>
#include <Oscil.h>
#include <tables/cos8192_int8.h>
#include <mozzi_midi.h>

#define CONTROL_RATE 256


#define NUM_VOICES 8
#define THRESHOLD 15 // Nils: Original war 10

// harmonics
Oscil<COS8192_NUM_CELLS, AUDIO_RATE> aCos1(COS8192_DATA);
Oscil<COS8192_NUM_CELLS, AUDIO_RATE> aCos2(COS8192_DATA);
Oscil<COS8192_NUM_CELLS, AUDIO_RATE> aCos3(COS8192_DATA);
Oscil<COS8192_NUM_CELLS, AUDIO_RATE> aCos4(COS8192_DATA);
Oscil<COS8192_NUM_CELLS, AUDIO_RATE> aCos5(COS8192_DATA);
Oscil<COS8192_NUM_CELLS, AUDIO_RATE> aCos6(COS8192_DATA);
Oscil<COS8192_NUM_CELLS, AUDIO_RATE> aCos7(COS8192_DATA);
Oscil<COS8192_NUM_CELLS, AUDIO_RATE> aCos0(COS8192_DATA);

// volume controls
Oscil<COS8192_NUM_CELLS, CONTROL_RATE> kVol1(COS8192_DATA);
Oscil<COS8192_NUM_CELLS, CONTROL_RATE> kVol2(COS8192_DATA);
Oscil<COS8192_NUM_CELLS, CONTROL_RATE> kVol3(COS8192_DATA);
Oscil<COS8192_NUM_CELLS, CONTROL_RATE> kVol4(COS8192_DATA);
Oscil<COS8192_NUM_CELLS, CONTROL_RATE> kVol5(COS8192_DATA);
Oscil<COS8192_NUM_CELLS, CONTROL_RATE> kVol6(COS8192_DATA);
Oscil<COS8192_NUM_CELLS, CONTROL_RATE> kVol7(COS8192_DATA);
Oscil<COS8192_NUM_CELLS, CONTROL_RATE> kVol0(COS8192_DATA);

// audio volumes updated each control interrupt and reused in audio till next control
char v1,v2,v3,v4,v5,v6,v7,v0;


// Nils: Das sind Midi-Noten, kann man einfach googeln
// https://computermusicresource.com/midikeys.html
float onenotes[NUM_VOICES] = {
  mtof(36.f),mtof(52.f),mtof(70.f),mtof(48.f),mtof(64.f),mtof(67.f),mtof(79.f),mtof(84.f)};  


float twonotes[NUM_VOICES] = {
  mtof(43.f),mtof(50.f),mtof(55.f),mtof(60.f),mtof(62.f),mtof(67.f), mtof(72.f), mtof(72.f)}; 


float threenotes[NUM_VOICES] = {
  mtof(40.f),mtof(51.f),mtof(69.f),mtof(47.f),mtof(63.f),mtof(66.f),mtof(64.f),mtof(61.f)};

float fournotes[NUM_VOICES] = {
  mtof(50.f),mtof(51.f),mtof(69.f),mtof(70.f),mtof(71.f),mtof(80.f),mtof(81.f),mtof(82.f)};


void setup(){
  //Serial.begin(921600);
  startMozzi(CONTROL_RATE);
}



// Nils
int distanceToFreq(char oscil_num, int distance){

  int freq;
  if (distance<1500 || distance > 3500){
    //Serial.println("1notes");
    freq = onenotes[oscil_num];
    }
  else if (distance >= 1500 && distance < 2000) {
    //Serial.println("2notes");
    freq = twonotes[oscil_num];    
    }
  else if  (distance >= 2000 && distance < 2500) {
    //Serial.println("3notes");
    freq = threenotes[oscil_num];    
    }
   else {
     //Serial.println("4notes");
     freq = fournotes[oscil_num];
  }
  return freq;
}


void updateControl(){
  

  int distance = analogRead(2);

  
  int light = analogRead(4); 
  light = 4096 - light; // Mehr Licht = höhere Werte für die Variable light
  if (light<100) {
    light = 100; // Minimalwert
    }
 

  // map light reading to volume pulse frequency
  float pulse_freq = (float)light/2048; //Nils: Original war /256 aber ESP32 liefert 12bit ADC -> bis zu 4096


  v0 = kVol0.next();
  v1 = kVol1.next();
  v2 = kVol2.next();
  v3 = kVol3.next();
  v4 = kVol4.next();
  v5 = kVol5.next();
  v6 = kVol6.next();
  v7 = kVol7.next();

  // set one note oscillator frequency each time (if it's volume is close to 0)
  static char whoseTurn;
  //Serial.println("whoseTurn");
  switch(whoseTurn){
    
  case 0:
    kVol0.setFreq(pulse_freq);
    //Serial.println("whoseTurn 0");
    if(abs(v0)<THRESHOLD) aCos0.setFreq(distanceToFreq(0,distance));
    break;

  case 1:
    kVol1.setFreq(pulse_freq);
    if(abs(v1)<THRESHOLD) aCos1.setFreq(distanceToFreq(1,distance));
    break;

  case 2:
    kVol2.setFreq(pulse_freq);
    if(abs(v2)<THRESHOLD) aCos2.setFreq(distanceToFreq(2,distance));
    break;

  case 3:
    kVol3.setFreq(pulse_freq);
    if(abs(v3)<THRESHOLD) aCos3.setFreq(distanceToFreq(3,distance));
    break;

  case 4:
    kVol4.setFreq(pulse_freq);
    if(abs(v4)<THRESHOLD) aCos4.setFreq(distanceToFreq(4,distance));
    break;

  case 5:
    kVol5.setFreq(pulse_freq);
    if(abs(v5)<THRESHOLD) aCos5.setFreq(distanceToFreq(5,distance));
    break;

  case 6:
    kVol6.setFreq(pulse_freq);
    if(abs(v6)<THRESHOLD) aCos6.setFreq(distanceToFreq(6,distance));
    break;

  case 7:
    kVol7.setFreq(pulse_freq);
    if(abs(v7)<THRESHOLD) aCos7.setFreq(distanceToFreq(7,distance));

    break;
  }

  if(++whoseTurn>=NUM_VOICES) whoseTurn = 0;
}


const float volume_reduction = 0.3; // von Nils
// Nils: *0.3 eingefügt, damit der Ausgang nicht übersteuert (da ich keinen Widerstand zum Testen hatte und volle Röhre in die Kopfhörer gegangen bin...)
AudioOutput_t updateAudio(){
  long asig = (long)
    aCos0.next()*v0*volume_reduction +
    aCos1.next()*v1*volume_reduction +
    aCos2.next()*v2*volume_reduction +
    aCos3.next()*v3*volume_reduction +
    aCos4.next()*v4*volume_reduction +
    aCos5.next()*v5*volume_reduction +
    aCos6.next()*v6*volume_reduction +
    aCos7.next()*v7*volume_reduction;
  return MonoOutput::fromAlmostNBit(18, asig);
}



void loop(){
  audioHook();
}