URL: https://linuxfr.org/users/mzf/journaux/taptempo-en-cmake Title: TapTempo en CMake Authors: mzf Date: 2021年10月24日T21:24:13+02:00 License: CC By-SA Tags: taptempo et cmake Score: 43 Cher journal, voilà un certains temps qu’il n’y a pas eu de nouveau portage de [TapTempo](https://linuxfr.org/tags/taptempo/public) alors je propose aujourd’hui une version en [CMake](https://cmake.org/). CMake n’est pas vraiment un langage de programmation, c’est plutôt un [système de construction logicielle multiplateforme](https://fr.wikipedia.org/wiki/CMake), dixit Wikipédia. Cependant, il intègre un langage de script qui permet de piloter la configuration logicielle. J’ai donc essayé de reproduire le comportement de TapTempo avec ce [DSL](https://fr.wikipedia.org/wiki/Langage_d%C3%A9di%C3%A9). Les prérequis pour un portage de TapTempo sont : 1. afficher du texte à l’écran 2. réagir à des entrées utilisateur 3. avoir accès à une horloge monotone précise à la milliseconde 4. pouvoir sauvegarder des valeurs dans des variables 5. pouvoir effectuer des calculs mathématiques simples sur des variables Hélas, CMake ne remplit pas tous ces critères, mais on peut les contourner. Commençons par ceux qui sont présents nativement. En CMake, c’est la commande [`message`](https://cmake.org/cmake/help/latest/command/message.html) qui permet d’afficher du texte à l’écran. Pour effectuer des calculs mathématiques simples on utilise [`math`](https://cmake.org/cmake/help/latest/command/math.html) et on stocke des valeurs arbitraires dans des variables avec les commandes [`set`](https://cmake.org/cmake/help/latest/command/set.html) et [`list`](https://cmake.org/cmake/help/latest/command/list.html). Ça se corse avec l’horloge. CMake possède bien la commande [`string(TIMESTAMP, ...)`](https://cmake.org/cmake/help/latest/command/string.html#timestamp) pour récupérer le temps courant mais son implémentation n’est précise qu’à la seconde. J’ai retrouvé un [ticket demandant une précision à la milliseconde](https://gitlab.kitware.com/cmake/cmake/-/issues/19335) mais qui n’a pas bougé depuis 2 ans. Tant pis pour la précision, ce sera un TapTempoGrossoModo ! Pour pouvoir gérer les tempi au-delà de 60 bpm qui comportent plusieurs frappes par secondes on ne peut pas utiliser l’algorithme original qui se base sur les intervalles entre deux frappes. Pour contourner cette limitation, on va compter le nombre d’entrées utilisateur par seconde puis effectuer le calcul du tempo sur les `N` dernières secondes. Ça fonctionne, mais il faut que l’utilisateur garde le même rythme pendant ces `N` secondes pour être précis, donc impossible de détecter des changements rapides de tempo à moins d’avoir un `N` petit mais avec une précision médiocre. D’ailleurs la formule pour le nombre de secondes nécessaires pour une précision `p` donnée est `N = 60 / p`. Ainsi, avec 10 secondes on a une précision de 6 bpm et pour une précision de 1 bpm il faut enregistrer 60 secondes de frappes. Dans le code ci-dessous j’ai limité à 5 secondes pour une précision de 12 bpm, un peu grossier mais suffisant pour l’exercice. Plus embêtant, CMake ne permet pas d’interagir pendant son exécution. C’est un comportement tout à fait logique pour un logiciel de configuration de construction, mais cela ne m’arrange pas pour ce portage. Encore une solution de contournement : utiliser la commande [`execute_process`](https://cmake.org/cmake/help/latest/command/execute_process.html) pour lancer un mini script `bash` avec la commande : ``` read var echo "$var" ``` On récupère alors l’entrée utilisateur dans la variable CMake pointée dans la commande `execute_process`. Avec ce mécanisme on pourrait même invoquer directement le programme `taptempo` original, mais ce serait de la triche ! Voilà, tout cela mis ensemble ça donne le script ci-dessous. Pour le lancer, il faut le copier dans un fichier `CMakeLists.txt` et exécuter la commande `cmake` avec comme argument le répertoire qui contient ce fichier. ```cmake cmake_minimum_required(VERSION 3.15) project(CMakeTapTempo) find_program(BASH NAMES bash) message("Hit enter key for each beat (q to quit).") set(current_keystroke_count 0) set(keystroke_count_history 0 0 0 0 0) set(last_timestamp 0) set(should_stop "") while(NOT should_stop) execute_process(COMMAND "${BASH}" "-c" [=[ read var echo "$var" ]=] WORKING_DIRECTORY ${CMAKE_CURRENT_LIST_DIR} OUTPUT_VARIABLE user_input RESULT_VARIABLE ret_code OUTPUT_STRIP_TRAILING_WHITESPACE ERROR_STRIP_TRAILING_WHITESPACE ) if(user_input) set(should_stop "1") endif() if(NOT should_stop) string(TIMESTAMP current_timestamp "%s") if(current_timestamp EQUAL last_timestamp) math(EXPR current_keystroke_count "${current_keystroke_count} + 1") else() set(last_timestamp ${current_timestamp}) list(PREPEND keystroke_count_history ${current_keystroke_count}) list(POP_BACK keystroke_count_history) set(current_keystroke_count 1) endif() set(total_keystroke_count 0) foreach(keystroke_count ${keystroke_count_history}) math(EXPR total_keystroke_count "${total_keystroke_count}+${keystroke_count}") endforeach() list(LENGTH keystroke_count_history history_length) math(EXPR tempo "(${total_keystroke_count} * 60) / ${history_length}") message("${tempo} bpm") endif() endwhile() message("Bye Bye!") ``` Sûrement que des experts CMake trouveront à redire et leurs commentaires sont les bienvenus !

AltStyle によって変換されたページ (->オリジナル) /