From 6c94f9895b64b05b1de4e3d1ab047f2dbcd5acd2 Mon Sep 17 00:00:00 2001 From: vmagnin Date: Thu, 28 Oct 2021 09:51:59 +0200 Subject: [PATCH] Initial commit --- CHANGELOG.md | 7 +++ README.md | 79 ++++++++++++++++++++++++++++ app/main.f90 | 8 +++ build.sh | 18 +++++++ fpm.toml | 14 +++++ src/taptempo.f90 | 130 +++++++++++++++++++++++++++++++++++++++++++++++ test/check.f90 | 5 ++ 7 files changed, 261 insertions(+) create mode 100644 CHANGELOG.md create mode 100644 README.md create mode 100644 app/main.f90 create mode 100755 build.sh create mode 100644 fpm.toml create mode 100644 src/taptempo.f90 create mode 100644 test/check.f90 diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..a0f56d3 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,7 @@ +# Changelog +All notable changes to the taptempo project will be documented in this file. +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). + + +## [TapTempo Fortran 0.9] 2021-10-28 +Initial commit. diff --git a/README.md b/README.md new file mode 100644 index 0000000..6057d06 --- /dev/null +++ b/README.md @@ -0,0 +1,79 @@ +# TapTempo Fortran + +A command line `taptempo` written in modern Fortran and under GNU GPLv3 license. Listen to a song and hit enter key with style and you'll get the corresponding number of beats per minute (BPM). This tool is very useful to quickly find the tempo of a song. + +The original TapTempo was written in C++, but lots of porting in other languages have been developed via the [LinuxFr website](https://linuxfr.org/wiki/taptempo). There is even a [TapTempo Federation](https://github.com/Taptempo-Federation). + + +## Compilation and execution + +You need a modern Fortran compiler. + +You can easily build and run the project using the Fortran Package Manager fpm (https://github.com/fortran-lang/fpm) at the root of the project directory: + +``` +$ fpm build +$ fpm run + Hit Enter key for each beat (q to quit). + + [Hit enter key one more time to start BPM computation...] + +Tempo: 85. BPM + +Tempo: 83. BPM + +Tempo: 84. BPM + +Tempo: 84. BPM + +Tempo: 81. BPM +``` + +To add options, put them after `--`: + +``` +$ fpm run -- -r 3 -s 8 -p 2 +``` + +Or you can also use the `build.sh` script if you don't have fpm installed, or just simply type: + +``` +$ gfortran src/taptempo.f90 app/main.f90 +``` + +## Options + +``` +Usage : taptempo [options] + +Options : + -h, --help display this help message + -p, --precision change the number of decimal for the tempo, + the default is 0 decimal places, the max is 5 decimals + -r, --reset-time change the time in seconds to reset the calculation, + the default is 5 seconds + -s, --sample-size change the number of samples needed to calculate the tempo, + the default is 5 samples, the minimum is 2 + -v, --version print the version number +``` + +## Contributing + +* Post a message in the GitHub *Issues* tab to discuss the function you want to work on. +* Concerning coding conventions, follow the stdlib conventions: +https://github.com/fortran-lang/stdlib/blob/master/STYLE_GUIDE.md +* When ready, make a *Pull Request*. + +## Technical information + +* https://en.wikipedia.org/wiki/Tempo + +* Introduced by Fortran 90: + * [SYSTEM_CLOCK()](https://gcc.gnu.org/onlinedocs/gfortran/SYSTEM_005fCLOCK.html) + +* Introduced by Fortran 2003: + * [COMMAND_ARGUMENT_COUNT()](https://gcc.gnu.org/onlinedocs/gfortran/COMMAND_005fARGUMENT_005fCOUNT.html) + * [GET_COMMAND_ARGUMENT()](https://gcc.gnu.org/onlinedocs/gfortran/GET_005fCOMMAND_005fARGUMENT.html) + +* Introduced by Fortran 2008: + * [ISO_FORTRAN_ENV real64 and int64](https://gcc.gnu.org/onlinedocs/gfortran/ISO_005fFORTRAN_005fENV.html) diff --git a/app/main.f90 b/app/main.f90 new file mode 100644 index 0000000..9f46019 --- /dev/null +++ b/app/main.f90 @@ -0,0 +1,8 @@ +program main + use taptempo, only: manage_command_line, measure_tempo + + implicit none + + call manage_command_line() + call measure_tempo() +end program main diff --git a/build.sh b/build.sh new file mode 100755 index 0000000..d1bea19 --- /dev/null +++ b/build.sh @@ -0,0 +1,18 @@ +#!/bin/bash + +# For a safer script: +set -eu + +# Default compiler can be overrided, for example: +# $ GFC='gfortran-8' ./build.sh +# Default: +: ${FC="gfortran"} + +# Create (if needed) the build directory: +if [ ! -d build ]; then + mkdir build +fi + +rm -f *.mod + +"${FC}" -Wall -Wextra -pedantic -std=f2008 -O2 src/taptempo.f90 app/main.f90 -o build/taptempo diff --git a/fpm.toml b/fpm.toml new file mode 100644 index 0000000..7578836 --- /dev/null +++ b/fpm.toml @@ -0,0 +1,14 @@ +name = "taptempo" +version = "0.9.0" +license = "license" +author = "Vincent Magnin and the Fortran-lang community" +maintainer = "Vincent Magnin" +copyright = "Copyright 2021" + +[build] +auto-executables = true +auto-tests = true +auto-examples = true + +[install] +library = false diff --git a/src/taptempo.f90 b/src/taptempo.f90 new file mode 100644 index 0000000..6957aba --- /dev/null +++ b/src/taptempo.f90 @@ -0,0 +1,130 @@ +module taptempo + ! Reals working precision and 64 bits integers: + use, intrinsic :: iso_fortran_env, only: wp=>real64, int64 + + implicit none + + ! Tap Tempo Version: + character(len=*), parameter :: version = "0.9.0" + + ! Default values of the command options: + integer :: s = 5 ! Stack size + integer :: p = 0 ! Precision + integer :: r = 5 ! Reset time in seconds + + private + + public :: manage_command_line, measure_tempo + +contains + + subroutine print_version + print '(A)', "TapTempo Fortran "//version + print '(A)', "Copyright (C) 2021 Vincent Magnin and the Fortran-lang community" + print '(A)', "License GPLv3+: GNU GPL version 3 or later " + print '(A)', "This is free software: you are free to change and redistribute it." + print '(A)', "There is NO WARRANTY, to the extent permitted by law." + end subroutine + + + subroutine print_options + print '(A)' + print '(A)', "Usage : taptempo [options]" + print '(A)' + print '(A)', "Options :" + print '(A)', " -h, --help display this help message" + print '(A)', " -p, --precision change the number of decimal for the tempo," + print '(A)', " the default is 0 decimal places, the max is 5 decimals" + print '(A)', " -r, --reset-time change the time in seconds to reset the calculation," + print '(A)', " the default is 5 seconds" + print '(A)', " -s, --sample-size change the number of samples needed to calculate the tempo," + print '(A)', " the default is 5 samples, the minimum is 2" + print '(A)', " -v, --version print the version number" + end subroutine + + + subroutine manage_command_line + integer :: i, nb + character(len=100) :: args + + nb = command_argument_count() + if (nb == 0) return + + i = 0 + do while (i <= nb) + i = i + 1 + call get_command_argument(i, value=args) + + if ((trim(args) == "-h").or.(trim(args) == "--help")) then + call print_version() + call print_options() + stop + else if ((trim(args) == "-p").or.(trim(args) == "--precision")) then + i = i + 1 + call get_command_argument(i, value=args) + read(args, *) p + p = min(p, 5) + else if ((trim(args) == "-r").or.(trim(args) == "--reset-time")) then + i = i + 1 + call get_command_argument(i, value=args) + read(args, *) r + else if ((trim(args) == "-s").or.(trim(args) == "--sample-size")) then + i = i + 1 + call get_command_argument(i, value=args) + read(args, *) s + s = max(2, s) + else if ((trim(args) == "-v").or.(trim(args) == "--version")) then + call print_version() + stop + end if + end do + end subroutine manage_command_line + + + subroutine measure_tempo + character(len=1) :: key + integer(int64) :: count ! Count of the processor clock + real(wp) :: rate ! Number of clock ticks per second + integer :: i + real(wp), dimension(s) :: t ! Time FIFO stack + character(len=25) :: fmt + + ! Format used for printing the tempo: + write(fmt, '(A, I1, A)') '("Tempo: ", f8.', p, ', " BPM")' + ! Stack initialization: + t = 0.0_wp + + print '(A)', "Hit Enter key for each beat (q to quit)." + + ! Infinite loop: + i = 0 + do + read(*, '(a1)') key + + if (key == 'q') exit + + call system_clock(count, rate) + ! Updating the time FIFO stack: + t(2:s) = t(1:s-1) + t(1) = count / rate + + i = i + 1 + + if (i == 1) then + print '(A)', "[Hit enter key one more time to start BPM computation...]" + else + ! Verify that the user is actively tapping: + if (t(1) - t(2) <= r) then + print fmt, 60.0_wp / ((t(1) - t(min(i, s))) / (min(i, s)-1)) + else + print '(A)', "Time reset" + i = 1 + t(2:s) = 0.0_wp + end if + end if + end do + + print '(A)', "I don't know why you say goodbye..." + end subroutine measure_tempo + +end module taptempo diff --git a/test/check.f90 b/test/check.f90 new file mode 100644 index 0000000..6c5f618 --- /dev/null +++ b/test/check.f90 @@ -0,0 +1,5 @@ +program check + implicit none + + print *, "Let It Be is around 72 BPM" +end program check