URL: https://linuxfr.org/users/abarret/journaux/taptempo-en-java-17 Title: TapTempo en Java 17 Authors: vitanix Date: 2021年11月07日T12:18:40+01:00 License: CC By-SA Tags: taptempo et java Score: 19 Cher journal, Suite à la sortie récente de Java 17, j'ai créer une nouvelle version de TapTempo en en utilisant les dernière évolutions de Java. La version initiale de TapTempo en Java est [ici](https://linuxfr.org/users/gle--2/journaux/portage-de-taptempo-en-java). Je n'ai pas trouvé le dépot avec les sources, mais un commentaire contenait le code principal. Je me suis basé sur ce commentaire pour faire la version en Java. J'ai utilisé divers nouveaux mécanisme de Java 17 : - le type var, - les records, - les texts blocs dans les tests (c-a-d les string sur plusieurs lignes), - le switch expression. J'ai hésité à ajouter les classe sealed et le pattern matching, mais ça aurais compliqué le code inutilement. Par rapport à la version initiale, j'ai ajouté l'internationalisation pour gérer le français, et j'ai mis quelques tests. La première version que j'ai faites (version 1.0.0), ne gérait pas l'internationalisation, et les chaines de caractères étaient en texts blocs. On peut voir le code [ici](https://github.com/abarhub/TapTempoJava17/blob/94dfa0228ddf03056e0ccc6a52c0d7babbb46535/src/main/java/org/github/abarhub/taptempojava/TapTempo.java). Je trouve ces texts bloc très lisibles. En ajoutant l'internationalisation, j'ai du les enlevé de la classe principale, mais je les ai laissé dans les tests. J'ai découvert la méthode formatted de la classe String qui permet de formater une chaine de caractère. Elle est apparue en Java 15. Exemple d'utilisation : ```java "Valeur: %d".formatted(10) ``` Voici le code principal : ```java package org.github.abarhub.taptempojava; import org.apache.commons.cli.*; import java.text.DecimalFormat; import java.time.Clock; import java.time.Duration; import java.time.Instant; import java.util.ArrayDeque; import java.util.Deque; import java.util.ResourceBundle; import java.util.Scanner; public class TapTempo { private static Clock clock = Clock.systemUTC(); record Parameter(int precision, int resetTime, int sampleSize) { } enum Action { END, CALCULATE, OTHER } static class ExitException extends RuntimeException { private final int exitCode; public ExitException(int exitCode) { this.exitCode = exitCode; } public int getExitCode() { return exitCode; } } private static Parameter parserArguments(String[] args, ResourceBundle messages) { var precision = 0; var precisionMax = 5; var resetTime = 5; var sampleSize = 5; var options = new Options(); var optHelp = new Option("h", "help", false, messages.getString("cliHelp")); optHelp.setRequired(false); options.addOption(optHelp); var optPrecision = new Option("p", "precision", true, messages.getString("cliPrecision").formatted(precision, precisionMax)); optPrecision.setRequired(false); options.addOption(optPrecision); var optResetTime = new Option("r", "reset-time", true, messages.getString("cliReset").formatted(resetTime)); optResetTime.setRequired(false); options.addOption(optResetTime); var optSampleSize = new Option("s", "sample-size", true, messages.getString("cliNbSample").formatted(sampleSize)); optSampleSize.setRequired(false); options.addOption(optSampleSize); var optVersion = new Option("v", "version", false, messages.getString("cliVersion")); optVersion.setRequired(false); options.addOption(optVersion); var parser = new DefaultParser(); var formatter = new HelpFormatter(); CommandLine cmd = null; try { cmd = parser.parse(options, args); if (cmd.hasOption('p')) { precision = Integer.parseInt(cmd.getOptionValue('p')); if (precision < 0) { precision = 0; } else if (precision> precisionMax) { precision = precisionMax; } } if (cmd.hasOption('r')) { resetTime = Integer.parseInt(cmd.getOptionValue('r')); if (resetTime < 1) { resetTime = 1; } } if (cmd.hasOption('s')) { sampleSize = Integer.parseInt(cmd.getOptionValue('s')); if (sampleSize < 1) { sampleSize = 1; } } } catch (NumberFormatException | ParseException e) { System.out.println(e.getClass() + ": " + e.getMessage()); formatter.printHelp("TempoTap", options); throw new ExitException(1); } if (cmd.hasOption('h') || cmd.hasOption('v')) { if (cmd.hasOption('h')) { formatter.printHelp("TempoTap", options); } if (cmd.hasOption('v')) { System.out.printf((messages.getString("version")), Version.getVersion()); } throw new ExitException(0); } return new Parameter(precision, resetTime, sampleSize); } public static double computeBPM(Instant currentTime, Instant lastTime, int occurenceCount) { if (occurenceCount == 0) { occurenceCount = 1; } Duration elapsedTime = Duration.between(lastTime, currentTime); var meanTime = elapsedTime.dividedBy(occurenceCount); return 60.0 * 1000 / meanTime.toMillis(); } public static boolean compareDiff(Instant lastTime, Instant currentTime, long resetTime) { return Duration.between(lastTime, currentTime).compareTo(Duration.ofSeconds(resetTime))> 0; } public static void setClock(Clock clock) { TapTempo.clock = clock; } public static void run(String[] args) { ResourceBundle messages = ResourceBundle.getBundle("Message"); Deque hitTimePoints = new ArrayDeque(); var parameter = parserArguments(args, messages); var df = new DecimalFormat(); df.setMaximumFractionDigits(parameter.precision); df.setMinimumFractionDigits(parameter.precision); System.out.println(messages.getString("start")); var keyboard = new Scanner(System.in); keyboard.useDelimiter(""); boolean shouldContinue = true; while (shouldContinue) { Action action; do { char c = keyboard.next().charAt(0); action = switch (c) { case 'q' -> Action.END; case '\n' -> Action.CALCULATE; default -> Action.OTHER; }; } while (action == Action.OTHER); if (action == Action.END) { shouldContinue = false; System.out.println(messages.getString("quit")); } else { var currentTime = Instant.now(clock); // Reset if the hit diff is too big. if (!hitTimePoints.isEmpty() && compareDiff(hitTimePoints.getLast(), currentTime, parameter.resetTime)) { // Clear the history. hitTimePoints.clear(); } hitTimePoints.add(currentTime); if (hitTimePoints.size()> 1) { var bpm = computeBPM(hitTimePoints.getLast(), hitTimePoints.getFirst(), hitTimePoints.size() - 1); String bpmRepresentation = df.format(bpm); System.out.printf(messages.getString("tempo"), bpmRepresentation); } else { System.out.println(messages.getString("hitEnter")); } while (hitTimePoints.size()> parameter.sampleSize) { hitTimePoints.pop(); } } } } public static void main(String[] args) throws Exception { try { run(args); } catch (ExitException e) { System.exit(e.exitCode); } } } ``` Le code compile avec Maven et Java 17. J'ai mis les sources en licence Apache 2. Il devrait fonctionner sur les principaux os (Debian/Raspberry/Windows/MacOS/...). [sources](https://github.com/abarhub/TapTempoJava17) [classe principale](https://github.com/abarhub/TapTempoJava17/blob/main/src/main/java/org/github/abarhub/taptempojava/TapTempo.java) [release](https://github.com/abarhub/TapTempoJava17/releases/tag/v1.0.1)

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