Se connecter
Se connecter

ou
Créer un compte

ou

Sujet Conception électronique: MIDI in et micro-contrôleur (arduino)

  • 23 réponses
  • 6 participants
  • 1 384 vues
  • 7 followers
1 Conception électronique: MIDI in et micro-contrôleur (arduino)
Salut à tous.

Ca fait quelques années que je pratique l'électronique en tant qu'amateur: réalisation de quelques mini synthés analogiques et numériques.

Je bloque néanmoins sur la conception d'un MIDI in.

Je m'explique: j'ai déjà réalisé des circuits qui pouvaient se synchroniser sur une horloge MIDI, ou pouvaient recevoir des notes MIDI. (6n138 + résistances + diode + micro-contrôleur.)

Cependant ça ne fonctionnait pas toujours très bien... il y avait des notes pas toujours jouées par exemple.


Côté micro-contrôleur j'utilise la gamme Arduino, comme la Uno qui tourne à 16Mhz ou la Due qui tourne à 84MHz. En ce qui concerne la programmation j'ai tendance à bien charger la mule et je pousse souvent ces cartes dans leurs retranchements.


Au final ma question est très simple.

Dans un synthétiseur numérique, vous semble t'il judicieux de dédier un micro-contrôleur à la réception et au filtrage des données MIDI ?

Les arduinos sont capables de lire les données MIDI grâce aux commandes "serial" (qui tournent ici à 31250 bauds). Cependant j'ai l'impression que cette tâche peut être polluée par les autres interruptions de mes programmes, provoquant des pertes de données MIDI.

Quelle solution est habituellement utilisée par les fabricants? un uc dédié ou pas?

Après je suis complètement autodidacte, je ne saisis pas toutes les subtilités de la programmation ... :lol:


Merci à ceux qui pourront m'éclairer. :bravo:

[ Dernière édition du message le 15/12/2018 à 20:52:08 ]

2
Si je devais le faire, je ferais un driver spécial et non des calls a la lib Serial: une petite machine a états qui lit les bytes bruts, filtre/parse les données, crée et passe uniquement les messages intéressants a l’application au travers d'une FIFO.
Arduino, si je ne m'abuse, est construit sur C++.
Une petite classe MIDI devrait faire l'affaire - d'ailleurs, il n'y a vraiment pas de module MIDI dans la lib d'arduino?

Pour les problèmes que tu décris, probable qu'une classe MIDI qui se brancherait directement sur le registre data de l'UART serait plus efficace.
Je ne sais pas comment fonctionne la lib Serial de l'arduino, mais je serais étonné qu'il n'y ait pas au moins un buffer entre l'UART et ton appli.
Le coup des notes non jouées me fait quand meme bien penser a un buffer overrun: les données arrivent plus rapidement que la vitesse a laquelle ton programme les traite.

Resistance is not futile... it's voltage divided by current

[ Dernière édition du message le 15/12/2018 à 21:05:10 ]

3
Citation de aaB :
Le coup des notes non jouées me fait quand meme bien penser a un buffer overrun: les données arrivent plus rapidement que la vitesse a laquelle ton programme les traite.


