Introduction: Controlador MIDI Theremin Ultrasónico

En este proyecto, hemos creado un controlador MIDI Theremin utilizando Arduino y sensores ultrasónicos. Inspirados en el icónico instrumento electrónico de Leon Theremin, hemos combinado la creatividad musical con la innovación tecnológica para ofrecer una experiencia única.

 

Nuestro dispositivo utiliza sensores ultrasónicos para detectar la posición de las manos, permitiendo a los usuarios controlar la altura y el volumen del sonido en tiempo real. Con Arduino como base, hemos desarrollado un sistema que convierte estos movimientos en señales MIDI, lo que proporciona una interfaz versátil y adaptable para la creación musical.

 

Lo mejor de todo es que hemos integrado este controlador MIDI Theremin con el software Ableton Live. Esta conexión nos permite no solo controlar la altura y el volumen, sino también explorar una amplia gama de sonidos y efectos disponibles en Ableton Live. Desde sintetizadores hasta instrumentos de percusión, la combinación de nuestro controlador con Ableton Live desbloquea un mundo de posibilidades musicales.

 

A través de este tutorial, te guiaremos paso a paso en la construcción de tu propio controlador MIDI Theremin, desde la configuración de los componentes hasta la programación del Arduino y la integración con Ableton Live.

Supplies

Hardware

1 Arduino Uno R3

1 Led Rojo

1 Led Azul

1 Led Verde

3 resistencias de 330 ohm

6 resistencias de 1k ohm

2 sensores de distancia ultrasónico

6 pulsadores


Software

Arduino IDE 2.2.1 (Software | Arduino)

loopMIDI (loopMIDI | Tobias Erichsen (tobias-erichsen.de))

Hairless MIDI (The Hairless MIDI<->Serial Bridge (projectgus.github.io))

Ableton Live 11 Lite (Creative tools for music makers | Ableton)

Step 1: Montaje Del Circuito

Montaje del Circuito para el Theremin Musical Interactivo

En la siguiente sección, presentamos un esquemático detallado que ilustra cómo ensamblar el circuito completo. Para facilitar la comprensión, dividimos el montaje en tres partes principales: el sensor de control de volumen, los pulsadores y LEDs, y finalmente, los sensores que modulan las notas musicales.


1. Sensor de Control de Volumen:

  Situado a la izquierda del circuito, empleamos un sensor ultrasónico característico de Arduino que emite y recibe pulsos para calcular la distancia. Hemos programado este sensor de manera que puedas ajustar el volumen de tu pista de audio según la proximidad de tu mano al sensor.


2. Sensor de Notas Musicales:

 El segundo sensor ultrasónico, ubicado en el circuito, se encarga de modular las notas musicales del instrumento programado. Al igual que el sensor de volumen, responde a la distancia de tu mano, permitiéndote controlar de manera intuitiva las melodías generadas.


3. Pulsadores y LEDs:

  - Primer Pulsador: Controla la grabación y reproducción de tu sesión. Asociado a dos LEDs, el rojo indica la grabación en curso y el azul señala la reproducción de la canción.

  - Segundo Pulsador: Activa el sensor ultrasónico de las notas, replicando el funcionamiento del theremin original. Un LED verde indica su estado activo.

  - Tercer Pulsador: Gestiona los efectos aplicados al instrumento musical, como reverb o ecualización. En nuestro caso, lo utilizamos para activar un arpegiador.


4. Pulsadores de Pistas:

  - Cuarto Pulsador: Asociado a la pista 1.

  - Quinto Pulsador: Asociado a la pista 2.

  - Sexto Pulsador: Asociado a la pista 3.


Cada pulsador de pista se asigna a un instrumento específico para componer la canción deseada. Este diseño modular y su programación intuitiva hacen que nuestro Theremin Musical Interactivo sea accesible para músicos y entusiastas, permitiéndote explorar y expresarte de maneras únicas.

Step 2: Código

int ledPinGrabacion = 7;   // LED ROJO DE GRABACION
int ledPinReprod = 6;     // LED AZUL DE REPRODUCCION
int ledPinON = 5;        // LED VERDE DE ENCENDIDO


int rec_boton = A0;      // BOTON GRABACION
int effect_boton = A1;   // BOTON EFECTOS
int P1_boton = A2;       // BOTON PISTA 1
int P2_boton = A3;       // BOTON PISTA 2
int P3_boton = A4;       // BOTON PISTA 3
int ON_boton = A5;       // BOTON ENCENDIDO


