Outils pour utilisateurs

Outils du site


clapping_music_for_robots

Différences

Ci-dessous, les différences entre deux révisions de la page.

Lien vers cette vue comparative

Les deux révisions précédentesRévision précédente
Prochaine révision
Révision précédente
clapping_music_for_robots [2022/03/14 09:29] Simon Deplatclapping_music_for_robots [2022/03/14 11:15] (Version actuelle) Simon Deplat
Ligne 13: Ligne 13:
  
 **Une clave de //x// temps est répétée en boucle par la première voix.** **Une clave de //x// temps est répétée en boucle par la première voix.**
-** + 
-Une seconde voix commence à jouer la clave de manière synchrone. Tous les Y cycles, la seconde voix "omet" un temps, ce qui décale la clave par rapport à la première voix.**+**Une seconde voix commence à jouer la clave de manière synchrone. Tous les Y cycles, la seconde voix "omet" un temps, ce qui décale la clave par rapport à la première voix.**
  
 De fait, tous les X * Y cycles, les deux voix finissent par se recaler l'une l'autre. De fait, tous les X * Y cycles, les deux voix finissent par se recaler l'une l'autre.
Ligne 21: Ligne 21:
 L'étudiante **souhaite utiliser un [[https://tutos.labomedia.org/books/renforcement-informatique/page/debuter-lelectronique-et-la-programmation-avec-larduino|Arduino]] afin de piloter deux servomoteurs qui jouent les deux voix** mentionnées ci-dessus. L'étudiante **souhaite utiliser un [[https://tutos.labomedia.org/books/renforcement-informatique/page/debuter-lelectronique-et-la-programmation-avec-larduino|Arduino]] afin de piloter deux servomoteurs qui jouent les deux voix** mentionnées ci-dessus.
  
-Dans sa première version, elle a eu une idée géniale : déclarer deux boucles différentes pour chacun des servos, et indiquer de manière brute la partition en assignant les temps d'attentes à l'aide de la fonction //delay()// .+Dans sa première version, elle a eu une idée géniale : **déclarer deux boucles différentes pour chacun des servos, et indiquer de manière brute la partition en assignant les temps d'attentes à l'aide de la fonction //delay()//** .
  
 Malheureusement, cette manière de faire nécessite de taper 4 lignes de code par note et par voix, soit, ben en fait, beaucoup trop de lignes. Malheureusement, cette manière de faire nécessite de taper 4 lignes de code par note et par voix, soit, ben en fait, beaucoup trop de lignes.
Ligne 27: Ligne 27:
 Plus grave encore, il n'est en fait **pas possible de déclarer deux boucles différentes dans l'Arduino** ! Cela impliquerait du //multithreading//, et la puce n'a qu'un cœur... Plus grave encore, il n'est en fait **pas possible de déclarer deux boucles différentes dans l'Arduino** ! Cela impliquerait du //multithreading//, et la puce n'a qu'un cœur...
  
-L'étudiante, qui a décidément une âme de hackeuse, a donc intuitionné qu'elle pouvait **utiliser deux Arduinos pour créer la polyrythmie**. C'est en effet faisable, mais peu être un peu //too much//... De plus, je suspecte qu'ils se désynchronisent assez rapidement, rendant la pièce inaudible.+L'étudiante, qui a décidément une âme de hackeuse, a donc intuitionné qu'elle pouvait **utiliser deux Arduinos pour créer la polyrythmie**. C'est en effet faisable, mais peut-être un peu //too much//... De plus, je suspecte qu'ils se désynchronisent assez rapidement, rendant la pièce inaudible. 
 + 
 +=====Partition à raie===== 
 +Je lui ai donc pointé du doigt quelque chose de presque magique : **il est possible de considérer une //Array// comme une partition**. 
 + 
 +Si nous prenons l'exemple suivant : 
 + 
 +<code> 
 +[ 1, 0, 0, 1, 0, 0, 1, 0 ] 
 +</code> 
 + 
 +On peut y voir **une représentation d'une partition rythmique représentant la clave ||: Noire pointée - Noire pointée - Noire :||** si l'on itère régulièrement dessus, ne jouant une note que si l'index est égal à 1. 
 + 
 +On peut étendre ce principe plus loin, en modifiant le //décodeur// pour réagir au contenu de l'//Array//, par exemple : 
 + 
 +Un séquenceur probabiliste : 
 + 
 +<code> 
 +[ 1, 0, 0.5, 1, 0, 0.5, 1, 0.25 ] 
 +</code> 
 + 
 +Ou des durée de notes : 
 + 
 +<code> 
 +[ 1, 0, 0.5, 1, 0, 0.5, 1, 0.25 ] 
 +</code> 
 + 
 +Mais du coup, **pas les deux en même temps** si le contenu de l'//Array// est une valeur seule (sinon on met des //Arrays// dans l'//Array//). 
 + 
 +Un dernier exemple, la basse d'Alberti, en Do, en MIDI : 
 + 
 +<code> 
 +[ 60, 67, 64, 67 ] 
 +</code> 
 + 
 +=====écalageD===== 
 +Dans cette optique, j'ai indiqué à l'étudiante que la meilleure manière de faire était de stocker **la clave initiale dans une Array, puis de déclarer une seconde Array qui serait reconstruite à chaque fin de cycles pour représenter la nouvelle partition de la voix II**. 
 + 
 +Elle m'a rétorqué qu'en fait, **vu qu'on accède à l'//Array// par un index, il suffisait de décaler cet index pour décaler la partition**... 
 + 
 +**Ce qui est tout-à-fait vrai, et même pertinent en terme algorithmique.** Cela pose un problème de dépassement car le décalage demandera à un moment d'accéder à un index supérieur à la taille de l'//Array//, mais ce problème se règle assez facilement (nous le verrons par la suite). 
 + 
 +Cependant, je lui ai conseillé dans un premier temps de **reconstruire la seconde voix de manière algorithmique pour que l'//Array// de la seconde voix corresponde effectivement à chaque instant à la partition réellement jouée par cette voix**. 
 + 
 +=====Représentation===== 
 +Cette orientation était avant tout didactique : je pense qu'il est mieux d'**apprendre en premier lieu à résoudre concrètement un problème équivalent à sa représentation conceptuelle**, avant d'ajouter un hack. 
 + 
 +Je laisse ici de côté la solution de reconstruction de la bonne partition à chaque changement pour me concentrer sur **les solutions alternatives, qui nous éloignent de la manière dont on conçoit la partition habituellement**. 
 + 
 +La première solution pour résoudre le problème est la suivante : 
 + 
 +Nous commençons par dupliquer notre partition : 
 + 
 +{{ ::2barsreich.png?600 |}} 
 + 
 +Si vous y réfléchissez, la dernière note est d'ailleurs superflue. 
 + 
 +Maintenant, voici comme est décrite notre partition en terme algorithmique : 
 + 
 +{{ ::reichoffset.png?600 |}} 
 + 
 +À chaque fin de cycles, on augmente le décalage de la seconde voix, et on le remet à zéro s'il atteint le nombre de temps de la clave initiale. 
 + 
 +Ce qui semble étrange, c'est que **cette méthode introduit une asymétrie de représentation des deux voix**. Une information supplémentaire est nécessaire pour situer la voix II : son décalage par rapport à l'origine. 
 + 
 +Ce n'est pas le cas avec la représentation "reconstruite"
 + 
 +{{ ::reichisomorph.png?600 |}} 
 + 
 +**Les deux font pourtant référence au même objet.** La première solution est efficiente en terme de calcul. La seconde est plus facile à comprendre pour les humains. 
 + 
 +Voici le code associé à cet algorithme, qui, à la place de servomoteurs, fait clignoter des LEDs : 
 + 
 +<code> 
 +int ledPin1 = 2; 
 +int ledPin2 = 3; 
 + 
 +int msBetweenBeats = 200; 
 + 
 +int initialScore[] = { 1, 0, 0, 1, 0, 0, 1, 0 }; 
 +int score[16]; 
 + 
 +int currentBeat = 0; 
 + 
 +int numberOfBeats; 
 + 
 +int currentCycleRepetition = 0; 
 +int numberOfCycleRepetitions = 4; 
 +int currentCycle = 0; 
 + 
 +void setup() { 
 +  Serial.begin(9600); 
 +   
 +  pinMode( ledPin1, OUTPUT ); 
 +  pinMode( ledPin2, OUTPUT ); 
 + 
 +  numberOfBeats = sizeof( initialScore ) / sizeof( initialScore[0] ); 
 + 
 +  for( int i = 0; i < ( numberOfBeats * 2 ); i++ ) { 
 +    score[ i ] = initialScore[ i%numberOfBeats ]; 
 +  }; 
 + 
 +
 + 
 +void loop() { 
 + 
 +  Serial.println( score[ currentBeat + currentCycle ] ); 
 + 
 +  if( score[ currentBeat ] == 1 ) { 
 +    digitalWrite( ledPin1, HIGH ); 
 +  }; 
 + 
 +  if( score[ currentBeat + currentCycle ] == 1 ) { 
 +    digitalWrite( ledPin2, HIGH ); 
 +  }; 
 + 
 +  delay( msBetweenBeats / 4 * 3 ); 
 + 
 +  digitalWrite( ledPin1, LOW ); 
 +  digitalWrite( ledPin2, LOW ); 
 + 
 +  delay( msBetweenBeats / 4 ); 
 + 
 +  currentBeat = currentBeat + 1; 
 +   
 +  if( currentBeat == numberOfBeats ) { 
 +     
 +    currentBeat = 0; 
 +    currentCycleRepetition = currentCycleRepetition + 1; 
 +     
 +    if( currentCycleRepetition == numberOfCycleRepetitions ) { 
 +        currentCycleRepetition = 0; 
 +        currentCycle = currentCycle + 1; 
 +         
 +        if( currentCycle == numberOfBeats ) { 
 +          currentCycle = 0; 
 +        }; 
 +      }; 
 +    }; 
 +
 +</code> 
 + 
 +=====Physiologie ?===== 
 +Pour aller encore plus loin, il faut noter que **le duplicata de l'//Array// n'est même pas nécessaire**. 
 + 
 +Avec cet algorithme, **il n'est même plus possible de représenter adéquatement la "partition" de la deuxième voix  à l'aide d'un dessin**, puisqu'elle est dépendante de l'utilisation du modulo (%) qui est une opération logique effectuée sur des électrons : 
 + 
 +<code> 
 +noteAcutelleDeLaDeuxièmeVoix = score[ ( currentBeat + currentCycle ) % numberOfBeats ]; 
 +</code> 
 + 
 +Encore une fois pourtant, l'effet réel est le bon et les LEDs jouent en rythme. 
 + 
 +Cela nous questionne sur **la définition de la partition**.  
 + 
 +**Doit-elle être considérée comme un système de symbole purement informatifs, ou comme un //objet// agissant sur le musicien de telle sorte à le faire jouer correctement ?** 
 + 
 +**La version "reconstruite" à l'aide de deux //Arrays// semble pencher du première côté**, étant très descriptive, même si l'inspection du contenu des //Arrays// agit effectivement sur les LEDs. Cela n'est cependant pas immédiat : la partition est d'abord "écrite", puis "lue" //a posteriori// pour déclencher le clignotement. 
 + 
 +À l'inverse, **dans le cas de la version "modulo" de l'algorithme, nous sommes beaucoup plus proche de la seconde hypothèse.** Ici, l'algorithme ressemble plus à une description de la manière dont le corps doit se comporter pour jouer la partition. La partition est en quelque sorte "cachée" dans l'algorithme, et bien qu'elle puisse être déduite de l'algorithme, elle n'est pas accessible de manière immédiate. Par ailleurs, contrairement au premier exemple, il n'y a aucune information donnée //a priori//, et l'Arduino a sa propre méthode pour savoir trouver la note qu'il doit jouer au moment même où il doit la jouer. 
 + 
 +<code> 
 +int ledPin1 = 2; 
 +int ledPin2 = 3; 
 + 
 +int msBetweenBeats = 200; 
 + 
 +int score[] = { 1, 0, 0, 1, 0, 0, 1, 0 }; 
 + 
 +int currentBeat = 0; 
 + 
 +int numberOfBeats; 
 + 
 +int currentCycleRepetition = 0; 
 +int numberOfCycleRepetitions = 4; 
 +int currentCycle = 0; 
 + 
 +void setup() { 
 +  Serial.begin(9600); 
 +   
 +  pinMode( ledPin1, OUTPUT ); 
 +  pinMode( ledPin2, OUTPUT ); 
 + 
 +  numberOfBeats = sizeof( score ) / sizeof( score[0] ); 
 + 
 +
 + 
 +void loop() { 
 + 
 +  Serial.println( score[ currentBeat + currentCycle ] ); 
 + 
 +  if( score[ currentBeat ] == 1 ) { 
 +    digitalWrite( ledPin1, HIGH ); 
 +  }; 
 + 
 +  if( score[ ( currentBeat + currentCycle ) % numberOfBeats ] == 1 ) { 
 +    digitalWrite( ledPin2, HIGH ); 
 +  }; 
 + 
 +  delay( msBetweenBeats / 4 * 3 ); 
 + 
 +  digitalWrite( ledPin1, LOW ); 
 +  digitalWrite( ledPin2, LOW ); 
 + 
 +  delay( msBetweenBeats / 4 ); 
 + 
 +  currentBeat = currentBeat + 1; 
 +   
 +  if( currentBeat == numberOfBeats ) { 
 +     
 +    currentBeat = 0; 
 +    currentCycleRepetition = currentCycleRepetition + 1; 
 +     
 +    if( currentCycleRepetition == numberOfCycleRepetitions ) { 
 +        currentCycleRepetition = 0; 
 +        currentCycle = currentCycle + 1; 
 +         
 +        if( currentCycle == numberOfBeats ) { 
 +          currentCycle = 0; 
 +        }; 
 +      }; 
 +    }; 
 +
 +</code>
clapping_music_for_robots.1647250148.txt.gz · Dernière modification : 2022/03/14 09:29 de Simon Deplat