Oui ça peut ressembler à ça.
Par exemple j'ai une interruption (ISR sur l'arduino Due) qui tourne en boucle à 64Khz. L'audio y est synthétisé en temps réel (oscillateurs, enveloppes, filtre 3 étages, delay... bref les calculs sont gourmands ... :) ).
Les autres fonctions, dont le MIDI sont traitées en second lieu ( "void loop()" sur l'arduino ) et donc moins rapidement.

Extrait de ma "void loop()" pour se synchroniser sur le MIDI:

MidiInput = Serial1.read ();

if ( MidiInput == 240 ) {
MidiPulseCounter = MidiPulseCounter + 1;
if (MidiPulseCounter == 6) {
MidiPulseCounter = 0;
}
}



Citation de aaB :
Si je devais le faire, je ferais un driver spécial et non des calls a la lib Serial: une petite machine a états qui lit les bytes bruts, filtre/parse les données, crée et passe uniquement les messages intéressants a l’application au travers d'une FIFO.


Là tu m'as perdu. :lol:

Il existe des librairies MIDI in pour Arduino qui assument ne pas être toujours réactives.
Pour la carte que j'utilise principalement, la Due qui tourne à 84Mhz, les librairies sont plus rares et les commandes un peu différentes. Je n'ai pas réussi à faire fonctionner le MIDI avec ça.

En tout cas, si je te comprends bien, pour toi la solution devrait être logicielle et non matérielle?
4
Citation de King :

Par exemple j'ai une interruption (ISR sur l'arduino Due) qui tourne en boucle à 64Khz. L'audio y est synthétisé en temps réel (oscillateurs, enveloppes, filtre 3 étages, delay... bref les calculs sont gourmands ...

A te lire, j'ai l'impression que tu as une interruption pour chaque échantillon audio et que tu calcules chaque échantillon dans l'ISR (du DAC?).
Si c'est le cas, c'est le premier problème a régler.
La manière habituelle, c'est d'utiliser un canal DMA entre un buffer (en RAM) et le registre data du DAC, et d'agir sur une interruption générée par le DMA (par exemple quand le buffer est a moitie vide) pour remplir le buffer.
Le DMA se charge de passer les échantillons un a un au DAC (sur interruption du DAC), ce qui te donne plus de temps entre les interruptions pour faire le reste du travail (calculer les échantillons de sortie, scanner les contrôles en façade, traiter les données MIDI, etc.).

Oui, ta solution est a coup sur logicielle, le Cortex-M3 sur lequel est base l'Arduino Due, est largement assez puissant pour faire un synthe (j'imagine VA) qui ne loupe aucune note MIDI et reste synchro.

Resistance is not futile... it's voltage divided by current

5
Citation de aaB :

A te lire, j'ai l'impression que tu as une interruption pour chaque échantillon audio et que tu calcules chaque échantillon dans l'ISR (du DAC?).
Si c'est le cas, c'est le premier problème a régler.

Absolument, c'est ainsi que travaillent mes programmes.

Citation de aaB :
La manière habituelle, c'est d'utiliser un canal DMA entre un buffer (en RAM) et le registre data du DAC, et d'agir sur une interruption générée par le DMA (par exemple quand le buffer est a moitie vide) pour remplir le buffer.

Jamais entendu parler de DMA mais je vois l'idée. Dans ce cas les calculs seraient effectués dans le void loop() ?
Après je me trompe peut-être mais j'ai tendance à penser que les calculs prennent toujours le même temps, que soit dans l'ISR ou ailleurs. Au final le temps libéré devrait être le même non? Ok il sera moins morcelé.


Citation de aaB :

Oui, ta solution est a coup sur logicielle, le Cortex-M3 sur lequel est base l'Arduino Due, est largement assez puissant pour faire un synthe (j'imagine VA) qui ne loupe aucune note MIDI et reste synchro.


Merci, ça c'est de la bonne info. je sais ce qu'il me reste à creuser. :lol:

Un exemple: https://www.youtube.com/watch?v=UO5aa68ijFg

[ Dernière édition du message le 16/12/2018 à 10:58:30 ]

6
Citation de King :

Jamais entendu parler de DMA mais je vois l'idée. Dans ce cas les calculs seraient effectués dans le void loop() ?
Après je me trompe peut-être mais j'ai tendance à penser que les calculs prennent toujours le même temps, que soit dans l'ISR ou ailleurs. Au final le temps libéré devrait être le même non? Ok il sera moins morcelé.

