mardi 7 août 2012

Séquenceur programmable Arduino

Je me suis amusé à faire cette réplique de l'Arduino Punk Console conçu par Dano, auteur de l'excellent site Beavis Audio Research.

Il s'agit d'un séquenceur "8 steps":  vous faites mémorisez à l'Arduino 8 notes musicales, qu'il joue ensuite en boucle à une vitesse réglable. Tout comme le Weird Sound Generator de Music from Outer Space, je classerais ce bidule dans la catégorie "jouets musicaux" (ma fille de 4 ans l'a beaucoup apprécié).

Pour placer une première note en mémoire, vous tournez le potentiomètre "frequency" afin d'ajuster la fréquence à la valeur désirée, et vous appuyez sur le bouton "step 1" .  Vous tournez à nouveau le potentiomètre "frequency" pour changer la fréquence et, lorsqu'elle vous convient, vous appuyez sur le bouton "step 2" pour la mettre en mémoire.  Vous répétez l'opération jusqu'à ce que chacune des 8 notes ait été mémorisée.

Pour jouer le résultat, vous maintenez le bouton "Start/Stop" enfoncé, et vous tournez les potentiomètres "duration" (durée des notes) et "tempo" délai entre deux notes successives jusqu'à ce que le résultat vous plaise!


La page de Beavis Audio concernant ce projet étant déjà très complète, je vous invite à la consulter pour le schéma du circuit et le sketch.  Faites attention à une petite erreur sur le schéma:  la broche libre du potentiomètre "tempo" doit en fait être reliée à 5 V (sinon l'entrée analogique 1 de l'Arduino ne recevra jamais le moindre signal).

De plus, Dano a utilisé un type d'écran LCD spécialement conçu pour le communication série (ce qui économise les pins de l'Arduino).  Puisque je ne dispose pas de ce genre spécifique d'écran LCD (et qu'il n'y a plus assez de pins disponibles pour utiliser un écran LCD conventionnel), j'ai effacé du sketch tout ce qui concernait le LCD.

Toutefois, l'absence d'informations affichées rendait hasardeux l'ajustement des fréquences (on ne pouvait que tourner le potentiomètre à une position aléatoire, ce qui donnait des résultats imprévisibles...).  J'ai donc ajouté une modification supplémentaire au sketch:  pendant que le bouton "step 1" (par exemple) est enfoncé, la note est jouée par le haut-parleur et il est facile de la modifier à votre convenance au moyen du potentiomètre avant de relâcher le bouton.  De cette façon, les 8 notes peuvent être choisies "à l'oreille" (je suis ainsi parvenu à séquencer une gamme à peu près juste).

Voici mon sketch (version modifiée)

