Sonic Pi - Controlando via MIDI

Sonic Pi MIDI improv

Parâmetros do Sonic Pi podem ser manipulados com controladores externos do padrão MIDI. Esse sistema proporciona sistemas táteis que estimulas a exploração de possibilidades sonoras com o código, transformando o programa em um instrumento físico.

A forma que implementei para este controle é composta de duas partes:
• Um programa feito no ambiente Processing, que recebe os parâmetros do hardware e grava os valores em arquivos de texto. Precisa da biblioteca The MIDI Bus para Processing.
• Código Ruby rodando dentro do Sonic Pi que lê e utiliza os valores nos arquivos de texto.

Código Java para ser rodado no Processing 3:

// saves MIDI data to text files,
// to be read by Sonic Pi
// Alexandre Rangel (www.quasecinema.org)
// runs on Processing 3.1.1
// to config for your system,
// change MIDI values and directory info

import themidibus.*; // www.smallbutdigital.com/themidibus.php

String myPath = "/Users/rangel/";
String myFile;
MidiBus myBus; // The MidiBus
PFont f;
int number = 0;
int value;
float[] volume = {1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0};
int[] step   = {127,127,127,127,127,127,127,127};
float[] fx     = {1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0};
int num;
float x,z;

void setup() {
  frameRate(30); //frameRate = 30;
  size(120, 600); 
  MidiBus.list();
  myBus = new MidiBus(this, 2, 2);
  f = createFont("Arial", 17); textFont(f);
}

void draw() {
  background (0);
 
  // top sliders
  for (int num = 1; num < 9; num = num+1) {
    stroke(150); fill(20);
    rect(6,(num*33)-19,105,22); // base
    noStroke(); fill(80);
    x = map(volume[num-1],0,1,0,104);
    rect(7,(num*33)-18,x,21); // fill
    fill(255);
    text("K"+num+" "+nfs(step[num-1],3)+ "  "+nf(volume[num-1],1,2),8,num*33);
  } // end for

  //bottom sliders
  for (int num = 1; num < 9; num = num+1) {
      stroke(150); fill(20);
      rect(6,((num+9)*33)-19,105,22); // base
      noStroke(); fill(80);
      z = map(fx[num-1],0,1,0,104);
      rect(7,((num+9)*33)-18,z,21); // fill
      fill(255);
      text("S"+num+" "+nfs(step[num-1],3)+ "  "+nf(fx[num-1],1,2),8,(num+9)*33);
} // end for
  
} // end draw


