Bonjour à tous,
j'aimerai vous présenter mon dernier petit projet : TapTempo.
C'est un détecteur de tempo en ligne de commande. L'utilisateur frappe une touche en cadence régulière et le programme en déduit le tempo correspondant. Il est affiché en nombre de battements équivalent par minute (ou BPM en anglais).
La plupart des logiciels audio-numériques ainsi que beaucoup de d'instruments électroniques ont cette fonctionnalité, mais ça me rebutait de lancer un gros logiciel ou de me déplacer vers mon synthé pour vérifier le tempo d'un morceau. Alors TapTempo est né !
Le scénario que je me suis fixé est de pourvoir simplement taper taptempo
dans un terminal, de frapper une touche régulièrement (tout en écoutant un morceau de musique par exemple) et d'obtenir le tempo correspondant à chaque frappe :
> taptempo
Appuyer sur la touche entrée en cadence (q pour quitter).
[Appuyer encore sur la touche entrée pour lancer le calcul du tempo...]
Tempo : 62 bpm
Tempo : 71 bpm
Tempo : 76 bpm
Tempo : 78 bpm
Tempo : 80 bpm
Tempo : 86 bpm
Tempo : 87 bpm
Tempo : 80 bpm q
Au revoir !
Le calcul s'effectue sur les 5 dernières frappes (ou moins si l'utilisateur n'a pas encore frappé 5 fois la touche entrée) pour limiter l'effet des micro-variations d'une frappe à l'autre. Le nombre de frappes enregistrés est remis à zéro au bout de 5 secondes sans interaction avec l'utilisateur. Ceci permet d'éviter que le premier affichage du tempo ne soit erroné dans le cas où le dernier intervalle de temps est très grand à cause de l'inactivité de l'utilisateur.
Ces options peuvent être changées via les arguments du programme :
-h, --help affiche ce message d'aide
-p, --precision changer le nombre de décimale du tempo à afficher
la valeur par défaut est 0 décimales, le max est 5 décimales
-r, --reset-time changer le temps en seconde de remise à zéro du calcul
la valeur par défaut est 5 secondes
-s, --sample-size changer le nombre d'échantillons nécessaires au calcul du tempo
la valeur par défaut est 5 échantillons
-v, --version afficher la version
Concernant le développement à proprement parler, c'est du C++ orienté objet, langage avec lequel je suis le plus à l'aise.
Lors de mes recherches j'ai hésité à utiliser ncurses mais ça m'a paru trop complexe pour ce que je souhaitais réaliser. J'utilise donc directement le terminal. Ceci a comme inconvénients d'afficher ce que l'utilisateur tape et de forcer l'utilisation de la touche entrée pour frapper le tempo. L'avantage c'est la simplicité, pas de dépendances, principe KISS.
De plus j'en ai profité pour mettre en place des outils et des bonnes pratiques issues des logiciels libres:
- licence GPL-3.0+
- génération du projet avec CMake
- traduction en français avec gettext, et potentiellement dans d'autres langues.
- analyse des paramètres d'entrée avec getopt
- génération d'un paquet pour Debian, avec une page de manuel
- génération d'un exécutable Windows via MSYS2 avec mingw-bundledlls pour inclure les dépendances
J'anticipe le seul bémol actuel : le code est hébergé par GitHub, qui n'est pas une forge libre.
N'hésitez pas à tester et à envoyer vos remarques !
# Pas mal !
Posté par gusterhack . Évalué à 3.
Effectivement ça marche bien et c'est une bonne idée ! Sa pourra sûrement me servir quand je veux trouver le rythme d'une chanson.
[^] # Re: Pas mal !
Posté par liberforce (site web personnel) . Évalué à 10.
Le tempo, malheureux !
# Très bonne idée !
Posté par liberforce (site web personnel) . Évalué à 7.
Ce genre d'appli en ligne de commande est une très bonne idée, vu que l'interaction utilisateur est de toute façon très limitée.
En revanche, GPL-3.0+ je trouve ça un peu restrictif pour un outil relativement simple. Tu dois être dans les 300 lignes de code C++, c'est à peu près dans dans ce cas là que la FSF recommande l'Apache-2.0.
Perso j'ai commencé un petit métronome en GTK+, j'ajouterai sans doute une fonction de détection des BPM, c'est effectivement très utile (mais pas de rapport avec la question de la licence, je compte recoder la fonctionnalité moi même ;-)). Pour le build system j'ai pris Meson (et apprécié la rapidité de configuration sous Windows par rapport à CMake), et pour la partie ligne de commande j'utilise en général les fonctionnalités de la glib.
J'avais testé la compatibilité Windows sous MSYS2, mais je ne connaissais pas mingw-bundledlls. Ça a l'air pas mal mais seulement adapté aux projets comme les tiens avec des dépendances purement à des bibliothèques. Ça ne résoud pas les dépendances à des ressources (configuration, icônes, thèmes). Une autre possibilité pour créer un bundle est de créer un package MSYS2 pour ton appli et de l'installer avec ses dépendances dans un préfixe spécifique. Tu n'as ensuite plus qu'à livrer le dossier en question. Je n'ai pas encore essayé en pratique mais ça se fait.
Et je ne dirai rien sur github ;). Merci d'avoir partagé ton projet :)
[^] # Re: Très bonne idée !
Posté par Kangs . Évalué à 4.
Aux fils des corrections/évolutions il y aura peu être plus de lignes de code, donc bon…
[^] # Re: Très bonne idée !
Posté par liberforce (site web personnel) . Évalué à 7.
Il me semble qu'il y a aussi un caractère d'innovation mis en avant par la FSF. Par tu peux mettre une bibliothèque qui fait des choses basiques en GPL-3, mais en pratique ça ne sert pas à grand chose, ça freine la redistribution alors qu'il y a plein d'alternatives qui font la même chose (ou mieux) sous des licenses plus libérales (BSD, MIT). Mais après chacun décide de faire ce qu'il veut avec son code :).
[^] # Re: Très bonne idée !
Posté par Kangs . Évalué à 4.
Oui, ton raisonnement se tient et me convient.
[^] # Re: Très bonne idée !
Posté par mzf (site web personnel) . Évalué à 3. Dernière modification le 19 février 2018 à 16:52.
Je ne connaissais cette recommandation de la FSF, merci pour ce retour !
Qu'est-ce qui compte ? les 230 lignes de C++ ou les 440 lignes au total ? ;-)
[^] # Re: Très bonne idée !
Posté par liberforce (site web personnel) . Évalué à 3.
C'est toi l'auteur, c'est toi qui vois ;-). Je suis pas sûr que la FSF oblige qui que ce soit à utiliser une licence à 299 LOC et une autre à 301 LOC donc c'est laissé à ton appréciation :-p
# dynamique sonore
Posté par kaliko (site web personnel) . Évalué à 4.
C'est un poil hors sujet, mais connaissez vous un outil similaire pour calculer la dynamique (en temps réel ou sur des fichiers son) ?
[^] # Re: dynamique sonore
Posté par Padishah . Évalué à 7.
DR14 T.meter
# Pas besoin de ncurses
Posté par Damien Thébault . Évalué à 10.
Pas besoin de ncurses pour cacher les touches et utiliser autre chose que entrée. Ça peut se faire avec tcsetattr(), en désactivant le mode 'canonical' et les différents ECHO.
Typiquement:
Si le but est d'être compatible windows, il faut cependant mettre ça sous ifdef (par exemple _MSC_VER) car c'est différent là-bas.struct termios tios = {0};
tcgetattr(0, &tios);
tios.c_lflag &= ~(ECHO|ECHONL|ICANON);
tcsetattr(0, TCSANOW, &tios);
Le reste a très peu besoin de changer, mais un peu quand même.
Sinon le fichier main.c a des fins de ligne windows contrairement aux autres fichiers.
[^] # Re: Pas besoin de ncurses
Posté par mzf (site web personnel) . Évalué à 3.
Merci pour l'astuce !
# Question et info
Posté par Axioplase ıɥs∀ (site web personnel) . Évalué à 6.
J'utilise généralement https://www.all8.com/tools/bpm.htm qui marche bien.
Question: pourquoi se limiter au 5 dernières frappes? Autant prendre la moyennes de toutes les frappes (en limitant à 100, si tu veux).
Sur des chansons à 200+bpm comme https://www.all8.com/tools/bpm.htm, 5 frappes, c'est pas idéal, je trouve.
[^] # Re: Question et info
Posté par liberforce (site web personnel) . Évalué à 4.
Les deux approches ont leurs avantages et inconvénients. Sur 5 frappes tu es moins précis mais tu peux gérer plus facilement les variations dans un morceau. Sur tout l'historique, c'est bien si ton morceau est joué au métronome mais si le tempo est fluctuant, tu lisses et t'éloignes du tempo original. À ce moment là les 5 dernières frappes est plus utile.
Source: http://blog.fixyourmix.com/category/audio/audio-myths/
Après peut être que les frappes des n dernières secondes plutôt que les n dernières frappes serait plus précis, car ça gère le cas des tempos rapides > 200bpm.
[^] # Re: Question et info
Posté par liberforce (site web personnel) . Évalué à 3.
Ah, l'article à l'origine du diagramme semble plutôt être celui ci:
https://musicmachinery.com/2009/03/02/in-search-of-the-click-track/
C'est un très bon article avec des exemples de morceaux jouées au métronome ou pas et comment les détecter.
[^] # Re: Question et info
Posté par mzf (site web personnel) . Évalué à 2.
Tu peux utiliser l'option -s ou --sample-size pour prendre en compte plus d'échantillons dans le calcul.
# Petite question d'implémentation
Posté par Blackknight (site web personnel, Mastodon) . Évalué à 5.
D'abord, félicitations pour ce petit soft bien sympa.
Ensuite, j'ai une petite question sûrement idiote mais pourquoi as-tu stocké les timestamps de chaque appui de touche pour ne finalement prendre que la première et la dernière valeur et calculer une moyenne ?
N'était-il pas plus simple de ne garder que le nombre d'appuis et le premier et le dernier horodatage ?
J'ai dû louper un truc :D
[^] # Re: Petite question d'implémentation
Posté par mzf (site web personnel) . Évalué à 4.
Merci pour tes félicitations.
Le calcul du tempo est glissant sur les 5 dernières frappes, donc on doit garder les 5 derniers timestamps car lorsqu'un nouveau timestamp arrive (une touche a été pressée), on décale la liste des timestamp. Le 4éme devient le 5éme, le 3éme le 4éme, etc. et le nouveau devient le 1er.
Un petit schéma vaut mieux qu'un grand discours :
On a 5 frappes enregistrées, le calcul du tempo se base sur t et t-4:
Une nouvelle frappe arrive (t+1), le calcul se base maintenant sur t+1 et t-3:
Donc on est obligé de garder l'historique des timestamps sinon on aurait perdu t-3.
Dans l'implémentation, les timestamps sont enregistrés dans une file (std::queue) on ajoute au début la nouvelle frappe et on jette la plus ancienne (t-4 ici).
J'espère que cela a répondu à tes questions :)
[^] # Re: Petite question d'implémentation
Posté par Blackknight (site web personnel, Mastodon) . Évalué à 4. Dernière modification le 22 février 2018 à 13:16.
Merci de l'explication, j'avais pas tout compris effectivement et c'est dommage puisque je me suis amusé à commencer une implémentation à l'identique en Ada ;)
Du coup, il vaut mieux comprendre :D
En tout cas, c'est finalement très logique et ça explique bien les paramètres que l'on peut passer.
Normalement, en Ada, il ne me reste que la gestion des options (autre que le simple argc, argv) et l'internationalisation car ce sont des trucs qu je n'ai jamais bidouillé.
[^] # Re: Petite question d'implémentation
Posté par mzf (site web personnel) . Évalué à 4.
Je suis curieux de voir le résultat. As-tu prévu de rendre le code public ?
[^] # Re: Petite question d'implémentation
Posté par Blackknight (site web personnel, Mastodon) . Évalué à 7. Dernière modification le 22 février 2018 à 14:14.
Bien sûr et ça devrait faire l'objet d'un journal ici-même ;)
Au départ, ce sera une implémentation directe de ton code et après, je regarderai pour mettre ça à la sauce Ada.
[^] # Re: Petite question d'implémentation
Posté par mzf (site web personnel) . Évalué à 2.
Super !
[^] # Re: Petite question d'implémentation
Posté par Blackknight (site web personnel, Mastodon) . Évalué à 4.
Et voilà, c'est fait :)
# J'ai trouvé ce projet marrant
Posté par Boiethios (site web personnel) . Évalué à 3. Dernière modification le 24 février 2018 à 17:26.
Et je me suis amusé à le traduire en Rust (pour le feun):
https://gitlab.com/Boiethios/tempotap
J'ai juste ignoré la traduction et ce qui concerne l'installation.
(Concernant la licence, je n'y connais pas grand' chose, j'espère que je n'ai pas fait d'erreur. J'ai mis le texte de la GPL V3 et j'ai indiqué la provenance du code).
[^] # Re: J'ai trouvé ce projet marrant
Posté par mzf (site web personnel) . Évalué à 3.
Bravo pour cette traduction :)
Le code a l'air plus compact, c'est une caractéristique de Rust ?
Je vois que tu as rajouté la gestion d'erreur si l'écriture sur le terminal échoue. Bonne idée, il faut que je le rajoute !
[^] # Re: J'ai trouvé ce projet marrant
Posté par Boiethios (site web personnel) . Évalué à 3.
J'aurais du mal à détailler toutes les caractéristiques du Rust (ou de n'importe quel langage) en quelques lignes, mais je dirais que si le code est plus concis, c'est dû d'une part aux macros procédurales qui permettent de générer n'importe quel code à la compilation:
Ce code va générer tout le code nécessaire à la gestion des paramètres en ligne de commande par exemple. Ça a la même utilité que les templates en C++, mais c'est plus puissant (tu peux réécrire tout l'arbre syntaxique du code sur lequel s'applique la macro).
La deuxième raison est que le code est orienté expression (comme un langage fonctionnel). On peut écrire des choses comme:
Si j'ai géré le cas d'erreur c'est que c'est obligatoire en Rust (sinon ça génère un warning). En soi, je ne suis pas sûr que ce soit super utile, j'imagine qu'il y a peu de chance que l'écriture sur le terminal échoue.
[^] # Re: J'ai trouvé ce projet marrant
Posté par mzf (site web personnel) . Évalué à 3.
Effectivement ça a l'air puissant ces macros. A voir pour la lisibilité sur des cas plus complexes.
Je ne sais pas ce que fait exactement
std::io::stdout().flush().expect("Error: cannot flush");
mais si ça écrit sur le terminal lors d'une erreur d'écriture, ça sent la boucle infinie :-)# une version python 2.7
Posté par manatlan (site web personnel) . Évalué à 0.
# Bientôt dans votre distribution
Posté par Glandos . Évalué à 4.
On dirait que TapTempo prend son envol : https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=893306
[^] # Re: Bientôt dans votre distribution
Posté par mzf (site web personnel) . Évalué à 3.
Et il cherche un sponsor !
https://lists.debian.org/debian-mentors/2018/03/msg00241.html
:-)
Suivre le flux des commentaires
Note : les commentaires appartiennent à celles et ceux qui les ont postés. Nous n’en sommes pas responsables.