DMA: Direct Memory Access
C'est un mécanisme qui permet d'automatiser le transfert de données entre un périphérique et la RAM (ou entre deux adresses en RAM).
En gros tu le configures en lui donnant l’adresse a laquelle se trouvent tes données en RAM et, sur interruption du périphérique, le DMA va transmettre les données sans intervention de ta part.
Ta responsabilité est seulement de faire en sorte que les données soient disponibles lorsque le DMA en a besoin (et le DMA lui-même peut générer des interruptions pour que tu ne rate aucune deadline).
Pour l'utiliser, la datasheet de ton MCU est ta meilleure amie.
Vu que tu utilises l'arduino, il te faudra aussi vérifier quels canaux DMA sont potentiellement déjà utilisés par le système lui-même.
Peut-être qu'un utilisateur arduino pourrait venir compléter ici (je ne suis pas moi-même arduiniste, mais je programme des MCUs dans mon boulot; mes infos ne peuvent qu’être généralistes et non spécifiques à ta plateforme).

Au niveau du temps, c'est un peu plus complexe que ça:
* oui, le temps nécessaire au calcul est le même
* par contre, il ne faut pas négliger le temps nécessaire aux accès mémoire
* et surtout, ne pas oublier le temps nécessaire aux interruptions: a chaque fois qu'une interruption pète, on doit sauvegarder l’état des registres du processeur, traiter l'interruption, puis recharger les registres pour continuer ce qu'on faisait avant. On appelle ça un context switch.
Rendre le calcul "moins morcelé" comme tu dis, revient a avoir moins de context switchs et sera plus efficace.

Ramener ton calcul audio dans ta main loop permet, entre autres, de garder des ISR le plus simples (donc rapides) possible:
* on calcule les échantillons, le plus vite possible, et on remplit le buffer de sortie
* tant que le buffer est plein, on ne fait rien (ce qui laisse du temps pour le reste du système)
* des que le buffer est vide et on recommence.

Le plus souvent, on utilisera une stratégie de double buffering (je ne connais pas de DMA qui ne soit pas configurable pour fonctionner ainsi):
* on utilise deux buffers de sortie, l'un contrôlé par le DMA, l'autre disponible pour l'application
* l'application remplit son buffer puis attend
* l'ISR du DMA "échange" les buffers (change l'adresse contenue dans un pointeur) et signale a l'application qu'un nouveau buffer de sortie est disponible.

Avec un buffer de taille N et une fréquence d'échantillonnage Fs, on dispose de N / Fs secondes pour calculer un buffer, traiter les autres interruptions, etc. avant la prochaine interruption du DMA.

Resistance is not futile... it's voltage divided by current

7
On peut voir le DMA comme un co-processeur spécialisé dans les copies de données (en mémoire, ou entre mémoire et périphérique).

Et je confirme tout ce que dit aaB. Faire du traitement dans une routine d'interruption n'est pas une bonne idée, il est préférable de garder une telle routine la plus courte possible. Le double buffering est très utilisé dans tout ce qui traite du signal en temps réel (audio, image, radar ...)
Si tu as une FIFO matérielle dans un périphérique, elle a souvent une source d'interruption "presque vide", qui permet de démarrer un remplissage via DMA avant qu'elle soit vraiment vide.
8
Super, merci les gars. :bravo:


D'habitude les datasheet ne me font pas trop peur mais c'est une autre histoire pour les micro-contrôleurs. :)

Bon je vais creuser tout ça... en croisant les doigts pour tomber sur un extrait de code pour la Due.
9
Oui, les Cortex-M ne sont pas ce qui se fait de plus simple comme MCU.
En prenant ton temps, tu devrais quand même t'en sortir, dans mes souvenirs la doc de chez Atmel est en général bien faite.
La dernière foisque j'ai utilisé un SAM4, c'etait il y a 4 pu 5 ans mais je me souviens qu'il y avait pas mal d'exemples de code sur le site d'Atmel.

Resistance is not futile... it's voltage divided by current

10
Merci Aab. :)

C'est vrai qu'Atmel présente bien ses produits, ça devrait m'aider à rentrer dans la doc.

Sous ces éclaircissement je vais pouvoir finaliser le hardware en cours avant d'étudier mieux tout ça.