void controllerChange(int channel, int number, int valueMIDI) {
  //println("--------"); // uncomment these 3 lines to find out
  //println("Number:"+number); // your MIDI controller sliders and knobs numbers
  //println("Value:"+value);   // get printed here. Use these  for configuration
  
  String[] valueStringInteger = {str(valueMIDI)}; // 0 to 127 MIDI range
 
  float tempValue = map(valueMIDI,0,127,0.0,1.0);
  String[] valueStringFloat = {str(tempValue)}; // 0.0 to 1.0 range
 

  // your MIDI values go into case options
  switch (number) {
    // volume
    case 8:
      myFile = myPath + "botao1i.txt"; saveStrings(myFile,valueStringInteger);
      myFile = myPath + "botao1f.txt"; saveStrings(myFile,valueStringFloat);
      volume[0] = tempValue;
      step[0] = valueMIDI;
      break;
    case 9:
      myFile = myPath + "botao2i.txt"; saveStrings(myFile,valueStringInteger);
      myFile = myPath + "botao2f.txt"; saveStrings(myFile,valueStringFloat);
      volume[1] = tempValue;
      step[1] = valueMIDI;
      break;
    case 10:
      myFile = myPath + "botao3i.txt"; saveStrings(myFile,valueStringInteger);
      myFile = myPath + "botao3f.txt"; saveStrings(myFile,valueStringFloat);
      volume[2] = tempValue;
      step[2] = valueMIDI;
      break;
    case 11:
      myFile = myPath + "botao4i.txt"; saveStrings(myFile,valueStringInteger);
      myFile = myPath + "botao4f.txt"; saveStrings(myFile,valueStringFloat);
      volume[3] = tempValue;
      step[3] = valueMIDI;
      break;
    case 12:
      myFile = myPath + "botao5i.txt"; saveStrings(myFile,valueStringInteger);
      myFile = myPath + "botao5f.txt"; saveStrings(myFile,valueStringFloat);
      volume[4] = tempValue;
      step[4] = valueMIDI;
      break;
    case 13:
      myFile = myPath + "botao6i.txt"; saveStrings(myFile,valueStringInteger);
      myFile = myPath + "botao6f.txt"; saveStrings(myFile,valueStringFloat);
      volume[5] = tempValue;
      step[5] = valueMIDI;
      break;
    case 14:
      myFile = myPath + "botao7i.txt"; saveStrings(myFile,valueStringInteger);
      myFile = myPath + "botao7f.txt"; saveStrings(myFile,valueStringFloat);
      volume[6] = tempValue;
      step[6] = valueMIDI;
      break;
    case 15:
      myFile = myPath + "botao8i.txt"; saveStrings(myFile,valueStringInteger);
      myFile = myPath + "botao8f.txt"; saveStrings(myFile,valueStringFloat);
      volume[7] = tempValue;
      step[7] = valueMIDI;
      break;
      
    // FXs
    case 32:
      myFile = myPath + "slider1i.txt"; saveStrings(myFile,valueStringInteger);
      myFile = myPath + "slider1i.txt"; saveStrings(myFile,valueStringFloat);
      fx[0] = tempValue;
      break;
    case 33:
      myFile = myPath + "slider2i.txt"; saveStrings(myFile,valueStringInteger);
      myFile = myPath + "slider2f.txt"; saveStrings(myFile,valueStringFloat);
      fx[1] = tempValue;
      break;
    case 34:
      myFile = myPath + "slider3i.txt"; saveStrings(myFile,valueStringInteger);
      myFile = myPath + "slider3f.txt"; saveStrings(myFile,valueStringFloat);
      fx[2] = tempValue;
      break;
    case 35:
      myFile = myPath + "slider4i.txt"; saveStrings(myFile,valueStringInteger);
      myFile = myPath + "slider4f.txt"; saveStrings(myFile,valueStringFloat);
      fx[3] = tempValue;
      break;
    case 36:
      myFile = myPath + "slider5i.txt"; saveStrings(myFile,valueStringInteger);
      myFile = myPath + "slider5f.txt"; saveStrings(myFile,valueStringFloat);
      fx[4] = tempValue;
      break;
    case 37:
      myFile = myPath + "slider6i.txt"; saveStrings(myFile,valueStringInteger);
      myFile = myPath + "slider6f.txt"; saveStrings(myFile,valueStringFloat);
      fx[5] = tempValue;
      break;
    case 38:
      myFile = myPath + "slider7i.txt"; saveStrings(myFile,valueStringInteger);
      myFile = myPath + "slider7f.txt"; saveStrings(myFile,valueStringFloat);
      fx[6] = tempValue;
      break;
    case 39:
      myFile = myPath + "slider8i.txt"; saveStrings(myFile,valueStringInteger);
      myFile = myPath + "slider8f.txt"; saveStrings(myFile,valueStringFloat);
      fx[7] = tempValue;
      break;

  } // end case
} // end controllerChange


Segue código Ruby para ser rodado no Sonic Pi.
As variáveis de b1i até b8i disponibilizam valores dos canais MIDI em números inteiros, de 1 a 127.
As variáveis de b1f até b8f disponibilizam valores dos canais MIDI em números fracionais, de 0.1 a 1.0.
Observe como as variáveis usadas são inicializadas (com valor 0) fora dos loops.

# exemplo MIDI 1
# Sonic Pi 2.10

b1i = 0

live_loop :midi do
  b1i = File.read("/Users/rangel/botao1i.txt").to_i
  sleep 1
end

live_loop :notas do
  puts b1i
  with_bpm b1i + 60 do
    use_synth :fm
    play b1i
    sample :bd_klub, amp: 4
    sleep 1
  end
end

# exemplo MIDI 2
# Sonic Pi 2.10

b1i = 0
s1i = 0

live_loop :midi do
  b1i = File.read("/Users/rangel/botao1i.txt").to_i  # botao  1
  s1i = File.read("/Users/rangel/slider1i.txt").to_i # slider 1
  sleep 1
end

live_loop :notas do
  puts b1i
  with_bpm 120 do
    #use_synth (ring :saw,:pulse,:prophet,:beep)[s1i]
    use_synth synth_names[s1i]
    #play b1i
    play scale(b1i/3, :spanish).choose
    sleep 1
  end
end

Exercício:
Que outros parâmetros podem ser manipulados com um controlador?
Faça uma composição com 8 parâmetros controlados via MIDI.