- Edited
Hello, we build an instrument to spacialise sound over 8 output with a bella mini and the expander. We use 5 audio input and build a control panel with 5 channel. Each channel have a Lorlin (6 positions) to chosse an effect and 2 potentiometer to modulate the effect (speed and spread). We use C++ but can't figure how to catch each value of the control panel. We are able to see the value of the first channel (or the last).
Sorry for the not so good english and also for the not so good C++ :')
This is the last version of the code for the control panel
#include <Bela.h>
#include <cmath>
#include <libraries/Scope/Scope.h>
Scope scope;
const int selectPins[] = {0, 1, 2};
const int numChannels = 5;
const int numPositions = 6;
void delayMicroseconds(unsigned int microseconds) {
struct timespec ts;
ts.tv_sec = microseconds / 1000000;
ts.tv_nsec = (microseconds % 1000000) * 1000;
nanosleep(&ts, NULL);
}
void setupMux(BelaContext *context) {
for (int i = 0; i < 3; i++) {
pinMode(context, 0, selectPins[i], OUTPUT);
}
}
void setMux(BelaContext *context, int channel) {
for (int i = 0; i < 3; i++) {
digitalWrite(context, 0, selectPins[i], (channel >> i) & 1);
}
delayMicroseconds(1000);
}
bool setup(BelaContext *context, void *userData) {
setupMux(context);
scope.setup(3, context->audioSampleRate);
return true;
}
float readMuxValue(BelaContext *context, int channel, int analogPin) {
setMux(context, channel);
return analogRead(context, 0, analogPin);
}
int getLorlinIndex(float lorlinValue) {
int index = 0;
float step = 1.0 / numPositions;
for (int i = 0; i < numPositions; i++) {
if (lorlinValue >= (i * step) && lorlinValue < ((i + 1) * step)) {
index = i;
break;
}
}
return index;
}
void render(BelaContext *context, void *userData) {
for (unsigned int n = 0; n < context->audioFrames; n++) {
for (int channel = 0; channel < numChannels; channel++) {
float lorlinValue = readMuxValue(context, channel, 0);
int lorlinIndex = getLorlinIndex(lorlinValue);
float pot1Value = readMuxValue(context, channel, 1);
float pot2Value = readMuxValue(context, channel, 2);
if (n % (context->audioFrames / 2) == 0) {
rt_printf("Channel: %d\n", channel);
rt_printf("Lorlin position: %d\n", lorlinIndex);
rt_printf("Pot 1 value: %f\n", pot1Value);
rt_printf("Pot 2 value: %f\n", pot2Value);
}
}
}
}
void cleanup(BelaContext *context, void *userData) {
}
And this is the effect code, not yet connected to the control panel (full of bug...)
#include <Bela.h>
#include <cmath>
#include <cstdlib>
#include <ctime>
const unsigned int gInputChannels = 5;
const unsigned int gOutputChannels = 8;
unsigned int gCurrentSpeaker[gInputChannels] = {0};
unsigned int gCounter[gInputChannels] = {0};
const unsigned int gThreshold = 44100; // Durée en échantillons pour changer d'enceinte
unsigned int gCurrentThreshold[gInputChannels];
unsigned int gRandomSpeaker[gInputChannels] = {0};
unsigned int gRandomCounter[gInputChannels] = {0};
//const unsigned int gRandomThreshold = 22050; // Durée en échantillons pour changer d'enceinte aléatoirement
enum Function {
ROTATE,
ROTATE_REVERSE,
RANDOM,
ALL,
PENDULUM,
LEFT_RIGHT
};
// Choisir la fonction pour chaque entrée ici
Function gFunctions[gInputChannels] = {
ALL, // entrée 1
ALL, // entrée 2
ROTATE, // entrée 3
RANDOM, // entrée 4
ALL // entrée 5
};
bool gPendulumDirection[gInputChannels] = {false};
float fadeFunction(float distance) {
float exponent = -1.0f; // Plus la valeur est négative, plus le fondu sera progressif
return std::pow(distance, exponent);
}
void nextSpeaker(unsigned int inputChannel) {
if (gFunctions[inputChannel] == ROTATE) {
gCurrentSpeaker[inputChannel] = (gCurrentSpeaker[inputChannel] + 1) % gOutputChannels;
} else if (gFunctions[inputChannel] == ROTATE_REVERSE) {
gCurrentSpeaker[inputChannel] = (gCurrentSpeaker[inputChannel] - 1 + gOutputChannels) % gOutputChannels;
} else if (gFunctions[inputChannel] == PENDULUM) {
if (gPendulumDirection[inputChannel]) {
gCurrentSpeaker[inputChannel]--;
} else {
gCurrentSpeaker[inputChannel]++;
}
if (gCurrentSpeaker[inputChannel] == 0 || gCurrentSpeaker[inputChannel] == gOutputChannels - 1) {
gPendulumDirection[inputChannel] = !gPendulumDirection[inputChannel];
}
} else if (gFunctions[inputChannel] == LEFT_RIGHT) {
gCurrentSpeaker[inputChannel]++;
gCurrentSpeaker[inputChannel] %= (gOutputChannels / 2);
}
}
bool setup(BelaContext *context, void *userData) {
if (context->audioInChannels < gInputChannels || context->audioOutChannels < gOutputChannels) {
rt_printf("Pas assez de canaux d'entrée ou de sortie\n");
return false;
}
std::srand(std::time(0));
for (unsigned int inputChannel = 0; inputChannel < gInputChannels; ++inputChannel) {
gCurrentThreshold[inputChannel] = gThreshold;
}
return true;
}
// Modifier la fonction processInput() pour appliquer le seuil de gain minimum
void processInput(float in, float &out, unsigned int output, unsigned int inputChannel) {
float distance = fmin(std::abs(static_cast<int>(output) - static_cast<int>(gCurrentSpeaker[inputChannel])), gOutputChannels - std::abs(static_cast<int>(output) - static_cast<int>(gCurrentSpeaker[inputChannel])));
//float gain = fadeFunction(distance);
switch (gFunctions[inputChannel]) {
case ROTATE:
case ROTATE_REVERSE:
case PENDULUM:
if (distance == 0) {
out += in;
}
break;
case RANDOM:
if (output == gRandomSpeaker[inputChannel]) {
out += in;
}
break;
case ALL:
out += in;
break;
case LEFT_RIGHT:
if (output == gCurrentSpeaker[inputChannel] || output == (gOutputChannels - 1 - gCurrentSpeaker[inputChannel])) {
out += in;
}
break;
}
}
void render(BelaContext *context, void *userData) {
// Boucle sur tous les échantillons audio dans le buffer
for (unsigned int n = 0; n < context->audioFrames; ++n) {
// Lire les valeurs d'entrée pour chaque canal d'entrée
float input[gInputChannels];
for (unsigned int inputChannel = 0; inputChannel < gInputChannels; ++inputChannel) {
input[inputChannel] = audioRead(context, n, inputChannel);
}
// Boucle sur tous les canaux de sortie (enceintes)
for (unsigned int output = 0; output < gOutputChannels; ++output) {
float out = 0.0f;
// Boucle sur tous les canaux d'entrée et appliquer les effets
for (unsigned int inputChannel = 0; inputChannel < gInputChannels; ++inputChannel) {
processInput(input[inputChannel], out, output, inputChannel);
}
// Écrire la sortie résultante pour le canal de sortie (enceinte) actuel
audioWrite(context, n, output, out);
}
// Boucle sur tous les effets
for (Function effect = ROTATE; effect <= LEFT_RIGHT; effect = static_cast<Function>(static_cast<int>(effect) + 1)) {
// Boucle sur tous les canaux d'entrée
for (unsigned int inputChannel = 0; inputChannel < gInputChannels; ++inputChannel) {
// Si l'effet actuel ne correspond pas à la fonction assignée au canal d'entrée, passez au canal d'entrée suivant
if (gFunctions[inputChannel] != effect) continue;
// Incrémenter le compteur pour le canal d'entrée actuel
gCounter[inputChannel]++;
// Si le compteur atteint le seuil pour le canal d'entrée actuel
if (gCounter[inputChannel] >= gCurrentThreshold[inputChannel]) {
// Réinitialiser le compteur
gCounter[inputChannel] = 0;
// Mettre à jour les positions des haut-parleurs en fonction de l'effet actuel
switch (effect) {
case ROTATE:
gCurrentSpeaker[inputChannel]++;
gCurrentSpeaker[inputChannel] %= gOutputChannels;
break;
case ROTATE_REVERSE:
gCurrentSpeaker[inputChannel]--;
gCurrentSpeaker[inputChannel] = (gCurrentSpeaker[inputChannel] + gOutputChannels) % gOutputChannels;
break;
case RANDOM:
gRandomSpeaker[inputChannel] = std::rand() % gOutputChannels;
break;
case PENDULUM:
if (gPendulumDirection[inputChannel]) {
gCurrentSpeaker[inputChannel]++;
} else {
gCurrentSpeaker[inputChannel]--;
}
gCurrentSpeaker[inputChannel] = (gCurrentSpeaker[inputChannel] + gOutputChannels) % gOutputChannels;
if (gCurrentSpeaker[inputChannel] == 0 || gCurrentSpeaker[inputChannel] == gOutputChannels - 1) {
gPendulumDirection[inputChannel] = !gPendulumDirection[inputChannel];
}
break;
case LEFT_RIGHT:
gCurrentSpeaker[inputChannel]++;
gCurrentSpeaker[inputChannel] %= gOutputChannels / 2;
break;
default:
break;
}
}
}
}
}
}
void cleanup(BelaContext *context, void *userData) {
// Nettoyage du code ici
}
Thanks for your help.