int Echo_TV = 10;        // ECHO THEREMIN VOLUMEN
int Trigger_TV = 11;     // TRIGGER THEREMIN VOLUMEN
int Echo_TN = 8;         // ECHO THEREMIN NOTAS
int Trigger_TN = 9;      // TRIGGER THEREMIN NOTAS


int ledStateG = 0;       // ESTADO DEL LED GRABACION
int ledStateE = 0;       // ESTADO DEL LED EFECTO
int ledState1 = 0;       // ESTADO DEL LED PISTA 1
int ledState2 = 0;       // ESTADO DEL LED PISTA 2
int ledState3 = 0;       // ESTADO DEL LED PISTA 3
int ledStateON = 0;      // ESTADO DEL LED ENCENDIDO


int buttonOldG = 1;      // ESTADO DEL BOTON GRABACION
int buttonOldE = 1;      // ESTADO DEL BOTON EFECTO
int buttonOld1 = 1;      // ESTADO DEL BOTON PISTA 1 
int buttonOld2 = 1;      // ESTADO DEL BOTON PISTA 2
int buttonOld3 = 1;      // ESTADO DEL BOTON PISTA 3
int buttonOldON = 1;     // ESTADO DEL BOTON ENCENDIDO


int buttonNewG,buttonNewE,buttonNew1,buttonNew2,buttonNew3, buttonNewON;  // DIGITAL READ DE LOS BOTONES


int CCG = 0;     // NOTAS CONTROL MIDI GRABACION
int CCE = 1;     // NOTAS CONTROL MIDI EFECTOS
int CC1 = 2;     // NOTAS CONTROL MIDI PISTA 1
int CC2 = 3;     // NOTAS CONTROL MIDI PISTA 2
int CC3 = 4;     // NOTAS CONTROL MIDI PISTA 3
int CCTV = 30;   // NOTAS CONTROL MIDI THEREMIN VOLUMEN


const int aMin = 5;     // ALTURA MINIMA A MAPEAR THEREMIN VOLUMEN
const int aMax = 50;    // ALTURA MAXIMA A MAPEAR THEREMIN VOLUMEN


float distanciaTV;           // DISTANCIA THEREMIN VOLUMEN
float distanciaAnteriorTV;    // DISTANCIA ANTERIOR THEREMIN VOLUMEN
int distanciaTN;              // DISTANCIA THEREMIN NOTAS


int nota;                   // NOTAS DEL THEREMIN


long tiempoTV;      // TIEMPO DE DURACION DEL PULSO (THEREMIN VOLUMEN)
long tiempoTN;      // TIEMPO DE DURACION DEL PULSO (THEREMIN NOTAS)
int valorTV;        // VALOR DE LA ALTURA DEL VOLUMEN


int dt = 100;       // DELAY
int dt1 = 5;        // DELAY


void setup() {
  // put your setup code here, to run once:
  Serial.begin(115200); 


  pinMode(ledPinGrabacion, OUTPUT);   
  pinMode(ledPinReprod, OUTPUT);


  pinMode(rec_boton, INPUT);
  pinMode(effect_boton, INPUT);
  pinMode(P1_boton, INPUT);
  pinMode(P2_boton, INPUT);
  pinMode(P3_boton, INPUT);


  pinMode(Echo_TV, INPUT);
  pinMode(Trigger_TV, OUTPUT);
  pinMode(Echo_TN, INPUT);
  pinMode(Trigger_TN, OUTPUT);
}


