Journal Taptempo en Zig

Posté par  . Licence CC By‑SA.
Étiquettes :
22
3
août
2023

Bonjour Nal,

Entrons tout de suite dans le vif du sujet : j'ai Ă©crit un Taptempo en Zig.
Il ne gÚre aucun argument, n'a aucune fonctionnalité marrante, mais il fonctionne.
Il suffit d'appuyer sur une touche pour l'activer, puis d'appuyer en cadence jusqu'Ă  cinq fois. On peut arrĂȘter l'enregistrement en appuyant sur la touche q, qui permet aussi de quitter le programme.
Le voici donc ci-dessous :

// taptempo.zig
//
//! Copyright 2023 alberic89 <alberic89@gmx.com>
//!
//! This program is free software; you can redistribute it and/or modify
//! it under the terms of the GNU General Public License as published by
//! the Free Software Foundation; either version 3 of the License, or
//! (at your option) any later version.
//!
//! This program is distributed in the hope that it will be useful,
//! but WITHOUT ANY WARRANTY; without even the implied warranty of
//! MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//! GNU General Public License for more details.
//!
//! You should have received a copy of the GNU General Public License
//! along with this program. If not, see <https://www.gnu.org/licenses/>. 
//!
//! Compilé avec succÚs avec zig 0.11.0-dev.4406+d370005d3 (2 août 2023)
//! Retouvez le code source Ă  <https://github.com/alberic89/taptempo-zig>
//
// Pour compiler :
// ```bash
// zig build-exe taptempo.zig
// ```
// Sur une idée de François Mazen
// https://linuxfr.org/users/mzf/journaux/un-tap-tempo-en-ligne-de-commande
//
// Merci Ă  Leon Henrik Plickat pour son exemple d'application zig "Uncooked"
// https://zig.news/lhp/want-to-create-a-tui-application-the-basics-of-uncooked-terminal-io-17gm
//

const std = @import("std");
const stdout = std.io.getStdOut().writer();
const fs = std.fs;
const os = std.os;
const time = std.time;

/// Cette fonction va capturer et calculer le tempo de la frappe au clavier.
/// Peut retourner une erreur.
pub fn captureTempo(tty: fs.File) !void {
    // On enregistre 5 frappes
    var tap: [5]?i64 = [5]?i64{null, null, null, null, null};
    try stdout.print("Capture du tempo", .{});
    for (tap, 0..) |_, index| {
        var buffer: [1]u8 = undefined;
        _ = try tty.read(&buffer);
        // Si la touche q est pressĂ©e, on arrĂȘte d'enregistrer la frappe
         if (buffer[0] == 'q') {
            break;
        } else {
            // La précision d'enregistrement est *au maximum* de l'ordre
            // de la milliseconde, mais peut ĂȘtre plus faible en fonction
            // du matériel et de l'OS
            tap[index] = time.milliTimestamp();
            try stdout.print(".", .{});
        }
    }
    try stdout.print(" Terminé.\n", .{});
    var ecart: [4]?i64 = [4]?i64{null, null, null, null};
    // On calcule l'Ă©cart entre les frappes, en prĂ©voyant le cas oĂč il
    // n'y a pas eu 5 frappes
    for (tap[1..], 0..) |ftime, index| {
        if (ftime != null) {
            ecart[index] = ftime.? - tap[index].?;
        }
    }
    var ecart_moy: ?f64 = null;
    // On calcule l'Ă©cart moyen
    for (ecart) |inter| {
        if (inter != null) {
            if (ecart_moy != null) {
                var inter_f: f64 = @floatFromInt(inter.?);
                ecart_moy = ( ecart_moy.? + inter_f ) / 2;
            } else {
                ecart_moy = @floatFromInt(inter.?);
            }
        }
    }
    // Si il y a eu moins de 2 frappes, on ne peut pas calculer le tempo
    if (ecart_moy == null) {
        try stdout.print("Tu n'as pas le rythme dans la peau !\n", .{});
        return;
    }
    // Le tempo est donné avec un entier en battements par minute
    var bpm: u64 = @intFromFloat((60 * time.ms_per_s) / ecart_moy.?);
    try stdout.print("Tempo : {} bpm\n", .{bpm});
    return;
}