/* ======================================================================
Arduino Punk Console
A simple programmable 8 step tone sequencer
by dano/beavisaudio.com
Modified by Y. Pelletier (no LCD)
https://electroniqueamateur.blogspot.com/2012/08/sequenceur-programmable-arduino.html
======================================================================*/
// Map all the input and output pins
#define AnalogInFrequency 1
#define AnalogInTempo 2
#define AnalogInDuration 0
#define DigitalOutSignal 11
#define DigitalInSwitch0 2
#define DigitalInSwitch1 3
#define DigitalInSwitch2 4
#define DigitalInSwitch3 5
#define DigitalInSwitch4 6
#define DigitalInSwitch5 7
#define DigitalInSwitch6 8
#define DigitalInSwitch7 9
#define DigitalInStartStop 10
#define DigitalOutLED 12
// Set up the array for each step
int steps[] = {100,120,140,160,180,200,220,240};
// misc housekeeping
int duration = 50;
int pitchval = 1;
int fPlayMode = true;
int lastPushedStep = -1;
// Initialize the tempo
int tempo = 100;
void setup()
{
// setup pin modes (Digital pins are input by default, but
// I like to set 'em explicitly just so the code is clear.
pinMode (DigitalInSwitch0, INPUT);
pinMode (DigitalInSwitch1, INPUT);
pinMode (DigitalInSwitch2, INPUT);
pinMode (DigitalInSwitch3, INPUT);
pinMode (DigitalInSwitch4, INPUT);
pinMode (DigitalInSwitch5, INPUT);
pinMode (DigitalInSwitch6, INPUT);
pinMode (DigitalInSwitch7, INPUT);
pinMode (DigitalInStartStop, INPUT);
pinMode (DigitalOutSignal, OUTPUT);
pinMode (DigitalOutLED, OUTPUT);
}
void loop()
{
// Main sequence loop
for (int i=0; i<8; i++)
{
// Are we playing or stopping?
fPlayMode = digitalRead (DigitalInStartStop);
digitalWrite (DigitalOutLED, HIGH);
// Check the Hardware
readSwitches();
readPots();
// Make the noise
if (fPlayMode)
{
freqout (steps[i], duration);
}
digitalWrite (DigitalOutLED, LOW);
// Pause between steps
delay (tempo);
}
}
// Read the current values of the pots, called from the loop.
void readPots ()
{
tempo = (analogRead (AnalogInTempo) * 1.9);
duration = (analogRead (AnalogInDuration));
}
// Read the current values of the switches and
// if pressed, replace the switch's slot frequency
// by reading the frequency pot.
void readSwitches()
{
// reset last pushed button number
lastPushedStep = -1;
int frequ = analogRead(AnalogInFrequency);
// check switch 0, if pressed, get the current freq into step 0, etc. etc.
if (digitalRead (DigitalInSwitch0) == HIGH)
{
freqout(frequ, 20);
steps[0] = frequ;
lastPushedStep = 1;
}
else if (digitalRead (DigitalInSwitch1) == HIGH)
{
freqout(frequ, 20);
steps[1] = frequ;
lastPushedStep = 2;
}
else if (digitalRead (DigitalInSwitch2) == HIGH)
{
freqout(frequ, 20);
steps[2] = frequ;
lastPushedStep = 3;
}
else if (digitalRead (DigitalInSwitch3) == HIGH)
{
freqout(frequ, 20);
steps[3] = frequ;
lastPushedStep = 4;
}
else if (digitalRead (DigitalInSwitch4) == HIGH)
{
freqout(frequ, 20);
steps[4] = frequ;
lastPushedStep = 5;
}
else if (digitalRead (DigitalInSwitch5) == HIGH)
{
freqout(frequ, 20);
steps[5] = frequ;
lastPushedStep = 6;
}
else if (digitalRead (DigitalInSwitch6) == HIGH)
{
freqout(frequ, 20);
steps[6] = frequ;
lastPushedStep = 7;
}
else if (digitalRead (DigitalInSwitch7) == HIGH)
{
freqout(frequ, 20);
steps[7] = frequ;
lastPushedStep = 8;
}
}
//freqout code by Paul Badger
// freq - frequency value
// t - time duration of tone
void freqout(int freq, int t)
{
int hperiod; //calculate 1/2 period in us
long cycles, i;
// subtract 7 us to make up for digitalWrite overhead - determined empirically
hperiod = (500000 / ((freq - 7) * pitchval));
// calculate cycles
cycles = ((long)freq * (long)t) / 1000; // calculate cycles
for (i=0; i<= cycles; i++)
{ // play note for t ms
digitalWrite(DigitalOutSignal, HIGH);
delayMicroseconds(hperiod);
digitalWrite(DigitalOutSignal, LOW);
delayMicroseconds(hperiod - 1); // - 1 to make up for fractional microsecond in digitaWrite overhead
}
}

Yves Pelletier (Twitter: @ElectroAmateur)

1 commentaire:

  1. Bonjour, Merci pour ce post, c'est exactement ce dont je cherchais à réaliser. Malheureusement le site de Beavis Audio n'est plus accessible. Est-ce possible d'avoir le schéma du circuit en question ? ainsi que les composants nécessaires ? D'avance je vous remercie. D'une manière générale j'adore votre blog, je le visite assez souvent car il est de qualité. Bonne continuation. Laurent

    RépondreSupprimer