void loop() {
  // put your main code here, to run repeatedly:
  buttonNewG = digitalRead(rec_boton);
  buttonNewE = digitalRead(effect_boton);
  buttonNew1 = digitalRead(P1_boton);
  buttonNew2 = digitalRead(P2_boton);
  buttonNew3 = digitalRead(P3_boton);
  buttonNewON = digitalRead(ON_boton); 
  delay(dt);


  //////////////////////////////  GRABACION  /////////////////////////////////
  if(buttonNewG == 0 && buttonOldG == 1){
    if (ledStateG == 0){
      digitalWrite(ledPinGrabacion, 1);
      digitalWrite(ledPinReprod, 0);
      ledStateG = 1;
      Serial.write(176);
      Serial.write(CCG);
      Serial.write(127);
    }
    else {
      digitalWrite (ledPinGrabacion,0);
      digitalWrite (ledPinReprod,1);
      Serial.write(176);
      Serial.write(CCG);
      Serial.write(127); 
      ledStateG = 0;
    }
  }
  buttonOldG=buttonNewG;


  //////////////////////////////  EFECTOS  /////////////////////////////////
  if(buttonNewE == 0 && buttonOldE == 1){
    if (ledStateE == 0){
      ledStateE = 1;
      Serial.write(176);
      Serial.write(CCE);
      Serial.write(127);
    }
    else {
      Serial.write(176);
      Serial.write(CCE);
      Serial.write(0); 
      ledStateE = 0;
    }
  }
  buttonOldE=buttonNewE;


  //////////////////////////////  PISTA 1  /////////////////////////////////
  if(buttonNew1 == 0 && buttonOld1 == 1){
    if (ledState1 == 0){
      ledState1 = 1;
      Serial.write(176);
      Serial.write(CC1);
      Serial.write(127);
    }
    else {
      Serial.write(176);
      Serial.write(CC1);
      Serial.write(127); 
      ledState1 = 0;
    }
  }
  buttonOld1=buttonNew1;


  //////////////////////////////  PISTA 2  /////////////////////////////////
  if(buttonNew2 == 0 && buttonOld2 == 1){
    if (ledState2 == 0){
      ledState2 = 1;
      Serial.write(176);
      Serial.write(CC2);
      Serial.write(127);
    }
    else {
      Serial.write(176);
      Serial.write(CC2);
      Serial.write(127); 
      ledState2 = 0;
    }
  }
  buttonOld2=buttonNew2;


  //////////////////////////////  PISTA 3  /////////////////////////////////
  if(buttonNew3 == 0 && buttonOld3 == 1){
    if (ledState3 == 0){
      ledState3 = 1;
      Serial.write(176);
      Serial.write(CC3);
      Serial.write(127);
    }
    else {
      Serial.write(176);
      Serial.write(CC3);
      Serial.write(127); 
      ledState3 = 0;
    }
  }
  buttonOld3=buttonNew3;


  //////////////////////////////  THEREMIN NOTAS  /////////////////////////////////
  if(buttonNewON == 0 && buttonOldON == 1){
    if (ledStateON == 0){
      digitalWrite(ledPinON, 1);
      ledStateON = 1;
    }
    else {
      digitalWrite(ledPinON, 0);
      ledStateON = 0;
    }
  }
  buttonOldON=buttonNewON;


  if(ledStateON == 1) {    
    digitalWrite(Trigger_TN,LOW); 
    delayMicroseconds(5);
    digitalWrite(Trigger_TN, HIGH);      //envio del pulso para iniciar medicion
    delayMicroseconds(10);           //arduino espera 10 micro segundos


    tiempoTN = pulseIn(Echo_TN, HIGH);      //se mide la duración del pulso
    distanciaTN = int(0.0177 * tiempoTN);      //multiplicamos el tiempo para convertirlo en dista
    nota = map(distanciaTN, 5, 60, 36, 60);


    if (nota > 127) nota = 127;    // limiter
    if (nota < 0) nota = 0;      // limiter para los negativos


    Serial.write(144);
    Serial.write(nota);
    Serial.write(100);
    delay(100);
    Serial.write(144);
    Serial.write(nota);
    Serial.write(0);
    delay(50);
  }


  //////////////////////////////  THEREMIN VOLUMEN  ///////////////////////////////
  digitalWrite(Trigger_TV,LOW); 
  delayMicroseconds(5);
  digitalWrite(Trigger_TV, HIGH);            //envio del pulso para iniciar medicion
  delayMicroseconds(10);                   //arduino espera 10 micro segundos


  tiempoTV = pulseIn(Echo_TV, HIGH);      //se mide la duración del pulso
  distanciaTV = 0.0177 * tiempoTV;        //multiplicamos el tiempo para convertirlo en distancia


  if (abs(distanciaAnteriorTV - distanciaTV) > 0.8) {     //resolución de 8 mm.
    if (distanciaTV <= aMax + 2) {
      valorTV = map(distanciaTV, aMin, aMax, 0, 127);     //mapear alturas mínima y máxima a valores de 0 a 127 


      if (valorTV > 127) valorTV = 127;           // limiter
      if (valorTV < 0) valorTV = 0;              // limiter para los negativos


      Serial.write(176);        
      Serial.write(CCTV);        
      Serial.write(valorTV);     
    }
    distanciaAnteriorTV = distanciaTV;
  }
  delay(dt1);
}


Step 3: Funcionamiento

Aquí os dejamos un vídeo explicativo que muestra como funciona nuestro controlador MIDI theremín.

Por orden:

El primer video explica como funciona el pulsador de grabación y reproducción.

El segundo y el tercero explican como funcionan los sensores ultrasónicos que controlan el volumen y las notas.

El cuarto vídeo enseña el funcionamiento del pulsador de efectos.

Y por último se muestra como funcionan los pulsadores de las pistas.