pub fn main() !void {
    // On récupÚre la sortie standart
    var tty = try fs.cwd().openFile("/dev/tty", .{ .mode = .read_write });
    defer tty.close();

    // On enregistre l'Ă©tat du terminal
    const original = try os.tcgetattr(tty.handle);
    var raw = original;

    // On active un certain nombre de paramĂštres :
    //   ECHO: Le terminal n'affiche plus les touches pressées.
    // ICANON: Désactive le mode d'entrée canonique ("cooked").
    //         Permet de lire l'entrée byte-par-byte au lieu de
    //         ligne-par-ligne.
    raw.lflag &= ~@as(
        os.linux.tcflag_t,
        os.linux.ECHO | os.linux.ICANON,
    );
    // BRKINT: DĂ©sactive la conversion de l'envoi de SIGNINT en cas de crash.
    //         N'as normalement pas d'effet sur les systĂšmes modernes.
    //  INPCK: Désactive le contrÎle de la parité.
    //         N'as normalement pas d'effet sur les systĂšmes modernes.
    // ISTRIP: DĂ©sactive la suppression du 8Ăšme bit des caractĂšres.
    //         N'as normalement pas d'effet sur les systĂšmes modernes.
    raw.iflag &= ~@as(
        os.linux.tcflag_t,
        os.linux.BRKINT | os.linux.INPCK | os.linux.ISTRIP,
    );

    // On met la taille des caractĂšres Ă  8 bits.
    // N'as normalement pas d'effet sur les systĂšmes modernes.
    raw.cflag |= os.system.CS8;

    raw.cc[os.system.V.TIME] = 0;
    raw.cc[os.system.V.MIN] = 1;

    // Applique les changements
    try os.tcsetattr(tty.handle, .FLUSH, raw);

    try stdout.print(
        "Bienvenue dans Taptempo !\nPour commencer, appuyez sur une touche.\n(q pour arrĂȘter)\n",
        .{});

    while (true) {
        var buffer: [1]u8 = undefined;
        _ = try tty.read(&buffer);
        if (buffer[0] == 'q') {
            break;
        } else {
            try captureTempo(tty);
        }
    }
    try stdout.print("Au revoir !\n", .{});
    // Restaure l'Ă©tat original du terminal
    try os.tcsetattr(tty.handle, .FLUSH, original);
    return;
}

Pour ceux que le cÎté technique intéresse, voici quelques détails :
- certaines fonctionnalités du terminal sont désactivées pour pouvoir intercepter correctement la frappe au clavier,
- la précision est de l'ordre de la milliseconde, mais le résultat affiché est le produit d'une conversion un peu bourine des bpm en un entier,
- il n'y a aucune dépendance externe mis à part la bibliothÚque standard.

Des exécutables pour x86_64 et aarch64 sont disponibles sur la page des releases du dépÎt GitHub.

Si vous voulez en apprendre plus sur Zig, sachez qu'une dĂ©pĂȘche sur le sujet est en cours de rĂ©daction.

  • # Qu'est-ce que TapTempo

    Posté par  (site web personnel) . Évalué à 2.

    Pour les nouveaux venus sur ce site, pourriez-vous expliquer ce qu'est exactement le TapTempo ? Merci d'avance !

    • [^] # Re: Qu'est-ce que TapTempo

      Posté par  (site web personnel, Mastodon) . Évalué à 4.

      Il y a toute une page du wiki dessus. Je cite :

      L’objectif de son programme Ă©tait de mesurer la cadence d’une musique en tapant simplement sur une touche de son clavier, le rĂ©sultat s’affichant ensuite dans la console.

      Il a été porté dans une foultitude de langages.

      « Tak ne veut pas quÊŒon pense Ă  lui, il veut quÊŒon pense », Terry Pratchett, DĂ©raillĂ©.

  • # Non mais ...

    Posté par  (site web personnel) . Évalué à 4.

    C'est qui ce zig qui a 3 ans de retard?!

  • # quelques remarques

    Posté par  . Évalué à 3. DerniĂšre modification le 07 aoĂ»t 2023 Ă  15:36.

    Merci pour ce début de tutoriel

Suivre le flux des commentaires

Note : les commentaires appartiennent Ă  celles et ceux qui les ont postĂ©s. Nous n’en sommes pas responsables.